From 07e2b99eadd905eaed4c529f717ebf365a80c5f9 Mon Sep 17 00:00:00 2001 From: XProger Date: Tue, 6 Sep 2016 03:20:14 +0300 Subject: [PATCH] #3 movement using overlap info, separate mesh builder class --- bin/OpenLara.exe | Bin 38400 -> 38400 bytes src/controller.h | 343 ++++++++++++++++++++++++------------- src/debug.h | 174 ++++++++++++------- src/format.h | 60 ++++--- src/level.h | 430 ++--------------------------------------------- src/mesh.h | 405 ++++++++++++++++++++++++++++++++++++++++++++ src/utils.h | 1 + 7 files changed, 793 insertions(+), 620 deletions(-) diff --git a/bin/OpenLara.exe b/bin/OpenLara.exe index 7c7ad15b13fffcdc7f14bc44b3eb33044bbbd968..bc4c2af94cb1e779ad0d0bd5bc2c42b2ca3ee1c3 100644 GIT binary patch delta 11787 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 RfBK%8uQ%EbTA%4Q{$ElVrF#GX delta 11286 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[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