Working on lights

This commit is contained in:
Vladislav Khorev committed 2026-06-04 15:14:18 +03:00
1 parent 442d3a7945
commit e3542b59f7
6 files changed
+237 -36

No files matched your search

+109 -21
View File
@@ -118,6 +118,7 @@ namespace ZL
loadTeleportZones(params.teleportsJsonPath, CONST_ZIP_FILE);
loadTriggerZones(params.triggerZonesJsonPath, CONST_ZIP_FILE);
loadPointLights(params.lightsJsonPath, CONST_ZIP_FILE);
#ifndef EMSCRIPTEN
// Create shadow map (2048x2048, ortho size 40, near 0.1, far 100)
@@ -274,6 +275,64 @@ namespace ZL
std::cout << "[TRIGGER] Loaded " << triggerZones.size() << " trigger zone(s) from " << jsonPath << std::endl;
}
void Location::loadPointLights(const std::string& jsonPath, const char* zipFile)
{
if (jsonPath.empty()) return;
std::string content;
try {
if (!zipFile || zipFile[0] == '\0') {
content = readTextFile(jsonPath);
} else {
auto buf = readFileFromZIP(jsonPath, zipFile);
if (buf.empty()) {
std::cerr << "[LIGHTS] Failed to read " << jsonPath << " from zip" << std::endl;
return;
}
content.assign(buf.begin(), buf.end());
}
} catch (const std::exception& e) {
std::cerr << "[LIGHTS] Failed to open " << jsonPath << ": " << e.what() << std::endl;
return;
}
using json = nlohmann::json;
json j;
try {
j = json::parse(content);
} catch (const std::exception& e) {
std::cerr << "[LIGHTS] JSON parse error in " << jsonPath << ": " << e.what() << std::endl;
return;
}
if (!j.contains("lights") || !j["lights"].is_array()) return;
for (const auto& item : j["lights"]) {
PointLight pl;
pl.id = item.value("id", "");
pl.autoLight = item.value("autoLight", false);
pl.autoLightDistance = item.value("autoLightDistance", -1.0f);
pl.position = Eigen::Vector3f(
item.value("positionX", 0.0f),
item.value("positionY", 0.0f),
item.value("positionZ", 0.0f)
);
pl.direction = Eigen::Vector3f(
item.value("directionX", 0.0f),
item.value("directionY", -1.0f),
item.value("directionZ", 0.0f)
);
pl.color = Eigen::Vector3f(
item.value("colorR", 1.0f),
item.value("colorG", 1.0f),
item.value("colorB", 1.0f)
);
pointLights.push_back(std::move(pl));
}
std::cout << "[LIGHTS] Loaded " << pointLights.size() << " light(s) from " << jsonPath << std::endl;
}
void Location::updateTriggerZones(const Eigen::Vector3f& playerPos)
{
for (auto& tz : triggerZones) {
@@ -918,13 +977,42 @@ namespace ZL
void Location::drawGameNight()
{
// --- Find nearest spotlight for shadow casting ---
const PointLight* shadowLight = nullptr;
// --- Determine which lights are active this frame ---
// autoLight=true lights only activate when they are the nearest light to the player.
// autoLight=false lights are always active.
int nearestLightIdx = -1;
if (player && !pointLights.empty()) {
float minDist = FLT_MAX;
for (const auto& pl : pointLights) {
const float d = (pl.position - player->position).norm();
if (d < minDist) { minDist = d; shadowLight = &pl; }
for (int i = 0; i < static_cast<int>(pointLights.size()); ++i) {
const float d = (pointLights[i].position - player->position).norm();
if (d < minDist) { minDist = d; nearestLightIdx = i; }
}
}
static constexpr int MAX_POINT_LIGHTS = 4;
std::vector<int> activeLightIndices;
activeLightIndices.reserve(pointLights.size());
for (int i = 0; i < static_cast<int>(pointLights.size()); ++i) {
const PointLight& pl = pointLights[i];
bool active = !pl.autoLight;
if (!active && i == nearestLightIdx) {
// autoLight: also check distance threshold if set
const float dist = player ? (pl.position - player->position).norm() : 0.0f;
active = (pl.autoLightDistance <= 0.0f || dist <= pl.autoLightDistance);
}
if (active) {
activeLightIndices.push_back(i);
if (static_cast<int>(activeLightIndices.size()) >= MAX_POINT_LIGHTS) break;
}
}
// Shadow caster = nearest active light to player
const PointLight* shadowLight = nullptr;
if (player && !activeLightIndices.empty()) {
float minDist = FLT_MAX;
for (int idx : activeLightIndices) {
const float d = (pointLights[idx].position - player->position).norm();
if (d < minDist) { minDist = d; shadowLight = &pointLights[idx]; }
}
}
const bool hasShadows = (shadowMap != nullptr && shadowLight != nullptr);
@@ -966,26 +1054,26 @@ namespace ZL
cameraViewMatrix = renderer.GetCurrentModelViewMatrix();
// Transform spot lights to eye space and build uniform arrays (max 4)
static constexpr int MAX_POINT_LIGHTS = 4;
// Transform active spot lights to eye space and build uniform arrays
float plPosEye[MAX_POINT_LIGHTS * 3] = {};
float plDirEye[MAX_POINT_LIGHTS * 3] = {};
float plColors[MAX_POINT_LIGHTS * 3] = {};
const Eigen::Matrix3f camRot = cameraViewMatrix.block<3, 3>(0, 0);
const int lightCount = min(static_cast<int>(pointLights.size()), MAX_POINT_LIGHTS);
for (int i = 0; i < lightCount; ++i) {
const Eigen::Vector4f worldPos(pointLights[i].position.x(), pointLights[i].position.y(), pointLights[i].position.z(), 1.0f);
const int lightCount = static_cast<int>(activeLightIndices.size());
for (int j = 0; j < lightCount; ++j) {
const PointLight& pl = pointLights[activeLightIndices[j]];
const Eigen::Vector4f worldPos(pl.position.x(), pl.position.y(), pl.position.z(), 1.0f);
const Eigen::Vector4f eyePos4 = cameraViewMatrix * worldPos;
plPosEye[i * 3 + 0] = eyePos4.x();
plPosEye[i * 3 + 1] = eyePos4.y();
plPosEye[i * 3 + 2] = eyePos4.z();
const Eigen::Vector3f eyeDir = camRot * pointLights[i].direction;
plDirEye[i * 3 + 0] = eyeDir.x();
plDirEye[i * 3 + 1] = eyeDir.y();
plDirEye[i * 3 + 2] = eyeDir.z();
plColors[i * 3 + 0] = pointLights[i].color.x();
plColors[i * 3 + 1] = pointLights[i].color.y();
plColors[i * 3 + 2] = pointLights[i].color.z();
plPosEye[j * 3 + 0] = eyePos4.x();
plPosEye[j * 3 + 1] = eyePos4.y();
plPosEye[j * 3 + 2] = eyePos4.z();
const Eigen::Vector3f eyeDir = camRot * pl.direction;
plDirEye[j * 3 + 0] = eyeDir.x();
plDirEye[j * 3 + 1] = eyeDir.y();
plDirEye[j * 3 + 2] = eyeDir.z();
plColors[j * 3 + 0] = pl.color.x();
plColors[j * 3 + 1] = pl.color.y();
plColors[j * 3 + 2] = pl.color.z();
}
// Compute light-from-camera matrix for shadow lookup
@@ -993,7 +1081,7 @@ namespace ZL
if (hasShadows) {
lightFromCamera = shadowMap->getLightSpaceMatrix() * cameraViewMatrix.inverse();
renderer.RenderUniformMatrix4fv("uLightFromCamera", false, lightFromCamera.data());
// Pass first light's eye-space direction as uLightDir for shadow bias
// Pass first active light's eye-space direction as uLightDir for shadow bias
renderer.RenderUniform3fv("uLightDir", plDirEye);
}