From c270527d405507640e9b4c8b2d48b13eb73f302d Mon Sep 17 00:00:00 2001 From: XProger Date: Tue, 28 Nov 2017 10:42:50 +0300 Subject: [PATCH] #23 dynamic lighting for stone artifacts and crystals --- src/controller.h | 2 +- src/debug.h | 6 ++--- src/format.h | 40 ++++++++++++++++++++++++++++++ src/lara.h | 1 + src/level.h | 64 ++++++++++++++++-------------------------------- src/mesh.h | 9 ++++++- src/trigger.h | 58 +++++++++++++++++++++++++++++++++++++++++-- 7 files changed, 130 insertions(+), 50 deletions(-) diff --git a/src/controller.h b/src/controller.h index 96d9af8..f8138ca 100644 --- a/src/controller.h +++ b/src/controller.h @@ -600,7 +600,7 @@ struct Controller { return level->entities[entity]; } - const TR::Room& getRoom() const { + TR::Room& getRoom() { int index = getRoomIndex(); ASSERT(index >= 0 && index < level->roomsCount); return level->rooms[index]; diff --git a/src/debug.h b/src/debug.h index 7694ca1..94805e8 100644 --- a/src/debug.h +++ b/src/debug.h @@ -277,12 +277,12 @@ namespace Debug { rc[i] = vec3( x + offsets[i][0], info.roomCeiling + 4, z + offsets[i][1] ); f[i] = vec3( x + offsets[i][0], info.floor - 4, z + offsets[i][1] ); c[i] = vec3( x + offsets[i][0], info.ceiling + 4, z + offsets[i][1] ); - if (info.roomBelow == 0xFF) rf[i].y = f[i].y; - if (info.roomAbove == 0xFF) rc[i].y = c[i].y; + if (info.roomBelow == TR::NO_ROOM) rf[i].y = f[i].y; + if (info.roomAbove == TR::NO_ROOM) rc[i].y = c[i].y; } if (info.roomNext != 0xFF) { - glColor4f(0.0f, 0.0f, 1.0f, 0.1f); + glColor4f(0.0f, 0.0f, 1.0f, 1.0f); glBegin(GL_QUADS); glVertex3fv((GLfloat*)&f[3]); glVertex3fv((GLfloat*)&f[2]); diff --git a/src/format.h b/src/format.h index c633a25..133e8b2 100644 --- a/src/format.h +++ b/src/format.h @@ -1461,6 +1461,14 @@ namespace TR { uint8 align; uint32 waterLevel; + struct DynLight { + int32 id; + vec4 pos; + vec4 color; + } dynLights[2]; + + int32 dynLightsCount; + struct Portal { uint16 roomIndex; Vertex normal; @@ -1506,6 +1514,36 @@ namespace TR { vec3 getOffset() const { return vec3(float(info.x), 0.0f, float(info.z)); } + + void addDynLight(int32 id, const vec4 &pos, const vec4 &color) { + DynLight *light = NULL; + for (int i = 0; i < dynLightsCount; i++) + if (dynLights[i].id == id) { + light = &dynLights[i]; + break; + } + // 1 is additional second light, can be overridden + if (!light) { + if (dynLightsCount < 2) { + light = &dynLights[dynLightsCount]; + dynLightsCount = min(2, dynLightsCount + 1); + } else + light = &dynLights[1]; + } + + light->id = id; + light->pos = pos; + light->color = color; + } + + void removeDynLight(int32 id) { + for (int i = 0; i < dynLightsCount; i++) + if (dynLights[i].id == id) { + if (i == 0) dynLights[0] = dynLights[1]; + dynLightsCount--; + break; + } + } }; union FloorData { @@ -3505,6 +3543,8 @@ namespace TR { stream.read(r.reverbType); stream.read(r.filter); // unused } + + r.dynLightsCount = 0; } diff --git a/src/lara.h b/src/lara.h index d00d25e..718b084 100644 --- a/src/lara.h +++ b/src/lara.h @@ -2572,6 +2572,7 @@ struct Lara : Character { for (int i = 0; i < pickupListCount; i++) { if (pickupList[i]->getEntity().type == TR::Entity::SCION_PICKUP_HOLDER) continue; + pickupList[i]->deactivate(); pickupList[i]->flags.invisible = true; game->invAdd(pickupList[i]->getEntity().type, 1); } diff --git a/src/level.h b/src/level.h index e29151f..d3bf0eb 100644 --- a/src/level.h +++ b/src/level.h @@ -323,6 +323,14 @@ struct Level : IGame { TR::Room &room = level.rooms[roomIndex]; + if (room.dynLightsCount) { + Core::lightPos[3] = room.dynLights[0].pos; + Core::lightColor[3] = room.dynLights[0].color; + } else { + Core::lightPos[3] = vec4(0); + Core::lightColor[3] = vec4(0, 0, 0, 1); + } + if (room.flags.water) setWaterParams(float(room.info.yTop)); else @@ -794,6 +802,12 @@ struct Level : IGame { case TR::Entity::ENEMY_MONK_1 : case TR::Entity::ENEMY_MONK_2 : return new Enemy(this, index, 100, 10, 0.0f, 0.0f); + case TR::Entity::CRYSTAL_PICKUP : return new CrystalPickup(this, index); + case TR::Entity::STONE_ITEM_1 : + case TR::Entity::STONE_ITEM_2 : + case TR::Entity::STONE_ITEM_3 : + case TR::Entity::STONE_ITEM_4 : return new StoneItem(this, index); + default : return (level.entities[index].modelIndex > 0) ? new Controller(this, index) : new Sprite(this, index, 0); } } @@ -1105,8 +1119,6 @@ struct Level : IGame { if (Core::pass == Core::passShadow) return; - bool hasGeom = false, hasSprite = false; - Basis basis; basis.identity(); @@ -1138,11 +1150,8 @@ struct Level : IGame { setRoomParams(roomIndex, Shader::ROOM, 1.0f, intensityf(level.rooms[roomIndex].ambient), 0.0f, 1.0f, transp == 1); Shader *sh = Core::active.shader; - if (!hasGeom) { - hasGeom = true; - sh->setParam(uLightColor, Core::lightColor[0], MAX_LIGHTS); - sh->setParam(uLightPos, Core::lightPos[0], MAX_LIGHTS); - } + sh->setParam(uLightColor, Core::lightColor[0], MAX_LIGHTS); + sh->setParam(uLightPos, Core::lightPos[0], MAX_LIGHTS); basis.pos = level.rooms[roomIndex].getOffset(); sh->setParam(uBasis, basis); @@ -1170,10 +1179,9 @@ struct Level : IGame { setRoomParams(roomIndex, Shader::SPRITE, 1.0f, 1.0f, 0.0f, 1.0f, true); Shader *sh = Core::active.shader; - if (!hasSprite) { - sh->setParam(uLightColor, Core::lightColor[0], MAX_LIGHTS); - sh->setParam(uLightPos, Core::lightPos[0], MAX_LIGHTS); - } + + sh->setParam(uLightColor, Core::lightColor[0], MAX_LIGHTS); + sh->setParam(uLightPos, Core::lightPos[0], MAX_LIGHTS); basis.pos = level.rooms[roomIndex].getOffset(); sh->setParam(uBasis, basis); @@ -1241,36 +1249,6 @@ struct Level : IGame { controller->render(camera->frustum, mesh, type, room.flags.water); } - void updateLighting() { - // update crystal lighting (TODO: make it per-room instead of global) - int index = -1; - float minDist = 1000000000.f; - - for (int i = 0; i < level.entitiesBaseCount; i++) { - TR::Entity &e = level.entities[i]; - if (e.type == TR::Entity::CRYSTAL) { - Crystal *crystal = (Crystal*)e.controller; - if (crystal->flags.state != TR::Entity::asActive && !level.rooms[crystal->getRoomIndex()].flags.visible) - continue; - - if (camera->frustum->isVisible(crystal->lightPos, CRYSTAL_LIGHT_RADIUS + 1024.0f)) { // 1024.0f because of vertex lighting - float d = (lara->pos - crystal->pos).length(); - if (d < minDist) { - index = i; - minDist = d; - } - }; - } - } - - if (index > -1) { - Crystal *crystal = (Crystal*)level.entities[index].controller; - Core::lightPos[3] = crystal->lightPos; - Core::lightColor[3] = CRYSTAL_LIGHT_COLOR; - } else - Core::lightColor[3] = Core::lightColor[3] = vec4(0, 0, 0, 1); - } - void update() { if (level.isCutsceneLevel() && (lara->health > 0.0f && !sndSoundtrack && TR::LEVEL_INFO[level.id].ambientTrack != TR::NO_TRACK)) { if (camera->timer > 0.0f) @@ -1309,8 +1287,6 @@ struct Level : IGame { camera->update(); - updateLighting(); - if (waterCache) waterCache->update(); @@ -1730,6 +1706,7 @@ struct Level : IGame { // Debug::Draw::box(bbox.min, bbox.max, vec4(1, 0, 1, 1)); // Core::setBlending(bmAlpha); + // Core::setDepthTest(false); // Core::validateRenderState(); // Debug::Level::rooms(level, lara->pos, lara->getEntity().room); // Debug::Level::lights(level, lara->getRoomIndex(), lara); @@ -1744,6 +1721,7 @@ struct Level : IGame { // Debug::Level::path(level, (Enemy*)level.entities[86].controller); // Debug::Level::debugOverlaps(level, lara->box); // Debug::Level::debugBoxes(level, lara->dbgBoxes, lara->dbgBoxesCount); + // Core::setDepthTest(true); // Core::setBlending(bmNone); /* diff --git a/src/mesh.h b/src/mesh.h index 14c8eef..5ceb1ac 100644 --- a/src/mesh.h +++ b/src/mesh.h @@ -363,7 +363,14 @@ struct MeshBuilder { if (range.iCount && model.type == TR::Entity::SKY && ((level.version & TR::VER_TR3))) range.iCount -= 16 * 3; } -// TR::Entity::fixOpaque(model.type, opaque); + + //int transp = TR::Entity::fixTransp(model.type); + + if (model.type == TR::Entity::SKY) { + models[i].geometry[0].iCount = iCount - models[i].geometry[0].iStart; + models[i].geometry[1].iCount = 0; + models[i].geometry[2].iCount = 0; + } } ASSERT(vCount - vStartModel <= 0xFFFF); diff --git a/src/trigger.h b/src/trigger.h index ce1e6f1..526bf80 100644 --- a/src/trigger.h +++ b/src/trigger.h @@ -678,7 +678,6 @@ struct Drawbridge : Controller { struct Crystal : Controller { Texture *environment; - vec3 lightPos; Crystal(IGame *game, int entity) : Controller(game, entity) { environment = new Texture(64, 64, Texture::RGBA, true, NULL, true, true); @@ -689,9 +688,15 @@ struct Crystal : Controller { delete environment; } + virtual void deactivate(bool removeFromList = false) { + Controller::deactivate(removeFromList); + getRoom().removeDynLight(entity); + } + virtual void update() { updateAnimation(false); - lightPos = animation.getJoints(getMatrix(), 0, false).pos - vec3(0, 256, 0); + vec3 lightPos = animation.getJoints(getMatrix(), 0, false).pos - vec3(0, 256, 0); + getRoom().addDynLight(entity, vec4(lightPos, 0.0f), CRYSTAL_LIGHT_COLOR); } virtual void render(Frustum *frustum, MeshBuilder *mesh, Shader::Type type, bool caustics) { @@ -701,6 +706,27 @@ struct Crystal : Controller { } }; + +#define CRYSTAL_PICKUP_LIGHT_COLOR vec4(0.1f, 0.5f, 0.1f, 1.0f / CRYSTAL_LIGHT_RADIUS) + +struct CrystalPickup : Controller { + + CrystalPickup(IGame *game, int entity) : Controller(game, entity) { + activate(); + } + + virtual void deactivate(bool removeFromList = false) { + Controller::deactivate(removeFromList); + getRoom().removeDynLight(entity); + } + + virtual void update() { + updateAnimation(false); + vec3 lightPos = animation.getJoints(getMatrix(), 0, false).pos; + getRoom().addDynLight(entity, vec4(lightPos, 0.0f), CRYSTAL_PICKUP_LIGHT_COLOR); + } +}; + #define BLADE_DAMAGE 100 #define BLADE_RANGE 1024 @@ -1404,4 +1430,32 @@ struct Explosion : Sprite { }; +#define STONE_ITEM_LIGHT_RADIUS 2048.0f + +struct StoneItem : Controller { + float phase; + + StoneItem(IGame *game, int entity) : Controller(game, entity), phase(0) { + activate(); + } + + virtual void deactivate(bool removeFromList = false) { + Controller::deactivate(removeFromList); + getRoom().removeDynLight(entity); + } + + virtual void update() { + updateAnimation(false); + + angle.y += Core::deltaTime * 2.0f; + phase += Core::deltaTime; + float s = 0.3f + (sinf(phase * PI2) * 0.5f + 0.5f) * 0.7f; + + vec4 lightColor(0.1f * s, 1.0f * s, 1.0f * s, 1.0f / STONE_ITEM_LIGHT_RADIUS); + vec3 lightPos = animation.getJoints(getMatrix(), 0, false).pos; + + getRoom().addDynLight(entity, vec4(lightPos, 0.0f), lightColor); + } +}; + #endif \ No newline at end of file