1
0
mirror of https://github.com/XProger/OpenLara.git synced 2025-08-13 16:44:50 +02:00

#3 movement using overlap info, separate mesh builder class

This commit is contained in:
XProger
2016-09-06 03:20:14 +03:00
parent 6a26a4eadd
commit 07e2b99ead
7 changed files with 793 additions and 620 deletions

Binary file not shown.

View File

@@ -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);
}
};

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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

View File

@@ -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

View File

@@ -20,6 +20,7 @@
#else
#define ASSERT(expr)
#define LOG(...) ((void)0)
// #define LOG(...) printf(__VA_ARGS__)
#endif