//TODO //public Boolean HasTangents; //TODO: //Fix missed vertexes on some meshes. (on lod's & sometimes the main model) public VVDFile(Stream FileInput, MDLFile mdl) { using (uReader FileStream = new uReader(FileInput)) { FileStream.ReadTypeFixed(ref VVD_Header, 64); if (VVD_Header.checksum != mdl.MDL_Header.checksum) { throw new FileLoadException(String.Format("{0}: Does not match the checksum in the .mdl", mdl.MDL_Header.Name)); } if (VVD_Header.numFixups > 0) { VVD_Fixups = new vertexFileFixup_t[VVD_Header.numFixups]; FileStream.ReadArrayFixed(ref VVD_Fixups, 12, VVD_Header.fixupTableStart); } //TODO //HasTangents = VVD_Header.tangentDataStart != 0; //"HasTagents" used to avoid non-zero length //Int64 TotalVerts = (HasTangents ? VVD_Header.tangentDataStart - VVD_Header.vertexDataStart : FileStream.InputStream.Length - VVD_Header.vertexDataStart) / 48; mstudiovertex_t[] tempVerts = new mstudiovertex_t[VVD_Header.numLODVertexes[0]]; FileStream.ReadArrayFixed(ref tempVerts, 48, VVD_Header.vertexDataStart); VVD_Vertexes = new mstudiovertex_t[VVD_Header.numLODs][]; List <mstudiovertex_t> TempVerts = new List <mstudiovertex_t>(); for (Int32 LODID = 0; LODID < VVD_Header.numLODs; ++LODID) { if (VVD_Header.numFixups == 0) { VVD_Vertexes[LODID] = tempVerts.Take(VVD_Header.numLODVertexes[LODID]).ToArray(); continue; } TempVerts.Clear(); for (Int32 FixupID = 0; FixupID < VVD_Fixups.Length; FixupID++) { if (VVD_Fixups[FixupID].lod >= LODID) { TempVerts.AddRange(tempVerts.Skip(VVD_Fixups[FixupID].sourceVertexID).Take(VVD_Fixups[FixupID].numVertexes)); } } VVD_Vertexes[LODID] = TempVerts.ToArray(); } } }
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 } }
//http://wiki.xentax.com/index.php/Source_VTF /// <summary> /// Parser VTF format /// <br>Supported versions: 7.1 - 7.5 (maybe 7.0)</br> /// </summary> /// <param name="stream">Stream of input file</param> /// <param name="FileName">Name of input file (optional)</param> public VTFFile(Stream stream, String FileName = "") { using (uReader FileStream = new uReader(stream)) { String TempHeader = FileStream.ReadFixedLengthString(Encoding.ASCII, 4); if (TempHeader != VTFHeader) { throw new Exception("Invalid VTF header. Expected '" + VTFHeader + "', got '" + TempHeader + "'."); } Header = new VTFHeader(); UInt32 VersionMajor = FileStream.ReadUInt32(); UInt32 VersionMinor = FileStream.ReadUInt32(); Decimal Version = VersionMajor + (VersionMinor / 10m); // e.g. 7.3 Header.Version = Version; UInt32 headerSize = FileStream.ReadUInt32(); Width = FileStream.ReadUInt16(); Height = FileStream.ReadUInt16(); Header.Flags = (VTFImageFlag)FileStream.ReadUInt32(); UInt16 NumFrames = FileStream.ReadUInt16(); UInt16 FirstFrame = FileStream.ReadUInt16(); FileStream.ReadBytes(4); // padding Header.Reflectivity = FileStream.ReadVector3D(false); FileStream.ReadBytes(4); // padding Header.BumpmapScale = FileStream.ReadSingle(); VTFImageFormat HighResImageFormat = (VTFImageFormat)FileStream.ReadUInt32(); Byte MipmapCount = FileStream.ReadByte(); VTFImageFormat LowResImageFormat = (VTFImageFormat)FileStream.ReadUInt32(); Byte LowResWidth = FileStream.ReadByte(); Byte LowResHeight = FileStream.ReadByte(); UInt16 Depth = 1; UInt32 NumResources = 0; if (Version >= 7.2m) { Depth = FileStream.ReadUInt16(); } if (Version >= 7.3m) { FileStream.ReadBytes(3); NumResources = FileStream.ReadUInt32(); FileStream.ReadBytes(8); } Int32 NumFaces = 1; if (Header.Flags.HasFlag(VTFImageFlag.TEXTUREFLAGS_ENVMAP)) { NumFaces = Version < 7.5m && FirstFrame != 0xFFFF ? 7 : 6; } VTFImageFormatInfo HighResFormatInfo = VTFImageFormatInfo.FromFormat(HighResImageFormat); VTFImageFormatInfo LowResFormatInfo = VTFImageFormatInfo.FromFormat(LowResImageFormat); Int32 ThumbnailSize = LowResImageFormat == VTFImageFormat.IMAGE_FORMAT_NONE ? 0 : LowResFormatInfo.GetSize(LowResWidth, LowResHeight); UInt32 ThumbnailOffset = headerSize; Int64 DataOffset = headerSize + ThumbnailSize; Resources = new VTFResource[NumResources]; for (Int32 i = 0; i < NumResources; i++) { VTFResourceType type = (VTFResourceType)FileStream.ReadUInt32(); UInt32 DataSize = FileStream.ReadUInt32(); switch (type) { case VTFResourceType.LowResImage: // Low res image ThumbnailOffset = DataSize; break; case VTFResourceType.Image: // Regular image DataOffset = DataSize; break; case VTFResourceType.Sheet: case VTFResourceType.CRC: case VTFResourceType.TextureLodSettings: case VTFResourceType.TextureSettingsEx: case VTFResourceType.KeyValueData: // todo Resources[i] = new VTFResource { Type = type, Data = DataSize }; break; default: throw new ArgumentOutOfRangeException(nameof(type), (uint)type, "Unknown resource type"); } } if (LowResImageFormat != VTFImageFormat.IMAGE_FORMAT_NONE) { FileStream.BaseStream.Position = ThumbnailOffset; Int32 thumbSize = LowResFormatInfo.GetSize(LowResWidth, LowResHeight); LowResImage = new VTFImage { Format = LowResImageFormat, Width = LowResWidth, Height = LowResHeight, Data = FileStream.ReadBytes(thumbSize) }; } Boolean ConvertToBGRA32 = true; Boolean hasAlpha = true; switch (HighResImageFormat) { //Unity support this formats natively case VTFImageFormat.IMAGE_FORMAT_A8: case VTFImageFormat.IMAGE_FORMAT_ABGR8888: case VTFImageFormat.IMAGE_FORMAT_ARGB8888: case VTFImageFormat.IMAGE_FORMAT_BGRA4444: case VTFImageFormat.IMAGE_FORMAT_DXT1_ONEBITALPHA: case VTFImageFormat.IMAGE_FORMAT_DXT3: case VTFImageFormat.IMAGE_FORMAT_DXT5: case VTFImageFormat.IMAGE_FORMAT_RGBA8888: case VTFImageFormat.IMAGE_FORMAT_BGRA8888: case VTFImageFormat.IMAGE_FORMAT_BGRX8888: case VTFImageFormat.IMAGE_FORMAT_RGBA16161616F: case VTFImageFormat.IMAGE_FORMAT_RGBA16161616: ConvertToBGRA32 = false; break; case VTFImageFormat.IMAGE_FORMAT_BGR565: case VTFImageFormat.IMAGE_FORMAT_RGB565: case VTFImageFormat.IMAGE_FORMAT_DXT1: case VTFImageFormat.IMAGE_FORMAT_RGB888: hasAlpha = false; ConvertToBGRA32 = false; break; } FileStream.BaseStream.Position = DataOffset; Frames = new Texture2D[NumFrames, NumFaces]; List <Byte>[] FramesData = new List <Byte> [NumFrames]; for (Int32 MipLevel = MipmapCount - 1; MipLevel >= 0; MipLevel--) { for (Int32 FrameID = 0; FrameID < NumFrames; FrameID++) { if (FramesData[FrameID] == null) { FramesData[FrameID] = new List <Byte>(); } for (Int32 FaceID = 0; FaceID < NumFaces; FaceID++) { for (Int32 SliceID = 0; SliceID < Depth; SliceID++) { Int32 Wid = GetMipSize(Width, MipLevel); Int32 Hei = GetMipSize(Height, MipLevel); Int32 DataSize = HighResFormatInfo.GetSize(Wid, Hei); if (ConvertToBGRA32) { FramesData[FrameID].InsertRange(0, VTFImageFormatInfo.FromFormat(HighResImageFormat).ConvertToBgra32(FileStream.ReadBytes(DataSize), Wid, Hei)); } else { FramesData[FrameID].InsertRange(0, FileStream.ReadBytes(DataSize)); } } } } } TextureFormat InternalFormat = TextureFormat.BGRA32; Boolean needCompress = false; switch (HighResImageFormat) { case VTFImageFormat.IMAGE_FORMAT_A8: InternalFormat = TextureFormat.Alpha8; break; case VTFImageFormat.IMAGE_FORMAT_ABGR8888: case VTFImageFormat.IMAGE_FORMAT_ARGB8888: InternalFormat = TextureFormat.ARGB32; break; case VTFImageFormat.IMAGE_FORMAT_BGR565: case VTFImageFormat.IMAGE_FORMAT_RGB565: InternalFormat = TextureFormat.RGB565; break; case VTFImageFormat.IMAGE_FORMAT_BGRA4444: InternalFormat = TextureFormat.RGBA4444; break; case VTFImageFormat.IMAGE_FORMAT_DXT1: case VTFImageFormat.IMAGE_FORMAT_DXT1_ONEBITALPHA: InternalFormat = TextureFormat.DXT1; break; case VTFImageFormat.IMAGE_FORMAT_DXT3: case VTFImageFormat.IMAGE_FORMAT_DXT5: InternalFormat = TextureFormat.DXT5; break; case VTFImageFormat.IMAGE_FORMAT_RGB888: InternalFormat = TextureFormat.RGB24; break; case VTFImageFormat.IMAGE_FORMAT_RGBA8888: InternalFormat = TextureFormat.RGBA32; break; case VTFImageFormat.IMAGE_FORMAT_RGBA16161616F: case VTFImageFormat.IMAGE_FORMAT_RGBA16161616: InternalFormat = TextureFormat.RGBAHalf; break; default: //needCompress = true; break; } Boolean mipmaps = MipmapCount > 1; for (Int32 FrameID = 0; FrameID < NumFrames; FrameID++) { for (Int32 FaceID = 0; FaceID < NumFaces; FaceID++) { Frames[FrameID, FaceID] = new Texture2D(Width, Height, InternalFormat, mipmaps); Frames[FrameID, FaceID].name = FileName; Frames[FrameID, FaceID].alphaIsTransparency = hasAlpha; Frames[FrameID, FaceID].LoadRawTextureData(FramesData[FrameID].ToArray()); Frames[FrameID, FaceID].Apply(); if (needCompress) { Frames[FrameID, FaceID].Compress(false); Debug.LogWarning(FileName + " compressed!"); } } } } }
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 } }
public void ReadData(uReader br) { var delta = (Flags & STUDIO_ANIM_DELTA) > 0; if ((Flags & STUDIO_ANIM_ANIMROT) > 0) { // Why is this so painful :( // Read the per-frame data using RLE, just like GoldSource models var startPos = br.BaseStream.Position; var offsets = br.ReadShortArray(3); var endPos = br.BaseStream.Position; var rotFrames = new List <float[]>(); for (var i = 0; i < NumFrames; i++) { rotFrames.Add(new float[] { 0, 0, 0 }); } for (var i = 0; i < 3; i++) { if (offsets[i] == 0) { continue; } br.BaseStream.Position = startPos + offsets[i]; var values = br.ReadAnimationFrameValues(NumFrames); for (var f = 0; f < values.Length; f++) { rotFrames[f][i] = +values[f]; if (f > 0 && delta) { rotFrames[f][i] += values[f - 1]; } } } FrameAngles.AddRange(rotFrames.Select(x => new Vector3(x[0], x[1], x[2]))); br.BaseStream.Position = endPos; } if ((Flags & STUDIO_ANIM_ANIMPOS) > 0) { // Same as above, except for the position coordinate var startPos = br.BaseStream.Position; var offsets = br.ReadShortArray(3); var endPos = br.BaseStream.Position; var posFrames = new List <float[]>(); for (var i = 0; i < NumFrames; i++) { posFrames.Add(new float[] { 0, 0, 0 }); } for (var i = 0; i < 3; i++) { if (offsets[i] == 0) { continue; } br.BaseStream.Position = startPos + offsets[i]; var values = br.ReadAnimationFrameValues(NumFrames); for (var f = 0; f < values.Length; f++) { posFrames[f][i] = +values[f]; if (f > 0 && delta) { posFrames[f][i] += values[f - 1]; } } } FramePositions.AddRange(posFrames.Select(x => new Vector3(x[0], x[1], x[2]))); br.BaseStream.Position = endPos; } if ((Flags & STUDIO_ANIM_RAWROT) > 0) { var quat48 = new Quaternion48(); br.ReadTypeFixed(ref quat48, 6); this.pQuat48 = quat48.quaternion; } if ((Flags & STUDIO_ANIM_RAWROT2) > 0) { var quat64 = new Quaternion64(); br.ReadTypeFixed(ref quat64, 8); this.pQuat64 = quat64.quaternion; } if ((Flags & STUDIO_ANIM_RAWPOS) > 0) { var vec48 = new Vector48(); br.ReadTypeFixed(ref vec48, 6); this.pVec48 = vec48.ToVector3(); } }
public PhysModel(Int32 modelIndex, Int32 solidCount, Byte[] collisionData, Byte[] keyData) { ModelIndex = modelIndex; KeyData = System.Text.Encoding.ASCII.GetString(keyData); using (var ms = new MemoryStream(collisionData)) { using (var br = new uReader(ms)) { for (Int32 i = 0; i < solidCount; i++) { var solid = new PhysModelSolid(); Solids.Add(solid); var size = br.ReadInt32(); var maxPos = br.BaseStream.Position + size; solid.vphysicsID = br.ReadInt16(); // ?? solid.version = br.ReadInt16(); br.ReadInt16(); solid.modelType = br.ReadInt16(); if (solid.modelType != 0x0) { br.BaseStream.Seek(maxPos - br.BaseStream.Position, SeekOrigin.Current); continue; } // ??? br.BaseStream.Seek(68, SeekOrigin.Current); while (true) { var cc = new PhysModelConvex(); solid.Convexes.Add(cc); var pos = br.BaseStream.Position; var vertexOffset = (Int32)(pos + br.ReadUInt32()); cc.BrushIndex = br.ReadInt32(); cc.idk2 = br.ReadByte(); cc.idk3 = br.ReadByte(); cc.idk4 = br.ReadUInt16(); var triCount = br.ReadInt16(); cc.idk5 = br.ReadUInt16(); for (Int32 j = 0; j < triCount; j++) { br.BaseStream.Seek(4, SeekOrigin.Current); var index1 = br.ReadInt16(); br.ReadInt16(); var index2 = br.ReadInt16(); br.ReadInt16(); var index3 = br.ReadInt16(); br.ReadInt16(); try { Vector3 v1 = collisionData.ReadAtPosition <Vector3>(vertexOffset + index1 * 16); Vector3 v2 = collisionData.ReadAtPosition <Vector3>(vertexOffset + index2 * 16); Vector3 v3 = collisionData.ReadAtPosition <Vector3>(vertexOffset + index3 * 16); cc.Triangles.Add(cc.Verts.Count); cc.Triangles.Add(cc.Verts.Count + 1); cc.Triangles.Add(cc.Verts.Count + 2); cc.Verts.Add(v1); cc.Verts.Add(v2); cc.Verts.Add(v3); } catch (System.Exception e) { Debug.Log("Error on solid type: " + solid.modelType); Debug.LogError(e); } } if (br.BaseStream.Position >= vertexOffset) { break; } } Int64 remainder = maxPos - br.BaseStream.Position; if (remainder > 0) { br.BaseStream.Seek(remainder, SeekOrigin.Current); } } } } KeyValues = KeyValues.Parse(KeyData); }