diff --git a/framework2/CMakeLists.txt b/framework2/CMakeLists.txt index b5ce486e..6c845c49 100644 --- a/framework2/CMakeLists.txt +++ b/framework2/CMakeLists.txt @@ -1,3 +1,20 @@ -add_library(renderware BinaryStream.cpp TextureArchive.cpp ) +add_library(renderware + + # RenderWare related + BinaryStream.cpp + TextureArchive.cpp + LoaderDFF.cpp + TextureLoader.cpp + + # Game data related + LoaderCOL.cpp + LoaderIMG.cpp + LoaderIPL.cpp + GTAData.cpp + + GTAEngine.cpp +) + +target_link_libraries(renderware sfml-window) include_directories(include) diff --git a/framework2/GTAData.cpp b/framework2/GTAData.cpp new file mode 100644 index 00000000..c14694cb --- /dev/null +++ b/framework2/GTAData.cpp @@ -0,0 +1,150 @@ +#include "engine/GTAData.h" +#include "loaders/LoaderIPL.h" +#include +#include +#include +#include + +GTAData::GTAData(const std::string& path) +: datpath(path) +{ + +} + +void GTAData::load() +{ + std::ifstream datfile((datpath+"/data/gta3.dat").c_str()); + + if(!datfile.is_open()) + { + std::cerr << "Failed to open gta.dat" << std::endl; + } + else + { + for(std::string line, cmd; std::getline(datfile, line);) + { + if(line.size() == 0 || line[0] == '#') continue; + line.erase(line.size()-1); + + size_t space = line.find_first_of(' '); + if(space != line.npos) + { + cmd = line.substr(0, space); + if(cmd == "IDE") + { + loadIDE(line.substr(space+1)); + } + else if(cmd == "SPLASH") + { + splash = line.substr(space+1); + } + else if(cmd == "COLFILE") + { + int zone = atoi(line.substr(space+1,1).c_str()); + std::string file = line.substr(space+3); + loadCOL(zone, file); + } + else if(cmd == "IPL") + { + loadIPL(line.substr(space+1)); + } + } + } + } +} + +void GTAData::loadIDE(const std::string& name) +{ + std::cout << "IDE File " << name << std::endl; +} + +void GTAData::loadCOL(const size_t zone, const std::string& name) +{ + std::cout << "COL File " << name << " for zone " << zone << std::endl; +} + +void GTAData::loadIMG(const std::string& name) +{ + LoaderIMG imgLoader; + std::string archivePath = datpath + name; + + if (imgLoader.load(archivePath)) { + for (int i = 0; i < imgLoader.getAssetCount(); i++) { + auto &asset = imgLoader.getAssetInfoByIndex(i); + + std::string filename = asset.name; + + if(asset.size == 0) + { + std::cerr << "Asset " << filename << " has no size, ignoring." << std::endl; + } + else + { + // Load TXDs immediatley + auto filetype = filename.substr(filename.size() - 3); + for(size_t t = 0; t < filetype.size(); ++t) + { + filetype[t] = tolower(filetype[t]); + } + if (filetype == "txd") + { + char *file = imgLoader.loadToMemory(filename); + if(file) { + textureLoader.loadFromMemory(file); + } + } + else if(filetype == "dff") + { + std::string modelname = filename.substr(0, filename.size() - 4); + char *file = imgLoader.loadToMemory(filename); + if(file) + { + LoaderDFF dffLoader; + models[modelname] = std::move(dffLoader.loadFromMemory(file)); + delete[] file; + } + } + else + { + fileLocations.insert({ filename, { true, archivePath }}); + } + } + } + std::cout << "Archive loaded" << std::endl; + archives.insert({name, imgLoader}); + std::cout << "Archive copied" << std::endl; + } +} + +char* GTAData::loadFile(const std::string& name) +{ + auto i = fileLocations.find(name); + if(i != fileLocations.end()) + { + if(i->second.archived) + { + // Find the archive + auto ai = archives.find(i->second.path); + if(ai != archives.end()) + { + return ai->second.loadToMemory(name); + } + } + else + { + std::cerr << "Cannot load unarchived files yet" << std::endl; + } + } + + return nullptr; +} + +void GTAData::loadIPL(const std::string& name) +{ + std::string lowername = name; + for(size_t t = 0; t < lowername.size(); ++t) + { + lowername[t] = tolower(lowername[t]); + } + iplLocations.insert({lowername, datpath + "/" + lowername}); +} diff --git a/framework2/GTAEngine.cpp b/framework2/GTAEngine.cpp new file mode 100644 index 00000000..36e051df --- /dev/null +++ b/framework2/GTAEngine.cpp @@ -0,0 +1,46 @@ +#include "engine/GTAEngine.h" +#include "loaders/LoaderIPL.h" + +GTAEngine::GTAEngine(const std::string& path) +: gameData(path) +{ + +} + +bool GTAEngine::load() +{ + gameData.load(); + + return true; +} + +bool GTAEngine::loadItems(const std::string& name) +{ + auto i = gameData.iplLocations.find(name); + std::string path = name; + + if(i != gameData.iplLocations.end()) + { + path = i->second; + } + else + { + std::cout << "IPL not pre-listed" << std::endl; + } + + LoaderIPL ipll; + + if(ipll.load(path)) + { + instances.insert(instances.end(), ipll.m_instances.begin(), ipll.m_instances.end()); + itemCentroid += ipll.centroid; + return true; + } + else + { + std::cerr << "Failed to load IPL: " << path << std::endl; + return false; + } + + return false; +} \ No newline at end of file diff --git a/framework2/LoaderCOL.cpp b/framework2/LoaderCOL.cpp new file mode 100644 index 00000000..74679214 --- /dev/null +++ b/framework2/LoaderCOL.cpp @@ -0,0 +1,84 @@ +#include "loaders/LoaderCOL.h" +#include +#include + +template T readType(char* data, size_t* offset) +{ + size_t orgoff = *offset; *offset += sizeof(T); + return *reinterpret_cast(data+orgoff); +} + +bool LoaderCOL::load(const std::string& file) +{ + std::ifstream dfile(file.c_str()); + if ( ! dfile.is_open()) { + return false; + } + + dfile.seekg(0, std::ios_base::end); + size_t length = dfile.tellg(); + dfile.seekg(0); + char* data = new char[length]; + dfile.read(data, length); + + char* dataptr = data; + int version = 1; + std::string verstr(dataptr, 4); + if(verstr == "COLL") + { + } + else if(verstr == "COL2") + { + version = 2; + } + else if(verstr == "COL3") + { + version = 3; + } + + size_t dataI = 4; + + auto filesize = readType(data, &dataI); + CollTHeader head = readType(data, &dataI); + CollTHeaderV2 head2; + CollTHeaderV3 head3; + if(version >= 2) + { + head2 = readType(data, &dataI); + if(version >= 3) + { + head3 = readType(data, &dataI); + } + } + + if(version == 1) + { + head2.numspheres = readType(data, &dataI); + head2.offsetspheres = dataI-4; + } + // Read spheres + dataI += sizeof(CollTSphere) * head2.numspheres; + + if(version == 1) + { + // skip unused bytes + dataI += sizeof(uint32_t); + } + + if(version == 1) + { + head2.numboxes = readType(data, &dataI); + head2.offsetboxes = dataI-4; + } + dataI += sizeof(CollTBox) * head2.numboxes; + + if(version == 1) + { + uint32_t numverts = readType(data, &dataI); + dataI += sizeof(CollTVertex) * numverts; + } + + + + return true; +} diff --git a/framework2/LoaderDFF.cpp b/framework2/LoaderDFF.cpp new file mode 100644 index 00000000..1bfa7172 --- /dev/null +++ b/framework2/LoaderDFF.cpp @@ -0,0 +1,232 @@ +#include "loaders/LoaderDFF.h" + +#include + +std::unique_ptr LoaderDFF::loadFromMemory(char *data) +{ + auto model = std::unique_ptr(new Model); + RW::BinaryStreamSection root(data); + + model->clump = root.readStructure(); + + size_t dataI = 0; + while (root.hasMoreData(dataI)) { + auto sec = root.getNextChildSection(dataI); + + switch (sec.header.id) { + case RW::SID_GeometryList: { + auto list = sec.readStructure(); + size_t gdataI = 0; + while (sec.hasMoreData(gdataI)) { + Model::Geometry geometryStruct; + auto item = sec.getNextChildSection(gdataI); + + if (item.header.id == RW::SID_Geometry) { + size_t dataI = 0, secI = 0; + auto geometry = item.readStructure(); + // std::cout << " verts(" << geometry.numverts << ") tris(" << geometry.numtris << ")" << std::endl; + + item.getNextChildSection(secI); + char *data = item.raw() + sizeof(RW::BSSectionHeader) + sizeof(RW::BSGeometry); + + if (item.header.versionid < 0x1003FFFF) + auto colors = readStructure(data, dataI); + + if ((geometry.flags & RW::BSGeometry::VertexColors) == RW::BSGeometry::VertexColors) { + for (size_t v = 0; v < geometry.numverts; ++v) { + readStructure(data, dataI); + } + } + + /** TEX COORDS **/ + if ((geometry.flags & RW::BSGeometry::TexCoords1) == RW::BSGeometry::TexCoords1 || + (geometry.flags & RW::BSGeometry::TexCoords2) == RW::BSGeometry::TexCoords1) { + for (size_t v = 0; v < geometry.numverts; ++v) { + geometryStruct.texcoords.push_back(readStructure(data, dataI)); + } + } + + /** INDICIES **/ + for (int j = 0; j < geometry.numtris; ++j) { + geometryStruct.triangles.push_back(readStructure(data, dataI)); + } + + /** GEOMETRY BOUNDS **/ + geometryStruct.geometryBounds = readStructure(data, dataI); + + /** VERTICES **/ + for (int v = 0; v < geometry.numverts; ++v) { + geometryStruct.vertices.push_back(readStructure(data, dataI)); + } + + /** NORMALS **/ + if ((geometry.flags & RW::BSGeometry::StoreNormals) == RW::BSGeometry::StoreNormals) { + for (int n = 0; n < geometry.numverts; ++n) { + geometryStruct.normals.push_back(readStructure(data, dataI)); + } + } + + /** TEXTURES **/ + auto materiallistsec = item.getNextChildSection(secI); + auto materialList = materiallistsec.readStructure(); + + // Skip over the per-material byte values that I don't know what do. + dataI += sizeof(uint32_t) * materialList.nummaterials; + + size_t matI = 0; + materiallistsec.getNextChildSection(matI); + + geometryStruct.materials.resize(materialList.nummaterials); + + for (size_t m = 0; m < materialList.nummaterials; ++m) { + auto materialsec = materiallistsec.getNextChildSection(matI); + if (materialsec.header.id != RW::SID_Material) + continue; + + auto material = materialsec.readStructure(); + geometryStruct.materials[m].textures.resize(material.numtextures); + + size_t texI = 0; + materialsec.getNextChildSection(texI); + + for (size_t t = 0; t < material.numtextures; ++t) { + auto texsec = materialsec.getNextChildSection(texI); + auto texture = texsec.readStructure(); + + std::string textureName, alphaName; + size_t yetAnotherI = 0; + texsec.getNextChildSection(yetAnotherI); + + auto namesec = texsec.getNextChildSection(yetAnotherI); + auto alphasec = texsec.getNextChildSection(yetAnotherI); + + // The data is null terminated anyway. + textureName = namesec.raw(); + alphaName = alphasec.raw(); + + geometryStruct.materials[m].textures[t] = {textureName, alphaName}; + } + } + + if(item.hasMoreData(secI)) + { + auto extensions = item.getNextChildSection(secI); + size_t extI = 0; + while(extensions.hasMoreData(extI)) + { + auto extsec = extensions.getNextChildSection(extI); + if(extsec.header.id == RW::SID_BinMeshPLG) + { + auto meshplg = extsec.readSubStructure(0); + geometryStruct.subgeom.resize(meshplg.numsplits); + size_t meshplgI = sizeof(RW::BSBinMeshPLG); + for(size_t i = 0; i < meshplg.numsplits; ++i) + { + auto plgHeader = extsec.readSubStructure(meshplgI); + meshplgI += sizeof(RW::BSMaterialSplit); + geometryStruct.subgeom[i].material = plgHeader.index; + geometryStruct.subgeom[i].indices.resize(plgHeader.numverts); + for (int j = 0; j < plgHeader.numverts; ++j) { + geometryStruct.subgeom[i].indices[j] = extsec.readSubStructure(meshplgI); + meshplgI += sizeof(uint32_t); + } + } + } + } + } + + // OpenGL buffer stuff + glGenBuffers(1, &geometryStruct.VBO); + glGenBuffers(1, &geometryStruct.EBO); + for(size_t i = 0; i < geometryStruct.subgeom.size(); ++i) + { + glGenBuffers(1, &(geometryStruct.subgeom[i].EBO)); + } + + size_t buffsize = (geometryStruct.vertices.size() * sizeof(float) * 3) + + (geometryStruct.texcoords.size() * sizeof(float) * 2) + + (geometryStruct.normals.size() * sizeof(float) * 3); + + // Vertices + glBindBuffer(GL_ARRAY_BUFFER, geometryStruct.VBO); + glBufferData(GL_ARRAY_BUFFER, buffsize, NULL, GL_STATIC_DRAW); + + glBufferSubData( + GL_ARRAY_BUFFER, + 0, + (geometryStruct.vertices.size() * sizeof(float) * 3), + &geometryStruct.vertices[0] + ); + + if(geometryStruct.texcoords.size() > 0) + { + glBufferSubData( + GL_ARRAY_BUFFER, + (geometryStruct.vertices.size() * sizeof(float) * 3), + (geometryStruct.texcoords.size() * sizeof(float) * 2), + &geometryStruct.texcoords[0] + ); + } + + if(geometryStruct.normals.size() > 0 ) + { + glBufferSubData( + GL_ARRAY_BUFFER, + (geometryStruct.vertices.size() * sizeof(float) * 3) + (geometryStruct.texcoords.size() * sizeof(float) * 2), + geometryStruct.normals.size() * 3 * sizeof(float), + &geometryStruct.normals[0] + ); + } + + // Elements + uint16_t indicies[geometryStruct.triangles.size() * 3]; + size_t i = 0; + for (auto &tri : geometryStruct.triangles) { + indicies[i] = tri.first; + indicies[i + 1] = tri.second; + indicies[i + 2] = tri.third; + i += 3; + } + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, geometryStruct.EBO); + glBufferData( + GL_ELEMENT_ARRAY_BUFFER, + sizeof(indicies), + indicies, + GL_STATIC_DRAW + ); + + for(size_t i = 0; i < geometryStruct.subgeom.size(); ++i) + { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, geometryStruct.subgeom[i].EBO); + glBufferData( + GL_ELEMENT_ARRAY_BUFFER, + sizeof(uint32_t) * geometryStruct.subgeom[i].indices.size(), + &(geometryStruct.subgeom[i].indices[0]), + GL_STATIC_DRAW + ); + } + + // Add it + model->geometries.push_back(geometryStruct); + + + } + } + } + } + } + + return model; +} + +template T LoaderDFF::readStructure(char *data, size_t &dataI) +{ + size_t originalOffset = dataI; + dataI += sizeof(T); + return *reinterpret_cast(data + originalOffset); +} + +RW::BSSectionHeader LoaderDFF::readHeader(char *data, size_t &dataI) +{ + return readStructure(data, dataI); +} diff --git a/framework2/LoaderIMG.cpp b/framework2/LoaderIMG.cpp new file mode 100644 index 00000000..74152b8b --- /dev/null +++ b/framework2/LoaderIMG.cpp @@ -0,0 +1,121 @@ +#include "loaders/LoaderIMG.h" +#include + +LoaderIMG::LoaderIMG() +: m_version(GTAIIIVC) +, m_assetCount(0) +{ + +} + +bool LoaderIMG::load(const std::string& filename) +{ + std::string dirName = filename; + dirName.append(".dir"); + + FILE* fp = fopen(dirName.c_str(), "rb"); + if(fp) + { + fseek(fp, 0, SEEK_END); + unsigned long fileSize = ftell(fp); + fseek(fp, 0, SEEK_SET); + + m_assets.resize(fileSize / 32); + + m_assetCount = fileSize / 32; + + fread(&m_assets[0], sizeof(LoaderIMGFile), fileSize / 32, fp); + + fclose(fp); + m_archive = filename; + return true; + } + else + return false; +} + +/// Get the information of a asset in the examining archive +LoaderIMGFile &LoaderIMG::getAssetInfo(const std::string& assetname) +{ + for(size_t i = 0; i < m_assets.size(); ++i) + { + if(strcmp(m_assets[i].name, assetname.c_str()) == 0) + { + return m_assets[i]; + } + } +} + +char* LoaderIMG::loadToMemory(const std::string& assetname) +{ + LoaderIMGFile assetInfo; + bool found = false; + for(size_t i = 0; i < m_assets.size(); ++i) + { + if(strcmp(m_assets[i].name, assetname.c_str()) == 0) + { + assetInfo = m_assets[i]; + found = true; + } + } + + if(!found) + { + std::cerr << "Asset '" << assetname << "' not found!" << std::endl; + return 0; + } + + std::string dirName = m_archive; + dirName.append(".img"); + + FILE* fp = fopen(dirName.c_str(), "rb"); + if(fp) + { + char* raw_data = new char[assetInfo.size * 2048]; + + fseek(fp, assetInfo.offset * 2048, SEEK_SET); + fread(raw_data, 2048, assetInfo.size, fp); + + fclose(fp); + return raw_data; + } + else + return 0; +} + +/// Writes the contents of assetname to filename +bool LoaderIMG::saveAsset(const std::string& assetname, const std::string& filename) +{ + char* raw_data = loadToMemory(assetname); + if(!raw_data) + return false; + + FILE* dumpFile = fopen(filename.c_str(), "wb"); + if(dumpFile) + { + fwrite(raw_data, getAssetInfo(assetname).size * 2048, 1, dumpFile); + fclose(dumpFile); + + printf("=> IMG: Saved %s to disk with filename %s\n", assetname.c_str(), filename.c_str()); + + delete[] raw_data; + return true; + } + else + { + delete[] raw_data; + return false; + } +} + +/// Get the information of an asset by its index +LoaderIMGFile &LoaderIMG::getAssetInfoByIndex(size_t index) +{ + return m_assets[index]; +} + + +uint32_t LoaderIMG::getAssetCount() +{ + return m_assetCount; +} diff --git a/framework2/LoaderIPL.cpp b/framework2/LoaderIPL.cpp new file mode 100644 index 00000000..2b1156f0 --- /dev/null +++ b/framework2/LoaderIPL.cpp @@ -0,0 +1,117 @@ +#include "loaders/LoaderIPL.h" + +#include +#include +#include +#include + +enum SectionTypes +{ + INST, + PICK, + CULL, + ZONE, + NONE +}; + +/// Load the IPL data into memory +bool LoaderIPL::load(const std::string& filename) +{ + std::ifstream str(filename); + + if(!str.is_open()) + return false; + + SectionTypes section = NONE; + while(!str.eof()) + { + std::string line; + getline(str, line); + line.erase(std::find_if(line.rbegin(), line.rend(), std::not1(std::ptr_fun(std::isspace))).base(), line.end()); + + if(!line.empty() && line[0] == '#') + { + // nothing, just a comment + } + else if(line == "end") // terminating a section + { + section = NONE; + } + else if(section == NONE) // starting a new section + { + if(line == "inst") + { + section = INST; + } + if(line == "pick") + { + section = PICK; + } + if(line == "cull") + { + section = CULL; + } + if(line == "zone") + { + section = ZONE; + } + } + else // regular entry + { + if(section == INST) + { + LoaderIPLInstance instance; + + std::string id; + std::string model; + std::string posX, posY, posZ; + std::string scaleX, scaleY, scaleZ; + std::string rotX, rotY, rotZ, rotW; + + std::stringstream strstream(line); + + // read all the contents of the line + getline(strstream, id, ','); + getline(strstream, model, ','); + getline(strstream, posX, ','); + getline(strstream, posY, ','); + getline(strstream, posZ, ','); + getline(strstream, scaleX, ','); + getline(strstream, scaleY, ','); + getline(strstream, scaleZ, ','); + getline(strstream, rotX, ','); + getline(strstream, rotY, ','); + getline(strstream, rotZ, ','); + getline(strstream, rotW, ','); + + // convert to our structure + instance.id = atoi(id.c_str()); + instance.model = model.substr(1, model.size()-1); + instance.posX = atof(posX.c_str()); + instance.posY = atof(posY.c_str()); + instance.posZ = atof(posZ.c_str()); + instance.scaleX = atof(scaleX.c_str()); + instance.scaleY = atof(scaleY.c_str()); + instance.scaleZ = atof(scaleZ.c_str()); + instance.rotX = atof(rotX.c_str()); + instance.rotY = atof(rotY.c_str()); + instance.rotZ = atof(rotZ.c_str()); + instance.rotW = atof(rotW.c_str()); + + centroid += glm::vec3(instance.posX, instance.posY, instance.posZ); + + /*std::cout << "id: " << instance.id << std::endl; + std::cout << "model: " << instance.model << std::endl; + std::cout << "posX: " << instance.posX << std::endl; + std::cout << "posY: " << instance.posY << std::endl; + std::cout << "posZ: " << instance.posZ << std::endl; + std::cout << "rotW: " << instance.rotW << std::endl;*/ + + m_instances.push_back(instance); + } + } + + } + + return true; +} \ No newline at end of file diff --git a/framework2/TextureLoader.cpp b/framework2/TextureLoader.cpp new file mode 100644 index 00000000..d19658eb --- /dev/null +++ b/framework2/TextureLoader.cpp @@ -0,0 +1,145 @@ +#include "loaders/TextureLoader.h" + +#include +#include + +bool TextureLoader::loadFromFile(std::string filename) +{ + std::ifstream dfile(filename); + if ( ! dfile.is_open()) { + std::cerr << "Error opening file " << filename << std::endl; + return false; + } + + dfile.seekg(0, std::ios_base::end); + size_t length = dfile.tellg(); + dfile.seekg(0); + char *data = new char[length]; + dfile.read(data, length); + + return loadFromMemory(data); +} + +GLuint gErrorTextureData[] = { 0x00FF00FF, 0x00FFFFFF, 0x00FFFFFF, 0x00FF00FF }; + +bool TextureLoader::loadFromMemory(char *data) +{ + RW::BinaryStreamSection root(data); + auto texDict = root.readStructure(); + + size_t rootI = 0; + while (root.hasMoreData(rootI)) { + auto rootSection = root.getNextChildSection(rootI); + + if (rootSection.header.id != RW::SID_TextureNative) + continue; + + auto texNative = rootSection.readStructure(); + + GLuint texture = 0; + + if(texNative.platform != 8) + { + std::cerr << "Unsupported texture platform " << std::dec << texNative.platform << std::endl; + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + glTexImage2D( + GL_TEXTURE_2D, 0, GL_RGBA, + 2, 2, 0, + GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, gErrorTextureData + ); + } + else if((texNative.rasterformat & RW::BSTextureNative::FORMAT_EXT_PAL8) == RW::BSTextureNative::FORMAT_EXT_PAL8) + { + auto palette = rootSection.readSubStructure(sizeof(RW::BSSectionHeader)+sizeof(RW::BSTextureNative) - 4); + uint8_t* coldata = reinterpret_cast(rootSection.raw() + sizeof(RW::BSSectionHeader) + sizeof(RW::BSTextureNative) + sizeof(RW::BSPaletteData) - 4); + uint8_t fullColor[texNative.width * texNative.height * 4]; + + bool hasAlpha = texNative.alpha == 1; + + for(size_t j = 0; j < texNative.width * texNative.height; ++j) + { + size_t iTex = j * 4; + size_t iPal = coldata[j] * 4; + fullColor[iTex+0] = palette.palette[iPal+0]; + fullColor[iTex+1] = palette.palette[iPal+1]; + fullColor[iTex+2] = palette.palette[iPal+2]; + fullColor[iTex+3] = hasAlpha ? palette.palette[iPal+3] : 255; + } + + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + glTexImage2D( + GL_TEXTURE_2D, 0, GL_RGBA, + texNative.width, texNative.height, 0, + GL_RGBA, GL_UNSIGNED_BYTE, fullColor + ); + } + else if( + texNative.rasterformat == RW::BSTextureNative::FORMAT_1555 || + texNative.rasterformat == RW::BSTextureNative::FORMAT_8888 || + texNative.rasterformat == RW::BSTextureNative::FORMAT_888 + ) + { + auto coldata = rootSection.raw() + sizeof(RW::BSTextureNative); + uint32_t rastersize = *coldata; + coldata += sizeof(uint32_t); + + GLenum type, format; + switch(texNative.rasterformat) + { + case RW::BSTextureNative::FORMAT_1555: + format = GL_RGBA; + type = GL_UNSIGNED_SHORT_1_5_5_5_REV; + break; + case RW::BSTextureNative::FORMAT_8888: + format = GL_BGRA; + type = GL_UNSIGNED_INT_8_8_8_8_REV; + break; + case RW::BSTextureNative::FORMAT_888: + format = GL_BGR; + type = GL_UNSIGNED_BYTE; + break; + } + + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + glTexImage2D( + GL_TEXTURE_2D, 0, GL_RGBA, + texNative.width, texNative.height, 0, + format, type, coldata + ); + } + else + { + std::cerr << "Unsuported raster format " << std::hex << texNative.rasterformat << std::endl; + } + + if(texture != 0) + { + // todo: not completely ignore everything the TXD says. + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR); + + glGenerateMipmap(GL_TEXTURE_2D); + + std::string name = std::string(texNative.diffuseName); + + textures[name] = texture; + } + + // std::cout << "Loaded texture '" << name << "'" << std::endl; + } + + return true; +} + +void TextureLoader::bindTexture(std::string texture) +{ + if (textures.find(texture) == textures.end()) { + // std::cerr << "Could not find nor bind texture '" << texture << "'" << std::endl; + return; + } + + glBindTexture(GL_TEXTURE_2D, textures[texture]); +} diff --git a/framework2/include/engine/GTAData.h b/framework2/include/engine/GTAData.h new file mode 100644 index 00000000..1d4482c8 --- /dev/null +++ b/framework2/include/engine/GTAData.h @@ -0,0 +1,99 @@ +#ifndef _GTADATA_H_ +#define _GTADATA_H_ +#include +#include +#include +#include +#include +#include + +/** + * Handles loading and management of the Game's DAT + */ +class GTAData +{ +private: + + std::string datpath; + std::string splash; + +public: + + /** + * @struct GTAFile + * Stores information about a file the engine might want to load + */ + struct GTAFile + { + bool archived; /// Is the file inside an IMG or on the filesystem? + std::string path; /// Path to the file containing the file + }; + + /** + * ctor + * @param path Path to the root of the game data. + */ + GTAData(const std::string& path); + + /** + * Returns the current platform + */ + std::string getPlatformString() + { + return "PC"; + } + + /** + * Loads the data contained in the given file + */ + void loadIDE(const std::string& name); + + /** + * Handles the parsing of a COL file. + */ + void loadCOL(const size_t zone, const std::string& name); + + /** + * Handles the loading of an IMG's data + */ + void loadIMG(const std::string& name); + + void loadIPL(const std::string& name); + + void load(); + + /** + * Returns a pointer to the named file if it is available, the memory must be freed. + * @param name the filename in the archive + * @return pointer to the data, NULL if it is not available + */ + char* loadFile(const std::string& name); + + /** + * Maps the paths in GTA3.dat to the real paths + */ + std::map iplLocations; + + /** + * Maps file names to their locations + */ + std::map fileLocations; + + /** + * Map of loaded archives + */ + std::map archives; + + /** + * Texture Loader + */ + TextureLoader textureLoader; + + /** + * Loaded models + */ + std::map> models; + +}; + +#endif \ No newline at end of file diff --git a/framework2/include/engine/GTAEngine.h b/framework2/include/engine/GTAEngine.h new file mode 100644 index 00000000..352960b5 --- /dev/null +++ b/framework2/include/engine/GTAEngine.h @@ -0,0 +1,45 @@ +#ifndef _GTAENGINE_H_ +#define _GTAENGINE_H_ +#include "GTAData.h" +#include +#include +#include + +/** + * @class GTAEngine + * Provides a simple interface to the framework's internals + */ +class GTAEngine +{ +public: + + GTAEngine(const std::string& gamepath); + + /** + * Loads the game data + */ + bool load(); + + /** + * Loads an IPL into the game. + * @param name The name of the IPL as it appears in the games' gta.dat + */ + bool loadItems(const std::string& name); + + /** + * Roughly the middle of everything + */ + glm::vec3 itemCentroid; + + /** + * Game data + */ + GTAData gameData; + + /** + * Until we have a real "object" class, just store a list of loaed instances. + */ + std::vector instances; +}; + +#endif \ No newline at end of file diff --git a/framework2/include/loaders/LoaderCOL.h b/framework2/include/loaders/LoaderCOL.h new file mode 100644 index 00000000..79c57538 --- /dev/null +++ b/framework2/include/loaders/LoaderCOL.h @@ -0,0 +1,121 @@ +#ifndef _LOADERCOL_H_ +#define _LOADERCOL_H_ +#include +#include +#include + +typedef glm::vec3 CollTVec3; + +struct CollTBounds +{ + CollTVec3 min, max; + CollTVec3 center; + float radius; +}; + +struct CollTBoundsV1 +{ + float radius; + CollTVec3 center; + CollTVec3 min, max; +}; + +struct CollTSurface +{ + uint8_t material; + uint8_t flag; + uint8_t brightness; + uint8_t light; +}; + +struct CollTSphere +{ + CollTVec3 center; + float radius; + CollTSurface surface; +}; + +struct CollTSphereV1 +{ + float radius; + CollTVec3 center; + CollTSurface surface; +}; + +struct CollTBox +{ + CollTVec3 min, max; + CollTSurface surface; +}; + +struct CollTFaceGroup +{ + CollTVec3 min, max; + uint16_t startface, endface; +}; + +typedef glm::vec3 CollTVertex; + +struct CollTFace +{ + uint16_t a, b, c; + uint8_t material; + uint8_t light; +}; + +struct CollTFaceV1 +{ + uint32_t a, b, c; + CollTSurface surface; +}; + +struct CollTHeader +{ + char name[22]; + uint16_t modelid; + CollTBounds bounds; +}; + +struct CollTHeaderV2 +{ + uint16_t numspheres; + uint16_t numboxes; + uint32_t numfaces; + uint32_t flags; + uint32_t offsetspheres; + uint32_t offsetboxes; + uint32_t offsetlines; + uint32_t offsetverts; + uint32_t offsetfaces; +}; + +struct CollTHeaderV3 +{ + uint32_t numshadowfaces; + uint32_t offsetverts; + uint32_t offsetfaces; +}; + +/** + * @class CollisionInstance + * Stores data about a collision proxy + */ +class CollisionInstance +{ + +}; + +/** + * @class LoaderCOL + * Loads collision data from COL files + */ +class LoaderCOL +{ +public: + /// Load the COL data into memory + bool load(const std::string& file); + + std::vector instances; +}; + +#endif \ No newline at end of file diff --git a/framework2/include/loaders/LoaderDFF.h b/framework2/include/loaders/LoaderDFF.h new file mode 100644 index 00000000..a21fbbca --- /dev/null +++ b/framework2/include/loaders/LoaderDFF.h @@ -0,0 +1,57 @@ +#pragma once + +#define GLEW_STATIC +#include + +#include "../../framework/rwbinarystream.h" + +#include +#include +#include + +class Model +{ +public: + RW::BSClump clump; + + struct Texture { + std::string name; + std::string alphaName; + }; + + struct Material { + std::vector textures; + }; + + struct SubGeometry { + GLuint EBO; + size_t material; + std::vector indices; + }; + + struct Geometry { + GLuint VBO, EBO; + + RW::BSGeometryBounds geometryBounds; + + std::vector texcoords; + std::vector triangles; + std::vector vertices; + std::vector normals; + + std::vector materials; + std::vector subgeom; + }; + + std::vector geometries; +}; + +class LoaderDFF +{ +private: + template T readStructure(char *data, size_t &dataI); + RW::BSSectionHeader readHeader(char *data, size_t &dataI); + +public: + std::unique_ptr loadFromMemory(char *data); +}; diff --git a/framework2/include/loaders/LoaderIMG.h b/framework2/include/loaders/LoaderIMG.h new file mode 100644 index 00000000..512c9a94 --- /dev/null +++ b/framework2/include/loaders/LoaderIMG.h @@ -0,0 +1,65 @@ +#ifndef LoaderIMG_h__ +#define LoaderIMG_h__ + +#include +#include +#include + +/// \brief Points to one file within the archive +class LoaderIMGFile +{ +public: + uint32_t offset; + uint32_t size; + char name[24]; +}; + +/** + \class LoaderIMG + \brief Parses the structure of GTA .IMG archives and loads the files in it +*/ +class LoaderIMG +{ +public: + /// Multiple versions of .IMG files + enum Versions + { + GTAIIIVC, ///< GTA III and GTA VC archives -- only this one is implemented + GTASA, + GTAIV + }; + + /// Construct + LoaderIMG(); + + /// Load the structure of the archive + /// Omit the extension in filename so both .dir and .img are loaded when appropriate + bool load(const std::string& filename); + + /// Load a file from the archive to memory and pass a pointer to it + /// Warning: Please delete[] the memory in the end. + /// Warning: Returns NULL (0) if by any reason it can't load the file + char* loadToMemory(const std::string& assetname); + + /// Writes the contents of assetname to filename + bool saveAsset(const std::string& assetname, const std::string& filename); + + /// Get the information of an asset in the examining archive + LoaderIMGFile &getAssetInfo(const std::string& assetname); + + /// Get the information of an asset by its index + LoaderIMGFile &getAssetInfoByIndex(size_t index); + + /// Returns the number of asset files in the archive + uint32_t getAssetCount(); + +private: + Versions m_version; ///< Version of this IMG archive + uint32_t m_assetCount; ///< Number of assets in the current archive + std::string m_archive; ///< Path to the archive being used (no extension) + + std::vector m_assets; ///< Asset info of the archive +}; + + +#endif // LoaderIMG_h__ diff --git a/framework2/include/loaders/LoaderIPL.h b/framework2/include/loaders/LoaderIPL.h new file mode 100644 index 00000000..11b99400 --- /dev/null +++ b/framework2/include/loaders/LoaderIPL.h @@ -0,0 +1,39 @@ +#ifndef LoaderIPL_h__ +#define LoaderIPL_h__ + +#include +#include +#include + +/** + \class LoaderIPLInstance + \brief One INST entry's data from a IPL file +*/ +class LoaderIPLInstance +{ +public: + int id; ///< ID of the asset in the main IMG archive + std::string model; ///< Name of the model for this instance, as seen in the IMG archive + float posX, posY, posZ; ///< 3D Position of the instance + float scaleX, scaleY, scaleZ; ///< Scale of the instance + float rotX, rotY, rotZ, rotW; ///< Rotation of the instance, in a Quaternion +}; + +/** + \class LoaderIPL + \brief Loads all data from a IPL file into memory +*/ +class LoaderIPL +{ +public: + /// Load the IPL data into memory + bool load(const std::string& filename); + + /// The list of instances from the IPL file + std::vector m_instances; + + /// The centroid of the instances + glm::vec3 centroid; +}; + +#endif // LoaderIPL_h__ diff --git a/framework2/include/loaders/TextureLoader.h b/framework2/include/loaders/TextureLoader.h new file mode 100644 index 00000000..b52d9103 --- /dev/null +++ b/framework2/include/loaders/TextureLoader.h @@ -0,0 +1,19 @@ +#pragma once + +#define GLEW_STATIC +#include + +#include "../../framework/rwbinarystream.h" + +#include +#include + +class TextureLoader +{ +public: + bool loadFromFile(std::string filename); + bool loadFromMemory(char *data); + void bindTexture(std::string texture); + + std::map textures; +}; diff --git a/viewer/CMakeLists.txt b/viewer/CMakeLists.txt index 5b4fde91..c8a9a3c6 100644 --- a/viewer/CMakeLists.txt +++ b/viewer/CMakeLists.txt @@ -1,5 +1,7 @@ -add_executable(viewer main.cpp ../framework/LoaderIPL.cpp ../framework/LoaderIMG.cpp ../framework/LoaderDFF.cpp ../framework/TextureLoader.cpp) +add_executable(viewer main.cpp ../framework/LoaderDFF.cpp) -target_link_libraries( viewer sfml-graphics sfml-window sfml-system GL GLEW ) +include_directories(../framework2/include) + +target_link_libraries( viewer renderware sfml-graphics sfml-window sfml-system GL GLEW ) install(TARGETS viewer RUNTIME DESTINATION bin) diff --git a/viewer/main.cpp b/viewer/main.cpp index 8bf8e393..a2e5aa3b 100644 --- a/viewer/main.cpp +++ b/viewer/main.cpp @@ -1,10 +1,8 @@ #define GLEW_STATIC #include -#include "../framework/LoaderIPL.h" -#include "../framework/LoaderIMG.h" -#include "../framework/LoaderDFF.h" -#include "../framework/TextureLoader.h" +#include +#include #include #include @@ -47,11 +45,8 @@ GLuint uniModel, uniProj, uniView; GLuint posAttrib, texAttrib; LoaderDFF dffLoader; -TextureLoader textureLoader; -LoaderIPL iplLoader; +GTAEngine* gta = nullptr; -std::map> models; -Model *selectedModel; glm::vec3 selectedModelCenter; glm::vec3 plyPos; @@ -116,63 +111,22 @@ void init(std::string gtapath) glm::mat4 proj = glm::perspective(80.f, (float) WIDTH/HEIGHT, 0.1f, 5000.f); glUniformMatrix4fv(uniProj, 1, GL_FALSE, glm::value_ptr(proj)); - - LoaderIMG imgLoader; - - if (imgLoader.load(gtapath +"/models/gta3")) { - for (int i = 0; i < imgLoader.getAssetCount(); i++) { - auto &asset = imgLoader.getAssetInfoByIndex(i); - - std::string filename = asset.name; - - if(asset.size == 0) - { - std::cerr << "Asset: " << filename << " has no size" << std::endl; - continue; - } - - auto filetype = filename.substr(filename.size() - 3); - std::transform(filetype.begin(), filetype.end(), filetype.begin(), ::tolower); - if (filetype == "dff") { - std::string modelname = filename.substr(0, filename.size() - 4); - char *file = imgLoader.loadToMemory(filename); - if(file) - { - models[modelname] = std::move(dffLoader.loadFromMemory(file)); - delete[] file; - } - } else if (filetype == "txd") { - char *file = imgLoader.loadToMemory(filename); - if(file) - { - textureLoader.loadFromMemory(file); - delete[] file; - } - } - } - } - - if (iplLoader.load(gtapath +"/data/maps/industSW.ipl")) { - printf("IPL Loaded, size: %d\n", iplLoader.m_instances.size()); - - // Get the center of the model by averaging all the vertices! Hax! - for (int i = 0; i < iplLoader.m_instances.size(); i++) { - selectedModelCenter += glm::vec3{ - iplLoader.m_instances[i].posX, - iplLoader.m_instances[i].posY, - iplLoader.m_instances[i].posZ - }; - } - selectedModelCenter /= iplLoader.m_instances.size(); - plyPos = selectedModelCenter; - } else { - printf("IPL failed to load.\n"); - exit(1); - } - - //textureLoader.loadFromFile("MISC.TXD"); - - selectedModel = models["Jetty"].get(); + + // GTA GET + gta = new GTAEngine(gtapath); + + // This is harcoded in GTA III for some reason + gta->gameData.loadIMG("/models/gta3"); + + gta->load(); + + // Test out a known IPL. + gta->loadItems(gtapath + "/data/maps/industsw/industSW.ipl"); + //gta->loadItems(gtapath + "/data/maps/industnw/industNW.ipl"); + //gta->loadItems(gtapath + "/data/maps/industse/industSE.ipl"); + //gta->loadItems(gtapath + "/data/maps/industne/industNE.ipl"); + + plyPos = gta->itemCentroid / (float) gta->instances.size(); } void update() @@ -233,14 +187,21 @@ void update() void render() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + auto& textureLoader = gta->gameData.textureLoader; - for (size_t i = 0; i < iplLoader.m_instances.size(); ++i) { - auto &obj = iplLoader.m_instances[i]; + for (size_t i = 0; i < gta->instances.size(); ++i) { + auto &obj = gta->instances[i]; std::string modelname = obj.model; if (modelname.substr(0, 3) == "LOD") continue; - auto &model = models[modelname]; + auto &model = gta->gameData.models[modelname]; // std::cout << "Rendering " << modelname << std::endl; + + if(!model) + { + std::cout << "model " << modelname << " not there (" << gta->gameData.models.size() << " models loaded)" << std::endl; + } for (size_t g = 0; g < model->geometries.size(); g++) {