init
This commit is contained in:
commit
87bff164f1
416 files changed
+279512
No files matched your search
@@ -0,0 +1,224 @@
|
||||
#version 450
|
||||
#extension GL_GOOGLE_include_directive : require
|
||||
layout(local_size_x = 64) in;
|
||||
#include "common.glsl"
|
||||
|
||||
// VerletPhysicsComponent chains (the vines and ropes of the caves): one invocation per chain root. The root entity has ex.w = 777, ex.x = number of points, ex.y = the
|
||||
// first part entity (the parts are consecutive entity slots, one per point; the point is the part's position, its previous position is kept in a.zw), ex.z = resting
|
||||
// distance, mv = (velocity dampening, stiffness, gravity (0/1), wind (0/1)). The first point hangs on the surface above the root; the others are simulated with Verlet
|
||||
// integration (the previous position gives the velocity), a distance constraint to the neighbour and the live cell grid as the collision.
|
||||
struct Ent { vec4 a, box, mv, ai; ivec4 st; vec4 hp; ivec4 x; ivec4 rg; ivec4 rg2; ivec4 aux; vec4 ex; vec4 ex2; ivec4 ex3; vec4 ex4; vec4 ex5; vec4 ex6; vec4 ex7; ivec4 ex8; vec4 ex9; vec4 ex10; vec4 ex11; vec4 ex12, ex13, ex14, ex15, ex16, ex17, ex18, ex19; };
|
||||
layout(std430, set = 0, binding = 22) buffer Ents { Ent ents[]; };
|
||||
layout(std430, set = 0, binding = 23) readonly buffer Ctrl { ivec4 inp; ivec4 inp2; ivec4 inp3; vec4 outp; vec4 outp2; vec4 view; vec4 hand0; ivec4 hand1; vec4 hand2; } ctrl;
|
||||
|
||||
bool solid_at(ivec2 w) {
|
||||
ivec2 p = w - ivec2(pc.view_x, pc.view_y);
|
||||
if (p.x < 0 || p.y < 0 || p.x >= pc.gw || p.y >= pc.gh) return false;
|
||||
uint m = cell_mat(cells[p.y * pc.gw + p.x]);
|
||||
return m != 0u && (mflags(m) & (F_STATIC | F_SAND)) != 0u;
|
||||
}
|
||||
|
||||
bool cell_present(ivec2 w) {
|
||||
ivec2 p = w - ivec2(pc.view_x, pc.view_y);
|
||||
return p.x >= 0 && p.y >= 0 && p.x < pc.gw && p.y < pc.gh && cell_mat(cells[p.y * pc.gw + p.x]) != 0u;
|
||||
}
|
||||
bool blocking_at(ivec2 w) { // a non-null cell whose type is solid (virtual +2c = 1; CLiquidCell 0070b0b0: a static powder - the rocks are liquid_static liquid_sand cells; CSolidCell)
|
||||
ivec2 p = w - ivec2(pc.view_x, pc.view_y);
|
||||
if (p.x < 0 || p.y < 0 || p.x >= pc.gw || p.y >= pc.gh) return false;
|
||||
uint m = cell_mat(cells[p.y * pc.gw + p.x]);
|
||||
return m != 0u && (mflags(m) & F_STATIC) != 0u && ((mflags(m) & F_POWDER) != 0u || (mflags(m) & F_LIQCELL) == 0u);
|
||||
}
|
||||
bool stain_here(ivec2 w) {
|
||||
ivec2 p = w - ivec2(pc.view_x, pc.view_y);
|
||||
if (p.x < 0 || p.y < 0 || p.x >= pc.gw || p.y >= pc.gh) return false;
|
||||
uint m = cell_mat(cells[p.y * pc.gw + p.x]);
|
||||
return m != 0u && (mflags(m) & F_STAINS) != 0u;
|
||||
}
|
||||
bool liquid_here(ivec2 w) {
|
||||
ivec2 p = w - ivec2(pc.view_x, pc.view_y);
|
||||
if (p.x < 0 || p.y < 0 || p.x >= pc.gw || p.y >= pc.gh) return false;
|
||||
uint m = cell_mat(cells[p.y * pc.gw + p.x]);
|
||||
return m != 0u && (mflags(m) & F_LIQCELL) != 0u && (mflags(m) & F_POWDER) == 0u;
|
||||
}
|
||||
|
||||
bool in_window(vec2 w, float margin) {
|
||||
vec2 p = w - vec2(pc.view_x, pc.view_y);
|
||||
return p.x > margin && p.y > margin && p.x < float(pc.gw) - margin && p.y < float(pc.gh) - margin;
|
||||
}
|
||||
|
||||
void main() {
|
||||
uint id = gl_GlobalInvocationID.x;
|
||||
if (id >= uint(ctrl.inp.w)) return;
|
||||
Ent root = ents[id];
|
||||
if (root.ex.w != 777.0) return;
|
||||
const int n = int(root.ex.x), first = int(root.ex.y);
|
||||
if ((root.st.w & 8) != 0) { // the chain was removed (its area is gone): so are its points
|
||||
for (int k = 0; k < n; ++k) ents[uint(first + k)].st.w |= 8;
|
||||
return;
|
||||
}
|
||||
const float rest = root.ex.z, damp = root.mv.x, stiff = root.mv.y;
|
||||
if (!in_window(root.a.xy, 24.0)) return;
|
||||
// a new chain waits 15 frames in the window before its surface search and its joints' first look at the cells: after a teleport the pixel scenes of the new area
|
||||
// reach the cells a few frames after the terrain (the holy mountain's brick over the generated rock), and the ropes searched the bare rock, or remembered it and broke
|
||||
if (root.hp.y == 0.0 && root.hp.z < 15.0) { ents[id].hp.z = root.hp.z + 1.0; return; }
|
||||
vec2 anchor = root.a.xy;
|
||||
const int jtype = int(root.ex4.x); // MoveToSurfaceOnCreateComponent: 0 the surface above, 1 VERLET_ROPE_ONE_JOINT, 2 VERLET_ROPE_TWO_JOINTS
|
||||
if (root.hp.y == 0.0) {
|
||||
if (jtype == 0 || jtype == 3) { // MoveToSurfaceOnCreate (3: the hair of a limb boss, its first point on the root entity the host moves): the first point goes to the surface above the spawn point (the cell below the first solid one)
|
||||
ivec2 c = ivec2(floor(anchor));
|
||||
if (jtype == 0 && !solid_at(c)) {
|
||||
for (int k = 1; k <= 24; ++k) if (solid_at(c - ivec2(0, k))) { anchor = vec2(anchor.x, float(c.y - k + 1)); break; }
|
||||
}
|
||||
ents[id].a.xy = anchor;
|
||||
for (int k = 0; k < n; ++k) { // hang straight down from the anchor
|
||||
vec2 p = anchor + vec2(0.0, float(k) * rest);
|
||||
ents[uint(first + k)].a = vec4(p, p);
|
||||
}
|
||||
} else if (jtype == 3) { // VerletWorldJointComponent (load_verlet_rope_with_*_joint*): the joints are given (ex2, ex7.z), nothing is searched
|
||||
} else {
|
||||
// 00bc3490 / 00bc2fa0 (called with first-hit and the upward fan): each try casts ray_count rays of length lookup_radius from the spawn point at random angles within
|
||||
// +-1.2566371 (0x3fa0d97c) of straight up, the first ray that hits gives the joint: the hit cell itself (offset_from_surface only moves the type-0 entity).
|
||||
// TWO_JOINTS: both joints are searched again until the second is at least verlet_min_joint_distance from the first; after 11 tries the joints are dropped (the rope is free).
|
||||
const float R = root.ex4.y, mind = root.ex4.w;
|
||||
const int rays = max(int(root.ex6.x), 1);
|
||||
vec2 A = vec2(0.0), B = vec2(0.0);
|
||||
bool gotA = false, gotB = false;
|
||||
for (int attempt = 0; attempt < 11 && !(jtype == 1 ? gotA : gotB); ++attempt) {
|
||||
gotA = false; gotB = false;
|
||||
for (int which = 0; which < 2; ++which) {
|
||||
if (which == 1 && (jtype != 2 || !gotA)) continue;
|
||||
vec2 pt = vec2(0.0);
|
||||
bool hit = false;
|
||||
for (int r = 0; r < rays && !hit; ++r) {
|
||||
const float ang = float(hash3(id, uint(attempt * 8 + which * 4 + r), 4242u) & 0x7FFFu) / 32767.0 * 2.5132742 - 1.2566371;
|
||||
const vec2 dir = vec2(sin(ang), -cos(ang));
|
||||
for (float t = 1.0; t <= R; t += 1.0) {
|
||||
const ivec2 c = ivec2(floor(root.a.xy + dir * t));
|
||||
if (solid_at(c)) { pt = vec2(c); hit = true; break; }
|
||||
}
|
||||
}
|
||||
if (!hit) break;
|
||||
if (which == 0) { A = pt; gotA = true; }
|
||||
else if (distance(pt, A) >= mind) { B = pt; gotB = true; }
|
||||
}
|
||||
}
|
||||
if (jtype == 2 && !gotB) gotA = false; // (no pair: both joints go)
|
||||
if (!gotA) { // 00bc3490: after 11 tries without its joint(s) the components go and the entity is killed (0044df60): no rope falls from the ceiling
|
||||
ents[id].st.w |= 8;
|
||||
for (int k = 0; k < n; ++k) ents[uint(first + k)].st.w |= 8;
|
||||
return;
|
||||
}
|
||||
float flags0 = 0.0;
|
||||
if (gotA) flags0 += 1.0;
|
||||
if (gotB) flags0 += 2.0;
|
||||
ents[id].ex2 = vec4(A, B);
|
||||
ents[id].ex7.z = flags0;
|
||||
}
|
||||
if (jtype == 1 || jtype == 2) {
|
||||
const int fl = int(ents[id].ex7.z);
|
||||
const bool gotA = (fl & 1) != 0, gotB = (fl & 2) != 0;
|
||||
const vec2 A = ents[id].ex2.xy, B = ents[id].ex2.zw;
|
||||
const vec2 P0 = gotA ? A : (gotB ? B - vec2(0.0, float(n - 1) * rest) : root.a.xy);
|
||||
ents[id].a.xy = P0;
|
||||
const float L = float(n - 1) * rest;
|
||||
for (int k = 0; k < n; ++k) { // 00c39fd0: the points start on a horizontal line from the entity position, resting_distance apart; the joints then move their points (first, last)
|
||||
vec2 p = root.a.xy + vec2(float(k) * rest, 0.0);
|
||||
if (k == 0 && gotA) p = A;
|
||||
if (gotB) { // two joints: started where the rope settles (along the chord with the sag of its length) so that no point begins inside the rock beside the spawn point
|
||||
const float d = max(distance(A, B), 1.0);
|
||||
const float sag = L > d ? sqrt(3.0 * d * (L - d) / 8.0) : 0.0;
|
||||
const float u = float(k) / float(max(n - 1, 1));
|
||||
p = mix(A, B, u) + vec2(0.0, 4.0 * sag * u * (1.0 - u));
|
||||
}
|
||||
ents[uint(first + k)].a = vec4(p, p);
|
||||
}
|
||||
}
|
||||
ents[id].hp.y = 1.0;
|
||||
}
|
||||
// the pins: ex7.z = which joints exist (1 first point, 2 last point; -1 none left); jtype 0 and a type 3 without data (a limb boss's hair) pin the first point
|
||||
int pinFlags = ents[id].ex7.z < 0.0 ? 0 : (jtype == 0 || jtype == 3) && ents[id].ex7.z == 0.0 ? 1 : int(ents[id].ex7.z);
|
||||
if (jtype == 1 || jtype == 2 || (jtype == 3 && ents[id].ex7.z > 0.0 && (pinFlags & 1) != 0)) anchor = ents[id].ex2.xy;
|
||||
{ // VerletWorldJointSystem (00c40b90): a joint remembers the cell at its world position on its first update (none there: it never breaks); when another cell or none is
|
||||
// there later, it breaks: the component goes and the rope's damping becomes 0.97 (and a "joint_break" sound, not played here). ex6.z / ex6.w: the remembered material
|
||||
// + 1 of the first / last point's joint, -1 for air, 0 not yet seen
|
||||
bool broke = false;
|
||||
for (int j = 0; j < 2; ++j) {
|
||||
if ((pinFlags & (1 << j)) == 0) continue;
|
||||
const vec2 jw = j == 0 ? anchor : ents[id].ex2.zw;
|
||||
const ivec2 q = ivec2(floor(jw)) - ivec2(pc.view_x, pc.view_y);
|
||||
if (q.x < 0 || q.y < 0 || q.x >= pc.gw || q.y >= pc.gh) continue;
|
||||
const uint m = cell_mat(cells[q.y * pc.gw + q.x]);
|
||||
const float rec = j == 0 ? ents[id].ex6.z : ents[id].ex6.w;
|
||||
if (rec == 0.0) { if (j == 0) ents[id].ex6.z = m == 0u ? -1.0 : float(m + 1u); else ents[id].ex6.w = m == 0u ? -1.0 : float(m + 1u); }
|
||||
else if (rec > 0.0 && float(m + 1u) != rec) { pinFlags &= ~(1 << j); broke = true; }
|
||||
}
|
||||
if (broke) { ents[id].ex7.z = pinFlags == 0 ? -1.0 : float(pinFlags); ents[id].mv.x = 0.97; }
|
||||
}
|
||||
const bool pinA = (pinFlags & 1) != 0, pinB = (pinFlags & 2) != 0;
|
||||
vec2 pts[40], prv[40];
|
||||
for (int k = 0; k < n && k < 40; ++k) { vec4 a = ents[uint(first + k)].a; pts[k] = a.xy; prv[k] = a.zw; }
|
||||
const float t = float(ctrl.inp2.x);
|
||||
float massOf[40]; // the random mass of every point between mass_min and mass_max (00c39fd0); it weights the relaxation, the forces are proportional to it
|
||||
for (int k = 0; k < n && k < 40; ++k) massOf[k] = root.ex5.y + (root.ex5.z - root.ex5.y) * float(hash3(id, uint(k), 77u) & 0xFFFFu) / 65536.0;
|
||||
const float T = root.ex5.x * t / 60.0; // wind_change_speed x time in seconds
|
||||
// wind (docs/EXPLOSION_KNOCKBACK_AND_VINES.md section 3): WorldStateComponent.wind (wind_speed x dt) times the sky exposure of the first point (00746620: the 2048 x 2048 map of
|
||||
// 64-pixel cells, 1 in the open and 0 a few cells under the biomes' ground; the earlier "x num_points" was the return of that lookup misread), set by the host in ex6.y
|
||||
const float W = root.ex6.y;
|
||||
const float Aw = sin(25.0 * T), Bw = sin(0.4 * T), Pw = 0.5 * (sin(T) + 1.0) + 0.5, Gw = 5.0 * abs(W) + 1.0;
|
||||
for (int k = 0; k < n && k < 40; ++k) {
|
||||
// 00c37d30: new = cur + (cur - prev) * damping + 0.5 F / m dt^2 (dt = 1/60); a point whose stored liquid material is set damps by 0.85
|
||||
const bool wet = ents[uint(first + k)].hp.x > 0.5; // the point carries a stored liquid material
|
||||
vec2 v = (pts[k] - prv[k]) * (wet ? 0.85 : damp);
|
||||
vec2 F = vec2(0.0); // force per unit mass
|
||||
if (root.mv.z > 0.5) F.y += 600.0; // 00c379e0: mass x 600
|
||||
if (root.mv.w > 0.5) { // 00c37b10
|
||||
F.x += 360.0 * W * Pw + 100.0 * sin(5.0 * pts[k].x) * Aw;
|
||||
F.y += 20.0 * sin(5.0 * pts[k].y) * Aw + 60.0 * sin(0.005 * pts[k].y) * Bw * Gw;
|
||||
}
|
||||
const vec2 delta = v + F * 0.5 / 3600.0;
|
||||
vec2 cand = pts[k] + delta;
|
||||
// the terrain (00c37d30): probe (truncated x, ceil y); a free cell takes the candidate (a liquid one only liquid_damping of the move); a blocking one halves the move once; if the midpoint still
|
||||
// blocks, the cell above it decides: free = one pixel up, otherwise the point stays and its velocity is gone
|
||||
vec2 np = cand;
|
||||
if (blocking_at(ivec2(int(cand.x), int(ceil(cand.y))))) {
|
||||
const vec2 mid = (pts[k] + cand) * 0.5;
|
||||
if (!blocking_at(ivec2(int(mid.x), int(ceil(mid.y))))) np = mid;
|
||||
else if (!blocking_at(ivec2(int(mid.x), int(ceil(mid.y)) - 1)) && cell_present(ivec2(int(mid.x), int(ceil(mid.y)) - 1))) { np = pts[k] + vec2(0.0, -1.0); prv[k] = np; pts[k] = np; continue; }
|
||||
else { prv[k] = pts[k]; continue; }
|
||||
} else if (liquid_here(ivec2(int(cand.x), int(ceil(cand.y))))) {
|
||||
np = pts[k] + root.ex5.w * delta;
|
||||
// 00c37d30: the liquid's material is sampled into the point on 6 of 10 points, else with a 1 in 30 draw (only liquids that stain)
|
||||
if (stain_here(ivec2(int(cand.x), int(ceil(cand.y)))) && (k % 10 < 6 || hash3(id, uint(k), uint(pc.frame)) % 30u == 0u)) ents[uint(first + k)].hp.x = 1.0;
|
||||
}
|
||||
prv[k] = pts[k];
|
||||
pts[k] = np;
|
||||
}
|
||||
for (int it = 0; it < 2; ++it) // 00c38310 runs twice over the links: the chain is soft, it stretches under its own weight
|
||||
for (int k = 0; k + 1 < n && k + 1 < 40; ++k) { // distance constraints; the joints' points move like the others here and are put back afterwards (00c3a610)
|
||||
vec2 d = pts[k + 1] - pts[k];
|
||||
float l = length(d);
|
||||
if (l < 1e-5) continue;
|
||||
float diff = (l - rest) / max(l, 0.01) * stiff * 0.5; // 00c3a610 passes stiffness x 0.5 (0x3f000000) to 00c38310; links shorter than 0.01 count as 0.01
|
||||
const float wa = 1.0 / massOf[k], wb = 1.0 / massOf[k + 1], sa = wa / (wa + wb); // the share of the correction by the inverse masses
|
||||
pts[k] += d * diff * sa; pts[k + 1] -= d * diff * (1.0 - sa);
|
||||
}
|
||||
if (pinA) pts[0] = anchor; // the world joints put their points back after the relaxation (00c3a610)
|
||||
if (pinB) pts[n - 1] = ents[id].ex2.zw;
|
||||
// the animation (00c385a0, after the relaxation when animation_target_offset is not 0 - the tentacler's tentacles, offset (0, 1)): every link pulls its far point toward
|
||||
// near point + offset + the offset's direction turned by 90 degrees x sin((link + time) x animation_speed) x animation_amount, by animation_energy shared by the inverse masses
|
||||
// (root ex17 = offset, amount, speed; ex18.x = energy; the time in seconds)
|
||||
if (root.ex17.x != 0.0 || root.ex17.y != 0.0) {
|
||||
const vec2 D = root.ex17.xy, u = normalize(D), perp = vec2(-u.y, u.x);
|
||||
for (int k = 0; k + 1 < n && k + 1 < 40; ++k) {
|
||||
const float w = sin((float(k) + t / 60.0) * root.ex17.w) * root.ex17.z;
|
||||
const vec2 delta = pts[k] + D + perp * w - pts[k + 1];
|
||||
const float wa = 1.0 / massOf[k], wb = 1.0 / massOf[k + 1];
|
||||
pts[k + 1] += delta * (root.ex18.x * wb / (wa + wb));
|
||||
}
|
||||
}
|
||||
for (int k = 0; k < n && k < 40; ++k) {
|
||||
ents[uint(first + k)].a = vec4(pts[k], prv[k]);
|
||||
const vec2 d = k + 1 < n ? pts[k + 1] - pts[k] : pts[k] - pts[max(k - 1, 0)];
|
||||
ents[uint(first + k)].ex7.x = atan(d.y, d.x); // the horizontal part sprite is turned along the chain
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user