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 {