@plasius/gpu-shared 0.1.7 → 0.1.10
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 +52 -0
- package/README.md +6 -0
- package/dist/index.cjs +513 -125
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/dist/{showcase-runtime-AIPDRK7G.js → showcase-runtime-55OVDYWT.js} +513 -125
- package/dist/{showcase-runtime-AIPDRK7G.js.map → showcase-runtime-55OVDYWT.js.map} +1 -1
- package/package.json +11 -11
- package/src/showcase-runtime.js +569 -151
package/dist/index.cjs
CHANGED
|
@@ -6342,6 +6342,10 @@ var init_browser = __esm({
|
|
|
6342
6342
|
// src/showcase-runtime.js
|
|
6343
6343
|
var showcase_runtime_exports = {};
|
|
6344
6344
|
__export(showcase_runtime_exports, {
|
|
6345
|
+
__testOnlyAdvanceShowcaseClothSimulationState: () => advanceShowcaseClothSimulationState,
|
|
6346
|
+
__testOnlyBuildWaterMotionEffects: () => buildWaterMotionEffects,
|
|
6347
|
+
__testOnlyCollectSceneLightSources: () => collectSceneLightSources,
|
|
6348
|
+
__testOnlyCreateShowcaseClothSimulationState: () => createShowcaseClothSimulationState,
|
|
6345
6349
|
mountGpuShowcase: () => mountGpuShowcase,
|
|
6346
6350
|
showcaseFocusModes: () => showcaseFocusModes
|
|
6347
6351
|
});
|
|
@@ -6917,6 +6921,250 @@ function normalizeColorOverride(color, fallback) {
|
|
|
6917
6921
|
function readVisualNumber(value, fallback) {
|
|
6918
6922
|
return typeof value === "number" && Number.isFinite(value) ? value : fallback;
|
|
6919
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
|
+
}
|
|
6920
7168
|
function resolveVisualConfig(nearLighting, lightingSnapshot, customVisuals = {}) {
|
|
6921
7169
|
const premiumShadows = nearLighting.primaryShadowSource === "ray-traced-primary";
|
|
6922
7170
|
const defaults = {
|
|
@@ -7000,58 +7248,17 @@ function resolveVisualConfig(nearLighting, lightingSnapshot, customVisuals = {})
|
|
|
7000
7248
|
};
|
|
7001
7249
|
}
|
|
7002
7250
|
function buildClothSurface(model, state, meshDetail, visuals) {
|
|
7003
|
-
const
|
|
7004
|
-
|
|
7005
|
-
kind: state.focus === "cloth" ? "flag" : clothGarmentKinds[0],
|
|
7006
|
-
profile: state.focus === "cloth" ? "cinematic" : clothProfileNames[0],
|
|
7007
|
-
supportsRayTracing: true,
|
|
7008
|
-
nearFieldMaxMeters: 18,
|
|
7009
|
-
midFieldMaxMeters: 55,
|
|
7010
|
-
farFieldMaxMeters: 180
|
|
7011
|
-
});
|
|
7012
|
-
const cameraDistance = lengthVec3(subVec3(state.camera.target, state.camera.eye ?? vec3(...CAMERA_PRESETS[state.focus].target)));
|
|
7013
|
-
const band = selectClothRepresentationBand(cameraDistance, clothPlan.thresholds);
|
|
7014
|
-
const representation = clothPlan.representations.find((entry) => entry.band === band) ?? clothPlan.representations[0];
|
|
7015
|
-
const continuity = createClothContinuityEnvelope({ garmentId: "shore-flag" });
|
|
7016
|
-
const cols = meshDetail.cols;
|
|
7017
|
-
const rows = meshDetail.rows;
|
|
7018
|
-
const origin = vec3(-3.5, 5.9, 2.4);
|
|
7019
|
-
const width = 4.8;
|
|
7020
|
-
const height = 2.7;
|
|
7021
|
-
const positions = [];
|
|
7022
|
-
const indices = [];
|
|
7023
|
-
const time = state.time;
|
|
7024
|
-
for (let row = 0; row < rows; row += 1) {
|
|
7025
|
-
for (let column = 0; column < cols; column += 1) {
|
|
7026
|
-
const u = column / (cols - 1);
|
|
7027
|
-
const v = row / (rows - 1);
|
|
7028
|
-
const gust = Math.sin(time * 1.9 + v * 3.2 + u * 2.1) * continuity.broadMotionFloor * visuals.flagMotion;
|
|
7029
|
-
const wrinkle = Math.sin(time * 4.4 + u * 9.2 + v * 5.6) * continuity.wrinkleFloor * 0.22 * Math.max(0.55, visuals.flagMotion);
|
|
7030
|
-
const x = origin.x + u * 1.8 + gust * 0.55 * (u * 0.9);
|
|
7031
|
-
const y = origin.y - height * v + wrinkle * 0.2;
|
|
7032
|
-
const z = origin.z + width * u + gust * 0.72 * (u * 0.85);
|
|
7033
|
-
const flap = Math.cos(time * 2.7 + u * 7.4 + v * 3.8) * continuity.broadMotionFloor * 0.28 * visuals.flagMotion;
|
|
7034
|
-
positions.push(vec3(x + flap, y, z));
|
|
7035
|
-
}
|
|
7036
|
-
}
|
|
7037
|
-
for (let row = 0; row < rows - 1; row += 1) {
|
|
7038
|
-
for (let column = 0; column < cols - 1; column += 1) {
|
|
7039
|
-
const a = row * cols + column;
|
|
7040
|
-
const b = a + 1;
|
|
7041
|
-
const c = a + cols + 1;
|
|
7042
|
-
const d = a + cols;
|
|
7043
|
-
indices.push(a, b, c, a, c, d);
|
|
7044
|
-
}
|
|
7045
|
-
}
|
|
7251
|
+
const clothPresentation = resolveClothPresentation(state, meshDetail);
|
|
7252
|
+
const clothState = ensureShowcaseClothState(state, meshDetail, clothPresentation);
|
|
7046
7253
|
return {
|
|
7047
|
-
clothPlan,
|
|
7048
|
-
band,
|
|
7049
|
-
representation,
|
|
7050
|
-
continuity,
|
|
7254
|
+
clothPlan: clothPresentation.clothPlan,
|
|
7255
|
+
band: clothPresentation.band,
|
|
7256
|
+
representation: clothPresentation.representation,
|
|
7257
|
+
continuity: clothPresentation.continuity,
|
|
7051
7258
|
color: visuals.flagColor,
|
|
7052
|
-
positions,
|
|
7053
|
-
indices,
|
|
7054
|
-
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 }
|
|
7055
7262
|
};
|
|
7056
7263
|
}
|
|
7057
7264
|
function resolveWaveDirection(state) {
|
|
@@ -7115,6 +7322,59 @@ function sampleWave(state, x, z, time) {
|
|
|
7115
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;
|
|
7116
7323
|
return base * amplitude + sampleShipWake(state, x, z, time) + sampleWaveImpulses(state, x, z, time);
|
|
7117
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
|
+
}
|
|
7118
7378
|
function buildWaterBands(state, fluidDetail, visuals) {
|
|
7119
7379
|
const fluidPlan = createFluidRepresentationPlan({
|
|
7120
7380
|
fluidBodyId: "harbor",
|
|
@@ -7267,6 +7527,7 @@ function createSceneState(options) {
|
|
|
7267
7527
|
contactCount: 0,
|
|
7268
7528
|
collisionCount: 0,
|
|
7269
7529
|
collisionFlash: 0,
|
|
7530
|
+
clothState: null,
|
|
7270
7531
|
physics: {
|
|
7271
7532
|
profile: physicsProfile,
|
|
7272
7533
|
plan: physicsPlan,
|
|
@@ -7902,16 +8163,73 @@ function renderFlagShadow(ctx, cloth, camera, viewport, lightDir, shadowStrength
|
|
|
7902
8163
|
blur: 12 + shadowStrength * 20
|
|
7903
8164
|
});
|
|
7904
8165
|
}
|
|
7905
|
-
function
|
|
7906
|
-
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);
|
|
7907
8225
|
if (!projected) {
|
|
7908
8226
|
return;
|
|
7909
8227
|
}
|
|
7910
|
-
const radius = clamp(1 / projected.depth * 420 * glowScale, 4, 34);
|
|
8228
|
+
const radius = clamp(1 / projected.depth * 420 * source.glowScale, 4, 34);
|
|
7911
8229
|
const halo = ctx.createRadialGradient(projected.x, projected.y, radius * 0.12, projected.x, projected.y, radius);
|
|
7912
|
-
halo.addColorStop(0, colorToRgba(coreColor, 0.98));
|
|
7913
|
-
halo.addColorStop(0.5, colorToRgba(glowColor, 0.42));
|
|
7914
|
-
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));
|
|
7915
8233
|
ctx.save();
|
|
7916
8234
|
ctx.globalCompositeOperation = "screen";
|
|
7917
8235
|
ctx.fillStyle = halo;
|
|
@@ -7919,79 +8237,119 @@ function renderGlowLight(ctx, point, camera, viewport, coreColor, glowColor, glo
|
|
|
7919
8237
|
ctx.arc(projected.x, projected.y, radius, 0, Math.PI * 2);
|
|
7920
8238
|
ctx.fill();
|
|
7921
8239
|
ctx.restore();
|
|
7922
|
-
ctx.fillStyle = colorToRgba(coreColor, 0.98);
|
|
8240
|
+
ctx.fillStyle = colorToRgba(source.coreColor, 0.98);
|
|
7923
8241
|
ctx.beginPath();
|
|
7924
8242
|
ctx.arc(projected.x, projected.y, Math.max(1.2, radius * 0.16), 0, Math.PI * 2);
|
|
7925
8243
|
ctx.fill();
|
|
7926
|
-
|
|
7927
|
-
|
|
7928
|
-
|
|
7929
|
-
|
|
7930
|
-
|
|
7931
|
-
|
|
7932
|
-
|
|
7933
|
-
|
|
7934
|
-
|
|
7935
|
-
|
|
7936
|
-
|
|
7937
|
-
|
|
7938
|
-
|
|
7939
|
-
|
|
7940
|
-
|
|
7941
|
-
|
|
7942
|
-
|
|
7943
|
-
|
|
7944
|
-
|
|
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})`;
|
|
7945
8324
|
ctx.beginPath();
|
|
7946
8325
|
ctx.ellipse(
|
|
7947
|
-
|
|
7948
|
-
|
|
7949
|
-
|
|
7950
|
-
|
|
8326
|
+
entry.projected.x,
|
|
8327
|
+
entry.projected.y,
|
|
8328
|
+
baseWidth * 0.72,
|
|
8329
|
+
baseWidth * 0.44,
|
|
7951
8330
|
0,
|
|
7952
8331
|
0,
|
|
7953
8332
|
Math.PI * 2
|
|
7954
8333
|
);
|
|
7955
8334
|
ctx.fill();
|
|
7956
|
-
ctx.restore();
|
|
7957
8335
|
}
|
|
7958
8336
|
}
|
|
7959
|
-
|
|
7960
|
-
|
|
7961
|
-
|
|
7962
|
-
|
|
7963
|
-
|
|
7964
|
-
|
|
7965
|
-
|
|
7966
|
-
|
|
7967
|
-
);
|
|
7968
|
-
|
|
7969
|
-
|
|
7970
|
-
|
|
7971
|
-
|
|
7972
|
-
|
|
7973
|
-
visuals.lanternCore,
|
|
7974
|
-
visuals.lanternGlow,
|
|
7975
|
-
lantern.glow * strength,
|
|
7976
|
-
visuals.lanternReflectionStrength,
|
|
7977
|
-
state
|
|
7978
|
-
);
|
|
7979
|
-
}
|
|
7980
|
-
}
|
|
7981
|
-
function renderHarborTorches(ctx, state, camera, viewport, visuals) {
|
|
7982
|
-
for (const torch of HARBOR_TORCHES) {
|
|
7983
|
-
renderGlowLight(
|
|
7984
|
-
ctx,
|
|
7985
|
-
vec3(torch.x, torch.y, torch.z),
|
|
7986
|
-
camera,
|
|
7987
|
-
viewport,
|
|
7988
|
-
visuals.torchCore,
|
|
7989
|
-
visuals.torchGlow,
|
|
7990
|
-
torch.glow,
|
|
7991
|
-
visuals.lanternReflectionStrength * 0.55,
|
|
7992
|
-
state
|
|
7993
|
-
);
|
|
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();
|
|
7994
8351
|
}
|
|
8352
|
+
ctx.restore();
|
|
7995
8353
|
}
|
|
7996
8354
|
function renderScene(ctx, canvas, state, shipModel, dom) {
|
|
7997
8355
|
const viewport = { width: canvas.width, height: canvas.height };
|
|
@@ -8021,8 +8379,8 @@ function renderScene(ctx, canvas, state, shipModel, dom) {
|
|
|
8021
8379
|
shadowStrength,
|
|
8022
8380
|
visuals
|
|
8023
8381
|
);
|
|
8024
|
-
const
|
|
8025
|
-
|
|
8382
|
+
const waterTriangles = [];
|
|
8383
|
+
const sceneTriangles = [];
|
|
8026
8384
|
const water = buildWaterBands(
|
|
8027
8385
|
state,
|
|
8028
8386
|
state.fluidDetail.getSnapshot().currentLevel.config,
|
|
@@ -8039,7 +8397,7 @@ function renderScene(ctx, canvas, state, shipModel, dom) {
|
|
|
8039
8397
|
if (projected.some((value) => value === null)) {
|
|
8040
8398
|
continue;
|
|
8041
8399
|
}
|
|
8042
|
-
|
|
8400
|
+
waterTriangles.push({
|
|
8043
8401
|
points: projected,
|
|
8044
8402
|
depth: (projected[0].depth + projected[1].depth + projected[2].depth) / 3,
|
|
8045
8403
|
worldCenter: scaleVec3(addVec3(addVec3(a, b), c), 1 / 3),
|
|
@@ -8049,6 +8407,9 @@ function renderScene(ctx, canvas, state, shipModel, dom) {
|
|
|
8049
8407
|
});
|
|
8050
8408
|
}
|
|
8051
8409
|
}
|
|
8410
|
+
const waterMotionEffects = buildWaterMotionEffects(state);
|
|
8411
|
+
const lightSources = collectSceneLightSources(state, visuals);
|
|
8412
|
+
pushHarborGeometry(camera, viewport, sceneTriangles, visuals);
|
|
8052
8413
|
const cloth = buildClothSurface(
|
|
8053
8414
|
state,
|
|
8054
8415
|
state,
|
|
@@ -8064,7 +8425,7 @@ function renderScene(ctx, canvas, state, shipModel, dom) {
|
|
|
8064
8425
|
if (projected.some((value) => value === null)) {
|
|
8065
8426
|
continue;
|
|
8066
8427
|
}
|
|
8067
|
-
|
|
8428
|
+
sceneTriangles.push({
|
|
8068
8429
|
points: projected,
|
|
8069
8430
|
depth: (projected[0].depth + projected[1].depth + projected[2].depth) / 3,
|
|
8070
8431
|
worldCenter: scaleVec3(addVec3(addVec3(a, b), c), 1 / 3),
|
|
@@ -8080,22 +8441,28 @@ function renderScene(ctx, canvas, state, shipModel, dom) {
|
|
|
8080
8441
|
ship.tint,
|
|
8081
8442
|
camera,
|
|
8082
8443
|
viewport,
|
|
8083
|
-
|
|
8444
|
+
sceneTriangles,
|
|
8084
8445
|
nearLighting.rtParticipation.directShadows === "premium" ? 0.08 : 0.02
|
|
8085
8446
|
);
|
|
8086
8447
|
}
|
|
8448
|
+
drawTriangles(ctx, waterTriangles, lightDir, reflectionStrength, camera, shadowStrength);
|
|
8087
8449
|
for (const ship of state.ships) {
|
|
8088
8450
|
renderShipShadow(ctx, shipModel, ship, state, camera, viewport, lightDir, shadowStrength);
|
|
8089
8451
|
}
|
|
8090
8452
|
renderFlagShadow(ctx, cloth, camera, viewport, lightDir, shadowStrength);
|
|
8091
|
-
|
|
8453
|
+
for (const source of lightSources.reflectionLights) {
|
|
8454
|
+
renderWaterLightReflection(ctx, source, state, camera, viewport);
|
|
8455
|
+
}
|
|
8456
|
+
renderWaterMotionEffects(ctx, waterMotionEffects, camera, viewport);
|
|
8092
8457
|
renderWaterHighlights(ctx, water.bandMeshes, camera, viewport);
|
|
8093
|
-
|
|
8458
|
+
drawTriangles(ctx, sceneTriangles, lightDir, reflectionStrength, camera, shadowStrength);
|
|
8094
8459
|
renderFlagPole(ctx, camera, viewport);
|
|
8095
8460
|
renderClothAccent(ctx, cloth, camera, viewport);
|
|
8461
|
+
for (const source of lightSources.directLights) {
|
|
8462
|
+
renderDirectLightGlow(ctx, source, camera, viewport);
|
|
8463
|
+
}
|
|
8096
8464
|
for (const ship of state.ships) {
|
|
8097
8465
|
renderShipRigging(ctx, ship, camera, viewport);
|
|
8098
|
-
renderShipLanterns(ctx, ship, state, camera, viewport, visuals);
|
|
8099
8466
|
}
|
|
8100
8467
|
renderSprays(ctx, state.sprays, camera, viewport);
|
|
8101
8468
|
const debugSnapshot = state.debugSession.getSnapshot();
|
|
@@ -8157,6 +8524,21 @@ function updateSceneState(state, dt, shipModel) {
|
|
|
8157
8524
|
updateShips(state, dt, shipModel);
|
|
8158
8525
|
updateWaveImpulses(state, dt);
|
|
8159
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
|
+
});
|
|
8160
8542
|
updatePhysicsSnapshot(state, shipModel);
|
|
8161
8543
|
}
|
|
8162
8544
|
function syncTextState(state, shipModel) {
|
|
@@ -8323,7 +8705,7 @@ function updatePhysicsSnapshot(state, shipModel) {
|
|
|
8323
8705
|
}
|
|
8324
8706
|
});
|
|
8325
8707
|
}
|
|
8326
|
-
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;
|
|
8327
8709
|
var init_showcase_runtime = __esm({
|
|
8328
8710
|
"src/showcase-runtime.js"() {
|
|
8329
8711
|
init_dist();
|
|
@@ -8438,6 +8820,12 @@ var init_showcase_runtime = __esm({
|
|
|
8438
8820
|
Object.freeze({ x: -8.6, y: 2.48, z: -0.72, glow: 1 }),
|
|
8439
8821
|
Object.freeze({ x: -10.4, y: 1.28, z: 0.82, glow: 0.92 })
|
|
8440
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
|
+
});
|
|
8441
8829
|
}
|
|
8442
8830
|
});
|
|
8443
8831
|
|