public static int gBL_SendCmd_GetMemBuffer(string sModuleList, string sCmd, ref CMemAlloc<byte> mem) { //####SOON ####IMPROVE: Can get rid of damn import list by importing everything once at game init?? string sCmdFull = "__import__(" + sModuleList + ")." + sCmd; IntPtr pSizeInBuf = ErosEngine.gBL_Cmd(sCmdFull, true); int nSizeByteBuf = pSizeInBuf.ToInt32(); if (nSizeByteBuf <= 0) CUtility.ThrowException("gBL_SendCmd_GetMemBuffer() could not obtain valid byte buffer from gBL_Cmd()!"); mem.Allocate(nSizeByteBuf); ErosEngine.gBL_Cmd_GetLastInBuf(mem.P, nSizeByteBuf); return nSizeByteBuf; }
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(); }