(function (global, factory) { if (typeof define === "function" && define.amd) { define(["exports", "three"], factory); } else if (typeof exports !== "undefined") { factory(exports, require("three")); } else { var mod = { exports: {} }; factory(mod.exports, global.three); global.LDrawLoader = mod.exports; } })(typeof globalThis !== "undefined" ? globalThis : typeof self !== "undefined" ? self : this, function (_exports, _three) { "use strict"; Object.defineProperty(_exports, "__esModule", { value: true }); _exports.LDrawLoader = void 0; function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } } function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; } function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } Object.defineProperty(subClass, "prototype", { value: Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }), writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); } function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } function _possibleConstructorReturn(self, call) { if (call && (typeof call === "object" || typeof call === "function")) { return call; } else if (call !== void 0) { throw new TypeError("Derived constructors may only return object or undefined"); } return _assertThisInitialized(self); } function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } // Special surface finish tag types. // Note: "MATERIAL" tag (e.g. GLITTER, SPECKLE) is not implemented var FINISH_TYPE_DEFAULT = 0; var FINISH_TYPE_CHROME = 1; var FINISH_TYPE_PEARLESCENT = 2; var FINISH_TYPE_RUBBER = 3; var FINISH_TYPE_MATTE_METALLIC = 4; var FINISH_TYPE_METAL = 5; // State machine to search a subobject path. // The LDraw standard establishes these various possible subfolders. var FILE_LOCATION_AS_IS = 0; var FILE_LOCATION_TRY_PARTS = 1; var FILE_LOCATION_TRY_P = 2; var FILE_LOCATION_TRY_MODELS = 3; var FILE_LOCATION_TRY_RELATIVE = 4; var FILE_LOCATION_TRY_ABSOLUTE = 5; var FILE_LOCATION_NOT_FOUND = 6; var _tempVec0 = new _three.Vector3(); var _tempVec1 = new _three.Vector3(); var LDrawConditionalLineMaterial = /*#__PURE__*/function (_ShaderMaterial) { _inherits(LDrawConditionalLineMaterial, _ShaderMaterial); var _super = _createSuper(LDrawConditionalLineMaterial); function LDrawConditionalLineMaterial(parameters) { var _this; _classCallCheck(this, LDrawConditionalLineMaterial); _this = _super.call(this, { uniforms: _three.UniformsUtils.merge([_three.UniformsLib.fog, { diffuse: { value: new _three.Color() }, opacity: { value: 1.0 } }]), vertexShader: /* glsl */ "\n\t\t\t\tattribute vec3 control0;\n\t\t\t\tattribute vec3 control1;\n\t\t\t\tattribute vec3 direction;\n\t\t\t\tvarying float discardFlag;\n\n\t\t\t\t#include \n\t\t\t\t#include \n\t\t\t\t#include \n\t\t\t\t#include \n\t\t\t\t#include \n\t\t\t\tvoid main() {\n\t\t\t\t\t#include \n\n\t\t\t\t\tvec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );\n\t\t\t\t\tgl_Position = projectionMatrix * mvPosition;\n\n\t\t\t\t\t// Transform the line segment ends and control points into camera clip space\n\t\t\t\t\tvec4 c0 = projectionMatrix * modelViewMatrix * vec4( control0, 1.0 );\n\t\t\t\t\tvec4 c1 = projectionMatrix * modelViewMatrix * vec4( control1, 1.0 );\n\t\t\t\t\tvec4 p0 = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n\t\t\t\t\tvec4 p1 = projectionMatrix * modelViewMatrix * vec4( position + direction, 1.0 );\n\n\t\t\t\t\tc0.xy /= c0.w;\n\t\t\t\t\tc1.xy /= c1.w;\n\t\t\t\t\tp0.xy /= p0.w;\n\t\t\t\t\tp1.xy /= p1.w;\n\n\t\t\t\t\t// Get the direction of the segment and an orthogonal vector\n\t\t\t\t\tvec2 dir = p1.xy - p0.xy;\n\t\t\t\t\tvec2 norm = vec2( -dir.y, dir.x );\n\n\t\t\t\t\t// Get control point directions from the line\n\t\t\t\t\tvec2 c0dir = c0.xy - p1.xy;\n\t\t\t\t\tvec2 c1dir = c1.xy - p1.xy;\n\n\t\t\t\t\t// If the vectors to the controls points are pointed in different directions away\n\t\t\t\t\t// from the line segment then the line should not be drawn.\n\t\t\t\t\tfloat d0 = dot( normalize( norm ), normalize( c0dir ) );\n\t\t\t\t\tfloat d1 = dot( normalize( norm ), normalize( c1dir ) );\n\t\t\t\t\tdiscardFlag = float( sign( d0 ) != sign( d1 ) );\n\n\t\t\t\t\t#include \n\t\t\t\t\t#include \n\t\t\t\t\t#include \n\t\t\t\t}\n\t\t\t", fragmentShader: /* glsl */ "\n\t\t\tuniform vec3 diffuse;\n\t\t\tuniform float opacity;\n\t\t\tvarying float discardFlag;\n\n\t\t\t#include \n\t\t\t#include \n\t\t\t#include \n\t\t\t#include \n\t\t\t#include \n\t\t\tvoid main() {\n\n\t\t\t\tif ( discardFlag > 0.5 ) discard;\n\n\t\t\t\t#include \n\t\t\t\tvec3 outgoingLight = vec3( 0.0 );\n\t\t\t\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t\t\t\t#include \n\t\t\t\t#include \n\t\t\t\toutgoingLight = diffuseColor.rgb; // simple shader\n\t\t\t\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t\t\t\t#include \n\t\t\t\t#include \n\t\t\t\t#include \n\t\t\t\t#include \n\t\t\t}\n\t\t\t" }); Object.defineProperties(_assertThisInitialized(_this), { opacity: { get: function get() { return this.uniforms.opacity.value; }, set: function set(value) { this.uniforms.opacity.value = value; } }, color: { get: function get() { return this.uniforms.diffuse.value; } } }); _this.setValues(parameters); _this.isLDrawConditionalLineMaterial = true; return _this; } return _createClass(LDrawConditionalLineMaterial); }(_three.ShaderMaterial); function smoothNormals(faces, lineSegments) { function hashVertex(v) { // NOTE: 1e2 is pretty coarse but was chosen because it allows edges // to be smoothed as expected (see minifig arms). The errors between edges // could be due to matrix multiplication. var x = ~~(v.x * 1e2); var y = ~~(v.y * 1e2); var z = ~~(v.z * 1e2); return "".concat(x, ",").concat(y, ",").concat(z); } function hashEdge(v0, v1) { return "".concat(hashVertex(v0), "_").concat(hashVertex(v1)); } var hardEdges = new Set(); var halfEdgeList = {}; var normals = []; // Save the list of hard edges by hash for (var i = 0, l = lineSegments.length; i < l; i++) { var ls = lineSegments[i]; var vertices = ls.vertices; var v0 = vertices[0]; var v1 = vertices[1]; hardEdges.add(hashEdge(v0, v1)); hardEdges.add(hashEdge(v1, v0)); } // track the half edges associated with each triangle for (var _i = 0, _l = faces.length; _i < _l; _i++) { var tri = faces[_i]; var _vertices = tri.vertices; var vertCount = _vertices.length; for (var i2 = 0; i2 < vertCount; i2++) { var index = i2; var next = (i2 + 1) % vertCount; var _v = _vertices[index]; var _v2 = _vertices[next]; var hash = hashEdge(_v, _v2); // don't add the triangle if the edge is supposed to be hard if (hardEdges.has(hash)) continue; var info = { index: index, tri: tri }; halfEdgeList[hash] = info; } } // Iterate until we've tried to connect all faces to share normals while (true) { // Stop if there are no more faces left var halfEdge = null; for (var key in halfEdgeList) { halfEdge = halfEdgeList[key]; break; } if (halfEdge === null) { break; } // Exhaustively find all connected faces var queue = [halfEdge]; while (queue.length > 0) { // initialize all vertex normals in this triangle var _tri = queue.pop().tri; var _vertices2 = _tri.vertices; var vertNormals = _tri.normals; var faceNormal = _tri.faceNormal; // Check if any edge is connected to another triangle edge var _vertCount = _vertices2.length; for (var _i2 = 0; _i2 < _vertCount; _i2++) { var _index = _i2; var _next = (_i2 + 1) % _vertCount; var _v3 = _vertices2[_index]; var _v4 = _vertices2[_next]; // delete this triangle from the list so it won't be found again var _hash = hashEdge(_v3, _v4); delete halfEdgeList[_hash]; var reverseHash = hashEdge(_v4, _v3); var otherInfo = halfEdgeList[reverseHash]; if (otherInfo) { var otherTri = otherInfo.tri; var otherIndex = otherInfo.index; var otherNormals = otherTri.normals; var otherVertCount = otherNormals.length; var otherFaceNormal = otherTri.faceNormal; // NOTE: If the angle between faces is > 67.5 degrees then assume it's // hard edge. There are some cases where the line segments do not line up exactly // with or span multiple triangle edges (see Lunar Vehicle wheels). if (Math.abs(otherTri.faceNormal.dot(_tri.faceNormal)) < 0.25) { continue; } // if this triangle has already been traversed then it won't be in // the halfEdgeList. If it has not then add it to the queue and delete // it so it won't be found again. if (reverseHash in halfEdgeList) { queue.push(otherInfo); delete halfEdgeList[reverseHash]; } // share the first normal var otherNext = (otherIndex + 1) % otherVertCount; if (vertNormals[_index] && otherNormals[otherNext] && vertNormals[_index] !== otherNormals[otherNext]) { otherNormals[otherNext].norm.add(vertNormals[_index].norm); vertNormals[_index].norm = otherNormals[otherNext].norm; } var sharedNormal1 = vertNormals[_index] || otherNormals[otherNext]; if (sharedNormal1 === null) { // it's possible to encounter an edge of a triangle that has already been traversed meaning // both edges already have different normals defined and shared. To work around this we create // a wrapper object so when those edges are merged the normals can be updated everywhere. sharedNormal1 = { norm: new _three.Vector3() }; normals.push(sharedNormal1.norm); } if (vertNormals[_index] === null) { vertNormals[_index] = sharedNormal1; sharedNormal1.norm.add(faceNormal); } if (otherNormals[otherNext] === null) { otherNormals[otherNext] = sharedNormal1; sharedNormal1.norm.add(otherFaceNormal); } // share the second normal if (vertNormals[_next] && otherNormals[otherIndex] && vertNormals[_next] !== otherNormals[otherIndex]) { otherNormals[otherIndex].norm.add(vertNormals[_next].norm); vertNormals[_next].norm = otherNormals[otherIndex].norm; } var sharedNormal2 = vertNormals[_next] || otherNormals[otherIndex]; if (sharedNormal2 === null) { sharedNormal2 = { norm: new _three.Vector3() }; normals.push(sharedNormal2.norm); } if (vertNormals[_next] === null) { vertNormals[_next] = sharedNormal2; sharedNormal2.norm.add(faceNormal); } if (otherNormals[otherIndex] === null) { otherNormals[otherIndex] = sharedNormal2; sharedNormal2.norm.add(otherFaceNormal); } } } } } // The normals of each face have been added up so now we average them by normalizing the vector. for (var _i3 = 0, _l2 = normals.length; _i3 < _l2; _i3++) { normals[_i3].normalize(); } } function isPartType(type) { return type === 'Part'; } function isModelType(type) { return type === 'Model' || type === 'Unofficial_Model'; } function isPrimitiveType(type) { return /primitive/i.test(type) || type === 'Subpart'; } var LineParser = /*#__PURE__*/function () { function LineParser(line, lineNumber) { _classCallCheck(this, LineParser); this.line = line; this.lineLength = line.length; this.currentCharIndex = 0; this.currentChar = ' '; this.lineNumber = lineNumber; } _createClass(LineParser, [{ key: "seekNonSpace", value: function seekNonSpace() { while (this.currentCharIndex < this.lineLength) { this.currentChar = this.line.charAt(this.currentCharIndex); if (this.currentChar !== ' ' && this.currentChar !== '\t') { return; } this.currentCharIndex++; } } }, { key: "getToken", value: function getToken() { var pos0 = this.currentCharIndex++; // Seek space while (this.currentCharIndex < this.lineLength) { this.currentChar = this.line.charAt(this.currentCharIndex); if (this.currentChar === ' ' || this.currentChar === '\t') { break; } this.currentCharIndex++; } var pos1 = this.currentCharIndex; this.seekNonSpace(); return this.line.substring(pos0, pos1); } }, { key: "getRemainingString", value: function getRemainingString() { return this.line.substring(this.currentCharIndex, this.lineLength); } }, { key: "isAtTheEnd", value: function isAtTheEnd() { return this.currentCharIndex >= this.lineLength; } }, { key: "setToEnd", value: function setToEnd() { this.currentCharIndex = this.lineLength; } }, { key: "getLineNumberString", value: function getLineNumberString() { return this.lineNumber >= 0 ? ' at line ' + this.lineNumber : ''; } }]); return LineParser; }(); var LDrawFileCache = /*#__PURE__*/function () { function LDrawFileCache(loader) { _classCallCheck(this, LDrawFileCache); this.cache = {}; this.loader = loader; } _createClass(LDrawFileCache, [{ key: "setData", value: function setData(key, contents) { this.cache[key.toLowerCase()] = contents; } }, { key: "loadData", value: function () { var _loadData = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee2(fileName) { var _this2 = this; var key; return regeneratorRuntime.wrap(function _callee2$(_context2) { while (1) { switch (_context2.prev = _context2.next) { case 0: key = fileName.toLowerCase(); if (!(key in this.cache)) { _context2.next = 3; break; } return _context2.abrupt("return", this.cache[key]); case 3: this.cache[fileName] = new Promise( /*#__PURE__*/function () { var _ref = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee(resolve, reject) { var triedLowerCase, locationState, subobjectURL, loader, fileLoader, text; return regeneratorRuntime.wrap(function _callee$(_context) { while (1) { switch (_context.prev = _context.next) { case 0: triedLowerCase = false; locationState = FILE_LOCATION_AS_IS; case 2: if (!(locationState !== FILE_LOCATION_NOT_FOUND)) { _context.next = 42; break; } subobjectURL = fileName; _context.t0 = locationState; _context.next = _context.t0 === FILE_LOCATION_AS_IS ? 7 : _context.t0 === FILE_LOCATION_TRY_PARTS ? 9 : _context.t0 === FILE_LOCATION_TRY_P ? 12 : _context.t0 === FILE_LOCATION_TRY_MODELS ? 15 : _context.t0 === FILE_LOCATION_TRY_RELATIVE ? 18 : _context.t0 === FILE_LOCATION_TRY_ABSOLUTE ? 21 : 23; break; case 7: locationState = locationState + 1; return _context.abrupt("break", 23); case 9: subobjectURL = 'parts/' + subobjectURL; locationState = locationState + 1; return _context.abrupt("break", 23); case 12: subobjectURL = 'p/' + subobjectURL; locationState = locationState + 1; return _context.abrupt("break", 23); case 15: subobjectURL = 'models/' + subobjectURL; locationState = locationState + 1; return _context.abrupt("break", 23); case 18: subobjectURL = fileName.substring(0, fileName.lastIndexOf('/') + 1) + subobjectURL; locationState = locationState + 1; return _context.abrupt("break", 23); case 21: if (triedLowerCase) { // Try absolute path locationState = FILE_LOCATION_NOT_FOUND; } else { // Next attempt is lower case fileName = fileName.toLowerCase(); subobjectURL = fileName; triedLowerCase = true; locationState = FILE_LOCATION_AS_IS; } return _context.abrupt("break", 23); case 23: loader = _this2.loader; fileLoader = new _three.FileLoader(loader.manager); fileLoader.setPath(loader.partsLibraryPath); fileLoader.setRequestHeader(loader.requestHeader); fileLoader.setWithCredentials(loader.withCredentials); _context.prev = 28; _context.next = 31; return fileLoader.loadAsync(subobjectURL); case 31: text = _context.sent; _this2.setData(fileName, text); resolve(text); return _context.abrupt("return"); case 37: _context.prev = 37; _context.t1 = _context["catch"](28); return _context.abrupt("continue", 2); case 40: _context.next = 2; break; case 42: reject(); case 43: case "end": return _context.stop(); } } }, _callee, null, [[28, 37]]); })); return function (_x2, _x3) { return _ref.apply(this, arguments); }; }()); return _context2.abrupt("return", this.cache[fileName]); case 5: case "end": return _context2.stop(); } } }, _callee2, this); })); function loadData(_x) { return _loadData.apply(this, arguments); } return loadData; }() }]); return LDrawFileCache; }(); function sortByMaterial(a, b) { if (a.colourCode === b.colourCode) { return 0; } if (a.colourCode < b.colourCode) { return -1; } return 1; } function createObject(elements, elementSize) { var isConditionalSegments = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; var totalElements = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null; // Creates a LineSegments (elementSize = 2) or a Mesh (elementSize = 3 ) // With per face / segment material, implemented with mesh groups and materials array // Sort the faces or line segments by colour code to make later the mesh groups elements.sort(sortByMaterial); if (totalElements === null) { totalElements = elements.length; } var positions = new Float32Array(elementSize * totalElements * 3); var normals = elementSize === 3 ? new Float32Array(elementSize * totalElements * 3) : null; var materials = []; var quadArray = new Array(6); var bufferGeometry = new _three.BufferGeometry(); var prevMaterial = null; var index0 = 0; var numGroupVerts = 0; var offset = 0; for (var iElem = 0, nElem = elements.length; iElem < nElem; iElem++) { var elem = elements[iElem]; var vertices = elem.vertices; if (vertices.length === 4) { quadArray[0] = vertices[0]; quadArray[1] = vertices[1]; quadArray[2] = vertices[2]; quadArray[3] = vertices[0]; quadArray[4] = vertices[2]; quadArray[5] = vertices[3]; vertices = quadArray; } for (var j = 0, l = vertices.length; j < l; j++) { var v = vertices[j]; var index = offset + j * 3; positions[index + 0] = v.x; positions[index + 1] = v.y; positions[index + 2] = v.z; } if (elementSize === 3) { var elemNormals = elem.normals; if (elemNormals.length === 4) { quadArray[0] = elemNormals[0]; quadArray[1] = elemNormals[1]; quadArray[2] = elemNormals[2]; quadArray[3] = elemNormals[0]; quadArray[4] = elemNormals[2]; quadArray[5] = elemNormals[3]; elemNormals = quadArray; } for (var _j = 0, _l3 = elemNormals.length; _j < _l3; _j++) { var n = elem.faceNormal; if (elemNormals[_j]) { n = elemNormals[_j].norm; } var _index2 = offset + _j * 3; normals[_index2 + 0] = n.x; normals[_index2 + 1] = n.y; normals[_index2 + 2] = n.z; } } if (prevMaterial !== elem.material) { if (prevMaterial !== null) { bufferGeometry.addGroup(index0, numGroupVerts, materials.length - 1); } materials.push(elem.material); prevMaterial = elem.material; index0 = offset / 3; numGroupVerts = vertices.length; } else { numGroupVerts += vertices.length; } offset += 3 * vertices.length; } if (numGroupVerts > 0) { bufferGeometry.addGroup(index0, Infinity, materials.length - 1); } bufferGeometry.setAttribute('position', new _three.BufferAttribute(positions, 3)); if (normals !== null) { bufferGeometry.setAttribute('normal', new _three.BufferAttribute(normals, 3)); } var object3d = null; if (elementSize === 2) { object3d = new _three.LineSegments(bufferGeometry, materials.length === 1 ? materials[0] : materials); } else if (elementSize === 3) { object3d = new _three.Mesh(bufferGeometry, materials.length === 1 ? materials[0] : materials); } if (isConditionalSegments) { object3d.isConditionalLine = true; var controlArray0 = new Float32Array(elements.length * 3 * 2); var controlArray1 = new Float32Array(elements.length * 3 * 2); var directionArray = new Float32Array(elements.length * 3 * 2); for (var i = 0, _l4 = elements.length; i < _l4; i++) { var os = elements[i]; var _vertices3 = os.vertices; var controlPoints = os.controlPoints; var c0 = controlPoints[0]; var c1 = controlPoints[1]; var v0 = _vertices3[0]; var v1 = _vertices3[1]; var _index3 = i * 3 * 2; controlArray0[_index3 + 0] = c0.x; controlArray0[_index3 + 1] = c0.y; controlArray0[_index3 + 2] = c0.z; controlArray0[_index3 + 3] = c0.x; controlArray0[_index3 + 4] = c0.y; controlArray0[_index3 + 5] = c0.z; controlArray1[_index3 + 0] = c1.x; controlArray1[_index3 + 1] = c1.y; controlArray1[_index3 + 2] = c1.z; controlArray1[_index3 + 3] = c1.x; controlArray1[_index3 + 4] = c1.y; controlArray1[_index3 + 5] = c1.z; directionArray[_index3 + 0] = v1.x - v0.x; directionArray[_index3 + 1] = v1.y - v0.y; directionArray[_index3 + 2] = v1.z - v0.z; directionArray[_index3 + 3] = v1.x - v0.x; directionArray[_index3 + 4] = v1.y - v0.y; directionArray[_index3 + 5] = v1.z - v0.z; } bufferGeometry.setAttribute('control0', new _three.BufferAttribute(controlArray0, 3, false)); bufferGeometry.setAttribute('control1', new _three.BufferAttribute(controlArray1, 3, false)); bufferGeometry.setAttribute('direction', new _three.BufferAttribute(directionArray, 3, false)); } return object3d; } // var LDrawLoader = /*#__PURE__*/function (_Loader) { _inherits(LDrawLoader, _Loader); var _super2 = _createSuper(LDrawLoader); function LDrawLoader(manager) { var _this3; _classCallCheck(this, LDrawLoader); _this3 = _super2.call(this, manager); // Array of THREE.Material _this3.materials = []; // Not using THREE.Cache here because it returns the previous HTML error response instead of calling onError() // This also allows to handle the embedded text files ("0 FILE" lines) _this3.cache = new LDrawFileCache(_assertThisInitialized(_this3)); // This object is a map from file names to paths. It agilizes the paths search. If it is not set then files will be searched by trial and error. _this3.fileMap = null; _this3.rootParseScope = _this3.newParseScopeLevel(); // Add default main triangle and line edge materials (used in pieces that can be coloured with a main color) _this3.setMaterials([_this3.parseColourMetaDirective(new LineParser('Main_Colour CODE 16 VALUE #FF8080 EDGE #333333')), _this3.parseColourMetaDirective(new LineParser('Edge_Colour CODE 24 VALUE #A0A0A0 EDGE #333333'))]); // If this flag is set to true, each subobject will be a Object. // If not (the default), only one object which contains all the merged primitives will be created. _this3.separateObjects = false; // If this flag is set to true the vertex normals will be smoothed. _this3.smoothNormals = true; // The path to load parts from the LDraw parts library from. _this3.partsLibraryPath = ''; return _this3; } _createClass(LDrawLoader, [{ key: "setPartsLibraryPath", value: function setPartsLibraryPath(path) { this.partsLibraryPath = path; return this; } }, { key: "preloadMaterials", value: function () { var _preloadMaterials = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee3(url) { var fileLoader, text, colorLineRegex, lines, materials, i, l, line, directive, material; return regeneratorRuntime.wrap(function _callee3$(_context3) { while (1) { switch (_context3.prev = _context3.next) { case 0: fileLoader = new _three.FileLoader(this.manager); fileLoader.setPath(this.path); fileLoader.setRequestHeader(this.requestHeader); fileLoader.setWithCredentials(this.withCredentials); _context3.next = 6; return fileLoader.loadAsync(url); case 6: text = _context3.sent; colorLineRegex = /^0 !COLOUR/; lines = text.split(/[\n\r]/g); materials = []; for (i = 0, l = lines.length; i < l; i++) { line = lines[i]; if (colorLineRegex.test(line)) { directive = line.replace(colorLineRegex, ''); material = this.parseColourMetaDirective(new LineParser(directive)); materials.push(material); } } this.setMaterials(materials); case 12: case "end": return _context3.stop(); } } }, _callee3, this); })); function preloadMaterials(_x4) { return _preloadMaterials.apply(this, arguments); } return preloadMaterials; }() }, { key: "load", value: function load(url, onLoad, onProgress, onError) { var _this4 = this; if (!this.fileMap) { this.fileMap = {}; } var fileLoader = new _three.FileLoader(this.manager); fileLoader.setPath(this.path); fileLoader.setRequestHeader(this.requestHeader); fileLoader.setWithCredentials(this.withCredentials); fileLoader.load(url, function (text) { _this4.processObject(text, null, url, _this4.rootParseScope).then(function (result) { onLoad(result.groupObject); }); }, onProgress, onError); } }, { key: "parse", value: function parse(text, path, onLoad) { // Async parse. This function calls onParse with the parsed THREE.Object3D as parameter this.processObject(text, null, path, this.rootParseScope).then(function (result) { onLoad(result.groupObject); }); } }, { key: "setMaterials", value: function setMaterials(materials) { // Clears parse scopes stack, adds new scope with material library this.rootParseScope = this.newParseScopeLevel(materials); this.rootParseScope.isFromParse = false; this.materials = materials; return this; } }, { key: "setFileMap", value: function setFileMap(fileMap) { this.fileMap = fileMap; return this; } }, { key: "newParseScopeLevel", value: function newParseScopeLevel() { var materials = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null; var parentScope = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; // Adds a new scope level, assign materials to it and returns it var matLib = {}; if (materials) { for (var i = 0, n = materials.length; i < n; i++) { var material = materials[i]; matLib[material.userData.code] = material; } } var newParseScope = { parentScope: parentScope, lib: matLib, url: null, // Subobjects subobjects: null, numSubobjects: 0, subobjectIndex: 0, inverted: false, category: null, keywords: null, // Current subobject currentFileName: null, mainColourCode: parentScope ? parentScope.mainColourCode : '16', mainEdgeColourCode: parentScope ? parentScope.mainEdgeColourCode : '24', currentMatrix: new _three.Matrix4(), matrix: new _three.Matrix4(), // If false, it is a root material scope previous to parse isFromParse: true, faces: null, lineSegments: null, conditionalSegments: null, totalFaces: 0, // If true, this object is the start of a construction step startingConstructionStep: false }; return newParseScope; } }, { key: "addMaterial", value: function addMaterial(material, parseScope) { // Adds a material to the material library which is on top of the parse scopes stack. And also to the materials array var matLib = parseScope.lib; if (!matLib[material.userData.code]) { this.materials.push(material); } matLib[material.userData.code] = material; return this; } }, { key: "getMaterial", value: function getMaterial(colourCode) { var parseScope = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.rootParseScope; // Given a colour code search its material in the parse scopes stack if (colourCode.startsWith('0x2')) { // Special 'direct' material value (RGB colour) var colour = colourCode.substring(3); return this.parseColourMetaDirective(new LineParser('Direct_Color_' + colour + ' CODE -1 VALUE #' + colour + ' EDGE #' + colour + '')); } while (parseScope) { var material = parseScope.lib[colourCode]; if (material) { return material; } else { parseScope = parseScope.parentScope; } } // Material was not found return null; } }, { key: "parseColourMetaDirective", value: function parseColourMetaDirective(lineParser) { // Parses a colour definition and returns a THREE.Material var code = null; // Triangle and line colours var colour = 0xFF00FF; var edgeColour = 0xFF00FF; // Transparency var alpha = 1; var isTransparent = false; // Self-illumination: var luminance = 0; var finishType = FINISH_TYPE_DEFAULT; var edgeMaterial = null; var name = lineParser.getToken(); if (!name) { throw 'LDrawLoader: Material name was expected after "!COLOUR tag' + lineParser.getLineNumberString() + '.'; } // Parse tag tokens and their parameters var token = null; while (true) { token = lineParser.getToken(); if (!token) { break; } switch (token.toUpperCase()) { case 'CODE': code = lineParser.getToken(); break; case 'VALUE': colour = lineParser.getToken(); if (colour.startsWith('0x')) { colour = '#' + colour.substring(2); } else if (!colour.startsWith('#')) { throw 'LDrawLoader: Invalid colour while parsing material' + lineParser.getLineNumberString() + '.'; } break; case 'EDGE': edgeColour = lineParser.getToken(); if (edgeColour.startsWith('0x')) { edgeColour = '#' + edgeColour.substring(2); } else if (!edgeColour.startsWith('#')) { // Try to see if edge colour is a colour code edgeMaterial = this.getMaterial(edgeColour); if (!edgeMaterial) { throw 'LDrawLoader: Invalid edge colour while parsing material' + lineParser.getLineNumberString() + '.'; } // Get the edge material for this triangle material edgeMaterial = edgeMaterial.userData.edgeMaterial; } break; case 'ALPHA': alpha = parseInt(lineParser.getToken()); if (isNaN(alpha)) { throw 'LDrawLoader: Invalid alpha value in material definition' + lineParser.getLineNumberString() + '.'; } alpha = Math.max(0, Math.min(1, alpha / 255)); if (alpha < 1) { isTransparent = true; } break; case 'LUMINANCE': luminance = parseInt(lineParser.getToken()); if (isNaN(luminance)) { throw 'LDrawLoader: Invalid luminance value in material definition' + LineParser.getLineNumberString() + '.'; } luminance = Math.max(0, Math.min(1, luminance / 255)); break; case 'CHROME': finishType = FINISH_TYPE_CHROME; break; case 'PEARLESCENT': finishType = FINISH_TYPE_PEARLESCENT; break; case 'RUBBER': finishType = FINISH_TYPE_RUBBER; break; case 'MATTE_METALLIC': finishType = FINISH_TYPE_MATTE_METALLIC; break; case 'METAL': finishType = FINISH_TYPE_METAL; break; case 'MATERIAL': // Not implemented lineParser.setToEnd(); break; default: throw 'LDrawLoader: Unknown token "' + token + '" while parsing material' + lineParser.getLineNumberString() + '.'; break; } } var material = null; switch (finishType) { case FINISH_TYPE_DEFAULT: material = new _three.MeshStandardMaterial({ color: colour, roughness: 0.3, metalness: 0 }); break; case FINISH_TYPE_PEARLESCENT: // Try to imitate pearlescency by setting the specular to the complementary of the color, and low shininess var specular = new _three.Color(colour); var hsl = specular.getHSL({ h: 0, s: 0, l: 0 }); hsl.h = (hsl.h + 0.5) % 1; hsl.l = Math.min(1, hsl.l + (1 - hsl.l) * 0.7); specular.setHSL(hsl.h, hsl.s, hsl.l); material = new _three.MeshPhongMaterial({ color: colour, specular: specular, shininess: 10, reflectivity: 0.3 }); break; case FINISH_TYPE_CHROME: // Mirror finish surface material = new _three.MeshStandardMaterial({ color: colour, roughness: 0, metalness: 1 }); break; case FINISH_TYPE_RUBBER: // Rubber finish material = new _three.MeshStandardMaterial({ color: colour, roughness: 0.9, metalness: 0 }); break; case FINISH_TYPE_MATTE_METALLIC: // Brushed metal finish material = new _three.MeshStandardMaterial({ color: colour, roughness: 0.8, metalness: 0.4 }); break; case FINISH_TYPE_METAL: // Average metal finish material = new _three.MeshStandardMaterial({ color: colour, roughness: 0.2, metalness: 0.85 }); break; default: // Should not happen break; } material.transparent = isTransparent; material.premultipliedAlpha = true; material.opacity = alpha; material.depthWrite = !isTransparent; material.polygonOffset = true; material.polygonOffsetFactor = 1; if (luminance !== 0) { material.emissive.set(material.color).multiplyScalar(luminance); } if (!edgeMaterial) { // This is the material used for edges edgeMaterial = new _three.LineBasicMaterial({ color: edgeColour, transparent: isTransparent, opacity: alpha, depthWrite: !isTransparent }); edgeMaterial.userData.code = code; edgeMaterial.name = name + ' - Edge'; // This is the material used for conditional edges edgeMaterial.userData.conditionalEdgeMaterial = new LDrawConditionalLineMaterial({ fog: true, transparent: isTransparent, depthWrite: !isTransparent, color: edgeColour, opacity: alpha }); } material.userData.code = code; material.name = name; material.userData.edgeMaterial = edgeMaterial; return material; } // }, { key: "objectParse", value: function objectParse(text, parseScope) { // Retrieve data from the parent parse scope var currentParseScope = parseScope; var parentParseScope = currentParseScope.parentScope; // Main colour codes passed to this subobject (or default codes 16 and 24 if it is the root object) var mainColourCode = currentParseScope.mainColourCode; var mainEdgeColourCode = currentParseScope.mainEdgeColourCode; // Parse result variables var faces; var lineSegments; var conditionalSegments; var subobjects = []; var category = null; var keywords = null; if (text.indexOf('\r\n') !== -1) { // This is faster than String.split with regex that splits on both text = text.replace(/\r\n/g, '\n'); } var lines = text.split('\n'); var numLines = lines.length; var parsingEmbeddedFiles = false; var currentEmbeddedFileName = null; var currentEmbeddedText = null; var bfcCertified = false; var bfcCCW = true; var bfcInverted = false; var bfcCull = true; var type = ''; var startingConstructionStep = false; var scope = this; function parseColourCode(lineParser, forEdge) { // Parses next colour code and returns a THREE.Material var colourCode = lineParser.getToken(); if (!forEdge && colourCode === '16') { colourCode = mainColourCode; } if (forEdge && colourCode === '24') { colourCode = mainEdgeColourCode; } var material = scope.getMaterial(colourCode, currentParseScope); if (!material) { throw 'LDrawLoader: Unknown colour code "' + colourCode + '" is used' + lineParser.getLineNumberString() + ' but it was not defined previously.'; } return material; } function parseVector(lp) { var v = new _three.Vector3(parseFloat(lp.getToken()), parseFloat(lp.getToken()), parseFloat(lp.getToken())); if (!scope.separateObjects) { v.applyMatrix4(currentParseScope.currentMatrix); } return v; } // Parse all line commands for (var lineIndex = 0; lineIndex < numLines; lineIndex++) { var line = lines[lineIndex]; if (line.length === 0) continue; if (parsingEmbeddedFiles) { if (line.startsWith('0 FILE ')) { // Save previous embedded file in the cache this.cache.setData(currentEmbeddedFileName.toLowerCase(), currentEmbeddedText); // New embedded text file currentEmbeddedFileName = line.substring(7); currentEmbeddedText = ''; } else { currentEmbeddedText += line + '\n'; } continue; } var lp = new LineParser(line, lineIndex + 1); lp.seekNonSpace(); if (lp.isAtTheEnd()) { // Empty line continue; } // Parse the line type var lineType = lp.getToken(); var material = void 0; var segment = void 0; var inverted = void 0; var ccw = void 0; var doubleSided = void 0; var v0 = void 0, v1 = void 0, v2 = void 0, v3 = void 0, c0 = void 0, c1 = void 0, faceNormal = void 0; switch (lineType) { // Line type 0: Comment or META case '0': // Parse meta directive var meta = lp.getToken(); if (meta) { switch (meta) { case '!LDRAW_ORG': type = lp.getToken(); currentParseScope.faces = []; currentParseScope.lineSegments = []; currentParseScope.conditionalSegments = []; currentParseScope.type = type; var isRoot = !parentParseScope.isFromParse; if (isRoot || scope.separateObjects && !isPrimitiveType(type)) { currentParseScope.groupObject = new _three.Group(); currentParseScope.groupObject.userData.startingConstructionStep = currentParseScope.startingConstructionStep; } // If the scale of the object is negated then the triangle winding order // needs to be flipped. if (currentParseScope.matrix.determinant() < 0 && (scope.separateObjects && isPrimitiveType(type) || !scope.separateObjects)) { currentParseScope.inverted = !currentParseScope.inverted; } faces = currentParseScope.faces; lineSegments = currentParseScope.lineSegments; conditionalSegments = currentParseScope.conditionalSegments; break; case '!COLOUR': material = this.parseColourMetaDirective(lp); if (material) { this.addMaterial(material, parseScope); } else { console.warn('LDrawLoader: Error parsing material' + lp.getLineNumberString()); } break; case '!CATEGORY': category = lp.getToken(); break; case '!KEYWORDS': var newKeywords = lp.getRemainingString().split(','); if (newKeywords.length > 0) { if (!keywords) { keywords = []; } newKeywords.forEach(function (keyword) { keywords.push(keyword.trim()); }); } break; case 'FILE': if (lineIndex > 0) { // Start embedded text files parsing parsingEmbeddedFiles = true; currentEmbeddedFileName = lp.getRemainingString(); currentEmbeddedText = ''; bfcCertified = false; bfcCCW = true; } break; case 'BFC': // Changes to the backface culling state while (!lp.isAtTheEnd()) { var token = lp.getToken(); switch (token) { case 'CERTIFY': case 'NOCERTIFY': bfcCertified = token === 'CERTIFY'; bfcCCW = true; break; case 'CW': case 'CCW': bfcCCW = token === 'CCW'; break; case 'INVERTNEXT': bfcInverted = true; break; case 'CLIP': case 'NOCLIP': bfcCull = token === 'CLIP'; break; default: console.warn('THREE.LDrawLoader: BFC directive "' + token + '" is unknown.'); break; } } break; case 'STEP': startingConstructionStep = true; break; default: // Other meta directives are not implemented break; } } break; // Line type 1: Sub-object file case '1': material = parseColourCode(lp); var posX = parseFloat(lp.getToken()); var posY = parseFloat(lp.getToken()); var posZ = parseFloat(lp.getToken()); var m0 = parseFloat(lp.getToken()); var m1 = parseFloat(lp.getToken()); var m2 = parseFloat(lp.getToken()); var m3 = parseFloat(lp.getToken()); var m4 = parseFloat(lp.getToken()); var m5 = parseFloat(lp.getToken()); var m6 = parseFloat(lp.getToken()); var m7 = parseFloat(lp.getToken()); var m8 = parseFloat(lp.getToken()); var matrix = new _three.Matrix4().set(m0, m1, m2, posX, m3, m4, m5, posY, m6, m7, m8, posZ, 0, 0, 0, 1); var fileName = lp.getRemainingString().trim().replace(/\\/g, '/'); if (scope.fileMap[fileName]) { // Found the subobject path in the preloaded file path map fileName = scope.fileMap[fileName]; } else { // Standardized subfolders if (fileName.startsWith('s/')) { fileName = 'parts/' + fileName; } else if (fileName.startsWith('48/')) { fileName = 'p/' + fileName; } } subobjects.push({ material: material, matrix: matrix, fileName: fileName, inverted: bfcInverted !== currentParseScope.inverted, startingConstructionStep: startingConstructionStep }); bfcInverted = false; break; // Line type 2: Line segment case '2': material = parseColourCode(lp, true); v0 = parseVector(lp); v1 = parseVector(lp); segment = { material: material.userData.edgeMaterial, colourCode: material.userData.code, v0: v0, v1: v1, vertices: [v0, v1] }; lineSegments.push(segment); break; // Line type 5: Conditional Line segment case '5': material = parseColourCode(lp, true); v0 = parseVector(lp); v1 = parseVector(lp); c0 = parseVector(lp); c1 = parseVector(lp); segment = { material: material.userData.edgeMaterial.userData.conditionalEdgeMaterial, colourCode: material.userData.code, vertices: [v0, v1], controlPoints: [c0, c1] }; conditionalSegments.push(segment); break; // Line type 3: Triangle case '3': material = parseColourCode(lp); inverted = currentParseScope.inverted; ccw = bfcCCW !== inverted; doubleSided = !bfcCertified || !bfcCull; if (ccw === true) { v0 = parseVector(lp); v1 = parseVector(lp); v2 = parseVector(lp); } else { v2 = parseVector(lp); v1 = parseVector(lp); v0 = parseVector(lp); } _tempVec0.subVectors(v1, v0); _tempVec1.subVectors(v2, v1); faceNormal = new _three.Vector3().crossVectors(_tempVec0, _tempVec1).normalize(); faces.push({ material: material, colourCode: material.userData.code, faceNormal: faceNormal, vertices: [v0, v1, v2], normals: [null, null, null] }); currentParseScope.totalFaces++; if (doubleSided === true) { faces.push({ material: material, colourCode: material.userData.code, faceNormal: faceNormal, vertices: [v2, v1, v0], normals: [null, null, null] }); currentParseScope.totalFaces++; } break; // Line type 4: Quadrilateral case '4': material = parseColourCode(lp); inverted = currentParseScope.inverted; ccw = bfcCCW !== inverted; doubleSided = !bfcCertified || !bfcCull; if (ccw === true) { v0 = parseVector(lp); v1 = parseVector(lp); v2 = parseVector(lp); v3 = parseVector(lp); } else { v3 = parseVector(lp); v2 = parseVector(lp); v1 = parseVector(lp); v0 = parseVector(lp); } _tempVec0.subVectors(v1, v0); _tempVec1.subVectors(v2, v1); faceNormal = new _three.Vector3().crossVectors(_tempVec0, _tempVec1).normalize(); // specifically place the triangle diagonal in the v0 and v1 slots so we can // account for the doubling of vertices later when smoothing normals. faces.push({ material: material, colourCode: material.userData.code, faceNormal: faceNormal, vertices: [v0, v1, v2, v3], normals: [null, null, null, null] }); currentParseScope.totalFaces += 2; if (doubleSided === true) { faces.push({ material: material, colourCode: material.userData.code, faceNormal: faceNormal, vertices: [v3, v2, v1, v0], normals: [null, null, null, null] }); currentParseScope.totalFaces += 2; } break; default: throw 'LDrawLoader: Unknown line type "' + lineType + '"' + lp.getLineNumberString() + '.'; break; } } if (parsingEmbeddedFiles) { this.cache.setData(currentEmbeddedFileName.toLowerCase(), currentEmbeddedText); } currentParseScope.category = category; currentParseScope.keywords = keywords; currentParseScope.subobjects = subobjects; currentParseScope.numSubobjects = subobjects.length; currentParseScope.subobjectIndex = 0; } }, { key: "computeConstructionSteps", value: function computeConstructionSteps(model) { // Sets userdata.constructionStep number in Group objects and userData.numConstructionSteps number in the root Group object. var stepNumber = 0; model.traverse(function (c) { if (c.isGroup) { if (c.userData.startingConstructionStep) { stepNumber++; } c.userData.constructionStep = stepNumber; } }); model.userData.numConstructionSteps = stepNumber + 1; } }, { key: "finalizeObject", value: function finalizeObject(subobjectParseScope) { var parentParseScope = subobjectParseScope.parentScope; // Smooth the normals if this is a part or if this is a case where the subpart // is added directly into the parent model (meaning it will never get smoothed by // being added to a part) var doSmooth = isPartType(subobjectParseScope.type) || !isPartType(subobjectParseScope.type) && !isModelType(subobjectParseScope.type) && isModelType(subobjectParseScope.parentScope.type); if (this.smoothNormals && doSmooth) { smoothNormals(subobjectParseScope.faces, subobjectParseScope.lineSegments); } var isRoot = !parentParseScope.isFromParse; if (this.separateObjects && !isPrimitiveType(subobjectParseScope.type) || isRoot) { var objGroup = subobjectParseScope.groupObject; if (subobjectParseScope.faces.length > 0) { objGroup.add(createObject(subobjectParseScope.faces, 3, false, subobjectParseScope.totalFaces)); } if (subobjectParseScope.lineSegments.length > 0) { objGroup.add(createObject(subobjectParseScope.lineSegments, 2)); } if (subobjectParseScope.conditionalSegments.length > 0) { objGroup.add(createObject(subobjectParseScope.conditionalSegments, 2, true)); } if (parentParseScope.groupObject) { objGroup.name = subobjectParseScope.fileName; objGroup.userData.category = subobjectParseScope.category; objGroup.userData.keywords = subobjectParseScope.keywords; subobjectParseScope.matrix.decompose(objGroup.position, objGroup.quaternion, objGroup.scale); parentParseScope.groupObject.add(objGroup); } } else { var separateObjects = this.separateObjects; var parentLineSegments = parentParseScope.lineSegments; var parentConditionalSegments = parentParseScope.conditionalSegments; var parentFaces = parentParseScope.faces; var lineSegments = subobjectParseScope.lineSegments; var conditionalSegments = subobjectParseScope.conditionalSegments; var faces = subobjectParseScope.faces; for (var i = 0, l = lineSegments.length; i < l; i++) { var ls = lineSegments[i]; if (separateObjects) { var vertices = ls.vertices; vertices[0].applyMatrix4(subobjectParseScope.matrix); vertices[1].applyMatrix4(subobjectParseScope.matrix); } parentLineSegments.push(ls); } for (var _i4 = 0, _l5 = conditionalSegments.length; _i4 < _l5; _i4++) { var os = conditionalSegments[_i4]; if (separateObjects) { var _vertices4 = os.vertices; var controlPoints = os.controlPoints; _vertices4[0].applyMatrix4(subobjectParseScope.matrix); _vertices4[1].applyMatrix4(subobjectParseScope.matrix); controlPoints[0].applyMatrix4(subobjectParseScope.matrix); controlPoints[1].applyMatrix4(subobjectParseScope.matrix); } parentConditionalSegments.push(os); } for (var _i5 = 0, _l6 = faces.length; _i5 < _l6; _i5++) { var tri = faces[_i5]; if (separateObjects) { var _vertices5 = tri.vertices; for (var _i6 = 0, _l7 = _vertices5.length; _i6 < _l7; _i6++) { _vertices5[_i6] = _vertices5[_i6].clone().applyMatrix4(subobjectParseScope.matrix); } _tempVec0.subVectors(_vertices5[1], _vertices5[0]); _tempVec1.subVectors(_vertices5[2], _vertices5[1]); tri.faceNormal.crossVectors(_tempVec0, _tempVec1).normalize(); } parentFaces.push(tri); } parentParseScope.totalFaces += subobjectParseScope.totalFaces; } } }, { key: "processObject", value: function () { var _processObject = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee4(text, subobject, url, parentScope) { var scope, parseScope, parentParseScope, subobjects, promises, i, l, subobjectScopes, _i7, _l8, loadSubobject; return regeneratorRuntime.wrap(function _callee4$(_context4) { while (1) { switch (_context4.prev = _context4.next) { case 0: loadSubobject = function _loadSubobject(subobject) { return scope.cache.loadData(subobject.fileName).then(function (text) { return scope.processObject(text, subobject, url, parseScope); }).catch(function () { console.warn('LDrawLoader: Subobject "' + subobject.fileName + '" could not be found.'); }); }; scope = this; parseScope = this.newParseScopeLevel(null, parentScope); parseScope.url = url; parentParseScope = parseScope.parentScope; // Set current matrix if (subobject) { parseScope.currentMatrix.multiplyMatrices(parentParseScope.currentMatrix, subobject.matrix); parseScope.matrix.copy(subobject.matrix); parseScope.inverted = subobject.inverted; parseScope.startingConstructionStep = subobject.startingConstructionStep; parseScope.mainColourCode = subobject.material.userData.code; parseScope.mainEdgeColourCode = subobject.material.userData.edgeMaterial.userData.code; parseScope.fileName = subobject.fileName; } // Parse the object this.objectParse(text, parseScope); subobjects = parseScope.subobjects; promises = []; for (i = 0, l = subobjects.length; i < l; i++) { promises.push(loadSubobject(parseScope.subobjects[i])); } // Kick off of the downloads in parallel but process all the subobjects // in order so all the assembly instructions are correct _context4.next = 12; return Promise.all(promises); case 12: subobjectScopes = _context4.sent; for (_i7 = 0, _l8 = subobjectScopes.length; _i7 < _l8; _i7++) { this.finalizeObject(subobjectScopes[_i7]); } // If it is root object then finalize this object and compute construction steps if (!parentParseScope.isFromParse) { this.finalizeObject(parseScope); this.computeConstructionSteps(parseScope.groupObject); } return _context4.abrupt("return", parseScope); case 16: case "end": return _context4.stop(); } } }, _callee4, this); })); function processObject(_x5, _x6, _x7, _x8) { return _processObject.apply(this, arguments); } return processObject; }() }]); return LDrawLoader; }(_three.Loader); _exports.LDrawLoader = LDrawLoader; });