@plasius/gpu-renderer 0.2.54 → 0.2.55

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 CHANGED
@@ -23,6 +23,20 @@ All notable changes to this project will be documented in this file.
23
23
  - **Security**
24
24
  - (placeholder)
25
25
 
26
+ ## [0.2.55] - 2026-10-24
27
+
28
+ - **Added**
29
+ - (placeholder)
30
+
31
+ - **Changed**
32
+ - (placeholder)
33
+
34
+ - **Fixed**
35
+ - Preserve canonical blend alpha exactly once and ignore factor/texture alpha for opaque materials through CPU/GPU packing and material sampling; retain legacy Wavefront alpha composition when mode is omitted.
36
+
37
+ - **Security**
38
+ - (placeholder)
39
+
26
40
  ## [0.2.54] - 2026-10-21
27
41
 
28
42
  - **Added**
@@ -1300,3 +1314,4 @@ All notable changes to this project will be documented in this file.
1300
1314
  [0.2.52]: https://github.com/Plasius-LTD/gpu-renderer/releases/tag/v0.2.52
1301
1315
  [0.2.53]: https://github.com/Plasius-LTD/gpu-renderer/releases/tag/v0.2.53
1302
1316
  [0.2.54]: https://github.com/Plasius-LTD/gpu-renderer/releases/tag/v0.2.54
1317
+ [0.2.55]: https://github.com/Plasius-LTD/gpu-renderer/releases/tag/v0.2.55
package/README.md CHANGED
@@ -74,6 +74,10 @@ The canonical adapter maps the core 0.5 material texture slots; it currently
74
74
  accepts UV0 and identity texture transforms. Other selections/transforms fail
75
75
  explicitly so authored appearance is not silently changed. See the
76
76
  [canonical integration contract](docs/guides/integration.md#canonical-model-ingestion).
77
+ Canonical blend materials multiply authored factor alpha by texture alpha once;
78
+ opaque materials ignore both alpha channels. Direct `WavefrontMeshInput` callers
79
+ can select `alphaMode: "opaque" | "blend"`; omitting it preserves legacy opacity
80
+ composition. Alpha masks remain unsupported by the canonical adapter.
77
81
  For an interactive model viewer with loading, integrity checks, camera gestures
78
82
  and quality controls, use the shared package's `model-inspection` entry.
79
83
 
package/dist/index.cjs CHANGED
@@ -1691,12 +1691,14 @@ function mapMaterial(material, materialRefId, textures) {
1691
1691
  }
1692
1692
  const mapped = {
1693
1693
  materialRefId,
1694
+ alphaMode: material.alphaMode,
1694
1695
  ...material.alphaMode === "blend" ? { materialKind: "transparent" } : {},
1695
1696
  baseColor: material.baseColorFactor,
1696
1697
  emission: material.emissiveFactor ? [...material.emissiveFactor, 1] : [0, 0, 0, 1],
1697
1698
  roughness: material.roughnessFactor,
1698
1699
  metallic: material.metallicFactor,
1699
- opacity: material.alphaMode === "opaque" ? 1 : material.baseColorFactor?.[3],
1700
+ // The authored factor remains in baseColor.a; do not multiply it twice.
1701
+ opacity: 1,
1700
1702
  clearcoat: material.clearcoatFactor,
1701
1703
  clearcoatRoughness: material.clearcoatRoughnessFactor,
1702
1704
  transmission: material.transmissionFactor,
@@ -8377,7 +8379,7 @@ fn sample_surface_material(
8377
8379
  let baseColorTexel = sample_atlas(baseColorAtlasTexture, triangle.baseColorAtlas, uv);
8378
8380
  let baseColor = vec4<f32>(
8379
8381
  clamp(triangle.color.rgb * srgb_to_linear_vec3(baseColorTexel.rgb), vec3<f32>(0.0), vec3<f32>(1.0)),
8380
- clamp(triangle.color.a * baseColorTexel.a, 0.0, 1.0)
8382
+ select(clamp(triangle.color.a * baseColorTexel.a, 0.0, 1.0), 1.0, triangle.textureSettings.w > 0.5)
8381
8383
  );
8382
8384
  let metallicRoughnessTexel = sample_atlas(
8383
8385
  metallicRoughnessAtlasTexture,
@@ -9361,6 +9363,10 @@ function createDefaultWavefrontSceneObjects() {
9361
9363
  ]);
9362
9364
  }
9363
9365
  function normalizeWavefrontMesh(input = {}, meshIndex = 0) {
9366
+ const alphaMode = input.alphaMode;
9367
+ if (alphaMode !== void 0 && alphaMode !== "opaque" && alphaMode !== "blend") {
9368
+ throw new TypeError("mesh alphaMode must be opaque or blend.");
9369
+ }
9364
9370
  const positions = input.positions;
9365
9371
  const positionLength = getArrayLikeLength(positions);
9366
9372
  if (positionLength < 9 || positionLength % 3 !== 0) {
@@ -9390,7 +9396,11 @@ function normalizeWavefrontMesh(input = {}, meshIndex = 0) {
9390
9396
  input.emission ?? input.emissive ?? input.material?.emission ?? input.material?.emissive,
9391
9397
  [0, 0, 0, 1]
9392
9398
  );
9393
- const opacity = clamp2(readFiniteNumber("opacity", input.opacity ?? input.material?.opacity, color[3] ?? 1), 0, 1);
9399
+ const opacity = alphaMode === "opaque" ? 1 : clamp2(
9400
+ readFiniteNumber("opacity", input.opacity ?? input.material?.opacity, alphaMode === "blend" ? 1 : color[3] ?? 1),
9401
+ 0,
9402
+ 1
9403
+ );
9394
9404
  const transmission = clamp2(
9395
9405
  readFiniteNumber("transmission", input.transmission ?? input.material?.transmission, materialExtensions.transmission),
9396
9406
  0,
@@ -9422,6 +9432,7 @@ function normalizeWavefrontMesh(input = {}, meshIndex = 0) {
9422
9432
  0
9423
9433
  ),
9424
9434
  medium,
9435
+ alphaMode,
9425
9436
  color: Object.freeze(color),
9426
9437
  emission: Object.freeze(emission),
9427
9438
  roughness: clamp2(readFiniteNumber("roughness", input.roughness ?? input.material?.roughness, 0.72), 0, 1),
@@ -9573,7 +9584,7 @@ function createMeshTriangleRecords(meshes, gpuMaterialSource = null) {
9573
9584
  clampUnit(mesh.normalTexture?.scale ?? mesh.normalTexture?.strength ?? 1),
9574
9585
  clampUnit(mesh.occlusionTexture?.strength ?? 1),
9575
9586
  clampUnit(mesh.emissiveTexture?.strength ?? 1),
9576
- 0
9587
+ mesh.alphaMode === "opaque" ? 1 : 0
9577
9588
  ]),
9578
9589
  extensionTextures: Object.freeze(
9579
9590
  Object.fromEntries(
@@ -9916,7 +9927,7 @@ function createWavefrontGpuMaterialSource(meshes = []) {
9916
9927
  clampUnit(mesh.normalTexture?.scale ?? mesh.normalTexture?.strength ?? 1),
9917
9928
  clampUnit(mesh.occlusionTexture?.strength ?? 1),
9918
9929
  clampUnit(mesh.emissiveTexture?.strength ?? 1),
9919
- 0
9930
+ mesh.alphaMode === "opaque" ? 1 : 0
9920
9931
  ]);
9921
9932
  });
9922
9933
  return Object.freeze({
@@ -10101,7 +10112,7 @@ function createWavefrontGpuMeshSource(meshes = [], gpuMaterialSourceInput = null
10101
10112
  clampUnit(mesh.normalTexture?.scale ?? mesh.normalTexture?.strength ?? 1),
10102
10113
  clampUnit(mesh.occlusionTexture?.strength ?? 1),
10103
10114
  clampUnit(mesh.emissiveTexture?.strength ?? 1),
10104
- 0
10115
+ mesh.alphaMode === "opaque" ? 1 : 0
10105
10116
  ]);
10106
10117
  vertexCursor += meshVertexCount;
10107
10118
  indexCursor += mesh.indices.length;