diff --git a/CMakeLists.txt b/CMakeLists.txt index bd65870..6f6cf4e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,6 +36,8 @@ set(SRC_FILES "src/libs/stb_impl.cpp" "src/libs/tiny_gltf.h" "src/libs/tiny_gltf_impl.cpp" + "src/libs/weldmesh.c" + "src/libs/weldmesh.h" "src/renderer.cpp" "src/resources/font.cpp" "src/resources/material.cpp" diff --git a/src/application.cpp b/src/application.cpp index d4cbc8b..6de29d4 100644 --- a/src/application.cpp +++ b/src/application.cpp @@ -268,7 +268,7 @@ void Application::GameLoop() int depth = find_depth(i, 0); for (int j = 0; j < depth; ++j) tabs += std::string{" "}; ImGui::Text("%s%s", tabs.c_str(), t->tag.c_str()); - ImGui::Text("%.1f %.1f %.1f", t->position.x, t->position.y, t->position.z); + //ImGui::Text("%.1f %.1f %.1f", t->position.x, t->position.y, t->position.z); } } else { diff --git a/src/libs/weldmesh.c b/src/libs/weldmesh.c new file mode 100644 index 0000000..c39a61d --- /dev/null +++ b/src/libs/weldmesh.c @@ -0,0 +1,194 @@ +/** + * Copyright (C) 2011 by Morten S. Mikkelsen + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + + +#include "weldmesh.h" +#include +#include + +#ifdef __APPLE__ +#include /* OSX gets its malloc stuff through here */ +#else +#include +#endif + +static void MergeVertsFast(int * piCurNrUniqueVertices, int * piRemapTable, float * pfVertexDataOut, int * piVertexIDs, + const float pfVertexDataIn[], const int iNrVerticesIn, const int iFloatsPerVert, + const int iL_in, const int iR_in, const int iChannelNum); + +int WeldMesh(int * piRemapTable, float * pfVertexDataOut, + const float pfVertexDataIn[], const int iNrVerticesIn, const int iFloatsPerVert) +{ + int iUniqueVertices = 0, i=0; + int * piVertexIDs = NULL; + if(iNrVerticesIn<=0) return 0; + + + iUniqueVertices = 0; + piVertexIDs = (int *) malloc(sizeof(int)*iNrVerticesIn); + if(piVertexIDs!=NULL) + { + for(i=0; i=0); + } + + return iUniqueVertices; +} + + + + + +static void MergeVertsFast(int * piCurNrUniqueVertices, int * piRemapTable, float * pfVertexDataOut, int * piVertexIDs, + const float pfVertexDataIn[], const int iNrVerticesIn, const int iFloatsPerVert, + const int iL_in, const int iR_in, const int iChannelNum) +{ + const int iCount = iR_in-iL_in+1; + int l=0; + float fMin, fMax, fAvg; + assert(iCount>0); + // make bbox + fMin = pfVertexDataIn[ piVertexIDs[iL_in]*iFloatsPerVert + iChannelNum]; fMax = fMin; + for(l=(iL_in+1); l<=iR_in; l++) + { + const int index = piVertexIDs[l]*iFloatsPerVert + iChannelNum; + const float fVal = pfVertexDataIn[index]; + if(fMin>fVal) fMin=fVal; + else if(fMax=fMax || iCount==1) + { + if((iChannelNum+1) == iFloatsPerVert || iCount==1) // we are done, weld by hand + { + int iUniqueNewVertices = 0; + float * pfNewUniVertsOut = &pfVertexDataOut[ piCurNrUniqueVertices[0]*iFloatsPerVert ]; + + for(l=iL_in; l<=iR_in; l++) + { + const int index = piVertexIDs[l]*iFloatsPerVert; + + int iFound = 0; // didn't find copy yet. + int l2=0; + while(l2=iL_in && iL<=iR_in); + index = piVertexIDs[iL]*iFloatsPerVert+iChannelNum; + iReadyLeftSwap = !(pfVertexDataIn[index]=iL_in && iR<=iR_in); + index = piVertexIDs[iR]*iFloatsPerVert+iChannelNum; + iReadyRightSwap = pfVertexDataIn[index](lround(doubles[0] * 255.0)); + g = static_cast(lround(doubles[1] * 255.0)); + b = static_cast(lround(doubles[2] * 255.0)); + a = static_cast(lround(doubles[3] * 255.0)); + } + }; + //std::unordered_map> colour_textures; + std::vector> materials{}; materials.reserve(model.materials.size()); for (const tg::Material& material : model.materials) { @@ -182,11 +196,16 @@ engine::Entity LoadGLTF(Scene& scene, const std::string& path, bool isStatic) } const auto& baseColorFactor4 = material.pbrMetallicRoughness.baseColorFactor; if (baseColorFactor4[0] != 1.0 || baseColorFactor4[1] != 1.0 || baseColorFactor4[2] != 1.0 || baseColorFactor4[3] != 1.0) { - LOG_WARN("Material {} contains a base color value which isn't supported yet.", material.name); if (material.pbrMetallicRoughness.baseColorTexture.index == -1) { - LOG_WARN("Material will be created with a white base color texture."); + LOG_INFO("Making color texture!"); + throw std::runtime_error("TODO"); + // convert double colors to integers + //Color col(baseColorFactor4.data()); + //if (colour_textures.contains(col)) { + // } } else { + LOG_WARN("Material {} contains a base color multiplier which isn't supported yet.", material.name); LOG_WARN("The material's base color texture will be used as-is."); } } @@ -243,6 +262,8 @@ engine::Entity LoadGLTF(Scene& scene, const std::string& path, bool isStatic) const size_t num_vertices = pos_accessor.count; + bool generate_tangents = false; // generating tangents creates a new index list and therefore all attribute accessors must be reassigned + // these checks are probably unneccesary assuming a valid glTF file // if (pos_accessor.componentType != TINYGLTF_COMPONENT_TYPE_FLOAT) throw std::runtime_error("Position att. must be float!"); // if (pos_accessor.type != 3) throw std::runtime_error("Position att. dim. must be 3!"); @@ -279,7 +300,7 @@ engine::Entity LoadGLTF(Scene& scene, const std::string& path, bool isStatic) } else { // TODO: use MikkTSpace to generate tangents - throw std::runtime_error(std::string("No tangents found in primitive from ") + mesh.name); + generate_tangents = true; } // UV0 @@ -326,12 +347,104 @@ engine::Entity LoadGLTF(Scene& scene, const std::string& path, bool isStatic) throw std::runtime_error(std::string("Invalid index buffer in primtive from: ") + mesh.name); } - // combine vertices into one array std::vector vertices; - vertices.reserve(num_vertices); - for (size_t i = 0; i < num_vertices; ++i) { - Vertex v{.pos = positions[i], .norm = normals[i], .tangent = tangents[i], .uv = uv0s[i]}; - vertices.push_back(v); + + if (generate_tangents) { + // generate tangents if they're not in the file + struct MeshData { + Attribute* positions; + Attribute* normals; + Attribute* uvs; + const uint32_t* indices; + size_t num_indices; + std::vector* new_vertices; + }; + + MeshData meshData{}; + meshData.positions = &positions; + meshData.normals = &normals; + meshData.uvs = &uv0s; + meshData.indices = indices.data(); + meshData.num_indices = num_indices; + meshData.new_vertices = &vertices; + vertices.resize(num_indices); + + SMikkTSpaceInterface mts_interface{}; + mts_interface.m_getNumFaces = [](const SMikkTSpaceContext* pContext) -> int { + const MeshData* meshData = static_cast(pContext->m_pUserData); + assert(meshData->num_indices % 3 == 0); + return meshData->num_indices / 3; + }; + mts_interface.m_getNumVerticesOfFace = [](const SMikkTSpaceContext*, const int) -> int { return 3; }; + mts_interface.m_getPosition = [](const SMikkTSpaceContext* pContext, float fvPosOut[], const int iFace, const int iVert) -> void { + const MeshData* const meshData = static_cast(pContext->m_pUserData); + const size_t i = iFace * 3 + iVert; + assert(i < meshData->num_indices); + const size_t vertex_index = meshData->indices[i]; + const glm::vec3 pos = meshData->positions->operator[](vertex_index); + fvPosOut[0] = pos.x; + fvPosOut[1] = pos.y; + fvPosOut[2] = pos.z; + }; + mts_interface.m_getNormal = [](const SMikkTSpaceContext* pContext, float fvNormOut[], const int iFace, const int iVert) -> void { + const MeshData* const meshData = static_cast(pContext->m_pUserData); + const size_t i = iFace * 3 + iVert; + assert(i < meshData->num_indices); + const size_t vertex_index = meshData->indices[i]; + const glm::vec3 norm = meshData->normals->operator[](vertex_index); + fvNormOut[0] = norm.x; + fvNormOut[1] = norm.y; + fvNormOut[2] = norm.z; + }; + mts_interface.m_getTexCoord = [](const SMikkTSpaceContext* pContext, float fvTexcOut[], const int iFace, const int iVert) -> void { + const MeshData* const meshData = static_cast(pContext->m_pUserData); + const size_t i = iFace * 3 + iVert; + assert(i < meshData->num_indices); + const size_t vertex_index = meshData->indices[i]; + const glm::vec2 uv = meshData->uvs->operator[](vertex_index); + fvTexcOut[0] = uv.x; + fvTexcOut[1] = uv.y; + }; + mts_interface.m_setTSpaceBasic = [](const SMikkTSpaceContext* pContext, const float fvTangent[], const float fSign, const int iFace, + const int iVert) -> void { + MeshData* const meshData = static_cast(pContext->m_pUserData); + const size_t i = iFace * 3 + iVert; + assert(i < meshData->num_indices); + const size_t vertex_index = meshData->indices[i]; + + Vertex& new_v = meshData->new_vertices->operator[](i); + + new_v.pos = meshData->positions->operator[](vertex_index); + new_v.norm = meshData->normals->operator[](vertex_index); + new_v.uv = meshData->uvs->operator[](vertex_index); + new_v.tangent.x = fvTangent[0]; + new_v.tangent.y = fvTangent[1]; + new_v.tangent.z = fvTangent[2]; + new_v.tangent.w = fSign; + }; + SMikkTSpaceContext mts_context{}; + mts_context.m_pInterface = &mts_interface; + mts_context.m_pUserData = &meshData; + + bool tan_result = genTangSpaceDefault(&mts_context); + if (tan_result == false) throw std::runtime_error("Failed to generate tangents!"); + + // regenerate indices as simple ones + indices.clear(); + indices.reserve(meshData.new_vertices->size()); + // temp generate simple indices + for (uint32_t i = 0; i < meshData.new_vertices->size(); ++i) { + indices.push_back(i); + } + } + else { + // combine vertices into one array + vertices.clear(); + vertices.reserve(num_vertices); + for (size_t i = 0; i < num_vertices; ++i) { + Vertex v{.pos = positions[i], .norm = normals[i], .tangent = tangents[i], .uv = uv0s[i]}; + vertices.push_back(v); + } } // generate mesh on GPU @@ -355,9 +468,12 @@ engine::Entity LoadGLTF(Scene& scene, const std::string& path, bool isStatic) } } - // glTF uses the Y-up convention so objects must be rotated to Z-up - const Entity parent = - scene.CreateEntity("test_node", 0, glm::vec3{}, glm::quat{glm::one_over_root_two(), glm::one_over_root_two(), 0.0f, 0.0f}); + /* now create the entities and traverse the glTF scene hierarchy */ + const std::filesystem::path filePath(path); + const std::string name = filePath.stem().string(); + + // glTF uses the Y-up convention so the parent object must be rotated to Z-up + const Entity parent = scene.CreateEntity(name, 0, glm::vec3{}, glm::quat{glm::one_over_root_two(), glm::one_over_root_two(), 0.0f, 0.0f}); std::vector entities(model.nodes.size(), 0); std::function generateEntities = [&](Entity parent_entity, const tg::Node& node) -> void { diff --git a/test/res/models/walls_with_tangents.glb b/test/res/models/walls_with_tangents.glb index 0437090..91cc84f 100644 Binary files a/test/res/models/walls_with_tangents.glb and b/test/res/models/walls_with_tangents.glb differ diff --git a/test/src/game.cpp b/test/src/game.cpp index 6cf242c..8c72159 100644 --- a/test/src/game.cpp +++ b/test/src/game.cpp @@ -50,7 +50,7 @@ void PlayGame(GameSettings settings) graphics_settings.vsync = true; graphics_settings.wait_for_present = false; graphics_settings.msaa_level = engine::gfx::MSAALevel::kOff; - graphics_settings.enable_anisotropy = false; + graphics_settings.enable_anisotropy = true; engine::Application::Configuration configuration{}; configuration.enable_frame_limiter = settings.enable_frame_limiter; @@ -167,15 +167,16 @@ void PlayGame(GameSettings settings) scene2->AddComponent(camera); } - { /* axes */ - //engine::util::LoadMeshFromFile(scene2, app.GetResourcePath("models/MY_AXES.dae"), true); + { + /* axes */ + // engine::util::LoadMeshFromFile(scene2, app.GetResourcePath("models/MY_AXES.dae"), true); } { /* floor */ - engine::Entity pivot = scene2->CreateEntity("pivot", 0, glm::vec3{ 0.0f, 0.0f, 0.0f }); + engine::Entity pivot = scene2->CreateEntity("pivot", 0, glm::vec3{0.0f, 0.0f, 0.0f}); - engine::Entity wall2 = scene2->CreateEntity("wall2", pivot, glm::vec3{-50.0f, -50.0f, 0.0f}); + engine::Entity wall2 = scene2->CreateEntity("floor", pivot, glm::vec3{-50.0f, -50.0f, 0.0f}); auto wall_renderable = scene2->AddComponent(wall2); wall_renderable->mesh = GenCuboidMesh(app.renderer()->GetDevice(), 100.0f, 100.0f, 0.1f, 100.0f); wall_renderable->material = std::make_unique(app.renderer(), app.GetResource("builtin.fancy")); @@ -197,14 +198,19 @@ void PlayGame(GameSettings settings) wall_renderable->material->SetAlbedoTexture(app.GetResource("builtin.white")); } - auto teapot = engine::util::LoadGLTF(*scene2, app.GetResourcePath("models/teapot_with_tangents.glb")); - scene2->GetComponent(teapot)->scale *= 10.0f; - //auto teapot2 = engine::util::LoadGLTF(*scene2, app.GetResourcePath("models/teapot.glb")); - //scene2->GetComponent(teapot2)->scale *= 10.0f; - //scene2->GetComponent(teapot2)->position.y += 5.0f; - //scene2->GetComponent(teapot2)->rotation = glm::angleAxis(glm::pi(), glm::vec3{ 0.0f, 0.0f, 1.0f }); - //scene2->GetComponent(teapot2)->rotation *= glm::angleAxis(glm::half_pi(), glm::vec3{1.0f, 0.0f, 0.0f}); - auto walls = engine::util::LoadGLTF(*scene2, app.GetResourcePath("models/walls_with_tangents.glb")); + //auto teapot = engine::util::LoadGLTF(*scene2, app.GetResourcePath("models/teapot_with_tangents.glb")); + //scene2->GetComponent(teapot)->scale *= 10.0f; + auto teapot2 = engine::util::LoadGLTF(*scene2, app.GetResourcePath("models/teapot.glb")); + scene2->GetComponent(teapot2)->scale *= 10.0f; + scene2->GetComponent(teapot2)->position.y += 5.0f; + auto custom = scene2->AddComponent(teapot2); + custom->onInit = [](void) { return; }; + custom->onUpdate = [&](float dt) { + scene2->GetComponent(teapot2)->rotation *= glm::angleAxis(dt, glm::vec3{0.0f, 1.0f, 0.0f}); + }; + // scene2->GetComponent(teapot2)->rotation = glm::angleAxis(glm::pi(), glm::vec3{ 0.0f, 0.0f, 1.0f }); + // scene2->GetComponent(teapot2)->rotation *= glm::angleAxis(glm::half_pi(), glm::vec3{1.0f, 0.0f, 0.0f}); + // auto walls = engine::util::LoadGLTF(*scene2, app.GetResourcePath("models/walls_with_tangents.glb")); } my_scene->GetSystem()->next_scene_ = scene2;