merge
This commit is contained in:
commit
3d3c3b2a36
20 files changed
+1036
-5351
No files matched your search
@@ -0,0 +1,661 @@
|
||||
#include "Location.h"
|
||||
#include "utils/Utils.h"
|
||||
#include "render/OpenGlExtensions.h"
|
||||
#include <iostream>
|
||||
#include "render/TextureManager.h"
|
||||
#include "TextModel.h"
|
||||
#include <random>
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#include "GameConstants.h"
|
||||
|
||||
|
||||
|
||||
namespace ZL
|
||||
{
|
||||
extern const char* CONST_ZIP_FILE;
|
||||
|
||||
static constexpr float CAMERA_FOV_Y = 1.0f / 1.5f;
|
||||
|
||||
Location::Location(Renderer& iRenderer, Inventory& iInventory)
|
||||
: renderer(iRenderer)
|
||||
, inventory(iInventory)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Location::setup()
|
||||
{
|
||||
roomTexture = std::make_unique<Texture>(CreateTextureDataFromPng("resources/w/room005.png", CONST_ZIP_FILE));
|
||||
roomMesh.data = LoadFromTextFile02("resources/w/room001.txt", CONST_ZIP_FILE);
|
||||
roomMesh.data.RotateByMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-M_PI * 0.5, Eigen::Vector3f::UnitY())).toRotationMatrix());
|
||||
roomMesh.RefreshVBO();
|
||||
|
||||
// Load static game objects
|
||||
gameObjects = GameObjectLoader::loadAndCreateGameObjects("resources/config2/gameobjects.json", renderer, CONST_ZIP_FILE);
|
||||
|
||||
// Load interactive objects
|
||||
interactiveObjects = GameObjectLoader::loadAndCreateInteractiveObjects("resources/config2/gameobjects.json", renderer, CONST_ZIP_FILE);
|
||||
|
||||
|
||||
auto playerTexture = std::make_shared<Texture>(CreateTextureDataFromPng("resources/w/gg/IMG_20260413_182354_992.png", CONST_ZIP_FILE));
|
||||
|
||||
player = std::make_unique<Character>();
|
||||
|
||||
player->loadBinaryAnimation(AnimationState::STAND, "resources/w/gg/gg_stand_idle001.anim", CONST_ZIP_FILE);
|
||||
player->loadBinaryAnimation(AnimationState::WALK, "resources/w/gg/gg_walking001.anim", CONST_ZIP_FILE);
|
||||
player->loadBinaryAnimation(AnimationState::STAND_TO_ACTION, "resources/w/gg/gg_stand_to_action002.anim", CONST_ZIP_FILE);
|
||||
player->loadBinaryAnimation(AnimationState::ACTION_ATTACK, "resources/w/gg/gg_action_attack001.anim", CONST_ZIP_FILE);
|
||||
player->loadBinaryAnimation(AnimationState::ACTION_IDLE, "resources/w/gg/gg_action_idle001.anim", CONST_ZIP_FILE);
|
||||
player->loadBinaryAnimation(AnimationState::ACTION_TO_STAND, "resources/w/gg/gg_action_to_stand001.anim", CONST_ZIP_FILE);
|
||||
|
||||
player->setTexture(playerTexture);
|
||||
player->walkSpeed = 3.0f;
|
||||
player->rotationSpeed = 8.0f;
|
||||
|
||||
player->modelScale = 1.f;
|
||||
player->modelCorrectionRotation = Eigen::Quaternionf(Eigen::AngleAxisf(M_PI, Eigen::Vector3f::UnitY()));
|
||||
|
||||
player->canAttack = true;
|
||||
player->isPlayer = true;
|
||||
std::cout << "Load resurces step 9" << std::endl;
|
||||
|
||||
// Load NPCs from JSON
|
||||
npcs = GameObjectLoader::loadAndCreateNpcs("resources/config2/npcs.json", CONST_ZIP_FILE);
|
||||
|
||||
auto ghostTexture = std::make_shared<Texture>(CreateTextureDataFromPng("resources/w/ghost_skin001.png", CONST_ZIP_FILE));
|
||||
|
||||
std::cout << "Load resurces step 11" << std::endl;
|
||||
auto npc02 = std::make_unique<Character>();
|
||||
|
||||
npc02->loadBinaryAnimation(AnimationState::STAND, "resources/w/default_float001.anim", CONST_ZIP_FILE);
|
||||
npc02->loadBinaryAnimation(AnimationState::WALK, "resources/w/default_float001.anim", CONST_ZIP_FILE);
|
||||
npc02->loadBinaryAnimation(AnimationState::ACTION_IDLE, "resources/w/float_attack003_cut.anim", CONST_ZIP_FILE);
|
||||
npc02->loadBinaryAnimation(AnimationState::ACTION_ATTACK, "resources/w/float_attack003.anim", CONST_ZIP_FILE);
|
||||
npc02->loadBinaryAnimation(AnimationState::STAND_TO_ACTION, "resources/w/default_float001_cut.anim", CONST_ZIP_FILE);
|
||||
npc02->loadBinaryAnimation(AnimationState::ACTION_TO_STAND, "resources/w/default_float001_cut.anim", CONST_ZIP_FILE);
|
||||
npc02->setTexture(ghostTexture);
|
||||
npc02->walkSpeed = 1.5f;
|
||||
npc02->rotationSpeed = 8.0f;
|
||||
npc02->modelScale = 0.01f;
|
||||
//npc02->modelScale = 0.1f;
|
||||
npc02->modelCorrectionRotation = Eigen::Quaternionf(Eigen::AngleAxisf(M_PI, Eigen::Vector3f::UnitY()));
|
||||
|
||||
npc02->position = Eigen::Vector3f(0.f, 0.f, -20.f);
|
||||
npc02->setTarget(npc02->position);
|
||||
npc02->canAttack = true;
|
||||
npc02->attackTarget = player.get();
|
||||
|
||||
npcs.push_back(std::move(npc02));
|
||||
|
||||
// Create shadow map (2048x2048, ortho size 40, near 0.1, far 100)
|
||||
shadowMap = std::make_unique<ShadowMap>(2048, 40.0f, 0.1f, 100.0f);
|
||||
shadowMap->setLightDirection(Eigen::Vector3f(-0.5f, -1.0f, -0.3f));
|
||||
std::cout << "Shadow map initialized" << std::endl;
|
||||
|
||||
setupNavigation();
|
||||
|
||||
scriptEngine.init(this, &inventory);
|
||||
|
||||
|
||||
dialogueSystem.init(renderer, CONST_ZIP_FILE);
|
||||
dialogueSystem.loadDatabase("resources/dialogue/sample_dialogues.json");
|
||||
/*dialogueSystem.addTriggerZone({
|
||||
"ghost_room_trigger",
|
||||
"test_line_dialogue",
|
||||
Eigen::Vector3f(0.0f, 0.0f, -8.5f),
|
||||
2.0f,
|
||||
true,
|
||||
false
|
||||
});*/
|
||||
|
||||
|
||||
}
|
||||
|
||||
void Location::setupNavigation()
|
||||
{
|
||||
std::vector<PathFinder::ObstacleMesh> obstacles;
|
||||
obstacles.reserve(gameObjects.size() + interactiveObjects.size());
|
||||
|
||||
for (const auto& item : gameObjects) {
|
||||
const LoadedGameObject& gameObj = item.second;
|
||||
obstacles.push_back({ &gameObj.mesh.data, Eigen::Vector3f::Zero() });
|
||||
}
|
||||
|
||||
for (const InteractiveObject& intObj : interactiveObjects) {
|
||||
if (!intObj.isActive) {
|
||||
continue;
|
||||
}
|
||||
obstacles.push_back({ &intObj.mesh.data, intObj.position });
|
||||
}
|
||||
|
||||
navigation.build(obstacles, "resources/config2/navigation.json", CONST_ZIP_FILE);
|
||||
|
||||
#ifdef SHOW_PATH
|
||||
buildDebugNavMeshes();
|
||||
#endif
|
||||
|
||||
auto planner = [this](const Eigen::Vector3f& start, const Eigen::Vector3f& end) {
|
||||
return navigation.findPath(start, end);
|
||||
};
|
||||
|
||||
if (player) {
|
||||
player->setPathPlanner(planner);
|
||||
}
|
||||
|
||||
for (auto& npc : npcs) {
|
||||
if (npc) {
|
||||
npc->setPathPlanner(planner);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SHOW_PATH
|
||||
void Location::buildDebugNavMeshes()
|
||||
{
|
||||
debugNavMeshes.clear();
|
||||
const auto& areas = navigation.getAreas();
|
||||
float y = navigation.getFloorY() + 0.02f;
|
||||
Eigen::Vector3f red(1.0f, 0.0f, 0.0f);
|
||||
|
||||
for (const auto& area : areas) {
|
||||
if (area.polygon.size() < 3) continue;
|
||||
VertexRenderStruct mesh;
|
||||
mesh.data = CreatePolygonFloor(area.polygon, y, red);
|
||||
mesh.RefreshVBO();
|
||||
debugNavMeshes.push_back(std::move(mesh));
|
||||
}
|
||||
}
|
||||
|
||||
void Location::drawDebugNavigation()
|
||||
{
|
||||
renderer.shaderManager.PushShader("defaultColor");
|
||||
renderer.SetMatrix();
|
||||
for (const auto& mesh : debugNavMeshes) {
|
||||
renderer.DrawVertexRenderStruct(mesh);
|
||||
}
|
||||
renderer.shaderManager.PopShader();
|
||||
renderer.SetMatrix();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
InteractiveObject* Location::raycastInteractiveObjects(const Eigen::Vector3f& rayOrigin, const Eigen::Vector3f& rayDir) {
|
||||
if (interactiveObjects.empty()) {
|
||||
std::cout << "[RAYCAST] No interactive objects to check" << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::cout << "[RAYCAST] Starting raycast with " << interactiveObjects.size() << " objects" << std::endl;
|
||||
std::cout << "[RAYCAST] Ray origin: (" << rayOrigin.x() << ", " << rayOrigin.y() << ", " << rayOrigin.z() << ")" << std::endl;
|
||||
std::cout << "[RAYCAST] Ray dir: (" << rayDir.x() << ", " << rayDir.y() << ", " << rayDir.z() << ")" << std::endl;
|
||||
|
||||
float closestDistance = FLT_MAX;
|
||||
InteractiveObject* closestObject = nullptr;
|
||||
|
||||
for (auto& intObj : interactiveObjects) {
|
||||
std::cout << "[RAYCAST] Checking object: " << intObj.name << " (active: " << intObj.isActive << ")" << std::endl;
|
||||
|
||||
if (!intObj.isActive) {
|
||||
std::cout << "[RAYCAST] -> Object inactive, skipping" << std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
std::cout << "[RAYCAST] Position: (" << intObj.position.x() << ", " << intObj.position.y() << ", "
|
||||
<< intObj.position.z() << "), Radius: " << intObj.interactionRadius << std::endl;
|
||||
|
||||
Eigen::Vector3f toObject = intObj.position - rayOrigin;
|
||||
std::cout << "[RAYCAST] Vector to object: (" << toObject.x() << ", " << toObject.y() << ", " << toObject.z() << ")" << std::endl;
|
||||
|
||||
float distanceAlongRay = toObject.dot(rayDir);
|
||||
std::cout << "[RAYCAST] Distance along ray: " << distanceAlongRay << std::endl;
|
||||
|
||||
if (distanceAlongRay < 0.1f) {
|
||||
std::cout << "[RAYCAST] -> Object behind camera, skipping" << std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
Eigen::Vector3f closestPointOnRay = rayOrigin + rayDir * distanceAlongRay;
|
||||
float distToObject = (closestPointOnRay - intObj.position).norm();
|
||||
|
||||
std::cout << "[RAYCAST] Distance to object: " << distToObject
|
||||
<< " (interaction radius: " << intObj.interactionRadius << ")" << std::endl;
|
||||
|
||||
if (distToObject <= intObj.interactionRadius && distanceAlongRay < closestDistance) {
|
||||
std::cout << "[RAYCAST] *** HIT DETECTED! ***" << std::endl;
|
||||
closestDistance = distanceAlongRay;
|
||||
closestObject = &intObj;
|
||||
}
|
||||
}
|
||||
|
||||
if (closestObject) {
|
||||
std::cout << "[RAYCAST] *** RAYCAST SUCCESS: Found object " << closestObject->name << " ***" << std::endl;
|
||||
}
|
||||
else {
|
||||
std::cout << "[RAYCAST] No objects hit" << std::endl;
|
||||
}
|
||||
|
||||
return closestObject;
|
||||
}
|
||||
|
||||
Character* Location::raycastNpcs(const Eigen::Vector3f& rayOrigin, const Eigen::Vector3f& rayDir, float maxDistance) {
|
||||
Character* closestNpc = nullptr;
|
||||
float closestDist = maxDistance;
|
||||
|
||||
for (auto& npc : npcs) {
|
||||
Eigen::Vector3f toNpc = npc->position - rayOrigin;
|
||||
float distAlongRay = toNpc.dot(rayDir);
|
||||
if (distAlongRay < 0.1f) continue;
|
||||
|
||||
Eigen::Vector3f closestPoint = rayOrigin + rayDir * distAlongRay;
|
||||
float distToNpc = (closestPoint - npc->position).norm();
|
||||
|
||||
float radius = npc->modelScale * 50.0f;
|
||||
|
||||
if (distToNpc <= radius && distAlongRay < closestDist) {
|
||||
closestDist = distAlongRay;
|
||||
closestNpc = npc.get();
|
||||
}
|
||||
}
|
||||
return closestNpc;
|
||||
}
|
||||
|
||||
|
||||
void Location::drawGame()
|
||||
{
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
|
||||
|
||||
|
||||
renderer.shaderManager.PushShader(defaultShaderName);
|
||||
renderer.RenderUniform1i(textureUniformName, 0);
|
||||
|
||||
renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5,
|
||||
static_cast<float>(Environment::width) / static_cast<float>(Environment::height),
|
||||
Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
|
||||
renderer.PushMatrix();
|
||||
|
||||
renderer.LoadIdentity();
|
||||
renderer.TranslateMatrix({ 0,0, -1.0f * Environment::zoom });
|
||||
//renderer.TranslateMatrix({ 0, -6.f, 0 });
|
||||
|
||||
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() });
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, roomTexture->getTexID());
|
||||
renderer.DrawVertexRenderStruct(roomMesh);
|
||||
|
||||
for (auto& [name, gameObj] : gameObjects) {
|
||||
glBindTexture(GL_TEXTURE_2D, gameObj.texture->getTexID());
|
||||
renderer.DrawVertexRenderStruct(gameObj.mesh);
|
||||
}
|
||||
|
||||
/*
|
||||
glBindTexture(GL_TEXTURE_2D, fireboxTexture->getTexID());
|
||||
renderer.DrawVertexRenderStruct(fireboxMesh);
|
||||
glBindTexture(GL_TEXTURE_2D, inaiTexture->getTexID());
|
||||
renderer.DrawVertexRenderStruct(inaiMesh);
|
||||
glBindTexture(GL_TEXTURE_2D, benchTexture->getTexID());
|
||||
renderer.DrawVertexRenderStruct(benchMesh);
|
||||
*/
|
||||
for (auto& intObj : interactiveObjects) {
|
||||
if (intObj.isActive) {
|
||||
intObj.draw(renderer);
|
||||
}
|
||||
}
|
||||
|
||||
if (player) player->draw(renderer);
|
||||
for (auto& npc : npcs) npc->draw(renderer);
|
||||
|
||||
#ifdef SHOW_PATH
|
||||
drawDebugNavigation();
|
||||
#endif
|
||||
|
||||
renderer.PopMatrix();
|
||||
|
||||
renderer.PopProjectionMatrix();
|
||||
|
||||
renderer.shaderManager.PopShader();
|
||||
|
||||
}
|
||||
|
||||
void Location::drawShadowDepthPass()
|
||||
{
|
||||
if (!shadowMap) return;
|
||||
|
||||
const Eigen::Vector3f& sceneCenter = player ? player->position : Eigen::Vector3f::Zero();
|
||||
shadowMap->updateLightSpaceMatrix(sceneCenter);
|
||||
|
||||
shadowMap->bind();
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glDepthFunc(GL_LESS);
|
||||
|
||||
// Use front-face culling during depth pass to reduce shadow acne on lit faces
|
||||
//glCullFace(GL_FRONT);
|
||||
glEnable(GL_CULL_FACE);
|
||||
|
||||
renderer.shaderManager.PushShader("shadow_depth");
|
||||
|
||||
// Set up light's orthographic projection
|
||||
const Eigen::Matrix4f& lightProj = shadowMap->getLightProjectionMatrix();
|
||||
const Eigen::Matrix4f& lightView = shadowMap->getLightViewMatrix();
|
||||
|
||||
// Push the light's projection matrix via the 6-param ortho overload won't
|
||||
// match our pre-computed matrix. Instead, use the raw stack approach:
|
||||
// push a dummy projection then overwrite via PushSpecialMatrix-style.
|
||||
// Simpler: push ortho then push the light view as modelview.
|
||||
renderer.PushProjectionMatrix(
|
||||
-40.0f, 40.0f,
|
||||
-40.0f, 40.0f,
|
||||
0.1f, 100.0f);
|
||||
|
||||
const Eigen::Vector3f& lightDir = shadowMap->getLightDirection();
|
||||
Eigen::Vector3f lightPos = sceneCenter - lightDir * 50.0f;
|
||||
Eigen::Vector3f up(0.0f, 1.0f, 0.0f);
|
||||
if (std::abs(lightDir.dot(up)) > 0.99f) {
|
||||
up = Eigen::Vector3f(0.0f, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
// Build the light view matrix and push it
|
||||
renderer.PushSpecialMatrix(lightView);
|
||||
|
||||
// Draw static geometry
|
||||
renderer.DrawVertexRenderStruct(roomMesh);
|
||||
|
||||
for (auto& [name, gameObj] : gameObjects) {
|
||||
renderer.DrawVertexRenderStruct(gameObj.mesh);
|
||||
}
|
||||
|
||||
for (auto& intObj : interactiveObjects) {
|
||||
if (intObj.isActive && intObj.texture) {
|
||||
renderer.PushMatrix();
|
||||
renderer.TranslateMatrix(intObj.position);
|
||||
renderer.DrawVertexRenderStruct(intObj.mesh);
|
||||
renderer.PopMatrix();
|
||||
}
|
||||
}
|
||||
|
||||
// Draw characters (they handle their own skinning shader switch internally)
|
||||
if (player) player->drawShadowDepth(renderer);
|
||||
for (auto& npc : npcs) npc->drawShadowDepth(renderer);
|
||||
|
||||
renderer.PopMatrix(); // light view
|
||||
renderer.PopProjectionMatrix();
|
||||
renderer.shaderManager.PopShader();
|
||||
|
||||
//glCullFace(GL_BACK);
|
||||
glDisable(GL_CULL_FACE);
|
||||
|
||||
shadowMap->unbind();
|
||||
}
|
||||
|
||||
void Location::drawGameWithShadows()
|
||||
{
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
|
||||
|
||||
#ifdef DEBUG_LIGHT
|
||||
// Debug mode: render from the light's point of view using the plain
|
||||
// textured shader so we can see what the shadow map "sees".
|
||||
renderer.shaderManager.PushShader(defaultShaderName);
|
||||
renderer.RenderUniform1i(textureUniformName, 0);
|
||||
|
||||
renderer.PushProjectionMatrix(
|
||||
-40.0f, 40.0f,
|
||||
-40.0f, 40.0f,
|
||||
0.1f, 1000.0f);
|
||||
|
||||
renderer.PushSpecialMatrix(shadowMap->getLightViewMatrix());
|
||||
|
||||
#else
|
||||
static const std::string shadowShaderName = "default_shadow";
|
||||
|
||||
renderer.shaderManager.PushShader(shadowShaderName);
|
||||
renderer.RenderUniform1i(textureUniformName, 0);
|
||||
renderer.RenderUniform1i("uShadowMap", 1);
|
||||
|
||||
// Bind shadow map texture to unit 1
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
glBindTexture(GL_TEXTURE_2D, shadowMap->getDepthTexture());
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
|
||||
renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5,
|
||||
static_cast<float>(Environment::width) / static_cast<float>(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() });
|
||||
|
||||
// Capture the camera view matrix and compute uLightFromCamera
|
||||
cameraViewMatrix = renderer.GetCurrentModelViewMatrix();
|
||||
Eigen::Matrix4f cameraViewInverse = cameraViewMatrix.inverse();
|
||||
Eigen::Matrix4f lightFromCamera = shadowMap->getLightSpaceMatrix() * cameraViewInverse;
|
||||
renderer.RenderUniformMatrix4fv("uLightFromCamera", false, lightFromCamera.data());
|
||||
|
||||
// Light direction in camera space for diffuse lighting
|
||||
Eigen::Vector3f lightDirCamera = cameraViewMatrix.block<3, 3>(0, 0) * shadowMap->getLightDirection();
|
||||
renderer.RenderUniform3fv("uLightDir", lightDirCamera.data());
|
||||
#endif
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, roomTexture->getTexID());
|
||||
renderer.DrawVertexRenderStruct(roomMesh);
|
||||
|
||||
for (auto& [name, gameObj] : gameObjects) {
|
||||
glBindTexture(GL_TEXTURE_2D, gameObj.texture->getTexID());
|
||||
renderer.DrawVertexRenderStruct(gameObj.mesh);
|
||||
}
|
||||
|
||||
for (auto& intObj : interactiveObjects) {
|
||||
if (intObj.isActive) {
|
||||
intObj.draw(renderer);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG_LIGHT
|
||||
// In debug-light mode characters use the plain shaders (draw normally
|
||||
// but from the light's viewpoint — projection/view already on stack).
|
||||
if (player) player->draw(renderer);
|
||||
for (auto& npc : npcs) npc->draw(renderer);
|
||||
#else
|
||||
// Characters use their own shadow-aware shaders
|
||||
if (player) player->drawWithShadow(renderer, lightFromCamera, shadowMap->getDepthTexture(), lightDirCamera);
|
||||
for (auto& npc : npcs) npc->drawWithShadow(renderer, lightFromCamera, shadowMap->getDepthTexture(), lightDirCamera);
|
||||
#endif
|
||||
|
||||
renderer.PopMatrix();
|
||||
renderer.PopProjectionMatrix();
|
||||
renderer.shaderManager.PopShader();
|
||||
}
|
||||
|
||||
bool Location::setNavigationAreaAvailable(const std::string& areaName, bool available)
|
||||
{
|
||||
return navigation.setAreaAvailable(areaName, available);
|
||||
}
|
||||
|
||||
void Location::update(int64_t delta)
|
||||
{
|
||||
if (player) {
|
||||
player->update(delta);
|
||||
dialogueSystem.update(static_cast<int>(delta), player->position);
|
||||
}
|
||||
for (auto& npc : npcs) npc->update(delta);
|
||||
|
||||
|
||||
|
||||
// Check if player reached target interactive object
|
||||
if (targetInteractiveObject && player) {
|
||||
float distToObject = (player->position - targetInteractiveObject->position).norm();
|
||||
|
||||
// If player is close enough to pick up the item
|
||||
if (distToObject <= targetInteractiveObject->interactionRadius + 1.0f) {
|
||||
std::cout << "[PICKUP] Player reached object! Distance: " << distToObject << std::endl;
|
||||
std::cout << "[PICKUP] Calling Lua callback for: " << targetInteractiveObject->id << std::endl;
|
||||
|
||||
// Call custom activate function if specified, otherwise use fallback
|
||||
try {
|
||||
if (!targetInteractiveObject->activateFunctionName.empty()) {
|
||||
std::cout << "[PICKUP] Using custom function: " << targetInteractiveObject->activateFunctionName << std::endl;
|
||||
scriptEngine.callActivateFunction(targetInteractiveObject->activateFunctionName);
|
||||
}
|
||||
else {
|
||||
std::cout << "[PICKUP] Using fallback callback" << std::endl;
|
||||
scriptEngine.callItemPickupCallback(targetInteractiveObject->id);
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "[PICKUP] Error calling function: " << e.what() << std::endl;
|
||||
}
|
||||
|
||||
targetInteractiveObject = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Location::handleDown(int64_t fingerId, int eventX, int eventY, int mx, int my)
|
||||
{
|
||||
// Calculate ray for picking
|
||||
if (dialogueSystem.blocksGameplayInput()) {
|
||||
dialogueSystem.handlePointerReleased(static_cast<float>(mx), Environment::projectionHeight - static_cast<float>(my));
|
||||
return;
|
||||
}
|
||||
|
||||
player->attackTarget = nullptr;
|
||||
|
||||
// Unproject click to ground plane (y=0) for Viola's walk target
|
||||
float ndcX = 2.0f * eventX / Environment::width - 1.0f;
|
||||
float ndcY = 1.0f - 2.0f * eventY / Environment::height;
|
||||
float aspect = (float)Environment::width / (float)Environment::height;
|
||||
float tanHalfFov = tan(CAMERA_FOV_Y * 0.5f);
|
||||
|
||||
float cosAzim = cos(cameraAzimuth), sinAzim = sin(cameraAzimuth);
|
||||
float cosIncl = cos(cameraInclination), sinIncl = sin(cameraInclination);
|
||||
|
||||
Eigen::Vector3f camRight(cosAzim, 0.f, sinAzim);
|
||||
Eigen::Vector3f camForward(sinAzim * cosIncl, -sinIncl, -cosAzim * cosIncl);
|
||||
Eigen::Vector3f camUp(sinAzim * sinIncl, cosIncl, -cosAzim * sinIncl);
|
||||
const Eigen::Vector3f& playerPos = player ? player->position : Eigen::Vector3f::Zero();
|
||||
Eigen::Vector3f camPos = playerPos + Eigen::Vector3f(-sinAzim * cosIncl, sinIncl, cosAzim * cosIncl) * Environment::zoom;
|
||||
|
||||
Eigen::Vector3f rayDir = (camForward + camRight * (ndcX * aspect * tanHalfFov) + camUp * (ndcY * tanHalfFov)).normalized();
|
||||
|
||||
std::cout << "[CLICK] Camera position: (" << camPos.x() << ", " << camPos.y() << ", " << camPos.z() << ")" << std::endl;
|
||||
std::cout << "[CLICK] Ray direction: (" << rayDir.x() << ", " << rayDir.y() << ", " << rayDir.z() << ")" << std::endl;
|
||||
|
||||
// First check if we clicked on interactive object
|
||||
InteractiveObject* clickedObject = raycastInteractiveObjects(camPos, rayDir);
|
||||
if (clickedObject && player && clickedObject->isActive) {
|
||||
std::cout << "[CLICK] *** SUCCESS: Clicked on interactive object: " << clickedObject->name << " ***" << std::endl;
|
||||
std::cout << "[CLICK] Object position: (" << clickedObject->position.x() << ", "
|
||||
<< clickedObject->position.y() << ", " << clickedObject->position.z() << ")" << std::endl;
|
||||
std::cout << "[CLICK] Player position: (" << player->position.x() << ", "
|
||||
<< player->position.y() << ", " << player->position.z() << ")" << std::endl;
|
||||
|
||||
targetInteractiveObject = clickedObject;
|
||||
player->setTarget(clickedObject->position);
|
||||
std::cout << "[CLICK] Player moving to object..." << std::endl;
|
||||
}
|
||||
else {
|
||||
|
||||
// Check if we clicked on an NPC
|
||||
Character* clickedNpc = raycastNpcs(camPos, rayDir);
|
||||
if (clickedNpc && player) {
|
||||
float distance = (player->position - clickedNpc->position).norm();
|
||||
int npcIndex = -1;
|
||||
for (size_t i = 0; i < npcs.size(); ++i) {
|
||||
if (npcs[i].get() == clickedNpc) {
|
||||
npcIndex = static_cast<int>(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (npcIndex != -1) {
|
||||
if (distance <= clickedNpc->interactionRadius) {
|
||||
std::cout << "[CLICK] *** SUCCESS: Clicked on NPC index: " << npcIndex << " ***" << std::endl;
|
||||
scriptEngine.callNpcInteractCallback(npcIndex);
|
||||
}
|
||||
else {
|
||||
std::cout << "[CLICK] Too far from NPC (distance " << distance
|
||||
<< " > " << clickedNpc->interactionRadius << ")" << std::endl;
|
||||
}
|
||||
|
||||
if (clickedNpc->canAttack)
|
||||
{
|
||||
player->attackTarget = clickedNpc;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else if (rayDir.y() < -0.001f && player) {
|
||||
// Otherwise, unproject click to ground plane for Viola's walk target
|
||||
float t = -camPos.y() / rayDir.y();
|
||||
Eigen::Vector3f hit = camPos + rayDir * t;
|
||||
std::cout << "[CLICK] Clicked on ground at: (" << hit.x() << ", " << hit.z() << ")" << std::endl;
|
||||
|
||||
if (player->currentState == AnimationState::STAND || player->currentState == AnimationState::WALK)
|
||||
{
|
||||
player->setTarget(Eigen::Vector3f(hit.x(), 0.f, hit.z()));
|
||||
}
|
||||
}
|
||||
else {
|
||||
std::cout << "[CLICK] No valid target found" << std::endl;
|
||||
}
|
||||
}
|
||||
std::cout << "========================================\n" << std::endl;
|
||||
}
|
||||
void Location::handleUp(int64_t fingerId, int mx, int my)
|
||||
{
|
||||
|
||||
}
|
||||
void Location::handleMotion(int64_t fingerId, int eventX, int eventY, int mx, int my)
|
||||
{
|
||||
if (dialogueSystem.blocksGameplayInput()) {
|
||||
dialogueSystem.handlePointerMoved(
|
||||
static_cast<float>(mx),
|
||||
Environment::projectionHeight - static_cast<float>(my)
|
||||
);
|
||||
}
|
||||
|
||||
if (rightMouseDown) {
|
||||
int dx = eventX - lastMouseX;
|
||||
int dy = eventY - lastMouseY;
|
||||
lastMouseX = eventX;
|
||||
lastMouseY = eventY;
|
||||
|
||||
const float sensitivity = 0.005f;
|
||||
cameraAzimuth += dx * sensitivity;
|
||||
cameraInclination += dy * sensitivity;
|
||||
|
||||
const float minInclination = M_PI * 30.f / 180.f;
|
||||
const float maxInclination = M_PI * 0.5f;
|
||||
cameraInclination = max(minInclination, min(maxInclination, cameraInclination));
|
||||
}
|
||||
}
|
||||
|
||||
bool Location::requestDialogueStart(const std::string& dialogueId)
|
||||
{
|
||||
return dialogueSystem.startDialogue(dialogueId);
|
||||
}
|
||||
|
||||
void Location::setDialogueFlag(const std::string& flag, int value)
|
||||
{
|
||||
dialogueSystem.setFlag(flag, value);
|
||||
}
|
||||
|
||||
int Location::getDialogueFlag(const std::string& flag) const
|
||||
{
|
||||
return dialogueSystem.getFlag(flag);
|
||||
}
|
||||
|
||||
|
||||
} // namespace ZL
|
||||
Reference in new issue
Block a user