private static void ParseVtxFile() { List <BoneWeight> pBoneWeight = new List <BoneWeight>(); List <Vector3> pVertices = new List <Vector3>(); List <Vector3> pNormals = new List <Vector3>(); List <Vector2> pUvBuffer = new List <Vector2>(); // Load necessary information mstudiomodel_t pModel = MDL_Models[0]; mstudiomesh_t pStudioMesh; BodyPartHeader_t vBodypart = CRead.ReadType <BodyPartHeader_t>(VTX_Header.bodyPartOffset); int ModelInputFilePosition = VTX_Header.bodyPartOffset + vBodypart.modelOffset; ModelHeader_t vModel = CRead.ReadType <ModelHeader_t>(ModelInputFilePosition); int ModelLODInputFilePosition = ModelInputFilePosition + vModel.lodOffset; ModelLODHeader_t vLod = CRead.ReadType <ModelLODHeader_t>(ModelLODInputFilePosition); int MeshInputFilePosition = ModelLODInputFilePosition + vLod.meshOffset; VTX_Meshes.AddRange(CRead.ReadType <MeshHeader_t>(MeshInputFilePosition, vLod.numMeshes)); // Get bone weight's, vertices, normals, uv for (int i = 0; i < pModel.numvertices; i++) { pBoneWeight.Add(GetBoneWeight(VVD_Vertexes[pModel.vertexindex + i].m_BoneWeights)); pVertices.Add(WorldController.SwapZY(VVD_Vertexes[pModel.vertexindex + i].m_vecPosition * WorldController.WorldScale)); pNormals.Add(WorldController.SwapZY(VVD_Vertexes[pModel.vertexindex + i].m_vecNormal)); pUvBuffer.Add(VVD_Vertexes[pModel.vertexindex + i].m_vecTexCoord); } GameObject meshObject = new GameObject(new string(pModel.name)); meshObject.transform.parent = ModelObject.transform; SkinnedMeshRenderer smr = meshObject.AddComponent <SkinnedMeshRenderer>(); smr.materials = new Material[vLod.numMeshes]; // Calculate bindposes Matrix4x4[] bindPoses = new Matrix4x4[MDL_Bones.Count]; for (int i = 0; i < bindPoses.Length; i++) { MDL_Bones[i].localPosition = Vector3.zero; bindPoses[i] = MDL_Bones[i].worldToLocalMatrix * ModelObject.transform.localToWorldMatrix; } // Generate skin mesh smr.sharedMesh = new Mesh(); smr.sharedMesh.name = new string(pModel.name); smr.sharedMesh.subMeshCount = vLod.numMeshes; smr.sharedMesh.vertices = pVertices.ToArray(); smr.sharedMesh.normals = pNormals.ToArray(); smr.sharedMesh.uv = pUvBuffer.ToArray(); smr.sharedMesh.boneWeights = pBoneWeight.ToArray(); smr.sharedMesh.bindposes = bindPoses; smr.sharedMesh.Optimize(); smr.bones = MDL_Bones.ToArray(); smr.updateWhenOffscreen = true; for (int i = 0; i < vLod.numMeshes; i++) { List <int> pIndices = new List <int>(); List <StripGroupHeader_t> StripGroups = new List <StripGroupHeader_t>(); int StripGroupFilePosition = MeshInputFilePosition + (Marshal.SizeOf(typeof(MeshHeader_t)) * i) + VTX_Meshes[i].stripGroupHeaderOffset; StripGroups.AddRange(CRead.ReadType <StripGroupHeader_t>(StripGroupFilePosition, VTX_Meshes[i].numStripGroups)); pStudioMesh = MDL_Meshes[i]; // Get indices for model for (int l = 0; l < VTX_Meshes[i].numStripGroups; l++) { List <Vertex_t> pVertexBuffer = new List <Vertex_t>(); pVertexBuffer.AddRange(CRead.ReadType <Vertex_t>(StripGroupFilePosition + (Marshal.SizeOf(typeof(StripGroupHeader_t)) * l) + StripGroups[l].vertOffset, StripGroups[l].numVerts)); List <ushort> Indices = new List <ushort>(); Indices.AddRange(CRead.ReadType <ushort>(StripGroupFilePosition + (Marshal.SizeOf(typeof(StripGroupHeader_t)) * l) + StripGroups[l].indexOffset, StripGroups[l].numIndices)); for (int n = 0; n < Indices.Count; n++) { pIndices.Add(pVertexBuffer[Indices[n]].origMeshVertID + pStudioMesh.vertexoffset); } } smr.sharedMesh.SetTriangles(pIndices.ToArray(), i); smr.materials[i].name = MDL_Textures[pStudioMesh.material]; string pathToTex = null; Material material = null; // Find path to mesh material for (int l = 0; l < MDL_TDirectories.Count; l++) { if (File.Exists(WorldController.DefaultTexPath + MDL_TDirectories[l] + MDL_Textures[pStudioMesh.material] + ".vmt")) { pathToTex = WorldController.DefaultTexPath + MDL_TDirectories[l] + MDL_Textures[pStudioMesh.material] + ".vmt"; } } if (pathToTex != null) { material = ValveTextureLoader.LoadMaterial(pathToTex.Replace(WorldController.DefaultTexPath, "")); } // Apply loaded material to mesh if (material != null) { smr.materials[i].CopyPropertiesFromMaterial(material); smr.materials[i].shader = material.shader; } } }
public VTXFile(Stream FileInput, MDLFile StudioMDL, VVDFile StudioVVD) { using (uReader FileStream = new uReader(FileInput)) { studiohdr_t MDL_Header = StudioMDL.MDL_Header; FileStream.ReadTypeFixed(ref VTX_Header, 36); if (VTX_Header.checkSum != MDL_Header.checksum) { throw new FileLoadException(String.Format("{0}: Does not match the checksum in the .mdl", MDL_Header.Name)); } #region BodyParts Int32[] VertexLODOffsets = new Int32[8]; for (Int32 BodypartID = 0; BodypartID < MDL_Header.bodypart_count; BodypartID++) { BodyPartHeader_t BodyPart = new BodyPartHeader_t(); Int64 BodyPartOffset = VTX_Header.bodyPartOffset + (8 * BodypartID); FileStream.ReadTypeFixed(ref BodyPart, 8, BodyPartOffset); StudioBodyPart StudioBodyPart = StudioMDL.MDL_Bodyparts[BodypartID]; #region Models for (Int32 ModelID = 0; ModelID < BodyPart.numModels; ModelID++) { StudioModel StudioModel = StudioBodyPart.Models[ModelID]; if (StudioModel.isBlank) { Debug.Log(String.Format("Model ID - {0} in bodypart \"{1}\" is blank, skip", ModelID, StudioBodyPart.Name)); continue; } ModelHeader_t Model = new ModelHeader_t(); Int64 ModelOffset = BodyPartOffset + (8 * ModelID) + BodyPart.modelOffset; FileStream.ReadTypeFixed(ref Model, 8, ModelOffset); StudioBodyPart.Models[ModelID].NumLODs = Model.numLODs; StudioBodyPart.Models[ModelID].LODData = new ModelLODHeader_t[Model.numLODs]; #region LOD's //TODO: Strip unused vertexes on lower lod's ("first" lod is fine) for (Int32 LODID = 0; LODID < Model.numLODs; LODID++) { ModelLODHeader_t LOD = new ModelLODHeader_t(); Int64 LODOffset = ModelOffset + (12 * LODID) + Model.lodOffset; FileStream.ReadTypeFixed(ref LOD, 12, LODOffset); StudioBodyPart.Models[ModelID].LODData[LODID] = LOD; #region Mesh LOD //Temp remember verts count per lod model Int32 VertexOffset = 0; //List<mstudiovertex_t> VertexesPerLod = new List<mstudiovertex_t>(); for (Int32 MeshID = 0; MeshID < StudioModel.Model.nummeshes; MeshID++) { mstudiomesh_t StudioMesh = StudioBodyPart.Models[ModelID].Meshes[MeshID]; //TODO: StudioModel.Meshes[MeshID].VertexData.numlodvertices[LODID]; - we no longer need this?? VertexOffset += StudioMesh.numvertices; List <Int32> IndicesPerMesh = new List <Int32>(); MeshHeader_t Mesh = new MeshHeader_t(); Int64 MeshOffset = LODOffset + (9 * MeshID) + LOD.meshOffset; FileStream.ReadTypeFixed(ref Mesh, 9, MeshOffset); #region StripGroups for (Int32 StripGroupID = 0; StripGroupID < Mesh.numStripGroups; StripGroupID++) { StripGroupHeader_t StripGroup = new StripGroupHeader_t(); Int64 StripGroupOffset = MeshOffset + (25 * StripGroupID) + Mesh.stripGroupHeaderOffset; FileStream.ReadTypeFixed(ref StripGroup, 25, StripGroupOffset); Vertex_t[] Vertexes = new Vertex_t[StripGroup.numVerts]; FileStream.BaseStream.Position = StripGroupOffset + StripGroup.vertOffset; FileStream.ReadArrayFixed(ref Vertexes, 9); FileStream.BaseStream.Position = StripGroupOffset + StripGroup.indexOffset; Int16[] Indices = FileStream.ReadShortArray(StripGroup.numIndices); #region Strips for (Int32 StripID = 0; StripID < StripGroup.numStrips; StripID++) { StripHeader_t VTXStrip = new StripHeader_t(); Int64 VTXStripOffset = StripGroupOffset + (27 * StripID) + StripGroup.stripOffset; FileStream.ReadTypeFixed(ref VTXStrip, 27, VTXStripOffset); //TODO: //Strip / "Split" vertexes //Pseudo code: /*for (Int32 VertID = 0; VertID < maxVertsPerLod; VertID++) * { * Int32 Index = MeshID * VTXStrip.numVerts + VertID; * * if (Index < numStripVerts) * { * splitVerts.Add(verts[Index]); * splitIndices.Add(j); * } * }*/ //Hmmmmm... Well, it's looks what we want.... but still doesn't perfect (for lod's mesh) /*Int32 NumVerts = VTXStrip.indexOffset + VTXStrip.numVerts; * for (Int32 VertID = VTXStrip.indexOffset; VertID < NumVerts; VertID++) * { * Int32 Index0 = VertID + StudioMesh.vertexoffset + VertexLODOffsets[LODID]; * VertexesPerLod.Add(StudioVVD.tempVerts[Index0]); * }*/ if ((VTXStrip.flags & VTXStripGroupTriStripFlag) > 0) { for (Int32 TempIdx = VTXStrip.indexOffset; TempIdx < VTXStrip.indexOffset + VTXStrip.numIndices - 2; TempIdx++) { Int32[] add = TempIdx % 2 == 1 ? new[] { TempIdx + 1, TempIdx, TempIdx + 2 } : new[] { TempIdx, TempIdx + 1, TempIdx + 2 }; foreach (Int32 Index in add) { IndicesPerMesh.Add(Vertexes[Indices[Index]].origMeshVertId + StudioMesh.vertexoffset); } } } else { for (Int32 Index = VTXStrip.indexOffset; Index < VTXStrip.indexOffset + VTXStrip.numIndices; Index++) { IndicesPerMesh.Add(Vertexes[Indices[Index]].origMeshVertId + StudioMesh.vertexoffset); } } } #endregion } #endregion StudioMDL.SetIndices(BodypartID, ModelID, LODID, MeshID, IndicesPerMesh); } #endregion //StudioMDL.MDL_Bodyparts[BodypartID].Models[ModelID].VerticesPerLod[LODID] = VertexesPerLod.ToArray(); ///TODO: Strip unused vertexes in <seealso cref="VVDFile.VVD_Vertexes"/> per lod StudioMDL.SetVertices(BodypartID, ModelID, LODID, VertexOffset, VertexLODOffsets[LODID], StudioVVD.VVD_Vertexes[0]); VertexLODOffsets[LODID] += VertexOffset; } #endregion } #endregion } #endregion } }