diff --git a/CMakeLists.txt b/CMakeLists.txt index 3e6f989e..e1713c10 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,3 +3,4 @@ project(gtfw) SET(CMAKE_CXX_FLAGS "-std=c++11") add_subdirectory(datadump) +add_subdirectory(viewer) diff --git a/framework/LoaderDFF.cpp b/framework/LoaderDFF.cpp new file mode 100644 index 00000000..9173ef05 --- /dev/null +++ b/framework/LoaderDFF.cpp @@ -0,0 +1,126 @@ +#include "LoaderDFF.h" + +#include "../framework/rwbinarystream.h" + +#include + +void LoaderDFF::loadFromMemory(char *data) +{ + RW::BinaryStreamSection root(data); + + this->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)) { + 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) { + for (size_t v = 0; v < geometry.numverts; ++v) { + readStructure(data, dataI); + } + } + + /** TEX COORDS **/ + if (geometry.flags & RW::BSGeometry::TexCoords1 || geometry.flags & RW::BSGeometry::TexCoords2) { + 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) { + 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); + + 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(); + + 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.textures.push_back({textureName, alphaName}); + } + } + + // Add it + geometries.push_back(geometryStruct); + } + } + } + } + } +} + +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/framework/LoaderDFF.h b/framework/LoaderDFF.h new file mode 100644 index 00000000..baea3f42 --- /dev/null +++ b/framework/LoaderDFF.h @@ -0,0 +1,35 @@ +#pragma once + +#include "rwbinarystream.h" + +#include +#include + +class LoaderDFF +{ +private: + template T readStructure(char *data, size_t &dataI); + RW::BSSectionHeader readHeader(char *data, size_t &dataI); + + RW::BSClump clump; + +public: + void loadFromMemory(char *data); + + struct Texture { + std::string name; + std::string alphaName; + }; + struct Geometry { + RW::BSGeometryBounds geometryBounds; + + std::vector texcoords; + std::vector triangles; + std::vector vertices; + std::vector normals; + + std::vector textures; + }; + + std::vector geometries; +}; diff --git a/viewer/CMakeLists.txt b/viewer/CMakeLists.txt new file mode 100644 index 00000000..608f6f39 --- /dev/null +++ b/viewer/CMakeLists.txt @@ -0,0 +1,5 @@ +add_executable(viewer main.cpp ../framework/LoaderIPL.cpp ../framework/LoaderIMG.cpp ../framework/LoaderDFF.cpp) + +target_link_libraries( viewer sfml-graphics sfml-window sfml-system GL GLEW ) + +install(TARGETS viewer RUNTIME DESTINATION bin) diff --git a/viewer/main.cpp b/viewer/main.cpp new file mode 100644 index 00000000..b39baeff --- /dev/null +++ b/viewer/main.cpp @@ -0,0 +1,230 @@ +#define GLEW_STATIC +#include + +#include "../framework/LoaderIPL.h" +#include "../framework/LoaderIMG.h" +#include "../framework/LoaderDFF.h" + +#include +#include +#include + +#include + +constexpr int WIDTH = 800, + HEIGHT = 600; + +sf::Window window; + +const char *vertexShaderSource = "#version 130\n" +"in vec3 position;" +// "in vec2 texCoords;" +// "out vec2 TexCoords;" +"uniform mat4 model;" +"uniform mat4 view;" +"uniform mat4 proj;" +"void main()" +"{" +// " TexCoords = texCoords;" +" gl_Position = proj * model * vec4(position, 1.0);" +"}"; +const char *fragmentShaderSource = "#version 130\n" +// "in vec2 TexCoords;" +// "uniform sampler2D texture;" +"void main()" +"{" +" gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);" +// " gl_FragColor = texture2D(texture, TexCoords);" +"}"; + +GLuint compileShader(GLenum type, const char *source) +{ + GLuint shader = glCreateShader(type); + glShaderSource(shader, 1, &source, NULL); + glCompileShader(shader); + + GLint status; + glGetShaderiv(shader, GL_COMPILE_STATUS, &status); + if (status != GL_TRUE) { + GLint len; + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &len); + GLchar *buffer = new GLchar[len]; + glGetShaderInfoLog(shader, len, NULL, buffer); + + std::cerr << "ERROR compiling shader: " << buffer << std::endl; + delete[] buffer; + exit(1); + } + + return shader; +} + +void handleEvent(sf::Event &event) +{ + switch (event.type) { + case sf::Event::KeyPressed: + switch (event.key.code) { + case sf::Keyboard::Escape: + window.close(); + break; + default: break; + } + break; + default: break; + } +} + +GLuint uniModel, uniProj, uniView; +GLuint posAttrib; +GLuint VBO, EBO; + +LoaderDFF dffLoader; + +LoaderDFF::Geometry *selectedGeometry; + +void init(std::string filepath) +{ + glClearColor(0.2, 0.2, 0.2, 1.0); + // glEnable(GL_DEPTH_TEST); + + GLuint vertexShader = compileShader(GL_VERTEX_SHADER, vertexShaderSource); + GLuint fragmentShader = compileShader(GL_FRAGMENT_SHADER, fragmentShaderSource); + GLuint shaderProgram = glCreateProgram(); + glAttachShader(shaderProgram, vertexShader); + glAttachShader(shaderProgram, fragmentShader); + glLinkProgram(shaderProgram); + glUseProgram(shaderProgram); + + glGenBuffers(1, &VBO); + glGenBuffers(1, &EBO); + + posAttrib = glGetAttribLocation(shaderProgram, "position"); + GLuint texAttrib = glGetAttribLocation(shaderProgram, "texCoords"); + + uniModel = glGetUniformLocation(shaderProgram, "model"); + uniView = glGetUniformLocation(shaderProgram, "view"); + uniProj = glGetUniformLocation(shaderProgram, "proj"); + + LoaderIPL iplLoader; + LoaderIMG imgLoader; + + if (iplLoader.load(filepath)) { + printf("IPL Loaded, size: %d\n", iplLoader.m_instances.size()); + + if (imgLoader.load("/home/iostream/.wine/drive_c/Program Files (x86)/Rockstar Games/GTAIII/models/gta3")) { + std::string filename = iplLoader.m_instances[0].model + ".dff"; + std::cout << "Loading " << filename << std::endl; + char *file = imgLoader.loadToMemory(filename); + + dffLoader.loadFromMemory(file); + + selectedGeometry = &dffLoader.geometries[0]; +/* + for (int i = 0; i < 10; i++) { + auto v = selectedGeometry->vertices[i]; + std::cout << v.x << ", " << v.y << ", " << v.z << std::endl; + } +*/ + + glBindBuffer(GL_ARRAY_BUFFER, VBO); + glBufferData( + GL_ARRAY_BUFFER, + selectedGeometry->vertices.size() * 3 * sizeof(float), + &selectedGeometry->vertices[0], + GL_STATIC_DRAW + ); + + uint16_t indicies[selectedGeometry->triangles.size() * 3]; + size_t i = 0; + for (auto &tri : selectedGeometry->triangles) { + indicies[i] = tri.first; + indicies[i + 1] = tri.second; + indicies[i + 2] = tri.third; + i += 3; + } + /* + for (int i = 0; i < 8; i++) { + glm::vec3 t{indicies[i*3], indicies[i*3 + 1], indicies[i*3 + 2]}; + auto v1 = selectedGeometry->vertices[t.x]; + auto v2 = selectedGeometry->vertices[t.y]; + auto v3 = selectedGeometry->vertices[t.z]; + std::cout << t.x << ", " << t.y << ", " << t.z << std::endl; + std::cout << v1.x << ", " << v1.y << ", " << v1.z << std::endl; + std::cout << v2.x << ", " << v2.y << ", " << v2.z << std::endl; + std::cout << v3.x << ", " << v3.y << ", " << v3.z << std::endl; + std::cout << std::endl; + } + */ + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); + glBufferData( + GL_ELEMENT_ARRAY_BUFFER, + sizeof(indicies), + indicies, + GL_STATIC_DRAW + ); + + glm::mat4 proj = glm::perspective(80.f, (float) WIDTH/HEIGHT, 0.1f, 100.f); + glUniformMatrix4fv(uniProj, 1, GL_FALSE, glm::value_ptr(proj)); + } else { + std::cerr << "IMG failed to load" << std::endl; + exit(1); + } +/* + for (size_t i = 0; i < iplLoader.m_instances.size(); ++i) { + printf("IPL entry id: %d\n", iplLoader.m_instances[i].id); + } +*/ + } else { + printf("IPL failed to load.\n"); + } +} + +void update() +{ + static int i = 0; + + glm::mat4 model; + model = glm::translate(model, glm::vec3(0, 0, -10.0)); + model = glm::rotate(model, 100.f, glm::vec3(1, 0, 0)); + model = glm::rotate(model, i*1.f, glm::vec3(0, 0, 1)); + glUniformMatrix4fv(uniModel, 1, GL_FALSE, glm::value_ptr(model)); + + i++; +} + +void render() +{ + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glBindBuffer(GL_ARRAY_BUFFER, VBO); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); + glVertexAttribPointer(posAttrib, 3, GL_FLOAT, GL_FALSE, 0, 0); + glEnableVertexAttribArray(posAttrib); + + glDrawElements(GL_TRIANGLES, selectedGeometry->triangles.size() * 3, GL_UNSIGNED_SHORT, NULL); +} + +int main(int argc, char *argv[]) +{ + glewExperimental = GL_TRUE; + glewInit(); + + window.create(sf::VideoMode(WIDTH, HEIGHT), "GTA3 Viewer", sf::Style::Close); + window.setVerticalSyncEnabled(true); + + init(argv[1]); + + while (window.isOpen()) { + sf::Event event; + while (window.pollEvent(event)) { + handleEvent(event); + } + + update(); + + render(); + window.display(); + } + + return 0; +}