#include "renderware.h" using namespace std; #define DBGOUT(x) cout x //#define DBGOUT(c) ; namespace rw { char *filename; /* * Clump */ bool Clump::read(ifstream& dff) { HeaderInfo header; header.read(dff); if (header.type != CHUNK_CLUMP) return false; READ_HEADER(CHUNK_STRUCT); uint32 numAtomics = readUInt32(dff); uint32 numLights; if (header.length == 12) { numLights = readUInt32(dff); dff.seekg(4, ios::cur); /* cameras aren't used in gta */ } else { numLights = 0; } atomicList.resize(numAtomics); READ_HEADER(CHUNK_FRAMELIST); READ_HEADER(CHUNK_STRUCT); uint32 numFrames = readUInt32(dff); frameList.resize(numFrames); for (uint32 i = 0; i < numFrames; i++) frameList[i].readStruct(dff); for (uint32 i = 0; i < numFrames; i++) frameList[i].readExtension(dff); READ_HEADER(CHUNK_GEOMETRYLIST); READ_HEADER(CHUNK_STRUCT); uint32 numGeometries = readUInt32(dff); geometryList.resize(numGeometries); for (uint32 i = 0; i < numGeometries; i++) geometryList[i].read(dff); /* read atomics */ for (uint32 i = 0; i < numAtomics; i++) atomicList[i].read(dff); /* skip lights */ for (uint32 i = 0; i < numLights; i++) { READ_HEADER(CHUNK_STRUCT); dff.seekg(header.length, ios::cur); READ_HEADER(CHUNK_LIGHT); dff.seekg(header.length, ios::cur); } readExtension(dff); return true; } void Clump::readExtension(ifstream &dff) { HeaderInfo header; READ_HEADER(CHUNK_EXTENSION); uint32 end = dff.tellg(); end += header.length; while (dff.tellg() < end) { header.read(dff); switch (header.type) { case CHUNK_COLLISIONMODEL: dff.seekg(header.length, ios::cur); break; default: dff.seekg(header.length, ios::cur); break; } } } Clump::Clump(void) { } Clump::~Clump(void) { } void Clump::writeMsh(ofstream &msh) { geometryList[0].writeMsh(msh); } /* * Atomic */ void Atomic::read(ifstream &dff) { HeaderInfo header; READ_HEADER(CHUNK_ATOMIC); READ_HEADER(CHUNK_STRUCT); frameIndex = readUInt32(dff); geometryIndex = readUInt32(dff); dff.seekg(8, ios::cur); readExtension(dff); } void Atomic::readExtension(ifstream &dff) { HeaderInfo header; READ_HEADER(CHUNK_EXTENSION); uint32 end = dff.tellg(); end += header.length; while (dff.tellg() < end) { header.read(dff); switch (header.type) { case CHUNK_RIGHTTORENDER: hasRightToRender = true; rightToRenderVal1 = readUInt32(dff); rightToRenderVal2 = readUInt32(dff); break; case CHUNK_PARTICLES: hasParticles = true; particlesVal = readUInt32(dff); break; case CHUNK_MATERIALEFFECTS: hasMaterialFx = true; materialFxVal = readUInt32(dff); break; case CHUNK_PIPELINESET: hasPipelineSet = true; pipelineSetVal = readUInt32(dff); break; default: dff.seekg(header.length, ios::cur); break; } } } Atomic::Atomic(void) { hasRightToRender = false; hasParticles = false; hasMaterialFx = false; hasPipelineSet = false; } Atomic::~Atomic(void) { } /* * Frame */ void Frame::readStruct(ifstream &dff) { dff.read(reinterpret_cast (rotationMatrix), 9*sizeof(float32)); dff.read(reinterpret_cast (position), 3*sizeof(float32)); parent = readInt32(dff); dff.seekg(4, ios::cur); } void Frame::readExtension(ifstream &dff) { HeaderInfo header; READ_HEADER(CHUNK_EXTENSION); uint32 end = dff.tellg(); end += header.length; while (dff.tellg() < end) { header.read(dff); switch (header.type) { case CHUNK_FRAME: { char *buffer = new char[header.length+1]; dff.read(buffer, header.length); buffer[header.length] = '\0'; name = buffer; delete[] buffer; break; } case CHUNK_HANIM: hasHAnim = true; hAnimUnknown1 = readUInt32(dff);; hAnimBoneId = readInt32(dff);; hAnimBoneCount = readUInt32(dff);; if (hAnimBoneCount != 0) { hAnimUnknown2 = readUInt32(dff);; hAnimUnknown3 = readUInt32(dff);; } for (uint32 i = 0; i < hAnimBoneCount; i++) { hAnimBoneIds.push_back(readInt32(dff)); hAnimBoneNumbers.push_back(readUInt32(dff)); hAnimBoneTypes.push_back(readUInt32(dff)); } break; default: dff.seekg(header.length, ios::cur); break; } } } Frame::Frame(void) { hasHAnim = false; } Frame::~Frame(void) { } /* * Geometry */ void Geometry::read(ifstream &dff) { HeaderInfo header; READ_HEADER(CHUNK_GEOMETRY); uint32 end = dff.tellg(); end += header.length; READ_HEADER(CHUNK_STRUCT); flags = readUInt16(dff); numUVs = readUInt8(dff); hasNativeGeometry = readUInt8(dff); uint32 triangleCount = readUInt32(dff); vertexCount = readUInt32(dff); dff.seekg(4, ios::cur); /* number of morph targets, uninteresting */ if (header.version == GTA3_1 || header.version == GTA3_2 || header.version == GTA3_3 || header.version == GTA3_4 || header.version == VCPS2) { dff.seekg(12, ios::cur); } if (!hasNativeGeometry) { if (flags & FLAGS_PRELIT) { vertexColors.resize(4*vertexCount); dff.read(reinterpret_cast (&vertexColors[0]), 4*vertexCount*sizeof(uint8)); } if (flags & FLAGS_TEXTURED) { texCoords1.resize(2*vertexCount); dff.read(reinterpret_cast (&texCoords1[0]), 2*vertexCount*sizeof(float32)); } if (flags & FLAGS_TEXTURED2) { texCoords1.resize(2*vertexCount); texCoords2.resize(2*vertexCount); dff.read(reinterpret_cast (&texCoords1[0]), 2*vertexCount*sizeof(float32)); dff.read(reinterpret_cast (&texCoords2[0]), 2*vertexCount*sizeof(float32)); } faces.resize(4*triangleCount); dff.read(reinterpret_cast (&faces[0]), 4*triangleCount*sizeof(uint16)); } dff.read(reinterpret_cast (boundingSphere), 4*sizeof(float32)); bsPos = readUInt32(dff); bsPos = 1; // this needs to be 1, otherwise renderware will crash bsNor = readUInt32(dff); if (!hasNativeGeometry) { vertices.resize(3*vertexCount); dff.read(reinterpret_cast (&vertices[0]), 3*vertexCount*sizeof(float32)); if (flags & FLAGS_NORMALS) { normals.resize(3*vertexCount); dff.read(reinterpret_cast (&normals[0]), 3*vertexCount*sizeof(float32)); } } READ_HEADER(CHUNK_MATLIST); READ_HEADER(CHUNK_STRUCT); uint32 numMaterials = readUInt32(dff); dff.seekg(numMaterials*4, ios::cur); materialList.resize(numMaterials); for (uint32 i = 0; i < numMaterials; i++) materialList[i].read(dff); readExtension(dff); } void Geometry::readExtension(ifstream &dff) { HeaderInfo header; READ_HEADER(CHUNK_EXTENSION); uint32 end = dff.tellg(); end += header.length; while (dff.tellg() < end) { header.read(dff); switch (header.type) { case CHUNK_BINMESH: { faceType = readUInt32(dff); uint32 numSplits = readUInt32(dff); numIndices = readUInt32(dff); splits.resize(numSplits); for (uint32 i = 0; i < numSplits; i++) { uint32 numIndices = readUInt32(dff); splits[i].matIndex = readUInt32(dff); splits[i].indices.resize(numIndices); if (!hasNativeGeometry) for (uint32 j = 0; j < numIndices; j++) splits[i].indices[j] = readUInt32(dff); } break; } case CHUNK_NATIVEDATA: { uint32 beg = dff.tellg(); dff.seekg(0x0c, ios::cur); uint32 platform = readUInt32(dff); dff.seekg(beg, ios::beg); if (platform == 0x04) readPs2NativeData(dff); else if (platform == 0x05) readXboxNativeData(dff); else cout << "unknown platform " << platform << endl; /* skip everything just to be sure */ dff.seekg(beg, ios::beg); dff.seekg(header.length, ios::cur); break; } case CHUNK_MESHEXTENSION: { hasMeshExtension = true; meshExtension = new MeshExtension; meshExtension->unknown = readUInt32(dff); readMeshExtension(dff); break; } case CHUNK_NIGHTVERTEXCOLOR: { hasNightColors = true; nightColorsUnknown = readUInt32(dff); if (nightColors.size() != 0) { dff.seekg(header.length - sizeof(uint32), ios::cur); break; } /* TODO: could be better */ if (nightColorsUnknown != 0) { nightColors.resize(header.length-4); dff.read(reinterpret_cast (&nightColors[0]), header.length-4); } break; } case CHUNK_MORPH: { hasMorph = true; /* always 0 */ readUInt32(dff); break; } case CHUNK_SKIN: { if (hasNativeGeometry) { dff.seekg(header.length, ios::cur); } else { hasSkin = true; boneCount = readUInt8(dff); specialIndexCount = readUInt8(dff); unknown1 = readUInt8(dff); unknown2 = readUInt8(dff); specialIndices.resize(specialIndexCount); dff.read(reinterpret_cast (&specialIndices[0]), specialIndexCount*sizeof(uint8)); vertexBoneIndices.resize(vertexCount); dff.read(reinterpret_cast (&vertexBoneIndices[0]), vertexCount*sizeof(uint32)); vertexBoneWeights.resize(vertexCount*4); dff.read(reinterpret_cast (&vertexBoneWeights[0]), vertexCount*4*sizeof(float32)); inverseMatrices.resize(boneCount*16); for (uint32 i = 0; i < boneCount; i++) { // skip 0xdeaddead if (specialIndexCount == 0) dff.seekg(4, ios::cur); dff.read(reinterpret_cast (&inverseMatrices[i*16]), 16*sizeof(float32)); } // skip some zeroes if (specialIndexCount != 0) dff.seekg(0x0C, ios::cur); } break; } case CHUNK_2DFX: dff.seekg(header.length, ios::cur); break; case CHUNK_ADCPLG: /* only sa ps2, ignore (not very interesting anyway) */ dff.seekg(header.length, ios::cur); break; default: dff.seekg(header.length, ios::cur); break; } } } void Geometry::readMeshExtension(ifstream &dff) { if (meshExtension->unknown == 0) return; dff.seekg(0x4, ios::cur); uint32 vertexCount = readUInt32(dff); dff.seekg(0xC, ios::cur); uint32 faceCount = readUInt32(dff); dff.seekg(0x8, ios::cur); uint32 materialCount = readUInt32(dff); dff.seekg(0x10, ios::cur); /* skip geometry */ /* vertices */ meshExtension->vertices.resize(3*vertexCount); dff.read(reinterpret_cast (&meshExtension->vertices[0]), 3*vertexCount*sizeof(float32)); /* tex coords */ meshExtension->texCoords.resize(2*vertexCount); dff.read(reinterpret_cast (&meshExtension->texCoords[0]), 2*vertexCount*sizeof(float32)); /* vertex colors */ meshExtension->vertexColors.resize(4*vertexCount); dff.read(reinterpret_cast (&meshExtension->vertexColors[0]), 4*vertexCount*sizeof(uint8)); /* faces */ meshExtension->faces.resize(3*faceCount); dff.read(reinterpret_cast (&meshExtension->faces[0]), 3*faceCount*sizeof(uint16)); /* material assignments */ meshExtension->assignment.resize(faceCount); dff.read(reinterpret_cast (&meshExtension->assignment[0]), faceCount*sizeof(uint16)); /* skip materials */ meshExtension->textureName.resize(materialCount); meshExtension->maskName.resize(materialCount); char buffer[0x20]; for (uint32 i = 0; i < materialCount; i++) { dff.read(buffer, 0x20); meshExtension->textureName[i] = buffer; dff.read(buffer, 0x20); meshExtension->maskName[i] = buffer; meshExtension->unknowns.push_back(readFloat32(dff)); meshExtension->unknowns.push_back(readFloat32(dff)); meshExtension->unknowns.push_back(readFloat32(dff)); } } bool Geometry::isDegenerateFace(uint32 i, uint32 j, uint32 k) { if (vertices[i*3+0] == vertices[j*3+0] && vertices[i*3+1] == vertices[j*3+1] && vertices[i*3+2] == vertices[j*3+2]) return true; if (vertices[i*3+0] == vertices[k*3+0] && vertices[i*3+1] == vertices[k*3+1] && vertices[i*3+2] == vertices[k*3+2]) return true; if (vertices[j*3+0] == vertices[k*3+0] && vertices[j*3+1] == vertices[k*3+1] && vertices[j*3+2] == vertices[k*3+2]) return true; return false; } void Geometry::generateFaces(void) { faces.clear(); uint32 inc; if (faceType == FACETYPE_STRIP) inc = 1; else inc = 3; for (uint32 i = 0; i < splits.size(); i++) { for (uint32 j = 0; j < splits[i].indices.size()-2; j += inc) { if (!isDegenerateFace(j, j+1, j+2)) { faces.push_back(splits[i].indices[j+1]); faces.push_back(splits[i].indices[j+0]); faces.push_back(splits[i].matIndex); faces.push_back(splits[i].indices[j+2]); } } } } Geometry::Geometry(void) { hasMorph = false; hasMeshExtension = false; meshExtension = 0; hasSkin = false; hasNightColors = false; } Geometry::~Geometry(void) { if (meshExtension != 0) delete meshExtension; } void Geometry::writeMsh(ofstream &msh) { msh << "node" << endl; msh << "clump" << endl; msh << "0 0 0" << endl; msh << "mesh" << endl; if (faceType == FACETYPE_STRIP) msh << "5" << endl; else msh << "7" << endl; uint32 attribs = 0; if (flags & FLAGS_NORMALS) attribs |= 0x1; if (flags & FLAGS_PRELIT) attribs |= 0x2; if (flags & FLAGS_TEXTURED) attribs |= 0x4; msh << attribs << endl; msh << vertices.size()/3 << endl; for (uint32 i = 0; i < vertices.size() / 3; i++) { msh << vertices[i*3+0] << " "; msh << vertices[i*3+1] << " "; msh << vertices[i*3+2] << endl; } if (flags & FLAGS_NORMALS) for (uint32 i = 0; i < vertices.size() / 3; i++) { msh << normals[i*3+0] << " "; msh << normals[i*3+1] << " "; msh << normals[i*3+2] << endl; } if (flags & FLAGS_PRELIT) { for (uint32 i = 0; i < vertices.size() / 3; i++) { msh << vertexColors[i*4+0]/255.0 << " "; msh << vertexColors[i*4+1]/255.0 << " "; msh << vertexColors[i*4+2]/255.0 << " "; msh << vertexColors[i*4+3]/255.0 << endl; } } if (flags & FLAGS_TEXTURED) for (uint32 i = 0; i < vertices.size() / 3; i++) { msh << texCoords1[i*3+0] << " "; msh << texCoords1[i*3+1] << endl; } msh << splits.size() << endl; for (uint32 i = 0; i < splits.size(); i++) { msh << splits[i].indices.size() << endl; for (uint32 j = 0; j < splits[i].indices.size(); j++) msh << splits[i].indices[j] << endl; } } /* * Material */ void Material::read(ifstream &dff) { HeaderInfo header; READ_HEADER(CHUNK_MATERIAL); READ_HEADER(CHUNK_STRUCT); flags = readUInt32(dff); dff.read(reinterpret_cast (color), 4*sizeof(uint8)); unknown = readUInt32(dff);; hasTex = readInt32(dff); dff.read(reinterpret_cast (surfaceProps), 3*sizeof(float32)); if (hasTex) texture.read(dff); readExtension(dff); } void Material::readExtension(ifstream &dff) { HeaderInfo header; READ_HEADER(CHUNK_EXTENSION); uint32 end = dff.tellg(); end += header.length; while (dff.tellg() < end) { header.read(dff); switch (header.type) { case CHUNK_RIGHTTORENDER: hasRightToRender = true; rightToRenderVal1 = readUInt32(dff); rightToRenderVal2 = readUInt32(dff); break; case CHUNK_MATERIALEFFECTS: { uint32 beg = dff.tellg(); uint32 type1 = readUInt32(dff); uint32 type2 = readUInt32(dff); dff.seekg(beg, ios::beg); dff.seekg(header.length, ios::cur); break; } case CHUNK_REFLECTIONMAT: hasReflectionMat = true; reflectionChannelAmount[0] = readFloat32(dff); reflectionChannelAmount[1] = readFloat32(dff); reflectionChannelAmount[2] = readFloat32(dff); reflectionChannelAmount[3] = readFloat32(dff); reflectionIntensity = readFloat32(dff); dff.seekg(4, ios::cur); break; case CHUNK_SPECULARMAT: { hasSpecularMat = true; specularLevel = readFloat32(dff); uint32 len = header.length - sizeof(float32) - 4; char *name = new char[len]; dff.read(name, len); specularName = name; dff.seekg(4, ios::cur); delete[] name; break; } case CHUNK_UVANIMDICT: dff.seekg(header.length, ios::cur); break; default: dff.seekg(header.length, ios::cur); break; } } } Material::Material(void) { hasRightToRender = false; hasReflectionMat = false; hasSpecularMat = false; } Material::~Material(void) { } /* * Texture */ void Texture::read(ifstream &dff) { HeaderInfo header; READ_HEADER(CHUNK_TEXTURE); READ_HEADER(CHUNK_STRUCT); filterFlags = readUInt16(dff); dff.seekg(2, ios::cur); READ_HEADER(CHUNK_STRING); char *buffer = new char[header.length+1]; dff.read(buffer, header.length); buffer[header.length] = '\0'; name = buffer; delete[] buffer; READ_HEADER(CHUNK_STRING); buffer = new char[header.length+1]; dff.read(buffer, header.length); buffer[header.length] = '\0'; maskName = buffer; readExtension(dff); } void Texture::readExtension(ifstream &dff) { HeaderInfo header; READ_HEADER(CHUNK_EXTENSION); uint32 end = dff.tellg(); end += header.length; while (dff.tellg() < end) { header.read(dff); switch (header.type) { case CHUNK_SKYMIPMAP: hasSkyMipmap = true; dff.seekg(header.length, ios::cur); break; default: dff.seekg(header.length, ios::cur); break; } } } Texture::Texture(void) { hasSkyMipmap = false; } Texture::~Texture(void) { } }