From 82019987e6b9a394977a6daf110c523708e4d3bf Mon Sep 17 00:00:00 2001 From: Kearwood Gilbert Date: Tue, 11 Nov 2025 23:07:04 -0800 Subject: [PATCH] Implemented KRMeshBinding, mesh lod functionality is now explicit in KRScene format and api --- kraken/CMakeLists.txt | 1 + kraken/KRContext.cpp | 4 +- kraken/KREngine-common.h | 1 + kraken/nodes/KRAmbientZone.cpp | 2 +- kraken/nodes/KRAudioSource.cpp | 2 +- kraken/nodes/KRBone.cpp | 2 +- kraken/nodes/KRCollider.cpp | 2 +- kraken/nodes/KRLight.cpp | 2 +- kraken/nodes/KRModel.cpp | 188 ++++++++++++++---------- kraken/nodes/KRModel.h | 10 +- kraken/nodes/KRNode.cpp | 9 +- kraken/nodes/KRReverbZone.cpp | 2 +- kraken/public/kraken.h | 2 +- kraken/resources/KRResource+fbx.cpp | 2 +- kraken/resources/mesh/KRMesh.cpp | 23 --- kraken/resources/mesh/KRMesh.h | 2 - kraken/resources/mesh/KRMeshBinding.cpp | 53 +++++++ kraken/resources/mesh/KRMeshBinding.h | 46 ++++++ kraken/resources/mesh/KRMeshManager.cpp | 78 ++++------ kraken/resources/mesh/KRMeshManager.h | 13 +- kraken/resources/scene/KRScene.cpp | 2 +- tests/smoke/hello_cube/hello_cube.cpp | 2 +- 22 files changed, 268 insertions(+), 180 deletions(-) create mode 100644 kraken/resources/mesh/KRMeshBinding.cpp create mode 100644 kraken/resources/mesh/KRMeshBinding.h diff --git a/kraken/CMakeLists.txt b/kraken/CMakeLists.txt index 45938bd..7183f3a 100644 --- a/kraken/CMakeLists.txt +++ b/kraken/CMakeLists.txt @@ -27,6 +27,7 @@ add_source_and_header(resources/bundle/KRBundleManager) add_source_and_header(resources/material/KRMaterial) add_source_and_header(resources/material/KRMaterialManager) add_source_and_header(resources/mesh/KRMesh) +add_source_and_header(resources/mesh/KRMeshBinding) add_source_and_header(resources/mesh/KRMeshCube) add_source_and_header(resources/mesh/KRMeshManager) add_source_and_header(resources/mesh/KRMeshQuad) diff --git a/kraken/KRContext.cpp b/kraken/KRContext.cpp index 2f7cbf2..642d6e3 100755 --- a/kraken/KRContext.cpp +++ b/kraken/KRContext.cpp @@ -260,7 +260,7 @@ std::vector KRContext::getResources() for (unordered_map::iterator itr = m_pMaterialManager->getMaterials().begin(); itr != m_pMaterialManager->getMaterials().end(); itr++) { resources.push_back((*itr).second); } - for (unordered_multimap::iterator itr = m_pMeshManager->getModels().begin(); itr != m_pMeshManager->getModels().end(); itr++) { + for (unordered_map::iterator itr = m_pMeshManager->getMeshes().begin(); itr != m_pMeshManager->getMeshes().end(); itr++) { resources.push_back((*itr).second); } for (unordered_map::iterator itr = m_pAnimationManager->getAnimations().begin(); itr != m_pAnimationManager->getAnimations().end(); itr++) { @@ -309,7 +309,7 @@ KRResource* KRContext::loadResource(const std::string& file_name, Block* data) if (extension.compare("krbundle") == 0) { resource = m_pBundleManager->loadBundle(name.c_str(), data); } else if (extension.compare("krmesh") == 0) { - resource = m_pMeshManager->loadModel(name.c_str(), data); + resource = m_pMeshManager->loadMesh(name.c_str(), data); } else if (extension.compare("krscene") == 0) { resource = m_pSceneManager->loadScene(name.c_str(), data); } else if (extension.compare("kranimation") == 0) { diff --git a/kraken/KREngine-common.h b/kraken/KREngine-common.h index 1b9bebb..e4f7dc2 100755 --- a/kraken/KREngine-common.h +++ b/kraken/KREngine-common.h @@ -44,6 +44,7 @@ using namespace kraken; #include "siren.h" #include +#include #include #include #include diff --git a/kraken/nodes/KRAmbientZone.cpp b/kraken/nodes/KRAmbientZone.cpp index 4249b45..1364043 100755 --- a/kraken/nodes/KRAmbientZone.cpp +++ b/kraken/nodes/KRAmbientZone.cpp @@ -130,7 +130,7 @@ void KRAmbientZone::render(RenderInfo& ri) bool bVisualize = ri.camera->settings.debug_display == KRRenderSettings::KRENGINE_DEBUG_DISPLAY_SIREN_AMBIENT_ZONES; if (ri.renderPass->getType() == RenderPassType::RENDER_PASS_FORWARD_TRANSPARENT && bVisualize) { - KRMesh* sphereModel = getContext().getMeshManager()->getMaxLODModel("__sphere"); + KRMesh* sphereModel = getContext().getMeshManager()->getMesh("__sphere"); if (sphereModel) { Matrix4 sphereModelMatrix = getModelMatrix(); diff --git a/kraken/nodes/KRAudioSource.cpp b/kraken/nodes/KRAudioSource.cpp index 8262831..00e2766 100755 --- a/kraken/nodes/KRAudioSource.cpp +++ b/kraken/nodes/KRAudioSource.cpp @@ -200,7 +200,7 @@ void KRAudioSource::render(RenderInfo& ri) bool bVisualize = false; if (ri.renderPass->getType() == RenderPassType::RENDER_PASS_FORWARD_TRANSPARENT && bVisualize) { - KRMesh* sphereModel = getContext().getMeshManager()->getMaxLODModel("__sphere"); + KRMesh* sphereModel = getContext().getMeshManager()->getMesh("__sphere"); if (sphereModel) { Matrix4 sphereModelMatrix = getModelMatrix(); diff --git a/kraken/nodes/KRBone.cpp b/kraken/nodes/KRBone.cpp index c6ca34a..33a6471 100755 --- a/kraken/nodes/KRBone.cpp +++ b/kraken/nodes/KRBone.cpp @@ -82,7 +82,7 @@ void KRBone::render(RenderInfo& ri) bool bVisualize = ri.camera->settings.debug_display == KRRenderSettings::KRENGINE_DEBUG_DISPLAY_BONES; if (ri.renderPass->getType() == RenderPassType::RENDER_PASS_FORWARD_TRANSPARENT && bVisualize) { - KRMesh* sphereModel = getContext().getMeshManager()->getMaxLODModel("__sphere"); + KRMesh* sphereModel = getContext().getMeshManager()->getMesh("__sphere"); if (sphereModel) { Matrix4 sphereModelMatrix = getModelMatrix(); diff --git a/kraken/nodes/KRCollider.cpp b/kraken/nodes/KRCollider.cpp index 2efc68e..8d13842 100755 --- a/kraken/nodes/KRCollider.cpp +++ b/kraken/nodes/KRCollider.cpp @@ -103,7 +103,7 @@ void KRCollider::loadXML(tinyxml2::XMLElement* e) void KRCollider::loadModel() { if (m_model == nullptr) { - m_model = m_pContext->getMeshManager()->getMaxLODModel(m_model_name.c_str()); + m_model = m_pContext->getMeshManager()->getMesh(m_model_name.c_str()); if (m_model) { getScene().notify_sceneGraphModify(this); } diff --git a/kraken/nodes/KRLight.cpp b/kraken/nodes/KRLight.cpp index 3901df0..16ebbf0 100755 --- a/kraken/nodes/KRLight.cpp +++ b/kraken/nodes/KRLight.cpp @@ -344,7 +344,7 @@ void KRLight::render(RenderInfo& ri) if (ri.renderPass->getType() == RenderPassType::RENDER_PASS_PARTICLE_OCCLUSION) { if (m_flareTexture.isSet() && m_flareSize > 0.0f) { - KRMesh* sphereModel = getContext().getMeshManager()->getMaxLODModel("__sphere"); + KRMesh* sphereModel = getContext().getMeshManager()->getMesh("__sphere"); if (sphereModel) { Matrix4 occlusion_test_sphere_matrix = Matrix4(); diff --git a/kraken/nodes/KRModel.cpp b/kraken/nodes/KRModel.cpp index 0013c54..b457f1c 100755 --- a/kraken/nodes/KRModel.cpp +++ b/kraken/nodes/KRModel.cpp @@ -46,7 +46,9 @@ void KRModel::InitNodeInfo(KrNodeInfo* nodeInfo) nodeInfo->model.faces_camera = false; nodeInfo->model.light_map_texture = KR_NULL_HANDLE; nodeInfo->model.lod_min_coverage = 0.0f; - nodeInfo->model.mesh = KR_NULL_HANDLE; + for (int lod = 0; lod < kMeshLODCount; lod++) { + nodeInfo->model.mesh[lod] = KR_NULL_HANDLE; + } nodeInfo->model.receives_shadow = true; nodeInfo->model.rim_color = Vector3::Zero(); nodeInfo->model.rim_power = 0.0f; @@ -78,11 +80,13 @@ KRModel::KRModel(KRScene& scene, std::string name) m_boundsCachedMat.c[15] = -1.0f; } -KRModel::KRModel(KRScene& scene, std::string instance_name, std::string model_name, std::string light_map, float lod_min_coverage, bool receives_shadow, bool faces_camera, Vector3 rim_color, float rim_power) +KRModel::KRModel(KRScene& scene, std::string instance_name, std::string model_name[kMeshLODCount], std::string light_map, float lod_min_coverage, bool receives_shadow, bool faces_camera, Vector3 rim_color, float rim_power) : KRNode(scene, instance_name) { m_lightMap.setName(light_map); - m_model_name = model_name; + for (int lod = 0; lod < kMeshLODCount; lod++) { + m_meshes[lod].setName(model_name[lod]); + } m_min_lod_coverage = lod_min_coverage; m_receivesShadow = receives_shadow; m_faces_camera = faces_camera; @@ -133,19 +137,15 @@ KrResult KRModel::update(const KrNodeInfo* nodeInfo) } m_lightMap.set(light_map_texture); - KRMesh* mesh = nullptr; - if (nodeInfo->model.mesh != KR_NULL_HANDLE) { - res = m_pContext->getMappedResource(nodeInfo->model.mesh, &mesh); - if (res != KR_SUCCESS) { - return res; + for (int lod = 0; lod < kMeshLODCount; lod++) { + KRMesh* mesh = nullptr; + if (nodeInfo->model.mesh[lod] != KR_NULL_HANDLE) { + res = m_pContext->getMappedResource(nodeInfo->model.mesh[lod], &mesh); + if (res != KR_SUCCESS) { + return res; + } } - } - if (mesh != nullptr) { - m_models.clear(); - m_model_name = mesh->getName(); - } else { - m_models.clear(); - m_model_name = ""; + m_meshes[lod].set(mesh); } return KR_SUCCESS; @@ -159,7 +159,12 @@ std::string KRModel::getElementName() tinyxml2::XMLElement* KRModel::saveXML(tinyxml2::XMLNode* parent) { tinyxml2::XMLElement* e = KRNode::saveXML(parent); - e->SetAttribute("mesh", m_model_name.c_str()); + e->SetAttribute("mesh", m_meshes[0].getName().c_str()); + for (int lod = 1; lod < kMeshLODCount; lod++) { + char attribName[8]; + snprintf(attribName, 8, "mesh%i", lod); + e->SetAttribute(attribName, m_meshes[lod].getName().c_str()); + } e->SetAttribute("light_map", m_lightMap.getName().c_str()); e->SetAttribute("lod_min_coverage", m_min_lod_coverage); e->SetAttribute("receives_shadow", m_receivesShadow ? "true" : "false"); @@ -201,34 +206,50 @@ std::string KRModel::getLightMap() void KRModel::loadModel() { - if (m_models.size() == 0) { - std::vector models = m_pContext->getMeshManager()->getModel(m_model_name.c_str()); // The model manager returns the LOD levels in sorted order, with the highest detail first - unordered_map > bones; - if (models.size() > 0) { + bool meshChanged = false; + for (int lod = 0; lod < kMeshLODCount; lod++) { + KRMesh* prevMesh = nullptr; + prevMesh = m_meshes[lod].get(); + m_meshes[lod].load(&getContext()); + if (m_meshes[lod].get() != prevMesh) { + meshChanged = true; + } + if (m_meshes[lod].isLoaded()) { + KRMesh* model = m_meshes[lod].get(); + std::vector model_bones; + int bone_count = model->getBoneCount(); bool all_bones_found = true; - for (std::vector::iterator model_itr = models.begin(); model_itr != models.end(); model_itr++) { - KRMesh* model = *model_itr; - std::vector model_bones; - int bone_count = model->getBoneCount(); - for (int bone_index = 0; bone_index < bone_count; bone_index++) { - KRBone* matching_bone = dynamic_cast(getScene().getRootNode()->find(model->getBoneName(bone_index))); - if (matching_bone) { - model_bones.push_back(matching_bone); - } else { - all_bones_found = false; // Reject when there are any missing bones or multiple matches - } + for (int bone_index = 0; bone_index < bone_count; bone_index++) { + KRBone* matching_bone = dynamic_cast(getScene().getRootNode()->find(model->getBoneName(bone_index))); + if (matching_bone) { + model_bones.push_back(matching_bone); + } else { + all_bones_found = false; // Reject when there are any missing bones or multiple matches } - bones[model] = model_bones; } if (all_bones_found) { - m_models = models; - m_bones = bones; - getScene().notify_sceneGraphModify(this); + if (m_bones[lod] != model_bones) { + m_bones[lod] = model_bones; + meshChanged = true; + } + } else { + if (!m_bones[lod].empty()) { + m_bones[lod].clear(); + meshChanged = true; + } + } + } else { + if (!m_bones[lod].empty()) { + m_bones[lod].clear(); + meshChanged = true; } - - invalidateBounds(); } } + + if (meshChanged) { + getScene().notify_sceneGraphModify(this); + invalidateBounds(); + } } void KRModel::render(KRNode::RenderInfo& ri) @@ -243,6 +264,7 @@ void KRModel::render(KRNode::RenderInfo& ri) KRNode::render(ri); + // Don't render meshes on second pass of the deferred lighting renderer, as only lights will be applied if (ri.renderPass->getType() != RenderPassType::RENDER_PASS_DEFERRED_LIGHTS && ri.renderPass->getType() != RenderPassType::RENDER_PASS_ADDITIVE_PARTICLES && ri.renderPass->getType() != RenderPassType::RENDER_PASS_PARTICLE_OCCLUSION @@ -251,43 +273,43 @@ void KRModel::render(KRNode::RenderInfo& ri) && ri.renderPass->getType() != RenderPassType::RENDER_PASS_PRESTREAM) { loadModel(); - if (m_models.size() > 0) { - // Don't render meshes on second pass of the deferred lighting renderer, as only lights will be applied + /* + float lod_coverage = 0.0f; + if(m_models.size() > 1) { + lod_coverage = viewport.coverage(getBounds()); // This also checks the view frustrum culling + } else if(viewport.visible(getBounds())) { + lod_coverage = 1.0f; + } + */ - /* - float lod_coverage = 0.0f; - if(m_models.size() > 1) { - lod_coverage = viewport.coverage(getBounds()); // This also checks the view frustrum culling - } else if(viewport.visible(getBounds())) { - lod_coverage = 1.0f; - } - */ + float lod_coverage = ri.viewport->coverage(getBounds()); // This also checks the view frustrum culling + if (lod_coverage > m_min_lod_coverage) { + // ---===--- Select the best LOD model based on screen coverage ---===--- + int bestLOD = -1; + KRMesh* pModel = nullptr; + for (int lod = 0; lod < kMeshLODCount; lod++) { + if (m_meshes[lod].isLoaded()) { + KRMesh* pLODModel = m_meshes[lod].get(); - float lod_coverage = ri.viewport->coverage(getBounds()); // This also checks the view frustrum culling - - if (lod_coverage > m_min_lod_coverage) { - - // ---===--- Select the best LOD model based on screen coverage ---===--- - std::vector::iterator itr = m_models.begin(); - KRMesh* pModel = *itr++; - - while (itr != m_models.end()) { - KRMesh* pLODModel = *itr++; - if ((float)pLODModel->getLODCoverage() / 100.0f > lod_coverage && pLODModel->getLODCoverage() < pModel->getLODCoverage()) { - pModel = pLODModel; - } else { - break; + if ((float)pLODModel->getLODCoverage() / 100.0f > lod_coverage) { + if(bestLOD == -1 || pLODModel->getLODCoverage() < pModel->getLODCoverage()) { + pModel = pLODModel; + bestLOD = lod; + continue; + } } } + } - m_lightMap.load(&getContext()); + m_lightMap.load(&getContext()); - if (m_lightMap.isLoaded() && ri.camera->settings.bEnableLightMap && ri.renderPass->getType() != RENDER_PASS_SHADOWMAP && ri.renderPass->getType() != RENDER_PASS_SHADOWMAP) { - m_lightMap.get()->resetPoolExpiry(lod_coverage, KRTexture::TEXTURE_USAGE_LIGHT_MAP); - // TODO - Vulkan refactoring. We need to bind the shadow map in KRMesh::Render - // m_pContext->getTextureManager()->selectTexture(5, m_pLightMap, lod_coverage, KRTexture::TEXTURE_USAGE_LIGHT_MAP); - } + if (m_lightMap.isLoaded() && ri.camera->settings.bEnableLightMap && ri.renderPass->getType() != RENDER_PASS_SHADOWMAP && ri.renderPass->getType() != RENDER_PASS_SHADOWMAP) { + m_lightMap.get()->resetPoolExpiry(lod_coverage, KRTexture::TEXTURE_USAGE_LIGHT_MAP); + // TODO - Vulkan refactoring. We need to bind the shadow map in KRMesh::Render + // m_pContext->getTextureManager()->selectTexture(5, m_pLightMap, lod_coverage, KRTexture::TEXTURE_USAGE_LIGHT_MAP); + } + if (pModel) { Matrix4 matModel = getModelMatrix(); if (m_faces_camera) { Vector3 model_center = Matrix4::Dot(matModel, Vector3::Zero()); @@ -295,7 +317,7 @@ void KRModel::render(KRNode::RenderInfo& ri) matModel = Quaternion::Create(Vector3::Forward(), Vector3::Normalize(camera_pos - model_center)).rotationMatrix() * matModel; } - pModel->render(ri, getName(), matModel, m_lightMap.get(), m_bones[pModel], lod_coverage); + pModel->render(ri, getName(), matModel, m_lightMap.get(), m_bones[bestLOD], lod_coverage); } } } @@ -308,8 +330,10 @@ void KRModel::preStream(const KRViewport& viewport) loadModel(); float lod_coverage = viewport.coverage(getBounds()); - for (auto itr = m_models.begin(); itr != m_models.end(); itr++) { - (*itr)->preStream(lod_coverage); + for (int i = 0; i < kMeshLODCount; i++) { + if (m_meshes[i].isLoaded()) { + m_meshes[i].get()->preStream(lod_coverage); + } } m_lightMap.load(&getContext()); @@ -326,8 +350,10 @@ kraken_stream_level KRModel::getStreamLevel(const KRViewport& viewport) loadModel(); - for (auto itr = m_models.begin(); itr != m_models.end(); itr++) { - stream_level = KRMIN(stream_level, (*itr)->getStreamLevel()); + for (int lod = 0; lod < kMeshLODCount; lod++) { + if (m_meshes[lod].isLoaded()) { + stream_level = KRMIN(stream_level, m_meshes[lod].get()->getStreamLevel()); + } } return stream_level; @@ -336,22 +362,28 @@ kraken_stream_level KRModel::getStreamLevel(const KRViewport& viewport) AABB KRModel::getBounds() { loadModel(); - if (m_models.size() > 0) { + + // Get the bounds of the lowest lod mesh + for(int lod=0; lodgetMinPoint(), m_models[0]->getMaxPoint(), getModelMatrix()); + AABB normal_bounds = AABB::Create(mesh->getMinPoint(), mesh->getMaxPoint(), getModelMatrix()); float max_dimension = normal_bounds.longest_radius(); return AABB::Create(normal_bounds.center() - Vector3::Create(max_dimension), normal_bounds.center() + Vector3::Create(max_dimension)); } else { - if (!(m_boundsCachedMat == getModelMatrix())) { m_boundsCachedMat = getModelMatrix(); - m_boundsCached = AABB::Create(m_models[0]->getMinPoint(), m_models[0]->getMaxPoint(), getModelMatrix()); + m_boundsCached = AABB::Create(mesh->getMinPoint(), mesh->getMaxPoint(), getModelMatrix()); } return m_boundsCached; } - } else { - return AABB::Infinite(); } + + // No models loaded + return AABB::Infinite(); } diff --git a/kraken/nodes/KRModel.h b/kraken/nodes/KRModel.h index e13404e..ca04e51 100755 --- a/kraken/nodes/KRModel.h +++ b/kraken/nodes/KRModel.h @@ -40,6 +40,7 @@ #include "KRNode.h" #include "KRContext.h" #include "resources/mesh/KRMesh.h" +#include "resources/mesh/KRMeshBinding.h" #include "resources/texture/KRTexture.h" #include "resources/texture/KRTextureBinding.h" #include "KRBone.h" @@ -48,10 +49,12 @@ class KRModel : public KRNode { public: + static const int kMeshLODCount = 8; + static void InitNodeInfo(KrNodeInfo* nodeInfo); KRModel(KRScene& scene, std::string name); - KRModel(KRScene& scene, std::string instance_name, std::string model_name, std::string light_map, float lod_min_coverage, bool receives_shadow, bool faces_camera, hydra::Vector3 rim_color = hydra::Vector3::Zero(), float rim_power = 0.0f); + KRModel(KRScene& scene, std::string instance_name, std::string model_name[kMeshLODCount], std::string light_map, float lod_min_coverage, bool receives_shadow, bool faces_camera, hydra::Vector3 rim_color = hydra::Vector3::Zero(), float rim_power = 0.0f); virtual ~KRModel(); KrResult update(const KrNodeInfo* nodeInfo) override; @@ -76,10 +79,9 @@ public: private: void preStream(const KRViewport& viewport); - std::vector m_models; - unordered_map > m_bones; // Outer std::map connects model to set of bones + std::array m_meshes; + std::array, kMeshLODCount> m_bones; // Connects model to set of bones KRTextureBinding m_lightMap; - std::string m_model_name; float m_min_lod_coverage; diff --git a/kraken/nodes/KRNode.cpp b/kraken/nodes/KRNode.cpp index 2d61eca..b17ed39 100755 --- a/kraken/nodes/KRNode.cpp +++ b/kraken/nodes/KRNode.cpp @@ -653,7 +653,14 @@ KRNode* KRNode::LoadXML(KRScene& scene, tinyxml2::XMLElement* e) } Vector3 rim_color = Vector3::Zero(); rim_color = kraken::getXMLAttribute("rim_color", e, Vector3::Zero()); - new_node = new KRModel(scene, szName, e->Attribute("mesh"), e->Attribute("light_map"), lod_min_coverage, receives_shadow, faces_camera, rim_color, rim_power); + std::string meshNames[KRModel::kMeshLODCount]; + meshNames[0] = e->Attribute("mesh"); + for (int lod = 1; lod < KRModel::kMeshLODCount; lod++) { + char attribName[8]; + snprintf(attribName, 8, "mesh%i", lod); + meshNames[lod] = e->Attribute(attribName); + } + new_node = new KRModel(scene, szName, meshNames, e->Attribute("light_map"), lod_min_coverage, receives_shadow, faces_camera, rim_color, rim_power); } else if (strcmp(szElementName, "collider") == 0) { new_node = new KRCollider(scene, szName, e->Attribute("mesh"), 65535, 1.0f); } else if (strcmp(szElementName, "bone") == 0) { diff --git a/kraken/nodes/KRReverbZone.cpp b/kraken/nodes/KRReverbZone.cpp index b52ef2a..37e76e7 100755 --- a/kraken/nodes/KRReverbZone.cpp +++ b/kraken/nodes/KRReverbZone.cpp @@ -128,7 +128,7 @@ void KRReverbZone::render(RenderInfo& ri) bool bVisualize = ri.camera->settings.debug_display == KRRenderSettings::KRENGINE_DEBUG_DISPLAY_SIREN_REVERB_ZONES; if (ri.renderPass->getType()== RenderPassType::RENDER_PASS_FORWARD_TRANSPARENT && bVisualize) { - KRMesh* sphereModel = getContext().getMeshManager()->getMaxLODModel("__sphere"); + KRMesh* sphereModel = getContext().getMeshManager()->getMesh("__sphere"); if (sphereModel) { Matrix4 sphereModelMatrix = getModelMatrix(); PipelineInfo info{}; diff --git a/kraken/public/kraken.h b/kraken/public/kraken.h index 37f77ce..46298c3 100644 --- a/kraken/public/kraken.h +++ b/kraken/public/kraken.h @@ -301,7 +301,7 @@ typedef struct bool faces_camera; float rim_power; hydra::Vector3 rim_color; - KrResourceMapIndex mesh; + KrResourceMapIndex mesh[8]; KrResourceMapIndex light_map_texture; } model; struct diff --git a/kraken/resources/KRResource+fbx.cpp b/kraken/resources/KRResource+fbx.cpp index 14175d8..de15ec6 100755 --- a/kraken/resources/KRResource+fbx.cpp +++ b/kraken/resources/KRResource+fbx.cpp @@ -1556,7 +1556,7 @@ void LoadMesh(KRContext& context, FbxScene* pFbxScene, FbxGeometryConverter* pGe KRMesh* new_mesh = new KRMesh(context, pMesh->GetNode()->GetName()); new_mesh->LoadData(mi, true, need_tangents); - context.getMeshManager()->addModel(new_mesh); + context.getMeshManager()->addMesh(new_mesh); } KRNode* LoadMesh(KRNode* parent_node, FbxScene* pFbxScene, FbxGeometryConverter* pGeometryConverter, FbxNode* pNode) diff --git a/kraken/resources/mesh/KRMesh.cpp b/kraken/resources/mesh/KRMesh.cpp index 97737b6..dc637ad 100755 --- a/kraken/resources/mesh/KRMesh.cpp +++ b/kraken/resources/mesh/KRMesh.cpp @@ -67,31 +67,8 @@ KRMesh::KRMesh(KRContext& context, std::string name, Block* data) : KRResource(c loadPack(data); } - -void KRMesh::parseName(const std::string& name, std::string& lodBaseName, int& lodCoverage) -{ - lodCoverage = 100; - lodBaseName = name; - - size_t last_underscore_pos = name.find_last_of('_'); - if (last_underscore_pos != std::string::npos) { - // Found an underscore - std::string suffix = name.substr(last_underscore_pos + 1); - if (suffix.find("lod") == 0) { - std::string lod_level_string = suffix.substr(3); - char* end = NULL; - int c = (int)strtol(lod_level_string.c_str(), &end, 10); - if (c >= 0 && c <= 100 && *end == '\0') { - lodCoverage = c; - lodBaseName = name.substr(0, last_underscore_pos); - } - } - } -} - void KRMesh::setName(const std::string name) { - parseName(name, m_lodBaseName, m_lodCoverage); m_lodCoverage = 100; m_lodBaseName = name; } diff --git a/kraken/resources/mesh/KRMesh.h b/kraken/resources/mesh/KRMesh.h index 1b92671..7af49e5 100755 --- a/kraken/resources/mesh/KRMesh.h +++ b/kraken/resources/mesh/KRMesh.h @@ -70,8 +70,6 @@ class KRMesh : public KRResource { public: - static void parseName(const std::string& name, std::string& lodBaseName, int& lodCoverage); - KRMesh(KRContext& context, std::string name, mimir::Block* data); KRMesh(KRContext& context, std::string name); virtual ~KRMesh(); diff --git a/kraken/resources/mesh/KRMeshBinding.cpp b/kraken/resources/mesh/KRMeshBinding.cpp new file mode 100644 index 0000000..f630e24 --- /dev/null +++ b/kraken/resources/mesh/KRMeshBinding.cpp @@ -0,0 +1,53 @@ +// +// KRMeshBinding.cpp +// Kraken Engine +// +// Copyright 2025 Kearwood Gilbert. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other materials +// provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// The views and conclusions contained in the software and documentation are those of the +// authors and should not be interpreted as representing official policies, either expressed +// or implied, of Kearwood Gilbert. +// + +#include "KREngine-common.h" +#include "KRContext.h" +#include "KRMesh.h" +#include "KRMeshBinding.h" + +KRMesh* KRMeshBinding::get() +{ + return static_cast(m_resource); +} + +bool KRMeshBinding::load(KRContext* context) +{ + if (m_name.size() == 0) { + return true; + } + if (m_resource != nullptr) { + return true; + } + m_resource = context->getMeshManager()->getMesh(m_name.c_str()); + + return (m_resource != nullptr); +} \ No newline at end of file diff --git a/kraken/resources/mesh/KRMeshBinding.h b/kraken/resources/mesh/KRMeshBinding.h new file mode 100644 index 0000000..85bcc99 --- /dev/null +++ b/kraken/resources/mesh/KRMeshBinding.h @@ -0,0 +1,46 @@ +// +// KRMeshBinding.h +// Kraken Engine +// +// Copyright 2025 Kearwood Gilbert. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other materials +// provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// The views and conclusions contained in the software and documentation are those of the +// authors and should not be interpreted as representing official policies, either expressed +// or implied, of Kearwood Gilbert. +// + +#pragma once + +#include "KREngine-common.h" +#include "resources/KRResourceBinding.h" + +class KRMesh; + +class KRMeshBinding : public KRResourceBinding +{ +public: + KRMesh* get(); + + bool load(KRContext* context) override final; +private: +}; \ No newline at end of file diff --git a/kraken/resources/mesh/KRMeshManager.cpp b/kraken/resources/mesh/KRMeshManager.cpp index b46bd42..6040478 100755 --- a/kraken/resources/mesh/KRMeshManager.cpp +++ b/kraken/resources/mesh/KRMeshManager.cpp @@ -55,9 +55,9 @@ KRMeshManager::KRMeshManager(KRContext& context) void KRMeshManager::init() { - addModel(new KRMeshCube(*m_pContext)); - addModel(new KRMeshQuad(*m_pContext)); - addModel(new KRMeshSphere(*m_pContext)); + addMesh(new KRMeshCube(*m_pContext)); + addMesh(new KRMeshQuad(*m_pContext)); + addMesh(new KRMeshSphere(*m_pContext)); // ---- Initialize stock models ---- static const float _KRENGINE_VBO_3D_CUBE_VERTEX_DATA[] = { @@ -114,87 +114,59 @@ void KRMeshManager::init() KRMeshManager::~KRMeshManager() { - for (unordered_multimap::iterator itr = m_models.begin(); itr != m_models.end(); ++itr) { + for (unordered_map::iterator itr = m_meshes.begin(); itr != m_meshes.end(); ++itr) { delete (*itr).second; } - m_models.clear(); + m_meshes.clear(); } KRResource* KRMeshManager::loadResource(const std::string& name, const std::string& extension, Block* data) { if (extension.compare("krmesh") == 0) { - return loadModel(name.c_str(), data); + return loadMesh(name.c_str(), data); } return nullptr; } KRResource* KRMeshManager::getResource(const std::string& name, const std::string& extension) { if (extension.compare("krmesh") == 0) { - std::string lodBaseName; - int lodCoverage; - KRMesh::parseName(name, lodBaseName, lodCoverage); - std::vector models = getModel(lodBaseName.c_str()); - for (KRMesh* mesh : models) { - if (mesh->getLODCoverage() == lodCoverage) { - return mesh; - } - } + return getMesh(name.c_str()); } return nullptr; } -KRMesh* KRMeshManager::loadModel(const char* szName, Block* pData) +KRMesh* KRMeshManager::loadMesh(const char* szName, Block* pData) { - KRMesh* pModel = new KRMesh(*m_pContext, szName, pData); - addModel(pModel); - return pModel; + KRMesh* mesh = new KRMesh(*m_pContext, szName, pData); + addMesh(mesh); + return mesh; } -void KRMeshManager::addModel(KRMesh* model) +void KRMeshManager::addMesh(KRMesh* mesh) { - std::string lowerName = model->getLODBaseName(); + std::string lowerName = mesh->getLODBaseName(); std::transform(lowerName.begin(), lowerName.end(), lowerName.begin(), ::tolower); - m_models.insert(std::pair(lowerName, model)); + m_meshes[lowerName] = mesh; } -KRMesh* KRMeshManager::getMaxLODModel(const char* szName) +KRMesh* KRMeshManager::getMesh(const char* szName) { - std::vector models = getModel(szName); - // models are always in order of highest LOD first - if (models.size()) { - return models[0]; + std::string lower_name = szName; + std::transform(lower_name.begin(), lower_name.end(), lower_name.begin(), ::tolower); + + unordered_map::iterator itr = m_meshes.find(lower_name); + if (itr == m_meshes.end()) { + KRContext::Log(KRContext::LOG_LEVEL_INFORMATION, "Model not found: %s", lower_name.c_str()); + return nullptr; } - return nullptr; + return itr->second; } -std::vector KRMeshManager::getModel(const char* szName) +unordered_map& KRMeshManager::getMeshes() { - std::string lowerName = szName; - std::transform(lowerName.begin(), lowerName.end(), - lowerName.begin(), ::tolower); - - - std::vector matching_models; - - std::pair::iterator, unordered_multimap::iterator> range = m_models.equal_range(lowerName); - for (unordered_multimap::iterator itr_match = range.first; itr_match != range.second; itr_match++) { - matching_models.push_back(itr_match->second); - } - - std::sort(matching_models.begin(), matching_models.end(), KRMesh::lod_sort_predicate); - - if (matching_models.size() == 0) { - KRContext::Log(KRContext::LOG_LEVEL_INFORMATION, "Model not found: %s", lowerName.c_str()); - } - - return matching_models; -} - -unordered_multimap& KRMeshManager::getModels() -{ - return m_models; + return m_meshes; } void KRMeshManager::bindVBO(VkCommandBuffer& commandBuffer, KRVBOData* vbo_data, float lodCoverage) diff --git a/kraken/resources/mesh/KRMeshManager.h b/kraken/resources/mesh/KRMeshManager.h index 5d53770..82abd78 100755 --- a/kraken/resources/mesh/KRMeshManager.h +++ b/kraken/resources/mesh/KRMeshManager.h @@ -58,13 +58,12 @@ public: void startFrame(float deltaTime); void endFrame(float deltaTime); - KRMesh* loadModel(const char* szName, mimir::Block* pData); - std::vector getModel(const char* szName); - KRMesh* getMaxLODModel(const char* szName); - void addModel(KRMesh* model); + KRMesh* loadMesh(const char* szName, mimir::Block* pData); + KRMesh* getMesh(const char* szName); + void addMesh(KRMesh* mesh); - std::vector getModelNames(); - unordered_multimap& getModels(); + std::vector getMeshNames(); + unordered_map& getMeshes(); class KRVBOData { @@ -217,7 +216,7 @@ private: mimir::Block KRENGINE_VBO_2D_SQUARE_VERTICES; __int32_t KRENGINE_VBO_2D_SQUARE_ATTRIBS; - unordered_multimap m_models; // Multiple models with the same name/key may be inserted, representing multiple LOD levels of the model + unordered_map m_meshes; long m_vboMemUsed; KRVBOData* m_currentVBO; diff --git a/kraken/resources/scene/KRScene.cpp b/kraken/resources/scene/KRScene.cpp index 4b2e169..4af4915 100755 --- a/kraken/resources/scene/KRScene.cpp +++ b/kraken/resources/scene/KRScene.cpp @@ -107,7 +107,7 @@ void KRScene::render(KRNode::RenderInfo& ri) // ---------- Start: Vulkan Debug Code ---------- /* if (ri.renderPass->getType() == RenderPassType::RENDER_PASS_FORWARD_OPAQUE) { - KRMesh* sphereMesh = getContext().getMeshManager()->getMaxLODModel("__sphere"); + KRMesh* sphereMesh = getContext().getMeshManager()->getMesh("__sphere"); if (sphereMesh && sphereMesh->isReady()) { PipelineInfo info{}; std::string shader_name("vulkan_test"); diff --git a/tests/smoke/hello_cube/hello_cube.cpp b/tests/smoke/hello_cube/hello_cube.cpp index fc7e2c2..6f9abf7 100644 --- a/tests/smoke/hello_cube/hello_cube.cpp +++ b/tests/smoke/hello_cube/hello_cube.cpp @@ -127,7 +127,7 @@ bool smoke_load() create_cube_info.newNodeHandle = kCubeNodeHandle; create_cube_info.sceneHandle = kSceneResourceHandle; create_cube_info.node.pName = "my_cube"; - create_cube_info.node.model.mesh = kCubeMeshResourceHandle; + create_cube_info.node.model.mesh[1] = kCubeMeshResourceHandle; res = KrCreateNode(&create_cube_info); assert(res == KR_SUCCESS);