private void OnEnable()
 {
     if (rig == null)
     {
         rig = GetRigComponent();
     }
 }
 void Start()
 {
     if (rig == null)
     {
         rig = GetRigComponent();
     }
 }
示例#3
0
        internal RigComponent Create(RigComponent newRigComponent)
        {
            int id = _repo.Create(newRigComponent);

            newRigComponent.Id = id;
            return(newRigComponent);
        }
示例#4
0
    // TODO (mogens) move this to RigConversion in animation package ?
    public static BlobAssetReference <RigDefinition> ConvertRig(RigComponent rigComponent)
    {
        var skeletonNodes = RigGenerator.ExtractSkeletonNodesFromRigComponent(rigComponent);
        var channels      = RigGenerator.ExtractAnimationChannelFromRigComponent(rigComponent);
        var rigDefinition = RigBuilder.CreateRigDefinition(skeletonNodes, null, channels);

        return(rigDefinition);
    }
 /// <summary>
 /// Retreives the Transforms and PhysicsShapeAuthorings' from the children of the GameObject
 /// </summary>
 /// <param name="rigComponent"></param>
 /// <param name="children"></param>
 /// <param name="childrenShapes"></param>
 void GetData(RigComponent rigComponent, out Transform[] children, out PhysicsShapeAuthoring[] childrenShapes)
 {
     children       = new Transform[rigComponent.Bones.Length];
     childrenShapes = new PhysicsShapeAuthoring[rigComponent.Bones.Length];
     for (int i = 0; i < rigComponent.Bones.Length; i++)
     {
         children[i]       = rigComponent.Bones[i];
         childrenShapes[i] = children[i].GetComponent <PhysicsShapeAuthoring>();
     }
 }
        internal int Create(RigComponent newRigComponent)
        {
            string sql = @"
      INSERT INTO RigComponents
      (rigId, componentId, userId)
      VALUES
      (@RigId, @ComponentId, @userId);
      SELECT LAST_INSERT_ID()";

            return(_db.ExecuteScalar <int>(sql, newRigComponent));
        }
示例#7
0
    /// <summary>
    /// From the transform's name, find the path in the rig that will later be converted to
    /// a hash, and then the hash will be used to find an index in the AnimationStream.
    /// </summary>
    public ChannelWeightQuery CreateFeatherBlendQuery(RigComponent rig, List <FeatherBlend> blendMap)
    {
        string status = "";

        List <ChannelWeightMap> channels = new List <ChannelWeightMap>();

        for (var mapIter = 0; mapIter < blendMap.Count; mapIter++)
        {
            bool success = false;

            for (var srcBoneIter = 0; srcBoneIter < rig.Bones.Length; srcBoneIter++)
            {
                if (blendMap[mapIter].Id == rig.Bones[srcBoneIter].name)
                {
                    var srcPath = RigGenerator.ComputeRelativePath(rig.Bones[srcBoneIter], rig.transform);

                    channels.Add(new ChannelWeightMap()
                    {
                        Id = srcPath, Weight = blendMap[mapIter].Weight
                    });
                }

                success = true;
            }

            if (!success)
            {
                status = status + mapIter + " ";
            }
        }

        ChannelWeightQuery featherBlendQuery = new ChannelWeightQuery();

        featherBlendQuery.Channels = new ChannelWeightMap[channels.Count];

        for (var iter = 0; iter < channels.Count; iter++)
        {
            featherBlendQuery.Channels[iter] = channels[iter];
        }

        if (!string.IsNullOrEmpty(status))
        {
            UnityEngine.Debug.LogError("Faulty Entries : " + status);
        }

        return(featherBlendQuery);
    }
 public ActionResult <RigComponent> Create([FromBody] RigComponent newRigComponent)
 {
     try
     {
         Claim user = HttpContext.User.FindFirst(ClaimTypes.NameIdentifier);
         if (user == null)
         {
             throw new Exception("You must be logged in to add a keep to a vault");
         }
         newRigComponent.UserId = user.Value;
         return(Ok(_rcs.Create(newRigComponent)));
     }
     catch (System.Exception err)
     {
         return(BadRequest(err.Message));
     }
 }
示例#9
0
    public RigRemapQuery CreateRemapQuery(RigComponent srcRig, RigComponent dstRig, List<string> retargetMap)
    {
        string status = "";

        List<ChannelMap> translationChannels = new List<ChannelMap>();
        List<ChannelMap> rotationChannels = new List<ChannelMap>();
        List<RigTranslationOffset> translationOffsets = new List<RigTranslationOffset>();
        List<RigRotationOffset> rotationOffsets = new List<RigRotationOffset>();

        for (var mapIter = 0; mapIter < retargetMap.Count; mapIter++)
        {
            bool success = false;

            string[] splitMap = retargetMap[mapIter].Split(new char[] { ' ' }, 3);

            for (var srcBoneIter = 0; srcBoneIter < srcRig.Bones.Length; srcBoneIter++)
            {
                if (splitMap.Length > 0 && splitMap[0] == srcRig.Bones[srcBoneIter].name)
                {
                    for (var dstBoneIter = 0; dstBoneIter < dstRig.Bones.Length; dstBoneIter++)
                    {
                        if (splitMap.Length > 1 && splitMap[1] == dstRig.Bones[dstBoneIter].name)
                        {
                            if (splitMap.Length > 2)
                            {
                                var srcPath = RigGenerator.ComputeRelativePath(srcRig.Bones[srcBoneIter], srcRig.transform);
                                var dstPath = RigGenerator.ComputeRelativePath(dstRig.Bones[dstBoneIter], dstRig.transform);

                                if (splitMap[2] == "TR" || splitMap[2] == "T")
                                {
                                    var translationOffset = new RigTranslationOffset();

                                    // heuristic that computes retarget scale based on translation node (ex: hips) height (assumed to be y)
                                    translationOffset.Scale = dstRig.Bones[dstBoneIter].position.y / srcRig.Bones[srcBoneIter].position.y;
                                    
                                    translationOffset.Rotation = mathex.mul(math.conjugate(dstRig.Bones[dstBoneIter].parent.rotation), srcRig.Bones[srcBoneIter].parent.rotation);

                                    translationOffsets.Add(translationOffset);
                                    translationChannels.Add(new ChannelMap() { SourceId = srcPath, DestinationId = dstPath, OffsetIndex = translationOffsets.Count });
                                }

                                if (splitMap[2] == "TR" || splitMap[2] == "R")
                                {
                                    var rotationOffset = new RigRotationOffset();

                                    rotationOffset.PreRotation = mathex.mul(math.conjugate(dstRig.Bones[dstBoneIter].parent.rotation), srcRig.Bones[srcBoneIter].parent.rotation);
                                    rotationOffset.PostRotation = mathex.mul(math.conjugate(srcRig.Bones[srcBoneIter].rotation), dstRig.Bones[dstBoneIter].rotation);

                                    rotationOffsets.Add(rotationOffset);
                                    rotationChannels.Add(new ChannelMap() { SourceId = srcPath, DestinationId = dstPath, OffsetIndex = rotationOffsets.Count });
                                }

                                success = true;
                            }
                        }
                    }
                }
            }

            if (!success)
            {
                status = status + mapIter + " ";
            }
        }

        RigRemapQuery rigRemapQuery = new RigRemapQuery();

        rigRemapQuery.TranslationChannels = new ChannelMap[translationChannels.Count];

        for (var iter = 0; iter < translationChannels.Count; iter++)
        {
            rigRemapQuery.TranslationChannels[iter] = translationChannels[iter];
        }

        rigRemapQuery.TranslationOffsets = new RigTranslationOffset[translationOffsets.Count + 1];
        rigRemapQuery.TranslationOffsets[0] = new RigTranslationOffset();

        for (var iter = 0; iter < translationOffsets.Count; iter++)
        {
            rigRemapQuery.TranslationOffsets[iter + 1] = translationOffsets[iter];
        }

        rigRemapQuery.RotationChannels = new ChannelMap[rotationChannels.Count];

        for (var iter = 0; iter < rotationChannels.Count; iter++)
        {
            rigRemapQuery.RotationChannels[iter] = rotationChannels[iter];
        }

        rigRemapQuery.RotationOffsets = new RigRotationOffset[rotationOffsets.Count + 1];
        rigRemapQuery.RotationOffsets[0] = new RigRotationOffset();

        for (var iter = 0; iter < rotationOffsets.Count; iter++)
        {
            rigRemapQuery.RotationOffsets[iter + 1] = rotationOffsets[iter];
        }

        if (!string.IsNullOrEmpty(status))
        {
            UnityEngine.Debug.LogError("Faulty Entries : " + status);
        }

        return rigRemapQuery;
    }
    private RigRemapQuery CreateRemapQuery(RigComponent srcRig, RigComponent dstRig, List <string> retargetMap)
    {
        var status = "";

        var translationChannels = new List <ChannelMap>();
        var rotationChannels    = new List <ChannelMap>();
        var translationOffsets  = new List <RigTranslationOffset>();
        var rotationOffsets     = new List <RigRotationOffset>();

        //Inverse Quaternion = (x * -1, y * -1, z * -1, w)
        var srcRootRotInv = math.inverse(srcRig.transform.rotation);
        var dstRootRotInv = math.inverse(dstRig.transform.rotation);

        //We are iterating through the retargetMap collection..
        for (var mapIter = 0; mapIter < retargetMap.Count; mapIter++)
        {
            var successFlag = false;

            //Splitting the element in each array when there a space. Three element
            //ex. "First Second Third" = ["First", "Second", "Third"]
            var splitMap = retargetMap[mapIter].Split(new[] { ' ' }, 3);

            //We are iterating through the for loop on the number of bones the srcPrefab.
            for (var srcBoneIter = 0; srcBoneIter < srcRig.Bones.Length; srcBoneIter++)
            {
                //We are checking if the splitMap element has atleast one element and if the first element matches any bone name of the srcRig prefab bone
                if (splitMap.Length > 0 && splitMap[0] == srcRig.Bones[srcBoneIter].name)
                {
                    //We are iterating through the for loop on the number of bones we have on the gameObject attached to this script.
                    for (var dstBoneIter = 0; dstBoneIter < dstRig.Bones.Length; dstBoneIter++)
                    {
                        //We are checking if the splitMap element has atleast two element and if the second element matches any bone name of this gameObject
                        if (splitMap.Length > 1 && splitMap[1] == dstRig.Bones[dstBoneIter].name)
                        {
                            //We are checking if the splitMap has atleast three element
                            if (splitMap.Length > 2)
                            {
                                //We are getting all the parent gameObject name of the current srcRig bone at srcBoneIter up until and excluding the srcRig.transform
                                //Ex.
                                // if srcRig.Bones[srcBoneIter] = Chest
                                // then the srcPath = Skeleton/Root/Spine/Chest (it traverse the hierarchy and get the parent up until the srcRig.transform)
                                var srcPath =
                                    RigGenerator.ComputeRelativePath(srcRig.Bones[srcBoneIter], srcRig.transform);
                                var dstPath
                                    = RigGenerator.ComputeRelativePath(dstRig.Bones[dstBoneIter], dstRig.transform);

                                if (splitMap[2] == "TR" || splitMap[2] == "T")
                                {
                                    var translationOffset = new RigTranslationOffset();

                                    //self bone position.y / target bone position.y to get the average height of the bone (assumed to be y/ standing up right)
                                    translationOffset.Scale = dstRig.Bones[dstBoneIter].position.y /
                                                              srcRig.Bones[srcBoneIter].position.y;

                                    var dstParentRot =
                                        math.mul(dstRootRotInv, dstRig.Bones[dstBoneIter].parent.rotation);
                                    var srcParentRot =
                                        math.mul(srcRootRotInv, srcRig.Bones[srcBoneIter].parent.rotation);

                                    translationOffset.Rotation = mathex.mul(math.inverse(dstParentRot), srcParentRot);

                                    translationOffsets.Add(translationOffset);
                                    translationChannels.Add(new ChannelMap
                                    {
                                        SourceId    = srcPath, DestinationId = dstPath,
                                        OffsetIndex = translationOffsets.Count
                                    });
                                }

                                if (splitMap[2] == "TR" || splitMap[2] == "R")
                                {
                                    var rotationOffset = new RigRotationOffset();

                                    var dstParentRot = math.mul(dstRootRotInv,
                                                                dstRig.Bones[dstBoneIter].parent.transform.rotation);
                                    var srcParentRot = math.mul(srcRootRotInv,
                                                                srcRig.Bones[srcBoneIter].parent.transform.rotation);

                                    var dstRot = math.mul(dstRootRotInv,
                                                          dstRig.Bones[dstBoneIter].transform.rotation);
                                    var srcRot = math.mul(srcRootRotInv,
                                                          srcRig.Bones[srcBoneIter].transform.rotation);

                                    rotationOffset.PreRotation  = mathex.mul(math.inverse(dstParentRot), srcParentRot);
                                    rotationOffset.PostRotation = mathex.mul(math.inverse(srcRot), dstRot);

                                    rotationOffsets.Add(rotationOffset);
                                    rotationChannels.Add(new ChannelMap
                                    {
                                        SourceId = srcPath, DestinationId = dstPath, OffsetIndex = rotationOffsets.Count
                                    });
                                }

                                successFlag = true;
                            }
                        }
                    }
                }
            }

            if (!successFlag)
            {
                status = status + mapIter + " ";
            }
        }

        var rigRemapQuery = new RigRemapQuery();

        rigRemapQuery.TranslationChannels = new ChannelMap[translationChannels.Count];

        for (var iter = 0; iter < translationChannels.Count; iter++)
        {
            rigRemapQuery.TranslationChannels[iter] = translationChannels[iter];
        }

        rigRemapQuery.TranslationOffsets    = new RigTranslationOffset[translationOffsets.Count + 1];
        rigRemapQuery.TranslationOffsets[0] = new RigTranslationOffset();

        for (var iter = 0; iter < translationOffsets.Count; iter++)
        {
            rigRemapQuery.TranslationOffsets[iter + 1] = translationOffsets[iter];
        }

        rigRemapQuery.RotationChannels = new ChannelMap[rotationChannels.Count];

        for (var iter = 0; iter < rotationChannels.Count; iter++)
        {
            rigRemapQuery.RotationChannels[iter] = rotationChannels[iter];
        }

        rigRemapQuery.RotationOffsets    = new RigRotationOffset[rotationOffsets.Count + 1];
        rigRemapQuery.RotationOffsets[0] = new RigRotationOffset();

        for (var iter = 0; iter < rotationOffsets.Count; iter++)
        {
            rigRemapQuery.RotationOffsets[iter + 1] = rotationOffsets[iter];
        }

        if (!string.IsNullOrEmpty(status))
        {
            Debug.LogError("Faulty Entries : " + status);
        }

        return(rigRemapQuery);
    }
示例#11
0
        void ConvertAttahments(RigBasedAssetAuthoring assetAuthoring, Entity assetEntity, RigComponent rigComponent, BlobAssetReference <RigDefinition> rig)
        {
            var attachmentInfos   = new List <AttachmentData>();
            var rigComponentBones = new List <Transform>(rigComponent.Bones);

            for (int nBone = 0; nBone < rigComponentBones.Count; nBone++)
            {
                var localBone = rigComponentBones[nBone];
                for (int nChild = 0; nChild < localBone.childCount; nChild++)
                {
                    var child = localBone.GetChild(nChild);

                    // Ignore children that are also bones
                    if (rigComponentBones.Contains(child))
                    {
//                        GameDebug.Log("bone" + child + " should not be attached");
                        continue;
                    }
                    attachmentInfos.Add(new AttachmentData {
                        go        = child.gameObject,
                        boneIndex = nBone,
                    });
                }
            }


            if (attachmentInfos.Count > 0)
            {
                DstEntityManager.AddBuffer <RigBasedAsset.Attachment>(assetEntity);
            }

            // Find objects attached to bones
            for (int i = 0; i < attachmentInfos.Count; i++)
            {
                var attachmentInfo   = attachmentInfos[i];
                var attachmentEntity = GetPrimaryEntity(attachmentInfo.go);

                var assetAttachments = DstEntityManager.GetBuffer <RigBasedAsset.Attachment>(assetEntity);
                assetAttachments.Add(new RigBasedAsset.Attachment()
                {
                    Value = attachmentEntity
                });

                DstEntityManager.AddComponentData(attachmentEntity, new RigAttacher.AttachEntity()
                {
                    Value = attachmentEntity,
                });


                // Remove from parent
                if (DstEntityManager.HasComponent <Parent>(attachmentEntity))
                {
                    DstEntityManager.RemoveComponent <Parent>(attachmentEntity);
                }
                if (DstEntityManager.HasComponent <LocalToParent>(attachmentEntity))
                {
                    DstEntityManager.RemoveComponent <LocalToParent>(attachmentEntity);
                }
                if (!DstEntityManager.HasComponent <Static>(attachmentEntity))
                {
                    DstEntityManager.AddComponent <Static>(attachmentEntity);
                }

                // Add rig attacher
                var boneRef = RuntimeBoneReference.Default;
                boneRef.BoneIndex    = attachmentInfo.boneIndex;
                boneRef.ReferenceRig = rig;
                RigAttacher.AddRigAttacher(attachmentEntity, DstEntityManager, boneRef);


                GameDebug.Log("  Found attrachment:{0} on bone:{1} rig:{2}", attachmentInfo.go, boneRef.BoneIndex, rigComponent);
            }
        }
示例#12
0
        void ConvertSkinnedMeshRenderes(RigBasedAssetAuthoring assetAuthoring, Entity assetEntity,
                                        RigComponent rigComponent)
        {
            var skinnedMeshRenderers = assetAuthoring.GetComponentsInChildren <SkinnedMeshRenderer>();

            if (skinnedMeshRenderers.Length > 0)
            {
                DstEntityManager.AddBuffer <RigBasedAsset.SkinnedMeshRenderer>(assetEntity);
            }
            foreach (var meshRenderer in skinnedMeshRenderers)
            {
                var skinEntity = GetPrimaryEntity(meshRenderer.gameObject);
                //var skinEntity = conversionSystem.CreateAdditionalEntity(meshRenderer);
    #if UNITY_EDITOR
                DstEntityManager.SetName(skinEntity, "Entity " + skinEntity.Index + " Skin_" + meshRenderer.gameObject.name);
    #endif

                var skinRendererBuffer = DstEntityManager.GetBuffer <RigBasedAsset.SkinnedMeshRenderer>(assetEntity);
                skinRendererBuffer.Add(new RigBasedAsset.SkinnedMeshRenderer {
                    Value = skinEntity,
                });

                //var rigEntity = GetPrimaryEntity(src.Rig);
                //DstEntityManager.AddComponentData(entity, new SkinnedMeshComponentData { RigEntity = rigEntity });
                DstEntityManager.AddComponentData(skinEntity, new SkinnedMeshRigEntity {
                    Value = assetEntity
                });
                DstEntityManager.AddComponentData(skinEntity, new LocalToWorld());
                DstEntityManager.AddComponentData(skinEntity, new BoneIndexOffset());

                DstEntityManager.AddBuffer <SkinnedMeshToRigIndex>(skinEntity);
                DstEntityManager.AddBuffer <BindPose>(skinEntity);
                DstEntityManager.AddBuffer <SkinMatrix>(skinEntity);

                var skeletonIndexArray = DstEntityManager.GetBuffer <SkinnedMeshToRigIndex>(skinEntity);
                var bindPoseArray      = DstEntityManager.GetBuffer <BindPose>(skinEntity);
                var skinMatrices       = DstEntityManager.GetBuffer <SkinMatrix>(skinEntity);

                var smBones = meshRenderer.bones;
                skeletonIndexArray.ResizeUninitialized(smBones.Length);
                bindPoseArray.ResizeUninitialized(smBones.Length);
                skinMatrices.ResizeUninitialized(smBones.Length);

                //GameDebug.Log("skin smBones");
                //for (int i = 0; i < smBones.Length; i++)
                //{
                //    var relativePath = RigGenerator.ComputeRelativePath(smBones[i], transform);
                //    var id = (StringHash)relativePath;
                //    GameDebug.Log("  " + i + ":" + id.Id + " path:" + relativePath);
                //}

                for (int j = 0; j != smBones.Length; ++j)
                {
                    var remap = new SkinnedMeshToRigIndex {
                        Value = -1
                    };

                    var smBoneRelativePath = RigGenerator.ComputeRelativePath(smBones[j], assetAuthoring.transform);
                    var smBoneId           = (StringHash)smBoneRelativePath;

                    for (int k = 0; k != rigComponent.Bones.Length; ++k)
                    {
                        var relativePath = RigGenerator.ComputeRelativePath(rigComponent.Bones[k], rigComponent.transform);
                        var id           = (StringHash)relativePath;

                        if (smBoneId.Equals(id))
                        {
                            remap.Value = k;
                            break;
                        }
                    }
                    skeletonIndexArray[j] = remap;

                    var bindPose = meshRenderer.sharedMesh.bindposes[j];
                    bindPoseArray[j] = new BindPose {
                        Value = bindPose
                    };

                    var skinMat = math.mul(meshRenderer.bones[j].localToWorldMatrix, bindPose);
                    skinMatrices[j] = new SkinMatrix {
                        Value = new float3x4(skinMat.c0.xyz, skinMat.c1.xyz, skinMat.c2.xyz, skinMat.c3.xyz)
                    };
                }
            }
        }
    private RigRemapQuery CreateAutoRemapQuery([NotNull] RigComponent srcRig, [NotNull] RigComponent dstRig)
    {
        var translationChannels = new List <ChannelMap>();
        var rotationChannels    = new List <ChannelMap>();

        var translationOffsets = new List <RigTranslationOffset>();
        var rotationOffsets    = new List <RigRotationOffset>();

        var srcRootRotInv = math.inverse(srcRig.transform.rotation);
        var dstRootRotInv = math.inverse(dstRig.transform.rotation);

        for (var boneIter = 0; boneIter < srcRig.Bones.Length; boneIter++)
        {
            if (srcRig.Bones[boneIter].parent != null)
            {
                if (srcRig.Bones[boneIter].name == dstRig.Bones[boneIter].name)
                {
                    var srcPath = RigGenerator.ComputeRelativePath(srcRig.Bones[boneIter], srcRig.transform);
                    var dstPath = RigGenerator.ComputeRelativePath(dstRig.Bones[boneIter], dstRig.transform);

                    var dstParentRot = math.mul(dstRootRotInv, dstRig.Bones[boneIter].parent.transform.rotation);
                    var srcParentRot = math.mul(srcRootRotInv, srcRig.Bones[boneIter].parent.transform.rotation);
                    var rotation     = mathex.mul(math.inverse(dstParentRot), srcParentRot);

                    //Got to calculate the translation.
                    //Only applying translationOffset to the first bone (most cases the hips)
                    if (boneIter == 1)
                    {
                        var translationOffset = new RigTranslationOffset
                        {
                            Scale    = dstRig.Bones[boneIter].position.y / srcRig.Bones[boneIter].position.y,
                            Rotation = rotation
                        };

                        translationOffsets.Add(translationOffset);
                        translationChannels.Add(new ChannelMap
                        {
                            DestinationId = dstPath, SourceId = srcPath, OffsetIndex = translationOffsets.Count
                        });
                    }

                    //Got to calculate the rotation.
                    var dstRot = math.mul(dstRootRotInv, dstRig.Bones[boneIter].transform.rotation);
                    var srcRot = math.mul(srcRootRotInv, srcRig.Bones[boneIter].transform.rotation);

                    var rotationOffset = new RigRotationOffset
                    {
                        PreRotation  = rotation,
                        PostRotation = mathex.mul(math.inverse(srcRot), dstRot)
                    };

                    rotationOffsets.Add(rotationOffset);
                    rotationChannels.Add(new ChannelMap
                    {
                        SourceId = srcPath, DestinationId = dstPath, OffsetIndex = rotationOffsets.Count
                    });
                }
            }
        }

        var rigRemapQuery = new RigRemapQuery
        {
            TranslationChannels = new ChannelMap[translationChannels.Count],
            RotationChannels    = new ChannelMap[rotationChannels.Count],
            TranslationOffsets  = new RigTranslationOffset[translationChannels.Count + 1],
            RotationOffsets     = new RigRotationOffset[rotationChannels.Count + 1]
        };

        rigRemapQuery.TranslationOffsets[0] = new RigTranslationOffset();
        rigRemapQuery.RotationOffsets[0]    = new RigRotationOffset();

        for (var iter = 0; iter < translationChannels.Count; iter++)
        {
            rigRemapQuery.TranslationChannels[iter]    = translationChannels[iter];
            rigRemapQuery.TranslationOffsets[iter + 1] = translationOffsets[iter];
        }

        for (var iter = 0; iter < rotationChannels.Count; iter++)
        {
            rigRemapQuery.RotationChannels[iter]    = rotationChannels[iter];
            rigRemapQuery.RotationOffsets[iter + 1] = rotationOffsets[iter];
        }

        return(rigRemapQuery);
    }