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

#5 fix portals culling, add camera controller

This commit is contained in:
XProger
2016-09-10 23:01:45 +03:00
parent 11ba6a52fe
commit b80c2790d0
8 changed files with 512 additions and 271 deletions

Binary file not shown.

View File

@@ -2,135 +2,194 @@
#define H_CAMERA
#include "core.h"
#include "controller.h"
#define MAX_CLIP_PLANES 16
#define MAX_CLIP_PLANES 10
struct Camera {
struct Frustum {
struct Frustum {
struct Poly {
vec3 vertices[MAX_CLIP_PLANES];
int count;
};
struct Poly {
vec3 vertices[MAX_CLIP_PLANES];
int count;
};
vec3 pos;
vec4 planes[MAX_CLIP_PLANES];
int count;
vec3 pos;
vec4 planes[MAX_CLIP_PLANES];
int count;
#ifdef _DEBUG
int dbg;
Poly debugPoly;
#endif
void calcPlanes(const mat4 &m) {
#ifdef _DEBUG
dbg = 0;
#endif
count = 5;
planes[0] = vec4(m.e30 - m.e20, m.e31 - m.e21, m.e32 - m.e22, m.e33 - m.e23); // near
planes[1] = vec4(m.e30 - m.e10, m.e31 - m.e11, m.e32 - m.e12, m.e33 - m.e13); // top
planes[2] = vec4(m.e30 - m.e00, m.e31 - m.e01, m.e32 - m.e02, m.e33 - m.e03); // right
planes[3] = vec4(m.e30 + m.e10, m.e31 + m.e11, m.e32 + m.e12, m.e33 + m.e13); // bottom
planes[4] = vec4(m.e30 + m.e00, m.e31 + m.e01, m.e32 + m.e02, m.e33 + m.e03); // left
for (int i = 0; i < count; i++)
planes[i] *= 1.0f / planes[i].xyz.length();
}
void calcPlanes(const mat4 &m) {
count = 4;
planes[0] = vec4(m.e30 - m.e00, m.e31 - m.e01, m.e32 - m.e02, m.e33 - m.e03); // right
planes[1] = vec4(m.e30 + m.e00, m.e31 + m.e01, m.e32 + m.e02, m.e33 + m.e03); // left
planes[2] = vec4(m.e30 - m.e10, m.e31 - m.e11, m.e32 - m.e12, m.e33 - m.e13); // top
planes[3] = vec4(m.e30 + m.e10, m.e31 + m.e11, m.e32 + m.e12, m.e33 + m.e13); // bottom
for (int i = 0; i < count; i++)
planes[i] *= 1.0f / planes[i].xyz.length();
void calcPlanes(const Poly &poly) {
count = 1 + poly.count; // add one for near plane (not changing)
ASSERT(count < MAX_CLIP_PLANES);
if (count < 4) return;
vec3 e1 = poly.vertices[0] - pos;
for (int i = 1; i < count; i++) {
vec3 e2 = poly.vertices[i % poly.count] - pos;
planes[i].xyz = e1.cross(e2).normal();
planes[i].w = -(pos.dot(planes[i].xyz));
e1 = e2;
}
#ifdef _DEBUG
dbg++;
debugPoly = poly;
#endif
}
void calcPlanes(const Poly &poly) {
count = poly.count;
ASSERT(count < MAX_CLIP_PLANES);
if (!count) return;
#ifdef _DEBUG
void debug() {
if (debugPoly.count < 3) return;
vec3 e1 = poly.vertices[0] - pos;
for (int i = 0; i < count; i++) {
vec3 e2 = poly.vertices[(i + 1) % count] - pos;
planes[i].xyz = e1.cross(e2).normal();
planes[i].w = -(pos.dot(planes[i].xyz));
e1 = e2;
glUseProgram(0);
Core::setBlending(bmAdd);
glDisable(GL_DEPTH_TEST);
glBegin(GL_TRIANGLES);
for (int i = 0; i < debugPoly.count; i++) {
glVertex3fv((GLfloat*)&pos);
glVertex3fv((GLfloat*)&debugPoly.vertices[i]);
glVertex3fv((GLfloat*)&debugPoly.vertices[(i + 1) % debugPoly.count]);
}
glEnd();
glColor3f(0, 1, 0);
glBegin(GL_LINE_STRIP);
for (int i = 0; i <= debugPoly.count; i++) {
glVertex3fv((GLfloat*)&debugPoly.vertices[i % debugPoly.count]);
}
glEnd();
glEnable(GL_DEPTH_TEST);
Core::setBlending(bmAlpha);
}
#endif
void clipPlane(const Poly &src, Poly &dst, const vec4 &plane) {
dst.count = 0;
if (!src.count) return;
float t1 = src.vertices[0].dot(plane.xyz) + plane.w;
for (int i = 0; i < src.count; i++) {
const vec3 &v1 = src.vertices[i];
const vec3 &v2 = src.vertices[(i + 1) % src.count];
float t2 = v2.dot(plane.xyz) + plane.w;
// hack for big float numbers
int s1 = sign((int)t1);
int s2 = sign((int)t2);
if (s1 >= 0) {
dst.vertices[dst.count++] = v1;
ASSERT(dst.count < MAX_CLIP_PLANES);
}
}
void clipPlane(const Poly &src, Poly &dst, const vec4 &plane) {
dst.count = 0;
if (!src.count) return;
float t1 = src.vertices[0].dot(plane.xyz) + plane.w;
for (int i = 0; i < src.count; i++) {
const vec3 &v1 = src.vertices[i];
const vec3 &v2 = src.vertices[(i + 1) % src.count];
float t2 = v2.dot(plane.xyz) + plane.w;
if (t1 >= 0.0f) {
dst.vertices[dst.count++] = v1;
ASSERT(dst.count < MAX_CLIP_PLANES);
}
if (t1 * t2 < 0.0f) {
float k1 = t2 / (t2 - t1);
float k2 = t1 / (t2 - t1);
dst.vertices[dst.count++] = v1 * k1 - v2 * k2;
ASSERT(dst.count < MAX_CLIP_PLANES);
}
t1 = t2;
if (s1 * s2 < 0) {
float k1 = t2 / (t2 - t1);
float k2 = t1 / (t2 - t1);
dst.vertices[dst.count++] = v1 * (float)k1 - v2 * (float)k2;
ASSERT(dst.count < MAX_CLIP_PLANES);
}
}
bool clipByPortal(const vec3 *vertices, const vec3 &normal) { // 4 vertices
if (normal.dot(pos - vertices[0]) < 0.0f) // check portal winding order
t1 = t2;
}
}
bool clipByPortal(const vec3 *vertices, int vCount, const vec3 &normal) {
if (normal.dot(pos - vertices[0]) < 0.0f) // check portal winding order
return false;
Poly poly[2];
poly[0].count = vCount;
memmove(poly[0].vertices, vertices, sizeof(vec3) * poly[0].count);
#ifdef _DEBUG
debugPoly.count = 0;
#endif
int j = 0;
for (int i = 1; i < count; i++, j ^= 1)
clipPlane(poly[j], poly[j ^ 1], planes[i]);
calcPlanes(poly[j]);
return count >= 4;
}
bool isVisible(const vec3 &min, const vec3 &max) const {
if (count < 4) return false;
for (int i = 0; i < count; i++) {
const vec3 &n = planes[i].xyz;
const float d = -planes[i].w;
if (n.dot(max) < d &&
n.dot(min) < d &&
n.dot(vec3(min.x, max.y, max.z)) < d &&
n.dot(vec3(max.x, min.y, max.z)) < d &&
n.dot(vec3(min.x, min.y, max.z)) < d &&
n.dot(vec3(max.x, max.y, min.z)) < d &&
n.dot(vec3(min.x, max.y, min.z)) < d &&
n.dot(vec3(max.x, min.y, min.z)) < d)
return false;
}
return true;
}
Poly poly[2];
bool isVisible(const vec3 &center, float radius) {
if (count < 4) return false;
poly[0].count = 4;
memmove(poly[0].vertices, vertices, sizeof(vec3) * poly[0].count);
int j = 0;
for (int i = 0; i < count; i++, j ^= 1)
clipPlane(poly[j], poly[j ^ 1], planes[i]);
if (poly[j].count < 3)
for (int i = 0; i < count; i++)
if (planes[i].xyz.dot(center) + planes[i].w < -radius)
return false;
return true;
}
calcPlanes(poly[j]);
return true;
}
};
bool isVisible(const vec3 &min, const vec3 &max) const {
if (count < 3) return false;
for (int i = 0; i < count; i++) {
const vec3 &n = planes[i].xyz;
const float d = -planes[i].w;
if (n.dot(max) < d &&
n.dot(vec3(min.x, max.y, max.z)) < d &&
n.dot(vec3(max.x, min.y, max.z)) < d &&
n.dot(vec3(min.x, min.y, max.z)) < d &&
n.dot(vec3(max.x, max.y, min.z)) < d &&
n.dot(vec3(min.x, max.y, min.z)) < d &&
n.dot(vec3(max.x, min.y, min.z)) < d &&
n.dot(min) < d)
return false;
}
return true;
}
bool isVisible(const vec3 &center, float radius) {
if (count < 3) return false;
for (int i = 0; i < count; i++)
if (planes[i].xyz.dot(center) + planes[i].w < -radius)
return false;
return true;
}
} *frustum;
struct Camera : Controller {
Controller *owner;
Frustum *frustum;
float fov, znear, zfar;
vec3 pos, angle, offset, deltaPos, deltaAngle, targetDeltaPos, targetAngle;
vec3 pos, target;
Camera() : frustum(new Frustum()) {}
int room;
Camera(TR::Level *level, Controller *owner) : Controller(level, owner->entity), owner(owner), frustum(new Frustum()) {
fov = 75.0f;
znear = 0.1f * 2048.0f;
zfar = 1000.0f * 2048.0f;
angle.y += PI;
room = owner->getEntity().room;
}
~Camera() {
delete frustum;
}
void update() {
virtual TR::Room& getRoom() {
return level->rooms[room];
}
virtual void update() {
#ifdef FREE_CAMERA
vec3 dir = vec3(sinf(angle.y - PI) * cosf(-angle.x), -sinf(-angle.x), cosf(angle.y - PI) * cosf(-angle.x));
vec3 v = vec3(0);
@@ -141,35 +200,73 @@ struct Camera {
if (Input::down[ikA]) v = v - dir.cross(vec3(0, 1, 0));
pos = pos + v.normal() * (Core::deltaTime * 2048.0f);
#endif
deltaPos = deltaPos.lerp(targetDeltaPos, Core::deltaTime * 10.0f);
angle = angle.lerp(targetAngle, Core::deltaTime);
// deltaPos = deltaPos.lerp(targetDeltaPos, Core::deltaTime * 10.0f);
// angle = angle.lerp(targetAngle, Core::deltaTime);
if (Input::down[ikMouseL]) {
vec2 delta = Input::mouse.pos - Input::mouse.start.L;
deltaAngle.x -= delta.y * 0.01f;
deltaAngle.y -= delta.x * 0.01f;
deltaAngle.x = min(max(deltaAngle.x + angle.x, -PI * 0.5f + EPS), PI * 0.5f - EPS) - angle.x;
angle.x += delta.y * 0.01f;
// angle.y -= delta.x * 0.01f;
Input::mouse.start.L = Input::mouse.pos;
}
// angle.x = owner->angle.x;
angle.y = PI - owner->angle.y;
angle.z = 0.0f;
angle.x = min(max(angle.x, -80 * DEG2RAD), 80 * DEG2RAD);
vec3 dir = vec3(sinf(PI - angle.y) * cosf(-angle.x), -sinf(-angle.x), cosf(PI - angle.y) * cosf(-angle.x));
float height = owner->inWater ? 256.0f : 768.0f;
target = vec3(owner->pos.x, owner->pos.y - height, owner->pos.z);
pos = target - dir * 1024.0;
FloorInfo info = getFloorInfo((int)pos.x, (int)pos.z);
if (info.roomNext != 255)
room = info.roomNext;
if (pos.y < info.ceiling) {
if (info.roomAbove != 255)
room = info.roomAbove;
else
if (info.ceiling != 0xffff8100)
pos.y = info.ceiling;
}
if (pos.y > info.floor) {
if (info.roomBelow != 255)
room = info.roomBelow;
else
if (info.floor != 0xffff8100)
pos.y = info.floor;
}
}
void setup() {
Core::mView.identity();
Core::mView.translate(vec3(-offset.x, -offset.y, -offset.z));
Core::mView.rotateZ(-(angle.z + deltaAngle.z));
Core::mView.rotateX(-(angle.x + deltaAngle.x));
Core::mView.rotateY(-(angle.y + deltaAngle.y));
Core::mView.translate(deltaPos - pos);
Core::mView.scale(vec3(-1, -1, 1));
Core::mViewInv = mat4(pos, target, vec3(0, -1, 0));
Core::mView = Core::mViewInv.inverse();
Core::mProj = mat4(fov, (float)Core::width / (float)Core::height, znear, zfar);
Core::mProj = mat4(fov, (float)Core::width / (float)Core::height, znear, zfar);
Core::mViewProj = Core::mProj * Core::mView;
Core::mViewInv = Core::mView.inverse();
Core::viewPos = Core::mViewInv.getPos();
Core::mViewProj = Core::mProj * Core::mView;
Core::viewPos = Core::mViewInv.offset.xyz;
frustum->pos = Core::viewPos;
frustum->calcPlanes(Core::mViewProj);
#ifdef _DEBUG
vec3 offset = vec3(0.0f) - (Input::down[ikR] ? (Core::mViewInv.dir.xyz * 2048 - vec3(0, 2048, 0)) : vec3(0.0f));
Core::mViewInv = mat4(pos - offset, target - offset, vec3(0, -1, 0));
Core::mView = Core::mViewInv.inverse();
Core::mProj = mat4(fov, (float)Core::width / (float)Core::height, znear, zfar);
Core::mViewProj = Core::mProj * Core::mView;
Core::viewPos = Core::mViewInv.offset.xyz;
#endif
}
};

View File

@@ -65,7 +65,7 @@ struct Controller {
return level->models[0];
}
TR::Room& getRoom() {
virtual TR::Room& getRoom() {
int index = getEntity().room;
ASSERT(index >= 0 && index < level->roomsCount);
return level->rooms[index];
@@ -73,9 +73,13 @@ struct Controller {
TR::Room::Sector& getSector(int x, int z, int &dx, int &dz) {
TR::Room &room = getRoom();
int sx = x - room.info.x;
int sz = z - room.info.z;
sx = clamp(sx, 0, (room.xSectors - 1) << 10);
sz = clamp(sz, 0, (room.zSectors - 1) << 10);
dx = sx & 1023; // mod 1024
dz = sz & 1023;
sx >>= 10; // div 1024
@@ -118,11 +122,6 @@ struct Controller {
return exists;
}
struct FloorInfo {
int floor, ceiling;
int roomNext, roomBelow, roomAbove;
};
int getOverlap(int fromX, int fromY, int fromZ, int toX, int toZ, int &delta) {
int dx, dz;
TR::Room::Sector &s = getSector(fromX, fromZ, dx, dz);
@@ -154,6 +153,11 @@ struct Controller {
return floor;
}
struct FloorInfo {
int floor, ceiling;
int roomNext, roomBelow, roomAbove;
};
FloorInfo getFloorInfo(int x, int z) {
int dx, dz;
TR::Room::Sector &s = getSector(x, z, dx, dz);
@@ -259,8 +263,6 @@ struct Controller {
#define GLIDE_SPEED 50.0f
struct Lara : Controller {
int sc;
bool lState;
Lara(TR::Level *level, int entity) : Controller(level, entity) {
/*
@@ -268,6 +270,11 @@ struct Lara : Controller {
angle = vec3(0.0f, -0.68f, 0.0f);
getEntity().room = 15;
*/
/*
pos = vec3(41015, 3584, 34494);
angle = vec3(0.0f, -PI, 0.0f);
getEntity().room = 51;
*/
}
virtual void update() {
@@ -425,7 +432,7 @@ struct Lara : Controller {
#ifdef _DEBUG
// show state transitions for current animation
static bool lState = false;
if (Input::down[ikEnter]) {
if (!lState) {
lState = true;
@@ -600,11 +607,11 @@ struct Lara : Controller {
fIndex = anim->nextFrame - nextAnim->frameStart;
fTime = fIndex / 30.0f;
}
move(velocity * dt);
collide();
updateEntity();
lastFrame = fIndex;
}

View File

@@ -17,6 +17,8 @@ namespace Debug {
glPointSize(32);
glUseProgram(0);
Core::active.shader = NULL;
Core::active.testures[0] = NULL;
}
void end() {
@@ -25,7 +27,8 @@ namespace Debug {
namespace Draw {
void box(const vec3 &min, const vec3 &max) {
void box(const vec3 &min, const vec3 &max, const vec4 &color) {
glColor4fv((GLfloat*)&color);
glBegin(GL_LINES);
glVertex3f(min.x, min.y, min.z);
glVertex3f(max.x, min.y, min.z);
@@ -350,6 +353,98 @@ namespace Debug {
glEnd();
}
void meshes(const TR::Level &level) {
// static objects
for (int i = 0; i < level.roomsCount; i++) {
TR::Room &r = level.rooms[i];
for (int j = 0; j < r.meshesCount; j++) {
TR::Room::Mesh &m = r.meshes[j];
TR::StaticMesh *sm = level.getMeshByID(m.meshID);
ASSERT(sm != NULL);
vec3 min, max, offset = vec3(m.x, m.y, m.z);
sm->getBox(false, m.rotation, min, max); // visible box
Debug::Draw::box(offset + min, offset + max, vec4(1, 1, 0, 0.25));
if (sm->flags == 2) { // collision box
sm->getBox(true, m.rotation, min, max);
Debug::Draw::box(offset + min - vec3(10.0f), offset + max + vec3(10.0f), vec4(1, 0, 0, 0.50));
}
TR::Mesh *mesh = (TR::Mesh*)&level.meshData[level.meshOffsets[sm->mesh] / 2];
ASSERT(mesh->radius == 0 || mesh->radius == 0x10000);
Debug::Draw::sphere(offset + (min + max) * 0.5f, 128, mesh->radius == 0 ? vec4(0, 0, 1, 1) : vec4(0, 1, 0, 1));
}
}
// dynamic objects
for (int i = 0; i < level.entitiesCount; i++) {
TR::Entity &e = level.entities[i];
mat4 matrix;
matrix.identity();
matrix.translate(vec3(e.x, e.y, e.z));
matrix.rotateY(e.rotation / 16384.0f * PI * 0.5f);
for (int j = 0; j < level.modelsCount; j++) {
TR::Model &m = level.models[j];
TR::Node *node = m.node < level.nodesDataSize ? (TR::Node*)&level.nodesData[m.node] : NULL;
if (!node) continue; // ???
TR::Animation *anim = m.animation < 0xFFFF ? &level.anims[m.animation] : NULL;
TR::AnimFrame *frame = anim ? (TR::AnimFrame*)&level.frameData[anim->frameOffset >> 1] : NULL;
//mat4 m;
//m.identity();
// m.translate(vec3(frame->x, frame->y, frame->z).lerp(vec3(frameB->x, frameB->y, frameB->z), k));
int sIndex = 0;
mat4 stack[20];
mat4 joint;
joint.identity();
if (frame) joint.translate(frame->pos);
if (e.id == m.id) {
for (int k = 0; k < m.mCount; k++) {
if (k > 0 && node) {
TR::Node &t = node[k - 1];
if (t.flags & 0x01) joint = stack[--sIndex];
if (t.flags & 0x02) stack[sIndex++] = joint;
ASSERT(sIndex >= 0 && sIndex < 20);
joint.translate(vec3(t.x, t.y, t.z));
}
vec3 a = frame ? frame->getAngle(k) : vec3(0.0f);
mat4 rot;
rot.identity();
rot.rotateY(a.y);
rot.rotateX(a.x);
rot.rotateZ(a.z);
joint = joint * rot;
int offset = level.meshOffsets[m.mStart + k];
TR::Mesh *mesh = (TR::Mesh*)&level.meshData[offset / 2];
Debug::Draw::sphere(matrix * joint * mesh->center, mesh->radius & 0x3FF, mesh->radius > 0x3FF ? vec4(1, 0, 0, 0.5f) : vec4(0, 1, 1, 0.5f));
}
break;
}
}
}
}
}
}

View File

@@ -244,7 +244,7 @@ namespace TR {
uint16 rotation;
uint16 intensity;
uint16 meshID;
uint16 align; // ! not exists in file !
uint16 flags; // ! not exists in file !
} *meshes;
};
@@ -342,11 +342,20 @@ namespace TR {
};
struct AnimFrame {
int16 minX, minY, minZ; // Bounding box (low)
int16 maxX, maxY, maxZ; // Bounding box (high)
int16 x, y, z; // Starting offset for this model
TR::Vertex min; // Bounding box (low)
TR::Vertex max; // Bounding box (high)
TR::Vertex pos; // Starting offset for this model
int16 aCount;
uint16 angles[0]; // angle frames in YXZ order
vec3 getAngle(int index) {
#define ANGLE_SCALE (2.0f * PI / 1024.0f)
uint16 b = angles[index * 2 + 0];
uint16 a = angles[index * 2 + 1];
return vec3((a & 0x3FF0) >> 4, ( ((a & 0x000F) << 6) | ((b & 0xFC00) >> 10)), b & 0x03FF) * ANGLE_SCALE;
}
};
struct AnimTexture {
@@ -372,9 +381,40 @@ namespace TR {
struct StaticMesh {
uint32 id; // Static Mesh Identifier
uint16 mesh; // Mesh (offset into MeshPointers[])
Vertex vBox[2];
Vertex cBox[2];
struct MinMax {
int16 minX, maxX, minY, maxY, minZ, maxZ;
} box[2]; // visible (minX, maxX, minY, maxY, minZ, maxZ) & collision
uint16 flags;
void getBox(bool collision, int rotation, vec3 &min, vec3 &max) {
int k = rotation / 16384;
MinMax &m = box[collision];
ASSERT(m.minX <= m.maxX && m.minY <= m.maxY && m.minZ <= m.maxZ);
switch (k) {
case 0 :
min = vec3(m.minX, m.minY, m.minZ);
max = vec3(m.maxX, m.maxY, m.maxZ);
break;
case 1 :
min = vec3(m.minZ, m.minY, -m.maxX);
max = vec3(m.maxZ, m.maxY, -m.minX);
break;
case 2 :
min = vec3(-m.maxX, m.minY, -m.maxZ);
max = vec3(-m.minX, m.maxY, -m.minZ);
break;
case 3 :
min = vec3(-m.maxZ, m.minY, m.minX);
max = vec3(-m.minZ, m.maxY, m.maxX);
break;
default :
ASSERT(false);
}
ASSERT(min.x <= max.x && min.y <= max.y && min.z <= max.z);
}
};
struct Tile {
@@ -589,7 +629,7 @@ namespace TR {
// meshes
r.meshes = new Room::Mesh[stream.read(r.meshesCount)];
for (int i = 0; i < r.meshesCount; i++)
stream.raw(&r.meshes[i], sizeof(r.meshes[i]) - sizeof(r.meshes[i].align));
stream.raw(&r.meshes[i], sizeof(r.meshes[i]) - sizeof(r.meshes[i].flags));
// misc flags
stream.read(r.alternateRoom);
stream.read(r.flags);
@@ -706,6 +746,13 @@ namespace TR {
delete[] soundData;
delete[] soundOffsets;
}
TR::StaticMesh* getMeshByID(int id) const { // TODO: map this
for (int i = 0; i < staticMeshesCount; i++)
if (staticMeshes[i].id == id)
return &staticMeshes[i];
return NULL;
}
};
}

View File

@@ -7,6 +7,7 @@
#include "controller.h"
#include "camera.h"
#ifdef _DEBUG
#include "debug.h"
#endif
@@ -24,9 +25,9 @@ struct Level {
MeshBuilder *mesh;
Controller *lara;
Camera *camera;
float time;
Camera camera;
Level(Stream &stream) : level{stream}, time(0.0f) {
mesh = new MeshBuilder(level);
@@ -42,15 +43,8 @@ struct Level {
break;
}
lara = new Lara(&level, entity);
camera.fov = 75.0f;
camera.znear = 0.1f * 2048.0f;
camera.zfar = 1000.0f * 2048.0f;
camera.offset = vec3(0, 0, 1024);
camera.deltaPos = vec3(0.0f, 768.0f, 0.0f);
camera.deltaAngle = vec3(0.0f, PI, 0.0f);
camera.angle = vec3(0.0f);
lara = new Lara(&level, entity);
camera = new Camera(&level, lara);
}
~Level() {
@@ -59,6 +53,7 @@ struct Level {
delete atlas;
delete mesh;
delete camera;
delete lara;
}
@@ -144,13 +139,6 @@ struct Level {
*/
}
TR::StaticMesh* getMeshByID(int id) {
for (int i = 0; i < level.staticMeshesCount; i++)
if (level.staticMeshes[i].id == id)
return &level.staticMeshes[i];
return NULL;
}
Shader *setRoomShader(const TR::Room &room, float intensity) {
if (room.flags & TR::ROOM_FLAG_WATER) {
Core::color = vec4(0.6f * intensity, 0.9f * intensity, 0.9f * intensity, 1.0f);
@@ -161,107 +149,120 @@ struct Level {
}
}
void renderRoom(int index) {
ASSERT(index >= 0 && index < level.roomsCount);
TR::Room &room = level.rooms[index];
if (room.flags & TR::ROOM_FLAG_VISIBLE) return; // already rendered
room.flags |= TR::ROOM_FLAG_VISIBLE;
void renderRoom(int roomIndex, int from = -1) {
ASSERT(roomIndex >= 0 && roomIndex < level.roomsCount);
TR::Room &room = level.rooms[roomIndex];
vec3 offset = vec3(room.info.x, 0.0f, room.info.z);
mat4 m = Core::mModel;
Core::mModel.translate(offset);
Core::ambient = vec3(1.0f);
Core::lightColor = vec4(0.0f, 0.0f, 0.0f, 1.0f);
Shader *sh = setRoomShader(room, 1.0f);
sh->bind();
sh->setParam(uModel, Core::mModel);
sh->setParam(uColor, Core::color);
sh->setParam(uAmbient, Core::ambient);
sh->setParam(uLightColor, Core::lightColor);
// render room geometry
mesh->renderRoomGeometry(index);
// render room sprites
if (mesh->hasRoomSprites(index)) {
sh = shaders[shSprite];
sh->bind();
sh->setParam(uModel, Core::mModel);
sh->setParam(uColor, Core::color);
mesh->renderRoomSprites(index);
}
Core::mModel = m;
// meshes
// room static meshes
for (int i = 0; i < room.meshesCount; i++) {
TR::Room::Mesh &rMesh = room.meshes[i];
TR::StaticMesh *sMesh = getMeshByID(rMesh.meshID);
if ((rMesh.flags & TR::ROOM_FLAG_VISIBLE)) continue; // skip if already rendered
TR::StaticMesh *sMesh = level.getMeshByID(rMesh.meshID);
ASSERT(sMesh != NULL);
mat4 m = Core::mModel;
Core::mModel.translate(vec3((float)rMesh.x, (float)rMesh.y, (float)rMesh.z));
// check visibility
vec3 min, max, offset = vec3(rMesh.x, rMesh.y, rMesh.z);
sMesh->getBox(false, rMesh.rotation, min, max);
if (!camera->frustum->isVisible(offset + min, offset + max))
continue;
rMesh.flags |= TR::ROOM_FLAG_VISIBLE;
// set light parameters
getLight(offset, roomIndex);
// render static mesh
mat4 mTemp = Core::mModel;
Core::mModel.translate(offset);
Core::mModel.rotateY(rMesh.rotation / 16384.0f * PI * 0.5f);
// TODO: check visibility for sMesh.vBox
getLight(vec3(rMesh.x, rMesh.y, rMesh.z), index);
renderMesh(sMesh->mesh);
Core::mModel = m;
Core::mModel = mTemp;
}
Camera::Frustum *camFrustum = camera.frustum; // push camera frustum
Camera::Frustum frustum = *camFrustum;
camera.frustum = &frustum;
// room geometry & sprites
if (!(room.flags & TR::ROOM_FLAG_VISIBLE)) { // skip if already rendered
room.flags |= TR::ROOM_FLAG_VISIBLE;
Core::lightColor = vec4(0.0f, 0.0f, 0.0f, 1.0f);
Core::ambient = vec3(1.0f);
sh->setParam(uLightColor, Core::lightColor);
sh->setParam(uAmbient, Core::ambient);
mat4 mTemp = Core::mModel;
Core::mModel.translate(offset);
// render room geometry
sh->setParam(uModel, Core::mModel);
mesh->renderRoomGeometry(roomIndex);
// render room sprites
if (mesh->hasRoomSprites(roomIndex)) {
sh = shaders[shSprite];
sh->bind();
sh->setParam(uModel, Core::mModel);
sh->setParam(uColor, Core::color);
mesh->renderRoomSprites(roomIndex);
}
Core::mModel = mTemp;
}
// render rooms through portals recursively
Frustum *camFrustum = camera->frustum; // push camera frustum
Frustum frustum;
camera->frustum = &frustum;
for (int i = 0; i < room.portalsCount; i++) {
TR::Room::Portal &p = room.portals[i];
vec3 v[4] = {
if (p.roomIndex == from) continue;
vec3 v[] = {
offset + p.vertices[0],
offset + p.vertices[1],
offset + p.vertices[2],
offset + p.vertices[3],
};
if (frustum.clipByPortal(v, p.normal)) {
renderRoom(p.roomIndex);
frustum = *camFrustum;
}
frustum = *camFrustum;
if (frustum.clipByPortal(v, 4, p.normal))
renderRoom(p.roomIndex, roomIndex);
}
camera.frustum = camFrustum; // pop camera frustum
camera->frustum = camFrustum; // pop camera frustum
#ifdef _DEBUG
glColor3f(0, 0.05, 0);
camera->frustum->debug();
#endif
}
MeshBuilder::MeshInfo* getMeshInfoByOffset(uint32 meshOffset) {
if (!level.meshOffsets[meshOffset] && meshOffset)
return NULL;
for (int i = 0; i < mesh->mCount; i++)
if (mesh->meshInfo[i].offset == level.meshOffsets[meshOffset])
return &mesh->meshInfo[i];
return NULL;
}
void renderMesh(uint32 meshOffset) {
if (!level.meshOffsets[meshOffset] && meshOffset)
return;
MeshBuilder::MeshInfo *m = getMeshInfoByOffset(meshOffset);
ASSERT(m != NULL);
if (!m) return;
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->renderMesh(i);
}
break;
}
}
vec3 getAngle(TR::AnimFrame *frame, int index) {
#define ANGLE_SCALE (2.0f * PI / 1024.0f)
uint16 b = frame->angles[index * 2 + 0];
uint16 a = frame->angles[index * 2 + 1];
return vec3((a & 0x3FF0) >> 4, ( ((a & 0x000F) << 6) | ((b & 0xFC00) >> 10)), b & 0x03FF) * ANGLE_SCALE;
if ((m->radius & 0xFFFF) == 0 || camera->frustum->isVisible(Core::mModel * m->center, (m->radius & 0x3FF)) * 2) {
Core::active.shader->setParam(uModel, Core::mModel);
mesh->renderMesh(m);
}
}
float lerpAngle(float a, float b, float t) {
@@ -327,23 +328,7 @@ struct Level {
} else
nextAnim = anim;
// LOG("%d %f\n", fIndexA, fTime);
TR::AnimFrame *frameB = (TR::AnimFrame*)&level.frameData[(nextAnim->frameOffset + fIndexB * fSize) >> 1];
// ASSERT(fpSize == fSize);
// fSize = fpSize;
// LOG("%d\n", fIndex % fCount);
//if (fCount > 1) LOG("%d %d\n", model->id, fCount);
// LOG("%d\n", fIndex % fCount);
// Debug::Draw::box(Box(vec3(frameA->minX, frameA->minY, frameA->minZ), vec3(frameA->maxX, frameA->maxY, frameA->maxZ)));
TR::Node *node = (int)model.node < level.nodesDataSize ? (TR::Node*)&level.nodesData[model.node] : NULL;
int sIndex = 0;
@@ -351,7 +336,7 @@ struct Level {
mat4 m;
m.identity();
m.translate(vec3(frameA->x, frameA->y, frameA->z).lerp(vec3(frameB->x, frameB->y, frameB->z), k));
m.translate(((vec3)frameA->pos).lerp(frameB->pos, k));
for (int i = 0; i < model.mCount; i++) {
@@ -361,13 +346,12 @@ struct Level {
if (t.flags & 0x01) m = stack[--sIndex];
if (t.flags & 0x02) stack[sIndex++] = m;
ASSERT(sIndex >= 0);
ASSERT(sIndex < 20);
ASSERT(sIndex >= 0 && sIndex < 20);
m.translate(vec3(t.x, t.y, t.z));
}
quat q = lerpAngle(getAngle(frameA, i), getAngle(frameB, i), k);
quat q = lerpAngle(frameA->getAngle(i), frameB->getAngle(i), k);
m = m * mat4(q, vec3(0.0f));
@@ -403,6 +387,7 @@ struct Level {
void getLight(const vec3 &pos, int roomIndex) {
int room = roomIndex;
int idx = getLightIndex(pos, room);
if (idx > -1) {
TR::Room::Light &light = level.rooms[room].lights[idx];
float c = level.rooms[room].lights[idx].intensity / 8191.0f;
@@ -412,6 +397,7 @@ struct Level {
Core::lightPos = vec3(0);
Core::lightColor = vec4(0, 0, 0, 1);
}
Core::ambient = vec3(1.0f - level.rooms[roomIndex].ambient / 8191.0f);
Core::active.shader->setParam(uAmbient, Core::ambient);
Core::active.shader->setParam(uLightPos, Core::lightPos);
@@ -470,13 +456,7 @@ struct Level {
void update() {
time += Core::deltaTime;
lara->update();
#ifndef FREE_CAMERA
camera.pos = vec3(-lara->pos.x, -lara->pos.y, lara->pos.z);
#endif
camera.targetDeltaPos = lara->inWater ? vec3(0.0f, -256.0f, 0.0f) : vec3(0.0f, -768.0f, 0.0f);
camera.targetAngle = vec3(lara->angle.x, -lara->angle.y, 0.0f); //-lara->angle.z);
camera.update();
camera->update();
}
int getCameraRoomIndex() {
@@ -489,7 +469,7 @@ struct Level {
void render() {
// glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
camera.setup();;
camera->setup();;
atlas->bind(0);
mesh->bind();
@@ -510,12 +490,17 @@ struct Level {
Core::mModel.identity();
for (int i = 0; i < level.roomsCount; i++)
level.rooms[i].flags &= ~TR::ROOM_FLAG_VISIBLE; // clear visible flag
// clear visible flags for rooms & static meshes
for (int i = 0; i < level.roomsCount; i++) {
TR::Room &room = level.rooms[i];
room.flags &= ~TR::ROOM_FLAG_VISIBLE; // clear visible flag for room geometry & sprites
for (int j = 0; j < room.meshesCount; j++)
room.meshes[j].flags &= ~TR::ROOM_FLAG_VISIBLE; // clear visible flag for room static meshes
}
// TODO: collision detection for camera
renderRoom(getCameraRoomIndex());
renderRoom(lara->getEntity().room);
renderRoom(camera->room);
shaders[shStatic]->bind();
for (int i = 0; i < level.entitiesCount; i++)
@@ -523,9 +508,10 @@ struct Level {
#ifdef _DEBUG
Debug::begin();
Debug::Level::rooms(level, lara->pos, lara->getEntity().room);
// Debug::Level::rooms(level, lara->pos, lara->getEntity().room);
// Debug::Level::lights(level);
Debug::Level::portals(level);
// Debug::Level::portals(level);
Debug::Level::meshes(level);
Debug::end();
#endif
}

View File

@@ -393,13 +393,7 @@ struct MeshBuilder {
}
void initAnimTextures(TR::Level &level) {
if (!level.animTexturesDataSize) {
animTexRangesCount = animTexOffsetsCount = 1;
animTexRanges = new vec2[1];
animTexOffsets = new vec2[1];
animTexRanges[0] = vec2(0.0f, 1.0f);
animTexOffsets[0] = vec2(0.0f);
}
ASSERT(level.animTexturesDataSize);
uint16 *ptr = &level.animTexturesData[0];
animTexRangesCount = *ptr++ + 1;
@@ -507,8 +501,9 @@ struct MeshBuilder {
Vertex *quad = &vertices[vCount];
quad[0].coord = quad[1].coord = quad[2].coord = quad[3].coord = { x, y, z, 0 };
quad[0].coord = quad[1].coord = quad[2].coord = quad[3].coord = { x, y, z, 0 };
quad[0].normal = quad[1].normal = quad[2].normal = quad[3].normal = { 0, 0, 0, 0 };
quad[0].color = quad[1].color = quad[2].color = quad[3].color = { intensity, intensity, intensity, 255 };
int tx = (sprite.tile % 4) * 256;
int ty = (sprite.tile / 4) * 256;
@@ -523,8 +518,6 @@ struct MeshBuilder {
quad[2].texCoord = { u1, v1, sprite.l, sprite.b };
quad[3].texCoord = { u0, v1, sprite.r, sprite.b };
quad[0].color = quad[1].color = quad[2].color = quad[3].color = { intensity, intensity, intensity, 255 };
vCount += 4;
}
@@ -544,8 +537,12 @@ struct MeshBuilder {
return roomRanges[roomIndex].sprites.iCount > 0;
}
void renderMesh(MeshInfo *meshInfo) {
mesh->render(*meshInfo);
}
void renderMesh(int meshIndex) {
mesh->render(meshInfo[meshIndex]);
renderMesh(&meshInfo[meshIndex]);
}
void renderSprite(int spriteIndex) {

View File

@@ -278,6 +278,18 @@ struct mat4 {
e23 = 2.0f * zfar * znear / (znear - zfar);
}
mat4(const vec3 &from, const vec3 &at, const vec3 &up) {
vec3 r, u, d;
d = (from - at).normal();
r = up.cross(d).normal();
u = d.cross(r);
this->right = vec4(r, 0.0f);
this->up = vec4(u, 0.0f);
this->dir = vec4(d, 0.0f);
this->offset = vec4(from, 1.0f);
}
void identity() {
e10 = e20 = e30 = e01 = e21 = e31 = e02 = e12 = e32 = e03 = e13 = e23 = 0.0f;
e00 = e11 = e22 = e33 = 1.0f;