void generateMultiple16bitMeshbuffers(int N) { Vertex3D[] vertices32bit; uint[] indices32bit; generateVerticesAndIndices(N, out vertices32bit, out indices32bit); List <Vertex3D> verticesChunk = new List <Vertex3D>(); List <ushort> indicesChunk = new List <ushort>(); int totalCubes = N * N * N; int indicesInCube = indices32bit.Length / totalCubes; int verticesInCube = vertices32bit.Length / totalCubes; int maximumVerticesPerChunk = ushort.MaxValue; // must not be more than 0xffff (because we use 16-bit indices) int verticesIndexOffset = 0; device.Logger.Log("Batching cubes into 16-bit meshbuffers..."); for (int cubeIndex = 0; cubeIndex < totalCubes; cubeIndex++) { // add vertices for (int i = 0; i < verticesInCube; i++) { verticesChunk.Add(vertices32bit[cubeIndex * verticesInCube + i]); } // add indices for (int i = 0; i < indicesInCube; i++) { indicesChunk.Add((ushort)(indices32bit[cubeIndex * indicesInCube + i] - verticesIndexOffset)); } if (verticesChunk.Count + verticesInCube > maximumVerticesPerChunk || // if this chunk is full cubeIndex == totalCubes - 1) // or this is last cube { // we create meshbuffer and add it to the main mesh MeshBuffer mb = MeshBuffer.Create(VertexType.Standard, IndexType._16Bit); mb.SetHardwareMappingHint(HardwareMappingHint.Static, HardwareBufferType.VertexAndIndex); //mb.Append(verticesChunk.ToArray(), indicesChunk.ToArray()); mb.Append(verticesChunk.ToArray(), indicesChunk.ToArray()); mb.RecalculateBoundingBox(); mesh.AddMeshBuffer(mb); mb.Drop(); // clean up vertex and index chunks verticesIndexOffset += verticesChunk.Count; verticesChunk.Clear(); indicesChunk.Clear(); device.Logger.Log( (((cubeIndex + 1) * 100) / totalCubes) + "%: " + mesh + ". ~" + Program.MemUsageText); GC.Collect(); } } }
/// <summary> /// Generates a MeshBuffer which represents all the vertices and indices for values of y /// between y0 and y1, and add it to the mesh. /// </summary> void addStrip(HeightMap map, ColorFunc cf, int y0, int y1) { Vertex3D[] vertices = new Vertex3D[(y1 - y0 + 1) * width]; ushort[] indices = new ushort[(y1 - y0) * (width - 1) * 6]; // "6" is a number of indices in 2 triangles (which forms a quad) // calculate vertices int i = 0; for (int y = y0; y <= y1; ++y) { for (int x = 0; x < width; ++x) { float z = map.GetHeight(x, y); float xf = (float)x / (float)width; float yf = (float)y / (float)height; vertices[i++] = new Vertex3D( new Vector3Df(x, scale * z, y), // position map.GetNormal(x, y, scale), // normal calculate(cf, xf, yf, z), // color new Vector2Df(xf, yf) // tcoords ); } } // calculate indices i = 0; for (int y = y0; y < y1; ++y) { for (int x = 0; x < width - 1; ++x) { int n = (y - y0) * width + x; indices[i++] = (ushort)n; indices[i++] = (ushort)(n + width); indices[i++] = (ushort)(n + width + 1); indices[i++] = (ushort)(n + width + 1); indices[i++] = (ushort)(n + 1); indices[i++] = (ushort)n; } } // append calculated verices and indices to mesh buffer MeshBuffer buf = MeshBuffer.Create(VertexType.Standard, IndexType._16Bit); // create new buffer Mesh.AddMeshBuffer(buf); buf.Append(vertices, indices); buf.RecalculateBoundingBox(); buf.Drop(); }
void generateSingle32BitMeshbuffer(int N) { Vertex3D[] vertices32bit; uint[] indices32bit; generateVerticesAndIndices(N, out vertices32bit, out indices32bit); MeshBuffer mb = MeshBuffer.Create(VertexType.Standard, IndexType._32Bit); mesh.AddMeshBuffer(mb); mb.Drop(); device.Logger.Log("Appending " + vertices32bit.Length + " vertices and " + indices32bit.Length + " indices to 32-bit meshbuffer..."); mb.Append(vertices32bit, indices32bit); mb.SetHardwareMappingHint(HardwareMappingHint.Static, HardwareBufferType.VertexAndIndex); }
/// <summary> /// Read mesh chunk and buffer infos. /// </summary> public void LoadData(CR2WFile meshFile) { // IMPLEMENTED FROM jlouis' witcherconverter // http://jlouisb.users.sourceforge.net/ // https://bitbucket.org/jlouis/witcherconverter SBufferInfos bufferInfos = new SBufferInfos(); // *************** READ CHUNK INFOS *************** foreach (var chunk in meshFile.chunks) { if (chunk.Type == "CMesh") { List <SVertexBufferInfos> vertexBufferInfos = new List <SVertexBufferInfos>(); var cookedDatas = chunk.GetVariableByName("cookedData") as CVector; foreach (var cookedData in cookedDatas.variables) { switch (cookedData.Name) { case "renderChunks": { var bytes = ((CByteArray)cookedData).Bytes; using (MemoryStream ms = new MemoryStream(bytes)) using (BinaryReader br = new BinaryReader(ms)) { var nbBuffers = br.ReadByte(); for (uint i = 0; i < nbBuffers; i++) { SVertexBufferInfos buffInfo = new SVertexBufferInfos(); br.BaseStream.Position += 1; // Unknown buffInfo.verticesCoordsOffset = br.ReadUInt32(); buffInfo.uvOffset = br.ReadUInt32(); buffInfo.normalsOffset = br.ReadUInt32(); br.BaseStream.Position += 9; // Unknown buffInfo.indicesOffset = br.ReadUInt32(); br.BaseStream.Position += 1; // 0x1D buffInfo.nbVertices = br.ReadUInt16(); buffInfo.nbIndices = br.ReadUInt32(); br.BaseStream.Position += 3; // Unknown buffInfo.lod = br.ReadByte(); // lod ? vertexBufferInfos.Add(buffInfo); } } break; } case "indexBufferOffset": { bufferInfos.indexBufferOffset = uint.Parse(cookedData.ToString()); break; } case "indexBufferSize": { bufferInfos.indexBufferSize = uint.Parse(cookedData.ToString()); break; } case "vertexBufferOffset": { bufferInfos.vertexBufferOffset = uint.Parse(cookedData.ToString()); break; } case "vertexBufferSize": { bufferInfos.vertexBufferSize = uint.Parse(cookedData.ToString()); break; } case "quantizationOffset": { bufferInfos.quantizationOffset.X = float.Parse((cookedData as CVector).variables[0].ToString()); bufferInfos.quantizationOffset.Y = float.Parse((cookedData as CVector).variables[1].ToString()); bufferInfos.quantizationOffset.Z = float.Parse((cookedData as CVector).variables[2].ToString()); break; } case "quantizationScale": { bufferInfos.quantizationScale.X = float.Parse((cookedData as CVector).variables[0].ToString()); bufferInfos.quantizationScale.Y = float.Parse((cookedData as CVector).variables[1].ToString()); bufferInfos.quantizationScale.Z = float.Parse((cookedData as CVector).variables[2].ToString()); break; } case "bonePositions": { foreach (CVector item in cookedData as CArray) { if (item.variables.Count == 4) { Vector3Df pos = new Vector3Df(); pos.X = (item.variables[0] as CFloat).val; pos.Y = (item.variables[1] as CFloat).val; pos.Z = (item.variables[2] as CFloat).val; bonePositions.Add(pos); } } break; } } } bufferInfos.verticesBuffer = vertexBufferInfos; var meshChunks = chunk.GetVariableByName("chunks") as CArray; foreach (var meshChunk in meshChunks.array) { SMeshInfos meshInfo = new SMeshInfos(); foreach (var meshinfo in (meshChunk as CVector).variables) { switch (meshinfo.Name) { case "numVertices": { meshInfo.numVertices = uint.Parse(meshinfo.ToString()); break; } case "numIndices": { meshInfo.numIndices = uint.Parse(meshinfo.ToString()); break; } case "numBonesPerVertex": { meshInfo.numBonesPerVertex = uint.Parse(meshinfo.ToString()); break; } case "firstVertex": { meshInfo.firstVertex = uint.Parse(meshinfo.ToString()); break; } case "firstIndex": { meshInfo.firstIndex = uint.Parse(meshinfo.ToString()); break; } case "vertexType": { if ((meshinfo as CName).Value == "MVT_StaticMesh") { meshInfo.vertexType = SMeshInfos.EMeshVertexType.EMVT_STATIC; } else if ((meshinfo as CName).Value == "MVT_SkinnedMesh") { meshInfo.vertexType = SMeshInfos.EMeshVertexType.EMVT_SKINNED; } break; } case "materialID": { meshInfo.materialID = uint.Parse(meshinfo.ToString()); break; } } } CData.meshInfos.Add(meshInfo); } // TODO: Create a more reliable solution var unknownBytes = chunk.unknownBytes.Bytes; using (var ms = new MemoryStream(unknownBytes)) using (var br = new BinaryReader(ms)) { long prevPos = 0; bool correctPos = false; do { prevPos = br.BaseStream.Position; CData.boneData.nbBones = (uint)br.ReadBit6(); if (CData.boneData.nbBones == bonePositions.Count) { var backPos = br.BaseStream.Position; correctPos = true; for (int i = 0; i < CData.boneData.nbBones; i++) { var stringIdx = br.ReadUInt16(); if (stringIdx == 0 || stringIdx >= meshFile.names.Count) { CData.boneData.nbBones = 0; correctPos = false; break; } } br.BaseStream.Position = backPos; } } while (CData.boneData.nbBones != bonePositions.Count && br.BaseStream.Position < unknownBytes.Length && !correctPos); if (br.BaseStream.Position < unknownBytes.Length) { br.BaseStream.Position = prevPos; CData.boneData.nbBones = (uint)br.ReadBit6(); for (uint i = 0; i < CData.boneData.nbBones; i++) { var stringIdx = br.ReadUInt16(); CData.boneData.jointNames.Add(meshFile.names[stringIdx].str); } br.ReadBit6(); for (uint i = 0; i < CData.boneData.nbBones; i++) { Matrix matrix = new Matrix(); for (int j = 0; j < 16; j++) { var value = br.ReadSingle(); matrix.SetElement(j, value); } CData.boneData.boneMatrices.Add(matrix); } } } } else if (chunk.Type == "CMaterialInstance") { CData.materialInstances.Add(chunk.data as CMaterialInstance); } } // *************** READ MESH BUFFER INFOS *************** foreach (var meshInfo in CData.meshInfos) { SVertexBufferInfos vBufferInf = new SVertexBufferInfos(); uint nbVertices = 0; uint firstVertexOffset = 0; uint nbIndices = 0; uint firstIndiceOffset = 0; for (int i = 0; i < bufferInfos.verticesBuffer.Count; i++) { nbVertices += bufferInfos.verticesBuffer[i].nbVertices; if (nbVertices > meshInfo.firstVertex) { vBufferInf = bufferInfos.verticesBuffer[i]; // the index of the first vertex in the buffer firstVertexOffset = meshInfo.firstVertex - (nbVertices - vBufferInf.nbVertices); break; } } for (int i = 0; i < bufferInfos.verticesBuffer.Count; i++) { nbIndices += bufferInfos.verticesBuffer[i].nbIndices; if (nbIndices > meshInfo.firstIndex) { vBufferInf = bufferInfos.verticesBuffer[i]; firstIndiceOffset = meshInfo.firstIndex - (nbIndices - vBufferInf.nbIndices); break; } } // Load only best LOD if (vBufferInf.lod == 1) { using (BinaryReader br = new BinaryReader(File.Open(meshFile.FileName + ".1.buffer", FileMode.Open))) { uint vertexSize = 8; if (meshInfo.vertexType == SMeshInfos.EMeshVertexType.EMVT_SKINNED) { vertexSize += meshInfo.numBonesPerVertex * 2; } br.BaseStream.Seek(vBufferInf.verticesCoordsOffset + firstVertexOffset * vertexSize, SeekOrigin.Begin); List <Vertex3D> vertex3DCoords = new List <Vertex3D>(); Color defaultColor = new Color(255, 255, 255, 255); for (uint i = 0; i < meshInfo.numVertices; i++) { ushort x = br.ReadUInt16(); ushort y = br.ReadUInt16(); ushort z = br.ReadUInt16(); ushort w = br.ReadUInt16(); if (meshInfo.vertexType == SMeshInfos.EMeshVertexType.EMVT_SKINNED) { //sr.BaseStream.Seek(meshInfo.numBonesPerVertex * 2, SeekOrigin.Current); byte[] skinningData = new byte[meshInfo.numBonesPerVertex * 2]; br.BaseStream.Read(skinningData, 0, (int)meshInfo.numBonesPerVertex * 2); for (uint j = 0; j < meshInfo.numBonesPerVertex; ++j) { uint boneId = skinningData[j]; uint weight = skinningData[j + meshInfo.numBonesPerVertex]; float fweight = weight / 255.0f; if (weight != 0) { var vertexSkinningEntry = new W3_DataCache.VertexSkinningEntry(); vertexSkinningEntry.boneId = boneId; vertexSkinningEntry.meshBufferId = 0; vertexSkinningEntry.vertexId = i; vertexSkinningEntry.strength = fweight; CData.w3_DataCache.vertices.Add(vertexSkinningEntry); } } } Vertex3D vertex3DCoord = new Vertex3D(); vertex3DCoord.Position = new Vector3Df(x, y, z) / 65535f * bufferInfos.quantizationScale + bufferInfos.quantizationOffset; vertex3DCoord.Color = defaultColor; vertex3DCoords.Add(vertex3DCoord); } br.BaseStream.Seek(vBufferInf.uvOffset + firstVertexOffset * 4, SeekOrigin.Begin); for (int i = 0; i < meshInfo.numVertices; i++) { float uf = br.ReadHalfFloat(); float vf = br.ReadHalfFloat(); Vertex3D vertex3DCoord = vertex3DCoords[i]; vertex3DCoord.TCoords = new Vector2Df(uf, vf); vertex3DCoords[i] = vertex3DCoord; } // Indices ------------------------------------------------------------------- br.BaseStream.Seek(bufferInfos.indexBufferOffset + vBufferInf.indicesOffset + firstIndiceOffset * 2, SeekOrigin.Begin); List <ushort> indices = new List <ushort>(); for (int i = 0; i < meshInfo.numIndices; i++) { indices.Add(0); } for (int i = 0; i < meshInfo.numIndices; i++) { ushort index = br.ReadUInt16(); // Indice need to be inversed for the normals if (i % 3 == 0) { indices[i] = index; } else if (i % 3 == 1) { indices[i + 1] = index; } else if (i % 3 == 2) { indices[i - 1] = index; } } MeshBuffer meshBuff = MeshBuffer.Create(VertexType.Standard, IndexType._16Bit); CData.staticMesh.AddMeshBuffer(meshBuff); meshBuff.Append(vertex3DCoords, indices); meshBuff.RecalculateBoundingBox(); meshBuff.Drop(); } } } }
/// <summary> /// Read mesh chunk and buffer infos. /// </summary> public void LoadData(CR2WFile meshFile) { // IMPLEMENTED FROM jlouis' witcherconverter // http://jlouisb.users.sourceforge.net/ // https://bitbucket.org/jlouis/witcherconverter SBufferInfos bufferInfos = new SBufferInfos(); // *************** READ CHUNK INFOS *************** foreach (var chunk in meshFile.Chunks) { if (chunk.REDType == "CMesh" && chunk.data is CMesh cmesh) { List <SVertexBufferInfos> vertexBufferInfos = new List <SVertexBufferInfos>(); SMeshCookedData meshCookedData = cmesh.CookedData; var bytes = meshCookedData.RenderChunks.Bytes; using (MemoryStream ms = new MemoryStream(bytes)) using (BinaryReader br = new BinaryReader(ms)) { var nbBuffers = br.ReadByte(); for (uint i = 0; i < nbBuffers; i++) { SVertexBufferInfos buffInfo = new SVertexBufferInfos(); br.BaseStream.Position += 1; // Unknown buffInfo.verticesCoordsOffset = br.ReadUInt32(); buffInfo.uvOffset = br.ReadUInt32(); buffInfo.normalsOffset = br.ReadUInt32(); br.BaseStream.Position += 9; // Unknown buffInfo.indicesOffset = br.ReadUInt32(); br.BaseStream.Position += 1; // 0x1D buffInfo.nbVertices = br.ReadUInt16(); buffInfo.nbIndices = br.ReadUInt32(); buffInfo.materialID = br.ReadByte(); br.BaseStream.Position += 2; // Unknown buffInfo.lod = br.ReadByte(); // lod ? vertexBufferInfos.Add(buffInfo); } } bufferInfos.indexBufferOffset = meshCookedData.IndexBufferOffset.val; bufferInfos.indexBufferSize = meshCookedData.IndexBufferSize.val; //bufferInfos.vertexBufferOffset = meshCookedData.vertexBufferOffset.val; //FIXME bufferInfos.vertexBufferSize = meshCookedData.VertexBufferSize.val; bufferInfos.quantizationOffset.X = meshCookedData.QuantizationOffset.X.val; bufferInfos.quantizationOffset.Y = meshCookedData.QuantizationOffset.Y.val; bufferInfos.quantizationOffset.Z = meshCookedData.QuantizationOffset.Z.val; bufferInfos.quantizationScale.X = meshCookedData.QuantizationScale.X.val; bufferInfos.quantizationScale.Y = meshCookedData.QuantizationScale.Y.val; bufferInfos.quantizationScale.Z = meshCookedData.QuantizationScale.Z.val; if (meshCookedData.BonePositions != null) { foreach (var item in meshCookedData.BonePositions) { Vector3Df pos = new Vector3Df(); pos.X = item.X.val; pos.Y = item.Y.val; pos.Z = item.Z.val; bonePositions.Add(pos); } } bufferInfos.verticesBuffer = vertexBufferInfos; CArray <SMeshChunkPacked> meshChunks = cmesh.Chunks; foreach (SMeshChunkPacked meshChunk in meshChunks.Elements) { SMeshInfos meshInfo = new SMeshInfos(); meshInfo.numVertices = meshChunk.NumVertices == null ? 0 : meshChunk.NumVertices.val; meshInfo.numIndices = meshChunk.NumIndices == null ? 0 : meshChunk.NumIndices.val; meshInfo.numBonesPerVertex = meshChunk.NumBonesPerVertex == null ? (uint)0 : meshChunk.NumBonesPerVertex.val; meshInfo.firstVertex = meshChunk.FirstVertex == null ? 0 : meshChunk.FirstVertex.val; meshInfo.firstIndex = meshChunk.FirstIndex == null ? 0 : meshChunk.FirstIndex.val; meshInfo.materialID = meshChunk.MaterialID == null ? 0 : meshChunk.MaterialID.val; if (meshChunk.VertexType != null) { if (meshChunk.VertexType.WrappedEnum == Enums.EMeshVertexType.MVT_StaticMesh) { meshInfo.vertexType = SMeshInfos.EMeshVertexType.EMVT_STATIC; } else if (meshChunk.VertexType.WrappedEnum == Enums.EMeshVertexType.MVT_SkinnedMesh) { meshInfo.vertexType = SMeshInfos.EMeshVertexType.EMVT_SKINNED; } } CData.meshInfos.Add(meshInfo); } // bone names and matrices CBufferVLQInt32 <CName> boneNames = cmesh.BoneNames; CBufferVLQInt32 <CMatrix4x4> bonematrices = cmesh.Bonematrices; CData.boneData.nbBones = (uint)boneNames.elements.Count; for (int i = 0; i < CData.boneData.nbBones; i++) { CName name = boneNames.elements[i]; CData.boneData.jointNames.Add(name.Value); CMatrix4x4 cmatrix = bonematrices.elements[i]; Matrix matrix = new Matrix(); for (int j = 0; j < 16; j++) { float value = (cmatrix.fields[j] as CFloat).val; matrix.SetElement(j, value); } CData.boneData.boneMatrices.Add(matrix); } } else if (chunk.REDType == "CMaterialInstance") { CData.materialInstances.Add(chunk.data as CMaterialInstance); } } // *************** READ MESH BUFFER INFOS *************** foreach (var meshInfo in CData.meshInfos) { SVertexBufferInfos vBufferInf = new SVertexBufferInfos(); uint nbVertices = 0; uint firstVertexOffset = 0; uint nbIndices = 0; uint firstIndiceOffset = 0; for (int i = 0; i < bufferInfos.verticesBuffer.Count; i++) { nbVertices += bufferInfos.verticesBuffer[i].nbVertices; if (nbVertices > meshInfo.firstVertex) { vBufferInf = bufferInfos.verticesBuffer[i]; // the index of the first vertex in the buffer firstVertexOffset = meshInfo.firstVertex - (nbVertices - vBufferInf.nbVertices); break; } } for (int i = 0; i < bufferInfos.verticesBuffer.Count; i++) { nbIndices += bufferInfos.verticesBuffer[i].nbIndices; if (nbIndices > meshInfo.firstIndex) { vBufferInf = bufferInfos.verticesBuffer[i]; firstIndiceOffset = meshInfo.firstIndex - (nbIndices - vBufferInf.nbIndices); break; } } // Load only best LOD if (vBufferInf.lod == 1) { using (BinaryReader br = new BinaryReader(File.Open(meshFile.FileName + ".1.buffer", FileMode.Open))) { uint vertexSize = 8; if (meshInfo.vertexType == SMeshInfos.EMeshVertexType.EMVT_SKINNED) { vertexSize += meshInfo.numBonesPerVertex * 2; } br.BaseStream.Seek(vBufferInf.verticesCoordsOffset + firstVertexOffset * vertexSize, SeekOrigin.Begin); List <Vertex3D> vertex3DCoords = new List <Vertex3D>(); Color defaultColor = new Color(255, 255, 255, 255); for (uint i = 0; i < meshInfo.numVertices; i++) { ushort x = br.ReadUInt16(); ushort y = br.ReadUInt16(); ushort z = br.ReadUInt16(); ushort w = br.ReadUInt16(); if (meshInfo.vertexType == SMeshInfos.EMeshVertexType.EMVT_SKINNED) { //sr.BaseStream.Seek(meshInfo.numBonesPerVertex * 2, SeekOrigin.Current); byte[] skinningData = new byte[meshInfo.numBonesPerVertex * 2]; br.BaseStream.Read(skinningData, 0, (int)meshInfo.numBonesPerVertex * 2); for (uint j = 0; j < meshInfo.numBonesPerVertex; ++j) { uint boneId = skinningData[j]; uint weight = skinningData[j + meshInfo.numBonesPerVertex]; float fweight = weight / 255.0f; if (weight != 0) { var vertexSkinningEntry = new W3_DataCache.VertexSkinningEntry(); vertexSkinningEntry.boneId = boneId; vertexSkinningEntry.meshBufferId = 0; vertexSkinningEntry.vertexId = i; vertexSkinningEntry.strength = fweight; CData.w3_DataCache.vertices.Add(vertexSkinningEntry); } } } Vertex3D vertex3DCoord = new Vertex3D(); vertex3DCoord.Position = new Vector3Df(x, y, z) / 65535f * bufferInfos.quantizationScale + bufferInfos.quantizationOffset; vertex3DCoord.Color = defaultColor; vertex3DCoords.Add(vertex3DCoord); } br.BaseStream.Seek(vBufferInf.uvOffset + firstVertexOffset * 4, SeekOrigin.Begin); for (int i = 0; i < meshInfo.numVertices; i++) { float uf = br.ReadHalfFloat(); float vf = br.ReadHalfFloat(); Vertex3D vertex3DCoord = vertex3DCoords[i]; vertex3DCoord.TCoords = new Vector2Df(uf, vf); vertex3DCoords[i] = vertex3DCoord; } // Indices ------------------------------------------------------------------- br.BaseStream.Seek(bufferInfos.indexBufferOffset + vBufferInf.indicesOffset + firstIndiceOffset * 2, SeekOrigin.Begin); List <ushort> indices = new List <ushort>(); for (int i = 0; i < meshInfo.numIndices; i++) { indices.Add(0); } for (int i = 0; i < meshInfo.numIndices; i++) { ushort index = br.ReadUInt16(); // Indice need to be inversed for the normals if (i % 3 == 0) { indices[i] = index; } else if (i % 3 == 1) { indices[i + 1] = index; } else if (i % 3 == 2) { indices[i - 1] = index; } } MeshBuffer meshBuff = MeshBuffer.Create(VertexType.Standard, IndexType._16Bit); meshBuff.Append(vertex3DCoords.ToArray(), indices.ToArray()); meshBuff.RecalculateBoundingBox(); CData.staticMesh.AddMeshBuffer(meshBuff); meshBuff.Drop(); } } } }
private void meshGeneratorThread_generateMesh(int layerIndex) { GridLayer l = layers[layerIndex]; if (l.Mesh != null) { l.Mesh.Drop(); } l.Mesh = Mesh.Create(); if (gridCubeCount > 0) { Vertex3D[] cubeVerts = null; ushort[] cubeInds = null; int cubeIndex = maxCubesPerBuffer; // this way we force buffers to be recreated at next new cube int totalCubesAdded = 0; float baseX = -(CubeSize * GridDim) / 2; float baseZ = -(CubeSize * GridDim) / 2; for (int i = 0; i < GridDim; i++) { for (int j = 0; j < GridDim; j++) { if (grid[i, j] == 0) { continue; } // check if current buffer is out of room, if so make a mesh buffer and attach it to the gridMesh and init next cubeVerts&Inds if (cubeIndex == maxCubesPerBuffer) { if (cubeVerts != null && cubeInds != null) { MeshBuffer b = MeshBuffer.Create(VertexType.Standard, IndexType._16Bit); b.Append(cubeVerts, cubeInds); l.Mesh.AddMeshBuffer(b); b.Drop(); totalCubesAdded += cubeIndex; } int cubeCount = gridCubeCount - totalCubesAdded; if (cubeCount > maxCubesPerBuffer) { cubeCount = maxCubesPerBuffer; } cubeIndex = 0; cubeVerts = new Vertex3D[cubeCount * 8]; // 8 verts per cube cubeInds = new ushort[cubeCount * 3 * 2 * 6]; // 3 indices per triangle; 2 triangles per face; 6 faces per cube } // build the cube and add it // note: we build 8 vertices cube because we don't need to texture it; if you want to change that (to use texture instead of vertex colors) - you need to build 12 vertices cube and initialize UV coords float x = baseX + i * CubeSize; float y = 0; float z = baseZ + j * CubeSize; int iv = cubeIndex * 8; cubeVerts[iv + 0] = new Vertex3D(x, y, z); cubeVerts[iv + 1] = new Vertex3D(x + CubeSize, y, z); cubeVerts[iv + 2] = new Vertex3D(x + CubeSize, y + CubeSize, z); cubeVerts[iv + 3] = new Vertex3D(x, y + CubeSize, z); cubeVerts[iv + 4] = new Vertex3D(x, y, z + CubeSize); cubeVerts[iv + 5] = new Vertex3D(x + CubeSize, y, z + CubeSize); cubeVerts[iv + 6] = new Vertex3D(x + CubeSize, y + CubeSize, z + CubeSize); cubeVerts[iv + 7] = new Vertex3D(x, y + CubeSize, z + CubeSize); int ii = cubeIndex * 3 * 2 * 6; // top cubeInds[ii + 0] = (ushort)(iv + 3); cubeInds[ii + 1] = (ushort)(iv + 7); cubeInds[ii + 2] = (ushort)(iv + 6); cubeInds[ii + 3] = (ushort)(iv + 3); cubeInds[ii + 4] = (ushort)(iv + 6); cubeInds[ii + 5] = (ushort)(iv + 2); // front cubeInds[ii + 6] = (ushort)(iv + 0); cubeInds[ii + 7] = (ushort)(iv + 3); cubeInds[ii + 8] = (ushort)(iv + 2); cubeInds[ii + 9] = (ushort)(iv + 0); cubeInds[ii + 10] = (ushort)(iv + 2); cubeInds[ii + 11] = (ushort)(iv + 1); // right cubeInds[ii + 12] = (ushort)(iv + 1); cubeInds[ii + 13] = (ushort)(iv + 2); cubeInds[ii + 14] = (ushort)(iv + 6); cubeInds[ii + 15] = (ushort)(iv + 1); cubeInds[ii + 16] = (ushort)(iv + 6); cubeInds[ii + 17] = (ushort)(iv + 5); // left cubeInds[ii + 18] = (ushort)(iv + 0); cubeInds[ii + 19] = (ushort)(iv + 4); cubeInds[ii + 20] = (ushort)(iv + 7); cubeInds[ii + 21] = (ushort)(iv + 0); cubeInds[ii + 22] = (ushort)(iv + 7); cubeInds[ii + 23] = (ushort)(iv + 3); // back cubeInds[ii + 24] = (ushort)(iv + 4); cubeInds[ii + 25] = (ushort)(iv + 5); cubeInds[ii + 26] = (ushort)(iv + 6); cubeInds[ii + 27] = (ushort)(iv + 4); cubeInds[ii + 28] = (ushort)(iv + 6); cubeInds[ii + 29] = (ushort)(iv + 7); // bottom cubeInds[ii + 30] = (ushort)(iv + 0); cubeInds[ii + 31] = (ushort)(iv + 1); cubeInds[ii + 32] = (ushort)(iv + 5); cubeInds[ii + 33] = (ushort)(iv + 0); cubeInds[ii + 34] = (ushort)(iv + 5); cubeInds[ii + 35] = (ushort)(iv + 4); cubeIndex++; } } if (cubeIndex > 0) { MeshBuffer b = MeshBuffer.Create(VertexType.Standard, IndexType._16Bit); b.Append(cubeVerts, cubeInds); l.Mesh.AddMeshBuffer(b); b.Drop(); } device.SceneManager.MeshManipulator.SetVertexColors(l.Mesh, cubeColor); } l.Transform.Rotation = new Vector3Df(gridGeneration * 0.93f, gridGeneration * 0.81f, gridGeneration * 0.69f); l.Generation = gridGeneration; l.CubeCount = gridCubeCount; l.MeshIsReady = true; }
/// <summary> /// Reads mesh buffer infos. /// </summary> void ReadMeshBufferInfos() { // IMPLEMENTED FROM jlouis' witcherconverter // http://jlouisb.users.sourceforge.net/ // https://bitbucket.org/jlouis/witcherconverter SBufferInfos bufferInfos = new SBufferInfos(); // *************** READ CHUNK INFOS *************** foreach (var chunk in _file.chunks) { if (chunk.Type == "CMesh") { List <SVertexBufferInfos> vertexBufferInfos = new List <SVertexBufferInfos>(); var cookedDatas = chunk.GetVariableByName("cookedData") as CVector; foreach (var cookedData in cookedDatas.variables) { switch (cookedData.Name) { case "renderChunks": { var bytes = ((CByteArray)cookedData).Bytes; var nbBuffers = bytes[0]; int curr = 1; for (uint i = 0; i < nbBuffers; i++) { SVertexBufferInfos buffInfo = new SVertexBufferInfos(); curr += 1; // Unknown buffInfo.verticesCoordsOffset = bytes.SubArray(ref curr, 4).GetUint(); buffInfo.uvOffset = bytes.SubArray(ref curr, 4).GetUint(); buffInfo.normalsOffset = bytes.SubArray(ref curr, 4).GetUint(); curr += 9; // Unknown buffInfo.indicesOffset = bytes.SubArray(ref curr, 4).GetUint(); curr += 1; // 0x1D buffInfo.nbVertices = bytes.SubArray(ref curr, 2).GetUshort(); buffInfo.nbIndices = bytes.SubArray(ref curr, 4).GetUint(); curr += 3; // Unknown buffInfo.lod = bytes.SubArray(ref curr, 1).GetByte(); // lod ? vertexBufferInfos.Add(buffInfo); } break; } case "indexBufferOffset": { bufferInfos.indexBufferOffset = uint.Parse(cookedData.ToString()); break; } case "indexBufferSize": { bufferInfos.indexBufferSize = uint.Parse(cookedData.ToString()); break; } case "vertexBufferOffset": { bufferInfos.vertexBufferOffset = uint.Parse(cookedData.ToString()); break; } case "vertexBufferSize": { bufferInfos.vertexBufferSize = uint.Parse(cookedData.ToString()); break; } case "quantizationOffset": { bufferInfos.quantizationOffset.X = float.Parse((cookedData as CVector).variables[0].ToString()); bufferInfos.quantizationOffset.Y = float.Parse((cookedData as CVector).variables[1].ToString()); bufferInfos.quantizationOffset.Z = float.Parse((cookedData as CVector).variables[2].ToString()); break; } case "quantizationScale": { bufferInfos.quantizationScale.X = float.Parse((cookedData as CVector).variables[0].ToString()); bufferInfos.quantizationScale.Y = float.Parse((cookedData as CVector).variables[1].ToString()); bufferInfos.quantizationScale.Z = float.Parse((cookedData as CVector).variables[2].ToString()); break; } case "bonePositions": { foreach (CVector item in cookedData as CArray) { if (item.variables.Count == 4) { Vector3Df pos = new Vector3Df(); pos.X = (item.variables[0] as CFloat).val; pos.Y = (item.variables[1] as CFloat).val; pos.Z = (item.variables[2] as CFloat).val; bonePositions.Add(pos); } } break; } } } bufferInfos.verticesBuffer = vertexBufferInfos; var meshChunks = chunk.GetVariableByName("chunks") as CArray; foreach (var meshChunk in meshChunks.array) { SMeshInfos meshInfo = new SMeshInfos(); foreach (var mesh in (meshChunk as CVector).variables) { switch (mesh.Name) { case "numVertices": { meshInfo.numVertices = uint.Parse(mesh.ToString()); break; } case "numIndices": { meshInfo.numIndices = uint.Parse(mesh.ToString()); break; } case "numBonesPerVertex": { meshInfo.numBonesPerVertex = uint.Parse(mesh.ToString()); break; } case "firstVertex": { meshInfo.firstVertex = uint.Parse(mesh.ToString()); break; } case "firstIndex": { meshInfo.firstIndex = uint.Parse(mesh.ToString()); break; } case "vertexType": { if ((mesh as CName).Value == "MVT_StaticMesh") { meshInfo.vertexType = SMeshInfos.EMeshVertexType.EMVT_STATIC; } else if ((mesh as CName).Value == "MVT_SkinnedMesh") { meshInfo.vertexType = SMeshInfos.EMeshVertexType.EMVT_SKINNED; } break; } case "materialID": { meshInfo.materialID = uint.Parse(mesh.ToString()); break; } } } meshInfos.Add(meshInfo); } } else if (chunk.Type == "CMaterialInstance") { materialInstances.Add(chunk.data as CMaterialInstance); } } // *************** READ MESH BUFFER INFOS *************** foreach (var meshInfo in meshInfos) { SVertexBufferInfos vBufferInf = new SVertexBufferInfos(); uint nbVertices = 0; uint firstVertexOffset = 0; uint nbIndices = 0; uint firstIndiceOffset = 0; for (int i = 0; i < bufferInfos.verticesBuffer.Count; i++) { nbVertices += bufferInfos.verticesBuffer[i].nbVertices; if (nbVertices > meshInfo.firstVertex) { vBufferInf = bufferInfos.verticesBuffer[i]; // the index of the first vertex in the buffer firstVertexOffset = meshInfo.firstVertex - (nbVertices - vBufferInf.nbVertices); break; } } for (int i = 0; i < bufferInfos.verticesBuffer.Count; i++) { nbIndices += bufferInfos.verticesBuffer[i].nbIndices; if (nbIndices > meshInfo.firstIndex) { vBufferInf = bufferInfos.verticesBuffer[i]; firstIndiceOffset = meshInfo.firstIndex - (nbIndices - vBufferInf.nbIndices); break; } } using (StreamReader sr = new StreamReader(_file.FileName + ".1.buffer")) { uint vertexSize = 8; if (meshInfo.vertexType == SMeshInfos.EMeshVertexType.EMVT_SKINNED) { vertexSize += meshInfo.numBonesPerVertex * 2; } sr.BaseStream.Seek(vBufferInf.verticesCoordsOffset + firstVertexOffset * vertexSize, SeekOrigin.Begin); List <Vertex3D> vertex3DCoords = new List <Vertex3D>(); Color defaultColor = new Color(255, 255, 255, 255); for (int i = 0; i < meshInfo.numVertices; i++) { ushort x, y, z, w; byte[] buff = new byte[2]; sr.BaseStream.Read(buff, 0, 2); x = buff.GetUshort(); sr.BaseStream.Read(buff, 0, 2); y = buff.GetUshort(); sr.BaseStream.Read(buff, 0, 2); z = buff.GetUshort(); sr.BaseStream.Read(buff, 0, 2); w = buff.GetUshort(); // skip skinning data if (meshInfo.vertexType == SMeshInfos.EMeshVertexType.EMVT_SKINNED) { sr.BaseStream.Seek(meshInfo.numBonesPerVertex * 2, SeekOrigin.Current); } Vertex3D vertex3DCoord = new Vertex3D(); vertex3DCoord.Position = new Vector3Df(x, y, z) / 65535f * bufferInfos.quantizationScale + bufferInfos.quantizationOffset; vertex3DCoord.Color = defaultColor; vertex3DCoords.Add(vertex3DCoord); } sr.BaseStream.Seek(vBufferInf.uvOffset + firstVertexOffset * 4, SeekOrigin.Begin); for (int i = 0; i < meshInfo.numVertices; i++) { ushort u, v; byte[] buff = new byte[2]; sr.BaseStream.Read(buff, 0, 2); u = buff.GetUshort(); sr.BaseStream.Read(buff, 0, 2); v = buff.GetUshort(); float uf = u.ToFloat(); float vf = v.ToFloat(); Vertex3D vertex3DCoord = vertex3DCoords[i]; vertex3DCoord.TCoords = new Vector2Df(uf, vf); vertex3DCoords[i] = vertex3DCoord; } // Indices ------------------------------------------------------------------- sr.BaseStream.Seek(bufferInfos.indexBufferOffset + vBufferInf.indicesOffset + firstIndiceOffset * 2, SeekOrigin.Begin); List <ushort> indices = new List <ushort>(); for (int i = 0; i < meshInfo.numIndices; i++) { indices.Add(0); } for (int i = 0; i < meshInfo.numIndices; i++) { ushort index; byte[] buff = new byte[2]; sr.BaseStream.Read(buff, 0, 2); index = buff.GetUshort(); // Indice need to be inversed for the normals if (i % 3 == 0) { indices[i] = index; } else if (i % 3 == 1) { indices[i + 1] = index; } else if (i % 3 == 2) { indices[i - 1] = index; } } MeshBuffer meshBuff = MeshBuffer.Create(VertexType.Standard, IndexType._16Bit); staticMesh.AddMeshBuffer(meshBuff); meshBuff.Append(vertex3DCoords, indices); meshBuff.RecalculateBoundingBox(); meshBuff.Drop(); } } // *************** READ RIG DATA *************** if (RigFile != null) { foreach (var chunk in RigFile.chunks) { if (chunk.Type == "CSkeleton") { var bones = chunk.GetVariableByName("bones") as CArray; meshSkeleton.nbBones = (uint)bones.array.Count; foreach (CVector bone in bones) { var boneName = bone.variables.GetVariableByName("nameAsCName") as CName; meshSkeleton.names.Add(boneName.Value); } var parentIndices = chunk.GetVariableByName("parentIndices") as CArray; foreach (CVariable parentIndex in parentIndices) { meshSkeleton.parentIdx.Add(short.Parse(parentIndex.ToString())); } var unknownBytes = chunk.unknownBytes.Bytes; int currPos = 0; for (uint i = 0; i < meshSkeleton.nbBones; i++) { Vector3Df position = new Vector3Df(); position.X = unknownBytes.SubArray(ref currPos, 4).GetFloat(); position.Y = unknownBytes.SubArray(ref currPos, 4).GetFloat(); position.Z = unknownBytes.SubArray(ref currPos, 4).GetFloat(); unknownBytes.SubArray(ref currPos, 4).GetFloat(); // the w component Quaternion orientation = new Quaternion(); orientation.X = unknownBytes.SubArray(ref currPos, 4).GetFloat(); orientation.Y = unknownBytes.SubArray(ref currPos, 4).GetFloat(); orientation.Z = unknownBytes.SubArray(ref currPos, 4).GetFloat(); orientation.W = unknownBytes.SubArray(ref currPos, 4).GetFloat(); Vector3Df scale; scale.X = unknownBytes.SubArray(ref currPos, 4).GetFloat(); scale.Y = unknownBytes.SubArray(ref currPos, 4).GetFloat(); scale.Z = unknownBytes.SubArray(ref currPos, 4).GetFloat(); unknownBytes.SubArray(ref currPos, 4).GetFloat(); // the w component Matrix posMat = new Matrix(); posMat.Translation = position; Matrix rotMat = new Matrix(); Vector3Df euler = orientation.ToEuler(); // chechNaNErrors(euler); rotMat.SetRotationRadians(euler); Matrix scaleMat = new Matrix(); scaleMat.Scale = scale; Matrix localTransform = posMat * rotMat * scaleMat; orientation = orientation.MakeInverse(); meshSkeleton.matrix.Add(localTransform); meshSkeleton.positions.Add(position); meshSkeleton.rotations.Add(orientation); meshSkeleton.scales.Add(scale); } } } } }