public MeshContainer(SkinnedMeshRenderer skinnedRenderer) { Mesh = skinnedRenderer.sharedMesh; transform = skinnedRenderer.GetComponent<Transform>(); bones = skinnedRenderer.bones; isAnimated = true; }
public EJMMesh(SkinnedMeshRenderer component){ isSkinned = true; _smRenderer = component; mRenderer = component.GetComponent<Renderer> (); targettransform = component.transform; SetSharedMesh (_smRenderer.sharedMesh); RefreshMatrix (); }
public static void CreateControlPoints(SkinnedMeshRenderer skin) { if (skin.sharedMesh != null) { Skin2D skin2D = skin.GetComponent<Skin2D>(); if (skin2D != null) { skin2D.controlPoints = new ControlPoints.Point[skin.sharedMesh.vertices.Length]; if (skin2D.points == null) { skin2D.points = skin2D.gameObject.AddComponent<ControlPoints>(); } } for (int i = 0; i < skin.sharedMesh.vertices.Length; i++) { Vector3 originalPos = skin.sharedMesh.vertices[i]; if (skin2D != null) { skin2D.controlPoints[i] = new ControlPoints.Point(originalPos); skin2D.controlPoints[i].index = i; skin2D.points.SetPoint(skin2D.controlPoints[i]); } GameObject b = new GameObject(skin.name + " Control Point"); // Unparent the skin temporarily before adding the control point Transform skinParent = skin.transform.parent; skin.transform.parent = null; // Reset the rotation before creating the mesh so the UV's will align properly Quaternion localRotation = skin.transform.localRotation; skin.transform.localRotation = Quaternion.identity; b.transform.position = new Vector3(skin.transform.position.x + (skin.sharedMesh.vertices[i].x * skin.transform.localScale.x), skin.transform.position.y + (skin.sharedMesh.vertices[i].y * skin.transform.localScale.y), skin.transform.position.z + (skin.sharedMesh.vertices[i].z * skin.transform.localScale.z)); b.transform.parent = skin.transform; ControlPoint[] points = b.transform.parent.transform.GetComponentsInChildren<ControlPoint>(); if (points != null && points.Length > 0) { b.gameObject.name = b.gameObject.name + points.Length; } Undo.RegisterCreatedObjectUndo(b, "Add control point"); ControlPoint controlPoint = b.AddComponent<ControlPoint>(); controlPoint.index = i; controlPoint.skin = skin; controlPoint.originalPosition = b.transform.localPosition; // Reset the rotations of the object skin.transform.localRotation = localRotation; skin.transform.parent = skinParent; } } }
//--------------------------------------------------------------------------- INIT public override void OnDeserializeFromBlender() { base.OnDeserializeFromBlender(); // First call the CBMesh base class to serialize the mesh itself... _oSkinMeshRendNow = (SkinnedMeshRenderer)CUtility.FindOrCreateComponent(transform, typeof(SkinnedMeshRenderer)); ////=== Send blender command to obtain the skinned mesh info === CByteArray oBA = new CByteArray("'CBody'", _oBodyBase._sBlenderInstancePath_CBodyBase + _sNameCBodyInstanceMember + ".Unity_GetMesh_SkinnedInfo()"); //===== RECEIVE SKINNING INFORMATION ===== Matrix4x4[] aSkinBindPoses = null; //###WEAK: A bit of unfortunate structure imposed on the code because of how late we read the stream versus how we must init vars for skinned mesh... Can be improved. Transform[] aSkinBones = null; List<BoneWeight> aBoneWeights = null; //=== Read in the flat array of names of vertex groups. This will enable us to map the Blender vertex group blends to our fixed Unity bone rig === byte nVertGroupsMesh = oBA.ReadByte(); aSkinBones = new Transform[nVertGroupsMesh]; aSkinBindPoses = new Matrix4x4[nVertGroupsMesh]; //=== Find the root of our skinning bones from our body's main skinned mesh === Transform oNodeBoneParentRoot = CUtility.FindChild(_oBodyBase._oBodyRootGO.transform, "Bones"); if (oNodeBoneParentRoot == null) CUtility.ThrowException("CBMesh is attempting to reconstruct a skinned mesh but was not able to find root node of bones!"); for (byte nVertGroup = 0; nVertGroup < nVertGroupsMesh; nVertGroup++) { string sVertGroup = oBA.ReadString(); Transform oNodeBone = CUtility.FindNodeByName(oNodeBoneParentRoot, sVertGroup); //###OPT: Recursive on hundreds of bones for hundreds of bones = O squared!! ###IMPROVE: Find by direct path! if (oNodeBone != null) { //Debug.Log("Bone found for '" + sVertGroup + "'"); aSkinBones[nVertGroup] = oNodeBone; aSkinBindPoses[nVertGroup] = oNodeBone.worldToLocalMatrix; //###CHECK } else { Debug.LogError("**ERROR: CBMesh.ctor() could not find bone '" + sVertGroup + "'"); //###DESIGN?: Throw? } } //=== Stream in the bone weight info === ###NOTE: Note that this code assumes a compatible bone tree between Blender and Unity!!! To update Unity bones from Blender bones use the CBodyEd.UpdateBonesFromBlender() function aBoneWeights = new List<BoneWeight>(); int[] aBoneIndex = new int[32]; // Temp arrays float[] aBoneWeight = new float[32]; int nErrSumOutOfRange = 0; int nVerts = GetNumVerts(); for (int nVert = 0; nVert < nVerts; nVert++) { byte nVertGroups = oBA.ReadByte(); float nBoneWeightSum = 0; if (nVertGroups >= 5) { Debug.LogWarningFormat("Warning: Skinned mesh '{0}' at vert {1} has {2} vert groups!", _sNameBlenderMesh, nVert, nVertGroups); CUtility.ThrowException("CBMesh.ctor() encountered a vertex with " + nVertGroups + " vertex groups!"); } for (byte nVertGroup = 0; nVertGroup < nVertGroups; nVertGroup++) { //###IMPROVE: It might be more elegant to shift this code to Blender Python? aBoneIndex[nVertGroup] = oBA.ReadByte(); float nBoneWeight = oBA.ReadFloat(); if (nBoneWeight < 0) //Debug.LogError("CBMesh.ctor() encountered a bone weight below 0 at vert " + nVert + " and vert group " + nVertGroup); CUtility.ThrowException("CBSkin.ctor() encountered a bone weight below 0 at vert " + nVert + " and vert group " + nVertGroup); if (nBoneWeight > 1) //CUtility.ThrowException("CBMesh.ctor() encountered a bone weight over 1 at vert " + nVert + " and vert group " + nVertGroup); //###IMPROVE: Common! What to do? cap?? Debug.LogWarning("CBSkin.ctor() encountered a bone weight over 1 = " + nBoneWeight + " at vert " + nVert + " and vert group " + nVertGroup); //###IMPROVE: Common! What to do? cap?? aBoneWeight[nVertGroup] = nBoneWeight; nBoneWeightSum += nBoneWeight; } if (nBoneWeightSum < 0.999 || nBoneWeightSum > 1.001) { Debug.LogWarning("###W: CBMesh.ctor() vertex " + nVert + " had out of range weight of " + nBoneWeightSum); nErrSumOutOfRange++; } BoneWeight oBoneWeight = new BoneWeight(); if (nVertGroups > 0) { oBoneWeight.boneIndex0 = aBoneIndex[0]; oBoneWeight.weight0 = aBoneWeight[0]; } if (nVertGroups > 1) { oBoneWeight.boneIndex1 = aBoneIndex[1]; oBoneWeight.weight1 = aBoneWeight[1]; } if (nVertGroups > 2) { oBoneWeight.boneIndex2 = aBoneIndex[2]; oBoneWeight.weight2 = aBoneWeight[2]; } if (nVertGroups > 3) { oBoneWeight.boneIndex3 = aBoneIndex[3]; oBoneWeight.weight3 = aBoneWeight[3]; } aBoneWeights.Add(oBoneWeight); } if (nErrSumOutOfRange > 0) //###CHECK: What to do??? Debug.LogWarning("###ERROR: CBSkin.ctor() found " + nErrSumOutOfRange + " bones with out-of-range sums!"); //=== Read the number of errors detected when sending over the blender bone weights... what to do?? === int nErrorsBoneGroups = oBA.ReadInt(); if (nErrorsBoneGroups > 0) //###IMPROVE ###CHECK: What to do??? Debug.LogError("###ERROR: CBSkin.ctor() detected " + nErrorsBoneGroups + " blender-side errors while reading in blender mesh!"); oBA.CheckMagicNumber_End(); //=== Finalize the mesh creation by stuffing _oMeshRender into mesh filter or skinned mesh renderer as appropriate === UpdateNormals(); // Fix the normals with the just-serialized map of shared normals _oMeshNow.bindposes = aSkinBindPoses; _oMeshNow.boneWeights = aBoneWeights.ToArray(); _oSkinMeshRendNow.sharedMesh = _oMeshNow; //###TODO: skinned mesh complex bounds! _oSkinMeshRendNow.materials = _aMats; _oSkinMeshRendNow.bones = aSkinBones; //=== Conveniently reset skinned mesh renderer flags we always keep constant... makes it easier to override the defaults which go the other way === _oSkinMeshRendNow.updateWhenOffscreen = true; //###OPT: Should some mesh have this off? _oSkinMeshRendNow.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.On; //###PERF: Runtime performance of shadows? (All done in different quality settings?) _oSkinMeshRendNow.receiveShadows = true; if (_oSkinMeshRendNow.GetComponent<Collider>() != null) //###CHECK: ReleaseGlobalHandles mesh collider here if it exists at gameplay Destroy(_oSkinMeshRendNow.GetComponent<Collider>()); }
private static string SkinnedMeshToString(SkinnedMeshRenderer skRenderer, Dictionary<string, ObjMaterial> materialList) { Mesh mTemp = skRenderer.sharedMesh; Vector3[] vertices = new Vector3[mTemp.vertexCount]; Vector3[] normals = new Vector3[mTemp.vertexCount]; Matrix4x4[] boneMatrices = new Matrix4x4[skRenderer.bones.Length]; for (int i = 0; i < boneMatrices.Length; i++) { boneMatrices[i] = skRenderer.bones[i].localToWorldMatrix * mTemp.bindposes[i]; } for (int i = 0; i < mTemp.vertexCount; i++) { BoneWeight weight = mTemp.boneWeights[i]; Matrix4x4 bm0 = boneMatrices[weight.boneIndex0]; Matrix4x4 bm1 = boneMatrices[weight.boneIndex1]; Matrix4x4 bm2 = boneMatrices[weight.boneIndex2]; Matrix4x4 bm3 = boneMatrices[weight.boneIndex3]; Matrix4x4 vertexMatrix = new Matrix4x4(); vertexMatrix = Matrix4x4.identity; for (int n=0; n<16; n++) { vertexMatrix[n] = bm0[n] * weight.weight0 + bm1[n] * weight.weight1 + bm2[n] * weight.weight2 + bm3[n] * weight.weight3; } /* Vector3 pos0 = bm0.MultiplyPoint(mTemp.vertices[i]); Vector3 pos1 = bm1.MultiplyPoint(mTemp.vertices[i]); Vector3 pos2 = bm2.MultiplyPoint(mTemp.vertices[i]); Vector3 pos3 = bm3.MultiplyPoint(mTemp.vertices[i]); Vector3 pos = pos0 * weight.weight0 + pos1 * weight.weight1 + pos2 * weight.weight2 + pos3 * weight.weight3; vertices[i] = pos; */ vertices[i] = vertexMatrix.MultiplyPoint (mTemp.vertices[i]); normals[i] = vertexMatrix.MultiplyVector (mTemp.normals[i]); } Mesh m = new Mesh(); m.vertices = vertices; m.normals = normals; m.uv = mTemp.uv; m.triangles = mTemp.triangles; m.subMeshCount = mTemp.subMeshCount; m.RecalculateBounds(); m.RecalculateNormals(); Material[] mats = skRenderer.GetComponent<Renderer>().sharedMaterials; StringBuilder sb = new StringBuilder(); sb.Append("g ").Append(skRenderer.gameObject.name).Append("\n"); foreach(Vector3 lv in m.vertices) { Vector3 wv = skRenderer.transform.TransformPoint(lv); //This is sort of ugly - inverting x-component since we're in //a different coordinate system than "everyone" is "used to". sb.Append(string.Format("v {0} {1} {2}\n",-wv.x,wv.y,wv.z)); } sb.Append("\n"); foreach(Vector3 lv in m.normals) { Vector3 wv = skRenderer.transform.TransformDirection(lv); sb.Append(string.Format("vn {0} {1} {2}\n",-wv.x,wv.y,wv.z)); } sb.Append("\n"); foreach(Vector3 v in m.uv) { sb.Append(string.Format("vt {0} {1}\n",v.x,v.y)); } for (int material=0; material < m.subMeshCount; material ++) { sb.Append("\n"); sb.Append("usemtl ").Append(mats[material].name).Append("\n"); sb.Append("usemap ").Append(mats[material].name).Append("\n"); //See if this material is already in the materiallist. try { ObjMaterial objMaterial = new ObjMaterial(); objMaterial.name = mats[material].name; if (mats[material].mainTexture) objMaterial.textureName = AssetDatabase.GetAssetPath(mats[material].mainTexture); else objMaterial.textureName = null; materialList.Add(objMaterial.name, objMaterial); } catch (ArgumentException) { //Already in the dictionary } int[] triangles = m.GetTriangles(material); for (int i=0;i<triangles.Length;i+=3) { //Because we inverted the x-component, we also needed to alter the triangle winding. sb.Append(string.Format("f {1}/{1}/{1} {0}/{0}/{0} {2}/{2}/{2}\n", triangles[i]+1 + vertexOffset, triangles[i+1]+1 + normalOffset, triangles[i+2]+1 + uvOffset)); } } vertexOffset += m.vertices.Length; normalOffset += m.normals.Length; uvOffset += m.uv.Length; return sb.ToString(); }