diff --git a/bin/OpenLara.exe b/bin/OpenLara.exe index 7c7ad15..bc4c2af 100644 Binary files a/bin/OpenLara.exe and b/bin/OpenLara.exe differ diff --git a/src/controller.h b/src/controller.h index 8e99926..b8e16db 100644 --- a/src/controller.h +++ b/src/controller.h @@ -3,7 +3,8 @@ #include "format.h" -#define GRAVITY 7.0f +#define GRAVITY 7.0f +#define NO_OVERLAP 0x7FFFFFFF struct Controller { TR::Level *level; @@ -29,6 +30,8 @@ struct Controller { onGround = inWater = false; } + virtual void update() {} + void updateEntity() { TR::Entity &e = getEntity(); e.x = int(pos.x); @@ -64,20 +67,24 @@ struct Controller { return level->rooms[getEntity().room]; } - TR::Room::Sector& getSector(int &dx, int &dz) { + TR::Room::Sector& getSector(int x, int z, int &dx, int &dz) { TR::Room &room = getRoom(); - TR::Entity &entity = getEntity(); - - dx = entity.x - room.info.x; - dz = entity.z - room.info.z; - int sx = dx / 1024; - int sz = dz / 1024; - dx -= sx * 1024; - dz -= sz * 1024; + + int sx = x - room.info.x; + int sz = z - room.info.z; + dx = sx & 1023; // mod 1024 + dz = sz & 1023; + sx >>= 10; // div 1024 + sz >>= 10; return room.sectors[sx * room.zSectors + sz]; } + TR::Room::Sector& getSector(int &dx, int &dz) { + TR::Entity &entity = getEntity(); + return getSector(entity.x, entity.z, dx, dz); + } + bool changeState(int state) { TR::Model &model = getModel(); TR::Animation *anim = &level->anims[model.animation]; @@ -107,7 +114,113 @@ struct Controller { return exists; } - virtual void update() {} + struct FloorInfo { + int floor, ceiling; + int roomNext, roomBelow, roomAbove; + }; + + int getOverlap(int fromX, int fromY, int fromZ, int toX, int toZ) { + int dx, dz; + TR::Room::Sector &s = getSector(fromX, fromZ, dx, dz); + + if (s.boxIndex == 0xFFFF) return NO_OVERLAP; + + TR::Box &b = level->boxes[s.boxIndex]; + if (b.contains(toX, toZ)) + return b.floor; + + int floor = NO_OVERLAP; + int delta = 0x7FFFFFFF; + + TR::Overlap *o = &level->overlaps[b.overlap & 0x7FFF]; + do { + TR::Box &b = level->boxes[o->boxIndex]; + if (b.contains(toX, toZ)) { // get min delta + int d = abs(fromY - b.floor); + if (d < delta) { + floor = b.floor; + delta = d; + } + } + } while (!(o++)->end); + + return floor; + } + + FloorInfo getFloorInfo(int x, int z) { + int dx, dz; + TR::Room::Sector &s = getSector(x, z, dx, dz); + + FloorInfo info; + info.floor = 256 * (int)s.floor; + info.ceiling = 256 * (int)s.ceiling; + info.roomNext = 255; + info.roomBelow = s.roomBelow; + info.roomAbove = s.roomAbove; + + if (!s.floorIndex) return info; + + TR::FloorData *fd = &level->floors[s.floorIndex]; + TR::FloorData::Command cmd; + + do { + cmd = (*fd++).cmd; + + switch (cmd.func) { + + case TR::FD_PORTAL : + info.roomNext = (*fd++).data; + break; + + case TR::FD_FLOOR : // floor & ceiling + case TR::FD_CEILING : { + TR::FloorData::Slant slant = (*fd++).slant; + int sx = (int)slant.x; + int sz = (int)slant.z; + if (cmd.func == TR::FD_FLOOR) { + info.floor -= sx * (sx > 0 ? (dx - 1024) : dx) >> 2; + info.floor -= sz * (sz > 0 ? (dz - 1024) : dz) >> 2; + } else { + info.ceiling -= sx * (sx < 0 ? (dx - 1024) : dx) >> 2; + info.ceiling += sz * (sz > 0 ? (dz - 1024) : dz) >> 2; + } + break; + } + + case TR::FD_TRIGGER : { + TR::FloorData::TriggerInfo info = (*fd++).triggerInfo; + TR::FloorData::TriggerCommand trigCmd; + do { + trigCmd = (*fd++).triggerCmd; // trigger action + switch (trigCmd.func) { + case 0 : break; // activate item + case 1 : break; // switch to camera + case 2 : break; // camera delay + case 3 : break; // flip map + case 4 : break; // flip on + case 5 : break; // flip off + case 6 : break; // look at item + case 7 : break; // end level + case 8 : break; // play soundtrack + case 9 : break; // special hadrdcode trigger + case 10 : break; // secret found + case 11 : break; // clear bodies + case 12 : break; // flyby camera sequence + case 13 : break; // play cutscene + } + // .. + } while (!trigCmd.end); + break; + } + + default : LOG("unknown func: %d\n", cmd.func); + } + + } while (!cmd.end); + + return info; + } + }; @@ -136,6 +249,8 @@ struct Lara : Controller { TR::Model &model = getModel(); TR::Animation *anim = &level->anims[model.animation]; + fTime += Core::deltaTime; + float rot = 0.0f; enum { LEFT = 1 << 1, @@ -150,6 +265,8 @@ struct Lara : Controller { WATER = 1 << 10, DEATH = 1 << 11 }; + inWater = (getRoom().flags & TR::ROOM_FLAG_WATER); + int mask = 0; if (Input::down[ikW] || Input::joy.L.y < 0) mask |= FORTH; @@ -215,10 +332,13 @@ struct Lara : Controller { } else if (mask & WATER) { // underwater - if (state == TR::STATE_FORWARD_JUMP || state == TR::STATE_BACK_JUMP || state == TR::STATE_LEFT_JUMP || state == TR::STATE_RIGHT_JUMP || state == TR::STATE_FAST_FALL) { + if (state == TR::STATE_FORWARD_JUMP || state == TR::STATE_UP_JUMP || state == TR::STATE_BACK_JUMP || state == TR::STATE_LEFT_JUMP || state == TR::STATE_RIGHT_JUMP || state == TR::STATE_FALL || state == TR::STATE_REACH) { model.animation = TR::ANIM_WATER_FALL; fTime = 0.0f; state = level->anims[model.animation].state; + } else if (state == TR::STATE_SWAN_DIVE) { + state = TR::STATE_DIVE; + angle.x = -PI * 0.5f; } else if (mask & JUMP) state = TR::STATE_SWIM; @@ -233,31 +353,46 @@ struct Lara : Controller { state = TR::STATE_REACH; else if ((mask & (FORTH | WALK)) == (FORTH | WALK)) state = TR::STATE_SWAN_DIVE; + } else if (state != TR::STATE_SWAN_DIVE && state != TR::STATE_REACH && state != TR::STATE_FALL && state != TR::STATE_UP_JUMP && state != TR::STATE_BACK_JUMP && state != TR::STATE_LEFT_JUMP && state != TR::STATE_RIGHT_JUMP) { + model.animation = TR::ANIM_FALL; + state = level->anims[model.animation].state; } - + // state = TR::STATE_FALL; // LOG("- speed: %f\n", velocity.length()); } // try to set new state if (!changeState(state)) { - int stopState = TR::STATE_FAST_FALL; + int stopState = TR::STATE_FALL; - if ((mask & (GROUND | WATER)) == (GROUND | WATER)) + if (state == TR::STATE_DIVE) + stopState = state; + else if ((mask & (GROUND | WATER)) == (GROUND | WATER)) stopState = TR::STATE_SURF_TREAD; else if (mask & WATER) stopState = TR::STATE_TREAD; else if (mask & GROUND) stopState = TR::STATE_STOP; - - if (state != stopState) - changeState(stopState); + + changeState(stopState); + /* + if (state == stopState || !changeState(stopState)) { + int stopAnim = -1; + switch (stopState) { + case TR::STATE_FALL : stopAnim = TR::ANIM_FALL; break; + } + + if (stopAnim > -1) { + model.animation = stopAnim; + fTime = 0.0f; + } + }*/ } anim = &level->anims[model.animation]; // get new animation and state (if it has been changed) state = anim->state; - fTime += Core::deltaTime; int fCount = anim->frameEnd - anim->frameStart + 1; int fIndex = int(fTime * 30.0f); @@ -350,8 +485,8 @@ struct Lara : Controller { if (state == TR::STATE_SWIM) { velocity = vec3(angle.x, angle.y) * 35.0f; - } else if (state == TR::STATE_GLIDE || state == TR::STATE_TREAD) - velocity = velocity - velocity * Core::deltaTime; + } else + velocity = velocity - velocity * min(1.0f, Core::deltaTime * 2.0f); // TODO: apply flow velocity } else { // on ground @@ -397,15 +532,20 @@ struct Lara : Controller { case 0x05 : { // play sound int frame = (*ptr++); int id = (*ptr++) & 0x3FFF; - if (fIndex == frame - anim->frameStart && fIndex != lastFrame) { + int idx = frame - anim->frameStart; + +// if (fIndex != lastFrame) +// LOG("play sound at %d current %d last %d (%d)\n", idx, fIndex, lastFrame, (int)id); + + if (idx > lastFrame && idx <= fIndex) { int16 a = level->soundsMap[id]; TR::SoundInfo &b = level->soundsInfo[a]; if (b.chance == 0 || (rand() & 0x7fff) <= b.chance) { uint32 c = level->soundOffsets[b.offset + rand() % ((b.flags & 0xFF) >> 2)]; - LOG("count %d\n", int(((b.flags & 0xFF) >> 2))); void *p = &level->soundData[c]; #ifdef WIN32 +// LOG("play\n"); PlaySound((LPSTR)p, NULL, SND_ASYNC | SND_MEMORY); #endif } @@ -425,15 +565,16 @@ struct Lara : Controller { } } - - // check for next animation if (endFrame) { model.animation = anim->nextAnimation; TR::Animation *nextAnim = &level->anims[anim->nextAnimation]; - fTime = (anim->nextFrame - nextAnim->frameStart) / 30.0f; + fIndex = anim->nextFrame - nextAnim->frameStart; + fTime = fIndex / 30.0f; } + + move(velocity * dt); collide(); @@ -444,21 +585,16 @@ struct Lara : Controller { vec3 p = pos; pos = pos + offset; - updateEntity(); + int d = getOverlap((int)p.x, (int)p.y, (int)p.z, (int)pos.x, (int)pos.z); - inWater = getRoom().flags & TR::ROOM_FLAG_WATER; + int state = level->anims[getModel().animation].state; + bool stop = false; - TR::Room &room = getRoom(); - TR::Entity &entity = getEntity(); + if ((d == NO_OVERLAP) || + ((state == TR::STATE_WALK || state == TR::STATE_BACK || state == TR::STATE_STEP_LEFT || state == TR::STATE_STEP_RIGHT) && (int)p.y - d < -256) || + ((int)p.y - d > 256)) { - int dx, dz; - TR::Room::Sector &s = getSector(dx, dz); - - int d = entity.y - s.floor * 256; - if (d >= 256 * 4) { - LOG("wall %d\n", d); pos = p; - updateEntity(); TR::Model &model = getModel(); TR::Animation *anim = &level->anims[model.animation]; @@ -470,6 +606,7 @@ struct Lara : Controller { else model.animation = TR::ANIM_STAND; velocity.x = velocity.z = 0.0f; + fTime = 0; } else if (inWater) { // in water // do nothing //velocity.x = velocity.z = 0.0f; @@ -478,96 +615,52 @@ struct Lara : Controller { velocity.x = -velocity.x * 0.5f; velocity.z = -velocity.z * 0.5f; velocity.y = 0.0f; + fTime = 0; } - fTime = 0; + + } else { + TR::Entity &entity = getEntity(); + entity.x = (int)pos.x; + entity.y = (int)pos.y; + entity.z = (int)pos.z; } } void collide() { - int dx, dz; - TR::Room::Sector &s = getSector(dx, dz); TR::Entity &entity = getEntity(); - float floor = s.floor * 256.0f; - float ceiling = s.ceiling * 256.0f; - - float fx = dx / 1024.0f, fz = dz / 1024.0f; - - uint16 cmd, *d = &level->floors[s.floorIndex]; - - if (s.floorIndex) - do { - cmd = *d++; - int func = cmd & 0x00FF; // function - int sub = (cmd & 0x7F00) >> 8; // sub function - - switch (func) { - case 1 : - entity.room = *d++; - break; - case 2 : - case 3 : { - int sx = (int8)(*d & 0x00FF); - int sz = (int8)((*d & 0xFF00) >> 8); - - if (func == 2) { - if (sx > 0) - floor += sx * (1024 - dx) >> 2; - else - floor -= sx * dx >> 2; - - if (sz > 0) - floor += sz * (1024 - dz) >> 2; - else - floor -= sz * dz >> 2; - } else { - if (sx < 0) - ceiling += sx * (1024 - dx) >> 2; - else - ceiling -= sx * dx >> 2; - - if (sz > 0) - ceiling -= sz * (1024 - dz) >> 2; - else - ceiling += sz * dz >> 2; - } - d++; - break; - } - case 4 : { - /* - if (sub == 0x00) LOG("trigger\n"); - if (sub == 0x01) LOG("pad\n"); - if (sub == 0x02) LOG("switch\n"); - if (sub == 0x03) LOG("key\n"); - if (sub == 0x04) LOG("pickup\n"); - if (sub == 0x05) LOG("heavy-trigger\n"); - if (sub == 0x06) LOG("anti-pad\n"); - if (sub == 0x07) LOG("combat\n"); - if (sub == 0x08) LOG("dummy\n"); - if (sub == 0x09) LOG("anti-trigger\n"); - */ - uint16 act; - do { - act = *d++; // trigger action - } while (!(act & 0x8000)); - - break; - } - default : - LOG("unknown func: %d\n", func); - } - - } while (!(cmd & 0x8000)); - + FloorInfo info = getFloorInfo(entity.x, entity.z); + + /* float hmin = 0.0f, hmax = -768.0f; if (inWater) { hmin = 256.0f + 128.0f; hmax = -256.0f - 128.0f; } + */ - onGround = (pos.y >= floor) && (s.roomBelow == 0xFF) && !(getRoom().flags & TR::ROOM_FLAG_WATER); + if (info.roomNext != 0xFF) + entity.room = info.roomNext; + if (entity.y >= info.floor) { + if (info.roomBelow == 0xFF) { + entity.y = info.floor; + pos.y = entity.y; + velocity.y = 0.0f; + } else + entity.room = info.roomBelow; + } + + if (entity.y <= info.ceiling) { + if (info.roomAbove == 0xFF) { + entity.y = info.ceiling; + pos.y = entity.y; + velocity.y = -velocity.y; + } else + entity.room = info.roomAbove; + } + + /* if (pos.y + hmin >= floor) { if (s.roomBelow == 0xFF) { pos.y = floor - hmin; @@ -579,12 +672,30 @@ struct Lara : Controller { if (pos.y + hmax <= ceiling) { if (s.roomAbove == 0xFF) { pos.y = ceiling - hmax; - velocity.y = 0.0f; + velocity.y = -velocity.y; } else entity.room = s.roomAbove; } - - entity.y = (int)pos.y; + */ + + int state = level->anims[getModel().animation].state; + + // TODO: use a brain! + float extra = 0; + if (state == TR::STATE_WALK || + state == TR::STATE_RUN || + state == TR::STATE_STOP || + state == TR::STATE_FAST_BACK || + state == TR::STATE_TURN_RIGHT || + state == TR::STATE_TURN_LEFT || + state == TR::STATE_BACK || + state == TR::STATE_FAST_TURN || + state == TR::STATE_STEP_RIGHT || + state == TR::STATE_STEP_LEFT || + state == TR::STATE_ROLL) + extra = 256 + 128; + + onGround = (pos.y + extra >= info.floor) && (info.roomBelow == 0xFF) && !(getRoom().flags & TR::ROOM_FLAG_WATER); } }; diff --git a/src/debug.h b/src/debug.h index 6043c3c..3992874 100644 --- a/src/debug.h +++ b/src/debug.h @@ -112,35 +112,37 @@ namespace Debug { namespace Level { - void debugFloor(const TR::Level &level, const vec3 &f, const vec3 &c, int floorIndex, bool current) { + void debugFloor(const TR::Level &level, const vec3 &f, const vec3 &c, int floorIndex, int boxIndex, bool current) { vec3 vf[4] = { f, f + vec3(1024, 0, 0), f + vec3(1024, 0, 1024), f + vec3(0, 0, 1024) }; vec3 vc[4] = { c, c + vec3(1024, 0, 0), c + vec3(1024, 0, 1024), c + vec3(0, 0, 1024) }; - uint16 cmd, *d = &level.floors[floorIndex]; - if (floorIndex) - do { - cmd = *d++; - int func = cmd & 0x00FF; // function - int sub = (cmd & 0x7F00) >> 8; // sub function + if (current) + glColor3f(1, 1, 1); + else + glColor3f(0, 1, 0); - if (func == 0x01) { // portal - d++; - // d += 2; + bool isPortal = false; - } - - if ((func == 0x02 || func == 0x03) && sub == 0x00) { // floor & ceiling corners - int sx = 256 * int((int8)(*d & 0x00FF)); - int sz = 256 * int((int8)((*d & 0xFF00) >> 8)); - - auto &p = func == 0x02 ? vf : vc; - - if (func == 0x02) { - - // if (current) - // LOG("%d\n", sx); + TR::FloorData *fd = &level.floors[floorIndex]; + TR::FloorData::Command cmd; + do { + cmd = (*fd++).cmd; + + switch (cmd.func) { + case TR::FD_PORTAL : + isPortal = true; + fd++; + break; // portal + case TR::FD_FLOOR : // floor & ceiling + case TR::FD_CEILING : { + TR::FloorData::Slant slant = (*fd++).slant; + int sx = 256 * (int)slant.x; + int sz = 256 * (int)slant.z; + auto &p = cmd.func == 0x02 ? vf : vc; + + if (cmd.func == 0x02) { // floor if (sx > 0) { p[0].y += sx; p[3].y += sx; @@ -156,9 +158,7 @@ namespace Debug { p[3].y -= sz; p[2].y -= sz; } - - } else { - + } else { // ceiling if (sx < 0) { p[0].y += sx; p[3].y += sx; @@ -174,40 +174,39 @@ namespace Debug { p[3].y += sz; p[2].y += sz; } - } - d++; - } - - - if (func == 0x04) { - //*d++; // trigger setup - /* - if (sub == 0x00) LOG("trigger\n"); - if (sub == 0x01) LOG("pad\n"); - if (sub == 0x02) LOG("switch\n"); - if (sub == 0x03) LOG("key\n"); - if (sub == 0x04) LOG("pickup\n"); - if (sub == 0x05) LOG("heavy-trigger\n"); - if (sub == 0x06) LOG("anti-pad\n"); - if (sub == 0x07) LOG("combat\n"); - if (sub == 0x08) LOG("dummy\n"); - if (sub == 0x09) LOG("anti-trigger\n"); - */ - uint16 act; - do { - act = *d++; // trigger action - } while (!(act & 0x8000)); - break; } - - } while (!(cmd & 0x8000)); - - if (current) - glColor3f(1, 1, 1); - else - glColor3f(0, 1, 0); + case TR::FD_TRIGGER : { + TR::FloorData::TriggerInfo info = (*fd++).triggerInfo; + TR::FloorData::TriggerCommand trigCmd; + glColor3f(1, 0, 1); + do { + trigCmd = (*fd++).triggerCmd; // trigger action + switch (trigCmd.func) { + case 0 : break; // activate item + case 1 : break; // switch to camera + case 2 : break; // camera delay + case 3 : break; // flip map + case 4 : break; // flip on + case 5 : break; // flip off + case 6 : break; // look at item + case 7 : break; // end level + case 8 : break; // play soundtrack + case 9 : break; // special hadrdcode trigger + case 10 : break; // secret found + case 11 : break; // clear bodies + case 12 : break; // flyby camera sequence + case 13 : break; // play cutscene + } + } while (!trigCmd.end); + break; + } + default : + if (!cmd.end) + LOG("unknown func %d : %d\n", cmd.func, cmd.sub); + } + } while (!cmd.end); glBegin(GL_LINE_STRIP); for (int i = 0; i < 5; i++) @@ -217,8 +216,43 @@ namespace Debug { glColor3f(1, 0, 0); glBegin(GL_LINE_STRIP); for (int i = 0; i < 5; i++) - glVertex3fv((GLfloat*)&vc[i % 4]); + glVertex3fv((GLfloat*)&vc[i % 4]); glEnd(); + + if (isPortal) { + glColor4f(0.0f, 0.0f, 1.0f, 0.5f); + glBegin(GL_QUADS); + for (int i = 3; i >= 0; i--) + glVertex3fv((GLfloat*)&vf[i]); + glEnd(); + } + + if (boxIndex == 0xFFFF) { + glBegin(GL_LINES); + float x = f.x + 512.0f, z = f.z + 512.0f; + glVertex3f(x, f.y, z); + glVertex3f(x, c.y, z); + glEnd(); + } + } + + void debugBox(const TR::Box &b) { + glBegin(GL_QUADS); + float y = b.floor - 16.0f; + glVertex3f(b.minX, y, b.maxZ); + glVertex3f(b.maxX, y, b.maxZ); + glVertex3f(b.maxX, y, b.minZ); + glVertex3f(b.minX, y, b.minZ); + glEnd(); + } + + void debugOverlaps(const TR::Level &level, int boxIndex) { + glColor4f(1.0f, 1.0f, 0.0f, 0.25f); + TR::Overlap *o = &level.overlaps[level.boxes[boxIndex].overlap & 0x7FFF]; + do { + TR::Box &b = level.boxes[o->boxIndex]; + debugBox(b); + } while (!(o++)->end); } void debugSectors(const TR::Level &level, const vec3 &pos, int roomIndex) { @@ -229,15 +263,30 @@ namespace Debug { for (int z = 0; z < room.zSectors; z++) for (int x = 0; x < room.xSectors; x++) { auto &s = room.sectors[x * room.zSectors + z]; - vec3 f(x * 1024 + room.info.x, s.floor * 256, z * 1024 + room.info.z); - vec3 c(x * 1024 + room.info.x, s.ceiling * 256, z * 1024 + room.info.z); + float floor = s.floor * 256; + /* + if (s.boxIndex < 0xFFFF) { + auto &b = level.boxes[s.boxIndex]; + // floor = b.floor; + } + */ + vec3 f(x * 1024 + room.info.x, floor - 1, z * 1024 + room.info.z); + vec3 c(x * 1024 + room.info.x, s.ceiling * 256 + 1, z * 1024 + room.info.z); - debugFloor(level, f, c, s.floorIndex, (int)p.x == x && (int)p.z == z); + bool current = (int)p.x == x && (int)p.z == z; + debugFloor(level, f, c, s.floorIndex, s.boxIndex, current); + + if (current && s.boxIndex != 0xFFFF && level.boxes[s.boxIndex].overlap != 0xFFFF) { + glDisable(GL_DEPTH_TEST); + glColor4f(0.0f, 1.0f, 0.0f, 0.25f); + debugBox(level.boxes[s.boxIndex]); + debugOverlaps(level, s.boxIndex); + glEnable(GL_DEPTH_TEST); + } } } void rooms(const TR::Level &level, const vec3 &pos, int roomIndex) { - Core::setBlending(bmAdd); glDepthMask(GL_FALSE); for (int i = 0; i < level.roomsCount; i++) { @@ -251,11 +300,10 @@ namespace Debug { } else glColor3f(1, 1, 1); - Debug::Draw::box(p, p + vec3(r.xSectors * 1024, r.info.yBottom - r.info.yTop, r.zSectors * 1024)); + // Debug::Draw::box(p, p + vec3(r.xSectors * 1024, r.info.yBottom - r.info.yTop, r.zSectors * 1024)); } glDepthMask(GL_TRUE); - Core::setBlending(bmAlpha); } void portals(const TR::Level &level) { diff --git a/src/format.h b/src/format.h index 48506d1..e61cc59 100644 --- a/src/format.h +++ b/src/format.h @@ -11,6 +11,13 @@ namespace TR { ROOM_FLAG_VISIBLE = 0x8000 }; + enum { + FD_PORTAL = 1, + FD_FLOOR = 2, + FD_CEILING = 3, + FD_TRIGGER = 4, + }; + #define DATA_PORTAL 0x01 #define DATA_FLOOR 0x02 #define DATA_CEILING 0x03 @@ -65,6 +72,7 @@ namespace TR { // http://www.tombraiderforums.com/showthread.php?t=148859&highlight=Explanation+left enum LaraAnim : int32 { ANIM_STAND = 11, + ANIM_FALL = 34, ANIM_SMASH_JUMP = 32, ANIM_SMASH_RUN_LEFT = 53, ANIM_SMASH_RUN_RIGHT = 54, @@ -82,7 +90,7 @@ namespace TR { STATE_TURN_RIGHT, STATE_TURN_LEFT, STATE_DEATH, - STATE_FAST_FALL, + STATE_FALL, STATE_HANG, STATE_REACH, STATE_SPLAT, @@ -129,25 +137,6 @@ namespace TR { STATE_FAST_DIVE, STATE_HANDSTAND, STATE_WATER_OUT, - STATE_CLIMB_START_AND_STANDING, - STATE_CLIMB_UP, - STATE_CLIMB_LEFT, - STATE_CLIMB_END, - STATE_CLIMB_RIGHT, - STATE_CLIMB_DOWN, - STATE_NULL_62, - STATE_NULL_63, - STATE_NULL_64, - STATE_WADE, - STATE_WATER_ROLL, - STATE_PICK_UP_FLARE, - STATE_NULL_68, - STATE_NULL_69, - STATE_DEATH_SLIDE, - STATE_DUCK, - STATE_DUCK_72, - STATE_DASH, - STATE_DASH_DIVE, STATE_MAX }; #pragma pack(push, 1) @@ -257,6 +246,26 @@ namespace TR { } *meshes; }; + union FloorData { + uint16 data; + struct Command { + uint16 func:8, sub:7, end:1; + } cmd; + struct Slant { + int8 x:8, z:8; + } slant; + struct TriggerInfo { + uint16 timer:8, once:1, mask:5, :2; + } triggerInfo; + struct TriggerCommand { + uint16 args:10, func:5, end:1; + } triggerCmd; + }; + + struct Overlap { + uint16 boxIndex:15, end:1; + }; + struct Mesh { Vertex center; int32 radius; @@ -417,7 +426,11 @@ namespace TR { int32 minZ, maxZ; // Horizontal dimensions in global units int32 minX, maxX; int16 floor; // Height value in global units - int16 overlap; // Index into Overlaps[]. + uint16 overlap; // Index into Overlaps[]. + + bool contains(int x, int z) { + return x >= minX && x <= maxX && z >= minZ && z <= maxZ; + } }; struct Zone { @@ -434,6 +447,7 @@ namespace TR { uint16 chance; // If !=0 and ((rand()&0x7fff) > Chance), this sound is not played uint16 flags; // Bits 0-1: Looped flag, bits 2-5: num samples, bits 6-7: UNUSED }; + #pragma pack(pop) struct Level { @@ -450,7 +464,7 @@ namespace TR { Room *rooms; int32 floorsCount; - uint16 *floors; + FloorData *floors; int32 meshDataSize; uint16 *meshData; @@ -500,7 +514,7 @@ namespace TR { int32 boxesCount; Box *boxes; int32 overlapsCount; - uint16 *overlaps; + Overlap *overlaps; Zone *zones; int32 animTexturesDataSize; diff --git a/src/level.h b/src/level.h index fa38b6a..5bef428 100644 --- a/src/level.h +++ b/src/level.h @@ -21,35 +21,21 @@ struct Level { TR::Level level; Shader *shaders[shMAX]; Texture *atlas; - Mesh *mesh; + MeshBuilder *mesh; Controller *lara; float time; - - struct RoomRange { - MeshRange geometry; - MeshRange sprites; - } *roomRanges; - MeshRange *spriteRanges; - Camera camera; - int mCount; - struct MeshInfo : MeshRange { - int offset; - TR::Vertex center; - int32 radius; - } *meshInfo; - Level(Stream &stream) : level{stream}, time(0.0f) { shaders[shStatic] = new Shader(SHADER); shaders[shCaustics] = new Shader(SHADER, "#define CAUSTICS\n"); shaders[shSprite] = new Shader(SHADER, "#define SPRITE\n"); - initAtlas(); - initMesh(); + + mesh = new MeshBuilder(level); int entity = 0; for (int i = 0; i < level.entitiesCount; i++) @@ -74,9 +60,6 @@ struct Level { delete shaders[i]; delete atlas; delete mesh; - delete[] roomRanges; - delete[] meshInfo; - delete[] spriteRanges; delete lara; } @@ -116,394 +99,6 @@ struct Level { delete[] data; } - void addSprite(Index *indices, Vertex *vertices, int &iCount, int &vCount, int vStart, int16 x, int16 y, int16 z, const TR::SpriteTexture &sprite, uint8 intensity) { - int vIndex = vCount - vStart; - - indices[iCount + 0] = vIndex + 0; - indices[iCount + 1] = vIndex + 1; - indices[iCount + 2] = vIndex + 2; - - indices[iCount + 3] = vIndex + 0; - indices[iCount + 4] = vIndex + 2; - indices[iCount + 5] = vIndex + 3; - - iCount += 6; - - Vertex *quad = &vertices[vCount]; - - quad[0].coord = quad[1].coord = quad[2].coord = quad[3].coord = { x, y, z }; - - int tx = (sprite.tile % 4) * 256; - int ty = (sprite.tile / 4) * 256; - - int16 u0 = ((tx + sprite.u) << 5) + 16; - int16 v0 = ((ty + sprite.v) << 5) + 16; - int16 u1 = u0 + (sprite.w >> 3); - int16 v1 = v0 + (sprite.h >> 3); - - quad[0].texCoord = { u0, v0 }; - quad[1].texCoord = { u1, v0 }; - quad[2].texCoord = { u1, v1 }; - quad[3].texCoord = { u0, v1 }; - - quad[0].normal = { sprite.r, sprite.t, 0, 0 }; - quad[1].normal = { sprite.l, sprite.t, 0, 0 }; - quad[2].normal = { sprite.l, sprite.b, 0, 0 }; - quad[3].normal = { sprite.r, sprite.b, 0, 0 }; - - quad[0].color = quad[1].color = quad[2].color = quad[3].color = { intensity, intensity, intensity, 255 }; - - vCount += 4; - } - - - void initMesh() { - // TODO: sort by texture attribute (t.attribute == 2 ? bmAdd : bmAlpha) - - roomRanges = new RoomRange[level.roomsCount]; - - int iCount = 0, vCount = 0; - - // get size of mesh for rooms (geometry & sprites) - for (int i = 0; i < level.roomsCount; i++) { - TR::Room::Data &d = level.rooms[i].data; - RoomRange &r = roomRanges[i]; - - r.geometry.vStart = vCount; - r.geometry.iStart = iCount; - iCount += d.rCount * 6 + d.tCount * 3; - vCount += d.rCount * 4 + d.tCount * 3; - r.geometry.iCount = iCount - r.geometry.iStart; - - r.sprites.vStart = vCount; - r.sprites.iStart = iCount; - iCount += d.sCount * 6; - vCount += d.sCount * 4; - r.sprites.iCount = iCount - r.sprites.iStart; - } - - // get objects mesh info - #define OFFSET(bytes) (ptr = (TR::Mesh*)((char*)ptr + (bytes) - sizeof(char*))) - - mCount = 0; - TR::Mesh *ptr = (TR::Mesh*)level.meshData; - while ( ((int)ptr - (int)level.meshData) < level.meshDataSize * 2 ) { - mCount++; - - OFFSET(ptr->vCount * sizeof(TR::Vertex)); - if (ptr->nCount > 0) - OFFSET(ptr->nCount * sizeof(TR::Vertex)); - else - OFFSET(-ptr->nCount * sizeof(int16)); - - iCount += ptr->rCount * 6; - vCount += ptr->rCount * 4; - OFFSET(ptr->rCount * sizeof(TR::Rectangle)); - - iCount += ptr->tCount * 3; - vCount += ptr->tCount * 3; - OFFSET(ptr->tCount * sizeof(TR::Triangle)); - - iCount += ptr->crCount * 6; - vCount += ptr->crCount * 4; - OFFSET(ptr->crCount * sizeof(TR::Rectangle)); - - iCount += ptr->ctCount * 3; - vCount += ptr->ctCount * 3; - OFFSET(ptr->ctCount * sizeof(TR::Triangle) + sizeof(TR::Mesh)); - ptr = (TR::Mesh*)(((int)ptr + 3) & -4); - } - meshInfo = new MeshInfo[mCount]; - - // get size of mesh for sprite sequences - spriteRanges = new MeshRange[level.spriteSequencesCount]; - for (int i = 0; i < level.spriteSequencesCount; i++) { - // TODO: sequences not only first frame - spriteRanges[i].vStart = vCount; - spriteRanges[i].iStart = iCount; - spriteRanges[i].iCount = 6; - iCount += 6; - vCount += 4; - } - - // make meshes buffer (single vertex buffer object for all geometry & sprites on level) - Index *indices = new Index[iCount]; - Vertex *vertices = new Vertex[vCount]; - iCount = vCount = 0; - - // build rooms - for (int i = 0; i < level.roomsCount; i++) { - TR::Room::Data &d = level.rooms[i].data; - - // rooms geometry - int vStart = vCount; - for (int j = 0; j < d.rCount; j++) { - auto &f = d.rectangles[j]; - auto &t = level.objectTextures[f.texture]; - - int tile = t.tileAndFlag & 0x7FFF; - int tx = (tile % 4) * 256; - int ty = (tile / 4) * 256; - - int vIndex = vCount - vStart; - - indices[iCount + 0] = vIndex + 0; - indices[iCount + 1] = vIndex + 1; - indices[iCount + 2] = vIndex + 2; - - indices[iCount + 3] = vIndex + 0; - indices[iCount + 4] = vIndex + 2; - indices[iCount + 5] = vIndex + 3; - - iCount += 6; - - for (int k = 0; k < 4; k++) { - auto &v = d.vertices[f.vertices[k]]; - uint8 a = 255 - (v.lighting >> 5); - - vertices[vCount].coord = { v.vertex.x, v.vertex.y, v.vertex.z }; - vertices[vCount].color = { a, a, a, 255 }; - vertices[vCount].normal = { 0, 0, 0, 1 }; - vertices[vCount].texCoord.x = ((tx + t.vertices[k].Xpixel) << 5) + 16; - vertices[vCount].texCoord.y = ((ty + t.vertices[k].Ypixel) << 5) + 16; - vCount++; - } - } - - for (int j = 0; j < d.tCount; j++) { - auto &f = d.triangles[j]; - auto &t = level.objectTextures[f.texture]; - - int tile = t.tileAndFlag & 0x7FFF; - int tx = (tile % 4) * 256; - int ty = (tile / 4) * 256; - - int vIndex = vCount - vStart; - - indices[iCount + 0] = vIndex + 0; - indices[iCount + 1] = vIndex + 1; - indices[iCount + 2] = vIndex + 2; - - iCount += 3; - - for (int k = 0; k < 3; k++) { - auto &v = d.vertices[f.vertices[k]]; - uint8 a = 255 - (v.lighting >> 5); - - vertices[vCount].coord = { v.vertex.x, v.vertex.y, v.vertex.z }; - vertices[vCount].color = { a, a, a, 255 }; - vertices[vCount].normal = { 0, 0, 0, 1 }; - vertices[vCount].texCoord.x = ((tx + t.vertices[k].Xpixel) << 5) + 16; - vertices[vCount].texCoord.y = ((ty + t.vertices[k].Ypixel) << 5) + 16; - vCount++; - } - } - - // rooms sprites - TR::Room::Info &info = level.rooms[i].info; - vStart = vCount; - for (int j = 0; j < d.sCount; j++) { - TR::Room::Data::Sprite &f = d.sprites[j]; - TR::Room::Data::Vertex &v = d.vertices[f.vertex]; - TR::SpriteTexture &sprite = level.spriteTextures[f.texture]; - uint8 intensity = 255 - (v.lighting >> 5); - addSprite(indices, vertices, iCount, vCount, vStart, v.vertex.x, v.vertex.y, v.vertex.z, sprite, intensity); - } - } - - // build objects geometry - mCount = 0; - ptr = (TR::Mesh*)level.meshData; - while ( ((int)ptr - (int)level.meshData) < level.meshDataSize * sizeof(uint16) ) { - MeshInfo &info = meshInfo[mCount++]; - info.offset = (int)ptr - (int)level.meshData; - info.vStart = vCount; - info.iStart = iCount; - info.center = ptr->center; - info.radius = ptr->radius; - - TR::Vertex *mVertices = (TR::Vertex*)&ptr->vertices; - - OFFSET(ptr->vCount * sizeof(TR::Vertex)); - - TR::Vertex *normals = NULL; - int16 *lights = NULL; - int nCount = ptr->nCount; - - if (ptr->nCount > 0) { - normals = (TR::Vertex*)&ptr->normals; - OFFSET(ptr->nCount * sizeof(TR::Vertex)); - } else { - lights = (int16*)&ptr->lights; - OFFSET(-ptr->nCount * sizeof(int16)); - } - - int vStart = vCount; - // rectangles - for (int j = 0; j < ptr->rCount; j++) { - auto &f = ((TR::Rectangle*)&ptr->rectangles)[j]; - auto &t = level.objectTextures[f.texture]; - - int tile = t.tileAndFlag & 0x7FFF; - int tx = (tile % 4) * 256; - int ty = (tile / 4) * 256; - - int vIndex = vCount - vStart; - - indices[iCount + 0] = vIndex + 0; - indices[iCount + 1] = vIndex + 1; - indices[iCount + 2] = vIndex + 2; - - indices[iCount + 3] = vIndex + 0; - indices[iCount + 4] = vIndex + 2; - indices[iCount + 5] = vIndex + 3; - - iCount += 6; - - for (int k = 0; k < 4; k++) { - auto &v = mVertices[f.vertices[k]]; - - vertices[vCount].coord = { v.x, v.y, v.z }; - - if (nCount > 0) { - TR::Vertex &n = normals[f.vertices[k]]; - vertices[vCount].normal = { n.x, n.y, n.z, 0 }; - vertices[vCount].color = { 255, 255, 255, 255 }; - } else { - uint8 a = 255 - (lights[f.vertices[k]] >> 5); - vertices[vCount].normal = { 0, 0, 0, 1 }; - vertices[vCount].color = { a, a, a, 255 }; - } - vertices[vCount].texCoord.x = ((tx + t.vertices[k].Xpixel) << 5) + 16; - vertices[vCount].texCoord.y = ((ty + t.vertices[k].Ypixel) << 5) + 16; - vCount++; - } - } - OFFSET(ptr->rCount * sizeof(TR::Rectangle)); - - // triangles - for (int j = 0; j < ptr->tCount; j++) { - auto &f = ((TR::Triangle*)&ptr->triangles)[j]; - auto &t = level.objectTextures[f.texture]; - - int tile = t.tileAndFlag & 0x7FFF; - int tx = (tile % 4) * 256; - int ty = (tile / 4) * 256; - - int vIndex = vCount - vStart; - - indices[iCount + 0] = vIndex + 0; - indices[iCount + 1] = vIndex + 1; - indices[iCount + 2] = vIndex + 2; - - iCount += 3; - - for (int k = 0; k < 3; k++) { - auto &v = mVertices[f.vertices[k]]; - vertices[vCount].coord = { v.x, v.y, v.z }; - - if (nCount > 0) { - TR::Vertex &n = normals[f.vertices[k]]; - vertices[vCount].normal = { n.x, n.y, n.z, 0 }; - vertices[vCount].color = { 255, 255, 255, 255 }; - } else { - uint8 a = 255 - (lights[f.vertices[k]] >> 5); - vertices[vCount].normal = { 0, 0, 0, 1 }; - vertices[vCount].color = { a, a, a, 255 }; - } - vertices[vCount].texCoord.x = ((tx + t.vertices[k].Xpixel) << 5) + 16; - vertices[vCount].texCoord.y = ((ty + t.vertices[k].Ypixel) << 5) + 16; - vCount++; - } - } - OFFSET(ptr->tCount * sizeof(TR::Triangle)); - - // color rectangles - for (int j = 0; j < ptr->crCount; j++) { - auto &f = ((TR::Rectangle*)&ptr->crectangles)[j]; - auto &c = level.palette[f.texture & 0xFF]; - - int vIndex = vCount - vStart; - - indices[iCount + 0] = vIndex + 0; - indices[iCount + 1] = vIndex + 1; - indices[iCount + 2] = vIndex + 2; - - indices[iCount + 3] = vIndex + 0; - indices[iCount + 4] = vIndex + 2; - indices[iCount + 5] = vIndex + 3; - - iCount += 6; - - for (int k = 0; k < 4; k++) { - auto &v = mVertices[f.vertices[k]]; - - vertices[vCount].coord = { v.x, v.y, v.z }; - - if (nCount > 0) { - TR::Vertex &n = normals[f.vertices[k]]; - vertices[vCount].normal = { n.x, n.y, n.z, 0 }; - vertices[vCount].color = { c.r, c.g, c.b, 255 }; - } else { - uint8 a = 255 - (lights[f.vertices[k]] >> 5); - vertices[vCount].normal = { 0, 0, 0, 1 }; - vertices[vCount].color = { a, a, a, 255 }; // TODO: apply color - } - vertices[vCount].texCoord = { 1022 << 5, 1022 << 5 }; - vCount++; - } - } - OFFSET(ptr->crCount * sizeof(TR::Rectangle)); - - // color triangles - for (int j = 0; j < ptr->ctCount; j++) { - auto &f = ((TR::Triangle*)&ptr->ctriangles)[j]; - auto &c = level.palette[f.texture & 0xFF]; - - int vIndex = vCount - vStart; - - indices[iCount + 0] = vIndex + 0; - indices[iCount + 1] = vIndex + 1; - indices[iCount + 2] = vIndex + 2; - - iCount += 3; - - for (int k = 0; k < 3; k++) { - auto &v = mVertices[f.vertices[k]]; - - vertices[vCount].coord = { v.x, v.y, v.z }; - - if (nCount > 0) { - TR::Vertex &n = normals[f.vertices[k]]; - vertices[vCount].normal = { n.x, n.y, n.z, 0 }; - vertices[vCount].color = { c.r, c.g, c.b, 255 }; - } else { - uint8 a = 255 - (lights[f.vertices[k]] >> 5); - vertices[vCount].normal = { 0, 0, 0, 1 }; - vertices[vCount].color = { a, a, a, 255 }; // TODO: apply color - } - vertices[vCount].texCoord = { 1022 << 5, 1022 << 5 }; - vCount++; - } - } - OFFSET(ptr->ctCount * sizeof(TR::Triangle) + sizeof(TR::Mesh)); - - ptr = (TR::Mesh*)(((int)ptr + 3) & -4); - - info.iCount = iCount - info.iStart; - } - - // build sprite sequences - for (int i = 0; i < level.spriteSequencesCount; i++) { - TR::SpriteTexture &sprite = level.spriteTextures[level.spriteSequences[i].sStart]; - addSprite(indices, vertices, iCount, vCount, vCount, 0, -16, 0, sprite, 255); - } - - mesh = new Mesh(indices, iCount, vertices, vCount); - delete[] indices; - delete[] vertices; - } - TR::StaticMesh* getMeshByID(int id) { for (int i = 0; i < level.staticMeshesCount; i++) if (level.staticMeshes[i].id == id) @@ -543,15 +138,15 @@ struct Level { sh->setParam(uLightColor, Core::lightColor); // render room geometry - mesh->render(roomRanges[index].geometry); + mesh->renderRoomGeometry(index); // render room sprites - if (roomRanges[index].sprites.iCount) { + if (mesh->hasRoomSprites(index)) { sh = shaders[shSprite]; sh->bind(); sh->setParam(uModel, Core::mModel); sh->setParam(uColor, Core::color); - mesh->render(roomRanges[index].sprites); + mesh->renderRoomSprites(index); } Core::mModel = m; @@ -597,18 +192,17 @@ struct Level { camera.frustum = camFrustum; // pop camera frustum } - void renderMesh(uint32 meshOffset) { if (!level.meshOffsets[meshOffset] && meshOffset) return; - for (int i = 0; i < mCount; i++) - if (meshInfo[i].offset == level.meshOffsets[meshOffset]) { - MeshInfo &m = meshInfo[i]; + for (int i = 0; i < mesh->mCount; i++) + if (mesh->meshInfo[i].offset == level.meshOffsets[meshOffset]) { + MeshBuilder::MeshInfo &m = mesh->meshInfo[i]; if (camera.frustum->isVisible(Core::mModel * m.center, m.radius)) { Core::active.shader->setParam(uModel, Core::mModel); - mesh->render(m); + mesh->renderMesh(i); } break; } @@ -816,7 +410,7 @@ struct Level { Core::active.shader->setParam(uColor, Core::color); for (int i = 0; i < level.spriteSequencesCount; i++) if (entity.id == level.spriteSequences[i].id) { - mesh->render(spriteRanges[i]); + mesh->renderSprite(i); break; } } @@ -901,7 +495,7 @@ struct Level { #ifdef _DEBUG Debug::begin(); Debug::Level::rooms(level, lara->pos, lara->getEntity().room); - Debug::Level::lights(level); + // Debug::Level::lights(level); Debug::Level::portals(level); Debug::end(); #endif diff --git a/src/mesh.h b/src/mesh.h index 8e0cb29..14be485 100644 --- a/src/mesh.h +++ b/src/mesh.h @@ -2,6 +2,7 @@ #define H_MESH #include "core.h" +#include "format.h" typedef unsigned short Index; @@ -57,4 +58,408 @@ struct Mesh { } }; +struct MeshBuilder { +// rooms + struct RoomRange { + MeshRange geometry; + MeshRange sprites; + } *roomRanges; + +// objects meshes + struct MeshInfo : MeshRange { + int offset; + TR::Vertex center; + int32 radius; + } *meshInfo; + int mCount; + +// sprite sequences + MeshRange *spriteRanges; + +// indexed mesh + Mesh *mesh; + + MeshBuilder(const TR::Level &level) { + roomRanges = new RoomRange[level.roomsCount]; + + int iCount = 0, vCount = 0; + + // get size of mesh for rooms (geometry & sprites) + for (int i = 0; i < level.roomsCount; i++) { + TR::Room::Data &d = level.rooms[i].data; + RoomRange &r = roomRanges[i]; + + r.geometry.vStart = vCount; + r.geometry.iStart = iCount; + iCount += d.rCount * 6 + d.tCount * 3; + vCount += d.rCount * 4 + d.tCount * 3; + r.geometry.iCount = iCount - r.geometry.iStart; + + r.sprites.vStart = vCount; + r.sprites.iStart = iCount; + iCount += d.sCount * 6; + vCount += d.sCount * 4; + r.sprites.iCount = iCount - r.sprites.iStart; + } + + // get objects mesh info + #define OFFSET(bytes) (ptr = (TR::Mesh*)((char*)ptr + (bytes) - sizeof(char*))) + + mCount = 0; + TR::Mesh *ptr = (TR::Mesh*)level.meshData; + while ( ((int)ptr - (int)level.meshData) < level.meshDataSize * 2 ) { + mCount++; + + OFFSET(ptr->vCount * sizeof(TR::Vertex)); + if (ptr->nCount > 0) + OFFSET(ptr->nCount * sizeof(TR::Vertex)); + else + OFFSET(-ptr->nCount * sizeof(int16)); + + iCount += ptr->rCount * 6; + vCount += ptr->rCount * 4; + OFFSET(ptr->rCount * sizeof(TR::Rectangle)); + + iCount += ptr->tCount * 3; + vCount += ptr->tCount * 3; + OFFSET(ptr->tCount * sizeof(TR::Triangle)); + + iCount += ptr->crCount * 6; + vCount += ptr->crCount * 4; + OFFSET(ptr->crCount * sizeof(TR::Rectangle)); + + iCount += ptr->ctCount * 3; + vCount += ptr->ctCount * 3; + OFFSET(ptr->ctCount * sizeof(TR::Triangle) + sizeof(TR::Mesh)); + ptr = (TR::Mesh*)(((int)ptr + 3) & -4); + } + meshInfo = new MeshInfo[mCount]; + + // get size of mesh for sprite sequences + spriteRanges = new MeshRange[level.spriteSequencesCount]; + for (int i = 0; i < level.spriteSequencesCount; i++) { + // TODO: sequences not only first frame + spriteRanges[i].vStart = vCount; + spriteRanges[i].iStart = iCount; + spriteRanges[i].iCount = 6; + iCount += 6; + vCount += 4; + } + + // make meshes buffer (single vertex buffer object for all geometry & sprites on level) + Index *indices = new Index[iCount]; + Vertex *vertices = new Vertex[vCount]; + iCount = vCount = 0; + + // build rooms + for (int i = 0; i < level.roomsCount; i++) { + TR::Room::Data &d = level.rooms[i].data; + + // rooms geometry + int vStart = vCount; + for (int j = 0; j < d.rCount; j++) { + auto &f = d.rectangles[j]; + auto &t = level.objectTextures[f.texture]; + + int tile = t.tileAndFlag & 0x7FFF; + int tx = (tile % 4) * 256; + int ty = (tile / 4) * 256; + + addQuad(indices, iCount, vCount, vStart); + + for (int k = 0; k < 4; k++) { + auto &v = d.vertices[f.vertices[k]]; + uint8 a = 255 - (v.lighting >> 5); + + vertices[vCount].coord = { v.vertex.x, v.vertex.y, v.vertex.z }; + vertices[vCount].color = { a, a, a, 255 }; + vertices[vCount].normal = { 0, 0, 0, 1 }; + vertices[vCount].texCoord.x = ((tx + t.vertices[k].Xpixel) << 5) + 16; + vertices[vCount].texCoord.y = ((ty + t.vertices[k].Ypixel) << 5) + 16; + vCount++; + } + } + + for (int j = 0; j < d.tCount; j++) { + auto &f = d.triangles[j]; + auto &t = level.objectTextures[f.texture]; + + int tile = t.tileAndFlag & 0x7FFF; + int tx = (tile % 4) * 256; + int ty = (tile / 4) * 256; + + addTriangle(indices, iCount, vCount, vStart); + + for (int k = 0; k < 3; k++) { + auto &v = d.vertices[f.vertices[k]]; + uint8 a = 255 - (v.lighting >> 5); + + vertices[vCount].coord = { v.vertex.x, v.vertex.y, v.vertex.z }; + vertices[vCount].color = { a, a, a, 255 }; + vertices[vCount].normal = { 0, 0, 0, 1 }; + vertices[vCount].texCoord.x = ((tx + t.vertices[k].Xpixel) << 5) + 16; + vertices[vCount].texCoord.y = ((ty + t.vertices[k].Ypixel) << 5) + 16; + vCount++; + } + } + + // rooms sprites + TR::Room::Info &info = level.rooms[i].info; + vStart = vCount; + for (int j = 0; j < d.sCount; j++) { + TR::Room::Data::Sprite &f = d.sprites[j]; + TR::Room::Data::Vertex &v = d.vertices[f.vertex]; + TR::SpriteTexture &sprite = level.spriteTextures[f.texture]; + uint8 intensity = 255 - (v.lighting >> 5); + addSprite(indices, vertices, iCount, vCount, vStart, v.vertex.x, v.vertex.y, v.vertex.z, sprite, intensity); + } + } + + // build objects geometry + mCount = 0; + ptr = (TR::Mesh*)level.meshData; + while ( ((int)ptr - (int)level.meshData) < level.meshDataSize * sizeof(uint16) ) { + MeshInfo &info = meshInfo[mCount++]; + info.offset = (int)ptr - (int)level.meshData; + info.vStart = vCount; + info.iStart = iCount; + info.center = ptr->center; + info.radius = ptr->radius; + + TR::Vertex *mVertices = (TR::Vertex*)&ptr->vertices; + + OFFSET(ptr->vCount * sizeof(TR::Vertex)); + + TR::Vertex *normals = NULL; + int16 *lights = NULL; + int nCount = ptr->nCount; + + if (ptr->nCount > 0) { + normals = (TR::Vertex*)&ptr->normals; + OFFSET(ptr->nCount * sizeof(TR::Vertex)); + } else { + lights = (int16*)&ptr->lights; + OFFSET(-ptr->nCount * sizeof(int16)); + } + + int vStart = vCount; + // rectangles + for (int j = 0; j < ptr->rCount; j++) { + auto &f = ((TR::Rectangle*)&ptr->rectangles)[j]; + auto &t = level.objectTextures[f.texture]; + + int tile = t.tileAndFlag & 0x7FFF; + int tx = (tile % 4) * 256; + int ty = (tile / 4) * 256; + + addQuad(indices, iCount, vCount, vStart); + + for (int k = 0; k < 4; k++) { + auto &v = mVertices[f.vertices[k]]; + + vertices[vCount].coord = { v.x, v.y, v.z }; + + if (nCount > 0) { + TR::Vertex &n = normals[f.vertices[k]]; + vertices[vCount].normal = { n.x, n.y, n.z, 0 }; + vertices[vCount].color = { 255, 255, 255, 255 }; + } else { + uint8 a = 255 - (lights[f.vertices[k]] >> 5); + vertices[vCount].normal = { 0, 0, 0, 1 }; + vertices[vCount].color = { a, a, a, 255 }; + } + vertices[vCount].texCoord.x = ((tx + t.vertices[k].Xpixel) << 5) + 16; + vertices[vCount].texCoord.y = ((ty + t.vertices[k].Ypixel) << 5) + 16; + vCount++; + } + } + OFFSET(ptr->rCount * sizeof(TR::Rectangle)); + + // triangles + for (int j = 0; j < ptr->tCount; j++) { + auto &f = ((TR::Triangle*)&ptr->triangles)[j]; + auto &t = level.objectTextures[f.texture]; + + int tile = t.tileAndFlag & 0x7FFF; + int tx = (tile % 4) * 256; + int ty = (tile / 4) * 256; + + addTriangle(indices, iCount, vCount, vStart); + + for (int k = 0; k < 3; k++) { + auto &v = mVertices[f.vertices[k]]; + vertices[vCount].coord = { v.x, v.y, v.z }; + + if (nCount > 0) { + TR::Vertex &n = normals[f.vertices[k]]; + vertices[vCount].normal = { n.x, n.y, n.z, 0 }; + vertices[vCount].color = { 255, 255, 255, 255 }; + } else { + uint8 a = 255 - (lights[f.vertices[k]] >> 5); + vertices[vCount].normal = { 0, 0, 0, 1 }; + vertices[vCount].color = { a, a, a, 255 }; + } + vertices[vCount].texCoord.x = ((tx + t.vertices[k].Xpixel) << 5) + 16; + vertices[vCount].texCoord.y = ((ty + t.vertices[k].Ypixel) << 5) + 16; + vCount++; + } + } + OFFSET(ptr->tCount * sizeof(TR::Triangle)); + + // color rectangles + for (int j = 0; j < ptr->crCount; j++) { + auto &f = ((TR::Rectangle*)&ptr->crectangles)[j]; + auto &c = level.palette[f.texture & 0xFF]; + + addQuad(indices, iCount, vCount, vStart); + + for (int k = 0; k < 4; k++) { + auto &v = mVertices[f.vertices[k]]; + + vertices[vCount].coord = { v.x, v.y, v.z }; + + if (nCount > 0) { + TR::Vertex &n = normals[f.vertices[k]]; + vertices[vCount].normal = { n.x, n.y, n.z, 0 }; + vertices[vCount].color = { c.r, c.g, c.b, 255 }; + } else { + uint8 a = 255 - (lights[f.vertices[k]] >> 5); + vertices[vCount].normal = { 0, 0, 0, 1 }; + vertices[vCount].color = { a, a, a, 255 }; // TODO: apply color + } + vertices[vCount].texCoord = { 1022 << 5, 1022 << 5 }; + vCount++; + } + } + OFFSET(ptr->crCount * sizeof(TR::Rectangle)); + + // color triangles + for (int j = 0; j < ptr->ctCount; j++) { + auto &f = ((TR::Triangle*)&ptr->ctriangles)[j]; + auto &c = level.palette[f.texture & 0xFF]; + + addTriangle(indices, iCount, vCount, vStart); + + for (int k = 0; k < 3; k++) { + auto &v = mVertices[f.vertices[k]]; + + vertices[vCount].coord = { v.x, v.y, v.z }; + + if (nCount > 0) { + TR::Vertex &n = normals[f.vertices[k]]; + vertices[vCount].normal = { n.x, n.y, n.z, 0 }; + vertices[vCount].color = { c.r, c.g, c.b, 255 }; + } else { + uint8 a = 255 - (lights[f.vertices[k]] >> 5); + vertices[vCount].normal = { 0, 0, 0, 1 }; + vertices[vCount].color = { a, a, a, 255 }; // TODO: apply color + } + vertices[vCount].texCoord = { 1022 << 5, 1022 << 5 }; + vCount++; + } + } + OFFSET(ptr->ctCount * sizeof(TR::Triangle) + sizeof(TR::Mesh)); + + ptr = (TR::Mesh*)(((int)ptr + 3) & -4); + + info.iCount = iCount - info.iStart; + } + + // build sprite sequences + for (int i = 0; i < level.spriteSequencesCount; i++) { + TR::SpriteTexture &sprite = level.spriteTextures[level.spriteSequences[i].sStart]; + addSprite(indices, vertices, iCount, vCount, vCount, 0, -16, 0, sprite, 255); + } + + mesh = new Mesh(indices, iCount, vertices, vCount); + delete[] indices; + delete[] vertices; + } + + ~MeshBuilder() { + delete[] roomRanges; + delete[] meshInfo; + delete[] spriteRanges; + delete mesh; + } + + void addTriangle(Index *indices, int &iCount, int vCount, int vStart) { + int vIndex = vCount - vStart; + + indices[iCount + 0] = vIndex + 0; + indices[iCount + 1] = vIndex + 1; + indices[iCount + 2] = vIndex + 2; + + iCount += 3; + } + + void addQuad(Index *indices, int &iCount, int vCount, int vStart) { + int vIndex = vCount - vStart; + + indices[iCount + 0] = vIndex + 0; + indices[iCount + 1] = vIndex + 1; + indices[iCount + 2] = vIndex + 2; + + indices[iCount + 3] = vIndex + 0; + indices[iCount + 4] = vIndex + 2; + indices[iCount + 5] = vIndex + 3; + + iCount += 6; + } + + void addSprite(Index *indices, Vertex *vertices, int &iCount, int &vCount, int vStart, int16 x, int16 y, int16 z, const TR::SpriteTexture &sprite, uint8 intensity) { + addQuad(indices, iCount, vCount, vStart); + + Vertex *quad = &vertices[vCount]; + + quad[0].coord = quad[1].coord = quad[2].coord = quad[3].coord = { x, y, z }; + + int tx = (sprite.tile % 4) * 256; + int ty = (sprite.tile / 4) * 256; + + int16 u0 = ((tx + sprite.u) << 5) + 16; + int16 v0 = ((ty + sprite.v) << 5) + 16; + int16 u1 = u0 + (sprite.w >> 3); + int16 v1 = v0 + (sprite.h >> 3); + + quad[0].texCoord = { u0, v0 }; + quad[1].texCoord = { u1, v0 }; + quad[2].texCoord = { u1, v1 }; + quad[3].texCoord = { u0, v1 }; + + quad[0].normal = { sprite.r, sprite.t, 0, 0 }; + quad[1].normal = { sprite.l, sprite.t, 0, 0 }; + quad[2].normal = { sprite.l, sprite.b, 0, 0 }; + quad[3].normal = { sprite.r, sprite.b, 0, 0 }; + + quad[0].color = quad[1].color = quad[2].color = quad[3].color = { intensity, intensity, intensity, 255 }; + + vCount += 4; + } + + void bind() { + mesh->bind(); + } + + void renderRoomGeometry(int roomIndex) { + mesh->render(roomRanges[roomIndex].geometry); + } + + void renderRoomSprites(int roomIndex) { + mesh->render(roomRanges[roomIndex].sprites); + } + + bool hasRoomSprites(int roomIndex) { + return roomRanges[roomIndex].sprites.iCount > 0; + } + + void renderMesh(int meshIndex) { + mesh->render(meshInfo[meshIndex]); + } + + void renderSprite(int spriteIndex) { + mesh->render(spriteRanges[spriteIndex]); + } +}; + #endif \ No newline at end of file diff --git a/src/utils.h b/src/utils.h index fd37ba5..6f44cec 100644 --- a/src/utils.h +++ b/src/utils.h @@ -20,6 +20,7 @@ #else #define ASSERT(expr) #define LOG(...) ((void)0) +// #define LOG(...) printf(__VA_ARGS__) #endif