@threepipe/plugin-blend-importer 0.0.4 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,13 +1,11 @@
1
1
  /**
2
2
  * @license
3
- * @threepipe/plugin-blend-importer v0.0.4
3
+ * @threepipe/plugin-blend-importer v0.0.5
4
4
  * Copyright 2022-2025 repalash <palash@shaders.app>
5
5
  * Apache-2.0 License
6
6
  * See ./dependencies.txt for any bundled third-party dependencies and licenses.
7
7
  */
8
- import { PointLight, BufferGeometry, BufferAttribute, MeshPhysicalMaterial, Mesh, BaseImporterPlugin, Importer, FileLoader, Object3D } from "threepipe";
9
- const DNA1 = 826363460;
10
- const ENDB = 1111772741;
8
+ import { BaseImporterPlugin, Importer, FileLoader, Object3D, Object3D2, PerspectiveCamera0, OrthographicCamera0, PointLight2, PhysicalMaterial, Mesh2, BufferGeometry2, BufferAttribute } from "threepipe";
11
9
  let worker = null;
12
10
  const return_object = {
13
11
  loadBlendFromArrayBuffer: function(array_buffer) {
@@ -48,7 +46,7 @@ function worker_code() {
48
46
  self.postMessage(FILE, ERROR);
49
47
  }
50
48
  }
51
- var BLENDER_FILE = function(AB2) {
49
+ const BLENDER_FILE = function(AB2) {
52
50
  this.AB = AB2;
53
51
  this.byte = new Uint8Array(AB2);
54
52
  this.dv = new DataView(AB2);
@@ -87,7 +85,9 @@ function worker_code() {
87
85
  else {
88
86
  this.__blender_file__.dv.setFloat64(this.__data_address__ + offset2, float, this.__blender_file__.template.endianess);
89
87
  }
90
- }
88
+ },
89
+ enumerable: true,
90
+ configurable: true
91
91
  };
92
92
  }
93
93
  function floatProp(offset2, Blender_Array_Length, length) {
@@ -100,7 +100,9 @@ function worker_code() {
100
100
  else {
101
101
  this.__blender_file__.dv.setFloat32(this.__data_address__ + offset2, float, this.__blender_file__.template.endianess);
102
102
  }
103
- }
103
+ },
104
+ enumerable: true,
105
+ configurable: true
104
106
  };
105
107
  }
106
108
  function intProp(offset2, Blender_Array_Length, length) {
@@ -113,7 +115,9 @@ function worker_code() {
113
115
  else {
114
116
  this.__blender_file__.dv.setInt32(this.__data_address__ + offset2, float, this.__blender_file__.template.endianess);
115
117
  }
116
- }
118
+ },
119
+ enumerable: true,
120
+ configurable: true
117
121
  };
118
122
  }
119
123
  function shortProp(offset2, Blender_Array_Length, length) {
@@ -126,9 +130,26 @@ function worker_code() {
126
130
  else {
127
131
  this.__blender_file__.dv.setInt16(this.__data_address__ + offset2, float, this.__blender_file__.template.endianess);
128
132
  }
129
- }
133
+ },
134
+ enumerable: true,
135
+ configurable: true
130
136
  };
131
137
  }
138
+ const uShortProp = (offset2, Blender_Array_Length, length) => {
139
+ return {
140
+ get: function() {
141
+ 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);
142
+ },
143
+ set: function(float) {
144
+ if (Blender_Array_Length > 1) ;
145
+ else {
146
+ this.__blender_file__.dv.setUint16(this.__data_address__ + offset2, float, this.__blender_file__.template.endianess);
147
+ }
148
+ },
149
+ enumerable: true,
150
+ configurable: true
151
+ };
152
+ };
132
153
  function charProp(offset2, Blender_Array_Length, length) {
133
154
  return {
134
155
  get: function() {
@@ -157,7 +178,9 @@ function worker_code() {
157
178
  } else {
158
179
  this.__blender_file__.byte[this.__data_address__ + offset2] = byte | 0;
159
180
  }
160
- }
181
+ },
182
+ enumerable: true,
183
+ configurable: true
161
184
  };
162
185
  }
163
186
  function pointerProp2(offset2) {
@@ -180,7 +203,9 @@ function worker_code() {
180
203
  return results;
181
204
  },
182
205
  set: function() {
183
- }
206
+ },
207
+ enumerable: true,
208
+ configurable: true
184
209
  };
185
210
  }
186
211
  function pointerProp(offset2, Blender_Array_Length, length) {
@@ -203,7 +228,9 @@ function worker_code() {
203
228
  }
204
229
  },
205
230
  set: function() {
206
- }
231
+ },
232
+ enumerable: true,
233
+ configurable: true
207
234
  };
208
235
  }
209
236
  function compileProp(obj, name, type, offset2, array_size, IS_POINTER, pointer_size2, length) {
@@ -219,9 +246,11 @@ function worker_code() {
219
246
  Object.defineProperty(obj, name, intProp(offset2, array_size, length >> 2));
220
247
  break;
221
248
  case "short":
222
- case "ushort":
223
249
  Object.defineProperty(obj, name, shortProp(offset2, array_size, length >> 1));
224
250
  break;
251
+ case "ushort":
252
+ Object.defineProperty(obj, name, uShortProp(offset2, array_size, length >> 1));
253
+ break;
225
254
  case "char":
226
255
  case "uchar":
227
256
  Object.defineProperty(obj, name, charProp(offset2, array_size, length));
@@ -234,6 +263,7 @@ function worker_code() {
234
263
  offset2 += length;
235
264
  } else {
236
265
  Object.defineProperty(obj, name, pointerProp(offset2, array_size, pointer_size2));
266
+ obj._length += pointer_size2 * array_size;
237
267
  offset2 += pointer_size2 * array_size;
238
268
  }
239
269
  return offset2;
@@ -250,19 +280,20 @@ function worker_code() {
250
280
  MASTER_SDNA_SCHEMA.prototype = {
251
281
  getSDNAStructureConstructor: function(name, struct) {
252
282
  if (struct) {
253
- const blen_struct = Function("function " + name + "(){}; return " + name)();
283
+ const jsName = name === "void" ? "void_" : name;
284
+ const blen_struct = Function("function " + jsName + "(){}; return " + jsName)();
254
285
  blen_struct.prototype = new BLENDER_STRUCTURE();
255
286
  blen_struct.prototype.blender_name = name;
256
287
  blen_struct.prototype.__pointers = [];
257
288
  blen_struct.prototype.__list__ = [];
289
+ const DNA = this.SDNA[name] = {
290
+ constructor: blen_struct
291
+ };
258
292
  let offset2 = 0;
259
293
  for (let i = 0; i < struct.length; i += 3) {
260
294
  let _name = struct[i];
261
295
  const n = _name, type = struct[i + 1];
262
296
  let length = struct[i + 2], array_length = 0, match = null, Blender_Array_Length = 1, Suparray_match = 1, PointerToArray = false, Pointer_Match = 0;
263
- const DNA = this.SDNA[name] = {
264
- constructor: blen_struct
265
- };
266
297
  if (match = _name.match(/(\*?)(\*?)(\w+)(\[(\w*)\])?(\[(\w*)\])?/)) {
267
298
  _name = match[3];
268
299
  if (match[1]) {
@@ -307,7 +338,9 @@ function worker_code() {
307
338
  }
308
339
  return array;
309
340
  };
310
- }(array_names)
341
+ }(array_names),
342
+ enumerable: true,
343
+ configurable: true
311
344
  });
312
345
  } else {
313
346
  offset2 = compileProp(blen_struct.prototype, _name, type, offset2, Blender_Array_Length, Pointer_Match, pointer_size, length);
@@ -322,7 +355,7 @@ function worker_code() {
322
355
  }
323
356
  }
324
357
  };
325
- var BLENDER_STRUCTURE = function() {
358
+ const BLENDER_STRUCTURE = function() {
326
359
  this.__blender_file__ = null;
327
360
  this.__list__ = null;
328
361
  this.__super_array_list__ = null;
@@ -390,7 +423,8 @@ function worker_code() {
390
423
  FILE.memory_lookup = {};
391
424
  struct_names = [];
392
425
  offset = 0;
393
- if (toString(_data, offset, 7) !== "BLENDER") return ERROR = "File supplied is not a .blend compatible Blender file.";
426
+ const magic = toString(_data, offset, 7);
427
+ if (magic !== "BLENDER") return ERROR = "File supplied is not a .blend compatible Blender file.";
394
428
  offset += 7;
395
429
  pointer_size = toString(_data, offset++, offset) == "_" ? 4 : 8;
396
430
  BIG_ENDIAN = toString(_data, offset++, offset) !== "V";
@@ -427,9 +461,17 @@ function worker_code() {
427
461
  offset2++;
428
462
  curr_count++;
429
463
  }
430
- if (offset2 % 4 > 0) offset2 = 4 - offset2 % 4 + offset2;
464
+ if (toString(_data, offset2, offset2 + 4) !== "TYPE") {
465
+ for (let j = 0; j < 8; j++) {
466
+ if (toString(_data, offset2, offset2 + 4) === "TYPE") break;
467
+ offset2++;
468
+ }
469
+ }
470
+ if (toString(_data, offset2, offset2 + 4) !== "TYPE") {
471
+ debugger;
472
+ }
431
473
  offset2 += 4;
432
- count = data.getInt32(offset2, true);
474
+ count = data.getUint32(offset2, true);
433
475
  offset2 += 4;
434
476
  curr_count = 0;
435
477
  while (curr_count < count) {
@@ -442,7 +484,15 @@ function worker_code() {
442
484
  offset2++;
443
485
  curr_count++;
444
486
  }
445
- if (offset2 % 4 > 0) offset2 = 4 - offset2 % 4 + offset2;
487
+ if (toString(_data, offset2, offset2 + 4) !== "TLEN") {
488
+ for (let j = 0; j < 8; j++) {
489
+ if (toString(_data, offset2, offset2 + 4) === "TLEN") break;
490
+ offset2++;
491
+ }
492
+ }
493
+ if (toString(_data, offset2, offset2 + 4) !== "TLEN") {
494
+ debugger;
495
+ }
446
496
  offset2 += 4;
447
497
  curr_count = 0;
448
498
  while (curr_count < count) {
@@ -450,7 +500,15 @@ function worker_code() {
450
500
  offset2 += 2;
451
501
  curr_count++;
452
502
  }
453
- if (offset2 % 4 > 0) offset2 = 4 - offset2 % 4 + offset2;
503
+ if (toString(_data, offset2, offset2 + 4) !== "STRC") {
504
+ for (let j = 0; j < 8; j++) {
505
+ if (toString(_data, offset2, offset2 + 4) === "STRC") break;
506
+ offset2++;
507
+ }
508
+ }
509
+ if (toString(_data, offset2, offset2 + 4) !== "STRC") {
510
+ debugger;
511
+ }
454
512
  offset2 += 4;
455
513
  const structure_count = data.getInt32(offset2, BIG_ENDIAN);
456
514
  offset2 += 4;
@@ -479,22 +537,56 @@ function worker_code() {
479
537
  }
480
538
  }
481
539
  while (true) {
482
- if (offset % 4 > 0) {
483
- offset = 4 - offset % 4 + offset;
540
+ for (let j = 0; j < 8; j++) {
541
+ if (data.getInt8(offset) === 0) {
542
+ offset++;
543
+ continue;
544
+ }
545
+ break;
546
+ }
547
+ if (data.getInt8(offset) === 0) {
548
+ debugger;
484
549
  }
485
550
  data_offset = offset;
551
+ if (offset + pointer_size + 12 >= data.byteLength) {
552
+ ERROR = "Unexpected end of file while parsing";
553
+ break;
554
+ }
486
555
  sdna_index = data.getInt32(offset + pointer_size + 8, BIG_ENDIAN);
487
- let code_uint = data.getUint32(offset, BIG_ENDIAN);
556
+ const code_str = toString(_data, offset, offset + 4);
488
557
  offset2 = offset + 16 + pointer_size;
489
- offset += data.getInt32(offset + 4, true) + 16 + pointer_size;
490
- if (code_uint === DNA1) ;
491
- else if (code_uint === ENDB) break;
492
- else {
558
+ const blockLength = data.getInt32(offset + 4, true);
559
+ if (blockLength < 0 || offset + blockLength + 16 + pointer_size > data.byteLength) {
560
+ ERROR = "Invalid block length detected";
561
+ break;
562
+ }
563
+ offset += blockLength + 16 + pointer_size;
564
+ if (code_str === "DNA1") ;
565
+ else if (code_str === "ENDB") break;
566
+ else if (code_str === "TEST") {
567
+ const data_start = data_offset + pointer_size + 16;
568
+ const width = data.getInt32(data_start, BIG_ENDIAN);
569
+ const height = data.getInt32(data_start + 4, BIG_ENDIAN);
570
+ if (width * height > 0) {
571
+ const data_len = width * height * 4;
572
+ if (blockLength < data_len + 8) {
573
+ ERROR = "Invalid TEST block length detected";
574
+ break;
575
+ }
576
+ const image_data = new Uint32Array(_data, data_start + 8, data_len >> 2);
577
+ const image = {
578
+ width,
579
+ height,
580
+ data: image_data
581
+ };
582
+ FILE.thumbnail = image;
583
+ }
584
+ } else {
493
585
  const data_start = data_offset + pointer_size + 16;
494
586
  const constructor = current_SDNA_template.getSDNAStructureConstructor(current_SDNA_template.SDNA_NAMES[sdna_index]);
495
587
  const size = data.getInt32(data_offset + 4, BIG_ENDIAN);
496
588
  count = data.getInt32(data_offset + 12 + pointer_size, BIG_ENDIAN);
497
- if (count > 0) {
589
+ if (count > 0 && constructor) {
498
590
  let obj = new constructor();
499
591
  const length = constructor.prototype._length;
500
592
  const address = FILE.getPointer(data_offset + 8);
@@ -518,8 +610,8 @@ function worker_code() {
518
610
  }
519
611
  }
520
612
  worker = new worker_code();
521
- worker.postMessage = function(message) {
522
- return_object.onParseReady(message);
613
+ worker.postMessage = function(message, err) {
614
+ return_object.onParseReady(message, err);
523
615
  };
524
616
  /**
525
617
  * JS.Blend
@@ -533,49 +625,201 @@ worker.postMessage = function(message) {
533
625
  async function parseBlend(buffer, name = "") {
534
626
  return new Promise((res, rej) => {
535
627
  return_object.onParseReady = (file, error) => {
536
- if (error) rej(error);
537
- else res(file);
628
+ if (error) console.error(error);
629
+ res(file);
538
630
  };
539
631
  return_object.loadBlendFromArrayBuffer(buffer, name);
540
632
  });
541
633
  }
634
+ function createCamera(object, ctx) {
635
+ const cdata = object.data;
636
+ if (!cdata) return void 0;
637
+ const type = cdata.type;
638
+ let camera;
639
+ if (type === 0) {
640
+ camera = new ctx.PerspectiveCamera(
641
+ cdata.lens || 50,
642
+ // fov or lens
643
+ (cdata.sensor_x || 36) / (cdata.sensor_y || 24),
644
+ // aspect
645
+ cdata.clipsta || 0.1,
646
+ // near
647
+ cdata.clipend || 1e3
648
+ // far
649
+ );
650
+ camera.position.set(0, 0, 0);
651
+ if ("shiftx" in cdata) camera.userData.shiftX = cdata.shiftx;
652
+ if ("shifty" in cdata) camera.userData.shiftY = cdata.shifty;
653
+ } else if (type === 1) {
654
+ const scale = cdata.ortho_scale || 1;
655
+ const aspect = (cdata.sensor_x || 36) / (cdata.sensor_y || 24);
656
+ const left = -scale * aspect / 2;
657
+ const right = scale * aspect / 2;
658
+ const top = scale / 2;
659
+ const bottom = -scale / 2;
660
+ camera = new ctx.OrthographicCamera(
661
+ left,
662
+ right,
663
+ top,
664
+ bottom,
665
+ cdata.clipsta || 0.1,
666
+ cdata.clipend || 1e3
667
+ );
668
+ camera.position.set(0, 0, 0);
669
+ if ("shiftx" in cdata) camera.userData.shiftX = cdata.shiftx;
670
+ if ("shifty" in cdata) camera.userData.shiftY = cdata.shifty;
671
+ } else {
672
+ camera = new ctx.PerspectiveCamera();
673
+ console.warn("Unsupported camera type", type);
674
+ }
675
+ return camera;
676
+ }
542
677
  const blenderLightTypes = {
543
678
  point: 0,
544
- sun: 1,
545
- spot: 0,
546
- hemi: 0,
547
- area: 0
679
+ sun: 1
548
680
  };
549
- function createLight(lamp) {
681
+ function createLight(lamp, ctx) {
550
682
  const ldata = lamp.data;
551
683
  const position = [lamp.loc[0], lamp.loc[2], -lamp.loc[1]];
552
684
  const color = ldata.r * 255 << 16 | ldata.g * 255 << 8 | ldata.b * 255 << 0;
553
685
  const intensity = ldata.energy;
554
686
  const distance = 0;
555
- let light = null;
687
+ let light = void 0;
556
688
  switch (ldata.type) {
557
689
  case blenderLightTypes.point:
558
- light = new PointLight(color, intensity, distance);
690
+ light = new ctx.PointLight(color, intensity, distance);
559
691
  light.position.fromArray(position, 0);
560
692
  light.castShadow = true;
561
693
  break;
562
694
  case blenderLightTypes.sun:
563
- light = new PointLight(color, intensity, distance);
695
+ light = new ctx.PointLight(color, intensity, distance);
564
696
  light.position.fromArray(position, 0);
565
697
  light.castShadow = true;
566
- light.shadow.mapSize.width = 1024;
567
- light.shadow.mapSize.height = 1024;
568
- light.shadow.camera.near = 0.01;
569
- light.shadow.camera.far = 500;
698
+ if (light.shadow) {
699
+ light.shadow.mapSize.width = 1024;
700
+ light.shadow.mapSize.height = 1024;
701
+ light.shadow.camera.near = 0.01;
702
+ light.shadow.camera.far = 500;
703
+ }
570
704
  break;
571
705
  default:
572
706
  console.warn("Unsupported light type", ldata.type);
573
707
  }
574
708
  return light;
575
709
  }
576
- function createBufferGeometry(mesh, origin) {
710
+ function getLayer(layers, i) {
711
+ if (!Array.isArray(layers)) return layers;
712
+ return layers[i];
713
+ }
714
+ function createBufferGeometry(meshData, ctx) {
715
+ var _a;
716
+ if (meshData.mpoly) return createBufferGeometryOld(meshData, ctx);
717
+ const geometry = new ctx.BufferGeometry();
718
+ geometry.name = meshData.aname || "";
719
+ let vertices;
720
+ let indices;
721
+ if (meshData.vdata && meshData.vdata.layers && meshData.vdata.totlayer > 0) {
722
+ for (let i = 0; i < meshData.vdata.totlayer; i++) {
723
+ const layer = getLayer(meshData.vdata.layers, i);
724
+ const data = layer.data || [];
725
+ if (data.length === meshData.totvert) {
726
+ if (vertices && (layer.name !== "position" || vertices.name === "position")) {
727
+ continue;
728
+ }
729
+ vertices = layer;
730
+ }
731
+ }
732
+ }
733
+ if (meshData.ldata && meshData.ldata.layers) {
734
+ for (let i = 0; i < meshData.ldata.totlayer; i++) {
735
+ const layer = getLayer(meshData.ldata.layers, i);
736
+ const data = layer.data || [];
737
+ if (data.length === meshData.totloop) {
738
+ if (indices && (layer.name !== ".corner_vert" || indices.name === ".corner_vert")) {
739
+ continue;
740
+ }
741
+ indices = layer;
742
+ }
743
+ }
744
+ }
745
+ let faceIndices = [];
746
+ if (meshData.poly_offset_indices && meshData.totpoly > 0) {
747
+ faceIndices = [...new Int32Array(meshData.poly_offset_indices.__blender_file__.AB, meshData.poly_offset_indices.__data_address__, meshData.totpoly + 1)];
748
+ }
749
+ if ((vertices == null ? void 0 : vertices.data) && vertices.data.length > 0) {
750
+ const positions = new Float32Array(vertices.data.length * 3);
751
+ for (let j = 0; j < vertices.data.length; j++) {
752
+ const vertex = vertices.data[j];
753
+ if (vertex.x !== void 0) {
754
+ positions[j * 3] = vertex.x;
755
+ positions[j * 3 + 1] = vertex.z;
756
+ positions[j * 3 + 2] = -vertex.y;
757
+ } else if (vertex.co !== void 0) {
758
+ positions[j * 3] = vertex.co[0];
759
+ positions[j * 3 + 1] = vertex.co[2];
760
+ positions[j * 3 + 2] = -vertex.co[1];
761
+ } else {
762
+ console.error("BlendLoader - unknown vertex", vertex);
763
+ break;
764
+ }
765
+ }
766
+ geometry.setAttribute("position", new ctx.BufferAttribute(positions, 3));
767
+ }
768
+ if ((indices == null ? void 0 : indices.data) && indices.data.length > 0 && ((_a = vertices == null ? void 0 : vertices.data) == null ? void 0 : _a.length)) {
769
+ const faceSize = meshData.totloop / meshData.totpoly;
770
+ if (faceIndices.length > 0) {
771
+ let totalTriangles = 0;
772
+ for (let i = 0; i < faceIndices.length - 1; i++) {
773
+ const faceVertCount = faceIndices[i + 1] - faceIndices[i];
774
+ totalTriangles += Math.max(0, faceVertCount - 2);
775
+ }
776
+ const indexes = new Uint32Array(totalTriangles * 3);
777
+ let t = 0;
778
+ for (let i = 0; i < faceIndices.length - 1; i++) {
779
+ const faceStart = faceIndices[i];
780
+ const faceEnd = faceIndices[i + 1];
781
+ const faceVertCount = faceEnd - faceStart;
782
+ if (faceVertCount >= 3) {
783
+ const firstVert = indices.data[faceStart].i;
784
+ for (let k = 1; k < faceVertCount - 1; k++) {
785
+ indexes[t++] = firstVert;
786
+ indexes[t++] = indices.data[faceStart + k].i;
787
+ indexes[t++] = indices.data[faceStart + k + 1].i;
788
+ }
789
+ }
790
+ }
791
+ geometry.setIndex(new ctx.BufferAttribute(indexes, 1));
792
+ } else if (faceSize === 3 || faceSize === 4) {
793
+ const isQuad = faceSize === 4;
794
+ const faceCount = indices.length / faceSize;
795
+ const indexes = new Uint32Array(faceCount * 3 * (isQuad ? 2 : 1));
796
+ if (faceSize !== 3 && faceSize !== 4) return geometry;
797
+ for (let j = 0, t = 0; j < indices.length; j += faceSize) {
798
+ const a = indices[j].i;
799
+ const b = indices[j + 1].i;
800
+ const c = indices[j + 2].i;
801
+ indexes[t++] = a;
802
+ indexes[t++] = b;
803
+ indexes[t++] = c;
804
+ if (isQuad) {
805
+ const d = indices[j + 3].i;
806
+ indexes[t++] = a;
807
+ indexes[t++] = c;
808
+ indexes[t++] = d;
809
+ }
810
+ }
811
+ geometry.setIndex(new ctx.BufferAttribute(indexes, 1));
812
+ }
813
+ } else if (faceIndices) {
814
+ console.error("BlendLoader - no indices data found, but face indices are present", faceIndices);
815
+ }
816
+ if (geometry.attributes.position && !geometry.attributes.normal)
817
+ geometry.computeVertexNormals();
818
+ return geometry;
819
+ }
820
+ function createBufferGeometryOld(mesh, ctx) {
577
821
  const faces = Array.isArray(mesh.mpoly) ? mesh.mpoly : [mesh.mpoly], loops = mesh.mloop, uv = mesh.mloopuv, vertices = mesh.mvert;
578
- const geometry = new BufferGeometry();
822
+ const geometry = new ctx.BufferGeometry();
579
823
  if (!faces) return geometry;
580
824
  const size = faces.reduce((acc, face) => acc + Math.floor(face.totloop * 3 / 2), 0);
581
825
  const indices = new Uint32Array(size);
@@ -583,6 +827,7 @@ function createBufferGeometry(mesh, origin) {
583
827
  const normals = new Float32Array(size * 3);
584
828
  const positions = new Float32Array(size * 3);
585
829
  let currentIndex = 0;
830
+ let computeNormals = false;
586
831
  for (const face of faces) {
587
832
  const len = face.totloop;
588
833
  const start = face.loopstart;
@@ -596,12 +841,16 @@ function createBufferGeometry(mesh, origin) {
596
841
  const loop = loops[index];
597
842
  const vert = vertices[loop.v];
598
843
  indices[currentIndex] = currentIndex;
599
- positions[currentIndex * 3 + 0] = vert.co[0] + origin[0];
600
- positions[currentIndex * 3 + 1] = vert.co[2] + origin[2];
601
- positions[currentIndex * 3 + 2] = -vert.co[1] + -0;
602
- normals[currentIndex * 3 + 0] = vert.no[0];
603
- normals[currentIndex * 3 + 1] = vert.no[2];
604
- normals[currentIndex * 3 + 2] = -vert.no[1];
844
+ positions[currentIndex * 3 + 0] = vert.co[0];
845
+ positions[currentIndex * 3 + 1] = vert.co[2];
846
+ positions[currentIndex * 3 + 2] = -vert.co[1];
847
+ if (vert.no) {
848
+ normals[currentIndex * 3 + 0] = vert.no[0];
849
+ normals[currentIndex * 3 + 1] = vert.no[2];
850
+ normals[currentIndex * 3 + 2] = -vert.no[1];
851
+ } else {
852
+ computeNormals = true;
853
+ }
605
854
  if (uv) {
606
855
  uvs[currentIndex * 2 + 0] = uv[index].uv[0];
607
856
  uvs[currentIndex * 2 + 1] = uv[index].uv[1];
@@ -611,55 +860,113 @@ function createBufferGeometry(mesh, origin) {
611
860
  indexi += 2;
612
861
  }
613
862
  }
614
- geometry.setAttribute("position", new BufferAttribute(positions, 3));
615
- geometry.setIndex(new BufferAttribute(indices, 1));
616
- geometry.setAttribute("normal", new BufferAttribute(normals, 3));
617
- geometry.setAttribute("uv", new BufferAttribute(uvs, 2));
863
+ geometry.setAttribute("position", new ctx.BufferAttribute(positions, 3));
864
+ geometry.setIndex(new ctx.BufferAttribute(indices, 1));
865
+ geometry.setAttribute("normal", new ctx.BufferAttribute(normals, 3));
866
+ geometry.setAttribute("uv", new ctx.BufferAttribute(uvs, 2));
867
+ if (computeNormals) {
868
+ geometry.computeVertexNormals();
869
+ }
618
870
  return geometry;
619
871
  }
620
- function createMaterial(mat) {
621
- const material = new MeshPhysicalMaterial();
872
+ function createMaterial(mat, ctx) {
873
+ const material = new ctx.MeshPhysicalMaterial();
622
874
  material.color.setRGB(mat.r, mat.g, mat.b);
623
875
  material.roughness = mat.roughness !== void 0 ? mat.roughness : 0.4;
624
876
  material.metalness = mat.metallic !== void 0 ? mat.metallic : 0;
625
- material.opacity = mat.alpha !== void 0 ? mat.alpha : 0;
877
+ material.opacity = 1;
626
878
  material.transparent = material.opacity < 1;
627
879
  return material;
628
880
  }
629
- function createMesh(object) {
630
- if (!object.data) return null;
631
- const geometry = createBufferGeometry(object.data, [0, 0, 0]);
881
+ function setCreateTransform(object, obj) {
882
+ obj.name = object.aname;
883
+ obj.quaternion.set(object.quat[1], object.quat[3], -object.quat[2], object.quat[0]);
884
+ obj.scale.set(object.size[0], object.size[1], object.size[2]);
885
+ obj.position.set(object.loc[0], object.loc[2], -object.loc[1]);
886
+ obj.updateMatrix();
887
+ }
888
+ function createMesh(object, loaded, ctx) {
889
+ if (!object.data) {
890
+ return void 0;
891
+ }
892
+ const geometry = loaded.get(object.data) ?? createBufferGeometry(object.data, ctx);
893
+ loaded.set(object.data, geometry);
632
894
  const mat = object.data.mat[0];
633
- const material = mat ? createMaterial(mat) : void 0;
634
- const mesh = new Mesh(geometry, material);
895
+ const material = mat ? loaded.get(mat) ?? createMaterial(mat, ctx) : new ctx.MeshPhysicalMaterial();
896
+ if (mat) loaded.set(mat, material);
897
+ const mesh = new ctx.Mesh(geometry, material);
635
898
  mesh.castShadow = true;
636
899
  mesh.receiveShadow = true;
637
- mesh.rotateZ(object.rot[2]);
638
- mesh.rotateY(object.rot[1]);
639
- mesh.rotateX(object.rot[0]);
640
- mesh.scale.fromArray(object.size, 0);
641
- mesh.position.fromArray([object.loc[0], object.loc[2], -object.loc[1]], 0);
642
900
  return mesh;
643
901
  }
644
902
  const blenderObjectTypes = {
903
+ empty: 0,
645
904
  mesh: 1,
646
- lamp: 10
905
+ lamp: 10,
906
+ camera: 11
647
907
  };
648
- async function createObjects(file) {
908
+ async function createObjects(file, ctx) {
649
909
  const objects = [];
650
- const blendObjects = file.objects.Object;
910
+ const childMap = /* @__PURE__ */ new Map();
911
+ const objMap = /* @__PURE__ */ new Map();
912
+ const blendObjects = file.objects.Object ?? [];
913
+ const loaded = /* @__PURE__ */ new WeakMap();
651
914
  for (const object of blendObjects) {
915
+ if (loaded.has(object)) {
916
+ console.warn("BlendLoader - duplicate object");
917
+ continue;
918
+ }
919
+ let obj = void 0;
652
920
  switch (object.type) {
921
+ case blenderObjectTypes.empty:
922
+ obj = new ctx.Object3D();
923
+ break;
653
924
  case blenderObjectTypes.mesh:
654
- objects.push(createMesh(object));
925
+ obj = createMesh(object, loaded, ctx);
655
926
  break;
656
927
  case blenderObjectTypes.lamp:
657
- objects.push(createLight(object));
928
+ obj = createLight(object, ctx);
929
+ break;
930
+ case blenderObjectTypes.camera:
931
+ obj = createCamera(object, ctx);
658
932
  break;
659
933
  default:
660
- console.warn("Unsupported object type", object.type);
934
+ obj = new ctx.Object3D();
935
+ console.warn("Unsupported object type", object.type, object, obj);
936
+ }
937
+ if (obj) {
938
+ setCreateTransform(object, obj);
939
+ const exChildren = childMap.get(object);
940
+ if (exChildren) {
941
+ for (const exChild of exChildren) {
942
+ obj.add(exChild);
943
+ }
944
+ childMap.delete(object);
945
+ }
946
+ objMap.set(object, obj);
947
+ if (object.parent === object) {
948
+ console.error("invalid, parent same as object, adding to root", object);
949
+ objects.push(obj);
950
+ } else if (!object.parent) {
951
+ objects.push(obj);
952
+ } else {
953
+ const parent = objMap.get(object.parent);
954
+ if (!parent) {
955
+ const chi = childMap.get(object.parent) ?? [];
956
+ chi.push(obj);
957
+ childMap.set(object, chi);
958
+ } else {
959
+ parent.add(obj);
960
+ }
961
+ }
962
+ loaded.set(object, obj);
661
963
  }
662
964
  }
965
+ if (childMap.size) {
966
+ const vals = [...childMap.values()].flat();
967
+ console.warn("unknown objects with parents, adding to root", vals, childMap);
968
+ objects.push(...vals);
969
+ }
663
970
  return objects.filter((o) => !!o);
664
971
  }
665
972
  const _BlendLoadPlugin = class _BlendLoadPlugin extends BaseImporterPlugin {
@@ -670,13 +977,26 @@ const _BlendLoadPlugin = class _BlendLoadPlugin extends BaseImporterPlugin {
670
977
  this.setResponseType("arraybuffer");
671
978
  const res = await super.loadAsync(url, onProgress);
672
979
  const blend = await parseBlend(res);
673
- const objects = await createObjects(blend);
980
+ const ctx = {
981
+ Object3D: Object3D2,
982
+ Mesh: Mesh2,
983
+ MeshPhysicalMaterial: PhysicalMaterial,
984
+ PerspectiveCamera: PerspectiveCamera0,
985
+ OrthographicCamera: OrthographicCamera0,
986
+ PointLight: PointLight2,
987
+ BufferGeometry: BufferGeometry2,
988
+ BufferAttribute
989
+ };
990
+ const objects = await createObjects(blend, ctx);
674
991
  const root = new Object3D();
675
992
  root.add(...objects);
676
993
  blend.scene = root;
677
994
  return blend;
678
995
  }
679
- transform(res, _) {
996
+ transform(res, options) {
997
+ if (typeof options.onBlendLoad === "function") {
998
+ options.onBlendLoad(res);
999
+ }
680
1000
  return res.scene;
681
1001
  }
682
1002
  }, ["blend"], ["application/x-blender"], true);