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