protected override IByteBuffer ExtractFrame(IChannelHandlerContext context, IByteBuffer buffer, int index, int length) { buffer = buffer.WithOrder(ByteOrder.LittleEndian); byte[] data = buffer.ToArray(); CMessage msg = new CMessage(new CByteArray(data)); short magic = 0; if (!msg.Read(ref magic) && magic != 0x5713) { throw new ProudException("Bad format in Magic"); } CByteArray coreMessage = new CByteArray(); if (!msg.Read(ref coreMessage)) { throw new ProudException("Bad format in Frame"); } IByteBuffer coreMsgBuffer = Unpooled.Buffer(coreMessage._buffer.Length); coreMsgBuffer.WriteBytes(coreMessage._buffer); return(coreMsgBuffer); }
private static void ConvertBalancemap(FileInfo fi) { //var tc = new TextureCache(cachePath); List <byte> buffer = new List <byte>(); UInt16 width; UInt16 height; string filename = fi.Name; string filenameNoExt = filename.Trim(fi.Extension.ToCharArray()); string outdir = @"E:\DumpedBalanceMaps"; using (var ms = new MemoryStream(File.ReadAllBytes(fi.FullName))) using (var br = new BinaryReader(ms)) { var cr2w = new CR2WFile(br); CR2WChunk imagechunk = cr2w.chunks[0]; CByteArray image = ((CBitmapTexture)(imagechunk.data)).Image; buffer = image.Bytes.ToList(); //conversion width = UInt16.Parse(imagechunk.GetVariableByName("width").ToString()); height = UInt16.Parse(imagechunk.GetVariableByName("height").ToString()); //remove some sort of header (28 bytes?) buffer.RemoveRange(0, 28); } //dbg if (buffer == null) { return; } byte[] magic = new byte[4]; magic = buffer.Take(4).ToArray(); if (magic[3] != 0xFF) { throw new NotImplementedException(); } //writing string convertedpath = Path.Combine(outdir, $"{filenameNoExt}.tga"); using (var fs = new FileStream(convertedpath, FileMode.Create)) using (var bw = new BinaryWriter(fs)) { //header bw.Write(new byte[4] { 0x00, 0x00, 0x02, 0x00 }); bw.Write(new byte[8]); bw.Write((UInt16)width); bw.Write((UInt16)height); bw.Write(new byte[2] { 0x20, 0x28 }); //data bw.Write(buffer.ToArray()); } }
public static List<int> GetArray_INT(string sNameBlenderModule, string sBlenderAccessString) { CByteArray oByteArray = new CByteArray(sNameBlenderModule, sBlenderAccessString); int nArrayElements = oByteArray.GetLengthPayload() / sizeof(int); List<int> aBlenderArray = new List<int>(nArrayElements); if (nArrayElements > 0) { for (int nArrayElement = 0; nArrayElement < nArrayElements; nArrayElement++) aBlenderArray.Add(oByteArray.ReadInt()); } else { Debug.LogWarningFormat("###WARNING: CByteArray.GetArray() gets zero-sided array on '{0}'", oByteArray._sBlenderAccessString); } return aBlenderArray; }
internal bool Read(ref CByteArray obj) { long length = 0; if (ReadScalar(ref length)) { byte[] data = new byte[length]; if (Read(ref data, data.Length)) { obj = new CByteArray(data); return(true); } } return(false); }
public void UpdateBonesFromBlender() { // Update our body's bones from the current Blender structure... Launched at edit-time by our helper class CBodyEd //StartBlender(); GameObject oBoneRootGO = GameObject.Find("Resources/PrefabWomanA/Bones"); //###WEAK: Hardcoded path. ###IMPROVE: Support man & woman body oBoneRootGO.SetActive(true); Transform oBoneRootT = oBoneRootGO.transform; string sNameBodySrc = "WomanA"; //###TEMP sNameBodySrc.Substring(6); // Remove 'Prefab' to obtain Blender body source name (a bit weak) CByteArray oBA = new CByteArray("'Client'", "gBL_GetBones('" + sNameBodySrc + "')"); //=== Read the recursive bone tree. The mesh is still based on our bone structure which remains authoritative but we need to map the bone IDs from Blender to Unity! === ReadBone(ref oBA, oBoneRootT); //###IMPROVE? Could define bones in Unity from what they are in Blender? (Big design decision as we have lots of extra stuff on Unity bones!!!) oBA.CheckMagicNumber_End(); // Read the 'end magic number' that always follows a stream. Debug.Log("+++ UpdateBonesFromBlender() OK +++"); }
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); }
public CMessage(CByteArray packet) : base(packet) { }
public CByteArray(CByteArray data) { _buffer = data._buffer; }
internal void Write(CByteArray obj) { WriteScalar(obj._writeoffset); Write(obj._buffer); }
//--------------------------------------------------------------------------- STATIC HOMOGENOUS ARRAY RETRIEVAL public static List<ushort> GetArray_USHORT(string sNameBlenderModule, string sBlenderAccessString) { // Deserialize a Blender mesh's previously-created array CByteArray oByteArray = new CByteArray(sNameBlenderModule, sBlenderAccessString); int nArrayElements = oByteArray.GetLengthPayload() / sizeof(ushort); List<ushort> aBlenderArray = new List<ushort>(nArrayElements); if (nArrayElements > 0) { for (int nArrayElement = 0; nArrayElement < nArrayElements; nArrayElement++) aBlenderArray.Add(oByteArray.ReadUShort()); } else { Debug.LogWarningFormat("###WARNING: CByteArray.GetArray() gets zero-sided array on '{0}'", oByteArray._sBlenderAccessString); } return aBlenderArray; }
//--------------------------------------------------------------------------- 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>()); }
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(); }