Static site
Browse files- index.html +2 -2
- static/main.js +2 -1
- static/npc.js +20 -18
- static/world.js +2 -2
index.html
CHANGED
|
@@ -8,7 +8,7 @@
|
|
| 8 |
<meta name="theme-color" content="#10131b">
|
| 9 |
<link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
| 10 |
<link href="https://fonts.googleapis.com/css2?family=Unbounded:wght@500;800;900&family=Inter:wght@400;500;600;700&family=Noto+Kufi+Arabic:wght@700&display=swap" rel="stylesheet">
|
| 11 |
-
<link rel="stylesheet" href="/static/style.css?v=
|
| 12 |
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 viewBox=%270 0 64 64%27%3E%3Crect width=%2764%27 height=%2764%27 rx=%2714%27 fill=%27%23050506%27/%3E%3Ccircle cx=%2732%27 cy=%2732%27 r=%2716%27 fill=%27none%27 stroke=%27%23fff%27 stroke-width=%275%27/%3E%3C/svg%3E">
|
| 13 |
<script type="importmap">
|
| 14 |
{ "imports": {
|
|
@@ -202,6 +202,6 @@
|
|
| 202 |
<div id="toast" role="status"></div>
|
| 203 |
<noscript><p style="position:fixed;inset:auto 0 0;padding:16px;background:#111;color:#fff;text-align:center">VOID needs JavaScript for the 3D car.</p></noscript>
|
| 204 |
<script src="/static/hub.js" data-app="void" defer></script>
|
| 205 |
-
<script type="module" src="/static/main.js?v=
|
| 206 |
</body>
|
| 207 |
</html>
|
|
|
|
| 8 |
<meta name="theme-color" content="#10131b">
|
| 9 |
<link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
| 10 |
<link href="https://fonts.googleapis.com/css2?family=Unbounded:wght@500;800;900&family=Inter:wght@400;500;600;700&family=Noto+Kufi+Arabic:wght@700&display=swap" rel="stylesheet">
|
| 11 |
+
<link rel="stylesheet" href="/static/style.css?v=1792935345">
|
| 12 |
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 viewBox=%270 0 64 64%27%3E%3Crect width=%2764%27 height=%2764%27 rx=%2714%27 fill=%27%23050506%27/%3E%3Ccircle cx=%2732%27 cy=%2732%27 r=%2716%27 fill=%27none%27 stroke=%27%23fff%27 stroke-width=%275%27/%3E%3C/svg%3E">
|
| 13 |
<script type="importmap">
|
| 14 |
{ "imports": {
|
|
|
|
| 202 |
<div id="toast" role="status"></div>
|
| 203 |
<noscript><p style="position:fixed;inset:auto 0 0;padding:16px;background:#111;color:#fff;text-align:center">VOID needs JavaScript for the 3D car.</p></noscript>
|
| 204 |
<script src="/static/hub.js" data-app="void" defer></script>
|
| 205 |
+
<script type="module" src="/static/main.js?v=1792935345"></script>
|
| 206 |
</body>
|
| 207 |
</html>
|
static/main.js
CHANGED
|
@@ -345,7 +345,7 @@ function drawGauge(S) {
|
|
| 345 |
if (S.nitro > .1) { gx.font = `800 ${W2 * .05}px Inter, sans-serif`; gx.fillStyle = "#3df5ff"; gx.fillText("NITRO", c, c - W2 * .16); }
|
| 346 |
}
|
| 347 |
let wasWrecked = false, wreckPose = null; const wrecks = [];
|
| 348 |
-
let fpsNow = 0;
|
| 349 |
const perf = { t: 0, n: 0, pr: renderer.getPixelRatio(), max: renderer.getPixelRatio(), down: 0 };
|
| 350 |
applySettings();
|
| 351 |
let lastHud = 0, prevGear = 1, feedFlip = 0;
|
|
@@ -509,6 +509,7 @@ function frameLoop(fixed) {
|
|
| 509 |
prevGear = S.gear;
|
| 510 |
sound.car({ rpm: S.rpm, load: S.load, engine: mode === "build" || mode === "home" ? .4 : S.engine, thrust: S.thrust.power, speed: S.speed, inside: S.inside });
|
| 511 |
|
|
|
|
| 512 |
composer.render();
|
| 513 |
}
|
| 514 |
|
|
|
|
| 345 |
if (S.nitro > .1) { gx.font = `800 ${W2 * .05}px Inter, sans-serif`; gx.fillStyle = "#3df5ff"; gx.fillText("NITRO", c, c - W2 * .16); }
|
| 346 |
}
|
| 347 |
let wasWrecked = false, wreckPose = null; const wrecks = [];
|
| 348 |
+
let fpsNow = 0, shadowN = 0;
|
| 349 |
const perf = { t: 0, n: 0, pr: renderer.getPixelRatio(), max: renderer.getPixelRatio(), down: 0 };
|
| 350 |
applySettings();
|
| 351 |
let lastHud = 0, prevGear = 1, feedFlip = 0;
|
|
|
|
| 509 |
prevGear = S.gear;
|
| 510 |
sound.car({ rpm: S.rpm, load: S.load, engine: mode === "build" || mode === "home" ? .4 : S.engine, thrust: S.thrust.power, speed: S.speed, inside: S.inside });
|
| 511 |
|
| 512 |
+
if (++shadowN % ((W.QUALITY[W.quality] || {}).shEvery || 3) === 0) renderer.shadowMap.needsUpdate = true;
|
| 513 |
composer.render();
|
| 514 |
}
|
| 515 |
|
static/npc.js
CHANGED
|
@@ -167,7 +167,8 @@ export function createLife(scene, roads, walkways, COARSE) {
|
|
| 167 |
// ---- per frame ----
|
| 168 |
const legQ = new THREE.Quaternion(), legM = new THREE.Matrix4(), legT = new THREE.Matrix4(), Z = new THREE.Vector3(0, 0, 1), squash = new THREE.Vector3(), m4 = new THREE.Matrix4(), q = new THREE.Quaternion(), e = new THREE.Euler(), Y = new THREE.Vector3(0, 1, 0), pos = new THREE.Vector3(), one = new THREE.Vector3(1, 1, 1), zero = new THREE.Vector3();
|
| 169 |
const buckets = new Map(), tmp = {}, cell = new Map(), CELL = 30;
|
| 170 |
-
let frameN = 0;
|
|
|
|
| 171 |
function decide(c, k) { // at the crossing k: straight on, or turn onto the cross road
|
| 172 |
const axis = c.axis, ok = segOK(axis, c.road, c.dir > 0 ? k : k - 1), oAxis = 1 - axis, oRoad = k;
|
| 173 |
const opts = [];
|
|
@@ -187,7 +188,7 @@ export function createLife(scene, roads, walkways, COARSE) {
|
|
| 187 |
function smash(c, vx, vz, power) {
|
| 188 |
if (power < 14 && !c.wreck) {
|
| 189 |
c.dent = Math.min(1, (c.dent || 0) + .45); c.stop = 5 + rnd() * 3; c.v = 0; c.col.multiplyScalar(.82); c.yaw += (rnd() - .5) * .5; c.x += vx * .05; c.z += vz * .05;
|
| 190 |
-
for (let k = 0; k < 4; k++) { const di = debI++ % DEB, d = deb[di]; d.life = 1.5; d.p.set(c.x, .8, c.z); d.v.set((rnd() - .5) * 6, 2 + rnd() * 3, (rnd() - .5) * 6); d.w.set(rnd() * 8, rnd() * 8, rnd() * 8); debris.setColorAt(di, col.setHex(c.color)); }
|
| 191 |
if (debris.instanceColor) debris.instanceColor.needsUpdate = true;
|
| 192 |
return;
|
| 193 |
}
|
|
@@ -197,13 +198,13 @@ export function createLife(scene, roads, walkways, COARSE) {
|
|
| 197 |
: { t: 0, vx: vx * .45, vz: vz * .45, vy: 0, wx: 0, wy: (rnd() - .5) * 3, wz: 0 };
|
| 198 |
c.y = 0; c.rx = 0; c.rz = 0; c.v = 0; c.turn = null;
|
| 199 |
c.col.setHex(c.color).multiplyScalar(fly ? .28 : .45);
|
| 200 |
-
for (let k = 0; k < (fly ? 14 : 6); k++) {
|
| 201 |
const di = debI++ % DEB, d = deb[di]; d.life = 3 + rnd() * 2; d.p.set(c.x, 1, c.z); d.v.set(vx * .6 + (rnd() - .5) * 14, 3 + rnd() * 9, vz * .6 + (rnd() - .5) * 14); d.w.set(rnd() * 12, rnd() * 12, rnd() * 12); d.c = k % 3 ? c.color : 0x222222;
|
| 202 |
debris.setColorAt(di, col.setHex(d.c));
|
| 203 |
}
|
| 204 |
if (debris.instanceColor) debris.instanceColor.needsUpdate = true;
|
| 205 |
}
|
| 206 |
-
const near = (x, z) => cell.get(Math.floor(x / CELL) * 7919 + Math.floor(z / CELL)) ||
|
| 207 |
|
| 208 |
const api = { detail: 1 };
|
| 209 |
return {
|
|
@@ -215,20 +216,21 @@ export function createLife(scene, roads, walkways, COARSE) {
|
|
| 215 |
update(dt, time, player, camPos) {
|
| 216 |
frameN++;
|
| 217 |
const px = player ? player.x : camPos ? camPos.x : 0, pz = player ? player.z : camPos ? camPos.z : 0;
|
| 218 |
-
buckets.
|
| 219 |
const cx0 = camPos ? camPos.x : px, cz0 = camPos ? camPos.z : pz, alt = camPos ? Math.max(0, camPos.y) : 0;
|
| 220 |
const D = api.detail || 1, RC = Math.min(900, ((COARSE ? 260 : 380) + alt * 2) * D), RC2 = RC * RC, RP = Math.min(260, ((COARSE ? 90 : 140) + alt * .4) * D), RP2 = RP * RP;
|
| 221 |
for (const m of meshes) m.k = 0;
|
| 222 |
-
for (const c of cars) {
|
| 223 |
if (c.wreck || c.turn) continue;
|
| 224 |
const key = c.axis * 100000 + c.road * 1000 + (c.dir > 0 ? 500 : 0) + Math.round(c.lane * 10);
|
| 225 |
let b = buckets.get(key); if (!b) buckets.set(key, b = []); b.push(c);
|
| 226 |
}
|
| 227 |
-
for (const b of buckets.values()) { b.sort((a, d) => a.s * a.dir - d.s * d.dir); for (let k = 0; k < b.length; k++) b[k].lead = b[k + 1] || null; }
|
| 228 |
for (const c of cars) {
|
| 229 |
const far = (Math.abs(c.x - px) > 450 || Math.abs(c.z - pz) > 450) && (Math.abs(c.x - cx0) > RC || Math.abs(c.z - cz0) > RC);
|
| 230 |
-
|
| 231 |
-
|
|
|
|
| 232 |
if (c.wreck) { // a smashed car tumbles, bounces, then lies there smoking until it is towed
|
| 233 |
const w = c.wreck; w.t += h;
|
| 234 |
if (c.y > 0 || w.vy > 0) { w.vy -= 9.81 * h; c.y = Math.max(0, c.y + w.vy * h); c.rx += w.wx * h; c.rz += w.wz * h; c.yaw += w.wy * h; if (c.y === 0) { w.vy = -w.vy * .3; w.wx *= .5; w.wz *= .5; w.wy *= .5; if (Math.abs(w.vy) < 1) w.vy = 0; } }
|
|
@@ -260,21 +262,21 @@ export function createLife(scene, roads, walkways, COARSE) {
|
|
| 260 |
c.v += Math.max(-9 * h, Math.min(3 * h, want - c.v));
|
| 261 |
if (!c.turn) { c.s += c.v * c.dir * h; posOf(c); }
|
| 262 |
}
|
| 263 |
-
const key = Math.floor(c.x / CELL) * 7919 + Math.floor(c.z / CELL); let b = cell.get(key); if (!b) cell.set(key, b = []); b.push(c);
|
| 264 |
-
const lean = c.turn ? .05 : 0;
|
| 265 |
-
e.set(c.rx, -c.yaw, c.rz + lean, "YXZ"); q.setFromEuler(e); m4.compose(pos.set(c.x, c.y, c.z), q, squash.set(1, c.crush || 1 - (c.dent || 0) * .07, 1));
|
| 266 |
const ddx = c.x - cx0, ddz = c.z - cz0;
|
| 267 |
-
if (ddx * ddx + ddz * ddz < RC2) {
|
|
|
|
|
|
|
| 268 |
}
|
| 269 |
for (const m of meshes) { m.body.count = m.lights.count = m.k; m.body.instanceMatrix.needsUpdate = m.lights.instanceMatrix.needsUpdate = true; if (m.body.instanceColor) m.body.instanceColor.needsUpdate = true; }
|
| 270 |
-
// debris
|
| 271 |
-
for (let k = 0; k < DEB; k++) {
|
| 272 |
const d = deb[k];
|
| 273 |
-
if (d.life > 0) { d.life -= dt; d.v.y -= 9.81 * dt; d.p.addScaledVector(d.v, dt); if (d.p.y < .1) { d.p.y = .1; d.v.multiplyScalar(.5); d.v.y = Math.abs(d.v.y) * .4; d.w.multiplyScalar(.6); } d.r.x += d.w.x * dt; d.r.y += d.w.y * dt; d.r.z += d.w.z * dt; q.setFromEuler(d.r); m4.compose(d.p, q, one); }
|
| 274 |
else m4.compose(pos.set(0, -50, 0), q, zero);
|
| 275 |
debris.setMatrixAt(k, m4); glassBits.setMatrixAt(k, d.life > 0 ? m4.compose(pos.copy(d.p).add({ x: .3, y: .2, z: -.2 }), q, one) : m4);
|
| 276 |
}
|
| 277 |
-
debris.instanceMatrix.needsUpdate = glassBits.instanceMatrix.needsUpdate = true;
|
| 278 |
// people
|
| 279 |
let pk = 0;
|
| 280 |
for (let n = 0; n < people.length; n++) {
|
|
@@ -307,7 +309,7 @@ export function createLife(scene, roads, walkways, COARSE) {
|
|
| 307 |
e.set(lie * Math.PI / 2 * .95, yaw + (p.fall ? p.fall.rot : 0), 0, "YXZ"); q.setFromEuler(e); m4.compose(pos.set(x, y, z), q, one);
|
| 308 |
bodies.setMatrixAt(pk, m4); heads.setMatrixAt(pk, m4); hair.setMatrixAt(pk, m4);
|
| 309 |
const swing = p.wait > 0 || p.down > 0 ? 0 : Math.sin(walk) * .5;
|
| 310 |
-
for (const [leg, sgn, off] of
|
| 311 |
bodies.setColorAt(pk, p.shirt); heads.setColorAt(pk, p.skin); legL.setColorAt(pk, p.pants); legR.setColorAt(pk, p.pants); hair.setColorAt(pk, p.hairC); pk++;
|
| 312 |
}
|
| 313 |
}
|
|
|
|
| 167 |
// ---- per frame ----
|
| 168 |
const legQ = new THREE.Quaternion(), legM = new THREE.Matrix4(), legT = new THREE.Matrix4(), Z = new THREE.Vector3(0, 0, 1), squash = new THREE.Vector3(), m4 = new THREE.Matrix4(), q = new THREE.Quaternion(), e = new THREE.Euler(), Y = new THREE.Vector3(0, 1, 0), pos = new THREE.Vector3(), one = new THREE.Vector3(1, 1, 1), zero = new THREE.Vector3();
|
| 169 |
const buckets = new Map(), tmp = {}, cell = new Map(), CELL = 30;
|
| 170 |
+
let frameN = 0, debLive = 1;
|
| 171 |
+
const LEGS = [[null, 1, .1], [null, -1, -.1]];
|
| 172 |
function decide(c, k) { // at the crossing k: straight on, or turn onto the cross road
|
| 173 |
const axis = c.axis, ok = segOK(axis, c.road, c.dir > 0 ? k : k - 1), oAxis = 1 - axis, oRoad = k;
|
| 174 |
const opts = [];
|
|
|
|
| 188 |
function smash(c, vx, vz, power) {
|
| 189 |
if (power < 14 && !c.wreck) {
|
| 190 |
c.dent = Math.min(1, (c.dent || 0) + .45); c.stop = 5 + rnd() * 3; c.v = 0; c.col.multiplyScalar(.82); c.yaw += (rnd() - .5) * .5; c.x += vx * .05; c.z += vz * .05;
|
| 191 |
+
debLive = 1; for (let k = 0; k < 4; k++) { const di = debI++ % DEB, d = deb[di]; d.life = 1.5; d.p.set(c.x, .8, c.z); d.v.set((rnd() - .5) * 6, 2 + rnd() * 3, (rnd() - .5) * 6); d.w.set(rnd() * 8, rnd() * 8, rnd() * 8); debris.setColorAt(di, col.setHex(c.color)); }
|
| 192 |
if (debris.instanceColor) debris.instanceColor.needsUpdate = true;
|
| 193 |
return;
|
| 194 |
}
|
|
|
|
| 198 |
: { t: 0, vx: vx * .45, vz: vz * .45, vy: 0, wx: 0, wy: (rnd() - .5) * 3, wz: 0 };
|
| 199 |
c.y = 0; c.rx = 0; c.rz = 0; c.v = 0; c.turn = null;
|
| 200 |
c.col.setHex(c.color).multiplyScalar(fly ? .28 : .45);
|
| 201 |
+
debLive = 1; for (let k = 0; k < (fly ? 14 : 6); k++) {
|
| 202 |
const di = debI++ % DEB, d = deb[di]; d.life = 3 + rnd() * 2; d.p.set(c.x, 1, c.z); d.v.set(vx * .6 + (rnd() - .5) * 14, 3 + rnd() * 9, vz * .6 + (rnd() - .5) * 14); d.w.set(rnd() * 12, rnd() * 12, rnd() * 12); d.c = k % 3 ? c.color : 0x222222;
|
| 203 |
debris.setColorAt(di, col.setHex(d.c));
|
| 204 |
}
|
| 205 |
if (debris.instanceColor) debris.instanceColor.needsUpdate = true;
|
| 206 |
}
|
| 207 |
+
const NONE = [], near = (x, z) => cell.get(Math.floor(x / CELL) * 7919 + Math.floor(z / CELL)) || NONE;
|
| 208 |
|
| 209 |
const api = { detail: 1 };
|
| 210 |
return {
|
|
|
|
| 216 |
update(dt, time, player, camPos) {
|
| 217 |
frameN++;
|
| 218 |
const px = player ? player.x : camPos ? camPos.x : 0, pz = player ? player.z : camPos ? camPos.z : 0;
|
| 219 |
+
const relead = frameN % 4 === 1; if (relead) for (const b of buckets.values()) b.length = 0; for (const b of cell.values()) b.length = 0;
|
| 220 |
const cx0 = camPos ? camPos.x : px, cz0 = camPos ? camPos.z : pz, alt = camPos ? Math.max(0, camPos.y) : 0;
|
| 221 |
const D = api.detail || 1, RC = Math.min(900, ((COARSE ? 260 : 380) + alt * 2) * D), RC2 = RC * RC, RP = Math.min(260, ((COARSE ? 90 : 140) + alt * .4) * D), RP2 = RP * RP;
|
| 222 |
for (const m of meshes) m.k = 0;
|
| 223 |
+
if (relead) for (const c of cars) {
|
| 224 |
if (c.wreck || c.turn) continue;
|
| 225 |
const key = c.axis * 100000 + c.road * 1000 + (c.dir > 0 ? 500 : 0) + Math.round(c.lane * 10);
|
| 226 |
let b = buckets.get(key); if (!b) buckets.set(key, b = []); b.push(c);
|
| 227 |
}
|
| 228 |
+
if (relead) for (const b of buckets.values()) if (b.length) { b.sort((a, d) => a.s * a.dir - d.s * d.dir); for (let k = 0; k < b.length; k++) b[k].lead = b[k + 1] || null; }
|
| 229 |
for (const c of cars) {
|
| 230 |
const far = (Math.abs(c.x - px) > 450 || Math.abs(c.z - pz) > 450) && (Math.abs(c.x - cx0) > RC || Math.abs(c.z - cz0) > RC);
|
| 231 |
+
const vfar = far && (Math.abs(c.x - px) > 1200 || Math.abs(c.z - pz) > 1200), every = vfar ? 15 : 6; // far away nobody sees them: move them in big, rare steps
|
| 232 |
+
if (far && (frameN + c.n) % every) { continue; }
|
| 233 |
+
const h = far ? dt * every : dt;
|
| 234 |
if (c.wreck) { // a smashed car tumbles, bounces, then lies there smoking until it is towed
|
| 235 |
const w = c.wreck; w.t += h;
|
| 236 |
if (c.y > 0 || w.vy > 0) { w.vy -= 9.81 * h; c.y = Math.max(0, c.y + w.vy * h); c.rx += w.wx * h; c.rz += w.wz * h; c.yaw += w.wy * h; if (c.y === 0) { w.vy = -w.vy * .3; w.wx *= .5; w.wz *= .5; w.wy *= .5; if (Math.abs(w.vy) < 1) w.vy = 0; } }
|
|
|
|
| 262 |
c.v += Math.max(-9 * h, Math.min(3 * h, want - c.v));
|
| 263 |
if (!c.turn) { c.s += c.v * c.dir * h; posOf(c); }
|
| 264 |
}
|
| 265 |
+
if (!far) { const key = Math.floor(c.x / CELL) * 7919 + Math.floor(c.z / CELL); let b = cell.get(key); if (!b) cell.set(key, b = []); b.push(c); }
|
|
|
|
|
|
|
| 266 |
const ddx = c.x - cx0, ddz = c.z - cz0;
|
| 267 |
+
if (ddx * ddx + ddz * ddz < RC2) {
|
| 268 |
+
e.set(c.rx, -c.yaw, c.rz + (c.turn ? .05 : 0), "YXZ"); q.setFromEuler(e); m4.compose(pos.set(c.x, c.y, c.z), q, squash.set(1, c.crush || 1 - (c.dent || 0) * .07, 1));
|
| 269 |
+
const M = meshes[c.ti], k = M.k++; M.body.setMatrixAt(k, m4); M.body.setColorAt(k, c.col); M.lights.setMatrixAt(k, c.wreck ? m4.compose(pos, q, zero) : m4); }
|
| 270 |
}
|
| 271 |
for (const m of meshes) { m.body.count = m.lights.count = m.k; m.body.instanceMatrix.needsUpdate = m.lights.instanceMatrix.needsUpdate = true; if (m.body.instanceColor) m.body.instanceColor.needsUpdate = true; }
|
| 272 |
+
// debris (skipped once every piece has landed and been hidden)
|
| 273 |
+
if (debLive) { debLive = 0; for (let k = 0; k < DEB; k++) {
|
| 274 |
const d = deb[k];
|
| 275 |
+
if (d.life > 0) { debLive++; d.life -= dt; d.v.y -= 9.81 * dt; d.p.addScaledVector(d.v, dt); if (d.p.y < .1) { d.p.y = .1; d.v.multiplyScalar(.5); d.v.y = Math.abs(d.v.y) * .4; d.w.multiplyScalar(.6); } d.r.x += d.w.x * dt; d.r.y += d.w.y * dt; d.r.z += d.w.z * dt; q.setFromEuler(d.r); m4.compose(d.p, q, one); }
|
| 276 |
else m4.compose(pos.set(0, -50, 0), q, zero);
|
| 277 |
debris.setMatrixAt(k, m4); glassBits.setMatrixAt(k, d.life > 0 ? m4.compose(pos.copy(d.p).add({ x: .3, y: .2, z: -.2 }), q, one) : m4);
|
| 278 |
}
|
| 279 |
+
debris.instanceMatrix.needsUpdate = glassBits.instanceMatrix.needsUpdate = true; if (debLive) debLive++; else debLive = 0; }
|
| 280 |
// people
|
| 281 |
let pk = 0;
|
| 282 |
for (let n = 0; n < people.length; n++) {
|
|
|
|
| 309 |
e.set(lie * Math.PI / 2 * .95, yaw + (p.fall ? p.fall.rot : 0), 0, "YXZ"); q.setFromEuler(e); m4.compose(pos.set(x, y, z), q, one);
|
| 310 |
bodies.setMatrixAt(pk, m4); heads.setMatrixAt(pk, m4); hair.setMatrixAt(pk, m4);
|
| 311 |
const swing = p.wait > 0 || p.down > 0 ? 0 : Math.sin(walk) * .5;
|
| 312 |
+
LEGS[0][0] = legL; LEGS[1][0] = legR; for (const [leg, sgn, off] of LEGS) { legQ.setFromAxisAngle(Z, swing * sgn); legM.compose(pos.set(0, .86, off), legQ, one); leg.setMatrixAt(pk, legT.copy(m4).multiply(legM)); }
|
| 313 |
bodies.setColorAt(pk, p.shirt); heads.setColorAt(pk, p.skin); legL.setColorAt(pk, p.pants); legR.setColorAt(pk, p.pants); hair.setColorAt(pk, p.hairC); pk++;
|
| 314 |
}
|
| 315 |
}
|
static/world.js
CHANGED
|
@@ -26,7 +26,7 @@ export const PAD_R = 28;
|
|
| 26 |
|
| 27 |
export async function createWorld(canvas, COARSE) {
|
| 28 |
const renderer = new THREE.WebGLRenderer({ canvas, antialias: false, powerPreference: "high-performance", stencil: false });
|
| 29 |
-
renderer.shadowMap.enabled = !COARSE; renderer.shadowMap.type = THREE.PCFShadowMap;
|
| 30 |
renderer.localClippingEnabled = true;
|
| 31 |
renderer.toneMapping = THREE.ACESFilmicToneMapping; renderer.toneMappingExposure = 1.2;
|
| 32 |
renderer.setPixelRatio(Math.min(devicePixelRatio || 1, COARSE ? 1.5 : 1.75));
|
|
@@ -148,7 +148,7 @@ export async function createWorld(canvas, COARSE) {
|
|
| 148 |
captureCity();
|
| 149 |
}
|
| 150 |
// quality: Low / Medium / High / Ultra (the adaptive system steps it down by itself when frames run slow)
|
| 151 |
-
const QUALITY = { low: { pr: .75, shadow: 0, bloom: false, grade: false, msaa: 0, bres: .25 }, medium: { pr: 1, shadow: 1024, bloom: true, grade: true, msaa: 0, bres: .3 }, high: { pr: 1.
|
| 152 |
let quality = "high"; const W0 = { bres: .5 };
|
| 153 |
function setQuality(q) {
|
| 154 |
const Q = QUALITY[q] || QUALITY.high; quality = q;
|
|
|
|
| 26 |
|
| 27 |
export async function createWorld(canvas, COARSE) {
|
| 28 |
const renderer = new THREE.WebGLRenderer({ canvas, antialias: false, powerPreference: "high-performance", stencil: false });
|
| 29 |
+
renderer.shadowMap.enabled = !COARSE; renderer.shadowMap.type = THREE.PCFShadowMap; renderer.shadowMap.autoUpdate = false; // redrawn every few frames (main.js): it was half of every frame
|
| 30 |
renderer.localClippingEnabled = true;
|
| 31 |
renderer.toneMapping = THREE.ACESFilmicToneMapping; renderer.toneMappingExposure = 1.2;
|
| 32 |
renderer.setPixelRatio(Math.min(devicePixelRatio || 1, COARSE ? 1.5 : 1.75));
|
|
|
|
| 148 |
captureCity();
|
| 149 |
}
|
| 150 |
// quality: Low / Medium / High / Ultra (the adaptive system steps it down by itself when frames run slow)
|
| 151 |
+
const QUALITY = { low: { pr: .75, shadow: 0, bloom: false, grade: false, msaa: 0, bres: .25 }, medium: { pr: 1, shadow: 1024, bloom: true, grade: true, msaa: 0, bres: .3, shEvery: 4 }, high: { pr: 1.25, shadow: 2048, bloom: true, grade: true, msaa: 4, bres: .5, shEvery: 3 }, ultra: { pr: 2, shadow: 4096, bloom: true, grade: true, msaa: 4, bres: .5, shEvery: 2 } };
|
| 152 |
let quality = "high"; const W0 = { bres: .5 };
|
| 153 |
function setQuality(q) {
|
| 154 |
const Q = QUALITY[q] || QUALITY.high; quality = q;
|