diff --git a/dist/aframe-teleport-controls.js b/dist/aframe-teleport-controls.js index 8ef6d908..8c80ff59 100644 --- a/dist/aframe-teleport-controls.js +++ b/dist/aframe-teleport-controls.js @@ -81,6 +81,7 @@ hitCylinderColor: {type: 'color', default: '#99ff99'}, hitCylinderRadius: {default: 0.25, min: 0}, hitCylinderHeight: {default: 0.3, min: 0}, + interval: {default: 0}, maxLength: {default: 10, min: 0, if: {type: ['line']}}, curveNumberPoints: {default: 30, min: 2, if: {type: ['parabolic']}}, curveLineWidth: {default: 0.025}, @@ -89,7 +90,11 @@ 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} + landingMaxAngle: {default: '45', min: 0, max: 360}, + drawIncrementally: {default: false}, + incrementalDrawMs: {default: 700}, + missOpacity: {default: 1.0}, + hitOpacity: {default: 1.0} }, init: function () { @@ -110,6 +115,7 @@ }; this.hit = false; + this.prevCheckTime = undefined; this.prevHitHeight = 0; this.referenceNormal = new THREE.Vector3(); this.curveMissColor = new THREE.Color(); @@ -117,6 +123,7 @@ this.raycaster = new THREE.Raycaster(); this.defaultPlane = createDefaultPlane(this.data.defaultPlaneSize); + this.defaultCollisionMeshes = [this.defaultPlane]; teleportEntity = this.teleportEntity = document.createElement('a-entity'); teleportEntity.classList.add('teleportRay'); @@ -152,10 +159,15 @@ this.curveMissColor.set(data.curveMissColor); this.curveHitColor.set(data.curveHitColor); + // Create or update line mesh. if (!this.line || 'curveLineWidth' in diff || 'curveNumberPoints' in diff || 'type' in diff) { + this.line = createLine(data); + this.line.material.opacity = this.data.hitOpacity; + this.line.material.transparent = this.data.hitOpacity < 1; + this.numActivePoints = data.curveNumberPoints; this.teleportEntity.setObject3D('mesh', this.line.mesh); } @@ -199,9 +211,24 @@ var shootAngle = new THREE.Vector3(); var lastNext = new THREE.Vector3(); var auxDirection = new THREE.Vector3(); + var timeSinceDrawStart = 0; return function (time, delta) { if (!this.active) { return; } + if (this.data.drawIncrementally && this.redrawLine){ + this.redrawLine = false; + timeSinceDrawStart = 0; + } + timeSinceDrawStart += delta; + this.numActivePoints = this.data.curveNumberPoints*timeSinceDrawStart/this.data.incrementalDrawMs; + if (this.numActivePoints > this.data.curveNumberPoints){ + this.numActivePoints = this.data.curveNumberPoints; + } + + // Only check for intersection if interval time has passed. + if (this.prevCheckTime && (time - this.prevCheckTime < this.data.interval)) { return; } + // Update check time. + this.prevCheckTime = time; var matrixWorld = this.obj.matrixWorld; matrixWorld.decompose(translation, quaternion, scale); @@ -216,23 +243,39 @@ // Set default status as non-hit this.teleportEntity.setAttribute('visible', true); this.line.material.color.set(this.curveMissColor); + this.line.material.opacity = this.data.missOpacity; + this.line.material.transparent = this.data.missOpacity < 1; this.hitEntity.setAttribute('visible', false); this.hit = false; if (this.data.type === 'parabolic') { v0.copy(direction).multiplyScalar(this.data.curveShootingSpeed); - for (var i = 0; i < this.line.numPoints; i++) { - var t = i / (this.line.numPoints - 1); + this.lastDrawnIndex = 0; + const numPoints = this.data.drawIncrementally ? this.numActivePoints : this.line.numPoints; + for (var i = 0; i < numPoints+1; i++) { + var t; + if (i == Math.floor(numPoints+1)){ + t = numPoints / (this.line.numPoints - 1); + } + else { + t = i / (this.line.numPoints - 1); + } parabolicCurve(p0, v0, a, t, next); // Update the raycaster with the length of the current segment last->next var dirLastNext = lastNext.copy(next).sub(last).normalize(); this.raycaster.far = dirLastNext.length(); this.raycaster.set(last, dirLastNext); + this.lastDrawnPoint = next; + this.lastDrawnIndex = i; if (this.checkMeshCollisions(i, next)) { break; } + last.copy(next); } + for (var j = this.lastDrawnIndex+1; j < this.line.numPoints; j++) { + this.line.setPoint(j, this.lastDrawnPoint); + } } else if (this.data.type === 'line') { next.copy(last).add(auxDirection.copy(direction).multiplyScalar(this.data.maxLength)); this.raycaster.far = this.data.maxLength; @@ -281,6 +324,7 @@ onButtonDown: function () { this.active = true; + this.redrawLine = true; }, /** @@ -355,10 +399,15 @@ // @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'); - }).filter(function (n) { return n; }); - meshes = meshes.length ? meshes : [this.defaultPlane]; + var meshes; + if (!this.data.collisionEntities) { + meshes = this.defaultCollisionMeshes; + } else { + meshes = this.collisionEntities.map(function (entity) { + return entity.getObject3D('mesh'); + }).filter(function (n) { return n; }); + meshes = meshes.length ? meshes : this.defaultCollisionMeshes; + } var intersects = this.raycaster.intersectObjects(meshes, true); if (intersects.length > 0 && !this.hit && @@ -366,6 +415,8 @@ var point = intersects[0].point; this.line.material.color.set(this.curveHitColor); + this.line.material.opacity = this.data.hitOpacity; + this.line.material.transparent= this.data.hitOpacity < 1; this.hitEntity.setAttribute('position', point); this.hitEntity.setAttribute('visible', true); diff --git a/dist/aframe-teleport-controls.min.js b/dist/aframe-teleport-controls.min.js index 43391f2d..9403f7ea 100644 --- a/dist/aframe-teleport-controls.min.js +++ b/dist/aframe-teleport-controls.min.js @@ -1 +1 @@ -!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;e0&&!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=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},interval:{default: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},drawIncrementally:{default:!1},incrementalDrawMs:{default:700},missOpacity:{default:1},hitOpacity:{default:1}},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.prevCheckTime=void 0,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=o(this.data.defaultPlaneSize),this.defaultCollisionMeshes=[this.defaultPlane],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;ethis.data.curveNumberPoints&&(this.numActivePoints=this.data.curveNumberPoints),!(this.prevCheckTime&&i-this.prevCheckTime0&&!this.hit&&this.isValidNormalsAngle(n[0].face.normal)){var s=n[0].point;this.line.material.color.set(this.curveHitColor),this.line.material.opacity=this.data.hitOpacity,this.line.material.transparent=this.data.hitOpacity<1,this.hitEntity.setAttribute("position",s),this.hitEntity.setAttribute("visible",!0),this.hit=!0,this.hitPoint.copy(n[0].point);for(var o=t;o this.data.curveNumberPoints){ + this.numActivePoints = this.data.curveNumberPoints; + } // Only check for intersection if interval time has passed. if (this.prevCheckTime && (time - this.prevCheckTime < this.data.interval)) { return; } @@ -178,23 +197,39 @@ AFRAME.registerComponent('teleport-controls', { // Set default status as non-hit this.teleportEntity.setAttribute('visible', true); this.line.material.color.set(this.curveMissColor); + this.line.material.opacity = this.data.missOpacity; + this.line.material.transparent = this.data.missOpacity < 1; this.hitEntity.setAttribute('visible', false); this.hit = false; if (this.data.type === 'parabolic') { v0.copy(direction).multiplyScalar(this.data.curveShootingSpeed); - for (var i = 0; i < this.line.numPoints; i++) { - var t = i / (this.line.numPoints - 1); + this.lastDrawnIndex = 0; + const numPoints = this.data.drawIncrementally ? this.numActivePoints : this.line.numPoints; + for (var i = 0; i < numPoints+1; i++) { + var t; + if (i == Math.floor(numPoints+1)){ + t = numPoints / (this.line.numPoints - 1); + } + else { + t = i / (this.line.numPoints - 1); + } parabolicCurve(p0, v0, a, t, next); // Update the raycaster with the length of the current segment last->next var dirLastNext = lastNext.copy(next).sub(last).normalize(); this.raycaster.far = dirLastNext.length(); this.raycaster.set(last, dirLastNext); + this.lastDrawnPoint = next; + this.lastDrawnIndex = i; if (this.checkMeshCollisions(i, next)) { break; } + last.copy(next); } + for (var j = this.lastDrawnIndex+1; j < this.line.numPoints; j++) { + this.line.setPoint(j, this.lastDrawnPoint); + } } else if (this.data.type === 'line') { next.copy(last).add(auxDirection.copy(direction).multiplyScalar(this.data.maxLength)); this.raycaster.far = this.data.maxLength; @@ -243,6 +278,7 @@ AFRAME.registerComponent('teleport-controls', { onButtonDown: function () { this.active = true; + this.redrawLine = true; }, /** @@ -333,6 +369,8 @@ AFRAME.registerComponent('teleport-controls', { var point = intersects[0].point; this.line.material.color.set(this.curveHitColor); + this.line.material.opacity = this.data.hitOpacity; + this.line.material.transparent= this.data.hitOpacity < 1; this.hitEntity.setAttribute('position', point); this.hitEntity.setAttribute('visible', true);