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