protected override SolidMeshVertex GetVertex(BinaryReader reader, SolidObjectMaterial material, int stride) { SolidMeshVertex vertex; switch (stride) { case 60: vertex = new SolidMeshVertex { Position = BinaryUtil.ReadVector3(reader), Normal = BinaryUtil.ReadVector3(reader), Color = reader.ReadUInt32(), TexCoords = BinaryUtil.ReadVector2(reader), BlendWeight = BinaryUtil.ReadVector3(reader), BlendIndices = BinaryUtil.ReadVector3(reader), }; break; // position (12 bytes) + normal (12 bytes) + color (4 bytes) + tex coords (8 bytes) case 36: vertex = new SolidMeshVertex { Position = BinaryUtil.ReadVector3(reader), Normal = BinaryUtil.ReadVector3(reader), Color = reader.ReadUInt32(), TexCoords = BinaryUtil.ReadVector2(reader) }; break; // position (12 bytes) + color (4 bytes) + tex coords (8 bytes) case 24: vertex = new SolidMeshVertex { Position = BinaryUtil.ReadVector3(reader), Color = reader.ReadUInt32(), TexCoords = BinaryUtil.ReadVector2(reader) }; break; default: throw new Exception($"Cannot handle vertex size: {stride}"); } return(vertex); }
protected override SolidMeshVertex GetVertex(BinaryReader reader, SolidObjectMaterial material, int stride) { MostWantedMaterial mwm = (MostWantedMaterial)material; SolidMeshVertex vertex = new SolidMeshVertex(); InternalEffectID id = (InternalEffectID)mwm.EffectId; switch (id) { case InternalEffectID.WorldNormalMap: case InternalEffectID.WorldReflectShader: vertex.Position = BinaryUtil.ReadVector3(reader); vertex.Normal = BinaryUtil.ReadVector3(reader); vertex.Color = reader.ReadUInt32(); vertex.TexCoords = BinaryUtil.ReadVector2(reader); reader.BaseStream.Position += 8; // TODO: What's this additional D3DDECLUSAGE_TEXCOORD element? vertex.Tangent = BinaryUtil.ReadVector3(reader); reader.BaseStream.Position += 4; // skip W component of tangent vector break; case InternalEffectID.skyshader: vertex.Position = BinaryUtil.ReadVector3(reader); vertex.Normal = BinaryUtil.ReadVector3(reader); vertex.Color = reader.ReadUInt32(); vertex.TexCoords = BinaryUtil.ReadVector2(reader); reader.BaseStream.Position += 8; // TODO: What's this additional D3DDECLUSAGE_TEXCOORD element? break; case InternalEffectID.WorldShader: case InternalEffectID.GlossyWindow: case InternalEffectID.billboardshader: case InternalEffectID.CarShader: vertex.Position = BinaryUtil.ReadVector3(reader); vertex.Normal = BinaryUtil.ReadVector3(reader); vertex.Color = reader.ReadUInt32(); vertex.TexCoords = BinaryUtil.ReadVector2(reader); break; default: throw new Exception($"Unsupported effect in object {Name}: {id}"); } return(vertex); }
/// <summary> /// Process the model data. /// </summary> public virtual void PostProcessing() { var vertexBuffersCount = VertexBuffers.Count; // Bookkeeping array: how many vertices are in each vertex buffer? var vbCounts = new uint[vertexBuffersCount]; // Bookkeeping array: how many vertices have been consumed from each vertex buffer? var vbOffsets = new uint[vertexBuffersCount]; // Vertex buffer data readers var vbReaders = new BinaryReader[vertexBuffersCount]; // Vertex arrays var vbArrays = new SolidMeshVertex[vertexBuffersCount][]; // Set up vertex buffer readers for (var i = 0; i < vbReaders.Length; i++) { vbReaders[i] = new BinaryReader(new MemoryStream(VertexBuffers[i])); } // Filling in vbCounts... if (vertexBuffersCount == 1) { // The mesh descriptor tells us how many vertices exist. // Since we only have one vertex buffer, we can use the info // from the mesh descriptor instead of doing the material loop // seen after this block. vbCounts[0] = MeshDescriptor.NumVerts; } else if (vertexBuffersCount > 1) { // Fill in vbCounts by examining every material. // We need to make sure at least one of our materials // actually has a NumVerts > 0, otherwise weird things // will probably happen. Debug.Assert(Materials.Any(m => m.NumVerts > 0)); foreach (var t in Materials) { vbCounts[t.VertexStreamIndex] += t.NumVerts; } } else { // If we have no vertex buffers, we can bail out. return; } // Verifying the integrity of our data... for (var i = 0; i < vbReaders.Length; i++) { // To avoid a division-by-zero exception, we allow a vertex buffer to have // EITHER 0 vertices OR exactly enough data for a given number of vertices. Debug.Assert(vbCounts[i] == 0 || VertexBuffers[i].Length % vbCounts[i] == 0); vbArrays[i] = new SolidMeshVertex[vbCounts[i]]; } // Reading vertices from buffers... foreach (var solidObjectMaterial in Materials) { var vbIndex = solidObjectMaterial.VertexStreamIndex; var vbStream = vbReaders[vbIndex]; var vbVertexCount = vbCounts[vbIndex]; var vbOffset = vbOffsets[vbIndex]; var vbStride = (int)(vbStream.BaseStream.Length / vbVertexCount); var numVerts = solidObjectMaterial.NumVerts == 0 ? vbVertexCount : solidObjectMaterial.NumVerts; if (vbOffset < vbVertexCount) { var vbStartPos = vbStream.BaseStream.Position; for (var i = 0; i < numVerts; i++) { Debug.Assert(vbStream.BaseStream.Position - vbStartPos == vbStride * i, "vbStream.BaseStream.Position - vbStartPos == vbStride * i"); vbArrays[vbIndex][vbOffset + i] = GetVertex(vbStream, solidObjectMaterial, vbStride); } vbOffsets[vbIndex] += numVerts; } else if (vbOffset != vbVertexCount) { throw new Exception($"Vertex buffer read is in a weird state. vbOffset={vbOffset} vbVertexCount={vbVertexCount}"); } } for (var i = 0; i < vbArrays.Length; i++) { ProcessVertices(ref vbArrays[i], i); } // Loading vertices into materials... foreach (var solidObjectMaterial in Materials) { var vertexStreamIndex = solidObjectMaterial.VertexStreamIndex; if (solidObjectMaterial.Indices.Any()) { var meshVertices = vbArrays[vertexStreamIndex]; var maxReferencedVertex = solidObjectMaterial.Indices.Max(); solidObjectMaterial.Vertices = new SolidMeshVertex[maxReferencedVertex + 1]; for (var j = 0; j <= maxReferencedVertex; j++) { solidObjectMaterial.Vertices[j] = meshVertices[j]; } // Validate material indices Debug.Assert(solidObjectMaterial.Indices.All(t => t < solidObjectMaterial.Vertices.Length)); } else { solidObjectMaterial.Vertices = Array.Empty <SolidMeshVertex>(); } } // Clean up vertex buffers, which are no longer needed VertexBuffers.Clear(); }
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); }
/// <inheritdoc /> /// <summary> /// This thing is responsible for somehow deriving proper vertex data from /// a NFS:Undercover vertex buffer. Getting the coordinates is easy, but /// the hard part is getting the texture coordinates. Can it be done? Who knnows? /// </summary> /// <remarks>And I didn't even mention the fact that there are wayyyy too many edge cases...</remarks> /// <remarks>I hate this game. Worse than E.T.</remarks> /// <param name="reader"></param> /// <param name="material"></param> /// <param name="stride"></param> /// <returns></returns> protected override SolidMeshVertex GetVertex(BinaryReader reader, SolidObjectMaterial material, int stride) { var effectId = ((UndercoverMaterial)material).EffectId; SolidMeshVertex vertex = new SolidMeshVertex(); switch (effectId) { case EffectID.mw2_constant: case EffectID.mw2_constant_alpha_bias: case EffectID.mw2_illuminated: case EffectID.mw2_pano: vertex.Position = BinaryUtil.ReadVector3(reader); reader.ReadSingle(); vertex.TexCoords = BinaryUtil.ReadShort2N(reader) * 32; break; case EffectID.mw2_matte: case EffectID.mw2_diffuse_spec: case EffectID.mw2_branches: case EffectID.mw2_glass_no_n: case EffectID.mw2_diffuse_spec_illum: case EffectID.diffuse_spec_2sided: case EffectID.mw2_combo_refl: case EffectID.mw2_dirt: case EffectID.mw2_grass: case EffectID.mw2_tunnel_illum: case EffectID.mw2_diffuse_spec_alpha: case EffectID.mw2_trunk: vertex.Position = BinaryUtil.ReadVector3(reader); reader.ReadSingle(); vertex.Color = reader.ReadUInt32(); vertex.TexCoords = BinaryUtil.ReadShort2N(reader) * 32; vertex.Normal = BinaryUtil.ReadNormal(reader, true); break; case EffectID.mw2_normalmap: case EffectID.mw2_normalmap_bias: case EffectID.mw2_glass_refl: case EffectID.normalmap2sided: case EffectID.mw2_road_overlay: case EffectID.mw2_road_refl_overlay: case EffectID.mw2_rock: vertex.Position = BinaryUtil.ReadVector3(reader); reader.ReadSingle(); vertex.Color = reader.ReadUInt32(); vertex.TexCoords = BinaryUtil.ReadShort2N(reader) * 32; vertex.Normal = BinaryUtil.ReadNormal(reader, true); // todo: read packed tangent vector reader.BaseStream.Position += 0x8; break; case EffectID.mw2_grass_rock: case EffectID.mw2_road_refl: case EffectID.mw2_road_refl_tile: case EffectID.mw2_road_tile: case EffectID.mw2_dirt_rock: vertex.Position = BinaryUtil.ReadVector3(reader); reader.ReadSingle(); vertex.Color = reader.ReadUInt32(); vertex.TexCoords = BinaryUtil.ReadShort2N(reader) * 32; // TODO: TEXCOORD1??? what do we do with this? reader.ReadInt16(); reader.ReadInt16(); vertex.Normal = BinaryUtil.ReadNormal(reader, true); // todo: read packed tangent vector reader.BaseStream.Position += 0x8; break; case EffectID.mw2_tunnel_road: vertex.Position = BinaryUtil.ReadVector3(reader); reader.ReadSingle(); vertex.Color = reader.ReadUInt32(); vertex.TexCoords = BinaryUtil.ReadShort2N(reader) * 32; // TODO: TEXCOORD1??? what do we do with this? reader.ReadInt16(); reader.ReadInt16(); // TODO: TEXCOORD2??? what do we do with this? reader.ReadInt16(); reader.ReadInt16(); vertex.Normal = BinaryUtil.ReadNormal(reader, true); // todo: read packed tangent vector reader.BaseStream.Position += 0x8; break; case EffectID.mw2_road_refl_lite: case EffectID.mw2_road_lite: case EffectID.mw2_grass_dirt: vertex.Position = BinaryUtil.ReadVector3(reader); reader.ReadSingle(); vertex.Color = reader.ReadUInt32(); vertex.TexCoords = BinaryUtil.ReadShort2N(reader) * 32; // TODO: TEXCOORD1??? what do we do with this? reader.ReadInt16(); reader.ReadInt16(); vertex.Normal = BinaryUtil.ReadNormal(reader, true); break; case EffectID.mw2_tunnel_wall: vertex.Position = BinaryUtil.ReadVector3(reader); reader.ReadSingle(); vertex.Color = reader.ReadUInt32(); vertex.TexCoords = BinaryUtil.ReadShort2N(reader) * 32; // TODO: TEXCOORD1??? what do we do with this? reader.ReadInt16(); reader.ReadInt16(); break; case EffectID.mw2_matte_alpha: case EffectID.mw2_dif_spec_a_bias: case EffectID.mw2_dirt_overlay: vertex.Position = BinaryUtil.ReadVector3(reader); reader.ReadSingle(); vertex.Color = reader.ReadUInt32(); vertex.TexCoords = BinaryUtil.ReadShort2N(reader) * 32; vertex.Normal = BinaryUtil.ReadNormal(reader, true); break; case EffectID.mw2_foliage: case EffectID.mw2_foliage_lod: case EffectID.mw2_scrub: case EffectID.mw2_scrub_lod: case EffectID.mw2_ocean: vertex.Position = BinaryUtil.ReadVector3(reader); reader.ReadSingle(); vertex.Color = reader.ReadUInt32(); vertex.TexCoords = BinaryUtil.ReadShort2N(reader) * 32; break; case EffectID.mw2_sky: vertex.Position = BinaryUtil.ReadVector3(reader); reader.ReadSingle(); vertex.TexCoords = BinaryUtil.ReadVector2(reader); break; case EffectID.mw2_texture_scroll: case EffectID.mw2_car_heaven_default: vertex.Position = BinaryUtil.ReadVector3(reader); reader.ReadSingle(); // TODO: COLOR0 is a float4. how do we deal with that? reader.BaseStream.Position += 0x10; vertex.TexCoords = BinaryUtil.ReadVector2(reader); break; case EffectID.mw2_car_heaven: vertex.Position = BinaryUtil.ReadVector3(reader); reader.ReadSingle(); // TODO: COLOR0 is a float4. how do we deal with that? reader.BaseStream.Position += 0x10; vertex.TexCoords = BinaryUtil.ReadVector2(reader); vertex.Normal = BinaryUtil.ReadNormal(reader, true); break; case EffectID.mw2_carhvn_floor: case EffectID.mw2_road: vertex.Position = BinaryUtil.ReadVector3(reader); reader.ReadSingle(); vertex.Color = reader.ReadUInt32(); vertex.TexCoords = BinaryUtil.ReadShort2N(reader) * 32; // TODO: TEXCOORD1??? what do we do with this? reader.ReadInt16(); reader.ReadInt16(); vertex.Normal = BinaryUtil.ReadNormal(reader, true); // todo: read packed tangent vector reader.BaseStream.Position += 0x8; break; case EffectID.car: case EffectID.car_a: case EffectID.car_a_nzw: case EffectID.car_nm: case EffectID.car_nm_a: case EffectID.car_nm_v_s: case EffectID.car_nm_v_s_a: case EffectID.car_si: case EffectID.car_si_a: case EffectID.car_t: case EffectID.car_t_a: case EffectID.car_t_nm: case EffectID.car_v: vertex.Position = BinaryUtil.ReadNormal(reader, true) * 10; vertex.TexCoords = BinaryUtil.ReadShort2N(reader) * 32; vertex.Color = reader.ReadUInt32(); vertex.Normal = BinaryUtil.ReadNormal(reader, true); vertex.Tangent = BinaryUtil.ReadNormal(reader, true); break; default: throw new Exception($"Unsupported effect: {effectId}"); } return(vertex); }
protected override SolidMeshVertex GetVertex(BinaryReader reader, SolidObjectMaterial material, int stride) { ProStreetMaterial psm = (ProStreetMaterial)material; SolidMeshVertex vertex = new SolidMeshVertex(); switch ((EffectID)psm.EffectId) { case EffectID.WORLDBAKEDLIGHTING: vertex.Position = BinaryUtil.ReadVector3(reader); vertex.Color = reader.ReadUInt32(); vertex.TexCoords = BinaryUtil.ReadVector2(reader); reader.BaseStream.Position += 16; vertex.Normal = BinaryUtil.ReadNormal(reader, true); break; case EffectID.WORLD: case EffectID.SKY: vertex.Position = BinaryUtil.ReadVector3(reader); vertex.Normal = BinaryUtil.ReadNormal(reader); vertex.Color = reader.ReadUInt32(); vertex.TexCoords = BinaryUtil.ReadVector2(reader); reader.BaseStream.Position += 8; // TODO: what is this second D3DDECLUSAGE_TEXCOORD element? break; case EffectID.WORLDNORMALMAP: vertex.Position = BinaryUtil.ReadVector3(reader); vertex.Normal = BinaryUtil.ReadNormal(reader); vertex.Color = reader.ReadUInt32(); vertex.TexCoords = BinaryUtil.ReadVector2(reader); reader.BaseStream.Position += 8; // TODO: what is this second D3DDECLUSAGE_TEXCOORD element? // TODO: read tangent reader.BaseStream.Position += 16; reader.BaseStream.Position += 8; // TODO: what are these other D3DDECLUSAGE_TEXCOORD elements? (2x D3DDECLTYPE_UBYTE4) break; case EffectID.WorldDepthShader: vertex.Position = BinaryUtil.ReadVector3(reader); vertex.Normal = BinaryUtil.ReadNormal(reader); vertex.Color = reader.ReadUInt32(); vertex.TexCoords = BinaryUtil.ReadVector2(reader); break; case EffectID.TREELEAVES: vertex.Position = BinaryUtil.ReadVector3(reader); vertex.Color = reader.ReadUInt32(); vertex.Normal = BinaryUtil.ReadNormal(reader); // TODO: COLLADA supports tangent vectors, so we should eventually read them //> elements[3]: type=D3DDECLTYPE_FLOAT4 usage=D3DDECLUSAGE_TANGENT size=16 offset=0x1c reader.BaseStream.Position += 0x10; vertex.TexCoords = BinaryUtil.ReadVector2(reader); break; case EffectID.WORLDCONSTANT: vertex.Position = BinaryUtil.ReadVector3(reader); vertex.TexCoords = BinaryUtil.ReadVector2(reader); vertex.Color = reader.ReadUInt32(); break; case EffectID.ROAD: vertex.Position = BinaryUtil.ReadVector3(reader); vertex.TexCoords = BinaryUtil.ReadVector2(reader); reader.BaseStream.Position += 28; vertex.Color = reader.ReadUInt32(); break; case EffectID.TERRAIN: vertex.Position = BinaryUtil.ReadVector3(reader); vertex.TexCoords = BinaryUtil.ReadVector2(reader); reader.BaseStream.Position += 16; vertex.Normal = BinaryUtil.ReadNormal(reader, true); // TODO: read packed tangent vector (4 short-components) reader.BaseStream.Position += 8; vertex.Color = reader.ReadUInt32(); reader.BaseStream.Position += 8; // TODO: what are these other D3DDECLUSAGE_TEXCOORD elements? (2x D3DDECLTYPE_UBYTE4) break; case EffectID.GRASSTERRAIN: vertex.Position = BinaryUtil.ReadVector3(reader); vertex.TexCoords = BinaryUtil.ReadVector2(reader); reader.BaseStream.Position += 20; vertex.Normal = BinaryUtil.ReadNormal(reader, true); // TODO: read packed tangent vector (4 short-components) reader.BaseStream.Position += 8; vertex.Color = reader.ReadUInt32(); break; case EffectID.FLAG: vertex.Position = BinaryUtil.ReadVector3(reader); vertex.Color = reader.ReadUInt32(); vertex.Normal = BinaryUtil.ReadNormal(reader, true); vertex.TexCoords = BinaryUtil.ReadVector2(reader); reader.BaseStream.Position += 8; // TODO: what is this second D3DDECLUSAGE_TEXCOORD element? break; case EffectID.GRASSCARD: vertex.Position = BinaryUtil.ReadVector3(reader); vertex.Color = reader.ReadUInt32(); vertex.Normal = BinaryUtil.ReadNormal(reader, true); // TODO: read packed tangent vector (4 short-components) reader.BaseStream.Position += 8; vertex.TexCoords = BinaryUtil.ReadVector2(reader); reader.BaseStream.Position += 8; // TODO: what is this second D3DDECLUSAGE_TEXCOORD element? break; case EffectID.CAR: case EffectID.CARNORMALMAP: case EffectID.CARVINYL: vertex.Position = BinaryUtil.ReadNormal(reader, true); vertex.TexCoords = BinaryUtil.ReadShort2N(reader); vertex.Color = reader.ReadUInt32(); vertex.Normal = BinaryUtil.ReadNormal(reader, true); vertex.Tangent = BinaryUtil.ReadNormal(reader, true); break; case EffectID.ALWAYSFACING: vertex.Position = BinaryUtil.ReadVector3(reader); vertex.Color = reader.ReadUInt32(); vertex.Normal = BinaryUtil.ReadNormal(reader, true); // TODO: read packed tangent vector (4 short-components) reader.BaseStream.Position += 8; vertex.TexCoords = BinaryUtil.ReadVector2(reader); break; case EffectID.STANDARD: vertex.Position = BinaryUtil.ReadVector3(reader); vertex.Color = reader.ReadUInt32(); vertex.TexCoords = BinaryUtil.ReadVector2(reader); reader.BaseStream.Position += 8; // todo: what's this? break; case EffectID.TUNNEL: vertex.Position = BinaryUtil.ReadVector3(reader); vertex.TexCoords = BinaryUtil.ReadVector2(reader); reader.BaseStream.Position += 8; // todo: what's this? reader.BaseStream.Position += 4; // todo: what's this? reader.BaseStream.Position += 4; // todo: what's this? vertex.Color = reader.ReadUInt32(); vertex.Normal = BinaryUtil.ReadNormal(reader, true); break; case EffectID.ROADLIGHTMAP: vertex.Position = BinaryUtil.ReadVector3(reader); vertex.TexCoords = BinaryUtil.ReadVector2(reader); reader.BaseStream.Position += 8; // todo: what's this? reader.BaseStream.Position += 8; // todo: what's this? reader.BaseStream.Position += 4; // todo: what's this? vertex.Normal = BinaryUtil.ReadNormal(reader, true); vertex.Color = reader.ReadUInt32(); break; case EffectID.WATER: vertex.Position = BinaryUtil.ReadVector3(reader); vertex.Color = reader.ReadUInt32(); vertex.TexCoords = BinaryUtil.ReadVector2(reader); break; case EffectID.WORLDBONE: vertex.Position = BinaryUtil.ReadVector3(reader); vertex.Normal = BinaryUtil.ReadNormal(reader); vertex.Color = reader.ReadUInt32(); vertex.TexCoords = BinaryUtil.ReadVector2(reader); vertex.BlendWeight = BinaryUtil.ReadVector3(reader); vertex.BlendIndices = BinaryUtil.ReadVector3(reader); vertex.Tangent = BinaryUtil.ReadVector3(reader); break; default: throw new Exception($"Unsupported effect in object {Name}: {psm.EffectId}"); } return(vertex); }