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 MDLFile(Stream FileInput, Boolean parseAnims = false, Boolean parseHitboxes = false) { using (uReader FileStream = new uReader(FileInput)) { FileStream.ReadTypeFixed(ref MDL_Header, 392); if (MDL_Header.id != 0x54534449) { throw new FileLoadException("File signature does not match 'IDST'"); } //Bones #region Bones //Bones MDL_StudioBones = new mstudiobone_t[MDL_Header.bone_count]; MDL_BoneNames = new String[MDL_Header.bone_count]; for (Int32 BoneID = 0; BoneID < MDL_Header.bone_count; BoneID++) { Int32 boneOffset = MDL_Header.bone_offset + (216 * BoneID); FileStream.ReadTypeFixed(ref MDL_StudioBones[BoneID], 216, boneOffset); MDL_BoneNames[BoneID] = FileStream.ReadNullTerminatedString(boneOffset + MDL_StudioBones[BoneID].sznameindex); } //Bones #endregion #region Hitboxes if (parseHitboxes) { MDL_Hitboxsets = new mstudiohitboxset_t[MDL_Header.hitbox_count]; Hitboxes = new Hitbox[MDL_Header.hitbox_count][]; for (Int32 HitboxsetID = 0; HitboxsetID < MDL_Header.hitbox_count; HitboxsetID++) { Int32 HitboxsetOffset = MDL_Header.hitbox_offset + (12 * HitboxsetID); FileStream.ReadTypeFixed(ref MDL_Hitboxsets[HitboxsetID], 12, HitboxsetOffset); Hitboxes[HitboxsetID] = new Hitbox[MDL_Hitboxsets[HitboxsetID].numhitboxes]; for (Int32 HitboxID = 0; HitboxID < MDL_Hitboxsets[HitboxsetID].numhitboxes; HitboxID++) { Int32 HitboxOffset = HitboxsetOffset + (68 * HitboxID) + MDL_Hitboxsets[HitboxsetID].hitboxindex; Hitboxes[HitboxsetID][HitboxID].BBox = new mstudiobbox_t(); FileStream.ReadTypeFixed(ref Hitboxes[HitboxsetID][HitboxID].BBox, 68, HitboxOffset); } } } #endregion #region Animations if (parseAnims) { try { //Animations MDL_AniDescriptions = new mstudioanimdesc_t[MDL_Header.localanim_count]; Animations = new AniInfo[MDL_Header.localanim_count]; for (Int32 AnimID = 0; AnimID < MDL_Header.localanim_count; AnimID++) { Int32 AnimOffset = MDL_Header.localanim_offset + (100 * AnimID); FileStream.ReadTypeFixed(ref MDL_AniDescriptions[AnimID], 100, AnimOffset); mstudioanimdesc_t StudioAnim = MDL_AniDescriptions[AnimID]; String StudioAnimName = FileStream.ReadNullTerminatedString(AnimOffset + StudioAnim.sznameindex); Animations[AnimID] = new AniInfo { name = StudioAnimName, studioAnim = StudioAnim }; Animations[AnimID].AnimationBones = new List <AnimationBone>(); //mstudioanim_t FileStream.BaseStream.Position = AnimOffset; Int64 StartOffset = FileStream.BaseStream.Position; Int32 CurrentOffset = MDL_AniDescriptions[AnimID].animindex; Int16 NextOffset; do { FileStream.BaseStream.Position = StartOffset + CurrentOffset; Byte BoneIndex = FileStream.ReadByte(); Byte BoneFlag = FileStream.ReadByte(); NextOffset = FileStream.ReadInt16(); CurrentOffset += NextOffset; AnimationBone AnimatedBone = new AnimationBone(BoneIndex, BoneFlag, MDL_AniDescriptions[AnimID].numframes); AnimatedBone.ReadData(FileStream); Animations[AnimID].AnimationBones.Add(AnimatedBone); } while (NextOffset != 0); //mstudioanim_t List <AnimationBone> AnimationBones = Animations[AnimID].AnimationBones; Int32 NumBones = MDL_Header.bone_count; Int32 NumFrames = StudioAnim.numframes; //Used to avoid "Assertion failed" key count in Unity (if frames less than 2) Boolean FramesLess = NumFrames < 2; if (FramesLess) { NumFrames += 1; } Animations[AnimID].PosX = new Keyframe[NumFrames][]; Animations[AnimID].PosY = new Keyframe[NumFrames][]; Animations[AnimID].PosZ = new Keyframe[NumFrames][]; Animations[AnimID].RotX = new Keyframe[NumFrames][]; Animations[AnimID].RotY = new Keyframe[NumFrames][]; Animations[AnimID].RotZ = new Keyframe[NumFrames][]; Animations[AnimID].RotW = new Keyframe[NumFrames][]; for (Int32 FrameID = 0; FrameID < NumFrames; FrameID++) { Animations[AnimID].PosX[FrameID] = new Keyframe[NumBones]; Animations[AnimID].PosY[FrameID] = new Keyframe[NumBones]; Animations[AnimID].PosZ[FrameID] = new Keyframe[NumBones]; Animations[AnimID].RotX[FrameID] = new Keyframe[NumBones]; Animations[AnimID].RotY[FrameID] = new Keyframe[NumBones]; Animations[AnimID].RotZ[FrameID] = new Keyframe[NumBones]; Animations[AnimID].RotW[FrameID] = new Keyframe[NumBones]; } for (Int32 boneID = 0; boneID < NumBones; boneID++) { AnimationBone AnimBone = AnimationBones.FirstOrDefault(x => x.Bone == boneID); //frameIndex < 30 && studioAnimName == "@ak47_reload" for (Int32 frameID = 0; frameID < NumFrames; frameID++) { //get current animation time (length) by divide frame index on "fps" Single time = frameID / StudioAnim.fps; mstudiobone_t StudioBone = MDL_StudioBones[boneID]; //Transform bone = Bones[boneIndex]; Vector3 Position = StudioBone.pos; Vector3 Rotation = StudioBone.rot; //BINGO! All animations are corrected :p if (AnimBone != null) { if ((AnimBone.Flags & STUDIO_ANIM_RAWROT) > 0) { Rotation = MathLibrary.ToEulerAngles(AnimBone.pQuat48); } if ((AnimBone.Flags & STUDIO_ANIM_RAWROT2) > 0) { Rotation = MathLibrary.ToEulerAngles(AnimBone.pQuat64); } if ((AnimBone.Flags & STUDIO_ANIM_RAWPOS) > 0) { Position = AnimBone.pVec48; } if ((AnimBone.Flags & STUDIO_ANIM_ANIMROT) > 0) { Rotation += AnimBone.FrameAngles[(FramesLess && frameID != 0) ? frameID - 1 : frameID].Multiply(StudioBone.rotscale); } if ((AnimBone.Flags & STUDIO_ANIM_ANIMPOS) > 0) { Position += AnimBone.FramePositions[(FramesLess && frameID != 0) ? frameID - 1 : frameID].Multiply(StudioBone.posscale); } if ((AnimBone.Flags & STUDIO_ANIM_DELTA) > 0) { Position = Vector3.zero; Rotation = Vector3.zero; } } //Invert right-handed position to left-handed if (StudioBone.parent == -1) { Position = MathLibrary.SwapY(Position); } else { Position.x = -Position.x; } //Corrects global scale and convert radians to degrees Position *= uLoader.UnitScale; Rotation *= Mathf.Rad2Deg; Quaternion quat; //Fix up bone rotations from right-handed to left-handed if (StudioBone.parent == -1) { quat = Quaternion.Euler(-90, 180, -90) * MathLibrary.AngleQuaternion(Rotation); } else { quat = MathLibrary.AngleQuaternion(Rotation); } Animations[AnimID].PosX[frameID][boneID] = new Keyframe(time, Position.x); Animations[AnimID].PosY[frameID][boneID] = new Keyframe(time, Position.y); Animations[AnimID].PosZ[frameID][boneID] = new Keyframe(time, Position.z); Animations[AnimID].RotX[frameID][boneID] = new Keyframe(time, quat.x); Animations[AnimID].RotY[frameID][boneID] = new Keyframe(time, quat.y); Animations[AnimID].RotZ[frameID][boneID] = new Keyframe(time, quat.z); Animations[AnimID].RotW[frameID][boneID] = new Keyframe(time, quat.w); } } } //Animations //Sequences MDL_SeqDescriptions = new mstudioseqdesc_t[MDL_Header.localseq_count]; Sequences = new SeqInfo[MDL_Header.localseq_count]; for (Int32 seqID = 0; seqID < MDL_Header.localseq_count; seqID++) { Int32 sequenceOffset = MDL_Header.localseq_offset + (212 * seqID); FileStream.ReadTypeFixed(ref MDL_SeqDescriptions[seqID], 212, sequenceOffset); mstudioseqdesc_t Sequence = MDL_SeqDescriptions[seqID]; Sequences[seqID] = new SeqInfo { name = FileStream.ReadNullTerminatedString(sequenceOffset + Sequence.szlabelindex), seq = Sequence }; FileStream.BaseStream.Position = sequenceOffset + Sequence.animindexindex; var animID = FileStream.ReadShortArray(Sequence.groupsize[0] * Sequence.groupsize[1]); //Debug.LogWarning(animIndices[0]); // Just use the first animation for now Sequences[seqID].ani = Animations[animID[0]]; } //Sequences } catch (Exception ex) { Debug.LogError(String.Format("\"{0}\" Parse animation failed: {1}", MDL_Header.Name, ex)); } } #endregion #region Materials //Materials MDL_TexturesInfo = new mstudiotexture_t[MDL_Header.texture_count]; MDL_Textures = new String[MDL_Header.texture_count]; for (Int32 TexID = 0; TexID < MDL_Header.texture_count; TexID++) { Int32 TextureOffset = MDL_Header.texture_offset + (64 * TexID); FileStream.ReadTypeFixed(ref MDL_TexturesInfo[TexID], 64, TextureOffset); MDL_Textures[TexID] = FileStream.ReadNullTerminatedString(TextureOffset + MDL_TexturesInfo[TexID].sznameindex); } Int32[] TDirOffsets = new Int32[MDL_Header.texturedir_count]; MDL_TDirectories = new String[MDL_Header.texturedir_count]; for (Int32 DirID = 0; DirID < MDL_Header.texturedir_count; DirID++) { FileStream.ReadTypeFixed(ref TDirOffsets[DirID], 4, MDL_Header.texturedir_offset + (4 * DirID)); MDL_TDirectories[DirID] = FileStream.ReadNullTerminatedString(TDirOffsets[DirID]); } //Materials #endregion #region BodyParts //Bodyparts MDL_Bodyparts = new StudioBodyPart[MDL_Header.bodypart_count]; for (Int32 BodypartID = 0; BodypartID < MDL_Header.bodypart_count; BodypartID++) { mstudiobodyparts_t BodyPart = new mstudiobodyparts_t(); Int32 BodyPartOffset = MDL_Header.bodypart_offset + (16 * BodypartID); FileStream.ReadTypeFixed(ref BodyPart, 16, BodyPartOffset); if (BodyPart.sznameindex != 0) { MDL_Bodyparts[BodypartID].Name = FileStream.ReadNullTerminatedString(BodyPartOffset + BodyPart.sznameindex); } else { MDL_Bodyparts[BodypartID].Name = String.Empty; } MDL_Bodyparts[BodypartID].Models = new StudioModel[BodyPart.nummodels]; for (Int32 ModelID = 0; ModelID < BodyPart.nummodels; ModelID++) { mstudiomodel_t Model = new mstudiomodel_t(); Int64 ModelOffset = BodyPartOffset + (148 * ModelID) + BodyPart.modelindex; FileStream.ReadTypeFixed(ref Model, 148, ModelOffset); MDL_Bodyparts[BodypartID].Models[ModelID].isBlank = (Model.numvertices <= 0 || Model.nummeshes <= 0); MDL_Bodyparts[BodypartID].Models[ModelID].Model = Model; MDL_Bodyparts[BodypartID].Models[ModelID].Meshes = new mstudiomesh_t[Model.nummeshes]; for (Int32 MeshID = 0; MeshID < Model.nummeshes; MeshID++) { mstudiomesh_t Mesh = new mstudiomesh_t(); Int64 MeshOffset = ModelOffset + (116 * MeshID) + Model.meshindex; FileStream.ReadTypeFixed(ref Mesh, 116, MeshOffset); MDL_Bodyparts[BodypartID].Models[ModelID].Meshes[MeshID] = Mesh; } MDL_Bodyparts[BodypartID].Models[ModelID].IndicesPerLod = new Dictionary <Int32, List <Int32> > [8]; for (Int32 i = 0; i < 8; i++) { MDL_Bodyparts[BodypartID].Models[ModelID].IndicesPerLod[i] = new Dictionary <Int32, List <Int32> >(); } MDL_Bodyparts[BodypartID].Models[ModelID].VerticesPerLod = new mstudiovertex_t[8][]; } } //BodyParts #endregion } }
static void ParseVtxFile() { if (VTX_Header.checkSum != MDL_Header.checksum) { throw new FileLoadException(String.Format("{0}: Does not match the checksum in the .mdl", ModelObject.name + ".dx90.vtx")); } mstudiomodel_t pModel = MDL_Models[0]; mstudiomesh_t pStudioMesh; BoneWeight[] pBoneWeight = new BoneWeight[pModel.numvertices]; Vector3[] pVertices = new Vector3[pModel.numvertices]; Vector3[] pNormals = new Vector3[pModel.numvertices]; Vector2[] pUvBuffer = new Vector2[pModel.numvertices]; List <Material> pMaterials = new List <Material>(); ModelFileLoader.ReadType(ref vBodypart, VTX_Header.bodyPartOffset); Int32 ModelInputFilePosition = VTX_Header.bodyPartOffset + vBodypart.modelOffset; ModelFileLoader.ReadType(ref vModel, ModelInputFilePosition); Int32 ModelLODInputFilePosition = ModelInputFilePosition + vModel.lodOffset; ModelFileLoader.ReadType(ref vLod, ModelLODInputFilePosition); Int32 MeshInputFilePosition = ModelLODInputFilePosition + vLod.meshOffset; VTX_Meshes = new MeshHeader_t[vLod.numMeshes]; ModelFileLoader.ReadArray(ref VTX_Meshes, MeshInputFilePosition); for (Int32 i = 0; i < pModel.numvertices; i++) { pVertices[i] = MathUtils.SwapZY(VVD_Vertexes[pModel.vertexindex + i].m_vecPosition * ConfigLoader.WorldScale); pNormals[i] = MathUtils.SwapZY(VVD_Vertexes[pModel.vertexindex + i].m_vecNormal); pUvBuffer[i] = VVD_Vertexes[pModel.vertexindex + i].m_vecTexCoord; } Mesh pMesh = new Mesh(); //ModelObject.AddComponent<MeshCollider>().sharedMesh = pMesh; pMesh.subMeshCount = vLod.numMeshes; pMesh.vertices = pVertices; pMesh.normals = pNormals; pMesh.uv = pUvBuffer; if (MDL_Bones.Count > 1) { for (Int32 i = 0; i < pModel.numvertices; i++) { pBoneWeight[i] = GetBoneWeight(VVD_Vertexes[pModel.vertexindex + i].m_BoneWeights); } SkinnedMeshRenderer smr = ModelObject.AddComponent <SkinnedMeshRenderer>(); Matrix4x4[] bindPoses = new Matrix4x4[MDL_Bones.Count]; for (Int32 i = 0; i < bindPoses.Length; i++) { bindPoses[i] = MDL_Bones[i].worldToLocalMatrix * ModelObject.transform.localToWorldMatrix; } pMesh.boneWeights = pBoneWeight; pMesh.bindposes = bindPoses; smr.sharedMesh = pMesh; smr.bones = MDL_Bones.ToArray(); smr.updateWhenOffscreen = true; } else { MeshFilter MeshFilter = ModelObject.AddComponent <MeshFilter>(); ModelObject.AddComponent <MeshRenderer>(); MeshFilter.sharedMesh = pMesh; } for (Int32 i = 0; i < vLod.numMeshes; i++) { List <Int32> pIndices = new List <Int32>(); pStudioMesh = MDL_Meshes[i]; StripGroupHeader_t[] StripGroups = new StripGroupHeader_t[VTX_Meshes[i].numStripGroups]; Int32 StripGroupFilePosition = MeshInputFilePosition + (Marshal.SizeOf(typeof(MeshHeader_t)) * i) + VTX_Meshes[i].stripGroupHeaderOffset; ModelFileLoader.ReadArray(ref StripGroups, StripGroupFilePosition); for (Int32 j = 0; j < VTX_Meshes[i].numStripGroups; j++) { Vertex_t[] pVertexBuffer = new Vertex_t[StripGroups[j].numVerts]; ModelFileLoader.ReadArray(ref pVertexBuffer, StripGroupFilePosition + (Marshal.SizeOf(typeof(StripGroupHeader_t)) * j) + StripGroups[j].vertOffset); UInt16[] Indices = new UInt16[StripGroups[j].numIndices]; ModelFileLoader.ReadArray(ref Indices, StripGroupFilePosition + (Marshal.SizeOf(typeof(StripGroupHeader_t)) * j) + StripGroups[j].indexOffset); for (Int32 n = 0; n < Indices.Length; n++) { pIndices.Add(pVertexBuffer[Indices[n]].origMeshVertID + pStudioMesh.vertexoffset); } } pMesh.SetTriangles(pIndices.ToArray(), i); String MaterialPath = String.Empty; for (Int32 j = 0; j < MDL_TDirectories.Length; j++) { for (Int32 n = 0; n < ConfigLoader.ModFolders.Length; n++) { if (File.Exists(ConfigLoader.GamePath + "/" + ConfigLoader.ModFolders[n] + "/materials/" + MDL_TDirectories[j] + MDL_Textures[pStudioMesh.material] + ".vmt")) { MaterialPath = MDL_TDirectories[j] + MDL_Textures[pStudioMesh.material]; } } } pMaterials.Add(MaterialLoader.Load(MaterialPath)); } ; ModelObject.GetComponent <Renderer>().sharedMaterials = pMaterials.ToArray(); }