mirror of
https://github.com/XProger/OpenLara.git
synced 2025-08-09 14:47:02 +02:00
This commit is contained in:
@@ -70,6 +70,9 @@ struct ShaderCache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
compile(Core::passFilter, Shader::FILTER_DOWNSAMPLE, 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);
|
||||||
|
|
||||||
compile(Core::passCompose, Shader::ROOM, FX_NONE);
|
compile(Core::passCompose, Shader::ROOM, FX_NONE);
|
||||||
compile(Core::passCompose, Shader::ROOM, FX_ALPHA_TEST);
|
compile(Core::passCompose, Shader::ROOM, FX_ALPHA_TEST);
|
||||||
@@ -153,7 +156,7 @@ struct ShaderCache {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Core::passFilter : {
|
case Core::passFilter : {
|
||||||
static const char *typeNames[] = { "DEFAULT", "DOWNSAMPLE" };
|
static const char *typeNames[] = { "DEFAULT", "DOWNSAMPLE", "GRAYSCALE", "BLUR", "MIXER" };
|
||||||
src = FILTER;
|
src = FILTER;
|
||||||
typ = typeNames[type];
|
typ = typeNames[type];
|
||||||
sprintf(def, "%s#define PASS_%s\n#define FILTER_%s\n", ext, passNames[pass], typ);
|
sprintf(def, "%s#define PASS_%s\n#define FILTER_%s\n", ext, passNames[pass], typ);
|
||||||
@@ -654,9 +657,8 @@ struct WaterCache {
|
|||||||
Item &item = items[i];
|
Item &item = items[i];
|
||||||
if (!item.visible) continue;
|
if (!item.visible) continue;
|
||||||
|
|
||||||
item.mask->bind(sMask);
|
|
||||||
|
|
||||||
if (item.timer >= SIMULATE_TIMESTEP || dropCount) {
|
if (item.timer >= SIMULATE_TIMESTEP || dropCount) {
|
||||||
|
item.mask->bind(sMask);
|
||||||
// add water drops
|
// add water drops
|
||||||
drop(item);
|
drop(item);
|
||||||
// simulation step
|
// simulation step
|
||||||
|
@@ -277,7 +277,8 @@ struct Camera : Controller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mat4 getProjMatrix() {
|
mat4 getProjMatrix() {
|
||||||
return mat4(fov, Core::viewport.z / Core::viewport.w, znear, zfar);
|
return mat4(fov, float(Core::width) / float(Core::height), znear, zfar);
|
||||||
|
//return mat4(fov, Core::viewport.z / Core::viewport.w, znear, zfar);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void setup(bool calcMatrices) {
|
virtual void setup(bool calcMatrices) {
|
||||||
|
@@ -26,9 +26,15 @@ struct IGame {
|
|||||||
virtual void updateParams() {}
|
virtual void updateParams() {}
|
||||||
virtual void waterDrop(const vec3 &pos, float radius, float strength) {}
|
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 setShader(Core::Pass pass, Shader::Type type, bool underwater = false, bool alphaTest = false) {}
|
||||||
|
virtual void setupBinding() {}
|
||||||
virtual void renderEnvironment(int roomIndex, const vec3 &pos, Texture **targets, int stride = 0) {}
|
virtual void renderEnvironment(int roomIndex, const vec3 &pos, Texture **targets, int stride = 0) {}
|
||||||
virtual void renderCompose(int roomIndex, bool genShadowMask = false) {}
|
virtual void renderCompose(int roomIndex, bool genShadowMask = false) {}
|
||||||
virtual void fxQuake(float time) {}
|
virtual void fxQuake(float time) {}
|
||||||
|
|
||||||
|
virtual bool invUse(TR::Entity::Type item, TR::Entity::Type slot) { return false; }
|
||||||
|
virtual void invAdd(TR::Entity::Type type, int count = 1) {}
|
||||||
|
|
||||||
|
virtual Sound::Sample* playSound(int id, const vec3 &pos, int flags, int group = -1) const { return NULL; }
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Controller {
|
struct Controller {
|
||||||
@@ -181,25 +187,7 @@ struct Controller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Sound::Sample* playSound(int id, const vec3 &pos, int flags) const {
|
Sound::Sample* playSound(int id, const vec3 &pos, int flags) const {
|
||||||
if (level->version == TR::Level::VER_TR1_PSX && id == TR::SND_SECRET)
|
return game->playSound(id, pos, flags, entity);
|
||||||
return NULL;
|
|
||||||
|
|
||||||
int16 a = level->soundsMap[id];
|
|
||||||
if (a == -1) return NULL;
|
|
||||||
|
|
||||||
TR::SoundInfo &b = level->soundsInfo[a];
|
|
||||||
if (b.chance == 0 || (rand() & 0x7fff) <= b.chance) {
|
|
||||||
int index = b.offset + rand() % b.flags.count;
|
|
||||||
float volume = (float)b.volume / 0x7FFF;
|
|
||||||
float pitch = b.flags.pitch ? (0.9f + randf() * 0.2f) : 1.0f;
|
|
||||||
if (b.flags.mode == 1) flags |= Sound::UNIQUE;
|
|
||||||
//if (b.flags.mode == 2) flags |= Sound::REPLAY;
|
|
||||||
if (b.flags.mode == 3) flags |= Sound::SYNC;
|
|
||||||
if (b.flags.gain) volume = max(0.0f, volume - randf() * 0.25f);
|
|
||||||
if (b.flags.fixed) flags |= Sound::LOOP;
|
|
||||||
return Sound::play(level->getSampleStream(index), pos, volume, pitch, flags, entity * 1000 + index);
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
vec3 getDir() const {
|
vec3 getDir() const {
|
||||||
|
32
src/core.h
32
src/core.h
@@ -123,6 +123,11 @@
|
|||||||
#define glActiveStencilFaceEXT(...)
|
#define glActiveStencilFaceEXT(...)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
float deltaTime;
|
||||||
|
int width, height;
|
||||||
|
}
|
||||||
|
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "input.h"
|
#include "input.h"
|
||||||
#include "sound.h"
|
#include "sound.h"
|
||||||
@@ -290,8 +295,6 @@ enum BlendMode { bmNone, bmAlpha, bmAdd, bmMultiply, bmScreen };
|
|||||||
extern int getTime();
|
extern int getTime();
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
int width, height;
|
|
||||||
float deltaTime;
|
|
||||||
float eye;
|
float eye;
|
||||||
vec4 viewport, viewportDef;
|
vec4 viewport, viewportDef;
|
||||||
vec4 scissor;
|
vec4 scissor;
|
||||||
@@ -307,6 +310,8 @@ namespace Core {
|
|||||||
enum Pass { passCompose, passShadow, passAmbient, passWater, passFilter, passVolume, passGUI, passMAX } pass;
|
enum Pass { passCompose, passShadow, passAmbient, passWater, passFilter, passVolume, passGUI, passMAX } pass;
|
||||||
|
|
||||||
GLuint FBO, defaultFBO;
|
GLuint FBO, defaultFBO;
|
||||||
|
Texture *defaultTarget;
|
||||||
|
|
||||||
struct RenderTargetCache {
|
struct RenderTargetCache {
|
||||||
int count;
|
int count;
|
||||||
struct Item {
|
struct Item {
|
||||||
@@ -322,6 +327,8 @@ namespace Core {
|
|||||||
Texture *target;
|
Texture *target;
|
||||||
int targetFace;
|
int targetFace;
|
||||||
GLuint VAO;
|
GLuint VAO;
|
||||||
|
GLuint iBuffer;
|
||||||
|
GLuint vBuffer;
|
||||||
BlendMode blendMode;
|
BlendMode blendMode;
|
||||||
CullMode cullMode;
|
CullMode cullMode;
|
||||||
bool stencilTwoSide;
|
bool stencilTwoSide;
|
||||||
@@ -371,7 +378,7 @@ namespace Core {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void init() {
|
void init() {
|
||||||
Input::reset();
|
Input::init();
|
||||||
#ifdef ANDROID
|
#ifdef ANDROID
|
||||||
void *libGL = dlopen("libGLESv2.so", RTLD_LAZY);
|
void *libGL = dlopen("libGLESv2.so", RTLD_LAZY);
|
||||||
#endif
|
#endif
|
||||||
@@ -499,6 +506,7 @@ namespace Core {
|
|||||||
glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&defaultFBO);
|
glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&defaultFBO);
|
||||||
glGenFramebuffers(1, &FBO);
|
glGenFramebuffers(1, &FBO);
|
||||||
memset(rtCache, 0, sizeof(rtCache));
|
memset(rtCache, 0, sizeof(rtCache));
|
||||||
|
defaultTarget = NULL;
|
||||||
|
|
||||||
Sound::init();
|
Sound::init();
|
||||||
|
|
||||||
@@ -564,9 +572,13 @@ namespace Core {
|
|||||||
glClearStencil(value);
|
glClearStencil(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setViewport(const vec4 &vp) {
|
||||||
|
glViewport(int(vp.x), int(vp.y), int(vp.z), int(vp.w));
|
||||||
|
viewport = vp;
|
||||||
|
}
|
||||||
|
|
||||||
void setViewport(int x, int y, int width, int height) {
|
void setViewport(int x, int y, int width, int height) {
|
||||||
glViewport(x, y, width, height);
|
setViewport(vec4(float(x), float(y), float(width), float(height)));
|
||||||
viewport = vec4(float(x), float(y), float(width), float(height));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void setScissor(int x, int y, int width, int height) {
|
void setScissor(int x, int y, int width, int height) {
|
||||||
@@ -699,16 +711,17 @@ namespace Core {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void setTarget(Texture *target, bool clear = false, int face = 0) {
|
void setTarget(Texture *target, bool clear = false, int face = 0) {
|
||||||
if (target == active.target && face == active.targetFace)
|
if (!target && defaultTarget)
|
||||||
return;
|
target = defaultTarget;
|
||||||
|
|
||||||
|
if (target != active.target || face != active.targetFace) {
|
||||||
if (!target) {
|
if (!target) {
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO);
|
glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO);
|
||||||
glColorMask(true, true, true, true);
|
glColorMask(true, true, true, true);
|
||||||
|
|
||||||
setViewport(int(viewportDef.x), int(viewportDef.y), int(viewportDef.z), int(viewportDef.w));
|
setViewport(int(viewportDef.x), int(viewportDef.y), int(viewportDef.z), int(viewportDef.w));
|
||||||
} else {
|
} else {
|
||||||
if (active.target == NULL)
|
if (active.target == NULL || active.target == defaultTarget)
|
||||||
viewportDef = viewport;
|
viewportDef = viewport;
|
||||||
GLenum texTarget = GL_TEXTURE_2D;
|
GLenum texTarget = GL_TEXTURE_2D;
|
||||||
if (target->cube)
|
if (target->cube)
|
||||||
@@ -723,8 +736,11 @@ namespace Core {
|
|||||||
|
|
||||||
if (depth)
|
if (depth)
|
||||||
glColorMask(false, false, false, false);
|
glColorMask(false, false, false, false);
|
||||||
|
else
|
||||||
|
glColorMask(true, true, true, true);
|
||||||
setViewport(0, 0, target->width, target->height);
|
setViewport(0, 0, target->width, target->height);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (clear)
|
if (clear)
|
||||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
||||||
|
99
src/format.h
99
src/format.h
@@ -80,7 +80,7 @@
|
|||||||
E( BRIDGE_0 ) \
|
E( BRIDGE_0 ) \
|
||||||
E( BRIDGE_1 ) \
|
E( BRIDGE_1 ) \
|
||||||
E( BRIDGE_2 ) \
|
E( BRIDGE_2 ) \
|
||||||
E( INV_GAME ) \
|
E( INV_PASSPORT ) \
|
||||||
E( INV_COMPASS ) \
|
E( INV_COMPASS ) \
|
||||||
E( INV_HOME ) \
|
E( INV_HOME ) \
|
||||||
E( GEARS_1 ) \
|
E( GEARS_1 ) \
|
||||||
@@ -90,7 +90,7 @@
|
|||||||
E( CUT_2 ) \
|
E( CUT_2 ) \
|
||||||
E( CUT_3 ) \
|
E( CUT_3 ) \
|
||||||
E( CUT_4 ) \
|
E( CUT_4 ) \
|
||||||
E( INV_GAME_CLOSED ) \
|
E( INV_PASSPORT_CLOSED ) \
|
||||||
E( INV_MAP ) \
|
E( INV_MAP ) \
|
||||||
E( CRYSTAL ) \
|
E( CRYSTAL ) \
|
||||||
E( WEAPON_PISTOLS ) \
|
E( WEAPON_PISTOLS ) \
|
||||||
@@ -107,12 +107,12 @@
|
|||||||
E( INV_DETAIL ) \
|
E( INV_DETAIL ) \
|
||||||
E( INV_SOUND ) \
|
E( INV_SOUND ) \
|
||||||
E( INV_CONTROLS ) \
|
E( INV_CONTROLS ) \
|
||||||
E( INV_FLASHLIGHT ) \
|
E( INV_GAMMA ) \
|
||||||
E( INV_PISTOLS ) \
|
E( INV_PISTOLS ) \
|
||||||
E( INV_SHOTGUN ) \
|
E( INV_SHOTGUN ) \
|
||||||
E( INV_MAGNUMS ) \
|
E( INV_MAGNUMS ) \
|
||||||
E( INV_UZIS ) \
|
E( INV_UZIS ) \
|
||||||
E( INV_AMMO_POSTOLS ) \
|
E( INV_AMMO_PISTOLS ) \
|
||||||
E( INV_AMMO_SHOTGUN ) \
|
E( INV_AMMO_SHOTGUN ) \
|
||||||
E( INV_AMMO_MAGNUMS ) \
|
E( INV_AMMO_MAGNUMS ) \
|
||||||
E( INV_AMMO_UZIS ) \
|
E( INV_AMMO_UZIS ) \
|
||||||
@@ -291,14 +291,14 @@ namespace TR {
|
|||||||
|
|
||||||
SND_UNDERWATER = 60,
|
SND_UNDERWATER = 60,
|
||||||
|
|
||||||
SND_MENU_SPIN = 108,
|
SND_INV_SPIN = 108,
|
||||||
SND_MENU_HOME = 109,
|
SND_INV_HOME = 109,
|
||||||
SND_MENU_CONTROLS = 110,
|
SND_INV_CONTROLS = 110,
|
||||||
SND_MENU_SHOW = 111,
|
SND_INV_SHOW = 111,
|
||||||
SND_MENU_HIDE = 112,
|
SND_INV_HIDE = 112,
|
||||||
SND_MENU_COMPASS = 113,
|
SND_INV_COMPASS = 113,
|
||||||
SND_MENU_WEAPON = 114,
|
SND_INV_WEAPON = 114,
|
||||||
SND_MENU_PAGE = 115,
|
SND_INV_PAGE = 115,
|
||||||
SND_HEALTH = 116,
|
SND_HEALTH = 116,
|
||||||
|
|
||||||
SND_DART = 151,
|
SND_DART = 151,
|
||||||
@@ -987,6 +987,29 @@ namespace TR {
|
|||||||
int16 puzzleSet;
|
int16 puzzleSet;
|
||||||
int16 weapons[4];
|
int16 weapons[4];
|
||||||
int16 braid;
|
int16 braid;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
int16 passport;
|
||||||
|
int16 passport_closed;
|
||||||
|
int16 map;
|
||||||
|
int16 compass;
|
||||||
|
int16 home;
|
||||||
|
int16 detail;
|
||||||
|
int16 sound;
|
||||||
|
int16 controls;
|
||||||
|
int16 gamma;
|
||||||
|
|
||||||
|
int16 weapon[4];
|
||||||
|
int16 ammo[4];
|
||||||
|
int16 medikit[2];
|
||||||
|
int16 puzzle[4];
|
||||||
|
int16 key[4];
|
||||||
|
|
||||||
|
int16 leadbar;
|
||||||
|
int16 scion;
|
||||||
|
} inv;
|
||||||
|
|
||||||
|
int16 glyphSeq;
|
||||||
} extra;
|
} extra;
|
||||||
|
|
||||||
Level(Stream &stream, bool demo) {
|
Level(Stream &stream, bool demo) {
|
||||||
@@ -1201,10 +1224,7 @@ namespace TR {
|
|||||||
// init secrets states
|
// init secrets states
|
||||||
memset(secrets, 0, MAX_SECRETS_COUNT * sizeof(secrets[0]));
|
memset(secrets, 0, MAX_SECRETS_COUNT * sizeof(secrets[0]));
|
||||||
// get special models indices
|
// get special models indices
|
||||||
memset(&extra, 0, sizeof(extra));
|
memset(&extra, 0xFF, sizeof(extra));
|
||||||
for (int i = 0; i < 4; i++)
|
|
||||||
extra.weapons[i] = -1;
|
|
||||||
extra.braid = -1;
|
|
||||||
|
|
||||||
for (int i = 0; i < modelsCount; i++)
|
for (int i = 0; i < modelsCount; i++)
|
||||||
switch (models[i].type) {
|
switch (models[i].type) {
|
||||||
@@ -1215,8 +1235,53 @@ namespace TR {
|
|||||||
case Entity::LARA_MAGNUMS : extra.weapons[2] = i; break;
|
case Entity::LARA_MAGNUMS : extra.weapons[2] = i; break;
|
||||||
case Entity::LARA_UZIS : extra.weapons[3] = i; break;
|
case Entity::LARA_UZIS : extra.weapons[3] = i; break;
|
||||||
case Entity::BRAID : extra.braid = i; break;
|
case Entity::BRAID : extra.braid = i; break;
|
||||||
|
|
||||||
|
case Entity::INV_PASSPORT : extra.inv.passport = i; break;
|
||||||
|
case Entity::INV_PASSPORT_CLOSED : extra.inv.passport_closed = i; break;
|
||||||
|
case Entity::INV_MAP : extra.inv.map = i; break;
|
||||||
|
case Entity::INV_COMPASS : extra.inv.compass = i; break;
|
||||||
|
case Entity::INV_HOME : extra.inv.home = i; break;
|
||||||
|
case Entity::INV_DETAIL : extra.inv.detail = i; break;
|
||||||
|
case Entity::INV_SOUND : extra.inv.sound = i; break;
|
||||||
|
case Entity::INV_CONTROLS : extra.inv.controls = i; break;
|
||||||
|
case Entity::INV_GAMMA : extra.inv.gamma = i; break;
|
||||||
|
|
||||||
|
case Entity::INV_PISTOLS : extra.inv.weapon[0] = i; break;
|
||||||
|
case Entity::INV_SHOTGUN : extra.inv.weapon[1] = i; break;
|
||||||
|
case Entity::INV_MAGNUMS : extra.inv.weapon[2] = i; break;
|
||||||
|
case Entity::INV_UZIS : extra.inv.weapon[3] = i; break;
|
||||||
|
|
||||||
|
case Entity::INV_AMMO_PISTOLS : extra.inv.ammo[0] = i; break;
|
||||||
|
case Entity::INV_AMMO_SHOTGUN : extra.inv.ammo[1] = i; break;
|
||||||
|
case Entity::INV_AMMO_MAGNUMS : extra.inv.ammo[2] = i; break;
|
||||||
|
case Entity::INV_AMMO_UZIS : extra.inv.ammo[3] = i; break;
|
||||||
|
|
||||||
|
case Entity::INV_MEDIKIT_SMALL : extra.inv.medikit[0] = i; break;
|
||||||
|
case Entity::INV_MEDIKIT_BIG : extra.inv.medikit[1] = i; break;
|
||||||
|
|
||||||
|
case Entity::INV_PUZZLE_1 : extra.inv.puzzle[0] = i; break;
|
||||||
|
case Entity::INV_PUZZLE_2 : extra.inv.puzzle[1] = i; break;
|
||||||
|
case Entity::INV_PUZZLE_3 : extra.inv.puzzle[2] = i; break;
|
||||||
|
case Entity::INV_PUZZLE_4 : extra.inv.puzzle[3] = i; break;
|
||||||
|
|
||||||
|
case Entity::INV_KEY_1 : extra.inv.key[0] = i; break;
|
||||||
|
case Entity::INV_KEY_2 : extra.inv.key[1] = i; break;
|
||||||
|
case Entity::INV_KEY_3 : extra.inv.key[2] = i; break;
|
||||||
|
case Entity::INV_KEY_4 : extra.inv.key[3] = i; break;
|
||||||
|
|
||||||
|
case Entity::INV_LEADBAR : extra.inv.leadbar = i; break;
|
||||||
|
case Entity::INV_SCION : extra.inv.scion = i; break;
|
||||||
|
|
||||||
default : ;
|
default : ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < spriteSequencesCount; i++)
|
||||||
|
if (spriteSequences[i].type == TR::Entity::GLYPH) {
|
||||||
|
extra.glyphSeq = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ASSERT(extra.glyphSeq != -1);
|
||||||
|
|
||||||
// init cutscene transform
|
// init cutscene transform
|
||||||
cutMatrix.identity();
|
cutMatrix.identity();
|
||||||
if (cutEntity > -1) {
|
if (cutEntity > -1) {
|
||||||
@@ -1665,7 +1730,7 @@ namespace TR {
|
|||||||
return Color24(255, 0, 255);
|
return Color24(255, 0, 255);
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream* getSampleStream(int index) {
|
Stream* getSampleStream(int index) const {
|
||||||
uint8 *data = &soundData[soundOffsets[index]];
|
uint8 *data = &soundData[soundOffsets[index]];
|
||||||
uint32 size = 0;
|
uint32 size = 0;
|
||||||
switch (version) {
|
switch (version) {
|
||||||
|
20
src/game.h
20
src/game.h
@@ -8,13 +8,11 @@
|
|||||||
|
|
||||||
namespace Game {
|
namespace Game {
|
||||||
Level *level;
|
Level *level;
|
||||||
UI *ui;
|
|
||||||
|
|
||||||
void startLevel(Stream *lvl, Stream *snd, bool demo, bool home) {
|
void startLevel(Stream *lvl, Stream *snd, bool demo, bool home) {
|
||||||
delete ui;
|
|
||||||
delete level;
|
delete level;
|
||||||
level = new Level(*lvl, snd, demo, home);
|
level = new Level(*lvl, snd, demo, home);
|
||||||
ui = new UI(level);
|
UI::init(level);
|
||||||
delete lvl;
|
delete lvl;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,7 +25,6 @@ namespace Game {
|
|||||||
Core::settings.water = Core::support.texFloat || Core::support.texHalf;
|
Core::settings.water = Core::support.texFloat || Core::support.texHalf;
|
||||||
|
|
||||||
level = NULL;
|
level = NULL;
|
||||||
ui = NULL;
|
|
||||||
startLevel(lvl, snd, false, false);
|
startLevel(lvl, snd, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,12 +37,14 @@ namespace Game {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void free() {
|
void free() {
|
||||||
delete ui;
|
|
||||||
delete level;
|
delete level;
|
||||||
Core::free();
|
Core::free();
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateTick() {
|
void updateTick() {
|
||||||
|
if (Input::state[cInventory])
|
||||||
|
level->inventory.toggle();
|
||||||
|
|
||||||
float dt = Core::deltaTime;
|
float dt = Core::deltaTime;
|
||||||
if (Input::down[ikR]) // slow motion (for animation debugging)
|
if (Input::down[ikR]) // slow motion (for animation debugging)
|
||||||
Core::deltaTime /= 10.0f;
|
Core::deltaTime /= 10.0f;
|
||||||
@@ -60,13 +59,15 @@ namespace Game {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void update(float delta) {
|
void update(float delta) {
|
||||||
|
Input::update();
|
||||||
|
|
||||||
if (Input::down[ikV]) { // third <-> first person view
|
if (Input::down[ikV]) { // third <-> first person view
|
||||||
level->camera->changeView(!level->camera->firstPerson);
|
level->camera->changeView(!level->camera->firstPerson);
|
||||||
Input::down[ikV] = false;
|
Input::down[ikV] = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Core::deltaTime = delta = min(1.0f, delta);
|
Core::deltaTime = delta = min(1.0f, delta);
|
||||||
ui->update();
|
UI::update();
|
||||||
|
|
||||||
while (delta > EPS) {
|
while (delta > EPS) {
|
||||||
Core::deltaTime = min(delta, 1.0f / 30.0f);
|
Core::deltaTime = min(delta, 1.0f / 30.0f);
|
||||||
@@ -79,7 +80,12 @@ namespace Game {
|
|||||||
PROFILE_TIMING(Core::stats.tFrame);
|
PROFILE_TIMING(Core::stats.tFrame);
|
||||||
Core::beginFrame();
|
Core::beginFrame();
|
||||||
level->render();
|
level->render();
|
||||||
ui->renderTouch();
|
UI::renderTouch();
|
||||||
|
|
||||||
|
#ifdef _DEBUG
|
||||||
|
level->renderDebug();
|
||||||
|
#endif
|
||||||
|
|
||||||
Core::endFrame();
|
Core::endFrame();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
202
src/input.h
202
src/input.h
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
enum InputKey { ikNone,
|
enum InputKey { ikNone,
|
||||||
// keyboard
|
// keyboard
|
||||||
ikLeft, ikRight, ikUp, ikDown, ikSpace, ikEnter, ikEscape, ikShift, ikCtrl, ikAlt,
|
ikLeft, ikRight, ikUp, ikDown, ikSpace, ikTab, ikEnter, ikEscape, ikShift, ikCtrl, ikAlt,
|
||||||
ik0, ik1, ik2, ik3, ik4, ik5, ik6, ik7, ik8, ik9,
|
ik0, ik1, ik2, ik3, ik4, ik5, ik6, ik7, ik8, ik9,
|
||||||
ikA, ikB, ikC, ikD, ikE, ikF, ikG, ikH, ikI, ikJ, ikK, ikL, ikM,
|
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,
|
ikN, ikO, ikP, ikQ, ikR, ikS, ikT, ikU, ikV, ikW, ikX, ikY, ikZ,
|
||||||
@@ -15,12 +15,55 @@ enum InputKey { ikNone,
|
|||||||
ikTouchA, ikTouchB, ikTouchC, ikTouchD, ikTouchE, ikTouchF,
|
ikTouchA, ikTouchB, ikTouchC, ikTouchD, ikTouchE, ikTouchF,
|
||||||
// gamepad
|
// gamepad
|
||||||
ikJoyA, ikJoyB, ikJoyX, ikJoyY, ikJoyLB, ikJoyRB, ikJoySelect, ikJoyStart, ikJoyL, ikJoyR, ikJoyLT, ikJoyRT, ikJoyPOV,
|
ikJoyA, ikJoyB, ikJoyX, ikJoyY, ikJoyLB, ikJoyRB, ikJoySelect, ikJoyStart, ikJoyL, ikJoyR, ikJoyLT, ikJoyRT, ikJoyPOV,
|
||||||
|
ikJoyLeft, ikJoyRight, ikJoyUp, ikJoyDown,
|
||||||
ikMAX };
|
ikMAX };
|
||||||
|
|
||||||
|
enum ControlKey { cLeft, cRight, cUp, cDown, cJump, cWalk, cAction, cWeapon, cLook, cStepLeft, cStepRight, cRoll, cInventory, cMAX };
|
||||||
|
|
||||||
namespace Input {
|
namespace Input {
|
||||||
|
|
||||||
bool down[ikMAX];
|
bool down[ikMAX];
|
||||||
|
|
||||||
|
struct KeySet {
|
||||||
|
InputKey key, joy;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const KeySet presets[1][cMAX] = {
|
||||||
|
{ { ikLeft, ikJoyLeft },
|
||||||
|
{ ikRight, ikJoyRight },
|
||||||
|
{ ikUp, ikJoyUp },
|
||||||
|
{ ikDown, ikJoyDown },
|
||||||
|
{ ikAlt, 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];
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
vec2 pos;
|
vec2 pos;
|
||||||
struct {
|
struct {
|
||||||
@@ -68,13 +111,15 @@ namespace Input {
|
|||||||
}
|
}
|
||||||
} head;
|
} head;
|
||||||
|
|
||||||
void reset() {
|
enum TouchButton { bNone, bWeapon, bWalk, bAction, bJump, bMAX };
|
||||||
memset(down, 0, sizeof(down));
|
enum TouchZone { zMove, zLook, zButton, zMAX };
|
||||||
memset(&mouse, 0, sizeof(mouse));
|
|
||||||
memset(&joy, 0, sizeof(joy));
|
float touchTimerVis, touchTimerTap;
|
||||||
memset(&touch, 0, sizeof(touch));
|
InputKey touchKey[zMAX];
|
||||||
head.reset();
|
TouchButton btn;
|
||||||
}
|
vec2 btnPos[bMAX];
|
||||||
|
float btnRadius;
|
||||||
|
bool doubleTap;
|
||||||
|
|
||||||
void setDown(InputKey key, bool value) {
|
void setDown(InputKey key, bool value) {
|
||||||
if (down[key] == value)
|
if (down[key] == value)
|
||||||
@@ -130,6 +175,147 @@ namespace Input {
|
|||||||
|
|
||||||
return ikNone;
|
return ikNone;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
memset(down, 0, sizeof(down));
|
||||||
|
memset(&mouse, 0, sizeof(mouse));
|
||||||
|
memset(&joy, 0, sizeof(joy));
|
||||||
|
memset(&touch, 0, sizeof(touch));
|
||||||
|
head.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void init() {
|
||||||
|
reset();
|
||||||
|
for (int i = 0; i < cMAX; i++)
|
||||||
|
controls[i] = presets[0][i];
|
||||||
|
|
||||||
|
touchTimerVis = 0.0f;
|
||||||
|
touchTimerTap = 0.0f;
|
||||||
|
doubleTap = false;
|
||||||
|
touchKey[zMove] = touchKey[zLook] = touchKey[zButton] = ikNone;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool checkTouchZone(TouchZone zone) {
|
||||||
|
InputKey &t = touchKey[zone];
|
||||||
|
if (t != ikNone && !down[t]) {
|
||||||
|
t = ikNone;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void getTouchDir(InputKey key, vec2 &dir) {
|
||||||
|
vec2 delta = vec2(0.0f);
|
||||||
|
if (key == ikNone)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Touch &t = touch[key - ikTouchA];
|
||||||
|
vec2 d = t.pos - t.start;
|
||||||
|
float len = d.length();
|
||||||
|
if (len > EPS)
|
||||||
|
delta = d * (min(len / 100.0f, 1.0f) / len);
|
||||||
|
|
||||||
|
dir = delta;
|
||||||
|
}
|
||||||
|
|
||||||
|
void update() {
|
||||||
|
int p = joy.POV;
|
||||||
|
setDown(ikJoyUp, p == 8 || p == 1 || p == 2);
|
||||||
|
setDown(ikJoyRight, p == 2 || p == 3 || p == 4);
|
||||||
|
setDown(ikJoyDown, p == 4 || p == 5 || p == 6);
|
||||||
|
setDown(ikJoyLeft, p == 6 || p == 7 || p == 8);
|
||||||
|
|
||||||
|
for (int i = 0; i < cMAX; i++) {
|
||||||
|
KeySet &c = controls[i];
|
||||||
|
state[i] = (c.key != ikNone && down[c.key]) || (c.joy != ikNone && down[c.joy]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// update touch controls
|
||||||
|
if (touchTimerTap > 0.0f)
|
||||||
|
touchTimerTap = max(0.0f, touchTimerTap - Core::deltaTime);
|
||||||
|
|
||||||
|
if (touchKey[zMove] != ikNone || touchKey[zLook] != ikNone || touchKey[zButton] != ikNone)
|
||||||
|
touchTimerVis = 30.0f;
|
||||||
|
else
|
||||||
|
if (touchTimerVis > 0.0f)
|
||||||
|
touchTimerVis = max(0.0f, touchTimerVis - Core::deltaTime);
|
||||||
|
|
||||||
|
// update buttons
|
||||||
|
float offset = Core::height * 0.25f;
|
||||||
|
float radius = offset;
|
||||||
|
vec2 center = vec2(Core::width - offset * 0.7f, Core::height - offset * 0.7f);
|
||||||
|
|
||||||
|
btnPos[bWeapon] = center;
|
||||||
|
btnPos[bJump] = center + vec2(cos(-PI * 0.5f), sin(-PI * 0.5f)) * radius;
|
||||||
|
btnPos[bAction] = center + vec2(cos(-PI * 3.0f / 4.0f), sin(-PI * 3.0f / 4.0f)) * radius;
|
||||||
|
btnPos[bWalk] = center + vec2(cos(-PI), sin(-PI)) * radius;
|
||||||
|
btnRadius = Core::height * (25.0f / 1080.0f);
|
||||||
|
|
||||||
|
// touch update
|
||||||
|
if (checkTouchZone(zMove))
|
||||||
|
joy.L = vec2(0.0f);
|
||||||
|
|
||||||
|
if (checkTouchZone(zLook))
|
||||||
|
joy.R = vec2(0.0f);
|
||||||
|
|
||||||
|
if (checkTouchZone(zButton))
|
||||||
|
btn = bNone;
|
||||||
|
|
||||||
|
if (doubleTap)
|
||||||
|
doubleTap = false;
|
||||||
|
|
||||||
|
float zoneSize = Core::width / 3.0f;
|
||||||
|
|
||||||
|
for (int i = 0; i < COUNT(touch); i++) {
|
||||||
|
InputKey key = InputKey(i + ikTouchA);
|
||||||
|
|
||||||
|
if (!down[key]) continue;
|
||||||
|
if (key == touchKey[zMove] || key == touchKey[zLook] || key == touchKey[zButton]) continue;
|
||||||
|
|
||||||
|
int zone = clamp(int(touch[i].pos.x / zoneSize), 0, 2);
|
||||||
|
InputKey &t = touchKey[zone];
|
||||||
|
|
||||||
|
if (t == ikNone) {
|
||||||
|
t = key;
|
||||||
|
if (zone == zMove) {
|
||||||
|
if (touchTimerTap > 0.0f && touchTimerTap < 0.3f) { // 100 ms gap to reduce false positives (bad touch sensors)
|
||||||
|
doubleTap = true;
|
||||||
|
touchTimerTap = 0.0f;
|
||||||
|
} else
|
||||||
|
touchTimerTap = 0.3f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// set active touches as gamepad controls
|
||||||
|
getTouchDir(touchKey[zMove], joy.L);
|
||||||
|
getTouchDir(touchKey[zLook], joy.R);
|
||||||
|
|
||||||
|
if (touchKey[zButton] != ikNone) {
|
||||||
|
vec2 pos = touch[touchKey[zButton] - ikTouchA].pos;
|
||||||
|
|
||||||
|
btn = bMAX;
|
||||||
|
float minDist = btnRadius * 8.0f;
|
||||||
|
for (int i = 0; i < bMAX; i++) {
|
||||||
|
float d = (pos - btnPos[i]).length();
|
||||||
|
if (d < minDist) {
|
||||||
|
minDist = d;
|
||||||
|
btn = TouchButton(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (btn) {
|
||||||
|
case bWeapon : state[cWeapon] = true; break;
|
||||||
|
case bWalk : state[cWalk] = true; break;
|
||||||
|
case bAction : state[cAction] = true; break;
|
||||||
|
case bJump : state[cJump] = true; break;
|
||||||
|
default : ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (doubleTap)
|
||||||
|
state[cRoll] = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
541
src/inventory.h
541
src/inventory.h
@@ -2,18 +2,175 @@
|
|||||||
#define H_INVENTORY
|
#define H_INVENTORY
|
||||||
|
|
||||||
#include "format.h"
|
#include "format.h"
|
||||||
|
#include "controller.h"
|
||||||
|
#include "ui.h"
|
||||||
|
|
||||||
#define MAX_ITEMS 64
|
#define INVENTORY_MAX_ITEMS 64
|
||||||
|
#define INVENTORY_MAX_RADIUS 688.0f
|
||||||
|
#define INVENTORY_BG_SIZE 512
|
||||||
|
#define INVENTORY_HEIGHT 2048.0f
|
||||||
|
|
||||||
struct Inventory {
|
struct Inventory {
|
||||||
|
|
||||||
|
enum Page {
|
||||||
|
PAGE_OPTION,
|
||||||
|
PAGE_INVENTORY,
|
||||||
|
PAGE_ITEMS,
|
||||||
|
PAGE_MAX
|
||||||
|
};
|
||||||
|
|
||||||
|
IGame *game;
|
||||||
|
Texture *background[3];
|
||||||
|
|
||||||
|
bool active;
|
||||||
|
bool chosen;
|
||||||
|
float phaseRing, phasePage, phaseChoose, phaseSelect;
|
||||||
|
int index, targetIndex, pageItemIndex[PAGE_MAX];
|
||||||
|
Page page, targetPage;
|
||||||
|
int itemsCount;
|
||||||
|
|
||||||
struct Item {
|
struct Item {
|
||||||
TR::Entity::Type type;
|
TR::Entity::Type type;
|
||||||
int count;
|
int count;
|
||||||
} items[MAX_ITEMS];
|
float angle;
|
||||||
int itemsCount;
|
Animation *anim;
|
||||||
|
|
||||||
Inventory() : itemsCount(0) {}
|
struct Desc {
|
||||||
|
const char *name;
|
||||||
|
int page;
|
||||||
|
int model;
|
||||||
|
} desc;
|
||||||
|
|
||||||
|
Item() : anim(NULL) {}
|
||||||
|
|
||||||
|
Item(TR::Level *level, TR::Entity::Type type, int count = 1) : type(type), count(count), angle(0.0f) {
|
||||||
|
switch (type) {
|
||||||
|
case TR::Entity::INV_PASSPORT : desc = { "Game", 0, level->extra.inv.passport }; break;
|
||||||
|
case TR::Entity::INV_PASSPORT_CLOSED : desc = { "Game", 0, level->extra.inv.passport_closed }; break;
|
||||||
|
case TR::Entity::INV_MAP : desc = { "Map", 1, level->extra.inv.map }; break;
|
||||||
|
case TR::Entity::INV_COMPASS : desc = { "Compass", 1, level->extra.inv.compass }; break;
|
||||||
|
case TR::Entity::INV_HOME : desc = { "Lara's Home", 0, level->extra.inv.home }; break;
|
||||||
|
case TR::Entity::INV_DETAIL : desc = { "Detail Levels", 0, level->extra.inv.detail }; break;
|
||||||
|
case TR::Entity::INV_SOUND : desc = { "Sound", 0, level->extra.inv.sound }; break;
|
||||||
|
case TR::Entity::INV_CONTROLS : desc = { "Controls", 0, level->extra.inv.controls }; break;
|
||||||
|
case TR::Entity::INV_GAMMA : desc = { "Gamma", 0, level->extra.inv.gamma }; break;
|
||||||
|
|
||||||
|
case TR::Entity::INV_PISTOLS : desc = { "Pistols", 1, level->extra.inv.weapon[0] }; break;
|
||||||
|
case TR::Entity::INV_SHOTGUN : desc = { "Shotgun", 1, level->extra.inv.weapon[1] }; break;
|
||||||
|
case TR::Entity::INV_MAGNUMS : desc = { "Magnums", 1, level->extra.inv.weapon[2] }; break;
|
||||||
|
case TR::Entity::INV_UZIS : desc = { "Uzis", 1, level->extra.inv.weapon[3] }; break;
|
||||||
|
|
||||||
|
case TR::Entity::INV_AMMO_PISTOLS : desc = { "Pistol Clips", 1, level->extra.inv.ammo[0] }; break;
|
||||||
|
case TR::Entity::INV_AMMO_SHOTGUN : desc = { "Shotgun Shells", 1, level->extra.inv.ammo[1] }; break;
|
||||||
|
case TR::Entity::INV_AMMO_MAGNUMS : desc = { "Magnum Clips", 1, level->extra.inv.ammo[2] }; break;
|
||||||
|
case TR::Entity::INV_AMMO_UZIS : desc = { "Uzi Clips", 1, level->extra.inv.ammo[3] }; break;
|
||||||
|
|
||||||
|
case TR::Entity::INV_MEDIKIT_SMALL : desc = { "Small Medi Pack", 1, level->extra.inv.medikit[0] }; break;
|
||||||
|
case TR::Entity::INV_MEDIKIT_BIG : desc = { "Large Medi Pack", 1, level->extra.inv.medikit[1] }; break;
|
||||||
|
|
||||||
|
case TR::Entity::INV_PUZZLE_1 : desc = { "Puzzle", 2, level->extra.inv.puzzle[0] }; break;
|
||||||
|
case TR::Entity::INV_PUZZLE_2 : desc = { "Puzzle", 2, level->extra.inv.puzzle[1] }; break;
|
||||||
|
case TR::Entity::INV_PUZZLE_3 : desc = { "Puzzle", 2, level->extra.inv.puzzle[2] }; break;
|
||||||
|
case TR::Entity::INV_PUZZLE_4 : desc = { "Puzzle", 2, level->extra.inv.puzzle[3] }; break;
|
||||||
|
|
||||||
|
case TR::Entity::INV_KEY_1 : desc = { "Key", 2, level->extra.inv.key[0] }; break;
|
||||||
|
case TR::Entity::INV_KEY_2 : desc = { "Key", 2, level->extra.inv.key[1] }; break;
|
||||||
|
case TR::Entity::INV_KEY_3 : desc = { "Key", 2, level->extra.inv.key[2] }; break;
|
||||||
|
case TR::Entity::INV_KEY_4 : desc = { "Key", 2, level->extra.inv.key[3] }; break;
|
||||||
|
|
||||||
|
case TR::Entity::INV_LEADBAR : desc = { "Lead Bar", 2, level->extra.inv.leadbar }; break;
|
||||||
|
case TR::Entity::INV_SCION : desc = { "Scion", 2, level->extra.inv.scion }; break;
|
||||||
|
default : desc = { "unknown", 2, -1 }; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (desc.model > -1) {
|
||||||
|
anim = new Animation(level, &level->models[desc.model]);
|
||||||
|
anim->isEnded = true;
|
||||||
|
} else
|
||||||
|
anim = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
~Item() {
|
||||||
|
delete anim;
|
||||||
|
}
|
||||||
|
|
||||||
|
Item& operator = (Item &item) {
|
||||||
|
memcpy(this, &item, sizeof(item));
|
||||||
|
item.anim = NULL;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
if (anim) {
|
||||||
|
anim->setAnim(0, 0, false);
|
||||||
|
anim->isEnded = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void update() {
|
||||||
|
if (anim) anim->update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void render(IGame *game, const Basis &basis) {
|
||||||
|
if (!anim) return;
|
||||||
|
|
||||||
|
TR::Level *level = game->getLevel();
|
||||||
|
TR::Model &m = level->models[desc.model];
|
||||||
|
Basis joints[34];
|
||||||
|
|
||||||
|
anim->getJoints(basis, -1, true, joints);
|
||||||
|
|
||||||
|
Core::active.shader->setParam(uBasis, joints[0], m.mCount);
|
||||||
|
|
||||||
|
game->getMesh()->renderModel(desc.model);
|
||||||
|
}
|
||||||
|
|
||||||
|
void choose() {
|
||||||
|
if (anim) anim->setAnim(0, 0, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
} items[INVENTORY_MAX_ITEMS];
|
||||||
|
|
||||||
|
Inventory(IGame *game) : game(game), active(false), chosen(false), index(0), targetIndex(0), page(PAGE_OPTION), targetPage(PAGE_OPTION), itemsCount(0) {
|
||||||
|
TR::Level *level = game->getLevel();
|
||||||
|
|
||||||
|
add(TR::Entity::INV_PASSPORT);
|
||||||
|
add(TR::Entity::INV_DETAIL);
|
||||||
|
add(TR::Entity::INV_SOUND);
|
||||||
|
add(TR::Entity::INV_CONTROLS);
|
||||||
|
add(TR::Entity::INV_COMPASS);
|
||||||
|
|
||||||
|
if (level->extra.inv.map)
|
||||||
|
add(TR::Entity::INV_MAP);
|
||||||
|
if (level->extra.inv.gamma)
|
||||||
|
add(TR::Entity::INV_GAMMA);
|
||||||
|
|
||||||
|
add(TR::Entity::INV_PISTOLS, 999);
|
||||||
|
add(TR::Entity::INV_SHOTGUN, 999);
|
||||||
|
add(TR::Entity::INV_MAGNUMS, 999);
|
||||||
|
add(TR::Entity::INV_UZIS, 999);
|
||||||
|
add(TR::Entity::INV_MEDIKIT_SMALL, 999);
|
||||||
|
add(TR::Entity::INV_MEDIKIT_BIG, 999);
|
||||||
|
|
||||||
|
add(TR::Entity::INV_SCION, 1);
|
||||||
|
add(TR::Entity::INV_KEY_1, 1);
|
||||||
|
add(TR::Entity::INV_PUZZLE_1, 1);
|
||||||
|
|
||||||
|
phaseRing = phasePage = phaseChoose = phaseSelect = 0.0f;
|
||||||
|
memset(pageItemIndex, 0, sizeof(pageItemIndex));
|
||||||
|
|
||||||
|
for (int i = 0; i < COUNT(background); i++)
|
||||||
|
background[i] = new Texture(INVENTORY_BG_SIZE, INVENTORY_BG_SIZE, Texture::RGBA, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
~Inventory() {
|
||||||
|
for (int i = 0; i < COUNT(background); i++)
|
||||||
|
delete background[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isActive() {
|
||||||
|
return active || phaseRing > 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
int contains(TR::Entity::Type type) {
|
int contains(TR::Entity::Type type) {
|
||||||
for (int i = 0; i < itemsCount; i++)
|
for (int i = 0; i < itemsCount; i++)
|
||||||
@@ -29,11 +186,21 @@ struct Inventory {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(itemsCount < MAX_ITEMS) {
|
ASSERT(itemsCount < INVENTORY_MAX_ITEMS);
|
||||||
items[itemsCount].type = type;
|
|
||||||
items[itemsCount].count = count;
|
int pos = 0;
|
||||||
itemsCount++;
|
for (int pos = 0; pos < itemsCount; pos++)
|
||||||
|
if (items[pos].type > type)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (pos - itemsCount) {
|
||||||
|
for (int i = itemsCount; i > pos; i--)
|
||||||
|
items[i] = items[i - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Item it(game->getLevel(), type, count);
|
||||||
|
items[pos] = it;
|
||||||
|
itemsCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
int getCount(TR::Entity::Type type) {
|
int getCount(TR::Entity::Type type) {
|
||||||
@@ -47,6 +214,364 @@ struct Inventory {
|
|||||||
if (i > -1)
|
if (i > -1)
|
||||||
items[i].count -= count;
|
items[i].count -= count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool use(TR::Entity::Type item, TR::Entity::Type slot) {
|
||||||
|
if (item == TR::Entity::NONE) {
|
||||||
|
switch (slot) {
|
||||||
|
case TR::Entity::KEY_HOLE_1 : item = TR::Entity::KEY_1; break; // TODO: 1-4
|
||||||
|
case TR::Entity::PUZZLE_HOLE_1 : item = TR::Entity::PUZZLE_1; break;
|
||||||
|
default : return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getCount(item) > 0) {
|
||||||
|
remove(item);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool toggle(Page curPage = PAGE_INVENTORY) {
|
||||||
|
if (phaseRing == 0.0f || phaseRing == 1.0f) {
|
||||||
|
active = !active;
|
||||||
|
vec3 p;
|
||||||
|
game->playSound(active ? TR::SND_INV_SHOW : TR::SND_INV_HIDE, p, 0, 0);
|
||||||
|
chosen = false;
|
||||||
|
|
||||||
|
if (active) {
|
||||||
|
for (int i = 0; i < itemsCount; i++)
|
||||||
|
items[i].reset();
|
||||||
|
|
||||||
|
phasePage = 1.0f;
|
||||||
|
phaseSelect = 1.0f;
|
||||||
|
page = targetPage = curPage;
|
||||||
|
index = targetIndex = pageItemIndex[page];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return active;
|
||||||
|
}
|
||||||
|
|
||||||
|
void doPhase(bool increase, float speed, float &value) {
|
||||||
|
if (increase) {
|
||||||
|
if (value < 1.0f) {
|
||||||
|
value += Core::deltaTime * speed;
|
||||||
|
if (value > 1.0f)
|
||||||
|
value = 1.0f;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (value > 0.0f) {
|
||||||
|
value -= Core::deltaTime * speed;
|
||||||
|
if (value < 0.0f)
|
||||||
|
value = 0.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int getItemIndex(Page page, int index) {
|
||||||
|
for (int i = 0; i < itemsCount; i++)
|
||||||
|
if (items[i].desc.page == page) {
|
||||||
|
if (!index)
|
||||||
|
return i;
|
||||||
|
index--;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void update() {
|
||||||
|
doPhase(active, 2.0f, phaseRing);
|
||||||
|
doPhase(true, 1.6f, phasePage);
|
||||||
|
doPhase(chosen, 4.0f, phaseChoose);
|
||||||
|
doPhase(true, 2.5f, phaseSelect);
|
||||||
|
|
||||||
|
if (page != targetPage && phasePage == 1.0f) {
|
||||||
|
page = targetPage;
|
||||||
|
index = targetIndex = pageItemIndex[page];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index != targetIndex && phaseSelect == 1.0f)
|
||||||
|
index = pageItemIndex[page] = targetIndex;
|
||||||
|
|
||||||
|
int count = getItemsCount(page);
|
||||||
|
|
||||||
|
bool ready = active && phaseRing == 1.0f && phasePage == 1.0f;
|
||||||
|
|
||||||
|
if (index == targetIndex && targetPage == page && ready && !chosen) {
|
||||||
|
if (Input::state[cLeft]) { phaseSelect = 0.0f; targetIndex = (targetIndex - 1 + count) % count; }
|
||||||
|
if (Input::state[cRight]) { phaseSelect = 0.0f; targetIndex = (targetIndex + 1) % count; }
|
||||||
|
if (Input::state[cUp] && page < PAGE_ITEMS && getItemsCount(page + 1)) { phasePage = 0.0f; targetPage = Page(page + 1); }
|
||||||
|
if (Input::state[cDown] && page > PAGE_OPTION && getItemsCount(page - 1)) { phasePage = 0.0f; targetPage = Page(page - 1); }
|
||||||
|
|
||||||
|
if (index != targetIndex) {
|
||||||
|
vec3 p;
|
||||||
|
game->playSound(TR::SND_INV_SPIN, p, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 p;
|
||||||
|
|
||||||
|
Item &item = items[getItemIndex(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 {
|
||||||
|
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;
|
||||||
|
case TR::Entity::INV_CONTROLS : game->playSound(TR::SND_INV_CONTROLS, p, 0, 0); break;
|
||||||
|
case TR::Entity::INV_PISTOLS :
|
||||||
|
case TR::Entity::INV_SHOTGUN :
|
||||||
|
case TR::Entity::INV_MAGNUMS :
|
||||||
|
case TR::Entity::INV_UZIS : game->playSound(TR::SND_INV_WEAPON, p, 0, 0); break;
|
||||||
|
default : game->playSound(TR::SND_INV_SHOW, p, 0, 0); break;
|
||||||
|
}
|
||||||
|
item.choose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float w = 90.0f * DEG2RAD * Core::deltaTime;
|
||||||
|
|
||||||
|
int itemIndex = getItemIndex(page, index);
|
||||||
|
|
||||||
|
for (int i = 0; i < itemsCount; i++) {
|
||||||
|
items[i].update();
|
||||||
|
float &angle = items[i].angle;
|
||||||
|
|
||||||
|
if (itemIndex != i || chosen) {
|
||||||
|
if (angle == 0.0f) {
|
||||||
|
continue;
|
||||||
|
} else if (angle < 0.0f) {
|
||||||
|
angle += w;
|
||||||
|
if (angle > 0.0f)
|
||||||
|
angle = 0.0f;
|
||||||
|
} else if (angle > 0.0f) {
|
||||||
|
angle -= w;
|
||||||
|
if (angle < 0.0f)
|
||||||
|
angle = 0.0f;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
angle += w;
|
||||||
|
|
||||||
|
angle = clampAngle(angle);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ready && chosen && phaseChoose == 1.0f && item.anim->isEnded) {
|
||||||
|
TR::Entity::Type type = item.type;
|
||||||
|
|
||||||
|
if (type == TR::Entity::INV_PISTOLS || type == TR::Entity::INV_SHOTGUN || type == TR::Entity::INV_MAGNUMS || type == TR::Entity::INV_UZIS ||
|
||||||
|
type == TR::Entity::INV_MEDIKIT_SMALL || type == TR::Entity::INV_MEDIKIT_BIG) {
|
||||||
|
|
||||||
|
game->invUse(type, TR::Entity::NONE);
|
||||||
|
toggle();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void prepareBackground() {
|
||||||
|
Core::setDepthTest(false);
|
||||||
|
|
||||||
|
// vertical blur
|
||||||
|
Core::setTarget(background[1], true);
|
||||||
|
game->setShader(Core::passFilter, Shader::FILTER_BLUR, false, false);
|
||||||
|
Core::active.shader->setParam(uParam, vec4(0, 1, 1.0f / INVENTORY_BG_SIZE, 0));;
|
||||||
|
background[0]->bind(sDiffuse);
|
||||||
|
game->getMesh()->renderQuad();
|
||||||
|
|
||||||
|
// horizontal blur
|
||||||
|
Core::setTarget(background[2], true);
|
||||||
|
game->setShader(Core::passFilter, Shader::FILTER_BLUR, false, false);
|
||||||
|
Core::active.shader->setParam(uParam, vec4(1, 0, 1.0f / INVENTORY_BG_SIZE, 0));;
|
||||||
|
background[1]->bind(sDiffuse);
|
||||||
|
game->getMesh()->renderQuad();
|
||||||
|
|
||||||
|
// grayscale
|
||||||
|
Core::setTarget(background[1], true);
|
||||||
|
game->setShader(Core::passFilter, Shader::FILTER_GRAYSCALE, false, false);
|
||||||
|
Core::active.shader->setParam(uParam, vec4(1, 0, 0, 0));;
|
||||||
|
background[2]->bind(sDiffuse);
|
||||||
|
game->getMesh()->renderQuad();
|
||||||
|
|
||||||
|
Core::setTarget(NULL, true);
|
||||||
|
|
||||||
|
Core::setDepthTest(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void renderItemText(const Item &item, float width) {
|
||||||
|
UI::textOut(game, vec2(0, 480 - 16), item.desc.name, UI::aCenter, width);
|
||||||
|
|
||||||
|
if (item.count > 1) {
|
||||||
|
char spec;
|
||||||
|
switch (item.type) {
|
||||||
|
case TR::Entity::INV_SHOTGUN : spec = 12; break;
|
||||||
|
case TR::Entity::INV_MAGNUMS : spec = 13; break;
|
||||||
|
case TR::Entity::INV_UZIS : spec = 14; break;
|
||||||
|
default : spec = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char buf[16];
|
||||||
|
sprintf(buf, "%d %c", item.count, spec);
|
||||||
|
for (int i = 0; buf[i] != ' '; i++)
|
||||||
|
buf[i] -= 47;
|
||||||
|
UI::textOut(game, vec2(width / 2 - 160, 480 - 96), buf, UI::aRight, 320);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float getAngle(int index, int count) {
|
||||||
|
return PI * 2.0f / float(count) * index;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getItemsCount(int page) {
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < itemsCount; i++)
|
||||||
|
if (items[i].desc.page == page)
|
||||||
|
count++;
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
void renderPage(int page) {
|
||||||
|
float phase = page == targetPage ? phasePage : (1.0f - phasePage);
|
||||||
|
|
||||||
|
float alpha = 1.0f - phaseRing * phase;
|
||||||
|
alpha *= alpha;
|
||||||
|
alpha = 1.0f - alpha;
|
||||||
|
Core::active.shader->setParam(uMaterial, vec4(1.0f, 0.4f, 0.0f, alpha));
|
||||||
|
|
||||||
|
int count = getItemsCount(page);
|
||||||
|
|
||||||
|
vec2 cpos(1286, 256 + 1280 * (1.0f - phaseRing));
|
||||||
|
float ringTilt = cpos.angle();
|
||||||
|
float radius = phaseRing * INVENTORY_MAX_RADIUS * phase;
|
||||||
|
float collapseAngle = phaseRing * phase * PI - PI;
|
||||||
|
float ringHeight = lerp(float(this->page), float(targetPage), hermite(phasePage)) * INVENTORY_HEIGHT;
|
||||||
|
float angle = getAngle(pageItemIndex[page], count);
|
||||||
|
|
||||||
|
if (phaseSelect < 1.0f)
|
||||||
|
angle = lerpAngle(angle, getAngle(targetIndex, count), hermite(phaseSelect));
|
||||||
|
|
||||||
|
int itemIndex = 0;
|
||||||
|
for (int i = 0; i < itemsCount; i++) {
|
||||||
|
Item &item = items[i];
|
||||||
|
|
||||||
|
if (item.desc.page != page)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
float a = getAngle(itemIndex, count) - angle - collapseAngle;
|
||||||
|
float ia = item.angle;
|
||||||
|
float ra = ringTilt;
|
||||||
|
float rd = radius;
|
||||||
|
|
||||||
|
if (itemIndex == pageItemIndex[page] && (chosen || phaseChoose > 0.0f)) {
|
||||||
|
ia *= 1.0f - phaseChoose;
|
||||||
|
ra *= 1.0f - phaseChoose;
|
||||||
|
rd += 296 * phaseChoose;
|
||||||
|
}
|
||||||
|
|
||||||
|
Basis basis = Basis(quat(vec3(1, 0, 0), ra), vec3(0.0f));
|
||||||
|
basis = basis * Basis(quat(vec3(0, 1, 0), PI + ia - a), vec3(sinf(a), 0, -cosf(a)) * rd - vec3(0, item.desc.page * INVENTORY_HEIGHT - ringHeight, 0));
|
||||||
|
|
||||||
|
item.render(game, basis);
|
||||||
|
|
||||||
|
itemIndex++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void render() {
|
||||||
|
// background
|
||||||
|
Core::setDepthTest(false);
|
||||||
|
|
||||||
|
game->setShader(Core::passFilter, Shader::FILTER_MIXER, false, false);
|
||||||
|
Core::active.shader->setParam(uParam, vec4(phaseRing, 1.0f - phaseRing * 0.4f, 0, 0));;
|
||||||
|
background[0]->bind(sDiffuse); // orignal image
|
||||||
|
background[1]->bind(sNormal); // blured grayscale image
|
||||||
|
game->getMesh()->renderQuad();
|
||||||
|
|
||||||
|
Core::setDepthTest(true);
|
||||||
|
Core::setBlending(bmAlpha);
|
||||||
|
|
||||||
|
// items
|
||||||
|
game->setupBinding();
|
||||||
|
|
||||||
|
Core::mLightProj.identity();
|
||||||
|
|
||||||
|
Core::mView.identity();
|
||||||
|
Core::mView.translate(vec3(0, 0, -1286)); // y = -96 in title
|
||||||
|
|
||||||
|
Core::mView.up *= -1.0f;
|
||||||
|
Core::mView.dir *= -1.0f;
|
||||||
|
Core::mViewInv = Core::mView.inverse();
|
||||||
|
|
||||||
|
float aspect = float(Core::width) / float(Core::height);
|
||||||
|
|
||||||
|
Core::mProj = mat4(70.0f, aspect, 32.0f, 2048.0f);
|
||||||
|
Core::mViewProj = Core::mProj * Core::mView;
|
||||||
|
Core::viewPos = Core::mViewInv.getPos();
|
||||||
|
|
||||||
|
Core::whiteTex->bind(sShadow);
|
||||||
|
game->setShader(Core::passCompose, Shader::ENTITY, false, false);
|
||||||
|
|
||||||
|
vec3 ambient[6] = {
|
||||||
|
vec3(0.4f), vec3(0.2f), vec3(0.4f), vec3(0.5f), vec3(0.4f), vec3(0.6f)
|
||||||
|
};
|
||||||
|
|
||||||
|
Core::lightPos[0] = vec3(1000, 2000, 1000);
|
||||||
|
Core::lightColor[0] = vec4(1, 1, 1, 8192);
|
||||||
|
for (int i = 1; i < MAX_LIGHTS; i++)
|
||||||
|
Core::lightColor[1] = vec4(0, 0, 0, 1);
|
||||||
|
|
||||||
|
Core::active.shader->setParam(uLightPos, Core::lightPos[0], MAX_LIGHTS);
|
||||||
|
Core::active.shader->setParam(uLightColor, Core::lightColor[0], MAX_LIGHTS);
|
||||||
|
Core::active.shader->setParam(uAmbient, ambient[0], 6);
|
||||||
|
|
||||||
|
renderPage(page);
|
||||||
|
if (page != targetPage)
|
||||||
|
renderPage(targetPage);
|
||||||
|
|
||||||
|
if (phaseRing < 1.0f)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Core::setDepthTest(false);
|
||||||
|
Core::setBlending(bmAlpha);
|
||||||
|
Core::setCulling(cfNone);
|
||||||
|
game->setupBinding();
|
||||||
|
|
||||||
|
float w = 480 * aspect;
|
||||||
|
Core::mViewProj = mat4(0.0f, w, 480, 0.0f, 0.0f, 1.0f);
|
||||||
|
|
||||||
|
game->setShader(Core::passGUI, Shader::DEFAULT);
|
||||||
|
Core::active.shader->setParam(uMaterial, vec4(1.0f));
|
||||||
|
Core::active.shader->setParam(uPosScale, vec4(0.0f, 0.0f, 1.0f, 1.0f));
|
||||||
|
|
||||||
|
UI::textBegin();
|
||||||
|
|
||||||
|
static const char* pageTitle[PAGE_MAX] = { "OPTION", "INVENTORY", "ITEMS" };
|
||||||
|
|
||||||
|
UI::textOut(game, vec2( 0, 32), pageTitle[page], UI::aCenter, w);
|
||||||
|
|
||||||
|
if (page < PAGE_ITEMS && getItemsCount(page + 1)) {
|
||||||
|
UI::textOut(game, vec2(16, 32), "\x5B", UI::aLeft, w);
|
||||||
|
UI::textOut(game, vec2( 0, 32), "\x5B", UI::aRight, w - 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (page > PAGE_OPTION && getItemsCount(page - 1)) {
|
||||||
|
UI::textOut(game, vec2(16, 480 - 16), "\x5D", UI::aLeft, w);
|
||||||
|
UI::textOut(game, vec2(0, 480 - 16), "\x5D", UI::aRight, w - 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index == targetIndex)
|
||||||
|
renderItemText(items[getItemIndex(page, index)], w);
|
||||||
|
|
||||||
|
UI::textEnd(game);
|
||||||
|
|
||||||
|
Core::setCulling(cfFront);
|
||||||
|
Core::setBlending(bmNone);
|
||||||
|
Core::setDepthTest(true);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
91
src/lara.h
91
src/lara.h
@@ -6,7 +6,6 @@
|
|||||||
#include "character.h"
|
#include "character.h"
|
||||||
#include "trigger.h"
|
#include "trigger.h"
|
||||||
#include "sprite.h"
|
#include "sprite.h"
|
||||||
#include "inventory.h"
|
|
||||||
|
|
||||||
#define TURN_FAST PI
|
#define TURN_FAST PI
|
||||||
#define TURN_FAST_BACK PI * 3.0f / 4.0f
|
#define TURN_FAST_BACK PI * 3.0f / 4.0f
|
||||||
@@ -21,6 +20,8 @@
|
|||||||
#define LARA_TILT_SPEED (DEG2RAD * 37.5f)
|
#define LARA_TILT_SPEED (DEG2RAD * 37.5f)
|
||||||
#define LARA_TILT_MAX (DEG2RAD * 10.0f)
|
#define LARA_TILT_MAX (DEG2RAD * 10.0f)
|
||||||
|
|
||||||
|
#define LARA_MAX_HEALTH 1000
|
||||||
|
|
||||||
#define LARA_HANG_OFFSET 724
|
#define LARA_HANG_OFFSET 724
|
||||||
#define LARA_HEIGHT 762
|
#define LARA_HEIGHT 762
|
||||||
#define LARA_HEIGHT_WATER 400
|
#define LARA_HEIGHT_WATER 400
|
||||||
@@ -213,7 +214,6 @@ struct Lara : Character {
|
|||||||
|
|
||||||
ActionCommand actionList[MAX_TRIGGER_ACTIONS];
|
ActionCommand actionList[MAX_TRIGGER_ACTIONS];
|
||||||
|
|
||||||
Inventory inventory;
|
|
||||||
int lastPickUp;
|
int lastPickUp;
|
||||||
int viewTarget;
|
int viewTarget;
|
||||||
int roomPrev; // water out from room
|
int roomPrev; // water out from room
|
||||||
@@ -384,7 +384,7 @@ struct Lara : Character {
|
|||||||
|
|
||||||
} *braid;
|
} *braid;
|
||||||
|
|
||||||
Lara(IGame *game, int entity, bool home) : Character(game, entity, 1000), home(home), wpnCurrent(Weapon::EMPTY), wpnNext(Weapon::EMPTY), chestOffset(pos), viewTarget(-1), braid(NULL) {
|
Lara(IGame *game, int entity, bool home) : Character(game, entity, LARA_MAX_HEALTH), home(home), wpnCurrent(Weapon::EMPTY), wpnNext(Weapon::EMPTY), chestOffset(pos), viewTarget(-1), braid(NULL) {
|
||||||
|
|
||||||
if (getEntity().type == TR::Entity::LARA) {
|
if (getEntity().type == TR::Entity::LARA) {
|
||||||
if (getRoom().flags.water)
|
if (getRoom().flags.water)
|
||||||
@@ -399,7 +399,11 @@ struct Lara : Character {
|
|||||||
getEntity().flags.active = 1;
|
getEntity().flags.active = 1;
|
||||||
initMeshOverrides();
|
initMeshOverrides();
|
||||||
|
|
||||||
memset(weapons, -1, sizeof(weapons));
|
weapons[Weapon::PISTOLS].ammo = -1;
|
||||||
|
weapons[Weapon::SHOTGUN].ammo = -1;
|
||||||
|
weapons[Weapon::MAGNUMS].ammo = -1;
|
||||||
|
weapons[Weapon::UZIS ].ammo = -1;
|
||||||
|
|
||||||
if (!home) {
|
if (!home) {
|
||||||
weapons[Weapon::PISTOLS].ammo = 0;
|
weapons[Weapon::PISTOLS].ammo = 0;
|
||||||
weapons[Weapon::SHOTGUN].ammo = 9000;
|
weapons[Weapon::SHOTGUN].ammo = 9000;
|
||||||
@@ -666,7 +670,11 @@ struct Lara : Character {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void wpnChange(Weapon::Type wType) {
|
void wpnChange(Weapon::Type wType) {
|
||||||
if (wpnCurrent == wType || home) return;
|
if (wpnCurrent == wType || home) {
|
||||||
|
if (emptyHands())
|
||||||
|
wpnDraw();
|
||||||
|
return;
|
||||||
|
}
|
||||||
wpnNext = wType;
|
wpnNext = wType;
|
||||||
wpnHide();
|
wpnHide();
|
||||||
}
|
}
|
||||||
@@ -803,11 +811,6 @@ struct Lara : Character {
|
|||||||
updateTargets();
|
updateTargets();
|
||||||
updateOverrides();
|
updateOverrides();
|
||||||
|
|
||||||
if (Input::down[ik1]) wpnChange(Weapon::PISTOLS);
|
|
||||||
if (Input::down[ik2]) wpnChange(Weapon::SHOTGUN);
|
|
||||||
if (Input::down[ik3]) wpnChange(Weapon::MAGNUMS);
|
|
||||||
if (Input::down[ik4]) wpnChange(Weapon::UZIS);
|
|
||||||
|
|
||||||
if (wpnNext != Weapon::EMPTY && emptyHands()) {
|
if (wpnNext != Weapon::EMPTY && emptyHands()) {
|
||||||
wpnSet(wpnNext);
|
wpnSet(wpnNext);
|
||||||
wpnDraw();
|
wpnDraw();
|
||||||
@@ -1233,6 +1236,21 @@ struct Lara : Character {
|
|||||||
Core::lightColor[1 + 0] = Core::lightColor[1 + 1] = vec4(0, 0, 0, 1);
|
Core::lightColor[1 + 0] = Core::lightColor[1 + 1] = vec4(0, 0, 0, 1);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void useItem(TR::Entity::Type item) {
|
||||||
|
switch (item) {
|
||||||
|
case TR::Entity::INV_PISTOLS : wpnChange(Lara::Weapon::PISTOLS); break;
|
||||||
|
case TR::Entity::INV_SHOTGUN : wpnChange(Lara::Weapon::SHOTGUN); break;
|
||||||
|
case TR::Entity::INV_MAGNUMS : wpnChange(Lara::Weapon::MAGNUMS); break;
|
||||||
|
case TR::Entity::INV_UZIS : wpnChange(Lara::Weapon::UZIS); break;
|
||||||
|
case TR::Entity::INV_MEDIKIT_SMALL :
|
||||||
|
case TR::Entity::INV_MEDIKIT_BIG :
|
||||||
|
health = min(LARA_MAX_HEALTH, health + (item == TR::Entity::INV_MEDIKIT_SMALL ? LARA_MAX_HEALTH / 2 : LARA_MAX_HEALTH));
|
||||||
|
playSound(TR::SND_HEALTH, pos, Sound::PAN);
|
||||||
|
break;
|
||||||
|
default : ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool waterOut() {
|
bool waterOut() {
|
||||||
// TODO: playSound 36
|
// TODO: playSound 36
|
||||||
if (collision.side != Collision::FRONT || pos.y - collision.info[Collision::FRONT].floor > 256 + 128)
|
if (collision.side != Collision::FRONT || pos.y - collision.info[Collision::FRONT].floor > 256 + 128)
|
||||||
@@ -1298,22 +1316,6 @@ struct Lara : Character {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool useItem(TR::Entity::Type item, TR::Entity::Type slot) {
|
|
||||||
if (item == TR::Entity::NONE) {
|
|
||||||
switch (slot) {
|
|
||||||
case TR::Entity::KEY_HOLE_1 : item = TR::Entity::KEY_1; break; // TODO: 1-4
|
|
||||||
case TR::Entity::PUZZLE_HOLE_1 : item = TR::Entity::PUZZLE_1; break;
|
|
||||||
default : return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inventory.getCount(item) > 0) {
|
|
||||||
inventory.remove(item);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void alignByItem(Controller *item, const TR::Limits::Limit &limit, bool dx, bool ay) {
|
void alignByItem(Controller *item, const TR::Limits::Limit &limit, bool dx, bool ay) {
|
||||||
if (ay)
|
if (ay)
|
||||||
angle = item->angle;
|
angle = item->angle;
|
||||||
@@ -1390,7 +1392,7 @@ struct Lara : Character {
|
|||||||
limit = actionState == STATE_USE_KEY ? &TR::Limits::KEY_HOLE : &TR::Limits::PUZZLE_HOLE;
|
limit = actionState == STATE_USE_KEY ? &TR::Limits::KEY_HOLE : &TR::Limits::PUZZLE_HOLE;
|
||||||
if (!checkInteraction((Controller*)level->entities[info.trigCmd[0].args].controller, *limit, isPressed(ACTION)))
|
if (!checkInteraction((Controller*)level->entities[info.trigCmd[0].args].controller, *limit, isPressed(ACTION)))
|
||||||
return;
|
return;
|
||||||
if (!useItem(TR::Entity::NONE, level->entities[info.trigCmd[0].args].type)) {
|
if (!game->invUse(TR::Entity::NONE, level->entities[info.trigCmd[0].args].type)) {
|
||||||
playSound(TR::SND_NO, pos, Sound::PAN);
|
playSound(TR::SND_NO, pos, Sound::PAN);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1884,32 +1886,21 @@ struct Lara : Character {
|
|||||||
input = Character::getInput();
|
input = Character::getInput();
|
||||||
if (input & DEATH) return input;
|
if (input & DEATH) return input;
|
||||||
|
|
||||||
int &p = Input::joy.POV;
|
if (Input::state[cUp]) input |= FORTH;
|
||||||
|
if (Input::state[cRight]) input |= RIGHT;
|
||||||
#ifndef LEVEL_EDITOR
|
if (Input::state[cDown]) input |= BACK;
|
||||||
if (Input::down[ikW]) input |= FORTH;
|
if (Input::state[cLeft]) input |= LEFT;
|
||||||
if (Input::down[ikD]) input |= RIGHT;
|
if (Input::state[cRoll]) input = FORTH | BACK;
|
||||||
if (Input::down[ikS]) input |= BACK;
|
if (Input::state[cStepRight]) input = WALK | RIGHT;
|
||||||
if (Input::down[ikA]) input |= LEFT;
|
if (Input::state[cStepLeft]) input = WALK | LEFT;
|
||||||
#endif
|
if (Input::state[cJump]) input |= JUMP;
|
||||||
|
if (Input::state[cWalk]) input |= WALK;
|
||||||
if (Input::down[ikUp] || p == 8 || p == 1 || p == 2) input |= FORTH;
|
if (Input::state[cAction]) input |= ACTION;
|
||||||
if (Input::down[ikRight] || p == 2 || p == 3 || p == 4) input |= RIGHT;
|
if (Input::state[cWeapon]) input |= WEAPON;
|
||||||
if (Input::down[ikDown] || p == 4 || p == 5 || p == 6) input |= BACK;
|
|
||||||
if (Input::down[ikLeft] || p == 6 || p == 7 || p == 8) input |= LEFT;
|
|
||||||
if (Input::down[ikJoyB]) input = FORTH | BACK; // roll
|
|
||||||
if (Input::down[ikJoyRT] || Input::down[ikX]) input = WALK | RIGHT; // step right
|
|
||||||
if (Input::down[ikJoyLT] || Input::down[ikZ]) input = WALK | LEFT; // step left
|
|
||||||
if (Input::down[ikSpace] || Input::down[ikJoyX]) input |= JUMP;
|
|
||||||
if (Input::down[ikShift] || Input::down[ikJoyLB]) input |= WALK;
|
|
||||||
if (Input::down[ikE] || Input::down[ikCtrl] || Input::down[ikJoyA]) input |= ACTION;
|
|
||||||
if (Input::down[ikQ] || Input::down[ikAlt] || Input::down[ikJoyY]) input |= WEAPON;
|
|
||||||
|
|
||||||
// analog control
|
// analog control
|
||||||
rotFactor = vec2(1.0f);
|
rotFactor = vec2(1.0f);
|
||||||
|
|
||||||
if (Input::down[ikJoyL]) input = FORTH | BACK;
|
|
||||||
|
|
||||||
if ((state == STATE_STOP || state == STATE_SURF_TREAD || state == STATE_HANG) && fabsf(Input::joy.L.x) < 0.5f && fabsf(Input::joy.L.y) < 0.5f)
|
if ((state == STATE_STOP || state == STATE_SURF_TREAD || state == STATE_HANG) && fabsf(Input::joy.L.x) < 0.5f && fabsf(Input::joy.L.y) < 0.5f)
|
||||||
return input;
|
return input;
|
||||||
|
|
||||||
@@ -1945,7 +1936,7 @@ struct Lara : Character {
|
|||||||
int pickupFrame = stand == STAND_GROUND ? PICKUP_FRAME_GROUND : PICKUP_FRAME_UNDERWATER;
|
int pickupFrame = stand == STAND_GROUND ? PICKUP_FRAME_GROUND : PICKUP_FRAME_UNDERWATER;
|
||||||
if (animation.isFrameActive(pickupFrame)) {
|
if (animation.isFrameActive(pickupFrame)) {
|
||||||
item.flags.invisible = true;
|
item.flags.invisible = true;
|
||||||
inventory.add(item.type, 1);
|
game->invAdd(item.type, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
292
src/level.h
292
src/level.h
@@ -9,6 +9,7 @@
|
|||||||
#include "enemy.h"
|
#include "enemy.h"
|
||||||
#include "camera.h"
|
#include "camera.h"
|
||||||
#include "trigger.h"
|
#include "trigger.h"
|
||||||
|
#include "inventory.h"
|
||||||
|
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
@@ -16,6 +17,7 @@
|
|||||||
|
|
||||||
struct Level : IGame {
|
struct Level : IGame {
|
||||||
TR::Level level;
|
TR::Level level;
|
||||||
|
Inventory inventory;
|
||||||
Texture *atlas;
|
Texture *atlas;
|
||||||
Texture *cube;
|
Texture *cube;
|
||||||
MeshBuilder *mesh;
|
MeshBuilder *mesh;
|
||||||
@@ -36,8 +38,11 @@ struct Level : IGame {
|
|||||||
WaterCache *waterCache;
|
WaterCache *waterCache;
|
||||||
ZoneCache *zoneCache;
|
ZoneCache *zoneCache;
|
||||||
|
|
||||||
Sound::Sample *sndAmbient;
|
Sound::Sample *sndSoundtrack;
|
||||||
Sound::Sample *sndUnderwater;
|
Sound::Sample *sndUnderwater;
|
||||||
|
Sound::Sample *sndCurrent;
|
||||||
|
|
||||||
|
bool lastTitle;
|
||||||
|
|
||||||
// IGame implementation ========
|
// IGame implementation ========
|
||||||
virtual TR::Level* getLevel() {
|
virtual TR::Level* getLevel() {
|
||||||
@@ -83,6 +88,16 @@ struct Level : IGame {
|
|||||||
shaderCache->bind(pass, type, (underwater ? ShaderCache::FX_UNDERWATER : 0) | (alphaTest ? ShaderCache::FX_ALPHA_TEST : 0) | ((params->clipHeight != NO_CLIP_PLANE && pass == Core::passCompose) ? ShaderCache::FX_CLIP_PLANE : 0));
|
shaderCache->bind(pass, type, (underwater ? ShaderCache::FX_UNDERWATER : 0) | (alphaTest ? ShaderCache::FX_ALPHA_TEST : 0) | ((params->clipHeight != NO_CLIP_PLANE && pass == Core::passCompose) ? ShaderCache::FX_CLIP_PLANE : 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual void setupBinding() {
|
||||||
|
atlas->bind(sDiffuse);
|
||||||
|
Core::whiteTex->bind(sNormal);
|
||||||
|
Core::whiteTex->bind(sMask);
|
||||||
|
Core::whiteTex->bind(sReflect);
|
||||||
|
cube->bind(sEnvironment);
|
||||||
|
Core::basis.identity();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
virtual void renderEnvironment(int roomIndex, const vec3 &pos, Texture **targets, int stride = 0) {
|
virtual void renderEnvironment(int roomIndex, const vec3 &pos, Texture **targets, int stride = 0) {
|
||||||
PROFILE_MARKER("ENVIRONMENT");
|
PROFILE_MARKER("ENVIRONMENT");
|
||||||
Core::eye = 0.0f;
|
Core::eye = 0.0f;
|
||||||
@@ -111,9 +126,40 @@ struct Level : IGame {
|
|||||||
virtual void fxQuake(float time) {
|
virtual void fxQuake(float time) {
|
||||||
camera->shake = time;
|
camera->shake = time;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual bool invUse(TR::Entity::Type item, TR::Entity::Type slot) {
|
||||||
|
lara->useItem(item);
|
||||||
|
return inventory.use(item, slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void invAdd(TR::Entity::Type type, int count) {
|
||||||
|
inventory.add(type, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Sound::Sample* playSound(int id, const vec3 &pos, int flags, int group = -1) const {
|
||||||
|
if (level.version == TR::Level::VER_TR1_PSX && id == TR::SND_SECRET)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
int16 a = level.soundsMap[id];
|
||||||
|
if (a == -1) return NULL;
|
||||||
|
|
||||||
|
TR::SoundInfo &b = level.soundsInfo[a];
|
||||||
|
if (b.chance == 0 || (rand() & 0x7fff) <= b.chance) {
|
||||||
|
int index = b.offset + rand() % b.flags.count;
|
||||||
|
float volume = (float)b.volume / 0x7FFF;
|
||||||
|
float pitch = b.flags.pitch ? (0.9f + randf() * 0.2f) : 1.0f;
|
||||||
|
if (b.flags.mode == 1) flags |= Sound::UNIQUE;
|
||||||
|
//if (b.flags.mode == 2) flags |= Sound::REPLAY;
|
||||||
|
if (b.flags.mode == 3) flags |= Sound::SYNC;
|
||||||
|
if (b.flags.gain) volume = max(0.0f, volume - randf() * 0.25f);
|
||||||
|
if (b.flags.fixed) flags |= Sound::LOOP;
|
||||||
|
return Sound::play(level.getSampleStream(index), pos, volume, pitch, flags, group * 1000 + index);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
//==============================
|
//==============================
|
||||||
|
|
||||||
Level(Stream &stream, Stream *snd, bool demo, bool home) : level(stream, demo), lara(NULL) {
|
Level(Stream &stream, Stream *snd, bool demo, bool home) : level(stream, demo), inventory(this), lara(NULL) {
|
||||||
params->time = 0.0f;
|
params->time = 0.0f;
|
||||||
|
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
@@ -241,6 +287,9 @@ struct Level : IGame {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lastTitle = false;
|
||||||
|
|
||||||
|
if (!isTitle()) {
|
||||||
ASSERT(lara != NULL);
|
ASSERT(lara != NULL);
|
||||||
camera = new Camera(this, lara);
|
camera = new Camera(this, lara);
|
||||||
|
|
||||||
@@ -255,16 +304,28 @@ struct Level : IGame {
|
|||||||
|
|
||||||
// init sounds
|
// init sounds
|
||||||
//Sound::play(Sound::openWAD("05_Lara's_Themes.wav"), 1, 1, 0);
|
//Sound::play(Sound::openWAD("05_Lara's_Themes.wav"), 1, 1, 0);
|
||||||
sndAmbient = Sound::play(snd, vec3(0.0f), 1, 1, Sound::Flags::LOOP);
|
sndSoundtrack = Sound::play(snd, vec3(0.0f), 1, 1, Sound::Flags::LOOP);
|
||||||
|
|
||||||
sndUnderwater = lara->playSound(TR::SND_UNDERWATER, vec3(0.0f), Sound::LOOP);
|
sndUnderwater = lara->playSound(TR::SND_UNDERWATER, vec3(0.0f), Sound::LOOP);
|
||||||
if (sndUnderwater)
|
if (sndUnderwater)
|
||||||
sndUnderwater->volume = 0.0f;
|
sndUnderwater->volume = 0.0f;
|
||||||
|
|
||||||
|
sndCurrent = sndSoundtrack;
|
||||||
|
|
||||||
for (int i = 0; i < level.soundSourcesCount; i++) {
|
for (int i = 0; i < level.soundSourcesCount; i++) {
|
||||||
TR::SoundSource &src = level.soundSources[i];
|
TR::SoundSource &src = level.soundSources[i];
|
||||||
lara->playSound(src.id, vec3(float(src.x), float(src.y), float(src.z)), Sound::PAN | Sound::LOOP | Sound::STATIC);
|
lara->playSound(src.id, vec3(float(src.x), float(src.y), float(src.z)), Sound::PAN | Sound::LOOP | Sound::STATIC);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
camera = NULL;
|
||||||
|
ambientCache = NULL;
|
||||||
|
waterCache = NULL;
|
||||||
|
zoneCache = NULL;
|
||||||
|
shadow = NULL;
|
||||||
|
sndSoundtrack = NULL;
|
||||||
|
sndUnderwater = NULL;
|
||||||
|
sndCurrent = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual ~Level() {
|
virtual ~Level() {
|
||||||
@@ -289,6 +350,10 @@ struct Level : IGame {
|
|||||||
Sound::stopAll();
|
Sound::stopAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isTitle() {
|
||||||
|
return lara == NULL || inventory.isActive();
|
||||||
|
}
|
||||||
|
|
||||||
void initTextures() {
|
void initTextures() {
|
||||||
if (!level.tilesCount) {
|
if (!level.tilesCount) {
|
||||||
atlas = NULL;
|
atlas = NULL;
|
||||||
@@ -554,6 +619,13 @@ struct Level : IGame {
|
|||||||
lara->reset(TR::NO_ROOM, camera->pos, camera->angle.y, false);
|
lara->reset(TR::NO_ROOM, camera->pos, camera->angle.y, false);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
if (isTitle()) {
|
||||||
|
sndCurrent->volume = 0.0f;
|
||||||
|
Sound::reverb.setRoomSize(vec3(1.0f));
|
||||||
|
inventory.update();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
params->time += Core::deltaTime;
|
params->time += Core::deltaTime;
|
||||||
|
|
||||||
for (int i = 0; i < level.entitiesCount; i++) {
|
for (int i = 0; i < level.entitiesCount; i++) {
|
||||||
@@ -572,9 +644,14 @@ struct Level : IGame {
|
|||||||
}
|
}
|
||||||
|
|
||||||
camera->update();
|
camera->update();
|
||||||
float ambientVolume = camera->isUnderwater() ? 0.0f : 1.0f;
|
|
||||||
if (sndAmbient) sndAmbient->volume = ambientVolume;
|
sndCurrent = camera->isUnderwater() ? sndUnderwater : sndSoundtrack;
|
||||||
if (sndUnderwater) sndUnderwater->volume = 1.0f - ambientVolume;
|
|
||||||
|
if (sndCurrent) {
|
||||||
|
if (sndSoundtrack && sndCurrent != sndSoundtrack) sndSoundtrack->volume = 0.0f;
|
||||||
|
if (sndUnderwater && sndCurrent != sndUnderwater) sndUnderwater->volume = 0.0f;
|
||||||
|
sndCurrent->volume = 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
if (waterCache)
|
if (waterCache)
|
||||||
waterCache->update();
|
waterCache->update();
|
||||||
@@ -585,16 +662,7 @@ struct Level : IGame {
|
|||||||
|
|
||||||
camera->setup(Core::pass == Core::passCompose);
|
camera->setup(Core::pass == Core::passCompose);
|
||||||
|
|
||||||
atlas->bind(sDiffuse);
|
setupBinding();
|
||||||
Core::whiteTex->bind(sNormal);
|
|
||||||
Core::whiteTex->bind(sMask);
|
|
||||||
Core::whiteTex->bind(sReflect);
|
|
||||||
cube->bind(sEnvironment);
|
|
||||||
|
|
||||||
if (!Core::support.VAO)
|
|
||||||
mesh->bind();
|
|
||||||
//Core::mViewProj = Core::mLightProj;
|
|
||||||
Core::basis.identity();
|
|
||||||
|
|
||||||
// clear visibility flag for rooms
|
// clear visibility flag for rooms
|
||||||
for (int i = 0; i < level.roomsCount; i++)
|
for (int i = 0; i < level.roomsCount; i++)
|
||||||
@@ -694,39 +762,8 @@ struct Level : IGame {
|
|||||||
Core::setClearColor(vec4(0.0f, 0.0f, 0.0f, 0.0f));
|
Core::setClearColor(vec4(0.0f, 0.0f, 0.0f, 0.0f));
|
||||||
}
|
}
|
||||||
|
|
||||||
void render() {
|
|
||||||
Core::invalidateTarget(true, true);
|
|
||||||
params->clipHeight = NO_CLIP_PLANE;
|
|
||||||
params->clipSign = 1.0f;
|
|
||||||
params->waterHeight = params->clipHeight;
|
|
||||||
|
|
||||||
if (ambientCache)
|
|
||||||
ambientCache->precessQueue();
|
|
||||||
if (waterCache)
|
|
||||||
waterCache->reset();
|
|
||||||
if (shadow)
|
|
||||||
renderShadows(lara->getRoomIndex());
|
|
||||||
|
|
||||||
Core::setClearStencil(128);
|
|
||||||
Core::setTarget(NULL, true);
|
|
||||||
Core::setViewport(0, 0, Core::width, Core::height);
|
|
||||||
|
|
||||||
if (waterCache)
|
|
||||||
waterCache->checkVisibility = true;
|
|
||||||
|
|
||||||
renderCompose(camera->getRoomIndex(), true);
|
|
||||||
|
|
||||||
if (waterCache) {
|
|
||||||
waterCache->checkVisibility = false;
|
|
||||||
if (waterCache->visible) {
|
|
||||||
waterCache->renderMask();
|
|
||||||
waterCache->getRefract();
|
|
||||||
waterCache->simulate();
|
|
||||||
waterCache->render();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
|
void renderDebug() {
|
||||||
// Core::mViewInv = camera->mViewInv;
|
// Core::mViewInv = camera->mViewInv;
|
||||||
// Core::mView = Core::mViewInv.inverse();
|
// Core::mView = Core::mViewInv.inverse();
|
||||||
Core::setViewport(0, 0, Core::width, Core::height);
|
Core::setViewport(0, 0, Core::width, Core::height);
|
||||||
@@ -868,107 +905,74 @@ struct Level : IGame {
|
|||||||
|
|
||||||
Core::setBlending(bmNone);
|
Core::setBlending(bmNone);
|
||||||
|
|
||||||
/*
|
|
||||||
static int dbg_ambient = 0;
|
|
||||||
dbg_ambient = int(params->time * 2) % 4;
|
|
||||||
|
|
||||||
shadow->unbind(sShadow);
|
|
||||||
atlas->bind(sDiffuse);
|
|
||||||
glEnable(GL_TEXTURE_2D);
|
|
||||||
glDisable(GL_CULL_FACE);
|
|
||||||
glColor3f(1, 1, 1);
|
|
||||||
for (int j = 0; j < 6; j++) {
|
|
||||||
glPushMatrix();
|
|
||||||
vec3 p = lara->pos;//getPos();
|
|
||||||
glTranslatef(p.x, p.y - 1024, p.z);
|
|
||||||
switch (j) {
|
|
||||||
case 0 : glRotatef( 90, 0, 1, 0); break;
|
|
||||||
case 1 : glRotatef(-90, 0, 1, 0); break;
|
|
||||||
case 2 : glRotatef(-90, 1, 0, 0); break;
|
|
||||||
case 3 : glRotatef( 90, 1, 0, 0); break;
|
|
||||||
case 4 : glRotatef( 0, 0, 1, 0); break;
|
|
||||||
case 5 : glRotatef(180, 0, 1, 0); break;
|
|
||||||
}
|
|
||||||
glTranslatef(0, 0, 128);
|
|
||||||
|
|
||||||
ambientCache->textures[j * 4 + dbg_ambient]->bind(sDiffuse);
|
|
||||||
|
|
||||||
glBegin(GL_QUADS);
|
|
||||||
glTexCoord2f(0, 0); glVertex3f(-128, 128, 0);
|
|
||||||
glTexCoord2f(1, 0); glVertex3f( 128, 128, 0);
|
|
||||||
glTexCoord2f(1, 1); glVertex3f( 128, -128, 0);
|
|
||||||
glTexCoord2f(0, 1); glVertex3f(-128, -128, 0);
|
|
||||||
glEnd();
|
|
||||||
|
|
||||||
glPopMatrix();
|
|
||||||
}
|
|
||||||
|
|
||||||
glEnable(GL_CULL_FACE);
|
|
||||||
glDisable(GL_TEXTURE_2D);
|
|
||||||
|
|
||||||
glLineWidth(4);
|
|
||||||
glBegin(GL_LINES);
|
|
||||||
float S = 64.0f;
|
|
||||||
for (int i = 0; i < level.roomsCount; i++) {
|
|
||||||
TR::Room &r = level.rooms[i];
|
|
||||||
for (int j = 0; j < r.xSectors * r.zSectors; j++) {
|
|
||||||
TR::Room::Sector &s = r.sectors[j];
|
|
||||||
vec3 p = vec3(float((j / r.zSectors) * 1024 + 512 + r.info.x),
|
|
||||||
float(max((s.floor - 2) * 256, (s.floor + s.ceiling) * 256 / 2)),
|
|
||||||
float((j % r.zSectors) * 1024 + 512 + r.info.z));
|
|
||||||
|
|
||||||
AmbientCache::Cube &cube = ambientCache->items[ambientCache->offsets[i] + j];
|
|
||||||
if (cube.status == AmbientCache::Cube::READY) {
|
|
||||||
glColor3f(powf(cube.colors[0].x, 1.0f / 2.2f), powf(cube.colors[0].y, 1.0f / 2.2f), powf(cube.colors[0].z, 1.0f / 2.2f));
|
|
||||||
glVertex3f(p.x + 0, p.y + 0, p.z + 0);
|
|
||||||
glVertex3f(p.x + S, p.y + 0, p.z + 0);
|
|
||||||
|
|
||||||
glColor3f(powf(cube.colors[1].x, 1.0f / 2.2f), powf(cube.colors[1].y, 1.0f / 2.2f), powf(cube.colors[1].z, 1.0f / 2.2f));
|
|
||||||
glVertex3f(p.x + 0, p.y + 0, p.z + 0);
|
|
||||||
glVertex3f(p.x - S, p.y + 0, p.z + 0);
|
|
||||||
|
|
||||||
glColor3f(powf(cube.colors[2].x, 1.0f / 2.2f), powf(cube.colors[2].y, 1.0f / 2.2f), powf(cube.colors[2].z, 1.0f / 2.2f));
|
|
||||||
glVertex3f(p.x + 0, p.y + 0, p.z + 0);
|
|
||||||
glVertex3f(p.x + 0, p.y + S, p.z + 0);
|
|
||||||
|
|
||||||
glColor3f(powf(cube.colors[3].x, 1.0f / 2.2f), powf(cube.colors[3].y, 1.0f / 2.2f), powf(cube.colors[3].z, 1.0f / 2.2f));
|
|
||||||
glVertex3f(p.x + 0, p.y + 0, p.z + 0);
|
|
||||||
glVertex3f(p.x + 0, p.y - S, p.z + 0);
|
|
||||||
|
|
||||||
glColor3f(powf(cube.colors[4].x, 1.0f / 2.2f), powf(cube.colors[4].y, 1.0f / 2.2f), powf(cube.colors[4].z, 1.0f / 2.2f));
|
|
||||||
glVertex3f(p.x + 0, p.y + 0, p.z + 0);
|
|
||||||
glVertex3f(p.x + 0, p.y + 0, p.z + S);
|
|
||||||
|
|
||||||
glColor3f(powf(cube.colors[5].x, 1.0f / 2.2f), powf(cube.colors[5].y, 1.0f / 2.2f), powf(cube.colors[5].z, 1.0f / 2.2f));
|
|
||||||
glVertex3f(p.x + 0, p.y + 0, p.z + 0);
|
|
||||||
glVertex3f(p.x + 0, p.y + 0, p.z - S);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
glEnd();
|
|
||||||
glLineWidth(1);
|
|
||||||
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
shaders[shGUI]->bind();
|
|
||||||
Core::mViewProj = mat4(0, (float)Core::width, (float)Core::height, 0, 0, 1);
|
|
||||||
Core::active.shader->setParam(uViewProj, Core::mViewProj);
|
|
||||||
atlas->bind(0);
|
|
||||||
|
|
||||||
glDisable(GL_DEPTH_TEST);
|
|
||||||
glDisable(GL_CULL_FACE);
|
|
||||||
|
|
||||||
glEnable(GL_CULL_FACE);
|
|
||||||
glEnable(GL_DEPTH_TEST);
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
Debug::Level::info(level, lara->getEntity(), lara->animation);
|
Debug::Level::info(level, lara->getEntity(), lara->animation);
|
||||||
|
|
||||||
|
|
||||||
Debug::end();
|
Debug::end();
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void renderGame() {
|
||||||
|
Core::invalidateTarget(true, true);
|
||||||
|
params->clipHeight = NO_CLIP_PLANE;
|
||||||
|
params->clipSign = 1.0f;
|
||||||
|
params->waterHeight = params->clipHeight;
|
||||||
|
|
||||||
|
if (ambientCache)
|
||||||
|
ambientCache->precessQueue();
|
||||||
|
if (waterCache)
|
||||||
|
waterCache->reset();
|
||||||
|
if (shadow)
|
||||||
|
renderShadows(lara->getRoomIndex());
|
||||||
|
|
||||||
|
Core::setClearStencil(128);
|
||||||
|
Core::setTarget(NULL, true);
|
||||||
|
|
||||||
|
if (waterCache)
|
||||||
|
waterCache->checkVisibility = true;
|
||||||
|
|
||||||
|
renderCompose(camera->getRoomIndex(), true);
|
||||||
|
|
||||||
|
if (waterCache) {
|
||||||
|
waterCache->checkVisibility = false;
|
||||||
|
if (waterCache->visible) {
|
||||||
|
waterCache->renderMask();
|
||||||
|
waterCache->getRefract();
|
||||||
|
waterCache->simulate();
|
||||||
|
waterCache->render();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void renderInventory() {
|
||||||
|
Core::setTarget(NULL, true);
|
||||||
|
inventory.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
void render() {
|
||||||
|
bool title = isTitle();
|
||||||
|
bool copyBg = title && lastTitle != title;
|
||||||
|
|
||||||
|
if (copyBg) {
|
||||||
|
vec4 vp = Core::viewportDef;
|
||||||
|
Core::defaultTarget = inventory.background[0];
|
||||||
|
Core::setTarget(Core::defaultTarget, true);
|
||||||
|
renderGame();
|
||||||
|
Core::viewportDef = vp;
|
||||||
|
Core::defaultTarget = NULL;
|
||||||
|
|
||||||
|
inventory.prepareBackground();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!title)
|
||||||
|
renderGame();
|
||||||
|
|
||||||
|
if (title)
|
||||||
|
renderInventory();
|
||||||
|
|
||||||
|
lastTitle = title;
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
149
src/mesh.h
149
src/mesh.h
@@ -21,8 +21,13 @@ struct MeshRange {
|
|||||||
|
|
||||||
MeshRange() : aIndex(-1) {}
|
MeshRange() : aIndex(-1) {}
|
||||||
|
|
||||||
void setup() const {
|
void setup(Vertex *offset) const {
|
||||||
Vertex *v = (Vertex*)(vStart * sizeof(Vertex));
|
glEnableVertexAttribArray(aCoord);
|
||||||
|
glEnableVertexAttribArray(aTexCoord);
|
||||||
|
glEnableVertexAttribArray(aNormal);
|
||||||
|
glEnableVertexAttribArray(aColor);
|
||||||
|
|
||||||
|
Vertex *v = (Vertex*)(offset + vStart);
|
||||||
glVertexAttribPointer(aCoord, 4, GL_SHORT, false, sizeof(Vertex), &v->coord);
|
glVertexAttribPointer(aCoord, 4, GL_SHORT, false, sizeof(Vertex), &v->coord);
|
||||||
glVertexAttribPointer(aTexCoord, 4, GL_SHORT, false, sizeof(Vertex), &v->texCoord);
|
glVertexAttribPointer(aTexCoord, 4, GL_SHORT, false, sizeof(Vertex), &v->texCoord);
|
||||||
glVertexAttribPointer(aNormal, 4, GL_SHORT, true, sizeof(Vertex), &v->normal);
|
glVertexAttribPointer(aNormal, 4, GL_SHORT, true, sizeof(Vertex), &v->normal);
|
||||||
@@ -30,12 +35,9 @@ struct MeshRange {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void bind(GLuint *VAO) const {
|
void bind(GLuint *VAO) const {
|
||||||
if (aIndex > -1) {
|
GLuint vao = aIndex == -1 ? 0 : VAO[aIndex];
|
||||||
if (Core::active.VAO != VAO[aIndex]) {
|
if (Core::support.VAO && Core::active.VAO != vao)
|
||||||
glBindVertexArray(Core::active.VAO = VAO[aIndex]);
|
glBindVertexArray(Core::active.VAO = vao);
|
||||||
}
|
|
||||||
} else
|
|
||||||
setup();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -52,7 +54,7 @@ struct Mesh {
|
|||||||
|
|
||||||
Mesh(Index *indices, int iCount, Vertex *vertices, int vCount, int aCount) : VAO(NULL), iCount(iCount), vCount(vCount), aCount(aCount), aIndex(0) {
|
Mesh(Index *indices, int iCount, Vertex *vertices, int vCount, int aCount) : VAO(NULL), iCount(iCount), vCount(vCount), aCount(aCount), aIndex(0) {
|
||||||
glGenBuffers(2, ID);
|
glGenBuffers(2, ID);
|
||||||
bind();
|
bind(true);
|
||||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, iCount * sizeof(Index), indices, GL_STATIC_DRAW);
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER, iCount * sizeof(Index), indices, GL_STATIC_DRAW);
|
||||||
glBufferData(GL_ARRAY_BUFFER, vCount * sizeof(Vertex), vertices, GL_STATIC_DRAW);
|
glBufferData(GL_ARRAY_BUFFER, vCount * sizeof(Vertex), vertices, GL_STATIC_DRAW);
|
||||||
|
|
||||||
@@ -74,40 +76,50 @@ struct Mesh {
|
|||||||
if (Core::support.VAO) {
|
if (Core::support.VAO) {
|
||||||
range.aIndex = aIndex++;
|
range.aIndex = aIndex++;
|
||||||
range.bind(VAO);
|
range.bind(VAO);
|
||||||
bind();
|
bind(true);
|
||||||
range.setup();
|
range.setup(NULL);
|
||||||
} else
|
} else
|
||||||
range.aIndex = -1;
|
range.aIndex = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void bind() {
|
void bind(bool force = false) {
|
||||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ID[0]);
|
if (force || Core::active.iBuffer != ID[0])
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, ID[1]);
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, Core::active.iBuffer = ID[0]);
|
||||||
|
if (force || Core::active.vBuffer != ID[1])
|
||||||
glEnableVertexAttribArray(aCoord);
|
glBindBuffer(GL_ARRAY_BUFFER, Core::active.vBuffer = ID[1]);
|
||||||
glEnableVertexAttribArray(aTexCoord);
|
|
||||||
glEnableVertexAttribArray(aNormal);
|
|
||||||
glEnableVertexAttribArray(aColor);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DIP(const MeshRange &range) {
|
void unbind() {
|
||||||
glDrawElements(GL_TRIANGLES, range.iCount, GL_UNSIGNED_SHORT, (GLvoid*)(range.iStart * sizeof(Index)));
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, Core::active.iBuffer = 0);
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, Core::active.vBuffer = 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DIP(const MeshRange &range, Index *iOffset = 0) {
|
||||||
|
glDrawElements(GL_TRIANGLES, range.iCount, GL_UNSIGNED_SHORT, (GLvoid*)(iOffset + range.iStart) );
|
||||||
Core::stats.dips++;
|
Core::stats.dips++;
|
||||||
Core::stats.tris += range.iCount / 3;
|
Core::stats.tris += range.iCount / 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
void render(const MeshRange &range) {
|
void render(const MeshRange &range, Index *iOffset = NULL, Vertex *vOffset = NULL) {
|
||||||
range.bind(VAO);
|
range.bind(VAO);
|
||||||
|
|
||||||
|
if (vOffset) {
|
||||||
|
unbind();
|
||||||
|
range.setup(vOffset);
|
||||||
|
} else if (range.aIndex == -1) {
|
||||||
|
bind();
|
||||||
|
range.setup(NULL);
|
||||||
|
};
|
||||||
|
|
||||||
if (Core::active.stencilTwoSide && Core::support.stencil == 0) {
|
if (Core::active.stencilTwoSide && Core::support.stencil == 0) {
|
||||||
Core::setCulling(cfBack);
|
Core::setCulling(cfBack);
|
||||||
glStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
|
glStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
|
||||||
DIP(range);
|
DIP(range, iOffset);
|
||||||
Core::setCulling(cfFront);
|
Core::setCulling(cfFront);
|
||||||
glStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
|
glStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
|
||||||
}
|
}
|
||||||
|
|
||||||
DIP(range);
|
DIP(range, iOffset);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -904,22 +916,39 @@ struct MeshBuilder {
|
|||||||
if (tex) addTexCoord(vertices, vCount, tex, false);
|
if (tex) addTexCoord(vertices, vCount, tex, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void addSprite(Index *indices, Vertex *vertices, int &iCount, int &vCount, int vStart, int16 x, int16 y, int16 z, const TR::SpriteTexture &sprite, uint8 intensity) {
|
void addSprite(Index *indices, Vertex *vertices, int &iCount, int &vCount, int vStart, int16 x, int16 y, int16 z, const TR::SpriteTexture &sprite, uint8 intensity, bool expand = false) {
|
||||||
addQuad(indices, iCount, vCount, vStart, NULL, NULL);
|
addQuad(indices, iCount, vCount, vStart, NULL, NULL);
|
||||||
|
|
||||||
Vertex *quad = &vertices[vCount];
|
Vertex *quad = &vertices[vCount];
|
||||||
|
|
||||||
quad[0].coord = quad[1].coord = quad[2].coord = quad[3].coord = { x, y, z, 0 };
|
int16 x0, y0, x1, y1;
|
||||||
|
|
||||||
|
if (expand) {
|
||||||
|
x0 = x + int16(sprite.l);
|
||||||
|
y0 = y + int16(sprite.t);
|
||||||
|
x1 = x + int16(sprite.r);
|
||||||
|
y1 = y + int16(sprite.b);
|
||||||
|
} else {
|
||||||
|
x0 = x1 = x;
|
||||||
|
y0 = y1 = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
quad[0].coord = { x0, y0, z, 0 };
|
||||||
|
quad[1].coord = { x1, y0, z, 0 };
|
||||||
|
quad[2].coord = { x1, y1, z, 0 };
|
||||||
|
quad[3].coord = { x0, y1, z, 0 };
|
||||||
|
|
||||||
|
|
||||||
quad[0].normal = quad[1].normal = quad[2].normal = quad[3].normal = { 0, 0, 0, 0 };
|
quad[0].normal = quad[1].normal = quad[2].normal = quad[3].normal = { 0, 0, 0, 0 };
|
||||||
quad[0].color = quad[1].color = quad[2].color = quad[3].color = { 255, 255, 255, intensity };
|
quad[0].color = quad[1].color = quad[2].color = quad[3].color = { 255, 255, 255, intensity };
|
||||||
|
|
||||||
int tx = (sprite.tile % 4) * 256;
|
int tx = (sprite.tile % 4) * 256;
|
||||||
int ty = (sprite.tile / 4) * 256;
|
int ty = (sprite.tile / 4) * 256;
|
||||||
|
|
||||||
int16 u0 = ((tx + sprite.texCoord[0].x + 1) << 5);
|
int16 u0 = (((tx + sprite.texCoord[0].x) << 5));
|
||||||
int16 v0 = ((ty + sprite.texCoord[0].y + 1) << 5);
|
int16 v0 = (((ty + sprite.texCoord[0].y) << 5));
|
||||||
int16 u1 = ((tx + sprite.texCoord[1].x - 1) << 5);
|
int16 u1 = (((tx + sprite.texCoord[1].x) << 5));
|
||||||
int16 v1 = ((ty + sprite.texCoord[1].y - 1) << 5);
|
int16 v1 = (((ty + sprite.texCoord[1].y) << 5));
|
||||||
|
|
||||||
quad[0].texCoord = { u0, v0, sprite.l, sprite.t };
|
quad[0].texCoord = { u0, v0, sprite.l, sprite.t };
|
||||||
quad[1].texCoord = { u1, v0, sprite.r, sprite.t };
|
quad[1].texCoord = { u1, v0, sprite.r, sprite.t };
|
||||||
@@ -933,6 +962,15 @@ struct MeshBuilder {
|
|||||||
mesh->bind();
|
mesh->bind();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void renderBuffer(Index *indices, Vertex *vertices, int iCount) {
|
||||||
|
MeshRange range;
|
||||||
|
range.iStart = 0;
|
||||||
|
range.vStart = 0;
|
||||||
|
range.iCount = iCount;
|
||||||
|
|
||||||
|
mesh->render(range, indices, vertices);
|
||||||
|
}
|
||||||
|
|
||||||
void renderRoomGeometry(int roomIndex, bool transparent) {
|
void renderRoomGeometry(int roomIndex, bool transparent) {
|
||||||
ASSERT(rooms[roomIndex].geometry[transparent].iCount > 0);
|
ASSERT(rooms[roomIndex].geometry[transparent].iCount > 0);
|
||||||
mesh->render(rooms[roomIndex].geometry[transparent]);
|
mesh->render(rooms[roomIndex].geometry[transparent]);
|
||||||
@@ -1000,57 +1038,6 @@ struct MeshBuilder {
|
|||||||
d[0] = clGrayD; d[1] = clGrayL; d[2] = clGrayL; d[3] = clGrayL; d[4] = clGrayL; d+= 1024;
|
d[0] = clGrayD; d[1] = clGrayL; d[2] = clGrayL; d[3] = clGrayL; d[4] = clGrayL; d+= 1024;
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
void textOut(const vec2 &pos, const vec4 &color, char *text) {
|
|
||||||
const static uint8 char_width[110] = {
|
|
||||||
14, 11, 11, 11, 11, 11, 11, 13, 8, 11, 12, 11, 13, 13, 12, 11, 12, 12, 11, 12, 13, 13, 13, 12,
|
|
||||||
12, 11, 9, 9, 9, 9, 9, 9, 9, 9, 5, 9, 9, 5, 12, 10, 9, 9, 9, 8, 9, 8, 9, 9, 11, 9, 9, 9, 12, 8,
|
|
||||||
10, 10, 10, 10, 10, 9, 10, 10, 5, 5, 5, 11, 9, 10, 8, 6, 6, 7, 7, 3, 11, 8, 13, 16, 9, 4, 12, 12,
|
|
||||||
7, 5, 7, 7, 7, 7, 7, 7, 7, 7, 16, 14, 14, 14, 16, 16, 16, 16, 16, 12, 14, 8, 8, 8, 8, 8, 8, 8 };
|
|
||||||
|
|
||||||
static const uint8 char_map[102] = {
|
|
||||||
0, 64, 66, 78, 77, 74, 78, 79, 69, 70, 92, 72, 63, 71, 62, 68, 52, 53, 54, 55, 56, 57, 58, 59,
|
|
||||||
60, 61, 73, 73, 66, 74, 75, 65, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
|
|
||||||
18, 19, 20, 21, 22, 23, 24, 25, 80, 76, 81, 97, 98, 77, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
|
|
||||||
37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 100, 101, 102, 67, 0, 0, 0, 0, 0, 0, 0 };
|
|
||||||
|
|
||||||
if (!text) return;
|
|
||||||
|
|
||||||
Core::active.shader->setParam(uMaterial, vec4(1.0, 0.0, 0.0, 1.0));
|
|
||||||
//text = "a: b";
|
|
||||||
Basis basis;
|
|
||||||
basis.identity();
|
|
||||||
basis.translate(vec3(pos.x, pos.y, 0.0f));
|
|
||||||
// text = "A";
|
|
||||||
while (char c = *text++) {
|
|
||||||
|
|
||||||
int frame = c > 10 ? (c > 15 ? char_map[c - 32] : c + 91) : c + 81;
|
|
||||||
|
|
||||||
|
|
||||||
//int frame = T_remapASCII[c - 32];
|
|
||||||
/*
|
|
||||||
if (c >= 'A' && c <= 'Z')
|
|
||||||
frame = c - 'A';
|
|
||||||
else if (c >= 'a' && c <= 'z')
|
|
||||||
frame = 26 + c - 'a';
|
|
||||||
else if (c >= '0' && c <= '9')
|
|
||||||
frame = 52 + c - '0';
|
|
||||||
else {
|
|
||||||
if (c == ' ')
|
|
||||||
m.translate(vec3(16, 0.0f, 0.0f));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
if (c == ' ' || c == '_') {
|
|
||||||
basis.translate(vec3(char_width[0], 0.0f, 0.0f));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Core::active.shader->setParam(uBasis, basis);
|
|
||||||
renderSprite(15, frame);
|
|
||||||
basis.translate(vec3(char_width[frame], 0.0f, 0.0f));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
@@ -98,7 +98,7 @@ int getPOV(int x, int y) {
|
|||||||
|
|
||||||
InputKey keyToInputKey(int code) {
|
InputKey keyToInputKey(int code) {
|
||||||
int codes[] = {
|
int codes[] = {
|
||||||
21, 22, 19, 20, 62, 66, 111, 59, 113, 57,
|
21, 22, 19, 20, 62, 61, 66, 111, 59, 113, 57,
|
||||||
7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
|
7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
|
||||||
29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41,
|
29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41,
|
||||||
42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,
|
42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,
|
||||||
|
@@ -78,7 +78,7 @@ int getTime() {
|
|||||||
|
|
||||||
InputKey keyToInputKey(int code) {
|
InputKey keyToInputKey(int code) {
|
||||||
int codes[] = {
|
int codes[] = {
|
||||||
113, 114, 111, 116, 65, 36, 9, 50, 37, 64,
|
113, 114, 111, 116, 65, 23, 36, 9, 50, 37, 64,
|
||||||
19, 10, 11, 12, 13, 14, 15, 16, 17, 18,
|
19, 10, 11, 12, 13, 14, 15, 16, 17, 18,
|
||||||
38, 56, 54, 40, 26, 41, 42, 43, 31, 44, 45, 46, 58,
|
38, 56, 54, 40, 26, 41, 42, 43, 31, 44, 45, 46, 58,
|
||||||
57, 32, 33, 24, 27, 39, 28, 30, 55, 25, 53, 29, 52,
|
57, 32, 33, 24, 27, 39, 28, 30, 55, 25, 53, 29, 52,
|
||||||
|
@@ -42,7 +42,7 @@ void soundInit() {
|
|||||||
// common input functions
|
// common input functions
|
||||||
InputKey keyToInputKey(int code) {
|
InputKey keyToInputKey(int code) {
|
||||||
static const int codes[] = {
|
static const int codes[] = {
|
||||||
0x7B, 0x7C, 0x7E, 0x7D, 0x31, 0x24, 0x35, 0x38, 0x3B, 0x3A,
|
0x7B, 0x7C, 0x7E, 0x7D, 0x31, 0x30, 0x24, 0x35, 0x38, 0x3B, 0x3A,
|
||||||
0x1D, 0x12, 0x13, 0x14, 0x15, 0x17, 0x16, 0x1A, 0x1C, 0x19, // 0..9
|
0x1D, 0x12, 0x13, 0x14, 0x15, 0x17, 0x16, 0x1A, 0x1C, 0x19, // 0..9
|
||||||
0x00, 0x0B, 0x08, 0x02, 0x0E, 0x03, 0x05, 0x04, 0x22, 0x26, 0x28, 0x25, 0x2E, // A..M
|
0x00, 0x0B, 0x08, 0x02, 0x0E, 0x03, 0x05, 0x04, 0x22, 0x26, 0x28, 0x25, 0x2E, // A..M
|
||||||
0x2D, 0x1F, 0x23, 0x0C, 0x0F, 0x01, 0x11, 0x20, 0x09, 0x0D, 0x07, 0x10, 0x06, // N..Z
|
0x2D, 0x1F, 0x23, 0x0C, 0x0F, 0x01, 0x11, 0x20, 0x09, 0x0D, 0x07, 0x10, 0x06, // N..Z
|
||||||
|
@@ -142,14 +142,14 @@
|
|||||||
<!-- <label for="browseFile">Browse Level</label> -->
|
<!-- <label for="browseFile">Browse Level</label> -->
|
||||||
<input type="button" value="Browse Level" onclick="document.getElementById('browseFile').click();" /> (.PHD, .PSX)
|
<input type="button" value="Browse Level" onclick="document.getElementById('browseFile').click();" /> (.PHD, .PSX)
|
||||||
<input type="checkbox" id="isHome"><label>alternative model (home suit, gold etc.)</label>
|
<input type="checkbox" id="isHome"><label>alternative model (home suit, gold etc.)</label>
|
||||||
<br><br>
|
<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>
|
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>
|
||||||
controls:<br>
|
controls:<br>
|
||||||
keyboad: move - WASD / arrows, jump - Space, action - E/Ctrl, draw weapon - Q, change weapon - 1-4, walk - Shift, side steps - ZX/walk+direction, camera - MouseR)<br>
|
keyboad: move - arrow keys, jump - Alt, action - Ctrl, draw weapon - Space, inventory - Tab, walk - Shift, side steps - ZX, change view - V)<br>
|
||||||
gamepad: PSX controls for Xbox controller<br>
|
gamepad: PSX controls for Xbox controller<br>
|
||||||
Change view: V<br>
|
|
||||||
Time Control: R - slow motion, T - fast motion<br>
|
Time Control: R - slow motion, T - fast motion<br>
|
||||||
FullScreen: Alt + Enter
|
FullScreen: Alt + Enter
|
||||||
|
</p>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<script>(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)})(window,document,'script','//www.google-analytics.com/analytics.js','ga');ga('create', 'UA-60009035-1', 'auto');ga('send', 'pageview');</script>
|
<script>(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)})(window,document,'script','//www.google-analytics.com/analytics.js','ga');ga('create', 'UA-60009035-1', 'auto');ga('send', 'pageview');</script>
|
||||||
|
@@ -178,7 +178,7 @@ void changeWindowMode() {
|
|||||||
|
|
||||||
InputKey keyToInputKey(int code) {
|
InputKey keyToInputKey(int code) {
|
||||||
static const int codes[] = {
|
static const int codes[] = {
|
||||||
0x25, 0x27, 0x26, 0x28, 0x20, 0x0D, 0x1B, 0x10, 0x11, 0x12,
|
0x25, 0x27, 0x26, 0x28, 0x20, 0x09, 0x0D, 0x1B, 0x10, 0x11, 0x12,
|
||||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
||||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
|
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
|
||||||
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
|
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
|
||||||
|
@@ -1,9 +1,13 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="..\..\libs\minimp3\minimp3.cpp" />
|
|
||||||
<ClCompile Include="..\..\libs\stb_vorbis\stb_vorbis.c" />
|
|
||||||
<ClCompile Include="main.cpp" />
|
<ClCompile Include="main.cpp" />
|
||||||
|
<ClCompile Include="..\..\libs\minimp3\minimp3.cpp">
|
||||||
|
<Filter>libs\minimp3</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\..\libs\stb_vorbis\stb_vorbis.c">
|
||||||
|
<Filter>libs\stb_vorbis</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="..\..\animation.h" />
|
<ClInclude Include="..\..\animation.h" />
|
||||||
@@ -22,8 +26,6 @@
|
|||||||
<ClInclude Include="..\..\inventory.h" />
|
<ClInclude Include="..\..\inventory.h" />
|
||||||
<ClInclude Include="..\..\lara.h" />
|
<ClInclude Include="..\..\lara.h" />
|
||||||
<ClInclude Include="..\..\level.h" />
|
<ClInclude Include="..\..\level.h" />
|
||||||
<ClInclude Include="..\..\libs\minimp3\libc.h" />
|
|
||||||
<ClInclude Include="..\..\libs\minimp3\minimp3.h" />
|
|
||||||
<ClInclude Include="..\..\mesh.h" />
|
<ClInclude Include="..\..\mesh.h" />
|
||||||
<ClInclude Include="..\..\shader.h" />
|
<ClInclude Include="..\..\shader.h" />
|
||||||
<ClInclude Include="..\..\sound.h" />
|
<ClInclude Include="..\..\sound.h" />
|
||||||
@@ -32,6 +34,12 @@
|
|||||||
<ClInclude Include="..\..\format.h" />
|
<ClInclude Include="..\..\format.h" />
|
||||||
<ClInclude Include="..\..\trigger.h" />
|
<ClInclude Include="..\..\trigger.h" />
|
||||||
<ClInclude Include="..\..\utils.h" />
|
<ClInclude Include="..\..\utils.h" />
|
||||||
|
<ClInclude Include="..\..\libs\minimp3\minimp3.h">
|
||||||
|
<Filter>libs\minimp3</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\..\libs\minimp3\libc.h">
|
||||||
|
<Filter>libs\minimp3</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="..\..\shaders\depth.glsl">
|
<None Include="..\..\shaders\depth.glsl">
|
||||||
@@ -60,5 +68,14 @@
|
|||||||
<Filter Include="shaders">
|
<Filter Include="shaders">
|
||||||
<UniqueIdentifier>{3fcb6c00-268a-4570-b6ca-3bf1cf1e10d7}</UniqueIdentifier>
|
<UniqueIdentifier>{3fcb6c00-268a-4570-b6ca-3bf1cf1e10d7}</UniqueIdentifier>
|
||||||
</Filter>
|
</Filter>
|
||||||
|
<Filter Include="libs">
|
||||||
|
<UniqueIdentifier>{0400c04a-7900-49a2-9b9d-6667d815168c}</UniqueIdentifier>
|
||||||
|
</Filter>
|
||||||
|
<Filter Include="libs\minimp3">
|
||||||
|
<UniqueIdentifier>{bc55e021-2a62-4bc1-8b80-0fd900f20b2b}</UniqueIdentifier>
|
||||||
|
</Filter>
|
||||||
|
<Filter Include="libs\stb_vorbis">
|
||||||
|
<UniqueIdentifier>{f26df5a9-d32a-4e5e-948c-e632c42be9fa}</UniqueIdentifier>
|
||||||
|
</Filter>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
@@ -28,7 +28,7 @@ int getTime() {
|
|||||||
// common input functions
|
// common input functions
|
||||||
InputKey keyToInputKey(int code) {
|
InputKey keyToInputKey(int code) {
|
||||||
static const int codes[] = {
|
static const int codes[] = {
|
||||||
VK_LEFT, VK_RIGHT, VK_UP, VK_DOWN, VK_SPACE, VK_RETURN, VK_ESCAPE, VK_SHIFT, VK_CONTROL, VK_MENU,
|
VK_LEFT, VK_RIGHT, VK_UP, VK_DOWN, VK_SPACE, VK_TAB, VK_RETURN, VK_ESCAPE, VK_SHIFT, VK_CONTROL, VK_MENU,
|
||||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
||||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
|
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
|
||||||
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
|
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
|
||||||
|
@@ -56,7 +56,7 @@ struct Shader {
|
|||||||
enum Type : GLint {
|
enum Type : GLint {
|
||||||
DEFAULT = 0,
|
DEFAULT = 0,
|
||||||
/* shader */ SPRITE = 0, FLASH = 1, ROOM = 2, ENTITY = 3, MIRROR = 4,
|
/* shader */ SPRITE = 0, FLASH = 1, ROOM = 2, ENTITY = 3, MIRROR = 4,
|
||||||
/* filter */ FILTER_DOWNSAMPLE = 1,
|
/* filter */ FILTER_DOWNSAMPLE = 1, FILTER_GRAYSCALE = 2, FILTER_BLUR = 3, FILTER_MIXER = 4,
|
||||||
/* water */ WATER_DROP = 0, WATER_STEP = 1, WATER_CAUSTICS = 2, WATER_MASK = 3, WATER_COMPOSE = 4,
|
/* water */ WATER_DROP = 0, WATER_STEP = 1, WATER_CAUSTICS = 2, WATER_MASK = 3, WATER_COMPOSE = 4,
|
||||||
MAX = 5
|
MAX = 5
|
||||||
};
|
};
|
||||||
|
@@ -17,10 +17,12 @@ uniform int uType;
|
|||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
uniform sampler2D sDiffuse;
|
uniform sampler2D sDiffuse;
|
||||||
uniform vec4 uParam; // texture size
|
uniform sampler2D sNormal;
|
||||||
|
|
||||||
vec4 downsample() {
|
uniform vec4 uParam;
|
||||||
float k = 1.0 / uParam.x;
|
|
||||||
|
vec4 downsample() { // uParam (textureSize, unused, unused, unused)
|
||||||
|
float k = 1.0 / uParam.x; // inverted texture size
|
||||||
|
|
||||||
vec4 color = vec4(0.0);
|
vec4 color = vec4(0.0);
|
||||||
for (float y = -1.5; y < 2.0; y++)
|
for (float y = -1.5; y < 2.0; y++)
|
||||||
@@ -35,10 +37,46 @@ uniform int uType;
|
|||||||
return vec4(color.xyz / color.w, 1.0);
|
return vec4(color.xyz / color.w, 1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vec4 grayscale() { // uParam (factor, unused, unused, unused)
|
||||||
|
vec4 color = texture2D(sDiffuse, vTexCoord);
|
||||||
|
vec3 gray = vec3(dot(color, vec4(0.299, 0.587, 0.114, 0.0)));
|
||||||
|
return vec4(mix(color.xyz, gray, uParam.x), color.w);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 blur() { // uParam (dirX, dirY, 1 / textureSize, unused)
|
||||||
|
const vec3 offset = vec3(0.0, 1.3846153846, 3.2307692308);
|
||||||
|
const vec3 weight = vec3(0.2270270270, 0.3162162162, 0.0702702703);
|
||||||
|
|
||||||
|
vec2 dir = uParam.xy * uParam.z;
|
||||||
|
vec4 color = texture2D(sDiffuse, vTexCoord) * weight[0];
|
||||||
|
color += texture2D(sDiffuse, vTexCoord + dir * offset[1]) * weight[1];
|
||||||
|
color += texture2D(sDiffuse, vTexCoord - dir * offset[1]) * weight[1];
|
||||||
|
color += texture2D(sDiffuse, vTexCoord + dir * offset[2]) * weight[2];
|
||||||
|
color += texture2D(sDiffuse, vTexCoord - dir * offset[2]) * weight[2];
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 mixer() { // uParam (lerp factor from diffuse to normal textures, multiply, unused, unused)
|
||||||
|
return mix(texture2D(sDiffuse, vTexCoord), texture2D(sNormal, vTexCoord), uParam.x) * uParam.y;
|
||||||
|
}
|
||||||
|
|
||||||
vec4 filter() {
|
vec4 filter() {
|
||||||
#ifdef FILTER_DOWNSAMPLE
|
#ifdef FILTER_DOWNSAMPLE
|
||||||
return downsample();
|
return downsample();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef FILTER_GRAYSCALE
|
||||||
|
return grayscale();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef FILTER_BLUR
|
||||||
|
return blur();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef FILTER_MIXER
|
||||||
|
return mixer();
|
||||||
|
#endif
|
||||||
|
|
||||||
return texture2D(sDiffuse, vTexCoord);
|
return texture2D(sDiffuse, vTexCoord);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -24,7 +24,7 @@ varying vec2 vTexCoord;
|
|||||||
uniform vec4 uMaterial;
|
uniform vec4 uMaterial;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
gl_FragColor = /* texture2D(sDiffuse, vTexCoord) * */ uMaterial;
|
gl_FragColor = texture2D(sDiffuse, vTexCoord) * uMaterial;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
)===="
|
)===="
|
207
src/ui.h
207
src/ui.h
@@ -4,137 +4,100 @@
|
|||||||
#include "core.h"
|
#include "core.h"
|
||||||
#include "controller.h"
|
#include "controller.h"
|
||||||
|
|
||||||
struct UI {
|
namespace UI {
|
||||||
enum TouchButton { bNone, bWeapon, bWalk, bAction, bJump, bMAX };
|
|
||||||
enum TouchZone { zMove, zLook, zButton, zMAX };
|
|
||||||
|
|
||||||
IGame *game;
|
IGame *game;
|
||||||
float touchTimerVis, touchTimerTap;
|
|
||||||
InputKey touch[zMAX];
|
|
||||||
TouchButton btn;
|
|
||||||
vec2 btnPos[bMAX];
|
|
||||||
float btnRadius;
|
|
||||||
bool doubleTap;
|
|
||||||
|
|
||||||
UI(IGame *game) : game(game), touchTimerVis(0.0f), touchTimerTap(0.0f), doubleTap(false) {
|
const static uint8 char_width[110] = {
|
||||||
touch[zMove] = touch[zLook] = touch[zButton] = ikNone;
|
14, 11, 11, 11, 11, 11, 11, 13, 8, 11, 12, 11, 13, 13, 12, 11, 12, 12, 11, 12, 13, 13, 13, 12,
|
||||||
|
12, 11, 9, 9, 9, 9, 9, 9, 9, 9, 5, 9, 9, 5, 12, 10, 9, 9, 9, 8, 9, 8, 9, 9, 11, 9, 9, 9, 12, 8,
|
||||||
|
10, 10, 10, 10, 10, 9, 10, 10, 5, 5, 5, 11, 9, 10, 8, 6, 6, 7, 7, 3, 11, 8, 13, 16, 9, 4, 12, 12,
|
||||||
|
7, 5, 7, 7, 7, 7, 7, 7, 7, 7, 16, 14, 14, 14, 16, 16, 16, 16, 16, 12, 14, 8, 8, 8, 8, 8, 8, 8 };
|
||||||
|
|
||||||
|
static const uint8 char_map[102] = {
|
||||||
|
0, 64, 66, 78, 77, 74, 78, 79, 69, 70, 92, 72, 63, 71, 62, 68, 52, 53, 54, 55, 56, 57, 58, 59,
|
||||||
|
60, 61, 73, 73, 66, 74, 75, 65, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
|
||||||
|
18, 19, 20, 21, 22, 23, 24, 25, 80, 76, 81, 97, 98, 77, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
|
||||||
|
37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 100, 101, 102, 67, 0, 0, 0, 0, 0, 0, 0 };
|
||||||
|
|
||||||
|
enum Align { aLeft, aRight, aCenter };
|
||||||
|
|
||||||
|
inline int charRemap(char c) {
|
||||||
|
return c > 10 ? (c > 15 ? char_map[c - 32] : c + 91) : c + 81;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool checkTouchZone(TouchZone zone) {
|
int getTextWidth(const char *text) {
|
||||||
InputKey &t = touch[zone];
|
int width = 0;
|
||||||
if (t != ikNone && !Input::down[t]) {
|
|
||||||
t = ikNone;
|
while (char c = *text++)
|
||||||
return true;
|
width += (c == ' ' || c == '_') ? 6 : (char_width[charRemap(c)] + 1);
|
||||||
}
|
|
||||||
return false;
|
return width - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void getTouchDir(InputKey touch, vec2 &dir) {
|
#define MAX_CHARS 1024
|
||||||
vec2 delta = vec2(0.0f);
|
|
||||||
if (touch == ikNone)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Input::Touch &t = Input::touch[touch - ikTouchA];
|
struct {
|
||||||
vec2 d = t.pos - t.start;
|
Vertex vertices[MAX_CHARS * 4];
|
||||||
float len = d.length();
|
Index indices[MAX_CHARS * 6];
|
||||||
if (len > EPS)
|
int iCount;
|
||||||
delta = d * (min(len / 100.0f, 1.0f) / len);
|
int vCount;
|
||||||
|
} buffer;
|
||||||
|
|
||||||
dir = delta;
|
void textBegin() {
|
||||||
|
buffer.iCount = buffer.vCount = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void getTouchButton(const vec2 &pos) {
|
void textEnd(IGame *game) {
|
||||||
btn = bMAX;
|
if (buffer.iCount > 0) {
|
||||||
float minDist = 1000.0f;
|
game->getMesh()->renderBuffer(buffer.indices, buffer.vertices, buffer.iCount);
|
||||||
for (int i = 0; i < bMAX; i++) {
|
buffer.iCount = buffer.vCount = 0;
|
||||||
float d = (pos - btnPos[i]).length();
|
|
||||||
if (d < minDist) {
|
|
||||||
minDist = d;
|
|
||||||
btn = TouchButton(i);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void touchSetDown(bool down) {
|
void textOut(IGame *game, const vec2 &pos, const char *text, Align align = aLeft, float width = 0) {
|
||||||
switch (btn) {
|
if (!text) return;
|
||||||
case bWeapon : Input::setDown(ikJoyY, down); break;
|
|
||||||
case bWalk : Input::setDown(ikJoyLB, down); break;
|
TR::Level *level = game->getLevel();
|
||||||
case bAction : Input::setDown(ikJoyA, down); break;
|
MeshBuilder *mesh = game->getMesh();
|
||||||
case bJump : Input::setDown(ikJoyX, down); break;
|
|
||||||
default : ;
|
int seq = level->extra.glyphSeq;
|
||||||
|
|
||||||
|
int x = int(pos.x);
|
||||||
|
int y = int(pos.y);
|
||||||
|
|
||||||
|
if (align == aCenter)
|
||||||
|
x += (int(width) - getTextWidth(text)) / 2;
|
||||||
|
|
||||||
|
if (align == aRight)
|
||||||
|
x += int(width) - getTextWidth(text);
|
||||||
|
|
||||||
|
while (char c = *text++) {
|
||||||
|
if (c == ' ' || c == '_') {
|
||||||
|
x += 6;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int frame = charRemap(c);
|
||||||
|
|
||||||
|
if (buffer.iCount == MAX_CHARS * 6)
|
||||||
|
textEnd(game);
|
||||||
|
|
||||||
|
TR::SpriteTexture &sprite = level->spriteTextures[level->spriteSequences[seq].sStart + frame];
|
||||||
|
mesh->addSprite(buffer.indices, buffer.vertices, buffer.iCount, buffer.vCount, 0, x, y, 0, sprite, 255, true);
|
||||||
|
|
||||||
|
x += char_width[frame] + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef MAX_CHARS
|
||||||
|
|
||||||
|
|
||||||
|
void init(IGame *game) {
|
||||||
|
UI::game = game;
|
||||||
}
|
}
|
||||||
|
|
||||||
void update() {
|
void update() {
|
||||||
if (touchTimerTap > 0.0f)
|
//
|
||||||
touchTimerTap = max(0.0f, touchTimerTap - Core::deltaTime);
|
|
||||||
|
|
||||||
if (touch[zMove] != ikNone || touch[zLook] != ikNone || touch[zButton] != ikNone)
|
|
||||||
touchTimerVis = 30.0f;
|
|
||||||
else
|
|
||||||
if (touchTimerVis > 0.0f)
|
|
||||||
touchTimerVis = max(0.0f, touchTimerVis - Core::deltaTime);
|
|
||||||
|
|
||||||
// update buttons
|
|
||||||
float offset = Core::height * 0.25f;
|
|
||||||
float radius = offset;
|
|
||||||
vec2 center = vec2(Core::width - offset * 0.7f, Core::height - offset * 0.7f);
|
|
||||||
|
|
||||||
btnPos[bWeapon] = center;
|
|
||||||
btnPos[bJump] = center + vec2(cos(-PI * 0.5f), sin(-PI * 0.5f)) * radius;
|
|
||||||
btnPos[bAction] = center + vec2(cos(-PI * 3.0f / 4.0f), sin(-PI * 3.0f / 4.0f)) * radius;
|
|
||||||
btnPos[bWalk] = center + vec2(cos(-PI), sin(-PI)) * radius;
|
|
||||||
btnRadius = Core::height * (25.0f / 1080.0f);
|
|
||||||
|
|
||||||
// touch update
|
|
||||||
if (checkTouchZone(zMove))
|
|
||||||
Input::joy.L = vec2(0.0f);
|
|
||||||
|
|
||||||
if (checkTouchZone(zLook))
|
|
||||||
Input::joy.R = vec2(0.0f);
|
|
||||||
|
|
||||||
if (checkTouchZone(zButton)) {
|
|
||||||
touchSetDown(false);
|
|
||||||
btn = bNone;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (doubleTap) {
|
|
||||||
doubleTap = false;
|
|
||||||
Input::setDown(ikJoyB, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
float zoneSize = Core::width / 3.0f;
|
|
||||||
|
|
||||||
for (int i = 0; i < COUNT(Input::touch); i++) {
|
|
||||||
InputKey key = InputKey(i + ikTouchA);
|
|
||||||
|
|
||||||
if (!Input::down[key]) continue;
|
|
||||||
if (key == touch[zMove] || key == touch[zLook] || key == touch[zButton]) continue;
|
|
||||||
|
|
||||||
int zone = clamp(int(Input::touch[i].pos.x / zoneSize), 0, 2);
|
|
||||||
InputKey &t = touch[zone];
|
|
||||||
|
|
||||||
if (t == ikNone) {
|
|
||||||
t = key;
|
|
||||||
if (zone == zMove) {
|
|
||||||
if (touchTimerTap > 0.0f && touchTimerTap < 0.3f) { // 100 ms gap to reduce false positives (bad touch sensors)
|
|
||||||
doubleTap = true;
|
|
||||||
touchTimerTap = 0.0f;
|
|
||||||
} else
|
|
||||||
touchTimerTap = 0.3f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// set active touches as gamepad controls
|
|
||||||
getTouchDir(touch[zMove], Input::joy.L);
|
|
||||||
getTouchDir(touch[zLook], Input::joy.R);
|
|
||||||
if (touch[zButton] != ikNone && btn == bNone) {
|
|
||||||
getTouchButton(Input::touch[touch[zButton] - ikTouchA].pos);
|
|
||||||
touchSetDown(true);
|
|
||||||
}
|
|
||||||
if (doubleTap)
|
|
||||||
Input::setDown(ikJoyB, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void renderControl(const vec2 &pos, float size, bool active) {
|
void renderControl(const vec2 &pos, float size, bool active) {
|
||||||
@@ -144,7 +107,9 @@ struct UI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void renderTouch() {
|
void renderTouch() {
|
||||||
if (touchTimerVis <= 0.0f) return;
|
game->setupBinding();
|
||||||
|
|
||||||
|
if (Input::touchTimerVis <= 0.0f) return;
|
||||||
|
|
||||||
Core::setDepthTest(false);
|
Core::setDepthTest(false);
|
||||||
Core::setBlending(bmAlpha);
|
Core::setBlending(bmAlpha);
|
||||||
@@ -157,15 +122,15 @@ struct UI {
|
|||||||
float offset = Core::height * 0.25f;
|
float offset = Core::height * 0.25f;
|
||||||
|
|
||||||
vec2 pos = vec2(offset, Core::height - offset);
|
vec2 pos = vec2(offset, Core::height - offset);
|
||||||
if (Input::down[touch[zMove]]) {
|
if (Input::down[Input::touchKey[Input::zMove]]) {
|
||||||
Input::Touch &t = Input::touch[touch[zMove] - ikTouchA];
|
Input::Touch &t = Input::touch[Input::touchKey[Input::zMove] - ikTouchA];
|
||||||
renderControl(t.pos, btnRadius, true);
|
renderControl(t.pos, Input::btnRadius, true);
|
||||||
pos = t.start;
|
pos = t.start;
|
||||||
}
|
}
|
||||||
renderControl(pos, btnRadius, false);
|
renderControl(pos, Input::btnRadius, false);
|
||||||
|
|
||||||
for (int i = bWeapon; i < bMAX; i++)
|
for (int i = Input::bWeapon; i < Input::bMAX; i++)
|
||||||
renderControl(btnPos[i], btnRadius, btn == i);
|
renderControl(Input::btnPos[i], Input::btnRadius, Input::btn == i);
|
||||||
|
|
||||||
Core::setCulling(cfFront);
|
Core::setCulling(cfFront);
|
||||||
Core::setBlending(bmNone);
|
Core::setBlending(bmNone);
|
||||||
|
27
src/utils.h
27
src/utils.h
@@ -78,12 +78,6 @@ inline void swap(T &a, T &b) {
|
|||||||
b = tmp;
|
b = tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
float lerp(float a, float b, float t) {
|
|
||||||
if (t <= 0.0f) return a;
|
|
||||||
if (t >= 1.0f) return b;
|
|
||||||
return a + (b - a) * t;
|
|
||||||
}
|
|
||||||
|
|
||||||
float clampAngle(float a) {
|
float clampAngle(float a) {
|
||||||
return a < -PI ? a + PI2 : (a >= PI ? a - PI2 : a);
|
return a < -PI ? a + PI2 : (a >= PI ? a - PI2 : a);
|
||||||
}
|
}
|
||||||
@@ -113,6 +107,22 @@ float decrease(float delta, float &value, float &speed) {
|
|||||||
return 0.0f;
|
return 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float hermite(float x) {
|
||||||
|
return x * x * (3.0f - 2.0f * x);
|
||||||
|
}
|
||||||
|
|
||||||
|
float lerp(float a, float b, float t) {
|
||||||
|
if (t <= 0.0f) return a;
|
||||||
|
if (t >= 1.0f) return b;
|
||||||
|
return a + (b - a) * t;
|
||||||
|
}
|
||||||
|
|
||||||
|
float lerpAngle(float a, float b, float t) {
|
||||||
|
if (t <= 0.0f) return a;
|
||||||
|
if (t >= 1.0f) return b;
|
||||||
|
return a + shortAngle(a, b) * t;
|
||||||
|
}
|
||||||
|
|
||||||
int nextPow2(uint32 x) {
|
int nextPow2(uint32 x) {
|
||||||
x--;
|
x--;
|
||||||
x |= x >> 1;
|
x |= x >> 1;
|
||||||
@@ -421,8 +431,13 @@ struct mat4 {
|
|||||||
mat4(float fov, float aspect, float znear, float zfar) {
|
mat4(float fov, float aspect, float znear, float zfar) {
|
||||||
float k = 1.0f / tanf(fov * 0.5f * DEG2RAD);
|
float k = 1.0f / tanf(fov * 0.5f * DEG2RAD);
|
||||||
identity();
|
identity();
|
||||||
|
if (aspect >= 1.0f) {
|
||||||
e00 = k / aspect;
|
e00 = k / aspect;
|
||||||
e11 = k;
|
e11 = k;
|
||||||
|
} else {
|
||||||
|
e00 = k;
|
||||||
|
e11 = k * aspect;
|
||||||
|
}
|
||||||
e22 = (znear + zfar) / (znear - zfar);
|
e22 = (znear + zfar) / (znear - zfar);
|
||||||
e33 = 0.0f;
|
e33 = 0.0f;
|
||||||
e32 = -1.0f;
|
e32 = -1.0f;
|
||||||
|
Reference in New Issue
Block a user