@plasius/gpu-shared 0.1.0 → 0.1.2
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 +30 -0
- package/dist/index.cjs +426 -40
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/dist/{showcase-runtime-5H44EZXD.js → showcase-runtime-67CHJBNO.js} +426 -40
- package/dist/showcase-runtime-67CHJBNO.js.map +1 -0
- package/package.json +10 -10
- package/src/showcase-runtime.js +372 -44
- package/dist/showcase-runtime-5H44EZXD.js.map +0 -1
|
@@ -4216,6 +4216,12 @@ var gpuResourceCategories = Object.freeze([
|
|
|
4216
4216
|
"pipeline",
|
|
4217
4217
|
"custom"
|
|
4218
4218
|
]);
|
|
4219
|
+
var gpuPipelinePhases = Object.freeze([
|
|
4220
|
+
"simulation",
|
|
4221
|
+
"secondary-simulation",
|
|
4222
|
+
"scene-preparation",
|
|
4223
|
+
"render"
|
|
4224
|
+
]);
|
|
4219
4225
|
function isRecord2(value) {
|
|
4220
4226
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4221
4227
|
}
|
|
@@ -4329,6 +4335,7 @@ var DEFAULT_OPTIONS = Object.freeze({
|
|
|
4329
4335
|
maxRetainedQueueSamples: 240,
|
|
4330
4336
|
maxRetainedReadyLaneSamples: 240,
|
|
4331
4337
|
maxRetainedDependencyUnlockSamples: 240,
|
|
4338
|
+
maxRetainedPipelinePhaseSamples: 240,
|
|
4332
4339
|
maxRetainedFrameSamples: 240,
|
|
4333
4340
|
maxTrackedAllocations: 512
|
|
4334
4341
|
});
|
|
@@ -4336,7 +4343,8 @@ var LIMITATIONS = Object.freeze([
|
|
|
4336
4343
|
"Tracked memory reflects only allocations reported to this debug session.",
|
|
4337
4344
|
"Portable WebGPU does not expose authoritative live GPU core-count or total-memory counters.",
|
|
4338
4345
|
"Hardware hints are optional caller-supplied metadata and may be platform-specific.",
|
|
4339
|
-
"Ready-lane and dependency-unlock diagnostics are caller-reported integration samples, not automatic WebGPU counters."
|
|
4346
|
+
"Ready-lane and dependency-unlock diagnostics are caller-reported integration samples, not automatic WebGPU counters.",
|
|
4347
|
+
"Pipeline phase and snapshot-lag diagnostics are caller-reported integration samples, not automatic WebGPU counters."
|
|
4340
4348
|
]);
|
|
4341
4349
|
function clampCount(value, fallback) {
|
|
4342
4350
|
if (!value || !Number.isFinite(value) || value <= 0) {
|
|
@@ -4497,6 +4505,40 @@ function normalizeDependencyUnlockSample(sample) {
|
|
|
4497
4505
|
frameId: sample.frameId === void 0 ? void 0 : assertIdentifier4("dependencyUnlock.frameId", sample.frameId)
|
|
4498
4506
|
};
|
|
4499
4507
|
}
|
|
4508
|
+
function normalizePipelinePhaseSample(sample) {
|
|
4509
|
+
if (sample.signal !== void 0 && !isAbortSignalLike2(sample.signal)) {
|
|
4510
|
+
throw new Error("pipelinePhase.signal must be an AbortSignal when provided.");
|
|
4511
|
+
}
|
|
4512
|
+
const snapshotAgeFrames = readNonNegativeNumber2(
|
|
4513
|
+
"pipelinePhase.snapshotAgeFrames",
|
|
4514
|
+
sample.snapshotAgeFrames
|
|
4515
|
+
);
|
|
4516
|
+
if (snapshotAgeFrames !== void 0 && !Number.isInteger(snapshotAgeFrames)) {
|
|
4517
|
+
throw new Error(
|
|
4518
|
+
"pipelinePhase.snapshotAgeFrames must be an integer greater than or equal to zero."
|
|
4519
|
+
);
|
|
4520
|
+
}
|
|
4521
|
+
return {
|
|
4522
|
+
owner: assertIdentifier4("pipelinePhase.owner", sample.owner),
|
|
4523
|
+
pipeline: assertEnumValue4(
|
|
4524
|
+
"pipelinePhase.pipeline",
|
|
4525
|
+
sample.pipeline,
|
|
4526
|
+
gpuPipelinePhases
|
|
4527
|
+
),
|
|
4528
|
+
stage: assertIdentifier4("pipelinePhase.stage", sample.stage),
|
|
4529
|
+
frameId: sample.frameId === void 0 ? void 0 : assertIdentifier4("pipelinePhase.frameId", sample.frameId),
|
|
4530
|
+
durationMs: readNonNegativeNumber2(
|
|
4531
|
+
"pipelinePhase.durationMs",
|
|
4532
|
+
sample.durationMs
|
|
4533
|
+
),
|
|
4534
|
+
snapshotFrameId: sample.snapshotFrameId === void 0 ? void 0 : assertIdentifier4("pipelinePhase.snapshotFrameId", sample.snapshotFrameId),
|
|
4535
|
+
snapshotAgeFrames,
|
|
4536
|
+
snapshotAgeMs: readNonNegativeNumber2(
|
|
4537
|
+
"pipelinePhase.snapshotAgeMs",
|
|
4538
|
+
sample.snapshotAgeMs
|
|
4539
|
+
)
|
|
4540
|
+
};
|
|
4541
|
+
}
|
|
4500
4542
|
function createGpuDebugSession(options = {}) {
|
|
4501
4543
|
const settings = {
|
|
4502
4544
|
enabled: options.enabled ?? DEFAULT_OPTIONS.enabled,
|
|
@@ -4516,6 +4558,10 @@ function createGpuDebugSession(options = {}) {
|
|
|
4516
4558
|
options.maxRetainedDependencyUnlockSamples,
|
|
4517
4559
|
DEFAULT_OPTIONS.maxRetainedDependencyUnlockSamples
|
|
4518
4560
|
),
|
|
4561
|
+
maxRetainedPipelinePhaseSamples: clampCount(
|
|
4562
|
+
options.maxRetainedPipelinePhaseSamples,
|
|
4563
|
+
DEFAULT_OPTIONS.maxRetainedPipelinePhaseSamples
|
|
4564
|
+
),
|
|
4519
4565
|
maxRetainedFrameSamples: clampCount(
|
|
4520
4566
|
options.maxRetainedFrameSamples,
|
|
4521
4567
|
DEFAULT_OPTIONS.maxRetainedFrameSamples
|
|
@@ -4533,6 +4579,7 @@ function createGpuDebugSession(options = {}) {
|
|
|
4533
4579
|
const readyLaneSamples = [];
|
|
4534
4580
|
const dispatchSamples = [];
|
|
4535
4581
|
const dependencyUnlockSamples = [];
|
|
4582
|
+
const pipelinePhaseSamples = [];
|
|
4536
4583
|
const frameSamples = [];
|
|
4537
4584
|
let peakTrackedBytes = 0;
|
|
4538
4585
|
const totalTrackedBytes = () => [...allocations.values()].reduce((total, allocation) => total + allocation.sizeBytes, 0);
|
|
@@ -4684,6 +4731,66 @@ function createGpuDebugSession(options = {}) {
|
|
|
4684
4731
|
)
|
|
4685
4732
|
};
|
|
4686
4733
|
};
|
|
4734
|
+
const buildPipelineSnapshot = () => {
|
|
4735
|
+
const durations = pipelinePhaseSamples.map((sample) => sample.durationMs).filter((value) => value !== void 0);
|
|
4736
|
+
const snapshotAgeMsValues = pipelinePhaseSamples.map((sample) => sample.snapshotAgeMs).filter((value) => value !== void 0);
|
|
4737
|
+
const snapshotAgeFrameValues = pipelinePhaseSamples.map((sample) => sample.snapshotAgeFrames).filter((value) => value !== void 0);
|
|
4738
|
+
const byPipeline = /* @__PURE__ */ new Map();
|
|
4739
|
+
for (const sample of pipelinePhaseSamples) {
|
|
4740
|
+
const bucket = byPipeline.get(sample.pipeline) ?? {
|
|
4741
|
+
pipeline: sample.pipeline,
|
|
4742
|
+
sampleCount: 0,
|
|
4743
|
+
totalDurationMs: 0,
|
|
4744
|
+
durationValues: [],
|
|
4745
|
+
snapshotAgeMsValues: [],
|
|
4746
|
+
snapshotAgeFramesValues: []
|
|
4747
|
+
};
|
|
4748
|
+
bucket.sampleCount += 1;
|
|
4749
|
+
bucket.totalDurationMs += sample.durationMs ?? 0;
|
|
4750
|
+
if (sample.durationMs !== void 0) {
|
|
4751
|
+
bucket.durationValues.push(sample.durationMs);
|
|
4752
|
+
}
|
|
4753
|
+
if (sample.snapshotAgeMs !== void 0) {
|
|
4754
|
+
bucket.snapshotAgeMsValues.push(sample.snapshotAgeMs);
|
|
4755
|
+
}
|
|
4756
|
+
if (sample.snapshotAgeFrames !== void 0) {
|
|
4757
|
+
bucket.snapshotAgeFramesValues.push(sample.snapshotAgeFrames);
|
|
4758
|
+
}
|
|
4759
|
+
byPipeline.set(sample.pipeline, bucket);
|
|
4760
|
+
}
|
|
4761
|
+
const hottestStages = pipelinePhaseSamples.map((sample) => ({
|
|
4762
|
+
owner: sample.owner,
|
|
4763
|
+
pipeline: sample.pipeline,
|
|
4764
|
+
stage: sample.stage,
|
|
4765
|
+
frameId: sample.frameId,
|
|
4766
|
+
durationMs: sample.durationMs,
|
|
4767
|
+
snapshotFrameId: sample.snapshotFrameId,
|
|
4768
|
+
snapshotAgeFrames: sample.snapshotAgeFrames,
|
|
4769
|
+
snapshotAgeMs: sample.snapshotAgeMs
|
|
4770
|
+
})).sort((left, right) => {
|
|
4771
|
+
const leftScore = left.durationMs ?? left.snapshotAgeMs ?? left.snapshotAgeFrames ?? 0;
|
|
4772
|
+
const rightScore = right.durationMs ?? right.snapshotAgeMs ?? right.snapshotAgeFrames ?? 0;
|
|
4773
|
+
return rightScore - leftScore;
|
|
4774
|
+
}).slice(0, 5);
|
|
4775
|
+
return {
|
|
4776
|
+
sampleCount: pipelinePhaseSamples.length,
|
|
4777
|
+
totalDurationMs: durations.reduce((total, value) => total + value, 0),
|
|
4778
|
+
averageDurationMs: average(durations),
|
|
4779
|
+
averageSnapshotAgeMs: average(snapshotAgeMsValues),
|
|
4780
|
+
maxSnapshotAgeMs: snapshotAgeMsValues.length > 0 ? Math.max(...snapshotAgeMsValues) : void 0,
|
|
4781
|
+
maxSnapshotAgeFrames: snapshotAgeFrameValues.length > 0 ? Math.max(...snapshotAgeFrameValues) : void 0,
|
|
4782
|
+
byPipeline: [...byPipeline.values()].map((bucket) => ({
|
|
4783
|
+
pipeline: bucket.pipeline,
|
|
4784
|
+
sampleCount: bucket.sampleCount,
|
|
4785
|
+
totalDurationMs: bucket.totalDurationMs,
|
|
4786
|
+
averageDurationMs: average(bucket.durationValues),
|
|
4787
|
+
averageSnapshotAgeMs: average(bucket.snapshotAgeMsValues),
|
|
4788
|
+
maxSnapshotAgeMs: bucket.snapshotAgeMsValues.length > 0 ? Math.max(...bucket.snapshotAgeMsValues) : void 0,
|
|
4789
|
+
maxSnapshotAgeFrames: bucket.snapshotAgeFramesValues.length > 0 ? Math.max(...bucket.snapshotAgeFramesValues) : void 0
|
|
4790
|
+
})).sort((left, right) => right.totalDurationMs - left.totalDurationMs),
|
|
4791
|
+
hottestStages
|
|
4792
|
+
};
|
|
4793
|
+
};
|
|
4687
4794
|
return {
|
|
4688
4795
|
isEnabled() {
|
|
4689
4796
|
return enabled;
|
|
@@ -4754,6 +4861,17 @@ function createGpuDebugSession(options = {}) {
|
|
|
4754
4861
|
);
|
|
4755
4862
|
return true;
|
|
4756
4863
|
},
|
|
4864
|
+
recordPipelinePhase(sample) {
|
|
4865
|
+
if (!enabled || sample.signal?.aborted === true) {
|
|
4866
|
+
return false;
|
|
4867
|
+
}
|
|
4868
|
+
pipelinePhaseSamples.push(normalizePipelinePhaseSample(sample));
|
|
4869
|
+
trimHistory(
|
|
4870
|
+
pipelinePhaseSamples,
|
|
4871
|
+
settings.maxRetainedPipelinePhaseSamples
|
|
4872
|
+
);
|
|
4873
|
+
return true;
|
|
4874
|
+
},
|
|
4757
4875
|
recordFrame(sample) {
|
|
4758
4876
|
if (!enabled || sample.signal?.aborted === true) {
|
|
4759
4877
|
return false;
|
|
@@ -4795,6 +4913,7 @@ function createGpuDebugSession(options = {}) {
|
|
|
4795
4913
|
averageGpuBusyMs: average(gpuBusyTimes)
|
|
4796
4914
|
},
|
|
4797
4915
|
dag: buildDagSnapshot(),
|
|
4916
|
+
pipeline: buildPipelineSnapshot(),
|
|
4798
4917
|
limitations: LIMITATIONS
|
|
4799
4918
|
};
|
|
4800
4919
|
return snapshot;
|
|
@@ -4806,6 +4925,7 @@ function createGpuDebugSession(options = {}) {
|
|
|
4806
4925
|
readyLaneSamples.splice(0, readyLaneSamples.length);
|
|
4807
4926
|
dispatchSamples.splice(0, dispatchSamples.length);
|
|
4808
4927
|
dependencyUnlockSamples.splice(0, dependencyUnlockSamples.length);
|
|
4928
|
+
pipelinePhaseSamples.splice(0, pipelinePhaseSamples.length);
|
|
4809
4929
|
frameSamples.splice(0, frameSamples.length);
|
|
4810
4930
|
peakTrackedBytes = 0;
|
|
4811
4931
|
}
|
|
@@ -6550,7 +6670,145 @@ function buildDemoDom(root, options) {
|
|
|
6550
6670
|
sceneNotes: root.querySelector("#sceneNotes")
|
|
6551
6671
|
};
|
|
6552
6672
|
}
|
|
6553
|
-
function
|
|
6673
|
+
function buildSceneSnapshot(state, shipModel) {
|
|
6674
|
+
return Object.freeze({
|
|
6675
|
+
focus: state.focus,
|
|
6676
|
+
frame: state.frame,
|
|
6677
|
+
time: state.time,
|
|
6678
|
+
stress: state.stress,
|
|
6679
|
+
collisions: state.contactCount,
|
|
6680
|
+
collisionCount: state.collisionCount,
|
|
6681
|
+
collisionFlash: state.collisionFlash,
|
|
6682
|
+
sprays: Object.freeze(
|
|
6683
|
+
state.sprays.map(
|
|
6684
|
+
(spray) => Object.freeze({
|
|
6685
|
+
life: spray.life,
|
|
6686
|
+
position: Object.freeze({ ...spray.position }),
|
|
6687
|
+
velocity: Object.freeze({ ...spray.velocity })
|
|
6688
|
+
})
|
|
6689
|
+
)
|
|
6690
|
+
),
|
|
6691
|
+
ships: Object.freeze(
|
|
6692
|
+
state.ships.map(
|
|
6693
|
+
(ship) => Object.freeze({
|
|
6694
|
+
id: ship.id,
|
|
6695
|
+
position: Object.freeze({ ...ship.position }),
|
|
6696
|
+
velocity: Object.freeze({ ...ship.velocity }),
|
|
6697
|
+
rotationY: ship.rotationY,
|
|
6698
|
+
angularVelocity: ship.angularVelocity,
|
|
6699
|
+
tint: Object.freeze({ ...ship.tint })
|
|
6700
|
+
})
|
|
6701
|
+
)
|
|
6702
|
+
),
|
|
6703
|
+
waveImpulses: Object.freeze(
|
|
6704
|
+
state.waveImpulses.map(
|
|
6705
|
+
(impulse) => Object.freeze({
|
|
6706
|
+
x: impulse.x,
|
|
6707
|
+
z: impulse.z,
|
|
6708
|
+
strength: impulse.strength,
|
|
6709
|
+
radius: impulse.radius,
|
|
6710
|
+
life: impulse.life
|
|
6711
|
+
})
|
|
6712
|
+
)
|
|
6713
|
+
),
|
|
6714
|
+
physics: Object.freeze({
|
|
6715
|
+
profile: state.physics.profile,
|
|
6716
|
+
plan: state.physics.plan,
|
|
6717
|
+
manifest: state.physics.manifest,
|
|
6718
|
+
snapshot: state.physics.snapshot,
|
|
6719
|
+
shipPhysics: shipModel?.physics ?? null
|
|
6720
|
+
})
|
|
6721
|
+
});
|
|
6722
|
+
}
|
|
6723
|
+
function resolveSceneDescription(state, options, shipModel) {
|
|
6724
|
+
const scene = buildSceneSnapshot(state, shipModel);
|
|
6725
|
+
if (typeof options.describeState !== "function") {
|
|
6726
|
+
return { scene, description: null };
|
|
6727
|
+
}
|
|
6728
|
+
const description = options.describeState(state.packageState, scene) ?? null;
|
|
6729
|
+
return { scene, description };
|
|
6730
|
+
}
|
|
6731
|
+
function updatePackageState(state, options, shipModel, dt) {
|
|
6732
|
+
if (typeof options.updateState !== "function") {
|
|
6733
|
+
return;
|
|
6734
|
+
}
|
|
6735
|
+
const scene = buildSceneSnapshot(state, shipModel);
|
|
6736
|
+
const nextState = options.updateState(state.packageState, scene, dt);
|
|
6737
|
+
if (typeof nextState !== "undefined") {
|
|
6738
|
+
state.packageState = nextState;
|
|
6739
|
+
}
|
|
6740
|
+
}
|
|
6741
|
+
function normalizeColorOverride(color, fallback) {
|
|
6742
|
+
if (!color || typeof color !== "object") {
|
|
6743
|
+
return fallback;
|
|
6744
|
+
}
|
|
6745
|
+
return {
|
|
6746
|
+
r: typeof color.r === "number" ? color.r : fallback.r,
|
|
6747
|
+
g: typeof color.g === "number" ? color.g : fallback.g,
|
|
6748
|
+
b: typeof color.b === "number" ? color.b : fallback.b
|
|
6749
|
+
};
|
|
6750
|
+
}
|
|
6751
|
+
function readVisualNumber(value, fallback) {
|
|
6752
|
+
return typeof value === "number" && Number.isFinite(value) ? value : fallback;
|
|
6753
|
+
}
|
|
6754
|
+
function resolveVisualConfig(nearLighting, lightingSnapshot, customVisuals = {}) {
|
|
6755
|
+
const premiumShadows = nearLighting.primaryShadowSource === "ray-traced-primary";
|
|
6756
|
+
const defaults = {
|
|
6757
|
+
skyTop: premiumShadows ? "#f0f7fb" : "#e8f1f7",
|
|
6758
|
+
skyMid: premiumShadows ? "#c7d9e5" : "#b9ceda",
|
|
6759
|
+
skyBottom: premiumShadows ? "#84a7bd" : "#7b9bb0",
|
|
6760
|
+
seaTop: premiumShadows ? "#235064" : "#264c5f",
|
|
6761
|
+
seaMid: premiumShadows ? "#153e53" : "#173d4f",
|
|
6762
|
+
seaBottom: "#0b2433",
|
|
6763
|
+
sunCore: "rgba(255, 244, 210, 0.9)",
|
|
6764
|
+
reflectionStrength: lightingSnapshot.currentLevel.config.reflectionStrength,
|
|
6765
|
+
shadowAccent: lightingSnapshot.currentLevel.config.shadowStrength,
|
|
6766
|
+
waveAmplitude: 1,
|
|
6767
|
+
waveDirection: { x: 0.86, z: 0.34 },
|
|
6768
|
+
wavePhaseSpeed: 1,
|
|
6769
|
+
wakeStrength: 0.24,
|
|
6770
|
+
wakeLength: 15,
|
|
6771
|
+
collisionRippleStrength: 0.34,
|
|
6772
|
+
waterNear: { r: 0.12, g: 0.36, b: 0.46 },
|
|
6773
|
+
waterFar: { r: 0.28, g: 0.56, b: 0.68 },
|
|
6774
|
+
harborWall: { r: 0.48, g: 0.4, b: 0.32 },
|
|
6775
|
+
harborDeck: { r: 0.5, g: 0.34, b: 0.22 },
|
|
6776
|
+
harborTower: { r: 0.34, g: 0.32, b: 0.36 },
|
|
6777
|
+
flagColor: { r: 0.76, g: 0.24, b: 0.18 },
|
|
6778
|
+
flagMotion: 1
|
|
6779
|
+
};
|
|
6780
|
+
return {
|
|
6781
|
+
skyTop: typeof customVisuals.skyTop === "string" ? customVisuals.skyTop : defaults.skyTop,
|
|
6782
|
+
skyMid: typeof customVisuals.skyMid === "string" ? customVisuals.skyMid : defaults.skyMid,
|
|
6783
|
+
skyBottom: typeof customVisuals.skyBottom === "string" ? customVisuals.skyBottom : defaults.skyBottom,
|
|
6784
|
+
seaTop: typeof customVisuals.seaTop === "string" ? customVisuals.seaTop : defaults.seaTop,
|
|
6785
|
+
seaMid: typeof customVisuals.seaMid === "string" ? customVisuals.seaMid : defaults.seaMid,
|
|
6786
|
+
seaBottom: typeof customVisuals.seaBottom === "string" ? customVisuals.seaBottom : defaults.seaBottom,
|
|
6787
|
+
sunCore: typeof customVisuals.sunCore === "string" ? customVisuals.sunCore : defaults.sunCore,
|
|
6788
|
+
reflectionStrength: readVisualNumber(
|
|
6789
|
+
customVisuals.reflectionStrength,
|
|
6790
|
+
defaults.reflectionStrength
|
|
6791
|
+
),
|
|
6792
|
+
shadowAccent: readVisualNumber(customVisuals.shadowAccent, defaults.shadowAccent),
|
|
6793
|
+
waveAmplitude: readVisualNumber(customVisuals.waveAmplitude, defaults.waveAmplitude),
|
|
6794
|
+
waveDirection: customVisuals.waveDirection && typeof customVisuals.waveDirection.x === "number" && typeof customVisuals.waveDirection.z === "number" ? { x: customVisuals.waveDirection.x, z: customVisuals.waveDirection.z } : defaults.waveDirection,
|
|
6795
|
+
wavePhaseSpeed: readVisualNumber(customVisuals.wavePhaseSpeed, defaults.wavePhaseSpeed),
|
|
6796
|
+
wakeStrength: readVisualNumber(customVisuals.wakeStrength, defaults.wakeStrength),
|
|
6797
|
+
wakeLength: readVisualNumber(customVisuals.wakeLength, defaults.wakeLength),
|
|
6798
|
+
collisionRippleStrength: readVisualNumber(
|
|
6799
|
+
customVisuals.collisionRippleStrength,
|
|
6800
|
+
defaults.collisionRippleStrength
|
|
6801
|
+
),
|
|
6802
|
+
waterNear: normalizeColorOverride(customVisuals.waterNear, defaults.waterNear),
|
|
6803
|
+
waterFar: normalizeColorOverride(customVisuals.waterFar, defaults.waterFar),
|
|
6804
|
+
harborWall: normalizeColorOverride(customVisuals.harborWall, defaults.harborWall),
|
|
6805
|
+
harborDeck: normalizeColorOverride(customVisuals.harborDeck, defaults.harborDeck),
|
|
6806
|
+
harborTower: normalizeColorOverride(customVisuals.harborTower, defaults.harborTower),
|
|
6807
|
+
flagColor: normalizeColorOverride(customVisuals.flagColor, defaults.flagColor),
|
|
6808
|
+
flagMotion: readVisualNumber(customVisuals.flagMotion, defaults.flagMotion)
|
|
6809
|
+
};
|
|
6810
|
+
}
|
|
6811
|
+
function buildClothSurface(model, state, meshDetail, visuals) {
|
|
6554
6812
|
const clothPlan = createClothRepresentationPlan({
|
|
6555
6813
|
garmentId: "shore-flag",
|
|
6556
6814
|
kind: state.focus === "cloth" ? "flag" : clothGarmentKinds[0],
|
|
@@ -6576,12 +6834,12 @@ function buildClothSurface(model, state, meshDetail) {
|
|
|
6576
6834
|
for (let column = 0; column < cols; column += 1) {
|
|
6577
6835
|
const u = column / (cols - 1);
|
|
6578
6836
|
const v = row / (rows - 1);
|
|
6579
|
-
const gust = Math.sin(time * 1.9 + v * 3.2 + u * 2.1) * continuity.broadMotionFloor;
|
|
6580
|
-
const wrinkle = Math.sin(time * 4.4 + u * 9.2 + v * 5.6) * continuity.wrinkleFloor * 0.22;
|
|
6837
|
+
const gust = Math.sin(time * 1.9 + v * 3.2 + u * 2.1) * continuity.broadMotionFloor * visuals.flagMotion;
|
|
6838
|
+
const wrinkle = Math.sin(time * 4.4 + u * 9.2 + v * 5.6) * continuity.wrinkleFloor * 0.22 * Math.max(0.55, visuals.flagMotion);
|
|
6581
6839
|
const x = origin.x + u * 1.8 + gust * 0.55 * (u * 0.9);
|
|
6582
6840
|
const y = origin.y - height * v + wrinkle * 0.2;
|
|
6583
6841
|
const z = origin.z + width * u + gust * 0.72 * (u * 0.85);
|
|
6584
|
-
const flap = Math.cos(time * 2.7 + u * 7.4 + v * 3.8) * continuity.broadMotionFloor * 0.28;
|
|
6842
|
+
const flap = Math.cos(time * 2.7 + u * 7.4 + v * 3.8) * continuity.broadMotionFloor * 0.28 * visuals.flagMotion;
|
|
6585
6843
|
positions.push(vec3(x + flap, y, z));
|
|
6586
6844
|
}
|
|
6587
6845
|
}
|
|
@@ -6599,15 +6857,74 @@ function buildClothSurface(model, state, meshDetail) {
|
|
|
6599
6857
|
band,
|
|
6600
6858
|
representation,
|
|
6601
6859
|
continuity,
|
|
6860
|
+
color: visuals.flagColor,
|
|
6602
6861
|
positions,
|
|
6603
6862
|
indices,
|
|
6604
6863
|
grid: { rows, cols }
|
|
6605
6864
|
};
|
|
6606
6865
|
}
|
|
6607
|
-
function
|
|
6608
|
-
|
|
6866
|
+
function resolveWaveDirection(state) {
|
|
6867
|
+
const direction = state.demoVisuals?.waveDirection;
|
|
6868
|
+
if (direction && typeof direction === "object" && typeof direction.x === "number" && typeof direction.z === "number") {
|
|
6869
|
+
return normalizeVec3(vec3(direction.x, 0, direction.z));
|
|
6870
|
+
}
|
|
6871
|
+
return normalizeVec3(vec3(0.86, 0, 0.34));
|
|
6609
6872
|
}
|
|
6610
|
-
function
|
|
6873
|
+
function sampleShipWake(state, x, z, time) {
|
|
6874
|
+
const wakeStrength = readVisualNumber(state.demoVisuals?.wakeStrength, 0.24);
|
|
6875
|
+
const wakeLength = readVisualNumber(state.demoVisuals?.wakeLength, 15);
|
|
6876
|
+
let total = 0;
|
|
6877
|
+
for (const ship of state.ships) {
|
|
6878
|
+
const speed = Math.hypot(ship.velocity.x, ship.velocity.z);
|
|
6879
|
+
if (speed <= 0.05) {
|
|
6880
|
+
continue;
|
|
6881
|
+
}
|
|
6882
|
+
const direction = normalizeVec3(vec3(ship.velocity.x, 0, ship.velocity.z));
|
|
6883
|
+
const behind = scaleVec3(direction, -1);
|
|
6884
|
+
const lateral = vec3(-direction.z, 0, direction.x);
|
|
6885
|
+
const delta = vec3(x - ship.position.x, 0, z - ship.position.z);
|
|
6886
|
+
const along = dotVec3(delta, behind);
|
|
6887
|
+
if (along < 0 || along > wakeLength) {
|
|
6888
|
+
continue;
|
|
6889
|
+
}
|
|
6890
|
+
const cross = Math.abs(dotVec3(delta, lateral));
|
|
6891
|
+
const width = 0.9 + along * 0.2;
|
|
6892
|
+
if (cross > width * 3.2) {
|
|
6893
|
+
continue;
|
|
6894
|
+
}
|
|
6895
|
+
const envelope = Math.exp(-along * 0.14) * Math.exp(-(cross * cross / Math.max(0.4, width * width * 2.4)));
|
|
6896
|
+
total += Math.sin(along * 1.6 - time * 4.2) * speed * wakeStrength * envelope;
|
|
6897
|
+
}
|
|
6898
|
+
return total;
|
|
6899
|
+
}
|
|
6900
|
+
function sampleWaveImpulses(state, x, z, time) {
|
|
6901
|
+
const rippleStrength = readVisualNumber(state.demoVisuals?.collisionRippleStrength, 0.34);
|
|
6902
|
+
let total = 0;
|
|
6903
|
+
for (const impulse of state.waveImpulses) {
|
|
6904
|
+
const dx = x - impulse.x;
|
|
6905
|
+
const dz = z - impulse.z;
|
|
6906
|
+
const distance = Math.hypot(dx, dz);
|
|
6907
|
+
const radius = impulse.radius + (1 - impulse.life) * 4.8;
|
|
6908
|
+
if (distance > radius * 2.8) {
|
|
6909
|
+
continue;
|
|
6910
|
+
}
|
|
6911
|
+
const phase = distance * 1.8 - (1 - impulse.life) * 10 - time * 0.4;
|
|
6912
|
+
const envelope = Math.exp(-distance / Math.max(0.1, radius)) * impulse.life;
|
|
6913
|
+
total += Math.sin(phase) * impulse.strength * rippleStrength * envelope * 0.18;
|
|
6914
|
+
}
|
|
6915
|
+
return total;
|
|
6916
|
+
}
|
|
6917
|
+
function sampleWave(state, x, z, time) {
|
|
6918
|
+
const direction = resolveWaveDirection(state);
|
|
6919
|
+
const lateral = vec3(-direction.z, 0, direction.x);
|
|
6920
|
+
const along = x * direction.x + z * direction.z;
|
|
6921
|
+
const cross = x * lateral.x + z * lateral.z;
|
|
6922
|
+
const phaseSpeed = readVisualNumber(state.demoVisuals?.wavePhaseSpeed, 1);
|
|
6923
|
+
const amplitude = readVisualNumber(state.demoVisuals?.waveAmplitude, 1);
|
|
6924
|
+
const base = Math.sin(along * 0.22 - time * 1.12 * phaseSpeed) * 0.42 + Math.cos(along * 0.11 + cross * 0.07 - time * 0.78 * phaseSpeed) * 0.26 + Math.sin(cross * 0.19 - time * 1.34 * phaseSpeed) * 0.16;
|
|
6925
|
+
return base * amplitude + sampleShipWake(state, x, z, time) + sampleWaveImpulses(state, x, z, time);
|
|
6926
|
+
}
|
|
6927
|
+
function buildWaterBands(state, fluidDetail, visuals) {
|
|
6611
6928
|
const fluidPlan = createFluidRepresentationPlan({
|
|
6612
6929
|
fluidBodyId: "harbor",
|
|
6613
6930
|
kind: state.focus === "fluid" ? "ocean" : fluidBodyKinds[0],
|
|
@@ -6640,7 +6957,7 @@ function buildWaterBands(state, fluidDetail) {
|
|
|
6640
6957
|
const v = row / (rows - 1);
|
|
6641
6958
|
const x = originX + bandSpec.width * u;
|
|
6642
6959
|
const z = originZ + bandSpec.depth * v;
|
|
6643
|
-
const y = bandSpec.y + sampleWave(x, z, state.time) * continuity.amplitudeFloor * (bandSpec.band === "near" ? 0.9 : bandSpec.band === "mid" ? 0.55 : 0.3);
|
|
6960
|
+
const y = bandSpec.y + sampleWave(state, x, z, state.time) * continuity.amplitudeFloor * (bandSpec.band === "near" ? 0.9 : bandSpec.band === "mid" ? 0.55 : 0.3);
|
|
6644
6961
|
positions.push(vec3(x, y, z));
|
|
6645
6962
|
}
|
|
6646
6963
|
}
|
|
@@ -6661,7 +6978,15 @@ function buildWaterBands(state, fluidDetail) {
|
|
|
6661
6978
|
cols,
|
|
6662
6979
|
positions,
|
|
6663
6980
|
indices,
|
|
6664
|
-
color: bandSpec.band === "near" ?
|
|
6981
|
+
color: bandSpec.band === "near" ? visuals.waterNear : bandSpec.band === "mid" ? {
|
|
6982
|
+
r: mix(visuals.waterNear.r, visuals.waterFar.r, 0.4),
|
|
6983
|
+
g: mix(visuals.waterNear.g, visuals.waterFar.g, 0.4),
|
|
6984
|
+
b: mix(visuals.waterNear.b, visuals.waterFar.b, 0.4)
|
|
6985
|
+
} : bandSpec.band === "far" ? visuals.waterFar : {
|
|
6986
|
+
r: mix(visuals.waterFar.r, 0.76, 0.2),
|
|
6987
|
+
g: mix(visuals.waterFar.g, 0.78, 0.2),
|
|
6988
|
+
b: mix(visuals.waterFar.b, 0.82, 0.2)
|
|
6989
|
+
}
|
|
6665
6990
|
});
|
|
6666
6991
|
}
|
|
6667
6992
|
return { fluidPlan, bandMeshes };
|
|
@@ -6700,6 +7025,9 @@ function createSceneState(options) {
|
|
|
6700
7025
|
clothDetail,
|
|
6701
7026
|
lightingDetail,
|
|
6702
7027
|
debugSession,
|
|
7028
|
+
packageState: void 0,
|
|
7029
|
+
demoDescription: null,
|
|
7030
|
+
demoVisuals: null,
|
|
6703
7031
|
time: 0,
|
|
6704
7032
|
lastTimeMs: null,
|
|
6705
7033
|
paused: false,
|
|
@@ -6727,7 +7055,9 @@ function createSceneState(options) {
|
|
|
6727
7055
|
}
|
|
6728
7056
|
],
|
|
6729
7057
|
sprays: [],
|
|
7058
|
+
waveImpulses: [],
|
|
6730
7059
|
frame: 0,
|
|
7060
|
+
contactCount: 0,
|
|
6731
7061
|
collisionCount: 0,
|
|
6732
7062
|
collisionFlash: 0,
|
|
6733
7063
|
physics: {
|
|
@@ -6742,24 +7072,24 @@ function createSceneState(options) {
|
|
|
6742
7072
|
function setListContent(element, values) {
|
|
6743
7073
|
element.innerHTML = values.map((value) => `<li>${value}</li>`).join("");
|
|
6744
7074
|
}
|
|
6745
|
-
function drawSkyAndShore(ctx, canvas, state, nearLighting, reflectionStrength, shadowStrength) {
|
|
7075
|
+
function drawSkyAndShore(ctx, canvas, state, nearLighting, reflectionStrength, shadowStrength, visuals) {
|
|
6746
7076
|
const premiumShadows = nearLighting.primaryShadowSource === "ray-traced-primary";
|
|
6747
7077
|
const sky = ctx.createLinearGradient(0, 0, 0, canvas.height * 0.5);
|
|
6748
|
-
sky.addColorStop(0,
|
|
6749
|
-
sky.addColorStop(0.6,
|
|
6750
|
-
sky.addColorStop(1,
|
|
7078
|
+
sky.addColorStop(0, visuals.skyTop);
|
|
7079
|
+
sky.addColorStop(0.6, visuals.skyMid);
|
|
7080
|
+
sky.addColorStop(1, visuals.skyBottom);
|
|
6751
7081
|
ctx.fillStyle = sky;
|
|
6752
7082
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
6753
7083
|
const shoreline = ctx.createLinearGradient(0, canvas.height * 0.45, 0, canvas.height);
|
|
6754
|
-
shoreline.addColorStop(0,
|
|
6755
|
-
shoreline.addColorStop(0.52,
|
|
6756
|
-
shoreline.addColorStop(1,
|
|
7084
|
+
shoreline.addColorStop(0, visuals.seaTop);
|
|
7085
|
+
shoreline.addColorStop(0.52, visuals.seaMid);
|
|
7086
|
+
shoreline.addColorStop(1, visuals.seaBottom);
|
|
6757
7087
|
ctx.fillStyle = shoreline;
|
|
6758
7088
|
ctx.fillRect(0, canvas.height * 0.45, canvas.width, canvas.height * 0.55);
|
|
6759
7089
|
const sunX = mix(canvas.width * 0.16, canvas.width * 0.84, (Math.sin(state.time * 0.12) + 1) * 0.5);
|
|
6760
7090
|
const sunY = canvas.height * 0.14 + Math.cos(state.time * 0.12) * 22;
|
|
6761
7091
|
const sun = ctx.createRadialGradient(sunX, sunY, 10, sunX, sunY, 90);
|
|
6762
|
-
sun.addColorStop(0,
|
|
7092
|
+
sun.addColorStop(0, visuals.sunCore);
|
|
6763
7093
|
sun.addColorStop(1, "rgba(255, 244, 210, 0)");
|
|
6764
7094
|
ctx.fillStyle = sun;
|
|
6765
7095
|
ctx.beginPath();
|
|
@@ -6835,27 +7165,27 @@ function renderProjectedShadow(ctx, worldPoints, camera, viewport, lightDir, opt
|
|
|
6835
7165
|
ctx.fill();
|
|
6836
7166
|
ctx.restore();
|
|
6837
7167
|
}
|
|
6838
|
-
function pushHarborGeometry(camera, viewport, triangles) {
|
|
7168
|
+
function pushHarborGeometry(camera, viewport, triangles, visuals) {
|
|
6839
7169
|
const harborObjects = [
|
|
6840
7170
|
{
|
|
6841
7171
|
position: vec3(-8.2, 1.1, -0.9),
|
|
6842
7172
|
rotationY: -0.16,
|
|
6843
7173
|
scale: { x: 5.4, y: 2.4, z: 4.2 },
|
|
6844
|
-
color:
|
|
7174
|
+
color: visuals.harborWall,
|
|
6845
7175
|
accent: 0.06
|
|
6846
7176
|
},
|
|
6847
7177
|
{
|
|
6848
7178
|
position: vec3(-5.7, 0.45, 1.4),
|
|
6849
7179
|
rotationY: -0.08,
|
|
6850
7180
|
scale: { x: 6.8, y: 0.3, z: 2.1 },
|
|
6851
|
-
color:
|
|
7181
|
+
color: visuals.harborDeck,
|
|
6852
7182
|
accent: 0.04
|
|
6853
7183
|
},
|
|
6854
7184
|
{
|
|
6855
7185
|
position: vec3(-10.4, 0.28, 0.8),
|
|
6856
7186
|
rotationY: 0.22,
|
|
6857
7187
|
scale: { x: 1.2, y: 0.9, z: 1.2 },
|
|
6858
|
-
color:
|
|
7188
|
+
color: visuals.harborTower,
|
|
6859
7189
|
accent: 0.02
|
|
6860
7190
|
}
|
|
6861
7191
|
];
|
|
@@ -6935,7 +7265,7 @@ function renderClothAccent(ctx, cloth, camera, viewport) {
|
|
|
6935
7265
|
cloth.grid.rows * cloth.grid.cols - 1,
|
|
6936
7266
|
(cloth.grid.rows - 1) * cloth.grid.cols
|
|
6937
7267
|
];
|
|
6938
|
-
ctx.fillStyle =
|
|
7268
|
+
ctx.fillStyle = colorToRgba(cloth.color, 0.95);
|
|
6939
7269
|
for (const index of borderIndices) {
|
|
6940
7270
|
const point = projected[index];
|
|
6941
7271
|
if (!point) {
|
|
@@ -6996,12 +7326,13 @@ function updateShips(state, dt, shipModel) {
|
|
|
6996
7326
|
const physics = shipModel.physics;
|
|
6997
7327
|
const halfExtents = physics.halfExtents ?? [1.35, 0.95, 3.9];
|
|
6998
7328
|
let collided = false;
|
|
7329
|
+
state.contactCount = 0;
|
|
6999
7330
|
for (const ship of state.ships) {
|
|
7000
7331
|
ship.position = addVec3(ship.position, scaleVec3(ship.velocity, dt));
|
|
7001
7332
|
ship.rotationY += ship.angularVelocity * dt;
|
|
7002
7333
|
ship.velocity = scaleVec3(ship.velocity, 1 - (physics.linearDamping ?? 0.04) * dt);
|
|
7003
7334
|
ship.angularVelocity *= 1 - (physics.angularDamping ?? 0.08) * dt;
|
|
7004
|
-
ship.position.y = sampleWave(ship.position.x, ship.position.z, state.time) * 0.22 + (physics.waterline ?? 0.42);
|
|
7335
|
+
ship.position.y = sampleWave(state, ship.position.x, ship.position.z, state.time) * 0.22 + (physics.waterline ?? 0.42);
|
|
7005
7336
|
if (Math.abs(ship.position.x) > 10) {
|
|
7006
7337
|
ship.velocity.x *= -1;
|
|
7007
7338
|
ship.angularVelocity *= -1;
|
|
@@ -7028,11 +7359,25 @@ function updateShips(state, dt, shipModel) {
|
|
|
7028
7359
|
b.angularVelocity -= 0.55;
|
|
7029
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);
|
|
7030
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
|
+
});
|
|
7031
7369
|
state.collisionCount += 1;
|
|
7370
|
+
state.contactCount = 1;
|
|
7032
7371
|
collided = true;
|
|
7033
7372
|
}
|
|
7034
7373
|
state.collisionFlash = collided ? 1 : Math.max(0, state.collisionFlash - dt * 1.8);
|
|
7035
7374
|
}
|
|
7375
|
+
function updateWaveImpulses(state, dt) {
|
|
7376
|
+
state.waveImpulses = state.waveImpulses.map((impulse) => ({
|
|
7377
|
+
...impulse,
|
|
7378
|
+
life: impulse.life - dt * 0.55
|
|
7379
|
+
})).filter((impulse) => impulse.life > 0);
|
|
7380
|
+
}
|
|
7036
7381
|
function updateSpray(state, dt) {
|
|
7037
7382
|
state.sprays = state.sprays.map((particle) => {
|
|
7038
7383
|
const nextVelocity = vec3(particle.velocity.x, particle.velocity.y - 4.2 * dt, particle.velocity.z);
|
|
@@ -7146,7 +7491,7 @@ function renderShipShadow(ctx, shipModel, ship, state, camera, viewport, lightDi
|
|
|
7146
7491
|
vec3(bounds.min[0], keelY, bounds.max[2])
|
|
7147
7492
|
].map((point) => transformPoint(point, transform));
|
|
7148
7493
|
renderProjectedShadow(ctx, hullCorners, camera, viewport, lightDir, {
|
|
7149
|
-
planeY: sampleWave(ship.position.x, ship.position.z, state.time) * 0.24 - 0.03,
|
|
7494
|
+
planeY: sampleWave(state, ship.position.x, ship.position.z, state.time) * 0.24 - 0.03,
|
|
7150
7495
|
alpha: 0.08 + shadowStrength * 0.2,
|
|
7151
7496
|
blur: 14 + shadowStrength * 24
|
|
7152
7497
|
});
|
|
@@ -7175,12 +7520,30 @@ function renderScene(ctx, canvas, state, shipModel, dom) {
|
|
|
7175
7520
|
const nearLighting = lightingPlan.bands.find((entry) => entry.band === "near") ?? lightingPlan.bands[0];
|
|
7176
7521
|
const lightDir = normalizeVec3(vec3(-0.45, 0.85, -0.24));
|
|
7177
7522
|
const lightingSnapshot = state.lightingDetail.getSnapshot();
|
|
7178
|
-
const
|
|
7179
|
-
|
|
7180
|
-
|
|
7523
|
+
const visuals = resolveVisualConfig(
|
|
7524
|
+
nearLighting,
|
|
7525
|
+
lightingSnapshot,
|
|
7526
|
+
state.demoDescription?.visuals
|
|
7527
|
+
);
|
|
7528
|
+
state.demoVisuals = visuals;
|
|
7529
|
+
const reflectionStrength = visuals.reflectionStrength;
|
|
7530
|
+
const shadowStrength = visuals.shadowAccent;
|
|
7531
|
+
drawSkyAndShore(
|
|
7532
|
+
ctx,
|
|
7533
|
+
canvas,
|
|
7534
|
+
state,
|
|
7535
|
+
nearLighting,
|
|
7536
|
+
reflectionStrength,
|
|
7537
|
+
shadowStrength,
|
|
7538
|
+
visuals
|
|
7539
|
+
);
|
|
7181
7540
|
const triangles = [];
|
|
7182
|
-
pushHarborGeometry(camera, viewport, triangles);
|
|
7183
|
-
const water = buildWaterBands(
|
|
7541
|
+
pushHarborGeometry(camera, viewport, triangles, visuals);
|
|
7542
|
+
const water = buildWaterBands(
|
|
7543
|
+
state,
|
|
7544
|
+
state.fluidDetail.getSnapshot().currentLevel.config,
|
|
7545
|
+
visuals
|
|
7546
|
+
);
|
|
7184
7547
|
for (const bandMesh of water.bandMeshes) {
|
|
7185
7548
|
const bandAccent = bandMesh.band === "near" ? 0.06 : bandMesh.band === "mid" ? 0.04 : 0;
|
|
7186
7549
|
for (let index = 0; index < bandMesh.indices.length; index += 3) {
|
|
@@ -7202,7 +7565,12 @@ function renderScene(ctx, canvas, state, shipModel, dom) {
|
|
|
7202
7565
|
});
|
|
7203
7566
|
}
|
|
7204
7567
|
}
|
|
7205
|
-
const cloth = buildClothSurface(
|
|
7568
|
+
const cloth = buildClothSurface(
|
|
7569
|
+
state,
|
|
7570
|
+
state,
|
|
7571
|
+
state.clothDetail.getSnapshot().currentLevel.config,
|
|
7572
|
+
visuals
|
|
7573
|
+
);
|
|
7206
7574
|
for (let index = 0; index < cloth.indices.length; index += 3) {
|
|
7207
7575
|
const a = cloth.positions[cloth.indices[index]];
|
|
7208
7576
|
const b = cloth.positions[cloth.indices[index + 1]];
|
|
@@ -7217,7 +7585,7 @@ function renderScene(ctx, canvas, state, shipModel, dom) {
|
|
|
7217
7585
|
depth: (projected[0].depth + projected[1].depth + projected[2].depth) / 3,
|
|
7218
7586
|
worldCenter: scaleVec3(addVec3(addVec3(a, b), c), 1 / 3),
|
|
7219
7587
|
normal,
|
|
7220
|
-
baseColor:
|
|
7588
|
+
baseColor: cloth.color,
|
|
7221
7589
|
accent: cloth.band === "near" ? 0.1 : 0.04
|
|
7222
7590
|
});
|
|
7223
7591
|
}
|
|
@@ -7280,15 +7648,26 @@ function renderScene(ctx, canvas, state, shipModel, dom) {
|
|
|
7280
7648
|
"The ships collide on GLTF-derived hull volumes while cloth and fluid remain downstream visual consumers.",
|
|
7281
7649
|
"Near-field lighting keeps the ray-traced-primary shadow impression so the collision read stays crisp."
|
|
7282
7650
|
] : SCENE_NOTES;
|
|
7283
|
-
|
|
7284
|
-
setListContent(
|
|
7285
|
-
|
|
7286
|
-
|
|
7287
|
-
|
|
7288
|
-
|
|
7651
|
+
const custom = state.demoDescription ?? null;
|
|
7652
|
+
setListContent(
|
|
7653
|
+
dom.sceneMetrics,
|
|
7654
|
+
Array.isArray(custom?.sceneMetrics) ? custom.sceneMetrics : sceneMetrics
|
|
7655
|
+
);
|
|
7656
|
+
setListContent(
|
|
7657
|
+
dom.qualityMetrics,
|
|
7658
|
+
Array.isArray(custom?.qualityMetrics) ? custom.qualityMetrics : qualityMetrics
|
|
7659
|
+
);
|
|
7660
|
+
setListContent(
|
|
7661
|
+
dom.debugMetrics,
|
|
7662
|
+
Array.isArray(custom?.debugMetrics) ? custom.debugMetrics : debugMetrics
|
|
7663
|
+
);
|
|
7664
|
+
setListContent(dom.sceneNotes, Array.isArray(custom?.notes) ? custom.notes : sceneNotes);
|
|
7665
|
+
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; GLTF ships collide on ${shipModel.physics.shape ?? "box"} volumes while visual follow-up remains downstream.` : `GLTF ships are colliding with ${shipModel.physics.shape ?? "box"} physics volumes; cloth and fluid remain continuous while the governor pressure is ${state.lastDecision.pressureLevel}.`;
|
|
7289
7667
|
}
|
|
7290
7668
|
function updateSceneState(state, dt, shipModel) {
|
|
7291
7669
|
updateShips(state, dt, shipModel);
|
|
7670
|
+
updateWaveImpulses(state, dt);
|
|
7292
7671
|
updateSpray(state, dt);
|
|
7293
7672
|
updatePhysicsSnapshot(state, shipModel);
|
|
7294
7673
|
}
|
|
@@ -7307,13 +7686,15 @@ function syncTextState(state, shipModel) {
|
|
|
7307
7686
|
})),
|
|
7308
7687
|
shipPhysics: shipModel.physics,
|
|
7309
7688
|
sprays: state.sprays.length,
|
|
7689
|
+
waveImpulses: state.waveImpulses.length,
|
|
7310
7690
|
pressure: state.lastDecision?.pressureLevel ?? "stable",
|
|
7311
7691
|
physics: {
|
|
7312
7692
|
profile: state.physics.profile,
|
|
7313
7693
|
snapshotStageId: state.physics.plan.snapshotStageId,
|
|
7314
7694
|
workerJobCount: state.physics.manifest.jobs.length,
|
|
7315
7695
|
snapshot: state.physics.snapshot
|
|
7316
|
-
}
|
|
7696
|
+
},
|
|
7697
|
+
package: state.demoDescription?.textState ?? null
|
|
7317
7698
|
};
|
|
7318
7699
|
window.render_game_to_text = () => JSON.stringify(snapshot);
|
|
7319
7700
|
window.advanceTime = (ms) => {
|
|
@@ -7339,8 +7720,10 @@ async function mountGpuShowcase(options = {}) {
|
|
|
7339
7720
|
const state = createSceneState({ focus });
|
|
7340
7721
|
const shipModel = await loadGltfModel(resolveShowcaseAssetUrl());
|
|
7341
7722
|
state.shipModel = shipModel;
|
|
7723
|
+
state.packageState = typeof options.createState === "function" ? options.createState() : void 0;
|
|
7342
7724
|
updatePhysicsSnapshot(state, shipModel);
|
|
7343
7725
|
state.lastDecision = recordTelemetry(state, 16.4);
|
|
7726
|
+
state.demoDescription = resolveSceneDescription(state, options, shipModel).description;
|
|
7344
7727
|
syncTextState(state, shipModel);
|
|
7345
7728
|
const ctx = dom.canvas.getContext("2d");
|
|
7346
7729
|
const renderFrame = (nowMs) => {
|
|
@@ -7353,9 +7736,11 @@ async function mountGpuShowcase(options = {}) {
|
|
|
7353
7736
|
state.time += dt;
|
|
7354
7737
|
state.frame += 1;
|
|
7355
7738
|
updateSceneState(state, dt, shipModel);
|
|
7739
|
+
updatePackageState(state, options, shipModel, dt);
|
|
7356
7740
|
const syntheticFrame = 14.2 + state.sprays.length * 0.1 + (state.stress ? 6.4 : 0);
|
|
7357
7741
|
state.lastDecision = recordTelemetry(state, syntheticFrame);
|
|
7358
7742
|
}
|
|
7743
|
+
state.demoDescription = resolveSceneDescription(state, options, shipModel).description;
|
|
7359
7744
|
renderScene(ctx, dom.canvas, state, shipModel, dom);
|
|
7360
7745
|
syncTextState(state, shipModel);
|
|
7361
7746
|
requestAnimationFrame(renderFrame);
|
|
@@ -7395,6 +7780,7 @@ function updatePhysicsSnapshot(state, shipModel) {
|
|
|
7395
7780
|
contactCount: state.collisionFlash > 0.02 ? 1 : 0,
|
|
7396
7781
|
metadata: {
|
|
7397
7782
|
collisionCount: state.collisionCount,
|
|
7783
|
+
contactCount: state.contactCount,
|
|
7398
7784
|
snapshotStageId: state.physics.plan.snapshotStageId,
|
|
7399
7785
|
rigidBodyShape: shipModel.physics.shape ?? "box"
|
|
7400
7786
|
}
|
|
@@ -7405,4 +7791,4 @@ export {
|
|
|
7405
7791
|
resolveShowcaseAssetUrl,
|
|
7406
7792
|
showcaseFocusModes
|
|
7407
7793
|
};
|
|
7408
|
-
//# sourceMappingURL=showcase-runtime-
|
|
7794
|
+
//# sourceMappingURL=showcase-runtime-67CHJBNO.js.map
|