private void LoadTagDataFromFile(EndianBinaryReader reader, int tagCount, bool dumpTextures, bool dumpShaders) { for (int i = 0; i < tagCount; i++) { long tagStart = reader.BaseStream.Position; string tagName = reader.ReadString(4); int tagSize = reader.ReadInt32(); switch (tagName) { // INFO - Vertex Count, Scene Hierarchy case "INF1": INF1Tag = new INF1(); INF1Tag.LoadINF1FromStream(reader, tagStart); break; // VERTEX - Stores vertex arrays for pos/normal/color0/tex0 etc. // Contains VertexAttributes which describe how the data is stored/laid out. case "VTX1": VTX1Tag = new VTX1(); VTX1Tag.LoadVTX1FromStream(reader, tagStart, tagSize); break; // ENVELOPES - Defines vertex weights for skinning case "EVP1": EVP1Tag = new EVP1(); EVP1Tag.LoadEVP1FromStream(reader, tagStart); break; // DRAW (Skeletal Animation Data) - Stores which matrices (?) are weighted, and which are used directly case "DRW1": DRW1Tag = new DRW1(); DRW1Tag.LoadDRW1FromStream(reader, tagStart); break; // JOINTS - Stores the skeletal joints (position, rotation, scale, etc...) case "JNT1": JNT1Tag = new JNT1(); JNT1Tag.LoadJNT1FromStream(reader, tagStart); JNT1Tag.CalculateParentJointsForSkeleton(INF1Tag.HierarchyRoot); break; // SHAPE - Face/Triangle information for model. case "SHP1": SHP1Tag = new SHP1(); SHP1Tag.ReadSHP1FromStream(reader, tagStart); CalculateModelBounds(); break; // MATERIAL - Stores materials (which describes how textures, etc. are drawn) case "MAT3": MAT3Tag = new MAT3(); MAT3Tag.LoadMAT3FromStream(reader, tagStart); break; // TEXTURES - Stores binary texture images. case "TEX1": TEX1Tag = new TEX1(); TEX1Tag.LoadTEX1FromStream(reader, tagStart, dumpTextures); break; // MODEL - Seems to be bypass commands for Materials and invokes GX registers directly. case "MDL3": break; } // Skip the stream reader to the start of the next tag since it gets moved around during loading. reader.BaseStream.Position = tagStart + tagSize; } INF1Tag.LinkData(MAT3Tag, SHP1Tag); Tick(float.Epsilon); if (EVP1Tag.InverseBindPose.Count <= 0) { EVP1Tag.GenerateInverseBindMatrices(JNT1Tag); } DRW1Tag.UpdateMatrices(JNT1Tag.BindJoints, EVP1Tag); SHP1Tag.LinkData(VTX1Tag, DRW1Tag, EVP1Tag); SHP1Tag.UploadShapesToGPU(); }
public void LinkData(VTX1 vertex_data, DRW1 draw_matrix_data, EVP1 envelope_data) { LinkVertexData(vertex_data); LinkSkinningData(draw_matrix_data, envelope_data); }
private void LinkSkinningData(DRW1 draw_matrix_data, EVP1 envelope_data) { foreach (Shape shape in Shapes) { int vertex_index = 0; shape.VertexDescription.EnableAttribute(ShaderAttributeIds.SkinIndices); shape.VertexDescription.EnableAttribute(ShaderAttributeIds.SkinWeights); foreach (MatrixGroup group in shape.MatrixGroups) { foreach (MeshVertexIndex vertex in group.Indices) { int draw_mat_index = vertex.PosMtxIndex >= 0 ? group.MatrixDataTable.MatrixTable[vertex.PosMtxIndex] : 0; int transform_index = draw_matrix_data.TransformIndexTable[draw_mat_index]; // If this vertex is influenced by mulltiple bones, we build vec4s containing the indices and weights // for each bone that influences it. if (draw_matrix_data.IsPartiallyWeighted[draw_mat_index]) { EVP1.Envelope envelope = envelope_data.Envelopes[transform_index]; // Because we're using vec4s for the skinning data, any vertex with more than 4 bones influencing it // is invalid to us. We're going to assume this never happens, but in the event that it does, this assert // will tell us. Trace.Assert(envelope.NumBones <= 4); Vector4 indices = new Vector4(0, 0, 0, 0); Vector4 weights = new Vector4(0, 0, 0, 0); for (int i = 0; i < envelope.NumBones; i++) { indices[i] = envelope.BoneIndexes[i]; weights[i] = envelope.BoneWeights[i]; } shape.VertexData.SkinIndices.Add(indices); shape.VertexData.SkinWeights.Add(weights); } // If the vertex only has a single bone influencing it, we need to do some extra work. The vertex is stored in // bone space instead of bind space. We need to transform the vertex into bind space so we can work with it more easily. else { Vector4 pos_as_vec4 = new Vector4(shape.VertexData.Position[vertex_index], 1.0f); shape.VertexData.Position[vertex_index] = Vector4.Transform(pos_as_vec4, envelope_data.InverseBindPose[transform_index].Inverted()).Xyz; Vector4 nrm_as_vec4 = new Vector4(shape.VertexData.Normal[vertex_index], 0.0f); Matrix4 nrm_mat = envelope_data.InverseBindPose[transform_index]; nrm_mat.Transpose(); shape.VertexData.Normal[vertex_index] = Vector4.Transform(nrm_as_vec4, nrm_mat).Xyz; shape.VertexData.SkinIndices.Add(new Vector4(transform_index, 0, 0, 0)); shape.VertexData.SkinWeights.Add(new Vector4(1, 0, 0, 0)); } vertex_index++; } } } }
private void LoadTagDataFromFile(EndianBinaryReader reader, int tagCount, bool dumpTextures, bool dumpShaders) { for (int i = 0; i < tagCount; i++) { long tagStart = reader.BaseStream.Position; string tagName = reader.ReadString(4); int tagSize = reader.ReadInt32(); switch (tagName) { // INFO - Vertex Count, Scene Hierarchy case "INF1": INF1Tag = new INF1(); INF1Tag.LoadINF1FromStream(reader, tagStart); break; // VERTEX - Stores vertex arrays for pos/normal/color0/tex0 etc. // Contains VertexAttributes which describe how the data is stored/laid out. case "VTX1": VTX1Tag = new VTX1(); VTX1Tag.LoadVTX1FromStream(reader, tagStart, tagSize); break; // ENVELOPES - Defines vertex weights for skinning case "EVP1": EVP1Tag = new EVP1(); EVP1Tag.LoadEVP1FromStream(reader, tagStart); break; // DRAW (Skeletal Animation Data) - Stores which matrices (?) are weighted, and which are used directly case "DRW1": DRW1Tag = new DRW1(); DRW1Tag.LoadDRW1FromStream(reader, tagStart); break; // JOINTS - Stores the skeletal joints (position, rotation, scale, etc...) case "JNT1": JNT1Tag = new JNT1(); JNT1Tag.LoadJNT1FromStream(reader, tagStart); JNT1Tag.CalculateParentJointsForSkeleton(INF1Tag.HierarchyRoot); break; // SHAPE - Face/Triangle information for model. case "SHP1": SHP1Tag = new SHP1(); SHP1Tag.ReadSHP1FromStream(reader, tagStart, VTX1Tag.VertexData); break; // MATERIAL - Stores materials (which describes how textures, etc. are drawn) case "MAT3": MAT3Tag = new MAT3(); MAT3Tag.LoadMAT3FromStream(reader, tagStart); break; // TEXTURES - Stores binary texture images. case "TEX1": TEX1Tag = new TEX1(); TEX1Tag.LoadTEX1FromStream(reader, tagStart, dumpTextures); break; // MODEL - Seems to be bypass commands for Materials and invokes GX registers directly. case "MDL3": break; } // Skip the stream reader to the start of the next tag since it gets moved around during loading. reader.BaseStream.Position = tagStart + tagSize; } // To generate shaders we need to know which vertex attributes need to be enabled for the shader. However, // the shader has no knowledge in our book as to what attributes are enabled. Theoretically we could enable // them on the fly as something requested it, but that'd involve more code that I don't want to do right now. // To resolve, we iterate once through the hierarchy to see which mesh is called after a material and bind the // vertex descriptions. Material dummyMat = null; AssignVertexAttributesToMaterialsRecursive(INF1Tag.HierarchyRoot, ref dummyMat, MAT3Tag); // Now that the vertex attributes are assigned to the materials, generate a shader from the data. GenerateShadersForMaterials(MAT3Tag, dumpShaders); // Iterate through the shapes and calculate a bounding box which encompasses all of them. Vector3 min = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue); Vector3 max = new Vector3(float.MinValue, float.MinValue, float.MinValue); foreach (var shape in SHP1Tag.Shapes) { Vector3 sMin = shape.BoundingBox.Min; Vector3 sMax = shape.BoundingBox.Max; if (sMin.X < min.X) { min.X = sMin.X; } if (sMax.X > max.X) { max.X = sMax.X; } if (sMin.Y < min.Y) { min.Y = sMin.Y; } if (sMax.Y > max.Y) { max.Y = sMax.Y; } if (sMin.Z < min.Z) { min.Z = sMin.Z; } if (sMax.Z > max.Z) { max.Z = sMax.Z; } } BoundingBox = new FAABox(min, max); BoundingSphere = new FSphere(BoundingBox.Center, BoundingBox.Max.Length); }