diff --git a/src/cache.h b/src/cache.h
index 71f85c9..a88f801 100644
--- a/src/cache.h
+++ b/src/cache.h
@@ -73,6 +73,8 @@ struct ShaderCache {
compile(Core::passCompose, Shader::ENTITY, FX_CLIP_PLANE);
compile(Core::passCompose, Shader::ENTITY, FX_UNDERWATER);
compile(Core::passCompose, Shader::ENTITY, FX_UNDERWATER | FX_CLIP_PLANE);
+ compile(Core::passCompose, Shader::ENTITY, FX_ALPHA_TEST);
+ compile(Core::passCompose, Shader::ENTITY, FX_ALPHA_TEST | FX_CLIP_PLANE);
compile(Core::passCompose, Shader::SPRITE, FX_ALPHA_TEST);
compile(Core::passCompose, Shader::SPRITE, FX_ALPHA_TEST | FX_CLIP_PLANE);
compile(Core::passCompose, Shader::SPRITE, FX_UNDERWATER | FX_ALPHA_TEST);
@@ -107,7 +109,7 @@ struct ShaderCache {
}
}
- const char *passNames[] = { "COMPOSE", "SHADOW", "AMBIENT", "FILTER", "WATER" };
+ const char *passNames[] = { "COMPOSE", "SHADOW", "AMBIENT", "WATER", "FILTER", "GUI" };
const char *src = NULL;
const char *typ = NULL;
switch (pass) {
@@ -146,6 +148,13 @@ struct ShaderCache {
sprintf(def, "%s#define PASS_%s\n#define FILTER_%s\n", ext, passNames[pass], typ);
break;
}
+ case Core::passGUI : {
+ static const char *typeNames[] = { "DEFAULT" };
+ src = GUI;
+ typ = typeNames[type];
+ sprintf(def, "%s#define PASS_%s\n", ext, passNames[pass]);
+ break;
+ }
default : ASSERT(false);
}
LOG("shader: compile %s -> %s %s%s%s\n", passNames[pass], typ, (fx & FX_UNDERWATER) ? "underwater " : "", (fx & FX_ALPHA_TEST) ? "alphaTest " : "", (fx & FX_CLIP_PLANE) ? "clipPlane" : "");
@@ -610,7 +619,7 @@ struct WaterCache {
Core::setCulling(cfFront);
// get refraction texture
- if (!refract || Core::width > refract->width || Core::height > refract->height) {
+ if (!refract || Core::width != refract->width || Core::height != refract->height) {
delete refract;
refract = new Texture(Core::width, Core::height, Texture::RGBA, false);
}
diff --git a/src/core.h b/src/core.h
index 0f93e2e..7c68e42 100644
--- a/src/core.h
+++ b/src/core.h
@@ -114,6 +114,11 @@
PFNGLOBJECTLABELPROC glObjectLabel;
PFNGLPUSHDEBUGGROUPPROC glPushDebugGroup;
PFNGLPOPDEBUGGROUPPROC glPopDebugGroup;
+ PFNGLGENQUERIESPROC glGenQueries;
+ PFNGLDELETEQUERIESPROC glDeleteQueries;
+ PFNGLGETQUERYOBJECTIVPROC glGetQueryObjectiv;
+ PFNGLBEGINQUERYPROC glBeginQuery;
+ PFNGLENDQUERYPROC glEndQuery;
#endif
// Shader
PFNGLCREATEPROGRAMPROC glCreateProgram;
@@ -173,26 +178,63 @@
struct Shader;
struct Texture;
+namespace Core {
+ struct {
+ bool VAO;
+ bool depthTexture;
+ bool shadowSampler;
+ bool discardFrame;
+ bool texNPOT;
+ bool texFloat, texFloatLinear;
+ bool texHalf, texHalfLinear;
+ bool shaderBinary;
+ #ifdef PROFILE
+ bool profMarker;
+ bool profTiming;
+ #endif
+ } support;
+}
+
#ifdef PROFILE
struct Marker {
Marker(const char *title) {
- if (glPushDebugGroup) glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 1, -1, title);
+ if (Core::support.profMarker) glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 1, -1, title);
}
~Marker() {
- if (glPopDebugGroup) glPopDebugGroup();
+ if (Core::support.profMarker) glPopDebugGroup();
}
static void setLabel(GLenum id, GLuint name, const char *label) {
- if (glObjectLabel) glObjectLabel(id, name, -1, label);
+ if (Core::support.profMarker) glObjectLabel(id, name, -1, label);
+ }
+ };
+
+ struct Timing {
+ GLuint ID;
+ int &result;
+
+ Timing(int &result) : result(result) {
+ if (!Core::support.profTiming) return;
+ glGenQueries(1, &ID);
+ glBeginQuery(GL_TIME_ELAPSED, ID);
+ }
+
+ ~Timing() {
+ if (!Core::support.profTiming) return;
+ glEndQuery(GL_TIME_ELAPSED);
+ glGetQueryObjectiv(ID, GL_QUERY_RESULT, (GLint*)&result);
+ glDeleteQueries(1, &ID);
}
};
#define PROFILE_MARKER(title) Marker marker(title)
#define PROFILE_LABEL(id, name, label) Marker::setLabel(GL_##id, name, label)
+ #define PROFILE_TIMING(result) Timing timing(result)
#else
#define PROFILE_MARKER(title)
#define PROFILE_LABEL(id, name, label)
+ #define PROFILE_TIMING(time)
#endif
enum CullMode { cfNone, cfBack, cfFront };
@@ -212,7 +254,7 @@ namespace Core {
Texture *blackTex, *whiteTex;
- enum Pass { passCompose, passShadow, passAmbient, passFilter, passWater, passMAX } pass;
+ enum Pass { passCompose, passShadow, passAmbient, passWater, passFilter, passGUI, passMAX } pass;
GLuint FBO;
struct RenderTargetCache {
@@ -236,6 +278,9 @@ namespace Core {
struct Stats {
int dips, tris, frame, fps, fpsTime;
+ #ifdef PROFILE
+ int tFrame;
+ #endif
Stats() : frame(0), fps(0), fpsTime(0) {}
@@ -246,6 +291,9 @@ namespace Core {
void stop() {
if (fpsTime < getTime()) {
LOG("FPS: %d DIP: %d TRI: %d\n", fps, dips, tris);
+ #ifdef PROFILE
+ LOG("frame time: %d mcs\n", tFrame / 1000);
+ #endif
fps = frame;
frame = 0;
fpsTime = getTime() + 1000;
@@ -254,17 +302,6 @@ namespace Core {
}
} stats;
- struct {
- bool VAO;
- bool depthTexture;
- bool shadowSampler;
- bool discardFrame;
- bool texNPOT;
- bool texFloat, texFloatLinear;
- bool texHalf, texHalfLinear;
- bool shaderBinary;
- } support;
-
struct {
bool ambient;
bool lighting;
@@ -298,6 +335,11 @@ namespace Core {
GetProcOGL(glObjectLabel);
GetProcOGL(glPushDebugGroup);
GetProcOGL(glPopDebugGroup);
+ GetProcOGL(glGenQueries);
+ GetProcOGL(glDeleteQueries);
+ GetProcOGL(glGetQueryObjectiv);
+ GetProcOGL(glBeginQuery);
+ GetProcOGL(glEndQuery);
#endif
GetProcOGL(glCreateProgram);
@@ -363,6 +405,10 @@ namespace Core {
support.texFloat = support.texFloatLinear || extSupport(ext, "_texture_float");
support.texHalfLinear = extSupport(ext, "GL_ARB_texture_float") || extSupport(ext, "_texture_half_float_linear");
support.texHalf = support.texHalfLinear || extSupport(ext, "_texture_half_float");
+ #ifdef PROFILE
+ support.profMarker = extSupport(ext, "_KHR_debug");
+ support.profTiming = extSupport(ext, "_timer_query");
+ #endif
char *vendor = (char*)glGetString(GL_VENDOR);
LOG("Vendor : %s\n", vendor);
diff --git a/src/game.h b/src/game.h
index c06d7bc..d3c1d9c 100644
--- a/src/game.h
+++ b/src/game.h
@@ -4,13 +4,17 @@
#include "core.h"
#include "format.h"
#include "level.h"
+#include "ui.h"
namespace Game {
Level *level;
+ UI *ui;
void startLevel(Stream *lvl, Stream *snd, bool demo, bool home) {
+ delete ui;
delete level;
level = new Level(*lvl, snd, demo, home);
+ ui = new UI(level);
delete lvl;
}
@@ -23,6 +27,7 @@ namespace Game {
Core::settings.water = Core::support.texFloat || Core::support.texHalf;
level = NULL;
+ ui = NULL;
startLevel(lvl, snd, false, false);
}
@@ -35,17 +40,13 @@ namespace Game {
}
void free() {
+ delete ui;
delete level;
Core::free();
}
void updateTick() {
float dt = Core::deltaTime;
- if (Input::down[ikV]) { // third <-> first person view
- level->camera->changeView(!level->camera->firstPerson);
- Input::down[ikV] = false;
- }
-
if (Input::down[ikR]) // slow motion (for animation debugging)
Core::deltaTime /= 10.0f;
@@ -59,7 +60,14 @@ namespace Game {
}
void update(float delta) {
- delta = min(1.0f, delta);
+ if (Input::down[ikV]) { // third <-> first person view
+ level->camera->changeView(!level->camera->firstPerson);
+ Input::down[ikV] = false;
+ }
+
+ Core::deltaTime = delta = min(1.0f, delta);
+ ui->update();
+
while (delta > EPS) {
Core::deltaTime = min(delta, 1.0f / 30.0f);
Game::updateTick();
@@ -68,8 +76,10 @@ namespace Game {
}
void render() {
+ PROFILE_TIMING(Core::stats.tFrame);
Core::beginFrame();
level->render();
+ ui->renderTouch();
Core::endFrame();
}
}
diff --git a/src/gui.glsl b/src/gui.glsl
index 1bdcd79..022f03e 100644
--- a/src/gui.glsl
+++ b/src/gui.glsl
@@ -1,6 +1,6 @@
R"====(
#ifdef GL_ES
- precision highp int;
+ precision lowp int;
precision highp float;
#endif
@@ -8,7 +8,7 @@ varying vec2 vTexCoord;
#ifdef VERTEX
uniform mat4 uViewProj;
- uniform mat4 uModel;
+ uniform vec4 uPosScale;
attribute vec4 aCoord;
attribute vec4 aTexCoord;
@@ -16,17 +16,15 @@ varying vec2 vTexCoord;
#define TEXCOORD_SCALE (1.0 / 32767.0)
void main() {
- vec4 coord = uModel * vec4(aCoord.xyz, 1.0);
vTexCoord = aTexCoord.xy * TEXCOORD_SCALE;
- coord.xy += aTexCoord.zw;
- gl_Position = uViewProj * coord;
+ gl_Position = uViewProj * vec4(aCoord.xy * uPosScale.zw + uPosScale.xy, 0.0, 1.0);
}
#else
uniform sampler2D sDiffuse;
- uniform vec4 uColor;
+ uniform vec4 uMaterial;
void main() {
- gl_FragColor = texture2D(sDiffuse, vTexCoord) * uColor;
+ gl_FragColor = /* texture2D(sDiffuse, vTexCoord) * */ uMaterial;
}
#endif
)===="
\ No newline at end of file
diff --git a/src/input.h b/src/input.h
index ef08894..cf559fb 100644
--- a/src/input.h
+++ b/src/input.h
@@ -12,7 +12,7 @@ enum InputKey { ikNone,
// mouse
ikMouseL, ikMouseR, ikMouseM,
// touch
- ikTouchA, ikTouchB,
+ ikTouchA, ikTouchB, ikTouchC, ikTouchD, ikTouchE, ikTouchF,
// gamepad
ikJoyA, ikJoyB, ikJoyX, ikJoyY, ikJoyLB, ikJoyRB, ikJoySelect, ikJoyStart, ikJoyL, ikJoyR, ikJoyLT, ikJoyRT, ikJoyPOV,
ikMAX };
@@ -34,13 +34,11 @@ namespace Input {
int POV;
} joy;
- struct {
- vec2 A, B;
-
- struct {
- vec2 A, B;
- } start;
- } touch;
+ struct Touch {
+ int id;
+ vec2 start;
+ vec2 pos;
+ } touch[6];
void reset() {
memset(down, 0, sizeof(down));
@@ -58,8 +56,12 @@ namespace Input {
case ikMouseL : mouse.start.L = mouse.pos; break;
case ikMouseR : mouse.start.R = mouse.pos; break;
case ikMouseM : mouse.start.M = mouse.pos; break;
- case ikTouchA : touch.start.A = touch.A; break;
- case ikTouchB : touch.start.B = touch.B; break;
+ case ikTouchA :
+ case ikTouchB :
+ case ikTouchC :
+ case ikTouchD :
+ case ikTouchE :
+ case ikTouchF : touch[key - ikTouchA].start = touch[key - ikTouchA].pos; break;
default : ;
}
down[key] = value;
@@ -72,14 +74,32 @@ namespace Input {
case ikMouseM : mouse.pos = pos; return;
case ikJoyL : joy.L = pos; return;
case ikJoyR : joy.R = pos; return;
- case ikTouchA : touch.A = pos; return;
- case ikTouchB : touch.B = pos; return;
case ikJoyLT : joy.LT = pos.x; break;
case ikJoyRT : joy.RT = pos.x; break;
case ikJoyPOV : joy.POV = (int)pos.x; break;
+ case ikTouchA :
+ case ikTouchB :
+ case ikTouchC :
+ case ikTouchD :
+ case ikTouchE :
+ case ikTouchF : touch[key - ikTouchA].pos = pos; return;
default : return;
}
- setDown(key, pos.x > 0.0f);
+ setDown(key, pos.x > 0.0f); // gamepad LT, RT, POV auto-down state
+ }
+
+ InputKey getTouch(int id) {
+ for (int i = 0; i < COUNT(touch); i++)
+ if (down[ikTouchA + i] && touch[i].id == id)
+ return InputKey(ikTouchA + i);
+
+ for (int i = 0; i < COUNT(touch); i++)
+ if (!down[ikTouchA + i]) {
+ touch[i].id = id;
+ return InputKey(ikTouchA + i);
+ }
+
+ return ikNone;
}
}
diff --git a/src/lara.h b/src/lara.h
index baca5a5..db27979 100644
--- a/src/lara.h
+++ b/src/lara.h
@@ -1656,13 +1656,13 @@ struct Lara : Character {
if (Input::joy.L.x != 0.0f) {
input |= (Input::joy.L.x < 0.0f) ? LEFT : RIGHT;
if (moving || stand == STAND_UNDERWATER || stand == STAND_ONWATER)
- rotFactor.y = min(fabsf(Input::joy.L.x) / 0.75f, 1.0f);
+ rotFactor.y = min(fabsf(Input::joy.L.x) / 0.9f, 1.0f);
}
if (Input::joy.L.y != 0.0f) {
input |= (Input::joy.L.y < 0.0f) ? FORTH : BACK;
if (stand == STAND_UNDERWATER)
- rotFactor.x = min(fabsf(Input::joy.L.y) / 0.75f, 1.0f);
+ rotFactor.x = min(fabsf(Input::joy.L.y) / 0.9f, 1.0f);
}
return input;
diff --git a/src/level.h b/src/level.h
index 23641a1..9e59a79 100644
--- a/src/level.h
+++ b/src/level.h
@@ -70,7 +70,7 @@ struct Level : IGame {
}
virtual void setShader(Core::Pass pass, Shader::Type type, bool underwater = false, bool alphaTest = false) {
- shaderCache->bind(pass, type, (underwater ? ShaderCache::FX_UNDERWATER : 0) | (alphaTest ? ShaderCache::FX_ALPHA_TEST : 0) | (params->clipHeight != NO_CLIP_PLANE ? 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 renderEnvironment(int roomIndex, const vec3 &pos, Texture **targets, int stride = 0) {
@@ -824,7 +824,7 @@ struct Level : IGame {
glEnd();
Core::setDepthTest(true);
- /*
+
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
@@ -834,9 +834,9 @@ struct Level : IGame {
glLoadIdentity();
glOrtho(0, Core::width, 0, Core::height, 0, 1);
-// if (waterCache->count)
-// waterCache->refract->bind(sDiffuse);
-// else
+ if (waterCache->count)
+ waterCache->refract->bind(sDiffuse);
+ else
shadow->bind(sDiffuse);
glEnable(GL_TEXTURE_2D);
glDisable(GL_CULL_FACE);
diff --git a/src/platform/android/AndroidManifest.xml b/src/platform/android/AndroidManifest.xml
index 66bbc92..31bbc63 100644
--- a/src/platform/android/AndroidManifest.xml
+++ b/src/platform/android/AndroidManifest.xml
@@ -7,16 +7,16 @@
-
-
+
+ android:label="@string/app_name"
+ android:debuggable="true">
1) return;
// gamepad
if (state < 0) {
switch (state) {
@@ -118,32 +117,12 @@ JNI_METHOD(void, nativeTouch)(JNIEnv* env, jobject obj, jint id, jint state, jfl
}
return;
}
-
- if (state == 3 && x < Core::width / 2) {
- vec2 center(Core::width * 0.25f, Core::height * 0.6f);
- vec2 pos(x, y);
- vec2 d = pos - center;
- Input::setPos(ikJoyL, d.normal());
- };
-
- if (state == 2 && x > Core::width / 2) {
- int i = y / (Core::height / 3);
- InputKey key;
- if (i == 0)
- key = ikJoyX;
- else if (i == 1)
- key = ikJoyA;
- else
- key = ikJoyY;
- Input::setDown(key, true);
- }
-
- if (state == 1) {
- Input::setPos(ikJoyL, vec2(0.0f));
- Input::setDown(ikJoyA, false);
- Input::setDown(ikJoyX, false);
- Input::setDown(ikJoyY, false);
- }
+// touch
+ InputKey key = Input::getTouch(id);
+ if (key == ikNone) return;
+ Input::setPos(key, vec2(x, y));
+ if (state == 1 || state == 2)
+ Input::setDown(key, state == 2);
}
JNI_METHOD(void, nativeSoundFill)(JNIEnv* env, jobject obj, jshortArray buffer) {
diff --git a/src/platform/web/main.cpp b/src/platform/web/main.cpp
index 3521eea..15cfd80 100644
--- a/src/platform/web/main.cpp
+++ b/src/platform/web/main.cpp
@@ -199,44 +199,16 @@ EM_BOOL keyCallback(int eventType, const EmscriptenKeyboardEvent *e, void *userD
}
EM_BOOL touchCallback(int eventType, const EmscriptenTouchEvent *e, void *userData) {
- bool flag = false;
- /*
- for (int i = 0; i < e->numTouches; i++) {
- long x = e->touches[i].canvasX;
- long y = e->touches[i].canvasY;
- if (x < 0 || y < 0 || x >= Core::width || y >= Core::height) continue;
- flag = true;
+ for (int i = 0; i < e->numTouches; i++) {
+ InputKey key = Input::getTouch(e->touches[i].identifier);
+ if (key == ikNone) continue;
+ Input::setPos(key, vec2(e->touches[i].canvasX, e->touches[i].canvasY));
- switch (eventType) {
- case EMSCRIPTEN_EVENT_TOUCHSTART :
- if (x > Core::width / 2)
- Input::joy.down[(y > Core::height / 2) ? 1 : 4] = true;
- break;
- case EMSCRIPTEN_EVENT_TOUCHMOVE :
- if (x < Core::width / 2) {
- vec2 center(Core::width * 0.25f, Core::height * 0.6f);
- vec2 pos(x, y);
- vec2 d = pos - center;
-
- Input::Joystick &joy = Input::joy;
-
- joy.L.x = d.x;
- joy.L.y = d.y;
- if (joy.L != vec2(0.0f))
- joy.L.normalize();
- }
- break;
- case EMSCRIPTEN_EVENT_TOUCHEND :
- case EMSCRIPTEN_EVENT_TOUCHCANCEL : {
- Input::joy.L = vec2(0.0f);
- Input::joy.down[1] = false;
- Input::joy.down[4] = false;
- break;
- }
- }
+ if (eventType == EMSCRIPTEN_EVENT_TOUCHSTART || eventType == EMSCRIPTEN_EVENT_TOUCHEND || eventType == EMSCRIPTEN_EVENT_TOUCHCANCEL)
+ Input::setDown(key, eventType == EMSCRIPTEN_EVENT_TOUCHSTART);
}
- */
- return flag;
+
+ return 1;
}
EM_BOOL mouseCallback(int eventType, const EmscriptenMouseEvent *e, void *userData) {
@@ -267,10 +239,10 @@ int main() {
emscripten_set_keyup_callback(0, 0, 1, keyCallback);
emscripten_set_resize_callback(0, 0, 1, resizeCallback);
- emscripten_set_touchstart_callback(0, 0, 1, touchCallback);
- emscripten_set_touchend_callback(0, 0, 1, touchCallback);
- emscripten_set_touchmove_callback(0, 0, 1, touchCallback);
- emscripten_set_touchcancel_callback(0, 0, 1, touchCallback);
+ emscripten_set_touchstart_callback(0, 0, 0, touchCallback);
+ emscripten_set_touchend_callback(0, 0, 0, touchCallback);
+ emscripten_set_touchmove_callback(0, 0, 0, touchCallback);
+ emscripten_set_touchcancel_callback(0, 0, 0, touchCallback);
emscripten_set_mousedown_callback(0, 0, 1, mouseCallback);
emscripten_set_mouseup_callback(0, 0, 1, mouseCallback);
diff --git a/src/platform/win/OpenLara.vcxproj b/src/platform/win/OpenLara.vcxproj
index b0d81b7..aeac447 100644
--- a/src/platform/win/OpenLara.vcxproj
+++ b/src/platform/win/OpenLara.vcxproj
@@ -198,6 +198,7 @@
+
diff --git a/src/platform/win/main.cpp b/src/platform/win/main.cpp
index ca283b3..43db6ee 100644
--- a/src/platform/win/main.cpp
+++ b/src/platform/win/main.cpp
@@ -128,7 +128,7 @@ PREGISTERTOUCHWINDOW RegisterTouchWindowX;
PGETTOUCHINPUTINFO GetTouchInputInfoX;
PCLOSETOUCHINPUTHANDLE CloseTouchInputHandleX;
-#define MAX_TOUCH_COUNT 10
+#define MAX_TOUCH_COUNT 6
void touchInit(HWND hWnd) {
int value = GetSystemMetrics(SM_DIGITIZER);
@@ -142,17 +142,23 @@ void touchInit(HWND hWnd) {
}
}
-void touchUpdate(HTOUCHINPUT hTouch, int count) {
- TOUCHINPUT touches[MAX_TOUCH_COUNT];
+void touchUpdate(HWND hWnd, HTOUCHINPUT hTouch, int count) {
+ TOUCHINPUT touch[MAX_TOUCH_COUNT];
count = min(count, MAX_TOUCH_COUNT);
- if (!GetTouchInputInfoX(hTouch, count, touches, sizeof(TOUCHINPUT)))
+ if (!GetTouchInputInfoX(hTouch, count, touch, sizeof(TOUCHINPUT)))
return;
- LOG("touch [ ", count);
- for (int i = 0; i < count; i++)
- LOG("%d (%d, %d) ", i, touches[i].x, touches[i].y);
- LOG("]\n");
+ for (int i = 0; i < count; i++) {
+ InputKey key = Input::getTouch(touch[i].dwID);
+ if (key == ikNone) continue;
+ POINT pos = { touch[i].x / 100, touch[i].y / 100 };
+ ScreenToClient(hWnd, &pos);
+ Input::setPos(key, vec2(float(pos.x), float(pos.y)));
+
+ if (touch[i].dwFlags & (TOUCHEVENTF_DOWN | TOUCHEVENTF_UP))
+ Input::setDown(key, (touch[i].dwFlags & TOUCHEVENTF_DOWN) != 0);
+ }
CloseTouchInputHandleX(hTouch);
}
@@ -283,10 +289,8 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lPara
return 1;
// touch
case WM_TOUCH :
- touchUpdate((HTOUCHINPUT)lParam, wParam);
+ touchUpdate(hWnd, (HTOUCHINPUT)lParam, wParam);
break;
- // TODO
- // sound
default :
return DefWindowProc(hWnd, msg, wParam, lParam);
}
diff --git a/src/utils.h b/src/utils.h
index 18d4fb9..abc4359 100644
--- a/src/utils.h
+++ b/src/utils.h
@@ -47,6 +47,8 @@ typedef unsigned int uint32;
#define FOURCC(str) (*((uint32*)str))
+#define COUNT(arr) (sizeof(arr) / sizeof(arr[0]))
+
template
inline const T& min(const T &a, const T &b) {
return a < b ? a : b;
@@ -231,7 +233,7 @@ struct vec3 {
struct vec4 {
union {
- struct { vec2 xy; };
+ struct { vec2 xy, zw; };
struct { vec3 xyz; };
struct { float x, y, z, w; };
};
@@ -240,6 +242,7 @@ struct vec4 {
vec4(float s) : x(s), y(s), z(s), w(s) {}
vec4(float x, float y, float z, float w) : x(x), y(y), z(z), w(w) {}
vec4(const vec3 &xyz, float w) : x(xyz.x), y(xyz.y), z(xyz.z), w(w) {}
+ vec4(const vec2 &xy, const vec2 &zw) : x(xy.x), y(xy.y), z(zw.x), w(zw.y) {}
vec4 operator * (const vec4 &v) const { return vec4(x*v.x, y*v.y, z*v.z, w*v.w); }
vec4& operator *= (const vec4 &v) { x*=v.x; y*=v.y; z*=v.z; w*=v.w; return *this; }