@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/BlendLoadPlugin.d.ts +1 -1
- package/dist/BlendLoadPlugin.d.ts.map +1 -1
- package/dist/index.js +400 -80
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +401 -81
- package/dist/index.mjs.map +1 -1
- package/dist/loader/bakeGetters.d.ts +2 -0
- package/dist/loader/bakeGetters.d.ts.map +1 -0
- package/dist/loader/camera.d.ts +3 -0
- package/dist/loader/camera.d.ts.map +1 -0
- package/dist/loader/geometry.d.ts +2 -2
- package/dist/loader/geometry.d.ts.map +1 -1
- package/dist/loader/index.d.ts +1 -1
- package/dist/loader/index.d.ts.map +1 -1
- package/dist/loader/light.d.ts +2 -2
- package/dist/loader/light.d.ts.map +1 -1
- package/dist/loader/material.d.ts +1 -2
- package/dist/loader/material.d.ts.map +1 -1
- package/dist/loader/mesh.d.ts +3 -2
- package/dist/loader/mesh.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/BlendLoadPlugin.ts +37 -3
- package/src/js-blend/main.js +3 -2
- package/src/js-blend/parser/parser.js +152 -24
- package/src/js-blend/threejs/blend_three.js +1 -1
- package/src/js-blend/threejs/light.js +1 -1
- package/src/js-blend/threejs/material.js +1 -1
- package/src/loader/bakeGetters.ts +63 -0
- package/src/loader/camera.ts +46 -0
- package/src/loader/geometry.ts +234 -13
- package/src/loader/index.ts +58 -6
- package/src/loader/light.ts +17 -13
- package/src/loader/material.ts +5 -5
- package/src/loader/mesh.ts +26 -13
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.
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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.
|
|
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
|
|
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
|
|
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
|
-
|
|
486
|
-
|
|
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
|
-
|
|
559
|
+
const code_str = toString(_data, offset, offset + 4);
|
|
491
560
|
offset2 = offset + 16 + pointer_size;
|
|
492
|
-
|
|
493
|
-
if (
|
|
494
|
-
|
|
495
|
-
|
|
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)
|
|
540
|
-
|
|
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 =
|
|
690
|
+
let light = void 0;
|
|
559
691
|
switch (ldata.type) {
|
|
560
692
|
case blenderLightTypes.point:
|
|
561
|
-
light = new
|
|
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
|
|
698
|
+
light = new ctx.PointLight(color, intensity, distance);
|
|
567
699
|
light.position.fromArray(position, 0);
|
|
568
700
|
light.castShadow = true;
|
|
569
|
-
light.shadow
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
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
|
|
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
|
|
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]
|
|
603
|
-
positions[currentIndex * 3 + 1] = vert.co[2]
|
|
604
|
-
positions[currentIndex * 3 + 2] = -vert.co[1]
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
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
|
|
618
|
-
geometry.setIndex(new
|
|
619
|
-
geometry.setAttribute("normal", new
|
|
620
|
-
geometry.setAttribute("uv", new
|
|
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
|
|
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 =
|
|
880
|
+
material.opacity = 1;
|
|
629
881
|
material.transparent = material.opacity < 1;
|
|
630
882
|
return material;
|
|
631
883
|
}
|
|
632
|
-
function
|
|
633
|
-
|
|
634
|
-
|
|
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) :
|
|
637
|
-
|
|
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
|
|
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
|
-
|
|
928
|
+
obj = createMesh(object, loaded, ctx);
|
|
658
929
|
break;
|
|
659
930
|
case blenderObjectTypes.lamp:
|
|
660
|
-
|
|
931
|
+
obj = createLight(object, ctx);
|
|
932
|
+
break;
|
|
933
|
+
case blenderObjectTypes.camera:
|
|
934
|
+
obj = createCamera(object, ctx);
|
|
661
935
|
break;
|
|
662
936
|
default:
|
|
663
|
-
|
|
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
|
|
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);
|