@plasius/gpu-renderer 0.2.46 → 0.2.47

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/dist/index.js CHANGED
@@ -1213,6 +1213,202 @@ async function createNativeSceneRenderer(options = {}) {
1213
1213
  }
1214
1214
  }
1215
1215
 
1216
+ // src/canonical-wavefront-meshes.js
1217
+ var STANDARD_TEXTURE_BINDINGS = Object.freeze([
1218
+ ["baseColor", "baseColorTexture"],
1219
+ ["metallicRoughness", "metallicRoughnessTexture"],
1220
+ ["normal", "normalTexture"],
1221
+ ["occlusion", "occlusionTexture"],
1222
+ ["emissive", "emissiveTexture"]
1223
+ ]);
1224
+ var EXTENSION_TEXTURE_BINDINGS = Object.freeze([
1225
+ ["clearcoat", "clearcoat", "clearcoatTexture"],
1226
+ ["clearcoatRoughness", "clearcoat", "clearcoatRoughnessTexture"],
1227
+ ["clearcoatNormal", "clearcoat", "clearcoatNormalTexture"],
1228
+ ["transmission", "transmission", "transmissionTexture"],
1229
+ ["sheenColor", "sheen", "sheenColorTexture"],
1230
+ ["sheenRoughness", "sheen", "sheenRoughnessTexture"]
1231
+ ]);
1232
+ function requireArray(value, label) {
1233
+ if (!Array.isArray(value)) {
1234
+ throw new TypeError(`${label} must be an array.`);
1235
+ }
1236
+ return value;
1237
+ }
1238
+ function requireMap(value, label) {
1239
+ if (!value || typeof value !== "object" || typeof value.get !== "function") {
1240
+ throw new TypeError(`${label} must provide a ReadonlyMap-compatible get method.`);
1241
+ }
1242
+ return value;
1243
+ }
1244
+ function textureSample(binding, textures, label, extra = {}) {
1245
+ if (binding === void 0 || binding === null) {
1246
+ return void 0;
1247
+ }
1248
+ const sample = textures.get(binding.textureId);
1249
+ if (!sample) {
1250
+ throw new Error(`Missing decoded texture sample ${binding.textureId} for ${label}.`);
1251
+ }
1252
+ const width = Number(sample.width);
1253
+ const height = Number(sample.height);
1254
+ const data = sample.data;
1255
+ const dataLength = data && typeof data.length === "number" ? data.length : -1;
1256
+ if (!Number.isInteger(width) || !Number.isInteger(height) || width < 1 || height < 1) {
1257
+ throw new TypeError(`Decoded texture sample ${binding.textureId} has invalid dimensions.`);
1258
+ }
1259
+ if (width > 4096 || height > 4096 || width * height > 4096 * 4096) {
1260
+ throw new RangeError(`Decoded texture sample ${binding.textureId} exceeds the 4096-pixel resource limit.`);
1261
+ }
1262
+ if (!(Array.isArray(data) || ArrayBuffer.isView(data)) || dataLength < width * height * 4) {
1263
+ throw new TypeError(`Decoded texture sample ${binding.textureId} must contain width \xD7 height \xD7 4 RGBA values.`);
1264
+ }
1265
+ return Object.freeze({
1266
+ ...sample,
1267
+ ...binding.texCoordSet === void 0 ? {} : { texCoord: binding.texCoordSet },
1268
+ ...extra
1269
+ });
1270
+ }
1271
+ function cloneMaterialExtensions(material, textures) {
1272
+ const extensions = { ...material.extensions ?? {} };
1273
+ if (material.workflow === "unlit") {
1274
+ const unlitKey = "KHR_materials_unlit";
1275
+ const unlitValue = extensions[unlitKey] ?? extensions.unlit ?? {};
1276
+ if (typeof unlitValue !== "object" || unlitValue === null || Array.isArray(unlitValue)) {
1277
+ throw new TypeError(`Canonical material extension ${unlitKey} must be an object.`);
1278
+ }
1279
+ extensions[unlitKey] = { ...unlitValue, enabled: true };
1280
+ }
1281
+ for (const [bindingName, extensionName, textureKey] of EXTENSION_TEXTURE_BINDINGS) {
1282
+ const binding = material.textures?.[bindingName];
1283
+ if (!binding) {
1284
+ continue;
1285
+ }
1286
+ const extensionKey = `KHR_materials_${extensionName}`;
1287
+ const extensionValue = extensions[extensionKey] ?? extensions[extensionName] ?? {};
1288
+ if (typeof extensionValue !== "object" || extensionValue === null || Array.isArray(extensionValue)) {
1289
+ throw new TypeError(`Canonical material extension ${extensionKey} must be an object.`);
1290
+ }
1291
+ extensions[extensionKey] = {
1292
+ ...extensionValue,
1293
+ [textureKey]: textureSample(binding, textures, `${extensionKey}.${textureKey}`)
1294
+ };
1295
+ }
1296
+ return extensions;
1297
+ }
1298
+ function mapMaterial(material, materialRefId, textures) {
1299
+ if (!material) {
1300
+ return { materialRefId };
1301
+ }
1302
+ if (material.alphaMode === "mask") {
1303
+ throw new Error("Canonical alphaMode mask is unsupported by WavefrontMeshInput.");
1304
+ }
1305
+ if (material.workflow !== "metallic-roughness" && material.workflow !== "unlit") {
1306
+ throw new Error(`Unsupported canonical material workflow: ${material.workflow}.`);
1307
+ }
1308
+ const mapped = {
1309
+ materialRefId,
1310
+ ...material.alphaMode === "blend" ? { materialKind: "transparent" } : {},
1311
+ baseColor: material.baseColorFactor,
1312
+ emission: material.emissiveFactor ? [...material.emissiveFactor, 1] : [0, 0, 0, 1],
1313
+ roughness: material.roughnessFactor,
1314
+ metallic: material.metallicFactor,
1315
+ opacity: material.alphaMode === "opaque" ? 1 : material.baseColorFactor?.[3],
1316
+ clearcoat: material.clearcoatFactor,
1317
+ clearcoatRoughness: material.clearcoatRoughnessFactor,
1318
+ transmission: material.transmissionFactor,
1319
+ sheenColor: material.sheenColorFactor,
1320
+ extensions: cloneMaterialExtensions(material, textures)
1321
+ };
1322
+ for (const [bindingName, inputName] of STANDARD_TEXTURE_BINDINGS) {
1323
+ const binding = material.textures?.[bindingName];
1324
+ if (!binding) {
1325
+ continue;
1326
+ }
1327
+ const extra = bindingName === "normal" && material.normalScale !== void 0 ? { scale: material.normalScale } : bindingName === "occlusion" && material.occlusionStrength !== void 0 ? { strength: material.occlusionStrength } : {};
1328
+ mapped[inputName] = textureSample(binding, textures, inputName, extra);
1329
+ }
1330
+ return mapped;
1331
+ }
1332
+ function createCanonicalWavefrontMeshInputs({ document, geometry, textures = /* @__PURE__ */ new Map() } = {}) {
1333
+ if (!document || typeof document !== "object") {
1334
+ throw new TypeError("Canonical model document is required.");
1335
+ }
1336
+ const meshes = requireArray(document.meshes, "Canonical model meshes");
1337
+ const materials = requireArray(document.materials, "Canonical model materials");
1338
+ const geometryInputs = requireArray(geometry, "Renderer-ready geometry");
1339
+ const textureSamples = requireMap(textures, "Decoded texture samples");
1340
+ const geometryByPrimitiveId = /* @__PURE__ */ new Map();
1341
+ for (const entry of geometryInputs) {
1342
+ if (!entry || typeof entry.primitiveId !== "string" || entry.primitiveId.length === 0) {
1343
+ throw new TypeError("Renderer-ready geometry requires a non-empty primitiveId.");
1344
+ }
1345
+ if (geometryByPrimitiveId.has(entry.primitiveId)) {
1346
+ throw new Error(`Duplicate renderer-ready geometry for primitive ${entry.primitiveId}.`);
1347
+ }
1348
+ geometryByPrimitiveId.set(entry.primitiveId, entry);
1349
+ }
1350
+ const materialsById = /* @__PURE__ */ new Map();
1351
+ const materialRefIds = /* @__PURE__ */ new Map();
1352
+ materials.forEach((material, index) => {
1353
+ if (!material || typeof material.id !== "string" || material.id.length === 0) {
1354
+ throw new TypeError("Canonical materials require a non-empty id.");
1355
+ }
1356
+ if (materialsById.has(material.id)) {
1357
+ throw new Error(`Duplicate canonical material ${material.id}.`);
1358
+ }
1359
+ materialsById.set(material.id, material);
1360
+ materialRefIds.set(material.id, index + 1);
1361
+ });
1362
+ const output = [];
1363
+ const consumedPrimitiveIds = /* @__PURE__ */ new Set();
1364
+ const canonicalPrimitiveIds = /* @__PURE__ */ new Set();
1365
+ for (const mesh of meshes) {
1366
+ if (!mesh || typeof mesh.id !== "string") {
1367
+ throw new TypeError("Canonical meshes require a string id.");
1368
+ }
1369
+ for (const primitive of requireArray(mesh.primitives, `Primitives for mesh ${mesh.id}`)) {
1370
+ if (!primitive || typeof primitive.id !== "string" || primitive.id.length === 0) {
1371
+ throw new TypeError(`Canonical mesh ${mesh.id} contains a primitive without an id.`);
1372
+ }
1373
+ if (canonicalPrimitiveIds.has(primitive.id)) {
1374
+ throw new Error(`Duplicate canonical primitive ${primitive.id}.`);
1375
+ }
1376
+ canonicalPrimitiveIds.add(primitive.id);
1377
+ if (primitive.topology !== "triangles") {
1378
+ throw new Error(`Unsupported canonical primitive topology: ${primitive.topology}.`);
1379
+ }
1380
+ const source = geometryByPrimitiveId.get(primitive.id);
1381
+ if (!source) {
1382
+ throw new Error(`Missing renderer-ready geometry for primitive ${primitive.id}.`);
1383
+ }
1384
+ consumedPrimitiveIds.add(primitive.id);
1385
+ let material = null;
1386
+ let materialRefId = 0;
1387
+ if (primitive.materialId !== void 0) {
1388
+ material = materialsById.get(primitive.materialId);
1389
+ if (!material) {
1390
+ throw new Error(`Missing canonical material ${primitive.materialId} for primitive ${primitive.id}.`);
1391
+ }
1392
+ materialRefId = materialRefIds.get(primitive.materialId);
1393
+ }
1394
+ output.push(Object.freeze({
1395
+ id: output.length + 1,
1396
+ positions: source.positions,
1397
+ ...source.indices === void 0 ? {} : { indices: source.indices },
1398
+ ...source.normals === void 0 ? {} : { normals: source.normals },
1399
+ ...(source.texcoords ?? source.uvs) === void 0 ? {} : { uvs: source.texcoords ?? source.uvs },
1400
+ ...mapMaterial(material, materialRefId, textureSamples)
1401
+ }));
1402
+ }
1403
+ }
1404
+ for (const primitiveId of geometryByPrimitiveId.keys()) {
1405
+ if (!consumedPrimitiveIds.has(primitiveId)) {
1406
+ throw new Error(`Renderer-ready geometry references unknown primitive ${primitiveId}.`);
1407
+ }
1408
+ }
1409
+ return Object.freeze(output);
1410
+ }
1411
+
1216
1412
  // src/animated-scene-renderer.js
1217
1413
  import { resolveCameraRigFrame } from "@plasius/gpu-camera";
1218
1414
 
@@ -14002,6 +14198,7 @@ export {
14002
14198
  bucketFeedbackGameFrameTime,
14003
14199
  bucketFeedbackGameViewport,
14004
14200
  createAnimatedSceneRenderer,
14201
+ createCanonicalWavefrontMeshInputs,
14005
14202
  createDefaultWavefrontSceneObjects,
14006
14203
  createFeedbackGameDiagnosticSnapshot,
14007
14204
  createGpuRenderer,