@plasius/gpu-shared 0.1.4 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/dist/{chunk-S5NCFNKJ.js → chunk-OTCJ3VOK.js} +23 -2
- package/dist/{chunk-S5NCFNKJ.js.map → chunk-OTCJ3VOK.js.map} +1 -1
- package/dist/{chunk-UUJLYYQS.js → chunk-QBMXJ3V2.js} +42 -15
- package/dist/chunk-QBMXJ3V2.js.map +1 -0
- package/dist/{gltf-loader-4FNTT63R.js → gltf-loader-LKALCZAV.js} +2 -2
- package/dist/index.cjs +583 -168
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +3 -3
- package/dist/{showcase-runtime-4BS7TWHS.js → showcase-runtime-JZIYGQAU.js} +531 -164
- package/dist/{showcase-runtime-4BS7TWHS.js.map → showcase-runtime-JZIYGQAU.js.map} +1 -1
- package/package.json +2 -2
- package/src/asset-url.js +28 -1
- package/src/gltf-loader.js +36 -1
- package/src/index.d.ts +4 -0
- package/src/showcase-runtime.js +619 -163
- package/dist/chunk-UUJLYYQS.js.map +0 -1
- /package/dist/{gltf-loader-4FNTT63R.js.map → gltf-loader-LKALCZAV.js.map} +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
resolveShowcaseAssetUrl
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-OTCJ3VOK.js";
|
|
4
4
|
import {
|
|
5
5
|
loadGltfModel
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-QBMXJ3V2.js";
|
|
7
7
|
import {
|
|
8
8
|
__require
|
|
9
9
|
} from "./chunk-DGUM43GV.js";
|
|
@@ -4935,7 +4935,7 @@ function createGpuDebugSession(options = {}) {
|
|
|
4935
4935
|
};
|
|
4936
4936
|
}
|
|
4937
4937
|
|
|
4938
|
-
// node_modules/@plasius/gpu-physics/dist/chunk-
|
|
4938
|
+
// node_modules/@plasius/gpu-physics/dist/chunk-PFUNZLNF.js
|
|
4939
4939
|
var physicsDebugOwner = "physics";
|
|
4940
4940
|
var physicsWorkerQueueClasses = Object.freeze({
|
|
4941
4941
|
simulation: "simulation",
|
|
@@ -5763,7 +5763,7 @@ var physicsWorkerDagSpecs = {
|
|
|
5763
5763
|
contactVisuals: { priority: 1, dependencies: ["worldSnapshot"] }
|
|
5764
5764
|
}
|
|
5765
5765
|
};
|
|
5766
|
-
var
|
|
5766
|
+
var physicsSimulationPlans = Object.freeze({
|
|
5767
5767
|
gameplay: Object.freeze({
|
|
5768
5768
|
description: "Gameplay simulation plan that hands off a stable post-commit world snapshot to visual preparation.",
|
|
5769
5769
|
snapshotStageId: "worldSnapshot",
|
|
@@ -6039,7 +6039,7 @@ function getPhysicsWorkerManifest(profile = defaultPhysicsWorkerProfile) {
|
|
|
6039
6039
|
return manifest;
|
|
6040
6040
|
}
|
|
6041
6041
|
function createPhysicsSimulationPlan(profile = defaultPhysicsWorkerProfile) {
|
|
6042
|
-
const plan =
|
|
6042
|
+
const plan = physicsSimulationPlans[profile];
|
|
6043
6043
|
if (!plan) {
|
|
6044
6044
|
const available = physicsWorkerProfileNames.join(", ");
|
|
6045
6045
|
throw new Error(
|
|
@@ -6106,8 +6106,16 @@ function createPhysicsWorldSnapshot(input) {
|
|
|
6106
6106
|
|
|
6107
6107
|
// src/showcase-runtime.js
|
|
6108
6108
|
var STYLE_ID = "plasius-shared-3d-showcase-style";
|
|
6109
|
+
var ROOT_CLASS = "plasius-showcase-root";
|
|
6109
6110
|
var DEFAULT_TITLE = "Flag by the Sea";
|
|
6110
6111
|
var DEFAULT_SUBTITLE = "Shared 3D validation scene using GLTF ships, cloth, fluid continuity, adaptive performance, and telemetry.";
|
|
6112
|
+
var SHIP_SCALE = 1.1;
|
|
6113
|
+
var HARBOR_BOUNDS = Object.freeze({
|
|
6114
|
+
minX: -11.2,
|
|
6115
|
+
maxX: 11.2,
|
|
6116
|
+
minZ: 1.8,
|
|
6117
|
+
maxZ: 17.2
|
|
6118
|
+
});
|
|
6111
6119
|
var CAMERA_PRESETS = Object.freeze({
|
|
6112
6120
|
integrated: Object.freeze({ yaw: -0.55, pitch: 0.34, distance: 27, target: [0, 2.2, 0] }),
|
|
6113
6121
|
lighting: Object.freeze({ yaw: -0.28, pitch: 0.28, distance: 23, target: [0, 2.8, 0] }),
|
|
@@ -6119,10 +6127,10 @@ var CAMERA_PRESETS = Object.freeze({
|
|
|
6119
6127
|
});
|
|
6120
6128
|
var showcaseFocusModes = Object.freeze(Object.keys(CAMERA_PRESETS));
|
|
6121
6129
|
var SCENE_NOTES = Object.freeze([
|
|
6122
|
-
"Ships are loaded from a GLTF asset and carry
|
|
6123
|
-
"
|
|
6124
|
-
"Cloth and fluid continuity stay coherent across near, mid, far, and horizon bands.",
|
|
6125
|
-
"Performance pressure reduces visual detail before authoritative collision motion is touched."
|
|
6130
|
+
"Ships are loaded from a GLTF asset and carry mass, damping, restitution, and hull extents from node extras.",
|
|
6131
|
+
"Moonlight sets the cold ambient read while deck lanterns and harbor torches provide warm local contrast.",
|
|
6132
|
+
"Cloth and fluid continuity stay coherent across near, mid, far, and horizon bands even in the darker night palette.",
|
|
6133
|
+
"Performance pressure reduces visual detail before mass-weighted authoritative collision motion is touched."
|
|
6126
6134
|
]);
|
|
6127
6135
|
var UNIT_BOX_MESH = Object.freeze({
|
|
6128
6136
|
positions: Object.freeze([
|
|
@@ -6190,6 +6198,17 @@ var UNIT_BOX_MESH = Object.freeze({
|
|
|
6190
6198
|
0
|
|
6191
6199
|
])
|
|
6192
6200
|
});
|
|
6201
|
+
var SHIP_LANTERNS = Object.freeze([
|
|
6202
|
+
Object.freeze({ x: 0.94, y: 1.54, z: 2.52, glow: 1 }),
|
|
6203
|
+
Object.freeze({ x: -0.9, y: 1.58, z: 2.44, glow: 0.92 }),
|
|
6204
|
+
Object.freeze({ x: 0.62, y: 1.42, z: -2.18, glow: 0.88 }),
|
|
6205
|
+
Object.freeze({ x: -0.58, y: 1.46, z: -2.04, glow: 0.84 })
|
|
6206
|
+
]);
|
|
6207
|
+
var HARBOR_TORCHES = Object.freeze([
|
|
6208
|
+
Object.freeze({ x: -5.2, y: 1.25, z: 1.36, glow: 1.1 }),
|
|
6209
|
+
Object.freeze({ x: -8.6, y: 2.48, z: -0.72, glow: 1 }),
|
|
6210
|
+
Object.freeze({ x: -10.4, y: 1.28, z: 0.82, glow: 0.92 })
|
|
6211
|
+
]);
|
|
6193
6212
|
function injectStyles() {
|
|
6194
6213
|
if (document.getElementById(STYLE_ID)) {
|
|
6195
6214
|
return;
|
|
@@ -6197,27 +6216,27 @@ function injectStyles() {
|
|
|
6197
6216
|
const style = document.createElement("style");
|
|
6198
6217
|
style.id = STYLE_ID;
|
|
6199
6218
|
style.textContent = `
|
|
6200
|
-
|
|
6201
|
-
color-scheme:
|
|
6202
|
-
--plasius-paper: #
|
|
6203
|
-
--plasius-ink: #
|
|
6204
|
-
--plasius-muted: #
|
|
6205
|
-
--plasius-accent: #
|
|
6206
|
-
--plasius-panel: rgba(
|
|
6207
|
-
--plasius-border: rgba(
|
|
6208
|
-
--plasius-shadow: 0
|
|
6209
|
-
}
|
|
6210
|
-
* {
|
|
6211
|
-
box-sizing: border-box;
|
|
6212
|
-
}
|
|
6213
|
-
body {
|
|
6219
|
+
.${ROOT_CLASS} {
|
|
6220
|
+
color-scheme: dark;
|
|
6221
|
+
--plasius-paper: #081321;
|
|
6222
|
+
--plasius-ink: #edf4ff;
|
|
6223
|
+
--plasius-muted: #b6c5dd;
|
|
6224
|
+
--plasius-accent: #f3b16a;
|
|
6225
|
+
--plasius-panel: rgba(8, 19, 33, 0.72);
|
|
6226
|
+
--plasius-border: rgba(159, 185, 223, 0.18);
|
|
6227
|
+
--plasius-shadow: 0 24px 56px rgba(1, 6, 14, 0.44);
|
|
6214
6228
|
margin: 0;
|
|
6215
|
-
min-height:
|
|
6229
|
+
min-height: 100%;
|
|
6216
6230
|
font-family: "Fraunces", "Iowan Old Style", serif;
|
|
6217
6231
|
color: var(--plasius-ink);
|
|
6218
6232
|
background:
|
|
6219
|
-
radial-gradient(circle at
|
|
6220
|
-
|
|
6233
|
+
radial-gradient(circle at 18% 12%, rgba(73, 101, 170, 0.28), transparent 30%),
|
|
6234
|
+
radial-gradient(circle at 82% 18%, rgba(240, 188, 103, 0.08), transparent 18%),
|
|
6235
|
+
linear-gradient(180deg, #04101d 0%, #0b1930 42%, #081321 100%);
|
|
6236
|
+
}
|
|
6237
|
+
.${ROOT_CLASS},
|
|
6238
|
+
.${ROOT_CLASS} * {
|
|
6239
|
+
box-sizing: border-box;
|
|
6221
6240
|
}
|
|
6222
6241
|
.plasius-demo {
|
|
6223
6242
|
width: min(1560px, calc(100vw - 32px));
|
|
@@ -6251,7 +6270,7 @@ function injectStyles() {
|
|
|
6251
6270
|
text-transform: uppercase;
|
|
6252
6271
|
letter-spacing: 0.18em;
|
|
6253
6272
|
font-size: 12px;
|
|
6254
|
-
color: rgba(
|
|
6273
|
+
color: rgba(226, 236, 255, 0.58);
|
|
6255
6274
|
}
|
|
6256
6275
|
.plasius-demo h1,
|
|
6257
6276
|
.plasius-demo h2,
|
|
@@ -6269,7 +6288,7 @@ function injectStyles() {
|
|
|
6269
6288
|
margin: 0;
|
|
6270
6289
|
padding: 8px 12px;
|
|
6271
6290
|
border-radius: 999px;
|
|
6272
|
-
background: rgba(
|
|
6291
|
+
background: rgba(243, 177, 106, 0.14);
|
|
6273
6292
|
color: var(--plasius-accent);
|
|
6274
6293
|
font-weight: 700;
|
|
6275
6294
|
}
|
|
@@ -6291,8 +6310,8 @@ function injectStyles() {
|
|
|
6291
6310
|
aspect-ratio: 16 / 9;
|
|
6292
6311
|
display: block;
|
|
6293
6312
|
border-radius: 20px;
|
|
6294
|
-
border: 1px solid rgba(
|
|
6295
|
-
background: linear-gradient(180deg, #
|
|
6313
|
+
border: 1px solid rgba(159, 185, 223, 0.12);
|
|
6314
|
+
background: linear-gradient(180deg, #071220 0%, #132440 42%, #10344b 42%, #05111d 100%);
|
|
6296
6315
|
}
|
|
6297
6316
|
.plasius-demo__toolbar {
|
|
6298
6317
|
position: absolute;
|
|
@@ -6312,9 +6331,9 @@ function injectStyles() {
|
|
|
6312
6331
|
.plasius-demo button,
|
|
6313
6332
|
.plasius-demo .plasius-toggle,
|
|
6314
6333
|
.plasius-demo select {
|
|
6315
|
-
border: 1px solid rgba(
|
|
6334
|
+
border: 1px solid rgba(159, 185, 223, 0.18);
|
|
6316
6335
|
border-radius: 999px;
|
|
6317
|
-
background: rgba(
|
|
6336
|
+
background: rgba(9, 20, 34, 0.84);
|
|
6318
6337
|
color: var(--plasius-ink);
|
|
6319
6338
|
padding: 10px 14px;
|
|
6320
6339
|
}
|
|
@@ -6353,8 +6372,8 @@ function injectStyles() {
|
|
|
6353
6372
|
bottom: 24px;
|
|
6354
6373
|
padding: 10px 14px;
|
|
6355
6374
|
border-radius: 16px;
|
|
6356
|
-
background: rgba(
|
|
6357
|
-
border: 1px solid rgba(
|
|
6375
|
+
background: rgba(9, 20, 34, 0.82);
|
|
6376
|
+
border: 1px solid rgba(159, 185, 223, 0.16);
|
|
6358
6377
|
color: var(--plasius-muted);
|
|
6359
6378
|
font-size: 12px;
|
|
6360
6379
|
line-height: 1.45;
|
|
@@ -6366,7 +6385,7 @@ function injectStyles() {
|
|
|
6366
6385
|
}
|
|
6367
6386
|
.plasius-demo__footer {
|
|
6368
6387
|
margin-top: 4px;
|
|
6369
|
-
color: rgba(
|
|
6388
|
+
color: rgba(226, 236, 255, 0.68);
|
|
6370
6389
|
font-size: 13px;
|
|
6371
6390
|
line-height: 1.6;
|
|
6372
6391
|
}
|
|
@@ -6385,6 +6404,10 @@ function clamp(value, min, max) {
|
|
|
6385
6404
|
function mix(a, b, t) {
|
|
6386
6405
|
return a + (b - a) * t;
|
|
6387
6406
|
}
|
|
6407
|
+
function pseudoRandom(seed) {
|
|
6408
|
+
const value = Math.sin(seed * 12.9898 + seed * seed * 17e-4) * 43758.5453;
|
|
6409
|
+
return value - Math.floor(value);
|
|
6410
|
+
}
|
|
6388
6411
|
function vec3(x = 0, y = 0, z = 0) {
|
|
6389
6412
|
return { x, y, z };
|
|
6390
6413
|
}
|
|
@@ -6418,6 +6441,12 @@ function reflectVec3(vector, normal) {
|
|
|
6418
6441
|
const unitNormal = normalizeVec3(normal);
|
|
6419
6442
|
return subVec3(vector, scaleVec3(unitNormal, 2 * dotVec3(vector, unitNormal)));
|
|
6420
6443
|
}
|
|
6444
|
+
function directionFromYaw(yaw) {
|
|
6445
|
+
return normalizeVec3(vec3(Math.sin(yaw), 0, Math.cos(yaw)));
|
|
6446
|
+
}
|
|
6447
|
+
function perpendicularOnWater(direction) {
|
|
6448
|
+
return vec3(-direction.z, 0, direction.x);
|
|
6449
|
+
}
|
|
6421
6450
|
function rotateY(point, angle) {
|
|
6422
6451
|
const cosine = Math.cos(angle);
|
|
6423
6452
|
const sine = Math.sin(angle);
|
|
@@ -6600,7 +6629,7 @@ function buildDemoDom(root, options) {
|
|
|
6600
6629
|
<section class="plasius-panel plasius-demo__status">
|
|
6601
6630
|
<p id="demoStatus" class="plasius-demo__status-badge">Booting 3D scene\u2026</p>
|
|
6602
6631
|
<p id="demoDetails" class="plasius-demo__status-text">
|
|
6603
|
-
Preparing GLTF
|
|
6632
|
+
Preparing a moonlit harbor scene, GLTF hull data, cloth and fluid continuity plans, and adaptive quality metadata.
|
|
6604
6633
|
</p>
|
|
6605
6634
|
</section>
|
|
6606
6635
|
</section>
|
|
@@ -6628,9 +6657,9 @@ function buildDemoDom(root, options) {
|
|
|
6628
6657
|
</div>
|
|
6629
6658
|
<div class="plasius-demo__legend">
|
|
6630
6659
|
<strong>Scene</strong>
|
|
6631
|
-
GLTF ships carry
|
|
6632
|
-
|
|
6633
|
-
|
|
6660
|
+
GLTF ships carry hull mass and damping metadata.<br />
|
|
6661
|
+
Lanterns and torches warm the moonlit harbor.<br />
|
|
6662
|
+
Mass-aware collisions stay authoritative near the camera.
|
|
6634
6663
|
</div>
|
|
6635
6664
|
</section>
|
|
6636
6665
|
<aside class="plasius-demo__sidebar">
|
|
@@ -6711,6 +6740,7 @@ function buildSceneSnapshot(state, shipModel) {
|
|
|
6711
6740
|
})
|
|
6712
6741
|
)
|
|
6713
6742
|
),
|
|
6743
|
+
shipPhysics: shipModel?.physics ?? null,
|
|
6714
6744
|
physics: Object.freeze({
|
|
6715
6745
|
profile: state.physics.profile,
|
|
6716
6746
|
plan: state.physics.plan,
|
|
@@ -6754,28 +6784,39 @@ function readVisualNumber(value, fallback) {
|
|
|
6754
6784
|
function resolveVisualConfig(nearLighting, lightingSnapshot, customVisuals = {}) {
|
|
6755
6785
|
const premiumShadows = nearLighting.primaryShadowSource === "ray-traced-primary";
|
|
6756
6786
|
const defaults = {
|
|
6757
|
-
skyTop: premiumShadows ? "#
|
|
6758
|
-
skyMid: premiumShadows ? "#
|
|
6759
|
-
skyBottom: premiumShadows ? "#
|
|
6760
|
-
|
|
6761
|
-
|
|
6762
|
-
|
|
6763
|
-
|
|
6787
|
+
skyTop: premiumShadows ? "#040c1a" : "#06101f",
|
|
6788
|
+
skyMid: premiumShadows ? "#11203b" : "#152643",
|
|
6789
|
+
skyBottom: premiumShadows ? "#2f4468" : "#364d73",
|
|
6790
|
+
duskGlow: premiumShadows ? "rgba(116, 142, 201, 0.26)" : "rgba(104, 128, 188, 0.22)",
|
|
6791
|
+
seaTop: premiumShadows ? "#102946" : "#153050",
|
|
6792
|
+
seaMid: premiumShadows ? "#0a1d33" : "#0d2138",
|
|
6793
|
+
seaBottom: "#04101d",
|
|
6794
|
+
moonCore: "rgba(241, 246, 255, 0.98)",
|
|
6795
|
+
moonHalo: "rgba(167, 191, 255, 0.24)",
|
|
6796
|
+
moonReflection: "rgba(192, 214, 255, 0.22)",
|
|
6797
|
+
starColor: "rgba(232, 239, 255, 0.82)",
|
|
6798
|
+
ambientMist: "rgba(41, 63, 97, 0.16)",
|
|
6764
6799
|
reflectionStrength: lightingSnapshot.currentLevel.config.reflectionStrength,
|
|
6765
6800
|
shadowAccent: lightingSnapshot.currentLevel.config.shadowStrength,
|
|
6766
|
-
waveAmplitude:
|
|
6767
|
-
waveDirection: { x: 0.
|
|
6768
|
-
wavePhaseSpeed:
|
|
6769
|
-
wakeStrength: 0.
|
|
6770
|
-
wakeLength:
|
|
6771
|
-
collisionRippleStrength: 0.
|
|
6772
|
-
waterNear: { r: 0.
|
|
6773
|
-
waterFar: { r: 0.
|
|
6774
|
-
harborWall: { r: 0.
|
|
6775
|
-
harborDeck: { r: 0.
|
|
6776
|
-
harborTower: { r: 0.
|
|
6777
|
-
flagColor: { r: 0.
|
|
6778
|
-
flagMotion:
|
|
6801
|
+
waveAmplitude: 0.94,
|
|
6802
|
+
waveDirection: { x: 0.88, z: 0.28 },
|
|
6803
|
+
wavePhaseSpeed: 0.88,
|
|
6804
|
+
wakeStrength: 0.31,
|
|
6805
|
+
wakeLength: 18,
|
|
6806
|
+
collisionRippleStrength: 0.42,
|
|
6807
|
+
waterNear: { r: 0.08, g: 0.23, b: 0.33 },
|
|
6808
|
+
waterFar: { r: 0.18, g: 0.35, b: 0.49 },
|
|
6809
|
+
harborWall: { r: 0.26, g: 0.24, b: 0.28 },
|
|
6810
|
+
harborDeck: { r: 0.33, g: 0.22, b: 0.16 },
|
|
6811
|
+
harborTower: { r: 0.23, g: 0.24, b: 0.29 },
|
|
6812
|
+
flagColor: { r: 0.66, g: 0.16, b: 0.13 },
|
|
6813
|
+
flagMotion: 0.92,
|
|
6814
|
+
lanternCore: { r: 0.98, g: 0.8, b: 0.48 },
|
|
6815
|
+
lanternGlow: { r: 1, g: 0.56, b: 0.2 },
|
|
6816
|
+
lanternReflectionStrength: 0.42,
|
|
6817
|
+
torchCore: { r: 0.99, g: 0.72, b: 0.36 },
|
|
6818
|
+
torchGlow: { r: 0.98, g: 0.38, b: 0.15 },
|
|
6819
|
+
collisionFlash: "rgba(255, 212, 168, 0.16)"
|
|
6779
6820
|
};
|
|
6780
6821
|
return {
|
|
6781
6822
|
skyTop: typeof customVisuals.skyTop === "string" ? customVisuals.skyTop : defaults.skyTop,
|
|
@@ -6784,7 +6825,12 @@ function resolveVisualConfig(nearLighting, lightingSnapshot, customVisuals = {})
|
|
|
6784
6825
|
seaTop: typeof customVisuals.seaTop === "string" ? customVisuals.seaTop : defaults.seaTop,
|
|
6785
6826
|
seaMid: typeof customVisuals.seaMid === "string" ? customVisuals.seaMid : defaults.seaMid,
|
|
6786
6827
|
seaBottom: typeof customVisuals.seaBottom === "string" ? customVisuals.seaBottom : defaults.seaBottom,
|
|
6787
|
-
|
|
6828
|
+
duskGlow: typeof customVisuals.duskGlow === "string" ? customVisuals.duskGlow : defaults.duskGlow,
|
|
6829
|
+
moonCore: typeof customVisuals.moonCore === "string" ? customVisuals.moonCore : typeof customVisuals.sunCore === "string" ? customVisuals.sunCore : defaults.moonCore,
|
|
6830
|
+
moonHalo: typeof customVisuals.moonHalo === "string" ? customVisuals.moonHalo : defaults.moonHalo,
|
|
6831
|
+
moonReflection: typeof customVisuals.moonReflection === "string" ? customVisuals.moonReflection : defaults.moonReflection,
|
|
6832
|
+
starColor: typeof customVisuals.starColor === "string" ? customVisuals.starColor : defaults.starColor,
|
|
6833
|
+
ambientMist: typeof customVisuals.ambientMist === "string" ? customVisuals.ambientMist : defaults.ambientMist,
|
|
6788
6834
|
reflectionStrength: readVisualNumber(
|
|
6789
6835
|
customVisuals.reflectionStrength,
|
|
6790
6836
|
defaults.reflectionStrength
|
|
@@ -6805,7 +6851,16 @@ function resolveVisualConfig(nearLighting, lightingSnapshot, customVisuals = {})
|
|
|
6805
6851
|
harborDeck: normalizeColorOverride(customVisuals.harborDeck, defaults.harborDeck),
|
|
6806
6852
|
harborTower: normalizeColorOverride(customVisuals.harborTower, defaults.harborTower),
|
|
6807
6853
|
flagColor: normalizeColorOverride(customVisuals.flagColor, defaults.flagColor),
|
|
6808
|
-
flagMotion: readVisualNumber(customVisuals.flagMotion, defaults.flagMotion)
|
|
6854
|
+
flagMotion: readVisualNumber(customVisuals.flagMotion, defaults.flagMotion),
|
|
6855
|
+
lanternCore: normalizeColorOverride(customVisuals.lanternCore, defaults.lanternCore),
|
|
6856
|
+
lanternGlow: normalizeColorOverride(customVisuals.lanternGlow, defaults.lanternGlow),
|
|
6857
|
+
lanternReflectionStrength: readVisualNumber(
|
|
6858
|
+
customVisuals.lanternReflectionStrength,
|
|
6859
|
+
defaults.lanternReflectionStrength
|
|
6860
|
+
),
|
|
6861
|
+
torchCore: normalizeColorOverride(customVisuals.torchCore, defaults.torchCore),
|
|
6862
|
+
torchGlow: normalizeColorOverride(customVisuals.torchGlow, defaults.torchGlow),
|
|
6863
|
+
collisionFlash: typeof customVisuals.collisionFlash === "string" ? customVisuals.collisionFlash : defaults.collisionFlash
|
|
6809
6864
|
};
|
|
6810
6865
|
}
|
|
6811
6866
|
function buildClothSurface(model, state, meshDetail, visuals) {
|
|
@@ -7040,18 +7095,34 @@ function createSceneState(options) {
|
|
|
7040
7095
|
{
|
|
7041
7096
|
id: "northwind",
|
|
7042
7097
|
position: vec3(-5.2, 0, 7.2),
|
|
7043
|
-
velocity: vec3(2.
|
|
7044
|
-
rotationY: 0.
|
|
7045
|
-
angularVelocity: 0.
|
|
7046
|
-
tint: { r: 0.62, g: 0.39, b: 0.23 }
|
|
7098
|
+
velocity: vec3(2.35, 0, -1.08),
|
|
7099
|
+
rotationY: 0.58,
|
|
7100
|
+
angularVelocity: 0.09,
|
|
7101
|
+
tint: { r: 0.62, g: 0.39, b: 0.23 },
|
|
7102
|
+
massScale: 1.42,
|
|
7103
|
+
cruiseSpeed: 2.25,
|
|
7104
|
+
throttleResponse: 0.46,
|
|
7105
|
+
rudderResponse: 0.54,
|
|
7106
|
+
wanderPhase: 0.35,
|
|
7107
|
+
lanterns: SHIP_LANTERNS,
|
|
7108
|
+
lanternStrength: 1.06,
|
|
7109
|
+
collisionRadiusScale: 1.04
|
|
7047
7110
|
},
|
|
7048
7111
|
{
|
|
7049
7112
|
id: "tidecaller",
|
|
7050
7113
|
position: vec3(4.8, 0, 4.4),
|
|
7051
|
-
velocity: vec3(-
|
|
7052
|
-
rotationY: -2.
|
|
7053
|
-
angularVelocity: -0.
|
|
7054
|
-
tint: { r: 0.48, g: 0.28, b: 0.19 }
|
|
7114
|
+
velocity: vec3(-2.15, 0, 1.74),
|
|
7115
|
+
rotationY: -2.48,
|
|
7116
|
+
angularVelocity: -0.2,
|
|
7117
|
+
tint: { r: 0.48, g: 0.28, b: 0.19 },
|
|
7118
|
+
massScale: 0.84,
|
|
7119
|
+
cruiseSpeed: 2.68,
|
|
7120
|
+
throttleResponse: 0.7,
|
|
7121
|
+
rudderResponse: 0.78,
|
|
7122
|
+
wanderPhase: 1.6,
|
|
7123
|
+
lanterns: SHIP_LANTERNS,
|
|
7124
|
+
lanternStrength: 1.18,
|
|
7125
|
+
collisionRadiusScale: 0.94
|
|
7055
7126
|
}
|
|
7056
7127
|
],
|
|
7057
7128
|
sprays: [],
|
|
@@ -7073,41 +7144,71 @@ function setListContent(element, values) {
|
|
|
7073
7144
|
element.innerHTML = values.map((value) => `<li>${value}</li>`).join("");
|
|
7074
7145
|
}
|
|
7075
7146
|
function drawSkyAndShore(ctx, canvas, state, nearLighting, reflectionStrength, shadowStrength, visuals) {
|
|
7076
|
-
const premiumShadows = nearLighting.primaryShadowSource === "ray-traced-primary";
|
|
7077
7147
|
const sky = ctx.createLinearGradient(0, 0, 0, canvas.height * 0.5);
|
|
7078
7148
|
sky.addColorStop(0, visuals.skyTop);
|
|
7079
|
-
sky.addColorStop(0.
|
|
7149
|
+
sky.addColorStop(0.54, visuals.skyMid);
|
|
7080
7150
|
sky.addColorStop(1, visuals.skyBottom);
|
|
7081
7151
|
ctx.fillStyle = sky;
|
|
7082
7152
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
7153
|
+
for (let index = 0; index < 70; index += 1) {
|
|
7154
|
+
const x = pseudoRandom(index + 13) * canvas.width;
|
|
7155
|
+
const y = pseudoRandom(index * 7 + 5) * canvas.height * 0.42;
|
|
7156
|
+
const twinkle = 0.45 + Math.sin(state.time * 1.4 + index * 0.73) * 0.25;
|
|
7157
|
+
const radius = 0.6 + pseudoRandom(index * 11 + 2) * 1.9;
|
|
7158
|
+
ctx.fillStyle = visuals.starColor.replace(/[\d.]+\)$/u, `${clamp(twinkle, 0.16, 0.92)})`);
|
|
7159
|
+
ctx.beginPath();
|
|
7160
|
+
ctx.arc(x, y, radius, 0, Math.PI * 2);
|
|
7161
|
+
ctx.fill();
|
|
7162
|
+
}
|
|
7163
|
+
const horizonGlow = ctx.createLinearGradient(0, canvas.height * 0.22, 0, canvas.height * 0.62);
|
|
7164
|
+
horizonGlow.addColorStop(0, "rgba(0, 0, 0, 0)");
|
|
7165
|
+
horizonGlow.addColorStop(1, visuals.duskGlow);
|
|
7166
|
+
ctx.fillStyle = horizonGlow;
|
|
7167
|
+
ctx.fillRect(0, canvas.height * 0.2, canvas.width, canvas.height * 0.45);
|
|
7083
7168
|
const shoreline = ctx.createLinearGradient(0, canvas.height * 0.45, 0, canvas.height);
|
|
7084
7169
|
shoreline.addColorStop(0, visuals.seaTop);
|
|
7085
7170
|
shoreline.addColorStop(0.52, visuals.seaMid);
|
|
7086
7171
|
shoreline.addColorStop(1, visuals.seaBottom);
|
|
7087
7172
|
ctx.fillStyle = shoreline;
|
|
7088
7173
|
ctx.fillRect(0, canvas.height * 0.45, canvas.width, canvas.height * 0.55);
|
|
7089
|
-
const
|
|
7090
|
-
const
|
|
7091
|
-
const
|
|
7092
|
-
|
|
7093
|
-
|
|
7094
|
-
|
|
7174
|
+
const moonX = canvas.width * 0.76 + Math.sin(state.time * 0.045) * 18;
|
|
7175
|
+
const moonY = canvas.height * 0.17 + Math.cos(state.time * 0.05) * 10;
|
|
7176
|
+
const moon = ctx.createRadialGradient(moonX, moonY, 14, moonX, moonY, 126);
|
|
7177
|
+
moon.addColorStop(0, visuals.moonCore);
|
|
7178
|
+
moon.addColorStop(0.46, visuals.moonHalo);
|
|
7179
|
+
moon.addColorStop(1, "rgba(167, 191, 255, 0)");
|
|
7180
|
+
ctx.fillStyle = moon;
|
|
7095
7181
|
ctx.beginPath();
|
|
7096
|
-
ctx.arc(
|
|
7182
|
+
ctx.arc(moonX, moonY, 94, 0, Math.PI * 2);
|
|
7097
7183
|
ctx.fill();
|
|
7098
|
-
const
|
|
7099
|
-
|
|
7100
|
-
|
|
7101
|
-
|
|
7184
|
+
const moonCore = ctx.createRadialGradient(moonX, moonY, 4, moonX, moonY, 28);
|
|
7185
|
+
moonCore.addColorStop(0, "rgba(255, 255, 255, 0.98)");
|
|
7186
|
+
moonCore.addColorStop(1, visuals.moonCore);
|
|
7187
|
+
ctx.fillStyle = moonCore;
|
|
7188
|
+
ctx.beginPath();
|
|
7189
|
+
ctx.arc(moonX, moonY, 24, 0, Math.PI * 2);
|
|
7190
|
+
ctx.fill();
|
|
7191
|
+
const track = ctx.createLinearGradient(moonX, canvas.height * 0.44, moonX, canvas.height * 0.98);
|
|
7192
|
+
track.addColorStop(0, visuals.moonReflection.replace(/[\d.]+\)$/u, `${0.08 + reflectionStrength * 0.12})`));
|
|
7193
|
+
track.addColorStop(0.42, visuals.moonReflection.replace(/[\d.]+\)$/u, `${0.04 + reflectionStrength * 0.18})`));
|
|
7194
|
+
track.addColorStop(1, "rgba(192, 214, 255, 0)");
|
|
7102
7195
|
ctx.save();
|
|
7103
7196
|
ctx.globalCompositeOperation = "screen";
|
|
7104
7197
|
ctx.fillStyle = track;
|
|
7105
7198
|
ctx.beginPath();
|
|
7106
|
-
ctx.ellipse(
|
|
7199
|
+
ctx.ellipse(moonX, canvas.height * 0.75, 38 + shadowStrength * 42, canvas.height * 0.24, 0, 0, Math.PI * 2);
|
|
7107
7200
|
ctx.fill();
|
|
7108
7201
|
ctx.restore();
|
|
7202
|
+
const mist = ctx.createLinearGradient(0, canvas.height * 0.5, 0, canvas.height);
|
|
7203
|
+
mist.addColorStop(0, "rgba(0, 0, 0, 0)");
|
|
7204
|
+
mist.addColorStop(1, visuals.ambientMist);
|
|
7205
|
+
ctx.fillStyle = mist;
|
|
7206
|
+
ctx.fillRect(0, canvas.height * 0.45, canvas.width, canvas.height * 0.55);
|
|
7109
7207
|
if (state.collisionFlash > 0.01) {
|
|
7110
|
-
ctx.fillStyle =
|
|
7208
|
+
ctx.fillStyle = visuals.collisionFlash.replace(
|
|
7209
|
+
/[\d.]+\)$/u,
|
|
7210
|
+
`${clamp(state.collisionFlash * 0.22, 0, 0.26)})`
|
|
7211
|
+
);
|
|
7111
7212
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
7112
7213
|
}
|
|
7113
7214
|
}
|
|
@@ -7206,7 +7307,7 @@ function pushHarborGeometry(camera, viewport, triangles, visuals) {
|
|
|
7206
7307
|
}
|
|
7207
7308
|
}
|
|
7208
7309
|
function renderShipRigging(ctx, ship, camera, viewport) {
|
|
7209
|
-
const transform = { position: ship.position, rotationY: ship.rotationY, scale:
|
|
7310
|
+
const transform = { position: ship.position, rotationY: ship.rotationY, scale: SHIP_SCALE };
|
|
7210
7311
|
const mastBase = transformPoint(vec3(0, 0.38, -0.4), transform);
|
|
7211
7312
|
const mastTop = transformPoint(vec3(0, 3.8, -0.2), transform);
|
|
7212
7313
|
const aftBase = transformPoint(vec3(-0.25, 0.32, -1.9), transform);
|
|
@@ -7310,6 +7411,35 @@ function renderWaterHighlights(ctx, waterBands, camera, viewport) {
|
|
|
7310
7411
|
}
|
|
7311
7412
|
}
|
|
7312
7413
|
}
|
|
7414
|
+
function readPhysicsNumber(physics, key, fallback) {
|
|
7415
|
+
const value = physics?.[key];
|
|
7416
|
+
return typeof value === "number" && Number.isFinite(value) ? value : fallback;
|
|
7417
|
+
}
|
|
7418
|
+
function getShipMass(ship, shipModel) {
|
|
7419
|
+
const baseMass = readPhysicsNumber(shipModel.physics, "mass", 3200);
|
|
7420
|
+
return baseMass * readVisualNumber(ship.massScale, 1);
|
|
7421
|
+
}
|
|
7422
|
+
function getShipHalfExtents(ship, shipModel) {
|
|
7423
|
+
const physicsHalfExtents = Array.isArray(shipModel.physics.halfExtents) ? shipModel.physics.halfExtents : [1.35, 0.95, 3.9];
|
|
7424
|
+
const scale = SHIP_SCALE * readVisualNumber(ship.collisionRadiusScale, 1);
|
|
7425
|
+
return {
|
|
7426
|
+
x: physicsHalfExtents[0] * scale,
|
|
7427
|
+
y: physicsHalfExtents[1] * scale,
|
|
7428
|
+
z: physicsHalfExtents[2] * scale
|
|
7429
|
+
};
|
|
7430
|
+
}
|
|
7431
|
+
function getShipCollisionRadius(ship, shipModel) {
|
|
7432
|
+
const halfExtents = getShipHalfExtents(ship, shipModel);
|
|
7433
|
+
return Math.max(halfExtents.x * 1.08, halfExtents.z * 0.62);
|
|
7434
|
+
}
|
|
7435
|
+
function getShipInverseMass(ship, shipModel) {
|
|
7436
|
+
return 1 / Math.max(1, getShipMass(ship, shipModel));
|
|
7437
|
+
}
|
|
7438
|
+
function getShipInverseInertia(ship, shipModel) {
|
|
7439
|
+
const radius = getShipCollisionRadius(ship, shipModel);
|
|
7440
|
+
const inertia = getShipMass(ship, shipModel) * radius * radius * 0.72;
|
|
7441
|
+
return 1 / Math.max(1, inertia);
|
|
7442
|
+
}
|
|
7313
7443
|
function spawnSpray(state, point, intensity) {
|
|
7314
7444
|
const count = state.fluidDetail.getSnapshot().currentLevel.config.splashCount;
|
|
7315
7445
|
for (let index = 0; index < count; index += 1) {
|
|
@@ -7322,55 +7452,182 @@ function spawnSpray(state, point, intensity) {
|
|
|
7322
7452
|
});
|
|
7323
7453
|
}
|
|
7324
7454
|
}
|
|
7325
|
-
function
|
|
7455
|
+
function resolveShipRoute(ship, state, radius) {
|
|
7456
|
+
if (typeof ship.routeDirection !== "number") {
|
|
7457
|
+
ship.routeDirection = ship.velocity.x >= 0 ? 1 : -1;
|
|
7458
|
+
}
|
|
7459
|
+
if (ship.position.x > HARBOR_BOUNDS.maxX - radius * 1.1) {
|
|
7460
|
+
ship.routeDirection = -1;
|
|
7461
|
+
} else if (ship.position.x < HARBOR_BOUNDS.minX + radius * 1.1) {
|
|
7462
|
+
ship.routeDirection = 1;
|
|
7463
|
+
}
|
|
7464
|
+
const wander = Math.sin(state.time * 0.22 + readVisualNumber(ship.wanderPhase, 0));
|
|
7465
|
+
const crossCurrent = Math.cos(state.time * 0.31 + readVisualNumber(ship.wanderPhase, 0));
|
|
7466
|
+
const laneCenter = ship.id === "northwind" ? 10.2 + wander * 2.1 + crossCurrent * 0.6 : 7 + wander * 3.3 - crossCurrent * 1.1;
|
|
7467
|
+
const targetX = ship.routeDirection > 0 ? HARBOR_BOUNDS.maxX - radius * 1.7 : HARBOR_BOUNDS.minX + radius * 1.7;
|
|
7468
|
+
return vec3(targetX, 0, clamp(laneCenter, HARBOR_BOUNDS.minZ + 1.8, HARBOR_BOUNDS.maxZ - 1.8));
|
|
7469
|
+
}
|
|
7470
|
+
function updateShipMotion(state, ship, dt, shipModel) {
|
|
7326
7471
|
const physics = shipModel.physics;
|
|
7327
|
-
const
|
|
7472
|
+
const massScale = Math.max(0.55, readVisualNumber(ship.massScale, 1));
|
|
7473
|
+
const radius = getShipCollisionRadius(ship, shipModel);
|
|
7474
|
+
const waterline = readPhysicsNumber(physics, "waterline", 0.42);
|
|
7475
|
+
const linearDamping = readPhysicsNumber(physics, "linearDamping", 0.04);
|
|
7476
|
+
const angularDamping = readPhysicsNumber(physics, "angularDamping", 0.08);
|
|
7477
|
+
const throttleResponse = readVisualNumber(ship.throttleResponse, 0.58);
|
|
7478
|
+
const rudderResponse = readVisualNumber(ship.rudderResponse, 0.62);
|
|
7479
|
+
const cruiseSpeed = readVisualNumber(ship.cruiseSpeed, 2.4);
|
|
7480
|
+
ship.collisionCooldown = Math.max(0, readVisualNumber(ship.collisionCooldown, 0) - dt);
|
|
7481
|
+
const forward = directionFromYaw(ship.rotationY);
|
|
7482
|
+
const lateral = perpendicularOnWater(forward);
|
|
7483
|
+
const routeTarget = resolveShipRoute(ship, state, radius);
|
|
7484
|
+
const desiredHeading = Math.atan2(routeTarget.x - ship.position.x, routeTarget.z - ship.position.z);
|
|
7485
|
+
const headingError = Math.atan2(
|
|
7486
|
+
Math.sin(desiredHeading - ship.rotationY),
|
|
7487
|
+
Math.cos(desiredHeading - ship.rotationY)
|
|
7488
|
+
);
|
|
7489
|
+
ship.angularVelocity += headingError * rudderResponse * dt * (1.18 / Math.sqrt(massScale)) + Math.sin(state.time * 0.9 + readVisualNumber(ship.wanderPhase, 0)) * dt * 0.04;
|
|
7490
|
+
const waveDirection = resolveWaveDirection(state);
|
|
7491
|
+
const forwardSpeed = dotVec3(ship.velocity, forward);
|
|
7492
|
+
const lateralSpeed = dotVec3(ship.velocity, lateral);
|
|
7493
|
+
const thrust = (cruiseSpeed - forwardSpeed) * throttleResponse;
|
|
7494
|
+
const currentDrift = sampleWave(state, ship.position.x, ship.position.z, state.time) * 0.016;
|
|
7495
|
+
const acceleration = addVec3(
|
|
7496
|
+
scaleVec3(forward, thrust),
|
|
7497
|
+
addVec3(
|
|
7498
|
+
scaleVec3(lateral, -lateralSpeed * (1.28 + rudderResponse * 0.4)),
|
|
7499
|
+
scaleVec3(waveDirection, currentDrift / Math.sqrt(massScale))
|
|
7500
|
+
)
|
|
7501
|
+
);
|
|
7502
|
+
ship.velocity = addVec3(ship.velocity, scaleVec3(acceleration, dt));
|
|
7503
|
+
ship.velocity = scaleVec3(
|
|
7504
|
+
ship.velocity,
|
|
7505
|
+
Math.max(0, 1 - linearDamping / Math.pow(massScale, 0.22) * dt)
|
|
7506
|
+
);
|
|
7507
|
+
ship.angularVelocity *= Math.max(
|
|
7508
|
+
0,
|
|
7509
|
+
1 - angularDamping / Math.pow(massScale, 0.15) * dt
|
|
7510
|
+
);
|
|
7511
|
+
ship.rotationY += ship.angularVelocity * dt;
|
|
7512
|
+
ship.position = addVec3(ship.position, scaleVec3(ship.velocity, dt));
|
|
7513
|
+
ship.position.y = sampleWave(state, ship.position.x, ship.position.z, state.time) * 0.24 + waterline;
|
|
7514
|
+
}
|
|
7515
|
+
function resolveBoundaryCollision(ship, state, shipModel) {
|
|
7516
|
+
const restitution = readPhysicsNumber(shipModel.physics, "restitution", 0.22) * 0.56;
|
|
7517
|
+
const radius = getShipCollisionRadius(ship, shipModel);
|
|
7518
|
+
const boundaries = [
|
|
7519
|
+
{ axis: "x", min: HARBOR_BOUNDS.minX + radius, max: HARBOR_BOUNDS.maxX - radius, normalMin: vec3(1, 0, 0), normalMax: vec3(-1, 0, 0) },
|
|
7520
|
+
{ axis: "z", min: HARBOR_BOUNDS.minZ + radius, max: HARBOR_BOUNDS.maxZ - radius, normalMin: vec3(0, 0, 1), normalMax: vec3(0, 0, -1) }
|
|
7521
|
+
];
|
|
7522
|
+
for (const boundary of boundaries) {
|
|
7523
|
+
if (ship.position[boundary.axis] < boundary.min) {
|
|
7524
|
+
ship.position[boundary.axis] = boundary.min;
|
|
7525
|
+
const normal = boundary.normalMin;
|
|
7526
|
+
const speedIntoWall = dotVec3(ship.velocity, normal);
|
|
7527
|
+
if (speedIntoWall < 0) {
|
|
7528
|
+
ship.velocity = subVec3(
|
|
7529
|
+
ship.velocity,
|
|
7530
|
+
scaleVec3(normal, (1 + restitution) * speedIntoWall)
|
|
7531
|
+
);
|
|
7532
|
+
const tangent = vec3(-normal.z, 0, normal.x);
|
|
7533
|
+
const tangentSpeed = dotVec3(ship.velocity, tangent);
|
|
7534
|
+
ship.velocity = subVec3(ship.velocity, scaleVec3(tangent, tangentSpeed * 0.12));
|
|
7535
|
+
ship.angularVelocity += tangentSpeed * 4e-3;
|
|
7536
|
+
}
|
|
7537
|
+
} else if (ship.position[boundary.axis] > boundary.max) {
|
|
7538
|
+
ship.position[boundary.axis] = boundary.max;
|
|
7539
|
+
const normal = boundary.normalMax;
|
|
7540
|
+
const speedIntoWall = dotVec3(ship.velocity, normal);
|
|
7541
|
+
if (speedIntoWall < 0) {
|
|
7542
|
+
ship.velocity = subVec3(
|
|
7543
|
+
ship.velocity,
|
|
7544
|
+
scaleVec3(normal, (1 + restitution) * speedIntoWall)
|
|
7545
|
+
);
|
|
7546
|
+
const tangent = vec3(-normal.z, 0, normal.x);
|
|
7547
|
+
const tangentSpeed = dotVec3(ship.velocity, tangent);
|
|
7548
|
+
ship.velocity = subVec3(ship.velocity, scaleVec3(tangent, tangentSpeed * 0.12));
|
|
7549
|
+
ship.angularVelocity += tangentSpeed * 4e-3;
|
|
7550
|
+
}
|
|
7551
|
+
}
|
|
7552
|
+
}
|
|
7553
|
+
}
|
|
7554
|
+
function resolveShipCollision(state, a, b, shipModel) {
|
|
7555
|
+
const delta = subVec3(b.position, a.position);
|
|
7556
|
+
const radiusA = getShipCollisionRadius(a, shipModel);
|
|
7557
|
+
const radiusB = getShipCollisionRadius(b, shipModel);
|
|
7558
|
+
const distance = Math.hypot(delta.x, delta.z);
|
|
7559
|
+
const minDistance = radiusA + radiusB;
|
|
7560
|
+
if (distance >= minDistance) {
|
|
7561
|
+
return false;
|
|
7562
|
+
}
|
|
7563
|
+
const normal = distance > 1e-4 ? normalizeVec3(vec3(delta.x / distance, 0, delta.z / distance)) : normalizeVec3(vec3(Math.cos(state.time * 5.2), 0, Math.sin(state.time * 4.8)));
|
|
7564
|
+
const tangent = vec3(-normal.z, 0, normal.x);
|
|
7565
|
+
const penetration = minDistance - distance;
|
|
7566
|
+
const invMassA = getShipInverseMass(a, shipModel);
|
|
7567
|
+
const invMassB = getShipInverseMass(b, shipModel);
|
|
7568
|
+
const invMassSum = invMassA + invMassB;
|
|
7569
|
+
const correction = scaleVec3(normal, penetration / Math.max(1e-4, invMassSum) * 0.72);
|
|
7570
|
+
a.position = subVec3(a.position, scaleVec3(correction, invMassA));
|
|
7571
|
+
b.position = addVec3(b.position, scaleVec3(correction, invMassB));
|
|
7572
|
+
const relativeVelocity = subVec3(b.velocity, a.velocity);
|
|
7573
|
+
const velocityAlongNormal = dotVec3(relativeVelocity, normal);
|
|
7574
|
+
const restitution = readPhysicsNumber(shipModel.physics, "restitution", 0.22) * 0.88;
|
|
7575
|
+
if (velocityAlongNormal < 0) {
|
|
7576
|
+
const impulseMagnitude = -(1 + restitution) * velocityAlongNormal / Math.max(1e-4, invMassSum);
|
|
7577
|
+
const impulse = scaleVec3(normal, impulseMagnitude);
|
|
7578
|
+
a.velocity = subVec3(a.velocity, scaleVec3(impulse, invMassA));
|
|
7579
|
+
b.velocity = addVec3(b.velocity, scaleVec3(impulse, invMassB));
|
|
7580
|
+
const tangentSpeed = dotVec3(relativeVelocity, tangent);
|
|
7581
|
+
const frictionMagnitude = clamp(
|
|
7582
|
+
-tangentSpeed / Math.max(1e-4, invMassSum),
|
|
7583
|
+
-impulseMagnitude * 0.16,
|
|
7584
|
+
impulseMagnitude * 0.16
|
|
7585
|
+
);
|
|
7586
|
+
const frictionImpulse = scaleVec3(tangent, frictionMagnitude);
|
|
7587
|
+
a.velocity = subVec3(a.velocity, scaleVec3(frictionImpulse, invMassA));
|
|
7588
|
+
b.velocity = addVec3(b.velocity, scaleVec3(frictionImpulse, invMassB));
|
|
7589
|
+
a.angularVelocity -= tangentSpeed * radiusA * getShipInverseInertia(a, shipModel) * 0.2 + impulseMagnitude * 24e-5;
|
|
7590
|
+
b.angularVelocity += tangentSpeed * radiusB * getShipInverseInertia(b, shipModel) * 0.2 + impulseMagnitude * 24e-5;
|
|
7591
|
+
const impactSpeed = Math.abs(velocityAlongNormal);
|
|
7592
|
+
if (impactSpeed > 0.18 && Math.max(readVisualNumber(a.collisionCooldown, 0), readVisualNumber(b.collisionCooldown, 0)) <= 0) {
|
|
7593
|
+
const contactPoint = vec3(
|
|
7594
|
+
(a.position.x + b.position.x) * 0.5,
|
|
7595
|
+
(a.position.y + b.position.y) * 0.5 + 0.14,
|
|
7596
|
+
(a.position.z + b.position.z) * 0.5
|
|
7597
|
+
);
|
|
7598
|
+
spawnSpray(state, contactPoint, impactSpeed * 2.4 + penetration * 8);
|
|
7599
|
+
state.waveImpulses.push({
|
|
7600
|
+
x: contactPoint.x,
|
|
7601
|
+
z: contactPoint.z,
|
|
7602
|
+
strength: clamp(0.24 + impactSpeed * 0.46 + penetration * 0.9, 0.2, 1.7),
|
|
7603
|
+
radius: 0.9 + penetration * 1.4,
|
|
7604
|
+
life: 1
|
|
7605
|
+
});
|
|
7606
|
+
state.collisionCount += 1;
|
|
7607
|
+
state.collisionFlash = Math.max(
|
|
7608
|
+
state.collisionFlash,
|
|
7609
|
+
clamp(impactSpeed * 0.55 + penetration * 1.8, 0.16, 1)
|
|
7610
|
+
);
|
|
7611
|
+
a.collisionCooldown = 0.2;
|
|
7612
|
+
b.collisionCooldown = 0.2;
|
|
7613
|
+
}
|
|
7614
|
+
}
|
|
7615
|
+
state.contactCount += 1;
|
|
7616
|
+
return true;
|
|
7617
|
+
}
|
|
7618
|
+
function updateShips(state, dt, shipModel) {
|
|
7328
7619
|
let collided = false;
|
|
7329
7620
|
state.contactCount = 0;
|
|
7330
7621
|
for (const ship of state.ships) {
|
|
7331
|
-
|
|
7332
|
-
ship
|
|
7333
|
-
ship.velocity = scaleVec3(ship.velocity, 1 - (physics.linearDamping ?? 0.04) * dt);
|
|
7334
|
-
ship.angularVelocity *= 1 - (physics.angularDamping ?? 0.08) * dt;
|
|
7335
|
-
ship.position.y = sampleWave(state, ship.position.x, ship.position.z, state.time) * 0.22 + (physics.waterline ?? 0.42);
|
|
7336
|
-
if (Math.abs(ship.position.x) > 10) {
|
|
7337
|
-
ship.velocity.x *= -1;
|
|
7338
|
-
ship.angularVelocity *= -1;
|
|
7339
|
-
}
|
|
7340
|
-
if (ship.position.z < 2 || ship.position.z > 16) {
|
|
7341
|
-
ship.velocity.z *= -1;
|
|
7342
|
-
ship.angularVelocity *= -1;
|
|
7343
|
-
}
|
|
7622
|
+
updateShipMotion(state, ship, dt, shipModel);
|
|
7623
|
+
resolveBoundaryCollision(ship, state, shipModel);
|
|
7344
7624
|
}
|
|
7345
|
-
|
|
7346
|
-
|
|
7347
|
-
|
|
7348
|
-
|
|
7349
|
-
const overlapZ = Math.abs(dz) < halfExtents[2] * 0.8;
|
|
7350
|
-
if (overlapX && overlapZ) {
|
|
7351
|
-
const restitution = physics.restitution ?? 0.22;
|
|
7352
|
-
const swapX = a.velocity.x;
|
|
7353
|
-
const swapZ = a.velocity.z;
|
|
7354
|
-
a.velocity.x = -b.velocity.x * (0.86 + restitution);
|
|
7355
|
-
a.velocity.z = -b.velocity.z * (0.82 + restitution);
|
|
7356
|
-
b.velocity.x = -swapX * (0.86 + restitution);
|
|
7357
|
-
b.velocity.z = -swapZ * (0.82 + restitution);
|
|
7358
|
-
a.angularVelocity += 0.55;
|
|
7359
|
-
b.angularVelocity -= 0.55;
|
|
7360
|
-
const contactPoint = vec3((a.position.x + b.position.x) * 0.5, (a.position.y + b.position.y) * 0.5 + 0.1, (a.position.z + b.position.z) * 0.5);
|
|
7361
|
-
spawnSpray(state, contactPoint, Math.abs(dx) + Math.abs(dz));
|
|
7362
|
-
state.waveImpulses.push({
|
|
7363
|
-
x: contactPoint.x,
|
|
7364
|
-
z: contactPoint.z,
|
|
7365
|
-
strength: Math.min(1.4, 0.2 + (Math.abs(dx) + Math.abs(dz)) * 0.18),
|
|
7366
|
-
radius: 0.8,
|
|
7367
|
-
life: 1
|
|
7368
|
-
});
|
|
7369
|
-
state.collisionCount += 1;
|
|
7370
|
-
state.contactCount = 1;
|
|
7371
|
-
collided = true;
|
|
7625
|
+
for (let index = 0; index < state.ships.length; index += 1) {
|
|
7626
|
+
for (let otherIndex = index + 1; otherIndex < state.ships.length; otherIndex += 1) {
|
|
7627
|
+
collided = resolveShipCollision(state, state.ships[index], state.ships[otherIndex], shipModel) || collided;
|
|
7628
|
+
}
|
|
7372
7629
|
}
|
|
7373
|
-
state.collisionFlash = collided ?
|
|
7630
|
+
state.collisionFlash = collided ? Math.max(0.12, state.collisionFlash) : Math.max(0, state.collisionFlash - dt * 1.3);
|
|
7374
7631
|
}
|
|
7375
7632
|
function updateWaveImpulses(state, dt) {
|
|
7376
7633
|
state.waveImpulses = state.waveImpulses.map((impulse) => ({
|
|
@@ -7483,7 +7740,7 @@ function renderFlagPole(ctx, camera, viewport) {
|
|
|
7483
7740
|
function renderShipShadow(ctx, shipModel, ship, state, camera, viewport, lightDir, shadowStrength) {
|
|
7484
7741
|
const bounds = shipModel.bounds;
|
|
7485
7742
|
const keelY = (shipModel.physics.waterline ?? 0.42) - 0.28;
|
|
7486
|
-
const transform = { position: ship.position, rotationY: ship.rotationY, scale:
|
|
7743
|
+
const transform = { position: ship.position, rotationY: ship.rotationY, scale: SHIP_SCALE };
|
|
7487
7744
|
const hullCorners = [
|
|
7488
7745
|
vec3(bounds.min[0], keelY, bounds.min[2]),
|
|
7489
7746
|
vec3(bounds.max[0], keelY, bounds.min[2]),
|
|
@@ -7509,6 +7766,97 @@ function renderFlagShadow(ctx, cloth, camera, viewport, lightDir, shadowStrength
|
|
|
7509
7766
|
blur: 12 + shadowStrength * 20
|
|
7510
7767
|
});
|
|
7511
7768
|
}
|
|
7769
|
+
function renderGlowLight(ctx, point, camera, viewport, coreColor, glowColor, glowScale, reflectionStrength = 0, state = null) {
|
|
7770
|
+
const projected = projectPoint(point, camera, viewport);
|
|
7771
|
+
if (!projected) {
|
|
7772
|
+
return;
|
|
7773
|
+
}
|
|
7774
|
+
const radius = clamp(1 / projected.depth * 420 * glowScale, 4, 34);
|
|
7775
|
+
const halo = ctx.createRadialGradient(projected.x, projected.y, radius * 0.12, projected.x, projected.y, radius);
|
|
7776
|
+
halo.addColorStop(0, colorToRgba(coreColor, 0.98));
|
|
7777
|
+
halo.addColorStop(0.5, colorToRgba(glowColor, 0.42));
|
|
7778
|
+
halo.addColorStop(1, colorToRgba(glowColor, 0));
|
|
7779
|
+
ctx.save();
|
|
7780
|
+
ctx.globalCompositeOperation = "screen";
|
|
7781
|
+
ctx.fillStyle = halo;
|
|
7782
|
+
ctx.beginPath();
|
|
7783
|
+
ctx.arc(projected.x, projected.y, radius, 0, Math.PI * 2);
|
|
7784
|
+
ctx.fill();
|
|
7785
|
+
ctx.restore();
|
|
7786
|
+
ctx.fillStyle = colorToRgba(coreColor, 0.98);
|
|
7787
|
+
ctx.beginPath();
|
|
7788
|
+
ctx.arc(projected.x, projected.y, Math.max(1.2, radius * 0.16), 0, Math.PI * 2);
|
|
7789
|
+
ctx.fill();
|
|
7790
|
+
if (state && reflectionStrength > 0) {
|
|
7791
|
+
const waterline = sampleWave(state, point.x, point.z, state.time) * 0.22;
|
|
7792
|
+
const reflectedPoint = vec3(point.x, waterline - (point.y - waterline) * 0.58, point.z + 0.08);
|
|
7793
|
+
const reflected = projectPoint(reflectedPoint, camera, viewport);
|
|
7794
|
+
if (reflected) {
|
|
7795
|
+
const reflectionRadius = radius * 0.72;
|
|
7796
|
+
const glow = ctx.createRadialGradient(
|
|
7797
|
+
reflected.x,
|
|
7798
|
+
reflected.y,
|
|
7799
|
+
reflectionRadius * 0.1,
|
|
7800
|
+
reflected.x,
|
|
7801
|
+
reflected.y,
|
|
7802
|
+
reflectionRadius
|
|
7803
|
+
);
|
|
7804
|
+
glow.addColorStop(0, colorToRgba(coreColor, reflectionStrength * 0.34));
|
|
7805
|
+
glow.addColorStop(1, colorToRgba(glowColor, 0));
|
|
7806
|
+
ctx.save();
|
|
7807
|
+
ctx.globalCompositeOperation = "screen";
|
|
7808
|
+
ctx.fillStyle = glow;
|
|
7809
|
+
ctx.beginPath();
|
|
7810
|
+
ctx.ellipse(
|
|
7811
|
+
reflected.x,
|
|
7812
|
+
reflected.y,
|
|
7813
|
+
reflectionRadius * 0.34,
|
|
7814
|
+
reflectionRadius,
|
|
7815
|
+
0,
|
|
7816
|
+
0,
|
|
7817
|
+
Math.PI * 2
|
|
7818
|
+
);
|
|
7819
|
+
ctx.fill();
|
|
7820
|
+
ctx.restore();
|
|
7821
|
+
}
|
|
7822
|
+
}
|
|
7823
|
+
}
|
|
7824
|
+
function renderShipLanterns(ctx, ship, state, camera, viewport, visuals) {
|
|
7825
|
+
const lanterns = Array.isArray(ship.lanterns) ? ship.lanterns : SHIP_LANTERNS;
|
|
7826
|
+
const strength = readVisualNumber(ship.lanternStrength, 1);
|
|
7827
|
+
for (const lantern of lanterns) {
|
|
7828
|
+
const position = transformPoint(
|
|
7829
|
+
vec3(lantern.x, lantern.y, lantern.z),
|
|
7830
|
+
{ position: ship.position, rotationY: ship.rotationY, scale: SHIP_SCALE }
|
|
7831
|
+
);
|
|
7832
|
+
renderGlowLight(
|
|
7833
|
+
ctx,
|
|
7834
|
+
position,
|
|
7835
|
+
camera,
|
|
7836
|
+
viewport,
|
|
7837
|
+
visuals.lanternCore,
|
|
7838
|
+
visuals.lanternGlow,
|
|
7839
|
+
lantern.glow * strength,
|
|
7840
|
+
visuals.lanternReflectionStrength,
|
|
7841
|
+
state
|
|
7842
|
+
);
|
|
7843
|
+
}
|
|
7844
|
+
}
|
|
7845
|
+
function renderHarborTorches(ctx, state, camera, viewport, visuals) {
|
|
7846
|
+
for (const torch of HARBOR_TORCHES) {
|
|
7847
|
+
renderGlowLight(
|
|
7848
|
+
ctx,
|
|
7849
|
+
vec3(torch.x, torch.y, torch.z),
|
|
7850
|
+
camera,
|
|
7851
|
+
viewport,
|
|
7852
|
+
visuals.torchCore,
|
|
7853
|
+
visuals.torchGlow,
|
|
7854
|
+
torch.glow,
|
|
7855
|
+
visuals.lanternReflectionStrength * 0.55,
|
|
7856
|
+
state
|
|
7857
|
+
);
|
|
7858
|
+
}
|
|
7859
|
+
}
|
|
7512
7860
|
function renderScene(ctx, canvas, state, shipModel, dom) {
|
|
7513
7861
|
const viewport = { width: canvas.width, height: canvas.height };
|
|
7514
7862
|
const camera = buildCamera(state, canvas);
|
|
@@ -7518,7 +7866,7 @@ function renderScene(ctx, canvas, state, shipModel, dom) {
|
|
|
7518
7866
|
importance: state.focus === "lighting" ? "critical" : "high"
|
|
7519
7867
|
});
|
|
7520
7868
|
const nearLighting = lightingPlan.bands.find((entry) => entry.band === "near") ?? lightingPlan.bands[0];
|
|
7521
|
-
const lightDir = normalizeVec3(vec3(-0.
|
|
7869
|
+
const lightDir = normalizeVec3(vec3(-0.22, 0.94, -0.31));
|
|
7522
7870
|
const lightingSnapshot = state.lightingDetail.getSnapshot();
|
|
7523
7871
|
const visuals = resolveVisualConfig(
|
|
7524
7872
|
nearLighting,
|
|
@@ -7592,7 +7940,7 @@ function renderScene(ctx, canvas, state, shipModel, dom) {
|
|
|
7592
7940
|
for (const ship of state.ships) {
|
|
7593
7941
|
buildTrianglesFromMesh(
|
|
7594
7942
|
shipModel,
|
|
7595
|
-
{ position: ship.position, rotationY: ship.rotationY, scale:
|
|
7943
|
+
{ position: ship.position, rotationY: ship.rotationY, scale: SHIP_SCALE },
|
|
7596
7944
|
ship.tint,
|
|
7597
7945
|
camera,
|
|
7598
7946
|
viewport,
|
|
@@ -7606,10 +7954,12 @@ function renderScene(ctx, canvas, state, shipModel, dom) {
|
|
|
7606
7954
|
renderFlagShadow(ctx, cloth, camera, viewport, lightDir, shadowStrength);
|
|
7607
7955
|
drawTriangles(ctx, triangles, lightDir, reflectionStrength, camera, shadowStrength);
|
|
7608
7956
|
renderWaterHighlights(ctx, water.bandMeshes, camera, viewport);
|
|
7957
|
+
renderHarborTorches(ctx, state, camera, viewport, visuals);
|
|
7609
7958
|
renderFlagPole(ctx, camera, viewport);
|
|
7610
7959
|
renderClothAccent(ctx, cloth, camera, viewport);
|
|
7611
7960
|
for (const ship of state.ships) {
|
|
7612
7961
|
renderShipRigging(ctx, ship, camera, viewport);
|
|
7962
|
+
renderShipLanterns(ctx, ship, state, camera, viewport, visuals);
|
|
7613
7963
|
}
|
|
7614
7964
|
renderSprays(ctx, state.sprays, camera, viewport);
|
|
7615
7965
|
const debugSnapshot = state.debugSession.getSnapshot();
|
|
@@ -7621,8 +7971,10 @@ function renderScene(ctx, canvas, state, shipModel, dom) {
|
|
|
7621
7971
|
const sceneMetrics = [
|
|
7622
7972
|
`focus: ${state.focus}`,
|
|
7623
7973
|
`ships: ${state.ships.length} active GLTF hulls`,
|
|
7974
|
+
`moonlight: cold overhead key + ${HARBOR_TORCHES.length + state.ships.length * SHIP_LANTERNS.length} warm deck and harbor lights`,
|
|
7624
7975
|
`physics snapshot: ${state.physics.snapshot.stage} (${state.physics.snapshot.stability})`,
|
|
7625
|
-
`physics contacts: ${state.
|
|
7976
|
+
`physics contacts: ${state.contactCount}`,
|
|
7977
|
+
`mass split: ${state.ships.map((ship) => `${ship.id} ${(getShipMass(ship, shipModel) / 1e3).toFixed(1)}t`).join(" \xB7 ")}`,
|
|
7626
7978
|
`cloth band: ${cloth.band} -> ${cloth.representation.output}`,
|
|
7627
7979
|
`fluid near band: ${water.bandMeshes[0].representation.output}`,
|
|
7628
7980
|
`lighting profile: ${lightingPlan.profile} (${lightingDistanceBands.length} bands)`
|
|
@@ -7645,8 +7997,8 @@ function renderScene(ctx, canvas, state, shipModel, dom) {
|
|
|
7645
7997
|
];
|
|
7646
7998
|
const sceneNotes = state.focus === "physics" ? [
|
|
7647
7999
|
"Stable world snapshots are taken after the authoritative rigid-body commit and before visual follow-up work.",
|
|
7648
|
-
"The ships collide
|
|
7649
|
-
"
|
|
8000
|
+
"The ships collide with mass-weighted impulses and positional correction, so the heavier hull keeps more of its line.",
|
|
8001
|
+
"Moonlight keeps the overall read legible while lanterns and torches make collision moments easy to track against the water."
|
|
7650
8002
|
] : SCENE_NOTES;
|
|
7651
8003
|
const custom = state.demoDescription ?? null;
|
|
7652
8004
|
setListContent(
|
|
@@ -7663,7 +8015,7 @@ function renderScene(ctx, canvas, state, shipModel, dom) {
|
|
|
7663
8015
|
);
|
|
7664
8016
|
setListContent(dom.sceneNotes, Array.isArray(custom?.notes) ? custom.notes : sceneNotes);
|
|
7665
8017
|
dom.status.textContent = typeof custom?.status === "string" ? custom.status : `3D scene live \xB7 ${state.lastDecision.metrics.fps.toFixed(1)} FPS`;
|
|
7666
|
-
dom.details.textContent = typeof custom?.details === "string" ? custom.details : state.focus === "physics" ? `Stable world snapshots are emitted from ${state.physics.plan.snapshotStageId} after the authoritative solver;
|
|
8018
|
+
dom.details.textContent = typeof custom?.details === "string" ? custom.details : state.focus === "physics" ? `Stable world snapshots are emitted from ${state.physics.plan.snapshotStageId} after the authoritative solver; the heavier hull now carries momentum through mass-aware collision impulses while cloth and fluid remain downstream.` : `Moonlit GLTF ships collide on ${shipModel.physics.shape ?? "box"} physics volumes; lantern reflections, cloth, and fluid remain continuous while the governor pressure is ${state.lastDecision.pressureLevel}.`;
|
|
7667
8019
|
}
|
|
7668
8020
|
function updateSceneState(state, dt, shipModel) {
|
|
7669
8021
|
updateShips(state, dt, shipModel);
|
|
@@ -7682,7 +8034,9 @@ function syncTextState(state, shipModel) {
|
|
|
7682
8034
|
y: Number(ship.position.y.toFixed(2)),
|
|
7683
8035
|
z: Number(ship.position.z.toFixed(2)),
|
|
7684
8036
|
vx: Number(ship.velocity.x.toFixed(2)),
|
|
7685
|
-
vz: Number(ship.velocity.z.toFixed(2))
|
|
8037
|
+
vz: Number(ship.velocity.z.toFixed(2)),
|
|
8038
|
+
massKg: Math.round(getShipMass(ship, shipModel)),
|
|
8039
|
+
lanterns: Array.isArray(ship.lanterns) ? ship.lanterns.length : 0
|
|
7686
8040
|
})),
|
|
7687
8041
|
shipPhysics: shipModel.physics,
|
|
7688
8042
|
sprays: state.sprays.length,
|
|
@@ -7710,6 +8064,7 @@ function syncTextState(state, shipModel) {
|
|
|
7710
8064
|
async function mountGpuShowcase(options = {}) {
|
|
7711
8065
|
injectStyles();
|
|
7712
8066
|
const root = options.root ?? document.body;
|
|
8067
|
+
root.classList?.add?.(ROOT_CLASS);
|
|
7713
8068
|
const previousMarkup = root.innerHTML;
|
|
7714
8069
|
const previousRenderGameToText = window.render_game_to_text;
|
|
7715
8070
|
const previousAdvanceTime = window.advanceTime;
|
|
@@ -7729,8 +8084,11 @@ async function mountGpuShowcase(options = {}) {
|
|
|
7729
8084
|
state.demoDescription = resolveSceneDescription(state, options, shipModel).description;
|
|
7730
8085
|
syncTextState(state, shipModel);
|
|
7731
8086
|
const ctx = dom.canvas.getContext("2d");
|
|
8087
|
+
if (!ctx) {
|
|
8088
|
+
throw new Error("2D canvas context is required for the shared showcase.");
|
|
8089
|
+
}
|
|
8090
|
+
let animationFrameId = 0;
|
|
7732
8091
|
let destroyed = false;
|
|
7733
|
-
let frameHandle = null;
|
|
7734
8092
|
const renderFrame = (nowMs) => {
|
|
7735
8093
|
if (destroyed) {
|
|
7736
8094
|
return;
|
|
@@ -7751,7 +8109,7 @@ async function mountGpuShowcase(options = {}) {
|
|
|
7751
8109
|
state.demoDescription = resolveSceneDescription(state, options, shipModel).description;
|
|
7752
8110
|
renderScene(ctx, dom.canvas, state, shipModel, dom);
|
|
7753
8111
|
syncTextState(state, shipModel);
|
|
7754
|
-
|
|
8112
|
+
animationFrameId = requestAnimationFrame(renderFrame);
|
|
7755
8113
|
};
|
|
7756
8114
|
const handlePauseClick = () => {
|
|
7757
8115
|
state.paused = !state.paused;
|
|
@@ -7770,34 +8128,43 @@ async function mountGpuShowcase(options = {}) {
|
|
|
7770
8128
|
dom.pauseButton.addEventListener("click", handlePauseClick);
|
|
7771
8129
|
dom.stressToggle.addEventListener("change", handleStressChange);
|
|
7772
8130
|
dom.focusMode.addEventListener("change", handleFocusChange);
|
|
7773
|
-
|
|
8131
|
+
animationFrameId = requestAnimationFrame(renderFrame);
|
|
8132
|
+
const destroy = () => {
|
|
8133
|
+
if (destroyed) {
|
|
8134
|
+
return;
|
|
8135
|
+
}
|
|
8136
|
+
destroyed = true;
|
|
8137
|
+
if (animationFrameId) {
|
|
8138
|
+
cancelAnimationFrame(animationFrameId);
|
|
8139
|
+
}
|
|
8140
|
+
dom.pauseButton.removeEventListener("click", handlePauseClick);
|
|
8141
|
+
dom.stressToggle.removeEventListener("change", handleStressChange);
|
|
8142
|
+
dom.focusMode.removeEventListener("change", handleFocusChange);
|
|
8143
|
+
try {
|
|
8144
|
+
if (typeof options.destroyState === "function") {
|
|
8145
|
+
options.destroyState(state.packageState);
|
|
8146
|
+
}
|
|
8147
|
+
} finally {
|
|
8148
|
+
state.packageState = void 0;
|
|
8149
|
+
}
|
|
8150
|
+
root.classList?.remove?.(ROOT_CLASS);
|
|
8151
|
+
root.innerHTML = previousMarkup;
|
|
8152
|
+
if (typeof previousRenderGameToText === "function") {
|
|
8153
|
+
window.render_game_to_text = previousRenderGameToText;
|
|
8154
|
+
} else {
|
|
8155
|
+
delete window.render_game_to_text;
|
|
8156
|
+
}
|
|
8157
|
+
if (typeof previousAdvanceTime === "function") {
|
|
8158
|
+
window.advanceTime = previousAdvanceTime;
|
|
8159
|
+
} else {
|
|
8160
|
+
delete window.advanceTime;
|
|
8161
|
+
}
|
|
8162
|
+
};
|
|
7774
8163
|
return {
|
|
7775
8164
|
state,
|
|
7776
8165
|
shipModel,
|
|
7777
8166
|
canvas: dom.canvas,
|
|
7778
|
-
destroy
|
|
7779
|
-
if (destroyed) {
|
|
7780
|
-
return;
|
|
7781
|
-
}
|
|
7782
|
-
destroyed = true;
|
|
7783
|
-
if (frameHandle != null) {
|
|
7784
|
-
cancelAnimationFrame(frameHandle);
|
|
7785
|
-
}
|
|
7786
|
-
dom.pauseButton.removeEventListener("click", handlePauseClick);
|
|
7787
|
-
dom.stressToggle.removeEventListener("change", handleStressChange);
|
|
7788
|
-
dom.focusMode.removeEventListener("change", handleFocusChange);
|
|
7789
|
-
root.innerHTML = previousMarkup;
|
|
7790
|
-
if (typeof previousRenderGameToText === "function") {
|
|
7791
|
-
window.render_game_to_text = previousRenderGameToText;
|
|
7792
|
-
} else {
|
|
7793
|
-
delete window.render_game_to_text;
|
|
7794
|
-
}
|
|
7795
|
-
if (typeof previousAdvanceTime === "function") {
|
|
7796
|
-
window.advanceTime = previousAdvanceTime;
|
|
7797
|
-
} else {
|
|
7798
|
-
delete window.advanceTime;
|
|
7799
|
-
}
|
|
7800
|
-
}
|
|
8167
|
+
destroy
|
|
7801
8168
|
};
|
|
7802
8169
|
}
|
|
7803
8170
|
function updatePhysicsSnapshot(state, shipModel) {
|
|
@@ -7811,7 +8178,7 @@ function updatePhysicsSnapshot(state, shipModel) {
|
|
|
7811
8178
|
animationInputRevision: state.frame,
|
|
7812
8179
|
bodyCount: state.ships.length + 2,
|
|
7813
8180
|
dynamicBodyCount: state.ships.length,
|
|
7814
|
-
contactCount: state.
|
|
8181
|
+
contactCount: state.contactCount,
|
|
7815
8182
|
metadata: {
|
|
7816
8183
|
collisionCount: state.collisionCount,
|
|
7817
8184
|
contactCount: state.contactCount,
|
|
@@ -7824,4 +8191,4 @@ export {
|
|
|
7824
8191
|
mountGpuShowcase,
|
|
7825
8192
|
showcaseFocusModes
|
|
7826
8193
|
};
|
|
7827
|
-
//# sourceMappingURL=showcase-runtime-
|
|
8194
|
+
//# sourceMappingURL=showcase-runtime-JZIYGQAU.js.map
|