@vizij/render 0.1.1 → 0.1.2

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
@@ -1006,6 +1006,48 @@ function InnerRenderedShape({
1006
1006
  );
1007
1007
  }
1008
1008
  },
1009
+ roughness: (value) => {
1010
+ const standard = material.current;
1011
+ if (standard?.roughness !== void 0 && (0, import_utils5.instanceOfRawNumber)(value)) {
1012
+ standard.roughness = value;
1013
+ standard.needsUpdate = true;
1014
+ }
1015
+ },
1016
+ metalness: (value) => {
1017
+ const standard = material.current;
1018
+ if (standard?.metalness !== void 0 && (0, import_utils5.instanceOfRawNumber)(value)) {
1019
+ standard.metalness = value;
1020
+ standard.needsUpdate = true;
1021
+ }
1022
+ },
1023
+ shininess: (value) => {
1024
+ const phong = material.current;
1025
+ if (phong?.shininess !== void 0 && (0, import_utils5.instanceOfRawNumber)(value)) {
1026
+ phong.shininess = value;
1027
+ phong.needsUpdate = true;
1028
+ }
1029
+ },
1030
+ emissive: (value) => {
1031
+ const standard = material.current;
1032
+ if (standard?.emissive && (0, import_utils5.instanceOfRawRGB)(value)) {
1033
+ standard.emissive.setRGB(value.r, value.g, value.b);
1034
+ standard.needsUpdate = true;
1035
+ }
1036
+ },
1037
+ emissiveIntensity: (value) => {
1038
+ const standard = material.current;
1039
+ if (standard?.emissiveIntensity !== void 0 && (0, import_utils5.instanceOfRawNumber)(value)) {
1040
+ standard.emissiveIntensity = value;
1041
+ standard.needsUpdate = true;
1042
+ }
1043
+ },
1044
+ specular: (value) => {
1045
+ const phong = material.current;
1046
+ if (phong?.specular && (0, import_utils5.instanceOfRawRGB)(value)) {
1047
+ phong.specular.setRGB(value.r, value.g, value.b);
1048
+ phong.needsUpdate = true;
1049
+ }
1050
+ },
1009
1051
  ...morphTargetSettings[2]
1010
1052
  },
1011
1053
  shape
@@ -1126,7 +1168,10 @@ var MATERIAL_FEATURE_KEYS = [
1126
1168
  "opacity",
1127
1169
  "roughness",
1128
1170
  "metalness",
1129
- "shininess"
1171
+ "shininess",
1172
+ "emissive",
1173
+ "emissiveIntensity",
1174
+ "specular"
1130
1175
  ];
1131
1176
  var MATERIAL_NAME_SUFFIXES = [
1132
1177
  " color",
@@ -1135,7 +1180,10 @@ var MATERIAL_NAME_SUFFIXES = [
1135
1180
  " opacity",
1136
1181
  " roughness",
1137
1182
  " metalness",
1138
- " shininess"
1183
+ " shininess",
1184
+ " emissive intensity",
1185
+ " emissive",
1186
+ " specular"
1139
1187
  ];
1140
1188
  function deriveMaterialName(shape, values) {
1141
1189
  for (const key of MATERIAL_FEATURE_KEYS) {
@@ -1242,19 +1290,20 @@ var import_zustand2 = require("zustand");
1242
1290
  var import_middleware = require("zustand/middleware");
1243
1291
  var import_immer = require("immer");
1244
1292
  var THREE3 = __toESM(require("three"));
1245
- var import_utils6 = require("@vizij/utils");
1293
+ var import_utils8 = require("@vizij/utils");
1246
1294
 
1247
1295
  // src/actions/create-new-element.ts
1248
1296
  var import_react9 = require("react");
1249
1297
  var import_lodash3 = require("lodash");
1250
1298
 
1251
1299
  // src/functions/create-world-element.ts
1300
+ var import_utils6 = require("@vizij/utils");
1252
1301
  function createDefaultGroup(partialBody) {
1253
1302
  const translation = partialBody.features?.translation || { animated: false, value: { x: 0, y: 0 } };
1254
1303
  const rotation = partialBody.features?.rotation || { animated: false, value: { x: 0, y: 0, z: 0 } };
1255
1304
  const scale = partialBody.features?.scale || { animated: false, value: { x: 1, y: 1, z: 1 } };
1256
1305
  return {
1257
- id: partialBody.id || crypto.randomUUID(),
1306
+ id: partialBody.id || (0, import_utils6.createBrowserSafeId)(),
1258
1307
  name: partialBody.name || "new-body",
1259
1308
  type: partialBody.type || "group",
1260
1309
  tags: partialBody.tags || [],
@@ -1315,7 +1364,32 @@ function removeFromTree(state, nodesToRemove) {
1315
1364
  });
1316
1365
  }
1317
1366
 
1367
+ // src/memoryInvestigation.ts
1368
+ function getGlobalMemoryInvestigation() {
1369
+ return globalThis;
1370
+ }
1371
+ function updateRenderState(updater) {
1372
+ const globalObj = getGlobalMemoryInvestigation();
1373
+ if (!globalObj.__VIZIJ_MEMORY_INVESTIGATION__?.enabled) {
1374
+ return;
1375
+ }
1376
+ if (!globalObj.__vizijMemoryDebugState) {
1377
+ globalObj.__vizijMemoryDebugState = {};
1378
+ }
1379
+ if (!globalObj.__vizijMemoryDebugState.render) {
1380
+ globalObj.__vizijMemoryDebugState.render = {};
1381
+ }
1382
+ updater(globalObj.__vizijMemoryDebugState.render);
1383
+ }
1384
+ function recordRenderCounter(key, delta = 1) {
1385
+ updateRenderState((renderState) => {
1386
+ const current = typeof renderState[key] === "number" ? Number(renderState[key]) : 0;
1387
+ renderState[key] = current + delta;
1388
+ });
1389
+ }
1390
+
1318
1391
  // src/functions/create-animatable.ts
1392
+ var import_utils7 = require("@vizij/utils");
1319
1393
  function createAnimatable(value) {
1320
1394
  if (!value.type) {
1321
1395
  return null;
@@ -1325,7 +1399,7 @@ function createAnimatable(value) {
1325
1399
  }
1326
1400
  if (value.type === "euler") {
1327
1401
  const newAnimatable = {
1328
- id: value.id ?? crypto.randomUUID(),
1402
+ id: value.id ?? (0, import_utils7.createBrowserSafeId)(),
1329
1403
  name: `${value.name} Rotation`,
1330
1404
  type: value.type,
1331
1405
  default: value.default ?? { x: 0, y: 0, z: 0 },
@@ -1342,7 +1416,7 @@ function createAnimatable(value) {
1342
1416
  return newAnimatable;
1343
1417
  } else if (value.type === "vector3") {
1344
1418
  const newAnimatable = {
1345
- id: value.id ?? crypto.randomUUID(),
1419
+ id: value.id ?? (0, import_utils7.createBrowserSafeId)(),
1346
1420
  name: `${value.name} Vector3`,
1347
1421
  type: value.type,
1348
1422
  default: value.default ?? { x: 0, y: 0, z: 0 },
@@ -1359,7 +1433,7 @@ function createAnimatable(value) {
1359
1433
  return newAnimatable;
1360
1434
  } else if (value.type === "string") {
1361
1435
  const newAnimatable = {
1362
- id: value.id ?? crypto.randomUUID(),
1436
+ id: value.id ?? (0, import_utils7.createBrowserSafeId)(),
1363
1437
  name: value.name,
1364
1438
  type: value.type,
1365
1439
  default: value.default ?? "Hello World",
@@ -1372,7 +1446,7 @@ function createAnimatable(value) {
1372
1446
  return newAnimatable;
1373
1447
  } else if (value.type === "number") {
1374
1448
  const newAnimatable = {
1375
- id: value.id ?? crypto.randomUUID(),
1449
+ id: value.id ?? (0, import_utils7.createBrowserSafeId)(),
1376
1450
  name: value.name,
1377
1451
  type: value.type,
1378
1452
  default: value.default ?? 0,
@@ -1385,7 +1459,7 @@ function createAnimatable(value) {
1385
1459
  return newAnimatable;
1386
1460
  } else if (value.type === "boolean") {
1387
1461
  const newAnimatable = {
1388
- id: value.id ?? crypto.randomUUID(),
1462
+ id: value.id ?? (0, import_utils7.createBrowserSafeId)(),
1389
1463
  name: value.name,
1390
1464
  type: value.type,
1391
1465
  default: value.default ?? false,
@@ -1398,7 +1472,7 @@ function createAnimatable(value) {
1398
1472
  return newAnimatable;
1399
1473
  } else if (value.type === "rgb") {
1400
1474
  const newAnimatable = {
1401
- id: value.id ?? crypto.randomUUID(),
1475
+ id: value.id ?? (0, import_utils7.createBrowserSafeId)(),
1402
1476
  name: value.name,
1403
1477
  type: value.type,
1404
1478
  default: value.default ?? { r: 0, g: 0, b: 0 },
@@ -1415,7 +1489,7 @@ function createAnimatable(value) {
1415
1489
  return newAnimatable;
1416
1490
  } else if (value.type === "hsl") {
1417
1491
  const newAnimatable = {
1418
- id: value.id ?? crypto.randomUUID(),
1492
+ id: value.id ?? (0, import_utils7.createBrowserSafeId)(),
1419
1493
  name: value.name,
1420
1494
  type: value.type,
1421
1495
  default: value.default ?? { h: 0, s: 0, l: 0 },
@@ -1561,7 +1635,7 @@ var VizijSlice = (set, get) => ({
1561
1635
  setValue: (id, namespace, value) => {
1562
1636
  set(
1563
1637
  (0, import_immer.produce)((state) => {
1564
- const lookupId = (0, import_utils6.getLookup)(namespace, id);
1638
+ const lookupId = (0, import_utils8.getLookup)(namespace, id);
1565
1639
  if (typeof value === "function") {
1566
1640
  const current = state.values.get(lookupId);
1567
1641
  if (current !== void 0) {
@@ -1590,7 +1664,7 @@ var VizijSlice = (set, get) => ({
1590
1664
  set(
1591
1665
  (0, import_immer.produce)((state) => {
1592
1666
  writes.forEach(({ id, namespace, value }) => {
1593
- const lookupId = (0, import_utils6.getLookup)(namespace, id);
1667
+ const lookupId = (0, import_utils8.getLookup)(namespace, id);
1594
1668
  state.values.set(lookupId, value);
1595
1669
  });
1596
1670
  })
@@ -1721,8 +1795,8 @@ var VizijSlice = (set, get) => ({
1721
1795
  setSlot: (parentId, parentNamespace, childId, childNamespace) => {
1722
1796
  set(
1723
1797
  (0, import_immer.produce)((state) => {
1724
- const parentLookupId = (0, import_utils6.getLookup)(parentNamespace, parentId);
1725
- const childLookupId = (0, import_utils6.getLookup)(childNamespace, childId);
1798
+ const parentLookupId = (0, import_utils8.getLookup)(parentNamespace, parentId);
1799
+ const childLookupId = (0, import_utils8.getLookup)(childNamespace, childId);
1726
1800
  state.slotConfig[parentLookupId] = childLookupId;
1727
1801
  })
1728
1802
  );
@@ -1741,12 +1815,13 @@ var VizijSlice = (set, get) => ({
1741
1815
  clearSlot: (parentId, parentNamespace) => {
1742
1816
  set(
1743
1817
  (0, import_immer.produce)((state) => {
1744
- const parentLookupId = (0, import_utils6.getLookup)(parentNamespace, parentId);
1818
+ const parentLookupId = (0, import_utils8.getLookup)(parentNamespace, parentId);
1745
1819
  delete state.slotConfig[parentLookupId];
1746
1820
  })
1747
1821
  );
1748
1822
  },
1749
1823
  setVizij: (scene, animatables) => {
1824
+ recordRenderCounter("rootReplacementCount");
1750
1825
  set({
1751
1826
  world: scene,
1752
1827
  animatables
@@ -1754,6 +1829,7 @@ var VizijSlice = (set, get) => ({
1754
1829
  },
1755
1830
  addWorldElements(world, animatables, replace) {
1756
1831
  if (replace) {
1832
+ recordRenderCounter("rootReplacementCount");
1757
1833
  set({ world, animatables });
1758
1834
  } else {
1759
1835
  set((state) => ({
@@ -1798,7 +1874,7 @@ var import_three = require("three");
1798
1874
  var import_jsx_runtime6 = require("react/jsx-runtime");
1799
1875
  function SelectionGlowEffect({
1800
1876
  enabled = false,
1801
- color = "#ff1010ff",
1877
+ color = "#ff1010",
1802
1878
  opacity = 0.9,
1803
1879
  thresholdAngle = 2
1804
1880
  }) {
@@ -1904,6 +1980,13 @@ function Vizij({
1904
1980
  onPointerMissed
1905
1981
  }) {
1906
1982
  const ctx = (0, import_react11.useContext)(VizijContext);
1983
+ (0, import_react11.useEffect)(() => {
1984
+ recordRenderCounter("canvasMountCount");
1985
+ recordRenderCounter("mountedCanvasCount");
1986
+ return () => {
1987
+ recordRenderCounter("mountedCanvasCount", -1);
1988
+ };
1989
+ }, []);
1907
1990
  if (ctx) {
1908
1991
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1909
1992
  import_fiber2.Canvas,
@@ -1972,7 +2055,7 @@ function InnerVizij({
1972
2055
  makeDefault: true,
1973
2056
  position: [0, 0, 100],
1974
2057
  near: 0.1,
1975
- far: 101
2058
+ far: 1e3
1976
2059
  }
1977
2060
  ),
1978
2061
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react11.Suspense, { fallback: null, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
@@ -2150,6 +2233,7 @@ var import_three5 = require("three");
2150
2233
 
2151
2234
  // src/functions/gltf-loading/import-group.ts
2152
2235
  var import_three4 = require("three");
2236
+ var import_utils11 = require("@vizij/utils");
2153
2237
 
2154
2238
  // src/functions/util.ts
2155
2239
  var import_react15 = require("react");
@@ -2159,8 +2243,10 @@ function namespaceArrayToRefs(namespaces) {
2159
2243
 
2160
2244
  // src/functions/gltf-loading/import-mesh.ts
2161
2245
  var import_three3 = require("three");
2246
+ var import_utils10 = require("@vizij/utils");
2162
2247
 
2163
2248
  // src/functions/gltf-loading/import-geometry.ts
2249
+ var import_utils9 = require("@vizij/utils");
2164
2250
  function sanitizeMorphKey(name, fallbackIndex, used) {
2165
2251
  const baseName = name && name.trim().length > 0 ? name.trim() : `morph_${fallbackIndex + 1}`;
2166
2252
  const slug = baseName.toLowerCase().replace(/[^a-z0-9]+/gi, "_").replace(/^_+|_+$/g, "");
@@ -2184,7 +2270,7 @@ function importGeometry(geometry, mesh) {
2184
2270
  const usedKeys = /* @__PURE__ */ new Set();
2185
2271
  Object.entries(mesh.morphTargetDictionary ?? {}).forEach(
2186
2272
  ([name, index]) => {
2187
- const morphId = crypto.randomUUID();
2273
+ const morphId = (0, import_utils9.createBrowserSafeId)();
2188
2274
  const featureKey = sanitizeMorphKey(name, index, usedKeys);
2189
2275
  morphIds.push(featureKey);
2190
2276
  features[featureKey] = {
@@ -2220,7 +2306,7 @@ function importMesh(mesh, namespaces, colorLookup) {
2220
2306
  let animatables = {};
2221
2307
  let newColorLookup = {};
2222
2308
  const translationAnimatable = {
2223
- id: crypto.randomUUID(),
2309
+ id: (0, import_utils10.createBrowserSafeId)(),
2224
2310
  name: `${mesh.name ?? "Mesh"} translation`,
2225
2311
  type: "vector3",
2226
2312
  default: { x: mesh.position.x, y: mesh.position.y, z: mesh.position.z },
@@ -2236,7 +2322,7 @@ function importMesh(mesh, namespaces, colorLookup) {
2236
2322
  [translationAnimatable.id]: translationAnimatable
2237
2323
  };
2238
2324
  const rotationAnimatable = {
2239
- id: crypto.randomUUID(),
2325
+ id: (0, import_utils10.createBrowserSafeId)(),
2240
2326
  name: `${mesh.name ?? "Mesh"} rotation`,
2241
2327
  type: "euler",
2242
2328
  default: { x: mesh.rotation.x, y: mesh.rotation.y, z: mesh.rotation.z },
@@ -2249,7 +2335,7 @@ function importMesh(mesh, namespaces, colorLookup) {
2249
2335
  };
2250
2336
  animatables = { ...animatables, [rotationAnimatable.id]: rotationAnimatable };
2251
2337
  const scaleAnimatable = {
2252
- id: crypto.randomUUID(),
2338
+ id: (0, import_utils10.createBrowserSafeId)(),
2253
2339
  name: `${mesh.name ?? "Mesh"} scale`,
2254
2340
  type: "vector3",
2255
2341
  default: { x: mesh.scale.x, y: mesh.scale.y, z: mesh.scale.z },
@@ -2260,56 +2346,120 @@ function importMesh(mesh, namespaces, colorLookup) {
2260
2346
  }
2261
2347
  };
2262
2348
  animatables = { ...animatables, [scaleAnimatable.id]: scaleAnimatable };
2263
- const color = mesh.material.color;
2264
- let useEmissive = false;
2265
- if (color.r === 0 && color.g === 0 && color.b === 0 && mesh.material.emissive) {
2266
- color.copy(mesh.material.emissive);
2267
- useEmissive = true;
2268
- }
2269
- const colorName = mesh.material.name;
2270
- const colorAnimatable = {
2271
- id: crypto.randomUUID(),
2272
- name: mesh.material.name ?? `${mesh.name ?? "Mesh"} color`,
2273
- type: "rgb",
2274
- default: { r: color.r, g: color.g, b: color.b },
2275
- constraints: {
2276
- min: [0, 0, 0],
2277
- max: [1, 1, 1]
2278
- },
2349
+ const material = mesh.material;
2350
+ const color = material.color;
2351
+ const colorName = material.name;
2352
+ const label = (suffix) => colorName ? `${colorName} ${suffix}` : `${mesh.name ?? "Mesh"} ${suffix}`;
2353
+ const makeNumberAnimatable = (suffix, value, constraints) => ({
2354
+ id: (0, import_utils10.createBrowserSafeId)(),
2355
+ name: label(suffix),
2356
+ type: "number",
2357
+ default: value,
2358
+ constraints,
2279
2359
  pub: {
2280
2360
  public: true,
2281
- output: mesh.material.name ? `${mesh.material.name} color` : `${mesh.name ?? "Mesh"} color`
2361
+ output: label(suffix)
2282
2362
  }
2283
- };
2284
- const opacityAnimatable = {
2285
- id: crypto.randomUUID(),
2286
- name: mesh.material.name ? `${mesh.material.name} opacity` : `${mesh.name ?? "Mesh"} opacity`,
2287
- type: "number",
2288
- default: mesh.material.opacity,
2363
+ });
2364
+ const makeColorAnimatable = (suffix, value) => ({
2365
+ id: (0, import_utils10.createBrowserSafeId)(),
2366
+ name: label(suffix),
2367
+ type: "rgb",
2368
+ default: { r: value.r, g: value.g, b: value.b },
2289
2369
  constraints: {
2290
- min: 0,
2291
- max: 1
2370
+ min: [0, 0, 0],
2371
+ max: [1, 1, 1]
2292
2372
  },
2293
2373
  pub: {
2294
2374
  public: true,
2295
- output: mesh.material.name ? `${mesh.material.name} opacity` : `${mesh.name ?? "Mesh"} opacity`
2375
+ output: label(suffix)
2296
2376
  }
2377
+ });
2378
+ const colorAnimatable = {
2379
+ ...makeColorAnimatable("color", color),
2380
+ name: material.name ?? `${mesh.name ?? "Mesh"} color`
2297
2381
  };
2298
- let colorId = colorAnimatable.id;
2299
- let opacityId = opacityAnimatable.id;
2382
+ const opacityAnimatable = makeNumberAnimatable("opacity", material.opacity, {
2383
+ min: 0,
2384
+ max: 1
2385
+ });
2386
+ const materialAnimatables = [
2387
+ colorAnimatable,
2388
+ opacityAnimatable
2389
+ ];
2390
+ let roughnessAnimatable;
2391
+ let metalnessAnimatable;
2392
+ let emissiveAnimatable;
2393
+ let emissiveIntensityAnimatable;
2394
+ let shininessAnimatable;
2395
+ let specularAnimatable;
2396
+ if (material.isMeshStandardMaterial) {
2397
+ roughnessAnimatable = makeNumberAnimatable(
2398
+ "roughness",
2399
+ material.roughness,
2400
+ {
2401
+ min: 0,
2402
+ max: 1
2403
+ }
2404
+ );
2405
+ metalnessAnimatable = makeNumberAnimatable(
2406
+ "metalness",
2407
+ material.metalness,
2408
+ {
2409
+ min: 0,
2410
+ max: 1
2411
+ }
2412
+ );
2413
+ emissiveAnimatable = makeColorAnimatable("emissive", material.emissive);
2414
+ emissiveIntensityAnimatable = makeNumberAnimatable(
2415
+ "emissive intensity",
2416
+ material.emissiveIntensity,
2417
+ { min: 0 }
2418
+ );
2419
+ materialAnimatables.push(
2420
+ roughnessAnimatable,
2421
+ metalnessAnimatable,
2422
+ emissiveAnimatable,
2423
+ emissiveIntensityAnimatable
2424
+ );
2425
+ } else if (mesh.material.isMeshPhongMaterial) {
2426
+ const phong = mesh.material;
2427
+ shininessAnimatable = makeNumberAnimatable("shininess", phong.shininess, {
2428
+ min: 0
2429
+ });
2430
+ emissiveAnimatable = makeColorAnimatable("emissive", phong.emissive);
2431
+ emissiveIntensityAnimatable = makeNumberAnimatable(
2432
+ "emissive intensity",
2433
+ phong.emissiveIntensity,
2434
+ { min: 0 }
2435
+ );
2436
+ specularAnimatable = makeColorAnimatable("specular", phong.specular);
2437
+ materialAnimatables.push(
2438
+ shininessAnimatable,
2439
+ emissiveAnimatable,
2440
+ emissiveIntensityAnimatable,
2441
+ specularAnimatable
2442
+ );
2443
+ }
2444
+ let materialIds;
2300
2445
  if (colorName && colorLookup[colorName]) {
2301
- colorId = colorLookup[colorName][0];
2302
- opacityId = colorLookup[colorName][1];
2303
- useEmissive = colorLookup[colorName][2];
2446
+ materialIds = colorLookup[colorName];
2304
2447
  } else {
2305
- animatables = { ...animatables, [colorAnimatable.id]: colorAnimatable };
2306
- animatables = { ...animatables, [opacityAnimatable.id]: opacityAnimatable };
2448
+ materialIds = {
2449
+ color: colorAnimatable.id,
2450
+ opacity: opacityAnimatable.id,
2451
+ roughness: roughnessAnimatable?.id,
2452
+ metalness: metalnessAnimatable?.id,
2453
+ emissive: emissiveAnimatable?.id,
2454
+ emissiveIntensity: emissiveIntensityAnimatable?.id,
2455
+ shininess: shininessAnimatable?.id,
2456
+ specular: specularAnimatable?.id
2457
+ };
2458
+ materialAnimatables.forEach((animatable) => {
2459
+ animatables = { ...animatables, [animatable.id]: animatable };
2460
+ });
2307
2461
  if (colorName) {
2308
- newColorLookup[colorName] = [
2309
- colorAnimatable.id,
2310
- opacityAnimatable.id,
2311
- useEmissive
2312
- ];
2462
+ newColorLookup[colorName] = materialIds;
2313
2463
  }
2314
2464
  }
2315
2465
  const [geometryFeatures, geometryAnimatables, morphTargets] = importGeometry(
@@ -2343,7 +2493,7 @@ function importMesh(mesh, namespaces, colorLookup) {
2343
2493
  id: mesh.uuid,
2344
2494
  name: mesh.name,
2345
2495
  geometry: mesh.geometry,
2346
- material: getShapeMaterial(mesh, useEmissive),
2496
+ material: getShapeMaterial(mesh),
2347
2497
  type: "shape",
2348
2498
  tags: [],
2349
2499
  morphTargets,
@@ -2351,8 +2501,19 @@ function importMesh(mesh, namespaces, colorLookup) {
2351
2501
  translation: { animated: true, value: translationAnimatable.id },
2352
2502
  rotation: { animated: true, value: rotationAnimatable.id },
2353
2503
  scale: { animated: true, value: scaleAnimatable.id },
2354
- color: { animated: true, value: colorId },
2355
- opacity: { animated: true, value: opacityId },
2504
+ color: { animated: true, value: materialIds.color },
2505
+ opacity: { animated: true, value: materialIds.opacity },
2506
+ ...materialIds.roughness ? { roughness: { animated: true, value: materialIds.roughness } } : {},
2507
+ ...materialIds.metalness ? { metalness: { animated: true, value: materialIds.metalness } } : {},
2508
+ ...materialIds.emissive ? { emissive: { animated: true, value: materialIds.emissive } } : {},
2509
+ ...materialIds.emissiveIntensity ? {
2510
+ emissiveIntensity: {
2511
+ animated: true,
2512
+ value: materialIds.emissiveIntensity
2513
+ }
2514
+ } : {},
2515
+ ...materialIds.shininess ? { shininess: { animated: true, value: materialIds.shininess } } : {},
2516
+ ...materialIds.specular ? { specular: { animated: true, value: materialIds.specular } } : {},
2356
2517
  ...geometryFeatures
2357
2518
  },
2358
2519
  children: children.length > 0 ? children : void 0,
@@ -2361,11 +2522,8 @@ function importMesh(mesh, namespaces, colorLookup) {
2361
2522
  world = { ...world, [newShape.id]: newShape };
2362
2523
  return [world, animatables, newShape.id, newColorLookup];
2363
2524
  }
2364
- function getShapeMaterial(mesh, useEmissive) {
2525
+ function getShapeMaterial(mesh) {
2365
2526
  const material = mesh.material;
2366
- if (useEmissive) {
2367
- return "basic" /* Basic */;
2368
- }
2369
2527
  if (material.isMeshStandardMaterial) {
2370
2528
  return "standard" /* Standard */;
2371
2529
  } else if (material.isMeshPhongMaterial) {
@@ -2407,7 +2565,7 @@ function importGroup(group, namespaces, colorLookup, rootBounds) {
2407
2565
  let newColorLookup = {};
2408
2566
  const children = [];
2409
2567
  const translationAnimatable = {
2410
- id: crypto.randomUUID(),
2568
+ id: (0, import_utils11.createBrowserSafeId)(),
2411
2569
  name: `${group.name ?? "Group"} translation`,
2412
2570
  type: "vector3",
2413
2571
  default: { x: group.position.x, y: group.position.y, z: group.position.z },
@@ -2423,7 +2581,7 @@ function importGroup(group, namespaces, colorLookup, rootBounds) {
2423
2581
  [translationAnimatable.id]: translationAnimatable
2424
2582
  };
2425
2583
  const rotationAnimatable = {
2426
- id: crypto.randomUUID(),
2584
+ id: (0, import_utils11.createBrowserSafeId)(),
2427
2585
  name: `${group.name ?? "Group"} rotation`,
2428
2586
  type: "euler",
2429
2587
  default: { x: group.rotation.x, y: group.rotation.y, z: group.rotation.z },
@@ -2436,7 +2594,7 @@ function importGroup(group, namespaces, colorLookup, rootBounds) {
2436
2594
  };
2437
2595
  animatables = { ...animatables, [rotationAnimatable.id]: rotationAnimatable };
2438
2596
  const scaleAnimatable = {
2439
- id: crypto.randomUUID(),
2597
+ id: (0, import_utils11.createBrowserSafeId)(),
2440
2598
  name: `${group.name ?? "Group"} scale`,
2441
2599
  type: "vector3",
2442
2600
  default: { x: group.scale.x, y: group.scale.y, z: group.scale.z },
@@ -2629,7 +2787,10 @@ var MATERIAL_NAME_FEATURE_KEYS = [
2629
2787
  "opacity",
2630
2788
  "roughness",
2631
2789
  "metalness",
2632
- "shininess"
2790
+ "shininess",
2791
+ "emissive",
2792
+ "emissiveIntensity",
2793
+ "specular"
2633
2794
  ];
2634
2795
  var MATERIAL_NAME_SUFFIXES2 = [
2635
2796
  " color",
@@ -2638,7 +2799,10 @@ var MATERIAL_NAME_SUFFIXES2 = [
2638
2799
  " opacity",
2639
2800
  " roughness",
2640
2801
  " metalness",
2641
- " shininess"
2802
+ " shininess",
2803
+ " emissive intensity",
2804
+ " emissive",
2805
+ " specular"
2642
2806
  ];
2643
2807
  function applyStoredRenderableNames(object, data) {
2644
2808
  if (typeof data.name === "string" && data.name.length > 0) {
@@ -2777,10 +2941,10 @@ function deriveRootBounds(group) {
2777
2941
  }
2778
2942
 
2779
2943
  // src/functions/vizij-bundle.ts
2780
- var import_utils7 = require("@vizij/utils");
2944
+ var import_utils12 = require("@vizij/utils");
2781
2945
  var BUNDLE_KEYS = ["VIZIJ_bundle"];
2782
2946
  function cloneBundle(value) {
2783
- return (0, import_utils7.cloneDeepSafe)(value);
2947
+ return (0, import_utils12.cloneDeepSafe)(value);
2784
2948
  }
2785
2949
  function readExtensionValue(extensionContainer) {
2786
2950
  for (const key of BUNDLE_KEYS) {
@@ -2858,12 +3022,29 @@ function applyVizijBundle(object, bundle) {
2858
3022
  const userData = object.userData && typeof object.userData === "object" ? object.userData : {};
2859
3023
  const originalExtensions = userData.gltfExtensions;
2860
3024
  let applied = false;
3025
+ const staleCarriers = [];
2861
3026
  if (bundle) {
2862
3027
  userData.gltfExtensions = {
2863
3028
  ...originalExtensions ?? {},
2864
3029
  VIZIJ_bundle: bundle
2865
3030
  };
2866
3031
  object.userData = userData;
3032
+ object.traverse((node) => {
3033
+ if (node === object) {
3034
+ return;
3035
+ }
3036
+ const extensions = node.userData?.gltfExtensions;
3037
+ if (!extensions || !(BUNDLE_KEYS[0] in extensions)) {
3038
+ return;
3039
+ }
3040
+ staleCarriers.push({ node, extensions });
3041
+ const { [BUNDLE_KEYS[0]]: _stale, ...rest } = extensions;
3042
+ if (Object.keys(rest).length > 0) {
3043
+ node.userData.gltfExtensions = rest;
3044
+ } else {
3045
+ delete node.userData.gltfExtensions;
3046
+ }
3047
+ });
2867
3048
  applied = true;
2868
3049
  }
2869
3050
  return () => {
@@ -2880,12 +3061,16 @@ function applyVizijBundle(object, bundle) {
2880
3061
  delete object.userData;
2881
3062
  }
2882
3063
  }
3064
+ for (const { node, extensions } of staleCarriers) {
3065
+ node.userData.gltfExtensions = extensions;
3066
+ }
3067
+ staleCarriers.length = 0;
2883
3068
  applied = false;
2884
3069
  };
2885
3070
  }
2886
3071
 
2887
3072
  // src/functions/gltf-loading/extract-animations.ts
2888
- var import_utils8 = require("@vizij/utils");
3073
+ var import_utils13 = require("@vizij/utils");
2889
3074
  var CHANNEL_PATH_TO_TRACK_PROPERTY = {
2890
3075
  translation: "position",
2891
3076
  rotation: "quaternion",
@@ -2901,7 +3086,7 @@ function clonePlainObject(value) {
2901
3086
  if (!value) {
2902
3087
  return void 0;
2903
3088
  }
2904
- return (0, import_utils8.cloneDeepSafe)(value);
3089
+ return (0, import_utils13.cloneDeepSafe)(value);
2905
3090
  }
2906
3091
  function inferValueSize(valueType) {
2907
3092
  switch (valueType) {
@@ -3331,20 +3516,12 @@ async function resolveParserJson(parserJson, fallback) {
3331
3516
  return parserJson;
3332
3517
  }
3333
3518
  async function loadGLTF(url, namespaces, aggressiveImport = false, rootBounds) {
3334
- const modelLoader = new import_three_stdlib.GLTFLoader();
3335
- modelLoader.setDRACOLoader(new import_three_stdlib.DRACOLoader());
3336
- const modelData = await modelLoader.loadAsync(url);
3337
- const parserJson = await resolveParserJson(modelData?.parser?.json, {
3338
- url
3339
- });
3340
- const actualizedNamespaces = namespaces.length > 0 ? namespaces : ["default"];
3341
- const asset = parseScene(
3342
- modelData.scene,
3343
- actualizedNamespaces,
3519
+ const asset = await loadGLTFWithBundleInternal(
3520
+ url,
3521
+ namespaces,
3344
3522
  aggressiveImport,
3345
3523
  rootBounds,
3346
- parserJson,
3347
- modelData.animations
3524
+ "url"
3348
3525
  );
3349
3526
  return [asset.world, asset.animatables, asset.animations];
3350
3527
  }
@@ -3353,11 +3530,12 @@ async function loadGLTFFromBlob(blob, namespaces, aggressiveImport = false, root
3353
3530
  if (typeof URL !== "undefined" && typeof URL.createObjectURL === "function") {
3354
3531
  const objectUrl = URL.createObjectURL(blob);
3355
3532
  try {
3356
- const asset = await loadGLTFWithBundle(
3533
+ const asset = await loadGLTFWithBundleInternal(
3357
3534
  objectUrl,
3358
3535
  actualizedNamespaces,
3359
3536
  aggressiveImport,
3360
3537
  rootBounds,
3538
+ "blob",
3361
3539
  { blob }
3362
3540
  );
3363
3541
  return [asset.world, asset.animatables, asset.animations];
@@ -3367,8 +3545,7 @@ async function loadGLTFFromBlob(blob, namespaces, aggressiveImport = false, root
3367
3545
  }
3368
3546
  const arrayBuffer = typeof blob.arrayBuffer === "function" ? await blob.arrayBuffer() : await new Response(blob).arrayBuffer();
3369
3547
  return new Promise((resolve, reject) => {
3370
- const loader = new import_three_stdlib.GLTFLoader();
3371
- loader.setDRACOLoader(new import_three_stdlib.DRACOLoader());
3548
+ const loader = createTrackedGLTFLoader();
3372
3549
  loader.parse(
3373
3550
  arrayBuffer,
3374
3551
  "",
@@ -3412,9 +3589,20 @@ function parseScene(scene, namespaces, aggressiveImport, rootBounds, parserJson,
3412
3589
  const animations = extractVizijAnimations(parserJson, clips);
3413
3590
  return { world, animatables, bundle, animations, scene };
3414
3591
  }
3415
- async function loadGLTFWithBundle(url, namespaces, aggressiveImport = false, rootBounds, parserJsonFallback) {
3592
+ function createTrackedGLTFLoader() {
3593
+ recordRenderCounter("gltfLoadCount");
3594
+ recordRenderCounter("dracoLoaderCreationCount");
3416
3595
  const modelLoader = new import_three_stdlib.GLTFLoader();
3417
3596
  modelLoader.setDRACOLoader(new import_three_stdlib.DRACOLoader());
3597
+ return modelLoader;
3598
+ }
3599
+ async function loadGLTFWithBundleInternal(url, namespaces, aggressiveImport = false, rootBounds, sourceKind = "url", parserJsonFallback) {
3600
+ if (sourceKind === "url") {
3601
+ recordRenderCounter("gltfUrlLoadCount");
3602
+ } else {
3603
+ recordRenderCounter("gltfBlobLoadCount");
3604
+ }
3605
+ const modelLoader = createTrackedGLTFLoader();
3418
3606
  const modelData = await modelLoader.loadAsync(url);
3419
3607
  const parserJson = await resolveParserJson(modelData?.parser?.json, {
3420
3608
  url,
@@ -3430,16 +3618,27 @@ async function loadGLTFWithBundle(url, namespaces, aggressiveImport = false, roo
3430
3618
  modelData.animations
3431
3619
  );
3432
3620
  }
3621
+ async function loadGLTFWithBundle(url, namespaces, aggressiveImport = false, rootBounds, parserJsonFallback) {
3622
+ return loadGLTFWithBundleInternal(
3623
+ url,
3624
+ namespaces,
3625
+ aggressiveImport,
3626
+ rootBounds,
3627
+ "url",
3628
+ parserJsonFallback
3629
+ );
3630
+ }
3433
3631
  async function loadGLTFFromBlobWithBundle(blob, namespaces, aggressiveImport = false, rootBounds) {
3434
3632
  const actualizedNamespaces = namespaces.length > 0 ? namespaces : ["default"];
3435
3633
  if (typeof URL !== "undefined" && typeof URL.createObjectURL === "function") {
3436
3634
  const objectUrl = URL.createObjectURL(blob);
3437
3635
  try {
3438
- return await loadGLTFWithBundle(
3636
+ return await loadGLTFWithBundleInternal(
3439
3637
  objectUrl,
3440
3638
  actualizedNamespaces,
3441
3639
  aggressiveImport,
3442
3640
  rootBounds,
3641
+ "blob",
3443
3642
  { blob }
3444
3643
  );
3445
3644
  } finally {
@@ -3448,8 +3647,7 @@ async function loadGLTFFromBlobWithBundle(blob, namespaces, aggressiveImport = f
3448
3647
  }
3449
3648
  const arrayBuffer = typeof blob.arrayBuffer === "function" ? await blob.arrayBuffer() : await new Response(blob).arrayBuffer();
3450
3649
  return new Promise((resolve, reject) => {
3451
- const loader = new import_three_stdlib.GLTFLoader();
3452
- loader.setDRACOLoader(new import_three_stdlib.DRACOLoader());
3650
+ const loader = createTrackedGLTFLoader();
3453
3651
  loader.parse(
3454
3652
  arrayBuffer,
3455
3653
  "",
@@ -3486,6 +3684,9 @@ async function loadGLTFFromBlobWithBundle(blob, namespaces, aggressiveImport = f
3486
3684
  // src/functions/load-gltf-blob.ts
3487
3685
  var import_three_stdlib2 = require("three-stdlib");
3488
3686
  var loadGltfFromBlob = (blob, namespaces) => {
3687
+ recordRenderCounter("gltfLoadCount");
3688
+ recordRenderCounter("gltfBlobLoadCount");
3689
+ recordRenderCounter("dracoLoaderCreationCount");
3489
3690
  return new Promise((resolve, reject) => {
3490
3691
  const loader = new import_three_stdlib2.GLTFLoader();
3491
3692
  loader.setDRACOLoader(new import_three_stdlib2.DRACOLoader());