1
0
mirror of https://github.com/XProger/OpenLara.git synced 2025-08-20 03:41:33 +02:00

#11 add details and sound settings menus; #14 fix wolves base state

This commit is contained in:
XProger
2017-09-18 04:30:05 +03:00
parent 9b1a5580b6
commit 432bc8868c
18 changed files with 818 additions and 502 deletions

View File

@@ -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);

View File

@@ -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() {}

View File

@@ -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() {

View File

@@ -487,6 +487,8 @@ struct Wolf : Enemy {
jointChest = 2;
jointHead = 3;
nextState = STATE_NONE;
animation.time = animation.timeMax;
updateAnimation(false);
}
virtual int getStateGround() {

View File

@@ -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) {

View File

@@ -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]);
}

View File

@@ -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);

View File

@@ -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)

View File

@@ -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;

View File

@@ -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];

View File

@@ -117,7 +117,7 @@
<input type="button" value="Browse Level" onclick="document.getElementById('browseFile').click();" /> (.PHD, .PSX)
<p style="margin:8px">
OpenLara on <a target="_blank" href="https://github.com/XProger/OpenLara">github</a> & <a target="_blank" href="https://www.facebook.com/OpenLaraTR">facebook</a><br>
<br><i>last update: 16.09.2017</i><br>
<br><i>last update: 18.09.2017</i><br>
</p>
</span>

View File

@@ -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

View File

@@ -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() {

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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() {

View File

@@ -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) {