protected override SolidMeshVertex GetVertex(BinaryReader reader, SolidObjectMaterial material, int stride) { World15Material wm = (World15Material)material; SolidMeshVertex vertex = new SolidMeshVertex(); InternalEffectID id = (InternalEffectID)wm.EffectId; switch (id) { case InternalEffectID.WorldShader: case InternalEffectID.GLASS_REFLECT: case InternalEffectID.WorldZBiasShader: case InternalEffectID.Tree: case InternalEffectID.WATER: vertex.Position = BinaryUtil.ReadVector3(reader); vertex.Normal = BinaryUtil.ReadNormal(reader, true); vertex.TexCoords = BinaryUtil.ReadVector2(reader); vertex.Color = reader.ReadUInt32(); // daytime color reader.ReadUInt32(); // nighttime color break; case InternalEffectID.WorldPrelitShader: vertex.Position = BinaryUtil.ReadVector3(reader); vertex.Color = reader.ReadUInt32(); // daytime color reader.ReadUInt32(); // nighttime color vertex.TexCoords = BinaryUtil.ReadVector2(reader); break; case InternalEffectID.WorldZBiasPrelitShader: vertex.Position = BinaryUtil.ReadVector3(reader); vertex.Normal = BinaryUtil.ReadNormal(reader, true); vertex.Color = reader.ReadUInt32(); // daytime color reader.ReadUInt32(); // nighttime color vertex.TexCoords = BinaryUtil.ReadVector2(reader); break; case InternalEffectID.WorldNormalMap: case InternalEffectID.GLASS_REFLECTNM: case InternalEffectID.WorldRoadShader: case InternalEffectID.WorldFEShader: vertex.Position = BinaryUtil.ReadVector3(reader); vertex.Normal = BinaryUtil.ReadNormal(reader, true); vertex.TexCoords = BinaryUtil.ReadVector2(reader); vertex.Color = reader.ReadUInt32(); // daytime color reader.ReadUInt32(); // nighttime color vertex.Tangent = BinaryUtil.ReadNormal(reader, true); break; case InternalEffectID.CarShader: case InternalEffectID.CARNORMALMAP: vertex.Position = BinaryUtil.ReadNormal(reader, true) * 8; vertex.TexCoords = new Vector2(reader.ReadInt16() / 4096f, reader.ReadInt16() / 4096f - 1); vertex.Color = reader.ReadUInt32(); vertex.Normal = BinaryUtil.ReadNormal(reader, true); vertex.Tangent = BinaryUtil.ReadNormal(reader, true); break; default: throw new Exception($"Unsupported effect in object {Name}: {id}"); } return(vertex); }
private World15Object ReadObject(BinaryReader br, long size, World15Object solidObject = null) { if (solidObject == null) { solidObject = new World15Object(); } var endPos = br.BaseStream.Position + size; while (br.BaseStream.Position < endPos) { var chunkId = br.ReadUInt32(); var chunkSize = br.ReadUInt32(); var chunkEndPos = br.BaseStream.Position + chunkSize; //Console.WriteLine($"@{chunkEndPos}: 0x{chunkId:X8} [{chunkSize}]"); if ((chunkId & 0x80000000) == 0x80000000) { solidObject = ReadObject(br, chunkSize, solidObject); } else { var padding = 0u; while (br.ReadUInt32() == 0x11111111) { padding += 4; } br.BaseStream.Position -= 4; chunkSize -= padding; switch (chunkId) { case SolidListObjHeadChunk: { _namedMaterials = 0; var header = BinaryUtil.ReadUnmanagedStruct <ObjectHeader>(br); var name = BinaryUtil.ReadNullTerminatedString(br); solidObject.Name = name; solidObject.Hash = header.ObjectHash; solidObject.MinPoint = header.BoundsMin; solidObject.MaxPoint = header.BoundsMax; solidObject.Transform = header.Transform; break; } case 0x134900: { var descriptor = BinaryUtil.ReadUnmanagedStruct <MeshDescriptor>(br); solidObject.MeshDescriptor = new SolidMeshDescriptor { Flags = descriptor.Flags, HasNormals = true, NumIndices = descriptor.NumTriIndex, NumMats = descriptor.MaterialShaderNum, NumVertexStreams = descriptor.NumVertexStreams, NumTris = descriptor.NumTriangles }; break; } case 0x00134b02: { Debug.Assert(chunkSize % solidObject.MeshDescriptor.NumMats == 0); var streamIndex = 0; var lastEffectId = 0u; for (var j = 0; j < solidObject.MeshDescriptor.NumMats; j++) { var shadingGroup = BinaryUtil.ReadUnmanagedStruct <Material>(br); if (j > 0 && shadingGroup.EffectId != lastEffectId) { streamIndex++; } var solidObjectMaterial = new World15Material { Flags = shadingGroup.Flags, NumIndices = shadingGroup.NumIndices == 0 ? shadingGroup.NumTris * 3 : shadingGroup.NumIndices, MinPoint = shadingGroup.BoundsMin, MaxPoint = shadingGroup.BoundsMax, NumVerts = shadingGroup.NumVerts, TextureHash = solidObject.TextureHashes[shadingGroup.DiffuseMapId], EffectId = shadingGroup.EffectId, VertexStreamIndex = streamIndex }; solidObject.Materials.Add(solidObjectMaterial); solidObject.MeshDescriptor.NumVerts += shadingGroup.NumVerts; lastEffectId = shadingGroup.EffectId; } break; } case 0x134012: { for (var j = 0; j < chunkSize / 8; j++) { solidObject.TextureHashes.Add(br.ReadUInt32()); br.BaseStream.Position += 4; } break; } case 0x134b01: { var vb = new byte[chunkSize]; var readSize = br.Read(vb, 0, vb.Length); Debug.Assert(readSize == chunkSize); solidObject.VertexBuffers.Add(vb); break; } case 0x134b03: { foreach (var material in solidObject.Materials) { material.Indices = new ushort[material.NumIndices]; for (var j = 0; j < material.NumIndices; j++) { material.Indices[j] = br.ReadUInt16(); } } break; } case 0x00134c02: { if (chunkSize > 0) { solidObject.Materials[_namedMaterials++].Name = BinaryUtil.ReadNullTerminatedString(br); } break; } case 0x134c06: { Debug.Assert(chunkSize % 0x40 == 0); for (int i = 0; i < chunkSize / 0x40; i++) { solidObject.MorphMatrices.Add(BinaryUtil.ReadUnmanagedStruct <Matrix4x4>(br)); } break; } case 0x134c05: { Debug.Assert(chunkSize % 0x10 == 0); var morphList = new List <World15Object.MorphInfo>(); for (int i = 0; i < chunkSize / 0x10; i++) { morphList.Add(new World15Object.MorphInfo { VertexStartIndex = br.ReadInt32(), VertexEndIndex = br.ReadInt32(), MorphMatrixIndex = br.ReadInt32() }); br.ReadInt32(); } solidObject.MorphLists.Add(morphList); break; } default: //Debug.WriteLine($"unhandled chunk in World15Solids: 0x{chunkId:X8} [{chunkSize}] @{br.BaseStream.Position:X}"); break; } } br.BaseStream.Position = chunkEndPos; } return(solidObject); }