@plasius/gpu-shared 0.1.17 → 0.1.19
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 +18 -0
- package/README.md +3 -0
- package/dist/{chunk-BXTHIQOO.js → chunk-EZHA3MH7.js} +9 -15
- package/dist/chunk-EZHA3MH7.js.map +1 -0
- package/dist/index.cjs +55 -61
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2 -2
- package/dist/{product-studio-runtime-CTXA45RJ.js → product-studio-runtime-3KANG3JE.js} +2 -2
- package/package.json +1 -1
- package/src/product-studio-runtime.js +8 -26
- package/dist/chunk-BXTHIQOO.js.map +0 -1
- /package/dist/{product-studio-runtime-CTXA45RJ.js.map → product-studio-runtime-3KANG3JE.js.map} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,20 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
- **Added**
|
|
8
|
+
- (placeholder)
|
|
9
|
+
|
|
10
|
+
- **Changed**
|
|
11
|
+
- (placeholder)
|
|
12
|
+
|
|
13
|
+
- **Fixed**
|
|
14
|
+
- (placeholder)
|
|
15
|
+
|
|
16
|
+
- **Security**
|
|
17
|
+
- (placeholder)
|
|
18
|
+
|
|
19
|
+
## [0.1.19] - 2026-06-22
|
|
20
|
+
|
|
7
21
|
- **Added**
|
|
8
22
|
- Deterministic showcase asset generation for a richer shared brigantine,
|
|
9
23
|
cutter, lighthouse, and harbor-dock catalog.
|
|
@@ -16,6 +30,9 @@ All notable changes to this project will be documented in this file.
|
|
|
16
30
|
delegation to the `@plasius/gpu-renderer` WebGPU wavefront renderer.
|
|
17
31
|
|
|
18
32
|
- **Changed**
|
|
33
|
+
- Lowered Product Studio wavefront renderer defaults to a host-safe `640x360`
|
|
34
|
+
frame at `maxDepth: 2` so browser demos do not start with a heavy 720p
|
|
35
|
+
depth-6 GPU workload.
|
|
19
36
|
- Updated the optional `@plasius/gpu-renderer` peer range to the 0.2 line so
|
|
20
37
|
Product Studio and demo consumers can use the released realtime wavefront
|
|
21
38
|
camera update API without npm peer conflicts.
|
|
@@ -273,3 +290,4 @@ All notable changes to this project will be documented in this file.
|
|
|
273
290
|
[0.1.9]: https://github.com/Plasius-LTD/gpu-shared/releases/tag/v0.1.9
|
|
274
291
|
[0.1.10]: https://github.com/Plasius-LTD/gpu-shared/releases/tag/v0.1.10
|
|
275
292
|
[0.1.11]: https://github.com/Plasius-LTD/gpu-shared/releases/tag/v0.1.11
|
|
293
|
+
[0.1.19]: https://github.com/Plasius-LTD/gpu-shared/releases/tag/v0.1.19
|
package/README.md
CHANGED
|
@@ -214,6 +214,9 @@ surface for these family demos.
|
|
|
214
214
|
|
|
215
215
|
- `mountGpuShowcase(options)`
|
|
216
216
|
- Returns `{ state, shipModel, canvas, destroy() }`
|
|
217
|
+
- Product Studio mode defaults to a host-safe `640x360` wavefront render at
|
|
218
|
+
`maxDepth: 2`; callers can raise `width`, `height`, or `maxDepth`
|
|
219
|
+
deliberately for capture or high-end review hardware.
|
|
217
220
|
- `captureMode: true` enables fullscreen scene-only presentation for local
|
|
218
221
|
screenshots and video capture.
|
|
219
222
|
- `renderScale` overrides the canvas backing scale when a capture workflow
|
|
@@ -7,9 +7,9 @@ var STYLE_ID = "plasius-product-studio-wavefront-style";
|
|
|
7
7
|
var DEFAULT_PRODUCT_ASSET_URL = "/data/models/eames-lounge-chair-ottoman/Eames_Lounge_Chair_Ottoman.gltf";
|
|
8
8
|
var DEFAULT_TARGET_CENTER = Object.freeze([0, 0.74, 0]);
|
|
9
9
|
var DEFAULT_TARGET_SIZE = 2.25;
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
var DEFAULT_RENDER_WIDTH = 640;
|
|
11
|
+
var DEFAULT_RENDER_HEIGHT = 360;
|
|
12
|
+
var DEFAULT_RENDER_MAX_DEPTH = 2;
|
|
13
13
|
function isFiniteVector(value) {
|
|
14
14
|
return Array.isArray(value) && value.length >= 3 && Number.isFinite(value[0]) && Number.isFinite(value[1]) && Number.isFinite(value[2]);
|
|
15
15
|
}
|
|
@@ -305,16 +305,10 @@ function resolveRoot(options) {
|
|
|
305
305
|
}
|
|
306
306
|
return root;
|
|
307
307
|
}
|
|
308
|
-
function resolveRenderSize(
|
|
309
|
-
const rect = root.getBoundingClientRect?.() ?? { width: 1280, height: 720 };
|
|
310
|
-
const devicePixelRatio = Number.isFinite(options.devicePixelRatio) ? options.devicePixelRatio : Number.isFinite(globalThis.window?.devicePixelRatio) ? globalThis.window.devicePixelRatio : 1;
|
|
311
|
-
const cssWidth = Number.isFinite(rect.width) && rect.width > 0 ? rect.width : 1280;
|
|
312
|
-
const cssHeight = Number.isFinite(rect.height) && rect.height > 0 ? rect.height : cssWidth * (9 / 16);
|
|
313
|
-
const width = Number.isFinite(options.width) ? Math.trunc(options.width) : clamp(Math.round(cssWidth * devicePixelRatio), 640, 1920);
|
|
314
|
-
const height = Number.isFinite(options.height) ? Math.trunc(options.height) : clamp(Math.round(cssHeight * devicePixelRatio), 360, 1080);
|
|
308
|
+
function resolveRenderSize(options) {
|
|
315
309
|
return {
|
|
316
|
-
width,
|
|
317
|
-
height
|
|
310
|
+
width: Number.isFinite(options.width) ? Math.trunc(options.width) : DEFAULT_RENDER_WIDTH,
|
|
311
|
+
height: Number.isFinite(options.height) ? Math.trunc(options.height) : DEFAULT_RENDER_HEIGHT
|
|
318
312
|
};
|
|
319
313
|
}
|
|
320
314
|
function installSnapshotHook(state) {
|
|
@@ -385,13 +379,13 @@ async function mountGpuProductStudio(options = {}, featureFlags = null) {
|
|
|
385
379
|
if (typeof rendererModule.createWavefrontPathTracingComputeRenderer !== "function") {
|
|
386
380
|
throw new Error("Product Studio renderer loader must provide createWavefrontPathTracingComputeRenderer.");
|
|
387
381
|
}
|
|
388
|
-
const size = resolveRenderSize(
|
|
382
|
+
const size = resolveRenderSize(options);
|
|
389
383
|
const lightingOptions = await resolveWavefrontLightingOptions(options);
|
|
390
384
|
const renderer = await rendererModule.createWavefrontPathTracingComputeRenderer({
|
|
391
385
|
canvas,
|
|
392
386
|
width: size.width,
|
|
393
387
|
height: size.height,
|
|
394
|
-
maxDepth: Number.isFinite(options.maxDepth) ? options.maxDepth :
|
|
388
|
+
maxDepth: Number.isFinite(options.maxDepth) ? options.maxDepth : DEFAULT_RENDER_MAX_DEPTH,
|
|
395
389
|
tileSize: Number.isFinite(options.tileSize) ? options.tileSize : 128,
|
|
396
390
|
samplesPerPixel: Number.isFinite(options.samplesPerPixel) ? options.samplesPerPixel : 8,
|
|
397
391
|
denoise: options.denoise !== false,
|
|
@@ -438,4 +432,4 @@ export {
|
|
|
438
432
|
createProductStudioMeshes,
|
|
439
433
|
mountGpuProductStudio
|
|
440
434
|
};
|
|
441
|
-
//# sourceMappingURL=chunk-
|
|
435
|
+
//# sourceMappingURL=chunk-EZHA3MH7.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/product-studio-runtime.js"],"sourcesContent":["import { loadGltfModel } from \"./gltf-loader.js\";\n\nconst STYLE_ID = \"plasius-product-studio-wavefront-style\";\nconst DEFAULT_PRODUCT_ASSET_URL =\n \"/data/models/eames-lounge-chair-ottoman/Eames_Lounge_Chair_Ottoman.gltf\";\nconst DEFAULT_TARGET_CENTER = Object.freeze([0, 0.74, 0]);\nconst DEFAULT_TARGET_SIZE = 2.25;\nconst DEFAULT_RENDER_WIDTH = 640;\nconst DEFAULT_RENDER_HEIGHT = 360;\nconst DEFAULT_RENDER_MAX_DEPTH = 2;\n\nfunction isFiniteVector(value) {\n return (\n Array.isArray(value) &&\n value.length >= 3 &&\n Number.isFinite(value[0]) &&\n Number.isFinite(value[1]) &&\n Number.isFinite(value[2])\n );\n}\n\nfunction createEmptyBounds() {\n return {\n min: [Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY],\n max: [Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY],\n };\n}\n\nfunction expandBounds(bounds, point) {\n bounds.min[0] = Math.min(bounds.min[0], point[0]);\n bounds.min[1] = Math.min(bounds.min[1], point[1]);\n bounds.min[2] = Math.min(bounds.min[2], point[2]);\n bounds.max[0] = Math.max(bounds.max[0], point[0]);\n bounds.max[1] = Math.max(bounds.max[1], point[1]);\n bounds.max[2] = Math.max(bounds.max[2], point[2]);\n}\n\nfunction getBoundsSize(bounds) {\n return [\n bounds.max[0] - bounds.min[0],\n bounds.max[1] - bounds.min[1],\n bounds.max[2] - bounds.min[2],\n ];\n}\n\nfunction getBoundsCenter(bounds) {\n return [\n (bounds.min[0] + bounds.max[0]) * 0.5,\n (bounds.min[1] + bounds.max[1]) * 0.5,\n (bounds.min[2] + bounds.max[2]) * 0.5,\n ];\n}\n\nfunction getModelBounds(model) {\n if (isFiniteVector(model?.bounds?.min) && isFiniteVector(model?.bounds?.max)) {\n return {\n min: [...model.bounds.min],\n max: [...model.bounds.max],\n };\n }\n\n const bounds = createEmptyBounds();\n for (const primitive of model?.primitives ?? []) {\n for (let index = 0; index < primitive.positions.length; index += 3) {\n expandBounds(bounds, [\n primitive.positions[index],\n primitive.positions[index + 1],\n primitive.positions[index + 2],\n ]);\n }\n }\n return bounds;\n}\n\nfunction transformPoint(point, modelCenter, scale, targetCenter) {\n return [\n (point[0] - modelCenter[0]) * scale + targetCenter[0],\n (point[1] - modelCenter[1]) * scale + targetCenter[1],\n (point[2] - modelCenter[2]) * scale + targetCenter[2],\n ];\n}\n\nfunction readMaterialColor(material) {\n const color = material?.color ?? {};\n return [\n Number.isFinite(color.r) ? color.r : 0.62,\n Number.isFinite(color.g) ? color.g : 0.56,\n Number.isFinite(color.b) ? color.b : 0.48,\n Number.isFinite(color.a) ? color.a : 1,\n ];\n}\n\nfunction readMaterialKind(material) {\n const emissive = material?.emissive ?? {};\n if ((emissive.r ?? 0) + (emissive.g ?? 0) + (emissive.b ?? 0) > 0.001) {\n return \"emissive\";\n }\n if ((material?.metallic ?? 0) >= 0.5) {\n return \"metal\";\n }\n const alpha = readMaterialColor(material)[3];\n if (alpha < 0.9) {\n return \"transparent\";\n }\n return \"diffuse\";\n}\n\nfunction readEmission(material) {\n const emissive = material?.emissive ?? {};\n return [\n Number.isFinite(emissive.r) ? emissive.r : 0,\n Number.isFinite(emissive.g) ? emissive.g : 0,\n Number.isFinite(emissive.b) ? emissive.b : 0,\n 1,\n ];\n}\n\nfunction createQuadMesh({\n id,\n corners,\n color,\n emission = [0, 0, 0, 1],\n materialKind = \"diffuse\",\n roughness = 0.72,\n metallic = 0,\n opacity = color[3] ?? 1,\n}) {\n const [a, b, c] = corners;\n const edge1 = [b[0] - a[0], b[1] - a[1], b[2] - a[2]];\n const edge2 = [c[0] - a[0], c[1] - a[1], c[2] - a[2]];\n const normal = [\n edge1[1] * edge2[2] - edge1[2] * edge2[1],\n edge1[2] * edge2[0] - edge1[0] * edge2[2],\n edge1[0] * edge2[1] - edge1[1] * edge2[0],\n ];\n const length = Math.hypot(normal[0], normal[1], normal[2]) || 1;\n const unitNormal = normal.map((value) => value / length);\n\n return Object.freeze({\n id,\n positions: Object.freeze(corners.flat()),\n indices: Object.freeze([0, 1, 2, 0, 2, 3]),\n normals: Object.freeze([unitNormal, unitNormal, unitNormal, unitNormal].flat()),\n color: Object.freeze([...color]),\n emission: Object.freeze([...emission]),\n materialKind,\n roughness,\n metallic,\n opacity,\n });\n}\n\nfunction createProductStudioEnvironmentMeshes() {\n return [\n createQuadMesh({\n id: 1,\n corners: [\n [-3.2, -0.08, 2.4],\n [3.2, -0.08, 2.4],\n [3.2, -0.08, -3.1],\n [-3.2, -0.08, -3.1],\n ],\n color: [0.48, 0.55, 0.55, 1],\n roughness: 0.82,\n }),\n createQuadMesh({\n id: 2,\n corners: [\n [-3.2, -0.08, -2.45],\n [3.2, -0.08, -2.45],\n [3.2, 2.65, -2.45],\n [-3.2, 2.65, -2.45],\n ],\n color: [0.43, 0.42, 0.38, 1],\n roughness: 0.86,\n }),\n createQuadMesh({\n id: 3,\n corners: [\n [-2.85, -0.08, -2.45],\n [-2.85, 2.55, -2.45],\n [-2.85, 2.55, 2.15],\n [-2.85, -0.08, 2.15],\n ],\n color: [0.36, 0.42, 0.45, 1],\n roughness: 0.8,\n }),\n createQuadMesh({\n id: 4,\n corners: [\n [0.78, 2.55, -0.82],\n [-0.78, 2.55, -0.82],\n [-0.78, 2.55, -1.78],\n [0.78, 2.55, -1.78],\n ],\n color: [1, 0.94, 0.78, 1],\n emission: [8.5, 7.2, 4.8, 1],\n materialKind: \"emissive\",\n roughness: 0,\n }),\n ];\n}\n\nfunction createProductStudioMeshFromPrimitive(primitive, primitiveIndex, transform) {\n if (!Array.isArray(primitive?.positions) || primitive.positions.length < 9) {\n return null;\n }\n\n const positions = [];\n for (let index = 0; index < primitive.positions.length; index += 3) {\n const point = transform([\n primitive.positions[index],\n primitive.positions[index + 1],\n primitive.positions[index + 2],\n ]);\n positions.push(point[0], point[1], point[2]);\n }\n\n const indices =\n Array.isArray(primitive.indices) && primitive.indices.length >= 3\n ? [...primitive.indices]\n : Array.from({ length: positions.length / 3 }, (_, index) => index);\n const material = primitive.material ?? {};\n const color = readMaterialColor(material);\n const uvs = Array.isArray(primitive.uvs) ? [...primitive.uvs] : null;\n\n return Object.freeze({\n id: 1000 + primitiveIndex,\n positions: Object.freeze(positions),\n indices: Object.freeze(indices),\n normals: Array.isArray(primitive.normals) ? Object.freeze([...primitive.normals]) : null,\n uvs: uvs ? Object.freeze(uvs) : null,\n material: Object.freeze({ ...material }),\n color: Object.freeze(color),\n emission: Object.freeze(readEmission(material)),\n materialKind: readMaterialKind(material),\n materialRefId: 1000 + primitiveIndex,\n roughness: Number.isFinite(material.roughness) ? material.roughness : 0.72,\n metallic: Number.isFinite(material.metallic) ? material.metallic : 0,\n opacity: color[3],\n baseColorTexture: material.baseColorTexture ?? null,\n metallicRoughnessTexture: material.metallicRoughnessTexture ?? null,\n normalTexture: material.normalTexture ?? null,\n occlusionTexture: material.occlusionTexture ?? null,\n emissiveTexture: material.emissiveTexture ?? null,\n specular: Number.isFinite(material.specular) ? material.specular : 1,\n specularColor: Array.isArray(material.specularColor)\n ? Object.freeze([...material.specularColor])\n : undefined,\n specularTexture: material.specularTexture ?? null,\n specularColorTexture: material.specularColorTexture ?? null,\n transmission: Number.isFinite(material.transmission) ? material.transmission : 0,\n transmissionTexture: material.transmissionTexture ?? null,\n ior: Number.isFinite(material.ior) ? material.ior : undefined,\n thickness: Number.isFinite(material.thickness) ? material.thickness : undefined,\n thicknessTexture: material.thicknessTexture ?? null,\n attenuationDistance:\n Number.isFinite(material.attenuationDistance) ? material.attenuationDistance : null,\n attenuationColor: Array.isArray(material.attenuationColor)\n ? Object.freeze([...material.attenuationColor])\n : undefined,\n clearcoat: Number.isFinite(material.clearcoat) ? material.clearcoat : undefined,\n clearcoatTexture: material.clearcoatTexture ?? null,\n clearcoatRoughness: Number.isFinite(material.clearcoatRoughness)\n ? material.clearcoatRoughness\n : undefined,\n clearcoatRoughnessTexture: material.clearcoatRoughnessTexture ?? null,\n clearcoatNormalTexture: material.clearcoatNormalTexture ?? null,\n sheenColor: Array.isArray(material.sheenColor)\n ? Object.freeze([...material.sheenColor])\n : undefined,\n sheenColorTexture: material.sheenColorTexture ?? null,\n sheenRoughness: Number.isFinite(material.sheenRoughness) ? material.sheenRoughness : undefined,\n sheenRoughnessTexture: material.sheenRoughnessTexture ?? null,\n iridescence: Number.isFinite(material.iridescence) ? material.iridescence : undefined,\n iridescenceTexture: material.iridescenceTexture ?? null,\n iridescenceIor: Number.isFinite(material.iridescenceIor) ? material.iridescenceIor : undefined,\n iridescenceThicknessMinimum: Number.isFinite(material.iridescenceThicknessMinimum)\n ? material.iridescenceThicknessMinimum\n : undefined,\n iridescenceThicknessMaximum: Number.isFinite(material.iridescenceThicknessMaximum)\n ? material.iridescenceThicknessMaximum\n : undefined,\n iridescenceThicknessTexture: material.iridescenceThicknessTexture ?? null,\n anisotropy: Number.isFinite(material.anisotropy) ? material.anisotropy : undefined,\n anisotropyRotation: Number.isFinite(material.anisotropyRotation)\n ? material.anisotropyRotation\n : undefined,\n anisotropyTexture: material.anisotropyTexture ?? null,\n dispersion: Number.isFinite(material.dispersion) ? material.dispersion : undefined,\n });\n}\n\nexport function createProductStudioMeshes(model, options = {}) {\n const primitives = Array.isArray(model?.primitives) ? model.primitives : [];\n if (primitives.length === 0) {\n throw new Error(\"Product Studio model must contain at least one renderable primitive.\");\n }\n\n const targetCenter = isFiniteVector(options.targetCenter)\n ? [...options.targetCenter]\n : [...DEFAULT_TARGET_CENTER];\n const targetSize = Number.isFinite(options.targetSize)\n ? Math.max(options.targetSize, 0.25)\n : DEFAULT_TARGET_SIZE;\n const modelBounds = getModelBounds(model);\n const modelSize = getBoundsSize(modelBounds);\n const modelCenter = getBoundsCenter(modelBounds);\n const scale = targetSize / Math.max(modelSize[0], modelSize[1], modelSize[2], 0.000001);\n const transform = (point) => transformPoint(point, modelCenter, scale, targetCenter);\n const modelMeshes = primitives\n .map((primitive, index) => createProductStudioMeshFromPrimitive(primitive, index, transform))\n .filter(Boolean);\n\n return Object.freeze([...createProductStudioEnvironmentMeshes(), ...modelMeshes]);\n}\n\nfunction ensureStyles(documentRef) {\n if (documentRef.getElementById?.(STYLE_ID)) {\n return;\n }\n const style = documentRef.createElement(\"style\");\n style.id = STYLE_ID;\n style.textContent = `\n .plasius-product-studio-wavefront {\n position: relative;\n width: 100%;\n min-height: 420px;\n overflow: hidden;\n background: #0f1418;\n display: grid;\n place-items: center;\n }\n\n .plasius-product-studio-wavefront canvas {\n display: block;\n width: 100%;\n height: auto;\n max-height: 100%;\n aspect-ratio: 16 / 9;\n min-height: 420px;\n object-fit: contain;\n }\n `;\n documentRef.head?.appendChild?.(style);\n}\n\nfunction resolveRoot(options) {\n const documentRef = options.document ?? globalThis.document;\n if (options.root) {\n return options.root;\n }\n const root =\n documentRef?.querySelector?.(\"[data-plasius-gpu-product-studio]\") ?? documentRef?.body;\n if (!root) {\n throw new Error(\"Product Studio requires a DOM root.\");\n }\n return root;\n}\n\nfunction resolveRenderSize(options) {\n return {\n width: Number.isFinite(options.width) ? Math.trunc(options.width) : DEFAULT_RENDER_WIDTH,\n height: Number.isFinite(options.height) ? Math.trunc(options.height) : DEFAULT_RENDER_HEIGHT,\n };\n}\n\nfunction installSnapshotHook(state) {\n if (typeof globalThis.window === \"undefined\") {\n return () => {};\n }\n const previous = globalThis.window.render_game_to_text;\n globalThis.window.render_game_to_text = () =>\n JSON.stringify({\n surface: \"gpu-product-studio-wavefront\",\n model: state.modelName,\n sourceTriangles: state.sourceTriangleCount,\n meshCount: state.meshCount,\n geometryMode: state.geometryMode,\n requiresTriangleMeshRenderer: state.requiresTriangleMeshRenderer,\n displayQuality: state.displayQuality,\n requiresMeshBvhForDisplayQuality: state.requiresMeshBvhForDisplayQuality,\n renderer: state.rendererStats,\n });\n return () => {\n if (previous === undefined) {\n delete globalThis.window.render_game_to_text;\n } else {\n globalThis.window.render_game_to_text = previous;\n }\n };\n}\n\nfunction countSourceTriangles(model) {\n return (model.primitives ?? []).reduce(\n (total, primitive) => total + Math.floor((primitive.indices?.length ?? 0) / 3),\n 0\n );\n}\n\nasync function resolveWavefrontLightingOptions(options) {\n const fallback = {\n environmentColor: [0.35, 0.43, 0.49, 1],\n ambientColor: [0.02, 0.024, 0.028, 1],\n };\n const lightingLoader =\n typeof options.__lightingLoader === \"function\"\n ? options.__lightingLoader\n : () => import(\"@plasius/gpu-lighting\").catch(() => null);\n const lightingModule = await lightingLoader();\n\n if (\n typeof lightingModule?.createWavefrontEnvironmentLightingOptions !== \"function\"\n ) {\n return fallback;\n }\n\n return lightingModule.createWavefrontEnvironmentLightingOptions({\n preset: options.lightingPreset ?? \"product-studio\",\n intensity: options.lightingIntensity,\n });\n}\n\nexport async function mountGpuProductStudio(options = {}, featureFlags = null) {\n const root = resolveRoot(options);\n const documentRef = options.document ?? root.ownerDocument ?? globalThis.document;\n ensureStyles(documentRef);\n const previousMarkup = root.innerHTML;\n root.innerHTML = \"\";\n root.classList?.add?.(\"plasius-product-studio-wavefront\");\n\n const canvas = documentRef.createElement(\"canvas\");\n canvas.dataset.plasiusGpuProductStudio = \"wavefront\";\n root.appendChild(canvas);\n\n const modelLoader =\n typeof options.__modelLoader === \"function\" ? options.__modelLoader : loadGltfModel;\n const rendererLoader =\n typeof options.__rendererLoader === \"function\"\n ? options.__rendererLoader\n : () => import(\"@plasius/gpu-renderer\");\n const assetUrl = options.productAssetUrl ?? options.assetUrl ?? DEFAULT_PRODUCT_ASSET_URL;\n const model = await modelLoader(assetUrl);\n const meshes = createProductStudioMeshes(model, {\n targetCenter: options.targetCenter,\n targetSize: options.targetSize,\n });\n const rendererModule = await rendererLoader();\n if (typeof rendererModule.createWavefrontPathTracingComputeRenderer !== \"function\") {\n throw new Error(\"Product Studio renderer loader must provide createWavefrontPathTracingComputeRenderer.\");\n }\n\n const size = resolveRenderSize(options);\n const lightingOptions = await resolveWavefrontLightingOptions(options);\n const renderer = await rendererModule.createWavefrontPathTracingComputeRenderer({\n canvas,\n width: size.width,\n height: size.height,\n maxDepth: Number.isFinite(options.maxDepth) ? options.maxDepth : DEFAULT_RENDER_MAX_DEPTH,\n tileSize: Number.isFinite(options.tileSize) ? options.tileSize : 128,\n samplesPerPixel: Number.isFinite(options.samplesPerPixel) ? options.samplesPerPixel : 8,\n denoise: options.denoise !== false,\n displayQuality: true,\n meshes,\n camera: {\n position: [0, 1.12, 5.05],\n target: [0, 0.72, 0],\n up: [0, 1, 0],\n fovYDegrees: 43,\n },\n ...lightingOptions,\n });\n const rendererStats = renderer.renderOnce();\n const state = Object.freeze({\n featureFlags,\n modelName: model.name,\n sourceTriangleCount: countSourceTriangles(model),\n meshCount: meshes.length,\n geometryMode: \"mesh-bvh-display-quality\",\n requiresTriangleMeshRenderer: true,\n displayQuality: true,\n requiresMeshBvhForDisplayQuality: true,\n rendererStats,\n });\n const restoreSnapshotHook = installSnapshotHook(state);\n\n return Object.freeze({\n state,\n model,\n productModel: model,\n canvas,\n renderer,\n meshes,\n destroy() {\n restoreSnapshotHook();\n renderer.destroy();\n root.classList?.remove?.(\"plasius-product-studio-wavefront\");\n root.innerHTML = previousMarkup;\n },\n });\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.cjs
CHANGED
|
@@ -2410,9 +2410,6 @@ __export(product_studio_runtime_exports, {
|
|
|
2410
2410
|
createProductStudioMeshes: () => createProductStudioMeshes,
|
|
2411
2411
|
mountGpuProductStudio: () => mountGpuProductStudio
|
|
2412
2412
|
});
|
|
2413
|
-
function clamp(value, min, max) {
|
|
2414
|
-
return Math.max(min, Math.min(max, value));
|
|
2415
|
-
}
|
|
2416
2413
|
function isFiniteVector(value) {
|
|
2417
2414
|
return Array.isArray(value) && value.length >= 3 && Number.isFinite(value[0]) && Number.isFinite(value[1]) && Number.isFinite(value[2]);
|
|
2418
2415
|
}
|
|
@@ -2708,16 +2705,10 @@ function resolveRoot(options) {
|
|
|
2708
2705
|
}
|
|
2709
2706
|
return root;
|
|
2710
2707
|
}
|
|
2711
|
-
function resolveRenderSize(
|
|
2712
|
-
const rect = root.getBoundingClientRect?.() ?? { width: 1280, height: 720 };
|
|
2713
|
-
const devicePixelRatio = Number.isFinite(options.devicePixelRatio) ? options.devicePixelRatio : Number.isFinite(globalThis.window?.devicePixelRatio) ? globalThis.window.devicePixelRatio : 1;
|
|
2714
|
-
const cssWidth = Number.isFinite(rect.width) && rect.width > 0 ? rect.width : 1280;
|
|
2715
|
-
const cssHeight = Number.isFinite(rect.height) && rect.height > 0 ? rect.height : cssWidth * (9 / 16);
|
|
2716
|
-
const width = Number.isFinite(options.width) ? Math.trunc(options.width) : clamp(Math.round(cssWidth * devicePixelRatio), 640, 1920);
|
|
2717
|
-
const height = Number.isFinite(options.height) ? Math.trunc(options.height) : clamp(Math.round(cssHeight * devicePixelRatio), 360, 1080);
|
|
2708
|
+
function resolveRenderSize(options) {
|
|
2718
2709
|
return {
|
|
2719
|
-
width,
|
|
2720
|
-
height
|
|
2710
|
+
width: Number.isFinite(options.width) ? Math.trunc(options.width) : DEFAULT_RENDER_WIDTH,
|
|
2711
|
+
height: Number.isFinite(options.height) ? Math.trunc(options.height) : DEFAULT_RENDER_HEIGHT
|
|
2721
2712
|
};
|
|
2722
2713
|
}
|
|
2723
2714
|
function installSnapshotHook(state) {
|
|
@@ -2788,13 +2779,13 @@ async function mountGpuProductStudio(options = {}, featureFlags = null) {
|
|
|
2788
2779
|
if (typeof rendererModule.createWavefrontPathTracingComputeRenderer !== "function") {
|
|
2789
2780
|
throw new Error("Product Studio renderer loader must provide createWavefrontPathTracingComputeRenderer.");
|
|
2790
2781
|
}
|
|
2791
|
-
const size = resolveRenderSize(
|
|
2782
|
+
const size = resolveRenderSize(options);
|
|
2792
2783
|
const lightingOptions = await resolveWavefrontLightingOptions(options);
|
|
2793
2784
|
const renderer = await rendererModule.createWavefrontPathTracingComputeRenderer({
|
|
2794
2785
|
canvas,
|
|
2795
2786
|
width: size.width,
|
|
2796
2787
|
height: size.height,
|
|
2797
|
-
maxDepth: Number.isFinite(options.maxDepth) ? options.maxDepth :
|
|
2788
|
+
maxDepth: Number.isFinite(options.maxDepth) ? options.maxDepth : DEFAULT_RENDER_MAX_DEPTH,
|
|
2798
2789
|
tileSize: Number.isFinite(options.tileSize) ? options.tileSize : 128,
|
|
2799
2790
|
samplesPerPixel: Number.isFinite(options.samplesPerPixel) ? options.samplesPerPixel : 8,
|
|
2800
2791
|
denoise: options.denoise !== false,
|
|
@@ -2836,7 +2827,7 @@ async function mountGpuProductStudio(options = {}, featureFlags = null) {
|
|
|
2836
2827
|
}
|
|
2837
2828
|
});
|
|
2838
2829
|
}
|
|
2839
|
-
var STYLE_ID, DEFAULT_PRODUCT_ASSET_URL, DEFAULT_TARGET_CENTER, DEFAULT_TARGET_SIZE;
|
|
2830
|
+
var STYLE_ID, DEFAULT_PRODUCT_ASSET_URL, DEFAULT_TARGET_CENTER, DEFAULT_TARGET_SIZE, DEFAULT_RENDER_WIDTH, DEFAULT_RENDER_HEIGHT, DEFAULT_RENDER_MAX_DEPTH;
|
|
2840
2831
|
var init_product_studio_runtime = __esm({
|
|
2841
2832
|
"src/product-studio-runtime.js"() {
|
|
2842
2833
|
init_gltf_loader();
|
|
@@ -2844,6 +2835,9 @@ var init_product_studio_runtime = __esm({
|
|
|
2844
2835
|
DEFAULT_PRODUCT_ASSET_URL = "/data/models/eames-lounge-chair-ottoman/Eames_Lounge_Chair_Ottoman.gltf";
|
|
2845
2836
|
DEFAULT_TARGET_CENTER = Object.freeze([0, 0.74, 0]);
|
|
2846
2837
|
DEFAULT_TARGET_SIZE = 2.25;
|
|
2838
|
+
DEFAULT_RENDER_WIDTH = 640;
|
|
2839
|
+
DEFAULT_RENDER_HEIGHT = 360;
|
|
2840
|
+
DEFAULT_RENDER_MAX_DEPTH = 2;
|
|
2847
2841
|
}
|
|
2848
2842
|
});
|
|
2849
2843
|
|
|
@@ -2964,17 +2958,17 @@ function createFallbackPerformanceFeatureModule() {
|
|
|
2964
2958
|
let pressureLevel = "stable";
|
|
2965
2959
|
let frameSamples = 0;
|
|
2966
2960
|
let averageMs = 16.67;
|
|
2967
|
-
const
|
|
2961
|
+
const clamp2 = (next = 16.67) => Number.isFinite(next) ? Math.max(1, next) : 16.67;
|
|
2968
2962
|
const target = Object.freeze({
|
|
2969
2963
|
targetFrameTimeMs: 16.67,
|
|
2970
|
-
downgradeFrameTimeMs:
|
|
2971
|
-
upgradeFrameTimeMs:
|
|
2964
|
+
downgradeFrameTimeMs: clamp2(adaptation?.degradeThresholdMs ?? 20),
|
|
2965
|
+
upgradeFrameTimeMs: clamp2(adaptation?.upgradeThresholdMs ?? 14)
|
|
2972
2966
|
});
|
|
2973
2967
|
return {
|
|
2974
2968
|
recordFrame({ frameTimeMs = averageMs } = {}) {
|
|
2975
2969
|
const sample = Number.isFinite(Number(frameTimeMs)) ? Number(frameTimeMs) : averageMs;
|
|
2976
2970
|
frameSamples += 1;
|
|
2977
|
-
averageMs =
|
|
2971
|
+
averageMs = clamp2((averageMs * (frameSamples - 1) + sample) / frameSamples);
|
|
2978
2972
|
const fps = 1e3 / averageMs;
|
|
2979
2973
|
pressureLevel = sample > target.downgradeFrameTimeMs ? "degrade" : pressureLevel === "degrade" && sample <= target.upgradeFrameTimeMs ? "stable" : pressureLevel;
|
|
2980
2974
|
return {
|
|
@@ -3655,14 +3649,14 @@ function injectStyles() {
|
|
|
3655
3649
|
`;
|
|
3656
3650
|
document.head.appendChild(style);
|
|
3657
3651
|
}
|
|
3658
|
-
function
|
|
3652
|
+
function clamp(value, min, max) {
|
|
3659
3653
|
return Math.max(min, Math.min(max, value));
|
|
3660
3654
|
}
|
|
3661
3655
|
function mix(a, b, t) {
|
|
3662
3656
|
return a + (b - a) * t;
|
|
3663
3657
|
}
|
|
3664
3658
|
function smoothstep(min, max, value) {
|
|
3665
|
-
const t =
|
|
3659
|
+
const t = clamp((value - min) / Math.max(1e-4, max - min), 0, 1);
|
|
3666
3660
|
return t * t * (3 - 2 * t);
|
|
3667
3661
|
}
|
|
3668
3662
|
function pseudoRandom(seed) {
|
|
@@ -3746,10 +3740,10 @@ function projectPoint(point, camera, viewport) {
|
|
|
3746
3740
|
};
|
|
3747
3741
|
}
|
|
3748
3742
|
function colorToRgba(color, alpha = 1) {
|
|
3749
|
-
const r = Math.round(
|
|
3750
|
-
const g = Math.round(
|
|
3751
|
-
const b = Math.round(
|
|
3752
|
-
return `rgba(${r}, ${g}, ${b}, ${
|
|
3743
|
+
const r = Math.round(clamp(color.r, 0, 1) * 255);
|
|
3744
|
+
const g = Math.round(clamp(color.g, 0, 1) * 255);
|
|
3745
|
+
const b = Math.round(clamp(color.b, 0, 1) * 255);
|
|
3746
|
+
return `rgba(${r}, ${g}, ${b}, ${clamp(alpha, 0, 1)})`;
|
|
3753
3747
|
}
|
|
3754
3748
|
function mixColor(a, b, t) {
|
|
3755
3749
|
return {
|
|
@@ -3823,12 +3817,12 @@ function projectShadowPoint(point, lightDir, planeY) {
|
|
|
3823
3817
|
return addVec3(point, scaleVec3(shadowDir, distance));
|
|
3824
3818
|
}
|
|
3825
3819
|
function shadeColor(base, normal, lightDir, heightBias = 0, accent = 0) {
|
|
3826
|
-
const diffuse =
|
|
3820
|
+
const diffuse = clamp(dotVec3(normalizeVec3(normal), lightDir), 0, 1);
|
|
3827
3821
|
const brightness = 0.24 + diffuse * 0.72 + heightBias * 0.08 + accent;
|
|
3828
3822
|
return {
|
|
3829
|
-
r:
|
|
3830
|
-
g:
|
|
3831
|
-
b:
|
|
3823
|
+
r: clamp(base.r * brightness, 0, 1),
|
|
3824
|
+
g: clamp(base.g * brightness, 0, 1),
|
|
3825
|
+
b: clamp(base.b * (brightness + 0.03), 0, 1)
|
|
3832
3826
|
};
|
|
3833
3827
|
}
|
|
3834
3828
|
function getMaterialSeed(materialName) {
|
|
@@ -3866,13 +3860,13 @@ function applyMaterialDetail(color, material, worldCenter, normal, surfaceType)
|
|
|
3866
3860
|
const sample = worldCenter.x * 3.17 + worldCenter.y * 5.29 + worldCenter.z * 7.83 + getMaterialSeed(materialName) * 0.013;
|
|
3867
3861
|
const grain = (pseudoRandom(sample) - 0.5) * detailStrength;
|
|
3868
3862
|
const lowerSurface = smoothstep(7.5, -0.8, worldCenter.y);
|
|
3869
|
-
const verticalSurface = 1 -
|
|
3863
|
+
const verticalSurface = 1 - clamp(Math.abs(normal.y), 0, 1);
|
|
3870
3864
|
const materialLowerWear = /stone|concrete|plaster|paint|wood|timber|plank|crate/.test(materialName.toLowerCase()) ? lowerSurface * verticalSurface * 0.055 : 0;
|
|
3871
3865
|
const wetlineWear = surfaceType === "ship" && worldCenter.y < 0.72 ? smoothstep(0.72, -0.1, worldCenter.y) * 0.05 : 0;
|
|
3872
3866
|
return {
|
|
3873
|
-
r:
|
|
3874
|
-
g:
|
|
3875
|
-
b:
|
|
3867
|
+
r: clamp(color.r * (1 + grain) - materialLowerWear - wetlineWear, 0, 1),
|
|
3868
|
+
g: clamp(color.g * (1 + grain * 0.82) - materialLowerWear * 0.9 - wetlineWear, 0, 1),
|
|
3869
|
+
b: clamp(color.b * (1 + grain * 0.62) - materialLowerWear * 0.68 - wetlineWear * 0.75, 0, 1)
|
|
3876
3870
|
};
|
|
3877
3871
|
}
|
|
3878
3872
|
function buildCamera(state, canvas) {
|
|
@@ -4298,7 +4292,7 @@ function resizeCanvasToDisplaySize(canvas, state) {
|
|
|
4298
4292
|
const deviceScale = readPositiveNumber(globalThis.devicePixelRatio, 1);
|
|
4299
4293
|
const requestedScale = readPositiveNumber(state.renderScale, deviceScale);
|
|
4300
4294
|
const maxScale = state.captureMode ? 2 : 1.5;
|
|
4301
|
-
let scale =
|
|
4295
|
+
let scale = clamp(requestedScale, 1, maxScale);
|
|
4302
4296
|
const pixelBudget = state.captureMode ? CAPTURE_CANVAS_PIXEL_BUDGET : DEFAULT_CANVAS_WIDTH * DEFAULT_CANVAS_HEIGHT * 1.5;
|
|
4303
4297
|
const projectedPixels = width * height * scale * scale;
|
|
4304
4298
|
if (projectedPixels > pixelBudget) {
|
|
@@ -4483,12 +4477,12 @@ function satisfyClothConstraint(clothState, constraint) {
|
|
|
4483
4477
|
}
|
|
4484
4478
|
}
|
|
4485
4479
|
function advanceShowcaseClothSimulationState(clothState, options = {}) {
|
|
4486
|
-
const dt =
|
|
4480
|
+
const dt = clamp(options.dt ?? 1 / 60, 1 / 240, 1 / 18);
|
|
4487
4481
|
const time = readVisualNumber(options.time, 0);
|
|
4488
4482
|
const flagMotion = readVisualNumber(options.flagMotion, 0.92);
|
|
4489
4483
|
const waveInfluence = readVisualNumber(options.waveInfluence, 0);
|
|
4490
4484
|
const wrinkleLayers = Math.max(1, clothState.representation.mesh?.wrinkleLayers ?? 2);
|
|
4491
|
-
const solverIterations =
|
|
4485
|
+
const solverIterations = clamp(
|
|
4492
4486
|
Math.round(clothState.representation.mesh?.solverIterations ?? 6),
|
|
4493
4487
|
2,
|
|
4494
4488
|
10
|
|
@@ -4731,7 +4725,7 @@ function buildWaterMotionEffects(state) {
|
|
|
4731
4725
|
impulse.z
|
|
4732
4726
|
),
|
|
4733
4727
|
radius,
|
|
4734
|
-
opacity:
|
|
4728
|
+
opacity: clamp(impulse.life * 0.28, 0.08, 0.3)
|
|
4735
4729
|
});
|
|
4736
4730
|
});
|
|
4737
4731
|
for (const ship of state.ships) {
|
|
@@ -4763,7 +4757,7 @@ function buildWaterMotionEffects(state) {
|
|
|
4763
4757
|
}
|
|
4764
4758
|
wakeTrails.push(
|
|
4765
4759
|
Object.freeze({
|
|
4766
|
-
opacity:
|
|
4760
|
+
opacity: clamp(0.18 + speed * 0.09, 0.22, 0.46),
|
|
4767
4761
|
points: Object.freeze(points)
|
|
4768
4762
|
})
|
|
4769
4763
|
);
|
|
@@ -4977,7 +4971,7 @@ function drawSkyAndShore(ctx, canvas, state, nearLighting, reflectionStrength, s
|
|
|
4977
4971
|
const y = pseudoRandom(index * 7 + 5) * canvas.height * 0.42;
|
|
4978
4972
|
const twinkle = 0.45 + Math.sin(state.time * 1.4 + index * 0.73) * 0.25;
|
|
4979
4973
|
const radius = 0.6 + pseudoRandom(index * 11 + 2) * 1.9;
|
|
4980
|
-
ctx.fillStyle = visuals.starColor.replace(/[\d.]+\)$/u, `${
|
|
4974
|
+
ctx.fillStyle = visuals.starColor.replace(/[\d.]+\)$/u, `${clamp(twinkle, 0.16, 0.92)})`);
|
|
4981
4975
|
ctx.beginPath();
|
|
4982
4976
|
ctx.arc(x, y, radius, 0, Math.PI * 2);
|
|
4983
4977
|
ctx.fill();
|
|
@@ -5029,7 +5023,7 @@ function drawSkyAndShore(ctx, canvas, state, nearLighting, reflectionStrength, s
|
|
|
5029
5023
|
if (state.collisionFlash > 0.01) {
|
|
5030
5024
|
ctx.fillStyle = visuals.collisionFlash.replace(
|
|
5031
5025
|
/[\d.]+\)$/u,
|
|
5032
|
-
`${
|
|
5026
|
+
`${clamp(state.collisionFlash * 0.22, 0, 0.26)})`
|
|
5033
5027
|
);
|
|
5034
5028
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
5035
5029
|
}
|
|
@@ -5048,7 +5042,7 @@ function resolveLocalLightContribution(triangle, lightSources) {
|
|
|
5048
5042
|
continue;
|
|
5049
5043
|
}
|
|
5050
5044
|
const lightDir = normalizeVec3(delta);
|
|
5051
|
-
const facing =
|
|
5045
|
+
const facing = clamp(dotVec3(normal, lightDir), 0, 1);
|
|
5052
5046
|
const response = attenuation * (0.18 + facing * 0.82);
|
|
5053
5047
|
const glowColor = source.glowColor ?? source.coreColor ?? { r: 1, g: 0.72, b: 0.4 };
|
|
5054
5048
|
contribution.r += glowColor.r * response * 0.32;
|
|
@@ -5070,31 +5064,31 @@ function drawTriangles(ctx, triangles, lightDir, reflectionStrength, camera, sha
|
|
|
5070
5064
|
triangle.baseColor,
|
|
5071
5065
|
surfaceNormal,
|
|
5072
5066
|
lightDir,
|
|
5073
|
-
|
|
5067
|
+
clamp((triangle.worldCenter.y + 3) / 10, 0, 1),
|
|
5074
5068
|
triangle.accent
|
|
5075
5069
|
);
|
|
5076
5070
|
const reflection = reflectionStrength * (triangle.reflection ?? 0);
|
|
5077
5071
|
const viewDir = normalizeVec3(subVec3(camera.eye, triangle.worldCenter));
|
|
5078
5072
|
const reflectedLight = reflectVec3(scaleVec3(lightDir, -1), surfaceNormal);
|
|
5079
|
-
const gloss = mix(0.78, 0.14,
|
|
5080
|
-
const specularPower = mix(26, 7,
|
|
5081
|
-
const specular = Math.pow(
|
|
5073
|
+
const gloss = mix(0.78, 0.14, clamp(material.roughness ?? 0.88, 0, 1)) + (material.metallic ?? 0) * 0.18;
|
|
5074
|
+
const specularPower = mix(26, 7, clamp(material.roughness ?? 0.88, 0, 1));
|
|
5075
|
+
const specular = Math.pow(clamp(dotVec3(reflectedLight, viewDir), 0, 1), specularPower) * gloss;
|
|
5082
5076
|
const emissive = material.emissive ?? { r: 0, g: 0, b: 0 };
|
|
5083
5077
|
const localLight = resolveLocalLightContribution(triangle, localLights);
|
|
5084
5078
|
const occlusion = triangle.surfaceType === "water" ? shadowStrength * 0.018 : shadowStrength * 0.04;
|
|
5085
5079
|
const detailed = applyMaterialDetail(
|
|
5086
5080
|
{
|
|
5087
|
-
r:
|
|
5081
|
+
r: clamp(
|
|
5088
5082
|
shaded.r + reflection * 0.08 + specular * 0.16 + emissive.r * 0.42 + localLight.r - occlusion,
|
|
5089
5083
|
0,
|
|
5090
5084
|
1
|
|
5091
5085
|
),
|
|
5092
|
-
g:
|
|
5086
|
+
g: clamp(
|
|
5093
5087
|
shaded.g + reflection * 0.08 + specular * 0.16 + emissive.g * 0.42 + localLight.g - occlusion,
|
|
5094
5088
|
0,
|
|
5095
5089
|
1
|
|
5096
5090
|
),
|
|
5097
|
-
b:
|
|
5091
|
+
b: clamp(
|
|
5098
5092
|
shaded.b + reflection * 0.16 + specular * 0.22 + emissive.b * 0.46 + localLight.b - occlusion * 0.5,
|
|
5099
5093
|
0,
|
|
5100
5094
|
1
|
|
@@ -5126,7 +5120,7 @@ function renderProjectedShadow(ctx, worldPoints, camera, viewport, lightDir, opt
|
|
|
5126
5120
|
}
|
|
5127
5121
|
ctx.save();
|
|
5128
5122
|
ctx.globalCompositeOperation = "multiply";
|
|
5129
|
-
ctx.fillStyle = options.color ?? `rgba(12, 24, 36, ${
|
|
5123
|
+
ctx.fillStyle = options.color ?? `rgba(12, 24, 36, ${clamp(options.alpha ?? 0.16, 0, 0.5)})`;
|
|
5130
5124
|
ctx.shadowColor = options.color ?? "rgba(12, 24, 36, 0.22)";
|
|
5131
5125
|
ctx.shadowBlur = options.blur ?? 18;
|
|
5132
5126
|
ctx.beginPath();
|
|
@@ -5363,7 +5357,7 @@ function resolveShipRoute(ship, state, radius) {
|
|
|
5363
5357
|
const crossCurrent = Math.cos(state.time * 0.31 + readVisualNumber(ship.wanderPhase, 0));
|
|
5364
5358
|
const laneCenter = ship.id === "northwind" ? 10.2 + wander * 2.1 + crossCurrent * 0.6 : 7 + wander * 3.3 - crossCurrent * 1.1;
|
|
5365
5359
|
const targetX = ship.routeDirection > 0 ? HARBOR_BOUNDS.maxX - radius * 1.7 : HARBOR_BOUNDS.minX + radius * 1.7;
|
|
5366
|
-
return vec3(targetX, 0,
|
|
5360
|
+
return vec3(targetX, 0, clamp(laneCenter, HARBOR_BOUNDS.minZ + 1.8, HARBOR_BOUNDS.maxZ - 1.8));
|
|
5367
5361
|
}
|
|
5368
5362
|
function updateShipMotion(state, ship, dt, shipModel) {
|
|
5369
5363
|
const physics = shipModel.physics;
|
|
@@ -5476,7 +5470,7 @@ function resolveShipCollision(state, a, b, shipModelA, shipModelB) {
|
|
|
5476
5470
|
a.velocity = subVec3(a.velocity, scaleVec3(impulse, invMassA));
|
|
5477
5471
|
b.velocity = addVec3(b.velocity, scaleVec3(impulse, invMassB));
|
|
5478
5472
|
const tangentSpeed = dotVec3(relativeVelocity, tangent);
|
|
5479
|
-
const frictionMagnitude =
|
|
5473
|
+
const frictionMagnitude = clamp(
|
|
5480
5474
|
-tangentSpeed / Math.max(1e-4, invMassSum),
|
|
5481
5475
|
-impulseMagnitude * 0.16,
|
|
5482
5476
|
impulseMagnitude * 0.16
|
|
@@ -5497,14 +5491,14 @@ function resolveShipCollision(state, a, b, shipModelA, shipModelB) {
|
|
|
5497
5491
|
state.waveImpulses.push({
|
|
5498
5492
|
x: contactPoint.x,
|
|
5499
5493
|
z: contactPoint.z,
|
|
5500
|
-
strength:
|
|
5494
|
+
strength: clamp(0.24 + impactSpeed * 0.46 + penetration * 0.9, 0.2, 1.7),
|
|
5501
5495
|
radius: 0.9 + penetration * 1.4,
|
|
5502
5496
|
life: 1
|
|
5503
5497
|
});
|
|
5504
5498
|
state.collisionCount += 1;
|
|
5505
5499
|
state.collisionFlash = Math.max(
|
|
5506
5500
|
state.collisionFlash,
|
|
5507
|
-
|
|
5501
|
+
clamp(impactSpeed * 0.55 + penetration * 1.8, 0.16, 1)
|
|
5508
5502
|
);
|
|
5509
5503
|
a.collisionCooldown = 0.2;
|
|
5510
5504
|
b.collisionCooldown = 0.2;
|
|
@@ -5620,8 +5614,8 @@ function renderSprays(ctx, sprays, camera, viewport) {
|
|
|
5620
5614
|
if (!projected) {
|
|
5621
5615
|
continue;
|
|
5622
5616
|
}
|
|
5623
|
-
const radius =
|
|
5624
|
-
ctx.fillStyle = `rgba(225, 243, 250, ${
|
|
5617
|
+
const radius = clamp(1 / projected.depth * 260, 1.5, 7.5);
|
|
5618
|
+
ctx.fillStyle = `rgba(225, 243, 250, ${clamp(spray.life / 1.6, 0, 0.9)})`;
|
|
5625
5619
|
ctx.beginPath();
|
|
5626
5620
|
ctx.arc(projected.x, projected.y, radius, 0, Math.PI * 2);
|
|
5627
5621
|
ctx.fill();
|
|
@@ -5731,7 +5725,7 @@ function renderDirectLightGlow(ctx, source, camera, viewport) {
|
|
|
5731
5725
|
if (!projected) {
|
|
5732
5726
|
return;
|
|
5733
5727
|
}
|
|
5734
|
-
const radius =
|
|
5728
|
+
const radius = clamp(1 / projected.depth * 420 * source.glowScale, 4, 34);
|
|
5735
5729
|
const halo = ctx.createRadialGradient(projected.x, projected.y, radius * 0.12, projected.x, projected.y, radius);
|
|
5736
5730
|
halo.addColorStop(0, colorToRgba(source.coreColor, 0.98));
|
|
5737
5731
|
halo.addColorStop(0.5, colorToRgba(source.glowColor, 0.42));
|
|
@@ -5753,7 +5747,7 @@ function renderWaterLightReflection(ctx, source, state, camera, viewport) {
|
|
|
5753
5747
|
if (!projected) {
|
|
5754
5748
|
return;
|
|
5755
5749
|
}
|
|
5756
|
-
const radius =
|
|
5750
|
+
const radius = clamp(1 / projected.depth * 420 * source.glowScale, 4, 34);
|
|
5757
5751
|
const waterline = sampleWave(state, source.point.x, source.point.z, state.time) * 0.22;
|
|
5758
5752
|
const reflectedPoint = vec3(
|
|
5759
5753
|
source.point.x,
|
|
@@ -5842,14 +5836,14 @@ function renderLighthouseBeam(ctx, state, camera, viewport, visuals) {
|
|
|
5842
5836
|
2,
|
|
5843
5837
|
projectedSource.x,
|
|
5844
5838
|
projectedSource.y,
|
|
5845
|
-
|
|
5839
|
+
clamp(beamLength * 0.22, 18, 80)
|
|
5846
5840
|
);
|
|
5847
5841
|
core.addColorStop(0, colorToRgba(visuals.torchCore, 0.58));
|
|
5848
5842
|
core.addColorStop(0.5, colorToRgba(visuals.torchGlow, 0.18));
|
|
5849
5843
|
core.addColorStop(1, colorToRgba(visuals.torchGlow, 0));
|
|
5850
5844
|
ctx.fillStyle = core;
|
|
5851
5845
|
ctx.beginPath();
|
|
5852
|
-
ctx.arc(projectedSource.x, projectedSource.y,
|
|
5846
|
+
ctx.arc(projectedSource.x, projectedSource.y, clamp(beamLength * 0.18, 14, 64), 0, Math.PI * 2);
|
|
5853
5847
|
ctx.fill();
|
|
5854
5848
|
ctx.restore();
|
|
5855
5849
|
}
|
|
@@ -5899,7 +5893,7 @@ function renderWaterMotionEffects(ctx, effects, camera, viewport) {
|
|
|
5899
5893
|
}
|
|
5900
5894
|
const averageDepth = projected.reduce((total, entry) => total + entry.projected.depth, 0) / projected.length;
|
|
5901
5895
|
const averageWidth = projected.reduce((total, entry) => total + entry.width, 0) / projected.length;
|
|
5902
|
-
const baseWidth =
|
|
5896
|
+
const baseWidth = clamp(averageWidth / Math.max(0.25, averageDepth) * 180, 1.6, 5.4);
|
|
5903
5897
|
ctx.strokeStyle = `rgba(146, 194, 236, ${wake.opacity * 0.52})`;
|
|
5904
5898
|
ctx.lineWidth = baseWidth * 1.9;
|
|
5905
5899
|
ctx.lineCap = "round";
|
|
@@ -5945,7 +5939,7 @@ function renderWaterMotionEffects(ctx, effects, camera, viewport) {
|
|
|
5945
5939
|
const radiusX = Math.hypot(xAxis.x - center.x, xAxis.y - center.y);
|
|
5946
5940
|
const radiusY = Math.hypot(zAxis.x - center.x, zAxis.y - center.y);
|
|
5947
5941
|
ctx.strokeStyle = `rgba(216, 235, 255, ${ring.opacity})`;
|
|
5948
|
-
ctx.lineWidth =
|
|
5942
|
+
ctx.lineWidth = clamp((radiusX + radiusY) * 0.02, 1, 3.1);
|
|
5949
5943
|
ctx.beginPath();
|
|
5950
5944
|
ctx.ellipse(center.x, center.y, radiusX, radiusY, 0, 0, Math.PI * 2);
|
|
5951
5945
|
ctx.stroke();
|