@threepipe/plugin-blend-importer 0.1.0 → 0.3.1

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 (47) hide show
  1. package/CHANGELOG.md +53 -3
  2. package/README.md +25 -1
  3. package/dist/BlendLoadPlugin.d.ts +63 -3
  4. package/dist/BlendLoadPlugin.d.ts.map +1 -1
  5. package/dist/decompress.d.ts +10 -0
  6. package/dist/decompress.d.ts.map +1 -0
  7. package/dist/dependencies.txt +31 -1
  8. package/dist/index.d.ts +2 -1
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +1354 -89
  11. package/dist/index.js.map +1 -1
  12. package/dist/index.mjs +1352 -87
  13. package/dist/index.mjs.map +1 -1
  14. package/dist/loader/camera.d.ts +1 -1
  15. package/dist/loader/ctx.d.ts +9 -1
  16. package/dist/loader/ctx.d.ts.map +1 -1
  17. package/dist/loader/geometry.d.ts +2 -2
  18. package/dist/loader/geometry.d.ts.map +1 -1
  19. package/dist/loader/index.d.ts +1 -1
  20. package/dist/loader/light.d.ts +1 -1
  21. package/dist/loader/material.d.ts +4 -1
  22. package/dist/loader/material.d.ts.map +1 -1
  23. package/dist/loader/mesh.d.ts +1 -1
  24. package/dist/loader/mesh.d.ts.map +1 -1
  25. package/docs/assets/highlight.css +49 -0
  26. package/docs/assets/icons.js +1 -1
  27. package/docs/assets/icons.svg +1 -1
  28. package/docs/assets/main.js +2 -2
  29. package/docs/assets/navigation.js +1 -1
  30. package/docs/assets/search.js +1 -1
  31. package/docs/assets/style.css +3 -3
  32. package/docs/classes/BlendLoadPlugin.html +6 -3
  33. package/docs/index.html +1 -1
  34. package/docs/interfaces/BlendFile.html +18 -0
  35. package/docs/interfaces/BlendLoadOptions.html +12 -0
  36. package/package.json +1 -1
  37. package/src/BlendLoadPlugin.ts +204 -13
  38. package/src/decompress.ts +59 -0
  39. package/src/index.ts +1 -0
  40. package/src/js-blend/main.js +4 -1
  41. package/src/js-blend/parser/parser.js +192 -52
  42. package/src/loader/ctx.ts +9 -0
  43. package/src/loader/geometry.ts +270 -13
  44. package/src/loader/material.ts +326 -8
  45. package/src/loader/mesh.ts +34 -8
  46. package/tsconfig.json +1 -0
  47. package/docs/.nojekyll +0 -1
package/dist/index.js CHANGED
@@ -1,14 +1,7 @@
1
1
  (function(global, factory) {
2
2
  typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("threepipe")) : typeof define === "function" && define.amd ? define(["exports", "threepipe"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global["@threepipe/plugin-blend-importer"] = {}, global.threepipe));
3
- })(this, function(exports2, threepipe) {
4
- "use strict";/**
5
- * @license
6
- * @threepipe/plugin-blend-importer v0.1.0
7
- * Copyright 2022-2025 repalash <palash@shaders.app>
8
- * Apache-2.0 License
9
- * See ./dependencies.txt for any bundled third-party dependencies and licenses.
10
- */
11
-
3
+ })(this, (function(exports2, threepipe) {
4
+ "use strict";
12
5
  let worker = null;
13
6
  const return_object = {
14
7
  loadBlendFromArrayBuffer: function(array_buffer) {
@@ -41,6 +34,7 @@
41
34
  struct_names = [];
42
35
  offset = 0;
43
36
  current_SDNA_template = null;
37
+ ERROR = null;
44
38
  _data = msg.data;
45
39
  AB = _data.slice();
46
40
  data = new DataView(_data);
@@ -75,13 +69,30 @@
75
69
  } else {
76
70
  return pointerLow;
77
71
  }
72
+ },
73
+ // Alignment-safe TypedArray read over the file buffer. Consumers (e.g. loader/geometry.ts)
74
+ // that reach into `__blender_file__.AB` at a datablock's `__data_address__` MUST use this
75
+ // instead of `new Ctor(AB, byteOffset, n)` — Blender packs blocks with no padding, so on
76
+ // v1 (17-byte header) and some v0 files the payload lands at an offset that isn't a multiple
77
+ // of the element size, where a direct view throws RangeError. All supported files are
78
+ // little-endian (big-endian is rejected at header parse), so a raw byte copy is correct.
79
+ readTypedArray: function(Ctor, byteOffset, length) {
80
+ return alignedTypedArray(Ctor, this.AB, byteOffset, length);
78
81
  }
79
82
  };
80
83
  self2.onmessage = parseFile;
84
+ function alignedTypedArray(Ctor, buffer, byteOffset, length) {
85
+ if (byteOffset % Ctor.BYTES_PER_ELEMENT === 0) {
86
+ return new Ctor(buffer, byteOffset, length);
87
+ }
88
+ const out = new Ctor(length);
89
+ new Uint8Array(out.buffer).set(new Uint8Array(buffer, byteOffset, length * Ctor.BYTES_PER_ELEMENT));
90
+ return out;
91
+ }
81
92
  function float64Prop(offset2, Blender_Array_Length, length) {
82
93
  return {
83
94
  get: function() {
84
- return Blender_Array_Length > 1 ? new Float64Array(this.__blender_file__.AB, this.__data_address__ + offset2, length) : this.__blender_file__.dv.getFloat64(this.__data_address__ + offset2, this.__blender_file__.template.endianess);
95
+ return Blender_Array_Length > 1 ? alignedTypedArray(Float64Array, this.__blender_file__.AB, this.__data_address__ + offset2, length) : this.__blender_file__.dv.getFloat64(this.__data_address__ + offset2, this.__blender_file__.template.endianess);
85
96
  },
86
97
  set: function(float) {
87
98
  if (Blender_Array_Length > 1) ;
@@ -96,7 +107,7 @@
96
107
  function floatProp(offset2, Blender_Array_Length, length) {
97
108
  return {
98
109
  get: function() {
99
- return Blender_Array_Length > 1 ? new Float32Array(this.__blender_file__.AB, this.__data_address__ + offset2, length) : this.__blender_file__.dv.getFloat32(this.__data_address__ + offset2, this.__blender_file__.template.endianess);
110
+ return Blender_Array_Length > 1 ? alignedTypedArray(Float32Array, this.__blender_file__.AB, this.__data_address__ + offset2, length) : this.__blender_file__.dv.getFloat32(this.__data_address__ + offset2, this.__blender_file__.template.endianess);
100
111
  },
101
112
  set: function(float) {
102
113
  if (Blender_Array_Length > 1) ;
@@ -111,7 +122,7 @@
111
122
  function intProp(offset2, Blender_Array_Length, length) {
112
123
  return {
113
124
  get: function() {
114
- return Blender_Array_Length > 1 ? new Int32Array(this.__blender_file__.AB, this.__data_address__ + offset2, length) : this.__blender_file__.dv.getInt32(this.__data_address__ + offset2, this.__blender_file__.template.endianess);
125
+ return Blender_Array_Length > 1 ? alignedTypedArray(Int32Array, this.__blender_file__.AB, this.__data_address__ + offset2, length) : this.__blender_file__.dv.getInt32(this.__data_address__ + offset2, this.__blender_file__.template.endianess);
115
126
  },
116
127
  set: function(float) {
117
128
  if (Blender_Array_Length > 1) ;
@@ -123,10 +134,25 @@
123
134
  configurable: true
124
135
  };
125
136
  }
137
+ function uIntProp(offset2, Blender_Array_Length, length) {
138
+ return {
139
+ get: function() {
140
+ return Blender_Array_Length > 1 ? alignedTypedArray(Uint32Array, this.__blender_file__.AB, this.__data_address__ + offset2, length) : this.__blender_file__.dv.getUint32(this.__data_address__ + offset2, this.__blender_file__.template.endianess);
141
+ },
142
+ set: function(float) {
143
+ if (Blender_Array_Length > 1) ;
144
+ else {
145
+ this.__blender_file__.dv.setUint32(this.__data_address__ + offset2, float, this.__blender_file__.template.endianess);
146
+ }
147
+ },
148
+ enumerable: true,
149
+ configurable: true
150
+ };
151
+ }
126
152
  function shortProp(offset2, Blender_Array_Length, length) {
127
153
  return {
128
154
  get: function() {
129
- return Blender_Array_Length > 1 ? new Int16Array(this.__blender_file__.AB, this.__data_address__ + offset2, length) : this.__blender_file__.dv.getInt16(this.__data_address__ + offset2, this.__blender_file__.template.endianess);
155
+ return Blender_Array_Length > 1 ? alignedTypedArray(Int16Array, this.__blender_file__.AB, this.__data_address__ + offset2, length) : this.__blender_file__.dv.getInt16(this.__data_address__ + offset2, this.__blender_file__.template.endianess);
130
156
  },
131
157
  set: function(float) {
132
158
  if (Blender_Array_Length > 1) ;
@@ -141,7 +167,7 @@
141
167
  const uShortProp = (offset2, Blender_Array_Length, length) => {
142
168
  return {
143
169
  get: function() {
144
- return Blender_Array_Length > 1 ? new Uint16Array(this.__blender_file__.AB, this.__data_address__ + offset2, length) : this.__blender_file__.dv.getUint16(this.__data_address__ + offset2, this.__blender_file__.template.endianess);
170
+ return Blender_Array_Length > 1 ? alignedTypedArray(Uint16Array, this.__blender_file__.AB, this.__data_address__ + offset2, length) : this.__blender_file__.dv.getUint16(this.__data_address__ + offset2, this.__blender_file__.template.endianess);
145
171
  },
146
172
  set: function(float) {
147
173
  if (Blender_Array_Length > 1) ;
@@ -189,18 +215,28 @@
189
215
  function pointerProp2(offset2) {
190
216
  return {
191
217
  get: function() {
192
- let pointer = this.__blender_file__.getPointer(this.__data_address__ + offset2, this.__blender_file__);
193
- const link = this.__blender_file__.memory_lookup[pointer];
218
+ const bf = this.__blender_file__;
219
+ let pointer = bf.getPointer(this.__data_address__ + offset2, bf);
220
+ const link = bf.memory_lookup[pointer];
194
221
  const results = [];
195
222
  if (link) {
196
223
  const address = link.__data_address__;
197
- let j = 0;
198
- while (true) {
199
- pointer = this.__blender_file__.getPointer(address + j * 8, this.__blender_file__);
200
- let obj = this.__blender_file__.memory_lookup[pointer];
201
- if (!obj) break;
202
- results.push(obj);
203
- j++;
224
+ const ps = bf.template && bf.template.pointer_size || 8;
225
+ const count = typeof link.__byte_length__ === "number" && link.__byte_length__ > 0 ? Math.floor(link.__byte_length__ / ps) : 0;
226
+ if (count > 0) {
227
+ for (let j = 0; j < count; j++) {
228
+ pointer = bf.getPointer(address + j * ps, bf);
229
+ results.push(bf.memory_lookup[pointer] || null);
230
+ }
231
+ } else {
232
+ let j = 0;
233
+ while (true) {
234
+ pointer = bf.getPointer(address + j * ps, bf);
235
+ const obj = bf.memory_lookup[pointer];
236
+ if (!obj) break;
237
+ results.push(obj);
238
+ j++;
239
+ }
204
240
  }
205
241
  }
206
242
  return results;
@@ -236,6 +272,33 @@
236
272
  configurable: true
237
273
  };
238
274
  }
275
+ function int8Prop(offset2, Blender_Array_Length, length) {
276
+ return {
277
+ get: function() {
278
+ return Blender_Array_Length > 1 ? alignedTypedArray(Int8Array, this.__blender_file__.AB, this.__data_address__ + offset2, length) : this.__blender_file__.dv.getInt8(this.__data_address__ + offset2);
279
+ },
280
+ set: function(v) {
281
+ if (Blender_Array_Length <= 1) this.__blender_file__.dv.setInt8(this.__data_address__ + offset2, v);
282
+ },
283
+ enumerable: true,
284
+ configurable: true
285
+ };
286
+ }
287
+ function int64Prop(offset2, Blender_Array_Length, length, unsigned) {
288
+ return {
289
+ get: function() {
290
+ if (Blender_Array_Length > 1) {
291
+ return alignedTypedArray(unsigned ? BigUint64Array : BigInt64Array, this.__blender_file__.AB, this.__data_address__ + offset2, length);
292
+ }
293
+ const dv = this.__blender_file__.dv, le = this.__blender_file__.template.endianess;
294
+ return Number(unsigned ? dv.getBigUint64(this.__data_address__ + offset2, le) : dv.getBigInt64(this.__data_address__ + offset2, le));
295
+ },
296
+ set: function() {
297
+ },
298
+ enumerable: true,
299
+ configurable: true
300
+ };
301
+ }
239
302
  function compileProp(obj, name, type, offset2, array_size, IS_POINTER, pointer_size2, length) {
240
303
  if (!IS_POINTER) {
241
304
  switch (type) {
@@ -246,18 +309,34 @@
246
309
  Object.defineProperty(obj, name, floatProp(offset2, array_size, length >> 2));
247
310
  break;
248
311
  case "int":
312
+ case "int32_t":
249
313
  Object.defineProperty(obj, name, intProp(offset2, array_size, length >> 2));
250
314
  break;
315
+ case "uint32_t":
316
+ Object.defineProperty(obj, name, uIntProp(offset2, array_size, length >> 2));
317
+ break;
251
318
  case "short":
319
+ case "int16_t":
252
320
  Object.defineProperty(obj, name, shortProp(offset2, array_size, length >> 1));
253
321
  break;
254
322
  case "ushort":
323
+ case "uint16_t":
255
324
  Object.defineProperty(obj, name, uShortProp(offset2, array_size, length >> 1));
256
325
  break;
326
+ case "int8_t":
327
+ Object.defineProperty(obj, name, int8Prop(offset2, array_size, length));
328
+ break;
257
329
  case "char":
258
330
  case "uchar":
331
+ case "uint8_t":
259
332
  Object.defineProperty(obj, name, charProp(offset2, array_size, length));
260
333
  break;
334
+ case "int64_t":
335
+ Object.defineProperty(obj, name, int64Prop(offset2, array_size, length >> 3, false));
336
+ break;
337
+ case "uint64_t":
338
+ Object.defineProperty(obj, name, int64Prop(offset2, array_size, length >> 3, true));
339
+ break;
261
340
  default:
262
341
  obj[name] = {};
263
342
  obj.__list__.push(name, type, length, offset2, array_size, IS_POINTER);
@@ -333,7 +412,7 @@
333
412
  offset2 = compileProp(blen_struct.prototype, array_name_, type, offset2, Blender_Array_Length, Pointer_Match, pointer_size, array_length);
334
413
  }
335
414
  Object.defineProperty(blen_struct.prototype, _name, {
336
- get: /* @__PURE__ */ function(array_names2) {
415
+ get: /* @__PURE__ */ (function(array_names2) {
337
416
  return function() {
338
417
  const array = [];
339
418
  for (let i2 = 0; i2 < array_names2.length; i2++) {
@@ -341,7 +420,7 @@
341
420
  }
342
421
  return array;
343
422
  };
344
- }(array_names),
423
+ })(array_names),
345
424
  enumerable: true,
346
425
  configurable: true
347
426
  });
@@ -375,6 +454,7 @@
375
454
  if (this.__list__ === null) return this;
376
455
  BLENDER_FILE2.addObject(this);
377
456
  this.__blender_file__ = BLENDER_FILE2;
457
+ this.__byte_length__ = data_block_length - _data_offset;
378
458
  const struct = this.__list__;
379
459
  let j = 0, i = 0, name = "", type, length, Blender_Array_Length, offset2, constructor;
380
460
  this.__data_address__ = _data_offset;
@@ -437,25 +517,53 @@
437
517
  offset = 0;
438
518
  const magic = toString(_data, offset, 7);
439
519
  if (magic !== "BLENDER") return ERROR = "File supplied is not a .blend compatible Blender file.";
440
- offset += 7;
441
- pointer_size = toString(_data, offset++, offset) == "_" ? 4 : 8;
442
- BIG_ENDIAN = toString(_data, offset++, offset) !== "V";
443
- const version = toString(_data, offset, offset + 3);
444
- if (!templates[version]) {
445
- templates[version] = new MASTER_SDNA_SCHEMA(version);
520
+ let version, large_bhead = false;
521
+ const fmtByte = toString(_data, 7, 8);
522
+ if (fmtByte === "_" || fmtByte === "-") {
523
+ pointer_size = fmtByte === "_" ? 4 : 8;
524
+ BIG_ENDIAN = toString(_data, 8, 9) !== "V";
525
+ version = toString(_data, 9, 12);
526
+ offset = 12;
527
+ } else {
528
+ const headerSize = parseInt(toString(_data, 7, 9), 10);
529
+ if (headerSize !== 17) return ERROR = "Unsupported new-style blend header size: " + headerSize;
530
+ pointer_size = 8;
531
+ BIG_ENDIAN = true;
532
+ large_bhead = true;
533
+ version = toString(_data, 13, 17);
534
+ offset = headerSize;
535
+ }
536
+ if (!BIG_ENDIAN) {
537
+ return ERROR = "Big-endian .blend files are not supported";
538
+ }
539
+ const BLOCK_HDR = large_bhead ? 32 : 16 + pointer_size;
540
+ const SDNA_OFF = large_bhead ? 4 : 8 + pointer_size;
541
+ const LEN_OFF = large_bhead ? 16 : 4;
542
+ const NR_OFF = large_bhead ? 24 : 12 + pointer_size;
543
+ const LEN_I64 = large_bhead;
544
+ const NR_I64 = large_bhead;
545
+ const readLen = (o) => LEN_I64 ? Number(data.getBigInt64(o + LEN_OFF, true)) : data.getInt32(o + LEN_OFF, true);
546
+ const readSdna = (o) => data.getInt32(o + SDNA_OFF, BIG_ENDIAN);
547
+ const readNr = (o) => NR_I64 ? Number(data.getBigInt64(o + NR_OFF, true)) : data.getInt32(o + NR_OFF, BIG_ENDIAN);
548
+ const templateKey = (large_bhead ? "v1:" : "v0:") + version;
549
+ if (!templates[templateKey]) {
550
+ templates[templateKey] = new MASTER_SDNA_SCHEMA(version);
446
551
  }
447
- current_SDNA_template = templates[version];
552
+ current_SDNA_template = templates[templateKey];
448
553
  FILE.template = current_SDNA_template;
449
- offset += 3;
450
554
  if (!current_SDNA_template.SDNA_SET) {
451
555
  current_SDNA_template.endianess = BIG_ENDIAN;
452
556
  current_SDNA_template.pointer_size = pointer_size;
453
557
  offset2 = offset;
454
558
  while (true) {
455
- sdna_index = data.getInt32(offset2 + pointer_size + 8, BIG_ENDIAN);
559
+ if (offset2 + BLOCK_HDR > data.byteLength) {
560
+ ERROR = "Unexpected end of file while searching for DNA1";
561
+ break;
562
+ }
563
+ sdna_index = readSdna(offset2);
456
564
  code = toString(_data, offset2, offset2 + 4).replace(/\u0000/g, "");
457
- block_length = data.getInt32(offset2 + 4, true);
458
- offset2 += 16 + pointer_size;
565
+ block_length = readLen(offset2);
566
+ offset2 += BLOCK_HDR;
459
567
  if (code === "DNA1") {
460
568
  count = 0;
461
569
  let types = [], names = [], lengths = [], curr_name = "";
@@ -553,27 +661,24 @@
553
661
  }
554
662
  break;
555
663
  }
556
- if (data.getInt8(offset) === 0) {
557
- debugger;
558
- }
559
664
  data_offset = offset;
560
- if (offset + pointer_size + 12 >= data.byteLength) {
665
+ if (offset + BLOCK_HDR > data.byteLength) {
561
666
  ERROR = "Unexpected end of file while parsing";
562
667
  break;
563
668
  }
564
- sdna_index = data.getInt32(offset + pointer_size + 8, BIG_ENDIAN);
669
+ sdna_index = readSdna(offset);
565
670
  const code_str = toString(_data, offset, offset + 4);
566
- offset2 = offset + 16 + pointer_size;
567
- const blockLength = data.getInt32(offset + 4, true);
568
- if (blockLength < 0 || offset + blockLength + 16 + pointer_size > data.byteLength) {
671
+ offset2 = offset + BLOCK_HDR;
672
+ const blockLength = readLen(offset);
673
+ if (blockLength < 0 || offset + blockLength + BLOCK_HDR > data.byteLength) {
569
674
  ERROR = "Invalid block length detected";
570
675
  break;
571
676
  }
572
- offset += blockLength + 16 + pointer_size;
677
+ offset += blockLength + BLOCK_HDR;
573
678
  if (code_str === "DNA1") ;
574
679
  else if (code_str === "ENDB") break;
575
680
  else if (code_str === "TEST") {
576
- const data_start = data_offset + pointer_size + 16;
681
+ const data_start = data_offset + BLOCK_HDR;
577
682
  const width = data.getInt32(data_start, BIG_ENDIAN);
578
683
  const height = data.getInt32(data_start + 4, BIG_ENDIAN);
579
684
  if (width * height > 0) {
@@ -582,7 +687,7 @@
582
687
  ERROR = "Invalid TEST block length detected";
583
688
  break;
584
689
  }
585
- const image_data = new Uint32Array(_data, data_start + 8, data_len >> 2);
690
+ const image_data = alignedTypedArray(Uint32Array, _data, data_start + 8, data_len >> 2);
586
691
  const image = {
587
692
  width,
588
693
  height,
@@ -591,10 +696,10 @@
591
696
  FILE.thumbnail = image;
592
697
  }
593
698
  } else {
594
- const data_start = data_offset + pointer_size + 16;
699
+ const data_start = data_offset + BLOCK_HDR;
595
700
  const constructor = current_SDNA_template.getSDNAStructureConstructor(current_SDNA_template.SDNA_NAMES[sdna_index]);
596
- const size = data.getInt32(data_offset + 4, BIG_ENDIAN);
597
- count = data.getInt32(data_offset + 12 + pointer_size, BIG_ENDIAN);
701
+ const size = readLen(data_offset);
702
+ count = readNr(data_offset);
598
703
  if (count > 0 && constructor) {
599
704
  let obj = new constructor();
600
705
  const length = constructor.prototype._length;
@@ -619,18 +724,9 @@
619
724
  }
620
725
  }
621
726
  worker = new worker_code();
622
- worker.postMessage = function(message, err) {
623
- return_object.onParseReady(message, err);
727
+ worker.postMessage = function(message, err2) {
728
+ return_object.onParseReady(message, err2);
624
729
  };
625
- /**
626
- * JS.Blend
627
- * Original Repo: https://github.com/acweathersby/js.blend
628
- * Slightly modified for three.js and js updates, minor refactor.
629
- * Object-creation part re-written for latest three.js and typescript
630
- * MIT License
631
- * Copyright (c) 2020 Anthony C, Weathersby
632
- * @license
633
- */
634
730
  async function parseBlend(buffer, name = "") {
635
731
  return new Promise((res, rej) => {
636
732
  return_object.onParseReady = (file, error) => {
@@ -719,14 +815,147 @@
719
815
  if (!Array.isArray(layers)) return layers;
720
816
  return layers[i];
721
817
  }
818
+ function readCString(block) {
819
+ if (!block || block.__data_address__ === void 0 || !block.__blender_file__) return null;
820
+ const u82 = block.__blender_file__.byte;
821
+ let s = "", o = block.__data_address__;
822
+ while (o < u82.length && u82[o] !== 0 && s.length < 256) s += String.fromCharCode(u82[o++]);
823
+ return s;
824
+ }
825
+ const ATTR_TYPE_INT32 = 3, ATTR_TYPE_FLOAT2 = 6, ATTR_TYPE_FLOAT3 = 7;
826
+ const ATTR_DOMAIN_CORNER = 3;
827
+ function getAttributes(meshData) {
828
+ const as = meshData.attribute_storage;
829
+ if (!as) return [];
830
+ let attrs = as.dna_attributes;
831
+ if (!attrs) return [];
832
+ attrs = Array.isArray(attrs) || attrs.length !== void 0 ? Array.from(attrs) : [attrs];
833
+ return attrs;
834
+ }
835
+ function findAttribute(attrs, name, type) {
836
+ for (const a of attrs) {
837
+ if (readCString(a.name) === name && (type === void 0 || a.data_type === type)) return a;
838
+ }
839
+ return null;
840
+ }
841
+ function attrSize(attr) {
842
+ const arr = attr && attr.data;
843
+ const n = arr && arr.size !== void 0 ? Number(arr.size) : 0;
844
+ return Number.isFinite(n) && n >= 0 ? n : 0;
845
+ }
846
+ function readAttrArray(attr, Ctor, count) {
847
+ const arr = attr && attr.data;
848
+ const block = arr && arr.data;
849
+ if (!block || block.__data_address__ === void 0 || !block.__blender_file__) return null;
850
+ return block.__blender_file__.readTypedArray(Ctor, block.__data_address__, count);
851
+ }
852
+ function mergeGroupRun(runs, start, count, mat) {
853
+ if (count <= 0) return;
854
+ const last = runs[runs.length - 1];
855
+ if (last && last.mat === mat && last.start + last.count === start) last.count += count;
856
+ else runs.push({ start, count, mat });
857
+ }
858
+ function readFaceMaterialIndex(meshData, faceCount) {
859
+ const pd = meshData.pdata;
860
+ if (!pd || !pd.layers || !pd.totlayer) return null;
861
+ for (let i = 0; i < pd.totlayer; i++) {
862
+ const l = getLayer(pd.layers, i);
863
+ if (l && l.name === "material_index" && l.data) {
864
+ const data = Array.isArray(l.data) ? l.data : l.data.length !== void 0 ? Array.from(l.data) : null;
865
+ if (!data) return null;
866
+ const out = new Array(faceCount);
867
+ for (let f = 0; f < faceCount; f++) {
868
+ const e = data[f];
869
+ out[f] = e && typeof e === "object" ? e.i ?? e.value ?? 0 : e || 0;
870
+ }
871
+ return out;
872
+ }
873
+ }
874
+ return null;
875
+ }
876
+ function createBufferGeometryFromAttributes(meshData, ctx) {
877
+ const attrs = getAttributes(meshData);
878
+ if (!attrs.length) return null;
879
+ const posAttr = findAttribute(attrs, "position", ATTR_TYPE_FLOAT3);
880
+ const vertCount = attrSize(posAttr);
881
+ const posRaw = vertCount > 0 && readAttrArray(posAttr, Float32Array, vertCount * 3);
882
+ if (!posRaw) return null;
883
+ const geometry = new ctx.BufferGeometry();
884
+ geometry.name = meshData.aname || "";
885
+ const positions = new Float32Array(vertCount * 3);
886
+ for (let j = 0; j < vertCount; j++) {
887
+ const x = posRaw[j * 3], y = posRaw[j * 3 + 1], z = posRaw[j * 3 + 2];
888
+ positions[j * 3] = Number.isFinite(x) ? x : 0;
889
+ positions[j * 3 + 1] = Number.isFinite(z) ? z : 0;
890
+ positions[j * 3 + 2] = Number.isFinite(y) ? -y : 0;
891
+ }
892
+ geometry.setAttribute("position", new ctx.BufferAttribute(positions, 3));
893
+ const cvAttr = findAttribute(attrs, ".corner_vert", ATTR_TYPE_INT32);
894
+ const loopCount = attrSize(cvAttr);
895
+ const cornerVerts = loopCount > 0 && readAttrArray(cvAttr, Int32Array, loopCount);
896
+ const poi = meshData.poly_offset_indices;
897
+ const faceOffsetCount = poi && poi.__byte_length__ ? poi.__byte_length__ >> 2 : 0;
898
+ if (cornerVerts && faceOffsetCount > 1 && poi) {
899
+ const faceIndices = poi.__blender_file__.readTypedArray(Int32Array, poi.__data_address__, faceOffsetCount);
900
+ const faceCount = faceIndices.length - 1;
901
+ let totalTriangles = 0;
902
+ for (let i = 0; i < faceCount; i++) totalTriangles += Math.max(0, faceIndices[i + 1] - faceIndices[i] - 2);
903
+ const indexes = new Uint32Array(totalTriangles * 3);
904
+ const inRange = (v) => v >= 0 && v < vertCount;
905
+ const matAttr = (meshData.totcol || 0) > 1 ? findAttribute(attrs, "material_index", ATTR_TYPE_INT32) : null;
906
+ const faceMat = matAttr && attrSize(matAttr) >= faceCount ? readAttrArray(matAttr, Int32Array, faceCount) : null;
907
+ const groupRuns = [];
908
+ let t = 0;
909
+ for (let i = 0; i < faceCount; i++) {
910
+ const faceStart = faceIndices[i], faceVertCount = faceIndices[i + 1] - faceStart;
911
+ const triStart = t;
912
+ if (faceVertCount >= 3 && faceStart >= 0 && faceStart + faceVertCount <= loopCount) {
913
+ const firstVert = cornerVerts[faceStart];
914
+ for (let k = 1; k < faceVertCount - 1; k++) {
915
+ const b = cornerVerts[faceStart + k], c = cornerVerts[faceStart + k + 1];
916
+ if (inRange(firstVert) && inRange(b) && inRange(c)) {
917
+ indexes[t++] = firstVert;
918
+ indexes[t++] = b;
919
+ indexes[t++] = c;
920
+ }
921
+ }
922
+ }
923
+ if (faceMat) mergeGroupRun(groupRuns, triStart, t - triStart, faceMat[i] || 0);
924
+ }
925
+ geometry.setIndex(new ctx.BufferAttribute(t === indexes.length ? indexes : indexes.slice(0, t), 1));
926
+ if (faceMat) for (const g of groupRuns) geometry.addGroup(g.start, g.count, g.mat);
927
+ const uvAttr = attrs.find((a) => a.data_type === ATTR_TYPE_FLOAT2 && a.domain === ATTR_DOMAIN_CORNER && !(readCString(a.name) || ".").startsWith("."));
928
+ const uvRaw = uvAttr && attrSize(uvAttr) >= loopCount && readAttrArray(uvAttr, Float32Array, loopCount * 2);
929
+ if (uvRaw) {
930
+ const uvs = new Float32Array(vertCount * 2);
931
+ for (let i = 0; i < loopCount; i++) {
932
+ const v = cornerVerts[i];
933
+ if (v >= 0 && v < vertCount) {
934
+ const u = uvRaw[i * 2], vv = uvRaw[i * 2 + 1];
935
+ uvs[v * 2] = Number.isFinite(u) ? u : 0;
936
+ uvs[v * 2 + 1] = Number.isFinite(vv) ? vv : 0;
937
+ }
938
+ }
939
+ geometry.setAttribute("uv", new ctx.BufferAttribute(uvs, 2));
940
+ }
941
+ }
942
+ if (geometry.attributes.position && !geometry.attributes.normal)
943
+ geometry.computeVertexNormals();
944
+ return geometry;
945
+ }
722
946
  function createBufferGeometry(meshData, ctx) {
723
947
  if (meshData.mpoly) return createBufferGeometryOld(meshData, ctx);
948
+ if (meshData.attribute_storage) {
949
+ const g = createBufferGeometryFromAttributes(meshData, ctx);
950
+ if (g) return g;
951
+ }
724
952
  const geometry = new ctx.BufferGeometry();
725
953
  geometry.name = meshData.aname || "";
726
954
  let vertices;
727
955
  let verticesData;
728
956
  let indices;
729
957
  let indicesData;
958
+ let uvLayerData = null;
730
959
  if (meshData.vdata && meshData.vdata.layers && meshData.vdata.totlayer > 0) {
731
960
  for (let i = 0; i < meshData.vdata.totlayer; i++) {
732
961
  const layer = getLayer(meshData.vdata.layers, i);
@@ -745,7 +974,12 @@
745
974
  const layer = getLayer(meshData.ldata.layers, i);
746
975
  const data = layer.data || [];
747
976
  if (data.length === meshData.totloop) {
748
- if (indices && (layer.name !== ".corner_vert" || indices.name === ".corner_vert")) {
977
+ const lname = layer.name || "";
978
+ if (!uvLayerData && (layer.type === 49 || lname[0] !== "." && /uv/i.test(lname))) {
979
+ uvLayerData = data;
980
+ continue;
981
+ }
982
+ if (indices && (lname !== ".corner_vert" || indices.name === ".corner_vert")) {
749
983
  continue;
750
984
  }
751
985
  indices = layer;
@@ -755,9 +989,44 @@
755
989
  }
756
990
  let faceIndices = [];
757
991
  if (meshData.poly_offset_indices && meshData.totpoly > 0) {
758
- faceIndices = [...new Int32Array(meshData.poly_offset_indices.__blender_file__.AB, meshData.poly_offset_indices.__data_address__, meshData.totpoly + 1)];
992
+ faceIndices = [...meshData.poly_offset_indices.__blender_file__.readTypedArray(Int32Array, meshData.poly_offset_indices.__data_address__, meshData.totpoly + 1)];
993
+ }
994
+ const vco = (vd) => vd?.x !== void 0 ? [vd.x, vd.y, vd.z] : vd?.co ? [vd.co[0], vd.co[1], vd.co[2]] : [0, 0, 0];
995
+ let cornerToVert = null;
996
+ let expandedPos = null;
997
+ let expandedUv = null;
998
+ if (uvLayerData && indicesData && verticesData && verticesData.length) {
999
+ cornerToVert = new Int32Array(indicesData.length);
1000
+ const seen = /* @__PURE__ */ new Map();
1001
+ const ps = [], us = [];
1002
+ const uvN = uvLayerData.length;
1003
+ for (let i = 0; i < indicesData.length; i++) {
1004
+ const bv = indicesData[i]?.i;
1005
+ if (!(bv >= 0 && bv < verticesData.length)) {
1006
+ cornerToVert[i] = 0;
1007
+ continue;
1008
+ }
1009
+ const e = i < uvN ? uvLayerData[i] : null;
1010
+ const u = e && Number.isFinite(e.x) ? e.x : 0;
1011
+ const w = e && Number.isFinite(e.y) ? e.y : 0;
1012
+ const key = bv + "|" + Math.round(u * 4096) + "|" + Math.round(w * 4096);
1013
+ let ev = seen.get(key);
1014
+ if (ev === void 0) {
1015
+ ev = us.length / 2;
1016
+ seen.set(key, ev);
1017
+ const [x, y, z] = vco(verticesData[bv]);
1018
+ ps.push(x, z, -y);
1019
+ us.push(u, w);
1020
+ }
1021
+ cornerToVert[i] = ev;
1022
+ }
1023
+ expandedPos = new Float32Array(ps);
1024
+ expandedUv = new Float32Array(us);
759
1025
  }
760
- if (verticesData && verticesData.length > 0) {
1026
+ const vAt = (corner) => cornerToVert ? cornerToVert[corner] : indicesData[corner].i;
1027
+ if (expandedPos) {
1028
+ geometry.setAttribute("position", new ctx.BufferAttribute(expandedPos, 3));
1029
+ } else if (verticesData && verticesData.length > 0) {
761
1030
  const positions = new Float32Array(verticesData.length * 3);
762
1031
  for (let j = 0; j < verticesData.length; j++) {
763
1032
  const { x, y, z, co } = verticesData[j] || {};
@@ -776,44 +1045,50 @@
776
1045
  }
777
1046
  geometry.setAttribute("position", new ctx.BufferAttribute(positions, 3));
778
1047
  }
779
- if (indicesData && indicesData.length > 0 && (verticesData == null ? void 0 : verticesData.length)) {
1048
+ if (indicesData && indicesData.length > 0 && verticesData?.length) {
780
1049
  const faceSize = meshData.totloop / meshData.totpoly;
781
1050
  if (faceIndices.length > 0) {
1051
+ const faceCount = faceIndices.length - 1;
782
1052
  let totalTriangles = 0;
783
- for (let i = 0; i < faceIndices.length - 1; i++) {
1053
+ for (let i = 0; i < faceCount; i++) {
784
1054
  const faceVertCount = faceIndices[i + 1] - faceIndices[i];
785
1055
  totalTriangles += Math.max(0, faceVertCount - 2);
786
1056
  }
787
1057
  const indexes = new Uint32Array(totalTriangles * 3);
788
1058
  let t = 0;
789
- for (let i = 0; i < faceIndices.length - 1; i++) {
1059
+ const faceMat = (meshData.totcol || 0) > 1 ? readFaceMaterialIndex(meshData, faceCount) : null;
1060
+ const groupRuns = [];
1061
+ for (let i = 0; i < faceCount; i++) {
790
1062
  const faceStart = faceIndices[i];
791
1063
  const faceEnd = faceIndices[i + 1];
792
1064
  const faceVertCount = faceEnd - faceStart;
1065
+ const triStart = t;
793
1066
  if (faceVertCount >= 3) {
794
- const firstVert = indicesData[faceStart].i;
1067
+ const firstVert = vAt(faceStart);
795
1068
  for (let k = 1; k < faceVertCount - 1; k++) {
796
1069
  indexes[t++] = firstVert;
797
- indexes[t++] = indicesData[faceStart + k].i;
798
- indexes[t++] = indicesData[faceStart + k + 1].i;
1070
+ indexes[t++] = vAt(faceStart + k);
1071
+ indexes[t++] = vAt(faceStart + k + 1);
799
1072
  }
800
1073
  }
1074
+ if (faceMat) mergeGroupRun(groupRuns, triStart, t - triStart, faceMat[i] || 0);
801
1075
  }
802
1076
  geometry.setIndex(new ctx.BufferAttribute(indexes, 1));
1077
+ if (faceMat) for (const g of groupRuns) geometry.addGroup(g.start, g.count, g.mat);
803
1078
  } else if (faceSize === 3 || faceSize === 4) {
804
1079
  const isQuad = faceSize === 4;
805
1080
  const faceCount = indices.length / faceSize;
806
1081
  const indexes = new Uint32Array(faceCount * 3 * (isQuad ? 2 : 1));
807
1082
  if (faceSize !== 3 && faceSize !== 4) return geometry;
808
1083
  for (let j = 0, t = 0; j < indices.length; j += faceSize) {
809
- const a = indices[j].i;
810
- const b = indices[j + 1].i;
811
- const c = indices[j + 2].i;
1084
+ const a = vAt(j);
1085
+ const b = vAt(j + 1);
1086
+ const c = vAt(j + 2);
812
1087
  indexes[t++] = a;
813
1088
  indexes[t++] = b;
814
1089
  indexes[t++] = c;
815
1090
  if (isQuad) {
816
- const d = indices[j + 3].i;
1091
+ const d = vAt(j + 3);
817
1092
  indexes[t++] = a;
818
1093
  indexes[t++] = c;
819
1094
  indexes[t++] = d;
@@ -824,6 +1099,9 @@
824
1099
  } else if (faceIndices) {
825
1100
  console.error("BlendLoader - no indices data found, but face indices are present", faceIndices);
826
1101
  }
1102
+ if (expandedUv && !geometry.attributes.uv) {
1103
+ geometry.setAttribute("uv", new ctx.BufferAttribute(expandedUv, 2));
1104
+ }
827
1105
  if (geometry.attributes.position && !geometry.attributes.normal)
828
1106
  geometry.computeVertexNormals();
829
1107
  return geometry;
@@ -839,9 +1117,12 @@
839
1117
  const positions = new Float32Array(size * 3);
840
1118
  let currentIndex = 0;
841
1119
  let computeNormals = false;
1120
+ const useGroups = (mesh.totcol || 0) > 1;
1121
+ const groupRuns = [];
842
1122
  for (const face of faces) {
843
1123
  const len = face.totloop;
844
1124
  const start = face.loopstart;
1125
+ const faceStartIndex = currentIndex;
845
1126
  let indexi = 1;
846
1127
  while (indexi < len) {
847
1128
  let index = 0;
@@ -873,34 +1154,297 @@
873
1154
  }
874
1155
  indexi += 2;
875
1156
  }
1157
+ if (useGroups) mergeGroupRun(groupRuns, faceStartIndex, currentIndex - faceStartIndex, face.mat_nr || 0);
876
1158
  }
877
1159
  geometry.setAttribute("position", new ctx.BufferAttribute(positions, 3));
878
1160
  geometry.setIndex(new ctx.BufferAttribute(indices, 1));
879
1161
  geometry.setAttribute("normal", new ctx.BufferAttribute(normals, 3));
880
1162
  geometry.setAttribute("uv", new ctx.BufferAttribute(uvs, 2));
1163
+ if (useGroups && groupRuns.length)
1164
+ for (const g of groupRuns) geometry.addGroup(g.start, g.count, g.mat);
881
1165
  if (computeNormals) {
882
1166
  geometry.computeVertexNormals();
883
1167
  }
884
1168
  return geometry;
885
1169
  }
1170
+ const MA_BL_CULL_BACKFACE = 1 << 2;
1171
+ const MA_BM_CLIP = 3;
1172
+ function listBaseToArray(lb) {
1173
+ const out = [];
1174
+ if (!lb) return out;
1175
+ let n = lb.first, guard = 0;
1176
+ while (n && guard++ < 8192) {
1177
+ out.push(n);
1178
+ n = n.next;
1179
+ }
1180
+ return out;
1181
+ }
1182
+ const idnameOf = (n) => n && typeof n.idname === "string" ? n.idname : "";
1183
+ const nameOf = (s) => s && typeof s.name === "string" ? s.name : "";
1184
+ function socketByName(sockets, ...names) {
1185
+ for (const name of names) {
1186
+ const s = sockets.find((x) => nameOf(x) === name);
1187
+ if (s) return s;
1188
+ }
1189
+ return null;
1190
+ }
1191
+ function inputsOf(node) {
1192
+ return node ? listBaseToArray(node.inputs) : [];
1193
+ }
1194
+ function socketConst(sock) {
1195
+ const dv = sock && sock.default_value;
1196
+ return dv ? dv.value : void 0;
1197
+ }
1198
+ const num = (v) => typeof v === "number" ? v : void 0;
1199
+ const clamp01 = (c) => Math.max(0, Math.min(1, c));
1200
+ function nodeFeeding(links, socket) {
1201
+ if (!socket) return null;
1202
+ let link = links.find((l) => l.tosock === socket);
1203
+ let guard = 0;
1204
+ while (link && idnameOf(link.fromnode).includes("Reroute") && guard++ < 32) {
1205
+ const re = inputsOf(link.fromnode)[0];
1206
+ link = links.find((l) => l.tosock === re);
1207
+ }
1208
+ return link ? link.fromnode : null;
1209
+ }
1210
+ function imageMime(b) {
1211
+ if (b[0] === 137 && b[1] === 80 && b[2] === 78 && b[3] === 71) return "image/png";
1212
+ if (b[0] === 255 && b[1] === 216 && b[2] === 255) return "image/jpeg";
1213
+ if (b[0] === 66 && b[1] === 77) return "image/bmp";
1214
+ if (b[0] === 82 && b[1] === 73 && b[2] === 70 && b[3] === 70 && b[8] === 87 && b[9] === 69) return "image/webp";
1215
+ return null;
1216
+ }
1217
+ function packedTexture(imageNode, srgb) {
1218
+ const img = imageNode && imageNode.id;
1219
+ if (!img) return null;
1220
+ const pf = img.packedfile || img.packedfiles && img.packedfiles.first && img.packedfiles.first.packedfile;
1221
+ const block = pf && pf.data;
1222
+ const size = pf && typeof pf.size === "number" ? pf.size : 0;
1223
+ if (!block || block.__data_address__ === void 0 || size <= 0) return null;
1224
+ const buf = block.__blender_file__.byte.buffer;
1225
+ if (block.__data_address__ + size > buf.byteLength) return null;
1226
+ const bytes = new Uint8Array(buf, block.__data_address__, size).slice();
1227
+ const mime = imageMime(bytes);
1228
+ if (!mime || typeof Blob === "undefined" || typeof URL === "undefined" || typeof Image === "undefined") return null;
1229
+ const url = URL.createObjectURL(new Blob([bytes], { type: mime }));
1230
+ const el = new Image();
1231
+ const texture = new threepipe.Texture(el);
1232
+ if (srgb) texture.colorSpace = threepipe.SRGBColorSpace;
1233
+ texture.name = (typeof img.aname === "string" ? img.aname : "") || "blendTexture";
1234
+ el.onload = () => {
1235
+ texture.needsUpdate = true;
1236
+ URL.revokeObjectURL(url);
1237
+ };
1238
+ el.onerror = () => {
1239
+ URL.revokeObjectURL(url);
1240
+ };
1241
+ el.src = url;
1242
+ return texture;
1243
+ }
1244
+ const IMA_SRC_GENERATED = 4, IMA_SRC_VIEWER = 5;
1245
+ function applyWrap(tex, imageNode) {
1246
+ if (!tex) return tex;
1247
+ const ext = imageNode?.storage?.extension;
1248
+ tex.wrapS = tex.wrapT = ext === 1 || ext === 2 ? threepipe.ClampToEdgeWrapping : ext === 3 ? threepipe.MirroredRepeatWrapping : threepipe.RepeatWrapping;
1249
+ tex.needsUpdate = true;
1250
+ return tex;
1251
+ }
1252
+ function imageNodeTexture(imageNode, srgb, ctx) {
1253
+ const packed = packedTexture(imageNode, srgb);
1254
+ if (packed) return applyWrap(packed, imageNode);
1255
+ const img = imageNode && imageNode.id;
1256
+ if (img && img.source !== IMA_SRC_GENERATED && img.source !== IMA_SRC_VIEWER && typeof img.name === "string" && img.name && ctx.loadExternalTexture) {
1257
+ return applyWrap(ctx.loadExternalTexture(img.name, srgb), imageNode);
1258
+ }
1259
+ return null;
1260
+ }
1261
+ function imageNodeFeeding(links, socket) {
1262
+ let src = nodeFeeding(links, socket);
1263
+ if (src && idnameOf(src).includes("NormalMap")) src = nodeFeeding(links, socketByName(inputsOf(src), "Color"));
1264
+ return src && idnameOf(src).includes("TexImage") ? src : null;
1265
+ }
1266
+ function textureFromSocket(links, socket, srgb, ctx) {
1267
+ const src = imageNodeFeeding(links, socket);
1268
+ return src ? imageNodeTexture(src, srgb, ctx) : null;
1269
+ }
1270
+ function resolveShaders(nodes, links) {
1271
+ const outputs = nodes.filter((n) => idnameOf(n).includes("OutputMaterial"));
1272
+ const activeOut = outputs.find((n) => n.is_active_output) || outputs[0];
1273
+ let principled = null, emission = null;
1274
+ if (activeOut) {
1275
+ let surf = nodeFeeding(links, socketByName(inputsOf(activeOut), "Surface"));
1276
+ let guard = 0;
1277
+ while (surf && !idnameOf(surf).includes("BsdfPrincipled") && guard++ < 4) {
1278
+ if (idnameOf(surf).includes("Emission") && !emission) emission = surf;
1279
+ const next = inputsOf(surf).map((s) => nodeFeeding(links, s)).find((n) => n && idnameOf(n).includes("BsdfPrincipled"));
1280
+ if (!next) break;
1281
+ surf = next;
1282
+ }
1283
+ if (surf && idnameOf(surf).includes("BsdfPrincipled")) principled = surf;
1284
+ }
1285
+ if (!principled) principled = nodes.find((n) => idnameOf(n).includes("BsdfPrincipled"));
1286
+ if (!emission) emission = nodes.find((n) => idnameOf(n) === "ShaderNodeEmission");
1287
+ return { principled, emission };
1288
+ }
886
1289
  function createMaterial(mat, ctx) {
887
1290
  const material = new ctx.MeshPhysicalMaterial();
888
- material.color.setRGB(mat.r, mat.g, mat.b);
889
- material.roughness = mat.roughness !== void 0 ? mat.roughness : 0.4;
890
- material.metalness = mat.metallic !== void 0 ? mat.metallic : 0;
891
- material.opacity = 1;
892
- material.transparent = material.opacity < 1;
1291
+ material.side = typeof mat.blend_flag === "number" && mat.blend_flag & MA_BL_CULL_BACKFACE ? threepipe.FrontSide : threepipe.DoubleSide;
1292
+ const nodes = mat.nodetree ? listBaseToArray(mat.nodetree.nodes) : [];
1293
+ const links = mat.nodetree ? listBaseToArray(mat.nodetree.links) : [];
1294
+ const { principled, emission } = resolveShaders(nodes, links);
1295
+ let applied = false;
1296
+ if (principled) {
1297
+ const inp = inputsOf(principled);
1298
+ applied = true;
1299
+ const baseSock = socketByName(inp, "Base Color", "BaseColor");
1300
+ const baseImgNode = imageNodeFeeding(links, baseSock);
1301
+ const baseTex = baseImgNode ? imageNodeTexture(baseImgNode, true, ctx) : null;
1302
+ if (baseTex) {
1303
+ material.map = baseTex;
1304
+ material.color.setRGB(1, 1, 1);
1305
+ } else {
1306
+ const base = socketConst(baseSock);
1307
+ if (base && base.length >= 3) material.color.setRGB(clamp01(base[0]), clamp01(base[1]), clamp01(base[2]));
1308
+ }
1309
+ const roughSock = socketByName(inp, "Roughness");
1310
+ const roughTex = textureFromSocket(links, roughSock, false, ctx);
1311
+ if (roughTex) material.roughnessMap = roughTex;
1312
+ else {
1313
+ const r = num(socketConst(roughSock));
1314
+ if (r !== void 0) material.roughness = r;
1315
+ }
1316
+ const metalSock = socketByName(inp, "Metallic");
1317
+ const metalTex = textureFromSocket(links, metalSock, false, ctx);
1318
+ if (metalTex) {
1319
+ material.metalnessMap = metalTex;
1320
+ material.metalness = 1;
1321
+ } else {
1322
+ const m = num(socketConst(metalSock));
1323
+ if (m !== void 0) material.metalness = m;
1324
+ }
1325
+ const normSrc = nodeFeeding(links, socketByName(inp, "Normal"));
1326
+ if (normSrc) {
1327
+ const normTex = textureFromSocket(links, socketByName(inp, "Normal"), false, ctx);
1328
+ if (normTex) {
1329
+ material.normalMap = normTex;
1330
+ const strength = num(socketConst(socketByName(inputsOf(normSrc), "Strength")));
1331
+ if (strength !== void 0 && material.normalScale) material.normalScale.set(strength, strength);
1332
+ }
1333
+ }
1334
+ const alphaSock = socketByName(inp, "Alpha");
1335
+ const alphaImgNode = imageNodeFeeding(links, alphaSock);
1336
+ const alphaFromBase = !!(alphaImgNode && baseImgNode && alphaImgNode === baseImgNode);
1337
+ const alphaTex = alphaImgNode && !alphaFromBase ? imageNodeTexture(alphaImgNode, false, ctx) : null;
1338
+ const alphaC = num(socketConst(alphaSock));
1339
+ const hasAlpha = !!alphaTex || alphaFromBase || alphaC !== void 0 && alphaC < 1;
1340
+ if (alphaTex) material.alphaMap = alphaTex;
1341
+ else if (!alphaFromBase && alphaC !== void 0 && alphaC < 1) material.opacity = alphaC;
1342
+ if (hasAlpha) {
1343
+ if (mat.blend_method === MA_BM_CLIP) {
1344
+ const thr = num(mat.alpha_threshold);
1345
+ material.alphaTest = thr !== void 0 && thr > 0 ? thr : 0.5;
1346
+ } else material.transparent = true;
1347
+ }
1348
+ const ior = num(socketConst(socketByName(inp, "IOR")));
1349
+ if (ior !== void 0 && material.ior !== void 0) material.ior = ior;
1350
+ const trans = num(socketConst(socketByName(inp, "Transmission Weight", "Transmission")));
1351
+ if (trans !== void 0 && trans > 0 && material.transmission !== void 0) {
1352
+ material.transmission = trans;
1353
+ }
1354
+ const coat = num(socketConst(socketByName(inp, "Coat Weight", "Clearcoat")));
1355
+ if (coat !== void 0 && coat > 0 && material.clearcoat !== void 0) {
1356
+ material.clearcoat = coat;
1357
+ const cr = num(socketConst(socketByName(inp, "Coat Roughness", "Clearcoat Roughness")));
1358
+ if (cr !== void 0) material.clearcoatRoughness = cr;
1359
+ }
1360
+ const sheen = num(socketConst(socketByName(inp, "Sheen Weight", "Sheen")));
1361
+ if (sheen !== void 0 && sheen > 0 && material.sheen !== void 0) {
1362
+ material.sheen = sheen;
1363
+ const stint = socketConst(socketByName(inp, "Sheen Tint"));
1364
+ if (stint && stint.length >= 3 && material.sheenColor) material.sheenColor.setRGB(stint[0], stint[1], stint[2]);
1365
+ const sr = num(socketConst(socketByName(inp, "Sheen Roughness")));
1366
+ if (sr !== void 0) material.sheenRoughness = sr;
1367
+ }
1368
+ const spec = num(socketConst(socketByName(inp, "Specular IOR Level", "Specular")));
1369
+ if (spec !== void 0 && material.specularIntensity !== void 0) {
1370
+ material.specularIntensity = Math.min(1, spec * 2);
1371
+ }
1372
+ }
1373
+ const matOutput = nodes.find((n) => idnameOf(n).includes("OutputMaterial") && n.is_active_output) || nodes.find((n) => idnameOf(n).includes("OutputMaterial"));
1374
+ const dispNode = matOutput ? nodeFeeding(links, socketByName(inputsOf(matOutput), "Displacement")) : null;
1375
+ if (dispNode && idnameOf(dispNode).includes("Displacement")) {
1376
+ const heightTex = textureFromSocket(links, socketByName(inputsOf(dispNode), "Height"), false, ctx);
1377
+ if (heightTex) {
1378
+ const scale = num(socketConst(socketByName(inputsOf(dispNode), "Scale")));
1379
+ const s = scale !== void 0 && scale > 0 ? scale : 1;
1380
+ const mid = num(socketConst(socketByName(inputsOf(dispNode), "Midlevel")));
1381
+ if (material.displacementMap !== void 0) {
1382
+ material.displacementMap = heightTex;
1383
+ material.displacementScale = s;
1384
+ material.displacementBias = -(mid ?? 0.5) * s;
1385
+ }
1386
+ if (!material.normalMap && material.bumpMap !== void 0) {
1387
+ material.bumpMap = heightTex;
1388
+ material.bumpScale = s;
1389
+ }
1390
+ }
1391
+ }
1392
+ const emSock = emission ? socketByName(inputsOf(emission), "Color") : principled ? socketByName(inputsOf(principled), "Emission Color", "Emission") : null;
1393
+ const emStrSock = emission ? socketByName(inputsOf(emission), "Strength") : principled ? socketByName(inputsOf(principled), "Emission Strength") : null;
1394
+ const emTex = textureFromSocket(links, emSock, true, ctx);
1395
+ if (emTex) material.emissiveMap = emTex;
1396
+ let emCol = socketConst(emSock);
1397
+ if (emTex && (!emCol || emCol.length < 3 || !emCol[0] && !emCol[1] && !emCol[2])) emCol = [1, 1, 1];
1398
+ if (emCol && emCol.length >= 3 && (emCol[0] || emCol[1] || emCol[2] || emTex)) {
1399
+ const f = [emCol[0], emCol[1], emCol[2]];
1400
+ const strength = num(socketConst(emStrSock));
1401
+ if (typeof strength === "number") for (let i = 0; i < 3; i++) f[i] *= strength;
1402
+ const peak = Math.max(f[0], f[1], f[2]);
1403
+ if (peak > 1) {
1404
+ material.emissiveIntensity = peak;
1405
+ material.emissive.setRGB(f[0] / peak, f[1] / peak, f[2] / peak);
1406
+ } else material.emissive.setRGB(f[0], f[1], f[2]);
1407
+ }
1408
+ if (!applied) {
1409
+ if (mat.r !== void 0) material.color.setRGB(mat.r, mat.g, mat.b);
1410
+ material.roughness = mat.roughness !== void 0 ? mat.roughness : 0.4;
1411
+ material.metalness = mat.metallic !== void 0 ? mat.metallic : 0;
1412
+ }
893
1413
  return material;
894
1414
  }
895
1415
  function createMesh(object, loaded, ctx) {
896
1416
  if (!object.data) {
897
1417
  return void 0;
898
1418
  }
899
- const geometry = loaded.get(object.data) ?? createBufferGeometry(object.data, ctx);
900
- loaded.set(object.data, geometry);
901
- const mat = object.data.mat[0];
902
- const material = mat ? loaded.get(mat) ?? createMaterial(mat, ctx) : new ctx.MeshPhysicalMaterial();
903
- if (mat) loaded.set(mat, material);
1419
+ let geometry = loaded.get(object.data);
1420
+ if (!geometry) {
1421
+ geometry = createBufferGeometry(object.data, ctx);
1422
+ loaded.set(object.data, geometry);
1423
+ }
1424
+ const matData = object.data.mat;
1425
+ const slots = Array.isArray(matData) ? matData : matData ? [matData] : [];
1426
+ const buildMaterial = (m) => {
1427
+ if (!m) {
1428
+ const d = new ctx.MeshPhysicalMaterial();
1429
+ d.side = threepipe.DoubleSide;
1430
+ return d;
1431
+ }
1432
+ const cached = loaded.get(m);
1433
+ if (cached) return cached;
1434
+ const created = createMaterial(m, ctx);
1435
+ loaded.set(m, created);
1436
+ return created;
1437
+ };
1438
+ const hasGroups = !!(geometry.groups && geometry.groups.length);
1439
+ let material;
1440
+ if (hasGroups) {
1441
+ material = slots.map(buildMaterial);
1442
+ let maxIdx = 0;
1443
+ for (const g of geometry.groups) if (g.materialIndex > maxIdx) maxIdx = g.materialIndex;
1444
+ while (material.length <= maxIdx) material.push(buildMaterial(null));
1445
+ } else {
1446
+ material = buildMaterial(slots[0]);
1447
+ }
904
1448
  const mesh = new ctx.Mesh(geometry, material);
905
1449
  mesh.castShadow = true;
906
1450
  mesh.receiveShadow = true;
@@ -1000,25 +1544,743 @@
1000
1544
  obj.updateMatrix();
1001
1545
  if (obj.setDirty) obj.setDirty();
1002
1546
  }
1547
+ var ab = ArrayBuffer, u8 = Uint8Array, u16 = Uint16Array, i16 = Int16Array, i32 = Int32Array;
1548
+ var slc = function(v, s, e) {
1549
+ if (u8.prototype.slice)
1550
+ return u8.prototype.slice.call(v, s, e);
1551
+ if (s == null || s < 0)
1552
+ s = 0;
1553
+ if (e == null || e > v.length)
1554
+ e = v.length;
1555
+ var n = new u8(e - s);
1556
+ n.set(v.subarray(s, e));
1557
+ return n;
1558
+ };
1559
+ var fill = function(v, n, s, e) {
1560
+ if (u8.prototype.fill)
1561
+ return u8.prototype.fill.call(v, n, s, e);
1562
+ if (s == null || s < 0)
1563
+ s = 0;
1564
+ if (e == null || e > v.length)
1565
+ e = v.length;
1566
+ for (; s < e; ++s)
1567
+ v[s] = n;
1568
+ return v;
1569
+ };
1570
+ var cpw = function(v, t, s, e) {
1571
+ if (u8.prototype.copyWithin)
1572
+ return u8.prototype.copyWithin.call(v, t, s, e);
1573
+ if (s == null || s < 0)
1574
+ s = 0;
1575
+ if (e == null || e > v.length)
1576
+ e = v.length;
1577
+ while (s < e) {
1578
+ v[t++] = v[s++];
1579
+ }
1580
+ };
1581
+ var ec = [
1582
+ "invalid zstd data",
1583
+ "window size too large (>2046MB)",
1584
+ "invalid block type",
1585
+ "FSE accuracy too high",
1586
+ "match distance too far back",
1587
+ "unexpected EOF"
1588
+ ];
1589
+ var err = function(ind, msg, nt) {
1590
+ var e = new Error(msg || ec[ind]);
1591
+ e.code = ind;
1592
+ if (Error.captureStackTrace)
1593
+ Error.captureStackTrace(e, err);
1594
+ if (!nt)
1595
+ throw e;
1596
+ return e;
1597
+ };
1598
+ var rb = function(d, b, n) {
1599
+ var i = 0, o = 0;
1600
+ for (; i < n; ++i)
1601
+ o |= d[b++] << (i << 3);
1602
+ return o;
1603
+ };
1604
+ var b4 = function(d, b) {
1605
+ return (d[b] | d[b + 1] << 8 | d[b + 2] << 16 | d[b + 3] << 24) >>> 0;
1606
+ };
1607
+ var rzfh = function(dat, w) {
1608
+ var n3 = dat[0] | dat[1] << 8 | dat[2] << 16;
1609
+ if (n3 == 3126568 && dat[3] == 253) {
1610
+ var flg = dat[4];
1611
+ var ss = flg >> 5 & 1, cc = flg >> 2 & 1, df = flg & 3, fcf = flg >> 6;
1612
+ if (flg & 8)
1613
+ err(0);
1614
+ var bt = 6 - ss;
1615
+ var db = df == 3 ? 4 : df;
1616
+ var di = rb(dat, bt, db);
1617
+ bt += db;
1618
+ var fsb = fcf ? 1 << fcf : ss;
1619
+ var fss = rb(dat, bt, fsb) + (fcf == 1 && 256);
1620
+ var ws = fss;
1621
+ if (!ss) {
1622
+ var wb = 1 << 10 + (dat[5] >> 3);
1623
+ ws = wb + (wb >> 3) * (dat[5] & 7);
1624
+ }
1625
+ if (ws > 2145386496)
1626
+ err(1);
1627
+ var buf = new u8((w == 1 ? fss || ws : w ? 0 : ws) + 12);
1628
+ buf[0] = 1, buf[4] = 4, buf[8] = 8;
1629
+ return {
1630
+ b: bt + fsb,
1631
+ y: 0,
1632
+ l: 0,
1633
+ d: di,
1634
+ w: w && w != 1 ? w : buf.subarray(12),
1635
+ e: ws,
1636
+ o: new i32(buf.buffer, 0, 3),
1637
+ u: fss,
1638
+ c: cc,
1639
+ m: Math.min(131072, ws)
1640
+ };
1641
+ } else if ((n3 >> 4 | dat[3] << 20) == 25481893) {
1642
+ return b4(dat, 4) + 8;
1643
+ }
1644
+ err(0);
1645
+ };
1646
+ var msb = function(val) {
1647
+ var bits = 0;
1648
+ for (; 1 << bits <= val; ++bits)
1649
+ ;
1650
+ return bits - 1;
1651
+ };
1652
+ var rfse = function(dat, bt, mal) {
1653
+ var tpos = (bt << 3) + 4;
1654
+ var al = (dat[bt] & 15) + 5;
1655
+ if (al > mal)
1656
+ err(3);
1657
+ var sz = 1 << al;
1658
+ var probs = sz, sym = -1, re = -1, i = -1, ht = sz;
1659
+ var buf = new ab(512 + (sz << 2));
1660
+ var freq = new i16(buf, 0, 256);
1661
+ var dstate = new u16(buf, 0, 256);
1662
+ var nstate = new u16(buf, 512, sz);
1663
+ var bb1 = 512 + (sz << 1);
1664
+ var syms = new u8(buf, bb1, sz);
1665
+ var nbits = new u8(buf, bb1 + sz);
1666
+ while (sym < 255 && probs > 0) {
1667
+ var bits = msb(probs + 1);
1668
+ var cbt = tpos >> 3;
1669
+ var msk = (1 << bits + 1) - 1;
1670
+ var val = (dat[cbt] | dat[cbt + 1] << 8 | dat[cbt + 2] << 16) >> (tpos & 7) & msk;
1671
+ var msk1fb = (1 << bits) - 1;
1672
+ var msv = msk - probs - 1;
1673
+ var sval = val & msk1fb;
1674
+ if (sval < msv)
1675
+ tpos += bits, val = sval;
1676
+ else {
1677
+ tpos += bits + 1;
1678
+ if (val > msk1fb)
1679
+ val -= msv;
1680
+ }
1681
+ freq[++sym] = --val;
1682
+ if (val == -1) {
1683
+ probs += val;
1684
+ syms[--ht] = sym;
1685
+ } else
1686
+ probs -= val;
1687
+ if (!val) {
1688
+ do {
1689
+ var rbt = tpos >> 3;
1690
+ re = (dat[rbt] | dat[rbt + 1] << 8) >> (tpos & 7) & 3;
1691
+ tpos += 2;
1692
+ sym += re;
1693
+ } while (re == 3);
1694
+ }
1695
+ }
1696
+ if (sym > 255 || probs)
1697
+ err(0);
1698
+ var sympos = 0;
1699
+ var sstep = (sz >> 1) + (sz >> 3) + 3;
1700
+ var smask = sz - 1;
1701
+ for (var s = 0; s <= sym; ++s) {
1702
+ var sf = freq[s];
1703
+ if (sf < 1) {
1704
+ dstate[s] = -sf;
1705
+ continue;
1706
+ }
1707
+ for (i = 0; i < sf; ++i) {
1708
+ syms[sympos] = s;
1709
+ do {
1710
+ sympos = sympos + sstep & smask;
1711
+ } while (sympos >= ht);
1712
+ }
1713
+ }
1714
+ if (sympos)
1715
+ err(0);
1716
+ for (i = 0; i < sz; ++i) {
1717
+ var ns = dstate[syms[i]]++;
1718
+ var nb = nbits[i] = al - msb(ns);
1719
+ nstate[i] = (ns << nb) - sz;
1720
+ }
1721
+ return [tpos + 7 >> 3, {
1722
+ b: al,
1723
+ s: syms,
1724
+ n: nbits,
1725
+ t: nstate
1726
+ }];
1727
+ };
1728
+ var rhu = function(dat, bt) {
1729
+ var i = 0, wc = -1;
1730
+ var buf = new u8(292), hb = dat[bt];
1731
+ var hw = buf.subarray(0, 256);
1732
+ var rc = buf.subarray(256, 268);
1733
+ var ri = new u16(buf.buffer, 268);
1734
+ if (hb < 128) {
1735
+ var _a = rfse(dat, bt + 1, 6), ebt = _a[0], fdt = _a[1];
1736
+ bt += hb;
1737
+ var epos = ebt << 3;
1738
+ var lb = dat[bt];
1739
+ if (!lb)
1740
+ err(0);
1741
+ var st1 = 0, st2 = 0, btr1 = fdt.b, btr2 = btr1;
1742
+ var fpos = (++bt << 3) - 8 + msb(lb);
1743
+ for (; ; ) {
1744
+ fpos -= btr1;
1745
+ if (fpos < epos)
1746
+ break;
1747
+ var cbt = fpos >> 3;
1748
+ st1 += (dat[cbt] | dat[cbt + 1] << 8) >> (fpos & 7) & (1 << btr1) - 1;
1749
+ hw[++wc] = fdt.s[st1];
1750
+ fpos -= btr2;
1751
+ if (fpos < epos)
1752
+ break;
1753
+ cbt = fpos >> 3;
1754
+ st2 += (dat[cbt] | dat[cbt + 1] << 8) >> (fpos & 7) & (1 << btr2) - 1;
1755
+ hw[++wc] = fdt.s[st2];
1756
+ btr1 = fdt.n[st1];
1757
+ st1 = fdt.t[st1];
1758
+ btr2 = fdt.n[st2];
1759
+ st2 = fdt.t[st2];
1760
+ }
1761
+ if (++wc > 255)
1762
+ err(0);
1763
+ } else {
1764
+ wc = hb - 127;
1765
+ for (; i < wc; i += 2) {
1766
+ var byte = dat[++bt];
1767
+ hw[i] = byte >> 4;
1768
+ hw[i + 1] = byte & 15;
1769
+ }
1770
+ ++bt;
1771
+ }
1772
+ var wes = 0;
1773
+ for (i = 0; i < wc; ++i) {
1774
+ var wt = hw[i];
1775
+ if (wt > 11)
1776
+ err(0);
1777
+ wes += wt && 1 << wt - 1;
1778
+ }
1779
+ var mb = msb(wes) + 1;
1780
+ var ts = 1 << mb;
1781
+ var rem = ts - wes;
1782
+ if (rem & rem - 1)
1783
+ err(0);
1784
+ hw[wc++] = msb(rem) + 1;
1785
+ for (i = 0; i < wc; ++i) {
1786
+ var wt = hw[i];
1787
+ ++rc[hw[i] = wt && mb + 1 - wt];
1788
+ }
1789
+ var hbuf = new u8(ts << 1);
1790
+ var syms = hbuf.subarray(0, ts), nb = hbuf.subarray(ts);
1791
+ ri[mb] = 0;
1792
+ for (i = mb; i > 0; --i) {
1793
+ var pv = ri[i];
1794
+ fill(nb, i, pv, ri[i - 1] = pv + rc[i] * (1 << mb - i));
1795
+ }
1796
+ if (ri[0] != ts)
1797
+ err(0);
1798
+ for (i = 0; i < wc; ++i) {
1799
+ var bits = hw[i];
1800
+ if (bits) {
1801
+ var code = ri[bits];
1802
+ fill(syms, i, code, ri[bits] = code + (1 << mb - bits));
1803
+ }
1804
+ }
1805
+ return [bt, {
1806
+ n: nb,
1807
+ b: mb,
1808
+ s: syms
1809
+ }];
1810
+ };
1811
+ var dllt = rfse(/* @__PURE__ */ new u8([
1812
+ 81,
1813
+ 16,
1814
+ 99,
1815
+ 140,
1816
+ 49,
1817
+ 198,
1818
+ 24,
1819
+ 99,
1820
+ 12,
1821
+ 33,
1822
+ 196,
1823
+ 24,
1824
+ 99,
1825
+ 102,
1826
+ 102,
1827
+ 134,
1828
+ 70,
1829
+ 146,
1830
+ 4
1831
+ ]), 0, 6)[1];
1832
+ var dmlt = rfse(/* @__PURE__ */ new u8([
1833
+ 33,
1834
+ 20,
1835
+ 196,
1836
+ 24,
1837
+ 99,
1838
+ 140,
1839
+ 33,
1840
+ 132,
1841
+ 16,
1842
+ 66,
1843
+ 8,
1844
+ 33,
1845
+ 132,
1846
+ 16,
1847
+ 66,
1848
+ 8,
1849
+ 33,
1850
+ 68,
1851
+ 68,
1852
+ 68,
1853
+ 68,
1854
+ 68,
1855
+ 68,
1856
+ 68,
1857
+ 68,
1858
+ 36,
1859
+ 9
1860
+ ]), 0, 6)[1];
1861
+ var doct = rfse(/* @__PURE__ */ new u8([
1862
+ 32,
1863
+ 132,
1864
+ 16,
1865
+ 66,
1866
+ 102,
1867
+ 70,
1868
+ 68,
1869
+ 68,
1870
+ 68,
1871
+ 68,
1872
+ 36,
1873
+ 73,
1874
+ 2
1875
+ ]), 0, 5)[1];
1876
+ var b2bl = function(b, s) {
1877
+ var len = b.length, bl = new i32(len);
1878
+ for (var i = 0; i < len; ++i) {
1879
+ bl[i] = s;
1880
+ s += 1 << b[i];
1881
+ }
1882
+ return bl;
1883
+ };
1884
+ var llb = /* @__PURE__ */ new u8((/* @__PURE__ */ new i32([
1885
+ 0,
1886
+ 0,
1887
+ 0,
1888
+ 0,
1889
+ 16843009,
1890
+ 50528770,
1891
+ 134678020,
1892
+ 202050057,
1893
+ 269422093
1894
+ ])).buffer, 0, 36);
1895
+ var llbl = /* @__PURE__ */ b2bl(llb, 0);
1896
+ var mlb = /* @__PURE__ */ new u8((/* @__PURE__ */ new i32([
1897
+ 0,
1898
+ 0,
1899
+ 0,
1900
+ 0,
1901
+ 0,
1902
+ 0,
1903
+ 0,
1904
+ 0,
1905
+ 16843009,
1906
+ 50528770,
1907
+ 117769220,
1908
+ 185207048,
1909
+ 252579084,
1910
+ 16
1911
+ ])).buffer, 0, 53);
1912
+ var mlbl = /* @__PURE__ */ b2bl(mlb, 3);
1913
+ var dhu = function(dat, out, hu) {
1914
+ var len = dat.length, ss = out.length, lb = dat[len - 1], msk = (1 << hu.b) - 1, eb = -hu.b;
1915
+ if (!lb)
1916
+ err(0);
1917
+ var st = 0, btr = hu.b, pos = (len << 3) - 8 + msb(lb) - btr, i = -1;
1918
+ for (; pos > eb && i < ss; ) {
1919
+ var cbt = pos >> 3;
1920
+ var val = (dat[cbt] | dat[cbt + 1] << 8 | dat[cbt + 2] << 16) >> (pos & 7);
1921
+ st = (st << btr | val) & msk;
1922
+ out[++i] = hu.s[st];
1923
+ pos -= btr = hu.n[st];
1924
+ }
1925
+ if (pos != eb || i + 1 != ss)
1926
+ err(0);
1927
+ };
1928
+ var dhu4 = function(dat, out, hu) {
1929
+ var bt = 6;
1930
+ var ss = out.length, sz1 = ss + 3 >> 2, sz2 = sz1 << 1, sz3 = sz1 + sz2;
1931
+ dhu(dat.subarray(bt, bt += dat[0] | dat[1] << 8), out.subarray(0, sz1), hu);
1932
+ dhu(dat.subarray(bt, bt += dat[2] | dat[3] << 8), out.subarray(sz1, sz2), hu);
1933
+ dhu(dat.subarray(bt, bt += dat[4] | dat[5] << 8), out.subarray(sz2, sz3), hu);
1934
+ dhu(dat.subarray(bt), out.subarray(sz3), hu);
1935
+ };
1936
+ var rzb = function(dat, st, out) {
1937
+ var _a;
1938
+ var bt = st.b;
1939
+ var b0 = dat[bt], btype = b0 >> 1 & 3;
1940
+ st.l = b0 & 1;
1941
+ var sz = b0 >> 3 | dat[bt + 1] << 5 | dat[bt + 2] << 13;
1942
+ var ebt = (bt += 3) + sz;
1943
+ if (btype == 1) {
1944
+ if (bt >= dat.length)
1945
+ return;
1946
+ st.b = bt + 1;
1947
+ if (out) {
1948
+ fill(out, dat[bt], st.y, st.y += sz);
1949
+ return out;
1950
+ }
1951
+ return fill(new u8(sz), dat[bt]);
1952
+ }
1953
+ if (ebt > dat.length)
1954
+ return;
1955
+ if (btype == 0) {
1956
+ st.b = ebt;
1957
+ if (out) {
1958
+ out.set(dat.subarray(bt, ebt), st.y);
1959
+ st.y += sz;
1960
+ return out;
1961
+ }
1962
+ return slc(dat, bt, ebt);
1963
+ }
1964
+ if (btype == 2) {
1965
+ var b3 = dat[bt], lbt = b3 & 3, sf = b3 >> 2 & 3;
1966
+ var lss = b3 >> 4, lcs = 0, s4 = 0;
1967
+ if (lbt < 2) {
1968
+ if (sf & 1)
1969
+ lss |= dat[++bt] << 4 | (sf & 2 && dat[++bt] << 12);
1970
+ else
1971
+ lss = b3 >> 3;
1972
+ } else {
1973
+ s4 = sf;
1974
+ if (sf < 2)
1975
+ lss |= (dat[++bt] & 63) << 4, lcs = dat[bt] >> 6 | dat[++bt] << 2;
1976
+ else if (sf == 2)
1977
+ lss |= dat[++bt] << 4 | (dat[++bt] & 3) << 12, lcs = dat[bt] >> 2 | dat[++bt] << 6;
1978
+ else
1979
+ lss |= dat[++bt] << 4 | (dat[++bt] & 63) << 12, lcs = dat[bt] >> 6 | dat[++bt] << 2 | dat[++bt] << 10;
1980
+ }
1981
+ ++bt;
1982
+ var buf = out ? out.subarray(st.y, st.y + st.m) : new u8(st.m);
1983
+ var spl = buf.length - lss;
1984
+ if (lbt == 0)
1985
+ buf.set(dat.subarray(bt, bt += lss), spl);
1986
+ else if (lbt == 1)
1987
+ fill(buf, dat[bt++], spl);
1988
+ else {
1989
+ var hu = st.h;
1990
+ if (lbt == 2) {
1991
+ var hud = rhu(dat, bt);
1992
+ lcs += bt - (bt = hud[0]);
1993
+ st.h = hu = hud[1];
1994
+ } else if (!hu)
1995
+ err(0);
1996
+ (s4 ? dhu4 : dhu)(dat.subarray(bt, bt += lcs), buf.subarray(spl), hu);
1997
+ }
1998
+ var ns = dat[bt++];
1999
+ if (ns) {
2000
+ if (ns == 255)
2001
+ ns = (dat[bt++] | dat[bt++] << 8) + 32512;
2002
+ else if (ns > 127)
2003
+ ns = ns - 128 << 8 | dat[bt++];
2004
+ var scm = dat[bt++];
2005
+ if (scm & 3)
2006
+ err(0);
2007
+ var dts = [dmlt, doct, dllt];
2008
+ for (var i = 2; i > -1; --i) {
2009
+ var md = scm >> (i << 1) + 2 & 3;
2010
+ if (md == 1) {
2011
+ var rbuf = new u8([0, 0, dat[bt++]]);
2012
+ dts[i] = {
2013
+ s: rbuf.subarray(2, 3),
2014
+ n: rbuf.subarray(0, 1),
2015
+ t: new u16(rbuf.buffer, 0, 1),
2016
+ b: 0
2017
+ };
2018
+ } else if (md == 2) {
2019
+ _a = rfse(dat, bt, 9 - (i & 1)), bt = _a[0], dts[i] = _a[1];
2020
+ } else if (md == 3) {
2021
+ if (!st.t)
2022
+ err(0);
2023
+ dts[i] = st.t[i];
2024
+ }
2025
+ }
2026
+ var _b = st.t = dts, mlt = _b[0], oct = _b[1], llt = _b[2];
2027
+ var lb = dat[ebt - 1];
2028
+ if (!lb)
2029
+ err(0);
2030
+ var spos = (ebt << 3) - 8 + msb(lb) - llt.b, cbt = spos >> 3, oubt = 0;
2031
+ var lst = (dat[cbt] | dat[cbt + 1] << 8) >> (spos & 7) & (1 << llt.b) - 1;
2032
+ cbt = (spos -= oct.b) >> 3;
2033
+ var ost = (dat[cbt] | dat[cbt + 1] << 8) >> (spos & 7) & (1 << oct.b) - 1;
2034
+ cbt = (spos -= mlt.b) >> 3;
2035
+ var mst = (dat[cbt] | dat[cbt + 1] << 8) >> (spos & 7) & (1 << mlt.b) - 1;
2036
+ for (++ns; --ns; ) {
2037
+ var llc = llt.s[lst];
2038
+ var lbtr = llt.n[lst];
2039
+ var mlc = mlt.s[mst];
2040
+ var mbtr = mlt.n[mst];
2041
+ var ofc = oct.s[ost];
2042
+ var obtr = oct.n[ost];
2043
+ cbt = (spos -= ofc) >> 3;
2044
+ var ofp = 1 << ofc;
2045
+ var off = ofp + ((dat[cbt] | dat[cbt + 1] << 8 | dat[cbt + 2] << 16 | dat[cbt + 3] << 24) >>> (spos & 7) & ofp - 1);
2046
+ cbt = (spos -= mlb[mlc]) >> 3;
2047
+ var ml = mlbl[mlc] + ((dat[cbt] | dat[cbt + 1] << 8 | dat[cbt + 2] << 16) >> (spos & 7) & (1 << mlb[mlc]) - 1);
2048
+ cbt = (spos -= llb[llc]) >> 3;
2049
+ var ll = llbl[llc] + ((dat[cbt] | dat[cbt + 1] << 8 | dat[cbt + 2] << 16) >> (spos & 7) & (1 << llb[llc]) - 1);
2050
+ cbt = (spos -= lbtr) >> 3;
2051
+ lst = llt.t[lst] + ((dat[cbt] | dat[cbt + 1] << 8) >> (spos & 7) & (1 << lbtr) - 1);
2052
+ cbt = (spos -= mbtr) >> 3;
2053
+ mst = mlt.t[mst] + ((dat[cbt] | dat[cbt + 1] << 8) >> (spos & 7) & (1 << mbtr) - 1);
2054
+ cbt = (spos -= obtr) >> 3;
2055
+ ost = oct.t[ost] + ((dat[cbt] | dat[cbt + 1] << 8) >> (spos & 7) & (1 << obtr) - 1);
2056
+ if (off > 3) {
2057
+ st.o[2] = st.o[1];
2058
+ st.o[1] = st.o[0];
2059
+ st.o[0] = off -= 3;
2060
+ } else {
2061
+ var idx = off - (ll != 0);
2062
+ if (idx) {
2063
+ off = idx == 3 ? st.o[0] - 1 : st.o[idx];
2064
+ if (idx > 1)
2065
+ st.o[2] = st.o[1];
2066
+ st.o[1] = st.o[0];
2067
+ st.o[0] = off;
2068
+ } else
2069
+ off = st.o[0];
2070
+ }
2071
+ for (var i = 0; i < ll; ++i) {
2072
+ buf[oubt + i] = buf[spl + i];
2073
+ }
2074
+ oubt += ll, spl += ll;
2075
+ var stin = oubt - off;
2076
+ if (stin < 0) {
2077
+ var len = -stin;
2078
+ var bs = st.e + stin;
2079
+ if (len > ml)
2080
+ len = ml;
2081
+ for (var i = 0; i < len; ++i) {
2082
+ buf[oubt + i] = st.w[bs + i];
2083
+ }
2084
+ oubt += len, ml -= len, stin = 0;
2085
+ }
2086
+ for (var i = 0; i < ml; ++i) {
2087
+ buf[oubt + i] = buf[stin + i];
2088
+ }
2089
+ oubt += ml;
2090
+ }
2091
+ if (oubt != spl) {
2092
+ while (spl < buf.length) {
2093
+ buf[oubt++] = buf[spl++];
2094
+ }
2095
+ } else
2096
+ oubt = buf.length;
2097
+ if (out)
2098
+ st.y += oubt;
2099
+ else
2100
+ buf = slc(buf, 0, oubt);
2101
+ } else if (out) {
2102
+ st.y += lss;
2103
+ if (spl) {
2104
+ for (var i = 0; i < lss; ++i) {
2105
+ buf[i] = buf[spl + i];
2106
+ }
2107
+ }
2108
+ } else if (spl)
2109
+ buf = slc(buf, spl);
2110
+ st.b = ebt;
2111
+ return buf;
2112
+ }
2113
+ err(2);
2114
+ };
2115
+ var cct = function(bufs, ol) {
2116
+ if (bufs.length == 1)
2117
+ return bufs[0];
2118
+ var buf = new u8(ol);
2119
+ for (var i = 0, b = 0; i < bufs.length; ++i) {
2120
+ var chk = bufs[i];
2121
+ buf.set(chk, b);
2122
+ b += chk.length;
2123
+ }
2124
+ return buf;
2125
+ };
2126
+ function decompress(dat, buf) {
2127
+ var bufs = [], nb = +!buf;
2128
+ var bt = 0, ol = 0;
2129
+ for (; dat.length; ) {
2130
+ var st = rzfh(dat, nb || buf);
2131
+ if (typeof st == "object") {
2132
+ if (nb) {
2133
+ buf = null;
2134
+ if (st.w.length == st.u) {
2135
+ bufs.push(buf = st.w);
2136
+ ol += st.u;
2137
+ }
2138
+ } else {
2139
+ bufs.push(buf);
2140
+ st.e = 0;
2141
+ }
2142
+ for (; !st.l; ) {
2143
+ var blk = rzb(dat, st, buf);
2144
+ if (!blk)
2145
+ err(5);
2146
+ if (buf)
2147
+ st.e = st.y;
2148
+ else {
2149
+ bufs.push(blk);
2150
+ ol += blk.length;
2151
+ cpw(st.w, 0, blk.length);
2152
+ st.w.set(blk, st.w.length - blk.length);
2153
+ }
2154
+ }
2155
+ bt = st.b + st.c * 4;
2156
+ } else
2157
+ bt = st;
2158
+ dat = dat.subarray(bt);
2159
+ }
2160
+ return cct(bufs, ol);
2161
+ }
2162
+ function detectBlendCompression(u82) {
2163
+ if (u82.length >= 3 && u82[0] === 31 && u82[1] === 139 && u82[2] === 8) return "gzip";
2164
+ if (u82.length >= 4) {
2165
+ if (u82[0] === 40 && u82[1] === 181 && u82[2] === 47 && u82[3] === 253) return "zstd";
2166
+ if ((u82[0] & 240) === 80 && u82[1] === 42 && u82[2] === 77 && u82[3] === 24) return "zstd";
2167
+ }
2168
+ if (u82.length >= 7 && u82[0] === 66 && u82[1] === 76 && u82[2] === 69 && u82[3] === 78 && u82[4] === 68 && u82[5] === 69 && u82[6] === 82) return "none";
2169
+ return "unknown";
2170
+ }
2171
+ function toArrayBuffer(u82) {
2172
+ if (u82.byteOffset === 0 && u82.byteLength === u82.buffer.byteLength) return u82.buffer;
2173
+ return u82.buffer.slice(u82.byteOffset, u82.byteOffset + u82.byteLength);
2174
+ }
2175
+ function decompressBlend(buf) {
2176
+ const u82 = new Uint8Array(buf);
2177
+ const kind = detectBlendCompression(u82);
2178
+ if (kind === "none") return buf;
2179
+ let out;
2180
+ if (kind === "gzip") out = toArrayBuffer(threepipe.gunzipSync(u82));
2181
+ else if (kind === "zstd") out = toArrayBuffer(decompress(u82));
2182
+ else {
2183
+ const hex = [u82[0], u82[1], u82[2], u82[3]].map((b) => (b ?? 0).toString(16).padStart(2, "0")).join(" ");
2184
+ throw new Error(`BlendLoadPlugin: unrecognised file magic (first 4 bytes: ${hex}). Expected 'BLENDER' literal, gzip, or zstd.`);
2185
+ }
2186
+ if (out.byteLength < 12) {
2187
+ throw new Error(`BlendLoadPlugin: ${kind} decompression produced ${out.byteLength} bytes, too short to be a valid .blend file.`);
2188
+ }
2189
+ return out;
2190
+ }
2191
+ function resolveExternalTexture(blenderPath, srgb, importer, blendUrl) {
2192
+ if (typeof document === "undefined") return null;
2193
+ const path = blenderPath.replace(/^\/\//, "").replace(/\\/g, "/").replace(/^\.\//, "");
2194
+ if (!path) return null;
2195
+ let url = importer.resolveURL(path);
2196
+ if (url === path && !/^([a-z][a-z0-9+.-]*:|\/|blob:|data:)/i.test(url)) {
2197
+ url = blendUrl.replace(/[?#].*$/, "").replace(/[^/]*$/, "") + path;
2198
+ }
2199
+ const isData = /\.(exr|hdr|rgbe)$/i.test(path.split(/[?#]/)[0]);
2200
+ let texture;
2201
+ if (isData) {
2202
+ texture = new threepipe.DataTexture(new Uint8Array([255, 255, 255, 255]), 1, 1);
2203
+ } else {
2204
+ const canvas = document.createElement("canvas");
2205
+ canvas.width = canvas.height = 1;
2206
+ const c2d = canvas.getContext("2d");
2207
+ if (c2d) {
2208
+ c2d.fillStyle = "#ffffff";
2209
+ c2d.fillRect(0, 0, 1, 1);
2210
+ }
2211
+ texture = new threepipe.Texture(canvas);
2212
+ }
2213
+ texture.needsUpdate = true;
2214
+ if (srgb) texture.colorSpace = threepipe.SRGBColorSpace;
2215
+ return { texture, url, srgb, path };
2216
+ }
2217
+ function flipDataTextureRowsY(tex) {
2218
+ const img = tex.image;
2219
+ const data = img?.data;
2220
+ if (!data?.length || !img.width || !img.height) return;
2221
+ const { width, height } = img;
2222
+ const elemsPerRow = data.length / height;
2223
+ if (!Number.isInteger(elemsPerRow)) return;
2224
+ const flipped = new data.constructor(data.length);
2225
+ for (let y = 0; y < height; y++) {
2226
+ flipped.set(data.subarray(y * elemsPerRow, (y + 1) * elemsPerRow), (height - 1 - y) * elemsPerRow);
2227
+ }
2228
+ const newImage = { data: flipped, width, height };
2229
+ tex.image = newImage;
2230
+ tex.source = new threepipe.Source(newImage);
2231
+ tex.needsUpdate = true;
2232
+ }
2233
+ async function importExternalTexture(req, importer) {
2234
+ try {
2235
+ const imported = await importer.importSingle(req.url);
2236
+ if (imported && imported.isTexture && imported.image) {
2237
+ const wrapS = req.texture.wrapS, wrapT = req.texture.wrapT;
2238
+ req.texture.copy(imported);
2239
+ req.texture.wrapS = wrapS;
2240
+ req.texture.wrapT = wrapT;
2241
+ req.texture.colorSpace = req.srgb ? threepipe.SRGBColorSpace : imported.colorSpace;
2242
+ if (imported.isDataTexture && imported.flipY === false) flipDataTextureRowsY(req.texture);
2243
+ req.texture.needsUpdate = true;
2244
+ } else {
2245
+ console.warn("BlendLoadPlugin - external texture not found/unsupported:", req.path);
2246
+ }
2247
+ } catch (e) {
2248
+ console.warn("BlendLoadPlugin - external texture failed:", req.path, e);
2249
+ }
2250
+ }
1003
2251
  const _BlendLoadPlugin = class _BlendLoadPlugin extends threepipe.BaseImporterPlugin {
1004
2252
  constructor() {
1005
2253
  super();
1006
2254
  this._importer = new threepipe.Importer(class extends threepipe.FileLoader {
1007
2255
  async loadAsync(url, onProgress) {
1008
2256
  this.setResponseType("arraybuffer");
1009
- const res = await super.loadAsync(url, onProgress);
1010
- const blend = await parseBlend(res);
2257
+ let res = await super.loadAsync(url, onProgress);
2258
+ const decompressed = decompressBlend(res);
2259
+ res = null;
2260
+ const blend = await parseBlend(decompressed);
2261
+ const importer = this.assetImporter;
2262
+ const texReqs = [];
1011
2263
  const ctx = {
1012
2264
  Object3D: threepipe.Object3D2,
1013
2265
  Mesh: threepipe.Mesh2,
1014
2266
  MeshPhysicalMaterial: threepipe.PhysicalMaterial,
2267
+ MeshBasicMaterial: threepipe.UnlitMaterial,
1015
2268
  PerspectiveCamera: threepipe.PerspectiveCamera0,
1016
2269
  OrthographicCamera: threepipe.OrthographicCamera0,
1017
2270
  PointLight: threepipe.PointLight2,
2271
+ DirectionalLight: threepipe.DirectionalLight2,
2272
+ SpotLight: threepipe.SpotLight2,
2273
+ AmbientLight: threepipe.AmbientLight2,
1018
2274
  BufferGeometry: threepipe.BufferGeometry2,
1019
- BufferAttribute: threepipe.BufferAttribute
2275
+ BufferAttribute: threepipe.BufferAttribute,
2276
+ loadExternalTexture: importer ? (p, srgb) => {
2277
+ const req = resolveExternalTexture(p, srgb, importer, url);
2278
+ if (req) texReqs.push(req);
2279
+ return req ? req.texture : null;
2280
+ } : void 0
1020
2281
  };
1021
2282
  const objects = await createObjects(blend, ctx);
2283
+ if (importer && texReqs.length) await Promise.all(texReqs.map((r) => importExternalTexture(r, importer)));
1022
2284
  const root = new threepipe.Object3D();
1023
2285
  root.add(...objects);
1024
2286
  blend.scene = root;
@@ -1030,12 +2292,15 @@
1030
2292
  }
1031
2293
  return res.scene;
1032
2294
  }
1033
- }, ["blend"], ["application/x-blender"], true);
2295
+ }, ["blend"], ["application/x-blender"], true, (loader, assetImporter) => {
2296
+ if (loader) loader.assetImporter = assetImporter;
2297
+ return loader;
2298
+ });
1034
2299
  }
1035
2300
  };
1036
2301
  _BlendLoadPlugin.PluginType = "BlendLoadPlugin";
1037
2302
  let BlendLoadPlugin = _BlendLoadPlugin;
1038
2303
  exports2.BlendLoadPlugin = BlendLoadPlugin;
1039
2304
  Object.defineProperty(exports2, Symbol.toStringTag, { value: "Module" });
1040
- });
2305
+ }));
1041
2306
  //# sourceMappingURL=index.js.map