void MapItemDatabase::ExportAllItems(string textureExportPath, string meshExportPath, string materialExportPath, string sceneExportPath, bool bOverwrite) { // export textures and preprocess textures for materials // TODO: seperate txd out into a unique list (txd tend to repeat in Item Definations) for (auto it : this->m_items) { MapItemDefination& mapItem = *(it.second); istream* txdStream = nullptr; // preprocess for materials? if (!this->GetResourceStreamFromIMGs(mapItem.GetTexDictStem() + ".txd", txdStream)) throw new MyException("TXD NOT FOUND"); TXDArchive txdArchive(txdStream); for (int i = 0; i < txdArchive.getTextureCount(); i++) { TXDTextureHeader* texHeader = txdArchive.getHeader(i); m_matDB->AddTextureHeaderForPreProcessing(texHeader); } this->m_texExporter->Export(&txdArchive, mapItem.GetTexDictStem(), textureExportPath, bOverwrite); } // export the DFFs for (auto it : this->m_items) { MapItemDefination& mapItem = *(it.second); istream* dffStream = nullptr; if (!this->GetResourceStreamFromIMGs(mapItem.GetDFFStem() + ".dff", dffStream)) throw new MyException("DFF NOT FOUND"); DFFLoader loader; DFFMesh* mesh = loader.loadMesh(dffStream); // TODO: Add material for Subclumps vector<string> matList = this->m_matDB->CreateOrRetrieveMaterial(mesh->getGeometry(0), &mapItem); this->m_dffExporter->Export(matList, mesh, mapItem.GetDFFStem(), meshExportPath, bOverwrite); delete mesh; } this->m_matDB->ExportAll(materialExportPath); this->ExportScene(sceneExportPath); }
DFFMesh* DFFLoader::loadMesh(RWSection* clump) { DFFMesh* mesh = new DFFMesh; // ************************************************** // * LOAD THE FRAMES * // ************************************************** RWSection* frameList = clump->getChild(RW_SECTION_FRAMELIST); RWSection* frameListStruct = frameList->getChild(RW_SECTION_STRUCT); uint8_t* frameListStructData = frameListStruct->getData(); uint32_t numFrames = FromLittleEndian32(*((uint32_t*) frameListStructData)); // In DFF, the frames are stored and indexed as a flat data structure, so at first we keep them flat. IndexedDFFFrame* indexedFrames = new IndexedDFFFrame[numFrames]; DFFFrame** boneFrames = NULL; map<int32_t, DFFBone*> bonesByIndex; map<int32_t, DFFBone*> bonesByNum; for (uint32_t i = 0 ; i < numFrames ; i++) { IndexedDFFFrame& indexedFrame = indexedFrames[i]; indexedFrame.boneID = -1; uint8_t* offData = frameListStructData + 4 + i*56; #ifdef GTAFORMATS_LITTLE_ENDIAN Matrix4* mm = new Matrix4 ( *((float*) (offData)), *((float*) (offData + 4)), *((float*) (offData + 8)), 0.0f, *((float*) (offData + 12)), *((float*) (offData + 16)), *((float*) (offData + 20)), 0.0f, *((float*) (offData + 24)), *((float*) (offData + 28)), *((float*) (offData + 32)), 0.0f, *((float*) (offData + 36)), *((float*) (offData + 40)), *((float*) (offData + 44)), 1.0f ); #else Matrix4* mm = new Matrix4 ( FromLittleEndianF32(*((float*) (offData))), FromLittleEndianF32(*((float*) (offData + 4))), FromLittleEndianF32(*((float*) (offData + 8))), 0.0f, FromLittleEndianF32(*((float*) (offData + 12))), FromLittleEndianF32(*((float*) (offData + 16))), FromLittleEndianF32(*((float*) (offData + 20))), 0.0f, FromLittleEndianF32(*((float*) (offData + 24))), FromLittleEndianF32(*((float*) (offData + 28))), FromLittleEndianF32(*((float*) (offData + 32))), 0.0f, FromLittleEndianF32(*((float*) (offData + 36))), FromLittleEndianF32(*((float*) (offData + 40))), FromLittleEndianF32(*((float*) (offData + 44))), 1.0f ); #endif /*#ifdef GTAFORMATS_LITTLE_ENDIAN Matrix3* rotMatrix = new Matrix3((float*) offData); Vector3* posVector = new Vector3((float*) (offData + 9*4)); #else float* rData = (float*) offData; float* pData = (float*) (offData + 9*4); Matrix3* rotMatrix = new Matrix3( FromLittleEndianF32(rData[0]), FromLittleEndianF32(rData[1]), FromLittleEndianF32(rData[2]), FromLittleEndianF32(rData[3]), FromLittleEndianF32(rData[4]), FromLittleEndianF32(rData[5]), FromLittleEndianF32(rData[6]), FromLittleEndianF32(rData[7]), FromLittleEndianF32(rData[8]) ); Vector3* posVector = new Vector3(FromLittleEndianF32(pData[0]), FromLittleEndianF32(pData[1]), FromLittleEndianF32(pData[2])); #endif*/ indexedFrame.parentIdx = FromLittleEndian32(*((uint32_t*) (offData + 12*4))); DFFFrame* frame = new DFFFrame(mm); frame->flags = FromLittleEndian32(*((uint32_t*) (offData + 13*4))); indexedFrame.frame = frame; } // Now we read the frame names. uint32_t i = 0; RWSection::ChildIterator it = frameList->getChildBegin(); while ((it = frameList->nextChild(RW_SECTION_EXTENSION, it)) != frameList->getChildEnd()) { if (i >= numFrames) { throw DFFException("Too many frame list extensions", __FILE__, __LINE__); } DFFFrame* frame = indexedFrames[i].frame; RWSection* frameListExt = *it; RWSection* frameSect = frameListExt->getChild(RW_SECTION_FRAME); if (frameSect) { char* frameName = new char[frameSect->getSize() + 1]; frameName[frameSect->getSize()] = '\0'; memcpy(frameName, frameSect->getData(), frameSect->getSize()); frame->setName(CString::from(frameName).trim()); } RWSection* hAnimPlg = frameListExt->getChild(RW_SECTION_HANIM_PLG); if (hAnimPlg) { // We have a hierarchical animation plugin (HANIM_PLG) section. Such a section can exist for each frame, but // does not have to. If it is present for a frame, this means that the frame acts as a bone for an animation, // and the frame gets assigned a bone ID. HANIM_PLG also doubles as a section that describes the types and // properties of bones when the boneCount property is not zero. This code currently assumes that all this // additional bone information is contained completely in a single HANIM_PLG section, and not spread across // multiple (which seems to be the case in all of the original DFF meshes). uint8_t* adata = hAnimPlg->getData(); // The bone ID of this specific frame. int32_t boneID = FromLittleEndian32(*((int32_t*) (adata+4))); // Number of bones for which additional properties are stored. See above. int32_t boneCount = FromLittleEndian32(*((int32_t*) (adata+8))); adata += 12; indexedFrames[i].boneID = boneID; if (boneCount != 0) { // We have additional bone data in this section. adata += 8; mesh->boneCount = boneCount; boneFrames = new DFFFrame*[boneCount]; // We assume that all bone detail data is contained in a single section. If we have multiple sections // containing bone detail, we keep the data of the more recent section. for (map<int32_t, DFFBone*>::iterator it = bonesByNum.begin() ; it != bonesByNum.end() ; it++) delete it->second; bonesByIndex.clear(); bonesByNum.clear(); for (int32_t i = 0 ; i < boneCount ; i++) { // NOTE: The 'type' value might be the node topology flags. I don't know this for sure // and we don't seem to need this either way, but it might be like this: // 0 = NONE // 1 = POP // 2 = PUSH // 3 = PUSH/POP DFFBone* bone = new DFFBone; memcpy(bone, adata, 12); #ifndef GTAFORMATS_LITTLE_ENDIAN bone->index = FromLittleEndian32(bone->index); bone->num = FromLittleEndian32(bone->num); bone->type = FromLittleEndian32(bone->type); #endif bonesByIndex[bone->getIndex()] = bone; bonesByNum[bone->getNumber()] = bone; adata += 12; } } } i++; it++; } // Associate frames with bones if (bonesByIndex.size() != 0) { uint32_t boneIdx = 0; for (uint32_t i = 0 ; i < numFrames ; i++) { if (indexedFrames[i].boneID != -1) { indexedFrames[i].frame->bone = bonesByIndex[indexedFrames[i].boneID]; boneFrames[boneIdx++] = indexedFrames[i].frame; } } } // And now we will actually build the frame hierarchy. // We still keep the flat structure (indexedFrames), because we will need this later when we read the // ATOMIC sections, which link frames and geometries. DFFFrame* rootFrame = mesh->getRootFrame(); for (i = 0 ; i < numFrames ; i++) { IndexedDFFFrame& indexedFrame = indexedFrames[i]; if (indexedFrame.parentIdx != -1) { indexedFrames[indexedFrame.parentIdx].frame->addChild(indexedFrame.frame); } else { rootFrame->addChild(indexedFrame.frame); } } // ****************************************************** // * LOAD THE GEOMETRIES * // ****************************************************** RWSection* geomList = clump->getChild(RW_SECTION_GEOMETRYLIST); RWSection* geomListStruct = geomList->getChild(RW_SECTION_STRUCT); uint32_t numGeoms = FromLittleEndian32(*((uint32_t*) geomListStruct->getData())); RWSection::ChildIterator geomIt = geomList->getChildBegin(); while ((geomIt = geomList->nextChild(RW_SECTION_GEOMETRY, geomIt)) != geomList->getChildEnd()) { RWSection* geomSect = *geomIt; // ********** LOAD THE GEOMETRY STRUCT ********** // This is most notably the vertex data. RWSection* geomStruct = geomSect->getChild(RW_SECTION_STRUCT); uint8_t* geomData = geomStruct->getData(); uint16_t flags = FromLittleEndian16(*((uint16_t*) geomData)); uint8_t uvSetCount = *(geomData+2); // uint8_t unknown = *(geomData+3); uint32_t triCount = FromLittleEndian32(*((uint32_t*) (geomData+4))); uint32_t vertexCount = FromLittleEndian32(*((uint32_t*) (geomData+8))); uint32_t frameCount = FromLittleEndian32(*((uint32_t*) (geomData+12))); if ((flags & GEOMETRY_FLAG_TEXCOORDS) != 0 && (flags & GEOMETRY_FLAG_MULTIPLEUVSETS) == 0) { // At least some meshes in GTA 3 have the UV set count set to 0 although GEOMETRY_FLAG_TEXCOORDS // is set. uvSetCount = 1; } // Check if the flags are correct. /*assert ( (flags & GEOMETRY_FLAG_MULTIPLEUVSETS) != 0 || ( (flags & GEOMETRY_FLAG_TEXCOORDS) != 0 && uvSetCount == 1 ) || uvSetCount == 0 );*/ float ambientColor = 0.0f; float diffuseColor = 0.0f; float specularColor = 0.0f; uint8_t* vertexColors = NULL; float* uvCoords = NULL; float* vertices = NULL; float* normals = NULL; geomData += 16; if (geomStruct->getVersion() < RW_VERSION_GTAVC_2) { ambientColor = FromLittleEndianF32(*((float*) geomData)); diffuseColor = FromLittleEndianF32(*((float*) (geomData + 4))); specularColor = FromLittleEndianF32(*((float*) (geomData + 8))); geomData += 12; } if ((flags & GEOMETRY_FLAG_COLORS) != 0) { vertexColors = new uint8_t[vertexCount*4]; memcpy(vertexColors, geomData, vertexCount*4); geomData += vertexCount*4; } if ((flags & (GEOMETRY_FLAG_TEXCOORDS | GEOMETRY_FLAG_MULTIPLEUVSETS)) != 0) { size_t size = uvSetCount * vertexCount * 2; uvCoords = new float[size]; memcpy(uvCoords, geomData, size*4); #ifndef GTAFORMATS_LITTLE_ENDIAN for (size_t i = 0 ; i < size ; i++) { uvCoords[i] = SwapEndiannessF32(uvCoords[i]); } #endif geomData += size*4; } // Skip the indices, we'll use the ones from the material split section geomData += triCount * 8; DFFBoundingSphere* bounds = new DFFBoundingSphere; memcpy(bounds, geomData, 4*4); #ifndef GTAFORMATS_LITTLE_ENDIAN bounds->x = SwapEndiannessF32(bounds->x); bounds->y = SwapEndiannessF32(bounds->y); bounds->z = SwapEndiannessF32(bounds->z); bounds->radius = SwapEndiannessF32(bounds->radius); #endif // uint32_t hasPositions = FromLittleEndian32(*((uint32_t*) (geomData+16))); // uint32_t hasNormals = FromLittleEndian32(*((uint32_t*) (geomData+20))); geomData += 6*4; // We ignore the setting of GEOMETRY_FLAG_POSITIONS. There are some meshes in GTA 3 where this flag // is not set but which have vertex positions nonetheless. The engine seems to ignore the flag. size_t size = vertexCount*3; vertices = new float[size]; memcpy(vertices, geomData, size*4); #ifndef GTAFORMATS_LITTLE_ENDIAN for (size_t i = 0 ; i < size ; i++) { vertices[i] = SwapEndiannessF32(vertices[i]); } #endif geomData += size*4; if ((flags & GEOMETRY_FLAG_NORMALS) != 0) { size = vertexCount*3; normals = new float[size]; memcpy(normals, geomData, size*4); #ifndef GTAFORMATS_LITTLE_ENDIAN for (size_t i = 0 ; i < size ; i++) { normals[i] = SwapEndiannessF32(normals[i]); } #endif geomData += size*4; } DFFGeometry* geom = new DFFGeometry(vertexCount, vertices, normals, uvCoords, uvSetCount, vertexColors); geom->setFlags(flags); geom->setFrameCount(frameCount); geom->setBounds(bounds); if (geomStruct->getVersion() < RW_VERSION_GTAVC_2) { geom->setAmbientLight(ambientColor); geom->setDiffuseLight(diffuseColor); geom->setSpecularLight(specularColor); } // ********** LOAD THE MATERIALS ********** RWSection* materialList = geomSect->getChild(RW_SECTION_MATERIALLIST); RWSection* materialListStruct = materialList->getChild(RW_SECTION_STRUCT); uint32_t numMaterials = FromLittleEndian32(*((uint32_t*) materialListStruct->getData())); RWSection::ChildIterator matIt = materialList->getChildBegin(); while ((matIt = materialList->nextChild(RW_SECTION_MATERIAL, matIt)) != materialList->getChildEnd()) { RWSection* matSect = *matIt; RWSection* matStruct = matSect->getChild(RW_SECTION_STRUCT); uint8_t* matData = matStruct->getData(); DFFMaterial* mat = new DFFMaterial; // uint32_t unknown = *((uint32_t*) matData); memcpy(mat->color, matData+4, 4); // uint32_t unknown = *((uint32_t*) (matData+8)); uint32_t texCount = FromLittleEndian32(*((uint32_t*) (matData+12))); // Load the textures RWSection::ChildIterator texIt = matSect->getChildBegin(); while ((texIt = matSect->nextChild(RW_SECTION_TEXTURE, texIt)) != matSect->getChildEnd()) { RWSection* texSect = *texIt; RWSection* texStruct = texSect->getChild(RW_SECTION_STRUCT); uint16_t filterFlags = FromLittleEndian16(*((uint16_t*) texStruct->getData())); RWSection::ChildIterator it = texSect->getChildBegin(); it = texSect->nextChild(RW_SECTION_STRING, it); RWSection* diffNameSect = *it; char* diffuseName = new char[diffNameSect->getSize()]; memcpy(diffuseName, diffNameSect->getData(), diffNameSect->getSize()); it++; it = texSect->nextChild(RW_SECTION_STRING, it); RWSection* alphaNameSect = *it; char* alphaName = new char[alphaNameSect->getSize()]; memcpy(alphaName, alphaNameSect->getData(), alphaNameSect->getSize()); DFFTexture* tex = new DFFTexture(CString::from(diffuseName), CString::from(alphaName), filterFlags); mat->addTexture(tex); texIt++; } geom->addMaterial(mat); matIt++; } RWSection* geomExt = geomSect->getChild(RW_SECTION_EXTENSION); // ********** Load the material split data ********** RWSection* materialSplit = geomExt->getChild(RW_SECTION_MATERIALSPLIT); uint8_t* msData = materialSplit->getData(); // uint32_t faceType = FromLittleEndian32(*((uint32_t*) msData)); uint32_t splitCount = FromLittleEndian32(*((uint32_t*) (msData+4))); // uint32_t numFaces = FromLittleEndian32(*((uint32_t*) (msData+8))); msData += 12; for (uint32_t j = 0 ; j < splitCount ; j++) { uint32_t idxCount = FromLittleEndian32(*((uint32_t*) msData)); //printf("Split %u: %u indices", j, idxCount); uint32_t materialIdx = FromLittleEndian32(*((uint32_t*) (msData+4))); msData += 8; uint32_t* indices = new uint32_t[idxCount]; memcpy(indices, msData, idxCount*4); #ifndef GTAFORMATS_LITTLE_ENDIAN for (size_t k = 0 ; k < idxCount ; k++) { indices[k] = SwapEndianness32(indices[k]); } #endif msData += idxCount*4; DFFGeometryPart* part = new DFFGeometryPart(idxCount, indices); geom->addPart(part); part->setMaterial(geom->getMaterial(materialIdx)); } // ********** Load the vertex skinning data ********** RWSection* skin = geomExt->getChild(RW_SECTION_SKIN_PLG); if (skin) { uint8_t* sData = skin->getData(); uint8_t boneCount = *sData; uint8_t spCount = *(sData + 1); //uint8_t unknown1 = *(sData + 2); //uint8_t unknown2 = *(sData + 3); sData += 4; sData += spCount; // spCount times unknown3 geom->boneIndices = new uint8_t[vertexCount*4]; memcpy(geom->boneIndices, sData, vertexCount*4); sData += vertexCount*4; geom->boneWeights = new float[vertexCount*4]; memcpy(geom->boneWeights, sData, vertexCount*4*sizeof(float)); sData += vertexCount*4*sizeof(float); #ifndef GTAFORMATS_LITTLE_ENDIAN for (size_t j = 0 ; j < vertexCount*4 ; j++) { geom->boneWeights[j] = SwapEndiannessF32(geom->boneWeights[j]); } #endif // Inverse Bone Matrices and possibly (version dependent) some unknown data follows... geom->boneCount = boneCount; //geom->inverseBoneMatrices = new Matrix4*[boneCount]; for (uint8_t i = 0 ; i < boneCount ; i++) { if (skin->getVersion() != RW_VERSION_GTASA) { sData += 4; // constDEAD } Matrix4 ibm; memcpy(&ibm, sData, 16*sizeof(float)); sData += 16*sizeof(float); const float* adata = ibm.toArray(); ibm = Matrix4 ( adata[0], adata[1], adata[2], 0.0f, adata[4], adata[5], adata[6], 0.0f, adata[8], adata[9], adata[10], 0.0f, adata[12], adata[13], adata[14], 1.0f ); DFFBone* bone = bonesByNum[i]; bone->ibm = ibm; /*const float* a = geom->inverseBoneMatrices[i]->toArray(); printf("Loading IBM #%u:\n%f\t%f\t%f\t%f\n%f\t%f\t%f\t%f\n%f\t%f\t%f\t%f\n%f\t%f\t%f\t%f\n\n", i, a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15]);*/ } } mesh->addGeometry(geom); geomIt++; } // ********************************************************************** // * LOAD THE GEOMETRY <-> FRAME LINKS * // ********************************************************************** RWSection::ChildIterator atomicIt = clump->getChildBegin(); while ((atomicIt = clump->nextChild(RW_SECTION_ATOMIC, atomicIt)) != clump->getChildEnd()) { RWSection* atomic = *atomicIt; RWSection* atomicStruct = atomic->getChild(RW_SECTION_STRUCT); uint8_t* asData = atomicStruct->getData(); uint32_t frameIdx = FromLittleEndian32(*((uint32_t*) asData)); uint32_t geomIdx = FromLittleEndian32(*((uint32_t*) (asData+4))); mesh->getGeometry(geomIdx)->setAssociatedFrame(indexedFrames[frameIdx].frame); atomicIt++; } mesh->integratedCOLModel = NULL; atomicIt = clump->getChildBegin(); while (!mesh->integratedCOLModel && (atomicIt = clump->nextChild(RW_SECTION_EXTENSION, atomicIt)) != clump->getChildEnd()) { RWSection* ext = *atomicIt; RWSection* colSect = ext->getChild(RW_SECTION_COLLISION_MODEL); if (colSect) { uint8_t* data = colSect->getData(); // TODO This is ridiculous. std::string makes a copy of the data, which is absolutely not needed... std::string tmp((char*) data, colSect->getSize()); std::stringstream in(tmp); COLLoader col; COLModel* model = col.loadModel(&in); if (model) { mesh->integratedCOLModel = model; } } atomicIt++; } // Delete the flat frame structure. delete[] indexedFrames; if (boneFrames) delete[] boneFrames; return mesh; }