@plasius/gpu-shared 0.1.6 → 0.1.9
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 +54 -0
- package/README.md +6 -0
- package/dist/index.cjs +517 -126
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/dist/{showcase-runtime-JZIYGQAU.js → showcase-runtime-55OVDYWT.js} +517 -126
- package/dist/showcase-runtime-55OVDYWT.js.map +1 -0
- package/package.json +2 -2
- package/src/showcase-runtime.js +569 -151
- package/dist/showcase-runtime-JZIYGQAU.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -2220,6 +2220,9 @@ var init_dist2 = __esm({
|
|
|
2220
2220
|
});
|
|
2221
2221
|
|
|
2222
2222
|
// node_modules/@plasius/gpu-lighting/dist/index.js
|
|
2223
|
+
function createModuleBaseUrl(metaUrl) {
|
|
2224
|
+
return Reflect.construct(URL, [String(metaUrl)]);
|
|
2225
|
+
}
|
|
2223
2226
|
function buildTechnique(name, spec) {
|
|
2224
2227
|
const preludeUrl = new URL(`./techniques/${name}/${spec.prelude}`, baseUrl);
|
|
2225
2228
|
const jobs = Object.entries(spec.jobs).map(([key, file]) => {
|
|
@@ -2528,7 +2531,7 @@ var init_dist3 = __esm({
|
|
|
2528
2531
|
});
|
|
2529
2532
|
baseUrl = (() => {
|
|
2530
2533
|
if (typeof import_meta2.url !== "undefined") {
|
|
2531
|
-
return
|
|
2534
|
+
return createModuleBaseUrl(import_meta2.url);
|
|
2532
2535
|
}
|
|
2533
2536
|
if (typeof __filename !== "undefined" && typeof __require !== "undefined") {
|
|
2534
2537
|
const { pathToFileURL } = __require("url");
|
|
@@ -6339,6 +6342,10 @@ var init_browser = __esm({
|
|
|
6339
6342
|
// src/showcase-runtime.js
|
|
6340
6343
|
var showcase_runtime_exports = {};
|
|
6341
6344
|
__export(showcase_runtime_exports, {
|
|
6345
|
+
__testOnlyAdvanceShowcaseClothSimulationState: () => advanceShowcaseClothSimulationState,
|
|
6346
|
+
__testOnlyBuildWaterMotionEffects: () => buildWaterMotionEffects,
|
|
6347
|
+
__testOnlyCollectSceneLightSources: () => collectSceneLightSources,
|
|
6348
|
+
__testOnlyCreateShowcaseClothSimulationState: () => createShowcaseClothSimulationState,
|
|
6342
6349
|
mountGpuShowcase: () => mountGpuShowcase,
|
|
6343
6350
|
showcaseFocusModes: () => showcaseFocusModes
|
|
6344
6351
|
});
|
|
@@ -6914,6 +6921,250 @@ function normalizeColorOverride(color, fallback) {
|
|
|
6914
6921
|
function readVisualNumber(value, fallback) {
|
|
6915
6922
|
return typeof value === "number" && Number.isFinite(value) ? value : fallback;
|
|
6916
6923
|
}
|
|
6924
|
+
function resolveClothPresentation(state, meshDetail) {
|
|
6925
|
+
const clothPlan = createClothRepresentationPlan({
|
|
6926
|
+
garmentId: "shore-flag",
|
|
6927
|
+
kind: state.focus === "cloth" ? "flag" : clothGarmentKinds[0],
|
|
6928
|
+
profile: state.focus === "cloth" ? "cinematic" : clothProfileNames[0],
|
|
6929
|
+
supportsRayTracing: true,
|
|
6930
|
+
nearFieldMaxMeters: 18,
|
|
6931
|
+
midFieldMaxMeters: 55,
|
|
6932
|
+
farFieldMaxMeters: 180
|
|
6933
|
+
});
|
|
6934
|
+
const preset = CAMERA_PRESETS[state.focus] ?? CAMERA_PRESETS.integrated;
|
|
6935
|
+
const fallbackEye = state.camera.eye ? state.camera.eye : addVec3(
|
|
6936
|
+
state.camera.target,
|
|
6937
|
+
vec3(
|
|
6938
|
+
Math.sin(state.camera.yaw ?? preset.yaw) * Math.cos(state.camera.pitch ?? preset.pitch) * (state.camera.distance ?? preset.distance),
|
|
6939
|
+
Math.sin(state.camera.pitch ?? preset.pitch) * (state.camera.distance ?? preset.distance),
|
|
6940
|
+
Math.cos(state.camera.yaw ?? preset.yaw) * Math.cos(state.camera.pitch ?? preset.pitch) * (state.camera.distance ?? preset.distance)
|
|
6941
|
+
)
|
|
6942
|
+
);
|
|
6943
|
+
const cameraDistance = lengthVec3(subVec3(state.camera.target, fallbackEye));
|
|
6944
|
+
const band = selectClothRepresentationBand(cameraDistance, clothPlan.thresholds);
|
|
6945
|
+
const representation = clothPlan.representations.find((entry) => entry.band === band) ?? clothPlan.representations[0];
|
|
6946
|
+
return {
|
|
6947
|
+
clothPlan,
|
|
6948
|
+
band,
|
|
6949
|
+
continuity: representation.continuity,
|
|
6950
|
+
representation
|
|
6951
|
+
};
|
|
6952
|
+
}
|
|
6953
|
+
function getFlagRestPosition(rows, cols, row, column) {
|
|
6954
|
+
const u = cols <= 1 ? 0 : column / (cols - 1);
|
|
6955
|
+
const v = rows <= 1 ? 0 : row / (rows - 1);
|
|
6956
|
+
return vec3(
|
|
6957
|
+
FLAG_LAYOUT.origin.x + u * FLAG_LAYOUT.mastOffsetX,
|
|
6958
|
+
FLAG_LAYOUT.origin.y - FLAG_LAYOUT.height * v - u * u * 0.08,
|
|
6959
|
+
FLAG_LAYOUT.origin.z + FLAG_LAYOUT.width * u
|
|
6960
|
+
);
|
|
6961
|
+
}
|
|
6962
|
+
function buildClothConstraints(rows, cols, restPositions) {
|
|
6963
|
+
const constraints = [];
|
|
6964
|
+
const indexFor = (row, column) => row * cols + column;
|
|
6965
|
+
const pushConstraint = (a, b, stiffness) => {
|
|
6966
|
+
constraints.push(
|
|
6967
|
+
Object.freeze({
|
|
6968
|
+
a,
|
|
6969
|
+
b,
|
|
6970
|
+
restLength: lengthVec3(subVec3(restPositions[a], restPositions[b])),
|
|
6971
|
+
stiffness
|
|
6972
|
+
})
|
|
6973
|
+
);
|
|
6974
|
+
};
|
|
6975
|
+
for (let row = 0; row < rows; row += 1) {
|
|
6976
|
+
for (let column = 0; column < cols; column += 1) {
|
|
6977
|
+
const index = indexFor(row, column);
|
|
6978
|
+
if (column + 1 < cols) {
|
|
6979
|
+
pushConstraint(index, indexFor(row, column + 1), 0.92);
|
|
6980
|
+
}
|
|
6981
|
+
if (row + 1 < rows) {
|
|
6982
|
+
pushConstraint(index, indexFor(row + 1, column), 0.9);
|
|
6983
|
+
}
|
|
6984
|
+
if (column + 1 < cols && row + 1 < rows) {
|
|
6985
|
+
pushConstraint(index, indexFor(row + 1, column + 1), 0.66);
|
|
6986
|
+
}
|
|
6987
|
+
if (column - 1 >= 0 && row + 1 < rows) {
|
|
6988
|
+
pushConstraint(index, indexFor(row + 1, column - 1), 0.66);
|
|
6989
|
+
}
|
|
6990
|
+
if (column + 2 < cols) {
|
|
6991
|
+
pushConstraint(index, indexFor(row, column + 2), 0.22);
|
|
6992
|
+
}
|
|
6993
|
+
if (row + 2 < rows) {
|
|
6994
|
+
pushConstraint(index, indexFor(row + 2, column), 0.18);
|
|
6995
|
+
}
|
|
6996
|
+
}
|
|
6997
|
+
}
|
|
6998
|
+
return Object.freeze(constraints);
|
|
6999
|
+
}
|
|
7000
|
+
function createShowcaseClothSimulationState(options = {}) {
|
|
7001
|
+
const rows = Math.max(4, options.rows ?? 11);
|
|
7002
|
+
const cols = Math.max(4, options.cols ?? 16);
|
|
7003
|
+
const continuity = options.continuity ?? {
|
|
7004
|
+
broadMotionFloor: 0.72,
|
|
7005
|
+
wrinkleFloor: 0.56
|
|
7006
|
+
};
|
|
7007
|
+
const representation = options.representation ?? {
|
|
7008
|
+
mesh: {
|
|
7009
|
+
solverIterations: 6,
|
|
7010
|
+
wrinkleLayers: 2
|
|
7011
|
+
}
|
|
7012
|
+
};
|
|
7013
|
+
const restPositions = [];
|
|
7014
|
+
const positions = [];
|
|
7015
|
+
const previousPositions = [];
|
|
7016
|
+
const uvs = [];
|
|
7017
|
+
const phaseOffsets = [];
|
|
7018
|
+
const pinned = [];
|
|
7019
|
+
for (let row = 0; row < rows; row += 1) {
|
|
7020
|
+
for (let column = 0; column < cols; column += 1) {
|
|
7021
|
+
const index = row * cols + column;
|
|
7022
|
+
const u = cols <= 1 ? 0 : column / (cols - 1);
|
|
7023
|
+
const v = rows <= 1 ? 0 : row / (rows - 1);
|
|
7024
|
+
const rest = getFlagRestPosition(rows, cols, row, column);
|
|
7025
|
+
const preload = vec3(
|
|
7026
|
+
u * 0.04,
|
|
7027
|
+
Math.sin(v * Math.PI) * 0.02 * continuity.wrinkleFloor,
|
|
7028
|
+
-u * 0.12
|
|
7029
|
+
);
|
|
7030
|
+
const pinnedPoint = column === 0;
|
|
7031
|
+
restPositions.push(rest);
|
|
7032
|
+
positions.push(pinnedPoint ? vec3(rest.x, rest.y, rest.z) : addVec3(rest, preload));
|
|
7033
|
+
previousPositions.push(
|
|
7034
|
+
pinnedPoint ? vec3(rest.x, rest.y, rest.z) : addVec3(rest, scaleVec3(preload, 0.35))
|
|
7035
|
+
);
|
|
7036
|
+
uvs.push(Object.freeze({ u, v }));
|
|
7037
|
+
phaseOffsets.push(pseudoRandom(index + 17) * Math.PI * 2);
|
|
7038
|
+
pinned.push(pinnedPoint);
|
|
7039
|
+
}
|
|
7040
|
+
}
|
|
7041
|
+
return {
|
|
7042
|
+
rows,
|
|
7043
|
+
cols,
|
|
7044
|
+
continuity,
|
|
7045
|
+
representation,
|
|
7046
|
+
restPositions,
|
|
7047
|
+
positions,
|
|
7048
|
+
previousPositions,
|
|
7049
|
+
constraints: buildClothConstraints(rows, cols, restPositions),
|
|
7050
|
+
indices: Object.freeze(
|
|
7051
|
+
Array.from({ length: (rows - 1) * (cols - 1) * 6 }, (_, listIndex) => listIndex).map((_, listIndex, source) => {
|
|
7052
|
+
if (listIndex >= source.length) {
|
|
7053
|
+
return 0;
|
|
7054
|
+
}
|
|
7055
|
+
const quadIndex = Math.floor(listIndex / 6);
|
|
7056
|
+
const quadColumn = quadIndex % (cols - 1);
|
|
7057
|
+
const quadRow = Math.floor(quadIndex / (cols - 1));
|
|
7058
|
+
const base = quadRow * cols + quadColumn;
|
|
7059
|
+
return [base, base + 1, base + cols + 1, base, base + cols + 1, base + cols][listIndex % 6];
|
|
7060
|
+
})
|
|
7061
|
+
),
|
|
7062
|
+
uvs,
|
|
7063
|
+
phaseOffsets,
|
|
7064
|
+
pinned
|
|
7065
|
+
};
|
|
7066
|
+
}
|
|
7067
|
+
function resetPinnedClothPoints(clothState) {
|
|
7068
|
+
for (let index = 0; index < clothState.positions.length; index += 1) {
|
|
7069
|
+
if (!clothState.pinned[index]) {
|
|
7070
|
+
continue;
|
|
7071
|
+
}
|
|
7072
|
+
const anchor = clothState.restPositions[index];
|
|
7073
|
+
clothState.positions[index] = vec3(anchor.x, anchor.y, anchor.z);
|
|
7074
|
+
clothState.previousPositions[index] = vec3(anchor.x, anchor.y, anchor.z);
|
|
7075
|
+
}
|
|
7076
|
+
}
|
|
7077
|
+
function satisfyClothConstraint(clothState, constraint) {
|
|
7078
|
+
const a = clothState.positions[constraint.a];
|
|
7079
|
+
const b = clothState.positions[constraint.b];
|
|
7080
|
+
const delta = subVec3(b, a);
|
|
7081
|
+
const distance = lengthVec3(delta);
|
|
7082
|
+
if (distance <= 1e-4) {
|
|
7083
|
+
return;
|
|
7084
|
+
}
|
|
7085
|
+
const correctionScale = (distance - constraint.restLength) / distance * 0.5 * constraint.stiffness;
|
|
7086
|
+
const correction = scaleVec3(delta, correctionScale);
|
|
7087
|
+
if (!clothState.pinned[constraint.a]) {
|
|
7088
|
+
clothState.positions[constraint.a] = addVec3(a, correction);
|
|
7089
|
+
}
|
|
7090
|
+
if (!clothState.pinned[constraint.b]) {
|
|
7091
|
+
clothState.positions[constraint.b] = subVec3(b, correction);
|
|
7092
|
+
}
|
|
7093
|
+
}
|
|
7094
|
+
function advanceShowcaseClothSimulationState(clothState, options = {}) {
|
|
7095
|
+
const dt = clamp(options.dt ?? 1 / 60, 1 / 240, 1 / 18);
|
|
7096
|
+
const time = readVisualNumber(options.time, 0);
|
|
7097
|
+
const flagMotion = readVisualNumber(options.flagMotion, 0.92);
|
|
7098
|
+
const waveInfluence = readVisualNumber(options.waveInfluence, 0);
|
|
7099
|
+
const wrinkleLayers = Math.max(1, clothState.representation.mesh?.wrinkleLayers ?? 2);
|
|
7100
|
+
const solverIterations = clamp(
|
|
7101
|
+
Math.round(clothState.representation.mesh?.solverIterations ?? 6),
|
|
7102
|
+
2,
|
|
7103
|
+
10
|
|
7104
|
+
);
|
|
7105
|
+
for (let index = 0; index < clothState.positions.length; index += 1) {
|
|
7106
|
+
if (clothState.pinned[index]) {
|
|
7107
|
+
continue;
|
|
7108
|
+
}
|
|
7109
|
+
const current = clothState.positions[index];
|
|
7110
|
+
const previous = clothState.previousPositions[index];
|
|
7111
|
+
const { u, v } = clothState.uvs[index];
|
|
7112
|
+
const phase = clothState.phaseOffsets[index];
|
|
7113
|
+
const broadMotion = clothState.continuity.broadMotionFloor;
|
|
7114
|
+
const wrinkleMotion = clothState.continuity.wrinkleFloor;
|
|
7115
|
+
const gustPhase = time * 2.1 + phase + u * 4.4 + v * 2.3;
|
|
7116
|
+
const wrinklePhase = time * 5.3 + phase * 0.72 + u * 9.6 + v * 7.1;
|
|
7117
|
+
const windDirection = normalizeVec3(
|
|
7118
|
+
vec3(
|
|
7119
|
+
0.18 + Math.sin(gustPhase) * (0.12 + broadMotion * 0.09),
|
|
7120
|
+
Math.cos(time * 1.4 + phase + v * 4.8) * 0.06 * wrinkleMotion,
|
|
7121
|
+
1 + Math.sin(gustPhase * 0.74) * 0.18
|
|
7122
|
+
)
|
|
7123
|
+
);
|
|
7124
|
+
const windStrength = (1.6 + broadMotion * 1.25 + wrinkleLayers * 0.12) * flagMotion * (0.44 + u * 1.14);
|
|
7125
|
+
const wrinkleForce = vec3(
|
|
7126
|
+
Math.sin(wrinklePhase) * 0.22 * wrinkleMotion * flagMotion,
|
|
7127
|
+
Math.cos(wrinklePhase * 0.7) * 0.08 * wrinkleMotion,
|
|
7128
|
+
Math.cos(wrinklePhase) * 0.14 * broadMotion * flagMotion
|
|
7129
|
+
);
|
|
7130
|
+
const acceleration = addVec3(
|
|
7131
|
+
vec3(0, -0.48 - u * 0.08, 0),
|
|
7132
|
+
addVec3(
|
|
7133
|
+
scaleVec3(windDirection, windStrength),
|
|
7134
|
+
addVec3(
|
|
7135
|
+
wrinkleForce,
|
|
7136
|
+
vec3(waveInfluence * (0.04 + u * 0.08), 0, waveInfluence * 0.16)
|
|
7137
|
+
)
|
|
7138
|
+
)
|
|
7139
|
+
);
|
|
7140
|
+
const inertia = scaleVec3(subVec3(current, previous), 0.987);
|
|
7141
|
+
const next = addVec3(addVec3(current, inertia), scaleVec3(acceleration, dt * dt));
|
|
7142
|
+
clothState.previousPositions[index] = vec3(current.x, current.y, current.z);
|
|
7143
|
+
clothState.positions[index] = next;
|
|
7144
|
+
}
|
|
7145
|
+
resetPinnedClothPoints(clothState);
|
|
7146
|
+
for (let iteration = 0; iteration < solverIterations; iteration += 1) {
|
|
7147
|
+
for (const constraint of clothState.constraints) {
|
|
7148
|
+
satisfyClothConstraint(clothState, constraint);
|
|
7149
|
+
}
|
|
7150
|
+
resetPinnedClothPoints(clothState);
|
|
7151
|
+
}
|
|
7152
|
+
return clothState;
|
|
7153
|
+
}
|
|
7154
|
+
function ensureShowcaseClothState(state, meshDetail, clothPresentation) {
|
|
7155
|
+
if (!state.clothState || state.clothState.rows !== meshDetail.rows || state.clothState.cols !== meshDetail.cols) {
|
|
7156
|
+
state.clothState = createShowcaseClothSimulationState({
|
|
7157
|
+
rows: meshDetail.rows,
|
|
7158
|
+
cols: meshDetail.cols,
|
|
7159
|
+
continuity: clothPresentation.continuity,
|
|
7160
|
+
representation: clothPresentation.representation
|
|
7161
|
+
});
|
|
7162
|
+
} else {
|
|
7163
|
+
state.clothState.continuity = clothPresentation.continuity;
|
|
7164
|
+
state.clothState.representation = clothPresentation.representation;
|
|
7165
|
+
}
|
|
7166
|
+
return state.clothState;
|
|
7167
|
+
}
|
|
6917
7168
|
function resolveVisualConfig(nearLighting, lightingSnapshot, customVisuals = {}) {
|
|
6918
7169
|
const premiumShadows = nearLighting.primaryShadowSource === "ray-traced-primary";
|
|
6919
7170
|
const defaults = {
|
|
@@ -6997,58 +7248,17 @@ function resolveVisualConfig(nearLighting, lightingSnapshot, customVisuals = {})
|
|
|
6997
7248
|
};
|
|
6998
7249
|
}
|
|
6999
7250
|
function buildClothSurface(model, state, meshDetail, visuals) {
|
|
7000
|
-
const
|
|
7001
|
-
|
|
7002
|
-
kind: state.focus === "cloth" ? "flag" : clothGarmentKinds[0],
|
|
7003
|
-
profile: state.focus === "cloth" ? "cinematic" : clothProfileNames[0],
|
|
7004
|
-
supportsRayTracing: true,
|
|
7005
|
-
nearFieldMaxMeters: 18,
|
|
7006
|
-
midFieldMaxMeters: 55,
|
|
7007
|
-
farFieldMaxMeters: 180
|
|
7008
|
-
});
|
|
7009
|
-
const cameraDistance = lengthVec3(subVec3(state.camera.target, state.camera.eye ?? vec3(...CAMERA_PRESETS[state.focus].target)));
|
|
7010
|
-
const band = selectClothRepresentationBand(cameraDistance, clothPlan.thresholds);
|
|
7011
|
-
const representation = clothPlan.representations.find((entry) => entry.band === band) ?? clothPlan.representations[0];
|
|
7012
|
-
const continuity = createClothContinuityEnvelope({ garmentId: "shore-flag" });
|
|
7013
|
-
const cols = meshDetail.cols;
|
|
7014
|
-
const rows = meshDetail.rows;
|
|
7015
|
-
const origin = vec3(-3.5, 5.9, 2.4);
|
|
7016
|
-
const width = 4.8;
|
|
7017
|
-
const height = 2.7;
|
|
7018
|
-
const positions = [];
|
|
7019
|
-
const indices = [];
|
|
7020
|
-
const time = state.time;
|
|
7021
|
-
for (let row = 0; row < rows; row += 1) {
|
|
7022
|
-
for (let column = 0; column < cols; column += 1) {
|
|
7023
|
-
const u = column / (cols - 1);
|
|
7024
|
-
const v = row / (rows - 1);
|
|
7025
|
-
const gust = Math.sin(time * 1.9 + v * 3.2 + u * 2.1) * continuity.broadMotionFloor * visuals.flagMotion;
|
|
7026
|
-
const wrinkle = Math.sin(time * 4.4 + u * 9.2 + v * 5.6) * continuity.wrinkleFloor * 0.22 * Math.max(0.55, visuals.flagMotion);
|
|
7027
|
-
const x = origin.x + u * 1.8 + gust * 0.55 * (u * 0.9);
|
|
7028
|
-
const y = origin.y - height * v + wrinkle * 0.2;
|
|
7029
|
-
const z = origin.z + width * u + gust * 0.72 * (u * 0.85);
|
|
7030
|
-
const flap = Math.cos(time * 2.7 + u * 7.4 + v * 3.8) * continuity.broadMotionFloor * 0.28 * visuals.flagMotion;
|
|
7031
|
-
positions.push(vec3(x + flap, y, z));
|
|
7032
|
-
}
|
|
7033
|
-
}
|
|
7034
|
-
for (let row = 0; row < rows - 1; row += 1) {
|
|
7035
|
-
for (let column = 0; column < cols - 1; column += 1) {
|
|
7036
|
-
const a = row * cols + column;
|
|
7037
|
-
const b = a + 1;
|
|
7038
|
-
const c = a + cols + 1;
|
|
7039
|
-
const d = a + cols;
|
|
7040
|
-
indices.push(a, b, c, a, c, d);
|
|
7041
|
-
}
|
|
7042
|
-
}
|
|
7251
|
+
const clothPresentation = resolveClothPresentation(state, meshDetail);
|
|
7252
|
+
const clothState = ensureShowcaseClothState(state, meshDetail, clothPresentation);
|
|
7043
7253
|
return {
|
|
7044
|
-
clothPlan,
|
|
7045
|
-
band,
|
|
7046
|
-
representation,
|
|
7047
|
-
continuity,
|
|
7254
|
+
clothPlan: clothPresentation.clothPlan,
|
|
7255
|
+
band: clothPresentation.band,
|
|
7256
|
+
representation: clothPresentation.representation,
|
|
7257
|
+
continuity: clothPresentation.continuity,
|
|
7048
7258
|
color: visuals.flagColor,
|
|
7049
|
-
positions,
|
|
7050
|
-
indices,
|
|
7051
|
-
grid: { rows, cols }
|
|
7259
|
+
positions: clothState.positions.map((point) => vec3(point.x, point.y, point.z)),
|
|
7260
|
+
indices: clothState.indices,
|
|
7261
|
+
grid: { rows: clothState.rows, cols: clothState.cols }
|
|
7052
7262
|
};
|
|
7053
7263
|
}
|
|
7054
7264
|
function resolveWaveDirection(state) {
|
|
@@ -7112,6 +7322,59 @@ function sampleWave(state, x, z, time) {
|
|
|
7112
7322
|
const base = Math.sin(along * 0.22 - time * 1.12 * phaseSpeed) * 0.42 + Math.cos(along * 0.11 + cross * 0.07 - time * 0.78 * phaseSpeed) * 0.26 + Math.sin(cross * 0.19 - time * 1.34 * phaseSpeed) * 0.16;
|
|
7113
7323
|
return base * amplitude + sampleShipWake(state, x, z, time) + sampleWaveImpulses(state, x, z, time);
|
|
7114
7324
|
}
|
|
7325
|
+
function buildWaterMotionEffects(state) {
|
|
7326
|
+
const wakeTrails = [];
|
|
7327
|
+
const rippleRings = state.waveImpulses.map((impulse) => {
|
|
7328
|
+
const radius = impulse.radius + (1 - impulse.life) * 4.8;
|
|
7329
|
+
return Object.freeze({
|
|
7330
|
+
center: vec3(
|
|
7331
|
+
impulse.x,
|
|
7332
|
+
sampleWave(state, impulse.x, impulse.z, state.time) * 0.24 + 0.06,
|
|
7333
|
+
impulse.z
|
|
7334
|
+
),
|
|
7335
|
+
radius,
|
|
7336
|
+
opacity: clamp(impulse.life * 0.28, 0.08, 0.3)
|
|
7337
|
+
});
|
|
7338
|
+
});
|
|
7339
|
+
for (const ship of state.ships) {
|
|
7340
|
+
const speed = Math.hypot(ship.velocity.x, ship.velocity.z);
|
|
7341
|
+
if (speed <= 0.18) {
|
|
7342
|
+
continue;
|
|
7343
|
+
}
|
|
7344
|
+
const direction = normalizeVec3(vec3(ship.velocity.x, 0, ship.velocity.z));
|
|
7345
|
+
const behind = scaleVec3(direction, -1);
|
|
7346
|
+
const lateral = vec3(-direction.z, 0, direction.x);
|
|
7347
|
+
const points = [];
|
|
7348
|
+
for (let sampleIndex = 0; sampleIndex < 6; sampleIndex += 1) {
|
|
7349
|
+
const along = 1 + sampleIndex * 1.45;
|
|
7350
|
+
const lateralOffset = Math.sin(state.time * 1.2 + sampleIndex * 0.8 + readVisualNumber(ship.wanderPhase, 0)) * 0.12;
|
|
7351
|
+
const worldPoint = addVec3(
|
|
7352
|
+
ship.position,
|
|
7353
|
+
addVec3(scaleVec3(behind, along), scaleVec3(lateral, lateralOffset))
|
|
7354
|
+
);
|
|
7355
|
+
points.push(
|
|
7356
|
+
Object.freeze({
|
|
7357
|
+
center: vec3(
|
|
7358
|
+
worldPoint.x,
|
|
7359
|
+
sampleWave(state, worldPoint.x, worldPoint.z, state.time) * 0.24 + 0.04,
|
|
7360
|
+
worldPoint.z
|
|
7361
|
+
),
|
|
7362
|
+
width: 0.34 + sampleIndex * 0.13
|
|
7363
|
+
})
|
|
7364
|
+
);
|
|
7365
|
+
}
|
|
7366
|
+
wakeTrails.push(
|
|
7367
|
+
Object.freeze({
|
|
7368
|
+
opacity: clamp(0.18 + speed * 0.09, 0.22, 0.46),
|
|
7369
|
+
points: Object.freeze(points)
|
|
7370
|
+
})
|
|
7371
|
+
);
|
|
7372
|
+
}
|
|
7373
|
+
return Object.freeze({
|
|
7374
|
+
wakeTrails: Object.freeze(wakeTrails),
|
|
7375
|
+
rippleRings: Object.freeze(rippleRings)
|
|
7376
|
+
});
|
|
7377
|
+
}
|
|
7115
7378
|
function buildWaterBands(state, fluidDetail, visuals) {
|
|
7116
7379
|
const fluidPlan = createFluidRepresentationPlan({
|
|
7117
7380
|
fluidBodyId: "harbor",
|
|
@@ -7264,6 +7527,7 @@ function createSceneState(options) {
|
|
|
7264
7527
|
contactCount: 0,
|
|
7265
7528
|
collisionCount: 0,
|
|
7266
7529
|
collisionFlash: 0,
|
|
7530
|
+
clothState: null,
|
|
7267
7531
|
physics: {
|
|
7268
7532
|
profile: physicsProfile,
|
|
7269
7533
|
plan: physicsPlan,
|
|
@@ -7899,16 +8163,73 @@ function renderFlagShadow(ctx, cloth, camera, viewport, lightDir, shadowStrength
|
|
|
7899
8163
|
blur: 12 + shadowStrength * 20
|
|
7900
8164
|
});
|
|
7901
8165
|
}
|
|
7902
|
-
function
|
|
7903
|
-
const
|
|
8166
|
+
function collectSceneLightSources(state, visuals) {
|
|
8167
|
+
const directLights = [];
|
|
8168
|
+
const reflectionLights = [];
|
|
8169
|
+
const pushLight = (point, glowScale, reflectionStrength, coreColor, glowColor) => {
|
|
8170
|
+
directLights.push(
|
|
8171
|
+
Object.freeze({
|
|
8172
|
+
pass: "direct-glow",
|
|
8173
|
+
point,
|
|
8174
|
+
coreColor,
|
|
8175
|
+
glowColor,
|
|
8176
|
+
glowScale
|
|
8177
|
+
})
|
|
8178
|
+
);
|
|
8179
|
+
if (reflectionStrength > 0) {
|
|
8180
|
+
reflectionLights.push(
|
|
8181
|
+
Object.freeze({
|
|
8182
|
+
pass: "water-reflection",
|
|
8183
|
+
point,
|
|
8184
|
+
coreColor,
|
|
8185
|
+
glowColor,
|
|
8186
|
+
glowScale,
|
|
8187
|
+
reflectionStrength
|
|
8188
|
+
})
|
|
8189
|
+
);
|
|
8190
|
+
}
|
|
8191
|
+
};
|
|
8192
|
+
for (const torch of HARBOR_TORCHES) {
|
|
8193
|
+
pushLight(
|
|
8194
|
+
vec3(torch.x, torch.y, torch.z),
|
|
8195
|
+
torch.glow,
|
|
8196
|
+
visuals.lanternReflectionStrength * 0.55,
|
|
8197
|
+
visuals.torchCore,
|
|
8198
|
+
visuals.torchGlow
|
|
8199
|
+
);
|
|
8200
|
+
}
|
|
8201
|
+
for (const ship of state.ships) {
|
|
8202
|
+
const lanterns = Array.isArray(ship.lanterns) ? ship.lanterns : SHIP_LANTERNS;
|
|
8203
|
+
const strength = readVisualNumber(ship.lanternStrength, 1);
|
|
8204
|
+
for (const lantern of lanterns) {
|
|
8205
|
+
const point = transformPoint(
|
|
8206
|
+
vec3(lantern.x, lantern.y, lantern.z),
|
|
8207
|
+
{ position: ship.position, rotationY: ship.rotationY, scale: SHIP_SCALE }
|
|
8208
|
+
);
|
|
8209
|
+
pushLight(
|
|
8210
|
+
point,
|
|
8211
|
+
lantern.glow * strength,
|
|
8212
|
+
visuals.lanternReflectionStrength,
|
|
8213
|
+
visuals.lanternCore,
|
|
8214
|
+
visuals.lanternGlow
|
|
8215
|
+
);
|
|
8216
|
+
}
|
|
8217
|
+
}
|
|
8218
|
+
return Object.freeze({
|
|
8219
|
+
directLights: Object.freeze(directLights),
|
|
8220
|
+
reflectionLights: Object.freeze(reflectionLights)
|
|
8221
|
+
});
|
|
8222
|
+
}
|
|
8223
|
+
function renderDirectLightGlow(ctx, source, camera, viewport) {
|
|
8224
|
+
const projected = projectPoint(source.point, camera, viewport);
|
|
7904
8225
|
if (!projected) {
|
|
7905
8226
|
return;
|
|
7906
8227
|
}
|
|
7907
|
-
const radius = clamp(1 / projected.depth * 420 * glowScale, 4, 34);
|
|
8228
|
+
const radius = clamp(1 / projected.depth * 420 * source.glowScale, 4, 34);
|
|
7908
8229
|
const halo = ctx.createRadialGradient(projected.x, projected.y, radius * 0.12, projected.x, projected.y, radius);
|
|
7909
|
-
halo.addColorStop(0, colorToRgba(coreColor, 0.98));
|
|
7910
|
-
halo.addColorStop(0.5, colorToRgba(glowColor, 0.42));
|
|
7911
|
-
halo.addColorStop(1, colorToRgba(glowColor, 0));
|
|
8230
|
+
halo.addColorStop(0, colorToRgba(source.coreColor, 0.98));
|
|
8231
|
+
halo.addColorStop(0.5, colorToRgba(source.glowColor, 0.42));
|
|
8232
|
+
halo.addColorStop(1, colorToRgba(source.glowColor, 0));
|
|
7912
8233
|
ctx.save();
|
|
7913
8234
|
ctx.globalCompositeOperation = "screen";
|
|
7914
8235
|
ctx.fillStyle = halo;
|
|
@@ -7916,79 +8237,119 @@ function renderGlowLight(ctx, point, camera, viewport, coreColor, glowColor, glo
|
|
|
7916
8237
|
ctx.arc(projected.x, projected.y, radius, 0, Math.PI * 2);
|
|
7917
8238
|
ctx.fill();
|
|
7918
8239
|
ctx.restore();
|
|
7919
|
-
ctx.fillStyle = colorToRgba(coreColor, 0.98);
|
|
8240
|
+
ctx.fillStyle = colorToRgba(source.coreColor, 0.98);
|
|
7920
8241
|
ctx.beginPath();
|
|
7921
8242
|
ctx.arc(projected.x, projected.y, Math.max(1.2, radius * 0.16), 0, Math.PI * 2);
|
|
7922
8243
|
ctx.fill();
|
|
7923
|
-
|
|
7924
|
-
|
|
7925
|
-
|
|
7926
|
-
|
|
7927
|
-
|
|
7928
|
-
|
|
7929
|
-
|
|
7930
|
-
|
|
7931
|
-
|
|
7932
|
-
|
|
7933
|
-
|
|
7934
|
-
|
|
7935
|
-
|
|
7936
|
-
|
|
7937
|
-
|
|
7938
|
-
|
|
7939
|
-
|
|
7940
|
-
|
|
7941
|
-
|
|
8244
|
+
}
|
|
8245
|
+
function renderWaterLightReflection(ctx, source, state, camera, viewport) {
|
|
8246
|
+
const projected = projectPoint(source.point, camera, viewport);
|
|
8247
|
+
if (!projected) {
|
|
8248
|
+
return;
|
|
8249
|
+
}
|
|
8250
|
+
const radius = clamp(1 / projected.depth * 420 * source.glowScale, 4, 34);
|
|
8251
|
+
const waterline = sampleWave(state, source.point.x, source.point.z, state.time) * 0.22;
|
|
8252
|
+
const reflectedPoint = vec3(
|
|
8253
|
+
source.point.x,
|
|
8254
|
+
waterline - (source.point.y - waterline) * 0.58,
|
|
8255
|
+
source.point.z + 0.08
|
|
8256
|
+
);
|
|
8257
|
+
const reflected = projectPoint(reflectedPoint, camera, viewport);
|
|
8258
|
+
if (!reflected) {
|
|
8259
|
+
return;
|
|
8260
|
+
}
|
|
8261
|
+
const reflectionRadius = radius * 0.72;
|
|
8262
|
+
const glow = ctx.createRadialGradient(
|
|
8263
|
+
reflected.x,
|
|
8264
|
+
reflected.y,
|
|
8265
|
+
reflectionRadius * 0.1,
|
|
8266
|
+
reflected.x,
|
|
8267
|
+
reflected.y,
|
|
8268
|
+
reflectionRadius
|
|
8269
|
+
);
|
|
8270
|
+
glow.addColorStop(0, colorToRgba(source.coreColor, source.reflectionStrength * 0.34));
|
|
8271
|
+
glow.addColorStop(1, colorToRgba(source.glowColor, 0));
|
|
8272
|
+
ctx.save();
|
|
8273
|
+
ctx.globalCompositeOperation = "screen";
|
|
8274
|
+
ctx.fillStyle = glow;
|
|
8275
|
+
ctx.beginPath();
|
|
8276
|
+
ctx.ellipse(
|
|
8277
|
+
reflected.x,
|
|
8278
|
+
reflected.y,
|
|
8279
|
+
reflectionRadius * 0.34,
|
|
8280
|
+
reflectionRadius,
|
|
8281
|
+
0,
|
|
8282
|
+
0,
|
|
8283
|
+
Math.PI * 2
|
|
8284
|
+
);
|
|
8285
|
+
ctx.fill();
|
|
8286
|
+
ctx.restore();
|
|
8287
|
+
}
|
|
8288
|
+
function renderWaterMotionEffects(ctx, effects, camera, viewport) {
|
|
8289
|
+
ctx.save();
|
|
8290
|
+
ctx.globalCompositeOperation = "screen";
|
|
8291
|
+
for (const wake of effects.wakeTrails) {
|
|
8292
|
+
const projected = wake.points.map((point) => ({
|
|
8293
|
+
projected: projectPoint(point.center, camera, viewport),
|
|
8294
|
+
width: point.width
|
|
8295
|
+
})).filter((entry) => entry.projected);
|
|
8296
|
+
if (projected.length < 2) {
|
|
8297
|
+
continue;
|
|
8298
|
+
}
|
|
8299
|
+
const averageDepth = projected.reduce((total, entry) => total + entry.projected.depth, 0) / projected.length;
|
|
8300
|
+
const averageWidth = projected.reduce((total, entry) => total + entry.width, 0) / projected.length;
|
|
8301
|
+
const baseWidth = clamp(averageWidth / Math.max(0.25, averageDepth) * 180, 1.6, 5.4);
|
|
8302
|
+
ctx.strokeStyle = `rgba(146, 194, 236, ${wake.opacity * 0.52})`;
|
|
8303
|
+
ctx.lineWidth = baseWidth * 1.9;
|
|
8304
|
+
ctx.lineCap = "round";
|
|
8305
|
+
ctx.lineJoin = "round";
|
|
8306
|
+
ctx.beginPath();
|
|
8307
|
+
ctx.moveTo(projected[0].projected.x, projected[0].projected.y);
|
|
8308
|
+
for (let index = 1; index < projected.length; index += 1) {
|
|
8309
|
+
ctx.lineTo(projected[index].projected.x, projected[index].projected.y);
|
|
8310
|
+
}
|
|
8311
|
+
ctx.stroke();
|
|
8312
|
+
ctx.strokeStyle = `rgba(234, 247, 255, ${wake.opacity})`;
|
|
8313
|
+
ctx.lineWidth = baseWidth;
|
|
8314
|
+
ctx.lineCap = "round";
|
|
8315
|
+
ctx.lineJoin = "round";
|
|
8316
|
+
ctx.beginPath();
|
|
8317
|
+
ctx.moveTo(projected[0].projected.x, projected[0].projected.y);
|
|
8318
|
+
for (let index = 1; index < projected.length; index += 1) {
|
|
8319
|
+
ctx.lineTo(projected[index].projected.x, projected[index].projected.y);
|
|
8320
|
+
}
|
|
8321
|
+
ctx.stroke();
|
|
8322
|
+
for (const entry of projected.slice(1, 5)) {
|
|
8323
|
+
ctx.fillStyle = `rgba(239, 248, 255, ${wake.opacity * 0.76})`;
|
|
7942
8324
|
ctx.beginPath();
|
|
7943
8325
|
ctx.ellipse(
|
|
7944
|
-
|
|
7945
|
-
|
|
7946
|
-
|
|
7947
|
-
|
|
8326
|
+
entry.projected.x,
|
|
8327
|
+
entry.projected.y,
|
|
8328
|
+
baseWidth * 0.72,
|
|
8329
|
+
baseWidth * 0.44,
|
|
7948
8330
|
0,
|
|
7949
8331
|
0,
|
|
7950
8332
|
Math.PI * 2
|
|
7951
8333
|
);
|
|
7952
8334
|
ctx.fill();
|
|
7953
|
-
ctx.restore();
|
|
7954
8335
|
}
|
|
7955
8336
|
}
|
|
7956
|
-
|
|
7957
|
-
|
|
7958
|
-
|
|
7959
|
-
|
|
7960
|
-
|
|
7961
|
-
|
|
7962
|
-
|
|
7963
|
-
|
|
7964
|
-
);
|
|
7965
|
-
|
|
7966
|
-
|
|
7967
|
-
|
|
7968
|
-
|
|
7969
|
-
|
|
7970
|
-
visuals.lanternCore,
|
|
7971
|
-
visuals.lanternGlow,
|
|
7972
|
-
lantern.glow * strength,
|
|
7973
|
-
visuals.lanternReflectionStrength,
|
|
7974
|
-
state
|
|
7975
|
-
);
|
|
7976
|
-
}
|
|
7977
|
-
}
|
|
7978
|
-
function renderHarborTorches(ctx, state, camera, viewport, visuals) {
|
|
7979
|
-
for (const torch of HARBOR_TORCHES) {
|
|
7980
|
-
renderGlowLight(
|
|
7981
|
-
ctx,
|
|
7982
|
-
vec3(torch.x, torch.y, torch.z),
|
|
7983
|
-
camera,
|
|
7984
|
-
viewport,
|
|
7985
|
-
visuals.torchCore,
|
|
7986
|
-
visuals.torchGlow,
|
|
7987
|
-
torch.glow,
|
|
7988
|
-
visuals.lanternReflectionStrength * 0.55,
|
|
7989
|
-
state
|
|
7990
|
-
);
|
|
8337
|
+
for (const ring of effects.rippleRings) {
|
|
8338
|
+
const center = projectPoint(ring.center, camera, viewport);
|
|
8339
|
+
const xAxis = projectPoint(addVec3(ring.center, vec3(ring.radius, 0, 0)), camera, viewport);
|
|
8340
|
+
const zAxis = projectPoint(addVec3(ring.center, vec3(0, 0, ring.radius)), camera, viewport);
|
|
8341
|
+
if (!center || !xAxis || !zAxis) {
|
|
8342
|
+
continue;
|
|
8343
|
+
}
|
|
8344
|
+
const radiusX = Math.hypot(xAxis.x - center.x, xAxis.y - center.y);
|
|
8345
|
+
const radiusY = Math.hypot(zAxis.x - center.x, zAxis.y - center.y);
|
|
8346
|
+
ctx.strokeStyle = `rgba(216, 235, 255, ${ring.opacity})`;
|
|
8347
|
+
ctx.lineWidth = clamp((radiusX + radiusY) * 0.02, 1, 3.1);
|
|
8348
|
+
ctx.beginPath();
|
|
8349
|
+
ctx.ellipse(center.x, center.y, radiusX, radiusY, 0, 0, Math.PI * 2);
|
|
8350
|
+
ctx.stroke();
|
|
7991
8351
|
}
|
|
8352
|
+
ctx.restore();
|
|
7992
8353
|
}
|
|
7993
8354
|
function renderScene(ctx, canvas, state, shipModel, dom) {
|
|
7994
8355
|
const viewport = { width: canvas.width, height: canvas.height };
|
|
@@ -8018,8 +8379,8 @@ function renderScene(ctx, canvas, state, shipModel, dom) {
|
|
|
8018
8379
|
shadowStrength,
|
|
8019
8380
|
visuals
|
|
8020
8381
|
);
|
|
8021
|
-
const
|
|
8022
|
-
|
|
8382
|
+
const waterTriangles = [];
|
|
8383
|
+
const sceneTriangles = [];
|
|
8023
8384
|
const water = buildWaterBands(
|
|
8024
8385
|
state,
|
|
8025
8386
|
state.fluidDetail.getSnapshot().currentLevel.config,
|
|
@@ -8036,7 +8397,7 @@ function renderScene(ctx, canvas, state, shipModel, dom) {
|
|
|
8036
8397
|
if (projected.some((value) => value === null)) {
|
|
8037
8398
|
continue;
|
|
8038
8399
|
}
|
|
8039
|
-
|
|
8400
|
+
waterTriangles.push({
|
|
8040
8401
|
points: projected,
|
|
8041
8402
|
depth: (projected[0].depth + projected[1].depth + projected[2].depth) / 3,
|
|
8042
8403
|
worldCenter: scaleVec3(addVec3(addVec3(a, b), c), 1 / 3),
|
|
@@ -8046,6 +8407,9 @@ function renderScene(ctx, canvas, state, shipModel, dom) {
|
|
|
8046
8407
|
});
|
|
8047
8408
|
}
|
|
8048
8409
|
}
|
|
8410
|
+
const waterMotionEffects = buildWaterMotionEffects(state);
|
|
8411
|
+
const lightSources = collectSceneLightSources(state, visuals);
|
|
8412
|
+
pushHarborGeometry(camera, viewport, sceneTriangles, visuals);
|
|
8049
8413
|
const cloth = buildClothSurface(
|
|
8050
8414
|
state,
|
|
8051
8415
|
state,
|
|
@@ -8061,7 +8425,7 @@ function renderScene(ctx, canvas, state, shipModel, dom) {
|
|
|
8061
8425
|
if (projected.some((value) => value === null)) {
|
|
8062
8426
|
continue;
|
|
8063
8427
|
}
|
|
8064
|
-
|
|
8428
|
+
sceneTriangles.push({
|
|
8065
8429
|
points: projected,
|
|
8066
8430
|
depth: (projected[0].depth + projected[1].depth + projected[2].depth) / 3,
|
|
8067
8431
|
worldCenter: scaleVec3(addVec3(addVec3(a, b), c), 1 / 3),
|
|
@@ -8077,22 +8441,28 @@ function renderScene(ctx, canvas, state, shipModel, dom) {
|
|
|
8077
8441
|
ship.tint,
|
|
8078
8442
|
camera,
|
|
8079
8443
|
viewport,
|
|
8080
|
-
|
|
8444
|
+
sceneTriangles,
|
|
8081
8445
|
nearLighting.rtParticipation.directShadows === "premium" ? 0.08 : 0.02
|
|
8082
8446
|
);
|
|
8083
8447
|
}
|
|
8448
|
+
drawTriangles(ctx, waterTriangles, lightDir, reflectionStrength, camera, shadowStrength);
|
|
8084
8449
|
for (const ship of state.ships) {
|
|
8085
8450
|
renderShipShadow(ctx, shipModel, ship, state, camera, viewport, lightDir, shadowStrength);
|
|
8086
8451
|
}
|
|
8087
8452
|
renderFlagShadow(ctx, cloth, camera, viewport, lightDir, shadowStrength);
|
|
8088
|
-
|
|
8453
|
+
for (const source of lightSources.reflectionLights) {
|
|
8454
|
+
renderWaterLightReflection(ctx, source, state, camera, viewport);
|
|
8455
|
+
}
|
|
8456
|
+
renderWaterMotionEffects(ctx, waterMotionEffects, camera, viewport);
|
|
8089
8457
|
renderWaterHighlights(ctx, water.bandMeshes, camera, viewport);
|
|
8090
|
-
|
|
8458
|
+
drawTriangles(ctx, sceneTriangles, lightDir, reflectionStrength, camera, shadowStrength);
|
|
8091
8459
|
renderFlagPole(ctx, camera, viewport);
|
|
8092
8460
|
renderClothAccent(ctx, cloth, camera, viewport);
|
|
8461
|
+
for (const source of lightSources.directLights) {
|
|
8462
|
+
renderDirectLightGlow(ctx, source, camera, viewport);
|
|
8463
|
+
}
|
|
8093
8464
|
for (const ship of state.ships) {
|
|
8094
8465
|
renderShipRigging(ctx, ship, camera, viewport);
|
|
8095
|
-
renderShipLanterns(ctx, ship, state, camera, viewport, visuals);
|
|
8096
8466
|
}
|
|
8097
8467
|
renderSprays(ctx, state.sprays, camera, viewport);
|
|
8098
8468
|
const debugSnapshot = state.debugSession.getSnapshot();
|
|
@@ -8154,6 +8524,21 @@ function updateSceneState(state, dt, shipModel) {
|
|
|
8154
8524
|
updateShips(state, dt, shipModel);
|
|
8155
8525
|
updateWaveImpulses(state, dt);
|
|
8156
8526
|
updateSpray(state, dt);
|
|
8527
|
+
const clothPresentation = resolveClothPresentation(
|
|
8528
|
+
state,
|
|
8529
|
+
state.clothDetail.getSnapshot().currentLevel.config
|
|
8530
|
+
);
|
|
8531
|
+
const clothState = ensureShowcaseClothState(
|
|
8532
|
+
state,
|
|
8533
|
+
state.clothDetail.getSnapshot().currentLevel.config,
|
|
8534
|
+
clothPresentation
|
|
8535
|
+
);
|
|
8536
|
+
advanceShowcaseClothSimulationState(clothState, {
|
|
8537
|
+
dt,
|
|
8538
|
+
time: state.time,
|
|
8539
|
+
flagMotion: readVisualNumber(state.demoVisuals?.flagMotion, 0.92),
|
|
8540
|
+
waveInfluence: sampleWave(state, FLAG_LAYOUT.origin.x + FLAG_LAYOUT.width * 0.55, FLAG_LAYOUT.origin.z + FLAG_LAYOUT.width * 0.48, state.time)
|
|
8541
|
+
});
|
|
8157
8542
|
updatePhysicsSnapshot(state, shipModel);
|
|
8158
8543
|
}
|
|
8159
8544
|
function syncTextState(state, shipModel) {
|
|
@@ -8320,7 +8705,7 @@ function updatePhysicsSnapshot(state, shipModel) {
|
|
|
8320
8705
|
}
|
|
8321
8706
|
});
|
|
8322
8707
|
}
|
|
8323
|
-
var STYLE_ID, ROOT_CLASS, DEFAULT_TITLE, DEFAULT_SUBTITLE, SHIP_SCALE, HARBOR_BOUNDS, CAMERA_PRESETS, showcaseFocusModes, SCENE_NOTES, UNIT_BOX_MESH, SHIP_LANTERNS, HARBOR_TORCHES;
|
|
8708
|
+
var STYLE_ID, ROOT_CLASS, DEFAULT_TITLE, DEFAULT_SUBTITLE, SHIP_SCALE, HARBOR_BOUNDS, CAMERA_PRESETS, showcaseFocusModes, SCENE_NOTES, UNIT_BOX_MESH, SHIP_LANTERNS, HARBOR_TORCHES, FLAG_LAYOUT;
|
|
8324
8709
|
var init_showcase_runtime = __esm({
|
|
8325
8710
|
"src/showcase-runtime.js"() {
|
|
8326
8711
|
init_dist();
|
|
@@ -8435,6 +8820,12 @@ var init_showcase_runtime = __esm({
|
|
|
8435
8820
|
Object.freeze({ x: -8.6, y: 2.48, z: -0.72, glow: 1 }),
|
|
8436
8821
|
Object.freeze({ x: -10.4, y: 1.28, z: 0.82, glow: 0.92 })
|
|
8437
8822
|
]);
|
|
8823
|
+
FLAG_LAYOUT = Object.freeze({
|
|
8824
|
+
origin: Object.freeze({ x: -3.5, y: 5.9, z: 2.4 }),
|
|
8825
|
+
width: 4.8,
|
|
8826
|
+
height: 2.7,
|
|
8827
|
+
mastOffsetX: 1.8
|
|
8828
|
+
});
|
|
8438
8829
|
}
|
|
8439
8830
|
});
|
|
8440
8831
|
|