Working on lights
This commit is contained in:
1 parent
442d3a7945
commit
e3542b59f7
6 files changed
+237
-36
No files matched your search
+109
-21
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user