@plasius/gpu-shared 0.1.19 → 1.0.0
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 +41 -1
- package/README.md +26 -13
- package/assets/shoreline.gltf +532 -0
- package/dist/{chunk-EZHA3MH7.js → chunk-DBTWE2EF.js} +6 -2
- package/dist/chunk-DBTWE2EF.js.map +1 -0
- package/dist/{chunk-UKCJ2AWJ.js → chunk-KGKLNL4X.js} +3 -2
- package/dist/chunk-KGKLNL4X.js.map +1 -0
- package/dist/{gltf-loader-FMRC3OEV.js → gltf-loader-44ILCO7V.js} +2 -2
- package/dist/index.cjs +421 -152
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +7 -5
- package/dist/index.js.map +1 -1
- package/dist/{product-studio-runtime-3KANG3JE.js → product-studio-runtime-H7S5WN64.js} +5 -3
- package/dist/{showcase-runtime-OH3H6ZW2.js → showcase-runtime-INRAPCXW.js} +414 -152
- package/dist/showcase-runtime-INRAPCXW.js.map +1 -0
- package/package.json +2 -1
- package/src/asset-url.js +1 -0
- package/src/index.d.ts +10 -1
- package/src/index.js +1 -0
- package/src/product-studio-runtime.js +4 -0
- package/src/showcase-runtime.js +430 -151
- package/dist/chunk-EZHA3MH7.js.map +0 -1
- package/dist/chunk-UKCJ2AWJ.js.map +0 -1
- package/dist/showcase-runtime-OH3H6ZW2.js.map +0 -1
- /package/dist/{gltf-loader-FMRC3OEV.js.map → gltf-loader-44ILCO7V.js.map} +0 -0
- /package/dist/{product-studio-runtime-3KANG3JE.js.map → product-studio-runtime-H7S5WN64.js.map} +0 -0
package/dist/index.cjs
CHANGED
|
@@ -119,7 +119,8 @@ var init_asset_url = __esm({
|
|
|
119
119
|
brigantine: "brigantine.gltf",
|
|
120
120
|
cutter: "cutter.gltf",
|
|
121
121
|
lighthouse: "lighthouse.gltf",
|
|
122
|
-
"harbor-dock": "harbor-dock.gltf"
|
|
122
|
+
"harbor-dock": "harbor-dock.gltf",
|
|
123
|
+
shoreline: "shoreline.gltf"
|
|
123
124
|
});
|
|
124
125
|
}
|
|
125
126
|
});
|
|
@@ -2407,6 +2408,7 @@ var init_dist = __esm({
|
|
|
2407
2408
|
// src/product-studio-runtime.js
|
|
2408
2409
|
var product_studio_runtime_exports = {};
|
|
2409
2410
|
__export(product_studio_runtime_exports, {
|
|
2411
|
+
buildProductStudioSceneObjects: () => buildProductStudioSceneObjects,
|
|
2410
2412
|
createProductStudioMeshes: () => createProductStudioMeshes,
|
|
2411
2413
|
mountGpuProductStudio: () => mountGpuProductStudio
|
|
2412
2414
|
});
|
|
@@ -2665,6 +2667,9 @@ function createProductStudioMeshes(model, options = {}) {
|
|
|
2665
2667
|
const modelMeshes = primitives.map((primitive, index) => createProductStudioMeshFromPrimitive(primitive, index, transform)).filter(Boolean);
|
|
2666
2668
|
return Object.freeze([...createProductStudioEnvironmentMeshes(), ...modelMeshes]);
|
|
2667
2669
|
}
|
|
2670
|
+
function buildProductStudioSceneObjects(model, options = {}) {
|
|
2671
|
+
return createProductStudioMeshes(model, options);
|
|
2672
|
+
}
|
|
2668
2673
|
function ensureStyles(documentRef) {
|
|
2669
2674
|
if (documentRef.getElementById?.(STYLE_ID)) {
|
|
2670
2675
|
return;
|
|
@@ -2845,6 +2850,8 @@ var init_product_studio_runtime = __esm({
|
|
|
2845
2850
|
var showcase_runtime_exports = {};
|
|
2846
2851
|
__export(showcase_runtime_exports, {
|
|
2847
2852
|
__testOnlyAdvanceShowcaseClothSimulationState: () => advanceShowcaseClothSimulationState,
|
|
2853
|
+
__testOnlyBuildClothSurface: () => buildClothSurface,
|
|
2854
|
+
__testOnlyBuildShorelineFoamSegments: () => buildShorelineFoamSegments,
|
|
2848
2855
|
__testOnlyBuildWaterBands: () => buildWaterBands,
|
|
2849
2856
|
__testOnlyBuildWaterMotionEffects: () => buildWaterMotionEffects,
|
|
2850
2857
|
__testOnlyCollectSceneLightSources: () => collectSceneLightSources,
|
|
@@ -3456,31 +3463,31 @@ function injectStyles() {
|
|
|
3456
3463
|
box-sizing: border-box;
|
|
3457
3464
|
}
|
|
3458
3465
|
.plasius-demo {
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
gap: 20px;
|
|
3464
|
-
}
|
|
3465
|
-
.plasius-demo__hero,
|
|
3466
|
-
.plasius-demo__layout {
|
|
3467
|
-
display: grid;
|
|
3468
|
-
gap: 20px;
|
|
3469
|
-
}
|
|
3470
|
-
.plasius-demo__hero {
|
|
3471
|
-
grid-template-columns: minmax(0, 1.5fr) minmax(320px, 0.85fr);
|
|
3472
|
-
align-items: start;
|
|
3466
|
+
position: relative;
|
|
3467
|
+
width: 100%;
|
|
3468
|
+
min-height: 100dvh;
|
|
3469
|
+
overflow: hidden;
|
|
3473
3470
|
}
|
|
3474
3471
|
.plasius-panel {
|
|
3475
3472
|
border: 1px solid var(--plasius-border);
|
|
3476
|
-
border-radius:
|
|
3473
|
+
border-radius: 8px;
|
|
3477
3474
|
background: var(--plasius-panel);
|
|
3478
3475
|
box-shadow: var(--plasius-shadow);
|
|
3479
3476
|
backdrop-filter: blur(12px);
|
|
3480
3477
|
}
|
|
3481
3478
|
.plasius-demo__hero-card,
|
|
3482
3479
|
.plasius-demo__status {
|
|
3483
|
-
|
|
3480
|
+
position: absolute;
|
|
3481
|
+
z-index: 3;
|
|
3482
|
+
padding: 10px 12px;
|
|
3483
|
+
}
|
|
3484
|
+
.plasius-demo__hero-card {
|
|
3485
|
+
display: none;
|
|
3486
|
+
}
|
|
3487
|
+
.plasius-demo__status {
|
|
3488
|
+
left: 16px;
|
|
3489
|
+
bottom: 84px;
|
|
3490
|
+
max-width: min(360px, calc(100vw - 32px));
|
|
3484
3491
|
}
|
|
3485
3492
|
.plasius-demo__eyebrow {
|
|
3486
3493
|
margin: 0 0 8px;
|
|
@@ -3503,31 +3510,36 @@ function injectStyles() {
|
|
|
3503
3510
|
.plasius-demo__status-badge {
|
|
3504
3511
|
width: fit-content;
|
|
3505
3512
|
margin: 0;
|
|
3506
|
-
padding:
|
|
3507
|
-
border-radius:
|
|
3513
|
+
padding: 6px 9px;
|
|
3514
|
+
border-radius: 6px;
|
|
3508
3515
|
background: rgba(243, 177, 106, 0.14);
|
|
3509
3516
|
color: var(--plasius-accent);
|
|
3510
3517
|
font-weight: 700;
|
|
3518
|
+
font-size: 12px;
|
|
3511
3519
|
}
|
|
3512
3520
|
.plasius-demo__status-text {
|
|
3513
3521
|
margin: 10px 0 0;
|
|
3514
3522
|
color: var(--plasius-muted);
|
|
3515
|
-
|
|
3516
|
-
|
|
3517
|
-
.plasius-demo__layout {
|
|
3518
|
-
grid-template-columns: minmax(0, 1.45fr) minmax(320px, 0.68fr);
|
|
3519
|
-
align-items: start;
|
|
3523
|
+
font-size: 12px;
|
|
3524
|
+
line-height: 1.45;
|
|
3520
3525
|
}
|
|
3521
3526
|
.plasius-demo__canvas-panel {
|
|
3522
|
-
|
|
3523
|
-
|
|
3527
|
+
position: absolute;
|
|
3528
|
+
inset: 0;
|
|
3529
|
+
padding: 0;
|
|
3530
|
+
border: 0;
|
|
3531
|
+
border-radius: 0;
|
|
3532
|
+
background: transparent;
|
|
3533
|
+
box-shadow: none;
|
|
3534
|
+
backdrop-filter: none;
|
|
3524
3535
|
}
|
|
3525
3536
|
.plasius-demo__canvas {
|
|
3526
3537
|
width: 100%;
|
|
3527
|
-
|
|
3538
|
+
height: 100%;
|
|
3539
|
+
min-height: 100dvh;
|
|
3528
3540
|
display: block;
|
|
3529
|
-
border
|
|
3530
|
-
border:
|
|
3541
|
+
border: 0;
|
|
3542
|
+
border-radius: 0;
|
|
3531
3543
|
background: linear-gradient(180deg, #071220 0%, #132440 42%, #10344b 42%, #05111d 100%);
|
|
3532
3544
|
}
|
|
3533
3545
|
.${CAPTURE_CLASS} .plasius-demo {
|
|
@@ -3540,6 +3552,8 @@ function injectStyles() {
|
|
|
3540
3552
|
.${CAPTURE_CLASS} .plasius-demo__toolbar,
|
|
3541
3553
|
.${CAPTURE_CLASS} .plasius-demo__legend,
|
|
3542
3554
|
.${CAPTURE_CLASS} .plasius-demo__sidebar,
|
|
3555
|
+
.${CAPTURE_CLASS} .plasius-demo__diagnostics,
|
|
3556
|
+
.${CAPTURE_CLASS} .plasius-demo__status,
|
|
3543
3557
|
.${CAPTURE_CLASS} .plasius-demo__footer {
|
|
3544
3558
|
display: none;
|
|
3545
3559
|
}
|
|
@@ -3566,12 +3580,14 @@ function injectStyles() {
|
|
|
3566
3580
|
}
|
|
3567
3581
|
.plasius-demo__toolbar {
|
|
3568
3582
|
position: absolute;
|
|
3569
|
-
top:
|
|
3570
|
-
left:
|
|
3583
|
+
top: 84px;
|
|
3584
|
+
left: 16px;
|
|
3585
|
+
z-index: 4;
|
|
3571
3586
|
display: flex;
|
|
3572
|
-
gap:
|
|
3587
|
+
gap: 8px;
|
|
3573
3588
|
flex-wrap: wrap;
|
|
3574
3589
|
align-items: center;
|
|
3590
|
+
max-width: min(560px, calc(100vw - 32px));
|
|
3575
3591
|
}
|
|
3576
3592
|
.plasius-demo button,
|
|
3577
3593
|
.plasius-demo label,
|
|
@@ -3583,22 +3599,63 @@ function injectStyles() {
|
|
|
3583
3599
|
.plasius-demo .plasius-toggle,
|
|
3584
3600
|
.plasius-demo select {
|
|
3585
3601
|
border: 1px solid rgba(159, 185, 223, 0.18);
|
|
3586
|
-
border-radius:
|
|
3602
|
+
border-radius: 6px;
|
|
3587
3603
|
background: rgba(9, 20, 34, 0.84);
|
|
3588
3604
|
color: var(--plasius-ink);
|
|
3589
|
-
padding: 10px
|
|
3605
|
+
padding: 8px 10px;
|
|
3590
3606
|
}
|
|
3591
3607
|
.plasius-toggle {
|
|
3592
3608
|
display: inline-flex;
|
|
3593
3609
|
align-items: center;
|
|
3594
3610
|
gap: 8px;
|
|
3595
3611
|
}
|
|
3612
|
+
.plasius-demo__diagnostics {
|
|
3613
|
+
position: absolute;
|
|
3614
|
+
right: 16px;
|
|
3615
|
+
bottom: 84px;
|
|
3616
|
+
z-index: 4;
|
|
3617
|
+
max-width: min(420px, calc(100vw - 32px));
|
|
3618
|
+
color: var(--plasius-ink);
|
|
3619
|
+
font-family: "JetBrains Mono", monospace;
|
|
3620
|
+
font-size: 12px;
|
|
3621
|
+
}
|
|
3622
|
+
.plasius-demo__diagnostics summary {
|
|
3623
|
+
width: fit-content;
|
|
3624
|
+
margin-left: auto;
|
|
3625
|
+
border: 1px solid rgba(159, 185, 223, 0.18);
|
|
3626
|
+
border-radius: 6px;
|
|
3627
|
+
padding: 8px 10px;
|
|
3628
|
+
background: rgba(9, 20, 34, 0.84);
|
|
3629
|
+
cursor: pointer;
|
|
3630
|
+
list-style: none;
|
|
3631
|
+
}
|
|
3632
|
+
.plasius-demo__diagnostics summary::-webkit-details-marker {
|
|
3633
|
+
display: none;
|
|
3634
|
+
}
|
|
3635
|
+
.plasius-demo__diagnostics[open] {
|
|
3636
|
+
width: min(420px, calc(100vw - 32px));
|
|
3637
|
+
}
|
|
3638
|
+
.plasius-demo__diagnostics[open] summary {
|
|
3639
|
+
margin-bottom: 8px;
|
|
3640
|
+
background: rgba(243, 177, 106, 0.14);
|
|
3641
|
+
color: var(--plasius-accent);
|
|
3642
|
+
}
|
|
3596
3643
|
.plasius-demo__sidebar {
|
|
3597
3644
|
display: grid;
|
|
3598
|
-
gap:
|
|
3645
|
+
gap: 8px;
|
|
3646
|
+
max-height: min(58vh, 520px);
|
|
3647
|
+
overflow: auto;
|
|
3599
3648
|
}
|
|
3600
3649
|
.plasius-demo__card {
|
|
3601
|
-
padding:
|
|
3650
|
+
padding: 10px;
|
|
3651
|
+
}
|
|
3652
|
+
.plasius-demo__card h2 {
|
|
3653
|
+
margin: 0;
|
|
3654
|
+
color: rgba(226, 236, 255, 0.72);
|
|
3655
|
+
font-family: "JetBrains Mono", monospace;
|
|
3656
|
+
font-size: 11px;
|
|
3657
|
+
letter-spacing: 0.08em;
|
|
3658
|
+
text-transform: uppercase;
|
|
3602
3659
|
}
|
|
3603
3660
|
.plasius-demo__metrics,
|
|
3604
3661
|
.plasius-demo__metrics li {
|
|
@@ -3607,27 +3664,19 @@ function injectStyles() {
|
|
|
3607
3664
|
list-style: none;
|
|
3608
3665
|
}
|
|
3609
3666
|
.plasius-demo__metrics {
|
|
3610
|
-
margin-top:
|
|
3667
|
+
margin-top: 8px;
|
|
3611
3668
|
display: grid;
|
|
3612
|
-
gap:
|
|
3669
|
+
gap: 5px;
|
|
3613
3670
|
color: var(--plasius-muted);
|
|
3614
|
-
|
|
3671
|
+
font-size: 12px;
|
|
3672
|
+
line-height: 1.35;
|
|
3615
3673
|
}
|
|
3616
3674
|
.plasius-demo__metrics li {
|
|
3617
3675
|
border-top: 1px solid rgba(21, 32, 40, 0.08);
|
|
3618
|
-
padding-top:
|
|
3676
|
+
padding-top: 5px;
|
|
3619
3677
|
}
|
|
3620
3678
|
.plasius-demo__legend {
|
|
3621
|
-
|
|
3622
|
-
right: 24px;
|
|
3623
|
-
bottom: 24px;
|
|
3624
|
-
padding: 10px 14px;
|
|
3625
|
-
border-radius: 16px;
|
|
3626
|
-
background: rgba(9, 20, 34, 0.82);
|
|
3627
|
-
border: 1px solid rgba(159, 185, 223, 0.16);
|
|
3628
|
-
color: var(--plasius-muted);
|
|
3629
|
-
font-size: 12px;
|
|
3630
|
-
line-height: 1.45;
|
|
3679
|
+
display: none;
|
|
3631
3680
|
}
|
|
3632
3681
|
.plasius-demo__legend strong {
|
|
3633
3682
|
display: block;
|
|
@@ -3635,15 +3684,62 @@ function injectStyles() {
|
|
|
3635
3684
|
margin-bottom: 4px;
|
|
3636
3685
|
}
|
|
3637
3686
|
.plasius-demo__footer {
|
|
3638
|
-
|
|
3639
|
-
color: rgba(226, 236, 255, 0.68);
|
|
3640
|
-
font-size: 13px;
|
|
3641
|
-
line-height: 1.6;
|
|
3687
|
+
display: none;
|
|
3642
3688
|
}
|
|
3643
3689
|
@media (max-width: 1200px) {
|
|
3644
|
-
.plasius-
|
|
3645
|
-
|
|
3646
|
-
|
|
3690
|
+
.plasius-demo__toolbar {
|
|
3691
|
+
top: 92px;
|
|
3692
|
+
}
|
|
3693
|
+
}
|
|
3694
|
+
@media (max-width: 640px) {
|
|
3695
|
+
.plasius-demo__status {
|
|
3696
|
+
left: 10px;
|
|
3697
|
+
bottom: 10px;
|
|
3698
|
+
max-width: calc(100vw - 126px);
|
|
3699
|
+
padding: 6px 8px;
|
|
3700
|
+
}
|
|
3701
|
+
.plasius-demo__status-text {
|
|
3702
|
+
display: none;
|
|
3703
|
+
}
|
|
3704
|
+
.plasius-demo__status-badge {
|
|
3705
|
+
max-width: calc(100vw - 142px);
|
|
3706
|
+
overflow: hidden;
|
|
3707
|
+
text-overflow: ellipsis;
|
|
3708
|
+
white-space: nowrap;
|
|
3709
|
+
}
|
|
3710
|
+
.plasius-demo__toolbar {
|
|
3711
|
+
top: 10px;
|
|
3712
|
+
left: 10px;
|
|
3713
|
+
right: 10px;
|
|
3714
|
+
max-width: calc(100vw - 20px);
|
|
3715
|
+
flex-wrap: nowrap;
|
|
3716
|
+
overflow-x: auto;
|
|
3717
|
+
padding-bottom: 4px;
|
|
3718
|
+
scrollbar-width: none;
|
|
3719
|
+
}
|
|
3720
|
+
.plasius-demo__toolbar::-webkit-scrollbar {
|
|
3721
|
+
display: none;
|
|
3722
|
+
}
|
|
3723
|
+
.plasius-demo button,
|
|
3724
|
+
.plasius-demo .plasius-toggle,
|
|
3725
|
+
.plasius-demo select {
|
|
3726
|
+
padding: 7px 8px;
|
|
3727
|
+
font-size: 12px;
|
|
3728
|
+
white-space: nowrap;
|
|
3729
|
+
}
|
|
3730
|
+
.plasius-demo__diagnostics {
|
|
3731
|
+
right: 10px;
|
|
3732
|
+
bottom: 10px;
|
|
3733
|
+
}
|
|
3734
|
+
.plasius-demo__diagnostics[open] {
|
|
3735
|
+
bottom: 56px;
|
|
3736
|
+
left: 10px;
|
|
3737
|
+
right: 10px;
|
|
3738
|
+
width: auto;
|
|
3739
|
+
max-width: none;
|
|
3740
|
+
}
|
|
3741
|
+
.plasius-demo__diagnostics[open] .plasius-demo__sidebar {
|
|
3742
|
+
max-height: min(42vh, 340px);
|
|
3647
3743
|
}
|
|
3648
3744
|
}
|
|
3649
3745
|
`;
|
|
@@ -3985,11 +4081,12 @@ function buildTrianglesFromMesh(mesh, transform, colorOverride, camera, viewport
|
|
|
3985
4081
|
}
|
|
3986
4082
|
}
|
|
3987
4083
|
async function loadShowcaseAssetCatalog() {
|
|
3988
|
-
const [brigantine, cutter, lighthouse, harborDock] = await Promise.all([
|
|
4084
|
+
const [brigantine, cutter, lighthouse, harborDock, shoreline] = await Promise.all([
|
|
3989
4085
|
loadGltfModel(resolveShowcaseAssetUrl("brigantine")),
|
|
3990
4086
|
loadGltfModel(resolveShowcaseAssetUrl("cutter")),
|
|
3991
4087
|
loadGltfModel(resolveShowcaseAssetUrl("lighthouse")),
|
|
3992
|
-
loadGltfModel(resolveShowcaseAssetUrl("harbor-dock"))
|
|
4088
|
+
loadGltfModel(resolveShowcaseAssetUrl("harbor-dock")),
|
|
4089
|
+
loadGltfModel(resolveShowcaseAssetUrl("shoreline"))
|
|
3993
4090
|
]);
|
|
3994
4091
|
return Object.freeze({
|
|
3995
4092
|
primaryShipKey: "brigantine",
|
|
@@ -3999,7 +4096,8 @@ async function loadShowcaseAssetCatalog() {
|
|
|
3999
4096
|
}),
|
|
4000
4097
|
environment: Object.freeze({
|
|
4001
4098
|
lighthouse,
|
|
4002
|
-
"harbor-dock": harborDock
|
|
4099
|
+
"harbor-dock": harborDock,
|
|
4100
|
+
shoreline
|
|
4003
4101
|
})
|
|
4004
4102
|
});
|
|
4005
4103
|
}
|
|
@@ -4125,24 +4223,27 @@ function buildDemoDom(root, options) {
|
|
|
4125
4223
|
${t(gpuSharedTranslationKeys.legendCollisions)}
|
|
4126
4224
|
</div>
|
|
4127
4225
|
</section>
|
|
4128
|
-
<
|
|
4129
|
-
<
|
|
4130
|
-
|
|
4131
|
-
<
|
|
4132
|
-
|
|
4133
|
-
|
|
4134
|
-
|
|
4135
|
-
<
|
|
4136
|
-
|
|
4137
|
-
|
|
4138
|
-
|
|
4139
|
-
<
|
|
4140
|
-
|
|
4141
|
-
|
|
4142
|
-
|
|
4143
|
-
<
|
|
4144
|
-
|
|
4145
|
-
|
|
4226
|
+
<details class="plasius-demo__diagnostics">
|
|
4227
|
+
<summary>${t(gpuSharedTranslationKeys.debugTelemetry)}</summary>
|
|
4228
|
+
<aside class="plasius-demo__sidebar">
|
|
4229
|
+
<section class="plasius-panel plasius-demo__card">
|
|
4230
|
+
<h2>${t(gpuSharedTranslationKeys.sceneState)}</h2>
|
|
4231
|
+
<ul id="sceneMetrics" class="plasius-demo__metrics"></ul>
|
|
4232
|
+
</section>
|
|
4233
|
+
<section class="plasius-panel plasius-demo__card">
|
|
4234
|
+
<h2>${t(gpuSharedTranslationKeys.qualityBudgets)}</h2>
|
|
4235
|
+
<ul id="qualityMetrics" class="plasius-demo__metrics"></ul>
|
|
4236
|
+
</section>
|
|
4237
|
+
<section class="plasius-panel plasius-demo__card">
|
|
4238
|
+
<h2>${t(gpuSharedTranslationKeys.debugTelemetry)}</h2>
|
|
4239
|
+
<ul id="debugMetrics" class="plasius-demo__metrics"></ul>
|
|
4240
|
+
</section>
|
|
4241
|
+
<section class="plasius-panel plasius-demo__card">
|
|
4242
|
+
<h2>${t(gpuSharedTranslationKeys.notes)}</h2>
|
|
4243
|
+
<ul id="sceneNotes" class="plasius-demo__metrics"></ul>
|
|
4244
|
+
</section>
|
|
4245
|
+
</aside>
|
|
4246
|
+
</details>
|
|
4146
4247
|
</section>
|
|
4147
4248
|
<p class="plasius-demo__footer">
|
|
4148
4249
|
This visual example is shared across the GPU packages to keep manual validation fast and consistent.
|
|
@@ -4506,11 +4607,11 @@ function advanceShowcaseClothSimulationState(clothState, options = {}) {
|
|
|
4506
4607
|
1 + Math.sin(gustPhase * 0.74) * 0.18
|
|
4507
4608
|
)
|
|
4508
4609
|
);
|
|
4509
|
-
const windStrength = (
|
|
4610
|
+
const windStrength = (0.94 + broadMotion * 0.82 + wrinkleLayers * 0.08) * flagMotion * (0.36 + u * 0.92);
|
|
4510
4611
|
const wrinkleForce = vec3(
|
|
4511
|
-
Math.sin(wrinklePhase) * 0.
|
|
4512
|
-
Math.cos(wrinklePhase * 0.7) * 0.
|
|
4513
|
-
Math.cos(wrinklePhase) * 0.
|
|
4612
|
+
Math.sin(wrinklePhase) * 0.12 * wrinkleMotion * flagMotion,
|
|
4613
|
+
Math.cos(wrinklePhase * 0.7) * 0.045 * wrinkleMotion,
|
|
4614
|
+
Math.cos(wrinklePhase) * 0.08 * broadMotion * flagMotion
|
|
4514
4615
|
);
|
|
4515
4616
|
const acceleration = addVec3(
|
|
4516
4617
|
vec3(0, -0.48 - u * 0.08, 0),
|
|
@@ -4567,25 +4668,25 @@ function resolveVisualConfig(nearLighting, lightingSnapshot, customVisuals = {})
|
|
|
4567
4668
|
ambientMist: "rgba(41, 63, 97, 0.16)",
|
|
4568
4669
|
reflectionStrength: lightingSnapshot.currentLevel.config.reflectionStrength,
|
|
4569
4670
|
shadowAccent: lightingSnapshot.currentLevel.config.shadowStrength,
|
|
4570
|
-
waveAmplitude: 0.
|
|
4671
|
+
waveAmplitude: 0.82,
|
|
4571
4672
|
waveDirection: { x: 0.88, z: 0.28 },
|
|
4572
|
-
wavePhaseSpeed: 0.
|
|
4573
|
-
wakeStrength: 0.
|
|
4574
|
-
wakeLength:
|
|
4575
|
-
collisionRippleStrength: 0.
|
|
4576
|
-
waterNear: { r: 0.
|
|
4577
|
-
waterFar: { r: 0.
|
|
4673
|
+
wavePhaseSpeed: 0.74,
|
|
4674
|
+
wakeStrength: 0.24,
|
|
4675
|
+
wakeLength: 17,
|
|
4676
|
+
collisionRippleStrength: 0.22,
|
|
4677
|
+
waterNear: { r: 0.05, g: 0.2, b: 0.3 },
|
|
4678
|
+
waterFar: { r: 0.13, g: 0.31, b: 0.45 },
|
|
4578
4679
|
harborWall: { r: 0.26, g: 0.24, b: 0.28 },
|
|
4579
4680
|
harborDeck: { r: 0.33, g: 0.22, b: 0.16 },
|
|
4580
4681
|
harborTower: { r: 0.23, g: 0.24, b: 0.29 },
|
|
4581
|
-
flagColor: { r: 0.
|
|
4582
|
-
flagMotion: 0.
|
|
4682
|
+
flagColor: { r: 0.54, g: 0.13, b: 0.11 },
|
|
4683
|
+
flagMotion: 0.58,
|
|
4583
4684
|
lanternCore: { r: 0.98, g: 0.8, b: 0.48 },
|
|
4584
4685
|
lanternGlow: { r: 1, g: 0.56, b: 0.2 },
|
|
4585
4686
|
lanternReflectionStrength: 0.42,
|
|
4586
4687
|
torchCore: { r: 0.99, g: 0.72, b: 0.36 },
|
|
4587
4688
|
torchGlow: { r: 0.98, g: 0.38, b: 0.15 },
|
|
4588
|
-
collisionFlash: "rgba(255, 212, 168, 0.
|
|
4689
|
+
collisionFlash: "rgba(255, 212, 168, 0.08)"
|
|
4589
4690
|
};
|
|
4590
4691
|
return {
|
|
4591
4692
|
skyTop: typeof customVisuals.skyTop === "string" ? customVisuals.skyTop : defaults.skyTop,
|
|
@@ -4642,6 +4743,11 @@ function buildClothSurface(model, state, meshDetail, visuals, clothFeatures) {
|
|
|
4642
4743
|
representation: clothPresentation.representation,
|
|
4643
4744
|
continuity: clothPresentation.continuity,
|
|
4644
4745
|
color: visuals.flagColor,
|
|
4746
|
+
material: Object.freeze({
|
|
4747
|
+
weaveAlpha: clothPresentation.band === "near" ? 0.22 : 0.12,
|
|
4748
|
+
foldAlpha: clothPresentation.band === "near" ? 0.3 : 0.18,
|
|
4749
|
+
edgeHighlightAlpha: clothPresentation.band === "near" ? 0.42 : 0.28
|
|
4750
|
+
}),
|
|
4645
4751
|
positions: clothState.positions.map((point) => vec3(point.x, point.y, point.z)),
|
|
4646
4752
|
indices: clothState.indices,
|
|
4647
4753
|
grid: { rows: clothState.rows, cols: clothState.cols }
|
|
@@ -4725,7 +4831,7 @@ function buildWaterMotionEffects(state) {
|
|
|
4725
4831
|
impulse.z
|
|
4726
4832
|
),
|
|
4727
4833
|
radius,
|
|
4728
|
-
opacity: clamp(impulse.life * 0.
|
|
4834
|
+
opacity: clamp(impulse.life * 0.13, 0.035, 0.15)
|
|
4729
4835
|
});
|
|
4730
4836
|
});
|
|
4731
4837
|
for (const ship of state.ships) {
|
|
@@ -4738,7 +4844,7 @@ function buildWaterMotionEffects(state) {
|
|
|
4738
4844
|
const lateral = vec3(-direction.z, 0, direction.x);
|
|
4739
4845
|
const points = [];
|
|
4740
4846
|
for (let sampleIndex = 0; sampleIndex < 6; sampleIndex += 1) {
|
|
4741
|
-
const along = 1 + sampleIndex * 1.
|
|
4847
|
+
const along = 1 + sampleIndex * 1.55;
|
|
4742
4848
|
const lateralOffset = Math.sin(state.time * 1.2 + sampleIndex * 0.8 + readVisualNumber(ship.wanderPhase, 0)) * 0.12;
|
|
4743
4849
|
const worldPoint = addVec3(
|
|
4744
4850
|
ship.position,
|
|
@@ -4751,13 +4857,14 @@ function buildWaterMotionEffects(state) {
|
|
|
4751
4857
|
sampleWave(state, worldPoint.x, worldPoint.z, state.time) * 0.24 + 0.04,
|
|
4752
4858
|
worldPoint.z
|
|
4753
4859
|
),
|
|
4754
|
-
width: 0.
|
|
4860
|
+
width: 0.3 + sampleIndex * 0.11,
|
|
4861
|
+
foam: clamp(0.28 - sampleIndex * 0.028 + speed * 0.025, 0.1, 0.34)
|
|
4755
4862
|
})
|
|
4756
4863
|
);
|
|
4757
4864
|
}
|
|
4758
4865
|
wakeTrails.push(
|
|
4759
4866
|
Object.freeze({
|
|
4760
|
-
opacity: clamp(0.
|
|
4867
|
+
opacity: clamp(0.1 + speed * 0.048, 0.12, 0.24),
|
|
4761
4868
|
points: Object.freeze(points)
|
|
4762
4869
|
})
|
|
4763
4870
|
);
|
|
@@ -4767,6 +4874,27 @@ function buildWaterMotionEffects(state) {
|
|
|
4767
4874
|
rippleRings: Object.freeze(rippleRings)
|
|
4768
4875
|
});
|
|
4769
4876
|
}
|
|
4877
|
+
function buildShorelineFoamSegments(state) {
|
|
4878
|
+
return Object.freeze(
|
|
4879
|
+
SHORELINE_FOAM_ANCHORS.map((anchor, index) => {
|
|
4880
|
+
const pulse = 0.5 + Math.sin(state.time * 0.84 + index * 1.17) * 0.5;
|
|
4881
|
+
const drift = Math.sin(state.time * 0.38 + index * 0.61) * 0.1;
|
|
4882
|
+
const direction = normalizeVec3(vec3(Math.cos(anchor.angle), 0, Math.sin(anchor.angle)));
|
|
4883
|
+
const center = vec3(
|
|
4884
|
+
anchor.x + direction.x * drift,
|
|
4885
|
+
sampleWave(state, anchor.x, anchor.z, state.time) * 0.12 - 0.02,
|
|
4886
|
+
anchor.z + direction.z * drift
|
|
4887
|
+
);
|
|
4888
|
+
return Object.freeze({
|
|
4889
|
+
center,
|
|
4890
|
+
direction,
|
|
4891
|
+
length: anchor.length * (0.78 + pulse * 0.34),
|
|
4892
|
+
width: 0.16 + pulse * 0.12,
|
|
4893
|
+
opacity: 0.07 + pulse * 0.12
|
|
4894
|
+
});
|
|
4895
|
+
})
|
|
4896
|
+
);
|
|
4897
|
+
}
|
|
4770
4898
|
function buildWaterBands(state, fluidDetail, visuals, fluidFeatures) {
|
|
4771
4899
|
const resolvedFluidFeatures = normalizeFluidFeatureAdapters(fluidFeatures);
|
|
4772
4900
|
const fluidPlan = resolvedFluidFeatures.createPlan({
|
|
@@ -4789,9 +4917,9 @@ function buildWaterBands(state, fluidDetail, visuals, fluidFeatures) {
|
|
|
4789
4917
|
const representation = fluidPlan.representations.find((entry) => entry.band === bandSpec.band) ?? fluidPlan.representations[0];
|
|
4790
4918
|
const continuity = resolvedFluidFeatures.createContinuityEnvelope({ fluidBodyId: "harbor" });
|
|
4791
4919
|
const bandContinuity = resolveFluidBandContinuity(continuity, bandSpec.band);
|
|
4792
|
-
const bandResolution = bandSpec.band === "near" ? fluidDetail.nearResolution : bandSpec.band === "mid" ? fluidDetail.midResolution : bandSpec.band === "far" ? 5 : 3;
|
|
4793
|
-
const cols = Math.max(4, bandResolution * 2);
|
|
4794
|
-
const rows = Math.max(4, bandResolution + 2);
|
|
4920
|
+
const bandResolution = bandSpec.band === "near" ? Math.ceil(fluidDetail.nearResolution * 1.28) : bandSpec.band === "mid" ? Math.ceil(fluidDetail.midResolution * 1.2) : bandSpec.band === "far" ? 5 : 3;
|
|
4921
|
+
const cols = Math.max(4, bandResolution * (bandSpec.band === "near" ? 3 : 2));
|
|
4922
|
+
const rows = Math.max(4, bandResolution + (bandSpec.band === "near" ? 5 : 2));
|
|
4795
4923
|
const positions = [];
|
|
4796
4924
|
const indices = [];
|
|
4797
4925
|
const originX = -bandSpec.width * 0.5;
|
|
@@ -4802,7 +4930,9 @@ function buildWaterBands(state, fluidDetail, visuals, fluidFeatures) {
|
|
|
4802
4930
|
const v = row / (rows - 1);
|
|
4803
4931
|
const x = originX + bandSpec.width * u;
|
|
4804
4932
|
const z = originZ + bandSpec.depth * v;
|
|
4805
|
-
const
|
|
4933
|
+
const baseHeight = bandSpec.y + sampleWave(state, x, z, state.time) * bandContinuity.amplitudeFloor * (bandSpec.band === "near" ? 0.9 : bandSpec.band === "mid" ? 0.55 : 0.3);
|
|
4934
|
+
const detailHeight = bandSpec.band === "near" ? Math.sin(x * 1.25 + z * 0.42 - state.time * 2.4) * 0.035 : 0;
|
|
4935
|
+
const y = baseHeight + detailHeight;
|
|
4806
4936
|
positions.push(vec3(x, y, z));
|
|
4807
4937
|
}
|
|
4808
4938
|
}
|
|
@@ -4831,7 +4961,12 @@ function buildWaterBands(state, fluidDetail, visuals, fluidFeatures) {
|
|
|
4831
4961
|
r: mix(visuals.waterFar.r, 0.76, 0.2),
|
|
4832
4962
|
g: mix(visuals.waterFar.g, 0.78, 0.2),
|
|
4833
4963
|
b: mix(visuals.waterFar.b, 0.82, 0.2)
|
|
4834
|
-
}
|
|
4964
|
+
},
|
|
4965
|
+
material: Object.freeze({
|
|
4966
|
+
highlightAlpha: bandSpec.band === "near" ? 0.2 : bandSpec.band === "mid" ? 0.13 : 0.07,
|
|
4967
|
+
foamAlpha: bandSpec.band === "near" ? 0.28 : bandSpec.band === "mid" ? 0.14 : 0.05,
|
|
4968
|
+
microRippleScale: bandSpec.band === "near" ? 1 : bandSpec.band === "mid" ? 0.58 : 0.28
|
|
4969
|
+
})
|
|
4835
4970
|
});
|
|
4836
4971
|
}
|
|
4837
4972
|
return { fluidPlan, bandMeshes };
|
|
@@ -4907,15 +5042,15 @@ function createSceneState(options, featureAdapters) {
|
|
|
4907
5042
|
{
|
|
4908
5043
|
id: "northwind",
|
|
4909
5044
|
modelKey: "brigantine",
|
|
4910
|
-
position: vec3(-
|
|
4911
|
-
velocity: vec3(
|
|
4912
|
-
rotationY:
|
|
4913
|
-
angularVelocity: 0.
|
|
5045
|
+
position: vec3(-7.8, 0, 11.2),
|
|
5046
|
+
velocity: vec3(1.08, 0, -0.18),
|
|
5047
|
+
rotationY: 1.38,
|
|
5048
|
+
angularVelocity: 0.025,
|
|
4914
5049
|
tint: { r: 0.62, g: 0.39, b: 0.23 },
|
|
4915
5050
|
massScale: 1.42,
|
|
4916
|
-
cruiseSpeed:
|
|
4917
|
-
throttleResponse: 0.
|
|
4918
|
-
rudderResponse: 0.
|
|
5051
|
+
cruiseSpeed: 1.22,
|
|
5052
|
+
throttleResponse: 0.36,
|
|
5053
|
+
rudderResponse: 0.4,
|
|
4919
5054
|
wanderPhase: 0.35,
|
|
4920
5055
|
lanterns: CUTTER_LANTERNS,
|
|
4921
5056
|
lanternStrength: 1.06,
|
|
@@ -4924,15 +5059,15 @@ function createSceneState(options, featureAdapters) {
|
|
|
4924
5059
|
{
|
|
4925
5060
|
id: "tidecaller",
|
|
4926
5061
|
modelKey: "cutter",
|
|
4927
|
-
position: vec3(
|
|
4928
|
-
velocity: vec3(-
|
|
4929
|
-
rotationY: -
|
|
4930
|
-
angularVelocity: -0.
|
|
5062
|
+
position: vec3(6.8, 0, 5.4),
|
|
5063
|
+
velocity: vec3(-0.82, 0, 0.14),
|
|
5064
|
+
rotationY: -1.34,
|
|
5065
|
+
angularVelocity: -0.035,
|
|
4931
5066
|
tint: { r: 0.58, g: 0.24, b: 0.16 },
|
|
4932
5067
|
massScale: 0.84,
|
|
4933
|
-
cruiseSpeed:
|
|
4934
|
-
throttleResponse: 0.
|
|
4935
|
-
rudderResponse: 0.
|
|
5068
|
+
cruiseSpeed: 1.36,
|
|
5069
|
+
throttleResponse: 0.52,
|
|
5070
|
+
rudderResponse: 0.58,
|
|
4936
5071
|
wanderPhase: 1.6,
|
|
4937
5072
|
lanterns: SHIP_LANTERNS,
|
|
4938
5073
|
lanternStrength: 1.18,
|
|
@@ -5231,9 +5366,10 @@ function renderShipRigging(ctx, ship, camera, viewport) {
|
|
|
5231
5366
|
}
|
|
5232
5367
|
function renderClothAccent(ctx, cloth, camera, viewport) {
|
|
5233
5368
|
const projected = cloth.positions.map((point) => projectPoint(point, camera, viewport));
|
|
5234
|
-
|
|
5235
|
-
ctx.
|
|
5236
|
-
|
|
5369
|
+
const material = cloth.material ?? {};
|
|
5370
|
+
ctx.strokeStyle = `rgba(255, 241, 226, ${material.foldAlpha ?? 0.32})`;
|
|
5371
|
+
ctx.lineWidth = 1.8;
|
|
5372
|
+
for (let row = 0; row < cloth.grid.rows; row += Math.max(1, Math.floor(cloth.grid.rows / 6))) {
|
|
5237
5373
|
ctx.beginPath();
|
|
5238
5374
|
let started = false;
|
|
5239
5375
|
for (let column = 0; column < cloth.grid.cols; column += 1) {
|
|
@@ -5252,6 +5388,27 @@ function renderClothAccent(ctx, cloth, camera, viewport) {
|
|
|
5252
5388
|
ctx.stroke();
|
|
5253
5389
|
}
|
|
5254
5390
|
}
|
|
5391
|
+
ctx.strokeStyle = `rgba(255, 228, 204, ${material.weaveAlpha ?? 0.22})`;
|
|
5392
|
+
ctx.lineWidth = 0.85;
|
|
5393
|
+
for (let column = 1; column < cloth.grid.cols - 1; column += Math.max(1, Math.floor(cloth.grid.cols / 8))) {
|
|
5394
|
+
ctx.beginPath();
|
|
5395
|
+
let started = false;
|
|
5396
|
+
for (let row = 0; row < cloth.grid.rows; row += 1) {
|
|
5397
|
+
const point = projected[row * cloth.grid.cols + column];
|
|
5398
|
+
if (!point) {
|
|
5399
|
+
continue;
|
|
5400
|
+
}
|
|
5401
|
+
if (!started) {
|
|
5402
|
+
ctx.moveTo(point.x, point.y);
|
|
5403
|
+
started = true;
|
|
5404
|
+
} else {
|
|
5405
|
+
ctx.lineTo(point.x, point.y);
|
|
5406
|
+
}
|
|
5407
|
+
}
|
|
5408
|
+
if (started) {
|
|
5409
|
+
ctx.stroke();
|
|
5410
|
+
}
|
|
5411
|
+
}
|
|
5255
5412
|
const borderIndices = [
|
|
5256
5413
|
0,
|
|
5257
5414
|
cloth.grid.cols - 1,
|
|
@@ -5259,6 +5416,25 @@ function renderClothAccent(ctx, cloth, camera, viewport) {
|
|
|
5259
5416
|
(cloth.grid.rows - 1) * cloth.grid.cols
|
|
5260
5417
|
];
|
|
5261
5418
|
ctx.fillStyle = colorToRgba(cloth.color, 0.95);
|
|
5419
|
+
ctx.strokeStyle = `rgba(255, 246, 236, ${material.edgeHighlightAlpha ?? 0.5})`;
|
|
5420
|
+
ctx.lineWidth = 1.4;
|
|
5421
|
+
ctx.beginPath();
|
|
5422
|
+
let borderStarted = false;
|
|
5423
|
+
for (let column = 0; column < cloth.grid.cols; column += 1) {
|
|
5424
|
+
const point = projected[column];
|
|
5425
|
+
if (!point) {
|
|
5426
|
+
continue;
|
|
5427
|
+
}
|
|
5428
|
+
if (!borderStarted) {
|
|
5429
|
+
ctx.moveTo(point.x, point.y);
|
|
5430
|
+
borderStarted = true;
|
|
5431
|
+
} else {
|
|
5432
|
+
ctx.lineTo(point.x, point.y);
|
|
5433
|
+
}
|
|
5434
|
+
}
|
|
5435
|
+
if (borderStarted) {
|
|
5436
|
+
ctx.stroke();
|
|
5437
|
+
}
|
|
5262
5438
|
for (const index of borderIndices) {
|
|
5263
5439
|
const point = projected[index];
|
|
5264
5440
|
if (!point) {
|
|
@@ -5274,14 +5450,22 @@ function renderWaterHighlights(ctx, waterBands, camera, viewport) {
|
|
|
5274
5450
|
if (band.band === "horizon") {
|
|
5275
5451
|
continue;
|
|
5276
5452
|
}
|
|
5277
|
-
const interval = band.band === "near" ?
|
|
5278
|
-
const alpha = band.band === "near" ? 0.22 : 0.14;
|
|
5453
|
+
const interval = band.band === "near" ? 4 : 5;
|
|
5454
|
+
const alpha = band.material?.highlightAlpha ?? (band.band === "near" ? 0.22 : 0.14);
|
|
5279
5455
|
ctx.strokeStyle = `rgba(232, 247, 255, ${alpha})`;
|
|
5280
|
-
ctx.lineWidth = band.band === "near" ?
|
|
5456
|
+
ctx.lineWidth = band.band === "near" ? 0.9 : 0.65;
|
|
5281
5457
|
for (let row = interval; row < band.rows - 1; row += interval) {
|
|
5282
|
-
ctx.beginPath();
|
|
5283
5458
|
let started = false;
|
|
5284
|
-
|
|
5459
|
+
ctx.beginPath();
|
|
5460
|
+
for (let column = 0; column < band.cols; column += band.band === "near" ? 2 : 3) {
|
|
5461
|
+
if (pseudoRandom(row * 47 + column * 13) < 0.18) {
|
|
5462
|
+
if (started) {
|
|
5463
|
+
ctx.stroke();
|
|
5464
|
+
ctx.beginPath();
|
|
5465
|
+
started = false;
|
|
5466
|
+
}
|
|
5467
|
+
continue;
|
|
5468
|
+
}
|
|
5285
5469
|
const point = projectPoint(
|
|
5286
5470
|
band.positions[row * band.cols + column],
|
|
5287
5471
|
camera,
|
|
@@ -5301,8 +5485,56 @@ function renderWaterHighlights(ctx, waterBands, camera, viewport) {
|
|
|
5301
5485
|
ctx.stroke();
|
|
5302
5486
|
}
|
|
5303
5487
|
}
|
|
5488
|
+
if (band.band === "near") {
|
|
5489
|
+
ctx.fillStyle = `rgba(236, 249, 255, ${(band.material?.foamAlpha ?? 0.28) * 0.72})`;
|
|
5490
|
+
for (let column = 3; column < band.cols - 3; column += 10) {
|
|
5491
|
+
const point = projectPoint(
|
|
5492
|
+
band.positions[Math.floor(band.rows * 0.42) * band.cols + column],
|
|
5493
|
+
camera,
|
|
5494
|
+
viewport
|
|
5495
|
+
);
|
|
5496
|
+
if (!point) {
|
|
5497
|
+
continue;
|
|
5498
|
+
}
|
|
5499
|
+
ctx.beginPath();
|
|
5500
|
+
ctx.ellipse(point.x, point.y, 1.8, 0.75, -0.2, 0, Math.PI * 2);
|
|
5501
|
+
ctx.fill();
|
|
5502
|
+
}
|
|
5503
|
+
}
|
|
5304
5504
|
}
|
|
5305
5505
|
}
|
|
5506
|
+
function renderShorelineFoamSegments(ctx, segments, camera, viewport) {
|
|
5507
|
+
ctx.save();
|
|
5508
|
+
ctx.globalCompositeOperation = "screen";
|
|
5509
|
+
ctx.lineCap = "round";
|
|
5510
|
+
ctx.lineJoin = "round";
|
|
5511
|
+
for (const segment of segments) {
|
|
5512
|
+
const half = scaleVec3(segment.direction, segment.length * 0.5);
|
|
5513
|
+
const start = projectPoint(subVec3(segment.center, half), camera, viewport);
|
|
5514
|
+
const end = projectPoint(addVec3(segment.center, half), camera, viewport);
|
|
5515
|
+
const center = projectPoint(segment.center, camera, viewport);
|
|
5516
|
+
if (!start || !end || !center) {
|
|
5517
|
+
continue;
|
|
5518
|
+
}
|
|
5519
|
+
const depthScale = clamp(140 / Math.max(12, center.depth), 3, 10);
|
|
5520
|
+
ctx.strokeStyle = `rgba(232, 242, 238, ${segment.opacity})`;
|
|
5521
|
+
ctx.lineWidth = clamp(segment.width * depthScale, 0.8, 2.8);
|
|
5522
|
+
ctx.beginPath();
|
|
5523
|
+
ctx.moveTo(start.x, start.y);
|
|
5524
|
+
ctx.quadraticCurveTo(
|
|
5525
|
+
center.x,
|
|
5526
|
+
center.y + Math.sin(segment.center.x * 1.7) * 2.4,
|
|
5527
|
+
end.x,
|
|
5528
|
+
end.y
|
|
5529
|
+
);
|
|
5530
|
+
ctx.stroke();
|
|
5531
|
+
ctx.fillStyle = `rgba(248, 251, 246, ${segment.opacity * 0.68})`;
|
|
5532
|
+
ctx.beginPath();
|
|
5533
|
+
ctx.ellipse(center.x, center.y, depthScale * 0.18, depthScale * 0.08, -0.2, 0, Math.PI * 2);
|
|
5534
|
+
ctx.fill();
|
|
5535
|
+
}
|
|
5536
|
+
ctx.restore();
|
|
5537
|
+
}
|
|
5306
5538
|
function readPhysicsNumber(physics, key, fallback) {
|
|
5307
5539
|
const value = physics?.[key];
|
|
5308
5540
|
return typeof value === "number" && Number.isFinite(value) ? value : fallback;
|
|
@@ -5333,14 +5565,17 @@ function getShipInverseInertia(ship, shipModel) {
|
|
|
5333
5565
|
return 1 / Math.max(1, inertia);
|
|
5334
5566
|
}
|
|
5335
5567
|
function spawnSpray(state, point, intensity) {
|
|
5336
|
-
const count =
|
|
5568
|
+
const count = Math.max(
|
|
5569
|
+
3,
|
|
5570
|
+
Math.ceil(state.fluidDetail.getSnapshot().currentLevel.config.splashCount * 0.32)
|
|
5571
|
+
);
|
|
5337
5572
|
for (let index = 0; index < count; index += 1) {
|
|
5338
5573
|
const angle = index / count * Math.PI * 2;
|
|
5339
|
-
const speed = 0.
|
|
5574
|
+
const speed = 0.46 + Math.random() * intensity * 0.24;
|
|
5340
5575
|
state.sprays.push({
|
|
5341
5576
|
position: vec3(point.x, point.y, point.z),
|
|
5342
|
-
velocity: vec3(Math.cos(angle) * speed * 0.
|
|
5343
|
-
life:
|
|
5577
|
+
velocity: vec3(Math.cos(angle) * speed * 0.24, 0.46 + Math.random() * 0.34, Math.sin(angle) * speed * 0.18),
|
|
5578
|
+
life: 0.72 + Math.random() * 0.22
|
|
5344
5579
|
});
|
|
5345
5580
|
}
|
|
5346
5581
|
}
|
|
@@ -5355,7 +5590,7 @@ function resolveShipRoute(ship, state, radius) {
|
|
|
5355
5590
|
}
|
|
5356
5591
|
const wander = Math.sin(state.time * 0.22 + readVisualNumber(ship.wanderPhase, 0));
|
|
5357
5592
|
const crossCurrent = Math.cos(state.time * 0.31 + readVisualNumber(ship.wanderPhase, 0));
|
|
5358
|
-
const laneCenter = ship.id === "northwind" ?
|
|
5593
|
+
const laneCenter = ship.id === "northwind" ? 11.6 + wander * 0.82 + crossCurrent * 0.24 : 5.4 + wander * 0.94 - crossCurrent * 0.32;
|
|
5359
5594
|
const targetX = ship.routeDirection > 0 ? HARBOR_BOUNDS.maxX - radius * 1.7 : HARBOR_BOUNDS.minX + radius * 1.7;
|
|
5360
5595
|
return vec3(targetX, 0, clamp(laneCenter, HARBOR_BOUNDS.minZ + 1.8, HARBOR_BOUNDS.maxZ - 1.8));
|
|
5361
5596
|
}
|
|
@@ -5368,7 +5603,7 @@ function updateShipMotion(state, ship, dt, shipModel) {
|
|
|
5368
5603
|
const angularDamping = readPhysicsNumber(physics, "angularDamping", 0.08);
|
|
5369
5604
|
const throttleResponse = readVisualNumber(ship.throttleResponse, 0.58);
|
|
5370
5605
|
const rudderResponse = readVisualNumber(ship.rudderResponse, 0.62);
|
|
5371
|
-
const cruiseSpeed = readVisualNumber(ship.cruiseSpeed,
|
|
5606
|
+
const cruiseSpeed = readVisualNumber(ship.cruiseSpeed, 1.25);
|
|
5372
5607
|
ship.collisionCooldown = Math.max(0, readVisualNumber(ship.collisionCooldown, 0) - dt);
|
|
5373
5608
|
const forward = directionFromYaw(ship.rotationY);
|
|
5374
5609
|
const lateral = perpendicularOnWater(forward);
|
|
@@ -5463,7 +5698,7 @@ function resolveShipCollision(state, a, b, shipModelA, shipModelB) {
|
|
|
5463
5698
|
b.position = addVec3(b.position, scaleVec3(correction, invMassB));
|
|
5464
5699
|
const relativeVelocity = subVec3(b.velocity, a.velocity);
|
|
5465
5700
|
const velocityAlongNormal = dotVec3(relativeVelocity, normal);
|
|
5466
|
-
const restitution = (readPhysicsNumber(shipModelA.physics, "restitution", 0.22) + readPhysicsNumber(shipModelB.physics, "restitution", 0.22)) / 2 * 0.
|
|
5701
|
+
const restitution = (readPhysicsNumber(shipModelA.physics, "restitution", 0.22) + readPhysicsNumber(shipModelB.physics, "restitution", 0.22)) / 2 * 0.42;
|
|
5467
5702
|
if (velocityAlongNormal < 0) {
|
|
5468
5703
|
const impulseMagnitude = -(1 + restitution) * velocityAlongNormal / Math.max(1e-4, invMassSum);
|
|
5469
5704
|
const impulse = scaleVec3(normal, impulseMagnitude);
|
|
@@ -5481,27 +5716,27 @@ function resolveShipCollision(state, a, b, shipModelA, shipModelB) {
|
|
|
5481
5716
|
a.angularVelocity -= tangentSpeed * radiusA * getShipInverseInertia(a, shipModelA) * 0.2 + impulseMagnitude * 24e-5;
|
|
5482
5717
|
b.angularVelocity += tangentSpeed * radiusB * getShipInverseInertia(b, shipModelB) * 0.2 + impulseMagnitude * 24e-5;
|
|
5483
5718
|
const impactSpeed = Math.abs(velocityAlongNormal);
|
|
5484
|
-
if (impactSpeed > 0.
|
|
5719
|
+
if (impactSpeed > 0.36 && Math.max(readVisualNumber(a.collisionCooldown, 0), readVisualNumber(b.collisionCooldown, 0)) <= 0) {
|
|
5485
5720
|
const contactPoint = vec3(
|
|
5486
5721
|
(a.position.x + b.position.x) * 0.5,
|
|
5487
5722
|
(a.position.y + b.position.y) * 0.5 + 0.14,
|
|
5488
5723
|
(a.position.z + b.position.z) * 0.5
|
|
5489
5724
|
);
|
|
5490
|
-
spawnSpray(state, contactPoint, impactSpeed *
|
|
5725
|
+
spawnSpray(state, contactPoint, impactSpeed * 0.9 + penetration * 2.4);
|
|
5491
5726
|
state.waveImpulses.push({
|
|
5492
5727
|
x: contactPoint.x,
|
|
5493
5728
|
z: contactPoint.z,
|
|
5494
|
-
strength: clamp(0.
|
|
5495
|
-
radius: 0.
|
|
5729
|
+
strength: clamp(0.1 + impactSpeed * 0.18 + penetration * 0.28, 0.08, 0.52),
|
|
5730
|
+
radius: 0.72 + penetration * 0.72,
|
|
5496
5731
|
life: 1
|
|
5497
5732
|
});
|
|
5498
5733
|
state.collisionCount += 1;
|
|
5499
5734
|
state.collisionFlash = Math.max(
|
|
5500
5735
|
state.collisionFlash,
|
|
5501
|
-
clamp(impactSpeed * 0.
|
|
5736
|
+
clamp(impactSpeed * 0.14 + penetration * 0.32, 0.04, 0.24)
|
|
5502
5737
|
);
|
|
5503
|
-
a.collisionCooldown = 0.
|
|
5504
|
-
b.collisionCooldown = 0.
|
|
5738
|
+
a.collisionCooldown = 0.72;
|
|
5739
|
+
b.collisionCooldown = 0.72;
|
|
5505
5740
|
}
|
|
5506
5741
|
}
|
|
5507
5742
|
state.contactCount += 1;
|
|
@@ -5524,7 +5759,7 @@ function updateShips(state, dt, shipModel) {
|
|
|
5524
5759
|
collided = resolveShipCollision(state, shipA, shipB, shipModelA, shipModelB) || collided;
|
|
5525
5760
|
}
|
|
5526
5761
|
}
|
|
5527
|
-
state.collisionFlash = collided ? Math.max(0.
|
|
5762
|
+
state.collisionFlash = collided ? Math.max(0.04, state.collisionFlash) : Math.max(0, state.collisionFlash - dt * 1.7);
|
|
5528
5763
|
}
|
|
5529
5764
|
function updateWaveImpulses(state, dt) {
|
|
5530
5765
|
state.waveImpulses = state.waveImpulses.map((impulse) => ({
|
|
@@ -5886,7 +6121,8 @@ function renderWaterMotionEffects(ctx, effects, camera, viewport) {
|
|
|
5886
6121
|
for (const wake of effects.wakeTrails) {
|
|
5887
6122
|
const projected = wake.points.map((point) => ({
|
|
5888
6123
|
projected: projectPoint(point.center, camera, viewport),
|
|
5889
|
-
width: point.width
|
|
6124
|
+
width: point.width,
|
|
6125
|
+
foam: point.foam
|
|
5890
6126
|
})).filter((entry) => entry.projected);
|
|
5891
6127
|
if (projected.length < 2) {
|
|
5892
6128
|
continue;
|
|
@@ -5894,8 +6130,8 @@ function renderWaterMotionEffects(ctx, effects, camera, viewport) {
|
|
|
5894
6130
|
const averageDepth = projected.reduce((total, entry) => total + entry.projected.depth, 0) / projected.length;
|
|
5895
6131
|
const averageWidth = projected.reduce((total, entry) => total + entry.width, 0) / projected.length;
|
|
5896
6132
|
const baseWidth = clamp(averageWidth / Math.max(0.25, averageDepth) * 180, 1.6, 5.4);
|
|
5897
|
-
ctx.strokeStyle = `rgba(146, 194, 236, ${wake.opacity * 0.
|
|
5898
|
-
ctx.lineWidth = baseWidth * 1.
|
|
6133
|
+
ctx.strokeStyle = `rgba(146, 194, 236, ${wake.opacity * 0.34})`;
|
|
6134
|
+
ctx.lineWidth = baseWidth * 1.45;
|
|
5899
6135
|
ctx.lineCap = "round";
|
|
5900
6136
|
ctx.lineJoin = "round";
|
|
5901
6137
|
ctx.beginPath();
|
|
@@ -5904,8 +6140,8 @@ function renderWaterMotionEffects(ctx, effects, camera, viewport) {
|
|
|
5904
6140
|
ctx.lineTo(projected[index].projected.x, projected[index].projected.y);
|
|
5905
6141
|
}
|
|
5906
6142
|
ctx.stroke();
|
|
5907
|
-
ctx.strokeStyle = `rgba(234, 247, 255, ${wake.opacity})`;
|
|
5908
|
-
ctx.lineWidth = baseWidth;
|
|
6143
|
+
ctx.strokeStyle = `rgba(234, 247, 255, ${wake.opacity * 0.72})`;
|
|
6144
|
+
ctx.lineWidth = baseWidth * 0.72;
|
|
5909
6145
|
ctx.lineCap = "round";
|
|
5910
6146
|
ctx.lineJoin = "round";
|
|
5911
6147
|
ctx.beginPath();
|
|
@@ -5915,13 +6151,14 @@ function renderWaterMotionEffects(ctx, effects, camera, viewport) {
|
|
|
5915
6151
|
}
|
|
5916
6152
|
ctx.stroke();
|
|
5917
6153
|
for (const entry of projected.slice(1, 5)) {
|
|
5918
|
-
|
|
6154
|
+
const foam = entry.foam ?? 0.3;
|
|
6155
|
+
ctx.fillStyle = `rgba(239, 248, 255, ${wake.opacity * foam * 0.92})`;
|
|
5919
6156
|
ctx.beginPath();
|
|
5920
6157
|
ctx.ellipse(
|
|
5921
6158
|
entry.projected.x,
|
|
5922
6159
|
entry.projected.y,
|
|
5923
|
-
baseWidth * 0.
|
|
5924
|
-
baseWidth * 0.
|
|
6160
|
+
baseWidth * 0.54,
|
|
6161
|
+
baseWidth * 0.28,
|
|
5925
6162
|
0,
|
|
5926
6163
|
0,
|
|
5927
6164
|
Math.PI * 2
|
|
@@ -5939,13 +6176,22 @@ function renderWaterMotionEffects(ctx, effects, camera, viewport) {
|
|
|
5939
6176
|
const radiusX = Math.hypot(xAxis.x - center.x, xAxis.y - center.y);
|
|
5940
6177
|
const radiusY = Math.hypot(zAxis.x - center.x, zAxis.y - center.y);
|
|
5941
6178
|
ctx.strokeStyle = `rgba(216, 235, 255, ${ring.opacity})`;
|
|
5942
|
-
ctx.lineWidth = clamp((radiusX + radiusY) * 0.
|
|
5943
|
-
|
|
5944
|
-
|
|
5945
|
-
|
|
6179
|
+
ctx.lineWidth = clamp((radiusX + radiusY) * 0.014, 0.65, 1.8);
|
|
6180
|
+
for (let segment = 0; segment < 5; segment += 1) {
|
|
6181
|
+
if (pseudoRandom(segment * 31 + radiusX * 0.7 + radiusY * 0.3) < 0.32) {
|
|
6182
|
+
continue;
|
|
6183
|
+
}
|
|
6184
|
+
const startAngle = segment * 1.22 + stateTimePhase(center.x, center.y) * 0.04;
|
|
6185
|
+
ctx.beginPath();
|
|
6186
|
+
ctx.ellipse(center.x, center.y, radiusX, radiusY, 0, startAngle, startAngle + 0.48);
|
|
6187
|
+
ctx.stroke();
|
|
6188
|
+
}
|
|
5946
6189
|
}
|
|
5947
6190
|
ctx.restore();
|
|
5948
6191
|
}
|
|
6192
|
+
function stateTimePhase(x, y) {
|
|
6193
|
+
return Math.sin(x * 0.013 + y * 0.017);
|
|
6194
|
+
}
|
|
5949
6195
|
function renderScene(ctx, canvas, state, shipModel, dom, lightingFeatures, fluidFeatures, clothFeatures) {
|
|
5950
6196
|
const viewport = { width: canvas.width, height: canvas.height };
|
|
5951
6197
|
const camera = buildCamera(state, canvas);
|
|
@@ -6013,6 +6259,7 @@ function renderScene(ctx, canvas, state, shipModel, dom, lightingFeatures, fluid
|
|
|
6013
6259
|
}
|
|
6014
6260
|
}
|
|
6015
6261
|
const waterMotionEffects = buildWaterMotionEffects(state);
|
|
6262
|
+
const shorelineFoamSegments = buildShorelineFoamSegments(state);
|
|
6016
6263
|
const lightSources = collectSceneLightSources(state, visuals);
|
|
6017
6264
|
pushHarborGeometry(camera, viewport, sceneTriangles, state);
|
|
6018
6265
|
const cloth = buildClothSurface(
|
|
@@ -6084,6 +6331,7 @@ function renderScene(ctx, canvas, state, shipModel, dom, lightingFeatures, fluid
|
|
|
6084
6331
|
}
|
|
6085
6332
|
renderWaterMotionEffects(ctx, waterMotionEffects, camera, viewport);
|
|
6086
6333
|
renderWaterHighlights(ctx, water.bandMeshes, camera, viewport);
|
|
6334
|
+
renderShorelineFoamSegments(ctx, shorelineFoamSegments, camera, viewport);
|
|
6087
6335
|
drawTriangles(
|
|
6088
6336
|
ctx,
|
|
6089
6337
|
sceneTriangles,
|
|
@@ -6180,7 +6428,7 @@ function updateSceneState(state, dt, shipModel, featureAdapters) {
|
|
|
6180
6428
|
advanceShowcaseClothSimulationState(clothState, {
|
|
6181
6429
|
dt,
|
|
6182
6430
|
time: state.time,
|
|
6183
|
-
flagMotion: readVisualNumber(state.demoVisuals?.flagMotion, 0.
|
|
6431
|
+
flagMotion: readVisualNumber(state.demoVisuals?.flagMotion, 0.58),
|
|
6184
6432
|
waveInfluence: sampleWave(state, FLAG_LAYOUT.origin.x + FLAG_LAYOUT.width * 0.55, FLAG_LAYOUT.origin.z + FLAG_LAYOUT.width * 0.48, state.time)
|
|
6185
6433
|
});
|
|
6186
6434
|
updatePhysicsSnapshot(state, shipModel, featureAdapters.physics);
|
|
@@ -6395,7 +6643,7 @@ function updatePhysicsSnapshot(state, shipModel, physicsFeatures) {
|
|
|
6395
6643
|
}
|
|
6396
6644
|
});
|
|
6397
6645
|
}
|
|
6398
|
-
var STYLE_ID2, ROOT_CLASS, CAPTURE_CLASS, DEFAULT_CANVAS_WIDTH, DEFAULT_CANVAS_HEIGHT, CAPTURE_CANVAS_PIXEL_BUDGET, SHIP_SCALE, HARBOR_BOUNDS, CAMERA_PRESETS, FALLBACK_LIGHTING_DISTANCE_BANDS, FALLBACK_LIGHTING_PROFILE, FALLBACK_PHYSICS_PROFILE, FALLBACK_PERFORMANCE_LEVELS, SHOWCASE_FEATURE_LOADERS, DEFAULT_FLUID_BAND_THRESHOLDS, DEFAULT_CLOTH_BAND_THRESHOLDS, showcaseFocusModes, FOCUS_MODE_TRANSLATION_KEYS, SCENE_NOTE_KEYS, PHYSICS_SCENE_NOTE_KEYS, LEGACY_HARBOR_LAYOUT, SHOWCASE_ENVIRONMENT_LAYOUT, SHIP_LANTERNS, CUTTER_LANTERNS, HARBOR_TORCHES, FLAG_LAYOUT;
|
|
6646
|
+
var STYLE_ID2, ROOT_CLASS, CAPTURE_CLASS, DEFAULT_CANVAS_WIDTH, DEFAULT_CANVAS_HEIGHT, CAPTURE_CANVAS_PIXEL_BUDGET, SHIP_SCALE, HARBOR_BOUNDS, CAMERA_PRESETS, FALLBACK_LIGHTING_DISTANCE_BANDS, FALLBACK_LIGHTING_PROFILE, FALLBACK_PHYSICS_PROFILE, FALLBACK_PERFORMANCE_LEVELS, SHOWCASE_FEATURE_LOADERS, DEFAULT_FLUID_BAND_THRESHOLDS, DEFAULT_CLOTH_BAND_THRESHOLDS, showcaseFocusModes, FOCUS_MODE_TRANSLATION_KEYS, SCENE_NOTE_KEYS, PHYSICS_SCENE_NOTE_KEYS, LEGACY_HARBOR_LAYOUT, SHOWCASE_ENVIRONMENT_LAYOUT, SHIP_LANTERNS, CUTTER_LANTERNS, HARBOR_TORCHES, SHORELINE_FOAM_ANCHORS, FLAG_LAYOUT;
|
|
6399
6647
|
var init_showcase_runtime = __esm({
|
|
6400
6648
|
"src/showcase-runtime.js"() {
|
|
6401
6649
|
init_asset_url();
|
|
@@ -6586,6 +6834,13 @@ var init_showcase_runtime = __esm({
|
|
|
6586
6834
|
})
|
|
6587
6835
|
]);
|
|
6588
6836
|
SHOWCASE_ENVIRONMENT_LAYOUT = Object.freeze([
|
|
6837
|
+
Object.freeze({
|
|
6838
|
+
assetKey: "shoreline",
|
|
6839
|
+
position: Object.freeze({ x: 1.8, y: -0.04, z: 0.48 }),
|
|
6840
|
+
rotationY: -0.03,
|
|
6841
|
+
scale: 1.02,
|
|
6842
|
+
accent: 0.03
|
|
6843
|
+
}),
|
|
6589
6844
|
Object.freeze({
|
|
6590
6845
|
assetKey: "harbor-dock",
|
|
6591
6846
|
position: Object.freeze({ x: -4.6, y: 0.16, z: 0.7 }),
|
|
@@ -6616,6 +6871,18 @@ var init_showcase_runtime = __esm({
|
|
|
6616
6871
|
Object.freeze({ x: -8.6, y: 2.48, z: -0.72, glow: 1 }),
|
|
6617
6872
|
Object.freeze({ x: -10.4, y: 1.28, z: 0.82, glow: 0.92 })
|
|
6618
6873
|
]);
|
|
6874
|
+
SHORELINE_FOAM_ANCHORS = Object.freeze([
|
|
6875
|
+
Object.freeze({ x: -7.8, z: 3, length: 1.25, angle: -0.12 }),
|
|
6876
|
+
Object.freeze({ x: -6.3, z: 2.72, length: 0.92, angle: 0.08 }),
|
|
6877
|
+
Object.freeze({ x: -4.9, z: 3.16, length: 1.08, angle: -0.2 }),
|
|
6878
|
+
Object.freeze({ x: -3.2, z: 2.42, length: 0.76, angle: 0.16 }),
|
|
6879
|
+
Object.freeze({ x: -1.4, z: 2.82, length: 1.18, angle: -0.04 }),
|
|
6880
|
+
Object.freeze({ x: 0.4, z: 3.08, length: 0.88, angle: 0.14 }),
|
|
6881
|
+
Object.freeze({ x: 2.1, z: 2.56, length: 1.34, angle: -0.18 }),
|
|
6882
|
+
Object.freeze({ x: 3.8, z: 3, length: 0.94, angle: 0.1 }),
|
|
6883
|
+
Object.freeze({ x: 5.5, z: 2.72, length: 1.12, angle: -0.08 }),
|
|
6884
|
+
Object.freeze({ x: 7, z: 3.22, length: 0.72, angle: 0.18 })
|
|
6885
|
+
]);
|
|
6619
6886
|
FLAG_LAYOUT = Object.freeze({
|
|
6620
6887
|
origin: Object.freeze({ x: -3.5, y: 5.9, z: 2.4 }),
|
|
6621
6888
|
width: 4.8,
|
|
@@ -6630,6 +6897,7 @@ var index_exports = {};
|
|
|
6630
6897
|
__export(index_exports, {
|
|
6631
6898
|
GPU_SHOWCASE_PRODUCT_STUDIO_FEATURE: () => GPU_SHOWCASE_PRODUCT_STUDIO_FEATURE,
|
|
6632
6899
|
GPU_SHOWCASE_REALISTIC_MODELS_FEATURE: () => GPU_SHOWCASE_REALISTIC_MODELS_FEATURE,
|
|
6900
|
+
buildProductStudioSceneObjects: () => buildProductStudioSceneObjects,
|
|
6633
6901
|
createGpuSharedTranslator: () => createGpuSharedTranslator,
|
|
6634
6902
|
createProductStudioMeshes: () => createProductStudioMeshes,
|
|
6635
6903
|
gpuSharedEnGbTranslations: () => gpuSharedEnGbTranslations,
|
|
@@ -6715,6 +6983,7 @@ async function mountGpuShowcase2(options = {}) {
|
|
|
6715
6983
|
0 && (module.exports = {
|
|
6716
6984
|
GPU_SHOWCASE_PRODUCT_STUDIO_FEATURE,
|
|
6717
6985
|
GPU_SHOWCASE_REALISTIC_MODELS_FEATURE,
|
|
6986
|
+
buildProductStudioSceneObjects,
|
|
6718
6987
|
createGpuSharedTranslator,
|
|
6719
6988
|
createProductStudioMeshes,
|
|
6720
6989
|
gpuSharedEnGbTranslations,
|