From 432bc8868cac2b79cbe8b827b2317cc0312cca70 Mon Sep 17 00:00:00 2001 From: XProger Date: Mon, 18 Sep 2017 04:30:05 +0300 Subject: [PATCH] #11 add details and sound settings menus; #14 fix wolves base state --- src/cache.h | 136 ++++++++------ src/controller.h | 5 +- src/core.h | 156 ++++++++++++---- src/enemy.h | 2 + src/game.h | 22 +-- src/input.h | 64 +------ src/inventory.h | 361 +++++++++++++++++++++++++---------- src/lara.h | 1 + src/level.h | 98 +++++++--- src/mesh.h | 12 +- src/platform/web/index.html | 2 +- src/shaders/shader.glsl | 362 +++++++++++++++++------------------- src/shaders/water.glsl | 2 +- src/sound.h | 11 +- src/texture.h | 18 +- src/trigger.h | 1 + src/ui.h | 62 +++++- src/utils.h | 5 + 18 files changed, 818 insertions(+), 502 deletions(-) diff --git a/src/cache.h b/src/cache.h index 285abdb..5eac336 100644 --- a/src/cache.h +++ b/src/cache.h @@ -40,55 +40,20 @@ struct ShaderCache { memset(shaders, 0, sizeof(shaders)); LOG("shader: cache warm up...\n"); - if (Core::settings.detail.shadows) - compile(Core::passShadow, Shader::ENTITY, FX_NONE); + prepareCompose(FX_NONE); + if (Core::settings.detail.water > Core::Settings::LOW) + prepareCompose(FX_CLIP_PLANE); - if (Core::settings.detail.ambient) { - compile(Core::passAmbient, Shader::ROOM, FX_NONE); - compile(Core::passAmbient, Shader::ROOM, FX_ALPHA_TEST); - compile(Core::passAmbient, Shader::ROOM, FX_UNDERWATER); - compile(Core::passAmbient, Shader::SPRITE, FX_ALPHA_TEST); - if (Core::settings.detail.water) { - compile(Core::passAmbient, Shader::ROOM, FX_CLIP_PLANE); - compile(Core::passAmbient, Shader::ROOM, FX_ALPHA_TEST | FX_CLIP_PLANE); - compile(Core::passAmbient, Shader::ROOM, FX_UNDERWATER | FX_CLIP_PLANE); - compile(Core::passAmbient, Shader::SPRITE, FX_ALPHA_TEST | FX_CLIP_PLANE); - } - } + prepareAmbient(FX_NONE); - if (Core::settings.detail.water) { - compile(Core::passWater, Shader::WATER_MASK, FX_NONE); - compile(Core::passWater, Shader::WATER_STEP, FX_NONE); - compile(Core::passWater, Shader::WATER_CAUSTICS, FX_NONE); - compile(Core::passWater, Shader::WATER_COMPOSE, FX_NONE); - compile(Core::passWater, Shader::WATER_DROP, FX_NONE); - } + if (Core::settings.detail.shadows > Core::Settings::LOW) + prepareShadows(FX_NONE); - compile(Core::passFilter, Shader::FILTER_DOWNSAMPLE, FX_NONE); - compile(Core::passFilter, Shader::FILTER_GRAYSCALE, FX_NONE); - compile(Core::passFilter, Shader::FILTER_BLUR, FX_NONE); - compile(Core::passFilter, Shader::FILTER_MIXER, FX_NONE); + if (Core::settings.detail.water > Core::Settings::LOW) + prepareWater(FX_NONE); - compile(Core::passCompose, Shader::ROOM, FX_NONE); - compile(Core::passCompose, Shader::ROOM, FX_ALPHA_TEST); - compile(Core::passCompose, Shader::ROOM, FX_UNDERWATER); - compile(Core::passCompose, Shader::ENTITY, FX_NONE); - compile(Core::passCompose, Shader::ENTITY, FX_UNDERWATER); - compile(Core::passCompose, Shader::ENTITY, FX_ALPHA_TEST); - compile(Core::passCompose, Shader::SPRITE, FX_ALPHA_TEST); - compile(Core::passCompose, Shader::SPRITE, FX_UNDERWATER | FX_ALPHA_TEST); - compile(Core::passCompose, Shader::FLASH, FX_ALPHA_TEST); - if (Core::settings.detail.water) { - compile(Core::passCompose, Shader::ROOM, FX_CLIP_PLANE); - compile(Core::passCompose, Shader::ROOM, FX_ALPHA_TEST | FX_CLIP_PLANE); - compile(Core::passCompose, Shader::ROOM, FX_UNDERWATER | FX_CLIP_PLANE); - compile(Core::passCompose, Shader::SPRITE, FX_ALPHA_TEST | FX_CLIP_PLANE); - compile(Core::passCompose, Shader::ENTITY, FX_ALPHA_TEST | FX_CLIP_PLANE); - compile(Core::passCompose, Shader::ENTITY, FX_CLIP_PLANE); - compile(Core::passCompose, Shader::ENTITY, FX_UNDERWATER | FX_CLIP_PLANE); - compile(Core::passCompose, Shader::SPRITE, FX_UNDERWATER | FX_ALPHA_TEST | FX_CLIP_PLANE); - } - compile(Core::passCompose, Shader::MIRROR, FX_NONE); + prepareFilter(FX_NONE); + prepareGUI(FX_NONE); LOG("shader: cache is ready\n"); } @@ -100,6 +65,54 @@ struct ShaderCache { delete shaders[pass][type][fx]; } + void prepareCompose(int fx) { + compile(Core::passCompose, Shader::MIRROR, fx | FX_NONE); + compile(Core::passCompose, Shader::ROOM, fx | FX_NONE); + compile(Core::passCompose, Shader::ROOM, fx | FX_ALPHA_TEST); + compile(Core::passCompose, Shader::ROOM, fx | FX_UNDERWATER); + compile(Core::passCompose, Shader::ROOM, fx | FX_UNDERWATER | FX_ALPHA_TEST); + + compile(Core::passCompose, Shader::ENTITY, fx | FX_NONE); + compile(Core::passCompose, Shader::ENTITY, fx | FX_UNDERWATER); + compile(Core::passCompose, Shader::ENTITY, fx | FX_ALPHA_TEST); + compile(Core::passCompose, Shader::SPRITE, fx | FX_ALPHA_TEST); + compile(Core::passCompose, Shader::SPRITE, fx | FX_UNDERWATER | FX_ALPHA_TEST); + compile(Core::passCompose, Shader::FLASH, fx | FX_NONE); + compile(Core::passCompose, Shader::FLASH, fx | FX_ALPHA_TEST); + } + + void prepareAmbient(int fx) { + compile(Core::passAmbient, Shader::ROOM, fx | FX_NONE); + compile(Core::passAmbient, Shader::ROOM, fx | FX_ALPHA_TEST); + compile(Core::passAmbient, Shader::ROOM, fx | FX_UNDERWATER); + compile(Core::passAmbient, Shader::ROOM, fx | FX_UNDERWATER | FX_ALPHA_TEST); + compile(Core::passAmbient, Shader::SPRITE, fx | FX_ALPHA_TEST); + } + + void prepareShadows(int fx) { + compile(Core::passShadow, Shader::ENTITY, fx | FX_NONE); + } + + void prepareWater(int fx) { + compile(Core::passWater, Shader::WATER_MASK, fx | FX_NONE); + compile(Core::passWater, Shader::WATER_STEP, fx | FX_NONE); + compile(Core::passWater, Shader::WATER_CAUSTICS, fx | FX_NONE); + compile(Core::passWater, Shader::WATER_COMPOSE, fx | FX_NONE); + compile(Core::passWater, Shader::WATER_DROP, fx | FX_NONE); + } + + void prepareFilter(int fx) { + compile(Core::passFilter, Shader::DEFAULT, fx | FX_NONE); + compile(Core::passFilter, Shader::FILTER_DOWNSAMPLE, fx | FX_NONE); + compile(Core::passFilter, Shader::FILTER_GRAYSCALE, fx | FX_NONE); + compile(Core::passFilter, Shader::FILTER_BLUR, fx | FX_NONE); + compile(Core::passFilter, Shader::FILTER_MIXER, fx | FX_NONE); + } + + void prepareGUI(int fx) { + compile(Core::passGUI, Shader::DEFAULT, fx | FX_NONE); + } + Shader* compile(Core::Pass pass, Shader::Type type, int fx) { char def[1024], ext[255]; ext[0] = 0; @@ -131,12 +144,22 @@ struct ShaderCache { 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); if (fx & FX_UNDERWATER) strcat(def, "#define UNDERWATER\n" UNDERWATER_COLOR); if (fx & FX_ALPHA_TEST) strcat(def, "#define ALPHA_TEST\n"); - if (fx & FX_CLIP_PLANE) strcat(def, "#define CLIP_PLANE\n"); - if (Core::settings.detail.ambient) strcat(def, "#define OPT_AMBIENT\n"); - if (Core::settings.detail.lighting) strcat(def, "#define OPT_LIGHTING\n"); - if (Core::settings.detail.shadows) strcat(def, "#define OPT_SHADOW\n"); - if (Core::settings.detail.water) strcat(def, "#define OPT_WATER\n"); - if (Core::settings.detail.contact) strcat(def, "#define OPT_CONTACT\n"); + if (pass == Core::passCompose) { + if (fx & FX_CLIP_PLANE) + strcat(def, "#define CLIP_PLANE\n"); + if (type == Shader::ROOM) + strcat(def, "#define OPT_ANIMTEX\n"); + if (Core::settings.detail.lighting > Core::Settings::LOW && (type == Shader::ENTITY || type == Shader::ROOM)) + strcat(def, "#define OPT_LIGHTING\n"); + if (Core::settings.detail.lighting > Core::Settings::MEDIUM && (type == Shader::ENTITY)) + strcat(def, "#define OPT_AMBIENT\n"); + if (Core::settings.detail.shadows > Core::Settings::LOW && (type == Shader::ENTITY || type == Shader::ROOM)) + strcat(def, "#define OPT_SHADOW\n"); + if (Core::settings.detail.shadows > Core::Settings::MEDIUM && (type == Shader::ROOM)) + strcat(def, "#define OPT_CONTACT\n"); + if (Core::settings.detail.water > Core::Settings::MEDIUM && (type == Shader::ENTITY || type == Shader::ROOM) && (fx & FX_UNDERWATER)) + strcat(def, "#define OPT_CAUSTICS\n"); + } break; } case Core::passWater : { @@ -410,7 +433,7 @@ struct WaterCache { data[0] = new Texture(w * 64, h * 64, Texture::RGBA_HALF); data[1] = new Texture(w * 64, h * 64, Texture::RGBA_HALF); - caustics = new Texture(512, 512, Texture::RGB16); + caustics = Core::settings.detail.water > Core::Settings::MEDIUM ? new Texture(512, 512, Texture::RGBA) : NULL; mask = new Texture(w, h, Texture::RGB16, false, m, false); delete[] m; @@ -520,16 +543,11 @@ struct WaterCache { break; } - if (!item || !item->caustics) { - Core::blackTex->bind(sReflect); - Core::active.shader->setParam(uRoomSize, vec4(0.0f)); - game->setWaterParams(-NO_CLIP_PLANE); - } else { + if (item && item->caustics) { item->caustics->bind(sReflect); Core::active.shader->setParam(uRoomSize, vec4(item->pos.x - item->size.x, item->pos.z - item->size.z, item->pos.x + item->size.x, item->pos.z + item->size.z)); game->setWaterParams(item->pos.y); } - game->updateParams(); } void addDrop(const vec3 &pos, float radius, float strength) { @@ -702,12 +720,10 @@ struct WaterCache { float sign = underwater ? -1.0f : 1.0f; game->setClipParams(sign, item.pos.y * sign); - game->updateParams(); game->renderView(underwater ? item.from : item.to, false); } Core::invalidateTarget(false, true); game->setClipParams(1.0f, NO_CLIP_PLANE); - game->updateParams(); camera->reflectPlane = NULL; camera->setup(true); diff --git a/src/controller.h b/src/controller.h index be1158c..e4af4da 100644 --- a/src/controller.h +++ b/src/controller.h @@ -29,6 +29,10 @@ struct ICamera { struct IGame { virtual ~IGame() {} virtual void loadLevel(TR::LevelID id) {} + virtual void loadGame(int slot) {} + virtual void saveGame(int slot) {} + virtual void applySettings(const Core::Settings &settings) {} + virtual TR::Level* getLevel() { return NULL; } virtual MeshBuilder* getMesh() { return NULL; } virtual ICamera* getCamera() { return NULL; } @@ -38,7 +42,6 @@ struct IGame { virtual uint16 findPath(int ascend, int descend, bool big, int boxStart, int boxEnd, uint16 *zones, uint16 **boxes) { return 0; } virtual void setClipParams(float clipSign, float clipHeight) {} virtual void setWaterParams(float height) {} - virtual void updateParams() {} virtual void waterDrop(const vec3 &pos, float radius, float strength) {} virtual void setShader(Core::Pass pass, Shader::Type type, bool underwater = false, bool alphaTest = false) {} virtual void setupBinding() {} diff --git a/src/core.h b/src/core.h index 5f19a81..3691010 100644 --- a/src/core.h +++ b/src/core.h @@ -137,34 +137,108 @@ #define glProgramBinary(...) #endif +#include "utils.h" + +enum ControlKey { cLeft, cRight, cUp, cDown, cJump, cWalk, cAction, cWeapon, cLook, cStepLeft, cStepRight, cRoll, cInventory, cMAX }; + +enum InputKey { ikNone, + // keyboard + ikLeft, ikRight, ikUp, ikDown, ikSpace, ikTab, ikEnter, ikEscape, ikShift, ikCtrl, ikAlt, + ik0, ik1, ik2, ik3, ik4, ik5, ik6, ik7, ik8, ik9, + ikA, ikB, ikC, ikD, ikE, ikF, ikG, ikH, ikI, ikJ, ikK, ikL, ikM, + ikN, ikO, ikP, ikQ, ikR, ikS, ikT, ikU, ikV, ikW, ikX, ikY, ikZ, + // mouse + ikMouseL, ikMouseR, ikMouseM, + // touch + ikTouchA, ikTouchB, ikTouchC, ikTouchD, ikTouchE, ikTouchF, + // gamepad + ikJoyA, ikJoyB, ikJoyX, ikJoyY, ikJoyLB, ikJoyRB, ikJoySelect, ikJoyStart, ikJoyL, ikJoyR, ikJoyLT, ikJoyRT, ikJoyPOV, + ikJoyLeft, ikJoyRight, ikJoyUp, ikJoyDown, + ikMAX }; + +struct KeySet { + InputKey key, joy; +}; + namespace Core { float deltaTime; int width, height; struct { - struct { - bool ambient; - bool lighting; - bool shadows; - bool water; - bool contact; + int maxVectors; + int maxAniso; + bool shaderBinary; + bool VAO; + bool depthTexture; + bool shadowSampler; + bool discardFrame; + bool texNPOT; + bool texRG; + bool texBorder; + bool colorFloat, texFloat, texFloatLinear; + bool colorHalf, texHalf, texHalfLinear; + #ifdef PROFILE + bool profMarker; + bool profTiming; + #endif + } support; + + struct Settings { + enum Quality : uint8 { LOW, MEDIUM, HIGH }; + + union { + struct { + Quality filter; + Quality lighting; + Quality shadows; + Quality water; + }; + Quality quality[4]; + + void setFilter(Quality value) { + if (value > MEDIUM && !(support.maxAniso > 1)) + value = MEDIUM; + filter = value; + } + + void setLighting(Quality value) { + lighting = value; + } + + void setShadows(Quality value) { + if (value > MEDIUM && !(support.maxVectors > 8)) + value = MEDIUM; + shadows = value; + } + + void setWater(Quality value) { + if (value > LOW && !(support.texFloat || support.texHalf)) + value = LOW; + else + if (value > MEDIUM && !(support.maxVectors > 8)) + value = MEDIUM; + water = value; + } } detail; struct { + KeySet keys[cMAX]; bool retarget; + bool multitarget; + bool vibration; } controls; struct { + float music; + float sound; bool reverb; } audio; } settings; } -#include "utils.h" #include "input.h" #include "sound.h" - #if defined(WIN32) || (defined(LINUX) && !defined(__RPI__)) || defined(ANDROID) #ifdef ANDROID @@ -293,26 +367,6 @@ struct Vertex { ubyte4 color; // xyz - color, w - intensity }; -namespace Core { - struct { - bool shaderBinary; - bool VAO; - bool depthTexture; - bool shadowSampler; - bool discardFrame; - bool texNPOT; - bool texRG; - bool texBorder; - int8 texAniso; - bool colorFloat, texFloat, texFloatLinear; - bool colorHalf, texHalf, texHalfLinear; - #ifdef PROFILE - bool profMarker; - bool profTiming; - #endif - } support; -} - #ifdef PROFILE #define USE_CV_MARKERS @@ -563,6 +617,8 @@ namespace Core { } } */ + glGetIntegerv(GL_MAX_VARYING_VECTORS, &support.maxVectors); + support.shaderBinary = extSupport(ext, "_program_binary"); support.VAO = extSupport(ext, "_vertex_array_object"); support.depthTexture = extSupport(ext, "_depth_texture"); @@ -571,7 +627,7 @@ namespace Core { support.texNPOT = extSupport(ext, "_texture_npot") || extSupport(ext, "_texture_non_power_of_two"); support.texRG = extSupport(ext, "_texture_rg "); // hope that isn't last extension in string ;) support.texBorder = extSupport(ext, "_texture_border_clamp"); - support.texAniso = extSupport(ext, "_texture_filter_anisotropic"); + support.maxAniso = extSupport(ext, "_texture_filter_anisotropic"); support.colorFloat = extSupport(ext, "_color_buffer_float"); support.colorHalf = extSupport(ext, "_color_buffer_half_float") || extSupport(ext, "GL_ARB_half_float_pixel"); support.texFloatLinear = support.colorFloat || extSupport(ext, "GL_ARB_texture_float") || extSupport(ext, "_texture_float_linear"); @@ -579,11 +635,8 @@ namespace Core { support.texHalfLinear = support.colorHalf || extSupport(ext, "GL_ARB_texture_float") || extSupport(ext, "_texture_half_float_linear") || extSupport(ext, "_color_buffer_half_float"); support.texHalf = support.texHalfLinear || extSupport(ext, "_texture_half_float"); - if (support.texAniso) { - int maxAniso; - glGetIntegerv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxAniso); - support.texAniso = maxAniso; - } + if (support.maxAniso) + glGetIntegerv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &support.maxAniso); #ifdef PROFILE support.profMarker = extSupport(ext, "_KHR_debug"); @@ -595,6 +648,7 @@ namespace Core { LOG("Version : %s\n", glGetString(GL_VERSION)); LOG("cache : %s\n", Stream::cacheDir); LOG("supports :\n"); + LOG(" variyngs count : %d\n", support.maxVectors); LOG(" binary shaders : %s\n", support.shaderBinary ? "true" : "false"); LOG(" vertex arrays : %s\n", support.VAO ? "true" : "false"); LOG(" depth texture : %s\n", support.depthTexture ? "true" : "false"); @@ -603,7 +657,7 @@ namespace Core { LOG(" NPOT textures : %s\n", support.texNPOT ? "true" : "false"); LOG(" RG textures : %s\n", support.texRG ? "true" : "false"); LOG(" border color : %s\n", support.texBorder ? "true" : "false"); - LOG(" anisotropic : %d\n", support.texAniso); + LOG(" anisotropic : %d\n", support.maxAniso); LOG(" float textures : float = %s, half = %s\n", support.colorFloat ? "full" : (support.texFloat ? (support.texFloatLinear ? "linear" : "nearest") : "false"), support.colorHalf ? "full" : (support.texHalf ? (support.texHalfLinear ? "linear" : "nearest") : "false")); @@ -627,6 +681,38 @@ namespace Core { blackTex = new Texture(1, 1, Texture::RGBA, false, &data, false); data = 0xFFFFFFFF; whiteTex = new Texture(1, 1, Texture::RGBA, false, &data, false); + + // init settings + settings.detail.setFilter (Core::Settings::HIGH); + settings.detail.setLighting (Core::Settings::HIGH); + settings.detail.setShadows (Core::Settings::MEDIUM); + settings.detail.setWater (Core::Settings::HIGH); + + settings.audio.music = 0.7f; + settings.audio.sound = 0.7f; + settings.audio.reverb = true; + + settings.controls.retarget = true; + 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[ cJump ] = { ikD, ikJoyX }; + 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 }; + +#ifdef __RPI__ + settings.detail.setShadows(Core::Settings::LOW); +#endif } void free() { diff --git a/src/enemy.h b/src/enemy.h index e1e3bb9..574d07f 100644 --- a/src/enemy.h +++ b/src/enemy.h @@ -487,6 +487,8 @@ struct Wolf : Enemy { jointChest = 2; jointHead = 3; nextState = STATE_NONE; + animation.time = animation.timeMax; + updateAnimation(false); } virtual int getStateGround() { diff --git a/src/game.h b/src/game.h index 124fcdc..3a6923e 100644 --- a/src/game.h +++ b/src/game.h @@ -39,18 +39,6 @@ namespace Game { Core::init(); - Core::settings.detail.ambient = true; - Core::settings.detail.lighting = true; - Core::settings.detail.shadows = true; - Core::settings.detail.water = Core::support.texFloat || Core::support.texHalf; - Core::settings.detail.contact = false; -#ifdef __RPI__ - Core::settings.detail.ambient = false; - Core::settings.detail.shadows = false; -#endif - Core::settings.controls.retarget = true; - Core::settings.audio.reverb = true; - shaderCache = new ShaderCache(); UI::init(level); @@ -100,12 +88,14 @@ namespace Game { Input::update(); - if (Input::down[ikV]) { // third <-> first person view - level->camera->changeView(!level->camera->firstPerson); - Input::down[ikV] = false; + if (level->camera) { + if (Input::down[ikV]) { // third <-> first person view + level->camera->changeView(!level->camera->firstPerson); + Input::down[ikV] = false; + } } - Core::deltaTime = delta = min(1.0f, delta); + Core::deltaTime = delta = min(0.2f, delta); UI::update(); while (delta > EPS) { diff --git a/src/input.h b/src/input.h index 172eeae..43068b7 100644 --- a/src/input.h +++ b/src/input.h @@ -1,68 +1,13 @@ #ifndef H_INPUT #define H_INPUT +#include "core.h" #include "utils.h" -enum InputKey { ikNone, - // keyboard - ikLeft, ikRight, ikUp, ikDown, ikSpace, ikTab, ikEnter, ikEscape, ikShift, ikCtrl, ikAlt, - ik0, ik1, ik2, ik3, ik4, ik5, ik6, ik7, ik8, ik9, - ikA, ikB, ikC, ikD, ikE, ikF, ikG, ikH, ikI, ikJ, ikK, ikL, ikM, - ikN, ikO, ikP, ikQ, ikR, ikS, ikT, ikU, ikV, ikW, ikX, ikY, ikZ, - // mouse - ikMouseL, ikMouseR, ikMouseM, - // touch - ikTouchA, ikTouchB, ikTouchC, ikTouchD, ikTouchE, ikTouchF, - // gamepad - ikJoyA, ikJoyB, ikJoyX, ikJoyY, ikJoyLB, ikJoyRB, ikJoySelect, ikJoyStart, ikJoyL, ikJoyR, ikJoyLT, ikJoyRT, ikJoyPOV, - ikJoyLeft, ikJoyRight, ikJoyUp, ikJoyDown, - ikMAX }; - -enum ControlKey { cLeft, cRight, cUp, cDown, cJump, cWalk, cAction, cWeapon, cLook, cStepLeft, cStepRight, cRoll, cInventory, cMAX }; - namespace Input { bool down[ikMAX]; - - struct KeySet { - InputKey key, joy; - }; - - static const KeySet presets[1][cMAX] = { - { { ikLeft, ikJoyLeft }, - { ikRight, ikJoyRight }, - { ikUp, ikJoyUp }, - { ikDown, ikJoyDown }, - { ikD, ikJoyX }, - { ikShift, ikJoyRB }, - { ikCtrl, ikJoyA }, - { ikSpace, ikJoyY }, - { ikC, ikJoyLB }, - { ikZ, ikJoyLT }, - { ikX, ikJoyRT }, - { ikA, ikJoyB }, - { ikTab, ikJoySelect }, - }, - /* - { { ikA, ikJoyLeft }, - { ikD, ikJoyRight }, - { ikW, ikJoyUp }, - { ikS, ikJoyDown }, - { ikSpace, ikJoyX }, - { ikShift, ikJoyRB }, - { ikP, ikJoyA }, - { ikMouseM, ikJoyY }, - { ikMouseR, ikJoyLB }, - { ikLeft, ikJoyLT }, - { ikRight, ikJoyRT }, - { ikUp, ikJoyB }, - { ikTab, ikJoySelect }, - }, - */ - }; - - KeySet controls[cMAX]; - bool state[cMAX]; + bool state[cMAX]; struct { vec2 pos; @@ -186,9 +131,6 @@ namespace Input { void init() { reset(); - for (int i = 0; i < cMAX; i++) - controls[i] = presets[0][i]; - touchTimerVis = 0.0f; touchTimerTap = 0.0f; doubleTap = false; @@ -226,7 +168,7 @@ namespace Input { setDown(ikJoyLeft, p == 6 || p == 7 || p == 8); for (int i = 0; i < cMAX; i++) { - KeySet &c = controls[i]; + KeySet &c = Core::settings.controls.keys[i]; state[i] = (c.key != ikNone && down[c.key]) || (c.joy != ikNone && down[c.joy]); } diff --git a/src/inventory.h b/src/inventory.h index b8fe9ce..1eecfae 100644 --- a/src/inventory.h +++ b/src/inventory.h @@ -29,7 +29,9 @@ struct Inventory { Page page, targetPage; int itemsCount; + float changeTimer; TR::LevelID nextLevel; // toggle result + ControlKey lastKey; struct Item { TR::Entity::Type type; @@ -156,7 +158,7 @@ struct Inventory { delete stream; } - Inventory(IGame *game) : game(game), active(false), chosen(false), index(0), targetIndex(0), page(PAGE_OPTION), targetPage(PAGE_OPTION), itemsCount(0), nextLevel(TR::LEVEL_MAX) { + Inventory(IGame *game) : game(game), active(false), chosen(false), index(0), targetIndex(0), page(PAGE_OPTION), targetPage(PAGE_OPTION), itemsCount(0), changeTimer(0.0f), nextLevel(TR::LEVEL_MAX), lastKey(cMAX) { TR::LevelID id = game->getLevel()->id; add(TR::Entity::INV_PASSPORT); @@ -331,7 +333,7 @@ struct Inventory { nextLevel = TR::LEVEL_MAX; phasePage = 1.0f; phaseSelect = 1.0f; - page = targetPage = curPage; + page = targetPage = curPage; if (type != TR::Entity::NONE) { int i = contains(type); @@ -403,13 +405,110 @@ struct Inventory { } void onChoose(Item *item) { - if (item->type == TR::Entity::INV_PASSPORT) { - game->playSound(TR::SND_INV_PAGE, vec3(), 0, 0); - item->value = 1; - passportSlot = 0; - passportSlotCount = 2; - passportSlots[0] = TR::LEVEL_1; - passportSlots[1] = TR::LEVEL_2; + slot = 0; + + switch (item->type) { + case TR::Entity::INV_PASSPORT : { + game->playSound(TR::SND_INV_PAGE, vec3(), 0, 0); + item->value = 1; + passportSlotCount = 2; + passportSlots[0] = TR::LEVEL_1; + passportSlots[1] = TR::LEVEL_2; + break; + } + case TR::Entity::INV_DETAIL : { + settings = Core::settings; + break; + } + default : ; + } + } + + void controlItem(Item *item, ControlKey key) { + if (item->type == TR::Entity::INV_PASSPORT && passportSlotCount) { + // passport slots + if (item->value == 0 && item->anim->dir == 0.0f) { // slot select + if (key == cUp ) { slot = (slot - 1 + passportSlotCount) % passportSlotCount; }; + if (key == cDown ) { slot = (slot + 1) % passportSlotCount; }; + } + // passport pages + if (key == cLeft && item->value > 0) { item->value--; item->anim->dir = -1.0f; game->playSound(TR::SND_INV_PAGE, vec3(), 0, 0); } + if (key == cRight && item->value < 2) { item->value++; item->anim->dir = 1.0f; game->playSound(TR::SND_INV_PAGE, vec3(), 0, 0); } + + if (key == cAction && phaseChoose == 1.0f) { + TR::LevelID id = game->getLevel()->id; + switch (item->value) { + case 0 : nextLevel = passportSlots[slot]; break; + case 1 : nextLevel = (id == TR::TITLE) ? TR::LEVEL_1 : game->getLevel()->id; break; + case 2 : nextLevel = (id == TR::TITLE) ? TR::LEVEL_MAX : TR::TITLE; break; + } + + if (nextLevel != TR::LEVEL_MAX) { + item->anim->dir = -1.0f; + item->value = -100; + toggle(); + } + } + } + + if (item->type == TR::Entity::INV_DETAIL) { + int count = 5; + if (key == cUp ) { slot = (slot - 1 + count) % count; }; + if (key == cDown ) { slot = (slot + 1) % count; }; + if (slot < count - 1) { + Core::Settings::Quality q = settings.detail.quality[slot]; + if (key == cLeft && q > Core::Settings::LOW ) { q = Core::Settings::Quality(q - 1); } + if (key == cRight && q < Core::Settings::HIGH ) { q = Core::Settings::Quality(q + 1); } + if (q != settings.detail.quality[slot]) { + switch (slot) { + case 0 : settings.detail.setFilter(q); break; + case 1 : settings.detail.setLighting(q); break; + case 2 : settings.detail.setShadows(q); break; + case 3 : settings.detail.setWater(q); break; + } + if (q == settings.detail.quality[slot]) + game->playSound(TR::SND_INV_PAGE, vec3(), 0, 0); + } + } + + if (slot == count -1 && key == cAction) { + game->applySettings(settings); + chosen = false; + } + } + + if (item->type == TR::Entity::INV_SOUND) { + int count = 3; + if (key == cUp ) { slot = (slot - 1 + count) % count; }; + if (key == cDown ) { slot = (slot + 1) % count; }; + + if (slot == 0 || slot == 1) { // music + float &v = slot == 0 ? Core::settings.audio.music : Core::settings.audio.sound; + if ((key == cLeft && v > 0.0f) || (key == cRight && v < 1.0f)) { + v = key == cLeft ? max(0.0f, v - 0.05f) : min(1.0f, v + 0.05f); + changeTimer = 0.2f; + if (slot == 1) + game->playSound(TR::SND_PISTOLS_SHOT, vec3(), 0, 0); + game->applySettings(Core::settings); + } + } + + if (slot == 2 && (key == cLeft || key == cRight)) { + Core::settings.audio.reverb = !Core::settings.audio.reverb; + game->applySettings(Core::settings); + } + } + + if (item->type == TR::Entity::INV_HOME && phaseChoose == 1.0f && key == cAction) { + nextLevel = TR::GYM; + toggle(); + } + + if ((key == cInventory || key == cJump) && phaseChoose == 1.0f) { + chosen = false; + item->anim->dir = 1.0f; + item->value = 1000; + item->angle = 0.0f; } } @@ -437,31 +536,32 @@ struct Inventory { bool ready = active && phaseRing == 1.0f && phasePage == 1.0f; - enum KeyDir { NONE, LEFT, RIGHT, UP, DOWN } dir; - - if (Input::state[cLeft] || Input::joy.L.x < -0.5f || Input::joy.R.x > 0.5f) - dir = LEFT; + ControlKey key = cMAX; + if (Input::state[cAction]) + key = cAction; + else if (Input::state[cInventory] || Input::state[cJump]) + key = cInventory; + else if (Input::state[cLeft] || Input::joy.L.x < -0.5f || Input::joy.R.x > 0.5f) + key = cLeft; else if (Input::state[cRight] || Input::joy.L.x > 0.5f || Input::joy.R.x < -0.5f) - dir = RIGHT; + key = cRight; else if (Input::state[cUp] || Input::joy.L.y < -0.5f || Input::joy.R.y > 0.5f) - dir = UP; + key = cUp; else if (Input::state[cDown] || Input::joy.L.y > 0.5f || Input::joy.R.y < -0.5f) - dir = DOWN; - else - dir = NONE; + key = cDown; - static KeyDir lastDir = NONE; + Item *item = items[getGlobalIndex(page, index)]; if (index == targetIndex && targetPage == page && ready) { if (!chosen) { - if (dir == UP && !(page < PAGE_ITEMS && getItemsCount(page + 1))) dir = NONE; - if (dir == DOWN && !(page > PAGE_OPTION && getItemsCount(page - 1))) dir = NONE; + if (key == cUp && !(page < PAGE_ITEMS && getItemsCount(page + 1))) key = cMAX; + if (key == cDown && !(page > PAGE_OPTION && getItemsCount(page - 1))) key = cMAX; - switch (dir) { - case LEFT : { phaseSelect = 0.0f; targetIndex = (targetIndex - 1 + count) % count; } break; - case RIGHT : { phaseSelect = 0.0f; targetIndex = (targetIndex + 1) % count; } break; - case UP : { phasePage = 0.0f; targetPage = Page(page + 1); } break; - case DOWN : { phasePage = 0.0f; targetPage = Page(page - 1); } break; + switch (key) { + case cLeft : { phaseSelect = 0.0f; targetIndex = (targetIndex - 1 + count) % count; } break; + case cRight : { phaseSelect = 0.0f; targetIndex = (targetIndex + 1) % count; } break; + case cUp : { phasePage = 0.0f; targetPage = Page(page + 1); } break; + case cDown : { phasePage = 0.0f; targetPage = Page(page - 1); } break; default : ; } @@ -469,60 +569,10 @@ struct Inventory { vec3 p; game->playSound(TR::SND_INV_SPIN, p, 0, 0); } - } else { - Item *item = items[getGlobalIndex(page, index)]; - if (item->type == TR::Entity::INV_PASSPORT && passportSlotCount) { - if (lastDir != dir) { - // passport slots - if (item->value == 0 && item->anim->dir == 0.0f) { // slot select - if (dir == UP) { passportSlot = (passportSlot - 1 + passportSlotCount) % passportSlotCount; }; - if (dir == DOWN) { passportSlot = (passportSlot + 1) % passportSlotCount; }; - } - // passport pages - if (dir == LEFT && item->value > 0) { item->value--; item->anim->dir = -1.0f; game->playSound(TR::SND_INV_PAGE, vec3(), 0, 0); } - if (dir == RIGHT && item->value < 2) { item->value++; item->anim->dir = 1.0f; game->playSound(TR::SND_INV_PAGE, vec3(), 0, 0); } - lastDir = dir; - } - - if (Input::state[cAction] && phaseChoose == 1.0f) { - TR::LevelID id = game->getLevel()->id; - switch (item->value) { - case 0 : nextLevel = passportSlots[passportSlot]; break; - case 1 : nextLevel = (id == TR::TITLE) ? TR::LEVEL_1 : game->getLevel()->id; break; - case 2 : nextLevel = (id == TR::TITLE) ? TR::LEVEL_MAX : TR::TITLE; break; - } - - if (nextLevel != TR::LEVEL_MAX) { - item->anim->dir = -1.0f; - item->value = -100; - toggle(); - } - } - } - - if (item->type == TR::Entity::INV_HOME) { - if (Input::state[cAction] && phaseChoose == 1.0f) { - nextLevel = TR::GYM; - toggle(); - } - } - - } - } - - ready = active && phaseRing == 1.0f && phasePage == 1.0f; - - vec3 p; - - Item *item = items[getGlobalIndex(page, index)]; - - if (index == targetIndex && ready) { - if (Input::state[cAction] && (phaseChoose == 0.0f || (phaseChoose == 1.0f && item->anim->isEnded))) { - chosen = !chosen; - if (!chosen) { - item->angle = 0.0f; - } else { + if (lastKey != key && key == cAction && phaseChoose == 0.0f) { + vec3 p; + chosen = true; switch (item->type) { case TR::Entity::INV_COMPASS : game->playSound(TR::SND_INV_COMPASS, p, 0, 0); break; case TR::Entity::INV_HOME : game->playSound(TR::SND_INV_HOME, p, 0, 0); break; @@ -535,8 +585,22 @@ struct Inventory { } item->choose(); } + } else { + if (changeTimer > 0.0f) { + changeTimer -= Core::deltaTime; + if (changeTimer <= 0.0f) { + changeTimer = 0.0f; + lastKey = cMAX; + } + } + + if (key != cMAX && lastKey != key && changeTimer == 0.0f) + controlItem(item, key); } } + lastKey = key; + + ready = active && phaseRing == 1.0f && phasePage == 1.0f; float w = 90.0f * DEG2RAD * Core::deltaTime; @@ -638,16 +702,35 @@ struct Inventory { } } - int passportSlot, passportSlotCount; + int slot, passportSlotCount; TR::LevelID passportSlots[32]; + Core::Settings settings; void renderPassport(Item *item) { - if (item->value != 0 || item->anim->dir != 0.0f) return; // check for "Load Game" page + if (item->anim->dir != 0.0f) return; // check for "Load Game" page float y = 120.0f; float h = 20.0f; float w = 320.0f; - + + StringID str = STR_LOAD_GAME; + + if (game->getLevel()->id == TR::TITLE) { + if (item->value == 1) str = STR_START_GAME; + if (item->value == 2) str = STR_EXIT_GAME; + } else { + if (item->value == 1) str = STR_RESTART_LEVEL; + if (item->value == 2) str = STR_EXIT_TO_TITLE; + } + + UI::textOut(vec2(0, 480 - 32), str, UI::aCenter, UI::width); + float tw = UI::getTextSize(STR[str]).x; + + if (item->value > 0) UI::specOut(vec2((UI::width - tw) * 0.5f - 32.0f, 480 - 32), 108); + if (item->value < 2) UI::specOut(vec2((UI::width + tw) * 0.5f + 16.0f, 480 - 32), 109); + + if (item->value != 0) return; + // background UI::renderBar(UI::BAR_OPTION, vec2((UI::width - w - 16.0f) * 0.5f, y - 16.0f), vec2(w + 16.0f, h * 16.0f), 0.0f, 0, 0xC0000000); // title @@ -655,7 +738,7 @@ struct Inventory { UI::textOut(vec2(0, y), STR_SELECT_LEVEL, UI::aCenter, UI::width); y += h * 2; - UI::renderBar(UI::BAR_OPTION, vec2((UI::width - w) * 0.5f, y + passportSlot * h + 6 - h), vec2(w, h - 6), 1.0f, 0xFFD8377C, 0); + UI::renderBar(UI::BAR_OPTION, vec2((UI::width - w) * 0.5f, y + slot * h + 6 - h), vec2(w, h - 6), 1.0f, 0xFFD8377C, 0); for (int i = 0; i < passportSlotCount; i++) if (passportSlots[i] == TR::LEVEL_MAX) @@ -664,21 +747,101 @@ struct Inventory { UI::textOut(vec2(0, y + i * h), TR::LEVEL_INFO[passportSlots[i]].title, UI::aCenter, UI::width); } + float printBool(float x, float y, float w, StringID oStr, bool active, bool value) { + StringID vStr = StringID(STR_OFF + int(value)); + + UI::textOut(vec2(x, y), oStr); + UI::textOut(vec2(x + w - 96.0f, y), vStr, UI::aCenter, 96.0f); + if (active) { + UI::specOut(vec2(x + w - 96.0f, y), 108); + UI::specOut(vec2(x + w - 12.0f, y), 109); + } + return y + 20.0f; + } + + float printQuality(float x, float y, float w, StringID oStr, bool active, Core::Settings::Quality value) { + StringID vStr = StringID(STR_QUALITY_LOW + int(value)); + + float d = x + w * 0.5f; + UI::textOut(vec2(x + 32.0f, y), oStr); + UI::textOut(vec2(d, y), vStr, UI::aCenter, w * 0.5f - 32.0f); + if (active) { + if (value > Core::Settings::LOW) UI::specOut(vec2(d, y), 108); + if (value < Core::Settings::HIGH) UI::specOut(vec2(d + w * 0.5f - 32.0f - 16.0f, y), 109); + } + return y + 20.0f; + } + + float printBar(float x, float y, float w, uint32 color, char icon, bool active, float value) { + float h = 20.0f; + UI::renderBar(UI::BAR_WHITE, vec2(x + (32.0f + 2.0f), y - h + 6 + 2), vec2(w - (64.0f + 4.0f), h - 6 - 4), value, color, 0xFF000000, 0xFFA0A0A0, 0xFFA0A0A0, 0xFF000000); + UI::specOut(vec2(x + 16.0f, y), icon); + if (active) { + if (value > 0.0f) UI::specOut(vec2(x, y), 108); + if (value < 1.0f) UI::specOut(vec2(x + w - 12.0f, y), 109); + } + return y + 20.0f; + } + + void renderDetail(Item *item) { + float w = 320.0f; + float h = 20.0f; + + float x = (UI::width - w) * 0.5f; + float y = 192.0f; + + // background + UI::renderBar(UI::BAR_OPTION, vec2(x, y - 16.0f), vec2(w, h * 8.0f + 8.0f), 0.0f, 0, 0xC0000000); + // title + UI::renderBar(UI::BAR_OPTION, vec2(x, y - h + 6), vec2(w, h - 6), 1.0f, 0x802288FF, 0, 0, 0); + UI::textOut(vec2(0, y), STR_SELECT_DETAIL, UI::aCenter, UI::width); + + y += h * 2; + x += 8.0f; + w -= 16.0f; + float aw = slot == 4 ? (w - 128.0f) : w; + + UI::renderBar(UI::BAR_OPTION, vec2((UI::width - aw) * 0.5f, y + (slot > 3 ? 5 : slot) * h + 6 - h), vec2(aw, h - 6), 1.0f, 0xFFD8377C, 0); + y = printQuality(x, y, w, STR_OPT_DETAIL_FILTER, slot == 0, settings.detail.filter); + y = printQuality(x, y, w, STR_OPT_DETAIL_LIGHTING, slot == 1, settings.detail.lighting); + y = printQuality(x, y, w, STR_OPT_DETAIL_SHADOWS, slot == 2, settings.detail.shadows); + y = printQuality(x, y, w, STR_OPT_DETAIL_WATER, slot == 3, settings.detail.water); + y += h; + UI::textOut(vec2(x + 64.0f, y), STR_APPLY, UI::aCenter, w - 128.0f); + } + + void renderSound(Item *item) { + float w = 320.0f; + float h = 20.0f; + + float x = (UI::width - w) * 0.5f; + float y = 192.0f; + + // background + UI::renderBar(UI::BAR_OPTION, vec2(x, y - 16.0f), vec2(w, h * 5.0f + 8.0f), 0.0f, 0, 0xC0000000); + // title + UI::renderBar(UI::BAR_OPTION, vec2(x, y - h + 6), vec2(w, h - 6), 1.0f, 0x802288FF, 0, 0, 0); + UI::textOut(vec2(0, y), STR_SET_VOLUMES, UI::aCenter, UI::width); + + y += h * 2; + x += 8.0f; + w -= 16.0f; + + UI::renderBar(UI::BAR_OPTION, vec2((UI::width - w) * 0.5f, y + slot * h + 6 - h), vec2(w, h - 6), 1.0f, 0xFFD8377C, 0); + + float aw = w - 64.0f; + aw -= 4.0f; + + y = printBar((UI::width - w) * 0.5f, y, w, 0xFF0080FF, 101, slot == 0, Core::settings.audio.music); + y = printBar((UI::width - w) * 0.5f, y, w, 0xFFFF8000, 102, slot == 1, Core::settings.audio.sound); + y = printBool(x + 32.0f, y, w - 64.0f, STR_REVERBERATION, slot == 2, Core::settings.audio.reverb); + } + void renderItemText(Item *item) { if (item->type == TR::Entity::INV_PASSPORT && phaseChoose == 1.0f) { - StringID str = STR_LOAD_GAME; - - if (game->getLevel()->id == TR::TITLE) { - if (item->value == 1) str = STR_START_GAME; - if (item->value == 2) str = STR_EXIT_GAME; - } else { - if (item->value == 1) str = STR_RESTART_LEVEL; - if (item->value == 2) str = STR_EXIT_TO_TITLE; - } - - UI::textOut(vec2(0, 480 - 16), str, UI::aCenter, UI::width); + // } else - UI::textOut(vec2(0, 480 - 16), item->desc.str, UI::aCenter, UI::width); + UI::textOut(vec2(0, 480 - 32), item->desc.str, UI::aCenter, UI::width); renderItemCount(item, vec2(UI::width / 2 - 160, 480 - 96), 320); @@ -688,11 +851,15 @@ struct Inventory { renderPassport(item); break; case TR::Entity::INV_HOME : - break; case TR::Entity::INV_COMPASS : case TR::Entity::INV_MAP : - case TR::Entity::INV_DETAIL : + break; + case TR::Entity::INV_DETAIL : + renderDetail(item); + break; case TR::Entity::INV_SOUND : + renderSound(item); + break; case TR::Entity::INV_CONTROLS : case TR::Entity::INV_GAMMA : UI::textOut(vec2(0, 240), STR_NOT_IMPLEMENTED, UI::aCenter, UI::width); diff --git a/src/lara.h b/src/lara.h index b266344..e925911 100644 --- a/src/lara.h +++ b/src/lara.h @@ -440,6 +440,7 @@ struct Lara : Character { //reset(14, vec3(40448, 3584, 60928), PI * 0.5f, STAND_ONWATER); // gym (pool) //reset(0, vec3(74858, 3072, 20795), 0); // level 1 (dart) //reset(14, vec3(20215, 6656, 52942), PI); // level 1 (bridge) + //reset(20, vec3(8952, 3840, 68071), PI); // level 1 (crystal) //reset(33, vec3(48229, 4608, 78420), 270 * DEG2RAD); // level 1 (end) //reset(15, vec3(70067, -256, 29104), -0.68f); // level 2 (pool) //reset(26, vec3(71980, 1546, 19000), 270 * DEG2RAD); // level 2 (underwater switch) diff --git a/src/level.h b/src/level.h index 02f519b..d572bd8 100644 --- a/src/level.h +++ b/src/level.h @@ -36,9 +36,9 @@ struct Level : IGame { float clipHeight; } *params = (Params*)&Core::params; + ZoneCache *zoneCache; AmbientCache *ambientCache; WaterCache *waterCache; - ZoneCache *zoneCache; Sound::Sample *sndSoundtrack; Sound::Sample *sndUnderwater; @@ -69,6 +69,52 @@ struct Level : IGame { new Stream(buf, loadAsync); } + virtual void loadGame(int slot) { + // + } + + virtual void saveGame(int slot) { + // + } + + virtual void applySettings(const Core::Settings &settings) { + if (settings.detail.filter != Core::settings.detail.filter) + atlas->setFilterQuality(settings.detail.filter); + + bool rebuildMesh = settings.detail.water != Core::settings.detail.water; + bool rebuildAmbient = settings.detail.lighting != Core::settings.detail.lighting; + bool rebuildShadows = settings.detail.shadows != Core::settings.detail.shadows; + bool rebuildWater = settings.detail.water != Core::settings.detail.water; + bool rebuildShaders = rebuildWater || rebuildAmbient || rebuildShadows; + + Core::settings = settings; + + if (rebuildShaders) { + delete shaderCache; + shaderCache = new ShaderCache(); + } + + if (rebuildMesh) { + delete mesh; + mesh = new MeshBuilder(level); + } + + if (rebuildAmbient) { + delete ambientCache; + ambientCache = Core::settings.detail.lighting > Core::Settings::MEDIUM ? new AmbientCache(this) : NULL; + } + + if (rebuildShadows) { + delete shadow; + shadow = Core::settings.detail.shadows > Core::Settings::LOW ? new Texture(SHADOW_TEX_SIZE, SHADOW_TEX_SIZE, Texture::SHADOW, false) : NULL; + } + + if (rebuildWater) { + delete waterCache; + waterCache = Core::settings.detail.water > Core::Settings::LOW ? new WaterCache(this) : NULL; + } + } + virtual TR::Level* getLevel() { return &level; } @@ -108,10 +154,6 @@ struct Level : IGame { params->waterHeight = height; } - virtual void updateParams() { - Core::active.shader->setParam(uParam, Core::params); - } - virtual void waterDrop(const vec3 &pos, float radius, float strength) { if (waterCache) waterCache->addDrop(pos, radius, strength); @@ -399,10 +441,10 @@ struct Level : IGame { level.cameraController = camera; level.laraController = lara; - ambientCache = Core::settings.detail.ambient ? new AmbientCache(this) : NULL; - waterCache = Core::settings.detail.water ? new WaterCache(this) : NULL; zoneCache = new ZoneCache(this); - shadow = Core::settings.detail.shadows ? new Texture(SHADOW_TEX_SIZE, SHADOW_TEX_SIZE, Texture::SHADOW, false) : NULL; + ambientCache = Core::settings.detail.lighting > Core::Settings::MEDIUM ? new AmbientCache(this) : NULL; + waterCache = Core::settings.detail.water > Core::Settings::LOW ? new WaterCache(this) : NULL; + shadow = Core::settings.detail.shadows > Core::Settings::LOW ? new Texture(SHADOW_TEX_SIZE, SHADOW_TEX_SIZE, Texture::SHADOW, false) : NULL; initReflections(); @@ -460,7 +502,6 @@ struct Level : IGame { } static void fillCallback(int id, int width, int height, int tileX, int tileY, void *userData, void *data) { - static const uint32 whiteColor = 0xFFFFFFFF; static const uint32 barColor[UI::BAR_MAX][25] = { // health bar { 0xFF2C5D71, 0xFF5E81AE, 0xFF2C5D71, 0xFF1B4557, 0xFF16304F }, @@ -472,6 +513,8 @@ struct Level : IGame { 0x00FFFFFF, 0x80FFFFFF, 0x80FFFFFF, 0x80FFFFFF, 0x00FFFFFF, 0x00FFFFFF, 0x60FFFFFF, 0x60FFFFFF, 0x60FFFFFF, 0x00FFFFFF, 0x00FFFFFF, 0x20FFFFFF, 0x20FFFFFF, 0x20FFFFFF, 0x00FFFFFF }, + // white bar (white tile) + { 0xFFFFFFFF }, }; int stride = 256, uvCount; @@ -504,21 +547,20 @@ struct Level : IGame { uvCount = 4; switch (id) { - case UI::BAR_HEALTH : - case UI::BAR_OXYGEN : - case UI::BAR_OPTION : + case UI::BAR_HEALTH : + case UI::BAR_OXYGEN : + case UI::BAR_OPTION : + case UI::BAR_WHITE : src = (TR::Color32*)&barColor[id][0]; tex = &barTile[id]; - mm.w = 4; // height - 1 - if (id == UI::BAR_OPTION) { - stride = 5; - mm.z = 4; + if (id != UI::BAR_WHITE) { + mm.w = 4; // height - 1 + if (id == UI::BAR_OPTION) { + stride = 5; + mm.z = 4; + } } break; - case 3 : // white color - src = (TR::Color32*)&whiteColor; - tex = &whiteTile; - break; default : return; } @@ -588,7 +630,7 @@ struct Level : IGame { } // repack texture tiles - Atlas *tiles = new Atlas(level.objectTexturesCount + level.spriteTexturesCount + 4, &level, fillCallback); + Atlas *tiles = new Atlas(level.objectTexturesCount + level.spriteTexturesCount + UI::BAR_MAX, &level, fillCallback); // add textures int texIdx = level.version == TR::VER_TR1_PSX ? 256 : 0; // skip palette color for PSX version for (int i = texIdx; i < level.objectTexturesCount; i++) { @@ -626,8 +668,10 @@ struct Level : IGame { tiles->add(short4(8192, 8192, 8192 + 4, 8192 + 4), texIdx++); // add white color tiles->add(short4(2048, 2048, 2048, 2048), texIdx++); + // get result texture atlas = tiles->pack(); + atlas->setFilterQuality(Core::settings.detail.filter); delete tiles; @@ -711,8 +755,9 @@ struct Level : IGame { setWaterParams(float(room.info.yTop)); } + Core::active.shader->setParam(uParam, Core::params); Core::active.shader->setParam(uMaterial, vec4(diffuse, ambient, specular, alpha)); - if (Core::settings.detail.contact) + if (Core::settings.detail.shadows > Core::Settings::MEDIUM) Core::active.shader->setParam(uContacts, Core::contacts[0], MAX_CONTACTS); } @@ -733,7 +778,7 @@ struct Level : IGame { if (Core::pass == Core::passShadow) return; - if (Core::settings.detail.contact) { + if (Core::settings.detail.shadows > Core::Settings::MEDIUM) { Sphere spheres[MAX_CONTACTS]; int spheresCount; lara->getSpheres(spheres, spheresCount); @@ -835,7 +880,7 @@ struct Level : IGame { if (isModel) { // model vec3 pos = controller->getPos(); - if (Core::settings.detail.ambient) { + if (ambientCache) { AmbientCache::Cube cube; if (Core::stats.frame != controller->frameIndex) { ambientCache->getAmbient(roomIndex, pos, cube); @@ -1242,14 +1287,15 @@ struct Level : IGame { glLoadIdentity(); glOrtho(0, Core::width, 0, Core::height, 0, 1); - if (waterCache->reflect) - waterCache->reflect->bind(sDiffuse); + if (waterCache && waterCache->count && waterCache->items[0].caustics) + waterCache->items[0].caustics->bind(sDiffuse); else atlas->bind(sDiffuse); glEnable(GL_TEXTURE_2D); Core::setCulling(cfNone); Core::setDepthTest(false); Core::setBlending(bmNone); + Core::validateRenderState(); glColor3f(10, 10, 10); int w = Core::active.textures[sDiffuse]->width / 2; diff --git a/src/mesh.h b/src/mesh.h index 123c112..b07f7e9 100644 --- a/src/mesh.h +++ b/src/mesh.h @@ -5,7 +5,8 @@ #include "format.h" -TR::ObjectTexture whiteTile, barTile[3]; +TR::ObjectTexture barTile[4 /* UI::BAR_MAX */]; +TR::ObjectTexture &whiteTile = barTile[3]; struct MeshRange { int iStart; @@ -198,7 +199,7 @@ struct MeshBuilder { iCount += d.rCount * 6 + d.tCount * 3; vCount += d.rCount * 4 + d.tCount * 3; - if (Core::settings.detail.water) + if (Core::settings.detail.water > Core::Settings::LOW) roomRemoveWaterSurfaces(r, iCount, vCount); for (int j = 0; j < r.meshesCount; j++) { @@ -906,7 +907,7 @@ struct MeshBuilder { vCount += 4; } - void addBar(Index *indices, Vertex *vertices, int &iCount, int &vCount, const TR::ObjectTexture &tile, const vec2 &pos, const vec2 &size, uint32 color) { + void addBar(Index *indices, Vertex *vertices, int &iCount, int &vCount, const TR::ObjectTexture &tile, const vec2 &pos, const vec2 &size, uint32 color, uint32 color2 = 0) { addQuad(indices, iCount, vCount, 0, vertices, NULL); int16 minX = int16(pos.x); @@ -922,7 +923,10 @@ struct MeshBuilder { for (int i = 0; i < 4; i++) { Vertex &v = vertices[vCount + i]; v.normal = { 0, 0, 0, 0 }; - v.color = *((ubyte4*)&color); + if (color2 != 0 && (i == 0 || i == 3)) + v.color = *((ubyte4*)&color2); + else + v.color = *((ubyte4*)&color); short2 uv = tile.texCoord[i]; diff --git a/src/platform/web/index.html b/src/platform/web/index.html index 670b7e3..7b69642 100644 --- a/src/platform/web/index.html +++ b/src/platform/web/index.html @@ -117,7 +117,7 @@ (.PHD, .PSX)

OpenLara on github & facebook
-
last update: 16.09.2017
+
last update: 18.09.2017

diff --git a/src/shaders/shader.glsl b/src/shaders/shader.glsl index d12b0dd..64b0f22 100644 --- a/src/shaders/shader.glsl +++ b/src/shaders/shader.glsl @@ -1,7 +1,7 @@ R"====( #ifdef GL_ES - precision lowp int; - precision highp float; + precision lowp int; + precision highp float; #endif #ifdef OPT_CONTACT @@ -10,8 +10,9 @@ R"====( varying vec4 vTexCoord; // xy - atlas coords, zw - trapezoidal correction -#if defined(OPT_WATER) && defined(UNDERWATER) +#ifdef OPT_CAUSTICS varying vec2 vCausticsCoord; // - xy caustics texture coord + uniform vec4 uRoomSize; // xy - minXZ, zw - maxXZ #endif uniform mat4 uLightProj; @@ -20,7 +21,6 @@ uniform vec3 uViewPos; uniform vec4 uParam; // x - time, y - water height, z - clip plane sign, w - clip plane height uniform vec3 uLightPos[MAX_LIGHTS]; uniform vec4 uLightColor[MAX_LIGHTS]; // xyz - color, w - radius * intensity -uniform vec4 uRoomSize; // xy - minXZ, zw - maxXZ uniform vec4 uMaterial; // x - diffuse, y - ambient, z - specular, w - alpha @@ -45,31 +45,28 @@ uniform vec4 uMaterial; // x - diffuse, y - ambient, z - specular, w - alpha #ifdef VERTEX -#ifdef TYPE_ENTITY - #if defined(OPT_AMBIENT) - uniform vec3 uAmbient[6]; + #ifdef TYPE_ENTITY + uniform vec4 uBasis[32 * 2]; + #else + uniform vec4 uBasis[2]; #endif - uniform vec4 uBasis[32 * 2]; -#else - uniform vec4 uBasis[2]; -#endif -#ifndef PASS_SHADOW - #if defined(OPT_AMBIENT) && defined(TYPE_ENTITY) - vec3 calcAmbient(vec3 n) { - vec3 sqr = n * n; - vec3 pos = step(0.0, n); - return sqr.x * mix(uAmbient[1], uAmbient[0], pos.x) + - sqr.y * mix(uAmbient[3], uAmbient[2], pos.y) + - sqr.z * mix(uAmbient[5], uAmbient[4], pos.z); - } - #endif -#endif - -#if defined(PASS_COMPOSE) && defined(TYPE_ROOM) - uniform vec2 uAnimTexRanges[MAX_RANGES]; - uniform vec2 uAnimTexOffsets[MAX_OFFSETS]; -#endif + #ifdef OPT_AMBIENT + uniform vec3 uAmbient[6]; + + vec3 calcAmbient(vec3 n) { + vec3 sqr = n * n; + vec3 pos = step(0.0, n); + return sqr.x * mix(uAmbient[1], uAmbient[0], pos.x) + + sqr.y * mix(uAmbient[3], uAmbient[2], pos.y) + + sqr.z * mix(uAmbient[5], uAmbient[4], pos.z); + } + #endif + + #ifdef OPT_ANIMTEX + uniform vec2 uAnimTexRanges[MAX_RANGES]; + uniform vec2 uAnimTexOffsets[MAX_OFFSETS]; + #endif attribute vec4 aCoord; attribute vec4 aTexCoord; @@ -142,7 +139,7 @@ uniform vec4 uMaterial; // x - diffuse, y - ambient, z - specular, w - alpha } void _diffuse() { - #if !defined(PASS_SHADOW) + #ifndef PASS_SHADOW vDiffuse = vec4(aColor.xyz * (uMaterial.x * 2.0), uMaterial.w); #ifdef UNDERWATER @@ -182,6 +179,7 @@ uniform vec4 uMaterial; // x - diffuse, y - ambient, z - specular, w - alpha #endif #endif + lum.y = dot(vNormal.xyz, normalize(lv1)); att.y = dot(lv1, lv1); lum.z = dot(vNormal.xyz, normalize(lv2)); att.z = dot(lv2, lv2); lum.w = dot(vNormal.xyz, normalize(lv3)); att.w = dot(lv3, lv3); @@ -193,13 +191,11 @@ uniform vec4 uMaterial; // x - diffuse, y - ambient, z - specular, w - alpha vec3 ambient; #ifdef TYPE_ENTITY - #ifdef OPT_AMBIENT ambient = calcAmbient(vNormal.xyz); #else ambient = vec3(uMaterial.y); #endif - #else ambient = vec3(min(uMaterial.y, light.x)); #endif @@ -216,8 +212,8 @@ uniform vec4 uMaterial; // x - diffuse, y - ambient, z - specular, w - alpha #else vLight.xyz += light.x; #endif - #endif + #endif #ifdef PASS_AMBIENT @@ -229,7 +225,7 @@ uniform vec4 uMaterial; // x - diffuse, y - ambient, z - specular, w - alpha void _uv(vec3 coord) { vTexCoord = aTexCoord; #if defined(PASS_COMPOSE) && !defined(TYPE_SPRITE) - #ifdef TYPE_ROOM + #ifdef OPT_ANIMTEX // animated texture coordinates vec2 range = uAnimTexRanges[int(aParam.x)]; // x - start index, y - count float frame = fract((aParam.y + uParam.x * 4.0 - range.x) / range.y) * range.y; @@ -239,7 +235,7 @@ uniform vec4 uMaterial; // x - diffuse, y - ambient, z - specular, w - alpha vTexCoord.xy *= vTexCoord.zw; #endif - #if defined(OPT_WATER) && defined(UNDERWATER) + #ifdef OPT_CAUSTICS vCausticsCoord.xy = clamp((coord.xz - uRoomSize.xy) / (uRoomSize.zw - uRoomSize.xy), vec2(0.0), vec2(1.0)); #endif } @@ -263,149 +259,138 @@ uniform vec4 uMaterial; // x - diffuse, y - ambient, z - specular, w - alpha #else uniform sampler2D sDiffuse; - #if defined(UNDERWATER) && defined(OPT_WATER) - uniform sampler2D sReflect; + #if defined(PASS_COMPOSE) && defined(TYPE_MIRROR) + uniform samplerCube sEnvironment; #endif - #ifdef PASS_COMPOSE - #ifdef TYPE_MIRROR - uniform samplerCube sEnvironment; - #endif + #if defined(PASS_SHADOW) && defined(SHADOW_COLOR) + vec4 pack(in float value) { + vec4 bitSh = vec4(256.0*256.0*256.0, 256.0*256.0, 256.0, 1.0); + vec4 bitMsk = vec4(0.0, 1.0/256.0, 1.0/256.0, 1.0/256.0); + vec4 res = fract(value * bitSh); + res -= res.xxyz * bitMsk; + return res; + } #endif - #ifdef PASS_SHADOW - #ifdef SHADOW_COLOR - vec4 pack(in float value) { - vec4 bitSh = vec4(256.0*256.0*256.0, 256.0*256.0, 256.0, 1.0); - vec4 bitMsk = vec4(0.0, 1.0/256.0, 1.0/256.0, 1.0/256.0); - vec4 res = fract(value * bitSh); - res -= res.xxyz * bitMsk; - return res; - } - #endif - #endif - - #ifdef PASS_COMPOSE - - #if defined(OPT_SHADOW) && !defined(TYPE_FLASH) - #ifdef SHADOW_SAMPLER - uniform sampler2DShadow sShadow; - #ifdef GL_ES - #define SHADOW(V) (shadow2DEXT(sShadow, V)) - #else - #define SHADOW(V) (shadow2D(sShadow, V).x) - #endif + #ifdef OPT_SHADOW + #ifdef SHADOW_SAMPLER + uniform sampler2DShadow sShadow; + #ifdef GL_ES + #define SHADOW(V) (shadow2DEXT(sShadow, V)) #else - uniform sampler2D sShadow; - #define CMP(a,b) step(min(1.0, b), a) + #define SHADOW(V) (shadow2D(sShadow, V).x) + #endif + #else + uniform sampler2D sShadow; + #define CMP(a,b) step(min(1.0, b), a) - #ifdef SHADOW_DEPTH - #define compare(p, z) CMP(texture2D(sShadow, (p)).x, (z)); - #elif defined(SHADOW_COLOR) - float unpack(vec4 value) { - vec4 bitSh = vec4(1.0/(256.0*256.0*256.0), 1.0/(256.0*256.0), 1.0/256.0, 1.0); - return dot(value, bitSh); - } - #define compare(p, z) CMP(unpack(texture2D(sShadow, (p))), (z)); - #endif - - float SHADOW(vec3 p) { - return compare(p.xy, p.z); + #ifdef SHADOW_DEPTH + #define compare(p, z) CMP(texture2D(sShadow, (p)).x, (z)); + #elif defined(SHADOW_COLOR) + float unpack(vec4 value) { + vec4 bitSh = vec4(1.0/(256.0*256.0*256.0), 1.0/(256.0*256.0), 1.0/256.0, 1.0); + return dot(value, bitSh); } + #define compare(p, z) CMP(unpack(texture2D(sShadow, (p))), (z)); #endif - #define SHADOW_TEXEL (2.0 / SHADOW_TEX_SIZE) - - float random(vec3 seed, float freq) { - float dt = dot(floor(seed * freq), vec3(53.1215, 21.1352, 9.1322)); - return fract(sin(dt) * 2105.2354); - } - - float randomAngle(vec3 seed, float freq) { - return random(seed, freq) * 6.283285; - } - - vec3 rotate(vec2 sc, vec2 v) { - return vec3(v.x * sc.y + v.y * sc.x, v.x * -sc.x + v.y * sc.y, 0.0); - } - - float getShadow(vec4 lightProj) { - vec3 p = lightProj.xyz / lightProj.w; - - float rShadow = SHADOW(SHADOW_TEXEL * vec3(-0.93289, -0.03146, 0.0) + p) + - SHADOW(SHADOW_TEXEL * vec3( 0.81628, -0.05965, 0.0) + p) + - SHADOW(SHADOW_TEXEL * vec3(-0.18455, 0.97225, 0.0) + p) + - SHADOW(SHADOW_TEXEL * vec3( 0.04032, -0.85898, 0.0) + p); - - if (rShadow > 0.1 && rShadow < 3.9) { - float angle = randomAngle(vTexCoord.xyy, 15.0); - vec2 sc = vec2(sin(angle), cos(angle)); - - rShadow += SHADOW(SHADOW_TEXEL * rotate(sc, vec2(-0.54316, 0.21186)) + p); - rShadow += SHADOW(SHADOW_TEXEL * rotate(sc, vec2(-0.03925, -0.34345)) + p); - rShadow += SHADOW(SHADOW_TEXEL * rotate(sc, vec2( 0.07695, 0.40667)) + p); - rShadow += SHADOW(SHADOW_TEXEL * rotate(sc, vec2(-0.66378, -0.54068)) + p); - rShadow += SHADOW(SHADOW_TEXEL * rotate(sc, vec2(-0.54130, 0.66730)) + p); - rShadow += SHADOW(SHADOW_TEXEL * rotate(sc, vec2( 0.69301, 0.46990)) + p); - rShadow += SHADOW(SHADOW_TEXEL * rotate(sc, vec2( 0.37228, 0.03811)) + p); - rShadow += SHADOW(SHADOW_TEXEL * rotate(sc, vec2( 0.28597, 0.80228)) + p); - rShadow += SHADOW(SHADOW_TEXEL * rotate(sc, vec2( 0.44801, -0.43844)) + p); - rShadow /= 13.0; - } else - rShadow /= 4.0; - - float fade = clamp(dot(vLightVec.xyz, vLightVec.xyz), 0.0, 1.0); - return rShadow + (1.0 - rShadow) * fade; - } - - float getShadow() { - #ifdef TYPE_ROOM - float vis = min(dot(vNormal.xyz, vLightVec.xyz), vLightProj.w); - #else - float vis = vLightProj.w; - #endif - return vis > 0.0 ? getShadow(vLightProj) : 1.0; + float SHADOW(vec3 p) { + return compare(p.xy, p.z); } #endif - float calcSpecular(vec3 normal, vec3 viewVec, vec3 lightVec, vec4 color, float intensity) { - vec3 vv = normalize(viewVec); - vec3 rv = reflect(-vv, normal); - vec3 lv = normalize(lightVec); - return pow(max(0.0, dot(rv, lv)), 8.0) * intensity; + #define SHADOW_TEXEL (2.0 / SHADOW_TEX_SIZE) + + float random(vec3 seed, float freq) { + float dt = dot(floor(seed * freq), vec3(53.1215, 21.1352, 9.1322)); + return fract(sin(dt) * 2105.2354); } - #if defined(OPT_WATER) && defined(UNDERWATER) - float calcCaustics(vec3 n) { - vec2 cc = vCausticsCoord.xy; - vec2 border = vec2(256.0) / (uRoomSize.zw - uRoomSize.xy); - vec2 fade = smoothstep(vec2(0.0), border, cc) * (1.0 - smoothstep(vec2(1.0) - border, vec2(1.0), cc)); - return texture2D(sReflect, cc).g * max(0.0, -n.y) * fade.x * fade.y; - } - #endif + float randomAngle(vec3 seed, float freq) { + return random(seed, freq) * 6.283285; + } + + vec3 rotate(vec2 sc, vec2 v) { + return vec3(v.x * sc.y + v.y * sc.x, v.x * -sc.x + v.y * sc.y, 0.0); + } + + float getShadow(vec4 lightProj) { + vec3 p = lightProj.xyz / lightProj.w; + + float rShadow = SHADOW(SHADOW_TEXEL * vec3(-0.93289, -0.03146, 0.0) + p) + + SHADOW(SHADOW_TEXEL * vec3( 0.81628, -0.05965, 0.0) + p) + + SHADOW(SHADOW_TEXEL * vec3(-0.18455, 0.97225, 0.0) + p) + + SHADOW(SHADOW_TEXEL * vec3( 0.04032, -0.85898, 0.0) + p); + + if (rShadow > 0.1 && rShadow < 3.9) { + float angle = randomAngle(vTexCoord.xyy, 15.0); + vec2 sc = vec2(sin(angle), cos(angle)); + + rShadow += SHADOW(SHADOW_TEXEL * rotate(sc, vec2(-0.54316, 0.21186)) + p); + rShadow += SHADOW(SHADOW_TEXEL * rotate(sc, vec2(-0.03925, -0.34345)) + p); + rShadow += SHADOW(SHADOW_TEXEL * rotate(sc, vec2( 0.07695, 0.40667)) + p); + rShadow += SHADOW(SHADOW_TEXEL * rotate(sc, vec2(-0.66378, -0.54068)) + p); + rShadow += SHADOW(SHADOW_TEXEL * rotate(sc, vec2(-0.54130, 0.66730)) + p); + rShadow += SHADOW(SHADOW_TEXEL * rotate(sc, vec2( 0.69301, 0.46990)) + p); + rShadow += SHADOW(SHADOW_TEXEL * rotate(sc, vec2( 0.37228, 0.03811)) + p); + rShadow += SHADOW(SHADOW_TEXEL * rotate(sc, vec2( 0.28597, 0.80228)) + p); + rShadow += SHADOW(SHADOW_TEXEL * rotate(sc, vec2( 0.44801, -0.43844)) + p); + rShadow /= 13.0; + } else + rShadow /= 4.0; + + float fade = clamp(dot(vLightVec.xyz, vLightVec.xyz), 0.0, 1.0); + return rShadow + (1.0 - rShadow) * fade; + } + + float getShadow() { + #ifdef TYPE_ROOM + float vis = min(dot(vNormal.xyz, vLightVec.xyz), vLightProj.w); + #else + float vis = vLightProj.w; + #endif + return vis > 0.0 ? getShadow(vLightProj) : 1.0; + } #endif -#ifdef OPT_CONTACT - uniform vec4 uContacts[MAX_CONTACTS]; + #ifdef OPT_CAUSTICS + uniform sampler2D sReflect; - float getContactAO(vec3 p, vec3 n) { - float res = 1.0; - for (int i = 0; i < MAX_CONTACTS; i++) { - vec3 v = uContacts[i].xyz - p; - float a = uContacts[i].w; - float o = a * clamp(dot(n, v), 0.0, 1.0) / dot(v, v); - res *= clamp(1.0 - o, 0.0, 1.0); + float calcCaustics(vec3 n) { + vec2 cc = vCausticsCoord.xy; + vec2 border = vec2(256.0) / (uRoomSize.zw - uRoomSize.xy); + vec2 fade = smoothstep(vec2(0.0), border, cc) * (1.0 - smoothstep(vec2(1.0) - border, vec2(1.0), cc)); + return texture2D(sReflect, cc).x * max(0.0, -n.y) * fade.x * fade.y; } - return res; + #endif + + #ifdef OPT_CONTACT + uniform vec4 uContacts[MAX_CONTACTS]; + + float getContactAO(vec3 p, vec3 n) { + float res = 1.0; + for (int i = 0; i < MAX_CONTACTS; i++) { + vec3 v = uContacts[i].xyz - p; + float a = uContacts[i].w; + float o = a * clamp(dot(n, v), 0.0, 1.0) / dot(v, v); + res *= clamp(1.0 - o, 0.0, 1.0); + } + return res; + } + #endif + + float calcSpecular(vec3 normal, vec3 viewVec, vec3 lightVec, vec4 color, float intensity) { + vec3 vv = normalize(viewVec); + vec3 rv = reflect(-vv, normal); + vec3 lv = normalize(lightVec); + return pow(max(0.0, dot(rv, lv)), 8.0) * intensity; } -#endif void main() { - #ifdef PASS_COMPOSE - #ifdef CLIP_PLANE - if (vViewVec.w * uParam.z > uParam.w) - discard; - #endif + #ifdef CLIP_PLANE + if (vViewVec.w * uParam.z > uParam.w) + discard; #endif vec4 color; @@ -442,57 +427,56 @@ uniform vec4 uMaterial; // x - diffuse, y - ambient, z - specular, w - alpha #else #ifndef TYPE_FLASH - #ifdef OPT_SHADOW - #ifdef PASS_COMPOSE - vec3 n = normalize(vNormal.xyz); + #ifdef PASS_AMBIENT + color.xyz *= vLight.x; + #endif + + #ifdef PASS_COMPOSE + + vec3 n = normalize(vNormal.xyz); + + #ifdef TYPE_ENTITY + float rSpecular = uMaterial.z + 0.03; + #endif + + #ifdef OPT_SHADOW vec3 light = uLightColor[1].xyz * vLight.y + uLightColor[2].xyz * vLight.z; - #ifdef TYPE_ENTITY + #if defined(TYPE_ENTITY) || defined(TYPE_ROOM) float rShadow = getShadow(); + #endif + + #ifdef TYPE_ENTITY + rSpecular *= rShadow; light += vAmbient + uLightColor[0].xyz * (vLight.x * rShadow); - #if defined(OPT_WATER) && defined(UNDERWATER) - light += calcCaustics(n); - #endif #endif - + #ifdef TYPE_ROOM - - light += mix(vAmbient.x, vLight.x, getShadow()); - #if defined(OPT_WATER) && defined(UNDERWATER) - light += calcCaustics(n); - #endif - - #ifdef OPT_CONTACT - light *= getContactAO(vCoord, n) * 0.5 + 0.5; - #endif - + light += mix(vAmbient.x, vLight.x, rShadow); #endif - + #ifdef TYPE_SPRITE light += vLight.x; #endif - - #ifndef TYPE_MIRROR - color.xyz *= light; - #endif - - #ifdef TYPE_ENTITY - color.xyz += calcSpecular(n, vViewVec.xyz, vLightVec.xyz, uLightColor[0], (uMaterial.z + 0.03) * rShadow); - #endif + #else + vec3 light = vLight.xyz; #endif - #ifdef PASS_AMBIENT - color.xyz *= vLight.x; + #ifdef OPT_CAUSTICS + light += calcCaustics(n); #endif - #else - #ifndef TYPE_MIRROR - color.xyz *= vLight.xyz; + #ifdef OPT_CONTACT + light *= getContactAO(vCoord, n) * 0.5 + 0.5; + #endif + + color.xyz *= light; + + #ifdef TYPE_ENTITY + color.xyz += calcSpecular(n, vViewVec.xyz, vLightVec.xyz, uLightColor[0], rSpecular); #endif - #endif - #if defined(PASS_COMPOSE) && !defined(TYPE_FLASH) #ifdef UNDERWATER color.xyz = mix(UNDERWATER_COLOR * 0.2, color.xyz, vLightVec.w); #else diff --git a/src/shaders/water.glsl b/src/shaders/water.glsl index 328f8ab..f7a0d6b 100644 --- a/src/shaders/water.glsl +++ b/src/shaders/water.glsl @@ -165,7 +165,7 @@ uniform sampler2D sNormal; float rOldArea = length(dFdx(vOldPos.xyz)) * length(dFdy(vOldPos.xyz)); float rNewArea = length(dFdx(vNewPos.xyz)) * length(dFdy(vNewPos.xyz)); float value = clamp(rOldArea / rNewArea * 0.2, 0.0, 1.0) * vOldPos.w; - return vec4(0.0, value, 0.0, 0.0); + return vec4(value, 0.0, 0.0, 0.0); } vec4 mask() { diff --git a/src/sound.h b/src/sound.h index 5994d1f..a6ed111 100644 --- a/src/sound.h +++ b/src/sound.h @@ -531,8 +531,12 @@ namespace Sound { i += res; } // 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 v = volume * m; vec2 pan = getPan(); - vec2 vol = pan * volume; + vec2 vol = pan * VOL_CONV(v); for (int j = 0; j < i; j++) { if (volumeDelta != 0.0f) { // increase / decrease channel volume volume += volumeDelta; @@ -543,11 +547,14 @@ namespace Sound { if (stopAfterFade) isPlaying = false; } - vol = pan * volume; + v = volume * m; + vol = pan * VOL_CONV(v); } frames[j].L = int(frames[j].L * vol.x); frames[j].R = int(frames[j].R * vol.y); } + #undef VOL_CONV + return true; } diff --git a/src/texture.h b/src/texture.h index 8f3762a..3b2be22 100644 --- a/src/texture.h +++ b/src/texture.h @@ -68,7 +68,7 @@ struct Texture { 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_MAG_FILTER, filter ? GL_LINEAR : GL_NEAREST); - + struct FormatDesc { GLuint ifmt, fmt; GLenum type; @@ -119,8 +119,8 @@ struct Texture { if (mips) { glGenerateMipmap(target); - if (!cube && filter && Core::support.texAniso) - glTexParameteri(target, GL_TEXTURE_MAX_ANISOTROPY_EXT, min(int(Core::support.texAniso), 8)); + if (!cube && filter && (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); } } @@ -129,6 +129,18 @@ struct Texture { glDeleteTextures(1, &ID); } + void setFilterQuality(Core::Settings::Quality value) { + bool filter = value > Core::Settings::LOW; + bool mips = value > Core::Settings::MEDIUM; + + 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_MAG_FILTER, filter ? GL_LINEAR : GL_NEAREST); + } + void bind(int sampler) { if (Core::active.textures[sampler] != this) { Core::active.textures[sampler] = this; diff --git a/src/trigger.h b/src/trigger.h index 1c68142..4992703 100644 --- a/src/trigger.h +++ b/src/trigger.h @@ -373,6 +373,7 @@ struct Door : Controller { s.ceiling = TR::NO_FLOOR; if (sectors[i].boxIndex != TR::NO_BOX) { + ASSERT(sectors[i].boxIndex < level->boxesCount); TR::Box &box = level->boxes[sectors[i].boxIndex]; if (box.overlap.blockable) box.overlap.block = true; diff --git a/src/ui.h b/src/ui.h index 7f8a070..8e743c3 100644 --- a/src/ui.h +++ b/src/ui.h @@ -6,10 +6,16 @@ enum StringID { STR_NOT_IMPLEMENTED -// help +// common , STR_LOADING , STR_HELP_PRESS , STR_HELP_TEXT + , STR_OFF + , STR_ON + , STR_QUALITY_LOW + , STR_QUALITY_MEDIUM + , STR_QUALITY_HIGH + , STR_APPLY // inventory pages , STR_OPTION , STR_INVENTORY @@ -31,6 +37,15 @@ enum StringID { , STR_EXIT_TO_TITLE , STR_EXIT_GAME , STR_SELECT_LEVEL +// detail options + , STR_SELECT_DETAIL + , STR_OPT_DETAIL_FILTER + , STR_OPT_DETAIL_LIGHTING + , STR_OPT_DETAIL_SHADOWS + , STR_OPT_DETAIL_WATER +// sound options + , STR_SET_VOLUMES + , STR_REVERBERATION // inventory items , STR_UNKNOWN , STR_PISTOLS @@ -84,6 +99,12 @@ const char *STR[STR_MAX] = { , "Loading..." , "Press H for help" , helpText + , "Off" + , "On" + , "Low" + , "Medium" + , "High" + , "Apply" // inventory pages , "OPTION" , "INVENTORY" @@ -97,7 +118,7 @@ const char *STR[STR_MAX] = { , "Sound" , "Controls" , "Gamma" -// passport options +// passport menu , "Autosave" , "Load Game" , "Start Game" @@ -105,6 +126,15 @@ const char *STR[STR_MAX] = { , "Exit to Title" , "Exit Game" , "Select Level" +// detail options + , "Select Detail" + , "Filtering" + , "Lighting" + , "Shadows" + , "Water" +// sound options + , "Set Volumes" + , "Reverberation" // inventory items , "Unknown" , "Pistols" @@ -144,7 +174,13 @@ namespace UI { enum Align { aLeft, aRight, aCenter }; inline int charRemap(char c) { - return c > 10 ? (c > 15 ? char_map[c - 32] : c + 91) : c + 81; + ASSERT(c <= 126); + if (c < 11) + return c + 81; + if (c < 16) + return c + 91; + ASSERT(c >= 32) + return char_map[c - 32]; } vec2 getTextSize(const char *text) { @@ -171,6 +207,7 @@ namespace UI { BAR_HEALTH, BAR_OXYGEN, BAR_OPTION, + BAR_WHITE, BAR_MAX, }; @@ -259,6 +296,19 @@ namespace UI { textOut(pos, STR[str], align, width); } + void specOut(const vec2 &pos, char specChar) { + TR::Level *level = game->getLevel(); + MeshBuilder *mesh = game->getMesh(); + + int seq = level->extra.glyphSeq; + + if (buffer.iCount == MAX_CHARS * 6) + flush(); + + TR::SpriteTexture &sprite = level->spriteTextures[level->spriteSequences[seq].sStart + specChar]; + mesh->addSprite(buffer.indices, buffer.vertices, buffer.iCount, buffer.vCount, 0, int(pos.x), int(pos.y), 0, sprite, 255, true); + } + #undef MAX_CHARS /* Texture *texInv, *texAction; @@ -334,15 +384,15 @@ namespace UI { Core::setDepthTest(true); } - void renderBar(BarType type, const vec2 &pos, const vec2 &size, float value, uint32 fgColor = 0xFFFFFFFF, uint32 bgColor = 0x80000000, uint32 brColor1 = 0xFF4C504C, uint32 brColor2 = 0xFF748474) { + void renderBar(BarType type, const vec2 &pos, const vec2 &size, float value, uint32 fgColor = 0xFFFFFFFF, uint32 bgColor = 0x80000000, uint32 brColor1 = 0xFF4C504C, uint32 brColor2 = 0xFF748474, uint32 fgColor2 = 0) { MeshBuilder *mesh = game->getMesh(); if (brColor1 != 0 || brColor2 != 0) mesh->addFrame(buffer.indices, buffer.vertices, buffer.iCount, buffer.vCount, pos - 2.0f, size + 4.0f, brColor1, brColor2); if (bgColor != 0) mesh->addBar(buffer.indices, buffer.vertices, buffer.iCount, buffer.vCount, whiteTile, pos - 1.0f, size + 2.0f, bgColor); - if (fgColor != 0 && value > 0.0f) - mesh->addBar(buffer.indices, buffer.vertices, buffer.iCount, buffer.vCount, barTile[type], pos, vec2(size.x * value, size.y), fgColor); + if ((fgColor != 0 || fgColor2 != 0) && value > 0.0f) + mesh->addBar(buffer.indices, buffer.vertices, buffer.iCount, buffer.vCount, barTile[type], pos, vec2(size.x * value, size.y), fgColor, fgColor2); } void renderHelp() { diff --git a/src/utils.h b/src/utils.h index 0012fcf..8d09f67 100644 --- a/src/utils.h +++ b/src/utils.h @@ -1030,10 +1030,15 @@ struct Stream { } static void write(const char *name, const void *data, int size) { + #ifdef __EMSCRIPTEN__ + extern void osSave(const char *name, const void *data, int size); + osSave(name, data, size); + #else FILE *f = fopen(name, "wb"); if (!f) return; fwrite(data, size, 1, f); fclose(f); + #endif } void setPos(int pos) {