diff --git a/bin/OpenLara.exe b/bin/OpenLara.exe index a6ffd44..00167a6 100644 Binary files a/bin/OpenLara.exe and b/bin/OpenLara.exe differ diff --git a/src/camera.h b/src/camera.h index 8cced19..63b6cc4 100644 --- a/src/camera.h +++ b/src/camera.h @@ -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 ¢er, 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 ¢er, 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 } }; diff --git a/src/controller.h b/src/controller.h index f0ff44d..95b2812 100644 --- a/src/controller.h +++ b/src/controller.h @@ -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; } diff --git a/src/debug.h b/src/debug.h index 8907e44..9dea695 100644 --- a/src/debug.h +++ b/src/debug.h @@ -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; + } + + } + + } + } + } } diff --git a/src/format.h b/src/format.h index ba148ac..0c78207 100644 --- a/src/format.h +++ b/src/format.h @@ -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; + } }; } diff --git a/src/level.h b/src/level.h index 15fd2a6..4903fe6 100644 --- a/src/level.h +++ b/src/level.h @@ -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 } diff --git a/src/mesh.h b/src/mesh.h index 6233afa..720be71 100644 --- a/src/mesh.h +++ b/src/mesh.h @@ -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) { diff --git a/src/utils.h b/src/utils.h index a7427cc..253d8dd 100644 --- a/src/utils.h +++ b/src/utils.h @@ -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;