From 8b7bdf6376eda94812a6987cf8440fb3aae10645 Mon Sep 17 00:00:00 2001 From: antopilo Date: Mon, 21 Apr 2025 11:37:56 -0400 Subject: [PATCH] Added quake BSP importer module --- Nuake/Source/Nuake/Modules/Modules.cpp | 5 + .../Nuake/Modules/QuakeModule/Module.lua | 11 + .../Modules/QuakeModule/QuakeBSPReader.h | 412 ++++++++++++++++++ .../Nuake/Modules/QuakeModule/QuakeBaker.cpp | 230 ++++++++++ .../Nuake/Modules/QuakeModule/QuakeBaker.h | 18 + .../Nuake/Modules/QuakeModule/QuakeModule.cpp | 18 + .../Nuake/Modules/QuakeModule/QuakeModule.h | 4 + 7 files changed, 698 insertions(+) create mode 100644 Nuake/Source/Nuake/Modules/QuakeModule/Module.lua create mode 100644 Nuake/Source/Nuake/Modules/QuakeModule/QuakeBSPReader.h create mode 100644 Nuake/Source/Nuake/Modules/QuakeModule/QuakeBaker.cpp create mode 100644 Nuake/Source/Nuake/Modules/QuakeModule/QuakeBaker.h create mode 100644 Nuake/Source/Nuake/Modules/QuakeModule/QuakeModule.cpp create mode 100644 Nuake/Source/Nuake/Modules/QuakeModule/QuakeModule.h diff --git a/Nuake/Source/Nuake/Modules/Modules.cpp b/Nuake/Source/Nuake/Modules/Modules.cpp index 8e9d7f0b..b0901fd4 100644 --- a/Nuake/Source/Nuake/Modules/Modules.cpp +++ b/Nuake/Source/Nuake/Modules/Modules.cpp @@ -4,6 +4,7 @@ #include "AssimpModule/AssimpModule.h" #include "AudioModule/AudioModule.h" #include "ExampleModule/ExampleModule.h" +#include "QuakeModule/QuakeModule.h" #include "Nuake/Core/Logger.h" @@ -15,6 +16,8 @@ void Nuake::Modules::StartupModules() AudioModule_Startup(); Logger::Log("Starting ExampleModule", "modules"); ExampleModule_Startup(); + Logger::Log("Starting QuakeModule", "modules"); + QuakeModule_Startup(); } void Nuake::Modules::ShutdownModules() @@ -25,4 +28,6 @@ void Nuake::Modules::ShutdownModules() AudioModule_Shutdown(); Logger::Log("Shutting down ExampleModule", "modules"); ExampleModule_Shutdown(); + Logger::Log("Shutting down QuakeModule", "modules"); + QuakeModule_Shutdown(); } diff --git a/Nuake/Source/Nuake/Modules/QuakeModule/Module.lua b/Nuake/Source/Nuake/Modules/QuakeModule/Module.lua new file mode 100644 index 00000000..d995f9e3 --- /dev/null +++ b/Nuake/Source/Nuake/Modules/QuakeModule/Module.lua @@ -0,0 +1,11 @@ +return { + name = "Quake BSP Module", + description = "Quake asset baker module", + module_header = "QuakeModule.h", + -- Place all your sources here + sources = { + "QuakeModule.cpp", + "QuakBaker.h", + "QuakBaker.cpp", + } +} \ No newline at end of file diff --git a/Nuake/Source/Nuake/Modules/QuakeModule/QuakeBSPReader.h b/Nuake/Source/Nuake/Modules/QuakeModule/QuakeBSPReader.h new file mode 100644 index 00000000..cf4eca44 --- /dev/null +++ b/Nuake/Source/Nuake/Modules/QuakeModule/QuakeBSPReader.h @@ -0,0 +1,412 @@ +#pragma once +#include +#include +#include + +namespace BSPParser +{ + unsigned char QuakePalette[768] = + { + // marked: colormap colors: cb = (colormap & 0xF0);cb += (cb >= 128 && cb < 224) ? 4 : 12; + // 0x0* + 0,0,0, 15,15,15, 31,31,31, 47,47,47, 63,63,63, 75,75,75, 91,91,91, 107,107,107, + 123,123,123, 139,139,139, 155,155,155, 171,171,171, 187,187,187, 203,203,203, 219,219,219, 235,235,235, + // 0x1* 0 ^ + 15,11,7, 23,15,11, 31,23,11, 39,27,15, 47,35,19, 55,43,23, 63,47,23, 75,55,27, + 83,59,27, 91,67,31, 99,75,31, 107,83,31, 115,87,31, 123,95,35, 131,103,35, 143,111,35, + // 0x2* 1 ^ + 11,11,15, 19,19,27, 27,27,39, 39,39,51, 47,47,63, 55,55,75, 63,63,87, 71,71,103, + 79,79,115, 91,91,127, 99,99,139, 107,107,151, 115,115,163, 123,123,175, 131,131,187, 139,139,203, + // 0x3* 2 ^ + 0,0,0, 7,7,0, 11,11,0, 19,19,0, 27,27,0, 35,35,0, 43,43,7, 47,47,7, + 55,55,7, 63,63,7, 71,71,7, 75,75,11, 83,83,11, 91,91,11, 99,99,11, 107,107,15, + // 0x4* 3 ^ + 7,0,0, 15,0,0, 23,0,0, 31,0,0, 39,0,0, 47,0,0, 55,0,0, 63,0,0, + 71,0,0, 79,0,0, 87,0,0, 95,0,0, 103,0,0, 111,0,0, 119,0,0, 127,0,0, + // 0x5* 4 ^ + 19,19,0, 27,27,0, 35,35,0, 47,43,0, 55,47,0, 67,55,0, 75,59,7, 87,67,7, + 95,71,7, 107,75,11, 119,83,15, 131,87,19, 139,91,19, 151,95,27, 163,99,31, 175,103,35, + // 0x6* 5 ^ + 35,19,7, 47,23,11, 59,31,15, 75,35,19, 87,43,23, 99,47,31, 115,55,35, 127,59,43, + 143,67,51, 159,79,51, 175,99,47, 191,119,47, 207,143,43, 223,171,39, 239,203,31, 255,243,27, + // 0x7* 6 ^ + 11,7,0, 27,19,0, 43,35,15, 55,43,19, 71,51,27, 83,55,35, 99,63,43, 111,71,51, + 127,83,63, 139,95,71, 155,107,83, 167,123,95, 183,135,107, 195,147,123, 211,163,139, 227,179,151, + // 0x8* 7 ^ v 8 + 171,139,163, 159,127,151, 147,115,135, 139,103,123, 127,91,111, 119,83,99, 107,75,87, 95,63,75, + 87,55,67, 75,47,55, 67,39,47, 55,31,35, 43,23,27, 35,19,19, 23,11,11, 15,7,7, + // 0x9* 9 v + 187,115,159, 175,107,143, 163,95,131, 151,87,119, 139,79,107, 127,75,95, 115,67,83, 107,59,75, + 95,51,63, 83,43,55, 71,35,43, 59,31,35, 47,23,27, 35,19,19, 23,11,11, 15,7,7, + // 0xA* 10 v + 219,195,187, 203,179,167, 191,163,155, 175,151,139, 163,135,123, 151,123,111, 135,111,95, 123,99,83, + 107,87,71, 95,75,59, 83,63,51, 67,51,39, 55,43,31, 39,31,23, 27,19,15, 15,11,7, + // 0xB* 11 v + 111,131,123, 103,123,111, 95,115,103, 87,107,95, 79,99,87, 71,91,79, 63,83,71, 55,75,63, + 47,67,55, 43,59,47, 35,51,39, 31,43,31, 23,35,23, 15,27,19, 11,19,11, 7,11,7, + // 0xC* 12 v + 255,243,27, 239,223,23, 219,203,19, 203,183,15, 187,167,15, 171,151,11, 155,131,7, 139,115,7, + 123,99,7, 107,83,0, 91,71,0, 75,55,0, 59,43,0, 43,31,0, 27,15,0, 11,7,0, + // 0xD* 13 v + 0,0,255, 11,11,239, 19,19,223, 27,27,207, 35,35,191, 43,43,175, 47,47,159, 47,47,143, + 47,47,127, 47,47,111, 47,47,95, 43,43,79, 35,35,63, 27,27,47, 19,19,31, 11,11,15, + // 0xE* + 43,0,0, 59,0,0, 75,7,0, 95,7,0, 111,15,0, 127,23,7, 147,31,7, 163,39,11, + 183,51,15, 195,75,27, 207,99,43, 219,127,59, 227,151,79, 231,171,95, 239,191,119, 247,211,139, + // 0xF* 14 ^ + 167,123,59, 183,155,55, 199,195,55, 231,227,87, 127,191,255, 171,231,255, 215,255,255, 103,0,0, + 139,0,0, 179,0,0, 215,0,0, 255,0,0, 255,243,147, 255,247,199, 255,255,255, 159,91,83 + }; // + // Structs declaration + typedef struct // A Directory entry + { + long offset; // Offset to entry, in bytes, from start of file + long size; // Size of entry in file, in bytes + } dentry_t; + + typedef struct // The BSP file header + { + long version; // Model version, must be 0x17 (23). + dentry_t entities; // List of Entities. + dentry_t planes; // Map Planes. + // numplanes = size/sizeof(plane_t) + dentry_t miptex; // Wall Textures. + dentry_t vertices; // Map Vertices. + // numvertices = size/sizeof(vertex_t) + dentry_t visilist; // Leaves Visibility lists. + dentry_t nodes; // BSP Nodes. + // numnodes = size/sizeof(node_t) + dentry_t texinfo; // Texture Info for faces. + // numtexinfo = size/sizeof(texinfo_t) + dentry_t faces; // Faces of each surface. + // numfaces = size/sizeof(face_t) + dentry_t lightmaps; // Wall Light Maps. + dentry_t clipnodes; // clip nodes, for Models. + // numclips = size/sizeof(clipnode_t) + dentry_t leaves; // BSP Leaves. + // numlaves = size/sizeof(leaf_t) + dentry_t lface; // List of Faces. + dentry_t edges; // Edges of faces. + // numedges = Size/sizeof(edge_t) + dentry_t ledges; // List of Edges. + dentry_t models; // List of Models. + // nummodels = Size/sizeof(model_t) + } dheader_t; + + typedef float scalar_t; // Scalar value, + + typedef struct // Vector or Position + { + scalar_t x; // horizontal + scalar_t y; // horizontal + scalar_t z; // vertical + } vec3_t; + + typedef struct // Bounding Box, Float values + { + vec3_t min; // minimum values of X,Y,Z + vec3_t max; // maximum values of X,Y,Z + } boundbox_t; + + typedef struct // Bounding Box, Short values + { + short min; // minimum values of X,Y,Z + short max; // maximum values of X,Y,Z + } bboxshort_t; + + typedef struct + { + float X; // X,Y,Z coordinates of the vertex + float Y; // usually some integer value + float Z; // but coded in floating point + } vertex_t; + + typedef struct + { + unsigned short vertex0; // index of the start vertex + // must be in [0,numvertices[ + unsigned short vertex1; // index of the end vertex + // must be in [0,numvertices[ + } edge_t; + + typedef struct + { + vec3_t vectorS; // S vector, horizontal in texture space) + scalar_t distS; // horizontal offset in texture space + vec3_t vectorT; // T vector, vertical in texture space + scalar_t distT; // vertical offset in texture space + unsigned long texture_id; // Index of Mip Texture + // must be in [0,numtex[ + unsigned long animated; // 0 for ordinary textures, 1 for water + } surface_t; + + typedef struct + { + unsigned short plane_id; // The plane in which the face lies + // must be in [0,numplanes[ + unsigned short side; // 0 if in front of the plane, 1 if behind the plane + long ledge_id; // first edge in the List of edges + // must be in [0,numledges[ + unsigned short ledge_num; // number of edges in the List of edges + unsigned short texinfo_id; // index of the Texture info the face is part of + // must be in [0,numtexinfos[ + unsigned char typelight; // type of lighting, for the face + unsigned char baselight; // from 0xFF (dark) to 0 (bright) + unsigned char light[2]; // two additional light models + long lightmap; // Pointer inside the general light map, or -1 + // this define the start of the face light map + } face_t; + + typedef struct + { + vec3_t normal; // Vector orthogonal to plane (Nx,Ny,Nz) + // with Nx2+Ny2+Nz2 = 1 + scalar_t dist; // Offset to plane, along the normal vector. + // Distance from (0,0,0) to the plane + long type; // Type of plane, depending on normal vector. + } plane_t; + + typedef struct // Mip texture list header + { + long numtex; // Number of textures in Mip Texture list + long* offset; // Offset to each of the individual texture + } mipheader_t; // from the beginning of mipheader_t + + typedef struct // Mip Texture + { + char name[16]; // Name of the texture. + unsigned long width; // width of picture, must be a multiple of 8 + unsigned long height; // height of picture, must be a multiple of 8 + unsigned long offset1; // offset to u_char Pix[width * height] + unsigned long offset2; // offset to u_char Pix[width/2 * height/2] + unsigned long offset4; // offset to u_char Pix[width/4 * height/4] + unsigned long offset8; // offset to u_char Pix[width/8 * height/8] + } miptex_t; + + typedef struct + { + boundbox_t bound; // The bounding box of the Model + vec3_t origin; // origin of model, usually (0,0,0) + long node_id0; // index of first BSP node + long node_id1; // index of the first Clip node + long node_id2; // index of the second Clip node + long node_id3; // usually zero + long numleafs; // number of BSP leaves + long face_id; // index of Faces + long face_num; // number of Faces + } model_t; + + struct vec2 + { + float x; + float y; + }; + + struct MipTextureData + { + std::string name; + vec2 size; + std::vector data; + }; + + // Parsed output, access these after calling Parse(). + std::vector Planes; + std::vector Vertices; + std::vector MipTextures; + std::vector MipTexturesData; + std::vector Faces; + std::vector Edges; + std::vector LEdges; + std::vector Models; + std::vector TextureInfos; + + // Private data + std::ifstream bspFile; + bool _headerParsed = false; + dheader_t _header; + + int LoadFile(const std::string& path) + { + bspFile.open(path, std::ios::in | std::ios::binary); + + if (!bspFile.is_open()) + { + return -1; + } + + return 1; + } + + void UnloadFile() + { + bspFile.close(); + } + + template + void Read(T& data) + { + bspFile.read((char*)&data, sizeof(T)); + } + + template + void Read(T& data, size_t size) + { + bspFile.read((char*)&data, sizeof(T) * size); + } + + template + void Read(T* data, size_t count) + { + bspFile.read(reinterpret_cast(data), sizeof(T) * count); + } + + inline void Seek(long offset) + { + bspFile.seekg(offset); + } + + void ParseHeader() + { + _headerParsed = true; + Read(_header); + } + + void ParsePlanes() + { + if (!_headerParsed) ParseHeader(); + Seek(_header.planes.offset); + uint32_t planeAmount = _header.planes.size / sizeof(plane_t); + Planes = std::vector(planeAmount); + Read(Planes[0], planeAmount); + } + + void ParseTextureInfos() + { + if (!_headerParsed) ParseHeader(); + Seek(_header.texinfo.offset); + uint32_t textureInfoAmount = _header.texinfo.size / sizeof(plane_t); + TextureInfos = std::vector(textureInfoAmount); + bspFile.read((char*)TextureInfos.data(), _header.texinfo.size); + } + + void ParseVertices() + { + if (!_headerParsed) ParseHeader(); + Seek(_header.vertices.offset); + const uint32_t vertexAmount = _header.vertices.size / sizeof(vertex_t); + Vertices = std::vector(vertexAmount); + bspFile.read((char*)Vertices.data(), _header.vertices.size); + } + + std::vector GetTextureData(const std::string& textureName) + { + auto foundIt = std::find_if(MipTexturesData.begin(), MipTexturesData.end(), [&textureName](const MipTextureData& tex) + { + return tex.name == textureName; + }); + + if (foundIt == MipTexturesData.end()) + { + assert(false && "texture not found in wad"); + } + + return foundIt->data; + } + + void ParseMips() + { + if (!_headerParsed) ParseHeader(); + Seek(_header.miptex.offset); + long mipTextureAmount; + Read(mipTextureAmount); + + // offsets are from mipTextureHeader, not from start of file. + std::vector mipTextureOffsets(mipTextureAmount); + Read(mipTextureOffsets[0], mipTextureAmount); + + MipTextures = std::vector(mipTextureAmount); + + for (uint32_t i = 0; i < mipTextureAmount; i++) + { + bspFile.seekg(_header.miptex.offset + mipTextureOffsets[i]); + + Read(MipTextures[i]); + + const size_t size = MipTextures[i].width * MipTextures[i].height; + std::vector rgbaData(size); +#define MAKE_RGBA(r, g, b, a) ((uint32_t)(((a) << 24) | ((b) << 16) | ((g) << 8) | (r))) +#define MAKE_RGB(r, g, b) MAKE_RGBA(r, g, b, 255) + + const std::vector indexedPixels = std::vector(size); + + // Read buffer + bspFile.seekg(_header.miptex.offset + mipTextureOffsets[i] + MipTextures[i].offset1); + bspFile.read((char*)indexedPixels.data(), size); + + for (int y = 0; y < MipTextures[i].height; y++) + { + for (int x = 0; x < MipTextures[i].width; x++) + { + unsigned char index = indexedPixels[y * MipTextures[i].width + x]; + unsigned char r = QuakePalette[index * 3 + 0]; + unsigned char g = QuakePalette[index * 3 + 1]; + unsigned char b = QuakePalette[index * 3 + 2]; + unsigned char a = (index == 255) ? 0 : 255; // Index 255 = transparent + + rgbaData[y * MipTextures[i].width + x] = MAKE_RGBA(r, g, b, a); + } + } + + MipTexturesData.push_back({ MipTextures[i].name, { (float)MipTextures[i].width, (float)MipTextures[i].height }, rgbaData }); + } + } + + void ParseFaces() + { + if (!_headerParsed) ParseHeader(); + Seek(_header.faces.offset); + uint32_t faceAmount = _header.faces.size / sizeof(face_t); + Faces = std::vector(faceAmount); + Read(Faces[0], faceAmount); + } + + void ParseEdges() + { + if (!_headerParsed) ParseHeader(); + Seek(_header.edges.offset); + uint32_t edgesAmount = _header.edges.size / sizeof(edge_t); + Edges = std::vector(edgesAmount); + Read(Edges[0], edgesAmount); + } + + void ParseLedges() + { + if (!_headerParsed) ParseHeader(); + Seek(_header.ledges.offset); + LEdges = std::vector(_header.ledges.size / sizeof(short)); + bspFile.read((char*)LEdges.data(), _header.ledges.size); + } + + void ParseModels() + { + if (!_headerParsed) ParseHeader(); + + Seek(_header.models.offset); + uint32_t modelAmount = _header.models.size / sizeof(model_t); + Models = std::vector(modelAmount); + bspFile.read((char*)Models.data(), _header.models.size); + } + + void Parse() + { + ParseHeader(); + ParsePlanes(); + ParseTextureInfos(); + ParseFaces(); + ParseEdges(); + ParseLedges(); + ParseModels(); + ParseVertices(); + ParseMips(); + } +} \ No newline at end of file diff --git a/Nuake/Source/Nuake/Modules/QuakeModule/QuakeBaker.cpp b/Nuake/Source/Nuake/Modules/QuakeModule/QuakeBaker.cpp new file mode 100644 index 00000000..2faa3821 --- /dev/null +++ b/Nuake/Source/Nuake/Modules/QuakeModule/QuakeBaker.cpp @@ -0,0 +1,230 @@ +#include "QuakeBaker.h" + +#include "QuakeBSPReader.h" +#include +#include +#include +#include +#include + +#include +#include + +using namespace Nuake; + +static const char* ExpandFileName(const char* lump_name, bool fullbright) +{ + int max_len = strlen(lump_name) + 32; + + char* result = (char*)calloc(max_len + 1, 1); (max_len); + + // convert any special first character + if (lump_name[0] == '*') + { + strcpy(result, "star_"); + } + else if (lump_name[0] == '+') + { + strcpy(result, "plus_"); + } + else if (lump_name[0] == '-') + { + strcpy(result, "minu_"); + } + else if (lump_name[0] == '/') + { + strcpy(result, "divd_"); + } + else if (lump_name[0] == '{') + { + strcpy(result, "bra_"); + } + else + { + result[0] = lump_name[0]; + result[1] = 0; + } + + strcat(result, lump_name + 1); + + // sanitize filename (remove problematic characters) + bool warned = false; + + for (char* p = result; *p; p++) + { + if (*p == ' ') + *p = '_'; + + if (*p != '_' && *p != '-' && !isalnum(*p)) + { + if (!warned) + { + printf("WARNING: removing weird characters from name (\\%03o)\n", + (unsigned char)*p); + warned = true; + } + + *p = '_'; + } + } + + //if (fullbright) + // strcat(result, "_fbr"); + + strcat(result, ".png"); + + return result; +} + +Ref QuakeBaker::Bake(const Ref& file) +{ + if (!BSPParser::LoadFile(file->GetAbsolutePath())) + { + return file; + } + + BSPParser::Parse(); + + std::map textureFileMap; + + std::map> textures; + for (auto& tex : BSPParser::MipTextures) + { + const std::string& textureName = tex.name; + auto data = BSPParser::GetTextureData(textureName); + + const std::string& fileName = std::string(ExpandFileName(textureName.c_str(), false)); + auto finalPath = file->GetAbsolutePath() + "/../" + "/" + fileName; + stbi_write_png(finalPath.c_str(), tex.width, tex.height, 4, data.data(), tex.width * 4); + textureFileMap[textureName] = fileName; + //auto texture = CreateRef(reinterpret_cast(data.data()), ImageFormat::RGBA8, Vector2{tex.width, tex.height}); + //textures[textureName] = texture; + } + + Ref model = CreateRef(); + + // Copy vertices + uint32_t verticesNum = BSPParser::Vertices.size(); + std::vector vertices; + for (const auto& v : BSPParser::Vertices) + { + vertices.push_back({ + Vector3(-v.X, v.Z, v.Y), + 0.0f, + Vector3(v.X, v.Z, v.Y), + 0.0f, + }); + } + + std::map> verticesByTexture; + std::map> indicesByTexture; + + auto& m = BSPParser::Models[0]; + //for (const auto& m : BSPParser::Models) + { + for (int i = m.face_id; i < m.face_id + m.face_num; i++) + { + const auto& face = BSPParser::Faces[i]; + long firstEdge = face.ledge_id; + long lastEdge = firstEdge + face.ledge_num; + + auto textureInfo = BSPParser::TextureInfos[face.texinfo_id]; + const std::string& textureName = BSPParser::MipTextures[textureInfo.texture_id].name; + + std::vector faceIndices; + + for (long e = firstEdge; e < lastEdge; e++) + { + int32_t edgeId = BSPParser::LEdges[e]; + if (edgeId >= 0) + { + auto edge = BSPParser::Edges[edgeId]; + faceIndices.push_back(edge.vertex0); + } + else + { + auto edge = BSPParser::Edges[-edgeId]; + faceIndices.push_back(edge.vertex1); + } + } + + auto calculateUV = [&](Vector3 pos, BSPParser::surface_t surface) -> Vector2 + { + Vector3 vectorS = { -surface.vectorS.x, surface.vectorS.z, surface.vectorS.y }; + Vector3 vectorT = { -surface.vectorT.x, surface.vectorT.z, surface.vectorT.y }; + float s = glm::dot(pos, vectorS) + surface.distS; + float t = glm::dot(pos, vectorT) + surface.distT; + return { (s / BSPParser::MipTextures[surface.texture_id].width), 1.0 - (t / BSPParser::MipTextures[surface.texture_id].height) }; + }; + + if (faceIndices.size() >= 3) + { + auto& verts = verticesByTexture[textureName]; + auto& inds = indicesByTexture[textureName]; + + for (size_t j = 1; j < faceIndices.size() - 1; ++j) + { + size_t baseIndex = verts.size(); + + Vector3 p0 = vertices[faceIndices[0]].position * Vector3(1.0 / 64.0); + Vector3 p1 = vertices[faceIndices[j]].position * Vector3(1.0 / 64.0); + Vector3 p2 = vertices[faceIndices[j + 1]].position * Vector3(1.0 / 64.0); + + Vector2 uv0 = Vector2{ 1.0 , 1.0 } - calculateUV(vertices[faceIndices[0]].position, textureInfo); + Vector2 uv1 = Vector2{ 1.0 , 1.0 } - calculateUV(vertices[faceIndices[j]].position, textureInfo); + Vector2 uv2 = Vector2{ 1.0 , 1.0 } - calculateUV(vertices[faceIndices[j + 1]].position, textureInfo); + + Vector3 edge1 = p1 - p0; + Vector3 edge2 = p2 - p0; + Vector3 normal = -glm::normalize(glm::cross(edge1, edge2)); + + verts.push_back({ .position = p0, .uv_x = uv0.x, .normal = normal, .uv_y = uv0.y }); + verts.push_back({ .position = p1, .uv_x = uv1.x, .normal = normal, .uv_y = uv1.y}); + verts.push_back({ .position = p2, .uv_x = uv2.x, .normal = normal, .uv_y = uv2.y}); + + inds.push_back(baseIndex + 2); + inds.push_back(baseIndex + 1); + inds.push_back(baseIndex + 0); + } + } + } + } + + std::vector triangleMeshes; + for (auto& [textureName, verts] : verticesByTexture) + { + if (String::BeginsWith(textureName, "sky")) + { + continue; + } + + auto& inds = indicesByTexture[textureName]; + + Ref material = CreateRef(); + auto materialPath = FileSystem::GetParentPath(file->GetRelativePath()) + FileSystem::GetFileNameFromPath(textureFileMap[textureName]) + ".material"; + auto texturePath = FileSystem::GetParentPath(file->GetRelativePath()) + FileSystem::GetFileNameFromPath(textureFileMap[textureName]) + ".png"; + material->SetAlbedo(texturePath); + + ResourceManager::RegisterResource(material); + ResourceManager::Manifest.RegisterResource(material->ID, materialPath); + + std::string materialJson = material->Serialize().dump(4); + FileSystem::BeginWriteFile(materialPath); + FileSystem::WriteLine(materialJson); + FileSystem::EndWriteFile(); + + Ref mesh = CreateRef(); + mesh->SetData(verts, inds); + mesh->SetMaterial(material); + model->AddMesh(std::move(mesh)); + } + + BinarySerializer serializer; + + const std::string outputPath = file->GetAbsolutePath() + ".nkmesh"; + serializer.SerializeModel(outputPath, model); + + BSPParser::UnloadFile(); + + return file; +} diff --git a/Nuake/Source/Nuake/Modules/QuakeModule/QuakeBaker.h b/Nuake/Source/Nuake/Modules/QuakeModule/QuakeBaker.h new file mode 100644 index 00000000..b2ac64e0 --- /dev/null +++ b/Nuake/Source/Nuake/Modules/QuakeModule/QuakeBaker.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +namespace Nuake +{ + + class QuakeBaker : public IAssetBaker + { + private: + std::string currentPath; + + public: + QuakeBaker() : IAssetBaker({ ".bsp" }) {} + + Ref Bake(const Ref& file) override; + }; +} \ No newline at end of file diff --git a/Nuake/Source/Nuake/Modules/QuakeModule/QuakeModule.cpp b/Nuake/Source/Nuake/Modules/QuakeModule/QuakeModule.cpp new file mode 100644 index 00000000..e736d0dd --- /dev/null +++ b/Nuake/Source/Nuake/Modules/QuakeModule/QuakeModule.cpp @@ -0,0 +1,18 @@ +#include "QuakeModule.h" +#include "QuakeBaker.h" + +#include "Nuake/Resource/Bakers/AssetBakerManager.h" + + +void QuakeModule_Startup() +{ + using namespace Nuake; + + AssetBakerManager& assetBakerMgr = AssetBakerManager::Get(); + assetBakerMgr.RegisterBaker(CreateRef()); +} + +void QuakeModule_Shutdown() +{ + +} \ No newline at end of file diff --git a/Nuake/Source/Nuake/Modules/QuakeModule/QuakeModule.h b/Nuake/Source/Nuake/Modules/QuakeModule/QuakeModule.h new file mode 100644 index 00000000..79b844ba --- /dev/null +++ b/Nuake/Source/Nuake/Modules/QuakeModule/QuakeModule.h @@ -0,0 +1,4 @@ +#pragma once + +void QuakeModule_Startup(); +void QuakeModule_Shutdown(); \ No newline at end of file