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