jsblend
Version:
A Blender to Javascript File Reader
1,376 lines (1,122 loc) • 142 kB
JavaScript
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
'use strict'
exports.byteLength = byteLength
exports.toByteArray = toByteArray
exports.fromByteArray = fromByteArray
var lookup = []
var revLookup = []
var Arr = typeof Uint8Array !== 'undefined' ? Uint8Array : Array
var code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
for (var i = 0, len = code.length; i < len; ++i) {
lookup[i] = code[i]
revLookup[code.charCodeAt(i)] = i
}
revLookup['-'.charCodeAt(0)] = 62
revLookup['_'.charCodeAt(0)] = 63
function placeHoldersCount (b64) {
var len = b64.length
if (len % 4 > 0) {
throw new Error('Invalid string. Length must be a multiple of 4')
}
// the number of equal signs (place holders)
// if there are two placeholders, than the two characters before it
// represent one byte
// if there is only one, then the three characters before it represent 2 bytes
// this is just a cheap hack to not do indexOf twice
return b64[len - 2] === '=' ? 2 : b64[len - 1] === '=' ? 1 : 0
}
function byteLength (b64) {
// base64 is 4/3 + up to two characters of the original data
return (b64.length * 3 / 4) - placeHoldersCount(b64)
}
function toByteArray (b64) {
var i, l, tmp, placeHolders, arr
var len = b64.length
placeHolders = placeHoldersCount(b64)
arr = new Arr((len * 3 / 4) - placeHolders)
// if there are placeholders, only get up to the last complete 4 chars
l = placeHolders > 0 ? len - 4 : len
var L = 0
for (i = 0; i < l; i += 4) {
tmp = (revLookup[b64.charCodeAt(i)] << 18) | (revLookup[b64.charCodeAt(i + 1)] << 12) | (revLookup[b64.charCodeAt(i + 2)] << 6) | revLookup[b64.charCodeAt(i + 3)]
arr[L++] = (tmp >> 16) & 0xFF
arr[L++] = (tmp >> 8) & 0xFF
arr[L++] = tmp & 0xFF
}
if (placeHolders === 2) {
tmp = (revLookup[b64.charCodeAt(i)] << 2) | (revLookup[b64.charCodeAt(i + 1)] >> 4)
arr[L++] = tmp & 0xFF
} else if (placeHolders === 1) {
tmp = (revLookup[b64.charCodeAt(i)] << 10) | (revLookup[b64.charCodeAt(i + 1)] << 4) | (revLookup[b64.charCodeAt(i + 2)] >> 2)
arr[L++] = (tmp >> 8) & 0xFF
arr[L++] = tmp & 0xFF
}
return arr
}
function tripletToBase64 (num) {
return lookup[num >> 18 & 0x3F] + lookup[num >> 12 & 0x3F] + lookup[num >> 6 & 0x3F] + lookup[num & 0x3F]
}
function encodeChunk (uint8, start, end) {
var tmp
var output = []
for (var i = start; i < end; i += 3) {
tmp = (uint8[i] << 16) + (uint8[i + 1] << 8) + (uint8[i + 2])
output.push(tripletToBase64(tmp))
}
return output.join('')
}
function fromByteArray (uint8) {
var tmp
var len = uint8.length
var extraBytes = len % 3 // if we have 1 byte left, pad 2 bytes
var output = ''
var parts = []
var maxChunkLength = 16383 // must be multiple of 3
// go through the array every three bytes, we'll deal with trailing stuff later
for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) {
parts.push(encodeChunk(uint8, i, (i + maxChunkLength) > len2 ? len2 : (i + maxChunkLength)))
}
// pad the end with zeros, but make sure to not forget the extra bytes
if (extraBytes === 1) {
tmp = uint8[len - 1]
output += lookup[tmp >> 2]
output += lookup[(tmp << 4) & 0x3F]
output += '=='
} else if (extraBytes === 2) {
tmp = (uint8[len - 2] << 8) + (uint8[len - 1])
output += lookup[tmp >> 10]
output += lookup[(tmp >> 4) & 0x3F]
output += lookup[(tmp << 2) & 0x3F]
output += '='
}
parts.push(output)
return parts.join('')
}
},{}],2:[function(require,module,exports){
/*jshint esversion: 6 */
const three = require("./threejs/blend_three.js");
const parser = require("./parser/parser.js")();
function loadFile(blender_file, res, rej){
three_module = three(blender_file);
//TODO: Report any errors with ThreeJS before continuing.
res({
file : blender_file,
three : three_module
});
}
/* This represents a parsed blendfile instance if parsing is successful. It will accept a string or a binary data object. Strings must be a valid URI to a blender file. Binary data may be in the form of an ArrayBuffer, TypedArray, or a Blob. Binary data must also contain the binary data of a blender file.*/
JSBLEND = (fileuri_or_filedata, name = "")=>{
const promise = new Promise(
(res, rej) =>{
parser.onParseReady = (blender_file, error) => {
if(error){
rej(error);
}else{
loadFile(blender_file, res, rej);
}
};
//If fileuri_or_filedata is a string, attempt to load the file asynchronously
if(typeof fileuri_or_filedata == "string"){
let request = new XMLHttpRequest();
request.open("GET", fileuri_or_filedata, true);
request.responseType = 'blob';
request.onload = () => {
let file = request.response;
parser.loadBlendFromBlob(new Blob([file]), fileuri_or_filedata);
};
request.send();
return;
}
if(typeof fileuri_or_filedata == "object"){
//Attempt to load from blob or array buffer;
if(fileuri_or_filedata instanceof ArrayBuffer){
parser.loadBlendFromArrayBuffer(fileuri_or_filedata, name);
return;
}
if(fileuri_or_filedata instanceof Blob){
parser.loadBlendFromBlob(fileuri_or_filedata, name);
return;
}
}
//Unknown file type passed -> abort and reject
rej("Unsupported file type passed to JSBlend ${fileuri_or_filedata}");
}
);
return promise;
};
},{"./parser/parser.js":3,"./threejs/blend_three.js":4}],3:[function(require,module,exports){
/*jshint esversion: 6 */
const DNA1 = 826363460;
const ENDB = 1111772741;
/* Note: Blender coordinates treat the Z axis as the vertical an Y as depth. */
module.exports = (function(unzipper) {
//web worker not functional in this version
USE_WEBWORKER = false;
var worker = null,
FR = new FileReader(),
return_object = {
loadBlendFromArrayBuffer: function(array_buffer) {
return_object.ready = false;
if (USE_WEBWORKER) {
worker.postMessage(array_buffer, array_buffer);
} else {
worker.onmessage({
data: array_buffer
});
}
},
loadBlendFromBlob: function(blob) {
FR.onload = function() {
return_object.loadBlendFromArrayBuffer(this.result);
};
FR.readAsArrayBuffer(blob);
},
ready: true,
onParseReady: function() {},
};
worker = new worker_code();
worker.postMessage = function(message) {
return_object.onParseReady(message);
};
function worker_code() {
"use strict";
var data = null,
_data = null,
BIG_ENDIAN = false,
pointer_size = 0,
struct_names = [],
offset = 0,
working_blend_file = null,
current_SDNA_template = null,
templates = {},
finished_objects = [],
FILE = null,
ERROR = null,
AB = null;
function parseFile(msg) {
var self = this;
if (typeof msg.data == "object") {
// reset global variables
AB = null;
data = null;
BIG_ENDIAN = false;
pointer_size = 0;
struct_names = [];
offset = 0;
working_blend_file = null;
finished_objects = [];
current_SDNA_template = null;
// set data
_data = msg.data;
AB = _data.slice();
data = new DataView(_data);
FILE = new BLENDER_FILE(AB);
//start parsing
readFile();
//export parsed data
self.postMessage(FILE, ERROR);
}
}
/*
Export object for a parsed __blender_file__.
*/
var BLENDER_FILE = function(AB) {
this.AB = AB;
//this.double = new Float64Array(AB);
this.byte = new Uint8Array(AB);
this.dv = new DataView(AB);
this.objects = {};
this.memory_lookup = {},
this.object_array = [];
this.template = null;
};
BLENDER_FILE.prototype = {
addObject: function(obj) {
this.object_array.push(obj);
if (!this.objects[obj.blender_name]) this.objects[obj.blender_name] = [];
this.objects[obj.blender_name].push(obj);
},
getPointer: function(offset) {
var pointerLow = this.dv.getUint32(offset, this.template.endianess);
if (this.template.pointer_size > 4) {
var pointerHigh = this.dv.getUint32(offset + 4, this.template.endianess);
if (this.template.endianess) {
return (pointerLow) + "l|h" + pointerHigh;
} else {
return (pointerHigh) + "h|l" + pointerLow;
}
} else {
return pointerLow;
}
}
};
self.onmessage = parseFile;
this.onmessage = parseFile;
/*
These functions map offsets in the blender __blender_file__ to basic types (byte,short,int,float) through TypedArrays;
This allows the underlying binary data to be changed.
*/
function float64Prop(offset, Blender_Array_Length, length) {
return {
get: function() {
return (Blender_Array_Length > 1) ?
new Float64Array(this.__blender_file__.AB, this.__data_address__ + offset, length) :
this.__blender_file__.dv.getFloat64(this.__data_address__ + offset, this.__blender_file__.template.endianess);
},
set: function(float) {
if (Blender_Array_Length > 1) {} else {
this.__blender_file__.dv.setFloat64(this.__data_address__ + offset, float, this.__blender_file__.template.endianess);
}
},
};
}
function floatProp(offset, Blender_Array_Length, length) {
return {
get: function() {
return (Blender_Array_Length > 1) ?
new Float32Array(this.__blender_file__.AB, this.__data_address__ + offset, length) :
this.__blender_file__.dv.getFloat32(this.__data_address__ + offset, this.__blender_file__.template.endianess);
},
set: function(float) {
if (Blender_Array_Length > 1) {} else {
this.__blender_file__.dv.setFloat32(this.__data_address__ + offset, float, this.__blender_file__.template.endianess);
}
},
};
}
function intProp(offset, Blender_Array_Length, length) {
return {
get: function() {
return (Blender_Array_Length > 1) ?
new Int32Array(this.__blender_file__.AB, this.__data_address__ + offset, length) :
this.__blender_file__.dv.getInt32(this.__data_address__ + offset, this.__blender_file__.template.endianess);
},
set: function(int) {
if (Blender_Array_Length > 1) {} else {
this.__blender_file__.dv.setInt32(this.__data_address__ + offset, float, this.__blender_file__.template.endianess);
}
},
};
}
function uIntProp(offset, Blender_Array_Length, length) {
return {
get: function() {
return (Blender_Array_Length > 1) ?
new Uint32Array(this.__blender_file__.AB, this.__data_address__ + offset, length) :
this.__blender_file__.dv.getUint32(this.__data_address__ + offset, this.__blender_file__.template.endianess);
},
set: function(int) {
if (Blender_Array_Length > 1) {} else {
this.__blender_file__.dv.setUint32(this.__data_address__ + offset, float, this.__blender_file__.template.endianess);
}
},
};
}
function shortProp(offset, Blender_Array_Length, length) {
return {
get: function() {
return (Blender_Array_Length > 1) ?
new Int16Array(this.__blender_file__.AB, this.__data_address__ + offset, length) :
this.__blender_file__.dv.getInt16(this.__data_address__ + offset, this.__blender_file__.template.endianess);
},
set: function(float) {
if (Blender_Array_Length > 1) {} else {
this.__blender_file__.dv.setInt16(this.__data_address__ + offset, float, this.__blender_file__.template.endianess);
}
},
};
}
var uShortProp = (offset, Blender_Array_Length, length) => {
return {
get: function() {
return (Blender_Array_Length > 1) ?
new Uint16Array(this.__blender_file__.AB, this.__data_address__ + offset, length) :
this.__blender_file__.dv.getUint16(this.__data_address__ + offset, this.__blender_file__.template.endianess);
},
set: function(float) {
if (Blender_Array_Length > 1) {} else {
this.__blender_file__.dv.setUint16(this.__data_address__ + offset, float, this.__blender_file__.template.endianess);
}
},
};
};
function charProp(offset, Blender_Array_Length, length) {
return {
get: function() {
if (Blender_Array_Length > 1) {
let start = this.__data_address__ + offset;
let end = start;
let buffer_guard = 0;
while (this.__blender_file__.byte[end] != 0 && buffer_guard++ < length) end++;
return toString(this.__blender_file__.AB, start, end);
}
return this.__blender_file__.byte[(this.__data_address__ + offset)];
},
set: function(byte) {
if (Blender_Array_Length > 1) {
var string = byte + "",
i = 0,
l = string.length;
while (i < length) {
if (i < l) {
this.__blender_file__.byte[(this.__data_address__ + offset + i)] = string.charCodeAt(i) | 0;
} else {
this.__blender_file__.byte[(this.__data_address__ + offset + i)] = 0;
}
i++;
}
} else {
this.__blender_file__.byte[(this.__data_address__ + offset)] = byte | 0;
}
}
};
}
function pointerProp2(offset) {
return {
get: function() {
let pointer = this.__blender_file__.getPointer(this.__data_address__ + offset, this.__blender_file__);
var link = this.__blender_file__.memory_lookup[pointer];
var results = [];
if (link) {
var address = link.__data_address__;
let j = 0;
while (true) {
pointer = this.__blender_file__.getPointer(address + j * 8, this.__blender_file__);
let obj = this.__blender_file__.memory_lookup[pointer];
if (!obj) break;
results.push(obj);
j++;
}
};
return results;
},
set: function() {}
};
}
function pointerProp(offset, Blender_Array_Length, length) {
return {
get: function() {
if (Blender_Array_Length > 1) {
let array = [];
let j = 0;
let off = offset;
while (j < Blender_Array_Length) {
let pointer = this.__blender_file__.getPointer(this.__data_address__ + off, this.__blender_file__);
array.push(this.__blender_file__.memory_lookup[pointer]);
off += length;
j++;
}
return array;
} else {
let pointer = this.__blender_file__.getPointer(this.__data_address__ + offset, this.__blender_file__);
return this.__blender_file__.memory_lookup[pointer];
}
},
set: function() {}
};
}
function compileProp(obj, name, type, offset, array_size, IS_POINTER, pointer_size, length) {
if (!IS_POINTER) {
switch (type) {
case "double":
Object.defineProperty(obj, name, float64Prop(offset, array_size, length >> 3));
break;
case "float":
Object.defineProperty(obj, name, floatProp(offset, array_size, length >> 2));
break;
case "int":
Object.defineProperty(obj, name, intProp(offset, array_size, length >> 2));
break;
case "short":
case "ushort":
Object.defineProperty(obj, name, shortProp(offset, array_size, length >> 1));
break;
case "char":
case "uchar":
Object.defineProperty(obj, name, charProp(offset, array_size, length));
break;
default:
//compile list to
obj[name] = {};
obj.__list__.push(name, type, length, offset, array_size, IS_POINTER);
}
obj._length += length;
offset += length;
} else {
Object.defineProperty(obj, name, pointerProp(offset, array_size, pointer_size));
offset += pointer_size * array_size;
}
return offset;
}
//Store final DNA structs
var MASTER_SDNA_SCHEMA = function(version) {
this.version = version;
this.SDNA_SET = false;
this.byte_size = 0;
this.struct_index = 0;
this.structs = {};
this.SDNA = {};
this.endianess = false;
};
MASTER_SDNA_SCHEMA.prototype = {
getSDNAStructureConstructor: function(name, struct) {
if (struct) {
var blen_struct = Function("function " + name + "(){}; return " + name)();
blen_struct.prototype = new BLENDER_STRUCTURE();
blen_struct.prototype.blender_name = name;
blen_struct.prototype.__pointers = [];
blen_struct.prototype.__list__ = [];
var offset = 0;
//Create properties of struct
for (var i = 0; i < struct.length; i += 3) {
var _name = struct[i],
n = _name,
type = struct[i + 1],
length = struct[i + 2],
array_length = 0,
match = null,
Blender_Array_Length = 1,
Suparray_match = 1,
PointerToArray = false,
Pointer_Match = 0;
var DNA = this.SDNA[name] = {
constructor: blen_struct
};
let original_name = _name;
//mini type parser
if ((match = _name.match(/(\*?)(\*?)(\w+)(\[(\w*)\])?(\[(\w*)\])?/))) {
//base name
_name = match[3];
//pointer type
if (match[1]) {
Pointer_Match = 10;
blen_struct.prototype.__pointers.push(_name);
}
if (match[2]) {
PointerToArray = true;
}
//arrays
if (match[4]) {
if (match[6]) {
Suparray_match = parseInt(match[5]);
Blender_Array_Length = parseInt(match[7]);
} else {
Blender_Array_Length = parseInt(match[5]);
}
}
array_length = Blender_Array_Length * length;
length = array_length * Suparray_match;
}
DNA[n] = {
type: type,
length: length,
isArray: (Blender_Array_Length > 0),
};
if (PointerToArray) {
Object.defineProperty(blen_struct.prototype, _name, pointerProp2(offset));
offset += pointer_size;
} else if (Suparray_match > 1) {
var array_names = new Array(Suparray_match);
//construct sub_array object that will return the correct structs
for (var j = 0; j < Suparray_match; j++) {
let array_name_ = `__${_name}[${j}]__`;
array_names[j] = array_name_;
offset = compileProp(blen_struct.prototype, array_name_, type, offset, Blender_Array_Length, Pointer_Match, pointer_size, array_length);
}
Object.defineProperty(blen_struct.prototype, _name, {
get: (function(array_names) {
return function() {
var array = [];
for (var i = 0; i < array_names.length; i++) {
array.push(this[array_names[i]]);
}
return array;
};
})(array_names)
});
} else {
offset = compileProp(blen_struct.prototype, _name, type, offset, Blender_Array_Length, Pointer_Match, pointer_size, length);
}
}
return this.SDNA[name].constructor;
} else {
if (!this.SDNA[name]) {
return null;
}
return this.SDNA[name].constructor;
}
}
};
var BLENDER_STRUCTURE = function() {
this.__blender_file__ = null;
this.__list__ = null;
this.__super_array_list__ = null;
this.blender_name = "";
this.__pointers = null;
this.address = null;
this.length = 0;
this.__data_address__ = 0;
this.blender_name = "";
this._length = 0;
};
/*
Returns a pre-constructed BLENDER_STRUCTURE or creates a new BLENDER_STRUCTURE to match the DNA struct type
*/
var pointer_function = (pointer) => () => {
return FILE.memory_lookup[pointer];
};
function getPointer(offset) {
var pointerLow = data.getUint32(offset, BIG_ENDIAN);
if (pointer_size > 4) {
var pointerHigh = data.getUint32(offset + 4, BIG_ENDIAN);
if (BIG_ENDIAN) {
return (pointerLow) + "" + pointerHigh;
} else {
return (pointerHigh) + "" + pointerLow;
}
} else {
return pointerLow;
}
}
BLENDER_STRUCTURE.prototype = {
setData: function(pointer, _data_offset, data_block_length, BLENDER_FILE) {
if (this.__list__ === null) return this;
BLENDER_FILE.addObject(this);
this.__blender_file__ = BLENDER_FILE;
var struct = this.__list__,
j = 0,
i = 0,
obj, name = "",
type, length, Blender_Array_Length, Pointer_Match, offset, constructor;
this.__data_address__ = _data_offset;
if (struct === null) return this;
for (i = 0; i < struct.length; i += 6) {
obj = null;
name = struct[i];
type = struct[i + 1];
Blender_Array_Length = struct[i + 4];
Pointer_Match = struct[i + 5];
offset = this.__data_address__ + struct[i + 3];
if (Blender_Array_Length > 1) {
this[name] = [];
j = 0;
while (j < Blender_Array_Length) {
if (current_SDNA_template.getSDNAStructureConstructor(type)) {
constructor = current_SDNA_template.getSDNAStructureConstructor(type);
this[name].push((new constructor()).setData(0, offset, offset + length / Blender_Array_Length, BLENDER_FILE));
} else this[name].push(null);
offset += length / Blender_Array_Length;
j++;
}
} else {
if (current_SDNA_template.getSDNAStructureConstructor(type)) {
constructor = current_SDNA_template.getSDNAStructureConstructor(type);
this[name] = (new constructor()).setData(0, offset, length + offset, BLENDER_FILE);
} else this[name] = null;
}
}
//break connection to configuration list
this.__list__ = null;
return this;
},
get aname() {
if (this.id) return this.id.name.slice(2);
else return undefined;
}
};
function toString(buffer, _in, _out) {
return String.fromCharCode.apply(String, new Uint8Array(buffer, _in, _out - _in));
}
//Begin parsing blender __blender_file__
function readFile() {
var count = 0;
var offset2 = 0;
var root = 0;
var i = 0;
var data_offset = 0;
var sdna_index = 0;
var code = "";
var block_length = 0;
var curr_count = 0;
var curr_count2 = 0;
FILE.memory_lookup = {};
struct_names = [];
offset = 0;
// Make sure we have a .blend __blender_file__. All blend files have the first 12bytes
// set with BLENDER-v### in Utf-8
if (toString(_data, offset, 7) !== "BLENDER") return ERROR = "File supplied is not a .blend compatible Blender file.";
// otherwise get templete from save version.
offset += 7;
pointer_size = ((toString(_data, offset++, offset)) == "_") ? 4 : 8;
BIG_ENDIAN = toString(_data, offset++, offset) !== "V";
var version = toString(_data, offset, offset + 3);
//create new master template if none exist for current blender version;
if (!templates[version]) {
templates[version] = new MASTER_SDNA_SCHEMA(version);
}
current_SDNA_template = templates[version];
FILE.template = current_SDNA_template;
offset += 3;
//Set SDNA structs if template hasn't been set.
//Todo: Move the following block into the MASTER_SDNA_SCHEMA object.
//*Like so:*/ current_SDNA_template.set(AB);
if (!current_SDNA_template.SDNA_SET) {
current_SDNA_template.endianess = BIG_ENDIAN;
current_SDNA_template.pointer_size = pointer_size;
//find DNA1 data block
offset2 = offset;
while (true) {
sdna_index = data.getInt32(offset2 + pointer_size + 8, BIG_ENDIAN);
code = toString(_data, offset2, offset2 + 4).replace(/\u0000/g, "");
block_length = data.getInt32(offset2 + 4, true);
offset2 += 16 + (pointer_size);
if (code === "DNA1") {
// DNA found; This is the core of the __blender_file__ and contains all the structure for the various data types used in Blender.
count = 0;
var types = [],
fields = [],
names = [],
lengths = [],
name = "",
curr_name = "";
//skip SDNA and NAME identifiers
offset2 += 8;
//Number of structs.
count = data.getInt32(offset2, true);
offset2 += 4;
curr_count = 0;
//Build up list of names for structs
while (curr_count < count) {
curr_name = "";
while (data.getInt8(offset2) !== 0) {
curr_name += toString(_data, offset2, offset2 + 1);
offset2++;
}
names.push(curr_name);
offset2++;
curr_count++;
}
//Adjust for 4byte alignment
if ((offset2 % 4) > 0) offset2 = (4 - (offset2 % 4)) + offset2;
offset2 += 4;
//Number of struct types
count = data.getInt32(offset2, true);
offset2 += 4;
curr_count = 0;
//Build up list of types
while (curr_count < count) {
curr_name = "";
while (data.getInt8(offset2) !== 0) {
curr_name += toString(_data, offset2, offset2 + 1);
offset2++;
}
types.push(curr_name);
offset2++;
curr_count++;
}
//Adjust for 4byte alignment
if ((offset2 % 4) > 0) offset2 = (4 - (offset2 % 4)) + offset2;
offset2 += 4;
curr_count = 0;
//Build up list of byte lengths for types
while (curr_count < count) {
lengths.push(data.getInt16(offset2, BIG_ENDIAN));
offset2 += 2;
curr_count++;
}
//Adjust for 4byte alignment
if ((offset2 % 4) > 0) offset2 = (4 - (offset2 % 4)) + offset2;
offset2 += 4;
//Number of structures
var structure_count = data.getInt32(offset2, BIG_ENDIAN);
offset2 += 4;
curr_count = 0;
//Create constructor objects from list of SDNA structs
while (curr_count < structure_count) {
var struct_name = types[data.getInt16(offset2, BIG_ENDIAN)];
offset2 += 2;
obj = [];
count = data.getInt16(offset2, BIG_ENDIAN);
offset2 += 2;
curr_count2 = 0;
struct_names.push(struct_name);
//Fill an array with name, type, and length for each SDNA struct property
while (curr_count2 < count) {
obj.push(names[data.getInt16(offset2 + 2, BIG_ENDIAN)], types[data.getInt16(offset2, BIG_ENDIAN)], lengths[data.getInt16(offset2, BIG_ENDIAN)]);
offset2 += 4;
curr_count2++;
}
//Create a SDNA constructor by passing [type,name,lenth] array as second argument
current_SDNA_template.getSDNAStructureConstructor(struct_name, obj);
curr_count++;
}
current_SDNA_template.SDNA_SET = true;
current_SDNA_template.SDNA_NAMES = struct_names;
break;
}
offset2 += block_length;
}
}
//parse the rest of the data, starting back at the top.
//TODO: turn into "on-demand" parsing.
while (true) {
if ((offset % 4) > 0) {
offset = (4 - (offset % 4)) + offset;
}
data_offset = offset;
sdna_index = data.getInt32(offset + pointer_size + 8, BIG_ENDIAN);
let code_uint = data.getUint32(offset, BIG_ENDIAN);
offset2 = offset + 16 + (pointer_size);
offset += data.getInt32(offset + 4, true) + 16 + (pointer_size);
if (code_uint === DNA1); //skip - already processed at this point
else if (code_uint === ENDB) break; //end of __blender_file__ found
else {
//Create a Blender object using a constructor template from current_SDNA_template
var data_start = data_offset + pointer_size + 16;
//Get a SDNA constructor by name;
var constructor = current_SDNA_template.getSDNAStructureConstructor(current_SDNA_template.SDNA_NAMES[sdna_index]);
var size = data.getInt32(data_offset + 4, BIG_ENDIAN);
count = data.getInt32(data_offset + 12 + pointer_size, BIG_ENDIAN);
if (count > 0) {
var obj = new constructor();
var length = constructor.prototype._length;
var address = FILE.getPointer(data_offset + 8);
obj.address = address + "";
obj.setData(address, data_start, data_start + size, FILE);
if (count > 1) {
let array = [];
array.push(obj);
for (var u = 1; u < count; u++) {
obj = new constructor();
obj.setData(address, data_start + length * u, data_start + (length * u) + length, FILE);
array.push(obj);
}
FILE.memory_lookup[address] = array;
} else {
FILE.memory_lookup[address] = obj;
}
}
}
}
}
}
return return_object;
});
},{}],4:[function(require,module,exports){
/*jshint esversion: 6 */
const createMaterial = require("./material.js");
const createTexture = require("./texture.js");
const createMesh = require("./mesh.js");
const createLight = require("./light.js");
const blender_object_types = {
mesh: 1,
lamp: 10
};
function createObject(blender_file, object) {
if (object.data) {
//get the mesh
var buffered_geometry = createMesh(object.data, [0, 0, 0]);
var blend_material = object.data.mat[0];
if (blend_material) {
var material = createMaterial(blend_material);
}
var mesh = new THREE.Mesh(buffered_geometry, material);
mesh.castShadow = true;
mesh.receiveShadow = true;
mesh.rotateZ(object.rot[2]);
mesh.rotateY(object.rot[1]);
mesh.rotateX(object.rot[0]);
mesh.scale.fromArray(object.size, 0);
mesh.position.fromArray([object.loc[0], (object.loc[2]), (-object.loc[1])], 0);
return mesh;
}
return null;
}
function loadObject(object_name, blender_file, cache) {
var objects = blender_file.Object;
materials = [];
for (var i = 0; i < objects.length; i++) {
let object = objects[i];
if (object.aname === object_name) {
switch (object.type) {
case blender_object_types.mesh:
return createObject(object, blender_file);
break;
case blender_object_types.lamp:
return createLight(object, blender_file);
break;
}
}
}
return null;
}
function loadScene(three_scene, blender_file, cache) {
for (let i = 0; i < blender_file.objects.Object.length; i++) {
let object = blender_file.objects.Object[i];
//Load Lights
if (object.type == blender_object_types.lamp) {
let light = createLight(blender_file, object);
three_scene.add(light);
}
//Load Meshes
if (object.type == blender_object_types.mesh) {
let mesh = createObject(blender_file, object);
if(mesh){
three_scene.add(mesh);
}
}
}
}
module.exports = (blender_file) => {
if (!THREE) {
console.warn("No ThreeJS object detected");
return {};
}
var cache = {};
return {
loadScene: (three_scene) => loadScene(three_scene, blender_file, cache),
loadObject: (object_name) => loadObject(object_name, blender_file, cache)
};
};
},{"./light.js":5,"./material.js":6,"./mesh.js":7,"./texture.js":8}],5:[function(require,module,exports){
/*jshint esversion: 6 */
var blender_light_types = {
point: 0,
sun: 1,
spot: 0,
hemi: 0,
area: 0
};
module.exports = function createThreeJSLamp(blend_lamp) {
let ldata = blend_lamp.data;
let pos_array = [blend_lamp.loc[0], blend_lamp.loc[2], -blend_lamp.loc[1]];
let color = ((ldata.r * 255) << 16) | ((ldata.g * 255) << 8) | ((ldata.b * 255) << 0);
let intesity = ldata.energy;
let distance = 0;
var three_light = null;
switch (ldata.type) {
case blender_light_types.point:
var three_light = new THREE.PointLight(color, intesity, distance);
three_light.position.fromArray(pos_array, 0);
three_light.castShadow = true;
break;
case blender_light_types.sun:
var three_light = new THREE.PointLight(color, intesity, distance);
three_light.position.fromArray(pos_array, 0);
three_light.castShadow = true;
three_light.shadow.mapSize.width = 1024;
three_light.shadow.mapSize.height = 1024;
three_light.shadow.camera.near = 0.01;
three_light.shadow.camera.far = 500;
break;
}
return three_light;
}
},{}],6:[function(require,module,exports){
/*jshint esversion: 6 */
module.exports = (() => {
const createTexture = require("./texture.js");
var texture_mappings = {
diff_color: 1,
normal: 2,
mirror: 8,
diff_intensity: 16,
spec_intensity: 32,
emit: 32,
alpha: 128,
spec_hardness: 256,
ray_mirror: 512,
translucency: 1024,
ambient: 2048,
displacement: 4096,
warp: 8192
};
let blender_specular_types = {
cooktorr: 0,
phong: 1,
blinn: 2,
toon: 3,
wardiso: 4
};
function applyColorMapping(blender_texture, three_texture, material) {
if (blender_texture.mapto & texture_mappings.diff_color) {
material.map = three_texture;
}
}
function applySpecMapping(blender_texture, three_texture, material) {
if (blender_texture.mapto & texture_mappings.spec_color && material.type != "MeshStandardMaterial") {
material.specularMap = three_texture;
}
if (blender_texture.mapto & texture_mappings.spec_intensity && material.type != "MeshStandardMaterial") {
material.roughnessMap = three_texture;
}
}
function applyAlphaMapping(blender_texture, three_texture, material) {
if (blender_texture.mapto & texture_mappings.alpha) {
material.alphaMap = three_texture;
}
}
function applyNormalMapping(blender_texture, three_texture, material) {
if (blender_texture.mapto & texture_mappings.normal) {
material.normalMap = three_texture;
material.normalScale = {
x: blender_texture.norfac,
y: blender_texture.norfac
};
}
}
function applyMirrorMapping(blender_texture, three_texture, material) {
if (blender_texture.mapto & texture_mappings.mirror) {
material.envMap = three_texture;
material.envMapIntensity = blender_texture.mirrfac;
}
}
var blender_texture_coordinates = {
GENERATED : 1,
REFLECTION : 2,
NORMAL:4,
GLOBAL : 8,
UV : 16,
OBJECT : 32,
WINDOW: 1024,
TANGENT:4096,
PARTICLE: 8192,
STRESS:16384
}
var blender_texture_mapping = {
FLAT : 0,
CUBE : 1,
TUBE : 2,
SPHERE : 3
}
function applyTexture(blender_texture, material) {
//extract blender_texture data. Use Only if image has been supplied.
if (blender_texture && blender_texture.tex && blender_texture.tex.ima) {
let three_texture = createTexture(blender_texture.tex.ima);
if(blender_texture.texco == blender_texture_coordinates.REFLECTION){
switch(blender_texture.mapping){
case blender_texture_mapping.FLAT:
three_texture.mapping = THREE.EquirectangularReflectionMapping;
break;
case blender_texture_mapping.SPHERE:
three_texture.mapping = THREE.SphericalReflectionMapping;
break;
}
//three_texture.mapping = THREE.EquirectangularRefractionMapping;
}
applyColorMapping(blender_texture, three_texture, material);
applySpecMapping(blender_texture, three_texture, material);
applyAlphaMapping(blender_texture, three_texture, material);
applyNormalMapping(blender_texture, three_texture, material);
applyMirrorMapping(blender_texture, three_texture, material);
}
}
return function createThreeJSMaterial(blend_mat) {
var material = null;
var textures = blend_mat.mtex;
switch (blend_mat.spec_shader) {
case blender_specular_types.lambert:
material = new THREE.MeshLambertMaterial();
material.color.setRGB(blend_mat.r, blend_mat.g, blend_mat.b);
break;
case blender_specular_types.blinn:
case blender_specular_types.phong:
material = new THREE.MeshStandardMaterial();
material.color.setRGB(blend_mat.r, blend_mat.g, blend_mat.b);
//material.specular.setRGB(blend_mat.specr, blend_mat.specg, blend_mat.specb);
material.roughness = (1 - (blend_mat.har / 512));
material.metalness = 1 - blend_mat.ref;
if(blend_mat.alpha < 0.98){
material.transparent = true;
material.opacity = blend_mat.alpha;
console.log(blend_mat, material)
}
break;
case blender_specular_types.wardiso:
case blender_specular_types.cooktorr:
material = new THREE.MeshPhongMaterial();
material.color.setRGB(blend_mat.r, blend_mat.g, blend_mat.b);
material.specular.setRGB(blend_mat.specr, blend_mat.specg, blend_mat.specb);
material.shininess = blend_mat.har / 512;
material.reflectivity = blend_mat.ref * 100;
break;
default:
material = new THREE.MeshLambertMaterial();
material.color.setRGB(blend_mat.r, blend_mat.g, blend_mat.b);
break;
}
var at = (texture) => applyTexture(texture, material);
if (textures && textures.length) textures.map(at);
return material;
};
})();
},{"./texture.js":8}],7:[function(require,module,exports){
/*jshint esversion: 6 */
module.exports = function createThreeJSBufferGeometry(blender_mesh, origin) {
//get materials
let pick_material = 0,
mesh = blender_mesh,
faces = mesh.mpoly,
loops = mesh.mloop,
UV = mesh.mloopuv,
verts = mesh.mvert;
var geometry = new THREE.BufferGeometry();
if (!faces) return geometry;
var index_count = 0;
//precalculate the size of the array needed for faces
var face_indice_count = 0;
var face_indice_counta = 0;
for (var i = 0; i < faces.length; i++) {
var face = faces[i] || faces;
var len = face.totloop;
var indexi = 1;
face_indice_counta += (len * 2 / 3) | 0;
while (indexi < len) {
face_indice_count += 3;
indexi += 2;
}
}
//extract face info and dump into array buffer;
var face_buffer = new Uint32Array(face_indice_count);
var uv_buffer = new Float32Array(face_indice_count * 2);
var normal_buffer = new Float32Array(face_indice_count * 3);
var verts_array_buff = new Float32Array(face_indice_count * 3);
for (var i = 0; i < faces.length; i++) {
var face = faces[i] || faces;
var len = face.totloop;
var start = face.loopstart;
var indexi = 1;
var offset = 0;
while (indexi < len) {
var face_normals = [];
var face_index_array = [];
var face_uvs = [];
let index = 0;
for (var l = 0; l < 3; l++) {
//Per Vertice
if ((indexi - 1) + l < len) {
index = start + (indexi - 1) + l;
} else {
index = start;
}
var v = loops[index].v;
var vert = verts[v];
face_buffer[index_count] = index_count;
//get normals, which are 16byte ints, and norm them back into floats.
verts_array_buff[index_count * 3 + 0] = vert.co[0] + origin[0];
verts_array_buff[index_count * 3 + 1] = vert.co[2] + origin[2];
verts_array_buff[index_count * 3 + 2] = -vert.co[1] + -origin[1];
normal_buffer[index_count * 3 + 0] = vert.no[0];
normal_buffer[index_count * 3 + 1] = vert.no[2];
normal_buffer[index_count * 3 + 2] = (-vert.no[1]);
if (UV) {
var uv = UV[index].uv;
uv_buffer[index_count * 2 + 0] = uv[0];
uv_buffer[index_count * 2 + 1] = uv[1];
}
index_count++;
}
indexi += 2;
}
}
geometry.addAttribute('position', new THREE.BufferAttribute(vert