diff --git a/CHANGELOG.md b/CHANGELOG.md
index 59d8b06..356d63c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,16 @@
# AnVRopomotron Changelog
-## 1.2.2 (next)
+## 1.2.3 (6/01/2023)
+A Three.JS update bonked A-Frame components in version 1.4.2, needed for Quest Pro compatibility, and my museum of cards collapsed.
+- Updated A-Frame to 1.4.2 and c-frame-managed components to latest versions.
+- Removed aframe-fps-component link and code that allowed switching to mouselook and back in PC mode. It no longer works with the latest A-Frame.
+- A-Frame 1.4.2 and the physics component now loads in a order that does not produce working physics bodies from gltf models. A new component, static-wait, now adds an event listener that waits for gltf models to load before trying to apply a physics body to it. A previously existing component, table-wait, dynamically adds dynamic bodies for grabbable objects in VR.
+- New version, new movement scheme in VR. Smooth movement is back. Right stick smooth turns.
+- Ada Rose Cannon's simple-navmesh-constraint component is now driving the navmesh. The A-Frame Extras version functions oddly as if everything was shifted despite where the model appeared to be.
+- Two flipped normals in the navmesh fixed to be in line with the rest of the navmesh. This was not an issue with the old navmesh but simple-navmesh-constraint did not like it.
+- Typos spotted and fixed.
+- My name is under the museum name on the back wall now.
+
+## 1.2.2 (8/08/2022)
It's summer so it's time to make models non-stop.
- Ardi the Ardipithecus ramidus makes her way down to the Human Evolution Hall! This important discovery gives a clear image of what hominin evolution was like pre-Lucy. I've never seen Ardi and Lucy lined up so making the model was illuminating for me. I didn't know that Ardi was taller and that her arms were so long. The reduction in arm length in Lucy is very stark now, though her arms are still proportionately longer than our own.
- Proboscis monkeys nose-in to the Asia section of Modern Primates! I just couldn't resist these special looking monkeys with the males and their bulbous flabby noses and females with sharp upturned ones. I modeled a male and female to show the sexual dimorphism in faces, limb proportions, and size. I had a hard time posing the male because they are typically seated upright while the monkey was modeled from the mandrill on four legs. Since the underlying rig is for a dog, sitting up caused a lot of visual problems that would take too long to fix. I avoided the issue by finding a vertical-yet-not-sitting pose for the male climbing up a tree that was based on a photograph online. The female is also posed from photograph relaxing on the same tree. Not to harp on the failures of this model, but I also tried leaf generation using the built-in Sapling Gen add-on, but I couldn't get satisfactory results this time.
diff --git a/index.html b/index.html
index aada359..4919fc3 100644
--- a/index.html
+++ b/index.html
@@ -20,21 +20,19 @@
-
-
-
+
+
-
+
-
+
-
-
-
+
+
-
+
@@ -62,15 +60,15 @@
-
+
-
+
@@ -83,18 +81,18 @@
-
-
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
@@ -115,22 +113,22 @@
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
@@ -144,38 +142,36 @@
-
+
-
+
-
+
-
+
-
-
+
+
-
+
-
-
-
-
+
+
-
-
+
+
-
+
@@ -184,13 +180,13 @@
-
+
-
-
-
+
+
+
@@ -209,8 +205,8 @@
-
-
+
+
@@ -230,7 +226,7 @@
-
+
@@ -245,7 +241,7 @@
-
+
@@ -270,7 +266,7 @@
-
+
@@ -279,7 +275,7 @@
-
+
@@ -290,19 +286,19 @@
-
+
-
-
+
+
-
+
-
+
@@ -311,52 +307,52 @@
-
+
-
-
-
-
-
-
+
+
+
+
+
+
-
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
+
-
-
-
-
-
+
+
+
+
+
@@ -364,7 +360,7 @@
-
+
@@ -388,7 +384,7 @@
-
+
@@ -408,25 +404,25 @@
-
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
+
-
-
-
-
+
+
+
+
@@ -434,7 +430,7 @@
-
+
@@ -454,7 +450,7 @@
-
+
@@ -463,7 +459,7 @@
-
+
@@ -471,7 +467,7 @@
-
+
@@ -479,43 +475,43 @@
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -523,45 +519,45 @@
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -569,7 +565,7 @@
-
+
0) tracks.push(new THREE.VectorKeyframeTrack(name + '.position', times, positionData));
- if (quaternionData.length > 0) tracks.push(new THREE.QuaternionKeyframeTrack(name + '.quaternion', times, quaternionData));
- if (scaleData.length > 0) tracks.push(new THREE.VectorKeyframeTrack(name + '.scale', times, scaleData));
-
- return tracks;
- }
-
- function transformAnimationData(keyframes, property, defaultValue) {
-
- var keyframe;
-
- var empty = true;
- var i, l;
-
- // check, if values of a property are missing in our keyframes
-
- for (i = 0, l = keyframes.length; i < l; i++) {
-
- keyframe = keyframes[i];
-
- if (keyframe.value[property] === undefined) {
-
- keyframe.value[property] = null; // mark as missing
- } else {
-
- empty = false;
- }
- }
-
- if (empty === true) {
-
- // no values at all, so we set a default value
-
- for (i = 0, l = keyframes.length; i < l; i++) {
-
- keyframe = keyframes[i];
-
- keyframe.value[property] = defaultValue;
- }
- } else {
-
- // filling gaps
-
- createMissingKeyframes(keyframes, property);
- }
- }
-
- function createMissingKeyframes(keyframes, property) {
-
- var prev, next;
-
- for (var i = 0, l = keyframes.length; i < l; i++) {
-
- var keyframe = keyframes[i];
-
- if (keyframe.value[property] === null) {
-
- prev = getPrev(keyframes, i, property);
- next = getNext(keyframes, i, property);
-
- if (prev === null) {
-
- keyframe.value[property] = next.value[property];
- continue;
- }
-
- if (next === null) {
-
- keyframe.value[property] = prev.value[property];
- continue;
- }
-
- interpolate(keyframe, prev, next, property);
- }
- }
- }
-
- function getPrev(keyframes, i, property) {
-
- while (i >= 0) {
-
- var keyframe = keyframes[i];
-
- if (keyframe.value[property] !== null) return keyframe;
-
- i--;
- }
-
- return null;
- }
-
- function getNext(keyframes, i, property) {
-
- while (i < keyframes.length) {
-
- var keyframe = keyframes[i];
-
- if (keyframe.value[property] !== null) return keyframe;
-
- i++;
- }
-
- return null;
- }
-
- function interpolate(key, prev, next, property) {
-
- if (next.time - prev.time === 0) {
-
- key.value[property] = prev.value[property];
- return;
- }
-
- key.value[property] = (key.time - prev.time) * (next.value[property] - prev.value[property]) / (next.time - prev.time) + prev.value[property];
- }
-
- // animation clips
-
- function parseAnimationClip(xml) {
-
- var data = {
- name: xml.getAttribute('id') || 'default',
- start: parseFloat(xml.getAttribute('start') || 0),
- end: parseFloat(xml.getAttribute('end') || 0),
- animations: []
- };
-
- for (var i = 0, l = xml.childNodes.length; i < l; i++) {
-
- var child = xml.childNodes[i];
-
- if (child.nodeType !== 1) continue;
-
- switch (child.nodeName) {
-
- case 'instance_animation':
- data.animations.push(parseId(child.getAttribute('url')));
- break;
-
- }
- }
-
- library.clips[xml.getAttribute('id')] = data;
- }
-
- function buildAnimationClip(data) {
-
- var tracks = [];
-
- var name = data.name;
- var duration = data.end - data.start || -1;
- var animations = data.animations;
-
- for (var i = 0, il = animations.length; i < il; i++) {
-
- var animationTracks = getAnimation(animations[i]);
-
- for (var j = 0, jl = animationTracks.length; j < jl; j++) {
-
- tracks.push(animationTracks[j]);
- }
- }
-
- return new THREE.AnimationClip(name, duration, tracks);
- }
-
- function getAnimationClip(id) {
-
- return getBuild(library.clips[id], buildAnimationClip);
- }
-
- // controller
-
- function parseController(xml) {
-
- var data = {};
-
- for (var i = 0, l = xml.childNodes.length; i < l; i++) {
-
- var child = xml.childNodes[i];
-
- if (child.nodeType !== 1) continue;
-
- switch (child.nodeName) {
-
- case 'skin':
- // there is exactly one skin per controller
- data.id = parseId(child.getAttribute('source'));
- data.skin = parseSkin(child);
- break;
-
- case 'morph':
- data.id = parseId(child.getAttribute('source'));
- console.warn('THREE.ColladaLoader: Morph target animation not supported yet.');
- break;
-
- }
- }
-
- library.controllers[xml.getAttribute('id')] = data;
- }
-
- function parseSkin(xml) {
-
- var data = {
- sources: {}
- };
-
- for (var i = 0, l = xml.childNodes.length; i < l; i++) {
-
- var child = xml.childNodes[i];
-
- if (child.nodeType !== 1) continue;
-
- switch (child.nodeName) {
-
- case 'bind_shape_matrix':
- data.bindShapeMatrix = parseFloats(child.textContent);
- break;
-
- case 'source':
- var id = child.getAttribute('id');
- data.sources[id] = parseSource(child);
- break;
-
- case 'joints':
- data.joints = parseJoints(child);
- break;
-
- case 'vertex_weights':
- data.vertexWeights = parseVertexWeights(child);
- break;
-
- }
- }
-
- return data;
- }
-
- function parseJoints(xml) {
-
- var data = {
- inputs: {}
- };
-
- for (var i = 0, l = xml.childNodes.length; i < l; i++) {
-
- var child = xml.childNodes[i];
-
- if (child.nodeType !== 1) continue;
-
- switch (child.nodeName) {
-
- case 'input':
- var semantic = child.getAttribute('semantic');
- var id = parseId(child.getAttribute('source'));
- data.inputs[semantic] = id;
- break;
-
- }
- }
-
- return data;
- }
-
- function parseVertexWeights(xml) {
-
- var data = {
- inputs: {}
- };
-
- for (var i = 0, l = xml.childNodes.length; i < l; i++) {
-
- var child = xml.childNodes[i];
-
- if (child.nodeType !== 1) continue;
-
- switch (child.nodeName) {
-
- case 'input':
- var semantic = child.getAttribute('semantic');
- var id = parseId(child.getAttribute('source'));
- var offset = parseInt(child.getAttribute('offset'));
- data.inputs[semantic] = { id: id, offset: offset };
- break;
-
- case 'vcount':
- data.vcount = parseInts(child.textContent);
- break;
-
- case 'v':
- data.v = parseInts(child.textContent);
- break;
-
- }
- }
-
- return data;
- }
-
- function buildController(data) {
-
- var build = {
- id: data.id
- };
-
- var geometry = library.geometries[build.id];
-
- if (data.skin !== undefined) {
-
- build.skin = buildSkin(data.skin);
-
- // we enhance the 'sources' property of the corresponding geometry with our skin data
-
- geometry.sources.skinIndices = build.skin.indices;
- geometry.sources.skinWeights = build.skin.weights;
- }
-
- return build;
- }
-
- function buildSkin(data) {
-
- var BONE_LIMIT = 4;
-
- var build = {
- joints: [], // this must be an array to preserve the joint order
- indices: {
- array: [],
- stride: BONE_LIMIT
- },
- weights: {
- array: [],
- stride: BONE_LIMIT
- }
- };
-
- var sources = data.sources;
- var vertexWeights = data.vertexWeights;
-
- var vcount = vertexWeights.vcount;
- var v = vertexWeights.v;
- var jointOffset = vertexWeights.inputs.JOINT.offset;
- var weightOffset = vertexWeights.inputs.WEIGHT.offset;
-
- var jointSource = data.sources[data.joints.inputs.JOINT];
- var inverseSource = data.sources[data.joints.inputs.INV_BIND_MATRIX];
-
- var weights = sources[vertexWeights.inputs.WEIGHT.id].array;
- var stride = 0;
-
- var i, j, l;
-
- // procces skin data for each vertex
-
- for (i = 0, l = vcount.length; i < l; i++) {
-
- var jointCount = vcount[i]; // this is the amount of joints that affect a single vertex
- var vertexSkinData = [];
-
- for (j = 0; j < jointCount; j++) {
-
- var skinIndex = v[stride + jointOffset];
- var weightId = v[stride + weightOffset];
- var skinWeight = weights[weightId];
-
- vertexSkinData.push({ index: skinIndex, weight: skinWeight });
-
- stride += 2;
- }
-
- // we sort the joints in descending order based on the weights.
- // this ensures, we only procced the most important joints of the vertex
-
- vertexSkinData.sort(descending);
-
- // now we provide for each vertex a set of four index and weight values.
- // the order of the skin data matches the order of vertices
-
- for (j = 0; j < BONE_LIMIT; j++) {
-
- var d = vertexSkinData[j];
-
- if (d !== undefined) {
-
- build.indices.array.push(d.index);
- build.weights.array.push(d.weight);
- } else {
-
- build.indices.array.push(0);
- build.weights.array.push(0);
- }
- }
- }
-
- // setup bind matrix
-
- if (data.bindShapeMatrix) {
-
- build.bindMatrix = new THREE.Matrix4().fromArray(data.bindShapeMatrix).transpose();
- } else {
-
- build.bindMatrix = new THREE.Matrix4().identity();
- }
-
- // process bones and inverse bind matrix data
-
- for (i = 0, l = jointSource.array.length; i < l; i++) {
-
- var name = jointSource.array[i];
- var boneInverse = new THREE.Matrix4().fromArray(inverseSource.array, i * inverseSource.stride).transpose();
-
- build.joints.push({ name: name, boneInverse: boneInverse });
- }
-
- return build;
-
- // array sort function
-
- function descending(a, b) {
-
- return b.weight - a.weight;
- }
- }
-
- function getController(id) {
-
- return getBuild(library.controllers[id], buildController);
- }
-
- // image
-
- function parseImage(xml) {
-
- var data = {
- init_from: getElementsByTagName(xml, 'init_from')[0].textContent
- };
-
- library.images[xml.getAttribute('id')] = data;
- }
-
- function buildImage(data) {
-
- if (data.build !== undefined) return data.build;
-
- return data.init_from;
- }
-
- function getImage(id) {
-
- var data = library.images[id];
-
- if (data !== undefined) {
-
- return getBuild(data, buildImage);
- }
-
- console.warn('THREE.ColladaLoader: Couldn\'t find image with ID:', id);
-
- return null;
- }
-
- // effect
-
- function parseEffect(xml) {
-
- var data = {};
-
- for (var i = 0, l = xml.childNodes.length; i < l; i++) {
-
- var child = xml.childNodes[i];
-
- if (child.nodeType !== 1) continue;
-
- switch (child.nodeName) {
-
- case 'profile_COMMON':
- data.profile = parseEffectProfileCOMMON(child);
- break;
-
- }
- }
-
- library.effects[xml.getAttribute('id')] = data;
- }
-
- function parseEffectProfileCOMMON(xml) {
-
- var data = {
- surfaces: {},
- samplers: {}
- };
-
- for (var i = 0, l = xml.childNodes.length; i < l; i++) {
-
- var child = xml.childNodes[i];
-
- if (child.nodeType !== 1) continue;
-
- switch (child.nodeName) {
-
- case 'newparam':
- parseEffectNewparam(child, data);
- break;
-
- case 'technique':
- data.technique = parseEffectTechnique(child);
- break;
-
- case 'extra':
- data.extra = parseEffectExtra(child);
- break;
-
- }
- }
-
- return data;
- }
-
- function parseEffectNewparam(xml, data) {
-
- var sid = xml.getAttribute('sid');
-
- for (var i = 0, l = xml.childNodes.length; i < l; i++) {
-
- var child = xml.childNodes[i];
-
- if (child.nodeType !== 1) continue;
-
- switch (child.nodeName) {
-
- case 'surface':
- data.surfaces[sid] = parseEffectSurface(child);
- break;
-
- case 'sampler2D':
- data.samplers[sid] = parseEffectSampler(child);
- break;
-
- }
- }
- }
-
- function parseEffectSurface(xml) {
-
- var data = {};
-
- for (var i = 0, l = xml.childNodes.length; i < l; i++) {
-
- var child = xml.childNodes[i];
-
- if (child.nodeType !== 1) continue;
-
- switch (child.nodeName) {
-
- case 'init_from':
- data.init_from = child.textContent;
- break;
-
- }
- }
-
- return data;
- }
-
- function parseEffectSampler(xml) {
-
- var data = {};
-
- for (var i = 0, l = xml.childNodes.length; i < l; i++) {
-
- var child = xml.childNodes[i];
-
- if (child.nodeType !== 1) continue;
-
- switch (child.nodeName) {
-
- case 'source':
- data.source = child.textContent;
- break;
-
- }
- }
-
- return data;
- }
-
- function parseEffectTechnique(xml) {
-
- var data = {};
-
- for (var i = 0, l = xml.childNodes.length; i < l; i++) {
-
- var child = xml.childNodes[i];
-
- if (child.nodeType !== 1) continue;
-
- switch (child.nodeName) {
-
- case 'constant':
- case 'lambert':
- case 'blinn':
- case 'phong':
- data.type = child.nodeName;
- data.parameters = parseEffectParameters(child);
- break;
-
- }
- }
-
- return data;
- }
-
- function parseEffectParameters(xml) {
-
- var data = {};
-
- for (var i = 0, l = xml.childNodes.length; i < l; i++) {
-
- var child = xml.childNodes[i];
-
- if (child.nodeType !== 1) continue;
-
- switch (child.nodeName) {
-
- case 'emission':
- case 'diffuse':
- case 'specular':
- case 'bump':
- case 'ambient':
- case 'shininess':
- case 'transparency':
- data[child.nodeName] = parseEffectParameter(child);
- break;
- case 'transparent':
- data[child.nodeName] = {
- opaque: child.getAttribute('opaque'),
- data: parseEffectParameter(child)
- };
- break;
-
- }
- }
-
- return data;
- }
-
- function parseEffectParameter(xml) {
-
- var data = {};
-
- for (var i = 0, l = xml.childNodes.length; i < l; i++) {
-
- var child = xml.childNodes[i];
-
- if (child.nodeType !== 1) continue;
-
- switch (child.nodeName) {
-
- case 'color':
- data[child.nodeName] = parseFloats(child.textContent);
- break;
-
- case 'float':
- data[child.nodeName] = parseFloat(child.textContent);
- break;
-
- case 'texture':
- data[child.nodeName] = { id: child.getAttribute('texture'), extra: parseEffectParameterTexture(child) };
- break;
-
- }
- }
-
- return data;
- }
-
- function parseEffectParameterTexture(xml) {
-
- var data = {
- technique: {}
- };
-
- for (var i = 0, l = xml.childNodes.length; i < l; i++) {
-
- var child = xml.childNodes[i];
-
- if (child.nodeType !== 1) continue;
-
- switch (child.nodeName) {
-
- case 'extra':
- parseEffectParameterTextureExtra(child, data);
- break;
-
- }
- }
-
- return data;
- }
-
- function parseEffectParameterTextureExtra(xml, data) {
-
- for (var i = 0, l = xml.childNodes.length; i < l; i++) {
-
- var child = xml.childNodes[i];
-
- if (child.nodeType !== 1) continue;
-
- switch (child.nodeName) {
-
- case 'technique':
- parseEffectParameterTextureExtraTechnique(child, data);
- break;
-
- }
- }
- }
-
- function parseEffectParameterTextureExtraTechnique(xml, data) {
-
- for (var i = 0, l = xml.childNodes.length; i < l; i++) {
-
- var child = xml.childNodes[i];
-
- if (child.nodeType !== 1) continue;
-
- switch (child.nodeName) {
-
- case 'repeatU':
- case 'repeatV':
- case 'offsetU':
- case 'offsetV':
- data.technique[child.nodeName] = parseFloat(child.textContent);
- break;
-
- case 'wrapU':
- case 'wrapV':
-
- // some files have values for wrapU/wrapV which become NaN via parseInt
-
- if (child.textContent.toUpperCase() === 'TRUE') {
-
- data.technique[child.nodeName] = 1;
- } else if (child.textContent.toUpperCase() === 'FALSE') {
-
- data.technique[child.nodeName] = 0;
- } else {
-
- data.technique[child.nodeName] = parseInt(child.textContent);
- }
-
- break;
-
- }
- }
- }
-
- function parseEffectExtra(xml) {
-
- var data = {};
-
- for (var i = 0, l = xml.childNodes.length; i < l; i++) {
-
- var child = xml.childNodes[i];
-
- if (child.nodeType !== 1) continue;
-
- switch (child.nodeName) {
-
- case 'technique':
- data.technique = parseEffectExtraTechnique(child);
- break;
-
- }
- }
-
- return data;
- }
-
- function parseEffectExtraTechnique(xml) {
-
- var data = {};
-
- for (var i = 0, l = xml.childNodes.length; i < l; i++) {
-
- var child = xml.childNodes[i];
-
- if (child.nodeType !== 1) continue;
-
- switch (child.nodeName) {
-
- case 'double_sided':
- data[child.nodeName] = parseInt(child.textContent);
- break;
-
- }
- }
-
- return data;
- }
-
- function buildEffect(data) {
-
- return data;
- }
-
- function getEffect(id) {
-
- return getBuild(library.effects[id], buildEffect);
- }
-
- // material
-
- function parseMaterial(xml) {
-
- var data = {
- name: xml.getAttribute('name')
- };
-
- for (var i = 0, l = xml.childNodes.length; i < l; i++) {
-
- var child = xml.childNodes[i];
-
- if (child.nodeType !== 1) continue;
-
- switch (child.nodeName) {
-
- case 'instance_effect':
- data.url = parseId(child.getAttribute('url'));
- break;
-
- }
- }
-
- library.materials[xml.getAttribute('id')] = data;
- }
-
- function getTextureLoader(image) {
-
- var loader;
-
- var extension = image.slice((image.lastIndexOf('.') - 1 >>> 0) + 2); // http://www.jstips.co/en/javascript/get-file-extension/
- extension = extension.toLowerCase();
-
- switch (extension) {
-
- case 'tga':
- loader = tgaLoader;
- break;
-
- default:
- loader = textureLoader;
-
- }
-
- return loader;
- }
-
- function buildMaterial(data) {
-
- var effect = getEffect(data.url);
- var technique = effect.profile.technique;
- var extra = effect.profile.extra;
-
- var material;
-
- switch (technique.type) {
-
- case 'phong':
- case 'blinn':
- material = new THREE.MeshPhongMaterial();
- break;
-
- case 'lambert':
- material = new THREE.MeshLambertMaterial();
- break;
-
- default:
- material = new THREE.MeshBasicMaterial();
- break;
-
- }
-
- material.name = data.name;
-
- function getTexture(textureObject) {
-
- var sampler = effect.profile.samplers[textureObject.id];
- var image = null;
-
- // get image
-
- if (sampler !== undefined) {
-
- var surface = effect.profile.surfaces[sampler.source];
- image = getImage(surface.init_from);
- } else {
-
- console.warn('THREE.ColladaLoader: Undefined sampler. Access image directly (see #12530).');
- image = getImage(textureObject.id);
- }
-
- // create texture if image is avaiable
-
- if (image !== null) {
-
- var loader = getTextureLoader(image);
-
- if (loader !== undefined) {
-
- var texture = loader.load(image);
-
- var extra = textureObject.extra;
-
- if (extra !== undefined && extra.technique !== undefined && isEmpty(extra.technique) === false) {
-
- var technique = extra.technique;
-
- texture.wrapS = technique.wrapU ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping;
- texture.wrapT = technique.wrapV ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping;
-
- texture.offset.set(technique.offsetU || 0, technique.offsetV || 0);
- texture.repeat.set(technique.repeatU || 1, technique.repeatV || 1);
- } else {
-
- texture.wrapS = THREE.RepeatWrapping;
- texture.wrapT = THREE.RepeatWrapping;
- }
-
- return texture;
- } else {
-
- console.warn('THREE.ColladaLoader: Loader for texture %s not found.', image);
-
- return null;
- }
- } else {
-
- console.warn('THREE.ColladaLoader: Couldn\'t create texture with ID:', textureObject.id);
-
- return null;
- }
- }
-
- var parameters = technique.parameters;
-
- for (var key in parameters) {
-
- var parameter = parameters[key];
-
- switch (key) {
-
- case 'diffuse':
- if (parameter.color) material.color.fromArray(parameter.color);
- if (parameter.texture) material.map = getTexture(parameter.texture);
- break;
- case 'specular':
- if (parameter.color && material.specular) material.specular.fromArray(parameter.color);
- if (parameter.texture) material.specularMap = getTexture(parameter.texture);
- break;
- case 'bump':
- if (parameter.texture) material.normalMap = getTexture(parameter.texture);
- break;
- case 'ambient':
- if (parameter.texture) material.lightMap = getTexture(parameter.texture);
- break;
- case 'shininess':
- if (parameter.float && material.shininess) material.shininess = parameter.float;
- break;
- case 'emission':
- if (parameter.color && material.emissive) material.emissive.fromArray(parameter.color);
- if (parameter.texture) material.emissiveMap = getTexture(parameter.texture);
- break;
-
- }
- }
-
- //
-
- var transparent = parameters['transparent'];
- var transparency = parameters['transparency'];
-
- // does not exist but
-
- if (transparency === undefined && transparent) {
-
- transparency = {
- float: 1
- };
- }
-
- // does not exist but
-
- if (transparent === undefined && transparency) {
-
- transparent = {
- opaque: 'A_ONE',
- data: {
- color: [1, 1, 1, 1]
- } };
- }
-
- if (transparent && transparency) {
-
- // handle case if a texture exists but no color
-
- if (transparent.data.texture) {
-
- // we do not set an alpha map (see #13792)
-
- material.transparent = true;
- } else {
-
- var color = transparent.data.color;
-
- switch (transparent.opaque) {
-
- case 'A_ONE':
- material.opacity = color[3] * transparency.float;
- break;
- case 'RGB_ZERO':
- material.opacity = 1 - color[0] * transparency.float;
- break;
- case 'A_ZERO':
- material.opacity = 1 - color[3] * transparency.float;
- break;
- case 'RGB_ONE':
- material.opacity = color[0] * transparency.float;
- break;
- default:
- console.warn('THREE.ColladaLoader: Invalid opaque type "%s" of transparent tag.', transparent.opaque);
-
- }
-
- if (material.opacity < 1) material.transparent = true;
- }
- }
-
- //
-
- if (extra !== undefined && extra.technique !== undefined && extra.technique.double_sided === 1) {
-
- material.side = THREE.DoubleSide;
- }
-
- return material;
- }
-
- function getMaterial(id) {
-
- return getBuild(library.materials[id], buildMaterial);
- }
-
- // camera
-
- function parseCamera(xml) {
-
- var data = {
- name: xml.getAttribute('name')
- };
-
- for (var i = 0, l = xml.childNodes.length; i < l; i++) {
-
- var child = xml.childNodes[i];
-
- if (child.nodeType !== 1) continue;
-
- switch (child.nodeName) {
-
- case 'optics':
- data.optics = parseCameraOptics(child);
- break;
-
- }
- }
-
- library.cameras[xml.getAttribute('id')] = data;
- }
-
- function parseCameraOptics(xml) {
-
- for (var i = 0; i < xml.childNodes.length; i++) {
-
- var child = xml.childNodes[i];
-
- switch (child.nodeName) {
-
- case 'technique_common':
- return parseCameraTechnique(child);
-
- }
- }
-
- return {};
- }
-
- function parseCameraTechnique(xml) {
-
- var data = {};
-
- for (var i = 0; i < xml.childNodes.length; i++) {
-
- var child = xml.childNodes[i];
-
- switch (child.nodeName) {
-
- case 'perspective':
- case 'orthographic':
-
- data.technique = child.nodeName;
- data.parameters = parseCameraParameters(child);
-
- break;
-
- }
- }
-
- return data;
- }
-
- function parseCameraParameters(xml) {
-
- var data = {};
-
- for (var i = 0; i < xml.childNodes.length; i++) {
-
- var child = xml.childNodes[i];
-
- switch (child.nodeName) {
-
- case 'xfov':
- case 'yfov':
- case 'xmag':
- case 'ymag':
- case 'znear':
- case 'zfar':
- case 'aspect_ratio':
- data[child.nodeName] = parseFloat(child.textContent);
- break;
-
- }
- }
-
- return data;
- }
-
- function buildCamera(data) {
-
- var camera;
-
- switch (data.optics.technique) {
-
- case 'perspective':
- camera = new THREE.PerspectiveCamera(data.optics.parameters.yfov, data.optics.parameters.aspect_ratio, data.optics.parameters.znear, data.optics.parameters.zfar);
- break;
-
- case 'orthographic':
- var ymag = data.optics.parameters.ymag;
- var xmag = data.optics.parameters.xmag;
- var aspectRatio = data.optics.parameters.aspect_ratio;
-
- xmag = xmag === undefined ? ymag * aspectRatio : xmag;
- ymag = ymag === undefined ? xmag / aspectRatio : ymag;
-
- xmag *= 0.5;
- ymag *= 0.5;
-
- camera = new THREE.OrthographicCamera(-xmag, xmag, ymag, -ymag, // left, right, top, bottom
- data.optics.parameters.znear, data.optics.parameters.zfar);
- break;
-
- default:
- camera = new THREE.PerspectiveCamera();
- break;
-
- }
-
- camera.name = data.name;
-
- return camera;
- }
-
- function getCamera(id) {
-
- var data = library.cameras[id];
-
- if (data !== undefined) {
-
- return getBuild(data, buildCamera);
- }
-
- console.warn('THREE.ColladaLoader: Couldn\'t find camera with ID:', id);
-
- return null;
- }
-
- // light
-
- function parseLight(xml) {
-
- var data = {};
-
- for (var i = 0, l = xml.childNodes.length; i < l; i++) {
-
- var child = xml.childNodes[i];
-
- if (child.nodeType !== 1) continue;
-
- switch (child.nodeName) {
-
- case 'technique_common':
- data = parseLightTechnique(child);
- break;
-
- }
- }
-
- library.lights[xml.getAttribute('id')] = data;
- }
-
- function parseLightTechnique(xml) {
-
- var data = {};
-
- for (var i = 0, l = xml.childNodes.length; i < l; i++) {
-
- var child = xml.childNodes[i];
-
- if (child.nodeType !== 1) continue;
-
- switch (child.nodeName) {
-
- case 'directional':
- case 'point':
- case 'spot':
- case 'ambient':
-
- data.technique = child.nodeName;
- data.parameters = parseLightParameters(child);
-
- }
- }
-
- return data;
- }
-
- function parseLightParameters(xml) {
-
- var data = {};
-
- for (var i = 0, l = xml.childNodes.length; i < l; i++) {
-
- var child = xml.childNodes[i];
-
- if (child.nodeType !== 1) continue;
-
- switch (child.nodeName) {
-
- case 'color':
- var array = parseFloats(child.textContent);
- data.color = new THREE.Color().fromArray(array);
- break;
-
- case 'falloff_angle':
- data.falloffAngle = parseFloat(child.textContent);
- break;
-
- case 'quadratic_attenuation':
- var f = parseFloat(child.textContent);
- data.distance = f ? Math.sqrt(1 / f) : 0;
- break;
-
- }
- }
-
- return data;
- }
-
- function buildLight(data) {
-
- var light;
-
- switch (data.technique) {
-
- case 'directional':
- light = new THREE.DirectionalLight();
- break;
-
- case 'point':
- light = new THREE.PointLight();
- break;
-
- case 'spot':
- light = new THREE.SpotLight();
- break;
-
- case 'ambient':
- light = new THREE.AmbientLight();
- break;
-
- }
-
- if (data.parameters.color) light.color.copy(data.parameters.color);
- if (data.parameters.distance) light.distance = data.parameters.distance;
-
- return light;
- }
-
- function getLight(id) {
-
- var data = library.lights[id];
-
- if (data !== undefined) {
-
- return getBuild(data, buildLight);
- }
-
- console.warn('THREE.ColladaLoader: Couldn\'t find light with ID:', id);
-
- return null;
- }
-
- // geometry
-
- function parseGeometry(xml) {
-
- var data = {
- name: xml.getAttribute('name'),
- sources: {},
- vertices: {},
- primitives: []
- };
-
- var mesh = getElementsByTagName(xml, 'mesh')[0];
-
- // the following tags inside geometry are not supported yet (see https://github.com/mrdoob/three.js/pull/12606): convex_mesh, spline, brep
- if (mesh === undefined) return;
-
- for (var i = 0; i < mesh.childNodes.length; i++) {
-
- var child = mesh.childNodes[i];
-
- if (child.nodeType !== 1) continue;
-
- var id = child.getAttribute('id');
-
- switch (child.nodeName) {
-
- case 'source':
- data.sources[id] = parseSource(child);
- break;
-
- case 'vertices':
- // data.sources[ id ] = data.sources[ parseId( getElementsByTagName( child, 'input' )[ 0 ].getAttribute( 'source' ) ) ];
- data.vertices = parseGeometryVertices(child);
- break;
-
- case 'polygons':
- console.warn('THREE.ColladaLoader: Unsupported primitive type: ', child.nodeName);
- break;
-
- case 'lines':
- case 'linestrips':
- case 'polylist':
- case 'triangles':
- data.primitives.push(parseGeometryPrimitive(child));
- break;
-
- default:
- console.log(child);
-
- }
- }
-
- library.geometries[xml.getAttribute('id')] = data;
- }
-
- function parseSource(xml) {
-
- var data = {
- array: [],
- stride: 3
- };
-
- for (var i = 0; i < xml.childNodes.length; i++) {
-
- var child = xml.childNodes[i];
-
- if (child.nodeType !== 1) continue;
-
- switch (child.nodeName) {
-
- case 'float_array':
- data.array = parseFloats(child.textContent);
- break;
-
- case 'Name_array':
- data.array = parseStrings(child.textContent);
- break;
-
- case 'technique_common':
- var accessor = getElementsByTagName(child, 'accessor')[0];
-
- if (accessor !== undefined) {
-
- data.stride = parseInt(accessor.getAttribute('stride'));
- }
- break;
-
- }
- }
-
- return data;
- }
-
- function parseGeometryVertices(xml) {
-
- var data = {};
-
- for (var i = 0; i < xml.childNodes.length; i++) {
-
- var child = xml.childNodes[i];
-
- if (child.nodeType !== 1) continue;
-
- data[child.getAttribute('semantic')] = parseId(child.getAttribute('source'));
- }
-
- return data;
- }
-
- function parseGeometryPrimitive(xml) {
-
- var primitive = {
- type: xml.nodeName,
- material: xml.getAttribute('material'),
- count: parseInt(xml.getAttribute('count')),
- inputs: {},
- stride: 0,
- hasUV: false
- };
-
- for (var i = 0, l = xml.childNodes.length; i < l; i++) {
-
- var child = xml.childNodes[i];
-
- if (child.nodeType !== 1) continue;
-
- switch (child.nodeName) {
-
- case 'input':
- var id = parseId(child.getAttribute('source'));
- var semantic = child.getAttribute('semantic');
- var offset = parseInt(child.getAttribute('offset'));
- var set = parseInt(child.getAttribute('set'));
- var inputname = set > 0 ? semantic + set : semantic;
- primitive.inputs[inputname] = { id: id, offset: offset };
- primitive.stride = Math.max(primitive.stride, offset + 1);
- if (semantic === 'TEXCOORD') primitive.hasUV = true;
- break;
-
- case 'vcount':
- primitive.vcount = parseInts(child.textContent);
- break;
-
- case 'p':
- primitive.p = parseInts(child.textContent);
- break;
-
- }
- }
-
- return primitive;
- }
-
- function groupPrimitives(primitives) {
-
- var build = {};
-
- for (var i = 0; i < primitives.length; i++) {
-
- var primitive = primitives[i];
-
- if (build[primitive.type] === undefined) build[primitive.type] = [];
-
- build[primitive.type].push(primitive);
- }
-
- return build;
- }
-
- function checkUVCoordinates(primitives) {
-
- var count = 0;
-
- for (var i = 0, l = primitives.length; i < l; i++) {
-
- var primitive = primitives[i];
-
- if (primitive.hasUV === true) {
-
- count++;
- }
- }
-
- if (count > 0 && count < primitives.length) {
-
- primitives.uvsNeedsFix = true;
- }
- }
-
- function buildGeometry(data) {
-
- var build = {};
-
- var sources = data.sources;
- var vertices = data.vertices;
- var primitives = data.primitives;
-
- if (primitives.length === 0) return {};
-
- // our goal is to create one buffer geometry for a single type of primitives
- // first, we group all primitives by their type
-
- var groupedPrimitives = groupPrimitives(primitives);
-
- for (var type in groupedPrimitives) {
-
- var primitiveType = groupedPrimitives[type];
-
- // second, ensure consistent uv coordinates for each type of primitives (polylist,triangles or lines)
-
- checkUVCoordinates(primitiveType);
-
- // third, create a buffer geometry for each type of primitives
-
- build[type] = buildGeometryType(primitiveType, sources, vertices);
- }
-
- return build;
- }
-
- function buildGeometryType(primitives, sources, vertices) {
-
- var build = {};
-
- var position = { array: [], stride: 0 };
- var normal = { array: [], stride: 0 };
- var uv = { array: [], stride: 0 };
- var uv2 = { array: [], stride: 0 };
- var color = { array: [], stride: 0 };
-
- var skinIndex = { array: [], stride: 4 };
- var skinWeight = { array: [], stride: 4 };
-
- var geometry = new THREE.BufferGeometry();
-
- var materialKeys = [];
-
- var start = 0;
-
- for (var p = 0; p < primitives.length; p++) {
-
- var primitive = primitives[p];
- var inputs = primitive.inputs;
-
- // groups
-
- var count = 0;
-
- switch (primitive.type) {
-
- case 'lines':
- case 'linestrips':
- count = primitive.count * 2;
- break;
-
- case 'triangles':
- count = primitive.count * 3;
- break;
-
- case 'polylist':
-
- for (var g = 0; g < primitive.count; g++) {
-
- var vc = primitive.vcount[g];
-
- switch (vc) {
-
- case 3:
- count += 3; // single triangle
- break;
-
- case 4:
- count += 6; // quad, subdivided into two triangles
- break;
-
- default:
- count += (vc - 2) * 3; // polylist with more than four vertices
- break;
-
- }
- }
-
- break;
-
- default:
- console.warn('THREE.ColladaLoader: Unknow primitive type:', primitive.type);
-
- }
-
- geometry.addGroup(start, count, p);
- start += count;
-
- // material
-
- if (primitive.material) {
-
- materialKeys.push(primitive.material);
- }
-
- // geometry data
-
- for (var name in inputs) {
-
- var input = inputs[name];
-
- switch (name) {
-
- case 'VERTEX':
- for (var key in vertices) {
-
- var id = vertices[key];
-
- switch (key) {
-
- case 'POSITION':
- var prevLength = position.array.length;
- buildGeometryData(primitive, sources[id], input.offset, position.array);
- position.stride = sources[id].stride;
-
- if (sources.skinWeights && sources.skinIndices) {
-
- buildGeometryData(primitive, sources.skinIndices, input.offset, skinIndex.array);
- buildGeometryData(primitive, sources.skinWeights, input.offset, skinWeight.array);
- }
-
- // see #3803
-
- if (primitive.hasUV === false && primitives.uvsNeedsFix === true) {
-
- var count = (position.array.length - prevLength) / position.stride;
-
- for (var i = 0; i < count; i++) {
-
- // fill missing uv coordinates
-
- uv.array.push(0, 0);
- }
- }
- break;
-
- case 'NORMAL':
- buildGeometryData(primitive, sources[id], input.offset, normal.array);
- normal.stride = sources[id].stride;
- break;
-
- case 'COLOR':
- buildGeometryData(primitive, sources[id], input.offset, color.array);
- color.stride = sources[id].stride;
- break;
-
- case 'TEXCOORD':
- buildGeometryData(primitive, sources[id], input.offset, uv.array);
- uv.stride = sources[id].stride;
- break;
-
- case 'TEXCOORD1':
- buildGeometryData(primitive, sources[id], input.offset, uv2.array);
- uv.stride = sources[id].stride;
- break;
-
- default:
- console.warn('THREE.ColladaLoader: Semantic "%s" not handled in geometry build process.', key);
-
- }
- }
- break;
-
- case 'NORMAL':
- buildGeometryData(primitive, sources[input.id], input.offset, normal.array);
- normal.stride = sources[input.id].stride;
- break;
-
- case 'COLOR':
- buildGeometryData(primitive, sources[input.id], input.offset, color.array);
- color.stride = sources[input.id].stride;
- break;
-
- case 'TEXCOORD':
- buildGeometryData(primitive, sources[input.id], input.offset, uv.array);
- uv.stride = sources[input.id].stride;
- break;
-
- case 'TEXCOORD1':
- buildGeometryData(primitive, sources[input.id], input.offset, uv2.array);
- uv2.stride = sources[input.id].stride;
- break;
-
- }
- }
- }
-
- // build geometry
-
- if (position.array.length > 0) geometry.addAttribute('position', new THREE.Float32BufferAttribute(position.array, position.stride));
- if (normal.array.length > 0) geometry.addAttribute('normal', new THREE.Float32BufferAttribute(normal.array, normal.stride));
- if (color.array.length > 0) geometry.addAttribute('color', new THREE.Float32BufferAttribute(color.array, color.stride));
- if (uv.array.length > 0) geometry.addAttribute('uv', new THREE.Float32BufferAttribute(uv.array, uv.stride));
- if (uv2.array.length > 0) geometry.addAttribute('uv2', new THREE.Float32BufferAttribute(uv2.array, uv2.stride));
-
- if (skinIndex.array.length > 0) geometry.addAttribute('skinIndex', new THREE.Float32BufferAttribute(skinIndex.array, skinIndex.stride));
- if (skinWeight.array.length > 0) geometry.addAttribute('skinWeight', new THREE.Float32BufferAttribute(skinWeight.array, skinWeight.stride));
-
- build.data = geometry;
- build.type = primitives[0].type;
- build.materialKeys = materialKeys;
-
- return build;
- }
-
- function buildGeometryData(primitive, source, offset, array) {
-
- var indices = primitive.p;
- var stride = primitive.stride;
- var vcount = primitive.vcount;
-
- function pushVector(i) {
-
- var index = indices[i + offset] * sourceStride;
- var length = index + sourceStride;
-
- for (; index < length; index++) {
-
- array.push(sourceArray[index]);
- }
- }
-
- var sourceArray = source.array;
- var sourceStride = source.stride;
-
- if (primitive.vcount !== undefined) {
-
- var index = 0;
-
- for (var i = 0, l = vcount.length; i < l; i++) {
-
- var count = vcount[i];
-
- if (count === 4) {
-
- var a = index + stride * 0;
- var b = index + stride * 1;
- var c = index + stride * 2;
- var d = index + stride * 3;
-
- pushVector(a);pushVector(b);pushVector(d);
- pushVector(b);pushVector(c);pushVector(d);
- } else if (count === 3) {
-
- var a = index + stride * 0;
- var b = index + stride * 1;
- var c = index + stride * 2;
-
- pushVector(a);pushVector(b);pushVector(c);
- } else if (count > 4) {
-
- for (var k = 1, kl = count - 2; k <= kl; k++) {
-
- var a = index + stride * 0;
- var b = index + stride * k;
- var c = index + stride * (k + 1);
-
- pushVector(a);pushVector(b);pushVector(c);
- }
- }
-
- index += stride * count;
- }
- } else {
-
- for (var i = 0, l = indices.length; i < l; i += stride) {
-
- pushVector(i);
- }
- }
- }
-
- function getGeometry(id) {
-
- return getBuild(library.geometries[id], buildGeometry);
- }
-
- // kinematics
-
- function parseKinematicsModel(xml) {
-
- var data = {
- name: xml.getAttribute('name') || '',
- joints: {},
- links: []
- };
-
- for (var i = 0; i < xml.childNodes.length; i++) {
-
- var child = xml.childNodes[i];
-
- if (child.nodeType !== 1) continue;
-
- switch (child.nodeName) {
-
- case 'technique_common':
- parseKinematicsTechniqueCommon(child, data);
- break;
-
- }
- }
-
- library.kinematicsModels[xml.getAttribute('id')] = data;
- }
-
- function buildKinematicsModel(data) {
-
- if (data.build !== undefined) return data.build;
-
- return data;
- }
-
- function getKinematicsModel(id) {
-
- return getBuild(library.kinematicsModels[id], buildKinematicsModel);
- }
-
- function parseKinematicsTechniqueCommon(xml, data) {
-
- for (var i = 0; i < xml.childNodes.length; i++) {
-
- var child = xml.childNodes[i];
-
- if (child.nodeType !== 1) continue;
-
- switch (child.nodeName) {
-
- case 'joint':
- data.joints[child.getAttribute('sid')] = parseKinematicsJoint(child);
- break;
-
- case 'link':
- data.links.push(parseKinematicsLink(child));
- break;
-
- }
- }
- }
-
- function parseKinematicsJoint(xml) {
-
- var data;
-
- for (var i = 0; i < xml.childNodes.length; i++) {
-
- var child = xml.childNodes[i];
-
- if (child.nodeType !== 1) continue;
-
- switch (child.nodeName) {
-
- case 'prismatic':
- case 'revolute':
- data = parseKinematicsJointParameter(child);
- break;
-
- }
- }
-
- return data;
- }
-
- function parseKinematicsJointParameter(xml, data) {
-
- var data = {
- sid: xml.getAttribute('sid'),
- name: xml.getAttribute('name') || '',
- axis: new THREE.Vector3(),
- limits: {
- min: 0,
- max: 0
- },
- type: xml.nodeName,
- static: false,
- zeroPosition: 0,
- middlePosition: 0
- };
-
- for (var i = 0; i < xml.childNodes.length; i++) {
-
- var child = xml.childNodes[i];
-
- if (child.nodeType !== 1) continue;
-
- switch (child.nodeName) {
-
- case 'axis':
- var array = parseFloats(child.textContent);
- data.axis.fromArray(array);
- break;
- case 'limits':
- var max = child.getElementsByTagName('max')[0];
- var min = child.getElementsByTagName('min')[0];
-
- data.limits.max = parseFloat(max.textContent);
- data.limits.min = parseFloat(min.textContent);
- break;
-
- }
- }
-
- // if min is equal to or greater than max, consider the joint static
-
- if (data.limits.min >= data.limits.max) {
-
- data.static = true;
- }
-
- // calculate middle position
-
- data.middlePosition = (data.limits.min + data.limits.max) / 2.0;
-
- return data;
- }
-
- function parseKinematicsLink(xml) {
-
- var data = {
- sid: xml.getAttribute('sid'),
- name: xml.getAttribute('name') || '',
- attachments: [],
- transforms: []
- };
-
- for (var i = 0; i < xml.childNodes.length; i++) {
-
- var child = xml.childNodes[i];
-
- if (child.nodeType !== 1) continue;
-
- switch (child.nodeName) {
-
- case 'attachment_full':
- data.attachments.push(parseKinematicsAttachment(child));
- break;
-
- case 'matrix':
- case 'translate':
- case 'rotate':
- data.transforms.push(parseKinematicsTransform(child));
- break;
-
- }
- }
-
- return data;
- }
-
- function parseKinematicsAttachment(xml) {
-
- var data = {
- joint: xml.getAttribute('joint').split('/').pop(),
- transforms: [],
- links: []
- };
-
- for (var i = 0; i < xml.childNodes.length; i++) {
-
- var child = xml.childNodes[i];
-
- if (child.nodeType !== 1) continue;
-
- switch (child.nodeName) {
-
- case 'link':
- data.links.push(parseKinematicsLink(child));
- break;
-
- case 'matrix':
- case 'translate':
- case 'rotate':
- data.transforms.push(parseKinematicsTransform(child));
- break;
-
- }
- }
-
- return data;
- }
-
- function parseKinematicsTransform(xml) {
-
- var data = {
- type: xml.nodeName
- };
-
- var array = parseFloats(xml.textContent);
-
- switch (data.type) {
-
- case 'matrix':
- data.obj = new THREE.Matrix4();
- data.obj.fromArray(array).transpose();
- break;
-
- case 'translate':
- data.obj = new THREE.Vector3();
- data.obj.fromArray(array);
- break;
-
- case 'rotate':
- data.obj = new THREE.Vector3();
- data.obj.fromArray(array);
- data.angle = THREE.Math.degToRad(array[3]);
- break;
-
- }
-
- return data;
- }
-
- // physics
-
- function parsePhysicsModel(xml) {
-
- var data = {
- name: xml.getAttribute('name') || '',
- rigidBodies: {}
- };
-
- for (var i = 0; i < xml.childNodes.length; i++) {
-
- var child = xml.childNodes[i];
-
- if (child.nodeType !== 1) continue;
-
- switch (child.nodeName) {
-
- case 'rigid_body':
- data.rigidBodies[child.getAttribute('name')] = {};
- parsePhysicsRigidBody(child, data.rigidBodies[child.getAttribute('name')]);
- break;
-
- }
- }
-
- library.physicsModels[xml.getAttribute('id')] = data;
- }
-
- function parsePhysicsRigidBody(xml, data) {
-
- for (var i = 0; i < xml.childNodes.length; i++) {
-
- var child = xml.childNodes[i];
-
- if (child.nodeType !== 1) continue;
-
- switch (child.nodeName) {
-
- case 'technique_common':
- parsePhysicsTechniqueCommon(child, data);
- break;
-
- }
- }
- }
-
- function parsePhysicsTechniqueCommon(xml, data) {
-
- for (var i = 0; i < xml.childNodes.length; i++) {
-
- var child = xml.childNodes[i];
-
- if (child.nodeType !== 1) continue;
-
- switch (child.nodeName) {
-
- case 'inertia':
- data.inertia = parseFloats(child.textContent);
- break;
-
- case 'mass':
- data.mass = parseFloats(child.textContent)[0];
- break;
-
- }
- }
- }
-
- // scene
-
- function parseKinematicsScene(xml) {
-
- var data = {
- bindJointAxis: []
- };
-
- for (var i = 0; i < xml.childNodes.length; i++) {
-
- var child = xml.childNodes[i];
-
- if (child.nodeType !== 1) continue;
-
- switch (child.nodeName) {
-
- case 'bind_joint_axis':
- data.bindJointAxis.push(parseKinematicsBindJointAxis(child));
- break;
-
- }
- }
-
- library.kinematicsScenes[parseId(xml.getAttribute('url'))] = data;
- }
-
- function parseKinematicsBindJointAxis(xml) {
-
- var data = {
- target: xml.getAttribute('target').split('/').pop()
- };
-
- for (var i = 0; i < xml.childNodes.length; i++) {
-
- var child = xml.childNodes[i];
-
- if (child.nodeType !== 1) continue;
-
- switch (child.nodeName) {
-
- case 'axis':
- var param = child.getElementsByTagName('param')[0];
- data.axis = param.textContent;
- var tmpJointIndex = data.axis.split('inst_').pop().split('axis')[0];
- data.jointIndex = tmpJointIndex.substr(0, tmpJointIndex.length - 1);
- break;
-
- }
- }
-
- return data;
- }
-
- function buildKinematicsScene(data) {
-
- if (data.build !== undefined) return data.build;
-
- return data;
- }
-
- function getKinematicsScene(id) {
-
- return getBuild(library.kinematicsScenes[id], buildKinematicsScene);
- }
-
- function setupKinematics() {
-
- var kinematicsModelId = Object.keys(library.kinematicsModels)[0];
- var kinematicsSceneId = Object.keys(library.kinematicsScenes)[0];
- var visualSceneId = Object.keys(library.visualScenes)[0];
-
- if (kinematicsModelId === undefined || kinematicsSceneId === undefined) return;
-
- var kinematicsModel = getKinematicsModel(kinematicsModelId);
- var kinematicsScene = getKinematicsScene(kinematicsSceneId);
- var visualScene = getVisualScene(visualSceneId);
-
- var bindJointAxis = kinematicsScene.bindJointAxis;
- var jointMap = {};
-
- for (var i = 0, l = bindJointAxis.length; i < l; i++) {
-
- var axis = bindJointAxis[i];
-
- // the result of the following query is an element of type 'translate', 'rotate','scale' or 'matrix'
-
- var targetElement = collada.querySelector('[sid="' + axis.target + '"]');
-
- if (targetElement) {
-
- // get the parent of the transfrom element
-
- var parentVisualElement = targetElement.parentElement;
-
- // connect the joint of the kinematics model with the element in the visual scene
-
- connect(axis.jointIndex, parentVisualElement);
- }
- }
-
- function connect(jointIndex, visualElement) {
-
- var visualElementName = visualElement.getAttribute('name');
- var joint = kinematicsModel.joints[jointIndex];
-
- visualScene.traverse(function (object) {
-
- if (object.name === visualElementName) {
-
- jointMap[jointIndex] = {
- object: object,
- transforms: buildTransformList(visualElement),
- joint: joint,
- position: joint.zeroPosition
- };
- }
- });
- }
-
- var m0 = new THREE.Matrix4();
-
- kinematics = {
-
- joints: kinematicsModel && kinematicsModel.joints,
-
- getJointValue: function getJointValue(jointIndex) {
-
- var jointData = jointMap[jointIndex];
-
- if (jointData) {
-
- return jointData.position;
- } else {
-
- console.warn('THREE.ColladaLoader: Joint ' + jointIndex + ' doesn\'t exist.');
- }
- },
-
- setJointValue: function setJointValue(jointIndex, value) {
-
- var jointData = jointMap[jointIndex];
-
- if (jointData) {
-
- var joint = jointData.joint;
-
- if (value > joint.limits.max || value < joint.limits.min) {
-
- console.warn('THREE.ColladaLoader: Joint ' + jointIndex + ' value ' + value + ' outside of limits (min: ' + joint.limits.min + ', max: ' + joint.limits.max + ').');
- } else if (joint.static) {
-
- console.warn('THREE.ColladaLoader: Joint ' + jointIndex + ' is static.');
- } else {
-
- var object = jointData.object;
- var axis = joint.axis;
- var transforms = jointData.transforms;
-
- matrix.identity();
-
- // each update, we have to apply all transforms in the correct order
-
- for (var i = 0; i < transforms.length; i++) {
-
- var transform = transforms[i];
-
- // if there is a connection of the transform node with a joint, apply the joint value
-
- if (transform.sid && transform.sid.indexOf(jointIndex) !== -1) {
-
- switch (joint.type) {
-
- case 'revolute':
- matrix.multiply(m0.makeRotationAxis(axis, THREE.Math.degToRad(value)));
- break;
-
- case 'prismatic':
- matrix.multiply(m0.makeTranslation(axis.x * value, axis.y * value, axis.z * value));
- break;
-
- default:
- console.warn('THREE.ColladaLoader: Unknown joint type: ' + joint.type);
- break;
-
- }
- } else {
-
- switch (transform.type) {
-
- case 'matrix':
- matrix.multiply(transform.obj);
- break;
-
- case 'translate':
- matrix.multiply(m0.makeTranslation(transform.obj.x, transform.obj.y, transform.obj.z));
- break;
-
- case 'scale':
- matrix.scale(transform.obj);
- break;
-
- case 'rotate':
- matrix.multiply(m0.makeRotationAxis(transform.obj, transform.angle));
- break;
-
- }
- }
- }
-
- object.matrix.copy(matrix);
- object.matrix.decompose(object.position, object.quaternion, object.scale);
-
- jointMap[jointIndex].position = value;
- }
- } else {
-
- console.log('THREE.ColladaLoader: ' + jointIndex + ' does not exist.');
- }
- }
-
- };
- }
-
- function buildTransformList(node) {
-
- var transforms = [];
-
- var xml = collada.querySelector('[id="' + node.id + '"]');
-
- for (var i = 0; i < xml.childNodes.length; i++) {
-
- var child = xml.childNodes[i];
-
- if (child.nodeType !== 1) continue;
-
- switch (child.nodeName) {
-
- case 'matrix':
- var array = parseFloats(child.textContent);
- var matrix = new THREE.Matrix4().fromArray(array).transpose();
- transforms.push({
- sid: child.getAttribute('sid'),
- type: child.nodeName,
- obj: matrix
- });
- break;
-
- case 'translate':
- case 'scale':
- var array = parseFloats(child.textContent);
- var vector = new THREE.Vector3().fromArray(array);
- transforms.push({
- sid: child.getAttribute('sid'),
- type: child.nodeName,
- obj: vector
- });
- break;
-
- case 'rotate':
- var array = parseFloats(child.textContent);
- var vector = new THREE.Vector3().fromArray(array);
- var angle = THREE.Math.degToRad(array[3]);
- transforms.push({
- sid: child.getAttribute('sid'),
- type: child.nodeName,
- obj: vector,
- angle: angle
- });
- break;
-
- }
- }
-
- return transforms;
- }
-
- // nodes
-
- function prepareNodes(xml) {
-
- var elements = xml.getElementsByTagName('node');
-
- // ensure all node elements have id attributes
-
- for (var i = 0; i < elements.length; i++) {
-
- var element = elements[i];
-
- if (element.hasAttribute('id') === false) {
-
- element.setAttribute('id', generateId());
- }
- }
- }
-
- var matrix = new THREE.Matrix4();
- var vector = new THREE.Vector3();
-
- function parseNode(xml) {
-
- var data = {
- name: xml.getAttribute('name') || '',
- type: xml.getAttribute('type'),
- id: xml.getAttribute('id'),
- sid: xml.getAttribute('sid'),
- matrix: new THREE.Matrix4(),
- nodes: [],
- instanceCameras: [],
- instanceControllers: [],
- instanceLights: [],
- instanceGeometries: [],
- instanceNodes: [],
- transforms: {}
- };
-
- for (var i = 0; i < xml.childNodes.length; i++) {
-
- var child = xml.childNodes[i];
-
- if (child.nodeType !== 1) continue;
-
- switch (child.nodeName) {
-
- case 'node':
- data.nodes.push(child.getAttribute('id'));
- parseNode(child);
- break;
-
- case 'instance_camera':
- data.instanceCameras.push(parseId(child.getAttribute('url')));
- break;
-
- case 'instance_controller':
- data.instanceControllers.push(parseNodeInstance(child));
- break;
-
- case 'instance_light':
- data.instanceLights.push(parseId(child.getAttribute('url')));
- break;
-
- case 'instance_geometry':
- data.instanceGeometries.push(parseNodeInstance(child));
- break;
-
- case 'instance_node':
- data.instanceNodes.push(parseId(child.getAttribute('url')));
- break;
-
- case 'matrix':
- var array = parseFloats(child.textContent);
- data.matrix.multiply(matrix.fromArray(array).transpose());
- data.transforms[child.getAttribute('sid')] = child.nodeName;
- break;
-
- case 'translate':
- var array = parseFloats(child.textContent);
- vector.fromArray(array);
- data.matrix.multiply(matrix.makeTranslation(vector.x, vector.y, vector.z));
- data.transforms[child.getAttribute('sid')] = child.nodeName;
- break;
-
- case 'rotate':
- var array = parseFloats(child.textContent);
- var angle = THREE.Math.degToRad(array[3]);
- data.matrix.multiply(matrix.makeRotationAxis(vector.fromArray(array), angle));
- data.transforms[child.getAttribute('sid')] = child.nodeName;
- break;
-
- case 'scale':
- var array = parseFloats(child.textContent);
- data.matrix.scale(vector.fromArray(array));
- data.transforms[child.getAttribute('sid')] = child.nodeName;
- break;
-
- case 'extra':
- break;
-
- default:
- console.log(child);
-
- }
- }
-
- if (hasNode(data.id)) {
-
- console.warn('THREE.ColladaLoader: There is already a node with ID %s. Exclude current node from further processing.', data.id);
- } else {
-
- library.nodes[data.id] = data;
- }
-
- return data;
- }
-
- function parseNodeInstance(xml) {
-
- var data = {
- id: parseId(xml.getAttribute('url')),
- materials: {},
- skeletons: []
- };
-
- for (var i = 0; i < xml.childNodes.length; i++) {
-
- var child = xml.childNodes[i];
-
- switch (child.nodeName) {
-
- case 'bind_material':
- var instances = child.getElementsByTagName('instance_material');
-
- for (var j = 0; j < instances.length; j++) {
-
- var instance = instances[j];
- var symbol = instance.getAttribute('symbol');
- var target = instance.getAttribute('target');
-
- data.materials[symbol] = parseId(target);
- }
-
- break;
-
- case 'skeleton':
- data.skeletons.push(parseId(child.textContent));
- break;
-
- default:
- break;
-
- }
- }
-
- return data;
- }
-
- function buildSkeleton(skeletons, joints) {
-
- var boneData = [];
- var sortedBoneData = [];
-
- var i, j, data;
-
- // a skeleton can have multiple root bones. collada expresses this
- // situtation with multiple "skeleton" tags per controller instance
-
- for (i = 0; i < skeletons.length; i++) {
-
- var skeleton = skeletons[i];
-
- var root;
-
- if (hasNode(skeleton)) {
-
- root = getNode(skeleton);
- buildBoneHierarchy(root, joints, boneData);
- } else if (hasVisualScene(skeleton)) {
-
- // handle case where the skeleton refers to the visual scene (#13335)
-
- var visualScene = library.visualScenes[skeleton];
- var children = visualScene.children;
-
- for (var j = 0; j < children.length; j++) {
-
- var child = children[j];
-
- if (child.type === 'JOINT') {
-
- var root = getNode(child.id);
- buildBoneHierarchy(root, joints, boneData);
- }
- }
- } else {
-
- console.error('THREE.ColladaLoader: Unable to find root bone of skeleton with ID:', skeleton);
- }
- }
-
- // sort bone data (the order is defined in the corresponding controller)
-
- for (i = 0; i < joints.length; i++) {
-
- for (j = 0; j < boneData.length; j++) {
-
- data = boneData[j];
-
- if (data.bone.name === joints[i].name) {
-
- sortedBoneData[i] = data;
- data.processed = true;
- break;
- }
- }
- }
-
- // add unprocessed bone data at the end of the list
-
- for (i = 0; i < boneData.length; i++) {
-
- data = boneData[i];
-
- if (data.processed === false) {
-
- sortedBoneData.push(data);
- data.processed = true;
- }
- }
-
- // setup arrays for skeleton creation
-
- var bones = [];
- var boneInverses = [];
-
- for (i = 0; i < sortedBoneData.length; i++) {
-
- data = sortedBoneData[i];
-
- bones.push(data.bone);
- boneInverses.push(data.boneInverse);
- }
-
- return new THREE.Skeleton(bones, boneInverses);
- }
-
- function buildBoneHierarchy(root, joints, boneData) {
-
- // setup bone data from visual scene
-
- root.traverse(function (object) {
-
- if (object.isBone === true) {
-
- var boneInverse;
-
- // retrieve the boneInverse from the controller data
-
- for (var i = 0; i < joints.length; i++) {
-
- var joint = joints[i];
-
- if (joint.name === object.name) {
-
- boneInverse = joint.boneInverse;
- break;
- }
- }
-
- if (boneInverse === undefined) {
-
- // Unfortunately, there can be joints in the visual scene that are not part of the
- // corresponding controller. In this case, we have to create a dummy boneInverse matrix
- // for the respective bone. This bone won't affect any vertices, because there are no skin indices
- // and weights defined for it. But we still have to add the bone to the sorted bone list in order to
- // ensure a correct animation of the model.
-
- boneInverse = new THREE.Matrix4();
- }
-
- boneData.push({ bone: object, boneInverse: boneInverse, processed: false });
- }
- });
- }
-
- function buildNode(data) {
-
- var objects = [];
-
- var matrix = data.matrix;
- var nodes = data.nodes;
- var type = data.type;
- var instanceCameras = data.instanceCameras;
- var instanceControllers = data.instanceControllers;
- var instanceLights = data.instanceLights;
- var instanceGeometries = data.instanceGeometries;
- var instanceNodes = data.instanceNodes;
-
- // nodes
-
- for (var i = 0, l = nodes.length; i < l; i++) {
-
- objects.push(getNode(nodes[i]));
- }
-
- // instance cameras
-
- for (var i = 0, l = instanceCameras.length; i < l; i++) {
-
- var instanceCamera = getCamera(instanceCameras[i]);
-
- if (instanceCamera !== null) {
-
- objects.push(instanceCamera.clone());
- }
- }
-
- // instance controllers
-
- for (var i = 0, l = instanceControllers.length; i < l; i++) {
-
- var instance = instanceControllers[i];
- var controller = getController(instance.id);
- var geometries = getGeometry(controller.id);
- var newObjects = buildObjects(geometries, instance.materials);
-
- var skeletons = instance.skeletons;
- var joints = controller.skin.joints;
-
- var skeleton = buildSkeleton(skeletons, joints);
-
- for (var j = 0, jl = newObjects.length; j < jl; j++) {
-
- var object = newObjects[j];
-
- if (object.isSkinnedMesh) {
-
- object.bind(skeleton, controller.skin.bindMatrix);
- object.normalizeSkinWeights();
- }
-
- objects.push(object);
- }
- }
-
- // instance lights
-
- for (var i = 0, l = instanceLights.length; i < l; i++) {
-
- var instanceLight = getLight(instanceLights[i]);
-
- if (instanceLight !== null) {
-
- objects.push(instanceLight.clone());
- }
- }
-
- // instance geometries
-
- for (var i = 0, l = instanceGeometries.length; i < l; i++) {
-
- var instance = instanceGeometries[i];
-
- // a single geometry instance in collada can lead to multiple object3Ds.
- // this is the case when primitives are combined like triangles and lines
-
- var geometries = getGeometry(instance.id);
- var newObjects = buildObjects(geometries, instance.materials);
-
- for (var j = 0, jl = newObjects.length; j < jl; j++) {
-
- objects.push(newObjects[j]);
- }
- }
-
- // instance nodes
-
- for (var i = 0, l = instanceNodes.length; i < l; i++) {
-
- objects.push(getNode(instanceNodes[i]).clone());
- }
-
- var object;
-
- if (nodes.length === 0 && objects.length === 1) {
-
- object = objects[0];
- } else {
-
- object = type === 'JOINT' ? new THREE.Bone() : new THREE.Group();
-
- for (var i = 0; i < objects.length; i++) {
-
- object.add(objects[i]);
- }
- }
-
- if (object.name === '') {
-
- object.name = type === 'JOINT' ? data.sid : data.name;
- }
-
- object.matrix.copy(matrix);
- object.matrix.decompose(object.position, object.quaternion, object.scale);
-
- return object;
- }
-
- var fallbackMaterial = new THREE.MeshBasicMaterial({ color: 0xff00ff });
-
- function resolveMaterialBinding(keys, instanceMaterials) {
-
- var materials = [];
-
- for (var i = 0, l = keys.length; i < l; i++) {
-
- var id = instanceMaterials[keys[i]];
-
- if (id === undefined) {
-
- console.warn('THREE.ColladaLoader: Material with key %s not found. Apply fallback material.', keys[i]);
- materials.push(fallbackMaterial);
- } else {
-
- materials.push(getMaterial(id));
- }
- }
-
- return materials;
- }
-
- function buildObjects(geometries, instanceMaterials) {
-
- var objects = [];
-
- for (var type in geometries) {
-
- var geometry = geometries[type];
-
- var materials = resolveMaterialBinding(geometry.materialKeys, instanceMaterials);
-
- // handle case if no materials are defined
-
- if (materials.length === 0) {
-
- if (type === 'lines' || type === 'linestrips') {
-
- materials.push(new THREE.LineBasicMaterial());
- } else {
-
- materials.push(new THREE.MeshPhongMaterial());
- }
- }
-
- // regard skinning
-
- var skinning = geometry.data.attributes.skinIndex !== undefined;
-
- if (skinning) {
-
- for (var i = 0, l = materials.length; i < l; i++) {
-
- materials[i].skinning = true;
- }
- }
-
- // choose between a single or multi materials (material array)
-
- var material = materials.length === 1 ? materials[0] : materials;
-
- // now create a specific 3D object
-
- var object;
-
- switch (type) {
-
- case 'lines':
- object = new THREE.LineSegments(geometry.data, material);
- break;
-
- case 'linestrips':
- object = new THREE.Line(geometry.data, material);
- break;
-
- case 'triangles':
- case 'polylist':
- if (skinning) {
-
- object = new THREE.SkinnedMesh(geometry.data, material);
- } else {
-
- object = new THREE.Mesh(geometry.data, material);
- }
- break;
-
- }
-
- objects.push(object);
- }
-
- return objects;
- }
-
- function hasNode(id) {
-
- return library.nodes[id] !== undefined;
- }
-
- function getNode(id) {
-
- return getBuild(library.nodes[id], buildNode);
- }
-
- // visual scenes
-
- function parseVisualScene(xml) {
-
- var data = {
- name: xml.getAttribute('name'),
- children: []
- };
-
- prepareNodes(xml);
-
- var elements = getElementsByTagName(xml, 'node');
-
- for (var i = 0; i < elements.length; i++) {
-
- data.children.push(parseNode(elements[i]));
- }
-
- library.visualScenes[xml.getAttribute('id')] = data;
- }
-
- function buildVisualScene(data) {
-
- var group = new THREE.Group();
- group.name = data.name;
-
- var children = data.children;
-
- for (var i = 0; i < children.length; i++) {
-
- var child = children[i];
-
- group.add(getNode(child.id));
- }
-
- return group;
- }
-
- function hasVisualScene(id) {
-
- return library.visualScenes[id] !== undefined;
- }
-
- function getVisualScene(id) {
-
- return getBuild(library.visualScenes[id], buildVisualScene);
- }
-
- // scenes
-
- function parseScene(xml) {
-
- var instance = getElementsByTagName(xml, 'instance_visual_scene')[0];
- return getVisualScene(parseId(instance.getAttribute('url')));
- }
-
- function setupAnimations() {
-
- var clips = library.clips;
-
- if (isEmpty(clips) === true) {
-
- if (isEmpty(library.animations) === false) {
-
- // if there are animations but no clips, we create a default clip for playback
-
- var tracks = [];
-
- for (var id in library.animations) {
-
- var animationTracks = getAnimation(id);
-
- for (var i = 0, l = animationTracks.length; i < l; i++) {
-
- tracks.push(animationTracks[i]);
- }
- }
-
- animations.push(new THREE.AnimationClip('default', -1, tracks));
- }
- } else {
-
- for (var id in clips) {
-
- animations.push(getAnimationClip(id));
- }
- }
- }
-
- if (text.length === 0) {
-
- return { scene: new THREE.Scene() };
- }
-
- var xml = new DOMParser().parseFromString(text, 'application/xml');
-
- var collada = getElementsByTagName(xml, 'COLLADA')[0];
-
- // metadata
-
- var version = collada.getAttribute('version');
- console.log('THREE.ColladaLoader: File version', version);
-
- var asset = parseAsset(getElementsByTagName(collada, 'asset')[0]);
- var textureLoader = new THREE.TextureLoader(this.manager);
- textureLoader.setPath(this.resourcePath || path).setCrossOrigin(this.crossOrigin);
-
- var tgaLoader;
-
- if (THREE.TGALoader) {
-
- tgaLoader = new THREE.TGALoader(this.manager);
- tgaLoader.setPath(this.resourcePath || path);
- }
-
- //
-
- var animations = [];
- var kinematics = {};
- var count = 0;
-
- //
-
- var library = {
- animations: {},
- clips: {},
- controllers: {},
- images: {},
- effects: {},
- materials: {},
- cameras: {},
- lights: {},
- geometries: {},
- nodes: {},
- visualScenes: {},
- kinematicsModels: {},
- physicsModels: {},
- kinematicsScenes: {}
- };
-
- parseLibrary(collada, 'library_animations', 'animation', parseAnimation);
- parseLibrary(collada, 'library_animation_clips', 'animation_clip', parseAnimationClip);
- parseLibrary(collada, 'library_controllers', 'controller', parseController);
- parseLibrary(collada, 'library_images', 'image', parseImage);
- parseLibrary(collada, 'library_effects', 'effect', parseEffect);
- parseLibrary(collada, 'library_materials', 'material', parseMaterial);
- parseLibrary(collada, 'library_cameras', 'camera', parseCamera);
- parseLibrary(collada, 'library_lights', 'light', parseLight);
- parseLibrary(collada, 'library_geometries', 'geometry', parseGeometry);
- parseLibrary(collada, 'library_nodes', 'node', parseNode);
- parseLibrary(collada, 'library_visual_scenes', 'visual_scene', parseVisualScene);
- parseLibrary(collada, 'library_kinematics_models', 'kinematics_model', parseKinematicsModel);
- parseLibrary(collada, 'library_physics_models', 'physics_model', parsePhysicsModel);
- parseLibrary(collada, 'scene', 'instance_kinematics_scene', parseKinematicsScene);
-
- buildLibrary(library.animations, buildAnimation);
- buildLibrary(library.clips, buildAnimationClip);
- buildLibrary(library.controllers, buildController);
- buildLibrary(library.images, buildImage);
- buildLibrary(library.effects, buildEffect);
- buildLibrary(library.materials, buildMaterial);
- buildLibrary(library.cameras, buildCamera);
- buildLibrary(library.lights, buildLight);
- buildLibrary(library.geometries, buildGeometry);
- buildLibrary(library.visualScenes, buildVisualScene);
-
- setupAnimations();
- setupKinematics();
-
- var scene = parseScene(getElementsByTagName(collada, 'scene')[0]);
-
- if (asset.upAxis === 'Z_UP') {
-
- scene.quaternion.setFromEuler(new THREE.Euler(-Math.PI / 2, 0, 0));
- }
-
- scene.scale.multiplyScalar(asset.unit);
-
- return {
- animations: animations,
- kinematics: kinematics,
- library: library,
- scene: scene
- };
- }
-
-};
-
-},{}],4:[function(require,module,exports){
-'use strict';
-
-var _typeof2 = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
-
-var _typeof = typeof Symbol === "function" && _typeof2(Symbol.iterator) === "symbol" ? function (obj) {
- return typeof obj === "undefined" ? "undefined" : _typeof2(obj);
-} : function (obj) {
- return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj === "undefined" ? "undefined" : _typeof2(obj);
-};
-
-/**
- * @author Kyle-Larson https://github.com/Kyle-Larson
- * @author Takahiro https://github.com/takahirox
- * @author Lewy Blue https://github.com/looeee
- *
- * Loader loads FBX file and generates Group representing FBX scene.
- * Requires FBX file to be >= 7.0 and in ASCII or >= 6400 in Binary format
- * Versions lower than this may load but will probably have errors
- *
- * Needs Support:
- * Morph normals / blend shape normals
- *
- * FBX format references:
- * https://wiki.blender.org/index.php/User:Mont29/Foundation/FBX_File_Structure
- * http://help.autodesk.com/view/FBX/2017/ENU/?guid=__cpp_ref_index_html (C++ SDK reference)
- *
- * Binary format specification:
- * https://code.blender.org/2013/08/fbx-binary-file-format-specification/
- */
-
-module.exports = THREE.FBXLoader = function () {
-
- var fbxTree;
- var connections;
- var sceneGraph;
-
- function FBXLoader(manager) {
-
- this.manager = manager !== undefined ? manager : THREE.DefaultLoadingManager;
- }
-
- FBXLoader.prototype = {
-
- constructor: FBXLoader,
-
- crossOrigin: 'anonymous',
-
- load: function load(url, onLoad, onProgress, onError) {
- var self = this;
-
- var resourceDirectory = THREE.LoaderUtils.extractUrlBase(url);
-
- var loader = new THREE.FileLoader(this.manager);
- loader.setResponseType('arraybuffer');
- loader.load(url, function (buffer) {
-
- try {
-
- var scene = self.parse(buffer, resourceDirectory);
- onLoad(scene);
- } catch (error) {
-
- setTimeout(function () {
-
- if (onError) onError(error);
-
- self.manager.itemError(url);
- }, 0);
- }
- }, onProgress, onError);
- },
-
- setCrossOrigin: function setCrossOrigin(value) {
-
- this.crossOrigin = value;
- return this;
- },
-
- parse: function parse(FBXBuffer, resourceDirectory) {
-
- if (isFbxFormatBinary(FBXBuffer)) {
-
- fbxTree = new BinaryParser().parse(FBXBuffer);
- } else {
-
- var FBXText = convertArrayBufferToString(FBXBuffer);
-
- if (!isFbxFormatASCII(FBXText)) {
-
- throw new Error('THREE.FBXLoader: Unknown format.');
- }
-
- if (getFbxVersion(FBXText) < 7000) {
-
- throw new Error('THREE.FBXLoader: FBX version not supported, FileVersion: ' + getFbxVersion(FBXText));
- }
-
- fbxTree = new TextParser().parse(FBXText);
- }
-
- //console.log( FBXTree );
-
- var textureLoader = new THREE.TextureLoader(this.manager).setPath(resourceDirectory).setCrossOrigin(this.crossOrigin);
-
- return new FBXTreeParser(textureLoader).parse(fbxTree);
- }
-
- };
-
- // Parse the FBXTree object returned by the BinaryParser or TextParser and return a THREE.Group
- function FBXTreeParser(textureLoader) {
-
- this.textureLoader = textureLoader;
- }
-
- FBXTreeParser.prototype = {
-
- constructor: FBXTreeParser,
-
- parse: function parse() {
-
- connections = this.parseConnections();
-
- var images = this.parseImages();
- var textures = this.parseTextures(images);
- var materials = this.parseMaterials(textures);
- var deformers = this.parseDeformers();
- var geometryMap = new GeometryParser().parse(deformers);
-
- this.parseScene(deformers, geometryMap, materials);
-
- return sceneGraph;
- },
-
- // Parses FBXTree.Connections which holds parent-child connections between objects (e.g. material -> texture, model->geometry )
- // and details the connection type
- parseConnections: function parseConnections() {
-
- var connectionMap = new Map();
-
- if ('Connections' in fbxTree) {
-
- var rawConnections = fbxTree.Connections.connections;
-
- rawConnections.forEach(function (rawConnection) {
-
- var fromID = rawConnection[0];
- var toID = rawConnection[1];
- var relationship = rawConnection[2];
-
- if (!connectionMap.has(fromID)) {
-
- connectionMap.set(fromID, {
- parents: [],
- children: []
- });
- }
-
- var parentRelationship = { ID: toID, relationship: relationship };
- connectionMap.get(fromID).parents.push(parentRelationship);
-
- if (!connectionMap.has(toID)) {
-
- connectionMap.set(toID, {
- parents: [],
- children: []
- });
- }
-
- var childRelationship = { ID: fromID, relationship: relationship };
- connectionMap.get(toID).children.push(childRelationship);
- });
- }
-
- return connectionMap;
- },
-
- // Parse FBXTree.Objects.Video for embedded image data
- // These images are connected to textures in FBXTree.Objects.Textures
- // via FBXTree.Connections.
- parseImages: function parseImages() {
-
- var images = {};
- var blobs = {};
-
- if ('Video' in fbxTree.Objects) {
-
- var videoNodes = fbxTree.Objects.Video;
-
- for (var nodeID in videoNodes) {
-
- var videoNode = videoNodes[nodeID];
-
- var id = parseInt(nodeID);
-
- images[id] = videoNode.RelativeFilename || videoNode.Filename;
-
- // raw image data is in videoNode.Content
- if ('Content' in videoNode) {
-
- var arrayBufferContent = videoNode.Content instanceof ArrayBuffer && videoNode.Content.byteLength > 0;
- var base64Content = typeof videoNode.Content === 'string' && videoNode.Content !== '';
-
- if (arrayBufferContent || base64Content) {
-
- var image = this.parseImage(videoNodes[nodeID]);
-
- blobs[videoNode.RelativeFilename || videoNode.Filename] = image;
- }
- }
- }
- }
-
- for (var id in images) {
-
- var filename = images[id];
-
- if (blobs[filename] !== undefined) images[id] = blobs[filename];else images[id] = images[id].split('\\').pop();
- }
-
- return images;
- },
-
- // Parse embedded image data in FBXTree.Video.Content
- parseImage: function parseImage(videoNode) {
-
- var content = videoNode.Content;
- var fileName = videoNode.RelativeFilename || videoNode.Filename;
- var extension = fileName.slice(fileName.lastIndexOf('.') + 1).toLowerCase();
-
- var type;
-
- switch (extension) {
-
- case 'bmp':
-
- type = 'image/bmp';
- break;
-
- case 'jpg':
- case 'jpeg':
-
- type = 'image/jpeg';
- break;
-
- case 'png':
-
- type = 'image/png';
- break;
-
- case 'tif':
-
- type = 'image/tiff';
- break;
-
- case 'tga':
-
- if (typeof THREE.TGALoader !== 'function') {
-
- console.warn('FBXLoader: THREE.TGALoader is required to load TGA textures');
- return;
- } else {
-
- if (THREE.Loader.Handlers.get('.tga') === null) {
-
- THREE.Loader.Handlers.add(/\.tga$/i, new THREE.TGALoader());
- }
-
- type = 'image/tga';
- break;
- }
-
- default:
-
- console.warn('FBXLoader: Image type "' + extension + '" is not supported.');
- return;
-
- }
-
- if (typeof content === 'string') {
- // ASCII format
-
- return 'data:' + type + ';base64,' + content;
- } else {
- // Binary Format
-
- var array = new Uint8Array(content);
- return window.URL.createObjectURL(new Blob([array], { type: type }));
- }
- },
-
- // Parse nodes in FBXTree.Objects.Texture
- // These contain details such as UV scaling, cropping, rotation etc and are connected
- // to images in FBXTree.Objects.Video
- parseTextures: function parseTextures(images) {
-
- var textureMap = new Map();
-
- if ('Texture' in fbxTree.Objects) {
-
- var textureNodes = fbxTree.Objects.Texture;
- for (var nodeID in textureNodes) {
-
- var texture = this.parseTexture(textureNodes[nodeID], images);
- textureMap.set(parseInt(nodeID), texture);
- }
- }
-
- return textureMap;
- },
-
- // Parse individual node in FBXTree.Objects.Texture
- parseTexture: function parseTexture(textureNode, images) {
-
- var texture = this.loadTexture(textureNode, images);
-
- texture.ID = textureNode.id;
-
- texture.name = textureNode.attrName;
-
- var wrapModeU = textureNode.WrapModeU;
- var wrapModeV = textureNode.WrapModeV;
-
- var valueU = wrapModeU !== undefined ? wrapModeU.value : 0;
- var valueV = wrapModeV !== undefined ? wrapModeV.value : 0;
-
- // http://download.autodesk.com/us/fbx/SDKdocs/FBX_SDK_Help/files/fbxsdkref/class_k_fbx_texture.html#889640e63e2e681259ea81061b85143a
- // 0: repeat(default), 1: clamp
-
- texture.wrapS = valueU === 0 ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping;
- texture.wrapT = valueV === 0 ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping;
-
- if ('Scaling' in textureNode) {
-
- var values = textureNode.Scaling.value;
-
- texture.repeat.x = values[0];
- texture.repeat.y = values[1];
- }
-
- return texture;
- },
-
- // load a texture specified as a blob or data URI, or via an external URL using THREE.TextureLoader
- loadTexture: function loadTexture(textureNode, images) {
-
- var fileName;
-
- var currentPath = this.textureLoader.path;
-
- var children = connections.get(textureNode.id).children;
-
- if (children !== undefined && children.length > 0 && images[children[0].ID] !== undefined) {
-
- fileName = images[children[0].ID];
-
- if (fileName.indexOf('blob:') === 0 || fileName.indexOf('data:') === 0) {
-
- this.textureLoader.setPath(undefined);
- }
- }
-
- var texture;
-
- var extension = textureNode.FileName.slice(-3).toLowerCase();
-
- if (extension === 'tga') {
-
- var loader = THREE.Loader.Handlers.get('.tga');
-
- if (loader === null) {
-
- console.warn('FBXLoader: TGALoader not found, creating empty placeholder texture for', fileName);
- texture = new THREE.Texture();
- } else {
-
- texture = loader.load(fileName);
- }
- } else if (extension === 'psd') {
-
- console.warn('FBXLoader: PSD textures are not supported, creating empty placeholder texture for', fileName);
- texture = new THREE.Texture();
- } else {
-
- texture = this.textureLoader.load(fileName);
- }
-
- this.textureLoader.setPath(currentPath);
-
- return texture;
- },
-
- // Parse nodes in FBXTree.Objects.Material
- parseMaterials: function parseMaterials(textureMap) {
-
- var materialMap = new Map();
-
- if ('Material' in fbxTree.Objects) {
-
- var materialNodes = fbxTree.Objects.Material;
-
- for (var nodeID in materialNodes) {
-
- var material = this.parseMaterial(materialNodes[nodeID], textureMap);
-
- if (material !== null) materialMap.set(parseInt(nodeID), material);
- }
- }
-
- return materialMap;
- },
-
- // Parse single node in FBXTree.Objects.Material
- // Materials are connected to texture maps in FBXTree.Objects.Textures
- // FBX format currently only supports Lambert and Phong shading models
- parseMaterial: function parseMaterial(materialNode, textureMap) {
-
- var ID = materialNode.id;
- var name = materialNode.attrName;
- var type = materialNode.ShadingModel;
-
- // Case where FBX wraps shading model in property object.
- if ((typeof type === 'undefined' ? 'undefined' : _typeof(type)) === 'object') {
-
- type = type.value;
- }
-
- // Ignore unused materials which don't have any connections.
- if (!connections.has(ID)) return null;
-
- var parameters = this.parseParameters(materialNode, textureMap, ID);
-
- var material;
-
- switch (type.toLowerCase()) {
-
- case 'phong':
- material = new THREE.MeshPhongMaterial();
- break;
- case 'lambert':
- material = new THREE.MeshLambertMaterial();
- break;
- default:
- console.warn('THREE.FBXLoader: unknown material type "%s". Defaulting to MeshPhongMaterial.', type);
- material = new THREE.MeshPhongMaterial({ color: 0x3300ff });
- break;
-
- }
-
- material.setValues(parameters);
- material.name = name;
-
- return material;
- },
-
- // Parse FBX material and return parameters suitable for a three.js material
- // Also parse the texture map and return any textures associated with the material
- parseParameters: function parseParameters(materialNode, textureMap, ID) {
-
- var parameters = {};
-
- if (materialNode.BumpFactor) {
-
- parameters.bumpScale = materialNode.BumpFactor.value;
- }
- if (materialNode.Diffuse) {
-
- parameters.color = new THREE.Color().fromArray(materialNode.Diffuse.value);
- } else if (materialNode.DiffuseColor && materialNode.DiffuseColor.type === 'Color') {
-
- // The blender exporter exports diffuse here instead of in materialNode.Diffuse
- parameters.color = new THREE.Color().fromArray(materialNode.DiffuseColor.value);
- }
- if (materialNode.DisplacementFactor) {
-
- parameters.displacementScale = materialNode.DisplacementFactor.value;
- }
- if (materialNode.Emissive) {
-
- parameters.emissive = new THREE.Color().fromArray(materialNode.Emissive.value);
- } else if (materialNode.EmissiveColor && materialNode.EmissiveColor.type === 'Color') {
-
- // The blender exporter exports emissive color here instead of in materialNode.Emissive
- parameters.emissive = new THREE.Color().fromArray(materialNode.EmissiveColor.value);
- }
- if (materialNode.EmissiveFactor) {
-
- parameters.emissiveIntensity = parseFloat(materialNode.EmissiveFactor.value);
- }
- if (materialNode.Opacity) {
-
- parameters.opacity = parseFloat(materialNode.Opacity.value);
- }
- if (parameters.opacity < 1.0) {
-
- parameters.transparent = true;
- }
- if (materialNode.ReflectionFactor) {
-
- parameters.reflectivity = materialNode.ReflectionFactor.value;
- }
- if (materialNode.Shininess) {
-
- parameters.shininess = materialNode.Shininess.value;
- }
- if (materialNode.Specular) {
-
- parameters.specular = new THREE.Color().fromArray(materialNode.Specular.value);
- } else if (materialNode.SpecularColor && materialNode.SpecularColor.type === 'Color') {
-
- // The blender exporter exports specular color here instead of in materialNode.Specular
- parameters.specular = new THREE.Color().fromArray(materialNode.SpecularColor.value);
- }
-
- var self = this;
- connections.get(ID).children.forEach(function (child) {
-
- var type = child.relationship;
-
- switch (type) {
-
- case 'Bump':
- parameters.bumpMap = self.getTexture(textureMap, child.ID);
- break;
-
- case 'DiffuseColor':
- parameters.map = self.getTexture(textureMap, child.ID);
- break;
-
- case 'DisplacementColor':
- parameters.displacementMap = self.getTexture(textureMap, child.ID);
- break;
-
- case 'EmissiveColor':
- parameters.emissiveMap = self.getTexture(textureMap, child.ID);
- break;
-
- case 'NormalMap':
- parameters.normalMap = self.getTexture(textureMap, child.ID);
- break;
-
- case 'ReflectionColor':
- parameters.envMap = self.getTexture(textureMap, child.ID);
- parameters.envMap.mapping = THREE.EquirectangularReflectionMapping;
- break;
-
- case 'SpecularColor':
- parameters.specularMap = self.getTexture(textureMap, child.ID);
- break;
-
- case 'TransparentColor':
- parameters.alphaMap = self.getTexture(textureMap, child.ID);
- parameters.transparent = true;
- break;
-
- case 'AmbientColor':
- case 'ShininessExponent': // AKA glossiness map
- case 'SpecularFactor': // AKA specularLevel
- case 'VectorDisplacementColor': // NOTE: Seems to be a copy of DisplacementColor
- default:
- console.warn('THREE.FBXLoader: %s map is not supported in three.js, skipping texture.', type);
- break;
-
- }
- });
-
- return parameters;
- },
-
- // get a texture from the textureMap for use by a material.
- getTexture: function getTexture(textureMap, id) {
-
- // if the texture is a layered texture, just use the first layer and issue a warning
- if ('LayeredTexture' in fbxTree.Objects && id in fbxTree.Objects.LayeredTexture) {
-
- console.warn('THREE.FBXLoader: layered textures are not supported in three.js. Discarding all but first layer.');
- id = connections.get(id).children[0].ID;
- }
-
- return textureMap.get(id);
- },
-
- // Parse nodes in FBXTree.Objects.Deformer
- // Deformer node can contain skinning or Vertex Cache animation data, however only skinning is supported here
- // Generates map of Skeleton-like objects for use later when generating and binding skeletons.
- parseDeformers: function parseDeformers() {
-
- var skeletons = {};
- var morphTargets = {};
-
- if ('Deformer' in fbxTree.Objects) {
-
- var DeformerNodes = fbxTree.Objects.Deformer;
-
- for (var nodeID in DeformerNodes) {
-
- var deformerNode = DeformerNodes[nodeID];
-
- var relationships = connections.get(parseInt(nodeID));
-
- if (deformerNode.attrType === 'Skin') {
-
- var skeleton = this.parseSkeleton(relationships, DeformerNodes);
- skeleton.ID = nodeID;
-
- if (relationships.parents.length > 1) console.warn('THREE.FBXLoader: skeleton attached to more than one geometry is not supported.');
- skeleton.geometryID = relationships.parents[0].ID;
-
- skeletons[nodeID] = skeleton;
- } else if (deformerNode.attrType === 'BlendShape') {
-
- var morphTarget = {
- id: nodeID
- };
-
- morphTarget.rawTargets = this.parseMorphTargets(relationships, DeformerNodes);
- morphTarget.id = nodeID;
-
- if (relationships.parents.length > 1) console.warn('THREE.FBXLoader: morph target attached to more than one geometry is not supported.');
-
- morphTargets[nodeID] = morphTarget;
- }
- }
- }
-
- return {
-
- skeletons: skeletons,
- morphTargets: morphTargets
-
- };
- },
-
- // Parse single nodes in FBXTree.Objects.Deformer
- // The top level skeleton node has type 'Skin' and sub nodes have type 'Cluster'
- // Each skin node represents a skeleton and each cluster node represents a bone
- parseSkeleton: function parseSkeleton(relationships, deformerNodes) {
-
- var rawBones = [];
-
- relationships.children.forEach(function (child) {
-
- var boneNode = deformerNodes[child.ID];
-
- if (boneNode.attrType !== 'Cluster') return;
-
- var rawBone = {
-
- ID: child.ID,
- indices: [],
- weights: [],
- transform: new THREE.Matrix4().fromArray(boneNode.Transform.a),
- transformLink: new THREE.Matrix4().fromArray(boneNode.TransformLink.a),
- linkMode: boneNode.Mode
-
- };
-
- if ('Indexes' in boneNode) {
-
- rawBone.indices = boneNode.Indexes.a;
- rawBone.weights = boneNode.Weights.a;
- }
-
- rawBones.push(rawBone);
- });
-
- return {
-
- rawBones: rawBones,
- bones: []
-
- };
- },
-
- // The top level morph deformer node has type "BlendShape" and sub nodes have type "BlendShapeChannel"
- parseMorphTargets: function parseMorphTargets(relationships, deformerNodes) {
-
- var rawMorphTargets = [];
-
- for (var i = 0; i < relationships.children.length; i++) {
-
- if (i === 8) {
-
- console.warn('FBXLoader: maximum of 8 morph targets supported. Ignoring additional targets.');
-
- break;
- }
-
- var child = relationships.children[i];
-
- var morphTargetNode = deformerNodes[child.ID];
-
- var rawMorphTarget = {
-
- name: morphTargetNode.attrName,
- initialWeight: morphTargetNode.DeformPercent,
- id: morphTargetNode.id,
- fullWeights: morphTargetNode.FullWeights.a
-
- };
-
- if (morphTargetNode.attrType !== 'BlendShapeChannel') return;
-
- var targetRelationships = connections.get(parseInt(child.ID));
-
- targetRelationships.children.forEach(function (child) {
-
- if (child.relationship === undefined) rawMorphTarget.geoID = child.ID;
- });
-
- rawMorphTargets.push(rawMorphTarget);
- }
-
- return rawMorphTargets;
- },
-
- // create the main THREE.Group() to be returned by the loader
- parseScene: function parseScene(deformers, geometryMap, materialMap) {
-
- sceneGraph = new THREE.Group();
-
- var modelMap = this.parseModels(deformers.skeletons, geometryMap, materialMap);
-
- var modelNodes = fbxTree.Objects.Model;
-
- var self = this;
- modelMap.forEach(function (model) {
-
- var modelNode = modelNodes[model.ID];
- self.setLookAtProperties(model, modelNode);
-
- var parentConnections = connections.get(model.ID).parents;
-
- parentConnections.forEach(function (connection) {
-
- var parent = modelMap.get(connection.ID);
- if (parent !== undefined) parent.add(model);
- });
-
- if (model.parent === null) {
-
- sceneGraph.add(model);
- }
- });
-
- this.bindSkeleton(deformers.skeletons, geometryMap, modelMap);
-
- this.createAmbientLight();
-
- this.setupMorphMaterials();
-
- var animations = new AnimationParser().parse();
-
- // if all the models where already combined in a single group, just return that
- if (sceneGraph.children.length === 1 && sceneGraph.children[0].isGroup) {
-
- sceneGraph.children[0].animations = animations;
- sceneGraph = sceneGraph.children[0];
- }
-
- sceneGraph.animations = animations;
- },
-
- // parse nodes in FBXTree.Objects.Model
- parseModels: function parseModels(skeletons, geometryMap, materialMap) {
-
- var modelMap = new Map();
- var modelNodes = fbxTree.Objects.Model;
-
- for (var nodeID in modelNodes) {
-
- var id = parseInt(nodeID);
- var node = modelNodes[nodeID];
- var relationships = connections.get(id);
-
- var model = this.buildSkeleton(relationships, skeletons, id, node.attrName);
-
- if (!model) {
-
- switch (node.attrType) {
-
- case 'Camera':
- model = this.createCamera(relationships);
- break;
- case 'Light':
- model = this.createLight(relationships);
- break;
- case 'Mesh':
- model = this.createMesh(relationships, geometryMap, materialMap);
- break;
- case 'NurbsCurve':
- model = this.createCurve(relationships, geometryMap);
- break;
- case 'LimbNode': // usually associated with a Bone, however if a Bone was not created we'll make a Group instead
- case 'Null':
- default:
- model = new THREE.Group();
- break;
-
- }
-
- model.name = THREE.PropertyBinding.sanitizeNodeName(node.attrName);
- model.ID = id;
- }
-
- this.setModelTransforms(model, node);
- modelMap.set(id, model);
- }
-
- return modelMap;
- },
-
- buildSkeleton: function buildSkeleton(relationships, skeletons, id, name) {
-
- var bone = null;
-
- relationships.parents.forEach(function (parent) {
-
- for (var ID in skeletons) {
-
- var skeleton = skeletons[ID];
-
- skeleton.rawBones.forEach(function (rawBone, i) {
-
- if (rawBone.ID === parent.ID) {
-
- var subBone = bone;
- bone = new THREE.Bone();
- bone.matrixWorld.copy(rawBone.transformLink);
-
- // set name and id here - otherwise in cases where "subBone" is created it will not have a name / id
- bone.name = THREE.PropertyBinding.sanitizeNodeName(name);
- bone.ID = id;
-
- skeleton.bones[i] = bone;
-
- // In cases where a bone is shared between multiple meshes
- // duplicate the bone here and and it as a child of the first bone
- if (subBone !== null) {
-
- bone.add(subBone);
- }
- }
- });
- }
- });
-
- return bone;
- },
-
- // create a THREE.PerspectiveCamera or THREE.OrthographicCamera
- createCamera: function createCamera(relationships) {
-
- var model;
- var cameraAttribute;
-
- relationships.children.forEach(function (child) {
-
- var attr = fbxTree.Objects.NodeAttribute[child.ID];
-
- if (attr !== undefined) {
-
- cameraAttribute = attr;
- }
- });
-
- if (cameraAttribute === undefined) {
-
- model = new THREE.Object3D();
- } else {
-
- var type = 0;
- if (cameraAttribute.CameraProjectionType !== undefined && cameraAttribute.CameraProjectionType.value === 1) {
-
- type = 1;
- }
-
- var nearClippingPlane = 1;
- if (cameraAttribute.NearPlane !== undefined) {
-
- nearClippingPlane = cameraAttribute.NearPlane.value / 1000;
- }
-
- var farClippingPlane = 1000;
- if (cameraAttribute.FarPlane !== undefined) {
-
- farClippingPlane = cameraAttribute.FarPlane.value / 1000;
- }
-
- var width = window.innerWidth;
- var height = window.innerHeight;
-
- if (cameraAttribute.AspectWidth !== undefined && cameraAttribute.AspectHeight !== undefined) {
-
- width = cameraAttribute.AspectWidth.value;
- height = cameraAttribute.AspectHeight.value;
- }
-
- var aspect = width / height;
-
- var fov = 45;
- if (cameraAttribute.FieldOfView !== undefined) {
-
- fov = cameraAttribute.FieldOfView.value;
- }
-
- var focalLength = cameraAttribute.FocalLength ? cameraAttribute.FocalLength.value : null;
-
- switch (type) {
-
- case 0:
- // Perspective
- model = new THREE.PerspectiveCamera(fov, aspect, nearClippingPlane, farClippingPlane);
- if (focalLength !== null) model.setFocalLength(focalLength);
- break;
-
- case 1:
- // Orthographic
- model = new THREE.OrthographicCamera(-width / 2, width / 2, height / 2, -height / 2, nearClippingPlane, farClippingPlane);
- break;
-
- default:
- console.warn('THREE.FBXLoader: Unknown camera type ' + type + '.');
- model = new THREE.Object3D();
- break;
-
- }
- }
-
- return model;
- },
-
- // Create a THREE.DirectionalLight, THREE.PointLight or THREE.SpotLight
- createLight: function createLight(relationships) {
-
- var model;
- var lightAttribute;
-
- relationships.children.forEach(function (child) {
-
- var attr = fbxTree.Objects.NodeAttribute[child.ID];
-
- if (attr !== undefined) {
-
- lightAttribute = attr;
- }
- });
-
- if (lightAttribute === undefined) {
-
- model = new THREE.Object3D();
- } else {
-
- var type;
-
- // LightType can be undefined for Point lights
- if (lightAttribute.LightType === undefined) {
-
- type = 0;
- } else {
-
- type = lightAttribute.LightType.value;
- }
-
- var color = 0xffffff;
-
- if (lightAttribute.Color !== undefined) {
-
- color = new THREE.Color().fromArray(lightAttribute.Color.value);
- }
-
- var intensity = lightAttribute.Intensity === undefined ? 1 : lightAttribute.Intensity.value / 100;
-
- // light disabled
- if (lightAttribute.CastLightOnObject !== undefined && lightAttribute.CastLightOnObject.value === 0) {
-
- intensity = 0;
- }
-
- var distance = 0;
- if (lightAttribute.FarAttenuationEnd !== undefined) {
-
- if (lightAttribute.EnableFarAttenuation !== undefined && lightAttribute.EnableFarAttenuation.value === 0) {
-
- distance = 0;
- } else {
-
- distance = lightAttribute.FarAttenuationEnd.value;
- }
- }
-
- // TODO: could this be calculated linearly from FarAttenuationStart to FarAttenuationEnd?
- var decay = 1;
-
- switch (type) {
-
- case 0:
- // Point
- model = new THREE.PointLight(color, intensity, distance, decay);
- break;
-
- case 1:
- // Directional
- model = new THREE.DirectionalLight(color, intensity);
- break;
-
- case 2:
- // Spot
- var angle = Math.PI / 3;
-
- if (lightAttribute.InnerAngle !== undefined) {
-
- angle = THREE.Math.degToRad(lightAttribute.InnerAngle.value);
- }
-
- var penumbra = 0;
- if (lightAttribute.OuterAngle !== undefined) {
-
- // TODO: this is not correct - FBX calculates outer and inner angle in degrees
- // with OuterAngle > InnerAngle && OuterAngle <= Math.PI
- // while three.js uses a penumbra between (0, 1) to attenuate the inner angle
- penumbra = THREE.Math.degToRad(lightAttribute.OuterAngle.value);
- penumbra = Math.max(penumbra, 1);
- }
-
- model = new THREE.SpotLight(color, intensity, distance, angle, penumbra, decay);
- break;
-
- default:
- console.warn('THREE.FBXLoader: Unknown light type ' + lightAttribute.LightType.value + ', defaulting to a THREE.PointLight.');
- model = new THREE.PointLight(color, intensity);
- break;
-
- }
-
- if (lightAttribute.CastShadows !== undefined && lightAttribute.CastShadows.value === 1) {
-
- model.castShadow = true;
- }
- }
-
- return model;
- },
-
- createMesh: function createMesh(relationships, geometryMap, materialMap) {
-
- var model;
- var geometry = null;
- var material = null;
- var materials = [];
-
- // get geometry and materials(s) from connections
- relationships.children.forEach(function (child) {
-
- if (geometryMap.has(child.ID)) {
-
- geometry = geometryMap.get(child.ID);
- }
-
- if (materialMap.has(child.ID)) {
-
- materials.push(materialMap.get(child.ID));
- }
- });
-
- if (materials.length > 1) {
-
- material = materials;
- } else if (materials.length > 0) {
-
- material = materials[0];
- } else {
-
- material = new THREE.MeshPhongMaterial({ color: 0xcccccc });
- materials.push(material);
- }
-
- if ('color' in geometry.attributes) {
-
- materials.forEach(function (material) {
-
- material.vertexColors = THREE.VertexColors;
- });
- }
-
- if (geometry.FBX_Deformer) {
-
- materials.forEach(function (material) {
-
- material.skinning = true;
- });
-
- model = new THREE.SkinnedMesh(geometry, material);
- } else {
-
- model = new THREE.Mesh(geometry, material);
- }
-
- return model;
- },
-
- createCurve: function createCurve(relationships, geometryMap) {
-
- var geometry = relationships.children.reduce(function (geo, child) {
-
- if (geometryMap.has(child.ID)) geo = geometryMap.get(child.ID);
-
- return geo;
- }, null);
-
- // FBX does not list materials for Nurbs lines, so we'll just put our own in here.
- var material = new THREE.LineBasicMaterial({ color: 0x3300ff, linewidth: 1 });
- return new THREE.Line(geometry, material);
- },
-
- // parse the model node for transform details and apply them to the model
- setModelTransforms: function setModelTransforms(model, modelNode) {
-
- var transformData = {};
-
- if ('RotationOrder' in modelNode) transformData.eulerOrder = parseInt(modelNode.RotationOrder.value);
- if ('Lcl_Translation' in modelNode) transformData.translation = modelNode.Lcl_Translation.value;
- if ('RotationOffset' in modelNode) transformData.rotationOffset = modelNode.RotationOffset.value;
- if ('Lcl_Rotation' in modelNode) transformData.rotation = modelNode.Lcl_Rotation.value;
- if ('PreRotation' in modelNode) transformData.preRotation = modelNode.PreRotation.value;
- if ('PostRotation' in modelNode) transformData.postRotation = modelNode.PostRotation.value;
- if ('Lcl_Scaling' in modelNode) transformData.scale = modelNode.Lcl_Scaling.value;
-
- var transform = generateTransform(transformData);
-
- model.applyMatrix(transform);
- },
-
- setLookAtProperties: function setLookAtProperties(model, modelNode) {
-
- if ('LookAtProperty' in modelNode) {
-
- var children = connections.get(model.ID).children;
-
- children.forEach(function (child) {
-
- if (child.relationship === 'LookAtProperty') {
-
- var lookAtTarget = fbxTree.Objects.Model[child.ID];
-
- if ('Lcl_Translation' in lookAtTarget) {
-
- var pos = lookAtTarget.Lcl_Translation.value;
-
- // DirectionalLight, SpotLight
- if (model.target !== undefined) {
-
- model.target.position.fromArray(pos);
- sceneGraph.add(model.target);
- } else {
- // Cameras and other Object3Ds
-
- model.lookAt(new THREE.Vector3().fromArray(pos));
- }
- }
- }
- });
- }
- },
-
- bindSkeleton: function bindSkeleton(skeletons, geometryMap, modelMap) {
-
- var bindMatrices = this.parsePoseNodes();
-
- for (var ID in skeletons) {
-
- var skeleton = skeletons[ID];
-
- var parents = connections.get(parseInt(skeleton.ID)).parents;
-
- parents.forEach(function (parent) {
-
- if (geometryMap.has(parent.ID)) {
-
- var geoID = parent.ID;
- var geoRelationships = connections.get(geoID);
-
- geoRelationships.parents.forEach(function (geoConnParent) {
-
- if (modelMap.has(geoConnParent.ID)) {
-
- var model = modelMap.get(geoConnParent.ID);
-
- model.bind(new THREE.Skeleton(skeleton.bones), bindMatrices[geoConnParent.ID]);
- }
- });
- }
- });
- }
- },
-
- parsePoseNodes: function parsePoseNodes() {
-
- var bindMatrices = {};
-
- if ('Pose' in fbxTree.Objects) {
-
- var BindPoseNode = fbxTree.Objects.Pose;
-
- for (var nodeID in BindPoseNode) {
-
- if (BindPoseNode[nodeID].attrType === 'BindPose') {
-
- var poseNodes = BindPoseNode[nodeID].PoseNode;
-
- if (Array.isArray(poseNodes)) {
-
- poseNodes.forEach(function (poseNode) {
-
- bindMatrices[poseNode.Node] = new THREE.Matrix4().fromArray(poseNode.Matrix.a);
- });
- } else {
-
- bindMatrices[poseNodes.Node] = new THREE.Matrix4().fromArray(poseNodes.Matrix.a);
- }
- }
- }
- }
-
- return bindMatrices;
- },
-
- // Parse ambient color in FBXTree.GlobalSettings - if it's not set to black (default), create an ambient light
- createAmbientLight: function createAmbientLight() {
-
- if ('GlobalSettings' in fbxTree && 'AmbientColor' in fbxTree.GlobalSettings) {
-
- var ambientColor = fbxTree.GlobalSettings.AmbientColor.value;
- var r = ambientColor[0];
- var g = ambientColor[1];
- var b = ambientColor[2];
-
- if (r !== 0 || g !== 0 || b !== 0) {
-
- var color = new THREE.Color(r, g, b);
- sceneGraph.add(new THREE.AmbientLight(color, 1));
- }
- }
- },
-
- setupMorphMaterials: function setupMorphMaterials() {
-
- sceneGraph.traverse(function (child) {
-
- if (child.isMesh) {
-
- if (child.geometry.morphAttributes.position || child.geometry.morphAttributes.normal) {
-
- var uuid = child.uuid;
- var matUuid = child.material.uuid;
-
- // if a geometry has morph targets, it cannot share the material with other geometries
- var sharedMat = false;
-
- sceneGraph.traverse(function (child) {
-
- if (child.isMesh) {
-
- if (child.material.uuid === matUuid && child.uuid !== uuid) sharedMat = true;
- }
- });
-
- if (sharedMat === true) child.material = child.material.clone();
-
- child.material.morphTargets = true;
- }
- }
- });
- }
-
- };
-
- // parse Geometry data from FBXTree and return map of BufferGeometries
- function GeometryParser() {}
-
- GeometryParser.prototype = {
-
- constructor: GeometryParser,
-
- // Parse nodes in FBXTree.Objects.Geometry
- parse: function parse(deformers) {
-
- var geometryMap = new Map();
-
- if ('Geometry' in fbxTree.Objects) {
-
- var geoNodes = fbxTree.Objects.Geometry;
-
- for (var nodeID in geoNodes) {
-
- var relationships = connections.get(parseInt(nodeID));
- var geo = this.parseGeometry(relationships, geoNodes[nodeID], deformers);
-
- geometryMap.set(parseInt(nodeID), geo);
- }
- }
-
- return geometryMap;
- },
-
- // Parse single node in FBXTree.Objects.Geometry
- parseGeometry: function parseGeometry(relationships, geoNode, deformers) {
-
- switch (geoNode.attrType) {
-
- case 'Mesh':
- return this.parseMeshGeometry(relationships, geoNode, deformers);
- break;
-
- case 'NurbsCurve':
- return this.parseNurbsGeometry(geoNode);
- break;
-
- }
- },
-
- // Parse single node mesh geometry in FBXTree.Objects.Geometry
- parseMeshGeometry: function parseMeshGeometry(relationships, geoNode, deformers) {
-
- var skeletons = deformers.skeletons;
- var morphTargets = deformers.morphTargets;
-
- var modelNodes = relationships.parents.map(function (parent) {
-
- return fbxTree.Objects.Model[parent.ID];
- });
-
- // don't create geometry if it is not associated with any models
- if (modelNodes.length === 0) return;
-
- var skeleton = relationships.children.reduce(function (skeleton, child) {
-
- if (skeletons[child.ID] !== undefined) skeleton = skeletons[child.ID];
-
- return skeleton;
- }, null);
-
- var morphTarget = relationships.children.reduce(function (morphTarget, child) {
-
- if (morphTargets[child.ID] !== undefined) morphTarget = morphTargets[child.ID];
-
- return morphTarget;
- }, null);
-
- // TODO: if there is more than one model associated with the geometry, AND the models have
- // different geometric transforms, then this will cause problems
- // if ( modelNodes.length > 1 ) { }
-
- // For now just assume one model and get the preRotations from that
- var modelNode = modelNodes[0];
-
- var transformData = {};
-
- if ('RotationOrder' in modelNode) transformData.eulerOrder = modelNode.RotationOrder.value;
- if ('GeometricTranslation' in modelNode) transformData.translation = modelNode.GeometricTranslation.value;
- if ('GeometricRotation' in modelNode) transformData.rotation = modelNode.GeometricRotation.value;
- if ('GeometricScaling' in modelNode) transformData.scale = modelNode.GeometricScaling.value;
-
- var transform = generateTransform(transformData);
-
- return this.genGeometry(geoNode, skeleton, morphTarget, transform);
- },
-
- // Generate a THREE.BufferGeometry from a node in FBXTree.Objects.Geometry
- genGeometry: function genGeometry(geoNode, skeleton, morphTarget, preTransform) {
-
- var geo = new THREE.BufferGeometry();
- if (geoNode.attrName) geo.name = geoNode.attrName;
-
- var geoInfo = this.parseGeoNode(geoNode, skeleton);
- var buffers = this.genBuffers(geoInfo);
-
- var positionAttribute = new THREE.Float32BufferAttribute(buffers.vertex, 3);
-
- preTransform.applyToBufferAttribute(positionAttribute);
-
- geo.addAttribute('position', positionAttribute);
-
- if (buffers.colors.length > 0) {
-
- geo.addAttribute('color', new THREE.Float32BufferAttribute(buffers.colors, 3));
- }
-
- if (skeleton) {
-
- geo.addAttribute('skinIndex', new THREE.Uint16BufferAttribute(buffers.weightsIndices, 4));
-
- geo.addAttribute('skinWeight', new THREE.Float32BufferAttribute(buffers.vertexWeights, 4));
-
- // used later to bind the skeleton to the model
- geo.FBX_Deformer = skeleton;
- }
-
- if (buffers.normal.length > 0) {
-
- var normalAttribute = new THREE.Float32BufferAttribute(buffers.normal, 3);
-
- var normalMatrix = new THREE.Matrix3().getNormalMatrix(preTransform);
- normalMatrix.applyToBufferAttribute(normalAttribute);
-
- geo.addAttribute('normal', normalAttribute);
- }
-
- buffers.uvs.forEach(function (uvBuffer, i) {
-
- // subsequent uv buffers are called 'uv1', 'uv2', ...
- var name = 'uv' + (i + 1).toString();
-
- // the first uv buffer is just called 'uv'
- if (i === 0) {
-
- name = 'uv';
- }
-
- geo.addAttribute(name, new THREE.Float32BufferAttribute(buffers.uvs[i], 2));
- });
-
- if (geoInfo.material && geoInfo.material.mappingType !== 'AllSame') {
-
- // Convert the material indices of each vertex into rendering groups on the geometry.
- var prevMaterialIndex = buffers.materialIndex[0];
- var startIndex = 0;
-
- buffers.materialIndex.forEach(function (currentIndex, i) {
-
- if (currentIndex !== prevMaterialIndex) {
-
- geo.addGroup(startIndex, i - startIndex, prevMaterialIndex);
-
- prevMaterialIndex = currentIndex;
- startIndex = i;
- }
- });
-
- // the loop above doesn't add the last group, do that here.
- if (geo.groups.length > 0) {
-
- var lastGroup = geo.groups[geo.groups.length - 1];
- var lastIndex = lastGroup.start + lastGroup.count;
-
- if (lastIndex !== buffers.materialIndex.length) {
-
- geo.addGroup(lastIndex, buffers.materialIndex.length - lastIndex, prevMaterialIndex);
- }
- }
-
- // case where there are multiple materials but the whole geometry is only
- // using one of them
- if (geo.groups.length === 0) {
-
- geo.addGroup(0, buffers.materialIndex.length, buffers.materialIndex[0]);
- }
- }
-
- this.addMorphTargets(geo, geoNode, morphTarget, preTransform);
-
- return geo;
- },
-
- parseGeoNode: function parseGeoNode(geoNode, skeleton) {
-
- var geoInfo = {};
-
- geoInfo.vertexPositions = geoNode.Vertices !== undefined ? geoNode.Vertices.a : [];
- geoInfo.vertexIndices = geoNode.PolygonVertexIndex !== undefined ? geoNode.PolygonVertexIndex.a : [];
-
- if (geoNode.LayerElementColor) {
-
- geoInfo.color = this.parseVertexColors(geoNode.LayerElementColor[0]);
- }
-
- if (geoNode.LayerElementMaterial) {
-
- geoInfo.material = this.parseMaterialIndices(geoNode.LayerElementMaterial[0]);
- }
-
- if (geoNode.LayerElementNormal) {
-
- geoInfo.normal = this.parseNormals(geoNode.LayerElementNormal[0]);
- }
-
- if (geoNode.LayerElementUV) {
-
- geoInfo.uv = [];
-
- var i = 0;
- while (geoNode.LayerElementUV[i]) {
-
- geoInfo.uv.push(this.parseUVs(geoNode.LayerElementUV[i]));
- i++;
- }
- }
-
- geoInfo.weightTable = {};
-
- if (skeleton !== null) {
-
- geoInfo.skeleton = skeleton;
-
- skeleton.rawBones.forEach(function (rawBone, i) {
-
- // loop over the bone's vertex indices and weights
- rawBone.indices.forEach(function (index, j) {
-
- if (geoInfo.weightTable[index] === undefined) geoInfo.weightTable[index] = [];
-
- geoInfo.weightTable[index].push({
-
- id: i,
- weight: rawBone.weights[j]
-
- });
- });
- });
- }
-
- return geoInfo;
- },
-
- genBuffers: function genBuffers(geoInfo) {
-
- var buffers = {
- vertex: [],
- normal: [],
- colors: [],
- uvs: [],
- materialIndex: [],
- vertexWeights: [],
- weightsIndices: []
- };
-
- var polygonIndex = 0;
- var faceLength = 0;
- var displayedWeightsWarning = false;
-
- // these will hold data for a single face
- var facePositionIndexes = [];
- var faceNormals = [];
- var faceColors = [];
- var faceUVs = [];
- var faceWeights = [];
- var faceWeightIndices = [];
-
- var self = this;
- geoInfo.vertexIndices.forEach(function (vertexIndex, polygonVertexIndex) {
-
- var endOfFace = false;
-
- // Face index and vertex index arrays are combined in a single array
- // A cube with quad faces looks like this:
- // PolygonVertexIndex: *24 {
- // a: 0, 1, 3, -3, 2, 3, 5, -5, 4, 5, 7, -7, 6, 7, 1, -1, 1, 7, 5, -4, 6, 0, 2, -5
- // }
- // Negative numbers mark the end of a face - first face here is 0, 1, 3, -3
- // to find index of last vertex bit shift the index: ^ - 1
- if (vertexIndex < 0) {
-
- vertexIndex = vertexIndex ^ -1; // equivalent to ( x * -1 ) - 1
- endOfFace = true;
- }
-
- var weightIndices = [];
- var weights = [];
-
- facePositionIndexes.push(vertexIndex * 3, vertexIndex * 3 + 1, vertexIndex * 3 + 2);
-
- if (geoInfo.color) {
-
- var data = getData(polygonVertexIndex, polygonIndex, vertexIndex, geoInfo.color);
-
- faceColors.push(data[0], data[1], data[2]);
- }
-
- if (geoInfo.skeleton) {
-
- if (geoInfo.weightTable[vertexIndex] !== undefined) {
-
- geoInfo.weightTable[vertexIndex].forEach(function (wt) {
-
- weights.push(wt.weight);
- weightIndices.push(wt.id);
- });
- }
-
- if (weights.length > 4) {
-
- if (!displayedWeightsWarning) {
-
- console.warn('THREE.FBXLoader: Vertex has more than 4 skinning weights assigned to vertex. Deleting additional weights.');
- displayedWeightsWarning = true;
- }
-
- var wIndex = [0, 0, 0, 0];
- var Weight = [0, 0, 0, 0];
-
- weights.forEach(function (weight, weightIndex) {
-
- var currentWeight = weight;
- var currentIndex = weightIndices[weightIndex];
-
- Weight.forEach(function (comparedWeight, comparedWeightIndex, comparedWeightArray) {
-
- if (currentWeight > comparedWeight) {
-
- comparedWeightArray[comparedWeightIndex] = currentWeight;
- currentWeight = comparedWeight;
-
- var tmp = wIndex[comparedWeightIndex];
- wIndex[comparedWeightIndex] = currentIndex;
- currentIndex = tmp;
- }
- });
- });
-
- weightIndices = wIndex;
- weights = Weight;
- }
-
- // if the weight array is shorter than 4 pad with 0s
- while (weights.length < 4) {
-
- weights.push(0);
- weightIndices.push(0);
- }
-
- for (var i = 0; i < 4; ++i) {
-
- faceWeights.push(weights[i]);
- faceWeightIndices.push(weightIndices[i]);
- }
- }
-
- if (geoInfo.normal) {
-
- var data = getData(polygonVertexIndex, polygonIndex, vertexIndex, geoInfo.normal);
-
- faceNormals.push(data[0], data[1], data[2]);
- }
-
- if (geoInfo.material && geoInfo.material.mappingType !== 'AllSame') {
-
- var materialIndex = getData(polygonVertexIndex, polygonIndex, vertexIndex, geoInfo.material)[0];
- }
-
- if (geoInfo.uv) {
-
- geoInfo.uv.forEach(function (uv, i) {
-
- var data = getData(polygonVertexIndex, polygonIndex, vertexIndex, uv);
-
- if (faceUVs[i] === undefined) {
-
- faceUVs[i] = [];
- }
-
- faceUVs[i].push(data[0]);
- faceUVs[i].push(data[1]);
- });
- }
-
- faceLength++;
-
- if (endOfFace) {
-
- self.genFace(buffers, geoInfo, facePositionIndexes, materialIndex, faceNormals, faceColors, faceUVs, faceWeights, faceWeightIndices, faceLength);
-
- polygonIndex++;
- faceLength = 0;
-
- // reset arrays for the next face
- facePositionIndexes = [];
- faceNormals = [];
- faceColors = [];
- faceUVs = [];
- faceWeights = [];
- faceWeightIndices = [];
- }
- });
-
- return buffers;
- },
-
- // Generate data for a single face in a geometry. If the face is a quad then split it into 2 tris
- genFace: function genFace(buffers, geoInfo, facePositionIndexes, materialIndex, faceNormals, faceColors, faceUVs, faceWeights, faceWeightIndices, faceLength) {
-
- for (var i = 2; i < faceLength; i++) {
-
- buffers.vertex.push(geoInfo.vertexPositions[facePositionIndexes[0]]);
- buffers.vertex.push(geoInfo.vertexPositions[facePositionIndexes[1]]);
- buffers.vertex.push(geoInfo.vertexPositions[facePositionIndexes[2]]);
-
- buffers.vertex.push(geoInfo.vertexPositions[facePositionIndexes[(i - 1) * 3]]);
- buffers.vertex.push(geoInfo.vertexPositions[facePositionIndexes[(i - 1) * 3 + 1]]);
- buffers.vertex.push(geoInfo.vertexPositions[facePositionIndexes[(i - 1) * 3 + 2]]);
-
- buffers.vertex.push(geoInfo.vertexPositions[facePositionIndexes[i * 3]]);
- buffers.vertex.push(geoInfo.vertexPositions[facePositionIndexes[i * 3 + 1]]);
- buffers.vertex.push(geoInfo.vertexPositions[facePositionIndexes[i * 3 + 2]]);
-
- if (geoInfo.skeleton) {
-
- buffers.vertexWeights.push(faceWeights[0]);
- buffers.vertexWeights.push(faceWeights[1]);
- buffers.vertexWeights.push(faceWeights[2]);
- buffers.vertexWeights.push(faceWeights[3]);
-
- buffers.vertexWeights.push(faceWeights[(i - 1) * 4]);
- buffers.vertexWeights.push(faceWeights[(i - 1) * 4 + 1]);
- buffers.vertexWeights.push(faceWeights[(i - 1) * 4 + 2]);
- buffers.vertexWeights.push(faceWeights[(i - 1) * 4 + 3]);
-
- buffers.vertexWeights.push(faceWeights[i * 4]);
- buffers.vertexWeights.push(faceWeights[i * 4 + 1]);
- buffers.vertexWeights.push(faceWeights[i * 4 + 2]);
- buffers.vertexWeights.push(faceWeights[i * 4 + 3]);
-
- buffers.weightsIndices.push(faceWeightIndices[0]);
- buffers.weightsIndices.push(faceWeightIndices[1]);
- buffers.weightsIndices.push(faceWeightIndices[2]);
- buffers.weightsIndices.push(faceWeightIndices[3]);
-
- buffers.weightsIndices.push(faceWeightIndices[(i - 1) * 4]);
- buffers.weightsIndices.push(faceWeightIndices[(i - 1) * 4 + 1]);
- buffers.weightsIndices.push(faceWeightIndices[(i - 1) * 4 + 2]);
- buffers.weightsIndices.push(faceWeightIndices[(i - 1) * 4 + 3]);
-
- buffers.weightsIndices.push(faceWeightIndices[i * 4]);
- buffers.weightsIndices.push(faceWeightIndices[i * 4 + 1]);
- buffers.weightsIndices.push(faceWeightIndices[i * 4 + 2]);
- buffers.weightsIndices.push(faceWeightIndices[i * 4 + 3]);
- }
-
- if (geoInfo.color) {
-
- buffers.colors.push(faceColors[0]);
- buffers.colors.push(faceColors[1]);
- buffers.colors.push(faceColors[2]);
-
- buffers.colors.push(faceColors[(i - 1) * 3]);
- buffers.colors.push(faceColors[(i - 1) * 3 + 1]);
- buffers.colors.push(faceColors[(i - 1) * 3 + 2]);
-
- buffers.colors.push(faceColors[i * 3]);
- buffers.colors.push(faceColors[i * 3 + 1]);
- buffers.colors.push(faceColors[i * 3 + 2]);
- }
-
- if (geoInfo.material && geoInfo.material.mappingType !== 'AllSame') {
-
- buffers.materialIndex.push(materialIndex);
- buffers.materialIndex.push(materialIndex);
- buffers.materialIndex.push(materialIndex);
- }
-
- if (geoInfo.normal) {
-
- buffers.normal.push(faceNormals[0]);
- buffers.normal.push(faceNormals[1]);
- buffers.normal.push(faceNormals[2]);
-
- buffers.normal.push(faceNormals[(i - 1) * 3]);
- buffers.normal.push(faceNormals[(i - 1) * 3 + 1]);
- buffers.normal.push(faceNormals[(i - 1) * 3 + 2]);
-
- buffers.normal.push(faceNormals[i * 3]);
- buffers.normal.push(faceNormals[i * 3 + 1]);
- buffers.normal.push(faceNormals[i * 3 + 2]);
- }
-
- if (geoInfo.uv) {
-
- geoInfo.uv.forEach(function (uv, j) {
-
- if (buffers.uvs[j] === undefined) buffers.uvs[j] = [];
-
- buffers.uvs[j].push(faceUVs[j][0]);
- buffers.uvs[j].push(faceUVs[j][1]);
-
- buffers.uvs[j].push(faceUVs[j][(i - 1) * 2]);
- buffers.uvs[j].push(faceUVs[j][(i - 1) * 2 + 1]);
-
- buffers.uvs[j].push(faceUVs[j][i * 2]);
- buffers.uvs[j].push(faceUVs[j][i * 2 + 1]);
- });
- }
- }
- },
-
- addMorphTargets: function addMorphTargets(parentGeo, parentGeoNode, morphTarget, preTransform) {
-
- if (morphTarget === null) return;
-
- parentGeo.morphAttributes.position = [];
- parentGeo.morphAttributes.normal = [];
-
- var self = this;
- morphTarget.rawTargets.forEach(function (rawTarget) {
-
- var morphGeoNode = fbxTree.Objects.Geometry[rawTarget.geoID];
-
- if (morphGeoNode !== undefined) {
-
- self.genMorphGeometry(parentGeo, parentGeoNode, morphGeoNode, preTransform);
- }
- });
- },
-
- // a morph geometry node is similar to a standard node, and the node is also contained
- // in FBXTree.Objects.Geometry, however it can only have attributes for position, normal
- // and a special attribute Index defining which vertices of the original geometry are affected
- // Normal and position attributes only have data for the vertices that are affected by the morph
- genMorphGeometry: function genMorphGeometry(parentGeo, parentGeoNode, morphGeoNode, preTransform) {
-
- var morphGeo = new THREE.BufferGeometry();
- if (morphGeoNode.attrName) morphGeo.name = morphGeoNode.attrName;
-
- var vertexIndices = parentGeoNode.PolygonVertexIndex !== undefined ? parentGeoNode.PolygonVertexIndex.a : [];
-
- // make a copy of the parent's vertex positions
- var vertexPositions = parentGeoNode.Vertices !== undefined ? parentGeoNode.Vertices.a.slice() : [];
-
- var morphPositions = morphGeoNode.Vertices !== undefined ? morphGeoNode.Vertices.a : [];
- var indices = morphGeoNode.Indexes !== undefined ? morphGeoNode.Indexes.a : [];
-
- for (var i = 0; i < indices.length; i++) {
-
- var morphIndex = indices[i] * 3;
-
- // FBX format uses blend shapes rather than morph targets. This can be converted
- // by additively combining the blend shape positions with the original geometry's positions
- vertexPositions[morphIndex] += morphPositions[i * 3];
- vertexPositions[morphIndex + 1] += morphPositions[i * 3 + 1];
- vertexPositions[morphIndex + 2] += morphPositions[i * 3 + 2];
- }
-
- // TODO: add morph normal support
- var morphGeoInfo = {
- vertexIndices: vertexIndices,
- vertexPositions: vertexPositions
- };
-
- var morphBuffers = this.genBuffers(morphGeoInfo);
-
- var positionAttribute = new THREE.Float32BufferAttribute(morphBuffers.vertex, 3);
- positionAttribute.name = morphGeoNode.attrName;
-
- preTransform.applyToBufferAttribute(positionAttribute);
-
- parentGeo.morphAttributes.position.push(positionAttribute);
- },
-
- // Parse normal from FBXTree.Objects.Geometry.LayerElementNormal if it exists
- parseNormals: function parseNormals(NormalNode) {
-
- var mappingType = NormalNode.MappingInformationType;
- var referenceType = NormalNode.ReferenceInformationType;
- var buffer = NormalNode.Normals.a;
- var indexBuffer = [];
- if (referenceType === 'IndexToDirect') {
-
- if ('NormalIndex' in NormalNode) {
-
- indexBuffer = NormalNode.NormalIndex.a;
- } else if ('NormalsIndex' in NormalNode) {
-
- indexBuffer = NormalNode.NormalsIndex.a;
- }
- }
-
- return {
- dataSize: 3,
- buffer: buffer,
- indices: indexBuffer,
- mappingType: mappingType,
- referenceType: referenceType
- };
- },
-
- // Parse UVs from FBXTree.Objects.Geometry.LayerElementUV if it exists
- parseUVs: function parseUVs(UVNode) {
-
- var mappingType = UVNode.MappingInformationType;
- var referenceType = UVNode.ReferenceInformationType;
- var buffer = UVNode.UV.a;
- var indexBuffer = [];
- if (referenceType === 'IndexToDirect') {
-
- indexBuffer = UVNode.UVIndex.a;
- }
-
- return {
- dataSize: 2,
- buffer: buffer,
- indices: indexBuffer,
- mappingType: mappingType,
- referenceType: referenceType
- };
- },
-
- // Parse Vertex Colors from FBXTree.Objects.Geometry.LayerElementColor if it exists
- parseVertexColors: function parseVertexColors(ColorNode) {
-
- var mappingType = ColorNode.MappingInformationType;
- var referenceType = ColorNode.ReferenceInformationType;
- var buffer = ColorNode.Colors.a;
- var indexBuffer = [];
- if (referenceType === 'IndexToDirect') {
-
- indexBuffer = ColorNode.ColorIndex.a;
- }
-
- return {
- dataSize: 4,
- buffer: buffer,
- indices: indexBuffer,
- mappingType: mappingType,
- referenceType: referenceType
- };
- },
-
- // Parse mapping and material data in FBXTree.Objects.Geometry.LayerElementMaterial if it exists
- parseMaterialIndices: function parseMaterialIndices(MaterialNode) {
-
- var mappingType = MaterialNode.MappingInformationType;
- var referenceType = MaterialNode.ReferenceInformationType;
-
- if (mappingType === 'NoMappingInformation') {
-
- return {
- dataSize: 1,
- buffer: [0],
- indices: [0],
- mappingType: 'AllSame',
- referenceType: referenceType
- };
- }
-
- var materialIndexBuffer = MaterialNode.Materials.a;
-
- // Since materials are stored as indices, there's a bit of a mismatch between FBX and what
- // we expect.So we create an intermediate buffer that points to the index in the buffer,
- // for conforming with the other functions we've written for other data.
- var materialIndices = [];
-
- for (var i = 0; i < materialIndexBuffer.length; ++i) {
-
- materialIndices.push(i);
- }
-
- return {
- dataSize: 1,
- buffer: materialIndexBuffer,
- indices: materialIndices,
- mappingType: mappingType,
- referenceType: referenceType
- };
- },
-
- // Generate a NurbGeometry from a node in FBXTree.Objects.Geometry
- parseNurbsGeometry: function parseNurbsGeometry(geoNode) {
-
- if (THREE.NURBSCurve === undefined) {
-
- console.error('THREE.FBXLoader: The loader relies on THREE.NURBSCurve for any nurbs present in the model. Nurbs will show up as empty geometry.');
- return new THREE.BufferGeometry();
- }
-
- var order = parseInt(geoNode.Order);
-
- if (isNaN(order)) {
-
- console.error('THREE.FBXLoader: Invalid Order %s given for geometry ID: %s', geoNode.Order, geoNode.id);
- return new THREE.BufferGeometry();
- }
-
- var degree = order - 1;
-
- var knots = geoNode.KnotVector.a;
- var controlPoints = [];
- var pointsValues = geoNode.Points.a;
-
- for (var i = 0, l = pointsValues.length; i < l; i += 4) {
-
- controlPoints.push(new THREE.Vector4().fromArray(pointsValues, i));
- }
-
- var startKnot, endKnot;
-
- if (geoNode.Form === 'Closed') {
-
- controlPoints.push(controlPoints[0]);
- } else if (geoNode.Form === 'Periodic') {
-
- startKnot = degree;
- endKnot = knots.length - 1 - startKnot;
-
- for (var i = 0; i < degree; ++i) {
-
- controlPoints.push(controlPoints[i]);
- }
- }
-
- var curve = new THREE.NURBSCurve(degree, knots, controlPoints, startKnot, endKnot);
- var vertices = curve.getPoints(controlPoints.length * 7);
-
- var positions = new Float32Array(vertices.length * 3);
-
- vertices.forEach(function (vertex, i) {
-
- vertex.toArray(positions, i * 3);
- });
-
- var geometry = new THREE.BufferGeometry();
- geometry.addAttribute('position', new THREE.BufferAttribute(positions, 3));
-
- return geometry;
- }
-
- };
-
- // parse animation data from FBXTree
- function AnimationParser() {}
-
- AnimationParser.prototype = {
-
- constructor: AnimationParser,
-
- // take raw animation clips and turn them into three.js animation clips
- parse: function parse() {
-
- var animationClips = [];
-
- var rawClips = this.parseClips();
-
- if (rawClips === undefined) return animationClips;
-
- for (var key in rawClips) {
-
- var rawClip = rawClips[key];
-
- var clip = this.addClip(rawClip);
-
- animationClips.push(clip);
- }
-
- return animationClips;
- },
-
- parseClips: function parseClips() {
-
- // since the actual transformation data is stored in FBXTree.Objects.AnimationCurve,
- // if this is undefined we can safely assume there are no animations
- if (fbxTree.Objects.AnimationCurve === undefined) return undefined;
-
- var curveNodesMap = this.parseAnimationCurveNodes();
-
- this.parseAnimationCurves(curveNodesMap);
-
- var layersMap = this.parseAnimationLayers(curveNodesMap);
- var rawClips = this.parseAnimStacks(layersMap);
-
- return rawClips;
- },
-
- // parse nodes in FBXTree.Objects.AnimationCurveNode
- // each AnimationCurveNode holds data for an animation transform for a model (e.g. left arm rotation )
- // and is referenced by an AnimationLayer
- parseAnimationCurveNodes: function parseAnimationCurveNodes() {
-
- var rawCurveNodes = fbxTree.Objects.AnimationCurveNode;
-
- var curveNodesMap = new Map();
-
- for (var nodeID in rawCurveNodes) {
-
- var rawCurveNode = rawCurveNodes[nodeID];
-
- if (rawCurveNode.attrName.match(/S|R|T|DeformPercent/) !== null) {
-
- var curveNode = {
-
- id: rawCurveNode.id,
- attr: rawCurveNode.attrName,
- curves: {}
-
- };
-
- curveNodesMap.set(curveNode.id, curveNode);
- }
- }
-
- return curveNodesMap;
- },
-
- // parse nodes in FBXTree.Objects.AnimationCurve and connect them up to
- // previously parsed AnimationCurveNodes. Each AnimationCurve holds data for a single animated
- // axis ( e.g. times and values of x rotation)
- parseAnimationCurves: function parseAnimationCurves(curveNodesMap) {
-
- var rawCurves = fbxTree.Objects.AnimationCurve;
-
- // TODO: Many values are identical up to roundoff error, but won't be optimised
- // e.g. position times: [0, 0.4, 0. 8]
- // position values: [7.23538335023477e-7, 93.67518615722656, -0.9982695579528809, 7.23538335023477e-7, 93.67518615722656, -0.9982695579528809, 7.235384487103147e-7, 93.67520904541016, -0.9982695579528809]
- // clearly, this should be optimised to
- // times: [0], positions [7.23538335023477e-7, 93.67518615722656, -0.9982695579528809]
- // this shows up in nearly every FBX file, and generally time array is length > 100
-
- for (var nodeID in rawCurves) {
-
- var animationCurve = {
-
- id: rawCurves[nodeID].id,
- times: rawCurves[nodeID].KeyTime.a.map(convertFBXTimeToSeconds),
- values: rawCurves[nodeID].KeyValueFloat.a
-
- };
-
- var relationships = connections.get(animationCurve.id);
-
- if (relationships !== undefined) {
-
- var animationCurveID = relationships.parents[0].ID;
- var animationCurveRelationship = relationships.parents[0].relationship;
-
- if (animationCurveRelationship.match(/X/)) {
-
- curveNodesMap.get(animationCurveID).curves['x'] = animationCurve;
- } else if (animationCurveRelationship.match(/Y/)) {
-
- curveNodesMap.get(animationCurveID).curves['y'] = animationCurve;
- } else if (animationCurveRelationship.match(/Z/)) {
-
- curveNodesMap.get(animationCurveID).curves['z'] = animationCurve;
- } else if (animationCurveRelationship.match(/d|DeformPercent/) && curveNodesMap.has(animationCurveID)) {
-
- curveNodesMap.get(animationCurveID).curves['morph'] = animationCurve;
- }
- }
- }
- },
-
- // parse nodes in FBXTree.Objects.AnimationLayer. Each layers holds references
- // to various AnimationCurveNodes and is referenced by an AnimationStack node
- // note: theoretically a stack can have multiple layers, however in practice there always seems to be one per stack
- parseAnimationLayers: function parseAnimationLayers(curveNodesMap) {
-
- var rawLayers = fbxTree.Objects.AnimationLayer;
-
- var layersMap = new Map();
-
- for (var nodeID in rawLayers) {
-
- var layerCurveNodes = [];
-
- var connection = connections.get(parseInt(nodeID));
-
- if (connection !== undefined) {
-
- // all the animationCurveNodes used in the layer
- var children = connection.children;
-
- var self = this;
- children.forEach(function (child, i) {
-
- if (curveNodesMap.has(child.ID)) {
-
- var curveNode = curveNodesMap.get(child.ID);
-
- // check that the curves are defined for at least one axis, otherwise ignore the curveNode
- if (curveNode.curves.x !== undefined || curveNode.curves.y !== undefined || curveNode.curves.z !== undefined) {
-
- if (layerCurveNodes[i] === undefined) {
-
- var modelID;
-
- connections.get(child.ID).parents.forEach(function (parent) {
-
- if (parent.relationship !== undefined) modelID = parent.ID;
- });
-
- var rawModel = fbxTree.Objects.Model[modelID.toString()];
-
- var node = {
-
- modelName: THREE.PropertyBinding.sanitizeNodeName(rawModel.attrName),
- initialPosition: [0, 0, 0],
- initialRotation: [0, 0, 0],
- initialScale: [1, 1, 1],
- transform: self.getModelAnimTransform(rawModel)
-
- };
-
- // if the animated model is pre rotated, we'll have to apply the pre rotations to every
- // animation value as well
- if ('PreRotation' in rawModel) node.preRotations = rawModel.PreRotation.value;
- if ('PostRotation' in rawModel) node.postRotations = rawModel.PostRotation.value;
-
- layerCurveNodes[i] = node;
- }
-
- layerCurveNodes[i][curveNode.attr] = curveNode;
- } else if (curveNode.curves.morph !== undefined) {
-
- if (layerCurveNodes[i] === undefined) {
-
- var deformerID;
-
- connections.get(child.ID).parents.forEach(function (parent) {
-
- if (parent.relationship !== undefined) deformerID = parent.ID;
- });
-
- var morpherID = connections.get(deformerID).parents[0].ID;
- var geoID = connections.get(morpherID).parents[0].ID;
-
- // assuming geometry is not used in more than one model
- var modelID = connections.get(geoID).parents[0].ID;
-
- var rawModel = fbxTree.Objects.Model[modelID];
-
- var node = {
-
- modelName: THREE.PropertyBinding.sanitizeNodeName(rawModel.attrName),
- morphName: fbxTree.Objects.Deformer[deformerID].attrName
-
- };
-
- layerCurveNodes[i] = node;
- }
-
- layerCurveNodes[i][curveNode.attr] = curveNode;
- }
- }
- });
-
- layersMap.set(parseInt(nodeID), layerCurveNodes);
- }
- }
-
- return layersMap;
- },
-
- getModelAnimTransform: function getModelAnimTransform(modelNode) {
-
- var transformData = {};
-
- if ('RotationOrder' in modelNode) transformData.eulerOrder = parseInt(modelNode.RotationOrder.value);
-
- if ('Lcl_Translation' in modelNode) transformData.translation = modelNode.Lcl_Translation.value;
- if ('RotationOffset' in modelNode) transformData.rotationOffset = modelNode.RotationOffset.value;
-
- if ('Lcl_Rotation' in modelNode) transformData.rotation = modelNode.Lcl_Rotation.value;
- if ('PreRotation' in modelNode) transformData.preRotation = modelNode.PreRotation.value;
-
- if ('PostRotation' in modelNode) transformData.postRotation = modelNode.PostRotation.value;
-
- if ('Lcl_Scaling' in modelNode) transformData.scale = modelNode.Lcl_Scaling.value;
-
- return generateTransform(transformData);
- },
-
- // parse nodes in FBXTree.Objects.AnimationStack. These are the top level node in the animation
- // hierarchy. Each Stack node will be used to create a THREE.AnimationClip
- parseAnimStacks: function parseAnimStacks(layersMap) {
-
- var rawStacks = fbxTree.Objects.AnimationStack;
-
- // connect the stacks (clips) up to the layers
- var rawClips = {};
-
- for (var nodeID in rawStacks) {
-
- var children = connections.get(parseInt(nodeID)).children;
-
- if (children.length > 1) {
-
- // it seems like stacks will always be associated with a single layer. But just in case there are files
- // where there are multiple layers per stack, we'll display a warning
- console.warn('THREE.FBXLoader: Encountered an animation stack with multiple layers, this is currently not supported. Ignoring subsequent layers.');
- }
-
- var layer = layersMap.get(children[0].ID);
-
- rawClips[nodeID] = {
-
- name: rawStacks[nodeID].attrName,
- layer: layer
-
- };
- }
-
- return rawClips;
- },
-
- addClip: function addClip(rawClip) {
-
- var tracks = [];
-
- var self = this;
- rawClip.layer.forEach(function (rawTracks) {
-
- tracks = tracks.concat(self.generateTracks(rawTracks));
- });
-
- return new THREE.AnimationClip(rawClip.name, -1, tracks);
- },
-
- generateTracks: function generateTracks(rawTracks) {
-
- var tracks = [];
-
- var initialPosition = new THREE.Vector3();
- var initialRotation = new THREE.Quaternion();
- var initialScale = new THREE.Vector3();
-
- if (rawTracks.transform) rawTracks.transform.decompose(initialPosition, initialRotation, initialScale);
-
- initialPosition = initialPosition.toArray();
- initialRotation = new THREE.Euler().setFromQuaternion(initialRotation).toArray(); // todo: euler order
- initialScale = initialScale.toArray();
-
- if (rawTracks.T !== undefined && Object.keys(rawTracks.T.curves).length > 0) {
-
- var positionTrack = this.generateVectorTrack(rawTracks.modelName, rawTracks.T.curves, initialPosition, 'position');
- if (positionTrack !== undefined) tracks.push(positionTrack);
- }
-
- if (rawTracks.R !== undefined && Object.keys(rawTracks.R.curves).length > 0) {
-
- var rotationTrack = this.generateRotationTrack(rawTracks.modelName, rawTracks.R.curves, initialRotation, rawTracks.preRotations, rawTracks.postRotations);
- if (rotationTrack !== undefined) tracks.push(rotationTrack);
- }
-
- if (rawTracks.S !== undefined && Object.keys(rawTracks.S.curves).length > 0) {
-
- var scaleTrack = this.generateVectorTrack(rawTracks.modelName, rawTracks.S.curves, initialScale, 'scale');
- if (scaleTrack !== undefined) tracks.push(scaleTrack);
- }
-
- if (rawTracks.DeformPercent !== undefined) {
-
- var morphTrack = this.generateMorphTrack(rawTracks);
- if (morphTrack !== undefined) tracks.push(morphTrack);
- }
-
- return tracks;
- },
-
- generateVectorTrack: function generateVectorTrack(modelName, curves, initialValue, type) {
-
- var times = this.getTimesForAllAxes(curves);
- var values = this.getKeyframeTrackValues(times, curves, initialValue);
-
- return new THREE.VectorKeyframeTrack(modelName + '.' + type, times, values);
- },
-
- generateRotationTrack: function generateRotationTrack(modelName, curves, initialValue, preRotations, postRotations) {
-
- if (curves.x !== undefined) {
-
- this.interpolateRotations(curves.x);
- curves.x.values = curves.x.values.map(THREE.Math.degToRad);
- }
- if (curves.y !== undefined) {
-
- this.interpolateRotations(curves.y);
- curves.y.values = curves.y.values.map(THREE.Math.degToRad);
- }
- if (curves.z !== undefined) {
-
- this.interpolateRotations(curves.z);
- curves.z.values = curves.z.values.map(THREE.Math.degToRad);
- }
-
- var times = this.getTimesForAllAxes(curves);
- var values = this.getKeyframeTrackValues(times, curves, initialValue);
-
- if (preRotations !== undefined) {
-
- preRotations = preRotations.map(THREE.Math.degToRad);
- preRotations.push('ZYX');
-
- preRotations = new THREE.Euler().fromArray(preRotations);
- preRotations = new THREE.Quaternion().setFromEuler(preRotations);
- }
-
- if (postRotations !== undefined) {
-
- postRotations = postRotations.map(THREE.Math.degToRad);
- postRotations.push('ZYX');
-
- postRotations = new THREE.Euler().fromArray(postRotations);
- postRotations = new THREE.Quaternion().setFromEuler(postRotations).inverse();
- }
-
- var quaternion = new THREE.Quaternion();
- var euler = new THREE.Euler();
-
- var quaternionValues = [];
-
- for (var i = 0; i < values.length; i += 3) {
-
- euler.set(values[i], values[i + 1], values[i + 2], 'ZYX');
-
- quaternion.setFromEuler(euler);
-
- if (preRotations !== undefined) quaternion.premultiply(preRotations);
- if (postRotations !== undefined) quaternion.multiply(postRotations);
-
- quaternion.toArray(quaternionValues, i / 3 * 4);
- }
-
- return new THREE.QuaternionKeyframeTrack(modelName + '.quaternion', times, quaternionValues);
- },
-
- generateMorphTrack: function generateMorphTrack(rawTracks) {
-
- var curves = rawTracks.DeformPercent.curves.morph;
- var values = curves.values.map(function (val) {
-
- return val / 100;
- });
-
- var morphNum = sceneGraph.getObjectByName(rawTracks.modelName).morphTargetDictionary[rawTracks.morphName];
-
- return new THREE.NumberKeyframeTrack(rawTracks.modelName + '.morphTargetInfluences[' + morphNum + ']', curves.times, values);
- },
-
- // For all animated objects, times are defined separately for each axis
- // Here we'll combine the times into one sorted array without duplicates
- getTimesForAllAxes: function getTimesForAllAxes(curves) {
-
- var times = [];
-
- // first join together the times for each axis, if defined
- if (curves.x !== undefined) times = times.concat(curves.x.times);
- if (curves.y !== undefined) times = times.concat(curves.y.times);
- if (curves.z !== undefined) times = times.concat(curves.z.times);
-
- // then sort them and remove duplicates
- times = times.sort(function (a, b) {
-
- return a - b;
- }).filter(function (elem, index, array) {
-
- return array.indexOf(elem) == index;
- });
-
- return times;
- },
-
- getKeyframeTrackValues: function getKeyframeTrackValues(times, curves, initialValue) {
-
- var prevValue = initialValue;
-
- var values = [];
-
- var xIndex = -1;
- var yIndex = -1;
- var zIndex = -1;
-
- times.forEach(function (time) {
-
- if (curves.x) xIndex = curves.x.times.indexOf(time);
- if (curves.y) yIndex = curves.y.times.indexOf(time);
- if (curves.z) zIndex = curves.z.times.indexOf(time);
-
- // if there is an x value defined for this frame, use that
- if (xIndex !== -1) {
-
- var xValue = curves.x.values[xIndex];
- values.push(xValue);
- prevValue[0] = xValue;
- } else {
-
- // otherwise use the x value from the previous frame
- values.push(prevValue[0]);
- }
-
- if (yIndex !== -1) {
-
- var yValue = curves.y.values[yIndex];
- values.push(yValue);
- prevValue[1] = yValue;
- } else {
-
- values.push(prevValue[1]);
- }
-
- if (zIndex !== -1) {
-
- var zValue = curves.z.values[zIndex];
- values.push(zValue);
- prevValue[2] = zValue;
- } else {
-
- values.push(prevValue[2]);
- }
- });
-
- return values;
- },
-
- // Rotations are defined as Euler angles which can have values of any size
- // These will be converted to quaternions which don't support values greater than
- // PI, so we'll interpolate large rotations
- interpolateRotations: function interpolateRotations(curve) {
-
- for (var i = 1; i < curve.values.length; i++) {
-
- var initialValue = curve.values[i - 1];
- var valuesSpan = curve.values[i] - initialValue;
-
- var absoluteSpan = Math.abs(valuesSpan);
-
- if (absoluteSpan >= 180) {
-
- var numSubIntervals = absoluteSpan / 180;
-
- var step = valuesSpan / numSubIntervals;
- var nextValue = initialValue + step;
-
- var initialTime = curve.times[i - 1];
- var timeSpan = curve.times[i] - initialTime;
- var interval = timeSpan / numSubIntervals;
- var nextTime = initialTime + interval;
-
- var interpolatedTimes = [];
- var interpolatedValues = [];
-
- while (nextTime < curve.times[i]) {
-
- interpolatedTimes.push(nextTime);
- nextTime += interval;
-
- interpolatedValues.push(nextValue);
- nextValue += step;
- }
-
- curve.times = inject(curve.times, i, interpolatedTimes);
- curve.values = inject(curve.values, i, interpolatedValues);
- }
- }
- }
-
- };
-
- // parse an FBX file in ASCII format
- function TextParser() {}
-
- TextParser.prototype = {
-
- constructor: TextParser,
-
- getPrevNode: function getPrevNode() {
-
- return this.nodeStack[this.currentIndent - 2];
- },
-
- getCurrentNode: function getCurrentNode() {
-
- return this.nodeStack[this.currentIndent - 1];
- },
-
- getCurrentProp: function getCurrentProp() {
-
- return this.currentProp;
- },
-
- pushStack: function pushStack(node) {
-
- this.nodeStack.push(node);
- this.currentIndent += 1;
- },
-
- popStack: function popStack() {
-
- this.nodeStack.pop();
- this.currentIndent -= 1;
- },
-
- setCurrentProp: function setCurrentProp(val, name) {
-
- this.currentProp = val;
- this.currentPropName = name;
- },
-
- parse: function parse(text) {
-
- this.currentIndent = 0;
- console.log("FBXTree: ", FBXTree);
- this.allNodes = new FBXTree();
- this.nodeStack = [];
- this.currentProp = [];
- this.currentPropName = '';
-
- var self = this;
-
- var split = text.split(/[\r\n]+/);
-
- split.forEach(function (line, i) {
-
- var matchComment = line.match(/^[\s\t]*;/);
- var matchEmpty = line.match(/^[\s\t]*$/);
-
- if (matchComment || matchEmpty) return;
-
- var matchBeginning = line.match('^\\t{' + self.currentIndent + '}(\\w+):(.*){', '');
- var matchProperty = line.match('^\\t{' + self.currentIndent + '}(\\w+):[\\s\\t\\r\\n](.*)');
- var matchEnd = line.match('^\\t{' + (self.currentIndent - 1) + '}}');
-
- if (matchBeginning) {
-
- self.parseNodeBegin(line, matchBeginning);
- } else if (matchProperty) {
-
- self.parseNodeProperty(line, matchProperty, split[++i]);
- } else if (matchEnd) {
-
- self.popStack();
- } else if (line.match(/^[^\s\t}]/)) {
-
- // large arrays are split over multiple lines terminated with a ',' character
- // if this is encountered the line needs to be joined to the previous line
- self.parseNodePropertyContinued(line);
- }
- });
-
- return this.allNodes;
- },
-
- parseNodeBegin: function parseNodeBegin(line, property) {
-
- var nodeName = property[1].trim().replace(/^"/, '').replace(/"$/, '');
-
- var nodeAttrs = property[2].split(',').map(function (attr) {
-
- return attr.trim().replace(/^"/, '').replace(/"$/, '');
- });
-
- var node = { name: nodeName };
- var attrs = this.parseNodeAttr(nodeAttrs);
-
- var currentNode = this.getCurrentNode();
-
- // a top node
- if (this.currentIndent === 0) {
-
- this.allNodes.add(nodeName, node);
- } else {
- // a subnode
-
- // if the subnode already exists, append it
- if (nodeName in currentNode) {
-
- // special case Pose needs PoseNodes as an array
- if (nodeName === 'PoseNode') {
-
- currentNode.PoseNode.push(node);
- } else if (currentNode[nodeName].id !== undefined) {
-
- currentNode[nodeName] = {};
- currentNode[nodeName][currentNode[nodeName].id] = currentNode[nodeName];
- }
-
- if (attrs.id !== '') currentNode[nodeName][attrs.id] = node;
- } else if (typeof attrs.id === 'number') {
-
- currentNode[nodeName] = {};
- currentNode[nodeName][attrs.id] = node;
- } else if (nodeName !== 'Properties70') {
-
- if (nodeName === 'PoseNode') currentNode[nodeName] = [node];else currentNode[nodeName] = node;
- }
- }
-
- if (typeof attrs.id === 'number') node.id = attrs.id;
- if (attrs.name !== '') node.attrName = attrs.name;
- if (attrs.type !== '') node.attrType = attrs.type;
-
- this.pushStack(node);
- },
-
- parseNodeAttr: function parseNodeAttr(attrs) {
-
- var id = attrs[0];
-
- if (attrs[0] !== '') {
-
- id = parseInt(attrs[0]);
-
- if (isNaN(id)) {
-
- id = attrs[0];
- }
- }
-
- var name = '',
- type = '';
-
- if (attrs.length > 1) {
-
- name = attrs[1].replace(/^(\w+)::/, '');
- type = attrs[2];
- }
-
- return { id: id, name: name, type: type };
- },
-
- parseNodeProperty: function parseNodeProperty(line, property, contentLine) {
-
- var propName = property[1].replace(/^"/, '').replace(/"$/, '').trim();
- var propValue = property[2].replace(/^"/, '').replace(/"$/, '').trim();
-
- // for special case: base64 image data follows "Content: ," line
- // Content: ,
- // "/9j/4RDaRXhpZgAATU0A..."
- if (propName === 'Content' && propValue === ',') {
-
- propValue = contentLine.replace(/"/g, '').replace(/,$/, '').trim();
- }
-
- var currentNode = this.getCurrentNode();
- var parentName = currentNode.name;
-
- if (parentName === 'Properties70') {
-
- this.parseNodeSpecialProperty(line, propName, propValue);
- return;
- }
-
- // Connections
- if (propName === 'C') {
-
- var connProps = propValue.split(',').slice(1);
- var from = parseInt(connProps[0]);
- var to = parseInt(connProps[1]);
-
- var rest = propValue.split(',').slice(3);
-
- rest = rest.map(function (elem) {
-
- return elem.trim().replace(/^"/, '');
- });
-
- propName = 'connections';
- propValue = [from, to];
- append(propValue, rest);
-
- if (currentNode[propName] === undefined) {
-
- currentNode[propName] = [];
- }
- }
-
- // Node
- if (propName === 'Node') currentNode.id = propValue;
-
- // connections
- if (propName in currentNode && Array.isArray(currentNode[propName])) {
-
- currentNode[propName].push(propValue);
- } else {
-
- if (propName !== 'a') currentNode[propName] = propValue;else currentNode.a = propValue;
- }
-
- this.setCurrentProp(currentNode, propName);
-
- // convert string to array, unless it ends in ',' in which case more will be added to it
- if (propName === 'a' && propValue.slice(-1) !== ',') {
-
- currentNode.a = parseNumberArray(propValue);
- }
- },
-
- parseNodePropertyContinued: function parseNodePropertyContinued(line) {
-
- var currentNode = this.getCurrentNode();
-
- currentNode.a += line;
-
- // if the line doesn't end in ',' we have reached the end of the property value
- // so convert the string to an array
- if (line.slice(-1) !== ',') {
-
- currentNode.a = parseNumberArray(currentNode.a);
- }
- },
-
- // parse "Property70"
- parseNodeSpecialProperty: function parseNodeSpecialProperty(line, propName, propValue) {
-
- // split this
- // P: "Lcl Scaling", "Lcl Scaling", "", "A",1,1,1
- // into array like below
- // ["Lcl Scaling", "Lcl Scaling", "", "A", "1,1,1" ]
- var props = propValue.split('",').map(function (prop) {
-
- return prop.trim().replace(/^\"/, '').replace(/\s/, '_');
- });
-
- var innerPropName = props[0];
- var innerPropType1 = props[1];
- var innerPropType2 = props[2];
- var innerPropFlag = props[3];
- var innerPropValue = props[4];
-
- // cast values where needed, otherwise leave as strings
- switch (innerPropType1) {
-
- case 'int':
- case 'enum':
- case 'bool':
- case 'ULongLong':
- case 'double':
- case 'Number':
- case 'FieldOfView':
- innerPropValue = parseFloat(innerPropValue);
- break;
-
- case 'Color':
- case 'ColorRGB':
- case 'Vector3D':
- case 'Lcl_Translation':
- case 'Lcl_Rotation':
- case 'Lcl_Scaling':
- innerPropValue = parseNumberArray(innerPropValue);
- break;
-
- }
-
- // CAUTION: these props must append to parent's parent
- this.getPrevNode()[innerPropName] = {
-
- 'type': innerPropType1,
- 'type2': innerPropType2,
- 'flag': innerPropFlag,
- 'value': innerPropValue
-
- };
-
- this.setCurrentProp(this.getPrevNode(), innerPropName);
- }
-
- };
-
- // Parse an FBX file in Binary format
- function BinaryParser() {}
-
- BinaryParser.prototype = {
-
- constructor: BinaryParser,
-
- parse: function parse(buffer) {
-
- var reader = new BinaryReader(buffer);
- reader.skip(23); // skip magic 23 bytes
-
- var version = reader.getUint32();
-
- console.log('THREE.FBXLoader: FBX binary version: ' + version);
-
- var allNodes = new FBXTree();
-
- while (!this.endOfContent(reader)) {
-
- var node = this.parseNode(reader, version);
- if (node !== null) allNodes.add(node.name, node);
- }
-
- return allNodes;
- },
-
- // Check if reader has reached the end of content.
- endOfContent: function endOfContent(reader) {
-
- // footer size: 160bytes + 16-byte alignment padding
- // - 16bytes: magic
- // - padding til 16-byte alignment (at least 1byte?)
- // (seems like some exporters embed fixed 15 or 16bytes?)
- // - 4bytes: magic
- // - 4bytes: version
- // - 120bytes: zero
- // - 16bytes: magic
- if (reader.size() % 16 === 0) {
-
- return (reader.getOffset() + 160 + 16 & ~0xf) >= reader.size();
- } else {
-
- return reader.getOffset() + 160 + 16 >= reader.size();
- }
- },
-
- // recursively parse nodes until the end of the file is reached
- parseNode: function parseNode(reader, version) {
-
- var node = {};
-
- // The first three data sizes depends on version.
- var endOffset = version >= 7500 ? reader.getUint64() : reader.getUint32();
- var numProperties = version >= 7500 ? reader.getUint64() : reader.getUint32();
-
- // note: do not remove this even if you get a linter warning as it moves the buffer forward
- var propertyListLen = version >= 7500 ? reader.getUint64() : reader.getUint32();
-
- var nameLen = reader.getUint8();
- var name = reader.getString(nameLen);
-
- // Regards this node as NULL-record if endOffset is zero
- if (endOffset === 0) return null;
-
- var propertyList = [];
-
- for (var i = 0; i < numProperties; i++) {
-
- propertyList.push(this.parseProperty(reader));
- }
-
- // Regards the first three elements in propertyList as id, attrName, and attrType
- var id = propertyList.length > 0 ? propertyList[0] : '';
- var attrName = propertyList.length > 1 ? propertyList[1] : '';
- var attrType = propertyList.length > 2 ? propertyList[2] : '';
-
- // check if this node represents just a single property
- // like (name, 0) set or (name2, [0, 1, 2]) set of {name: 0, name2: [0, 1, 2]}
- node.singleProperty = numProperties === 1 && reader.getOffset() === endOffset ? true : false;
-
- while (endOffset > reader.getOffset()) {
-
- var subNode = this.parseNode(reader, version);
-
- if (subNode !== null) this.parseSubNode(name, node, subNode);
- }
-
- node.propertyList = propertyList; // raw property list used by parent
-
- if (typeof id === 'number') node.id = id;
- if (attrName !== '') node.attrName = attrName;
- if (attrType !== '') node.attrType = attrType;
- if (name !== '') node.name = name;
-
- return node;
- },
-
- parseSubNode: function parseSubNode(name, node, subNode) {
-
- // special case: child node is single property
- if (subNode.singleProperty === true) {
-
- var value = subNode.propertyList[0];
-
- if (Array.isArray(value)) {
-
- node[subNode.name] = subNode;
-
- subNode.a = value;
- } else {
-
- node[subNode.name] = value;
- }
- } else if (name === 'Connections' && subNode.name === 'C') {
-
- var array = [];
-
- subNode.propertyList.forEach(function (property, i) {
-
- // first Connection is FBX type (OO, OP, etc.). We'll discard these
- if (i !== 0) array.push(property);
- });
-
- if (node.connections === undefined) {
-
- node.connections = [];
- }
-
- node.connections.push(array);
- } else if (subNode.name === 'Properties70') {
-
- var keys = Object.keys(subNode);
-
- keys.forEach(function (key) {
-
- node[key] = subNode[key];
- });
- } else if (name === 'Properties70' && subNode.name === 'P') {
-
- var innerPropName = subNode.propertyList[0];
- var innerPropType1 = subNode.propertyList[1];
- var innerPropType2 = subNode.propertyList[2];
- var innerPropFlag = subNode.propertyList[3];
- var innerPropValue;
-
- if (innerPropName.indexOf('Lcl ') === 0) innerPropName = innerPropName.replace('Lcl ', 'Lcl_');
- if (innerPropType1.indexOf('Lcl ') === 0) innerPropType1 = innerPropType1.replace('Lcl ', 'Lcl_');
-
- if (innerPropType1 === 'Color' || innerPropType1 === 'ColorRGB' || innerPropType1 === 'Vector' || innerPropType1 === 'Vector3D' || innerPropType1.indexOf('Lcl_') === 0) {
-
- innerPropValue = [subNode.propertyList[4], subNode.propertyList[5], subNode.propertyList[6]];
- } else {
-
- innerPropValue = subNode.propertyList[4];
- }
-
- // this will be copied to parent, see above
- node[innerPropName] = {
-
- 'type': innerPropType1,
- 'type2': innerPropType2,
- 'flag': innerPropFlag,
- 'value': innerPropValue
-
- };
- } else if (node[subNode.name] === undefined) {
-
- if (typeof subNode.id === 'number') {
-
- node[subNode.name] = {};
- node[subNode.name][subNode.id] = subNode;
- } else {
-
- node[subNode.name] = subNode;
- }
- } else {
-
- if (subNode.name === 'PoseNode') {
-
- if (!Array.isArray(node[subNode.name])) {
-
- node[subNode.name] = [node[subNode.name]];
- }
-
- node[subNode.name].push(subNode);
- } else if (node[subNode.name][subNode.id] === undefined) {
-
- node[subNode.name][subNode.id] = subNode;
- }
- }
- },
-
- parseProperty: function parseProperty(reader) {
-
- var type = reader.getString(1);
-
- switch (type) {
-
- case 'C':
- return reader.getBoolean();
-
- case 'D':
- return reader.getFloat64();
-
- case 'F':
- return reader.getFloat32();
-
- case 'I':
- return reader.getInt32();
-
- case 'L':
- return reader.getInt64();
-
- case 'R':
- var length = reader.getUint32();
- return reader.getArrayBuffer(length);
-
- case 'S':
- var length = reader.getUint32();
- return reader.getString(length);
-
- case 'Y':
- return reader.getInt16();
-
- case 'b':
- case 'c':
- case 'd':
- case 'f':
- case 'i':
- case 'l':
-
- var arrayLength = reader.getUint32();
- var encoding = reader.getUint32(); // 0: non-compressed, 1: compressed
- var compressedLength = reader.getUint32();
-
- if (encoding === 0) {
-
- switch (type) {
-
- case 'b':
- case 'c':
- return reader.getBooleanArray(arrayLength);
-
- case 'd':
- return reader.getFloat64Array(arrayLength);
-
- case 'f':
- return reader.getFloat32Array(arrayLength);
-
- case 'i':
- return reader.getInt32Array(arrayLength);
-
- case 'l':
- return reader.getInt64Array(arrayLength);
-
- }
- }
-
- if (typeof Zlib === 'undefined') {
-
- console.error('THREE.FBXLoader: External library Inflate.min.js required, obtain or import from https://github.com/imaya/zlib.js');
- }
-
- var inflate = new Zlib.Inflate(new Uint8Array(reader.getArrayBuffer(compressedLength))); // eslint-disable-line no-undef
- var reader2 = new BinaryReader(inflate.decompress().buffer);
-
- switch (type) {
-
- case 'b':
- case 'c':
- return reader2.getBooleanArray(arrayLength);
-
- case 'd':
- return reader2.getFloat64Array(arrayLength);
-
- case 'f':
- return reader2.getFloat32Array(arrayLength);
-
- case 'i':
- return reader2.getInt32Array(arrayLength);
-
- case 'l':
- return reader2.getInt64Array(arrayLength);
-
- }
-
- default:
- throw new Error('THREE.FBXLoader: Unknown property type ' + type);
-
- }
- }
-
- };
-
- function BinaryReader(buffer, littleEndian) {
-
- this.dv = new DataView(buffer);
- this.offset = 0;
- this.littleEndian = littleEndian !== undefined ? littleEndian : true;
- }
-
- BinaryReader.prototype = {
-
- constructor: BinaryReader,
-
- getOffset: function getOffset() {
-
- return this.offset;
- },
-
- size: function size() {
-
- return this.dv.buffer.byteLength;
- },
-
- skip: function skip(length) {
-
- this.offset += length;
- },
-
- // seems like true/false representation depends on exporter.
- // true: 1 or 'Y'(=0x59), false: 0 or 'T'(=0x54)
- // then sees LSB.
- getBoolean: function getBoolean() {
-
- return (this.getUint8() & 1) === 1;
- },
-
- getBooleanArray: function getBooleanArray(size) {
-
- var a = [];
-
- for (var i = 0; i < size; i++) {
-
- a.push(this.getBoolean());
- }
-
- return a;
- },
-
- getUint8: function getUint8() {
-
- var value = this.dv.getUint8(this.offset);
- this.offset += 1;
- return value;
- },
-
- getInt16: function getInt16() {
-
- var value = this.dv.getInt16(this.offset, this.littleEndian);
- this.offset += 2;
- return value;
- },
-
- getInt32: function getInt32() {
-
- var value = this.dv.getInt32(this.offset, this.littleEndian);
- this.offset += 4;
- return value;
- },
-
- getInt32Array: function getInt32Array(size) {
-
- var a = [];
-
- for (var i = 0; i < size; i++) {
-
- a.push(this.getInt32());
- }
-
- return a;
- },
-
- getUint32: function getUint32() {
-
- var value = this.dv.getUint32(this.offset, this.littleEndian);
- this.offset += 4;
- return value;
- },
-
- // JavaScript doesn't support 64-bit integer so calculate this here
- // 1 << 32 will return 1 so using multiply operation instead here.
- // There's a possibility that this method returns wrong value if the value
- // is out of the range between Number.MAX_SAFE_INTEGER and Number.MIN_SAFE_INTEGER.
- // TODO: safely handle 64-bit integer
- getInt64: function getInt64() {
-
- var low, high;
-
- if (this.littleEndian) {
-
- low = this.getUint32();
- high = this.getUint32();
- } else {
-
- high = this.getUint32();
- low = this.getUint32();
- }
-
- // calculate negative value
- if (high & 0x80000000) {
-
- high = ~high & 0xFFFFFFFF;
- low = ~low & 0xFFFFFFFF;
-
- if (low === 0xFFFFFFFF) high = high + 1 & 0xFFFFFFFF;
-
- low = low + 1 & 0xFFFFFFFF;
-
- return -(high * 0x100000000 + low);
- }
-
- return high * 0x100000000 + low;
- },
-
- getInt64Array: function getInt64Array(size) {
-
- var a = [];
-
- for (var i = 0; i < size; i++) {
-
- a.push(this.getInt64());
- }
-
- return a;
- },
-
- // Note: see getInt64() comment
- getUint64: function getUint64() {
-
- var low, high;
-
- if (this.littleEndian) {
-
- low = this.getUint32();
- high = this.getUint32();
- } else {
-
- high = this.getUint32();
- low = this.getUint32();
- }
-
- return high * 0x100000000 + low;
- },
-
- getFloat32: function getFloat32() {
-
- var value = this.dv.getFloat32(this.offset, this.littleEndian);
- this.offset += 4;
- return value;
- },
-
- getFloat32Array: function getFloat32Array(size) {
-
- var a = [];
-
- for (var i = 0; i < size; i++) {
-
- a.push(this.getFloat32());
- }
-
- return a;
- },
-
- getFloat64: function getFloat64() {
-
- var value = this.dv.getFloat64(this.offset, this.littleEndian);
- this.offset += 8;
- return value;
- },
-
- getFloat64Array: function getFloat64Array(size) {
-
- var a = [];
-
- for (var i = 0; i < size; i++) {
-
- a.push(this.getFloat64());
- }
-
- return a;
- },
-
- getArrayBuffer: function getArrayBuffer(size) {
-
- var value = this.dv.buffer.slice(this.offset, this.offset + size);
- this.offset += size;
- return value;
- },
-
- getString: function getString(size) {
-
- // note: safari 9 doesn't support Uint8Array.indexOf; create intermediate array instead
- var a = [];
-
- for (var i = 0; i < size; i++) {
-
- a[i] = this.getUint8();
- }
-
- var nullByte = a.indexOf(0);
- if (nullByte >= 0) a = a.slice(0, nullByte);
-
- return THREE.LoaderUtils.decodeText(new Uint8Array(a));
- }
-
- };
-
- // FBXTree holds a representation of the FBX data, returned by the TextParser ( FBX ASCII format)
- // and BinaryParser( FBX Binary format)
- function FBXTree() {}
-
- FBXTree.prototype = {
-
- constructor: FBXTree,
-
- add: function add(key, val) {
-
- this[key] = val;
- }
-
- };
-
- // ************** UTILITY FUNCTIONS **************
-
- function isFbxFormatBinary(buffer) {
-
- var CORRECT = 'Kaydara FBX Binary \0';
-
- return buffer.byteLength >= CORRECT.length && CORRECT === convertArrayBufferToString(buffer, 0, CORRECT.length);
- }
-
- function isFbxFormatASCII(text) {
-
- var CORRECT = ['K', 'a', 'y', 'd', 'a', 'r', 'a', '\\', 'F', 'B', 'X', '\\', 'B', 'i', 'n', 'a', 'r', 'y', '\\', '\\'];
-
- var cursor = 0;
-
- function read(offset) {
-
- var result = text[offset - 1];
- text = text.slice(cursor + offset);
- cursor++;
- return result;
- }
-
- for (var i = 0; i < CORRECT.length; ++i) {
-
- var num = read(1);
- if (num === CORRECT[i]) {
-
- return false;
- }
- }
-
- return true;
- }
-
- function getFbxVersion(text) {
-
- var versionRegExp = /FBXVersion: (\d+)/;
- var match = text.match(versionRegExp);
- if (match) {
-
- var version = parseInt(match[1]);
- return version;
- }
- throw new Error('THREE.FBXLoader: Cannot find the version number for the file given.');
- }
-
- // Converts FBX ticks into real time seconds.
- function convertFBXTimeToSeconds(time) {
-
- return time / 46186158000;
- }
-
- var dataArray = [];
-
- // extracts the data from the correct position in the FBX array based on indexing type
- function getData(polygonVertexIndex, polygonIndex, vertexIndex, infoObject) {
-
- var index;
-
- switch (infoObject.mappingType) {
-
- case 'ByPolygonVertex':
- index = polygonVertexIndex;
- break;
- case 'ByPolygon':
- index = polygonIndex;
- break;
- case 'ByVertice':
- index = vertexIndex;
- break;
- case 'AllSame':
- index = infoObject.indices[0];
- break;
- default:
- console.warn('THREE.FBXLoader: unknown attribute mapping type ' + infoObject.mappingType);
-
- }
-
- if (infoObject.referenceType === 'IndexToDirect') index = infoObject.indices[index];
-
- var from = index * infoObject.dataSize;
- var to = from + infoObject.dataSize;
-
- return slice(dataArray, infoObject.buffer, from, to);
- }
-
- var tempMat = new THREE.Matrix4();
- var tempEuler = new THREE.Euler();
- var tempVec = new THREE.Vector3();
- var translation = new THREE.Vector3();
- var rotation = new THREE.Matrix4();
-
- // generate transformation from FBX transform data
- // ref: https://help.autodesk.com/view/FBX/2017/ENU/?guid=__files_GUID_10CDD63C_79C1_4F2D_BB28_AD2BE65A02ED_htm
- // transformData = {
- // eulerOrder: int,
- // translation: [],
- // rotationOffset: [],
- // preRotation
- // rotation
- // postRotation
- // scale
- // }
- // all entries are optional
- function generateTransform(transformData) {
-
- var transform = new THREE.Matrix4();
- translation.set(0, 0, 0);
- rotation.identity();
-
- var order = transformData.eulerOrder ? getEulerOrder(transformData.eulerOrder) : getEulerOrder(0);
-
- if (transformData.translation) translation.fromArray(transformData.translation);
- if (transformData.rotationOffset) translation.add(tempVec.fromArray(transformData.rotationOffset));
-
- if (transformData.rotation) {
-
- var array = transformData.rotation.map(THREE.Math.degToRad);
- array.push(order);
- rotation.makeRotationFromEuler(tempEuler.fromArray(array));
- }
-
- if (transformData.preRotation) {
-
- var array = transformData.preRotation.map(THREE.Math.degToRad);
- array.push(order);
- tempMat.makeRotationFromEuler(tempEuler.fromArray(array));
-
- rotation.premultiply(tempMat);
- }
-
- if (transformData.postRotation) {
-
- var array = transformData.postRotation.map(THREE.Math.degToRad);
- array.push(order);
- tempMat.makeRotationFromEuler(tempEuler.fromArray(array));
-
- tempMat.getInverse(tempMat);
-
- rotation.multiply(tempMat);
- }
-
- if (transformData.scale) transform.scale(tempVec.fromArray(transformData.scale));
-
- transform.setPosition(translation);
- transform.multiply(rotation);
-
- return transform;
- }
-
- // Returns the three.js intrinsic Euler order corresponding to FBX extrinsic Euler order
- // ref: http://help.autodesk.com/view/FBX/2017/ENU/?guid=__cpp_ref_class_fbx_euler_html
- function getEulerOrder(order) {
-
- var enums = ['ZYX', // -> XYZ extrinsic
- 'YZX', // -> XZY extrinsic
- 'XZY', // -> YZX extrinsic
- 'ZXY', // -> YXZ extrinsic
- 'YXZ', // -> ZXY extrinsic
- 'XYZ'];
-
- if (order === 6) {
-
- console.warn('THREE.FBXLoader: unsupported Euler Order: Spherical XYZ. Animations and rotations may be incorrect.');
- return enums[0];
- }
-
- return enums[order];
- }
-
- // Parses comma separated list of numbers and returns them an array.
- // Used internally by the TextParser
- function parseNumberArray(value) {
-
- var array = value.split(',').map(function (val) {
-
- return parseFloat(val);
- });
-
- return array;
- }
-
- function convertArrayBufferToString(buffer, from, to) {
-
- if (from === undefined) from = 0;
- if (to === undefined) to = buffer.byteLength;
-
- return THREE.LoaderUtils.decodeText(new Uint8Array(buffer, from, to));
- }
-
- function append(a, b) {
-
- for (var i = 0, j = a.length, l = b.length; i < l; i++, j++) {
-
- a[j] = b[i];
- }
- }
-
- function slice(a, b, from, to) {
-
- for (var i = from, j = 0; i < to; i++, j++) {
-
- a[j] = b[i];
- }
-
- return a;
- }
-
- // inject array a2 into array a1 at index
- function inject(a1, index, a2) {
-
- return a1.slice(0, index).concat(a2).concat(a1.slice(index));
- }
-
- return FBXLoader;
-}();
-
-},{}],5:[function(require,module,exports){
-"use strict";
-
-module.exports = Object.assign(function GamepadButton() {}, {
- FACE_1: 0,
- FACE_2: 1,
- FACE_3: 2,
- FACE_4: 3,
-
- L_SHOULDER_1: 4,
- R_SHOULDER_1: 5,
- L_SHOULDER_2: 6,
- R_SHOULDER_2: 7,
-
- SELECT: 8,
- START: 9,
-
- DPAD_UP: 12,
- DPAD_DOWN: 13,
- DPAD_LEFT: 14,
- DPAD_RIGHT: 15,
-
- VENDOR: 16
-});
-
-},{}],6:[function(require,module,exports){
-"use strict";
-
-function GamepadButtonEvent(type, index, details) {
- this.type = type;
- this.index = index;
- this.pressed = details.pressed;
- this.value = details.value;
-}
-
-module.exports = GamepadButtonEvent;
-
-},{}],7:[function(require,module,exports){
-"use strict";
-
-module.exports = {
- "size": 5,
- "cellSize": 10,
- "extrudeSettings": {
- "amount": 1,
- "bevelEnabled": true,
- "bevelSegments": 1,
- "steps": 1,
- "bevelSize": 0.5,
- "bevelThickness": 0.5
- },
- "autogenerated": true,
- "cells": [{
- "q": -1,
- "r": 0,
- "s": 1,
- "h": 1,
- "walkable": true,
- "userData": {}
- }, {
- "q": 0,
- "r": -1,
- "s": 1,
- "h": 1,
- "walkable": true,
- "userData": {}
- }, {
- "q": 0,
- "r": 0,
- "s": 0,
- "h": 1,
- "walkable": true,
- "userData": {}
- }, {
- "q": 1,
- "r": -1,
- "s": 0,
- "h": 1,
- "walkable": true,
- "userData": {}
- }, {
- "q": -1,
- "r": 1,
- "s": 0,
- "h": 0,
- "walkable": true,
- "userData": {}
- }, {
- "q": 0,
- "r": 1,
- "s": -1,
- "h": 0,
- "walkable": true,
- "userData": {}
- }, {
- "q": 1,
- "r": 0,
- "s": -1,
- "h": 0,
- "walkable": true,
- "userData": {}
- }]
-};
-
-},{}],8:[function(require,module,exports){
-'use strict';
-
-/**
- * Source: https://github.com/Adobe-Marketing-Cloud/fetch-script
- */
-
-function getScriptId() {
- return 'script_' + Date.now() + '_' + Math.ceil(Math.random() * 100000);
-}
-
-function createScript(url, id) {
- var script = document.createElement('script');
- script.type = 'text/javascript';
- script.async = true;
- script.id = id;
- script.src = url;
-
- return script;
-}
-
-function removeScript(id) {
- var script = document.getElementById(id);
- var parent = script.parentNode;
-
- try {
- parent && parent.removeChild(script);
- } catch (e) {
- // ignore
- }
-}
-
-function appendScript(script) {
- var firstScript = document.getElementsByTagName('script')[0];
- firstScript.parentNode.insertBefore(script, firstScript);
-}
-
-function fetchScriptInternal(url, options, Promise) {
- return new Promise(function (resolve, reject) {
- var timeout = options.timeout || 5000;
- var scriptId = getScriptId();
- var script = createScript(url, scriptId);
-
- var timeoutId = setTimeout(function () {
- reject(new Error('Script request to ' + url + ' timed out'));
-
- removeScript(scriptId);
- }, timeout);
-
- var disableTimeout = function disableTimeout(timeoutId) {
- clearTimeout(timeoutId);
- };
-
- script.addEventListener('load', function (e) {
- resolve({ ok: true });
-
- disableTimeout(timeoutId);
- removeScript(scriptId);
- });
-
- script.addEventListener('error', function (e) {
- reject(new Error('Script request to ' + url + ' failed ' + e));
-
- disableTimeout(timeoutId);
- removeScript(scriptId);
- });
-
- appendScript(script);
- });
-}
-
-function fetchScript(settings) {
- settings = settings || {};
- return function (url, options) {
- options = options || {};
- return fetchScriptInternal(url, options, settings.Promise || Promise);
- };
-}
-
-module.exports = fetchScript;
-
-},{}],9:[function(require,module,exports){
-"use strict";
-
-var _typeof2 = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
-
-var _typeof = typeof Symbol === "function" && _typeof2(Symbol.iterator) === "symbol" ? function (obj) {
- return typeof obj === "undefined" ? "undefined" : _typeof2(obj);
-} : function (obj) {
- return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj === "undefined" ? "undefined" : _typeof2(obj);
-};
-
-var vg = module.exports = { VERSION: "0.1.1", PI: Math.PI, TAU: 2 * Math.PI, DEG_TO_RAD: .0174532925, RAD_TO_DEG: 57.2957795, SQRT3: Math.sqrt(3), TILE: "tile", ENT: "entity", STR: "structure", HEX: "hex", SQR: "square", ABS: "abstract" };vg.Board = function (e, t) {
- if (!e) throw new Error("You must pass in a grid system for the board to use.");this.tiles = [], this.tileGroup = null, this.group = new THREE.Object3D(), this.grid = null, this.overlay = null, this.finder = new vg.AStarFinder(t), vg.Loader.init(), this.setGrid(e);
-}, vg.Board.prototype = { setEntityOnTile: function setEntityOnTile(e, t) {
- var i = this.grid.cellToPixel(t.cell);e.position.copy(i), e.position.y += e.heightOffset || 0, e.tile && (e.tile.entity = null), e.tile = t, t.entity = e;
- }, addTile: function addTile(e) {
- var t = this.tiles.indexOf(e);-1 === t && (this.tiles.push(e), this.snapTileToGrid(e), e.position.y = 0, this.tileGroup.add(e.mesh), this.grid.add(e.cell), e.cell.tile = e);
- }, removeTile: function removeTile(e) {
- if (e) {
- var t = this.tiles.indexOf(e);this.grid.remove(e.cell), -1 !== t && this.tiles.splice(t, 1), e.dispose();
- }
- }, removeAllTiles: function removeAllTiles() {
- if (this.tileGroup) for (var e = this.tileGroup.children, t = 0; t < e.length; t++) {
- this.tileGroup.remove(e[t]);
- }
- }, getTileAtCell: function getTileAtCell(e) {
- var t = this.grid.cellToHash(e);return e.tile || ("undefined" != typeof this.grid.cells[t] ? this.grid.cells[t].tile : null);
- }, snapToGrid: function snapToGrid(e) {
- var t = this.grid.pixelToCell(e);e.copy(this.grid.cellToPixel(t));
- }, snapTileToGrid: function snapTileToGrid(e) {
- if (e.cell) e.position.copy(this.grid.cellToPixel(e.cell));else {
- var t = this.grid.pixelToCell(e.position);e.position.copy(this.grid.cellToPixel(t));
- }return e;
- }, getRandomTile: function getRandomTile() {
- var e = vg.Tools.randomInt(0, this.tiles.length - 1);return this.tiles[e];
- }, findPath: function findPath(e, t, i) {
- return this.finder.findPath(e.cell, t.cell, i, this.grid);
- }, setGrid: function setGrid(e) {
- this.group.remove(this.tileGroup), this.grid && e !== this.grid && (this.removeAllTiles(), this.tiles.forEach(function (e) {
- this.grid.remove(e.cell), e.dispose();
- }), this.grid.dispose()), this.grid = e, this.tiles = [], this.tileGroup = new THREE.Object3D(), this.group.add(this.tileGroup);
- }, generateOverlay: function generateOverlay(e) {
- var t = new THREE.LineBasicMaterial({ color: 0, opacity: .3 });this.overlay && this.group.remove(this.overlay), this.overlay = new THREE.Object3D(), this.grid.generateOverlay(e, this.overlay, t), this.group.add(this.overlay);
- }, generateTilemap: function generateTilemap(e) {
- this.reset();var t = this.grid.generateTiles(e);this.tiles = t, this.tileGroup = new THREE.Object3D();for (var i = 0; i < t.length; i++) {
- this.tileGroup.add(t[i].mesh);
- }this.group.add(this.tileGroup);
- }, reset: function reset() {
- this.removeAllTiles(), this.tileGroup && this.group.remove(this.tileGroup);
- } }, vg.Board.prototype.constructor = vg.Board, vg.Cell = function (e, t, i, s) {
- this.q = e || 0, this.r = t || 0, this.s = i || 0, this.h = s || 1, this.tile = null, this.userData = {}, this.walkable = !0, this._calcCost = 0, this._priority = 0, this._visited = !1, this._parent = null, this.uniqueID = vg.LinkedList.generateID();
-}, vg.Cell.prototype = { set: function set(e, t, i) {
- return this.q = e, this.r = t, this.s = i, this;
- }, copy: function copy(e) {
- return this.q = e.q, this.r = e.r, this.s = e.s, this.h = e.h, this.tile = e.tile || null, this.userData = e.userData || {}, this.walkable = e.walkable, this;
- }, add: function add(e) {
- return this.q += e.q, this.r += e.r, this.s += e.s, this;
- }, equals: function equals(e) {
- return this.q === e.q && this.r === e.r && this.s === e.s;
- } }, vg.Cell.prototype.constructor = vg.Cell, vg.HexGrid = function (e) {
- e = e || {}, this.type = vg.HEX, this.size = 5, this.cellSize = "undefined" == typeof e.cellSize ? 10 : e.cellSize, this.cells = {}, this.numCells = 0, this.extrudeSettings = null, this.autogenerated = !1;var t,
- i = [];for (t = 0; 6 > t; t++) {
- i.push(this._createVertex(t));
- }for (this.cellShape = new THREE.Shape(), this.cellShape.moveTo(i[0].x, i[0].y), t = 1; 6 > t; t++) {
- this.cellShape.lineTo(i[t].x, i[t].y);
- }this.cellShape.lineTo(i[0].x, i[0].y), this.cellShape.autoClose = !0, this.cellGeo = new THREE.Geometry(), this.cellGeo.vertices = i, this.cellGeo.verticesNeedUpdate = !0, this.cellShapeGeo = new THREE.ShapeGeometry(this.cellShape), this._cellWidth = 2 * this.cellSize, this._cellLength = .5 * vg.SQRT3 * this._cellWidth, this._hashDelimeter = ".", this._directions = [new vg.Cell(1, -1, 0), new vg.Cell(1, 0, -1), new vg.Cell(0, 1, -1), new vg.Cell(-1, 1, 0), new vg.Cell(-1, 0, 1), new vg.Cell(0, -1, 1)], this._diagonals = [new vg.Cell(2, -1, -1), new vg.Cell(1, 1, -2), new vg.Cell(-1, 2, -1), new vg.Cell(-2, 1, 1), new vg.Cell(-1, -1, 2), new vg.Cell(1, -2, 1)], this._list = [], this._vec3 = new THREE.Vector3(), this._cel = new vg.Cell(), this._conversionVec = new THREE.Vector3(), this._geoCache = [], this._matCache = [];
-}, vg.HexGrid.TWO_THIRDS = 2 / 3, vg.HexGrid.prototype = { cellToPixel: function cellToPixel(e) {
- return this._vec3.x = e.q * this._cellWidth * .75, this._vec3.y = e.h, this._vec3.z = -((e.s - e.r) * this._cellLength * .5), this._vec3;
- }, pixelToCell: function pixelToCell(e) {
- var t = e.x * (vg.HexGrid.TWO_THIRDS / this.cellSize),
- i = (-e.x / 3 + vg.SQRT3 / 3 * e.z) / this.cellSize;return this._cel.set(t, i, -t - i), this._cubeRound(this._cel);
- }, getCellAt: function getCellAt(e) {
- var t = e.x * (vg.HexGrid.TWO_THIRDS / this.cellSize),
- i = (-e.x / 3 + vg.SQRT3 / 3 * e.z) / this.cellSize;return this._cel.set(t, i, -t - i), this._cubeRound(this._cel), this.cells[this.cellToHash(this._cel)];
- }, getNeighbors: function getNeighbors(e, t, i) {
- var s,
- n,
- l = this._directions.length;for (this._list.length = 0, s = 0; l > s; s++) {
- this._cel.copy(e), this._cel.add(this._directions[s]), n = this.cells[this.cellToHash(this._cel)], !n || i && !i(e, n) || this._list.push(n);
- }if (t) for (s = 0; l > s; s++) {
- this._cel.copy(e), this._cel.add(this._diagonals[s]), n = this.cells[this.cellToHash(this._cel)], !n || i && !i(e, n) || this._list.push(n);
- }return this._list;
- }, getRandomCell: function getRandomCell() {
- var e,
- t = 0,
- i = vg.Tools.randomInt(0, this.numCells);for (e in this.cells) {
- if (t === i) return this.cells[e];t++;
- }return this.cells[e];
- }, cellToHash: function cellToHash(e) {
- return e.q + this._hashDelimeter + e.r + this._hashDelimeter + e.s;
- }, distance: function distance(e, t) {
- var i = Math.max(Math.abs(e.q - t.q), Math.abs(e.r - t.r), Math.abs(e.s - t.s));return i += t.h - e.h;
- }, clearPath: function clearPath() {
- var e, t;for (e in this.cells) {
- t = this.cells[e], t._calcCost = 0, t._priority = 0, t._parent = null, t._visited = !1;
- }
- }, traverse: function traverse(e) {
- var t;for (t in this.cells) {
- e(this.cells[t]);
- }
- }, generateTile: function generateTile(e, t, i) {
- var s = Math.abs(e.h);1 > s && (s = 1);var n = this._geoCache[s];n || (this.extrudeSettings.amount = s, n = new THREE.ExtrudeGeometry(this.cellShape, this.extrudeSettings), this._geoCache[s] = n);var l = new vg.Tile({ size: this.cellSize, scale: t, cell: e, geometry: n, material: i });return e.tile = l, l;
- }, generateTiles: function generateTiles(e) {
- e = e || {};var t = [],
- i = { tileScale: .95, cellSize: this.cellSize, material: null, extrudeSettings: { amount: 1, bevelEnabled: !0, bevelSegments: 1, steps: 1, bevelSize: .5, bevelThickness: .5 } };i = vg.Tools.merge(i, e), this.cellSize = i.cellSize, this._cellWidth = 2 * this.cellSize, this._cellLength = .5 * vg.SQRT3 * this._cellWidth, this.autogenerated = !0, this.extrudeSettings = i.extrudeSettings;var s, n, l;for (s in this.cells) {
- l = this.cells[s], n = this.generateTile(l, i.tileScale, i.material), n.position.copy(this.cellToPixel(l)), n.position.y = 0, t.push(n);
- }return t;
- }, generateTilePoly: function generateTilePoly(e) {
- e || (e = new THREE.MeshBasicMaterial({ color: 2405631 }));var t = new THREE.Mesh(this.cellShapeGeo, e);return this._vec3.set(1, 0, 0), t.rotateOnAxis(this._vec3, vg.PI / 2), t;
- }, generate: function generate(e) {
- e = e || {}, this.size = "undefined" == typeof e.size ? this.size : e.size;var t, i, s, n;for (t = -this.size; t < this.size + 1; t++) {
- for (i = -this.size; i < this.size + 1; i++) {
- s = -t - i, Math.abs(t) <= this.size && Math.abs(i) <= this.size && Math.abs(s) <= this.size && (n = new vg.Cell(t, i, s), this.add(n));
- }
- }
- }, generateOverlay: function generateOverlay(e, t, i) {
- var s,
- n,
- l,
- r = this.cellShape.createPointsGeometry();for (s = -e; e + 1 > s; s++) {
- for (n = -e; e + 1 > n; n++) {
- if (l = -s - n, Math.abs(s) <= e && Math.abs(n) <= e && Math.abs(l) <= e) {
- this._cel.set(s, n, l);var h = new THREE.Line(r, i);h.position.copy(this.cellToPixel(this._cel)), h.rotation.x = 90 * vg.DEG_TO_RAD, t.add(h);
- }
- }
- }
- }, add: function add(e) {
- var t = this.cellToHash(e);if (!this.cells[t]) return this.cells[t] = e, this.numCells++, e;
- }, remove: function remove(e) {
- var t = this.cellToHash(e);this.cells[t] && (delete this.cells[t], this.numCells--);
- }, dispose: function dispose() {
- this.cells = null, this.numCells = 0, this.cellShape = null, this.cellGeo.dispose(), this.cellGeo = null, this.cellShapeGeo.dispose(), this.cellShapeGeo = null, this._list = null, this._vec3 = null, this._conversionVec = null, this._geoCache = null, this._matCache = null;
- }, load: function load(e, t, i) {
- var s = this;vg.Tools.getJSON({ url: e, callback: function callback(e) {
- s.fromJSON(e), t.call(i || null, e);
- }, cache: !1, scope: s });
- }, fromJSON: function fromJSON(e) {
- var t,
- i,
- s = e.cells;for (this.cells = {}, this.numCells = 0, this.size = e.size, this.cellSize = e.cellSize, this._cellWidth = 2 * this.cellSize, this._cellLength = .5 * vg.SQRT3 * this._cellWidth, this.extrudeSettings = e.extrudeSettings, this.autogenerated = e.autogenerated, t = 0; t < s.length; t++) {
- i = new vg.Cell(), i.copy(s[t]), this.add(i);
- }
- }, toJSON: function toJSON() {
- var e,
- t,
- i = { size: this.size, cellSize: this.cellSize, extrudeSettings: this.extrudeSettings, autogenerated: this.autogenerated },
- s = [];for (t in this.cells) {
- e = this.cells[t], s.push({ q: e.q, r: e.r, s: e.s, h: e.h, walkable: e.walkable, userData: e.userData });
- }return i.cells = s, i;
- }, _createVertex: function _createVertex(e) {
- var t = vg.TAU / 6 * e;return new THREE.Vector3(this.cellSize * Math.cos(t), this.cellSize * Math.sin(t), 0);
- }, _cubeRound: function _cubeRound(e) {
- var t = Math.round(e.q),
- i = Math.round(e.r),
- s = Math.round(e.s),
- n = Math.abs(t - e.q),
- l = Math.abs(i - e.r),
- r = Math.abs(s - e.s);return n > l && n > r ? t = -i - s : l > r ? i = -t - s : s = -t - i, this._cel.set(t, i, s);
- } }, vg.HexGrid.prototype.constructor = vg.HexGrid, vg.SqrGrid = function (e) {
- e = e || {}, this.type = vg.SQR, this.size = 5, this.cellSize = "undefined" == typeof e.cellSize ? 10 : e.cellSize, this.cells = {}, this.numCells = 0, this.extrudeSettings = null, this.autogenerated = !1;var t = [];t.push(new THREE.Vector3()), t.push(new THREE.Vector3(-this.cellSize, this.cellSize)), t.push(new THREE.Vector3(this.cellSize, this.cellSize)), t.push(new THREE.Vector3(this.cellSize, -this.cellSize)), this.cellShape = new THREE.Shape(), this.cellShape.moveTo(-this.cellSize, -this.cellSize), this.cellShape.lineTo(-this.cellSize, this.cellSize), this.cellShape.lineTo(this.cellSize, this.cellSize), this.cellShape.lineTo(this.cellSize, -this.cellSize), this.cellShape.lineTo(-this.cellSize, -this.cellSize), this.cellGeo = new THREE.Geometry(), this.cellGeo.vertices = t, this.cellGeo.verticesNeedUpdate = !0, this.cellShapeGeo = new THREE.ShapeGeometry(this.cellShape), this._fullCellSize = 2 * this.cellSize, this._hashDelimeter = ".", this._directions = [new vg.Cell(1, 0, 0), new vg.Cell(0, -1, 0), new vg.Cell(-1, 0, 0), new vg.Cell(0, 1, 0)], this._diagonals = [new vg.Cell(-1, -1, 0), new vg.Cell(-1, 1, 0), new vg.Cell(1, 1, 0), new vg.Cell(1, -1, 0)], this._list = [], this._vec3 = new THREE.Vector3(), this._cel = new vg.Cell(), this._conversionVec = new THREE.Vector3(), this._geoCache = [], this._matCache = [];
-}, vg.SqrGrid.prototype = { cellToPixel: function cellToPixel(e) {
- return this._vec3.x = e.q * this._fullCellSize, this._vec3.y = e.h, this._vec3.z = e.r * this._fullCellSize, this._vec3;
- }, pixelToCell: function pixelToCell(e) {
- var t = Math.round(e.x / this._fullCellSize),
- i = Math.round(e.z / this._fullCellSize);return this._cel.set(t, i, 0);
- }, getCellAt: function getCellAt(e) {
- var t = Math.round(e.x / this._fullCellSize),
- i = Math.round(e.z / this._fullCellSize);return this._cel.set(t, i), this.cells[this.cellToHash(this._cel)];
- }, getNeighbors: function getNeighbors(e, t, i) {
- var s,
- n,
- l = this._directions.length;for (this._list.length = 0, s = 0; l > s; s++) {
- this._cel.copy(e), this._cel.add(this._directions[s]), n = this.cells[this.cellToHash(this._cel)], !n || i && !i(e, n) || this._list.push(n);
- }if (t) for (s = 0; l > s; s++) {
- this._cel.copy(e), this._cel.add(this._diagonals[s]), n = this.cells[this.cellToHash(this._cel)], !n || i && !i(e, n) || this._list.push(n);
- }return this._list;
- }, getRandomCell: function getRandomCell() {
- var e,
- t = 0,
- i = vg.Tools.randomInt(0, this.numCells);for (e in this.cells) {
- if (t === i) return this.cells[e];t++;
- }return this.cells[e];
- }, cellToHash: function cellToHash(e) {
- return e.q + this._hashDelimeter + e.r;
- }, distance: function distance(e, t) {
- var i = Math.max(Math.abs(e.q - t.q), Math.abs(e.r - t.r));return i += t.h - e.h;
- }, clearPath: function clearPath() {
- var e, t;for (e in this.cells) {
- t = this.cells[e], t._calcCost = 0, t._priority = 0, t._parent = null, t._visited = !1;
- }
- }, traverse: function traverse(e) {
- var t;for (t in this.cells) {
- e(this.cells[t]);
- }
- }, generateTile: function generateTile(e, t, i) {
- var s = Math.abs(e.h);1 > s && (s = 1);var n = this._geoCache[s];n || (this.extrudeSettings.amount = s, n = new THREE.ExtrudeGeometry(this.cellShape, this.extrudeSettings), this._geoCache[s] = n);var l = new vg.Tile({ size: this.cellSize, scale: t, cell: e, geometry: n, material: i });return e.tile = l, l;
- }, generateTiles: function generateTiles(e) {
- e = e || {};var t = [],
- i = { tileScale: .95, cellSize: this.cellSize, material: null, extrudeSettings: { amount: 1, bevelEnabled: !0, bevelSegments: 1, steps: 1, bevelSize: .5, bevelThickness: .5 } };i = vg.Tools.merge(i, e), this.cellSize = i.cellSize, this._fullCellSize = 2 * this.cellSize, this.autogenerated = !0, this.extrudeSettings = i.extrudeSettings;var s, n, l;for (s in this.cells) {
- l = this.cells[s], n = this.generateTile(l, i.tileScale, i.material), n.position.copy(this.cellToPixel(l)), n.position.y = 0, t.push(n);
- }return t;
- }, generateTilePoly: function generateTilePoly(e) {
- e || (e = new THREE.MeshBasicMaterial({ color: 2405631 }));var t = new THREE.Mesh(this.cellShapeGeo, e);return this._vec3.set(1, 0, 0), t.rotateOnAxis(this._vec3, vg.PI / 2), t;
- }, generate: function generate(e) {
- e = e || {}, this.size = "undefined" == typeof e.size ? this.size : e.size;var t,
- i,
- s,
- n = Math.ceil(this.size / 2);for (t = -n; n > t; t++) {
- for (i = -n; n > i; i++) {
- s = new vg.Cell(t, i + 1), this.add(s);
- }
- }
- }, generateOverlay: function generateOverlay(e, t, i) {
- var s,
- n,
- l = Math.ceil(e / 2);for (s = -l; l > s; s++) {
- for (n = -l; l > n; n++) {
- this._cel.set(s, n);var r = new THREE.Line(this.cellGeo, i);r.position.copy(this.cellToPixel(this._cel)), r.rotation.x = 90 * vg.DEG_TO_RAD, t.add(r);
- }
- }
- }, add: function add(e) {
- var t = this.cellToHash(e);if (!this.cells[t]) return this.cells[t] = e, this.numCells++, e;
- }, remove: function remove(e) {
- var t = this.cellToHash(e);this.cells[t] && (delete this.cells[t], this.numCells--);
- }, dispose: function dispose() {
- this.cells = null, this.numCells = 0, this.cellShape = null, this.cellGeo.dispose(), this.cellGeo = null, this.cellShapeGeo.dispose(), this.cellShapeGeo = null, this._list = null, this._vec3 = null, this._conversionVec = null, this._geoCache = null, this._matCache = null;
- }, load: function load(e, t, i) {
- vg.Tools.getJSON({ url: e, callback: function callback(e) {
- this.fromJSON(e), t.call(i || null, e);
- }, cache: !1, scope: this });
- }, fromJSON: function fromJSON(e) {
- var t,
- i,
- s = e.cells;for (this.cells = {}, this.numCells = 0, this.size = e.size, this.cellSize = e.cellSize, this._fullCellSize = 2 * this.cellSize, this.extrudeSettings = e.extrudeSettings, this.autogenerated = e.autogenerated, t = 0; t < s.length; t++) {
- i = new vg.Cell(), i.copy(s[t]), this.add(i);
- }
- }, toJSON: function toJSON() {
- var e,
- t,
- i = { size: this.size, cellSize: this.cellSize, extrudeSettings: this.extrudeSettings, autogenerated: this.autogenerated },
- s = [];for (t in this.cells) {
- e = this.cells[t], s.push({ q: e.q, r: e.r, s: e.s, h: e.h, walkable: e.walkable, userData: e.userData });
- }return i.cells = s, i;
- } }, vg.SqrGrid.prototype.constructor = vg.SqrGrid, vg.Tile = function (e) {
- e = e || {};var t = { cell: null, geometry: null, material: null };if (t = vg.Tools.merge(t, e), !t.cell || !t.geometry) throw new Error("Missing vg.Tile configuration");this.cell = t.cell, this.cell.tile && this.cell.tile !== this && this.cell.tile.dispose(), this.cell.tile = this, this.uniqueID = vg.Tools.generateID(), this.geometry = t.geometry, this.material = t.material, this.material || (this.material = new THREE.MeshPhongMaterial({ color: vg.Tools.randomizeRGB("30, 30, 30", 13) })), this.objectType = vg.TILE, this.entity = null, this.userData = {}, this.selected = !1, this.highlight = "0x0084cc", this.mesh = new THREE.Mesh(this.geometry, this.material), this.mesh.userData.structure = this, this.position = this.mesh.position, this.rotation = this.mesh.rotation, this.rotation.x = -90 * vg.DEG_TO_RAD, this.mesh.scale.set(t.scale, t.scale, 1), this.material.emissive ? this._emissive = this.material.emissive.getHex() : this._emissive = null;
-}, vg.Tile.prototype = { select: function select() {
- return this.material.emissive && this.material.emissive.setHex(this.highlight), this.selected = !0, this;
- }, deselect: function deselect() {
- return null !== this._emissive && this.material.emissive && this.material.emissive.setHex(this._emissive), this.selected = !1, this;
- }, toggle: function toggle() {
- return this.selected ? this.deselect() : this.select(), this;
- }, dispose: function dispose() {
- this.cell && this.cell.tile && (this.cell.tile = null), this.cell = null, this.position = null, this.rotation = null, this.mesh.parent && this.mesh.parent.remove(this.mesh), this.mesh.userData.structure = null, this.mesh = null, this.material = null, this.userData = null, this.entity = null, this.geometry = null, this._emissive = null;
- } }, vg.Tile.prototype.constructor = vg.Tile, function () {
- var e = function e() {
- this.obj = null, this.next = null, this.prev = null, this.free = !0;
- },
- t = function t() {
- this.first = null, this.last = null, this.length = 0, this.objToNodeMap = {}, this.uniqueID = Date.now() + "" + Math.floor(1e3 * Math.random()), this.sortArray = [];
- };t.generateID = function () {
- return Math.random().toString(36).slice(2) + Date.now();
- }, t.prototype = { getNode: function getNode(e) {
- return this.objToNodeMap[e.uniqueID];
- }, addNode: function addNode(i) {
- var s = new e();if (!i.uniqueID) try {
- i.uniqueID = t.generateID();
- } catch (n) {
- return console.error("[LinkedList.addNode] obj passed is immutable: cannot attach necessary identifier"), null;
- }return s.obj = i, s.free = !1, this.objToNodeMap[i.uniqueID] = s, s;
- }, swapObjects: function swapObjects(e, t) {
- this.objToNodeMap[e.obj.uniqueID] = null, this.objToNodeMap[t.uniqueID] = e, e.obj = t;
- }, add: function add(e) {
- var t = this.objToNodeMap[e.uniqueID];if (t) {
- if (t.free === !1) return;t.obj = e, t.free = !1, t.next = null, t.prev = null;
- } else t = this.addNode(e);if (this.first) {
- if (!this.last) throw new Error("[LinkedList.add] No last in the list -- that shouldn't happen here");this.last.next = t, t.prev = this.last, this.last = t, t.next = null;
- } else this.first = t, this.last = t, t.next = null, t.prev = null;this.length++, this.showDebug && this.dump("after add");
- }, has: function has(e) {
- return !!this.objToNodeMap[e.uniqueID];
- }, moveUp: function moveUp(e) {
- this.dump("before move up");var t = this.getNode(e);if (!t) throw "Oops, trying to move an object that isn't in the list";if (t.prev) {
- var i = t.prev,
- s = i.prev;t == this.last && (this.last = i);var n = t.next;s && (s.next = t), t.next = i, t.prev = i.prev, i.next = n, i.prev = t, this.first == i && (this.first = t);
- }
- }, moveDown: function moveDown(e) {
- var t = this.getNode(e);if (!t) throw "Oops, trying to move an object that isn't in the list";if (t.next) {
- var i = t.next;this.moveUp(i.obj), this.last == i && (this.last = t);
- }
- }, sort: function sort(e) {
- var t,
- i,
- s = this.sortArray,
- n = this.first;for (s.length = 0; n;) {
- s.push(n.obj), n = n.next;
- }for (this.clear(), s.sort(e), i = s.length, t = 0; i > t; t++) {
- this.add(s[t]);
- }
- }, remove: function remove(e) {
- var t = this.getNode(e);return !t || t.free ? !1 : (t.prev && (t.prev.next = t.next), t.next && (t.next.prev = t.prev), t.prev || (this.first = t.next), t.next || (this.last = t.prev), t.free = !0, t.prev = null, t.next = null, this.length--, !0);
- }, shift: function shift() {
- var e = this.first;return 0 === this.length ? null : (e.prev && (e.prev.next = e.next), e.next && (e.next.prev = e.prev), this.first = e.next, e.next || (this.last = null), e.free = !0, e.prev = null, e.next = null, this.length--, e.obj);
- }, pop: function pop() {
- var e = this.last;return 0 === this.length ? null : (e.prev && (e.prev.next = e.next), e.next && (e.next.prev = e.prev), this.last = e.prev, e.prev || (this.first = null), e.free = !0, e.prev = null, e.next = null, this.length--, e.obj);
- }, concat: function concat(e) {
- for (var t = e.first; t;) {
- this.add(t.obj), t = t.next;
- }
- }, clear: function clear() {
- for (var e = this.first; e;) {
- e.free = !0, e = e.next;
- }this.first = null, this.length = 0;
- }, dispose: function dispose() {
- for (var e = this.first; e;) {
- e.obj = null, e = e.next;
- }this.first = null, this.objToNodeMap = null;
- }, dump: function dump(e) {
- console.log("====================" + e + "=====================");for (var t = this.first; t;) {
- console.log("{" + t.obj.toString() + "} previous=" + (t.prev ? t.prev.obj : "NULL")), t = t.next();
- }console.log("==================================="), console.log("Last: {" + (this.last ? this.last.obj : "NULL") + "} First: {" + (this.first ? this.first.obj : "NULL") + "}");
- } }, t.prototype.constructor = t, vg.LinkedList = t;
-}(), function () {
- var e = function e(_e, t, i, s, n) {
- this._listener = t, this.isOnce = i, this.context = s, this.signal = _e, this._priority = n || 0;
- };e.prototype = { active: !0, params: null, execute: function execute(e) {
- var t, i;return this.active && this._listener && (i = this.params ? this.params.concat(e) : e, t = this._listener.apply(this.context, i), this.isOnce && this.detach()), t;
- }, detach: function detach() {
- return this.isBound() ? this.signal.remove(this._listener, this.context) : null;
- }, isBound: function isBound() {
- return !!this.signal && !!this._listener;
- }, _destroy: function _destroy() {
- delete this.signal, delete this._listener, delete this.context;
- }, toString: function toString() {
- return "[SignalBinding isOnce:" + this.isOnce + ", isBound:" + this.isBound() + ", active:" + this.active + "]";
- } }, e.prototype.constructor = e;var t = function t() {
- this._bindings = [], this._prevParams = null;var e = this;this.dispatch = function () {
- t.prototype.dispatch.apply(e, arguments);
- };
- };t.prototype = { memorize: !1, _shouldPropagate: !0, active: !0, validateListener: function validateListener(e, t) {
- if ("function" != typeof e) throw new Error("Signal: listener is a required param of {fn}() and should be a Function.".replace("{fn}", t));
- }, _registerListener: function _registerListener(t, i, s, n) {
- var l,
- r = this._indexOfListener(t, s);if (-1 !== r) {
- if (l = this._bindings[r], l.isOnce !== i) throw new Error("You cannot add" + (i ? "" : "Once") + "() then add" + (i ? "Once" : "") + "() the same listener without removing the relationship first.");
- } else l = new e(this, t, i, s, n), this._addBinding(l);return this.memorize && this._prevParams && l.execute(this._prevParams), l;
- }, _addBinding: function _addBinding(e) {
- var t = this._bindings.length;do {
- t--;
- } while (this._bindings[t] && e._priority <= this._bindings[t]._priority);this._bindings.splice(t + 1, 0, e);
- }, _indexOfListener: function _indexOfListener(e, t) {
- for (var i, s = this._bindings.length; s--;) {
- if (i = this._bindings[s], i._listener === e && i.context === t) return s;
- }return -1;
- }, has: function has(e, t) {
- return -1 !== this._indexOfListener(e, t);
- }, add: function add(e, t, i) {
- return this.validateListener(e, "add"), this._registerListener(e, !1, t, i);
- }, addOnce: function addOnce(e, t, i) {
- return this.validateListener(e, "addOnce"), this._registerListener(e, !0, t, i);
- }, remove: function remove(e, t) {
- this.validateListener(e, "remove");var i = this._indexOfListener(e, t);return -1 !== i && (this._bindings[i]._destroy(), this._bindings.splice(i, 1)), e;
- }, removeAll: function removeAll(e) {
- "undefined" == typeof e && (e = null);for (var t = this._bindings.length; t--;) {
- e ? this._bindings[t].context === e && (this._bindings[t]._destroy(), this._bindings.splice(t, 1)) : this._bindings[t]._destroy();
- }e || (this._bindings.length = 0);
- }, getNumListeners: function getNumListeners() {
- return this._bindings.length;
- }, halt: function halt() {
- this._shouldPropagate = !1;
- }, dispatch: function dispatch() {
- if (this.active) {
- var e,
- t = Array.prototype.slice.call(arguments),
- i = this._bindings.length;if (this.memorize && (this._prevParams = t), i) {
- e = this._bindings.slice(), this._shouldPropagate = !0;do {
- i--;
- } while (e[i] && this._shouldPropagate && e[i].execute(t) !== !1);
- }
- }
- }, forget: function forget() {
- this._prevParams = null;
- }, dispose: function dispose() {
- this.removeAll(), delete this._bindings, delete this._prevParams;
- }, toString: function toString() {
- return "[Signal active:" + this.active + " numListeners:" + this.getNumListeners() + "]";
- } }, t.prototype.constructor = t, vg.Signal = t;
-}(), vg.AStarFinder = function (e) {
- e = e || {};var t = { allowDiagonal: !1, heuristicFilter: null };t = vg.Tools.merge(t, e), this.allowDiagonal = t.allowDiagonal, this.heuristicFilter = t.heuristicFilter, this.list = new vg.LinkedList();
-}, vg.AStarFinder.prototype = { findPath: function findPath(e, t, i, s) {
- var n, l, r, h, o, a;for (i = i || this.heuristicFilter, s.clearPath(), this.list.clear(), this.list.add(e); this.list.length > 0;) {
- if (this.list.sort(this.compare), n = this.list.shift(), n._visited = !0, n === t) return vg.PathUtil.backtrace(t);for (r = s.getNeighbors(n, this.allowDiagonal, i), o = 0, a = r.length; a > o; o++) {
- if (h = r[o], h.walkable && (l = n._calcCost + s.distance(n, h), !h._visited || l < h._calcCost)) {
- if (h._visited = !0, h._parent = n, h._calcCost = l, h._priority = l + s.distance(t, h), h === t) return vg.PathUtil.backtrace(t);this.list.add(h);
- }
- }
- }return null;
- }, compare: function compare(e, t) {
- return e._priority - t._priority;
- } }, vg.AStarFinder.prototype.constructor = vg.AStarFinder, vg.PathUtil = { backtrace: function backtrace(e) {
- for (var t = [e]; e._parent;) {
- e = e._parent, t.push(e);
- }return t.reverse();
- }, biBacktrace: function biBacktrace(e, t) {
- var i = this.backtrace(e),
- s = this.backtrace(t);return i.concat(s.reverse());
- }, pathLength: function pathLength(e) {
- var t,
- i,
- s,
- n,
- l,
- r = 0;for (t = 1; t < e.length; ++t) {
- i = e[t - 1], s = e[t], n = i[0] - s[0], l = i[1] - s[1], r += Math.sqrt(n * n + l * l);
- }return r;
- }, interpolate: function interpolate(e, t, i, s) {
- var n,
- l,
- r,
- h,
- o,
- a,
- c = Math.abs,
- u = [];for (r = c(i - e), h = c(s - t), n = i > e ? 1 : -1, l = s > t ? 1 : -1, o = r - h; e !== i || t !== s;) {
- u.push([e, t]), a = 2 * o, a > -h && (o -= h, e += n), r > a && (o += r, t += l);
- }return u;
- }, expandPath: function expandPath(e) {
- var t,
- i,
- s,
- n,
- l,
- r,
- h = [],
- o = e.length;if (2 > o) return h;for (l = 0; o - 1 > l; ++l) {
- for (t = e[l], i = e[l + 1], s = this.interpolate(t[0], t[1], i[0], i[1]), n = s.length, r = 0; n - 1 > r; ++r) {
- h.push(s[r]);
- }
- }return h.push(e[o - 1]), h;
- }, smoothenPath: function smoothenPath(e, t) {
- var i,
- s,
- n,
- l,
- r,
- h,
- o,
- a,
- c,
- u,
- d,
- g,
- p = t.length,
- v = t[0][0],
- f = t[0][1],
- m = t[p - 1][0],
- _ = t[p - 1][1];for (i = v, s = f, r = [[i, s]], o = 2; p > o; ++o) {
- for (c = t[o], n = c[0], l = c[1], u = this.interpolate(i, s, n, l), g = !1, a = 1; a < u.length; ++a) {
- if (d = u[a], !e.isWalkableAt(d[0], d[1])) {
- g = !0;break;
- }
- }g && (h = t[o - 1], r.push(h), i = h[0], s = h[1]);
- }return r.push([m, _]), r;
- }, compressPath: function compressPath(e) {
- if (e.length < 3) return e;var t,
- i,
- s,
- n,
- l,
- r,
- h = [],
- o = e[0][0],
- a = e[0][1],
- c = e[1][0],
- u = e[1][1],
- d = c - o,
- g = u - a;for (l = Math.sqrt(d * d + g * g), d /= l, g /= l, h.push([o, a]), r = 2; r < e.length; r++) {
- t = c, i = u, s = d, n = g, c = e[r][0], u = e[r][1], d = c - t, g = u - i, l = Math.sqrt(d * d + g * g), d /= l, g /= l, (d !== s || g !== n) && h.push([t, i]);
- }return h.push([c, u]), h;
- } }, vg.Loader = { manager: null, imageLoader: null, crossOrigin: !1, init: function init(e) {
- this.crossOrigin = e || !1, this.manager = new THREE.LoadingManager(function () {}, function () {}, function () {
- console.warn("Error loading images");
- }), this.imageLoader = new THREE.ImageLoader(this.manager), this.imageLoader.crossOrigin = e;
- }, loadTexture: function loadTexture(e, t, i, s) {
- var n = new THREE.Texture(null, t);return this.imageLoader.load(e, function (e) {
- n.image = e, n.needsUpdate = !0, i && i(n);
- }, null, function (e) {
- s && s(e);
- }), n.sourceFile = e, n;
- } }, vg.MouseCaster = function (e, t, i) {
- this.down = !1, this.rightDown = !1, this.pickedObject = null, this.selectedObject = null, this.allHits = null, this.active = !0, this.shift = !1, this.ctrl = !1, this.wheel = 0, this.position = new THREE.Vector3(), this.screenPosition = new THREE.Vector2(), this.signal = new vg.Signal(), this.group = e, this._camera = t, this._raycaster = new THREE.Raycaster(), this._preventDefault = !1, i = i || document, i.addEventListener("mousemove", this._onDocumentMouseMove.bind(this), !1), i.addEventListener("mousedown", this._onDocumentMouseDown.bind(this), !1), i.addEventListener("mouseup", this._onDocumentMouseUp.bind(this), !1), i.addEventListener("mousewheel", this._onMouseWheel.bind(this), !1), i.addEventListener("DOMMouseScroll", this._onMouseWheel.bind(this), !1);
-}, vg.MouseCaster.OVER = "over", vg.MouseCaster.OUT = "out", vg.MouseCaster.DOWN = "down", vg.MouseCaster.UP = "up", vg.MouseCaster.CLICK = "click", vg.MouseCaster.WHEEL = "wheel", vg.MouseCaster.prototype = { update: function update() {
- if (this.active) {
- this._raycaster.setFromCamera(this.screenPosition, this._camera);var e,
- t,
- i = this._raycaster.intersectObject(this.group, !0);i.length > 0 ? (e = i[0], t = e.object.userData.structure, this.pickedObject != t && (this.pickedObject && this.signal.dispatch(vg.MouseCaster.OUT, this.pickedObject), this.pickedObject = t, this.selectedObject = null, this.signal.dispatch(vg.MouseCaster.OVER, this.pickedObject)), this.position.copy(e.point), this.screenPosition.z = e.distance) : (this.pickedObject && this.signal.dispatch(vg.MouseCaster.OUT, this.pickedObject), this.pickedObject = null, this.selectedObject = null), this.allHits = i;
- }
- }, preventDefault: function preventDefault() {
- this._preventDefault = !0;
- }, _onDocumentMouseDown: function _onDocumentMouseDown(e) {
- return e = e || window.event, e.preventDefault(), this._preventDefault ? (this._preventDefault = !1, !1) : (this.pickedObject && (this.selectedObject = this.pickedObject), this.shift = e.shiftKey, this.ctrl = e.ctrlKey, this.down = 1 === e.which, this.rightDown = 3 === e.which, void this.signal.dispatch(vg.MouseCaster.DOWN, this.pickedObject));
- }, _onDocumentMouseUp: function _onDocumentMouseUp(e) {
- return e.preventDefault(), this._preventDefault ? (this._preventDefault = !1, !1) : (this.shift = e.shiftKey, this.ctrl = e.ctrlKey, this.signal.dispatch(vg.MouseCaster.UP, this.pickedObject), this.selectedObject && this.pickedObject && this.selectedObject.uniqueID === this.pickedObject.uniqueID && this.signal.dispatch(vg.MouseCaster.CLICK, this.pickedObject), this.down = 1 === e.which ? !1 : this.down, void (this.rightDown = 3 === e.which ? !1 : this.rightDown));
- }, _onDocumentMouseMove: function _onDocumentMouseMove(e) {
- e.preventDefault(), this.screenPosition.x = e.clientX / window.innerWidth * 2 - 1, this.screenPosition.y = 2 * -(e.clientY / window.innerHeight) + 1;
- }, _onMouseWheel: function _onMouseWheel(e) {
- if (this.active) {
- e.preventDefault(), e.stopPropagation();var t = 0;void 0 !== e.wheelDelta ? t = e.wheelDelta : void 0 !== e.detail && (t = -e.detail), t > 0 ? this.wheel++ : this.wheel--, this.signal.dispatch(vg.MouseCaster.WHEEL, this.wheel);
- }
- } }, vg.MouseCaster.prototype.constructor = vg.MouseCaster, vg.Scene = function (e, t) {
- var i = { element: document.body, alpha: !0, antialias: !0, clearColor: "#fff", sortObjects: !1, fog: null, light: new THREE.DirectionalLight(16777215), lightPosition: null, cameraType: "PerspectiveCamera", cameraPosition: null, orthoZoom: 4 },
- s = { minDistance: 100, maxDistance: 1e3, zoomSpeed: 2, noZoom: !1 };if (i = vg.Tools.merge(i, e), "boolean" != typeof t && (s = vg.Tools.merge(s, t)), this.renderer = new THREE.WebGLRenderer({ alpha: i.alpha, antialias: i.antialias }), this.renderer.setClearColor(i.clearColor, 0), this.renderer.sortObjects = i.sortObjects, this.width = window.innerWidth, this.height = window.innerHeight, this.orthoZoom = i.orthoZoom, this.container = new THREE.Scene(), this.container.fog = i.fog, this.container.add(new THREE.AmbientLight(14540253)), i.lightPosition || i.light.position.set(-1, 1, -1).normalize(), this.container.add(i.light), "OrthographicCamera" === i.cameraType) {
- var n = window.innerWidth / this.orthoZoom,
- l = window.innerHeight / this.orthoZoom;this.camera = new THREE.OrthographicCamera(n / -2, n / 2, l / 2, l / -2, 1, 5e3);
- } else this.camera = new THREE.PerspectiveCamera(50, this.width / this.height, 1, 5e3);this.contolled = !!t, this.contolled && (this.controls = new THREE.OrbitControls(this.camera, this.renderer.domElement), this.controls.minDistance = s.minDistance, this.controls.maxDistance = s.maxDistance, this.controls.zoomSpeed = s.zoomSpeed, this.controls.noZoom = s.noZoom), i.cameraPosition && this.camera.position.copy(i.cameraPosition), window.addEventListener("resize", function () {
- if (this.width = window.innerWidth, this.height = window.innerHeight, "OrthographicCamera" === this.camera.type) {
- var e = this.width / this.orthoZoom,
- t = this.height / this.orthoZoom;this.camera.left = e / -2, this.camera.right = e / 2, this.camera.top = t / 2, this.camera.bottom = t / -2;
- } else this.camera.aspect = this.width / this.height;this.camera.updateProjectionMatrix(), this.renderer.setSize(this.width, this.height);
- }.bind(this), !1), this.attachTo(i.element);
-}, vg.Scene.prototype = { attachTo: function attachTo(e) {
- e.style.width = this.width + "px", e.style.height = this.height + "px", this.renderer.setPixelRatio(window.devicePixelRatio), this.renderer.setSize(this.width, this.height), e.appendChild(this.renderer.domElement);
- }, add: function add(e) {
- this.container.add(e);
- }, remove: function remove(e) {
- this.container.remove(e);
- }, render: function render() {
- this.contolled && this.controls.update(), this.renderer.render(this.container, this.camera);
- }, updateOrthoZoom: function updateOrthoZoom() {
- if (this.orthoZoom <= 0) return void (this.orthoZoom = 0);var e = this.width / this.orthoZoom,
- t = this.height / this.orthoZoom;this.camera.left = e / -2, this.camera.right = e / 2, this.camera.top = t / 2, this.camera.bottom = t / -2, this.camera.updateProjectionMatrix();
- }, focusOn: function focusOn(e) {
- this.camera.lookAt(e.position);
- } }, vg.Scene.prototype.constructor = vg.Scene, vg.SelectionManager = function (e) {
- this.mouse = e, this.onSelect = new vg.Signal(), this.onDeselect = new vg.Signal(), this.selected = null, this.toggleSelection = !1, this.mouse.signal.add(this.onMouse, this);
-}, vg.SelectionManager.prototype = { select: function select(e, t) {
- e && (t = t || !0, this.selected !== e && this.clearSelection(t), e.selected ? this.toggleSelection && (t && this.onDeselect.dispatch(e), e.deselect()) : e.select(), this.selected = e, t && this.onSelect.dispatch(e));
- }, clearSelection: function clearSelection(e) {
- e = e || !0, this.selected && (e && this.onDeselect.dispatch(this.selected), this.selected.deselect()), this.selected = null;
- }, onMouse: function onMouse(e, t) {
- switch (e) {case vg.MouseCaster.DOWN:
- t || this.clearSelection();break;case vg.MouseCaster.CLICK:
- this.select(t);}
- } }, vg.SelectionManager.prototype.constructor = vg.SelectionManager, vg.Tools = { clamp: function clamp(e, t, i) {
- return Math.max(t, Math.min(i, e));
- }, sign: function sign(e) {
- return e && e / Math.abs(e);
- }, random: function random(e, t) {
- return 1 === arguments.length ? Math.random() * e - .5 * e : Math.random() * (t - e) + e;
- }, randomInt: function randomInt(e, t) {
- return 1 === arguments.length ? Math.random() * e - .5 * e | 0 : Math.random() * (t - e + 1) + e | 0;
- }, normalize: function normalize(e, t, i) {
- return (e - t) / (i - t);
- }, getShortRotation: function getShortRotation(e) {
- return e %= this.TAU, e > this.PI ? e -= this.TAU : e < -this.PI && (e += this.TAU), e;
- }, generateID: function generateID() {
- return Math.random().toString(36).slice(2) + Date.now();
- }, isPlainObject: function isPlainObject(e) {
- if ("object" != (typeof e === "undefined" ? "undefined" : _typeof(e)) || e.nodeType || e === e.window) return !1;try {
- if (e.constructor && !Object.prototype.hasOwnProperty.call(e.constructor.prototype, "isPrototypeOf")) return !1;
- } catch (t) {
- return !1;
- }return !0;
- }, merge: function merge(e, t) {
- var i = this,
- s = Array.isArray(t),
- n = s && [] || {};return s ? (e = e || [], n = n.concat(e), t.forEach(function (t, s) {
- "undefined" == typeof n[s] ? n[s] = t : i.isPlainObject(t) ? n[s] = i.merge(e[s], t) : -1 === e.indexOf(t) && n.push(t);
- }), n) : (e && i.isPlainObject(e) && Object.keys(e).forEach(function (t) {
- n[t] = e[t];
- }), Object.keys(t).forEach(function (s) {
- t[s] && i.isPlainObject(t[s]) && e[s] ? n[s] = i.merge(e[s], t[s]) : n[s] = t[s];
- }), n);
- }, now: function now() {
- return window.nwf ? window.nwf.system.Performance.elapsedTime : window.performance.now();
- }, empty: function empty(e) {
- for (; e.lastChild;) {
- e.removeChild(e.lastChild);
- }
- }, radixSort: function radixSort(e, t, i, s) {
- if (t = t || 0, i = i || e.length, s = s || 31, !(t >= i - 1 || 0 > s)) {
- for (var n = t, l = i, r = 1 << s; l > n;) {
- if (e[n] & r) {
- --l;var h = e[n];e[n] = e[l], e[l] = h;
- } else ++n;
- }this.radixSort(e, t, l, s - 1), this.radixSort(e, l, i, s - 1);
- }
- }, randomizeRGB: function randomizeRGB(e, t) {
- var i,
- s,
- n = e.split(","),
- l = "rgb(";for (t = this.randomInt(t), i = 0; 3 > i; i++) {
- s = parseInt(n[i]) + t, 0 > s ? s = 0 : s > 255 && (s = 255), l += s + ",";
- }return l = l.substring(0, l.length - 1), l += ")";
- }, getJSON: function getJSON(e) {
- var t = new XMLHttpRequest(),
- i = "undefined" == typeof e.cache ? !1 : e.cache,
- s = i ? e.url : e.url + "?t=" + Math.floor(1e4 * Math.random()) + Date.now();t.onreadystatechange = function () {
- if (200 === this.status) {
- var t = null;try {
- t = JSON.parse(this.responseText);
- } catch (i) {
- return;
- }return void e.callback.call(e.scope || null, t);
- }0 !== this.status && console.warn("[Tools.getJSON] Error: " + this.status + " (" + this.statusText + ") :: " + e.url);
- }, t.open("GET", s, !0), t.setRequestHeader("Accept", "application/json"), t.setRequestHeader("Content-Type", "application/json"), t.send("");
- } };
-
-
-},{}],10:[function(require,module,exports){
-'use strict';
-
-/**
- * Polyfill for the additional KeyboardEvent properties defined in the D3E and
- * D4E draft specifications, by @inexorabletash.
- *
- * See: https://github.com/inexorabletash/polyfill
- */
-
-(function (global) {
- var nativeKeyboardEvent = 'KeyboardEvent' in global;
- if (!nativeKeyboardEvent) global.KeyboardEvent = function KeyboardEvent() {
- throw TypeError('Illegal constructor');
- };
-
- if (!('DOM_KEY_LOCATION_STANDARD' in global.KeyboardEvent)) global.KeyboardEvent.DOM_KEY_LOCATION_STANDARD = 0x00; // Default or unknown location
- if (!('DOM_KEY_LOCATION_LEFT' in global.KeyboardEvent)) global.KeyboardEvent.DOM_KEY_LOCATION_LEFT = 0x01; // e.g. Left Alt key
- if (!('DOM_KEY_LOCATION_RIGHT' in global.KeyboardEvent)) global.KeyboardEvent.DOM_KEY_LOCATION_RIGHT = 0x02; // e.g. Right Alt key
- if (!('DOM_KEY_LOCATION_NUMPAD' in global.KeyboardEvent)) global.KeyboardEvent.DOM_KEY_LOCATION_NUMPAD = 0x03; // e.g. Numpad 0 or +
-
- var STANDARD = window.KeyboardEvent.DOM_KEY_LOCATION_STANDARD,
- LEFT = window.KeyboardEvent.DOM_KEY_LOCATION_LEFT,
- RIGHT = window.KeyboardEvent.DOM_KEY_LOCATION_RIGHT,
- NUMPAD = window.KeyboardEvent.DOM_KEY_LOCATION_NUMPAD;
-
- //--------------------------------------------------------------------
- //
- // Utilities
- //
- //--------------------------------------------------------------------
-
- function contains(s, ss) {
- return String(s).indexOf(ss) !== -1;
- }
-
- var os = function () {
- if (contains(navigator.platform, 'Win')) {
- return 'win';
- }
- if (contains(navigator.platform, 'Mac')) {
- return 'mac';
- }
- if (contains(navigator.platform, 'CrOS')) {
- return 'cros';
- }
- if (contains(navigator.platform, 'Linux')) {
- return 'linux';
- }
- if (contains(navigator.userAgent, 'iPad') || contains(navigator.platform, 'iPod') || contains(navigator.platform, 'iPhone')) {
- return 'ios';
- }
- return '';
- }();
-
- var browser = function () {
- if (contains(navigator.userAgent, 'Chrome/')) {
- return 'chrome';
- }
- if (contains(navigator.vendor, 'Apple')) {
- return 'safari';
- }
- if (contains(navigator.userAgent, 'MSIE')) {
- return 'ie';
- }
- if (contains(navigator.userAgent, 'Gecko/')) {
- return 'moz';
- }
- if (contains(navigator.userAgent, 'Opera/')) {
- return 'opera';
- }
- return '';
- }();
-
- var browser_os = browser + '-' + os;
-
- function mergeIf(baseTable, select, table) {
- if (browser_os === select || browser === select || os === select) {
- Object.keys(table).forEach(function (keyCode) {
- baseTable[keyCode] = table[keyCode];
- });
- }
- }
-
- function remap(o, key) {
- var r = {};
- Object.keys(o).forEach(function (k) {
- var item = o[k];
- if (key in item) {
- r[item[key]] = item;
- }
- });
- return r;
- }
-
- function invert(o) {
- var r = {};
- Object.keys(o).forEach(function (k) {
- r[o[k]] = k;
- });
- return r;
- }
-
- //--------------------------------------------------------------------
- //
- // Generic Mappings
- //
- //--------------------------------------------------------------------
-
- // "keyInfo" is a dictionary:
- // code: string - name from DOM Level 3 KeyboardEvent code Values
- // https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3Events-code.html
- // location (optional): number - one of the DOM_KEY_LOCATION values
- // keyCap (optional): string - keyboard label in en-US locale
- // USB code Usage ID from page 0x07 unless otherwise noted (Informative)
-
- // Map of keyCode to keyInfo
- var keyCodeToInfoTable = {
- // 0x01 - VK_LBUTTON
- // 0x02 - VK_RBUTTON
- 0x03: { code: 'Cancel' }, // [USB: 0x9b] char \x0018 ??? (Not in D3E)
- // 0x04 - VK_MBUTTON
- // 0x05 - VK_XBUTTON1
- // 0x06 - VK_XBUTTON2
- 0x06: { code: 'Help' }, // [USB: 0x75] ???
- // 0x07 - undefined
- 0x08: { code: 'Backspace' }, // [USB: 0x2a] Labelled Delete on Macintosh keyboards.
- 0x09: { code: 'Tab' }, // [USB: 0x2b]
- // 0x0A-0x0B - reserved
- 0X0C: { code: 'Clear' }, // [USB: 0x9c] NumPad Center (Not in D3E)
- 0X0D: { code: 'Enter' }, // [USB: 0x28]
- // 0x0E-0x0F - undefined
-
- 0x10: { code: 'Shift' },
- 0x11: { code: 'Control' },
- 0x12: { code: 'Alt' },
- 0x13: { code: 'Pause' }, // [USB: 0x48]
- 0x14: { code: 'CapsLock' }, // [USB: 0x39]
- 0x15: { code: 'KanaMode' }, // [USB: 0x88] - "HangulMode" for Korean layout
- 0x16: { code: 'HangulMode' }, // [USB: 0x90] 0x15 as well in MSDN VK table ???
- 0x17: { code: 'JunjaMode' }, // (Not in D3E)
- 0x18: { code: 'FinalMode' }, // (Not in D3E)
- 0x19: { code: 'KanjiMode' }, // [USB: 0x91] - "HanjaMode" for Korean layout
- // 0x1A - undefined
- 0x1B: { code: 'Escape' }, // [USB: 0x29]
- 0x1C: { code: 'Convert' }, // [USB: 0x8a]
- 0x1D: { code: 'NonConvert' }, // [USB: 0x8b]
- 0x1E: { code: 'Accept' }, // (Not in D3E)
- 0x1F: { code: 'ModeChange' }, // (Not in D3E)
-
- 0x20: { code: 'Space' }, // [USB: 0x2c]
- 0x21: { code: 'PageUp' }, // [USB: 0x4b]
- 0x22: { code: 'PageDown' }, // [USB: 0x4e]
- 0x23: { code: 'End' }, // [USB: 0x4d]
- 0x24: { code: 'Home' }, // [USB: 0x4a]
- 0x25: { code: 'ArrowLeft' }, // [USB: 0x50]
- 0x26: { code: 'ArrowUp' }, // [USB: 0x52]
- 0x27: { code: 'ArrowRight' }, // [USB: 0x4f]
- 0x28: { code: 'ArrowDown' }, // [USB: 0x51]
- 0x29: { code: 'Select' }, // (Not in D3E)
- 0x2A: { code: 'Print' }, // (Not in D3E)
- 0x2B: { code: 'Execute' }, // [USB: 0x74] (Not in D3E)
- 0x2C: { code: 'PrintScreen' }, // [USB: 0x46]
- 0x2D: { code: 'Insert' }, // [USB: 0x49]
- 0x2E: { code: 'Delete' }, // [USB: 0x4c]
- 0x2F: { code: 'Help' }, // [USB: 0x75] ???
-
- 0x30: { code: 'Digit0', keyCap: '0' }, // [USB: 0x27] 0)
- 0x31: { code: 'Digit1', keyCap: '1' }, // [USB: 0x1e] 1!
- 0x32: { code: 'Digit2', keyCap: '2' }, // [USB: 0x1f] 2@
- 0x33: { code: 'Digit3', keyCap: '3' }, // [USB: 0x20] 3#
- 0x34: { code: 'Digit4', keyCap: '4' }, // [USB: 0x21] 4$
- 0x35: { code: 'Digit5', keyCap: '5' }, // [USB: 0x22] 5%
- 0x36: { code: 'Digit6', keyCap: '6' }, // [USB: 0x23] 6^
- 0x37: { code: 'Digit7', keyCap: '7' }, // [USB: 0x24] 7&
- 0x38: { code: 'Digit8', keyCap: '8' }, // [USB: 0x25] 8*
- 0x39: { code: 'Digit9', keyCap: '9' }, // [USB: 0x26] 9(
- // 0x3A-0x40 - undefined
-
- 0x41: { code: 'KeyA', keyCap: 'a' }, // [USB: 0x04]
- 0x42: { code: 'KeyB', keyCap: 'b' }, // [USB: 0x05]
- 0x43: { code: 'KeyC', keyCap: 'c' }, // [USB: 0x06]
- 0x44: { code: 'KeyD', keyCap: 'd' }, // [USB: 0x07]
- 0x45: { code: 'KeyE', keyCap: 'e' }, // [USB: 0x08]
- 0x46: { code: 'KeyF', keyCap: 'f' }, // [USB: 0x09]
- 0x47: { code: 'KeyG', keyCap: 'g' }, // [USB: 0x0a]
- 0x48: { code: 'KeyH', keyCap: 'h' }, // [USB: 0x0b]
- 0x49: { code: 'KeyI', keyCap: 'i' }, // [USB: 0x0c]
- 0x4A: { code: 'KeyJ', keyCap: 'j' }, // [USB: 0x0d]
- 0x4B: { code: 'KeyK', keyCap: 'k' }, // [USB: 0x0e]
- 0x4C: { code: 'KeyL', keyCap: 'l' }, // [USB: 0x0f]
- 0x4D: { code: 'KeyM', keyCap: 'm' }, // [USB: 0x10]
- 0x4E: { code: 'KeyN', keyCap: 'n' }, // [USB: 0x11]
- 0x4F: { code: 'KeyO', keyCap: 'o' }, // [USB: 0x12]
-
- 0x50: { code: 'KeyP', keyCap: 'p' }, // [USB: 0x13]
- 0x51: { code: 'KeyQ', keyCap: 'q' }, // [USB: 0x14]
- 0x52: { code: 'KeyR', keyCap: 'r' }, // [USB: 0x15]
- 0x53: { code: 'KeyS', keyCap: 's' }, // [USB: 0x16]
- 0x54: { code: 'KeyT', keyCap: 't' }, // [USB: 0x17]
- 0x55: { code: 'KeyU', keyCap: 'u' }, // [USB: 0x18]
- 0x56: { code: 'KeyV', keyCap: 'v' }, // [USB: 0x19]
- 0x57: { code: 'KeyW', keyCap: 'w' }, // [USB: 0x1a]
- 0x58: { code: 'KeyX', keyCap: 'x' }, // [USB: 0x1b]
- 0x59: { code: 'KeyY', keyCap: 'y' }, // [USB: 0x1c]
- 0x5A: { code: 'KeyZ', keyCap: 'z' }, // [USB: 0x1d]
- 0x5B: { code: 'OSLeft', location: LEFT }, // [USB: 0xe3]
- 0x5C: { code: 'OSRight', location: RIGHT }, // [USB: 0xe7]
- 0x5D: { code: 'ContextMenu' }, // [USB: 0x65] Context Menu
- // 0x5E - reserved
- 0x5F: { code: 'Standby' }, // [USB: 0x82] Sleep
-
- 0x60: { code: 'Numpad0', keyCap: '0', location: NUMPAD }, // [USB: 0x62]
- 0x61: { code: 'Numpad1', keyCap: '1', location: NUMPAD }, // [USB: 0x59]
- 0x62: { code: 'Numpad2', keyCap: '2', location: NUMPAD }, // [USB: 0x5a]
- 0x63: { code: 'Numpad3', keyCap: '3', location: NUMPAD }, // [USB: 0x5b]
- 0x64: { code: 'Numpad4', keyCap: '4', location: NUMPAD }, // [USB: 0x5c]
- 0x65: { code: 'Numpad5', keyCap: '5', location: NUMPAD }, // [USB: 0x5d]
- 0x66: { code: 'Numpad6', keyCap: '6', location: NUMPAD }, // [USB: 0x5e]
- 0x67: { code: 'Numpad7', keyCap: '7', location: NUMPAD }, // [USB: 0x5f]
- 0x68: { code: 'Numpad8', keyCap: '8', location: NUMPAD }, // [USB: 0x60]
- 0x69: { code: 'Numpad9', keyCap: '9', location: NUMPAD }, // [USB: 0x61]
- 0x6A: { code: 'NumpadMultiply', keyCap: '*', location: NUMPAD }, // [USB: 0x55]
- 0x6B: { code: 'NumpadAdd', keyCap: '+', location: NUMPAD }, // [USB: 0x57]
- 0x6C: { code: 'NumpadComma', keyCap: ',', location: NUMPAD }, // [USB: 0x85]
- 0x6D: { code: 'NumpadSubtract', keyCap: '-', location: NUMPAD }, // [USB: 0x56]
- 0x6E: { code: 'NumpadDecimal', keyCap: '.', location: NUMPAD }, // [USB: 0x63]
- 0x6F: { code: 'NumpadDivide', keyCap: '/', location: NUMPAD }, // [USB: 0x54]
-
- 0x70: { code: 'F1' }, // [USB: 0x3a]
- 0x71: { code: 'F2' }, // [USB: 0x3b]
- 0x72: { code: 'F3' }, // [USB: 0x3c]
- 0x73: { code: 'F4' }, // [USB: 0x3d]
- 0x74: { code: 'F5' }, // [USB: 0x3e]
- 0x75: { code: 'F6' }, // [USB: 0x3f]
- 0x76: { code: 'F7' }, // [USB: 0x40]
- 0x77: { code: 'F8' }, // [USB: 0x41]
- 0x78: { code: 'F9' }, // [USB: 0x42]
- 0x79: { code: 'F10' }, // [USB: 0x43]
- 0x7A: { code: 'F11' }, // [USB: 0x44]
- 0x7B: { code: 'F12' }, // [USB: 0x45]
- 0x7C: { code: 'F13' }, // [USB: 0x68]
- 0x7D: { code: 'F14' }, // [USB: 0x69]
- 0x7E: { code: 'F15' }, // [USB: 0x6a]
- 0x7F: { code: 'F16' }, // [USB: 0x6b]
-
- 0x80: { code: 'F17' }, // [USB: 0x6c]
- 0x81: { code: 'F18' }, // [USB: 0x6d]
- 0x82: { code: 'F19' }, // [USB: 0x6e]
- 0x83: { code: 'F20' }, // [USB: 0x6f]
- 0x84: { code: 'F21' }, // [USB: 0x70]
- 0x85: { code: 'F22' }, // [USB: 0x71]
- 0x86: { code: 'F23' }, // [USB: 0x72]
- 0x87: { code: 'F24' }, // [USB: 0x73]
- // 0x88-0x8F - unassigned
-
- 0x90: { code: 'NumLock', location: NUMPAD }, // [USB: 0x53]
- 0x91: { code: 'ScrollLock' }, // [USB: 0x47]
- // 0x92-0x96 - OEM specific
- // 0x97-0x9F - unassigned
-
- // NOTE: 0xA0-0xA5 usually mapped to 0x10-0x12 in browsers
- 0xA0: { code: 'ShiftLeft', location: LEFT }, // [USB: 0xe1]
- 0xA1: { code: 'ShiftRight', location: RIGHT }, // [USB: 0xe5]
- 0xA2: { code: 'ControlLeft', location: LEFT }, // [USB: 0xe0]
- 0xA3: { code: 'ControlRight', location: RIGHT }, // [USB: 0xe4]
- 0xA4: { code: 'AltLeft', location: LEFT }, // [USB: 0xe2]
- 0xA5: { code: 'AltRight', location: RIGHT }, // [USB: 0xe6]
-
- 0xA6: { code: 'BrowserBack' }, // [USB: 0x0c/0x0224]
- 0xA7: { code: 'BrowserForward' }, // [USB: 0x0c/0x0225]
- 0xA8: { code: 'BrowserRefresh' }, // [USB: 0x0c/0x0227]
- 0xA9: { code: 'BrowserStop' }, // [USB: 0x0c/0x0226]
- 0xAA: { code: 'BrowserSearch' }, // [USB: 0x0c/0x0221]
- 0xAB: { code: 'BrowserFavorites' }, // [USB: 0x0c/0x0228]
- 0xAC: { code: 'BrowserHome' }, // [USB: 0x0c/0x0222]
- 0xAD: { code: 'VolumeMute' }, // [USB: 0x7f]
- 0xAE: { code: 'VolumeDown' }, // [USB: 0x81]
- 0xAF: { code: 'VolumeUp' }, // [USB: 0x80]
-
- 0xB0: { code: 'MediaTrackNext' }, // [USB: 0x0c/0x00b5]
- 0xB1: { code: 'MediaTrackPrevious' }, // [USB: 0x0c/0x00b6]
- 0xB2: { code: 'MediaStop' }, // [USB: 0x0c/0x00b7]
- 0xB3: { code: 'MediaPlayPause' }, // [USB: 0x0c/0x00cd]
- 0xB4: { code: 'LaunchMail' }, // [USB: 0x0c/0x018a]
- 0xB5: { code: 'MediaSelect' },
- 0xB6: { code: 'LaunchApp1' },
- 0xB7: { code: 'LaunchApp2' },
- // 0xB8-0xB9 - reserved
- 0xBA: { code: 'Semicolon', keyCap: ';' }, // [USB: 0x33] ;: (US Standard 101)
- 0xBB: { code: 'Equal', keyCap: '=' }, // [USB: 0x2e] =+
- 0xBC: { code: 'Comma', keyCap: ',' }, // [USB: 0x36] ,<
- 0xBD: { code: 'Minus', keyCap: '-' }, // [USB: 0x2d] -_
- 0xBE: { code: 'Period', keyCap: '.' }, // [USB: 0x37] .>
- 0xBF: { code: 'Slash', keyCap: '/' }, // [USB: 0x38] /? (US Standard 101)
-
- 0xC0: { code: 'Backquote', keyCap: '`' }, // [USB: 0x35] `~ (US Standard 101)
- // 0xC1-0xCF - reserved
-
- // 0xD0-0xD7 - reserved
- // 0xD8-0xDA - unassigned
- 0xDB: { code: 'BracketLeft', keyCap: '[' }, // [USB: 0x2f] [{ (US Standard 101)
- 0xDC: { code: 'Backslash', keyCap: '\\' }, // [USB: 0x31] \| (US Standard 101)
- 0xDD: { code: 'BracketRight', keyCap: ']' }, // [USB: 0x30] ]} (US Standard 101)
- 0xDE: { code: 'Quote', keyCap: '\'' }, // [USB: 0x34] '" (US Standard 101)
- // 0xDF - miscellaneous/varies
-
- // 0xE0 - reserved
- // 0xE1 - OEM specific
- 0xE2: { code: 'IntlBackslash', keyCap: '\\' }, // [USB: 0x64] \| (UK Standard 102)
- // 0xE3-0xE4 - OEM specific
- 0xE5: { code: 'Process' }, // (Not in D3E)
- // 0xE6 - OEM specific
- // 0xE7 - VK_PACKET
- // 0xE8 - unassigned
- // 0xE9-0xEF - OEM specific
-
- // 0xF0-0xF5 - OEM specific
- 0xF6: { code: 'Attn' }, // [USB: 0x9a] (Not in D3E)
- 0xF7: { code: 'CrSel' }, // [USB: 0xa3] (Not in D3E)
- 0xF8: { code: 'ExSel' }, // [USB: 0xa4] (Not in D3E)
- 0xF9: { code: 'EraseEof' }, // (Not in D3E)
- 0xFA: { code: 'Play' }, // (Not in D3E)
- 0xFB: { code: 'ZoomToggle' }, // (Not in D3E)
- // 0xFC - VK_NONAME - reserved
- // 0xFD - VK_PA1
- 0xFE: { code: 'Clear' // [USB: 0x9c] (Not in D3E)
- } };
-
- // No legacy keyCode, but listed in D3E:
-
- // code: usb
- // 'IntlHash': 0x070032,
- // 'IntlRo': 0x070087,
- // 'IntlYen': 0x070089,
- // 'NumpadBackspace': 0x0700bb,
- // 'NumpadClear': 0x0700d8,
- // 'NumpadClearEntry': 0x0700d9,
- // 'NumpadMemoryAdd': 0x0700d3,
- // 'NumpadMemoryClear': 0x0700d2,
- // 'NumpadMemoryRecall': 0x0700d1,
- // 'NumpadMemoryStore': 0x0700d0,
- // 'NumpadMemorySubtract': 0x0700d4,
- // 'NumpadParenLeft': 0x0700b6,
- // 'NumpadParenRight': 0x0700b7,
-
- //--------------------------------------------------------------------
- //
- // Browser/OS Specific Mappings
- //
- //--------------------------------------------------------------------
-
- mergeIf(keyCodeToInfoTable, 'moz', {
- 0x3B: { code: 'Semicolon', keyCap: ';' }, // [USB: 0x33] ;: (US Standard 101)
- 0x3D: { code: 'Equal', keyCap: '=' }, // [USB: 0x2e] =+
- 0x6B: { code: 'Equal', keyCap: '=' }, // [USB: 0x2e] =+
- 0x6D: { code: 'Minus', keyCap: '-' }, // [USB: 0x2d] -_
- 0xBB: { code: 'NumpadAdd', keyCap: '+', location: NUMPAD }, // [USB: 0x57]
- 0xBD: { code: 'NumpadSubtract', keyCap: '-', location: NUMPAD // [USB: 0x56]
- } });
-
- mergeIf(keyCodeToInfoTable, 'moz-mac', {
- 0x0C: { code: 'NumLock', location: NUMPAD }, // [USB: 0x53]
- 0xAD: { code: 'Minus', keyCap: '-' // [USB: 0x2d] -_
- } });
-
- mergeIf(keyCodeToInfoTable, 'moz-win', {
- 0xAD: { code: 'Minus', keyCap: '-' // [USB: 0x2d] -_
- } });
-
- mergeIf(keyCodeToInfoTable, 'chrome-mac', {
- 0x5D: { code: 'OSRight', location: RIGHT // [USB: 0xe7]
- } });
-
- // Windows via Bootcamp (!)
- if (0) {
- mergeIf(keyCodeToInfoTable, 'chrome-win', {
- 0xC0: { code: 'Quote', keyCap: '\'' }, // [USB: 0x34] '" (US Standard 101)
- 0xDE: { code: 'Backslash', keyCap: '\\' }, // [USB: 0x31] \| (US Standard 101)
- 0xDF: { code: 'Backquote', keyCap: '`' // [USB: 0x35] `~ (US Standard 101)
- } });
-
- mergeIf(keyCodeToInfoTable, 'ie', {
- 0xC0: { code: 'Quote', keyCap: '\'' }, // [USB: 0x34] '" (US Standard 101)
- 0xDE: { code: 'Backslash', keyCap: '\\' }, // [USB: 0x31] \| (US Standard 101)
- 0xDF: { code: 'Backquote', keyCap: '`' // [USB: 0x35] `~ (US Standard 101)
- } });
- }
-
- mergeIf(keyCodeToInfoTable, 'safari', {
- 0x03: { code: 'Enter' }, // [USB: 0x28] old Safari
- 0x19: { code: 'Tab' // [USB: 0x2b] old Safari for Shift+Tab
- } });
-
- mergeIf(keyCodeToInfoTable, 'ios', {
- 0x0A: { code: 'Enter', location: STANDARD // [USB: 0x28]
- } });
-
- mergeIf(keyCodeToInfoTable, 'safari-mac', {
- 0x5B: { code: 'OSLeft', location: LEFT }, // [USB: 0xe3]
- 0x5D: { code: 'OSRight', location: RIGHT }, // [USB: 0xe7]
- 0xE5: { code: 'KeyQ', keyCap: 'Q' // [USB: 0x14] On alternate presses, Ctrl+Q sends this
- } });
-
- //--------------------------------------------------------------------
- //
- // Identifier Mappings
- //
- //--------------------------------------------------------------------
-
- // Cases where newer-ish browsers send keyIdentifier which can be
- // used to disambiguate keys.
-
- // keyIdentifierTable[keyIdentifier] -> keyInfo
-
- var keyIdentifierTable = {};
- if ('cros' === os) {
- keyIdentifierTable['U+00A0'] = { code: 'ShiftLeft', location: LEFT };
- keyIdentifierTable['U+00A1'] = { code: 'ShiftRight', location: RIGHT };
- keyIdentifierTable['U+00A2'] = { code: 'ControlLeft', location: LEFT };
- keyIdentifierTable['U+00A3'] = { code: 'ControlRight', location: RIGHT };
- keyIdentifierTable['U+00A4'] = { code: 'AltLeft', location: LEFT };
- keyIdentifierTable['U+00A5'] = { code: 'AltRight', location: RIGHT };
- }
- if ('chrome-mac' === browser_os) {
- keyIdentifierTable['U+0010'] = { code: 'ContextMenu' };
- }
- if ('safari-mac' === browser_os) {
- keyIdentifierTable['U+0010'] = { code: 'ContextMenu' };
- }
- if ('ios' === os) {
- // These only generate keyup events
- keyIdentifierTable['U+0010'] = { code: 'Function' };
-
- keyIdentifierTable['U+001C'] = { code: 'ArrowLeft' };
- keyIdentifierTable['U+001D'] = { code: 'ArrowRight' };
- keyIdentifierTable['U+001E'] = { code: 'ArrowUp' };
- keyIdentifierTable['U+001F'] = { code: 'ArrowDown' };
-
- keyIdentifierTable['U+0001'] = { code: 'Home' }; // [USB: 0x4a] Fn + ArrowLeft
- keyIdentifierTable['U+0004'] = { code: 'End' }; // [USB: 0x4d] Fn + ArrowRight
- keyIdentifierTable['U+000B'] = { code: 'PageUp' }; // [USB: 0x4b] Fn + ArrowUp
- keyIdentifierTable['U+000C'] = { code: 'PageDown' }; // [USB: 0x4e] Fn + ArrowDown
- }
-
- //--------------------------------------------------------------------
- //
- // Location Mappings
- //
- //--------------------------------------------------------------------
-
- // Cases where newer-ish browsers send location/keyLocation which
- // can be used to disambiguate keys.
-
- // locationTable[location][keyCode] -> keyInfo
- var locationTable = [];
- locationTable[LEFT] = {
- 0x10: { code: 'ShiftLeft', location: LEFT }, // [USB: 0xe1]
- 0x11: { code: 'ControlLeft', location: LEFT }, // [USB: 0xe0]
- 0x12: { code: 'AltLeft', location: LEFT // [USB: 0xe2]
- } };
- locationTable[RIGHT] = {
- 0x10: { code: 'ShiftRight', location: RIGHT }, // [USB: 0xe5]
- 0x11: { code: 'ControlRight', location: RIGHT }, // [USB: 0xe4]
- 0x12: { code: 'AltRight', location: RIGHT // [USB: 0xe6]
- } };
- locationTable[NUMPAD] = {
- 0x0D: { code: 'NumpadEnter', location: NUMPAD // [USB: 0x58]
- } };
-
- mergeIf(locationTable[NUMPAD], 'moz', {
- 0x6D: { code: 'NumpadSubtract', location: NUMPAD }, // [USB: 0x56]
- 0x6B: { code: 'NumpadAdd', location: NUMPAD // [USB: 0x57]
- } });
- mergeIf(locationTable[LEFT], 'moz-mac', {
- 0xE0: { code: 'OSLeft', location: LEFT // [USB: 0xe3]
- } });
- mergeIf(locationTable[RIGHT], 'moz-mac', {
- 0xE0: { code: 'OSRight', location: RIGHT // [USB: 0xe7]
- } });
- mergeIf(locationTable[RIGHT], 'moz-win', {
- 0x5B: { code: 'OSRight', location: RIGHT // [USB: 0xe7]
- } });
-
- mergeIf(locationTable[RIGHT], 'mac', {
- 0x5D: { code: 'OSRight', location: RIGHT // [USB: 0xe7]
- } });
-
- mergeIf(locationTable[NUMPAD], 'chrome-mac', {
- 0x0C: { code: 'NumLock', location: NUMPAD // [USB: 0x53]
- } });
-
- mergeIf(locationTable[NUMPAD], 'safari-mac', {
- 0x0C: { code: 'NumLock', location: NUMPAD }, // [USB: 0x53]
- 0xBB: { code: 'NumpadAdd', location: NUMPAD }, // [USB: 0x57]
- 0xBD: { code: 'NumpadSubtract', location: NUMPAD }, // [USB: 0x56]
- 0xBE: { code: 'NumpadDecimal', location: NUMPAD }, // [USB: 0x63]
- 0xBF: { code: 'NumpadDivide', location: NUMPAD // [USB: 0x54]
- } });
-
- //--------------------------------------------------------------------
- //
- // Key Values
- //
- //--------------------------------------------------------------------
-
- // Mapping from `code` values to `key` values. Values defined at:
- // https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3Events-key.html
- // Entries are only provided when `key` differs from `code`. If
- // printable, `shiftKey` has the shifted printable character. This
- // assumes US Standard 101 layout
-
- var codeToKeyTable = {
- // Modifier Keys
- ShiftLeft: { key: 'Shift' },
- ShiftRight: { key: 'Shift' },
- ControlLeft: { key: 'Control' },
- ControlRight: { key: 'Control' },
- AltLeft: { key: 'Alt' },
- AltRight: { key: 'Alt' },
- OSLeft: { key: 'OS' },
- OSRight: { key: 'OS' },
-
- // Whitespace Keys
- NumpadEnter: { key: 'Enter' },
- Space: { key: ' ' },
-
- // Printable Keys
- Digit0: { key: '0', shiftKey: ')' },
- Digit1: { key: '1', shiftKey: '!' },
- Digit2: { key: '2', shiftKey: '@' },
- Digit3: { key: '3', shiftKey: '#' },
- Digit4: { key: '4', shiftKey: '$' },
- Digit5: { key: '5', shiftKey: '%' },
- Digit6: { key: '6', shiftKey: '^' },
- Digit7: { key: '7', shiftKey: '&' },
- Digit8: { key: '8', shiftKey: '*' },
- Digit9: { key: '9', shiftKey: '(' },
- KeyA: { key: 'a', shiftKey: 'A' },
- KeyB: { key: 'b', shiftKey: 'B' },
- KeyC: { key: 'c', shiftKey: 'C' },
- KeyD: { key: 'd', shiftKey: 'D' },
- KeyE: { key: 'e', shiftKey: 'E' },
- KeyF: { key: 'f', shiftKey: 'F' },
- KeyG: { key: 'g', shiftKey: 'G' },
- KeyH: { key: 'h', shiftKey: 'H' },
- KeyI: { key: 'i', shiftKey: 'I' },
- KeyJ: { key: 'j', shiftKey: 'J' },
- KeyK: { key: 'k', shiftKey: 'K' },
- KeyL: { key: 'l', shiftKey: 'L' },
- KeyM: { key: 'm', shiftKey: 'M' },
- KeyN: { key: 'n', shiftKey: 'N' },
- KeyO: { key: 'o', shiftKey: 'O' },
- KeyP: { key: 'p', shiftKey: 'P' },
- KeyQ: { key: 'q', shiftKey: 'Q' },
- KeyR: { key: 'r', shiftKey: 'R' },
- KeyS: { key: 's', shiftKey: 'S' },
- KeyT: { key: 't', shiftKey: 'T' },
- KeyU: { key: 'u', shiftKey: 'U' },
- KeyV: { key: 'v', shiftKey: 'V' },
- KeyW: { key: 'w', shiftKey: 'W' },
- KeyX: { key: 'x', shiftKey: 'X' },
- KeyY: { key: 'y', shiftKey: 'Y' },
- KeyZ: { key: 'z', shiftKey: 'Z' },
- Numpad0: { key: '0' },
- Numpad1: { key: '1' },
- Numpad2: { key: '2' },
- Numpad3: { key: '3' },
- Numpad4: { key: '4' },
- Numpad5: { key: '5' },
- Numpad6: { key: '6' },
- Numpad7: { key: '7' },
- Numpad8: { key: '8' },
- Numpad9: { key: '9' },
- NumpadMultiply: { key: '*' },
- NumpadAdd: { key: '+' },
- NumpadComma: { key: ',' },
- NumpadSubtract: { key: '-' },
- NumpadDecimal: { key: '.' },
- NumpadDivide: { key: '/' },
- Semicolon: { key: ';', shiftKey: ':' },
- Equal: { key: '=', shiftKey: '+' },
- Comma: { key: ',', shiftKey: '<' },
- Minus: { key: '-', shiftKey: '_' },
- Period: { key: '.', shiftKey: '>' },
- Slash: { key: '/', shiftKey: '?' },
- Backquote: { key: '`', shiftKey: '~' },
- BracketLeft: { key: '[', shiftKey: '{' },
- Backslash: { key: '\\', shiftKey: '|' },
- BracketRight: { key: ']', shiftKey: '}' },
- Quote: { key: '\'', shiftKey: '"' },
- IntlBackslash: { key: '\\', shiftKey: '|' }
- };
-
- mergeIf(codeToKeyTable, 'mac', {
- OSLeft: { key: 'Meta' },
- OSRight: { key: 'Meta' }
- });
-
- // Corrections for 'key' names in older browsers (e.g. FF36-)
- // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent.key#Key_values
- var keyFixTable = {
- Esc: 'Escape',
- Nonconvert: 'NonConvert',
- Left: 'ArrowLeft',
- Up: 'ArrowUp',
- Right: 'ArrowRight',
- Down: 'ArrowDown',
- Del: 'Delete',
- Menu: 'ContextMenu',
- MediaNextTrack: 'MediaTrackNext',
- MediaPreviousTrack: 'MediaTrackPrevious',
- SelectMedia: 'MediaSelect',
- HalfWidth: 'Hankaku',
- FullWidth: 'Zenkaku',
- RomanCharacters: 'Romaji',
- Crsel: 'CrSel',
- Exsel: 'ExSel',
- Zoom: 'ZoomToggle'
- };
-
- //--------------------------------------------------------------------
- //
- // Exported Functions
- //
- //--------------------------------------------------------------------
-
-
- var codeTable = remap(keyCodeToInfoTable, 'code');
-
- try {
- var nativeLocation = nativeKeyboardEvent && 'location' in new KeyboardEvent('');
- } catch (_) {}
-
- function keyInfoForEvent(event) {
- var keyCode = 'keyCode' in event ? event.keyCode : 'which' in event ? event.which : 0;
-
- var keyInfo = function () {
- if (nativeLocation || 'keyLocation' in event) {
- var location = nativeLocation ? event.location : event.keyLocation;
- if (location && keyCode in locationTable[location]) {
- return locationTable[location][keyCode];
- }
- }
- if ('keyIdentifier' in event && event.keyIdentifier in keyIdentifierTable) {
- return keyIdentifierTable[event.keyIdentifier];
- }
- if (keyCode in keyCodeToInfoTable) {
- return keyCodeToInfoTable[keyCode];
- }
- return null;
- }();
-
- // TODO: Track these down and move to general tables
- if (0) {
- // TODO: Map these for newerish browsers?
- // TODO: iOS only?
- // TODO: Override with more common keyIdentifier name?
- switch (event.keyIdentifier) {
- case 'U+0010':
- keyInfo = { code: 'Function' };break;
- case 'U+001C':
- keyInfo = { code: 'ArrowLeft' };break;
- case 'U+001D':
- keyInfo = { code: 'ArrowRight' };break;
- case 'U+001E':
- keyInfo = { code: 'ArrowUp' };break;
- case 'U+001F':
- keyInfo = { code: 'ArrowDown' };break;
- }
- }
-
- if (!keyInfo) return null;
-
- var key = function () {
- var entry = codeToKeyTable[keyInfo.code];
- if (!entry) return keyInfo.code;
- return event.shiftKey && 'shiftKey' in entry ? entry.shiftKey : entry.key;
- }();
-
- return {
- code: keyInfo.code,
- key: key,
- location: keyInfo.location,
- keyCap: keyInfo.keyCap
- };
- }
-
- function queryKeyCap(code, locale) {
- code = String(code);
- if (!codeTable.hasOwnProperty(code)) return 'Undefined';
- if (locale && String(locale).toLowerCase() !== 'en-us') throw Error('Unsupported locale');
- var keyInfo = codeTable[code];
- return keyInfo.keyCap || keyInfo.code || 'Undefined';
- }
-
- if ('KeyboardEvent' in global && 'defineProperty' in Object) {
- (function () {
- function define(o, p, v) {
- if (p in o) return;
- Object.defineProperty(o, p, v);
- }
-
- define(KeyboardEvent.prototype, 'code', { get: function get() {
- var keyInfo = keyInfoForEvent(this);
- return keyInfo ? keyInfo.code : '';
- } });
-
- // Fix for nonstandard `key` values (FF36-)
- if ('key' in KeyboardEvent.prototype) {
- var desc = Object.getOwnPropertyDescriptor(KeyboardEvent.prototype, 'key');
- Object.defineProperty(KeyboardEvent.prototype, 'key', { get: function get() {
- var key = desc.get.call(this);
- return keyFixTable.hasOwnProperty(key) ? keyFixTable[key] : key;
- } });
- }
-
- define(KeyboardEvent.prototype, 'key', { get: function get() {
- var keyInfo = keyInfoForEvent(this);
- return keyInfo && 'key' in keyInfo ? keyInfo.key : 'Unidentified';
- } });
-
- define(KeyboardEvent.prototype, 'location', { get: function get() {
- var keyInfo = keyInfoForEvent(this);
- return keyInfo && 'location' in keyInfo ? keyInfo.location : STANDARD;
- } });
-
- define(KeyboardEvent.prototype, 'locale', { get: function get() {
- return '';
- } });
- })();
- }
-
- if (!('queryKeyCap' in global.KeyboardEvent)) global.KeyboardEvent.queryKeyCap = queryKeyCap;
-
- // Helper for IE8-
- global.identifyKey = function (event) {
- if ('code' in event) return;
-
- var keyInfo = keyInfoForEvent(event);
- event.code = keyInfo ? keyInfo.code : '';
- event.key = keyInfo && 'key' in keyInfo ? keyInfo.key : 'Unidentified';
- event.location = 'location' in event ? event.location : 'keyLocation' in event ? event.keyLocation : keyInfo && 'location' in keyInfo ? keyInfo.location : STANDARD;
- event.locale = '';
- };
-})(window);
-
-},{}],11:[function(require,module,exports){
-var e=function(){};e.computeCentroids=function(e){var t,n,r;for(t=0,n=e.faces.length;t=0&&n.push(e)}),n.length<2)return[];n.includes(e[0])&&n.includes(e[e.length-1])&&e.push(e.shift()),n.includes(t[0])&&n.includes(t[t.length-1])&&t.push(t.shift()),n=[],e.forEach(function(e){t.includes(e)&&n.push(e)});for(var r=n[1],o=n[0],i=e.slice();i[0]!==r;)i.push(i.shift());for(var s=0,u=t.slice();u[0]!==o;)if(u.push(u.shift()),s++>10)throw new Error("Unexpected state");return u.shift(),u.pop(),i=i.concat(u)},e.setPolygonCentroid=function(e,t){var n=new THREE.Vector3,r=t.vertices;e.vertexIds.forEach(function(e){n.add(r[e])}),n.divideScalar(e.vertexIds.length),e.centroid.copy(n)},e.cleanPolygon=function(e,t){for(var n=[],r=t.vertices,o=0;oMath.PI-.01&&d0?function(e){e<0&&(r=!1)}:function(e){e>0&&(r=!1)}),r},e.distanceToSquared=function(e,t){var n=e.x-t.x,r=e.y-t.y,o=e.z-t.z;return n*n+r*r+o*o},e.isPointInPoly=function(e,t){for(var n=!1,r=-1,o=e.length,i=o-1;++rr-.5&&this.isPointInPoly(i,e))},e.triarea2=function(e,t,n){return(n.x-e.x)*(t.z-e.z)-(t.x-e.x)*(n.z-e.z)},e.vequal=function(e,t){return this.distanceToSquared(e,t)<1e-5};var t=function(e){this.content=[],this.scoreFunction=e};t.prototype.push=function(e){this.content.push(e),this.sinkDown(this.content.length-1)},t.prototype.pop=function(){var e=this.content[0],t=this.content.pop();return this.content.length>0&&(this.content[0]=t,this.bubbleUp(0)),e},t.prototype.remove=function(e){var t=this.content.indexOf(e),n=this.content.pop();t!==this.content.length-1&&(this.content[t]=n,this.scoreFunction(n)0;){var n=(e+1>>1)-1,r=this.content[n];if(!(this.scoreFunction(t)0;){var o=r.pop();if(o===n){for(var i=o,s=[];i.parent;)s.push(i),i=i.parent;return this.cleanUp(s),s.reverse()}o.closed=!0;for(var u=this.neighbours(e,o),c=0,h=u.length;c0)){i.push(n),n=t=n,r=t,u=s=u,c=s,h=s;continue}r=d,c=h}if(e.triarea2(t,n,a)>=0){if(!(e.vequal(t,n)||e.triarea2(t,r,a)<0)){i.push(r),n=t=r,r=t,u=s=c,c=s,h=s;continue}n=a,u=h}}return 0!==i.length&&e.vequal(i[i.length-1],o[o.length-1].left)||i.push(o[o.length-1].left),this.path=i,i};var s,u,c,h,a,d,f=function(){this.zones={}};f.createZone=function(e){return o.buildZone(e)},f.prototype.setZoneData=function(e,t){this.zones[e]=t},f.prototype.getGroup=function(t,n){if(!this.zones[t])return null;var r=null,o=Math.pow(50,2);return this.zones[t].groups.forEach(function(t,i){t.forEach(function(t){var s=e.distanceToSquared(t.centroid,n);s2))for(var I=0;I JOYSTICK_EPS || Math.abs(inputY) > JOYSTICK_EPS;
- },
-
- getVelocityDelta: function getVelocityDelta() {
- var dpad = this._dpadVector;
- var joystick = this._moveVector;
-
- this.getDpad(dpad);
- this.getJoystick(Joystick.MOVEMENT, joystick);
-
- var inputX = dpad.x || joystick.x;
- var inputY = dpad.y || joystick.y;
- var dVelocity = new THREE.Vector3();
-
- if (Math.abs(inputX) > JOYSTICK_EPS) {
- dVelocity.x += inputX;
- }
- if (Math.abs(inputY) > JOYSTICK_EPS) {
- dVelocity.z += inputY;
- }
-
- return dVelocity;
- },
-
- /*******************************************************************
- * Rotation
- */
-
- isRotationActive: function isRotationActive() {
- if (!this.data.enabled || !this.isConnected()) return false;
-
- var joystick = this._lookVector;
-
- this.getJoystick(Joystick.ROTATION, joystick);
-
- return Math.abs(joystick.x) > JOYSTICK_EPS || Math.abs(joystick.y) > JOYSTICK_EPS;
- },
-
- updateRotation: function updateRotation(dt) {
- if (!this.isRotationActive()) return;
-
- var data = this.data;
- var yaw = this.yaw;
- var pitch = this.pitch;
- var lookControls = data.camera.components['look-controls'];
- var hasLookControls = lookControls && lookControls.pitchObject && lookControls.yawObject;
-
- // Sync with look-controls pitch/yaw if available.
- if (hasLookControls) {
- pitch.rotation.copy(lookControls.pitchObject.rotation);
- yaw.rotation.copy(lookControls.yawObject.rotation);
- }
-
- var lookVector = this._lookVector;
-
- this.getJoystick(Joystick.ROTATION, lookVector);
-
- if (Math.abs(lookVector.x) <= JOYSTICK_EPS) lookVector.x = 0;
- if (Math.abs(lookVector.y) <= JOYSTICK_EPS) lookVector.y = 0;
-
- lookVector.multiplyScalar(data.rotationSensitivity * dt / 1000);
- yaw.rotation.y -= lookVector.x;
- pitch.rotation.x -= lookVector.y;
- pitch.rotation.x = Math.max(-Math.PI / 2, Math.min(Math.PI / 2, pitch.rotation.x));
- data.camera.object3D.rotation.set(pitch.rotation.x, yaw.rotation.y, 0);
-
- // Sync with look-controls pitch/yaw if available.
- if (hasLookControls) {
- lookControls.pitchObject.rotation.copy(pitch.rotation);
- lookControls.yawObject.rotation.copy(yaw.rotation);
- }
- },
-
- /*******************************************************************
- * Button events
- */
-
- updateButtonState: function updateButtonState() {
- var gamepad = this.getGamepad(Hand.RIGHT);
- if (this.data.enabled && gamepad) {
-
- // Fire DOM events for button state changes.
- for (var i = 0; i < gamepad.buttons.length; i++) {
- if (gamepad.buttons[i].pressed && !this.buttons[i]) {
- this.emit(new GamepadButtonEvent('gamepadbuttondown', i, gamepad.buttons[i]));
- } else if (!gamepad.buttons[i].pressed && this.buttons[i]) {
- this.emit(new GamepadButtonEvent('gamepadbuttonup', i, gamepad.buttons[i]));
- }
- this.buttons[i] = gamepad.buttons[i].pressed;
- }
- } else if (Object.keys(this.buttons)) {
- // Reset state if controls are disabled or controller is lost.
- this.buttons = {};
- }
- },
-
- emit: function emit(event) {
- // Emit original event.
- this.el.emit(event.type, event);
-
- // Emit convenience event, identifying button index.
- this.el.emit(event.type + ':' + event.index, new GamepadButtonEvent(event.type, event.index, event));
- },
-
- /*******************************************************************
- * Gamepad state
- */
-
- /**
- * Returns the Gamepad instance attached to the component. If connected,
- * a proxy-controls component may provide access to Gamepad input from a
- * remote device.
- *
- * @param {string} handPreference
- * @return {Gamepad}
- */
- getGamepad: function () {
- var _xrGamepads = [];
- var _empty = [];
-
- return function (handPreference) {
- // https://github.com/donmccurdy/aframe-proxy-controls
- var proxyControls = this.el.sceneEl.components['proxy-controls'];
- var proxyGamepad = proxyControls && proxyControls.isConnected() && proxyControls.getGamepad(0);
- if (proxyGamepad) return proxyGamepad;
-
- // https://www.w3.org/TR/webxr/#dom-xrinputsource-handedness
- _xrGamepads.length = 0;
- for (var i = 0; i < this.system.controllers.length; i++) {
- var xrController = this.system.controllers[i];
- var xrGamepad = xrController ? xrController.gamepad : null;
- _xrGamepads.push(xrGamepad);
- if (xrGamepad && xrGamepad.handedness === handPreference) return xrGamepad;
- }
-
- // https://developer.mozilla.org/en-US/docs/Web/API/Gamepad/hand
- var navGamepads = navigator.getGamepads ? navigator.getGamepads() : _empty;
- for (var _i = 0; _i < navGamepads.length; _i++) {
- var navGamepad = navGamepads[_i];
- if (navGamepad && navGamepad.hand === handPreference) return navGamepad;
- }
-
- return _xrGamepads[0] || navGamepads[0];
- };
- }(),
-
- /**
- * Returns the state of the given button.
- * @param {number} index The button (0-N) for which to find state.
- * @return {GamepadButton}
- */
- getButton: function getButton(index) {
- return this.getGamepad(Hand.RIGHT).buttons[index];
- },
-
- /**
- * Returns state of the given axis. Axes are labelled 0-N, where 0-1 will
- * represent X/Y on the first joystick, and 2-3 X/Y on the second.
- * @param {number} index The axis (0-N) for which to find state.
- * @return {number} On the interval [-1,1].
- */
- getAxis: function getAxis(index) {
- return this.getGamepad(index > 1 ? Hand.RIGHT : Hand.LEFT).axes[index];
- },
-
- /**
- * Returns the state of the specified joystick as a THREE.Vector2.
- * @param {Joystick} role
- * @param {THREE.Vector2} target
- * @return {THREE.Vector2}
- */
- getJoystick: function getJoystick(index, target) {
- var gamepad = this.getGamepad(index === Joystick.MOVEMENT ? Hand.LEFT : Hand.RIGHT);
- if (gamepad.mapping === 'xr-standard') {
- // See: https://github.com/donmccurdy/aframe-extras/issues/307
- switch (index) {
- case Joystick.MOVEMENT:
- return target.set(gamepad.axes[2], gamepad.axes[3]);
- case Joystick.ROTATION:
- return target.set(gamepad.axes[0], gamepad.axes[1]);
- }
- } else {
- switch (index) {
- case Joystick.MOVEMENT:
- return target.set(gamepad.axes[0], gamepad.axes[1]);
- case Joystick.ROTATION:
- return target.set(gamepad.axes[2], gamepad.axes[3]);
- }
- }
- throw new Error('Unexpected joystick index "%d".', index);
- },
-
- /**
- * Returns the state of the dpad as a THREE.Vector2.
- * @param {THREE.Vector2} target
- * @return {THREE.Vector2}
- */
- getDpad: function getDpad(target) {
- var gamepad = this.getGamepad(Hand.LEFT);
- if (!gamepad.buttons[GamepadButton.DPAD_RIGHT]) {
- return target.set(0, 0);
- }
- return target.set((gamepad.buttons[GamepadButton.DPAD_RIGHT].pressed ? 1 : 0) + (gamepad.buttons[GamepadButton.DPAD_LEFT].pressed ? -1 : 0), (gamepad.buttons[GamepadButton.DPAD_UP].pressed ? -1 : 0) + (gamepad.buttons[GamepadButton.DPAD_DOWN].pressed ? 1 : 0));
- },
-
- /**
- * Returns true if the gamepad is currently connected to the system.
- * @return {boolean}
- */
- isConnected: function isConnected() {
- var gamepad = this.getGamepad(Hand.LEFT);
- return !!(gamepad && gamepad.connected);
- },
-
- /**
- * Returns a string containing some information about the controller. Result
- * may vary across browsers, for a given controller.
- * @return {string}
- */
- getID: function getID() {
- return this.getGamepad(Hand.LEFT).id;
- }
-});
-
-},{"../../lib/GamepadButton":5,"../../lib/GamepadButtonEvent":6}],14:[function(require,module,exports){
-'use strict';
-
-require('./checkpoint-controls');
-require('./gamepad-controls');
-require('./keyboard-controls');
-require('./touch-controls');
-require('./movement-controls');
-require('./trackpad-controls');
-
-},{"./checkpoint-controls":12,"./gamepad-controls":13,"./keyboard-controls":15,"./movement-controls":16,"./touch-controls":17,"./trackpad-controls":18}],15:[function(require,module,exports){
-'use strict';
-
-require('../../lib/keyboard.polyfill');
-
-var MAX_DELTA = 0.2,
- PROXY_FLAG = '__keyboard-controls-proxy';
-
-var KeyboardEvent = window.KeyboardEvent;
-
-/**
- * Keyboard Controls component.
- *
- * Stripped-down version of: https://github.com/donmccurdy/aframe-keyboard-controls
- *
- * Bind keyboard events to components, or control your entities with the WASD keys.
- *
- * Why use KeyboardEvent.code? "This is set to a string representing the key that was pressed to
- * generate the KeyboardEvent, without taking the current keyboard layout (e.g., QWERTY vs.
- * Dvorak), locale (e.g., English vs. French), or any modifier keys into account. This is useful
- * when you care about which physical key was pressed, rather thanwhich character it corresponds
- * to. For example, if you’re a writing a game, you might want a certain set of keys to move the
- * player in different directions, and that mapping should ideally be independent of keyboard
- * layout. See: https://developers.google.com/web/updates/2016/04/keyboardevent-keys-codes
- *
- * @namespace wasd-controls
- * keys the entity moves and if you release it will stop. Easing simulates friction.
- * to the entity when pressing the keys.
- * @param {bool} [enabled=true] - To completely enable or disable the controls
- */
-module.exports = AFRAME.registerComponent('keyboard-controls', {
- schema: {
- enabled: { default: true },
- debug: { default: false }
- },
-
- init: function init() {
- this.dVelocity = new THREE.Vector3();
- this.localKeys = {};
- this.listeners = {
- keydown: this.onKeyDown.bind(this),
- keyup: this.onKeyUp.bind(this),
- blur: this.onBlur.bind(this),
- onContextMenu: this.onContextMenu.bind(this)
- };
- this.attachEventListeners();
- },
-
- /*******************************************************************
- * Movement
- */
-
- isVelocityActive: function isVelocityActive() {
- return this.data.enabled && !!Object.keys(this.getKeys()).length;
- },
-
- getVelocityDelta: function getVelocityDelta() {
- var data = this.data,
- keys = this.getKeys();
-
- this.dVelocity.set(0, 0, 0);
- if (data.enabled) {
- if (keys.KeyW || keys.ArrowUp) {
- this.dVelocity.z -= 1;
- }
- if (keys.KeyA || keys.ArrowLeft) {
- this.dVelocity.x -= 1;
- }
- if (keys.KeyS || keys.ArrowDown) {
- this.dVelocity.z += 1;
- }
- if (keys.KeyD || keys.ArrowRight) {
- this.dVelocity.x += 1;
- }
- }
-
- return this.dVelocity.clone();
- },
-
- /*******************************************************************
- * Events
- */
-
- play: function play() {
- this.attachEventListeners();
- },
-
- pause: function pause() {
- this.removeEventListeners();
- },
-
- remove: function remove() {
- this.pause();
- },
-
- attachEventListeners: function attachEventListeners() {
- window.oncontextmenu = this.listeners.onContextMenu;
- window.addEventListener("keydown", this.listeners.keydown, false);
- window.addEventListener("keyup", this.listeners.keyup, false);
- window.addEventListener("blur", this.listeners.blur, false);
- },
-
- onContextMenu: function onContextMenu() {
- for (var code in this.localKeys) {
- if (this.localKeys.hasOwnProperty(code)) {
- delete this.localKeys[code];
- }
- }
- },
-
- removeEventListeners: function removeEventListeners() {
- window.removeEventListener('keydown', this.listeners.keydown);
- window.removeEventListener('keyup', this.listeners.keyup);
- window.removeEventListener('blur', this.listeners.blur);
- },
-
- onKeyDown: function onKeyDown(event) {
- if (AFRAME.utils.shouldCaptureKeyEvent(event)) {
- this.localKeys[event.code] = true;
- this.emit(event);
- }
- },
-
- onKeyUp: function onKeyUp(event) {
- if (AFRAME.utils.shouldCaptureKeyEvent(event)) {
- delete this.localKeys[event.code];
- this.emit(event);
- }
- },
-
- onBlur: function onBlur() {
- for (var code in this.localKeys) {
- if (this.localKeys.hasOwnProperty(code)) {
- delete this.localKeys[code];
- }
- }
- },
-
- emit: function emit(event) {
- // TODO - keydown only initially?
- // TODO - where the f is the spacebar
-
- // Emit original event.
- if (PROXY_FLAG in event) {
- // TODO - Method never triggered.
- this.el.emit(event.type, event);
- }
-
- // Emit convenience event, identifying key.
- this.el.emit(event.type + ':' + event.code, new KeyboardEvent(event.type, event));
- if (this.data.debug) console.log(event.type + ':' + event.code);
- },
-
- /*******************************************************************
- * Accessors
- */
-
- isPressed: function isPressed(code) {
- return code in this.getKeys();
- },
-
- getKeys: function getKeys() {
- if (this.isProxied()) {
- return this.el.sceneEl.components['proxy-controls'].getKeyboard();
- }
- return this.localKeys;
- },
-
- isProxied: function isProxied() {
- var proxyControls = this.el.sceneEl.components['proxy-controls'];
- return proxyControls && proxyControls.isConnected();
- }
-
-});
-
-},{"../../lib/keyboard.polyfill":10}],16:[function(require,module,exports){
-'use strict';
-
-/**
- * Movement Controls
- *
- * @author Don McCurdy
- */
-
-var COMPONENT_SUFFIX = '-controls',
- MAX_DELTA = 0.2,
-
-// ms
-EPS = 10e-6;
-
-module.exports = AFRAME.registerComponent('movement-controls', {
-
- /*******************************************************************
- * Schema
- */
-
- dependencies: ['rotation'],
-
- schema: {
- enabled: { default: true },
- controls: { default: ['gamepad', 'trackpad', 'keyboard', 'touch'] },
- speed: { default: 0.3, min: 0 },
- fly: { default: false },
- constrainToNavMesh: { default: false },
- camera: { default: '[movement-controls] [camera]', type: 'selector' }
- },
-
- /*******************************************************************
- * Lifecycle
- */
-
- init: function init() {
- var el = this.el;
-
- this.velocityCtrl = null;
-
- this.velocity = new THREE.Vector3();
- this.heading = new THREE.Quaternion();
-
- // Navigation
- this.navGroup = null;
- this.navNode = null;
-
- if (el.sceneEl.hasLoaded) {
- this.injectControls();
- } else {
- el.sceneEl.addEventListener('loaded', this.injectControls.bind(this));
- }
- },
-
- update: function update(prevData) {
- var el = this.el;
- var data = this.data;
- var nav = el.sceneEl.systems.nav;
- if (el.sceneEl.hasLoaded) {
- this.injectControls();
- }
- if (nav && data.constrainToNavMesh !== prevData.constrainToNavMesh) {
- data.constrainToNavMesh ? nav.addAgent(this) : nav.removeAgent(this);
- }
- },
-
- injectControls: function injectControls() {
- var data = this.data;
- var name;
-
- for (var i = 0; i < data.controls.length; i++) {
- name = data.controls[i] + COMPONENT_SUFFIX;
- if (!this.el.components[name]) {
- this.el.setAttribute(name, '');
- }
- }
- },
-
- updateNavLocation: function updateNavLocation() {
- this.navGroup = null;
- this.navNode = null;
- },
-
- /*******************************************************************
- * Tick
- */
-
- tick: function () {
- var start = new THREE.Vector3();
- var end = new THREE.Vector3();
- var clampedEnd = new THREE.Vector3();
-
- return function (t, dt) {
- if (!dt) return;
-
- var el = this.el;
- var data = this.data;
-
- if (!data.enabled) return;
-
- this.updateVelocityCtrl();
- var velocityCtrl = this.velocityCtrl;
- var velocity = this.velocity;
-
- if (!velocityCtrl) return;
-
- // Update velocity. If FPS is too low, reset.
- if (dt / 1000 > MAX_DELTA) {
- velocity.set(0, 0, 0);
- } else {
- this.updateVelocity(dt);
- }
-
- if (data.constrainToNavMesh && velocityCtrl.isNavMeshConstrained !== false) {
-
- if (velocity.lengthSq() < EPS) return;
-
- start.copy(el.object3D.position);
- end.copy(velocity).multiplyScalar(dt / 1000).add(start);
-
- var nav = el.sceneEl.systems.nav;
- this.navGroup = this.navGroup === null ? nav.getGroup(start) : this.navGroup;
- this.navNode = this.navNode || nav.getNode(start, this.navGroup);
- this.navNode = nav.clampStep(start, end, this.navGroup, this.navNode, clampedEnd);
- el.object3D.position.copy(clampedEnd);
- } else if (el.hasAttribute('velocity')) {
- el.setAttribute('velocity', velocity);
- } else {
- el.object3D.position.x += velocity.x * dt / 1000;
- el.object3D.position.y += velocity.y * dt / 1000;
- el.object3D.position.z += velocity.z * dt / 1000;
- }
- };
- }(),
-
- /*******************************************************************
- * Movement
- */
-
- updateVelocityCtrl: function updateVelocityCtrl() {
- var data = this.data;
- if (data.enabled) {
- for (var i = 0, l = data.controls.length; i < l; i++) {
- var control = this.el.components[data.controls[i] + COMPONENT_SUFFIX];
- if (control && control.isVelocityActive()) {
- this.velocityCtrl = control;
- return;
- }
- }
- this.velocityCtrl = null;
- }
- },
-
- updateVelocity: function () {
- var vector2 = new THREE.Vector2();
- var quaternion = new THREE.Quaternion();
-
- return function (dt) {
- var dVelocity = void 0;
- var el = this.el;
- var control = this.velocityCtrl;
- var velocity = this.velocity;
- var data = this.data;
-
- if (control) {
- if (control.getVelocityDelta) {
- dVelocity = control.getVelocityDelta(dt);
- } else if (control.getVelocity) {
- velocity.copy(control.getVelocity());
- return;
- } else if (control.getPositionDelta) {
- velocity.copy(control.getPositionDelta(dt).multiplyScalar(1000 / dt));
- return;
- } else {
- throw new Error('Incompatible movement controls: ', control);
- }
- }
-
- if (el.hasAttribute('velocity') && !data.constrainToNavMesh) {
- velocity.copy(this.el.getAttribute('velocity'));
- }
-
- if (dVelocity && data.enabled) {
- var cameraEl = data.camera;
-
- // Rotate to heading
- quaternion.copy(cameraEl.object3D.quaternion);
- quaternion.premultiply(el.object3D.quaternion);
- dVelocity.applyQuaternion(quaternion);
-
- var factor = dVelocity.length();
- if (data.fly) {
- velocity.copy(dVelocity);
- velocity.multiplyScalar(this.data.speed * 16.66667);
- } else {
- vector2.set(dVelocity.x, dVelocity.z);
- vector2.setLength(factor * this.data.speed * 16.66667);
- velocity.x = vector2.x;
- velocity.z = vector2.y;
- }
- }
- };
- }()
-});
-
-},{}],17:[function(require,module,exports){
-'use strict';
-
-/**
- * Touch-to-move-forward controls for mobile.
- */
-
-module.exports = AFRAME.registerComponent('touch-controls', {
- schema: {
- enabled: { default: true },
- reverseEnabled: { default: true }
- },
-
- init: function init() {
- this.dVelocity = new THREE.Vector3();
- this.bindMethods();
- this.direction = 0;
- },
-
- play: function play() {
- this.addEventListeners();
- },
-
- pause: function pause() {
- this.removeEventListeners();
- this.dVelocity.set(0, 0, 0);
- },
-
- remove: function remove() {
- this.pause();
- },
-
- addEventListeners: function addEventListeners() {
- var sceneEl = this.el.sceneEl;
- var canvasEl = sceneEl.canvas;
-
- if (!canvasEl) {
- sceneEl.addEventListener('render-target-loaded', this.addEventListeners.bind(this));
- return;
- }
-
- canvasEl.addEventListener('touchstart', this.onTouchStart);
- canvasEl.addEventListener('touchend', this.onTouchEnd);
- },
-
- removeEventListeners: function removeEventListeners() {
- var canvasEl = this.el.sceneEl && this.el.sceneEl.canvas;
- if (!canvasEl) {
- return;
- }
-
- canvasEl.removeEventListener('touchstart', this.onTouchStart);
- canvasEl.removeEventListener('touchend', this.onTouchEnd);
- },
-
- isVelocityActive: function isVelocityActive() {
- return this.data.enabled && !!this.direction;
- },
-
- getVelocityDelta: function getVelocityDelta() {
- this.dVelocity.z = this.direction;
- return this.dVelocity.clone();
- },
-
- bindMethods: function bindMethods() {
- this.onTouchStart = this.onTouchStart.bind(this);
- this.onTouchEnd = this.onTouchEnd.bind(this);
- },
-
- onTouchStart: function onTouchStart(e) {
- this.direction = -1;
- if (this.data.reverseEnabled && e.touches.length === 2) {
- this.direction = 1;
- }
- e.preventDefault();
- },
-
- onTouchEnd: function onTouchEnd(e) {
- this.direction = 0;
- e.preventDefault();
- }
-});
-
-},{}],18:[function(require,module,exports){
-'use strict';
-
-/**
- * 3dof (Gear VR, Daydream) controls for mobile.
- */
-
-module.exports = AFRAME.registerComponent('trackpad-controls', {
- schema: {
- enabled: { default: true },
- enableNegX: { default: true },
- enablePosX: { default: true },
- enableNegZ: { default: true },
- enablePosZ: { default: true },
- mode: { default: 'touch', oneOf: ['swipe', 'touch', 'press'] }
-
- },
-
- init: function init() {
- this.dVelocity = new THREE.Vector3();
- this.zVel = 0;
- this.xVel = 0;
- this.bindMethods();
- },
-
- play: function play() {
- this.addEventListeners();
- },
-
- pause: function pause() {
- this.removeEventListeners();
- this.dVelocity.set(0, 0, 0);
- },
-
- remove: function remove() {
- this.pause();
- },
-
- addEventListeners: function addEventListeners() {
- var data = this.data;
- var sceneEl = this.el.sceneEl;
-
- sceneEl.addEventListener('axismove', this.onAxisMove);
-
- switch (data.mode) {
- case 'swipe':
- case 'touch':
- sceneEl.addEventListener('trackpadtouchstart', this.onTouchStart);
- sceneEl.addEventListener('trackpadtouchend', this.onTouchEnd);
- break;
-
- case 'press':
- sceneEl.addEventListener('trackpaddown', this.onTouchStart);
- sceneEl.addEventListener('trackpadup', this.onTouchEnd);
- break;
- }
- },
-
- removeEventListeners: function removeEventListeners() {
- var sceneEl = this.el.sceneEl;
-
- sceneEl.removeEventListener('axismove', this.onAxisMove);
- sceneEl.removeEventListener('trackpadtouchstart', this.onTouchStart);
- sceneEl.removeEventListener('trackpadtouchend', this.onTouchEnd);
- sceneEl.removeEventListener('trackpaddown', this.onTouchStart);
- sceneEl.removeEventListener('trackpadup', this.onTouchEnd);
- },
-
- isVelocityActive: function isVelocityActive() {
- return this.data.enabled && this.isMoving;
- },
-
- getVelocityDelta: function getVelocityDelta() {
- this.dVelocity.z = this.isMoving ? -this.zVel : 1;
- this.dVelocity.x = this.isMoving ? this.xVel : 1;
- return this.dVelocity.clone();
- },
-
- bindMethods: function bindMethods() {
- this.onTouchStart = this.onTouchStart.bind(this);
- this.onTouchEnd = this.onTouchEnd.bind(this);
- this.onAxisMove = this.onAxisMove.bind(this);
- },
-
- onTouchStart: function onTouchStart(e) {
- switch (this.data.mode) {
- case 'swipe':
- this.canRecordAxis = true;
- this.startingAxisData = [];
- break;
- case 'touch':
- this.isMoving = true;
- break;
- case 'press':
- this.isMoving = true;
- break;
- }
-
- e.preventDefault();
- },
-
- onTouchEnd: function onTouchEnd(e) {
- if (this.data.mode == 'swipe') {
- this.startingAxisData = [];
- }
-
- this.isMoving = false;
- e.preventDefault();
- },
-
- onAxisMove: function onAxisMove(e) {
- switch (this.data.mode) {
- case 'swipe':
- return this.handleSwipeAxis(e);
- case 'touch':
- case 'press':
- return this.handleTouchAxis(e);
- }
- },
-
- handleSwipeAxis: function handleSwipeAxis(e) {
- var data = this.data;
- var axisData = e.detail.axis;
-
- if (this.startingAxisData.length === 0 && this.canRecordAxis) {
- this.canRecordAxis = false;
- this.startingAxisData[0] = axisData[0];
- this.startingAxisData[1] = axisData[1];
- }
-
- if (this.startingAxisData.length > 0) {
- var velX = 0;
- var velZ = 0;
-
- if (data.enableNegX && axisData[0] < this.startingAxisData[0]) {
- velX = -1;
- }
-
- if (data.enablePosX && axisData[0] > this.startingAxisData[0]) {
- velX = 1;
- }
-
- if (data.enablePosZ && axisData[1] > this.startingAxisData[1]) {
- velZ = -1;
- }
-
- if (data.enableNegZ && axisData[1] < this.startingAxisData[1]) {
- velZ = 1;
- }
-
- var absChangeZ = Math.abs(this.startingAxisData[1] - axisData[1]);
- var absChangeX = Math.abs(this.startingAxisData[0] - axisData[0]);
-
- if (absChangeX > absChangeZ) {
- this.zVel = 0;
- this.xVel = velX;
- this.isMoving = true;
- } else {
- this.xVel = 0;
- this.zVel = velZ;
- this.isMoving = true;
- }
- }
- },
-
- handleTouchAxis: function handleTouchAxis(e) {
- var data = this.data;
- var axisData = e.detail.axis;
-
- var velX = 0;
- var velZ = 0;
-
- if (data.enableNegX && axisData[0] < 0) {
- velX = -1;
- }
-
- if (data.enablePosX && axisData[0] > 0) {
- velX = 1;
- }
-
- if (data.enablePosZ && axisData[1] > 0) {
- velZ = -1;
- }
-
- if (data.enableNegZ && axisData[1] < 0) {
- velZ = 1;
- }
-
- if (Math.abs(axisData[0]) > Math.abs(axisData[1])) {
- this.zVel = 0;
- this.xVel = velX;
- } else {
- this.xVel = 0;
- this.zVel = velZ;
- }
- }
-
-});
-
-},{}],19:[function(require,module,exports){
-'use strict';
-
-var LoopMode = {
- once: THREE.LoopOnce,
- repeat: THREE.LoopRepeat,
- pingpong: THREE.LoopPingPong
-};
-
-/**
- * animation-mixer
- *
- * Player for animation clips. Intended to be compatible with any model format that supports
- * skeletal or morph animations through THREE.AnimationMixer.
- * See: https://threejs.org/docs/?q=animation#Reference/Animation/AnimationMixer
- */
-module.exports = AFRAME.registerComponent('animation-mixer', {
- schema: {
- clip: { default: '*' },
- duration: { default: 0 },
- clampWhenFinished: { default: false, type: 'boolean' },
- crossFadeDuration: { default: 0 },
- loop: { default: 'repeat', oneOf: Object.keys(LoopMode) },
- repetitions: { default: Infinity, min: 0 },
- timeScale: { default: 1 }
- },
-
- init: function init() {
- var _this = this;
-
- /** @type {THREE.Mesh} */
- this.model = null;
- /** @type {THREE.AnimationMixer} */
- this.mixer = null;
- /** @type {Array} */
- this.activeActions = [];
-
- var model = this.el.getObject3D('mesh');
-
- if (model) {
- this.load(model);
- } else {
- this.el.addEventListener('model-loaded', function (e) {
- _this.load(e.detail.model);
- });
- }
- },
-
- load: function load(model) {
- var el = this.el;
- this.model = model;
- this.mixer = new THREE.AnimationMixer(model);
- this.mixer.addEventListener('loop', function (e) {
- el.emit('animation-loop', { action: e.action, loopDelta: e.loopDelta });
- });
- this.mixer.addEventListener('finished', function (e) {
- el.emit('animation-finished', { action: e.action, direction: e.direction });
- });
- if (this.data.clip) this.update({});
- },
-
- remove: function remove() {
- if (this.mixer) this.mixer.stopAllAction();
- },
-
- update: function update(prevData) {
- if (!prevData) return;
-
- var data = this.data;
- var changes = AFRAME.utils.diff(data, prevData);
-
- // If selected clips have changed, restart animation.
- if ('clip' in changes) {
- this.stopAction();
- if (data.clip) this.playAction();
- return;
- }
-
- // Otherwise, modify running actions.
- this.activeActions.forEach(function (action) {
- if ('duration' in changes && data.duration) {
- action.setDuration(data.duration);
- }
- if ('clampWhenFinished' in changes) {
- action.clampWhenFinished = data.clampWhenFinished;
- }
- if ('loop' in changes || 'repetitions' in changes) {
- action.setLoop(LoopMode[data.loop], data.repetitions);
- }
- if ('timeScale' in changes) {
- action.setEffectiveTimeScale(data.timeScale);
- }
- });
- },
-
- stopAction: function stopAction() {
- var data = this.data;
- for (var i = 0; i < this.activeActions.length; i++) {
- data.crossFadeDuration ? this.activeActions[i].fadeOut(data.crossFadeDuration) : this.activeActions[i].stop();
- }
- this.activeActions.length = 0;
- },
-
- playAction: function playAction() {
- if (!this.mixer) return;
-
- var model = this.model,
- data = this.data,
- clips = model.animations || (model.geometry || {}).animations || [];
-
- if (!clips.length) return;
-
- var re = wildcardToRegExp(data.clip);
-
- for (var clip, i = 0; clip = clips[i]; i++) {
- if (clip.name.match(re)) {
- var action = this.mixer.clipAction(clip, model);
- action.enabled = true;
- action.clampWhenFinished = data.clampWhenFinished;
- if (data.duration) action.setDuration(data.duration);
- if (data.timeScale !== 1) action.setEffectiveTimeScale(data.timeScale);
- action.setLoop(LoopMode[data.loop], data.repetitions).fadeIn(data.crossFadeDuration).play();
- this.activeActions.push(action);
- }
- }
- },
-
- tick: function tick(t, dt) {
- if (this.mixer && !isNaN(dt)) this.mixer.update(dt / 1000);
- }
-});
-
-/**
- * Creates a RegExp from the given string, converting asterisks to .* expressions,
- * and escaping all other characters.
- */
-function wildcardToRegExp(s) {
- return new RegExp('^' + s.split(/\*+/).map(regExpEscape).join('.*') + '$');
-}
-
-/**
- * RegExp-escapes all characters in the given string.
- */
-function regExpEscape(s) {
- return s.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&');
-}
-
-},{}],20:[function(require,module,exports){
-'use strict';
-
-THREE.ColladaLoader = require('../../lib/ColladaLoader');
-
-/**
- * collada-model-legacy
- *
- * Loader for COLLADA (.dae) format.
- */
-module.exports.Component = AFRAME.registerComponent('collada-model-legacy', {
- schema: { type: 'asset' },
-
- init: function init() {
- this.model = null;
- this.loader = new THREE.ColladaLoader();
- },
-
- update: function update() {
- var self = this;
- var el = this.el;
- var src = this.data;
- var rendererSystem = this.el.sceneEl.systems.renderer;
-
- if (!src) {
- return;
- }
-
- this.remove();
-
- this.loader.load(src, function (colladaModel) {
- self.model = colladaModel.scene;
- self.model.traverse(function (object) {
- if (object.isMesh) {
- var material = object.material;
- if (material.color) rendererSystem.applyColorCorrection(material.color);
- if (material.map) rendererSystem.applyColorCorrection(material.map);
- if (material.emissive) rendererSystem.applyColorCorrection(material.emissive);
- if (material.emissiveMap) rendererSystem.applyColorCorrection(material.emissiveMap);
- }
- });
- el.setObject3D('mesh', self.model);
- el.emit('model-loaded', { format: 'collada', model: self.model });
- });
- },
-
- remove: function remove() {
- if (!this.model) {
- return;
- }
- this.el.removeObject3D('mesh');
- }
-});
-
-},{"../../lib/ColladaLoader":3}],21:[function(require,module,exports){
-'use strict';
-
-THREE.FBXLoader = require('../../lib/FBXLoader');
-
-/**
- * fbx-model
- *
- * Loader for FBX format. Supports ASCII, but *not* binary, models.
- */
-module.exports = AFRAME.registerComponent('fbx-model', {
- schema: {
- src: { type: 'asset' },
- crossorigin: { default: '' }
- },
-
- init: function init() {
- this.model = null;
- },
-
- update: function update() {
- var data = this.data;
- if (!data.src) return;
-
- this.remove();
- var loader = new THREE.FBXLoader();
- if (data.crossorigin) loader.setCrossOrigin(data.crossorigin);
- loader.load(data.src, this.load.bind(this));
- },
-
- load: function load(model) {
- this.model = model;
- this.el.setObject3D('mesh', model);
- this.el.emit('model-loaded', { format: 'fbx', model: model });
- },
-
- remove: function remove() {
- if (this.model) this.el.removeObject3D('mesh');
- }
-});
-
-},{"../../lib/FBXLoader":4}],22:[function(require,module,exports){
-'use strict';
-
-var fetchScript = require('../../lib/fetch-script')();
-
-var LOADER_SRC = 'https://cdn.jsdelivr.net/gh/mrdoob/three.js@r86/examples/js/loaders/GLTFLoader.js';
-
-var loadLoader = function () {
- var promise = void 0;
- return function () {
- promise = promise || fetchScript(LOADER_SRC);
- return promise;
- };
-}();
-
-/**
- * Legacy loader for glTF 1.0 models.
- * Asynchronously loads THREE.GLTFLoader from jsdelivr.
- */
-module.exports = AFRAME.registerComponent('gltf-model-legacy', {
- schema: { type: 'model' },
-
- init: function init() {
- var _this = this;
-
- this.model = null;
- this.loader = null;
- this.loaderPromise = loadLoader().then(function () {
- _this.loader = new THREE.GLTFLoader();
- _this.loader.setCrossOrigin('Anonymous');
- });
- },
-
- update: function update() {
- var _this2 = this;
-
- var self = this;
- var el = this.el;
- var src = this.data;
-
- if (!src) {
- return;
- }
-
- this.remove();
-
- this.loaderPromise.then(function () {
- _this2.loader.load(src, function gltfLoaded(gltfModel) {
- self.model = gltfModel.scene;
- self.model.animations = gltfModel.animations;
- el.setObject3D('mesh', self.model);
- el.emit('model-loaded', { format: 'gltf', model: self.model });
- });
- });
- },
-
- remove: function remove() {
- if (!this.model) {
- return;
- }
- this.el.removeObject3D('mesh');
- }
-});
-
-},{"../../lib/fetch-script":8}],23:[function(require,module,exports){
-'use strict';
-
-require('./animation-mixer');
-require('./collada-model-legacy');
-require('./fbx-model');
-require('./gltf-model-legacy');
-require('./object-model');
-
-},{"./animation-mixer":19,"./collada-model-legacy":20,"./fbx-model":21,"./gltf-model-legacy":22,"./object-model":24}],24:[function(require,module,exports){
-'use strict';
-
-/**
- * object-model
- *
- * Loader for THREE.js JSON format. Somewhat confusingly, there are two different THREE.js formats,
- * both having the .json extension. This loader supports only THREE.ObjectLoader, which typically
- * includes multiple meshes or an entire scene.
- *
- * Check the console for errors, if in doubt. You may need to use `json-model` or
- * `blend-character-model` for some .js and .json files.
- *
- * See: https://clara.io/learn/user-guide/data_exchange/threejs_export
- */
-
-module.exports = AFRAME.registerComponent('object-model', {
- schema: {
- src: { type: 'asset' },
- crossorigin: { default: '' }
- },
-
- init: function init() {
- this.model = null;
- },
-
- update: function update() {
- var _this = this;
-
- var loader = void 0;
- var data = this.data;
- if (!data.src) return;
-
- this.remove();
- loader = new THREE.ObjectLoader();
- if (data.crossorigin) loader.setCrossOrigin(data.crossorigin);
- loader.load(data.src, function (object) {
-
- // Enable skinning, if applicable.
- object.traverse(function (o) {
- if (o instanceof THREE.SkinnedMesh && o.material) {
- o.material.skinning = !!(o.geometry && o.geometry.bones || []).length;
- }
- });
-
- _this.load(object);
- });
- },
-
- load: function load(model) {
- this.model = model;
- this.el.setObject3D('mesh', model);
- this.el.emit('model-loaded', { format: 'json', model: model });
- },
-
- remove: function remove() {
- if (this.model) this.el.removeObject3D('mesh');
- }
-});
-
-},{}],25:[function(require,module,exports){
-'use strict';
-
-module.exports = AFRAME.registerComponent('checkpoint', {
- schema: {
- offset: { default: { x: 0, y: 0, z: 0 }, type: 'vec3' }
- },
-
- init: function init() {
- this.active = false;
- this.targetEl = null;
- this.fire = this.fire.bind(this);
- this.offset = new THREE.Vector3();
- },
-
- update: function update() {
- this.offset.copy(this.data.offset);
- },
-
- play: function play() {
- this.el.addEventListener('click', this.fire);
- },
- pause: function pause() {
- this.el.removeEventListener('click', this.fire);
- },
- remove: function remove() {
- this.pause();
- },
-
- fire: function fire() {
- var targetEl = this.el.sceneEl.querySelector('[checkpoint-controls]');
- if (!targetEl) {
- throw new Error('No `checkpoint-controls` component found.');
- }
- targetEl.components['checkpoint-controls'].setCheckpoint(this.el);
- },
-
- getOffset: function getOffset() {
- return this.offset.copy(this.data.offset);
- }
-});
-
-},{}],26:[function(require,module,exports){
-'use strict';
-
-/**
- * @param {Array|THREE.Material} material
- * @return {Array}
- */
-
-function ensureMaterialArray(material) {
- if (!material) {
- return [];
- } else if (Array.isArray(material)) {
- return material;
- } else if (material.materials) {
- return material.materials;
- } else {
- return [material];
- }
-}
-
-/**
- * @param {THREE.Object3D} mesh
- * @param {Array} materialNames
- * @param {THREE.Texture} envMap
- * @param {number} reflectivity [description]
- */
-function applyEnvMap(mesh, materialNames, envMap, reflectivity) {
- if (!mesh) return;
-
- materialNames = materialNames || [];
-
- mesh.traverse(function (node) {
-
- if (!node.isMesh) return;
-
- var meshMaterials = ensureMaterialArray(node.material);
-
- meshMaterials.forEach(function (material) {
-
- if (material && !('envMap' in material)) return;
- if (materialNames.length && materialNames.indexOf(material.name) === -1) return;
-
- material.envMap = envMap;
- material.reflectivity = reflectivity;
- material.needsUpdate = true;
- });
- });
-}
-
-/**
- * Specifies an envMap on an entity, without replacing any existing material
- * properties.
- */
-module.exports = AFRAME.registerComponent('cube-env-map', {
- multiple: true,
-
- schema: {
- path: { default: '' },
- extension: { default: 'jpg', oneOf: ['jpg', 'png'] },
- format: { default: 'RGBFormat', oneOf: ['RGBFormat', 'RGBAFormat'] },
- enableBackground: { default: false },
- reflectivity: { default: 1, min: 0, max: 1 },
- materials: { default: [] }
- },
-
- init: function init() {
- var _this = this;
-
- var data = this.data;
-
- this.texture = new THREE.CubeTextureLoader().load([data.path + 'posx.' + data.extension, data.path + 'negx.' + data.extension, data.path + 'posy.' + data.extension, data.path + 'negy.' + data.extension, data.path + 'posz.' + data.extension, data.path + 'negz.' + data.extension]);
- this.texture.format = THREE[data.format];
-
- this.object3dsetHandler = function () {
- var mesh = _this.el.getObject3D('mesh');
- var data = _this.data;
- applyEnvMap(mesh, data.materials, _this.texture, data.reflectivity);
- };
- this.el.addEventListener('object3dset', this.object3dsetHandler);
- },
-
- update: function update(oldData) {
- var data = this.data;
- var mesh = this.el.getObject3D('mesh');
-
- var addedMaterialNames = [];
- var removedMaterialNames = [];
-
- if (data.materials.length) {
- if (oldData.materials) {
- addedMaterialNames = data.materials.filter(function (name) {
- return !oldData.materials.includes(name);
- });
- removedMaterialNames = oldData.materials.filter(function (name) {
- return !data.materials.includes(name);
- });
- } else {
- addedMaterialNames = data.materials;
- }
- }
- if (addedMaterialNames.length) {
- applyEnvMap(mesh, addedMaterialNames, this.texture, data.reflectivity);
- }
- if (removedMaterialNames.length) {
- applyEnvMap(mesh, removedMaterialNames, null, 1);
- }
-
- if (oldData.materials && data.reflectivity !== oldData.reflectivity) {
- var maintainedMaterialNames = data.materials.filter(function (name) {
- return oldData.materials.includes(name);
- });
- if (maintainedMaterialNames.length) {
- applyEnvMap(mesh, maintainedMaterialNames, this.texture, data.reflectivity);
- }
- }
-
- if (this.data.enableBackground && !oldData.enableBackground) {
- this.setBackground(this.texture);
- } else if (!this.data.enableBackground && oldData.enableBackground) {
- this.setBackground(null);
- }
- },
-
- remove: function remove() {
- this.el.removeEventListener('object3dset', this.object3dsetHandler);
- var mesh = this.el.getObject3D('mesh');
- var data = this.data;
-
- applyEnvMap(mesh, data.materials, null, 1);
- if (data.enableBackground) this.setBackground(null);
- },
-
- setBackground: function setBackground(texture) {
- this.el.sceneEl.object3D.background = texture;
- }
-});
-
-},{}],27:[function(require,module,exports){
-'use strict';
-
-/* global CANNON */
-
-/**
- * Based on aframe/examples/showcase/tracked-controls.
- *
- * Handles events coming from the hand-controls.
- * Determines if the entity is grabbed or released.
- * Updates its position to move along the controller.
- */
-
-module.exports = AFRAME.registerComponent('grab', {
- init: function init() {
- this.system = this.el.sceneEl.systems.physics;
-
- this.GRABBED_STATE = 'grabbed';
-
- this.grabbing = false;
- this.hitEl = /** @type {AFRAME.Element} */null;
- this.physics = /** @type {AFRAME.System} */this.el.sceneEl.systems.physics;
- this.constraint = /** @type {CANNON.Constraint} */null;
-
- // Bind event handlers
- this.onHit = this.onHit.bind(this);
- this.onGripOpen = this.onGripOpen.bind(this);
- this.onGripClose = this.onGripClose.bind(this);
- },
-
- play: function play() {
- var el = this.el;
- el.addEventListener('hit', this.onHit);
- el.addEventListener('gripdown', this.onGripClose);
- el.addEventListener('gripup', this.onGripOpen);
- el.addEventListener('trackpaddown', this.onGripClose);
- el.addEventListener('trackpadup', this.onGripOpen);
- el.addEventListener('triggerdown', this.onGripClose);
- el.addEventListener('triggerup', this.onGripOpen);
- },
-
- pause: function pause() {
- var el = this.el;
- el.removeEventListener('hit', this.onHit);
- el.removeEventListener('gripdown', this.onGripClose);
- el.removeEventListener('gripup', this.onGripOpen);
- el.removeEventListener('trackpaddown', this.onGripClose);
- el.removeEventListener('trackpadup', this.onGripOpen);
- el.removeEventListener('triggerdown', this.onGripClose);
- el.removeEventListener('triggerup', this.onGripOpen);
- },
-
- onGripClose: function onGripClose() {
- this.grabbing = true;
- },
-
- onGripOpen: function onGripOpen() {
- var hitEl = this.hitEl;
- this.grabbing = false;
- if (!hitEl) {
- return;
- }
- hitEl.removeState(this.GRABBED_STATE);
- this.hitEl = undefined;
- this.system.removeConstraint(this.constraint);
- this.constraint = null;
- },
-
- onHit: function onHit(evt) {
- var hitEl = evt.detail.el;
- // If the element is already grabbed (it could be grabbed by another controller).
- // If the hand is not grabbing the element does not stick.
- // If we're already grabbing something you can't grab again.
- if (!hitEl || hitEl.is(this.GRABBED_STATE) || !this.grabbing || this.hitEl) {
- return;
- }
- hitEl.addState(this.GRABBED_STATE);
- this.hitEl = hitEl;
- this.constraint = new CANNON.LockConstraint(this.el.body, hitEl.body);
- this.system.addConstraint(this.constraint);
- }
-});
-
-},{}],28:[function(require,module,exports){
-'use strict';
-
-require('./checkpoint');
-require('./cube-env-map');
-require('./grab');
-require('./jump-ability');
-require('./kinematic-body');
-require('./mesh-smooth');
-require('./normal-material');
-require('./sphere-collider');
-
-},{"./checkpoint":25,"./cube-env-map":26,"./grab":27,"./jump-ability":29,"./kinematic-body":30,"./mesh-smooth":31,"./normal-material":32,"./sphere-collider":33}],29:[function(require,module,exports){
-'use strict';
-
-var ACCEL_G = -9.8,
-
-// m/s^2
-EASING = -15; // m/s^2
-
-/**
- * Jump ability.
- */
-module.exports = AFRAME.registerComponent('jump-ability', {
- dependencies: ['velocity'],
-
- /* Schema
- ——————————————————————————————————————————————*/
-
- schema: {
- on: { default: 'keydown:Space gamepadbuttondown:0' },
- playerHeight: { default: 1.764 },
- maxJumps: { default: 1 },
- distance: { default: 5 },
- debug: { default: false }
- },
-
- init: function init() {
- this.velocity = 0;
- this.numJumps = 0;
-
- var beginJump = this.beginJump.bind(this),
- events = this.data.on.split(' ');
- this.bindings = {};
- for (var i = 0; i < events.length; i++) {
- this.bindings[events[i]] = beginJump;
- this.el.addEventListener(events[i], beginJump);
- }
- this.bindings.collide = this.onCollide.bind(this);
- this.el.addEventListener('collide', this.bindings.collide);
- },
-
- remove: function remove() {
- for (var event in this.bindings) {
- if (this.bindings.hasOwnProperty(event)) {
- this.el.removeEventListener(event, this.bindings[event]);
- delete this.bindings[event];
- }
- }
- this.el.removeEventListener('collide', this.bindings.collide);
- delete this.bindings.collide;
- },
-
- beginJump: function beginJump() {
- if (this.numJumps < this.data.maxJumps) {
- var data = this.data,
- initialVelocity = Math.sqrt(-2 * data.distance * (ACCEL_G + EASING)),
- v = this.el.getAttribute('velocity');
- this.el.setAttribute('velocity', { x: v.x, y: initialVelocity, z: v.z });
- this.numJumps++;
- this.el.emit('jumpstart');
- }
- },
-
- onCollide: function onCollide() {
- if (this.numJumps > 0) this.el.emit('jumpend');
- this.numJumps = 0;
- }
-});
-
-},{}],30:[function(require,module,exports){
-'use strict';
-
-/* global CANNON */
-
-/**
- * Kinematic body.
- *
- * Managed dynamic body, which moves but is not affected (directly) by the
- * physics engine. This is not a true kinematic body, in the sense that we are
- * letting the physics engine _compute_ collisions against it and selectively
- * applying those collisions to the object. The physics engine does not decide
- * the position/velocity/rotation of the element.
- *
- * Used for the camera object, because full physics simulation would create
- * movement that feels unnatural to the player. Bipedal movement does not
- * translate nicely to rigid body physics.
- *
- * See: http://www.learn-cocos2d.com/2013/08/physics-engine-platformer-terrible-idea/
- * And: http://oxleygamedev.blogspot.com/2011/04/player-physics-part-2.html
- */
-
-var EPS = 0.000001;
-
-module.exports = AFRAME.registerComponent('kinematic-body', {
- dependencies: ['velocity'],
-
- /*******************************************************************
- * Schema
- */
-
- schema: {
- mass: { default: 5 },
- radius: { default: 1.3 },
- linearDamping: { default: 0.05 },
- enableSlopes: { default: true },
- enableJumps: { default: false }
- },
-
- /*******************************************************************
- * Lifecycle
- */
-
- init: function init() {
- this.system = this.el.sceneEl.systems.physics;
- this.system.addComponent(this);
-
- var el = this.el,
- data = this.data,
- position = new CANNON.Vec3().copy(el.object3D.getWorldPosition(new THREE.Vector3()));
-
- this.body = new CANNON.Body({
- material: this.system.getMaterial('staticMaterial'),
- position: position,
- mass: data.mass,
- linearDamping: data.linearDamping,
- fixedRotation: true
- });
- this.body.addShape(new CANNON.Sphere(data.radius), new CANNON.Vec3(0, data.radius, 0));
-
- this.body.el = this.el;
- this.el.body = this.body;
- this.system.addBody(this.body);
-
- if (el.hasAttribute('wasd-controls')) {
- console.warn('[kinematic-body] Not compatible with wasd-controls, use movement-controls.');
- }
- },
-
- remove: function remove() {
- this.system.removeBody(this.body);
- this.system.removeComponent(this);
- delete this.el.body;
- },
-
- /*******************************************************************
- * Update
- */
-
- /**
- * Checks CANNON.World for collisions and attempts to apply them to the
- * element automatically, in a player-friendly way.
- *
- * There's extra logic for horizontal surfaces here. The basic requirements:
- * (1) Only apply gravity when not in contact with _any_ horizontal surface.
- * (2) When moving, project the velocity against exactly one ground surface.
- * If in contact with two ground surfaces (e.g. ground + ramp), choose
- * the one that collides with current velocity, if any.
- */
- beforeStep: function beforeStep(t, dt) {
- if (!dt) return;
-
- var el = this.el;
- var data = this.data;
- var body = this.body;
-
- if (!data.enableJumps) body.velocity.set(0, 0, 0);
- body.position.copy(el.getAttribute('position'));
- },
-
- step: function () {
- var velocity = new THREE.Vector3(),
- normalizedVelocity = new THREE.Vector3(),
- currentSurfaceNormal = new THREE.Vector3(),
- groundNormal = new THREE.Vector3();
-
- return function (t, dt) {
- if (!dt) return;
-
- var body = this.body,
- data = this.data,
- didCollide = false,
- height = void 0,
- groundHeight = -Infinity,
- groundBody = void 0,
- contacts = this.system.getContacts();
-
- dt = Math.min(dt, this.system.data.maxInterval * 1000);
-
- groundNormal.set(0, 0, 0);
- velocity.copy(this.el.getAttribute('velocity'));
- body.velocity.copy(velocity);
-
- for (var i = 0, contact; contact = contacts[i]; i++) {
- // 1. Find any collisions involving this element. Get the contact
- // normal, and make sure it's oriented _out_ of the other object and
- // enabled (body.collisionReponse is true for both bodies)
- if (!contact.enabled) {
- continue;
- }
- if (body.id === contact.bi.id) {
- contact.ni.negate(currentSurfaceNormal);
- } else if (body.id === contact.bj.id) {
- currentSurfaceNormal.copy(contact.ni);
- } else {
- continue;
- }
-
- didCollide = body.velocity.dot(currentSurfaceNormal) < -EPS;
- if (didCollide && currentSurfaceNormal.y <= 0.5) {
- // 2. If current trajectory attempts to move _through_ another
- // object, project the velocity against the collision plane to
- // prevent passing through.
- velocity.projectOnPlane(currentSurfaceNormal);
- } else if (currentSurfaceNormal.y > 0.5) {
- // 3. If in contact with something roughly horizontal (+/- 45º) then
- // consider that the current ground. Only the highest qualifying
- // ground is retained.
- height = body.id === contact.bi.id ? Math.abs(contact.rj.y + contact.bj.position.y) : Math.abs(contact.ri.y + contact.bi.position.y);
- if (height > groundHeight) {
- groundHeight = height;
- groundNormal.copy(currentSurfaceNormal);
- groundBody = body.id === contact.bi.id ? contact.bj : contact.bi;
- }
- }
- }
-
- normalizedVelocity.copy(velocity).normalize();
- if (groundBody && (!data.enableJumps || normalizedVelocity.y < 0.5)) {
- if (!data.enableSlopes) {
- groundNormal.set(0, 1, 0);
- } else if (groundNormal.y < 1 - EPS) {
- groundNormal.copy(this.raycastToGround(groundBody, groundNormal));
- }
-
- // 4. Project trajectory onto the top-most ground object, unless
- // trajectory is > 45º.
- velocity.projectOnPlane(groundNormal);
- } else if (this.system.driver.world) {
- // 5. If not in contact with anything horizontal, apply world gravity.
- // TODO - Why is the 4x scalar necessary.
- // NOTE: Does not work if physics runs on a worker.
- velocity.add(this.system.driver.world.gravity.scale(dt * 4.0 / 1000));
- }
-
- body.velocity.copy(velocity);
- this.el.setAttribute('velocity', body.velocity);
- this.el.setAttribute('position', body.position);
- };
- }(),
-
- /**
- * When walking on complex surfaces (trimeshes, borders between two shapes),
- * the collision normals returned for the player sphere can be very
- * inconsistent. To address this, raycast straight down, find the collision
- * normal, and return whichever normal is more vertical.
- * @param {CANNON.Body} groundBody
- * @param {CANNON.Vec3} groundNormal
- * @return {CANNON.Vec3}
- */
- raycastToGround: function raycastToGround(groundBody, groundNormal) {
- var ray = void 0,
- hitNormal = void 0,
- vFrom = this.body.position,
- vTo = this.body.position.clone();
-
- ray = new CANNON.Ray(vFrom, vTo);
- ray._updateDirection(); // TODO - Report bug.
- ray.intersectBody(groundBody);
-
- if (!ray.hasHit) return groundNormal;
-
- // Compare ABS, in case we're projecting against the inside of the face.
- hitNormal = ray.result.hitNormalWorld;
- return Math.abs(hitNormal.y) > Math.abs(groundNormal.y) ? hitNormal : groundNormal;
- }
-});
-
-},{}],31:[function(require,module,exports){
-'use strict';
-
-/**
- * Apply this component to models that looks "blocky", to have Three.js compute
- * vertex normals on the fly for a "smoother" look.
- */
-
-module.exports = AFRAME.registerComponent('mesh-smooth', {
- init: function init() {
- this.el.addEventListener('model-loaded', function (e) {
- e.detail.model.traverse(function (node) {
- if (node.isMesh) node.geometry.computeVertexNormals();
- });
- });
- }
-});
-
-},{}],32:[function(require,module,exports){
-'use strict';
-
-/**
- * Recursively applies a MeshNormalMaterial to the entity, such that
- * face colors are determined by their orientation. Helpful for
- * debugging geometry
- */
-
-module.exports = AFRAME.registerComponent('normal-material', {
- init: function init() {
- this.material = new THREE.MeshNormalMaterial({ flatShading: true });
- this.applyMaterial = this.applyMaterial.bind(this);
- this.el.addEventListener('object3dset', this.applyMaterial);
- },
-
- remove: function remove() {
- this.el.removeEventListener('object3dset', this.applyMaterial);
- },
-
- applyMaterial: function applyMaterial() {
- var _this = this;
-
- this.el.object3D.traverse(function (node) {
- if (node.isMesh) node.material = _this.material;
- });
- }
-});
-
-},{}],33:[function(require,module,exports){
-'use strict';
-
-/**
- * Based on aframe/examples/showcase/tracked-controls.
- *
- * Implement bounding sphere collision detection for entities with a mesh.
- * Sets the specified state on the intersected entities.
- *
- * @property {string} objects - Selector of the entities to test for collision.
- * @property {string} state - State to set on collided entities.
- *
- */
-
-module.exports = AFRAME.registerComponent('sphere-collider', {
- schema: {
- objects: { default: '' },
- state: { default: 'collided' },
- radius: { default: 0.05 },
- watch: { default: true }
- },
-
- init: function init() {
- /** @type {MutationObserver} */
- this.observer = null;
- /** @type {Array} Elements to watch for collisions. */
- this.els = [];
- /** @type {Array} Elements currently in collision state. */
- this.collisions = [];
-
- this.handleHit = this.handleHit.bind(this);
- this.handleHitEnd = this.handleHitEnd.bind(this);
- },
-
- remove: function remove() {
- this.pause();
- },
-
- play: function play() {
- var sceneEl = this.el.sceneEl;
-
- if (this.data.watch) {
- this.observer = new MutationObserver(this.update.bind(this, null));
- this.observer.observe(sceneEl, { childList: true, subtree: true });
- }
- },
-
- pause: function pause() {
- if (this.observer) {
- this.observer.disconnect();
- this.observer = null;
- }
- },
-
- /**
- * Update list of entities to test for collision.
- */
- update: function update() {
- var data = this.data;
- var objectEls = void 0;
-
- // Push entities into list of els to intersect.
- if (data.objects) {
- objectEls = this.el.sceneEl.querySelectorAll(data.objects);
- } else {
- // If objects not defined, intersect with everything.
- objectEls = this.el.sceneEl.children;
- }
- // Convert from NodeList to Array
- this.els = Array.prototype.slice.call(objectEls);
- },
-
- tick: function () {
- var position = new THREE.Vector3(),
- meshPosition = new THREE.Vector3(),
- colliderScale = new THREE.Vector3(),
- size = new THREE.Vector3(),
- box = new THREE.Box3(),
- distanceMap = new Map();
- return function () {
- var el = this.el,
- data = this.data,
- mesh = el.getObject3D('mesh'),
- collisions = [];
- var colliderRadius = void 0;
-
- if (!mesh) {
- return;
- }
-
- distanceMap.clear();
- el.object3D.getWorldPosition(position);
- el.object3D.getWorldScale(colliderScale);
- colliderRadius = data.radius * scaleFactor(colliderScale);
- // Update collision list.
- this.els.forEach(intersect);
-
- // Emit events and add collision states, in order of distance.
- collisions.sort(function (a, b) {
- return distanceMap.get(a) > distanceMap.get(b) ? 1 : -1;
- }).forEach(this.handleHit);
-
- // Remove collision state from current element.
- if (collisions.length === 0) {
- el.emit('hit', { el: null });
- }
-
- // Remove collision state from other elements.
- this.collisions.filter(function (el) {
- return !distanceMap.has(el);
- }).forEach(this.handleHitEnd);
-
- // Store new collisions
- this.collisions = collisions;
-
- // Bounding sphere collision detection
- function intersect(el) {
- var radius = void 0,
- mesh = void 0,
- distance = void 0,
- extent = void 0;
-
- if (!el.isEntity) {
- return;
- }
-
- mesh = el.getObject3D('mesh');
-
- if (!mesh) {
- return;
- }
-
- box.setFromObject(mesh).getSize(size);
- extent = Math.max(size.x, size.y, size.z) / 2;
- radius = Math.sqrt(2 * extent * extent);
- box.getCenter(meshPosition);
-
- if (!radius) {
- return;
- }
-
- distance = position.distanceTo(meshPosition);
- if (distance < radius + colliderRadius) {
- collisions.push(el);
- distanceMap.set(el, distance);
- }
- }
- // use max of scale factors to maintain bounding sphere collision
- function scaleFactor(scaleVec) {
- return Math.max.apply(null, scaleVec.toArray());
- }
- };
- }(),
-
- handleHit: function handleHit(targetEl) {
- targetEl.emit('hit');
- targetEl.addState(this.data.state);
- this.el.emit('hit', { el: targetEl });
- },
- handleHitEnd: function handleHitEnd(targetEl) {
- targetEl.emit('hitend');
- targetEl.removeState(this.data.state);
- this.el.emit('hitend', { el: targetEl });
- }
-});
-
-},{}],34:[function(require,module,exports){
-'use strict';
-
-require('./nav-mesh');
-require('./nav-agent');
-require('./system');
-
-},{"./nav-agent":35,"./nav-mesh":36,"./system":37}],35:[function(require,module,exports){
-'use strict';
-
-module.exports = AFRAME.registerComponent('nav-agent', {
- schema: {
- destination: { type: 'vec3' },
- active: { default: false },
- speed: { default: 2 }
- },
- init: function init() {
- this.system = this.el.sceneEl.systems.nav;
- this.system.addAgent(this);
- this.group = null;
- this.path = [];
- this.raycaster = new THREE.Raycaster();
- },
- remove: function remove() {
- this.system.removeAgent(this);
- },
- update: function update() {
- this.path.length = 0;
- },
- updateNavLocation: function updateNavLocation() {
- this.group = null;
- this.path = [];
- },
- tick: function () {
- var vDest = new THREE.Vector3();
- var vDelta = new THREE.Vector3();
- var vNext = new THREE.Vector3();
-
- return function (t, dt) {
- var el = this.el;
- var data = this.data;
- var raycaster = this.raycaster;
- var speed = data.speed * dt / 1000;
-
- if (!data.active) return;
-
- // Use PatrolJS pathfinding system to get shortest path to target.
- if (!this.path.length) {
- var position = this.el.object3D.position;
- this.group = this.group || this.system.getGroup(position);
- this.path = this.system.getPath(position, vDest.copy(data.destination), this.group) || [];
- el.emit('navigation-start');
- }
-
- // If no path is found, exit.
- if (!this.path.length) {
- console.warn('[nav] Unable to find path to %o.', data.destination);
- this.el.setAttribute('nav-agent', { active: false });
- el.emit('navigation-end');
- return;
- }
-
- // Current segment is a vector from current position to next waypoint.
- var vCurrent = el.object3D.position;
- var vWaypoint = this.path[0];
- vDelta.subVectors(vWaypoint, vCurrent);
-
- var distance = vDelta.length();
- var gazeTarget = void 0;
-
- if (distance < speed) {
- // If <1 step from current waypoint, discard it and move toward next.
- this.path.shift();
-
- // After discarding the last waypoint, exit pathfinding.
- if (!this.path.length) {
- this.el.setAttribute('nav-agent', { active: false });
- el.emit('navigation-end');
- return;
- }
-
- vNext.copy(vCurrent);
- gazeTarget = this.path[0];
- } else {
- // If still far away from next waypoint, find next position for
- // the current frame.
- vNext.copy(vDelta.setLength(speed)).add(vCurrent);
- gazeTarget = vWaypoint;
- }
-
- // Look at the next waypoint.
- gazeTarget.y = vCurrent.y;
- el.object3D.lookAt(gazeTarget);
-
- // Raycast against the nav mesh, to keep the agent moving along the
- // ground, not traveling in a straight line from higher to lower waypoints.
- raycaster.ray.origin.copy(vNext);
- raycaster.ray.origin.y += 1.5;
- raycaster.ray.direction = { x: 0, y: -1, z: 0 };
- var intersections = raycaster.intersectObject(this.system.getNavMesh());
-
- if (!intersections.length) {
- // Raycasting failed. Step toward the waypoint and hope for the best.
- vCurrent.copy(vNext);
- } else {
- // Re-project next position onto nav mesh.
- vDelta.subVectors(intersections[0].point, vCurrent);
- vCurrent.add(vDelta.setLength(speed));
- }
- };
- }()
-});
-
-},{}],36:[function(require,module,exports){
-'use strict';
-
-/**
- * nav-mesh
- *
- * Waits for a mesh to be loaded on the current entity, then sets it as the
- * nav mesh in the pathfinding system.
- */
-
-module.exports = AFRAME.registerComponent('nav-mesh', {
- init: function init() {
- this.system = this.el.sceneEl.systems.nav;
- this.hasLoadedNavMesh = false;
- this.el.addEventListener('object3dset', this.loadNavMesh.bind(this));
- },
-
- play: function play() {
- if (!this.hasLoadedNavMesh) this.loadNavMesh();
- },
-
- loadNavMesh: function loadNavMesh() {
- var object = this.el.getObject3D('mesh');
- var scene = this.el.sceneEl.object3D;
-
- if (!object) return;
-
- var navMesh = void 0;
- object.traverse(function (node) {
- if (node.isMesh) navMesh = node;
- });
-
- if (!navMesh) return;
-
- var navMeshGeometry = navMesh.geometry.isBufferGeometry ? new THREE.Geometry().fromBufferGeometry(navMesh.geometry) : navMesh.geometry.clone();
-
- scene.updateMatrixWorld();
- navMeshGeometry.applyMatrix(navMesh.matrixWorld);
- this.system.setNavMeshGeometry(navMeshGeometry);
-
- this.hasLoadedNavMesh = true;
- }
-});
-
-},{}],37:[function(require,module,exports){
-'use strict';
-
-var _require = require('three-pathfinding'),
- Pathfinding = _require.Pathfinding;
-
-var pathfinder = new Pathfinding();
-var ZONE = 'level';
-
-/**
- * nav
- *
- * Pathfinding system, using PatrolJS.
- */
-module.exports = AFRAME.registerSystem('nav', {
- init: function init() {
- this.navMesh = null;
- this.agents = new Set();
- },
-
- /**
- * @param {THREE.Geometry} geometry
- */
- setNavMeshGeometry: function setNavMeshGeometry(geometry) {
- this.navMesh = new THREE.Mesh(geometry);
- pathfinder.setZoneData(ZONE, Pathfinding.createZone(geometry));
- Array.from(this.agents).forEach(function (agent) {
- return agent.updateNavLocation();
- });
- },
-
- /**
- * @return {THREE.Mesh}
- */
- getNavMesh: function getNavMesh() {
- return this.navMesh;
- },
-
- /**
- * @param {NavAgent} ctrl
- */
- addAgent: function addAgent(ctrl) {
- this.agents.add(ctrl);
- },
-
- /**
- * @param {NavAgent} ctrl
- */
- removeAgent: function removeAgent(ctrl) {
- this.agents.delete(ctrl);
- },
-
- /**
- * @param {THREE.Vector3} start
- * @param {THREE.Vector3} end
- * @param {number} groupID
- * @return {Array}
- */
- getPath: function getPath(start, end, groupID) {
- return this.navMesh ? pathfinder.findPath(start, end, ZONE, groupID) : null;
- },
-
- /**
- * @param {THREE.Vector3} position
- * @return {number}
- */
- getGroup: function getGroup(position) {
- return this.navMesh ? pathfinder.getGroup(ZONE, position) : null;
- },
-
- /**
- * @param {THREE.Vector3} position
- * @param {number} groupID
- * @return {Node}
- */
- getNode: function getNode(position, groupID) {
- return this.navMesh ? pathfinder.getClosestNode(position, ZONE, groupID, true) : null;
- },
-
- /**
- * @param {THREE.Vector3} start Starting position.
- * @param {THREE.Vector3} end Desired ending position.
- * @param {number} groupID
- * @param {Node} node
- * @param {THREE.Vector3} endTarget (Output) Adjusted step end position.
- * @return {Node} Current node, after step is taken.
- */
- clampStep: function clampStep(start, end, groupID, node, endTarget) {
- if (!this.navMesh) {
- endTarget.copy(end);
- return null;
- } else if (!node) {
- endTarget.copy(end);
- return this.getNode(end, groupID);
- }
- return pathfinder.clampStep(start, end, node, ZONE, groupID, endTarget);
- }
-});
-
-},{"three-pathfinding":11}],38:[function(require,module,exports){
-'use strict';
-
-/**
- * Flat grid.
- *
- * Defaults to 75x75.
- */
-
-module.exports = AFRAME.registerPrimitive('a-grid', {
- defaultComponents: {
- geometry: {
- primitive: 'plane',
- width: 75,
- height: 75
- },
- rotation: { x: -90, y: 0, z: 0 },
- material: {
- src: 'url(https://cdn.jsdelivr.net/gh/donmccurdy/aframe-extras@v1.16.3/assets/grid.png)',
- repeat: '75 75'
- }
- },
- mappings: {
- width: 'geometry.width',
- height: 'geometry.height',
- src: 'material.src'
- }
-});
-
-},{}],39:[function(require,module,exports){
-'use strict';
-
-var vg = require('../../lib/hex-grid.min.js');
-var defaultHexGrid = require('../../lib/default-hex-grid');
-
-/**
- * Hex grid.
- */
-module.exports.Primitive = AFRAME.registerPrimitive('a-hexgrid', {
- defaultComponents: {
- 'hexgrid': {}
- },
- mappings: {
- src: 'hexgrid.src'
- }
-});
-
-module.exports.Component = AFRAME.registerComponent('hexgrid', {
- dependencies: ['material'],
- schema: {
- src: { type: 'asset' }
- },
- init: function init() {
- var _this = this;
-
- var data = this.data;
- if (data.src) {
- fetch(data.src).then(function (response) {
- return response.json();
- }).then(function (json) {
- return _this.addMesh(json);
- });
- } else {
- this.addMesh(defaultHexGrid);
- }
- },
- addMesh: function addMesh(json) {
- var grid = new vg.HexGrid();
- grid.fromJSON(json);
- var board = new vg.Board(grid);
- board.generateTilemap();
- this.el.setObject3D('mesh', board.group);
- this.addMaterial();
- },
- addMaterial: function addMaterial() {
- var materialComponent = this.el.components.material;
- var material = (materialComponent || {}).material;
- if (!material) return;
- this.el.object3D.traverse(function (node) {
- if (node.isMesh) {
- node.material = material;
- }
- });
- },
- remove: function remove() {
- this.el.removeObject3D('mesh');
- }
-});
-
-},{"../../lib/default-hex-grid":7,"../../lib/hex-grid.min.js":9}],40:[function(require,module,exports){
-'use strict';
-
-/**
- * Flat-shaded ocean primitive.
- *
- * Based on a Codrops tutorial:
- * http://tympanus.net/codrops/2016/04/26/the-aviator-animating-basic-3d-scene-threejs/
- */
-
-module.exports.Primitive = AFRAME.registerPrimitive('a-ocean', {
- defaultComponents: {
- ocean: {},
- rotation: { x: -90, y: 0, z: 0 }
- },
- mappings: {
- width: 'ocean.width',
- depth: 'ocean.depth',
- density: 'ocean.density',
- amplitude: 'ocean.amplitude',
- amplitudeVariance: 'ocean.amplitudeVariance',
- speed: 'ocean.speed',
- speedVariance: 'ocean.speedVariance',
- color: 'ocean.color',
- opacity: 'ocean.opacity'
- }
-});
-
-module.exports.Component = AFRAME.registerComponent('ocean', {
- schema: {
- // Dimensions of the ocean area.
- width: { default: 10, min: 0 },
- depth: { default: 10, min: 0 },
-
- // Density of waves.
- density: { default: 10 },
-
- // Wave amplitude and variance.
- amplitude: { default: 0.1 },
- amplitudeVariance: { default: 0.3 },
-
- // Wave speed and variance.
- speed: { default: 1 },
- speedVariance: { default: 2 },
-
- // Material.
- color: { default: '#7AD2F7', type: 'color' },
- opacity: { default: 0.8 }
- },
-
- /**
- * Use play() instead of init(), because component mappings – unavailable as dependencies – are
- * not guaranteed to have parsed when this component is initialized.
- */
- play: function play() {
- var el = this.el,
- data = this.data;
- var material = el.components.material;
-
- var geometry = new THREE.PlaneGeometry(data.width, data.depth, data.density, data.density);
- geometry.mergeVertices();
- this.waves = [];
- for (var v, i = 0, l = geometry.vertices.length; i < l; i++) {
- v = geometry.vertices[i];
- this.waves.push({
- z: v.z,
- ang: Math.random() * Math.PI * 2,
- amp: data.amplitude + Math.random() * data.amplitudeVariance,
- speed: (data.speed + Math.random() * data.speedVariance) / 1000 // radians / frame
- });
- }
-
- if (!material) {
- material = {};
- material.material = new THREE.MeshPhongMaterial({
- color: data.color,
- transparent: data.opacity < 1,
- opacity: data.opacity,
- shading: THREE.FlatShading
- });
- }
-
- this.mesh = new THREE.Mesh(geometry, material.material);
- el.setObject3D('mesh', this.mesh);
- },
-
- remove: function remove() {
- this.el.removeObject3D('mesh');
- },
-
- tick: function tick(t, dt) {
- if (!dt) return;
-
- var verts = this.mesh.geometry.vertices;
- for (var v, vprops, i = 0; v = verts[i]; i++) {
- vprops = this.waves[i];
- v.z = vprops.z + Math.sin(vprops.ang) * vprops.amp;
- vprops.ang += vprops.speed * dt;
- }
- this.mesh.geometry.verticesNeedUpdate = true;
- }
-});
-
-},{}],41:[function(require,module,exports){
-'use strict';
-
-/**
- * Tube following a custom path.
- *
- * Usage:
- *
- * ```html
- *
- * ```
- */
-
-module.exports.Primitive = AFRAME.registerPrimitive('a-tube', {
- defaultComponents: {
- tube: {}
- },
- mappings: {
- path: 'tube.path',
- segments: 'tube.segments',
- radius: 'tube.radius',
- 'radial-segments': 'tube.radialSegments',
- closed: 'tube.closed'
- }
-});
-
-module.exports.Component = AFRAME.registerComponent('tube', {
- schema: {
- path: { default: [] },
- segments: { default: 64 },
- radius: { default: 1 },
- radialSegments: { default: 8 },
- closed: { default: false }
- },
-
- init: function init() {
- var el = this.el,
- data = this.data;
- var material = el.components.material;
-
- if (!data.path.length) {
- console.error('[a-tube] `path` property expected but not found.');
- return;
- }
-
- var curve = new THREE.CatmullRomCurve3(data.path.map(function (point) {
- point = point.split(' ');
- return new THREE.Vector3(Number(point[0]), Number(point[1]), Number(point[2]));
- }));
- var geometry = new THREE.TubeGeometry(curve, data.segments, data.radius, data.radialSegments, data.closed);
-
- if (!material) {
- material = {};
- material.material = new THREE.MeshPhongMaterial();
- }
-
- this.mesh = new THREE.Mesh(geometry, material.material);
- this.el.setObject3D('mesh', this.mesh);
- },
-
- update: function update(prevData) {
- if (!Object.keys(prevData).length) return;
-
- this.remove();
- this.init();
- },
-
- remove: function remove() {
- if (this.mesh) this.el.removeObject3D('mesh');
- }
-});
-
-},{}],42:[function(require,module,exports){
-'use strict';
-
-require('./a-grid');
-require('./a-hexgrid');
-require('./a-ocean');
-require('./a-tube');
-
-},{"./a-grid":38,"./a-hexgrid":39,"./a-ocean":40,"./a-tube":41}]},{},[1]);
diff --git a/js/aframe-physics-system.min.js b/js/aframe-physics-system.min.js
deleted file mode 100644
index 7598f03..0000000
--- a/js/aframe-physics-system.min.js
+++ /dev/null
@@ -1 +0,0 @@
-!function(){return function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){return o(e[i][1][r]||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;ii){var temp=j;j=i,i=temp}return i+"-"+j in this.matrix},_proto.set=function(bi,bj,value){var i=bi.id,j=bj.id;if(j>i){var temp=j;j=i,i=temp}value?this.matrix[i+"-"+j]=!0:delete this.matrix[i+"-"+j]},_proto.reset=function(){this.matrix={}},_proto.setNumObjects=function(n){},ObjectCollisionMatrix}(),Mat3=function(){function Mat3(elements){void 0===elements&&(elements=[0,0,0,0,0,0,0,0,0]),this.elements=elements}var _proto=Mat3.prototype;return _proto.identity=function(){var e=this.elements;e[0]=1,e[1]=0,e[2]=0,e[3]=0,e[4]=1,e[5]=0,e[6]=0,e[7]=0,e[8]=1},_proto.setZero=function(){var e=this.elements;e[0]=0,e[1]=0,e[2]=0,e[3]=0,e[4]=0,e[5]=0,e[6]=0,e[7]=0,e[8]=0},_proto.setTrace=function(vector){var e=this.elements;e[0]=vector.x,e[4]=vector.y,e[8]=vector.z},_proto.getTrace=function(target){void 0===target&&(target=new Vec3);var e=this.elements;target.x=e[0],target.y=e[4],target.z=e[8]},_proto.vmult=function(v,target){void 0===target&&(target=new Vec3);var e=this.elements,x=v.x,y=v.y,z=v.z;return target.x=e[0]*x+e[1]*y+e[2]*z,target.y=e[3]*x+e[4]*y+e[5]*z,target.z=e[6]*x+e[7]*y+e[8]*z,target},_proto.smult=function(s){for(var i=0;i0){var invN=1/n;this.x*=invN,this.y*=invN,this.z*=invN}else this.x=0,this.y=0,this.z=0;return n},_proto.unit=function(target){void 0===target&&(target=new Vec3);var x=this.x,y=this.y,z=this.z,ninv=Math.sqrt(x*x+y*y+z*z);return ninv>0?(ninv=1/ninv,target.x=x*ninv,target.y=y*ninv,target.z=z*ninv):(target.x=1,target.y=0,target.z=0),target},_proto.length=function(){var x=this.x,y=this.y,z=this.z;return Math.sqrt(x*x+y*y+z*z)},_proto.lengthSquared=function(){return this.dot(this)},_proto.distanceTo=function(p){var x=this.x,y=this.y,z=this.z,px=p.x,py=p.y,pz=p.z;return Math.sqrt((px-x)*(px-x)+(py-y)*(py-y)+(pz-z)*(pz-z))},_proto.distanceSquared=function(p){var x=this.x,y=this.y,z=this.z,px=p.x,py=p.y,pz=p.z;return(px-x)*(px-x)+(py-y)*(py-y)+(pz-z)*(pz-z)},_proto.scale=function(scalar,target){void 0===target&&(target=new Vec3);var x=this.x,y=this.y,z=this.z;return target.x=scalar*x,target.y=scalar*y,target.z=scalar*z,target},_proto.vmul=function(vector,target){return void 0===target&&(target=new Vec3),target.x=vector.x*this.x,target.y=vector.y*this.y,target.z=vector.z*this.z,target},_proto.addScaledVector=function(scalar,vector,target){return void 0===target&&(target=new Vec3),target.x=this.x+scalar*vector.x,target.y=this.y+scalar*vector.y,target.z=this.z+scalar*vector.z,target},_proto.dot=function(vector){return this.x*vector.x+this.y*vector.y+this.z*vector.z},_proto.isZero=function(){return 0===this.x&&0===this.y&&0===this.z},_proto.negate=function(target){return void 0===target&&(target=new Vec3),target.x=-this.x,target.y=-this.y,target.z=-this.z,target},_proto.tangents=function(t1,t2){var norm=this.length();if(norm>0){var n=Vec3_tangents_n,inorm=1/norm;n.set(this.x*inorm,this.y*inorm,this.z*inorm);var randVec=Vec3_tangents_randVec;Math.abs(n.x)<.9?(randVec.set(1,0,0),n.cross(randVec,t1)):(randVec.set(0,1,0),n.cross(randVec,t1)),n.cross(t1,t2)}else t1.set(1,0,0),t2.set(0,1,0)},_proto.toString=function(){return this.x+","+this.y+","+this.z},_proto.toArray=function(){return[this.x,this.y,this.z]},_proto.copy=function(vector){return this.x=vector.x,this.y=vector.y,this.z=vector.z,this},_proto.lerp=function(vector,t,target){var x=this.x,y=this.y,z=this.z;target.x=x+(vector.x-x)*t,target.y=y+(vector.y-y)*t,target.z=z+(vector.z-z)*t},_proto.almostEquals=function(vector,precision){return void 0===precision&&(precision=1e-6),!(Math.abs(this.x-vector.x)>precision||Math.abs(this.y-vector.y)>precision||Math.abs(this.z-vector.z)>precision)},_proto.almostZero=function(precision){return void 0===precision&&(precision=1e-6),!(Math.abs(this.x)>precision||Math.abs(this.y)>precision||Math.abs(this.z)>precision)},_proto.isAntiparallelTo=function(vector,precision){return this.negate(antip_neg),antip_neg.almostEquals(vector,precision)},_proto.clone=function(){return new Vec3(this.x,this.y,this.z)},Vec3}();Vec3.ZERO=new Vec3(0,0,0),Vec3.UNIT_X=new Vec3(1,0,0),Vec3.UNIT_Y=new Vec3(0,1,0),Vec3.UNIT_Z=new Vec3(0,0,1);var Vec3_tangents_n=new Vec3,Vec3_tangents_randVec=new Vec3,antip_neg=new Vec3,AABB=function(){function AABB(options){void 0===options&&(options={}),this.lowerBound=new Vec3,this.upperBound=new Vec3,options.lowerBound&&this.lowerBound.copy(options.lowerBound),options.upperBound&&this.upperBound.copy(options.upperBound)}var _proto=AABB.prototype;return _proto.setFromPoints=function(points,position,quaternion,skinSize){var l=this.lowerBound,u=this.upperBound,q=quaternion;l.copy(points[0]),q&&q.vmult(l,l),u.copy(l);for(var i=1;iu.x&&(u.x=p.x),p.xu.y&&(u.y=p.y),p.yu.z&&(u.z=p.z),p.z=u2.x&&l1.y<=l2.y&&u1.y>=u2.y&&l1.z<=l2.z&&u1.z>=u2.z},_proto.getCorners=function(a,b,c,d,e,f,g,h){var l=this.lowerBound,u=this.upperBound;a.copy(l),b.set(u.x,l.y,l.z),c.set(u.x,u.y,l.z),d.set(l.x,u.y,u.z),e.set(u.x,l.y,u.z),f.set(l.x,u.y,l.z),g.set(l.x,l.y,u.z),h.copy(u)},_proto.toLocalFrame=function(frame,target){var corners=transformIntoFrame_corners,a=corners[0],b=corners[1],c=corners[2],d=corners[3],e=corners[4],f=corners[5],g=corners[6],h=corners[7];this.getCorners(a,b,c,d,e,f,g,h);for(var i=0;8!==i;i++){var corner=corners[i];frame.pointToLocal(corner,corner)}return target.setFromPoints(corners)},_proto.toWorldFrame=function(frame,target){var corners=transformIntoFrame_corners,a=corners[0],b=corners[1],c=corners[2],d=corners[3],e=corners[4],f=corners[5],g=corners[6],h=corners[7];this.getCorners(a,b,c,d,e,f,g,h);for(var i=0;8!==i;i++){var corner=corners[i];frame.pointToWorld(corner,corner)}return target.setFromPoints(corners)},_proto.overlapsRay=function(ray){var direction=ray.direction,from=ray.from,dirFracX=1/direction.x,dirFracY=1/direction.y,dirFracZ=1/direction.z,t1=(this.lowerBound.x-from.x)*dirFracX,t2=(this.upperBound.x-from.x)*dirFracX,t3=(this.lowerBound.y-from.y)*dirFracY,t4=(this.upperBound.y-from.y)*dirFracY,t5=(this.lowerBound.z-from.z)*dirFracZ,t6=(this.upperBound.z-from.z)*dirFracZ,tmin=Math.max(Math.max(Math.min(t1,t2),Math.min(t3,t4)),Math.min(t5,t6)),tmax=Math.min(Math.min(Math.max(t1,t2),Math.max(t3,t4)),Math.max(t5,t6));return!(tmax<0)&&!(tmin>tmax)},AABB}(),tmp=new Vec3,transformIntoFrame_corners=[new Vec3,new Vec3,new Vec3,new Vec3,new Vec3,new Vec3,new Vec3,new Vec3],ArrayCollisionMatrix=function(){function ArrayCollisionMatrix(){this.matrix=[]}var _proto=ArrayCollisionMatrix.prototype;return _proto.get=function(bi,bj){var i=bi.index,j=bj.index;if(j>i){var temp=j;j=i,i=temp}return this.matrix[(i*(i+1)>>1)+j-1]},_proto.set=function(bi,bj,value){var i=bi.index,j=bj.index;if(j>i){var temp=j;j=i,i=temp}this.matrix[(i*(i+1)>>1)+j-1]=value?1:0},_proto.reset=function(){for(var i=0,l=this.matrix.length;i!==l;i++)this.matrix[i]=0},_proto.setNumObjects=function(n){this.matrix.length=n*(n-1)>>1},ArrayCollisionMatrix}();function _inheritsLoose(subClass,superClass){subClass.prototype=Object.create(superClass.prototype),subClass.prototype.constructor=subClass,subClass.__proto__=superClass}function _assertThisInitialized(self){if(void 0===self)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return self}var EventTarget=function(){function EventTarget(){}var _proto=EventTarget.prototype;return _proto.addEventListener=function(type,listener){void 0===this._listeners&&(this._listeners={});var listeners=this._listeners;return void 0===listeners[type]&&(listeners[type]=[]),listeners[type].includes(listener)||listeners[type].push(listener),this},_proto.hasEventListener=function(type,listener){if(void 0===this._listeners)return!1;var listeners=this._listeners;return!(void 0===listeners[type]||!listeners[type].includes(listener))},_proto.hasAnyEventListener=function(type){return void 0!==this._listeners&&void 0!==this._listeners[type]},_proto.removeEventListener=function(type,listener){if(void 0===this._listeners)return this;var listeners=this._listeners;if(void 0===listeners[type])return this;var index=listeners[type].indexOf(listener);return-1!==index&&listeners[type].splice(index,1),this},_proto.dispatchEvent=function(event){if(void 0===this._listeners)return this;var listenerArray=this._listeners[event.type];if(void 0!==listenerArray){event.target=this;for(var i=0,l=listenerArray.length;i.499&&(heading=2*Math.atan2(x,w),attitude=Math.PI/2,bank=0),test<-.499&&(heading=-2*Math.atan2(x,w),attitude=-Math.PI/2,bank=0),void 0===heading){var sqx=x*x,sqy=y*y,sqz=z*z;heading=Math.atan2(2*y*w-2*x*z,1-2*sqy-2*sqz),attitude=Math.asin(2*test),bank=Math.atan2(2*x*w-2*y*z,1-2*sqx-2*sqz)}break;default:throw new Error("Euler order "+order+" not supported yet.")}target.y=heading,target.z=attitude,target.x=bank},_proto.setFromEuler=function(x,y,z,order){void 0===order&&(order="XYZ");var c1=Math.cos(x/2),c2=Math.cos(y/2),c3=Math.cos(z/2),s1=Math.sin(x/2),s2=Math.sin(y/2),s3=Math.sin(z/2);return"XYZ"===order?(this.x=s1*c2*c3+c1*s2*s3,this.y=c1*s2*c3-s1*c2*s3,this.z=c1*c2*s3+s1*s2*c3,this.w=c1*c2*c3-s1*s2*s3):"YXZ"===order?(this.x=s1*c2*c3+c1*s2*s3,this.y=c1*s2*c3-s1*c2*s3,this.z=c1*c2*s3-s1*s2*c3,this.w=c1*c2*c3+s1*s2*s3):"ZXY"===order?(this.x=s1*c2*c3-c1*s2*s3,this.y=c1*s2*c3+s1*c2*s3,this.z=c1*c2*s3+s1*s2*c3,this.w=c1*c2*c3-s1*s2*s3):"ZYX"===order?(this.x=s1*c2*c3-c1*s2*s3,this.y=c1*s2*c3+s1*c2*s3,this.z=c1*c2*s3-s1*s2*c3,this.w=c1*c2*c3+s1*s2*s3):"YZX"===order?(this.x=s1*c2*c3+c1*s2*s3,this.y=c1*s2*c3+s1*c2*s3,this.z=c1*c2*s3-s1*s2*c3,this.w=c1*c2*c3-s1*s2*s3):"XZY"===order&&(this.x=s1*c2*c3-c1*s2*s3,this.y=c1*s2*c3-s1*c2*s3,this.z=c1*c2*s3+s1*s2*c3,this.w=c1*c2*c3+s1*s2*s3),this},_proto.clone=function(){return new Quaternion(this.x,this.y,this.z,this.w)},_proto.slerp=function(toQuat,t,target){void 0===target&&(target=new Quaternion);var omega,cosom,sinom,scale0,scale1,ax=this.x,ay=this.y,az=this.z,aw=this.w,bx=toQuat.x,by=toQuat.y,bz=toQuat.z,bw=toQuat.w;return(cosom=ax*bx+ay*by+az*bz+aw*bw)<0&&(cosom=-cosom,bx=-bx,by=-by,bz=-bz,bw=-bw),1-cosom>1e-6?(omega=Math.acos(cosom),sinom=Math.sin(omega),scale0=Math.sin((1-t)*omega)/sinom,scale1=Math.sin(t*omega)/sinom):(scale0=1-t,scale1=t),target.x=scale0*ax+scale1*bx,target.y=scale0*ay+scale1*by,target.z=scale0*az+scale1*bz,target.w=scale0*aw+scale1*bw,target},_proto.integrate=function(angularVelocity,dt,angularFactor,target){void 0===target&&(target=new Quaternion);var ax=angularVelocity.x*angularFactor.x,ay=angularVelocity.y*angularFactor.y,az=angularVelocity.z*angularFactor.z,bx=this.x,by=this.y,bz=this.z,bw=this.w,half_dt=.5*dt;return target.x+=half_dt*(ax*bw+ay*bz-az*by),target.y+=half_dt*(ay*bw+az*bx-ax*bz),target.z+=half_dt*(az*bw+ax*by-ay*bx),target.w+=half_dt*(-ax*bx-ay*by-az*bz),target},Quaternion}(),sfv_t1=new Vec3,sfv_t2=new Vec3,SHAPE_TYPES={SPHERE:1,PLANE:2,BOX:4,COMPOUND:8,CONVEXPOLYHEDRON:16,HEIGHTFIELD:32,PARTICLE:64,CYLINDER:128,TRIMESH:256},Shape=function(){function Shape(options){void 0===options&&(options={}),this.id=Shape.idCounter++,this.type=options.type||0,this.boundingSphereRadius=0,this.collisionResponse=!options.collisionResponse||options.collisionResponse,this.collisionFilterGroup=void 0!==options.collisionFilterGroup?options.collisionFilterGroup:1,this.collisionFilterMask=void 0!==options.collisionFilterMask?options.collisionFilterMask:-1,this.material=options.material?options.material:null,this.body=null}var _proto=Shape.prototype;return _proto.updateBoundingSphereRadius=function(){throw"computeBoundingSphereRadius() not implemented for shape type "+this.type},_proto.volume=function(){throw"volume() not implemented for shape type "+this.type},_proto.calculateLocalInertia=function(mass,target){throw"calculateLocalInertia() not implemented for shape type "+this.type},_proto.calculateWorldAABB=function(pos,quat,min,max){throw"calculateWorldAABB() not implemented for shape type "+this.type},Shape}();Shape.idCounter=0,Shape.types=SHAPE_TYPES;var Transform=function(){function Transform(options){void 0===options&&(options={}),this.position=new Vec3,this.quaternion=new Quaternion,options.position&&this.position.copy(options.position),options.quaternion&&this.quaternion.copy(options.quaternion)}var _proto=Transform.prototype;return _proto.pointToLocal=function(worldPoint,result){return Transform.pointToLocalFrame(this.position,this.quaternion,worldPoint,result)},_proto.pointToWorld=function(localPoint,result){return Transform.pointToWorldFrame(this.position,this.quaternion,localPoint,result)},_proto.vectorToWorldFrame=function(localVector,result){return void 0===result&&(result=new Vec3),this.quaternion.vmult(localVector,result),result},Transform.pointToLocalFrame=function(position,quaternion,worldPoint,result){return void 0===result&&(result=new Vec3),worldPoint.vsub(position,result),quaternion.conjugate(tmpQuat),tmpQuat.vmult(result,result),result},Transform.pointToWorldFrame=function(position,quaternion,localPoint,result){return void 0===result&&(result=new Vec3),quaternion.vmult(localPoint,result),result.vadd(position,result),result},Transform.vectorToWorldFrame=function(quaternion,localVector,result){return void 0===result&&(result=new Vec3),quaternion.vmult(localVector,result),result},Transform.vectorToLocalFrame=function(position,quaternion,worldVector,result){return void 0===result&&(result=new Vec3),quaternion.w*=-1,quaternion.vmult(worldVector,result),quaternion.w*=-1,result},Transform}(),tmpQuat=new Quaternion,ConvexPolyhedron=function(_Shape){function ConvexPolyhedron(props){var _this;void 0===props&&(props={});var _props=props,_props$vertices=_props.vertices,vertices=void 0===_props$vertices?[]:_props$vertices,_props$faces=_props.faces,faces=void 0===_props$faces?[]:_props$faces,_props$normals=_props.normals,normals=void 0===_props$normals?[]:_props$normals,axes=_props.axes,boundingSphereRadius=_props.boundingSphereRadius;return(_this=_Shape.call(this,{type:Shape.types.CONVEXPOLYHEDRON})||this).vertices=vertices,_this.faces=faces,_this.faceNormals=normals,0===_this.faceNormals.length&&_this.computeNormals(),boundingSphereRadius?_this.boundingSphereRadius=boundingSphereRadius:_this.updateBoundingSphereRadius(),_this.worldVertices=[],_this.worldVerticesNeedsUpdate=!0,_this.worldFaceNormals=[],_this.worldFaceNormalsNeedsUpdate=!0,_this.uniqueAxes=axes?axes.slice():null,_this.uniqueEdges=[],_this.computeEdges(),_this}_inheritsLoose(ConvexPolyhedron,_Shape);var _proto=ConvexPolyhedron.prototype;return _proto.computeEdges=function(){var faces=this.faces,vertices=this.vertices,edges=this.uniqueEdges;edges.length=0;for(var edge=new Vec3,i=0;i!==faces.length;i++)for(var face=faces[i],numVertices=face.length,j=0;j!==numVertices;j++){var k=(j+1)%numVertices;vertices[face[j]].vsub(vertices[face[k]],edge),edge.normalize();for(var found=!1,p=0;p!==edges.length;p++)if(edges[p].almostEquals(edge)||edges[p].almostEquals(edge)){found=!0;break}found||edges.push(edge.clone())}},_proto.computeNormals=function(){this.faceNormals.length=this.faces.length;for(var i=0;idmax&&(dmax=d,closestFaceB=face)}for(var worldVertsB1=[],i=0;i=0&&this.clipFaceAgainstHull(separatingNormal,posA,quatA,worldVertsB1,minDist,maxDist,result)},_proto.findSeparatingAxis=function(hullB,posA,quatA,posB,quatB,target,faceListA,faceListB){var faceANormalWS3=new Vec3,Worldnormal1=new Vec3,deltaC=new Vec3,worldEdge0=new Vec3,worldEdge1=new Vec3,Cross=new Vec3,dmin=Number.MAX_VALUE;if(this.uniqueAxes)for(var _i=0;_i!==this.uniqueAxes.length;_i++){quatA.vmult(this.uniqueAxes[_i],faceANormalWS3);var _d=this.testSepAxis(faceANormalWS3,hullB,posA,quatA,posB,quatB);if(!1===_d)return!1;_d0&&target.negate(target),!0},_proto.testSepAxis=function(axis,hullB,posA,quatA,posB,quatB){ConvexPolyhedron.project(this,axis,posA,quatA,maxminA),ConvexPolyhedron.project(hullB,axis,posB,quatB,maxminB);var maxA=maxminA[0],minA=maxminA[1],maxB=maxminB[0],minB=maxminB[1];if(maxA0?1/mass:0,_this.material=options.material||null,_this.linearDamping="number"==typeof options.linearDamping?options.linearDamping:.01,_this.type=mass<=0?Body.STATIC:Body.DYNAMIC,typeof options.type==typeof Body.STATIC&&(_this.type=options.type),_this.allowSleep=void 0===options.allowSleep||options.allowSleep,_this.sleepState=0,_this.sleepSpeedLimit=void 0!==options.sleepSpeedLimit?options.sleepSpeedLimit:.1,_this.sleepTimeLimit=void 0!==options.sleepTimeLimit?options.sleepTimeLimit:1,_this.timeLastSleepy=0,_this.wakeUpAfterNarrowphase=!1,_this.torque=new Vec3,_this.quaternion=new Quaternion,_this.initQuaternion=new Quaternion,_this.previousQuaternion=new Quaternion,_this.interpolatedQuaternion=new Quaternion,options.quaternion&&(_this.quaternion.copy(options.quaternion),_this.initQuaternion.copy(options.quaternion),_this.previousQuaternion.copy(options.quaternion),_this.interpolatedQuaternion.copy(options.quaternion)),_this.angularVelocity=new Vec3,options.angularVelocity&&_this.angularVelocity.copy(options.angularVelocity),_this.initAngularVelocity=new Vec3,_this.shapes=[],_this.shapeOffsets=[],_this.shapeOrientations=[],_this.inertia=new Vec3,_this.invInertia=new Vec3,_this.invInertiaWorld=new Mat3,_this.invMassSolve=0,_this.invInertiaSolve=new Vec3,_this.invInertiaWorldSolve=new Mat3,_this.fixedRotation=void 0!==options.fixedRotation&&options.fixedRotation,_this.angularDamping=void 0!==options.angularDamping?options.angularDamping:.01,_this.linearFactor=new Vec3(1,1,1),options.linearFactor&&_this.linearFactor.copy(options.linearFactor),_this.angularFactor=new Vec3(1,1,1),options.angularFactor&&_this.angularFactor.copy(options.angularFactor),_this.aabb=new AABB,_this.aabbNeedsUpdate=!0,_this.boundingRadius=0,_this.wlambda=new Vec3,options.shape&&_this.addShape(options.shape),_this.updateMassProperties(),_this}_inheritsLoose(Body,_EventTarget);var _proto=Body.prototype;return _proto.wakeUp=function(){var prevState=this.sleepState;this.sleepState=0,this.wakeUpAfterNarrowphase=!1,prevState===Body.SLEEPING&&this.dispatchEvent(Body.wakeupEvent)},_proto.sleep=function(){this.sleepState=Body.SLEEPING,this.velocity.set(0,0,0),this.angularVelocity.set(0,0,0),this.wakeUpAfterNarrowphase=!1},_proto.sleepTick=function(time){if(this.allowSleep){var sleepState=this.sleepState,speedSquared=this.velocity.lengthSquared()+this.angularVelocity.lengthSquared(),speedLimitSquared=Math.pow(this.sleepSpeedLimit,2);sleepState===Body.AWAKE&&speedSquaredspeedLimitSquared?this.wakeUp():sleepState===Body.SLEEPY&&time-this.timeLastSleepy>this.sleepTimeLimit&&(this.sleep(),this.dispatchEvent(Body.sleepEvent))}},_proto.updateSolveMassProperties=function(){this.sleepState===Body.SLEEPING||this.type===Body.KINEMATIC?(this.invMassSolve=0,this.invInertiaSolve.setZero(),this.invInertiaWorldSolve.setZero()):(this.invMassSolve=this.invMass,this.invInertiaSolve.copy(this.invInertia),this.invInertiaWorldSolve.copy(this.invInertiaWorld))},_proto.pointToLocalFrame=function(worldPoint,result){return void 0===result&&(result=new Vec3),worldPoint.vsub(this.position,result),this.quaternion.conjugate().vmult(result,result),result},_proto.vectorToLocalFrame=function(worldVector,result){return void 0===result&&(result=new Vec3),this.quaternion.conjugate().vmult(worldVector,result),result},_proto.pointToWorldFrame=function(localPoint,result){return void 0===result&&(result=new Vec3),this.quaternion.vmult(localPoint,result),result.vadd(this.position,result),result},_proto.vectorToWorldFrame=function(localVector,result){return void 0===result&&(result=new Vec3),this.quaternion.vmult(localVector,result),result},_proto.addShape=function(shape,_offset,_orientation){var offset=new Vec3,orientation=new Quaternion;return _offset&&offset.copy(_offset),_orientation&&orientation.copy(_orientation),this.shapes.push(shape),this.shapeOffsets.push(offset),this.shapeOrientations.push(orientation),this.updateMassProperties(),this.updateBoundingRadius(),this.aabbNeedsUpdate=!0,shape.body=this,this},_proto.updateBoundingRadius=function(){for(var shapes=this.shapes,shapeOffsets=this.shapeOffsets,N=shapes.length,radius=0,i=0;i!==N;i++){var shape=shapes[i];shape.updateBoundingSphereRadius();var offset=shapeOffsets[i].length(),r=shape.boundingSphereRadius;offset+r>radius&&(radius=offset+r)}this.boundingRadius=radius},_proto.computeAABB=function(){for(var shapes=this.shapes,shapeOffsets=this.shapeOffsets,shapeOrientations=this.shapeOrientations,N=shapes.length,offset=tmpVec,orientation=tmpQuat$1,bodyQuat=this.quaternion,aabb=this.aabb,shapeAABB=computeAABB_shapeAABB,i=0;i!==N;i++){var shape=shapes[i];bodyQuat.vmult(shapeOffsets[i],offset),offset.vadd(this.position,offset),bodyQuat.mult(shapeOrientations[i],orientation),shape.calculateWorldAABB(offset,orientation,shapeAABB.lowerBound,shapeAABB.upperBound),0===i?aabb.copy(shapeAABB):aabb.extend(shapeAABB)}this.aabbNeedsUpdate=!1},_proto.updateInertiaWorld=function(force){var I=this.invInertia;if(I.x!==I.y||I.y!==I.z||force){var m1=uiw_m1,m2=uiw_m2;m1.setRotationFromQuaternion(this.quaternion),m1.transpose(m2),m1.scale(I,m1),m1.mmult(m2,this.invInertiaWorld)}else;},_proto.applyForce=function(force,relativePoint){if(this.type===Body.DYNAMIC){var rotForce=Body_applyForce_rotForce;relativePoint.cross(force,rotForce),this.force.vadd(force,this.force),this.torque.vadd(rotForce,this.torque)}},_proto.applyLocalForce=function(localForce,localPoint){if(this.type===Body.DYNAMIC){var worldForce=Body_applyLocalForce_worldForce,relativePointWorld=Body_applyLocalForce_relativePointWorld;this.vectorToWorldFrame(localForce,worldForce),this.vectorToWorldFrame(localPoint,relativePointWorld),this.applyForce(worldForce,relativePointWorld)}},_proto.applyImpulse=function(impulse,relativePoint){if(this.type===Body.DYNAMIC){var r=relativePoint,velo=Body_applyImpulse_velo;velo.copy(impulse),velo.scale(this.invMass,velo),this.velocity.vadd(velo,this.velocity);var rotVelo=Body_applyImpulse_rotVelo;r.cross(impulse,rotVelo),this.invInertiaWorld.vmult(rotVelo,rotVelo),this.angularVelocity.vadd(rotVelo,this.angularVelocity)}},_proto.applyLocalImpulse=function(localImpulse,localPoint){if(this.type===Body.DYNAMIC){var worldImpulse=Body_applyLocalImpulse_worldImpulse,relativePointWorld=Body_applyLocalImpulse_relativePoint;this.vectorToWorldFrame(localImpulse,worldImpulse),this.vectorToWorldFrame(localPoint,relativePointWorld),this.applyImpulse(worldImpulse,relativePointWorld)}},_proto.updateMassProperties=function(){var halfExtents=Body_updateMassProperties_halfExtents;this.invMass=this.mass>0?1/this.mass:0;var I=this.inertia,fixed=this.fixedRotation;this.computeAABB(),halfExtents.set((this.aabb.upperBound.x-this.aabb.lowerBound.x)/2,(this.aabb.upperBound.y-this.aabb.lowerBound.y)/2,(this.aabb.upperBound.z-this.aabb.lowerBound.z)/2),Box.calculateInertia(halfExtents,this.mass,I),this.invInertia.set(I.x>0&&!fixed?1/I.x:0,I.y>0&&!fixed?1/I.y:0,I.z>0&&!fixed?1/I.z:0),this.updateInertiaWorld(!0)},_proto.getVelocityAtWorldPoint=function(worldPoint,result){var r=new Vec3;return worldPoint.vsub(this.position,r),this.angularVelocity.cross(r,result),this.velocity.vadd(result,result),result},_proto.integrate=function(dt,quatNormalize,quatNormalizeFast){if(this.previousPosition.copy(this.position),this.previousQuaternion.copy(this.quaternion),(this.type===Body.DYNAMIC||this.type===Body.KINEMATIC)&&this.sleepState!==Body.SLEEPING){var velo=this.velocity,angularVelo=this.angularVelocity,pos=this.position,force=this.force,torque=this.torque,quat=this.quaternion,invMass=this.invMass,invInertia=this.invInertiaWorld,linearFactor=this.linearFactor,iMdt=invMass*dt;velo.x+=force.x*iMdt*linearFactor.x,velo.y+=force.y*iMdt*linearFactor.y,velo.z+=force.z*iMdt*linearFactor.z;var e=invInertia.elements,angularFactor=this.angularFactor,tx=torque.x*angularFactor.x,ty=torque.y*angularFactor.y,tz=torque.z*angularFactor.z;angularVelo.x+=dt*(e[0]*tx+e[1]*ty+e[2]*tz),angularVelo.y+=dt*(e[3]*tx+e[4]*ty+e[5]*tz),angularVelo.z+=dt*(e[6]*tx+e[7]*ty+e[8]*tz),pos.x+=velo.x*dt,pos.y+=velo.y*dt,pos.z+=velo.z*dt,quat.integrate(this.angularVelocity,dt,this.angularFactor,quat),quatNormalize&&(quatNormalizeFast?quat.normalizeFast():quat.normalize()),this.aabbNeedsUpdate=!0,this.updateInertiaWorld()}},Body}(EventTarget);Body.COLLIDE_EVENT_NAME="collide",Body.DYNAMIC=1,Body.STATIC=2,Body.KINEMATIC=4,Body.AWAKE=BODY_SLEEP_STATES.AWAKE,Body.SLEEPY=BODY_SLEEP_STATES.SLEEPY,Body.SLEEPING=BODY_SLEEP_STATES.SLEEPING,Body.idCounter=0,Body.wakeupEvent={type:"wakeup"},Body.sleepyEvent={type:"sleepy"},Body.sleepEvent={type:"sleep"};var tmpVec=new Vec3,tmpQuat$1=new Quaternion,computeAABB_shapeAABB=new AABB,uiw_m1=new Mat3,uiw_m2=new Mat3,Body_applyForce_rotForce=(new Mat3,new Vec3),Body_applyLocalForce_worldForce=new Vec3,Body_applyLocalForce_relativePointWorld=new Vec3,Body_applyImpulse_velo=new Vec3,Body_applyImpulse_rotVelo=new Vec3,Body_applyLocalImpulse_worldImpulse=new Vec3,Body_applyLocalImpulse_relativePoint=new Vec3,Body_updateMassProperties_halfExtents=new Vec3,Broadphase=function(){function Broadphase(){this.world=null,this.useBoundingBoxes=!1,this.dirty=!0}var _proto=Broadphase.prototype;return _proto.collisionPairs=function(world,p1,p2){throw new Error("collisionPairs not implemented for this BroadPhase class!")},_proto.needBroadphaseCollision=function(bodyA,bodyB){return 0!=(bodyA.collisionFilterGroup&bodyB.collisionFilterMask)&&0!=(bodyB.collisionFilterGroup&bodyA.collisionFilterMask)&&(0==(bodyA.type&Body.STATIC)&&bodyA.sleepState!==Body.SLEEPING||0==(bodyB.type&Body.STATIC)&&bodyB.sleepState!==Body.SLEEPING)},_proto.intersectionTest=function(bodyA,bodyB,pairs1,pairs2){this.useBoundingBoxes?this.doBoundingBoxBroadphase(bodyA,bodyB,pairs1,pairs2):this.doBoundingSphereBroadphase(bodyA,bodyB,pairs1,pairs2)},_proto.doBoundingSphereBroadphase=function(bodyA,bodyB,pairs1,pairs2){var r=Broadphase_collisionPairs_r;bodyB.position.vsub(bodyA.position,r);var boundingRadiusSum2=Math.pow(bodyA.boundingRadius+bodyB.boundingRadius,2);r.lengthSquared()dist.lengthSquared()};var GridBroadphase=function(_Broadphase){function GridBroadphase(aabbMin,aabbMax,nx,ny,nz){var _this;void 0===aabbMin&&(aabbMin=new Vec3(100,100,100)),void 0===aabbMax&&(aabbMax=new Vec3(-100,-100,-100)),void 0===nx&&(nx=10),void 0===ny&&(ny=10),void 0===nz&&(nz=10),(_this=_Broadphase.call(this)||this).nx=nx,_this.ny=ny,_this.nz=nz,_this.aabbMin=aabbMin,_this.aabbMax=aabbMax;var nbins=_this.nx*_this.ny*_this.nz;if(nbins<=0)throw"GridBroadphase: Each dimension's n must be >0";_this.bins=[],_this.binLengths=[],_this.bins.length=nbins,_this.binLengths.length=nbins;for(var i=0;i=nx&&(xoff0=nx-1),yoff0<0?yoff0=0:yoff0>=ny&&(yoff0=ny-1),zoff0<0?zoff0=0:zoff0>=nz&&(zoff0=nz-1),xoff1<0?xoff1=0:xoff1>=nx&&(xoff1=nx-1),yoff1<0?yoff1=0:yoff1>=ny&&(yoff1=ny-1),zoff1<0?zoff1=0:zoff1>=nz&&(zoff1=nz-1),yoff0*=ystep,zoff0*=zstep,xoff1*=xstep,yoff1*=ystep,zoff1*=zstep;for(var xoff=xoff0*=xstep;xoff<=xoff1;xoff+=xstep)for(var yoff=yoff0;yoff<=yoff1;yoff+=ystep)for(var zoff=zoff0;zoff<=zoff1;zoff+=zstep){var idx=xoff+yoff+zoff;bins[idx][binLengths[idx]++]=bi}}for(var _i=0;_i!==N;_i++){var bi=bodies[_i],si=bi.shapes[0];switch(si.type){case SPHERE:var shape=si,x=bi.position.x,y=bi.position.y,z=bi.position.z,r=shape.radius;addBoxToBins(x-r,y-r,z-r,x+r,y+r,z+r,bi);break;case PLANE:var _shape=si;_shape.worldNormalNeedsUpdate&&_shape.computeWorldNormal(bi.quaternion);var planeNormal=_shape.worldNormal,xreset=xmin+.5*binsizeX-bi.position.x,yreset=ymin+.5*binsizeY-bi.position.y,zreset=zmin+.5*binsizeZ-bi.position.z,d=GridBroadphase_collisionPairs_d;d.set(xreset,yreset,zreset);for(var xi=0,xoff=0;xi!==nx;xi++,xoff+=xstep,d.y=yreset,d.x+=binsizeX)for(var yi=0,yoff=0;yi!==ny;yi++,yoff+=ystep,d.z=zreset,d.y+=binsizeY)for(var zi=0,zoff=0;zi!==nz;zi++,zoff+=zstep,d.z+=binsizeZ)if(d.dot(planeNormal)1)for(var bin=bins[_i2],_xi=0;_xi!==binLength;_xi++)for(var _bi=bin[_xi],_yi=0;_yi!==_xi;_yi++){var bj=bin[_yi];this.needBroadphaseCollision(_bi,bj)&&this.intersectionTest(_bi,bj,pairs1,pairs2)}}this.makePairsUnique(pairs1,pairs2)},GridBroadphase}(Broadphase),GridBroadphase_collisionPairs_d=new Vec3,NaiveBroadphase=(new Vec3,function(_Broadphase){function NaiveBroadphase(){return _Broadphase.call(this)||this}_inheritsLoose(NaiveBroadphase,_Broadphase);var _proto=NaiveBroadphase.prototype;return _proto.collisionPairs=function(world,pairs1,pairs2){for(var bi,bj,bodies=world.bodies,n=bodies.length,i=0;i!==n;i++)for(var j=0;j!==i;j++)bi=bodies[i],bj=bodies[j],this.needBroadphaseCollision(bi,bj)&&this.intersectionTest(bi,bj,pairs1,pairs2)},_proto.aabbQuery=function(world,aabb,result){void 0===result&&(result=[]);for(var i=0;ishape.boundingSphereRadius)){var intersectMethod=this[shape.type];intersectMethod&&intersectMethod.call(this,shape,quat,position,body,shape)}},_proto._intersectBox=function(box,quat,position,body,reportedShape){return this._intersectConvex(box.convexPolyhedronRepresentation,quat,position,body,reportedShape)},_proto._intersectPlane=function(shape,quat,position,body,reportedShape){var from=this.from,to=this.to,direction=this.direction,worldNormal=new Vec3(0,0,1);quat.vmult(worldNormal,worldNormal);var len=new Vec3;from.vsub(position,len);var planeToFrom=len.dot(worldNormal);if(to.vsub(position,len),!(planeToFrom*len.dot(worldNormal)>0||from.distanceTo(to)=0&&d1<=1&&(from.lerp(to,d1,intersectionPoint),intersectionPoint.vsub(position,normal),normal.normalize(),this.reportIntersection(normal,intersectionPoint,reportedShape,body,-1)),this.result.shouldStop)return;d2>=0&&d2<=1&&(from.lerp(to,d2,intersectionPoint),intersectionPoint.vsub(position,normal),normal.normalize(),this.reportIntersection(normal,intersectionPoint,reportedShape,body,-1))}},_proto._intersectConvex=function(shape,quat,position,body,reportedShape,options){for(var normal=intersectConvex_normal,vector=intersectConvex_vector,faceList=options&&options.faceList||null,faces=shape.faces,vertices=shape.vertices,normals=shape.faceNormals,direction=this.direction,from=this.from,to=this.to,fromToDistance=from.distanceTo(to),Nfaces=faceList?faceList.length:faces.length,result=this.result,j=0;!result.shouldStop&&jfromToDistance||this.reportIntersection(normal,intersectPoint,reportedShape,body,fi)}}}}},_proto._intersectTrimesh=function(mesh,quat,position,body,reportedShape,options){var normal=intersectTrimesh_normal,triangles=intersectTrimesh_triangles,treeTransform=intersectTrimesh_treeTransform,vector=intersectConvex_vector,localDirection=intersectTrimesh_localDirection,localFrom=intersectTrimesh_localFrom,localTo=intersectTrimesh_localTo,worldIntersectPoint=intersectTrimesh_worldIntersectPoint,worldNormal=intersectTrimesh_worldNormal,indices=(options&&options.faceList,mesh.indices),from=(mesh.vertices,this.from),to=this.to,direction=this.direction;treeTransform.position.copy(position),treeTransform.quaternion.copy(quat),Transform.vectorToLocalFrame(position,quat,direction,localDirection),Transform.pointToLocalFrame(position,quat,from,localFrom),Transform.pointToLocalFrame(position,quat,to,localTo),localTo.x*=mesh.scale.x,localTo.y*=mesh.scale.y,localTo.z*=mesh.scale.z,localFrom.x*=mesh.scale.x,localFrom.y*=mesh.scale.y,localFrom.z*=mesh.scale.z,localTo.vsub(localFrom,localDirection),localDirection.normalize();var fromToDistanceSquared=localFrom.distanceSquared(localTo);mesh.tree.rayQuery(this,treeTransform,triangles);for(var i=0,N=triangles.length;!this.result.shouldStop&&i!==N;i++){var trianglesIndex=triangles[i];mesh.getNormal(trianglesIndex,normal),mesh.getVertex(indices[3*trianglesIndex],a),a.vsub(localFrom,vector);var dot=localDirection.dot(normal),scalar=normal.dot(vector)/dot;if(!(scalar<0)){localDirection.scale(scalar,intersectPoint),intersectPoint.vadd(localFrom,intersectPoint),mesh.getVertex(indices[3*trianglesIndex+1],b),mesh.getVertex(indices[3*trianglesIndex+2],c);var squaredDistance=intersectPoint.distanceSquared(localFrom);!pointInTriangle(intersectPoint,b,a,c)&&!pointInTriangle(intersectPoint,a,b,c)||squaredDistance>fromToDistanceSquared||(Transform.vectorToWorldFrame(quat,normal,worldNormal),Transform.pointToWorldFrame(position,quat,intersectPoint,worldIntersectPoint),this.reportIntersection(worldNormal,worldIntersectPoint,reportedShape,body,trianglesIndex))}}triangles.length=0},_proto.reportIntersection=function(normal,hitPointWorld,shape,body,hitFaceIndex){var from=this.from,to=this.to,distance=from.distanceTo(hitPointWorld),result=this.result;if(!(this.skipBackfaces&&normal.dot(this.direction)>0))switch(result.hitFaceIndex=void 0!==hitFaceIndex?hitFaceIndex:-1,this.mode){case Ray.ALL:this.hasHit=!0,result.set(from,to,normal,hitPointWorld,shape,body,distance),result.hasHit=!0,this.callback(result);break;case Ray.CLOSEST:(distance=0&&(v=dot00*dot12-dot01*dot02)>=0&&u+vvarianceY?varianceX>varianceZ?0:2:varianceY>varianceZ?1:2},_proto.aabbQuery=function(world,aabb,result){void 0===result&&(result=[]),this.dirty&&(this.sortList(),this.dirty=!1);var axisIndex=this.axisIndex,axis="x";1===axisIndex&&(axis="y"),2===axisIndex&&(axis="z");for(var axisList=this.axisList,i=(aabb.lowerBound[axis],aabb.upperBound[axis],0);i=0&&!(a[j].aabb.lowerBound.x<=v.aabb.lowerBound.x);j--)a[j+1]=a[j];a[j+1]=v}return a},SAPBroadphase.insertionSortY=function(a){for(var i=1,l=a.length;i=0&&!(a[j].aabb.lowerBound.y<=v.aabb.lowerBound.y);j--)a[j+1]=a[j];a[j+1]=v}return a},SAPBroadphase.insertionSortZ=function(a){for(var i=1,l=a.length;i=0&&!(a[j].aabb.lowerBound.z<=v.aabb.lowerBound.z);j--)a[j+1]=a[j];a[j+1]=v}return a},SAPBroadphase.checkBounds=function(bi,bj,axisIndex){var biPos,bjPos;0===axisIndex?(biPos=bi.position.x,bjPos=bj.position.x):1===axisIndex?(biPos=bi.position.y,bjPos=bj.position.y):2===axisIndex&&(biPos=bi.position.z,bjPos=bj.position.z);var ri=bi.boundingRadius;return bjPos-bj.boundingRadius=-.1)this.suspensionRelativeVelocity=0,this.clippedInvContactDotSuspension=10;else{var inv=-1/project;this.suspensionRelativeVelocity=projVel*inv,this.clippedInvContactDotSuspension=inv}}else raycastResult.suspensionLength=this.suspensionRestLength,this.suspensionRelativeVelocity=0,raycastResult.directionWorld.scale(-1,raycastResult.hitNormalWorld),this.clippedInvContactDotSuspension=1},WheelInfo}(),chassis_velocity_at_contactPoint=new Vec3,relpos=new Vec3,RaycastVehicle=function(){function RaycastVehicle(options){this.chassisBody=options.chassisBody,this.wheelInfos=[],this.sliding=!1,this.world=null,this.indexRightAxis=void 0!==options.indexRightAxis?options.indexRightAxis:1,this.indexForwardAxis=void 0!==options.indexForwardAxis?options.indexForwardAxis:0,this.indexUpAxis=void 0!==options.indexUpAxis?options.indexUpAxis:2,this.constraints=[],this.preStepCallback=function(){},this.currentVehicleSpeedKmHour=0}var _proto=RaycastVehicle.prototype;return _proto.addWheel=function(options){void 0===options&&(options={});var info=new WheelInfo(options),index=this.wheelInfos.length;return this.wheelInfos.push(info),index},_proto.setSteeringValue=function(value,wheelIndex){this.wheelInfos[wheelIndex].steering=value},_proto.applyEngineForce=function(value,wheelIndex){this.wheelInfos[wheelIndex].engineForce=value},_proto.setBrake=function(brake,wheelIndex){this.wheelInfos[wheelIndex].brake=brake},_proto.addToWorld=function(world){this.constraints;world.addBody(this.chassisBody);var that=this;this.preStepCallback=function(){that.updateVehicle(world.dt)},world.addEventListener("preStep",this.preStepCallback),this.world=world},_proto.getVehicleAxisWorld=function(axisIndex,result){result.set(0===axisIndex?1:0,1===axisIndex?1:0,2===axisIndex?1:0),this.chassisBody.vectorToWorldFrame(result,result)},_proto.updateVehicle=function(timeStep){for(var wheelInfos=this.wheelInfos,numWheels=wheelInfos.length,chassisBody=this.chassisBody,i=0;iwheel.maxSuspensionForce&&(suspensionForce=wheel.maxSuspensionForce),wheel.raycastResult.hitNormalWorld.scale(suspensionForce*timeStep,impulse),wheel.raycastResult.hitPointWorld.vsub(chassisBody.position,relpos),chassisBody.applyImpulse(impulse,relpos)}this.updateFriction(timeStep);for(var hitNormalWorldScaledWithProj=new Vec3,fwd=new Vec3,vel=new Vec3,_i3=0;_i30?1:-1)*_wheel.customSlidingRotationalSpeed*timeStep),Math.abs(_wheel.brake)>Math.abs(_wheel.engineForce)&&(_wheel.deltaRotation=0),_wheel.rotation+=_wheel.deltaRotation,_wheel.deltaRotation*=.99}},_proto.updateSuspension=function(deltaTime){for(var chassisMass=this.chassisBody.mass,wheelInfos=this.wheelInfos,numWheels=wheelInfos.length,w_it=0;w_itmaxSuspensionLength&&(wheel.suspensionLength=maxSuspensionLength,wheel.raycastResult.reset());var denominator=wheel.raycastResult.hitNormalWorld.dot(wheel.directionWorld),chassis_velocity_at_contactPoint=new Vec3;chassisBody.getVelocityAtWorldPoint(wheel.raycastResult.hitPointWorld,chassis_velocity_at_contactPoint);var projVel=wheel.raycastResult.hitNormalWorld.dot(chassis_velocity_at_contactPoint);if(denominator>=-.1)wheel.suspensionRelativeVelocity=0,wheel.clippedInvContactDotSuspension=10;else{var inv=-1/denominator;wheel.suspensionRelativeVelocity=projVel*inv,wheel.clippedInvContactDotSuspension=inv}}else wheel.suspensionLength=wheel.suspensionRestLength+0*wheel.maxSuspensionTravel,wheel.suspensionRelativeVelocity=0,wheel.directionWorld.scale(-1,wheel.raycastResult.hitNormalWorld),wheel.clippedInvContactDotSuspension=1;return depth},_proto.updateWheelTransformWorld=function(wheel){wheel.isInContact=!1;var chassisBody=this.chassisBody;chassisBody.pointToWorldFrame(wheel.chassisConnectionPointLocal,wheel.chassisConnectionPointWorld),chassisBody.vectorToWorldFrame(wheel.directionLocal,wheel.directionWorld),chassisBody.vectorToWorldFrame(wheel.axleLocal,wheel.axleWorld)},_proto.updateWheelTransform=function(wheelIndex){var up=tmpVec4,right=tmpVec5,fwd=tmpVec6,wheel=this.wheelInfos[wheelIndex];this.updateWheelTransformWorld(wheel),wheel.directionLocal.scale(-1,up),right.copy(wheel.axleLocal),up.cross(right,fwd),fwd.normalize(),right.normalize();var steering=wheel.steering,steeringOrn=new Quaternion;steeringOrn.setFromAxisAngle(up,steering);var rotatingOrn=new Quaternion;rotatingOrn.setFromAxisAngle(right,wheel.rotation);var q=wheel.worldTransform.quaternion;this.chassisBody.quaternion.mult(steeringOrn,q),q.mult(rotatingOrn,q),q.normalize();var p=wheel.worldTransform.position;p.copy(wheel.directionWorld),p.scale(wheel.suspensionLength,p),p.vadd(wheel.chassisConnectionPointWorld,p)},_proto.getWheelTransformWorld=function(wheelIndex){return this.wheelInfos[wheelIndex].worldTransform},_proto.updateFriction=function(timeStep){for(var surfNormalWS_scaled_proj=updateFriction_surfNormalWS_scaled_proj,wheelInfos=this.wheelInfos,numWheels=wheelInfos.length,chassisBody=this.chassisBody,forwardWS=updateFriction_forwardWS,axle=updateFriction_axle,i=0;imaximpSquared){this.sliding=!0,_wheel3.sliding=!0;var _factor=maximp/Math.sqrt(impulseSquared);_wheel3.skidInfo*=_factor}}}if(this.sliding)for(var _i6=0;_i61.1)return 0;var vel1=resolveSingleBilateral_vel1,vel2=resolveSingleBilateral_vel2,vel=resolveSingleBilateral_vel;body1.getVelocityAtWorldPoint(pos1,vel1),body2.getVelocityAtWorldPoint(pos2,vel2),vel1.vsub(vel2,vel);return-.2*normal.dot(vel)*(1/(body1.invMass+body2.invMass))}var Sphere=function(_Shape){function Sphere(radius){var _this;if((_this=_Shape.call(this,{type:Shape.types.SPHERE})||this).radius=void 0!==radius?radius:1,_this.radius<0)throw new Error("The sphere radius cannot be negative.");return _this.updateBoundingSphereRadius(),_this}_inheritsLoose(Sphere,_Shape);var _proto=Sphere.prototype;return _proto.calculateLocalInertia=function(mass,target){void 0===target&&(target=new Vec3);var I=2*mass*this.radius*this.radius/5;return target.x=I,target.y=I,target.z=I,target},_proto.volume=function(){return 4*Math.PI*Math.pow(this.radius,3)/3},_proto.updateBoundingSphereRadius=function(){this.boundingSphereRadius=this.radius},_proto.calculateWorldAABB=function(pos,quat,min,max){for(var r=this.radius,axes=["x","y","z"],i=0;ithis.particles.length&&this.neighbors.pop())},_proto.getNeighbors=function(particle,neighbors){for(var N=this.particles.length,id=particle.id,R2=this.smoothingRadius*this.smoothingRadius,dist=SPHSystem_getNeighbors_dist,i=0;i!==N;i++){var p=this.particles[i];p.position.vsub(particle.position,dist),id!==p.id&&dist.lengthSquared()maxValue&&(maxValue=v)}this.maxValue=maxValue},_proto.setHeightValueAtIndex=function(xi,yi,value){this.data[xi][yi]=value,this.clearCachedConvexTrianglePillar(xi,yi,!1),xi>0&&(this.clearCachedConvexTrianglePillar(xi-1,yi,!0),this.clearCachedConvexTrianglePillar(xi-1,yi,!1)),yi>0&&(this.clearCachedConvexTrianglePillar(xi,yi-1,!0),this.clearCachedConvexTrianglePillar(xi,yi-1,!1)),yi>0&&xi>0&&this.clearCachedConvexTrianglePillar(xi-1,yi-1,!0)},_proto.getRectMinMax=function(iMinX,iMinY,iMaxX,iMaxY,result){void 0===result&&(result=[]);for(var data=this.data,max=this.minValue,i=iMinX;i<=iMaxX;i++)for(var j=iMinY;j<=iMaxY;j++){var height=data[i][j];height>max&&(max=height)}result[0]=this.minValue,result[1]=max},_proto.getIndexOfPosition=function(x,y,result,clamp){var w=this.elementSize,data=this.data,xi=Math.floor(x/w),yi=Math.floor(y/w);return result[0]=xi,result[1]=yi,clamp&&(xi<0&&(xi=0),yi<0&&(yi=0),xi>=data.length-1&&(xi=data.length-1),yi>=data[0].length-1&&(yi=data[0].length-1)),!(xi<0||yi<0||xi>=data.length-1||yi>=data[0].length-1)},_proto.getTriangleAt=function(x,y,edgeClamp,a,b,c){var idx=getHeightAt_idx;this.getIndexOfPosition(x,y,idx,edgeClamp);var xi=idx[0],yi=idx[1],data=this.data;edgeClamp&&(xi=Math.min(data.length-2,Math.max(0,xi)),yi=Math.min(data[0].length-2,Math.max(0,yi)));var elementSize=this.elementSize,upper=Math.pow(x/elementSize-xi,2)+Math.pow(y/elementSize-yi,2)>Math.pow(x/elementSize-(xi+1),2)+Math.pow(y/elementSize-(yi+1),2);return this.getTriangle(xi,yi,upper,a,b,c),upper},_proto.getNormalAt=function(x,y,edgeClamp,result){var a=getNormalAt_a,b=getNormalAt_b,c=getNormalAt_c,e0=getNormalAt_e0,e1=getNormalAt_e1;this.getTriangleAt(x,y,edgeClamp,a,b,c),b.vsub(a,e0),c.vsub(a,e1),e0.cross(e1,result),result.normalize()},_proto.getAabbAtIndex=function(xi,yi,_ref){var lowerBound=_ref.lowerBound,upperBound=_ref.upperBound,data=this.data,elementSize=this.elementSize;lowerBound.set(xi*elementSize,yi*elementSize,data[xi][yi]),upperBound.set((xi+1)*elementSize,(yi+1)*elementSize,data[xi+1][yi+1])},_proto.getHeightAt=function(x,y,edgeClamp){var data=this.data,a=getHeightAt_a,b=getHeightAt_b,c=getHeightAt_c,idx=getHeightAt_idx;this.getIndexOfPosition(x,y,idx,edgeClamp);var xi=idx[0],yi=idx[1];edgeClamp&&(xi=Math.min(data.length-2,Math.max(0,xi)),yi=Math.min(data[0].length-2,Math.max(0,yi)));var upper=this.getTriangleAt(x,y,edgeClamp,a,b,c);!function(x,y,ax,ay,bx,by,cx,cy,result){result.x=((by-cy)*(x-cx)+(cx-bx)*(y-cy))/((by-cy)*(ax-cx)+(cx-bx)*(ay-cy)),result.y=((cy-ay)*(x-cx)+(ax-cx)*(y-cy))/((by-cy)*(ax-cx)+(cx-bx)*(ay-cy)),result.z=1-result.x-result.y}(x,y,a.x,a.y,b.x,b.y,c.x,c.y,getHeightAt_weights);var w=getHeightAt_weights;return upper?data[xi+1][yi+1]*w.x+data[xi][yi+1]*w.y+data[xi+1][yi]*w.z:data[xi][yi]*w.x+data[xi+1][yi]*w.y+data[xi][yi+1]*w.z},_proto.getCacheConvexTrianglePillarKey=function(xi,yi,getUpperTriangle){return xi+"_"+yi+"_"+(getUpperTriangle?1:0)},_proto.getCachedConvexTrianglePillar=function(xi,yi,getUpperTriangle){return this._cachedPillars[this.getCacheConvexTrianglePillarKey(xi,yi,getUpperTriangle)]},_proto.setCachedConvexTrianglePillar=function(xi,yi,getUpperTriangle,convex,offset){this._cachedPillars[this.getCacheConvexTrianglePillarKey(xi,yi,getUpperTriangle)]={convex:convex,offset:offset}},_proto.clearCachedConvexTrianglePillar=function(xi,yi,getUpperTriangle){delete this._cachedPillars[this.getCacheConvexTrianglePillarKey(xi,yi,getUpperTriangle)]},_proto.getTriangle=function(xi,yi,upper,a,b,c){var data=this.data,elementSize=this.elementSize;upper?(a.set((xi+1)*elementSize,(yi+1)*elementSize,data[xi+1][yi+1]),b.set(xi*elementSize,(yi+1)*elementSize,data[xi][yi+1]),c.set((xi+1)*elementSize,yi*elementSize,data[xi+1][yi])):(a.set(xi*elementSize,yi*elementSize,data[xi][yi]),b.set((xi+1)*elementSize,yi*elementSize,data[xi+1][yi]),c.set(xi*elementSize,(yi+1)*elementSize,data[xi][yi+1]))},_proto.getConvexTrianglePillar=function(xi,yi,getUpperTriangle){var result=this.pillarConvex,offsetResult=this.pillarOffset;if(this.cacheEnabled){var _data=this.getCachedConvexTrianglePillar(xi,yi,getUpperTriangle);if(_data)return this.pillarConvex=_data.convex,void(this.pillarOffset=_data.offset);result=new ConvexPolyhedron,offsetResult=new Vec3,this.pillarConvex=result,this.pillarOffset=offsetResult}var data=this.data,elementSize=this.elementSize,faces=result.faces;result.vertices.length=6;for(var i=0;i<6;i++)result.vertices[i]||(result.vertices[i]=new Vec3);faces.length=5;for(var _i=0;_i<5;_i++)faces[_i]||(faces[_i]=[]);var verts=result.vertices,h=(Math.min(data[xi][yi],data[xi+1][yi],data[xi][yi+1],data[xi+1][yi+1])-this.minValue)/2+this.minValue;getUpperTriangle?(offsetResult.set((xi+.75)*elementSize,(yi+.75)*elementSize,h),verts[0].set(.25*elementSize,.25*elementSize,data[xi+1][yi+1]-h),verts[1].set(-.75*elementSize,.25*elementSize,data[xi][yi+1]-h),verts[2].set(.25*elementSize,-.75*elementSize,data[xi+1][yi]-h),verts[3].set(.25*elementSize,.25*elementSize,-h-1),verts[4].set(-.75*elementSize,.25*elementSize,-h-1),verts[5].set(.25*elementSize,-.75*elementSize,-h-1),faces[0][0]=0,faces[0][1]=1,faces[0][2]=2,faces[1][0]=5,faces[1][1]=4,faces[1][2]=3,faces[2][0]=2,faces[2][1]=5,faces[2][2]=3,faces[2][3]=0,faces[3][0]=3,faces[3][1]=4,faces[3][2]=1,faces[3][3]=0,faces[4][0]=1,faces[4][1]=4,faces[4][2]=5,faces[4][3]=2):(offsetResult.set((xi+.25)*elementSize,(yi+.25)*elementSize,h),verts[0].set(-.25*elementSize,-.25*elementSize,data[xi][yi]-h),verts[1].set(.75*elementSize,-.25*elementSize,data[xi+1][yi]-h),verts[2].set(-.25*elementSize,.75*elementSize,data[xi][yi+1]-h),verts[3].set(-.25*elementSize,-.25*elementSize,-h-1),verts[4].set(.75*elementSize,-.25*elementSize,-h-1),verts[5].set(-.25*elementSize,.75*elementSize,-h-1),faces[0][0]=0,faces[0][1]=1,faces[0][2]=2,faces[1][0]=5,faces[1][1]=4,faces[1][2]=3,faces[2][0]=0,faces[2][1]=2,faces[2][2]=5,faces[2][3]=3,faces[3][0]=1,faces[3][1]=0,faces[3][2]=3,faces[3][3]=4,faces[4][0]=4,faces[4][1]=5,faces[4][2]=2,faces[4][3]=1),result.computeNormals(),result.computeEdges(),result.updateBoundingSphereRadius(),this.setCachedConvexTrianglePillar(xi,yi,getUpperTriangle,result,offsetResult)},_proto.calculateLocalInertia=function(mass,target){return void 0===target&&(target=new Vec3),target.set(0,0,0),target},_proto.volume=function(){return Number.MAX_VALUE},_proto.calculateWorldAABB=function(pos,quat,min,max){min.set(-Number.MAX_VALUE,-Number.MAX_VALUE,-Number.MAX_VALUE),max.set(Number.MAX_VALUE,Number.MAX_VALUE,Number.MAX_VALUE)},_proto.updateBoundingSphereRadius=function(){var data=this.data,s=this.elementSize;this.boundingSphereRadius=new Vec3(data.length*s,data[0].length*s,Math.max(Math.abs(this.maxValue),Math.abs(this.minValue))).length()},_proto.setHeightsFromImage=function(image,scale){var x=scale.x,z=scale.z,y=scale.y,canvas=document.createElement("canvas");canvas.width=image.width,canvas.height=image.height;var context=canvas.getContext("2d");context.drawImage(image,0,0);var imageData=context.getImageData(0,0,image.width,image.height),matrix=this.data;matrix.length=0,this.elementSize=Math.abs(x)/imageData.width;for(var i=0;i=0;i--)this.children[i].removeEmptyNodes(),this.children[i].children.length||this.children[i].data.length||this.children.splice(i,1)},OctreeNode}()),halfDiagonal=new Vec3,tmpAABB$1=new AABB,Trimesh=function(_Shape){function Trimesh(vertices,indices){var _this;return(_this=_Shape.call(this,{type:Shape.types.TRIMESH})||this).vertices=new Float32Array(vertices),_this.indices=new Int16Array(indices),_this.normals=new Float32Array(indices.length),_this.aabb=new AABB,_this.edges=null,_this.scale=new Vec3(1,1,1),_this.tree=new Octree,_this.updateEdges(),_this.updateNormals(),_this.updateAABB(),_this.updateBoundingSphereRadius(),_this.updateTree(),_this}_inheritsLoose(Trimesh,_Shape);var _proto=Trimesh.prototype;return _proto.updateTree=function(){var tree=this.tree;tree.reset(),tree.aabb.copy(this.aabb);var scale=this.scale;tree.aabb.lowerBound.x*=1/scale.x,tree.aabb.lowerBound.y*=1/scale.y,tree.aabb.lowerBound.z*=1/scale.z,tree.aabb.upperBound.x*=1/scale.x,tree.aabb.upperBound.y*=1/scale.y,tree.aabb.upperBound.z*=1/scale.z;for(var triangleAABB=new AABB,a=new Vec3,b=new Vec3,c=new Vec3,points=[a,b,c],i=0;iu.x&&(u.x=v.x),v.yu.y&&(u.y=v.y),v.zu.z&&(u.z=v.z)},_proto.updateAABB=function(){this.computeLocalAABB(this.aabb)},_proto.updateBoundingSphereRadius=function(){for(var max2=0,vertices=this.vertices,v=new Vec3,i=0,N=vertices.length/3;i!==N;i++){this.getVertex(i,v);var norm2=v.lengthSquared();norm2>max2&&(max2=norm2)}this.boundingSphereRadius=Math.sqrt(max2)},_proto.calculateWorldAABB=function(pos,quat,min,max){var frame=calculateWorldAABB_frame,result=calculateWorldAABB_aabb;frame.position=pos,frame.quaternion=quat,this.aabb.toWorldFrame(frame,result),min.copy(result.lowerBound),max.copy(result.upperBound)},_proto.volume=function(){return 4*Math.PI*this.boundingSphereRadius/3},Trimesh}(Shape),computeNormals_n=new Vec3,unscaledAABB=new AABB,getEdgeVector_va=new Vec3,getEdgeVector_vb=new Vec3,cb=new Vec3,ab=new Vec3;Trimesh.computeNormal=function(va,vb,vc,target){vb.vsub(va,ab),vc.vsub(vb,cb),cb.cross(ab,target),target.isZero()||target.normalize()};var va=new Vec3,vb=new Vec3,vc=new Vec3,cli_aabb=new AABB,computeLocalAABB_worldVert=new Vec3,calculateWorldAABB_frame=new Transform,calculateWorldAABB_aabb=new AABB;Trimesh.createTorus=function(radius,tube,radialSegments,tubularSegments,arc){void 0===radius&&(radius=1),void 0===tube&&(tube=.5),void 0===radialSegments&&(radialSegments=8),void 0===tubularSegments&&(tubularSegments=6),void 0===arc&&(arc=2*Math.PI);for(var vertices=[],indices=[],j=0;j<=radialSegments;j++)for(var i=0;i<=tubularSegments;i++){var u=i/tubularSegments*arc,v=j/radialSegments*Math.PI*2,x=(radius+tube*Math.cos(v))*Math.cos(u),y=(radius+tube*Math.cos(v))*Math.sin(u),z=tube*Math.sin(v);vertices.push(x,y,z)}for(var _j=1;_j<=radialSegments;_j++)for(var _i2=1;_i2<=tubularSegments;_i2++){var a=(tubularSegments+1)*_j+_i2-1,b=(tubularSegments+1)*(_j-1)+_i2-1,c=(tubularSegments+1)*(_j-1)+_i2,d=(tubularSegments+1)*_j+_i2;indices.push(a,b,d),indices.push(b,c,d)}return new Trimesh(vertices,indices)};var Solver=function(){function Solver(){this.equations=[]}var _proto=Solver.prototype;return _proto.solve=function(dt,world){return 0},_proto.addEquation=function(eq){eq.enabled&&this.equations.push(eq)},_proto.removeEquation=function(eq){var eqs=this.equations,i=eqs.indexOf(eq);-1!==i&&eqs.splice(i,1)},_proto.removeAllEquations=function(){this.equations.length=0},Solver}(),GSSolver=function(_Solver){function GSSolver(){var _this;return(_this=_Solver.call(this)||this).iterations=10,_this.tolerance=1e-7,_this}return _inheritsLoose(GSSolver,_Solver),GSSolver.prototype.solve=function(dt,world){var B,invC,deltalambda,deltalambdaTot,lambdaj,iter=0,maxIter=this.iterations,tolSquared=this.tolerance*this.tolerance,equations=this.equations,Neq=equations.length,bodies=world.bodies,Nbodies=bodies.length,h=dt;if(0!==Neq)for(var i=0;i!==Nbodies;i++)bodies[i].updateSolveMassProperties();var invCs=GSSolver_solve_invCs,Bs=GSSolver_solve_Bs,lambda=GSSolver_solve_lambda;invCs.length=Neq,Bs.length=Neq,lambda.length=Neq;for(var _i=0;_i!==Neq;_i++){var c=equations[_i];lambda[_i]=0,Bs[_i]=c.computeB(h),invCs[_i]=1/c.computeC()}if(0!==Neq){for(var _i2=0;_i2!==Nbodies;_i2++){var b=bodies[_i2],vlambda=b.vlambda,wlambda=b.wlambda;vlambda.set(0,0,0),wlambda.set(0,0,0)}for(iter=0;iter!==maxIter;iter++){deltalambdaTot=0;for(var j=0;j!==Neq;j++){var _c=equations[j];B=Bs[j],invC=invCs[j],(lambdaj=lambda[j])+(deltalambda=invC*(B-_c.computeGWlambda()-_c.eps*lambdaj))<_c.minForce?deltalambda=_c.minForce-lambdaj:lambdaj+deltalambda>_c.maxForce&&(deltalambda=_c.maxForce-lambdaj),lambda[j]+=deltalambda,deltalambdaTot+=deltalambda>0?deltalambda:-deltalambda,_c.addToWlambda(deltalambda)}if(deltalambdaTot*deltalambdaTotsize;)objects.pop();for(;objects.length=0&&matB.restitution>=0&&(c.restitution=matA.restitution*matB.restitution),c.si=overrideShapeA||si,c.sj=overrideShapeB||sj,c},_proto.createFrictionEquationsFromContact=function(contactEquation,outArray){var bodyA=contactEquation.bi,bodyB=contactEquation.bj,shapeA=contactEquation.si,shapeB=contactEquation.sj,world=this.world,cm=this.currentContactMaterial,friction=cm.friction,matA=shapeA.material||bodyA.material,matB=shapeB.material||bodyB.material;if(matA&&matB&&matA.friction>=0&&matB.friction>=0&&(friction=matA.friction*matB.friction),friction>0){var mug=friction*world.gravity.length(),reducedMass=bodyA.invMass+bodyB.invMass;reducedMass>0&&(reducedMass=1/reducedMass);var pool=this.frictionEquationPool,c1=pool.length?pool.pop():new FrictionEquation(bodyA,bodyB,mug*reducedMass),c2=pool.length?pool.pop():new FrictionEquation(bodyA,bodyB,mug*reducedMass);return c1.bi=c2.bi=bodyA,c1.bj=c2.bj=bodyB,c1.minForce=c2.minForce=-mug*reducedMass,c1.maxForce=c2.maxForce=mug*reducedMass,c1.ri.copy(contactEquation.ri),c1.rj.copy(contactEquation.rj),c2.ri.copy(contactEquation.ri),c2.rj.copy(contactEquation.rj),contactEquation.ni.tangents(c1.t,c2.t),c1.setSpookParams(cm.frictionEquationStiffness,cm.frictionEquationRelaxation,world.dt),c2.setSpookParams(cm.frictionEquationStiffness,cm.frictionEquationRelaxation,world.dt),c1.enabled=c2.enabled=contactEquation.enabled,outArray.push(c1,c2),!0}return!1},_proto.createFrictionFromAverage=function(numContacts){var c=this.result[this.result.length-1];if(this.createFrictionEquationsFromContact(c,this.frictionResult)&&1!==numContacts){var f1=this.frictionResult[this.frictionResult.length-2],f2=this.frictionResult[this.frictionResult.length-1];averageNormal.setZero(),averageContactPointA.setZero(),averageContactPointB.setZero();for(var bodyA=c.bi,i=(c.bj,0);i!==numContacts;i++)(c=this.result[this.result.length-1-i]).bi!==bodyA?(averageNormal.vadd(c.ni,averageNormal),averageContactPointA.vadd(c.ri,averageContactPointA),averageContactPointB.vadd(c.rj,averageContactPointB)):(averageNormal.vsub(c.ni,averageNormal),averageContactPointA.vadd(c.rj,averageContactPointA),averageContactPointB.vadd(c.ri,averageContactPointB));var invNumContacts=1/numContacts;averageContactPointA.scale(invNumContacts,f1.ri),averageContactPointB.scale(invNumContacts,f1.rj),f2.ri.copy(f1.ri),f2.rj.copy(f1.rj),averageNormal.normalize(),averageNormal.tangents(f1.t,f2.t)}},_proto.getContacts=function(p1,p2,world,result,oldcontacts,frictionResult,frictionPool){this.contactPointPool=oldcontacts,this.frictionEquationPool=frictionPool,this.result=result,this.frictionResult=frictionResult;for(var qi=tmpQuat1,qj=tmpQuat2,xi=tmpVec1$3,xj=tmpVec2$3,k=0,N=p1.length;k!==N;k++){var bi=p1[k],bj=p2[k],bodyContactMaterial=null;bi.material&&bj.material&&(bodyContactMaterial=world.getContactMaterial(bi.material,bj.material)||null);for(var justTest=bi.type&Body.KINEMATIC&&bj.type&Body.STATIC||bi.type&Body.STATIC&&bj.type&Body.KINEMATIC||bi.type&Body.KINEMATIC&&bj.type&Body.KINEMATIC,i=0;isi.boundingSphereRadius+sj.boundingSphereRadius)){var shapeContactMaterial=null;si.material&&sj.material&&(shapeContactMaterial=world.getContactMaterial(si.material,sj.material)||null),this.currentContactMaterial=shapeContactMaterial||bodyContactMaterial||world.defaultContactMaterial;var resolver=this[si.type|sj.type];if(resolver){(si.type0){var ns1=sphereBox_ns1,ns2=sphereBox_ns2;ns1.copy(sides[(idx+1)%3]),ns2.copy(sides[(idx+2)%3]);var h1=ns1.length(),h2=ns2.length();ns1.normalize(),ns2.normalize();var dot1=box_to_sphere.dot(ns1),dot2=box_to_sphere.dot(ns2);if(dot1-h1&&dot2-h2){var _dist=Math.abs(dot-h-R);if((null===side_distance||_distsi.boundingSphereRadius+sj.boundingSphereRadius)&&si.findSeparatingAxis(sj,xi,qi,xj,qj,sepAxis,faceListA,faceListB)){var res=[],q=convexConvex_q;si.clipAgainstHull(xi,qi,sj,xj,qj,sepAxis,-100,100,res);for(var numContacts=0,j=0;j!==res.length;j++){if(justTest)return!0;var r=this.createContactEquation(bi,bj,si,sj,rsi,rsj),ri=r.ri,rj=r.rj;sepAxis.negate(r.ni),res[j].normal.negate(q),q.scale(res[j].depth,q),res[j].point.vadd(q,ri),rj.copy(res[j].point),ri.vsub(xi,ri),rj.vsub(xj,rj),ri.vadd(xi,ri),ri.vsub(bi.position,ri),rj.vadd(xj,rj),rj.vsub(bj.position,rj),this.result.push(r),numContacts++,this.enableFrictionReduction||this.createFrictionEquationsFromContact(r,this.frictionResult)}this.enableFrictionReduction&&numContacts&&this.createFrictionFromAverage(numContacts)}},_proto.sphereConvex=function(si,sj,xi,xj,qi,qj,bi,bj,rsi,rsj,justTest){var v3pool=this.v3pool;xi.vsub(xj,convex_to_sphere);for(var normals=sj.faceNormals,faces=sj.faces,verts=sj.vertices,R=si.radius,found=!1,i=0;i!==verts.length;i++){var v=verts[i],worldCorner=sphereConvex_worldCorner;qj.vmult(v,worldCorner),xj.vadd(worldCorner,worldCorner);var sphere_to_corner=sphereConvex_sphereToCorner;if(worldCorner.vsub(xi,sphere_to_corner),sphere_to_corner.lengthSquared()0){for(var faceVerts=[],j=0,Nverts=face.length;j!==Nverts;j++){var worldVertex=v3pool.get();qj.vmult(verts[face[j]],worldVertex),xj.vadd(worldVertex,worldVertex),faceVerts.push(worldVertex)}if(pointInPolygon(faceVerts,worldNormal,xi)){if(justTest)return!0;found=!0;var _r3=this.createContactEquation(bi,bj,si,sj,rsi,rsj);worldNormal.scale(-R,_r3.ri),worldNormal.negate(_r3.ni);var penetrationVec2=v3pool.get();worldNormal.scale(-penetration,penetrationVec2);var penetrationSpherePoint=v3pool.get();worldNormal.scale(-R,penetrationSpherePoint),xi.vsub(xj,_r3.rj),_r3.rj.vadd(penetrationSpherePoint,_r3.rj),_r3.rj.vadd(penetrationVec2,_r3.rj),_r3.rj.vadd(xj,_r3.rj),_r3.rj.vsub(bj.position,_r3.rj),_r3.ri.vadd(xi,_r3.ri),_r3.ri.vsub(bi.position,_r3.ri),v3pool.release(penetrationVec2),v3pool.release(penetrationSpherePoint),this.result.push(_r3),this.createFrictionEquationsFromContact(_r3,this.frictionResult);for(var _j2=0,Nfaceverts=faceVerts.length;_j2!==Nfaceverts;_j2++)v3pool.release(faceVerts[_j2]);return}for(var _j3=0;_j3!==face.length;_j3++){var v1=v3pool.get(),v2=v3pool.get();qj.vmult(verts[face[(_j3+1)%face.length]],v1),qj.vmult(verts[face[(_j3+2)%face.length]],v2),xj.vadd(v1,v1),xj.vadd(v2,v2);var edge=sphereConvex_edge;v2.vsub(v1,edge);var edgeUnit=sphereConvex_edgeUnit;edge.unit(edgeUnit);var p=v3pool.get(),v1_to_xi=v3pool.get();xi.vsub(v1,v1_to_xi);var dot=v1_to_xi.dot(edgeUnit);edgeUnit.scale(dot,p),p.vadd(v1,p);var xi_to_p=v3pool.get();if(p.vsub(xi,xi_to_p),dot>0&&dot*dotdata.length||iMinY>data[0].length)){iMinX<0&&(iMinX=0),iMaxX<0&&(iMaxX=0),iMinY<0&&(iMinY=0),iMaxY<0&&(iMaxY=0),iMinX>=data.length&&(iMinX=data.length-1),iMaxX>=data.length&&(iMaxX=data.length-1),iMaxY>=data[0].length&&(iMaxY=data[0].length-1),iMinY>=data[0].length&&(iMinY=data[0].length-1);var minMax=[];hfShape.getRectMinMax(iMinX,iMinY,iMaxX,iMaxY,minMax);var min=minMax[0],max=minMax[1];if(!(localSpherePos.z-radius>max||localSpherePos.z+radius2)return}}},_proto.boxHeightfield=function(si,sj,xi,xj,qi,qj,bi,bj,rsi,rsj,justTest){return si.convexPolyhedronRepresentation.material=si.material,si.convexPolyhedronRepresentation.collisionResponse=si.collisionResponse,this.convexHeightfield(si.convexPolyhedronRepresentation,sj,xi,xj,qi,qj,bi,bj,si,sj,justTest)},_proto.convexHeightfield=function(convexShape,hfShape,convexPos,hfPos,convexQuat,hfQuat,convexBody,hfBody,rsi,rsj,justTest){var data=hfShape.data,w=hfShape.elementSize,radius=convexShape.boundingSphereRadius,worldPillarOffset=convexHeightfield_tmp2,faceList=convexHeightfield_faceList,localConvexPos=convexHeightfield_tmp1;Transform.pointToLocalFrame(hfPos,hfQuat,convexPos,localConvexPos);var iMinX=Math.floor((localConvexPos.x-radius)/w)-1,iMaxX=Math.ceil((localConvexPos.x+radius)/w)+1,iMinY=Math.floor((localConvexPos.y-radius)/w)-1,iMaxY=Math.ceil((localConvexPos.y+radius)/w)+1;if(!(iMaxX<0||iMaxY<0||iMinX>data.length||iMinY>data[0].length)){iMinX<0&&(iMinX=0),iMaxX<0&&(iMaxX=0),iMinY<0&&(iMinY=0),iMaxY<0&&(iMaxY=0),iMinX>=data.length&&(iMinX=data.length-1),iMaxX>=data.length&&(iMaxX=data.length-1),iMaxY>=data[0].length&&(iMaxY=data[0].length-1),iMinY>=data[0].length&&(iMinY=data[0].length-1);var minMax=[];hfShape.getRectMinMax(iMinX,iMinY,iMaxX,iMaxY,minMax);var min=minMax[0],max=minMax[1];if(!(localConvexPos.z-radius>max||localConvexPos.z+radius0&&positionAlongEdgeB<0)if(localSpherePos.vsub(edgeVertexA,tmp),edgeVectorUnit.copy(edgeVector),edgeVectorUnit.normalize(),positionAlongEdgeA=tmp.dot(edgeVectorUnit),edgeVectorUnit.scale(positionAlongEdgeA,tmp),tmp.vadd(edgeVertexA,tmp),tmp.distanceTo(localSpherePos)0&&!0===positiveResult||r<=0&&!1===positiveResult))return!1;null===positiveResult&&(positiveResult=r>0)}return!0}var box_to_sphere=new Vec3,sphereBox_ns=new Vec3,sphereBox_ns1=new Vec3,sphereBox_ns2=new Vec3,sphereBox_sides=[new Vec3,new Vec3,new Vec3,new Vec3,new Vec3,new Vec3],sphereBox_sphere_to_corner=new Vec3,sphereBox_side_ns=new Vec3,sphereBox_side_ns1=new Vec3,sphereBox_side_ns2=new Vec3;Narrowphase.prototype[COLLISION_TYPES.sphereBox]=Narrowphase.prototype.sphereBox;var convex_to_sphere=new Vec3,sphereConvex_edge=new Vec3,sphereConvex_edgeUnit=new Vec3,sphereConvex_sphereToCorner=new Vec3,sphereConvex_worldCorner=new Vec3,sphereConvex_worldNormal=new Vec3,sphereConvex_worldPoint=new Vec3,sphereConvex_worldSpherePointClosestToPlane=new Vec3,sphereConvex_penetrationVec=new Vec3,sphereConvex_sphereToWorldPoint=new Vec3;Narrowphase.prototype[COLLISION_TYPES.sphereConvex]=Narrowphase.prototype.sphereConvex;new Vec3,new Vec3;Narrowphase.prototype[COLLISION_TYPES.planeBox]=Narrowphase.prototype.planeBox;var planeConvex_v=new Vec3,planeConvex_normal=new Vec3,planeConvex_relpos=new Vec3,planeConvex_projected=new Vec3;Narrowphase.prototype[COLLISION_TYPES.planeConvex]=Narrowphase.prototype.planeConvex;var convexConvex_sepAxis=new Vec3,convexConvex_q=new Vec3;Narrowphase.prototype[COLLISION_TYPES.convexConvex]=Narrowphase.prototype.convexConvex;var particlePlane_normal=new Vec3,particlePlane_relpos=new Vec3,particlePlane_projected=new Vec3;Narrowphase.prototype[COLLISION_TYPES.planeParticle]=Narrowphase.prototype.planeParticle;var particleSphere_normal=new Vec3;Narrowphase.prototype[COLLISION_TYPES.sphereParticle]=Narrowphase.prototype.sphereParticle;var cqj=new Quaternion,convexParticle_local=new Vec3,convexParticle_penetratedFaceNormal=(new Vec3,new Vec3),convexParticle_vertexToParticle=new Vec3,convexParticle_worldPenetrationVec=new Vec3;Narrowphase.prototype[COLLISION_TYPES.convexParticle]=Narrowphase.prototype.convexParticle,Narrowphase.prototype[COLLISION_TYPES.boxHeightfield]=Narrowphase.prototype.boxHeightfield;var convexHeightfield_tmp1=new Vec3,convexHeightfield_tmp2=new Vec3,convexHeightfield_faceList=[0];Narrowphase.prototype[COLLISION_TYPES.convexHeightfield]=Narrowphase.prototype.convexHeightfield;var sphereHeightfield_tmp1=new Vec3,sphereHeightfield_tmp2=new Vec3;Narrowphase.prototype[COLLISION_TYPES.sphereHeightfield]=Narrowphase.prototype.sphereHeightfield;var OverlapKeeper=function(){function OverlapKeeper(){this.current=[],this.previous=[]}var _proto=OverlapKeeper.prototype;return _proto.getKey=function(i,j){if(jcurrent[index];)index++;if(key!==current[index]){for(var _j=current.length-1;_j>=index;_j--)current[_j+1]=current[_j];current[index]=key}},_proto.tick=function(){var tmp=this.current;this.current=this.previous,this.previous=tmp,this.current.length=0},_proto.getDiff=function(additions,removals){for(var a=this.current,b=this.previous,al=a.length,bl=b.length,j=0,i=0;ib[j];)j++;keyA===b[j]||unpackAndPush(additions,keyA)}j=0;for(var _i=0;_ia[j];)j++;a[j]===keyB||unpackAndPush(removals,keyB)}},OverlapKeeper}();function unpackAndPush(array,key){array.push((4294901760&key)>>16,65535&key)}var TupleDictionary=function(){function TupleDictionary(){this.data={keys:[]}}var _proto=TupleDictionary.prototype;return _proto.get=function(i,j){if(i>j){var temp=j;j=i,i=temp}return this.data[i+"-"+j]},_proto.set=function(i,j,value){if(i>j){var temp=j;j=i,i=temp}var key=i+"-"+j;this.get(i,j)||this.data.keys.push(key),this.data[key]=value},_proto.reset=function(){for(var data=this.data,keys=data.keys;keys.length>0;){delete data[keys.pop()]}},TupleDictionary}(),World=function(_EventTarget){function World(options){var _this;return void 0===options&&(options={}),(_this=_EventTarget.call(this)||this).dt=-1,_this.allowSleep=!!options.allowSleep,_this.contacts=[],_this.frictionEquations=[],_this.quatNormalizeSkip=void 0!==options.quatNormalizeSkip?options.quatNormalizeSkip:0,_this.quatNormalizeFast=void 0!==options.quatNormalizeFast&&options.quatNormalizeFast,_this.time=0,_this.stepnumber=0,_this.default_dt=1/60,_this.nextId=0,_this.gravity=new Vec3,options.gravity&&_this.gravity.copy(options.gravity),_this.broadphase=void 0!==options.broadphase?options.broadphase:new NaiveBroadphase,_this.bodies=[],_this.hasActiveBodies=!1,_this.solver=void 0!==options.solver?options.solver:new GSSolver,_this.constraints=[],_this.narrowphase=new Narrowphase(_assertThisInitialized(_this)),_this.collisionMatrix=new ArrayCollisionMatrix,_this.collisionMatrixPrevious=new ArrayCollisionMatrix,_this.bodyOverlapKeeper=new OverlapKeeper,_this.shapeOverlapKeeper=new OverlapKeeper,_this.materials=[],_this.contactmaterials=[],_this.contactMaterialTable=new TupleDictionary,_this.defaultMaterial=new Material("default"),_this.defaultContactMaterial=new ContactMaterial(_this.defaultMaterial,_this.defaultMaterial,{friction:.3,restitution:0}),_this.doProfiling=!1,_this.profile={solve:0,makeContactConstraints:0,broadphase:0,integrate:0,narrowphase:0},_this.accumulator=0,_this.subsystems=[],_this.addBodyEvent={type:"addBody",body:null},_this.removeBodyEvent={type:"removeBody",body:null},_this.idToBodyMap={},_this.broadphase.setWorld(_assertThisInitialized(_this)),_this}_inheritsLoose(World,_EventTarget);var _proto=World.prototype;return _proto.getContactMaterial=function(m1,m2){return this.contactMaterialTable.get(m1.id,m2.id)},_proto.numObjects=function(){return this.bodies.length},_proto.collisionMatrixTick=function(){var temp=this.collisionMatrixPrevious;this.collisionMatrixPrevious=this.collisionMatrix,this.collisionMatrix=temp,this.collisionMatrix.reset(),this.bodyOverlapKeeper.tick(),this.shapeOverlapKeeper.tick()},_proto.addConstraint=function(c){this.constraints.push(c)},_proto.removeConstraint=function(c){var idx=this.constraints.indexOf(c);-1!==idx&&this.constraints.splice(idx,1)},_proto.rayTest=function(from,to,result){result instanceof RaycastResult?this.raycastClosest(from,to,{skipBackfaces:!0},result):this.raycastAll(from,to,{skipBackfaces:!0},result)},_proto.raycastAll=function(from,to,options,callback){return void 0===options&&(options={}),options.mode=Ray.ALL,options.from=from,options.to=to,options.callback=callback,tmpRay$1.intersectWorld(this,options)},_proto.raycastAny=function(from,to,options,result){return void 0===options&&(options={}),options.mode=Ray.ANY,options.from=from,options.to=to,options.result=result,tmpRay$1.intersectWorld(this,options)},_proto.raycastClosest=function(from,to,options,result){return void 0===options&&(options={}),options.mode=Ray.CLOSEST,options.from=from,options.to=to,options.result=result,tmpRay$1.intersectWorld(this,options)},_proto.addBody=function(body){this.bodies.includes(body)||(body.index=this.bodies.length,this.bodies.push(body),body.world=this,body.initPosition.copy(body.position),body.initVelocity.copy(body.velocity),body.timeLastSleepy=this.time,body instanceof Body&&(body.initAngularVelocity.copy(body.angularVelocity),body.initQuaternion.copy(body.quaternion)),this.collisionMatrix.setNumObjects(this.bodies.length),this.addBodyEvent.body=body,this.idToBodyMap[body.id]=body,this.dispatchEvent(this.addBodyEvent))},_proto.removeBody=function(body){body.world=null;var n=this.bodies.length-1,bodies=this.bodies,idx=bodies.indexOf(body);if(-1!==idx){bodies.splice(idx,1);for(var i=0;i!==bodies.length;i++)bodies[i].index=i;this.collisionMatrix.setNumObjects(n),this.removeBodyEvent.body=body,delete this.idToBodyMap[body.id],this.dispatchEvent(this.removeBodyEvent)}},_proto.getBodyById=function(id){return this.idToBodyMap[id]},_proto.getShapeById=function(id){for(var bodies=this.bodies,i=0,bl=bodies.length;i=dt&&substeps=0;j-=1)(c.bodyA===p1[j]&&c.bodyB===p2[j]||c.bodyB===p1[j]&&c.bodyA===p2[j])&&(p1.splice(j,1),p2.splice(j,1))}this.collisionMatrixTick(),doProfiling&&(profilingStart=performance.now());var oldcontacts=World_step_oldContacts,NoldContacts=contacts.length;for(i=0;i!==NoldContacts;i++)oldcontacts.push(contacts[i]);contacts.length=0;var NoldFrictionEquations=this.frictionEquations.length;for(i=0;i!==NoldFrictionEquations;i++)frictionEquationPool.push(this.frictionEquations[i]);for(this.frictionEquations.length=0,this.narrowphase.getContacts(p1,p2,this,contacts,oldcontacts,this.frictionEquations,frictionEquationPool),doProfiling&&(profile.narrowphase=performance.now()-profilingStart),doProfiling&&(profilingStart=performance.now()),i=0;i=0&&bj.material.friction>=0&&_bi.material.friction*bj.material.friction,_bi.material.restitution>=0&&bj.material.restitution>=0&&(_c.restitution=_bi.material.restitution*bj.material.restitution)),solver.addEquation(_c),_bi.allowSleep&&_bi.type===Body.DYNAMIC&&_bi.sleepState===Body.SLEEPING&&bj.sleepState===Body.AWAKE&&bj.type!==Body.STATIC)bj.velocity.lengthSquared()+bj.angularVelocity.lengthSquared()>=2*Math.pow(bj.sleepSpeedLimit,2)&&(_bi.wakeUpAfterNarrowphase=!0);if(bj.allowSleep&&bj.type===Body.DYNAMIC&&bj.sleepState===Body.SLEEPING&&_bi.sleepState===Body.AWAKE&&_bi.type!==Body.STATIC)_bi.velocity.lengthSquared()+_bi.angularVelocity.lengthSquared()>=2*Math.pow(_bi.sleepSpeedLimit,2)&&(bj.wakeUpAfterNarrowphase=!0);this.collisionMatrix.set(_bi,bj,!0),this.collisionMatrixPrevious.get(_bi,bj)||(World_step_collideEvent.body=bj,World_step_collideEvent.contact=_c,_bi.dispatchEvent(World_step_collideEvent),World_step_collideEvent.body=_bi,bj.dispatchEvent(World_step_collideEvent)),this.bodyOverlapKeeper.set(_bi.id,bj.id),this.shapeOverlapKeeper.set(si.id,sj.id)}for(this.emitContactEvents(),doProfiling&&(profile.makeContactConstraints=performance.now()-profilingStart,profilingStart=performance.now()),i=0;i!==N;i++){var _bi2=bodies[i];_bi2.wakeUpAfterNarrowphase&&(_bi2.wakeUp(),_bi2.wakeUpAfterNarrowphase=!1)}for(Nconstraints=constraints.length,i=0;i!==Nconstraints;i++){var _c2=constraints[i];_c2.update();for(var _j=0,Neq=_c2.equations.length;_j!==Neq;_j++){var eq=_c2.equations[_j];solver.addEquation(eq)}}solver.solve(dt,this),doProfiling&&(profile.solve=performance.now()-profilingStart),solver.removeAllEquations();var pow=Math.pow;for(i=0;i!==N;i++){var _bi3=bodies[i];if(_bi3.type&DYNAMIC){var ld=pow(1-_bi3.linearDamping,dt),v=_bi3.velocity;v.scale(ld,v);var av=_bi3.angularVelocity;if(av){var ad=pow(1-_bi3.angularDamping,dt);av.scale(ad,av)}}}for(this.dispatchEvent(World_step_preStepEvent),i=0;i!==N;i++){var _bi4=bodies[i];_bi4.preStep&&_bi4.preStep.call(_bi4)}doProfiling&&(profilingStart=performance.now());var quatNormalize=this.stepnumber%(this.quatNormalizeSkip+1)==0,quatNormalizeFast=this.quatNormalizeFast;for(i=0;i!==N;i++)bodies[i].integrate(dt,quatNormalize,quatNormalizeFast);for(this.clearForces(),this.broadphase.dirty=!0,doProfiling&&(profile.integrate=performance.now()-profilingStart),this.time+=dt,this.stepnumber+=1,this.dispatchEvent(World_step_postStepEvent),i=0;i!==N;i++){var _bi5=bodies[i],postStep=_bi5.postStep;postStep&&postStep.call(_bi5)}var hasActiveBodies=!0;if(this.allowSleep)for(hasActiveBodies=!1,i=0;i!==N;i++){var _bi6=bodies[i];_bi6.sleepTick(this.time),_bi6.sleepState!==Body.SLEEPING&&(hasActiveBodies=!0)}this.hasActiveBodies=hasActiveBodies},_proto.clearForces=function(){for(var bodies=this.bodies,N=bodies.length,i=0;i!==N;i++){var b=bodies[i];b.force,b.torque;b.force.set(0,0,0),b.torque.set(0,0,0)}},World}(EventTarget),tmpRay$1=(new AABB,new Ray);if("undefined"==typeof performance&&(performance={}),!performance.now){var nowOffset=Date.now();performance.timing&&performance.timing.navigationStart&&(nowOffset=performance.timing.navigationStart),performance.now=function(){return Date.now()-nowOffset}}new Vec3;var additions,removals,beginContactEvent,endContactEvent,beginShapeContactEvent,endShapeContactEvent,World_step_postStepEvent={type:"postStep"},World_step_preStepEvent={type:"preStep"},World_step_collideEvent={type:Body.COLLIDE_EVENT_NAME,body:null,contact:null},World_step_oldContacts=[],World_step_frictionEquationPool=[],World_step_p1=[],World_step_p2=[];World.prototype.emitContactEvents=(additions=[],removals=[],beginContactEvent={type:"beginContact",bodyA:null,bodyB:null},endContactEvent={type:"endContact",bodyA:null,bodyB:null},beginShapeContactEvent={type:"beginShapeContact",bodyA:null,bodyB:null,shapeA:null,shapeB:null},endShapeContactEvent={type:"endShapeContact",bodyA:null,bodyB:null,shapeA:null,shapeB:null},function(){var hasBeginContact=this.hasAnyEventListener("beginContact"),hasEndContact=this.hasAnyEventListener("endContact");if((hasBeginContact||hasEndContact)&&this.bodyOverlapKeeper.getDiff(additions,removals),hasBeginContact){for(var i=0,l=additions.length;i{switch(options.cylinderAxis){case"y":return new Ammo.btCylinderShape(btHalfExtents);case"x":return new Ammo.btCylinderShapeX(btHalfExtents);case"z":return new Ammo.btCylinderShapeZ(btHalfExtents)}return null})();return Ammo.destroy(btHalfExtents),_finishCollisionShape(collisionShape,options,_computeScale(root,options)),collisionShape},exports.createCapsuleShape=function(root,options){options.type=TYPE.CAPSULE,_setOptions(options),options.fit===FIT.ALL&&(options.halfExtents=_computeHalfExtents(root,_computeBounds(root),options.minHalfExtent,options.maxHalfExtent));const{x:x,y:y,z:z}=options.halfExtents,collisionShape=(()=>{switch(options.cylinderAxis){case"y":return new Ammo.btCapsuleShape(Math.max(x,z),2*y);case"x":return new Ammo.btCapsuleShapeX(Math.max(y,z),2*x);case"z":return new Ammo.btCapsuleShapeZ(Math.max(x,y),2*z)}return null})();return _finishCollisionShape(collisionShape,options,_computeScale(root,options)),collisionShape},exports.createConeShape=function(root,options){options.type=TYPE.CONE,_setOptions(options),options.fit===FIT.ALL&&(options.halfExtents=_computeHalfExtents(root,_computeBounds(root),options.minHalfExtent,options.maxHalfExtent));const{x:x,y:y,z:z}=options.halfExtents,collisionShape=(()=>{switch(options.cylinderAxis){case"y":return new Ammo.btConeShape(Math.max(x,z),2*y);case"x":return new Ammo.btConeShapeX(Math.max(y,z),2*x);case"z":return new Ammo.btConeShapeZ(Math.max(x,y),2*z)}return null})();return _finishCollisionShape(collisionShape,options,_computeScale(root,options)),collisionShape},exports.createSphereShape=function(root,options){let radius;options.type=TYPE.SPHERE,radius=options.fit!==FIT.MANUAL||isNaN(options.sphereRadius)?_computeRadius(root,_computeBounds(root)):options.sphereRadius;const collisionShape=new Ammo.btSphereShape(radius);return _finishCollisionShape(collisionShape,options,_computeScale(root,options)),collisionShape},exports.createHullShape=function(){const vertex=new THREE.Vector3,center=new THREE.Vector3;return function(root,options){if(options.type=TYPE.HULL,_setOptions(options),options.fit===FIT.MANUAL)return console.warn("cannot use fit: manual with type: hull"),null;const bounds=_computeBounds(root),btVertex=new Ammo.btVector3,originalHull=new Ammo.btConvexHullShape;originalHull.setMargin(options.margin),center.addVectors(bounds.max,bounds.min).multiplyScalar(.5);let vertexCount=0;_iterateGeometries(root,geo=>{vertexCount+=geo.attributes.position.array.length/3});const maxVertices=options.hullMaxVertices||1e5;vertexCount>maxVertices&&console.warn(`too many vertices for hull shape; sampling ~${maxVertices} from ~${vertexCount} vertices`);const p=Math.min(1,maxVertices/vertexCount);_iterateGeometries(root,(geo,transform)=>{const components=geo.attributes.position.array;for(let i=0;i=100){const shapeHull=new Ammo.btShapeHull(originalHull);shapeHull.buildHull(options.margin),Ammo.destroy(originalHull),collisionShape=new Ammo.btConvexHullShape(Ammo.getPointer(shapeHull.getVertexPointer()),shapeHull.numVertices()),Ammo.destroy(shapeHull)}return Ammo.destroy(btVertex),_finishCollisionShape(collisionShape,options,_computeScale(root,options)),collisionShape}}(),exports.createHACDShapes=function(){const v=new THREE.Vector3,center=new THREE.Vector3;return function(root,options){if(options.type=TYPE.HACD,_setOptions(options),options.fit===FIT.MANUAL)return console.warn("cannot use fit: manual with type: hacd"),[];if(!Ammo.hasOwnProperty("HACD"))return console.warn("HACD unavailable in included build of Ammo.js. Visit https://github.com/mozillareality/ammo.js for the latest version."),[];const bounds=_computeBounds(root),scale=_computeScale(root,options);let vertexCount=0,triCount=0;center.addVectors(bounds.max,bounds.min).multiplyScalar(.5),_iterateGeometries(root,geo=>{vertexCount+=geo.attributes.position.array.length/3,geo.index?triCount+=geo.index.array.length/3:triCount+=geo.attributes.position.array.length/9});const hacd=new Ammo.HACD;options.hasOwnProperty("compacityWeight")&&hacd.SetCompacityWeight(options.compacityWeight),options.hasOwnProperty("volumeWeight")&&hacd.SetVolumeWeight(options.volumeWeight),options.hasOwnProperty("nClusters")&&hacd.SetNClusters(options.nClusters),options.hasOwnProperty("nVerticesPerCH")&&hacd.SetNVerticesPerCH(options.nVerticesPerCH),options.hasOwnProperty("concavity")&&hacd.SetConcavity(options.concavity);const points=Ammo._malloc(3*vertexCount*8),triangles=Ammo._malloc(3*triCount*4);hacd.SetPoints(points),hacd.SetTriangles(triangles),hacd.SetNPoints(vertexCount),hacd.SetNTriangles(triCount);const pptr=points/8,tptr=triangles/4;_iterateGeometries(root,(geo,transform)=>{const components=geo.attributes.position.array,indices=geo.index?geo.index.array:null;for(let i=0;i{vertexCount+=geo.attributes.position.count,geo.index?triCount+=geo.index.count/3:triCount+=geo.attributes.position.count/3});const vhacd=new Ammo.VHACD,params=new Ammo.Parameters;options.hasOwnProperty("resolution")&¶ms.set_m_resolution(options.resolution),options.hasOwnProperty("depth")&¶ms.set_m_depth(options.depth),options.hasOwnProperty("concavity")&¶ms.set_m_concavity(options.concavity),options.hasOwnProperty("planeDownsampling")&¶ms.set_m_planeDownsampling(options.planeDownsampling),options.hasOwnProperty("convexhullDownsampling")&¶ms.set_m_convexhullDownsampling(options.convexhullDownsampling),options.hasOwnProperty("alpha")&¶ms.set_m_alpha(options.alpha),options.hasOwnProperty("beta")&¶ms.set_m_beta(options.beta),options.hasOwnProperty("gamma")&¶ms.set_m_gamma(options.gamma),options.hasOwnProperty("pca")&¶ms.set_m_pca(options.pca),options.hasOwnProperty("mode")&¶ms.set_m_mode(options.mode),options.hasOwnProperty("maxNumVerticesPerCH")&¶ms.set_m_maxNumVerticesPerCH(options.maxNumVerticesPerCH),options.hasOwnProperty("minVolumePerCH")&¶ms.set_m_minVolumePerCH(options.minVolumePerCH),options.hasOwnProperty("convexhullApproximation")&¶ms.set_m_convexhullApproximation(options.convexhullApproximation),options.hasOwnProperty("oclAcceleration")&¶ms.set_m_oclAcceleration(options.oclAcceleration);const points=Ammo._malloc(3*vertexCount*8),triangles=Ammo._malloc(3*triCount*4);let pptr=points/8,tptr=triangles/4;_iterateGeometries(root,(geo,transform)=>{const components=geo.attributes.position.array,indices=geo.index?geo.index.array:null;for(let i=0;i{const components=geo.attributes.position.array;if(geo.index)for(let i=0;i{for(let res of collisionShape.resources||[])Ammo.destroy(res);Ammo.destroy(collisionShape)});const localTransform=new Ammo.btTransform,rotation=new Ammo.btQuaternion;if(localTransform.setIdentity(),localTransform.getOrigin().setValue(options.offset.x,options.offset.y,options.offset.z),rotation.setValue(options.orientation.x,options.orientation.y,options.orientation.z,options.orientation.w),localTransform.setRotation(rotation),Ammo.destroy(rotation),scale){const localScale=new Ammo.btVector3(scale.x,scale.y,scale.z);collisionShape.setLocalScaling(localScale),Ammo.destroy(localScale)}collisionShape.localTransform=localTransform},_iterateGeometries=function(){const transform=new THREE.Matrix4,inverse=new THREE.Matrix4,bufferGeometry=new THREE.BufferGeometry;return function(root,cb){inverse.getInverse(root.matrixWorld),root.traverse(mesh=>{!mesh.isMesh||THREE.Sky&&mesh.__proto__==THREE.Sky.prototype||(mesh===root?transform.identity():(hasUpdateMatricesFunction&&mesh.updateMatrices(),transform.multiplyMatrices(inverse,mesh.matrixWorld)),cb(mesh.geometry.isBufferGeometry?mesh.geometry:bufferGeometry.fromGeometry(mesh.geometry),transform))})}}(),_computeScale=function(root,options){const scale=new THREE.Vector3(1,1,1);return options.fit===FIT.ALL&&scale.setFromMatrixScale(root.matrixWorld),scale},_computeRadius=function(){const v=new THREE.Vector3,center=new THREE.Vector3;return function(root,bounds){let maxRadiusSq=0,{x:cx,y:cy,z:cz}=bounds.getCenter(center);return _iterateGeometries(root,(geo,transform)=>{const components=geo.attributes.position.array;for(let i=0;i{const components=geo.attributes.position.array;for(let i=0;imaxX&&(maxX=v.x),v.y>maxY&&(maxY=v.y),v.z>maxZ&&(maxZ=v.z)}),bounds.min.set(minX,minY,minZ),bounds.max.set(maxX,maxY,maxZ),bounds}}()},{}],6:[function(require,module,exports){var e=require("cannon-es"),t=function(){var e,t,r,n,o,i,s,a,u,c,m,f,l,d,p,x,y,E,h,g=[],v=[],w=0,T=function(){var e=new THREE.Vector3,t=new THREE.Vector3,r=new THREE.Vector3;return function(n,o,i){return e.subVectors(i,n),t.subVectors(o,n),r.crossVectors(e,t),r.normalize()}}();function V(e,t){if(void 0!==e.normal)return e.normal;var r=t[e[0]],n=t[e[2]];return m.subVectors(t[e[1]],r),f.subVectors(n,r),p.crossVectors(f,m),p.normalize(),e.normal=p.clone()}function b(e,t,r){var n=r[e[0]],o=[],i=V(e,r);t.sort(function(e,t){return o[e.x/3]=void 0!==o[e.x/3]?o[e.x/3]:i.dot(l.subVectors(e,n)),o[t.x/3]=void 0!==o[t.x/3]?o[t.x/3]:i.dot(d.subVectors(t,n)),o[e.x/3]-o[t.x/3]});var s=t.length;for(1===s&&(o[t[0].x/3]=i.dot(l.subVectors(t[0],n)));s-- >0&&o[t[s].x/3]>0;);s+10&&(e.visiblePoints=t.splice(s+1))}function R(e,t){for(var r,n=g.length,o=[e],i=t.indexOf(e.visiblePoints.pop());n-- >0;)(r=g[n])!==e&&V(r,t).dot(x.subVectors(t[i],t[r[0]]))>0&&o.push(r);var s,a,u,c,m=n=o.length,f=1===n,l=[],d=0,p=[];if(1===o.length)l=[(r=o[0])[0],r[1],r[1],r[2],r[2],r[0]],v.indexOf(r)>-1&&v.splice(v.indexOf(r),1),r.visiblePoints&&(p=p.concat(r.visiblePoints)),g.splice(g.indexOf(r),1);else for(;n-- >0;){var y;for(v.indexOf(r=o[n])>-1&&v.splice(v.indexOf(r),1),r.visiblePoints&&(p=p.concat(r.visiblePoints)),g.splice(g.indexOf(r),1),cEdgeIndex=0;cEdgeIndex<3;){for(y=!1,m=o.length,u=r[cEdgeIndex],c=r[(cEdgeIndex+1)%3];m-- >0&&!y;)if(d=0,(s=o[m])!==r)for(;d<3&&!y;)a=d+1,y=s[d]===u&&s[a%3]===c||s[d]===c&&s[a%3]===u,d++;y&&!f||(l.push(u),l.push(c)),cEdgeIndex++}}n=0;for(var E,h=l.length/2;n=a?r.dot(r):t.dot(t)-s*s/a}}();return function(V){for(m=new THREE.Vector3,f=new THREE.Vector3,new THREE.Vector3,l=new THREE.Vector3,d=new THREE.Vector3,p=new THREE.Vector3,x=new THREE.Vector3,y=new THREE.Vector3,E=new THREE.Vector3,h=new THREE.Vector3,points=V.vertices,g=[],v=[],M=e=points.length,t=points.slice(0,6),w=0;M-- >0;)points[M].xt[1].x&&(t[1]=points[M]),points[M].y0;)for(n=M-1;n-- >0;)w<(r=t[M].distanceToSquared(t[n]))&&(w=r,o=t[M],i=t[n]);for(M=6,w=0;M-- >0;)r=H(o,i,t[M]),w0;)r=Math.abs(points[M].dot(u)-c),w0;)b(G[M],C,points),void 0!==G[M].visiblePoints&&v.push(G[M]),g.push(G[M]);!function(e){for(;v.length>0;)R(v.shift(),e)}(points);for(var P=g.length;P-- >0;)V.faces[P]=new THREE.Face3(g[P][2],g[P][1],g[P][0],g[P].normal);return V.normalsNeedUpdate=!0,V}}(),r=Math.PI/2,n={BOX:"Box",CYLINDER:"Cylinder",SPHERE:"Sphere",HULL:"ConvexPolyhedron",MESH:"Trimesh"},o=function(o,c){var m;if((c=c||{}).type===n.BOX)return s(o);if(c.type===n.CYLINDER)return function(t,n){var o,i,s,a=new THREE.Box3,u=["x","y","z"],c=n.cylinderAxis||"y",m=u.splice(u.indexOf(c),1)&&u;return a.setFromObject(t),isFinite(a.min.lengthSq())?(i=a.max[c]-a.min[c],s=.5*Math.max(a.max[m[0]]-a.min[m[0]],a.max[m[1]]-a.min[m[1]]),(o=new e.Cylinder(s,s,i,12))._type=e.Shape.types.CYLINDER,o.radiusTop=s,o.radiusBottom=s,o.height=i,o.numSegments=12,o.orientation=new e.Quaternion,o.orientation.setFromEuler("y"===c?r:0,"z"===c?r:0,0,"XYZ").normalize(),o):null}(o,c);if(c.type===n.SPHERE)return function(t,r){if(r.sphereRadius)return new e.Sphere(r.sphereRadius);var n=a(t);return n?(n.computeBoundingSphere(),new e.Sphere(n.boundingSphere.radius)):null}(o,c);if(c.type===n.HULL)return function(r){var n,o,i,s,u=a(r);if(!u||!u.vertices.length)return null;for(n=0;n2&&o.fromBufferGeometry(n[0].geometry):o=n[0].geometry.clone(),o.metadata=n[0].geometry.metadata,n[0].updateMatrixWorld(),n[0].matrixWorld.decompose(s,a,u),o.scale(u.x,u.y,u.z)}for(;r=n.pop();)if(r.updateMatrixWorld(),r.geometry.isBufferGeometry){if(r.geometry.attributes.position&&r.geometry.attributes.position.itemSize>2){var c=new THREE.Geometry;c.fromBufferGeometry(r.geometry),i.merge(c,r.matrixWorld),c.dispose()}}else i.merge(r.geometry,r.matrixWorld);return(t=new THREE.Matrix4).scale(e.scale),i.applyMatrix(t),i}function u(e){return e.attributes||(e=(new THREE.BufferGeometry).fromGeometry(e)),(e.attributes.position||{}).array||[]}o.Type=n,exports.threeToCannon=o},{"cannon-es":4}],7:[function(require,module,exports){var bundleFn=arguments[3],sources=arguments[4],cache=arguments[5],stringify=JSON.stringify;module.exports=function(fn,options){for(var wkey,cacheKeys=Object.keys(cache),i=0,l=cacheKeys.length;i{this.loadedEventFired=!0},{once:!0}),this.system.initialized&&this.loadedEventFired&&this.initBody()},initBody:function(){const pos=new THREE.Vector3,quat=new THREE.Quaternion;new THREE.Box3;return function(){const el=this.el,data=this.data;this.localScaling=new Ammo.btVector3;const obj=this.el.object3D;obj.getWorldPosition(pos),obj.getWorldQuaternion(quat),this.prevScale=new THREE.Vector3(1,1,1),this.prevNumChildShapes=0,this.msTransform=new Ammo.btTransform,this.msTransform.setIdentity(),this.rotation=new Ammo.btQuaternion(quat.x,quat.y,quat.z,quat.w),this.msTransform.getOrigin().setValue(pos.x,pos.y,pos.z),this.msTransform.setRotation(this.rotation),this.motionState=new Ammo.btDefaultMotionState(this.msTransform),this.localInertia=new Ammo.btVector3(0,0,0),this.compoundShape=new Ammo.btCompoundShape(!0),this.rbInfo=new Ammo.btRigidBodyConstructionInfo(data.mass,this.motionState,this.compoundShape,this.localInertia),this.body=new Ammo.btRigidBody(this.rbInfo),this.body.setActivationState(ACTIVATION_STATES.indexOf(data.activationState)+1),this.body.setSleepingThresholds(data.linearSleepingThreshold,data.angularSleepingThreshold),this.body.setDamping(data.linearDamping,data.angularDamping);const angularFactor=new Ammo.btVector3(data.angularFactor.x,data.angularFactor.y,data.angularFactor.z);this.body.setAngularFactor(angularFactor),Ammo.destroy(angularFactor);const gravity=new Ammo.btVector3(data.gravity.x,data.gravity.y,data.gravity.z);almostEqualsBtVector3(.001,gravity,this.system.driver.physicsWorld.getGravity())||(this.body.setGravity(gravity),this.body.setFlags(RIGID_BODY_FLAGS_DISABLE_WORLD_GRAVITY)),Ammo.destroy(gravity),this.updateCollisionFlags(),this.el.body=this.body,this.body.el=el,this.isLoaded=!0,this.el.emit("body-loaded",{body:this.el.body}),this._addToSystem()}}(),tick:function(){this.system.initialized&&!this.isLoaded&&this.loadedEventFired&&this.initBody()},_updateShapes:function(){const needsPolyhedralInitialization=[SHAPE.HULL,SHAPE.HACD,SHAPE.VHACD];return function(){let updated=!1;const obj=this.el.object3D;if(this.data.scaleAutoUpdate&&this.prevScale&&!almostEqualsVector3(.001,obj.scale,this.prevScale)&&(this.prevScale.copy(obj.scale),updated=!0,this.localScaling.setValue(this.prevScale.x,this.prevScale.y,this.prevScale.z),this.compoundShape.setLocalScaling(this.localScaling)),this.shapeComponentsChanged){this.shapeComponentsChanged=!1,updated=!0;for(let i=0;i0;){const collisionShape=this.collisionShapes.pop();collisionShape.destroy(),Ammo.destroy(collisionShape.localTransform)}}};module.exports.definition=AmmoShape,module.exports.Component=AFRAME.registerComponent("ammo-shape",AmmoShape)},{"../../constants":19,"three-to-ammo":5}],17:[function(require,module,exports){var CANNON=require("cannon-es"),Shape={schema:{shape:{default:"box",oneOf:["box","sphere","cylinder"]},offset:{type:"vec3",default:{x:0,y:0,z:0}},orientation:{type:"vec4",default:{x:0,y:0,z:0,w:1}},radius:{type:"number",default:1,if:{shape:["sphere"]}},halfExtents:{type:"vec3",default:{x:.5,y:.5,z:.5},if:{shape:["box"]}},radiusTop:{type:"number",default:1,if:{shape:["cylinder"]}},radiusBottom:{type:"number",default:1,if:{shape:["cylinder"]}},height:{type:"number",default:1,if:{shape:["cylinder"]}},numSegments:{type:"int",default:8,if:{shape:["cylinder"]}}},multiple:!0,init:function(){this.el.sceneEl.hasLoaded?this.initShape():this.el.sceneEl.addEventListener("loaded",this.initShape.bind(this))},initShape:function(){this.bodyEl=this.el;for(var bodyType=this._findType(this.bodyEl),data=this.data;!bodyType&&this.bodyEl.parentNode!=this.el.sceneEl;)this.bodyEl=this.bodyEl.parentNode,bodyType=this._findType(this.bodyEl);if(bodyType){var shape,offset,orientation,scale=new THREE.Vector3;switch(this.bodyEl.object3D.getWorldScale(scale),data.hasOwnProperty("offset")&&(offset=new CANNON.Vec3(data.offset.x*scale.x,data.offset.y*scale.y,data.offset.z*scale.z)),data.hasOwnProperty("orientation")&&(orientation=new CANNON.Quaternion).copy(data.orientation),data.shape){case"sphere":shape=new CANNON.Sphere(data.radius*scale.x);break;case"box":var halfExtents=new CANNON.Vec3(data.halfExtents.x*scale.x,data.halfExtents.y*scale.y,data.halfExtents.z*scale.z);shape=new CANNON.Box(halfExtents);break;case"cylinder":shape=new CANNON.Cylinder(data.radiusTop*scale.x,data.radiusBottom*scale.x,data.height*scale.y,data.numSegments);var quat=new CANNON.Quaternion;quat.setFromEuler(90*THREE.Math.DEG2RAD,0,0,"XYZ").normalize(),orientation.mult(quat,orientation);break;default:return void console.warn(data.shape+" shape not supported")}this.bodyEl.body?this.bodyEl.components[bodyType].addShape(shape,offset,orientation):this.bodyEl.addEventListener("body-loaded",function(){this.bodyEl.components[bodyType].addShape(shape,offset,orientation)},{once:!0})}else console.warn("body not found")},_findType:function(el){return el.hasAttribute("body")?"body":el.hasAttribute("dynamic-body")?"dynamic-body":el.hasAttribute("static-body")?"static-body":null},remove:function(){this.bodyEl.parentNode&&console.warn("removing shape component not currently supported")}};module.exports.definition=Shape,module.exports.Component=AFRAME.registerComponent("shape",Shape)},{"cannon-es":4}],18:[function(require,module,exports){var CANNON=require("cannon-es");module.exports=AFRAME.registerComponent("spring",{multiple:!0,schema:{target:{type:"selector"},restLength:{default:1,min:0},stiffness:{default:100,min:0},damping:{default:1,min:0},localAnchorA:{type:"vec3",default:{x:0,y:0,z:0}},localAnchorB:{type:"vec3",default:{x:0,y:0,z:0}}},init:function(){this.system=this.el.sceneEl.systems.physics,this.system.addComponent(this),this.isActive=!0,this.spring=null},update:function(oldData){var el=this.el,data=this.data;data.target?el.body&&data.target.body?(this.createSpring(),this.updateSpring(oldData)):(el.body?data.target:el).addEventListener("body-loaded",this.update.bind(this,{})):console.warn("Spring: invalid target specified.")},updateSpring:function(oldData){if(this.spring){var data=this.data,spring=this.spring;Object.keys(data).forEach(function(attr){if(data[attr]!==oldData[attr]){if("target"===attr)return void(spring.bodyB=data.target.body);spring[attr]=data[attr]}})}else console.warn("Spring: Component attempted to change spring before its created. No changes made.")},createSpring:function(){this.spring||(this.spring=new CANNON.Spring(this.el.body))},step:function(t,dt){return this.spring&&this.isActive?this.spring.applyForce():void 0},play:function(){this.isActive=!0},pause:function(){this.isActive=!1},remove:function(){this.spring&&delete this.spring,this.spring=null}})},{"cannon-es":4}],19:[function(require,module,exports){module.exports={GRAVITY:-9.8,MAX_INTERVAL:4/60,ITERATIONS:10,CONTACT_MATERIAL:{friction:.01,restitution:.3,contactEquationStiffness:1e8,contactEquationRelaxation:3,frictionEquationStiffness:1e8,frictionEquationRegularization:3},ACTIVATION_STATE:{ACTIVE_TAG:"active",ISLAND_SLEEPING:"islandSleeping",WANTS_DEACTIVATION:"wantsDeactivation",DISABLE_DEACTIVATION:"disableDeactivation",DISABLE_SIMULATION:"disableSimulation"},COLLISION_FLAG:{STATIC_OBJECT:1,KINEMATIC_OBJECT:2,NO_CONTACT_RESPONSE:4,CUSTOM_MATERIAL_CALLBACK:8,CHARACTER_OBJECT:16,DISABLE_VISUALIZE_OBJECT:32,DISABLE_SPU_COLLISION_PROCESSING:64},TYPE:{STATIC:"static",DYNAMIC:"dynamic",KINEMATIC:"kinematic"},SHAPE:{BOX:"box",CYLINDER:"cylinder",SPHERE:"sphere",CAPSULE:"capsule",CONE:"cone",HULL:"hull",HACD:"hacd",VHACD:"vhacd",MESH:"mesh",HEIGHTFIELD:"heightfield"},FIT:{ALL:"all",MANUAL:"manual"},CONSTRAINT:{LOCK:"lock",FIXED:"fixed",SPRING:"spring",SLIDER:"slider",HINGE:"hinge",CONE_TWIST:"coneTwist",POINT_TO_POINT:"pointToPoint"}}},{}],20:[function(require,module,exports){const Driver=require("./driver");"undefined"!=typeof window&&(window.AmmoModule=window.Ammo,window.Ammo=null);function AmmoDriver(){this.collisionConfiguration=null,this.dispatcher=null,this.broadphase=null,this.solver=null,this.physicsWorld=null,this.debugDrawer=null,this.els=new Map,this.eventListeners=[],this.collisions=new Map,this.collisionKeys=[],this.currentCollisions=new Map}AmmoDriver.prototype=new Driver,AmmoDriver.prototype.constructor=AmmoDriver,module.exports=AmmoDriver,AmmoDriver.prototype.init=function(worldConfig){return new Promise(resolve=>{AmmoModule().then(result=>{Ammo=result,this.epsilon=worldConfig.epsilon||1e-5,this.debugDrawMode=worldConfig.debugDrawMode||THREE.AmmoDebugConstants.NoDebug,this.maxSubSteps=worldConfig.maxSubSteps||4,this.fixedTimeStep=worldConfig.fixedTimeStep||1/60,this.collisionConfiguration=new Ammo.btDefaultCollisionConfiguration,this.dispatcher=new Ammo.btCollisionDispatcher(this.collisionConfiguration),this.broadphase=new Ammo.btDbvtBroadphase,this.solver=new Ammo.btSequentialImpulseConstraintSolver,this.physicsWorld=new Ammo.btDiscreteDynamicsWorld(this.dispatcher,this.broadphase,this.solver,this.collisionConfiguration),this.physicsWorld.setForceUpdateAllAabbs(!1),this.physicsWorld.setGravity(new Ammo.btVector3(0,worldConfig.hasOwnProperty("gravity")?worldConfig.gravity:-9.8,0)),this.physicsWorld.getSolverInfo().set_m_numIterations(worldConfig.solverIterations),resolve()})})},AmmoDriver.prototype.addBody=function(body,group,mask){this.physicsWorld.addRigidBody(body,group,mask),this.els.set(Ammo.getPointer(body),body.el)},AmmoDriver.prototype.removeBody=function(body){this.physicsWorld.removeRigidBody(body),this.removeEventListener(body);const bodyptr=Ammo.getPointer(body);this.els.delete(bodyptr),this.collisions.delete(bodyptr),this.collisionKeys.splice(this.collisionKeys.indexOf(bodyptr),1),this.currentCollisions.delete(bodyptr)},AmmoDriver.prototype.updateBody=function(body){this.els.has(Ammo.getPointer(body))&&this.physicsWorld.updateSingleAabb(body)},AmmoDriver.prototype.step=function(deltaTime){this.physicsWorld.stepSimulation(deltaTime,this.maxSubSteps,this.fixedTimeStep);const numManifolds=this.dispatcher.getNumManifolds();for(let i=0;i=0;j--){const body1ptr=body1ptrs[j];this.currentCollisions.get(body0ptr).has(body1ptr)||(-1!==this.eventListeners.indexOf(body0ptr)&&this.els.get(body0ptr).emit("collideend",{targetEl:this.els.get(body1ptr)}),-1!==this.eventListeners.indexOf(body1ptr)&&this.els.get(body1ptr).emit("collideend",{targetEl:this.els.get(body0ptr)}),body1ptrs.splice(j,1))}this.currentCollisions.get(body0ptr).clear()}this.debugDrawer&&this.debugDrawer.update()},AmmoDriver.prototype.addConstraint=function(constraint){this.physicsWorld.addConstraint(constraint,!1)},AmmoDriver.prototype.removeConstraint=function(constraint){this.physicsWorld.removeConstraint(constraint)},AmmoDriver.prototype.addEventListener=function(body){this.eventListeners.push(Ammo.getPointer(body))},AmmoDriver.prototype.removeEventListener=function(body){const ptr=Ammo.getPointer(body);-1!==this.eventListeners.indexOf(ptr)&&this.eventListeners.splice(this.eventListeners.indexOf(ptr),1)},AmmoDriver.prototype.destroy=function(){Ammo.destroy(this.collisionConfiguration),Ammo.destroy(this.dispatcher),Ammo.destroy(this.broadphase),Ammo.destroy(this.solver),Ammo.destroy(this.physicsWorld),Ammo.destroy(this.debugDrawer)},AmmoDriver.prototype.getDebugDrawer=function(scene,options){return this.debugDrawer||((options=options||{}).debugDrawMode=options.debugDrawMode||this.debugDrawMode,this.debugDrawer=new THREE.AmmoDebugDrawer(scene,this.physicsWorld,options)),this.debugDrawer}},{"./driver":21}],21:[function(require,module,exports){function Driver(){}function abstractMethod(){throw new Error("Method not implemented.")}module.exports=Driver,Driver.prototype.init=abstractMethod,Driver.prototype.step=abstractMethod,Driver.prototype.destroy=abstractMethod,Driver.prototype.addBody=abstractMethod,Driver.prototype.removeBody=abstractMethod,Driver.prototype.applyBodyMethod=abstractMethod,Driver.prototype.updateBodyProperties=abstractMethod,Driver.prototype.addMaterial=abstractMethod,Driver.prototype.addContactMaterial=abstractMethod,Driver.prototype.addConstraint=abstractMethod,Driver.prototype.removeConstraint=abstractMethod,Driver.prototype.getContacts=abstractMethod},{}],22:[function(require,module,exports){module.exports={INIT:"init",STEP:"step",ADD_BODY:"add-body",REMOVE_BODY:"remove-body",APPLY_BODY_METHOD:"apply-body-method",UPDATE_BODY_PROPERTIES:"update-body-properties",ADD_MATERIAL:"add-material",ADD_CONTACT_MATERIAL:"add-contact-material",ADD_CONSTRAINT:"add-constraint",REMOVE_CONSTRAINT:"remove-constraint",COLLIDE:"collide"}},{}],23:[function(require,module,exports){var CANNON=require("cannon-es"),Driver=require("./driver");function LocalDriver(){this.world=null,this.materials={},this.contactMaterial=null}LocalDriver.prototype=new Driver,LocalDriver.prototype.constructor=LocalDriver,module.exports=LocalDriver,LocalDriver.prototype.init=function(worldConfig){var world=new CANNON.World;world.quatNormalizeSkip=worldConfig.quatNormalizeSkip,world.quatNormalizeFast=worldConfig.quatNormalizeFast,world.solver.iterations=worldConfig.solverIterations,world.gravity.set(0,worldConfig.gravity,0),world.broadphase=new CANNON.NaiveBroadphase,this.world=world},LocalDriver.prototype.step=function(deltaMS){this.world.step(deltaMS)},LocalDriver.prototype.destroy=function(){delete this.world,delete this.contactMaterial,this.materials={}},LocalDriver.prototype.addBody=function(body){this.world.addBody(body)},LocalDriver.prototype.removeBody=function(body){this.world.removeBody(body)},LocalDriver.prototype.applyBodyMethod=function(body,methodName,args){body["__"+methodName].apply(body,args)},LocalDriver.prototype.updateBodyProperties=function(){},LocalDriver.prototype.getMaterial=function(name){return this.materials[name]},LocalDriver.prototype.addMaterial=function(materialConfig){this.materials[materialConfig.name]=new CANNON.Material(materialConfig),this.materials[materialConfig.name].name=materialConfig.name},LocalDriver.prototype.addContactMaterial=function(matName1,matName2,contactMaterialConfig){var mat1=this.materials[matName1],mat2=this.materials[matName2];this.contactMaterial=new CANNON.ContactMaterial(mat1,mat2,contactMaterialConfig),this.world.addContactMaterial(this.contactMaterial)},LocalDriver.prototype.addConstraint=function(constraint){constraint.type||(constraint instanceof CANNON.LockConstraint?constraint.type="LockConstraint":constraint instanceof CANNON.DistanceConstraint?constraint.type="DistanceConstraint":constraint instanceof CANNON.HingeConstraint?constraint.type="HingeConstraint":constraint instanceof CANNON.ConeTwistConstraint?constraint.type="ConeTwistConstraint":constraint instanceof CANNON.PointToPointConstraint&&(constraint.type="PointToPointConstraint")),this.world.addConstraint(constraint)},LocalDriver.prototype.removeConstraint=function(constraint){this.world.removeConstraint(constraint)},LocalDriver.prototype.getContacts=function(){return this.world.contacts}},{"./driver":21,"cannon-es":4}],24:[function(require,module,exports){var Driver=require("./driver");function NetworkDriver(){throw new Error("[NetworkDriver] Driver not implemented.")}NetworkDriver.prototype=new Driver,NetworkDriver.prototype.constructor=NetworkDriver,module.exports=NetworkDriver},{"./driver":21}],25:[function(require,module,exports){function EventTarget(){this.listeners=[]}module.exports=function(worker){var targetA=new EventTarget,targetB=new EventTarget;return targetA.setTarget(targetB),targetB.setTarget(targetA),worker(targetA),targetB},EventTarget.prototype.setTarget=function(target){this.target=target},EventTarget.prototype.addEventListener=function(type,fn){this.listeners.push(fn)},EventTarget.prototype.dispatchEvent=function(type,event){for(var i=0;ithis.frameDelay;)this.frameBuffer.shift(),prevFrame=this.frameBuffer[0],nextFrame=this.frameBuffer[1];if(prevFrame&&nextFrame){var mix=(timestamp-prevFrame.timestamp)/this.frameDelay;for(var id in mix=(mix-(1-1/this.interpBufferSize))*this.interpBufferSize,prevFrame.bodies)prevFrame.bodies.hasOwnProperty(id)&&nextFrame.bodies.hasOwnProperty(id)&&protocol.deserializeInterpBodyUpdate(prevFrame.bodies[id],nextFrame.bodies[id],this.bodies[id],mix)}}},WorkerDriver.prototype.destroy=function(){this.worker.terminate(),delete this.worker},WorkerDriver.prototype._onMessage=function(event){if(event.data.type===Event.STEP){var bodies=event.data.bodies;if(this.contacts=event.data.contacts,this.interpolate)this.frameBuffer.push({timestamp:performance.now(),bodies:bodies});else for(var id in bodies)bodies.hasOwnProperty(id)&&protocol.deserializeBodyUpdate(bodies[id],this.bodies[id])}else{if(event.data.type!==Event.COLLIDE)throw new Error("[WorkerDriver] Unexpected message type.");var body=this.bodies[event.data.bodyID],target=this.bodies[event.data.targetID],contact=protocol.deserializeContact(event.data.contact,this.bodies);if(!body._listeners||!body._listeners.collide)return;for(var i=0;i=1)return b;var x=a[0],y=a[1],z=a[2],w=a[3],cosHalfTheta=w*b[3]+x*b[0]+y*b[1]+z*b[2];if(!(cosHalfTheta<0))return b;if((a=a.slice())[3]=-b[3],a[0]=-b[0],a[1]=-b[1],a[2]=-b[2],(cosHalfTheta=-cosHalfTheta)>=1)return a[3]=w,a[0]=x,a[1]=y,a[2]=z,this;var sinHalfTheta=Math.sqrt(1-cosHalfTheta*cosHalfTheta);if(Math.abs(sinHalfTheta)<.001)return a[3]=.5*(w+a[3]),a[0]=.5*(x+a[0]),a[1]=.5*(y+a[1]),a[2]=.5*(z+a[2]),this;var halfTheta=Math.atan2(sinHalfTheta,cosHalfTheta),ratioA=Math.sin((1-t)*halfTheta)/sinHalfTheta,ratioB=Math.sin(t*halfTheta)/sinHalfTheta;return a[3]=w*ratioA+a[3]*ratioB,a[0]=x*ratioA+a[0]*ratioB,a[1]=y*ratioA+a[1]*ratioB,a[2]=z*ratioA+a[2]*ratioB,a}},{}],30:[function(require,module,exports){var CANNON=require("cannon-es"),mathUtils=require("./math"),ID="__id";module.exports.ID=ID;var nextID={};function serializeShape(shape){var shapeMsg={type:shape.type};if(shape.type===CANNON.Shape.types.BOX)shapeMsg.halfExtents=serializeVec3(shape.halfExtents);else if(shape.type===CANNON.Shape.types.SPHERE)shapeMsg.radius=shape.radius;else{if(shape._type!==CANNON.Shape.types.CYLINDER)throw new Error("Unimplemented shape type: %s",shape.type);shapeMsg.type=CANNON.Shape.types.CYLINDER,shapeMsg.radiusTop=shape.radiusTop,shapeMsg.radiusBottom=shape.radiusBottom,shapeMsg.height=shape.height,shapeMsg.numSegments=shape.numSegments}return shapeMsg}function deserializeShape(message){var shape;if(message.type===CANNON.Shape.types.BOX)shape=new CANNON.Box(deserializeVec3(message.halfExtents));else if(message.type===CANNON.Shape.types.SPHERE)shape=new CANNON.Sphere(message.radius);else{if(message.type!==CANNON.Shape.types.CYLINDER)throw new Error("Unimplemented shape type: %s",message.type);(shape=new CANNON.Cylinder(message.radiusTop,message.radiusBottom,message.height,message.numSegments))._type=CANNON.Shape.types.CYLINDER}return shape}function serializeVec3(vec3){return vec3.toArray()}function deserializeVec3(message){return new CANNON.Vec3(message[0],message[1],message[2])}function serializeQuaternion(quat){return quat.toArray()}function deserializeQuaternion(message){return new CANNON.Quaternion(message[0],message[1],message[2],message[3])}module.exports.assignID=function(prefix,object){object[ID]||(nextID[prefix]=nextID[prefix]||1,object[ID]=prefix+"_"+nextID[prefix]++)},module.exports.serializeBody=function(body){return{shapes:body.shapes.map(serializeShape),shapeOffsets:body.shapeOffsets.map(serializeVec3),shapeOrientations:body.shapeOrientations.map(serializeQuaternion),position:serializeVec3(body.position),quaternion:body.quaternion.toArray(),velocity:serializeVec3(body.velocity),angularVelocity:serializeVec3(body.angularVelocity),id:body[ID],mass:body.mass,linearDamping:body.linearDamping,angularDamping:body.angularDamping,fixedRotation:body.fixedRotation,allowSleep:body.allowSleep,sleepSpeedLimit:body.sleepSpeedLimit,sleepTimeLimit:body.sleepTimeLimit}},module.exports.deserializeBodyUpdate=function(message,body){return body.position.set(message.position[0],message.position[1],message.position[2]),body.quaternion.set(message.quaternion[0],message.quaternion[1],message.quaternion[2],message.quaternion[3]),body.velocity.set(message.velocity[0],message.velocity[1],message.velocity[2]),body.angularVelocity.set(message.angularVelocity[0],message.angularVelocity[1],message.angularVelocity[2]),body.linearDamping=message.linearDamping,body.angularDamping=message.angularDamping,body.fixedRotation=message.fixedRotation,body.allowSleep=message.allowSleep,body.sleepSpeedLimit=message.sleepSpeedLimit,body.sleepTimeLimit=message.sleepTimeLimit,body.mass!==message.mass&&(body.mass=message.mass,body.updateMassProperties()),body},module.exports.deserializeInterpBodyUpdate=function(message1,message2,body,mix){var weight1=1-mix,weight2=mix;body.position.set(message1.position[0]*weight1+message2.position[0]*weight2,message1.position[1]*weight1+message2.position[1]*weight2,message1.position[2]*weight1+message2.position[2]*weight2);var quaternion=mathUtils.slerp(message1.quaternion,message2.quaternion,mix);return body.quaternion.set(quaternion[0],quaternion[1],quaternion[2],quaternion[3]),body.velocity.set(message1.velocity[0]*weight1+message2.velocity[0]*weight2,message1.velocity[1]*weight1+message2.velocity[1]*weight2,message1.velocity[2]*weight1+message2.velocity[2]*weight2),body.angularVelocity.set(message1.angularVelocity[0]*weight1+message2.angularVelocity[0]*weight2,message1.angularVelocity[1]*weight1+message2.angularVelocity[1]*weight2,message1.angularVelocity[2]*weight1+message2.angularVelocity[2]*weight2),body.linearDamping=message2.linearDamping,body.angularDamping=message2.angularDamping,body.fixedRotation=message2.fixedRotation,body.allowSleep=message2.allowSleep,body.sleepSpeedLimit=message2.sleepSpeedLimit,body.sleepTimeLimit=message2.sleepTimeLimit,body.mass!==message2.mass&&(body.mass=message2.mass,body.updateMassProperties()),body},module.exports.deserializeBody=function(message){for(var shapeMsg,body=new CANNON.Body({mass:message.mass,position:deserializeVec3(message.position),quaternion:deserializeQuaternion(message.quaternion),velocity:deserializeVec3(message.velocity),angularVelocity:deserializeVec3(message.angularVelocity),linearDamping:message.linearDamping,angularDamping:message.angularDamping,fixedRotation:message.fixedRotation,allowSleep:message.allowSleep,sleepSpeedLimit:message.sleepSpeedLimit,sleepTimeLimit:message.sleepTimeLimit}),i=0;shapeMsg=message.shapes[i];i++)body.addShape(deserializeShape(shapeMsg),deserializeVec3(message.shapeOffsets[i]),deserializeQuaternion(message.shapeOrientations[i]));return body[ID]=message.id,body},module.exports.serializeShape=serializeShape,module.exports.deserializeShape=deserializeShape,module.exports.serializeConstraint=function(constraint){var message={id:constraint[ID],type:constraint.type,maxForce:constraint.maxForce,bodyA:constraint.bodyA[ID],bodyB:constraint.bodyB[ID]};switch(constraint.type){case"LockConstraint":break;case"DistanceConstraint":message.distance=constraint.distance;break;case"HingeConstraint":case"ConeTwistConstraint":message.axisA=serializeVec3(constraint.axisA),message.axisB=serializeVec3(constraint.axisB),message.pivotA=serializeVec3(constraint.pivotA),message.pivotB=serializeVec3(constraint.pivotB);break;case"PointToPointConstraint":message.pivotA=serializeVec3(constraint.pivotA),message.pivotB=serializeVec3(constraint.pivotB);break;default:throw new Error("Unexpected constraint type: "+constraint.type+'. You may need to manually set `constraint.type = "FooConstraint";`.')}return message},module.exports.deserializeConstraint=function(message,bodies){var constraint,TypedConstraint=CANNON[message.type],bodyA=bodies[message.bodyA],bodyB=bodies[message.bodyB];switch(message.type){case"LockConstraint":constraint=new CANNON.LockConstraint(bodyA,bodyB,message);break;case"DistanceConstraint":constraint=new CANNON.DistanceConstraint(bodyA,bodyB,message.distance,message.maxForce);break;case"HingeConstraint":case"ConeTwistConstraint":constraint=new TypedConstraint(bodyA,bodyB,{pivotA:deserializeVec3(message.pivotA),pivotB:deserializeVec3(message.pivotB),axisA:deserializeVec3(message.axisA),axisB:deserializeVec3(message.axisB),maxForce:message.maxForce});break;case"PointToPointConstraint":constraint=new CANNON.PointToPointConstraint(bodyA,deserializeVec3(message.pivotA),bodyB,deserializeVec3(message.pivotB),message.maxForce);break;default:throw new Error("Unexpected constraint type: "+message.type)}return constraint[ID]=message.id,constraint},module.exports.serializeContact=function(contact){return{bi:contact.bi[ID],bj:contact.bj[ID],ni:serializeVec3(contact.ni),ri:serializeVec3(contact.ri),rj:serializeVec3(contact.rj)}},module.exports.deserializeContact=function(message,bodies){return{bi:bodies[message.bi],bj:bodies[message.bj],ni:deserializeVec3(message.ni),ri:deserializeVec3(message.ri),rj:deserializeVec3(message.rj)}},module.exports.serializeVec3=serializeVec3,module.exports.deserializeVec3=deserializeVec3,module.exports.serializeQuaternion=serializeQuaternion,module.exports.deserializeQuaternion=deserializeQuaternion},{"./math":29,"cannon-es":4}]},{},[1]);
\ No newline at end of file
diff --git a/js/anvro-122.js b/js/anvro-123.js
similarity index 93%
rename from js/anvro-122.js
rename to js/anvro-123.js
index 94c95f3..8298945 100644
--- a/js/anvro-122.js
+++ b/js/anvro-123.js
@@ -1,53 +1,37 @@
-console.warn = console.error = () => {}; // Suppresses Three.js warnings. Remove to debug
+console.warn = console.error = () => {};
+// Suppresses Three.js warnings. Remove to debug
AFRAME.registerComponent('table-wait', {
init: function () {
var tablename = this.el.id;
var tableitems = sceneEl.querySelectorAll('.'+tablename+'obj');
- this.el.addEventListener('body-loaded', () => { // Wait for model to load.
+ this.el.addEventListener('body-loaded', () => { // Wait for table model to load.
setTimeout(function(){
if (AFRAME.utils.device.checkHeadsetConnected() === true) {
for (let each of tableitems) {
each.removeAttribute('static-body');
- each.setAttribute('dynamic-body', {shape: 'box', mass: 3});
+ each.setAttribute('dynamic-body', {shape: 'box', mass: 2});
+ console.log(each + " is dynamic");
+
}}
- }, 200);});
+ }, 5000);});
}});
-// PC Look Preference Switcher
-AFRAME.registerComponent("look-switch", {
- init: function() {
- var sceneEl = this.el.sceneEl;
- var canvasEl = sceneEl.canvas;
- var camera = document.querySelector('#camera');
- var PCmode = 0;
- window.addEventListener("keydown", function(e){ // Mouselook toggle
- if(e.keyCode === 77 && PCmode == 0) { // Swipe to FPS
- camera.setAttribute('look-controls', {enabled: false});
- camera.setAttribute('fps-look-controls', 'userHeight', 0);
- document.querySelector('#SMH-PC1').object3D.visible = false;
- document.querySelector('#SMH-PC2').object3D.visible = true;
- document.querySelector('#GL-PC1').object3D.visible = false;
- document.querySelector('#GL-PC2').object3D.visible = true;
- document.querySelector('#crosshair').object3D.visible = true;
- PCmode = 1;
- } else if (e.keyCode === 77 && PCmode == 1) { // FPS to swipe
- camera.removeAttribute('fps-look-controls');
- camera.setAttribute('look-controls', {enabled: true});
- canvasEl.onclick = null; // Removes FPS components taking mouse on click
- document.exitPointerLock();
- document.querySelector('#SMH-PC1').object3D.visible = true;
- document.querySelector('#SMH-PC2').object3D.visible = false;
- document.querySelector('#GL-PC1').object3D.visible = true;
- document.querySelector('#GL-PC2').object3D.visible = false;
-
- document.querySelector('#crosshair').object3D.visible = false;
- PCmode = 0;
-
+AFRAME.registerComponent('all-wait', { // Waits for static physics objects to load then applies physics setting
+ init: function () {
+ var static = sceneEl.querySelectorAll('.static');
+ for (let each of static) {
+ each.addEventListener('model-loaded', () => { // Wait for model to load.
+ setTimeout(function(){
+ if (AFRAME.utils.device.checkHeadsetConnected() === true) {
+ each.removeAttribute('static-body');
+ each.setAttribute('static-body', {shape: 'box'});
+ console.log(each);
}
- });
- }
- })
+ }, 1000);});
+ }
+}});
+
AFRAME.registerComponent('device-set', { // Device-specific settings
init: function() {
@@ -69,7 +53,7 @@ AFRAME.registerComponent('device-set', { // Device-specific settings
for (let each of grabbable) {
each.removeAttribute('dynamic-body');
each.removeAttribute('grabbable');
- each.setAttribute('static-body');
+ each.setAttribute('static-body', {shape: 'box'});
each.object3D.position.y += 0.245;
}
for (let each of standup) {
@@ -80,7 +64,6 @@ AFRAME.registerComponent('device-set', { // Device-specific settings
console.log('VR detected');
document.querySelector('#GL-VR').object3D.visible = true;
document.querySelector('#SMH-VR').object3D.visible = true;
- rig.removeAttribute('movement-controls'); // Remove non-working controls
} else if (AFRAME.utils.device.checkHeadsetConnected() === false) { // PC Mode
console.log('PC detected');
document.querySelector('#GL-PC1').object3D.visible = true;
@@ -89,11 +72,10 @@ AFRAME.registerComponent('device-set', { // Device-specific settings
for (let each of grabbable) {
each.removeAttribute('dynamic-body');
each.removeAttribute('grabbable');
- each.setAttribute('static-body');
+ each.setAttribute('static-body', {shape: 'box'});
each.object3D.position.y +=0.25;
}
for (let each of tablestand) {
- let poss = each.getAttribute('position');
each.object3D.position.y += 0.25;
}
for (let each of standup) { // Stands up small objects
diff --git a/js/simple-navmesh-constraint.js b/js/simple-navmesh-constraint.js
new file mode 100644
index 0000000..d03a4a0
--- /dev/null
+++ b/js/simple-navmesh-constraint.js
@@ -0,0 +1,129 @@
+/* global AFRAME, THREE */
+
+AFRAME.registerComponent('simple-navmesh-constraint', {
+ schema: {
+ enabled: {
+ default: true
+ },
+ navmesh: {
+ default: ''
+ },
+ fall: {
+ default: 0.5
+ },
+ height: {
+ default: 1.6
+ },
+ exclude: {
+ default: ''
+ },
+ xzOrigin: {
+ default: ''
+ }
+ },
+
+ update: function () {
+ this.lastPosition = null;
+ this.excludes = this.data.exclude ? Array.from(document.querySelectorAll(this.data.exclude)):[];
+ const els = Array.from(document.querySelectorAll(this.data.navmesh));
+ if (els === null) {
+ console.warn('simple-navmesh-constraint: Did not match any elements');
+ this.objects = [];
+ } else {
+ this.objects = els.map(el => el.object3D).concat(this.excludes.map(el => el.object3D));
+ }
+ this.xzOrigin = this.data.xzOrigin ? this.el.querySelector(this.data.xzOrigin) : this.el;
+ },
+
+ tick: (function () {
+ const nextPosition = new THREE.Vector3();
+ const tempVec = new THREE.Vector3();
+ const scanPattern = [
+ [0,1], // Default the next location
+ [0,0.5], // Check that the path to that location was fine
+ [30,0.4], // A little to the side shorter range
+ [-30,0.4], // A little to the side shorter range
+ [60,0.2], // Moderately to the side short range
+ [-60,0.2], // Moderately to the side short range
+ [80,0.06], // Perpendicular very short range
+ [-80,0.06], // Perpendicular very short range
+ ];
+ const down = new THREE.Vector3(0,-1,0);
+ const raycaster = new THREE.Raycaster();
+ const gravity = -1;
+ const maxYVelocity = 0.5;
+ const results = [];
+ let yVel = 0;
+ let firstTry = true;
+
+ return function tick(time, delta) {
+ if (this.data.enabled === false) return;
+ if (this.lastPosition === null) {
+ firstTry = true;
+ this.lastPosition = new THREE.Vector3();
+ this.xzOrigin.object3D.getWorldPosition(this.lastPosition);
+ if (this.data.xzOrigin) this.lastPosition.y -= this.xzOrigin.object3D.position.y;
+ }
+
+ const el = this.el;
+ if (this.objects.length === 0) return;
+
+ this.xzOrigin.object3D.getWorldPosition(nextPosition);
+ if (this.data.xzOrigin) nextPosition.y -= this.xzOrigin.object3D.position.y;
+ if (nextPosition.distanceTo(this.lastPosition) <= 0.01) return;
+
+ let didHit = false;
+ // So that it does not get stuck it takes as few samples around the user and finds the most appropriate
+ scanPatternLoop:
+ for (const [angle, distance] of scanPattern) {
+ tempVec.subVectors(nextPosition, this.lastPosition);
+ tempVec.applyAxisAngle(down, angle*Math.PI/180);
+ tempVec.multiplyScalar(distance);
+ tempVec.add(this.lastPosition);
+ tempVec.y += maxYVelocity;
+ tempVec.y -= this.data.height;
+ raycaster.set(tempVec, down);
+ raycaster.far = this.data.fall > 0 ? this.data.fall + maxYVelocity : Infinity;
+ raycaster.intersectObjects(this.objects, true, results);
+
+ if (results.length) {
+ // If it hit something we want to avoid then ignore it and stop looking
+ for (const result of results) {
+ if(this.excludes.includes(result.object.el)) {
+ results.splice(0);
+ continue scanPatternLoop;
+ }
+ }
+ const hitPos = results[0].point;
+ results.splice(0);
+ hitPos.y += this.data.height;
+ if (nextPosition.y - (hitPos.y - yVel*2) > 0.01) {
+ yVel += Math.max(gravity * delta * 0.001, -maxYVelocity);
+ hitPos.y = nextPosition.y + yVel;
+ } else {
+ yVel = 0;
+ }
+ tempVec.copy(hitPos);
+ this.xzOrigin.object3D.parent.worldToLocal(tempVec);
+ tempVec.sub(this.xzOrigin.object3D.position);
+ if (this.data.xzOrigin) tempVec.y += this.xzOrigin.object3D.position.y;
+ this.el.object3D.position.add(tempVec);
+
+ this.lastPosition.copy(hitPos);
+ didHit = true;
+ break;
+ }
+
+ }
+
+ if (didHit) {
+ firstTry = false;
+ }
+
+ if (!firstTry && !didHit) {
+ this.el.object3D.position.copy(this.lastPosition);
+ this.el.object3D.parent.worldToLocal(this.el.object3D.position);
+ }
+ }
+ }())
+});
diff --git a/js/src/controls/README.md b/js/src/controls/README.md
deleted file mode 100644
index 54b9f77..0000000
--- a/js/src/controls/README.md
+++ /dev/null
@@ -1,111 +0,0 @@
-# Controls
-
-Extensible movement/rotation/hotkey controls, with support for a variety of input devices.
-
-- **movement-controls**: Collection of locomotion controls, which can switch between input devices as they become active. Automatically includes the following components:
- + **keyboard-controls**: WASD + arrow controls for movement, and more.
- + **touch-controls**: Touch screen (or Cardboard button) to move forward.
- + **gamepad-controls**: Gamepad-based rotation and movement.
- + **trackpad-controls**: Trackpad-based movement.
-- **checkpoint-controls**: Move to checkpoints created with the `checkpoint` component. *Not included by default with `movement-controls`, but may be added as shown in examples.*
-
-## Usage
-
-The `movement-controls` component requires the use of a camera "rig" wrapping the camera element. The rig may be assigned any position within your scene, and should be placed at ground level. The camera should only have height offset (used for devices without positional tracking) such as `0 1.6 0`.
-
-Basic movement:
-
-```html
-
-
-
-```
-
-With checkpoints, and other input methods disabled:
-
-```html
-
-
-
-
-```
-
-With navigation mesh:
-
-```html
-
-
-
-
-```
-
-With physics-based movement.
-
-> **WARNING** *Using physics for movement is unstable and performs poorly. When preventing players from passing through obstacles, use a navigation mesh instead whenever possible.*
-
-```html
-
-
-
-```
-
-## Options
-
-| Property | Default | Description |
-|--------------------|---------|-------------|
-| enabled | true | Enables/disables movement controls. |
-| controls | gamepad, keyboard, touch | Ordered list of controls to be injected. |
-| speed | 0.3 | Movement speed. |
-| fly | false | Whether vertical movement is enabled. |
-| constrainToNavMesh | false | Whether to use navigation system to clamp movement. |
-| camera | [camera] | Camera element used for heading of the camera rig. |
-
-## Customizing movement-controls
-
-To implement your custom controls, define a component and override one or more methods:
-
-| Method | Type | Required |
-|----------------------------------------------------|----------|----------|
-| isVelocityActive() : boolean | Movement | Yes |
-| getVelocityDelta(deltaMS : number) : THREE.Vector3 | Movement | No |
-| getPositionDelta(deltaMS : number) : THREE.Vector3 | Movement | No |
-
-Example:
-
-```js
-AFRAME.registerComponent('custom-controls', {
- isVelocityActive: function () {
- return Math.random() < 0.25;
- },
- getPositionDelta: function () {
- return new THREE.Vector3(1, 0, 0);
- }
-});
-```
-
-## Other Controls
-
-I've written standalone components for several other control components. These do not work with `movement-controls`, and are older and less well maintained.
-
-- [gamepad-controls](https://github.com/donmccurdy/aframe-gamepad-controls): A more advanced standalone gamepad controller than the version in this package.
-- [keyboard-controls](https://github.com/donmccurdy/aframe-keyboard-controls): A more advanced standalone keyboard controller than the version in this package.
-
-## Mobile + Desktop Input Devices
-
-Connect input devices from your desktop to your mobile phone with WebRTC, using [ProxyControls.js](https://proxy-controls.donmccurdy.com).
-
-## Mobile Gamepad Support
-
-See my [separate overview of gamepad support](https://gist.github.com/donmccurdy/cf336a8b88ba0f10991d4aab936cc28b).
diff --git a/js/src/controls/checkpoint-controls.js b/js/src/controls/checkpoint-controls.js
deleted file mode 100644
index c8a5266..0000000
--- a/js/src/controls/checkpoint-controls.js
+++ /dev/null
@@ -1,86 +0,0 @@
-const EPS = 0.1;
-
-module.exports = AFRAME.registerComponent('checkpoint-controls', {
- schema: {
- enabled: {default: true},
- mode: {default: 'teleport', oneOf: ['teleport', 'animate']},
- animateSpeed: {default: 3.0}
- },
-
- init: function () {
- this.active = true;
- this.checkpoint = null;
-
- this.isNavMeshConstrained = false;
-
- this.offset = new THREE.Vector3();
- this.position = new THREE.Vector3();
- this.targetPosition = new THREE.Vector3();
- },
-
- play: function () { this.active = true; },
- pause: function () { this.active = false; },
-
- setCheckpoint: function (checkpoint) {
- const el = this.el;
-
- if (!this.active) return;
- if (this.checkpoint === checkpoint) return;
-
- if (this.checkpoint) {
- el.emit('navigation-end', {checkpoint: this.checkpoint});
- }
-
- this.checkpoint = checkpoint;
- this.sync();
-
- // Ignore new checkpoint if we're already there.
- if (this.position.distanceTo(this.targetPosition) < EPS) {
- this.checkpoint = null;
- return;
- }
-
- el.emit('navigation-start', {checkpoint: checkpoint});
-
- if (this.data.mode === 'teleport') {
- this.el.setAttribute('position', this.targetPosition);
- this.checkpoint = null;
- el.emit('navigation-end', {checkpoint: checkpoint});
- el.components['movement-controls'].updateNavLocation();
- }
- },
-
- isVelocityActive: function () {
- return !!(this.active && this.checkpoint);
- },
-
- getVelocity: function () {
- if (!this.active) return;
-
- const data = this.data;
- const offset = this.offset;
- const position = this.position;
- const targetPosition = this.targetPosition;
- const checkpoint = this.checkpoint;
-
- this.sync();
- if (position.distanceTo(targetPosition) < EPS) {
- this.checkpoint = null;
- this.el.emit('navigation-end', {checkpoint: checkpoint});
- return offset.set(0, 0, 0);
- }
- offset.setLength(data.animateSpeed);
- return offset;
- },
-
- sync: function () {
- const offset = this.offset;
- const position = this.position;
- const targetPosition = this.targetPosition;
-
- position.copy(this.el.getAttribute('position'));
- this.checkpoint.object3D.getWorldPosition(targetPosition);
- targetPosition.add(this.checkpoint.components.checkpoint.getOffset());
- offset.copy(targetPosition).sub(position);
- }
-});
diff --git a/js/src/controls/index.js b/js/src/controls/index.js
deleted file mode 100644
index eee89d3..0000000
--- a/js/src/controls/index.js
+++ /dev/null
@@ -1,6 +0,0 @@
-require('./checkpoint-controls');
-require('./gamepad-controls');
-require('./keyboard-controls');
-require('./touch-controls');
-require('./movement-controls');
-require('./trackpad-controls');
diff --git a/js/src/controls/keyboard-controls.js b/js/src/controls/keyboard-controls.js
deleted file mode 100644
index 281fd0e..0000000
--- a/js/src/controls/keyboard-controls.js
+++ /dev/null
@@ -1,163 +0,0 @@
-require('../../lib/keyboard.polyfill');
-
-const MAX_DELTA = 0.2,
- PROXY_FLAG = '__keyboard-controls-proxy';
-
-const KeyboardEvent = window.KeyboardEvent;
-
-/**
- * Keyboard Controls component.
- *
- * Stripped-down version of: https://github.com/donmccurdy/aframe-keyboard-controls
- *
- * Bind keyboard events to components, or control your entities with the WASD keys.
- *
- * Why use KeyboardEvent.code? "This is set to a string representing the key that was pressed to
- * generate the KeyboardEvent, without taking the current keyboard layout (e.g., QWERTY vs.
- * Dvorak), locale (e.g., English vs. French), or any modifier keys into account. This is useful
- * when you care about which physical key was pressed, rather thanwhich character it corresponds
- * to. For example, if you’re a writing a game, you might want a certain set of keys to move the
- * player in different directions, and that mapping should ideally be independent of keyboard
- * layout. See: https://developers.google.com/web/updates/2016/04/keyboardevent-keys-codes
- *
- * @namespace wasd-controls
- * keys the entity moves and if you release it will stop. Easing simulates friction.
- * to the entity when pressing the keys.
- * @param {bool} [enabled=true] - To completely enable or disable the controls
- */
-module.exports = AFRAME.registerComponent('keyboard-controls', {
- schema: {
- enabled: { default: true },
- debug: { default: false }
- },
-
- init: function () {
- this.dVelocity = new THREE.Vector3();
- this.localKeys = {};
- this.listeners = {
- keydown: this.onKeyDown.bind(this),
- keyup: this.onKeyUp.bind(this),
- blur: this.onBlur.bind(this),
- onContextMenu: this.onContextMenu.bind(this),
- };
- this.attachEventListeners();
- },
-
- /*******************************************************************
- * Movement
- */
-
- isVelocityActive: function () {
- return this.data.enabled && !!Object.keys(this.getKeys()).length;
- },
-
- getVelocityDelta: function () {
- const data = this.data,
- keys = this.getKeys();
-
- this.dVelocity.set(0, 0, 0);
- if (data.enabled) {
- if (keys.KeyW || keys.ArrowUp) { this.dVelocity.z -= 1; }
- if (keys.KeyA || keys.ArrowLeft) { this.dVelocity.x -= 1; }
- if (keys.KeyS || keys.ArrowDown) { this.dVelocity.z += 1; }
- if (keys.KeyD || keys.ArrowRight) { this.dVelocity.x += 1; }
- }
-
- return this.dVelocity.clone();
- },
-
- /*******************************************************************
- * Events
- */
-
- play: function () {
- this.attachEventListeners();
- },
-
- pause: function () {
- this.removeEventListeners();
- },
-
- remove: function () {
- this.pause();
- },
-
- attachEventListeners: function () {
- window.oncontextmenu = this.listeners.onContextMenu;
- window.addEventListener("keydown", this.listeners.keydown, false);
- window.addEventListener("keyup", this.listeners.keyup, false);
- window.addEventListener("blur", this.listeners.blur, false);
- },
-
- onContextMenu: function () {
- for (let code in this.localKeys) {
- if (this.localKeys.hasOwnProperty(code)) {
- delete this.localKeys[code];
- }
- }
- },
-
- removeEventListeners: function () {
- window.removeEventListener('keydown', this.listeners.keydown);
- window.removeEventListener('keyup', this.listeners.keyup);
- window.removeEventListener('blur', this.listeners.blur);
- },
-
- onKeyDown: function (event) {
- if (AFRAME.utils.shouldCaptureKeyEvent(event)) {
- this.localKeys[event.code] = true;
- this.emit(event);
- }
- },
-
- onKeyUp: function (event) {
- if (AFRAME.utils.shouldCaptureKeyEvent(event)) {
- delete this.localKeys[event.code];
- this.emit(event);
- }
- },
-
- onBlur: function () {
- for (let code in this.localKeys) {
- if (this.localKeys.hasOwnProperty(code)) {
- delete this.localKeys[code];
- }
- }
- },
-
- emit: function (event) {
- // TODO - keydown only initially?
- // TODO - where the f is the spacebar
-
- // Emit original event.
- if (PROXY_FLAG in event) {
- // TODO - Method never triggered.
- this.el.emit(event.type, event);
- }
-
- // Emit convenience event, identifying key.
- this.el.emit(event.type + ':' + event.code, new KeyboardEvent(event.type, event));
- if (this.data.debug) console.log(event.type + ':' + event.code);
- },
-
- /*******************************************************************
- * Accessors
- */
-
- isPressed: function (code) {
- return code in this.getKeys();
- },
-
- getKeys: function () {
- if (this.isProxied()) {
- return this.el.sceneEl.components['proxy-controls'].getKeyboard();
- }
- return this.localKeys;
- },
-
- isProxied: function () {
- const proxyControls = this.el.sceneEl.components['proxy-controls'];
- return proxyControls && proxyControls.isConnected();
- }
-
-});
diff --git a/js/src/controls/movement-controls.js b/js/src/controls/movement-controls.js
deleted file mode 100644
index 519a292..0000000
--- a/js/src/controls/movement-controls.js
+++ /dev/null
@@ -1,210 +0,0 @@
-/**
- * Movement Controls
- *
- * @author Don McCurdy
- */
-
-const COMPONENT_SUFFIX = '-controls',
- MAX_DELTA = 0.2, // ms
- EPS = 10e-6;
-
-module.exports = AFRAME.registerComponent('movement-controls', {
-
- /*******************************************************************
- * Schema
- */
-
- dependencies: ['rotation'],
-
- schema: {
- enabled: { default: true },
- controls: { default: ['gamepad', 'trackpad', 'keyboard', 'touch'] },
- speed: { default: 0.3, min: 0 },
- fly: { default: false },
- constrainToNavMesh: { default: false },
- camera: { default: '[movement-controls] [camera]', type: 'selector' }
- },
-
- /*******************************************************************
- * Lifecycle
- */
-
- init: function () {
- const el = this.el;
- if (!this.data.camera) {
- this.data.camera = el.querySelector('[camera]')
- }
- this.velocityCtrl = null;
-
- this.velocity = new THREE.Vector3();
- this.heading = new THREE.Quaternion();
-
- // Navigation
- this.navGroup = null;
- this.navNode = null;
-
- if (el.sceneEl.hasLoaded) {
- this.injectControls();
- } else {
- el.sceneEl.addEventListener('loaded', this.injectControls.bind(this));
- }
- },
-
- update: function (prevData) {
- const el = this.el;
- const data = this.data;
- const nav = el.sceneEl.systems.nav;
- if (el.sceneEl.hasLoaded) {
- this.injectControls();
- }
- if (nav && data.constrainToNavMesh !== prevData.constrainToNavMesh) {
- data.constrainToNavMesh
- ? nav.addAgent(this)
- : nav.removeAgent(this);
- }
- },
-
- injectControls: function () {
- const data = this.data;
- var name;
-
- for (let i = 0; i < data.controls.length; i++) {
- name = data.controls[i] + COMPONENT_SUFFIX;
- if (!this.el.components[name]) {
- this.el.setAttribute(name, '');
- }
- }
- },
-
- updateNavLocation: function () {
- this.navGroup = null;
- this.navNode = null;
- },
-
- /*******************************************************************
- * Tick
- */
-
- tick: (function () {
- const start = new THREE.Vector3();
- const end = new THREE.Vector3();
- const clampedEnd = new THREE.Vector3();
-
- return function (t, dt) {
- if (!dt) return;
-
- const el = this.el;
- const data = this.data;
-
- if (!data.enabled) return;
-
- this.updateVelocityCtrl();
- const velocityCtrl = this.velocityCtrl;
- const velocity = this.velocity;
-
- if (!velocityCtrl) return;
-
- // Update velocity. If FPS is too low, reset.
- if (dt / 1000 > MAX_DELTA) {
- velocity.set(0, 0, 0);
- } else {
- this.updateVelocity(dt);
- }
-
- if (data.constrainToNavMesh
- && velocityCtrl.isNavMeshConstrained !== false) {
-
- if (velocity.lengthSq() < EPS) return;
-
- start.copy(el.object3D.position);
- end
- .copy(velocity)
- .multiplyScalar(dt / 1000)
- .add(start);
-
- const nav = el.sceneEl.systems.nav;
- this.navGroup = this.navGroup === null ? nav.getGroup(start) : this.navGroup;
- this.navNode = this.navNode || nav.getNode(start, this.navGroup);
- this.navNode = nav.clampStep(start, end, this.navGroup, this.navNode, clampedEnd);
- el.object3D.position.copy(clampedEnd);
- } else if (el.hasAttribute('velocity')) {
- el.setAttribute('velocity', velocity);
- } else {
- el.object3D.position.x += velocity.x * dt / 1000;
- el.object3D.position.y += velocity.y * dt / 1000;
- el.object3D.position.z += velocity.z * dt / 1000;
- }
-
- };
- }()),
-
- /*******************************************************************
- * Movement
- */
-
- updateVelocityCtrl: function () {
- const data = this.data;
- if (data.enabled) {
- for (let i = 0, l = data.controls.length; i < l; i++) {
- const control = this.el.components[data.controls[i] + COMPONENT_SUFFIX];
- if (control && control.isVelocityActive()) {
- this.velocityCtrl = control;
- return;
- }
- }
- this.velocityCtrl = null;
- }
- },
-
- updateVelocity: (function () {
- const vector2 = new THREE.Vector2();
- const quaternion = new THREE.Quaternion();
-
- return function (dt) {
- let dVelocity;
- const el = this.el;
- const control = this.velocityCtrl;
- const velocity = this.velocity;
- const data = this.data;
-
- if (control) {
- if (control.getVelocityDelta) {
- dVelocity = control.getVelocityDelta(dt);
- } else if (control.getVelocity) {
- velocity.copy(control.getVelocity());
- return;
- } else if (control.getPositionDelta) {
- velocity.copy(control.getPositionDelta(dt).multiplyScalar(1000 / dt));
- return;
- } else {
- throw new Error('Incompatible movement controls: ', control);
- }
- }
-
- if (el.hasAttribute('velocity') && !data.constrainToNavMesh) {
- velocity.copy(this.el.getAttribute('velocity'));
- }
-
- if (dVelocity && data.enabled) {
- const cameraEl = data.camera;
-
- // Rotate to heading
- quaternion.copy(cameraEl.object3D.quaternion);
- quaternion.premultiply(el.object3D.quaternion);
- dVelocity.applyQuaternion(quaternion);
-
- const factor = dVelocity.length();
- if (data.fly) {
- velocity.copy(dVelocity);
- velocity.multiplyScalar(this.data.speed * 16.66667);
- } else {
- vector2.set(dVelocity.x, dVelocity.z);
- vector2.setLength(factor * this.data.speed * 16.66667);
- velocity.x = vector2.x;
- velocity.z = vector2.y;
- }
- }
- };
-
- }())
-});
diff --git a/js/src/controls/touch-controls.js b/js/src/controls/touch-controls.js
deleted file mode 100644
index 1286abb..0000000
--- a/js/src/controls/touch-controls.js
+++ /dev/null
@@ -1,76 +0,0 @@
-/**
- * Touch-to-move-forward controls for mobile.
- */
-module.exports = AFRAME.registerComponent('touch-controls', {
- schema: {
- enabled: { default: true },
- reverseEnabled: { default: true }
- },
-
- init: function () {
- this.dVelocity = new THREE.Vector3();
- this.bindMethods();
- this.direction = 0;
- },
-
- play: function () {
- this.addEventListeners();
- },
-
- pause: function () {
- this.removeEventListeners();
- this.dVelocity.set(0, 0, 0);
- },
-
- remove: function () {
- this.pause();
- },
-
- addEventListeners: function () {
- const sceneEl = this.el.sceneEl;
- const canvasEl = sceneEl.canvas;
-
- if (!canvasEl) {
- sceneEl.addEventListener('render-target-loaded', this.addEventListeners.bind(this));
- return;
- }
-
- canvasEl.addEventListener('touchstart', this.onTouchStart);
- canvasEl.addEventListener('touchend', this.onTouchEnd);
- },
-
- removeEventListeners: function () {
- const canvasEl = this.el.sceneEl && this.el.sceneEl.canvas;
- if (!canvasEl) { return; }
-
- canvasEl.removeEventListener('touchstart', this.onTouchStart);
- canvasEl.removeEventListener('touchend', this.onTouchEnd);
- },
-
- isVelocityActive: function () {
- return this.data.enabled && !!this.direction;
- },
-
- getVelocityDelta: function () {
- this.dVelocity.z = this.direction;
- return this.dVelocity.clone();
- },
-
- bindMethods: function () {
- this.onTouchStart = this.onTouchStart.bind(this);
- this.onTouchEnd = this.onTouchEnd.bind(this);
- },
-
- onTouchStart: function (e) {
- this.direction = -1;
- if (this.data.reverseEnabled && e.touches.length === 2) {
- this.direction = 1;
- }
- e.preventDefault();
- },
-
- onTouchEnd: function (e) {
- this.direction = 0;
- e.preventDefault();
- }
-});
diff --git a/js/src/controls/trackpad-controls.js b/js/src/controls/trackpad-controls.js
deleted file mode 100644
index 491a508..0000000
--- a/js/src/controls/trackpad-controls.js
+++ /dev/null
@@ -1,198 +0,0 @@
-/**
- * 3dof (Gear VR, Daydream) controls for mobile.
- */
-module.exports = AFRAME.registerComponent('trackpad-controls', {
- schema: {
- enabled: { default: true },
- enableNegX: { default: true },
- enablePosX: { default: true },
- enableNegZ: { default: true },
- enablePosZ: { default: true },
- mode: { default: 'touch', oneOf: ['swipe', 'touch', 'press'] }
-
- },
-
- init: function () {
- this.dVelocity = new THREE.Vector3();
- this.zVel = 0;
- this.xVel = 0;
- this.bindMethods();
- },
-
- play: function () {
- this.addEventListeners();
- },
-
- pause: function () {
- this.removeEventListeners();
- this.dVelocity.set(0, 0, 0);
- },
-
- remove: function () {
- this.pause();
- },
-
- addEventListeners: function () {
- const data = this.data;
- const sceneEl = this.el.sceneEl;
-
- sceneEl.addEventListener('axismove', this.onAxisMove);
-
- switch (data.mode) {
- case 'swipe':
- case 'touch':
- sceneEl.addEventListener('trackpadtouchstart', this.onTouchStart);
- sceneEl.addEventListener('trackpadtouchend', this.onTouchEnd);
- break;
-
- case 'press':
- sceneEl.addEventListener('trackpaddown', this.onTouchStart);
- sceneEl.addEventListener('trackpadup', this.onTouchEnd);
- break;
- }
-
- },
-
- removeEventListeners: function () {
- const sceneEl = this.el.sceneEl;
-
- sceneEl.removeEventListener('axismove', this.onAxisMove);
- sceneEl.removeEventListener('trackpadtouchstart', this.onTouchStart);
- sceneEl.removeEventListener('trackpadtouchend', this.onTouchEnd);
- sceneEl.removeEventListener('trackpaddown', this.onTouchStart);
- sceneEl.removeEventListener('trackpadup', this.onTouchEnd);
- },
-
- isVelocityActive: function () {
- return this.data.enabled && this.isMoving;
- },
-
- getVelocityDelta: function () {
- this.dVelocity.z = this.isMoving ? -this.zVel : 1;
- this.dVelocity.x = this.isMoving ? this.xVel : 1;
- return this.dVelocity.clone();
- },
-
- bindMethods: function () {
- this.onTouchStart = this.onTouchStart.bind(this);
- this.onTouchEnd = this.onTouchEnd.bind(this);
- this.onAxisMove = this.onAxisMove.bind(this);
- },
-
- onTouchStart: function (e) {
- switch(this.data.mode){
- case 'swipe':
- this.canRecordAxis = true;
- this.startingAxisData = [];
- break;
- case 'touch':
- this.isMoving = true;
- break;
- case 'press':
- this.isMoving = true;
- break;
- }
-
- e.preventDefault();
- },
-
- onTouchEnd: function (e) {
- if(this.data.mode == 'swipe') {
- this.startingAxisData = [];
- }
-
- this.isMoving = false;
- e.preventDefault();
- },
-
- onAxisMove: function(e){
- switch (this.data.mode) {
- case 'swipe':
- return this.handleSwipeAxis(e);
- case 'touch':
- case 'press':
- return this.handleTouchAxis(e);
- }
- },
-
- handleSwipeAxis: function(e) {
- const data = this.data;
- const axisData = e.detail.axis;
-
- if(this.startingAxisData.length === 0 && this.canRecordAxis){
- this.canRecordAxis = false;
- this.startingAxisData[0] = axisData[0];
- this.startingAxisData[1] = axisData[1];
- }
-
- if(this.startingAxisData.length > 0){
- let velX = 0;
- let velZ = 0;
-
- if (data.enableNegX && axisData[0] < this.startingAxisData[0]) {
- velX = -1;
- }
-
- if (data.enablePosX && axisData[0] > this.startingAxisData[0]) {
- velX = 1;
- }
-
- if (data.enablePosZ && axisData[1] > this.startingAxisData[1]) {
- velZ = -1;
- }
-
- if (data.enableNegZ && axisData[1] < this.startingAxisData[1]) {
- velZ = 1;
- }
-
- const absChangeZ = Math.abs(this.startingAxisData[1] - axisData[1]);
- const absChangeX = Math.abs(this.startingAxisData[0] - axisData[0]);
-
- if (absChangeX > absChangeZ) {
- this.zVel = 0;
- this.xVel = velX;
- this.isMoving = true;
- } else {
- this.xVel = 0;
- this.zVel = velZ;
- this.isMoving = true;
- }
-
- }
- },
-
- handleTouchAxis: function(e) {
- const data = this.data;
- const axisData = e.detail.axis;
-
- let velX = 0;
- let velZ = 0;
-
- if (data.enableNegX && axisData[0] < 0) {
- velX = -1;
- }
-
- if (data.enablePosX && axisData[0] > 0) {
- velX = 1;
- }
-
- if (data.enablePosZ && axisData[1] > 0) {
- velZ = -1;
- }
-
- if (data.enableNegZ && axisData[1] < 0) {
- velZ = 1;
- }
-
- if (Math.abs(axisData[0]) > Math.abs(axisData[1])) {
- this.zVel = 0;
- this.xVel = velX;
- } else {
- this.xVel = 0;
- this.zVel = velZ;
- }
-
- }
-
-});
-
diff --git a/js/src/loaders/README.md b/js/src/loaders/README.md
deleted file mode 100644
index 0bc94d8..0000000
--- a/js/src/loaders/README.md
+++ /dev/null
@@ -1,54 +0,0 @@
-# Loaders
-
-Loaders for various 3D model types. All are trivial wrappers around one of the [many THREE.js loader classes](https://github.com/mrdoob/three.js/tree/master/examples/js/loaders).
-
-- **collada-model-legacy**: Loader for COLLADA (`.dae`) format, removed from A-Frame core with v0.9.0 release. Where possible, use the `gltf-model` component that ships with A-Frame instead.
-- **gltf-model-legacy**: Loader for glTF 1.0 format, removed from A-Frame core with v0.7.0 release. For glTF 2.0, use the `gltf-model` component that ships with A-Frame instead.
-- **object-model**: Loader for THREE.js .JSON format, generally containing multiple meshes or an entire scene. Where possible, use the `gltf-model` component that ships with A-Frame instead.
-- **fbx-model**: Loader for FBX format.
-- **animation-mixer**: Controls animations embedded in a glTF model.
-
-## Usage
-
-```html
-
-
-```
-
-THREE.js models often need to be scaled down. Example:
-
-```html
-
-
-```
-
-## Animation
-
-![9ae34fd9-9ea5-44c5-9b95-2873484a1603-6702-0003a29fed9e49a0](https://cloud.githubusercontent.com/assets/1848368/25648601/845485de-2f82-11e7-8ae8-8e58c9dab9ff.gif)
-> Example by [Joe Campbell](https://github.com/rexraptor08) ([source](https://github.com/rexraptor08/animation-controls)).
-
-glTF and three.js models also support animation, through the `animation-mixer` component. All animations will play by default, or you can specify
-an animation and its duration:
-
-| Property | Default | Description |
-|-------------------|----------|-----------------------------------------------------------|
-| clip | * | Name of the animation clip(s) to play. Accepts wildcards. |
-| duration | AUTO | Duration of the animation, in seconds. |
-| crossFadeDuration | 0 | Duration of cross-fades between clips, in seconds. |
-| loop | repeat | `once`, `repeat`, or `pingpong`. In `repeat` and `pingpong` modes, the clip plays once plus the specified number of repetitions. For `pingpong`, every second clip plays in reverse. |
-| repetitions | Infinity | Number of times to play the clip, in addition to the first play. Repetitions are ignored for `loop: once`. |
-| timeScale | 1 | Scaling factor for playback speed. A value of 0 causes the animation to pause. Negative values cause the animation to play backwards. |
-| clampWhenFinished | false | If true, halts the animation at the last frame. |
-| startFrame | 0 | Sets the start of an animation to a specific time (in milliseconds). This is useful when you need to jump to an exact time in an animation. The input parameter will be scaled by the mixer's timeScale. |
-
-
-A list of available animations can usually be found by inspecting the model file or its documentation. All animations will play by default. To play only a specific set of animations, use wildcards: `animation-mixer="clip: run_*"`.
-
-### Animation Events
-
-The `animation-mixer` component emits events at certain points in the animation cycle.
-
-| Event | Details | Description |
-|--------------------|-----------------------|----------------------------------------------------------------|
-| animation-loop | `action`, `loopDelta` | Emitted when a single loop of the animation clip has finished. |
-| animation-finished | `action`, `direction` | Emitted when all loops of an animation clip have finished. |
diff --git a/js/src/loaders/animation-mixer.js b/js/src/loaders/animation-mixer.js
deleted file mode 100644
index b883006..0000000
--- a/js/src/loaders/animation-mixer.js
+++ /dev/null
@@ -1,149 +0,0 @@
-const LoopMode = {
- once: THREE.LoopOnce,
- repeat: THREE.LoopRepeat,
- pingpong: THREE.LoopPingPong
-};
-
-/**
- * animation-mixer
- *
- * Player for animation clips. Intended to be compatible with any model format that supports
- * skeletal or morph animations through THREE.AnimationMixer.
- * See: https://threejs.org/docs/?q=animation#Reference/Animation/AnimationMixer
- */
-module.exports = AFRAME.registerComponent('animation-mixer', {
- schema: {
- clip: { default: '*' },
- duration: { default: 0 },
- clampWhenFinished: { default: false, type: 'boolean' },
- crossFadeDuration: { default: 0 },
- loop: { default: 'repeat', oneOf: Object.keys(LoopMode) },
- repetitions: { default: Infinity, min: 0 },
- timeScale: { default: 1 },
- startFrame: { default: 0 }
- },
-
- init: function () {
- /** @type {THREE.Mesh} */
- this.model = null;
- /** @type {THREE.AnimationMixer} */
- this.mixer = null;
- /** @type {Array} */
- this.activeActions = [];
-
- const model = this.el.getObject3D('mesh');
-
- if (model) {
- this.load(model);
- } else {
- this.el.addEventListener('model-loaded', (e) => {
- this.load(e.detail.model);
- });
- }
- },
-
- load: function (model) {
- const el = this.el;
- this.model = model;
- this.mixer = new THREE.AnimationMixer(model);
- this.mixer.addEventListener('loop', (e) => {
- el.emit('animation-loop', { action: e.action, loopDelta: e.loopDelta });
- });
- this.mixer.addEventListener('finished', (e) => {
- el.emit('animation-finished', { action: e.action, direction: e.direction });
- });
- if (this.data.clip) this.update({});
- },
-
- remove: function () {
- if (this.mixer) this.mixer.stopAllAction();
- },
-
- update: function (prevData) {
- if (!prevData) return;
-
- const data = this.data;
- const changes = AFRAME.utils.diff(data, prevData);
-
- // If selected clips have changed, restart animation.
- if ('clip' in changes) {
- this.stopAction();
- if (data.clip) this.playAction();
- return;
- }
-
- // Otherwise, modify running actions.
- this.activeActions.forEach((action) => {
- if ('duration' in changes && data.duration) {
- action.setDuration(data.duration);
- }
- if ('clampWhenFinished' in changes) {
- action.clampWhenFinished = data.clampWhenFinished;
- }
- if ('loop' in changes || 'repetitions' in changes) {
- action.setLoop(LoopMode[data.loop], data.repetitions);
- }
- if ('timeScale' in changes) {
- action.setEffectiveTimeScale(data.timeScale);
- }
- });
- },
-
- stopAction: function () {
- const data = this.data;
- for (let i = 0; i < this.activeActions.length; i++) {
- data.crossFadeDuration
- ? this.activeActions[i].fadeOut(data.crossFadeDuration)
- : this.activeActions[i].stop();
- }
- this.activeActions.length = 0;
- },
-
- playAction: function () {
- if (!this.mixer) return;
-
- const model = this.model,
- data = this.data,
- clips = model.animations || (model.geometry || {}).animations || [];
-
- if (!clips.length) return;
-
- const re = wildcardToRegExp(data.clip);
-
- for (let clip, i = 0; (clip = clips[i]); i++) {
- if (clip.name.match(re)) {
- const action = this.mixer.clipAction(clip, model);
-
- action.enabled = true;
- action.clampWhenFinished = data.clampWhenFinished;
- if (data.duration) action.setDuration(data.duration);
- if (data.timeScale !== 1) action.setEffectiveTimeScale(data.timeScale);
- action
- .setLoop(LoopMode[data.loop], data.repetitions)
- .fadeIn(data.crossFadeDuration)
- .play();
- this.activeActions.push(action);
- this.mixer.setTime(data.startFrame / 1000);
- }
- }
- },
-
- tick: function (t, dt) {
- if (this.mixer && !isNaN(dt)) this.mixer.update(dt / 1000);
- }
-});
-
-/**
- * Creates a RegExp from the given string, converting asterisks to .* expressions,
- * and escaping all other characters.
- */
-function wildcardToRegExp(s) {
- return new RegExp('^' + s.split(/\*+/).map(regExpEscape).join('.*') + '$');
-}
-
-/**
- * RegExp-escapes all characters in the given string.
- */
-function regExpEscape(s) {
- return s.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&');
-}
diff --git a/js/src/loaders/collada-model-legacy.js b/js/src/loaders/collada-model-legacy.js
deleted file mode 100644
index 55a8c9a..0000000
--- a/js/src/loaders/collada-model-legacy.js
+++ /dev/null
@@ -1,46 +0,0 @@
-THREE.ColladaLoader = require('../../lib/ColladaLoader');
-
-/**
- * collada-model-legacy
- *
- * Loader for COLLADA (.dae) format.
- */
-module.exports.Component = AFRAME.registerComponent('collada-model-legacy', {
- schema: {type: 'asset'},
-
- init: function () {
- this.model = null;
- this.loader = new THREE.ColladaLoader();
- },
-
- update: function () {
- var self = this;
- var el = this.el;
- var src = this.data;
- var rendererSystem = this.el.sceneEl.systems.renderer;
-
- if (!src) { return; }
-
- this.remove();
-
- this.loader.load(src, function (colladaModel) {
- self.model = colladaModel.scene;
- self.model.traverse(function (object) {
- if (object.isMesh) {
- var material = object.material;
- if (material.color) rendererSystem.applyColorCorrection(material.color);
- if (material.map) rendererSystem.applyColorCorrection(material.map);
- if (material.emissive) rendererSystem.applyColorCorrection(material.emissive);
- if (material.emissiveMap) rendererSystem.applyColorCorrection(material.emissiveMap);
- }
- });
- el.setObject3D('mesh', self.model);
- el.emit('model-loaded', {format: 'collada', model: self.model});
- });
- },
-
- remove: function () {
- if (!this.model) { return; }
- this.el.removeObject3D('mesh');
- }
-});
diff --git a/js/src/loaders/fbx-model.js b/js/src/loaders/fbx-model.js
deleted file mode 100644
index 34d2ce6..0000000
--- a/js/src/loaders/fbx-model.js
+++ /dev/null
@@ -1,37 +0,0 @@
-THREE.FBXLoader = require('../../lib/FBXLoader');
-
-/**
- * fbx-model
- *
- * Loader for FBX format. Supports ASCII, but *not* binary, models.
- */
-module.exports = AFRAME.registerComponent('fbx-model', {
- schema: {
- src: { type: 'asset' },
- crossorigin: { default: '' }
- },
-
- init: function () {
- this.model = null;
- },
-
- update: function () {
- const data = this.data;
- if (!data.src) return;
-
- this.remove();
- const loader = new THREE.FBXLoader();
- if (data.crossorigin) loader.setCrossOrigin(data.crossorigin);
- loader.load(data.src, this.load.bind(this));
- },
-
- load: function (model) {
- this.model = model;
- this.el.setObject3D('mesh', model);
- this.el.emit('model-loaded', {format: 'fbx', model: model});
- },
-
- remove: function () {
- if (this.model) this.el.removeObject3D('mesh');
- }
-});
diff --git a/js/src/loaders/gltf-model-legacy.js b/js/src/loaders/gltf-model-legacy.js
deleted file mode 100644
index fd27ca2..0000000
--- a/js/src/loaders/gltf-model-legacy.js
+++ /dev/null
@@ -1,52 +0,0 @@
-const fetchScript = require('../../lib/fetch-script')();
-
-const LOADER_SRC = 'https://cdn.jsdelivr.net/gh/mrdoob/three.js@r86/examples/js/loaders/GLTFLoader.js';
-
-const loadLoader = (function () {
- let promise;
- return function () {
- promise = promise || fetchScript(LOADER_SRC);
- return promise;
- };
-}());
-
-/**
- * Legacy loader for glTF 1.0 models.
- * Asynchronously loads THREE.GLTFLoader from jsdelivr.
- */
-module.exports = AFRAME.registerComponent('gltf-model-legacy', {
- schema: {type: 'model'},
-
- init: function () {
- this.model = null;
- this.loader = null;
- this.loaderPromise = loadLoader().then(() => {
- this.loader = new THREE.GLTFLoader();
- this.loader.setCrossOrigin('Anonymous');
- });
- },
-
- update: function () {
- const self = this;
- const el = this.el;
- const src = this.data;
-
- if (!src) { return; }
-
- this.remove();
-
- this.loaderPromise.then(() => {
- this.loader.load(src, function gltfLoaded (gltfModel) {
- self.model = gltfModel.scene;
- self.model.animations = gltfModel.animations;
- el.setObject3D('mesh', self.model);
- el.emit('model-loaded', {format: 'gltf', model: self.model});
- });
- });
- },
-
- remove: function () {
- if (!this.model) { return; }
- this.el.removeObject3D('mesh');
- }
-});
diff --git a/js/src/loaders/index.js b/js/src/loaders/index.js
deleted file mode 100644
index 84e1fd4..0000000
--- a/js/src/loaders/index.js
+++ /dev/null
@@ -1,5 +0,0 @@
-require('./animation-mixer');
-require('./collada-model-legacy');
-require('./fbx-model');
-require('./gltf-model-legacy');
-require('./object-model');
diff --git a/js/src/loaders/object-model.js b/js/src/loaders/object-model.js
deleted file mode 100644
index 47603cd..0000000
--- a/js/src/loaders/object-model.js
+++ /dev/null
@@ -1,53 +0,0 @@
-/**
- * object-model
- *
- * Loader for THREE.js JSON format. Somewhat confusingly, there are two different THREE.js formats,
- * both having the .json extension. This loader supports only THREE.ObjectLoader, which typically
- * includes multiple meshes or an entire scene.
- *
- * Check the console for errors, if in doubt. You may need to use `json-model` or
- * `blend-character-model` for some .js and .json files.
- *
- * See: https://clara.io/learn/user-guide/data_exchange/threejs_export
- */
-module.exports = AFRAME.registerComponent('object-model', {
- schema: {
- src: { type: 'asset' },
- crossorigin: { default: '' }
- },
-
- init: function () {
- this.model = null;
- },
-
- update: function () {
- let loader;
- const data = this.data;
- if (!data.src) return;
-
- this.remove();
- loader = new THREE.ObjectLoader();
- if (data.crossorigin) loader.setCrossOrigin(data.crossorigin);
- loader.load(data.src, (object) => {
-
- // Enable skinning, if applicable.
- object.traverse((o) => {
- if (o instanceof THREE.SkinnedMesh && o.material) {
- o.material.skinning = !!((o.geometry && o.geometry.bones) || []).length;
- }
- });
-
- this.load(object);
- });
- },
-
- load: function (model) {
- this.model = model;
- this.el.setObject3D('mesh', model);
- this.el.emit('model-loaded', {format: 'json', model: model});
- },
-
- remove: function () {
- if (this.model) this.el.removeObject3D('mesh');
- }
-});
diff --git a/js/src/misc/README.md b/js/src/misc/README.md
deleted file mode 100644
index 9010a91..0000000
--- a/js/src/misc/README.md
+++ /dev/null
@@ -1,39 +0,0 @@
-# Miscellaneous
-
-Various other components.
-
-- **checkpoint**: Target for [checkpoint-controls](/src/controls/checkpoint-controls.js).
-- **grab**: When used on one or both hands, lets the player pick up objects with `vive-controls`. Requires `sphere-collider`.
-- **jump-ability**: Allows player to jump using keyboard or gamepad, when physics is enabled. *Not VR-friendly*.
-- **mesh-smooth**: Apply to models that looks "blocky", to have Three.js compute vertex normals on the fly for a "smoother" look.
-- **normal-material**: Applies a MeshNormalMaterial to the entity, such that face colors are determined by their orientation. Helpful for debugging geometry.
-- **sphere-collider**: Detects collisions with specified objects. Required for `grab`.
-- **toggle-velocity**: Animates an object back and forth between two points, at a constant velocity.
-- **cube-env-map**: Applies a CubeTexture as the envMap of an entity, without otherwise modifying the preset materials.
-- **kinematic-body**: Constraints player movement using physics. *Deprecated — see below.*
-
-## `cube-env-map`
-
-Usage:
-
-```
-
-
-```
-
-| Option | Description |
-|--------|-------------|
-| path | Folder containing cubemap images. Path should end in a trailing `/`. Assumes naming scheme `negx.`, `posx.`, ... |
-| extension | File extension for each cubemap image. |
-| reflectivity | Amount [0,1] of the cubemap that should be reflected. |
-| materials | Names of materials to be modified. Defaults to all materials. |
-
-## `kinematic-body` (Deprecated)
-
-> **WARNING** *Using physics for movement is unstable and performs poorly. When preventing players from passing through obstacles, use a navigation mesh instead whenever possible.*
-
-The `kinematic-body` component constraints player movement using physics, and depends on [aframe-physics-system](http://github.com/donmccurdy/aframe-physics-system/). Using physics for locomotion is not VR-friendly, and often glitchy even for traditional 3D experiences. [Use a navigation mesh](https://github.com/donmccurdy/aframe-extras/tree/master/src/controls#usage) instead, whenever possible.
diff --git a/js/src/misc/checkpoint.js b/js/src/misc/checkpoint.js
deleted file mode 100644
index 8336e35..0000000
--- a/js/src/misc/checkpoint.js
+++ /dev/null
@@ -1,32 +0,0 @@
-module.exports = AFRAME.registerComponent('checkpoint', {
- schema: {
- offset: {default: {x: 0, y: 0, z: 0}, type: 'vec3'}
- },
-
- init: function () {
- this.active = false;
- this.targetEl = null;
- this.fire = this.fire.bind(this);
- this.offset = new THREE.Vector3();
- },
-
- update: function () {
- this.offset.copy(this.data.offset);
- },
-
- play: function () { this.el.addEventListener('click', this.fire); },
- pause: function () { this.el.removeEventListener('click', this.fire); },
- remove: function () { this.pause(); },
-
- fire: function () {
- const targetEl = this.el.sceneEl.querySelector('[checkpoint-controls]');
- if (!targetEl) {
- throw new Error('No `checkpoint-controls` component found.');
- }
- targetEl.components['checkpoint-controls'].setCheckpoint(this.el);
- },
-
- getOffset: function () {
- return this.offset.copy(this.data.offset);
- }
-});
diff --git a/js/src/misc/cube-env-map.js b/js/src/misc/cube-env-map.js
deleted file mode 100644
index b22b5ff..0000000
--- a/js/src/misc/cube-env-map.js
+++ /dev/null
@@ -1,131 +0,0 @@
-/**
- * @param {Array|THREE.Material} material
- * @return {Array}
- */
-function ensureMaterialArray (material) {
- if (!material) {
- return [];
- } else if (Array.isArray(material)) {
- return material;
- } else if (material.materials) {
- return material.materials;
- } else {
- return [material];
- }
-}
-
-/**
- * @param {THREE.Object3D} mesh
- * @param {Array} materialNames
- * @param {THREE.Texture} envMap
- * @param {number} reflectivity [description]
- */
-function applyEnvMap (mesh, materialNames, envMap, reflectivity) {
- if (!mesh) return;
-
- materialNames = materialNames || [];
-
- mesh.traverse((node) => {
-
- if (!node.isMesh) return;
-
- const meshMaterials = ensureMaterialArray(node.material);
-
- meshMaterials.forEach((material) => {
-
- if (material && !('envMap' in material)) return;
- if (materialNames.length && materialNames.indexOf(material.name) === -1) return;
-
- material.envMap = envMap;
- material.reflectivity = reflectivity;
- material.needsUpdate = true;
-
- });
-
- });
-}
-
-/**
- * Specifies an envMap on an entity, without replacing any existing material
- * properties.
- */
-module.exports = AFRAME.registerComponent('cube-env-map', {
- multiple: true,
-
- schema: {
- path: {default: ''},
- extension: {default: 'jpg', oneOf: ['jpg', 'png']},
- format: {default: 'RGBFormat', oneOf: ['RGBFormat', 'RGBAFormat']},
- enableBackground: {default: false},
- reflectivity: {default: 1, min: 0, max: 1},
- materials: {default: []}
- },
-
- init: function () {
- const data = this.data;
-
- this.texture = new THREE.CubeTextureLoader().load([
- data.path + 'posx.' + data.extension, data.path + 'negx.' + data.extension,
- data.path + 'posy.' + data.extension, data.path + 'negy.' + data.extension,
- data.path + 'posz.' + data.extension, data.path + 'negz.' + data.extension
- ]);
- this.texture.format = THREE[data.format];
-
- this.object3dsetHandler = () => {
- const mesh = this.el.getObject3D('mesh');
- const data = this.data;
- applyEnvMap(mesh, data.materials, this.texture, data.reflectivity);
- };
- this.el.addEventListener('object3dset', this.object3dsetHandler);
- },
-
- update: function (oldData) {
- const data = this.data;
- const mesh = this.el.getObject3D('mesh');
-
- let addedMaterialNames = [];
- let removedMaterialNames = [];
-
- if (data.materials.length) {
- if (oldData.materials) {
- addedMaterialNames = data.materials.filter((name) => !oldData.materials.includes(name));
- removedMaterialNames = oldData.materials.filter((name) => !data.materials.includes(name));
- } else {
- addedMaterialNames = data.materials;
- }
- }
- if (addedMaterialNames.length) {
- applyEnvMap(mesh, addedMaterialNames, this.texture, data.reflectivity);
- }
- if (removedMaterialNames.length) {
- applyEnvMap(mesh, removedMaterialNames, null, 1);
- }
-
- if (oldData.materials && data.reflectivity !== oldData.reflectivity) {
- const maintainedMaterialNames = data.materials
- .filter((name) => oldData.materials.includes(name));
- if (maintainedMaterialNames.length) {
- applyEnvMap(mesh, maintainedMaterialNames, this.texture, data.reflectivity);
- }
- }
-
- if (this.data.enableBackground && !oldData.enableBackground) {
- this.setBackground(this.texture);
- } else if (!this.data.enableBackground && oldData.enableBackground) {
- this.setBackground(null);
- }
- },
-
- remove: function () {
- this.el.removeEventListener('object3dset', this.object3dsetHandler);
- const mesh = this.el.getObject3D('mesh');
- const data = this.data;
-
- applyEnvMap(mesh, data.materials, null, 1);
- if (data.enableBackground) this.setBackground(null);
- },
-
- setBackground: function (texture) {
- this.el.sceneEl.object3D.background = texture;
- }
-});
diff --git a/js/src/misc/grab.js b/js/src/misc/grab.js
deleted file mode 100644
index 42286f7..0000000
--- a/js/src/misc/grab.js
+++ /dev/null
@@ -1,74 +0,0 @@
-/* global CANNON */
-
-/**
- * Based on aframe/examples/showcase/tracked-controls.
- *
- * Handles events coming from the hand-controls.
- * Determines if the entity is grabbed or released.
- * Updates its position to move along the controller.
- */
-module.exports = AFRAME.registerComponent('grab', {
- init: function () {
- this.system = this.el.sceneEl.systems.physics;
-
- this.GRABBED_STATE = 'grabbed';
-
- this.grabbing = false;
- this.hitEl = /** @type {AFRAME.Element} */ null;
- this.physics = /** @type {AFRAME.System} */ this.el.sceneEl.systems.physics;
- this.constraint = /** @type {CANNON.Constraint} */ null;
-
- // Bind event handlers
- this.onHit = this.onHit.bind(this);
- this.onGripOpen = this.onGripOpen.bind(this);
- this.onGripClose = this.onGripClose.bind(this);
- },
-
- play: function () {
- const el = this.el;
- el.addEventListener('hit', this.onHit);
- el.addEventListener('gripdown', this.onGripClose);
- el.addEventListener('gripup', this.onGripOpen);
- el.addEventListener('trackpaddown', this.onGripClose);
- el.addEventListener('trackpadup', this.onGripOpen);
- el.addEventListener('triggerdown', this.onGripClose);
- el.addEventListener('triggerup', this.onGripOpen);
- },
-
- pause: function () {
- const el = this.el;
- el.removeEventListener('hit', this.onHit);
- el.removeEventListener('gripdown', this.onGripClose);
- el.removeEventListener('gripup', this.onGripOpen);
- el.removeEventListener('trackpaddown', this.onGripClose);
- el.removeEventListener('trackpadup', this.onGripOpen);
- el.removeEventListener('triggerdown', this.onGripClose);
- el.removeEventListener('triggerup', this.onGripOpen);
- },
-
- onGripClose: function () {
- this.grabbing = true;
- },
-
- onGripOpen: function () {
- const hitEl = this.hitEl;
- this.grabbing = false;
- if (!hitEl) { return; }
- hitEl.removeState(this.GRABBED_STATE);
- this.hitEl = undefined;
- this.system.removeConstraint(this.constraint);
- this.constraint = null;
- },
-
- onHit: function (evt) {
- const hitEl = evt.detail.el;
- // If the element is already grabbed (it could be grabbed by another controller).
- // If the hand is not grabbing the element does not stick.
- // If we're already grabbing something you can't grab again.
- if (!hitEl || hitEl.is(this.GRABBED_STATE) || !this.grabbing || this.hitEl) { return; }
- hitEl.addState(this.GRABBED_STATE);
- this.hitEl = hitEl;
- this.constraint = new CANNON.LockConstraint(this.el.body, hitEl.body);
- this.system.addConstraint(this.constraint);
- }
-});
diff --git a/js/src/misc/index.js b/js/src/misc/index.js
deleted file mode 100644
index 34c29cd..0000000
--- a/js/src/misc/index.js
+++ /dev/null
@@ -1,8 +0,0 @@
-require('./checkpoint');
-require('./cube-env-map');
-require('./grab');
-require('./jump-ability');
-require('./kinematic-body');
-require('./mesh-smooth');
-require('./normal-material');
-require('./sphere-collider');
diff --git a/js/src/misc/jump-ability.js b/js/src/misc/jump-ability.js
deleted file mode 100644
index 351dde3..0000000
--- a/js/src/misc/jump-ability.js
+++ /dev/null
@@ -1,62 +0,0 @@
-const ACCEL_G = -9.8, // m/s^2
- EASING = -15; // m/s^2
-
-/**
- * Jump ability.
- */
-module.exports = AFRAME.registerComponent('jump-ability', {
- dependencies: ['velocity'],
-
- /* Schema
- ——————————————————————————————————————————————*/
-
- schema: {
- on: { default: 'keydown:Space gamepadbuttondown:0' },
- playerHeight: { default: 1.764 },
- maxJumps: { default: 1 },
- distance: { default: 5 },
- debug: { default: false }
- },
-
- init: function () {
- this.velocity = 0;
- this.numJumps = 0;
-
- const beginJump = this.beginJump.bind(this),
- events = this.data.on.split(' ');
- this.bindings = {};
- for (let i = 0; i < events.length; i++) {
- this.bindings[events[i]] = beginJump;
- this.el.addEventListener(events[i], beginJump);
- }
- this.bindings.collide = this.onCollide.bind(this);
- this.el.addEventListener('collide', this.bindings.collide);
- },
-
- remove: function () {
- for (var event in this.bindings) {
- if (this.bindings.hasOwnProperty(event)) {
- this.el.removeEventListener(event, this.bindings[event]);
- delete this.bindings[event];
- }
- }
- this.el.removeEventListener('collide', this.bindings.collide);
- delete this.bindings.collide;
- },
-
- beginJump: function () {
- if (this.numJumps < this.data.maxJumps) {
- const data = this.data,
- initialVelocity = Math.sqrt(-2 * data.distance * (ACCEL_G + EASING)),
- v = this.el.getAttribute('velocity');
- this.el.setAttribute('velocity', {x: v.x, y: initialVelocity, z: v.z});
- this.numJumps++;
- this.el.emit('jumpstart');
- }
- },
-
- onCollide: function () {
- if (this.numJumps > 0) this.el.emit('jumpend');
- this.numJumps = 0;
- }
-});
diff --git a/js/src/misc/kinematic-body.js b/js/src/misc/kinematic-body.js
deleted file mode 100644
index 672c23c..0000000
--- a/js/src/misc/kinematic-body.js
+++ /dev/null
@@ -1,206 +0,0 @@
-/* global CANNON */
-
-/**
- * Kinematic body.
- *
- * Managed dynamic body, which moves but is not affected (directly) by the
- * physics engine. This is not a true kinematic body, in the sense that we are
- * letting the physics engine _compute_ collisions against it and selectively
- * applying those collisions to the object. The physics engine does not decide
- * the position/velocity/rotation of the element.
- *
- * Used for the camera object, because full physics simulation would create
- * movement that feels unnatural to the player. Bipedal movement does not
- * translate nicely to rigid body physics.
- *
- * See: http://www.learn-cocos2d.com/2013/08/physics-engine-platformer-terrible-idea/
- * And: http://oxleygamedev.blogspot.com/2011/04/player-physics-part-2.html
- */
-const EPS = 0.000001;
-
-module.exports = AFRAME.registerComponent('kinematic-body', {
- dependencies: ['velocity'],
-
- /*******************************************************************
- * Schema
- */
-
- schema: {
- mass: { default: 5 },
- radius: { default: 1.3 },
- linearDamping: { default: 0.05 },
- enableSlopes: { default: true },
- enableJumps: { default: false },
- },
-
- /*******************************************************************
- * Lifecycle
- */
-
- init: function () {
- this.system = this.el.sceneEl.systems.physics;
- this.system.addComponent(this);
-
- const el = this.el,
- data = this.data,
- position = (new CANNON.Vec3()).copy(el.object3D.getWorldPosition(new THREE.Vector3()));
-
- this.body = new CANNON.Body({
- material: this.system.getMaterial('staticMaterial'),
- position: position,
- mass: data.mass,
- linearDamping: data.linearDamping,
- fixedRotation: true
- });
- this.body.addShape(
- new CANNON.Sphere(data.radius),
- new CANNON.Vec3(0, data.radius, 0)
- );
-
- this.body.el = this.el;
- this.el.body = this.body;
- this.system.addBody(this.body);
-
- if (el.hasAttribute('wasd-controls')) {
- console.warn('[kinematic-body] Not compatible with wasd-controls, use movement-controls.');
- }
- },
-
- remove: function () {
- this.system.removeBody(this.body);
- this.system.removeComponent(this);
- delete this.el.body;
- },
-
- /*******************************************************************
- * Update
- */
-
- /**
- * Checks CANNON.World for collisions and attempts to apply them to the
- * element automatically, in a player-friendly way.
- *
- * There's extra logic for horizontal surfaces here. The basic requirements:
- * (1) Only apply gravity when not in contact with _any_ horizontal surface.
- * (2) When moving, project the velocity against exactly one ground surface.
- * If in contact with two ground surfaces (e.g. ground + ramp), choose
- * the one that collides with current velocity, if any.
- */
- beforeStep: function (t, dt) {
- if (!dt) return;
-
- const el = this.el;
- const data = this.data
- const body = this.body;
-
- if (!data.enableJumps) body.velocity.set(0, 0, 0);
- body.position.copy(el.getAttribute('position'));
- },
-
- step: (function () {
- const velocity = new THREE.Vector3(),
- normalizedVelocity = new THREE.Vector3(),
- currentSurfaceNormal = new THREE.Vector3(),
- groundNormal = new THREE.Vector3();
-
- return function (t, dt) {
- if (!dt) return;
-
- let body = this.body,
- data = this.data,
- didCollide = false,
- height, groundHeight = -Infinity,
- groundBody,
- contacts = this.system.getContacts();
-
- dt = Math.min(dt, this.system.data.maxInterval * 1000);
-
- groundNormal.set(0, 0, 0);
- velocity.copy(this.el.getAttribute('velocity'));
- body.velocity.copy(velocity);
-
- for (var i = 0, contact; contact = contacts[i]; i++) {
- // 1. Find any collisions involving this element. Get the contact
- // normal, and make sure it's oriented _out_ of the other object and
- // enabled (body.collisionReponse is true for both bodies)
- if (!contact.enabled) { continue; }
- if (body.id === contact.bi.id) {
- contact.ni.negate(currentSurfaceNormal);
- } else if (body.id === contact.bj.id) {
- currentSurfaceNormal.copy(contact.ni);
- } else {
- continue;
- }
-
- didCollide = body.velocity.dot(currentSurfaceNormal) < -EPS;
- if (didCollide && currentSurfaceNormal.y <= 0.5) {
- // 2. If current trajectory attempts to move _through_ another
- // object, project the velocity against the collision plane to
- // prevent passing through.
- velocity.projectOnPlane(currentSurfaceNormal);
- } else if (currentSurfaceNormal.y > 0.5) {
- // 3. If in contact with something roughly horizontal (+/- 45º) then
- // consider that the current ground. Only the highest qualifying
- // ground is retained.
- height = body.id === contact.bi.id
- ? Math.abs(contact.rj.y + contact.bj.position.y)
- : Math.abs(contact.ri.y + contact.bi.position.y);
- if (height > groundHeight) {
- groundHeight = height;
- groundNormal.copy(currentSurfaceNormal);
- groundBody = body.id === contact.bi.id ? contact.bj : contact.bi;
- }
- }
- }
-
- normalizedVelocity.copy(velocity).normalize();
- if (groundBody && (!data.enableJumps || normalizedVelocity.y < 0.5)) {
- if (!data.enableSlopes) {
- groundNormal.set(0, 1, 0);
- } else if (groundNormal.y < 1 - EPS) {
- groundNormal.copy(this.raycastToGround(groundBody, groundNormal));
- }
-
- // 4. Project trajectory onto the top-most ground object, unless
- // trajectory is > 45º.
- velocity.projectOnPlane(groundNormal);
-
- } else if (this.system.driver.world) {
- // 5. If not in contact with anything horizontal, apply world gravity.
- // TODO - Why is the 4x scalar necessary.
- // NOTE: Does not work if physics runs on a worker.
- velocity.add(this.system.driver.world.gravity.scale(dt * 4.0 / 1000));
- }
-
- body.velocity.copy(velocity);
- this.el.setAttribute('velocity', body.velocity);
- this.el.setAttribute('position', body.position);
- };
- }()),
-
- /**
- * When walking on complex surfaces (trimeshes, borders between two shapes),
- * the collision normals returned for the player sphere can be very
- * inconsistent. To address this, raycast straight down, find the collision
- * normal, and return whichever normal is more vertical.
- * @param {CANNON.Body} groundBody
- * @param {CANNON.Vec3} groundNormal
- * @return {CANNON.Vec3}
- */
- raycastToGround: function (groundBody, groundNormal) {
- let ray,
- hitNormal,
- vFrom = this.body.position,
- vTo = this.body.position.clone();
-
- ray = new CANNON.Ray(vFrom, vTo);
- ray._updateDirection(); // TODO - Report bug.
- ray.intersectBody(groundBody);
-
- if (!ray.hasHit) return groundNormal;
-
- // Compare ABS, in case we're projecting against the inside of the face.
- hitNormal = ray.result.hitNormalWorld;
- return Math.abs(hitNormal.y) > Math.abs(groundNormal.y) ? hitNormal : groundNormal;
- }
-});
diff --git a/js/src/misc/mesh-smooth.js b/js/src/misc/mesh-smooth.js
deleted file mode 100644
index 2b14b39..0000000
--- a/js/src/misc/mesh-smooth.js
+++ /dev/null
@@ -1,13 +0,0 @@
-/**
- * Apply this component to models that looks "blocky", to have Three.js compute
- * vertex normals on the fly for a "smoother" look.
- */
-module.exports = AFRAME.registerComponent('mesh-smooth', {
- init: function () {
- this.el.addEventListener('model-loaded', (e) => {
- e.detail.model.traverse((node) => {
- if (node.isMesh) node.geometry.computeVertexNormals();
- });
- });
- }
-});
diff --git a/js/src/misc/normal-material.js b/js/src/misc/normal-material.js
deleted file mode 100644
index a73e12f..0000000
--- a/js/src/misc/normal-material.js
+++ /dev/null
@@ -1,22 +0,0 @@
-/**
- * Recursively applies a MeshNormalMaterial to the entity, such that
- * face colors are determined by their orientation. Helpful for
- * debugging geometry
- */
-module.exports = AFRAME.registerComponent('normal-material', {
- init: function () {
- this.material = new THREE.MeshNormalMaterial({flatShading: true});
- this.applyMaterial = this.applyMaterial.bind(this);
- this.el.addEventListener('object3dset', this.applyMaterial);
- },
-
- remove: function () {
- this.el.removeEventListener('object3dset', this.applyMaterial);
- },
-
- applyMaterial: function () {
- this.el.object3D.traverse((node) => {
- if (node.isMesh) node.material = this.material;
- });
- }
-});
diff --git a/js/src/misc/sphere-collider.js b/js/src/misc/sphere-collider.js
deleted file mode 100644
index aedff95..0000000
--- a/js/src/misc/sphere-collider.js
+++ /dev/null
@@ -1,148 +0,0 @@
-/**
- * Based on aframe/examples/showcase/tracked-controls.
- *
- * Implement bounding sphere collision detection for entities with a mesh.
- * Sets the specified state on the intersected entities.
- *
- * @property {string} objects - Selector of the entities to test for collision.
- * @property {string} state - State to set on collided entities.
- *
- */
-module.exports = AFRAME.registerComponent('sphere-collider', {
- schema: {
- objects: {default: ''},
- state: {default: 'collided'},
- radius: {default: 0.05},
- watch: {default: true}
- },
-
- init: function () {
- /** @type {MutationObserver} */
- this.observer = null;
- /** @type {Array} Elements to watch for collisions. */
- this.els = [];
- /** @type {Array} Elements currently in collision state. */
- this.collisions = [];
-
- this.handleHit = this.handleHit.bind(this);
- this.handleHitEnd = this.handleHitEnd.bind(this);
- },
-
- remove: function () {
- this.pause();
- },
-
- play: function () {
- const sceneEl = this.el.sceneEl;
-
- if (this.data.watch) {
- this.observer = new MutationObserver(this.update.bind(this, null));
- this.observer.observe(sceneEl, {childList: true, subtree: true});
- }
- },
-
- pause: function () {
- if (this.observer) {
- this.observer.disconnect();
- this.observer = null;
- }
- },
-
- /**
- * Update list of entities to test for collision.
- */
- update: function () {
- const data = this.data;
- let objectEls;
-
- // Push entities into list of els to intersect.
- if (data.objects) {
- objectEls = this.el.sceneEl.querySelectorAll(data.objects);
- } else {
- // If objects not defined, intersect with everything.
- objectEls = this.el.sceneEl.children;
- }
- // Convert from NodeList to Array
- this.els = Array.prototype.slice.call(objectEls);
- },
-
- tick: (function () {
- const position = new THREE.Vector3(),
- meshPosition = new THREE.Vector3(),
- colliderScale = new THREE.Vector3(),
- size = new THREE.Vector3(),
- box = new THREE.Box3(),
- distanceMap = new Map();
- return function () {
- const el = this.el,
- data = this.data,
- mesh = el.getObject3D('mesh'),
- collisions = [];
- let colliderRadius;
-
- if (!mesh) { return; }
-
- distanceMap.clear();
- el.object3D.getWorldPosition(position);
- el.object3D.getWorldScale(colliderScale);
- colliderRadius = data.radius * scaleFactor(colliderScale);
- // Update collision list.
- this.els.forEach(intersect);
-
- // Emit events and add collision states, in order of distance.
- collisions
- .sort((a, b) => distanceMap.get(a) > distanceMap.get(b) ? 1 : -1)
- .forEach(this.handleHit);
-
- // Remove collision state from current element.
- if (collisions.length === 0) { el.emit('hit', {el: null}); }
-
- // Remove collision state from other elements.
- this.collisions
- .filter((el) => !distanceMap.has(el))
- .forEach(this.handleHitEnd);
-
- // Store new collisions
- this.collisions = collisions;
-
- // Bounding sphere collision detection
- function intersect (el) {
- let radius, mesh, distance, extent;
-
- if (!el.isEntity) { return; }
-
- mesh = el.getObject3D('mesh');
-
- if (!mesh) { return; }
-
- box.setFromObject(mesh).getSize(size);
- extent = Math.max(size.x, size.y, size.z) / 2;
- radius = Math.sqrt(2 * extent * extent);
- box.getCenter(meshPosition);
-
- if (!radius) { return; }
-
- distance = position.distanceTo(meshPosition);
- if (distance < radius + colliderRadius) {
- collisions.push(el);
- distanceMap.set(el, distance);
- }
- }
- // use max of scale factors to maintain bounding sphere collision
- function scaleFactor (scaleVec) {
- return Math.max.apply(null, scaleVec.toArray());
- }
- };
- })(),
-
- handleHit: function (targetEl) {
- targetEl.emit('hit');
- targetEl.addState(this.data.state);
- this.el.emit('hit', {el: targetEl});
- },
- handleHitEnd: function (targetEl) {
- targetEl.emit('hitend');
- targetEl.removeState(this.data.state);
- this.el.emit('hitend', {el: targetEl});
- }
-});
diff --git a/js/src/pathfinding/README.md b/js/src/pathfinding/README.md
deleted file mode 100644
index 57ff3aa..0000000
--- a/js/src/pathfinding/README.md
+++ /dev/null
@@ -1,51 +0,0 @@
-# Pathfinding
-
-Set of components for pathfinding along a nav mesh, using [PatrolJS](https://github.com/nickjanssen/PatrolJS/).
-
-- **nav-mesh**: Assigns model from the current entity as a [navigation mesh](https://en.wikipedia.org/wiki/Navigation_mesh) for the pathfinding system. A navigation mesh is not the same as visible terrain geometry. See below.
-- **nav-agent**: Adds behaviors to an entity allowing it to navigate to any reachable destination along the nav mesh.
-
-## Creating a nav mesh
-
-[Blog post](https://medium.com/@donmccurdy/creating-a-nav-mesh-for-a-webvr-scene-b3fdb6bed918).
-
-## Setting a destination
-
-Controllers can be activated to begin moving their entity toward a destination. Example:
-
-```html
-
-
-
-
-
-
-
-
-```
-
-```js
-var npcEl = document.querySelector('#npc');
-npcEl.setAttribute('nav-agent', {
- active: true,
- destination: e.detail.intersection.point
-});
-```
-
-## Events
-
-The `nav-agent` component will emit two events:
-
-- `navigation-start`: Entity beginning travel to a destination.
-- `navigation-end`: Entity has reached destination.
-
-## Important notes
-
-This implementation is meant as a proof-of-concept, and doesn't have all the features and polish of game engine navigation. Currently missing:
-
-- [ ] Smooth rotation when navigating around corners.
-- [ ] Dynamic obstacles, like mobile props and NPCs.
-- [ ] Multiple nav meshes and/or levels.
diff --git a/js/src/pathfinding/index.js b/js/src/pathfinding/index.js
deleted file mode 100644
index 50a508a..0000000
--- a/js/src/pathfinding/index.js
+++ /dev/null
@@ -1,3 +0,0 @@
-require('./nav-mesh');
-require('./nav-agent');
-require('./system');
diff --git a/js/src/pathfinding/nav-agent.js b/js/src/pathfinding/nav-agent.js
deleted file mode 100644
index e1b0d2d..0000000
--- a/js/src/pathfinding/nav-agent.js
+++ /dev/null
@@ -1,103 +0,0 @@
-module.exports = AFRAME.registerComponent('nav-agent', {
- schema: {
- destination: {type: 'vec3'},
- active: {default: false},
- speed: {default: 2}
- },
- init: function () {
- this.system = this.el.sceneEl.systems.nav;
- this.system.addAgent(this);
- this.group = null;
- this.path = [];
- this.raycaster = new THREE.Raycaster();
- },
- remove: function () {
- this.system.removeAgent(this);
- },
- update: function () {
- this.path.length = 0;
- },
- updateNavLocation: function () {
- this.group = null;
- this.path = [];
- },
- tick: (function () {
- const vDest = new THREE.Vector3();
- const vDelta = new THREE.Vector3();
- const vNext = new THREE.Vector3();
-
- return function (t, dt) {
- const el = this.el;
- const data = this.data;
- const raycaster = this.raycaster;
- const speed = data.speed * dt / 1000;
-
- if (!data.active) return;
-
- // Use PatrolJS pathfinding system to get shortest path to target.
- if (!this.path.length) {
- const position = this.el.object3D.position;
- this.group = this.group || this.system.getGroup(position);
- this.path = this.system.getPath(position, vDest.copy(data.destination), this.group) || [];
- el.emit('navigation-start');
- }
-
- // If no path is found, exit.
- if (!this.path.length) {
- console.warn('[nav] Unable to find path to %o.', data.destination);
- this.el.setAttribute('nav-agent', {active: false});
- el.emit('navigation-end');
- return;
- }
-
- // Current segment is a vector from current position to next waypoint.
- const vCurrent = el.object3D.position;
- const vWaypoint = this.path[0];
- vDelta.subVectors(vWaypoint, vCurrent);
-
- const distance = vDelta.length();
- let gazeTarget;
-
- if (distance < speed) {
- // If <1 step from current waypoint, discard it and move toward next.
- this.path.shift();
-
- // After discarding the last waypoint, exit pathfinding.
- if (!this.path.length) {
- this.el.setAttribute('nav-agent', {active: false});
- el.emit('navigation-end');
- return;
- }
-
- vNext.copy(vCurrent);
- gazeTarget = this.path[0];
- } else {
- // If still far away from next waypoint, find next position for
- // the current frame.
- vNext.copy(vDelta.setLength(speed)).add(vCurrent);
- gazeTarget = vWaypoint;
- }
-
- // Look at the next waypoint.
- gazeTarget.y = vCurrent.y;
- el.object3D.lookAt(gazeTarget);
-
- // Raycast against the nav mesh, to keep the agent moving along the
- // ground, not traveling in a straight line from higher to lower waypoints.
- raycaster.ray.origin.copy(vNext);
- raycaster.ray.origin.y += 1.5;
- raycaster.ray.direction = {x:0, y:-1, z:0};
- const intersections = raycaster.intersectObject(this.system.getNavMesh());
-
- if (!intersections.length) {
- // Raycasting failed. Step toward the waypoint and hope for the best.
- vCurrent.copy(vNext);
- } else {
- // Re-project next position onto nav mesh.
- vDelta.subVectors(intersections[0].point, vCurrent);
- vCurrent.add(vDelta.setLength(speed));
- }
-
- };
- }())
-});
diff --git a/js/src/pathfinding/nav-mesh.js b/js/src/pathfinding/nav-mesh.js
deleted file mode 100644
index 49bb1fc..0000000
--- a/js/src/pathfinding/nav-mesh.js
+++ /dev/null
@@ -1,42 +0,0 @@
-/**
- * nav-mesh
- *
- * Waits for a mesh to be loaded on the current entity, then sets it as the
- * nav mesh in the pathfinding system.
- */
- module.exports = AFRAME.registerComponent('nav-mesh', {
- schema: {
- nodeName: {type: 'string'}
- },
-
- init: function () {
- this.system = this.el.sceneEl.systems.nav;
- this.hasLoadedNavMesh = false;
- this.nodeName = this.data.nodeName;
- this.el.addEventListener('object3dset', this.loadNavMesh.bind(this));
- },
-
- play: function () {
- if (!this.hasLoadedNavMesh) this.loadNavMesh();
- },
-
- loadNavMesh: function () {
- var self = this;
- const object = this.el.getObject3D('mesh');
- const scene = this.el.sceneEl.object3D;
-
- if (!object) return;
-
- let navMesh;
- object.traverse((node) => {
- if (node.isMesh &&
- (!self.nodeName || node.name === self.nodeName)) navMesh = node;
- });
-
- if (!navMesh) return;
-
- scene.updateMatrixWorld();
- this.system.setNavMeshGeometry(navMesh.geometry);
- this.hasLoadedNavMesh = true;
- }
-});
diff --git a/js/src/pathfinding/system.js b/js/src/pathfinding/system.js
deleted file mode 100644
index 3e9ba02..0000000
--- a/js/src/pathfinding/system.js
+++ /dev/null
@@ -1,98 +0,0 @@
-const { Pathfinding } = require('three-pathfinding');
-
-const pathfinder = new Pathfinding();
-const ZONE = 'level';
-
-/**
- * nav
- *
- * Pathfinding system, using PatrolJS.
- */
-module.exports = AFRAME.registerSystem('nav', {
- init: function () {
- this.navMesh = null;
- this.agents = new Set();
- },
-
- /**
- * @param {THREE.Geometry} geometry
- */
- setNavMeshGeometry: function (geometry) {
- this.navMesh = new THREE.Mesh(geometry);
- pathfinder.setZoneData(ZONE, Pathfinding.createZone(geometry));
- Array.from(this.agents).forEach((agent) => agent.updateNavLocation());
- },
-
- /**
- * @return {THREE.Mesh}
- */
- getNavMesh: function () {
- return this.navMesh;
- },
-
- /**
- * @param {NavAgent} ctrl
- */
- addAgent: function (ctrl) {
- this.agents.add(ctrl);
- },
-
- /**
- * @param {NavAgent} ctrl
- */
- removeAgent: function (ctrl) {
- this.agents.delete(ctrl);
- },
-
- /**
- * @param {THREE.Vector3} start
- * @param {THREE.Vector3} end
- * @param {number} groupID
- * @return {Array}
- */
- getPath: function (start, end, groupID) {
- return this.navMesh
- ? pathfinder.findPath(start, end, ZONE, groupID)
- : null;
- },
-
- /**
- * @param {THREE.Vector3} position
- * @return {number}
- */
- getGroup: function (position) {
- return this.navMesh
- ? pathfinder.getGroup(ZONE, position)
- : null;
- },
-
- /**
- * @param {THREE.Vector3} position
- * @param {number} groupID
- * @return {Node}
- */
- getNode: function (position, groupID) {
- return this.navMesh
- ? pathfinder.getClosestNode(position, ZONE, groupID, true)
- : null;
- },
-
- /**
- * @param {THREE.Vector3} start Starting position.
- * @param {THREE.Vector3} end Desired ending position.
- * @param {number} groupID
- * @param {Node} node
- * @param {THREE.Vector3} endTarget (Output) Adjusted step end position.
- * @return {Node} Current node, after step is taken.
- */
- clampStep: function (start, end, groupID, node, endTarget) {
- if (!this.navMesh) {
- endTarget.copy(end);
- return null;
- } else if (!node) {
- endTarget.copy(end);
- return this.getNode(end, groupID);
- }
- return pathfinder.clampStep(start, end, node, ZONE, groupID, endTarget);
- }
-});
diff --git a/js/src/primitives/README.md b/js/src/primitives/README.md
deleted file mode 100644
index f25f8c3..0000000
--- a/js/src/primitives/README.md
+++ /dev/null
@@ -1,34 +0,0 @@
-# Primitives
-
-Reusable entities / primitives.
-
-- ``: Flat grid, with subdivisions at regular intervals.
-- ``: Hexagon map loaded from a JSON file, using [von-grid](https://github.com/vonWolfehaus/von-grid).
-- ``: Ocean with animated waves.
-- ``: Tube following a custom path.
-
-## Usage
-
-Basic:
-
-```html
-
-
-
-
-
-
-
-
-```
-
-Custom:
-
-```html
-
-```
-
-```html
-
-```
diff --git a/js/src/primitives/a-grid.js b/js/src/primitives/a-grid.js
deleted file mode 100644
index 9b28c57..0000000
--- a/js/src/primitives/a-grid.js
+++ /dev/null
@@ -1,24 +0,0 @@
-/**
- * Flat grid.
- *
- * Defaults to 75x75.
- */
-module.exports = AFRAME.registerPrimitive('a-grid', {
- defaultComponents: {
- geometry: {
- primitive: 'plane',
- width: 75,
- height: 75
- },
- rotation: {x: -90, y: 0, z: 0},
- material: {
- src: 'url(https://cdn.jsdelivr.net/gh/donmccurdy/aframe-extras@v1.16.3/assets/grid.png)',
- repeat: '75 75'
- }
- },
- mappings: {
- width: 'geometry.width',
- height: 'geometry.height',
- src: 'material.src'
- }
-});
diff --git a/js/src/primitives/a-hexgrid.js b/js/src/primitives/a-hexgrid.js
deleted file mode 100644
index fb42138..0000000
--- a/js/src/primitives/a-hexgrid.js
+++ /dev/null
@@ -1,52 +0,0 @@
-const vg = require('../../lib/hex-grid.min.js');
-const defaultHexGrid = require('../../lib/default-hex-grid');
-
-/**
- * Hex grid.
- */
-module.exports.Primitive = AFRAME.registerPrimitive('a-hexgrid', {
- defaultComponents: {
- 'hexgrid': {}
- },
- mappings: {
- src: 'hexgrid.src'
- }
-});
-
-module.exports.Component = AFRAME.registerComponent('hexgrid', {
- dependencies: ['material'],
- schema: {
- src: {type: 'asset'}
- },
- init: function () {
- const data = this.data;
- if (data.src) {
- fetch(data.src)
- .then((response) => response.json())
- .then((json) => this.addMesh(json));
- } else {
- this.addMesh(defaultHexGrid);
- }
- },
- addMesh: function (json) {
- const grid = new vg.HexGrid();
- grid.fromJSON(json);
- const board = new vg.Board(grid);
- board.generateTilemap();
- this.el.setObject3D('mesh', board.group);
- this.addMaterial();
- },
- addMaterial: function () {
- const materialComponent = this.el.components.material;
- const material = (materialComponent || {}).material;
- if (!material) return;
- this.el.object3D.traverse((node) => {
- if (node.isMesh) {
- node.material = material;
- }
- });
- },
- remove: function () {
- this.el.removeObject3D('mesh');
- }
-});
diff --git a/js/src/primitives/a-ocean.js b/js/src/primitives/a-ocean.js
deleted file mode 100644
index a68d604..0000000
--- a/js/src/primitives/a-ocean.js
+++ /dev/null
@@ -1,98 +0,0 @@
-/**
- * Flat-shaded ocean primitive.
- *
- * Based on a Codrops tutorial:
- * http://tympanus.net/codrops/2016/04/26/the-aviator-animating-basic-3d-scene-threejs/
- */
-module.exports.Primitive = AFRAME.registerPrimitive('a-ocean', {
- defaultComponents: {
- ocean: {},
- rotation: {x: -90, y: 0, z: 0}
- },
- mappings: {
- width: 'ocean.width',
- depth: 'ocean.depth',
- density: 'ocean.density',
- amplitude: 'ocean.amplitude',
- amplitudeVariance: 'ocean.amplitudeVariance',
- speed: 'ocean.speed',
- speedVariance: 'ocean.speedVariance',
- color: 'ocean.color',
- opacity: 'ocean.opacity'
- }
-});
-
-module.exports.Component = AFRAME.registerComponent('ocean', {
- schema: {
- // Dimensions of the ocean area.
- width: {default: 10, min: 0},
- depth: {default: 10, min: 0},
-
- // Density of waves.
- density: {default: 10},
-
- // Wave amplitude and variance.
- amplitude: {default: 0.1},
- amplitudeVariance: {default: 0.3},
-
- // Wave speed and variance.
- speed: {default: 1},
- speedVariance: {default: 2},
-
- // Material.
- color: {default: '#7AD2F7', type: 'color'},
- opacity: {default: 0.8}
- },
-
- /**
- * Use play() instead of init(), because component mappings – unavailable as dependencies – are
- * not guaranteed to have parsed when this component is initialized.
- */
- play: function () {
- const el = this.el,
- data = this.data;
- let material = el.components.material;
-
- const geometry = new THREE.PlaneGeometry(data.width, data.depth, data.density, data.density);
- geometry.mergeVertices();
- this.waves = [];
- for (let v, i = 0, l = geometry.vertices.length; i < l; i++) {
- v = geometry.vertices[i];
- this.waves.push({
- z: v.z,
- ang: Math.random() * Math.PI * 2,
- amp: data.amplitude + Math.random() * data.amplitudeVariance,
- speed: (data.speed + Math.random() * data.speedVariance) / 1000 // radians / frame
- });
- }
-
- if (!material) {
- material = {};
- material.material = new THREE.MeshPhongMaterial({
- color: data.color,
- transparent: data.opacity < 1,
- opacity: data.opacity,
- shading: THREE.FlatShading,
- });
- }
-
- this.mesh = new THREE.Mesh(geometry, material.material);
- el.setObject3D('mesh', this.mesh);
- },
-
- remove: function () {
- this.el.removeObject3D('mesh');
- },
-
- tick: function (t, dt) {
- if (!dt) return;
-
- const verts = this.mesh.geometry.vertices;
- for (let v, vprops, i = 0; (v = verts[i]); i++){
- vprops = this.waves[i];
- v.z = vprops.z + Math.sin(vprops.ang) * vprops.amp;
- vprops.ang += vprops.speed * dt;
- }
- this.mesh.geometry.verticesNeedUpdate = true;
- }
-});
diff --git a/js/src/primitives/a-tube.js b/js/src/primitives/a-tube.js
deleted file mode 100644
index fae89cb..0000000
--- a/js/src/primitives/a-tube.js
+++ /dev/null
@@ -1,69 +0,0 @@
-/**
- * Tube following a custom path.
- *
- * Usage:
- *
- * ```html
- *
- * ```
- */
-module.exports.Primitive = AFRAME.registerPrimitive('a-tube', {
- defaultComponents: {
- tube: {},
- },
- mappings: {
- path: 'tube.path',
- segments: 'tube.segments',
- radius: 'tube.radius',
- 'radial-segments': 'tube.radialSegments',
- closed: 'tube.closed'
- }
-});
-
-module.exports.Component = AFRAME.registerComponent('tube', {
- schema: {
- path: {default: []},
- segments: {default: 64},
- radius: {default: 1},
- radialSegments: {default: 8},
- closed: {default: false}
- },
-
- init: function () {
- const el = this.el,
- data = this.data;
- let material = el.components.material;
-
- if (!data.path.length) {
- console.error('[a-tube] `path` property expected but not found.');
- return;
- }
-
- const curve = new THREE.CatmullRomCurve3(data.path.map(function (point) {
- point = point.split(' ');
- return new THREE.Vector3(Number(point[0]), Number(point[1]), Number(point[2]));
- }));
- const geometry = new THREE.TubeGeometry(
- curve, data.segments, data.radius, data.radialSegments, data.closed
- );
-
- if (!material) {
- material = {};
- material.material = new THREE.MeshPhongMaterial();
- }
-
- this.mesh = new THREE.Mesh(geometry, material.material);
- this.el.setObject3D('mesh', this.mesh);
- },
-
- update: function (prevData) {
- if (!Object.keys(prevData).length) return;
-
- this.remove();
- this.init();
- },
-
- remove: function () {
- if (this.mesh) this.el.removeObject3D('mesh');
- }
-});
diff --git a/js/src/primitives/index.js b/js/src/primitives/index.js
deleted file mode 100644
index 72da669..0000000
--- a/js/src/primitives/index.js
+++ /dev/null
@@ -1,4 +0,0 @@
-require('./a-grid');
-require('./a-hexgrid');
-require('./a-ocean');
-require('./a-tube');
diff --git a/models/nav.glb b/models/nav.glb
index 828df36..926ef72 100644
Binary files a/models/nav.glb and b/models/nav.glb differ