mirror of
https://github.com/XProger/OpenLara.git
synced 2025-08-15 09:34:18 +02:00
#23 dynamic lighting for stone artifacts and crystals
This commit is contained in:
@@ -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];
|
||||
|
@@ -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]);
|
||||
|
40
src/format.h
40
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;
|
||||
}
|
||||
|
||||
|
||||
|
@@ -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);
|
||||
}
|
||||
|
56
src/level.h
56
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
/*
|
||||
|
@@ -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);
|
||||
|
||||
|
@@ -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
|
Reference in New Issue
Block a user