private void ParseModels(Stream stream, long bodyPartPosition, mstudiobodyparts_t bodyPart) { if (bodyPart.modelCount >= 0) { long nextModelPosition = bodyPartPosition + bodyPart.modelOffset; bodyPart.models = new mstudiomodel_t[bodyPart.modelCount]; for (int i = 0; i < bodyPart.models.Length; i++) { stream.Position = nextModelPosition; long modelPosition = nextModelPosition; bodyPart.models[i] = new mstudiomodel_t(); bodyPart.models[i].name = new char[64]; for (int j = 0; j < bodyPart.models[i].name.Length; j++) { bodyPart.models[i].name[j] = DataParser.ReadChar(stream); } bodyPart.models[i].type = DataParser.ReadInt(stream); bodyPart.models[i].boundingRadius = DataParser.ReadFloat(stream); bodyPart.models[i].meshCount = DataParser.ReadInt(stream); bodyPart.models[i].meshOffset = DataParser.ReadInt(stream); bodyPart.models[i].vertexCount = DataParser.ReadInt(stream); bodyPart.models[i].vertexOffset = DataParser.ReadInt(stream); bodyPart.models[i].tangentOffset = DataParser.ReadInt(stream); bodyPart.models[i].attachmentCount = DataParser.ReadInt(stream); bodyPart.models[i].attachmentOffset = DataParser.ReadInt(stream); bodyPart.models[i].eyeballCount = DataParser.ReadInt(stream); bodyPart.models[i].eyeballOffset = DataParser.ReadInt(stream); bodyPart.models[i].vertexData = new mstudio_modelvertexdata_t(); bodyPart.models[i].vertexData.vertexDataP = DataParser.ReadInt(stream); bodyPart.models[i].vertexData.tangentDataP = DataParser.ReadInt(stream); bodyPart.models[i].unused = new int[8]; for (int j = 0; j < bodyPart.models[i].unused.Length; j++) { bodyPart.models[i].unused[j] = DataParser.ReadInt(stream); } nextModelPosition = stream.Position; ParseEyeballs(stream, modelPosition, bodyPart.models[i]); ParseMeshes(stream, modelPosition, bodyPart.models[i]); } } }
private void ParseVertices(Stream stream, int rootLod) { for (int i = 0; i < rootLod; i++) { header.numLODVertices[i] = header.numLODVertices[rootLod]; } //int lodIndex = 0; if (header.numLODs > 0) { Func <Stream, mstudiovertex_t> ReadVertexFromStream = (innerStream) => { mstudiovertex_t vertex = new mstudiovertex_t(); vertex.m_BoneWeights.weight = new float[MAX_NUM_BONES_PER_VERT]; //0 + 12 = 12 for (int k = 0; k < vertex.m_BoneWeights.weight.Length; k++) { vertex.m_BoneWeights.weight[k] = DataParser.ReadFloat(innerStream); } vertex.m_BoneWeights.bone = new char[MAX_NUM_BONES_PER_VERT]; //12 + 12 = 24 for (int k = 0; k < vertex.m_BoneWeights.bone.Length; k++) { vertex.m_BoneWeights.bone[k] = DataParser.ReadChar(innerStream); } vertex.m_BoneWeights.numbones = DataParser.ReadByte(innerStream); //24 + 1 = 25 float ex = DataParser.ReadFloat(innerStream); //25 + 4 = 29 float why = DataParser.ReadFloat(innerStream); //33 + 4 = 37 float zee = DataParser.ReadFloat(innerStream); //29 + 4 = 33 vertex.m_vecPosition = new Vector3(ex, why, zee); ex = DataParser.ReadFloat(innerStream); //37 + 4 = 41 why = DataParser.ReadFloat(innerStream); //45 + 4 = 49 zee = DataParser.ReadFloat(innerStream); //41 + 4 = 45 vertex.m_vecNormal = new Vector3(ex, why, zee); ex = DataParser.ReadFloat(innerStream); //49 + 4 = 53 why = DataParser.ReadFloat(innerStream); //53 + 4 = 57 vertex.m_vecTexCoord = new Vector2(ex, 1 - why); return(vertex); }; stream.Position = fileOffsetPosition + header.vertexDataStart; //int vertexCount = header.numLODVertices[0]; int vertexCount = 0; for (int i = 0; i < header.numLODVertices.Length; i++) { vertexCount += header.numLODVertices[i]; } vertices = new mstudiovertex_t[vertexCount]; for (int i = 0; i < vertices.Length; i++) { vertices[i] = ReadVertexFromStream(stream); } if (header.numFixups > 0) { vertexCount = 0; for (int fixupIndex = 0; fixupIndex < header.numFixups; fixupIndex++) { if (fileFixup[fixupIndex].lod < rootLod) { continue; } vertexCount += fileFixup[fixupIndex].numVertices; } mstudiovertex_t[] oldVertices = vertices; vertices = new mstudiovertex_t[vertexCount]; int currentIndex = 0; for (int fixupIndex = 0; fixupIndex < header.numFixups; fixupIndex++) { if (fileFixup[fixupIndex].lod < rootLod) { continue; } Array.Copy(oldVertices, fileFixup[fixupIndex].sourceVertexID, vertices, currentIndex, fileFixup[fixupIndex].numVertices); currentIndex += fileFixup[fixupIndex].numVertices; } } } else { Debug.LogError("VVDParser: Header's numLODs less than or equal to zero"); } }
private mstudioattachment_t[] ParseAttachments(Stream stream) { if (header1.attachment_count >= 0) { long nextAttachmentPosition = fileBeginOffset + header1.attachment_offset; attachments = new mstudioattachment_t[header1.attachment_count]; for (int i = 0; i < attachments.Length; i++) { stream.Position = nextAttachmentPosition; long attachmentPosition = nextAttachmentPosition; if (header1.version == 10) { attachments[i].builtName = new char[32]; for (int j = 0; j < attachments[i].builtName.Length; j++) { attachments[i].builtName[j] = DataParser.ReadChar(stream); } attachments[i].type = DataParser.ReadInt(stream); attachments[i].bone = DataParser.ReadInt(stream); attachments[i].attachmentPoint = new Vector3(DataParser.ReadFloat(stream), DataParser.ReadFloat(stream), DataParser.ReadFloat(stream)); attachments[i].vectors = new Vector3[3]; for (int j = 0; j < attachments[i].vectors.Length; j++) { attachments[i].vectors[j] = new Vector3(DataParser.ReadFloat(stream), DataParser.ReadFloat(stream), DataParser.ReadFloat(stream)); } } else { attachments[i].nameOffset = DataParser.ReadInt(stream); attachments[i].flags = DataParser.ReadInt(stream); attachments[i].localBoneIndex = DataParser.ReadInt(stream); attachments[i].localM11 = DataParser.ReadFloat(stream); attachments[i].localM12 = DataParser.ReadFloat(stream); attachments[i].localM13 = DataParser.ReadFloat(stream); attachments[i].localM14 = DataParser.ReadFloat(stream); attachments[i].localM21 = DataParser.ReadFloat(stream); attachments[i].localM22 = DataParser.ReadFloat(stream); attachments[i].localM23 = DataParser.ReadFloat(stream); attachments[i].localM24 = DataParser.ReadFloat(stream); attachments[i].localM31 = DataParser.ReadFloat(stream); attachments[i].localM32 = DataParser.ReadFloat(stream); attachments[i].localM33 = DataParser.ReadFloat(stream); attachments[i].localM34 = DataParser.ReadFloat(stream); attachments[i].unused = new int[8]; for (int j = 0; j < attachments[i].unused.Length; j++) { attachments[i].unused[j] = DataParser.ReadInt(stream); } } nextAttachmentPosition = stream.Position; if (attachments[i].nameOffset != 0) { stream.Position = attachmentPosition + attachments[i].nameOffset; attachments[i].name = DataParser.ReadNullTerminatedString(stream); } } } return(attachments); }
private void ParseEyeballs(Stream stream, long modelPosition, mstudiomodel_t model) { if (model.eyeballCount >= 0 && model.eyeballOffset != 0) { model.theEyeballs = new mstudioeyeball_t[model.eyeballCount]; long nextEyeballPosition = modelPosition + model.eyeballOffset; for (int i = 0; i < model.theEyeballs.Length; i++) { stream.Position = nextEyeballPosition; long eyeballPosition = nextEyeballPosition; model.theEyeballs[i] = new mstudioeyeball_t(); model.theEyeballs[i].nameOffset = DataParser.ReadInt(stream); model.theEyeballs[i].boneIndex = DataParser.ReadInt(stream); model.theEyeballs[i].org = new Vector3(DataParser.ReadFloat(stream), DataParser.ReadFloat(stream), DataParser.ReadFloat(stream)); model.theEyeballs[i].zOffset = DataParser.ReadFloat(stream); model.theEyeballs[i].radius = DataParser.ReadFloat(stream); model.theEyeballs[i].up = new Vector3(DataParser.ReadFloat(stream), DataParser.ReadFloat(stream), DataParser.ReadFloat(stream)); model.theEyeballs[i].forward = new Vector3(DataParser.ReadFloat(stream), DataParser.ReadFloat(stream), DataParser.ReadFloat(stream)); model.theEyeballs[i].texture = DataParser.ReadInt(stream); model.theEyeballs[i].unused1 = DataParser.ReadInt(stream); model.theEyeballs[i].irisScale = DataParser.ReadFloat(stream); model.theEyeballs[i].unused2 = DataParser.ReadInt(stream); model.theEyeballs[i].upperFlexDesc = new int[3]; model.theEyeballs[i].lowerFlexDesc = new int[3]; model.theEyeballs[i].upperTarget = new double[3]; model.theEyeballs[i].lowerTarget = new double[3]; model.theEyeballs[i].upperFlexDesc[0] = DataParser.ReadInt(stream); model.theEyeballs[i].upperFlexDesc[1] = DataParser.ReadInt(stream); model.theEyeballs[i].upperFlexDesc[2] = DataParser.ReadInt(stream); model.theEyeballs[i].lowerFlexDesc[0] = DataParser.ReadInt(stream); model.theEyeballs[i].lowerFlexDesc[1] = DataParser.ReadInt(stream); model.theEyeballs[i].lowerFlexDesc[2] = DataParser.ReadInt(stream); model.theEyeballs[i].upperTarget[0] = DataParser.ReadFloat(stream); model.theEyeballs[i].upperTarget[1] = DataParser.ReadFloat(stream); model.theEyeballs[i].upperTarget[2] = DataParser.ReadFloat(stream); model.theEyeballs[i].lowerTarget[0] = DataParser.ReadFloat(stream); model.theEyeballs[i].lowerTarget[1] = DataParser.ReadFloat(stream); model.theEyeballs[i].lowerTarget[2] = DataParser.ReadFloat(stream); model.theEyeballs[i].upperLidFlexDesc = DataParser.ReadInt(stream); model.theEyeballs[i].lowerLidFlexDesc = DataParser.ReadInt(stream); model.theEyeballs[i].unused = new int[4]; for (int j = 0; j < model.theEyeballs[i].unused.Length; j++) { model.theEyeballs[i].unused[j] = DataParser.ReadInt(stream); } model.theEyeballs[i].eyeballIsNonFacs = DataParser.ReadByte(stream); model.theEyeballs[i].unused3 = new char[3]; for (int j = 0; j < model.theEyeballs[i].unused3.Length; j++) { model.theEyeballs[i].unused3[j] = DataParser.ReadChar(stream); } model.theEyeballs[i].unused4 = new int[7]; for (int j = 0; j < model.theEyeballs[i].unused4.Length; j++) { model.theEyeballs[i].unused4[j] = DataParser.ReadInt(stream); } //Set the default value to -1 to distinguish it from value assigned to it by ReadMeshes() model.theEyeballs[i].theTextureIndex = -1; nextEyeballPosition = stream.Position; if (model.theEyeballs[i].nameOffset != 0) { stream.Position = eyeballPosition + model.theEyeballs[i].nameOffset; model.theEyeballs[i].name = DataParser.ReadNullTerminatedString(stream); } else { model.theEyeballs[i].name = ""; } } } }
private studiohdr_t ParseHeader1(Stream stream) { header1 = new studiohdr_t(); stream.Position = fileBeginOffset; header1.id = DataParser.ReadInt(stream); // Model format ID, such as "IDST" (0x49 0x44 0x53 0x54) header1.version = DataParser.ReadInt(stream); // Format version number, such as 48 (0x30,0x00,0x00,0x00) header1.checkSum = DataParser.ReadInt(stream); // this has to be the same in the phy and vtx files to load! char[] name = new char[64]; for (int i = 0; i < name.Length; i++) { name[i] = DataParser.ReadChar(stream); } header1.name = name; // The internal name of the model, padding with null bytes. // Typically "my_model.mdl" will have an internal name of "my_model" this.name = new string(name).Replace("\0", ""); header1.dataLength = DataParser.ReadInt(stream); // Data size of MDL file in bytes. // A vector is 12 bytes, three 4-byte float-values in a row. header1.eyeposition = new Vector3(DataParser.ReadFloat(stream), DataParser.ReadFloat(stream), DataParser.ReadFloat(stream)); // Position of player viewpoint relative to model origin header1.illumposition = new Vector3(DataParser.ReadFloat(stream), DataParser.ReadFloat(stream), DataParser.ReadFloat(stream)); // ?? Presumably the point used for lighting when per-vertex lighting is not enabled. header1.hull_min = new Vector3(DataParser.ReadFloat(stream), DataParser.ReadFloat(stream), DataParser.ReadFloat(stream)); // Corner of model hull box with the least X/Y/Z values header1.hull_max = new Vector3(DataParser.ReadFloat(stream), DataParser.ReadFloat(stream), DataParser.ReadFloat(stream)); // Opposite corner of model hull box header1.view_bbmin = new Vector3(DataParser.ReadFloat(stream), DataParser.ReadFloat(stream), DataParser.ReadFloat(stream)); // View Bounding Box Minimum Position header1.view_bbmax = new Vector3(DataParser.ReadFloat(stream), DataParser.ReadFloat(stream), DataParser.ReadFloat(stream)); // View Bounding Box Maximum Position header1.flags = DataParser.ReadInt(stream); // Binary flags in little-endian order. // ex (00000001,00000000,00000000,11000000) means flags for position 0, 30, and 31 are set. // Set model flags section for more information //Debug.Log("ID: " + header1.id + ", \nVersion: " + header1.version + ", \nCheckSum: " + header1.checkSum + ", \nName: " + this.name + ", \nLength: " + header1.dataLength); //Debug.Log("EyePos: " + header1.eyeposition + ", \nIllumPos: " + header1.illumposition + ", \nHullMin: " + header1.hull_min + ", \nHullMax: " + header1.hull_max + ", \nViewBBMin: " + header1.view_bbmin + ", \nViewBBMax: " + header1.view_bbmax); /* * After this point, the header contains many references to offsets * within the MDL file and the number of items at those offsets. * * Offsets are from the very beginning of the file. * * Note that indexes/counts are not always paired and ordered consistently. */ // mstudiobone_t header1.bone_count = DataParser.ReadInt(stream); // Number of data sections (of type mstudiobone_t) header1.bone_offset = DataParser.ReadInt(stream); // Offset of first data section // mstudiobonecontroller_t header1.bonecontroller_count = DataParser.ReadInt(stream); header1.bonecontroller_offset = DataParser.ReadInt(stream); // mstudiohitboxset_t header1.hitbox_count = DataParser.ReadInt(stream); header1.hitbox_offset = DataParser.ReadInt(stream); // mstudioanimdesc_t header1.localanim_count = DataParser.ReadInt(stream); header1.localanim_offset = DataParser.ReadInt(stream); // mstudioseqdesc_t header1.localseq_count = DataParser.ReadInt(stream); header1.localseq_offset = DataParser.ReadInt(stream); header1.activitylistversion = DataParser.ReadInt(stream); // ?? header1.eventsindexed = DataParser.ReadInt(stream); // ?? // VMT texture filenames // mstudiotexture_t header1.texture_count = DataParser.ReadInt(stream); header1.texture_offset = DataParser.ReadInt(stream); // This offset points to a series of ints. // Each int value, in turn, is an offset relative to the start of this header/the-file, // At which there is a null-terminated string. header1.texturedir_count = DataParser.ReadInt(stream); header1.texturedir_offset = DataParser.ReadInt(stream); // Each skin-family assigns a texture-id to a skin location header1.skinreference_count = DataParser.ReadInt(stream); header1.skinrfamily_count = DataParser.ReadInt(stream); header1.skinreference_index = DataParser.ReadInt(stream); // mstudiobodyparts_t header1.bodypart_count = DataParser.ReadInt(stream); header1.bodypart_offset = DataParser.ReadInt(stream); // Local attachment points // mstudioattachment_t header1.attachment_count = DataParser.ReadInt(stream); header1.attachment_offset = DataParser.ReadInt(stream); // Node values appear to be single bytes, while their names are null-terminated strings. header1.localnode_count = DataParser.ReadInt(stream); header1.localnode_index = DataParser.ReadInt(stream); header1.localnode_name_index = DataParser.ReadInt(stream); // mstudioflexdesc_t header1.flexdesc_count = DataParser.ReadInt(stream); header1.flexdesc_index = DataParser.ReadInt(stream); // mstudioflexcontroller_t header1.flexcontroller_count = DataParser.ReadInt(stream); header1.flexcontroller_index = DataParser.ReadInt(stream); // mstudioflexrule_t header1.flexrules_count = DataParser.ReadInt(stream); header1.flexrules_index = DataParser.ReadInt(stream); // IK probably referse to inverse kinematics // mstudioikchain_t header1.ikchain_count = DataParser.ReadInt(stream); header1.ikchain_index = DataParser.ReadInt(stream); // Information about any "mouth" on the model for speech animation // More than one sounds pretty creepy. // mstudiomouth_t header1.mouths_count = DataParser.ReadInt(stream); header1.mouths_index = DataParser.ReadInt(stream); // mstudioposeparamdesc_t header1.localposeparam_count = DataParser.ReadInt(stream); header1.localposeparam_index = DataParser.ReadInt(stream); /* * For anyone trying to follow along, as of this writing, * the next "surfaceprop_index" value is at position 0x0134 (308) * from the start of the file. */ //stream.Position = 308; // Surface property value (single null-terminated string) header1.surfaceprop_index = DataParser.ReadInt(stream); // Unusual: In this one index comes first, then count. // Key-value data is a series of strings. If you can't find // what you're interested in, check the associated PHY file as well. header1.keyvalue_index = DataParser.ReadInt(stream); header1.keyvalue_count = DataParser.ReadInt(stream); // More inverse-kinematics // mstudioiklock_t header1.iklock_count = DataParser.ReadInt(stream); header1.iklock_index = DataParser.ReadInt(stream); header1.mass = DataParser.ReadFloat(stream); // Mass of object (4-bytes) header1.contents = DataParser.ReadInt(stream); // ?? // Other models can be referenced for re-used sequences and animations // (See also: The $includemodel QC option.) // mstudiomodelgroup_t header1.includemodel_count = DataParser.ReadInt(stream); header1.includemodel_index = DataParser.ReadInt(stream); header1.virtualModel = DataParser.ReadInt(stream); // Placeholder for mutable-void* // mstudioanimblock_t header1.animblocks_name_index = DataParser.ReadInt(stream); header1.animblocks_count = DataParser.ReadInt(stream); header1.animblocks_index = DataParser.ReadInt(stream); header1.animblockModel = DataParser.ReadInt(stream); // Placeholder for mutable-void* // Points to a series of bytes? header1.bonetablename_index = DataParser.ReadInt(stream); header1.vertex_base = DataParser.ReadInt(stream); // Placeholder for void* header1.offset_base = DataParser.ReadInt(stream); // Placeholder for void* // Used with $constantdirectionallight from the QC // Model should have flag #13 set if enabled header1.directionaldotproduct = DataParser.ReadByte(stream); header1.rootLod = DataParser.ReadByte(stream); // Preferred rather than clamped // 0 means any allowed, N means Lod 0 -> (N-1) header1.numAllowedRootLods = DataParser.ReadByte(stream); //header.unused; // ?? header1.unused1 = DataParser.ReadByte(stream); //header.unused; // ?? header1.unused2 = DataParser.ReadInt(stream); // mstudioflexcontrollerui_t header1.flexcontrollerui_count = DataParser.ReadInt(stream); header1.flexcontrollerui_index = DataParser.ReadInt(stream); header1.vertAnimFixedPointScale = DataParser.ReadFloat(stream); header1.surfacePropLookup = DataParser.ReadInt(stream); /** * Offset for additional header information. * May be zero if not present, or also 408 if it immediately * follows this studiohdr_t */ // studiohdr2_t header1.studiohdr2index = DataParser.ReadInt(stream); //header.unused; // ?? header1.unused3 = DataParser.ReadInt(stream); return(header1); }