Spaces:
Running
Running
Update index.html
Browse files- index.html +155 -157
index.html
CHANGED
|
@@ -88,10 +88,9 @@
|
|
| 88 |
the <em>resonance</em> meter runs hot as you near the key. no enemies, no clocks —<br>
|
| 89 |
only geometry, depth, and the hash. nothing is stored; walk back and every<br>
|
| 90 |
room is re-dreamed identically. click / drag to enter · mouse look is inverted.<br><br>
|
| 91 |
-
<em>first launch
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
after this one is near-instant.</div>
|
| 95 |
<div id="bootStat">waking up…</div>
|
| 96 |
</div>
|
| 97 |
|
|
@@ -120,6 +119,7 @@
|
|
| 120 |
<button id="m0" class="on">Boundary</button>
|
| 121 |
<button id="m1">Projection</button>
|
| 122 |
<button id="m2">Cascade</button>
|
|
|
|
| 123 |
</div>
|
| 124 |
<div class="field"><label>Mass gain <var id="m_l">1.0</var></label>
|
| 125 |
<input id="m" type="range" min="0.2" max="2.2" step="0.05" value="1.0"></div>
|
|
@@ -184,12 +184,17 @@ uniform vec2 iRes;
|
|
| 184 |
uniform float iTime;
|
| 185 |
uniform vec3 camP,camR,camU,camF;
|
| 186 |
uniform int nWell,mode,keyWell;
|
|
|
|
|
|
|
|
|
|
|
|
|
| 187 |
uniform vec4 wells[${MAXW}]; /* xyz pos · w size */
|
| 188 |
uniform vec3 wellCol[${MAXW}];
|
| 189 |
-
uniform vec4 wellPar[${MAXW}]; /* x
|
| 190 |
uniform vec3 roomPos[3]; /* prev · current · next */
|
| 191 |
-
uniform vec4 roomPar[3]; /* x
|
| 192 |
-
uniform vec4 roomMod[3]; /* x twist · y
|
|
|
|
| 193 |
uniform vec3 roomTintU[3];
|
| 194 |
uniform vec4 seal; /* xyz pos · w radius (<0 = open) */
|
| 195 |
uniform float mGain,lensK;
|
|
@@ -197,65 +202,46 @@ uniform sampler2D camTex;
|
|
| 197 |
|
| 198 |
/* ---------- primitives ---------- */
|
| 199 |
vec2 rot2(vec2 v,float a){float c=cos(a),s=sin(a);return vec2(c*v.x-s*v.y,s*v.x+c*v.y);}
|
| 200 |
-
float sdBox3(vec3 p,vec3 b){vec3 q=abs(p)-b;return length(max(q,0.))+min(max(q.x,max(q.y,q.z)),0.);}
|
| 201 |
-
float sdHexP(vec3 p,vec2 h){ /* cross-section xy · half-height z */
|
| 202 |
-
const vec3 k=vec3(-0.8660254,0.5,0.57735);
|
| 203 |
-
p=abs(p);
|
| 204 |
-
p.xy-=2.0*min(dot(k.xy,p.xy),0.0)*k.xy;
|
| 205 |
-
vec2 d=vec2(length(p.xy-vec2(clamp(p.x,-k.z*h.x,k.z*h.x),h.x))*sign(p.y-h.x),p.z-h.y);
|
| 206 |
-
return min(max(d.x,d.y),0.0)+length(max(d,0.0));}
|
| 207 |
-
float sdStarN(vec2 p,float r,float n,float m){ /* n-pointed star, m in (2,n) */
|
| 208 |
-
float an=3.1415927/n, en=3.1415927/m;
|
| 209 |
-
vec2 acs=vec2(cos(an),sin(an)), ecs=vec2(cos(en),sin(en));
|
| 210 |
-
float bn=mod(atan(p.x,p.y),2.0*an)-an;
|
| 211 |
-
p=length(p)*vec2(cos(bn),abs(sin(bn)));
|
| 212 |
-
p-=r*acs;
|
| 213 |
-
p+=ecs*clamp(-dot(p,ecs),0.0,r*acs.y/ecs.y);
|
| 214 |
-
return length(p)*sign(p.x);}
|
| 215 |
-
float sdOcta(vec3 p,float s){p=abs(p);return (p.x+p.y+p.z-s)*0.57735;}
|
| 216 |
-
float opExtr(float d2,float z,float h){vec2 w=vec2(d2,abs(z)-h);
|
| 217 |
-
return min(max(w.x,w.y),0.0)+length(max(w,0.0));}
|
| 218 |
float sdSeg(vec3 p,vec3 a,vec3 b){vec3 pa=p-a,ba=b-a;
|
| 219 |
float h=clamp(dot(pa,ba)/dot(ba,ba),0.0,1.0);return length(pa-ba*h);}
|
| 220 |
|
| 221 |
-
/* ----------
|
| 222 |
-
|
| 223 |
-
|
| 224 |
-
|
| 225 |
-
|
| 226 |
-
|
| 227 |
-
|
| 228 |
-
|
| 229 |
-
|
| 230 |
-
|
| 231 |
-
|
| 232 |
-
|
| 233 |
-
|
| 234 |
-
|
| 235 |
-
|
| 236 |
-
|
| 237 |
-
|
| 238 |
-
|
| 239 |
-
|
| 240 |
-
|
| 241 |
-
|
| 242 |
-
|
| 243 |
-
|
| 244 |
-
|
| 245 |
-
|
| 246 |
-
|
| 247 |
-
|
| 248 |
-
sdSeg(q,vec3(0.),vec3(0.,0.,-s*0.72))))-s*0.17;
|
| 249 |
-
return min(tor,min(hub,sp));
|
| 250 |
}
|
| 251 |
float roomSD(vec3 p,int r){
|
| 252 |
vec3 q=p-roomPos[r];
|
| 253 |
float tw=roomMod[r].x;
|
| 254 |
if(abs(tw)>0.0005) q.xz=rot2(q.xz,q.y*tw);
|
| 255 |
-
float d=shapeSD(q,roomPar[r].x,roomPar[r].y,roomPar[r].z,roomPar[r].w
|
|
|
|
| 256 |
float ra=roomMod[r].y;
|
| 257 |
if(ra>0.001) d+=ra*sin(q.x*roomMod[r].z)*sin(q.y*roomMod[r].z)*sin(q.z*roomMod[r].z);
|
| 258 |
-
return d/(1.0+abs(tw)*roomPar[r].
|
| 259 |
}
|
| 260 |
float worldSD(vec3 p){
|
| 261 |
float d=min(roomSD(p,0),min(roomSD(p,1),roomSD(p,2)));
|
|
@@ -271,21 +257,18 @@ vec3 wallNormal(vec3 p){ /* inward-facing */
|
|
| 271 |
e.xyy*worldSD(p+e.xyy)+e.yyx*worldSD(p+e.yyx)+
|
| 272 |
e.yxy*worldSD(p+e.yxy)+e.xxx*worldSD(p+e.xxx));}
|
| 273 |
|
| 274 |
-
/* ---------- moons
|
| 275 |
-
|
| 276 |
-
this keeps the shader small enough for slow driver compilers ---------- */
|
| 277 |
-
const float CS[12]=float[12](0.50,1.00,0.60,0.765,0.613,0.52,0.644,0.590,0.740,0.600,0.568,0.55);
|
| 278 |
-
const float CH[12]=float[12](0.50,1.00,0.34,0.180,0.613,0.42,0.545,0.590,0.740,0.470,0.280,0.55);
|
| 279 |
-
const float CR[12]=float[12](0.26,0.00,0.22,0.16,0.24,0.20,0.00,0.18,0.06,0.14,0.12,0.10);
|
| 280 |
float moonSD(vec3 p,int k){
|
| 281 |
vec3 q=p-wells[k].xyz;
|
| 282 |
float s=wells[k].w;
|
| 283 |
float bs=length(q)-s*2.2;
|
| 284 |
if(bs>0.6) return bs;
|
| 285 |
-
q.xz=rot2(q.xz,wellPar[k].
|
| 286 |
-
q.xy=rot2(q.xy,wellPar[k].
|
| 287 |
-
int
|
| 288 |
-
return shapeSD(q,
|
|
|
|
| 289 |
}
|
| 290 |
vec3 moonNormal(vec3 p,int k){
|
| 291 |
const vec2 e=vec2(0.012,-0.012);
|
|
@@ -314,7 +297,7 @@ void main(){
|
|
| 314 |
vec3 gA0=mix(roomPos[0],roomPos[1],0.28), gB0=mix(roomPos[0],roomPos[1],0.72);
|
| 315 |
vec3 gA1=mix(roomPos[1],roomPos[2],0.28), gB1=mix(roomPos[1],roomPos[2],0.72);
|
| 316 |
|
| 317 |
-
for(int i=0;i<
|
| 318 |
/* lensing: deflection per path length, dtheta ~ gradPhi * ds */
|
| 319 |
if(lensK>0.001){
|
| 320 |
vec3 pull=vec3(0.0);
|
|
@@ -366,19 +349,44 @@ void main(){
|
|
| 366 |
L+=wells[k].w*mGain/(length(x-wells[k].xyz)+1.0);}
|
| 367 |
col=tint*eqSample(radial)*(0.55+0.7*min(L,1.4)); done=true; break;
|
| 368 |
}
|
| 369 |
-
|
| 370 |
-
|
| 371 |
-
|
| 372 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 373 |
glow+=tint*eqSample(reflect(rd,opticN))*F*0.55; /* mirror glint, Fresnel-weighted */
|
| 374 |
-
if(
|
| 375 |
-
bnc++; if(bnc>MAXB){col=(mode==2)?accum:vec3(0.0);done=true;break;}
|
| 376 |
const float IOR=1.45;
|
| 377 |
vec3 d=refract(rd,opticN,1.0/IOR);
|
| 378 |
if(dot(d,d)<1e-4) d=reflect(rd,opticN); /* entry-grazing guard */
|
| 379 |
x-=n*0.04; /* step inside the glass */
|
| 380 |
float chord=0.0;
|
| 381 |
-
for(int j=0;j<
|
| 382 |
float sd=moonSD(x,hb);
|
| 383 |
if(sd>-0.006){ /* reached the far surface */
|
| 384 |
vec3 ne=moonNormal(x,hb);
|
|
@@ -399,11 +407,11 @@ void main(){
|
|
| 399 |
tint*=exp(-(vec3(1.0)-wellCol[hb])*chord*0.35)*0.96;
|
| 400 |
rd=d;
|
| 401 |
vec3 ne2=moonNormal(x,hb);
|
| 402 |
-
for(int j=0;j<
|
| 403 |
x+=rd*0.04; trav+=chord+0.08; stp=0.05; continue;
|
| 404 |
}
|
| 405 |
if(dw<0.03){
|
| 406 |
-
if(mode==0){
|
| 407 |
vec3 wc=wallImg(x);
|
| 408 |
float L=0.0; for(int k=0;k<nWell;k++){
|
| 409 |
L+=wells[k].w*mGain/(length(x-wells[k].xyz)+1.0);}
|
|
@@ -412,8 +420,8 @@ void main(){
|
|
| 412 |
} else { /* PROJECTION / CASCADE: walls mirror */
|
| 413 |
vec3 n=wallNormal(x);
|
| 414 |
rd=reflect(rd,n); tint*=0.85; bnc++;
|
| 415 |
-
if(bnc>
|
| 416 |
-
for(int j=0;j<
|
| 417 |
x+=rd*0.04; stp=0.05; continue;
|
| 418 |
}
|
| 419 |
}
|
|
@@ -493,8 +501,17 @@ function rng(i,salt){
|
|
| 493 |
t^=t+Math.imul(t^(t>>>7),t|61);
|
| 494 |
return ((t^(t>>>14))>>>0)/4294967296;
|
| 495 |
}
|
| 496 |
-
|
| 497 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 498 |
const SYL=['KA','RA','JA','PIN','TA','VUO','LU','MI','SA','TÄH','TI','KI','VI','HEL','ME','NU','SO','LO'];
|
| 499 |
|
| 500 |
/* ------- level geometry: depth(L) rooms per level, cumulative starts ------- */
|
|
@@ -514,23 +531,40 @@ function nMoonsOf(L){return Math.min(3+((L-1)>>1),5);}
|
|
| 514 |
|
| 515 |
function roomDef(i){
|
| 516 |
const L=levelOfRoom(i);
|
| 517 |
-
|
| 518 |
-
|
|
|
|
|
|
|
|
|
|
| 519 |
const size=7.0+rng(i,2)*4.0+Math.min(L*0.15,3.0);
|
| 520 |
const height=4.5+rng(i,3)*2.5;
|
| 521 |
-
const
|
|
|
|
|
|
|
|
|
|
|
|
|
| 522 |
const hue=rng(i,5);
|
| 523 |
-
const turn=(rng(i,6)-0.5)*2.4;
|
| 524 |
-
|
| 525 |
-
|
| 526 |
const ripF=0.5+rng(i,10)*1.2;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 527 |
const massK=Math.min(0.7+rng(i,11)*0.9+L*0.04,2.8);
|
| 528 |
let name='';const nc=2+Math.floor(rng(i,7)*2);
|
| 529 |
for(let k=0;k<nc;k++)name+=SYL[Math.floor(rng(i,20+k)*SYL.length)];
|
| 530 |
const tint=[0.86+0.14*Math.cos(6.283*hue),
|
| 531 |
0.86+0.14*Math.cos(6.283*(hue+0.33)),
|
| 532 |
0.86+0.14*Math.cos(6.283*(hue+0.66))];
|
| 533 |
-
return {
|
| 534 |
}
|
| 535 |
function moonDef(i,m){ /* moon m of room i — re-dreamed identically every visit */
|
| 536 |
const s=100+m*13;
|
|
@@ -566,7 +600,9 @@ function rebuildChain(){
|
|
| 566 |
dirOut
|
| 567 |
};
|
| 568 |
$('roomV').textContent=dCur.name;
|
| 569 |
-
$('roomI').textContent='#'+CI+' · '+(dCur
|
|
|
|
|
|
|
| 570 |
$('massV').textContent=dCur.massK.toFixed(2)+' · '+(dCur.twist>=0?'+':'')+dCur.twist.toFixed(3);
|
| 571 |
updateLevelHud();
|
| 572 |
}
|
|
@@ -609,70 +645,22 @@ function retreat(){ /* stepped back — re-dream the past */
|
|
| 609 |
}
|
| 610 |
|
| 611 |
/* ---------------- JS twins of the SDFs (collision + transitions) ---------- */
|
| 612 |
-
|
| 613 |
-
const qx=Math.abs(px)-bx,qy=Math.abs(py)-by,qz=Math.abs(pz)-bz;
|
| 614 |
-
return Math.hypot(Math.max(qx,0),Math.max(qy,0),Math.max(qz,0))
|
| 615 |
-
+Math.min(Math.max(qx,Math.max(qy,qz)),0);}
|
| 616 |
-
function sdHexJ(px,py,pz,h1,h2){
|
| 617 |
-
const kx=-0.8660254,ky=0.5,kz=0.57735;
|
| 618 |
-
px=Math.abs(px);py=Math.abs(py);pz=Math.abs(pz);
|
| 619 |
-
const d0=Math.min(kx*px+ky*py,0)*2;
|
| 620 |
-
px-=d0*kx;py-=d0*ky;
|
| 621 |
-
const cl=Math.min(Math.max(px,-kz*h1),kz*h1);
|
| 622 |
-
const dx=Math.hypot(px-cl,py-h1)*Math.sign(py-h1);
|
| 623 |
-
const dy=pz-h2;
|
| 624 |
-
return Math.min(Math.max(dx,dy),0)+Math.hypot(Math.max(dx,0),Math.max(dy,0));}
|
| 625 |
-
function sdStarNJ(px,py,r,n,m){
|
| 626 |
-
const an=Math.PI/n,en=Math.PI/m;
|
| 627 |
-
const ax=Math.cos(an),ay=Math.sin(an),ex=Math.cos(en),ey=Math.sin(en);
|
| 628 |
-
let bn=Math.atan2(px,py)%(2*an); if(bn<0)bn+=2*an; bn-=an;
|
| 629 |
-
const l=Math.hypot(px,py);
|
| 630 |
-
px=l*Math.cos(bn); py=l*Math.abs(Math.sin(bn));
|
| 631 |
-
px-=r*ax; py-=r*ay;
|
| 632 |
-
const cl=Math.min(Math.max(-(px*ex+py*ey),0),r*ay/ey);
|
| 633 |
-
px+=ex*cl; py+=ey*cl;
|
| 634 |
-
return Math.hypot(px,py)*Math.sign(px);}
|
| 635 |
-
function opExtrJ(d2,z,h){const wy=Math.abs(z)-h;
|
| 636 |
-
return Math.min(Math.max(d2,wy),0)+Math.hypot(Math.max(d2,0),Math.max(wy,0));}
|
| 637 |
-
function sdSegJ(px,py,pz,a,b){
|
| 638 |
-
const bax=b[0]-a[0],bay=b[1]-a[1],baz=b[2]-a[2];
|
| 639 |
-
const pax=px-a[0],pay=py-a[1],paz=pz-a[2];
|
| 640 |
-
const h=Math.min(Math.max((pax*bax+pay*bay+paz*baz)/(bax*bax+bay*bay+baz*baz),0),1);
|
| 641 |
-
return Math.hypot(pax-bax*h,pay-bay*h,paz-baz*h);}
|
| 642 |
-
function sdSegOJ(px,py,pz,ax,ay,az,bx,by,bz){
|
| 643 |
-
const bax=bx-ax,bay=by-ay,baz=bz-az;
|
| 644 |
-
const pax=px-ax,pay=py-ay,paz=pz-az;
|
| 645 |
-
const h=Math.min(Math.max((pax*bax+pay*bay+paz*baz)/(bax*bax+bay*bay+baz*baz),0),1);
|
| 646 |
-
return Math.hypot(pax-bax*h,pay-bay*h,paz-baz*h);}
|
| 647 |
function shapeSDJ(x,y,z,d){
|
| 648 |
-
const s=d.size
|
| 649 |
-
|
| 650 |
-
|
| 651 |
-
|
| 652 |
-
|
| 653 |
-
|
| 654 |
-
|
| 655 |
-
|
| 656 |
-
|
| 657 |
-
|
| 658 |
-
|
| 659 |
-
|
| 660 |
-
(Math.abs(x)+Math.abs(y)+Math.abs(z)-s*1.45)*0.57735);
|
| 661 |
-
case 8:return (Math.hypot(x,y*1.35,z)-s*1.08)*0.72;
|
| 662 |
-
case 9:return Math.min(sdBoxJ(x,y,z,s,h*0.55,s*0.38),
|
| 663 |
-
Math.min(sdBoxJ(x,y,z,s*0.38,h*0.55,s),
|
| 664 |
-
sdBoxJ(x,y,z,s*0.45,h,s*0.45)));
|
| 665 |
-
case 10:{const a=Math.atan2(z,x);
|
| 666 |
-
const rr=s*(0.88+0.11*Math.cos(a*d.starN*2.0));
|
| 667 |
-
return Math.max(Math.hypot(x,z)-rr,Math.abs(y)-h)*0.68;}
|
| 668 |
-
default:{const tor=Math.hypot(Math.hypot(x,z)-s*0.72,y)-s*0.40;
|
| 669 |
-
const hub=Math.hypot(x,y,z)-s*0.40;
|
| 670 |
-
let sp=Math.min(sdSegOJ(x,y,z,0,0,0,s*0.72,0,0),
|
| 671 |
-
sdSegOJ(x,y,z,0,0,0,-s*0.72,0,0));
|
| 672 |
-
sp=Math.min(sp,Math.min(sdSegOJ(x,y,z,0,0,0,0,0,s*0.72),
|
| 673 |
-
sdSegOJ(x,y,z,0,0,0,0,0,-s*0.72)))-s*0.17;
|
| 674 |
-
return Math.min(tor,Math.min(hub,sp));}
|
| 675 |
}
|
|
|
|
| 676 |
}
|
| 677 |
function roomSDJ(px,py,pz,r){
|
| 678 |
const d=R.def[r],c=R.pos[r];
|
|
@@ -697,7 +685,8 @@ let P={mGain:1.0,lensK:1.0},mode=0,idle=1;
|
|
| 697 |
const wellsF=new Float32Array(MAXW*4), colsF=new Float32Array(MAXW*3),
|
| 698 |
parF=new Float32Array(MAXW*4),
|
| 699 |
roomPosF=new Float32Array(9), roomParF=new Float32Array(12),
|
| 700 |
-
roomModF=new Float32Array(12),
|
|
|
|
| 701 |
function moonCol(h){
|
| 702 |
return[0.55+0.45*Math.cos(6.283*h),0.55+0.45*Math.cos(6.283*(h+0.33)),0.55+0.45*Math.cos(6.283*(h+0.66))];}
|
| 703 |
let keyWell=-1, keyPos=null, keyR=0;
|
|
@@ -708,8 +697,9 @@ function packWorld(t){
|
|
| 708 |
for(let r=0;r<3;r++){
|
| 709 |
const d=R.def[r],c=R.pos[r];
|
| 710 |
roomPosF[r*3]=c[0];roomPosF[r*3+1]=c[1];roomPosF[r*3+2]=c[2];
|
| 711 |
-
roomParF[r*4]=d.
|
| 712 |
roomModF[r*4]=d.twist;roomModF[r*4+1]=d.ripA;roomModF[r*4+2]=d.ripF;roomModF[r*4+3]=d.massK;
|
|
|
|
| 713 |
roomTintF[r*3]=d.tint[0];roomTintF[r*3+1]=d.tint[1];roomTintF[r*3+2]=d.tint[2];
|
| 714 |
const A=d.size*0.62, Ay=d.height*0.55;
|
| 715 |
for(let m=0;m<nM && w<MAXW;m++,w++){
|
|
@@ -727,7 +717,7 @@ function packWorld(t){
|
|
| 727 |
wellsF[w*4+3]=size;
|
| 728 |
const cc=isKey?[1.0,0.84,0.36]:moonCol(md.hue);
|
| 729 |
colsF[w*3]=cc[0];colsF[w*3+1]=cc[1];colsF[w*3+2]=cc[2];
|
| 730 |
-
parF[w*4]=
|
| 731 |
if(isKey){keyWell=w;keyPos=[wellsF[w*4],wellsF[w*4+1],wellsF[w*4+2]];keyR=size;}
|
| 732 |
}
|
| 733 |
}
|
|
@@ -844,13 +834,14 @@ $('bLoad').onclick=()=>{
|
|
| 844 |
|
| 845 |
/* ---------------- controls ---------------- */
|
| 846 |
const MODES=[
|
| 847 |
-
{name:'Boundary', note:'the walls are your camera
|
| 848 |
{name:'Projection', note:'the moons carry your face; every wall is a mirror. the image emanates from the bulk — the holographic swap.'},
|
| 849 |
-
{name:'Cascade', note:'
|
|
|
|
| 850 |
];
|
| 851 |
function setMode(mi){
|
| 852 |
mode=mi;
|
| 853 |
-
['m0','m1','m2'].forEach((id,i)=>$(id).classList.toggle('on',i===mi));
|
| 854 |
$('modeV').textContent=MODES[mi].name;
|
| 855 |
$('modeNote').textContent=MODES[mi].note;
|
| 856 |
}
|
|
@@ -862,6 +853,7 @@ bind('v','v_l',v=>{if(audio)audio.volume=v;});
|
|
| 862 |
$('m0').onclick=()=>setMode(0);
|
| 863 |
$('m1').onclick=()=>setMode(1);
|
| 864 |
$('m2').onclick=()=>setMode(2);
|
|
|
|
| 865 |
$('bSrc').onclick=()=>{useCam=!useCam;setSrcLabel();};
|
| 866 |
$('bSeed').onclick=()=>{ SEED=(Math.random()*4294967296)>>>0;
|
| 867 |
LEVEL=1;maxReached=1;keyFound=false;CI=0;
|
|
@@ -929,9 +921,14 @@ function compileAndStart(){
|
|
| 929 |
}
|
| 930 |
gl.useProgram(prog);
|
| 931 |
['iRes','iTime','camP','camR','camU','camF','nWell','mode','keyWell','mGain','lensK','camTex',
|
| 932 |
-
'
|
|
|
|
| 933 |
.forEach(k=>U[k]=gl.getUniformLocation(prog,k));
|
| 934 |
gl.uniform1i(U.camTex,0);
|
|
|
|
|
|
|
|
|
|
|
|
|
| 935 |
$('bootStat').textContent='ready — click to enter';
|
| 936 |
requestAnimationFrame(loop);
|
| 937 |
}
|
|
@@ -996,6 +993,7 @@ function loop(now){
|
|
| 996 |
gl.uniform3fv(U.roomPos,roomPosF);
|
| 997 |
gl.uniform4fv(U.roomPar,roomParF);
|
| 998 |
gl.uniform4fv(U.roomMod,roomModF);
|
|
|
|
| 999 |
gl.uniform3fv(U.roomTintU,roomTintF);
|
| 1000 |
gl.uniform4f(U.seal,sealV[0],sealV[1],sealV[2],sealV[3]);
|
| 1001 |
gl.drawArrays(gl.TRIANGLES,0,3);
|
|
|
|
| 88 |
the <em>resonance</em> meter runs hot as you near the key. no enemies, no clocks —<br>
|
| 89 |
only geometry, depth, and the hash. nothing is stored; walk back and every<br>
|
| 90 |
room is re-dreamed identically. click / drag to enter · mouse look is inverted.<br><br>
|
| 91 |
+
<em>first launch:</em> the GPU driver compiles the shader once, then caches it.<br>
|
| 92 |
+
the world is now ONE branchless shape kernel — this should take seconds, not<br>
|
| 93 |
+
minutes. the counter below is honest elapsed time either way.</div>
|
|
|
|
| 94 |
<div id="bootStat">waking up…</div>
|
| 95 |
</div>
|
| 96 |
|
|
|
|
| 119 |
<button id="m0" class="on">Boundary</button>
|
| 120 |
<button id="m1">Projection</button>
|
| 121 |
<button id="m2">Cascade</button>
|
| 122 |
+
<button id="m3">Glass</button>
|
| 123 |
</div>
|
| 124 |
<div class="field"><label>Mass gain <var id="m_l">1.0</var></label>
|
| 125 |
<input id="m" type="range" min="0.2" max="2.2" step="0.05" value="1.0"></div>
|
|
|
|
| 184 |
uniform float iTime;
|
| 185 |
uniform vec3 camP,camR,camU,camF;
|
| 186 |
uniform int nWell,mode,keyWell;
|
| 187 |
+
/* loop bounds as UNIFORMS: the D3D/ANGLE compiler cannot know their value,
|
| 188 |
+
so it cannot unroll the raymarch 180-wide (x26 for the glass chord) —
|
| 189 |
+
it must compile each loop body ONCE. This is the first-launch fix. */
|
| 190 |
+
uniform int marchN,glassN,escN;
|
| 191 |
uniform vec4 wells[${MAXW}]; /* xyz pos · w size */
|
| 192 |
uniform vec3 wellCol[${MAXW}];
|
| 193 |
+
uniform vec4 wellPar[${MAXW}]; /* x spin-xz · y spin-xy · z room index · w free */
|
| 194 |
uniform vec3 roomPos[3]; /* prev · current · next */
|
| 195 |
+
uniform vec4 roomPar[3]; /* x size · y p-xz · z p-vert · w squash */
|
| 196 |
+
uniform vec4 roomMod[3]; /* x twist · y breath amp · z breath freq · w mass */
|
| 197 |
+
uniform vec4 roomShp[3]; /* x ripple amp · y lobes · z ring blend · w free */
|
| 198 |
uniform vec3 roomTintU[3];
|
| 199 |
uniform vec4 seal; /* xyz pos · w radius (<0 = open) */
|
| 200 |
uniform float mGain,lensK;
|
|
|
|
| 202 |
|
| 203 |
/* ---------- primitives ---------- */
|
| 204 |
vec2 rot2(vec2 v,float a){float c=cos(a),s=sin(a);return vec2(c*v.x-s*v.y,s*v.x+c*v.y);}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 205 |
float sdSeg(vec3 p,vec3 a,vec3 b){vec3 pa=p-a,ba=b-a;
|
| 206 |
float h=clamp(dot(pa,ba)/dot(ba,ba),0.0,1.0);return length(pa-ba*h);}
|
| 207 |
|
| 208 |
+
/* ---------- THE ONE SHAPE — the Aeon Forge move applied to geometry.
|
| 209 |
+
Shape is a PARAMETER VECTOR drawn from the hash, not a branch:
|
| 210 |
+
px cross-section exponent (2 = circle -> 8 = square)
|
| 211 |
+
pv profile exponent (2 = ball -> 8 = prism)
|
| 212 |
+
hy squash ratio (height / size)
|
| 213 |
+
aa · an angular ripple amp · lobes (gears, stars)
|
| 214 |
+
tw torus blend (0 = solid -> 1 = ring)
|
| 215 |
+
One branchless formula: compiles in a blink, and rooms MORPH
|
| 216 |
+
continuously with depth instead of unlocking discrete shapes.
|
| 217 |
+
Conservative distance: p-norms with p>=2 are 1-Lipschitz, mix of
|
| 218 |
+
1-Lipschitz fields is 1-Lipschitz, ripple guarded by its own slope. */
|
| 219 |
+
float shapeSD(vec3 q,float s,float px,float pv,float hy,
|
| 220 |
+
float aa,float an,float tw){
|
| 221 |
+
float cx=pow(pow(abs(q.x)+1e-5,px)+pow(abs(q.z)+1e-5,px),1.0/px);
|
| 222 |
+
float lp=pow(pow(cx,pv)+pow(abs(q.y/hy)+1e-5,pv),1.0/pv);
|
| 223 |
+
float d=(lp-s)*min(hy,1.0);
|
| 224 |
+
if(tw>0.001){
|
| 225 |
+
float dR=length(vec2(length(q.xz)-s*0.72,q.y))-s*0.42;
|
| 226 |
+
d=mix(d,dR,tw);
|
| 227 |
+
}
|
| 228 |
+
if(aa>0.001){
|
| 229 |
+
float rr=length(q.xz);
|
| 230 |
+
float w=clamp(rr/(0.35*s),0.0,1.0); /* fade lobes near the axis:
|
| 231 |
+
kills the 1/r slope blow-up */
|
| 232 |
+
d-=s*aa*w*cos(an*atan(q.z,q.x+1e-6));
|
| 233 |
+
}
|
| 234 |
+
return d/(1.0+aa*(an+1.0)*2.9); /* true slope bound of the faded ripple */
|
|
|
|
|
|
|
| 235 |
}
|
| 236 |
float roomSD(vec3 p,int r){
|
| 237 |
vec3 q=p-roomPos[r];
|
| 238 |
float tw=roomMod[r].x;
|
| 239 |
if(abs(tw)>0.0005) q.xz=rot2(q.xz,q.y*tw);
|
| 240 |
+
float d=shapeSD(q,roomPar[r].x,roomPar[r].y,roomPar[r].z,roomPar[r].w,
|
| 241 |
+
roomShp[r].x,roomShp[r].y,roomShp[r].z);
|
| 242 |
float ra=roomMod[r].y;
|
| 243 |
if(ra>0.001) d+=ra*sin(q.x*roomMod[r].z)*sin(q.y*roomMod[r].z)*sin(q.z*roomMod[r].z);
|
| 244 |
+
return d/(1.0+abs(tw)*roomPar[r].x*0.6+ra*roomMod[r].z); /* Lipschitz guard */
|
| 245 |
}
|
| 246 |
float worldSD(vec3 p){
|
| 247 |
float d=min(roomSD(p,0),min(roomSD(p,1),roomSD(p,2)));
|
|
|
|
| 257 |
e.xyy*worldSD(p+e.xyy)+e.yyx*worldSD(p+e.yyx)+
|
| 258 |
e.yxy*worldSD(p+e.yxy)+e.xxx*worldSD(p+e.xxx));}
|
| 259 |
|
| 260 |
+
/* ---------- moons wear their room's shape — the SAME kernel,
|
| 261 |
+
shrunk and rounded into optical glass. no tables, no branches. ---------- */
|
|
|
|
|
|
|
|
|
|
|
|
|
| 262 |
float moonSD(vec3 p,int k){
|
| 263 |
vec3 q=p-wells[k].xyz;
|
| 264 |
float s=wells[k].w;
|
| 265 |
float bs=length(q)-s*2.2;
|
| 266 |
if(bs>0.6) return bs;
|
| 267 |
+
q.xz=rot2(q.xz,wellPar[k].x);
|
| 268 |
+
q.xy=rot2(q.xy,wellPar[k].y);
|
| 269 |
+
int ri=int(wellPar[k].z+0.5);
|
| 270 |
+
return shapeSD(q,s*0.62,roomPar[ri].y,roomPar[ri].z,roomPar[ri].w,
|
| 271 |
+
roomShp[ri].x,roomShp[ri].y,roomShp[ri].z)-s*0.10;
|
| 272 |
}
|
| 273 |
vec3 moonNormal(vec3 p,int k){
|
| 274 |
const vec2 e=vec2(0.012,-0.012);
|
|
|
|
| 297 |
vec3 gA0=mix(roomPos[0],roomPos[1],0.28), gB0=mix(roomPos[0],roomPos[1],0.72);
|
| 298 |
vec3 gA1=mix(roomPos[1],roomPos[2],0.28), gB1=mix(roomPos[1],roomPos[2],0.72);
|
| 299 |
|
| 300 |
+
for(int i=0;i<marchN;i++){
|
| 301 |
/* lensing: deflection per path length, dtheta ~ gradPhi * ds */
|
| 302 |
if(lensK>0.001){
|
| 303 |
vec3 pull=vec3(0.0);
|
|
|
|
| 349 |
L+=wells[k].w*mGain/(length(x-wells[k].xyz)+1.0);}
|
| 350 |
col=tint*eqSample(radial)*(0.55+0.7*min(L,1.4)); done=true; break;
|
| 351 |
}
|
| 352 |
+
if(mode==2){
|
| 353 |
+
/* CASCADE: the inspiration.html magic, verbatim in spirit.
|
| 354 |
+
The ray NEVER enters the crystal. It deposits a translucent
|
| 355 |
+
layer of the boundary image (hologram accumulation), then
|
| 356 |
+
bounces off the sphere-blended normal and keeps flying —
|
| 357 |
+
moon to wall to moon. Recursion is capped at 4 like the
|
| 358 |
+
original: it LOOKS endless because the lensing fisheye
|
| 359 |
+
compounds every bounce. Cheap, so it can never TDR. */
|
| 360 |
+
accum+=tint*eqSample(radial)*0.5;
|
| 361 |
+
rd=reflect(rd,opticN);
|
| 362 |
+
tint*=0.62; bnc++;
|
| 363 |
+
if(bnc>4){col=accum;done=true;break;}
|
| 364 |
+
for(int j=0;j<escN;j++){ if(moonSD(x,hb)>0.05)break; x+=n*0.05; }
|
| 365 |
+
x+=rd*0.04; trav+=0.08; stp=0.05; continue;
|
| 366 |
+
}
|
| 367 |
+
if(mode==0){
|
| 368 |
+
/* BOUNDARY: the rajapinta_box move — moons are curved mirrors.
|
| 369 |
+
gravity already bent the ray on the way in; the surface just
|
| 370 |
+
reflects the boundary. your face drags where the moon passes. */
|
| 371 |
+
rd=reflect(rd,opticN);
|
| 372 |
+
tint*=0.82; bnc++;
|
| 373 |
+
if(bnc>4){col=vec3(0.0);done=true;break;}
|
| 374 |
+
for(int j=0;j<escN;j++){ if(moonSD(x,hb)>0.05)break; x+=n*0.05; }
|
| 375 |
+
x+=rd*0.04; trav+=0.08; stp=0.05; continue;
|
| 376 |
+
}
|
| 377 |
+
/* GLASS (mode 3): heavy optical crystal — the one bulk-transport
|
| 378 |
+
mode, kept as the honest-physics exhibit. Snell in, marched
|
| 379 |
+
chord, Beer-Lambert tint, Snell out. Affordable ONLY because
|
| 380 |
+
Glass rays terminate at the first (image) wall; in the
|
| 381 |
+
mirror-wall modes this march is what caused the TDR. */
|
| 382 |
glow+=tint*eqSample(reflect(rd,opticN))*F*0.55; /* mirror glint, Fresnel-weighted */
|
| 383 |
+
bnc++; if(bnc>MAXB){col=vec3(0.0);done=true;break;}
|
|
|
|
| 384 |
const float IOR=1.45;
|
| 385 |
vec3 d=refract(rd,opticN,1.0/IOR);
|
| 386 |
if(dot(d,d)<1e-4) d=reflect(rd,opticN); /* entry-grazing guard */
|
| 387 |
x-=n*0.04; /* step inside the glass */
|
| 388 |
float chord=0.0;
|
| 389 |
+
for(int j=0;j<glassN;j++){
|
| 390 |
float sd=moonSD(x,hb);
|
| 391 |
if(sd>-0.006){ /* reached the far surface */
|
| 392 |
vec3 ne=moonNormal(x,hb);
|
|
|
|
| 407 |
tint*=exp(-(vec3(1.0)-wellCol[hb])*chord*0.35)*0.96;
|
| 408 |
rd=d;
|
| 409 |
vec3 ne2=moonNormal(x,hb);
|
| 410 |
+
for(int j=0;j<escN;j++){ if(moonSD(x,hb)>0.05)break; x+=ne2*0.05; }
|
| 411 |
x+=rd*0.04; trav+=chord+0.08; stp=0.05; continue;
|
| 412 |
}
|
| 413 |
if(dw<0.03){
|
| 414 |
+
if(mode==0||mode==3){ /* BOUNDARY & GLASS: walls are the image */
|
| 415 |
vec3 wc=wallImg(x);
|
| 416 |
float L=0.0; for(int k=0;k<nWell;k++){
|
| 417 |
L+=wells[k].w*mGain/(length(x-wells[k].xyz)+1.0);}
|
|
|
|
| 420 |
} else { /* PROJECTION / CASCADE: walls mirror */
|
| 421 |
vec3 n=wallNormal(x);
|
| 422 |
rd=reflect(rd,n); tint*=0.85; bnc++;
|
| 423 |
+
if(bnc>4){ col=(mode==2)?accum:vec3(0.0); done=true; break; } /* inspiration's maxBnc=4 */
|
| 424 |
+
for(int j=0;j<escN;j++){ if(-worldSD(x)>0.05)break; x+=n*0.05; }
|
| 425 |
x+=rd*0.04; stp=0.05; continue;
|
| 426 |
}
|
| 427 |
}
|
|
|
|
| 501 |
t^=t+Math.imul(t^(t>>>7),t|61);
|
| 502 |
return ((t^(t>>>14))>>>0)/4294967296;
|
| 503 |
}
|
| 504 |
+
/* shape is continuous now — names are nearest archetypes, for the HUD */
|
| 505 |
+
function shapeName(d){
|
| 506 |
+
if(d.tw>0.30)return 'RING';
|
| 507 |
+
if(d.aa>0.05)return (d.an>=8?'GEAR-':'STAR-')+d.an;
|
| 508 |
+
const bx=d.px>4.5, pr=d.pv>4.5;
|
| 509 |
+
if(bx&&pr)return 'CUBE';
|
| 510 |
+
if(!bx&&pr)return 'CYLINDER';
|
| 511 |
+
if(bx&&!pr)return 'CUSHION';
|
| 512 |
+
if(d.hy<0.62)return 'DOME';
|
| 513 |
+
return 'ORB';
|
| 514 |
+
}
|
| 515 |
const SYL=['KA','RA','JA','PIN','TA','VUO','LU','MI','SA','TÄH','TI','KI','VI','HEL','ME','NU','SO','LO'];
|
| 516 |
|
| 517 |
/* ------- level geometry: depth(L) rooms per level, cumulative starts ------- */
|
|
|
|
| 531 |
|
| 532 |
function roomDef(i){
|
| 533 |
const L=levelOfRoom(i);
|
| 534 |
+
/* the Aeon Forge move: depth widens the PARAMETER RANGES of one kernel —
|
| 535 |
+
rooms morph continuously with the descent instead of unlocking shapes */
|
| 536 |
+
const spread=Math.min(1.2+L*0.8,6.0);
|
| 537 |
+
const px=2.0+rng(i,1)*spread; /* 2 circle -> 8 square */
|
| 538 |
+
const pv=2.0+rng(i,4)*spread; /* 2 ball -> 8 prism */
|
| 539 |
const size=7.0+rng(i,2)*4.0+Math.min(L*0.15,3.0);
|
| 540 |
const height=4.5+rng(i,3)*2.5;
|
| 541 |
+
const hy=Math.min(Math.max(height/size,0.45),1.0);
|
| 542 |
+
const an=4+Math.floor(rng(i,13)*9); /* 4..12 lobes */
|
| 543 |
+
let aa=L>=2?rng(i,12)*rng(i,12)*Math.min(0.02+L*0.013,0.12):0.0; /* lobes amp */
|
| 544 |
+
aa=Math.min(aa,0.5/an); /* slope budget: aa*an <= 0.5 */
|
| 545 |
+
let tw=L>=4?Math.pow(rng(i,14),1.5)*Math.min((L-3)*0.10,0.55):0.0; /* ring blend */
|
| 546 |
const hue=rng(i,5);
|
| 547 |
+
const turn=(rng(i,6)-0.5)*2.4; /* gate heading vs. entry */
|
| 548 |
+
let twist=(rng(i,8)-0.5)*2.0*Math.min(0.015+L*0.011,0.13);
|
| 549 |
+
let ripA=Math.min(Math.max(L-2,0)*0.06,0.45)*rng(i,9);
|
| 550 |
const ripF=0.5+rng(i,10)*1.2;
|
| 551 |
+
/* GUARANTEE the room centre is deep interior (transition fires at -1.0).
|
| 552 |
+
Pure function of the drawn values, so still deterministic per (SEED,i).
|
| 553 |
+
Lobes are zero at the axis (faded), so they don't enter the formula. */
|
| 554 |
+
const cDepth=()=>((-(size*hy)*(1-tw)+size*0.30*tw)
|
| 555 |
+
/(1.0+aa*(an+1.0)*2.9)
|
| 556 |
+
/(1.0+Math.abs(twist)*size*0.6+ripA*ripF));
|
| 557 |
+
for(let g=0;g<10 && cDepth()>-1.3;g++){
|
| 558 |
+
tw*=0.7; aa*=0.7;
|
| 559 |
+
if(g>2){twist*=0.75; ripA*=0.75;}
|
| 560 |
+
}
|
| 561 |
const massK=Math.min(0.7+rng(i,11)*0.9+L*0.04,2.8);
|
| 562 |
let name='';const nc=2+Math.floor(rng(i,7)*2);
|
| 563 |
for(let k=0;k<nc;k++)name+=SYL[Math.floor(rng(i,20+k)*SYL.length)];
|
| 564 |
const tint=[0.86+0.14*Math.cos(6.283*hue),
|
| 565 |
0.86+0.14*Math.cos(6.283*(hue+0.33)),
|
| 566 |
0.86+0.14*Math.cos(6.283*(hue+0.66))];
|
| 567 |
+
return {px,pv,hy,aa,an,tw,size,height,hue,turn,name,tint,twist,ripA,ripF,massK,level:L};
|
| 568 |
}
|
| 569 |
function moonDef(i,m){ /* moon m of room i — re-dreamed identically every visit */
|
| 570 |
const s=100+m*13;
|
|
|
|
| 600 |
dirOut
|
| 601 |
};
|
| 602 |
$('roomV').textContent=dCur.name;
|
| 603 |
+
$('roomI').textContent='#'+CI+' · '+shapeName(dCur)
|
| 604 |
+
+' · p'+dCur.px.toFixed(1)+'/'+dCur.pv.toFixed(1)
|
| 605 |
+
+(dCur.tw>0.01?' · ring '+dCur.tw.toFixed(2):'');
|
| 606 |
$('massV').textContent=dCur.massK.toFixed(2)+' · '+(dCur.twist>=0?'+':'')+dCur.twist.toFixed(3);
|
| 607 |
updateLevelHud();
|
| 608 |
}
|
|
|
|
| 645 |
}
|
| 646 |
|
| 647 |
/* ---------------- JS twins of the SDFs (collision + transitions) ---------- */
|
| 648 |
+
/* the SAME kernel, mirrored exactly for collision and gate transitions */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 649 |
function shapeSDJ(x,y,z,d){
|
| 650 |
+
const s=d.size;
|
| 651 |
+
const cx=Math.pow(Math.pow(Math.abs(x)+1e-5,d.px)+Math.pow(Math.abs(z)+1e-5,d.px),1/d.px);
|
| 652 |
+
const lp=Math.pow(Math.pow(cx,d.pv)+Math.pow(Math.abs(y/d.hy)+1e-5,d.pv),1/d.pv);
|
| 653 |
+
let sd=(lp-s)*Math.min(d.hy,1.0);
|
| 654 |
+
if(d.tw>0.001){
|
| 655 |
+
const dR=Math.hypot(Math.hypot(x,z)-s*0.72,y)-s*0.42;
|
| 656 |
+
sd=sd+(dR-sd)*d.tw;
|
| 657 |
+
}
|
| 658 |
+
if(d.aa>0.001){
|
| 659 |
+
const rr=Math.hypot(x,z);
|
| 660 |
+
const w=Math.min(Math.max(rr/(0.35*s),0),1);
|
| 661 |
+
sd-=s*d.aa*w*Math.cos(d.an*Math.atan2(z,x+1e-6));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 662 |
}
|
| 663 |
+
return sd/(1.0+d.aa*(d.an+1.0)*2.9);
|
| 664 |
}
|
| 665 |
function roomSDJ(px,py,pz,r){
|
| 666 |
const d=R.def[r],c=R.pos[r];
|
|
|
|
| 685 |
const wellsF=new Float32Array(MAXW*4), colsF=new Float32Array(MAXW*3),
|
| 686 |
parF=new Float32Array(MAXW*4),
|
| 687 |
roomPosF=new Float32Array(9), roomParF=new Float32Array(12),
|
| 688 |
+
roomModF=new Float32Array(12), roomShpF=new Float32Array(12),
|
| 689 |
+
roomTintF=new Float32Array(9);
|
| 690 |
function moonCol(h){
|
| 691 |
return[0.55+0.45*Math.cos(6.283*h),0.55+0.45*Math.cos(6.283*(h+0.33)),0.55+0.45*Math.cos(6.283*(h+0.66))];}
|
| 692 |
let keyWell=-1, keyPos=null, keyR=0;
|
|
|
|
| 697 |
for(let r=0;r<3;r++){
|
| 698 |
const d=R.def[r],c=R.pos[r];
|
| 699 |
roomPosF[r*3]=c[0];roomPosF[r*3+1]=c[1];roomPosF[r*3+2]=c[2];
|
| 700 |
+
roomParF[r*4]=d.size;roomParF[r*4+1]=d.px;roomParF[r*4+2]=d.pv;roomParF[r*4+3]=d.hy;
|
| 701 |
roomModF[r*4]=d.twist;roomModF[r*4+1]=d.ripA;roomModF[r*4+2]=d.ripF;roomModF[r*4+3]=d.massK;
|
| 702 |
+
roomShpF[r*4]=d.aa;roomShpF[r*4+1]=d.an;roomShpF[r*4+2]=d.tw;roomShpF[r*4+3]=0;
|
| 703 |
roomTintF[r*3]=d.tint[0];roomTintF[r*3+1]=d.tint[1];roomTintF[r*3+2]=d.tint[2];
|
| 704 |
const A=d.size*0.62, Ay=d.height*0.55;
|
| 705 |
for(let m=0;m<nM && w<MAXW;m++,w++){
|
|
|
|
| 717 |
wellsF[w*4+3]=size;
|
| 718 |
const cc=isKey?[1.0,0.84,0.36]:moonCol(md.hue);
|
| 719 |
colsF[w*3]=cc[0];colsF[w*3+1]=cc[1];colsF[w*3+2]=cc[2];
|
| 720 |
+
parF[w*4]=md.ra*t+md.pa;parF[w*4+1]=md.rb*t+md.pb;parF[w*4+2]=r;parF[w*4+3]=0;
|
| 721 |
if(isKey){keyWell=w;keyPos=[wellsF[w*4],wellsF[w*4+1],wellsF[w*4+2]];keyR=size;}
|
| 722 |
}
|
| 723 |
}
|
|
|
|
| 834 |
|
| 835 |
/* ---------------- controls ---------------- */
|
| 836 |
const MODES=[
|
| 837 |
+
{name:'Boundary', note:'the walls are your camera; the moons are curved mirrors that lens and reflect them — your face drags where a moon passes.'},
|
| 838 |
{name:'Projection', note:'the moons carry your face; every wall is a mirror. the image emanates from the bulk — the holographic swap.'},
|
| 839 |
+
{name:'Cascade', note:'every crystal wears your face and mirrors its neighbours — worlds inside worlds, moon to wall to moon, built entirely from you.'},
|
| 840 |
+
{name:'Glass', note:'heavy optics: the ray refracts through each crystal (Snell + Beer-Lambert) and keeps flying — the honest-physics exhibit.'},
|
| 841 |
];
|
| 842 |
function setMode(mi){
|
| 843 |
mode=mi;
|
| 844 |
+
['m0','m1','m2','m3'].forEach((id,i)=>$(id).classList.toggle('on',i===mi));
|
| 845 |
$('modeV').textContent=MODES[mi].name;
|
| 846 |
$('modeNote').textContent=MODES[mi].note;
|
| 847 |
}
|
|
|
|
| 853 |
$('m0').onclick=()=>setMode(0);
|
| 854 |
$('m1').onclick=()=>setMode(1);
|
| 855 |
$('m2').onclick=()=>setMode(2);
|
| 856 |
+
$('m3').onclick=()=>setMode(3);
|
| 857 |
$('bSrc').onclick=()=>{useCam=!useCam;setSrcLabel();};
|
| 858 |
$('bSeed').onclick=()=>{ SEED=(Math.random()*4294967296)>>>0;
|
| 859 |
LEVEL=1;maxReached=1;keyFound=false;CI=0;
|
|
|
|
| 921 |
}
|
| 922 |
gl.useProgram(prog);
|
| 923 |
['iRes','iTime','camP','camR','camU','camF','nWell','mode','keyWell','mGain','lensK','camTex',
|
| 924 |
+
'marchN','glassN','escN',
|
| 925 |
+
'wells','wellCol','wellPar','roomPos','roomPar','roomMod','roomShp','roomTintU','seal']
|
| 926 |
.forEach(k=>U[k]=gl.getUniformLocation(prog,k));
|
| 927 |
gl.uniform1i(U.camTex,0);
|
| 928 |
+
/* loop bounds — same values as the old constants, now opaque to the compiler */
|
| 929 |
+
gl.uniform1i(U.marchN,180);
|
| 930 |
+
gl.uniform1i(U.glassN,26);
|
| 931 |
+
gl.uniform1i(U.escN,10);
|
| 932 |
$('bootStat').textContent='ready — click to enter';
|
| 933 |
requestAnimationFrame(loop);
|
| 934 |
}
|
|
|
|
| 993 |
gl.uniform3fv(U.roomPos,roomPosF);
|
| 994 |
gl.uniform4fv(U.roomPar,roomParF);
|
| 995 |
gl.uniform4fv(U.roomMod,roomModF);
|
| 996 |
+
gl.uniform4fv(U.roomShp,roomShpF);
|
| 997 |
gl.uniform3fv(U.roomTintU,roomTintF);
|
| 998 |
gl.uniform4f(U.seal,sealV[0],sealV[1],sealV[2],sealV[3]);
|
| 999 |
gl.drawArrays(gl.TRIANGLES,0,3);
|