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:
BIN
bin/OpenLara.exe
BIN
bin/OpenLara.exe
Binary file not shown.
343
src/controller.h
343
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);
|
||||
}
|
||||
|
||||
};
|
||||
|
174
src/debug.h
174
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) {
|
||||
|
60
src/format.h
60
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;
|
||||
|
430
src/level.h
430
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
|
||||
|
405
src/mesh.h
405
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
|
@@ -20,6 +20,7 @@
|
||||
#else
|
||||
#define ASSERT(expr)
|
||||
#define LOG(...) ((void)0)
|
||||
// #define LOG(...) printf(__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user