@oeave/bakery3 0.1.0 → 0.2.0

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.
Files changed (67) hide show
  1. package/README.md +10 -10
  2. package/dist/bake/index.d.ts +3 -3
  3. package/dist/bake/index.js +4 -4
  4. package/dist/{bake-DaOlenyx.d.ts → bake-DZ-CJR6f.d.ts} +8 -1
  5. package/dist/catalog/index.d.ts +5 -5
  6. package/dist/catalog/index.js +3 -3
  7. package/dist/{chunk-V3QINEEG.js → chunk-62M6XXNX.js} +58 -3
  8. package/dist/chunk-62M6XXNX.js.map +1 -0
  9. package/dist/{chunk-ARMTTZXZ.js → chunk-6FYI6AJM.js} +8 -8
  10. package/dist/chunk-6FYI6AJM.js.map +1 -0
  11. package/dist/{chunk-W6WQ5UP7.js → chunk-APWUCEEB.js} +32 -5
  12. package/dist/chunk-APWUCEEB.js.map +1 -0
  13. package/dist/{chunk-6YVQANMW.js → chunk-HNMSWQU7.js} +262 -16
  14. package/dist/chunk-HNMSWQU7.js.map +1 -0
  15. package/dist/{chunk-HQTUFVMM.js → chunk-JFYUDERE.js} +3 -3
  16. package/dist/chunk-JFYUDERE.js.map +1 -0
  17. package/dist/{chunk-TGFNQBKC.js → chunk-KNUAOILG.js} +5 -5
  18. package/dist/chunk-KNUAOILG.js.map +1 -0
  19. package/dist/chunk-KZLVTSBI.js +191 -0
  20. package/dist/chunk-KZLVTSBI.js.map +1 -0
  21. package/dist/{chunk-MD2U6S4N.js → chunk-LAKXC4WR.js} +218 -142
  22. package/dist/chunk-LAKXC4WR.js.map +1 -0
  23. package/dist/{chunk-25ZXSVQ3.js → chunk-NXBAZGNB.js} +3 -3
  24. package/dist/{chunk-25ZXSVQ3.js.map → chunk-NXBAZGNB.js.map} +1 -1
  25. package/dist/{chunk-54WJDN5R.js → chunk-QRCT5UGZ.js} +4 -4
  26. package/dist/{chunk-54WJDN5R.js.map → chunk-QRCT5UGZ.js.map} +1 -1
  27. package/dist/{chunk-APBOWWSV.js → chunk-UWBP7B54.js} +3 -3
  28. package/dist/chunk-UWBP7B54.js.map +1 -0
  29. package/dist/{chunk-WBIAFIHV.js → chunk-XK35ANPJ.js} +3 -3
  30. package/dist/{chunk-WBIAFIHV.js.map → chunk-XK35ANPJ.js.map} +1 -1
  31. package/dist/{chunk-CX4WTVGH.js → chunk-Z7IYVH22.js} +386 -97
  32. package/dist/chunk-Z7IYVH22.js.map +1 -0
  33. package/dist/devtools/index.d.ts +3 -3
  34. package/dist/devtools/index.js +9 -9
  35. package/dist/index.d.ts +39 -8
  36. package/dist/index.js +10 -10
  37. package/dist/node/index.cjs +266 -141
  38. package/dist/node/index.cjs.map +1 -1
  39. package/dist/node/index.d.cts +152 -87
  40. package/dist/node/index.d.ts +27 -6
  41. package/dist/node/index.js +52 -3
  42. package/dist/node/index.js.map +1 -1
  43. package/dist/{prepare-C5qVvaw4.d.ts → prepare-BtjY4G3q.d.ts} +1 -1
  44. package/dist/presets/index.d.ts +37 -1
  45. package/dist/presets/index.js +4 -4
  46. package/dist/presets/index.js.map +1 -1
  47. package/dist/r3f/index.d.ts +4 -4
  48. package/dist/r3f/index.js +12 -12
  49. package/dist/{room-S5TLCK4F.js → room-FS26KAPQ.js} +6 -6
  50. package/dist/{room-S5TLCK4F.js.map → room-FS26KAPQ.js.map} +1 -1
  51. package/dist/{rooms.gen-vhj6sFW5.d.ts → rooms.gen-DItzBR9k.d.ts} +112 -84
  52. package/dist/session-VCIQEO26.js +9 -0
  53. package/dist/{session-YCQS4MRH.js.map → session-VCIQEO26.js.map} +1 -1
  54. package/dist/{testRun-BIPXaVPb.d.ts → testRun-20OARnQr.d.ts} +12 -3
  55. package/package.json +1 -1
  56. package/dist/chunk-6YVQANMW.js.map +0 -1
  57. package/dist/chunk-AF5BIELI.js +0 -70
  58. package/dist/chunk-AF5BIELI.js.map +0 -1
  59. package/dist/chunk-APBOWWSV.js.map +0 -1
  60. package/dist/chunk-ARMTTZXZ.js.map +0 -1
  61. package/dist/chunk-CX4WTVGH.js.map +0 -1
  62. package/dist/chunk-HQTUFVMM.js.map +0 -1
  63. package/dist/chunk-MD2U6S4N.js.map +0 -1
  64. package/dist/chunk-TGFNQBKC.js.map +0 -1
  65. package/dist/chunk-V3QINEEG.js.map +0 -1
  66. package/dist/chunk-W6WQ5UP7.js.map +0 -1
  67. package/dist/session-YCQS4MRH.js +0 -9
@@ -1,11 +1,11 @@
1
- import { captureCamera } from './chunk-V3QINEEG.js';
2
- import { protocolVersionFor, SDK_VERSION, QUALITY } from './chunk-APBOWWSV.js';
1
+ import { captureCamera } from './chunk-62M6XXNX.js';
2
+ import { protocolVersionFor, SDK_VERSION, QUALITY } from './chunk-UWBP7B54.js';
3
3
  import { BAKERY_NODE_MARKER, builtinFor, tslRuntimeRegistered, toNodeObject } from './chunk-S4AJTLLN.js';
4
- import { findPresetRooms, sha256, environmentSourceFor, isPresetEnvironment, roomPresetSpecOf, roomPresetFactsOf, roomPresetAssetsOf } from './chunk-AF5BIELI.js';
5
- import { idOf, shouldExclude, captureFog, readUserData, scenePath, captureLights, captureRuntime, stampIds, captureEnvironmentSettings, excludedItself, bakeCandidates, resolveProbe, defaultIncludeIds, quantizeBakeView, bakeBounds, isBakeWrittenUv } from './chunk-HQTUFVMM.js';
6
- import { MAX_BAKE_OBJECTS, MAX_BAKE_TEXELS, roomIsPrebaked, roomPrebakeChanges, MAX_BAKE_SIMPLIFY, BAKE_TEXTURE_SIZES, MAX_BAKE_SIMPLIFY_TRIANGLES, BAKE_ATLAS_SIZES } from './chunk-W6WQ5UP7.js';
4
+ import { sha256, environmentSourceFor, isPresetEnvironment } from './chunk-KZLVTSBI.js';
5
+ import { idOf, shouldExclude, captureFog, scenePath, captureLights, captureRuntime, stampIds, captureEnvironmentSettings, excludedItself, readUserData, bakeCandidates, resolveProbe, defaultIncludeIds, quantizeBakeView, bakeBounds, isBakeWrittenUv, ID_KEY } from './chunk-JFYUDERE.js';
6
+ import { findPresetRooms, roomShownBaked, roomPresetFactsOf, roomPresetSpecOf, roomPresetAssetsOf, MAX_BAKE_OBJECTS, MAX_BAKE_TEXELS, roomIsPrebaked, roomPrebakeChanges, MAX_BAKE_SIMPLIFY, BAKE_TEXTURE_SIZES, MAX_BAKE_SIMPLIFY_TRIANGLES, BAKE_ATLAS_SIZES } from './chunk-APWUCEEB.js';
7
7
  import { Bakery3Error } from './chunk-3G5QL4F4.js';
8
- import { Vector3, Vector2, Matrix4, Quaternion } from 'three';
8
+ import { Vector3, Vector2, Matrix4, Quaternion, Color, MeshPhysicalMaterial, MeshStandardMaterial, LinearSRGBColorSpace } from 'three';
9
9
  import { GLTFExporter } from 'three/examples/jsm/exporters/GLTFExporter.js';
10
10
 
11
11
  // src/timeline/easing.ts
@@ -3024,6 +3024,175 @@ function describeNodeType(type) {
3024
3024
  };
3025
3025
  return KNOWN[type] ?? `A "${type}"`;
3026
3026
  }
3027
+ function fallbackFor(material, object) {
3028
+ const own = material.userData?.bakery3?.fallback;
3029
+ if (isRecord(own)) return { values: own, declaredOn: "material" };
3030
+ const objects = readUserData(object).fallback;
3031
+ if (isRecord(objects)) return { values: objects, declaredOn: "object" };
3032
+ return null;
3033
+ }
3034
+ function objectFallbackId(material, object) {
3035
+ return `${idOf(material)}@${idOf(object)}`;
3036
+ }
3037
+ function fallbackMaterialId(material, object, fallback) {
3038
+ return fallback.declaredOn === "object" ? objectFallbackId(material, object) : idOf(material);
3039
+ }
3040
+ function exporterCannotWrite(material) {
3041
+ const m = material;
3042
+ if (m.isShaderMaterial) return true;
3043
+ return typeof m.color?.toArray !== "function";
3044
+ }
3045
+ function fallbackEmits(values) {
3046
+ if (values.emissive === void 0) return false;
3047
+ const intensity = typeof values.emissiveIntensity === "number" ? values.emissiveIntensity : 1;
3048
+ if (!(intensity > 0)) return false;
3049
+ const color = toColor(values.emissive);
3050
+ return Boolean(color && color.r + color.g + color.b > 0);
3051
+ }
3052
+ function materialStandIns(root) {
3053
+ const shared = /* @__PURE__ */ new Map();
3054
+ const standIns = /* @__PURE__ */ new Map();
3055
+ root.traverse((object) => {
3056
+ if (shouldExclude(object)) return;
3057
+ const drawable = object;
3058
+ if (!(drawable.isMesh || drawable.isLine || drawable.isPoints)) return;
3059
+ if (!drawable.material) return;
3060
+ const list = Array.isArray(drawable.material) ? drawable.material : [drawable.material];
3061
+ const own = /* @__PURE__ */ new Map();
3062
+ let changed = false;
3063
+ const written = list.map((material) => {
3064
+ if (!material) return material;
3065
+ const standIn2 = standInFor(material, object, shared, own);
3066
+ if (standIn2 !== material) changed = true;
3067
+ return standIn2;
3068
+ });
3069
+ if (!changed) return;
3070
+ standIns.set(
3071
+ object,
3072
+ Array.isArray(drawable.material) ? written : written[0]
3073
+ );
3074
+ });
3075
+ return standIns;
3076
+ }
3077
+ function standInFor(material, object, shared, own) {
3078
+ const fallback = fallbackFor(material, object);
3079
+ if (fallback?.declaredOn === "object") {
3080
+ let copy2 = own.get(material);
3081
+ if (!copy2) {
3082
+ copy2 = standIn(
3083
+ material,
3084
+ fallback.values,
3085
+ objectFallbackId(material, object)
3086
+ );
3087
+ own.set(material, copy2);
3088
+ }
3089
+ return copy2;
3090
+ }
3091
+ if (!fallback && !exporterCannotWrite(material)) return material;
3092
+ let copy = shared.get(material);
3093
+ if (!copy) {
3094
+ copy = standIn(material, fallback?.values ?? null, idOf(material));
3095
+ shared.set(material, copy);
3096
+ }
3097
+ return copy;
3098
+ }
3099
+ function standIn(material, fallback, id) {
3100
+ const out = fallback ? fromFallback(fallback) : flatGray();
3101
+ out.name = material.name;
3102
+ out.side = material.side;
3103
+ const userData = {
3104
+ ...material.userData,
3105
+ [ID_KEY]: id
3106
+ };
3107
+ if (fallback) {
3108
+ const bakery3 = isRecord(userData.bakery3) ? userData.bakery3 : {};
3109
+ userData.bakery3 = { ...bakery3, fallback };
3110
+ }
3111
+ out.userData = userData;
3112
+ return out;
3113
+ }
3114
+ function flatGray() {
3115
+ const material = new MeshStandardMaterial({ roughness: 1, metalness: 0 });
3116
+ material.color.setRGB(0.8, 0.8, 0.8, LinearSRGBColorSpace);
3117
+ return material;
3118
+ }
3119
+ var NUMBER_FIELDS = [
3120
+ "metalness",
3121
+ "roughness",
3122
+ "clearcoat",
3123
+ "clearcoatRoughness",
3124
+ "transmission",
3125
+ "thickness",
3126
+ "ior",
3127
+ "emissiveIntensity",
3128
+ "opacity",
3129
+ "sheen",
3130
+ "iridescence",
3131
+ "anisotropy"
3132
+ ];
3133
+ function fromFallback(values) {
3134
+ const material = new MeshPhysicalMaterial();
3135
+ const color = toColor(values.color);
3136
+ if (color) material.color.copy(color);
3137
+ const emissive = toColor(values.emissive);
3138
+ if (emissive) material.emissive.copy(emissive);
3139
+ const sheenColor = toColor(values.sheenColor);
3140
+ if (sheenColor) material.sheenColor.copy(sheenColor);
3141
+ const target = material;
3142
+ for (const field of NUMBER_FIELDS) {
3143
+ const value = values[field];
3144
+ if (typeof value === "number" && Number.isFinite(value))
3145
+ target[field] = value;
3146
+ }
3147
+ if (material.opacity < 1) material.transparent = true;
3148
+ return material;
3149
+ }
3150
+ function toColor(value) {
3151
+ if (typeof value !== "string" && typeof value !== "number" && !value?.isColor)
3152
+ return null;
3153
+ return new Color(value);
3154
+ }
3155
+ function substituteMaterials(exporter, standIns) {
3156
+ let undo = () => {
3157
+ };
3158
+ if (standIns.size === 0) return undo;
3159
+ exporter.register((writer) => {
3160
+ const steps = writer;
3161
+ const name = ["processMeshAsync", "processMesh"].find(
3162
+ (key) => typeof steps[key] === "function"
3163
+ );
3164
+ if (name) {
3165
+ const original = steps[name];
3166
+ steps[name] = function(mesh) {
3167
+ const material = standIns.get(mesh);
3168
+ return original.call(
3169
+ this,
3170
+ material ? Object.create(mesh, {
3171
+ material: { value: material, enumerable: true }
3172
+ }) : mesh
3173
+ );
3174
+ };
3175
+ } else {
3176
+ undo = swapOnScene(standIns);
3177
+ }
3178
+ return {};
3179
+ });
3180
+ return () => undo();
3181
+ }
3182
+ function swapOnScene(standIns) {
3183
+ const originals = [];
3184
+ for (const [object, material] of standIns) {
3185
+ const drawable = object;
3186
+ originals.push({ object: drawable, material: drawable.material });
3187
+ drawable.material = material;
3188
+ }
3189
+ return () => {
3190
+ for (const { object, material } of originals) object.material = material;
3191
+ };
3192
+ }
3193
+ function isRecord(value) {
3194
+ return typeof value === "object" && value !== null && !Array.isArray(value);
3195
+ }
3027
3196
 
3028
3197
  // src/compat/analyze.ts
3029
3198
  var DOCS2 = "https://bakery3.com/docs";
@@ -3031,15 +3200,46 @@ var LARGE_TEXTURE_PX = 4096;
3031
3200
  var LARGE_TRIANGLE_COUNT = 1e7;
3032
3201
  var TRANSLATABLE = /* @__PURE__ */ new Set(["MeshStandardMaterial", "MeshPhysicalMaterial"]);
3033
3202
  var APPROXIMATED = {
3034
- MeshBasicMaterial: "Rendered as an emissive surface, since a path tracer has no unlit shading model.",
3035
- MeshLambertMaterial: "Rendered as a rough physically based material. Lambert has no specular term, so highlights will differ.",
3036
- MeshPhongMaterial: "Converted to roughness/metalness. Phong shininess is not a physical parameter, so the specular falloff is an approximation.",
3037
- MeshToonMaterial: "Rendered as flat diffuse. Cel shading is a screen-space stylization with no path-traced equivalent.",
3038
- MeshMatcapMaterial: "Rendered as diffuse using the matcap average. A matcap encodes lighting into a texture, which is exactly what path tracing replaces.",
3039
- MeshNormalMaterial: "Rendered as flat gray. Normal visualization is a debug view, not a surface.",
3040
- MeshDepthMaterial: "Rendered as flat gray. Depth output is a debug view.",
3041
- MeshDistanceMaterial: "Rendered as flat gray. Distance output is a debug view."
3203
+ MeshBasicMaterial: "It shows its color and map unlit, as in the viewport, but it does not light the scene, cast a shadow, or show in reflections or through glass.",
3204
+ MeshLambertMaterial: "It renders as a fully rough, non-metal surface in its color and map.",
3205
+ MeshPhongMaterial: "It renders as a fully rough, non-metal surface in its color and map, like Lambert. Its `shininess` and `specular` are not used, so it has no sharp highlights.",
3206
+ MeshToonMaterial: "It renders as a fully rough, non-metal surface in its color and map, without the cel shading of its `gradientMap`.",
3207
+ MeshMatcapMaterial: "It renders as a fully rough, non-metal surface in its color and map, lit by the scene. The `matcap` texture is not used.",
3208
+ MeshNormalMaterial: "It renders flat gray. It colors a surface by its normals, which is a debug view, not a look.",
3209
+ MeshDepthMaterial: "It renders flat gray. It shows depth, which is a debug view, not a look.",
3210
+ MeshDistanceMaterial: "It renders flat gray. It is made for shadow maps, not for a visible surface."
3042
3211
  };
3212
+ var IGNORED = [
3213
+ {
3214
+ name: "alphaMap",
3215
+ used: (m) => isTexture(m.alphaMap) && Boolean(m.transparent || m.alphaTest > 0 || m.alphaHash),
3216
+ fix: "For a cutout, use the alpha channel of `map`."
3217
+ },
3218
+ {
3219
+ name: "bumpMap",
3220
+ used: (m) => isTexture(m.bumpMap) && m.bumpScale !== 0,
3221
+ fix: "Bake bump detail into a `normalMap`."
3222
+ },
3223
+ {
3224
+ name: "displacementMap",
3225
+ used: (m) => isTexture(m.displacementMap) && m.displacementScale !== 0,
3226
+ fix: "Model the shape, or bake the detail into a `normalMap`."
3227
+ },
3228
+ { name: "specularMap", used: (m) => isTexture(m.specularMap) },
3229
+ { name: "iridescence", used: (m) => m.iridescence > 0 },
3230
+ {
3231
+ name: "iridescenceMap",
3232
+ used: (m) => m.iridescence > 0 && isTexture(m.iridescenceMap)
3233
+ },
3234
+ {
3235
+ name: "iridescenceThicknessMap",
3236
+ used: (m) => m.iridescence > 0 && isTexture(m.iridescenceThicknessMap)
3237
+ },
3238
+ {
3239
+ name: "dispersion",
3240
+ used: (m) => m.dispersion > 0 && m.transmission > 0
3241
+ }
3242
+ ];
3043
3243
  function emitsLight(material) {
3044
3244
  const m = material;
3045
3245
  if (m.isMeshBasicMaterial) return true;
@@ -3052,6 +3252,7 @@ function emitsLight(material) {
3052
3252
  function analyzeScene(root) {
3053
3253
  const issues = [];
3054
3254
  const seenMaterials = /* @__PURE__ */ new Set();
3255
+ const unfaithful = /* @__PURE__ */ new Set();
3055
3256
  const seenTextures = /* @__PURE__ */ new Set();
3056
3257
  let triangles = 0;
3057
3258
  let faithfulTriangles = 0;
@@ -3085,12 +3286,21 @@ function analyzeScene(root) {
3085
3286
  });
3086
3287
  }
3087
3288
  }
3088
- if (seenMaterials.has(material.uuid)) continue;
3089
- seenMaterials.add(material.uuid);
3090
- if (emitsLight(material)) emitters++;
3091
- for (const issue of classifyMaterial(material, object)) {
3289
+ const fallback = fallbackFor(material, object);
3290
+ const key = fallback?.declaredOn === "object" ? `${object.uuid}:${material.uuid}` : material.uuid;
3291
+ if (seenMaterials.has(key)) {
3292
+ if (unfaithful.has(key)) objectIsFaithful = false;
3293
+ continue;
3294
+ }
3295
+ seenMaterials.add(key);
3296
+ if (fallback ? fallbackEmits(fallback.values) : emitsLight(material))
3297
+ emitters++;
3298
+ for (const issue of classifyMaterial(material, object, fallback)) {
3092
3299
  issues.push(issue);
3093
- if (issue.severity === "error") objectIsFaithful = false;
3300
+ if (issue.severity === "error") {
3301
+ objectIsFaithful = false;
3302
+ unfaithful.add(key);
3303
+ }
3094
3304
  }
3095
3305
  }
3096
3306
  if (objectIsFaithful) faithfulTriangles += count;
@@ -3123,98 +3333,142 @@ function collectFallbacks(root) {
3123
3333
  const fallbacks = [];
3124
3334
  const seen = /* @__PURE__ */ new Set();
3125
3335
  root.traverse((object) => {
3336
+ if (shouldExclude(object)) return;
3126
3337
  const mesh = object;
3127
3338
  if (!mesh.isMesh) return;
3128
3339
  const materials = Array.isArray(mesh.material) ? mesh.material : [mesh.material];
3129
3340
  for (const material of materials) {
3130
- if (!material || seen.has(material.uuid)) continue;
3131
- const declared = material.userData?.bakery3?.fallback ?? readUserData(object).fallback;
3132
- if (!declared) continue;
3133
- seen.add(material.uuid);
3341
+ if (!material) continue;
3342
+ const fallback = fallbackFor(material, object);
3343
+ if (!fallback) continue;
3344
+ const materialId = fallbackMaterialId(material, object, fallback);
3345
+ if (seen.has(materialId)) continue;
3346
+ seen.add(materialId);
3134
3347
  fallbacks.push({
3135
- ...declared,
3136
- materialId: idOf(material),
3348
+ ...fallback.values,
3349
+ materialId,
3137
3350
  type: "physical"
3138
3351
  });
3139
3352
  }
3140
3353
  });
3141
3354
  return fallbacks;
3142
3355
  }
3143
- function classifyMaterial(material, object) {
3144
- const hasFallback = Boolean(
3145
- material.userData?.bakery3?.fallback ?? readUserData(object).fallback
3146
- );
3147
- if (isNodeMaterial(material) && !hasFallback) {
3356
+ function classifyMaterial(material, object, fallback) {
3357
+ if (fallback) {
3358
+ return [
3359
+ {
3360
+ code: "material_fallback_used",
3361
+ severity: "info",
3362
+ message: `${labelOf(material)} renders with your declared fallback.`,
3363
+ path: pathOf(object),
3364
+ materialId: fallbackMaterialId(material, object, fallback),
3365
+ materialName: material.name,
3366
+ materialType: material.type,
3367
+ docs: `${DOCS2}/material-fallbacks`
3368
+ }
3369
+ ];
3370
+ }
3371
+ if (isNodeMaterial(material)) {
3148
3372
  const extracted = extractMaterialShader(material);
3149
- return extracted.issues.map((issue) => ({
3373
+ const issues = extracted.issues.map((issue) => ({
3150
3374
  ...issue,
3151
3375
  path: pathOf(object),
3152
3376
  materialId: idOf(material),
3153
3377
  materialName: material.name,
3154
3378
  materialType: material.type
3155
3379
  }));
3380
+ if (!extracted.spec)
3381
+ issues.push(...classicIssues(material, object, classicType(material)));
3382
+ return issues;
3156
3383
  }
3157
- const verdict = classify(material, object, hasFallback);
3158
- return verdict ? [verdict.issue] : [];
3384
+ return classicIssues(material, object, material.type);
3159
3385
  }
3160
- function classify(material, object, hasFallback) {
3161
- if (TRANSLATABLE.has(material.type)) return null;
3162
- if (hasFallback) {
3163
- return {
3164
- issue: {
3165
- code: "material_fallback_used",
3166
- severity: "info",
3167
- message: `${material.type} "${material.name || "unnamed"}" renders with your declared fallback.`,
3168
- path: pathOf(object),
3169
- materialId: idOf(material),
3170
- materialName: material.name,
3171
- materialType: material.type,
3172
- docs: `${DOCS2}/material-fallbacks`
3173
- }
3174
- };
3175
- }
3176
- if (material.type === "ShaderMaterial" || material.type === "RawShaderMaterial") {
3386
+ function classicIssues(material, object, type) {
3387
+ const issues = [];
3388
+ const verdict = classify(material, object, type);
3389
+ if (verdict) issues.push(verdict);
3390
+ if (!exporterCannotWrite(material)) {
3391
+ const ignored = ignoredSlots(material, object);
3392
+ if (ignored) issues.push(ignored);
3393
+ }
3394
+ return issues;
3395
+ }
3396
+ function classify(material, object, type) {
3397
+ if (TRANSLATABLE.has(type)) return null;
3398
+ const about = {
3399
+ path: pathOf(object),
3400
+ materialId: idOf(material),
3401
+ materialName: material.name,
3402
+ materialType: material.type
3403
+ };
3404
+ if (material.isShaderMaterial) {
3177
3405
  return {
3178
- issue: {
3179
- code: "material_custom_shader",
3180
- severity: "error",
3181
- message: `Unsupported material: ${material.name || material.type}`,
3182
- path: pathOf(object),
3183
- materialId: idOf(material),
3184
- materialName: material.name,
3185
- materialType: material.type,
3186
- docs: `${DOCS2}/material-fallbacks`
3187
- }
3406
+ code: "material_custom_shader",
3407
+ severity: "error",
3408
+ message: `${labelOf(material)} renders flat gray, because the render cannot run GLSL. Declare a fallback, or port it to TSL.`,
3409
+ ...about,
3410
+ docs: `${DOCS2}/material-fallbacks`
3188
3411
  };
3189
3412
  }
3190
- const approximation = APPROXIMATED[material.type];
3413
+ const approximation = APPROXIMATED[type];
3191
3414
  if (approximation) {
3192
3415
  return {
3193
- issue: {
3194
- code: "material_approximated",
3195
- severity: "warning",
3196
- message: `${material.type} "${material.name || "unnamed"}" is approximated. ${approximation}`,
3197
- path: pathOf(object),
3198
- materialId: idOf(material),
3199
- materialName: material.name,
3200
- materialType: material.type,
3201
- docs: `${DOCS2}/materials`
3202
- }
3416
+ code: "material_approximated",
3417
+ severity: "warning",
3418
+ message: `${labelOf(material)} is approximated. ${approximation}${emissiveNote(material)}`,
3419
+ ...about,
3420
+ docs: `${DOCS2}/materials`
3203
3421
  };
3204
3422
  }
3423
+ const looks = exporterCannotWrite(material) ? "flat gray" : "as a fully rough, non-metal surface in its color";
3205
3424
  return {
3206
- issue: {
3207
- code: "material_unknown",
3208
- severity: "error",
3209
- message: `Unsupported material: ${material.name || material.type}`,
3210
- path: pathOf(object),
3211
- materialId: idOf(material),
3212
- materialName: material.name,
3213
- materialType: material.type,
3214
- docs: `${DOCS2}/materials`
3215
- }
3425
+ code: "material_unknown",
3426
+ severity: "error",
3427
+ message: `${labelOf(material)} renders ${looks}, because the render does not support this material. Use MeshStandardMaterial or MeshPhysicalMaterial, or declare a fallback.`,
3428
+ ...about,
3429
+ docs: `${DOCS2}/materials`
3430
+ };
3431
+ }
3432
+ function emissiveNote(material) {
3433
+ const m = material;
3434
+ if (m.isMeshStandardMaterial || !m.emissive) return "";
3435
+ if (m.emissiveIntensity === void 0 || m.emissiveIntensity === 1) return "";
3436
+ if (m.emissive.r + m.emissive.g + m.emissive.b <= 0) return "";
3437
+ return " Its `emissiveIntensity` is not used, so it glows at intensity 1.";
3438
+ }
3439
+ function ignoredSlots(material, object) {
3440
+ const record2 = material;
3441
+ const ignored = IGNORED.filter((slot) => slot.used(record2));
3442
+ if (!ignored.length) return null;
3443
+ const names = ignored.map((slot) => `\`${slot.name}\``);
3444
+ const list = names.length === 1 ? names[0] : `${names.slice(0, -1).join(", ")} and ${names[names.length - 1]}`;
3445
+ const fixes = ignored.flatMap((slot) => slot.fix ? [slot.fix] : []);
3446
+ return {
3447
+ code: "material_map_ignored",
3448
+ severity: "warning",
3449
+ message: [
3450
+ `${labelOf(material)} uses ${list}, which the render ignores.`,
3451
+ ...fixes
3452
+ ].join(" "),
3453
+ path: pathOf(object),
3454
+ materialId: idOf(material),
3455
+ materialName: material.name,
3456
+ materialType: material.type,
3457
+ docs: `${DOCS2}/materials#slots-the-render-ignores`
3216
3458
  };
3217
3459
  }
3460
+ function classicType(material) {
3461
+ const m = material;
3462
+ if (m.isMeshPhysicalMaterial) return "MeshPhysicalMaterial";
3463
+ if (m.isMeshStandardMaterial) return "MeshStandardMaterial";
3464
+ return material.type.replace(/NodeMaterial$/, "Material");
3465
+ }
3466
+ function labelOf(material) {
3467
+ return `${material.type} "${material.name || "unnamed"}"`;
3468
+ }
3469
+ function isTexture(value) {
3470
+ return Boolean(value?.isTexture);
3471
+ }
3218
3472
  var TEXTURE_SLOTS = [
3219
3473
  "map",
3220
3474
  "normalMap",
@@ -3268,7 +3522,8 @@ function sceneComplexity(scene, camera) {
3268
3522
  let objects = 0;
3269
3523
  const visit = (object) => {
3270
3524
  if (!object.visible) return;
3271
- if (excludedItself(object) && !rooms.has(object)) return;
3525
+ const shell = Boolean(object.userData?.bakery3RoomShell);
3526
+ if (excludedItself(object) && !rooms.has(object) && !shell) return;
3272
3527
  const mesh = object;
3273
3528
  if (mesh.isMesh && mesh.geometry?.attributes?.position) {
3274
3529
  const position = mesh.geometry.attributes.position;
@@ -3279,7 +3534,7 @@ function sceneComplexity(scene, camera) {
3279
3534
  (_, i) => i % 3 === 0 ? position.getX(i / 3 | 0) : i % 3 === 1 ? position.getY(i / 3 | 0) : position.getZ(i / 3 | 0)
3280
3535
  );
3281
3536
  const materials = Array.isArray(mesh.material) ? mesh.material : [mesh.material];
3282
- const emitter = materials.length > 0 && materials.every((material) => material && emitsLight(material));
3537
+ const emitter = !shell && materials.length > 0 && materials.every((material) => material && emitsLight(material));
3283
3538
  const base = {
3284
3539
  positions,
3285
3540
  indices: index?.array ?? null,
@@ -3297,13 +3552,17 @@ function sceneComplexity(scene, camera) {
3297
3552
  });
3298
3553
  }
3299
3554
  } else {
3300
- objects += 1;
3555
+ if (!shell) objects += 1;
3301
3556
  meshes.push({ ...base, matrix: mesh.matrixWorld.elements });
3302
3557
  }
3303
3558
  }
3304
3559
  for (const child of object.children) visit(child);
3305
3560
  };
3306
3561
  visit(scene);
3562
+ for (const room of rooms) {
3563
+ if (room.visible && roomShownBaked(room))
3564
+ objects += roomPresetFactsOf(room)?.objects ?? 0;
3565
+ }
3307
3566
  const eye = camera.getWorldPosition(new Vector3());
3308
3567
  return complexityOf(meshes, [eye.x, eye.y, eye.z], objects);
3309
3568
  }
@@ -3354,13 +3613,10 @@ async function captureShaders(root) {
3354
3613
  const list = Array.isArray(mesh.material) ? mesh.material : [mesh.material];
3355
3614
  for (const material of list) {
3356
3615
  if (!material || seen.has(material.uuid)) continue;
3616
+ if (!isNodeMaterial(material)) continue;
3617
+ if (fallbackFor(material, object)) continue;
3357
3618
  seen.add(material.uuid);
3358
- const hasFallback = Boolean(
3359
- material.userData?.bakery3?.fallback ?? readUserData(object).fallback
3360
- );
3361
- if (isNodeMaterial(material) && !hasFallback) {
3362
- materials.push({ material, path: pathOf2(object) });
3363
- }
3619
+ materials.push({ material, path: pathOf2(object) });
3364
3620
  }
3365
3621
  });
3366
3622
  for (const { material, path } of materials) {
@@ -3674,8 +3930,10 @@ async function captureScene(options) {
3674
3930
  }
3675
3931
  function exportGLB(scene) {
3676
3932
  const exporter = new GLTFExporter();
3933
+ const standIns = materialStandIns(scene);
3677
3934
  const reattach = detachExcluded(scene);
3678
3935
  const unmute = muteLightWarnings();
3936
+ const unswap = substituteMaterials(exporter, standIns);
3679
3937
  return new Promise((resolve, reject) => {
3680
3938
  exporter.parse(
3681
3939
  scene,
@@ -3709,6 +3967,7 @@ function exportGLB(scene) {
3709
3967
  }
3710
3968
  );
3711
3969
  }).finally(() => {
3970
+ unswap();
3712
3971
  unmute();
3713
3972
  reattach();
3714
3973
  });
@@ -3925,7 +4184,7 @@ function presetRoomBake(scene, candidates, include) {
3925
4184
  const group = rooms[0];
3926
4185
  const room = roomPresetSpecOf(group);
3927
4186
  if (!room) return null;
3928
- if (!roomIsPrebaked(room.name, room.lighting)) {
4187
+ if (!roomShownBaked(group) && !roomIsPrebaked(room.name, room.lighting)) {
3929
4188
  return {
3930
4189
  refused: {
3931
4190
  code: "BakeRoomNotPrebaked",
@@ -4009,15 +4268,31 @@ function isUnder(object, root) {
4009
4268
  function planBake(scene, camera, settings = {}) {
4010
4269
  const quality = settings.quality ?? "standard";
4011
4270
  const textureSize = settings.textureSize ?? 1024;
4012
- const candidates = bakeCandidates({ scene });
4271
+ let candidates = bakeCandidates({ scene });
4013
4272
  let include = resolveInclude(candidates, settings);
4014
4273
  let preset;
4015
4274
  if (settings.room !== "full") {
4016
- const verdict = presetRoomBake(scene, candidates, include);
4275
+ const hide = revealBakedStands(scene);
4276
+ let seen = candidates;
4277
+ let seenInclude = include;
4278
+ if (hide.length) {
4279
+ try {
4280
+ seen = bakeCandidates({ scene });
4281
+ seenInclude = resolveInclude(seen, settings);
4282
+ } finally {
4283
+ for (const undo of hide) undo();
4284
+ }
4285
+ }
4286
+ const verdict = presetRoomBake(scene, seen, seenInclude);
4017
4287
  if (verdict && "ok" in verdict) {
4018
4288
  preset = verdict.ok;
4289
+ candidates = seen;
4019
4290
  const keep = /* @__PURE__ */ new Set([...preset.spanIds]);
4020
- include = include.filter((id) => keep.has(id));
4291
+ include = seenInclude.filter((id) => keep.has(id));
4292
+ if (hide.length) {
4293
+ for (const id of preset.standIds)
4294
+ if (!include.includes(id)) include.push(id);
4295
+ }
4021
4296
  }
4022
4297
  }
4023
4298
  const standIns = resolveSimplify(scene, candidates, settings);
@@ -4202,7 +4477,8 @@ async function prepareBake(options) {
4202
4477
  function holdRoomBack(preset, include) {
4203
4478
  const undo = [];
4204
4479
  const state = preset.group.userData?.bakery3Preset?.roomPreset;
4205
- if (!state?.standIn) return undo;
4480
+ if (!state) return undo;
4481
+ if (!state.standIn && !roomShownBaked(preset.group)) return undo;
4206
4482
  const shipping = new Set(
4207
4483
  include.filter((id) => preset.standIds.includes(id))
4208
4484
  );
@@ -4224,6 +4500,19 @@ function holdRoomBack(preset, include) {
4224
4500
  });
4225
4501
  return undo;
4226
4502
  }
4503
+ function revealBakedStands(scene) {
4504
+ const undo = [];
4505
+ for (const group of findPresetRooms(scene)) {
4506
+ if (!roomShownBaked(group)) continue;
4507
+ const state = group.userData?.bakery3Preset?.roomPreset;
4508
+ if (!state || state.standIn) continue;
4509
+ state.standIn = "stand";
4510
+ undo.push(() => {
4511
+ state.standIn = void 0;
4512
+ });
4513
+ }
4514
+ return undo;
4515
+ }
4227
4516
  function developerUv1(candidates, include) {
4228
4517
  const wanted = new Set(include);
4229
4518
  return candidates.filter((candidate) => {
@@ -4488,5 +4777,5 @@ function resolveInclude(candidates, settings) {
4488
4777
  }
4489
4778
 
4490
4779
  export { OBJECT_PROPERTIES, Timeline, analyzeScene, aspectOf, bakeQuoteOf, bindTimelineContext, captureScene, createTimeline, extractMaterialShader, isNodeMaterial, linearToSrgbHex, outputSize, prepareBake, quoteBake, resolveOutput, sceneComplexity, timelineFromGsap, timelineFromTheatre, tracedTriangles };
4491
- //# sourceMappingURL=chunk-CX4WTVGH.js.map
4492
- //# sourceMappingURL=chunk-CX4WTVGH.js.map
4780
+ //# sourceMappingURL=chunk-Z7IYVH22.js.map
4781
+ //# sourceMappingURL=chunk-Z7IYVH22.js.map