public Model(Scene scene, Arguments args) { EnsureOneMaterialPerMesh(scene); SortMeshesByObjectNames(scene); VertexData = new VTX1(scene); Joints = new JNT1(scene, VertexData); Textures = new TEX1(scene, args); SkinningEnvelopes = new EVP1(); SkinningEnvelopes.SetInverseBindMatrices(scene, Joints.FlatSkeleton); PartialWeightData = new DRW1(scene, Joints.BoneNameIndices); Shapes = SHP1.Create(scene, Joints.BoneNameIndices, VertexData.Attributes, SkinningEnvelopes, PartialWeightData, args.tristrip_mode); Materials = new MAT3(scene, Textures, Shapes, args); if (args.output_bdl) { MatDisplayList = new MDL3(Materials.m_Materials, Textures.Textures); } Scenegraph = new INF1(scene, Joints); foreach (Geometry.Shape shape in Shapes.Shapes) { packetCount += shape.Packets.Count; } vertexCount = VertexData.Attributes.Positions.Count; }
public Model(EndianBinaryReader reader, List <Materials.Material> mat_presets = null) { int j3d2Magic = reader.ReadInt32(); int modelMagic = reader.ReadInt32(); if (j3d2Magic != 0x4A334432) { throw new Exception("Model was not a BMD or BDL! (J3D2 magic not found)"); } if ((modelMagic != 0x62646C34) && (modelMagic != 0x626D6433)) { throw new Exception("Model was not a BMD or BDL! (Model type was not bmd3 or bdl4)"); } int modelSize = reader.ReadInt32(); int sectionCount = reader.ReadInt32(); // Skip the dummy section, SVR3 reader.Skip(16); Scenegraph = new INF1(reader, 32); VertexData = new VTX1(reader, (int)reader.BaseStream.Position); SkinningEnvelopes = new EVP1(reader, (int)reader.BaseStream.Position); PartialWeightData = new DRW1(reader, (int)reader.BaseStream.Position); Joints = new JNT1(reader, (int)reader.BaseStream.Position); //Joints.SetInverseBindMatrices(SkinningEnvelopes.InverseBindMatrices); SkinningEnvelopes.SetInverseBindMatrices(Joints.FlatSkeleton); Shapes = SHP1.Create(reader, (int)reader.BaseStream.Position); Shapes.SetVertexWeights(SkinningEnvelopes, PartialWeightData); Materials = new MAT3(reader, (int)reader.BaseStream.Position, mat_presets); SkipMDL3(reader); Textures = new TEX1(reader, (int)reader.BaseStream.Position); // This is useful for dumping material data to json Materials.FillTextureNames(Textures); foreach (Geometry.Shape shape in Shapes.Shapes) { packetCount += shape.Packets.Count; } vertexCount = VertexData.Attributes.Positions.Count; }
public Model(Scene scene, Arguments args) { VertexData = new VTX1(scene); Joints = new JNT1(scene, VertexData); Scenegraph = new INF1(scene, Joints); Textures = new TEX1(scene, args); SkinningEnvelopes = new EVP1(); SkinningEnvelopes.SetInverseBindMatrices(scene, Joints.FlatSkeleton); PartialWeightData = new DRW1(scene, Joints.BoneNameIndices); Shapes = SHP1.Create(scene, Joints.BoneNameIndices, VertexData.Attributes, SkinningEnvelopes, PartialWeightData); Materials = new MAT3(scene, Textures, Shapes); foreach (Geometry.Shape shape in Shapes.Shapes) { packetCount += shape.Packets.Count; } vertexCount = VertexData.Attributes.Positions.Count; }
public Model( Scene scene, string modelDirectory, List <Materials.Material> mat_presets = null, TristripOption triopt = TristripOption.DoNotTriStrip, bool flipAxis = false, bool fixNormals = false, string additionalTexPath = null) { Assimp.Node root = null; for (int i = 0; i < scene.RootNode.ChildCount; i++) { if (scene.RootNode.Children[i].Name.ToLowerInvariant() == "skeleton_root") { if (scene.RootNode.Children[i].ChildCount == 0) { throw new System.Exception("skeleton_root has no children! If you are making a rigged model, make sure skeleton_root contains the root of your skeleton."); } root = scene.RootNode.Children[i].Children[0]; break; } } foreach (Mesh mesh in scene.Meshes) { if (mesh.HasBones && root == null) { throw new System.Exception("Model uses bones but the skeleton root has not been found! Make sure your skeleton is inside a dummy object called 'skeleton_root'."); } } Matrix3x3 rotateXminus90 = Matrix3x3.FromRotationX((float)(-(1 / 2.0) * Math.PI)); Matrix3x3 rotateXplus90 = Matrix3x3.FromRotationX((float)((1 / 2.0) * Math.PI)); Matrix3x3 rotateYminus90 = Matrix3x3.FromRotationY((float)(-(1 / 2.0) * Math.PI)); Matrix3x3 rotateYplus90 = Matrix3x3.FromRotationY((float)((1 / 2.0) * Math.PI)); Matrix3x3 rotateZminus90 = Matrix3x3.FromRotationZ((float)(-(1 / 2.0) * Math.PI)); Matrix3x3 rotateZplus90 = Matrix3x3.FromRotationZ((float)((1 / 2.0) * Math.PI)); if (flipAxis) { Console.WriteLine("Rotating the model..."); int i = 0; Matrix4x4 rotate = Matrix4x4.FromRotationX((float)(-(1 / 2.0) * Math.PI)); //rotate = Matrix4x4.FromRotationZ((float)(-(1 / 2.0) * Math.PI)); Matrix4x4 rotateinv = rotate; //Matrix3x3 rotvec = rotateZplus90 * rotateXplus90; //Matrix3x3 rotvec = rotateYplus90*rotateYplus90 * rotateZplus90 * rotateZplus90* rotateXplus90* rotateXplus90; rotateinv.Inverse(); //rotate = Matrix4x4.FromRotationY((float)(-(1 / 2.0) * Math.PI)); //rotate = Matrix4x4.FromRotationZ((float)(-(1 / 2.0) * Math.PI)); Matrix4x4 rotateC = Matrix4x4.FromRotationX((float)(-(1 / 2.0) * Math.PI)); Matrix4x4 trans; if (root != null) { // Rotate rigged mesh foreach (Mesh mesh in scene.Meshes) { Console.WriteLine(mesh.Name); Console.WriteLine(String.Format("Does it have bones? {0}", mesh.HasBones)); Matrix3x3[] weightedmats = new Matrix3x3[mesh.Normals.Count]; foreach (Assimp.Bone bone in mesh.Bones) { bone.OffsetMatrix = rotateinv * bone.OffsetMatrix; /*Matrix3x3 invbind = bone.OffsetMatrix; * //bind.Inverse(); * //List<int> vertices = new List<VertexWeight>(); * * foreach (Assimp.VertexWeight weight in bone.VertexWeights) { * Matrix3x3 weightedcurrentmat = new Matrix3x3( * weight.Weight * invbind.A1, weight.Weight * invbind.A2, weight.Weight * invbind.A1, * weight.Weight * invbind.B1, weight.Weight * invbind.B2, weight.Weight * invbind.B3, * weight.Weight * invbind.C1, weight.Weight * invbind.C2, weight.Weight * invbind.C3); * * if (weightedmats[weight.VertexID] == null) { * weightedmats[weight.VertexID] = weightedcurrentmat; * } * else { * Matrix3x3 existingmat = weightedmats[weight.VertexID]; * weightedmats[weight.VertexID] = new Matrix3x3( * existingmat.A1 + invbind.A1, existingmat.A2 + invbind.A2, existingmat.A3 + invbind.A3, * existingmat.B1 + invbind.B1, existingmat.B2 + invbind.B2, existingmat.B3 + invbind.B3, * existingmat.C1 + invbind.C1, existingmat.C2 + invbind.C2, existingmat.C3 + invbind.C3); * } * }*/ //Matrix4x4 bindMat = bone.OffsetMatrix; //bindMat.Inverse(); //trans = /*bone.OffsetMatrix = root.Transform * bone.OffsetMatrix; * Matrix4x4 newtransform = root.Transform * rotate; * newtransform.Inverse(); * bone.OffsetMatrix = newtransform * bone.OffsetMatrix;*/ } for (i = 0; i < mesh.VertexCount; i++) { Vector3D vertex = mesh.Vertices[i]; vertex.Set(vertex.X, vertex.Z, -vertex.Y); mesh.Vertices[i] = vertex; } for (i = 0; i < mesh.Normals.Count; i++) { Vector3D norm = mesh.Normals[i]; norm.Set(norm.X, norm.Z, -norm.Y); //Matrix3x3 invbind = weightedmats[i]; //invbind.Inverse(); //norm = invbind * norm; mesh.Normals[i] = norm; } } } else { // Rotate static mesh foreach (Mesh mesh in scene.Meshes) { for (i = 0; i < mesh.VertexCount; i++) { Vector3D vertex = mesh.Vertices[i]; vertex.Set(vertex.X, vertex.Z, -vertex.Y); mesh.Vertices[i] = vertex; } for (i = 0; i < mesh.Normals.Count; i++) { Vector3D norm = mesh.Normals[i]; norm.Set(norm.X, norm.Z, -norm.Y); mesh.Normals[i] = norm; } } } if (root != null) { List <Assimp.Node> allnodes = new List <Assimp.Node>(); List <Assimp.Node> processnodes = new List <Assimp.Node>(); processnodes.Add(root); root.Transform = root.Transform * rotate; /*while (processnodes.Count > 0) { * Assimp.Node current = processnodes[0]; * processnodes.RemoveAt(0); * * current.Transform = current.Transform * rotate; * * foreach (Assimp.Node child in current.Children) { * processnodes.Add(child); * } * }*/ } } // On rigged models we attempt to fix normals for shading to work properly (when using materials) // That works by multiplying the normal for a vertex with the inverse bind matrices that have an effect on the vertex. // Seems to work semi-well, might need to look over this at a later point again though. Console.WriteLine(String.Format("fixNormals is {0}", fixNormals)); if (fixNormals && root != null) { Console.WriteLine("Fixing the normals on the rigged mesh..."); foreach (Mesh mesh in scene.Meshes) { List <Tuple <float, Matrix3x3> >[] weightedmats = new List <Tuple <float, Matrix3x3> > [mesh.Normals.Count];//new Matrix3x3[mesh.Normals.Count]; foreach (Assimp.Bone bone in mesh.Bones) { Matrix3x3 invbind = bone.OffsetMatrix; foreach (Assimp.VertexWeight weight in bone.VertexWeights) { if (weightedmats[weight.VertexID] == null) { weightedmats[weight.VertexID] = new List <Tuple <float, Matrix3x3> >(); } weightedmats[weight.VertexID].Add(Tuple.Create(weight.Weight, invbind)); /*Matrix3x3 weightedcurrentmat = new Matrix3x3( * weight.Weight * invbind.A1, weight.Weight * invbind.A2, weight.Weight * invbind.A1, * weight.Weight * invbind.B1, weight.Weight * invbind.B2, weight.Weight * invbind.B3, * weight.Weight * invbind.C1, weight.Weight * invbind.C2, weight.Weight * invbind.C3); * * if (weightedmats[weight.VertexID] == null) { * weightedmats[weight.VertexID] = weightedcurrentmat; * } * else { * Matrix3x3 existingmat = weightedmats[weight.VertexID]; * weightedmats[weight.VertexID] = new Matrix3x3( * existingmat.A1 + invbind.A1, existingmat.A2 + invbind.A2, existingmat.A3 + invbind.A3, * existingmat.B1 + invbind.B1, existingmat.B2 + invbind.B2, existingmat.B3 + invbind.B3, * existingmat.C1 + invbind.C1, existingmat.C2 + invbind.C2, existingmat.C3 + invbind.C3); * }*/ } } for (int i = 0; i < mesh.Normals.Count; i++) { if (weightedmats[i] == null) { continue; // means that index hasn't been weighted to so we can't do anything? } //weightedmats[i].Sort((x, y) => y.Item1.CompareTo(x.Item1)); Vector3D norm = mesh.Normals[i]; Matrix3x3 invbind = ScalarMultiply3x3(weightedmats[i][0].Item1, weightedmats[i][0].Item2); for (int j = 1; j < weightedmats[i].Count; j++) { invbind = AddMatrix3x3( invbind, ScalarMultiply3x3(weightedmats[i][j].Item1, weightedmats[i][j].Item2) ); } norm = invbind * norm; mesh.Normals[i] = norm; } } } // We check if the model mixes weighted and unweighted vertices. // If that is the case, we throw an exception here. If we don't, // an exception will be thrown later on that is less helpful to the user. if (true) { bool usesWeights = false; foreach (Mesh mesh in scene.Meshes) { bool[] weightedmats = new bool[mesh.VertexCount]; foreach (Assimp.Bone bone in mesh.Bones) { foreach (Assimp.VertexWeight weight in bone.VertexWeights) { weightedmats[weight.VertexID] = true; } } for (uint i = 0; i < mesh.VertexCount; i++) { if (weightedmats[i] == true) { usesWeights = true; } else if (usesWeights) { throw new System.Exception("Model has a mixture of weighted and unweighted vertices! Please weight all vertices to at least one bone."); } } } } VertexData = new VTX1(scene); Joints = new JNT1(scene, VertexData); Scenegraph = new INF1(scene, Joints); Textures = new TEX1(scene, Path.GetDirectoryName(modelDirectory)); SkinningEnvelopes = new EVP1(); SkinningEnvelopes.SetInverseBindMatrices(scene, Joints.FlatSkeleton); //SkinningEnvelopes.AddInverseBindMatrices(Joints.FlatSkeleton); PartialWeightData = new DRW1(scene, Joints.BoneNameIndices); Shapes = SHP1.Create(scene, Joints.BoneNameIndices, VertexData.Attributes, SkinningEnvelopes, PartialWeightData, triopt); Materials = new MAT3(scene, Textures, Shapes, mat_presets); if (additionalTexPath == null) { Materials.LoadAdditionalTextures(Textures, Path.GetDirectoryName(modelDirectory)); } else { Materials.LoadAdditionalTextures(Textures, additionalTexPath); } Materials.MapTextureNamesToIndices(Textures); foreach (Geometry.Shape shape in Shapes.Shapes) { packetCount += shape.Packets.Count; } vertexCount = VertexData.Attributes.Positions.Count; }
public void ProcessVerticesWithWeights(Mesh mesh, VertexData vertData, Dictionary <string, int> boneNames, EVP1 envelopes, DRW1 partialWeight, bool doStrip = true) { Weight[] weights = new Weight[mesh.Vertices.Count]; for (int i = 0; i < mesh.Vertices.Count; i++) { int vertexid = i; Weight vertWeight = new Weight(); foreach (Assimp.Bone bone in mesh.Bones) { foreach (VertexWeight weight in bone.VertexWeights) { if (weight.VertexID == vertexid) { vertWeight.AddWeight(weight.Weight, boneNames[bone.Name]); } } } vertWeight.reorderBones(); weights[vertexid] = vertWeight; } //Primitive prim = new Primitive(Enums.GXPrimitiveType.Triangles); List <Enums.GXVertexAttribute> activeAttribs = Descriptor.GetActiveAttributes(); AttributeData.SetAttributesFromList(activeAttribs); uint[] triindices = MakeTriIndexList(mesh); List <PrimitiveBrawl> primlist; if (doStrip) { //Console.WriteLine("Calculating triangle strips for Weighted"); TriStripper stripper = new TriStripper(triindices, weights); primlist = stripper.Strip(); } else { //Console.WriteLine("Calculating triangle list for Weighted"); primlist = new List <PrimitiveBrawl>(); PrimitiveBrawl prim = new PrimitiveBrawl(PrimType.TriangleList); // Trilist foreach (uint index in triindices) { prim.Indices.Add(index); } primlist.Add(prim); } //Console.WriteLine(String.Format("Done, {0} primitives", primlist.Count)); Packet pack = new Packet(); List <Weight> packetWeights = new List <Weight>(); int numMatrices = 0; foreach (PrimitiveBrawl primbrawl in primlist) { int numNewMatricesForFirstThreeVerts = 0; if (!packetWeights.Contains(weights[primbrawl.Indices[0]])) { numNewMatricesForFirstThreeVerts++; } if (!packetWeights.Contains(weights[primbrawl.Indices[1]])) { numNewMatricesForFirstThreeVerts++; } if (!packetWeights.Contains(weights[primbrawl.Indices[2]])) { numNewMatricesForFirstThreeVerts++; } if (numMatrices + numNewMatricesForFirstThreeVerts > MaxMatricesPerPacket) { // We won't be able to fit even the first 3 vertices of this primitive without going over the matrix limit. // So we need to start a new packet. packetWeights.Clear(); numMatrices = 0; Packets.Add(pack); pack = new Packet(); } Primitive prim = new Primitive((Enums.GXPrimitiveType)primbrawl.Type); int currvert = -1; int maxvert = primbrawl.Indices.Count - 1; Enums.GXPrimitiveType primtype = (Enums.GXPrimitiveType)primbrawl.Type; if (primtype == Enums.GXPrimitiveType.TriangleStrip) { //Console.WriteLine("Doing Tristrip"); foreach (int vertIndex in primbrawl.Indices) { currvert++; Weight vertWeight = weights[vertIndex]; int oldmat = numMatrices; if (!packetWeights.Contains(vertWeight)) { packetWeights.Add(vertWeight); numMatrices++; } //Console.WriteLine(String.Format("Added {0} matrices, is now {1}", numMatrices - oldmat, numMatrices)); // There are too many matrices, we need to create a new packet if (numMatrices > MaxMatricesPerPacket) { // If we break up and the resulting TriStrip becomes invalid, // then we need to handle those cases. //Console.WriteLine(String.Format("Breaking up because over the limit: {0}", numMatrices)); if (prim.PrimitiveType == Enums.GXPrimitiveType.TriangleStrip) { Debug.Assert(prim.Vertices.Count >= 3); } else if (prim.PrimitiveType == Enums.GXPrimitiveType.Triangles) { Debug.Assert(prim.Vertices.Count % 3 == 0); } pack.Primitives.Add(prim); Primitive newprim = new Primitive(Enums.GXPrimitiveType.TriangleStrip); Vertex prev3 = new Vertex(prim.Vertices[prim.Vertices.Count - 3]); Vertex prev2 = new Vertex(prim.Vertices[prim.Vertices.Count - 2]); Vertex prev = new Vertex(prim.Vertices[prim.Vertices.Count - 1]); bool isOdd = currvert % 2 != 0; if (isOdd) { // Need to preserve whether each vertex is even or odd inside the triangle strip. // Do this by adding an extra vertex from the previous packet to the start of this one. newprim.Vertices.Add(prev3); } newprim.Vertices.Add(prev2); newprim.Vertices.Add(prev); prim = newprim; packetWeights.Clear(); numMatrices = 0; Packets.Add(pack); Packet oldPack = pack; pack = new Packet(); // Calculate matrices for current packet in case we added vertices foreach (Vertex vertex in prim.Vertices) { if (!packetWeights.Contains(vertex.VertexWeight)) { packetWeights.Add(vertex.VertexWeight); numMatrices++; } // Re-add the matrix index for the duplicated verts to the new packet. // And recalculate the matrix index index in each vert's attribute data. uint oldMatrixIndexIndex = vertex.GetAttributeIndex(Enums.GXVertexAttribute.PositionMatrixIdx); int matrixIndex = oldPack.MatrixIndices[(int)oldMatrixIndexIndex]; if (!pack.MatrixIndices.Contains(matrixIndex)) { pack.MatrixIndices.Add(matrixIndex); } vertex.SetAttributeIndex(Enums.GXVertexAttribute.PositionMatrixIdx, (uint)pack.MatrixIndices.IndexOf(matrixIndex)); } if (!packetWeights.Contains(vertWeight)) { packetWeights.Add(vertWeight); numMatrices++; } } Vertex vert = new Vertex(); Weight curWeight = vertWeight; vert.SetWeight(curWeight); foreach (Enums.GXVertexAttribute attrib in activeAttribs) { switch (attrib) { case Enums.GXVertexAttribute.PositionMatrixIdx: int newMatrixIndex = -1; if (curWeight.WeightCount == 1) { newMatrixIndex = partialWeight.MeshWeights.IndexOf(curWeight); } else { if (!envelopes.Weights.Contains(curWeight)) { envelopes.Weights.Add(curWeight); } int envIndex = envelopes.Weights.IndexOf(curWeight); int drwIndex = partialWeight.MeshWeights.IndexOf(curWeight); if (drwIndex == -1) { throw new System.Exception($"Model has unweighted vertices in mesh \"{mesh.Name}\". Please weight all vertices to at least one bone."); } newMatrixIndex = drwIndex; partialWeight.Indices[drwIndex] = envIndex; } if (!pack.MatrixIndices.Contains(newMatrixIndex)) { pack.MatrixIndices.Add(newMatrixIndex); } vert.SetAttributeIndex(Enums.GXVertexAttribute.PositionMatrixIdx, (uint)pack.MatrixIndices.IndexOf(newMatrixIndex)); break; case Enums.GXVertexAttribute.Position: List <Vector3> posData = (List <Vector3>)vertData.GetAttributeData(Enums.GXVertexAttribute.Position); Vector3 vertPos = mesh.Vertices[vertIndex].ToOpenTKVector3(); if (curWeight.WeightCount == 1) { Matrix4 ibm = envelopes.InverseBindMatrices[curWeight.BoneIndices[0]]; Vector3 transVec = Vector3.TransformPosition(vertPos, ibm); if (!posData.Contains(transVec)) { posData.Add(transVec); } AttributeData.Positions.Add(transVec); vert.SetAttributeIndex(Enums.GXVertexAttribute.Position, (uint)posData.IndexOf(transVec)); } else { if (!posData.Contains(vertPos)) { posData.Add(vertPos); } AttributeData.Positions.Add(vertPos); vert.SetAttributeIndex(Enums.GXVertexAttribute.Position, (uint)posData.IndexOf(vertPos)); } break; case Enums.GXVertexAttribute.Normal: List <Vector3> normData = (List <Vector3>)vertData.GetAttributeData(Enums.GXVertexAttribute.Normal); Vector3 vertNrm = mesh.Normals[vertIndex].ToOpenTKVector3(); if (curWeight.WeightCount == 1) { Matrix4 ibm = envelopes.InverseBindMatrices[curWeight.BoneIndices[0]]; vertNrm = Vector3.TransformNormal(vertNrm, ibm); if (!normData.Contains(vertNrm)) { normData.Add(vertNrm); } } else { if (!normData.Contains(vertNrm)) { normData.Add(vertNrm); } } AttributeData.Normals.Add(vertNrm); vert.SetAttributeIndex(Enums.GXVertexAttribute.Normal, (uint)normData.IndexOf(vertNrm)); break; case Enums.GXVertexAttribute.Color0: case Enums.GXVertexAttribute.Color1: int colNo = (int)attrib - 11; List <Color> colData = (List <Color>)vertData.GetAttributeData(Enums.GXVertexAttribute.Color0 + colNo); Color vertCol = mesh.VertexColorChannels[colNo][vertIndex].ToSuperBMDColorRGBA(); if (colNo == 0) { AttributeData.Color_0.Add(vertCol); } else { AttributeData.Color_1.Add(vertCol); } vert.SetAttributeIndex(Enums.GXVertexAttribute.Color0 + colNo, (uint)colData.IndexOf(vertCol)); break; case Enums.GXVertexAttribute.Tex0: case Enums.GXVertexAttribute.Tex1: case Enums.GXVertexAttribute.Tex2: case Enums.GXVertexAttribute.Tex3: case Enums.GXVertexAttribute.Tex4: case Enums.GXVertexAttribute.Tex5: case Enums.GXVertexAttribute.Tex6: case Enums.GXVertexAttribute.Tex7: int texNo = (int)attrib - 13; List <Vector2> texCoordData = (List <Vector2>)vertData.GetAttributeData(Enums.GXVertexAttribute.Tex0 + texNo); Vector2 vertTexCoord = mesh.TextureCoordinateChannels[texNo][vertIndex].ToOpenTKVector2(); vertTexCoord = new Vector2(vertTexCoord.X, 1.0f - vertTexCoord.Y); switch (texNo) { case 0: AttributeData.TexCoord_0.Add(vertTexCoord); break; case 1: AttributeData.TexCoord_1.Add(vertTexCoord); break; case 2: AttributeData.TexCoord_2.Add(vertTexCoord); break; case 3: AttributeData.TexCoord_3.Add(vertTexCoord); break; case 4: AttributeData.TexCoord_4.Add(vertTexCoord); break; case 5: AttributeData.TexCoord_5.Add(vertTexCoord); break; case 6: AttributeData.TexCoord_6.Add(vertTexCoord); break; case 7: AttributeData.TexCoord_7.Add(vertTexCoord); break; } vert.SetAttributeIndex(Enums.GXVertexAttribute.Tex0 + texNo, (uint)texCoordData.IndexOf(vertTexCoord)); break; } } prim.Vertices.Add(vert); } } else if (primtype == Enums.GXPrimitiveType.Triangles) { for (int j = 0; j < primbrawl.Indices.Count / 3; j++) { int vert1Index = (int)primbrawl.Indices[j * 3 + 0]; int vert2Index = (int)primbrawl.Indices[j * 3 + 1]; int vert3Index = (int)primbrawl.Indices[j * 3 + 2]; Weight vert1Weight = weights[vert1Index]; Weight vert2Weight = weights[vert2Index]; Weight vert3Weight = weights[vert3Index]; int oldcount = numMatrices; if (!packetWeights.Contains(vert1Weight)) { packetWeights.Add(vert1Weight); numMatrices++; } if (!packetWeights.Contains(vert2Weight)) { packetWeights.Add(vert2Weight); numMatrices++; } if (!packetWeights.Contains(vert3Weight)) { packetWeights.Add(vert3Weight); numMatrices++; } // There are too many matrices, we need to create a new packet if (numMatrices > MaxMatricesPerPacket) { //Console.WriteLine(String.Format("Making new packet because previous one would have {0}", numMatrices)); //Console.WriteLine(oldcount); pack.Primitives.Add(prim); Packets.Add(pack); prim = new Primitive(Enums.GXPrimitiveType.Triangles); pack = new Packet(); packetWeights.Clear(); numMatrices = 0; if (!packetWeights.Contains(vert1Weight)) { packetWeights.Add(vert1Weight); numMatrices++; } if (!packetWeights.Contains(vert2Weight)) { packetWeights.Add(vert2Weight); numMatrices++; } if (!packetWeights.Contains(vert3Weight)) { packetWeights.Add(vert3Weight); numMatrices++; } } int[] vertexIndexArray = new int[] { vert1Index, vert2Index, vert3Index }; Weight[] vertWeightArray = new Weight[] { vert1Weight, vert2Weight, vert3Weight }; for (int i = 0; i < 3; i++) { Vertex vert = new Vertex(); int vertIndex = vertexIndexArray[i]; Weight curWeight = vertWeightArray[i]; vert.SetWeight(curWeight); foreach (Enums.GXVertexAttribute attrib in activeAttribs) { switch (attrib) { case Enums.GXVertexAttribute.PositionMatrixIdx: int newMatrixIndex = -1; if (curWeight.WeightCount == 1) { newMatrixIndex = partialWeight.MeshWeights.IndexOf(curWeight); } else { if (!envelopes.Weights.Contains(curWeight)) { envelopes.Weights.Add(curWeight); } int envIndex = envelopes.Weights.IndexOf(curWeight); int drwIndex = partialWeight.MeshWeights.IndexOf(curWeight); if (drwIndex == -1) { throw new System.Exception($"Model has unweighted vertices in mesh \"{mesh.Name}\". Please weight all vertices to at least one bone."); } newMatrixIndex = drwIndex; partialWeight.Indices[drwIndex] = envIndex; } if (!pack.MatrixIndices.Contains(newMatrixIndex)) { pack.MatrixIndices.Add(newMatrixIndex); } vert.SetAttributeIndex(Enums.GXVertexAttribute.PositionMatrixIdx, (uint)pack.MatrixIndices.IndexOf(newMatrixIndex)); break; case Enums.GXVertexAttribute.Position: List <Vector3> posData = (List <Vector3>)vertData.GetAttributeData(Enums.GXVertexAttribute.Position); Vector3 vertPos = mesh.Vertices[vertIndex].ToOpenTKVector3(); if (curWeight.WeightCount == 1) { Matrix4 ibm = envelopes.InverseBindMatrices[curWeight.BoneIndices[0]]; Vector3 transVec = Vector3.TransformPosition(vertPos, ibm); if (!posData.Contains(transVec)) { posData.Add(transVec); } AttributeData.Positions.Add(transVec); vert.SetAttributeIndex(Enums.GXVertexAttribute.Position, (uint)posData.IndexOf(transVec)); } else { if (!posData.Contains(vertPos)) { posData.Add(vertPos); } AttributeData.Positions.Add(vertPos); vert.SetAttributeIndex(Enums.GXVertexAttribute.Position, (uint)posData.IndexOf(vertPos)); } break; case Enums.GXVertexAttribute.Normal: List <Vector3> normData = (List <Vector3>)vertData.GetAttributeData(Enums.GXVertexAttribute.Normal); Vector3 vertNrm = mesh.Normals[vertIndex].ToOpenTKVector3(); if (curWeight.WeightCount == 1) { Matrix4 ibm = envelopes.InverseBindMatrices[curWeight.BoneIndices[0]]; vertNrm = Vector3.TransformNormal(vertNrm, ibm); if (!normData.Contains(vertNrm)) { normData.Add(vertNrm); } } else { if (!normData.Contains(vertNrm)) { normData.Add(vertNrm); } } AttributeData.Normals.Add(vertNrm); vert.SetAttributeIndex(Enums.GXVertexAttribute.Normal, (uint)normData.IndexOf(vertNrm)); break; case Enums.GXVertexAttribute.Color0: case Enums.GXVertexAttribute.Color1: int colNo = (int)attrib - 11; List <Color> colData = (List <Color>)vertData.GetAttributeData(Enums.GXVertexAttribute.Color0 + colNo); Color vertCol = mesh.VertexColorChannels[colNo][vertIndex].ToSuperBMDColorRGBA(); if (colNo == 0) { AttributeData.Color_0.Add(vertCol); } else { AttributeData.Color_1.Add(vertCol); } vert.SetAttributeIndex(Enums.GXVertexAttribute.Color0 + colNo, (uint)colData.IndexOf(vertCol)); break; case Enums.GXVertexAttribute.Tex0: case Enums.GXVertexAttribute.Tex1: case Enums.GXVertexAttribute.Tex2: case Enums.GXVertexAttribute.Tex3: case Enums.GXVertexAttribute.Tex4: case Enums.GXVertexAttribute.Tex5: case Enums.GXVertexAttribute.Tex6: case Enums.GXVertexAttribute.Tex7: int texNo = (int)attrib - 13; List <Vector2> texCoordData = (List <Vector2>)vertData.GetAttributeData(Enums.GXVertexAttribute.Tex0 + texNo); Vector2 vertTexCoord = mesh.TextureCoordinateChannels[texNo][vertIndex].ToOpenTKVector2(); vertTexCoord = new Vector2(vertTexCoord.X, 1.0f - vertTexCoord.Y); switch (texNo) { case 0: AttributeData.TexCoord_0.Add(vertTexCoord); break; case 1: AttributeData.TexCoord_1.Add(vertTexCoord); break; case 2: AttributeData.TexCoord_2.Add(vertTexCoord); break; case 3: AttributeData.TexCoord_3.Add(vertTexCoord); break; case 4: AttributeData.TexCoord_4.Add(vertTexCoord); break; case 5: AttributeData.TexCoord_5.Add(vertTexCoord); break; case 6: AttributeData.TexCoord_6.Add(vertTexCoord); break; case 7: AttributeData.TexCoord_7.Add(vertTexCoord); break; } vert.SetAttributeIndex(Enums.GXVertexAttribute.Tex0 + texNo, (uint)texCoordData.IndexOf(vertTexCoord)); break; } } prim.Vertices.Add(vert); } } } /* * if (prim.PrimitiveType == Enums.GXPrimitiveType.TriangleStrip) { * Debug.Assert(prim.Vertices.Count >= 3); * } * else if (prim.PrimitiveType == Enums.GXPrimitiveType.Triangles) { * Debug.Assert(prim.Vertices.Count % 3 == 0); * }*/ //Console.WriteLine(String.Format("We had this many matrices: {0}", numMatrices)); pack.Primitives.Add(prim); } Packets.Add(pack); int mostmatrices = 0; if (true) { List <Weight> packWeights = new List <Weight>(); foreach (Packet packet in Packets) { int matrices = 0; foreach (Primitive prim in packet.Primitives) { foreach (Vertex vert in prim.Vertices) { if (!packWeights.Contains(vert.VertexWeight)) { packWeights.Add(vert.VertexWeight); matrices++; } } if (prim.PrimitiveType == Enums.GXPrimitiveType.TriangleStrip) { Debug.Assert(prim.Vertices.Count >= 3); } else if (prim.PrimitiveType == Enums.GXPrimitiveType.Triangles) { Debug.Assert(prim.Vertices.Count % 3 == 0); } } if (matrices > mostmatrices) { mostmatrices = matrices; } //Debug.Assert(matrices <= MaxMatricesPerPacket); //Console.WriteLine(matrices); packWeights.Clear(); } } //Console.WriteLine(String.Format("Most matrices: {0}", mostmatrices)); }
public Model(EndianBinaryReader reader, Arguments args) { ModelStats = new BMDInfo(); int j3d2Magic = reader.ReadInt32(); int modelMagic = reader.ReadInt32(); if (j3d2Magic != 0x4A334432 && j3d2Magic != 0x3244334A) { throw new Exception("Model was not a BMD or BDL! (J3D2 magic not found)"); } if ((modelMagic != 0x62646C34) && (modelMagic != 0x626D6433 && modelMagic != 0x346C6462)) { throw new Exception("Model was not a BMD or BDL! (Model type was not bmd3 or bdl4)"); } //Reversed magic found. File uses little endian byte order. if (j3d2Magic == 0x3244334A) { reader.CurrentEndian = Endian.Little; littleEndian = true; } int modelSize = reader.ReadInt32(); int sectionCount = reader.ReadInt32(); ModelStats.TotalSize = modelSize; // Skip the dummy section, SVR3 reader.Skip(16); Scenegraph = new INF1(reader, 32, ModelStats); VertexData = new VTX1(reader, (int)reader.BaseStream.Position, ModelStats); SkinningEnvelopes = new EVP1(reader, (int)reader.BaseStream.Position, ModelStats); PartialWeightData = new DRW1(reader, (int)reader.BaseStream.Position, ModelStats); Joints = new JNT1(reader, (int)reader.BaseStream.Position, ModelStats); SkinningEnvelopes.SetInverseBindMatrices(Joints.FlatSkeleton); Shapes = SHP1.Create(reader, (int)reader.BaseStream.Position, ModelStats); Shapes.SetVertexWeights(SkinningEnvelopes, PartialWeightData); Materials = new MAT3(reader, (int)reader.BaseStream.Position, ModelStats); SkipMDL3(reader); Textures = new TEX1(reader, (int)reader.BaseStream.Position, ModelStats); Materials.SetTextureNames(Textures); if (args.output_materials_path != "") { Materials.DumpMaterials(Path.GetDirectoryName(args.output_materials_path)); } else { if (args.output_path != "") { string outDir = Path.GetDirectoryName(args.output_path); string filenameNoExt = Path.GetFileNameWithoutExtension(args.input_path); Materials.DumpMaterials(Path.Combine(outDir, filenameNoExt + "_materials.json")); } else { string inDir = Path.GetDirectoryName(args.input_path); string filenameNoExt = Path.GetFileNameWithoutExtension(args.input_path); Materials.DumpMaterials(Path.Combine(inDir, filenameNoExt + "_materials.json")); } } foreach (Geometry.Shape shape in Shapes.Shapes) { packetCount += shape.Packets.Count; } vertexCount = VertexData.Attributes.Positions.Count; }
public Model(Scene scene, Arguments args, List <SuperBMDLib.Materials.Material> mat_presets = null, string additionalTexPath = null) { ModelStats = new BMDInfo(); if (args.ensure_one_material_per_mesh) { EnsureOneMaterialPerMesh(scene); } Console.WriteLine(); if (args.sort_meshes) { SortMeshesByObjectNames(scene); Console.WriteLine(); } // For FBX mesh names are empty, instead we need to check the nodes and rename // the meshes after the node names. foreach (Assimp.Node node in scene.RootNode.Children) { foreach (int meshindex in node.MeshIndices) { Assimp.Mesh mesh = scene.Meshes[meshindex]; if (mesh.Name == String.Empty) { mesh.Name = node.Name; } } } Console.WriteLine(); Console.Write("Searching for the Skeleton Root"); Assimp.Node root = null; for (int i = 0; i < scene.RootNode.ChildCount; i++) { if (scene.RootNode.Children[i].Name.ToLowerInvariant() == "skeleton_root") { if (scene.RootNode.Children[i].ChildCount == 0) { throw new System.Exception("skeleton_root has no children! If you are making a rigged model, make sure skeleton_root contains the root of your skeleton."); } root = scene.RootNode.Children[i].Children[0]; break; } Console.Write("."); } Console.Write(root == null ? "✓ No Skeleton found" : "✓ Skeleton Found"); Console.WriteLine(); foreach (Mesh mesh in scene.Meshes) { if (mesh.HasBones && root == null) { throw new System.Exception("Model uses bones but the skeleton root has not been found! Make sure your skeleton is inside a dummy object called 'skeleton_root'."); } } if (args.rotate_model) { Console.WriteLine(); Console.Write("Rotating the model"); int i = 0; Matrix4x4 rotate = Matrix4x4.FromRotationX((float)(-(1 / 2.0) * Math.PI)); Matrix4x4 rotateinv = rotate; rotateinv.Inverse(); foreach (Mesh mesh in scene.Meshes) { if (root != null) { foreach (Assimp.Bone bone in mesh.Bones) { bone.OffsetMatrix = rotateinv * bone.OffsetMatrix; Console.Write("|"); } } for (i = 0; i < mesh.VertexCount; i++) { Vector3D vertex = mesh.Vertices[i]; vertex.Set(vertex.X, vertex.Z, -vertex.Y); mesh.Vertices[i] = vertex; } for (i = 0; i < mesh.Normals.Count; i++) { Vector3D norm = mesh.Normals[i]; norm.Set(norm.X, norm.Z, -norm.Y); mesh.Normals[i] = norm; } Console.Write("."); } Console.Write("✓"); Console.WriteLine(); } foreach (Mesh mesh in scene.Meshes) { if (mesh.HasNormals) { for (int i = 0; i < mesh.Normals.Count; i++) { Vector3D normal = mesh.Normals[i]; normal.X = (float)Math.Round(normal.X, 4); normal.Y = (float)Math.Round(normal.Y, 4); normal.Z = (float)Math.Round(normal.Z, 4); mesh.Normals[i] = normal; } } } Console.WriteLine(); Console.WriteLine("Generating the Vertex Data ->"); VertexData = new VTX1(scene, args.forceFloat); Console.WriteLine(); Console.Write("Generating the Bone Data"); Joints = new JNT1(scene, VertexData); Console.WriteLine(); Console.WriteLine("Generating the Texture Data -> "); Textures = new TEX1(scene, args); Console.WriteLine(); Console.Write("Generating the Envelope Data"); SkinningEnvelopes = new EVP1(); SkinningEnvelopes.SetInverseBindMatrices(scene, Joints.FlatSkeleton); Console.WriteLine(); Console.Write("Generating the Weight Data"); PartialWeightData = new DRW1(scene, Joints.BoneNameIndices); Console.WriteLine(); Console.WriteLine(); Console.WriteLine("Generating the Mesh Data ->"); Shapes = SHP1.Create(scene, Joints.BoneNameIndices, VertexData.Attributes, SkinningEnvelopes, PartialWeightData, args.tristrip_mode, args.degenerateTriangles); Console.WriteLine(); Console.WriteLine("Generating the Material Data ->"); Materials = new MAT3(scene, Textures, Shapes, args, mat_presets); Console.WriteLine(); Console.WriteLine("Loading the Textures ->"); if (additionalTexPath == null) { Materials.LoadAdditionalTextures(Textures, Path.GetDirectoryName(args.input_path), args.readMipmaps); } else { Materials.LoadAdditionalTextures(Textures, additionalTexPath, args.readMipmaps); } Materials.MapTextureNamesToIndices(Textures); if (args.output_bdl) { Console.WriteLine(); Console.WriteLine("Compiling the MDL3 ->"); MatDisplayList = new MDL3(Materials.m_Materials, Textures.Textures); } if (args.littleEndian) { littleEndian = true; } Console.WriteLine(); Console.Write("Generating the Joints"); Scenegraph = new INF1(scene, Joints); foreach (Geometry.Shape shape in Shapes.Shapes) { packetCount += shape.Packets.Count; } vertexCount = VertexData.Attributes.Positions.Count; }
public void ProcessVerticesWithWeights(Mesh mesh, VertexData vertData, Dictionary <string, int> boneNames, EVP1 envelopes, DRW1 partialWeight) { Packet pack = new Packet(); Primitive prim = new Primitive(Enums.GXPrimitiveType.Triangles); List <Enums.GXVertexAttribute> activeAttribs = Descriptor.GetActiveAttributes(); AttributeData.SetAttributesFromList(activeAttribs); List <Weight> packetWeights = new List <Weight>(); int numMatrices = 0; foreach (Face face in mesh.Faces) { int vert1Index = face.Indices[0]; int vert2Index = face.Indices[1]; int vert3Index = face.Indices[2]; Weight vert1Weight = new Weight(); Weight vert2Weight = new Weight(); Weight vert3Weight = new Weight(); // Get the weights for this tri's vertices foreach (Assimp.Bone bone in mesh.Bones) { foreach (VertexWeight weight in bone.VertexWeights) { if (weight.VertexID == vert1Index) { vert1Weight.AddWeight(weight.Weight, boneNames[bone.Name]); } if (weight.VertexID == vert2Index) { vert2Weight.AddWeight(weight.Weight, boneNames[bone.Name]); } if (weight.VertexID == vert3Index) { vert3Weight.AddWeight(weight.Weight, boneNames[bone.Name]); } } } if (!packetWeights.Contains(vert1Weight)) { numMatrices += vert1Weight.WeightCount; } if (!packetWeights.Contains(vert2Weight)) { numMatrices += vert2Weight.WeightCount; } if (!packetWeights.Contains(vert3Weight)) { numMatrices += vert1Weight.WeightCount; } // There are too many matrices, we need to create a new packet if (numMatrices > 10) { pack.Primitives.Add(prim); Packets.Add(pack); prim = new Primitive(Enums.GXPrimitiveType.Triangles); pack = new Packet(); packetWeights.Clear(); numMatrices = 0; packetWeights.Add(vert1Weight); packetWeights.Add(vert2Weight); packetWeights.Add(vert3Weight); if (!packetWeights.Contains(vert1Weight)) { numMatrices += vert1Weight.WeightCount; } if (!packetWeights.Contains(vert2Weight)) { numMatrices += vert2Weight.WeightCount; } if (!packetWeights.Contains(vert3Weight)) { numMatrices += vert1Weight.WeightCount; } } // Matrix count is below 10, we can continue using the current packet else { if (!packetWeights.Contains(vert1Weight)) { packetWeights.Add(vert1Weight); } if (!packetWeights.Contains(vert2Weight)) { packetWeights.Add(vert2Weight); } if (!packetWeights.Contains(vert3Weight)) { packetWeights.Add(vert3Weight); } } int[] vertexIndexArray = new int[] { vert1Index, vert2Index, vert3Index }; Weight[] vertWeightArray = new Weight[] { vert1Weight, vert2Weight, vert3Weight }; for (int i = 0; i < 3; i++) { Vertex vert = new Vertex(); int vertIndex = vertexIndexArray[i]; Weight curWeight = vertWeightArray[i]; vert.SetWeight(curWeight); foreach (Enums.GXVertexAttribute attrib in activeAttribs) { switch (attrib) { case Enums.GXVertexAttribute.PositionMatrixIdx: int newMatrixIndex = -1; if (curWeight.WeightCount == 1) { newMatrixIndex = partialWeight.MeshWeights.IndexOf(curWeight); } else { if (!envelopes.Weights.Contains(curWeight)) { envelopes.Weights.Add(curWeight); } int envIndex = envelopes.Weights.IndexOf(curWeight); int drwIndex = partialWeight.MeshWeights.IndexOf(curWeight); newMatrixIndex = drwIndex; partialWeight.Indices[drwIndex] = envIndex; } if (!pack.MatrixIndices.Contains(newMatrixIndex)) { pack.MatrixIndices.Add(newMatrixIndex); } vert.SetAttributeIndex(Enums.GXVertexAttribute.PositionMatrixIdx, (uint)pack.MatrixIndices.IndexOf(newMatrixIndex)); break; case Enums.GXVertexAttribute.Position: List <Vector3> posData = (List <Vector3>)vertData.GetAttributeData(Enums.GXVertexAttribute.Position); Vector3 vertPos = mesh.Vertices[vertIndex].ToOpenTKVector3(); if (curWeight.WeightCount == 1) { Matrix4 ibm = envelopes.InverseBindMatrices[curWeight.BoneIndices[0]]; Vector3 transVec = Vector3.TransformPosition(vertPos, ibm); posData.Add(transVec); AttributeData.Positions.Add(transVec); vert.SetAttributeIndex(Enums.GXVertexAttribute.Position, (uint)posData.IndexOf(transVec)); } else { AttributeData.Positions.Add(vertPos); vert.SetAttributeIndex(Enums.GXVertexAttribute.Position, (uint)posData.IndexOf(vertPos)); } break; case Enums.GXVertexAttribute.Normal: List <Vector3> normData = (List <Vector3>)vertData.GetAttributeData(Enums.GXVertexAttribute.Normal); Vector3 vertNrm = mesh.Normals[vertIndex].ToOpenTKVector3(); AttributeData.Normals.Add(vertNrm); vert.SetAttributeIndex(Enums.GXVertexAttribute.Normal, (uint)normData.IndexOf(vertNrm)); break; case Enums.GXVertexAttribute.Color0: case Enums.GXVertexAttribute.Color1: int colNo = (int)attrib - 11; List <Color> colData = (List <Color>)vertData.GetAttributeData(Enums.GXVertexAttribute.Color0 + colNo); Color vertCol = mesh.VertexColorChannels[colNo][vertIndex].ToSuperBMDColorRGBA(); if (colNo == 0) { AttributeData.Color_0.Add(vertCol); } else { AttributeData.Color_1.Add(vertCol); } vert.SetAttributeIndex(Enums.GXVertexAttribute.Color0 + colNo, (uint)colData.IndexOf(vertCol)); break; case Enums.GXVertexAttribute.Tex0: case Enums.GXVertexAttribute.Tex1: case Enums.GXVertexAttribute.Tex2: case Enums.GXVertexAttribute.Tex3: case Enums.GXVertexAttribute.Tex4: case Enums.GXVertexAttribute.Tex5: case Enums.GXVertexAttribute.Tex6: case Enums.GXVertexAttribute.Tex7: int texNo = (int)attrib - 13; List <Vector2> texCoordData = (List <Vector2>)vertData.GetAttributeData(Enums.GXVertexAttribute.Tex0 + texNo); Vector2 vertTexCoord = mesh.TextureCoordinateChannels[texNo][vertIndex].ToOpenTKVector2(); vertTexCoord = new Vector2(vertTexCoord.X, 1.0f - vertTexCoord.Y); switch (texNo) { case 0: AttributeData.TexCoord_0.Add(vertTexCoord); break; case 1: AttributeData.TexCoord_1.Add(vertTexCoord); break; case 2: AttributeData.TexCoord_2.Add(vertTexCoord); break; case 3: AttributeData.TexCoord_3.Add(vertTexCoord); break; case 4: AttributeData.TexCoord_4.Add(vertTexCoord); break; case 5: AttributeData.TexCoord_5.Add(vertTexCoord); break; case 6: AttributeData.TexCoord_6.Add(vertTexCoord); break; case 7: AttributeData.TexCoord_7.Add(vertTexCoord); break; } vert.SetAttributeIndex(Enums.GXVertexAttribute.Tex0 + texNo, (uint)texCoordData.IndexOf(vertTexCoord)); break; } } prim.Vertices.Add(vert); } } pack.Primitives.Add(prim); Packets.Add(pack); }