public static GameObject CreateEditorContext() { GameObject EditorUMAContext = null; if (UnityEditor.BuildPipeline.isBuildingPlayer) { return(null); } if (Application.isPlaying) { if (Debug.isDebugBuild) { Debug.LogWarning("There was no UMAContext in this scene. Please add the UMA_DCS prefab to this scene before you try to generate an UMA."); } return(null); } if (Debug.isDebugBuild) { Debug.Log("UMA Recipe Editor created an UMAEditorContext to enable editing. This will auto delete once you have finished editing your recipe or you add the UMA_DCS prefab to this scene."); } //if there is already an EditorUMAContext use it if (UMAContext.FindInstance() != null) { if (UMAContext.FindInstance().gameObject.name == "UMAEditorContext") { EditorUMAContext = UMAContext.FindInstance().gameObject; //if the UMAContext itself is on this game object, it means this was created and not deleted by the previous version of 'CreateEditorContext' //(The new version creates the UMAContext on a child game object called 'UMAContext' so that UMAContext.FindInstance can find it properly) //so in this case delete all the components that would have been added from the found gameObject from the previous code if (EditorUMAContext.GetComponent <UMAContext>()) { UMAUtils.DestroySceneObject(EditorUMAContext.GetComponent <UMAContext>()); //should also make the instance null again if (EditorUMAContext.GetComponent <DynamicRaceLibrary>()) { UMAUtils.DestroySceneObject(EditorUMAContext.GetComponent <DynamicRaceLibrary>()); } if (EditorUMAContext.GetComponent <DynamicSlotLibrary>()) { UMAUtils.DestroySceneObject(EditorUMAContext.GetComponent <DynamicSlotLibrary>()); } if (EditorUMAContext.GetComponent <DynamicOverlayLibrary>()) { UMAUtils.DestroySceneObject(EditorUMAContext.GetComponent <DynamicOverlayLibrary>()); } if (EditorUMAContext.GetComponent <DynamicCharacterSystem>()) { UMAUtils.DestroySceneObject(EditorUMAContext.GetComponent <DynamicCharacterSystem>()); } if (EditorUMAContext.GetComponent <DynamicAssetLoader>()) { UMAUtils.DestroySceneObject(EditorUMAContext.GetComponent <DynamicAssetLoader>()); } } } else if (UMAContext.FindInstance().gameObject.transform.parent.gameObject.name == "UMAEditorContext") { EditorUMAContext = UMAContext.FindInstance().gameObject.transform.parent.gameObject; } } else if (GameObject.Find("UMAEditorContext")) { EditorUMAContext = GameObject.Find("UMAEditorContext"); } else { EditorUMAContext = new GameObject(); EditorUMAContext.name = "UMAEditorContext"; } //Make this GameObject not show up in the scene or save EditorUMAContext.hideFlags = HideFlags.DontSave | HideFlags.NotEditable; //if this gameobject does not contain an UMAContext add it - we have to call it UMAContext because UMAContext.FindInstance searches for that game object var thisUMAContext = UMAContext.Instance = EditorUMAContext.GetComponentInChildren <UMAContext>(); if (UMAContext.Instance == null) { var thisUMAContextGO = new GameObject(); thisUMAContextGO.name = "UMAContext"; thisUMAContextGO.transform.parent = EditorUMAContext.transform; thisUMAContext = thisUMAContextGO.AddComponent <UMAContext>(); UMAContext.Instance = thisUMAContext; } //we need to add the libraries as components of the game object too //and then set THOSE components to the umaContext component thisUMAContext.raceLibrary = thisUMAContext.gameObject.AddComponent <DynamicRaceLibrary>(); (thisUMAContext.raceLibrary as DynamicRaceLibrary).dynamicallyAddFromResources = true; (thisUMAContext.raceLibrary as DynamicRaceLibrary).dynamicallyAddFromAssetBundles = true; thisUMAContext.overlayLibrary = thisUMAContext.gameObject.AddComponent <DynamicOverlayLibrary>(); (thisUMAContext.overlayLibrary as DynamicOverlayLibrary).dynamicallyAddFromResources = true; (thisUMAContext.overlayLibrary as DynamicOverlayLibrary).dynamicallyAddFromAssetBundles = true; thisUMAContext.slotLibrary = thisUMAContext.gameObject.AddComponent <DynamicSlotLibrary>(); (thisUMAContext.slotLibrary as DynamicSlotLibrary).dynamicallyAddFromResources = true; (thisUMAContext.slotLibrary as DynamicSlotLibrary).dynamicallyAddFromAssetBundles = true; thisUMAContext.dynamicCharacterSystem = thisUMAContext.gameObject.AddComponent <DynamicCharacterSystem>(); (thisUMAContext.dynamicCharacterSystem as DynamicCharacterSystem).dynamicallyAddFromResources = true; (thisUMAContext.dynamicCharacterSystem as DynamicCharacterSystem).dynamicallyAddFromAssetBundles = true; var thisDAL = thisUMAContext.gameObject.AddComponent <DynamicAssetLoader>(); DynamicAssetLoader.Instance = thisDAL; return(EditorUMAContext); }
protected void EnsureUMADataSetup(UMAData umaData) { if (umaData.umaRoot == null) { GameObject newRoot = new GameObject("Root"); //make root of the UMAAvatar respect the layer setting of the UMAAvatar so cameras can just target this layer newRoot.layer = umaData.gameObject.layer; newRoot.transform.parent = umaData.transform; newRoot.transform.localPosition = Vector3.zero; newRoot.transform.localRotation = Quaternion.Euler(270f, 0, 0f); newRoot.transform.localScale = Vector3.one; umaData.umaRoot = newRoot; GameObject newGlobal = new GameObject("Global"); newGlobal.transform.parent = newRoot.transform; newGlobal.transform.localPosition = Vector3.zero; newGlobal.transform.localRotation = Quaternion.Euler(90f, 90f, 0f); umaData.skeleton = new UMASkeleton(newGlobal.transform); renderers = new SkinnedMeshRenderer[umaData.generatedMaterials.rendererCount]; for (int i = 0; i < umaData.generatedMaterials.rendererCount; i++) { renderers[i] = MakeRenderer(i, newGlobal.transform); } umaData.SetRenderers(renderers); } else { umaData.CleanMesh(false); if (umaData.rendererCount == umaData.generatedMaterials.rendererCount) { renderers = umaData.GetRenderers(); } else { var oldRenderers = umaData.GetRenderers(); var globalTransform = umaData.GetGlobalTransform(); renderers = new SkinnedMeshRenderer[umaData.generatedMaterials.rendererCount]; for (int i = 0; i < umaData.generatedMaterials.rendererCount; i++) { if (oldRenderers != null && oldRenderers.Length > i) { renderers[i] = oldRenderers[i]; continue; } renderers[i] = MakeRenderer(i, globalTransform); } if (oldRenderers != null) { for (int i = umaData.generatedMaterials.rendererCount; i < oldRenderers.Length; i++) { UMAUtils.DestroySceneObject(oldRenderers[i].gameObject); //For cloth, be aware of issue: 845868 //https://issuetracker.unity3d.com/issues/cloth-repeatedly-destroying-objects-with-cloth-components-causes-a-crash-in-unity-cloth-updatenormals } } umaData.SetRenderers(renderers); } } //Clear out old cloth components for (int i = 0; i < umaData.rendererCount; i++) { Cloth cloth = renderers[i].GetComponent <Cloth>(); if (cloth != null) { DestroyImmediate(cloth, false); //Crashes if trying to use Destroy() } } }
/// <summary> /// Apply the modifiers using the given dna (determined by the typehash) /// </summary> /// <param name="umaData"></param> /// <param name="skeleton"></param> /// <param name="dnaTypeHash"></param> public override void ApplyDNA(UMAData umaData, UMASkeleton skeleton, int dnaTypeHash) { var umaDna = umaData.GetDna(dnaTypeHash); var masterWeightCalc = masterWeight.GetWeight(umaDna); if (masterWeightCalc == 0f) { return; } for (int i = 0; i < _skeletonModifiers.Count; i++) { _skeletonModifiers[i].umaDNA = umaDna; var thisHash = (_skeletonModifiers[i].hash != 0) ? _skeletonModifiers[i].hash : UMAUtils.StringToHash(_skeletonModifiers[i].hashName); //check skeleton has the bone we want to change if (!skeleton.HasBone(thisHash)) { Debug.LogWarning("You were trying to apply skeleton modifications to a bone that didn't exist (" + _skeletonModifiers[i].hashName + ") on " + umaData.gameObject.name); continue; } //With these ValueX.x is the calculated value and ValueX.y is min and ValueX.z is max var thisValueX = _skeletonModifiers[i].CalculateValueX(umaDna); var thisValueY = _skeletonModifiers[i].CalculateValueY(umaDna); var thisValueZ = _skeletonModifiers[i].CalculateValueZ(umaDna); if (_skeletonModifiers[i].property == SkeletonModifier.SkeletonPropType.Position) { skeleton.SetPositionRelative(thisHash, new Vector3( Mathf.Clamp(thisValueX.x, thisValueX.y, thisValueX.z), Mathf.Clamp(thisValueY.x, thisValueY.y, thisValueY.z), Mathf.Clamp(thisValueZ.x, thisValueZ.y, thisValueZ.z)), masterWeightCalc); } else if (_skeletonModifiers[i].property == SkeletonModifier.SkeletonPropType.Rotation) { skeleton.SetRotationRelative(thisHash, Quaternion.Euler(new Vector3( Mathf.Clamp(thisValueX.x, thisValueX.y, thisValueX.z), Mathf.Clamp(thisValueY.x, thisValueY.y, thisValueY.z), Mathf.Clamp(thisValueZ.x, thisValueZ.y, thisValueZ.z))), masterWeightCalc); } else if (_skeletonModifiers[i].property == SkeletonModifier.SkeletonPropType.Scale) { //If there are two sets of skeletonModifiers and both are at 50% it needs to apply them both but the result should be cumulative //so we need to work out the difference this one is making, weight that and add it to the current scale of the bone var scale = new Vector3( Mathf.Clamp(thisValueX.x, thisValueX.y, thisValueX.z), Mathf.Clamp(thisValueY.x, thisValueY.y, thisValueY.z), Mathf.Clamp(thisValueZ.x, thisValueZ.y, thisValueZ.z)); //we cant use val.value here because the initial values always need to be applied var defaultVal = SkeletonModifier.skelAddDefaults[SkeletonModifier.SkeletonPropType.Scale].x; var scaleDiff = new Vector3(scale.x - defaultVal, scale.y - defaultVal, scale.z - defaultVal); var weightedScaleDiff = scaleDiff * masterWeightCalc; var fullScale = skeleton.GetScale(_skeletonModifiers[i].hash) + weightedScaleDiff; skeleton.SetScale(thisHash, fullScale); } } }
public static GameObject CreateEditorContext() { GameObject EditorUMAContextBase = null; if (UnityEditor.BuildPipeline.isBuildingPlayer) { return(null); } if (Application.isPlaying) { if (Debug.isDebugBuild) { Debug.LogWarning("There was no UMAContext in this scene. Please add the UMA_DCS prefab to this scene before you try to generate an UMA."); } return(null); } if (Debug.isDebugBuild) { Debug.Log("UMA Recipe Editor created an UMAEditorContext to enable editing. This will auto delete once you have finished editing your recipe or you add the UMA_DCS prefab to this scene."); } //if there is already an EditorUMAContextBase use it if (UMAContextBase.FindInstance() != null) { if (UMAContextBase.FindInstance().gameObject.name == "UMAEditorContext") { EditorUMAContextBase = UMAContextBase.FindInstance().gameObject; //if the UMAContextBase itself is on this game object, it means this was created and not deleted by the previous version of 'CreateEditorContext' //(The new version creates the UMAContextBase on a child game object called 'UMAContextBase' so that UMAContextBase.FindInstance can find it properly) //so in this case delete all the components that would have been added from the found gameObject from the previous code if (EditorUMAContextBase.GetComponent <UMAContextBase>()) { UMAUtils.DestroySceneObject(EditorUMAContextBase.GetComponent <UMAContextBase>()); //should also make the instance null again } } else if (UMAContextBase.FindInstance().gameObject.transform.parent.gameObject.name == "UMAEditorContext") { EditorUMAContextBase = UMAContextBase.FindInstance().gameObject.transform.parent.gameObject; } } else if (GameObject.Find("UMAEditorContext")) { EditorUMAContextBase = GameObject.Find("UMAEditorContext"); } else { EditorUMAContextBase = new GameObject(); EditorUMAContextBase.name = "UMAEditorContext"; } //Make this GameObject not show up in the scene or save EditorUMAContextBase.hideFlags = HideFlags.DontSave | HideFlags.NotEditable; //if this gameobject does not contain an UMAContextBase add it - we have to call it UMAContextBase because UMAContextBase.FindInstance searches for that game object var thisUMAContextBase = UMAContextBase.Instance = EditorUMAContextBase.GetComponentInChildren <UMAContextBase>(); if (UMAContextBase.Instance == null) { var thisUMAContextBaseGO = new GameObject(); thisUMAContextBaseGO.name = "UMAContext"; thisUMAContextBaseGO.transform.parent = EditorUMAContextBase.transform; thisUMAContextBase = thisUMAContextBaseGO.AddComponent <UMAGlobalContext>(); UMAContextBase.Instance = thisUMAContextBase; } return(EditorUMAContextBase); }
/// <summary> /// Combines a set of meshes into the target mesh. /// </summary> /// <param name="target">Target.</param> /// <param name="sources">Sources.</param> /// <param name="blendShapeSettings">BlendShape Settings.</param> public static void CombineMeshes(UMAMeshData target, CombineInstance[] sources, BlendShapeSettings blendShapeSettings = null) { if (blendShapeSettings == null) { blendShapeSettings = new BlendShapeSettings(); } int boneWeightCount = 0; int vertexCount = 0; int bindPoseCount = 0; int transformHierarchyCount = 0; Dictionary <string, BlendShapeVertexData> blendShapeNames = new Dictionary <string, BlendShapeVertexData>(); MeshComponents meshComponents = MeshComponents.none; int subMeshCount = FindTargetSubMeshCount(sources); var subMeshTriangleLength = new int[subMeshCount]; AnalyzeSources(sources, subMeshTriangleLength, ref vertexCount, ref boneWeightCount, ref bindPoseCount, ref transformHierarchyCount, ref meshComponents); if (!blendShapeSettings.ignoreBlendShapes) { AnalyzeBlendShapeSources(sources, blendShapeSettings, ref meshComponents, out blendShapeNames); } int[][] submeshTriangles = new int[subMeshCount][]; for (int i = 0; i < subMeshTriangleLength.Length; i++) { submeshTriangles[i] = target.GetSubmeshBuffer(subMeshTriangleLength[i], i); subMeshTriangleLength[i] = 0; } bool has_normals = (meshComponents & MeshComponents.has_normals) != MeshComponents.none; bool has_tangents = (meshComponents & MeshComponents.has_tangents) != MeshComponents.none; bool has_uv = (meshComponents & MeshComponents.has_uv) != MeshComponents.none; bool has_uv2 = (meshComponents & MeshComponents.has_uv2) != MeshComponents.none; bool has_uv3 = (meshComponents & MeshComponents.has_uv3) != MeshComponents.none; bool has_uv4 = (meshComponents & MeshComponents.has_uv4) != MeshComponents.none; bool has_colors32 = (meshComponents & MeshComponents.has_colors32) != MeshComponents.none; bool has_blendShapes = (meshComponents & MeshComponents.has_blendShapes) != MeshComponents.none; if (blendShapeSettings.ignoreBlendShapes) { has_blendShapes = false; } bool has_clothSkinning = (meshComponents & MeshComponents.has_clothSkinning) != MeshComponents.none; if (nativeBoneWeights.Length < boneWeightCount || nativeBonesPerVertex.Length < vertexCount) { CleanupNativeArrays(); //Debug.Log("Creating native arrays in mesh combiner"); nativeBoneWeights = new NativeArray <BoneWeight1>(boneWeightCount, Allocator.Persistent); nativeBonesPerVertex = new NativeArray <byte>(vertexCount, Allocator.Persistent); } Vector3[] vertices = EnsureArrayLength(target.vertices, vertexCount); Vector3[] normals = has_normals ? EnsureArrayLength(target.normals, vertexCount) : null; Vector4[] tangents = has_tangents ? EnsureArrayLength(target.tangents, vertexCount) : null; Vector2[] uv = has_uv ? EnsureArrayLength(target.uv, vertexCount) : null; Vector2[] uv2 = has_uv2 ? EnsureArrayLength(target.uv2, vertexCount) : null; Vector2[] uv3 = has_uv3 ? EnsureArrayLength(target.uv3, vertexCount) : null; Vector2[] uv4 = has_uv4 ? EnsureArrayLength(target.uv4, vertexCount) : null; Color32[] colors32 = has_colors32 ? EnsureArrayLength(target.colors32, vertexCount) : null; UMABlendShape[] blendShapes = has_blendShapes ? new UMABlendShape[blendShapeNames.Keys.Count] : null; UMATransform[] umaTransforms = EnsureArrayLength(target.umaBones, transformHierarchyCount); ClothSkinningCoefficient[] clothSkinning = has_clothSkinning ? EnsureArrayLength(target.clothSkinning, vertexCount) : null; Dictionary <Vector3, int> clothVertices = has_clothSkinning ? new Dictionary <Vector3, int>(vertexCount) : null; Dictionary <Vector3, int> localClothVertices = has_clothSkinning ? new Dictionary <Vector3, int>(vertexCount) : null; InitializeBlendShapeData(ref vertexCount, blendShapeNames, blendShapes); int boneCount = 0; foreach (var source in sources) { MergeSortedTransforms(umaTransforms, ref boneCount, source.meshData.umaBones); } int vertexIndex = 0; int boneWeightIndex = 0; if (bonesCollection == null) { bonesCollection = new Dictionary <int, BoneIndexEntry>(boneCount); } else { bonesCollection.Clear(); } if (bindPoses == null) { bindPoses = new List <Matrix4x4>(bindPoseCount); } else { bindPoses.Clear(); } if (bonesList == null) { bonesList = new List <int>(boneCount); } else { bonesList.Clear(); } foreach (var source in sources) { int sourceVertexCount = source.meshData.vertices.Length; BuildBoneWeights(source.meshData, nativeBoneWeights, nativeBonesPerVertex, vertexIndex, boneWeightIndex, sourceVertexCount, source.meshData.boneNameHashes, source.meshData.bindPoses, bonesCollection, bindPoses, bonesList); Array.Copy(source.meshData.vertices, 0, vertices, vertexIndex, sourceVertexCount); if (has_normals) { if (source.meshData.normals != null && source.meshData.normals.Length > 0) { Array.Copy(source.meshData.normals, 0, normals, vertexIndex, sourceVertexCount); } else { FillArray(tangents, vertexIndex, sourceVertexCount, Vector3.zero); } } if (has_tangents) { if (source.meshData.tangents != null && source.meshData.tangents.Length > 0) { Array.Copy(source.meshData.tangents, 0, tangents, vertexIndex, sourceVertexCount); } else { FillArray(tangents, vertexIndex, sourceVertexCount, Vector4.zero); } } if (has_uv) { if (source.meshData.uv != null && source.meshData.uv.Length >= sourceVertexCount) { Array.Copy(source.meshData.uv, 0, uv, vertexIndex, sourceVertexCount); } else { FillArray(uv, vertexIndex, sourceVertexCount, Vector4.zero); } } if (has_uv2) { if (source.meshData.uv2 != null && source.meshData.uv2.Length >= sourceVertexCount) { Array.Copy(source.meshData.uv2, 0, uv2, vertexIndex, sourceVertexCount); } else { FillArray(uv2, vertexIndex, sourceVertexCount, Vector4.zero); } } if (has_uv3) { if (source.meshData.uv3 != null && source.meshData.uv3.Length >= sourceVertexCount) { Array.Copy(source.meshData.uv3, 0, uv3, vertexIndex, sourceVertexCount); } else { FillArray(uv3, vertexIndex, sourceVertexCount, Vector4.zero); } } if (has_uv4) { if (source.meshData.uv4 != null && source.meshData.uv4.Length >= sourceVertexCount) { Array.Copy(source.meshData.uv4, 0, uv4, vertexIndex, sourceVertexCount); } else { FillArray(uv4, vertexIndex, sourceVertexCount, Vector4.zero); } } if (has_colors32) { if (source.meshData.colors32 != null && source.meshData.colors32.Length > 0) { Array.Copy(source.meshData.colors32, 0, colors32, vertexIndex, sourceVertexCount); } else { Color32 white32 = Color.white; FillArray(colors32, vertexIndex, sourceVertexCount, white32); } } if (has_blendShapes) { if (source.meshData.blendShapes != null && source.meshData.blendShapes.Length > 0) { int sourceBlendShapeLength = source.meshData.blendShapes.Length; for (int shapeIndex = 0; shapeIndex < sourceBlendShapeLength; shapeIndex++) { string shapeName = source.meshData.blendShapes[shapeIndex].shapeName; //If we aren't loading all blendshapes and we don't find the blendshape name in the list of explicit blendshapes to combine, then skip to the next one. if (!blendShapeSettings.loadAllBlendShapes && !blendShapeSettings.blendShapes.ContainsKey(shapeName)) { continue; } #region BlendShape Baking if (BakeBlendShape(blendShapeSettings.blendShapes, source.meshData.blendShapes[shapeIndex], ref vertexIndex, vertices, normals, tangents, has_normals, has_tangents)) { continue; //If we baked this blendshape, then continue to the next one and skip adding the regular blendshape. } #endregion //If our dictionary contains the shape name, which it should if (blendShapeNames.ContainsKey(shapeName)) { UMABlendShape[] sourceBlendShapes = source.meshData.blendShapes; int i = blendShapeNames[shapeName].index; if (blendShapes[i].frames.Length != sourceBlendShapes[shapeIndex].frames.Length) { if (Debug.isDebugBuild) { Debug.LogError("SkinnedMeshCombiner: mesh blendShape frame counts don't match!"); } break; } for (int frameIndex = 0; frameIndex < sourceBlendShapes[shapeIndex].frames.Length; frameIndex++) { Array.Copy(sourceBlendShapes[shapeIndex].frames[frameIndex].deltaVertices, 0, blendShapes[i].frames[frameIndex].deltaVertices, vertexIndex, sourceVertexCount); Vector3[] sourceDeltaNormals = sourceBlendShapes[shapeIndex].frames[frameIndex].deltaNormals; Vector3[] sourceDeltaTangents = sourceBlendShapes[shapeIndex].frames[frameIndex].deltaTangents; //if out dictionary says at least one source has normals or tangents and the current source has normals or tangents then copy them. if (blendShapeNames[shapeName].hasNormals && sourceDeltaNormals.Length > 0) { Array.Copy(sourceDeltaNormals, 0, blendShapes[i].frames[frameIndex].deltaNormals, vertexIndex, sourceVertexCount); } if (blendShapeNames[shapeName].hasTangents && sourceDeltaTangents.Length > 0) { Array.Copy(sourceDeltaTangents, 0, blendShapes[i].frames[frameIndex].deltaTangents, vertexIndex, sourceVertexCount); } } } else { if (Debug.isDebugBuild) { Debug.LogError("BlendShape " + shapeName + " not found in dictionary!"); } } } } } if (has_clothSkinning) { localClothVertices.Clear(); if (source.meshData.clothSkinningSerialized != null && source.meshData.clothSkinningSerialized.Length > 0) { for (int i = 0; i < source.meshData.vertexCount; i++) { var vertice = source.meshData.vertices[i]; if (!localClothVertices.ContainsKey(vertice)) { int localCount = localClothVertices.Count; localClothVertices.Add(vertice, localCount); if (!clothVertices.ContainsKey(vertice)) { ConvertData(ref source.meshData.clothSkinningSerialized[localCount], ref clothSkinning[clothVertices.Count]); clothVertices.Add(vertice, clothVertices.Count); } else { ConvertData(ref source.meshData.clothSkinningSerialized[localCount], ref clothSkinning[clothVertices[vertice]]); } } } } else { for (int i = 0; i < source.meshData.vertexCount; i++) { var vertice = source.meshData.vertices[i]; if (!clothVertices.ContainsKey(vertice)) { clothSkinning[clothVertices.Count].maxDistance = 0; clothSkinning[clothVertices.Count].collisionSphereDistance = float.MaxValue; clothVertices.Add(vertice, clothVertices.Count); localClothVertices.Add(vertice, clothVertices.Count); } } } } for (int i = 0; i < source.meshData.subMeshCount; i++) { if (source.targetSubmeshIndices[i] >= 0) { int[] subTriangles = source.meshData.submeshes[i].triangles; int triangleLength = subTriangles.Length; int destMesh = source.targetSubmeshIndices[i]; if (source.triangleMask == null) { CopyIntArrayAdd(subTriangles, 0, submeshTriangles[destMesh], subMeshTriangleLength[destMesh], triangleLength, vertexIndex); subMeshTriangleLength[destMesh] += triangleLength; } else { MaskedCopyIntArrayAdd(subTriangles, 0, submeshTriangles[destMesh], subMeshTriangleLength[destMesh], triangleLength, vertexIndex, source.triangleMask[i]); subMeshTriangleLength[destMesh] += (triangleLength - (UMAUtils.GetCardinality(source.triangleMask[i]) * 3)); } } } vertexIndex += sourceVertexCount; #if USE_NATIVE_ARRAYS boneWeightIndex += source.meshData.unityBoneWeights.Length; #else boneWeightIndex += source.meshData.ManagedBoneWeights.Length; #endif } if (vertexCount != vertexIndex) { if (Debug.isDebugBuild) { Debug.LogError("Combined vertices size didn't match precomputed value!"); } } // fill in new values. target.vertexCount = vertexCount; target.vertices = vertices; #if USE_NATIVE_ARRAYS target.unityBoneWeights = nativeBoneWeights.GetSubArray(0, boneWeightIndex); target.unityBonesPerVertex = nativeBonesPerVertex.GetSubArray(0, vertexCount); #else target.ManagedBoneWeights = nativeBoneWeights.GetSubArray(0, boneWeightIndex).ToArray(); target.ManagedBonesPerVertex = nativeBonesPerVertex.GetSubArray(0, vertexCount).ToArray(); #endif target.bindPoses = bindPoses.ToArray(); target.normals = normals; target.tangents = tangents; target.uv = uv; target.uv2 = uv2; target.uv3 = uv3; target.uv4 = uv4; target.colors32 = colors32; if (has_blendShapes) { target.blendShapes = blendShapes; } if (has_clothSkinning) { Array.Resize(ref clothSkinning, clothVertices.Count); } target.clothSkinning = clothSkinning; target.subMeshCount = subMeshCount; target.submeshes = new SubMeshTriangles[subMeshCount]; target.umaBones = umaTransforms; target.umaBoneCount = boneCount; for (int i = 0; i < subMeshCount; i++) { target.submeshes[i].triangles = submeshTriangles[i]; } target.boneNameHashes = bonesList.ToArray(); }
private static void AnalyzeSources(CombineInstance[] sources, int[] subMeshTriangleLength, ref int vertexCount, ref int boneweightcount, ref int bindPoseCount, ref int transformHierarchyCount, ref MeshComponents meshComponents) { for (int i = 0; i < subMeshTriangleLength.Length; i++) { subMeshTriangleLength[i] = 0; } foreach (var source in sources) { #if USE_NATIVE_ARRAYS boneweightcount += source.meshData.unityBoneWeights.Length; #else boneweightcount += source.meshData.ManagedBoneWeights.Length; #endif vertexCount += source.meshData.vertices.Length; bindPoseCount += source.meshData.bindPoses.Length; transformHierarchyCount += source.meshData.umaBones.Length; if (source.meshData.normals != null && source.meshData.normals.Length != 0) { meshComponents |= MeshComponents.has_normals; } if (source.meshData.tangents != null && source.meshData.tangents.Length != 0) { meshComponents |= MeshComponents.has_tangents; } if (source.meshData.uv != null && source.meshData.uv.Length != 0) { meshComponents |= MeshComponents.has_uv; } if (source.meshData.uv2 != null && source.meshData.uv2.Length != 0) { meshComponents |= MeshComponents.has_uv2; } if (source.meshData.uv3 != null && source.meshData.uv3.Length != 0) { meshComponents |= MeshComponents.has_uv3; } if (source.meshData.uv4 != null && source.meshData.uv4.Length != 0) { meshComponents |= MeshComponents.has_uv4; } if (source.meshData.colors32 != null && source.meshData.colors32.Length != 0) { meshComponents |= MeshComponents.has_colors32; } if (source.meshData.clothSkinningSerialized != null && source.meshData.clothSkinningSerialized.Length != 0) { meshComponents |= MeshComponents.has_clothSkinning; } for (int i = 0; i < source.meshData.subMeshCount; i++) { if (source.targetSubmeshIndices[i] >= 0) { int triangleLength = (source.triangleMask == null) ? source.meshData.submeshes[i].triangles.Length : (source.meshData.submeshes[i].triangles.Length - (UMAUtils.GetCardinality(source.triangleMask[i]) * 3)); subMeshTriangleLength[source.targetSubmeshIndices[i]] += triangleLength; } } } }
public static UMAMeshData ShallowInstanceMesh(UMAMeshData source, BitArray[] triangleMask = null) { var target = new UMAMeshData(); target.bindPoses = source.bindPoses; target.boneNameHashes = source.boneNameHashes; #if USE_NATIVE_ARRAYS target.unityBoneWeights = source.unityBoneWeights; target.unityBonesPerVertex = source.unityBonesPerVertex; #endif target.colors32 = source.colors32; target.normals = source.normals; target.rootBoneHash = source.rootBoneHash; target.subMeshCount = source.subMeshCount; target.tangents = source.tangents; target.umaBoneCount = source.umaBoneCount; target.umaBones = source.umaBones; target.uv = source.uv; target.uv2 = source.uv2; target.uv3 = source.uv3; target.uv4 = source.uv4; target.vertexCount = source.vertexCount; target.vertices = source.vertices; target.blendShapes = source.blendShapes; target.SlotName = source.SlotName + " (shallow copy)"; target.ManagedBonesPerVertex = source.ManagedBonesPerVertex; target.ManagedBoneWeights = source.ManagedBoneWeights; if (triangleMask != null) { target.submeshes = new SubMeshTriangles[source.subMeshCount]; for (int i = 0; i < source.subMeshCount; i++) { int sourceLength = source.submeshes[i].triangles.Length; int triangleLength = sourceLength - (UMAUtils.GetCardinality(triangleMask[i]) * 3); int[] destTriangles = new int[triangleLength]; MaskedCopyIntArrayAdd(source.submeshes[i].triangles, 0, destTriangles, 0, sourceLength, 0, triangleMask[i]); target.submeshes[i].triangles = destTriangles; } } else { target.submeshes = source.submeshes; } if (source.clothSkinningSerialized != null && source.clothSkinningSerialized.Length != 0) { target.clothSkinning = new ClothSkinningCoefficient[source.clothSkinningSerialized.Length]; for (int i = 0; i < source.clothSkinningSerialized.Length; i++) { ConvertData(ref source.clothSkinningSerialized[i], ref target.clothSkinning[i]); } } else { target.clothSkinning = null; } return(target); }
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { EditorGUI.BeginProperty(position, label, property); Init(property); if (!_manuallyConfigured) { if (this.fieldInfo != null) { var attrib = this.fieldInfo.GetCustomAttributes(typeof(BaseCharacterModifier.ConfigAttribute), true).FirstOrDefault() as BaseCharacterModifier.ConfigAttribute; if (attrib != null) { _alwaysExpanded = attrib.alwaysExpanded; } } } if (!_alwaysExpanded) { var foldoutRect = new Rect(position.xMin, position.yMin, position.width, (EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing)); property.isExpanded = EditorGUI.Foldout(foldoutRect, property.isExpanded, label); } if (property.isExpanded || _alwaysExpanded) { EditorGUI.indentLevel++; position = EditorGUI.IndentedRect(position); if (!_alwaysExpanded) { position.yMin = position.yMin + (EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing); } // Don't make child fields be indented var indent = EditorGUI.indentLevel; EditorGUI.indentLevel = 0; var adjustScaleProp = property.FindPropertyRelative("_adjustScale"); var adjustHeightProp = property.FindPropertyRelative("_adjustHeight"); var adjustRadiusProp = property.FindPropertyRelative("_adjustRadius"); var adjustMassProp = property.FindPropertyRelative("_adjustMass"); var updateBoundsProp = property.FindPropertyRelative("_updateBounds"); var tightenBoundsProp = property.FindPropertyRelative("_tightenBounds"); var adjustBoundsProp = property.FindPropertyRelative("_adjustBounds"); var scaleAdjustProp = property.FindPropertyRelative("_scale"); var scaleBoneProp = property.FindPropertyRelative("_bone"); var scaleBoneHashProp = property.FindPropertyRelative("_scaleBoneHash"); var headRatioProp = property.FindPropertyRelative("_headRatio"); var radiusAdjustYProp = property.FindPropertyRelative("_radiusAdjustY"); var radiusAdjustProp = property.FindPropertyRelative("_radiusAdjust"); var massAdjustProp = property.FindPropertyRelative("_massAdjust"); var boundAdjustProp = property.FindPropertyRelative("_boundsAdjust"); //overall rects var scaleRect = new Rect(position.xMin, position.yMin, position.width, (EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing)); var heightRect = new Rect(position.xMin, scaleRect.yMax, position.width, (EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing)); var radiusRect = new Rect(position.xMin, heightRect.yMax, position.width, (EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing)); var massRect = new Rect(position.xMin, radiusRect.yMax, position.width, (EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing)); var boundsBoolsRect = new Rect(position.xMin, massRect.yMax, position.width, (EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing)); //labelsRects are all 120f wide or maybe 1/3rd or whatever unity makes them by standard var scaleLabelRect = new Rect(position.xMin, scaleRect.yMin, (position.width / 3), scaleRect.height); var heightLabelRect = new Rect(position.xMin, heightRect.yMin, (position.width / 3), scaleRect.height); var radiusLabelRect = new Rect(position.xMin, radiusRect.yMin, (position.width / 3), scaleRect.height); var massLabelRect = new Rect(position.xMin, massRect.yMin, (position.width / 3), scaleRect.height); var boundsLabelRect = new Rect(position.xMin, boundsBoolsRect.yMin, (position.width / 3), scaleRect.height); var boundsLabel1Rect = new Rect(boundsLabelRect.xMax, boundsBoolsRect.yMin, (position.width / 3), scaleRect.height); var boundsLabel2Rect = new Rect(position.xMin, boundsBoolsRect.yMax, (position.width / 3), scaleRect.height); //fieldsRects are whatever is left var scaleFieldsRect = new Rect(scaleLabelRect.xMax, scaleRect.yMin, (position.width / 3) * 2, scaleRect.height); var scaleFields1FieldRect = new Rect(scaleFieldsRect.xMin, scaleFieldsRect.yMin, (scaleFieldsRect.width / 3), EditorGUIUtility.singleLineHeight); var scaleFields2LabelRect = new Rect(scaleFields1FieldRect.xMax, scaleFieldsRect.yMin, 40f, EditorGUIUtility.singleLineHeight); var scaleFields2FieldRect = new Rect(scaleFields2LabelRect.xMax, scaleFieldsRect.yMin, ((scaleFieldsRect.width / 3) * 2) - 40f, EditorGUIUtility.singleLineHeight); var heightFieldsRect = new Rect(scaleLabelRect.xMax, heightRect.yMin, (position.width / 3) * 2, scaleRect.height); var heightFields1FieldRect = new Rect(heightFieldsRect.xMin, heightFieldsRect.yMin, ((heightFieldsRect.width / 3) * 2), EditorGUIUtility.singleLineHeight); var heightFields2FieldRect = new Rect(heightFields1FieldRect.xMax, heightFieldsRect.yMin, (heightFieldsRect.width / 3), EditorGUIUtility.singleLineHeight); var radiusFieldsRect = new Rect(scaleLabelRect.xMax, radiusRect.yMin, (position.width / 3) * 2, scaleRect.height); var radiusFields1FieldRect = new Rect(radiusFieldsRect.xMin, radiusFieldsRect.yMin, (radiusFieldsRect.width / 2), EditorGUIUtility.singleLineHeight); var radiusFields2FieldRect = new Rect(radiusFields1FieldRect.xMax, radiusFieldsRect.yMin, (radiusFieldsRect.width / 2), EditorGUIUtility.singleLineHeight); var massFieldsRect = new Rect(scaleLabelRect.xMax, massRect.yMin, (position.width / 3) * 2, scaleRect.height); var boundsFieldsRect = new Rect(scaleLabelRect.xMax, boundsBoolsRect.yMax, (position.width / 3) * 2, scaleRect.height); float prevlabelWidth = EditorGUIUtility.labelWidth; //you loose the tooltips when you do toggleLeft WTFF?!? //none of the tooltips show in play mode either!! What a f*****g pain... var scaleLabel = EditorGUI.BeginProperty(scaleLabelRect, new GUIContent(adjustScaleProp.displayName), adjustScaleProp); adjustScaleProp.boolValue = EditorGUI.ToggleLeft(scaleLabelRect, scaleLabel, adjustScaleProp.boolValue); EditorGUI.EndProperty(); EditorGUI.BeginDisabledGroup(!adjustScaleProp.boolValue); EditorGUIUtility.labelWidth = 40f; if (Application.isPlaying && _target.liveScale != -1) { EditorGUI.BeginDisabledGroup(true); EditorGUI.LabelField(scaleFields1FieldRect, new GUIContent("Scale", "The live scale is being modified by a converter above. Please exit playmode if you want to edit the base scale")); var fieldRect = new Rect(scaleFields1FieldRect.xMin + 40f, scaleFields1FieldRect.yMin, scaleFields1FieldRect.width - 40f, scaleFields1FieldRect.height); EditorGUI.FloatField(fieldRect, _target.liveScale); EditorGUI.EndDisabledGroup(); } else { scaleAdjustProp.floatValue = EditorGUI.FloatField(scaleFields1FieldRect, "Scale", scaleAdjustProp.floatValue); } EditorGUI.LabelField(scaleFields2LabelRect, " Bone"); EditorGUI.BeginChangeCheck(); //I want this to draw a bone selection popup when it can- i.e. when drawn inside a ConverterBehaviour and we are in play mode. scaleBoneProp.stringValue = EditorGUI.TextField(scaleFields2FieldRect, scaleBoneProp.stringValue); if (EditorGUI.EndChangeCheck()) { scaleBoneHashProp.intValue = UMAUtils.StringToHash(scaleBoneProp.stringValue); } EditorGUI.EndDisabledGroup(); var heightLabel = EditorGUI.BeginProperty(heightLabelRect, new GUIContent(adjustHeightProp.displayName), adjustHeightProp); adjustHeightProp.boolValue = EditorGUI.ToggleLeft(heightLabelRect, heightLabel, adjustHeightProp.boolValue); EditorGUI.EndProperty(); EditorGUI.BeginDisabledGroup(!adjustHeightProp.boolValue); EditorGUIUtility.labelWidth = 80f; headRatioProp.floatValue = EditorGUI.FloatField(heightFields1FieldRect, "Head Ratio", headRatioProp.floatValue); EditorGUIUtility.labelWidth = 20f; radiusAdjustYProp.floatValue = EditorGUI.FloatField(heightFields2FieldRect, " Y ", radiusAdjustYProp.floatValue); EditorGUI.EndDisabledGroup(); var radiusLabel = EditorGUI.BeginProperty(radiusLabelRect, new GUIContent(adjustRadiusProp.displayName), adjustRadiusProp); adjustRadiusProp.boolValue = EditorGUI.ToggleLeft(radiusLabelRect, radiusLabel, adjustRadiusProp.boolValue); EditorGUI.EndProperty(); EditorGUI.BeginDisabledGroup(!adjustRadiusProp.boolValue); var radiusV2 = radiusAdjustProp.vector2Value; EditorGUI.BeginChangeCheck(); radiusV2.x = EditorGUI.FloatField(radiusFields1FieldRect, "X ", radiusV2.x); radiusV2.y = EditorGUI.FloatField(radiusFields2FieldRect, " Z ", radiusV2.y); if (EditorGUI.EndChangeCheck()) { radiusAdjustProp.vector2Value = radiusV2; } EditorGUI.EndDisabledGroup(); var massLabel = EditorGUI.BeginProperty(massLabelRect, new GUIContent(adjustMassProp.displayName), adjustMassProp); adjustMassProp.boolValue = EditorGUI.ToggleLeft(massLabelRect, massLabel, adjustMassProp.boolValue); EditorGUI.EndProperty(); EditorGUI.BeginDisabledGroup(!adjustMassProp.boolValue); EditorGUI.PropertyField(massFieldsRect, massAdjustProp, GUIContent.none); EditorGUI.EndDisabledGroup(); var boundsLabel = EditorGUI.BeginProperty(boundsLabelRect, new GUIContent(updateBoundsProp.displayName), updateBoundsProp); updateBoundsProp.boolValue = EditorGUI.ToggleLeft(boundsLabelRect, boundsLabel, updateBoundsProp.boolValue); EditorGUI.EndProperty(); var boundsLabel1 = EditorGUI.BeginProperty(boundsLabelRect, new GUIContent(tightenBoundsProp.displayName), tightenBoundsProp); tightenBoundsProp.boolValue = EditorGUI.ToggleLeft(boundsLabel1Rect, boundsLabel1, tightenBoundsProp.boolValue); EditorGUI.EndProperty(); var boundsLabel2 = EditorGUI.BeginProperty(boundsLabel2Rect, new GUIContent(adjustBoundsProp.displayName), adjustBoundsProp); adjustBoundsProp.boolValue = EditorGUI.ToggleLeft(boundsLabel2Rect, boundsLabel2, adjustBoundsProp.boolValue); EditorGUI.EndProperty(); EditorGUI.BeginDisabledGroup(!adjustBoundsProp.boolValue); EditorGUI.PropertyField(boundsFieldsRect, boundAdjustProp, GUIContent.none); EditorGUI.EndDisabledGroup(); property.serializedObject.ApplyModifiedProperties(); EditorGUIUtility.labelWidth = prevlabelWidth; // Set indent back to what it was EditorGUI.indentLevel = indent; EditorGUI.indentLevel--; } EditorGUI.EndProperty(); }
public override void Prepare() { if (builtHashes) { return; } /* * man's config must be same with woman's config, * you must change this script if you change 3d resources' names */ headAdjustHash = UMAUtils.StringToHash("HeadAdjust"); neckAdjustHash = UMAUtils.StringToHash("NeckAdjust"); leftOuterBreastHash = UMAUtils.StringToHash("LeftOuterBreast"); rightOuterBreastHash = UMAUtils.StringToHash("RightOuterBreast"); leftEyeHash = UMAUtils.StringToHash("LeftEye"); rightEyeHash = UMAUtils.StringToHash("RightEye"); leftEyeAdjustHash = UMAUtils.StringToHash("LeftEyeAdjust"); rightEyeAdjustHash = UMAUtils.StringToHash("RightEyeAdjust"); spine1AdjustHash = UMAUtils.StringToHash("Spine1Adjust"); spineAdjustHash = UMAUtils.StringToHash("SpineAdjust"); lowerBackBellyHash = UMAUtils.StringToHash("LowerBackBelly"); lowerBackAdjustHash = UMAUtils.StringToHash("LowerBackAdjust"); leftTrapeziusHash = UMAUtils.StringToHash("LeftTrapezius"); rightTrapeziusHash = UMAUtils.StringToHash("RightTrapezius"); leftArmAdjustHash = UMAUtils.StringToHash("LeftArmAdjust"); rightArmAdjustHash = UMAUtils.StringToHash("RightArmAdjust"); leftForeArmAdjustHash = UMAUtils.StringToHash("LeftForeArmAdjust"); rightForeArmAdjustHash = UMAUtils.StringToHash("RightForeArmAdjust"); leftForeArmTwistAdjustHash = UMAUtils.StringToHash("LeftForeArmTwistAdjust"); rightForeArmTwistAdjustHash = UMAUtils.StringToHash("RightForeArmTwistAdjust"); leftShoulderAdjustHash = UMAUtils.StringToHash("LeftShoulderAdjust"); rightShoulderAdjustHash = UMAUtils.StringToHash("RightShoulderAdjust"); leftUpLegAdjustHash = UMAUtils.StringToHash("LeftUpLegAdjust"); rightUpLegAdjustHash = UMAUtils.StringToHash("RightUpLegAdjust"); leftLegAdjustHash = UMAUtils.StringToHash("LeftLegAdjust"); rightLegAdjustHash = UMAUtils.StringToHash("RightLegAdjust"); leftGluteusHash = UMAUtils.StringToHash("LeftGluteus"); rightGluteusHash = UMAUtils.StringToHash("RightGluteus"); leftEarAdjustHash = UMAUtils.StringToHash("LeftEarAdjust"); rightEarAdjustHash = UMAUtils.StringToHash("RightEarAdjust"); noseBaseAdjustHash = UMAUtils.StringToHash("NoseBaseAdjust"); noseMiddleAdjustHash = UMAUtils.StringToHash("NoseMiddleAdjust"); leftNoseAdjustHash = UMAUtils.StringToHash("LeftNoseAdjust"); rightNoseAdjustHash = UMAUtils.StringToHash("RightNoseAdjust"); upperLipsAdjustHash = UMAUtils.StringToHash("UpperLipsAdjust"); mandibleAdjustHash = UMAUtils.StringToHash("MandibleAdjust"); leftLowMaxilarAdjustHash = UMAUtils.StringToHash("LeftLowMaxilarAdjust"); rightLowMaxilarAdjustHash = UMAUtils.StringToHash("RightLowMaxilarAdjust"); leftCheekAdjustHash = UMAUtils.StringToHash("LeftCheekAdjust"); rightCheekAdjustHash = UMAUtils.StringToHash("RightCheekAdjust"); leftLowCheekAdjustHash = UMAUtils.StringToHash("LeftLowCheekAdjust"); rightLowCheekAdjustHash = UMAUtils.StringToHash("RightLowCheekAdjust"); noseTopAdjustHash = UMAUtils.StringToHash("NoseTopAdjust"); leftEyebrowLowAdjustHash = UMAUtils.StringToHash("LeftEyebrowLowAdjust"); rightEyebrowLowAdjustHash = UMAUtils.StringToHash("RightEyebrowLowAdjust"); leftEyebrowMiddleAdjustHash = UMAUtils.StringToHash("LeftEyebrowMiddleAdjust"); rightEyebrowMiddleAdjustHash = UMAUtils.StringToHash("RightEyebrowMiddleAdjust"); leftEyebrowUpAdjustHash = UMAUtils.StringToHash("LeftEyebrowUpAdjust"); rightEyebrowUpAdjustHash = UMAUtils.StringToHash("RightEyebrowUpAdjust"); lipsSuperiorAdjustHash = UMAUtils.StringToHash("LipsSuperiorAdjust"); lipsInferiorAdjustHash = UMAUtils.StringToHash("LipsInferiorAdjust"); leftLipsSuperiorMiddleAdjustHash = UMAUtils.StringToHash("LeftLipsSuperiorMiddleAdjust"); rightLipsSuperiorMiddleAdjustHash = UMAUtils.StringToHash("RightLipsSuperiorMiddleAdjust"); leftLipsInferiorAdjustHash = UMAUtils.StringToHash("LeftLipsInferiorAdjust"); rightLipsInferiorAdjustHash = UMAUtils.StringToHash("RightLipsInferiorAdjust"); leftLipsAdjustHash = UMAUtils.StringToHash("LeftLipsAdjust"); rightLipsAdjustHash = UMAUtils.StringToHash("RightLipsAdjust"); globalHash = UMAUtils.StringToHash("Global"); positionHash = UMAUtils.StringToHash("Position"); lowerBackHash = UMAUtils.StringToHash("LowerBack"); //headHash = UMAUtils.StringToHash("Head"); headHash = UMAUtils.StringToHash("Bip001 Head"); leftArmHash = UMAUtils.StringToHash("LeftArm"); rightArmHash = UMAUtils.StringToHash("RightArm"); leftForeArmHash = UMAUtils.StringToHash("LeftForeArm"); rightForeArmHash = UMAUtils.StringToHash("RightForeArm"); leftHandHash = UMAUtils.StringToHash("LeftHand"); rightHandHash = UMAUtils.StringToHash("RightHand"); //leftFootHash = UMAUtils.StringToHash("LeftFoot"); leftFootHash = UMAUtils.StringToHash("Bip001 L Foot"); //rightFootHash = UMAUtils.StringToHash("RightFoot"); rightFootHash = UMAUtils.StringToHash("Bip001 R Foot"); leftUpLegHash = UMAUtils.StringToHash("LeftUpLeg"); rightUpLegHash = UMAUtils.StringToHash("RightUpLeg"); leftShoulderHash = UMAUtils.StringToHash("LeftShoulder"); rightShoulderHash = UMAUtils.StringToHash("RightShoulder"); mandibleHash = UMAUtils.StringToHash("Mandible"); builtHashes = true; }