diff --git a/resources/config2/gameobjects_uni_interior.json b/resources/config2/gameobjects_uni_interior.json index 8c0f464..90ff82d 100644 --- a/resources/config2/gameobjects_uni_interior.json +++ b/resources/config2/gameobjects_uni_interior.json @@ -27,6 +27,7 @@ { "name": "inai", "texturePath": "resources/w/exterior/Building_work014.png", + "textureDarkandsPath" : "resources/w/interior/darklands_generated_building001.png", "meshPath": "resources/w/interior/Building_002.txt", "rotationX": 0.0, "rotationY": 0.0, diff --git a/resources/shaders/darklands_flash_desktop.fragment b/resources/shaders/darklands_flash_desktop.fragment new file mode 100644 index 0000000..636c618 --- /dev/null +++ b/resources/shaders/darklands_flash_desktop.fragment @@ -0,0 +1,6 @@ +uniform float uAlpha; + +void main() +{ + gl_FragColor = vec4(1.0, 1.0, 1.0, uAlpha); +} diff --git a/resources/shaders/darklands_flash_web.fragment b/resources/shaders/darklands_flash_web.fragment new file mode 100644 index 0000000..bddf49e --- /dev/null +++ b/resources/shaders/darklands_flash_web.fragment @@ -0,0 +1,7 @@ +precision mediump float; +uniform float uAlpha; + +void main() +{ + gl_FragColor = vec4(1.0, 1.0, 1.0, uAlpha); +} diff --git a/resources/shaders/darklands_fog.vertex b/resources/shaders/darklands_fog.vertex new file mode 100644 index 0000000..9bcfd39 --- /dev/null +++ b/resources/shaders/darklands_fog.vertex @@ -0,0 +1,16 @@ +attribute vec3 vPosition; +attribute vec2 vTexCoord; +varying vec2 texCoord; +varying float fogDistance; + +uniform mat4 ProjectionModelViewMatrix; +uniform mat4 ModelViewMatrix; +uniform vec3 uPlayerEyePos; + +void main() +{ + vec4 eyePos = ModelViewMatrix * vec4(vPosition.xyz, 1.0); + fogDistance = length(eyePos.xyz - uPlayerEyePos); + gl_Position = ProjectionModelViewMatrix * vec4(vPosition.xyz, 1.0); + texCoord = vTexCoord; +} diff --git a/resources/shaders/darklands_fog_desktop.fragment b/resources/shaders/darklands_fog_desktop.fragment new file mode 100644 index 0000000..8e02a6e --- /dev/null +++ b/resources/shaders/darklands_fog_desktop.fragment @@ -0,0 +1,21 @@ +//precisionmediump float; +uniform sampler2D Texture; +uniform float uAlpha; +varying vec2 texCoord; +varying float fogDistance; + +void main() +{ + vec4 color = texture2D(Texture, texCoord).rgba; + + if (color.a < 0.1) + discard; + + vec3 tinted = mix(color.rgb, vec3(0.5, 0.5, 0.8), 0.3); + + vec3 fogColor = vec3(0.05, 0.05, 0.2); + float fogFactor = clamp((fogDistance - 5.0) / 10.0, 0.0, 1.0); + vec3 finalColor = mix(tinted, fogColor, fogFactor); + + gl_FragColor = vec4(finalColor, color.a * uAlpha); +} diff --git a/resources/shaders/darklands_fog_skinning.vertex b/resources/shaders/darklands_fog_skinning.vertex new file mode 100644 index 0000000..a251224 --- /dev/null +++ b/resources/shaders/darklands_fog_skinning.vertex @@ -0,0 +1,55 @@ +attribute vec3 vPosition; +attribute vec2 vTexCoord; +attribute vec4 aBoneIndices0; +attribute vec2 aBoneIndices1; +attribute vec4 aBoneWeights0; +attribute vec2 aBoneWeights1; + +varying vec2 texCoord; +varying float fogDistance; + +uniform mat4 ProjectionModelViewMatrix; +uniform mat4 ModelViewMatrix; +uniform mat4 uBoneMatrices[64]; +uniform vec3 uPlayerEyePos; + +void main() +{ + vec4 skinnedPos = vec4(0.0, 0.0, 0.0, 0.0); + vec4 originalPos = vec4(vPosition, 1.0); + float totalWeight = 0.0; + + if (aBoneWeights0.x > 0.0) { + skinnedPos += uBoneMatrices[int(aBoneIndices0.x)] * originalPos * aBoneWeights0.x; + totalWeight += aBoneWeights0.x; + } + if (aBoneWeights0.y > 0.0) { + skinnedPos += uBoneMatrices[int(aBoneIndices0.y)] * originalPos * aBoneWeights0.y; + totalWeight += aBoneWeights0.y; + } + if (aBoneWeights0.z > 0.0) { + skinnedPos += uBoneMatrices[int(aBoneIndices0.z)] * originalPos * aBoneWeights0.z; + totalWeight += aBoneWeights0.z; + } + if (aBoneWeights0.w > 0.0) { + skinnedPos += uBoneMatrices[int(aBoneIndices0.w)] * originalPos * aBoneWeights0.w; + totalWeight += aBoneWeights0.w; + } + if (aBoneWeights1.x > 0.0) { + skinnedPos += uBoneMatrices[int(aBoneIndices1.x)] * originalPos * aBoneWeights1.x; + totalWeight += aBoneWeights1.x; + } + if (aBoneWeights1.y > 0.0) { + skinnedPos += uBoneMatrices[int(aBoneIndices1.y)] * originalPos * aBoneWeights1.y; + totalWeight += aBoneWeights1.y; + } + + if (totalWeight < 0.001) { + skinnedPos = originalPos; + } + + vec4 eyePos = ModelViewMatrix * skinnedPos; + fogDistance = length(eyePos.xyz - uPlayerEyePos); + gl_Position = ProjectionModelViewMatrix * skinnedPos; + texCoord = vTexCoord; +} diff --git a/resources/shaders/darklands_fog_web.fragment b/resources/shaders/darklands_fog_web.fragment new file mode 100644 index 0000000..8a71230 --- /dev/null +++ b/resources/shaders/darklands_fog_web.fragment @@ -0,0 +1,21 @@ +precision mediump float; +uniform sampler2D Texture; +uniform float uAlpha; +varying vec2 texCoord; +varying float fogDistance; + +void main() +{ + vec4 color = texture2D(Texture, texCoord).rgba; + + if (color.a < 0.1) + discard; + + vec3 tinted = mix(color.rgb, vec3(0.5, 0.5, 0.8), 0.3); + + vec3 fogColor = vec3(0.05, 0.05, 0.2); + float fogFactor = clamp((fogDistance - 5.0) / 10.0, 0.0, 1.0); + vec3 finalColor = mix(tinted, fogColor, fogFactor); + + gl_FragColor = vec4(finalColor, color.a * uAlpha); +} diff --git a/resources/w/interior/darklands_generated_building001.png b/resources/w/interior/darklands_generated_building001.png new file mode 100644 index 0000000..7c966b0 --- /dev/null +++ b/resources/w/interior/darklands_generated_building001.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:066cad8017dc60069fdedad5010832c6f657f2d2170df46561ead362ceaa24c0 +size 5951490 diff --git a/src/Character.cpp b/src/Character.cpp index 716a4b2..6331d1b 100644 --- a/src/Character.cpp +++ b/src/Character.cpp @@ -7,6 +7,7 @@ #include "GameConstants.h" #include "Environment.h" + namespace ZL { const float ATTACK_COOLDOWN_TIME = 1.6f; diff --git a/src/Game.cpp b/src/Game.cpp index b385dd4..010ab3a 100644 --- a/src/Game.cpp +++ b/src/Game.cpp @@ -144,6 +144,9 @@ namespace ZL renderer.shaderManager.AddShaderFromFiles("skinning", "resources/shaders/skinning.vertex", "resources/shaders/default_web.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("fog", "resources/shaders/fog.vertex", "resources/shaders/fog_web.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("fog_skinning", "resources/shaders/fog_skinning.vertex", "resources/shaders/fog_web.fragment", CONST_ZIP_FILE); + renderer.shaderManager.AddShaderFromFiles("darklands_fog", "resources/shaders/darklands_fog.vertex", "resources/shaders/darklands_fog_web.fragment", CONST_ZIP_FILE); + renderer.shaderManager.AddShaderFromFiles("darklands_fog_skinning", "resources/shaders/darklands_fog_skinning.vertex", "resources/shaders/darklands_fog_web.fragment", CONST_ZIP_FILE); + renderer.shaderManager.AddShaderFromFiles("darklands_flash", "resources/shaders/default.vertex", "resources/shaders/darklands_flash_web.fragment", CONST_ZIP_FILE); #else renderer.shaderManager.AddShaderFromFiles("env_sky", "resources/shaders/env_sky.vertex", "resources/shaders/env_sky_desktop.fragment", CONST_ZIP_FILE); @@ -155,6 +158,9 @@ namespace ZL renderer.shaderManager.AddShaderFromFiles("skinning", "resources/shaders/skinning.vertex", "resources/shaders/default_desktop.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("fog", "resources/shaders/fog.vertex", "resources/shaders/fog_desktop.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("fog_skinning", "resources/shaders/fog_skinning.vertex", "resources/shaders/fog_desktop.fragment", CONST_ZIP_FILE); + renderer.shaderManager.AddShaderFromFiles("darklands_fog", "resources/shaders/darklands_fog.vertex", "resources/shaders/darklands_fog_desktop.fragment", CONST_ZIP_FILE); + renderer.shaderManager.AddShaderFromFiles("darklands_fog_skinning", "resources/shaders/darklands_fog_skinning.vertex", "resources/shaders/darklands_fog_desktop.fragment", CONST_ZIP_FILE); + renderer.shaderManager.AddShaderFromFiles("darklands_flash", "resources/shaders/default.vertex", "resources/shaders/darklands_flash_desktop.fragment", CONST_ZIP_FILE); #endif std::cout << "Load resurces step 4" << std::endl; @@ -343,7 +349,11 @@ namespace ZL { if (currentLocation) { - if (currentLocation->shadowMap) { + if (currentLocation->isDarklands) { + currentLocation->drawGameDarklands(); + CheckGlError(__FILE__, __LINE__); + } + else if (currentLocation->shadowMap) { CheckGlError(__FILE__, __LINE__); currentLocation->drawShadowDepthPass(); CheckGlError(__FILE__, __LINE__); @@ -532,6 +542,10 @@ namespace ZL if (event.type == SDL_KEYDOWN && event.key.repeat == 0) { switch (event.key.keysym.sym) { + case SDLK_0: + if (currentLocation) + currentLocation->startDarklandsTransition(); + break; case SDLK_1: //if (audioPlayer) audioPlayer->playSoundAsync("audio/background.wav"); break; diff --git a/src/Location.cpp b/src/Location.cpp index a1b0144..0c8a736 100644 --- a/src/Location.cpp +++ b/src/Location.cpp @@ -270,6 +270,77 @@ namespace ZL } } + bool Location::startDarklandsTransition() + { + if (darklandsFlashActive) return false; + darklandsFlashActive = true; + darklandsFlashFadingIn = true; + darklandsFlashAlpha = 0.0f; + return true; + } + + void Location::updateDarklandsFlash(int64_t deltaMs) + { + if (!darklandsFlashActive) return; + static constexpr float kFadeDurationMs = 500.0f; + const float step = static_cast(deltaMs) / kFadeDurationMs; + + if (darklandsFlashFadingIn) { + darklandsFlashAlpha = min(darklandsFlashAlpha + step, 1.0f); + if (darklandsFlashAlpha >= 1.0f) { + isDarklands = !isDarklands; + darklandsFlashFadingIn = false; + } + } else { + darklandsFlashAlpha = max(darklandsFlashAlpha - step, 0.0f); + if (darklandsFlashAlpha <= 0.0f) { + darklandsFlashAlpha = 0.0f; + darklandsFlashActive = false; + } + } + } + + void Location::drawDarklandsFlash() + { + if (darklandsFlashAlpha <= 0.001f) return; + + const float W = static_cast(Environment::projectionWidth); + const float H = static_cast(Environment::projectionHeight); + + if (darklandsFlashQuadW != W || darklandsFlashQuadH != H) { + darklandsFlashQuadW = W; + darklandsFlashQuadH = H; + VertexDataStruct data; + data.PositionData = { + {0.f, 0.f, 0.f}, {0.f, H, 0.f}, {W, H, 0.f}, + {W, H, 0.f}, {W, 0.f, 0.f}, {0.f, 0.f, 0.f} + }; + data.TexCoordData = { + {0.f, 0.f}, {0.f, 1.f}, {1.f, 1.f}, + {1.f, 1.f}, {1.f, 0.f}, {0.f, 0.f} + }; + darklandsFlashQuad.data = std::move(data); + darklandsFlashQuad.RefreshVBO(); + } + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + renderer.shaderManager.PushShader("darklands_flash"); + renderer.PushProjectionMatrix(0.0f, W, 0.0f, H, -10.0f, 10.0f); + renderer.PushMatrix(); + renderer.LoadIdentity(); + + renderer.RenderUniform1f("uAlpha", darklandsFlashAlpha); + renderer.DrawVertexRenderStruct(darklandsFlashQuad); + + renderer.PopMatrix(); + renderer.PopProjectionMatrix(); + renderer.shaderManager.PopShader(); + + glDisable(GL_BLEND); + } + void Location::setupNavigation(const std::vector& paths) { navigationMapPaths = paths; @@ -520,6 +591,12 @@ namespace ZL continue; } + if (isDarklands) { + if (!intObj.loadedObject.textureDarklands) continue; + } else { + if (!intObj.loadedObject.texture) continue; + } + //std::cout << "[RAYCAST] Position: (" << intObj.position.x() << ", " << intObj.position.y() << ", " // << intObj.position.z() << "), Radius: " << intObj.interactionRadius << std::endl; @@ -656,6 +733,7 @@ namespace ZL renderer.RenderUniform1f("uAlpha", 1.0f); for (auto& [name, gameObj] : gameObjects) { + if (!gameObj.texture) continue; glBindTexture(GL_TEXTURE_2D, gameObj.texture->getTexID()); renderer.DrawVertexRenderStruct(gameObj.mesh); } @@ -699,6 +777,7 @@ namespace ZL if (npc) npc->drawHealthBar(renderer, currentView, proj); } } + drawDarklandsFlash(); } void Location::drawShadowDepthPass() @@ -835,6 +914,7 @@ namespace ZL CheckGlError(__FILE__, __LINE__); renderer.RenderUniform1f("uAlpha", 1.0f); for (auto& [name, gameObj] : gameObjects) { + if (!gameObj.texture) continue; glBindTexture(GL_TEXTURE_2D, gameObj.texture->getTexID()); renderer.DrawVertexRenderStruct(gameObj.mesh); } @@ -897,6 +977,77 @@ namespace ZL if (npc) npc->drawHealthBar(renderer, cameraViewMatrix, proj); } } + drawDarklandsFlash(); + } + + void Location::drawGameDarklands() + { + glClearColor(0.05f, 0.05f, 0.2f, 1.0f); + glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); + + renderer.shaderManager.PushShader("darklands_fog"); + renderer.RenderUniform1i(textureUniformName, 0); + const float playerEyePos[3] = { 0.0f, 0.0f, -Environment::zoom }; + renderer.RenderUniform3fv("uPlayerEyePos", playerEyePos); + + renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5, + static_cast(Environment::width) / static_cast(Environment::height), + Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR); + renderer.PushMatrix(); + + renderer.LoadIdentity(); + renderer.TranslateMatrix({ 0, 0, -1.0f * Environment::zoom }); + + renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(cameraInclination, Eigen::Vector3f::UnitX())).toRotationMatrix()); + renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(cameraAzimuth, Eigen::Vector3f::UnitY())).toRotationMatrix()); + const Eigen::Vector3f& camTarget = player ? player->position : Eigen::Vector3f::Zero(); + renderer.TranslateMatrix({ -camTarget.x(), -camTarget.y(), -camTarget.z() }); + + renderer.RenderUniform1f("uAlpha", 1.0f); + for (auto& [name, gameObj] : gameObjects) { + if (!gameObj.textureDarklands) continue; + glBindTexture(GL_TEXTURE_2D, gameObj.textureDarklands->getTexID()); + renderer.DrawVertexRenderStruct(gameObj.mesh); + } + + for (auto& intObj : interactiveObjects) { + if (intObj.isActive) { + intObj.drawDarklands(renderer); + } + } + renderer.RenderUniform1f("uAlpha", 1.0f); + const Eigen::Matrix4f currentView = renderer.GetCurrentModelViewMatrix(); + if (player) player->prepareHitSparksForDraw(currentView); + for (auto& npc : npcs) npc->prepareHitSparksForDraw(currentView); + for (auto& tz : teleportZones) tz.prepareForDraw(currentView); + + if (player) player->draw(renderer); + for (auto& npc : npcs) npc->draw(renderer); + + for (auto& tz : teleportZones) tz.draw(renderer, Environment::zoom, Environment::width, Environment::height); + + if (navigationEditorMode) { + navigationEditorDrawNavigation(); + navigationEditorDrawPoints(); + } + + renderer.PopMatrix(); + renderer.PopProjectionMatrix(); + renderer.shaderManager.PopShader(); + + if (npcNameText) { + Eigen::Matrix4f proj = MakePerspectiveMatrix(1.0f / 1.5f, + static_cast(Environment::width) / static_cast(Environment::height), + Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR); + for (auto& npc : npcs) { + if (npc) npc->drawName(*npcNameText, currentView, proj); + } + if (player) player->drawHealthBar(renderer, currentView, proj); + for (auto& npc : npcs) { + if (npc) npc->drawHealthBar(renderer, currentView, proj); + } + } + drawDarklandsFlash(); } bool Location::setNavigationAreaAvailable(const std::string& areaName, bool available) @@ -1057,6 +1208,8 @@ namespace ZL void Location::update(int64_t delta) { + updateDarklandsFlash(delta); + for (auto& intObj : interactiveObjects) { intObj.update(delta); } diff --git a/src/Location.h b/src/Location.h index 06883f6..0b9d82b 100644 --- a/src/Location.h +++ b/src/Location.h @@ -83,6 +83,11 @@ namespace ZL std::vector triggerZones; + bool isDarklands = false; + + // Returns false if a transition is already in progress (guards against double-press). + bool startDarklandsTransition(); + // Navigation editor — toggle with 'N', save with 'B', right-click to finalize polygon bool navigationEditorMode = false; @@ -117,6 +122,7 @@ namespace ZL void drawGame(); void drawShadowDepthPass(); void drawGameWithShadows(); + void drawGameDarklands(); bool setNavigationAreaAvailable(const std::string& areaName, bool available); @@ -137,6 +143,17 @@ namespace ZL Inventory& inventory; private: + // White-flash transition state for darklands toggle + float darklandsFlashAlpha = 0.0f; + bool darklandsFlashActive = false; + bool darklandsFlashFadingIn = true; + VertexRenderStruct darklandsFlashQuad; + float darklandsFlashQuadW = -1.0f; + float darklandsFlashQuadH = -1.0f; + + void updateDarklandsFlash(int64_t deltaMs); + void drawDarklandsFlash(); + void resolveCharacterCollisions(); void updateDynamicReplans(int64_t deltaMs); void loadTeleportZones(const std::string& jsonPath, const char* zipFile); diff --git a/src/items/GameObjectLoader.cpp b/src/items/GameObjectLoader.cpp index f54fa73..a38e786 100644 --- a/src/items/GameObjectLoader.cpp +++ b/src/items/GameObjectLoader.cpp @@ -45,9 +45,10 @@ namespace ZL { static GameObjectData parseGameObjectData(const json& item) { GameObjectData data; - data.name = item.value("name", "Unknown"); - data.texturePath = item.value("texturePath", ""); - data.meshPath = item.value("meshPath", ""); + data.name = item.value("name", "Unknown"); + data.texturePath = item.value("texturePath", ""); + data.textureDarkandsPath = item.value("textureDarkandsPath", ""); + data.meshPath = item.value("meshPath", ""); data.rotationX = item.value("rotationX", 0.0f); data.rotationY = item.value("rotationY", 0.0f); data.rotationZ = item.value("rotationZ", 0.0f); @@ -110,6 +111,8 @@ namespace ZL { obj.name = data.name; obj.texture = renderer.textureManager.LoadFromPng(data.texturePath, zipPath); + if (!data.textureDarkandsPath.empty()) + obj.textureDarklands = renderer.textureManager.LoadFromPng(data.textureDarkandsPath, zipPath); if (data.meshPath.size() > 4 && data.meshPath.compare(data.meshPath.size() - 4, 4, ".bin") == 0) obj.mesh.data = LoadModelFromBinFile(data.meshPath, zipPath); diff --git a/src/items/GameObjectLoader.h b/src/items/GameObjectLoader.h index ac0e356..aa1c269 100644 --- a/src/items/GameObjectLoader.h +++ b/src/items/GameObjectLoader.h @@ -15,6 +15,7 @@ namespace ZL { struct GameObjectData { std::string name; std::string texturePath; + std::string textureDarkandsPath; std::string meshPath; float rotationX = 0.0f; float rotationY = 0.0f; diff --git a/src/items/InteractiveObject.cpp b/src/items/InteractiveObject.cpp index f670f2d..b30cef7 100644 --- a/src/items/InteractiveObject.cpp +++ b/src/items/InteractiveObject.cpp @@ -117,6 +117,32 @@ namespace ZL { } } + void InteractiveObject::drawDarklands(Renderer& renderer) const { + if (!isActive || !loadedObject.textureDarklands) return; + + if (alpha < 0.999f) { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + + renderer.PushMatrix(); + renderer.TranslateMatrix(position); + if (rotationY != 0.f) { + renderer.TranslateMatrix(pivot); + renderer.RotateMatrix(Eigen::AngleAxisf(rotationY, Eigen::Vector3f::UnitY()).toRotationMatrix()); + renderer.TranslateMatrix(-pivot); + } + if (scale != 1.f) + renderer.ScaleMatrix(scale); + renderer.RenderUniform1i(textureUniformName, 0); + renderer.RenderUniform1f("uAlpha", alpha); + glBindTexture(GL_TEXTURE_2D, loadedObject.textureDarklands->getTexID()); + renderer.DrawVertexRenderStruct(loadedObject.mesh); + renderer.PopMatrix(); + + renderer.RenderUniform1f("uAlpha", 1.0f); + } + void InteractiveObject::draw(Renderer& renderer) const { if (!isActive || !loadedObject.texture) return; diff --git a/src/items/InteractiveObject.h b/src/items/InteractiveObject.h index 71869d2..4ef4c51 100644 --- a/src/items/InteractiveObject.h +++ b/src/items/InteractiveObject.h @@ -13,6 +13,7 @@ namespace ZL { struct LoadedGameObject { std::shared_ptr texture; + std::shared_ptr textureDarklands; VertexRenderStruct mesh; std::string name; }; @@ -52,6 +53,7 @@ namespace ZL { void fadeTo(float targetAlpha, float durationSec, std::function onComplete = {}); void update(int64_t deltaMs); void draw(Renderer& renderer) const; + void drawDarklands(Renderer& renderer) const; }; } // namespace ZL