@plasius/gpu-shared 0.1.20 → 1.0.1
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 +44 -1
- package/README.md +30 -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/{chunk-CH3ZS5TQ.js → chunk-Z6SOXBHL.js} +2 -2
- package/dist/chunk-Z6SOXBHL.js.map +1 -0
- package/dist/{gltf-loader-FMRC3OEV.js → gltf-loader-44ILCO7V.js} +2 -2
- package/dist/index.cjs +503 -189
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +8 -6
- 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-B544T6AM.js} +496 -189
- package/dist/showcase-runtime-B544T6AM.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 +521 -189
- package/src/translations/en-GB.js +1 -2
- package/dist/chunk-CH3ZS5TQ.js.map +0 -1
- 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
|
@@ -2,11 +2,11 @@ import {
|
|
|
2
2
|
GPU_SHOWCASE_REALISTIC_MODELS_FEATURE,
|
|
3
3
|
createGpuSharedTranslator,
|
|
4
4
|
gpuSharedTranslationKeys
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-Z6SOXBHL.js";
|
|
6
6
|
import {
|
|
7
7
|
loadGltfModel,
|
|
8
8
|
resolveShowcaseAssetUrl
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-KGKLNL4X.js";
|
|
10
10
|
import "./chunk-2GM64LB6.js";
|
|
11
11
|
import "./chunk-DGUM43GV.js";
|
|
12
12
|
|
|
@@ -764,6 +764,13 @@ var LEGACY_HARBOR_LAYOUT = Object.freeze([
|
|
|
764
764
|
})
|
|
765
765
|
]);
|
|
766
766
|
var SHOWCASE_ENVIRONMENT_LAYOUT = Object.freeze([
|
|
767
|
+
Object.freeze({
|
|
768
|
+
assetKey: "shoreline",
|
|
769
|
+
position: Object.freeze({ x: 1.8, y: -0.04, z: 0.48 }),
|
|
770
|
+
rotationY: -0.03,
|
|
771
|
+
scale: 1.02,
|
|
772
|
+
accent: 0.03
|
|
773
|
+
}),
|
|
767
774
|
Object.freeze({
|
|
768
775
|
assetKey: "harbor-dock",
|
|
769
776
|
position: Object.freeze({ x: -4.6, y: 0.16, z: 0.7 }),
|
|
@@ -794,6 +801,18 @@ var HARBOR_TORCHES = Object.freeze([
|
|
|
794
801
|
Object.freeze({ x: -8.6, y: 2.48, z: -0.72, glow: 1 }),
|
|
795
802
|
Object.freeze({ x: -10.4, y: 1.28, z: 0.82, glow: 0.92 })
|
|
796
803
|
]);
|
|
804
|
+
var SHORELINE_FOAM_ANCHORS = Object.freeze([
|
|
805
|
+
Object.freeze({ x: -7.8, z: 3, length: 1.25, angle: -0.12 }),
|
|
806
|
+
Object.freeze({ x: -6.3, z: 2.72, length: 0.92, angle: 0.08 }),
|
|
807
|
+
Object.freeze({ x: -4.9, z: 3.16, length: 1.08, angle: -0.2 }),
|
|
808
|
+
Object.freeze({ x: -3.2, z: 2.42, length: 0.76, angle: 0.16 }),
|
|
809
|
+
Object.freeze({ x: -1.4, z: 2.82, length: 1.18, angle: -0.04 }),
|
|
810
|
+
Object.freeze({ x: 0.4, z: 3.08, length: 0.88, angle: 0.14 }),
|
|
811
|
+
Object.freeze({ x: 2.1, z: 2.56, length: 1.34, angle: -0.18 }),
|
|
812
|
+
Object.freeze({ x: 3.8, z: 3, length: 0.94, angle: 0.1 }),
|
|
813
|
+
Object.freeze({ x: 5.5, z: 2.72, length: 1.12, angle: -0.08 }),
|
|
814
|
+
Object.freeze({ x: 7, z: 3.22, length: 0.72, angle: 0.18 })
|
|
815
|
+
]);
|
|
797
816
|
var FLAG_LAYOUT = Object.freeze({
|
|
798
817
|
origin: Object.freeze({ x: -3.5, y: 5.9, z: 2.4 }),
|
|
799
818
|
width: 4.8,
|
|
@@ -835,31 +854,31 @@ function injectStyles() {
|
|
|
835
854
|
box-sizing: border-box;
|
|
836
855
|
}
|
|
837
856
|
.plasius-demo {
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
gap: 20px;
|
|
843
|
-
}
|
|
844
|
-
.plasius-demo__hero,
|
|
845
|
-
.plasius-demo__layout {
|
|
846
|
-
display: grid;
|
|
847
|
-
gap: 20px;
|
|
848
|
-
}
|
|
849
|
-
.plasius-demo__hero {
|
|
850
|
-
grid-template-columns: minmax(0, 1.5fr) minmax(320px, 0.85fr);
|
|
851
|
-
align-items: start;
|
|
857
|
+
position: relative;
|
|
858
|
+
width: 100%;
|
|
859
|
+
min-height: 100dvh;
|
|
860
|
+
overflow: hidden;
|
|
852
861
|
}
|
|
853
862
|
.plasius-panel {
|
|
854
863
|
border: 1px solid var(--plasius-border);
|
|
855
|
-
border-radius:
|
|
864
|
+
border-radius: 8px;
|
|
856
865
|
background: var(--plasius-panel);
|
|
857
866
|
box-shadow: var(--plasius-shadow);
|
|
858
867
|
backdrop-filter: blur(12px);
|
|
859
868
|
}
|
|
860
869
|
.plasius-demo__hero-card,
|
|
861
870
|
.plasius-demo__status {
|
|
862
|
-
|
|
871
|
+
position: absolute;
|
|
872
|
+
z-index: 3;
|
|
873
|
+
padding: 10px 12px;
|
|
874
|
+
}
|
|
875
|
+
.plasius-demo__hero-card {
|
|
876
|
+
display: none;
|
|
877
|
+
}
|
|
878
|
+
.plasius-demo__status {
|
|
879
|
+
left: 16px;
|
|
880
|
+
bottom: 84px;
|
|
881
|
+
max-width: min(360px, calc(100vw - 32px));
|
|
863
882
|
}
|
|
864
883
|
.plasius-demo__eyebrow {
|
|
865
884
|
margin: 0 0 8px;
|
|
@@ -882,31 +901,36 @@ function injectStyles() {
|
|
|
882
901
|
.plasius-demo__status-badge {
|
|
883
902
|
width: fit-content;
|
|
884
903
|
margin: 0;
|
|
885
|
-
padding:
|
|
886
|
-
border-radius:
|
|
904
|
+
padding: 6px 9px;
|
|
905
|
+
border-radius: 6px;
|
|
887
906
|
background: rgba(243, 177, 106, 0.14);
|
|
888
907
|
color: var(--plasius-accent);
|
|
889
908
|
font-weight: 700;
|
|
909
|
+
font-size: 12px;
|
|
890
910
|
}
|
|
891
911
|
.plasius-demo__status-text {
|
|
892
912
|
margin: 10px 0 0;
|
|
893
913
|
color: var(--plasius-muted);
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
.plasius-demo__layout {
|
|
897
|
-
grid-template-columns: minmax(0, 1.45fr) minmax(320px, 0.68fr);
|
|
898
|
-
align-items: start;
|
|
914
|
+
font-size: 12px;
|
|
915
|
+
line-height: 1.45;
|
|
899
916
|
}
|
|
900
917
|
.plasius-demo__canvas-panel {
|
|
901
|
-
|
|
902
|
-
|
|
918
|
+
position: absolute;
|
|
919
|
+
inset: 0;
|
|
920
|
+
padding: 0;
|
|
921
|
+
border: 0;
|
|
922
|
+
border-radius: 0;
|
|
923
|
+
background: transparent;
|
|
924
|
+
box-shadow: none;
|
|
925
|
+
backdrop-filter: none;
|
|
903
926
|
}
|
|
904
927
|
.plasius-demo__canvas {
|
|
905
928
|
width: 100%;
|
|
906
|
-
|
|
929
|
+
height: 100%;
|
|
930
|
+
min-height: 100dvh;
|
|
907
931
|
display: block;
|
|
908
|
-
border
|
|
909
|
-
border:
|
|
932
|
+
border: 0;
|
|
933
|
+
border-radius: 0;
|
|
910
934
|
background: linear-gradient(180deg, #071220 0%, #132440 42%, #10344b 42%, #05111d 100%);
|
|
911
935
|
}
|
|
912
936
|
.${CAPTURE_CLASS} .plasius-demo {
|
|
@@ -919,6 +943,8 @@ function injectStyles() {
|
|
|
919
943
|
.${CAPTURE_CLASS} .plasius-demo__toolbar,
|
|
920
944
|
.${CAPTURE_CLASS} .plasius-demo__legend,
|
|
921
945
|
.${CAPTURE_CLASS} .plasius-demo__sidebar,
|
|
946
|
+
.${CAPTURE_CLASS} .plasius-demo__diagnostics,
|
|
947
|
+
.${CAPTURE_CLASS} .plasius-demo__status,
|
|
922
948
|
.${CAPTURE_CLASS} .plasius-demo__footer {
|
|
923
949
|
display: none;
|
|
924
950
|
}
|
|
@@ -945,12 +971,14 @@ function injectStyles() {
|
|
|
945
971
|
}
|
|
946
972
|
.plasius-demo__toolbar {
|
|
947
973
|
position: absolute;
|
|
948
|
-
top:
|
|
949
|
-
left:
|
|
974
|
+
top: 84px;
|
|
975
|
+
left: 16px;
|
|
976
|
+
z-index: 4;
|
|
950
977
|
display: flex;
|
|
951
|
-
gap:
|
|
978
|
+
gap: 8px;
|
|
952
979
|
flex-wrap: wrap;
|
|
953
980
|
align-items: center;
|
|
981
|
+
max-width: min(560px, calc(100vw - 32px));
|
|
954
982
|
}
|
|
955
983
|
.plasius-demo button,
|
|
956
984
|
.plasius-demo label,
|
|
@@ -962,22 +990,63 @@ function injectStyles() {
|
|
|
962
990
|
.plasius-demo .plasius-toggle,
|
|
963
991
|
.plasius-demo select {
|
|
964
992
|
border: 1px solid rgba(159, 185, 223, 0.18);
|
|
965
|
-
border-radius:
|
|
993
|
+
border-radius: 6px;
|
|
966
994
|
background: rgba(9, 20, 34, 0.84);
|
|
967
995
|
color: var(--plasius-ink);
|
|
968
|
-
padding: 10px
|
|
996
|
+
padding: 8px 10px;
|
|
969
997
|
}
|
|
970
998
|
.plasius-toggle {
|
|
971
999
|
display: inline-flex;
|
|
972
1000
|
align-items: center;
|
|
973
1001
|
gap: 8px;
|
|
974
1002
|
}
|
|
1003
|
+
.plasius-demo__diagnostics {
|
|
1004
|
+
position: absolute;
|
|
1005
|
+
right: 16px;
|
|
1006
|
+
bottom: 84px;
|
|
1007
|
+
z-index: 4;
|
|
1008
|
+
max-width: min(420px, calc(100vw - 32px));
|
|
1009
|
+
color: var(--plasius-ink);
|
|
1010
|
+
font-family: "JetBrains Mono", monospace;
|
|
1011
|
+
font-size: 12px;
|
|
1012
|
+
}
|
|
1013
|
+
.plasius-demo__diagnostics summary {
|
|
1014
|
+
width: fit-content;
|
|
1015
|
+
margin-left: auto;
|
|
1016
|
+
border: 1px solid rgba(159, 185, 223, 0.18);
|
|
1017
|
+
border-radius: 6px;
|
|
1018
|
+
padding: 8px 10px;
|
|
1019
|
+
background: rgba(9, 20, 34, 0.84);
|
|
1020
|
+
cursor: pointer;
|
|
1021
|
+
list-style: none;
|
|
1022
|
+
}
|
|
1023
|
+
.plasius-demo__diagnostics summary::-webkit-details-marker {
|
|
1024
|
+
display: none;
|
|
1025
|
+
}
|
|
1026
|
+
.plasius-demo__diagnostics[open] {
|
|
1027
|
+
width: min(420px, calc(100vw - 32px));
|
|
1028
|
+
}
|
|
1029
|
+
.plasius-demo__diagnostics[open] summary {
|
|
1030
|
+
margin-bottom: 8px;
|
|
1031
|
+
background: rgba(243, 177, 106, 0.14);
|
|
1032
|
+
color: var(--plasius-accent);
|
|
1033
|
+
}
|
|
975
1034
|
.plasius-demo__sidebar {
|
|
976
1035
|
display: grid;
|
|
977
|
-
gap:
|
|
1036
|
+
gap: 8px;
|
|
1037
|
+
max-height: min(58vh, 520px);
|
|
1038
|
+
overflow: auto;
|
|
978
1039
|
}
|
|
979
1040
|
.plasius-demo__card {
|
|
980
|
-
padding:
|
|
1041
|
+
padding: 10px;
|
|
1042
|
+
}
|
|
1043
|
+
.plasius-demo__card h2 {
|
|
1044
|
+
margin: 0;
|
|
1045
|
+
color: rgba(226, 236, 255, 0.72);
|
|
1046
|
+
font-family: "JetBrains Mono", monospace;
|
|
1047
|
+
font-size: 11px;
|
|
1048
|
+
letter-spacing: 0.08em;
|
|
1049
|
+
text-transform: uppercase;
|
|
981
1050
|
}
|
|
982
1051
|
.plasius-demo__metrics,
|
|
983
1052
|
.plasius-demo__metrics li {
|
|
@@ -986,27 +1055,19 @@ function injectStyles() {
|
|
|
986
1055
|
list-style: none;
|
|
987
1056
|
}
|
|
988
1057
|
.plasius-demo__metrics {
|
|
989
|
-
margin-top:
|
|
1058
|
+
margin-top: 8px;
|
|
990
1059
|
display: grid;
|
|
991
|
-
gap:
|
|
1060
|
+
gap: 5px;
|
|
992
1061
|
color: var(--plasius-muted);
|
|
993
|
-
|
|
1062
|
+
font-size: 12px;
|
|
1063
|
+
line-height: 1.35;
|
|
994
1064
|
}
|
|
995
1065
|
.plasius-demo__metrics li {
|
|
996
1066
|
border-top: 1px solid rgba(21, 32, 40, 0.08);
|
|
997
|
-
padding-top:
|
|
1067
|
+
padding-top: 5px;
|
|
998
1068
|
}
|
|
999
1069
|
.plasius-demo__legend {
|
|
1000
|
-
|
|
1001
|
-
right: 24px;
|
|
1002
|
-
bottom: 24px;
|
|
1003
|
-
padding: 10px 14px;
|
|
1004
|
-
border-radius: 16px;
|
|
1005
|
-
background: rgba(9, 20, 34, 0.82);
|
|
1006
|
-
border: 1px solid rgba(159, 185, 223, 0.16);
|
|
1007
|
-
color: var(--plasius-muted);
|
|
1008
|
-
font-size: 12px;
|
|
1009
|
-
line-height: 1.45;
|
|
1070
|
+
display: none;
|
|
1010
1071
|
}
|
|
1011
1072
|
.plasius-demo__legend strong {
|
|
1012
1073
|
display: block;
|
|
@@ -1014,15 +1075,62 @@ function injectStyles() {
|
|
|
1014
1075
|
margin-bottom: 4px;
|
|
1015
1076
|
}
|
|
1016
1077
|
.plasius-demo__footer {
|
|
1017
|
-
|
|
1018
|
-
color: rgba(226, 236, 255, 0.68);
|
|
1019
|
-
font-size: 13px;
|
|
1020
|
-
line-height: 1.6;
|
|
1078
|
+
display: none;
|
|
1021
1079
|
}
|
|
1022
1080
|
@media (max-width: 1200px) {
|
|
1023
|
-
.plasius-
|
|
1024
|
-
|
|
1025
|
-
|
|
1081
|
+
.plasius-demo__toolbar {
|
|
1082
|
+
top: 92px;
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
@media (max-width: 640px) {
|
|
1086
|
+
.plasius-demo__status {
|
|
1087
|
+
left: 10px;
|
|
1088
|
+
bottom: 10px;
|
|
1089
|
+
max-width: calc(100vw - 126px);
|
|
1090
|
+
padding: 6px 8px;
|
|
1091
|
+
}
|
|
1092
|
+
.plasius-demo__status-text {
|
|
1093
|
+
display: none;
|
|
1094
|
+
}
|
|
1095
|
+
.plasius-demo__status-badge {
|
|
1096
|
+
max-width: calc(100vw - 142px);
|
|
1097
|
+
overflow: hidden;
|
|
1098
|
+
text-overflow: ellipsis;
|
|
1099
|
+
white-space: nowrap;
|
|
1100
|
+
}
|
|
1101
|
+
.plasius-demo__toolbar {
|
|
1102
|
+
top: 10px;
|
|
1103
|
+
left: 10px;
|
|
1104
|
+
right: 10px;
|
|
1105
|
+
max-width: calc(100vw - 20px);
|
|
1106
|
+
flex-wrap: nowrap;
|
|
1107
|
+
overflow-x: auto;
|
|
1108
|
+
padding-bottom: 4px;
|
|
1109
|
+
scrollbar-width: none;
|
|
1110
|
+
}
|
|
1111
|
+
.plasius-demo__toolbar::-webkit-scrollbar {
|
|
1112
|
+
display: none;
|
|
1113
|
+
}
|
|
1114
|
+
.plasius-demo button,
|
|
1115
|
+
.plasius-demo .plasius-toggle,
|
|
1116
|
+
.plasius-demo select {
|
|
1117
|
+
padding: 7px 8px;
|
|
1118
|
+
font-size: 12px;
|
|
1119
|
+
white-space: nowrap;
|
|
1120
|
+
}
|
|
1121
|
+
.plasius-demo__diagnostics {
|
|
1122
|
+
right: 10px;
|
|
1123
|
+
bottom: 10px;
|
|
1124
|
+
}
|
|
1125
|
+
.plasius-demo__diagnostics[open] {
|
|
1126
|
+
bottom: 56px;
|
|
1127
|
+
left: 10px;
|
|
1128
|
+
right: 10px;
|
|
1129
|
+
width: auto;
|
|
1130
|
+
max-width: none;
|
|
1131
|
+
}
|
|
1132
|
+
.plasius-demo__diagnostics[open] .plasius-demo__sidebar {
|
|
1133
|
+
max-height: min(42vh, 340px);
|
|
1026
1134
|
}
|
|
1027
1135
|
}
|
|
1028
1136
|
`;
|
|
@@ -1363,40 +1471,74 @@ function buildTrianglesFromMesh(mesh, transform, colorOverride, camera, viewport
|
|
|
1363
1471
|
}
|
|
1364
1472
|
}
|
|
1365
1473
|
}
|
|
1366
|
-
|
|
1367
|
-
|
|
1474
|
+
function createShowcaseAssetCatalog({
|
|
1475
|
+
mode,
|
|
1476
|
+
ships,
|
|
1477
|
+
environment,
|
|
1478
|
+
primaryShipKey = "brigantine",
|
|
1479
|
+
fallbackReason = null
|
|
1480
|
+
}) {
|
|
1481
|
+
return Object.freeze({
|
|
1482
|
+
mode,
|
|
1483
|
+
primaryShipKey,
|
|
1484
|
+
ships: Object.freeze(ships),
|
|
1485
|
+
environment: Object.freeze(environment),
|
|
1486
|
+
fallbackReason
|
|
1487
|
+
});
|
|
1488
|
+
}
|
|
1489
|
+
function normalizeAssetCatalogFailureReason(error) {
|
|
1490
|
+
if (typeof error?.message === "string" && error.message.trim().length > 0) {
|
|
1491
|
+
return error.message;
|
|
1492
|
+
}
|
|
1493
|
+
return "showcase asset loading failed";
|
|
1494
|
+
}
|
|
1495
|
+
async function loadShowcaseAssetCatalog({ includeSecondaryShip = true } = {}) {
|
|
1496
|
+
const [brigantine, lighthouse, harborDock, shoreline] = await Promise.all([
|
|
1368
1497
|
loadGltfModel(resolveShowcaseAssetUrl("brigantine")),
|
|
1369
|
-
loadGltfModel(resolveShowcaseAssetUrl("cutter")),
|
|
1370
1498
|
loadGltfModel(resolveShowcaseAssetUrl("lighthouse")),
|
|
1371
|
-
loadGltfModel(resolveShowcaseAssetUrl("harbor-dock"))
|
|
1499
|
+
loadGltfModel(resolveShowcaseAssetUrl("harbor-dock")),
|
|
1500
|
+
loadGltfModel(resolveShowcaseAssetUrl("shoreline"))
|
|
1372
1501
|
]);
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1502
|
+
const ships = {
|
|
1503
|
+
brigantine
|
|
1504
|
+
};
|
|
1505
|
+
if (includeSecondaryShip) {
|
|
1506
|
+
ships.cutter = await loadGltfModel(resolveShowcaseAssetUrl("cutter"));
|
|
1507
|
+
}
|
|
1508
|
+
return createShowcaseAssetCatalog({
|
|
1509
|
+
mode: includeSecondaryShip ? "modeled-rich" : "modeled-baseline",
|
|
1510
|
+
ships,
|
|
1511
|
+
environment: {
|
|
1380
1512
|
lighthouse,
|
|
1381
|
-
"harbor-dock": harborDock
|
|
1382
|
-
|
|
1513
|
+
"harbor-dock": harborDock,
|
|
1514
|
+
shoreline
|
|
1515
|
+
}
|
|
1383
1516
|
});
|
|
1384
1517
|
}
|
|
1385
|
-
function createLegacyShowcaseAssetCatalog() {
|
|
1386
|
-
const brigantine = loadGltfModel(resolveShowcaseAssetUrl("brigantine"));
|
|
1387
|
-
return
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1518
|
+
async function createLegacyShowcaseAssetCatalog(error = null) {
|
|
1519
|
+
const brigantine = await loadGltfModel(resolveShowcaseAssetUrl("brigantine"));
|
|
1520
|
+
return createShowcaseAssetCatalog({
|
|
1521
|
+
mode: "legacy-fallback",
|
|
1522
|
+
ships: {
|
|
1523
|
+
brigantine
|
|
1524
|
+
},
|
|
1525
|
+
environment: {},
|
|
1526
|
+
fallbackReason: normalizeAssetCatalogFailureReason(error)
|
|
1527
|
+
});
|
|
1528
|
+
}
|
|
1529
|
+
async function loadShowcaseAssetCatalogWithFallback({ includeSecondaryShip = true } = {}) {
|
|
1530
|
+
try {
|
|
1531
|
+
return await loadShowcaseAssetCatalog({ includeSecondaryShip });
|
|
1532
|
+
} catch (error) {
|
|
1533
|
+
return createLegacyShowcaseAssetCatalog(error);
|
|
1534
|
+
}
|
|
1396
1535
|
}
|
|
1397
1536
|
function resolveShipModel(state, ship, fallbackModel = null) {
|
|
1398
1537
|
return state.assetCatalog?.ships?.[ship.modelKey ?? state.assetCatalog?.primaryShipKey ?? "brigantine"] ?? fallbackModel ?? state.shipModel;
|
|
1399
1538
|
}
|
|
1539
|
+
function hasModeledHarborEnvironment(state) {
|
|
1540
|
+
return Object.keys(state.assetCatalog?.environment ?? {}).length > 0;
|
|
1541
|
+
}
|
|
1400
1542
|
function createPerformanceGovernor(performanceFeatures) {
|
|
1401
1543
|
const createQualityLadderAdapter = assertRequiredFunction(
|
|
1402
1544
|
performanceFeatures,
|
|
@@ -1504,24 +1646,27 @@ function buildDemoDom(root, options) {
|
|
|
1504
1646
|
${t(gpuSharedTranslationKeys.legendCollisions)}
|
|
1505
1647
|
</div>
|
|
1506
1648
|
</section>
|
|
1507
|
-
<
|
|
1508
|
-
<
|
|
1509
|
-
|
|
1510
|
-
<
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
<
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
<
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
<
|
|
1523
|
-
|
|
1524
|
-
|
|
1649
|
+
<details class="plasius-demo__diagnostics">
|
|
1650
|
+
<summary>${t(gpuSharedTranslationKeys.debugTelemetry)}</summary>
|
|
1651
|
+
<aside class="plasius-demo__sidebar">
|
|
1652
|
+
<section class="plasius-panel plasius-demo__card">
|
|
1653
|
+
<h2>${t(gpuSharedTranslationKeys.sceneState)}</h2>
|
|
1654
|
+
<ul id="sceneMetrics" class="plasius-demo__metrics"></ul>
|
|
1655
|
+
</section>
|
|
1656
|
+
<section class="plasius-panel plasius-demo__card">
|
|
1657
|
+
<h2>${t(gpuSharedTranslationKeys.qualityBudgets)}</h2>
|
|
1658
|
+
<ul id="qualityMetrics" class="plasius-demo__metrics"></ul>
|
|
1659
|
+
</section>
|
|
1660
|
+
<section class="plasius-panel plasius-demo__card">
|
|
1661
|
+
<h2>${t(gpuSharedTranslationKeys.debugTelemetry)}</h2>
|
|
1662
|
+
<ul id="debugMetrics" class="plasius-demo__metrics"></ul>
|
|
1663
|
+
</section>
|
|
1664
|
+
<section class="plasius-panel plasius-demo__card">
|
|
1665
|
+
<h2>${t(gpuSharedTranslationKeys.notes)}</h2>
|
|
1666
|
+
<ul id="sceneNotes" class="plasius-demo__metrics"></ul>
|
|
1667
|
+
</section>
|
|
1668
|
+
</aside>
|
|
1669
|
+
</details>
|
|
1525
1670
|
</section>
|
|
1526
1671
|
<p class="plasius-demo__footer">
|
|
1527
1672
|
This visual example is shared across the GPU packages to keep manual validation fast and consistent.
|
|
@@ -1885,11 +2030,11 @@ function advanceShowcaseClothSimulationState(clothState, options = {}) {
|
|
|
1885
2030
|
1 + Math.sin(gustPhase * 0.74) * 0.18
|
|
1886
2031
|
)
|
|
1887
2032
|
);
|
|
1888
|
-
const windStrength = (
|
|
2033
|
+
const windStrength = (0.94 + broadMotion * 0.82 + wrinkleLayers * 0.08) * flagMotion * (0.36 + u * 0.92);
|
|
1889
2034
|
const wrinkleForce = vec3(
|
|
1890
|
-
Math.sin(wrinklePhase) * 0.
|
|
1891
|
-
Math.cos(wrinklePhase * 0.7) * 0.
|
|
1892
|
-
Math.cos(wrinklePhase) * 0.
|
|
2035
|
+
Math.sin(wrinklePhase) * 0.12 * wrinkleMotion * flagMotion,
|
|
2036
|
+
Math.cos(wrinklePhase * 0.7) * 0.045 * wrinkleMotion,
|
|
2037
|
+
Math.cos(wrinklePhase) * 0.08 * broadMotion * flagMotion
|
|
1893
2038
|
);
|
|
1894
2039
|
const acceleration = addVec3(
|
|
1895
2040
|
vec3(0, -0.48 - u * 0.08, 0),
|
|
@@ -1946,25 +2091,25 @@ function resolveVisualConfig(nearLighting, lightingSnapshot, customVisuals = {})
|
|
|
1946
2091
|
ambientMist: "rgba(41, 63, 97, 0.16)",
|
|
1947
2092
|
reflectionStrength: lightingSnapshot.currentLevel.config.reflectionStrength,
|
|
1948
2093
|
shadowAccent: lightingSnapshot.currentLevel.config.shadowStrength,
|
|
1949
|
-
waveAmplitude: 0.
|
|
2094
|
+
waveAmplitude: 0.82,
|
|
1950
2095
|
waveDirection: { x: 0.88, z: 0.28 },
|
|
1951
|
-
wavePhaseSpeed: 0.
|
|
1952
|
-
wakeStrength: 0.
|
|
1953
|
-
wakeLength:
|
|
1954
|
-
collisionRippleStrength: 0.
|
|
1955
|
-
waterNear: { r: 0.
|
|
1956
|
-
waterFar: { r: 0.
|
|
2096
|
+
wavePhaseSpeed: 0.74,
|
|
2097
|
+
wakeStrength: 0.24,
|
|
2098
|
+
wakeLength: 17,
|
|
2099
|
+
collisionRippleStrength: 0.22,
|
|
2100
|
+
waterNear: { r: 0.05, g: 0.2, b: 0.3 },
|
|
2101
|
+
waterFar: { r: 0.13, g: 0.31, b: 0.45 },
|
|
1957
2102
|
harborWall: { r: 0.26, g: 0.24, b: 0.28 },
|
|
1958
2103
|
harborDeck: { r: 0.33, g: 0.22, b: 0.16 },
|
|
1959
2104
|
harborTower: { r: 0.23, g: 0.24, b: 0.29 },
|
|
1960
|
-
flagColor: { r: 0.
|
|
1961
|
-
flagMotion: 0.
|
|
2105
|
+
flagColor: { r: 0.54, g: 0.13, b: 0.11 },
|
|
2106
|
+
flagMotion: 0.58,
|
|
1962
2107
|
lanternCore: { r: 0.98, g: 0.8, b: 0.48 },
|
|
1963
2108
|
lanternGlow: { r: 1, g: 0.56, b: 0.2 },
|
|
1964
2109
|
lanternReflectionStrength: 0.42,
|
|
1965
2110
|
torchCore: { r: 0.99, g: 0.72, b: 0.36 },
|
|
1966
2111
|
torchGlow: { r: 0.98, g: 0.38, b: 0.15 },
|
|
1967
|
-
collisionFlash: "rgba(255, 212, 168, 0.
|
|
2112
|
+
collisionFlash: "rgba(255, 212, 168, 0.08)"
|
|
1968
2113
|
};
|
|
1969
2114
|
return {
|
|
1970
2115
|
skyTop: typeof customVisuals.skyTop === "string" ? customVisuals.skyTop : defaults.skyTop,
|
|
@@ -2021,6 +2166,11 @@ function buildClothSurface(model, state, meshDetail, visuals, clothFeatures) {
|
|
|
2021
2166
|
representation: clothPresentation.representation,
|
|
2022
2167
|
continuity: clothPresentation.continuity,
|
|
2023
2168
|
color: visuals.flagColor,
|
|
2169
|
+
material: Object.freeze({
|
|
2170
|
+
weaveAlpha: clothPresentation.band === "near" ? 0.22 : 0.12,
|
|
2171
|
+
foldAlpha: clothPresentation.band === "near" ? 0.3 : 0.18,
|
|
2172
|
+
edgeHighlightAlpha: clothPresentation.band === "near" ? 0.42 : 0.28
|
|
2173
|
+
}),
|
|
2024
2174
|
positions: clothState.positions.map((point) => vec3(point.x, point.y, point.z)),
|
|
2025
2175
|
indices: clothState.indices,
|
|
2026
2176
|
grid: { rows: clothState.rows, cols: clothState.cols }
|
|
@@ -2104,7 +2254,7 @@ function buildWaterMotionEffects(state) {
|
|
|
2104
2254
|
impulse.z
|
|
2105
2255
|
),
|
|
2106
2256
|
radius,
|
|
2107
|
-
opacity: clamp(impulse.life * 0.
|
|
2257
|
+
opacity: clamp(impulse.life * 0.13, 0.035, 0.15)
|
|
2108
2258
|
});
|
|
2109
2259
|
});
|
|
2110
2260
|
for (const ship of state.ships) {
|
|
@@ -2117,7 +2267,7 @@ function buildWaterMotionEffects(state) {
|
|
|
2117
2267
|
const lateral = vec3(-direction.z, 0, direction.x);
|
|
2118
2268
|
const points = [];
|
|
2119
2269
|
for (let sampleIndex = 0; sampleIndex < 6; sampleIndex += 1) {
|
|
2120
|
-
const along = 1 + sampleIndex * 1.
|
|
2270
|
+
const along = 1 + sampleIndex * 1.55;
|
|
2121
2271
|
const lateralOffset = Math.sin(state.time * 1.2 + sampleIndex * 0.8 + readVisualNumber(ship.wanderPhase, 0)) * 0.12;
|
|
2122
2272
|
const worldPoint = addVec3(
|
|
2123
2273
|
ship.position,
|
|
@@ -2130,13 +2280,14 @@ function buildWaterMotionEffects(state) {
|
|
|
2130
2280
|
sampleWave(state, worldPoint.x, worldPoint.z, state.time) * 0.24 + 0.04,
|
|
2131
2281
|
worldPoint.z
|
|
2132
2282
|
),
|
|
2133
|
-
width: 0.
|
|
2283
|
+
width: 0.3 + sampleIndex * 0.11,
|
|
2284
|
+
foam: clamp(0.28 - sampleIndex * 0.028 + speed * 0.025, 0.1, 0.34)
|
|
2134
2285
|
})
|
|
2135
2286
|
);
|
|
2136
2287
|
}
|
|
2137
2288
|
wakeTrails.push(
|
|
2138
2289
|
Object.freeze({
|
|
2139
|
-
opacity: clamp(0.
|
|
2290
|
+
opacity: clamp(0.1 + speed * 0.048, 0.12, 0.24),
|
|
2140
2291
|
points: Object.freeze(points)
|
|
2141
2292
|
})
|
|
2142
2293
|
);
|
|
@@ -2146,6 +2297,27 @@ function buildWaterMotionEffects(state) {
|
|
|
2146
2297
|
rippleRings: Object.freeze(rippleRings)
|
|
2147
2298
|
});
|
|
2148
2299
|
}
|
|
2300
|
+
function buildShorelineFoamSegments(state) {
|
|
2301
|
+
return Object.freeze(
|
|
2302
|
+
SHORELINE_FOAM_ANCHORS.map((anchor, index) => {
|
|
2303
|
+
const pulse = 0.5 + Math.sin(state.time * 0.84 + index * 1.17) * 0.5;
|
|
2304
|
+
const drift = Math.sin(state.time * 0.38 + index * 0.61) * 0.1;
|
|
2305
|
+
const direction = normalizeVec3(vec3(Math.cos(anchor.angle), 0, Math.sin(anchor.angle)));
|
|
2306
|
+
const center = vec3(
|
|
2307
|
+
anchor.x + direction.x * drift,
|
|
2308
|
+
sampleWave(state, anchor.x, anchor.z, state.time) * 0.12 - 0.02,
|
|
2309
|
+
anchor.z + direction.z * drift
|
|
2310
|
+
);
|
|
2311
|
+
return Object.freeze({
|
|
2312
|
+
center,
|
|
2313
|
+
direction,
|
|
2314
|
+
length: anchor.length * (0.78 + pulse * 0.34),
|
|
2315
|
+
width: 0.16 + pulse * 0.12,
|
|
2316
|
+
opacity: 0.07 + pulse * 0.12
|
|
2317
|
+
});
|
|
2318
|
+
})
|
|
2319
|
+
);
|
|
2320
|
+
}
|
|
2149
2321
|
function buildWaterBands(state, fluidDetail, visuals, fluidFeatures) {
|
|
2150
2322
|
const resolvedFluidFeatures = normalizeFluidFeatureAdapters(fluidFeatures);
|
|
2151
2323
|
const fluidPlan = resolvedFluidFeatures.createPlan({
|
|
@@ -2168,9 +2340,9 @@ function buildWaterBands(state, fluidDetail, visuals, fluidFeatures) {
|
|
|
2168
2340
|
const representation = fluidPlan.representations.find((entry) => entry.band === bandSpec.band) ?? fluidPlan.representations[0];
|
|
2169
2341
|
const continuity = resolvedFluidFeatures.createContinuityEnvelope({ fluidBodyId: "harbor" });
|
|
2170
2342
|
const bandContinuity = resolveFluidBandContinuity(continuity, bandSpec.band);
|
|
2171
|
-
const bandResolution = bandSpec.band === "near" ? fluidDetail.nearResolution : bandSpec.band === "mid" ? fluidDetail.midResolution : bandSpec.band === "far" ? 5 : 3;
|
|
2172
|
-
const cols = Math.max(4, bandResolution * 2);
|
|
2173
|
-
const rows = Math.max(4, bandResolution + 2);
|
|
2343
|
+
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;
|
|
2344
|
+
const cols = Math.max(4, bandResolution * (bandSpec.band === "near" ? 3 : 2));
|
|
2345
|
+
const rows = Math.max(4, bandResolution + (bandSpec.band === "near" ? 5 : 2));
|
|
2174
2346
|
const positions = [];
|
|
2175
2347
|
const indices = [];
|
|
2176
2348
|
const originX = -bandSpec.width * 0.5;
|
|
@@ -2181,7 +2353,9 @@ function buildWaterBands(state, fluidDetail, visuals, fluidFeatures) {
|
|
|
2181
2353
|
const v = row / (rows - 1);
|
|
2182
2354
|
const x = originX + bandSpec.width * u;
|
|
2183
2355
|
const z = originZ + bandSpec.depth * v;
|
|
2184
|
-
const
|
|
2356
|
+
const baseHeight = bandSpec.y + sampleWave(state, x, z, state.time) * bandContinuity.amplitudeFloor * (bandSpec.band === "near" ? 0.9 : bandSpec.band === "mid" ? 0.55 : 0.3);
|
|
2357
|
+
const detailHeight = bandSpec.band === "near" ? Math.sin(x * 1.25 + z * 0.42 - state.time * 2.4) * 0.035 : 0;
|
|
2358
|
+
const y = baseHeight + detailHeight;
|
|
2185
2359
|
positions.push(vec3(x, y, z));
|
|
2186
2360
|
}
|
|
2187
2361
|
}
|
|
@@ -2210,7 +2384,12 @@ function buildWaterBands(state, fluidDetail, visuals, fluidFeatures) {
|
|
|
2210
2384
|
r: mix(visuals.waterFar.r, 0.76, 0.2),
|
|
2211
2385
|
g: mix(visuals.waterFar.g, 0.78, 0.2),
|
|
2212
2386
|
b: mix(visuals.waterFar.b, 0.82, 0.2)
|
|
2213
|
-
}
|
|
2387
|
+
},
|
|
2388
|
+
material: Object.freeze({
|
|
2389
|
+
highlightAlpha: bandSpec.band === "near" ? 0.2 : bandSpec.band === "mid" ? 0.13 : 0.07,
|
|
2390
|
+
foamAlpha: bandSpec.band === "near" ? 0.28 : bandSpec.band === "mid" ? 0.14 : 0.05,
|
|
2391
|
+
microRippleScale: bandSpec.band === "near" ? 1 : bandSpec.band === "mid" ? 0.58 : 0.28
|
|
2392
|
+
})
|
|
2214
2393
|
});
|
|
2215
2394
|
}
|
|
2216
2395
|
return { fluidPlan, bandMeshes };
|
|
@@ -2286,15 +2465,15 @@ function createSceneState(options, featureAdapters) {
|
|
|
2286
2465
|
{
|
|
2287
2466
|
id: "northwind",
|
|
2288
2467
|
modelKey: "brigantine",
|
|
2289
|
-
position: vec3(-
|
|
2290
|
-
velocity: vec3(
|
|
2291
|
-
rotationY:
|
|
2292
|
-
angularVelocity: 0.
|
|
2468
|
+
position: vec3(-7.8, 0, 11.2),
|
|
2469
|
+
velocity: vec3(1.08, 0, -0.18),
|
|
2470
|
+
rotationY: 1.38,
|
|
2471
|
+
angularVelocity: 0.025,
|
|
2293
2472
|
tint: { r: 0.62, g: 0.39, b: 0.23 },
|
|
2294
2473
|
massScale: 1.42,
|
|
2295
|
-
cruiseSpeed:
|
|
2296
|
-
throttleResponse: 0.
|
|
2297
|
-
rudderResponse: 0.
|
|
2474
|
+
cruiseSpeed: 1.22,
|
|
2475
|
+
throttleResponse: 0.36,
|
|
2476
|
+
rudderResponse: 0.4,
|
|
2298
2477
|
wanderPhase: 0.35,
|
|
2299
2478
|
lanterns: CUTTER_LANTERNS,
|
|
2300
2479
|
lanternStrength: 1.06,
|
|
@@ -2303,15 +2482,15 @@ function createSceneState(options, featureAdapters) {
|
|
|
2303
2482
|
{
|
|
2304
2483
|
id: "tidecaller",
|
|
2305
2484
|
modelKey: "cutter",
|
|
2306
|
-
position: vec3(
|
|
2307
|
-
velocity: vec3(-
|
|
2308
|
-
rotationY: -
|
|
2309
|
-
angularVelocity: -0.
|
|
2485
|
+
position: vec3(6.8, 0, 5.4),
|
|
2486
|
+
velocity: vec3(-0.82, 0, 0.14),
|
|
2487
|
+
rotationY: -1.34,
|
|
2488
|
+
angularVelocity: -0.035,
|
|
2310
2489
|
tint: { r: 0.58, g: 0.24, b: 0.16 },
|
|
2311
2490
|
massScale: 0.84,
|
|
2312
|
-
cruiseSpeed:
|
|
2313
|
-
throttleResponse: 0.
|
|
2314
|
-
rudderResponse: 0.
|
|
2491
|
+
cruiseSpeed: 1.36,
|
|
2492
|
+
throttleResponse: 0.52,
|
|
2493
|
+
rudderResponse: 0.58,
|
|
2315
2494
|
wanderPhase: 1.6,
|
|
2316
2495
|
lanterns: SHIP_LANTERNS,
|
|
2317
2496
|
lanternStrength: 1.18,
|
|
@@ -2512,7 +2691,7 @@ function renderProjectedShadow(ctx, worldPoints, camera, viewport, lightDir, opt
|
|
|
2512
2691
|
ctx.restore();
|
|
2513
2692
|
}
|
|
2514
2693
|
function pushHarborGeometry(camera, viewport, triangles, state) {
|
|
2515
|
-
if (!state
|
|
2694
|
+
if (!hasModeledHarborEnvironment(state)) {
|
|
2516
2695
|
for (const object of LEGACY_HARBOR_LAYOUT) {
|
|
2517
2696
|
buildTrianglesFromMesh(
|
|
2518
2697
|
{ positions: [object], indices: [0], normals: null, colors: null, material: createLegacyMeshPrimitive({})?.material, bounds: null, name: "legacy-structure" },
|
|
@@ -2610,9 +2789,10 @@ function renderShipRigging(ctx, ship, camera, viewport) {
|
|
|
2610
2789
|
}
|
|
2611
2790
|
function renderClothAccent(ctx, cloth, camera, viewport) {
|
|
2612
2791
|
const projected = cloth.positions.map((point) => projectPoint(point, camera, viewport));
|
|
2613
|
-
|
|
2614
|
-
ctx.
|
|
2615
|
-
|
|
2792
|
+
const material = cloth.material ?? {};
|
|
2793
|
+
ctx.strokeStyle = `rgba(255, 241, 226, ${material.foldAlpha ?? 0.32})`;
|
|
2794
|
+
ctx.lineWidth = 1.8;
|
|
2795
|
+
for (let row = 0; row < cloth.grid.rows; row += Math.max(1, Math.floor(cloth.grid.rows / 6))) {
|
|
2616
2796
|
ctx.beginPath();
|
|
2617
2797
|
let started = false;
|
|
2618
2798
|
for (let column = 0; column < cloth.grid.cols; column += 1) {
|
|
@@ -2631,6 +2811,27 @@ function renderClothAccent(ctx, cloth, camera, viewport) {
|
|
|
2631
2811
|
ctx.stroke();
|
|
2632
2812
|
}
|
|
2633
2813
|
}
|
|
2814
|
+
ctx.strokeStyle = `rgba(255, 228, 204, ${material.weaveAlpha ?? 0.22})`;
|
|
2815
|
+
ctx.lineWidth = 0.85;
|
|
2816
|
+
for (let column = 1; column < cloth.grid.cols - 1; column += Math.max(1, Math.floor(cloth.grid.cols / 8))) {
|
|
2817
|
+
ctx.beginPath();
|
|
2818
|
+
let started = false;
|
|
2819
|
+
for (let row = 0; row < cloth.grid.rows; row += 1) {
|
|
2820
|
+
const point = projected[row * cloth.grid.cols + column];
|
|
2821
|
+
if (!point) {
|
|
2822
|
+
continue;
|
|
2823
|
+
}
|
|
2824
|
+
if (!started) {
|
|
2825
|
+
ctx.moveTo(point.x, point.y);
|
|
2826
|
+
started = true;
|
|
2827
|
+
} else {
|
|
2828
|
+
ctx.lineTo(point.x, point.y);
|
|
2829
|
+
}
|
|
2830
|
+
}
|
|
2831
|
+
if (started) {
|
|
2832
|
+
ctx.stroke();
|
|
2833
|
+
}
|
|
2834
|
+
}
|
|
2634
2835
|
const borderIndices = [
|
|
2635
2836
|
0,
|
|
2636
2837
|
cloth.grid.cols - 1,
|
|
@@ -2638,6 +2839,25 @@ function renderClothAccent(ctx, cloth, camera, viewport) {
|
|
|
2638
2839
|
(cloth.grid.rows - 1) * cloth.grid.cols
|
|
2639
2840
|
];
|
|
2640
2841
|
ctx.fillStyle = colorToRgba(cloth.color, 0.95);
|
|
2842
|
+
ctx.strokeStyle = `rgba(255, 246, 236, ${material.edgeHighlightAlpha ?? 0.5})`;
|
|
2843
|
+
ctx.lineWidth = 1.4;
|
|
2844
|
+
ctx.beginPath();
|
|
2845
|
+
let borderStarted = false;
|
|
2846
|
+
for (let column = 0; column < cloth.grid.cols; column += 1) {
|
|
2847
|
+
const point = projected[column];
|
|
2848
|
+
if (!point) {
|
|
2849
|
+
continue;
|
|
2850
|
+
}
|
|
2851
|
+
if (!borderStarted) {
|
|
2852
|
+
ctx.moveTo(point.x, point.y);
|
|
2853
|
+
borderStarted = true;
|
|
2854
|
+
} else {
|
|
2855
|
+
ctx.lineTo(point.x, point.y);
|
|
2856
|
+
}
|
|
2857
|
+
}
|
|
2858
|
+
if (borderStarted) {
|
|
2859
|
+
ctx.stroke();
|
|
2860
|
+
}
|
|
2641
2861
|
for (const index of borderIndices) {
|
|
2642
2862
|
const point = projected[index];
|
|
2643
2863
|
if (!point) {
|
|
@@ -2653,14 +2873,22 @@ function renderWaterHighlights(ctx, waterBands, camera, viewport) {
|
|
|
2653
2873
|
if (band.band === "horizon") {
|
|
2654
2874
|
continue;
|
|
2655
2875
|
}
|
|
2656
|
-
const interval = band.band === "near" ?
|
|
2657
|
-
const alpha = band.band === "near" ? 0.22 : 0.14;
|
|
2876
|
+
const interval = band.band === "near" ? 4 : 5;
|
|
2877
|
+
const alpha = band.material?.highlightAlpha ?? (band.band === "near" ? 0.22 : 0.14);
|
|
2658
2878
|
ctx.strokeStyle = `rgba(232, 247, 255, ${alpha})`;
|
|
2659
|
-
ctx.lineWidth = band.band === "near" ?
|
|
2879
|
+
ctx.lineWidth = band.band === "near" ? 0.9 : 0.65;
|
|
2660
2880
|
for (let row = interval; row < band.rows - 1; row += interval) {
|
|
2661
|
-
ctx.beginPath();
|
|
2662
2881
|
let started = false;
|
|
2663
|
-
|
|
2882
|
+
ctx.beginPath();
|
|
2883
|
+
for (let column = 0; column < band.cols; column += band.band === "near" ? 2 : 3) {
|
|
2884
|
+
if (pseudoRandom(row * 47 + column * 13) < 0.18) {
|
|
2885
|
+
if (started) {
|
|
2886
|
+
ctx.stroke();
|
|
2887
|
+
ctx.beginPath();
|
|
2888
|
+
started = false;
|
|
2889
|
+
}
|
|
2890
|
+
continue;
|
|
2891
|
+
}
|
|
2664
2892
|
const point = projectPoint(
|
|
2665
2893
|
band.positions[row * band.cols + column],
|
|
2666
2894
|
camera,
|
|
@@ -2680,7 +2908,55 @@ function renderWaterHighlights(ctx, waterBands, camera, viewport) {
|
|
|
2680
2908
|
ctx.stroke();
|
|
2681
2909
|
}
|
|
2682
2910
|
}
|
|
2911
|
+
if (band.band === "near") {
|
|
2912
|
+
ctx.fillStyle = `rgba(236, 249, 255, ${(band.material?.foamAlpha ?? 0.28) * 0.72})`;
|
|
2913
|
+
for (let column = 3; column < band.cols - 3; column += 10) {
|
|
2914
|
+
const point = projectPoint(
|
|
2915
|
+
band.positions[Math.floor(band.rows * 0.42) * band.cols + column],
|
|
2916
|
+
camera,
|
|
2917
|
+
viewport
|
|
2918
|
+
);
|
|
2919
|
+
if (!point) {
|
|
2920
|
+
continue;
|
|
2921
|
+
}
|
|
2922
|
+
ctx.beginPath();
|
|
2923
|
+
ctx.ellipse(point.x, point.y, 1.8, 0.75, -0.2, 0, Math.PI * 2);
|
|
2924
|
+
ctx.fill();
|
|
2925
|
+
}
|
|
2926
|
+
}
|
|
2927
|
+
}
|
|
2928
|
+
}
|
|
2929
|
+
function renderShorelineFoamSegments(ctx, segments, camera, viewport) {
|
|
2930
|
+
ctx.save();
|
|
2931
|
+
ctx.globalCompositeOperation = "screen";
|
|
2932
|
+
ctx.lineCap = "round";
|
|
2933
|
+
ctx.lineJoin = "round";
|
|
2934
|
+
for (const segment of segments) {
|
|
2935
|
+
const half = scaleVec3(segment.direction, segment.length * 0.5);
|
|
2936
|
+
const start = projectPoint(subVec3(segment.center, half), camera, viewport);
|
|
2937
|
+
const end = projectPoint(addVec3(segment.center, half), camera, viewport);
|
|
2938
|
+
const center = projectPoint(segment.center, camera, viewport);
|
|
2939
|
+
if (!start || !end || !center) {
|
|
2940
|
+
continue;
|
|
2941
|
+
}
|
|
2942
|
+
const depthScale = clamp(140 / Math.max(12, center.depth), 3, 10);
|
|
2943
|
+
ctx.strokeStyle = `rgba(232, 242, 238, ${segment.opacity})`;
|
|
2944
|
+
ctx.lineWidth = clamp(segment.width * depthScale, 0.8, 2.8);
|
|
2945
|
+
ctx.beginPath();
|
|
2946
|
+
ctx.moveTo(start.x, start.y);
|
|
2947
|
+
ctx.quadraticCurveTo(
|
|
2948
|
+
center.x,
|
|
2949
|
+
center.y + Math.sin(segment.center.x * 1.7) * 2.4,
|
|
2950
|
+
end.x,
|
|
2951
|
+
end.y
|
|
2952
|
+
);
|
|
2953
|
+
ctx.stroke();
|
|
2954
|
+
ctx.fillStyle = `rgba(248, 251, 246, ${segment.opacity * 0.68})`;
|
|
2955
|
+
ctx.beginPath();
|
|
2956
|
+
ctx.ellipse(center.x, center.y, depthScale * 0.18, depthScale * 0.08, -0.2, 0, Math.PI * 2);
|
|
2957
|
+
ctx.fill();
|
|
2683
2958
|
}
|
|
2959
|
+
ctx.restore();
|
|
2684
2960
|
}
|
|
2685
2961
|
function readPhysicsNumber(physics, key, fallback) {
|
|
2686
2962
|
const value = physics?.[key];
|
|
@@ -2712,14 +2988,17 @@ function getShipInverseInertia(ship, shipModel) {
|
|
|
2712
2988
|
return 1 / Math.max(1, inertia);
|
|
2713
2989
|
}
|
|
2714
2990
|
function spawnSpray(state, point, intensity) {
|
|
2715
|
-
const count =
|
|
2991
|
+
const count = Math.max(
|
|
2992
|
+
3,
|
|
2993
|
+
Math.ceil(state.fluidDetail.getSnapshot().currentLevel.config.splashCount * 0.32)
|
|
2994
|
+
);
|
|
2716
2995
|
for (let index = 0; index < count; index += 1) {
|
|
2717
2996
|
const angle = index / count * Math.PI * 2;
|
|
2718
|
-
const speed = 0.
|
|
2997
|
+
const speed = 0.46 + Math.random() * intensity * 0.24;
|
|
2719
2998
|
state.sprays.push({
|
|
2720
2999
|
position: vec3(point.x, point.y, point.z),
|
|
2721
|
-
velocity: vec3(Math.cos(angle) * speed * 0.
|
|
2722
|
-
life:
|
|
3000
|
+
velocity: vec3(Math.cos(angle) * speed * 0.24, 0.46 + Math.random() * 0.34, Math.sin(angle) * speed * 0.18),
|
|
3001
|
+
life: 0.72 + Math.random() * 0.22
|
|
2723
3002
|
});
|
|
2724
3003
|
}
|
|
2725
3004
|
}
|
|
@@ -2734,7 +3013,7 @@ function resolveShipRoute(ship, state, radius) {
|
|
|
2734
3013
|
}
|
|
2735
3014
|
const wander = Math.sin(state.time * 0.22 + readVisualNumber(ship.wanderPhase, 0));
|
|
2736
3015
|
const crossCurrent = Math.cos(state.time * 0.31 + readVisualNumber(ship.wanderPhase, 0));
|
|
2737
|
-
const laneCenter = ship.id === "northwind" ?
|
|
3016
|
+
const laneCenter = ship.id === "northwind" ? 11.6 + wander * 0.82 + crossCurrent * 0.24 : 5.4 + wander * 0.94 - crossCurrent * 0.32;
|
|
2738
3017
|
const targetX = ship.routeDirection > 0 ? HARBOR_BOUNDS.maxX - radius * 1.7 : HARBOR_BOUNDS.minX + radius * 1.7;
|
|
2739
3018
|
return vec3(targetX, 0, clamp(laneCenter, HARBOR_BOUNDS.minZ + 1.8, HARBOR_BOUNDS.maxZ - 1.8));
|
|
2740
3019
|
}
|
|
@@ -2747,7 +3026,7 @@ function updateShipMotion(state, ship, dt, shipModel) {
|
|
|
2747
3026
|
const angularDamping = readPhysicsNumber(physics, "angularDamping", 0.08);
|
|
2748
3027
|
const throttleResponse = readVisualNumber(ship.throttleResponse, 0.58);
|
|
2749
3028
|
const rudderResponse = readVisualNumber(ship.rudderResponse, 0.62);
|
|
2750
|
-
const cruiseSpeed = readVisualNumber(ship.cruiseSpeed,
|
|
3029
|
+
const cruiseSpeed = readVisualNumber(ship.cruiseSpeed, 1.25);
|
|
2751
3030
|
ship.collisionCooldown = Math.max(0, readVisualNumber(ship.collisionCooldown, 0) - dt);
|
|
2752
3031
|
const forward = directionFromYaw(ship.rotationY);
|
|
2753
3032
|
const lateral = perpendicularOnWater(forward);
|
|
@@ -2842,7 +3121,7 @@ function resolveShipCollision(state, a, b, shipModelA, shipModelB) {
|
|
|
2842
3121
|
b.position = addVec3(b.position, scaleVec3(correction, invMassB));
|
|
2843
3122
|
const relativeVelocity = subVec3(b.velocity, a.velocity);
|
|
2844
3123
|
const velocityAlongNormal = dotVec3(relativeVelocity, normal);
|
|
2845
|
-
const restitution = (readPhysicsNumber(shipModelA.physics, "restitution", 0.22) + readPhysicsNumber(shipModelB.physics, "restitution", 0.22)) / 2 * 0.
|
|
3124
|
+
const restitution = (readPhysicsNumber(shipModelA.physics, "restitution", 0.22) + readPhysicsNumber(shipModelB.physics, "restitution", 0.22)) / 2 * 0.42;
|
|
2846
3125
|
if (velocityAlongNormal < 0) {
|
|
2847
3126
|
const impulseMagnitude = -(1 + restitution) * velocityAlongNormal / Math.max(1e-4, invMassSum);
|
|
2848
3127
|
const impulse = scaleVec3(normal, impulseMagnitude);
|
|
@@ -2860,27 +3139,27 @@ function resolveShipCollision(state, a, b, shipModelA, shipModelB) {
|
|
|
2860
3139
|
a.angularVelocity -= tangentSpeed * radiusA * getShipInverseInertia(a, shipModelA) * 0.2 + impulseMagnitude * 24e-5;
|
|
2861
3140
|
b.angularVelocity += tangentSpeed * radiusB * getShipInverseInertia(b, shipModelB) * 0.2 + impulseMagnitude * 24e-5;
|
|
2862
3141
|
const impactSpeed = Math.abs(velocityAlongNormal);
|
|
2863
|
-
if (impactSpeed > 0.
|
|
3142
|
+
if (impactSpeed > 0.36 && Math.max(readVisualNumber(a.collisionCooldown, 0), readVisualNumber(b.collisionCooldown, 0)) <= 0) {
|
|
2864
3143
|
const contactPoint = vec3(
|
|
2865
3144
|
(a.position.x + b.position.x) * 0.5,
|
|
2866
3145
|
(a.position.y + b.position.y) * 0.5 + 0.14,
|
|
2867
3146
|
(a.position.z + b.position.z) * 0.5
|
|
2868
3147
|
);
|
|
2869
|
-
spawnSpray(state, contactPoint, impactSpeed *
|
|
3148
|
+
spawnSpray(state, contactPoint, impactSpeed * 0.9 + penetration * 2.4);
|
|
2870
3149
|
state.waveImpulses.push({
|
|
2871
3150
|
x: contactPoint.x,
|
|
2872
3151
|
z: contactPoint.z,
|
|
2873
|
-
strength: clamp(0.
|
|
2874
|
-
radius: 0.
|
|
3152
|
+
strength: clamp(0.1 + impactSpeed * 0.18 + penetration * 0.28, 0.08, 0.52),
|
|
3153
|
+
radius: 0.72 + penetration * 0.72,
|
|
2875
3154
|
life: 1
|
|
2876
3155
|
});
|
|
2877
3156
|
state.collisionCount += 1;
|
|
2878
3157
|
state.collisionFlash = Math.max(
|
|
2879
3158
|
state.collisionFlash,
|
|
2880
|
-
clamp(impactSpeed * 0.
|
|
3159
|
+
clamp(impactSpeed * 0.14 + penetration * 0.32, 0.04, 0.24)
|
|
2881
3160
|
);
|
|
2882
|
-
a.collisionCooldown = 0.
|
|
2883
|
-
b.collisionCooldown = 0.
|
|
3161
|
+
a.collisionCooldown = 0.72;
|
|
3162
|
+
b.collisionCooldown = 0.72;
|
|
2884
3163
|
}
|
|
2885
3164
|
}
|
|
2886
3165
|
state.contactCount += 1;
|
|
@@ -2903,7 +3182,7 @@ function updateShips(state, dt, shipModel) {
|
|
|
2903
3182
|
collided = resolveShipCollision(state, shipA, shipB, shipModelA, shipModelB) || collided;
|
|
2904
3183
|
}
|
|
2905
3184
|
}
|
|
2906
|
-
state.collisionFlash = collided ? Math.max(0.
|
|
3185
|
+
state.collisionFlash = collided ? Math.max(0.04, state.collisionFlash) : Math.max(0, state.collisionFlash - dt * 1.7);
|
|
2907
3186
|
}
|
|
2908
3187
|
function updateWaveImpulses(state, dt) {
|
|
2909
3188
|
state.waveImpulses = state.waveImpulses.map((impulse) => ({
|
|
@@ -3168,7 +3447,7 @@ function renderLighthouseBeam(ctx, state, camera, viewport, visuals) {
|
|
|
3168
3447
|
const lighthousePlacement = SHOWCASE_ENVIRONMENT_LAYOUT.find(
|
|
3169
3448
|
(placement) => placement.assetKey === "lighthouse"
|
|
3170
3449
|
);
|
|
3171
|
-
if (!lighthousePlacement || !state.showcaseRealisticModelsEnabled) {
|
|
3450
|
+
if (!lighthousePlacement || !state.showcaseRealisticModelsEnabled || !hasModeledHarborEnvironment(state)) {
|
|
3172
3451
|
return;
|
|
3173
3452
|
}
|
|
3174
3453
|
const source = transformPoint(
|
|
@@ -3265,7 +3544,8 @@ function renderWaterMotionEffects(ctx, effects, camera, viewport) {
|
|
|
3265
3544
|
for (const wake of effects.wakeTrails) {
|
|
3266
3545
|
const projected = wake.points.map((point) => ({
|
|
3267
3546
|
projected: projectPoint(point.center, camera, viewport),
|
|
3268
|
-
width: point.width
|
|
3547
|
+
width: point.width,
|
|
3548
|
+
foam: point.foam
|
|
3269
3549
|
})).filter((entry) => entry.projected);
|
|
3270
3550
|
if (projected.length < 2) {
|
|
3271
3551
|
continue;
|
|
@@ -3273,8 +3553,8 @@ function renderWaterMotionEffects(ctx, effects, camera, viewport) {
|
|
|
3273
3553
|
const averageDepth = projected.reduce((total, entry) => total + entry.projected.depth, 0) / projected.length;
|
|
3274
3554
|
const averageWidth = projected.reduce((total, entry) => total + entry.width, 0) / projected.length;
|
|
3275
3555
|
const baseWidth = clamp(averageWidth / Math.max(0.25, averageDepth) * 180, 1.6, 5.4);
|
|
3276
|
-
ctx.strokeStyle = `rgba(146, 194, 236, ${wake.opacity * 0.
|
|
3277
|
-
ctx.lineWidth = baseWidth * 1.
|
|
3556
|
+
ctx.strokeStyle = `rgba(146, 194, 236, ${wake.opacity * 0.34})`;
|
|
3557
|
+
ctx.lineWidth = baseWidth * 1.45;
|
|
3278
3558
|
ctx.lineCap = "round";
|
|
3279
3559
|
ctx.lineJoin = "round";
|
|
3280
3560
|
ctx.beginPath();
|
|
@@ -3283,8 +3563,8 @@ function renderWaterMotionEffects(ctx, effects, camera, viewport) {
|
|
|
3283
3563
|
ctx.lineTo(projected[index].projected.x, projected[index].projected.y);
|
|
3284
3564
|
}
|
|
3285
3565
|
ctx.stroke();
|
|
3286
|
-
ctx.strokeStyle = `rgba(234, 247, 255, ${wake.opacity})`;
|
|
3287
|
-
ctx.lineWidth = baseWidth;
|
|
3566
|
+
ctx.strokeStyle = `rgba(234, 247, 255, ${wake.opacity * 0.72})`;
|
|
3567
|
+
ctx.lineWidth = baseWidth * 0.72;
|
|
3288
3568
|
ctx.lineCap = "round";
|
|
3289
3569
|
ctx.lineJoin = "round";
|
|
3290
3570
|
ctx.beginPath();
|
|
@@ -3294,13 +3574,14 @@ function renderWaterMotionEffects(ctx, effects, camera, viewport) {
|
|
|
3294
3574
|
}
|
|
3295
3575
|
ctx.stroke();
|
|
3296
3576
|
for (const entry of projected.slice(1, 5)) {
|
|
3297
|
-
|
|
3577
|
+
const foam = entry.foam ?? 0.3;
|
|
3578
|
+
ctx.fillStyle = `rgba(239, 248, 255, ${wake.opacity * foam * 0.92})`;
|
|
3298
3579
|
ctx.beginPath();
|
|
3299
3580
|
ctx.ellipse(
|
|
3300
3581
|
entry.projected.x,
|
|
3301
3582
|
entry.projected.y,
|
|
3302
|
-
baseWidth * 0.
|
|
3303
|
-
baseWidth * 0.
|
|
3583
|
+
baseWidth * 0.54,
|
|
3584
|
+
baseWidth * 0.28,
|
|
3304
3585
|
0,
|
|
3305
3586
|
0,
|
|
3306
3587
|
Math.PI * 2
|
|
@@ -3318,13 +3599,22 @@ function renderWaterMotionEffects(ctx, effects, camera, viewport) {
|
|
|
3318
3599
|
const radiusX = Math.hypot(xAxis.x - center.x, xAxis.y - center.y);
|
|
3319
3600
|
const radiusY = Math.hypot(zAxis.x - center.x, zAxis.y - center.y);
|
|
3320
3601
|
ctx.strokeStyle = `rgba(216, 235, 255, ${ring.opacity})`;
|
|
3321
|
-
ctx.lineWidth = clamp((radiusX + radiusY) * 0.
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3602
|
+
ctx.lineWidth = clamp((radiusX + radiusY) * 0.014, 0.65, 1.8);
|
|
3603
|
+
for (let segment = 0; segment < 5; segment += 1) {
|
|
3604
|
+
if (pseudoRandom(segment * 31 + radiusX * 0.7 + radiusY * 0.3) < 0.32) {
|
|
3605
|
+
continue;
|
|
3606
|
+
}
|
|
3607
|
+
const startAngle = segment * 1.22 + stateTimePhase(center.x, center.y) * 0.04;
|
|
3608
|
+
ctx.beginPath();
|
|
3609
|
+
ctx.ellipse(center.x, center.y, radiusX, radiusY, 0, startAngle, startAngle + 0.48);
|
|
3610
|
+
ctx.stroke();
|
|
3611
|
+
}
|
|
3325
3612
|
}
|
|
3326
3613
|
ctx.restore();
|
|
3327
3614
|
}
|
|
3615
|
+
function stateTimePhase(x, y) {
|
|
3616
|
+
return Math.sin(x * 0.013 + y * 0.017);
|
|
3617
|
+
}
|
|
3328
3618
|
function renderScene(ctx, canvas, state, shipModel, dom, lightingFeatures, fluidFeatures, clothFeatures) {
|
|
3329
3619
|
const viewport = { width: canvas.width, height: canvas.height };
|
|
3330
3620
|
const camera = buildCamera(state, canvas);
|
|
@@ -3392,6 +3682,7 @@ function renderScene(ctx, canvas, state, shipModel, dom, lightingFeatures, fluid
|
|
|
3392
3682
|
}
|
|
3393
3683
|
}
|
|
3394
3684
|
const waterMotionEffects = buildWaterMotionEffects(state);
|
|
3685
|
+
const shorelineFoamSegments = buildShorelineFoamSegments(state);
|
|
3395
3686
|
const lightSources = collectSceneLightSources(state, visuals);
|
|
3396
3687
|
pushHarborGeometry(camera, viewport, sceneTriangles, state);
|
|
3397
3688
|
const cloth = buildClothSurface(
|
|
@@ -3463,6 +3754,7 @@ function renderScene(ctx, canvas, state, shipModel, dom, lightingFeatures, fluid
|
|
|
3463
3754
|
}
|
|
3464
3755
|
renderWaterMotionEffects(ctx, waterMotionEffects, camera, viewport);
|
|
3465
3756
|
renderWaterHighlights(ctx, water.bandMeshes, camera, viewport);
|
|
3757
|
+
renderShorelineFoamSegments(ctx, shorelineFoamSegments, camera, viewport);
|
|
3466
3758
|
drawTriangles(
|
|
3467
3759
|
ctx,
|
|
3468
3760
|
sceneTriangles,
|
|
@@ -3491,7 +3783,7 @@ function renderScene(ctx, canvas, state, shipModel, dom, lightingFeatures, fluid
|
|
|
3491
3783
|
};
|
|
3492
3784
|
const sceneMetrics = [
|
|
3493
3785
|
`focus: ${state.focus}`,
|
|
3494
|
-
`ships: ${state.ships.length} active GLTF hulls across ${new Set(state.ships.map((ship) => ship.modelKey)).size} model families`,
|
|
3786
|
+
`ships: ${state.ships.length} active GLTF hulls across ${new Set(state.ships.map((ship) => resolveShipModel(state, ship, shipModel)?.name ?? ship.modelKey)).size} model families`,
|
|
3495
3787
|
`moonlight: cold overhead key + ${HARBOR_TORCHES.length + state.ships.reduce((total, ship) => total + (Array.isArray(ship.lanterns) ? ship.lanterns.length : 0), 0)} warm deck and harbor lights`,
|
|
3496
3788
|
`physics snapshot: ${state.physics.snapshot.stage} (${state.physics.snapshot.stability})`,
|
|
3497
3789
|
`physics contacts: ${state.contactCount}`,
|
|
@@ -3559,7 +3851,7 @@ function updateSceneState(state, dt, shipModel, featureAdapters) {
|
|
|
3559
3851
|
advanceShowcaseClothSimulationState(clothState, {
|
|
3560
3852
|
dt,
|
|
3561
3853
|
time: state.time,
|
|
3562
|
-
flagMotion: readVisualNumber(state.demoVisuals?.flagMotion, 0.
|
|
3854
|
+
flagMotion: readVisualNumber(state.demoVisuals?.flagMotion, 0.58),
|
|
3563
3855
|
waveInfluence: sampleWave(state, FLAG_LAYOUT.origin.x + FLAG_LAYOUT.width * 0.55, FLAG_LAYOUT.origin.z + FLAG_LAYOUT.width * 0.48, state.time)
|
|
3564
3856
|
});
|
|
3565
3857
|
updatePhysicsSnapshot(state, shipModel, featureAdapters.physics);
|
|
@@ -3569,17 +3861,28 @@ function syncTextState(state, shipModel, featureAdapters) {
|
|
|
3569
3861
|
coordinateSystem: "right-handed world; +x right, +y up, +z forward from the shore",
|
|
3570
3862
|
focus: state.focus,
|
|
3571
3863
|
stress: state.stress,
|
|
3572
|
-
ships: state.ships.map((ship) =>
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
3864
|
+
ships: state.ships.map((ship) => {
|
|
3865
|
+
const resolvedShipModel = resolveShipModel(state, ship, shipModel);
|
|
3866
|
+
return {
|
|
3867
|
+
id: ship.id,
|
|
3868
|
+
modelKey: ship.modelKey ?? "brigantine",
|
|
3869
|
+
resolvedModelKey: resolvedShipModel?.name ?? ship.modelKey ?? "brigantine",
|
|
3870
|
+
x: Number(ship.position.x.toFixed(2)),
|
|
3871
|
+
y: Number(ship.position.y.toFixed(2)),
|
|
3872
|
+
z: Number(ship.position.z.toFixed(2)),
|
|
3873
|
+
vx: Number(ship.velocity.x.toFixed(2)),
|
|
3874
|
+
vz: Number(ship.velocity.z.toFixed(2)),
|
|
3875
|
+
massKg: Math.round(getShipMass(ship, resolvedShipModel)),
|
|
3876
|
+
lanterns: Array.isArray(ship.lanterns) ? ship.lanterns.length : 0
|
|
3877
|
+
};
|
|
3878
|
+
}),
|
|
3879
|
+
assetCatalog: {
|
|
3880
|
+
mode: state.assetCatalog?.mode ?? "unknown",
|
|
3881
|
+
shipKeys: Object.keys(state.assetCatalog?.ships ?? {}).sort(),
|
|
3882
|
+
environmentKeys: Object.keys(state.assetCatalog?.environment ?? {}).sort(),
|
|
3883
|
+
fallbackReason: state.assetCatalog?.fallbackReason ?? null,
|
|
3884
|
+
requestedRealisticModels: state.showcaseRealisticModelsEnabled
|
|
3885
|
+
},
|
|
3583
3886
|
shipPhysics: Object.fromEntries(
|
|
3584
3887
|
state.ships.map((ship) => [ship.id, resolveShipModel(state, ship, shipModel)?.physics ?? null])
|
|
3585
3888
|
),
|
|
@@ -3636,7 +3939,9 @@ async function mountGpuShowcase(options = {}, featureFlags = null) {
|
|
|
3636
3939
|
},
|
|
3637
3940
|
featureAdapters
|
|
3638
3941
|
);
|
|
3639
|
-
const assetCatalog = await (
|
|
3942
|
+
const assetCatalog = await loadShowcaseAssetCatalogWithFallback({
|
|
3943
|
+
includeSecondaryShip: state.showcaseRealisticModelsEnabled
|
|
3944
|
+
});
|
|
3640
3945
|
const shipModel = assetCatalog.ships[assetCatalog.primaryShipKey];
|
|
3641
3946
|
state.assetCatalog = assetCatalog;
|
|
3642
3947
|
state.shipModel = shipModel;
|
|
@@ -3776,6 +4081,8 @@ function updatePhysicsSnapshot(state, shipModel, physicsFeatures) {
|
|
|
3776
4081
|
}
|
|
3777
4082
|
export {
|
|
3778
4083
|
advanceShowcaseClothSimulationState as __testOnlyAdvanceShowcaseClothSimulationState,
|
|
4084
|
+
buildClothSurface as __testOnlyBuildClothSurface,
|
|
4085
|
+
buildShorelineFoamSegments as __testOnlyBuildShorelineFoamSegments,
|
|
3779
4086
|
buildWaterBands as __testOnlyBuildWaterBands,
|
|
3780
4087
|
buildWaterMotionEffects as __testOnlyBuildWaterMotionEffects,
|
|
3781
4088
|
collectSceneLightSources as __testOnlyCollectSceneLightSources,
|
|
@@ -3783,4 +4090,4 @@ export {
|
|
|
3783
4090
|
mountGpuShowcase,
|
|
3784
4091
|
showcaseFocusModes
|
|
3785
4092
|
};
|
|
3786
|
-
//# sourceMappingURL=showcase-runtime-
|
|
4093
|
+
//# sourceMappingURL=showcase-runtime-B544T6AM.js.map
|