@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
package/src/showcase-runtime.js
CHANGED
|
@@ -844,6 +844,13 @@ const LEGACY_HARBOR_LAYOUT = Object.freeze([
|
|
|
844
844
|
]);
|
|
845
845
|
|
|
846
846
|
const SHOWCASE_ENVIRONMENT_LAYOUT = Object.freeze([
|
|
847
|
+
Object.freeze({
|
|
848
|
+
assetKey: "shoreline",
|
|
849
|
+
position: Object.freeze({ x: 1.8, y: -0.04, z: 0.48 }),
|
|
850
|
+
rotationY: -0.03,
|
|
851
|
+
scale: 1.02,
|
|
852
|
+
accent: 0.03,
|
|
853
|
+
}),
|
|
847
854
|
Object.freeze({
|
|
848
855
|
assetKey: "harbor-dock",
|
|
849
856
|
position: Object.freeze({ x: -4.6, y: 0.16, z: 0.7 }),
|
|
@@ -876,6 +883,18 @@ const HARBOR_TORCHES = Object.freeze([
|
|
|
876
883
|
Object.freeze({ x: -8.6, y: 2.48, z: -0.72, glow: 1 }),
|
|
877
884
|
Object.freeze({ x: -10.4, y: 1.28, z: 0.82, glow: 0.92 }),
|
|
878
885
|
]);
|
|
886
|
+
const SHORELINE_FOAM_ANCHORS = Object.freeze([
|
|
887
|
+
Object.freeze({ x: -7.8, z: 3.0, length: 1.25, angle: -0.12 }),
|
|
888
|
+
Object.freeze({ x: -6.3, z: 2.72, length: 0.92, angle: 0.08 }),
|
|
889
|
+
Object.freeze({ x: -4.9, z: 3.16, length: 1.08, angle: -0.2 }),
|
|
890
|
+
Object.freeze({ x: -3.2, z: 2.42, length: 0.76, angle: 0.16 }),
|
|
891
|
+
Object.freeze({ x: -1.4, z: 2.82, length: 1.18, angle: -0.04 }),
|
|
892
|
+
Object.freeze({ x: 0.4, z: 3.08, length: 0.88, angle: 0.14 }),
|
|
893
|
+
Object.freeze({ x: 2.1, z: 2.56, length: 1.34, angle: -0.18 }),
|
|
894
|
+
Object.freeze({ x: 3.8, z: 3.0, length: 0.94, angle: 0.1 }),
|
|
895
|
+
Object.freeze({ x: 5.5, z: 2.72, length: 1.12, angle: -0.08 }),
|
|
896
|
+
Object.freeze({ x: 7.0, z: 3.22, length: 0.72, angle: 0.18 }),
|
|
897
|
+
]);
|
|
879
898
|
const FLAG_LAYOUT = Object.freeze({
|
|
880
899
|
origin: Object.freeze({ x: -3.5, y: 5.9, z: 2.4 }),
|
|
881
900
|
width: 4.8,
|
|
@@ -918,31 +937,31 @@ function injectStyles() {
|
|
|
918
937
|
box-sizing: border-box;
|
|
919
938
|
}
|
|
920
939
|
.plasius-demo {
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
gap: 20px;
|
|
926
|
-
}
|
|
927
|
-
.plasius-demo__hero,
|
|
928
|
-
.plasius-demo__layout {
|
|
929
|
-
display: grid;
|
|
930
|
-
gap: 20px;
|
|
931
|
-
}
|
|
932
|
-
.plasius-demo__hero {
|
|
933
|
-
grid-template-columns: minmax(0, 1.5fr) minmax(320px, 0.85fr);
|
|
934
|
-
align-items: start;
|
|
940
|
+
position: relative;
|
|
941
|
+
width: 100%;
|
|
942
|
+
min-height: 100dvh;
|
|
943
|
+
overflow: hidden;
|
|
935
944
|
}
|
|
936
945
|
.plasius-panel {
|
|
937
946
|
border: 1px solid var(--plasius-border);
|
|
938
|
-
border-radius:
|
|
947
|
+
border-radius: 8px;
|
|
939
948
|
background: var(--plasius-panel);
|
|
940
949
|
box-shadow: var(--plasius-shadow);
|
|
941
950
|
backdrop-filter: blur(12px);
|
|
942
951
|
}
|
|
943
952
|
.plasius-demo__hero-card,
|
|
944
953
|
.plasius-demo__status {
|
|
945
|
-
|
|
954
|
+
position: absolute;
|
|
955
|
+
z-index: 3;
|
|
956
|
+
padding: 10px 12px;
|
|
957
|
+
}
|
|
958
|
+
.plasius-demo__hero-card {
|
|
959
|
+
display: none;
|
|
960
|
+
}
|
|
961
|
+
.plasius-demo__status {
|
|
962
|
+
left: 16px;
|
|
963
|
+
bottom: 84px;
|
|
964
|
+
max-width: min(360px, calc(100vw - 32px));
|
|
946
965
|
}
|
|
947
966
|
.plasius-demo__eyebrow {
|
|
948
967
|
margin: 0 0 8px;
|
|
@@ -965,31 +984,36 @@ function injectStyles() {
|
|
|
965
984
|
.plasius-demo__status-badge {
|
|
966
985
|
width: fit-content;
|
|
967
986
|
margin: 0;
|
|
968
|
-
padding:
|
|
969
|
-
border-radius:
|
|
987
|
+
padding: 6px 9px;
|
|
988
|
+
border-radius: 6px;
|
|
970
989
|
background: rgba(243, 177, 106, 0.14);
|
|
971
990
|
color: var(--plasius-accent);
|
|
972
991
|
font-weight: 700;
|
|
992
|
+
font-size: 12px;
|
|
973
993
|
}
|
|
974
994
|
.plasius-demo__status-text {
|
|
975
995
|
margin: 10px 0 0;
|
|
976
996
|
color: var(--plasius-muted);
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
.plasius-demo__layout {
|
|
980
|
-
grid-template-columns: minmax(0, 1.45fr) minmax(320px, 0.68fr);
|
|
981
|
-
align-items: start;
|
|
997
|
+
font-size: 12px;
|
|
998
|
+
line-height: 1.45;
|
|
982
999
|
}
|
|
983
1000
|
.plasius-demo__canvas-panel {
|
|
984
|
-
|
|
985
|
-
|
|
1001
|
+
position: absolute;
|
|
1002
|
+
inset: 0;
|
|
1003
|
+
padding: 0;
|
|
1004
|
+
border: 0;
|
|
1005
|
+
border-radius: 0;
|
|
1006
|
+
background: transparent;
|
|
1007
|
+
box-shadow: none;
|
|
1008
|
+
backdrop-filter: none;
|
|
986
1009
|
}
|
|
987
1010
|
.plasius-demo__canvas {
|
|
988
1011
|
width: 100%;
|
|
989
|
-
|
|
1012
|
+
height: 100%;
|
|
1013
|
+
min-height: 100dvh;
|
|
990
1014
|
display: block;
|
|
991
|
-
border
|
|
992
|
-
border:
|
|
1015
|
+
border: 0;
|
|
1016
|
+
border-radius: 0;
|
|
993
1017
|
background: linear-gradient(180deg, #071220 0%, #132440 42%, #10344b 42%, #05111d 100%);
|
|
994
1018
|
}
|
|
995
1019
|
.${CAPTURE_CLASS} .plasius-demo {
|
|
@@ -1002,6 +1026,8 @@ function injectStyles() {
|
|
|
1002
1026
|
.${CAPTURE_CLASS} .plasius-demo__toolbar,
|
|
1003
1027
|
.${CAPTURE_CLASS} .plasius-demo__legend,
|
|
1004
1028
|
.${CAPTURE_CLASS} .plasius-demo__sidebar,
|
|
1029
|
+
.${CAPTURE_CLASS} .plasius-demo__diagnostics,
|
|
1030
|
+
.${CAPTURE_CLASS} .plasius-demo__status,
|
|
1005
1031
|
.${CAPTURE_CLASS} .plasius-demo__footer {
|
|
1006
1032
|
display: none;
|
|
1007
1033
|
}
|
|
@@ -1028,12 +1054,14 @@ function injectStyles() {
|
|
|
1028
1054
|
}
|
|
1029
1055
|
.plasius-demo__toolbar {
|
|
1030
1056
|
position: absolute;
|
|
1031
|
-
top:
|
|
1032
|
-
left:
|
|
1057
|
+
top: 84px;
|
|
1058
|
+
left: 16px;
|
|
1059
|
+
z-index: 4;
|
|
1033
1060
|
display: flex;
|
|
1034
|
-
gap:
|
|
1061
|
+
gap: 8px;
|
|
1035
1062
|
flex-wrap: wrap;
|
|
1036
1063
|
align-items: center;
|
|
1064
|
+
max-width: min(560px, calc(100vw - 32px));
|
|
1037
1065
|
}
|
|
1038
1066
|
.plasius-demo button,
|
|
1039
1067
|
.plasius-demo label,
|
|
@@ -1045,22 +1073,63 @@ function injectStyles() {
|
|
|
1045
1073
|
.plasius-demo .plasius-toggle,
|
|
1046
1074
|
.plasius-demo select {
|
|
1047
1075
|
border: 1px solid rgba(159, 185, 223, 0.18);
|
|
1048
|
-
border-radius:
|
|
1076
|
+
border-radius: 6px;
|
|
1049
1077
|
background: rgba(9, 20, 34, 0.84);
|
|
1050
1078
|
color: var(--plasius-ink);
|
|
1051
|
-
padding: 10px
|
|
1079
|
+
padding: 8px 10px;
|
|
1052
1080
|
}
|
|
1053
1081
|
.plasius-toggle {
|
|
1054
1082
|
display: inline-flex;
|
|
1055
1083
|
align-items: center;
|
|
1056
1084
|
gap: 8px;
|
|
1057
1085
|
}
|
|
1086
|
+
.plasius-demo__diagnostics {
|
|
1087
|
+
position: absolute;
|
|
1088
|
+
right: 16px;
|
|
1089
|
+
bottom: 84px;
|
|
1090
|
+
z-index: 4;
|
|
1091
|
+
max-width: min(420px, calc(100vw - 32px));
|
|
1092
|
+
color: var(--plasius-ink);
|
|
1093
|
+
font-family: "JetBrains Mono", monospace;
|
|
1094
|
+
font-size: 12px;
|
|
1095
|
+
}
|
|
1096
|
+
.plasius-demo__diagnostics summary {
|
|
1097
|
+
width: fit-content;
|
|
1098
|
+
margin-left: auto;
|
|
1099
|
+
border: 1px solid rgba(159, 185, 223, 0.18);
|
|
1100
|
+
border-radius: 6px;
|
|
1101
|
+
padding: 8px 10px;
|
|
1102
|
+
background: rgba(9, 20, 34, 0.84);
|
|
1103
|
+
cursor: pointer;
|
|
1104
|
+
list-style: none;
|
|
1105
|
+
}
|
|
1106
|
+
.plasius-demo__diagnostics summary::-webkit-details-marker {
|
|
1107
|
+
display: none;
|
|
1108
|
+
}
|
|
1109
|
+
.plasius-demo__diagnostics[open] {
|
|
1110
|
+
width: min(420px, calc(100vw - 32px));
|
|
1111
|
+
}
|
|
1112
|
+
.plasius-demo__diagnostics[open] summary {
|
|
1113
|
+
margin-bottom: 8px;
|
|
1114
|
+
background: rgba(243, 177, 106, 0.14);
|
|
1115
|
+
color: var(--plasius-accent);
|
|
1116
|
+
}
|
|
1058
1117
|
.plasius-demo__sidebar {
|
|
1059
1118
|
display: grid;
|
|
1060
|
-
gap:
|
|
1119
|
+
gap: 8px;
|
|
1120
|
+
max-height: min(58vh, 520px);
|
|
1121
|
+
overflow: auto;
|
|
1061
1122
|
}
|
|
1062
1123
|
.plasius-demo__card {
|
|
1063
|
-
padding:
|
|
1124
|
+
padding: 10px;
|
|
1125
|
+
}
|
|
1126
|
+
.plasius-demo__card h2 {
|
|
1127
|
+
margin: 0;
|
|
1128
|
+
color: rgba(226, 236, 255, 0.72);
|
|
1129
|
+
font-family: "JetBrains Mono", monospace;
|
|
1130
|
+
font-size: 11px;
|
|
1131
|
+
letter-spacing: 0.08em;
|
|
1132
|
+
text-transform: uppercase;
|
|
1064
1133
|
}
|
|
1065
1134
|
.plasius-demo__metrics,
|
|
1066
1135
|
.plasius-demo__metrics li {
|
|
@@ -1069,27 +1138,19 @@ function injectStyles() {
|
|
|
1069
1138
|
list-style: none;
|
|
1070
1139
|
}
|
|
1071
1140
|
.plasius-demo__metrics {
|
|
1072
|
-
margin-top:
|
|
1141
|
+
margin-top: 8px;
|
|
1073
1142
|
display: grid;
|
|
1074
|
-
gap:
|
|
1143
|
+
gap: 5px;
|
|
1075
1144
|
color: var(--plasius-muted);
|
|
1076
|
-
|
|
1145
|
+
font-size: 12px;
|
|
1146
|
+
line-height: 1.35;
|
|
1077
1147
|
}
|
|
1078
1148
|
.plasius-demo__metrics li {
|
|
1079
1149
|
border-top: 1px solid rgba(21, 32, 40, 0.08);
|
|
1080
|
-
padding-top:
|
|
1150
|
+
padding-top: 5px;
|
|
1081
1151
|
}
|
|
1082
1152
|
.plasius-demo__legend {
|
|
1083
|
-
|
|
1084
|
-
right: 24px;
|
|
1085
|
-
bottom: 24px;
|
|
1086
|
-
padding: 10px 14px;
|
|
1087
|
-
border-radius: 16px;
|
|
1088
|
-
background: rgba(9, 20, 34, 0.82);
|
|
1089
|
-
border: 1px solid rgba(159, 185, 223, 0.16);
|
|
1090
|
-
color: var(--plasius-muted);
|
|
1091
|
-
font-size: 12px;
|
|
1092
|
-
line-height: 1.45;
|
|
1153
|
+
display: none;
|
|
1093
1154
|
}
|
|
1094
1155
|
.plasius-demo__legend strong {
|
|
1095
1156
|
display: block;
|
|
@@ -1097,15 +1158,62 @@ function injectStyles() {
|
|
|
1097
1158
|
margin-bottom: 4px;
|
|
1098
1159
|
}
|
|
1099
1160
|
.plasius-demo__footer {
|
|
1100
|
-
|
|
1101
|
-
color: rgba(226, 236, 255, 0.68);
|
|
1102
|
-
font-size: 13px;
|
|
1103
|
-
line-height: 1.6;
|
|
1161
|
+
display: none;
|
|
1104
1162
|
}
|
|
1105
1163
|
@media (max-width: 1200px) {
|
|
1106
|
-
.plasius-
|
|
1107
|
-
|
|
1108
|
-
|
|
1164
|
+
.plasius-demo__toolbar {
|
|
1165
|
+
top: 92px;
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
@media (max-width: 640px) {
|
|
1169
|
+
.plasius-demo__status {
|
|
1170
|
+
left: 10px;
|
|
1171
|
+
bottom: 10px;
|
|
1172
|
+
max-width: calc(100vw - 126px);
|
|
1173
|
+
padding: 6px 8px;
|
|
1174
|
+
}
|
|
1175
|
+
.plasius-demo__status-text {
|
|
1176
|
+
display: none;
|
|
1177
|
+
}
|
|
1178
|
+
.plasius-demo__status-badge {
|
|
1179
|
+
max-width: calc(100vw - 142px);
|
|
1180
|
+
overflow: hidden;
|
|
1181
|
+
text-overflow: ellipsis;
|
|
1182
|
+
white-space: nowrap;
|
|
1183
|
+
}
|
|
1184
|
+
.plasius-demo__toolbar {
|
|
1185
|
+
top: 10px;
|
|
1186
|
+
left: 10px;
|
|
1187
|
+
right: 10px;
|
|
1188
|
+
max-width: calc(100vw - 20px);
|
|
1189
|
+
flex-wrap: nowrap;
|
|
1190
|
+
overflow-x: auto;
|
|
1191
|
+
padding-bottom: 4px;
|
|
1192
|
+
scrollbar-width: none;
|
|
1193
|
+
}
|
|
1194
|
+
.plasius-demo__toolbar::-webkit-scrollbar {
|
|
1195
|
+
display: none;
|
|
1196
|
+
}
|
|
1197
|
+
.plasius-demo button,
|
|
1198
|
+
.plasius-demo .plasius-toggle,
|
|
1199
|
+
.plasius-demo select {
|
|
1200
|
+
padding: 7px 8px;
|
|
1201
|
+
font-size: 12px;
|
|
1202
|
+
white-space: nowrap;
|
|
1203
|
+
}
|
|
1204
|
+
.plasius-demo__diagnostics {
|
|
1205
|
+
right: 10px;
|
|
1206
|
+
bottom: 10px;
|
|
1207
|
+
}
|
|
1208
|
+
.plasius-demo__diagnostics[open] {
|
|
1209
|
+
bottom: 56px;
|
|
1210
|
+
left: 10px;
|
|
1211
|
+
right: 10px;
|
|
1212
|
+
width: auto;
|
|
1213
|
+
max-width: none;
|
|
1214
|
+
}
|
|
1215
|
+
.plasius-demo__diagnostics[open] .plasius-demo__sidebar {
|
|
1216
|
+
max-height: min(42vh, 340px);
|
|
1109
1217
|
}
|
|
1110
1218
|
}
|
|
1111
1219
|
`;
|
|
@@ -1532,38 +1640,74 @@ function buildTrianglesFromMesh(
|
|
|
1532
1640
|
}
|
|
1533
1641
|
}
|
|
1534
1642
|
|
|
1535
|
-
|
|
1536
|
-
|
|
1643
|
+
function createShowcaseAssetCatalog({
|
|
1644
|
+
mode,
|
|
1645
|
+
ships,
|
|
1646
|
+
environment,
|
|
1647
|
+
primaryShipKey = "brigantine",
|
|
1648
|
+
fallbackReason = null,
|
|
1649
|
+
}) {
|
|
1650
|
+
return Object.freeze({
|
|
1651
|
+
mode,
|
|
1652
|
+
primaryShipKey,
|
|
1653
|
+
ships: Object.freeze(ships),
|
|
1654
|
+
environment: Object.freeze(environment),
|
|
1655
|
+
fallbackReason,
|
|
1656
|
+
});
|
|
1657
|
+
}
|
|
1658
|
+
|
|
1659
|
+
function normalizeAssetCatalogFailureReason(error) {
|
|
1660
|
+
if (typeof error?.message === "string" && error.message.trim().length > 0) {
|
|
1661
|
+
return error.message;
|
|
1662
|
+
}
|
|
1663
|
+
|
|
1664
|
+
return "showcase asset loading failed";
|
|
1665
|
+
}
|
|
1666
|
+
|
|
1667
|
+
async function loadShowcaseAssetCatalog({ includeSecondaryShip = true } = {}) {
|
|
1668
|
+
const [brigantine, lighthouse, harborDock, shoreline] = await Promise.all([
|
|
1537
1669
|
loadGltfModel(resolveShowcaseAssetUrl("brigantine")),
|
|
1538
|
-
loadGltfModel(resolveShowcaseAssetUrl("cutter")),
|
|
1539
1670
|
loadGltfModel(resolveShowcaseAssetUrl("lighthouse")),
|
|
1540
1671
|
loadGltfModel(resolveShowcaseAssetUrl("harbor-dock")),
|
|
1672
|
+
loadGltfModel(resolveShowcaseAssetUrl("shoreline")),
|
|
1541
1673
|
]);
|
|
1674
|
+
const ships = {
|
|
1675
|
+
brigantine,
|
|
1676
|
+
};
|
|
1542
1677
|
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1678
|
+
if (includeSecondaryShip) {
|
|
1679
|
+
ships.cutter = await loadGltfModel(resolveShowcaseAssetUrl("cutter"));
|
|
1680
|
+
}
|
|
1681
|
+
|
|
1682
|
+
return createShowcaseAssetCatalog({
|
|
1683
|
+
mode: includeSecondaryShip ? "modeled-rich" : "modeled-baseline",
|
|
1684
|
+
ships,
|
|
1685
|
+
environment: {
|
|
1550
1686
|
lighthouse,
|
|
1551
1687
|
"harbor-dock": harborDock,
|
|
1552
|
-
|
|
1688
|
+
shoreline,
|
|
1689
|
+
},
|
|
1553
1690
|
});
|
|
1554
1691
|
}
|
|
1555
1692
|
|
|
1556
|
-
function createLegacyShowcaseAssetCatalog() {
|
|
1557
|
-
const brigantine = loadGltfModel(resolveShowcaseAssetUrl("brigantine"));
|
|
1558
|
-
return
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1693
|
+
async function createLegacyShowcaseAssetCatalog(error = null) {
|
|
1694
|
+
const brigantine = await loadGltfModel(resolveShowcaseAssetUrl("brigantine"));
|
|
1695
|
+
return createShowcaseAssetCatalog({
|
|
1696
|
+
mode: "legacy-fallback",
|
|
1697
|
+
ships: {
|
|
1698
|
+
brigantine,
|
|
1699
|
+
},
|
|
1700
|
+
environment: {},
|
|
1701
|
+
fallbackReason: normalizeAssetCatalogFailureReason(error),
|
|
1702
|
+
});
|
|
1703
|
+
}
|
|
1704
|
+
|
|
1705
|
+
async function loadShowcaseAssetCatalogWithFallback({ includeSecondaryShip = true } = {}) {
|
|
1706
|
+
try {
|
|
1707
|
+
return await loadShowcaseAssetCatalog({ includeSecondaryShip });
|
|
1708
|
+
} catch (error) {
|
|
1709
|
+
return createLegacyShowcaseAssetCatalog(error);
|
|
1710
|
+
}
|
|
1567
1711
|
}
|
|
1568
1712
|
|
|
1569
1713
|
function resolveShipModel(state, ship, fallbackModel = null) {
|
|
@@ -1574,6 +1718,10 @@ function resolveShipModel(state, ship, fallbackModel = null) {
|
|
|
1574
1718
|
);
|
|
1575
1719
|
}
|
|
1576
1720
|
|
|
1721
|
+
function hasModeledHarborEnvironment(state) {
|
|
1722
|
+
return Object.keys(state.assetCatalog?.environment ?? {}).length > 0;
|
|
1723
|
+
}
|
|
1724
|
+
|
|
1577
1725
|
function createPerformanceGovernor(performanceFeatures) {
|
|
1578
1726
|
const createQualityLadderAdapter = assertRequiredFunction(
|
|
1579
1727
|
performanceFeatures,
|
|
@@ -1690,24 +1838,27 @@ function buildDemoDom(root, options) {
|
|
|
1690
1838
|
${t(gpuSharedTranslationKeys.legendCollisions)}
|
|
1691
1839
|
</div>
|
|
1692
1840
|
</section>
|
|
1693
|
-
<
|
|
1694
|
-
<
|
|
1695
|
-
|
|
1696
|
-
<
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
<
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
<
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
<
|
|
1709
|
-
|
|
1710
|
-
|
|
1841
|
+
<details class="plasius-demo__diagnostics">
|
|
1842
|
+
<summary>${t(gpuSharedTranslationKeys.debugTelemetry)}</summary>
|
|
1843
|
+
<aside class="plasius-demo__sidebar">
|
|
1844
|
+
<section class="plasius-panel plasius-demo__card">
|
|
1845
|
+
<h2>${t(gpuSharedTranslationKeys.sceneState)}</h2>
|
|
1846
|
+
<ul id="sceneMetrics" class="plasius-demo__metrics"></ul>
|
|
1847
|
+
</section>
|
|
1848
|
+
<section class="plasius-panel plasius-demo__card">
|
|
1849
|
+
<h2>${t(gpuSharedTranslationKeys.qualityBudgets)}</h2>
|
|
1850
|
+
<ul id="qualityMetrics" class="plasius-demo__metrics"></ul>
|
|
1851
|
+
</section>
|
|
1852
|
+
<section class="plasius-panel plasius-demo__card">
|
|
1853
|
+
<h2>${t(gpuSharedTranslationKeys.debugTelemetry)}</h2>
|
|
1854
|
+
<ul id="debugMetrics" class="plasius-demo__metrics"></ul>
|
|
1855
|
+
</section>
|
|
1856
|
+
<section class="plasius-panel plasius-demo__card">
|
|
1857
|
+
<h2>${t(gpuSharedTranslationKeys.notes)}</h2>
|
|
1858
|
+
<ul id="sceneNotes" class="plasius-demo__metrics"></ul>
|
|
1859
|
+
</section>
|
|
1860
|
+
</aside>
|
|
1861
|
+
</details>
|
|
1711
1862
|
</section>
|
|
1712
1863
|
<p class="plasius-demo__footer">
|
|
1713
1864
|
This visual example is shared across the GPU packages to keep manual validation fast and consistent.
|
|
@@ -2125,13 +2276,13 @@ function advanceShowcaseClothSimulationState(clothState, options = {}) {
|
|
|
2125
2276
|
)
|
|
2126
2277
|
);
|
|
2127
2278
|
const windStrength =
|
|
2128
|
-
(
|
|
2279
|
+
(0.94 + broadMotion * 0.82 + wrinkleLayers * 0.08) *
|
|
2129
2280
|
flagMotion *
|
|
2130
|
-
(0.
|
|
2281
|
+
(0.36 + u * 0.92);
|
|
2131
2282
|
const wrinkleForce = vec3(
|
|
2132
|
-
Math.sin(wrinklePhase) * 0.
|
|
2133
|
-
Math.cos(wrinklePhase * 0.7) * 0.
|
|
2134
|
-
Math.cos(wrinklePhase) * 0.
|
|
2283
|
+
Math.sin(wrinklePhase) * 0.12 * wrinkleMotion * flagMotion,
|
|
2284
|
+
Math.cos(wrinklePhase * 0.7) * 0.045 * wrinkleMotion,
|
|
2285
|
+
Math.cos(wrinklePhase) * 0.08 * broadMotion * flagMotion
|
|
2135
2286
|
);
|
|
2136
2287
|
const acceleration = addVec3(
|
|
2137
2288
|
vec3(0, -0.48 - u * 0.08, 0),
|
|
@@ -2197,25 +2348,25 @@ function resolveVisualConfig(nearLighting, lightingSnapshot, customVisuals = {})
|
|
|
2197
2348
|
ambientMist: "rgba(41, 63, 97, 0.16)",
|
|
2198
2349
|
reflectionStrength: lightingSnapshot.currentLevel.config.reflectionStrength,
|
|
2199
2350
|
shadowAccent: lightingSnapshot.currentLevel.config.shadowStrength,
|
|
2200
|
-
waveAmplitude: 0.
|
|
2351
|
+
waveAmplitude: 0.82,
|
|
2201
2352
|
waveDirection: { x: 0.88, z: 0.28 },
|
|
2202
|
-
wavePhaseSpeed: 0.
|
|
2203
|
-
wakeStrength: 0.
|
|
2204
|
-
wakeLength:
|
|
2205
|
-
collisionRippleStrength: 0.
|
|
2206
|
-
waterNear: { r: 0.
|
|
2207
|
-
waterFar: { r: 0.
|
|
2353
|
+
wavePhaseSpeed: 0.74,
|
|
2354
|
+
wakeStrength: 0.24,
|
|
2355
|
+
wakeLength: 17,
|
|
2356
|
+
collisionRippleStrength: 0.22,
|
|
2357
|
+
waterNear: { r: 0.05, g: 0.2, b: 0.3 },
|
|
2358
|
+
waterFar: { r: 0.13, g: 0.31, b: 0.45 },
|
|
2208
2359
|
harborWall: { r: 0.26, g: 0.24, b: 0.28 },
|
|
2209
2360
|
harborDeck: { r: 0.33, g: 0.22, b: 0.16 },
|
|
2210
2361
|
harborTower: { r: 0.23, g: 0.24, b: 0.29 },
|
|
2211
|
-
flagColor: { r: 0.
|
|
2212
|
-
flagMotion: 0.
|
|
2362
|
+
flagColor: { r: 0.54, g: 0.13, b: 0.11 },
|
|
2363
|
+
flagMotion: 0.58,
|
|
2213
2364
|
lanternCore: { r: 0.98, g: 0.8, b: 0.48 },
|
|
2214
2365
|
lanternGlow: { r: 1, g: 0.56, b: 0.2 },
|
|
2215
2366
|
lanternReflectionStrength: 0.42,
|
|
2216
2367
|
torchCore: { r: 0.99, g: 0.72, b: 0.36 },
|
|
2217
2368
|
torchGlow: { r: 0.98, g: 0.38, b: 0.15 },
|
|
2218
|
-
collisionFlash: "rgba(255, 212, 168, 0.
|
|
2369
|
+
collisionFlash: "rgba(255, 212, 168, 0.08)",
|
|
2219
2370
|
};
|
|
2220
2371
|
|
|
2221
2372
|
return {
|
|
@@ -2299,6 +2450,11 @@ function buildClothSurface(model, state, meshDetail, visuals, clothFeatures) {
|
|
|
2299
2450
|
representation: clothPresentation.representation,
|
|
2300
2451
|
continuity: clothPresentation.continuity,
|
|
2301
2452
|
color: visuals.flagColor,
|
|
2453
|
+
material: Object.freeze({
|
|
2454
|
+
weaveAlpha: clothPresentation.band === "near" ? 0.22 : 0.12,
|
|
2455
|
+
foldAlpha: clothPresentation.band === "near" ? 0.3 : 0.18,
|
|
2456
|
+
edgeHighlightAlpha: clothPresentation.band === "near" ? 0.42 : 0.28,
|
|
2457
|
+
}),
|
|
2302
2458
|
positions: clothState.positions.map((point) => vec3(point.x, point.y, point.z)),
|
|
2303
2459
|
indices: clothState.indices,
|
|
2304
2460
|
grid: { rows: clothState.rows, cols: clothState.cols },
|
|
@@ -2412,7 +2568,7 @@ function buildWaterMotionEffects(state) {
|
|
|
2412
2568
|
impulse.z
|
|
2413
2569
|
),
|
|
2414
2570
|
radius,
|
|
2415
|
-
opacity: clamp(impulse.life * 0.
|
|
2571
|
+
opacity: clamp(impulse.life * 0.13, 0.035, 0.15),
|
|
2416
2572
|
});
|
|
2417
2573
|
});
|
|
2418
2574
|
|
|
@@ -2427,7 +2583,7 @@ function buildWaterMotionEffects(state) {
|
|
|
2427
2583
|
const lateral = vec3(-direction.z, 0, direction.x);
|
|
2428
2584
|
const points = [];
|
|
2429
2585
|
for (let sampleIndex = 0; sampleIndex < 6; sampleIndex += 1) {
|
|
2430
|
-
const along = 1 + sampleIndex * 1.
|
|
2586
|
+
const along = 1 + sampleIndex * 1.55;
|
|
2431
2587
|
const lateralOffset =
|
|
2432
2588
|
Math.sin(state.time * 1.2 + sampleIndex * 0.8 + readVisualNumber(ship.wanderPhase, 0)) * 0.12;
|
|
2433
2589
|
const worldPoint = addVec3(
|
|
@@ -2441,13 +2597,14 @@ function buildWaterMotionEffects(state) {
|
|
|
2441
2597
|
sampleWave(state, worldPoint.x, worldPoint.z, state.time) * 0.24 + 0.04,
|
|
2442
2598
|
worldPoint.z
|
|
2443
2599
|
),
|
|
2444
|
-
width: 0.
|
|
2600
|
+
width: 0.3 + sampleIndex * 0.11,
|
|
2601
|
+
foam: clamp(0.28 - sampleIndex * 0.028 + speed * 0.025, 0.1, 0.34),
|
|
2445
2602
|
})
|
|
2446
2603
|
);
|
|
2447
2604
|
}
|
|
2448
2605
|
wakeTrails.push(
|
|
2449
2606
|
Object.freeze({
|
|
2450
|
-
opacity: clamp(0.
|
|
2607
|
+
opacity: clamp(0.1 + speed * 0.048, 0.12, 0.24),
|
|
2451
2608
|
points: Object.freeze(points),
|
|
2452
2609
|
})
|
|
2453
2610
|
);
|
|
@@ -2459,6 +2616,28 @@ function buildWaterMotionEffects(state) {
|
|
|
2459
2616
|
});
|
|
2460
2617
|
}
|
|
2461
2618
|
|
|
2619
|
+
function buildShorelineFoamSegments(state) {
|
|
2620
|
+
return Object.freeze(
|
|
2621
|
+
SHORELINE_FOAM_ANCHORS.map((anchor, index) => {
|
|
2622
|
+
const pulse = 0.5 + Math.sin(state.time * 0.84 + index * 1.17) * 0.5;
|
|
2623
|
+
const drift = Math.sin(state.time * 0.38 + index * 0.61) * 0.1;
|
|
2624
|
+
const direction = normalizeVec3(vec3(Math.cos(anchor.angle), 0, Math.sin(anchor.angle)));
|
|
2625
|
+
const center = vec3(
|
|
2626
|
+
anchor.x + direction.x * drift,
|
|
2627
|
+
sampleWave(state, anchor.x, anchor.z, state.time) * 0.12 - 0.02,
|
|
2628
|
+
anchor.z + direction.z * drift
|
|
2629
|
+
);
|
|
2630
|
+
return Object.freeze({
|
|
2631
|
+
center,
|
|
2632
|
+
direction,
|
|
2633
|
+
length: anchor.length * (0.78 + pulse * 0.34),
|
|
2634
|
+
width: 0.16 + pulse * 0.12,
|
|
2635
|
+
opacity: 0.07 + pulse * 0.12,
|
|
2636
|
+
});
|
|
2637
|
+
})
|
|
2638
|
+
);
|
|
2639
|
+
}
|
|
2640
|
+
|
|
2462
2641
|
function buildWaterBands(state, fluidDetail, visuals, fluidFeatures) {
|
|
2463
2642
|
const resolvedFluidFeatures = normalizeFluidFeatureAdapters(fluidFeatures);
|
|
2464
2643
|
const fluidPlan = resolvedFluidFeatures.createPlan({
|
|
@@ -2487,14 +2666,14 @@ function buildWaterBands(state, fluidDetail, visuals, fluidFeatures) {
|
|
|
2487
2666
|
const bandContinuity = resolveFluidBandContinuity(continuity, bandSpec.band);
|
|
2488
2667
|
const bandResolution =
|
|
2489
2668
|
bandSpec.band === "near"
|
|
2490
|
-
? fluidDetail.nearResolution
|
|
2669
|
+
? Math.ceil(fluidDetail.nearResolution * 1.28)
|
|
2491
2670
|
: bandSpec.band === "mid"
|
|
2492
|
-
? fluidDetail.midResolution
|
|
2671
|
+
? Math.ceil(fluidDetail.midResolution * 1.2)
|
|
2493
2672
|
: bandSpec.band === "far"
|
|
2494
2673
|
? 5
|
|
2495
2674
|
: 3;
|
|
2496
|
-
const cols = Math.max(4, bandResolution * 2);
|
|
2497
|
-
const rows = Math.max(4, bandResolution + 2);
|
|
2675
|
+
const cols = Math.max(4, bandResolution * (bandSpec.band === "near" ? 3 : 2));
|
|
2676
|
+
const rows = Math.max(4, bandResolution + (bandSpec.band === "near" ? 5 : 2));
|
|
2498
2677
|
const positions = [];
|
|
2499
2678
|
const indices = [];
|
|
2500
2679
|
const originX = -bandSpec.width * 0.5;
|
|
@@ -2505,11 +2684,16 @@ function buildWaterBands(state, fluidDetail, visuals, fluidFeatures) {
|
|
|
2505
2684
|
const v = row / (rows - 1);
|
|
2506
2685
|
const x = originX + bandSpec.width * u;
|
|
2507
2686
|
const z = originZ + bandSpec.depth * v;
|
|
2508
|
-
const
|
|
2687
|
+
const baseHeight =
|
|
2509
2688
|
bandSpec.y +
|
|
2510
2689
|
sampleWave(state, x, z, state.time) *
|
|
2511
2690
|
bandContinuity.amplitudeFloor *
|
|
2512
2691
|
(bandSpec.band === "near" ? 0.9 : bandSpec.band === "mid" ? 0.55 : 0.3);
|
|
2692
|
+
const detailHeight =
|
|
2693
|
+
bandSpec.band === "near"
|
|
2694
|
+
? Math.sin(x * 1.25 + z * 0.42 - state.time * 2.4) * 0.035
|
|
2695
|
+
: 0;
|
|
2696
|
+
const y = baseHeight + detailHeight;
|
|
2513
2697
|
positions.push(vec3(x, y, z));
|
|
2514
2698
|
}
|
|
2515
2699
|
}
|
|
@@ -2546,7 +2730,12 @@ function buildWaterBands(state, fluidDetail, visuals, fluidFeatures) {
|
|
|
2546
2730
|
r: mix(visuals.waterFar.r, 0.76, 0.2),
|
|
2547
2731
|
g: mix(visuals.waterFar.g, 0.78, 0.2),
|
|
2548
2732
|
b: mix(visuals.waterFar.b, 0.82, 0.2),
|
|
2549
|
-
|
|
2733
|
+
},
|
|
2734
|
+
material: Object.freeze({
|
|
2735
|
+
highlightAlpha: bandSpec.band === "near" ? 0.2 : bandSpec.band === "mid" ? 0.13 : 0.07,
|
|
2736
|
+
foamAlpha: bandSpec.band === "near" ? 0.28 : bandSpec.band === "mid" ? 0.14 : 0.05,
|
|
2737
|
+
microRippleScale: bandSpec.band === "near" ? 1 : bandSpec.band === "mid" ? 0.58 : 0.28,
|
|
2738
|
+
}),
|
|
2550
2739
|
});
|
|
2551
2740
|
}
|
|
2552
2741
|
|
|
@@ -2626,15 +2815,15 @@ function createSceneState(options, featureAdapters) {
|
|
|
2626
2815
|
{
|
|
2627
2816
|
id: "northwind",
|
|
2628
2817
|
modelKey: "brigantine",
|
|
2629
|
-
position: vec3(-
|
|
2630
|
-
velocity: vec3(
|
|
2631
|
-
rotationY:
|
|
2632
|
-
angularVelocity: 0.
|
|
2818
|
+
position: vec3(-7.8, 0, 11.2),
|
|
2819
|
+
velocity: vec3(1.08, 0, -0.18),
|
|
2820
|
+
rotationY: 1.38,
|
|
2821
|
+
angularVelocity: 0.025,
|
|
2633
2822
|
tint: { r: 0.62, g: 0.39, b: 0.23 },
|
|
2634
2823
|
massScale: 1.42,
|
|
2635
|
-
cruiseSpeed:
|
|
2636
|
-
throttleResponse: 0.
|
|
2637
|
-
rudderResponse: 0.
|
|
2824
|
+
cruiseSpeed: 1.22,
|
|
2825
|
+
throttleResponse: 0.36,
|
|
2826
|
+
rudderResponse: 0.4,
|
|
2638
2827
|
wanderPhase: 0.35,
|
|
2639
2828
|
lanterns: CUTTER_LANTERNS,
|
|
2640
2829
|
lanternStrength: 1.06,
|
|
@@ -2643,15 +2832,15 @@ function createSceneState(options, featureAdapters) {
|
|
|
2643
2832
|
{
|
|
2644
2833
|
id: "tidecaller",
|
|
2645
2834
|
modelKey: "cutter",
|
|
2646
|
-
position: vec3(
|
|
2647
|
-
velocity: vec3(-
|
|
2648
|
-
rotationY: -
|
|
2649
|
-
angularVelocity: -0.
|
|
2835
|
+
position: vec3(6.8, 0, 5.4),
|
|
2836
|
+
velocity: vec3(-0.82, 0, 0.14),
|
|
2837
|
+
rotationY: -1.34,
|
|
2838
|
+
angularVelocity: -0.035,
|
|
2650
2839
|
tint: { r: 0.58, g: 0.24, b: 0.16 },
|
|
2651
2840
|
massScale: 0.84,
|
|
2652
|
-
cruiseSpeed:
|
|
2653
|
-
throttleResponse: 0.
|
|
2654
|
-
rudderResponse: 0.
|
|
2841
|
+
cruiseSpeed: 1.36,
|
|
2842
|
+
throttleResponse: 0.52,
|
|
2843
|
+
rudderResponse: 0.58,
|
|
2655
2844
|
wanderPhase: 1.6,
|
|
2656
2845
|
lanterns: SHIP_LANTERNS,
|
|
2657
2846
|
lanternStrength: 1.18,
|
|
@@ -2885,7 +3074,7 @@ function renderProjectedShadow(ctx, worldPoints, camera, viewport, lightDir, opt
|
|
|
2885
3074
|
}
|
|
2886
3075
|
|
|
2887
3076
|
function pushHarborGeometry(camera, viewport, triangles, state) {
|
|
2888
|
-
if (!state
|
|
3077
|
+
if (!hasModeledHarborEnvironment(state)) {
|
|
2889
3078
|
for (const object of LEGACY_HARBOR_LAYOUT) {
|
|
2890
3079
|
buildTrianglesFromMesh(
|
|
2891
3080
|
{ positions: [object], indices: [0], normals: null, colors: null, material: createLegacyMeshPrimitive({})?.material, bounds: null, name: "legacy-structure" },
|
|
@@ -2995,13 +3184,14 @@ function renderShipRigging(ctx, ship, camera, viewport) {
|
|
|
2995
3184
|
|
|
2996
3185
|
function renderClothAccent(ctx, cloth, camera, viewport) {
|
|
2997
3186
|
const projected = cloth.positions.map((point) => projectPoint(point, camera, viewport));
|
|
2998
|
-
|
|
2999
|
-
ctx.
|
|
3187
|
+
const material = cloth.material ?? {};
|
|
3188
|
+
ctx.strokeStyle = `rgba(255, 241, 226, ${material.foldAlpha ?? 0.32})`;
|
|
3189
|
+
ctx.lineWidth = 1.8;
|
|
3000
3190
|
|
|
3001
3191
|
for (
|
|
3002
3192
|
let row = 0;
|
|
3003
3193
|
row < cloth.grid.rows;
|
|
3004
|
-
row += Math.max(1, Math.floor(cloth.grid.rows /
|
|
3194
|
+
row += Math.max(1, Math.floor(cloth.grid.rows / 6))
|
|
3005
3195
|
) {
|
|
3006
3196
|
ctx.beginPath();
|
|
3007
3197
|
let started = false;
|
|
@@ -3022,6 +3212,32 @@ function renderClothAccent(ctx, cloth, camera, viewport) {
|
|
|
3022
3212
|
}
|
|
3023
3213
|
}
|
|
3024
3214
|
|
|
3215
|
+
ctx.strokeStyle = `rgba(255, 228, 204, ${material.weaveAlpha ?? 0.22})`;
|
|
3216
|
+
ctx.lineWidth = 0.85;
|
|
3217
|
+
for (
|
|
3218
|
+
let column = 1;
|
|
3219
|
+
column < cloth.grid.cols - 1;
|
|
3220
|
+
column += Math.max(1, Math.floor(cloth.grid.cols / 8))
|
|
3221
|
+
) {
|
|
3222
|
+
ctx.beginPath();
|
|
3223
|
+
let started = false;
|
|
3224
|
+
for (let row = 0; row < cloth.grid.rows; row += 1) {
|
|
3225
|
+
const point = projected[row * cloth.grid.cols + column];
|
|
3226
|
+
if (!point) {
|
|
3227
|
+
continue;
|
|
3228
|
+
}
|
|
3229
|
+
if (!started) {
|
|
3230
|
+
ctx.moveTo(point.x, point.y);
|
|
3231
|
+
started = true;
|
|
3232
|
+
} else {
|
|
3233
|
+
ctx.lineTo(point.x, point.y);
|
|
3234
|
+
}
|
|
3235
|
+
}
|
|
3236
|
+
if (started) {
|
|
3237
|
+
ctx.stroke();
|
|
3238
|
+
}
|
|
3239
|
+
}
|
|
3240
|
+
|
|
3025
3241
|
const borderIndices = [
|
|
3026
3242
|
0,
|
|
3027
3243
|
cloth.grid.cols - 1,
|
|
@@ -3029,6 +3245,26 @@ function renderClothAccent(ctx, cloth, camera, viewport) {
|
|
|
3029
3245
|
(cloth.grid.rows - 1) * cloth.grid.cols,
|
|
3030
3246
|
];
|
|
3031
3247
|
ctx.fillStyle = colorToRgba(cloth.color, 0.95);
|
|
3248
|
+
ctx.strokeStyle = `rgba(255, 246, 236, ${material.edgeHighlightAlpha ?? 0.5})`;
|
|
3249
|
+
ctx.lineWidth = 1.4;
|
|
3250
|
+
ctx.beginPath();
|
|
3251
|
+
let borderStarted = false;
|
|
3252
|
+
for (let column = 0; column < cloth.grid.cols; column += 1) {
|
|
3253
|
+
const point = projected[column];
|
|
3254
|
+
if (!point) {
|
|
3255
|
+
continue;
|
|
3256
|
+
}
|
|
3257
|
+
if (!borderStarted) {
|
|
3258
|
+
ctx.moveTo(point.x, point.y);
|
|
3259
|
+
borderStarted = true;
|
|
3260
|
+
} else {
|
|
3261
|
+
ctx.lineTo(point.x, point.y);
|
|
3262
|
+
}
|
|
3263
|
+
}
|
|
3264
|
+
if (borderStarted) {
|
|
3265
|
+
ctx.stroke();
|
|
3266
|
+
}
|
|
3267
|
+
|
|
3032
3268
|
for (const index of borderIndices) {
|
|
3033
3269
|
const point = projected[index];
|
|
3034
3270
|
if (!point) {
|
|
@@ -3045,14 +3281,22 @@ function renderWaterHighlights(ctx, waterBands, camera, viewport) {
|
|
|
3045
3281
|
if (band.band === "horizon") {
|
|
3046
3282
|
continue;
|
|
3047
3283
|
}
|
|
3048
|
-
const interval = band.band === "near" ?
|
|
3049
|
-
const alpha = band.band === "near" ? 0.22 : 0.14;
|
|
3284
|
+
const interval = band.band === "near" ? 4 : 5;
|
|
3285
|
+
const alpha = band.material?.highlightAlpha ?? (band.band === "near" ? 0.22 : 0.14);
|
|
3050
3286
|
ctx.strokeStyle = `rgba(232, 247, 255, ${alpha})`;
|
|
3051
|
-
ctx.lineWidth = band.band === "near" ?
|
|
3287
|
+
ctx.lineWidth = band.band === "near" ? 0.9 : 0.65;
|
|
3052
3288
|
for (let row = interval; row < band.rows - 1; row += interval) {
|
|
3053
|
-
ctx.beginPath();
|
|
3054
3289
|
let started = false;
|
|
3055
|
-
|
|
3290
|
+
ctx.beginPath();
|
|
3291
|
+
for (let column = 0; column < band.cols; column += band.band === "near" ? 2 : 3) {
|
|
3292
|
+
if (pseudoRandom(row * 47 + column * 13) < 0.18) {
|
|
3293
|
+
if (started) {
|
|
3294
|
+
ctx.stroke();
|
|
3295
|
+
ctx.beginPath();
|
|
3296
|
+
started = false;
|
|
3297
|
+
}
|
|
3298
|
+
continue;
|
|
3299
|
+
}
|
|
3056
3300
|
const point = projectPoint(
|
|
3057
3301
|
band.positions[row * band.cols + column],
|
|
3058
3302
|
camera,
|
|
@@ -3072,9 +3316,63 @@ function renderWaterHighlights(ctx, waterBands, camera, viewport) {
|
|
|
3072
3316
|
ctx.stroke();
|
|
3073
3317
|
}
|
|
3074
3318
|
}
|
|
3319
|
+
|
|
3320
|
+
if (band.band === "near") {
|
|
3321
|
+
ctx.fillStyle = `rgba(236, 249, 255, ${(band.material?.foamAlpha ?? 0.28) * 0.72})`;
|
|
3322
|
+
for (let column = 3; column < band.cols - 3; column += 10) {
|
|
3323
|
+
const point = projectPoint(
|
|
3324
|
+
band.positions[Math.floor(band.rows * 0.42) * band.cols + column],
|
|
3325
|
+
camera,
|
|
3326
|
+
viewport
|
|
3327
|
+
);
|
|
3328
|
+
if (!point) {
|
|
3329
|
+
continue;
|
|
3330
|
+
}
|
|
3331
|
+
ctx.beginPath();
|
|
3332
|
+
ctx.ellipse(point.x, point.y, 1.8, 0.75, -0.2, 0, Math.PI * 2);
|
|
3333
|
+
ctx.fill();
|
|
3334
|
+
}
|
|
3335
|
+
}
|
|
3075
3336
|
}
|
|
3076
3337
|
}
|
|
3077
3338
|
|
|
3339
|
+
function renderShorelineFoamSegments(ctx, segments, camera, viewport) {
|
|
3340
|
+
ctx.save();
|
|
3341
|
+
ctx.globalCompositeOperation = "screen";
|
|
3342
|
+
ctx.lineCap = "round";
|
|
3343
|
+
ctx.lineJoin = "round";
|
|
3344
|
+
|
|
3345
|
+
for (const segment of segments) {
|
|
3346
|
+
const half = scaleVec3(segment.direction, segment.length * 0.5);
|
|
3347
|
+
const start = projectPoint(subVec3(segment.center, half), camera, viewport);
|
|
3348
|
+
const end = projectPoint(addVec3(segment.center, half), camera, viewport);
|
|
3349
|
+
const center = projectPoint(segment.center, camera, viewport);
|
|
3350
|
+
if (!start || !end || !center) {
|
|
3351
|
+
continue;
|
|
3352
|
+
}
|
|
3353
|
+
|
|
3354
|
+
const depthScale = clamp(140 / Math.max(12, center.depth), 3, 10);
|
|
3355
|
+
ctx.strokeStyle = `rgba(232, 242, 238, ${segment.opacity})`;
|
|
3356
|
+
ctx.lineWidth = clamp(segment.width * depthScale, 0.8, 2.8);
|
|
3357
|
+
ctx.beginPath();
|
|
3358
|
+
ctx.moveTo(start.x, start.y);
|
|
3359
|
+
ctx.quadraticCurveTo(
|
|
3360
|
+
center.x,
|
|
3361
|
+
center.y + Math.sin(segment.center.x * 1.7) * 2.4,
|
|
3362
|
+
end.x,
|
|
3363
|
+
end.y
|
|
3364
|
+
);
|
|
3365
|
+
ctx.stroke();
|
|
3366
|
+
|
|
3367
|
+
ctx.fillStyle = `rgba(248, 251, 246, ${segment.opacity * 0.68})`;
|
|
3368
|
+
ctx.beginPath();
|
|
3369
|
+
ctx.ellipse(center.x, center.y, depthScale * 0.18, depthScale * 0.08, -0.2, 0, Math.PI * 2);
|
|
3370
|
+
ctx.fill();
|
|
3371
|
+
}
|
|
3372
|
+
|
|
3373
|
+
ctx.restore();
|
|
3374
|
+
}
|
|
3375
|
+
|
|
3078
3376
|
function readPhysicsNumber(physics, key, fallback) {
|
|
3079
3377
|
const value = physics?.[key];
|
|
3080
3378
|
return typeof value === "number" && Number.isFinite(value) ? value : fallback;
|
|
@@ -3113,14 +3411,17 @@ function getShipInverseInertia(ship, shipModel) {
|
|
|
3113
3411
|
}
|
|
3114
3412
|
|
|
3115
3413
|
function spawnSpray(state, point, intensity) {
|
|
3116
|
-
const count =
|
|
3414
|
+
const count = Math.max(
|
|
3415
|
+
3,
|
|
3416
|
+
Math.ceil(state.fluidDetail.getSnapshot().currentLevel.config.splashCount * 0.32)
|
|
3417
|
+
);
|
|
3117
3418
|
for (let index = 0; index < count; index += 1) {
|
|
3118
3419
|
const angle = (index / count) * Math.PI * 2;
|
|
3119
|
-
const speed = 0.
|
|
3420
|
+
const speed = 0.46 + Math.random() * intensity * 0.24;
|
|
3120
3421
|
state.sprays.push({
|
|
3121
3422
|
position: vec3(point.x, point.y, point.z),
|
|
3122
|
-
velocity: vec3(Math.cos(angle) * speed * 0.
|
|
3123
|
-
life:
|
|
3423
|
+
velocity: vec3(Math.cos(angle) * speed * 0.24, 0.46 + Math.random() * 0.34, Math.sin(angle) * speed * 0.18),
|
|
3424
|
+
life: 0.72 + Math.random() * 0.22,
|
|
3124
3425
|
});
|
|
3125
3426
|
}
|
|
3126
3427
|
}
|
|
@@ -3140,8 +3441,8 @@ function resolveShipRoute(ship, state, radius) {
|
|
|
3140
3441
|
const crossCurrent = Math.cos(state.time * 0.31 + readVisualNumber(ship.wanderPhase, 0));
|
|
3141
3442
|
const laneCenter =
|
|
3142
3443
|
ship.id === "northwind"
|
|
3143
|
-
?
|
|
3144
|
-
:
|
|
3444
|
+
? 11.6 + wander * 0.82 + crossCurrent * 0.24
|
|
3445
|
+
: 5.4 + wander * 0.94 - crossCurrent * 0.32;
|
|
3145
3446
|
const targetX =
|
|
3146
3447
|
ship.routeDirection > 0
|
|
3147
3448
|
? HARBOR_BOUNDS.maxX - radius * 1.7
|
|
@@ -3158,7 +3459,7 @@ function updateShipMotion(state, ship, dt, shipModel) {
|
|
|
3158
3459
|
const angularDamping = readPhysicsNumber(physics, "angularDamping", 0.08);
|
|
3159
3460
|
const throttleResponse = readVisualNumber(ship.throttleResponse, 0.58);
|
|
3160
3461
|
const rudderResponse = readVisualNumber(ship.rudderResponse, 0.62);
|
|
3161
|
-
const cruiseSpeed = readVisualNumber(ship.cruiseSpeed,
|
|
3462
|
+
const cruiseSpeed = readVisualNumber(ship.cruiseSpeed, 1.25);
|
|
3162
3463
|
|
|
3163
3464
|
ship.collisionCooldown = Math.max(0, readVisualNumber(ship.collisionCooldown, 0) - dt);
|
|
3164
3465
|
|
|
@@ -3272,7 +3573,7 @@ function resolveShipCollision(state, a, b, shipModelA, shipModelB) {
|
|
|
3272
3573
|
((readPhysicsNumber(shipModelA.physics, "restitution", 0.22) +
|
|
3273
3574
|
readPhysicsNumber(shipModelB.physics, "restitution", 0.22)) /
|
|
3274
3575
|
2) *
|
|
3275
|
-
0.
|
|
3576
|
+
0.42;
|
|
3276
3577
|
if (velocityAlongNormal < 0) {
|
|
3277
3578
|
const impulseMagnitude =
|
|
3278
3579
|
(-(1 + restitution) * velocityAlongNormal) / Math.max(0.0001, invMassSum);
|
|
@@ -3299,7 +3600,7 @@ function resolveShipCollision(state, a, b, shipModelA, shipModelB) {
|
|
|
3299
3600
|
|
|
3300
3601
|
const impactSpeed = Math.abs(velocityAlongNormal);
|
|
3301
3602
|
if (
|
|
3302
|
-
impactSpeed > 0.
|
|
3603
|
+
impactSpeed > 0.36 &&
|
|
3303
3604
|
Math.max(readVisualNumber(a.collisionCooldown, 0), readVisualNumber(b.collisionCooldown, 0)) <= 0
|
|
3304
3605
|
) {
|
|
3305
3606
|
const contactPoint = vec3(
|
|
@@ -3307,21 +3608,21 @@ function resolveShipCollision(state, a, b, shipModelA, shipModelB) {
|
|
|
3307
3608
|
(a.position.y + b.position.y) * 0.5 + 0.14,
|
|
3308
3609
|
(a.position.z + b.position.z) * 0.5
|
|
3309
3610
|
);
|
|
3310
|
-
spawnSpray(state, contactPoint, impactSpeed *
|
|
3611
|
+
spawnSpray(state, contactPoint, impactSpeed * 0.9 + penetration * 2.4);
|
|
3311
3612
|
state.waveImpulses.push({
|
|
3312
3613
|
x: contactPoint.x,
|
|
3313
3614
|
z: contactPoint.z,
|
|
3314
|
-
strength: clamp(0.
|
|
3315
|
-
radius: 0.
|
|
3615
|
+
strength: clamp(0.1 + impactSpeed * 0.18 + penetration * 0.28, 0.08, 0.52),
|
|
3616
|
+
radius: 0.72 + penetration * 0.72,
|
|
3316
3617
|
life: 1,
|
|
3317
3618
|
});
|
|
3318
3619
|
state.collisionCount += 1;
|
|
3319
3620
|
state.collisionFlash = Math.max(
|
|
3320
3621
|
state.collisionFlash,
|
|
3321
|
-
clamp(impactSpeed * 0.
|
|
3622
|
+
clamp(impactSpeed * 0.14 + penetration * 0.32, 0.04, 0.24)
|
|
3322
3623
|
);
|
|
3323
|
-
a.collisionCooldown = 0.
|
|
3324
|
-
b.collisionCooldown = 0.
|
|
3624
|
+
a.collisionCooldown = 0.72;
|
|
3625
|
+
b.collisionCooldown = 0.72;
|
|
3325
3626
|
}
|
|
3326
3627
|
}
|
|
3327
3628
|
|
|
@@ -3352,8 +3653,8 @@ function updateShips(state, dt, shipModel) {
|
|
|
3352
3653
|
}
|
|
3353
3654
|
|
|
3354
3655
|
state.collisionFlash = collided
|
|
3355
|
-
? Math.max(0.
|
|
3356
|
-
: Math.max(0, state.collisionFlash - dt * 1.
|
|
3656
|
+
? Math.max(0.04, state.collisionFlash)
|
|
3657
|
+
: Math.max(0, state.collisionFlash - dt * 1.7);
|
|
3357
3658
|
}
|
|
3358
3659
|
|
|
3359
3660
|
function updateWaveImpulses(state, dt) {
|
|
@@ -3642,7 +3943,11 @@ function renderLighthouseBeam(ctx, state, camera, viewport, visuals) {
|
|
|
3642
3943
|
const lighthousePlacement = SHOWCASE_ENVIRONMENT_LAYOUT.find(
|
|
3643
3944
|
(placement) => placement.assetKey === "lighthouse"
|
|
3644
3945
|
);
|
|
3645
|
-
if (
|
|
3946
|
+
if (
|
|
3947
|
+
!lighthousePlacement ||
|
|
3948
|
+
!state.showcaseRealisticModelsEnabled ||
|
|
3949
|
+
!hasModeledHarborEnvironment(state)
|
|
3950
|
+
) {
|
|
3646
3951
|
return;
|
|
3647
3952
|
}
|
|
3648
3953
|
|
|
@@ -3749,6 +4054,7 @@ function renderWaterMotionEffects(ctx, effects, camera, viewport) {
|
|
|
3749
4054
|
.map((point) => ({
|
|
3750
4055
|
projected: projectPoint(point.center, camera, viewport),
|
|
3751
4056
|
width: point.width,
|
|
4057
|
+
foam: point.foam,
|
|
3752
4058
|
}))
|
|
3753
4059
|
.filter((entry) => entry.projected);
|
|
3754
4060
|
if (projected.length < 2) {
|
|
@@ -3760,8 +4066,8 @@ function renderWaterMotionEffects(ctx, effects, camera, viewport) {
|
|
|
3760
4066
|
const averageWidth =
|
|
3761
4067
|
projected.reduce((total, entry) => total + entry.width, 0) / projected.length;
|
|
3762
4068
|
const baseWidth = clamp((averageWidth / Math.max(0.25, averageDepth)) * 180, 1.6, 5.4);
|
|
3763
|
-
ctx.strokeStyle = `rgba(146, 194, 236, ${wake.opacity * 0.
|
|
3764
|
-
ctx.lineWidth = baseWidth * 1.
|
|
4069
|
+
ctx.strokeStyle = `rgba(146, 194, 236, ${wake.opacity * 0.34})`;
|
|
4070
|
+
ctx.lineWidth = baseWidth * 1.45;
|
|
3765
4071
|
ctx.lineCap = "round";
|
|
3766
4072
|
ctx.lineJoin = "round";
|
|
3767
4073
|
ctx.beginPath();
|
|
@@ -3771,8 +4077,8 @@ function renderWaterMotionEffects(ctx, effects, camera, viewport) {
|
|
|
3771
4077
|
}
|
|
3772
4078
|
ctx.stroke();
|
|
3773
4079
|
|
|
3774
|
-
ctx.strokeStyle = `rgba(234, 247, 255, ${wake.opacity})`;
|
|
3775
|
-
ctx.lineWidth = baseWidth;
|
|
4080
|
+
ctx.strokeStyle = `rgba(234, 247, 255, ${wake.opacity * 0.72})`;
|
|
4081
|
+
ctx.lineWidth = baseWidth * 0.72;
|
|
3776
4082
|
ctx.lineCap = "round";
|
|
3777
4083
|
ctx.lineJoin = "round";
|
|
3778
4084
|
ctx.beginPath();
|
|
@@ -3783,13 +4089,14 @@ function renderWaterMotionEffects(ctx, effects, camera, viewport) {
|
|
|
3783
4089
|
ctx.stroke();
|
|
3784
4090
|
|
|
3785
4091
|
for (const entry of projected.slice(1, 5)) {
|
|
3786
|
-
|
|
4092
|
+
const foam = entry.foam ?? 0.3;
|
|
4093
|
+
ctx.fillStyle = `rgba(239, 248, 255, ${wake.opacity * foam * 0.92})`;
|
|
3787
4094
|
ctx.beginPath();
|
|
3788
4095
|
ctx.ellipse(
|
|
3789
4096
|
entry.projected.x,
|
|
3790
4097
|
entry.projected.y,
|
|
3791
|
-
baseWidth * 0.
|
|
3792
|
-
baseWidth * 0.
|
|
4098
|
+
baseWidth * 0.54,
|
|
4099
|
+
baseWidth * 0.28,
|
|
3793
4100
|
0,
|
|
3794
4101
|
0,
|
|
3795
4102
|
Math.PI * 2
|
|
@@ -3809,15 +4116,25 @@ function renderWaterMotionEffects(ctx, effects, camera, viewport) {
|
|
|
3809
4116
|
const radiusX = Math.hypot(xAxis.x - center.x, xAxis.y - center.y);
|
|
3810
4117
|
const radiusY = Math.hypot(zAxis.x - center.x, zAxis.y - center.y);
|
|
3811
4118
|
ctx.strokeStyle = `rgba(216, 235, 255, ${ring.opacity})`;
|
|
3812
|
-
ctx.lineWidth = clamp((radiusX + radiusY) * 0.
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
|
|
4119
|
+
ctx.lineWidth = clamp((radiusX + radiusY) * 0.014, 0.65, 1.8);
|
|
4120
|
+
for (let segment = 0; segment < 5; segment += 1) {
|
|
4121
|
+
if (pseudoRandom(segment * 31 + radiusX * 0.7 + radiusY * 0.3) < 0.32) {
|
|
4122
|
+
continue;
|
|
4123
|
+
}
|
|
4124
|
+
const startAngle = segment * 1.22 + stateTimePhase(center.x, center.y) * 0.04;
|
|
4125
|
+
ctx.beginPath();
|
|
4126
|
+
ctx.ellipse(center.x, center.y, radiusX, radiusY, 0, startAngle, startAngle + 0.48);
|
|
4127
|
+
ctx.stroke();
|
|
4128
|
+
}
|
|
3816
4129
|
}
|
|
3817
4130
|
|
|
3818
4131
|
ctx.restore();
|
|
3819
4132
|
}
|
|
3820
4133
|
|
|
4134
|
+
function stateTimePhase(x, y) {
|
|
4135
|
+
return Math.sin(x * 0.013 + y * 0.017);
|
|
4136
|
+
}
|
|
4137
|
+
|
|
3821
4138
|
function renderScene(
|
|
3822
4139
|
ctx,
|
|
3823
4140
|
canvas,
|
|
@@ -3899,6 +4216,7 @@ function renderScene(
|
|
|
3899
4216
|
}
|
|
3900
4217
|
|
|
3901
4218
|
const waterMotionEffects = buildWaterMotionEffects(state);
|
|
4219
|
+
const shorelineFoamSegments = buildShorelineFoamSegments(state);
|
|
3902
4220
|
const lightSources = collectSceneLightSources(state, visuals);
|
|
3903
4221
|
|
|
3904
4222
|
pushHarborGeometry(camera, viewport, sceneTriangles, state);
|
|
@@ -3973,6 +4291,7 @@ function renderScene(
|
|
|
3973
4291
|
}
|
|
3974
4292
|
renderWaterMotionEffects(ctx, waterMotionEffects, camera, viewport);
|
|
3975
4293
|
renderWaterHighlights(ctx, water.bandMeshes, camera, viewport);
|
|
4294
|
+
renderShorelineFoamSegments(ctx, shorelineFoamSegments, camera, viewport);
|
|
3976
4295
|
drawTriangles(
|
|
3977
4296
|
ctx,
|
|
3978
4297
|
sceneTriangles,
|
|
@@ -4003,7 +4322,7 @@ function renderScene(
|
|
|
4003
4322
|
|
|
4004
4323
|
const sceneMetrics = [
|
|
4005
4324
|
`focus: ${state.focus}`,
|
|
4006
|
-
`ships: ${state.ships.length} active GLTF hulls across ${new Set(state.ships.map((ship) => ship.modelKey)).size} model families`,
|
|
4325
|
+
`ships: ${state.ships.length} active GLTF hulls across ${new Set(state.ships.map((ship) => resolveShipModel(state, ship, shipModel)?.name ?? ship.modelKey)).size} model families`,
|
|
4007
4326
|
`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`,
|
|
4008
4327
|
`physics snapshot: ${state.physics.snapshot.stage} (${state.physics.snapshot.stability})`,
|
|
4009
4328
|
`physics contacts: ${state.contactCount}`,
|
|
@@ -4087,7 +4406,7 @@ function updateSceneState(state, dt, shipModel, featureAdapters) {
|
|
|
4087
4406
|
advanceShowcaseClothSimulationState(clothState, {
|
|
4088
4407
|
dt,
|
|
4089
4408
|
time: state.time,
|
|
4090
|
-
flagMotion: readVisualNumber(state.demoVisuals?.flagMotion, 0.
|
|
4409
|
+
flagMotion: readVisualNumber(state.demoVisuals?.flagMotion, 0.58),
|
|
4091
4410
|
waveInfluence: sampleWave(state, FLAG_LAYOUT.origin.x + FLAG_LAYOUT.width * 0.55, FLAG_LAYOUT.origin.z + FLAG_LAYOUT.width * 0.48, state.time),
|
|
4092
4411
|
});
|
|
4093
4412
|
updatePhysicsSnapshot(state, shipModel, featureAdapters.physics);
|
|
@@ -4098,17 +4417,28 @@ function syncTextState(state, shipModel, featureAdapters) {
|
|
|
4098
4417
|
coordinateSystem: "right-handed world; +x right, +y up, +z forward from the shore",
|
|
4099
4418
|
focus: state.focus,
|
|
4100
4419
|
stress: state.stress,
|
|
4101
|
-
ships: state.ships.map((ship) =>
|
|
4102
|
-
|
|
4103
|
-
|
|
4104
|
-
|
|
4105
|
-
|
|
4106
|
-
|
|
4107
|
-
|
|
4108
|
-
|
|
4109
|
-
|
|
4110
|
-
|
|
4111
|
-
|
|
4420
|
+
ships: state.ships.map((ship) => {
|
|
4421
|
+
const resolvedShipModel = resolveShipModel(state, ship, shipModel);
|
|
4422
|
+
return {
|
|
4423
|
+
id: ship.id,
|
|
4424
|
+
modelKey: ship.modelKey ?? "brigantine",
|
|
4425
|
+
resolvedModelKey: resolvedShipModel?.name ?? ship.modelKey ?? "brigantine",
|
|
4426
|
+
x: Number(ship.position.x.toFixed(2)),
|
|
4427
|
+
y: Number(ship.position.y.toFixed(2)),
|
|
4428
|
+
z: Number(ship.position.z.toFixed(2)),
|
|
4429
|
+
vx: Number(ship.velocity.x.toFixed(2)),
|
|
4430
|
+
vz: Number(ship.velocity.z.toFixed(2)),
|
|
4431
|
+
massKg: Math.round(getShipMass(ship, resolvedShipModel)),
|
|
4432
|
+
lanterns: Array.isArray(ship.lanterns) ? ship.lanterns.length : 0,
|
|
4433
|
+
};
|
|
4434
|
+
}),
|
|
4435
|
+
assetCatalog: {
|
|
4436
|
+
mode: state.assetCatalog?.mode ?? "unknown",
|
|
4437
|
+
shipKeys: Object.keys(state.assetCatalog?.ships ?? {}).sort(),
|
|
4438
|
+
environmentKeys: Object.keys(state.assetCatalog?.environment ?? {}).sort(),
|
|
4439
|
+
fallbackReason: state.assetCatalog?.fallbackReason ?? null,
|
|
4440
|
+
requestedRealisticModels: state.showcaseRealisticModelsEnabled,
|
|
4441
|
+
},
|
|
4112
4442
|
shipPhysics: Object.fromEntries(
|
|
4113
4443
|
state.ships.map((ship) => [ship.id, resolveShipModel(state, ship, shipModel)?.physics ?? null])
|
|
4114
4444
|
),
|
|
@@ -4166,9 +4496,9 @@ export async function mountGpuShowcase(options = {}, featureFlags = null) {
|
|
|
4166
4496
|
},
|
|
4167
4497
|
featureAdapters
|
|
4168
4498
|
);
|
|
4169
|
-
const assetCatalog = await (
|
|
4170
|
-
|
|
4171
|
-
|
|
4499
|
+
const assetCatalog = await loadShowcaseAssetCatalogWithFallback({
|
|
4500
|
+
includeSecondaryShip: state.showcaseRealisticModelsEnabled,
|
|
4501
|
+
});
|
|
4172
4502
|
const shipModel = assetCatalog.ships[assetCatalog.primaryShipKey];
|
|
4173
4503
|
|
|
4174
4504
|
state.assetCatalog = assetCatalog;
|
|
@@ -4320,6 +4650,8 @@ function updatePhysicsSnapshot(state, shipModel, physicsFeatures) {
|
|
|
4320
4650
|
|
|
4321
4651
|
export {
|
|
4322
4652
|
advanceShowcaseClothSimulationState as __testOnlyAdvanceShowcaseClothSimulationState,
|
|
4653
|
+
buildClothSurface as __testOnlyBuildClothSurface,
|
|
4654
|
+
buildShorelineFoamSegments as __testOnlyBuildShorelineFoamSegments,
|
|
4323
4655
|
buildWaterBands as __testOnlyBuildWaterBands,
|
|
4324
4656
|
buildWaterMotionEffects as __testOnlyBuildWaterMotionEffects,
|
|
4325
4657
|
collectSceneLightSources as __testOnlyCollectSceneLightSources,
|