diff --git a/src/components/tools/pen.js b/src/components/tools/pen.js index 52b698edd2adfc83d9fb2753272c55897e842b25..ac9e57fb18d4ccdb381ed2c4ce49e9a31c88255a 100644 --- a/src/components/tools/pen.js +++ b/src/components/tools/pen.js @@ -1,3 +1,5 @@ +const EPS = 10e-6; + /** * Pen tool * @component pen @@ -7,19 +9,21 @@ import SharedBufferGeometryManager from "../../vendor/sharedbuffergeometrymanage AFRAME.registerComponent("pen", { schema: { - drawFrequency: { default: 1000 }, drawPoints: { default: [] }, + drawFrequency: { default: 100 }, minDistanceBetweenPoints: { default: 0.05 }, - segments: { default: 3 }, - radius: { default: 0.05 }, - debug: { default: true } + defaultDirection: { default: { x: 1, y: 0, z: 0 } }, + segments: { default: 8 }, + radius: { default: 0.02 }, + debug: { default: false }, + camera: { type: "selector" } }, init() { this.onMouseDown = this.onMouseDown.bind(this); this.onMouseUp = this.onMouseUp.bind(this); - let material = new THREE.MeshBasicMaterial({ side: THREE.DoubleSide, color: 0xff0000 }); + const material = new THREE.MeshBasicMaterial({ side: THREE.DoubleSide, color: 0xff0000 }); this.sharedBufferGeometryManager = new SharedBufferGeometryManager(); this.sharedBufferGeometryManager.addSharedBuffer(0, material, THREE.TriangleStripDrawMode); @@ -28,9 +32,14 @@ AFRAME.registerComponent("pen", { this.timeSinceLastDraw = 0; this.lastPosition = new THREE.Vector3(); - this.lastPositionSet = false; - this.lastSegmentsSet = false; - this.firstVertex = true; + this.lastPosition.copy(this.el.object3D.position); + this.direction = new THREE.Vector3(); + this.direction.copy(this.data.defaultDirection); + + this.lastPoint = new THREE.Vector3(); + this.lastPointSet = false; + this.initialized = false; + this.lastSegments = []; this.currentSegments = []; for (var x = 0; x < this.data.segments; x++) { @@ -45,7 +54,7 @@ AFRAME.registerComponent("pen", { this.scene.add(this.drawing); if (this.data.debug) { - this.debugGeometry = new THREE.SphereGeometry(0.005, 32, 32); + this.debugGeometry = new THREE.SphereGeometry(0.005, 16, 16); this.debugMaterial = new THREE.MeshBasicMaterial({ color: 0xffff00 }); } }, @@ -60,104 +69,167 @@ AFRAME.registerComponent("pen", { document.removeEventListener("mouseup", this.onMouseUp); }, - tick: (() => { - return function(t, dt) { - if (this.isDrawing && this.timeSinceLastDraw + dt >= this.data.drawFrequency) { - this.addPoint(this.el.object3D.position); - this.sharedBuffer.update(); + tick(t, dt) { + const currentPosition = this.el.object3D.position; + + if (currentPosition.distanceToSquared(this.lastPosition) > EPS) { + this.direction.subVectors(currentPosition, this.lastPoint).normalize(); + } + this.lastPosition.copy(currentPosition); + + if (this.isDrawing) { + const time = this.timeSinceLastDraw + dt; + if ( + time >= this.data.drawFrequency && + this.lastPoint.distanceTo(currentPosition) >= this.data.minDistanceBetweenPoints + ) { + this.addPoint(currentPosition); } - this.timeSinceLastDraw = (this.timeSinceLastDraw + dt) % this.data.drawFrequency; - }; - })(), + this.timeSinceLastDraw = time % this.data.drawFrequency; + } + }, onMouseDown(e) { if (e.button === 0) { this.isDrawing = true; - this.restart(); + this.startDraw(); } }, onMouseUp(e) { if (e.button === 0) { this.isDrawing = false; + this.endDraw(); } }, - restart() { - this.sharedBuffer.restartPrimitive(); + startDraw: (() => { + const normal = new THREE.Vector3(); + return function() { + this.addPoint(this.el.object3D.position); + this.getNormal(normal, this.el.object3D.position); + this.drawStart(normal); + }; + })(), - //restart the draw (readd the last vertex) if this drawing has already been started - if (!this.firstVertex) { - this.restartDraw = true; + endDraw: (() => { + const endPoint = new THREE.Vector3(); + const direction = new THREE.Vector3(); + return function() { + //add the final point and cap + this.addPoint(this.el.object3D.position); + direction.copy(this.direction).multiplyScalar(this.data.radius); + endPoint.copy(this.el.object3D.position).add(direction); + this.drawCap(endPoint, this.currentSegments); + + //reset + this.sharedBuffer.restartPrimitive(); + this.lastPointSet = false; + this.lastSegmentsSet = false; + this.timeSinceLastDraw = 0; + // this.direction.copy(this.data.defaultDirection); + }; + })(), + + //add a "cap" to the start or end of a drawing + drawCap(point, segments) { + let j = 0; + for (let i = 0; i < this.data.segments * 2 - 1; i++) { + if ((i - 1) % 3 === 0) { + this.addVertex(point); + } else { + this.addVertex(segments[j % this.data.segments]); + j++; + } } - this.lastPositionSet = false; - this.lastSegmentsSet = false; + this.sharedBuffer.update(); }, + //calculate the segments for a given point addSegments(segmentsList, point, forward, up) { const angleIncrement = Math.PI * 2 / this.data.segments; for (let i = 0; i < this.data.segments; i++) { const segment = segmentsList[i]; this.rotatePointAroundAxis(segment, point, forward, up, angleIncrement * i, this.data.radius); - - if (this.data.debug) { - const sphere = new THREE.Mesh(this.debugGeometry, this.debugMaterial); - this.scene.add(sphere); - sphere.position.copy(segment); - } } }, addVertex(point) { - this.firstVertex = false; + this.initialized = true; this.sharedBuffer.addVertex(point.x, point.y, point.z); + + if (this.data.debug) { + const sphere = new THREE.Mesh(this.debugGeometry, this.debugMaterial); + this.scene.add(sphere); + sphere.position.copy(point); + } }, + //get lastSegments, draw the start cap + drawStart: (() => { + const startPoint = new THREE.Vector3(); + const inverseDirection = new THREE.Vector3(); + return function(normal) { + this.addSegments(this.lastSegments, this.lastPoint, this.direction, normal); + + inverseDirection + .copy(this.direction) + .negate() + .multiplyScalar(this.data.radius); + startPoint.copy(this.lastPoint).add(inverseDirection); + + //add the first vertex of the lastSegments if this drawing has already been initialized + if (this.initialized) { + this.addVertex(this.lastSegments[0]); + } + + this.drawCap(startPoint, this.lastSegments); + + this.sharedBuffer.restartPrimitive(); + this.addVertex(this.lastSegments[0]); + }; + })(), + + //helper function to get normal of direction of drawing cross direction to camera + getNormal: (() => { + const directionToCamera = new THREE.Vector3(); + return function(normal, position) { + if (this.data.camera) { + directionToCamera.subVectors(position, this.data.camera.object3D.position).normalize(); + normal.crossVectors(this.direction, directionToCamera); + } else { + normal.copy(this.el.object3D.up); + } + }; + })(), + addPoint: (() => { - const forward = new THREE.Vector3(); + const normal = new THREE.Vector3(); return function(position) { - if (this.lastPositionSet) { - //don't draw if distance from last point is not far enough - const distance = position.distanceTo(this.lastPosition); - if (distance >= this.data.minDistanceBetweenPoints) { - //calculate forward only if I have lastPositionSet - forward.subVectors(position, this.lastPosition).normalize(); - - //if I don't have the lastSegments yet, add them now - if (!this.lastSegmentsSet) { - this.addSegments(this.lastSegments, this.lastPosition, forward, THREE.Object3D.DefaultUp); - this.lastSegmentsSet = true; - } - - //add currentSegments - this.addSegments(this.currentSegments, position, forward, THREE.Object3D.DefaultUp); - - //add the first vertex of the currentSegment if this is a restartDraw - if (this.restartDraw) { - this.addVertex(this.lastSegments[0]); - this.restartDraw = false; - } - - //draw the triangle strip - // this.printSegments(); - for (var j = 0; j <= this.data.segments; j++) { - this.addVertex(this.lastSegments[j % this.data.segments]); - this.addVertex(this.currentSegments[j % this.data.segments]); - } - - //copy the currentSegments to lastSegments - for (var j = 0; j < this.data.segments; j++) { - this.lastSegments[j].copy(this.currentSegments[j]); - } - - this.lastPosition.copy(position); + if (this.lastPointSet) { + this.getNormal(normal, position); + + this.addSegments(this.currentSegments, position, this.direction, normal); + + //draw the triangle strip + for (let j = 0; j <= this.data.segments; j++) { + this.addVertex(this.lastSegments[j % this.data.segments]); + this.addVertex(this.currentSegments[j % this.data.segments]); + } + + //update the drawing + this.sharedBuffer.update(); + + //copy the currentSegments to lastSegments + for (var j = 0; j < this.data.segments; j++) { + this.lastSegments[j].copy(this.currentSegments[j]); } - } else { - this.lastPosition.copy(position); - this.lastPositionSet = true; } + + this.lastPoint.copy(position); + this.lastPointSet = true; }; })(), @@ -168,19 +240,5 @@ AFRAME.registerComponent("pen", { calculatedDirection.applyAxisAngle(axis, angle); out.copy(point).add(calculatedDirection.normalize().multiplyScalar(radius)); }; - })(), - - printSegments() { - console.group("lastSegments"); - for (var i = 0; i < this.data.segments; i++) { - console.log(this.lastSegments[i].x, this.lastSegments[i].y, this.lastSegments[i].z); - } - console.groupEnd(); - - console.group("currentSegments"); - for (var j = 0; j < this.data.segments; j++) { - console.log(this.currentSegments[j].x, this.currentSegments[j].y, this.currentSegments[j].z); - } - console.groupEnd(); - } + })() }); diff --git a/src/hub.html b/src/hub.html index bf421fc71224821f5e63e7f348580853463c207a..bd31d164c9362fbcf4e6e98bf6d4bb66b042290d 100644 --- a/src/hub.html +++ b/src/hub.html @@ -231,7 +231,7 @@ segments-height="9" segments-width="9" event-repeater="events: raycaster-intersection, raycaster-intersection-cleared; eventSource: #cursor-controller" - pen + pen="camera: #player-camera" ></a-sphere> <!-- Player Rig -->