From 1c0fde576d18627b1ddbc1fc3dd72b1e96a008bd Mon Sep 17 00:00:00 2001 From: XProger Date: Sat, 10 Mar 2018 04:19:31 +0300 Subject: [PATCH] #14 Rats AI; #23 flipmap and water surfaces rework, fix rooms visibility in cutscenes, fix water reflection for additive blended geometry --- src/animation.h | 33 ++++++++- src/cache.h | 73 +++++++++++++++---- src/camera.h | 40 +--------- src/character.h | 2 - src/controller.h | 24 +++--- src/debug.h | 4 +- src/enemy.h | 186 ++++++++++++++++++++++++++++++++++++++++++++++- src/format.h | 45 +++++++++++- src/lara.h | 7 +- src/level.h | 95 ++++++++++++++++++------ src/mesh.h | 8 +- src/trigger.h | 11 ++- 12 files changed, 419 insertions(+), 109 deletions(-) diff --git a/src/animation.h b/src/animation.h index 01480c7..1bd0cf4 100644 --- a/src/animation.h +++ b/src/animation.h @@ -22,19 +22,42 @@ struct Animation { Animation() : overrides(NULL) {} - Animation(TR::Level *level, const TR::Model *model) : level(level), model(model), anims(model ? &level->anims[model->animation] : NULL), time(0), delta(0), dir(1.0f), - index(-1), prev(0), next(0), overrides(NULL), overrideMask(0) { - if (anims) setAnim(0); + Animation(TR::Level *level, const TR::Model *model) : level(level), overrides(NULL), overrideMask(0) { + setModel(model); } ~Animation() { delete[] overrides; } + void setModel(const TR::Model *model) { + if (this->model == model) + return; + + this->model = model; + anims = model ? &level->anims[model->animation] : NULL; + time = 0; + delta = 0; + dir = 1.0f; + index = -1; + prev = 0; + next = 0; + overrideMask = 0; + + if (overrides) { + delete[] overrides; + overrides = NULL; + initOverrides(); + } + + if (anims) setAnim(0); + } + inline operator TR::Animation* () const { return anims + index; } void initOverrides() { ASSERT(model); + ASSERT(!overrides) overrides = new quat[model->mCount]; overrideMask = 0; } @@ -79,6 +102,10 @@ struct Animation { return (TR::AnimFrame*)&level->frameData[anim->frameOffset / 2 + index * frameSize]; // >> 1 (div 2) because frameData is array of shorts } + void goEnd(bool lerpToNext = true) { + setAnim(index, -(framesCount - 1), lerpToNext); + } + void updateInfo() { ASSERT(model); ASSERT(anims); diff --git a/src/cache.h b/src/cache.h index 46c5c32..63071d4 100644 --- a/src/cache.h +++ b/src/cache.h @@ -230,7 +230,7 @@ struct AmbientCache { enum int32 { BLANK, WAIT, READY } status; - vec3 colors[6]; + vec3 colors[6]; // TODO: ubyte4[6] } *items; int *offsets; @@ -251,7 +251,7 @@ struct AmbientCache { for (int i = 0; i < level->roomsCount; i++) { TR::Room &r = level->rooms[i]; offsets[i] = sectors; - sectors += r.xSectors * r.zSectors; + sectors += r.xSectors * r.zSectors * (r.alternateRoom > -1 ? 2 : 1); // x2 for flipped rooms } // init cache buffer items = new Cube[sectors]; @@ -270,11 +270,11 @@ struct AmbientCache { } void addTask(int room, int sector) { - if (tasksCount >= 32) return; + if (tasksCount >= COUNT(tasks)) return; Task &task = tasks[tasksCount++]; task.room = room; - task.flip = level->state.flags.flipped; + task.flip = level->state.flags.flipped && level->rooms[room].alternateRoom > -1; task.sector = sector; task.cube = &items[offsets[room] + sector]; task.cube->status = Cube::WAIT; @@ -326,20 +326,33 @@ struct AmbientCache { for (int i = 0; i < tasksCount; i++) { Task &task = tasks[i]; - bool oldFlip = level->state.flags.flipped; - level->state.flags.flipped = task.flip != 0; - renderAmbient(task.room, task.sector, &task.cube->colors[0]); - level->state.flags.flipped = oldFlip; + bool needFlip = task.flip != level->state.flags.flipped; + + if (needFlip) game->flipMap(); + + int sector = task.sector; + if (task.flip) { + TR::Room &r = level->rooms[task.room]; + sector -= r.xSectors * r.zSectors; + } + + renderAmbient(task.room, sector, &task.cube->colors[0]); + if (needFlip) game->flipMap(); task.cube->status = Cube::READY; } tasksCount = 0; } - Cube* getAmbient(int room, int sector) { - Cube *cube = &items[offsets[room] + sector]; + Cube* getAmbient(int roomIndex, int sector) { + TR::Room &r = level->rooms[roomIndex]; + if (level->state.flags.flipped && r.alternateRoom > -1) + sector += r.xSectors * r.zSectors; + + Cube *cube = &items[offsets[roomIndex] + sector]; if (cube->status == Cube::BLANK) - addTask(room, sector); + addTask(roomIndex, sector); + return cube->status == Cube::READY ? cube : NULL; } @@ -373,6 +386,8 @@ struct WaterCache { struct Item { int from, to, caust; float timer; + float waterLevel; + bool flip; bool visible; bool blank; vec3 pos, size; @@ -395,8 +410,16 @@ struct WaterCache { TR::Level *level = game->getLevel(); TR::Room &r = level->rooms[to]; // underwater room ASSERT(r.flags.water); - int minX = r.xSectors, minZ = r.zSectors, maxX = 0, maxZ = 0, posY = level->rooms[to].waterLevel, caustY = posY; + int minX = r.xSectors, minZ = r.zSectors, maxX = 0, maxZ = 0; + + int posY = level->rooms[to].waterLevel; + if (posY == -1) + posY = level->rooms[from].waterLevel; + ASSERT(posY != -1); // underwater room without reaching the surface + + int caustY = posY; + for (int z = 0; z < r.zSectors; z++) for (int x = 0; x < r.xSectors; x++) { TR::Room::Sector &s = r.sectors[x * r.zSectors + z]; @@ -411,8 +434,6 @@ struct WaterCache { if (floor > caustY) { caustY = floor; caust = caustRoom; - if (level->state.flags.flipped && level->rooms[caust].alternateRoom > -1) - caust = level->rooms[caust].alternateRoom; } } } @@ -516,6 +537,20 @@ struct WaterCache { visible = 0; } + void flipMap() { + for (int i = 0; i < level->roomsCount && count; i++) + if (level->rooms[i].alternateRoom > -1) { + int j = 0; + while (j < count) { + if (items[j].from == i || items[j].to == i) { + items[j].deinit(); + items[j] = items[--count]; + } else + j++; + } + } + } + void setVisible(int roomIndex, int nextRoom = TR::NO_ROOM) { if (nextRoom == TR::NO_ROOM) { // setVisible(underwaterRoom) for caustics update for (int i = 0; i < count; i++) @@ -539,6 +574,9 @@ struct WaterCache { to = nextRoom; } + if (level->rooms[to].waterLevel == -1 && level->rooms[from].waterLevel == -1) // not have water surface + return; + for (int i = 0; i < count; i++) { Item &item = items[i]; if (item.from == from && item.to == to) { @@ -735,6 +773,11 @@ struct WaterCache { item.init(game); } + if (!count) { + visible = false; + return; + } + // render mirror reflection Core::setTarget(reflect, CLEAR_ALL); Camera *camera = (Camera*)game->getCamera(); @@ -754,7 +797,7 @@ struct WaterCache { vec4 reflectPlane = vec4(n.x, n.y, n.z, -n.dot(p)); bool underwater = level->rooms[camera->getRoomIndex()].flags.water; - //bool underwater = level->camera->pos.y > item.pos.y; + //bool underwater = camera->eye.pos.y > item.pos.y; camera->reflectPlane = &reflectPlane; camera->setup(true); diff --git a/src/camera.h b/src/camera.h index 0f15683..5168703 100644 --- a/src/camera.h +++ b/src/camera.h @@ -65,43 +65,7 @@ struct Camera : ICamera { } virtual int getRoomIndex() const { - return (level->state.flags.flipped && level->rooms[eye.room].alternateRoom > -1) ? level->rooms[eye.room].alternateRoom : eye.room; - } - - virtual void checkRoom() { - // level->getSector(eye.room, eye.pos); - // return; - - if (mode == MODE_CUTSCENE) { - for (int i = 0; i < level->roomsCount; i++) - if (owner->insideRoom(eye.pos, i)) { - eye.room = i; - break; - } - return; - } - - TR::Level::FloorInfo info; - owner->getFloorInfo(getRoomIndex(), eye.pos, info); - - if (info.roomNext != TR::NO_ROOM) - eye.room = info.roomNext; - - if (eye.pos.y < info.roomCeiling) { - if (info.roomAbove != TR::NO_ROOM) - eye.room = info.roomAbove; - else - if (info.roomCeiling != 0xffff8100) - eye.pos.y = (float)info.roomCeiling; - } - - if (eye.pos.y > info.roomFloor) { - if (info.roomBelow != TR::NO_ROOM) - eye.room = info.roomBelow; - else - if (info.roomFloor != 0xffff8100) - eye.pos.y = (float)info.roomFloor; - } + return eye.room; } void updateListener() { @@ -387,7 +351,7 @@ struct Camera : ICamera { } else updateFirstPerson(); - checkRoom(); + level->getSector(eye.room, eye.pos); } else { Controller *lookAt = NULL; diff --git a/src/character.h b/src/character.h index 16c91f6..f74aa5a 100644 --- a/src/character.h +++ b/src/character.h @@ -126,8 +126,6 @@ struct Character : Controller { if (info.roomAbove != TR::NO_ROOM && pos.y <= info.roomCeiling) { TR::Room *room = &level->rooms[info.roomAbove]; - if (level->state.flags.flipped && room->alternateRoom > -1) - room = &level->rooms[room->alternateRoom]; if (stand == STAND_UNDERWATER && !room->flags.water) { stand = STAND_ONWATER; diff --git a/src/controller.h b/src/controller.h index 1ed9567..57074ac 100644 --- a/src/controller.h +++ b/src/controller.h @@ -64,6 +64,7 @@ struct IGame { virtual bool isCutscene() { return false; } virtual uint16 getRandomBox(uint16 zone, uint16 *zones) { return 0; } virtual uint16 findPath(int ascend, int descend, bool big, int boxStart, int boxEnd, uint16 *zones, uint16 **boxes) { return 0; } + virtual void flipMap() {} virtual void setClipParams(float clipSign, float clipHeight) {} virtual void setWaterParams(float height) {} virtual void waterDrop(const vec3 &pos, float radius, float strength) {} @@ -124,6 +125,7 @@ struct Controller { TR::Room::Light *targetLight; vec3 mainLightPos; vec4 mainLightColor; + bool mainLightFlip; struct MeshLayer { uint32 model; @@ -153,9 +155,6 @@ struct Controller { joints = m ? new Basis[m->mCount] : NULL; jointsFrame = -1; - if (level->isCutsceneLevel()) - fixRoomIndex(); - specular = 0.0f; intensity = e.intensity == -1 ? -1.0f : intensityf(e.intensity); timer = 0.0f; @@ -186,8 +185,10 @@ struct Controller { deactivate(true); } - bool fixRoomIndex() { + bool fixRoomIndex() { // TODO: remove this and fix braid vec3 p = getPos(); + if (insideRoom(p, roomIndex)) + return true; for (int i = 0; i < level->roomsCount; i++) if (insideRoom(p, i)) { roomIndex = i; @@ -619,7 +620,7 @@ struct Controller { pos.z >= min.z && pos.z <= max.z; } - const TR::Model* getModel() const { + virtual const TR::Model* getModel() { int index = getEntity().modelIndex; return index > 0 ? &level->models[index - 1] : NULL; } @@ -635,10 +636,7 @@ struct Controller { } virtual int getRoomIndex() const { - int index = roomIndex; - if (level->state.flags.flipped && level->rooms[index].alternateRoom > -1) - index = level->rooms[index].alternateRoom; - return index; + return roomIndex; } virtual vec3 getPos() { @@ -1002,7 +1000,7 @@ struct Controller { case TR::Effect::ROTATE_180 : angle.y = angle.y + PI; break; case TR::Effect::FLOOR_SHAKE : game->setEffect(this, TR::Effect::Type(fx)); break; case TR::Effect::FINISH_LEVEL : game->loadNextLevel(); break; - case TR::Effect::FLIP_MAP : level->state.flags.flipped = !level->state.flags.flipped; break; + case TR::Effect::FLIP_MAP : game->flipMap(); break; default : cmdEffect(fx); break; } } else { @@ -1131,6 +1129,12 @@ struct Controller { vec3 tpos = vec3(float(targetLight->x), float(targetLight->y), float(targetLight->z)); vec4 tcolor = vec4(vec3(targetLight->color.r, targetLight->color.g, targetLight->color.b) * (1.0f / 255.0f), float(targetLight->radius)); + if (mainLightFlip != level->state.flags.flipped) { + if (getRoom().alternateRoom > -1) + lerp = false; + mainLightFlip = level->state.flags.flipped; + } + if (lerp) { float t = Core::deltaTime * 2.0f; mainLightPos = mainLightPos.lerp(tpos, t); diff --git a/src/debug.h b/src/debug.h index d148148..0364077 100644 --- a/src/debug.h +++ b/src/debug.h @@ -481,9 +481,9 @@ namespace Debug { for (int i = 0; i < level.entitiesCount; i++) { TR::Entity &e = level.entities[i]; Controller *controller = (Controller*)e.controller; - if (!controller || controller->flags.invisible) return; + if (!controller) continue; - sprintf(buf, "%s (%d)", getEntityName(level, e), i); + sprintf(buf, "%s (%d) %s", getEntityName(level, e), i, controller->flags.invisible ? "INVISIBLE" : ""); Debug::Draw::text(controller->getPos() + randf() * 64, controller->flags.active ? vec4(0, 0, 0.8f, 1) : vec4(0.8f, 0, 0, 1), buf); } diff --git a/src/enemy.h b/src/enemy.h index ba54e82..83c505a 100644 --- a/src/enemy.h +++ b/src/enemy.h @@ -745,9 +745,188 @@ struct Lion : Enemy { } }; + +#define RAT_TURN_SLOW (DEG2RAD * 90) +#define RAT_TURN_FAST (DEG2RAD * 180) +#define RAT_DIST_BITE 341.0f +#define RAT_DIST_ATTACK 1536.0f +#define RAT_WAIT 0.01f +#define RAT_DAMAGE 20 + struct Rat : Enemy { - Rat(IGame *game, int entity) : Enemy(game, entity, 6, 341, 375.0f, 0.25f) { - hitSound = TR::SND_HIT_RAT; + + enum { + HIT_MASK = 0x300018F, + }; + + enum { + ANIM_GROUND_DEATH = 8, + ANIM_WATER_DEATH = 2, + }; + + enum { + STATE_NONE , + STATE_STOP , + STATE_ATTACK , + STATE_RUN , + STATE_BITE , + STATE_DEATH , + STATE_WAIT , + STATE_WATER_SWIM = 1, + STATE_WATER_ATTACK , + STATE_WATER_DEATH , + }; + + int modelLand, modelWater; + + Rat(IGame *game, int entity) : Enemy(game, entity, 5, 204, 200.0f, 0.25f) { + hitSound = TR::SND_HIT_RAT; + jointChest = 1; + jointHead = 2; + + modelLand = level->getModelIndex(TR::Entity::ENEMY_RAT_LAND) - 1; + modelWater = level->getModelIndex(TR::Entity::ENEMY_RAT_WATER) - 1; + } + + const virtual TR::Model* getModel() { + bool water = getRoom().flags.water; + int modelIndex = water ? modelWater : modelLand; + + ASSERT(modelIndex > -1); + const TR::Model *model = &level->models[modelIndex]; + if (animation.model != model) { + targetBox = -1; + animation.setModel(model); + stand = water ? STAND_ONWATER : STAND_GROUND; + + int16 rIndex = getRoomIndex(); + if (water) { + TR::Room::Sector *sector = level->getWaterLevelSector(rIndex, pos); + if (sector) { + pos.y = float(sector->ceiling * 256); + roomIndex = rIndex; + } + } else { + int16 rIndex = getRoomIndex(); + TR::Room::Sector *sector = level->getSector(rIndex, pos); + if (sector) { + pos.y = float(sector->floor * 256); + roomIndex = rIndex; + } + } + + nextState = STATE_NONE; + state = STATE_NONE; + + if (health <= 0.0f) { + getStateDeath(); + animation.goEnd(false); + } + + updateZone(); + } + return animation.model; + } + + virtual int getStateGround() { + if (!think(false)) + return state; + + float angle; + getTargetInfo(0, NULL, NULL, &angle, NULL); + + if (nextState == state) + nextState = STATE_NONE; + + bool isBite = targetInView && fabsf(target->pos.y - pos.y) < 256.0f; + + switch (state) { + case STATE_STOP : + if (nextState != STATE_NONE) + return nextState; + if (isBite && targetDist < RAT_DIST_BITE) + return STATE_BITE; + return STATE_RUN; + case STATE_RUN : + if (targetInView && (collide(target) & HIT_MASK)) + return STATE_STOP; + if (isBite && targetDist < RAT_DIST_ATTACK) + return STATE_ATTACK; + if (targetInView && randf() < RAT_WAIT) { + nextState = STATE_WAIT; + return STATE_STOP; + } + break; + case STATE_ATTACK : + case STATE_BITE : + if (nextState == STATE_NONE && targetInView && (collide(target) & HIT_MASK)) { + bite(getJoint(jointHead).pos, RAT_DAMAGE); + nextState = state == STATE_ATTACK ? STATE_RUN : STATE_STOP; + } + break; + case STATE_WAIT : + if (mood == MOOD_SLEEP || randf() < RAT_WAIT) + return STATE_STOP; + default : ; + } + + return state; + } + + virtual int getStateOnwater() { + if (!think(false)) + return state; + + float angle; + getTargetInfo(0, NULL, NULL, &angle, NULL); + + if (nextState == state) + nextState = STATE_NONE; + + if (animation.frameIndex % 4 == 0) + game->waterDrop(getJoint(jointHead).pos, 96.0f, 0.02f); + + switch (state) { + case STATE_WATER_SWIM : + if (targetInView && (collide(target) & HIT_MASK)) + return STATE_WATER_ATTACK; + break; + case STATE_WATER_ATTACK : + if (nextState == STATE_NONE && targetInView && (collide(target) & HIT_MASK)) { + game->waterDrop(getJoint(jointHead).pos, 256.0f, 0.2f); + bite(getJoint(jointHead).pos, RAT_DAMAGE); + nextState = STATE_WATER_SWIM; + } + return STATE_NONE; + default : ; + } + + return state; + } + + virtual void updatePosition() { + float angleY = 0.0f; + + if ((stand == STAND_GROUND && state == STATE_RUN) || (stand == STAND_ONWATER && state == STATE_WATER_SWIM)) + getTargetInfo(0, NULL, NULL, &angleY, NULL); + + turn(angleY, RAT_TURN_FAST); + + if (state == STATE_DEATH) { + animation.overrideMask = 0; + return; + } + + Enemy::updatePosition(); + setOverrides(state != STATE_DEATH, jointChest, jointHead); + lookAt(target); + } + + virtual int getStateDeath() { + bool water = getRoom().flags.water; + if ((water && state == STATE_WATER_DEATH) || (!water && state == STATE_DEATH)) + return state; + return animation.setAnim(water ? ANIM_WATER_DEATH : ANIM_GROUND_DEATH); } }; @@ -1547,8 +1726,7 @@ struct Human : Enemy { game->addMuzzleFlash(this, jointGun, muzzleOffset, -1); if (targetDist < HUMAN_DIST_SHOT && randf() < ((HUMAN_DIST_SHOT - targetDist) / HUMAN_DIST_SHOT - 0.25f)) { - target->hit(damage, this); - target->addBlood(target->getJoint(rand() % target->getModel()->mCount).pos, vec3(0)); + bite(target->getJoint(rand() % target->getModel()->mCount).pos, damage); game->playSound(target->stand == STAND_UNDERWATER ? TR::SND_HIT_UNDERWATER : TR::SND_HIT, target->pos, Sound::PAN); return true; } diff --git a/src/format.h b/src/format.h index f0ddc73..76426ee 100644 --- a/src/format.h +++ b/src/format.h @@ -2884,7 +2884,7 @@ namespace TR { cutMatrix.rotateY(16380.0f / float(0x4000) * PI * 0.5f); break; case LVL_TR1_CUT_3 : - state.flags.flipped = true; + flipMap(); case LVL_TR1_CUT_4 : cutMatrix.translate(vec3(float(e.x), float(e.y), float(e.z))); cutMatrix.rotateY(PI * 0.5f); @@ -3766,6 +3766,18 @@ namespace TR { return 0; } + void flipMap() { + for (int i = 0; i < roomsCount; i++) + if (rooms[i].alternateRoom > -1) { + TR::Room &src = rooms[i]; + TR::Room &dst = rooms[src.alternateRoom]; + + swap(src, dst); + swap(src.alternateRoom, dst.alternateRoom); + } + state.flags.flipped = !state.flags.flipped; + } + void floorSkipCommand(FloorData* &fd, int func) { switch (func) { case FloorData::PORTAL : @@ -3819,9 +3831,6 @@ namespace TR { Room::Sector& getSector(int roomIndex, int x, int z, int &dx, int &dz) const { ASSERT(roomIndex >= 0 && roomIndex < roomsCount); - if (state.flags.flipped && rooms[roomIndex].alternateRoom > -1) - roomIndex = rooms[roomIndex].alternateRoom; - Room &room = rooms[roomIndex]; int sx = x - room.info.x; @@ -3983,6 +3992,34 @@ namespace TR { return float(ceiling); } + TR::Room::Sector* getWaterLevelSector(int16 &roomIndex, const vec3 &pos) { + int x = int(pos.x); + int z = int(pos.z); + + TR::Room *room = &rooms[roomIndex]; + TR::Room::Sector *sector = room->getSector((x - room->info.x) / 1024, (z - room->info.z) / 1024); + + if (room->flags.water) { // go up to the air + while (sector->roomAbove != NO_ROOM) { + room = &rooms[sector->roomAbove]; + if (!room->flags.water) + break; + roomIndex = sector->roomAbove; + sector = room->getSector((x - room->info.x) / 1024, (z - room->info.z) / 1024); + } + return sector; + } else { // go down to the water + while (sector->roomBelow != NO_ROOM) { + room = &rooms[roomIndex = sector->roomBelow]; + sector = room->getSector((x - room->info.x) / 1024, (z - room->info.z) / 1024); + if (room->flags.water) + break; + } + return sector; + } + return NULL; + } + bool isBlocked(int16 &roomIndex, const vec3 &pos) { Room::Sector *sector = getSector(roomIndex, pos); return pos.y >= getFloor(sector, pos) || pos.y <= getCeiling(sector, pos); diff --git a/src/lara.h b/src/lara.h index 69413d9..1cf2bff 100644 --- a/src/lara.h +++ b/src/lara.h @@ -299,7 +299,7 @@ struct Lara : Character { delete[] basis; } - TR::Model* getModel() const { + TR::Model* getModel() { return &lara->level->models[lara->level->extra.braid]; } @@ -2025,7 +2025,7 @@ struct Lara : Character { camera->viewIndex = cameraIndex; if (needFlip) { - level->state.flags.flipped = !level->state.flags.flipped; + game->flipMap(); game->setEffect(this, effect); } } @@ -2616,6 +2616,9 @@ struct Lara : Character { //reset(44, vec3(62976, 1536, 23040), 0); reset(44, vec3(62976, 1536, 23040), 0); break; + case TR::LVL_TR2_PLATFORM : + reset(16, vec3(53029, -5120, 77359), 0); + break; case TR::LVL_TR3_TEMPLE : reset(204, vec3(40562, 3584, 58694), 0); break; diff --git a/src/level.h b/src/level.h index 32c3b18..97a4fb0 100644 --- a/src/level.h +++ b/src/level.h @@ -315,6 +315,32 @@ struct Level : IGame { return zoneCache->findPath(ascend, descend, big, boxStart, boxEnd, zones, boxes); } + void updateBlocks(bool rise) { + for (int i = 0; i < level.entitiesBaseCount; i++) { + Controller *controller = (Controller*)level.entities[i].controller; + switch (level.entities[i].type) { + case TR::Entity::BLOCK_1 : + case TR::Entity::BLOCK_2 : + case TR::Entity::BLOCK_3 : + case TR::Entity::BLOCK_4 : + ((Block*)controller)->updateFloor(rise); + break; + case TR::Entity::MOVING_BLOCK : + ((MovingBlock*)controller)->updateFloor(rise); + break; + default : ; + } + } + } + + virtual void flipMap() { + updateBlocks(false); + if (waterCache) waterCache->flipMap(); + mesh->flipMap(); + level.flipMap(); + updateBlocks(true); + } + virtual void setClipParams(float clipSign, float clipHeight) { params->clipSign = clipSign; params->clipHeight = clipHeight; @@ -1520,7 +1546,7 @@ struct Level : IGame { case 4 : if (effectTimer > 4.1f) { effectIdx++; effect = TR::Effect::NONE; } break; } if (idx != effectIdx) - level.state.flags.flipped = !level.state.flags.flipped; + flipMap(); break; } case TR::Effect::EARTHQUAKE : { @@ -1742,9 +1768,6 @@ struct Level : IGame { return; } - if (level.rooms[to].alternateRoom > -1 && level.state.flags.flipped) - to = level.rooms[to].alternateRoom; - TR::Room &room = level.rooms[to]; if (!room.flags.visible) { @@ -1770,20 +1793,39 @@ struct Level : IGame { waterCache->reset(); } - for (int i = 0; i < level.roomsCount; i++) - level.rooms[i].flags.visible = false; - int roomsList[256]; int roomsCount = 0; - getVisibleRooms(roomsList, roomsCount, TR::NO_ROOM, roomIndex, vec4(-1.0f, -1.0f, 1.0f, 1.0f), water); - /* - if (level.isCutsceneLevel()) { + if (level.isCutsceneLevel()) { // render all rooms except flipped for (int i = 0; i < level.roomsCount; i++) - roomsList[i] = i; - roomsCount = level.roomsCount; + level.rooms[i].flags.visible = true; + + for (int i = 0; i < level.roomsCount; i++) { + int flipIndex = level.rooms[i].alternateRoom; + if (flipIndex > 0) + level.rooms[flipIndex].flags.visible = false; + } + + for (int i = 0; i < level.roomsCount; i++) + if (level.rooms[i].flags.visible) { + roomsList[roomsCount++] = i; + if (Core::pass == Core::passCompose && water && waterCache) { + TR::Room &r = level.rooms[i]; + for (int j = 0; j < r.portalsCount; j++) { + int to = r.portals[j].roomIndex; + if (level.rooms[to].flags.visible && (level.rooms[to].flags.water ^ r.flags.water)) + waterCache->setVisible(i, to); + } + } + } + + } else { + for (int i = 0; i < level.roomsCount; i++) + level.rooms[i].flags.visible = false; + + getVisibleRooms(roomsList, roomsCount, TR::NO_ROOM, roomIndex, vec4(-1.0f, -1.0f, 1.0f, 1.0f), water); } - */ + if (water && waterCache) { for (int i = 0; i < roomsCount; i++) waterCache->setVisible(roomsList[i]); @@ -1817,12 +1859,6 @@ struct Level : IGame { // alpha blending pass renderRooms(roomsList, roomsCount, 1); renderEntities(1); - // additive blending pass - vec4 oldFog = Core::fogParams; - Core::fogParams = FOG_BLACK; // don't apply fog for additive - renderRooms(roomsList, roomsCount, 2); - renderEntities(2); - Core::fogParams = oldFog; Core::setBlending(bmNone); if (water && waterCache && waterCache->visible) { @@ -1832,8 +1868,18 @@ struct Level : IGame { setMainLight(player); waterCache->render(); Core::pass = pass; + setupBinding(); } + // additive blending pass + vec4 oldFog = Core::fogParams; + Core::fogParams = FOG_BLACK; // don't apply fog for additive + renderRooms(roomsList, roomsCount, 2); + renderEntities(2); + Core::fogParams = oldFog; + + Core::setBlending(bmNone); + if (showUI) { Core::Pass pass = Core::pass; renderUI(); @@ -1908,7 +1954,7 @@ struct Level : IGame { camera->setup(true); if (Input::down[ikF]) { - level.state.flags.flipped = !level.state.flags.flipped; + flipMap(); Input::down[ikF] = false; } @@ -2011,18 +2057,19 @@ struct Level : IGame { // Debug::Level::entities(level); // Debug::Level::zones(level, lara); // Debug::Level::blocks(level); - // Debug::Level::path(level, (Enemy*)level.entities[86].controller); + // Debug::Level::path(level, (Enemy*)level.entities[105].controller); // Debug::Level::debugOverlaps(level, lara->box); // Debug::Level::debugBoxes(level, lara->dbgBoxes, lara->dbgBoxesCount); Core::setDepthTest(true); Core::setBlending(bmNone); - /* + Core::validateRenderState(); + static int dbg_ambient = 0; dbg_ambient = int(params->time * 2) % 4; shadow->unbind(sShadow); - cube->unbind(sEnvironment); + Core::whiteCube->unbind(sEnvironment); glActiveTexture(GL_TEXTURE0); glEnable(GL_TEXTURE_2D); @@ -2030,7 +2077,7 @@ struct Level : IGame { glColor3f(1, 1, 1); for (int j = 0; j < 6; j++) { glPushMatrix(); - glTranslatef(lara->pos.x, lara->pos.y - 1024, lara->pos.z); + glTranslatef(player->pos.x, player->pos.y - 1024, player->pos.z); switch (j) { case 0 : glRotatef( 90, 0, 1, 0); break; case 1 : glRotatef(-90, 0, 1, 0); break; diff --git a/src/mesh.h b/src/mesh.h index 57cdf02..453e35c 100644 --- a/src/mesh.h +++ b/src/mesh.h @@ -63,7 +63,7 @@ struct MeshRange { #define DYN_MESH_QUADS 1024 #define DOUBLE_SIDED 2 -#define MAX_ROOM_DYN_FACES 256 +#define MAX_ROOM_DYN_FACES 512 struct Mesh { Index *iBuffer; @@ -800,6 +800,12 @@ struct MeshBuilder { #endif } + void flipMap() { + for (int i = 0; i < level->roomsCount; i++) + if (level->rooms[i].alternateRoom > -1) + swap(rooms[i], rooms[level->rooms[i].alternateRoom]); + } + inline short4 rotate(const short4 &v, int dir) { if (dir == 0) return v; short4 res = v; diff --git a/src/trigger.h b/src/trigger.h index d062d7f..11ecf03 100644 --- a/src/trigger.h +++ b/src/trigger.h @@ -1047,12 +1047,14 @@ struct Lightning : Controller { if (timer <= 0.0f) { if (flash) { - level->state.flags.flipped = false; + if (level->state.flags.flipped) + game->flipMap(); flash = false; armed = true; timer = (35.0f + randf() * 45.0f) / 30.0f; } else { - level->state.flags.flipped = true; + if (!level->state.flags.flipped) + game->flipMap(); flash = true; timer = 20.0f / 30.0f; @@ -1071,7 +1073,8 @@ struct Lightning : Controller { } else { timer = 0.0f; flash = false; - level->state.flags.flipped = false; + if (level->state.flags.flipped) + game->flipMap(); deactivate(true); } } @@ -1306,7 +1309,7 @@ struct Cabin : Controller { if (state == STATE_GROUND) { flags.invisible = true; level->state.flipmaps[3].active = TR::ACTIVE; - level->state.flags.flipped = !level->state.flags.flipped; + game->flipMap(); deactivate(true); }