From 12537924c6571817f4db2a9a34186fac2d5b1791 Mon Sep 17 00:00:00 2001 From: XProger Date: Fri, 24 Mar 2017 02:17:06 +0300 Subject: [PATCH] #23 main shader optimizations; touch UI --- src/cache.h | 30 +- src/camera.h | 2 +- src/game.h | 4 +- src/lara.h | 9 +- src/level.h | 2 +- src/mesh.h | 43 +- src/platform/android/AndroidManifest.xml | 7 +- src/platform/android/project.properties | 2 +- .../org/xproger/openlara/MainActivity.java | 21 +- src/shader.glsl | 506 ++++++++++-------- src/ui.h | 179 +++++++ src/utils.h | 2 + 12 files changed, 547 insertions(+), 260 deletions(-) create mode 100644 src/ui.h diff --git a/src/cache.h b/src/cache.h index a88f801..8224ca1 100644 --- a/src/cache.h +++ b/src/cache.h @@ -45,12 +45,14 @@ struct ShaderCache { if (Core::settings.ambient) { compile(Core::passAmbient, Shader::ROOM, FX_NONE); compile(Core::passAmbient, Shader::ROOM, FX_ALPHA_TEST); - compile(Core::passAmbient, Shader::ROOM, FX_CLIP_PLANE); - compile(Core::passAmbient, Shader::ROOM, FX_ALPHA_TEST | FX_CLIP_PLANE); compile(Core::passAmbient, Shader::ROOM, FX_UNDERWATER); - compile(Core::passAmbient, Shader::ROOM, FX_UNDERWATER | FX_CLIP_PLANE); compile(Core::passAmbient, Shader::SPRITE, FX_ALPHA_TEST); - compile(Core::passAmbient, Shader::SPRITE, FX_ALPHA_TEST | FX_CLIP_PLANE); + if (Core::settings.water) { + compile(Core::passAmbient, Shader::ROOM, FX_CLIP_PLANE); + compile(Core::passAmbient, Shader::ROOM, FX_ALPHA_TEST | FX_CLIP_PLANE); + compile(Core::passAmbient, Shader::ROOM, FX_UNDERWATER | FX_CLIP_PLANE); + compile(Core::passAmbient, Shader::SPRITE, FX_ALPHA_TEST | FX_CLIP_PLANE); + } } if (Core::settings.water) { @@ -65,21 +67,23 @@ struct ShaderCache { compile(Core::passCompose, Shader::ROOM, FX_NONE); compile(Core::passCompose, Shader::ROOM, FX_ALPHA_TEST); - compile(Core::passCompose, Shader::ROOM, FX_ALPHA_TEST | FX_CLIP_PLANE); - compile(Core::passCompose, Shader::ROOM, FX_CLIP_PLANE); compile(Core::passCompose, Shader::ROOM, FX_UNDERWATER); - compile(Core::passCompose, Shader::ROOM, FX_UNDERWATER | FX_CLIP_PLANE); compile(Core::passCompose, Shader::ENTITY, FX_NONE); - 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); - compile(Core::passCompose, Shader::SPRITE, FX_UNDERWATER | FX_ALPHA_TEST | FX_CLIP_PLANE); compile(Core::passCompose, Shader::FLASH, FX_ALPHA_TEST); + if (Core::settings.water) { + compile(Core::passCompose, Shader::ROOM, FX_CLIP_PLANE); + compile(Core::passCompose, Shader::ROOM, FX_ALPHA_TEST | FX_CLIP_PLANE); + compile(Core::passCompose, Shader::ROOM, FX_UNDERWATER | FX_CLIP_PLANE); + compile(Core::passCompose, Shader::SPRITE, FX_ALPHA_TEST | FX_CLIP_PLANE); + compile(Core::passCompose, Shader::ENTITY, FX_ALPHA_TEST | FX_CLIP_PLANE); + compile(Core::passCompose, Shader::ENTITY, FX_CLIP_PLANE); + compile(Core::passCompose, Shader::ENTITY, FX_UNDERWATER | FX_CLIP_PLANE); + compile(Core::passCompose, Shader::SPRITE, FX_UNDERWATER | FX_ALPHA_TEST | FX_CLIP_PLANE); + } compile(Core::passCompose, Shader::MIRROR, FX_NONE); LOG("shader: cache is ready\n"); @@ -276,7 +280,7 @@ struct AmbientCache { TR::Color32 color; glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &color); colors[j] = vec3(color.r / 255.0f, color.g / 255.0f, color.b / 255.0f); - colors[j] *= colors[j]; // to "linear" space + // colors[j] *= colors[j]; // to "linear" space } Core::setDepthTest(true); diff --git a/src/camera.h b/src/camera.h index f9261b1..11d33f0 100644 --- a/src/camera.h +++ b/src/camera.h @@ -291,7 +291,7 @@ struct Camera : Controller { advTimer = 0.0f; fov = firstPerson ? 90.0f : 65.0f; - znear = firstPerson ? 8.0f : 16.0f; + znear = firstPerson ? 8.0f : 128.0f; zfar = 40.0f * 1024.0f; } }; diff --git a/src/game.h b/src/game.h index d3c1d9c..a159479 100644 --- a/src/game.h +++ b/src/game.h @@ -23,8 +23,8 @@ namespace Game { Core::settings.ambient = true; Core::settings.lighting = true; - Core::settings.shadows = true; - Core::settings.water = Core::support.texFloat || Core::support.texHalf; + Core::settings.shadows = false;//true; + Core::settings.water = false;//Core::support.texFloat || Core::support.texHalf; level = NULL; ui = NULL; diff --git a/src/lara.h b/src/lara.h index db27979..eb6326b 100644 --- a/src/lara.h +++ b/src/lara.h @@ -43,7 +43,7 @@ #define DESCENT_SPEED 2048.0f #define MUZZLE_FLASH_TIME 0.1f -#define FLASH_LIGHT_COLOR vec4(0.8f, 0.7f, 0.3f, 3072 * 3072) +#define FLASH_LIGHT_COLOR vec4(0.6f, 0.5f, 0.1f, 1.0f / 3072.0f) #define TARGET_MAX_DIST (8.0f * 1024.0f) struct Lara : Character { @@ -417,6 +417,7 @@ struct Lara : Character { //reset(43, vec3(31400, -2560, 25200), PI); // level 2 (reach) //reset(16, vec3(60907, 0, 39642), PI * 3 / 2); // level 2 (hang & climb) //reset(19, vec3(60843, 1024, 30557), PI); // level 2 (block) + reset(1, vec3(62630, -1280, 19633), 0); // level 2 (dark medikit) //reset(7, vec3(64108, -512, 16514), -PI * 0.5f); // level 2 (bat trigger) //reset(15, vec3(70082, -512, 26935), PI * 0.5f); // level 2 (bear) //reset(63, vec3(31390, -2048, 33472), 0.0f); // level 2 (trap floor) @@ -783,8 +784,8 @@ struct Lara : Character { arms[i].animation.update(); arms[i].shotTimer += Core::deltaTime; - float intensity = clamp((0.1f - arms[i].shotTimer) * 20.0f, 0.0f, 1.0f); - Core::lightColor[1 + i] = FLASH_LIGHT_COLOR * vec4(intensity, intensity, intensity, sqrtf(intensity)); + float intensity = clamp((0.1f - arms[i].shotTimer) * 20.0f, EPS, 1.0f); + Core::lightColor[1 + i] = FLASH_LIGHT_COLOR * vec4(intensity, intensity, intensity, 1.0f / sqrtf(intensity)); } if (isRifle) @@ -1644,7 +1645,7 @@ struct Lara : Character { 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; - bool moving = state == STATE_RUN || state == STATE_WALK || state == STATE_BACK || state == STATE_FAST_BACK || state == STATE_SURF_SWIM || state == STATE_SURF_BACK; + bool moving = state == STATE_RUN || state == STATE_WALK || state == STATE_BACK || state == STATE_FAST_BACK || state == STATE_SURF_SWIM || state == STATE_SURF_BACK || state == STATE_FORWARD_JUMP; if (!moving) { if (fabsf(Input::joy.L.x) < fabsf(Input::joy.L.y)) diff --git a/src/level.h b/src/level.h index 9e59a79..3d52f6c 100644 --- a/src/level.h +++ b/src/level.h @@ -485,7 +485,7 @@ struct Level : IGame { TR::Room::Light &light = level.rooms[room].lights[idx]; float c = 1.0f - intensityf(level.rooms[room].lights[idx].intensity); Core::lightPos[0] = vec3(float(light.x), float(light.y), float(light.z)); - Core::lightColor[0] = vec4(c, c, c, (float)light.radius * (float)light.radius); + Core::lightColor[0] = vec4(c, c, c, 1.0f / (float)light.radius); } else { Core::lightPos[0] = vec3(0); Core::lightColor[0] = vec4(0, 0, 0, 1); diff --git a/src/mesh.h b/src/mesh.h index 18cfedf..5547b1e 100644 --- a/src/mesh.h +++ b/src/mesh.h @@ -9,7 +9,7 @@ typedef unsigned short Index; struct Vertex { short4 coord; // xyz - position, w - unused short4 texCoord; // xy - texture coordinates, z - anim tex range index, w - anim tex frame index - short4 normal; // xyz - vertex normal, w - disable lighting (0, 1) + short4 normal; // xyz - vertex normalá w - unused ubyte4 color; // xyz - color, w - intensity }; @@ -25,7 +25,7 @@ struct MeshRange { Vertex *v = (Vertex*)(vStart * sizeof(Vertex)); glVertexAttribPointer(aCoord, 4, GL_SHORT, false, sizeof(Vertex), &v->coord); glVertexAttribPointer(aTexCoord, 4, GL_SHORT, false, sizeof(Vertex), &v->texCoord); - glVertexAttribPointer(aNormal, 4, GL_SHORT, false, sizeof(Vertex), &v->normal); + glVertexAttribPointer(aNormal, 4, GL_SHORT, true, sizeof(Vertex), &v->normal); glVertexAttribPointer(aColor, 4, GL_UNSIGNED_BYTE, true, sizeof(Vertex), &v->color); } @@ -40,6 +40,7 @@ struct MeshRange { }; #define PLANE_DETAIL 48 +#define CIRCLE_SEGS 16 struct Mesh { GLuint ID[2]; @@ -123,7 +124,7 @@ float intensityf(int lighting) { if (lighting < 0) return 1.0f; float lum = 1.0f - (lighting >> 5) / 255.0f; //return powf(lum, 2.2f); // gamma to linear space - return lum * lum; // gamma to "linear" space + return lum;// * lum; // gamma to "linear" space } uint8 intensity(int lighting) { @@ -146,7 +147,7 @@ struct MeshBuilder { // procedured MeshRange shadowBlob; MeshRange bar; - MeshRange quad; + MeshRange quad, circle; MeshRange plane; vec2 *animTexRanges; @@ -251,6 +252,13 @@ struct MeshBuilder { iCount += quad.iCount; vCount += 4; + // circle + circle.vStart = vCount; + circle.iStart = iCount; + circle.iCount = CIRCLE_SEGS * 3; + iCount += circle.iCount; + vCount += CIRCLE_SEGS + 1; + // detailed plane plane.vStart = vCount; plane.iStart = iCount; @@ -395,6 +403,28 @@ struct MeshBuilder { vCount += 4; aCount++; + // circle + vec2 pos(32767.0f, 0.0f); + vec2 cs(cos(PI2 / CIRCLE_SEGS), sin(PI2 / CIRCLE_SEGS)); + + for (int i = 0; i < CIRCLE_SEGS; i++) { + Vertex &v = vertices[vCount + i]; + pos.rotate(cs); + v.coord = { short(pos.x), short(pos.y), 0, 0 }; + v.normal = { 0, 0, 0, 0 }; + v.color = { 255, 255, 255, 255 }; + v.texCoord = { 32688, 32688, 0, 0 }; + + indices[iCount++] = i; + indices[iCount++] = (i + 1) % CIRCLE_SEGS; + indices[iCount++] = CIRCLE_SEGS; + } + vertices[vCount + CIRCLE_SEGS] = vertices[vCount]; + vertices[vCount + CIRCLE_SEGS].coord = { 0, 0, 0, 0 }; + + vCount += CIRCLE_SEGS + 1; + aCount++; + // plane for (int16 j = -PLANE_DETAIL; j <= PLANE_DETAIL; j++) for (int16 i = -PLANE_DETAIL; i <= PLANE_DETAIL; i++) { @@ -439,6 +469,7 @@ struct MeshBuilder { mesh->initRange(shadowBlob); mesh->initRange(bar); mesh->initRange(quad); + mesh->initRange(circle); mesh->initRange(plane); } @@ -837,6 +868,10 @@ struct MeshBuilder { mesh->render(quad); } + void renderCircle() { + mesh->render(circle); + } + void renderPlane() { mesh->render(plane); } diff --git a/src/platform/android/AndroidManifest.xml b/src/platform/android/AndroidManifest.xml index 31bbc63..f59d6f1 100644 --- a/src/platform/android/AndroidManifest.xml +++ b/src/platform/android/AndroidManifest.xml @@ -3,20 +3,19 @@ android:versionCode="1" android:versionName="0.1" > - + + + android:label="@string/app_name"> 0.1 && rShadow < 3.9) { + float angle = randomAngle(vTexCoord.xyy, 15.0); + vec2 sc = vec2(sin(angle), cos(angle)); - vec3 lv = uLightPos[0].xyz - vCoord.xyz; - float fade = clamp(dot(lv, lv) / uLightColor[0].w, 0.0, 1.0); + rShadow += SHADOW(SHADOW_TEXEL * rotate(sc, vec2(-0.54316, 0.21186)) + p); + rShadow += SHADOW(SHADOW_TEXEL * rotate(sc, vec2(-0.03925, -0.34345)) + p); + rShadow += SHADOW(SHADOW_TEXEL * rotate(sc, vec2( 0.07695, 0.40667)) + p); + rShadow += SHADOW(SHADOW_TEXEL * rotate(sc, vec2(-0.66378, -0.54068)) + p); + rShadow += SHADOW(SHADOW_TEXEL * rotate(sc, vec2(-0.54130, 0.66730)) + p); + rShadow += SHADOW(SHADOW_TEXEL * rotate(sc, vec2( 0.69301, 0.46990)) + p); + rShadow += SHADOW(SHADOW_TEXEL * rotate(sc, vec2( 0.37228, 0.03811)) + p); + rShadow += SHADOW(SHADOW_TEXEL * rotate(sc, vec2( 0.28597, 0.80228)) + p); + rShadow += SHADOW(SHADOW_TEXEL * rotate(sc, vec2( 0.44801, -0.43844)) + p); + rShadow /= 13.0; + } else + rShadow /= 4.0; + + vec3 lv = vLightVec; + float fade = clamp(dot(lv, lv), 0.0, 1.0); return mix(rShadow, 1.0, fade); } float getShadow() { - return min(dot(vNormal.xyz, uLightPos[0].xyz - vCoord), vLightProj.w) > 0.0 ? getShadow(vLightProj) : 1.0; + return min(dot(vNormal.xyz, vLightVec), vLightProj.w) > 0.0 ? getShadow(vLightProj) : 1.0; } #endif - vec3 calcLight(vec3 normal, vec3 pos, vec4 color) { - vec3 lv = pos - vCoord.xyz; - float lum = max(0.0, dot(normal, normalize(lv))); - float att = max(0.0, 1.0 - dot(lv, lv) / color.w); - return color.xyz * (lum * att); + float calcSpecular(vec3 normal, vec3 viewVec, vec3 lightVec, vec4 color, float intensity) { + vec3 vv = normalize(viewVec); + vec3 rv = reflect(-vv, normal); + vec3 lv = normalize(lightVec); + return pow(max(0.0, dot(rv, lv)), 8.0) * intensity; } - vec3 calcSpecular(vec3 normal, vec3 viewVec, vec3 pos, vec4 color, float intensity) { - vec3 rv = reflect(-viewVec, normal); - vec3 lv = normalize(pos - vCoord.xyz); - float spec = pow(max(0.0, dot(rv, lv)), 8.0) * intensity; - return vec3(spec); - } - - #ifdef OPT_AMBIENT - vec3 calcAmbient(vec3 n) { - vec3 sqr = n * n; - vec3 pos = step(0.0, n); - return sqr.x * mix(uAmbient[1], uAmbient[0], pos.x) + - sqr.y * mix(uAmbient[3], uAmbient[2], pos.y) + - sqr.z * mix(uAmbient[5], uAmbient[4], pos.z); - } - #endif - - #if defined(UNDERWATER) && defined(OPT_WATER) + #if defined(OPT_WATER) && defined(UNDERWATER) float calcCaustics(vec3 n) { - vec2 fade = smoothstep(uRoomSize.xy, uRoomSize.xy + vec2(256.0), vCoord.xz) * (1.0 - smoothstep(uRoomSize.zw - vec2(256.0), uRoomSize.zw, vCoord.xz)); - return texture2D(sReflect, vTexCoord.zw).g * (max(0.0, -n.y)) * fade.x * fade.y; + vec2 border = vec2(256.0) / (uRoomSize.zw - uRoomSize.xy); + vec2 fade = smoothstep(vec2(0.0), border, vTexCoord.zw) * (1.0 - smoothstep(vec2(1.0) - border, vec2(1.0), vTexCoord.zw)); + return texture2D(sReflect, vTexCoord.zw).g * max(0.0, -n.y) * fade.x * fade.y; } #endif - - vec3 applyFog(vec3 color, vec3 fogColor, float factor) { - float fog = clamp(1.0 / exp(factor), 0.0, 1.0); - return mix(fogColor, color, fog); - } #endif void main() { - #ifndef PASS_SHADOW - #ifndef PASS_AMBIENT - #ifdef CLIP_PLANE - if (vCoord.y * uParam.z > uParam.w) - discard; - #endif + #ifdef PASS_COMPOSE + #ifdef CLIP_PLANE + if (vViewVec.w * uParam.z > uParam.w) + discard; #endif #endif - vec4 color = texture2D(sDiffuse, vTexCoord.xy); + vec4 color; + #ifdef TYPE_MIRROR + #ifdef PASS_COMPOSE + vec3 rv = reflect(-normalize(vViewVec.xyz), normalize(vNormal.xyz)); + color = textureCube(sEnvironment, normalize(rv)); + #endif + #else + color = texture2D(sDiffuse, vTexCoord.xy); + #endif #ifdef ALPHA_TEST - if (color.w <= 0.1) + if (color.w <= 0.5) discard; - //color.xyz *= vec3(1.0, 0.0, 0.0); #endif - + + #ifndef PASS_SHADOW + #ifdef TYPE_FLASH + color *= uMaterial.xxxw; + #else + color *= vDiffuse; + #endif + #endif + #ifdef PASS_SHADOW + #ifdef SHADOW_COLOR gl_FragColor = pack(gl_FragCoord.z); #else gl_FragColor = vec4(1.0); #endif + #else - color.xyz *= vColor.xyz; - color.xyz *= color.xyz; // to "linear" space - #ifdef PASS_AMBIENT - color.xyz *= vColor.w; - #else - // calc point lights - #ifndef TYPE_FLASH - vec3 normal = normalize(vNormal.xyz); - vec3 viewVec = normalize(vViewVec); - vec3 light = vec3(0.0); + #ifndef TYPE_FLASH + #ifdef OPT_SHADOW + #ifdef PASS_COMPOSE + vec3 light = uLightColor[1].xyz * vLight.y + uLightColor[2].xyz * vLight.z; - #ifdef OPT_LIGHTING - light += calcLight(normal, uLightPos[1], uLightColor[1]); - light += calcLight(normal, uLightPos[2], uLightColor[2]); - #endif - - // apply lighting - #ifdef TYPE_SPRITE - light += vColor.w * uMaterial.y; // apply diffuse intensity - #endif - - #ifdef TYPE_ROOM - #ifdef OPT_SHADOW - light += mix(min(uMaterial.y, vColor.w), vColor.w, getShadow()); - #else - light += vColor.w; + #ifdef TYPE_ENTITY + light += vAmbient + uLightColor[0].xyz * (vLight.x * getShadow()); #endif - #if defined(UNDERWATER) && defined(OPT_WATER) - light += calcCaustics(normal); + + #ifdef TYPE_ROOM + vec3 n = normalize(vNormal.xyz); + light += mix(vAmbient.x, vLight.x, getShadow()); + #if defined(OPT_WATER) && defined(UNDERWATER) + light += calcCaustics(n); + #endif + #endif + + #ifdef TYPE_SPRITE + light += vLight.x; + #endif + + #ifndef TYPE_MIRROR + color.xyz *= light; #endif #endif - #ifdef TYPE_ENTITY - vec3 mainLight = calcLight(normal, uLightPos[0], uLightColor[0]); - float mainSpec = uMaterial.z; - - #ifdef OPT_SHADOW - float rShadow = getShadow(); - mainLight *= rShadow; - mainSpec *= rShadow; - #endif - light += mainLight; - - #ifdef OPT_AMBIENT - light += calcAmbient(normal); - #else - light += uMaterial.y; - #endif - - #ifdef UNDERWATER - if (vCoord.y > uParam.y) { - color.xyz *= UNDERWATER_COLOR; - #ifdef OPT_WATER - light += calcCaustics(normal); - #endif - } else - color.xyz += calcSpecular(normal, viewVec, uLightPos[0], uLightColor[0], mainSpec + 0.03); - #else - color.xyz += calcSpecular(normal, viewVec, uLightPos[0], uLightColor[0], mainSpec + 0.03); - #endif + #ifdef PASS_AMBIENT + color.xyz *= vLight.x; #endif - color.xyz *= light; - - #else // ifndef TYPE_FLASH - - color.w *= uMaterial.w; - + #else + #ifndef TYPE_MIRROR + color.xyz *= vLight.xyz; + #endif #endif + + #if defined(PASS_COMPOSE) && !defined(TYPE_FLASH) + #ifdef UNDERWATER + color.xyz = mix(color.xyz, UNDERWATER_COLOR * 0.2, vNormal.w); + #else + color.xyz = mix(color.xyz, vec3(0.0), vNormal.w); + #endif + #endif + #endif - color.xyz = sqrt(color.xyz); // back to "gamma" space - - #ifdef PASS_COMPOSE - #ifdef TYPE_MIRROR - vec3 rv = reflect(-normalize(vViewVec), normalize(vNormal.xyz)); - color.xyz = vColor.xyz * textureCube(sEnvironment, normalize(rv)).xyz; - #endif - - color.xyz = applyFog(color.xyz, vec3(0.0), length(vViewVec) * FOG_DIST); - #if defined(UNDERWATER) && defined(OPT_WATER) - float d = abs((vCoord.y - max(uViewPos.y, uParam.y)) / normalize(vViewVec).y); - d *= step(0.0, vCoord.y - uParam.y); - color.xyz = applyFog(color.xyz, UNDERWATER_COLOR * 0.2, d * WATER_FOG_DIST); - #endif - #endif - - color.xyz *= 1.5; // add contrast - gl_FragColor = color; #endif } diff --git a/src/ui.h b/src/ui.h new file mode 100644 index 0000000..e11d82d --- /dev/null +++ b/src/ui.h @@ -0,0 +1,179 @@ +#ifndef H_UI +#define H_UI + +#include "core.h" +#include "controller.h" + +struct UI { + enum TouchButton { bWeapon, bWalk, bAction, bJump, bMAX }; + enum TouchZone { zMove, zLook, zButton, zMAX }; + + 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) { + touch[zMove] = touch[zLook] = touch[zButton] = ikNone; + } + + bool checkTouchZone(TouchZone zone) { + InputKey &t = touch[zone]; + if (t != ikNone && !Input::down[t]) { + t = ikNone; + return true; + } + return false; + } + + void getTouchDir(InputKey touch, vec2 &dir) { + vec2 delta = vec2(0.0f); + if (touch == ikNone) + return; + + Input::Touch &t = Input::touch[touch - 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 getTouchButton(const vec2 &pos) { + btn = bMAX; + float minDist = 1000.0f; + for (int i = 0; i < bMAX; i++) { + float d = (pos - btnPos[i]).length(); + if (d < minDist) { + minDist = d; + btn = TouchButton(i); + } + } + } + + void touchSetDown(bool down) { + switch (btn) { + case bWeapon : Input::setDown(ikJoyY, down); break; + case bWalk : Input::setDown(ikJoyLB, down); break; + case bAction : Input::setDown(ikJoyA, down); break; + case bJump : Input::setDown(ikJoyX, down); break; + default : ; + } + } + + void update() { + if (touch[zMove] != ikNone || touch[zLook] != ikNone || touch[zButton] != ikNone) { + touchTimerVis = 30.0f; + + if (touchTimerTap > 0.0f) + touchTimerTap = max(0.0f, touchTimerTap - Core::deltaTime); + + } 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); + + 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.4f) { // 100 ms gap to reduce false positives (bad touch sensors) + doubleTap = true; + touchTimerTap = 0.0f; + } else + touchTimerTap = 0.5f; + } + } + } + + // set active touches as gamepad controls + getTouchDir(touch[zMove], Input::joy.L); + getTouchDir(touch[zLook], Input::joy.R); + if (touch[zButton] != ikNone) { + getTouchButton(Input::touch[touch[zButton] - ikTouchA].pos); + touchSetDown(true); + } + if (doubleTap) + Input::setDown(ikJoyB, true); + } + + void renderControl(const vec2 &pos, const vec2 &size, const vec4 &color) { + Core::active.shader->setParam(uPosScale, vec4(pos, size * vec2(1.0f / 32767.0f))); + Core::active.shader->setParam(uMaterial, color); + game->getMesh()->renderCircle(); + } + + void renderTouch() { + if (touchTimerVis <= 0.0f) return; + + Core::setDepthTest(false); + Core::setBlending(bmAlpha); + Core::setCulling(cfNone); + + Core::mViewProj = mat4(0.0f, float(Core::width), float(Core::height), 0.0f, 0.0f, 1.0f); + + game->setShader(Core::passGUI, Shader::NONE); + + float offset = Core::height * 0.25f; + + vec2 pos = vec2(offset, Core::height - offset); + if (Input::down[touch[zMove]]) { + Input::Touch &t = Input::touch[touch[zMove] - ikTouchA]; + renderControl(t.pos, vec2(btnRadius), vec4(0.5f)); + pos = t.start; + } + renderControl(pos, vec2(btnRadius), vec4(0.5f)); + + for (int i = 0; i < bMAX; i++) + renderControl(btnPos[i], vec2(btnRadius), vec4(0.5f)); + + Core::setCulling(cfFront); + Core::setBlending(bmNone); + Core::setDepthTest(true); + } + + void renderBar(const vec2 &pos, const vec2 &size, float value) { + // + } +}; + +#endif \ No newline at end of file diff --git a/src/utils.h b/src/utils.h index abc4359..ca60e4d 100644 --- a/src/utils.h +++ b/src/utils.h @@ -167,6 +167,8 @@ struct vec2 { float length() const { return sqrtf(length2()); } vec2 normal() const { float s = length(); return s == 0.0 ? (*this) : (*this)*(1.0f/s); } float angle() const { return atan2f(y, x); } + vec2& rotate(const vec2 &cs) { *this = vec2(x*cs.x - y*cs.y, x*cs.y + y*cs.x); return *this; } + vec2& rotate(float angle) { return rotate(vec2(cosf(angle), sinf(angle))); } }; struct vec3 {