diff --git a/include/rendering/m2_renderer.hpp b/include/rendering/m2_renderer.hpp index 8f3652e4..fcda4b89 100644 --- a/include/rendering/m2_renderer.hpp +++ b/include/rendering/m2_renderer.hpp @@ -60,6 +60,7 @@ struct M2ModelGPU { bool collisionNoBlock = false; bool collisionStatue = false; bool isSmallFoliage = false; // Small foliage (bushes, grass, plants) - skip during taxi + bool isInvisibleTrap = false; // Invisible trap objects (don't render, no collision) // Collision mesh with spatial grid (from M2 bounding geometry) struct CollisionMesh { diff --git a/src/rendering/m2_renderer.cpp b/src/rendering/m2_renderer.cpp index 8e1b2dbf..4a98ba25 100644 --- a/src/rendering/m2_renderer.cpp +++ b/src/rendering/m2_renderer.cpp @@ -792,6 +792,16 @@ bool M2Renderer::loadModel(const pipeline::M2Model& model, uint32_t modelId) { M2ModelGPU gpuModel; gpuModel.name = model.name; + + // Detect invisible trap models (event objects that should not render or collide) + std::string lowerName = model.name; + std::transform(lowerName.begin(), lowerName.end(), lowerName.begin(), + [](unsigned char c) { return static_cast(std::tolower(c)); }); + bool isInvisibleTrap = (lowerName.find("invisibletrap") != std::string::npos); + gpuModel.isInvisibleTrap = isInvisibleTrap; + if (isInvisibleTrap) { + LOG_INFO("Loading InvisibleTrap model: ", model.name, " (will be invisible, no collision)"); + } // Use tight bounds from actual vertices for collision/camera occlusion. // Header bounds in some M2s are overly conservative. glm::vec3 tightMin( std::numeric_limits::max()); @@ -1045,10 +1055,22 @@ bool M2Renderer::loadModel(const pipeline::M2Model& model, uint32_t modelId) { // Load ALL textures from the model into a local vector std::vector allTextures; if (assetManager) { - for (const auto& tex : model.textures) { + for (size_t ti = 0; ti < model.textures.size(); ti++) { + const auto& tex = model.textures[ti]; if (!tex.filename.empty()) { - allTextures.push_back(loadTexture(tex.filename)); + GLuint texId = loadTexture(tex.filename); + if (texId == whiteTexture) { + LOG_WARNING("M2 model ", model.name, " texture[", ti, "] failed to load: ", tex.filename); + } + if (isInvisibleTrap) { + LOG_INFO(" InvisibleTrap texture[", ti, "]: ", tex.filename, " -> ", (texId == whiteTexture ? "WHITE" : "OK")); + } + allTextures.push_back(texId); } else { + LOG_WARNING("M2 model ", model.name, " texture[", ti, "] has empty filename (using white fallback)"); + if (isInvisibleTrap) { + LOG_INFO(" InvisibleTrap texture[", ti, "]: EMPTY (using white fallback)"); + } allTextures.push_back(whiteTexture); } } @@ -1669,7 +1691,7 @@ void M2Renderer::render(const Camera& camera, const glm::mat4& view, const glm:: auto it = models.find(instance.modelId); if (it == models.end()) continue; const M2ModelGPU& model = it->second; - if (!model.isValid() || model.isSmoke) continue; + if (!model.isValid() || model.isSmoke || model.isInvisibleTrap) continue; glm::vec3 toCam = instance.position - camPos; float distSq = glm::dot(toCam, toCam); @@ -2671,7 +2693,7 @@ std::optional M2Renderer::getFloorHeight(float glX, float glY, float glZ) if (instance.scale <= 0.001f) continue; const M2ModelGPU& model = it->second; - if (model.collisionNoBlock) continue; + if (model.collisionNoBlock || model.isInvisibleTrap) continue; // --- Mesh-based floor: vertical ray vs collision triangles --- // Does NOT skip the AABB path — both contribute and highest wins. @@ -2818,7 +2840,7 @@ bool M2Renderer::checkCollision(const glm::vec3& from, const glm::vec3& to, if (it == models.end()) continue; const M2ModelGPU& model = it->second; - if (model.collisionNoBlock) continue; + if (model.collisionNoBlock || model.isInvisibleTrap) continue; if (instance.scale <= 0.001f) continue; // --- Mesh-based wall collision: closest-point push --- @@ -3058,7 +3080,7 @@ float M2Renderer::raycastBoundingBoxes(const glm::vec3& origin, const glm::vec3& if (it == models.end()) continue; const M2ModelGPU& model = it->second; - if (model.collisionNoBlock) continue; + if (model.collisionNoBlock || model.isInvisibleTrap) continue; glm::vec3 localMin, localMax; getTightCollisionBounds(model, localMin, localMax); // Skip tiny doodads for camera occlusion; they cause jitter and false hits. diff --git a/test_gameobject_display.cpp b/test_gameobject_display.cpp new file mode 100644 index 00000000..afc23233 --- /dev/null +++ b/test_gameobject_display.cpp @@ -0,0 +1,36 @@ +#include +#include "pipeline/dbc.hpp" +#include "pipeline/asset_manager.hpp" + +int main() { + wowee::pipeline::AssetManager assetManager; + assetManager.initialize("Data"); + + auto godi = assetManager.loadDBC("GameObjectDisplayInfo.dbc"); + if (!godi || !godi->isLoaded()) { + std::cerr << "Failed to load GameObjectDisplayInfo.dbc\n"; + return 1; + } + + std::cout << "GameObjectDisplayInfo.dbc loaded with " << godi->getRecordCount() << " records\n\n"; + + // Check displayIds 35 and 1287 + uint32_t targetIds[] = {35, 1287}; + for (uint32_t targetId : targetIds) { + bool found = false; + for (uint32_t i = 0; i < godi->getRecordCount(); i++) { + uint32_t displayId = godi->getUInt32(i, 0); + if (displayId == targetId) { + std::string modelName = godi->getString(i, 1); + std::cout << "DisplayId " << displayId << ": " << modelName << "\n"; + found = true; + break; + } + } + if (!found) { + std::cout << "DisplayId " << targetId << ": NOT FOUND\n"; + } + } + + return 0; +}