public void CopyFlags(Gcmf other) { //if(Meshes.Count != other.Meshes.Count) //{ // throw new InvalidOperationException("Replacing model must have the same number of meshes."); //} SectionFlags = other.SectionFlags; BoundingSphereCenter = other.BoundingSphereCenter; TransformMatrices = other.TransformMatrices; TransformMatrixDefaultIdxs = other.TransformMatrixDefaultIdxs; Type8Unknown1 = other.Type8Unknown1; Type8Unknown2 = other.Type8Unknown2; if (Meshes.Count == other.Meshes.Count) { for (int i = 0; i < Meshes.Count && i < other.Meshes.Count; i++) { GcmfMesh thisMesh = Meshes[i]; GcmfMesh otherMesh = other.Meshes[i]; thisMesh.Layer = otherMesh.Layer; thisMesh.RenderFlags = otherMesh.RenderFlags; thisMesh.Unk4 = otherMesh.Unk4; thisMesh.Unk8 = otherMesh.Unk8; thisMesh.UnkC = otherMesh.UnkC; thisMesh.Unk10 = otherMesh.Unk10; // Header section info (Section flags) thisMesh.Unk14 = otherMesh.Unk14; // Header section info (Vertex flags) thisMesh.TransformMatrixSpecificIdxsObj1 = otherMesh.TransformMatrixSpecificIdxsObj1; thisMesh.TransformMatrixSpecificIdxsObj2 = otherMesh.TransformMatrixSpecificIdxsObj2; thisMesh.BoundingSphereCenter = otherMesh.BoundingSphereCenter; thisMesh.Unk3C = otherMesh.Unk3C; thisMesh.Unk40 = otherMesh.Unk40; } } if (Materials.Count == other.Materials.Count) { for (int i = 0; i < Materials.Count && i < other.Materials.Count; i++) { GcmfMaterial thisMaterial = Materials[i]; GcmfMaterial otherMaterial = other.Materials[i]; thisMaterial.Flags = otherMaterial.Flags; thisMaterial.Unk6 = otherMaterial.Unk6; thisMaterial.AnisotropyLevel = otherMaterial.AnisotropyLevel; thisMaterial.UnkC = otherMaterial.UnkC; thisMaterial.Unk10 = otherMaterial.Unk10; } } }
internal void Load(EndianBinaryReader input, GxGame game) { int baseOffset = Convert.ToInt32(input.BaseStream.Position); // Load GCMF header if (input.ReadUInt32() != GcmfMagic) { throw new InvalidGmaFileException("Expected Gcmf[0x00] == GcmfMagic."); } SectionFlags = input.ReadUInt32(); BoundingSphereCenter = new Vector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle()); BoundingSphereRadius = input.ReadSingle(); int numMaterials = (int)input.ReadUInt16(); int numLayer1Meshes = (int)input.ReadUInt16(); int numLayer2Meshes = (int)input.ReadUInt16(); int transformMatrixCount = (int)input.ReadByte(); if (input.ReadByte() != 0) { throw new InvalidGmaFileException("Expected Gcmf[0x1F] == 0"); } int headerSize = input.ReadInt32(); if (input.ReadUInt32() != 0) { throw new InvalidGmaFileException("Expected Gcmf[0x24] == 0"); } input.Read(TransformMatrixDefaultIdxs, 0, 8); if (input.ReadUInt32() != 0) { throw new InvalidGmaFileException("Expected Gcmf[0x30] == 0"); } if (input.ReadUInt32() != 0) { throw new InvalidGmaFileException("Expected Gcmf[0x34] == 0"); } if (input.ReadUInt32() != 0) { throw new InvalidGmaFileException("Expected Gcmf[0x38] == 0"); } if (input.ReadUInt32() != 0) { throw new InvalidGmaFileException("Expected Gcmf[0x3C] == 0"); } // Load materials for (int i = 0; i < numMaterials; i++) { GcmfMaterial mat = new GcmfMaterial(); mat.Load(input, i); Materials.Add(mat); } if ((SectionFlags & (uint)~(GcmfSectionFlags._16Bit | GcmfSectionFlags.StitchingModel | GcmfSectionFlags.SkinModel | GcmfSectionFlags.EffectiveModel)) != 0) { throw new InvalidGmaFileException("Unknown GCMF section flags."); } if ((SectionFlags & (uint)GcmfSectionFlags.StitchingModel) != 0 || (SectionFlags & (uint)GcmfSectionFlags.SkinModel) != 0) { for (int i = 0; i < transformMatrixCount; i++) { GcmfTransformMatrix tmtx = new GcmfTransformMatrix(); tmtx.Load(input); TransformMatrices.Add(tmtx); } } else { if (transformMatrixCount != 0) { throw new InvalidGmaFileException("GcmfSection: No transform matrices expected, but transformMatrixCount != 0?"); } } if (PaddingUtils.Align(Convert.ToInt32(input.BaseStream.Position), 0x20) != baseOffset + headerSize) { throw new InvalidGmaFileException("Gcmf [End Header Offset] mismatch."); } input.BaseStream.Position = baseOffset + headerSize; if ((SectionFlags & (uint)GcmfSectionFlags.SkinModel) != 0 || (SectionFlags & (uint)GcmfSectionFlags.EffectiveModel) != 0) { int sectionBaseOffset = Convert.ToInt32(input.BaseStream.Position); int numVertices = input.ReadInt32(); int offsetPartType8Unknown1 = input.ReadInt32(); int offsetPartVertexPool = input.ReadInt32(); int offsetPartMeshData = input.ReadInt32(); int offsetPartType8Unknown2 = input.ReadInt32(); if (input.ReadUInt32() != 0) { throw new InvalidGmaFileException("Gcmf[PreSectionHdr-0x14]"); } if (input.ReadUInt32() != 0) { throw new InvalidGmaFileException("Gcmf[PreSectionHdr-0x18]"); } if (input.ReadUInt32() != 0) { throw new InvalidGmaFileException("Gcmf[PreSectionHdr-0x1C]"); } // Load the mesh headers List <GcmfMesh.HeaderSectionInfo> meshHeaderSectionInfos = new List <GcmfMesh.HeaderSectionInfo>(); for (int i = 0; i < numLayer1Meshes + numLayer2Meshes; i++) { GcmfMesh mesh = new GcmfMesh(); meshHeaderSectionInfos.Add(mesh.LoadHeader(input, (i < numLayer1Meshes) ? GcmfMesh.MeshLayer.Layer1 : GcmfMesh.MeshLayer.Layer2)); Meshes.Add(mesh); } if (Convert.ToInt32(input.BaseStream.Position) != sectionBaseOffset + offsetPartVertexPool) { throw new InvalidGmaFileException("Gcmf [PreSectionHdr] offsetPartVertexPool doesn't match expected value."); } // Load the vertex pool, i.e. the vertices referenced from the indexed triangle strips for (int i = 0; i < numVertices; i++) { GcmfVertex vtx = new GcmfVertex(); vtx.LoadIndexed(input); VertexPool.Add(vtx); } // Just because it probably won't be there doesn't mean we shouldn't allow it // and some stages have the things that are not allowed /*if ((game == GxGame.SuperMonkeyBall || game == GxGame.SuperMonkeyBallDX) && (SectionFlags & (uint)GcmfSectionFlags.EffectiveModel) != 0) * { * // SMB doesn't have have any 0x08 section flags, so it's unknown how this field may work in that case * if (offsetPartType8Unknown1 != 0) * throw new InvalidGmaFileException("Gcmf [PreSectionHdr] offsetPartType8Unknown1 is not zero on SMB."); * } * else * {*/ if (Convert.ToInt32(input.BaseStream.Position) != sectionBaseOffset + offsetPartType8Unknown1) { throw new InvalidGmaFileException("Gcmf [PreSectionHdr] offsetPartType8Unknown1 doesn't match expected value."); } //} if ((SectionFlags & (uint)GcmfSectionFlags.SkinModel) != 0) { for (int i = 0; i < (offsetPartType8Unknown2 - offsetPartType8Unknown1) / 0x20; i++) { GcmfType8Unknown1 unk1 = new GcmfType8Unknown1(); unk1.Load(input); Type8Unknown1.Add(unk1); } } // Just because it probably won't be there doesn't mean we shouldn't allow it // and some stages have the things that are not allowed /*if ((game == GxGame.SuperMonkeyBall || game == GxGame.SuperMonkeyBallDX) && (SectionFlags & (uint)GcmfSectionFlags.EffectiveModel) != 0) * { * // SMB doesn't have have any 0x08 section flags, so it's unknown how this field may work in that case * if (offsetPartType8Unknown2 != 0) * throw new InvalidGmaFileException("Gcmf [PreSectionHdr] offsetPartType8Unknown2 is not zero on SMB."); * } * else * {*/ if (Convert.ToInt32(input.BaseStream.Position) != sectionBaseOffset + offsetPartType8Unknown2) { throw new InvalidGmaFileException("Gcmf [PreSectionHdr] offsetPartType8Unknown2 doesn't match expected value."); } //} if ((SectionFlags & (uint)GcmfSectionFlags.SkinModel) != 0) { // TODO figure out a better way to calculate this int numEntriesPart3 = Type8Unknown1.Max(u1 => u1.unk18) + 1; for (int i = 0; i < numEntriesPart3; i++) { Type8Unknown2.Add(input.ReadUInt16()); } if (PaddingUtils.Align(Convert.ToInt32(input.BaseStream.Position), 0x20) != sectionBaseOffset + offsetPartMeshData) { throw new InvalidGmaFileException("Gcmf [PreSectionHdr] offsetPart4 doesn't match expected value."); } input.BaseStream.Position = sectionBaseOffset + offsetPartMeshData; } if (Convert.ToInt32(input.BaseStream.Position) != sectionBaseOffset + offsetPartMeshData) { throw new InvalidGmaFileException("Gcmf [PreSectionHdr] offsetPartMeshData doesn't match expected value."); } // Load the mesh data itself (the indexed triangle strips) for (int i = 0; i < numLayer1Meshes + numLayer2Meshes; i++) { Meshes[i].LoadIndexedData(input, VertexPool, meshHeaderSectionInfos[i]); } // Make sure that all the vertices in the vertex pool got their // vertex flags set when we read the indexed triangle strips, // that is, that they were referenced at least once. // Otherwise, we would have semi-initialized vertices in the vertex pool! if (VertexPool.Any(vtx => !vtx.IsIndexedVertexInitialized())) { throw new InvalidGmaFileException("Not all vertex flags of all vertices in the vertex pool got initialized!"); } } else { for (int i = 0; i < numLayer1Meshes + numLayer2Meshes; i++) { GcmfMesh mesh = new GcmfMesh(); GcmfMesh.HeaderSectionInfo headerSectionInfo = mesh.LoadHeader(input, (i < numLayer1Meshes) ? GcmfMesh.MeshLayer.Layer1 : GcmfMesh.MeshLayer.Layer2); mesh.LoadNonIndexedData(input, headerSectionInfo, (SectionFlags & (uint)GcmfSectionFlags._16Bit) != 0); Meshes.Add(mesh); } } }