Beispiel #1
0
    void ReadBone(ref CByteArray oBA, Transform oBoneParent)
    {
        // Precise opposite of gBlender.Stream_SendBone(): Reads a bone from Blender, finds (or create) equivalent Unity bone and updates position
        string sBoneName = oBA.ReadString();
        Vector3 vecBone  = oBA.ReadVector();              // Bone position itself.
        Quaternion quatBone = oBA.ReadQuaternion();       // And its quaternion rotation (in Blender's 90-degree rotated about x domain)

        Transform oBone = oBoneParent.FindChild(sBoneName);
        if (oBone == null) {
            oBone = new GameObject(sBoneName).transform;
            oBone.parent = oBoneParent;
        }
        Debug.LogFormat("ReadBone created bone '{0}' under '{1}' with rot:{2:F3},{3:F3},{4:F3},{5:F3} / pos:{6:F3},{7:F3},{8:F3}", sBoneName, oBoneParent.name, quatBone.x, quatBone.y, quatBone.z, quatBone.w, vecBone.x, vecBone.y, vecBone.z);
        oBone.position = vecBone;
        oBone.rotation = quatBone;
        oBone.Rotate(new Vector3(1, 0, 0),  90, Space.World);           //###LEARN!!!: How to rotate about a global x-axis!
        oBone.Rotate(new Vector3(0, 0, 1), 180, Space.Self);            // Rotate about Z axis so bone points +Y from parent bone to child ###CHECK!

        int nBones = oBA.ReadByte();
        for (int nBone = 0; nBone < nBones; nBone++)
            ReadBone(ref oBA, oBone);
    }
Beispiel #2
0
    //---------------------------------------------------------------------------	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>());
    }
Beispiel #3
0
    public virtual void OnDeserializeFromBlender()
    {
        //===== Very important top-level call to begin the process of deserializing a mesh from Blender.  Central to all our meshes! =====
        //=== Send blender command to obtain the mesh header.  This will return a large stream containing material names, bones, bone weight, etc ===
        CByteArray oBA = new CByteArray("'CBody'", _oBodyBase._sBlenderInstancePath_CBodyBase + _sNameCBodyInstanceMember + ".Unity_GetMesh()");

        //=== Create various casts to subclasses to facilitate deserializing info into the proper subclasses ===
        bool bCreatingSkinnedMesh = ((this as CBSkin) != null);     // We create a skinned mesh if we're a CBSkin or related rim... otherwise we're (probably) a body part requiring softbody/cloth simulation so we're just a simple mesh
        _bConnectedToBlenderMesh = true;							// Mark this mesh as having a valid C++ connection to Blender

        //=== Read the number of verts and tris.  Must be < 64K for Unity! ===
        int nVerts = oBA.ReadInt();
        int nTris  = oBA.ReadInt();
        byte nMats = oBA.ReadByte();
        Debug.Log("CBMesh obtains mesh '" + _sNameCBodyInstanceMember + "' for body '" + _oBodyBase._nBodyID + "' with " + nVerts + " verts " + nTris + " triangles and " + nMats + " materials.");

        if (nVerts > 65530)
            CUtility.ThrowException("ERROR in CBMesh.ctor().  Mesh has over 64K verts: " + nVerts);

        //=== Create the new mesh / skinned mesh now that we have the beginning of a valid stream ===
        MeshFilter   		oMeshFilter 	= null;
        MeshRenderer 		oMeshRenderer 	= null;
        if (bCreatingSkinnedMesh == false) {
            oMeshFilter 	= (MeshFilter)  CUtility.FindOrCreateComponent(transform, typeof(MeshFilter));
            oMeshRenderer	= (MeshRenderer)CUtility.FindOrCreateComponent(transform, typeof(MeshRenderer));
        }
        _oMeshNow = new Mesh();				// Create the actual mesh the object will use.  Will persist throughout...
        //_oMeshNow.MarkDynamic();			//###CHECK: Use by default or case-by-case basis in subclasses??    ###OPT!

        if (GetComponent<Collider>() != null)
            Destroy(GetComponent<Collider>());                      //###LEARN: Hugely expensive mesh collider created by the above lines... turn it off!

        //===== READ THE MATERIALS =====
        //=== Read the material list from Blender and create the necessary materials linking to the provided texture images ===
        _aMats = new Material[nMats];
        for (byte nMat = 0; nMat < nMats; nMat++) {
            string sCodedMaterial = oBA.ReadString();
            sCodedMaterial = sCodedMaterial.Replace('\\', '/');						// Replace backslashes to the slash Unity requires.
            if (sCodedMaterial != "NoTexture") {			//###IMPROVE: What to do??  ###IMPROVE: Standarize constants between blender and Unity!

                if (sCodedMaterial.StartsWith("Material_")) {			// Blender material that start with this string are processed differently: We attempt to find the same name material here and attach to this submesh.

                    Material oMat = Resources.Load("Materials/" + sCodedMaterial, typeof(Material)) as Material;
                    if (oMat != null)
                        _aMats[nMat] = oMat;
                    else
                        Debug.LogWarning("ERROR: Unknown special material '" + sCodedMaterial + "' in CBMesh.ctor()");

                } else {

                    int nLocTexPath = sCodedMaterial.IndexOf(CGame.C_RelPath_Textures);		// Blender gives a fully-qualified path that MUST point to our Resources folder
                    if (nLocTexPath != -1) {
                        string sTextureResourcePath = sCodedMaterial.Substring(nLocTexPath + CGame.C_RelPath_Textures.Length);
                        int nLocExtDot = sTextureResourcePath.IndexOf('.');			// Strip the extension... ###CHECK: Assume jpg throughout??
                        if (nLocExtDot != -1)
                            sTextureResourcePath = sTextureResourcePath.Substring(0, nLocExtDot);

                        Material oMat = Resources.Load(sTextureResourcePath + "_Mat", typeof(Material)) as Material;		// We attempt to load the pre-defined material with _Mat suffix if its there...
                        if (oMat == null) {
                            if (sTextureResourcePath.EndsWith("_Transp"))			// If texture ends with this string we create a standard transparent material
                                oMat = new Material(Shader.Find("Transparent/Diffuse"));
                            else
                                oMat = new Material(Shader.Find("Diffuse"));		// If material was not found (usual case) we just create a standard diffuse on
                            //Debug.Log("-Texture: " + sTextureResourcePath);
                            object o = Resources.Load(sTextureResourcePath, typeof(Texture));
                            Texture oTex = o as Texture;
                            if (oTex != null) {
                                int nLocLastSlash = sTextureResourcePath.LastIndexOf('/');
                                oMat.name = sTextureResourcePath.Substring(nLocLastSlash + 1);
                                oMat.mainTexture = oTex;
                            } else {
                                Debug.LogError("**ERROR: Texture '" + sTextureResourcePath + "' not found!");
                            }
                        } else {
                            //Debug.Log("-Material: " + sTextureResourcePath);			// If material was defined we leave it to material designer to connect whatever texture...
                        }
                        _aMats[nMat] = oMat;
                    } else {
                        Debug.LogError("ERROR: in CBMesh.ctor().  Could not find Unity resource path in texture path " + sCodedMaterial);
                    }
                }
            }
        }

        if (nMats == 0) {					// If Blender didn't have a material for this mesh we now create a dummy one as we need at least one 'submesh' for DLL to pass in triangles to us ===
            nMats = 1;
            _aMats = new Material[nMats];
            _aMats[0] = new Material(Shader.Find("Diffuse"));	// Create a default material so we can see the material-less Blender mesh in Unity
        }
        _oMeshNow.subMeshCount = nMats;
        oBA.CheckMagicNumber_End();                 // Check the 'end magic number' that always follows a stream.

        //===== CREATE THE MESH =====
        //=== Create the memory buffers that can extract the data from our C++ dll ===
        CMemAlloc<Vector3> memVerts 	= new CMemAlloc<Vector3>();
        CMemAlloc<Vector3> memNormals 	= new CMemAlloc<Vector3>();		//###DESIGN! ###OPT!! We don't use Blender's normals anymore... keep serializing??
        CMemAlloc<Vector2> memUVs 		= new CMemAlloc<Vector2>();
        memVerts  .Allocate(nVerts);
        memNormals.Allocate(nVerts);
        memUVs    .Allocate(nVerts);

        //=== With the header data process, the GetMeshHeader command also copied the large verts & tri arrays in shared memory for us to access quickly ===
        int nError = ErosEngine.gBL_GetMeshArrays(_sNameBlenderMesh, nMats, memVerts.P, memNormals.P, memUVs.P);
        if (nError != 0)
            CUtility.ThrowException("Exception in CBMesh.ctor().  gBL_GetMeshArrays('" + _sNameBlenderMesh + "') returns error " + nError + " on mesh " + gameObject.name);

        _oMeshNow.vertices 	= memVerts.L;
        _oMeshNow.normals 	= memNormals.L;
        _oMeshNow.uv 		= memUVs.L;

        //=== Create separation of mesh by material ===
        for (int nMat = 0; nMat < nMats; nMat++) {
            int nTrisThisMat = ErosEngine.gBL_GetNumTrianglesAtMaterial(nMat);			//###HACK: These are 'last access' only!
            if (nTrisThisMat > 0) {								//###IMPROVE: Issue warning when no materials?  (So Blender mesh can clean up its unused materials)?
                CMemAlloc<int> memTris = new CMemAlloc<int>();
                memTris.Allocate(3 * nTrisThisMat);				// Number of triangles = 3 * number of triangles
                ErosEngine.gBL_GetTrianglesAtMaterial(nMat, memTris.P);
                _oMeshNow.SetTriangles(memTris.L, nMat);
            }
        }

        //=== Read the 'shared normals' flattened map ===
        _aMapSharedNormals = CByteArray.GetArray_USHORT("'CBody'", _oBodyBase._sBlenderInstancePath_CBodyBase + _sNameCBodyInstanceMember + ".aMapSharedNormals.Unity_GetBytes()");

        //=== Finalize the mesh creation by stuffing _oMeshRender into mesh filter or skinned mesh renderer as appropriate ===
        if (bCreatingSkinnedMesh == false) {
            UpdateNormals();									// Fix the normals with the just-serialized map of shared normals
            _oMeshNow.RecalculateBounds();
            oMeshRenderer.materials = _aMats;
            oMeshFilter.mesh = _oMeshNow;
        }

        _memVerts		.AssignAndPin(_oMeshNow.vertices);
        _memVertsStart	.AssignAndPin(_oMeshNow.vertices);
        _memNormals		.AssignAndPin(_oMeshNow.normals);
        _memTris		.AssignAndPin(_oMeshNow.triangles);

        //=== Mesh is loaded in Unity.  Close link to Blender to save memory unless maintaining the link is requested by base class ===
        if (_bKeepBlenderShare == false)
            ReleaseBlenderMesh();
    }