public bool Equals(VertexWeight other) { if (!other.Weight.Equals(this.Weight)) { return false; } if (this.Weight == 0) { return true; } return other.BoneIndex == this.BoneIndex; }
/// <summary> /// Creates controllers to skin each geometry in the collada file /// </summary> void CreateControllerList() { H1.Tags.gbxmodel_group definition = tagManager.TagDefinition as H1.Tags.gbxmodel_group; // if there are no nodes then no skinning is possible if (definition.Nodes.Count == 0) { return; } // create a controller for each geometry for (int i = 0; i < modelInfo.GetGeometryCount(); i++) { List <VertexWeight> vertex_weights = new List <VertexWeight>(); // create a list of vertex weights from all of the geometry parts foreach (var part in definition.Geometries[modelInfo.GetGeometryIndex(i)].Parts) { foreach (var vertex in part.UncompressedVertices) { VertexWeight vertex_weight = new VertexWeight(); int node1 = vertex.NodeIndex1; int node2 = vertex.NodeIndex2; // if the bone index count is 0 then the index references the main node list, // otherwise it references a local node map for this part if (part.NodeMapCount != 0) { node1 = part.NodeMap[node1]; node2 = (node2 != -1 ? part.NodeMap[node2].Value : node2); } vertex_weight.AddWeight(node1, vertex.NodeWeight1); // if the first weight is 1 the vertex is weighted to one bone only so the second weight is not needed if (vertex.NodeWeight1 != 1) { vertex_weight.AddWeight(node2, vertex.NodeWeight2); } vertex_weights.Add(vertex_weight); } } // create the controller element CreateSkinController(listGeometry[i].ID, vertex_weights); } }
/// <summary> /// Load the bone information for each vertex /// </summary> /// <param name="assimpMesh"> mesh that contains the information to be processed </param> /// <param name="mesh"> customized mesh that stores information for later use </param> protected void LoadBoneWeights(Mesh assimpMesh, ClientMesh mesh) { // create a new data structures to store the bones mesh.VertexBoneDatas = new List <VertexBoneData>(mesh.CountVertices); for (int i = 0; i < mesh.CountVertices; i++) { mesh.VertexBoneDatas.Add(new VertexBoneData()); } // copy bone weights from the meshes for each vertex for (int boneIndex = 0; boneIndex < assimpMesh.BoneCount; boneIndex++) { Bone currBone = assimpMesh.Bones[boneIndex]; for (int weighti = 0; weighti < currBone.VertexWeightCount; weighti++) { VertexWeight vertexWeight = currBone.VertexWeights[weighti]; mesh.VertexBoneDatas[vertexWeight.VertexID].AddBoneData(_allBoneMappings[currBone.Name], vertexWeight.Weight); } } // create new datastream so that we can stream them to the VBOs mesh.boneIDSize = sizeof(int) * VertexBoneData.MAX_BONES_PER_VERTEX * mesh.CountVertices; mesh.boneWeightSize = sizeof(float) * VertexBoneData.MAX_BONES_PER_VERTEX * mesh.CountVertices; mesh.DSBoneIDs = new DataStream(mesh.boneIDSize, true, true); mesh.DSBoneWeights = new DataStream(mesh.boneWeightSize, true, true); // for each vertex, write the vertex buffer datastreams for (int i = 0; i < mesh.CountVertices; i++) { // normalize bone weights //mesh.VertexBoneDatas[i].NormalizeBoneData(); // write the data into datastreams for (int bonei = 0; bonei < VertexBoneData.MAX_BONES_PER_VERTEX; bonei++) { mesh.DSBoneIDs.Write(mesh.VertexBoneDatas[i].BoneIndices[bonei]); mesh.DSBoneWeights.Write(mesh.VertexBoneDatas[i].BoneWeights[bonei]); } } mesh.DSBoneWeights.Position = 0; mesh.DSBoneIDs.Position = 0; // create the datastreams mesh.VBOBoneIDs = new Buffer(GraphicsRenderer.Device, mesh.DSBoneIDs, mesh.boneIDSize, ResourceUsage.Default, BindFlags.VertexBuffer, CpuAccessFlags.None, ResourceOptionFlags.None, 0); mesh.VBOBoneWeights = new Buffer(GraphicsRenderer.Device, mesh.DSBoneWeights, mesh.boneWeightSize, ResourceUsage.Default, BindFlags.VertexBuffer, CpuAccessFlags.None, ResourceOptionFlags.None, 0); }
/// <summary> /// Creates a controller to skin each geometry in the file /// </summary> void CreateControllerList() { H2.Tags.render_model_group definition = tagManager.TagDefinition as H2.Tags.render_model_group; // if there are no bones then skinning isnt necessary if (definition.Nodes.Count == 0) { return; } // create a controller for each geometry in modelInfo for (int i = 0; i < modelInfo.GetGeometryCount(); i++) { H2.Tags.render_model_section_block.render_model_section_data_block section_block = definition.Sections[modelInfo.GetGeometryIndex(i)].SectionData[0]; // the node map contains a list of bone indices that the section is rigged to List <int> node_map = new List <int>(); foreach (var node in section_block.NodeMap) { node_map.Add(node.NodeIndex.Value); } List <VertexWeight> vertex_weights = new List <VertexWeight>(); // create a generic list of vertex weights foreach (var vertex in section_block.Section.Value.RawVertices) { VertexWeight common_weight = new VertexWeight(); // only add the weights with valid nodes for (int weight_index = 0; (weight_index < vertex.Point.NodeIndex.Length) && (vertex.Point.NodeIndex[weight_index] != -1); weight_index++) { common_weight.AddWeight(node_map[vertex.Point.NodeIndex[weight_index]], vertex.Point.NodeWeight[weight_index]); } vertex_weights.Add(common_weight); } // create the controller element CreateSkinController(listGeometry[i].ID, vertex_weights); } }
// This method use original assimp bone structure where many VertexWeights (with VertexId and Weight) // structs are assigned to each bone. This data structure is optimized for CPU updates. // The UpdateVertexPositions below requires that each vertex has its array of bones and weights - optimized for GPU updates. // // This method is based on sample code from: https://sourceforge.net/p/assimp/discussion/817654/thread/5462cbf5/ /// <summary> /// UpdateVertexPositions updates the positions in the MeshGeometry3D /// </summary> public void UpdateVertexPositions() { if (SkeletonNodes == null) { return; } var originalPositions = _wpfOriginalPositions; var transformedPositions = new Point3D[originalPositions.Count]; foreach (var skeletonNode in SkeletonNodes) { var assimpBone = skeletonNode.AssimpBone; if (assimpBone == null) { continue; } var finalBoneMatrix = skeletonNode.FinalMatrix; // = skeletonNode.BoneOffsetMatrix * skeletonNode.CurrentWorldMatrix; for (int i = 0; i < assimpBone.VertexWeightCount; i++) { VertexWeight boneWeight = assimpBone.VertexWeights[i]; int vertexId = boneWeight.VertexID; double weightFactor = boneWeight.Weight; var sourcePosition = originalPositions[vertexId]; var transformedPosition = finalBoneMatrix.Transform(sourcePosition); transformedPositions[vertexId] += new System.Windows.Media.Media3D.Vector3D(transformedPosition.X * weightFactor, transformedPosition.Y * weightFactor, transformedPosition.Z * weightFactor); } } MeshGeometry3D.Positions = new Point3DCollection(transformedPositions); }
public static List <Chunk> ReadVerts(ILogger log, byte[] fileData, int offset, int endOffset) { var chunks = new List <Chunk>(); var currentChunk = new Chunk(); Chunk previousChunk = null; while (offset < endOffset) { var vifCommand = fileData[offset + 3] & 0x7f; var numCommand = fileData[offset + 2] & 0xff; int immCommand = DataUtil.getLEShort(fileData, offset); switch (vifCommand) { case NOP_CMD: DebugWrite(HexUtil.formatHex(offset) + " "); DebugWriteLine("NOP"); offset += 4; break; case STCYCL_CMD: DebugWrite(HexUtil.formatHex(offset) + " "); DebugWriteLine("STCYCL: WL: " + (immCommand >> 8) + " CL: " + (immCommand & 0xFF)); offset += 4; break; case ITOP_CMD: DebugWrite(HexUtil.formatHex(offset) + " "); DebugWriteLine("ITOP: " + immCommand); offset += 4; break; case STMOD_CMD: DebugWrite(HexUtil.formatHex(offset) + " "); DebugWriteLine("STMOD: " + immCommand); offset += 4; break; case MSCAL_CMD: DebugWrite(HexUtil.formatHex(offset) + " "); DebugWriteLine("MSCAL: " + immCommand); if (immCommand != 66 && immCommand != 68 && immCommand != 70) { DebugWriteLine("**** Microcode " + immCommand + " not supported"); } currentChunk.mscalID = immCommand; chunks.Add(currentChunk); previousChunk = currentChunk; currentChunk = new Chunk(); offset += 4; break; case STMASK_CMD: DebugWrite(HexUtil.formatHex(offset) + " "); offset += 4; var stmask = DataUtil.getLEInt(fileData, offset); DebugWriteLine("STMASK: " + stmask); offset += 4; break; case FLUSH_CMD: DebugWrite(HexUtil.formatHex(offset) + " "); DebugWriteLine("FLUSH"); offset += 4; break; case DIRECT_CMD: DebugWrite(HexUtil.formatHex(offset) + " "); DebugWriteLine("DIRECT, " + immCommand * 16 + " bytes"); var tags = new GIFTag[immCommand]; for (var i = 0; i < immCommand; i++) { tags[i] = new GIFTag(); tags[i].parse(fileData, offset + 4 + i * 16); } currentChunk.DIRECTGifTags.AddRange(tags); offset += 4; offset += immCommand * 16; break; default: if ((vifCommand & 0x60) == 0x60) { // unpack command var mask = ((vifCommand & 0x10) == 0x10); var vn = (vifCommand >> 2) & 3; var vl = vifCommand & 3; var addr = immCommand & 0x1ff; var flag = (immCommand & 0x8000) == 0x8000; var usn = (immCommand & 0x4000) == 0x4000; DebugWrite(HexUtil.formatHex(offset) + " "); var debugMsg = "UNPACK: vn: " + vn + ", vl: " + vl + ", Addr: " + addr + ", num: " + numCommand; if (flag) { debugMsg += ", Flag"; } if (usn) { debugMsg += ", Unsigned"; } if (mask) { debugMsg += ", Mask"; } DebugWriteLine(debugMsg); offset += 4; if (vn == 1 && vl == 1) { // v2-16 // I don't know why but the UVs come after the MSCAL instruction. if (previousChunk != null) { for (var uvnum = 0; uvnum < numCommand; ++uvnum) { var u = DataUtil.getLEShort(fileData, offset); var v = DataUtil.getLEShort(fileData, offset + 2); previousChunk.uvs.Add(new UV(u, v)); offset += 4; } } else { var numBytes = numCommand * 4; offset += numBytes; } } else if (vn == 2 && vl == 1) { // v3-16 // each vertex is 128 bits, so num is the number of vertices for (var vnum = 0; vnum < numCommand; ++vnum) { if (!usn) { var x = DataUtil.getLEShort(fileData, offset); var y = DataUtil.getLEShort(fileData, offset + 2); var z = DataUtil.getLEShort(fileData, offset + 4); offset += 6; var vertex = new Vertex { x = x, y = y, z = z }; currentChunk.vertices.Add(vertex); } else { int x = DataUtil.getLEUShort(fileData, offset); int y = DataUtil.getLEUShort(fileData, offset + 2); int z = DataUtil.getLEUShort(fileData, offset + 4); offset += 6; var vloc = new VLoc { v1 = x, v2 = y, v3 = z }; currentChunk.vlocs.Add(vloc); } } offset = (offset + 3) & ~3; } else if (vn == 2 && vl == 2) { // v3-8 var idx = offset; for (var vnum = 0; vnum < numCommand; ++vnum) { var vec = new SByteVector { x = (sbyte)fileData[idx++], y = (sbyte)fileData[idx++], z = (sbyte)fileData[idx++] }; currentChunk.normals.Add(vec); } var numBytes = ((numCommand * 3) + 3) & ~3; offset += numBytes; } else if (vn == 3 && vl == 0) { // v4-32 log.LogLine("v4-32 data, " + numCommand + (numCommand == 1 ? " entry" : " entries") + ", addr=" + addr); if (1 == numCommand) { currentChunk.gifTag0 = new GIFTag(); currentChunk.gifTag0.parse(fileData, offset); DebugWrite(HexUtil.formatHex(offset) + " "); DebugWriteLine("GifTag: " + currentChunk.gifTag0.ToString()); } else if (2 == numCommand) { currentChunk.gifTag0 = new GIFTag(); currentChunk.gifTag0.parse(fileData, offset); currentChunk.gifTag1 = new GIFTag(); currentChunk.gifTag1.parse(fileData, offset + 16); DebugWrite(HexUtil.formatHex(offset) + " "); DebugWriteLine("GifTag0: " + currentChunk.gifTag0.ToString()); DebugWrite(HexUtil.formatHex(offset) + " "); DebugWriteLine("GifTag1: " + currentChunk.gifTag1.ToString()); } else { log.LogLine("unknown number of gif commands."); } var numBytes = numCommand * 16; offset += numBytes; } else if (vn == 3 && vl == 1) { // v4-16 log.LogLine("v4-16 data, " + numCommand + (numCommand == 1 ? " entry" : " entries") + ", addr=" + addr); var numShorts = numCommand * 4; if (usn) { currentChunk.extraVlocs = new ushort[numShorts]; for (var i = 0; i < numCommand; ++i) { currentChunk.extraVlocs[i * 4] = DataUtil.getLEUShort(fileData, offset + i * 8); currentChunk.extraVlocs[i * 4 + 1] = DataUtil.getLEUShort(fileData, offset + i * 8 + 2); currentChunk.extraVlocs[i * 4 + 2] = DataUtil.getLEUShort(fileData, offset + i * 8 + 4); currentChunk.extraVlocs[i * 4 + 3] = DataUtil.getLEUShort(fileData, offset + i * 8 + 6); } } else { log.LogLine("Unsupported tag"); } offset += numShorts * 2; } else if (vn == 3 && vl == 2) { // v4-8 var numBytes = numCommand * 4; currentChunk.vertexWeights = new List <VertexWeight>(); var curVertex = 0; for (var i = 0; i < numCommand; ++i) { var vw = new VertexWeight { startVertex = curVertex, bone1 = fileData[offset++] / 4, boneWeight1 = fileData[offset++], bone2 = fileData[offset++] }; if (vw.bone2 == 0xFF) { // Single bone vw.boneWeight2 = 0; int count = fileData[offset++]; curVertex += count; } else { vw.bone2 /= 4; vw.boneWeight2 = fileData[offset++]; ++curVertex; if (vw.boneWeight1 + vw.boneWeight2 < 255) { ++i; vw.bone3 = fileData[offset++] / 4; vw.boneWeight3 = fileData[offset++]; vw.bone4 = fileData[offset++]; int bw4 = fileData[offset++]; if (vw.bone4 != 255) { vw.bone4 /= 4; vw.boneWeight4 = bw4; } } } vw.endVertex = curVertex - 1; currentChunk.vertexWeights.Add(vw); } } else { DebugWriteLine("Unknown vnvl combination: vn=" + vn + ", vl=" + vl); offset = endOffset; } } else { DebugWriteLine("Unknown command: " + vifCommand); offset = endOffset; } break; } } return(chunks); }
public void ParseMesh(Lexer parser, int numJoints, JointMat[] joints) { int num, jointnum, i, j; int[] tris, firstWeightForVertex, numWeightsForVertex; VertexWeight[] tempWeights; parser.ExpectTokenString("{"); // parse name if (parser.CheckTokenString("name")) { parser.ReadToken(out var name); } // parse shader parser.ExpectTokenString("shader"); parser.ReadToken(out var token); var shaderName = token; shader = declManager.FindMaterial(shaderName); // parse texture coordinates parser.ExpectTokenString("numverts"); var count = parser.ParseInt(); if (count < 0) { parser.Error($"Invalid size: {token}"); } texCoords = new Vector2[count]; firstWeightForVertex = new int[count]; numWeightsForVertex = new int[count]; numWeights = 0; var maxweight = 0; for (i = 0; i < texCoords.Length; i++) { parser.ExpectTokenString("vert"); parser.ParseInt(); fixed(float *textCoordsF = &texCoords[i].x) parser.Parse1DMatrix(2, textCoordsF); firstWeightForVertex[i] = parser.ParseInt(); numWeightsForVertex[i] = parser.ParseInt(); if (numWeightsForVertex[i] == 0) { parser.Error("Vertex without any joint weights."); } numWeights += numWeightsForVertex[i]; if (numWeightsForVertex[i] + firstWeightForVertex[i] > maxweight) { maxweight = numWeightsForVertex[i] + firstWeightForVertex[i]; } } // parse tris parser.ExpectTokenString("numtris"); count = parser.ParseInt(); if (count < 0) { parser.Error($"Invalid size: {count}"); } tris = new int[count * 3]; numTris = count; for (i = 0; i < count; i++) { parser.ExpectTokenString("tri"); parser.ParseInt(); tris[i * 3 + 0] = parser.ParseInt(); tris[i * 3 + 1] = parser.ParseInt(); tris[i * 3 + 2] = parser.ParseInt(); } // parse weights parser.ExpectTokenString("numweights"); count = parser.ParseInt(); if (count < 0) { parser.Error($"Invalid size: {count}"); } if (maxweight > count) { parser.Warning($"Vertices reference out of range weights in model ({maxweight} of {count} weights)."); } tempWeights = new VertexWeight[count]; for (i = 0; i < count; i++) { parser.ExpectTokenString("weight"); parser.ParseInt(); jointnum = parser.ParseInt(); if ((jointnum < 0) || (jointnum >= numJoints)) { parser.Error($"Joint Index out of range({numJoints}): {jointnum}"); } tempWeights[i].joint = jointnum; tempWeights[i].jointWeight = parser.ParseFloat(); fixed(float *offsetF = &tempWeights[i].offset.x) parser.Parse1DMatrix(3, offsetF); } // create pre-scaled weights and an index for the vertex/joint lookup scaledWeights = new Vector4[numWeights]; weightIndex = new int[numWeights * 2]; count = 0; for (i = 0; i < texCoords.Length; i++) { num = firstWeightForVertex[i]; for (j = 0; j < numWeightsForVertex[i]; j++, num++, count++) { scaledWeights[count].ToVec3() = tempWeights[num].offset * tempWeights[num].jointWeight; scaledWeights[count].w = tempWeights[num].jointWeight; weightIndex[count * 2 + 0] = tempWeights[num].joint * sizeof(JointMat); } weightIndex[count * 2 - 1] = 1; } tempWeights = null; numWeightsForVertex = null; firstWeightForVertex = null; parser.ExpectTokenString("}"); // update counters c_numVerts += texCoords.Length; c_numWeights += numWeights; c_numWeightJoints++; for (i = 0; i < numWeights; i++) { c_numWeightJoints += weightIndex[i * 2 + 1]; } // build the information that will be common to all animations of this mesh: silhouette edge connectivity and normal / tangent generation information // //DG: windows only has a 1MB stack and it could happen that we try to allocate >1MB here (in lost mission mod, game/le_hell map), causing a stack overflow to prevent that, use heap allocation if it's >600KB var verts = texCoords.Length * sizeof(DrawVert) < 600000 ? stackalloc DrawVert[texCoords.Length + DrawVert.ALLOC16] : new DrawVert[texCoords.Length + DrawVert.ALLOC16]; verts = Platform._alloca16(verts); for (i = 0; i < texCoords.Length; i++) { verts[i].Clear(); verts[i].st = texCoords[i]; } fixed(DrawVert *vertsD = verts) fixed(JointMat * jointsJ = joints) TransformVerts(vertsD, jointsJ); deformInfo = R_BuildDeformInfo(texCoords.Length, verts, tris.Length, tris, shader.UseUnsmoothedTangents); }
public static void WritePosedObj(string savePath, Model model, WriteableBitmap texture, AnimData pose, int frame, double scale) { var dir = Path.GetDirectoryName(savePath) ?? ""; var name = Path.GetFileNameWithoutExtension(savePath); // Save the texture to a .png file using (var stream = new FileStream(Path.Combine(dir, name + ".png"), FileMode.Create)) { var encoder = new PngBitmapEncoder(); encoder.Frames.Add(BitmapFrame.Create(texture)); encoder.Save(stream); stream.Flush(); stream.Close(); } WriteMtlFile(Path.Combine(dir, name + ".mtl"), name); var objFile = File.Open(Path.Combine(dir, name + ".obj"), FileMode.Create); var writer = new StreamWriter(objFile); writer.WriteLine("mtllib " + name + ".mtl"); writer.WriteLine(""); var vStart = 0; var meshCount = 1; foreach (var mesh in model.meshList) { writer.WriteLine("g Mesh_" + meshCount); writer.WriteLine("usemtl " + name); var hasVertexWeights = mesh.vertexWeights.Count > 0; var vwNum = 0; var vw = new VertexWeight(); if (mesh.vertexWeights.Count > 0) { vw = mesh.vertexWeights[vwNum]; } var vnum = 0; foreach (var vertex in mesh.Positions) { var point = vertex; if (frame >= 0 && pose != null) { if (vw.endVertex < vnum) { ++vwNum; vw = mesh.vertexWeights[vwNum]; if (vnum < vw.startVertex || vnum > vw.endVertex) { Debug.Fail("Vertex " + vnum + " out of range of bone weights " + vw.startVertex + " -> " + vw.endVertex); } } var bone1No = vw.bone1; var bindingPos1 = pose.bindingPose[bone1No]; var bone1Pose = pose.perFrameFKPoses[frame, bone1No]; var joint1Pos = bone1Pose.Position; if (vw.bone2 == 0xFF) { if (bone1No == 1) { bone1No = 1; } var m = Matrix3D.Identity; m.Translate(new Vector3D(-bindingPos1.X, -bindingPos1.Y, -bindingPos1.Z)); // Inverse binding matrix m.Rotate(bone1Pose.Rotation); m.Translate(new Vector3D(bone1Pose.Position.X, bone1Pose.Position.Y, bone1Pose.Position.Z)); point = m.Transform(point); } else { // multi-bone var bone2No = vw.bone2; var bindingPos2 = pose.bindingPose[bone2No]; var bone2Pose = pose.perFrameFKPoses[frame, bone2No]; double boneSum = vw.boneWeight1 + vw.boneWeight2; var bone1Coeff = vw.boneWeight1 / boneSum; var bone2Coeff = vw.boneWeight2 / boneSum; var m = Matrix3D.Identity; m.Translate(new Vector3D(-bindingPos1.X, -bindingPos1.Y, -bindingPos1.Z)); // Inverse binding matrix m.Rotate(bone1Pose.Rotation); m.Translate(new Vector3D(bone1Pose.Position.X, bone1Pose.Position.Y, bone1Pose.Position.Z)); var point1 = m.Transform(point); // Now rotate var m2 = Matrix3D.Identity; m2.Translate(new Vector3D(-bindingPos2.X, -bindingPos2.Y, -bindingPos2.Z)); // Inverse binding matrix m2.Rotate(bone2Pose.Rotation); m2.Translate(new Vector3D(bone2Pose.Position.X, bone2Pose.Position.Y, bone2Pose.Position.Z)); var point2 = m2.Transform(point); point = new Point3D(point1.X * bone1Coeff + point2.X * bone2Coeff, point1.Y * bone1Coeff + point2.Y * bone2Coeff, point1.Z * bone1Coeff + point2.Z * bone2Coeff); } } ++vnum; writer.WriteLine("v {0} {1} {2}", FormatDouble(point.X / scale), FormatDouble(point.Y / scale), FormatDouble(point.Z / scale)); } writer.WriteLine(""); foreach (var uv in mesh.TextureCoordinates) { writer.WriteLine("vt {0} {1}", FormatDouble(uv.X), FormatDouble(1 - uv.Y)); // Flip uv's vertically } writer.WriteLine(""); foreach (var vec in mesh.Normals) { // TODO: If the mesh is posed, the normals needs modifying too. writer.WriteLine("vn {0} {1} {2}", FormatDouble(vec.X), FormatDouble(vec.Y), FormatDouble(vec.Z)); } writer.WriteLine(""); for (var i = 0; i < mesh.TriangleIndices.Count - 3; i += 6) { writer.WriteLine("f {0}/{1}/{2} {3}/{4}/{5} {6}/{7}/{8}", mesh.TriangleIndices[i] + 1 + vStart, mesh.TriangleIndices[i] + 1 + vStart, mesh.TriangleIndices[i] + 1 + vStart, mesh.TriangleIndices[i + 1] + 1 + vStart, mesh.TriangleIndices[i + 1] + 1 + vStart, mesh.TriangleIndices[i + 1] + 1 + vStart, mesh.TriangleIndices[i + 2] + 1 + vStart, mesh.TriangleIndices[i + 2] + 1 + vStart, mesh.TriangleIndices[i + 2] + 1 + vStart); } writer.WriteLine(""); vStart += mesh.Positions.Count; meshCount++; } writer.Flush(); writer.Close(); }
private static Assimp.Scene GetPMOScene(Pmo pmo) { Assimp.Scene scene = new Assimp.Scene(); scene.RootNode = new Assimp.Node("root"); // Add materials. List <Material> matList = new List <Material>(); for (int t = 0; t < pmo.header.TextureCount; t++) { Material mat = new Material(); mat.Clear(); mat.Name = pmo.textureInfo[t].TextureName; scene.Materials.Add(mat); } // Add skeleton. List <Node> Skeleton = new List <Node>(); for (int b = 0; b < pmo.skeletonHeader.BoneCount; b++) { Pmo.BoneData bn = pmo.boneList[b]; Assimp.Matrix4x4 mtx = new Assimp.Matrix4x4(); mtx.A1 = bn.Transform.M11; mtx.A2 = bn.Transform.M12; mtx.A3 = bn.Transform.M13; mtx.A4 = bn.Transform.M14; mtx.B1 = bn.Transform.M21; mtx.B2 = bn.Transform.M22; mtx.B3 = bn.Transform.M23; mtx.B4 = bn.Transform.M24; mtx.C1 = bn.Transform.M31; mtx.C2 = bn.Transform.M32; mtx.C3 = bn.Transform.M33; mtx.C4 = bn.Transform.M34; mtx.D1 = bn.Transform.M41; mtx.D2 = bn.Transform.M42; mtx.D3 = bn.Transform.M43; mtx.D4 = bn.Transform.M44; Assimp.Matrix4x4 nd_mtx = mtx; nd_mtx.Transpose(); if (bn.ParentBoneIndex == 0xFFFF) { Node curNode = new Node(bn.JointName); curNode.Transform = nd_mtx; scene.RootNode.Children.Add(curNode); Skeleton.Add(curNode); } else { Node curNode = new Node(bn.JointName, Skeleton[bn.ParentBoneIndex]); nd_mtx.A4 *= 100.0f; nd_mtx.B4 *= 100.0f; nd_mtx.C4 *= 100.0f; curNode.Transform = nd_mtx; Skeleton.Add(curNode); scene.RootNode.FindNode(Skeleton[bn.ParentBoneIndex].Name).Children.Add(curNode); } } // Add meshes. for (int i = 0; i < pmo.Meshes.Count; i++) { Assimp.Mesh mesh = new Assimp.Mesh($"Mesh{i}", Assimp.PrimitiveType.Triangle); Pmo.MeshChunks chunk = pmo.Meshes[i]; // Add vertices, vertex color and normals. for (int j = 0; j < chunk.vertices.Count; j++) { mesh.Vertices.Add(new Assimp.Vector3D( chunk.vertices[j].X * pmo.header.ModelScale * 100.0f, chunk.vertices[j].Y * pmo.header.ModelScale * 100.0f, chunk.vertices[j].Z * pmo.header.ModelScale * 100.0f)); mesh.VertexColorChannels[0].Add(new Color4D(1.0f, 1.0f, 1.0f, 1.0f)); mesh.Normals.Add(new Vector3D()); } mesh.SetIndices(chunk.Indices.ToArray(), 3); mesh.MaterialIndex = chunk.SectionInfo.TextureID; scene.Meshes.Add(mesh); for (int v = 0; v < chunk.vertices.Count; v++) { // Build bone influences. for (int z = 0; z < chunk.SectionInfo_opt1.SectionBoneIndices.Length; z++) { if (chunk.SectionInfo_opt1.SectionBoneIndices[z] != 0xFF) { Pmo.BoneData currentBone = new Pmo.BoneData(); int currentIndex = chunk.SectionInfo_opt1.SectionBoneIndices[z]; currentBone = pmo.boneList[currentIndex]; string boneName = currentBone.JointName; Assimp.Matrix4x4 mtx = new Assimp.Matrix4x4(); mtx.A1 = currentBone.Transform.M11; mtx.A2 = currentBone.Transform.M12; mtx.A3 = currentBone.Transform.M13; mtx.A4 = currentBone.Transform.M14; mtx.B1 = currentBone.Transform.M21; mtx.B2 = currentBone.Transform.M22; mtx.B3 = currentBone.Transform.M23; mtx.B4 = currentBone.Transform.M24; mtx.C1 = currentBone.Transform.M31; mtx.C2 = currentBone.Transform.M32; mtx.C3 = currentBone.Transform.M33; mtx.C4 = currentBone.Transform.M34; mtx.D1 = currentBone.Transform.M41; mtx.D2 = currentBone.Transform.M42; mtx.D3 = currentBone.Transform.M43; mtx.D4 = currentBone.Transform.M44; Matrix3x3 mtx3 = new Matrix3x3(mtx); mtx.Transpose(); mtx.A4 *= 100.0f; mtx.B4 *= 100.0f; mtx.C4 *= 100.0f; mtx3.Transpose(); List <VertexWeight> weight = new List <VertexWeight>(); VertexWeight vW = new VertexWeight(); vW.VertexID = v; float currentWeight = chunk.jointWeights[v].weights[z]; switch (chunk.jointWeights[v].coordFormart) { case Pmo.CoordinateFormat.NO_VERTEX: break; case Pmo.CoordinateFormat.NORMALIZED_8_BITS: currentWeight *= 127.0f; currentWeight /= 128.0f; break; case Pmo.CoordinateFormat.NORMALIZED_16_BITS: currentWeight *= 32767.0f; currentWeight /= 32768.0f; break; case Pmo.CoordinateFormat.FLOAT_32_BITS: break; } vW.Weight = currentWeight; weight.Add(vW); Bone tempBone = scene.Meshes[i].Bones.Find(x => x.Name == boneName); int boneInd = scene.Meshes[i].Bones.FindIndex(0, x => x.Name == boneName); if (tempBone == null) { Bone bone = new Bone(boneName, mtx3, weight.ToArray()); scene.Meshes[i].Bones.Add(bone); } else { scene.Meshes[i].Bones[boneInd].VertexWeights.Add(vW); } } } } } scene.RootNode.MeshIndices.AddRange(Enumerable.Range(0, scene.MeshCount)); return(scene); }
protected void ProcessMesh(Assimp.Mesh m) { var newMesh = new Mesh { Name = m.Name, Material = _materials[m.MaterialIndex] }; _meshes.Add(newMesh); uint[] indices = m.GetUnsignedIndices(); var emotionIndices = new ushort[indices.Length]; for (var i = 0; i < indices.Length; i++) { emotionIndices[i] = (ushort)indices[i]; } newMesh.Indices = emotionIndices; var vertices = new VertexDataWithBones[m.VertexCount]; for (var i = 0; i < m.VertexCount; i++) { // todo: check if uvs exist, vert colors, normals Vector3D vertex = m.Vertices[i]; Vector3D uv = m.TextureCoordinateChannels[0][i]; vertices[i] = new VertexDataWithBones { Vertex = new Vector3(vertex.X, vertex.Y, vertex.Z), UV = new Vector2(uv.X, uv.Y), BoneIds = new Vector4(0), BoneWeights = new Vector4(1, 0, 0, 0) }; } newMesh.VerticesWithBones = vertices; if (!m.HasBones) { return; } _boneToIndex ??= new Dictionary <string, int>(); var bones = new MeshBone[m.BoneCount]; newMesh.Bones = bones; for (var i = 0; i < m.Bones.Count; i++) { Bone bone = m.Bones[i]; var emBone = new MeshBone { Name = bone.Name, OffsetMatrix = AssMatrixToEmoMatrix(bone.OffsetMatrix) }; bones[i] = emBone; // Check if this bone has an id assigned. if (!_boneToIndex.TryGetValue(bone.Name, out int boneIndex)) { boneIndex = _boneToIndex.Count + 1; _boneToIndex.Add(bone.Name, boneIndex); } emBone.BoneIndex = boneIndex; for (var j = 0; j < bone.VertexWeightCount; j++) { VertexWeight boneDef = bone.VertexWeights[j]; ref VertexDataWithBones vertex = ref vertices[boneDef.VertexID]; // Todo: better way of doing this if (vertex.BoneIds.X == 0) { vertex.BoneIds.X = boneIndex; vertex.BoneWeights.X = boneDef.Weight; } else if (vertex.BoneIds.Y == 0) { vertex.BoneIds.Y = boneIndex; vertex.BoneWeights.Y = boneDef.Weight; } else if (vertex.BoneIds.Z == 0) { vertex.BoneIds.Z = boneIndex; vertex.BoneWeights.Z = boneDef.Weight; } else if (vertex.BoneIds.W == 0) { vertex.BoneIds.W = boneIndex; vertex.BoneWeights.W = boneDef.Weight; } else { Engine.Log.Warning($"Bone {bone.Name} affects more than 4 vertices in mesh {m.Name}.", "Assimp", true); } } }
/// <summary> /// /// </summary> /// <param name="pobj"></param> private Mesh ProcessPOBJ(HSD_POBJ pobj, HSD_JOBJ parent, HSD_JOBJ singleBind) { Mesh m = new Mesh(); m.Name = "pobj"; m.PrimitiveType = PrimitiveType.Triangle; m.MaterialIndex = 0; var dl = pobj.ToDisplayList(); var envelopes = pobj.EnvelopeWeights; var parentTransform = Matrix4.Identity; if (parent != null) { parentTransform = WorldTransforms[jobjToIndex[parent]]; } var singleBindTransform = Matrix4.Identity; if (singleBind != null) { singleBindTransform = WorldTransforms[jobjToIndex[singleBind]]; } Dictionary <HSD_JOBJ, Bone> jobjToBone = new Dictionary <HSD_JOBJ, Bone>(); if (envelopes != null) { foreach (var jobj in Jobjs) { var bone = new Bone(); bone.Name = JobjNodes[jobjToIndex[jobj]].Name; bone.OffsetMatrix = Matrix4ToMatrix4x4(WorldTransforms[jobjToIndex[jobj]].Inverted()); m.Bones.Add(bone); jobjToBone.Add(jobj, bone); } } /*foreach (var en in envelopes) * { * foreach (var jobj in en.JOBJs) * { * if (!jobjToBone.ContainsKey(jobj)) * { * var bone = new Bone(); * bone.Name = JobjNodes[jobjToIndex[jobj]].Name; * bone.OffsetMatrix = Matrix4ToMatrix4x4(WorldTransforms[jobjToIndex[jobj]].Inverted()); * m.Bones.Add(bone); * jobjToBone.Add(jobj, bone); * } * } * }*/ if (singleBind != null && !jobjToBone.ContainsKey(singleBind)) { var bone = new Bone(); bone.Name = JobjNodes[jobjToIndex[singleBind]].Name; bone.OffsetMatrix = Matrix4ToMatrix4x4(WorldTransforms[jobjToIndex[singleBind]].Inverted()); m.Bones.Add(bone); jobjToBone.Add(singleBind, bone); } int offset = 0; var vIndex = -1; foreach (var prim in dl.Primitives) { var verts = dl.Vertices.GetRange(offset, prim.Count); offset += prim.Count; switch (prim.PrimitiveType) { case GXPrimitiveType.Quads: verts = TriangleConverter.QuadToList(verts); break; case GXPrimitiveType.TriangleStrip: verts = TriangleConverter.StripToList(verts); break; case GXPrimitiveType.Triangles: break; default: Console.WriteLine(prim.PrimitiveType); break; } for (int i = m.VertexCount; i < m.VertexCount + verts.Count; i += 3) { var f = new Face(); f.Indices.Add(i); f.Indices.Add(i + 1); f.Indices.Add(i + 2); m.Faces.Add(f); } foreach (var v in verts) { vIndex++; if (singleBind != null) { var vertexWeight = new VertexWeight(); vertexWeight.VertexID = vIndex; vertexWeight.Weight = 1; jobjToBone[singleBind].VertexWeights.Add(vertexWeight); } Matrix4 weight = Matrix4.Identity; foreach (var a in pobj.Attributes) { switch (a.AttributeName) { case GXAttribName.GX_VA_PNMTXIDX: var en = envelopes[v.PNMTXIDX / 3]; for (int w = 0; w < en.EnvelopeCount; w++) { var vertexWeight = new VertexWeight(); vertexWeight.VertexID = vIndex; vertexWeight.Weight = en.Weights[w]; jobjToBone[en.JOBJs[w]].VertexWeights.Add(vertexWeight); } if (en.EnvelopeCount == 1 && jobjToIndex[parent] == 0) { weight = WorldTransforms[jobjToIndex[en.JOBJs[0]]]; } break; case GXAttribName.GX_VA_POS: var vert = Vector3.TransformPosition(GXTranslator.toVector3(v.POS), parentTransform); vert = Vector3.TransformPosition(vert, weight); vert = Vector3.TransformPosition(vert, singleBindTransform); m.Vertices.Add(new Vector3D(vert.X, vert.Y, vert.Z)); break; case GXAttribName.GX_VA_NRM: var nrm = Vector3.TransformNormal(GXTranslator.toVector3(v.NRM), parentTransform); nrm = Vector3.TransformNormal(nrm, weight); nrm = Vector3.TransformNormal(nrm, singleBindTransform); m.Normals.Add(new Vector3D(nrm.X, nrm.Y, nrm.Z)); break; case GXAttribName.GX_VA_CLR0: m.VertexColorChannels[0].Add(new Color4D(v.CLR0.R, v.CLR0.G, v.CLR0.B, v.CLR0.A)); break; case GXAttribName.GX_VA_CLR1: m.VertexColorChannels[1].Add(new Color4D(v.CLR1.R, v.CLR1.G, v.CLR1.B, v.CLR1.A)); break; case GXAttribName.GX_VA_TEX0: m.TextureCoordinateChannels[0].Add(new Vector3D(v.TEX0.X, v.TEX0.Y, 1)); break; case GXAttribName.GX_VA_TEX1: m.TextureCoordinateChannels[1].Add(new Vector3D(v.TEX1.X, v.TEX1.Y, 1)); break; case GXAttribName.GX_VA_TEX2: m.TextureCoordinateChannels[2].Add(new Vector3D(v.TEX2.X, v.TEX2.Y, 1)); break; case GXAttribName.GX_VA_TEX3: m.TextureCoordinateChannels[3].Add(new Vector3D(v.TEX3.X, v.TEX3.Y, 1)); break; case GXAttribName.GX_VA_TEX4: m.TextureCoordinateChannels[4].Add(new Vector3D(v.TEX4.X, v.TEX4.Y, 1)); break; case GXAttribName.GX_VA_TEX5: m.TextureCoordinateChannels[5].Add(new Vector3D(v.TEX5.X, v.TEX5.Y, 1)); break; case GXAttribName.GX_VA_TEX6: m.TextureCoordinateChannels[6].Add(new Vector3D(v.TEX6.X, v.TEX6.Y, 1)); break; case GXAttribName.GX_VA_TEX7: m.TextureCoordinateChannels[7].Add(new Vector3D(v.TEX7.X, v.TEX7.Y, 1)); break; } } } } return(m); }
/// <summary> /// Creates controllers to skin each geometry in the collada file /// </summary> void CreateControllerList() { H1.Tags.gbxmodel_group definition = tagManager.TagDefinition as H1.Tags.gbxmodel_group; // if there are no nodes then no skinning is possible if (definition.Nodes.Count == 0) return; // create a controller for each geometry for (int i = 0; i < modelInfo.GetGeometryCount(); i++) { List<VertexWeight> vertex_weights = new List<VertexWeight>(); // create a list of vertex weights from all of the geometry parts foreach (var part in definition.Geometries[modelInfo.GetGeometryIndex(i)].Parts) { foreach (var vertex in part.UncompressedVertices) { VertexWeight vertex_weight = new VertexWeight(); int node1 = vertex.NodeIndex1; int node2 = vertex.NodeIndex2; // if the bone index count is 0 then the index references the main node list, // otherwise it references a local node map for this part if (part.NodeMapCount != 0) { node1 = part.NodeMap[node1]; node2 = (node2 != -1 ? part.NodeMap[node2].Value : node2); } vertex_weight.AddWeight(node1, vertex.NodeWeight1); // if the first weight is 1 the vertex is weighted to one bone only so the second weight is not needed if(vertex.NodeWeight1 != 1) vertex_weight.AddWeight(node2, vertex.NodeWeight2); vertex_weights.Add(vertex_weight); } } // create the controller element CreateSkinController(listGeometry[i].ID, vertex_weights); } }
public static Model3D CreateModel3D(List <Mesh> meshGroups, BitmapSource texture, AnimData pose, int frame) { var model = new GeometryModel3D(); var mesh3D = new MeshGeometry3D(); var numVertices = 0; foreach (var meshGroup in meshGroups) { numVertices += meshGroup.Positions.Count; } var triangleIndices = new Int32Collection(); var positions = new Point3DCollection(numVertices); var normals = new Vector3DCollection(numVertices); var uvCoords = new PointCollection(numVertices); var vstart = 0; foreach (var meshGroup in meshGroups) { var hasVertexWeights = meshGroup.vertexWeights.Count > 0; var vwNum = 0; var vw = new VertexWeight(); if (meshGroup.vertexWeights.Count > 0) { vw = meshGroup.vertexWeights[vwNum]; } var vnum = 0; foreach (var vertex in meshGroup.Positions) { var point = vertex; if (frame >= 0 && pose != null) { if (vw.endVertex < vnum) { ++vwNum; vw = meshGroup.vertexWeights[vwNum]; if (vnum < vw.startVertex || vnum > vw.endVertex) { Debug.Fail("Vertex " + vnum + " out of range of bone weights " + vw.startVertex + " -> " + vw.endVertex); } } var bone1No = vw.bone1; var bindingPos1 = pose.bindingPose[bone1No]; var bone1Pose = pose.perFrameFKPoses[frame, bone1No]; var joint1Pos = bone1Pose.Position; if (vw.bone2 == 0xFF) { if (bone1No == 1) { bone1No = 1; } var m = Matrix3D.Identity; m.Translate(new Vector3D(-bindingPos1.X, -bindingPos1.Y, -bindingPos1.Z)); // Inverse binding matrix m.Rotate(bone1Pose.Rotation); m.Translate(new Vector3D(bone1Pose.Position.X, bone1Pose.Position.Y, bone1Pose.Position.Z)); point = m.Transform(point); } else { // multi-bone var bone2No = vw.bone2; var bindingPos2 = pose.bindingPose[bone2No]; var bone2Pose = pose.perFrameFKPoses[frame, bone2No]; double boneSum = vw.boneWeight1 + vw.boneWeight2; var bone1Coeff = vw.boneWeight1 / boneSum; var bone2Coeff = vw.boneWeight2 / boneSum; var m = Matrix3D.Identity; m.Translate(new Vector3D(-bindingPos1.X, -bindingPos1.Y, -bindingPos1.Z)); // Inverse binding matrix m.Rotate(bone1Pose.Rotation); m.Translate(new Vector3D(bone1Pose.Position.X, bone1Pose.Position.Y, bone1Pose.Position.Z)); var point1 = m.Transform(point); // Now rotate var m2 = Matrix3D.Identity; m2.Translate(new Vector3D(-bindingPos2.X, -bindingPos2.Y, -bindingPos2.Z)); // Inverse binding matrix m2.Rotate(bone2Pose.Rotation); m2.Translate(new Vector3D(bone2Pose.Position.X, bone2Pose.Position.Y, bone2Pose.Position.Z)); var point2 = m2.Transform(point); point = new Point3D(point1.X * bone1Coeff + point2.X * bone2Coeff, point1.Y * bone1Coeff + point2.Y * bone2Coeff, point1.Z * bone1Coeff + point2.Z * bone2Coeff); } } positions.Add(point); ++vnum; } foreach (var normal in meshGroup.Normals) { normals.Add(normal); } foreach (var ti in meshGroup.TriangleIndices) { triangleIndices.Add(ti + vstart); } foreach (var uv in meshGroup.TextureCoordinates) { uvCoords.Add(uv); } vstart += meshGroup.Positions.Count; } mesh3D.TriangleIndices = triangleIndices; mesh3D.Positions = positions; mesh3D.TextureCoordinates = uvCoords; mesh3D.Normals = normals; model.Geometry = mesh3D; var dm = new DiffuseMaterial(); if (texture != null && texture.Width > 0 && texture.Height > 0) { var ib = new ImageBrush(texture) { ViewportUnits = BrushMappingMode.Absolute }; // May be needed at a later point //ib.TileMode = TileMode.Tile; dm.Brush = ib; } else { var dg = new DrawingGroup(); // Background dg.Children.Add(new GeometryDrawing() { Brush = new SolidColorBrush(Colors.Black), Geometry = new RectangleGeometry(new Rect(0, 0, 2, 2)) }); // Tiles dg.Children.Add(new GeometryDrawing() { Brush = new SolidColorBrush(Colors.Violet), Geometry = new RectangleGeometry(new Rect(0, 0, 1, 1)) }); dg.Children.Add(new GeometryDrawing() { Brush = new SolidColorBrush(Colors.Violet), Geometry = new RectangleGeometry(new Rect(1, 1, 1, 1)) }); dm.Brush = new DrawingBrush(dg) { TileMode = TileMode.Tile, Transform = new ScaleTransform(0.1, 0.1) }; } model.Material = dm; return(model); }
public static Mesh ChunksToMesh(ILogger log, List <Chunk> chunks, int texturePixelWidth, int texturePixelHeight) { Mesh mesh = new Mesh(); int numVertices = 0; foreach (Chunk chunk in chunks) { numVertices += chunk.vertices.Count; } mesh.TriangleIndices = new Int32Collection(); mesh.Positions = new Point3DCollection(numVertices); mesh.Normals = new Vector3DCollection(numVertices); mesh.vertexWeights = new List <VertexWeight>(); var uvCoords = new Point[numVertices]; Point uninitPoint = new Point(-10000, -10000); for (int uv = 0; uv < uvCoords.Length; ++uv) { uvCoords[uv] = uninitPoint; } int vstart = 0; foreach (var chunk in chunks) { if ((chunk.gifTag0.prim & 0x07) != 4) { Debug.Fail("Can only deal with tri strips"); } foreach (var vertex in chunk.vertices) { var point = new Point3D(vertex.x / 16.0, vertex.y / 16.0, vertex.z / 16.0); mesh.Positions.Add(point); } foreach (var normal in chunk.normals) { mesh.Normals.Add(new Vector3D(normal.x / 127.0, normal.y / 127.0, normal.z / 127.0)); } foreach (VertexWeight vw in chunk.vertexWeights) { VertexWeight vwAdjusted = vw; vwAdjusted.startVertex += vstart; if (vwAdjusted.endVertex >= chunk.vertices.Count) { vwAdjusted.endVertex = chunk.vertices.Count - 1; } vwAdjusted.endVertex += vstart; if (vw.startVertex <= (chunk.vertices.Count - 1)) { mesh.vertexWeights.Add(vwAdjusted); } } int[] vstrip = new int[chunk.gifTag0.nloop]; int regsPerVertex = chunk.gifTag0.nreg; int numVlocs = chunk.vlocs.Count; int numVertsInChunk = chunk.vertices.Count; for (int vlocIndx = 2; vlocIndx < numVlocs; ++vlocIndx) { int v = vlocIndx - 2; int stripIdx2 = (chunk.vlocs[vlocIndx].v2 & 0x1FF) / regsPerVertex; int stripIdx3 = (chunk.vlocs[vlocIndx].v3 & 0x1FF) / regsPerVertex; if (stripIdx3 < vstrip.Length && stripIdx2 < vstrip.Length) { vstrip[stripIdx3] = vstrip[stripIdx2] & 0x1FF; bool skip2 = (chunk.vlocs[vlocIndx].v3 & 0x8000) == 0x8000; if (skip2) { vstrip[stripIdx3] |= 0x8000; } } int stripIdx = (chunk.vlocs[vlocIndx].v1 & 0x1FF) / regsPerVertex; bool skip = (chunk.vlocs[vlocIndx].v1 & 0x8000) == 0x8000; if (v < numVertsInChunk && stripIdx < vstrip.Length) { vstrip[stripIdx] = skip ? (v | 0x8000) : v; } } int numExtraVlocs = chunk.extraVlocs[0]; for (int extraVloc = 0; extraVloc < numExtraVlocs; ++extraVloc) { int idx = extraVloc * 4 + 4; int stripIndxSrc = (chunk.extraVlocs[idx] & 0x1FF) / regsPerVertex; int stripIndxDest = (chunk.extraVlocs[idx + 1] & 0x1FF) / regsPerVertex;; vstrip[stripIndxDest] = (chunk.extraVlocs[idx + 1] & 0x8000) | (vstrip[stripIndxSrc] & 0x1FF); stripIndxSrc = (chunk.extraVlocs[idx + 2] & 0x1FF) / regsPerVertex; stripIndxDest = (chunk.extraVlocs[idx + 3] & 0x1FF) / regsPerVertex;; vstrip[stripIndxDest] = (chunk.extraVlocs[idx + 3] & 0x8000) | (vstrip[stripIndxSrc] & 0x1FF); } int triIdx = 0; for (int i = 2; i < vstrip.Length; ++i) { int vidx1 = vstart + (vstrip[i - 2] & 0xFF); int vidx2 = vstart + (vstrip[i - 1] & 0xFF); int vidx3 = vstart + (vstrip[i] & 0xFF); int uv1 = i - 2; int uv2 = i - 1; int uv3 = i; // Flip the faces (indices 1 and 2) to keep the winding rule consistent. if ((triIdx & 1) == 1) { int temp = uv1; uv1 = uv2; uv2 = temp; temp = vidx1; vidx1 = vidx2; vidx2 = temp; } if ((vstrip[i] & 0x8000) == 0) { // WPF really has S,T coords rather than u,v double udiv = texturePixelWidth * 16.0; double vdiv = texturePixelHeight * 16.0; var p1 = new Point(chunk.uvs[uv1].u / udiv, chunk.uvs[uv1].v / vdiv); var p2 = new Point(chunk.uvs[uv2].u / udiv, chunk.uvs[uv2].v / vdiv); var p3 = new Point(chunk.uvs[uv3].u / udiv, chunk.uvs[uv3].v / vdiv); p1 = TileST(p1); p2 = TileST(p2); p3 = TileST(p3); if (!uninitPoint.Equals(uvCoords[vidx1]) && !p1.Equals(uvCoords[vidx1])) { // There is more than 1 uv assigment to this vertex, so we need to duplicate it. int originalVIdx = vidx1; vidx1 = vstart + numVertsInChunk; numVertsInChunk++; mesh.Positions.Add(mesh.Positions.ElementAt(originalVIdx)); mesh.Normals.Add(mesh.Normals.ElementAt(originalVIdx)); Array.Resize(ref uvCoords, uvCoords.Length + 1); uvCoords[uvCoords.Length - 1] = uninitPoint; var weight = FindVertexWeight(chunk.vertexWeights, originalVIdx - vstart); if (weight.boneWeight1 > 0) { var vw = weight; vw.startVertex = vidx1; vw.endVertex = vidx1; mesh.vertexWeights.Add(vw); } } if (!uninitPoint.Equals(uvCoords[vidx2]) && !p2.Equals(uvCoords[vidx2])) { // There is more than 1 uv assigment to this vertex, so we need to duplicate it. int originalVIdx = vidx2; vidx2 = vstart + numVertsInChunk; numVertsInChunk++; mesh.Positions.Add(mesh.Positions.ElementAt(originalVIdx)); mesh.Normals.Add(mesh.Normals.ElementAt(originalVIdx)); Array.Resize(ref uvCoords, uvCoords.Length + 1); uvCoords[uvCoords.Length - 1] = uninitPoint; var weight = FindVertexWeight(chunk.vertexWeights, originalVIdx - vstart); if (weight.boneWeight1 > 0) { var vw = weight; vw.startVertex = vidx2; vw.endVertex = vidx2; mesh.vertexWeights.Add(vw); } } if (!uninitPoint.Equals(uvCoords[vidx3]) && !p3.Equals(uvCoords[vidx3])) { // There is more than 1 uv assigment to this vertex, so we need to duplicate it. int originalVIdx = vidx3; vidx3 = vstart + numVertsInChunk; numVertsInChunk++; mesh.Positions.Add(mesh.Positions.ElementAt(originalVIdx)); mesh.Normals.Add(mesh.Normals.ElementAt(originalVIdx)); Array.Resize(ref uvCoords, uvCoords.Length + 1); uvCoords[uvCoords.Length - 1] = uninitPoint; var weight = FindVertexWeight(chunk.vertexWeights, originalVIdx - vstart); if (weight.boneWeight1 > 0) { var vw = weight; vw.startVertex = vidx3; vw.endVertex = vidx3; mesh.vertexWeights.Add(vw); } } uvCoords[vidx1] = p1; uvCoords[vidx2] = p2; uvCoords[vidx3] = p3; // Double sided hack. Should fix this with normals really mesh.TriangleIndices.Add(vidx1); mesh.TriangleIndices.Add(vidx2); mesh.TriangleIndices.Add(vidx3); mesh.TriangleIndices.Add(vidx2); mesh.TriangleIndices.Add(vidx1); mesh.TriangleIndices.Add(vidx3); } ++triIdx; } vstart += numVertsInChunk; } mesh.TextureCoordinates = new PointCollection(uvCoords); return(mesh); }
public Model FromScene(Scene scene, string name, AnimationEngine engine) { if (!scene.HasMeshes) { throw new Exception("Scene has no meshes!"); } Model model = new Model(name); model.OriginalModel = scene; model.Root = convert(scene.RootNode.Transform); foreach (Mesh mesh in scene.Meshes) { if (mesh.Name.ToLower().Contains("collision")) { continue; } ModelMesh modmesh = new ModelMesh(mesh.Name, mesh); modmesh.Base = scene; modmesh.vbo.Prepare(); bool hastc = mesh.HasTextureCoords(0); bool hasn = mesh.HasNormals; if (!hasn) { SysConsole.Output(OutputType.WARNING, "Mesh has no normals!"); } if (!hastc) { SysConsole.Output(OutputType.WARNING, "Mesh has no texcoords!"); } for (int i = 0; i < mesh.Vertices.Count; i++) { Vector3D vertex = mesh.Vertices[i]; modmesh.vbo.Vertices.Add(new Vector3(vertex.X, vertex.Y, vertex.Z)); if (!hastc) { modmesh.vbo.TexCoords.Add(new Vector3(0, 0, 0)); } else { Vector3D texCoord = mesh.TextureCoordinateChannels[0][i]; modmesh.vbo.TexCoords.Add(new Vector3(texCoord.X, 1 - texCoord.Y, texCoord.Z)); } if (!hasn) { modmesh.vbo.Normals.Add(new Vector3(0, 0, 1)); } else { modmesh.vbo.Normals.Add(new Vector3(mesh.Normals[i].X, mesh.Normals[i].Y, mesh.Normals[i].Z)); } modmesh.vbo.Colors.Add(new Vector4(1, 1, 1, 1)); // TODO: From the mesh? } foreach (Face face in mesh.Faces) { if (face.Indices.Count == 3) { for (int i = 2; i >= 0; i--) { modmesh.vbo.Indices.Add((uint)face.Indices[i]); } } else { SysConsole.Output(OutputType.WARNING, "Mesh has face with " + face.Indices.Count + " vertices!"); } } int bc = mesh.Bones.Count; if (bc > 70) { SysConsole.Output(OutputType.WARNING, "Mesh has " + bc + " bones!"); bc = 70; } modmesh.vbo.BoneIDs = new Vector4[modmesh.vbo.Vertices.Count].ToList(); modmesh.vbo.BoneWeights = new Vector4[modmesh.vbo.Vertices.Count].ToList(); int[] pos = new int[modmesh.vbo.Vertices.Count]; for (int i = 0; i < bc; i++) { for (int x = 0; x < mesh.Bones[i].VertexWeights.Count; x++) { VertexWeight vw = mesh.Bones[i].VertexWeights[x]; int spot = pos[vw.VertexID]++; if (spot > 3) { //SysConsole.Output(OutputType.WARNING, "Too many bones influencing " + vw.VertexID + "!"); ForceSet(modmesh.vbo.BoneWeights, vw.VertexID, 3, modmesh.vbo.BoneWeights[vw.VertexID][3] + vw.Weight); } else { ForceSet(modmesh.vbo.BoneIDs, vw.VertexID, spot, i); ForceSet(modmesh.vbo.BoneWeights, vw.VertexID, spot, vw.Weight); } } } model.Meshes.Add(modmesh); modmesh.GenerateVBO(); } model.RootNode = new ModelNode() { Internal = scene.RootNode, Parent = null, Name = scene.RootNode.Name.ToLower() }; List <ModelNode> allNodes = new List <ModelNode>(); PopulateChildren(model.RootNode, scene, model, engine, allNodes); for (int i = 0; i < model.Meshes.Count; i++) { for (int x = 0; x < model.Meshes[i].Original.Bones.Count; x++) { ModelNode nodet = null; string nl = model.Meshes[i].Original.Bones[x].Name.ToLower(); for (int n = 0; n < allNodes.Count; n++) { if (allNodes[n].Name == nl) { nodet = allNodes[n]; break; } } ModelBone mb = new ModelBone() { Internal = model.Meshes[i].Original.Bones[x], Offset = convert(model.Meshes[i].Original.Bones[x].OffsetMatrix) }; nodet.Bones.Add(mb); model.Meshes[i].Bones.Add(mb); } } return(model); }
private void RemoveDuplicateVertices(Mesh mesh) { // Calculate which vertices are duplicates (based on their position, texture coordinates, and normals). List <Tuple <Vector3D, Vector3D?, List <Vector3D> > > uniqueVertInfos = new List <Tuple <Vector3D, Vector3D?, List <Vector3D> > >(); int[] replaceVertexIDs = new int[mesh.Vertices.Count]; bool[] vertexIsUnique = new bool[mesh.Vertices.Count]; for (var origVertexID = 0; origVertexID < mesh.Vertices.Count; origVertexID++) { var coordsForVert = new List <Vector3D>(); for (var i = 0; i < mesh.TextureCoordinateChannelCount; i++) { coordsForVert.Add(mesh.TextureCoordinateChannels[i][origVertexID]); } Vector3D?normal; if (origVertexID < mesh.Normals.Count) { normal = mesh.Normals[origVertexID]; } else { normal = null; } var vertInfo = new Tuple <Vector3D, Vector3D?, List <Vector3D> >(mesh.Vertices[origVertexID], normal, coordsForVert); // Determine if this vertex is a duplicate of a previously encountered vertex or not and if it is keep track of the new index var duplicateVertexIndex = -1; for (var i = 0; i < uniqueVertInfos.Count; i++) { Tuple <Vector3D, Vector3D?, List <Vector3D> > otherVertInfo = uniqueVertInfos[i]; if (CheckVertInfosAreDuplicates(vertInfo.Item1, vertInfo.Item2, vertInfo.Item3, otherVertInfo.Item1, otherVertInfo.Item2, otherVertInfo.Item3)) { duplicateVertexIndex = i; break; } } if (duplicateVertexIndex == -1) { vertexIsUnique[origVertexID] = true; uniqueVertInfos.Add(vertInfo); replaceVertexIDs[origVertexID] = uniqueVertInfos.Count - 1; } else { vertexIsUnique[origVertexID] = false; replaceVertexIDs[origVertexID] = duplicateVertexIndex; } } // Remove duplicate vertices, normals, and texture coordinates. mesh.Vertices.Clear(); mesh.Normals.Clear(); // Need to preserve the channel count since it gets set to 0 when clearing all the channels int origTexCoordChannelCount = mesh.TextureCoordinateChannelCount; for (var i = 0; i < origTexCoordChannelCount; i++) { mesh.TextureCoordinateChannels[i].Clear(); } foreach (Tuple <Vector3D, Vector3D?, List <Vector3D> > vertInfo in uniqueVertInfos) { mesh.Vertices.Add(vertInfo.Item1); if (vertInfo.Item2 != null) { mesh.Normals.Add(vertInfo.Item2.Value); } for (var i = 0; i < origTexCoordChannelCount; i++) { var coord = vertInfo.Item3[i]; mesh.TextureCoordinateChannels[i].Add(coord); } } // Update vertex indices for the faces. foreach (Face face in mesh.Faces) { for (var i = 0; i < face.IndexCount; i++) { face.Indices[i] = replaceVertexIDs[face.Indices[i]]; } } // Update vertex indices for the bone vertex weights. foreach (Bone bone in mesh.Bones) { List <VertexWeight> origVertexWeights = new List <VertexWeight>(bone.VertexWeights); bone.VertexWeights.Clear(); for (var i = 0; i < origVertexWeights.Count; i++) { VertexWeight origWeight = origVertexWeights[i]; int origVertexID = origWeight.VertexID; if (!vertexIsUnique[origVertexID]) { continue; } int newVertexID = replaceVertexIDs[origVertexID]; VertexWeight newWeight = new VertexWeight(newVertexID, origWeight.Weight); bone.VertexWeights.Add(newWeight); } } }
public void SortBones() { VertexWeight tmp; if (this.Bone0.Weight < this.Bone1.Weight) { tmp = this.Bone0; this.Bone0 = this.Bone1; this.Bone1 = tmp; } if (this.Bone2.Weight < this.Bone3.Weight) { tmp = this.Bone2; this.Bone2 = this.Bone3; this.Bone3 = tmp; } if (this.Bone0.Weight < this.Bone2.Weight) { tmp = this.Bone0; this.Bone0 = this.Bone2; this.Bone2 = tmp; } if (this.Bone1.Weight < this.Bone2.Weight) { tmp = this.Bone1; this.Bone1 = this.Bone2; this.Bone2 = tmp; } if (this.Bone2.Weight < this.Bone3.Weight) { tmp = this.Bone2; this.Bone2 = this.Bone3; this.Bone3 = tmp; } }
/// <summary> /// Creates a controller to skin each geometry in the file /// </summary> void CreateControllerList() { H2.Tags.render_model_group definition = tagManager.TagDefinition as H2.Tags.render_model_group; // if there are no bones then skinning isnt necessary if (definition.Nodes.Count == 0) return; // create a controller for each geometry in modelInfo for (int i = 0; i < modelInfo.GetGeometryCount(); i++) { H2.Tags.render_model_section_block.render_model_section_data_block section_block = definition.Sections[modelInfo.GetGeometryIndex(i)].SectionData[0]; // the node map contains a list of bone indices that the section is rigged to List<int> node_map = new List<int>(); foreach (var node in section_block.NodeMap) node_map.Add(node.NodeIndex.Value); List<VertexWeight> vertex_weights = new List<VertexWeight>(); // create a generic list of vertex weights foreach(var vertex in section_block.Section.Value.RawVertices) { VertexWeight common_weight = new VertexWeight(); // only add the weights with valid nodes for(int weight_index = 0; (weight_index < vertex.Point.NodeIndex.Length) && (vertex.Point.NodeIndex[weight_index] != -1); weight_index++) common_weight.AddWeight(node_map[vertex.Point.NodeIndex[weight_index]], vertex.Point.NodeWeight[weight_index]); vertex_weights.Add(common_weight); } // create the controller element CreateSkinController(listGeometry[i].ID, vertex_weights); } }