Spaces:
Running
Running
File size: 10,215 Bytes
b860a9f 3597f5a b860a9f 74b2f87 b860a9f 74b2f87 b860a9f 3597f5a | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 | ;
;
/* ============================================================================
SKYLINE LANDMARKS β large-scale worldbuilding verticals
----------------------------------------------------------------------------
The tallest thing any battlefield owned was an 87-unit derelict block β the
whole "city" sat below the height of one gameplay building's footprint, so
districts read as crates, never as a metropolis a war had hollowed out.
These are true skyscrapers: 260β335 world units, authored at plot scale
(β50-unit footprint, the planner's kind-5 anchor lot), with LIT_WIN /
COOL_WIN strips the night shader already knows how to sell.
Three silhouettes, deliberately distinct at command zoom:
mdlSkyTower1 β stepped brutalist monolith (human worlds)
mdlSkyTower2 β twin spires joined by a high skybridge
mdlSkySpire β an alien crystalline monolith for the foreign worlds:
grown, not built, lit from inside through CRYST facets.
MeshBuilder convention notes (learned the hard way elsewhere): box() takes
the BASE y, not the centre; the primitive patch resolves materials from the
exact palette-constant array passed, so WALL / LIT_WIN / ENERGY carry their
semantics; tri() is NOT patched, so crystalSpike inherits m.mat(MAT.CRYST).
============================================================================ */
/* Vertical window strips proud of each face, floor-banded. LIT_WIN carries
warm interiors, COOL_WIN cold service floors, and an unlit band every fifth
row keeps the tower from reading as a paper lantern. y0/y1 are world bases. */
function skyStrips(m,cx,cz,w,d,y0,y1,step,warm){
const half=w/2, dh=d/2;
let row=0;
for(let y=y0;y<y1-step*0.45;y+=step,row++){
const litC=(row%5===4)?WALL_D:(warm&&(row%2===0)?LIT_WIN:COOL_WIN);
const hh=step*0.44;
for(const sd of [-1,1]){
m.box(cx+sd*(half+0.55), y, cz, 1.1, hh, d*0.60, litC);
m.box(cx, y, cz+sd*(dh+0.55), w*0.60, hh, 1.1, litC);
}
}
}
function mdlSkyTower1(){
const m=MB();
/* Stepped monolith: plinth, three setback tiers, service crown, mast. */
m.box(0, 0,0, 54,12,54, CONC_D); // plinth 0β12
m.box(0, 12,0, 46,10,46, CONC); // podium 12β22
m.box(0, 22,0, 40,96,40, WALL); // tier 1 22β118
skyStrips(m,0,0,40,40,30,112,11,true);
m.box(0,118,0, 44,5,44, ROOFC); // setback ledge
m.box(0,123,0, 32,92,32, WALL_D); // tier 2 123β215
skyStrips(m,0,0,32,32,130,208,11,true);
m.box(0,215,0, 35,5,35, ROOFC);
m.box(0,220,0, 23,74,23, WALL); // tier 3 220β294
skyStrips(m,0,0,23,23,226,288,11,false);
m.box(0,294,0, 26,5,26, ROOF_T); // crown ring
m.box(0,299,0, 13,11,13, MET_D); // plant room
for(const sd of [-1,1]) m.box(sd*8.4,299,0, 2.4,9,2.4, MET_D);
m.cyl(0,310,0, 1.3,0.7, 22, 6, MET_L); // mast 310β332
m.box(0,332,0, 2.2,2.2,2.2, HOT); // aviation beacon
/* Corner piers give the monolith its brutalist read from directly above. */
for(const sx of [-1,1]) for(const sz of [-1,1]){
m.box(sx*20.4,12,sz*20.4, 4.4,204,4.4, CONC_D);
m.box(sx*20.4,216,sz*20.4, 3.4,24,3.4, CONC);
}
return m.build();
}
function mdlSkyTower2(){
const m=MB();
/* Twin spires + high skybridge β a silhouette no gameplay structure has. */
m.box(0,0,0, 56,10,40, CONC_D); // shared plinth
const twr=(cx,H,warm)=>{
m.box(cx,10,0, 20,H,20, WALL);
skyStrips(m,cx,0,20,20,18,10+H-8,10.5,warm);
m.box(cx,10+H,0, 22,5,22, ROOFC);
m.box(cx,15+H,0, 9,8,9, MET_D);
m.cyl(cx,23+H,0, 1.1,0.6, 15, 6, MET_L);
m.box(cx,38+H,0, 1.8,1.8,1.8, HOT); // aviation beacon
};
twr(-16,230,true); twr(16,200,false); // tops 268 / 238
m.box(0,152,0, 14,7,10, WALL_D); // skybridge deck
m.box(0,159,0, 14,2,10, ROOF_T);
skyStrips(m,0,0,12,8,153,158,7,true);
for(const sd of [-1,1]) m.box(sd*6.2,144,0, 1.6,8,1.6, MET_D); // hangers
return m.build();
}
function mdlSkySpire(){
const m=MB(); m.mat(MAT.CRYST);
/* Alien monolith: one dominant crystal blade with satellite spires, grown
through a fractured plinth. Authored near-white β the render tint owns
the world's hue, so one model serves every foreign biome. crystalSpike
emits through tri(), which the material patch leaves alone, so the CRYST
transmission path lights the whole growth from inside. */
const BASE=C(150,140,185), MID=C(210,205,240), TIP=C(255,255,255);
crystalSpike(m, 0.0, 0.0, 11.5, 320, 4.0, 2.2, 0.35, BASE,MID,TIP);
crystalSpike(m, 13.5, 4.5, 6.8, 195, 7.5, 3.5, 1.75, BASE,MID,TIP);
crystalSpike(m,-11.5, 8.0, 5.9, 152, -6.5, 4.5, 0.95, BASE,MID,TIP);
crystalSpike(m, -7.0,-12.5, 5.2, 128, -4.0, -7.0, 2.45, BASE,MID,TIP);
crystalSpike(m, 10.0,-9.5, 4.4, 98, 5.5, -5.5, 3.05, BASE,MID,TIP);
/* Fractured plinth the growth erupted through. */
m.box(0, 0,0, 34,14,34, CONC_D);
m.box(0,14,0, 26,4,26, WALL_D);
for(let k=0;k<6;k++){
const a=k/6*Math.PI*2, r=15.5;
m.box(Math.cos(a)*r, 0, Math.sin(a)*r, 5.5,9,5.5, CONC, a);
}
m.mat(MAT.CRYST);
for(let k=0;k<5;k++){ // shard ring debris
const a=k/5*Math.PI*2+0.5;
crystalSpike(m, Math.cos(a)*21, Math.sin(a)*21, 2.6, 26+((k*13)%18), Math.cos(a)*3, Math.sin(a)*3, k*1.3, BASE,MID,TIP);
}
/* Luminous seams RIDE THE SURFACE of the blade β buried inside the crystal
they were swallowed whole at night. Four ribs lean with the taper, plus
two halo rings and vent glows at the root: the cyan structure lines that
make the monolith read as ALIVE from command zoom after dark. */
for(let k=0;k<4;k++){
const a=k/4*Math.PI*2+0.4;
const rx=Math.cos(a), rz=Math.sin(a);
for(let seg=0;seg<5;seg++){
const y0=18+seg*30, rr2=10.8*(1-(y0+15)/330); // hug the taper
m.box(rx*(rr2+1.3), y0, rz*(rr2+1.3), 2.6, 26, 2.6, ENERGY);
}
}
m.box(0, 186, 0, 12.5, 4.5, 12.5, ENERGY); // high halo rings
m.box(0, 122, 0, 16.5, 3.5, 16.5, ENERGY);
for(let k=0;k<6;k++){ // root vents
const a=k/6*Math.PI*2+0.25;
m.box(Math.cos(a)*13.5, 17, Math.sin(a)*13.5, 3.4, 5, 3.4, ENERGY);
}
return m.build();
}
/* ---------------------------------------------------------------------------
GROUND SKIRT β the piece that makes a structure BELONG to the ground.
A model box meeting graded terrain at a perfect 90 degree line is exactly
what reads as "pasted on": real buildings sit in a berm of spoil, drifted
soil and broken slab. This is a two-band flare drawn per structure, tinted
with the BIOME's ground colour (not the model's), so a tower on arctic ice
is skirted in ice and the same tower on ashland sits in slag.
Built as a rectangle-to-rectangle loop so the instance's cross-axis lane
can stretch it to a footprint's real aspect: scale = width, wide = depth.
Four quads per band covers corners too, because the loop is convex.
--------------------------------------------------------------------------- */
function skirtBand(m,r0,y0,r1,y1,col){
const A=[[-r0,-r0],[r0,-r0],[r0,r0],[-r0,r0]];
const B=[[-r1,-r1],[r1,-r1],[r1,r1],[-r1,r1]];
for(let k=0;k<4;k++){
const j=(k+1)&3;
m.quad([A[k][0],y0,A[k][1]],[A[j][0],y0,A[j][1]],
[B[j][0],y1,B[j][1]],[B[k][0],y1,B[k][1]],col);
}
}
function mdlBaseSkirt(){
const m=MB();
/* Band 1: the structural berm, proud of the wall and steep.
Band 2: the long soil feather that dies into the terrain. */
skirtBand(m, 0.50, 0.075, 0.70,-0.010, CONC_D);
skirtBand(m, 0.70,-0.010, 0.98,-0.055, C(126,120,110));
/* Spoil heaps break the straight contact line. Sizes and offsets are
deliberately uneven β a regular ring of identical lumps reads as a
decoration, an irregular one reads as debris. */
const L=[[0.62,0.18,0.16,0.055],[-0.55,-0.42,0.13,0.042],[0.10,-0.66,0.18,0.048],
[-0.68,0.30,0.11,0.036],[0.48,0.60,0.14,0.040],[-0.22,0.70,0.10,0.030],
[0.72,-0.34,0.12,0.034],[-0.44,0.06,0.09,0.026]];
for(const [lx,lz,r,h] of L) m.box(lx,-0.012,lz, r,h,r*0.82, C(138,132,122), (lx*7+lz*13));
return m.build();
}
/* Partial collapse for the skyline anchors: the podium and a sheared stump of
the first tier, left standing in its own wreckage. Swapped in at half
health so a skyscraper visibly COMES DOWN in stages instead of blinking
out of existence when its bar empties. */
function mdlSkyStump(){
const m=MB();
m.box(0, 0,0, 54,12,54, CONC_D);
m.box(0,12,0, 46,10,46, CONC);
m.box(0,22,0, 40,44,40, WALL); // sheared at 66
skyStrips(m,0,0,40,40,30,58,11,false);
/* A shear is a jagged line, never a clean lid: broken floor plates at
different heights, with rebar stubs standing proud of them. */
const P=[[-12,66,-11,15,5,14],[9,66,10,17,9,15],[-8,66,13,13,3,12],[14,66,-9,11,7,11]];
for(const [x,y,z,w,h,d] of P) m.box(x,y,z, w,h,d, CONC_D);
for(let k=0;k<7;k++){
const a=k/7*Math.PI*2+0.4, r=13+((k*5)%9);
m.box(Math.cos(a)*r, 70, Math.sin(a)*r, 0.9, 5+((k*3)%7), 0.9, MET_D);
}
for(const sx of [-1,1]) for(const sz of [-1,1])
m.box(sx*20.4,12,sz*20.4, 4.4,52,4.4, CONC_D); // piers survive the shear
return m.build();
}
/* REGISTRATION BY TAKEOVER (AGENTS.md): wrap initModels so both the boot path
and the glrecover.js context-loss rebuild pick the skyline up, with zero
edits inside models.js itself. */
const skyBaseInitModels=initModels;
initModels=function(){
skyBaseInitModels();
FX.sky1=new InstMesh(gl,mdlSkyTower1(),48);
FX.sky2=new InstMesh(gl,mdlSkyTower2(),48);
FX.skyA=new InstMesh(gl,mdlSkySpire(),48);
FX.skyS=new InstMesh(gl,mdlSkyStump(),48);
/* Every relic and every player structure can carry a skirt, so the cap is
sized to the whole visible field, not to the skyline. */
FX.skirt=new InstMesh(gl,mdlBaseSkirt(),1200);
};
|