(function (global, factory) { if (typeof define === "function" && define.amd) { define(["exports", "three", "../shaders/MMDToonShader.js", "../loaders/TGALoader.js", "../libs/mmdparser.module.js"], factory); } else if (typeof exports !== "undefined") { factory(exports, require("three"), require("../shaders/MMDToonShader.js"), require("../loaders/TGALoader.js"), require("../libs/mmdparser.module.js")); } else { var mod = { exports: {} }; factory(mod.exports, global.three, global.MMDToonShader, global.TGALoader, global.mmdparserModule); global.MMDLoader = mod.exports; } })(typeof globalThis !== "undefined" ? globalThis : typeof self !== "undefined" ? self : this, function (_exports, _three, _MMDToonShader, _TGALoader, _mmdparserModule) { "use strict"; Object.defineProperty(_exports, "__esModule", { value: true }); _exports.MMDLoader = void 0; function _get() { if (typeof Reflect !== "undefined" && Reflect.get) { _get = Reflect.get; } else { _get = function _get(target, property, receiver) { var base = _superPropBase(target, property); if (!base) return; var desc = Object.getOwnPropertyDescriptor(base, property); if (desc.get) { return desc.get.call(arguments.length < 3 ? target : receiver); } return desc.value; }; } return _get.apply(this, arguments); } function _superPropBase(object, property) { while (!Object.prototype.hasOwnProperty.call(object, property)) { object = _getPrototypeOf(object); if (object === null) break; } return object; } 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); } /** * Dependencies * - mmd-parser https://github.com/takahirox/mmd-parser * - TGALoader * - OutlineEffect * * MMDLoader creates Three.js Objects from MMD resources as * PMD, PMX, VMD, and VPD files. * * PMD/PMX is a model data format, VMD is a motion data format * VPD is a posing data format used in MMD(Miku Miku Dance). * * MMD official site * - https://sites.google.com/view/evpvp/ * * PMD, VMD format (in Japanese) * - http://blog.goo.ne.jp/torisu_tetosuki/e/209ad341d3ece2b1b4df24abf619d6e4 * * PMX format * - https://gist.github.com/felixjones/f8a06bd48f9da9a4539f * * TODO * - light motion in vmd support. * - SDEF support. * - uv/material/bone morphing support. * - more precise grant skinning support. * - shadow support. */ /** * @param {THREE.LoadingManager} manager */ var MMDLoader = /*#__PURE__*/function (_Loader) { _inherits(MMDLoader, _Loader); var _super = _createSuper(MMDLoader); function MMDLoader(manager) { var _this; _classCallCheck(this, MMDLoader); _this = _super.call(this, manager); _this.loader = new _three.FileLoader(_this.manager); _this.parser = null; // lazy generation _this.meshBuilder = new MeshBuilder(_this.manager); _this.animationBuilder = new AnimationBuilder(); return _this; } /** * @param {string} animationPath * @return {MMDLoader} */ _createClass(MMDLoader, [{ key: "setAnimationPath", value: function setAnimationPath(animationPath) { this.animationPath = animationPath; return this; } // Load MMD assets as Three.js Object /** * Loads Model file (.pmd or .pmx) as a SkinnedMesh. * * @param {string} url - url to Model(.pmd or .pmx) file * @param {function} onLoad * @param {function} onProgress * @param {function} onError */ }, { key: "load", value: function load(url, onLoad, onProgress, onError) { var builder = this.meshBuilder.setCrossOrigin(this.crossOrigin); // resource path var resourcePath; if (this.resourcePath !== '') { resourcePath = this.resourcePath; } else if (this.path !== '') { resourcePath = this.path; } else { resourcePath = _three.LoaderUtils.extractUrlBase(url); } var modelExtension = this._extractExtension(url).toLowerCase(); // Should I detect by seeing header? if (modelExtension !== 'pmd' && modelExtension !== 'pmx') { if (onError) onError(new Error('THREE.MMDLoader: Unknown model file extension .' + modelExtension + '.')); return; } this[modelExtension === 'pmd' ? 'loadPMD' : 'loadPMX'](url, function (data) { onLoad(builder.build(data, resourcePath, onProgress, onError)); }, onProgress, onError); } /** * Loads Motion file(s) (.vmd) as a AnimationClip. * If two or more files are specified, they'll be merged. * * @param {string|Array<string>} url - url(s) to animation(.vmd) file(s) * @param {SkinnedMesh|THREE.Camera} object - tracks will be fitting to this object * @param {function} onLoad * @param {function} onProgress * @param {function} onError */ }, { key: "loadAnimation", value: function loadAnimation(url, object, onLoad, onProgress, onError) { var builder = this.animationBuilder; this.loadVMD(url, function (vmd) { onLoad(object.isCamera ? builder.buildCameraAnimation(vmd) : builder.build(vmd, object)); }, onProgress, onError); } /** * Loads mode file and motion file(s) as an object containing * a SkinnedMesh and a AnimationClip. * Tracks of AnimationClip are fitting to the model. * * @param {string} modelUrl - url to Model(.pmd or .pmx) file * @param {string|Array{string}} vmdUrl - url(s) to animation(.vmd) file * @param {function} onLoad * @param {function} onProgress * @param {function} onError */ }, { key: "loadWithAnimation", value: function loadWithAnimation(modelUrl, vmdUrl, onLoad, onProgress, onError) { var scope = this; this.load(modelUrl, function (mesh) { scope.loadAnimation(vmdUrl, mesh, function (animation) { onLoad({ mesh: mesh, animation: animation }); }, onProgress, onError); }, onProgress, onError); } // Load MMD assets as Object data parsed by MMDParser /** * Loads .pmd file as an Object. * * @param {string} url - url to .pmd file * @param {function} onLoad * @param {function} onProgress * @param {function} onError */ }, { key: "loadPMD", value: function loadPMD(url, onLoad, onProgress, onError) { var parser = this._getParser(); this.loader.setMimeType(undefined).setPath(this.path).setResponseType('arraybuffer').setRequestHeader(this.requestHeader).setWithCredentials(this.withCredentials).load(url, function (buffer) { onLoad(parser.parsePmd(buffer, true)); }, onProgress, onError); } /** * Loads .pmx file as an Object. * * @param {string} url - url to .pmx file * @param {function} onLoad * @param {function} onProgress * @param {function} onError */ }, { key: "loadPMX", value: function loadPMX(url, onLoad, onProgress, onError) { var parser = this._getParser(); this.loader.setMimeType(undefined).setPath(this.path).setResponseType('arraybuffer').setRequestHeader(this.requestHeader).setWithCredentials(this.withCredentials).load(url, function (buffer) { onLoad(parser.parsePmx(buffer, true)); }, onProgress, onError); } /** * Loads .vmd file as an Object. If two or more files are specified * they'll be merged. * * @param {string|Array<string>} url - url(s) to .vmd file(s) * @param {function} onLoad * @param {function} onProgress * @param {function} onError */ }, { key: "loadVMD", value: function loadVMD(url, onLoad, onProgress, onError) { var urls = Array.isArray(url) ? url : [url]; var vmds = []; var vmdNum = urls.length; var parser = this._getParser(); this.loader.setMimeType(undefined).setPath(this.animationPath).setResponseType('arraybuffer').setRequestHeader(this.requestHeader).setWithCredentials(this.withCredentials); for (var i = 0, il = urls.length; i < il; i++) { this.loader.load(urls[i], function (buffer) { vmds.push(parser.parseVmd(buffer, true)); if (vmds.length === vmdNum) onLoad(parser.mergeVmds(vmds)); }, onProgress, onError); } } /** * Loads .vpd file as an Object. * * @param {string} url - url to .vpd file * @param {boolean} isUnicode * @param {function} onLoad * @param {function} onProgress * @param {function} onError */ }, { key: "loadVPD", value: function loadVPD(url, isUnicode, onLoad, onProgress, onError) { var parser = this._getParser(); this.loader.setMimeType(isUnicode ? undefined : 'text/plain; charset=shift_jis').setPath(this.animationPath).setResponseType('text').setRequestHeader(this.requestHeader).setWithCredentials(this.withCredentials).load(url, function (text) { onLoad(parser.parseVpd(text, true)); }, onProgress, onError); } // private methods }, { key: "_extractExtension", value: function _extractExtension(url) { var index = url.lastIndexOf('.'); return index < 0 ? '' : url.slice(index + 1); } }, { key: "_getParser", value: function _getParser() { if (this.parser === null) { if (typeof _mmdparserModule.MMDParser === 'undefined') { throw new Error('THREE.MMDLoader: Import MMDParser https://github.com/takahirox/mmd-parser'); } this.parser = new _mmdparserModule.MMDParser.Parser(); // eslint-disable-line no-undef } return this.parser; } }]); return MMDLoader; }(_three.Loader); // Utilities /* * base64 encoded defalut toon textures toon00.bmp - toon10.bmp. * We don't need to request external toon image files. * This idea is from http://www20.atpages.jp/katwat/three.js_r58/examples/mytest37/mmd.three.js */ _exports.MMDLoader = MMDLoader; var DEFAULT_TOON_TEXTURES = ['data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAL0lEQVRYR+3QQREAAAzCsOFfNJPBJ1XQS9r2hsUAAQIECBAgQIAAAQIECBAgsBZ4MUx/ofm2I/kAAAAASUVORK5CYII=', 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAN0lEQVRYR+3WQREAMBACsZ5/bWiiMvgEBTt5cW37hjsBBAgQIECAwFwgyfYPCCBAgAABAgTWAh8aBHZBl14e8wAAAABJRU5ErkJggg==', 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAOUlEQVRYR+3WMREAMAwDsYY/yoDI7MLwIiP40+RJklfcCCBAgAABAgTqArfb/QMCCBAgQIAAgbbAB3z/e0F3js2cAAAAAElFTkSuQmCC', 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAN0lEQVRYR+3WQREAMBACsZ5/B5ilMvgEBTt5cW37hjsBBAgQIECAwFwgyfYPCCBAgAABAgTWAh81dWyx0gFwKAAAAABJRU5ErkJggg==', 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAOklEQVRYR+3WoREAMAwDsWb/UQtCy9wxTOQJ/oQ8SXKKGwEECBAgQIBAXeDt7f4BAQQIECBAgEBb4AOz8Hzx7WLY4wAAAABJRU5ErkJggg==', 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABPUlEQVRYR+1XwW7CMAy1+f9fZOMysSEOEweEOPRNdm3HbdOyIhAcklPrOs/PLy9RygBALxzcCDQFmgJNgaZAU6Ap0BR4PwX8gsRMVLssMRH5HcpzJEaWL7EVg9F1IHRlyqQohgVr4FGUlUcMJSjcUlDw0zvjeun70cLWmneoyf7NgBTQSniBTQQSuJAZsOnnaczjIMb5hCiuHKxokCrJfVnrctyZL0PkJAJe1HMil4nxeyi3Ypfn1kX51jpPvo/JeCNC4PhVdHdJw2XjBR8brF8PEIhNVn12AgP7uHsTBguBn53MUZCqv7Lp07Pn5k1Ro+uWmUNn7D+M57rtk7aG0Vo73xyF/fbFf0bPJjDXngnGocDTdFhygZjwUQrMNrDcmZlQT50VJ/g/UwNyHpu778+yW+/ksOz/BFo54P4AsUXMfRq7XWsAAAAASUVORK5CYII=', 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAACMElEQVRYR+2Xv4pTQRTGf2dubhLdICiii2KnYKHVolhauKWPoGAnNr6BD6CvIVaihYuI2i1ia0BY0MZGRHQXjZj/mSPnnskfNWiWZUlzJ5k7M2cm833nO5Mziej2DWWJRUoCpQKlAntSQCqgw39/iUWAGmh37jrRnVsKlgpiqmkoGVABA7E57fvY+pJDdgKqF6HzFCSADkDq+F6AHABtQ+UMVE5D7zXod7fFNhTEckTbj5XQgHzNN+5tQvc5NG7C6BNkp6D3EmpXHDR+dQAjFLchW3VS9rlw3JBh+B7ys5Cf9z0GW1C/7P32AyBAOAz1q4jGliIH3YPuBnSfQX4OGreTIgEYQb/pBDtPnEQ4CivXYPAWBk13oHrB54yA9QuSn2H4AcKRpEILDt0BUzj+RLR1V5EqjD66NPRBVpLcQwjHoHYJOhsQv6U4mnzmrIXJCFr4LDwm/xBUoboG9XX4cc9VKdYoSA2yk5NQLJaKDUjTBoveG3Z2TElTxwjNK4M3LEZgUdDdruvcXzKBpStgp2NPiWi3ks9ZXxIoFVi+AvHLdc9TqtjL3/aYjpPlrzOcEnK62Szhimdd7xX232zFDTgtxezOu3WNMRLjiKgjtOhHVMd1loynVHvOgjuIIJMaELEqhJAV/RCSLbWTcfPFakFgFlALTRRvx+ok6Hlp/Q+v3fmx90bMyUzaEAhmM3KvHlXTL5DxnbGf/1M8RNNACLL5MNtPxP/mypJAqcDSFfgFhpYqWUzhTEAAAAAASUVORK5CYII=', 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAL0lEQVRYR+3QQREAAAzCsOFfNJPBJ1XQS9r2hsUAAQIECBAgQIAAAQIECBAgsBZ4MUx/ofm2I/kAAAAASUVORK5CYII=', 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAL0lEQVRYR+3QQREAAAzCsOFfNJPBJ1XQS9r2hsUAAQIECBAgQIAAAQIECBAgsBZ4MUx/ofm2I/kAAAAASUVORK5CYII=', 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAL0lEQVRYR+3QQREAAAzCsOFfNJPBJ1XQS9r2hsUAAQIECBAgQIAAAQIECBAgsBZ4MUx/ofm2I/kAAAAASUVORK5CYII=', 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAL0lEQVRYR+3QQREAAAzCsOFfNJPBJ1XQS9r2hsUAAQIECBAgQIAAAQIECBAgsBZ4MUx/ofm2I/kAAAAASUVORK5CYII=']; var NON_ALPHA_CHANNEL_FORMATS = [_three.RGB_S3TC_DXT1_Format, _three.RGB_PVRTC_4BPPV1_Format, _three.RGB_PVRTC_2BPPV1_Format, _three.RGB_ETC1_Format, _three.RGB_ETC2_Format]; // Builders. They build Three.js object from Object data parsed by MMDParser. /** * @param {THREE.LoadingManager} manager */ var MeshBuilder = /*#__PURE__*/function () { function MeshBuilder(manager) { _classCallCheck(this, MeshBuilder); this.crossOrigin = 'anonymous'; this.geometryBuilder = new GeometryBuilder(); this.materialBuilder = new MaterialBuilder(manager); } /** * @param {string} crossOrigin * @return {MeshBuilder} */ _createClass(MeshBuilder, [{ key: "setCrossOrigin", value: function setCrossOrigin(crossOrigin) { this.crossOrigin = crossOrigin; return this; } /** * @param {Object} data - parsed PMD/PMX data * @param {string} resourcePath * @param {function} onProgress * @param {function} onError * @return {SkinnedMesh} */ }, { key: "build", value: function build(data, resourcePath, onProgress, onError) { var geometry = this.geometryBuilder.build(data); var material = this.materialBuilder.setCrossOrigin(this.crossOrigin).setResourcePath(resourcePath).build(data, geometry, onProgress, onError); var mesh = new _three.SkinnedMesh(geometry, material); var skeleton = new _three.Skeleton(initBones(mesh)); mesh.bind(skeleton); // console.log( mesh ); // for console debug return mesh; } }]); return MeshBuilder; }(); // TODO: Try to remove this function function initBones(mesh) { var geometry = mesh.geometry; var bones = []; if (geometry && geometry.bones !== undefined) { // first, create array of 'Bone' objects from geometry data for (var i = 0, il = geometry.bones.length; i < il; i++) { var gbone = geometry.bones[i]; // create new 'Bone' object var bone = new _three.Bone(); bones.push(bone); // apply values bone.name = gbone.name; bone.position.fromArray(gbone.pos); bone.quaternion.fromArray(gbone.rotq); if (gbone.scl !== undefined) bone.scale.fromArray(gbone.scl); } // second, create bone hierarchy for (var _i = 0, _il = geometry.bones.length; _i < _il; _i++) { var _gbone = geometry.bones[_i]; if (_gbone.parent !== -1 && _gbone.parent !== null && bones[_gbone.parent] !== undefined) { // subsequent bones in the hierarchy bones[_gbone.parent].add(bones[_i]); } else { // topmost bone, immediate child of the skinned mesh mesh.add(bones[_i]); } } } // now the bones are part of the scene graph and children of the skinned mesh. // let's update the corresponding matrices mesh.updateMatrixWorld(true); return bones; } // var GeometryBuilder = /*#__PURE__*/function () { function GeometryBuilder() { _classCallCheck(this, GeometryBuilder); } _createClass(GeometryBuilder, [{ key: "build", value: /** * @param {Object} data - parsed PMD/PMX data * @return {BufferGeometry} */ function build(data) { // for geometry var positions = []; var uvs = []; var normals = []; var indices = []; var groups = []; var bones = []; var skinIndices = []; var skinWeights = []; var morphTargets = []; var morphPositions = []; var iks = []; var grants = []; var rigidBodies = []; var constraints = []; // for work var offset = 0; var boneTypeTable = {}; // positions, normals, uvs, skinIndices, skinWeights for (var i = 0; i < data.metadata.vertexCount; i++) { var v = data.vertices[i]; for (var j = 0, jl = v.position.length; j < jl; j++) { positions.push(v.position[j]); } for (var _j = 0, _jl = v.normal.length; _j < _jl; _j++) { normals.push(v.normal[_j]); } for (var _j2 = 0, _jl2 = v.uv.length; _j2 < _jl2; _j2++) { uvs.push(v.uv[_j2]); } for (var _j3 = 0; _j3 < 4; _j3++) { skinIndices.push(v.skinIndices.length - 1 >= _j3 ? v.skinIndices[_j3] : 0.0); } for (var _j4 = 0; _j4 < 4; _j4++) { skinWeights.push(v.skinWeights.length - 1 >= _j4 ? v.skinWeights[_j4] : 0.0); } } // indices for (var _i2 = 0; _i2 < data.metadata.faceCount; _i2++) { var face = data.faces[_i2]; for (var _j5 = 0, _jl3 = face.indices.length; _j5 < _jl3; _j5++) { indices.push(face.indices[_j5]); } } // groups for (var _i3 = 0; _i3 < data.metadata.materialCount; _i3++) { var material = data.materials[_i3]; groups.push({ offset: offset * 3, count: material.faceCount * 3 }); offset += material.faceCount; } // bones for (var _i4 = 0; _i4 < data.metadata.rigidBodyCount; _i4++) { var body = data.rigidBodies[_i4]; var value = boneTypeTable[body.boneIndex]; // keeps greater number if already value is set without any special reasons value = value === undefined ? body.type : Math.max(body.type, value); boneTypeTable[body.boneIndex] = value; } for (var _i5 = 0; _i5 < data.metadata.boneCount; _i5++) { var boneData = data.bones[_i5]; var bone = { index: _i5, transformationClass: boneData.transformationClass, parent: boneData.parentIndex, name: boneData.name, pos: boneData.position.slice(0, 3), rotq: [0, 0, 0, 1], scl: [1, 1, 1], rigidBodyType: boneTypeTable[_i5] !== undefined ? boneTypeTable[_i5] : -1 }; if (bone.parent !== -1) { bone.pos[0] -= data.bones[bone.parent].position[0]; bone.pos[1] -= data.bones[bone.parent].position[1]; bone.pos[2] -= data.bones[bone.parent].position[2]; } bones.push(bone); } // iks // TODO: remove duplicated codes between PMD and PMX if (data.metadata.format === 'pmd') { for (var _i6 = 0; _i6 < data.metadata.ikCount; _i6++) { var ik = data.iks[_i6]; var param = { target: ik.target, effector: ik.effector, iteration: ik.iteration, maxAngle: ik.maxAngle * 4, links: [] }; for (var _j6 = 0, _jl4 = ik.links.length; _j6 < _jl4; _j6++) { var link = {}; link.index = ik.links[_j6].index; link.enabled = true; if (data.bones[link.index].name.indexOf('ひざ') >= 0) { link.limitation = new _three.Vector3(1.0, 0.0, 0.0); } param.links.push(link); } iks.push(param); } } else { for (var _i7 = 0; _i7 < data.metadata.boneCount; _i7++) { var _ik = data.bones[_i7].ik; if (_ik === undefined) continue; var _param = { target: _i7, effector: _ik.effector, iteration: _ik.iteration, maxAngle: _ik.maxAngle, links: [] }; for (var _j7 = 0, _jl5 = _ik.links.length; _j7 < _jl5; _j7++) { var _link = {}; _link.index = _ik.links[_j7].index; _link.enabled = true; if (_ik.links[_j7].angleLimitation === 1) { // Revert if rotationMin/Max doesn't work well // link.limitation = new Vector3( 1.0, 0.0, 0.0 ); var rotationMin = _ik.links[_j7].lowerLimitationAngle; var rotationMax = _ik.links[_j7].upperLimitationAngle; // Convert Left to Right coordinate by myself because // MMDParser doesn't convert. It's a MMDParser's bug var tmp1 = -rotationMax[0]; var tmp2 = -rotationMax[1]; rotationMax[0] = -rotationMin[0]; rotationMax[1] = -rotationMin[1]; rotationMin[0] = tmp1; rotationMin[1] = tmp2; _link.rotationMin = new _three.Vector3().fromArray(rotationMin); _link.rotationMax = new _three.Vector3().fromArray(rotationMax); } _param.links.push(_link); } iks.push(_param); // Save the reference even from bone data for efficiently // simulating PMX animation system bones[_i7].ik = _param; } } // grants if (data.metadata.format === 'pmx') { // Sort grant parameters from parents to children because // grant uses parent's transform that parent's grant is already applied // so grant should be applied in order from parents to children var traverse = function traverse(entry) { if (entry.param) { grants.push(entry.param); // Save the reference even from bone data for efficiently // simulating PMX animation system bones[entry.param.index].grant = entry.param; } entry.visited = true; for (var _i9 = 0, il = entry.children.length; _i9 < il; _i9++) { var child = entry.children[_i9]; // Cut off a loop if exists. (Is a grant loop invalid?) if (!child.visited) traverse(child); } }; // bone index -> grant entry map var grantEntryMap = {}; for (var _i8 = 0; _i8 < data.metadata.boneCount; _i8++) { var _boneData = data.bones[_i8]; var grant = _boneData.grant; if (grant === undefined) continue; var _param2 = { index: _i8, parentIndex: grant.parentIndex, ratio: grant.ratio, isLocal: grant.isLocal, affectRotation: grant.affectRotation, affectPosition: grant.affectPosition, transformationClass: _boneData.transformationClass }; grantEntryMap[_i8] = { parent: null, children: [], param: _param2, visited: false }; } var rootEntry = { parent: null, children: [], param: null, visited: false }; // Build a tree representing grant hierarchy for (var boneIndex in grantEntryMap) { var grantEntry = grantEntryMap[boneIndex]; var parentGrantEntry = grantEntryMap[grantEntry.parentIndex] || rootEntry; grantEntry.parent = parentGrantEntry; parentGrantEntry.children.push(grantEntry); } traverse(rootEntry); } // morph function updateAttributes(attribute, morph, ratio) { for (var _i10 = 0; _i10 < morph.elementCount; _i10++) { var element = morph.elements[_i10]; var index = void 0; if (data.metadata.format === 'pmd') { index = data.morphs[0].elements[element.index].index; } else { index = element.index; } attribute.array[index * 3 + 0] += element.position[0] * ratio; attribute.array[index * 3 + 1] += element.position[1] * ratio; attribute.array[index * 3 + 2] += element.position[2] * ratio; } } for (var _i11 = 0; _i11 < data.metadata.morphCount; _i11++) { var morph = data.morphs[_i11]; var params = { name: morph.name }; var attribute = new _three.Float32BufferAttribute(data.metadata.vertexCount * 3, 3); attribute.name = morph.name; for (var _j8 = 0; _j8 < data.metadata.vertexCount * 3; _j8++) { attribute.array[_j8] = positions[_j8]; } if (data.metadata.format === 'pmd') { if (_i11 !== 0) { updateAttributes(attribute, morph, 1.0); } } else { if (morph.type === 0) { // group for (var _j9 = 0; _j9 < morph.elementCount; _j9++) { var morph2 = data.morphs[morph.elements[_j9].index]; var ratio = morph.elements[_j9].ratio; if (morph2.type === 1) { updateAttributes(attribute, morph2, ratio); } else {// TODO: implement } } } else if (morph.type === 1) { // vertex updateAttributes(attribute, morph, 1.0); } else if (morph.type === 2) {// bone // TODO: implement } else if (morph.type === 3) {// uv // TODO: implement } else if (morph.type === 4) {// additional uv1 // TODO: implement } else if (morph.type === 5) {// additional uv2 // TODO: implement } else if (morph.type === 6) {// additional uv3 // TODO: implement } else if (morph.type === 7) {// additional uv4 // TODO: implement } else if (morph.type === 8) {// material // TODO: implement } } morphTargets.push(params); morphPositions.push(attribute); } // rigid bodies from rigidBodies field. for (var _i12 = 0; _i12 < data.metadata.rigidBodyCount; _i12++) { var rigidBody = data.rigidBodies[_i12]; var _params = {}; for (var key in rigidBody) { _params[key] = rigidBody[key]; } /* * RigidBody position parameter in PMX seems global position * while the one in PMD seems offset from corresponding bone. * So unify being offset. */ if (data.metadata.format === 'pmx') { if (_params.boneIndex !== -1) { var _bone = data.bones[_params.boneIndex]; _params.position[0] -= _bone.position[0]; _params.position[1] -= _bone.position[1]; _params.position[2] -= _bone.position[2]; } } rigidBodies.push(_params); } // constraints from constraints field. for (var _i13 = 0; _i13 < data.metadata.constraintCount; _i13++) { var constraint = data.constraints[_i13]; var _params2 = {}; for (var _key in constraint) { _params2[_key] = constraint[_key]; } var bodyA = rigidBodies[_params2.rigidBodyIndex1]; var bodyB = rigidBodies[_params2.rigidBodyIndex2]; // Refer to http://www20.atpages.jp/katwat/wp/?p=4135 if (bodyA.type !== 0 && bodyB.type === 2) { if (bodyA.boneIndex !== -1 && bodyB.boneIndex !== -1 && data.bones[bodyB.boneIndex].parentIndex === bodyA.boneIndex) { bodyB.type = 1; } } constraints.push(_params2); } // build BufferGeometry. var geometry = new _three.BufferGeometry(); geometry.setAttribute('position', new _three.Float32BufferAttribute(positions, 3)); geometry.setAttribute('normal', new _three.Float32BufferAttribute(normals, 3)); geometry.setAttribute('uv', new _three.Float32BufferAttribute(uvs, 2)); geometry.setAttribute('skinIndex', new _three.Uint16BufferAttribute(skinIndices, 4)); geometry.setAttribute('skinWeight', new _three.Float32BufferAttribute(skinWeights, 4)); geometry.setIndex(indices); for (var _i14 = 0, il = groups.length; _i14 < il; _i14++) { geometry.addGroup(groups[_i14].offset, groups[_i14].count, _i14); } geometry.bones = bones; geometry.morphTargets = morphTargets; geometry.morphAttributes.position = morphPositions; geometry.morphTargetsRelative = false; geometry.userData.MMD = { bones: bones, iks: iks, grants: grants, rigidBodies: rigidBodies, constraints: constraints, format: data.metadata.format }; geometry.computeBoundingSphere(); return geometry; } }]); return GeometryBuilder; }(); // /** * @param {THREE.LoadingManager} manager */ var MaterialBuilder = /*#__PURE__*/function () { function MaterialBuilder(manager) { _classCallCheck(this, MaterialBuilder); this.manager = manager; this.textureLoader = new _three.TextureLoader(this.manager); this.tgaLoader = null; // lazy generation this.crossOrigin = 'anonymous'; this.resourcePath = undefined; } /** * @param {string} crossOrigin * @return {MaterialBuilder} */ _createClass(MaterialBuilder, [{ key: "setCrossOrigin", value: function setCrossOrigin(crossOrigin) { this.crossOrigin = crossOrigin; return this; } /** * @param {string} resourcePath * @return {MaterialBuilder} */ }, { key: "setResourcePath", value: function setResourcePath(resourcePath) { this.resourcePath = resourcePath; return this; } /** * @param {Object} data - parsed PMD/PMX data * @param {BufferGeometry} geometry - some properties are dependend on geometry * @param {function} onProgress * @param {function} onError * @return {Array<MMDToonMaterial>} */ }, { key: "build", value: function build(data, geometry /*, onProgress, onError */ ) { var materials = []; var textures = {}; this.textureLoader.setCrossOrigin(this.crossOrigin); // materials for (var i = 0; i < data.metadata.materialCount; i++) { var material = data.materials[i]; var params = { userData: { MMD: {} } }; if (material.name !== undefined) params.name = material.name; /* * Color * * MMD MMDToonMaterial * ambient - emissive * a * (a = 1.0 without map texture or 0.2 with map texture) * * MMDToonMaterial doesn't have ambient. Set it to emissive instead. * It'll be too bright if material has map texture so using coef 0.2. */ params.diffuse = new _three.Color().fromArray(material.diffuse); params.opacity = material.diffuse[3]; params.specular = new _three.Color().fromArray(material.specular); params.shininess = material.shininess; params.emissive = new _three.Color().fromArray(material.ambient); params.transparent = params.opacity !== 1.0; // params.fog = true; // blend params.blending = _three.CustomBlending; params.blendSrc = _three.SrcAlphaFactor; params.blendDst = _three.OneMinusSrcAlphaFactor; params.blendSrcAlpha = _three.SrcAlphaFactor; params.blendDstAlpha = _three.DstAlphaFactor; // side if (data.metadata.format === 'pmx' && (material.flag & 0x1) === 1) { params.side = _three.DoubleSide; } else { params.side = params.opacity === 1.0 ? _three.FrontSide : _three.DoubleSide; } if (data.metadata.format === 'pmd') { // map, envMap if (material.fileName) { var fileName = material.fileName; var fileNames = fileName.split('*'); // fileNames[ 0 ]: mapFileName // fileNames[ 1 ]: envMapFileName( optional ) params.map = this._loadTexture(fileNames[0], textures); if (fileNames.length > 1) { var extension = fileNames[1].slice(-4).toLowerCase(); params.envMap = this._loadTexture(fileNames[1], textures); params.combine = extension === '.sph' ? _three.MultiplyOperation : _three.AddOperation; } } // gradientMap var toonFileName = material.toonIndex === -1 ? 'toon00.bmp' : data.toonTextures[material.toonIndex].fileName; params.gradientMap = this._loadTexture(toonFileName, textures, { isToonTexture: true, isDefaultToonTexture: this._isDefaultToonTexture(toonFileName) }); // parameters for OutlineEffect params.userData.outlineParameters = { thickness: material.edgeFlag === 1 ? 0.003 : 0.0, color: [0, 0, 0], alpha: 1.0, visible: material.edgeFlag === 1 }; } else { // map if (material.textureIndex !== -1) { params.map = this._loadTexture(data.textures[material.textureIndex], textures); // Since PMX spec don't have standard to list map files except color map and env map, // we need to save file name for further mapping, like matching normal map file names after model loaded. // ref: https://gist.github.com/felixjones/f8a06bd48f9da9a4539f#texture params.userData.MMD.mapFileName = data.textures[material.textureIndex]; } // envMap TODO: support m.envFlag === 3 if (material.envTextureIndex !== -1 && (material.envFlag === 1 || material.envFlag == 2)) { params.matcap = this._loadTexture(data.textures[material.envTextureIndex], textures); // Same as color map above, keep file name in userData for further usage. params.userData.MMD.matcapFileName = data.textures[material.envTextureIndex]; params.matcapCombine = material.envFlag === 1 ? _three.MultiplyOperation : _three.AddOperation; } // gradientMap var _toonFileName = void 0, isDefaultToon = void 0; if (material.toonIndex === -1 || material.toonFlag !== 0) { _toonFileName = 'toon' + ('0' + (material.toonIndex + 1)).slice(-2) + '.bmp'; isDefaultToon = true; } else { _toonFileName = data.textures[material.toonIndex]; isDefaultToon = false; } params.gradientMap = this._loadTexture(_toonFileName, textures, { isToonTexture: true, isDefaultToonTexture: isDefaultToon }); // parameters for OutlineEffect params.userData.outlineParameters = { thickness: material.edgeSize / 300, // TODO: better calculation? color: material.edgeColor.slice(0, 3), alpha: material.edgeColor[3], visible: (material.flag & 0x10) !== 0 && material.edgeSize > 0.0 }; } if (params.map !== undefined) { if (!params.transparent) { this._checkImageTransparency(params.map, geometry, i); } params.emissive.multiplyScalar(0.2); } materials.push(new MMDToonMaterial(params)); } if (data.metadata.format === 'pmx') { // set transparent true if alpha morph is defined. var checkAlphaMorph = function checkAlphaMorph(elements, materials) { for (var _i15 = 0, il = elements.length; _i15 < il; _i15++) { var element = elements[_i15]; if (element.index === -1) continue; var _material = materials[element.index]; if (_material.opacity !== element.diffuse[3]) { _material.transparent = true; } } }; for (var _i16 = 0, il = data.morphs.length; _i16 < il; _i16++) { var morph = data.morphs[_i16]; var elements = morph.elements; if (morph.type === 0) { for (var j = 0, jl = elements.length; j < jl; j++) { var morph2 = data.morphs[elements[j].index]; if (morph2.type !== 8) continue; checkAlphaMorph(morph2.elements, materials); } } else if (morph.type === 8) { checkAlphaMorph(elements, materials); } } } return materials; } // private methods }, { key: "_getTGALoader", value: function _getTGALoader() { if (this.tgaLoader === null) { if (_TGALoader.TGALoader === undefined) { throw new Error('THREE.MMDLoader: Import TGALoader'); } this.tgaLoader = new _TGALoader.TGALoader(this.manager); } return this.tgaLoader; } }, { key: "_isDefaultToonTexture", value: function _isDefaultToonTexture(name) { if (name.length !== 10) return false; return /toon(10|0[0-9])\.bmp/.test(name); } }, { key: "_loadTexture", value: function _loadTexture(filePath, textures, params, onProgress, onError) { params = params || {}; var scope = this; var fullPath; if (params.isDefaultToonTexture === true) { var index; try { index = parseInt(filePath.match(/toon([0-9]{2})\.bmp$/)[1]); } catch (e) { console.warn('THREE.MMDLoader: ' + filePath + ' seems like a ' + 'not right default texture path. Using toon00.bmp instead.'); index = 0; } fullPath = DEFAULT_TOON_TEXTURES[index]; } else { fullPath = this.resourcePath + filePath; } if (textures[fullPath] !== undefined) return textures[fullPath]; var loader = this.manager.getHandler(fullPath); if (loader === null) { loader = filePath.slice(-4).toLowerCase() === '.tga' ? this._getTGALoader() : this.textureLoader; } var texture = loader.load(fullPath, function (t) { // MMD toon texture is Axis-Y oriented // but Three.js gradient map is Axis-X oriented. // So here replaces the toon texture image with the rotated one. if (params.isToonTexture === true) { t.image = scope._getRotatedImage(t.image); t.magFilter = _three.NearestFilter; t.minFilter = _three.NearestFilter; } t.flipY = false; t.wrapS = _three.RepeatWrapping; t.wrapT = _three.RepeatWrapping; for (var i = 0; i < texture.readyCallbacks.length; i++) { texture.readyCallbacks[i](texture); } delete texture.readyCallbacks; }, onProgress, onError); texture.readyCallbacks = []; textures[fullPath] = texture; return texture; } }, { key: "_getRotatedImage", value: function _getRotatedImage(image) { var canvas = document.createElement('canvas'); var context = canvas.getContext('2d'); var width = image.width; var height = image.height; canvas.width = width; canvas.height = height; context.clearRect(0, 0, width, height); context.translate(width / 2.0, height / 2.0); context.rotate(0.5 * Math.PI); // 90.0 * Math.PI / 180.0 context.translate(-width / 2.0, -height / 2.0); context.drawImage(image, 0, 0); return context.getImageData(0, 0, width, height); } // Check if the partial image area used by the texture is transparent. }, { key: "_checkImageTransparency", value: function _checkImageTransparency(map, geometry, groupIndex) { map.readyCallbacks.push(function (texture) { // Is there any efficient ways? function createImageData(image) { var canvas = document.createElement('canvas'); canvas.width = image.width; canvas.height = image.height; var context = canvas.getContext('2d'); context.drawImage(image, 0, 0); return context.getImageData(0, 0, canvas.width, canvas.height); } function detectImageTransparency(image, uvs, indices) { var width = image.width; var height = image.height; var data = image.data; var threshold = 253; if (data.length / (width * height) !== 4) return false; for (var i = 0; i < indices.length; i += 3) { var centerUV = { x: 0.0, y: 0.0 }; for (var j = 0; j < 3; j++) { var index = indices[i * 3 + j]; var uv = { x: uvs[index * 2 + 0], y: uvs[index * 2 + 1] }; if (getAlphaByUv(image, uv) < threshold) return true; centerUV.x += uv.x; centerUV.y += uv.y; } centerUV.x /= 3; centerUV.y /= 3; if (getAlphaByUv(image, centerUV) < threshold) return true; } return false; } /* * This method expects * texture.flipY = false * texture.wrapS = RepeatWrapping * texture.wrapT = RepeatWrapping * TODO: more precise */ function getAlphaByUv(image, uv) { var width = image.width; var height = image.height; var x = Math.round(uv.x * width) % width; var y = Math.round(uv.y * height) % height; if (x < 0) x += width; if (y < 0) y += height; var index = y * width + x; return image.data[index * 4 + 3]; } if (texture.isCompressedTexture === true) { if (NON_ALPHA_CHANNEL_FORMATS.includes(texture.format)) { map.transparent = false; } else { // any other way to check transparency of CompressedTexture? map.transparent = true; } return; } var imageData = texture.image.data !== undefined ? texture.image : createImageData(texture.image); var group = geometry.groups[groupIndex]; if (detectImageTransparency(imageData, geometry.attributes.uv.array, geometry.index.array.slice(group.start, group.start + group.count))) { map.transparent = true; } }); } }]); return MaterialBuilder; }(); // var AnimationBuilder = /*#__PURE__*/function () { function AnimationBuilder() { _classCallCheck(this, AnimationBuilder); } _createClass(AnimationBuilder, [{ key: "build", value: /** * @param {Object} vmd - parsed VMD data * @param {SkinnedMesh} mesh - tracks will be fitting to mesh * @return {AnimationClip} */ function build(vmd, mesh) { // combine skeletal and morph animations var tracks = this.buildSkeletalAnimation(vmd, mesh).tracks; var tracks2 = this.buildMorphAnimation(vmd, mesh).tracks; for (var i = 0, il = tracks2.length; i < il; i++) { tracks.push(tracks2[i]); } return new _three.AnimationClip('', -1, tracks); } /** * @param {Object} vmd - parsed VMD data * @param {SkinnedMesh} mesh - tracks will be fitting to mesh * @return {AnimationClip} */ }, { key: "buildSkeletalAnimation", value: function buildSkeletalAnimation(vmd, mesh) { function pushInterpolation(array, interpolation, index) { array.push(interpolation[index + 0] / 127); // x1 array.push(interpolation[index + 8] / 127); // x2 array.push(interpolation[index + 4] / 127); // y1 array.push(interpolation[index + 12] / 127); // y2 } var tracks = []; var motions = {}; var bones = mesh.skeleton.bones; var boneNameDictionary = {}; for (var i = 0, il = bones.length; i < il; i++) { boneNameDictionary[bones[i].name] = true; } for (var _i17 = 0; _i17 < vmd.metadata.motionCount; _i17++) { var motion = vmd.motions[_i17]; var boneName = motion.boneName; if (boneNameDictionary[boneName] === undefined) continue; motions[boneName] = motions[boneName] || []; motions[boneName].push(motion); } for (var key in motions) { var array = motions[key]; array.sort(function (a, b) { return a.frameNum - b.frameNum; }); var times = []; var positions = []; var rotations = []; var pInterpolations = []; var rInterpolations = []; var basePosition = mesh.skeleton.getBoneByName(key).position.toArray(); for (var _i18 = 0, _il2 = array.length; _i18 < _il2; _i18++) { var time = array[_i18].frameNum / 30; var position = array[_i18].position; var rotation = array[_i18].rotation; var interpolation = array[_i18].interpolation; times.push(time); for (var j = 0; j < 3; j++) { positions.push(basePosition[j] + position[j]); } for (var _j10 = 0; _j10 < 4; _j10++) { rotations.push(rotation[_j10]); } for (var _j11 = 0; _j11 < 3; _j11++) { pushInterpolation(pInterpolations, interpolation, _j11); } pushInterpolation(rInterpolations, interpolation, 3); } var targetName = '.bones[' + key + ']'; tracks.push(this._createTrack(targetName + '.position', _three.VectorKeyframeTrack, times, positions, pInterpolations)); tracks.push(this._createTrack(targetName + '.quaternion', _three.QuaternionKeyframeTrack, times, rotations, rInterpolations)); } return new _three.AnimationClip('', -1, tracks); } /** * @param {Object} vmd - parsed VMD data * @param {SkinnedMesh} mesh - tracks will be fitting to mesh * @return {AnimationClip} */ }, { key: "buildMorphAnimation", value: function buildMorphAnimation(vmd, mesh) { var tracks = []; var morphs = {}; var morphTargetDictionary = mesh.morphTargetDictionary; for (var i = 0; i < vmd.metadata.morphCount; i++) { var morph = vmd.morphs[i]; var morphName = morph.morphName; if (morphTargetDictionary[morphName] === undefined) continue; morphs[morphName] = morphs[morphName] || []; morphs[morphName].push(morph); } for (var key in morphs) { var array = morphs[key]; array.sort(function (a, b) { return a.frameNum - b.frameNum; }); var times = []; var values = []; for (var _i19 = 0, il = array.length; _i19 < il; _i19++) { times.push(array[_i19].frameNum / 30); values.push(array[_i19].weight); } tracks.push(new _three.NumberKeyframeTrack('.morphTargetInfluences[' + morphTargetDictionary[key] + ']', times, values)); } return new _three.AnimationClip('', -1, tracks); } /** * @param {Object} vmd - parsed VMD data * @return {AnimationClip} */ }, { key: "buildCameraAnimation", value: function buildCameraAnimation(vmd) { function pushVector3(array, vec) { array.push(vec.x); array.push(vec.y); array.push(vec.z); } function pushQuaternion(array, q) { array.push(q.x); array.push(q.y); array.push(q.z); array.push(q.w); } function pushInterpolation(array, interpolation, index) { array.push(interpolation[index * 4 + 0] / 127); // x1 array.push(interpolation[index * 4 + 1] / 127); // x2 array.push(interpolation[index * 4 + 2] / 127); // y1 array.push(interpolation[index * 4 + 3] / 127); // y2 } var cameras = vmd.cameras === undefined ? [] : vmd.cameras.slice(); cameras.sort(function (a, b) { return a.frameNum - b.frameNum; }); var times = []; var centers = []; var quaternions = []; var positions = []; var fovs = []; var cInterpolations = []; var qInterpolations = []; var pInterpolations = []; var fInterpolations = []; var quaternion = new _three.Quaternion(); var euler = new _three.Euler(); var position = new _three.Vector3(); var center = new _three.Vector3(); for (var i = 0, il = cameras.length; i < il; i++) { var motion = cameras[i]; var time = motion.frameNum / 30; var pos = motion.position; var rot = motion.rotation; var distance = motion.distance; var fov = motion.fov; var interpolation = motion.interpolation; times.push(time); position.set(0, 0, -distance); center.set(pos[0], pos[1], pos[2]); euler.set(-rot[0], -rot[1], -rot[2]); quaternion.setFromEuler(euler); position.add(center); position.applyQuaternion(quaternion); pushVector3(centers, center); pushQuaternion(quaternions, quaternion); pushVector3(positions, position); fovs.push(fov); for (var j = 0; j < 3; j++) { pushInterpolation(cInterpolations, interpolation, j); } pushInterpolation(qInterpolations, interpolation, 3); // use the same parameter for x, y, z axis. for (var _j12 = 0; _j12 < 3; _j12++) { pushInterpolation(pInterpolations, interpolation, 4); } pushInterpolation(fInterpolations, interpolation, 5); } var tracks = []; // I expect an object whose name 'target' exists under THREE.Camera tracks.push(this._createTrack('target.position', _three.VectorKeyframeTrack, times, centers, cInterpolations)); tracks.push(this._createTrack('.quaternion', _three.QuaternionKeyframeTrack, times, quaternions, qInterpolations)); tracks.push(this._createTrack('.position', _three.VectorKeyframeTrack, times, positions, pInterpolations)); tracks.push(this._createTrack('.fov', _three.NumberKeyframeTrack, times, fovs, fInterpolations)); return new _three.AnimationClip('', -1, tracks); } // private method }, { key: "_createTrack", value: function _createTrack(node, typedKeyframeTrack, times, values, interpolations) { /* * optimizes here not to let KeyframeTrackPrototype optimize * because KeyframeTrackPrototype optimizes times and values but * doesn't optimize interpolations. */ if (times.length > 2) { times = times.slice(); values = values.slice(); interpolations = interpolations.slice(); var stride = values.length / times.length; var interpolateStride = interpolations.length / times.length; var index = 1; for (var aheadIndex = 2, endIndex = times.length; aheadIndex < endIndex; aheadIndex++) { for (var i = 0; i < stride; i++) { if (values[index * stride + i] !== values[(index - 1) * stride + i] || values[index * stride + i] !== values[aheadIndex * stride + i]) { index++; break; } } if (aheadIndex > index) { times[index] = times[aheadIndex]; for (var _i20 = 0; _i20 < stride; _i20++) { values[index * stride + _i20] = values[aheadIndex * stride + _i20]; } for (var _i21 = 0; _i21 < interpolateStride; _i21++) { interpolations[index * interpolateStride + _i21] = interpolations[aheadIndex * interpolateStride + _i21]; } } } times.length = index + 1; values.length = (index + 1) * stride; interpolations.length = (index + 1) * interpolateStride; } var track = new typedKeyframeTrack(node, times, values); track.createInterpolant = function InterpolantFactoryMethodCubicBezier(result) { return new CubicBezierInterpolation(this.times, this.values, this.getValueSize(), result, new Float32Array(interpolations)); }; return track; } }]); return AnimationBuilder; }(); // interpolation var CubicBezierInterpolation = /*#__PURE__*/function (_Interpolant) { _inherits(CubicBezierInterpolation, _Interpolant); var _super2 = _createSuper(CubicBezierInterpolation); function CubicBezierInterpolation(parameterPositions, sampleValues, sampleSize, resultBuffer, params) { var _this2; _classCallCheck(this, CubicBezierInterpolation); _this2 = _super2.call(this, parameterPositions, sampleValues, sampleSize, resultBuffer); _this2.interpolationParams = params; return _this2; } _createClass(CubicBezierInterpolation, [{ key: "interpolate_", value: function interpolate_(i1, t0, t, t1) { var result = this.resultBuffer; var values = this.sampleValues; var stride = this.valueSize; var params = this.interpolationParams; var offset1 = i1 * stride; var offset0 = offset1 - stride; // No interpolation if next key frame is in one frame in 30fps. // This is from MMD animation spec. // '1.5' is for precision loss. times are Float32 in Three.js Animation system. var weight1 = t1 - t0 < 1 / 30 * 1.5 ? 0.0 : (t - t0) / (t1 - t0); if (stride === 4) { // Quaternion var x1 = params[i1 * 4 + 0]; var x2 = params[i1 * 4 + 1]; var y1 = params[i1 * 4 + 2]; var y2 = params[i1 * 4 + 3]; var ratio = this._calculate(x1, x2, y1, y2, weight1); _three.Quaternion.slerpFlat(result, 0, values, offset0, values, offset1, ratio); } else if (stride === 3) { // Vector3 for (var i = 0; i !== stride; ++i) { var _x = params[i1 * 12 + i * 4 + 0]; var _x2 = params[i1 * 12 + i * 4 + 1]; var _y = params[i1 * 12 + i * 4 + 2]; var _y2 = params[i1 * 12 + i * 4 + 3]; var _ratio = this._calculate(_x, _x2, _y, _y2, weight1); result[i] = values[offset0 + i] * (1 - _ratio) + values[offset1 + i] * _ratio; } } else { // Number var _x3 = params[i1 * 4 + 0]; var _x4 = params[i1 * 4 + 1]; var _y3 = params[i1 * 4 + 2]; var _y4 = params[i1 * 4 + 3]; var _ratio2 = this._calculate(_x3, _x4, _y3, _y4, weight1); result[0] = values[offset0] * (1 - _ratio2) + values[offset1] * _ratio2; } return result; } }, { key: "_calculate", value: function _calculate(x1, x2, y1, y2, x) { /* * Cubic Bezier curves * https://en.wikipedia.org/wiki/B%C3%A9zier_curve#Cubic_B.C3.A9zier_curves * * B(t) = ( 1 - t ) ^ 3 * P0 * + 3 * ( 1 - t ) ^ 2 * t * P1 * + 3 * ( 1 - t ) * t^2 * P2 * + t ^ 3 * P3 * ( 0 <= t <= 1 ) * * MMD uses Cubic Bezier curves for bone and camera animation interpolation. * http://d.hatena.ne.jp/edvakf/20111016/1318716097 * * x = ( 1 - t ) ^ 3 * x0 * + 3 * ( 1 - t ) ^ 2 * t * x1 * + 3 * ( 1 - t ) * t^2 * x2 * + t ^ 3 * x3 * y = ( 1 - t ) ^ 3 * y0 * + 3 * ( 1 - t ) ^ 2 * t * y1 * + 3 * ( 1 - t ) * t^2 * y2 * + t ^ 3 * y3 * ( x0 = 0, y0 = 0 ) * ( x3 = 1, y3 = 1 ) * ( 0 <= t, x1, x2, y1, y2 <= 1 ) * * Here solves this equation with Bisection method, * https://en.wikipedia.org/wiki/Bisection_method * gets t, and then calculate y. * * f(t) = 3 * ( 1 - t ) ^ 2 * t * x1 * + 3 * ( 1 - t ) * t^2 * x2 * + t ^ 3 - x = 0 * * (Another option: Newton's method * https://en.wikipedia.org/wiki/Newton%27s_method) */ var c = 0.5; var t = c; var s = 1.0 - t; var loop = 15; var eps = 1e-5; var math = Math; var sst3, stt3, ttt; for (var i = 0; i < loop; i++) { sst3 = 3.0 * s * s * t; stt3 = 3.0 * s * t * t; ttt = t * t * t; var ft = sst3 * x1 + stt3 * x2 + ttt - x; if (math.abs(ft) < eps) break; c /= 2.0; t += ft < 0 ? c : -c; s = 1.0 - t; } return sst3 * y1 + stt3 * y2 + ttt; } }]); return CubicBezierInterpolation; }(_three.Interpolant); var MMDToonMaterial = /*#__PURE__*/function (_ShaderMaterial) { _inherits(MMDToonMaterial, _ShaderMaterial); var _super3 = _createSuper(MMDToonMaterial); function MMDToonMaterial(parameters) { var _this3; _classCallCheck(this, MMDToonMaterial); _this3 = _super3.call(this); _this3._matcapCombine = _three.AddOperation; _this3.emissiveIntensity = 1.0; _this3.normalMapType = _three.TangentSpaceNormalMap; _this3.combine = _three.MultiplyOperation; _this3.wireframeLinecap = 'round'; _this3.wireframeLinejoin = 'round'; _this3.flatShading = false; _this3.lights = true; _this3.vertexShader = _MMDToonShader.MMDToonShader.vertexShader; _this3.fragmentShader = _MMDToonShader.MMDToonShader.fragmentShader; _this3.defines = Object.assign({}, _MMDToonShader.MMDToonShader.defines); Object.defineProperty(_assertThisInitialized(_this3), 'matcapCombine', { get: function get() { return this._matcapCombine; }, set: function set(value) { this._matcapCombine = value; switch (value) { case _three.MultiplyOperation: this.defines.MATCAP_BLENDING_MULTIPLY = true; delete this.defines.MATCAP_BLENDING_ADD; break; default: case _three.AddOperation: this.defines.MATCAP_BLENDING_ADD = true; delete this.defines.MATCAP_BLENDING_MULTIPLY; break; } } }); _this3.uniforms = _three.UniformsUtils.clone(_MMDToonShader.MMDToonShader.uniforms); // merged from MeshToon/Phong/MatcapMaterial var exposePropertyNames = ['specular', 'shininess', 'opacity', 'diffuse', 'map', 'matcap', 'gradientMap', 'lightMap', 'lightMapIntensity', 'aoMap', 'aoMapIntensity', 'emissive', 'emissiveMap', 'bumpMap', 'bumpScale', 'normalMap', 'normalScale', 'displacemantBias', 'displacemantMap', 'displacemantScale', 'specularMap', 'alphaMap', 'envMap', 'reflectivity', 'refractionRatio']; var _loop = function _loop() { var propertyName = _exposePropertyNames[_i22]; Object.defineProperty(_assertThisInitialized(_this3), propertyName, { get: function get() { return this.uniforms[propertyName].value; }, set: function set(value) { this.uniforms[propertyName].value = value; } }); }; for (var _i22 = 0, _exposePropertyNames = exposePropertyNames; _i22 < _exposePropertyNames.length; _i22++) { _loop(); } Object.defineProperty(_assertThisInitialized(_this3), 'color', Object.getOwnPropertyDescriptor(_assertThisInitialized(_this3), 'diffuse')); _this3.setValues(parameters); return _this3; } _createClass(MMDToonMaterial, [{ key: "copy", value: function copy(source) { _get(_getPrototypeOf(MMDToonMaterial.prototype), "copy", this).call(this, source); this.matcapCombine = source.matcapCombine; this.emissiveIntensity = source.emissiveIntensity; this.normalMapType = source.normalMapType; this.combine = source.combine; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; this.flatShading = source.flatShading; return this; } }]); return MMDToonMaterial; }(_three.ShaderMaterial); MMDToonMaterial.prototype.isMMDToonMaterial = true; });