From b80c2790d0c45a3757e3367ccbc9cdc67d597acb Mon Sep 17 00:00:00 2001 From: XProger Date: Sat, 10 Sep 2016 23:01:45 +0300 Subject: [PATCH] #5 fix portals culling, add camera controller --- bin/OpenLara.exe | Bin 40448 -> 41472 bytes src/camera.h | 333 ++++++++++++++++++++++++++++++----------------- src/controller.h | 31 +++-- src/debug.h | 97 +++++++++++++- src/format.h | 61 ++++++++- src/level.h | 230 +++++++++++++++----------------- src/mesh.h | 19 ++- src/utils.h | 12 ++ 8 files changed, 512 insertions(+), 271 deletions(-) diff --git a/bin/OpenLara.exe b/bin/OpenLara.exe index a6ffd44b4bc40b25657b7c67a2699baf9c17bb90..00167a67247e0e354fa1b83eaa06277f242c6f09 100644 GIT binary patch delta 16222 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 u_= 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;