@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.mjs
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @license
|
|
3
|
-
* @threepipe/plugin-blend-importer v0.0.
|
|
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 {
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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.
|
|
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
|
|
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
|
|
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
|
-
|
|
483
|
-
|
|
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
|
-
|
|
556
|
+
const code_str = toString(_data, offset, offset + 4);
|
|
488
557
|
offset2 = offset + 16 + pointer_size;
|
|
489
|
-
|
|
490
|
-
if (
|
|
491
|
-
|
|
492
|
-
|
|
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)
|
|
537
|
-
|
|
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 =
|
|
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
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
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
|
|
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]
|
|
600
|
-
positions[currentIndex * 3 + 1] = vert.co[2]
|
|
601
|
-
positions[currentIndex * 3 + 2] = -vert.co[1]
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
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 =
|
|
877
|
+
material.opacity = 1;
|
|
626
878
|
material.transparent = material.opacity < 1;
|
|
627
879
|
return material;
|
|
628
880
|
}
|
|
629
|
-
function
|
|
630
|
-
|
|
631
|
-
|
|
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) :
|
|
634
|
-
|
|
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
|
|
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
|
-
|
|
925
|
+
obj = createMesh(object, loaded, ctx);
|
|
655
926
|
break;
|
|
656
927
|
case blenderObjectTypes.lamp:
|
|
657
|
-
|
|
928
|
+
obj = createLight(object, ctx);
|
|
929
|
+
break;
|
|
930
|
+
case blenderObjectTypes.camera:
|
|
931
|
+
obj = createCamera(object, ctx);
|
|
658
932
|
break;
|
|
659
933
|
default:
|
|
660
|
-
|
|
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
|
|
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);
|