diff --git a/include/rendering/wmo_renderer.hpp b/include/rendering/wmo_renderer.hpp index 136cbc0e..08108dc0 100644 --- a/include/rendering/wmo_renderer.hpp +++ b/include/rendering/wmo_renderer.hpp @@ -150,7 +150,8 @@ public: */ /** Pre-update mutable state (frame ID, material UBOs) on main thread before parallel render. */ void prepareRender(); - void render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet, const Camera& camera); + void render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet, const Camera& camera, + const glm::vec3* viewerPos = nullptr); /** * Initialize shadow pipeline (Phase 7) diff --git a/src/rendering/renderer.cpp b/src/rendering/renderer.cpp index 6da94182..71cb2a7c 100644 --- a/src/rendering/renderer.cpp +++ b/src/rendering/renderer.cpp @@ -2096,8 +2096,8 @@ void Renderer::updateCharacterAnimation() { // Rider uses character facing yaw, not mount bone rotation // (rider faces character direction, seat bone only provides position) float yawRad = glm::radians(characterYaw); - float riderPitch = taxiFlight_ ? mountPitch_ * 0.35f : 0.0f; - float riderRoll = taxiFlight_ ? mountRoll_ * 0.35f : 0.0f; + float riderPitch = mountPitch_ * 0.35f; + float riderRoll = mountRoll_ * 0.35f; characterRenderer->setInstanceRotation(characterInstanceId, glm::vec3(riderPitch, riderRoll, yawRad)); } else { // Fallback to old manual positioning if attachment not found @@ -4737,7 +4737,7 @@ void Renderer::renderWorld(game::World* world, game::GameHandler* gameHandler) { auto t0 = std::chrono::steady_clock::now(); VkCommandBuffer cmd = beginSecondary(SEC_WMO); setSecondaryViewportScissor(cmd); - wmoRenderer->render(cmd, perFrameSet, *camera); + wmoRenderer->render(cmd, perFrameSet, *camera, &characterPosition); vkEndCommandBuffer(cmd); return std::chrono::duration( std::chrono::steady_clock::now() - t0).count(); @@ -4905,7 +4905,7 @@ void Renderer::renderWorld(game::World* world, game::GameHandler* gameHandler) { if (wmoRenderer && camera && !skipWMO) { wmoRenderer->prepareRender(); auto wmoStart = std::chrono::steady_clock::now(); - wmoRenderer->render(currentCmd, perFrameSet, *camera); + wmoRenderer->render(currentCmd, perFrameSet, *camera, &characterPosition); lastWMORenderMs = std::chrono::duration( std::chrono::steady_clock::now() - wmoStart).count(); } diff --git a/src/rendering/wmo_renderer.cpp b/src/rendering/wmo_renderer.cpp index 3df2b3fd..85f56431 100644 --- a/src/rendering/wmo_renderer.cpp +++ b/src/rendering/wmo_renderer.cpp @@ -1356,7 +1356,8 @@ void WMORenderer::prepareRender() { } } -void WMORenderer::render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet, const Camera& camera) { +void WMORenderer::render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet, const Camera& camera, + const glm::vec3* viewerPos) { if (!opaquePipeline_ || instances.empty()) { lastDrawCalls = 0; return; @@ -1380,6 +1381,11 @@ void WMORenderer::render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet, const } glm::vec3 camPos = camera.getPosition(); + // For portal culling, use the character/player position when available. + // The 3rd-person camera can orbit outside a WMO while the character is inside, + // causing the portal traversal to start from outside and cull interior groups. + // Passing the actual character position as the viewer fixes this. + glm::vec3 portalViewerPos = viewerPos ? *viewerPos : camPos; bool doPortalCull = portalCulling; bool doDistanceCull = distanceCulling; @@ -1400,7 +1406,7 @@ void WMORenderer::render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet, const bool usePortalCulling = doPortalCull && !model.portals.empty() && !model.portalRefs.empty(); if (usePortalCulling) { std::unordered_set pvgSet; - glm::vec4 localCamPos = instance.invModelMatrix * glm::vec4(camPos, 1.0f); + glm::vec4 localCamPos = instance.invModelMatrix * glm::vec4(portalViewerPos, 1.0f); getVisibleGroupsViaPortals(model, glm::vec3(localCamPos), frustum, instance.modelMatrix, pvgSet); portalVisibleGroups.assign(pvgSet.begin(), pvgSet.end()); diff --git a/src/ui/game_screen.cpp b/src/ui/game_screen.cpp index c9c897fc..c1b1274f 100644 --- a/src/ui/game_screen.cpp +++ b/src/ui/game_screen.cpp @@ -1203,16 +1203,19 @@ void GameScreen::renderChatWindow(game::GameHandler& gameHandler) { } else if (msg.type == game::ChatType::TEXT_EMOTE) { renderTextWithLinks(tsPrefix + processedMessage, color); } else if (!msg.senderName.empty()) { - if (msg.type == game::ChatType::MONSTER_SAY || msg.type == game::ChatType::MONSTER_PARTY) { + if (msg.type == game::ChatType::SAY || + msg.type == game::ChatType::MONSTER_SAY || msg.type == game::ChatType::MONSTER_PARTY) { std::string fullMsg = tsPrefix + msg.senderName + " says: " + processedMessage; renderTextWithLinks(fullMsg, color); - } else if (msg.type == game::ChatType::MONSTER_YELL) { + } else if (msg.type == game::ChatType::YELL || msg.type == game::ChatType::MONSTER_YELL) { std::string fullMsg = tsPrefix + msg.senderName + " yells: " + processedMessage; renderTextWithLinks(fullMsg, color); - } else if (msg.type == game::ChatType::MONSTER_WHISPER || msg.type == game::ChatType::RAID_BOSS_WHISPER) { + } else if (msg.type == game::ChatType::WHISPER || + msg.type == game::ChatType::MONSTER_WHISPER || msg.type == game::ChatType::RAID_BOSS_WHISPER) { std::string fullMsg = tsPrefix + msg.senderName + " whispers: " + processedMessage; renderTextWithLinks(fullMsg, color); - } else if (msg.type == game::ChatType::MONSTER_EMOTE || msg.type == game::ChatType::RAID_BOSS_EMOTE) { + } else if (msg.type == game::ChatType::EMOTE || + msg.type == game::ChatType::MONSTER_EMOTE || msg.type == game::ChatType::RAID_BOSS_EMOTE) { std::string fullMsg = tsPrefix + msg.senderName + " " + processedMessage; renderTextWithLinks(fullMsg, color); } else if (msg.type == game::ChatType::CHANNEL && !msg.channelName.empty()) {