From cc0316ff80f8a412baad47528b50176977afcc31 Mon Sep 17 00:00:00 2001 From: "Grayson Riffe (Laptop)" Date: Fri, 10 Dec 2021 12:10:07 -0600 Subject: [PATCH] Started reworking the asset builder --- Game/Game.vcxproj | 4 +- .../NFAssetBuilder.rc | 10 +- .../NFAssetBuilder.vcxproj | 11 +- .../NFAssetBuilder.vcxproj.filters | 20 +- .../NFAssetBuilder.vcxproj.user | 3 +- .../dep/include/stb_image.h | 0 {NFPackCreator => NFAssetBuilder}/resource.h | 2 +- NFAssetBuilder/src/Models.cpp | 297 ++++++++++++++++++ NFAssetBuilder/src/Textures.cpp | 18 ++ NFAssetBuilder/src/Utility.cpp | 84 +++++ NFAssetBuilder/src/include/Models.h | 33 ++ NFAssetBuilder/src/include/Textures.h | 4 + NFAssetBuilder/src/include/Utility.h | 117 +++++++ NFAssetBuilder/src/main.cpp | 129 ++++++++ NFPackCreator/src/main.cpp | 195 ------------ NothinFancy/NothinFancy.vcxproj | 9 +- NothinFancy/src/Renderer/Model.cpp | 6 +- NothinFancy/src/Renderer/Texture.cpp | 10 +- STEMSln.sln | 2 +- docs/pages/2_tutorial.md | 2 +- 20 files changed, 735 insertions(+), 221 deletions(-) rename NFPackCreator/NFPackCreator.rc => NFAssetBuilder/NFAssetBuilder.rc (85%) rename NFPackCreator/NFPackCreator.vcxproj => NFAssetBuilder/NFAssetBuilder.vcxproj (87%) rename NFPackCreator/NFPackCreator.vcxproj.filters => NFAssetBuilder/NFAssetBuilder.vcxproj.filters (64%) rename NFPackCreator/NFPackCreator.vcxproj.user => NFAssetBuilder/NFAssetBuilder.vcxproj.user (88%) rename {NothinFancy => NFAssetBuilder}/dep/include/stb_image.h (100%) rename {NFPackCreator => NFAssetBuilder}/resource.h (92%) create mode 100644 NFAssetBuilder/src/Models.cpp create mode 100644 NFAssetBuilder/src/Textures.cpp create mode 100644 NFAssetBuilder/src/Utility.cpp create mode 100644 NFAssetBuilder/src/include/Models.h create mode 100644 NFAssetBuilder/src/include/Textures.h create mode 100644 NFAssetBuilder/src/include/Utility.h create mode 100644 NFAssetBuilder/src/main.cpp delete mode 100644 NFPackCreator/src/main.cpp diff --git a/Game/Game.vcxproj b/Game/Game.vcxproj index f2386a9..f59a23b 100644 --- a/Game/Game.vcxproj +++ b/Game/Game.vcxproj @@ -72,7 +72,7 @@ cd assets -"$(SolutionDir)NFPackCreator\bin\x64$(Configuration)\NFPackCreator.exe" +"$(SolutionDir)NFAssetBuilder\bin\x64$(Configuration)\NFAssetBuilder.exe" if exist "$(OutDir)assets" (rmdir "$(OutDir)assets" /S /Q && goto end) else goto end :end mkdir "$(OutDir)assets" @@ -105,7 +105,7 @@ if exist "base.nfpack" (copy "base.nfpack" "$(OutDir)assets\") cd assets -"$(SolutionDir)NFPackCreator\bin\x64$(Configuration)\NFPackCreator.exe" +"$(SolutionDir)NFAssetBuilder\bin\x64$(Configuration)\NFAssetBuilder.exe" if exist "$(OutDir)assets" (rmdir "$(OutDir)assets" /S /Q && goto end) else goto end :end mkdir "$(OutDir)assets" diff --git a/NFPackCreator/NFPackCreator.rc b/NFAssetBuilder/NFAssetBuilder.rc similarity index 85% rename from NFPackCreator/NFPackCreator.rc rename to NFAssetBuilder/NFAssetBuilder.rc index d44f749..684cfdc 100644 --- a/NFPackCreator/NFPackCreator.rc +++ b/NFAssetBuilder/NFAssetBuilder.rc @@ -68,12 +68,12 @@ BEGIN BLOCK "040904b0" BEGIN VALUE "CompanyName", "Grayson Riffe" - VALUE "FileDescription", "Nothin' Fancy Asset Pack Creator" + VALUE "FileDescription", "Nothin' Fancy Asset Builder" VALUE "FileVersion", "1.0" - VALUE "InternalName", "NFPackCreator.exe" - VALUE "LegalCopyright", "Copyright (C) 2021 Grayson Riffe" - VALUE "OriginalFilename", "NFPackCreator.exe" - VALUE "ProductName", "Nothin' Fancy Asset Pack Creator" + VALUE "InternalName", "NFAssetBuilder.exe" + VALUE "LegalCopyright", "Copyright (C) 2021" + VALUE "OriginalFilename", "NFAssetBuilder.exe" + VALUE "ProductName", "Nothin' Fancy Asset Builder" VALUE "ProductVersion", "1.0" END END diff --git a/NFPackCreator/NFPackCreator.vcxproj b/NFAssetBuilder/NFAssetBuilder.vcxproj similarity index 87% rename from NFPackCreator/NFPackCreator.vcxproj rename to NFAssetBuilder/NFAssetBuilder.vcxproj index fca2a3a..74ffbda 100644 --- a/NFPackCreator/NFPackCreator.vcxproj +++ b/NFAssetBuilder/NFAssetBuilder.vcxproj @@ -16,6 +16,7 @@ {771b4aee-e2c6-4745-ac40-1ef57149612e} NFPackCreator 10.0 + NFAssetBuilder @@ -60,6 +61,7 @@ WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) true stdcpp17 + $(ProjectDir)src\include;$(ProjectDir)dep\include;%(AdditionalIncludeDirectories) Console @@ -76,6 +78,7 @@ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true stdcpp17 + $(ProjectDir)src\include;$(ProjectDir)dep\include;%(AdditionalIncludeDirectories) Console @@ -87,12 +90,18 @@ + + + + + + - + diff --git a/NFPackCreator/NFPackCreator.vcxproj.filters b/NFAssetBuilder/NFAssetBuilder.vcxproj.filters similarity index 64% rename from NFPackCreator/NFPackCreator.vcxproj.filters rename to NFAssetBuilder/NFAssetBuilder.vcxproj.filters index 61fce6a..372b91f 100644 --- a/NFPackCreator/NFPackCreator.vcxproj.filters +++ b/NFAssetBuilder/NFAssetBuilder.vcxproj.filters @@ -18,14 +18,32 @@ Source Files + + Source Files + + + Source Files + + + Source Files + + + Header Files + + + Header Files + + + Header Files + Header Files - + Resource Files diff --git a/NFPackCreator/NFPackCreator.vcxproj.user b/NFAssetBuilder/NFAssetBuilder.vcxproj.user similarity index 88% rename from NFPackCreator/NFPackCreator.vcxproj.user rename to NFAssetBuilder/NFAssetBuilder.vcxproj.user index d54cb99..9242973 100644 --- a/NFPackCreator/NFPackCreator.vcxproj.user +++ b/NFAssetBuilder/NFAssetBuilder.vcxproj.user @@ -10,6 +10,7 @@ $(OutDir) WindowsLocalDebugger - -h + + \ No newline at end of file diff --git a/NothinFancy/dep/include/stb_image.h b/NFAssetBuilder/dep/include/stb_image.h similarity index 100% rename from NothinFancy/dep/include/stb_image.h rename to NFAssetBuilder/dep/include/stb_image.h diff --git a/NFPackCreator/resource.h b/NFAssetBuilder/resource.h similarity index 92% rename from NFPackCreator/resource.h rename to NFAssetBuilder/resource.h index a2d92eb..fdaab2e 100644 --- a/NFPackCreator/resource.h +++ b/NFAssetBuilder/resource.h @@ -1,6 +1,6 @@ //{{NO_DEPENDENCIES}} // Microsoft Visual C++ generated include file. -// Used by NFPackCreator.rc +// Used by NFAssetBuilder.rc // Next default values for new objects // diff --git a/NFAssetBuilder/src/Models.cpp b/NFAssetBuilder/src/Models.cpp new file mode 100644 index 0000000..0bd04c8 --- /dev/null +++ b/NFAssetBuilder/src/Models.cpp @@ -0,0 +1,297 @@ +#include "Models.h" + +#include +#include +#include +#include + +void cookModel(std::string& OBJin, std::string& MTLin, std::string& out) { + std::vector obj(&OBJin[0], &OBJin[0] + OBJin.size()); + std::vector mtl(&MTLin[0], &MTLin[0] + MTLin.size()); + + std::unordered_map mats; + parseMaterials(mats, mtl); + + std::vector vbRaw, tcRaw, vnRaw; + std::string usingMat; + + bool tcPresent = false, vnPresent = false; + size_t position = 0; + + while (true) { + size_t nextLine = std::search(obj.begin() + position, obj.end(), "\n", "\n" + std::strlen("\n")) - (obj.begin() + position); + if (!nextLine) + break; + std::vector line(&obj[position], &obj[position + nextLine]); + position += nextLine + 1; + std::stringstream ss(std::string(&line[0], line.size())); + std::string firstWord; + ss >> firstWord; + + if (std::strcmp(&firstWord[0], "v") == 0) { + float x = 0.0f, y = 0.0f, z = 0.0f; + ss >> x >> y >> z; + vbRaw.push_back(x); + vbRaw.push_back(y); + vbRaw.push_back(z); + } + else if (std::strcmp(&firstWord[0], "vt") == 0) { + tcPresent = true; + float u = 0.0f, v = 0.0f; + ss >> u >> v; + tcRaw.push_back(u); + tcRaw.push_back(v); + } + else if (std::strcmp(&firstWord[0], "vn") == 0) { + vnPresent = true; + float x = 0.0f, y = 0.0f, z = 0.0f; + ss >> x >> y >> z; + vnRaw.push_back(x); + vnRaw.push_back(y); + vnRaw.push_back(z); + } + else if (std::strcmp(&firstWord[0], "usemtl") == 0) { + std::string matName; + ss >> matName; + usingMat = matName; + } + else if (std::strcmp(&firstWord[0], "f") == 0) { + if (!tcPresent) + Error("No texture coordinates found in model!"); + if (!vnPresent) + Error("No normals found in model!"); + + unsigned int vertexIndex[3], uvIndex[3], vnIndex[3]; + char temp; + ss >> vertexIndex[0] >> temp >> uvIndex[0] >> temp >> vnIndex[0] >> vertexIndex[1] >> temp >> uvIndex[1] >> temp >> vnIndex[1] >> vertexIndex[2] >> temp >> uvIndex[2] >> temp >> vnIndex[2]; + if (ss.rdbuf()->in_avail() > 1) + Error("Model has non-triangle faces!"); + mats[usingMat]->vbIndices.push_back(vertexIndex[0]); + mats[usingMat]->vbIndices.push_back(vertexIndex[1]); + mats[usingMat]->vbIndices.push_back(vertexIndex[2]); + mats[usingMat]->tcIndices.push_back(uvIndex[0]); + mats[usingMat]->tcIndices.push_back(uvIndex[1]); + mats[usingMat]->tcIndices.push_back(uvIndex[2]); + mats[usingMat]->vnIndices.push_back(vnIndex[0]); + mats[usingMat]->vnIndices.push_back(vnIndex[1]); + mats[usingMat]->vnIndices.push_back(vnIndex[2]); + } + } + + for (auto& m : mats) { + std::string curr = m.first; + for (unsigned int i = 0; i < mats[curr]->vbIndices.size(); i++) { + unsigned int vertexIndex = mats[curr]->vbIndices[i]; + unsigned int uvIndex = mats[curr]->tcIndices[i]; + unsigned int vnIndex = mats[curr]->vnIndices[i]; + float vertexX = vbRaw[(vertexIndex - 1) * 3]; + float vertexY = vbRaw[(vertexIndex - 1) * 3 + 1]; + float vertexZ = vbRaw[(vertexIndex - 1) * 3 + 2]; + float vertexU = tcRaw[(uvIndex - 1) * 2]; + float vertexV = tcRaw[(uvIndex - 1) * 2 + 1]; + float vnX = vnRaw[(vnIndex - 1) * 3]; + float vnY = vnRaw[(vnIndex - 1) * 3 + 1]; + float vnZ = vnRaw[(vnIndex - 1) * 3 + 2]; + mats[curr]->unindexedVB.push_back(vertexX); + mats[curr]->unindexedVB.push_back(vertexY); + mats[curr]->unindexedVB.push_back(vertexZ); + mats[curr]->unindexedTC.push_back(vertexU); + mats[curr]->unindexedTC.push_back(vertexV); + mats[curr]->unindexedVN.push_back(vnX); + mats[curr]->unindexedVN.push_back(vnY); + mats[curr]->unindexedVN.push_back(vnZ); + } + + for (unsigned int i = 0; i * 9 < mats[curr]->unindexedVB.size(); i++) { + Vec3 pos1(mats[curr]->unindexedVB[i * 9], mats[curr]->unindexedVB[i * 9 + 1], mats[curr]->unindexedVB[i * 9 + 2]); + Vec2 uv1(mats[curr]->unindexedTC[i * 6], mats[curr]->unindexedTC[i * 6 + 1]); + Vec3 pos2(mats[curr]->unindexedVB[i * 9 + 3], mats[curr]->unindexedVB[i * 9 + 4], mats[curr]->unindexedVB[i * 9 + 5]); + Vec2 uv2(mats[curr]->unindexedTC[i * 6 + 2], mats[curr]->unindexedTC[i * 6 + 3]); + Vec3 pos3(mats[curr]->unindexedVB[i * 9 + 6], mats[curr]->unindexedVB[i * 9 + 7], mats[curr]->unindexedVB[i * 9 + 8]); + Vec2 uv3(mats[curr]->unindexedTC[i * 6 + 4], mats[curr]->unindexedTC[i * 6 + 5]); + + Vec3 edge1 = pos2 - pos1; + Vec3 edge2 = pos3 - pos1; + Vec2 delta1 = uv2 - uv1; + Vec2 delta2 = uv3 - uv1; + float f = 1.0f / (delta1.x * delta2.y - delta2.x * delta1.y); + float x = f * (delta2.y * edge1.x - delta1.y * edge2.x); + float y = f * (delta2.y * edge1.y - delta1.y * edge2.y); + float z = f * (delta2.y * edge1.z - delta1.y * edge2.z); + mats[curr]->unindexedTan.push_back(x); + mats[curr]->unindexedTan.push_back(y); + mats[curr]->unindexedTan.push_back(z); + mats[curr]->unindexedTan.push_back(x); + mats[curr]->unindexedTan.push_back(y); + mats[curr]->unindexedTan.push_back(z); + mats[curr]->unindexedTan.push_back(x); + mats[curr]->unindexedTan.push_back(y); + mats[curr]->unindexedTan.push_back(z); + } + } + + struct Vertex { + float x; + float y; + float z; + + float u; + float v; + + float vnX; + float vnY; + float vnZ; + + bool operator<(const Vertex other) const { + return std::memcmp((void*)this, (void*)&other, sizeof(Vertex)) > 0; + } + }; + + std::vector vboPositions; + std::vector vboTexCoords; + std::vector vboNormals; + std::vector vboTangents; + std::vector vboMaterialIndices; + std::vector vboIndices; + int matCount = 0; + + std::string modelHeader; + int numMats = (int)mats.size(); + modelHeader.append((char*)&numMats, sizeof(numMats)); + + for (auto& m : mats) { + std::string curr = m.first; + std::map vertexMap; + for (unsigned int i = 0; i * 3 < mats[curr]->unindexedVB.size(); i++) { + Vertex currVertex = { mats[curr]->unindexedVB[(i * 3)], mats[curr]->unindexedVB[(i * 3) + 1], mats[curr]->unindexedVB[(i * 3) + 2], mats[curr]->unindexedTC[(i * 2)], mats[curr]->unindexedTC[(i * 2) + 1], mats[curr]->unindexedVN[(i * 3)], mats[curr]->unindexedVN[(i * 3) + 1], mats[curr]->unindexedVN[(i * 3) + 2] }; + bool found = false; + found = vertexMap.find(currVertex) != vertexMap.end(); + if (found) { + unsigned int index = vertexMap[currVertex]; + mats[curr]->outIB.push_back(index); + mats[curr]->ibCount++; + } + else { + mats[curr]->outVB.push_back(currVertex.x); + mats[curr]->outVB.push_back(currVertex.y); + mats[curr]->outVB.push_back(currVertex.z); + mats[curr]->outTC.push_back(currVertex.u); + mats[curr]->outTC.push_back(currVertex.v); + mats[curr]->outVN.push_back(currVertex.vnX); + mats[curr]->outVN.push_back(currVertex.vnY); + mats[curr]->outVN.push_back(currVertex.vnZ); + mats[curr]->outTan.push_back(mats[curr]->unindexedTan[(i * 3)]); + mats[curr]->outTan.push_back(mats[curr]->unindexedTan[(i * 3 + 1)]); + mats[curr]->outTan.push_back(mats[curr]->unindexedTan[(i * 3 + 2)]); + size_t index = (mats[curr]->outVB.size() / 3) - 1; + mats[curr]->outIB.push_back((unsigned int)index); + vertexMap[currVertex] = (unsigned int)index; + mats[curr]->ibCount++; + } + } + + TempMaterial& curr2 = *m.second; + + //Serialize this + //curr2.diffuseColor.x, curr2.diffuseColor.y, curr2.diffuseColor.z, curr2.shininess + + modelHeader.append((char*)&curr2.diffuseColor.x, sizeof(curr2.diffuseColor.x)); + modelHeader.append((char*)&curr2.diffuseColor.y, sizeof(curr2.diffuseColor.y)); + modelHeader.append((char*)&curr2.diffuseColor.z, sizeof(curr2.diffuseColor.z)); + modelHeader.append((char*)&curr2.shininess, sizeof(curr2.shininess)); + + size_t offset = vboPositions.size() / 3; + vboPositions.insert(vboPositions.end(), curr2.outVB.begin(), curr2.outVB.end()); + vboTexCoords.insert(vboTexCoords.end(), curr2.outTC.begin(), curr2.outTC.end()); + vboNormals.insert(vboNormals.end(), curr2.outVN.begin(), curr2.outVN.end()); + vboTangents.insert(vboTangents.end(), curr2.outTan.begin(), curr2.outTan.end()); + vboMaterialIndices.insert(vboMaterialIndices.end(), curr2.outVB.size() / 3, matCount); + if (offset) + std::for_each(curr2.outIB.begin(), curr2.outIB.end(), [offset](unsigned int& out) { out += (unsigned int)offset; }); + vboIndices.insert(vboIndices.end(), curr2.outIB.begin(), curr2.outIB.end()); + delete m.second; + matCount++; + } + + size_t posSize = vboPositions.size() * sizeof(float); + size_t tcSize = vboTexCoords.size() * sizeof(float); + size_t normSize = vboNormals.size() * sizeof(float); + size_t tanSize = vboTangents.size() * sizeof(float); + size_t matIndicesSize = vboMaterialIndices.size() * sizeof(int); + size_t indicesSize = vboIndices.size() * sizeof(unsigned int); + + modelHeader.append((char*)&posSize, sizeof(posSize)); + modelHeader.append((char*)&tcSize, sizeof(tcSize)); + modelHeader.append((char*)&normSize, sizeof(normSize)); + modelHeader.append((char*)&tanSize, sizeof(tanSize)); + modelHeader.append((char*)&matIndicesSize, sizeof(matIndicesSize)); + modelHeader.append((char*)&indicesSize, sizeof(indicesSize)); + + out.append(modelHeader); + out.append((char*)vboPositions.data(), posSize); + out.append((char*)vboTexCoords.data(), tcSize); + out.append((char*)vboNormals.data(), normSize); + out.append((char*)vboTangents.data(), tanSize); + out.append((char*)vboMaterialIndices.data(), matIndicesSize); + out.append((char*)vboIndices.data(), indicesSize); +} + +void parseMaterials(std::unordered_map& mats, std::vector& mtl) { + std::string currMat; + size_t position = 0; + while (true) { + size_t nextLine = std::search(mtl.begin() + position, mtl.end(), "\n", "\n" + std::strlen("\n")) - (mtl.begin() + position); + if (position + nextLine >= mtl.size()) + break; + std::vector line(&mtl[position], &mtl[position + nextLine]); + position += nextLine + 1; + std::stringstream ss(std::string(&line[0], line.size())); + std::string firstWord; + ss >> firstWord; + + if (std::strcmp(&firstWord[0], "newmtl") == 0) { + ss >> currMat; + mats[currMat] = new TempMaterial; + } + else if (std::strcmp(&firstWord[0], "Kd") == 0) { + float r = 0.0f, g = 0.0f, b = 0.0f; + ss >> r >> g >> b; + mats[currMat]->diffuseColor = Vec3(r, g, b); + } + else if (std::strcmp(&firstWord[0], "map_Kd") == 0) { + std::string texName = getNewLine(ss); + mats[currMat]->diffuseTextureName = texName; + } + else if (std::strcmp(&firstWord[0], "map_Ks") == 0) { + std::string texName = getNewLine(ss); + mats[currMat]->specularTextureName = texName; + } + else if (std::strcmp(&firstWord[0], "map_Bump") == 0) { + std::string texName = getNewLine(ss); + mats[currMat]->normalTextureName = texName; + } + else if (std::strcmp(&firstWord[0], "Ns") == 0) { + float s = 0.0f; + ss >> s; + mats[currMat]->shininess = s; + } + } +} + +void getNeededImages(std::string mtl, std::set& set) { + while (mtl.size()) { + size_t pos = mtl.find("map_"); + if (pos == std::string::npos) + break; + if (pos == mtl.find("map_Kd") || pos == mtl.find("map_Ks")) + mtl = mtl.substr(pos + 7); + else + mtl = mtl.substr(pos + 9); + std::stringstream ss(mtl); + std::string temp = getNewLine(ss); + size_t pos2 = temp.find_last_of("/\\"); + if (pos2 != std::string::npos) + temp = temp.substr(pos2 + 1); + set.insert(temp); + } +} \ No newline at end of file diff --git a/NFAssetBuilder/src/Textures.cpp b/NFAssetBuilder/src/Textures.cpp new file mode 100644 index 0000000..5da6910 --- /dev/null +++ b/NFAssetBuilder/src/Textures.cpp @@ -0,0 +1,18 @@ +#include "Textures.h" + +#define STB_IMAGE_IMPLEMENTATION +#include "stb_image.h" + +void cookTexture(const std::string& in, std::string& out) { + stbi_set_flip_vertically_on_load(true); + + int x, y, channels; + unsigned char* texture = stbi_load_from_memory((unsigned char*)&in[0], (int)in.size(), &x, &y, &channels, 0); + + out.append((char*)&x, sizeof(x)); + out.append((char*)&y, sizeof(y)); + out.append((char*)&channels, sizeof(channels)); + out.append((char*)texture, x * y * channels); + + stbi_image_free(texture); +} \ No newline at end of file diff --git a/NFAssetBuilder/src/Utility.cpp b/NFAssetBuilder/src/Utility.cpp new file mode 100644 index 0000000..e19147c --- /dev/null +++ b/NFAssetBuilder/src/Utility.cpp @@ -0,0 +1,84 @@ +#include "Utility.h" + +#include +#include +#include +#include + +#include +#include + +static COMPRESSOR_HANDLE cHandle; + +void Log(const char* in) { + std::cout << "[NFAssetBuilder] Info: " << in << "\n"; +} + +void Log(const std::string& in) { + Log(in.c_str()); +} + +[[noreturn]] +void Error(const std::string& in) { + HANDLE cmd = GetStdHandle(STD_OUTPUT_HANDLE); + SetConsoleTextAttribute(cmd, FOREGROUND_RED); + std::cout << "[NFAssetBuilder] Error: " + in + "\n"; + SetConsoleTextAttribute(cmd, 7); + std::this_thread::sleep_for(std::chrono::seconds(3)); + std::exit(-1); +} + +void Success(const std::string& in) { + HANDLE cmd = GetStdHandle(STD_OUTPUT_HANDLE); + SetConsoleTextAttribute(cmd, FOREGROUND_GREEN); + std::cout << "[NFAssetBuilder] Success: " + in + "\n"; + SetConsoleTextAttribute(cmd, 7); +} + +void initCompressor() { + CreateCompressor(COMPRESS_ALGORITHM_XPRESS_HUFF, NULL, &cHandle); +} + +std::string readFile(const std::string& filename) { + std::ifstream in; + in.open(filename, std::ios::binary); + if (!in) + Error("File \"" + (std::string)filename + (std::string)"\" could not be opened!"); + std::stringstream ss; + ss << in.rdbuf(); + std::string read(ss.str()); + return read; +} + +void writePack(const std::string& filename, std::string& in) { + std::ofstream out; + out.open(filename, std::ios::binary); + if (!out) + Error("Pack \"" + filename + (std::string)"\" could not be written!"); + + in.insert(0, "NFASSETPACK"); + + Log("Encrypting..."); + for (unsigned int i = 0; i < in.size(); i++) + in[i] += 100; + + Log("Compressing..."); + size_t compSize; + Compress(cHandle, &in[0], in.size(), NULL, 0, &compSize); + char* buff = new char[compSize]; + Compress(cHandle, &in[0], in.size(), buff, compSize, &compSize); + + out.write(buff, compSize); + delete[] buff; + out.close(); +} + +std::string getNewLine(std::stringstream& stringstream) { + std::string out; + std::getline(stringstream, out); + + if (out[out.size() - 1] == '\r') + out.erase(out.size() - 1); + + return out; +} \ No newline at end of file diff --git a/NFAssetBuilder/src/include/Models.h b/NFAssetBuilder/src/include/Models.h new file mode 100644 index 0000000..841db33 --- /dev/null +++ b/NFAssetBuilder/src/include/Models.h @@ -0,0 +1,33 @@ +#pragma once +#include +#include +#include +#include + +#include "Utility.h" + +struct TempMaterial { + std::vector outVB; + std::vector unindexedVB; + std::vector vbIndices; + std::vector outTC; + std::vector unindexedTC; + std::vector tcIndices; + std::vector outVN; + std::vector unindexedVN; + std::vector vnIndices; + std::vector unindexedTan; + std::vector outTan; + std::vector outIB; + unsigned int ibCount = 0; + + std::string diffuseTextureName; + Vec3 diffuseColor; + std::string specularTextureName; + std::string normalTextureName; + float shininess = 1.0f; +}; + +void cookModel(std::string& in, std::string& MTLin, std::string& out); +void parseMaterials(std::unordered_map& mats, std::vector& mtl); +void getNeededImages(std::string mtl, std::set& set); \ No newline at end of file diff --git a/NFAssetBuilder/src/include/Textures.h b/NFAssetBuilder/src/include/Textures.h new file mode 100644 index 0000000..95d6c84 --- /dev/null +++ b/NFAssetBuilder/src/include/Textures.h @@ -0,0 +1,4 @@ +#pragma once +#include + +void cookTexture(const std::string& in, std::string& out); \ No newline at end of file diff --git a/NFAssetBuilder/src/include/Utility.h b/NFAssetBuilder/src/include/Utility.h new file mode 100644 index 0000000..73a58a5 --- /dev/null +++ b/NFAssetBuilder/src/include/Utility.h @@ -0,0 +1,117 @@ +#pragma once +#include + +void Log(const char* in); +void Log(const std::string& in); + +void Error(const std::string& in); +void Success(const std::string& in); + +void initCompressor(); + +std::string readFile(const std::string& filename); +void writePack(const std::string& filename, std::string& in); + +std::string getNewLine(std::stringstream& stringstream); + +struct Vec2 { + Vec2() : x(0.0f), y(0.0f) {} + + Vec2(float x1) : x(x1), y(x1) {} + + Vec2(float x1, float y1) : x(x1), y(y1) {} + + Vec2(double x1) : x((float)x1), y((float)x1) {} + + Vec2(double x1, double y1) : x((float)x1), y((float)y1) {} + Vec2 operator+(const Vec2& rhs) const { + return Vec2(x + rhs.x, y + rhs.y); + } + Vec2 operator-(const Vec2& rhs) const { + return Vec2(x - rhs.x, y - rhs.y); + } + Vec2 operator*(const float scalar) const { + return Vec2(x * scalar, y * scalar); + } + Vec2 operator/(const Vec2& rhs) const { + return Vec2(x / rhs.x, y / rhs.y); + } + Vec2& operator+=(const Vec2& rhs) { + this->x += rhs.x; + this->y += rhs.y; + return *this; + } + Vec2& operator-=(const Vec2& rhs) { + this->x -= rhs.x; + this->y -= rhs.y; + return *this; + } + Vec2& operator*=(const Vec2& rhs) { + this->x *= rhs.x; + this->y *= rhs.y; + return *this; + } + Vec2& operator/=(const Vec2& rhs) { + this->x /= rhs.x; + this->y /= rhs.y; + return *this; + } + bool operator==(const Vec2& rhs) { + return this->x == rhs.x && this->y == rhs.y; + } + + float x, y; +}; + +struct Vec3 { + Vec3() : x(0.0f), y(0.0f), z(0.0f) {} + + Vec3(float x1) : x(x1), y(x1), z(x1) {} + + Vec3(float x1, float y1, float z1) : x(x1), y(y1), z(z1) {} + + Vec3(double x1) : x((float)x1), y((float)x1), z((float)x1) {} + + Vec3(double x1, double y1, double z1) : x((float)x1), y((float)y1), z((float)z1) {} + Vec3 operator+(const Vec3& rhs) const { + return Vec3(x + rhs.x, y + rhs.y, z + rhs.z); + } + Vec3 operator-(const Vec3& rhs) const { + return Vec3(x - rhs.x, y - rhs.y, z - rhs.z); + } + Vec3 operator*(const float scalar) const { + return Vec3(x * scalar, y * scalar, z * scalar); + } + Vec3 operator/(const Vec3& rhs) const { + return Vec3(x / rhs.x, y / rhs.y, z / rhs.z); + } + Vec3& operator+=(const Vec3& rhs) { + this->x += rhs.x; + this->y += rhs.y; + this->z += rhs.z; + return *this; + } + Vec3& operator-=(const Vec3& rhs) { + this->x -= rhs.x; + this->y -= rhs.y; + this->z -= rhs.z; + return *this; + } + Vec3& operator*=(const Vec3& rhs) { + this->x *= rhs.x; + this->y *= rhs.y; + this->z *= rhs.z; + return *this; + } + Vec3& operator/=(const Vec3& rhs) { + this->x /= rhs.x; + this->y /= rhs.y; + this->z /= rhs.z; + return *this; + } + bool operator==(const Vec3& rhs) { + return this->x == rhs.x && this->y == rhs.y && this->z == rhs.z; + } + + float x, y, z; +}; \ No newline at end of file diff --git a/NFAssetBuilder/src/main.cpp b/NFAssetBuilder/src/main.cpp new file mode 100644 index 0000000..11a2e8e --- /dev/null +++ b/NFAssetBuilder/src/main.cpp @@ -0,0 +1,129 @@ +#include +#include +#include +#include + +#include +#include + +#include "Utility.h" +#include "Models.h" +#include "Textures.h" + +int main(int argc, char* argv[]) { + Log("Starting up"); + + if (argc > 1) + if ((std::string)argv[1] == "-h") { + Log("Nothin' Fancy Asset Builder\nThis tool creates .nfpack files for the NF engine to load " + "at runtime.\nA pack gets created for each directory in the working directory if there " + "are only compatible files inside.\nSubdirectories are not ignored."); + return 0; + } + + initCompressor(); + + std::set extensions; + extensions.insert({ "shader", "obj", "png", "jpg", "ttf", "wav", "ogg" }); + + unsigned int dirCount = 0; + + const std::filesystem::path workingDir = std::filesystem::current_path(); + + for (const auto& currDir : std::filesystem::directory_iterator(workingDir)) { + if (!currDir.is_directory()) continue; + std::string currPackFilename = currDir.path().filename().string().append(".nfpack"); + Log("Building pack " + currPackFilename); + + std::unordered_map packFiles; + std::string currFileExtension; + std::string currFileIn; + std::string currPackOut; + unsigned int fileCount = 0; + + for (const auto& curr : std::filesystem::recursive_directory_iterator(currDir)) { + if (curr.is_directory()) continue; + + std::filesystem::path relative = std::filesystem::relative(curr, currDir); + std::string currFileName = relative.filename().string(); + + for (auto& file : packFiles) + if (currFileName == file.first) + Error("Duplicate asset \"" + currFileName + (std::string)"\" in pack!"); + + currFileExtension = relative.extension().string().substr(1); + if (currFileExtension == "mtl") + continue; + if (extensions.find(currFileExtension) == extensions.end()) + Error("File \"" + currFileName + (std::string)"\" is not of supported type!"); + + Log("Current file: " + currFileName); + + currFileIn = readFile(curr.path().string()); + std::string currFileOut; + + if (currFileExtension == "obj") { + std::string mtlFileName = currFileName.substr(0, currFileName.find_last_of('.')) + (std::string)".mtl"; + std::string mtl; + for (const auto& curr2 : std::filesystem::recursive_directory_iterator(currDir)) { + if (curr2.path().filename().string() == mtlFileName) { + Log("Found mtl file for " + currFileName); + mtl = readFile(curr2.path().string()); + break; + } + } + if (mtl.empty()) + Error("No mtl file found for " + currFileName + (std::string)"!"); + Log("Cooking model..."); + cookModel(currFileIn, mtl, currFileOut); + } + else if (currFileExtension == "png" || currFileExtension == "jpg") + cookTexture(currFileIn, currFileOut); + + packFiles[currFileName] = currFileOut.size(); + + currPackOut += currFileOut; + + fileCount++; + } + + if (!fileCount) { + Log("No files found inside of \"" + currDir.path().filename().string() + (std::string)"\". No pack written."); + continue; + } + else + Log("Finished gathering files"); + + std::string header; + for (auto& currFile : packFiles) { + header += currFile.first; + header += ':'; + header.append((char*)&currFile.second, sizeof(size_t)); + header += ':'; + } + header.erase(header.size() - 1); + header.append("/NFENDOFPACKHEADER"); + int headerTableSize = (int)header.size(); + header.insert(0, (char*)&headerTableSize, sizeof(headerTableSize)); + header.insert(0, (char*)&fileCount, sizeof(fileCount)); + + currPackOut.insert(0, header); + + writePack(currPackFilename, currPackOut); + + Success("Wrote \"" + currPackFilename + (std::string)"\" containing " + std::to_string(fileCount) + (std::string)" files."); + + dirCount++; + } + + if (dirCount > 0) + Log("Wrote " + std::to_string(dirCount) + (std::string)" asset pack(s)."); + else + Log("No directories found!"); + + Log("Done"); +#ifdef _DEBUG + std::this_thread::sleep_for(std::chrono::seconds(2)); +#endif + return 0; +} \ No newline at end of file diff --git a/NFPackCreator/src/main.cpp b/NFPackCreator/src/main.cpp deleted file mode 100644 index def9ebc..0000000 --- a/NFPackCreator/src/main.cpp +++ /dev/null @@ -1,195 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -COMPRESSOR_HANDLE cHandle; - -void Log(const std::string& in) { - std::cout << "[NFPackCreator] Info: " << in << "\n"; -} - -void Log(const char* in) { - std::cout << "[NFPackCreator] Info: " << in << "\n"; -} - -[[noreturn]] -void Error(const std::string& in) { - HANDLE cmd = GetStdHandle(STD_OUTPUT_HANDLE); - SetConsoleTextAttribute(cmd, FOREGROUND_RED); - std::cout << "[NFPackCreator] Error: " + in + "\n"; - SetConsoleTextAttribute(cmd, 7); - std::this_thread::sleep_for(std::chrono::seconds(3)); - std::exit(-1); -} - -void Success(const std::string& in) { - HANDLE cmd = GetStdHandle(STD_OUTPUT_HANDLE); - SetConsoleTextAttribute(cmd, FOREGROUND_GREEN); - std::cout << "[NFPackCreator] Success: " + in + "\n"; - SetConsoleTextAttribute(cmd, 7); -} - -std::string readFile(const std::string& filename) { - std::ifstream in; - in.open(filename, std::ios::binary); - if (!in) - Error("File \"" + (std::string)filename + (std::string)"\" could not be read!"); - std::stringstream ss; - ss << in.rdbuf(); - std::string read(ss.str()); - if (read.size() > 4 && read.substr(0, 4) == "NFEF") { - read = read.substr(4); - for (unsigned int i = 0; i < read.size(); i++) - read[i] = read[i] - 100; - } - return read; -} - -void writeFile(const std::string& filename, const std::string& in, bool encrypted) { - std::ofstream out; - out.open(filename, std::ios::binary); - if (!out) - Error("File \"" + (std::string)filename + (std::string)"\" could not be written!"); - std::string write(in); - if (encrypted) { - for (unsigned int i = 0; i < write.size(); i++) { - char temp = write[i] + 100; - write[i] = temp; - } - write.insert(0, "NFEF"); - } - Log("Compressing..."); - size_t compSize; - Compress(cHandle, &write[0], write.size(), NULL, 0, &compSize); - char* buff = new char[compSize]; - Compress(cHandle, &write[0], write.size(), buff, compSize, &compSize); - - out.write(buff, compSize); - delete[] buff; - out << write; - out.close(); -} - -void getNeededImages(std::string mtl, std::set& set) { - while (mtl.size()) { - size_t pos = mtl.find("map_"); - if (pos == std::string::npos) - break; - if (pos == mtl.find("map_Kd") || pos == mtl.find("map_Ks")) - mtl = mtl.substr(pos + 7); - else - mtl = mtl.substr(pos + 9); - std::stringstream ss(mtl); - std::string temp; - ss >> temp; - size_t pos2 = temp.find_last_of("/\\"); - if (pos2 != std::string::npos) - temp = temp.substr(pos2 + 1); - set.insert(temp); - } -} - -int main(int argc, char* argv[]) { - Log("Starting up"); - - if (argc > 1) { - if ((std::string)argv[1] == "-h") { - Log("Nothin' Fancy Asset Pack Creator\nThis tool creates .nfpack files for the NF engine to load at runtime.\nA pack gets created for each directory in the working directory if there are only compatible files inside.\nSubdirectories are not ignored."); - return 0; - } - } - - CreateCompressor(COMPRESS_ALGORITHM_XPRESS_HUFF, NULL, &cHandle); - - std::set extensions; - extensions.insert({ "shader", "obj", "png", "jpg", "ttf", "wav", "ogg" }); - - unsigned int dirCount = 0; - const std::filesystem::path workingDir = std::filesystem::current_path(); - for (const auto& currDir : std::filesystem::directory_iterator(std::filesystem::current_path())) { - if (!currDir.is_directory()) - continue; - std::string filename = currDir.path().filename().string().append(".nfpack"); - Log("Creating pack \"" + filename + (std::string)"\""); - std::vector packFilenames; - std::string currFileExtension; - std::string currFileContents; - std::stringstream out; - unsigned int fileCount = 0; - for (const auto& curr : std::filesystem::recursive_directory_iterator(currDir)) { - if (curr.is_directory()) - continue; - std::filesystem::path relative = std::filesystem::relative(curr, currDir); - currFileExtension = relative.extension().string().substr(1); - if (currFileExtension == "mtl") - continue; - if (extensions.find(currFileExtension) == extensions.end()) - Error("File \"" + relative.string() + (std::string)"\" is not of supported type!"); - Log("Current file: " + relative.string()); - - currFileContents = readFile(curr.path().string()); - if (currFileExtension == "obj") { - std::filesystem::path mtlPath; - for (const auto& curr2 : std::filesystem::recursive_directory_iterator(curr.path().parent_path())) { - if (curr2.is_directory()) - continue; - if (curr2.path().extension() != ".mtl") - continue; - std::string mtlFile = relative.filename().string().substr(0, relative.filename().string().size() - 4) + (std::string)".mtl"; - if (curr2.path().filename().string() == mtlFile) { - mtlPath = curr2.path(); - break; - } - } - if (mtlPath.empty()) - Error("No mtl file found for " + relative.filename().string() + (std::string)"!"); - Log("Found mtl file for " + relative.filename().string()); - std::set neededImages; - std::string mtlContents = readFile(mtlPath.string()); - getNeededImages(mtlContents, neededImages); - if (!neededImages.empty()) { - currFileContents.insert(0, "\n"); - for (std::string curr : neededImages) { - currFileContents.insert(0, curr); - currFileContents.insert(0, " "); - } - currFileContents = currFileContents.substr(1); - } - else { - currFileContents.insert(0, "none\n"); - } - currFileContents += '\n' + mtlContents; - } - for (std::string& currFilename : packFilenames) - if (relative.filename().string() == currFilename) - Error("Duplicate asset name \"" + relative.filename().string() + (std::string)"\" in pack!"); - packFilenames.push_back(relative.filename().string()); - if (out.rdbuf()->in_avail() != 0) - out << "\n"; - out << "#NFASSET " + curr.path().filename().string(); - out << "\n"; - out << currFileContents; - fileCount++; - } - if (fileCount == 0) { - Log("No files found inside of \"" + currDir.path().filename().string() + (std::string)"\". No pack written."); - continue; - } - writeFile(filename, out.str(), true); - Success("Wrote \"" + filename + (std::string)"\" containing " + std::to_string(fileCount) + (std::string)" files."); - dirCount++; - } - if (dirCount > 0) - Log("Wrote " + std::to_string(dirCount) + (std::string)" asset pack(s)."); - else - Log("No directories found!"); - Log("Done"); - std::this_thread::sleep_for(std::chrono::seconds(2)); - return 0; -} \ No newline at end of file diff --git a/NothinFancy/NothinFancy.vcxproj b/NothinFancy/NothinFancy.vcxproj index c0d0ece..214b1dc 100644 --- a/NothinFancy/NothinFancy.vcxproj +++ b/NothinFancy/NothinFancy.vcxproj @@ -81,7 +81,7 @@ cd assets -"$(SolutionDir)NFPackCreator\bin\x64$(Configuration)\NFPackCreator.exe" +"$(SolutionDir)NFAssetBuilder\bin\x64$(Configuration)\NFAssetBuilder.exe" @@ -116,7 +116,7 @@ cd assets -"$(SolutionDir)NFPackCreator\bin\x64$(Configuration)\NFPackCreator.exe" +"$(SolutionDir)NFAssetBuilder\bin\x64$(Configuration)\NFAssetBuilder.exe" @@ -184,6 +184,11 @@ + + + {771b4aee-e2c6-4745-ac40-1ef57149612e} + + diff --git a/NothinFancy/src/Renderer/Model.cpp b/NothinFancy/src/Renderer/Model.cpp index ba5ce41..d5ef314 100644 --- a/NothinFancy/src/Renderer/Model.cpp +++ b/NothinFancy/src/Renderer/Model.cpp @@ -361,11 +361,11 @@ namespace nf { for (unsigned int i = 0; i < m_materials.size(); i++) { Texture* curr; if ((curr = std::get<0>(m_materials[i])) != nullptr) - Application::getApp()->getCurrentState()->m_texturesToDelete.insert(std::get<0>(m_materials[i])); + Application::getApp()->getCurrentState()->m_texturesToDelete.insert(curr); if ((curr = std::get<1>(m_materials[i])) != nullptr) - Application::getApp()->getCurrentState()->m_texturesToDelete.insert(std::get<1>(m_materials[i])); + Application::getApp()->getCurrentState()->m_texturesToDelete.insert(curr); if ((curr = std::get<2>(m_materials[i])) != nullptr) - Application::getApp()->getCurrentState()->m_texturesToDelete.insert(std::get<2>(m_materials[i])); + Application::getApp()->getCurrentState()->m_texturesToDelete.insert(curr); } } } \ No newline at end of file diff --git a/NothinFancy/src/Renderer/Texture.cpp b/NothinFancy/src/Renderer/Texture.cpp index 1e47ec4..2cbeab8 100644 --- a/NothinFancy/src/Renderer/Texture.cpp +++ b/NothinFancy/src/Renderer/Texture.cpp @@ -1,8 +1,6 @@ #include "nf/Texture.h" #include "GL/glew.h" -#define STB_IMAGE_IMPLEMENTATION -#include "stb_image.h" #include "nf/Assets.h" #include "nf/Utility.h" @@ -14,18 +12,14 @@ namespace nf { m_x(0), m_y(0) { + //Load dimensions and channels from cooked data int nChannels; - stbi_set_flip_vertically_on_load(true); - unsigned char* texture = stbi_load_from_memory((unsigned char*)tex->data, (unsigned int)tex->size, &m_x, &m_y, &nChannels, 0); - if (!texture) - NFError("Texture failed to load from memory!"); glGenTextures(1, &m_id); glBindTexture(GL_TEXTURE_2D, m_id); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexImage2D(GL_TEXTURE_2D, 0, linear ? GL_RGBA : GL_SRGB_ALPHA, m_x, m_y, 0, nChannels == 3 ? GL_RGB : GL_RGBA, GL_UNSIGNED_BYTE, texture); + glTexImage2D(GL_TEXTURE_2D, 0, linear ? GL_RGBA : GL_SRGB_ALPHA, m_x, m_y, 0, nChannels == 3 ? GL_RGB : GL_RGBA, GL_UNSIGNED_BYTE, tex->data); glGenerateMipmap(GL_TEXTURE_2D); - stbi_image_free(texture); m_isBase = tex->isBaseAsset; tex->alreadyLoaded = true; tex->loadedTexture = this; diff --git a/STEMSln.sln b/STEMSln.sln index 57e45a1..9aff097 100644 --- a/STEMSln.sln +++ b/STEMSln.sln @@ -10,7 +10,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Game", "Game\Game.vcxproj", {771B4AEE-E2C6-4745-AC40-1EF57149612E} = {771B4AEE-E2C6-4745-AC40-1EF57149612E} EndProjectSection EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NFPackCreator", "NFPackCreator\NFPackCreator.vcxproj", "{771B4AEE-E2C6-4745-AC40-1EF57149612E}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NFAssetBuilder", "NFAssetBuilder\NFAssetBuilder.vcxproj", "{771B4AEE-E2C6-4745-AC40-1EF57149612E}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/docs/pages/2_tutorial.md b/docs/pages/2_tutorial.md index f6f7a72..19ecefb 100644 --- a/docs/pages/2_tutorial.md +++ b/docs/pages/2_tutorial.md @@ -336,7 +336,7 @@ After rendering, our world will have a background. @section customAssetsTut Adding Your Assets NF's asset system builds your assets into NFPacks that the engine reads at runtime. The -external tool `NFPackCreator.exe` creates these for you. For a complete guide, please +external tool `NFAssetCreator.exe` creates these for you. For a complete guide, please see @ref assets. @section createUITut Creating a UI