(function (global, factory) { if (typeof define === "function" && define.amd) { define(["exports", "three", "./lwo/IFFParser.js"], factory); } else if (typeof exports !== "undefined") { factory(exports, require("three"), require("./lwo/IFFParser.js")); } else { var mod = { exports: {} }; factory(mod.exports, global.three, global.IFFParser); global.LWOLoader = mod.exports; } })(typeof globalThis !== "undefined" ? globalThis : typeof self !== "undefined" ? self : this, function (_exports, _three, _IFFParser) { "use strict"; Object.defineProperty(_exports, "__esModule", { value: true }); _exports.LWOLoader = void 0; function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 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 _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); } var _lwoTree; var LWOLoader = /*#__PURE__*/function (_Loader) { _inherits(LWOLoader, _Loader); var _super = _createSuper(LWOLoader); function LWOLoader(manager) { var _this; var parameters = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; _classCallCheck(this, LWOLoader); _this = _super.call(this, manager); _this.resourcePath = parameters.resourcePath !== undefined ? parameters.resourcePath : ''; return _this; } _createClass(LWOLoader, [{ key: "load", value: function load(url, onLoad, onProgress, onError) { var scope = this; var path = scope.path === '' ? extractParentUrl(url, 'Objects') : scope.path; // give the mesh a default name based on the filename var modelName = url.split(path).pop().split('.')[0]; var loader = new _three.FileLoader(this.manager); loader.setPath(scope.path); loader.setResponseType('arraybuffer'); loader.load(url, function (buffer) { // console.time( 'Total parsing: ' ); try { onLoad(scope.parse(buffer, path, modelName)); } catch (e) { if (onError) { onError(e); } else { console.error(e); } scope.manager.itemError(url); } // console.timeEnd( 'Total parsing: ' ); }, onProgress, onError); } }, { key: "parse", value: function parse(iffBuffer, path, modelName) { _lwoTree = new _IFFParser.IFFParser().parse(iffBuffer); // console.log( 'lwoTree', lwoTree ); var textureLoader = new _three.TextureLoader(this.manager).setPath(this.resourcePath || path).setCrossOrigin(this.crossOrigin); return new LWOTreeParser(textureLoader).parse(modelName); } }]); return LWOLoader; }(_three.Loader); // Parse the lwoTree object _exports.LWOLoader = LWOLoader; var LWOTreeParser = /*#__PURE__*/function () { function LWOTreeParser(textureLoader) { _classCallCheck(this, LWOTreeParser); this.textureLoader = textureLoader; } _createClass(LWOTreeParser, [{ key: "parse", value: function parse(modelName) { this.materials = new MaterialParser(this.textureLoader).parse(); this.defaultLayerName = modelName; this.meshes = this.parseLayers(); return { materials: this.materials, meshes: this.meshes }; } }, { key: "parseLayers", value: function parseLayers() { // array of all meshes for building hierarchy var meshes = []; // final array containing meshes with scene graph hierarchy set up var finalMeshes = []; var geometryParser = new GeometryParser(); var scope = this; _lwoTree.layers.forEach(function (layer) { var geometry = geometryParser.parse(layer.geometry, layer); var mesh = scope.parseMesh(geometry, layer); meshes[layer.number] = mesh; if (layer.parent === -1) finalMeshes.push(mesh);else meshes[layer.parent].add(mesh); }); this.applyPivots(finalMeshes); return finalMeshes; } }, { key: "parseMesh", value: function parseMesh(geometry, layer) { var mesh; var materials = this.getMaterials(geometry.userData.matNames, layer.geometry.type); this.duplicateUVs(geometry, materials); if (layer.geometry.type === 'points') mesh = new _three.Points(geometry, materials);else if (layer.geometry.type === 'lines') mesh = new _three.LineSegments(geometry, materials);else mesh = new _three.Mesh(geometry, materials); if (layer.name) mesh.name = layer.name;else mesh.name = this.defaultLayerName + '_layer_' + layer.number; mesh.userData.pivot = layer.pivot; return mesh; } // TODO: may need to be reversed in z to convert LWO to three.js coordinates }, { key: "applyPivots", value: function applyPivots(meshes) { meshes.forEach(function (mesh) { mesh.traverse(function (child) { var pivot = child.userData.pivot; child.position.x += pivot[0]; child.position.y += pivot[1]; child.position.z += pivot[2]; if (child.parent) { var parentPivot = child.parent.userData.pivot; child.position.x -= parentPivot[0]; child.position.y -= parentPivot[1]; child.position.z -= parentPivot[2]; } }); }); } }, { key: "getMaterials", value: function getMaterials(namesArray, type) { var materials = []; var scope = this; namesArray.forEach(function (name, i) { materials[i] = scope.getMaterialByName(name); }); // convert materials to line or point mats if required if (type === 'points' || type === 'lines') { materials.forEach(function (mat, i) { var spec = { color: mat.color }; if (type === 'points') { spec.size = 0.1; spec.map = mat.map; materials[i] = new _three.PointsMaterial(spec); } else if (type === 'lines') { materials[i] = new _three.LineBasicMaterial(spec); } }); } // if there is only one material, return that directly instead of array var filtered = materials.filter(Boolean); if (filtered.length === 1) return filtered[0]; return materials; } }, { key: "getMaterialByName", value: function getMaterialByName(name) { return this.materials.filter(function (m) { return m.name === name; })[0]; } // If the material has an aoMap, duplicate UVs }, { key: "duplicateUVs", value: function duplicateUVs(geometry, materials) { var duplicateUVs = false; if (!Array.isArray(materials)) { if (materials.aoMap) duplicateUVs = true; } else { materials.forEach(function (material) { if (material.aoMap) duplicateUVs = true; }); } if (!duplicateUVs) return; geometry.setAttribute('uv2', new _three.BufferAttribute(geometry.attributes.uv.array, 2)); } }]); return LWOTreeParser; }(); var MaterialParser = /*#__PURE__*/function () { function MaterialParser(textureLoader) { _classCallCheck(this, MaterialParser); this.textureLoader = textureLoader; } _createClass(MaterialParser, [{ key: "parse", value: function parse() { var materials = []; this.textures = {}; for (var name in _lwoTree.materials) { if (_lwoTree.format === 'LWO3') { materials.push(this.parseMaterial(_lwoTree.materials[name], name, _lwoTree.textures)); } else if (_lwoTree.format === 'LWO2') { materials.push(this.parseMaterialLwo2(_lwoTree.materials[name], name, _lwoTree.textures)); } } return materials; } }, { key: "parseMaterial", value: function parseMaterial(materialData, name, textures) { var params = { name: name, side: this.getSide(materialData.attributes), flatShading: this.getSmooth(materialData.attributes) }; var connections = this.parseConnections(materialData.connections, materialData.nodes); var maps = this.parseTextureNodes(connections.maps); this.parseAttributeImageMaps(connections.attributes, textures, maps, materialData.maps); var attributes = this.parseAttributes(connections.attributes, maps); this.parseEnvMap(connections, maps, attributes); params = Object.assign(maps, params); params = Object.assign(params, attributes); var materialType = this.getMaterialType(connections.attributes); return new materialType(params); } }, { key: "parseMaterialLwo2", value: function parseMaterialLwo2(materialData, name /*, textures*/ ) { var params = { name: name, side: this.getSide(materialData.attributes), flatShading: this.getSmooth(materialData.attributes) }; var attributes = this.parseAttributes(materialData.attributes, {}); params = Object.assign(params, attributes); return new _three.MeshPhongMaterial(params); } // Note: converting from left to right handed coords by switching x -> -x in vertices, and // then switching mat FrontSide -> BackSide // NB: this means that FrontSide and BackSide have been switched! }, { key: "getSide", value: function getSide(attributes) { if (!attributes.side) return _three.BackSide; switch (attributes.side) { case 0: case 1: return _three.BackSide; case 2: return _three.FrontSide; case 3: return _three.DoubleSide; } } }, { key: "getSmooth", value: function getSmooth(attributes) { if (!attributes.smooth) return true; return !attributes.smooth; } }, { key: "parseConnections", value: function parseConnections(connections, nodes) { var materialConnections = { maps: {} }; var inputName = connections.inputName; var inputNodeName = connections.inputNodeName; var nodeName = connections.nodeName; var scope = this; inputName.forEach(function (name, index) { if (name === 'Material') { var matNode = scope.getNodeByRefName(inputNodeName[index], nodes); materialConnections.attributes = matNode.attributes; materialConnections.envMap = matNode.fileName; materialConnections.name = inputNodeName[index]; } }); nodeName.forEach(function (name, index) { if (name === materialConnections.name) { materialConnections.maps[inputName[index]] = scope.getNodeByRefName(inputNodeName[index], nodes); } }); return materialConnections; } }, { key: "getNodeByRefName", value: function getNodeByRefName(refName, nodes) { for (var name in nodes) { if (nodes[name].refName === refName) return nodes[name]; } } }, { key: "parseTextureNodes", value: function parseTextureNodes(textureNodes) { var maps = {}; for (var name in textureNodes) { var node = textureNodes[name]; var path = node.fileName; if (!path) return; var texture = this.loadTexture(path); if (node.widthWrappingMode !== undefined) texture.wrapS = this.getWrappingType(node.widthWrappingMode); if (node.heightWrappingMode !== undefined) texture.wrapT = this.getWrappingType(node.heightWrappingMode); switch (name) { case 'Color': maps.map = texture; break; case 'Roughness': maps.roughnessMap = texture; maps.roughness = 1; break; case 'Specular': maps.specularMap = texture; maps.specular = 0xffffff; break; case 'Luminous': maps.emissiveMap = texture; maps.emissive = 0x808080; break; case 'Luminous Color': maps.emissive = 0x808080; break; case 'Metallic': maps.metalnessMap = texture; maps.metalness = 1; break; case 'Transparency': case 'Alpha': maps.alphaMap = texture; maps.transparent = true; break; case 'Normal': maps.normalMap = texture; if (node.amplitude !== undefined) maps.normalScale = new _three.Vector2(node.amplitude, node.amplitude); break; case 'Bump': maps.bumpMap = texture; break; } } // LWO BSDF materials can have both spec and rough, but this is not valid in three if (maps.roughnessMap && maps.specularMap) delete maps.specularMap; return maps; } // maps can also be defined on individual material attributes, parse those here // This occurs on Standard (Phong) surfaces }, { key: "parseAttributeImageMaps", value: function parseAttributeImageMaps(attributes, textures, maps) { for (var name in attributes) { var attribute = attributes[name]; if (attribute.maps) { var mapData = attribute.maps[0]; var path = this.getTexturePathByIndex(mapData.imageIndex, textures); if (!path) return; var texture = this.loadTexture(path); if (mapData.wrap !== undefined) texture.wrapS = this.getWrappingType(mapData.wrap.w); if (mapData.wrap !== undefined) texture.wrapT = this.getWrappingType(mapData.wrap.h); switch (name) { case 'Color': maps.map = texture; break; case 'Diffuse': maps.aoMap = texture; break; case 'Roughness': maps.roughnessMap = texture; maps.roughness = 1; break; case 'Specular': maps.specularMap = texture; maps.specular = 0xffffff; break; case 'Luminosity': maps.emissiveMap = texture; maps.emissive = 0x808080; break; case 'Metallic': maps.metalnessMap = texture; maps.metalness = 1; break; case 'Transparency': case 'Alpha': maps.alphaMap = texture; maps.transparent = true; break; case 'Normal': maps.normalMap = texture; break; case 'Bump': maps.bumpMap = texture; break; } } } } }, { key: "parseAttributes", value: function parseAttributes(attributes, maps) { var params = {}; // don't use color data if color map is present if (attributes.Color && !maps.map) { params.color = new _three.Color().fromArray(attributes.Color.value); } else params.color = new _three.Color(); if (attributes.Transparency && attributes.Transparency.value !== 0) { params.opacity = 1 - attributes.Transparency.value; params.transparent = true; } if (attributes['Bump Height']) params.bumpScale = attributes['Bump Height'].value * 0.1; if (attributes['Refraction Index']) params.refractionRatio = 0.98 / attributes['Refraction Index'].value; this.parsePhysicalAttributes(params, attributes, maps); this.parseStandardAttributes(params, attributes, maps); this.parsePhongAttributes(params, attributes, maps); return params; } }, { key: "parsePhysicalAttributes", value: function parsePhysicalAttributes(params, attributes /*, maps*/ ) { if (attributes.Clearcoat && attributes.Clearcoat.value > 0) { params.clearcoat = attributes.Clearcoat.value; if (attributes['Clearcoat Gloss']) { params.clearcoatRoughness = 0.5 * (1 - attributes['Clearcoat Gloss'].value); } } } }, { key: "parseStandardAttributes", value: function parseStandardAttributes(params, attributes, maps) { if (attributes.Luminous) { params.emissiveIntensity = attributes.Luminous.value; if (attributes['Luminous Color'] && !maps.emissive) { params.emissive = new _three.Color().fromArray(attributes['Luminous Color'].value); } else { params.emissive = new _three.Color(0x808080); } } if (attributes.Roughness && !maps.roughnessMap) params.roughness = attributes.Roughness.value; if (attributes.Metallic && !maps.metalnessMap) params.metalness = attributes.Metallic.value; } }, { key: "parsePhongAttributes", value: function parsePhongAttributes(params, attributes, maps) { if (attributes.Diffuse) params.color.multiplyScalar(attributes.Diffuse.value); if (attributes.Reflection) { params.reflectivity = attributes.Reflection.value; params.combine = _three.AddOperation; } if (attributes.Luminosity) { params.emissiveIntensity = attributes.Luminosity.value; if (!maps.emissiveMap && !maps.map) { params.emissive = params.color; } else { params.emissive = new _three.Color(0x808080); } } // parse specular if there is no roughness - we will interpret the material as 'Phong' in this case if (!attributes.Roughness && attributes.Specular && !maps.specularMap) { if (attributes['Color Highlight']) { params.specular = new _three.Color().setScalar(attributes.Specular.value).lerp(params.color.clone().multiplyScalar(attributes.Specular.value), attributes['Color Highlight'].value); } else { params.specular = new _three.Color().setScalar(attributes.Specular.value); } } if (params.specular && attributes.Glossiness) params.shininess = 7 + Math.pow(2, attributes.Glossiness.value * 12 + 2); } }, { key: "parseEnvMap", value: function parseEnvMap(connections, maps, attributes) { if (connections.envMap) { var envMap = this.loadTexture(connections.envMap); if (attributes.transparent && attributes.opacity < 0.999) { envMap.mapping = _three.EquirectangularRefractionMapping; // Reflectivity and refraction mapping don't work well together in Phong materials if (attributes.reflectivity !== undefined) { delete attributes.reflectivity; delete attributes.combine; } if (attributes.metalness !== undefined) { attributes.metalness = 1; // For most transparent materials metalness should be set to 1 if not otherwise defined. If set to 0 no refraction will be visible } attributes.opacity = 1; // transparency fades out refraction, forcing opacity to 1 ensures a closer visual match to the material in Lightwave. } else envMap.mapping = _three.EquirectangularReflectionMapping; maps.envMap = envMap; } } // get texture defined at top level by its index }, { key: "getTexturePathByIndex", value: function getTexturePathByIndex(index) { var fileName = ''; if (!_lwoTree.textures) return fileName; _lwoTree.textures.forEach(function (texture) { if (texture.index === index) fileName = texture.fileName; }); return fileName; } }, { key: "loadTexture", value: function loadTexture(path) { if (!path) return null; var texture = this.textureLoader.load(path, undefined, undefined, function () { console.warn('LWOLoader: non-standard resource hierarchy. Use \`resourcePath\` parameter to specify root content directory.'); }); return texture; } // 0 = Reset, 1 = Repeat, 2 = Mirror, 3 = Edge }, { key: "getWrappingType", value: function getWrappingType(num) { switch (num) { case 0: console.warn('LWOLoader: "Reset" texture wrapping type is not supported in three.js'); return _three.ClampToEdgeWrapping; case 1: return _three.RepeatWrapping; case 2: return _three.MirroredRepeatWrapping; case 3: return _three.ClampToEdgeWrapping; } } }, { key: "getMaterialType", value: function getMaterialType(nodeData) { if (nodeData.Clearcoat && nodeData.Clearcoat.value > 0) return _three.MeshPhysicalMaterial; if (nodeData.Roughness) return _three.MeshStandardMaterial; return _three.MeshPhongMaterial; } }]); return MaterialParser; }(); var GeometryParser = /*#__PURE__*/function () { function GeometryParser() { _classCallCheck(this, GeometryParser); } _createClass(GeometryParser, [{ key: "parse", value: function parse(geoData, layer) { var geometry = new _three.BufferGeometry(); geometry.setAttribute('position', new _three.Float32BufferAttribute(geoData.points, 3)); var indices = this.splitIndices(geoData.vertexIndices, geoData.polygonDimensions); geometry.setIndex(indices); this.parseGroups(geometry, geoData); geometry.computeVertexNormals(); this.parseUVs(geometry, layer, indices); this.parseMorphTargets(geometry, layer, indices); // TODO: z may need to be reversed to account for coordinate system change geometry.translate(-layer.pivot[0], -layer.pivot[1], -layer.pivot[2]); // let userData = geometry.userData; // geometry = geometry.toNonIndexed() // geometry.userData = userData; return geometry; } // split quads into tris }, { key: "splitIndices", value: function splitIndices(indices, polygonDimensions) { var remappedIndices = []; var i = 0; polygonDimensions.forEach(function (dim) { if (dim < 4) { for (var k = 0; k < dim; k++) { remappedIndices.push(indices[i + k]); } } else if (dim === 4) { remappedIndices.push(indices[i], indices[i + 1], indices[i + 2], indices[i], indices[i + 2], indices[i + 3]); } else if (dim > 4) { for (var _k = 1; _k < dim - 1; _k++) { remappedIndices.push(indices[i], indices[i + _k], indices[i + _k + 1]); } console.warn('LWOLoader: polygons with greater than 4 sides are not supported'); } i += dim; }); return remappedIndices; } // NOTE: currently ignoring poly indices and assuming that they are intelligently ordered }, { key: "parseGroups", value: function parseGroups(geometry, geoData) { var tags = _lwoTree.tags; var matNames = []; var elemSize = 3; if (geoData.type === 'lines') elemSize = 2; if (geoData.type === 'points') elemSize = 1; var remappedIndices = this.splitMaterialIndices(geoData.polygonDimensions, geoData.materialIndices); var indexNum = 0; // create new indices in numerical order var indexPairs = {}; // original indices mapped to numerical indices var prevMaterialIndex; var materialIndex; var prevStart = 0; var currentCount = 0; for (var i = 0; i < remappedIndices.length; i += 2) { materialIndex = remappedIndices[i + 1]; if (i === 0) matNames[indexNum] = tags[materialIndex]; if (prevMaterialIndex === undefined) prevMaterialIndex = materialIndex; if (materialIndex !== prevMaterialIndex) { var currentIndex = void 0; if (indexPairs[tags[prevMaterialIndex]]) { currentIndex = indexPairs[tags[prevMaterialIndex]]; } else { currentIndex = indexNum; indexPairs[tags[prevMaterialIndex]] = indexNum; matNames[indexNum] = tags[prevMaterialIndex]; indexNum++; } geometry.addGroup(prevStart, currentCount, currentIndex); prevStart += currentCount; prevMaterialIndex = materialIndex; currentCount = 0; } currentCount += elemSize; } // the loop above doesn't add the last group, do that here. if (geometry.groups.length > 0) { var _currentIndex; if (indexPairs[tags[materialIndex]]) { _currentIndex = indexPairs[tags[materialIndex]]; } else { _currentIndex = indexNum; indexPairs[tags[materialIndex]] = indexNum; matNames[indexNum] = tags[materialIndex]; } geometry.addGroup(prevStart, currentCount, _currentIndex); } // Mat names from TAGS chunk, used to build up an array of materials for this geometry geometry.userData.matNames = matNames; } }, { key: "splitMaterialIndices", value: function splitMaterialIndices(polygonDimensions, indices) { var remappedIndices = []; polygonDimensions.forEach(function (dim, i) { if (dim <= 3) { remappedIndices.push(indices[i * 2], indices[i * 2 + 1]); } else if (dim === 4) { remappedIndices.push(indices[i * 2], indices[i * 2 + 1], indices[i * 2], indices[i * 2 + 1]); } else { // ignore > 4 for now for (var k = 0; k < dim - 2; k++) { remappedIndices.push(indices[i * 2], indices[i * 2 + 1]); } } }); return remappedIndices; } // UV maps: // 1: are defined via index into an array of points, not into a geometry // - the geometry is also defined by an index into this array, but the indexes may not match // 2: there can be any number of UV maps for a single geometry. Here these are combined, // with preference given to the first map encountered // 3: UV maps can be partial - that is, defined for only a part of the geometry // 4: UV maps can be VMAP or VMAD (discontinuous, to allow for seams). In practice, most // UV maps are defined as partially VMAP and partially VMAD // VMADs are currently not supported }, { key: "parseUVs", value: function parseUVs(geometry, layer) { // start by creating a UV map set to zero for the whole geometry var remappedUVs = Array.from(Array(geometry.attributes.position.count * 2), function () { return 0; }); var _loop = function _loop(name) { var uvs = layer.uvs[name].uvs; var uvIndices = layer.uvs[name].uvIndices; uvIndices.forEach(function (i, j) { remappedUVs[i * 2] = uvs[j * 2]; remappedUVs[i * 2 + 1] = uvs[j * 2 + 1]; }); }; for (var name in layer.uvs) { _loop(name); } geometry.setAttribute('uv', new _three.Float32BufferAttribute(remappedUVs, 2)); } }, { key: "parseMorphTargets", value: function parseMorphTargets(geometry, layer) { var num = 0; var _loop2 = function _loop2(name) { var remappedPoints = geometry.attributes.position.array.slice(); if (!geometry.morphAttributes.position) geometry.morphAttributes.position = []; var morphPoints = layer.morphTargets[name].points; var morphIndices = layer.morphTargets[name].indices; var type = layer.morphTargets[name].type; morphIndices.forEach(function (i, j) { if (type === 'relative') { remappedPoints[i * 3] += morphPoints[j * 3]; remappedPoints[i * 3 + 1] += morphPoints[j * 3 + 1]; remappedPoints[i * 3 + 2] += morphPoints[j * 3 + 2]; } else { remappedPoints[i * 3] = morphPoints[j * 3]; remappedPoints[i * 3 + 1] = morphPoints[j * 3 + 1]; remappedPoints[i * 3 + 2] = morphPoints[j * 3 + 2]; } }); geometry.morphAttributes.position[num] = new _three.Float32BufferAttribute(remappedPoints, 3); geometry.morphAttributes.position[num].name = name; num++; }; for (var name in layer.morphTargets) { _loop2(name); } geometry.morphTargetsRelative = false; } }]); return GeometryParser; }(); // ************** UTILITY FUNCTIONS ************** function extractParentUrl(url, dir) { var index = url.indexOf(dir); if (index === -1) return './'; return url.substr(0, index); } });