diff --git a/src/animation.h b/src/animation.h index 53525c0..01480c7 100644 --- a/src/animation.h +++ b/src/animation.h @@ -92,7 +92,7 @@ struct Animation { // real frame index & lerp delta int fIndex = int(time * 30.0f) / anim->frameRate; int k = fIndex * anim->frameRate; - delta = (time * 30.0f - k) / min((int)anim->frameRate, framesCount - k); // min is because in some cases framesCount > realFramesCount / frameRate * frameRate + delta = (time * 30.0f - k) / min((int)anim->frameRate, max(1, framesCount - k)); // min is because in some cases framesCount > realFramesCount / frameRate * frameRate // size of frame (in bytes) int fIndexA = fIndex % fCount, @@ -218,7 +218,9 @@ struct Animation { return lerpAngle(frameA->getAngle(level->version, joint), frameB->getAngle(level->version, joint), delta); } - Basis getJoints(Basis basis, int joint, bool postRot = false, Basis *joints = NULL) { + Basis getJoints(const mat4 &matrix, int joint, bool postRot = false, Basis *joints = NULL) { + mat4 basis = matrix; + ASSERT(model); vec3 offset = isPrepareToNext ? this->offset : vec3(0.0f); basis.translate(((vec3)frameA->pos).lerp(offset + frameB->pos, delta)); @@ -226,7 +228,7 @@ struct Animation { TR::Node *node = (int)model->node < level->nodesDataSize ? (TR::Node*)&level->nodesData[model->node] : NULL; int sIndex = 0; - Basis stack[16]; + mat4 stack[16]; for (int i = 0; i < model->mCount; i++) { @@ -236,7 +238,7 @@ struct Animation { if (t.flags & 0x01) basis = stack[--sIndex]; if (t.flags & 0x02) stack[sIndex++] = basis; - ASSERT(sIndex >= 0 && sIndex < 16); + ASSERT(sIndex >= 0 && sIndex < COUNT(stack)); basis.translate(vec3((float)t.x, (float)t.y, (float)t.z)); } @@ -249,7 +251,8 @@ struct Animation { q = overrides[i]; else q = getJointRot(i); - basis.rotate(q); + + basis.setRot(basis.getRot() * q); if (i == joint && postRot) return basis; @@ -257,6 +260,7 @@ struct Animation { if (joints) joints[i] = basis; } + return basis; } diff --git a/src/cache.h b/src/cache.h index 47c58bf..46be96b 100644 --- a/src/cache.h +++ b/src/cache.h @@ -15,6 +15,7 @@ //#define WATER_USE_GRID #define UNDERWATER_COLOR "#define UNDERWATER_COLOR vec3(0.6, 0.9, 0.9)\n" +#ifndef FFP const char SHADER[] = #include "shaders/shader.glsl" ; @@ -30,6 +31,7 @@ const char FILTER[] = const char GUI[] = #include "shaders/gui.glsl" ; +#endif struct ShaderCache { enum Effect { FX_NONE = 0, FX_UNDERWATER = 1, FX_ALPHA_TEST = 2, FX_CLIP_PLANE = 4 }; @@ -114,6 +116,7 @@ struct ShaderCache { } Shader* compile(Core::Pass pass, Shader::Type type, int fx) { + #ifndef FFP char def[1024], ext[255]; ext[0] = 0; if (Core::settings.detail.shadows) { @@ -142,6 +145,11 @@ struct ShaderCache { src = SHADER; typ = typeNames[type]; sprintf(def, "%s#define PASS_%s\n#define TYPE_%s\n#define MAX_LIGHTS %d\n#define MAX_RANGES %d\n#define MAX_OFFSETS %d\n#define MAX_CONTACTS %d\n#define FOG_DIST (1.0/%d.0)\n#define WATER_FOG_DIST (1.0/%d.0)\n#define SHADOW_TEX_SIZE %d.0\n", ext, passNames[pass], typ, MAX_LIGHTS, MAX_ANIM_TEX_RANGES, MAX_ANIM_TEX_OFFSETS, MAX_CONTACTS, FOG_DIST, WATER_FOG_DIST, SHADOW_TEX_SIZE); + #ifdef MERGE_SPRITES + if (type == Shader::SPRITE) + strcat(def, "#define ALIGN_SPRITES 1\n"); + #endif + if (fx & FX_UNDERWATER) strcat(def, "#define UNDERWATER\n" UNDERWATER_COLOR); if (fx & FX_ALPHA_TEST) strcat(def, "#define ALPHA_TEST\n"); if (pass == Core::passCompose) { @@ -190,10 +198,14 @@ struct ShaderCache { } LOG("shader: compile %s -> %s %s%s%s\n", passNames[pass], typ, (fx & FX_UNDERWATER) ? "underwater " : "", (fx & FX_ALPHA_TEST) ? "alphaTest " : "", (fx & FX_CLIP_PLANE) ? "clipPlane" : ""); return shaders[pass][type][fx] = new Shader(src, def); + #else + return NULL; + #endif } void bind(Core::Pass pass, Shader::Type type, int fx, IGame *game) { Core::pass = pass; + #ifndef FFP Shader *shader = shaders[pass][type][fx]; if (!shader) shader = compile(pass, type, fx); @@ -209,6 +221,7 @@ struct ShaderCache { ASSERT(mesh->animTexOffsetsCount <= MAX_ANIM_TEX_OFFSETS); shader->setParam(uAnimTexRanges, mesh->animTexRanges[0], mesh->animTexRangesCount); shader->setParam(uAnimTexOffsets, mesh->animTexOffsets[0], mesh->animTexOffsetsCount); + #endif } }; @@ -306,7 +319,7 @@ struct AmbientCache { // get result color from 1x1 textures for (int j = 0; j < 6; j++) { Core::setTarget(textures[j * 4 + 3]); - colors[j] = Core::copyPixel(0, 0).xyz; + colors[j] = Core::copyPixel(0, 0).xyz(); } Core::setDepthTest(true); @@ -435,7 +448,7 @@ struct WaterCache { #ifdef BLUR_CAUSTICS caustics_tmp = Core::settings.detail.water > Core::Settings::MEDIUM ? new Texture(512, 512, Texture::RGBA) : NULL; #endif - mask = new Texture(w, h, Texture::RGB16, false, m, false); + mask = new Texture(w, h, Texture::RGB16, Texture::NEAREST, m); delete[] m; blank = false; diff --git a/src/camera.h b/src/camera.h index 57e46cd..7e9b401 100644 --- a/src/camera.h +++ b/src/camera.h @@ -6,7 +6,7 @@ #include "controller.h" #include "character.h" -#define CAMERA_OFFSET (1024.0f + 256.0f) +#define CAMERA_OFFSET (1024.0f + 512.0f) struct Camera : ICamera { @@ -154,7 +154,7 @@ struct Camera : ICamera { if (!firstPerson || viewIndex != -1) return false; - Basis head = owner->animation.getJoints(owner->getMatrix(), 14, true); + Basis head = owner->getJoint(owner->jointHead); Basis eye(quat(0.0f, 0.0f, 0.0f, 1.0f), vec3(0.0f, -40.0f, 10.0f)); eye = head * eye; mViewInv.identity(); @@ -347,16 +347,14 @@ struct Camera : ICamera { Core::mView.translate(vec3(0.0f, sinf(shake * PI * 7) * shake * 48.0f, 0.0f)); if (Core::settings.detail.stereo) - Core::mView.translate(Core::mViewInv.right.xyz * (-Core::eye * (firstPerson ? 8.0f : 32.0f) )); + Core::mView.translate(Core::mViewInv.right().xyz() * (-Core::eye * (firstPerson ? 8.0f : 32.0f) )); Core::mProj = getProjMatrix(); - - // TODO: temporal anti-aliasing - // Core::mProj.e02 = (randf() - 0.5f) * 32.0f / Core::width; - // Core::mProj.e12 = (randf() - 0.5f) * 32.0f / Core::height; } - Core::mViewProj = Core::mProj * Core::mView; - Core::viewPos = Core::mViewInv.offset.xyz; + + Core::setViewProj(Core::mView, Core::mProj); + + Core::viewPos = Core::mViewInv.offset().xyz(); frustum->pos = Core::viewPos; frustum->calcPlanes(Core::mViewProj); @@ -373,6 +371,9 @@ struct Camera : ICamera { fov = firstPerson ? 90.0f : 65.0f; znear = firstPerson ? 8.0f : 32.0f; + #ifdef _PSP + znear = 256.0f; + #endif zfar = 45.0f * 1024.0f; } }; diff --git a/src/character.h b/src/character.h index 14e5c7e..ba22c3a 100644 --- a/src/character.h +++ b/src/character.h @@ -216,7 +216,16 @@ struct Character : Controller { } vec3 getViewPoint() { - return animation.getJoints(getMatrix(), jointChest).pos; + /* + Box box = getBoundingBoxLocal(); + vec3 p = pos; + float delta = (box.max.z + box.min.z) * 0.5f; + p.x += sinf(angle.y) * delta; + p.z += cosf(angle.y) * delta; + p.y += box.max.y + (box.min.y - box.max.y) * 0.75f; + return p; + */ + return getJoint(jointChest).pos; } virtual void lookAt(Controller *target) { diff --git a/src/collision.h b/src/collision.h index 93c6728..de25cb7 100644 --- a/src/collision.h +++ b/src/collision.h @@ -24,10 +24,10 @@ struct Collision { int q = angleQuadrant(angle); const vec2 v[] = { - { -radius, radius }, - { radius, radius }, - { radius, -radius }, - { -radius, -radius }, + vec2( -radius, radius ), + vec2( radius, radius ), + vec2( radius, -radius ), + vec2( -radius, -radius ), }; const vec2 &l = v[q], &r = v[(q + 1) % 4]; diff --git a/src/controller.h b/src/controller.h index 859b58d..a3bcd07 100644 --- a/src/controller.h +++ b/src/controller.h @@ -39,6 +39,7 @@ struct IGame { virtual TR::Level* getLevel() { return NULL; } virtual MeshBuilder* getMesh() { return NULL; } + virtual Texture* getAtlas() { return NULL; } virtual ICamera* getCamera() { return NULL; } virtual Controller* getLara() { return NULL; } virtual bool isCutscene() { return false; } @@ -53,7 +54,7 @@ struct IGame { virtual void renderEnvironment(int roomIndex, const vec3 &pos, Texture **targets, int stride = 0, Core::Pass pass = Core::passAmbient) {} virtual void renderCompose(int roomIndex) {} virtual void renderView(int roomIndex, bool water) {} - virtual void setEffect(Controller *controller, TR::Effect effect) {} + virtual void setEffect(Controller *controller, TR::Effect::Type effect) {} virtual void checkTrigger(Controller *controller, bool heavy) {} @@ -89,7 +90,7 @@ struct Controller { TR::Entity::Flags flags; Basis *joints; - int frameIndex; + int jointsFrame; vec3 ambient[6]; float specular; @@ -129,8 +130,9 @@ struct Controller { fixRoomIndex(); const TR::Model *m = getModel(); - joints = m ? new Basis[m->mCount] : NULL; - frameIndex = -1; + joints = m ? new Basis[m->mCount] : NULL; + jointsFrame = -1; + specular = 0.0f; intensity = e.intensity == -1 ? -1.0f : intensityf(e.intensity); timer = 0.0f; @@ -152,6 +154,7 @@ struct Controller { if (e.isLara() || e.isActor()) // Lara and cutscene entities is active by default activate(); + updated = false; } virtual ~Controller() { @@ -369,7 +372,7 @@ struct Controller { } case TR::FloorData::TRIGGER : { - info.trigger = (TR::Level::Trigger)cmd.sub; + info.trigger = (TR::Level::Trigger::Type)cmd.sub; info.trigCmdCount = 0; info.trigInfo = (*fd++).triggerInfo; TR::FloorData::TriggerCommand trigCmd; @@ -558,7 +561,8 @@ struct Controller { Box box = target->getBoundingBox(); vec3 t = (box.min + box.max) * 0.5f; - Basis b = animation.getJoints(Basis(getMatrix()), joint); + updateJoints(); + Basis b = animation.getJoints(getMatrix(), joint); vec3 delta = (b.inverse() * t).normal(); if (invertAim) delta = -delta; @@ -617,7 +621,7 @@ struct Controller { } virtual vec3 getPos() { - return getEntity().isActor() ? animation.getJoints(getMatrix(), 0).pos : pos; + return getEntity().isActor() ? getJoint(0).pos : pos; } vec3 getDir() const { @@ -679,13 +683,13 @@ struct Controller { const TR::Model *m = getModel(); ASSERT(m->mCount <= MAX_SPHERES); - Basis basis(getMatrix()); - // TODO: optimize (check frame index for animation updates, use joints array) + updateJoints(); + count = 0; for (int i = 0; i < m->mCount; i++) { TR::Mesh &aMesh = level->meshes[level->meshOffsets[m->mStart + i]]; if (aMesh.radius <= 0) continue; - vec3 center = animation.getJoints(basis, i, true) * aMesh.center; + vec3 center = joints[i] * aMesh.center; spheres[count++] = Sphere(center, aMesh.radius); } } @@ -863,14 +867,14 @@ struct Controller { if (cmd == TR::ANIM_CMD_EFFECT) { switch (fx) { case TR::Effect::ROTATE_180 : angle.y = angle.y + PI; break; - case TR::Effect::FLOOR_SHAKE : game->setEffect(this, TR::Effect(fx)); break; + case TR::Effect::FLOOR_SHAKE : game->setEffect(this, TR::Effect::Type(fx)); break; case TR::Effect::FINISH_LEVEL : game->loadNextLevel(); break; case TR::Effect::FLIP_MAP : level->state.flags.flipped = !level->state.flags.flipped; break; default : cmdEffect(fx); break; } } else { if (!(sfx & 0x8000)) { // TODO 0x4000 / 0x8000 for ground / water foot steps - game->playSound(fx, pos, Sound::Flags::PAN); + game->playSound(fx, pos, Sound::PAN); } } } @@ -1028,7 +1032,8 @@ struct Controller { explodeParts = new ExplodePart[model->mCount]; explodeMask = 0; - animation.getJoints(getMatrix(), -1, true, joints); + updateJoints(); + int roomIndex = getRoomIndex(); for (int i = 0; i < model->mCount; i++) { if (!(mask & (1 << i))) @@ -1066,27 +1071,44 @@ struct Controller { mat4 m; m.identity(); - m.dir = vec4(dir * size.z, 0.0f); - m.up = vec4(up, 0.0f); - m.right = vec4(right * size.x, 0.0f); - m.offset = vec4(center.x, info.floor - 8.0f, center.z, 1.0f); + m.dir() = vec4(dir * size.z, 0.0f); + m.up() = vec4(up, 0.0f); + m.right() = vec4(right * size.x, 0.0f); + m.offset() = vec4(center.x, info.floor - 8.0f, center.z, 1.0f); + Core::mModel = m; Basis b; b.identity(); game->setShader(Core::pass, Shader::FLASH, false, false); Core::active.shader->setParam(uViewProj, Core::mViewProj * m); - Core::active.shader->setParam(uBasis, b); + Core::setBasis(&b, 1); + float alpha = lerp(0.7f, 0.90f, clamp((info.floor - boxA.max.y) / 1024.0f, 0.0f, 1.0f) ); - Core::active.shader->setParam(uMaterial, vec4(vec3(0.5f * (1.0f - alpha)), alpha)); + float lum = 0.5f * (1.0f - alpha); + Core::setMaterial(lum, lum, lum, alpha); Core::active.shader->setParam(uAmbient, vec3(0.0f)); Core::setDepthWrite(false); mesh->renderShadowBlob(); Core::setDepthWrite(true); } + bool updated; + void updateJoints() { + //if (updated) return; + if (Core::stats.frame == jointsFrame) + return; + animation.getJoints(getMatrix(), -1, true, joints); + jointsFrame = Core::stats.frame; + updated = true; + } - virtual void render(Frustum *frustum, MeshBuilder *mesh, Shader::Type type, bool caustics) { // TODO: animation.calcJoints + Basis& getJoint(int index) { + updateJoints(); + return joints[index]; + } + + virtual void render(Frustum *frustum, MeshBuilder *mesh, Shader::Type type, bool caustics) { mat4 matrix = getMatrix(); Box box = animation.getBoundingBox(vec3(0, 0, 0), 0); @@ -1098,8 +1120,9 @@ struct Controller { flags.rendered = true; - if (Core::stats.frame != frameIndex) - animation.getJoints(matrix, -1, true, joints); + updateJoints(); + + Core::mModel = getMatrix(); if (layers) { uint32 mask = 0; @@ -1111,7 +1134,7 @@ struct Controller { mask |= layers[i].mask; // set meshes visibility for (int j = 0; j < model->mCount; j++) - joints[j].w = (vmask & (1 << j)) ? 1.0f : -1.0f; // AHAHA + joints[j].w = (vmask & (1 << j)) ? 1.0f : -1.0f; // hide invisible parts if (explodeMask) { ASSERT(explodeParts); @@ -1121,18 +1144,14 @@ struct Controller { joints[i] = explodeParts[i].basis; } - // if (entity.type == TR::Entity::LARA && Core::eye != 0) - // joints[14].w = -1.0f; // render - Core::active.shader->setParam(uBasis, joints[0], model->mCount); - mesh->renderModel(layers[i].model); + Core::setBasis(joints, model->mCount); + mesh->renderModel(layers[i].model, caustics); } } else { - Core::active.shader->setParam(uBasis, joints[0], model->mCount); - mesh->renderModel(getEntity().modelIndex - 1); + Core::setBasis(joints, model->mCount); + mesh->renderModel(getEntity().modelIndex - 1, caustics); } - - frameIndex = Core::stats.frame; } }; diff --git a/src/core.h b/src/core.h index 9b487a9..dfbc724 100644 --- a/src/core.h +++ b/src/core.h @@ -1,7 +1,9 @@ #ifndef H_CORE #define H_CORE -#define USE_INFLATE +#ifndef _PSP + #define USE_INFLATE +#endif #include #ifdef WIN32 @@ -137,12 +139,31 @@ #define glGetProgramBinary(...) #define glProgramBinary(...) +#elif _PSP + #include + #include + + #define FFP + //#define TEX_SWIZZLE + #define EDRAM_MESH + //#define EDRAM_TEX #endif #ifdef USE_INFLATE #include "libs/tinf/tinf.h" #endif +#ifdef FFP + #define SPLIT_BY_TILE + #ifdef _PSP + #define SPLIT_BY_CLUT + #endif +#else + #define MERGE_MODELS + #define MERGE_SPRITES + #define GENERATE_WATER_PLANE +#endif + #include "utils.h" enum ControlKey { cLeft, cRight, cUp, cDown, cJump, cWalk, cAction, cWeapon, cLook, cStepLeft, cStepRight, cRoll, cInventory, cMAX }; @@ -164,6 +185,9 @@ enum InputKey { ikNone, struct KeySet { InputKey key, joy; + + KeySet() {} + KeySet(InputKey key, InputKey joy) : key(key), joy(joy) {} }; namespace Core { @@ -171,7 +195,7 @@ namespace Core { int lastTime; int width, height; - struct { + struct Support { int maxVectors; int maxAniso; bool shaderBinary; @@ -191,7 +215,7 @@ namespace Core { } support; struct Settings { - enum Quality : uint8 { LOW, MEDIUM, HIGH }; + enum Quality { LOW, MEDIUM, HIGH }; struct { union { @@ -363,7 +387,7 @@ namespace Core { struct Shader; struct Texture; -enum RenderState : int32 { +enum RenderState { RS_TARGET = 1 << 0, RS_VIEWPORT = 1 << 1, RS_DEPTH_TEST = 1 << 2, @@ -383,7 +407,7 @@ enum RenderState : int32 { RS_BLEND = RS_BLEND_ADD | RS_BLEND_ALPHA | RS_BLEND_MULT | RS_BLEND_PREMULT, }; -typedef unsigned short Index; +typedef uint16 Index; struct Vertex { short4 coord; // xyz - position, w - joint index (for entities only) @@ -394,6 +418,31 @@ struct Vertex { ubyte4 light; // xyz - color, w - use premultiplied alpha }; +#ifdef FFP + #ifdef _PSP + struct VertexGPU { + short2 texCoord; + ubyte4 color; + short3 normal; + short3 coord; + }; + #else +/* + struct VertexGPU { + short2 texCoord; + ubyte4 color; + short3 normal; + uint16 _alignN; + short3 coord; + uint16 _alignC; + }; +*/ + typedef Vertex VertexGPU; + #endif +#else + typedef Vertex VertexGPU; +#endif + #ifdef PROFILE //#define USE_CV_MARKERS @@ -470,7 +519,7 @@ enum BlendMode { bmNone, bmAlpha, bmAdd, bmMult, bmPremult }; namespace Core { float eye; vec4 viewport, viewportDef; - mat4 mView, mProj, mViewProj, mViewInv, mLightProj; + mat4 mModel, mView, mProj, mViewProj, mViewInv, mLightProj; Basis basis; vec3 viewPos; vec4 lightPos[MAX_LIGHTS]; @@ -482,33 +531,46 @@ namespace Core { enum Pass { passCompose, passShadow, passAmbient, passWater, passFilter, passGUI, passMAX } pass; - GLuint FBO, defaultFBO; + #ifdef _PSP + void *curBackBuffer; + #else + GLuint FBO, defaultFBO; + struct RenderTargetCache { + int count; + struct Item { + GLuint ID; + int width; + int height; + } items[MAX_RENDER_BUFFERS]; + } rtCache[2]; + #endif + Texture *defaultTarget; + + int32 renderState; - struct RenderTargetCache { - int count; - struct Item { - GLuint ID; - int width; - int height; - } items[MAX_RENDER_BUFFERS]; - } rtCache[2]; - - int32 renderState; - - struct { + struct Active { Shader *shader; Texture *textures[8]; Texture *target; - int targetFace; vec4 viewport; + vec4 material; + uint32 targetFace; + #ifdef _PSP + Index *iBuffer; + VertexGPU *vBuffer; + #else GLuint VAO; GLuint iBuffer; GLuint vBuffer; + #endif int32 renderState; + + int32 basisCount; + Basis *basis; } active; - struct { + struct ReqTarget { Texture *texture; bool clear; uint8 face; @@ -539,6 +601,45 @@ namespace Core { frame++; } } stats; + +#ifdef _PSP + uint32 *cmdBuf = NULL; + + static int EDRAM_OFFSET; + static int EDRAM_SIZE; + + void* allocEDRAM(int size) { + LOG("EDRAM ALLOC: offset: %d size %d\n", Core::EDRAM_OFFSET, size); + if (Core::EDRAM_OFFSET + size > EDRAM_SIZE) + LOG("! EDRAM overflow !\n"); + + void *ptr = ((char*)sceGeEdramGetAddr()) + EDRAM_OFFSET; + EDRAM_OFFSET += (size + 15) / 16 * 16; + return ptr; + } + + void freeEDRAM() { + EDRAM_OFFSET = (512 * 272 * 2 * 2) + (512 * 272 * 2); + LOG("EDRAM FREE: offset: %d\n", EDRAM_OFFSET); + } +#endif + + void beginCmdBuf() { + #ifdef _PSP + if (!cmdBuf) + cmdBuf = new uint32[262144]; + + sceGuStart(GU_DIRECT, cmdBuf); + #endif + } + + void submitCmdBuf() { + #ifdef _PSP + ASSERT(cmdBuf); + sceGuFinish(); + sceGuSync(GU_SYNC_WAIT, GU_SYNC_FINISH); + #endif + } } #include "texture.h" @@ -632,6 +733,16 @@ namespace Core { GetProcOGL(glProgramBinary); #endif + const char *vendor, *renderer, *version; + + #ifdef _PSP + vendor = "Sony"; + renderer = "SCE GU"; + version = "1.0"; + #else + vendor = (char*)glGetString(GL_VENDOR); + renderer = (char*)glGetString(GL_RENDERER); + version = (char*)glGetString(GL_VERSION); char *ext = (char*)glGetString(GL_EXTENSIONS); /* @@ -648,8 +759,27 @@ namespace Core { } } */ - glGetIntegerv(GL_MAX_VARYING_VECTORS, &support.maxVectors); + #endif + #ifdef FFP + support.maxAniso = 1; + support.maxVectors = 0; + support.shaderBinary = false; + support.VAO = false; + support.depthTexture = false; + support.shadowSampler = false; + support.discardFrame = false; + support.texNPOT = false; + support.texRG = false; + support.texBorder = false; + support.maxAniso = false; + support.colorFloat = false; + support.colorHalf = false; + support.texFloatLinear = false; + support.texFloat = false; + support.texHalfLinear = false; + support.texHalf = false; + #else support.shaderBinary = extSupport(ext, "_program_binary"); support.VAO = extSupport(ext, "_vertex_array_object"); support.depthTexture = extSupport(ext, "_depth_texture"); @@ -668,16 +798,22 @@ namespace Core { if (support.maxAniso) glGetIntegerv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &support.maxAniso); + glGetIntegerv(GL_MAX_VARYING_VECTORS, &support.maxVectors); + #endif #ifdef PROFILE support.profMarker = extSupport(ext, "_KHR_debug"); support.profTiming = extSupport(ext, "_timer_query"); #endif - char *vendor = (char*)glGetString(GL_VENDOR); + LOG("Vendor : %s\n", vendor); - LOG("Renderer : %s\n", glGetString(GL_RENDERER)); - LOG("Version : %s\n", glGetString(GL_VERSION)); + LOG("Renderer : %s\n", renderer); + LOG("Version : %s\n", version); LOG("cache : %s\n", Stream::cacheDir); + #ifdef _PSP + EDRAM_SIZE = sceGeEdramGetSize(); + LOG("VRAM : %d\n", EDRAM_SIZE); + #endif LOG("supports :\n"); LOG(" variyngs count : %d\n", support.maxVectors); LOG(" binary shaders : %s\n", support.shaderBinary ? "true" : "false"); @@ -694,13 +830,89 @@ namespace Core { support.colorHalf ? "full" : (support.texHalf ? (support.texHalfLinear ? "linear" : "nearest") : "false")); LOG("\n"); + #ifdef FFP + #ifdef _PSP + Core::width = 480; + Core::height = 272; + + sceGuDepthFunc(GU_LEQUAL); + sceGuDepthRange(0x0000, 0xFFFF); + sceGuClearDepth(0xFFFF); + + sceGuShadeModel(GU_SMOOTH); + sceGuAlphaFunc(GU_GREATER, 127, 255); + sceGuEnable(GU_ALPHA_TEST); + + int swizzle = GU_FALSE; + #ifdef TEX_SWIZZLE + swizzle = GU_TRUE; + #endif + + sceGuClutMode(GU_PSM_5551, 0, 0xFF, 0); + sceGuTexMode(GU_PSM_T4, 0, 0, swizzle); + sceGuTexFunc(GU_TFX_MODULATE, GU_TCC_RGBA); + sceGuTexScale(1.0f, 1.0f); + sceGuTexOffset(0.0f, 0.0f); + sceGuTexFilter(GU_LINEAR, GU_LINEAR); + //sceGuTexFilter(GU_NEAREST, GU_NEAREST); + sceGuEnable(GU_CLIP_PLANES); + + const ScePspIMatrix4 dith = + { {-4, 0, -3, 1}, + { 2, -2, 3, -1}, + {-3, 1, -4, 0}, + { 3, -1, 2, -2} }; + sceGuSetDither(&dith); + sceGuEnable(GU_DITHER); + + sceGuEnable(GU_LIGHT0); + sceGuDisable(GU_LIGHT1); + sceGuDisable(GU_LIGHT2); + sceGuDisable(GU_LIGHT3); + sceGuAmbientColor(0xFFFFFFFF); + sceGuColor(0xFFFFFFFF); + + freeEDRAM(); + #else + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + glEnableClientState(GL_NORMAL_ARRAY); + glEnableClientState(GL_VERTEX_ARRAY); + + glClearColor(0.5, 0.5, 0.5, 1); + glAlphaFunc(GL_GREATER, 0.5f); + glEnable(GL_ALPHA_TEST); + + glEnable(GL_LIGHT0); + glDisable(GL_LIGHT1); + glDisable(GL_LIGHT2); + glDisable(GL_LIGHT3); + glEnable(GL_NORMALIZE); + + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + glScalef(1.0f / 32767.0f, 1.0f / 32767.0f, 1.0f / 32767.0f); + #endif + #endif + + #ifndef _PSP glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&defaultFBO); glGenFramebuffers(1, &FBO); - - memset(rtCache, 0, sizeof(rtCache)); - defaultTarget = NULL; glDepthFunc(GL_LEQUAL); + memset(rtCache, 0, sizeof(rtCache)); + #endif + + #if defined(FFP) && defined(SPLIT_BY_TILE) + #ifdef _PSP + sceGuEnable(GU_TEXTURE_2D); + #else + glEnable(GL_TEXTURE_2D); + #endif + #endif + + defaultTarget = NULL; + Sound::init(); for (int i = 0; i < MAX_LIGHTS; i++) { @@ -710,9 +922,9 @@ namespace Core { eye = 0.0f; uint32 data = 0x00000000; - blackTex = new Texture(1, 1, Texture::RGBA, false, &data, false); + blackTex = new Texture(1, 1, Texture::RGBA, Texture::NEAREST, &data); data = 0xFFFFFFFF; - whiteTex = new Texture(1, 1, Texture::RGBA, false, &data, false); + whiteTex = new Texture(1, 1, Texture::RGBA, Texture::NEAREST, &data); // init settings settings.detail.setFilter (Core::Settings::HIGH); @@ -729,33 +941,45 @@ namespace Core { settings.controls.multitarget = true; settings.controls.vibration = true; - settings.controls.keys[ cLeft ] = { ikLeft, ikJoyLeft }; - settings.controls.keys[ cRight ] = { ikRight, ikJoyRight }; - settings.controls.keys[ cUp ] = { ikUp, ikJoyUp }; - settings.controls.keys[ cDown ] = { ikDown, ikJoyDown }; + settings.controls.keys[ cLeft ] = KeySet( ikLeft, ikJoyLeft ); + settings.controls.keys[ cRight ] = KeySet( ikRight, ikJoyRight ); + settings.controls.keys[ cUp ] = KeySet( ikUp, ikJoyUp ); + settings.controls.keys[ cDown ] = KeySet( ikDown, ikJoyDown ); #ifdef __EMSCRIPTEN__ - settings.controls.keys[ cJump ] = { ikD, ikJoyX }; + settings.controls.keys[ cJump ] = KeySet( ikD, ikJoyX ); #else - settings.controls.keys[ cJump ] = { ikAlt, ikJoyX }; + settings.controls.keys[ cJump ] = KeySet( ikAlt, ikJoyX ); #endif - settings.controls.keys[ cWalk ] = { ikShift, ikJoyRB }; - settings.controls.keys[ cAction ] = { ikCtrl, ikJoyA }; - settings.controls.keys[ cWeapon ] = { ikSpace, ikJoyY }; - settings.controls.keys[ cLook ] = { ikC, ikJoyLB }; - settings.controls.keys[ cStepLeft ] = { ikZ, ikJoyLT }; - settings.controls.keys[ cStepRight ] = { ikX, ikJoyRT }; - settings.controls.keys[ cRoll ] = { ikA, ikJoyB }; - settings.controls.keys[ cInventory ] = { ikTab, ikJoySelect }; + settings.controls.keys[ cWalk ] = KeySet( ikShift, ikJoyRB ); + settings.controls.keys[ cAction ] = KeySet( ikCtrl, ikJoyA ); + settings.controls.keys[ cWeapon ] = KeySet( ikSpace, ikJoyY ); + settings.controls.keys[ cLook ] = KeySet( ikC, ikJoyLB ); + settings.controls.keys[ cStepLeft ] = KeySet( ikZ, ikJoyLT ); + settings.controls.keys[ cStepRight ] = KeySet( ikX, ikJoyRT ); + settings.controls.keys[ cRoll ] = KeySet( ikA, ikJoyB ); + settings.controls.keys[ cInventory ] = KeySet( ikTab, ikJoySelect ); -#ifdef __RPI__ + #ifdef __RPI__ settings.detail.setShadows(Core::Settings::LOW); -#endif + #endif + + #ifdef FFP + settings.detail.setFilter (Core::Settings::MEDIUM); + settings.detail.setLighting (Core::Settings::LOW); + settings.detail.setShadows (Core::Settings::LOW); + settings.detail.setWater (Core::Settings::LOW); + settings.audio.reverb = false; + #endif + resetTime(); } void deinit() { delete blackTex; delete whiteTex; + #ifdef _PSP + delete[] cmdBuf; + #else /* glBindFramebuffer(GL_FRAMEBUFFER, 0); glDeleteFrameBuffers(1, &FBO); @@ -765,9 +989,11 @@ namespace Core { for (int i = 0; i < rtCache[b].count; i++) glDeleteRenderBuffers(1, &rtCache[b].items[i].ID); */ + #endif Sound::deinit(); } +#ifndef _PSP int cacheRenderTarget(bool depth, int width, int height) { RenderTargetCache &cache = rtCache[depth]; @@ -778,17 +1004,16 @@ namespace Core { ASSERT(cache.count < MAX_RENDER_BUFFERS); RenderTargetCache::Item &item = cache.items[cache.count]; - - glGenRenderbuffers(1, &item.ID); item.width = width; item.height = height; + glGenRenderbuffers(1, &item.ID); glBindRenderbuffer(GL_RENDERBUFFER, item.ID); glRenderbufferStorage(GL_RENDERBUFFER, depth ? GL_RGB565 : GL_DEPTH_COMPONENT16, width, height); glBindRenderbuffer(GL_RENDERBUFFER, 0); - return cache.count++; } +#endif bool update() { resetState = false; @@ -809,12 +1034,19 @@ namespace Core { uint8 face = reqTarget.face; if (target != active.target || face != active.targetFace) { - + #ifdef _PSP +/* + if (!target) + sceGuDrawBufferList(GU_PSM_5650, curBackBuffer, 512); + else + sceGuDrawBufferList(GU_PSM_5650, target->offset, target->width); +*/ + #else if (!target) { // may be a null glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO); } else { GLenum texTarget = GL_TEXTURE_2D; - if (target->cube) + if (target->opt & Texture::CUBEMAP) texTarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + face; bool depth = target->format == Texture::DEPTH || target->format == Texture::SHADOW; @@ -824,6 +1056,7 @@ namespace Core { glFramebufferTexture2D (GL_FRAMEBUFFER, depth ? GL_DEPTH_ATTACHMENT : GL_COLOR_ATTACHMENT0, texTarget, target->ID, 0); glFramebufferRenderbuffer (GL_FRAMEBUFFER, depth ? GL_COLOR_ATTACHMENT0 : GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rtCache[depth].items[rtIndex].ID); } + #endif active.target = target; active.targetFace = face; @@ -833,30 +1066,62 @@ namespace Core { if (mask & RS_VIEWPORT) { if (viewport != active.viewport) { active.viewport = viewport; + #ifdef _PSP + sceGuOffset(2048 - int(viewport.z) / 2, 2048 - int(viewport.w) / 2); + sceGuViewport(2048 + int(viewport.x), 2048 + int(viewport.y), int(viewport.z), int(viewport.w)); + #else glViewport(int(viewport.x), int(viewport.y), int(viewport.z), int(viewport.w)); + #endif } renderState &= ~RS_VIEWPORT; } if (mask & RS_DEPTH_TEST) { + #ifdef _PSP + if (renderState & RS_DEPTH_TEST) + sceGuEnable(GU_DEPTH_TEST); + else + sceGuDisable(GU_DEPTH_TEST); + #else if (renderState & RS_DEPTH_TEST) glEnable(GL_DEPTH_TEST); else glDisable(GL_DEPTH_TEST); + #endif } if (mask & RS_DEPTH_WRITE) { - glDepthMask((renderState & RS_DEPTH_WRITE) != 0); + #ifdef _PSP + sceGuDepthMask((renderState & RS_DEPTH_WRITE) != 0 ? GU_FALSE : GU_TRUE); + #else + glDepthMask((renderState & RS_DEPTH_WRITE) != 0 ? GL_TRUE : GL_FALSE); + #endif } if (mask & RS_COLOR_WRITE) { + #ifdef _PSP + sceGuPixelMask(~(((renderState & RS_COLOR_WRITE_R) != 0 ? 0x000000FF : 0) | + ((renderState & RS_COLOR_WRITE_G) != 0 ? 0x0000FF00 : 0) | + ((renderState & RS_COLOR_WRITE_B) != 0 ? 0x00FF0000 : 0) | + ((renderState & RS_COLOR_WRITE_A) != 0 ? 0xFF000000 : 0))); + #else glColorMask((renderState & RS_COLOR_WRITE_R) != 0, (renderState & RS_COLOR_WRITE_G) != 0, (renderState & RS_COLOR_WRITE_B) != 0, (renderState & RS_COLOR_WRITE_A) != 0); + #endif } if (mask & RS_CULL) { + #ifdef _PSP + if (!(active.renderState & RS_CULL)) + sceGuEnable(GU_CULL_FACE); + switch (renderState & RS_CULL) { + case RS_CULL_BACK : sceGuFrontFace(GU_CCW); break; + case RS_CULL_FRONT : sceGuFrontFace(GU_CW); break; + default : sceGuDisable(GU_CULL_FACE); + } + #else if (!(active.renderState & RS_CULL)) glEnable(GL_CULL_FACE); switch (renderState & RS_CULL) { @@ -864,9 +1129,21 @@ namespace Core { case RS_CULL_FRONT : glCullFace(GL_FRONT); break; default : glDisable(GL_CULL_FACE); } + #endif } if (mask & RS_BLEND) { + #ifdef _PSP + if (!(active.renderState & RS_BLEND)) + sceGuEnable(GU_BLEND); + switch (renderState & RS_BLEND) { + case RS_BLEND_ALPHA : sceGuBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0); break; + case RS_BLEND_ADD : sceGuBlendFunc(GU_ADD, GU_FIX, GU_FIX, 0xffffffff, 0xffffffff); break; + case RS_BLEND_MULT : sceGuBlendFunc(GU_ADD, GU_DST_COLOR, GU_FIX, 0, 0); break; + case RS_BLEND_PREMULT : sceGuBlendFunc(GU_ADD, GU_FIX, GU_ONE_MINUS_SRC_ALPHA, 0xffffffff, 0); break; + default : sceGuDisable(GU_BLEND); + } + #else if (!(active.renderState & RS_BLEND)) glEnable(GL_BLEND); switch (renderState & RS_BLEND) { @@ -876,11 +1153,17 @@ namespace Core { case RS_BLEND_PREMULT : glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); break; default : glDisable(GL_BLEND); } + #endif } - if (mask & RS_TARGET) { // for cler the RT & reset mask - if (reqTarget.clear) + if (mask & RS_TARGET) { + if (reqTarget.clear) { + #ifdef _PSP + sceGuClear(GU_COLOR_BUFFER_BIT | GU_DEPTH_BUFFER_BIT | GU_FAST_CLEAR_BIT); + #else glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + #endif + } renderState &= ~RS_TARGET; } @@ -888,7 +1171,15 @@ namespace Core { } void setClearColor(const vec4 &color) { + #ifdef _PSP + ubyte4 c(clamp(int(color.x * 255), 0, 255), + clamp(int(color.y * 255), 0, 255), + clamp(int(color.z * 255), 0, 255), + clamp(int(color.w * 255), 0, 255)); + sceGuClearColor(*((uint32*)&c)); + #else glClearColor(color.x, color.y, color.z, color.w); + #endif } void setViewport(int x, int y, int width, int height) { @@ -968,17 +1259,38 @@ namespace Core { renderState |= RS_TARGET; } + void setBasis(Basis *basis, int count) { + Core::active.basis = basis; + Core::active.basisCount = count; + + Core::active.shader->setParam(uBasis, basis[0], count); + } + + void setMaterial(float diffuse, float ambient, float specular, float alpha) { + Core::active.material = vec4(diffuse, ambient, specular, alpha); + + Core::active.shader->setParam(uMaterial, Core::active.material); + } + void copyTarget(Texture *dst, int xOffset, int yOffset, int x, int y, int width, int height) { validateRenderState(); dst->bind(sDiffuse); + #ifdef _PSP + + #else glCopyTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, x, y, width, height); // TODO: too bad for iOS devices! + #endif } vec4 copyPixel(int x, int y) { // GPU sync! validateRenderState(); + #ifdef _PSP + return vec4(0.0f); + #else ubyte4 c; glReadPixels(x, y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &c); return vec4(float(c.x), float(c.y), float(c.z), float(c.w)) * (1.0f / 255.0f); + #endif } void beginFrame() { @@ -1004,9 +1316,45 @@ namespace Core { Core::stats.stop(); } + void setViewProj(const mat4 &mView, const mat4 &mProj) { + Core::mProj = mProj; + Core::mView = mView; + Core::mViewProj = mProj * mView; + #ifdef FFP + #ifdef _PSP + sceGumMatrixMode(GU_PROJECTION); + sceGumLoadMatrix((ScePspFMatrix4*)&mProj); + sceGumMatrixMode(GU_VIEW); + sceGumLoadMatrix((ScePspFMatrix4*)&mView); + #else + glMatrixMode(GL_PROJECTION); + glLoadMatrixf((float*)&mProj); + #endif + #endif + } + void DIP(int iStart, int iCount) { validateRenderState(); + + #ifdef FFP + #ifdef _PSP + mat4 m = mModel; + m.scale(vec3(32767.0f)); + sceGumMatrixMode(GU_MODEL); + sceGumLoadMatrix((ScePspFMatrix4*)&m); + #else + mat4 m = mView * mModel; + glMatrixMode(GL_MODELVIEW); + glLoadMatrixf((GLfloat*)&m); + #endif + #endif + + #ifdef _PSP + sceGumDrawArray(GU_TRIANGLES, GU_TEXTURE_16BIT | GU_COLOR_8888 | GU_NORMAL_16BIT | GU_VERTEX_16BIT | GU_INDEX_16BIT | GU_TRANSFORM_3D, iCount, active.iBuffer + iStart, active.vBuffer); + #else glDrawElements(GL_TRIANGLES, iCount, GL_UNSIGNED_SHORT, (Index*)NULL + iStart); + #endif + stats.dips++; stats.tris += iCount / 3; } diff --git a/src/debug.h b/src/debug.h index 3125942..482659c 100644 --- a/src/debug.h +++ b/src/debug.h @@ -48,6 +48,7 @@ namespace Debug { } void begin() { + glDisable(GL_TEXTURE_2D); glMatrixMode(GL_PROJECTION); glLoadMatrixf((GLfloat*)&Core::mProj); glMatrixMode(GL_MODELVIEW); @@ -64,7 +65,7 @@ namespace Debug { } void end() { - // + glEnable(GL_TEXTURE_2D); } namespace Draw { @@ -198,9 +199,9 @@ namespace Debug { void text(const vec3 &pos, const vec4 &color, const char *str) { vec4 p = Core::mViewProj * vec4(pos, 1); if (p.w > 0) { - p.xyz = p.xyz * (1.0f / p.w); + p.xyz() = p.xyz() * (1.0f / p.w); p.y = -p.y; - p.xyz = (p.xyz * 0.5f + vec3(0.5f)) * vec3(float(Core::width), float(Core::height), 1.0f); + p.xyz() = (p.xyz() * 0.5f + vec3(0.5f)) * vec3(float(Core::width), float(Core::height), 1.0f); text(vec2(p.x, p.y), color, str); } } @@ -210,7 +211,7 @@ namespace Debug { #define case_name(a,b) case a::b : return #b - const char *getTriggerType(const TR::Level &level, const TR::Level::Trigger &trigger) { + const char *getTriggerType(const TR::Level &level, const TR::Level::Trigger::Type &trigger) { switch (trigger) { case_name(TR::Level::Trigger, ACTIVATE ); case_name(TR::Level::Trigger, PAD ); @@ -225,7 +226,7 @@ namespace Debug { return "UNKNOWN"; } - const char *getTriggerAction(const TR::Level &level, const TR::Action &action) { + const char *getTriggerAction(const TR::Level &level, uint16 action) { switch (action) { case_name(TR::Action, ACTIVATE ); case_name(TR::Action, CAMERA_SWITCH ); @@ -269,14 +270,30 @@ namespace Debug { vec3 rf[4], rc[4], f[4], c[4]; - float offsets[4][2] = { { 1, 1 }, { 1023, 1 }, { 1023, 1023 }, { 1, 1023 } }; + int offsets[4][2] = { { 1, 1 }, { 1023, 1 }, { 1023, 1023 }, { 1, 1023 } }; for (int i = 0; i < 4; i++) { game->getLara()->getFloorInfo(roomIndex, vec3(float(x + offsets[i][0]), float(y), float(z + offsets[i][1])), info); - rf[i] = vec3( x + offsets[i][0], info.roomFloor - 4, z + offsets[i][1] ); - rc[i] = vec3( x + offsets[i][0], info.roomCeiling + 4, z + offsets[i][1] ); - f[i] = vec3( x + offsets[i][0], info.floor - 4, z + offsets[i][1] ); - c[i] = vec3( x + offsets[i][0], info.ceiling + 4, z + offsets[i][1] ); + rf[i] = vec3( float(x + offsets[i][0]), info.roomFloor - 4, float(z + offsets[i][1]) ); + rc[i] = vec3( float(x + offsets[i][0]), info.roomCeiling + 4, float(z + offsets[i][1]) ); + f[i] = vec3( float(x + offsets[i][0]), info.floor - 4, float(z + offsets[i][1]) ); + c[i] = vec3( float(x + offsets[i][0]), info.ceiling + 4, float(z + offsets[i][1]) ); + + /* + int px = x + offsets[i][0]; + int py = y; + int pz = z + offsets[i][1]; + + int dx, dz; + + int16 ridx = roomIndex; + TR::Room::Sector *sector = game->getLevel()->getSectorNext(ridx, px, py, pz); + int floor = game->getLevel()->getFloor(sector, px, py, pz); + int ceiling = game->getLevel()->getCeiling(sector, px, py, pz); + + f[i] = vec3( px, floor - 4, pz ); + c[i] = vec3( px, ceiling + 4, pz ); + */ if (info.roomBelow == TR::NO_ROOM) rf[i].y = f[i].y; if (info.roomAbove == TR::NO_ROOM) rc[i].y = c[i].y; } @@ -531,7 +548,7 @@ namespace Debug { Debug::Draw::sphere(p, float(l.radius), color); } - vec4 color = vec4(lara->mainLightColor.xyz, 1.0f); + vec4 color = vec4(lara->mainLightColor.xyz(), 1.0f); Debug::Draw::point(lara->mainLightPos, color); Debug::Draw::sphere(lara->mainLightPos, lara->mainLightColor.w, color); } diff --git a/src/enemy.h b/src/enemy.h index ac7192c..bb8f623 100644 --- a/src/enemy.h +++ b/src/enemy.h @@ -590,7 +590,7 @@ struct Wolf : Enemy { case STATE_ATTACK : case STATE_BITE : if (nextState == STATE_NONE && targetInView && (collide(target) & HIT_MASK)) { - bite(animation.getJoints(getMatrix(), jointHead, true).pos, state == STATE_ATTACK ? 50.0f : 100.0f); + bite(getJoint(jointHead).pos, state == STATE_ATTACK ? 50.0f : 100.0f); nextState = state == STATE_ATTACK ? STATE_RUN : STATE_GROWL; } return state == STATE_ATTACK ? STATE_RUN : state; @@ -751,7 +751,7 @@ struct Bear : Enemy { case STATE_BITE : case STATE_ATTACK : if (nextState == STATE_NONE && (collide(target) & HIT_MASK)) { - bite(animation.getJoints(getMatrix(), jointHead, true).pos, state == STATE_BITE ? 200.0f : 400.0f); + bite(getJoint(jointHead).pos, state == STATE_BITE ? 200.0f : 400.0f); nextState = state == STATE_BITE ? STATE_STOP : STATE_HOWL; } break; @@ -832,7 +832,7 @@ struct Bat : Enemy { mood = MOOD_SLEEP; return STATE_FLY; } else - bite(animation.getJoints(getMatrix(), jointHead, true).pos, 2); + bite(getJoint(jointHead).pos, 2); break; case STATE_FLY : if (collide(target)) { @@ -1071,7 +1071,7 @@ struct Raptor : Enemy { case STATE_ATTACK_2 : case STATE_BITE : if (nextState == STATE_NONE && targetInView && (mask & HIT_MASK)) { - bite(animation.getJoints(getMatrix(), jointHead, true).pos, 100); + bite(getJoint(jointHead).pos, 100); nextState = state == STATE_ATTACK_2 ? STATE_RUN : STATE_STOP; } break; diff --git a/src/format.h b/src/format.h index ed11bd6..623cfed 100644 --- a/src/format.h +++ b/src/format.h @@ -851,6 +851,8 @@ namespace TR { + struct Level; + enum { NO_FLOOR = -127, NO_ROOM = 0xFF, @@ -868,36 +870,38 @@ namespace TR { ANIM_CMD_EFFECT , }; - enum Effect : int32 { - NONE = -1, - ROTATE_180 , - FLOOR_SHAKE , - LARA_NORMAL , - LARA_BUBBLES , - FINISH_LEVEL , - EARTHQUAKE , - FLOOD , - UNK1 , - STAIRS2SLOPE , - UNK3 , - UNK4 , - EXPLOSION , - LARA_HANDSFREE , - FLIP_MAP , - DRAW_RIGHTGUN , - DRAW_LEFTGUN , - SHOT_RIGHTGUN , - SHOT_LEFTGUN , - FLICKER = 16, - UNKNOWN , - MESH_SWAP_1 , - MESH_SWAP_2 , - MESH_SWAP_3 , - INV_ON , - INV_OFF , - DYN_ON , - DYN_OFF , - FOOTPRINT = 32, + struct Effect { + enum Type { + NONE = -1, + ROTATE_180 , + FLOOR_SHAKE , + LARA_NORMAL , + LARA_BUBBLES , + FINISH_LEVEL , + EARTHQUAKE , + FLOOD , + UNK1 , + STAIRS2SLOPE , + UNK3 , + UNK4 , + EXPLOSION , + LARA_HANDSFREE , + FLIP_MAP , + DRAW_RIGHTGUN , + DRAW_LEFTGUN , + SHOT_RIGHTGUN , + SHOT_LEFTGUN , + FLICKER = 16, + UNKNOWN , + MESH_SWAP_1 , + MESH_SWAP_2 , + MESH_SWAP_3 , + INV_ON , + INV_OFF , + DYN_ON , + DYN_OFF , + FOOTPRINT = 32, + }; }; enum { @@ -989,18 +993,20 @@ namespace TR { HIT_MIDAS, }; - enum Action : uint16 { - ACTIVATE , // activate item - CAMERA_SWITCH , // switch to camera - FLOW , // underwater flow - FLIP , // flip map - FLIP_ON , // flip on - FLIP_OFF , // flip off - CAMERA_TARGET , // look at item - END , // end level - SOUNDTRACK , // play soundtrack - EFFECT , // special effect trigger - SECRET , // secret found + struct Action { + enum Type { + ACTIVATE , // activate item + CAMERA_SWITCH , // switch to camera + FLOW , // underwater flow + FLIP , // flip map + FLIP_ON , // flip on + FLIP_OFF , // flip off + CAMERA_TARGET , // look at item + END , // end level + SOUNDTRACK , // play soundtrack + EFFECT , // special effect trigger + SECRET , // secret found + }; }; namespace Limits { @@ -1013,43 +1019,43 @@ namespace TR { }; Limit SWITCH = { - 0, 376, 30, {{-200, 0, 312}, {200, 0, 512}}, true, false + 0, 376, 30, ::Box(vec3(-200, 0, 312), vec3(200, 0, 512)), true, false }; Limit SWITCH_UNDERWATER = { - 0, 100, 80, {{-1024, -1024, -1024}, {1024, 1024, 512}}, true, true + 0, 100, 80, ::Box(vec3(-1024, -1024, -1024), vec3(1024, 1024, 512)), true, true }; Limit PICKUP = { - 0, -100, 180, {{-256, -100, -256}, {256, 100, 100}}, false, true + 0, -100, 180, ::Box(vec3(-256, -100, -256), vec3(256, 100, 100)), false, true }; Limit PICKUP_UNDERWATER = { - -200, -350, 45, {{-512, -512, -512}, {512, 512, 512}}, false, true + -200, -350, 45, ::Box(vec3(-512, -512, -512), vec3(512, 512, 512)), false, true }; Limit KEY_HOLE = { - 0, 362, 30, {{-200, 0, 312}, {200, 0, 512}}, true, true + 0, 362, 30, ::Box(vec3(-200, 0, 312), vec3(200, 0, 512)), true, true }; Limit PUZZLE_HOLE = { - 0, 327, 30, {{-200, 0, 312}, {200, 0, 512}}, true, true + 0, 327, 30, ::Box(vec3(-200, 0, 312), vec3(200, 0, 512)), true, true }; Limit BLOCK = { - 0, -612, 30, {{-300, 0, -692}, {300, 0, -512}}, true, false + 0, -612, 30, ::Box(vec3(-300, 0, -692), vec3(300, 0, -512)), true, false }; Limit MIDAS = { - 512, -612, 30, {{-700, 284, -700}, {700, 996, 700}}, true, false + 512, -612, 30, ::Box(vec3(-700, 284, -700), vec3(700, 996, 700)), true, false }; Limit SCION = { - 640, -202, 30, {{-256, 540, -350}, {256, 740, -200}}, false, false + 640, -202, 30, ::Box(vec3(-256, 540, -350), vec3(256, 740, -200)), false, false }; Limit SCION_HOLDER = { - 640, -202, 10, {{-256, 206, -862}, {256, 306, -200}}, true, false + 640, -202, 10, ::Box(vec3(-256, 206, -862), vec3(256, 306, -200)), true, false }; } @@ -1102,26 +1108,80 @@ namespace TR { operator short3() const { return *((short3*)this); } }; - struct Rectangle { + struct Tile { + uint16 index:14, undefined:1, triangle:1; + }; + + struct ObjectTexture { + uint16 clut; + Tile tile; // tile or palette index + uint16 attribute:15, repeat:1; // 0 - opaque, 1 - transparent, 2 - blend additive, + short2 texCoord[4]; + + short4 getMinMax() const { + return short4( + min(min(texCoord[0].x, texCoord[1].x), texCoord[2].x), + min(min(texCoord[0].y, texCoord[1].y), texCoord[2].y), + max(max(texCoord[0].x, texCoord[1].x), texCoord[2].x), + max(max(texCoord[0].y, texCoord[1].y), texCoord[2].y) + ); + } + }; + + struct SpriteTexture { + uint16 clut; + uint16 tile; + int16 l, t, r, b; + short2 texCoord[2]; + + short4 getMinMax() const { + return short4( texCoord[0].x, texCoord[0].y, texCoord[1].x, texCoord[1].y ); + } + }; + + // used for access from ::cmp func + static SpriteTexture *gSpriteTextures = NULL; + static ObjectTexture *gObjectTextures = NULL; + + struct Face { + union { + struct { uint16 texture:15, doubleSided:1; }; + uint16 value; + } flags; + uint8 colored; // !!! not existing in file + uint8 vCount; // !!! not existing in file uint16 vertices[4]; - union { - struct { uint16 texture:15, doubleSided:1; }; - uint16 value; - } flags; - uint16 colored; // !!! not existing in file + + static int cmp(const Face &a, const Face &b) { + int aIndex = a.flags.texture; + int bIndex = b.flags.texture; + + ObjectTexture &ta = gObjectTextures[aIndex]; + ObjectTexture &tb = gObjectTextures[bIndex]; + + if (ta.tile.index < tb.tile.index) + return -1; + if (ta.tile.index > tb.tile.index) + return 1; + + #ifdef SPLIT_BY_CLUT + if (ta.clut < tb.clut) + return -1; + if (ta.clut > tb.clut) + return 1; + #endif + + if (aIndex < bIndex) + return -1; + if (aIndex > bIndex) + return 1; + + return 0; + } }; - struct Triangle { - uint16 vertices[3]; - union { - struct { uint16 texture:15, doubleSided:1; }; - uint16 value; - } flags; - uint32 colored; // !!! not existing in file - }; - - #define FACE4_SIZE (sizeof(Rectangle) - sizeof(uint16)) - #define FACE3_SIZE (sizeof(Triangle) - sizeof(uint32)) + #define FACE4_SIZE (sizeof(Face) - sizeof(uint8) - sizeof(uint8)) + #define FACE3_SIZE (FACE4_SIZE - sizeof(uint16)) struct Tile4 { struct { @@ -1156,10 +1216,13 @@ namespace TR { uint32 size; // Number of data words (uint16_t's) int16 vCount; - int16 rCount; int16 tCount; + int16 rCount; + int16 fCount; int16 sCount; + Face *faces; + struct Vertex { TR::Vertex vertex; int16 unused_lighting; // 0 (bright) .. 0x1FFF (dark) @@ -1167,12 +1230,34 @@ namespace TR { Color32 color; } *vertices; - Rectangle *rectangles; - Triangle *triangles; - struct Sprite { int16 vertex; int16 texture; + + static int cmp(const Sprite &a, const Sprite &b) { + SpriteTexture &ta = gSpriteTextures[a.texture]; + SpriteTexture &tb = gSpriteTextures[b.texture]; + + if (ta.tile < tb.tile) + return -1; + if (ta.tile > tb.tile) + return 1; + + #ifdef SPLIT_BY_CLUT + if (ta.clut < tb.clut) + return -1; + if (ta.clut > tb.clut) + return 1; + #endif + + if (a.texture < b.texture) + return -1; + if (a.texture > b.texture) + return 1; + + return 0; + } + } *sprites; } data; @@ -1185,8 +1270,11 @@ namespace TR { uint16 lightsCount; uint16 meshesCount; int16 alternateRoom; - struct { - uint16 water:1, :2, sky:1, :1, wind:1, unused:9, visible:1; + union { + struct { + uint16 water:1, :2, sky:1, :1, wind:1, unused:9, visible:1; + }; + uint16 value; } flags; uint8 waterScheme; uint8 reverbType; @@ -1293,9 +1381,7 @@ namespace TR { union TriggerCommand { uint16 value; struct { - uint16 args:10; - Action action:5; - uint16 end:1; + uint16 args:10, action:5, end:1; }; struct { uint16 timer:8, once:1, speed:5, :2; @@ -1342,25 +1428,24 @@ namespace TR { uint16 value; } flags; int16 vCount; - int16 rCount; int16 tCount; + int16 rCount; + int16 fCount; int32 offset; Vertex *vertices; - Rectangle *rectangles; - Triangle *triangles; + Face *faces; - Mesh() : vertices(0), rectangles(0), triangles(0) {} + Mesh() : vertices(0), faces(0) {} ~Mesh() { delete[] vertices; - delete[] rectangles; - delete[] triangles; + delete[] faces; } }; struct Entity { - enum ActiveState : uint16 { asNone, asActive, asInactive }; - enum Type : uint16 { TR_TYPES(DECL_ENUM) }; + enum ActiveState { asNone, asActive, asInactive }; + enum Type { TR_TYPES(DECL_ENUM) TYPE_MAX = 0xFFFF }; Type type; int16 room; @@ -1369,7 +1454,9 @@ namespace TR { int16 intensity; int16 intensity2; union Flags { - struct { ActiveState state:2; uint16 unused:4, collision:1, invisible:1, once:1, active:5, reverse:1, rendered:1; }; + struct { + uint16 state:2, unused:4, collision:1, invisible:1, once:1, active:5, reverse:1, rendered:1; + }; uint16 value; } flags; // not exists in file @@ -1928,19 +2015,19 @@ namespace TR { }; struct Model { - Entity::Type type; - uint16 unused; - uint16 mCount; - uint16 mStart; - uint32 node; - uint32 frame; - uint16 animation; - uint16 align; + Entity::Type type; + uint16 unused; + uint16 mCount; + uint16 mStart; + uint32 node; + uint32 frame; + uint16 animation; + uint16 align; }; struct StaticMesh { - uint32 id; - uint16 mesh; + uint32 id; + uint16 mesh; MinMax vbox; MinMax cbox; uint16 flags; @@ -1959,42 +2046,11 @@ namespace TR { } }; - struct Tile { - uint16 index:14, undefined:1, triangle:1; - }; - - struct ObjectTexture { - uint16 clut; - Tile tile; // tile or palette index - uint16 attribute:15, repeat:1; // 0 - opaque, 1 - transparent, 2 - blend additive, - short2 texCoord[4]; - - short4 getMinMax() const { - return { - min(min(texCoord[0].x, texCoord[1].x), texCoord[2].x), - min(min(texCoord[0].y, texCoord[1].y), texCoord[2].y), - max(max(texCoord[0].x, texCoord[1].x), texCoord[2].x), - max(max(texCoord[0].y, texCoord[1].y), texCoord[2].y), - }; - } - }; - - struct SpriteTexture { - uint16 clut; - uint16 tile; - int16 l, t, r, b; - short2 texCoord[2]; - - short4 getMinMax() const { - return { texCoord[0].x, texCoord[0].y, texCoord[1].x, texCoord[1].y }; - } - }; - struct SpriteSequence { - Entity::Type type; - uint16 unused; - int16 sCount; - int16 sStart; + Entity::Type type; + uint16 unused; + int16 sCount; + int16 sStart; }; struct Camera { @@ -2023,9 +2079,15 @@ namespace TR { }; struct Box { - uint32 minZ, maxZ; // Horizontal dimensions in global units - uint32 minX, maxX; - int16 floor; // Height value in global units + union { + struct { + uint32 minZ, maxZ; // Horizontal dimensions in global units + uint32 minX, maxX; + }; + int32 sides[4]; + }; + + int16 floor; // Height value in global units union { struct { uint16 index:14, block:1, blockable:1; // Index into Overlaps[]. @@ -2033,8 +2095,22 @@ namespace TR { uint16 value; } overlap; - bool contains(uint32 x, uint32 z) { - return x >= minX && x <= maxX && z >= minZ && z <= maxZ; + bool contains(uint32 x, uint32 z) const { + return z >= minZ && z <= maxZ && x >= minX && x <= maxX; + } + + void expand(uint32 value) { + minZ -= value; + minX -= value; + maxZ += value; + maxX += value; + } + + void clip(const Box &box) { + minZ = max(minZ, box.minZ); + minX = max(minX, box.minX); + maxZ = min(maxZ, box.maxZ); + maxX = min(maxX, box.maxX); } }; @@ -2220,9 +2296,13 @@ namespace TR { int32 paletteSize; Color24 *palette; Color32 *palette32; + int32 clutsCount; CLUT *cluts; + Tile4 *tiles4; + Tile8 *tiles8; + Tile16 *tiles16; uint16 cameraFramesCount; CameraFrame *cameraFrames; @@ -2246,16 +2326,18 @@ namespace TR { SaveGame::CurrentState state; // common - enum Trigger : uint32 { - ACTIVATE , - PAD , - SWITCH , - KEY , - PICKUP , - HEAVY , - ANTIPAD , - COMBAT , - DUMMY , + struct Trigger { + enum Type { + ACTIVATE , + PAD , + SWITCH , + KEY , + PICKUP , + HEAVY , + ANTIPAD , + COMBAT , + DUMMY , + }; }; struct FloorInfo { @@ -2269,7 +2351,7 @@ namespace TR { int lava; int trigCmdCount; int climb; - Trigger trigger; + Trigger::Type trigger; FloorData::TriggerInfo trigInfo; FloorData::TriggerCommand trigCmd[MAX_TRIGGER_COMMANDS]; @@ -2334,9 +2416,10 @@ namespace TR { int startPos = stream.pos; memset(this, 0, sizeof(*this)); cutEntity = -1; - Tile8 *tiles8 = NULL; - Tile16 *tiles16 = NULL; + tiles4 = NULL; + tiles8 = NULL; + tiles16 = NULL; palette = NULL; palette32 = NULL; @@ -2431,8 +2514,8 @@ namespace TR { if (version == VER_TR1_PSX) { // tiles stream.read(tiles4, tilesCount = 13); - stream.read(cluts, clutsCount = 512); - stream.seek(0x4000); + stream.read(cluts, clutsCount = 1024); + //stream.seek(0x4000); } stream.read(unused); @@ -2456,7 +2539,8 @@ namespace TR { models = stream.read(modelsCount) ? new Model[modelsCount] : NULL; for (int i = 0; i < modelsCount; i++) { Model &m = models[i]; - stream.read(m.type); + uint16 type; + m.type = Entity::Type(stream.read(type)); stream.read(m.unused); stream.read(m.mCount); stream.read(m.mStart); @@ -2531,7 +2615,8 @@ namespace TR { entities = new Entity[entitiesCount]; for (int i = 0; i < entitiesBaseCount; i++) { Entity &e = entities[i]; - stream.read(e.type); + uint16 type; + e.type = Entity::Type(stream.read(type)); stream.read(e.room); stream.read(e.x); stream.read(e.y); @@ -2618,17 +2703,14 @@ namespace TR { } initRoomMeshes(); - initTiles(tiles4, tiles8, tiles16, palette, palette32, cluts); - - //delete[] tiles4; - //tiles4 = NULL; - delete[] tiles8; - delete[] tiles16; memset(&state, 0, sizeof(state)); initExtra(); initCutscene(); + + TR::gObjectTextures = objectTextures; + TR::gSpriteTextures = spriteTextures; } ~Level() { @@ -2637,8 +2719,7 @@ namespace TR { for (int i = 0; i < roomsCount; i++) { Room &r = rooms[i]; delete[] r.data.vertices; - delete[] r.data.rectangles; - delete[] r.data.triangles; + delete[] r.data.faces; delete[] r.data.sprites; delete[] r.portals; delete[] r.sectors; @@ -2676,6 +2757,8 @@ namespace TR { delete[] palette32; delete[] cluts; delete[] tiles4; + delete[] tiles8; + delete[] tiles16; delete[] cameraFrames; delete[] demoData; delete[] soundsMap; @@ -2848,18 +2931,22 @@ namespace TR { return TR::isCutsceneLevel(id); } - void readFace(Stream &stream, Rectangle &v, bool colored = false) { - for (int i = 0; i < COUNT(v.vertices); i++) - stream.read(v.vertices[i]); - stream.read(v.flags.value); - v.colored = colored; - } + void readFace(Stream &stream, Face &f, bool colored, bool triangle) { + f.vCount = triangle ? 3 : 4; - void readFace(Stream &stream, Triangle &v, bool colored = false) { - for (int i = 0; i < COUNT(v.vertices); i++) - stream.read(v.vertices[i]); - stream.read(v.flags.value); - v.colored = colored; + for (int i = 0; i < f.vCount; i++) + stream.read(f.vertices[i]); + + if (triangle) + f.vertices[3] = 0; + + stream.read(f.flags.value); + + #ifndef SPLIT_BY_TILE + f.colored = colored; + #else + f.colored = false; + #endif } void readRoom(Stream &stream, Room &r) { @@ -2868,7 +2955,6 @@ namespace TR { stream.read(r.info); // room data stream.read(d.size); - int startOffset = stream.pos; if (version == VER_TR1_PSX) stream.seek(2); d.vertices = stream.read(d.vCount) ? new Room::Data::Vertex[d.vCount] : NULL; for (int i = 0; i < d.vCount; i++) { @@ -2920,10 +3006,22 @@ namespace TR { if (version == VER_TR2_PSX) stream.seek(2); - d.rectangles = stream.read(d.rCount) ? new Rectangle[d.rCount] : NULL; + int tmp = stream.pos; + stream.seek(stream.read(d.rCount) * FACE4_SIZE); // uint32 colored (not existing in file) + stream.seek(stream.read(d.tCount) * FACE3_SIZE); + stream.setPos(tmp); + + d.fCount = d.rCount + d.tCount; + d.faces = d.fCount ? new Face[d.fCount] : NULL; + + int idx = 0; + + stream.seek(sizeof(d.rCount)); if (version == VER_TR2_PSX) { - for (int i = 0; i < d.rCount; i++) - stream.raw(&d.rectangles[i].flags.value, sizeof(uint16)); + ASSERT(false); // TODO + /* + for (int i = 0; i < d.fCount; i++) + stream.read(d.rectangles[i].flags.value); if ((stream.pos - startOffset) % 4) stream.seek(2); for (int i = 0; i < d.rCount; i++) { Rectangle &v = d.rectangles[i]; @@ -2934,18 +3032,19 @@ namespace TR { v.vertices[3] >>= 2; v.colored = false; } - } else { - for (int i = 0; i < d.rCount; i++) - readFace(stream, d.rectangles[i]); - } + */ + } else + for (int i = 0; i < d.rCount; i++) readFace(stream, d.faces[idx++], false, false); if (version & VER_PSX) { // swap indices (quad strip -> quad list) only for PSX version for (int j = 0; j < d.rCount; j++) - swap(d.rectangles[j].vertices[2], d.rectangles[j].vertices[3]); + swap(d.faces[j].vertices[2], d.faces[j].vertices[3]); } - d.triangles = stream.read(d.tCount) ? new Triangle[d.tCount] : NULL; + stream.seek(sizeof(d.tCount)); if (version == VER_TR2_PSX) { + ASSERT(false); // TODO + /* stream.seek(2); for (int i = 0; i < d.tCount; i++) { Triangle &v = d.triangles[i]; @@ -2956,9 +3055,10 @@ namespace TR { v.vertices[2] >>= 2; v.colored = false; } + */ } else { for (int i = 0; i < d.tCount; i++) - readFace(stream, d.triangles[i]); + readFace(stream, d.faces[idx++], false, true); } // room sprites @@ -2983,10 +3083,10 @@ namespace TR { // sectors stream.read(r.zSectors); stream.read(r.xSectors); - r.sectors = (r.zSectors * r.xSectors) ? new TR::Room::Sector[r.zSectors * r.xSectors] : NULL; + r.sectors = (r.zSectors * r.xSectors) ? new Room::Sector[r.zSectors * r.xSectors] : NULL; for (int i = 0; i < r.zSectors * r.xSectors; i++) { - TR::Room::Sector &s = r.sectors[i]; + Room::Sector &s = r.sectors[i]; stream.read(s.floorIndex); stream.read(s.boxIndex); @@ -3079,7 +3179,7 @@ namespace TR { // misc flags stream.read(r.alternateRoom); - stream.read(r.flags); + stream.read(r.flags.value); if (version & VER_TR3) { stream.read(r.waterScheme); stream.read(r.reverbType); @@ -3102,7 +3202,7 @@ namespace TR { stream.read(mesh.center); stream.read(mesh.radius); - stream.read(mesh.flags); + stream.read(mesh.flags.value); stream.read(mesh.vCount); switch (version) { @@ -3147,11 +3247,11 @@ namespace TR { c.w = 0x1FFF; } else { // intensity stream.read(c.w); - n = { 0, 0, 0, 0 }; + n = short4( 0, 0, 0, 0 ); } } - uint16 rCount, crCount, tCount, ctCount; + uint16 rCount, tCount, crCount, ctCount; int tmp = stream.pos; stream.seek(stream.read(rCount) * FACE4_SIZE); // uint32 colored (not existing in file) @@ -3162,14 +3262,14 @@ namespace TR { mesh.rCount = rCount + crCount; mesh.tCount = tCount + ctCount; + mesh.fCount = mesh.rCount + mesh.tCount; + mesh.faces = mesh.fCount ? new Face[mesh.fCount] : NULL; - mesh.rectangles = mesh.rCount ? new Rectangle[mesh.rCount] : NULL; - mesh.triangles = mesh.tCount ? new Triangle[mesh.tCount] : NULL; - - stream.seek(sizeof(uint16)); for (int i = 0; i < rCount; i++) readFace(stream, mesh.rectangles[i]); - stream.seek(sizeof(uint16)); for (int i = 0; i < tCount; i++) readFace(stream, mesh.triangles[i]); - stream.seek(sizeof(uint16)); for (int i = 0; i < crCount; i++) readFace(stream, mesh.rectangles[rCount + i], true); - stream.seek(sizeof(uint16)); for (int i = 0; i < ctCount; i++) readFace(stream, mesh.triangles[tCount + i], true); + int idx = 0; + stream.seek(sizeof(rCount)); for (int i = 0; i < rCount; i++) readFace(stream, mesh.faces[idx++], false, false); + stream.seek(sizeof(tCount)); for (int i = 0; i < tCount; i++) readFace(stream, mesh.faces[idx++], false, true); + stream.seek(sizeof(crCount)); for (int i = 0; i < crCount; i++) readFace(stream, mesh.faces[idx++], true, false); + stream.seek(sizeof(ctCount)); for (int i = 0; i < ctCount; i++) readFace(stream, mesh.faces[idx++], true, true); break; } case VER_TR1_PSX : @@ -3202,7 +3302,7 @@ namespace TR { c.w = 0x1FFF; } else { // intensity stream.read(c.w); - n = { 0, 0, 0, 0 }; + n = short4( 0, 0, 0, 0 ); } } @@ -3214,19 +3314,27 @@ namespace TR { stream.seek((FACE3_SIZE + 2) * ctCount); } - mesh.rectangles = stream.read(mesh.rCount) ? new Rectangle[mesh.rCount] : NULL; - for (int i = 0; i < mesh.rCount; i++) readFace(stream, mesh.rectangles[i]); + int tmp = stream.pos; + stream.seek(stream.read(mesh.rCount) * FACE4_SIZE); // uint32 colored (not existing in file) + stream.seek(stream.read(mesh.tCount) * FACE3_SIZE); + stream.setPos(tmp); - mesh.triangles = stream.read(mesh.tCount) ? new Triangle[mesh.tCount] : NULL; - for (int i = 0; i < mesh.tCount; i++) readFace(stream, mesh.triangles[i]); + mesh.fCount = mesh.rCount + mesh.tCount; + mesh.faces = mesh.fCount ? new Face[mesh.fCount] : NULL; - if (mesh.rCount == 0 && mesh.tCount == 0) + int idx = 0; + stream.seek(sizeof(mesh.rCount)); for (int i = 0; i < mesh.rCount; i++) readFace(stream, mesh.faces[idx++], false, false); + stream.seek(sizeof(mesh.tCount)); for (int i = 0; i < mesh.tCount; i++) readFace(stream, mesh.faces[idx++], false, true); + + if (!mesh.fCount) LOG("! warning: mesh %d has no geometry with %d vertices\n", meshesCount - 1, mesh.vCount); //ASSERT(mesh.rCount != 0 || mesh.tCount != 0); - for (int i = 0; i < mesh.rCount; i++) { - Rectangle &f = mesh.rectangles[i]; - f.colored = (f.flags.texture < 256) ? true : false; + for (int i = 0; i < mesh.fCount; i++) { + Face &f = mesh.faces[i]; + #ifndef SPLIT_BY_TILE + f.colored = (f.flags.texture < 256) ? true : false; + #endif if (version == VER_TR2_PSX) { f.vertices[0] >>= 3; @@ -3236,25 +3344,14 @@ namespace TR { } } - for (int i = 0; i < mesh.tCount; i++) { - Triangle &f = mesh.triangles[i]; - f.colored = (f.flags.texture < 256) ? true : false; - - if (version == VER_TR2_PSX) { - f.vertices[0] >>= 3; - f.vertices[1] >>= 3; - f.vertices[2] >>= 3; - } - } - break; } default : ASSERT(false); } - #define RECALC_ZERO_NORMALS(mesh, face, count)\ + #define RECALC_ZERO_NORMALS(mesh, face)\ int fn = -1;\ - for (int j = 0; j < count; j++) {\ + for (int j = 0; j < face.vCount; j++) {\ Mesh::Vertex &v = mesh.vertices[face.vertices[j]];\ short4 &n = v.normal;\ if (!(n.x | n.y | n.z)) {\ @@ -3274,21 +3371,9 @@ namespace TR { } // recalc zero normals - for (int i = 0; i < mesh.rCount; i++) { - Rectangle &f = mesh.rectangles[i]; - ASSERT(f.vertices[0] < mesh.vCount); - ASSERT(f.vertices[1] < mesh.vCount); - ASSERT(f.vertices[2] < mesh.vCount); - ASSERT(f.vertices[3] < mesh.vCount); - RECALC_ZERO_NORMALS(mesh, f, 4); - } - - for (int i = 0; i < mesh.tCount; i++) { - Triangle &f = mesh.triangles[i]; - ASSERT(f.vertices[0] < mesh.vCount); - ASSERT(f.vertices[1] < mesh.vCount); - ASSERT(f.vertices[2] < mesh.vCount); - RECALC_ZERO_NORMALS(mesh, f, 3); + for (int i = 0; i < mesh.fCount; i++) { + Face &f = mesh.faces[i]; + RECALC_ZERO_NORMALS(mesh, f); } #undef RECALC_ZERO_NORMALS @@ -3320,10 +3405,10 @@ namespace TR { t.clut = c;\ t.tile = d.tile;\ t.attribute = d.attribute;\ - t.texCoord[0] = { d.x0, d.y0 };\ - t.texCoord[1] = { d.x1, d.y1 };\ - t.texCoord[2] = { d.x2, d.y2 };\ - t.texCoord[3] = { d.x3, d.y3 };\ + t.texCoord[0] = short2( d.x0, d.y0 );\ + t.texCoord[1] = short2( d.x1, d.y1 );\ + t.texCoord[2] = short2( d.x2, d.y2 );\ + t.texCoord[3] = short2( d.x3, d.y3 );\ ASSERT(d.x0 < 256 && d.x1 < 256 && d.x2 < 256 && d.x3 < 256 && d.y0 < 256 && d.y1 < 256 && d.y2 < 256 && d.y3 < 256);\ } @@ -3342,7 +3427,7 @@ namespace TR { uint8 xh2, x2, yh2, y2; uint8 xh3, x3, yh3, y3; } d; - stream.read(d); + stream.raw(&d, sizeof(d)); SET_PARAMS(t, d, 0); break; } @@ -3358,7 +3443,7 @@ namespace TR { uint8 x3, y3; uint16 attribute; } d; - stream.read(d); + stream.raw(&d, sizeof(d)); SET_PARAMS(t, d, d.clut); break; } @@ -3392,10 +3477,10 @@ namespace TR { uint16 w, h; int16 l, t, r, b; } d; - stream.read(d); + stream.raw(&d, sizeof(d)); SET_PARAMS(t, d, 0); - t.texCoord[0] = { d.u, d.v }; - t.texCoord[1] = { (uint8)(d.u + (d.w >> 8)), (uint8)(d.v + (d.h >> 8)) }; + t.texCoord[0] = short2( d.u, d.v ); + t.texCoord[1] = short2( (uint8)(d.u + (d.w >> 8)), (uint8)(d.v + (d.h >> 8)) ); break; } case VER_TR1_PSX : @@ -3407,10 +3492,10 @@ namespace TR { uint8 u0, v0; uint8 u1, v1; } d; - stream.read(d); + stream.raw(&d, sizeof(d)); SET_PARAMS(t, d, d.clut); - t.texCoord[0] = { d.u0, d.v0 }; - t.texCoord[1] = { d.u1, d.v1 }; + t.texCoord[0] = short2( d.u0, d.v0 ); + t.texCoord[1] = short2( d.u1, d.v1 ); break; } default : ASSERT(false); @@ -3421,12 +3506,13 @@ namespace TR { spriteSequences = stream.read(spriteSequencesCount) ? new SpriteSequence[spriteSequencesCount] : NULL; for (int i = 0; i < spriteSequencesCount; i++) { - TR::SpriteSequence &s = spriteSequences[i]; - stream.read(s.type); + SpriteSequence &s = spriteSequences[i]; + uint16 type; + stream.read(type); + s.type = Entity::remap(version, Entity::Type(type)); stream.read(s.unused); stream.read(s.sCount); stream.read(s.sStart); - s.type = Entity::remap(version, s.type); s.sCount = -s.sCount; } } @@ -3439,7 +3525,7 @@ namespace TR { } } - void initTiles(Tile4 *tiles4, Tile8 *tiles8, Tile16 *tiles16, Color24 *palette, Color32 *palette32, CLUT *cluts) { + void initTiles() { tiles = new Tile32[tilesCount]; // convert to RGBA switch (version) { @@ -3514,7 +3600,7 @@ namespace TR { Color32 *ptr = &tiles[i].color[0]; for (int y = 0; y < 256; y++) { for (int x = 0; x < 256; x++) { - TR::Color32 c = tiles16[i].color[y * 256 + x]; + Color32 c = tiles16[i].color[y * 256 + x]; ptr[x].r = c.b; ptr[x].g = c.g; ptr[x].b = c.r; @@ -3527,6 +3613,11 @@ namespace TR { } default : ASSERT(false); } + + delete[] tiles8; + delete[] tiles16; + tiles8 = NULL; + tiles16 = NULL; } // common methods @@ -3598,12 +3689,52 @@ namespace TR { return 0; } - int getNextRoom(int floorIndex) const { - if (!floorIndex) return NO_ROOM; - FloorData *fd = &floors[floorIndex]; - // floor data always in this order and can't be less than uint16 x 3 - if (fd->cmd.func == FloorData::FLOOR) fd += 2; // skip floor slant info - if (fd->cmd.func == FloorData::CEILING) fd += 2; // skip ceiling slant info + void floorSkipCommand(FloorData* &fd, int func) { + switch (func) { + case FloorData::PORTAL : + case FloorData::FLOOR : + case FloorData::CEILING : + fd++; + break; + + case FloorData::TRIGGER : + fd++; + do {} while (!(*fd++).triggerCmd.end); + break; + + case FloorData::LAVA : + case FloorData::CLIMB : + break; + + case 0x07 : + case 0x08 : + case 0x09 : + case 0x0A : + case 0x0B : + case 0x0C : + case 0x0D : + case 0x0E : + case 0x0F : + case 0x10 : + case 0x11 : + case 0x12 : fd++; break; // TODO TR3 triangulation + + case 0x13 : break; // TODO TR3 monkeyswing + + case 0x14 : + case 0x15 : break; // TODO TR3 minecart + + default : LOG("unknown func to skip: %d\n", func); + } + } + + int getNextRoom(const Room::Sector *sector) const { + ASSERT(sector); + if (!sector->floorIndex) return NO_ROOM; + FloorData *fd = &floors[sector->floorIndex]; + // floor data always in this order + if (!fd->cmd.end && fd->cmd.func == FloorData::FLOOR) fd += 2; // skip floor slant info + if (!fd->cmd.end && fd->cmd.func == FloorData::CEILING) fd += 2; // skip ceiling slant info if (fd->cmd.func == FloorData::PORTAL) return (++fd)->data; return NO_ROOM; } @@ -3629,7 +3760,7 @@ namespace TR { return room.sectors[sx * room.zSectors + sz]; } - + Room::Sector& getSector(int roomIndex, int x, int z, int §orIndex) { ASSERT(roomIndex >= 0 && roomIndex < roomsCount); Room &room = rooms[roomIndex]; @@ -3639,28 +3770,35 @@ namespace TR { z /= 1024; return room.sectors[sectorIndex = (x * room.zSectors + z)]; } - - Room::Sector* getSector(int16 &roomIndex, int x, int y, int z) const { + + Room::Sector* getSector(int16 &roomIndex, const vec3 &pos) { ASSERT(roomIndex >= 0 && roomIndex <= roomsCount); Room::Sector *sector = NULL; + int x = int(pos.x); + int y = int(pos.y); + int z = int(pos.z); + // check horizontal while (1) { // Let's Rock! - TR::Room &room = rooms[roomIndex]; + Room &room = rooms[roomIndex]; - int sx = (x - room.info.x) >> 10; - int sz = (z - room.info.z) >> 10; + int sx = (x - room.info.x) / 1024; + int sz = (z - room.info.z) / 1024; - if (sz <= 0 || sz >= room.xSectors - 1) { - sx = clamp(sx, 0, room.xSectors - 1); - sz = clamp(sz, 1, room.zSectors - 2); +// sx = clamp(sx, 0, room.xSectors - 1); +// sz = clamp(sz, 0, room.zSectors - 1); + + if (sz <= 0 || sz >= room.zSectors - 1) { + sz = clamp(sz, 0, room.zSectors - 1); + sx = clamp(sx, 1, room.xSectors - 2); } else sx = clamp(sx, 0, room.xSectors - 1); sector = room.sectors + sx * room.zSectors + sz; - int nextRoom = getNextRoom(sector->floorIndex); + int nextRoom = getNextRoom(sector); if (nextRoom == NO_ROOM) break; @@ -3669,17 +3807,110 @@ namespace TR { // check vertical while (sector->roomAbove != NO_ROOM && y < sector->ceiling * 256) { - TR::Room &room = rooms[roomIndex = sector->roomAbove]; + Room &room = rooms[roomIndex = sector->roomAbove]; sector = room.sectors + (x - room.info.x) / 1024 * room.zSectors + (z - room.info.z) / 1024; } while (sector->roomBelow != NO_ROOM && y >= sector->floor * 256) { - TR::Room &room = rooms[roomIndex = sector->roomBelow]; + Room &room = rooms[roomIndex = sector->roomBelow]; sector = room.sectors + (x - room.info.x) / 1024 * room.zSectors + (z - room.info.z) / 1024; } return sector; } + + float getFloor(const Room::Sector *sector, const vec3 &pos) { + int x = int(pos.x); + int z = int(pos.z); + + while (sector->roomBelow != NO_ROOM) { + Room &room = rooms[sector->roomBelow]; + sector = room.sectors + (x - room.info.x) / 1024 * room.zSectors + (z - room.info.z) / 1024; + } + + int floor = sector->floor * 256; + + if (!sector->floorIndex) + return float(floor); + + FloorData *fd = &floors[sector->floorIndex]; + FloorData::Command cmd; + + do { + cmd = (*fd++).cmd; + + switch (cmd.func) { + case FloorData::FLOOR : { + FloorData::Slant slant = (*fd++).slant; + int sx = (int)slant.x; + int sz = (int)slant.z; + int dx = x % 1024; + int dz = z % 1024; + floor -= sx * (sx > 0 ? (dx - 1023) : dx) >> 2; + floor -= sz * (sz > 0 ? (dz - 1023) : dz) >> 2; + break; + } + + case FloorData::TRIGGER : { + fd++; + FloorData::TriggerCommand trigCmd; + do { + trigCmd = (*fd++).triggerCmd; + if (trigCmd.action != Action::ACTIVATE) + continue; + // TODO controller[trigCmd.args]->getFloor(&floor, x, y, z); + } while (!trigCmd.end); + break; + } + + default : floorSkipCommand(fd, cmd.func); + } + } while (!cmd.end); + + return float(floor); + } + + float getCeiling(const Room::Sector *sector, const vec3 &pos) { + int x = int(pos.x); + int z = int(pos.z); + + while (sector->roomAbove != NO_ROOM) { + Room &room = rooms[sector->roomAbove]; + sector = room.sectors + (x - room.info.x) / 1024 * room.zSectors + (z - room.info.z) / 1024; + } + + int ceiling = sector->ceiling * 256; + + if (!sector->floorIndex) + return float(ceiling); + + FloorData *fd = &floors[sector->floorIndex]; + + if (fd->cmd.func == FloorData::FLOOR) { + if (fd->cmd.end) return float(ceiling); + fd += 2; // skip floor slant + } + + if (fd->cmd.func == FloorData::CEILING) { + FloorData::Slant slant = (++fd)->slant; + int sx = (int)slant.x; + int sz = (int)slant.z; + int dx = x % 1024; + int dz = z % 1024; + ceiling -= sx * (sx < 0 ? (dx - 1023) : dx) >> 2; + ceiling += sz * (sz > 0 ? (dz - 1023) : dz) >> 2; + } + + // TODO parse triggers to collide with objects (bridges, trap doors/floors etc) + + return float(ceiling); + } + + bool isBlocked(int16 &roomIndex, const vec3 &pos) { + Room::Sector *sector = getSector(roomIndex, pos); + return pos.y >= getFloor(sector, pos) || pos.y <= getCeiling(sector, pos); + } + }; // struct Level } diff --git a/src/frustum.h b/src/frustum.h index 670b1f4..4428485 100644 --- a/src/frustum.h +++ b/src/frustum.h @@ -19,7 +19,7 @@ struct Frustum { 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(); + planes[i] *= 1.0f / planes[i].xyz().length(); } // AABB visibility check @@ -27,7 +27,7 @@ struct Frustum { if (count < 4) return false; for (int i = start; i < start + count; i++) { - const vec3 &n = planes[i].xyz; + const vec3 &n = planes[i].xyz(); const float d = -planes[i].w; if (n.dot(max) < d && @@ -50,9 +50,9 @@ struct Frustum { mat4 m = matrix.inverse(); for (int i = 0; i < count; i++) { vec4 &p = planes[i]; - vec4 o = m * vec4(p.xyz * (-p.w), 1.0f); - vec4 n = m * vec4(p.xyz, 0.0f); - planes[start + i] = vec4(n.xyz, -n.xyz.dot(o.xyz)); + vec4 o = m * vec4(p.xyz() * (-p.w), 1.0f); + vec4 n = m * vec4(p.xyz(), 0.0f); + planes[start + i] = vec4(n.xyz(), -n.xyz().dot(o.xyz())); } bool visible = isVisible(min, max); start = 0; @@ -64,7 +64,7 @@ struct Frustum { if (count < 4) return false; for (int i = 0; i < count; i++) - if (planes[i].xyz.dot(center) + planes[i].w < -radius) + if (planes[i].xyz().dot(center) + planes[i].w < -radius) return false; return true; } diff --git a/src/game.h b/src/game.h index d747f77..e206a8c 100644 --- a/src/game.h +++ b/src/game.h @@ -38,7 +38,6 @@ namespace Game { nextLevel = NULL; Core::init(); - shaderCache = new ShaderCache(); UI::init(level); @@ -49,21 +48,19 @@ namespace Game { startLevel(lvl); } - void init(char *lvlName = NULL, char *sndName = NULL) { + void init(const char *lvlName = NULL) { char fileName[255]; TR::Version version = TR::getGameVersion(); - if (!lvlName && version != TR::VER_UNKNOWN) { - lvlName = fileName; - TR::getGameLevelFile(lvlName, version, TR::getTitleId(version)); - } - if (!lvlName) { - lvlName = fileName; - strcpy(lvlName, "level/1/TITLE.PSX"); - } + if (version != TR::VER_UNKNOWN) + TR::getGameLevelFile(fileName, version, TR::getTitleId(version)); + else + strcpy(fileName, "level/1/TITLE.PSX"); + } else + strcpy(fileName, lvlName); - init(new Stream(lvlName)); + init(new Stream(fileName)); } void deinit() { @@ -152,4 +149,4 @@ namespace Game { } } -#endif +#endif \ No newline at end of file diff --git a/src/gameflow.h b/src/gameflow.h index eeac48b..95e7e27 100644 --- a/src/gameflow.h +++ b/src/gameflow.h @@ -13,7 +13,7 @@ namespace TR { NO_TRACK = 0xFF, }; - enum Version : uint32 { + enum Version { VER_UNKNOWN = 0, VER_PC = 256, @@ -38,9 +38,11 @@ namespace TR { VER_TR3_PC = VER_TR3 | VER_PC, VER_TR3_PSX = VER_TR3 | VER_PSX, + + VER_MAX = 0xFFFFFFFF, }; - enum LevelID : uint32 { + enum LevelID { LVL_CUSTOM, // TR1 LVL_TR1_TITLE, @@ -188,7 +190,7 @@ namespace TR { TRACK_TR3_CUT_12 = 66, }; - struct { + struct LevelInfo { const char *name; const char *title; int ambientTrack; diff --git a/src/input.h b/src/input.h index 43068b7..527d36e 100644 --- a/src/input.h +++ b/src/input.h @@ -9,14 +9,14 @@ namespace Input { bool down[ikMAX]; bool state[cMAX]; - struct { + struct Mouse { vec2 pos; struct { vec2 L, R, M; } start; } mouse; - struct { + struct Joystick { vec2 L, R; float LT, RT; int POV; @@ -28,7 +28,7 @@ namespace Input { vec2 pos; } touch[6]; - struct { + struct HMD { Basis pivot; Basis basis; bool ready; diff --git a/src/inventory.h b/src/inventory.h index 8ce4931..4bb70d2 100644 --- a/src/inventory.h +++ b/src/inventory.h @@ -7,7 +7,12 @@ #define INVENTORY_MAX_ITEMS 32 #define INVENTORY_MAX_RADIUS 688.0f -#define INVENTORY_BG_SIZE 512 +#ifdef _PSP + #define INVENTORY_BG_SIZE 256 +#else + #define INVENTORY_BG_SIZE 512 +#endif + #define INVENTORY_HEIGHT 2048.0f #define TITLE_LOADING 64.0f @@ -48,49 +53,52 @@ struct Inventory { StringID str; Page page; int model; + + Desc() {} + Desc(StringID str, Page page, int model) : str(str), page(page), model(model) {} } desc; Item() : anim(NULL) {} Item(TR::Level *level, TR::Entity::Type type, int count = 1) : type(type), count(count), angle(0.0f), value(0) { switch (type) { - case TR::Entity::INV_PASSPORT : desc = { STR_GAME, PAGE_OPTION, level->extra.inv.passport }; break; - case TR::Entity::INV_PASSPORT_CLOSED : desc = { STR_GAME, PAGE_OPTION, level->extra.inv.passport_closed }; break; - case TR::Entity::INV_MAP : desc = { STR_MAP, PAGE_INVENTORY, level->extra.inv.map }; break; - case TR::Entity::INV_COMPASS : desc = { STR_COMPASS, PAGE_INVENTORY, level->extra.inv.compass }; break; - case TR::Entity::INV_STOPWATCH : desc = { STR_STOPWATCH, PAGE_INVENTORY, level->extra.inv.stopwatch }; break; - case TR::Entity::INV_HOME : desc = { STR_HOME, PAGE_OPTION, level->extra.inv.home }; break; - case TR::Entity::INV_DETAIL : desc = { STR_DETAIL, PAGE_OPTION, level->extra.inv.detail }; break; - case TR::Entity::INV_SOUND : desc = { STR_SOUND, PAGE_OPTION, level->extra.inv.sound }; break; - case TR::Entity::INV_CONTROLS : desc = { STR_CONTROLS, PAGE_OPTION, level->extra.inv.controls }; break; - case TR::Entity::INV_GAMMA : desc = { STR_GAMMA, PAGE_OPTION, level->extra.inv.gamma }; break; + case TR::Entity::INV_PASSPORT : desc = Desc( STR_GAME, PAGE_OPTION, level->extra.inv.passport ); break; + case TR::Entity::INV_PASSPORT_CLOSED : desc = Desc( STR_GAME, PAGE_OPTION, level->extra.inv.passport_closed ); break; + case TR::Entity::INV_MAP : desc = Desc( STR_MAP, PAGE_INVENTORY, level->extra.inv.map ); break; + case TR::Entity::INV_COMPASS : desc = Desc( STR_COMPASS, PAGE_INVENTORY, level->extra.inv.compass ); break; + case TR::Entity::INV_STOPWATCH : desc = Desc( STR_STOPWATCH, PAGE_INVENTORY, level->extra.inv.stopwatch ); break; + case TR::Entity::INV_HOME : desc = Desc( STR_HOME, PAGE_OPTION, level->extra.inv.home ); break; + case TR::Entity::INV_DETAIL : desc = Desc( STR_DETAIL, PAGE_OPTION, level->extra.inv.detail ); break; + case TR::Entity::INV_SOUND : desc = Desc( STR_SOUND, PAGE_OPTION, level->extra.inv.sound ); break; + case TR::Entity::INV_CONTROLS : desc = Desc( STR_CONTROLS, PAGE_OPTION, level->extra.inv.controls ); break; + case TR::Entity::INV_GAMMA : desc = Desc( STR_GAMMA, PAGE_OPTION, level->extra.inv.gamma ); break; - case TR::Entity::INV_PISTOLS : desc = { STR_PISTOLS, PAGE_INVENTORY, level->extra.inv.weapon[0] }; break; - case TR::Entity::INV_SHOTGUN : desc = { STR_SHOTGUN, PAGE_INVENTORY, level->extra.inv.weapon[1] }; break; - case TR::Entity::INV_MAGNUMS : desc = { STR_MAGNUMS, PAGE_INVENTORY, level->extra.inv.weapon[2] }; break; - case TR::Entity::INV_UZIS : desc = { STR_UZIS, PAGE_INVENTORY, level->extra.inv.weapon[3] }; break; + case TR::Entity::INV_PISTOLS : desc = Desc( STR_PISTOLS, PAGE_INVENTORY, level->extra.inv.weapon[0] ); break; + case TR::Entity::INV_SHOTGUN : desc = Desc( STR_SHOTGUN, PAGE_INVENTORY, level->extra.inv.weapon[1] ); break; + case TR::Entity::INV_MAGNUMS : desc = Desc( STR_MAGNUMS, PAGE_INVENTORY, level->extra.inv.weapon[2] ); break; + case TR::Entity::INV_UZIS : desc = Desc( STR_UZIS, PAGE_INVENTORY, level->extra.inv.weapon[3] ); break; - case TR::Entity::INV_AMMO_PISTOLS : desc = { STR_AMMO_PISTOLS, PAGE_INVENTORY, level->extra.inv.ammo[0] }; break; - case TR::Entity::INV_AMMO_SHOTGUN : desc = { STR_AMMO_SHOTGUN, PAGE_INVENTORY, level->extra.inv.ammo[1] }; break; - case TR::Entity::INV_AMMO_MAGNUMS : desc = { STR_AMMO_MAGNUMS, PAGE_INVENTORY, level->extra.inv.ammo[2] }; break; - case TR::Entity::INV_AMMO_UZIS : desc = { STR_AMMO_UZIS, PAGE_INVENTORY, level->extra.inv.ammo[3] }; break; + case TR::Entity::INV_AMMO_PISTOLS : desc = Desc( STR_AMMO_PISTOLS, PAGE_INVENTORY, level->extra.inv.ammo[0] ); break; + case TR::Entity::INV_AMMO_SHOTGUN : desc = Desc( STR_AMMO_SHOTGUN, PAGE_INVENTORY, level->extra.inv.ammo[1] ); break; + case TR::Entity::INV_AMMO_MAGNUMS : desc = Desc( STR_AMMO_MAGNUMS, PAGE_INVENTORY, level->extra.inv.ammo[2] ); break; + case TR::Entity::INV_AMMO_UZIS : desc = Desc( STR_AMMO_UZIS, PAGE_INVENTORY, level->extra.inv.ammo[3] ); break; - case TR::Entity::INV_MEDIKIT_SMALL : desc = { STR_MEDI_SMALL, PAGE_INVENTORY, level->extra.inv.medikit[0] }; break; - case TR::Entity::INV_MEDIKIT_BIG : desc = { STR_MEDI_BIG, PAGE_INVENTORY, level->extra.inv.medikit[1] }; break; + case TR::Entity::INV_MEDIKIT_SMALL : desc = Desc( STR_MEDI_SMALL, PAGE_INVENTORY, level->extra.inv.medikit[0] ); break; + case TR::Entity::INV_MEDIKIT_BIG : desc = Desc( STR_MEDI_BIG, PAGE_INVENTORY, level->extra.inv.medikit[1] ); break; - case TR::Entity::INV_PUZZLE_1 : desc = { STR_PUZZLE, PAGE_ITEMS, level->extra.inv.puzzle[0] }; break; - case TR::Entity::INV_PUZZLE_2 : desc = { STR_PUZZLE, PAGE_ITEMS, level->extra.inv.puzzle[1] }; break; - case TR::Entity::INV_PUZZLE_3 : desc = { STR_PUZZLE, PAGE_ITEMS, level->extra.inv.puzzle[2] }; break; - case TR::Entity::INV_PUZZLE_4 : desc = { STR_PUZZLE, PAGE_ITEMS, level->extra.inv.puzzle[3] }; break; + case TR::Entity::INV_PUZZLE_1 : desc = Desc( STR_PUZZLE, PAGE_ITEMS, level->extra.inv.puzzle[0] ); break; + case TR::Entity::INV_PUZZLE_2 : desc = Desc( STR_PUZZLE, PAGE_ITEMS, level->extra.inv.puzzle[1] ); break; + case TR::Entity::INV_PUZZLE_3 : desc = Desc( STR_PUZZLE, PAGE_ITEMS, level->extra.inv.puzzle[2] ); break; + case TR::Entity::INV_PUZZLE_4 : desc = Desc( STR_PUZZLE, PAGE_ITEMS, level->extra.inv.puzzle[3] ); break; - case TR::Entity::INV_KEY_1 : desc = { STR_KEY, PAGE_ITEMS, level->extra.inv.key[0] }; break; - case TR::Entity::INV_KEY_2 : desc = { STR_KEY, PAGE_ITEMS, level->extra.inv.key[1] }; break; - case TR::Entity::INV_KEY_3 : desc = { STR_KEY, PAGE_ITEMS, level->extra.inv.key[2] }; break; - case TR::Entity::INV_KEY_4 : desc = { STR_KEY, PAGE_ITEMS, level->extra.inv.key[3] }; break; + case TR::Entity::INV_KEY_1 : desc = Desc( STR_KEY, PAGE_ITEMS, level->extra.inv.key[0] ); break; + case TR::Entity::INV_KEY_2 : desc = Desc( STR_KEY, PAGE_ITEMS, level->extra.inv.key[1] ); break; + case TR::Entity::INV_KEY_3 : desc = Desc( STR_KEY, PAGE_ITEMS, level->extra.inv.key[2] ); break; + case TR::Entity::INV_KEY_4 : desc = Desc( STR_KEY, PAGE_ITEMS, level->extra.inv.key[3] ); break; - case TR::Entity::INV_LEADBAR : desc = { STR_LEAD_BAR, PAGE_ITEMS, level->extra.inv.leadbar }; break; - case TR::Entity::INV_SCION : desc = { STR_SCION, PAGE_ITEMS, level->extra.inv.scion }; break; - default : desc = { STR_UNKNOWN, PAGE_ITEMS, -1 }; break; + case TR::Entity::INV_LEADBAR : desc = Desc( STR_LEAD_BAR, PAGE_ITEMS, level->extra.inv.leadbar ); break; + case TR::Entity::INV_SCION : desc = Desc( STR_SCION, PAGE_ITEMS, level->extra.inv.scion ); break; + default : desc = Desc( STR_UNKNOWN, PAGE_ITEMS, -1 ); break; } if (desc.model > -1 && level->models[desc.model].animation != 0xFFFF) { @@ -142,9 +150,14 @@ struct Inventory { TR::Model &m = level->models[desc.model]; Basis joints[MAX_SPHERES]; - anim->getJoints(basis, -1, true, joints); + mat4 matrix; + matrix.identity(); + matrix.setRot(basis.rot); + matrix.setPos(basis.pos); - Core::active.shader->setParam(uBasis, joints[0], m.mCount); + anim->getJoints(matrix, -1, true, joints); + + Core::setBasis(joints, m.mCount); Core::setBlending(bmNone); mesh->transparent = 0; @@ -775,6 +788,16 @@ struct Inventory { Core::setDepthTest(false); Core::setBlending(bmNone); + #ifdef FFP + mat4 m; + m.identity(); + Core::setViewProj(m, m); + Core::mModel.identity(); + #endif + + #ifdef _PSP + // + #else // vertical blur Core::setTarget(background[1], true); game->setShader(Core::passFilter, Shader::FILTER_BLUR, false, false); @@ -792,9 +815,10 @@ struct Inventory { // grayscale Core::setTarget(background[1], true); game->setShader(Core::passFilter, Shader::FILTER_GRAYSCALE, false, false); - Core::active.shader->setParam(uParam, vec4(1, 0, 0, 0));; + Core::active.shader->setParam(uParam, vec4(1, 0, 0, 0)); background[2]->bind(sDiffuse); game->getMesh()->renderQuad(); + #endif Core::setTarget(NULL, true); @@ -1050,6 +1074,14 @@ struct Inventory { Core::setDepthTest(false); if (background[0]) { + #ifdef FFP + mat4 m; + m.identity(); + Core::setViewProj(m, m); + Core::mModel.identity(); + // Core::mModel.scale(vec3(0.9f)); + #endif + background[0]->bind(sDiffuse); // orignal image if (background[1]) { game->setShader(Core::passFilter, Shader::FILTER_MIXER, false, false); @@ -1091,14 +1123,15 @@ struct Inventory { Core::mView.identity(); Core::mView.translate(vec3(-Core::eye * 8.0f, 0, -1286)); // y = -96 in title - Core::mView.up *= -1.0f; - Core::mView.dir *= -1.0f; + Core::mView.up() *= -1.0f; + Core::mView.dir() *= -1.0f; Core::mViewInv = Core::mView.inverse(); float aspect = float(Core::width) / float(Core::height); Core::mProj = mat4(70.0f, aspect, 32.0f, 2048.0f); - Core::mViewProj = Core::mProj * Core::mView; + Core::setViewProj(Core::mView, Core::mProj); + Core::viewPos = Core::mViewInv.getPos(); Core::whiteTex->bind(sShadow); diff --git a/src/lara.h b/src/lara.h index 3e176fe..827bafb 100644 --- a/src/lara.h +++ b/src/lara.h @@ -179,7 +179,7 @@ struct Lara : Character { STATE_WATER_OUT, STATE_MAX }; - enum : int { + enum { BODY_HIP = 0x0001, BODY_LEG_L1 = 0x0002, BODY_LEG_L2 = 0x0004, @@ -209,22 +209,24 @@ struct Lara : Character { struct Weapon { enum Type { EMPTY = -1, PISTOLS, SHOTGUN, MAGNUMS, UZIS, MAX }; enum State { IS_HIDDEN, IS_ARMED, IS_FIRING }; - enum Anim { NONE, PREPARE, UNHOLSTER, HOLSTER, HOLD, AIM, FIRE }; + struct Anim { + enum Type { NONE, PREPARE, UNHOLSTER, HOLSTER, HOLD, AIM, FIRE }; + }; }; Weapon::Type wpnCurrent; Weapon::Type wpnNext; Weapon::State wpnState; int *wpnAmmo; - vec3 chestOffset; struct Arm { Controller *tracking; // tracking target (main target) Controller *target; // target for shooting float shotTimer; quat rot, rotAbs; - Weapon::Anim anim; - Animation animation; + + Weapon::Anim::Type anim; + Animation animation; Arm() : tracking(NULL), target(NULL) {} } arms[2]; @@ -290,7 +292,7 @@ struct Lara : Character { } Basis getBasis() { - return lara->animation.getJoints(lara->getMatrix(), 14, true); + return lara->getJoint(lara->jointHead); } vec3 getPos() { @@ -338,13 +340,15 @@ struct Lara : Character { #define BRAID_RADIUS 16.0f + lara->updateJoints(); + for (int i = 0; i < model->mCount; i++) { if (!(BODY_BRAID_MASK & (1 << i))) continue; int offset = level->meshOffsets[model->mStart + i]; TR::Mesh *mesh = (TR::Mesh*)&level->meshes[offset]; - vec3 center = lara->animation.getJoints(lara->getMatrix(), i, true) * mesh->center; + vec3 center = lara->joints[i] * mesh->center; float radiusSq = mesh->radius + BRAID_RADIUS; radiusSq *= radiusSq; @@ -397,10 +401,10 @@ struct Lara : Character { vec3 u = d.cross(r).normal(); mat4 m; - m.up = vec4(u, 0.0f); - m.dir = vec4(d, 0.0f); - m.right = vec4(r, 0.0f); - m.offset = vec4(0.0f, 0.0f, 0.0f, 1.0f); + m.up() = vec4(u, 0.0f); + m.dir() = vec4(d, 0.0f); + m.right() = vec4(r, 0.0f); + m.offset() = vec4(0.0f, 0.0f, 0.0f, 1.0f); basis[i].identity(); basis[i].translate(joints[i].pos); @@ -409,13 +413,13 @@ struct Lara : Character { } void render(MeshBuilder *mesh) { - Core::active.shader->setParam(uBasis, basis[0], jointsCount - 1); + Core::setBasis(basis, jointsCount - 1); mesh->renderModel(lara->level->extra.braid); } } *braid; - Lara(IGame *game, int entity) : Character(game, entity, LARA_MAX_HEALTH), dozy(false), wpnCurrent(Weapon::EMPTY), wpnNext(Weapon::EMPTY), chestOffset(pos), braid(NULL) { + Lara(IGame *game, int entity) : Character(game, entity, LARA_MAX_HEALTH), dozy(false), wpnCurrent(Weapon::EMPTY), wpnNext(Weapon::EMPTY), braid(NULL) { if (level->extra.laraSkin > -1) level->entities[entity].modelIndex = level->extra.laraSkin + 1; @@ -516,8 +520,6 @@ struct Lara : Character { } else animation.setAnim(ANIM_STAND); } - - chestOffset = animation.getJoints(getMatrix(), jointChest).pos; } virtual ~Lara() { @@ -621,11 +623,11 @@ struct Lara : Character { TR::Entity::Type getCurrentWeaponInv() { switch (wpnCurrent) { - case Weapon::Type::PISTOLS : return TR::Entity::PISTOLS; - case Weapon::Type::SHOTGUN : return TR::Entity::SHOTGUN; - case Weapon::Type::MAGNUMS : return TR::Entity::MAGNUMS; - case Weapon::Type::UZIS : return TR::Entity::UZIS; - default : return TR::Entity::LARA; + case Weapon::PISTOLS : return TR::Entity::PISTOLS; + case Weapon::SHOTGUN : return TR::Entity::SHOTGUN; + case Weapon::MAGNUMS : return TR::Entity::MAGNUMS; + case Weapon::UZIS : return TR::Entity::UZIS; + default : return TR::Entity::LARA; } } @@ -643,7 +645,7 @@ struct Lara : Character { wpnSetAnim(arms[1], Weapon::IS_HIDDEN, Weapon::Anim::NONE, 0.0f, 0.0f); } - void wpnSetAnim(Arm &arm, Weapon::State wState, Weapon::Anim wAnim, float wAnimTime, float wAnimDir, bool playing = true) { + void wpnSetAnim(Arm &arm, Weapon::State wState, Weapon::Anim::Type wAnim, float wAnimTime, float wAnimDir, bool playing = true) { arm.animation.setAnim(wpnGetAnimIndex(wAnim), 0, wAnim == Weapon::Anim::FIRE); arm.animation.dir = playing ? wAnimDir : 0.0f; @@ -698,8 +700,8 @@ struct Lara : Character { default : ; } - if (wpnState == Weapon::IS_HIDDEN && wState == Weapon::IS_ARMED) game->playSound(TR::SND_UNHOLSTER, pos, Sound::Flags::PAN); - if (wpnState == Weapon::IS_ARMED && wState == Weapon::IS_HIDDEN) game->playSound(TR::SND_HOLSTER, pos, Sound::Flags::PAN); + if (wpnState == Weapon::IS_HIDDEN && wState == Weapon::IS_ARMED) game->playSound(TR::SND_UNHOLSTER, pos, Sound::PAN); + if (wpnState == Weapon::IS_ARMED && wState == Weapon::IS_HIDDEN) game->playSound(TR::SND_HOLSTER, pos, Sound::PAN); // swap layers // 0 - body (full) @@ -823,7 +825,7 @@ struct Lara : Character { wpnHide(); } - int wpnGetAnimIndex(Weapon::Anim wAnim) { + int wpnGetAnimIndex(Weapon::Anim::Type wAnim) { if (wpnCurrent == Weapon::SHOTGUN) { switch (wAnim) { case Weapon::Anim::PREPARE : ASSERT(false); break; // rifle has no prepare animation @@ -874,7 +876,7 @@ struct Lara : Character { // shotgun reload sound if (wpnCurrent == Weapon::SHOTGUN) { if (anim.frameIndex == 10) - game->playSound(TR::SND_SHOTGUN_RELOAD, pos, Sound::Flags::PAN); + game->playSound(TR::SND_SHOTGUN_RELOAD, pos, Sound::PAN); } } } @@ -916,7 +918,7 @@ struct Lara : Character { int joint = wpnCurrent == Weapon::SHOTGUN ? 8 : (i ? 11 : 8); - vec3 p = animation.getJoints(getMatrix(), joint, false).pos; + vec3 p = getJoint(joint).pos; vec3 d = arm->rotAbs * vec3(0, 0, 1); vec3 t = p + d * (24.0f * 1024.0f) + ((vec3(randf(), randf(), randf()) * 2.0f) - vec3(1.0f)) * 1024.0f; @@ -939,13 +941,13 @@ struct Lara : Character { } } - Core::lightPos[1 + armIndex] = animation.getJoints(getMatrix(), armIndex == 0 ? 10 : 13, false).pos; + Core::lightPos[1 + armIndex] = getJoint(armIndex == 0 ? 10 : 13).pos; Core::lightColor[1 + armIndex] = FLASH_LIGHT_COLOR; } if (shots) { - game->playSound(wpnGetSound(), pos, Sound::Flags::PAN); - game->playSound(TR::SND_RICOCHET, nearPos, Sound::Flags::PAN); + game->playSound(wpnGetSound(), pos, Sound::PAN); + game->playSound(TR::SND_RICOCHET, nearPos, Sound::PAN); if (wpnAmmo && *wpnAmmo != UNLIMITED_AMMO && wpnCurrent == Weapon::SHOTGUN) *wpnAmmo -= 1; @@ -1122,6 +1124,7 @@ struct Lara : Character { } animation.overrideMask = overrideMask; + jointsFrame = -1; } virtual void lookAt(Controller *target) { @@ -1164,7 +1167,7 @@ struct Lara : Character { Arm &arm = arms[i]; int j = joints[i]; - if (!aim(arm.target, j, ranges[i], rot, &arm.rotAbs)) { + if (!aim(arm.target, j, ranges[i], rot, &arm.rotAbs)) { arm.target = arms[i^1].target; if (!aim(arm.target, j, ranges[i], rot, &arm.rotAbs)) { rot = quat(0, 0, 0, 1); @@ -1182,6 +1185,7 @@ struct Lara : Character { arm.rot = arm.rot.slerp(rot, speed); animation.overrides[j] = animation.overrides[j].slerp(arm.rot * animation.overrides[j], t); + jointsFrame = -1; } } @@ -1372,8 +1376,8 @@ struct Lara : Character { void doBubbles() { int count = rand() % 3; if (!count) return; - game->playSound(TR::SND_BUBBLE, pos, Sound::Flags::PAN); - vec3 head = animation.getJoints(getMatrix(), 14, true) * vec3(0.0f, 0.0f, 50.0f); + game->playSound(TR::SND_BUBBLE, pos, Sound::PAN); + vec3 head = getJoint(jointHead) * vec3(0.0f, 0.0f, 50.0f); for (int i = 0; i < count; i++) game->addEntity(TR::Entity::BUBBLE, getRoomIndex(), head, 0); } @@ -1436,7 +1440,7 @@ struct Lara : Character { void bakeEnvironment() { flags.invisible = true; if (!environment) - environment = new Texture(256, 256, Texture::RGBA, true, NULL, true, true); + environment = new Texture(256, 256, Texture::RGBA, Texture::CUBEMAP | Texture::MIPMAPS); Core::beginFrame(); game->renderEnvironment(getRoomIndex(), pos - vec3(0.0f, 384.0f, 0.0f), &environment, 0, Core::passCompose); environment->generateMipMap(); @@ -1733,11 +1737,11 @@ struct Lara : Character { if (!limit->alignHoriz) fx = (m.transpose() * vec4(pos - controller->pos, 0.0f)).x; - vec3 targetPos = controller->pos + (m * vec4(fx, limit->dy, limit->dz, 0.0f)).xyz; + vec3 targetPos = controller->pos + (m * vec4(fx, limit->dy, limit->dz, 0.0f)).xyz(); vec3 deltaAbs = pos - targetPos; - vec3 deltaRel = (m.transpose() * vec4(pos - controller->pos, 0.0f)).xyz; // inverse transform + vec3 deltaRel = (m.transpose() * vec4(pos - controller->pos, 0.0f)).xyz(); // inverse transform // set item orientation to hack limits check if (limit->box.contains(deltaRel)) { @@ -1883,7 +1887,7 @@ struct Lara : Character { } bool needFlip = false; - TR::Effect effect = TR::Effect::NONE; + TR::Effect::Type effect = TR::Effect::NONE; int cameraIndex = -1; Controller *cameraTarget = NULL; @@ -2001,7 +2005,7 @@ struct Lara : Character { break; } case TR::Action::EFFECT : - effect = TR::Effect(cmd.args); + effect = TR::Effect::Type(cmd.args); break; case TR::Action::SECRET : if (!(level->state.progress.secrets & (1 << cmd.args))) { @@ -2436,10 +2440,10 @@ struct Lara : Character { if (state == STATE_SURF_TREAD) { if (animation.isFrameActive(0)) - game->waterDrop(animation.getJoints(getMatrix(), jointHead).pos, 96.0f, 0.03f); + game->waterDrop(getJoint(jointHead).pos, 96.0f, 0.03f); } else { if (animation.frameIndex % 4 == 0) - game->waterDrop(animation.getJoints(getMatrix(), jointHead).pos, 96.0f, 0.02f); + game->waterDrop(getJoint(jointHead).pos, 96.0f, 0.02f); } if (input & FORTH) { @@ -2635,7 +2639,7 @@ struct Lara : Character { arms[i].shotTimer += Core::deltaTime; float intensity = clamp((0.1f - arms[i].shotTimer) * 20.0f, EPS, 1.0f); Core::lightColor[1 + i] = FLASH_LIGHT_COLOR * vec4(intensity, intensity, intensity, 1.0f / sqrtf(intensity)); - Core::lightPos[1 + i] = animation.getJoints(getMatrix(), i == 0 ? 10 : 13, false).pos; + Core::lightPos[1 + i] = getJoint(i == 0 ? 10 : 13).pos; } else { Core::lightColor[1 + i] = vec4(0, 0, 0, 1); } @@ -2805,7 +2809,7 @@ struct Lara : Character { } virtual vec3 getPos() { - return level->isCutsceneLevel() ? chestOffset : pos; + return level->isCutsceneLevel() ? getViewPoint() : pos; } bool checkCollisions() { @@ -3100,29 +3104,27 @@ struct Lara : Character { float alpha = min(1.0f, (0.1f - time) * 20.0f); float lum = 3.0f; Basis b(basis); + b.w = 1.0f; b.rotate(quat(vec3(1, 0, 0), -PI * 0.5f)); b.translate(offset); if (level->version & (TR::VER_TR2 | TR::VER_TR3)) lum = alpha; Core::active.shader->setParam(uMaterial, vec4(lum, 0.0f, 0.0f, alpha)); - Core::active.shader->setParam(uBasis, b); + Core::setBasis(&b, 1); mesh->renderModel(level->extra.muzzleFlash); } - virtual void render(Frustum *frustum, MeshBuilder *mesh, Shader::Type type, bool caustics) { // TODO TR3 render in additive pass + virtual void render(Frustum *frustum, MeshBuilder *mesh, Shader::Type type, bool caustics) { uint32 visMask = visibleMask; - if (Core::pass != Core::passShadow && game->getCamera()->firstPerson) // hide head from first person view + if (Core::pass != Core::passShadow && game->getCamera()->firstPerson) // hide head in first person view visibleMask &= ~BODY_HEAD; Controller::render(frustum, mesh, type, caustics); visibleMask = visMask; - chestOffset = animation.getJoints(getMatrix(), jointChest).pos; // TODO: move to update func - if (braid) braid->render(mesh); if (wpnCurrent != Weapon::SHOTGUN && Core::pass != Core::passShadow && (arms[0].shotTimer < MUZZLE_FLASH_TIME || arms[1].shotTimer < MUZZLE_FLASH_TIME)) { - mat4 matrix = getMatrix(); game->setShader(Core::pass, Shader::FLASH, false, true); int meshTransp = mesh->transparent; @@ -3136,8 +3138,8 @@ struct Lara : Character { zOffset = 150; } - renderMuzzleFlash(mesh, animation.getJoints(matrix, 10, true), vec3(-10, -50, zOffset), arms[0].shotTimer); - renderMuzzleFlash(mesh, animation.getJoints(matrix, 13, true), vec3( 10, -50, zOffset), arms[1].shotTimer); + renderMuzzleFlash(mesh, joints[10], vec3(-10, -50, zOffset), arms[0].shotTimer); + renderMuzzleFlash(mesh, joints[13], vec3( 10, -50, zOffset), arms[1].shotTimer); mesh->transparent = meshTransp; switch (mesh->transparent) { diff --git a/src/level.h b/src/level.h index d588145..a04c13f 100644 --- a/src/level.h +++ b/src/level.h @@ -34,7 +34,7 @@ struct Level : IGame { float waterHeight; float clipSign; float clipHeight; - } *params = (Params*)&Core::params; + } *params; ZoneCache *zoneCache; AmbientCache *ambientCache; @@ -47,7 +47,7 @@ struct Level : IGame { bool lastTitle; bool isEnded; - TR::Effect effect; + TR::Effect::Type effect; float effectTimer; int effectIdx; float cutsceneWaitTimer; @@ -235,7 +235,7 @@ struct Level : IGame { if (rebuildMesh) { delete mesh; - mesh = new MeshBuilder(level); + mesh = new MeshBuilder(level, atlas); } if (rebuildAmbient) { @@ -262,6 +262,10 @@ struct Level : IGame { return mesh; } + virtual Texture* getAtlas() { + return atlas; + } + virtual ICamera* getCamera() { return camera; } @@ -332,7 +336,7 @@ struct Level : IGame { } Core::active.shader->setParam(uParam, Core::params); - Core::active.shader->setParam(uMaterial, vec4(diffuse, ambient, specular, alpha)); + Core::setMaterial(diffuse, ambient, specular, alpha); if (Core::settings.detail.shadows > Core::Settings::MEDIUM) Core::active.shader->setParam(uContacts, Core::contacts[0], MAX_CONTACTS); @@ -356,7 +360,7 @@ struct Level : IGame { for (int i = 0; i < 6; i++) { setupCubeCamera(pos, i); Core::pass = pass; - Texture *target = targets[0]->cube ? targets[0] : targets[i * stride]; + Texture *target = (targets[0]->opt & Texture::CUBEMAP) ? targets[0] : targets[i * stride]; Core::setTarget(target, true, i); renderView(roomIndex, false); Core::invalidateTarget(false, true); @@ -364,7 +368,7 @@ struct Level : IGame { Core::pass = tmpPass; } - virtual void setEffect(Controller *controller, TR::Effect effect) { + virtual void setEffect(Controller *controller, TR::Effect::Type effect) { this->effect = effect; this->effectTimer = 0.0f; this->effectIdx = 0; @@ -461,6 +465,7 @@ struct Level : IGame { } virtual Sound::Sample* playSound(int id, const vec3 &pos = vec3(0.0f), int flags = 0) const { + //return NULL; if (level.version == TR::VER_TR1_PSX && id == TR::SND_SECRET) return NULL; @@ -547,15 +552,18 @@ struct Level : IGame { //============================== Level(Stream &stream) : level(stream), inventory(this), lara(NULL), isEnded(false), cutsceneWaitTimer(0.0f) { + #ifdef _PSP + Core::freeEDRAM(); + #endif + params = (Params*)&Core::params; params->time = 0.0f; #ifdef _DEBUG Debug::init(); #endif - - initTextures(); - mesh = new MeshBuilder(level); + initTextures(); + mesh = new MeshBuilder(level, atlas); initOverrides(); for (int i = 0; i < level.entitiesBaseCount; i++) { @@ -918,15 +926,21 @@ struct Level : IGame { } void initTextures() { - if (!level.tilesCount) { - atlas = NULL; - return; - } + ASSERT(level.tilesCount); + + #ifndef SPLIT_BY_TILE + + #ifdef _PSP + #error atlas packing is not allowed for this platform + #endif + + level.initTiles(); + + int texIdx = (level.version & TR::VER_PSX) ? 256 : 0; // skip palette color for PSX version // repack texture tiles Atlas *tiles = new Atlas(level.objectTexturesCount + level.spriteTexturesCount + UI::BAR_MAX, &level, fillCallback); // add textures - int texIdx = (level.version & TR::VER_PSX) ? 256 : 0; // skip palette color for PSX version for (int i = texIdx; i < level.objectTexturesCount; i++) { TR::ObjectTexture &t = level.objectTextures[i]; int16 tx = (t.tile.index % 4) * 256; @@ -955,7 +969,7 @@ struct Level : IGame { tiles->add(uv, texIdx++); } // add common textures - const short2 bar[UI::BAR_MAX] = { {0, 4}, {0, 4}, {0, 4}, {4, 4}, {0, 0} }; + const short2 bar[UI::BAR_MAX] = { short2(0, 4), short2(0, 4), short2(0, 4), short2(4, 4), short2(0, 0) }; for (int i = 0; i < UI::BAR_MAX; i++) tiles->add(short4(i * 32, 4096, i * 32 + bar[i].x, 4096 + bar[i].y), texIdx++); @@ -973,6 +987,57 @@ struct Level : IGame { delete[] level.tiles; level.tiles = NULL; + #else + cube = NULL; + + #ifdef _PSP + atlas = new Texture(level.tiles4, level.tilesCount, level.cluts, level.clutsCount); + #else + level.initTiles(); + + atlas = new Texture(level.tiles, level.tilesCount); + + delete[] level.tiles; + level.tiles = NULL; + #endif + + for (int i = 0; i < level.objectTexturesCount; i++) { + TR::ObjectTexture &t = level.objectTextures[i]; + + t.texCoord[0].x <<= 7; + t.texCoord[0].y <<= 7; + t.texCoord[1].x <<= 7; + t.texCoord[1].y <<= 7; + t.texCoord[2].x <<= 7; + t.texCoord[2].y <<= 7; + t.texCoord[3].x <<= 7; + t.texCoord[3].y <<= 7; + + t.texCoord[0].x += 64; + t.texCoord[0].y += 64; + t.texCoord[1].x += 64; + t.texCoord[1].y += 64; + t.texCoord[2].x += 64; + t.texCoord[2].y += 64; + t.texCoord[3].x += 64; + t.texCoord[3].y += 64; + } + + for (int i = 0; i < level.spriteTexturesCount; i++) { + TR::SpriteTexture &t = level.spriteTextures[i]; + + t.texCoord[0].x <<= 7; + t.texCoord[0].y <<= 7; + t.texCoord[1].x <<= 7; + t.texCoord[1].y <<= 7; + /* + t.texCoord[0].x += 16; + t.texCoord[0].y += 16; + t.texCoord[1].x += 16; + t.texCoord[1].y += 16; + */ + } + #endif } void initOverrides() { @@ -1027,7 +1092,7 @@ struct Level : IGame { void setMainLight(Controller *controller) { Core::lightPos[0] = controller->mainLightPos; - Core::lightColor[0] = vec4(controller->mainLightColor.xyz, 1.0f / controller->mainLightColor.w); + Core::lightColor[0] = vec4(controller->mainLightColor.xyz(), 1.0f / controller->mainLightColor.w); } void renderSky() { @@ -1051,7 +1116,8 @@ struct Level : IGame { Core::setDepthTest(false); setShader(Core::pass, Shader::FLASH, false, false); Core::active.shader->setParam(uMaterial, vec4(0.5f, 0.0f, 0.0f, 0.0f)); - Core::active.shader->setParam(uBasis, b);// anim.getJoints(Basis(quat(0, 0, 0, 1), vec3(0)), 0, false));//Basis(anim.getJointRot(0), vec3(0))); + // anim.getJoints(Basis(quat(0, 0, 0, 1), vec3(0)), 0, false));//Basis(anim.getJointRot(0), vec3(0))); + Core::setBasis(&b, 1); mesh->transparent = 0; mesh->renderModel(level.extra.sky); @@ -1102,6 +1168,8 @@ struct Level : IGame { Basis basis; basis.identity(); + Core::mModel.identity(); + switch (transp) { case 0 : Core::setBlending(bmNone); break; case 1 : Core::setBlending(bmAlpha); break; @@ -1122,7 +1190,7 @@ struct Level : IGame { int roomIndex = roomsList[i]; MeshBuilder::RoomRange &range = mesh->rooms[roomIndex]; - if (!range.geometry[transp].iCount) { + if (!range.geometry[transp].count) { i += dir; continue; } @@ -1134,7 +1202,9 @@ struct Level : IGame { sh->setParam(uLightPos, Core::lightPos[0], MAX_LIGHTS); basis.pos = level.rooms[roomIndex].getOffset(); - sh->setParam(uBasis, basis); + Core::setBasis(&basis, 1); + + Core::mModel.setPos(basis.pos); mesh->transparent = transp; mesh->renderRoomGeometry(roomIndex); @@ -1147,7 +1217,12 @@ struct Level : IGame { if (transp == 1) { Core::setBlending(bmAlpha); - basis.rot = Core::mViewInv.getRot(); + #ifdef MERGE_SPRITES + basis.rot = Core::mViewInv.getRot(); + #else + basis.rot = quat(0, 0, 0, 1); + #endif + for (int i = 0; i < roomsCount; i++) { level.rooms[roomsList[i]].flags.visible = true; @@ -1164,7 +1239,8 @@ struct Level : IGame { sh->setParam(uLightPos, Core::lightPos[0], MAX_LIGHTS); basis.pos = level.rooms[roomIndex].getOffset(); - sh->setParam(uBasis, basis); + Core::setBasis(&basis, 1); + mesh->renderRoomSprites(roomIndex); } } @@ -1178,9 +1254,9 @@ struct Level : IGame { bool isModel = entity.modelIndex > 0; if (isModel) { - if (!mesh->models[entity.modelIndex - 1].geometry[mesh->transparent].iCount) return; + if (!mesh->models[entity.modelIndex - 1].geometry[mesh->transparent].count) return; } else { - if (mesh->sequences[-(entity.modelIndex + 1)].transp != mesh->transparent) return; + if (mesh->sequences[-(entity.modelIndex + 1)].transp != mesh->transparent) return; } Controller *controller = (Controller*)entity.controller; @@ -1209,7 +1285,7 @@ struct Level : IGame { vec3 pos = controller->getPos(); if (ambientCache) { AmbientCache::Cube cube; - if (Core::stats.frame != controller->frameIndex) { + if (Core::stats.frame != controller->jointsFrame) { ambientCache->getAmbient(roomIndex, pos, cube); if (cube.status == AmbientCache::Cube::READY) memcpy(controller->ambient, cube.colors, sizeof(cube.colors)); // store last calculated ambient into controller @@ -1379,7 +1455,7 @@ struct Level : IGame { for (int i = 0; i < level.entitiesCount; i++) { TR::Entity &entity = level.entities[i]; Controller *controller = (Controller*)entity.controller; - if (controller && controller->flags.rendered) + if (controller && controller->flags.rendered && controller->getEntity().castShadow()) controller->renderShadow(mesh); } Core::setBlending(bmNone); @@ -1409,7 +1485,7 @@ struct Level : IGame { p[i] = Core::mViewProj * vec4(vec3(portal.vertices[i]) + room.getOffset(), 1.0f); if (p[i].w > 0.0f) { - p[i].xyz *= (1.0f / p[i].w); + p[i].xyz() *= (1.0f / p[i].w); clipPort.x = min(clipPort.x, p[i].x); clipPort.y = min(clipPort.y, p[i].y); @@ -1568,7 +1644,7 @@ struct Level : IGame { Core::mView = Core::mViewInv.inverse(); Core::mProj = mat4(90, 1.0f, camera->znear, camera->zfar); Core::mViewProj = Core::mProj * Core::mView; - Core::viewPos = Core::mViewInv.offset.xyz; + Core::viewPos = Core::mViewInv.offset().xyz(); } void setupLightCamera() { @@ -1590,7 +1666,7 @@ struct Level : IGame { Core::eye = 0.0f; Core::pass = Core::passShadow; shadow->unbind(sShadow); - bool colorShadow = shadow->format == Texture::Format::RGBA ? true : false; + bool colorShadow = shadow->format == Texture::RGBA ? true : false; if (colorShadow) Core::setClearColor(vec4(1.0f)); Core::setTarget(shadow, true); @@ -1704,9 +1780,9 @@ struct Level : IGame { // Box bbox = lara->getBoundingBox(); // Debug::Draw::box(bbox.min, bbox.max, vec4(1, 0, 1, 1)); - // Core::setBlending(bmAlpha); - // Core::setDepthTest(false); - // Core::validateRenderState(); + Core::setBlending(bmAlpha); + Core::setDepthTest(false); + Core::validateRenderState(); // Debug::Level::rooms(level, lara->pos, lara->getEntity().room); // Debug::Level::lights(level, lara->getRoomIndex(), lara); // Debug::Level::sectors(this, lara->getRoomIndex(), (int)lara->pos.y); @@ -1720,8 +1796,8 @@ struct Level : IGame { // Debug::Level::path(level, (Enemy*)level.entities[86].controller); // Debug::Level::debugOverlaps(level, lara->box); // Debug::Level::debugBoxes(level, lara->dbgBoxes, lara->dbgBoxesCount); - // Core::setDepthTest(true); - // Core::setBlending(bmNone); + Core::setDepthTest(true); + Core::setBlending(bmNone); /* static int dbg_ambient = 0; diff --git a/src/mesh.h b/src/mesh.h index bcd99cd..e3278dd 100644 --- a/src/mesh.h +++ b/src/mesh.h @@ -4,7 +4,6 @@ #include "core.h" #include "format.h" - TR::ObjectTexture barTile[5 /* UI::BAR_MAX */]; TR::ObjectTexture &whiteTile = barTile[4]; // BAR_WHITE @@ -14,8 +13,28 @@ struct MeshRange { int vStart; int aIndex; - MeshRange() : iStart(0), iCount(0), vStart(0), aIndex(-1) {} + uint16 tile; + uint16 clut; + MeshRange() : iStart(0), iCount(0), vStart(0), aIndex(-1), tile(0), clut(0) {} + +#ifdef FFP + #ifdef _PSP + void setup() const { + Core::active.vBuffer += vStart; + } + #else + void setup() const { + VertexGPU *v = (VertexGPU*)NULL + vStart; + glTexCoordPointer (2, GL_SHORT, sizeof(*v), &v->texCoord); + glColorPointer (4, GL_UNSIGNED_BYTE, sizeof(*v), &v->light); + glNormalPointer ( GL_SHORT, sizeof(*v), &v->normal); + glVertexPointer (3, GL_SHORT, sizeof(*v), &v->coord); + } + #endif + + void bind(uint32 *VAO) const {} +#else void setup() const { glEnableVertexAttribArray(aCoord); glEnableVertexAttribArray(aNormal); @@ -24,13 +43,13 @@ struct MeshRange { glEnableVertexAttribArray(aColor); glEnableVertexAttribArray(aLight); - Vertex *v = (Vertex*)NULL + vStart; - glVertexAttribPointer(aCoord, 4, GL_SHORT, false, sizeof(Vertex), &v->coord); - glVertexAttribPointer(aNormal, 4, GL_SHORT, true, sizeof(Vertex), &v->normal); - glVertexAttribPointer(aTexCoord, 4, GL_SHORT, true, sizeof(Vertex), &v->texCoord); - glVertexAttribPointer(aParam, 4, GL_UNSIGNED_BYTE, false, sizeof(Vertex), &v->param); - glVertexAttribPointer(aColor, 4, GL_UNSIGNED_BYTE, true, sizeof(Vertex), &v->color); - glVertexAttribPointer(aLight, 4, GL_UNSIGNED_BYTE, true, sizeof(Vertex), &v->light); + VertexGPU *v = (VertexGPU*)NULL + vStart; + glVertexAttribPointer(aCoord, 4, GL_SHORT, false, sizeof(*v), &v->coord); + glVertexAttribPointer(aNormal, 4, GL_SHORT, true, sizeof(*v), &v->normal); + glVertexAttribPointer(aTexCoord, 4, GL_SHORT, true, sizeof(*v), &v->texCoord); + glVertexAttribPointer(aParam, 4, GL_UNSIGNED_BYTE, false, sizeof(*v), &v->param); + glVertexAttribPointer(aColor, 4, GL_UNSIGNED_BYTE, true, sizeof(*v), &v->color); + glVertexAttribPointer(aLight, 4, GL_UNSIGNED_BYTE, true, sizeof(*v), &v->light); } void bind(GLuint *VAO) const { @@ -38,6 +57,7 @@ struct MeshRange { if (Core::support.VAO && Core::active.VAO != vao) glBindVertexArray(Core::active.VAO = vao); } +#endif }; #define PLANE_DETAIL 48 @@ -46,14 +66,40 @@ struct MeshRange { #define DYN_MESH_QUADS 1024 struct Mesh { - GLuint ID[2]; - GLuint *VAO; + #ifdef _PSP + Index *iBuffer; + VertexGPU *vBuffer; + #else + GLuint ID[2]; + GLuint *VAO; + #endif + int iCount; int vCount; int aCount; int aIndex; + bool cmdBufAlloc; - Mesh(Index *indices, int iCount, Vertex *vertices, int vCount, int aCount) : VAO(NULL), iCount(iCount), vCount(vCount), aCount(aCount), aIndex(0) { + Mesh(int iCount, int vCount) : iCount(iCount), vCount(vCount), aCount(0), aIndex(-1), cmdBufAlloc(true) { + #ifdef _PSP + iBuffer = (Index*)sceGuGetMemory(iCount * sizeof(Index)); + vBuffer = (VertexGPU*)sceGuGetMemory(vCount * sizeof(VertexGPU)); + #endif + } + + Mesh(Index *indices, int iCount, Vertex *vertices, int vCount, int aCount) : iCount(iCount), vCount(vCount), aCount(aCount), aIndex(0), cmdBufAlloc(false) { + #ifdef _PSP + #ifdef EDRAM_MESH + iBuffer = (Index*)Core::allocEDRAM(iCount * sizeof(Index)); + vBuffer = (VertexGPU*)Core::allocEDRAM(vCount * sizeof(VertexGPU)); + #else + iBuffer = new Index[iCount]; + vBuffer = new VertexGPU[vCount]; + #endif + + update(indices, iCount, vertices, vCount); + #else + VAO = NULL; if (Core::support.VAO) glBindVertexArray(Core::active.VAO = 0); @@ -66,9 +112,29 @@ struct Mesh { VAO = new GLuint[aCount]; glGenVertexArrays(aCount, VAO); } + #endif } void update(Index *indices, int iCount, Vertex *vertices, int vCount) { + #ifdef _PSP + if (indices) + memcpy(iBuffer, indices, iCount * sizeof(indices[0])); + + if (vertices) { + Vertex *src = vertices; + VertexGPU *dst = vBuffer; + + for (int i = 0; i < vCount; i++) { + dst->texCoord = short2(src->texCoord.x, src->texCoord.y); + dst->color = ubyte4(src->light.x, src->light.y, src->light.z, 255); //color; + dst->normal = src->normal; + dst->coord = src->coord; + + dst++; + src++; + } + } + #else if (Core::support.VAO) glBindVertexArray(Core::active.VAO = 0); @@ -80,17 +146,28 @@ struct Mesh { glBindBuffer(GL_ARRAY_BUFFER, Core::active.vBuffer = ID[1]); glBufferSubData(GL_ARRAY_BUFFER, 0, vCount * sizeof(Vertex), vertices); } + #endif } virtual ~Mesh() { + #ifdef _PSP + #ifndef ERWAM_MESH + if (!cmdBufAlloc) { + delete[] iBuffer; + delete[] vBuffer; + } + #endif + #else if (VAO) { glDeleteVertexArrays(aCount, VAO); delete[] VAO; } glDeleteBuffers(2, ID); + #endif } void initRange(MeshRange &range) { + #ifndef _PSP if (Core::support.VAO) { ASSERT(aIndex < aCount); range.aIndex = aIndex++; @@ -98,18 +175,26 @@ struct Mesh { bind(true); range.setup(); } else + #endif range.aIndex = -1; } void bind(bool force = false) { + #ifdef _PSP + Core::active.iBuffer = iBuffer; + Core::active.vBuffer = vBuffer; + #else if (force || Core::active.iBuffer != ID[0]) glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, Core::active.iBuffer = ID[0]); if (force || Core::active.vBuffer != ID[1]) glBindBuffer(GL_ARRAY_BUFFER, Core::active.vBuffer = ID[1]); + #endif } void render(const MeshRange &range) { + #ifndef _PSP range.bind(VAO); + #endif if (range.aIndex == -1) { bind(); @@ -156,20 +241,78 @@ struct MeshBuilder { MeshRange dynRange; Mesh *dynMesh; - Mesh *mesh; + Mesh *mesh; + Texture *atlas; + TR::Level *level; + // level + struct Geometry { + int count; + MeshRange ranges[100]; + + Geometry() : count(0) {} + + void finish(int iCount) { + MeshRange *range = count ? &ranges[count - 1] : NULL; + + if (range) { + range->iCount = iCount - range->iStart; + if (!range->iCount) + count--; + } + } + + bool validForTile(uint16 tile, uint16 clut) { + #ifdef SPLIT_BY_TILE + if (!count) return false; + MeshRange &range = ranges[count - 1]; + + return (tile == range.tile + #ifdef SPLIT_BY_CLUT + && clut == range.clut + #endif + ); + #else + return count != 0; + #endif + } + + MeshRange* getNextRange(int vStart, int iCount, uint16 tile, uint16 clut) { + MeshRange *range = count ? &ranges[count - 1] : NULL; + + if (range) + range->iCount = iCount - range->iStart; + + if (!range || range->iCount) { + ASSERT(count < COUNT(ranges)); + range = &ranges[count++]; + } + + range->vStart = vStart; + range->iStart = iCount; + range->tile = tile; + range->clut = clut; + + return range; + } + }; + struct RoomRange { - MeshRange geometry[3]; // opaque, double-side alpha, additive + Geometry geometry[3]; // opaque, double-side alpha, additive MeshRange sprites; int split; } *rooms; + struct ModelRange { - MeshRange geometry[3]; + int parts[3][32]; + Geometry geometry[3]; } *models; + struct SpriteRange { MeshRange sprites; int transp; } *sequences; + // procedured MeshRange shadowBlob; MeshRange quad, circle; @@ -181,7 +324,7 @@ struct MeshBuilder { int animTexRangesCount; int animTexOffsetsCount; - TR::Level *level; + int transparent; enum { BLEND_NONE = 1, @@ -189,7 +332,7 @@ struct MeshBuilder { BLEND_ADD = 4, }; - MeshBuilder(TR::Level &level) : level(&level) { + MeshBuilder(TR::Level &level, Texture *atlas) : atlas(atlas), level(&level) { dynMesh = new Mesh(NULL, DYN_MESH_QUADS * 6, NULL, DYN_MESH_QUADS * 4, 1); dynRange.vStart = 0; dynRange.iStart = 0; @@ -202,6 +345,20 @@ struct MeshBuilder { int iCount = 0, vCount = 0; + // sort room faces by material + for (int i = 0; i < level.roomsCount; i++) { + TR::Room::Data &data = level.rooms[i].data; + sort(data.faces, data.fCount); + // sort room sprites by material + sort(data.sprites, data.sCount); + } + + // sort mesh faces by material + for (int i = 0; i < level.meshesCount; i++) { + TR::Mesh &mesh = level.meshes[i]; + sort(mesh.faces, mesh.fCount); + } + // get size of mesh for rooms (geometry & sprites) int vStartRoom = vCount; @@ -227,8 +384,10 @@ struct MeshBuilder { vCount += mesh.rCount * 4 + mesh.tCount * 3; } + #ifdef MERGE_SPRITES iCount += d.sCount * 6; vCount += d.sCount * 4; + #endif if (vCount - vStartRoom > 0xFFFF) { vStartRoom = vStartCount; @@ -254,8 +413,11 @@ struct MeshBuilder { // get size of mesh for sprite sequences sequences = new SpriteRange[level.spriteSequencesCount]; for (int i = 0; i < level.spriteSequencesCount; i++) { + sequences[i].transp = 1; // alpha blending by default + #ifdef MERGE_SPRITES iCount += level.spriteSequences[i].sCount * 6; vCount += level.spriteSequences[i].sCount * 4; + #endif } // shadow blob mesh (8 triangles, 8 vertices) @@ -271,8 +433,10 @@ struct MeshBuilder { vCount += CIRCLE_SEGS + 1; // detailed plane + #ifdef GENERATE_WATER_PLANE iCount += PLANE_DETAIL * 2 * PLANE_DETAIL * 2 * (2 * 3); vCount += (PLANE_DETAIL * 2 + 1) * (PLANE_DETAIL * 2 + 1); + #endif // make meshes buffer (single vertex buffer object for all geometry & sprites on level) Index *indices = new Index[iCount]; @@ -297,11 +461,10 @@ struct MeshBuilder { for (int transp = 0; transp < 3; transp++) { // opaque, opacity int blendMask = getBlendMask(transp); - range.geometry[transp].vStart = vStartRoom; - range.geometry[transp].iStart = iCount; + Geometry &geom = range.geometry[transp]; // rooms geometry - buildRoom(blendMask, room, level, indices, vertices, iCount, vCount, vStartRoom); + buildRoom(geom, blendMask, room, level, indices, vertices, iCount, vCount, vStartRoom); // static meshes for (int j = 0; j < room.meshesCount; j++) { @@ -314,13 +477,14 @@ struct MeshBuilder { int y = m.y; int z = m.z - room.info.z; int d = m.rotation.value / 0x4000; - buildMesh(blendMask, mesh, level, indices, vertices, iCount, vCount, vStartRoom, 0, x, y, z, d, m.color); + buildMesh(geom, blendMask, mesh, level, indices, vertices, iCount, vCount, vStartRoom, 0, x, y, z, d, m.color); } - range.geometry[transp].iCount = iCount - range.geometry[transp].iStart; + geom.finish(iCount); } // rooms sprites + #ifdef MERGE_SPRITES range.sprites.vStart = vStartRoom; range.sprites.iStart = iCount; for (int j = 0; j < d.sCount; j++) { @@ -331,6 +495,9 @@ struct MeshBuilder { addSprite(indices, vertices, iCount, vCount, vStartRoom, v.vertex.x, v.vertex.y, v.vertex.z, sprite, v.color, v.color); } range.sprites.iCount = iCount - range.sprites.iStart; + #else + range.sprites.iCount = d.sCount * 6; + #endif } ASSERT(vCount - vStartRoom <= 0xFFFF); @@ -338,44 +505,58 @@ struct MeshBuilder { int vStartModel = vCount; aCount++; - TR::Color32 COLOR_WHITE = { 255, 255, 255, 255 }; + TR::Color32 COLOR_WHITE(255, 255, 255, 255); for (int i = 0; i < level.modelsCount; i++) { TR::Model &model = level.models[i]; for (int transp = 0; transp < 3; transp++) { - MeshRange &range = models[i].geometry[transp]; - range.vStart = vStartModel; - range.iStart = iCount; + Geometry &geom = models[i].geometry[transp]; int blendMask = getBlendMask(transp); for (int j = 0; j < model.mCount; j++) { - int index = level.meshOffsets[model.mStart + j]; - if (!index && model.mStart + j > 0) continue; + #ifndef MERGE_MODELS + models[i].parts[transp][j] = geom.count; + #endif - TR::Mesh &mesh = level.meshes[index]; - buildMesh(blendMask, mesh, level, indices, vertices, iCount, vCount, vStartModel, j, 0, 0, 0, 0, COLOR_WHITE); + int index = level.meshOffsets[model.mStart + j]; + if (index || model.mStart + j <= 0) { + TR::Mesh &mesh = level.meshes[index]; + #ifndef MERGE_MODELS + geom.getNextRange(vStartModel, iCount, 0xFFFF, 0xFFFF); + #endif + buildMesh(geom, blendMask, mesh, level, indices, vertices, iCount, vCount, vStartModel, j, 0, 0, 0, 0, COLOR_WHITE); + } + + #ifndef MERGE_MODELS + geom.finish(iCount); + models[i].parts[transp][j] = geom.count - models[i].parts[transp][j]; + #endif } - range.iCount = iCount - range.iStart; + #ifdef MERGE_MODELS + geom.finish(iCount); + models[i].parts[transp][0] = geom.count; + #endif } //int transp = TR::Entity::fixTransp(model.type); if (model.type == TR::Entity::SKY) { ModelRange &m = models[i]; - m.geometry[0].iCount = iCount - models[i].geometry[0].iStart; - m.geometry[1].iCount = 0; - m.geometry[2].iCount = 0; + m.geometry[0].ranges[0].iCount = iCount - models[i].geometry[0].ranges[0].iStart; + m.geometry[1].ranges[0].iCount = 0; + m.geometry[2].ranges[0].iCount = 0; // remove bottom triangles from skybox - if (m.geometry[0].iCount && ((level.version & TR::VER_TR3))) - m.geometry[0].iCount -= 16 * 3; + if (m.geometry[0].ranges[0].iCount && ((level.version & TR::VER_TR3))) + m.geometry[0].ranges[0].iCount -= 16 * 3; } } ASSERT(vCount - vStartModel <= 0xFFFF); // build sprite sequences + #ifdef MERGE_SPRITES int vStartSprite = vCount; aCount++; @@ -388,9 +569,9 @@ struct MeshBuilder { addSprite(indices, vertices, iCount, vCount, vStartSprite, 0, 0, 0, sprite, TR::Color32(255, 255, 255, 255), TR::Color32(255, 255, 255, 255)); } range.iCount = iCount - range.iStart; - sequences[i].transp = 1; // alpha blending by default } ASSERT(vCount - vStartSprite <= 0xFFFF); + #endif // build common primitives int vStartCommon = vCount; @@ -401,14 +582,14 @@ struct MeshBuilder { shadowBlob.iCount = 8 * 3 * 3; for (int i = 0; i < 9; i++) { Vertex &v0 = vertices[vCount + i * 2 + 0]; - v0.normal = { 0, -1, 0, 32767 }; - v0.texCoord = { whiteTile.texCoord[0].x, whiteTile.texCoord[0].y, 32767, 32767 }; - v0.param = { 0, 0, 0, 0 }; - v0.color = { 0, 0, 0, 0 }; - v0.light = { 255, 255, 255, 255 }; + v0.normal = short4( 0, -1, 0, 32767 ); + v0.texCoord = short4( whiteTile.texCoord[0].x, whiteTile.texCoord[0].y, 32767, 32767 ); + v0.param = ubyte4( 0, 0, 0, 0 ); + v0.color = ubyte4( 255, 255, 255, 255 ); + v0.light = ubyte4( 0, 0, 0, 0 ); if (i == 8) { - v0.coord = { 0, 0, 0, 0 }; + v0.coord = short4( 0, 0, 0, 0 ); break; } @@ -419,13 +600,13 @@ struct MeshBuilder { short s0 = short(s * 256.0f); short c1 = short(c * 512.0f); short s1 = short(s * 512.0f); - v0.coord = { c0, 0, s0, 0 }; + v0.coord = short4( c0, 0, s0, 0 ); Vertex &v1 = vertices[vCount + i * 2 + 1]; v1 = v0; - v1.coord = { c1, 0, s1, 0 }; - v1.color = { 255, 255, 255, 0 }; - v1.light = { 255, 255, 255, 255 }; + v1.coord = short4( c1, 0, s1, 0 ); + v1.color = ubyte4( 255, 255, 255, 255 ); + v1.light = ubyte4( 255, 255, 255, 0 ); int idx = iCount + i * 3 * 3; int j = ((i + 1) % 8) * 2; @@ -450,18 +631,28 @@ struct MeshBuilder { quad.iCount = 2 * 3; addQuad(indices, iCount, vCount, vStartCommon, vertices, &whiteTile, false); - vertices[vCount + 3].coord = { -1, -1, 0, 0 }; - vertices[vCount + 2].coord = { 1, -1, 1, 0 }; - vertices[vCount + 1].coord = { 1, 1, 1, 1 }; - vertices[vCount + 0].coord = { -1, 1, 0, 1 }; + vertices[vCount + 3].coord = short4( -1, -1, 0, 0 ); + vertices[vCount + 2].coord = short4( 1, -1, 1, 0 ); + vertices[vCount + 1].coord = short4( 1, 1, 1, 1 ); + vertices[vCount + 0].coord = short4( -1, 1, 0, 1 ); + + vertices[vCount + 3].texCoord = short4( 0, 0, 0, 0 ); + vertices[vCount + 2].texCoord = short4( 32767, 0, 0, 0 ); + vertices[vCount + 1].texCoord = short4( 32767, 32767, 0, 0 ); + vertices[vCount + 0].texCoord = short4( 0, 0, 0, 0 ); + vertices[vCount + 3].light = + vertices[vCount + 2].light = + vertices[vCount + 1].light = + vertices[vCount + 0].light = vertices[vCount + 3].color = ubyte4(255, 255, 255, 255); + for (int i = 0; i < 4; i++) { Vertex &v = vertices[vCount + i]; - v.normal = { 0, 0, 0, 32767 }; - v.color = { 255, 255, 255, 255 }; - v.light = { 255, 255, 255, 255 }; - v.texCoord = { whiteTile.texCoord[0].x, whiteTile.texCoord[0].y, 32767, 32767 }; - v.param = { 0, 0, 0, 0 }; + v.normal = short4( 0, 0, 0, 32767 ); + v.texCoord = short4( whiteTile.texCoord[0].x, whiteTile.texCoord[0].y, 32767, 32767 ); + v.param = ubyte4( 0, 0, 0, 0 ); + v.color = ubyte4( 255, 255, 255, 255 ); + v.light = ubyte4( 255, 255, 255, 255 ); } vCount += 4; @@ -477,22 +668,23 @@ struct MeshBuilder { for (int i = 0; i < CIRCLE_SEGS; i++) { Vertex &v = vertices[vCount + i]; pos.rotate(cs); - v.coord = { short(pos.x), short(pos.y), 0, 0 }; - v.normal = { 0, 0, 0, 32767 }; - v.color = { 255, 255, 255, 255 }; - v.light = { 255, 255, 255, 255 }; - v.texCoord = { whiteTile.texCoord[0].x, whiteTile.texCoord[0].y, 32767, 32767 }; - v.param = { 0, 0, 0, 0 }; + v.coord = short4( short(pos.x), short(pos.y), 0, 0 ); + v.normal = short4( 0, 0, 0, 32767 ); + v.texCoord = short4( whiteTile.texCoord[0].x, whiteTile.texCoord[0].y, 32767, 32767 ); + v.param = ubyte4( 0, 0, 0, 0 ); + v.color = ubyte4( 255, 255, 255, 255 ); + v.light = ubyte4( 255, 255, 255, 255 ); indices[iCount++] = baseIdx + i; indices[iCount++] = baseIdx + (i + 1) % CIRCLE_SEGS; indices[iCount++] = baseIdx + CIRCLE_SEGS; } vertices[vCount + CIRCLE_SEGS] = vertices[vCount]; - vertices[vCount + CIRCLE_SEGS].coord = { 0, 0, 0, 0 }; + vertices[vCount + CIRCLE_SEGS].coord = short4( 0, 0, 0, 0 ); vCount += CIRCLE_SEGS + 1; // plane + #ifdef GENERATE_WATER_PLANE plane.vStart = vStartCommon; plane.iStart = iCount; plane.iCount = PLANE_DETAIL * 2 * PLANE_DETAIL * 2 * (2 * 3); @@ -500,7 +692,7 @@ struct MeshBuilder { baseIdx = vCount - vStartCommon; for (int16 j = -PLANE_DETAIL; j <= PLANE_DETAIL; j++) for (int16 i = -PLANE_DETAIL; i <= PLANE_DETAIL; i++) { - vertices[vCount++].coord = { i, j, 0, 0 }; + vertices[vCount++].coord = short4( i, j, 0, 0 ); if (j < PLANE_DETAIL && i < PLANE_DETAIL) { int idx = baseIdx + (j + PLANE_DETAIL) * (PLANE_DETAIL * 2 + 1) + i + PLANE_DETAIL; indices[iCount + 0] = idx + PLANE_DETAIL * 2 + 1; @@ -513,6 +705,9 @@ struct MeshBuilder { } } ASSERT(vCount - vStartCommon <= 0xFFFF); + #else + plane.iCount = 0; + #endif LOG("MegaMesh (i:%d v:%d a:%d)\n", iCount, vCount, aCount); @@ -529,31 +724,38 @@ struct MeshBuilder { rangeRoom.vStart = 0; mesh->initRange(rangeRoom); for (int i = 0; i < level.roomsCount; i++) { + if (rooms[i].split) { - rangeRoom.vStart = rooms[i].geometry[0].vStart; + ASSERT(rooms[i].geometry[0].count); + rangeRoom.vStart = rooms[i].geometry[0].ranges[0].vStart; mesh->initRange(rangeRoom); } + RoomRange &r = rooms[i]; - r.geometry[0].aIndex = rangeRoom.aIndex; - r.geometry[1].aIndex = rangeRoom.aIndex; - r.geometry[2].aIndex = rangeRoom.aIndex; - r.sprites.aIndex = rangeRoom.aIndex; + for (int j = 0; j < 3; j++) + for (int k = 0; k < r.geometry[j].count; k++) + r.geometry[j].ranges[k].aIndex = rangeRoom.aIndex; + + r.sprites.aIndex = rangeRoom.aIndex; } MeshRange rangeModel; rangeModel.vStart = vStartModel; mesh->initRange(rangeModel); - for (int i = 0; i < level.modelsCount; i++) { - models[i].geometry[0].aIndex = rangeModel.aIndex; - models[i].geometry[1].aIndex = rangeModel.aIndex; - models[i].geometry[2].aIndex = rangeModel.aIndex; - } + for (int i = 0; i < level.modelsCount; i++) + for (int j = 0; j < 3; j++) { + Geometry &geom = models[i].geometry[j]; + for (int k = 0; k < geom.count; k++) + geom.ranges[k].aIndex = rangeModel.aIndex; + } + #ifdef MERGE_SPRITES MeshRange rangeSprite; rangeSprite.vStart = vStartSprite; mesh->initRange(rangeSprite); for (int i = 0; i < level.spriteSequencesCount; i++) sequences[i].sprites.aIndex = rangeSprite.aIndex; + #endif MeshRange rangeCommon; rangeCommon.vStart = vStartCommon; @@ -609,40 +811,9 @@ struct MeshBuilder { void roomRemoveWaterSurfaces(TR::Room &room, int &iCount, int &vCount) { room.waterLevel = -1; - // remove animated water polygons from room geometry - for (int i = 0; i < room.data.rCount; i++) { - TR::Rectangle &f = room.data.rectangles[i]; - if (f.vertices[0] == 0xFFFF) continue; - TR::Vertex &a = room.data.vertices[f.vertices[0]].vertex; - TR::Vertex &b = room.data.vertices[f.vertices[1]].vertex; - TR::Vertex &c = room.data.vertices[f.vertices[2]].vertex; - TR::Vertex &d = room.data.vertices[f.vertices[3]].vertex; - - if (a.y != b.y || a.y != c.y || a.y != d.y) // skip non-horizontal or non-portal plane primitive - continue; - - int sx = (int(a.x) + int(b.x) + int(c.x) + int(d.x)) / 4 / 1024; - int sz = (int(a.z) + int(b.z) + int(c.z) + int(d.z)) / 4 / 1024; - - TR::Room::Sector &s = room.sectors[sx * room.zSectors + sz]; - - int yt = abs(a.y - s.ceiling * 256); - int yb = abs(s.floor * 256 - a.y); - - if (yt > 0 && yb > 0) continue; - - if (isWaterSurface(yt, s.roomAbove, room.flags.water) || - isWaterSurface(yb, s.roomBelow, room.flags.water)) { - f.vertices[0] = 0xFFFF; // mark as unused - room.waterLevel = a.y; - iCount -= 6; - vCount -= 4; - } - } - - for (int i = 0; i < room.data.tCount; i++) { - TR::Triangle &f = room.data.triangles[i]; + for (int i = 0; i < room.data.fCount; i++) { + TR::Face &f = room.data.faces[i]; if (f.vertices[0] == 0xFFFF) continue; TR::Vertex &a = room.data.vertices[f.vertices[0]].vertex; @@ -666,8 +837,13 @@ struct MeshBuilder { isWaterSurface(yb, s.roomBelow, room.flags.water)) { f.vertices[0] = 0xFFFF; // mark as unused room.waterLevel = a.y; - iCount -= 3; - vCount -= 3; + if (f.vCount == 4) { + iCount -= 6; + vCount -= 4; + } else { + iCount -= 3; + vCount -= 3; + } } } } @@ -677,11 +853,11 @@ struct MeshBuilder { return 1 << texAttribute; } - void buildRoom(int blendMask, const TR::Room &room, const TR::Level &level, Index *indices, Vertex *vertices, int &iCount, int &vCount, int vStart) { + void buildRoom(Geometry &geom, int blendMask, const TR::Room &room, const TR::Level &level, Index *indices, Vertex *vertices, int &iCount, int &vCount, int vStart) { const TR::Room::Data &d = room.data; - for (int j = 0; j < d.rCount; j++) { - TR::Rectangle &f = d.rectangles[j]; + for (int j = 0; j < d.fCount; j++) { + TR::Face &f = d.faces[j]; TR::ObjectTexture &t = level.objectTextures[f.flags.texture]; if (f.vertices[0] == 0xFFFF) continue; // skip if marks as unused (removing water planes) @@ -689,58 +865,44 @@ struct MeshBuilder { if (!(blendMask & getBlendMask(t.attribute))) continue; - addQuad(indices, iCount, vCount, vStart, vertices, &t, f.flags.doubleSided, - d.vertices[f.vertices[0]].vertex, - d.vertices[f.vertices[1]].vertex, - d.vertices[f.vertices[2]].vertex, + if (!geom.validForTile(t.tile.index, t.clut)) + geom.getNextRange(vStart, iCount, t.tile.index, t.clut); + + addFace(indices, iCount, vCount, vStart, vertices, f, &t, + d.vertices[f.vertices[0]].vertex, + d.vertices[f.vertices[1]].vertex, + d.vertices[f.vertices[2]].vertex, d.vertices[f.vertices[3]].vertex); TR::Vertex n; CHECK_ROOM_NORMAL(n); - for (int k = 0; k < 4; k++) { + for (int k = 0; k < f.vCount; k++) { TR::Room::Data::Vertex &v = d.vertices[f.vertices[k]]; - vertices[vCount].coord = { v.vertex.x, v.vertex.y, v.vertex.z, 0 }; - vertices[vCount].normal = { n.x, n.y, n.z, int16(t.attribute == 2 ? 0 : 32767) }; - vertices[vCount].color = { 255, 255, 255, 255 }; + vertices[vCount].coord = short4( v.vertex.x, v.vertex.y, v.vertex.z, 0 ); + vertices[vCount].normal = short4( n.x, n.y, n.z, int16(t.attribute == 2 ? 0 : 32767) ); + vertices[vCount].color = ubyte4( 255, 255, 255, 255 ); TR::Color32 c = v.color; - vertices[vCount].light = { c.r, c.g, c.b, 255 }; - vCount++; - } - } - - for (int j = 0; j < d.tCount; j++) { - TR::Triangle &f = d.triangles[j]; - TR::ObjectTexture &t = level.objectTextures[f.flags.texture]; - - if (f.vertices[0] == 0xFFFF) continue; // skip if marks as unused (removing water planes) - - if (!(blendMask & getBlendMask(t.attribute))) - continue; - - addTriangle(indices, iCount, vCount, vStart, vertices, &t, f.flags.doubleSided); - - TR::Vertex n; - CHECK_ROOM_NORMAL(n); - - for (int k = 0; k < 3; k++) { - auto &v = d.vertices[f.vertices[k]]; - vertices[vCount].coord = { v.vertex.x, v.vertex.y, v.vertex.z, 0 }; - vertices[vCount].normal = { n.x, n.y, n.z, int16(t.attribute == 2 ? 0 : 32767) }; - vertices[vCount].color = { 255, 255, 255, 255 }; - TR::Color32 c = v.color; - vertices[vCount].light = { c.r, c.g, c.b, 255 }; + /* + #ifdef FFP + if (room.flags.water) { + vertices[vCount].light = ubyte4( int(c.r * 0.6f), int(c.g * 0.9), int(c.b * 0.9), 255 ); // apply underwater color + } else + vertices[vCount].light = ubyte4( c.r, c.g, c.b, 255 ); + #endif + */ + vertices[vCount].light = ubyte4( c.r, c.g, c.b, 255 ); vCount++; } } } - bool buildMesh(int blendMask, const TR::Mesh &mesh, const TR::Level &level, Index *indices, Vertex *vertices, int &iCount, int &vCount, int vStart, int16 joint, int x, int y, int z, int dir, const TR::Color32 &light) { - TR::Color24 COLOR_WHITE = { 255, 255, 255 }; + bool buildMesh(Geometry &geom, int blendMask, const TR::Mesh &mesh, const TR::Level &level, Index *indices, Vertex *vertices, int &iCount, int &vCount, int vStart, int16 joint, int x, int y, int z, int dir, const TR::Color32 &light) { + TR::Color24 COLOR_WHITE( 255, 255, 255 ); bool isOpaque = true; - for (int j = 0; j < mesh.rCount; j++) { - TR::Rectangle &f = mesh.rectangles[j]; + for (int j = 0; j < mesh.fCount; j++) { + TR::Face &f = mesh.faces[j]; TR::ObjectTexture &t = f.colored ? whiteTile : level.objectTextures[f.flags.texture]; if (t.attribute != 0) @@ -749,49 +911,25 @@ struct MeshBuilder { if (!(blendMask & getBlendMask(t.attribute))) continue; + if (!geom.validForTile(t.tile.index, t.clut)) + geom.getNextRange(vStart, iCount, t.tile.index, t.clut); + TR::Color32 c = f.colored ? level.getColor(f.flags.value) : COLOR_WHITE; - addQuad(indices, iCount, vCount, vStart, vertices, &t, f.flags.doubleSided, - mesh.vertices[f.vertices[0]].coord, - mesh.vertices[f.vertices[1]].coord, - mesh.vertices[f.vertices[2]].coord, + addFace(indices, iCount, vCount, vStart, vertices, f, &t, + mesh.vertices[f.vertices[0]].coord, + mesh.vertices[f.vertices[1]].coord, + mesh.vertices[f.vertices[2]].coord, mesh.vertices[f.vertices[3]].coord); - for (int k = 0; k < 4; k++) { + for (int k = 0; k < f.vCount; k++) { TR::Mesh::Vertex &v = mesh.vertices[f.vertices[k]]; vertices[vCount].coord = transform(v.coord, joint, x, y, z, dir); vertices[vCount].normal = rotate(v.normal, dir); vertices[vCount].normal.w = t.attribute == 2 ? 0 : 32767; - vertices[vCount].color = { c.r, c.g, c.b, 255 }; - vertices[vCount].light = { light.r, light.g, light.b, 255 }; - - vCount++; - } - } - - for (int j = 0; j < mesh.tCount; j++) { - TR::Triangle &f = mesh.triangles[j]; - TR::ObjectTexture &t = f.colored ? whiteTile : level.objectTextures[f.flags.texture]; - - if (t.attribute != 0) - isOpaque = false; - - if (!(blendMask & getBlendMask(t.attribute))) - continue; - - TR::Color32 c = f.colored ? level.getColor(f.flags.value) : COLOR_WHITE; - - addTriangle(indices, iCount, vCount, vStart, vertices, &t, f.flags.doubleSided); - - for (int k = 0; k < 3; k++) { - TR::Mesh::Vertex &v = mesh.vertices[f.vertices[k]]; - - vertices[vCount].coord = transform(v.coord, joint, x, y, z, dir); - vertices[vCount].normal = rotate(v.normal, dir); - vertices[vCount].normal.w = t.attribute == 2 ? 0 : 32767; - vertices[vCount].color = { c.r, c.g, c.b, 255 }; - vertices[vCount].light = { light.r, light.g, light.b, 255 }; + vertices[vCount].color = ubyte4( c.r, c.g, c.b, 255 ); + vertices[vCount].light = ubyte4( light.r, light.g, light.b, 255 ); vCount++; } @@ -868,11 +1006,11 @@ struct MeshBuilder { int count = triangle ? 3 : 4; for (int i = 0; i < count; i++) { Vertex &v = vertices[vCount + i]; - v.texCoord = { tex->texCoord[i].x, tex->texCoord[i].y, 32767, 32767 }; - v.param = { range, frame, 0, 0 }; + v.texCoord = short4( tex->texCoord[i].x, tex->texCoord[i].y, 32767, 32767 ); + v.param = ubyte4( range, frame, 0, 0 ); } - if (((level->version & TR::VER_PSX)) && !triangle) + if (((level->version & TR::VER_PSX)) && !triangle) // TODO: swap vertices instead of rectangle indices and vertices.texCoords (WRONG lighting in TR2!) swap(vertices[vCount + 2].texCoord, vertices[vCount + 3].texCoord); } @@ -960,6 +1098,23 @@ struct MeshBuilder { } } + + void addFace(Index *indices, int &iCount, int &vCount, int vStart, Vertex *vertices, const TR::Face &f, TR::ObjectTexture *tex, const short3 &a, const short3 &b, const short3 &c, const short3 &d) { + if (f.vCount == 4) + addQuad(indices, iCount, vCount, vStart, vertices, tex, f.flags.doubleSided, a, b, c, d); + else + addTriangle(indices, iCount, vCount, vStart, vertices, tex, f.flags.doubleSided); + } + + + short4 coordTransform(const vec3 ¢er, const vec3 &offset) { + mat4 m = Core::mViewInv; + m.setPos(center); + + vec3 coord = m * offset; + return short4(int16(coord.x), int16(coord.y), int16(coord.z), 0); + } + void addSprite(Index *indices, Vertex *vertices, int &iCount, int &vCount, int vStart, int16 x, int16 y, int16 z, const TR::SpriteTexture &sprite, const TR::Color32 &tColor, const TR::Color32 &bColor, bool expand = false) { addQuad(indices, iCount, vCount, vStart, NULL, NULL, false); @@ -977,21 +1132,33 @@ struct MeshBuilder { y0 = y1 = y; } - quad[0].coord = { x0, y0, z, 0 }; - quad[1].coord = { x1, y0, z, 0 }; - quad[2].coord = { x1, y1, z, 0 }; - quad[3].coord = { x0, y1, z, 0 }; + #ifndef MERGE_SPRITES + if (!expand) { + vec3 pos = vec3(float(x), float(y), float(z)); + quad[0].coord = coordTransform(pos, vec3( float(sprite.l), float(-sprite.t), 0 )); + quad[1].coord = coordTransform(pos, vec3( float(sprite.r), float(-sprite.t), 0 )); + quad[2].coord = coordTransform(pos, vec3( float(sprite.r), float(-sprite.b), 0 )); + quad[3].coord = coordTransform(pos, vec3( float(sprite.l), float(-sprite.b), 0 )); + } else + #endif + { + quad[0].coord = short4( x0, y0, z, 0 ); + quad[1].coord = short4( x1, y0, z, 0 ); + quad[2].coord = short4( x1, y1, z, 0 ); + quad[3].coord = short4( x0, y1, z, 0 ); + } - quad[0].normal = quad[1].normal = quad[2].normal = quad[3].normal = { 0, 0, 0, 0 }; - quad[0].color = quad[1].color = { tColor.r, tColor.g, tColor.b, tColor.a }; - quad[2].color = quad[3].color = { bColor.r, bColor.g, bColor.b, bColor.a }; - quad[0].light = quad[1].light = quad[2].light = quad[3].light = { 255, 255, 255, 255 }; - quad[0].param = quad[1].param = quad[2].param = quad[3].param = { 0, 0, 0, 0 }; + quad[0].normal = quad[1].normal = quad[2].normal = quad[3].normal = short4( 0, 0, 0, 0 ); + quad[0].color = quad[1].color = + quad[2].color = quad[3].color = ubyte4( 255, 255, 255, 255 ); + quad[0].light = quad[1].light = ubyte4( tColor.r, tColor.g, tColor.b, tColor.a ); + quad[2].light = quad[3].light = ubyte4( bColor.r, bColor.g, bColor.b, bColor.a ); + quad[0].param = quad[1].param = quad[2].param = quad[3].param = ubyte4( 0, 0, 0, 0 ); - quad[0].texCoord = { sprite.texCoord[0].x, sprite.texCoord[0].y, sprite.l, sprite.t }; - quad[1].texCoord = { sprite.texCoord[1].x, sprite.texCoord[0].y, sprite.r, sprite.t }; - quad[2].texCoord = { sprite.texCoord[1].x, sprite.texCoord[1].y, sprite.r, sprite.b }; - quad[3].texCoord = { sprite.texCoord[0].x, sprite.texCoord[1].y, sprite.l, sprite.b }; + quad[0].texCoord = short4( sprite.texCoord[0].x, sprite.texCoord[0].y, sprite.l, -sprite.t ); + quad[1].texCoord = short4( sprite.texCoord[1].x, sprite.texCoord[0].y, sprite.r, -sprite.t ); + quad[2].texCoord = short4( sprite.texCoord[1].x, sprite.texCoord[1].y, sprite.r, -sprite.b ); + quad[3].texCoord = short4( sprite.texCoord[0].x, sprite.texCoord[1].y, sprite.l, -sprite.b ); vCount += 4; } @@ -1004,14 +1171,14 @@ struct MeshBuilder { int16 maxX = int16(size.x) + minX; int16 maxY = int16(size.y) + minY; - vertices[vCount + 0].coord = { minX, minY, 0, 0 }; - vertices[vCount + 1].coord = { maxX, minY, 0, 0 }; - vertices[vCount + 2].coord = { maxX, maxY, 0, 0 }; - vertices[vCount + 3].coord = { minX, maxY, 0, 0 }; + vertices[vCount + 0].coord = short4( minX, minY, 0, 0 ); + vertices[vCount + 1].coord = short4( maxX, minY, 0, 0 ); + vertices[vCount + 2].coord = short4( maxX, maxY, 0, 0 ); + vertices[vCount + 3].coord = short4( minX, maxY, 0, 0 ); for (int i = 0; i < 4; i++) { Vertex &v = vertices[vCount + i]; - v.normal = { 0, 0, 0, 0 }; + v.normal = short4( 0, 0, 0, 0 ); if (color2 != 0 && (i == 0 || i == 3)) v.color = *((ubyte4*)&color2); else @@ -1019,34 +1186,34 @@ struct MeshBuilder { short2 uv = tile.texCoord[i]; - v.texCoord = { uv.x, uv.y, 32767, 32767 }; - v.param = { 0, 0, 0, 0 }; + v.texCoord = short4( uv.x, uv.y, 32767, 32767 ); + v.param = ubyte4( 0, 0, 0, 0 ); } vCount += 4; } void addFrame(Index *indices, Vertex *vertices, int &iCount, int &vCount, const vec2 &pos, const vec2 &size, uint32 color1, uint32 color2) { - short4 uv = { whiteTile.texCoord[0].x, whiteTile.texCoord[0].y, 32767, 32767 }; + short4 uv = short4( whiteTile.texCoord[0].x, whiteTile.texCoord[0].y, 32767, 32767 ); int16 minX = int16(pos.x); int16 minY = int16(pos.y); int16 maxX = int16(size.x) + minX; int16 maxY = int16(size.y) + minY; - vertices[vCount + 0].coord = { minX, minY, 0, 0 }; - vertices[vCount + 1].coord = { maxX, minY, 0, 0 }; - vertices[vCount + 2].coord = { maxX, int16(minY + 1), 0, 0 }; - vertices[vCount + 3].coord = { minX, int16(minY + 1), 0, 0 }; + vertices[vCount + 0].coord = short4( minX, minY, 0, 0 ); + vertices[vCount + 1].coord = short4( maxX, minY, 0, 0 ); + vertices[vCount + 2].coord = short4( maxX, int16(minY + 1), 0, 0 ); + vertices[vCount + 3].coord = short4( minX, int16(minY + 1), 0, 0 ); - vertices[vCount + 4].coord = { minX, minY, 0, 0 }; - vertices[vCount + 5].coord = { int16(minX + 1), minY, 0, 0 }; - vertices[vCount + 6].coord = { int16(minX + 1), maxY, 0, 0 }; - vertices[vCount + 7].coord = { minX, maxY, 0, 0 }; + vertices[vCount + 4].coord = short4( minX, minY, 0, 0 ); + vertices[vCount + 5].coord = short4( int16(minX + 1), minY, 0, 0 ); + vertices[vCount + 6].coord = short4( int16(minX + 1), maxY, 0, 0 ); + vertices[vCount + 7].coord = short4( minX, maxY, 0, 0 ); for (int i = 0; i < 8; i++) { Vertex &v = vertices[vCount + i]; - v.normal = { 0, 0, 0, 0 }; + v.normal = short4( 0, 0, 0, 0 ); v.color = *((ubyte4*)&color1); v.texCoord = uv; } @@ -1054,19 +1221,19 @@ struct MeshBuilder { addQuad(indices, iCount, vCount, 0, vertices, NULL, false); vCount += 4; addQuad(indices, iCount, vCount, 0, vertices, NULL, false); vCount += 4; - vertices[vCount + 0].coord = { minX, int16(maxY - 1), 0, 0 }; - vertices[vCount + 1].coord = { maxX, int16(maxY - 1), 0, 0 }; - vertices[vCount + 2].coord = { maxX, maxY, 0, 0 }; - vertices[vCount + 3].coord = { minX, maxY, 0, 0 }; + vertices[vCount + 0].coord = short4( minX, int16(maxY - 1), 0, 0 ); + vertices[vCount + 1].coord = short4( maxX, int16(maxY - 1), 0, 0 ); + vertices[vCount + 2].coord = short4( maxX, maxY, 0, 0 ); + vertices[vCount + 3].coord = short4( minX, maxY, 0, 0 ); - vertices[vCount + 4].coord = { int16(maxX - 1), minY, 0, 0 }; - vertices[vCount + 5].coord = { maxX, minY, 0, 0 }; - vertices[vCount + 6].coord = { maxX, maxY, 0, 0 }; - vertices[vCount + 7].coord = { int16(maxX - 1), maxY, 0, 0 }; + vertices[vCount + 4].coord = short4( int16(maxX - 1), minY, 0, 0 ); + vertices[vCount + 5].coord = short4( maxX, minY, 0, 0 ); + vertices[vCount + 6].coord = short4( maxX, maxY, 0, 0 ); + vertices[vCount + 7].coord = short4( int16(maxX - 1), maxY, 0, 0 ); for (int i = 0; i < 8; i++) { Vertex &v = vertices[vCount + i]; - v.normal = { 0, 0, 0, 0 }; + v.normal = short4( 0, 0, 0, 0 ); v.color = *((ubyte4*)&color2); v.texCoord = uv; } @@ -1080,42 +1247,218 @@ struct MeshBuilder { } void renderBuffer(Index *indices, int iCount, Vertex *vertices, int vCount) { - dynRange.iStart = 0; - dynRange.iCount = iCount; + dynRange.iStart = 0; + dynRange.iCount = iCount; + #ifdef _PSP + Mesh cmdBufMesh(iCount, vCount); + Mesh *dynMesh = &cmdBufMesh; + #endif dynMesh->update(indices, iCount, vertices, vCount); dynMesh->render(dynRange); } - int transparent; - void renderRoomGeometry(int roomIndex) { - MeshRange &range = rooms[roomIndex].geometry[transparent]; - if (range.iCount) mesh->render(range); + Geometry &geom = rooms[roomIndex].geometry[transparent]; + for (int i = 0; i < geom.count; i++) { + MeshRange &range = geom.ranges[i]; + + #ifdef SPLIT_BY_TILE + int clutOffset = level->rooms[roomIndex].flags.water ? 512 : 0; + atlas->bind(range.tile, range.clut + clutOffset); + #endif + + mesh->render(range); + } } void renderRoomSprites(int roomIndex) { + #ifndef MERGE_SPRITES + #ifdef SPLIT_BY_TILE + uint16 curTile = 0xFFFF, curClut = 0xFFFF; + #endif + + Core::mModel.identity(); + Core::mModel.setPos(Core::active.basis[0].pos); + + int vCount = 0, iCount = 0; + Index indices[DYN_MESH_QUADS * 6]; + Vertex vertices[DYN_MESH_QUADS * 4]; + + TR::Room::Data &d = level->rooms[roomIndex].data; + 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]; + + #ifdef SPLIT_BY_TILE + if (sprite.tile != curTile + #ifdef SPLIT_BY_CLUT + || sprite.clut != curClut + #endif + ) { + if (iCount) { + renderBuffer(indices, iCount, vertices, vCount); + iCount = 0; + vCount = 0; + } + curTile = sprite.tile; + curClut = sprite.clut; + atlas->bind(curTile, curClut); + } + #endif + + addSprite(indices, vertices, iCount, vCount, 0, v.vertex.x, v.vertex.y, v.vertex.z, sprite, v.color, v.color); + } + + if (iCount) + renderBuffer(indices, iCount, vertices, vCount); + #else mesh->render(rooms[roomIndex].sprites); + #endif } void renderMesh(const MeshRange &range) { mesh->render(range); } - void renderModel(int modelIndex) { - MeshRange &range = models[modelIndex].geometry[transparent]; - if (range.iCount) mesh->render(range); + void renderModel(int modelIndex, bool underwater = false) { + #ifdef FFP + Core::mModel.identity(); + + #ifdef _PSP + //sceGuDisable(GU_TEXTURE_2D); + //Core::setBlending(bmNone); + sceGuEnable(GU_LIGHTING); + + ubyte4 ambient; + ambient.x = ambient.y = ambient.z = clamp(int(Core::active.material.y * 255), 0, 255); + ambient.w = 255; + sceGuAmbient(*(uint32*)&ambient); + + for (int i = 0; i < 1 /*MAX_LIGHTS*/; i++) { + ScePspFVector3 pos; + pos.x = Core::lightPos[i].x; + pos.y = Core::lightPos[i].y; + pos.z = Core::lightPos[i].z; + + sceGuLight(i, GU_POINTLIGHT, GU_DIFFUSE, &pos); + + ubyte4 color; + color.x = clamp(int(Core::lightColor[i].x * 255), 0, 255); + color.y = clamp(int(Core::lightColor[i].y * 255), 0, 255); + color.z = clamp(int(Core::lightColor[i].z * 255), 0, 255); + color.w = 255; + + sceGuLightColor(i, GU_DIFFUSE, *(uint32*)&color); + sceGuLightAtt(i, 1.0f, 0.0f, Core::lightColor[i].w * Core::lightColor[i].w); + } + #else + glEnable(GL_LIGHTING); + glEnable(GL_COLOR_MATERIAL); + + glMatrixMode(GL_MODELVIEW); + glLoadMatrixf((GLfloat*)&Core::mView); + + vec4 ambient(vec3(Core::active.material.y), 1.0f); + glLightModelfv(GL_LIGHT_MODEL_AMBIENT, (GLfloat*)&ambient); + + for (int i = 0; i < 1 /*MAX_LIGHTS*/; i++) { + vec4 pos(Core::lightPos[i].xyz(), 1.0f); + vec4 color(Core::lightColor[i].xyz(), 1.0f); + float att = Core::lightColor[i].w; + att *= att; + + glLightfv(GL_LIGHT0 + i, GL_POSITION, (GLfloat*)&pos); + glLightfv(GL_LIGHT0 + i, GL_DIFFUSE, (GLfloat*)&color); + glLightfv(GL_LIGHT0 + i, GL_QUADRATIC_ATTENUATION, (GLfloat*)&att); + } + #endif + #endif + + ASSERT(level->models[modelIndex].mCount == Core::active.basisCount); + + int part = 0; + + Geometry &geom = models[modelIndex].geometry[transparent]; + for (int i = 0; i < level->models[modelIndex].mCount; i++) { + #ifndef MERGE_MODELS + Basis &basis = Core::active.basis[i]; + if (basis.w == -1.0f) { + part += models[modelIndex].parts[transparent][i]; + continue; + } + #ifdef FFP +// Core::setMatrix(NULL, NULL, &m); + Core::mModel.setRot(basis.rot); + Core::mModel.setPos(basis.pos); + #endif + #endif + + for (int j = 0; j < models[modelIndex].parts[transparent][i]; j++) { + MeshRange &range = geom.ranges[part++]; + + ASSERT(range.iCount); + + #ifdef SPLIT_BY_TILE + int clutOffset = underwater ? 512 : 0; + atlas->bind(range.tile, range.clut + clutOffset); + #endif + + mesh->render(range); + } + } + + #ifdef FFP + #ifdef _PSP + sceGuDisable(GU_LIGHTING); + #else + glDisable(GL_COLOR_MATERIAL); + glDisable(GL_LIGHTING); + #endif + #endif } void renderSprite(int sequenceIndex, int frame) { - MeshRange range = sequences[sequenceIndex].sprites; - range.iCount = 6; - range.iStart += frame * 6; - mesh->render(range); + #ifndef MERGE_SPRITES + Core::mModel.identity(); + Core::mModel.setPos(Core::active.basis[0].pos); + + int vCount = 0, iCount = 0; + Index indices[1 * 6]; + Vertex vertices[1 * 4]; + + TR::SpriteTexture &sprite = level->spriteTextures[level->spriteSequences[sequenceIndex].sStart + frame]; + + uint8 ambient = clamp(int(Core::active.material.y * 255.0f), 0, 255); + uint8 alpha = clamp(int(Core::active.material.w * 255.0f), 0, 255); + + TR::Color32 color(ambient, ambient, ambient, alpha); + addSprite(indices, vertices, iCount, vCount, 0, 0, 0, 0, sprite, color, color); + + #ifdef SPLIT_BY_TILE + atlas->bind(sprite.tile, sprite.clut); + #endif + + renderBuffer(indices, iCount, vertices, vCount); + #else + MeshRange range = sequences[sequenceIndex].sprites; + range.iCount = 6; + range.iStart += frame * 6; + mesh->render(range); + #endif } void renderShadowBlob() { + #ifdef _PSP + sceGuDisable(GU_TEXTURE_2D); + #endif + mesh->render(shadowBlob); + + #ifdef _PSP + sceGuEnable(GU_TEXTURE_2D); + #endif } void renderQuad() { diff --git a/src/platform/psp/ICON0.PNG b/src/platform/psp/ICON0.PNG new file mode 100644 index 0000000..089d00d Binary files /dev/null and b/src/platform/psp/ICON0.PNG differ diff --git a/src/platform/psp/Makefile b/src/platform/psp/Makefile new file mode 100644 index 0000000..a6276c6 --- /dev/null +++ b/src/platform/psp/Makefile @@ -0,0 +1,23 @@ +TARGET = OpenLara +OBJS = main.o + +INCDIR = ../../ +CFLAGS = -G0 -O3 +CXXFLAGS = $(CFLAGS) -fno-exceptions -fno-rtti +ASFLAGS = $(CFLAGS) + +BUILD_PRX = 1 + +LIBDIR = +LDFLAGS = +LIBS = -lpspgum -lpspgu -lpspaudiolib -lpspaudio -lpsprtc -lpspmath -lm -lstdc++ + +COPY_DIR = D:/Games/PSP/memstick/PSP/GAME/OL/ +EXTRA_TARGETS = EBOOT.PBP +PSP_EBOOT_TITLE = OpenLara +PSP_EBOOT_ICON = ICON0.PNG +PSP_EBOOT_PIC1 = PIC1.PNG + +PSPSDK = $(shell psp-config --pspsdk-path) +#USE_PSPSDK_LIBC=1 +include $(PSPSDK)/lib/build.mak \ No newline at end of file diff --git a/src/platform/psp/PIC1.PNG b/src/platform/psp/PIC1.PNG new file mode 100644 index 0000000..0bb5ba7 Binary files /dev/null and b/src/platform/psp/PIC1.PNG differ diff --git a/src/platform/psp/main.cpp b/src/platform/psp/main.cpp new file mode 100644 index 0000000..56c5070 --- /dev/null +++ b/src/platform/psp/main.cpp @@ -0,0 +1,165 @@ +#include +#include +#include + +#include +#include +//#include +#include +#include +#include +#include + +PSP_MODULE_INFO("OpenLara", 0, 1, 1); +PSP_HEAP_SIZE_KB(20480); +PSP_MAIN_THREAD_ATTR(THREAD_ATTR_VFPU); + +//#define printf pspDebugScreenPrintf +//#define printf Kprintf + +#include "game.h" + +#define BUF_WIDTH (512) +#define SCR_WIDTH (480) +#define SCR_HEIGHT (272) + +int exitCallback(int arg1, int arg2, void *common) { + Core::quit(); + return 0; +} + +int callbackThread(SceSize args, void *argp) { + int cbid = sceKernelCreateCallback("Exit Callback", exitCallback, NULL); + sceKernelRegisterExitCallback(cbid); + sceKernelSleepThreadCB(); + return 0; +} + +int setupCallbacks(void) { + int thid = sceKernelCreateThread("update_thread", callbackThread, 0x11, 0xFA0, 0, 0); + sceKernelStartThread(thid, 0, 0); + return thid; +} + +int osStartTime = 0; +int osTimerFreq; + +int osGetTime() { + u64 time; + sceRtcGetCurrentTick(&time); + return int(time * 1000 / osTimerFreq - osStartTime); +} + +bool osSave(const char *name, const void *data, int size) { + return false; +} + +int joyGetPOV(int mask) { + switch (mask) { + case 0b0001 : return 1; + case 0b1001 : return 2; + case 0b1000 : return 3; + case 0b1010 : return 4; + case 0b0010 : return 5; + case 0b0110 : return 6; + case 0b0100 : return 7; + case 0b0101 : return 8; + } + return 0; +} + +void joyInit() { + sceCtrlSetSamplingCycle(0); + sceCtrlSetSamplingMode(PSP_CTRL_MODE_ANALOG); +} + +void joyUpdate() { + SceCtrlData pad; + sceCtrlReadBufferPositive(&pad, 1); + + Input::setDown(ikJoyA, (pad.Buttons & PSP_CTRL_CROSS)); + Input::setDown(ikJoyB, (pad.Buttons & PSP_CTRL_CIRCLE)); + Input::setDown(ikJoyX, (pad.Buttons & PSP_CTRL_SQUARE)); + Input::setDown(ikJoyY, (pad.Buttons & PSP_CTRL_TRIANGLE)); + Input::setDown(ikJoyLB, (pad.Buttons & PSP_CTRL_LTRIGGER)); + Input::setDown(ikJoyRB, (pad.Buttons & PSP_CTRL_RTRIGGER)); + Input::setDown(ikJoyStart, (pad.Buttons & PSP_CTRL_START)); + Input::setDown(ikJoySelect, (pad.Buttons & PSP_CTRL_SELECT)); + + int pov = joyGetPOV( ((pad.Buttons & PSP_CTRL_UP) != 0) | + (((pad.Buttons & PSP_CTRL_DOWN) != 0) << 1) | + (((pad.Buttons & PSP_CTRL_LEFT) != 0) << 2) | + (((pad.Buttons & PSP_CTRL_RIGHT) != 0) << 3)); + Input::setPos(ikJoyPOV, vec2(float(pov), 0.0f)); + + vec2 stick = vec2(float(pad.Lx), float(pad.Ly)) / 128.0f - 1.0f; + if (fabsf(stick.x) < 0.2f && fabsf(stick.y) < 0.2f) + stick = vec2(0.0f); + Input::setPos(ikJoyR, stick); +} + +void sndFill(void* buf, unsigned int length, void *userdata) { + Sound::fill((Sound::Frame*)buf, length); +} + +void sndInit() { + pspAudioInit(); + pspAudioSetChannelCallback(0, sndFill, NULL); +} + +char Stream::cacheDir[255]; +char Stream::contentDir[255]; + +int main() { + //pspDebugScreenInit(); + setupCallbacks(); + + sceGuInit(); + + Core::beginCmdBuf(); + + sceGuDrawBuffer(GU_PSM_5650, (void*)0, BUF_WIDTH); + sceGuDispBuffer(SCR_WIDTH, SCR_HEIGHT, (void*)(BUF_WIDTH * SCR_HEIGHT * 2), BUF_WIDTH); + sceGuDepthBuffer((void*)(BUF_WIDTH * SCR_HEIGHT * 2 * 2), BUF_WIDTH); + + sceGuScissor(0, 0, SCR_WIDTH, SCR_HEIGHT); + sceGuEnable(GU_SCISSOR_TEST); + + sndInit(); + joyInit(); + + osTimerFreq = sceRtcGetTickResolution(); + osStartTime = osGetTime(); + + Game::init("PSXDATA/LEVEL2.PSX"); + + Core::submitCmdBuf(); + + sceDisplayWaitVblankStart(); + sceGuDisplay(GU_TRUE); + + Core::curBackBuffer = 0; + + while (!Core::isQuit) { + //pspDebugScreenSetOffset((int)frameOffset); + //pspDebugScreenSetXY(0, 0); + + Core::beginCmdBuf(); + + joyUpdate(); + Game::update(); + Game::render(); + + Core::submitCmdBuf(); + + //sceDisplayWaitVblankStart(); + Core::curBackBuffer = sceGuSwapBuffers(); + } + + Game::deinit(); + + sceGuTerm(); + sceKernelExitGame(); + + return 0; +} \ No newline at end of file diff --git a/src/platform/win/main.cpp b/src/platform/win/main.cpp index 369935a..c29960b 100644 --- a/src/platform/win/main.cpp +++ b/src/platform/win/main.cpp @@ -365,10 +365,7 @@ int main(int argc, char** argv) { joyInit(); sndInit(hWnd); - char *lvlName = argc > 1 ? argv[1] : NULL; - char *sndName = argc > 2 ? argv[2] : NULL; - - Game::init(lvlName, sndName); + Game::init(argc > 1 ? argv[1] : NULL); SetWindowLong(hWnd, GWL_WNDPROC, (LONG)&WndProc); ShowWindow(hWnd, SW_SHOWDEFAULT); diff --git a/src/shader.h b/src/shader.h index 54ab8df..430c064 100644 --- a/src/shader.h +++ b/src/shader.h @@ -51,17 +51,29 @@ const char *UniformName[uMAX] = { SHADER_UNIFORMS(DECL_STR) }; #undef STR struct Shader { - GLuint ID; - GLint uID[uMAX]; vec4 params[uMAX][4]; - enum Type : GLint { + enum Type { DEFAULT = 0, /* shader */ SPRITE = 0, FLASH = 1, ROOM = 2, ENTITY = 3, MIRROR = 4, /* filter */ FILTER_DOWNSAMPLE = 1, FILTER_GRAYSCALE = 2, FILTER_BLUR = 3, FILTER_MIXER = 4, FILTER_EQUIRECTANGULAR = 5, /* water */ WATER_DROP = 0, WATER_STEP = 1, WATER_CAUSTICS = 2, WATER_MASK = 3, WATER_COMPOSE = 4, MAX = 6 }; +#ifdef FFP + Shader(const char *source, const char *defines = "") {} + virtual ~Shader() {} + bool bind() { return true; } + void setParam(UniformType uType, const int &value, int count = 1) {} + void setParam(UniformType uType, const float &value, int count = 1) {} + void setParam(UniformType uType, const vec2 &value, int count = 1) {} + void setParam(UniformType uType, const vec3 &value, int count = 1) {} + void setParam(UniformType uType, const vec4 &value, int count = 1) {} + void setParam(UniformType uType, const mat4 &value, int count = 1) {} + void setParam(UniformType uType, const Basis &value, int count = 1) {} +#else + uint32 ID; + int32 uID[uMAX]; Shader(const char *source, const char *defines = "") { char fileName[255]; @@ -227,6 +239,7 @@ struct Shader { if (uID[uType] != -1 && checkParam(uType, &value, sizeof(value) * count)) glUniform4fv(uID[uType], count * 2, (GLfloat*)&value); } +#endif }; #endif diff --git a/src/shaders/shader.glsl b/src/shaders/shader.glsl index f78ecd2..73fafb1 100644 --- a/src/shaders/shader.glsl +++ b/src/shaders/shader.glsl @@ -99,8 +99,8 @@ uniform vec4 uMaterial; // x - diffuse, y - ambient, z - specular, w - alpha vec4 coord; coord.w = rBasisPos.w; // visible flag - #ifdef TYPE_SPRITE - coord.xyz = mulBasis(rBasisRot, rBasisPos.xyz + aCoord.xyz, vec3(aTexCoord.z, -aTexCoord.w, 0.0) * 32767.0); + #if defined(TYPE_SPRITE) && defined(ALIGN_SPRITES) + coord.xyz = mulBasis(rBasisRot, rBasisPos.xyz + aCoord.xyz, vec3(aTexCoord.z, aTexCoord.w, 0.0) * 32767.0); #else coord.xyz = mulBasis(rBasisRot, rBasisPos.xyz, aCoord.xyz); #endif diff --git a/src/sound.h b/src/sound.h index d97cfd3..f8ce934 100644 --- a/src/sound.h +++ b/src/sound.h @@ -3,10 +3,12 @@ #define DECODE_VAG #define DECODE_ADPCM -#define DECODE_OGG -#ifndef __EMSCRIPTEN__ - #define DECODE_MP3 +#ifndef _PSP + #define DECODE_OGG + #ifndef __EMSCRIPTEN__ + #define DECODE_MP3 + #endif #endif #include "utils.h" @@ -436,7 +438,7 @@ namespace Sound { uint32 fourcc; stream->read(fourcc); if (fourcc == FOURCC("RIFF")) { // wav - + struct { uint16 format; uint16 channels; @@ -452,7 +454,7 @@ namespace Sound { stream->read(type); stream->read(size); if (type == FOURCC("fmt ")) { - stream->read(waveFmt); + stream->raw(&waveFmt, sizeof(waveFmt)); stream->seek(size - sizeof(waveFmt)); } else if (type == FOURCC("data")) { if (waveFmt.format == 1) decoder = new PCM(stream, waveFmt.channels, waveFmt.samplesPerSec, size, waveFmt.sampleBits); @@ -464,26 +466,28 @@ namespace Sound { stream->seek(size); } } - #ifdef DECODE_OGG else if (fourcc == FOURCC("OggS")) { // ogg stream->seek(-4); - decoder = new OGG(stream, 2); + #ifdef DECODE_OGG + decoder = new OGG(stream, 2); + #endif } - #endif - #ifdef DECODE_MP3 else if (fourcc == FOURCC("ID3\3")) { // mp3 - decoder = new MP3(stream, 2); + #ifdef DECODE_MP3 + decoder = new MP3(stream, 2); + #endif } - #endif - #ifdef DECODE_VAG else { // vag stream->setPos(0); - decoder = new VAG(stream); + #ifdef DECODE_VAG + decoder = new VAG(stream); + #endif } - #endif + + if (!decoder) + delete stream; isPlaying = decoder != NULL; - ASSERT(isPlaying); } ~Sample() { @@ -507,10 +511,10 @@ namespace Sound { if (!(flags & PAN)) return vec2(1.0f); mat4 m = Sound::listener.matrix; - vec3 v = pos - m.offset.xyz; + vec3 v = pos - m.offset().xyz(); float dist = max(0.0f, 1.0f - (v.length() / SND_FADEOFF_DIST)); - float pan = m.right.xyz.dot(v.normal()); + float pan = m.right().xyz().dot(v.normal()); float l = min(1.0f, 1.0f - pan); float r = min(1.0f, 1.0f + pan); @@ -525,7 +529,7 @@ namespace Sound { while (i < count) { int res = decoder->decode(&frames[i], count - i); if (res == 0) { - if (!(flags & Flags::LOOP)) { + if (!(flags & LOOP)) { isPlaying = false; break; } else @@ -536,7 +540,7 @@ namespace Sound { // apply volume #define VOL_CONV(x) (1.0f - sqrtf(1.0f - x * x)); - float m = ((flags & Flags::MUSIC) ? Core::settings.audio.music : Core::settings.audio.sound); + float m = ((flags & MUSIC) ? Core::settings.audio.music : Core::settings.audio.sound); float v = volume * m; vec2 pan = getPan(); vec2 vol = pan * VOL_CONV(v); @@ -693,7 +697,7 @@ namespace Sound { } entity; stream->seek(sizeof(entity) * index); - stream->read(entity); + stream->raw(&entity, sizeof(entity)); stream->setPos(entity.offset); return stream; } @@ -704,8 +708,8 @@ namespace Sound { Stream *openCDAudioMP3(const char *dat, const char *name, int index = -1) { if (!Stream::existsContent(dat) || !Stream::existsContent(name)) return NULL; - // TODO - return NULL; + Stream *stream = new Stream(name); + return stream; } Sample* play(Stream *stream, const vec3 &pos, float volume = 1.0f, float pitch = 0.0f, int flags = 0, int id = - 1) { diff --git a/src/sprite.h b/src/sprite.h index 5d38b55..c0c65d2 100644 --- a/src/sprite.h +++ b/src/sprite.h @@ -53,7 +53,15 @@ struct Sprite : Controller { } virtual void render(Frustum *frustum, MeshBuilder *mesh, Shader::Type type, bool caustics) { - Core::active.shader->setParam(uBasis, Basis(Core::mViewInv.getRot(), pos)); + Basis b; + b.w = 1.0f; + b.pos = pos; + #ifdef MERGE_SPRITES + b.rot = Core::mViewInv.getRot(); + #else + b.rot = quat(0, 0, 0, 1); + #endif + Core::setBasis(&b, 1); mesh->renderSprite(-(getEntity().modelIndex + 1), frame); } }; diff --git a/src/texture.h b/src/texture.h index 5128f89..e714ccd 100644 --- a/src/texture.h +++ b/src/texture.h @@ -2,17 +2,89 @@ #define H_TEXTURE #include "core.h" +#include "format.h" struct Texture { - enum Format : uint32 { LUMINANCE, RGBA, RGB16, RGBA16, RGBA_FLOAT, RGBA_HALF, DEPTH, DEPTH_STENCIL, SHADOW, MAX }; + enum Format { LUMINANCE, RGBA, RGB16, RGBA16, RGBA_FLOAT, RGBA_HALF, DEPTH, DEPTH_STENCIL, SHADOW, MAX }; + enum Option { CUBEMAP = 1, MIPMAPS = 2, NEAREST = 4 }; - GLuint ID; int width, height, origWidth, origHeight; Format format; - bool cube; - bool filter; + uint32 opt; + +#ifdef _PSP + TR::Tile4 *tiles; + TR::CLUT *cluts; + uint8 *memory; + + void swizzle(uint8* out, const uint8* in, uint32 width, uint32 height) { + int rowblocks = width / 16; + + for (int j = 0; j < height; j++) + for (int i = 0; i < width; i++) { + int blockx = i / 16; + int blocky = j / 8; + + int x = i - blockx * 16; + int y = j - blocky * 8; + int block_index = blockx + blocky * rowblocks; + int block_address = block_index * 16 * 8; + + out[block_address + x + y * 16] = in[i + j * width]; + } + } + +#else + uint32 ID; + Texture *tiles[32]; +#endif + + #ifdef SPLIT_BY_TILE + + #ifdef _PSP + Texture(TR::Tile4 *tiles, int tilesCount, TR::CLUT *cluts, int clutsCount) : width(256), height(256), memory(0) { + #ifdef EDRAM_TEX + this->tiles = (TR::Tile4*)Core::allocEDRAM(tilesCount * sizeof(tiles[0])); + this->cluts = (TR::CLUT*)Core::allocEDRAM(clutsCount * sizeof(cluts[0])); + memcpy(this->cluts, cluts, clutsCount * sizeof(cluts[0])); + #ifdef TEX_SWIZZLE + for (int i = 0; i < tilesCount; i++) + swizzle((uint8*)&this->tiles[i], (uint8*)&tiles[i], width / 2, height); + #else + memcpy(this->tiles, tiles, tilesCount * sizeof(tiles[0])); + #endif + #else + this->tiles = tiles; + this->cluts = cluts; + #endif + } + #else + Texture(TR::Tile32 *tiles, int tilesCount) : width(256), height(256), ID(0) { + memset(this->tiles, 0, sizeof(this->tiles)); + + ASSERT(tilesCount < COUNT(this->tiles)); + for (int i = 0; i < tilesCount; i++) + this->tiles[i] = new Texture(width, height, RGBA, 0, tiles + i); + } + #endif + + void bind(uint16 tile, uint16 clut) { + #ifdef _PSP + sceGuClutLoad(1, cluts + clut); + sceGuTexImage(0, width, height, width, tiles + tile); + #else + tiles[tile]->bind(0); + #endif + } + #endif + + Texture(int width, int height, Format format, uint32 opt = 0, void *data = NULL) : opt(opt) { + #ifndef _PSP + #ifdef SPLIT_BY_TILE + memset(this->tiles, 0, sizeof(tiles)); + #endif + #endif - Texture(int width, int height, Format format, bool cube = false, void *data = NULL, bool filter = true, bool mips = false) : cube(cube), filter(filter) { if (!Core::support.texNPOT) { width = nextPow2(width); height = nextPow2(height); @@ -20,10 +92,9 @@ struct Texture { this->width = origWidth = width; this->height = origHeight = height; - glGenTextures(1, &ID); - bind(0); - - GLenum target = cube ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D; + bool filter = (opt & NEAREST) == 0; + bool cube = (opt & CUBEMAP) != 0; + bool mipmaps = (opt & MIPMAPS) != 0; bool isShadow = format == SHADOW; if (format == SHADOW && !Core::support.shadowSampler) { @@ -54,6 +125,14 @@ struct Texture { this->format = format; + #ifdef _PSP + memory = NULL;//new uint8[width * height * 2]; + #else + glGenTextures(1, &ID); + bind(0); + + GLenum target = (opt & CUBEMAP) ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D; + if (format == SHADOW) { glTexParameteri(target, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); glTexParameteri(target, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL); @@ -67,7 +146,7 @@ struct Texture { glTexParameterfv(target, GL_TEXTURE_BORDER_COLOR, color); } - glTexParameteri(target, GL_TEXTURE_MIN_FILTER, filter ? (mips ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR ) : ( mips ? GL_NEAREST_MIPMAP_NEAREST : GL_NEAREST )); + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, filter ? (mipmaps ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR ) : ( mipmaps ? GL_NEAREST_MIPMAP_NEAREST : GL_NEAREST )); glTexParameteri(target, GL_TEXTURE_MAG_FILTER, filter ? GL_LINEAR : GL_NEAREST); struct FormatDesc { @@ -87,83 +166,127 @@ struct Texture { FormatDesc desc = formats[format]; -#ifdef __EMSCRIPTEN__ // fucking firefox! - if (format == RGBA_FLOAT) { - if (Core::support.texFloat) { - desc.ifmt = GL_RGBA; - desc.type = GL_FLOAT; + #ifdef __EMSCRIPTEN__ // fucking firefox! + if (format == RGBA_FLOAT) { + if (Core::support.texFloat) { + desc.ifmt = GL_RGBA; + desc.type = GL_FLOAT; + } } - } - if (format == RGBA_HALF) { - if (Core::support.texHalf) { + if (format == RGBA_HALF) { + if (Core::support.texHalf) { + desc.ifmt = GL_RGBA; + #ifdef MOBILE + desc.type = GL_HALF_FLOAT_OES; + #endif + } + } + #else + if ((format == RGBA_FLOAT && !Core::support.colorFloat) || (format == RGBA_HALF && !Core::support.colorHalf)) { desc.ifmt = GL_RGBA; #ifdef MOBILE - desc.type = GL_HALF_FLOAT_OES; + if (format == RGBA_HALF) + desc.type = GL_HALF_FLOAT_OES; #endif } - } -#else - if ((format == RGBA_FLOAT && !Core::support.colorFloat) || (format == RGBA_HALF && !Core::support.colorHalf)) { - desc.ifmt = GL_RGBA; - #ifdef MOBILE - if (format == RGBA_HALF) - desc.type = GL_HALF_FLOAT_OES; - #endif - } -#endif + #endif for (int i = 0; i < 6; i++) { glTexImage2D(cube ? (GL_TEXTURE_CUBE_MAP_POSITIVE_X + i) : GL_TEXTURE_2D, 0, desc.ifmt, width, height, 0, desc.fmt, desc.type, data); if (!cube) break; } + #endif - if (mips) + if (mipmaps) generateMipMap(); - this->filter = filter; + if (filter) + this->opt &= ~NEAREST; + else + this->opt |= NEAREST; } void generateMipMap() { + #ifdef _PSP + // TODO + #else bind(0); - GLenum target = cube ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D; + GLenum target = (opt & CUBEMAP) ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D; glGenerateMipmap(target); - if (!cube && filter && (Core::support.maxAniso > 0)) + if (!(opt & CUBEMAP) && !(opt & NEAREST) && (Core::support.maxAniso > 0)) glTexParameteri(target, GL_TEXTURE_MAX_ANISOTROPY_EXT, min(int(Core::support.maxAniso), 8)); //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 4); + #endif } virtual ~Texture() { - glDeleteTextures(1, &ID); + #ifdef _PSP + delete[] memory; + #else + #ifdef SPLIT_BY_TILE + if (!ID) { + for (int i = 0; i < COUNT(tiles); i++) + delete[] tiles[i]; + return; + } + #endif + glDeleteTextures(1, &ID); + #endif } void setFilterQuality(Core::Settings::Quality value) { - bool filter = value > Core::Settings::LOW; - bool mips = value > Core::Settings::MEDIUM; + bool filter = value > Core::Settings::LOW; + bool mipmaps = value > Core::Settings::MEDIUM; + #ifdef _PSP + if (filter) + opt &= ~NEAREST; + else + opt |= NEAREST; + + if (mipmaps) + opt |= MIPMAPS; + else + opt &= ~MIPMAPS; + #else Core::active.textures[0] = NULL; bind(0); if (Core::support.maxAniso > 0) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, value > Core::Settings::MEDIUM ? min(int(Core::support.maxAniso), 8) : 1); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter ? (mips ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR ) : ( mips ? GL_NEAREST_MIPMAP_NEAREST : GL_NEAREST )); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter ? (mipmaps ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR ) : ( mipmaps ? GL_NEAREST_MIPMAP_NEAREST : GL_NEAREST )); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter ? GL_LINEAR : GL_NEAREST); + #endif } void bind(int sampler) { + #ifdef _PSP + if (this && !sampler && memory) + sceGuTexImage(0, width, height, width, memory); + #else + #ifdef SPLIT_BY_TILE + if (sampler || !ID) return; + #endif + if (Core::active.textures[sampler] != this) { Core::active.textures[sampler] = this; glActiveTexture(GL_TEXTURE0 + sampler); - glBindTexture(cube ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D, ID); + glBindTexture((opt & CUBEMAP) ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D, ID); } + #endif } void unbind(int sampler) { + #ifdef _PSP + // + #else if (Core::active.textures[sampler]) { Core::active.textures[sampler] = NULL; glActiveTexture(GL_TEXTURE0 + sampler); - glBindTexture(cube ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D, 0); + glBindTexture((opt & CUBEMAP) ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D, 0); } + #endif } struct Color24 { @@ -201,10 +324,14 @@ struct Texture { strcat(buf, ".bmp"); FILE *f = fopen(buf, "wb"); - fwrite(&bmfh, sizeof(bmfh), 1, f); - fwrite(&BMIH, sizeof(BMIH), 1, f); - fwrite(data32, width * height * 4, 1, f); - fclose(f); + if (f) { + fwrite(&bmfh, sizeof(bmfh), 1, f); + fwrite(&BMIH, sizeof(BMIH), 1, f); + int res = fwrite(data32, width * height * 4, 1, f); + LOG("Res %d\n", res); + fclose(f); + } else + ASSERT(false); } */ static uint8* LoadPCX(Stream &stream, uint32 &width, uint32 &height) { @@ -669,7 +796,7 @@ struct Texture { ((uint32*)data)[y * dw] = ((uint32*)data)[y * dw + dw - 1] = 0xFF000000; } - Texture *tex = new Texture(dw, dh, Texture::RGBA, false, data); + Texture *tex = new Texture(dw, dh, Texture::RGBA, 0, data); tex->origWidth = width; tex->origHeight = height; @@ -829,8 +956,7 @@ struct Atlas { fill(root, data); fillInstances(); - Texture *atlas = new Texture(width, height, Texture::RGBA, false, data, true, true); - + Texture *atlas = new Texture(width, height, Texture::RGBA, Texture::MIPMAPS, data); delete[] data; return atlas; }; diff --git a/src/trigger.h b/src/trigger.h index 14f0bf7..9a521cd 100644 --- a/src/trigger.h +++ b/src/trigger.h @@ -107,7 +107,7 @@ struct TrapDartEmitter : Controller { game->addEntity(TR::Entity::DART, getRoomIndex(), p, angle.y); if (level->extra.smoke != -1) game->addEntity(TR::Entity::SMOKE, getRoomIndex(), p); - game->playSound(TR::SND_DART, p, Sound::Flags::PAN); + game->playSound(TR::SND_DART, p, Sound::PAN); } updateAnimation(true); @@ -160,7 +160,7 @@ struct Flame : Sprite { return; } - pos = lara->animation.getJoints(lara->getMatrix(), jointIndex).pos; + pos = lara->getJoint(jointIndex).pos; if (jointIndex == 0) pos.y += 100.0f; @@ -321,6 +321,7 @@ struct TrapBoulder : Controller { if (onGround) { pos = p; deactivate(true); + game->checkTrigger(this, true); return; } else { pos.x = p.x; @@ -528,7 +529,7 @@ struct Door : Controller { sectors[0] = level->getSector(roomIndex[0], x, z, sectorIndex[0]); // behind - roomIndex[1] = level->getNextRoom(sectors[0].floorIndex); + roomIndex[1] = level->getNextRoom(§ors[0]); if (roomIndex[1] == TR::NO_ROOM) return; @@ -698,7 +699,7 @@ struct Crystal : Controller { Texture *environment; Crystal(IGame *game, int entity) : Controller(game, entity) { - environment = new Texture(64, 64, Texture::RGBA, true, NULL, true, true); + environment = new Texture(64, 64, Texture::RGBA, Texture::CUBEMAP | Texture::MIPMAPS); activate(); } @@ -713,7 +714,7 @@ struct Crystal : Controller { virtual void update() { updateAnimation(false); - vec3 lightPos = animation.getJoints(getMatrix(), 0, false).pos - vec3(0, 256, 0); + vec3 lightPos = getJoint(0).pos - vec3(0, 256, 0); getRoom().addDynLight(entity, vec4(lightPos, 0.0f), CRYSTAL_LIGHT_COLOR); } @@ -740,7 +741,7 @@ struct CrystalPickup : Controller { virtual void update() { updateAnimation(false); - vec3 lightPos = animation.getJoints(getMatrix(), 0, false).pos; + vec3 lightPos = getJoint(0).pos; getRoom().addDynLight(entity, vec4(lightPos, 0.0f), CRYSTAL_PICKUP_LIGHT_COLOR); } }; @@ -1001,7 +1002,7 @@ struct Lightning : Controller { } else if (!hasTargets) { target = pos + vec3(0.0f, 1024.0f, 0.0f); } else - target = animation.getJoints(getMatrix(), 1 + int(randf() * 5)).pos; + target = getJoint(1 + int(randf() * 5)).pos; } game->playSound(TR::SND_LIGHTNING, pos, Sound::PAN); } @@ -1028,10 +1029,10 @@ struct Lightning : Controller { void setVertex(Vertex &v, const vec3 &coord, int16 joint, int idx) { v.coord = toCoord(coord, joint); - v.normal = { 0, -1, 0, 0 }; - v.texCoord = { barTile[0].texCoord[idx].x, barTile[0].texCoord[idx].y, 32767, 32767 }; - v.param = { 0, 0, 0, 0 }; - v.color = { 255, 255, 255, 255 }; + v.normal = short4( 0, -1, 0, 0 ); + v.texCoord = short4( barTile[0].texCoord[idx].x, barTile[0].texCoord[idx].y, 32767, 32767 ); + v.param = ubyte4( 0, 0, 0, 0 ); + v.color = ubyte4( 255, 255, 255, 255 ); } void renderPolyline(const vec3 &start, const vec3 &end, float width, float spread, int depth) { @@ -1061,7 +1062,7 @@ struct Lightning : Controller { ASSERT(vCount == count * 2); // build vertices - vec3 dir = Core::mViewInv.dir.xyz; + vec3 dir = Core::mViewInv.dir().xyz(); vCount = 0; vec3 n; @@ -1096,7 +1097,7 @@ struct Lightning : Controller { if (!armed) target = game->getLara()->pos; - Basis b = animation.getJoints(getMatrix(), 0); + Basis b = getJoint(0); b.rot = quat(0, 0, 0, 1); game->setShader(Core::pass, Shader::FLASH, false, false); @@ -1166,7 +1167,7 @@ struct TrapLava : Controller { vec3 dir = getDir(); roomIndex = getRoomIndex(); - TR::Room::Sector *s = level->getSector(roomIndex, int(pos.x + dir.x * 2048.0f), int(pos.y), int(pos.z + dir.z * 2048.0f)); + TR::Room::Sector *s = level->getSector(roomIndex, pos + dir * 2048.0f); if (!s || s->floor * 256 != int(pos.y)) return; @@ -1469,7 +1470,7 @@ struct StoneItem : Controller { float s = 0.3f + (sinf(phase * PI2) * 0.5f + 0.5f) * 0.7f; vec4 lightColor(0.1f * s, 1.0f * s, 1.0f * s, 1.0f / STONE_ITEM_LIGHT_RADIUS); - vec3 lightPos = animation.getJoints(getMatrix(), 0, false).pos; + vec3 lightPos = getJoint(0).pos; getRoom().addDynLight(entity, vec4(lightPos, 0.0f), lightColor); } diff --git a/src/ui.h b/src/ui.h index cdab125..22e6c16 100644 --- a/src/ui.h +++ b/src/ui.h @@ -164,9 +164,10 @@ namespace UI { bool showHelp; const static uint8 char_width[110] = { - 14, 11, 11, 11, 11, 11, 11, 13, 8, 11, 12, 11, 13, 13, 12, 11, 12, 12, 11, 12, 13, 13, 13, 12, - 12, 11, 9, 9, 9, 9, 9, 9, 9, 9, 5, 9, 9, 5, 12, 10, 9, 9, 9, 8, 9, 8, 9, 9, 11, 9, 9, 9, 12, 8, - 10, 10, 10, 10, 10, 9, 10, 10, 5, 5, 5, 11, 9, 10, 8, 6, 6, 7, 7, 3, 11, 8, 13, 16, 9, 4, 12, 12, + 14, 11, 11, 11, 11, 11, 11, 13, 8, 11, 12, 11, 13, 13, 12, 11, 12, 12, 11, 12, 13, 13, 13, 12, 12, 11, // A-Z + 9, 9, 9, 9, 9, 9, 9, 9, 5, 9, 9, 5, 12, 10, 9, 9, 9, 8, 9, 8, 9, 9, 11, 9, 9, 9, // a-z + 12, 8, 10, 10, 10, 10, 10, 9, 10, 10, // 0-9 + 5, 5, 5, 11, 9, 10, 8, 6, 6, 7, 7, 3, 11, 8, 13, 16, 9, 4, 12, 12, 7, 5, 7, 7, 7, 7, 7, 7, 7, 7, 16, 14, 14, 14, 16, 16, 16, 16, 16, 12, 14, 8, 8, 8, 8, 8, 8, 8 }; static const uint8 char_map[102] = { @@ -216,13 +217,17 @@ namespace UI { BAR_MAX, }; - struct { + struct Buffer { Vertex vertices[MAX_CHARS * 4]; Index indices[MAX_CHARS * 6]; int iCount; int vCount; } buffer; + #ifdef SPLIT_BY_TILE + uint16 curTile, curClut; + #endif + void begin() { Core::setDepthTest(false); Core::setBlending(bmAlpha); @@ -231,17 +236,30 @@ namespace UI { float aspect = float(Core::width) / float(Core::height); width = 480 * aspect; - Core::mViewProj = mat4(0.0f, width, 480, 0.0f, 0.0f, 1.0f); + + Core::mProj = mat4(0.0f, width, 480, 0.0f, 0.0f, 1.0f); + Core::mView.identity(); + Core::mModel.identity(); + + Core::setViewProj(Core::mView, Core::mProj); game->setShader(Core::passGUI, Shader::DEFAULT); - Core::active.shader->setParam(uMaterial, vec4(1)); + Core::setMaterial(1, 1, 1, 1); Core::active.shader->setParam(uPosScale, vec4(0, 0, 1, 1)); buffer.iCount = buffer.vCount = 0; + + #ifdef SPLIT_BY_TILE + curTile = curClut = 0xFFFF; + #endif } void flush() { if (buffer.iCount > 0) { + #ifdef SPLIT_BY_TILE + if (curTile != 0xFFFF) + game->getAtlas()->bind(curTile, curClut); + #endif game->getMesh()->renderBuffer(buffer.indices, buffer.iCount, buffer.vertices, buffer.vCount); buffer.iCount = buffer.vCount = 0; } @@ -319,6 +337,19 @@ namespace UI { } } + #ifdef SPLIT_BY_TILE + if (sprite.tile != curTile + #ifdef SPLIT_BY_CLUT + || sprite.clut != curClut + #endif + ) { + flush(); + curTile = sprite.tile; + curClut = sprite.clut; + } + #endif + + mesh->addSprite(buffer.indices, buffer.vertices, buffer.iCount, buffer.vCount, 0, x, y, 0, sprite, tColor, bColor, true); x += char_width[frame] + 1; @@ -339,6 +370,19 @@ namespace UI { flush(); TR::SpriteTexture &sprite = level->spriteTextures[level->spriteSequences[seq].sStart + specChar]; + + #ifdef SPLIT_BY_TILE + if (sprite.tile != curTile + #ifdef SPLIT_BY_CLUT + || sprite.clut != curClut + #endif + ) { + flush(); + curTile = sprite.tile; + curClut = sprite.clut; + } + #endif + mesh->addSprite(buffer.indices, buffer.vertices, buffer.iCount, buffer.vCount, 0, int(pos.x), int(pos.y), 0, sprite, TR::Color32(255, 255, 255, 255), TR::Color32(255, 255, 255, 255), true); } diff --git a/src/utils.h b/src/utils.h index 1d570ba..8e757d1 100644 --- a/src/utils.h +++ b/src/utils.h @@ -30,11 +30,32 @@ #endif #ifdef ANDROID - #include - #undef LOG - #define LOG(...) __android_log_print(ANDROID_LOG_INFO,"OpenLara",__VA_ARGS__) + #include + #undef LOG + #define LOG(...) __android_log_print(ANDROID_LOG_INFO,"OpenLara",__VA_ARGS__) #endif +#ifdef _PSP + extern "C" { + // pspmath.h + extern float vfpu_sinf(float x); + extern float vfpu_cosf(float x); + extern float vfpu_atan2f(float x, float y); + extern void vfpu_sincos(float r, float *s, float *c); + } + + #define sinf(x) vfpu_sinf(x) + #define cosf(x) vfpu_cosf(x) + #define atan2f(x, y) vfpu_atan2f(x, y) + #define sincos(a, s, c) vfpu_sincos(a, s, c) +#else + void sincos(float r, float *s, float *c) { + *s = sinf(r); + *c = cosf(r); + } +#endif + + extern int osGetTime(); extern bool osSave(const char *name, const void *data, int size); @@ -167,6 +188,30 @@ uint32 fnv32(const char *data, int32 size, uint32 hash = 0x811c9dc5) { return hash; } +template +void qsort(T* v, int L, int R) { + int i = L; + int j = R; + const T m = v[(L + R) / 2]; + + while (i <= j) { + while (T::cmp(v[i], m) < 0) i++; + while (T::cmp(m, v[j]) < 0) j--; + + if (i <= j) + swap(v[i++], v[j--]); + } + + if (L < j) qsort(v, L, j); + if (i < R) qsort(v, i, R); +} + +template +void sort(T *items, int count) { + if (count) + qsort(items, 0, count - 1); +} + struct vec2 { float x, y; vec2() {} @@ -210,14 +255,11 @@ struct vec2 { vec2 normal() const { float s = length(); return s == 0.0 ? (*this) : (*this)*(1.0f/s); } float angle() const { return atan2f(y, x); } vec2& rotate(const vec2 &cs) { *this = vec2(x*cs.x - y*cs.y, x*cs.y + y*cs.x); return *this; } - vec2& rotate(float angle) { return rotate(vec2(cosf(angle), sinf(angle))); } + vec2& rotate(float angle) { vec2 cs; sincos(angle, &cs.y, &cs.x); return rotate(cs); } }; struct vec3 { - union { - struct { float x, y, z; }; - struct { vec2 xy; }; - }; + float x, y, z; vec3() {} vec3(float s) : x(s), y(s), z(s) {} @@ -225,6 +267,9 @@ struct vec3 { vec3(const vec2 &xy, float z = 0.0f) : x(xy.x), y(xy.y), z(z) {} vec3(float lng, float lat) : x(sinf(lat) * cosf(lng)), y(-sinf(lng)), z(cosf(lat) * cosf(lng)) {} + vec2& xy() const { return *((vec2*)&x); } + vec2& yz() const { return *((vec2*)&y); } + inline float& operator [] (int index) const { ASSERT(index >= 0 && index <= 2); return ((float*)this)[index]; } inline bool operator == (const vec3 &v) const { return x == v.x && y == v.y && z == v.z; } @@ -273,7 +318,8 @@ struct vec3 { } vec3 rotateY(float angle) const { - float s = sinf(angle), c = cosf(angle); + float s, c; + sincos(angle, &s, &c); return vec3(x*c - z*s, y, x*s + z*c); } @@ -285,11 +331,9 @@ struct vec3 { }; struct vec4 { - union { - struct { float x, y, z, w; }; - struct { vec2 xy, zw; }; - struct { vec3 xyz; }; - }; + float x, y, z, w; + + vec3& xyz() const { return *((vec3*)&x); } vec4() {} vec4(float s) : x(s), y(s), z(s), w(s) {} @@ -314,21 +358,20 @@ struct vec4 { }; struct quat { - union { - struct { float x, y, z, w; }; - struct { vec3 xyz; }; - }; + float x, y, z, w; + + vec3& xyz() const { return *((vec3*)&x); } quat() {} quat(float x, float y, float z, float w) : x(x), y(y), z(z), w(w) {} quat(const vec3 &axis, float angle) { - angle *= 0.5f; - float s = sinf(angle); + float s, c; + sincos(angle * 0.5f, &s, &c); x = axis.x * s; y = axis.y * s; z = axis.z * s; - w = cosf(angle); + w = c; } quat operator - () const { @@ -355,8 +398,8 @@ struct quat { } vec3 operator * (const vec3 &v) const { - // return v + xyz.cross(xyz.cross(v) + v * w) * 2.0f; - return (*this * quat(v.x, v.y, v.z, 0) * inverse()).xyz; + //return v + xyz.cross(xyz.cross(v) + v * w) * 2.0f; + return (*this * quat(v.x, v.y, v.z, 0) * inverse()).xyz(); } float dot(const quat &q) const { @@ -424,16 +467,15 @@ struct quat { }; struct mat4 { + float e00, e10, e20, e30, + e01, e11, e21, e31, + e02, e12, e22, e32, + e03, e13, e23, e33; - union { - struct { - float e00, e10, e20, e30, - e01, e11, e21, e31, - e02, e12, e22, e32, - e03, e13, e23, e33; - }; - struct { vec4 right, up, dir, offset; }; - }; + vec4& right() const { return *((vec4*)&e00); } + vec4& up() const { return *((vec4*)&e01); } + vec4& dir() const { return *((vec4*)&e02); } + vec4& offset() const { return *((vec4*)&e03); } mat4() {} @@ -485,10 +527,10 @@ struct mat4 { 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); + this->right() = vec4(r, 0.0f); + this->up() = vec4(u, 0.0f); + this->dir() = vec4(d, 0.0f); + this->offset() = vec4(from, 1.0f); } mat4(const vec4 &reflectPlane) { @@ -497,10 +539,10 @@ struct mat4 { c = reflectPlane.z, d = reflectPlane.w; - right = vec4(1 - 2*a*a, - 2*b*a, - 2*c*a, 0); - up = vec4( - 2*a*b, 1 - 2*b*b, - 2*c*b, 0); - dir = vec4( - 2*a*c, - 2*b*c, 1 - 2*c*c, 0); - offset = vec4( - 2*a*d, - 2*b*d, - 2*c*d, 1); + right() = vec4(1 - 2*a*a, - 2*b*a, - 2*c*a, 0); + up() = vec4( - 2*a*b, 1 - 2*b*b, - 2*c*b, 0); + dir() = vec4( - 2*a*c, - 2*b*c, 1 - 2*c*c, 0); + offset() = vec4( - 2*a*d, - 2*b*d, - 2*c*d, 1); } @@ -564,7 +606,8 @@ struct mat4 { void rotateX(float angle) { mat4 m; m.identity(); - float s = sinf(angle), c = cosf(angle); + float s, c; + sincos(angle, &s, &c); m.e11 = c; m.e21 = s; m.e12 = -s; m.e22 = c; *this = *this * m; @@ -573,7 +616,8 @@ struct mat4 { void rotateY(float angle) { mat4 m; m.identity(); - float s = sinf(angle), c = cosf(angle); + float s, c; + sincos(angle, &s, &c); m.e00 = c; m.e20 = -s; m.e02 = s; m.e22 = c; *this = *this * m; @@ -582,12 +626,91 @@ struct mat4 { void rotateZ(float angle) { mat4 m; m.identity(); - float s = sinf(angle), c = cosf(angle); + float s, c; + sincos(angle, &s, &c); m.e00 = c; m.e01 = -s; m.e10 = s; m.e11 = c; *this = *this * m; } + void rotateYXZ(const vec3 &angle) { + float s, c, a, b; + + if (angle.y != 0.0f) { + sincos(angle.y, &s, &c); + + a = e00 * c - e02 * s; + b = e02 * c + e00 * s; + e00 = a; + e02 = b; + + a = e10 * c - e12 * s; + b = e12 * c + e10 * s; + e10 = a; + e12 = b; + + a = e20 * c - e22 * s; + b = e22 * c + e20 * s; + e20 = a; + e22 = b; + } + + if (angle.x != 0.0f) { + sincos(angle.x, &s, &c); + + a = e01 * c + e02 * s; + b = e02 * c - e01 * s; + e01 = a; + e02 = b; + + a = e11 * c + e12 * s; + b = e12 * c - e11 * s; + e11 = a; + e12 = b; + + a = e21 * c + e22 * s; + b = e22 * c - e21 * s; + e21 = a; + e22 = b; + } + + if (angle.z != 0.0f) { + sincos(angle.z, &s, &c); + + a = e00 * c + e01 * s; + b = e01 * c - e00 * s; + e00 = a; + e01 = b; + + a = e10 * c + e11 * s; + b = e11 * c - e10 * s; + e10 = a; + e11 = b; + + a = e20 * c + e21 * s; + b = e21 * c - e20 * s; + e20 = a; + e21 = b; + } + } + + void lerp(const mat4 &m, float t) { + e00 += (m.e00 - e00) * t; + e01 += (m.e01 - e01) * t; + e02 += (m.e02 - e02) * t; + e03 += (m.e03 - e03) * t; + + e10 += (m.e10 - e10) * t; + e11 += (m.e11 - e11) * t; + e12 += (m.e12 - e12) * t; + e13 += (m.e13 - e13) * t; + + e20 += (m.e20 - e20) * t; + e21 += (m.e21 - e21) * t; + e22 += (m.e22 - e22) * t; + e23 += (m.e23 - e23) * t; + } + float det() const { return e00 * (e11 * (e22 * e33 - e32 * e23) - e21 * (e12 * e33 - e32 * e13) + e31 * (e12 * e23 - e22 * e13)) - e10 * (e01 * (e22 * e33 - e32 * e23) - e21 * (e02 * e33 - e32 * e03) + e31 * (e02 * e23 - e22 * e03)) + @@ -675,11 +798,11 @@ struct mat4 { } vec3 getPos() const { - return offset.xyz; + return offset().xyz(); } void setPos(const vec3 &pos) { - offset.xyz = pos; + offset().xyz() = pos; } }; @@ -733,10 +856,16 @@ struct ubyte2 { struct ubyte4 { uint8 x, y, z, w; + + ubyte4() {} + ubyte4(uint8 x, uint8 y, uint8 z, uint8 w) : x(x), y(y), z(z), w(w) {} }; struct short2 { int16 x, y; + + short2() {} + short2(int16 x, int16 y) : x(x), y(y) {} }; struct short3 { @@ -766,17 +895,16 @@ struct short4 { inline int16& operator [] (int index) const { ASSERT(index >= 0 && index <= 3); return ((int16*)this)[index]; } }; -quat rotYXZ(const vec3 &a) { +quat rotYXZ(const vec3 &angle) { mat4 m; m.identity(); - m.rotateY(a.y); - m.rotateX(a.x); - m.rotateZ(a.z); + m.rotateYXZ(angle); return m.getRot(); } quat lerpAngle(const vec3 &a, const vec3 &b, float t) { // TODO: optimization - return rotYXZ(a).slerp(rotYXZ(b), t).normal(); + + return rotYXZ(a).lerp(rotYXZ(b), t);//.normal(); } vec3 boxNormal(int x, int z) { @@ -890,7 +1018,7 @@ struct Box { Box res(vec3(+INF), vec3(-INF)); for (int i = 0; i < 8; i++) { vec4 v = m * vec4((*this)[i], 1.0f); - res += v.xyz /= v.w; + res += v.xyz() /= v.w; } return res; } @@ -1000,7 +1128,7 @@ struct Box { bool intersect(const mat4 &matrix, const vec3 &rayPos, const vec3 &rayDir, float &t) const { mat4 mInv = matrix.inverse(); - return intersect(mInv * rayPos, (mInv * vec4(rayDir, 0)).xyz, t); + return intersect(mInv * rayPos, (mInv * vec4(rayDir, 0)).xyz(), t); } };