Created
April 18, 2021 21:06
-
-
Save zwade/7771ef895f4f18703b060e207bf1648c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifdef GL_ES | |
precision highp float; | |
precision highp int; | |
#endif | |
uniform float time; | |
uniform float dt; | |
uniform vec2 resolution; | |
uniform ivec4 loopback[250]; | |
varying vec3 surface_loc; | |
varying vec3 color; | |
varying vec3 focus; | |
varying vec3 viewport_center; | |
@define PATH_LEN 100 | |
@define PATH 0.0 | |
@define MOUSE_LOCATION 100.0 | |
@define PLAYER_LOCATION 102.0 | |
@define VIEW_ANGLE 104.0 | |
@define ACTIVE_PUZZLE 120.0 | |
@define ACTIVE_LEVEL 121.0 | |
@define SOLUTION1 130.0 | |
@define SOLUTION2 136.0 | |
@define SOLUTION3 142.0 | |
@define COMPLETE 150.0 | |
@define KEY_LEFT 200.0 | |
@define KEY_RIGHT 201.0 | |
@define KEY_UP 202.0 | |
@define KEY_DOWN 203.0 | |
@define CLICK 204.0 | |
@define RIGHT_CLICK 205.0 | |
@define MOUSE_DELTA 210.0 | |
#define imin(x,y) (x < y ? x : y) | |
#define minmax(x, minval, maxval) (min(max(x, minval), maxval)) | |
#define read_float(addr, scale) fromWTFloat(loopback[int(addr)].xyz) * scale | |
#define read_byte(addr) loopback[int(addr)].x | |
#define read_ivec2(addr) loopback[int(addr)].xy | |
#define read_ivec3(addr) loopback[int(addr)].xyz | |
#define read_bool(addr) (loopback[int(addr)].x == 0 ? false : true) | |
#define read_vec2(addr, scale) vec2(read_float(addr, scale), read_float(int(addr) + 1, scale)) | |
#define at_addr(addr, exp) if (gl_FragCoord.y <= 1.0 && gl_FragCoord.x > addr && gl_FragCoord.x <= addr + 1.0) { gl_FragColor = exp; return; } | |
#define write_float(addr, value, scale) at_addr(addr, vec4(toWTFloat((value) / (scale)), 0.0)) | |
#define write_byte(addr, value) at_addr(addr, vec4(float(value) / 255.0, 0, 0, 0)) | |
#define write_ivec2(addr, value) at_addr(addr, vec4(float(value.xy) / 255.0, 0, 0)) | |
#define write_ivec3(addr, value) at_addr(addr, vec4(float(value.x) / 255.0, float(value.y) / 255.0, float(value.z) / 255.0, 0)) | |
#define write_bool(addr, value) at_addr(addr, vec4(value ? 1.0 : 0.0, 0, 0, 0)) | |
#define PI 3.1415 | |
float fromWTFloat(ivec3 inp) { | |
return ( | |
float(inp.x) * 256.0 * 256.0 + | |
float(inp.y) * 256.0 + | |
float(inp.z) | |
) / (256.0 * 256.0 * 256.0) * 2.0; | |
} | |
vec3 toWTFloat(float x) { | |
float bigboi = (x / 2.0) * 256.0 * 256.0 * 256.0; | |
return vec3( | |
floor(bigboi / (256.0 * 256.0)) / 256.0, | |
floor(mod(bigboi, 256.0 * 256.0) / 256.0) / 256.0, | |
floor(mod(bigboi, 256.0)) / 256.0 | |
); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@include "./common.glsl" | |
attribute vec3 position; | |
attribute vec3 myInput; | |
void main() { | |
vec2 aspectRatio = vec2(1.0, resolution.x / resolution.y); | |
vec4 base_position = vec4(position.xy / aspectRatio, 2, 1); | |
gl_Position = vec4(position.xyz, 1); | |
vec2 rotation = read_vec2(VIEW_ANGLE, 1.0) * vec2(2.0 * PI, PI) + vec2(0, 3.0 * PI / 2.0); | |
vec2 footLocation = read_vec2(PLAYER_LOCATION, 1.0); | |
mat4 ud_rotation_mat = mat4( | |
vec4(1, 0, 0, 0), | |
vec4(0, cos(rotation.y), -sin(rotation.y), 0), | |
vec4(0, sin(rotation.y), cos(rotation.y), 0), | |
vec4(0, 0, 0, 1) | |
); | |
mat4 lr_rotation_mat = mat4( | |
vec4(cos(rotation.x), 0, -sin(rotation.x), 0), | |
vec4(0, 1, 0, 0), | |
vec4(sin(rotation.x), 0, cos(rotation.x), 0), | |
vec4(0, 0, 0, 1) | |
); | |
mat4 translation_mat = mat4( | |
vec4(1, 0, 0, (footLocation.x - 0.5) * 18.0), | |
vec4(0, 1, 0, 0), | |
vec4(0, 0, 1, (footLocation.y - 0.5) * 18.0), | |
vec4(0, 0, 0, 1) | |
); | |
vec4 full_focus = ((vec4(0, 0, 0, 1) * ud_rotation_mat) * lr_rotation_mat) * translation_mat; | |
focus = full_focus.xyz / full_focus.w; | |
vec4 full_center = ((vec4(0, 0, 1, 1) * ud_rotation_mat) * lr_rotation_mat) * translation_mat; | |
viewport_center = full_center.xyz / full_center.w; | |
vec4 result = ((base_position * ud_rotation_mat) * lr_rotation_mat) * translation_mat; | |
surface_loc = result.xyz / result.w; | |
color = vec3(surface_loc.z); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@include "./common.glsl" | |
uniform vec2 mouse; | |
uniform vec2 footLocation; | |
uniform sampler2D introImage; | |
uniform sampler2D treeImage; | |
uniform sampler2D shrubImage; | |
const float radius = 0.03; | |
const float scale = 0.50; | |
const vec4 paleYellow = vec4(1.0, 1.0, 0.8, 1.0); | |
const vec4 defaultEmpty = vec4(1.0, 0.0, 0.2, 1.0); | |
const vec4 orange = vec4(1.0, 0.6, 0.0, 1.0); | |
const vec4 cyan = vec4(0, 0.67, 0.76, 1.0); | |
const vec4 foliage = vec4(0.22, 0.56, 0.24, 1.0); | |
const vec4 foliageShadow = vec4(0.11, 0.37, 0.13, 1.0); | |
const vec4 gray600 = vec4(0.38, 0.49, 0.55, 1.0); | |
const vec4 gray900 = vec4(0.15, 0.20, 0.22, 1.0); | |
const vec4 cursor = vec4(1, 1, 1, 0.8); | |
const vec4 shadow = vec4(0, 0, 0, 0.8); | |
const vec4 darkGreen = vec4(0.11, 0.37, 0.13, 1.0); | |
const vec4 darkOrange900 = vec4(0.76, 0.21, 0.05, 1.0); | |
const vec4 orange900 = vec4(0.90, 0.32, 0.0, 1.0); | |
const vec4 lightOrange900 = vec4(1.0, 0.44, 0.0, 1.0); | |
const vec4 yellow900 = vec4(0.96, 0.5, 0.09, 1.0); | |
const vec4 blue800 = vec4(0.16, 0.21, 0.58, 1.0); | |
const vec4 brown600 = vec4(0.43, 0.30, 0.25, 1.0); // 6D4C41 | |
#define MAX_SIZE 12 | |
struct Plane { | |
int id; | |
vec3 center; | |
vec3 xAxis; | |
vec3 yAxis; | |
int shape; | |
}; | |
struct GameState { | |
bool puzzleActive; | |
bool puzzleSolved; | |
}; | |
int iabs(int x) { | |
return x >= 0 ? x : -x; | |
} | |
float dist(vec2 a, vec2 b) { | |
vec2 diff = a - b; | |
return sqrt(dot(diff, diff)); | |
} | |
float dist(vec3 a, vec3 b) { | |
vec3 diff = a - b; | |
return sqrt(dot(diff, diff)); | |
} | |
float round(float a) { | |
return floor(a + 0.5); | |
} | |
vec4 blend(vec4 base, vec4 overlay) { | |
return vec4(base.rgb * (1.0 - overlay.a) + overlay.rgb * overlay.a, 1.0); | |
} | |
float modExp (float b, float e, float m) { | |
float base = mod(b, m); | |
float result = 1.0; | |
for (int i = 0; i < 8; i++) { | |
if (mod(e, 2.0) == 1.0) { | |
result = mod(result * base, m); | |
} | |
base = mod(base * base, m); | |
e = floor(e / 2.0); | |
} | |
return result; | |
} | |
int hash(float value, float maxVal) { | |
float modRes = modExp(1021.0, value + 12.0, 4093.0); | |
return int(floor(modRes * maxVal / 4093.0)); | |
} | |
bool isNode(vec2 position, int size, out ivec2 where) { | |
for (int i = 0; i < MAX_SIZE; i++) { | |
for (int j = 0; j < MAX_SIZE; j++) { | |
if (i >= size || j >= size) continue; | |
vec2 loc = vec2( | |
(-float(size - 1) + 2.0 * float(i)) / float(size - 1), | |
(-float(size - 1) + 2.0 * float(j)) / float(size - 1)); | |
float dist = sqrt(pow(position.x - loc.x, 2.0) + pow(position.y - loc.y, 2.0)); | |
if (dist < radius) { | |
where.x = i; | |
where.y = j; | |
return true; | |
} | |
} | |
} | |
return false; | |
} | |
bool isVEdge(vec2 position, int size, out ivec2 where) { | |
for (int i = 0; i < MAX_SIZE; i++) { | |
for (int j = 0; j < MAX_SIZE - 1; j++) { | |
if (i >= size || j >= size - 1) continue; | |
vec2 top = vec2( | |
(-float(size - 1) + 2.0 * float(i)) / float(size - 1), | |
(-float(size - 1) + 2.0 * float(j)) / float(size - 1)); | |
vec2 bottom = vec2( | |
(-float(size - 1) + 2.0 * float(i)) / float(size - 1), | |
(-float(size - 1) + 2.0 * float(j + 1)) / float(size - 1)); | |
if ( | |
abs(position.x - top.x) < radius && | |
position.y > top.y && | |
position.y < bottom.y | |
) { | |
where.x = i; | |
where.y = j; | |
return true; | |
} | |
} | |
} | |
return false; | |
} | |
bool isHEdge(vec2 position, int size, out ivec2 where) { | |
for (int i = 0; i < MAX_SIZE - 1; i++) { | |
for (int j = 0; j < MAX_SIZE; j++) { | |
if (i >= size - 1 || j >= size) continue; | |
vec2 left = vec2( | |
(-float(size - 1) + 2.0 * float(i)) / float(size - 1), | |
(-float(size - 1) + 2.0 * float(j)) / float(size - 1)); | |
vec2 right = vec2( | |
(-float(size - 1) + 2.0 * float(i + 1)) / float(size - 1), | |
(-float(size - 1) + 2.0 * float(j)) / float(size - 1)); | |
if ( | |
abs(position.y - left.y) < radius && | |
position.x > left.x && | |
position.x < right.x | |
) { | |
where.x = i; | |
where.y = j; | |
return true; | |
} | |
} | |
} | |
return false; | |
} | |
bool nodeIsOnPath(ivec2 where, int size) { | |
for (int i = 0; i < PATH_LEN; i ++) { | |
ivec3 loc = read_ivec3(int(PATH) + i); | |
if (loc == ivec3(255, 255, 1)) { | |
return false; | |
} | |
if (loc.xy == where) { | |
return true; | |
} | |
} | |
return false; | |
} | |
bool vEdgeIsOnPath(ivec2 where, int size) { | |
for (int i = 0; i < PATH_LEN - 1; i++) { | |
ivec3 loc = read_ivec3(int(PATH) + i); | |
ivec3 next = read_ivec3(int(PATH) + i + 1); | |
if (next == ivec3(255, 255, 1)) { | |
return false; | |
} | |
if (loc.x == next.x && loc.x == where.x && imin(loc.y, next.y) == where.y) { | |
return true; | |
} | |
} | |
return false; | |
} | |
bool hEdgeIsOnPath(ivec2 where, int size) { | |
for (int i = 0; i < PATH_LEN - 1; i ++) { | |
ivec3 loc = read_ivec3(int(PATH) + i); | |
ivec3 next = read_ivec3(int(PATH) + i + 1); | |
if (next == ivec3(255, 255, 1)) { | |
return false; | |
} | |
if (loc.y == next.y && loc.y == where.y && imin(loc.x, next.x) == where.x) { | |
return true; | |
} | |
} | |
return false; | |
} | |
bool isValidNextSegment(ivec2 where, int size) { | |
if (where.x < 0 || where.y < 0 || where.x >= size || where.y >= size) { | |
return false; | |
} | |
if (where == ivec2(0, 0) && read_ivec3(PATH) == ivec3(255, 255, 1)) { | |
return true; | |
} | |
for (int i = 0; i < PATH_LEN - 1; i++) { | |
ivec3 current = read_ivec3(int(PATH) + i); | |
ivec3 next = read_ivec3(int(PATH) + i + 1); | |
if (next == ivec3(255, 255, 1)) { | |
return iabs(current.x - where.x) + iabs(current.y - where.y) == 1; | |
} | |
if (current.xy == where) { | |
return false; | |
} | |
} | |
} | |
float rayIntersectsPlane(vec3 start, vec3 end, Plane plane, out vec2 planarPosition, out vec3 worldPosition) { | |
vec3 normal = cross(plane.xAxis, plane.yAxis); | |
vec3 ray = end - start; | |
vec3 normalizedView = ray / sqrt(dot(ray, ray)); | |
float projection = dot(normalizedView, normal); | |
float t = (-dot(start - plane.center, normal)) / projection; | |
vec3 pointOnPlane = start + normalizedView * t; | |
vec3 distanceFromCenter = pointOnPlane - plane.center; | |
float xComponent = dot(distanceFromCenter, plane.xAxis) / dot(plane.xAxis, plane.xAxis); | |
float yComponent = dot(distanceFromCenter, plane.yAxis) / dot(plane.yAxis, plane.yAxis); | |
if (t > 0.0) { | |
if ( | |
(plane.shape == 1 && xComponent >= -1.0 && xComponent <= 1.0 && yComponent >= -1.0 && yComponent <= 1.0) | |
|| (plane.shape == 2 && dist(vec2(xComponent, yComponent), vec2(0)) < 1.0) | |
) { | |
planarPosition = vec2(xComponent, yComponent); | |
worldPosition = pointOnPlane; | |
return t; | |
} | |
} | |
return -1.0; | |
} | |
bool rayIntersectsCylinder(vec3 trueStart, vec3 trueEnd, vec3 center, float radius, float height, out float theta, out float y, out float t) { | |
float minT = dist(trueEnd, trueStart); | |
vec3 trueRay = (trueEnd - trueStart) / dist(trueEnd, trueStart); | |
vec2 start = trueStart.xz - center.xz; | |
vec2 end = trueEnd.xz - center.xz; | |
vec2 ray = (end - start) / dist(end, start); | |
float a = dot(ray, ray); | |
float b = 2.0 * dot(start, ray); | |
float c = dot(start, start) - radius * radius; | |
if (4.0 * a * c > b * b) { | |
return false; | |
} | |
float groundTs[2]; | |
groundTs[0] = (-b + sqrt(b * b - 4.0 * a * c)) / (2.0 * a); | |
groundTs[1] = (-b - sqrt(b * b - 4.0 * a * c)) / (2.0 * a); | |
float ts[2]; | |
ts[0] = groundTs[0] / sqrt(1.0 - trueRay.y * trueRay.y); | |
ts[1] = groundTs[1] / sqrt(1.0 - trueRay.y * trueRay.y); | |
float bestTheta; | |
float bestY; | |
float bestT = -1.0; | |
for (int i = 0; i < 2; i++) { | |
float currentT = ts[i]; | |
vec3 position = trueStart + trueRay * currentT; | |
float currentTheta = (atan(position.z - center.z, position.x - center.x) + PI) / (2.0 * PI); | |
float currentY = (position.y - center.y + height) / (2.0 * height); | |
if (currentT >= 0.0 && (bestT < 0.0 || currentT < bestT) && currentY >= 0.0 && currentY <= 1.0) { | |
bestTheta = currentTheta; | |
bestY = currentY; | |
bestT = currentT; | |
} | |
} | |
if (bestT > 0.0) { | |
y = bestY; | |
theta = bestTheta; | |
t = bestT; | |
return true; | |
} | |
return false; | |
} | |
float findIntersection(vec3 start, vec3 end, Plane planes[6], out vec2 planarPosition, out vec3 worldPosition, out int id) { | |
float bestDist = -1.0; | |
id = -1; | |
for (int i = 0; i < 6; i++) { | |
vec2 pp; | |
vec3 wp; | |
float dist = rayIntersectsPlane(start, end, planes[i], pp, wp); | |
if (dist > 0.0 && (bestDist < 0.0 || dist < bestDist)) { | |
bestDist = dist; | |
id = planes[i].id; | |
planarPosition = pp; | |
worldPosition = wp; | |
} | |
} | |
return bestDist; | |
} | |
int decodePath(int index, int len) { | |
float offset = 0.0; | |
float result = 0.0; | |
for (int i = 0; i < PATH_LEN - 1; i++) { | |
ivec3 current = read_ivec3(int(PATH) + i); | |
ivec3 next = read_ivec3(int(PATH) + i + 1); | |
if (next == ivec3(255, 255, 1)) { | |
return int(result); | |
} | |
if (mod(float(i), float(len)) == float(index)) { | |
float val; | |
ivec3 diff = next - current; | |
if (diff == ivec3(1, 0, 0)) { | |
val = 0.0; | |
} else if (diff == ivec3(-1, 0, 0)) { | |
val = 1.0; | |
} else if (diff == ivec3(0, 1, 0)) { | |
val = 2.0; | |
} else if (diff == ivec3(0, -1, 0)) { | |
val = 3.0; | |
} | |
result = mod(result + val * pow(2.0, offset), 16.0); | |
offset = mod(offset + 1.0, 2.0); | |
} | |
} | |
} | |
bool checkLevel3(int size) { | |
ivec2 leftTurns[14]; | |
leftTurns[0] = ivec2(2, 6); | |
leftTurns[1] = ivec2(2, 4); | |
leftTurns[2] = ivec2(1, 3); | |
leftTurns[3] = ivec2(1, 1); | |
leftTurns[4] = ivec2(4, 1); | |
leftTurns[5] = ivec2(6, 8); | |
leftTurns[6] = ivec2(6, 6); | |
leftTurns[7] = ivec2(5, 5); | |
leftTurns[8] = ivec2(5, 3); | |
leftTurns[9] = ivec2(8, 3); | |
leftTurns[10] = ivec2(9, 10); | |
leftTurns[11] = ivec2(9, 4); | |
leftTurns[12] = ivec2(10, 4); | |
leftTurns[13] = ivec2(11, 7); | |
ivec2 rightTurns[14]; | |
rightTurns[0] = ivec2(0, 7); | |
rightTurns[1] = ivec2(2, 7); | |
rightTurns[2] = ivec2(3, 6); | |
rightTurns[3] = ivec2(3, 4); | |
rightTurns[4] = ivec2(2, 3); | |
rightTurns[5] = ivec2(4, 9); | |
rightTurns[6] = ivec2(6, 9); | |
rightTurns[7] = ivec2(7, 8); | |
rightTurns[8] = ivec2(7, 6); | |
rightTurns[9] = ivec2(6, 5); | |
rightTurns[10] = ivec2(8, 11); | |
rightTurns[11] = ivec2(10, 11); | |
rightTurns[12] = ivec2(10, 10); | |
rightTurns[13] = ivec2(10, 7); | |
int leftIdx = 0; | |
int rightIdx = 0; | |
for (int i = 1; i < PATH_LEN - 1; i++) { | |
ivec3 previous = read_ivec3(int(PATH) + i - 1); | |
ivec3 current = read_ivec3(int(PATH) + i); | |
ivec3 next = read_ivec3(int(PATH) + i + 1); | |
if (next == ivec3(255, 255, 1)) { | |
return false; | |
} | |
if (next == ivec3(size - 1, size - 1, 1) && leftIdx == 14 && rightIdx == 14) { | |
return true; | |
} | |
vec3 forward = vec3(next - current); | |
vec3 back = vec3(previous - current); | |
vec3 dir = cross(forward, back); | |
if (dir.z != 0.0) { | |
for (int index = 0; index < 14; index++) { | |
if (dir.z < -0.5) { | |
if (rightIdx >= 14) return false; | |
if (index == rightIdx) { | |
if (rightTurns[index] != current.xy) return false; | |
rightIdx ++; | |
break; | |
} | |
} | |
if (dir.z > 0.5) { | |
if (leftIdx >= 14) return false; | |
if (index == leftIdx) { | |
if (leftTurns[index] != current.xy) return false; | |
leftIdx ++; | |
break; | |
} | |
} | |
} | |
} | |
} | |
return false; | |
} | |
bool checkLevel2(int size) { | |
for (int i = 0; i < PATH_LEN - 1; i++) { | |
ivec3 current = read_ivec3(int(PATH) + i); | |
if (current == ivec3(255, 255, 1)) { | |
return false; | |
} | |
if (current == ivec3(size - 1, size - 1, 1)) { | |
return true; | |
} | |
int value = hash(float(current.y * size + current.x), 2.0); | |
if (value != 1) { | |
return false; | |
} | |
} | |
return true; | |
} | |
bool checkLevel1(int size) { | |
for (int i = 0; i < PATH_LEN - 1; i++) { | |
ivec3 current = read_ivec3(int(PATH) + i); | |
ivec3 next = read_ivec3(int(PATH) + i + 1); | |
if (current == ivec3(255, 255, 1)) { | |
return false; | |
} | |
if (current == ivec3(size - 1, size - 1, 1)) { | |
return true; | |
} | |
float pointX = (float(current.x) + float(next.x)) / (2.0 * float(size)); | |
float pointY = (float(current.y) + float(next.y)) / (2.0 * float(size)); | |
vec4 data = texture2D(introImage, vec2(pointX, pointY)); | |
if (data.a >= 1.0) { | |
return false; | |
} | |
} | |
return false; | |
} | |
vec4 drawPuzzle(vec2 position, vec2 newMouse, int size, bool isSolved) { | |
position *= 1.1; | |
float distToMouse = sqrt(pow(position.x - newMouse.x, 2.0) + pow(position.y - newMouse.y, 2.0)); | |
if (distToMouse < radius) { | |
return orange; | |
} | |
ivec2 where; | |
if (isNode(position, size, where)) { | |
if (nodeIsOnPath(where, size)) { | |
return orange; | |
} | |
return paleYellow; | |
} | |
if (isVEdge(position, size, where)) { | |
if (vEdgeIsOnPath(where, size)) { | |
return orange; | |
} | |
if (isHEdge(position, size, where) && hEdgeIsOnPath(where, size)) { | |
return orange; | |
} | |
return paleYellow; | |
} | |
if (isHEdge(position, size, where)) { | |
if (hEdgeIsOnPath(where, size)) { | |
return orange; | |
} | |
return paleYellow; | |
} | |
if (isSolved) { | |
return foliage; | |
} | |
return defaultEmpty; | |
} | |
vec2 normalizeMouse(int size) { | |
if (read_byte(ACTIVE_PUZZLE) == 0) { | |
return vec2(-1, -1); | |
} | |
vec2 mouseLocation = read_vec2(MOUSE_LOCATION, 1.0); | |
vec2 mouseDelta = read_vec2(MOUSE_DELTA, 1.0) - 0.5; | |
vec2 mouse = (mouseLocation + mouseDelta - 0.5) * 2.0; | |
vec2 bounded = minmax(mouse, vec2(-1, -1), vec2(1, 1)); | |
vec2 normalized = (bounded + vec2(1, 1)) / 2.0; | |
vec2 haligned = vec2( | |
round(normalized.x * float(size - 1)) / float(size - 1), | |
normalized.y | |
); | |
vec2 valigned = vec2( | |
normalized.x, | |
round(normalized.y * float(size - 1)) / float(size - 1) | |
); | |
if (dist(haligned, normalized) < dist(valigned, normalized)) { | |
return haligned * 2.0 - vec2(1, 1); | |
} else { | |
return valigned * 2.0 - vec2(1, 1); | |
} | |
} | |
vec2 normalizeLocation() { | |
vec2 base = read_vec2(PLAYER_LOCATION, 1.0); | |
if (read_byte(ACTIVE_PUZZLE) == 0) { | |
vec3 viewVector = viewport_center - focus; | |
vec3 unnormalizedForwardProjection = vec3(viewVector.x, viewVector.z, 0); | |
vec3 forwardProjection = | |
unnormalizedForwardProjection / sqrt( | |
dot(unnormalizedForwardProjection, unnormalizedForwardProjection) | |
); | |
vec3 sideProjection = cross(forwardProjection, vec3(0, 0, 1)); | |
float speed = 0.0005; | |
if (read_bool(KEY_UP)) { | |
base += speed * forwardProjection.xy * dt; | |
} | |
if (read_bool(KEY_DOWN)) { | |
base -= speed * forwardProjection.xy * dt; | |
} | |
if (read_bool(KEY_LEFT)) { | |
base -= speed * sideProjection.xy * dt; | |
} | |
if (read_bool(KEY_RIGHT)) { | |
base += speed * sideProjection.xy * dt; | |
} | |
} | |
return minmax(base, vec2(0, 0), vec2(1, 1)); | |
} | |
vec2 normalizeRotation() { | |
vec2 rotation = read_vec2(VIEW_ANGLE, 1.0) * vec2(2.0 * PI, PI); | |
if (read_byte(ACTIVE_PUZZLE) == 0) { | |
vec2 mouseDelta = read_vec2(MOUSE_DELTA, 1.0) - 0.5; | |
rotation = rotation - mouseDelta; | |
} | |
rotation.x = mod(rotation.x + 2.0 * PI, 2.0 * PI); | |
rotation.y = minmax(rotation.y, 0.0, PI); | |
return rotation; | |
} | |
vec4 drawFoliage(vec2 worldPosition) { | |
vec2 actual = (worldPosition * 1000.0); | |
vec2 quantized = vec2(floor(actual.x), floor(actual.y)); | |
int color = hash(quantized.y + quantized.x * 8.0, 4.0); | |
float blendVal = dist(quantized + 0.5, actual); | |
vec4 base; | |
if (color == 0) { | |
base = darkOrange900; | |
} else if (color == 1) { | |
base = orange900; | |
} else if (color == 2) { | |
base = lightOrange900; | |
} else { | |
base = yellow900; | |
} | |
return base; | |
} | |
vec3 reflect(vec3 ray, Plane plane) { | |
vec3 normalized = normalize(ray); | |
vec3 xNormalized = normalize(plane.xAxis); | |
vec3 yNormalized = normalize(plane.yAxis); | |
vec3 nNormalized = normalize(cross(plane.xAxis, plane.yAxis)); | |
return | |
dot(normalized, xNormalized) * xNormalized + | |
dot(normalized, yNormalized) * yNormalized - | |
dot(normalized, nNormalized) * nNormalized; | |
} | |
GameState renderLevel3(vec2 newMouse) { | |
bool puzzleActive = read_bool(ACTIVE_PUZZLE); | |
bool isSolved = checkLevel3(12); | |
vec2 planarPosition; | |
vec3 worldPosition; | |
float angle; | |
float height; | |
float t; | |
Plane puzzle = Plane(0, vec3(0, -2.01, 30), vec3(-6, 0, 0), vec3(0, 0, 6), 1); | |
Plane dock = Plane(1, vec3(0, -2, -10), vec3(10, 0, 0), vec3(0, 0, 20), 1); | |
Plane dock2 = Plane(2, vec3(0, -2, 40), vec3(10, 0, 0), vec3(0, 0, 20), 1); | |
Plane lake = Plane(3, vec3(0, -10.0, 3), vec3(0, 0, 30), vec3(30, 0, 0), 2); | |
Plane shore = Plane(4, vec3(0, -10.1, 3), vec3(0, 0, 100), vec3(100, 0, 0), 2); | |
Plane wall = Plane(5, vec3(0, 0, -10), vec3(0, 15, 0), vec3(15, 0, 0), 1); | |
float timeOfDay = time / 5.0; | |
Plane lightSource = Plane( | |
4, | |
vec3(0, 300.0 * sin(timeOfDay), 300.0 * cos(timeOfDay)), | |
vec3(0, 42.0 * sin(timeOfDay + PI / 2.0), 42.0 * cos(timeOfDay + PI / 2.0)), | |
vec3(42, 0, 0), | |
1 | |
); | |
Plane renderables[6]; | |
renderables[0] = puzzle; | |
renderables[1] = dock; | |
renderables[2] = dock2; | |
renderables[3] = lake; | |
renderables[4] = shore; | |
renderables[5] = wall; | |
Plane puzzles[6]; | |
puzzles[0] = puzzle; | |
int found = -1; | |
float blendVal = (cos(timeOfDay - PI / 2.0) + 1.0) / 2.0; | |
gl_FragColor = cyan; | |
float foundDistance = findIntersection( | |
focus, | |
surface_loc, | |
renderables, | |
planarPosition, | |
worldPosition, | |
found | |
); | |
if (foundDistance >= 0.0) { | |
if (found == 0) { | |
gl_FragColor = drawPuzzle(planarPosition, newMouse, 12, isSolved); | |
} else if (found == 1 || found == 2) { | |
gl_FragColor = brown600; | |
} else if (found == 3) { | |
vec3 wp = worldPosition; | |
vec3 reflection = reflect(wp - focus, lake); | |
vec4 foundColor = vec4(0, 0, 0, 0); | |
if (rayIntersectsCylinder( | |
wp, | |
wp + reflection, | |
vec3(0, -9, 3), | |
31.0, | |
1.0, | |
angle, | |
height, | |
t | |
)) { | |
foundColor = texture2D(shrubImage, vec2(mod(angle, 1.0/80.0) * 80.0, 1.0 - height)); | |
} | |
Plane reflectables[6]; | |
reflectables[0] = puzzle; | |
reflectables[2] = dock2; | |
if (findIntersection(wp, wp + reflection, reflectables, planarPosition, worldPosition, found) >= 0.0) { | |
if (found == 0) { | |
foundColor = blend(drawPuzzle(planarPosition, newMouse, 12, isSolved), foundColor); | |
} | |
if (found == 2) { | |
foundColor = blend(brown600, foundColor); | |
} | |
} | |
gl_FragColor = blend(blue800, vec4(foundColor.rgb, foundColor.a / 2.0)); | |
} else if (found == 4) { | |
gl_FragColor = paleYellow; | |
} else if (found == 5) { | |
gl_FragColor = gray600; | |
} | |
} | |
if (rayIntersectsCylinder( | |
focus, | |
surface_loc, | |
vec3(0, -9, 3), | |
31.0, | |
1.0, | |
angle, | |
height, | |
t | |
)) { | |
if (t < foundDistance) { | |
gl_FragColor = blend(gl_FragColor, texture2D(shrubImage, vec2(mod(angle, 1.0/80.0) * 80.0, 1.0 - height))); | |
} | |
} | |
if (!puzzleActive && dist(gl_FragCoord.xy, resolution / 2.0) < 4.0) { | |
gl_FragColor = blend(gl_FragColor, cursor); | |
} | |
if (read_bool(CLICK) && rayIntersectsPlane(focus, viewport_center, puzzle, planarPosition, worldPosition) >= 0.0) { | |
puzzleActive = true; | |
} | |
return GameState(puzzleActive, isSolved); | |
} | |
GameState renderLevel2(vec2 newMouse) { | |
bool puzzleActive = read_bool(ACTIVE_PUZZLE); | |
bool isSolved = checkLevel2(8); | |
vec2 planarPosition; | |
vec3 worldPosition; | |
float angle; | |
float height; | |
float t; | |
Plane puzzle = Plane(0, vec3(0, 0.5, 7), vec3(2, 0, 0), vec3(0, 2, 0), 1); | |
Plane tree = Plane(1, vec3(-2, 3, 12), vec3(3.75, 0, 0), vec3(0, 5, 0), 1); | |
Plane ground = Plane(3, vec3(0, -2.0, 0), vec3(0, 0, 100), vec3(100, 0, 0), 1); | |
float timeOfDay = time / 5.0; | |
Plane lightSource = Plane( | |
2, | |
vec3(0, 300.0 * sin(timeOfDay), 300.0 * cos(timeOfDay)), | |
vec3(0, 42.0 * sin(timeOfDay + PI / 2.0), 42.0 * cos(timeOfDay + PI / 2.0)), | |
vec3(42, 0, 0), | |
2 | |
); | |
Plane renderables[6]; | |
renderables[0] = puzzle; | |
renderables[2] = lightSource; | |
renderables[3] = ground; | |
Plane puzzles[6]; | |
puzzles[0] = puzzle; | |
int found = -1; | |
float blendVal = (cos(timeOfDay - PI / 2.0) + 1.0) / 2.0; | |
gl_FragColor = cyan; | |
float foundDistance = findIntersection( | |
focus, | |
surface_loc, | |
renderables, | |
planarPosition, | |
worldPosition, | |
found | |
); | |
if (foundDistance >= 0.0) { | |
if (found == 0) { | |
gl_FragColor = drawPuzzle(planarPosition, newMouse, 8, isSolved); | |
} else if (found == 2) { | |
gl_FragColor = paleYellow; | |
} else if (found == 3) { | |
gl_FragColor = drawFoliage(planarPosition); | |
vec3 currentWP = worldPosition; | |
if (rayIntersectsCylinder( | |
currentWP, | |
lightSource.center, | |
vec3(0, 2, 0), | |
31.0, | |
4.0, | |
angle, | |
height, | |
t | |
)) { | |
vec4 value = texture2D(treeImage, vec2(mod(angle, 1.0/20.0) * 40.0, 1.0 - height)); | |
gl_FragColor = blend(gl_FragColor, vec4(shadow.rgb, shadow.a * value.a)); | |
} | |
if (rayIntersectsPlane( | |
currentWP, | |
lightSource.center, | |
puzzle, | |
planarPosition, | |
worldPosition | |
) >= 0.0) { | |
gl_FragColor = blend(gl_FragColor, shadow); | |
} | |
} | |
} | |
float treeDistance; | |
bool treeIntersects = | |
rayIntersectsCylinder( | |
focus, | |
surface_loc, | |
vec3(0, 2, 0), | |
31.0, | |
4.0, | |
angle, | |
height, | |
treeDistance | |
); | |
if (treeIntersects && (foundDistance < 0.0 || treeDistance < foundDistance)) { | |
vec4 treeColor = texture2D(treeImage, vec2(mod(angle, 1.0/20.0) * 40.0, 1.0 - height)); | |
gl_FragColor = blend(gl_FragColor, treeColor); | |
} | |
gl_FragColor = | |
gl_FragColor * (0.25 + 3.0 * blendVal / 4.0) + | |
vec4(0, 0, 0, 1.0) * (0.75 - 3.0 * blendVal / 4.0); | |
if (!puzzleActive && dist(gl_FragCoord.xy, resolution / 2.0) < 4.0) { | |
gl_FragColor = blend(gl_FragColor, cursor); | |
} | |
if (read_bool(CLICK) && rayIntersectsPlane(focus, viewport_center, puzzle, planarPosition, worldPosition) >= 0.0) { | |
puzzleActive = true; | |
} | |
return GameState(puzzleActive, isSolved); | |
} | |
GameState renderLevel1(vec2 newMouse) { | |
bool puzzleActive = read_bool(ACTIVE_PUZZLE); | |
bool isSolved = checkLevel1(8); | |
vec2 planarPosition; | |
vec3 worldPosition; | |
Plane puzzle = Plane(0, vec3(0, 0.5, 7), vec3(2, 0, 0), vec3(0, 2, 0), 1); | |
Plane lightSource = Plane(1, vec3(0, 7, -9), vec3(0, 1, 1), vec3(10, 0, 0), 1); | |
Plane renderables[6]; | |
renderables[0] = puzzle; | |
renderables[1] = lightSource; | |
Plane puzzles[6]; | |
puzzles[0] = puzzle; | |
Plane boxes[6]; | |
boxes[0] = Plane(0, vec3(0, -2.0, 0), vec3(0, 0, 10), vec3(10, 0, 0), 1); | |
boxes[1] = Plane(1, vec3(10, 3, 0), vec3(0, 5, 0), vec3(0, 0, 10), 1); | |
boxes[2] = Plane(2, vec3(0, 3, 10), vec3(10, 0, 0), vec3(0, 5, 0), 1); | |
boxes[3] = Plane(3, vec3(0, 3, -10), vec3(10, 0, 0), vec3(0, 5, 0), 1); | |
boxes[4] = Plane(4, vec3(-10, 3, 0), vec3(0, 5, 0), vec3(0, 0, 10), 1); | |
boxes[5] = Plane(5, vec3(0, 8, 0), vec3(0, 0, 10), vec3(10, 0, 0), 1); | |
int found = -1; | |
gl_FragColor = cyan; | |
if (findIntersection( | |
focus, | |
surface_loc, | |
renderables, | |
planarPosition, | |
worldPosition, | |
found) > 0.0 | |
) { | |
if (found == 0) { | |
gl_FragColor = drawPuzzle(planarPosition, newMouse, 8, isSolved); | |
} else if (found == 1) { | |
gl_FragColor = paleYellow; | |
} | |
} else { | |
if (findIntersection( | |
focus, | |
surface_loc, | |
boxes, | |
planarPosition, | |
worldPosition, | |
found) >= 0.0 | |
) { | |
if (found == 0 || found == 5) { | |
gl_FragColor = gray600; | |
} if (found == 2) { | |
gl_FragColor = texture2D(introImage, (vec2(1, -1) * planarPosition + 1.0) / 2.0); | |
} else { | |
gl_FragColor = gray900; | |
} | |
float dx = (abs(planarPosition.x) - 0.99) * 100.0; | |
float dy = (abs(planarPosition.y) - 0.99) * 100.0; | |
if (dx > 0.0 || dy > 0.0) { | |
float amount = max(dx, dy); | |
gl_FragColor = blend(gl_FragColor, shadow); | |
} | |
if (findIntersection( | |
worldPosition, | |
lightSource.center, | |
puzzles, | |
planarPosition, | |
worldPosition, | |
found) >= 0.0 | |
) { | |
gl_FragColor = blend(gl_FragColor, shadow); | |
} | |
} | |
} | |
if (!puzzleActive && dist(gl_FragCoord.xy, resolution / 2.0) < 4.0) { | |
gl_FragColor = blend(gl_FragColor, cursor); | |
} | |
if (read_bool(CLICK) && rayIntersectsPlane(focus, viewport_center, puzzle, planarPosition, worldPosition) >= 0.0) { | |
puzzleActive = true; | |
} | |
return GameState(puzzleActive, isSolved); | |
} | |
void main() { | |
vec4 empty = defaultEmpty; | |
int puzzle = read_byte(ACTIVE_LEVEL); | |
int size; | |
if (puzzle == 0) size = 8; | |
if (puzzle == 1) size = 8; | |
if (puzzle == 2 ) size = MAX_SIZE; | |
vec2 newMouse = normalizeMouse(size); | |
vec2 newLocation = normalizeLocation(); | |
vec2 rotation = normalizeRotation(); | |
bool puzzleActive = read_bool(ACTIVE_PUZZLE); | |
GameState state; | |
if (puzzle == 0) state = renderLevel1(newMouse); | |
if (puzzle == 1) state = renderLevel2(newMouse); | |
if (puzzle == 2) state = renderLevel3(newMouse); | |
write_float(MOUSE_LOCATION, newMouse.x / 2.0 + 0.5, 1.0) | |
write_float(MOUSE_LOCATION + 1.0, newMouse.y / 2.0 + 0.5, 1.0) | |
write_float(PLAYER_LOCATION, newLocation.x, 1.0) | |
write_float(PLAYER_LOCATION + 1.0, newLocation.y, 1.0) | |
write_float(VIEW_ANGLE, rotation.x, 2.0 * PI) | |
write_float(VIEW_ANGLE + 1.0, rotation.y, PI) | |
int next_segment = 0; | |
for (int i = 0; i < PATH_LEN; i++) { | |
ivec3 loc = read_ivec3(int(PATH) + i); | |
if (loc == ivec3(255, 255, 1)) { | |
next_segment = i; | |
break; | |
} | |
} | |
for (int i = 0; i < PATH_LEN; i++) { | |
ivec3 loc = read_ivec3(int(PATH) + i); | |
if (loc.z == 0) { | |
write_ivec3(PATH + float(i), ivec3(255, 255, 1)) | |
} else if (i == 0) { | |
write_ivec3(PATH + float(i), ivec3(0, 0, 1)); | |
} else if (state.puzzleActive) { | |
if (i == next_segment) { | |
ivec2 nextNode; | |
if (!state.puzzleSolved && isNode(newMouse, size, nextNode) && isValidNextSegment(nextNode, size)) { | |
write_ivec3(PATH + float(i), ivec3(nextNode.xy, 1)) | |
} | |
} | |
} else if (!state.puzzleSolved) { | |
write_ivec3(PATH + float(i), ivec3(255, 255, 1)); | |
} | |
write_ivec3(PATH + float(i), ivec3(loc.xy, 1)) | |
} | |
for (int i = 0; i < 6; i++) { | |
if (puzzle == 0 && state.puzzleSolved) { | |
write_byte(SOLUTION1 + float(i), decodePath(i, 6)); | |
} | |
if (puzzle == 1 && state.puzzleSolved) { | |
write_byte(SOLUTION2 + float(i), decodePath(i, 6)); | |
} | |
if (puzzle == 2 && state.puzzleSolved) { | |
write_byte(SOLUTION3 + float(i), decodePath(i, 6)); | |
} | |
write_byte(SOLUTION1 + float(i), read_byte(int(SOLUTION1) + i)); | |
write_byte(SOLUTION2 + float(i), read_byte(int(SOLUTION2) + i)); | |
write_byte(SOLUTION3 + float(i), read_byte(int(SOLUTION3) + i)); | |
} | |
vec3 worldPosition; | |
vec2 planarPosition; | |
Plane exit = Plane(0, vec3(0, 0, -10), vec3(0, 3, 0), vec3(2, 0, 0), 1); | |
if (state.puzzleSolved && rayIntersectsPlane(focus, surface_loc, exit, planarPosition, worldPosition) >= 0.0) { | |
gl_FragColor = blend(gl_FragColor, vec4(1, 1, 1, 0.95)); | |
} | |
if (state.puzzleSolved && dist(viewport_center, exit.center) < 2.0) { | |
puzzle = puzzle + 1; | |
} | |
if (read_bool(RIGHT_CLICK)) { | |
state.puzzleActive = false; | |
} | |
write_bool(ACTIVE_PUZZLE, state.puzzleActive); | |
write_byte(ACTIVE_LEVEL, puzzle); | |
write_bool(COMPLETE, (puzzle == 3)); | |
if (gl_FragCoord.y <= 1.0) { | |
gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0); | |
return; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment