aframe-teleport-controls
Advanced tools
Comparing version 0.3.0 to 0.3.1
@@ -47,3 +47,3 @@ /******/ (function(modules) { // webpackBootstrap | ||
/* global THREE, AFRAME */ | ||
/* global THREE, AFRAME, Element */ | ||
var cylinderTexture = __webpack_require__(1); | ||
@@ -76,5 +76,8 @@ var parabolicCurve = __webpack_require__(2); | ||
button: {default: 'trackpad', oneOf: ['trackpad', 'trigger', 'grip', 'menu']}, | ||
startEvents: {type: 'array'}, | ||
endEvents: {type: 'array'}, | ||
collisionEntities: {default: ''}, | ||
hitEntity: {type: 'selector'}, | ||
cameraRig: {type: 'selector'}, | ||
teleportOrigin: {type: 'selector'}, | ||
hitCylinderColor: {type: 'color', default: '#99ff99'}, | ||
@@ -89,2 +92,3 @@ hitCylinderRadius: {default: 0.25, min: 0}, | ||
curveShootingSpeed: {default: 5, min: 0, if: {type: ['parabolic']}}, | ||
defaultPlaneSize: { default: 100 }, | ||
landingNormal: {type: 'vec3', default: '0 1 0'}, | ||
@@ -98,2 +102,3 @@ landingMaxAngle: {default: '45', min: 0, max: 360} | ||
var teleportEntity; | ||
var i; | ||
@@ -103,4 +108,12 @@ this.active = false; | ||
this.hitPoint = new THREE.Vector3(); | ||
this.rigWorldPosition = new THREE.Vector3(); | ||
this.newRigWorldPosition = new THREE.Vector3(); | ||
this.teleportEventDetail = { | ||
oldPosition: this.rigWorldPosition, | ||
newPosition: this.newRigWorldPosition, | ||
hitPoint: this.hitPoint | ||
}; | ||
this.hit = false; | ||
this.prevHeightDiff = 0; | ||
this.prevHitHeight = 0; | ||
this.referenceNormal = new THREE.Vector3(); | ||
@@ -111,3 +124,3 @@ this.curveMissColor = new THREE.Color(); | ||
this.defaultPlane = createDefaultPlane(); | ||
this.defaultPlane = createDefaultPlane(this.data.defaultPlaneSize); | ||
@@ -119,5 +132,17 @@ teleportEntity = this.teleportEntity = document.createElement('a-entity'); | ||
el.addEventListener(data.button + 'down', this.onButtonDown.bind(this)); | ||
el.addEventListener(data.button + 'up', this.onButtonUp.bind(this)); | ||
this.onButtonDown = this.onButtonDown.bind(this); | ||
this.onButtonUp = this.onButtonUp.bind(this); | ||
if (this.data.startEvents.length && this.data.endEvents.length) { | ||
for (i = 0; i < this.data.startEvents.length; i++) { | ||
el.addEventListener(this.data.startEvents[i], this.onButtonDown); | ||
} | ||
for (i = 0; i < this.data.endEvents.length; i++) { | ||
el.addEventListener(this.data.endEvents[i], this.onButtonUp); | ||
} | ||
} else { | ||
el.addEventListener(data.button + 'down', this.onButtonDown); | ||
el.addEventListener(data.button + 'up', this.onButtonUp); | ||
} | ||
this.queryCollisionEntities(); | ||
@@ -173,2 +198,7 @@ }, | ||
var p0 = new THREE.Vector3(); | ||
var v0 = new THREE.Vector3(); | ||
var g = -9.8; | ||
var a = new THREE.Vector3(0, g, 0); | ||
var next = new THREE.Vector3(); | ||
var last = new THREE.Vector3(); | ||
var quaternion = new THREE.Quaternion(); | ||
@@ -179,2 +209,3 @@ var translation = new THREE.Vector3(); | ||
var lastNext = new THREE.Vector3(); | ||
var auxDirection = new THREE.Vector3(); | ||
@@ -189,7 +220,6 @@ return function (time, delta) { | ||
.applyQuaternion(quaternion).normalize(); | ||
this.line.setDirection(direction.clone()); | ||
p0.copy(this.obj.getWorldPosition()); | ||
this.line.setDirection(auxDirection.copy(direction)); | ||
this.obj.getWorldPosition(p0); | ||
var last = p0.clone(); | ||
var next; | ||
last.copy(p0); | ||
@@ -203,9 +233,7 @@ // Set default status as non-hit | ||
if (this.data.type === 'parabolic') { | ||
var v0 = direction.clone().multiplyScalar(this.data.curveShootingSpeed); | ||
var g = -9.8; | ||
var a = new THREE.Vector3(0, g, 0); | ||
v0.copy(direction).multiplyScalar(this.data.curveShootingSpeed); | ||
for (var i = 0; i < this.line.numPoints; i++) { | ||
var t = i / (this.line.numPoints - 1); | ||
next = parabolicCurve(p0, v0, a, t); | ||
parabolicCurve(p0, v0, a, t, next); | ||
// Update the raycaster with the length of the current segment last->next | ||
@@ -220,5 +248,4 @@ var dirLastNext = lastNext.copy(next).sub(last).normalize(); | ||
} else if (this.data.type === 'line') { | ||
next = last.add(direction.clone().multiplyScalar(this.data.maxLength)); | ||
next.copy(last).add(auxDirection.copy(direction).multiplyScalar(this.data.maxLength)); | ||
this.raycaster.far = this.data.maxLength; | ||
this.raycaster.set(p0, direction); | ||
@@ -243,3 +270,3 @@ this.line.setPoint(0, p0); | ||
this.collisionEntities = []; | ||
return | ||
return; | ||
} | ||
@@ -275,52 +302,60 @@ | ||
*/ | ||
onButtonUp: function (evt) { | ||
if (!this.active) { return; } | ||
onButtonUp: (function () { | ||
const teleportOriginWorldPosition = new THREE.Vector3(); | ||
const newRigLocalPosition = new THREE.Vector3(); | ||
const newHandPosition = new THREE.Vector3(); | ||
const handPosition = new THREE.Vector3(); | ||
// Jump! | ||
return function (evt) { | ||
if (!this.active) { return; } | ||
// Hide the hit point and the curve | ||
this.active = false; | ||
this.hitEntity.setAttribute('visible', false); | ||
this.teleportEntity.setAttribute('visible', false); | ||
// Hide the hit point and the curve | ||
this.active = false; | ||
this.hitEntity.setAttribute('visible', false); | ||
this.teleportEntity.setAttribute('visible', false); | ||
if (!this.hit) { | ||
// Button released but not hit point | ||
return; | ||
} | ||
if (!this.hit) { | ||
// Button released but not hit point | ||
return; | ||
} | ||
// @todo Create this aux vectors outside | ||
if (this.data.cameraRig) { | ||
var cameraRigPosition = new THREE.Vector3().copy(this.data.cameraRig.getAttribute('position')); | ||
var newCameraRigPositionY = cameraRigPosition.y + this.hitPoint.y - this.prevHeightDiff; | ||
var newCameraRigPosition = new THREE.Vector3(this.hitPoint.x, newCameraRigPositionY, this.hitPoint.z); | ||
this.prevHeightDiff = this.hitPoint.y; | ||
this.data.cameraRig.setAttribute('position', newCameraRigPosition); | ||
} else { | ||
var cameraEl = this.el.sceneEl.camera.el; | ||
var camPosition = new THREE.Vector3().copy(cameraEl.getAttribute('position')); | ||
const rig = this.data.cameraRig || this.el.sceneEl.camera.el; | ||
rig.object3D.getWorldPosition(this.rigWorldPosition); | ||
this.newRigWorldPosition.copy(this.hitPoint); | ||
var newCamPositionY = camPosition.y + this.hitPoint.y - this.prevHeightDiff; | ||
var newCamPosition = new THREE.Vector3(this.hitPoint.x, newCamPositionY, this.hitPoint.z); | ||
this.prevHeightDiff = this.hitPoint.y; | ||
// If a teleportOrigin exists, offset the rig such that the teleportOrigin is above the hitPoint | ||
const teleportOrigin = this.data.teleportOrigin; | ||
if (teleportOrigin) { | ||
teleportOrigin.object3D.getWorldPosition(teleportOriginWorldPosition); | ||
this.newRigWorldPosition.sub(teleportOriginWorldPosition).add(this.rigWorldPosition); | ||
} | ||
cameraEl.setAttribute('position', newCamPosition); | ||
// Always keep the rig at the same offset off the ground after teleporting | ||
this.newRigWorldPosition.y = this.rigWorldPosition.y + this.hitPoint.y - this.prevHitHeight; | ||
this.prevHitHeight = this.hitPoint.y; | ||
// Find the hands and move them proportionally | ||
var hands = document.querySelectorAll('a-entity[tracked-controls]'); | ||
for (var i = 0; i < hands.length; i++) { | ||
var position = hands[i].getAttribute('position'); | ||
var pos = new THREE.Vector3().copy(position); | ||
var diff = camPosition.clone().sub(pos); | ||
var newPosition = newCamPosition.clone().sub(diff); | ||
hands[i].setAttribute('position', newPosition); | ||
// Finally update the rigs position | ||
newRigLocalPosition.copy(this.newRigWorldPosition); | ||
if (rig.object3D.parent) { | ||
rig.object3D.parent.worldToLocal(newRigLocalPosition); | ||
} | ||
} | ||
rig.setAttribute('position', newRigLocalPosition); | ||
this.el.emit('teleport', { | ||
oldPosition: camPosition, | ||
newPosition: newCamPosition, | ||
hitPoint: this.hitPoint | ||
}); | ||
}, | ||
// If a rig was not explicitly declared, look for hands and mvoe them proportionally as well | ||
if (!this.data.cameraRig) { | ||
var hands = document.querySelectorAll('a-entity[tracked-controls]'); | ||
for (var i = 0; i < hands.length; i++) { | ||
hands[i].object3D.getWorldPosition(handPosition); | ||
// diff = rigWorldPosition - handPosition | ||
// newPos = newRigWorldPosition - diff | ||
newHandPosition.copy(this.newRigWorldPosition).sub(this.rigWorldPosition).add(handPosition); | ||
hands[i].setAttribute('position', newHandPosition); | ||
} | ||
} | ||
this.el.emit('teleported', this.teleportEventDetail); | ||
}; | ||
})(), | ||
/** | ||
@@ -334,7 +369,6 @@ * Check for raycaster intersection. | ||
checkMeshCollisions: function (i, next) { | ||
var intersects; | ||
var meshes; | ||
// Gather the meshes here to avoid having to wait for entities to iniitalize. | ||
meshes = this.collisionEntities.map(function (entity) { | ||
// @todo We should add a property to define if the collisionEntity is dynamic or static | ||
// If static we should do the map just once, otherwise we're recreating the array in every | ||
// loop when aiming. | ||
var meshes = this.collisionEntities.map(function (entity) { | ||
return entity.getObject3D('mesh'); | ||
@@ -344,3 +378,3 @@ }).filter(function (n) { return n; }); | ||
intersects = this.raycaster.intersectObjects(meshes, true); | ||
var intersects = this.raycaster.intersectObjects(meshes, true); | ||
if (intersects.length > 0 && !this.hit && | ||
@@ -432,9 +466,8 @@ this.isValidNormalsAngle(intersects[0].face.normal)) { | ||
function createDefaultPlane () { | ||
function createDefaultPlane (size) { | ||
var geometry; | ||
var material; | ||
// @hack: Because I can't get THREE.BufferPlane working on raycaster. | ||
geometry = new THREE.BoxBufferGeometry(100, 0.5, 100); | ||
geometry.applyMatrix(new THREE.Matrix4().makeTranslation(0, -0.25, 0)); | ||
geometry = new THREE.PlaneBufferGeometry(100, 100); | ||
geometry.rotateX(-Math.PI / 2); | ||
material = new THREE.MeshBasicMaterial({color: 0xffff00}); | ||
@@ -463,8 +496,7 @@ return new THREE.Mesh(geometry, material); | ||
// Parabolic motion equation applied to 3 dimensions | ||
function parabolicCurve (p0, v0, a, t) { | ||
var ret = new THREE.Vector3(); | ||
ret.x = parabolicCurveScalar(p0.x, v0.x, a.x, t); | ||
ret.y = parabolicCurveScalar(p0.y, v0.y, a.y, t); | ||
ret.z = parabolicCurveScalar(p0.z, v0.z, a.z, t); | ||
return ret; | ||
function parabolicCurve (p0, v0, a, t, out) { | ||
out.x = parabolicCurveScalar(p0.x, v0.x, a.x, t); | ||
out.y = parabolicCurveScalar(p0.y, v0.y, a.y, t); | ||
out.z = parabolicCurveScalar(p0.z, v0.z, a.z, t); | ||
return out; | ||
} | ||
@@ -471,0 +503,0 @@ |
@@ -1,1 +0,1 @@ | ||
!function(t){function e(n){if(i[n])return i[n].exports;var r=i[n]={exports:{},id:n,loaded:!1};return t[n].call(r.exports,r,r.exports,e),r.loaded=!0,r.exports}var i={};return e.m=t,e.c=i,e.p="",e(0)}([function(t,e,i){function n(t){var e="line"===t.type?2:t.curveNumberPoints;return new h(e,t.curveLineWidth)}function r(t){var e,i,n;return i=document.createElement("a-entity"),i.className="hitEntity",n=document.createElement("a-entity"),n.setAttribute("geometry",{primitive:"torus",radius:t.hitCylinderRadius,radiusTubular:.01}),n.setAttribute("rotation",{x:90,y:0,z:0}),n.setAttribute("material",{shader:"flat",color:t.hitCylinderColor,side:"double",depthTest:!1}),i.appendChild(n),e=document.createElement("a-entity"),e.setAttribute("position",{x:0,y:t.hitCylinderHeight/2,z:0}),e.setAttribute("geometry",{primitive:"cylinder",segmentsHeight:1,radius:t.hitCylinderRadius,height:t.hitCylinderHeight,openEnded:!0}),e.setAttribute("material",{shader:"flat",color:t.hitCylinderColor,side:"double",src:s,transparent:!0,depthTest:!1}),i.appendChild(e),i}function o(){var t,e;return t=new THREE.BoxBufferGeometry(100,.5,100),t.applyMatrix((new THREE.Matrix4).makeTranslation(0,-.25,0)),e=new THREE.MeshBasicMaterial({color:16776960}),new THREE.Mesh(t,e)}var s=i(3),a=i(1),h=i(2);if("undefined"==typeof AFRAME)throw new Error("Component attempted to register before AFRAME was available.");Element.prototype.matches||(Element.prototype.matches=Element.prototype.matchesSelector||Element.prototype.mozMatchesSelector||Element.prototype.msMatchesSelector||Element.prototype.oMatchesSelector||Element.prototype.webkitMatchesSelector||function(t){for(var e=(this.document||this.ownerDocument).querySelectorAll(t),i=e.length;--i>=0&&e.item(i)!==this;);return i>-1}),AFRAME.registerComponent("teleport-controls",{schema:{type:{default:"parabolic",oneOf:["parabolic","line"]},button:{default:"trackpad",oneOf:["trackpad","trigger","grip","menu"]},collisionEntities:{default:""},hitEntity:{type:"selector"},cameraRig:{type:"selector"},hitCylinderColor:{type:"color",default:"#99ff99"},hitCylinderRadius:{default:.25,min:0},hitCylinderHeight:{default:.3,min:0},maxLength:{default:10,min:0,if:{type:["line"]}},curveNumberPoints:{default:30,min:2,if:{type:["parabolic"]}},curveLineWidth:{default:.025},curveHitColor:{type:"color",default:"#99ff99"},curveMissColor:{type:"color",default:"#ff0000"},curveShootingSpeed:{default:5,min:0,if:{type:["parabolic"]}},landingNormal:{type:"vec3",default:"0 1 0"},landingMaxAngle:{default:"45",min:0,max:360}},init:function(){var t,e=this.data,i=this.el;this.active=!1,this.obj=i.object3D,this.hitPoint=new THREE.Vector3,this.hit=!1,this.prevHeightDiff=0,this.referenceNormal=new THREE.Vector3,this.curveMissColor=new THREE.Color,this.curveHitColor=new THREE.Color,this.raycaster=new THREE.Raycaster,this.defaultPlane=o(),t=this.teleportEntity=document.createElement("a-entity"),t.classList.add("teleportRay"),t.setAttribute("visible",!1),i.sceneEl.appendChild(this.teleportEntity),i.addEventListener(e.button+"down",this.onButtonDown.bind(this)),i.addEventListener(e.button+"up",this.onButtonUp.bind(this)),this.queryCollisionEntities()},update:function(t){var e=this.data,i=AFRAME.utils.diff(e,t);this.referenceNormal.copy(e.landingNormal),this.curveMissColor.set(e.curveMissColor),this.curveHitColor.set(e.curveHitColor),(!this.line||"curveLineWidth"in i||"curveNumberPoints"in i||"type"in i)&&(this.line=n(e),this.teleportEntity.setObject3D("mesh",this.line.mesh)),e.hitEntity?this.hitEntity=e.hitEntity:(!this.hitEntity||"hitCylinderColor"in i||"hitCylinderHeight"in i||"hitCylinderRadius"in i)&&(this.hitEntity&&this.hitEntity.parentNode.removeChild(this.hitEntity),this.hitEntity=r(e),this.el.sceneEl.appendChild(this.hitEntity)),this.hitEntity.setAttribute("visible",!1),"collisionEntities"in i&&this.queryCollisionEntities()},remove:function(){var t=this.el,e=this.hitEntity,i=this.teleportEntity;e&&e.parentNode.removeChild(e),i&&i.parentNode.removeChild(i),t.sceneEl.removeEventListener("child-attached",this.childAttachHandler),t.sceneEl.removeEventListener("child-detached",this.childDetachHandler)},tick:function(){var t=new THREE.Vector3,e=new THREE.Quaternion,i=new THREE.Vector3,n=new THREE.Vector3,r=new THREE.Vector3,o=new THREE.Vector3;return function(s,h){if(this.active){var l=this.obj.matrixWorld;l.decompose(i,e,n);var c=r.set(0,0,-1).applyQuaternion(e).normalize();this.line.setDirection(c.clone()),t.copy(this.obj.getWorldPosition());var u,d=t.clone();if(this.teleportEntity.setAttribute("visible",!0),this.line.material.color.set(this.curveMissColor),this.hitEntity.setAttribute("visible",!1),this.hit=!1,"parabolic"===this.data.type)for(var A=c.clone().multiplyScalar(this.data.curveShootingSpeed),E=-9.8,p=new THREE.Vector3(0,E,0),y=0;y<this.line.numPoints;y++){var m=y/(this.line.numPoints-1);u=a(t,A,p,m);var f=o.copy(u).sub(d).normalize();if(this.raycaster.far=f.length(),this.raycaster.set(d,f),this.checkMeshCollisions(y,u))break;d.copy(u)}else"line"===this.data.type&&(u=d.add(c.clone().multiplyScalar(this.data.maxLength)),this.raycaster.far=this.data.maxLength,this.raycaster.set(t,c),this.line.setPoint(0,t),this.checkMeshCollisions(1,u))}}}(),queryCollisionEntities:function(){var t,e=this.data,i=this.el;return e.collisionEntities?(t=[].slice.call(i.sceneEl.querySelectorAll(e.collisionEntities)),this.collisionEntities=t,this.childAttachHandler=function(i){i.detail.el.matches(e.collisionEntities)&&t.push(i.detail.el)},i.sceneEl.addEventListener("child-attached",this.childAttachHandler),this.childDetachHandler=function(i){var n;i.detail.el.matches(e.collisionEntities)&&(n=t.indexOf(i.detail.el),n!==-1&&t.splice(n,1))},void i.sceneEl.addEventListener("child-detached",this.childDetachHandler)):void(this.collisionEntities=[])},onButtonDown:function(){this.active=!0},onButtonUp:function(t){if(this.active&&(this.active=!1,this.hitEntity.setAttribute("visible",!1),this.teleportEntity.setAttribute("visible",!1),this.hit)){if(this.data.cameraRig){var e=(new THREE.Vector3).copy(this.data.cameraRig.getAttribute("position")),i=e.y+this.hitPoint.y-this.prevHeightDiff,n=new THREE.Vector3(this.hitPoint.x,i,this.hitPoint.z);this.prevHeightDiff=this.hitPoint.y,this.data.cameraRig.setAttribute("position",n)}else{var r=this.el.sceneEl.camera.el,o=(new THREE.Vector3).copy(r.getAttribute("position")),s=o.y+this.hitPoint.y-this.prevHeightDiff,a=new THREE.Vector3(this.hitPoint.x,s,this.hitPoint.z);this.prevHeightDiff=this.hitPoint.y,r.setAttribute("position",a);for(var h=document.querySelectorAll("a-entity[tracked-controls]"),l=0;l<h.length;l++){var c=h[l].getAttribute("position"),u=(new THREE.Vector3).copy(c),d=o.clone().sub(u),A=a.clone().sub(d);h[l].setAttribute("position",A)}}this.el.emit("teleport",{oldPosition:o,newPosition:a,hitPoint:this.hitPoint})}},checkMeshCollisions:function(t,e){var i,n;if(n=this.collisionEntities.map(function(t){return t.getObject3D("mesh")}).filter(function(t){return t}),n=n.length?n:[this.defaultPlane],i=this.raycaster.intersectObjects(n,!0),i.length>0&&!this.hit&&this.isValidNormalsAngle(i[0].face.normal)){var r=i[0].point;this.line.material.color.set(this.curveHitColor),this.hitEntity.setAttribute("position",r),this.hitEntity.setAttribute("visible",!0),this.hit=!0,this.hitPoint.copy(i[0].point);for(var o=t;o<this.line.numPoints;o++)this.line.setPoint(o,this.hitPoint);return!0}return this.line.setPoint(t,e),!1},isValidNormalsAngle:function(t){var e=this.referenceNormal.angleTo(t);return THREE.Math.RAD2DEG*e<=this.data.landingMaxAngle}})},function(t,e){function i(t,e,i,n){return t+e*n+.5*i*n*n}function n(t,e,n,r){var o=new THREE.Vector3;return o.x=i(t.x,e.x,n.x,r),o.y=i(t.y,e.y,n.y,r),o.z=i(t.z,e.z,n.z,r),o}t.exports=n},function(t,e){var i=function(t,e){this.geometry=new THREE.BufferGeometry,this.vertices=new Float32Array(3*t*2),this.uvs=new Float32Array(2*t*2),this.width=e,this.geometry.addAttribute("position",new THREE.BufferAttribute(this.vertices,3).setDynamic(!0)),this.material=new THREE.MeshBasicMaterial({side:THREE.DoubleSide,color:16711680}),this.mesh=new THREE.Mesh(this.geometry,this.material),this.mesh.drawMode=THREE.TriangleStripDrawMode,this.mesh.frustumCulled=!1,this.mesh.vertices=this.vertices,this.direction=new THREE.Vector3,this.numPoints=t};i.prototype={setDirection:function(t){var e=new THREE.Vector3(0,1,0);this.direction.copy(t).cross(e).normalize().multiplyScalar(this.width/2)},setWidth:function(t){this.width=t},setPoint:function(){var t=new THREE.Vector3,e=new THREE.Vector3;return function(i,n){t.copy(n).add(this.direction),e.copy(n).sub(this.direction);var r=6*i;this.vertices[r++]=t.x,this.vertices[r++]=t.y,this.vertices[r++]=t.z,this.vertices[r++]=e.x,this.vertices[r++]=e.y,this.vertices[r++]=e.z,this.geometry.attributes.position.needsUpdate=!0}}()},t.exports=i},function(t,e){t.exports="url(data:image/png;base64,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)"}]); | ||
!function(t){function e(n){if(i[n])return i[n].exports;var o=i[n]={exports:{},id:n,loaded:!1};return t[n].call(o.exports,o,o.exports,e),o.loaded=!0,o.exports}var i={};return e.m=t,e.c=i,e.p="",e(0)}([function(t,e,i){function n(t){var e="line"===t.type?2:t.curveNumberPoints;return new l(e,t.curveLineWidth)}function o(t){var e,i,n;return i=document.createElement("a-entity"),i.className="hitEntity",n=document.createElement("a-entity"),n.setAttribute("geometry",{primitive:"torus",radius:t.hitCylinderRadius,radiusTubular:.01}),n.setAttribute("rotation",{x:90,y:0,z:0}),n.setAttribute("material",{shader:"flat",color:t.hitCylinderColor,side:"double",depthTest:!1}),i.appendChild(n),e=document.createElement("a-entity"),e.setAttribute("position",{x:0,y:t.hitCylinderHeight/2,z:0}),e.setAttribute("geometry",{primitive:"cylinder",segmentsHeight:1,radius:t.hitCylinderRadius,height:t.hitCylinderHeight,openEnded:!0}),e.setAttribute("material",{shader:"flat",color:t.hitCylinderColor,side:"double",src:s,transparent:!0,depthTest:!1}),i.appendChild(e),i}function r(t){var e,i;return e=new THREE.PlaneBufferGeometry(100,100),e.rotateX(-Math.PI/2),i=new THREE.MeshBasicMaterial({color:16776960}),new THREE.Mesh(e,i)}var s=i(3),a=i(1),l=i(2);if("undefined"==typeof AFRAME)throw new Error("Component attempted to register before AFRAME was available.");Element.prototype.matches||(Element.prototype.matches=Element.prototype.matchesSelector||Element.prototype.mozMatchesSelector||Element.prototype.msMatchesSelector||Element.prototype.oMatchesSelector||Element.prototype.webkitMatchesSelector||function(t){for(var e=(this.document||this.ownerDocument).querySelectorAll(t),i=e.length;--i>=0&&e.item(i)!==this;);return i>-1}),AFRAME.registerComponent("teleport-controls",{schema:{type:{default:"parabolic",oneOf:["parabolic","line"]},button:{default:"trackpad",oneOf:["trackpad","trigger","grip","menu"]},startEvents:{type:"array"},endEvents:{type:"array"},collisionEntities:{default:""},hitEntity:{type:"selector"},cameraRig:{type:"selector"},teleportOrigin:{type:"selector"},hitCylinderColor:{type:"color",default:"#99ff99"},hitCylinderRadius:{default:.25,min:0},hitCylinderHeight:{default:.3,min:0},maxLength:{default:10,min:0,if:{type:["line"]}},curveNumberPoints:{default:30,min:2,if:{type:["parabolic"]}},curveLineWidth:{default:.025},curveHitColor:{type:"color",default:"#99ff99"},curveMissColor:{type:"color",default:"#ff0000"},curveShootingSpeed:{default:5,min:0,if:{type:["parabolic"]}},defaultPlaneSize:{default:100},landingNormal:{type:"vec3",default:"0 1 0"},landingMaxAngle:{default:"45",min:0,max:360}},init:function(){var t,e,i=this.data,n=this.el;if(this.active=!1,this.obj=n.object3D,this.hitPoint=new THREE.Vector3,this.rigWorldPosition=new THREE.Vector3,this.newRigWorldPosition=new THREE.Vector3,this.teleportEventDetail={oldPosition:this.rigWorldPosition,newPosition:this.newRigWorldPosition,hitPoint:this.hitPoint},this.hit=!1,this.prevHitHeight=0,this.referenceNormal=new THREE.Vector3,this.curveMissColor=new THREE.Color,this.curveHitColor=new THREE.Color,this.raycaster=new THREE.Raycaster,this.defaultPlane=r(this.data.defaultPlaneSize),t=this.teleportEntity=document.createElement("a-entity"),t.classList.add("teleportRay"),t.setAttribute("visible",!1),n.sceneEl.appendChild(this.teleportEntity),this.onButtonDown=this.onButtonDown.bind(this),this.onButtonUp=this.onButtonUp.bind(this),this.data.startEvents.length&&this.data.endEvents.length){for(e=0;e<this.data.startEvents.length;e++)n.addEventListener(this.data.startEvents[e],this.onButtonDown);for(e=0;e<this.data.endEvents.length;e++)n.addEventListener(this.data.endEvents[e],this.onButtonUp)}else n.addEventListener(i.button+"down",this.onButtonDown),n.addEventListener(i.button+"up",this.onButtonUp);this.queryCollisionEntities()},update:function(t){var e=this.data,i=AFRAME.utils.diff(e,t);this.referenceNormal.copy(e.landingNormal),this.curveMissColor.set(e.curveMissColor),this.curveHitColor.set(e.curveHitColor),(!this.line||"curveLineWidth"in i||"curveNumberPoints"in i||"type"in i)&&(this.line=n(e),this.teleportEntity.setObject3D("mesh",this.line.mesh)),e.hitEntity?this.hitEntity=e.hitEntity:(!this.hitEntity||"hitCylinderColor"in i||"hitCylinderHeight"in i||"hitCylinderRadius"in i)&&(this.hitEntity&&this.hitEntity.parentNode.removeChild(this.hitEntity),this.hitEntity=o(e),this.el.sceneEl.appendChild(this.hitEntity)),this.hitEntity.setAttribute("visible",!1),"collisionEntities"in i&&this.queryCollisionEntities()},remove:function(){var t=this.el,e=this.hitEntity,i=this.teleportEntity;e&&e.parentNode.removeChild(e),i&&i.parentNode.removeChild(i),t.sceneEl.removeEventListener("child-attached",this.childAttachHandler),t.sceneEl.removeEventListener("child-detached",this.childDetachHandler)},tick:function(){var t=new THREE.Vector3,e=new THREE.Vector3,i=-9.8,n=new THREE.Vector3(0,i,0),o=new THREE.Vector3,r=new THREE.Vector3,s=new THREE.Quaternion,l=new THREE.Vector3,h=new THREE.Vector3,c=new THREE.Vector3,d=new THREE.Vector3,u=new THREE.Vector3;return function(i,E){if(this.active){var A=this.obj.matrixWorld;A.decompose(l,s,h);var p=c.set(0,0,-1).applyQuaternion(s).normalize();if(this.line.setDirection(u.copy(p)),this.obj.getWorldPosition(t),r.copy(t),this.teleportEntity.setAttribute("visible",!0),this.line.material.color.set(this.curveMissColor),this.hitEntity.setAttribute("visible",!1),this.hit=!1,"parabolic"===this.data.type){e.copy(p).multiplyScalar(this.data.curveShootingSpeed);for(var y=0;y<this.line.numPoints;y++){var v=y/(this.line.numPoints-1);a(t,e,n,v,o);var m=d.copy(o).sub(r).normalize();if(this.raycaster.far=m.length(),this.raycaster.set(r,m),this.checkMeshCollisions(y,o))break;r.copy(o)}}else"line"===this.data.type&&(o.copy(r).add(u.copy(p).multiplyScalar(this.data.maxLength)),this.raycaster.far=this.data.maxLength,this.raycaster.set(t,p),this.line.setPoint(0,t),this.checkMeshCollisions(1,o))}}}(),queryCollisionEntities:function(){var t,e=this.data,i=this.el;return e.collisionEntities?(t=[].slice.call(i.sceneEl.querySelectorAll(e.collisionEntities)),this.collisionEntities=t,this.childAttachHandler=function(i){i.detail.el.matches(e.collisionEntities)&&t.push(i.detail.el)},i.sceneEl.addEventListener("child-attached",this.childAttachHandler),this.childDetachHandler=function(i){var n;i.detail.el.matches(e.collisionEntities)&&(n=t.indexOf(i.detail.el),n!==-1&&t.splice(n,1))},void i.sceneEl.addEventListener("child-detached",this.childDetachHandler)):void(this.collisionEntities=[])},onButtonDown:function(){this.active=!0},onButtonUp:function(){const t=new THREE.Vector3,e=new THREE.Vector3,i=new THREE.Vector3,n=new THREE.Vector3;return function(o){if(this.active&&(this.active=!1,this.hitEntity.setAttribute("visible",!1),this.teleportEntity.setAttribute("visible",!1),this.hit)){const r=this.data.cameraRig||this.el.sceneEl.camera.el;r.object3D.getWorldPosition(this.rigWorldPosition),this.newRigWorldPosition.copy(this.hitPoint);const s=this.data.teleportOrigin;if(s&&(s.object3D.getWorldPosition(t),this.newRigWorldPosition.sub(t).add(this.rigWorldPosition)),this.newRigWorldPosition.y=this.rigWorldPosition.y+this.hitPoint.y-this.prevHitHeight,this.prevHitHeight=this.hitPoint.y,e.copy(this.newRigWorldPosition),r.object3D.parent&&r.object3D.parent.worldToLocal(e),r.setAttribute("position",e),!this.data.cameraRig)for(var a=document.querySelectorAll("a-entity[tracked-controls]"),l=0;l<a.length;l++)a[l].object3D.getWorldPosition(n),i.copy(this.newRigWorldPosition).sub(this.rigWorldPosition).add(n),a[l].setAttribute("position",i);this.el.emit("teleported",this.teleportEventDetail)}}}(),checkMeshCollisions:function(t,e){var i=this.collisionEntities.map(function(t){return t.getObject3D("mesh")}).filter(function(t){return t});i=i.length?i:[this.defaultPlane];var n=this.raycaster.intersectObjects(i,!0);if(n.length>0&&!this.hit&&this.isValidNormalsAngle(n[0].face.normal)){var o=n[0].point;this.line.material.color.set(this.curveHitColor),this.hitEntity.setAttribute("position",o),this.hitEntity.setAttribute("visible",!0),this.hit=!0,this.hitPoint.copy(n[0].point);for(var r=t;r<this.line.numPoints;r++)this.line.setPoint(r,this.hitPoint);return!0}return this.line.setPoint(t,e),!1},isValidNormalsAngle:function(t){var e=this.referenceNormal.angleTo(t);return THREE.Math.RAD2DEG*e<=this.data.landingMaxAngle}})},function(t,e){function i(t,e,i,n){return t+e*n+.5*i*n*n}function n(t,e,n,o,r){return r.x=i(t.x,e.x,n.x,o),r.y=i(t.y,e.y,n.y,o),r.z=i(t.z,e.z,n.z,o),r}t.exports=n},function(t,e){var i=function(t,e){this.geometry=new THREE.BufferGeometry,this.vertices=new Float32Array(3*t*2),this.uvs=new Float32Array(2*t*2),this.width=e,this.geometry.addAttribute("position",new THREE.BufferAttribute(this.vertices,3).setDynamic(!0)),this.material=new THREE.MeshBasicMaterial({side:THREE.DoubleSide,color:16711680}),this.mesh=new THREE.Mesh(this.geometry,this.material),this.mesh.drawMode=THREE.TriangleStripDrawMode,this.mesh.frustumCulled=!1,this.mesh.vertices=this.vertices,this.direction=new THREE.Vector3,this.numPoints=t};i.prototype={setDirection:function(t){var e=new THREE.Vector3(0,1,0);this.direction.copy(t).cross(e).normalize().multiplyScalar(this.width/2)},setWidth:function(t){this.width=t},setPoint:function(){var t=new THREE.Vector3,e=new THREE.Vector3;return function(i,n){t.copy(n).add(this.direction),e.copy(n).sub(this.direction);var o=6*i;this.vertices[o++]=t.x,this.vertices[o++]=t.y,this.vertices[o++]=t.z,this.vertices[o++]=e.x,this.vertices[o++]=e.y,this.vertices[o++]=e.z,this.geometry.attributes.position.needsUpdate=!0}}()},t.exports=i},function(t,e){t.exports="url(data:image/png;base64,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)"}]); |
165
index.js
@@ -1,2 +0,2 @@ | ||
/* global THREE, AFRAME */ | ||
/* global THREE, AFRAME, Element */ | ||
var cylinderTexture = require('./lib/cylinderTexture'); | ||
@@ -29,5 +29,8 @@ var parabolicCurve = require('./lib/ParabolicCurve'); | ||
button: {default: 'trackpad', oneOf: ['trackpad', 'trigger', 'grip', 'menu']}, | ||
startEvents: {type: 'array'}, | ||
endEvents: {type: 'array'}, | ||
collisionEntities: {default: ''}, | ||
hitEntity: {type: 'selector'}, | ||
cameraRig: {type: 'selector'}, | ||
teleportOrigin: {type: 'selector'}, | ||
hitCylinderColor: {type: 'color', default: '#99ff99'}, | ||
@@ -42,2 +45,3 @@ hitCylinderRadius: {default: 0.25, min: 0}, | ||
curveShootingSpeed: {default: 5, min: 0, if: {type: ['parabolic']}}, | ||
defaultPlaneSize: { default: 100 }, | ||
landingNormal: {type: 'vec3', default: '0 1 0'}, | ||
@@ -51,2 +55,3 @@ landingMaxAngle: {default: '45', min: 0, max: 360} | ||
var teleportEntity; | ||
var i; | ||
@@ -56,4 +61,12 @@ this.active = false; | ||
this.hitPoint = new THREE.Vector3(); | ||
this.rigWorldPosition = new THREE.Vector3(); | ||
this.newRigWorldPosition = new THREE.Vector3(); | ||
this.teleportEventDetail = { | ||
oldPosition: this.rigWorldPosition, | ||
newPosition: this.newRigWorldPosition, | ||
hitPoint: this.hitPoint | ||
}; | ||
this.hit = false; | ||
this.prevHeightDiff = 0; | ||
this.prevHitHeight = 0; | ||
this.referenceNormal = new THREE.Vector3(); | ||
@@ -64,3 +77,3 @@ this.curveMissColor = new THREE.Color(); | ||
this.defaultPlane = createDefaultPlane(); | ||
this.defaultPlane = createDefaultPlane(this.data.defaultPlaneSize); | ||
@@ -72,5 +85,17 @@ teleportEntity = this.teleportEntity = document.createElement('a-entity'); | ||
el.addEventListener(data.button + 'down', this.onButtonDown.bind(this)); | ||
el.addEventListener(data.button + 'up', this.onButtonUp.bind(this)); | ||
this.onButtonDown = this.onButtonDown.bind(this); | ||
this.onButtonUp = this.onButtonUp.bind(this); | ||
if (this.data.startEvents.length && this.data.endEvents.length) { | ||
for (i = 0; i < this.data.startEvents.length; i++) { | ||
el.addEventListener(this.data.startEvents[i], this.onButtonDown); | ||
} | ||
for (i = 0; i < this.data.endEvents.length; i++) { | ||
el.addEventListener(this.data.endEvents[i], this.onButtonUp); | ||
} | ||
} else { | ||
el.addEventListener(data.button + 'down', this.onButtonDown); | ||
el.addEventListener(data.button + 'up', this.onButtonUp); | ||
} | ||
this.queryCollisionEntities(); | ||
@@ -126,2 +151,7 @@ }, | ||
var p0 = new THREE.Vector3(); | ||
var v0 = new THREE.Vector3(); | ||
var g = -9.8; | ||
var a = new THREE.Vector3(0, g, 0); | ||
var next = new THREE.Vector3(); | ||
var last = new THREE.Vector3(); | ||
var quaternion = new THREE.Quaternion(); | ||
@@ -132,2 +162,3 @@ var translation = new THREE.Vector3(); | ||
var lastNext = new THREE.Vector3(); | ||
var auxDirection = new THREE.Vector3(); | ||
@@ -142,7 +173,6 @@ return function (time, delta) { | ||
.applyQuaternion(quaternion).normalize(); | ||
this.line.setDirection(direction.clone()); | ||
p0.copy(this.obj.getWorldPosition()); | ||
this.line.setDirection(auxDirection.copy(direction)); | ||
this.obj.getWorldPosition(p0); | ||
var last = p0.clone(); | ||
var next; | ||
last.copy(p0); | ||
@@ -156,9 +186,7 @@ // Set default status as non-hit | ||
if (this.data.type === 'parabolic') { | ||
var v0 = direction.clone().multiplyScalar(this.data.curveShootingSpeed); | ||
var g = -9.8; | ||
var a = new THREE.Vector3(0, g, 0); | ||
v0.copy(direction).multiplyScalar(this.data.curveShootingSpeed); | ||
for (var i = 0; i < this.line.numPoints; i++) { | ||
var t = i / (this.line.numPoints - 1); | ||
next = parabolicCurve(p0, v0, a, t); | ||
parabolicCurve(p0, v0, a, t, next); | ||
// Update the raycaster with the length of the current segment last->next | ||
@@ -173,5 +201,4 @@ var dirLastNext = lastNext.copy(next).sub(last).normalize(); | ||
} else if (this.data.type === 'line') { | ||
next = last.add(direction.clone().multiplyScalar(this.data.maxLength)); | ||
next.copy(last).add(auxDirection.copy(direction).multiplyScalar(this.data.maxLength)); | ||
this.raycaster.far = this.data.maxLength; | ||
this.raycaster.set(p0, direction); | ||
@@ -196,3 +223,3 @@ this.line.setPoint(0, p0); | ||
this.collisionEntities = []; | ||
return | ||
return; | ||
} | ||
@@ -228,52 +255,60 @@ | ||
*/ | ||
onButtonUp: function (evt) { | ||
if (!this.active) { return; } | ||
onButtonUp: (function () { | ||
const teleportOriginWorldPosition = new THREE.Vector3(); | ||
const newRigLocalPosition = new THREE.Vector3(); | ||
const newHandPosition = new THREE.Vector3(); | ||
const handPosition = new THREE.Vector3(); | ||
// Jump! | ||
return function (evt) { | ||
if (!this.active) { return; } | ||
// Hide the hit point and the curve | ||
this.active = false; | ||
this.hitEntity.setAttribute('visible', false); | ||
this.teleportEntity.setAttribute('visible', false); | ||
// Hide the hit point and the curve | ||
this.active = false; | ||
this.hitEntity.setAttribute('visible', false); | ||
this.teleportEntity.setAttribute('visible', false); | ||
if (!this.hit) { | ||
// Button released but not hit point | ||
return; | ||
} | ||
if (!this.hit) { | ||
// Button released but not hit point | ||
return; | ||
} | ||
// @todo Create this aux vectors outside | ||
if (this.data.cameraRig) { | ||
var cameraRigPosition = new THREE.Vector3().copy(this.data.cameraRig.getAttribute('position')); | ||
var newCameraRigPositionY = cameraRigPosition.y + this.hitPoint.y - this.prevHeightDiff; | ||
var newCameraRigPosition = new THREE.Vector3(this.hitPoint.x, newCameraRigPositionY, this.hitPoint.z); | ||
this.prevHeightDiff = this.hitPoint.y; | ||
this.data.cameraRig.setAttribute('position', newCameraRigPosition); | ||
} else { | ||
var cameraEl = this.el.sceneEl.camera.el; | ||
var camPosition = new THREE.Vector3().copy(cameraEl.getAttribute('position')); | ||
const rig = this.data.cameraRig || this.el.sceneEl.camera.el; | ||
rig.object3D.getWorldPosition(this.rigWorldPosition); | ||
this.newRigWorldPosition.copy(this.hitPoint); | ||
var newCamPositionY = camPosition.y + this.hitPoint.y - this.prevHeightDiff; | ||
var newCamPosition = new THREE.Vector3(this.hitPoint.x, newCamPositionY, this.hitPoint.z); | ||
this.prevHeightDiff = this.hitPoint.y; | ||
// If a teleportOrigin exists, offset the rig such that the teleportOrigin is above the hitPoint | ||
const teleportOrigin = this.data.teleportOrigin; | ||
if (teleportOrigin) { | ||
teleportOrigin.object3D.getWorldPosition(teleportOriginWorldPosition); | ||
this.newRigWorldPosition.sub(teleportOriginWorldPosition).add(this.rigWorldPosition); | ||
} | ||
cameraEl.setAttribute('position', newCamPosition); | ||
// Always keep the rig at the same offset off the ground after teleporting | ||
this.newRigWorldPosition.y = this.rigWorldPosition.y + this.hitPoint.y - this.prevHitHeight; | ||
this.prevHitHeight = this.hitPoint.y; | ||
// Find the hands and move them proportionally | ||
var hands = document.querySelectorAll('a-entity[tracked-controls]'); | ||
for (var i = 0; i < hands.length; i++) { | ||
var position = hands[i].getAttribute('position'); | ||
var pos = new THREE.Vector3().copy(position); | ||
var diff = camPosition.clone().sub(pos); | ||
var newPosition = newCamPosition.clone().sub(diff); | ||
hands[i].setAttribute('position', newPosition); | ||
// Finally update the rigs position | ||
newRigLocalPosition.copy(this.newRigWorldPosition); | ||
if (rig.object3D.parent) { | ||
rig.object3D.parent.worldToLocal(newRigLocalPosition); | ||
} | ||
} | ||
rig.setAttribute('position', newRigLocalPosition); | ||
this.el.emit('teleport', { | ||
oldPosition: camPosition, | ||
newPosition: newCamPosition, | ||
hitPoint: this.hitPoint | ||
}); | ||
}, | ||
// If a rig was not explicitly declared, look for hands and mvoe them proportionally as well | ||
if (!this.data.cameraRig) { | ||
var hands = document.querySelectorAll('a-entity[tracked-controls]'); | ||
for (var i = 0; i < hands.length; i++) { | ||
hands[i].object3D.getWorldPosition(handPosition); | ||
// diff = rigWorldPosition - handPosition | ||
// newPos = newRigWorldPosition - diff | ||
newHandPosition.copy(this.newRigWorldPosition).sub(this.rigWorldPosition).add(handPosition); | ||
hands[i].setAttribute('position', newHandPosition); | ||
} | ||
} | ||
this.el.emit('teleported', this.teleportEventDetail); | ||
}; | ||
})(), | ||
/** | ||
@@ -287,7 +322,6 @@ * Check for raycaster intersection. | ||
checkMeshCollisions: function (i, next) { | ||
var intersects; | ||
var meshes; | ||
// Gather the meshes here to avoid having to wait for entities to iniitalize. | ||
meshes = this.collisionEntities.map(function (entity) { | ||
// @todo We should add a property to define if the collisionEntity is dynamic or static | ||
// If static we should do the map just once, otherwise we're recreating the array in every | ||
// loop when aiming. | ||
var meshes = this.collisionEntities.map(function (entity) { | ||
return entity.getObject3D('mesh'); | ||
@@ -297,3 +331,3 @@ }).filter(function (n) { return n; }); | ||
intersects = this.raycaster.intersectObjects(meshes, true); | ||
var intersects = this.raycaster.intersectObjects(meshes, true); | ||
if (intersects.length > 0 && !this.hit && | ||
@@ -385,11 +419,10 @@ this.isValidNormalsAngle(intersects[0].face.normal)) { | ||
function createDefaultPlane () { | ||
function createDefaultPlane (size) { | ||
var geometry; | ||
var material; | ||
// @hack: Because I can't get THREE.BufferPlane working on raycaster. | ||
geometry = new THREE.BoxBufferGeometry(100, 0.5, 100); | ||
geometry.applyMatrix(new THREE.Matrix4().makeTranslation(0, -0.25, 0)); | ||
geometry = new THREE.PlaneBufferGeometry(100, 100); | ||
geometry.rotateX(-Math.PI / 2); | ||
material = new THREE.MeshBasicMaterial({color: 0xffff00}); | ||
return new THREE.Mesh(geometry, material); | ||
} |
@@ -8,10 +8,9 @@ /* global THREE */ | ||
// Parabolic motion equation applied to 3 dimensions | ||
function parabolicCurve (p0, v0, a, t) { | ||
var ret = new THREE.Vector3(); | ||
ret.x = parabolicCurveScalar(p0.x, v0.x, a.x, t); | ||
ret.y = parabolicCurveScalar(p0.y, v0.y, a.y, t); | ||
ret.z = parabolicCurveScalar(p0.z, v0.z, a.z, t); | ||
return ret; | ||
function parabolicCurve (p0, v0, a, t, out) { | ||
out.x = parabolicCurveScalar(p0.x, v0.x, a.x, t); | ||
out.y = parabolicCurveScalar(p0.y, v0.y, a.y, t); | ||
out.z = parabolicCurveScalar(p0.z, v0.z, a.z, t); | ||
return out; | ||
} | ||
module.exports = parabolicCurve; |
{ | ||
"name": "aframe-teleport-controls", | ||
"version": "0.3.0", | ||
"version": "0.3.1", | ||
"description": "A-Frame teleport controls component", | ||
@@ -5,0 +5,0 @@ "author": "Fernando Serrano <fernandojsg@gmail.com> (http://fernandojsg.com/)", |
@@ -12,5 +12,7 @@ # aframe-teleport-controls | ||
| cameraRig | Selector of the camera Rig to teleport | | | ||
| teleportOrigin | Selector of the child of cameraRig to use as the center point for teleporting, typically the camera. If set teleporting will position the cameraRig such that this element ends up above the teleport location (rather than the center of the camreaRig) | | | ||
| type | Type of teleport: line or parabolic | parabolic | | ||
| button | Button used to launch the teleport | trackpad | | ||
| button | Button used to launch the teleport: trackpad, trigger, grip, menu | trackpad | | ||
| collisionEntities | Selector of the meshes used to check the collisions. If no value provided a plane Y=0 is used | | | ||
| endEvents | Paired with `startEvents`, list of events to listen for to finish teleporting.| [] | | ||
| hitEntity | Entity used to show at the hitting position. If no value provided a cylinder will be used as default. | | | ||
@@ -20,3 +22,2 @@ | hitCylinderColor | Color used for the default `hitEntity` primitives | #99ff99 | | ||
| hitCylinderHeight | Height used for the default `hitEntity` primitives | 0.3 | | ||
| maxLength | Max. length for line teleport | 10 | | ||
| curveHitColor | Color used for the curve when hit the mesh | #99ff99 | | ||
@@ -27,5 +28,7 @@ | curveMissColor | Color used for the curve when it doesn't hit anything | #ff0000 | | ||
| curveShootingSpeed | Curve shooting speed, as bigger value, farther distance. | 5 | | ||
| defaultPlaneSize | Default plane size | 100 | | ||
| maxLength | Max length of the ray when using type=line teleport | 10 | | ||
| landingNormal | Normal vector to detect collisions with the `collisionEntity` | (0, 1, 0) | | ||
| landingMaxAngle | Angle threshold (in degrees) used together with `landingNormal` to detect if the mesh is so steep to jump to it. | 45 | | ||
| startEvents | Alternative to `button`, list of events to listen to start teleporting.| [] | | ||
@@ -43,3 +46,3 @@ ### Usage | ||
<title>My A-Frame Scene</title> | ||
<script src="https://aframe.io/releases/0.4.0/aframe.min.js"></script> | ||
<script src="https://aframe.io/releases/0.7.0/aframe.min.js"></script> | ||
<script src="https://rawgit.com/fernandojsg/aframe-teleport-controls/master/dist/aframe-teleport-controls.min.js"></script> | ||
@@ -52,6 +55,6 @@ </head> | ||
<!-- camera --> | ||
<a-entity camera wasd-controls look-controls></a-entity> | ||
<a-entity id="head" camera wasd-controls look-controls></a-entity> | ||
<!-- hand controls --> | ||
<a-entity id="left-hand" teleport-controls="cameraRig: #cameraRig"></a-entity> | ||
<a-entity id="right-hand" teleport-controls="cameraRig: #cameraRig"></a-entity> | ||
<a-entity id="left-hand" teleport-controls="cameraRig: #cameraRig; teleportOrigin: #head;"></a-entity> | ||
<a-entity id="right-hand" teleport-controls="cameraRig: #cameraRig; teleportOrigin: #head;"></a-entity> | ||
</a-entity> | ||
@@ -62,2 +65,26 @@ </a-scene> | ||
To use this component with Gear VR, you need to add `gearvr-controls`: | ||
```html | ||
<a-scene> | ||
<a-entity id="cameraRig"> | ||
<a-camera /> | ||
<a-entity | ||
teleport-controls="cameraRig: #cameraRig" | ||
gearvr-controls | ||
/> | ||
</a-entity> | ||
</a-scene> | ||
``` | ||
You can also use the trigger button instead of trackpad button by adding `button: trigger`. | ||
For Daydream, replace `gearvr-controls` by `daydream-controls`. | ||
If you use [aframe-environment-component](https://github.com/feiss/aframe-environment-component) > 1.0.0 | ||
and want to teleport on the generated ground, on the hills, you can | ||
specify `collisionEntities: .environmentGround`. You can also add `.environmentDressing` if you want to teleport on the dressing like the mushrooms. | ||
On Gear VR, it can be very slow with the curved line. Use `maxLength: 200; type: line;` in this case. | ||
#### NPM Installation | ||
@@ -64,0 +91,0 @@ |
@@ -11,2 +11,4 @@ /* global assert, setup, suite, test */ | ||
var sceneEl; | ||
var rig; | ||
var teleportOrigin; | ||
@@ -21,2 +23,14 @@ setup(function (done) { | ||
}); | ||
rig = document.createElement('a-entity'); | ||
rig.id = 'rig'; | ||
rig.setAttribute('position', '1 2 3'); | ||
teleportOrigin = document.createElement('a-entity'); | ||
teleportOrigin.id = 'origin'; | ||
teleportOrigin.setAttribute('position', '4 5 6'); | ||
rig.appendChild(teleportOrigin); | ||
sceneEl.appendChild(rig); | ||
el.setAttribute('teleport-controls', {}); | ||
@@ -146,2 +160,20 @@ }); | ||
test('teleports rig', function () { | ||
el.setAttribute('teleport-controls', 'cameraRig', '#rig'); | ||
component.hitPoint = {x: 10, y: 20, z: 30}; | ||
component.onButtonUp(); | ||
assert.shallowDeepEqual(rig.getAttribute('position'), {x: 10, y: 22, z: 30}); | ||
}); | ||
test('teleports rig relative to teleportOrigin', function () { | ||
el.setAttribute('teleport-controls', 'cameraRig', '#rig'); | ||
el.setAttribute('teleport-controls', 'teleportOrigin', '#origin'); | ||
component.hitPoint = {x: 10, y: 20, z: 30}; | ||
component.onButtonUp(); | ||
assert.shallowDeepEqual(rig.getAttribute('position'), {x: 6, y: 22, z: 24}); | ||
assert.shallowDeepEqual(teleportOrigin.getAttribute('position'), {x: 4, y: 5, z: 6}); | ||
}); | ||
test('teleports tracked-controls', function (done) { | ||
@@ -148,0 +180,0 @@ var hand = document.createElement('a-entity'); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
4198970
8212
97