From 71d361ed3bc7f31cec1d2d09d2cf674a90432877 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Mon, 30 Nov 2015 17:06:35 -0800 Subject: [PATCH 01/94] fixed hydra paint, adding more interactive brushes --- examples/example/painting/hydraPaint.js | 12 +++---- .../src/RenderablePolyLineEntityItem.cpp | 32 ++++++++++++++----- .../src/RenderablePolyLineEntityItem.h | 4 +++ .../entities-renderer/src/paintStroke.slf | 14 +++++--- .../entities-renderer/src/paintStroke.slv | 2 ++ libraries/entities/src/PolyLineEntityItem.cpp | 2 ++ libraries/entities/src/PolyLineEntityItem.h | 4 ++- 7 files changed, 51 insertions(+), 19 deletions(-) diff --git a/examples/example/painting/hydraPaint.js b/examples/example/painting/hydraPaint.js index 36137945cc..14ea256369 100644 --- a/examples/example/painting/hydraPaint.js +++ b/examples/example/painting/hydraPaint.js @@ -67,12 +67,12 @@ var colorPalette = [{ var MIN_STROKE_WIDTH = 0.002; var MAX_STROKE_WIDTH = 0.05; -function controller(side, cycleColorButton) { +function controller(side, triggerAction) { this.triggerHeld = false; this.triggerThreshold = 0.9; this.side = side; - this.trigger = side == LEFT ? Controller.Stantard.LT : Controller.Standard.RT; - this.cycleColorButton = side == LEFT ? Controller.Stantard.LeftPrimaryThumb : Controller.Standard.RightPrimaryThumb; + this.triggerAction = triggerAction; + this.cycleColorButton = side == LEFT ? Controller.Standard.LeftPrimaryThumb : Controller.Standard.RightPrimaryThumb; this.points = []; this.normals = []; @@ -174,7 +174,7 @@ function controller(side, cycleColorButton) { this.cycleColorButtonPressed = Controller.getValue(this.cycleColorButton); this.palmPosition = this.side == RIGHT ? MyAvatar.rightHandPose.translation : MyAvatar.leftHandPose.translation; this.tipPosition = this.side == RIGHT ? MyAvatar.rightHandTipPose.translation : MyAvatar.leftHandTipPose.translation; - this.triggerValue = Controller.getValue(this.trigger); + this.triggerValue = Controller.getActionValue(this.triggerAction); if (this.prevCycleColorButtonPressed === true && this.cycleColorButtonPressed === false) { @@ -212,8 +212,8 @@ function vectorIsZero(v) { } -var rightController = new controller(RIGHT); -var leftController = new controller(LEFT); +var rightController = new controller(RIGHT, Controller.findAction("RIGHT_HAND_CLICK")); +var leftController = new controller(LEFT, Controller.findAction("LEFT_HAND_CLICK")); Script.update.connect(update); Script.scriptEnding.connect(scriptEnding); diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp index 036d37a95b..63a03bb8e7 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp @@ -24,14 +24,22 @@ +struct PolyLineUniforms { + float time; +}; + EntityItemPointer RenderablePolyLineEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { return EntityItemPointer(new RenderablePolyLineEntityItem(entityID, properties)); } RenderablePolyLineEntityItem::RenderablePolyLineEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) : -PolyLineEntityItem(entityItemID, properties) { - _numVertices = 0; +PolyLineEntityItem(entityItemID, properties), +_counter(0.0f), +_numVertices(0) +{ _vertices = QVector(0.0f); + PolyLineUniforms uniforms; + _uniformBuffer = std::make_shared(sizeof(PolyLineUniforms), (const gpu::Byte*) &uniforms); } @@ -161,6 +169,18 @@ void RenderablePolyLineEntityItem::updateVertices() { } +void RenderablePolyLineEntityItem::update(const quint64& now) { + PolyLineUniforms uniforms; + _counter += 0.01; + uniforms.time = _counter; + memcpy(&_uniformBuffer.edit(), &uniforms, sizeof(PolyLineUniforms)); + + if (_pointsChanged || _strokeWidthsChanged || _normalsChanged) { + updateVertices(); + updateGeometry(); + } + +} void RenderablePolyLineEntityItem::render(RenderArgs* args) { QWriteLocker lock(&_quadReadWriteLock); @@ -181,17 +201,13 @@ void RenderablePolyLineEntityItem::render(RenderArgs* args) { PerformanceTimer perfTimer("RenderablePolyLineEntityItem::render"); Q_ASSERT(getType() == EntityTypes::PolyLine); - Q_ASSERT(args->_batch); - if (_pointsChanged || _strokeWidthsChanged || _normalsChanged) { - updateVertices(); - updateGeometry(); - } gpu::Batch& batch = *args->_batch; Transform transform = Transform(); transform.setTranslation(getPosition()); transform.setRotation(getRotation()); + batch.setUniformBuffer(0, _uniformBuffer); batch.setModelTransform(transform); batch.setPipeline(_pipeline); @@ -200,7 +216,7 @@ void RenderablePolyLineEntityItem::render(RenderArgs* args) { } else { batch.setResourceTexture(PAINTSTROKE_GPU_SLOT, args->_whiteTexture); } - + batch.setInputFormat(_format); batch.setInputBuffer(0, _verticesBuffer, 0, _format->getChannels().at(0)._stride); diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h index c49777cfa3..658614e72e 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h @@ -29,6 +29,8 @@ public: RenderablePolyLineEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties); virtual void render(RenderArgs* args); + virtual void update(const quint64& now) override; + virtual bool needsToCallUpdate() const { return true; }; SIMPLE_RENDERABLE(); @@ -42,8 +44,10 @@ protected: void updateGeometry(); void updateVertices(); gpu::BufferPointer _verticesBuffer; + gpu::BufferView _uniformBuffer; unsigned int _numVertices; QVector _vertices; + float _counter; }; diff --git a/libraries/entities-renderer/src/paintStroke.slf b/libraries/entities-renderer/src/paintStroke.slf index 4e2bc8d097..48de1274f4 100644 --- a/libraries/entities-renderer/src/paintStroke.slf +++ b/libraries/entities-renderer/src/paintStroke.slf @@ -23,11 +23,15 @@ in vec3 interpolatedNormal; in vec2 varTexcoord; in vec4 varColor; -float rand(vec2 point){ - return fract(sin(dot(point.xy ,vec2(12.9898,78.233))) * 43758.5453); -} +flat in int varVertexId; +struct PolyLineUniforms { + float time; +}; +uniform polyLineBuffer { + PolyLineUniforms polyline; +}; void main(void) { @@ -35,11 +39,13 @@ void main(void) { vec4 texel = texture(originalTexture, varTexcoord); int frontCondition = 1 -int(gl_FrontFacing) * 2; vec3 color = varColor.rgb; + color.g = 1.0 + sin(polyline.time) * 0.5; + //texel.a = varVertexId / 20.0; //vec3 normal, float alpha, vec3 diffuse, vec3 specular, float shininess packDeferredFragmentTranslucent( interpolatedNormal * frontCondition, texel.a, - color *texel.rgb, + color * texel.rgb, vec3(0.01, 0.01, 0.01), 10.0); } diff --git a/libraries/entities-renderer/src/paintStroke.slv b/libraries/entities-renderer/src/paintStroke.slv index 7d7523deb9..25f046456a 100644 --- a/libraries/entities-renderer/src/paintStroke.slv +++ b/libraries/entities-renderer/src/paintStroke.slv @@ -24,6 +24,7 @@ out vec3 interpolatedNormal; out vec2 varTexcoord; out vec4 varColor; +flat out int varVertexId; void main(void) { @@ -31,6 +32,7 @@ void main(void) { // pass along the diffuse color varColor = inColor; + varVertexId = gl_VertexID; // standard transform TransformCamera cam = getTransformCamera(); diff --git a/libraries/entities/src/PolyLineEntityItem.cpp b/libraries/entities/src/PolyLineEntityItem.cpp index 012ec3ca4a..f2cdd6c1c1 100644 --- a/libraries/entities/src/PolyLineEntityItem.cpp +++ b/libraries/entities/src/PolyLineEntityItem.cpp @@ -90,6 +90,7 @@ bool PolyLineEntityItem::setProperties(const EntityItemProperties& properties) { return somethingChanged; } + bool PolyLineEntityItem::appendPoint(const glm::vec3& point) { if (_points.size() > MAX_POINTS_PER_LINE - 1) { qDebug() << "MAX POINTS REACHED!"; @@ -105,6 +106,7 @@ bool PolyLineEntityItem::appendPoint(const glm::vec3& point) { return true; } + bool PolyLineEntityItem::setStrokeWidths(const QVector& strokeWidths) { _strokeWidths = strokeWidths; _strokeWidthsChanged = true; diff --git a/libraries/entities/src/PolyLineEntityItem.h b/libraries/entities/src/PolyLineEntityItem.h index 4da52f3f21..1610bf87b5 100644 --- a/libraries/entities/src/PolyLineEntityItem.h +++ b/libraries/entities/src/PolyLineEntityItem.h @@ -75,7 +75,9 @@ class PolyLineEntityItem : public EntityItem { _texturesChangedFlag = true; } } - + + virtual bool needsToCallUpdate() const { return true; } + virtual ShapeType getShapeType() const { return SHAPE_TYPE_LINE; } // never have a ray intersection pick a PolyLineEntityItem. From 191cab2e5f5fd60f40c5310cb8c35d564b1a4157 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Tue, 1 Dec 2015 10:15:47 -0800 Subject: [PATCH 02/94] adding light trail app --- examples/flowArts/lightTrails.js | 223 ++++++++++++++++++++++++++++++ examples/lightTrails.js | 224 +++++++++++++++++++++++++++++++ 2 files changed, 447 insertions(+) create mode 100644 examples/flowArts/lightTrails.js create mode 100644 examples/lightTrails.js diff --git a/examples/flowArts/lightTrails.js b/examples/flowArts/lightTrails.js new file mode 100644 index 0000000000..60754f42a8 --- /dev/null +++ b/examples/flowArts/lightTrails.js @@ -0,0 +1,223 @@ +// +// hydraPaint.js +// examples +// +// Created by Eric Levin on 5/14/15. +// Copyright 2014 High Fidelity, Inc. +// +// This script allows you to paint with the hydra! +// +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +var LEFT = 0; +var RIGHT = 1; +var LASER_WIDTH = 3; +var LASER_COLOR = { + red: 50, + green: 150, + blue: 200 +}; +var TRIGGER_THRESHOLD = .1; + +var MAX_POINTS_PER_LINE = 40; + +var LIFETIME = 6000; +var DRAWING_DEPTH = 1; +var LINE_DIMENSIONS = 20; + + +var MIN_POINT_DISTANCE = 0.01; + +var MIN_BRUSH_RADIUS = 0.08; +var MAX_BRUSH_RADIUS = 0.1; + +var RIGHT_BUTTON_1 = 7 +var RIGHT_BUTTON_2 = 8 +var RIGHT_BUTTON_3 = 9; +var RIGHT_BUTTON_4 = 10 +var LEFT_BUTTON_1 = 1; +var LEFT_BUTTON_2 = 2; +var LEFT_BUTTON_3 = 3; +var LEFT_BUTTON_4 = 4; + +var colorPalette = [{ + red: 250, + green: 0, + blue: 0 +}, { + red: 214, + green: 91, + blue: 67 +}, { + red: 192, + green: 41, + blue: 66 +}, { + red: 84, + green: 36, + blue: 55 +}, { + red: 83, + green: 119, + blue: 122 +}]; + +var MIN_STROKE_WIDTH = 0.002; +var MAX_STROKE_WIDTH = 0.05; + +function controller(side, triggerAction) { + this.triggerHeld = false; + this.triggerThreshold = 0.9; + this.side = side; + this.triggerAction = triggerAction; + this.cycleColorButton = side == LEFT ? Controller.Standard.LeftPrimaryThumb : Controller.Standard.RightPrimaryThumb; + + this.points = []; + this.normals = []; + this.strokeWidths = []; + + this.currentColorIndex = 0; + this.currentColor = colorPalette[this.currentColorIndex]; + + var self = this; + + + this.brush = Entities.addEntity({ + type: 'Sphere', + position: { + x: 0, + y: 0, + z: 0 + }, + color: this.currentColor, + dimensions: { + x: MIN_BRUSH_RADIUS, + y: MIN_BRUSH_RADIUS, + z: MIN_BRUSH_RADIUS + } + }); + + this.cycleColor = function() { + this.currentColor = colorPalette[++this.currentColorIndex]; + if (this.currentColorIndex === colorPalette.length - 1) { + this.currentColorIndex = -1; + } + } + this.newLine = function(position) { + this.linePosition = position; + this.line = Entities.addEntity({ + position: position, + type: "PolyLine", + color: this.currentColor, + dimensions: { + x: LINE_DIMENSIONS, + y: LINE_DIMENSIONS, + z: LINE_DIMENSIONS + }, + textures: "http://localhost:8080/trails.png", + lifetime: LIFETIME + }); + this.points = []; + this.normals = [] + this.strokeWidths = []; + } + + this.update = function(deltaTime) { + this.updateControllerState(); + var newBrushPosOffset = Vec3.multiply(Vec3.normalize(Vec3.subtract(this.tipPosition, this.palmPosition)), DRAWING_DEPTH); + var newBrushPos = Vec3.sum(this.palmPosition, newBrushPosOffset); + var brushRadius = map(this.triggerValue, TRIGGER_THRESHOLD, 1, MIN_BRUSH_RADIUS, MAX_BRUSH_RADIUS) + Entities.editEntity(this.brush, { + position: newBrushPos, + color: this.currentColor, + dimensions: { + x: brushRadius, + y: brushRadius, + z: brushRadius + } + }); + + + if (this.triggerValue > TRIGGER_THRESHOLD && !this.drawing) { + this.newLine(newBrushPos); + this.drawing = true; + } else if (this.drawing && this.triggerValue < TRIGGER_THRESHOLD) { + this.drawing = false; + } + + if (this.drawing && this.points.length < MAX_POINTS_PER_LINE) { + var localPoint = Vec3.subtract(newBrushPos, this.linePosition); + if (Vec3.distance(localPoint, this.points[this.points.length - 1]) < MIN_POINT_DISTANCE) { + //Need a minimum distance to avoid binormal NANs + return; + } + + this.points.push(localPoint); + var normal = computeNormal(newBrushPos, Camera.getPosition()); + + this.normals.push(normal); + var strokeWidth = map(this.triggerValue, TRIGGER_THRESHOLD, 1, MIN_STROKE_WIDTH, MAX_STROKE_WIDTH); + this.strokeWidths.push(strokeWidth); + Entities.editEntity(this.line, { + linePoints: this.points, + normals: this.normals, + strokeWidths: this.strokeWidths, + color: this.currentColor + }); + + } + } + + + this.updateControllerState = function() { + this.cycleColorButtonPressed = Controller.getValue(this.cycleColorButton); + this.palmPosition = this.side == RIGHT ? MyAvatar.rightHandPose.translation : MyAvatar.leftHandPose.translation; + this.tipPosition = this.side == RIGHT ? MyAvatar.rightHandTipPose.translation : MyAvatar.leftHandTipPose.translation; + this.triggerValue = Controller.getActionValue(this.triggerAction); + + + if (this.prevCycleColorButtonPressed === true && this.cycleColorButtonPressed === false) { + this.cycleColor(); + Entities.editEntity(this.brush, { + // color: this.currentColor + }); + } + + this.prevCycleColorButtonPressed = this.cycleColorButtonPressed; + + } + + this.cleanup = function() { + Entities.deleteEntity(self.brush); + } +} + +function computeNormal(p1, p2) { + return Vec3.normalize(Vec3.subtract(p2, p1)); +} + +function update(deltaTime) { + leftController.update(deltaTime); + rightController.update(deltaTime); +} + +function scriptEnding() { + leftController.cleanup(); + rightController.cleanup(); +} + +function vectorIsZero(v) { + return v.x === 0 && v.y === 0 && v.z === 0; +} + + +var rightController = new controller(RIGHT, Controller.findAction("RIGHT_HAND_CLICK")); +var leftController = new controller(LEFT, Controller.findAction("LEFT_HAND_CLICK")); +Script.update.connect(update); +Script.scriptEnding.connect(scriptEnding); + +function map(value, min1, max1, min2, max2) { + return min2 + (max2 - min2) * ((value - min1) / (max1 - min1)); +} \ No newline at end of file diff --git a/examples/lightTrails.js b/examples/lightTrails.js new file mode 100644 index 0000000000..fc8d7082e4 --- /dev/null +++ b/examples/lightTrails.js @@ -0,0 +1,224 @@ +// +// hydraPaint.js +// examples +// +// Created by Eric Levin on 5/14/15. +// Copyright 2014 High Fidelity, Inc. +// +// This script allows you to paint with the hydra! +// +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +var LEFT = 0; +var RIGHT = 1; +var LASER_WIDTH = 3; +var LASER_COLOR = { + red: 50, + green: 150, + blue: 200 +}; +var TRIGGER_THRESHOLD = .1; + +var MAX_POINTS_PER_LINE = 40; + +var LIFETIME = 6000; +var DRAWING_DEPTH = 1; +var LINE_DIMENSIONS = 20; + + +var MIN_POINT_DISTANCE = 0.01; + +var MIN_BRUSH_RADIUS = 0.08; +var MAX_BRUSH_RADIUS = 0.1; + +var RIGHT_BUTTON_1 = 7 +var RIGHT_BUTTON_2 = 8 +var RIGHT_BUTTON_3 = 9; +var RIGHT_BUTTON_4 = 10 +var LEFT_BUTTON_1 = 1; +var LEFT_BUTTON_2 = 2; +var LEFT_BUTTON_3 = 3; +var LEFT_BUTTON_4 = 4; + +var colorPalette = [{ + red: 250, + green: 0, + blue: 0 +}, { + red: 214, + green: 91, + blue: 67 +}, { + red: 192, + green: 41, + blue: 66 +}, { + red: 84, + green: 36, + blue: 55 +}, { + red: 83, + green: 119, + blue: 122 +}]; + +var MIN_STROKE_WIDTH = 0.002; +var MAX_STROKE_WIDTH = 0.05; + +function controller(side, triggerAction) { + this.triggerHeld = false; + this.triggerThreshold = 0.9; + this.side = side; + this.triggerAction = triggerAction; + this.cycleColorButton = side == LEFT ? Controller.Standard.LeftPrimaryThumb : Controller.Standard.RightPrimaryThumb; + + this.points = []; + this.normals = []; + this.strokeWidths = []; + + this.currentColorIndex = 0; + this.currentColor = colorPalette[this.currentColorIndex]; + + var self = this; + + + this.brush = Entities.addEntity({ + type: 'Sphere', + position: { + x: 0, + y: 0, + z: 0 + }, + color: this.currentColor, + dimensions: { + x: MIN_BRUSH_RADIUS, + y: MIN_BRUSH_RADIUS, + z: MIN_BRUSH_RADIUS + } + }); + + this.cycleColor = function() { + this.currentColor = colorPalette[++this.currentColorIndex]; + if (this.currentColorIndex === colorPalette.length - 1) { + this.currentColorIndex = -1; + } + } + this.newLine = function(position) { + this.linePosition = position; + this.line = Entities.addEntity({ + position: position, + type: "PolyLine", + color: this.currentColor, + dimensions: { + x: LINE_DIMENSIONS, + y: LINE_DIMENSIONS, + z: LINE_DIMENSIONS + }, + textures: "http://localhost:8080/trails.png", + lifetime: LIFETIME + }); + this.points = []; + this.normals = [] + this.strokeWidths = []; + } + + this.update = function(deltaTime) { + this.updateControllerState(); + var newBrushPosOffset = Vec3.multiply(Vec3.normalize(Vec3.subtract(this.tipPosition, this.palmPosition)), DRAWING_DEPTH); + var newBrushPos = Vec3.sum(this.palmPosition, newBrushPosOffset); + var brushRadius = map(this.triggerValue, TRIGGER_THRESHOLD, 1, MIN_BRUSH_RADIUS, MAX_BRUSH_RADIUS) + Entities.editEntity(this.brush, { + position: newBrushPos, + color: this.currentColor, + dimensions: { + x: brushRadius, + y: brushRadius, + z: brushRadius + } + }); + + + if (this.triggerValue > TRIGGER_THRESHOLD && !this.drawing) { + this.newLine(newBrushPos); + this.drawing = true; + } else if (this.drawing && this.triggerValue < TRIGGER_THRESHOLD) { + this.drawing = false; + } + + if (this.drawing && this.points.length < MAX_POINTS_PER_LINE) { + var localPoint = Vec3.subtract(newBrushPos, this.linePosition); + if (Vec3.distance(localPoint, this.points[this.points.length - 1]) < MIN_POINT_DISTANCE) { + //Need a minimum distance to avoid binormal NANs + return; + } + + this.points.push(localPoint); + var normal = computeNormal(newBrushPos, Camera.getPosition()); + + this.normals.push(normal); + var strokeWidth = map(this.triggerValue, TRIGGER_THRESHOLD, 1, MIN_STROKE_WIDTH, MAX_STROKE_WIDTH); + this.strokeWidths.push(strokeWidth); + Entities.editEntity(this.line, { + linePoints: this.points, + normals: this.normals, + strokeWidths: this.strokeWidths, + color: this.currentColor + }); + } + + // Once we get to max points + } + + + this.updateControllerState = function() { + this.cycleColorButtonPressed = Controller.getValue(this.cycleColorButton); + this.palmPosition = this.side == RIGHT ? MyAvatar.rightHandPose.translation : MyAvatar.leftHandPose.translation; + this.tipPosition = this.side == RIGHT ? MyAvatar.rightHandTipPose.translation : MyAvatar.leftHandTipPose.translation; + this.triggerValue = Controller.getActionValue(this.triggerAction); + + + if (this.prevCycleColorButtonPressed === true && this.cycleColorButtonPressed === false) { + this.cycleColor(); + Entities.editEntity(this.brush, { + // color: this.currentColor + }); + } + + this.prevCycleColorButtonPressed = this.cycleColorButtonPressed; + + } + + this.cleanup = function() { + Entities.deleteEntity(self.brush); + } +} + +function computeNormal(p1, p2) { + return Vec3.normalize(Vec3.subtract(p2, p1)); +} + +function update(deltaTime) { + leftController.update(deltaTime); + rightController.update(deltaTime); +} + +function scriptEnding() { + leftController.cleanup(); + rightController.cleanup(); +} + +function vectorIsZero(v) { + return v.x === 0 && v.y === 0 && v.z === 0; +} + + +var rightController = new controller(RIGHT, Controller.findAction("RIGHT_HAND_CLICK")); +var leftController = new controller(LEFT, Controller.findAction("LEFT_HAND_CLICK")); +Script.update.connect(update); +Script.scriptEnding.connect(scriptEnding); + +function map(value, min1, max1, min2, max2) { + return min2 + (max2 - min2) * ((value - min1) / (max1 - min1)); +} \ No newline at end of file From a83f374036727eab11afe3616f5f8299814c1e97 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Tue, 1 Dec 2015 10:50:25 -0800 Subject: [PATCH 03/94] tweaking light trail --- examples/flowArts/lightTrails.js | 103 +++++++++++-------------------- 1 file changed, 36 insertions(+), 67 deletions(-) diff --git a/examples/flowArts/lightTrails.js b/examples/flowArts/lightTrails.js index 60754f42a8..c01fc1c4d3 100644 --- a/examples/flowArts/lightTrails.js +++ b/examples/flowArts/lightTrails.js @@ -33,14 +33,6 @@ var MIN_POINT_DISTANCE = 0.01; var MIN_BRUSH_RADIUS = 0.08; var MAX_BRUSH_RADIUS = 0.1; -var RIGHT_BUTTON_1 = 7 -var RIGHT_BUTTON_2 = 8 -var RIGHT_BUTTON_3 = 9; -var RIGHT_BUTTON_4 = 10 -var LEFT_BUTTON_1 = 1; -var LEFT_BUTTON_2 = 2; -var LEFT_BUTTON_3 = 3; -var LEFT_BUTTON_4 = 4; var colorPalette = [{ red: 250, @@ -73,94 +65,73 @@ function controller(side, triggerAction) { this.side = side; this.triggerAction = triggerAction; this.cycleColorButton = side == LEFT ? Controller.Standard.LeftPrimaryThumb : Controller.Standard.RightPrimaryThumb; - + this.currentColorIndex = 0; + this.currentColor = colorPalette[this.currentColorIndex]; + this.trail = Entities.addEntity({ + type: "PolyLine", + color: this.currentColor, + dimensions: { + x: LINE_DIMENSIONS, + y: LINE_DIMENSIONS, + z: LINE_DIMENSIONS + }, + textures: "http://localhost:8080/trails.png", + lifetime: LIFETIME + }); this.points = []; this.normals = []; this.strokeWidths = []; - - this.currentColorIndex = 0; - this.currentColor = colorPalette[this.currentColorIndex]; - var self = this; - this.brush = Entities.addEntity({ - type: 'Sphere', - position: { - x: 0, - y: 0, - z: 0 - }, - color: this.currentColor, - dimensions: { - x: MIN_BRUSH_RADIUS, - y: MIN_BRUSH_RADIUS, - z: MIN_BRUSH_RADIUS - } - }); - this.cycleColor = function() { this.currentColor = colorPalette[++this.currentColorIndex]; if (this.currentColorIndex === colorPalette.length - 1) { this.currentColorIndex = -1; } } - this.newLine = function(position) { - this.linePosition = position; - this.line = Entities.addEntity({ - position: position, - type: "PolyLine", - color: this.currentColor, - dimensions: { - x: LINE_DIMENSIONS, - y: LINE_DIMENSIONS, - z: LINE_DIMENSIONS - }, - textures: "http://localhost:8080/trails.png", - lifetime: LIFETIME + + this.setTrailPosition = function(position) { + this.trailPosition = position; + Entities.editEntity(this.trail, { + position: this.trailPosition }); - this.points = []; - this.normals = [] - this.strokeWidths = []; } + this.update = function(deltaTime) { this.updateControllerState(); - var newBrushPosOffset = Vec3.multiply(Vec3.normalize(Vec3.subtract(this.tipPosition, this.palmPosition)), DRAWING_DEPTH); - var newBrushPos = Vec3.sum(this.palmPosition, newBrushPosOffset); - var brushRadius = map(this.triggerValue, TRIGGER_THRESHOLD, 1, MIN_BRUSH_RADIUS, MAX_BRUSH_RADIUS) - Entities.editEntity(this.brush, { - position: newBrushPos, - color: this.currentColor, - dimensions: { - x: brushRadius, - y: brushRadius, - z: brushRadius - } - }); + var newTrailPosOffset = Vec3.multiply(Vec3.normalize(Vec3.subtract(this.tipPosition, this.palmPosition)), DRAWING_DEPTH); + var newTrailPos = Vec3.sum(this.palmPosition, newTrailPosOffset); if (this.triggerValue > TRIGGER_THRESHOLD && !this.drawing) { - this.newLine(newBrushPos); + this.setTrailPosition(newTrailPos); this.drawing = true; } else if (this.drawing && this.triggerValue < TRIGGER_THRESHOLD) { this.drawing = false; } - if (this.drawing && this.points.length < MAX_POINTS_PER_LINE) { - var localPoint = Vec3.subtract(newBrushPos, this.linePosition); + if (this.drawing) { + var localPoint = Vec3.subtract(newTrailPos, this.trailPosition); if (Vec3.distance(localPoint, this.points[this.points.length - 1]) < MIN_POINT_DISTANCE) { //Need a minimum distance to avoid binormal NANs return; } + if (this.points.length === MAX_POINTS_PER_LINE) { + this.points.shift(); + this.normals.shift(); + this.strokeWidths.shift(); + } + this.points.push(localPoint); - var normal = computeNormal(newBrushPos, Camera.getPosition()); + var normal = computeNormal(newTrailPos, Camera.getPosition()); this.normals.push(normal); var strokeWidth = map(this.triggerValue, TRIGGER_THRESHOLD, 1, MIN_STROKE_WIDTH, MAX_STROKE_WIDTH); this.strokeWidths.push(strokeWidth); - Entities.editEntity(this.line, { + Entities.editEntity(this.trail, { linePoints: this.points, normals: this.normals, strokeWidths: this.strokeWidths, @@ -175,9 +146,9 @@ function controller(side, triggerAction) { this.cycleColorButtonPressed = Controller.getValue(this.cycleColorButton); this.palmPosition = this.side == RIGHT ? MyAvatar.rightHandPose.translation : MyAvatar.leftHandPose.translation; this.tipPosition = this.side == RIGHT ? MyAvatar.rightHandTipPose.translation : MyAvatar.leftHandTipPose.translation; - this.triggerValue = Controller.getActionValue(this.triggerAction); + this.triggerValue = Controller.getActionValue(this.triggerAction); + - if (this.prevCycleColorButtonPressed === true && this.cycleColorButtonPressed === false) { this.cycleColor(); Entities.editEntity(this.brush, { @@ -189,9 +160,7 @@ function controller(side, triggerAction) { } - this.cleanup = function() { - Entities.deleteEntity(self.brush); - } + this.cleanup = function() {} } function computeNormal(p1, p2) { @@ -204,7 +173,7 @@ function update(deltaTime) { } function scriptEnding() { - leftController.cleanup(); + leftController.cleanup(); rightController.cleanup(); } From 191f7f2c228771d55c544d2fb497ae34e2d0527e Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Tue, 1 Dec 2015 16:22:11 -0800 Subject: [PATCH 04/94] no audio for now --- examples/flowArts/lightTrails.js | 22 ++++++++-------- .../src/RenderablePolyLineEntityItem.cpp | 25 +++---------------- .../entities-renderer/src/paintStroke.slf | 7 ++---- 3 files changed, 16 insertions(+), 38 deletions(-) diff --git a/examples/flowArts/lightTrails.js b/examples/flowArts/lightTrails.js index c01fc1c4d3..8251803a5f 100644 --- a/examples/flowArts/lightTrails.js +++ b/examples/flowArts/lightTrails.js @@ -19,19 +19,17 @@ var LASER_COLOR = { green: 150, blue: 200 }; -var TRIGGER_THRESHOLD = .1; -var MAX_POINTS_PER_LINE = 40; +var MAX_POINTS_PER_LINE = 50; var LIFETIME = 6000; -var DRAWING_DEPTH = 1; +var DRAWING_DEPTH = 0.3; var LINE_DIMENSIONS = 20; -var MIN_POINT_DISTANCE = 0.01; +var MIN_POINT_DISTANCE = 0.03; + -var MIN_BRUSH_RADIUS = 0.08; -var MAX_BRUSH_RADIUS = 0.1; var colorPalette = [{ @@ -75,7 +73,7 @@ function controller(side, triggerAction) { y: LINE_DIMENSIONS, z: LINE_DIMENSIONS }, - textures: "http://localhost:8080/trails.png", + textures: "http://localhost:8080/trails.png?v1" + Math.random(), lifetime: LIFETIME }); this.points = []; @@ -105,11 +103,9 @@ function controller(side, triggerAction) { var newTrailPos = Vec3.sum(this.palmPosition, newTrailPosOffset); - if (this.triggerValue > TRIGGER_THRESHOLD && !this.drawing) { + if (!this.drawing) { this.setTrailPosition(newTrailPos); this.drawing = true; - } else if (this.drawing && this.triggerValue < TRIGGER_THRESHOLD) { - this.drawing = false; } if (this.drawing) { @@ -129,7 +125,7 @@ function controller(side, triggerAction) { var normal = computeNormal(newTrailPos, Camera.getPosition()); this.normals.push(normal); - var strokeWidth = map(this.triggerValue, TRIGGER_THRESHOLD, 1, MIN_STROKE_WIDTH, MAX_STROKE_WIDTH); + var strokeWidth = MAX_STROKE_WIDTH; this.strokeWidths.push(strokeWidth); Entities.editEntity(this.trail, { linePoints: this.points, @@ -160,7 +156,9 @@ function controller(side, triggerAction) { } - this.cleanup = function() {} + this.cleanup = function() { + Entities.deleteEntity(this.trail); + } } function computeNormal(p1, p2) { diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp index 63a03bb8e7..bfb27a93b9 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp @@ -80,30 +80,12 @@ void RenderablePolyLineEntityItem::updateGeometry() { _verticesBuffer.reset(new gpu::Buffer()); int vertexIndex = 0; vec2 uv; - float tailStart = 0.0f; - float tailEnd = 0.25f; - float tailLength = tailEnd - tailStart; - - float headStart = 0.76f; - float headEnd = 1.0f; - float headLength = headEnd - headStart; float uCoord, vCoord; - - int numTailStrips = 5; - int numHeadStrips = 10; - int startHeadIndex = _vertices.size() / 2 - numHeadStrips; + uCoord = 0.0f; + float uCoordInc = 1.0 / (_vertices.size() / 2); for (int i = 0; i < _vertices.size() / 2; i++) { - uCoord = 0.26f; vCoord = 0.0f; - //tail - if (i < numTailStrips) { - uCoord = float(i) / numTailStrips * tailLength + tailStart; - } - - //head - if (i > startHeadIndex) { - uCoord = float((i + 1) - startHeadIndex) / numHeadStrips * headLength + headStart; - } + uv = vec2(uCoord, vCoord); @@ -121,6 +103,7 @@ void RenderablePolyLineEntityItem::updateGeometry() { vertexIndex++; _numVertices += 2; + uCoord += uCoordInc; } _pointsChanged = false; _normalsChanged = false; diff --git a/libraries/entities-renderer/src/paintStroke.slf b/libraries/entities-renderer/src/paintStroke.slf index 48de1274f4..6c51b4c979 100644 --- a/libraries/entities-renderer/src/paintStroke.slf +++ b/libraries/entities-renderer/src/paintStroke.slf @@ -38,14 +38,11 @@ void main(void) { vec4 texel = texture(originalTexture, varTexcoord); int frontCondition = 1 -int(gl_FrontFacing) * 2; - vec3 color = varColor.rgb; - color.g = 1.0 + sin(polyline.time) * 0.5; - //texel.a = varVertexId / 20.0; - //vec3 normal, float alpha, vec3 diffuse, vec3 specular, float shininess + vec3 color = varColor.rgb; packDeferredFragmentTranslucent( interpolatedNormal * frontCondition, texel.a, - color * texel.rgb, + texel.rgb, vec3(0.01, 0.01, 0.01), 10.0); } From 8aeb5598488905b7b47bca4db686874ec1ad51fa Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Wed, 2 Dec 2015 15:58:30 -0800 Subject: [PATCH 05/94] flowy --- examples/flowArts/lightTrails.js | 45 +++++-- examples/lightTrails.js | 224 ------------------------------- 2 files changed, 36 insertions(+), 233 deletions(-) delete mode 100644 examples/lightTrails.js diff --git a/examples/flowArts/lightTrails.js b/examples/flowArts/lightTrails.js index 8251803a5f..8d88957f03 100644 --- a/examples/flowArts/lightTrails.js +++ b/examples/flowArts/lightTrails.js @@ -11,6 +11,10 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // + +var eraseTrail = true; +var ugLSD = 25; +// var eraseTrail = false; var LEFT = 0; var RIGHT = 1; var LASER_WIDTH = 3; @@ -23,13 +27,11 @@ var LASER_COLOR = { var MAX_POINTS_PER_LINE = 50; var LIFETIME = 6000; -var DRAWING_DEPTH = 0.3; +var DRAWING_DEPTH = 0.2; var LINE_DIMENSIONS = 20; -var MIN_POINT_DISTANCE = 0.03; - - +var MIN_POINT_DISTANCE = 0.02; var colorPalette = [{ @@ -54,8 +56,7 @@ var colorPalette = [{ blue: 122 }]; -var MIN_STROKE_WIDTH = 0.002; -var MAX_STROKE_WIDTH = 0.05; +var STROKE_WIDTH = 0.05; function controller(side, triggerAction) { this.triggerHeld = false; @@ -65,6 +66,16 @@ function controller(side, triggerAction) { this.cycleColorButton = side == LEFT ? Controller.Standard.LeftPrimaryThumb : Controller.Standard.RightPrimaryThumb; this.currentColorIndex = 0; this.currentColor = colorPalette[this.currentColorIndex]; + var textures = "http://localhost:8080/trails.png?v2" + Math.random(); + + // this.light = Entities.addEntity({ + // type: 'Light', + // position: MyAvatar.position, + // dimensions: {x: 20, y: 20, z: 20}, + // color: {red: 60, green: 10, blue: 100}, + // intensity: 10 + // }); + this.trail = Entities.addEntity({ type: "PolyLine", color: this.currentColor, @@ -73,7 +84,7 @@ function controller(side, triggerAction) { y: LINE_DIMENSIONS, z: LINE_DIMENSIONS }, - textures: "http://localhost:8080/trails.png?v1" + Math.random(), + textures: textures, lifetime: LIFETIME }); this.points = []; @@ -81,6 +92,18 @@ function controller(side, triggerAction) { this.strokeWidths = []; var self = this; + this.trailEraseInterval = Script.setInterval(function() { + if (self.points.length > 0 && eraseTrail) { + self.points.shift(); + self.normals.shift(); + self.strokeWidths.shift(); + Entities.editEntity(self.trail, { + linePoints: self.points, + strokeWidths: self.strokeWidths, + normals: self.normals + }); + } + }, ugLSD); this.cycleColor = function() { this.currentColor = colorPalette[++this.currentColorIndex]; @@ -101,6 +124,9 @@ function controller(side, triggerAction) { this.updateControllerState(); var newTrailPosOffset = Vec3.multiply(Vec3.normalize(Vec3.subtract(this.tipPosition, this.palmPosition)), DRAWING_DEPTH); var newTrailPos = Vec3.sum(this.palmPosition, newTrailPosOffset); + // Entities.editEntity(this.light, { + // position: newTrailPos + // }); if (!this.drawing) { @@ -125,8 +151,7 @@ function controller(side, triggerAction) { var normal = computeNormal(newTrailPos, Camera.getPosition()); this.normals.push(normal); - var strokeWidth = MAX_STROKE_WIDTH; - this.strokeWidths.push(strokeWidth); + this.strokeWidths.push(STROKE_WIDTH); Entities.editEntity(this.trail, { linePoints: this.points, normals: this.normals, @@ -158,6 +183,8 @@ function controller(side, triggerAction) { this.cleanup = function() { Entities.deleteEntity(this.trail); + // Entities.deleteEntity(this.light); + Script.clearInterval(this.trailEraseInterval); } } diff --git a/examples/lightTrails.js b/examples/lightTrails.js deleted file mode 100644 index fc8d7082e4..0000000000 --- a/examples/lightTrails.js +++ /dev/null @@ -1,224 +0,0 @@ -// -// hydraPaint.js -// examples -// -// Created by Eric Levin on 5/14/15. -// Copyright 2014 High Fidelity, Inc. -// -// This script allows you to paint with the hydra! -// -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// -var LEFT = 0; -var RIGHT = 1; -var LASER_WIDTH = 3; -var LASER_COLOR = { - red: 50, - green: 150, - blue: 200 -}; -var TRIGGER_THRESHOLD = .1; - -var MAX_POINTS_PER_LINE = 40; - -var LIFETIME = 6000; -var DRAWING_DEPTH = 1; -var LINE_DIMENSIONS = 20; - - -var MIN_POINT_DISTANCE = 0.01; - -var MIN_BRUSH_RADIUS = 0.08; -var MAX_BRUSH_RADIUS = 0.1; - -var RIGHT_BUTTON_1 = 7 -var RIGHT_BUTTON_2 = 8 -var RIGHT_BUTTON_3 = 9; -var RIGHT_BUTTON_4 = 10 -var LEFT_BUTTON_1 = 1; -var LEFT_BUTTON_2 = 2; -var LEFT_BUTTON_3 = 3; -var LEFT_BUTTON_4 = 4; - -var colorPalette = [{ - red: 250, - green: 0, - blue: 0 -}, { - red: 214, - green: 91, - blue: 67 -}, { - red: 192, - green: 41, - blue: 66 -}, { - red: 84, - green: 36, - blue: 55 -}, { - red: 83, - green: 119, - blue: 122 -}]; - -var MIN_STROKE_WIDTH = 0.002; -var MAX_STROKE_WIDTH = 0.05; - -function controller(side, triggerAction) { - this.triggerHeld = false; - this.triggerThreshold = 0.9; - this.side = side; - this.triggerAction = triggerAction; - this.cycleColorButton = side == LEFT ? Controller.Standard.LeftPrimaryThumb : Controller.Standard.RightPrimaryThumb; - - this.points = []; - this.normals = []; - this.strokeWidths = []; - - this.currentColorIndex = 0; - this.currentColor = colorPalette[this.currentColorIndex]; - - var self = this; - - - this.brush = Entities.addEntity({ - type: 'Sphere', - position: { - x: 0, - y: 0, - z: 0 - }, - color: this.currentColor, - dimensions: { - x: MIN_BRUSH_RADIUS, - y: MIN_BRUSH_RADIUS, - z: MIN_BRUSH_RADIUS - } - }); - - this.cycleColor = function() { - this.currentColor = colorPalette[++this.currentColorIndex]; - if (this.currentColorIndex === colorPalette.length - 1) { - this.currentColorIndex = -1; - } - } - this.newLine = function(position) { - this.linePosition = position; - this.line = Entities.addEntity({ - position: position, - type: "PolyLine", - color: this.currentColor, - dimensions: { - x: LINE_DIMENSIONS, - y: LINE_DIMENSIONS, - z: LINE_DIMENSIONS - }, - textures: "http://localhost:8080/trails.png", - lifetime: LIFETIME - }); - this.points = []; - this.normals = [] - this.strokeWidths = []; - } - - this.update = function(deltaTime) { - this.updateControllerState(); - var newBrushPosOffset = Vec3.multiply(Vec3.normalize(Vec3.subtract(this.tipPosition, this.palmPosition)), DRAWING_DEPTH); - var newBrushPos = Vec3.sum(this.palmPosition, newBrushPosOffset); - var brushRadius = map(this.triggerValue, TRIGGER_THRESHOLD, 1, MIN_BRUSH_RADIUS, MAX_BRUSH_RADIUS) - Entities.editEntity(this.brush, { - position: newBrushPos, - color: this.currentColor, - dimensions: { - x: brushRadius, - y: brushRadius, - z: brushRadius - } - }); - - - if (this.triggerValue > TRIGGER_THRESHOLD && !this.drawing) { - this.newLine(newBrushPos); - this.drawing = true; - } else if (this.drawing && this.triggerValue < TRIGGER_THRESHOLD) { - this.drawing = false; - } - - if (this.drawing && this.points.length < MAX_POINTS_PER_LINE) { - var localPoint = Vec3.subtract(newBrushPos, this.linePosition); - if (Vec3.distance(localPoint, this.points[this.points.length - 1]) < MIN_POINT_DISTANCE) { - //Need a minimum distance to avoid binormal NANs - return; - } - - this.points.push(localPoint); - var normal = computeNormal(newBrushPos, Camera.getPosition()); - - this.normals.push(normal); - var strokeWidth = map(this.triggerValue, TRIGGER_THRESHOLD, 1, MIN_STROKE_WIDTH, MAX_STROKE_WIDTH); - this.strokeWidths.push(strokeWidth); - Entities.editEntity(this.line, { - linePoints: this.points, - normals: this.normals, - strokeWidths: this.strokeWidths, - color: this.currentColor - }); - } - - // Once we get to max points - } - - - this.updateControllerState = function() { - this.cycleColorButtonPressed = Controller.getValue(this.cycleColorButton); - this.palmPosition = this.side == RIGHT ? MyAvatar.rightHandPose.translation : MyAvatar.leftHandPose.translation; - this.tipPosition = this.side == RIGHT ? MyAvatar.rightHandTipPose.translation : MyAvatar.leftHandTipPose.translation; - this.triggerValue = Controller.getActionValue(this.triggerAction); - - - if (this.prevCycleColorButtonPressed === true && this.cycleColorButtonPressed === false) { - this.cycleColor(); - Entities.editEntity(this.brush, { - // color: this.currentColor - }); - } - - this.prevCycleColorButtonPressed = this.cycleColorButtonPressed; - - } - - this.cleanup = function() { - Entities.deleteEntity(self.brush); - } -} - -function computeNormal(p1, p2) { - return Vec3.normalize(Vec3.subtract(p2, p1)); -} - -function update(deltaTime) { - leftController.update(deltaTime); - rightController.update(deltaTime); -} - -function scriptEnding() { - leftController.cleanup(); - rightController.cleanup(); -} - -function vectorIsZero(v) { - return v.x === 0 && v.y === 0 && v.z === 0; -} - - -var rightController = new controller(RIGHT, Controller.findAction("RIGHT_HAND_CLICK")); -var leftController = new controller(LEFT, Controller.findAction("LEFT_HAND_CLICK")); -Script.update.connect(update); -Script.scriptEnding.connect(scriptEnding); - -function map(value, min1, max1, min2, max2) { - return min2 + (max2 - min2) * ((value - min1) / (max1 - min1)); -} \ No newline at end of file From d2d0326c02268b4cf8cd496de12d5e23888910b9 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Wed, 2 Dec 2015 16:44:47 -0800 Subject: [PATCH 06/94] using deployed texture --- examples/flowArts/lightTrails.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/flowArts/lightTrails.js b/examples/flowArts/lightTrails.js index 8d88957f03..a494f9a810 100644 --- a/examples/flowArts/lightTrails.js +++ b/examples/flowArts/lightTrails.js @@ -66,7 +66,7 @@ function controller(side, triggerAction) { this.cycleColorButton = side == LEFT ? Controller.Standard.LeftPrimaryThumb : Controller.Standard.RightPrimaryThumb; this.currentColorIndex = 0; this.currentColor = colorPalette[this.currentColorIndex]; - var textures = "http://localhost:8080/trails.png?v2" + Math.random(); + var texture = "https://s3.amazonaws.com/hifi-public/eric/textures/paintStrokes/trails.png"; // this.light = Entities.addEntity({ // type: 'Light', @@ -84,7 +84,7 @@ function controller(side, triggerAction) { y: LINE_DIMENSIONS, z: LINE_DIMENSIONS }, - textures: textures, + textures: texture, lifetime: LIFETIME }); this.points = []; From 2336dd96dd934f5efc1832db5815bf2dc87f3c21 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Wed, 2 Dec 2015 18:10:45 -0800 Subject: [PATCH 07/94] lights --- examples/flowArts/lightTrails.js | 108 ++++++++++++++----------------- 1 file changed, 50 insertions(+), 58 deletions(-) diff --git a/examples/flowArts/lightTrails.js b/examples/flowArts/lightTrails.js index a494f9a810..4a7f9de277 100644 --- a/examples/flowArts/lightTrails.js +++ b/examples/flowArts/lightTrails.js @@ -12,73 +12,82 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +Script.include("../libraries/utils.js"); + var eraseTrail = true; var ugLSD = 25; // var eraseTrail = false; var LEFT = 0; var RIGHT = 1; -var LASER_WIDTH = 3; -var LASER_COLOR = { - red: 50, - green: 150, - blue: 200 -}; - var MAX_POINTS_PER_LINE = 50; var LIFETIME = 6000; -var DRAWING_DEPTH = 0.2; +var DRAWING_DEPTH = 0.6; var LINE_DIMENSIONS = 20; +var lightZone = Entities.addEntity({ + type: "Zone", + shapeType: 'box', + keyLightIntensity: 0.02, + keyLightColor: { + red: 5, + green: 0, + blue: 5 + }, + keyLightAmbientIntensity: .05, + position: MyAvatar.position, + dimensions: { + x: 100, + y: 100, + z: 100 + } +}); + var MIN_POINT_DISTANCE = 0.02; var colorPalette = [{ red: 250, - green: 0, - blue: 0 + green: 137, + blue: 162 }, { - red: 214, - green: 91, - blue: 67 + red: 204, + green: 244, + blue: 249 }, { - red: 192, - green: 41, - blue: 66 + red: 146, + green: 206, + blue: 116 }, { - red: 84, - green: 36, - blue: 55 -}, { - red: 83, - green: 119, - blue: 122 + red: 240, + green: 87, + blue: 129 }]; -var STROKE_WIDTH = 0.05; +var STROKE_WIDTH = 0.03; function controller(side, triggerAction) { this.triggerHeld = false; this.triggerThreshold = 0.9; this.side = side; this.triggerAction = triggerAction; - this.cycleColorButton = side == LEFT ? Controller.Standard.LeftPrimaryThumb : Controller.Standard.RightPrimaryThumb; - this.currentColorIndex = 0; - this.currentColor = colorPalette[this.currentColorIndex]; var texture = "https://s3.amazonaws.com/hifi-public/eric/textures/paintStrokes/trails.png"; - // this.light = Entities.addEntity({ - // type: 'Light', - // position: MyAvatar.position, - // dimensions: {x: 20, y: 20, z: 20}, - // color: {red: 60, green: 10, blue: 100}, - // intensity: 10 - // }); + this.light = Entities.addEntity({ + type: 'Light', + position: MyAvatar.position, + dimensions: { + x: 20, + y: 20, + z: 20 + }, + color: colorPalette[randInt(0, colorPalette.length)], + intensity: 20 + }); this.trail = Entities.addEntity({ type: "PolyLine", - color: this.currentColor, dimensions: { x: LINE_DIMENSIONS, y: LINE_DIMENSIONS, @@ -105,12 +114,6 @@ function controller(side, triggerAction) { } }, ugLSD); - this.cycleColor = function() { - this.currentColor = colorPalette[++this.currentColorIndex]; - if (this.currentColorIndex === colorPalette.length - 1) { - this.currentColorIndex = -1; - } - } this.setTrailPosition = function(position) { this.trailPosition = position; @@ -124,9 +127,9 @@ function controller(side, triggerAction) { this.updateControllerState(); var newTrailPosOffset = Vec3.multiply(Vec3.normalize(Vec3.subtract(this.tipPosition, this.palmPosition)), DRAWING_DEPTH); var newTrailPos = Vec3.sum(this.palmPosition, newTrailPosOffset); - // Entities.editEntity(this.light, { - // position: newTrailPos - // }); + Entities.editEntity(this.light, { + position: newTrailPos + }); if (!this.drawing) { @@ -151,12 +154,11 @@ function controller(side, triggerAction) { var normal = computeNormal(newTrailPos, Camera.getPosition()); this.normals.push(normal); - this.strokeWidths.push(STROKE_WIDTH); + this.strokeWidths.push(STROKE_WIDTH + Math.random() * 0.01); Entities.editEntity(this.trail, { linePoints: this.points, normals: this.normals, strokeWidths: this.strokeWidths, - color: this.currentColor }); } @@ -164,26 +166,15 @@ function controller(side, triggerAction) { this.updateControllerState = function() { - this.cycleColorButtonPressed = Controller.getValue(this.cycleColorButton); this.palmPosition = this.side == RIGHT ? MyAvatar.rightHandPose.translation : MyAvatar.leftHandPose.translation; this.tipPosition = this.side == RIGHT ? MyAvatar.rightHandTipPose.translation : MyAvatar.leftHandTipPose.translation; this.triggerValue = Controller.getActionValue(this.triggerAction); - - if (this.prevCycleColorButtonPressed === true && this.cycleColorButtonPressed === false) { - this.cycleColor(); - Entities.editEntity(this.brush, { - // color: this.currentColor - }); - } - - this.prevCycleColorButtonPressed = this.cycleColorButtonPressed; - } this.cleanup = function() { Entities.deleteEntity(this.trail); - // Entities.deleteEntity(this.light); + Entities.deleteEntity(this.light); Script.clearInterval(this.trailEraseInterval); } } @@ -200,6 +191,7 @@ function update(deltaTime) { function scriptEnding() { leftController.cleanup(); rightController.cleanup(); + Entities.deleteEntity(lightZone); } function vectorIsZero(v) { From 5f881e765093d62d9ecc0031b3883ff31acb5505 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Thu, 3 Dec 2015 13:46:29 -0800 Subject: [PATCH 08/94] polylines now take color as a uniform --- examples/flowArts/lightTrails.js | 7 ++++--- examples/painting/closePaint.js | 5 ++++- .../painting/whiteboard/whiteboardEntityScript.js | 3 +++ .../src/RenderablePolyLineEntityItem.cpp | 13 +++---------- .../src/RenderablePolyLineEntityItem.h | 1 - libraries/entities-renderer/src/paintStroke.slf | 6 ++---- libraries/entities-renderer/src/paintStroke.slv | 3 --- libraries/entities/src/PolyLineEntityItem.h | 2 +- 8 files changed, 17 insertions(+), 23 deletions(-) diff --git a/examples/flowArts/lightTrails.js b/examples/flowArts/lightTrails.js index 4a7f9de277..e063897eb8 100644 --- a/examples/flowArts/lightTrails.js +++ b/examples/flowArts/lightTrails.js @@ -22,7 +22,7 @@ var RIGHT = 1; var MAX_POINTS_PER_LINE = 50; var LIFETIME = 6000; -var DRAWING_DEPTH = 0.6; +var DRAWING_DEPTH = 0.8; var LINE_DIMENSIONS = 20; var lightZone = Entities.addEntity({ @@ -65,7 +65,7 @@ var colorPalette = [{ blue: 129 }]; -var STROKE_WIDTH = 0.03; +var STROKE_WIDTH = 0.04; function controller(side, triggerAction) { this.triggerHeld = false; @@ -83,7 +83,7 @@ function controller(side, triggerAction) { z: 20 }, color: colorPalette[randInt(0, colorPalette.length)], - intensity: 20 + intensity: 5 }); this.trail = Entities.addEntity({ @@ -93,6 +93,7 @@ function controller(side, triggerAction) { y: LINE_DIMENSIONS, z: LINE_DIMENSIONS }, + color: {red: 255, green: 255, blue: 255}, textures: texture, lifetime: LIFETIME }); diff --git a/examples/painting/closePaint.js b/examples/painting/closePaint.js index 60b4ac2e25..bea4d9c9aa 100644 --- a/examples/painting/closePaint.js +++ b/examples/painting/closePaint.js @@ -43,6 +43,8 @@ var MAX_STROKE_WIDTH = 0.04; var center = Vec3.sum(MyAvatar.position, Vec3.multiply(2, Quat.getFront(Camera.getOrientation()))); +var textureURL = "https://s3.amazonaws.com/hifi-public/eric/textures/paintStrokes/paintStroke.png"; + function MyController(hand, triggerAction) { @@ -148,7 +150,8 @@ function MyController(hand, triggerAction) { y: 50, z: 50 }, - lifetime: 200 + lifetime: 200, + textures: textureURL }); this.strokePoints = []; this.strokeNormals = []; diff --git a/examples/painting/whiteboard/whiteboardEntityScript.js b/examples/painting/whiteboard/whiteboardEntityScript.js index 61d7291e11..374ec8b873 100644 --- a/examples/painting/whiteboard/whiteboardEntityScript.js +++ b/examples/painting/whiteboard/whiteboardEntityScript.js @@ -29,6 +29,8 @@ var MIN_STROKE_WIDTH = 0.0005; var MAX_STROKE_WIDTH = 0.03; + var textureURL = "https://s3.amazonaws.com/hifi-public/eric/textures/paintStrokes/paintStroke.png"; + var TRIGGER_CONTROLS = [ Controller.Standard.LT, Controller.Standard.RT, @@ -168,6 +170,7 @@ type: "PolyLine", name: "paintStroke", color: this.strokeColor, + textures: "https://s3.amazonaws.com/hifi-public/eric/textures/paintStrokes/paintStroke.png", dimensions: { x: 50, y: 50, diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp index bfb27a93b9..d38da0d3f7 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp @@ -25,7 +25,7 @@ struct PolyLineUniforms { - float time; + glm::vec3 color; }; EntityItemPointer RenderablePolyLineEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { @@ -34,7 +34,6 @@ EntityItemPointer RenderablePolyLineEntityItem::factory(const EntityItemID& enti RenderablePolyLineEntityItem::RenderablePolyLineEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) : PolyLineEntityItem(entityItemID, properties), -_counter(0.0f), _numVertices(0) { _vertices = QVector(0.0f); @@ -49,13 +48,11 @@ int32_t RenderablePolyLineEntityItem::PAINTSTROKE_GPU_SLOT; void RenderablePolyLineEntityItem::createPipeline() { static const int NORMAL_OFFSET = 12; - static const int COLOR_OFFSET = 24; - static const int TEXTURE_OFFSET = 28; + static const int TEXTURE_OFFSET = 24; _format.reset(new gpu::Stream::Format()); _format->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0); _format->setAttribute(gpu::Stream::NORMAL, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), NORMAL_OFFSET); - _format->setAttribute(gpu::Stream::COLOR, 0, gpu::Element::COLOR_RGBA_32, COLOR_OFFSET); _format->setAttribute(gpu::Stream::TEXCOORD, 0, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV), TEXTURE_OFFSET); auto VS = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(paintStroke_vert))); @@ -91,14 +88,12 @@ void RenderablePolyLineEntityItem::updateGeometry() { _verticesBuffer->append(sizeof(glm::vec3), (const gpu::Byte*)&_vertices.at(vertexIndex)); _verticesBuffer->append(sizeof(glm::vec3), (const gpu::Byte*)&_normals.at(i)); - _verticesBuffer->append(sizeof(int), (gpu::Byte*)&_color); _verticesBuffer->append(sizeof(glm::vec2), (gpu::Byte*)&uv); vertexIndex++; uv.y = 1.0f; _verticesBuffer->append(sizeof(glm::vec3), (const gpu::Byte*)&_vertices.at(vertexIndex)); _verticesBuffer->append(sizeof(glm::vec3), (const gpu::Byte*)&_normals.at(i)); - _verticesBuffer->append(sizeof(int), (gpu::Byte*)_color); _verticesBuffer->append(sizeof(glm::vec2), (const gpu::Byte*)&uv); vertexIndex++; @@ -154,10 +149,8 @@ void RenderablePolyLineEntityItem::updateVertices() { void RenderablePolyLineEntityItem::update(const quint64& now) { PolyLineUniforms uniforms; - _counter += 0.01; - uniforms.time = _counter; + uniforms.color = toGlm(getXColor()); memcpy(&_uniformBuffer.edit(), &uniforms, sizeof(PolyLineUniforms)); - if (_pointsChanged || _strokeWidthsChanged || _normalsChanged) { updateVertices(); updateGeometry(); diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h index 658614e72e..411b856d91 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h @@ -47,7 +47,6 @@ protected: gpu::BufferView _uniformBuffer; unsigned int _numVertices; QVector _vertices; - float _counter; }; diff --git a/libraries/entities-renderer/src/paintStroke.slf b/libraries/entities-renderer/src/paintStroke.slf index 6c51b4c979..1a87f84bfd 100644 --- a/libraries/entities-renderer/src/paintStroke.slf +++ b/libraries/entities-renderer/src/paintStroke.slf @@ -21,12 +21,11 @@ uniform sampler2D originalTexture; // the interpolated normal in vec3 interpolatedNormal; in vec2 varTexcoord; -in vec4 varColor; flat in int varVertexId; struct PolyLineUniforms { - float time; + vec3 color; }; uniform polyLineBuffer { @@ -38,11 +37,10 @@ void main(void) { vec4 texel = texture(originalTexture, varTexcoord); int frontCondition = 1 -int(gl_FrontFacing) * 2; - vec3 color = varColor.rgb; packDeferredFragmentTranslucent( interpolatedNormal * frontCondition, texel.a, - texel.rgb, + polyline.color * texel.rgb, vec3(0.01, 0.01, 0.01), 10.0); } diff --git a/libraries/entities-renderer/src/paintStroke.slv b/libraries/entities-renderer/src/paintStroke.slv index 25f046456a..5c368f9c0f 100644 --- a/libraries/entities-renderer/src/paintStroke.slv +++ b/libraries/entities-renderer/src/paintStroke.slv @@ -23,15 +23,12 @@ out vec3 interpolatedNormal; //the diffuse texture out vec2 varTexcoord; -out vec4 varColor; flat out int varVertexId; void main(void) { varTexcoord = inTexCoord0.st; - // pass along the diffuse color - varColor = inColor; varVertexId = gl_VertexID; // standard transform diff --git a/libraries/entities/src/PolyLineEntityItem.h b/libraries/entities/src/PolyLineEntityItem.h index 1610bf87b5..20549d1bbd 100644 --- a/libraries/entities/src/PolyLineEntityItem.h +++ b/libraries/entities/src/PolyLineEntityItem.h @@ -49,7 +49,7 @@ class PolyLineEntityItem : public EntityItem { memcpy(_color, value, sizeof(_color)); } void setColor(const xColor& value) { - + _color[RED_INDEX] = value.red; _color[GREEN_INDEX] = value.green; _color[BLUE_INDEX] = value.blue; From 2fb318cc5366afe54e8794f33094e5bca6d84969 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Tue, 8 Dec 2015 17:52:23 -0800 Subject: [PATCH 09/94] work with clements branch --- examples/controllers/handControllerGrab.js | 161 +++++++++++++++++++-- 1 file changed, 149 insertions(+), 12 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 138240f5d6..20ca3811c0 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -251,9 +251,10 @@ function MyController(hand) { this.triggerValue = 0; // rolling average of trigger value this.rawTriggerValue = 0; this.rawBumperValue = 0; - + this.overlayLine = null; - + this.particleBeam = null; + this.offsetPosition = Vec3.ZERO; this.offsetRotation = Quat.IDENTITY; @@ -398,6 +399,119 @@ function MyController(hand) { } }; + + //test particles instead of overlays + + + this.handleParticleBeam = function(position, orientation) { + + var rotation = Quat.angleAxis(0, { + x: 1, + y: 0, + z: 0 + }); + + var finalRotation = Quat.multiply(orientation, rotation); + + + if (this.particleBeam === null) { + print('create beam') + this.createParticleBeam(position, finalRotation) + } else { + print('update beam') + this.updateParticleBeam(position, finalRotation) + } + } + + this.createParticleBeam = function(position, orientation) { + var particleBeamProperties = { + type: "ParticleEffect", + isEmitting: true, + position: position, + //rotation:Quat.fromPitchYawRollDegrees(-90.0, 0.0, 0.0), + "name": "Particle Beam", + "color": { + "red": 110, + "green": 118.52941176470593, + "blue": 255 + }, + "maxParticles": 1000, + "lifespan": 3, + "emitRate": 20, + "emitSpeed": 10, + "speedSpread": 0, + "emitOrientation": { + "x": -0.7000000000000001, + "y": 0, + "z": 0, + "w": 0.7071068286895752 + }, + "emitDimensions": { + "x": 0, + "y": 0, + "z": 0 + }, + "emitRadiusStart": 0.5, + "polarStart": 0, + "polarFinish": 0, + "azimuthStart": -3.1415927410125732, + "azimuthFinish": 3.1415927410125732, + "emitAcceleration": { + x: 0, + y: 0, + z: 0 + }, + "accelerationSpread": { + "x": 0, + "y": 0, + "z": 0 + }, + "particleRadius": 0.02, + "radiusSpread": 0, + "radiusStart": 0.01, + "radiusFinish": 0.01, + "colorSpread": { + "red": 0, + "green": 0, + "blue": 0 + }, + "colorStart": { + "red": 110, + "green": 118.52941176470593, + "blue": 255 + }, + "colorFinish": { + "red": 110, + "green": 118.52941176470593, + "blue": 255 + }, + "alpha": 1, + "alphaSpread": 0, + "alphaStart": 1, + "alphaFinish": 1, + "additiveBlending": 0, + "textures": "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png" + } + + this.particleBeam = Entities.addEntity(particleBeamProperties); + } + + this.updateParticleBeam = function(position, orientation, acceleration) { + print('O IN UPDATE:::' + JSON.stringify(orientation)) + + // var beamProps = Entities.getEntityProperties(this.particleBeam); + + Entities.editEntity(this.particleBeam, { + //rotation:rotation, + rotation: orientation, + position: position, + + }) + + // var emitO = Entities.getEntityProperties(this.particleBeam, "emitOrientation").emitOrientation; + // print('EMIT o :::' + JSON.stringify(emitO)); + } + this.lineOff = function() { if (this.pointer !== null) { Entities.deleteEntity(this.pointer); @@ -412,6 +526,14 @@ function MyController(hand) { this.overlayLine = null; }; + this.particleBeamOff = function() { + if (this.particleBeam !== null) { + Entities.deleteEntity(this.particleBeam) + } + + this.particleBeam = null; + } + this.triggerPress = function(value) { _this.rawTriggerValue = value; }; @@ -662,6 +784,9 @@ function MyController(hand) { //this.lineOn(distantPickRay.origin, Vec3.multiply(distantPickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); this.overlayLineOn(distantPickRay.origin, Vec3.sum(distantPickRay.origin, Vec3.multiply(distantPickRay.direction, LINE_LENGTH)), NO_INTERSECT_COLOR); + this.handleParticleBeam(distantPickRay.origin, this.getHandRotation()); + + }; this.distanceHolding = function() { @@ -715,6 +840,7 @@ function MyController(hand) { this.currentAvatarOrientation = MyAvatar.orientation; this.overlayLineOff(); + this.particleBeamOff(); }; this.continueDistanceHolding = function() { @@ -808,8 +934,16 @@ function MyController(hand) { // mix in head motion if (MOVE_WITH_HEAD) { var objDistance = Vec3.length(objectToAvatar); - var before = Vec3.multiplyQbyV(this.currentCameraOrientation, { x: 0.0, y: 0.0, z: objDistance }); - var after = Vec3.multiplyQbyV(Camera.orientation, { x: 0.0, y: 0.0, z: objDistance }); + var before = Vec3.multiplyQbyV(this.currentCameraOrientation, { + x: 0.0, + y: 0.0, + z: objDistance + }); + var after = Vec3.multiplyQbyV(Camera.orientation, { + x: 0.0, + y: 0.0, + z: objDistance + }); var change = Vec3.subtract(before, after); this.currentCameraOrientation = Camera.orientation; this.currentObjectPosition = Vec3.sum(this.currentObjectPosition, change); @@ -837,6 +971,7 @@ function MyController(hand) { this.lineOff(); this.overlayLineOff(); + this.particleBeamOff(); var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); this.activateEntity(this.grabbedEntity, grabbedProperties); @@ -975,6 +1110,7 @@ function MyController(hand) { this.pullTowardEquipPosition = function() { this.lineOff(); this.overlayLineOff(); + this.particleBeamOff(); var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); @@ -1164,6 +1300,7 @@ function MyController(hand) { this.lineOff(); this.overlayLineOff(); + this.particleBeamOff(); if (this.grabbedEntity !== null) { if (this.actionID !== null) { Entities.deleteAction(this.grabbedEntity, this.actionID); @@ -1285,10 +1422,10 @@ Controller.enableMapping(MAPPING_NAME); var handToDisable = 'none'; function update() { - if (handToDisable !== LEFT_HAND && handToDisable!=='both') { + if (handToDisable !== LEFT_HAND && handToDisable !== 'both') { leftController.update(); } - if (handToDisable !== RIGHT_HAND && handToDisable!=='both') { + if (handToDisable !== RIGHT_HAND && handToDisable !== 'both') { rightController.update(); } } @@ -1296,7 +1433,7 @@ function update() { Messages.subscribe('Hifi-Hand-Disabler'); handleHandDisablerMessages = function(channel, message, sender) { - + if (sender === MyAvatar.sessionUUID) { if (message === 'left') { handToDisable = LEFT_HAND; @@ -1304,11 +1441,11 @@ handleHandDisablerMessages = function(channel, message, sender) { if (message === 'right') { handToDisable = RIGHT_HAND; } - if(message==='both'){ - handToDisable='both'; + if (message === 'both') { + handToDisable = 'both'; } - if(message==='none'){ - handToDisable='none'; + if (message === 'none') { + handToDisable = 'none'; } } @@ -1323,4 +1460,4 @@ function cleanup() { } Script.scriptEnding.connect(cleanup); -Script.update.connect(update); +Script.update.connect(update); \ No newline at end of file From 1134d12ea0e9f92de38646e6d45c45e91f86bac3 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Wed, 9 Dec 2015 16:08:32 -0800 Subject: [PATCH 10/94] particles --- examples/controllers/handControllerGrab.js | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 20ca3811c0..b2af86c479 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -399,10 +399,7 @@ function MyController(hand) { } }; - //test particles instead of overlays - - this.handleParticleBeam = function(position, orientation) { var rotation = Quat.angleAxis(0, { @@ -435,11 +432,11 @@ function MyController(hand) { "green": 118.52941176470593, "blue": 255 }, - "maxParticles": 1000, + "maxParticles": 2000, "lifespan": 3, - "emitRate": 20, - "emitSpeed": 10, - "speedSpread": 0, + "emitRate": 50, + "emitSpeed": 2, + "speedSpread": 0.1, "emitOrientation": { "x": -0.7000000000000001, "y": 0, From 31526091d323a5ed8ad5038fa533697a6ac257bf Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Thu, 10 Dec 2015 11:13:35 -0800 Subject: [PATCH 11/94] create beams at start --- examples/controllers/handControllerGrab.js | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index b2af86c479..4348722df0 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -425,6 +425,7 @@ function MyController(hand) { type: "ParticleEffect", isEmitting: true, position: position, + visible: false, //rotation:Quat.fromPitchYawRollDegrees(-90.0, 0.0, 0.0), "name": "Particle Beam", "color": { @@ -436,12 +437,12 @@ function MyController(hand) { "lifespan": 3, "emitRate": 50, "emitSpeed": 2, - "speedSpread": 0.1, + "speedSpread": 0, "emitOrientation": { - "x": -0.7000000000000001, + "x": -1, "y": 0, "z": 0, - "w": 0.7071068286895752 + "w": 1 }, "emitDimensions": { "x": 0, @@ -486,7 +487,7 @@ function MyController(hand) { "alphaSpread": 0, "alphaStart": 1, "alphaFinish": 1, - "additiveBlending": 0, + "additiveBlending": 1, "textures": "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png" } @@ -502,6 +503,7 @@ function MyController(hand) { //rotation:rotation, rotation: orientation, position: position, + visible: true }) @@ -525,10 +527,12 @@ function MyController(hand) { this.particleBeamOff = function() { if (this.particleBeam !== null) { - Entities.deleteEntity(this.particleBeam) + Entities.editEntity(this.particleBeam, { + visible: false + }) } - this.particleBeam = null; + //this.particleBeam = null; } this.triggerPress = function(value) { @@ -1314,6 +1318,7 @@ function MyController(hand) { this.cleanup = function() { this.release(); this.endHandGrasp(); + Entities.deleteEntity(this.particleBeam); }; this.activateEntity = function(entityID, grabbedProperties) { @@ -1404,6 +1409,8 @@ function MyController(hand) { var rightController = new MyController(RIGHT_HAND); var leftController = new MyController(LEFT_HAND); +rightController.createParticleBeam(); +leftController.createParticleBeam(); var MAPPING_NAME = "com.highfidelity.handControllerGrab"; From 1000b602808f4a487012be55df9ec685824a8f29 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Thu, 10 Dec 2015 12:21:50 -0800 Subject: [PATCH 12/94] far beams --- examples/controllers/handControllerGrab.js | 65 ++++++++++++---------- 1 file changed, 35 insertions(+), 30 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 4348722df0..812eef07f8 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -400,7 +400,7 @@ function MyController(hand) { }; //test particles instead of overlays - this.handleParticleBeam = function(position, orientation) { + this.handleParticleBeam = function(position, orientation, color) { var rotation = Quat.angleAxis(0, { x: 1, @@ -409,18 +409,36 @@ function MyController(hand) { }); var finalRotation = Quat.multiply(orientation, rotation); + // var finalRotation = orientation + + if (this.particleBeam === null) { + print('create beam') + this.createParticleBeam(position, finalRotation, color) + } else { + print('update beam') + this.updateParticleBeam(position, finalRotation, color) + } + } + + this.handleDistantParticleBeam = function(handPosition, objectPosition, objectRotation, color) { + + + var handToObject = Vec3.subtract(objectPosition, handPosition); + var finalRotation = Quat.rotationBetween(Vec3.multiply(-1,Vec3.UP), handToObject); + + if (this.particleBeam === null) { print('create beam') - this.createParticleBeam(position, finalRotation) + this.createParticleBeam(objectPosition, finalRotation, color) } else { print('update beam') - this.updateParticleBeam(position, finalRotation) + this.updateParticleBeam(objectPosition, finalRotation, color) } } - this.createParticleBeam = function(position, orientation) { + this.createParticleBeam = function(position, orientation, color) { var particleBeamProperties = { type: "ParticleEffect", isEmitting: true, @@ -428,15 +446,11 @@ function MyController(hand) { visible: false, //rotation:Quat.fromPitchYawRollDegrees(-90.0, 0.0, 0.0), "name": "Particle Beam", - "color": { - "red": 110, - "green": 118.52941176470593, - "blue": 255 - }, + "color": color, "maxParticles": 2000, "lifespan": 3, "emitRate": 50, - "emitSpeed": 2, + "emitSpeed": 20, "speedSpread": 0, "emitOrientation": { "x": -1, @@ -473,16 +487,8 @@ function MyController(hand) { "green": 0, "blue": 0 }, - "colorStart": { - "red": 110, - "green": 118.52941176470593, - "blue": 255 - }, - "colorFinish": { - "red": 110, - "green": 118.52941176470593, - "blue": 255 - }, + "colorStart": color, + "colorFinish": color, "alpha": 1, "alphaSpread": 0, "alphaStart": 1, @@ -494,21 +500,19 @@ function MyController(hand) { this.particleBeam = Entities.addEntity(particleBeamProperties); } - this.updateParticleBeam = function(position, orientation, acceleration) { + this.updateParticleBeam = function(position, orientation, color) { print('O IN UPDATE:::' + JSON.stringify(orientation)) // var beamProps = Entities.getEntityProperties(this.particleBeam); Entities.editEntity(this.particleBeam, { - //rotation:rotation, rotation: orientation, position: position, - visible: true + visible: true, + color: color }) - // var emitO = Entities.getEntityProperties(this.particleBeam, "emitOrientation").emitOrientation; - // print('EMIT o :::' + JSON.stringify(emitO)); } this.lineOff = function() { @@ -784,8 +788,8 @@ function MyController(hand) { } //this.lineOn(distantPickRay.origin, Vec3.multiply(distantPickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); - this.overlayLineOn(distantPickRay.origin, Vec3.sum(distantPickRay.origin, Vec3.multiply(distantPickRay.direction, LINE_LENGTH)), NO_INTERSECT_COLOR); - this.handleParticleBeam(distantPickRay.origin, this.getHandRotation()); + //this.overlayLineOn(distantPickRay.origin, Vec3.sum(distantPickRay.origin, Vec3.multiply(distantPickRay.direction, LINE_LENGTH)), NO_INTERSECT_COLOR); + this.handleParticleBeam(distantPickRay.origin, this.getHandRotation(), NO_INTERSECT_COLOR); }; @@ -867,9 +871,8 @@ function MyController(hand) { return; } - this.lineOn(handPosition, Vec3.subtract(grabbedProperties.position, handPosition), INTERSECT_COLOR); - - // the action was set up on a previous call. update the targets. + // this.lineOn(handPosition, Vec3.subtract(grabbedProperties.position, handPosition), INTERSECT_COLOR); + // the action was set up on a previous call. update the targets. var radius = Vec3.distance(this.currentObjectPosition, handControllerPosition) * this.radiusScalar * DISTANCE_HOLDING_RADIUS_FACTOR; if (radius < 1.0) { @@ -950,6 +953,8 @@ function MyController(hand) { this.currentObjectPosition = Vec3.sum(this.currentObjectPosition, change); } + this.handleDistantParticleBeam(handPosition, grabbedProperties.position,this.currentObjectRotation, INTERSECT_COLOR) + Entities.updateAction(this.grabbedEntity, this.actionID, { targetPosition: this.currentObjectPosition, linearTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME, From c3e9cde7fbc59ce8dafb0f8c7c443212bd08662c Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Thu, 10 Dec 2015 13:44:53 -0800 Subject: [PATCH 13/94] beamz --- examples/controllers/handControllerGrab.js | 31 +++++++++++++--------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 812eef07f8..1106295cde 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -423,22 +423,25 @@ function MyController(hand) { this.handleDistantParticleBeam = function(handPosition, objectPosition, objectRotation, color) { - var handToObject = Vec3.subtract(objectPosition, handPosition); - var finalRotation = Quat.rotationBetween(Vec3.multiply(-1,Vec3.UP), handToObject); - + var handToObject = Vec3.subtract(objectPosition, handPosition); + var finalRotation = Quat.rotationBetween(Vec3.multiply(-1, Vec3.UP), handToObject); + var distance = Vec3.distance(handPosition, objectPosition); + var speed = distance * 1; + var lifepsan = distance / speed; + var lifespan = 1; if (this.particleBeam === null) { print('create beam') - this.createParticleBeam(objectPosition, finalRotation, color) + this.createParticleBeam(objectPosition, finalRotation, color, speed) } else { print('update beam') - this.updateParticleBeam(objectPosition, finalRotation, color) + this.updateParticleBeam(objectPosition, finalRotation, color, speed, lifepsan) } } - this.createParticleBeam = function(position, orientation, color) { + this.createParticleBeam = function(position, orientation, color, speed, lifepsan) { var particleBeamProperties = { type: "ParticleEffect", isEmitting: true, @@ -448,9 +451,9 @@ function MyController(hand) { "name": "Particle Beam", "color": color, "maxParticles": 2000, - "lifespan": 3, + "lifespan": 1, "emitRate": 50, - "emitSpeed": 20, + "emitSpeed": 1, "speedSpread": 0, "emitOrientation": { "x": -1, @@ -500,7 +503,7 @@ function MyController(hand) { this.particleBeam = Entities.addEntity(particleBeamProperties); } - this.updateParticleBeam = function(position, orientation, color) { + this.updateParticleBeam = function(position, orientation, color, speed, lifepsan) { print('O IN UPDATE:::' + JSON.stringify(orientation)) // var beamProps = Entities.getEntityProperties(this.particleBeam); @@ -509,7 +512,9 @@ function MyController(hand) { rotation: orientation, position: position, visible: true, - color: color + color: color, + emitSpeed: speed, + lifepsan: lifepsan }) @@ -871,8 +876,8 @@ function MyController(hand) { return; } - // this.lineOn(handPosition, Vec3.subtract(grabbedProperties.position, handPosition), INTERSECT_COLOR); - // the action was set up on a previous call. update the targets. + // this.lineOn(handPosition, Vec3.subtract(grabbedProperties.position, handPosition), INTERSECT_COLOR); + // the action was set up on a previous call. update the targets. var radius = Vec3.distance(this.currentObjectPosition, handControllerPosition) * this.radiusScalar * DISTANCE_HOLDING_RADIUS_FACTOR; if (radius < 1.0) { @@ -953,7 +958,7 @@ function MyController(hand) { this.currentObjectPosition = Vec3.sum(this.currentObjectPosition, change); } - this.handleDistantParticleBeam(handPosition, grabbedProperties.position,this.currentObjectRotation, INTERSECT_COLOR) + this.handleDistantParticleBeam(handPosition, grabbedProperties.position, this.currentObjectRotation, INTERSECT_COLOR) Entities.updateAction(this.grabbedEntity, this.actionID, { targetPosition: this.currentObjectPosition, From efaa30a509148a6f0da8921d88c0c4c732aa04ca Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Thu, 10 Dec 2015 16:18:40 -0800 Subject: [PATCH 14/94] rotatable light --- examples/controllers/handControllerGrab.js | 91 ++++++++++++++++++---- 1 file changed, 77 insertions(+), 14 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index d92cb35b43..4e612799f7 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -116,6 +116,17 @@ var DEFAULT_GRABBABLE_DATA = { invertSolidWhileHeld: false }; +var MODEL_LIGHT_POSITION = { + x: 0, + y: -0.3, + z: 0 +}; +var MODEL_LIGHT_ROTATION = Quat.angleAxis(-90, { + x: 1, + y: 0, + z: 0 +}); + // states for the state machine var STATE_OFF = 0; @@ -137,6 +148,7 @@ var STATE_WAITING_FOR_BUMPER_RELEASE = 15; var STATE_EQUIP_SPRING = 16; + function stateToName(state) { switch (state) { case STATE_OFF: @@ -218,6 +230,7 @@ function getSpatialOffsetPosition(hand, spatialKey) { } var yFlip = Quat.angleAxis(180, Vec3.UNIT_Y); + function getSpatialOffsetRotation(hand, spatialKey) { var rotation = Quat.IDENTITY; @@ -264,7 +277,8 @@ function MyController(hand) { this.overlayLine = null; this.particleBeam = null; - + this.spotlight = null; + this.ignoreIK = false; this.offsetPosition = Vec3.ZERO; this.offsetRotation = Quat.IDENTITY; @@ -420,7 +434,6 @@ function MyController(hand) { }); var finalRotation = Quat.multiply(orientation, rotation); - // var finalRotation = orientation if (this.particleBeam === null) { print('create beam') @@ -433,7 +446,6 @@ function MyController(hand) { this.handleDistantParticleBeam = function(handPosition, objectPosition, objectRotation, color) { - var handToObject = Vec3.subtract(objectPosition, handPosition); var finalRotation = Quat.rotationBetween(Vec3.multiply(-1, Vec3.UP), handToObject); @@ -453,6 +465,7 @@ function MyController(hand) { } this.createParticleBeam = function(position, orientation, color, speed, lifepsan) { + var particleBeamProperties = { type: "ParticleEffect", isEmitting: true, @@ -463,7 +476,7 @@ function MyController(hand) { "color": color, "maxParticles": 2000, "lifespan": 1, - "emitRate": 50, + "emitRate": 15, "emitSpeed": 1, "speedSpread": 0, "emitOrientation": { @@ -494,21 +507,21 @@ function MyController(hand) { }, "particleRadius": 0.02, "radiusSpread": 0, - "radiusStart": 0.01, - "radiusFinish": 0.01, - "colorSpread": { - "red": 0, - "green": 0, - "blue": 0 - }, - "colorStart": color, - "colorFinish": color, + // "radiusStart": 0.01, + // "radiusFinish": 0.01, + // "colorSpread": { + // "red": 0, + // "green": 0, + // "blue": 0 + // }, + // "colorStart": color, + // "colorFinish": color, "alpha": 1, "alphaSpread": 0, "alphaStart": 1, "alphaFinish": 1, "additiveBlending": 1, - "textures": "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png" + "textures": "https://hifi-content.s3.amazonaws.com/alan/dev/textures/grabsprite-3.png" } this.particleBeam = Entities.addEntity(particleBeamProperties); @@ -531,6 +544,49 @@ function MyController(hand) { } + + this.evalLightWorldTransform = function(modelPos, modelRot) { + return { + p: Vec3.sum(modelPos, Vec3.multiplyQbyV(modelRot, MODEL_LIGHT_POSITION)), + q: Quat.multiply(modelRot, MODEL_LIGHT_ROTATION) + }; + } + + this.createSpotlight = function(parentID, position) { + var LIFETIME = 100; + var modelProperties = Entities.getEntityProperties(parentID, ['position', 'rotation']); + var lightTransform = this.evalLightWorldTransform(modelProperties.position, modelProperties.rotation); + //this light casts the beam + var lightProperties = { + type: "Light", + isSpotlight: true, + dimensions: { + x: 2, + y: 2, + z: 20 + }, + parentID: parentID, + color: { + red: 255, + green: 255, + blue: 255 + }, + intensity: 2, + exponent: 0.3, + cutoff: 20, + lifetime: LIFETIME, + position: lightTransform.p, + rotation: lightTransform.q, + }; + + + if (this.spotlight === null) { + this.spotlight = Entities.addEntity(lightProperties); + } else { + + } + } + this.lineOff = function() { if (this.pointer !== null) { Entities.deleteEntity(this.pointer); @@ -555,6 +611,11 @@ function MyController(hand) { //this.particleBeam = null; } + this.spotlightOff = function() { + // Entities.deleteEntity(this.spotlight); + // this.spotlight = null; + } + this.triggerPress = function(value) { _this.rawTriggerValue = value; }; @@ -970,6 +1031,7 @@ function MyController(hand) { } this.handleDistantParticleBeam(handPosition, grabbedProperties.position, this.currentObjectRotation, INTERSECT_COLOR) + this.createSpotlight(this.grabbedEntity); Entities.updateAction(this.grabbedEntity, this.actionID, { targetPosition: this.currentObjectPosition, @@ -1331,6 +1393,7 @@ function MyController(hand) { this.lineOff(); this.overlayLineOff(); this.particleBeamOff(); + this.spotlightOff(); if (this.grabbedEntity !== null) { if (this.actionID !== null) { Entities.deleteAction(this.grabbedEntity, this.actionID); From 16ab7e74de0c12813694e4f006c21f8c89e6f739 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Thu, 10 Dec 2015 16:53:13 -0800 Subject: [PATCH 15/94] cleanup spotlights --- examples/controllers/handControllerGrab.js | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 4e612799f7..b787d8f0df 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -476,7 +476,7 @@ function MyController(hand) { "color": color, "maxParticles": 2000, "lifespan": 1, - "emitRate": 15, + "emitRate": 300, "emitSpeed": 1, "speedSpread": 0, "emitOrientation": { @@ -505,7 +505,7 @@ function MyController(hand) { "y": 0, "z": 0 }, - "particleRadius": 0.02, + "particleRadius": 0.01, "radiusSpread": 0, // "radiusStart": 0.01, // "radiusFinish": 0.01, @@ -583,7 +583,10 @@ function MyController(hand) { if (this.spotlight === null) { this.spotlight = Entities.addEntity(lightProperties); } else { - + // var rotationBetween = Quat.rotationBetween(Vec3.UP, ) + // Entities.editEntity(this.spotlight, { + // rotation: lightTransform.q + // }) } } @@ -612,8 +615,10 @@ function MyController(hand) { } this.spotlightOff = function() { - // Entities.deleteEntity(this.spotlight); - // this.spotlight = null; + if (this.spotlight !== null) { + Overlays.deleteOverlay(this.spotlight); + } + this.spotlight = null; } this.triggerPress = function(value) { From 2acc0df1f0bf29315d146b63d4e0f46342f63f37 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Thu, 10 Dec 2015 17:39:29 -0800 Subject: [PATCH 16/94] beams with spotlights --- examples/controllers/handControllerGrab.js | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index b787d8f0df..58eeb83b0e 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -559,7 +559,7 @@ function MyController(hand) { //this light casts the beam var lightProperties = { type: "Light", - isSpotlight: true, + isSpotlight: false, dimensions: { x: 2, y: 2, @@ -576,17 +576,18 @@ function MyController(hand) { cutoff: 20, lifetime: LIFETIME, position: lightTransform.p, - rotation: lightTransform.q, + // rotation: lightTransform.q, }; if (this.spotlight === null) { this.spotlight = Entities.addEntity(lightProperties); } else { - // var rotationBetween = Quat.rotationBetween(Vec3.UP, ) - // Entities.editEntity(this.spotlight, { - // rotation: lightTransform.q - // }) + Entities.editEntity(this.spotlight, { + parentID:parentID, + position:lightTransform.p, + visible:true + }) } } @@ -616,9 +617,13 @@ function MyController(hand) { this.spotlightOff = function() { if (this.spotlight !== null) { - Overlays.deleteOverlay(this.spotlight); + print('SHOULD DELETE SPOTLIGHT' + this.spotlight) + Entities.editEntity(this.spotlight,{ + visible:false + }) + //Entities.deleteEntity(this.spotlight); } - this.spotlight = null; + //this.spotlight = null; } this.triggerPress = function(value) { @@ -1416,6 +1421,7 @@ function MyController(hand) { this.release(); this.endHandGrasp(); Entities.deleteEntity(this.particleBeam); + Entities.deleteEntity(this.spotLight); }; this.activateEntity = function(entityID, grabbedProperties) { From 4e9d81f18737203e4bbdd60343aab98d65a210a1 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Fri, 11 Dec 2015 11:38:16 -0800 Subject: [PATCH 17/94] add support for various beam visualizations --- examples/controllers/handControllerGrab.js | 269 ++++++++++++++------- 1 file changed, 178 insertions(+), 91 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 58eeb83b0e..9e2f55b238 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -116,17 +116,14 @@ var DEFAULT_GRABBABLE_DATA = { invertSolidWhileHeld: false }; -var MODEL_LIGHT_POSITION = { - x: 0, - y: -0.3, - z: 0 -}; -var MODEL_LIGHT_ROTATION = Quat.angleAxis(-90, { - x: 1, - y: 0, - z: 0 -}); - +//we've created various ways of visualizing looking for and moving distant objects +var USE_ENTITY_LASERS_FOR_SEARCHING = false; +var USE_ENTITY_LASERS_FOR_MOVING = true; +var USE_OVERLAY_LINES_FOR_SEARCHING = true; +var USE_PARTICLE_BEAM_FOR_SEARCHING = false; +var USE_PARTICLE_BEAM_FOR_MOVING = false; +var USE_SPOTLIGHT = false; +var USE_POINTLIGHT = false; // states for the state machine var STATE_OFF = 0; @@ -275,9 +272,13 @@ function MyController(hand) { this.rawTriggerValue = 0; this.rawBumperValue = 0; + //for visualizations this.overlayLine = null; this.particleBeam = null; + + //for lights this.spotlight = null; + this.pointlight = null; this.ignoreIK = false; this.offsetPosition = Vec3.ZERO; @@ -366,33 +367,6 @@ function MyController(hand) { }); } - this.overlayLineOn = function(closePoint, farPoint, color) { - if (this.overlayLine === null) { - var lineProperties = { - lineWidth: 5, - start: closePoint, - end: farPoint, - color: color, - ignoreRayIntersection: true, // always ignore this - visible: true, - alpha: 1 - }; - - this.overlayLine = Overlays.addOverlay("line3d", lineProperties); - - } else { - var success = Overlays.editOverlay(this.overlayLine, { - lineWidth: 5, - start: closePoint, - end: farPoint, - color: color, - visible: true, - ignoreRayIntersection: true, // always ignore this - alpha: 1 - }); - } - } - this.lineOn = function(closePoint, farPoint, color) { // draw a line if (this.pointer === null) { @@ -424,7 +398,33 @@ function MyController(hand) { } }; - //test particles instead of overlays + this.overlayLineOn = function(closePoint, farPoint, color) { + if (this.overlayLine === null) { + var lineProperties = { + lineWidth: 5, + start: closePoint, + end: farPoint, + color: color, + ignoreRayIntersection: true, // always ignore this + visible: true, + alpha: 1 + }; + + this.overlayLine = Overlays.addOverlay("line3d", lineProperties); + + } else { + var success = Overlays.editOverlay(this.overlayLine, { + lineWidth: 5, + start: closePoint, + end: farPoint, + color: color, + visible: true, + ignoreRayIntersection: true, // always ignore this + alpha: 1 + }); + } + } + this.handleParticleBeam = function(position, orientation, color) { var rotation = Quat.angleAxis(0, { @@ -437,10 +437,10 @@ function MyController(hand) { if (this.particleBeam === null) { print('create beam') - this.createParticleBeam(position, finalRotation, color) + this.createParticleBeam(position, finalRotation, color); } else { print('update beam') - this.updateParticleBeam(position, finalRotation, color) + this.updateParticleBeam(position, finalRotation, color); } } @@ -456,11 +456,9 @@ function MyController(hand) { var lifespan = 1; if (this.particleBeam === null) { - print('create beam') - this.createParticleBeam(objectPosition, finalRotation, color, speed) + this.createParticleBeam(objectPosition, finalRotation, color, speed); } else { - print('update beam') - this.updateParticleBeam(objectPosition, finalRotation, color, speed, lifepsan) + this.updateParticleBeam(objectPosition, finalRotation, color, speed, lifepsan); } } @@ -475,10 +473,10 @@ function MyController(hand) { "name": "Particle Beam", "color": color, "maxParticles": 2000, - "lifespan": 1, - "emitRate": 300, - "emitSpeed": 1, - "speedSpread": 0, + "lifespan": LINE_LENGTH / 10, + "emitRate": 50, + "emitSpeed": 5, + "speedSpread": 2, "emitOrientation": { "x": -1, "y": 0, @@ -528,9 +526,6 @@ function MyController(hand) { } this.updateParticleBeam = function(position, orientation, color, speed, lifepsan) { - print('O IN UPDATE:::' + JSON.stringify(orientation)) - - // var beamProps = Entities.getEntityProperties(this.particleBeam); Entities.editEntity(this.particleBeam, { rotation: orientation, @@ -544,19 +539,72 @@ function MyController(hand) { } - this.evalLightWorldTransform = function(modelPos, modelRot) { + + var MODEL_LIGHT_POSITION = { + x: 0, + y: -0.3, + z: 0 + }; + + var MODEL_LIGHT_ROTATION = Quat.angleAxis(-90, { + x: 1, + y: 0, + z: 0 + }); + return { p: Vec3.sum(modelPos, Vec3.multiplyQbyV(modelRot, MODEL_LIGHT_POSITION)), q: Quat.multiply(modelRot, MODEL_LIGHT_ROTATION) }; } - this.createSpotlight = function(parentID, position) { + this.handleSpotlight = function(parentID, position) { var LIFETIME = 100; + + var modelProperties = Entities.getEntityProperties(parentID, ['position', 'rotation']); + + var lightTransform = this.evalLightWorldTransform(modelProperties.position, modelProperties.rotation); + var lightProperties = { + type: "Light", + isSpotlight: true, + dimensions: { + x: 2, + y: 2, + z: 20 + }, + parentID: parentID, + color: { + red: 255, + green: 255, + blue: 255 + }, + intensity: 2, + exponent: 0.3, + cutoff: 20, + lifetime: LIFETIME, + position: lightTransform.p, + }; + + if (this.spotlight === null) { + this.spotlight = Entities.addEntity(lightProperties); + } else { + Entities.editEntity(this.spotlight, { + parentID: parentID, + position: lightTransform.p, + //without this, this light would maintain rotation with its parent + rotation: Quat.fromPitchYawRollDegrees(-90, 0, 0), + visible: true + }) + } + } + + this.handlePointLight = function(parentID, position) { + var LIFETIME = 100; + var modelProperties = Entities.getEntityProperties(parentID, ['position', 'rotation']); var lightTransform = this.evalLightWorldTransform(modelProperties.position, modelProperties.rotation); - //this light casts the beam + var lightProperties = { type: "Light", isSpotlight: false, @@ -576,17 +624,18 @@ function MyController(hand) { cutoff: 20, lifetime: LIFETIME, position: lightTransform.p, - // rotation: lightTransform.q, }; + if (this.pointlight === null) { - if (this.spotlight === null) { - this.spotlight = Entities.addEntity(lightProperties); + print('create pointlight') + this.pointlight = Entities.addEntity(lightProperties); } else { - Entities.editEntity(this.spotlight, { - parentID:parentID, - position:lightTransform.p, - visible:true + print('update pointlight') + Entities.editEntity(this.pointlight, { + parentID: parentID, + position: lightTransform.p, + visible: true }) } } @@ -611,19 +660,25 @@ function MyController(hand) { visible: false }) } - - //this.particleBeam = null; } - this.spotlightOff = function() { + this.turnLightsOff = function() { + //use visibility for now instead of creating and deleting since deleting seems to crash if (this.spotlight !== null) { - print('SHOULD DELETE SPOTLIGHT' + this.spotlight) - Entities.editEntity(this.spotlight,{ - visible:false - }) - //Entities.deleteEntity(this.spotlight); + Entities.editEntity(this.spotlight, { + visible: false + }) + //Entities.deleteEntity(this.spotlight); + //this.spotLight=null; + } + + if (this.pointlight !== null) { + Entities.editEntity(this.pointlight, { + visible: false + }) + //Entities.deleteEntity(this.pointlight); + //this.pointlight = null; } - //this.spotlight = null; } this.triggerPress = function(value) { @@ -634,7 +689,6 @@ function MyController(hand) { _this.rawBumperValue = value; }; - this.updateSmoothedTrigger = function() { var triggerValue = this.rawTriggerValue; // smooth out trigger value @@ -663,7 +717,6 @@ function MyController(hand) { return _this.rawBumperValue < BUMPER_ON_VALUE; } - this.off = function() { if (this.triggerSmoothedSqueezed()) { this.lastPickTime = 0; @@ -874,10 +927,18 @@ function MyController(hand) { } } - //this.lineOn(distantPickRay.origin, Vec3.multiply(distantPickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); - //this.overlayLineOn(distantPickRay.origin, Vec3.sum(distantPickRay.origin, Vec3.multiply(distantPickRay.direction, LINE_LENGTH)), NO_INTERSECT_COLOR); - this.handleParticleBeam(distantPickRay.origin, this.getHandRotation(), NO_INTERSECT_COLOR); + //search line visualizations + if (USE_ENTITY_LASERS_FOR_SEARCHING === true) { + this.lineOn(distantPickRay.origin, Vec3.multiply(distantPickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); + } + if (USE_OVERLAY_LINES_FOR_SEARCHING === true) { + this.overlayLineOn(distantPickRay.origin, Vec3.sum(distantPickRay.origin, Vec3.multiply(distantPickRay.direction, LINE_LENGTH)), NO_INTERSECT_COLOR); + } + + if (USE_PARTICLE_BEAM_FOR_SEARCHING === true) { + this.handleParticleBeam(distantPickRay.origin, this.getHandRotation(), NO_INTERSECT_COLOR); + } }; @@ -931,10 +992,22 @@ function MyController(hand) { this.currentAvatarPosition = MyAvatar.position; this.currentAvatarOrientation = MyAvatar.orientation; - this.overlayLineOff(); - this.particleBeamOff(); + this.turnOffVisualizations(); }; + this.turnOffVisualizations = function() { + if (USE_ENTITY_LASERS_FOR_SEARCHING === true || USE_ENTITY_LASERS_FOR_MOVING === true) { + this.lineOff(); + } + if (USE_OVERLAY_LINES_FOR_SEARCHING === true) { + this.overlayLineOff(); + } + + if (USE_PARTICLE_BEAM_FOR_SEARCHING === true || USE_PARTICLE_BEAM_FOR_MOVING === true) { + this.particleBeamOff(); + } + } + this.continueDistanceHolding = function() { if (this.triggerSmoothedReleased()) { this.setState(STATE_RELEASE); @@ -958,7 +1031,7 @@ function MyController(hand) { return; } - // this.lineOn(handPosition, Vec3.subtract(grabbedProperties.position, handPosition), INTERSECT_COLOR); + // the action was set up on a previous call. update the targets. var radius = Vec3.distance(this.currentObjectPosition, handControllerPosition) * this.radiusScalar * DISTANCE_HOLDING_RADIUS_FACTOR; @@ -1040,8 +1113,22 @@ function MyController(hand) { this.currentObjectPosition = Vec3.sum(this.currentObjectPosition, change); } - this.handleDistantParticleBeam(handPosition, grabbedProperties.position, this.currentObjectRotation, INTERSECT_COLOR) - this.createSpotlight(this.grabbedEntity); + + //visualizations + if (USE_ENTITY_LASERS_FOR_MOVING === true) { + this.lineOn(handPosition, Vec3.subtract(grabbedProperties.position, handPosition), INTERSECT_COLOR); + } + if (USE_PARTICLE_BEAM_FOR_MOVING === true) { + this.handleDistantParticleBeam(handPosition, grabbedProperties.position, this.currentObjectRotation, INTERSECT_COLOR) + } + + if (USE_POINTLIGHT === true) { + this.handlePointLight(this.grabbedEntity); + } + + if (USE_SPOTLIGHT === true) { + this.handleSpotlight(this.grabbedEntity); + } Entities.updateAction(this.grabbedEntity, this.actionID, { targetPosition: this.currentObjectPosition, @@ -1050,6 +1137,7 @@ function MyController(hand) { angularTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME, ttl: ACTION_TTL }); + this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC); }; @@ -1063,9 +1151,7 @@ function MyController(hand) { return; } - this.lineOff(); - this.overlayLineOff(); - this.particleBeamOff(); + this.turnOffVisualizations(); var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); this.activateEntity(this.grabbedEntity, grabbedProperties); @@ -1207,9 +1293,8 @@ function MyController(hand) { }; this.pullTowardEquipPosition = function() { - this.lineOff(); - this.overlayLineOff(); - this.particleBeamOff(); + + this.turnOffVisualizations(); var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); @@ -1400,10 +1485,9 @@ function MyController(hand) { this.release = function() { - this.lineOff(); - this.overlayLineOff(); - this.particleBeamOff(); - this.spotlightOff(); + this.turnLightsOff(); + this.turnOffVisualizations(); + if (this.grabbedEntity !== null) { if (this.actionID !== null) { Entities.deleteAction(this.grabbedEntity, this.actionID); @@ -1422,6 +1506,7 @@ function MyController(hand) { this.endHandGrasp(); Entities.deleteEntity(this.particleBeam); Entities.deleteEntity(this.spotLight); + Entities.deleteEntity(this.pointLight); }; this.activateEntity = function(entityID, grabbedProperties) { @@ -1512,6 +1597,8 @@ function MyController(hand) { var rightController = new MyController(RIGHT_HAND); var leftController = new MyController(LEFT_HAND); + +//reload the particle beams rightController.createParticleBeam(); leftController.createParticleBeam(); From 11aaeb1ec00a76273f639648d47a00e4f691e0aa Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Fri, 11 Dec 2015 14:48:07 -0800 Subject: [PATCH 18/94] only create particle beams if set --- examples/controllers/handControllerGrab.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 9e2f55b238..217b5b6632 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -1599,8 +1599,10 @@ var rightController = new MyController(RIGHT_HAND); var leftController = new MyController(LEFT_HAND); //reload the particle beams -rightController.createParticleBeam(); -leftController.createParticleBeam(); +if (USE_PARTICLE_BEAM_FOR_SEARCHING === true || USE_PARTICLE_BEAM_FOR_MOVING === true) { + rightController.createParticleBeam(); + leftController.createParticleBeam(); +} var MAPPING_NAME = "com.highfidelity.handControllerGrab"; From 1b1edf6f7ab8a8e94e7d78df9eaf2b42d81c703b Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Fri, 11 Dec 2015 17:23:35 -0800 Subject: [PATCH 19/94] actually delete lights now that it doesnt crash --- examples/controllers/handControllerGrab.js | 30 +++++----------------- 1 file changed, 6 insertions(+), 24 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 217b5b6632..ae95ab47fd 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -122,7 +122,7 @@ var USE_ENTITY_LASERS_FOR_MOVING = true; var USE_OVERLAY_LINES_FOR_SEARCHING = true; var USE_PARTICLE_BEAM_FOR_SEARCHING = false; var USE_PARTICLE_BEAM_FOR_MOVING = false; -var USE_SPOTLIGHT = false; +var USE_SPOTLIGHT = true; var USE_POINTLIGHT = false; // states for the state machine @@ -436,10 +436,8 @@ function MyController(hand) { var finalRotation = Quat.multiply(orientation, rotation); if (this.particleBeam === null) { - print('create beam') this.createParticleBeam(position, finalRotation, color); } else { - print('update beam') this.updateParticleBeam(position, finalRotation, color); } } @@ -590,11 +588,8 @@ function MyController(hand) { this.spotlight = Entities.addEntity(lightProperties); } else { Entities.editEntity(this.spotlight, { - parentID: parentID, - position: lightTransform.p, //without this, this light would maintain rotation with its parent rotation: Quat.fromPitchYawRollDegrees(-90, 0, 0), - visible: true }) } } @@ -627,16 +622,9 @@ function MyController(hand) { }; if (this.pointlight === null) { - - print('create pointlight') this.pointlight = Entities.addEntity(lightProperties); } else { - print('update pointlight') - Entities.editEntity(this.pointlight, { - parentID: parentID, - position: lightTransform.p, - visible: true - }) + } } @@ -665,19 +653,13 @@ function MyController(hand) { this.turnLightsOff = function() { //use visibility for now instead of creating and deleting since deleting seems to crash if (this.spotlight !== null) { - Entities.editEntity(this.spotlight, { - visible: false - }) - //Entities.deleteEntity(this.spotlight); - //this.spotLight=null; + Entities.deleteEntity(this.spotlight); + this.spotlight = null; } if (this.pointlight !== null) { - Entities.editEntity(this.pointlight, { - visible: false - }) - //Entities.deleteEntity(this.pointlight); - //this.pointlight = null; + Entities.deleteEntity(this.pointlight); + this.pointlight = null; } } From 70a0e51cc9b5898dc49b1134f84a6f96d50b7aff Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Fri, 11 Dec 2015 17:59:03 -0800 Subject: [PATCH 20/94] back to default state --- examples/controllers/handControllerGrab.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index ae95ab47fd..fc217de03a 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -122,7 +122,7 @@ var USE_ENTITY_LASERS_FOR_MOVING = true; var USE_OVERLAY_LINES_FOR_SEARCHING = true; var USE_PARTICLE_BEAM_FOR_SEARCHING = false; var USE_PARTICLE_BEAM_FOR_MOVING = false; -var USE_SPOTLIGHT = true; +var USE_SPOTLIGHT = false; var USE_POINTLIGHT = false; // states for the state machine From fe05542440f2c74668cbec2859c2c8227eb91e42 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Sat, 12 Dec 2015 14:30:34 -0800 Subject: [PATCH 21/94] more vis --- examples/controllers/handControllerGrab.js | 80 ++++++++++++---------- 1 file changed, 43 insertions(+), 37 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index fc217de03a..afad62412e 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -1,5 +1,4 @@ // handControllerGrab.js -// examples // // Created by Eric Levin on 9/2/15 // Additions by James B. Pollack @imgntn on 9/24/2015 @@ -7,6 +6,7 @@ // Copyright 2015 High Fidelity, Inc. // // Grabs physically moveable entities with hydra-like controllers; it works for either near or far objects. +// Also supports touch and equipping objects. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -14,7 +14,6 @@ Script.include("../libraries/utils.js"); - // // add lines where the hand ray picking is happening // @@ -54,6 +53,7 @@ var LINE_ENTITY_DIMENSIONS = { y: 1000, z: 1000 }; + var LINE_LENGTH = 500; var PICK_MAX_DISTANCE = 500; // max length of pick-ray @@ -120,6 +120,7 @@ var DEFAULT_GRABBABLE_DATA = { var USE_ENTITY_LASERS_FOR_SEARCHING = false; var USE_ENTITY_LASERS_FOR_MOVING = true; var USE_OVERLAY_LINES_FOR_SEARCHING = true; +var USE_OVERLAY_LINES_FOR_MOVING = false; var USE_PARTICLE_BEAM_FOR_SEARCHING = false; var USE_PARTICLE_BEAM_FOR_MOVING = false; var USE_SPOTLIGHT = false; @@ -345,7 +346,7 @@ function MyController(hand) { print("STATE: " + stateToName(this.state) + " --> " + stateToName(newState) + ", hand: " + this.hand); } this.state = newState; - } + }; this.debugLine = function(closePoint, farPoint, color) { Entities.addEntity({ @@ -365,7 +366,7 @@ function MyController(hand) { } }) }); - } + }; this.lineOn = function(closePoint, farPoint, color) { // draw a line @@ -423,7 +424,7 @@ function MyController(hand) { alpha: 1 }); } - } + }; this.handleParticleBeam = function(position, orientation, color) { @@ -440,7 +441,7 @@ function MyController(hand) { } else { this.updateParticleBeam(position, finalRotation, color); } - } + }; this.handleDistantParticleBeam = function(handPosition, objectPosition, objectRotation, color) { @@ -458,7 +459,7 @@ function MyController(hand) { } else { this.updateParticleBeam(objectPosition, finalRotation, color, speed, lifepsan); } - } + }; this.createParticleBeam = function(position, orientation, color, speed, lifepsan) { @@ -521,7 +522,7 @@ function MyController(hand) { } this.particleBeam = Entities.addEntity(particleBeamProperties); - } + }; this.updateParticleBeam = function(position, orientation, color, speed, lifepsan) { @@ -535,7 +536,7 @@ function MyController(hand) { }) - } + }; this.evalLightWorldTransform = function(modelPos, modelRot) { @@ -555,7 +556,7 @@ function MyController(hand) { p: Vec3.sum(modelPos, Vec3.multiplyQbyV(modelRot, MODEL_LIGHT_POSITION)), q: Quat.multiply(modelRot, MODEL_LIGHT_ROTATION) }; - } + }; this.handleSpotlight = function(parentID, position) { var LIFETIME = 100; @@ -592,7 +593,7 @@ function MyController(hand) { rotation: Quat.fromPitchYawRollDegrees(-90, 0, 0), }) } - } + }; this.handlePointLight = function(parentID, position) { var LIFETIME = 100; @@ -626,7 +627,7 @@ function MyController(hand) { } else { } - } + }; this.lineOff = function() { if (this.pointer !== null) { @@ -651,7 +652,6 @@ function MyController(hand) { } this.turnLightsOff = function() { - //use visibility for now instead of creating and deleting since deleting seems to crash if (this.spotlight !== null) { Entities.deleteEntity(this.spotlight); this.spotlight = null; @@ -661,7 +661,22 @@ function MyController(hand) { Entities.deleteEntity(this.pointlight); this.pointlight = null; } - } + }; + + + this.turnOffVisualizations = function() { + if (USE_ENTITY_LASERS_FOR_SEARCHING === true || USE_ENTITY_LASERS_FOR_MOVING === true) { + this.lineOff(); + } + + if (USE_OVERLAY_LINES_FOR_SEARCHING === true || USE_OVERLAY_LINES_FOR_MOVING === true) { + this.overlayLineOff(); + } + + if (USE_PARTICLE_BEAM_FOR_SEARCHING === true || USE_PARTICLE_BEAM_FOR_MOVING === true) { + this.particleBeamOff(); + } + }; this.triggerPress = function(value) { _this.rawTriggerValue = value; @@ -693,11 +708,11 @@ function MyController(hand) { this.bumperSqueezed = function() { return _this.rawBumperValue > BUMPER_ON_VALUE; - } + }; this.bumperReleased = function() { return _this.rawBumperValue < BUMPER_ON_VALUE; - } + }; this.off = function() { if (this.triggerSmoothedSqueezed()) { @@ -710,7 +725,7 @@ function MyController(hand) { this.setState(STATE_EQUIP_SEARCHING); return; } - } + }; this.search = function() { this.grabbedEntity = null; @@ -977,19 +992,6 @@ function MyController(hand) { this.turnOffVisualizations(); }; - this.turnOffVisualizations = function() { - if (USE_ENTITY_LASERS_FOR_SEARCHING === true || USE_ENTITY_LASERS_FOR_MOVING === true) { - this.lineOff(); - } - if (USE_OVERLAY_LINES_FOR_SEARCHING === true) { - this.overlayLineOff(); - } - - if (USE_PARTICLE_BEAM_FOR_SEARCHING === true || USE_PARTICLE_BEAM_FOR_MOVING === true) { - this.particleBeamOff(); - } - } - this.continueDistanceHolding = function() { if (this.triggerSmoothedReleased()) { this.setState(STATE_RELEASE); @@ -1100,14 +1102,15 @@ function MyController(hand) { if (USE_ENTITY_LASERS_FOR_MOVING === true) { this.lineOn(handPosition, Vec3.subtract(grabbedProperties.position, handPosition), INTERSECT_COLOR); } + if (USE_OVERLAY_LINES_FOR_MOVING === true) { + this.overlayLineOn(handPosition, Vec3.subtract(grabbedProperties.position, handPosition), INTERSECT_COLOR); + } if (USE_PARTICLE_BEAM_FOR_MOVING === true) { this.handleDistantParticleBeam(handPosition, grabbedProperties.position, this.currentObjectRotation, INTERSECT_COLOR) } - if (USE_POINTLIGHT === true) { this.handlePointLight(this.grabbedEntity); } - if (USE_SPOTLIGHT === true) { this.handleSpotlight(this.grabbedEntity); } @@ -1399,6 +1402,7 @@ function MyController(hand) { }; _this.allTouchedIDs = {}; + this.touchTest = function() { var maxDistance = 0.05; var leftHandPosition = MyAvatar.getLeftPalmPosition(); @@ -1559,28 +1563,29 @@ function MyController(hand) { } //return an object with our updated settings return result; - } + }; this.graspHandler = null + this.startHandGrasp = function() { if (this.hand === RIGHT_HAND) { this.graspHandler = MyAvatar.addAnimationStateHandler(this.graspHand, ['isRightHandGrab']); } else if (this.hand === LEFT_HAND) { this.graspHandler = MyAvatar.addAnimationStateHandler(this.graspHand, ['isLeftHandGrab']); } - } + }; this.endHandGrasp = function() { // Tell the animation system we don't need any more callbacks. MyAvatar.removeAnimationStateHandler(this.graspHandler); - } + }; -} +}; var rightController = new MyController(RIGHT_HAND); var leftController = new MyController(LEFT_HAND); -//reload the particle beams +//preload the particle beams so that they are full length when you start searching if (USE_PARTICLE_BEAM_FOR_SEARCHING === true || USE_PARTICLE_BEAM_FOR_MOVING === true) { rightController.createParticleBeam(); leftController.createParticleBeam(); @@ -1597,6 +1602,7 @@ mapping.from([Controller.Standard.LB]).peek().to(leftController.bumperPress); Controller.enableMapping(MAPPING_NAME); +//the section below allows the grab script to listen for messages that disable either one or both hands. useful for two handed items var handToDisable = 'none'; function update() { From 0ddc2ba76e22359f202cb2205efbf6da24ec08a1 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Sat, 12 Dec 2015 16:18:15 -0800 Subject: [PATCH 22/94] rename lasers to lines --- examples/controllers/handControllerGrab.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index afad62412e..f8fe1b821c 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -117,8 +117,8 @@ var DEFAULT_GRABBABLE_DATA = { }; //we've created various ways of visualizing looking for and moving distant objects -var USE_ENTITY_LASERS_FOR_SEARCHING = false; -var USE_ENTITY_LASERS_FOR_MOVING = true; +var USE_ENTITY_LINES_FOR_SEARCHING = false; +var USE_ENTITY_LINES_FOR_MOVING = true; var USE_OVERLAY_LINES_FOR_SEARCHING = true; var USE_OVERLAY_LINES_FOR_MOVING = false; var USE_PARTICLE_BEAM_FOR_SEARCHING = false; @@ -665,7 +665,7 @@ function MyController(hand) { this.turnOffVisualizations = function() { - if (USE_ENTITY_LASERS_FOR_SEARCHING === true || USE_ENTITY_LASERS_FOR_MOVING === true) { + if (USE_ENTITY_LINES_FOR_SEARCHING === true || USE_ENTITY_LINES_FOR_MOVING === true) { this.lineOff(); } @@ -925,7 +925,7 @@ function MyController(hand) { } //search line visualizations - if (USE_ENTITY_LASERS_FOR_SEARCHING === true) { + if (USE_ENTITY_LINES_FOR_SEARCHING === true) { this.lineOn(distantPickRay.origin, Vec3.multiply(distantPickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); } @@ -1099,7 +1099,7 @@ function MyController(hand) { //visualizations - if (USE_ENTITY_LASERS_FOR_MOVING === true) { + if (USE_ENTITY_LINES_FOR_MOVING === true) { this.lineOn(handPosition, Vec3.subtract(grabbedProperties.position, handPosition), INTERSECT_COLOR); } if (USE_OVERLAY_LINES_FOR_MOVING === true) { From a0b698c0283ce162720ad13465d04ccb8105439d Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Sat, 12 Dec 2015 17:33:54 -0800 Subject: [PATCH 23/94] reorg props --- examples/controllers/handControllerGrab.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index f8fe1b821c..382d5cac7a 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -118,11 +118,13 @@ var DEFAULT_GRABBABLE_DATA = { //we've created various ways of visualizing looking for and moving distant objects var USE_ENTITY_LINES_FOR_SEARCHING = false; -var USE_ENTITY_LINES_FOR_MOVING = true; var USE_OVERLAY_LINES_FOR_SEARCHING = true; -var USE_OVERLAY_LINES_FOR_MOVING = false; var USE_PARTICLE_BEAM_FOR_SEARCHING = false; + +var USE_ENTITY_LINES_FOR_MOVING = true; +var USE_OVERLAY_LINES_FOR_MOVING = false; var USE_PARTICLE_BEAM_FOR_MOVING = false; + var USE_SPOTLIGHT = false; var USE_POINTLIGHT = false; From ab5b783cb7dbb3bf6f1878bf2f2e134ef331406e Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Mon, 14 Dec 2015 09:01:45 -0800 Subject: [PATCH 24/94] zone tweal --- examples/flowArts/lightTrails.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/flowArts/lightTrails.js b/examples/flowArts/lightTrails.js index e063897eb8..8e0dc98bbb 100644 --- a/examples/flowArts/lightTrails.js +++ b/examples/flowArts/lightTrails.js @@ -23,7 +23,7 @@ var MAX_POINTS_PER_LINE = 50; var LIFETIME = 6000; var DRAWING_DEPTH = 0.8; -var LINE_DIMENSIONS = 20; +var LINE_DIMENSIONS = 100; var lightZone = Entities.addEntity({ type: "Zone", @@ -78,9 +78,9 @@ function controller(side, triggerAction) { type: 'Light', position: MyAvatar.position, dimensions: { - x: 20, - y: 20, - z: 20 + x: 30, + y: 30, + z: 30 }, color: colorPalette[randInt(0, colorPalette.length)], intensity: 5 From e43b60f9f54c27b72e2377f32defe489647a415f Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Mon, 14 Dec 2015 11:40:47 -0800 Subject: [PATCH 25/94] other variations --- examples/controllers/handControllerGrab.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 382d5cac7a..cd82db22b9 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -1105,7 +1105,7 @@ function MyController(hand) { this.lineOn(handPosition, Vec3.subtract(grabbedProperties.position, handPosition), INTERSECT_COLOR); } if (USE_OVERLAY_LINES_FOR_MOVING === true) { - this.overlayLineOn(handPosition, Vec3.subtract(grabbedProperties.position, handPosition), INTERSECT_COLOR); + this.overlayLineOn(handPosition, grabbedProperties.position, INTERSECT_COLOR); } if (USE_PARTICLE_BEAM_FOR_MOVING === true) { this.handleDistantParticleBeam(handPosition, grabbedProperties.position, this.currentObjectRotation, INTERSECT_COLOR) From 4d9cb6a6df3f3c07866ca4d6fa2170b939f7196b Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Mon, 14 Dec 2015 11:41:34 -0800 Subject: [PATCH 26/94] other variations --- .../handControllerGrab-all-overlays.js | 1649 +++++++++++++++++ .../handControllerGrab-particles.js | 1649 +++++++++++++++++ .../handControllerGrab-pointlight.js | 1649 +++++++++++++++++ .../handControllerGrab-spotlight.js | 1649 +++++++++++++++++ 4 files changed, 6596 insertions(+) create mode 100644 examples/controllers/handControllerGrab-all-overlays.js create mode 100644 examples/controllers/handControllerGrab-particles.js create mode 100644 examples/controllers/handControllerGrab-pointlight.js create mode 100644 examples/controllers/handControllerGrab-spotlight.js diff --git a/examples/controllers/handControllerGrab-all-overlays.js b/examples/controllers/handControllerGrab-all-overlays.js new file mode 100644 index 0000000000..acb47b2260 --- /dev/null +++ b/examples/controllers/handControllerGrab-all-overlays.js @@ -0,0 +1,1649 @@ +// handControllerGrab.js +// +// Created by Eric Levin on 9/2/15 +// Additions by James B. Pollack @imgntn on 9/24/2015 +// Additions By Seth Alves on 10/20/2015 +// Copyright 2015 High Fidelity, Inc. +// +// Grabs physically moveable entities with hydra-like controllers; it works for either near or far objects. +// Also supports touch and equipping objects. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +/*global print, MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, Audio, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt, pointInExtents, vec3equal, setEntityCustomData, getEntityCustomData */ + +Script.include("../libraries/utils.js"); + +// +// add lines where the hand ray picking is happening +// +var WANT_DEBUG = false; + +// +// these tune time-averaging and "on" value for analog trigger +// + +var TRIGGER_SMOOTH_RATIO = 0.1; // 0.0 disables smoothing of trigger value +var TRIGGER_ON_VALUE = 0.4; +var TRIGGER_OFF_VALUE = 0.15; + +var BUMPER_ON_VALUE = 0.5; + +// +// distant manipulation +// + +var DISTANCE_HOLDING_RADIUS_FACTOR = 3.5; // multiplied by distance between hand and object +var DISTANCE_HOLDING_ACTION_TIMEFRAME = 0.1; // how quickly objects move to their new position +var DISTANCE_HOLDING_ROTATION_EXAGGERATION_FACTOR = 2.0; // object rotates this much more than hand did +var MOVE_WITH_HEAD = true; // experimental head-controll of distantly held objects + +var NO_INTERSECT_COLOR = { + red: 10, + green: 10, + blue: 255 +}; // line color when pick misses +var INTERSECT_COLOR = { + red: 250, + green: 10, + blue: 10 +}; // line color when pick hits +var LINE_ENTITY_DIMENSIONS = { + x: 1000, + y: 1000, + z: 1000 +}; + +var LINE_LENGTH = 500; +var PICK_MAX_DISTANCE = 500; // max length of pick-ray + +// +// near grabbing +// + +var GRAB_RADIUS = 0.03; // if the ray misses but an object is this close, it will still be selected +var NEAR_GRABBING_ACTION_TIMEFRAME = 0.05; // how quickly objects move to their new position +var NEAR_GRABBING_VELOCITY_SMOOTH_RATIO = 1.0; // adjust time-averaging of held object's velocity. 1.0 to disable. +var NEAR_PICK_MAX_DISTANCE = 0.3; // max length of pick-ray for close grabbing to be selected +var RELEASE_VELOCITY_MULTIPLIER = 1.5; // affects throwing things +var PICK_BACKOFF_DISTANCE = 0.2; // helps when hand is intersecting the grabble object +var NEAR_GRABBING_KINEMATIC = true; // force objects to be kinematic when near-grabbed + +// +// equip +// + +var EQUIP_SPRING_SHUTOFF_DISTANCE = 0.05; +var EQUIP_SPRING_TIMEFRAME = 0.4; // how quickly objects move to their new position + +// +// other constants +// + +var RIGHT_HAND = 1; +var LEFT_HAND = 0; + +var ZERO_VEC = { + x: 0, + y: 0, + z: 0 +}; + +var NULL_ACTION_ID = "{00000000-0000-0000-000000000000}"; +var MSEC_PER_SEC = 1000.0; + +// these control how long an abandoned pointer line or action will hang around +var LIFETIME = 10; +var ACTION_TTL = 15; // seconds +var ACTION_TTL_REFRESH = 5; +var PICKS_PER_SECOND_PER_HAND = 5; +var MSECS_PER_SEC = 1000.0; +var GRABBABLE_PROPERTIES = [ + "position", + "rotation", + "gravity", + "ignoreForCollisions", + "collisionsWillMove", + "locked", + "name" +]; + +var GRABBABLE_DATA_KEY = "grabbableKey"; // shared with grab.js +var GRAB_USER_DATA_KEY = "grabKey"; // shared with grab.js + +var DEFAULT_GRABBABLE_DATA = { + grabbable: true, + invertSolidWhileHeld: false +}; + +//we've created various ways of visualizing looking for and moving distant objects +var USE_ENTITY_LINES_FOR_SEARCHING = false; +var USE_OVERLAY_LINES_FOR_SEARCHING = true; +var USE_PARTICLE_BEAM_FOR_SEARCHING = false; + +var USE_ENTITY_LINES_FOR_MOVING = false; +var USE_OVERLAY_LINES_FOR_MOVING = true; +var USE_PARTICLE_BEAM_FOR_MOVING = false; + +var USE_SPOTLIGHT = false; +var USE_POINTLIGHT = false; + +// states for the state machine +var STATE_OFF = 0; +var STATE_SEARCHING = 1; +var STATE_DISTANCE_HOLDING = 2; +var STATE_CONTINUE_DISTANCE_HOLDING = 3; +var STATE_NEAR_GRABBING = 4; +var STATE_CONTINUE_NEAR_GRABBING = 5; +var STATE_NEAR_TRIGGER = 6; +var STATE_CONTINUE_NEAR_TRIGGER = 7; +var STATE_FAR_TRIGGER = 8; +var STATE_CONTINUE_FAR_TRIGGER = 9; +var STATE_RELEASE = 10; +var STATE_EQUIP_SEARCHING = 11; +var STATE_EQUIP = 12 +var STATE_CONTINUE_EQUIP_BD = 13; // equip while bumper is still held down +var STATE_CONTINUE_EQUIP = 14; +var STATE_WAITING_FOR_BUMPER_RELEASE = 15; +var STATE_EQUIP_SPRING = 16; + + + +function stateToName(state) { + switch (state) { + case STATE_OFF: + return "off"; + case STATE_SEARCHING: + return "searching"; + case STATE_DISTANCE_HOLDING: + return "distance_holding"; + case STATE_CONTINUE_DISTANCE_HOLDING: + return "continue_distance_holding"; + case STATE_NEAR_GRABBING: + return "near_grabbing"; + case STATE_CONTINUE_NEAR_GRABBING: + return "continue_near_grabbing"; + case STATE_NEAR_TRIGGER: + return "near_trigger"; + case STATE_CONTINUE_NEAR_TRIGGER: + return "continue_near_trigger"; + case STATE_FAR_TRIGGER: + return "far_trigger"; + case STATE_CONTINUE_FAR_TRIGGER: + return "continue_far_trigger"; + case STATE_RELEASE: + return "release"; + case STATE_EQUIP_SEARCHING: + return "equip_searching"; + case STATE_EQUIP: + return "equip"; + case STATE_CONTINUE_EQUIP_BD: + return "continue_equip_bd"; + case STATE_CONTINUE_EQUIP: + return "continue_equip"; + case STATE_WAITING_FOR_BUMPER_RELEASE: + return "waiting_for_bumper_release"; + case STATE_EQUIP_SPRING: + return "state_equip_spring"; + } + + return "unknown"; +} + +function getTag() { + return "grab-" + MyAvatar.sessionUUID; +} + +function entityIsGrabbedByOther(entityID) { + // by convention, a distance grab sets the tag of its action to be grab-*owner-session-id*. + var actionIDs = Entities.getActionIDs(entityID); + for (var actionIndex = 0; actionIndex < actionIDs.length; actionIndex++) { + var actionID = actionIDs[actionIndex]; + var actionArguments = Entities.getActionArguments(entityID, actionID); + var tag = actionArguments["tag"]; + if (tag == getTag()) { + // we see a grab-*uuid* shaped tag, but it's our tag, so that's okay. + continue; + } + if (tag.slice(0, 5) == "grab-") { + // we see a grab-*uuid* shaped tag and it's not ours, so someone else is grabbing it. + return true; + } + } + return false; +} + +function getSpatialOffsetPosition(hand, spatialKey) { + var position = Vec3.ZERO; + + if (hand !== RIGHT_HAND && spatialKey.leftRelativePosition) { + position = spatialKey.leftRelativePosition; + } + if (hand === RIGHT_HAND && spatialKey.rightRelativePosition) { + position = spatialKey.rightRelativePosition; + } + if (spatialKey.relativePosition) { + position = spatialKey.relativePosition; + } + + return position; +} + +var yFlip = Quat.angleAxis(180, Vec3.UNIT_Y); + +function getSpatialOffsetRotation(hand, spatialKey) { + var rotation = Quat.IDENTITY; + + if (hand !== RIGHT_HAND && spatialKey.leftRelativeRotation) { + rotation = spatialKey.leftRelativeRotation; + } + if (hand === RIGHT_HAND && spatialKey.rightRelativeRotation) { + rotation = spatialKey.rightRelativeRotation; + } + if (spatialKey.relativeRotation) { + rotation = spatialKey.relativeRotation; + } + + // Flip left hand + if (hand !== RIGHT_HAND) { + rotation = Quat.multiply(yFlip, rotation); + } + + return rotation; +} + +function MyController(hand) { + this.hand = hand; + if (this.hand === RIGHT_HAND) { + this.getHandPosition = MyAvatar.getRightPalmPosition; + this.getHandRotation = MyAvatar.getRightPalmRotation; + } else { + this.getHandPosition = MyAvatar.getLeftPalmPosition; + this.getHandRotation = MyAvatar.getLeftPalmRotation; + } + + var SPATIAL_CONTROLLERS_PER_PALM = 2; + var TIP_CONTROLLER_OFFSET = 1; + this.palm = SPATIAL_CONTROLLERS_PER_PALM * hand; + this.tip = SPATIAL_CONTROLLERS_PER_PALM * hand + TIP_CONTROLLER_OFFSET; + + this.actionID = null; // action this script created... + this.grabbedEntity = null; // on this entity. + this.state = STATE_OFF; + this.pointer = null; // entity-id of line object + this.triggerValue = 0; // rolling average of trigger value + this.rawTriggerValue = 0; + this.rawBumperValue = 0; + + //for visualizations + this.overlayLine = null; + this.particleBeam = null; + + //for lights + this.spotlight = null; + this.pointlight = null; + + this.ignoreIK = false; + this.offsetPosition = Vec3.ZERO; + this.offsetRotation = Quat.IDENTITY; + + var _this = this; + + this.update = function() { + + this.updateSmoothedTrigger(); + + switch (this.state) { + case STATE_OFF: + this.off(); + this.touchTest(); + break; + case STATE_SEARCHING: + this.search(); + break; + case STATE_EQUIP_SEARCHING: + this.search(); + break; + case STATE_DISTANCE_HOLDING: + this.distanceHolding(); + break; + case STATE_CONTINUE_DISTANCE_HOLDING: + this.continueDistanceHolding(); + break; + case STATE_NEAR_GRABBING: + case STATE_EQUIP: + this.nearGrabbing(); + break; + case STATE_WAITING_FOR_BUMPER_RELEASE: + this.waitingForBumperRelease(); + break; + case STATE_EQUIP_SPRING: + this.pullTowardEquipPosition() + break; + case STATE_CONTINUE_NEAR_GRABBING: + case STATE_CONTINUE_EQUIP_BD: + case STATE_CONTINUE_EQUIP: + this.continueNearGrabbing(); + break; + case STATE_NEAR_TRIGGER: + this.nearTrigger(); + break; + case STATE_CONTINUE_NEAR_TRIGGER: + this.continueNearTrigger(); + break; + case STATE_FAR_TRIGGER: + this.farTrigger(); + break; + case STATE_CONTINUE_FAR_TRIGGER: + this.continueFarTrigger(); + break; + case STATE_RELEASE: + this.release(); + break; + } + }; + + this.setState = function(newState) { + if (WANT_DEBUG) { + print("STATE: " + stateToName(this.state) + " --> " + stateToName(newState) + ", hand: " + this.hand); + } + this.state = newState; + }; + + this.debugLine = function(closePoint, farPoint, color) { + Entities.addEntity({ + type: "Line", + name: "Grab Debug Entity", + dimensions: LINE_ENTITY_DIMENSIONS, + visible: true, + position: closePoint, + linePoints: [ZERO_VEC, farPoint], + color: color, + lifetime: 0.1, + collisionsWillMove: false, + ignoreForCollisions: true, + userData: JSON.stringify({ + grabbableKey: { + grabbable: false + } + }) + }); + }; + + this.lineOn = function(closePoint, farPoint, color) { + // draw a line + if (this.pointer === null) { + this.pointer = Entities.addEntity({ + type: "Line", + name: "grab pointer", + dimensions: LINE_ENTITY_DIMENSIONS, + visible: true, + position: closePoint, + linePoints: [ZERO_VEC, farPoint], + color: color, + lifetime: LIFETIME, + collisionsWillMove: false, + ignoreForCollisions: true, + userData: JSON.stringify({ + grabbableKey: { + grabbable: false + } + }) + }); + } else { + var age = Entities.getEntityProperties(this.pointer, "age").age; + this.pointer = Entities.editEntity(this.pointer, { + position: closePoint, + linePoints: [ZERO_VEC, farPoint], + color: color, + lifetime: age + LIFETIME + }); + } + }; + + this.overlayLineOn = function(closePoint, farPoint, color) { + if (this.overlayLine === null) { + var lineProperties = { + lineWidth: 5, + start: closePoint, + end: farPoint, + color: color, + ignoreRayIntersection: true, // always ignore this + visible: true, + alpha: 1 + }; + + this.overlayLine = Overlays.addOverlay("line3d", lineProperties); + + } else { + var success = Overlays.editOverlay(this.overlayLine, { + lineWidth: 5, + start: closePoint, + end: farPoint, + color: color, + visible: true, + ignoreRayIntersection: true, // always ignore this + alpha: 1 + }); + } + }; + + this.handleParticleBeam = function(position, orientation, color) { + + var rotation = Quat.angleAxis(0, { + x: 1, + y: 0, + z: 0 + }); + + var finalRotation = Quat.multiply(orientation, rotation); + + if (this.particleBeam === null) { + this.createParticleBeam(position, finalRotation, color); + } else { + this.updateParticleBeam(position, finalRotation, color); + } + }; + + this.handleDistantParticleBeam = function(handPosition, objectPosition, objectRotation, color) { + + var handToObject = Vec3.subtract(objectPosition, handPosition); + var finalRotation = Quat.rotationBetween(Vec3.multiply(-1, Vec3.UP), handToObject); + + var distance = Vec3.distance(handPosition, objectPosition); + var speed = distance * 1; + + var lifepsan = distance / speed; + var lifespan = 1; + + if (this.particleBeam === null) { + this.createParticleBeam(objectPosition, finalRotation, color, speed); + } else { + this.updateParticleBeam(objectPosition, finalRotation, color, speed, lifepsan); + } + }; + + this.createParticleBeam = function(position, orientation, color, speed, lifepsan) { + + var particleBeamProperties = { + type: "ParticleEffect", + isEmitting: true, + position: position, + visible: false, + //rotation:Quat.fromPitchYawRollDegrees(-90.0, 0.0, 0.0), + "name": "Particle Beam", + "color": color, + "maxParticles": 2000, + "lifespan": LINE_LENGTH / 10, + "emitRate": 50, + "emitSpeed": 5, + "speedSpread": 2, + "emitOrientation": { + "x": -1, + "y": 0, + "z": 0, + "w": 1 + }, + "emitDimensions": { + "x": 0, + "y": 0, + "z": 0 + }, + "emitRadiusStart": 0.5, + "polarStart": 0, + "polarFinish": 0, + "azimuthStart": -3.1415927410125732, + "azimuthFinish": 3.1415927410125732, + "emitAcceleration": { + x: 0, + y: 0, + z: 0 + }, + "accelerationSpread": { + "x": 0, + "y": 0, + "z": 0 + }, + "particleRadius": 0.01, + "radiusSpread": 0, + // "radiusStart": 0.01, + // "radiusFinish": 0.01, + // "colorSpread": { + // "red": 0, + // "green": 0, + // "blue": 0 + // }, + // "colorStart": color, + // "colorFinish": color, + "alpha": 1, + "alphaSpread": 0, + "alphaStart": 1, + "alphaFinish": 1, + "additiveBlending": 1, + "textures": "https://hifi-content.s3.amazonaws.com/alan/dev/textures/grabsprite-3.png" + } + + this.particleBeam = Entities.addEntity(particleBeamProperties); + }; + + this.updateParticleBeam = function(position, orientation, color, speed, lifepsan) { + + Entities.editEntity(this.particleBeam, { + rotation: orientation, + position: position, + visible: true, + color: color, + emitSpeed: speed, + lifepsan: lifepsan + + }) + + }; + + this.evalLightWorldTransform = function(modelPos, modelRot) { + + var MODEL_LIGHT_POSITION = { + x: 0, + y: -0.3, + z: 0 + }; + + var MODEL_LIGHT_ROTATION = Quat.angleAxis(-90, { + x: 1, + y: 0, + z: 0 + }); + + return { + p: Vec3.sum(modelPos, Vec3.multiplyQbyV(modelRot, MODEL_LIGHT_POSITION)), + q: Quat.multiply(modelRot, MODEL_LIGHT_ROTATION) + }; + }; + + this.handleSpotlight = function(parentID, position) { + var LIFETIME = 100; + + var modelProperties = Entities.getEntityProperties(parentID, ['position', 'rotation']); + + var lightTransform = this.evalLightWorldTransform(modelProperties.position, modelProperties.rotation); + var lightProperties = { + type: "Light", + isSpotlight: true, + dimensions: { + x: 2, + y: 2, + z: 20 + }, + parentID: parentID, + color: { + red: 255, + green: 255, + blue: 255 + }, + intensity: 2, + exponent: 0.3, + cutoff: 20, + lifetime: LIFETIME, + position: lightTransform.p, + }; + + if (this.spotlight === null) { + this.spotlight = Entities.addEntity(lightProperties); + } else { + Entities.editEntity(this.spotlight, { + //without this, this light would maintain rotation with its parent + rotation: Quat.fromPitchYawRollDegrees(-90, 0, 0), + }) + } + }; + + this.handlePointLight = function(parentID, position) { + var LIFETIME = 100; + + var modelProperties = Entities.getEntityProperties(parentID, ['position', 'rotation']); + var lightTransform = this.evalLightWorldTransform(modelProperties.position, modelProperties.rotation); + + var lightProperties = { + type: "Light", + isSpotlight: false, + dimensions: { + x: 2, + y: 2, + z: 20 + }, + parentID: parentID, + color: { + red: 255, + green: 255, + blue: 255 + }, + intensity: 2, + exponent: 0.3, + cutoff: 20, + lifetime: LIFETIME, + position: lightTransform.p, + }; + + if (this.pointlight === null) { + this.pointlight = Entities.addEntity(lightProperties); + } else { + + } + }; + + this.lineOff = function() { + if (this.pointer !== null) { + Entities.deleteEntity(this.pointer); + } + this.pointer = null; + }; + + this.overlayLineOff = function() { + if (this.overlayLine !== null) { + Overlays.deleteOverlay(this.overlayLine); + } + this.overlayLine = null; + }; + + this.particleBeamOff = function() { + if (this.particleBeam !== null) { + Entities.editEntity(this.particleBeam, { + visible: false + }) + } + } + + this.turnLightsOff = function() { + if (this.spotlight !== null) { + Entities.deleteEntity(this.spotlight); + this.spotlight = null; + } + + if (this.pointlight !== null) { + Entities.deleteEntity(this.pointlight); + this.pointlight = null; + } + }; + + + this.turnOffVisualizations = function() { + if (USE_ENTITY_LINES_FOR_SEARCHING === true || USE_ENTITY_LINES_FOR_MOVING === true) { + this.lineOff(); + } + + if (USE_OVERLAY_LINES_FOR_SEARCHING === true || USE_OVERLAY_LINES_FOR_MOVING === true) { + this.overlayLineOff(); + } + + if (USE_PARTICLE_BEAM_FOR_SEARCHING === true || USE_PARTICLE_BEAM_FOR_MOVING === true) { + this.particleBeamOff(); + } + }; + + this.triggerPress = function(value) { + _this.rawTriggerValue = value; + }; + + this.bumperPress = function(value) { + _this.rawBumperValue = value; + }; + + this.updateSmoothedTrigger = function() { + var triggerValue = this.rawTriggerValue; + // smooth out trigger value + this.triggerValue = (this.triggerValue * TRIGGER_SMOOTH_RATIO) + + (triggerValue * (1.0 - TRIGGER_SMOOTH_RATIO)); + }; + + this.triggerSmoothedSqueezed = function() { + return this.triggerValue > TRIGGER_ON_VALUE; + }; + + this.triggerSmoothedReleased = function() { + return this.triggerValue < TRIGGER_OFF_VALUE; + }; + + this.triggerSqueezed = function() { + var triggerValue = this.rawTriggerValue; + return triggerValue > TRIGGER_ON_VALUE; + }; + + this.bumperSqueezed = function() { + return _this.rawBumperValue > BUMPER_ON_VALUE; + }; + + this.bumperReleased = function() { + return _this.rawBumperValue < BUMPER_ON_VALUE; + }; + + this.off = function() { + if (this.triggerSmoothedSqueezed()) { + this.lastPickTime = 0; + this.setState(STATE_SEARCHING); + return; + } + if (this.bumperSqueezed()) { + this.lastPickTime = 0; + this.setState(STATE_EQUIP_SEARCHING); + return; + } + }; + + this.search = function() { + this.grabbedEntity = null; + + if (this.state == STATE_SEARCHING ? this.triggerSmoothedReleased() : this.bumperReleased()) { + this.setState(STATE_RELEASE); + return; + } + + // the trigger is being pressed, do a ray test + var handPosition = this.getHandPosition(); + var distantPickRay = { + origin: handPosition, + direction: Quat.getUp(this.getHandRotation()), + length: PICK_MAX_DISTANCE + }; + + // don't pick 60x per second. + var pickRays = []; + var now = Date.now(); + if (now - this.lastPickTime > MSECS_PER_SEC / PICKS_PER_SECOND_PER_HAND) { + pickRays = [distantPickRay]; + this.lastPickTime = now; + } + + for (var index = 0; index < pickRays.length; ++index) { + var pickRay = pickRays[index]; + var directionNormalized = Vec3.normalize(pickRay.direction); + var directionBacked = Vec3.multiply(directionNormalized, PICK_BACKOFF_DISTANCE); + var pickRayBacked = { + origin: Vec3.subtract(pickRay.origin, directionBacked), + direction: pickRay.direction + }; + + if (WANT_DEBUG) { + this.debugLine(pickRayBacked.origin, Vec3.multiply(pickRayBacked.direction, NEAR_PICK_MAX_DISTANCE), { + red: 0, + green: 255, + blue: 0 + }) + } + + var intersection = Entities.findRayIntersection(pickRayBacked, true); + + if (intersection.intersects) { + // the ray is intersecting something we can move. + var intersectionDistance = Vec3.distance(pickRay.origin, intersection.intersection); + + var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, intersection.entityID, DEFAULT_GRABBABLE_DATA); + + if (intersection.properties.name == "Grab Debug Entity") { + continue; + } + + if (typeof grabbableData.grabbable !== 'undefined' && !grabbableData.grabbable) { + continue; + } + if (intersectionDistance > pickRay.length) { + // too far away for this ray. + continue; + } + if (intersectionDistance <= NEAR_PICK_MAX_DISTANCE) { + // the hand is very close to the intersected object. go into close-grabbing mode. + if (grabbableData.wantsTrigger) { + this.grabbedEntity = intersection.entityID; + this.setState(STATE_NEAR_TRIGGER); + return; + } else if (!intersection.properties.locked) { + this.grabbedEntity = intersection.entityID; + if (this.state == STATE_SEARCHING) { + this.setState(STATE_NEAR_GRABBING); + } else { // equipping + if (typeof grabbableData.spatialKey !== 'undefined') { + // TODO + // if we go to STATE_EQUIP_SPRING the item will be pulled to the hand and will then switch + // to STATE_EQUIP. This needs some debugging, so just jump straight to STATE_EQUIP here. + // this.setState(STATE_EQUIP_SPRING); + this.setState(STATE_EQUIP); + } else { + this.setState(STATE_EQUIP); + } + } + return; + } + } else if (!entityIsGrabbedByOther(intersection.entityID)) { + // don't allow two people to distance grab the same object + if (intersection.properties.collisionsWillMove && !intersection.properties.locked) { + // the hand is far from the intersected object. go into distance-holding mode + this.grabbedEntity = intersection.entityID; + if (typeof grabbableData.spatialKey !== 'undefined' && this.state == STATE_EQUIP_SEARCHING) { + // if a distance pick in equip mode hits something with a spatialKey, equip it + // TODO use STATE_EQUIP_SPRING here once it works right. + // this.setState(STATE_EQUIP_SPRING); + this.setState(STATE_EQUIP); + return; + } else if (this.state == STATE_SEARCHING) { + this.setState(STATE_DISTANCE_HOLDING); + return; + } + } else if (grabbableData.wantsTrigger) { + this.grabbedEntity = intersection.entityID; + this.setState(STATE_FAR_TRIGGER); + return; + } + } + } + } + + // forward ray test failed, try sphere test. + if (WANT_DEBUG) { + Entities.addEntity({ + type: "Sphere", + name: "Grab Debug Entity", + dimensions: { + x: GRAB_RADIUS, + y: GRAB_RADIUS, + z: GRAB_RADIUS + }, + visible: true, + position: handPosition, + color: { + red: 0, + green: 255, + blue: 0 + }, + lifetime: 0.1, + collisionsWillMove: false, + ignoreForCollisions: true, + userData: JSON.stringify({ + grabbableKey: { + grabbable: false + } + }) + }); + } + + var nearbyEntities = Entities.findEntities(handPosition, GRAB_RADIUS); + var minDistance = PICK_MAX_DISTANCE; + var i, props, distance, grabbableData; + this.grabbedEntity = null; + for (i = 0; i < nearbyEntities.length; i++) { + var grabbableDataForCandidate = + getEntityCustomData(GRABBABLE_DATA_KEY, nearbyEntities[i], DEFAULT_GRABBABLE_DATA); + if (typeof grabbableDataForCandidate.grabbable !== 'undefined' && !grabbableDataForCandidate.grabbable) { + continue; + } + var propsForCandidate = Entities.getEntityProperties(nearbyEntities[i], GRABBABLE_PROPERTIES); + + if (propsForCandidate.type == 'Unknown') { + continue; + } + + if (propsForCandidate.type == 'Light') { + continue; + } + + if (propsForCandidate.type == 'ParticleEffect') { + continue; + } + + if (propsForCandidate.type == 'PolyLine') { + continue; + } + + if (propsForCandidate.type == 'Zone') { + continue; + } + + if (propsForCandidate.locked && !grabbableDataForCandidate.wantsTrigger) { + continue; + } + + if (propsForCandidate.name == "Grab Debug Entity") { + continue; + } + + if (propsForCandidate.name == "grab pointer") { + continue; + } + + distance = Vec3.distance(propsForCandidate.position, handPosition); + if (distance < minDistance) { + this.grabbedEntity = nearbyEntities[i]; + minDistance = distance; + props = propsForCandidate; + grabbableData = grabbableDataForCandidate; + } + } + if (this.grabbedEntity !== null) { + if (grabbableData.wantsTrigger) { + this.setState(STATE_NEAR_TRIGGER); + return; + } else if (!props.locked && props.collisionsWillMove) { + this.setState(this.state == STATE_SEARCHING ? STATE_NEAR_GRABBING : STATE_EQUIP) + return; + } + } + + //search line visualizations + if (USE_ENTITY_LINES_FOR_SEARCHING === true) { + this.lineOn(distantPickRay.origin, Vec3.multiply(distantPickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); + } + + if (USE_OVERLAY_LINES_FOR_SEARCHING === true) { + this.overlayLineOn(distantPickRay.origin, Vec3.sum(distantPickRay.origin, Vec3.multiply(distantPickRay.direction, LINE_LENGTH)), NO_INTERSECT_COLOR); + } + + if (USE_PARTICLE_BEAM_FOR_SEARCHING === true) { + this.handleParticleBeam(distantPickRay.origin, this.getHandRotation(), NO_INTERSECT_COLOR); + } + + }; + + this.distanceHolding = function() { + var handControllerPosition = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandPosition : MyAvatar.leftHandPosition; + var controllerHandInput = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; + var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getPoseValue(controllerHandInput).rotation); + var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); + var now = Date.now(); + + // add the action and initialize some variables + this.currentObjectPosition = grabbedProperties.position; + this.currentObjectRotation = grabbedProperties.rotation; + this.currentObjectTime = now; + this.handRelativePreviousPosition = Vec3.subtract(handControllerPosition, MyAvatar.position); + this.handPreviousRotation = handRotation; + this.currentCameraOrientation = Camera.orientation; + + // compute a constant based on the initial conditions which we use below to exagerate hand motion onto the held object + this.radiusScalar = Math.log(Vec3.distance(this.currentObjectPosition, handControllerPosition) + 1.0); + if (this.radiusScalar < 1.0) { + this.radiusScalar = 1.0; + } + + this.actionID = NULL_ACTION_ID; + this.actionID = Entities.addAction("spring", this.grabbedEntity, { + targetPosition: this.currentObjectPosition, + linearTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME, + targetRotation: this.currentObjectRotation, + angularTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME, + tag: getTag(), + ttl: ACTION_TTL + }); + if (this.actionID === NULL_ACTION_ID) { + this.actionID = null; + } + this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC); + + if (this.actionID !== null) { + this.setState(STATE_CONTINUE_DISTANCE_HOLDING); + this.activateEntity(this.grabbedEntity, grabbedProperties); + if (this.hand === RIGHT_HAND) { + Entities.callEntityMethod(this.grabbedEntity, "setRightHand"); + } else { + Entities.callEntityMethod(this.grabbedEntity, "setLeftHand"); + } + Entities.callEntityMethod(this.grabbedEntity, "setHand", [this.hand]); + Entities.callEntityMethod(this.grabbedEntity, "startDistantGrab"); + } + + this.currentAvatarPosition = MyAvatar.position; + this.currentAvatarOrientation = MyAvatar.orientation; + + this.turnOffVisualizations(); + }; + + this.continueDistanceHolding = function() { + if (this.triggerSmoothedReleased()) { + this.setState(STATE_RELEASE); + Entities.callEntityMethod(this.grabbedEntity, "releaseGrab"); + return; + } + + var handPosition = this.getHandPosition(); + var handControllerPosition = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandPosition : MyAvatar.leftHandPosition; + var controllerHandInput = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; + var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getPoseValue(controllerHandInput).rotation); + var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); + var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); + + if (this.state == STATE_CONTINUE_DISTANCE_HOLDING && this.bumperSqueezed() && + typeof grabbableData.spatialKey !== 'undefined') { + var saveGrabbedID = this.grabbedEntity; + this.release(); + this.setState(STATE_EQUIP); + this.grabbedEntity = saveGrabbedID; + return; + } + + + // the action was set up on a previous call. update the targets. + var radius = Vec3.distance(this.currentObjectPosition, handControllerPosition) * + this.radiusScalar * DISTANCE_HOLDING_RADIUS_FACTOR; + if (radius < 1.0) { + radius = 1.0; + } + + // how far did avatar move this timestep? + var currentPosition = MyAvatar.position; + var avatarDeltaPosition = Vec3.subtract(currentPosition, this.currentAvatarPosition); + this.currentAvatarPosition = currentPosition; + + // How far did the avatar turn this timestep? + // Note: The following code is too long because we need a Quat.quatBetween() function + // that returns the minimum quaternion between two quaternions. + var currentOrientation = MyAvatar.orientation; + if (Quat.dot(currentOrientation, this.currentAvatarOrientation) < 0.0) { + var negativeCurrentOrientation = { + x: -currentOrientation.x, + y: -currentOrientation.y, + z: -currentOrientation.z, + w: -currentOrientation.w + }; + var avatarDeltaOrientation = Quat.multiply(negativeCurrentOrientation, Quat.inverse(this.currentAvatarOrientation)); + } else { + var avatarDeltaOrientation = Quat.multiply(currentOrientation, Quat.inverse(this.currentAvatarOrientation)); + } + var handToAvatar = Vec3.subtract(handControllerPosition, this.currentAvatarPosition); + var objectToAvatar = Vec3.subtract(this.currentObjectPosition, this.currentAvatarPosition); + var handMovementFromTurning = Vec3.subtract(Quat.multiply(avatarDeltaOrientation, handToAvatar), handToAvatar); + var objectMovementFromTurning = Vec3.subtract(Quat.multiply(avatarDeltaOrientation, objectToAvatar), objectToAvatar); + this.currentAvatarOrientation = currentOrientation; + + // how far did hand move this timestep? + var handMoved = Vec3.subtract(handToAvatar, this.handRelativePreviousPosition); + this.handRelativePreviousPosition = handToAvatar; + + // magnify the hand movement but not the change from avatar movement & rotation + handMoved = Vec3.subtract(handMoved, handMovementFromTurning); + var superHandMoved = Vec3.multiply(handMoved, radius); + + // Move the object by the magnified amount and then by amount from avatar movement & rotation + var newObjectPosition = Vec3.sum(this.currentObjectPosition, superHandMoved); + newObjectPosition = Vec3.sum(newObjectPosition, avatarDeltaPosition); + newObjectPosition = Vec3.sum(newObjectPosition, objectMovementFromTurning); + + var deltaPosition = Vec3.subtract(newObjectPosition, this.currentObjectPosition); // meters + var now = Date.now(); + var deltaTime = (now - this.currentObjectTime) / MSEC_PER_SEC; // convert to seconds + + this.currentObjectPosition = newObjectPosition; + this.currentObjectTime = now; + + // this doubles hand rotation + var handChange = Quat.multiply(Quat.slerp(this.handPreviousRotation, + handRotation, + DISTANCE_HOLDING_ROTATION_EXAGGERATION_FACTOR), + Quat.inverse(this.handPreviousRotation)); + this.handPreviousRotation = handRotation; + this.currentObjectRotation = Quat.multiply(handChange, this.currentObjectRotation); + + Entities.callEntityMethod(this.grabbedEntity, "continueDistantGrab"); + + // mix in head motion + if (MOVE_WITH_HEAD) { + var objDistance = Vec3.length(objectToAvatar); + var before = Vec3.multiplyQbyV(this.currentCameraOrientation, { + x: 0.0, + y: 0.0, + z: objDistance + }); + var after = Vec3.multiplyQbyV(Camera.orientation, { + x: 0.0, + y: 0.0, + z: objDistance + }); + var change = Vec3.subtract(before, after); + this.currentCameraOrientation = Camera.orientation; + this.currentObjectPosition = Vec3.sum(this.currentObjectPosition, change); + } + + + //visualizations + if (USE_ENTITY_LINES_FOR_MOVING === true) { + this.lineOn(handPosition, Vec3.subtract(grabbedProperties.position, handPosition), INTERSECT_COLOR); + } + if (USE_OVERLAY_LINES_FOR_MOVING === true) { + this.overlayLineOn(handPosition, grabbedProperties.position, INTERSECT_COLOR); + } + if (USE_PARTICLE_BEAM_FOR_MOVING === true) { + this.handleDistantParticleBeam(handPosition, grabbedProperties.position, this.currentObjectRotation, INTERSECT_COLOR) + } + if (USE_POINTLIGHT === true) { + this.handlePointLight(this.grabbedEntity); + } + if (USE_SPOTLIGHT === true) { + this.handleSpotlight(this.grabbedEntity); + } + + Entities.updateAction(this.grabbedEntity, this.actionID, { + targetPosition: this.currentObjectPosition, + linearTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME, + targetRotation: this.currentObjectRotation, + angularTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME, + ttl: ACTION_TTL + }); + + this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC); + }; + + this.nearGrabbing = function() { + var now = Date.now(); + var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); + + if (this.state == STATE_NEAR_GRABBING && this.triggerSmoothedReleased()) { + this.setState(STATE_RELEASE); + Entities.callEntityMethod(this.grabbedEntity, "releaseGrab"); + return; + } + + this.turnOffVisualizations(); + + var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); + this.activateEntity(this.grabbedEntity, grabbedProperties); + if (grabbedProperties.collisionsWillMove && NEAR_GRABBING_KINEMATIC) { + Entities.editEntity(this.grabbedEntity, { + collisionsWillMove: false + }); + } + + var handRotation = this.getHandRotation(); + var handPosition = this.getHandPosition(); + + var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); + + if (this.state != STATE_NEAR_GRABBING && grabbableData.spatialKey) { + // if an object is "equipped" and has a spatialKey, use it. + this.ignoreIK = grabbableData.spatialKey.ignoreIK ? grabbableData.spatialKey.ignoreIK : false; + this.offsetPosition = getSpatialOffsetPosition(this.hand, grabbableData.spatialKey); + this.offsetRotation = getSpatialOffsetRotation(this.hand, grabbableData.spatialKey); + } else { + this.ignoreIK = false; + + var objectRotation = grabbedProperties.rotation; + this.offsetRotation = Quat.multiply(Quat.inverse(handRotation), objectRotation); + + var currentObjectPosition = grabbedProperties.position; + var offset = Vec3.subtract(currentObjectPosition, handPosition); + this.offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, this.offsetRotation)), offset); + } + + this.actionID = NULL_ACTION_ID; + this.actionID = Entities.addAction("hold", this.grabbedEntity, { + hand: this.hand === RIGHT_HAND ? "right" : "left", + timeScale: NEAR_GRABBING_ACTION_TIMEFRAME, + relativePosition: this.offsetPosition, + relativeRotation: this.offsetRotation, + ttl: ACTION_TTL, + kinematic: NEAR_GRABBING_KINEMATIC, + kinematicSetVelocity: true, + ignoreIK: this.ignoreIK + }); + if (this.actionID === NULL_ACTION_ID) { + this.actionID = null; + } else { + this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC); + if (this.state == STATE_NEAR_GRABBING) { + this.setState(STATE_CONTINUE_NEAR_GRABBING); + } else { + // equipping + Entities.callEntityMethod(this.grabbedEntity, "startEquip", [JSON.stringify(this.hand)]); + this.startHandGrasp(); + + this.setState(STATE_CONTINUE_EQUIP_BD); + } + + if (this.hand === RIGHT_HAND) { + Entities.callEntityMethod(this.grabbedEntity, "setRightHand"); + } else { + Entities.callEntityMethod(this.grabbedEntity, "setLeftHand"); + } + + Entities.callEntityMethod(this.grabbedEntity, "setHand", [this.hand]); + + Entities.callEntityMethod(this.grabbedEntity, "startNearGrab"); + + } + + this.currentHandControllerTipPosition = + (this.hand === RIGHT_HAND) ? MyAvatar.rightHandTipPosition : MyAvatar.leftHandTipPosition; + + this.currentObjectTime = Date.now(); + }; + + this.continueNearGrabbing = function() { + if (this.state == STATE_CONTINUE_NEAR_GRABBING && this.triggerSmoothedReleased()) { + this.setState(STATE_RELEASE); + Entities.callEntityMethod(this.grabbedEntity, "releaseGrab"); + return; + } + if (this.state == STATE_CONTINUE_EQUIP_BD && this.bumperReleased()) { + this.setState(STATE_CONTINUE_EQUIP); + return; + } + if (this.state == STATE_CONTINUE_EQUIP && this.bumperSqueezed()) { + this.setState(STATE_WAITING_FOR_BUMPER_RELEASE); + return; + } + if (this.state == STATE_CONTINUE_NEAR_GRABBING && this.bumperSqueezed()) { + this.setState(STATE_CONTINUE_EQUIP_BD); + Entities.callEntityMethod(this.grabbedEntity, "startEquip", [JSON.stringify(this.hand)]); + return; + } + + // Keep track of the fingertip velocity to impart when we release the object. + // Note that the idea of using a constant 'tip' velocity regardless of the + // object's actual held offset is an idea intended to make it easier to throw things: + // Because we might catch something or transfer it between hands without a good idea + // of it's actual offset, let's try imparting a velocity which is at a fixed radius + // from the palm. + + var handControllerPosition = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandPosition : MyAvatar.leftHandPosition; + var now = Date.now(); + + var deltaPosition = Vec3.subtract(handControllerPosition, this.currentHandControllerTipPosition); // meters + var deltaTime = (now - this.currentObjectTime) / MSEC_PER_SEC; // convert to seconds + + this.currentHandControllerTipPosition = handControllerPosition; + this.currentObjectTime = now; + Entities.callEntityMethod(this.grabbedEntity, "continueNearGrab"); + + if (this.state === STATE_CONTINUE_EQUIP_BD) { + Entities.callEntityMethod(this.grabbedEntity, "continueEquip"); + } + + if (this.actionTimeout - now < ACTION_TTL_REFRESH * MSEC_PER_SEC) { + // if less than a 5 seconds left, refresh the actions ttl + Entities.updateAction(this.grabbedEntity, this.actionID, { + hand: this.hand === RIGHT_HAND ? "right" : "left", + timeScale: NEAR_GRABBING_ACTION_TIMEFRAME, + relativePosition: this.offsetPosition, + relativeRotation: this.offsetRotation, + ttl: ACTION_TTL, + kinematic: NEAR_GRABBING_KINEMATIC, + kinematicSetVelocity: true, + ignoreIK: this.ignoreIK + }); + this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC); + } + }; + + this.waitingForBumperRelease = function() { + if (this.bumperReleased()) { + this.setState(STATE_RELEASE); + Entities.callEntityMethod(this.grabbedEntity, "releaseGrab"); + Entities.callEntityMethod(this.grabbedEntity, "unequip"); + this.endHandGrasp(); + + } + }; + + this.pullTowardEquipPosition = function() { + + this.turnOffVisualizations(); + + var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); + var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); + + // use a spring to pull the object to where it will be when equipped + var relativeRotation = getSpatialOffsetRotation(this.hand, grabbableData.spatialKey); + var relativePosition = getSpatialOffsetPosition(this.hand, grabbableData.spatialKey); + var ignoreIK = grabbableData.spatialKey.ignoreIK ? grabbableData.spatialKey.ignoreIK : false; + var handRotation = this.getHandRotation(); + var handPosition = this.getHandPosition(); + var targetRotation = Quat.multiply(handRotation, relativeRotation); + var offset = Vec3.multiplyQbyV(targetRotation, relativePosition); + var targetPosition = Vec3.sum(handPosition, offset); + + if (typeof this.equipSpringID === 'undefined' || + this.equipSpringID === null || + this.equipSpringID === NULL_ACTION_ID) { + this.equipSpringID = Entities.addAction("spring", this.grabbedEntity, { + targetPosition: targetPosition, + linearTimeScale: EQUIP_SPRING_TIMEFRAME, + targetRotation: targetRotation, + angularTimeScale: EQUIP_SPRING_TIMEFRAME, + ttl: ACTION_TTL, + ignoreIK: ignoreIK + }); + if (this.equipSpringID === NULL_ACTION_ID) { + this.equipSpringID = null; + this.setState(STATE_OFF); + return; + } + } else { + Entities.updateAction(this.grabbedEntity, this.equipSpringID, { + targetPosition: targetPosition, + linearTimeScale: EQUIP_SPRING_TIMEFRAME, + targetRotation: targetRotation, + angularTimeScale: EQUIP_SPRING_TIMEFRAME, + ttl: ACTION_TTL, + ignoreIK: ignoreIK + }); + } + + if (Vec3.distance(grabbedProperties.position, targetPosition) < EQUIP_SPRING_SHUTOFF_DISTANCE) { + Entities.deleteAction(this.grabbedEntity, this.equipSpringID); + this.equipSpringID = null; + this.setState(STATE_EQUIP); + } + }; + + this.nearTrigger = function() { + if (this.triggerSmoothedReleased()) { + this.setState(STATE_RELEASE); + Entities.callEntityMethod(this.grabbedEntity, "stopNearTrigger"); + return; + } + if (this.hand === RIGHT_HAND) { + Entities.callEntityMethod(this.grabbedEntity, "setRightHand"); + } else { + Entities.callEntityMethod(this.grabbedEntity, "setLeftHand"); + } + + Entities.callEntityMethod(this.grabbedEntity, "setHand", [this.hand]); + + Entities.callEntityMethod(this.grabbedEntity, "startNearTrigger"); + this.setState(STATE_CONTINUE_NEAR_TRIGGER); + }; + + this.farTrigger = function() { + if (this.triggerSmoothedReleased()) { + this.setState(STATE_RELEASE); + Entities.callEntityMethod(this.grabbedEntity, "stopFarTrigger"); + return; + } + + if (this.hand === RIGHT_HAND) { + Entities.callEntityMethod(this.grabbedEntity, "setRightHand"); + } else { + Entities.callEntityMethod(this.grabbedEntity, "setLeftHand"); + } + Entities.callEntityMethod(this.grabbedEntity, "setHand", [this.hand]); + Entities.callEntityMethod(this.grabbedEntity, "startFarTrigger"); + this.setState(STATE_CONTINUE_FAR_TRIGGER); + }; + + this.continueNearTrigger = function() { + if (this.triggerSmoothedReleased()) { + this.setState(STATE_RELEASE); + Entities.callEntityMethod(this.grabbedEntity, "stopNearTrigger"); + return; + } + + Entities.callEntityMethod(this.grabbedEntity, "continueNearTrigger"); + }; + + this.continueFarTrigger = function() { + if (this.triggerSmoothedReleased()) { + this.setState(STATE_RELEASE); + Entities.callEntityMethod(this.grabbedEntity, "stopNearTrigger"); + return; + } + + var handPosition = this.getHandPosition(); + var pickRay = { + origin: handPosition, + direction: Quat.getUp(this.getHandRotation()) + }; + + var now = Date.now(); + if (now - this.lastPickTime > MSECS_PER_SEC / PICKS_PER_SECOND_PER_HAND) { + var intersection = Entities.findRayIntersection(pickRay, true); + this.lastPickTime = now; + if (intersection.entityID != this.grabbedEntity) { + this.setState(STATE_RELEASE); + Entities.callEntityMethod(this.grabbedEntity, "stopFarTrigger"); + return; + } + } + + this.lineOn(pickRay.origin, Vec3.multiply(pickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); + Entities.callEntityMethod(this.grabbedEntity, "continueFarTrigger"); + }; + + _this.allTouchedIDs = {}; + + this.touchTest = function() { + var maxDistance = 0.05; + var leftHandPosition = MyAvatar.getLeftPalmPosition(); + var rightHandPosition = MyAvatar.getRightPalmPosition(); + var leftEntities = Entities.findEntities(leftHandPosition, maxDistance); + var rightEntities = Entities.findEntities(rightHandPosition, maxDistance); + var ids = []; + + if (leftEntities.length !== 0) { + leftEntities.forEach(function(entity) { + ids.push(entity); + }); + + } + + if (rightEntities.length !== 0) { + rightEntities.forEach(function(entity) { + ids.push(entity); + }); + } + + ids.forEach(function(id) { + + var props = Entities.getEntityProperties(id, ["boundingBox", "name"]); + if (props.name === 'pointer') { + return; + } else { + var entityMinPoint = props.boundingBox.brn; + var entityMaxPoint = props.boundingBox.tfl; + var leftIsTouching = pointInExtents(leftHandPosition, entityMinPoint, entityMaxPoint); + var rightIsTouching = pointInExtents(rightHandPosition, entityMinPoint, entityMaxPoint); + + if ((leftIsTouching || rightIsTouching) && _this.allTouchedIDs[id] === undefined) { + // we haven't been touched before, but either right or left is touching us now + _this.allTouchedIDs[id] = true; + _this.startTouch(id); + } else if ((leftIsTouching || rightIsTouching) && _this.allTouchedIDs[id]) { + // we have been touched before and are still being touched + // continue touch + _this.continueTouch(id); + } else if (_this.allTouchedIDs[id]) { + delete _this.allTouchedIDs[id]; + _this.stopTouch(id); + + } else { + //we are in another state + return; + } + } + + }); + + }; + + this.startTouch = function(entityID) { + Entities.callEntityMethod(entityID, "startTouch"); + }; + + this.continueTouch = function(entityID) { + Entities.callEntityMethod(entityID, "continueTouch"); + }; + + this.stopTouch = function(entityID) { + Entities.callEntityMethod(entityID, "stopTouch"); + }; + + this.release = function() { + + this.turnLightsOff(); + this.turnOffVisualizations(); + + if (this.grabbedEntity !== null) { + if (this.actionID !== null) { + Entities.deleteAction(this.grabbedEntity, this.actionID); + } + } + + this.deactivateEntity(this.grabbedEntity); + + this.grabbedEntity = null; + this.actionID = null; + this.setState(STATE_OFF); + }; + + this.cleanup = function() { + this.release(); + this.endHandGrasp(); + Entities.deleteEntity(this.particleBeam); + Entities.deleteEntity(this.spotLight); + Entities.deleteEntity(this.pointLight); + }; + + this.activateEntity = function(entityID, grabbedProperties) { + var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, entityID, DEFAULT_GRABBABLE_DATA); + var invertSolidWhileHeld = grabbableData["invertSolidWhileHeld"]; + var data = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, {}); + data["activated"] = true; + data["avatarId"] = MyAvatar.sessionUUID; + data["refCount"] = data["refCount"] ? data["refCount"] + 1 : 1; + // zero gravity and set ignoreForCollisions in a way that lets us put them back, after all grabs are done + if (data["refCount"] == 1) { + data["gravity"] = grabbedProperties.gravity; + data["ignoreForCollisions"] = grabbedProperties.ignoreForCollisions; + data["collisionsWillMove"] = grabbedProperties.collisionsWillMove; + var whileHeldProperties = { + gravity: { + x: 0, + y: 0, + z: 0 + } + }; + if (invertSolidWhileHeld) { + whileHeldProperties["ignoreForCollisions"] = !grabbedProperties.ignoreForCollisions; + } + Entities.editEntity(entityID, whileHeldProperties); + } + + setEntityCustomData(GRAB_USER_DATA_KEY, entityID, data); + return data; + }; + + this.deactivateEntity = function(entityID) { + var data = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, {}); + if (data && data["refCount"]) { + data["refCount"] = data["refCount"] - 1; + if (data["refCount"] < 1) { + Entities.editEntity(entityID, { + gravity: data["gravity"], + ignoreForCollisions: data["ignoreForCollisions"], + collisionsWillMove: data["collisionsWillMove"] + }); + data = null; + } + } else { + data = null; + } + setEntityCustomData(GRAB_USER_DATA_KEY, entityID, data); + }; + + + //this is our handler, where we do the actual work of changing animation settings + this.graspHand = function(animationProperties) { + var result = {}; + //full alpha on overlay for this hand + //set grab to true + //set idle to false + //full alpha on the blend btw open and grab + if (_this.hand === RIGHT_HAND) { + result['rightHandOverlayAlpha'] = 1.0; + result['isRightHandGrab'] = true; + result['isRightHandIdle'] = false; + result['rightHandGrabBlend'] = 1.0; + } else if (_this.hand === LEFT_HAND) { + result['leftHandOverlayAlpha'] = 1.0; + result['isLeftHandGrab'] = true; + result['isLeftHandIdle'] = false; + result['leftHandGrabBlend'] = 1.0; + } + //return an object with our updated settings + return result; + }; + + this.graspHandler = null + + this.startHandGrasp = function() { + if (this.hand === RIGHT_HAND) { + this.graspHandler = MyAvatar.addAnimationStateHandler(this.graspHand, ['isRightHandGrab']); + } else if (this.hand === LEFT_HAND) { + this.graspHandler = MyAvatar.addAnimationStateHandler(this.graspHand, ['isLeftHandGrab']); + } + }; + + this.endHandGrasp = function() { + // Tell the animation system we don't need any more callbacks. + MyAvatar.removeAnimationStateHandler(this.graspHandler); + }; + +}; + +var rightController = new MyController(RIGHT_HAND); +var leftController = new MyController(LEFT_HAND); + +//preload the particle beams so that they are full length when you start searching +if (USE_PARTICLE_BEAM_FOR_SEARCHING === true || USE_PARTICLE_BEAM_FOR_MOVING === true) { + rightController.createParticleBeam(); + leftController.createParticleBeam(); +} + +var MAPPING_NAME = "com.highfidelity.handControllerGrab"; + +var mapping = Controller.newMapping(MAPPING_NAME); +mapping.from([Controller.Standard.RT]).peek().to(rightController.triggerPress); +mapping.from([Controller.Standard.LT]).peek().to(leftController.triggerPress); + +mapping.from([Controller.Standard.RB]).peek().to(rightController.bumperPress); +mapping.from([Controller.Standard.LB]).peek().to(leftController.bumperPress); + +Controller.enableMapping(MAPPING_NAME); + +//the section below allows the grab script to listen for messages that disable either one or both hands. useful for two handed items +var handToDisable = 'none'; + +function update() { + if (handToDisable !== LEFT_HAND && handToDisable !== 'both') { + leftController.update(); + } + if (handToDisable !== RIGHT_HAND && handToDisable !== 'both') { + rightController.update(); + } +} + +Messages.subscribe('Hifi-Hand-Disabler'); + +handleHandDisablerMessages = function(channel, message, sender) { + + if (sender === MyAvatar.sessionUUID) { + if (message === 'left') { + handToDisable = LEFT_HAND; + } + if (message === 'right') { + handToDisable = RIGHT_HAND; + } + if (message === 'both') { + handToDisable = 'both'; + } + if (message === 'none') { + handToDisable = 'none'; + } + } + +} + +Messages.messageReceived.connect(handleHandDisablerMessages); + +function cleanup() { + rightController.cleanup(); + leftController.cleanup(); + Controller.disableMapping(MAPPING_NAME); +} + +Script.scriptEnding.connect(cleanup); +Script.update.connect(update); \ No newline at end of file diff --git a/examples/controllers/handControllerGrab-particles.js b/examples/controllers/handControllerGrab-particles.js new file mode 100644 index 0000000000..d9ecb18b01 --- /dev/null +++ b/examples/controllers/handControllerGrab-particles.js @@ -0,0 +1,1649 @@ +// handControllerGrab.js +// +// Created by Eric Levin on 9/2/15 +// Additions by James B. Pollack @imgntn on 9/24/2015 +// Additions By Seth Alves on 10/20/2015 +// Copyright 2015 High Fidelity, Inc. +// +// Grabs physically moveable entities with hydra-like controllers; it works for either near or far objects. +// Also supports touch and equipping objects. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +/*global print, MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, Audio, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt, pointInExtents, vec3equal, setEntityCustomData, getEntityCustomData */ + +Script.include("../libraries/utils.js"); + +// +// add lines where the hand ray picking is happening +// +var WANT_DEBUG = false; + +// +// these tune time-averaging and "on" value for analog trigger +// + +var TRIGGER_SMOOTH_RATIO = 0.1; // 0.0 disables smoothing of trigger value +var TRIGGER_ON_VALUE = 0.4; +var TRIGGER_OFF_VALUE = 0.15; + +var BUMPER_ON_VALUE = 0.5; + +// +// distant manipulation +// + +var DISTANCE_HOLDING_RADIUS_FACTOR = 3.5; // multiplied by distance between hand and object +var DISTANCE_HOLDING_ACTION_TIMEFRAME = 0.1; // how quickly objects move to their new position +var DISTANCE_HOLDING_ROTATION_EXAGGERATION_FACTOR = 2.0; // object rotates this much more than hand did +var MOVE_WITH_HEAD = true; // experimental head-controll of distantly held objects + +var NO_INTERSECT_COLOR = { + red: 10, + green: 10, + blue: 255 +}; // line color when pick misses +var INTERSECT_COLOR = { + red: 250, + green: 10, + blue: 10 +}; // line color when pick hits +var LINE_ENTITY_DIMENSIONS = { + x: 1000, + y: 1000, + z: 1000 +}; + +var LINE_LENGTH = 500; +var PICK_MAX_DISTANCE = 500; // max length of pick-ray + +// +// near grabbing +// + +var GRAB_RADIUS = 0.03; // if the ray misses but an object is this close, it will still be selected +var NEAR_GRABBING_ACTION_TIMEFRAME = 0.05; // how quickly objects move to their new position +var NEAR_GRABBING_VELOCITY_SMOOTH_RATIO = 1.0; // adjust time-averaging of held object's velocity. 1.0 to disable. +var NEAR_PICK_MAX_DISTANCE = 0.3; // max length of pick-ray for close grabbing to be selected +var RELEASE_VELOCITY_MULTIPLIER = 1.5; // affects throwing things +var PICK_BACKOFF_DISTANCE = 0.2; // helps when hand is intersecting the grabble object +var NEAR_GRABBING_KINEMATIC = true; // force objects to be kinematic when near-grabbed + +// +// equip +// + +var EQUIP_SPRING_SHUTOFF_DISTANCE = 0.05; +var EQUIP_SPRING_TIMEFRAME = 0.4; // how quickly objects move to their new position + +// +// other constants +// + +var RIGHT_HAND = 1; +var LEFT_HAND = 0; + +var ZERO_VEC = { + x: 0, + y: 0, + z: 0 +}; + +var NULL_ACTION_ID = "{00000000-0000-0000-000000000000}"; +var MSEC_PER_SEC = 1000.0; + +// these control how long an abandoned pointer line or action will hang around +var LIFETIME = 10; +var ACTION_TTL = 15; // seconds +var ACTION_TTL_REFRESH = 5; +var PICKS_PER_SECOND_PER_HAND = 5; +var MSECS_PER_SEC = 1000.0; +var GRABBABLE_PROPERTIES = [ + "position", + "rotation", + "gravity", + "ignoreForCollisions", + "collisionsWillMove", + "locked", + "name" +]; + +var GRABBABLE_DATA_KEY = "grabbableKey"; // shared with grab.js +var GRAB_USER_DATA_KEY = "grabKey"; // shared with grab.js + +var DEFAULT_GRABBABLE_DATA = { + grabbable: true, + invertSolidWhileHeld: false +}; + +//we've created various ways of visualizing looking for and moving distant objects +var USE_ENTITY_LINES_FOR_SEARCHING = false; +var USE_OVERLAY_LINES_FOR_SEARCHING = false; +var USE_PARTICLE_BEAM_FOR_SEARCHING = true; + +var USE_ENTITY_LINES_FOR_MOVING = false; +var USE_OVERLAY_LINES_FOR_MOVING = false; +var USE_PARTICLE_BEAM_FOR_MOVING = true; + +var USE_SPOTLIGHT = false; +var USE_POINTLIGHT = false; + +// states for the state machine +var STATE_OFF = 0; +var STATE_SEARCHING = 1; +var STATE_DISTANCE_HOLDING = 2; +var STATE_CONTINUE_DISTANCE_HOLDING = 3; +var STATE_NEAR_GRABBING = 4; +var STATE_CONTINUE_NEAR_GRABBING = 5; +var STATE_NEAR_TRIGGER = 6; +var STATE_CONTINUE_NEAR_TRIGGER = 7; +var STATE_FAR_TRIGGER = 8; +var STATE_CONTINUE_FAR_TRIGGER = 9; +var STATE_RELEASE = 10; +var STATE_EQUIP_SEARCHING = 11; +var STATE_EQUIP = 12 +var STATE_CONTINUE_EQUIP_BD = 13; // equip while bumper is still held down +var STATE_CONTINUE_EQUIP = 14; +var STATE_WAITING_FOR_BUMPER_RELEASE = 15; +var STATE_EQUIP_SPRING = 16; + + + +function stateToName(state) { + switch (state) { + case STATE_OFF: + return "off"; + case STATE_SEARCHING: + return "searching"; + case STATE_DISTANCE_HOLDING: + return "distance_holding"; + case STATE_CONTINUE_DISTANCE_HOLDING: + return "continue_distance_holding"; + case STATE_NEAR_GRABBING: + return "near_grabbing"; + case STATE_CONTINUE_NEAR_GRABBING: + return "continue_near_grabbing"; + case STATE_NEAR_TRIGGER: + return "near_trigger"; + case STATE_CONTINUE_NEAR_TRIGGER: + return "continue_near_trigger"; + case STATE_FAR_TRIGGER: + return "far_trigger"; + case STATE_CONTINUE_FAR_TRIGGER: + return "continue_far_trigger"; + case STATE_RELEASE: + return "release"; + case STATE_EQUIP_SEARCHING: + return "equip_searching"; + case STATE_EQUIP: + return "equip"; + case STATE_CONTINUE_EQUIP_BD: + return "continue_equip_bd"; + case STATE_CONTINUE_EQUIP: + return "continue_equip"; + case STATE_WAITING_FOR_BUMPER_RELEASE: + return "waiting_for_bumper_release"; + case STATE_EQUIP_SPRING: + return "state_equip_spring"; + } + + return "unknown"; +} + +function getTag() { + return "grab-" + MyAvatar.sessionUUID; +} + +function entityIsGrabbedByOther(entityID) { + // by convention, a distance grab sets the tag of its action to be grab-*owner-session-id*. + var actionIDs = Entities.getActionIDs(entityID); + for (var actionIndex = 0; actionIndex < actionIDs.length; actionIndex++) { + var actionID = actionIDs[actionIndex]; + var actionArguments = Entities.getActionArguments(entityID, actionID); + var tag = actionArguments["tag"]; + if (tag == getTag()) { + // we see a grab-*uuid* shaped tag, but it's our tag, so that's okay. + continue; + } + if (tag.slice(0, 5) == "grab-") { + // we see a grab-*uuid* shaped tag and it's not ours, so someone else is grabbing it. + return true; + } + } + return false; +} + +function getSpatialOffsetPosition(hand, spatialKey) { + var position = Vec3.ZERO; + + if (hand !== RIGHT_HAND && spatialKey.leftRelativePosition) { + position = spatialKey.leftRelativePosition; + } + if (hand === RIGHT_HAND && spatialKey.rightRelativePosition) { + position = spatialKey.rightRelativePosition; + } + if (spatialKey.relativePosition) { + position = spatialKey.relativePosition; + } + + return position; +} + +var yFlip = Quat.angleAxis(180, Vec3.UNIT_Y); + +function getSpatialOffsetRotation(hand, spatialKey) { + var rotation = Quat.IDENTITY; + + if (hand !== RIGHT_HAND && spatialKey.leftRelativeRotation) { + rotation = spatialKey.leftRelativeRotation; + } + if (hand === RIGHT_HAND && spatialKey.rightRelativeRotation) { + rotation = spatialKey.rightRelativeRotation; + } + if (spatialKey.relativeRotation) { + rotation = spatialKey.relativeRotation; + } + + // Flip left hand + if (hand !== RIGHT_HAND) { + rotation = Quat.multiply(yFlip, rotation); + } + + return rotation; +} + +function MyController(hand) { + this.hand = hand; + if (this.hand === RIGHT_HAND) { + this.getHandPosition = MyAvatar.getRightPalmPosition; + this.getHandRotation = MyAvatar.getRightPalmRotation; + } else { + this.getHandPosition = MyAvatar.getLeftPalmPosition; + this.getHandRotation = MyAvatar.getLeftPalmRotation; + } + + var SPATIAL_CONTROLLERS_PER_PALM = 2; + var TIP_CONTROLLER_OFFSET = 1; + this.palm = SPATIAL_CONTROLLERS_PER_PALM * hand; + this.tip = SPATIAL_CONTROLLERS_PER_PALM * hand + TIP_CONTROLLER_OFFSET; + + this.actionID = null; // action this script created... + this.grabbedEntity = null; // on this entity. + this.state = STATE_OFF; + this.pointer = null; // entity-id of line object + this.triggerValue = 0; // rolling average of trigger value + this.rawTriggerValue = 0; + this.rawBumperValue = 0; + + //for visualizations + this.overlayLine = null; + this.particleBeam = null; + + //for lights + this.spotlight = null; + this.pointlight = null; + + this.ignoreIK = false; + this.offsetPosition = Vec3.ZERO; + this.offsetRotation = Quat.IDENTITY; + + var _this = this; + + this.update = function() { + + this.updateSmoothedTrigger(); + + switch (this.state) { + case STATE_OFF: + this.off(); + this.touchTest(); + break; + case STATE_SEARCHING: + this.search(); + break; + case STATE_EQUIP_SEARCHING: + this.search(); + break; + case STATE_DISTANCE_HOLDING: + this.distanceHolding(); + break; + case STATE_CONTINUE_DISTANCE_HOLDING: + this.continueDistanceHolding(); + break; + case STATE_NEAR_GRABBING: + case STATE_EQUIP: + this.nearGrabbing(); + break; + case STATE_WAITING_FOR_BUMPER_RELEASE: + this.waitingForBumperRelease(); + break; + case STATE_EQUIP_SPRING: + this.pullTowardEquipPosition() + break; + case STATE_CONTINUE_NEAR_GRABBING: + case STATE_CONTINUE_EQUIP_BD: + case STATE_CONTINUE_EQUIP: + this.continueNearGrabbing(); + break; + case STATE_NEAR_TRIGGER: + this.nearTrigger(); + break; + case STATE_CONTINUE_NEAR_TRIGGER: + this.continueNearTrigger(); + break; + case STATE_FAR_TRIGGER: + this.farTrigger(); + break; + case STATE_CONTINUE_FAR_TRIGGER: + this.continueFarTrigger(); + break; + case STATE_RELEASE: + this.release(); + break; + } + }; + + this.setState = function(newState) { + if (WANT_DEBUG) { + print("STATE: " + stateToName(this.state) + " --> " + stateToName(newState) + ", hand: " + this.hand); + } + this.state = newState; + }; + + this.debugLine = function(closePoint, farPoint, color) { + Entities.addEntity({ + type: "Line", + name: "Grab Debug Entity", + dimensions: LINE_ENTITY_DIMENSIONS, + visible: true, + position: closePoint, + linePoints: [ZERO_VEC, farPoint], + color: color, + lifetime: 0.1, + collisionsWillMove: false, + ignoreForCollisions: true, + userData: JSON.stringify({ + grabbableKey: { + grabbable: false + } + }) + }); + }; + + this.lineOn = function(closePoint, farPoint, color) { + // draw a line + if (this.pointer === null) { + this.pointer = Entities.addEntity({ + type: "Line", + name: "grab pointer", + dimensions: LINE_ENTITY_DIMENSIONS, + visible: true, + position: closePoint, + linePoints: [ZERO_VEC, farPoint], + color: color, + lifetime: LIFETIME, + collisionsWillMove: false, + ignoreForCollisions: true, + userData: JSON.stringify({ + grabbableKey: { + grabbable: false + } + }) + }); + } else { + var age = Entities.getEntityProperties(this.pointer, "age").age; + this.pointer = Entities.editEntity(this.pointer, { + position: closePoint, + linePoints: [ZERO_VEC, farPoint], + color: color, + lifetime: age + LIFETIME + }); + } + }; + + this.overlayLineOn = function(closePoint, farPoint, color) { + if (this.overlayLine === null) { + var lineProperties = { + lineWidth: 5, + start: closePoint, + end: farPoint, + color: color, + ignoreRayIntersection: true, // always ignore this + visible: true, + alpha: 1 + }; + + this.overlayLine = Overlays.addOverlay("line3d", lineProperties); + + } else { + var success = Overlays.editOverlay(this.overlayLine, { + lineWidth: 5, + start: closePoint, + end: farPoint, + color: color, + visible: true, + ignoreRayIntersection: true, // always ignore this + alpha: 1 + }); + } + }; + + this.handleParticleBeam = function(position, orientation, color) { + + var rotation = Quat.angleAxis(0, { + x: 1, + y: 0, + z: 0 + }); + + var finalRotation = Quat.multiply(orientation, rotation); + + if (this.particleBeam === null) { + this.createParticleBeam(position, finalRotation, color); + } else { + this.updateParticleBeam(position, finalRotation, color); + } + }; + + this.handleDistantParticleBeam = function(handPosition, objectPosition, objectRotation, color) { + + var handToObject = Vec3.subtract(objectPosition, handPosition); + var finalRotation = Quat.rotationBetween(Vec3.multiply(-1, Vec3.UP), handToObject); + + var distance = Vec3.distance(handPosition, objectPosition); + var speed = distance * 1; + + var lifepsan = distance / speed; + var lifespan = 1; + + if (this.particleBeam === null) { + this.createParticleBeam(objectPosition, finalRotation, color, speed); + } else { + this.updateParticleBeam(objectPosition, finalRotation, color, speed, lifepsan); + } + }; + + this.createParticleBeam = function(position, orientation, color, speed, lifepsan) { + + var particleBeamProperties = { + type: "ParticleEffect", + isEmitting: true, + position: position, + visible: false, + //rotation:Quat.fromPitchYawRollDegrees(-90.0, 0.0, 0.0), + "name": "Particle Beam", + "color": color, + "maxParticles": 2000, + "lifespan": LINE_LENGTH / 10, + "emitRate": 50, + "emitSpeed": 5, + "speedSpread": 2, + "emitOrientation": { + "x": -1, + "y": 0, + "z": 0, + "w": 1 + }, + "emitDimensions": { + "x": 0, + "y": 0, + "z": 0 + }, + "emitRadiusStart": 0.5, + "polarStart": 0, + "polarFinish": 0, + "azimuthStart": -3.1415927410125732, + "azimuthFinish": 3.1415927410125732, + "emitAcceleration": { + x: 0, + y: 0, + z: 0 + }, + "accelerationSpread": { + "x": 0, + "y": 0, + "z": 0 + }, + "particleRadius": 0.01, + "radiusSpread": 0, + // "radiusStart": 0.01, + // "radiusFinish": 0.01, + // "colorSpread": { + // "red": 0, + // "green": 0, + // "blue": 0 + // }, + // "colorStart": color, + // "colorFinish": color, + "alpha": 1, + "alphaSpread": 0, + "alphaStart": 1, + "alphaFinish": 1, + "additiveBlending": 1, + "textures": "https://hifi-content.s3.amazonaws.com/alan/dev/textures/grabsprite-3.png" + } + + this.particleBeam = Entities.addEntity(particleBeamProperties); + }; + + this.updateParticleBeam = function(position, orientation, color, speed, lifepsan) { + + Entities.editEntity(this.particleBeam, { + rotation: orientation, + position: position, + visible: true, + color: color, + emitSpeed: speed, + lifepsan: lifepsan + + }) + + }; + + this.evalLightWorldTransform = function(modelPos, modelRot) { + + var MODEL_LIGHT_POSITION = { + x: 0, + y: -0.3, + z: 0 + }; + + var MODEL_LIGHT_ROTATION = Quat.angleAxis(-90, { + x: 1, + y: 0, + z: 0 + }); + + return { + p: Vec3.sum(modelPos, Vec3.multiplyQbyV(modelRot, MODEL_LIGHT_POSITION)), + q: Quat.multiply(modelRot, MODEL_LIGHT_ROTATION) + }; + }; + + this.handleSpotlight = function(parentID, position) { + var LIFETIME = 100; + + var modelProperties = Entities.getEntityProperties(parentID, ['position', 'rotation']); + + var lightTransform = this.evalLightWorldTransform(modelProperties.position, modelProperties.rotation); + var lightProperties = { + type: "Light", + isSpotlight: true, + dimensions: { + x: 2, + y: 2, + z: 20 + }, + parentID: parentID, + color: { + red: 255, + green: 255, + blue: 255 + }, + intensity: 2, + exponent: 0.3, + cutoff: 20, + lifetime: LIFETIME, + position: lightTransform.p, + }; + + if (this.spotlight === null) { + this.spotlight = Entities.addEntity(lightProperties); + } else { + Entities.editEntity(this.spotlight, { + //without this, this light would maintain rotation with its parent + rotation: Quat.fromPitchYawRollDegrees(-90, 0, 0), + }) + } + }; + + this.handlePointLight = function(parentID, position) { + var LIFETIME = 100; + + var modelProperties = Entities.getEntityProperties(parentID, ['position', 'rotation']); + var lightTransform = this.evalLightWorldTransform(modelProperties.position, modelProperties.rotation); + + var lightProperties = { + type: "Light", + isSpotlight: false, + dimensions: { + x: 2, + y: 2, + z: 20 + }, + parentID: parentID, + color: { + red: 255, + green: 255, + blue: 255 + }, + intensity: 2, + exponent: 0.3, + cutoff: 20, + lifetime: LIFETIME, + position: lightTransform.p, + }; + + if (this.pointlight === null) { + this.pointlight = Entities.addEntity(lightProperties); + } else { + + } + }; + + this.lineOff = function() { + if (this.pointer !== null) { + Entities.deleteEntity(this.pointer); + } + this.pointer = null; + }; + + this.overlayLineOff = function() { + if (this.overlayLine !== null) { + Overlays.deleteOverlay(this.overlayLine); + } + this.overlayLine = null; + }; + + this.particleBeamOff = function() { + if (this.particleBeam !== null) { + Entities.editEntity(this.particleBeam, { + visible: false + }) + } + } + + this.turnLightsOff = function() { + if (this.spotlight !== null) { + Entities.deleteEntity(this.spotlight); + this.spotlight = null; + } + + if (this.pointlight !== null) { + Entities.deleteEntity(this.pointlight); + this.pointlight = null; + } + }; + + + this.turnOffVisualizations = function() { + if (USE_ENTITY_LINES_FOR_SEARCHING === true || USE_ENTITY_LINES_FOR_MOVING === true) { + this.lineOff(); + } + + if (USE_OVERLAY_LINES_FOR_SEARCHING === true || USE_OVERLAY_LINES_FOR_MOVING === true) { + this.overlayLineOff(); + } + + if (USE_PARTICLE_BEAM_FOR_SEARCHING === true || USE_PARTICLE_BEAM_FOR_MOVING === true) { + this.particleBeamOff(); + } + }; + + this.triggerPress = function(value) { + _this.rawTriggerValue = value; + }; + + this.bumperPress = function(value) { + _this.rawBumperValue = value; + }; + + this.updateSmoothedTrigger = function() { + var triggerValue = this.rawTriggerValue; + // smooth out trigger value + this.triggerValue = (this.triggerValue * TRIGGER_SMOOTH_RATIO) + + (triggerValue * (1.0 - TRIGGER_SMOOTH_RATIO)); + }; + + this.triggerSmoothedSqueezed = function() { + return this.triggerValue > TRIGGER_ON_VALUE; + }; + + this.triggerSmoothedReleased = function() { + return this.triggerValue < TRIGGER_OFF_VALUE; + }; + + this.triggerSqueezed = function() { + var triggerValue = this.rawTriggerValue; + return triggerValue > TRIGGER_ON_VALUE; + }; + + this.bumperSqueezed = function() { + return _this.rawBumperValue > BUMPER_ON_VALUE; + }; + + this.bumperReleased = function() { + return _this.rawBumperValue < BUMPER_ON_VALUE; + }; + + this.off = function() { + if (this.triggerSmoothedSqueezed()) { + this.lastPickTime = 0; + this.setState(STATE_SEARCHING); + return; + } + if (this.bumperSqueezed()) { + this.lastPickTime = 0; + this.setState(STATE_EQUIP_SEARCHING); + return; + } + }; + + this.search = function() { + this.grabbedEntity = null; + + if (this.state == STATE_SEARCHING ? this.triggerSmoothedReleased() : this.bumperReleased()) { + this.setState(STATE_RELEASE); + return; + } + + // the trigger is being pressed, do a ray test + var handPosition = this.getHandPosition(); + var distantPickRay = { + origin: handPosition, + direction: Quat.getUp(this.getHandRotation()), + length: PICK_MAX_DISTANCE + }; + + // don't pick 60x per second. + var pickRays = []; + var now = Date.now(); + if (now - this.lastPickTime > MSECS_PER_SEC / PICKS_PER_SECOND_PER_HAND) { + pickRays = [distantPickRay]; + this.lastPickTime = now; + } + + for (var index = 0; index < pickRays.length; ++index) { + var pickRay = pickRays[index]; + var directionNormalized = Vec3.normalize(pickRay.direction); + var directionBacked = Vec3.multiply(directionNormalized, PICK_BACKOFF_DISTANCE); + var pickRayBacked = { + origin: Vec3.subtract(pickRay.origin, directionBacked), + direction: pickRay.direction + }; + + if (WANT_DEBUG) { + this.debugLine(pickRayBacked.origin, Vec3.multiply(pickRayBacked.direction, NEAR_PICK_MAX_DISTANCE), { + red: 0, + green: 255, + blue: 0 + }) + } + + var intersection = Entities.findRayIntersection(pickRayBacked, true); + + if (intersection.intersects) { + // the ray is intersecting something we can move. + var intersectionDistance = Vec3.distance(pickRay.origin, intersection.intersection); + + var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, intersection.entityID, DEFAULT_GRABBABLE_DATA); + + if (intersection.properties.name == "Grab Debug Entity") { + continue; + } + + if (typeof grabbableData.grabbable !== 'undefined' && !grabbableData.grabbable) { + continue; + } + if (intersectionDistance > pickRay.length) { + // too far away for this ray. + continue; + } + if (intersectionDistance <= NEAR_PICK_MAX_DISTANCE) { + // the hand is very close to the intersected object. go into close-grabbing mode. + if (grabbableData.wantsTrigger) { + this.grabbedEntity = intersection.entityID; + this.setState(STATE_NEAR_TRIGGER); + return; + } else if (!intersection.properties.locked) { + this.grabbedEntity = intersection.entityID; + if (this.state == STATE_SEARCHING) { + this.setState(STATE_NEAR_GRABBING); + } else { // equipping + if (typeof grabbableData.spatialKey !== 'undefined') { + // TODO + // if we go to STATE_EQUIP_SPRING the item will be pulled to the hand and will then switch + // to STATE_EQUIP. This needs some debugging, so just jump straight to STATE_EQUIP here. + // this.setState(STATE_EQUIP_SPRING); + this.setState(STATE_EQUIP); + } else { + this.setState(STATE_EQUIP); + } + } + return; + } + } else if (!entityIsGrabbedByOther(intersection.entityID)) { + // don't allow two people to distance grab the same object + if (intersection.properties.collisionsWillMove && !intersection.properties.locked) { + // the hand is far from the intersected object. go into distance-holding mode + this.grabbedEntity = intersection.entityID; + if (typeof grabbableData.spatialKey !== 'undefined' && this.state == STATE_EQUIP_SEARCHING) { + // if a distance pick in equip mode hits something with a spatialKey, equip it + // TODO use STATE_EQUIP_SPRING here once it works right. + // this.setState(STATE_EQUIP_SPRING); + this.setState(STATE_EQUIP); + return; + } else if (this.state == STATE_SEARCHING) { + this.setState(STATE_DISTANCE_HOLDING); + return; + } + } else if (grabbableData.wantsTrigger) { + this.grabbedEntity = intersection.entityID; + this.setState(STATE_FAR_TRIGGER); + return; + } + } + } + } + + // forward ray test failed, try sphere test. + if (WANT_DEBUG) { + Entities.addEntity({ + type: "Sphere", + name: "Grab Debug Entity", + dimensions: { + x: GRAB_RADIUS, + y: GRAB_RADIUS, + z: GRAB_RADIUS + }, + visible: true, + position: handPosition, + color: { + red: 0, + green: 255, + blue: 0 + }, + lifetime: 0.1, + collisionsWillMove: false, + ignoreForCollisions: true, + userData: JSON.stringify({ + grabbableKey: { + grabbable: false + } + }) + }); + } + + var nearbyEntities = Entities.findEntities(handPosition, GRAB_RADIUS); + var minDistance = PICK_MAX_DISTANCE; + var i, props, distance, grabbableData; + this.grabbedEntity = null; + for (i = 0; i < nearbyEntities.length; i++) { + var grabbableDataForCandidate = + getEntityCustomData(GRABBABLE_DATA_KEY, nearbyEntities[i], DEFAULT_GRABBABLE_DATA); + if (typeof grabbableDataForCandidate.grabbable !== 'undefined' && !grabbableDataForCandidate.grabbable) { + continue; + } + var propsForCandidate = Entities.getEntityProperties(nearbyEntities[i], GRABBABLE_PROPERTIES); + + if (propsForCandidate.type == 'Unknown') { + continue; + } + + if (propsForCandidate.type == 'Light') { + continue; + } + + if (propsForCandidate.type == 'ParticleEffect') { + continue; + } + + if (propsForCandidate.type == 'PolyLine') { + continue; + } + + if (propsForCandidate.type == 'Zone') { + continue; + } + + if (propsForCandidate.locked && !grabbableDataForCandidate.wantsTrigger) { + continue; + } + + if (propsForCandidate.name == "Grab Debug Entity") { + continue; + } + + if (propsForCandidate.name == "grab pointer") { + continue; + } + + distance = Vec3.distance(propsForCandidate.position, handPosition); + if (distance < minDistance) { + this.grabbedEntity = nearbyEntities[i]; + minDistance = distance; + props = propsForCandidate; + grabbableData = grabbableDataForCandidate; + } + } + if (this.grabbedEntity !== null) { + if (grabbableData.wantsTrigger) { + this.setState(STATE_NEAR_TRIGGER); + return; + } else if (!props.locked && props.collisionsWillMove) { + this.setState(this.state == STATE_SEARCHING ? STATE_NEAR_GRABBING : STATE_EQUIP) + return; + } + } + + //search line visualizations + if (USE_ENTITY_LINES_FOR_SEARCHING === true) { + this.lineOn(distantPickRay.origin, Vec3.multiply(distantPickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); + } + + if (USE_OVERLAY_LINES_FOR_SEARCHING === true) { + this.overlayLineOn(distantPickRay.origin, Vec3.sum(distantPickRay.origin, Vec3.multiply(distantPickRay.direction, LINE_LENGTH)), NO_INTERSECT_COLOR); + } + + if (USE_PARTICLE_BEAM_FOR_SEARCHING === true) { + this.handleParticleBeam(distantPickRay.origin, this.getHandRotation(), NO_INTERSECT_COLOR); + } + + }; + + this.distanceHolding = function() { + var handControllerPosition = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandPosition : MyAvatar.leftHandPosition; + var controllerHandInput = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; + var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getPoseValue(controllerHandInput).rotation); + var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); + var now = Date.now(); + + // add the action and initialize some variables + this.currentObjectPosition = grabbedProperties.position; + this.currentObjectRotation = grabbedProperties.rotation; + this.currentObjectTime = now; + this.handRelativePreviousPosition = Vec3.subtract(handControllerPosition, MyAvatar.position); + this.handPreviousRotation = handRotation; + this.currentCameraOrientation = Camera.orientation; + + // compute a constant based on the initial conditions which we use below to exagerate hand motion onto the held object + this.radiusScalar = Math.log(Vec3.distance(this.currentObjectPosition, handControllerPosition) + 1.0); + if (this.radiusScalar < 1.0) { + this.radiusScalar = 1.0; + } + + this.actionID = NULL_ACTION_ID; + this.actionID = Entities.addAction("spring", this.grabbedEntity, { + targetPosition: this.currentObjectPosition, + linearTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME, + targetRotation: this.currentObjectRotation, + angularTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME, + tag: getTag(), + ttl: ACTION_TTL + }); + if (this.actionID === NULL_ACTION_ID) { + this.actionID = null; + } + this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC); + + if (this.actionID !== null) { + this.setState(STATE_CONTINUE_DISTANCE_HOLDING); + this.activateEntity(this.grabbedEntity, grabbedProperties); + if (this.hand === RIGHT_HAND) { + Entities.callEntityMethod(this.grabbedEntity, "setRightHand"); + } else { + Entities.callEntityMethod(this.grabbedEntity, "setLeftHand"); + } + Entities.callEntityMethod(this.grabbedEntity, "setHand", [this.hand]); + Entities.callEntityMethod(this.grabbedEntity, "startDistantGrab"); + } + + this.currentAvatarPosition = MyAvatar.position; + this.currentAvatarOrientation = MyAvatar.orientation; + + this.turnOffVisualizations(); + }; + + this.continueDistanceHolding = function() { + if (this.triggerSmoothedReleased()) { + this.setState(STATE_RELEASE); + Entities.callEntityMethod(this.grabbedEntity, "releaseGrab"); + return; + } + + var handPosition = this.getHandPosition(); + var handControllerPosition = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandPosition : MyAvatar.leftHandPosition; + var controllerHandInput = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; + var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getPoseValue(controllerHandInput).rotation); + var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); + var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); + + if (this.state == STATE_CONTINUE_DISTANCE_HOLDING && this.bumperSqueezed() && + typeof grabbableData.spatialKey !== 'undefined') { + var saveGrabbedID = this.grabbedEntity; + this.release(); + this.setState(STATE_EQUIP); + this.grabbedEntity = saveGrabbedID; + return; + } + + + // the action was set up on a previous call. update the targets. + var radius = Vec3.distance(this.currentObjectPosition, handControllerPosition) * + this.radiusScalar * DISTANCE_HOLDING_RADIUS_FACTOR; + if (radius < 1.0) { + radius = 1.0; + } + + // how far did avatar move this timestep? + var currentPosition = MyAvatar.position; + var avatarDeltaPosition = Vec3.subtract(currentPosition, this.currentAvatarPosition); + this.currentAvatarPosition = currentPosition; + + // How far did the avatar turn this timestep? + // Note: The following code is too long because we need a Quat.quatBetween() function + // that returns the minimum quaternion between two quaternions. + var currentOrientation = MyAvatar.orientation; + if (Quat.dot(currentOrientation, this.currentAvatarOrientation) < 0.0) { + var negativeCurrentOrientation = { + x: -currentOrientation.x, + y: -currentOrientation.y, + z: -currentOrientation.z, + w: -currentOrientation.w + }; + var avatarDeltaOrientation = Quat.multiply(negativeCurrentOrientation, Quat.inverse(this.currentAvatarOrientation)); + } else { + var avatarDeltaOrientation = Quat.multiply(currentOrientation, Quat.inverse(this.currentAvatarOrientation)); + } + var handToAvatar = Vec3.subtract(handControllerPosition, this.currentAvatarPosition); + var objectToAvatar = Vec3.subtract(this.currentObjectPosition, this.currentAvatarPosition); + var handMovementFromTurning = Vec3.subtract(Quat.multiply(avatarDeltaOrientation, handToAvatar), handToAvatar); + var objectMovementFromTurning = Vec3.subtract(Quat.multiply(avatarDeltaOrientation, objectToAvatar), objectToAvatar); + this.currentAvatarOrientation = currentOrientation; + + // how far did hand move this timestep? + var handMoved = Vec3.subtract(handToAvatar, this.handRelativePreviousPosition); + this.handRelativePreviousPosition = handToAvatar; + + // magnify the hand movement but not the change from avatar movement & rotation + handMoved = Vec3.subtract(handMoved, handMovementFromTurning); + var superHandMoved = Vec3.multiply(handMoved, radius); + + // Move the object by the magnified amount and then by amount from avatar movement & rotation + var newObjectPosition = Vec3.sum(this.currentObjectPosition, superHandMoved); + newObjectPosition = Vec3.sum(newObjectPosition, avatarDeltaPosition); + newObjectPosition = Vec3.sum(newObjectPosition, objectMovementFromTurning); + + var deltaPosition = Vec3.subtract(newObjectPosition, this.currentObjectPosition); // meters + var now = Date.now(); + var deltaTime = (now - this.currentObjectTime) / MSEC_PER_SEC; // convert to seconds + + this.currentObjectPosition = newObjectPosition; + this.currentObjectTime = now; + + // this doubles hand rotation + var handChange = Quat.multiply(Quat.slerp(this.handPreviousRotation, + handRotation, + DISTANCE_HOLDING_ROTATION_EXAGGERATION_FACTOR), + Quat.inverse(this.handPreviousRotation)); + this.handPreviousRotation = handRotation; + this.currentObjectRotation = Quat.multiply(handChange, this.currentObjectRotation); + + Entities.callEntityMethod(this.grabbedEntity, "continueDistantGrab"); + + // mix in head motion + if (MOVE_WITH_HEAD) { + var objDistance = Vec3.length(objectToAvatar); + var before = Vec3.multiplyQbyV(this.currentCameraOrientation, { + x: 0.0, + y: 0.0, + z: objDistance + }); + var after = Vec3.multiplyQbyV(Camera.orientation, { + x: 0.0, + y: 0.0, + z: objDistance + }); + var change = Vec3.subtract(before, after); + this.currentCameraOrientation = Camera.orientation; + this.currentObjectPosition = Vec3.sum(this.currentObjectPosition, change); + } + + + //visualizations + if (USE_ENTITY_LINES_FOR_MOVING === true) { + this.lineOn(handPosition, Vec3.subtract(grabbedProperties.position, handPosition), INTERSECT_COLOR); + } + if (USE_OVERLAY_LINES_FOR_MOVING === true) { + this.overlayLineOn(handPosition, grabbedProperties.position, INTERSECT_COLOR); + } + if (USE_PARTICLE_BEAM_FOR_MOVING === true) { + this.handleDistantParticleBeam(handPosition, grabbedProperties.position, this.currentObjectRotation, INTERSECT_COLOR) + } + if (USE_POINTLIGHT === true) { + this.handlePointLight(this.grabbedEntity); + } + if (USE_SPOTLIGHT === true) { + this.handleSpotlight(this.grabbedEntity); + } + + Entities.updateAction(this.grabbedEntity, this.actionID, { + targetPosition: this.currentObjectPosition, + linearTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME, + targetRotation: this.currentObjectRotation, + angularTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME, + ttl: ACTION_TTL + }); + + this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC); + }; + + this.nearGrabbing = function() { + var now = Date.now(); + var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); + + if (this.state == STATE_NEAR_GRABBING && this.triggerSmoothedReleased()) { + this.setState(STATE_RELEASE); + Entities.callEntityMethod(this.grabbedEntity, "releaseGrab"); + return; + } + + this.turnOffVisualizations(); + + var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); + this.activateEntity(this.grabbedEntity, grabbedProperties); + if (grabbedProperties.collisionsWillMove && NEAR_GRABBING_KINEMATIC) { + Entities.editEntity(this.grabbedEntity, { + collisionsWillMove: false + }); + } + + var handRotation = this.getHandRotation(); + var handPosition = this.getHandPosition(); + + var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); + + if (this.state != STATE_NEAR_GRABBING && grabbableData.spatialKey) { + // if an object is "equipped" and has a spatialKey, use it. + this.ignoreIK = grabbableData.spatialKey.ignoreIK ? grabbableData.spatialKey.ignoreIK : false; + this.offsetPosition = getSpatialOffsetPosition(this.hand, grabbableData.spatialKey); + this.offsetRotation = getSpatialOffsetRotation(this.hand, grabbableData.spatialKey); + } else { + this.ignoreIK = false; + + var objectRotation = grabbedProperties.rotation; + this.offsetRotation = Quat.multiply(Quat.inverse(handRotation), objectRotation); + + var currentObjectPosition = grabbedProperties.position; + var offset = Vec3.subtract(currentObjectPosition, handPosition); + this.offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, this.offsetRotation)), offset); + } + + this.actionID = NULL_ACTION_ID; + this.actionID = Entities.addAction("hold", this.grabbedEntity, { + hand: this.hand === RIGHT_HAND ? "right" : "left", + timeScale: NEAR_GRABBING_ACTION_TIMEFRAME, + relativePosition: this.offsetPosition, + relativeRotation: this.offsetRotation, + ttl: ACTION_TTL, + kinematic: NEAR_GRABBING_KINEMATIC, + kinematicSetVelocity: true, + ignoreIK: this.ignoreIK + }); + if (this.actionID === NULL_ACTION_ID) { + this.actionID = null; + } else { + this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC); + if (this.state == STATE_NEAR_GRABBING) { + this.setState(STATE_CONTINUE_NEAR_GRABBING); + } else { + // equipping + Entities.callEntityMethod(this.grabbedEntity, "startEquip", [JSON.stringify(this.hand)]); + this.startHandGrasp(); + + this.setState(STATE_CONTINUE_EQUIP_BD); + } + + if (this.hand === RIGHT_HAND) { + Entities.callEntityMethod(this.grabbedEntity, "setRightHand"); + } else { + Entities.callEntityMethod(this.grabbedEntity, "setLeftHand"); + } + + Entities.callEntityMethod(this.grabbedEntity, "setHand", [this.hand]); + + Entities.callEntityMethod(this.grabbedEntity, "startNearGrab"); + + } + + this.currentHandControllerTipPosition = + (this.hand === RIGHT_HAND) ? MyAvatar.rightHandTipPosition : MyAvatar.leftHandTipPosition; + + this.currentObjectTime = Date.now(); + }; + + this.continueNearGrabbing = function() { + if (this.state == STATE_CONTINUE_NEAR_GRABBING && this.triggerSmoothedReleased()) { + this.setState(STATE_RELEASE); + Entities.callEntityMethod(this.grabbedEntity, "releaseGrab"); + return; + } + if (this.state == STATE_CONTINUE_EQUIP_BD && this.bumperReleased()) { + this.setState(STATE_CONTINUE_EQUIP); + return; + } + if (this.state == STATE_CONTINUE_EQUIP && this.bumperSqueezed()) { + this.setState(STATE_WAITING_FOR_BUMPER_RELEASE); + return; + } + if (this.state == STATE_CONTINUE_NEAR_GRABBING && this.bumperSqueezed()) { + this.setState(STATE_CONTINUE_EQUIP_BD); + Entities.callEntityMethod(this.grabbedEntity, "startEquip", [JSON.stringify(this.hand)]); + return; + } + + // Keep track of the fingertip velocity to impart when we release the object. + // Note that the idea of using a constant 'tip' velocity regardless of the + // object's actual held offset is an idea intended to make it easier to throw things: + // Because we might catch something or transfer it between hands without a good idea + // of it's actual offset, let's try imparting a velocity which is at a fixed radius + // from the palm. + + var handControllerPosition = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandPosition : MyAvatar.leftHandPosition; + var now = Date.now(); + + var deltaPosition = Vec3.subtract(handControllerPosition, this.currentHandControllerTipPosition); // meters + var deltaTime = (now - this.currentObjectTime) / MSEC_PER_SEC; // convert to seconds + + this.currentHandControllerTipPosition = handControllerPosition; + this.currentObjectTime = now; + Entities.callEntityMethod(this.grabbedEntity, "continueNearGrab"); + + if (this.state === STATE_CONTINUE_EQUIP_BD) { + Entities.callEntityMethod(this.grabbedEntity, "continueEquip"); + } + + if (this.actionTimeout - now < ACTION_TTL_REFRESH * MSEC_PER_SEC) { + // if less than a 5 seconds left, refresh the actions ttl + Entities.updateAction(this.grabbedEntity, this.actionID, { + hand: this.hand === RIGHT_HAND ? "right" : "left", + timeScale: NEAR_GRABBING_ACTION_TIMEFRAME, + relativePosition: this.offsetPosition, + relativeRotation: this.offsetRotation, + ttl: ACTION_TTL, + kinematic: NEAR_GRABBING_KINEMATIC, + kinematicSetVelocity: true, + ignoreIK: this.ignoreIK + }); + this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC); + } + }; + + this.waitingForBumperRelease = function() { + if (this.bumperReleased()) { + this.setState(STATE_RELEASE); + Entities.callEntityMethod(this.grabbedEntity, "releaseGrab"); + Entities.callEntityMethod(this.grabbedEntity, "unequip"); + this.endHandGrasp(); + + } + }; + + this.pullTowardEquipPosition = function() { + + this.turnOffVisualizations(); + + var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); + var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); + + // use a spring to pull the object to where it will be when equipped + var relativeRotation = getSpatialOffsetRotation(this.hand, grabbableData.spatialKey); + var relativePosition = getSpatialOffsetPosition(this.hand, grabbableData.spatialKey); + var ignoreIK = grabbableData.spatialKey.ignoreIK ? grabbableData.spatialKey.ignoreIK : false; + var handRotation = this.getHandRotation(); + var handPosition = this.getHandPosition(); + var targetRotation = Quat.multiply(handRotation, relativeRotation); + var offset = Vec3.multiplyQbyV(targetRotation, relativePosition); + var targetPosition = Vec3.sum(handPosition, offset); + + if (typeof this.equipSpringID === 'undefined' || + this.equipSpringID === null || + this.equipSpringID === NULL_ACTION_ID) { + this.equipSpringID = Entities.addAction("spring", this.grabbedEntity, { + targetPosition: targetPosition, + linearTimeScale: EQUIP_SPRING_TIMEFRAME, + targetRotation: targetRotation, + angularTimeScale: EQUIP_SPRING_TIMEFRAME, + ttl: ACTION_TTL, + ignoreIK: ignoreIK + }); + if (this.equipSpringID === NULL_ACTION_ID) { + this.equipSpringID = null; + this.setState(STATE_OFF); + return; + } + } else { + Entities.updateAction(this.grabbedEntity, this.equipSpringID, { + targetPosition: targetPosition, + linearTimeScale: EQUIP_SPRING_TIMEFRAME, + targetRotation: targetRotation, + angularTimeScale: EQUIP_SPRING_TIMEFRAME, + ttl: ACTION_TTL, + ignoreIK: ignoreIK + }); + } + + if (Vec3.distance(grabbedProperties.position, targetPosition) < EQUIP_SPRING_SHUTOFF_DISTANCE) { + Entities.deleteAction(this.grabbedEntity, this.equipSpringID); + this.equipSpringID = null; + this.setState(STATE_EQUIP); + } + }; + + this.nearTrigger = function() { + if (this.triggerSmoothedReleased()) { + this.setState(STATE_RELEASE); + Entities.callEntityMethod(this.grabbedEntity, "stopNearTrigger"); + return; + } + if (this.hand === RIGHT_HAND) { + Entities.callEntityMethod(this.grabbedEntity, "setRightHand"); + } else { + Entities.callEntityMethod(this.grabbedEntity, "setLeftHand"); + } + + Entities.callEntityMethod(this.grabbedEntity, "setHand", [this.hand]); + + Entities.callEntityMethod(this.grabbedEntity, "startNearTrigger"); + this.setState(STATE_CONTINUE_NEAR_TRIGGER); + }; + + this.farTrigger = function() { + if (this.triggerSmoothedReleased()) { + this.setState(STATE_RELEASE); + Entities.callEntityMethod(this.grabbedEntity, "stopFarTrigger"); + return; + } + + if (this.hand === RIGHT_HAND) { + Entities.callEntityMethod(this.grabbedEntity, "setRightHand"); + } else { + Entities.callEntityMethod(this.grabbedEntity, "setLeftHand"); + } + Entities.callEntityMethod(this.grabbedEntity, "setHand", [this.hand]); + Entities.callEntityMethod(this.grabbedEntity, "startFarTrigger"); + this.setState(STATE_CONTINUE_FAR_TRIGGER); + }; + + this.continueNearTrigger = function() { + if (this.triggerSmoothedReleased()) { + this.setState(STATE_RELEASE); + Entities.callEntityMethod(this.grabbedEntity, "stopNearTrigger"); + return; + } + + Entities.callEntityMethod(this.grabbedEntity, "continueNearTrigger"); + }; + + this.continueFarTrigger = function() { + if (this.triggerSmoothedReleased()) { + this.setState(STATE_RELEASE); + Entities.callEntityMethod(this.grabbedEntity, "stopNearTrigger"); + return; + } + + var handPosition = this.getHandPosition(); + var pickRay = { + origin: handPosition, + direction: Quat.getUp(this.getHandRotation()) + }; + + var now = Date.now(); + if (now - this.lastPickTime > MSECS_PER_SEC / PICKS_PER_SECOND_PER_HAND) { + var intersection = Entities.findRayIntersection(pickRay, true); + this.lastPickTime = now; + if (intersection.entityID != this.grabbedEntity) { + this.setState(STATE_RELEASE); + Entities.callEntityMethod(this.grabbedEntity, "stopFarTrigger"); + return; + } + } + + this.lineOn(pickRay.origin, Vec3.multiply(pickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); + Entities.callEntityMethod(this.grabbedEntity, "continueFarTrigger"); + }; + + _this.allTouchedIDs = {}; + + this.touchTest = function() { + var maxDistance = 0.05; + var leftHandPosition = MyAvatar.getLeftPalmPosition(); + var rightHandPosition = MyAvatar.getRightPalmPosition(); + var leftEntities = Entities.findEntities(leftHandPosition, maxDistance); + var rightEntities = Entities.findEntities(rightHandPosition, maxDistance); + var ids = []; + + if (leftEntities.length !== 0) { + leftEntities.forEach(function(entity) { + ids.push(entity); + }); + + } + + if (rightEntities.length !== 0) { + rightEntities.forEach(function(entity) { + ids.push(entity); + }); + } + + ids.forEach(function(id) { + + var props = Entities.getEntityProperties(id, ["boundingBox", "name"]); + if (props.name === 'pointer') { + return; + } else { + var entityMinPoint = props.boundingBox.brn; + var entityMaxPoint = props.boundingBox.tfl; + var leftIsTouching = pointInExtents(leftHandPosition, entityMinPoint, entityMaxPoint); + var rightIsTouching = pointInExtents(rightHandPosition, entityMinPoint, entityMaxPoint); + + if ((leftIsTouching || rightIsTouching) && _this.allTouchedIDs[id] === undefined) { + // we haven't been touched before, but either right or left is touching us now + _this.allTouchedIDs[id] = true; + _this.startTouch(id); + } else if ((leftIsTouching || rightIsTouching) && _this.allTouchedIDs[id]) { + // we have been touched before and are still being touched + // continue touch + _this.continueTouch(id); + } else if (_this.allTouchedIDs[id]) { + delete _this.allTouchedIDs[id]; + _this.stopTouch(id); + + } else { + //we are in another state + return; + } + } + + }); + + }; + + this.startTouch = function(entityID) { + Entities.callEntityMethod(entityID, "startTouch"); + }; + + this.continueTouch = function(entityID) { + Entities.callEntityMethod(entityID, "continueTouch"); + }; + + this.stopTouch = function(entityID) { + Entities.callEntityMethod(entityID, "stopTouch"); + }; + + this.release = function() { + + this.turnLightsOff(); + this.turnOffVisualizations(); + + if (this.grabbedEntity !== null) { + if (this.actionID !== null) { + Entities.deleteAction(this.grabbedEntity, this.actionID); + } + } + + this.deactivateEntity(this.grabbedEntity); + + this.grabbedEntity = null; + this.actionID = null; + this.setState(STATE_OFF); + }; + + this.cleanup = function() { + this.release(); + this.endHandGrasp(); + Entities.deleteEntity(this.particleBeam); + Entities.deleteEntity(this.spotLight); + Entities.deleteEntity(this.pointLight); + }; + + this.activateEntity = function(entityID, grabbedProperties) { + var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, entityID, DEFAULT_GRABBABLE_DATA); + var invertSolidWhileHeld = grabbableData["invertSolidWhileHeld"]; + var data = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, {}); + data["activated"] = true; + data["avatarId"] = MyAvatar.sessionUUID; + data["refCount"] = data["refCount"] ? data["refCount"] + 1 : 1; + // zero gravity and set ignoreForCollisions in a way that lets us put them back, after all grabs are done + if (data["refCount"] == 1) { + data["gravity"] = grabbedProperties.gravity; + data["ignoreForCollisions"] = grabbedProperties.ignoreForCollisions; + data["collisionsWillMove"] = grabbedProperties.collisionsWillMove; + var whileHeldProperties = { + gravity: { + x: 0, + y: 0, + z: 0 + } + }; + if (invertSolidWhileHeld) { + whileHeldProperties["ignoreForCollisions"] = !grabbedProperties.ignoreForCollisions; + } + Entities.editEntity(entityID, whileHeldProperties); + } + + setEntityCustomData(GRAB_USER_DATA_KEY, entityID, data); + return data; + }; + + this.deactivateEntity = function(entityID) { + var data = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, {}); + if (data && data["refCount"]) { + data["refCount"] = data["refCount"] - 1; + if (data["refCount"] < 1) { + Entities.editEntity(entityID, { + gravity: data["gravity"], + ignoreForCollisions: data["ignoreForCollisions"], + collisionsWillMove: data["collisionsWillMove"] + }); + data = null; + } + } else { + data = null; + } + setEntityCustomData(GRAB_USER_DATA_KEY, entityID, data); + }; + + + //this is our handler, where we do the actual work of changing animation settings + this.graspHand = function(animationProperties) { + var result = {}; + //full alpha on overlay for this hand + //set grab to true + //set idle to false + //full alpha on the blend btw open and grab + if (_this.hand === RIGHT_HAND) { + result['rightHandOverlayAlpha'] = 1.0; + result['isRightHandGrab'] = true; + result['isRightHandIdle'] = false; + result['rightHandGrabBlend'] = 1.0; + } else if (_this.hand === LEFT_HAND) { + result['leftHandOverlayAlpha'] = 1.0; + result['isLeftHandGrab'] = true; + result['isLeftHandIdle'] = false; + result['leftHandGrabBlend'] = 1.0; + } + //return an object with our updated settings + return result; + }; + + this.graspHandler = null + + this.startHandGrasp = function() { + if (this.hand === RIGHT_HAND) { + this.graspHandler = MyAvatar.addAnimationStateHandler(this.graspHand, ['isRightHandGrab']); + } else if (this.hand === LEFT_HAND) { + this.graspHandler = MyAvatar.addAnimationStateHandler(this.graspHand, ['isLeftHandGrab']); + } + }; + + this.endHandGrasp = function() { + // Tell the animation system we don't need any more callbacks. + MyAvatar.removeAnimationStateHandler(this.graspHandler); + }; + +}; + +var rightController = new MyController(RIGHT_HAND); +var leftController = new MyController(LEFT_HAND); + +//preload the particle beams so that they are full length when you start searching +if (USE_PARTICLE_BEAM_FOR_SEARCHING === true || USE_PARTICLE_BEAM_FOR_MOVING === true) { + rightController.createParticleBeam(); + leftController.createParticleBeam(); +} + +var MAPPING_NAME = "com.highfidelity.handControllerGrab"; + +var mapping = Controller.newMapping(MAPPING_NAME); +mapping.from([Controller.Standard.RT]).peek().to(rightController.triggerPress); +mapping.from([Controller.Standard.LT]).peek().to(leftController.triggerPress); + +mapping.from([Controller.Standard.RB]).peek().to(rightController.bumperPress); +mapping.from([Controller.Standard.LB]).peek().to(leftController.bumperPress); + +Controller.enableMapping(MAPPING_NAME); + +//the section below allows the grab script to listen for messages that disable either one or both hands. useful for two handed items +var handToDisable = 'none'; + +function update() { + if (handToDisable !== LEFT_HAND && handToDisable !== 'both') { + leftController.update(); + } + if (handToDisable !== RIGHT_HAND && handToDisable !== 'both') { + rightController.update(); + } +} + +Messages.subscribe('Hifi-Hand-Disabler'); + +handleHandDisablerMessages = function(channel, message, sender) { + + if (sender === MyAvatar.sessionUUID) { + if (message === 'left') { + handToDisable = LEFT_HAND; + } + if (message === 'right') { + handToDisable = RIGHT_HAND; + } + if (message === 'both') { + handToDisable = 'both'; + } + if (message === 'none') { + handToDisable = 'none'; + } + } + +} + +Messages.messageReceived.connect(handleHandDisablerMessages); + +function cleanup() { + rightController.cleanup(); + leftController.cleanup(); + Controller.disableMapping(MAPPING_NAME); +} + +Script.scriptEnding.connect(cleanup); +Script.update.connect(update); \ No newline at end of file diff --git a/examples/controllers/handControllerGrab-pointlight.js b/examples/controllers/handControllerGrab-pointlight.js new file mode 100644 index 0000000000..fc14e6026e --- /dev/null +++ b/examples/controllers/handControllerGrab-pointlight.js @@ -0,0 +1,1649 @@ +// handControllerGrab.js +// +// Created by Eric Levin on 9/2/15 +// Additions by James B. Pollack @imgntn on 9/24/2015 +// Additions By Seth Alves on 10/20/2015 +// Copyright 2015 High Fidelity, Inc. +// +// Grabs physically moveable entities with hydra-like controllers; it works for either near or far objects. +// Also supports touch and equipping objects. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +/*global print, MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, Audio, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt, pointInExtents, vec3equal, setEntityCustomData, getEntityCustomData */ + +Script.include("../libraries/utils.js"); + +// +// add lines where the hand ray picking is happening +// +var WANT_DEBUG = false; + +// +// these tune time-averaging and "on" value for analog trigger +// + +var TRIGGER_SMOOTH_RATIO = 0.1; // 0.0 disables smoothing of trigger value +var TRIGGER_ON_VALUE = 0.4; +var TRIGGER_OFF_VALUE = 0.15; + +var BUMPER_ON_VALUE = 0.5; + +// +// distant manipulation +// + +var DISTANCE_HOLDING_RADIUS_FACTOR = 3.5; // multiplied by distance between hand and object +var DISTANCE_HOLDING_ACTION_TIMEFRAME = 0.1; // how quickly objects move to their new position +var DISTANCE_HOLDING_ROTATION_EXAGGERATION_FACTOR = 2.0; // object rotates this much more than hand did +var MOVE_WITH_HEAD = true; // experimental head-controll of distantly held objects + +var NO_INTERSECT_COLOR = { + red: 10, + green: 10, + blue: 255 +}; // line color when pick misses +var INTERSECT_COLOR = { + red: 250, + green: 10, + blue: 10 +}; // line color when pick hits +var LINE_ENTITY_DIMENSIONS = { + x: 1000, + y: 1000, + z: 1000 +}; + +var LINE_LENGTH = 500; +var PICK_MAX_DISTANCE = 500; // max length of pick-ray + +// +// near grabbing +// + +var GRAB_RADIUS = 0.03; // if the ray misses but an object is this close, it will still be selected +var NEAR_GRABBING_ACTION_TIMEFRAME = 0.05; // how quickly objects move to their new position +var NEAR_GRABBING_VELOCITY_SMOOTH_RATIO = 1.0; // adjust time-averaging of held object's velocity. 1.0 to disable. +var NEAR_PICK_MAX_DISTANCE = 0.3; // max length of pick-ray for close grabbing to be selected +var RELEASE_VELOCITY_MULTIPLIER = 1.5; // affects throwing things +var PICK_BACKOFF_DISTANCE = 0.2; // helps when hand is intersecting the grabble object +var NEAR_GRABBING_KINEMATIC = true; // force objects to be kinematic when near-grabbed + +// +// equip +// + +var EQUIP_SPRING_SHUTOFF_DISTANCE = 0.05; +var EQUIP_SPRING_TIMEFRAME = 0.4; // how quickly objects move to their new position + +// +// other constants +// + +var RIGHT_HAND = 1; +var LEFT_HAND = 0; + +var ZERO_VEC = { + x: 0, + y: 0, + z: 0 +}; + +var NULL_ACTION_ID = "{00000000-0000-0000-000000000000}"; +var MSEC_PER_SEC = 1000.0; + +// these control how long an abandoned pointer line or action will hang around +var LIFETIME = 10; +var ACTION_TTL = 15; // seconds +var ACTION_TTL_REFRESH = 5; +var PICKS_PER_SECOND_PER_HAND = 5; +var MSECS_PER_SEC = 1000.0; +var GRABBABLE_PROPERTIES = [ + "position", + "rotation", + "gravity", + "ignoreForCollisions", + "collisionsWillMove", + "locked", + "name" +]; + +var GRABBABLE_DATA_KEY = "grabbableKey"; // shared with grab.js +var GRAB_USER_DATA_KEY = "grabKey"; // shared with grab.js + +var DEFAULT_GRABBABLE_DATA = { + grabbable: true, + invertSolidWhileHeld: false +}; + +//we've created various ways of visualizing looking for and moving distant objects +var USE_ENTITY_LINES_FOR_SEARCHING = false; +var USE_OVERLAY_LINES_FOR_SEARCHING = true; +var USE_PARTICLE_BEAM_FOR_SEARCHING = false; + +var USE_ENTITY_LINES_FOR_MOVING = true; +var USE_OVERLAY_LINES_FOR_MOVING = false; +var USE_PARTICLE_BEAM_FOR_MOVING = false; + +var USE_SPOTLIGHT = false; +var USE_POINTLIGHT = true; + +// states for the state machine +var STATE_OFF = 0; +var STATE_SEARCHING = 1; +var STATE_DISTANCE_HOLDING = 2; +var STATE_CONTINUE_DISTANCE_HOLDING = 3; +var STATE_NEAR_GRABBING = 4; +var STATE_CONTINUE_NEAR_GRABBING = 5; +var STATE_NEAR_TRIGGER = 6; +var STATE_CONTINUE_NEAR_TRIGGER = 7; +var STATE_FAR_TRIGGER = 8; +var STATE_CONTINUE_FAR_TRIGGER = 9; +var STATE_RELEASE = 10; +var STATE_EQUIP_SEARCHING = 11; +var STATE_EQUIP = 12 +var STATE_CONTINUE_EQUIP_BD = 13; // equip while bumper is still held down +var STATE_CONTINUE_EQUIP = 14; +var STATE_WAITING_FOR_BUMPER_RELEASE = 15; +var STATE_EQUIP_SPRING = 16; + + + +function stateToName(state) { + switch (state) { + case STATE_OFF: + return "off"; + case STATE_SEARCHING: + return "searching"; + case STATE_DISTANCE_HOLDING: + return "distance_holding"; + case STATE_CONTINUE_DISTANCE_HOLDING: + return "continue_distance_holding"; + case STATE_NEAR_GRABBING: + return "near_grabbing"; + case STATE_CONTINUE_NEAR_GRABBING: + return "continue_near_grabbing"; + case STATE_NEAR_TRIGGER: + return "near_trigger"; + case STATE_CONTINUE_NEAR_TRIGGER: + return "continue_near_trigger"; + case STATE_FAR_TRIGGER: + return "far_trigger"; + case STATE_CONTINUE_FAR_TRIGGER: + return "continue_far_trigger"; + case STATE_RELEASE: + return "release"; + case STATE_EQUIP_SEARCHING: + return "equip_searching"; + case STATE_EQUIP: + return "equip"; + case STATE_CONTINUE_EQUIP_BD: + return "continue_equip_bd"; + case STATE_CONTINUE_EQUIP: + return "continue_equip"; + case STATE_WAITING_FOR_BUMPER_RELEASE: + return "waiting_for_bumper_release"; + case STATE_EQUIP_SPRING: + return "state_equip_spring"; + } + + return "unknown"; +} + +function getTag() { + return "grab-" + MyAvatar.sessionUUID; +} + +function entityIsGrabbedByOther(entityID) { + // by convention, a distance grab sets the tag of its action to be grab-*owner-session-id*. + var actionIDs = Entities.getActionIDs(entityID); + for (var actionIndex = 0; actionIndex < actionIDs.length; actionIndex++) { + var actionID = actionIDs[actionIndex]; + var actionArguments = Entities.getActionArguments(entityID, actionID); + var tag = actionArguments["tag"]; + if (tag == getTag()) { + // we see a grab-*uuid* shaped tag, but it's our tag, so that's okay. + continue; + } + if (tag.slice(0, 5) == "grab-") { + // we see a grab-*uuid* shaped tag and it's not ours, so someone else is grabbing it. + return true; + } + } + return false; +} + +function getSpatialOffsetPosition(hand, spatialKey) { + var position = Vec3.ZERO; + + if (hand !== RIGHT_HAND && spatialKey.leftRelativePosition) { + position = spatialKey.leftRelativePosition; + } + if (hand === RIGHT_HAND && spatialKey.rightRelativePosition) { + position = spatialKey.rightRelativePosition; + } + if (spatialKey.relativePosition) { + position = spatialKey.relativePosition; + } + + return position; +} + +var yFlip = Quat.angleAxis(180, Vec3.UNIT_Y); + +function getSpatialOffsetRotation(hand, spatialKey) { + var rotation = Quat.IDENTITY; + + if (hand !== RIGHT_HAND && spatialKey.leftRelativeRotation) { + rotation = spatialKey.leftRelativeRotation; + } + if (hand === RIGHT_HAND && spatialKey.rightRelativeRotation) { + rotation = spatialKey.rightRelativeRotation; + } + if (spatialKey.relativeRotation) { + rotation = spatialKey.relativeRotation; + } + + // Flip left hand + if (hand !== RIGHT_HAND) { + rotation = Quat.multiply(yFlip, rotation); + } + + return rotation; +} + +function MyController(hand) { + this.hand = hand; + if (this.hand === RIGHT_HAND) { + this.getHandPosition = MyAvatar.getRightPalmPosition; + this.getHandRotation = MyAvatar.getRightPalmRotation; + } else { + this.getHandPosition = MyAvatar.getLeftPalmPosition; + this.getHandRotation = MyAvatar.getLeftPalmRotation; + } + + var SPATIAL_CONTROLLERS_PER_PALM = 2; + var TIP_CONTROLLER_OFFSET = 1; + this.palm = SPATIAL_CONTROLLERS_PER_PALM * hand; + this.tip = SPATIAL_CONTROLLERS_PER_PALM * hand + TIP_CONTROLLER_OFFSET; + + this.actionID = null; // action this script created... + this.grabbedEntity = null; // on this entity. + this.state = STATE_OFF; + this.pointer = null; // entity-id of line object + this.triggerValue = 0; // rolling average of trigger value + this.rawTriggerValue = 0; + this.rawBumperValue = 0; + + //for visualizations + this.overlayLine = null; + this.particleBeam = null; + + //for lights + this.spotlight = null; + this.pointlight = null; + + this.ignoreIK = false; + this.offsetPosition = Vec3.ZERO; + this.offsetRotation = Quat.IDENTITY; + + var _this = this; + + this.update = function() { + + this.updateSmoothedTrigger(); + + switch (this.state) { + case STATE_OFF: + this.off(); + this.touchTest(); + break; + case STATE_SEARCHING: + this.search(); + break; + case STATE_EQUIP_SEARCHING: + this.search(); + break; + case STATE_DISTANCE_HOLDING: + this.distanceHolding(); + break; + case STATE_CONTINUE_DISTANCE_HOLDING: + this.continueDistanceHolding(); + break; + case STATE_NEAR_GRABBING: + case STATE_EQUIP: + this.nearGrabbing(); + break; + case STATE_WAITING_FOR_BUMPER_RELEASE: + this.waitingForBumperRelease(); + break; + case STATE_EQUIP_SPRING: + this.pullTowardEquipPosition() + break; + case STATE_CONTINUE_NEAR_GRABBING: + case STATE_CONTINUE_EQUIP_BD: + case STATE_CONTINUE_EQUIP: + this.continueNearGrabbing(); + break; + case STATE_NEAR_TRIGGER: + this.nearTrigger(); + break; + case STATE_CONTINUE_NEAR_TRIGGER: + this.continueNearTrigger(); + break; + case STATE_FAR_TRIGGER: + this.farTrigger(); + break; + case STATE_CONTINUE_FAR_TRIGGER: + this.continueFarTrigger(); + break; + case STATE_RELEASE: + this.release(); + break; + } + }; + + this.setState = function(newState) { + if (WANT_DEBUG) { + print("STATE: " + stateToName(this.state) + " --> " + stateToName(newState) + ", hand: " + this.hand); + } + this.state = newState; + }; + + this.debugLine = function(closePoint, farPoint, color) { + Entities.addEntity({ + type: "Line", + name: "Grab Debug Entity", + dimensions: LINE_ENTITY_DIMENSIONS, + visible: true, + position: closePoint, + linePoints: [ZERO_VEC, farPoint], + color: color, + lifetime: 0.1, + collisionsWillMove: false, + ignoreForCollisions: true, + userData: JSON.stringify({ + grabbableKey: { + grabbable: false + } + }) + }); + }; + + this.lineOn = function(closePoint, farPoint, color) { + // draw a line + if (this.pointer === null) { + this.pointer = Entities.addEntity({ + type: "Line", + name: "grab pointer", + dimensions: LINE_ENTITY_DIMENSIONS, + visible: true, + position: closePoint, + linePoints: [ZERO_VEC, farPoint], + color: color, + lifetime: LIFETIME, + collisionsWillMove: false, + ignoreForCollisions: true, + userData: JSON.stringify({ + grabbableKey: { + grabbable: false + } + }) + }); + } else { + var age = Entities.getEntityProperties(this.pointer, "age").age; + this.pointer = Entities.editEntity(this.pointer, { + position: closePoint, + linePoints: [ZERO_VEC, farPoint], + color: color, + lifetime: age + LIFETIME + }); + } + }; + + this.overlayLineOn = function(closePoint, farPoint, color) { + if (this.overlayLine === null) { + var lineProperties = { + lineWidth: 5, + start: closePoint, + end: farPoint, + color: color, + ignoreRayIntersection: true, // always ignore this + visible: true, + alpha: 1 + }; + + this.overlayLine = Overlays.addOverlay("line3d", lineProperties); + + } else { + var success = Overlays.editOverlay(this.overlayLine, { + lineWidth: 5, + start: closePoint, + end: farPoint, + color: color, + visible: true, + ignoreRayIntersection: true, // always ignore this + alpha: 1 + }); + } + }; + + this.handleParticleBeam = function(position, orientation, color) { + + var rotation = Quat.angleAxis(0, { + x: 1, + y: 0, + z: 0 + }); + + var finalRotation = Quat.multiply(orientation, rotation); + + if (this.particleBeam === null) { + this.createParticleBeam(position, finalRotation, color); + } else { + this.updateParticleBeam(position, finalRotation, color); + } + }; + + this.handleDistantParticleBeam = function(handPosition, objectPosition, objectRotation, color) { + + var handToObject = Vec3.subtract(objectPosition, handPosition); + var finalRotation = Quat.rotationBetween(Vec3.multiply(-1, Vec3.UP), handToObject); + + var distance = Vec3.distance(handPosition, objectPosition); + var speed = distance * 1; + + var lifepsan = distance / speed; + var lifespan = 1; + + if (this.particleBeam === null) { + this.createParticleBeam(objectPosition, finalRotation, color, speed); + } else { + this.updateParticleBeam(objectPosition, finalRotation, color, speed, lifepsan); + } + }; + + this.createParticleBeam = function(position, orientation, color, speed, lifepsan) { + + var particleBeamProperties = { + type: "ParticleEffect", + isEmitting: true, + position: position, + visible: false, + //rotation:Quat.fromPitchYawRollDegrees(-90.0, 0.0, 0.0), + "name": "Particle Beam", + "color": color, + "maxParticles": 2000, + "lifespan": LINE_LENGTH / 10, + "emitRate": 50, + "emitSpeed": 5, + "speedSpread": 2, + "emitOrientation": { + "x": -1, + "y": 0, + "z": 0, + "w": 1 + }, + "emitDimensions": { + "x": 0, + "y": 0, + "z": 0 + }, + "emitRadiusStart": 0.5, + "polarStart": 0, + "polarFinish": 0, + "azimuthStart": -3.1415927410125732, + "azimuthFinish": 3.1415927410125732, + "emitAcceleration": { + x: 0, + y: 0, + z: 0 + }, + "accelerationSpread": { + "x": 0, + "y": 0, + "z": 0 + }, + "particleRadius": 0.01, + "radiusSpread": 0, + // "radiusStart": 0.01, + // "radiusFinish": 0.01, + // "colorSpread": { + // "red": 0, + // "green": 0, + // "blue": 0 + // }, + // "colorStart": color, + // "colorFinish": color, + "alpha": 1, + "alphaSpread": 0, + "alphaStart": 1, + "alphaFinish": 1, + "additiveBlending": 1, + "textures": "https://hifi-content.s3.amazonaws.com/alan/dev/textures/grabsprite-3.png" + } + + this.particleBeam = Entities.addEntity(particleBeamProperties); + }; + + this.updateParticleBeam = function(position, orientation, color, speed, lifepsan) { + + Entities.editEntity(this.particleBeam, { + rotation: orientation, + position: position, + visible: true, + color: color, + emitSpeed: speed, + lifepsan: lifepsan + + }) + + }; + + this.evalLightWorldTransform = function(modelPos, modelRot) { + + var MODEL_LIGHT_POSITION = { + x: 0, + y: -0.3, + z: 0 + }; + + var MODEL_LIGHT_ROTATION = Quat.angleAxis(-90, { + x: 1, + y: 0, + z: 0 + }); + + return { + p: Vec3.sum(modelPos, Vec3.multiplyQbyV(modelRot, MODEL_LIGHT_POSITION)), + q: Quat.multiply(modelRot, MODEL_LIGHT_ROTATION) + }; + }; + + this.handleSpotlight = function(parentID, position) { + var LIFETIME = 100; + + var modelProperties = Entities.getEntityProperties(parentID, ['position', 'rotation']); + + var lightTransform = this.evalLightWorldTransform(modelProperties.position, modelProperties.rotation); + var lightProperties = { + type: "Light", + isSpotlight: true, + dimensions: { + x: 2, + y: 2, + z: 20 + }, + parentID: parentID, + color: { + red: 255, + green: 255, + blue: 255 + }, + intensity: 2, + exponent: 0.3, + cutoff: 20, + lifetime: LIFETIME, + position: lightTransform.p, + }; + + if (this.spotlight === null) { + this.spotlight = Entities.addEntity(lightProperties); + } else { + Entities.editEntity(this.spotlight, { + //without this, this light would maintain rotation with its parent + rotation: Quat.fromPitchYawRollDegrees(-90, 0, 0), + }) + } + }; + + this.handlePointLight = function(parentID, position) { + var LIFETIME = 100; + + var modelProperties = Entities.getEntityProperties(parentID, ['position', 'rotation']); + var lightTransform = this.evalLightWorldTransform(modelProperties.position, modelProperties.rotation); + + var lightProperties = { + type: "Light", + isSpotlight: false, + dimensions: { + x: 2, + y: 2, + z: 20 + }, + parentID: parentID, + color: { + red: 255, + green: 255, + blue: 255 + }, + intensity: 2, + exponent: 0.3, + cutoff: 20, + lifetime: LIFETIME, + position: lightTransform.p, + }; + + if (this.pointlight === null) { + this.pointlight = Entities.addEntity(lightProperties); + } else { + + } + }; + + this.lineOff = function() { + if (this.pointer !== null) { + Entities.deleteEntity(this.pointer); + } + this.pointer = null; + }; + + this.overlayLineOff = function() { + if (this.overlayLine !== null) { + Overlays.deleteOverlay(this.overlayLine); + } + this.overlayLine = null; + }; + + this.particleBeamOff = function() { + if (this.particleBeam !== null) { + Entities.editEntity(this.particleBeam, { + visible: false + }) + } + } + + this.turnLightsOff = function() { + if (this.spotlight !== null) { + Entities.deleteEntity(this.spotlight); + this.spotlight = null; + } + + if (this.pointlight !== null) { + Entities.deleteEntity(this.pointlight); + this.pointlight = null; + } + }; + + + this.turnOffVisualizations = function() { + if (USE_ENTITY_LINES_FOR_SEARCHING === true || USE_ENTITY_LINES_FOR_MOVING === true) { + this.lineOff(); + } + + if (USE_OVERLAY_LINES_FOR_SEARCHING === true || USE_OVERLAY_LINES_FOR_MOVING === true) { + this.overlayLineOff(); + } + + if (USE_PARTICLE_BEAM_FOR_SEARCHING === true || USE_PARTICLE_BEAM_FOR_MOVING === true) { + this.particleBeamOff(); + } + }; + + this.triggerPress = function(value) { + _this.rawTriggerValue = value; + }; + + this.bumperPress = function(value) { + _this.rawBumperValue = value; + }; + + this.updateSmoothedTrigger = function() { + var triggerValue = this.rawTriggerValue; + // smooth out trigger value + this.triggerValue = (this.triggerValue * TRIGGER_SMOOTH_RATIO) + + (triggerValue * (1.0 - TRIGGER_SMOOTH_RATIO)); + }; + + this.triggerSmoothedSqueezed = function() { + return this.triggerValue > TRIGGER_ON_VALUE; + }; + + this.triggerSmoothedReleased = function() { + return this.triggerValue < TRIGGER_OFF_VALUE; + }; + + this.triggerSqueezed = function() { + var triggerValue = this.rawTriggerValue; + return triggerValue > TRIGGER_ON_VALUE; + }; + + this.bumperSqueezed = function() { + return _this.rawBumperValue > BUMPER_ON_VALUE; + }; + + this.bumperReleased = function() { + return _this.rawBumperValue < BUMPER_ON_VALUE; + }; + + this.off = function() { + if (this.triggerSmoothedSqueezed()) { + this.lastPickTime = 0; + this.setState(STATE_SEARCHING); + return; + } + if (this.bumperSqueezed()) { + this.lastPickTime = 0; + this.setState(STATE_EQUIP_SEARCHING); + return; + } + }; + + this.search = function() { + this.grabbedEntity = null; + + if (this.state == STATE_SEARCHING ? this.triggerSmoothedReleased() : this.bumperReleased()) { + this.setState(STATE_RELEASE); + return; + } + + // the trigger is being pressed, do a ray test + var handPosition = this.getHandPosition(); + var distantPickRay = { + origin: handPosition, + direction: Quat.getUp(this.getHandRotation()), + length: PICK_MAX_DISTANCE + }; + + // don't pick 60x per second. + var pickRays = []; + var now = Date.now(); + if (now - this.lastPickTime > MSECS_PER_SEC / PICKS_PER_SECOND_PER_HAND) { + pickRays = [distantPickRay]; + this.lastPickTime = now; + } + + for (var index = 0; index < pickRays.length; ++index) { + var pickRay = pickRays[index]; + var directionNormalized = Vec3.normalize(pickRay.direction); + var directionBacked = Vec3.multiply(directionNormalized, PICK_BACKOFF_DISTANCE); + var pickRayBacked = { + origin: Vec3.subtract(pickRay.origin, directionBacked), + direction: pickRay.direction + }; + + if (WANT_DEBUG) { + this.debugLine(pickRayBacked.origin, Vec3.multiply(pickRayBacked.direction, NEAR_PICK_MAX_DISTANCE), { + red: 0, + green: 255, + blue: 0 + }) + } + + var intersection = Entities.findRayIntersection(pickRayBacked, true); + + if (intersection.intersects) { + // the ray is intersecting something we can move. + var intersectionDistance = Vec3.distance(pickRay.origin, intersection.intersection); + + var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, intersection.entityID, DEFAULT_GRABBABLE_DATA); + + if (intersection.properties.name == "Grab Debug Entity") { + continue; + } + + if (typeof grabbableData.grabbable !== 'undefined' && !grabbableData.grabbable) { + continue; + } + if (intersectionDistance > pickRay.length) { + // too far away for this ray. + continue; + } + if (intersectionDistance <= NEAR_PICK_MAX_DISTANCE) { + // the hand is very close to the intersected object. go into close-grabbing mode. + if (grabbableData.wantsTrigger) { + this.grabbedEntity = intersection.entityID; + this.setState(STATE_NEAR_TRIGGER); + return; + } else if (!intersection.properties.locked) { + this.grabbedEntity = intersection.entityID; + if (this.state == STATE_SEARCHING) { + this.setState(STATE_NEAR_GRABBING); + } else { // equipping + if (typeof grabbableData.spatialKey !== 'undefined') { + // TODO + // if we go to STATE_EQUIP_SPRING the item will be pulled to the hand and will then switch + // to STATE_EQUIP. This needs some debugging, so just jump straight to STATE_EQUIP here. + // this.setState(STATE_EQUIP_SPRING); + this.setState(STATE_EQUIP); + } else { + this.setState(STATE_EQUIP); + } + } + return; + } + } else if (!entityIsGrabbedByOther(intersection.entityID)) { + // don't allow two people to distance grab the same object + if (intersection.properties.collisionsWillMove && !intersection.properties.locked) { + // the hand is far from the intersected object. go into distance-holding mode + this.grabbedEntity = intersection.entityID; + if (typeof grabbableData.spatialKey !== 'undefined' && this.state == STATE_EQUIP_SEARCHING) { + // if a distance pick in equip mode hits something with a spatialKey, equip it + // TODO use STATE_EQUIP_SPRING here once it works right. + // this.setState(STATE_EQUIP_SPRING); + this.setState(STATE_EQUIP); + return; + } else if (this.state == STATE_SEARCHING) { + this.setState(STATE_DISTANCE_HOLDING); + return; + } + } else if (grabbableData.wantsTrigger) { + this.grabbedEntity = intersection.entityID; + this.setState(STATE_FAR_TRIGGER); + return; + } + } + } + } + + // forward ray test failed, try sphere test. + if (WANT_DEBUG) { + Entities.addEntity({ + type: "Sphere", + name: "Grab Debug Entity", + dimensions: { + x: GRAB_RADIUS, + y: GRAB_RADIUS, + z: GRAB_RADIUS + }, + visible: true, + position: handPosition, + color: { + red: 0, + green: 255, + blue: 0 + }, + lifetime: 0.1, + collisionsWillMove: false, + ignoreForCollisions: true, + userData: JSON.stringify({ + grabbableKey: { + grabbable: false + } + }) + }); + } + + var nearbyEntities = Entities.findEntities(handPosition, GRAB_RADIUS); + var minDistance = PICK_MAX_DISTANCE; + var i, props, distance, grabbableData; + this.grabbedEntity = null; + for (i = 0; i < nearbyEntities.length; i++) { + var grabbableDataForCandidate = + getEntityCustomData(GRABBABLE_DATA_KEY, nearbyEntities[i], DEFAULT_GRABBABLE_DATA); + if (typeof grabbableDataForCandidate.grabbable !== 'undefined' && !grabbableDataForCandidate.grabbable) { + continue; + } + var propsForCandidate = Entities.getEntityProperties(nearbyEntities[i], GRABBABLE_PROPERTIES); + + if (propsForCandidate.type == 'Unknown') { + continue; + } + + if (propsForCandidate.type == 'Light') { + continue; + } + + if (propsForCandidate.type == 'ParticleEffect') { + continue; + } + + if (propsForCandidate.type == 'PolyLine') { + continue; + } + + if (propsForCandidate.type == 'Zone') { + continue; + } + + if (propsForCandidate.locked && !grabbableDataForCandidate.wantsTrigger) { + continue; + } + + if (propsForCandidate.name == "Grab Debug Entity") { + continue; + } + + if (propsForCandidate.name == "grab pointer") { + continue; + } + + distance = Vec3.distance(propsForCandidate.position, handPosition); + if (distance < minDistance) { + this.grabbedEntity = nearbyEntities[i]; + minDistance = distance; + props = propsForCandidate; + grabbableData = grabbableDataForCandidate; + } + } + if (this.grabbedEntity !== null) { + if (grabbableData.wantsTrigger) { + this.setState(STATE_NEAR_TRIGGER); + return; + } else if (!props.locked && props.collisionsWillMove) { + this.setState(this.state == STATE_SEARCHING ? STATE_NEAR_GRABBING : STATE_EQUIP) + return; + } + } + + //search line visualizations + if (USE_ENTITY_LINES_FOR_SEARCHING === true) { + this.lineOn(distantPickRay.origin, Vec3.multiply(distantPickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); + } + + if (USE_OVERLAY_LINES_FOR_SEARCHING === true) { + this.overlayLineOn(distantPickRay.origin, Vec3.sum(distantPickRay.origin, Vec3.multiply(distantPickRay.direction, LINE_LENGTH)), NO_INTERSECT_COLOR); + } + + if (USE_PARTICLE_BEAM_FOR_SEARCHING === true) { + this.handleParticleBeam(distantPickRay.origin, this.getHandRotation(), NO_INTERSECT_COLOR); + } + + }; + + this.distanceHolding = function() { + var handControllerPosition = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandPosition : MyAvatar.leftHandPosition; + var controllerHandInput = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; + var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getPoseValue(controllerHandInput).rotation); + var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); + var now = Date.now(); + + // add the action and initialize some variables + this.currentObjectPosition = grabbedProperties.position; + this.currentObjectRotation = grabbedProperties.rotation; + this.currentObjectTime = now; + this.handRelativePreviousPosition = Vec3.subtract(handControllerPosition, MyAvatar.position); + this.handPreviousRotation = handRotation; + this.currentCameraOrientation = Camera.orientation; + + // compute a constant based on the initial conditions which we use below to exagerate hand motion onto the held object + this.radiusScalar = Math.log(Vec3.distance(this.currentObjectPosition, handControllerPosition) + 1.0); + if (this.radiusScalar < 1.0) { + this.radiusScalar = 1.0; + } + + this.actionID = NULL_ACTION_ID; + this.actionID = Entities.addAction("spring", this.grabbedEntity, { + targetPosition: this.currentObjectPosition, + linearTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME, + targetRotation: this.currentObjectRotation, + angularTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME, + tag: getTag(), + ttl: ACTION_TTL + }); + if (this.actionID === NULL_ACTION_ID) { + this.actionID = null; + } + this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC); + + if (this.actionID !== null) { + this.setState(STATE_CONTINUE_DISTANCE_HOLDING); + this.activateEntity(this.grabbedEntity, grabbedProperties); + if (this.hand === RIGHT_HAND) { + Entities.callEntityMethod(this.grabbedEntity, "setRightHand"); + } else { + Entities.callEntityMethod(this.grabbedEntity, "setLeftHand"); + } + Entities.callEntityMethod(this.grabbedEntity, "setHand", [this.hand]); + Entities.callEntityMethod(this.grabbedEntity, "startDistantGrab"); + } + + this.currentAvatarPosition = MyAvatar.position; + this.currentAvatarOrientation = MyAvatar.orientation; + + this.turnOffVisualizations(); + }; + + this.continueDistanceHolding = function() { + if (this.triggerSmoothedReleased()) { + this.setState(STATE_RELEASE); + Entities.callEntityMethod(this.grabbedEntity, "releaseGrab"); + return; + } + + var handPosition = this.getHandPosition(); + var handControllerPosition = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandPosition : MyAvatar.leftHandPosition; + var controllerHandInput = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; + var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getPoseValue(controllerHandInput).rotation); + var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); + var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); + + if (this.state == STATE_CONTINUE_DISTANCE_HOLDING && this.bumperSqueezed() && + typeof grabbableData.spatialKey !== 'undefined') { + var saveGrabbedID = this.grabbedEntity; + this.release(); + this.setState(STATE_EQUIP); + this.grabbedEntity = saveGrabbedID; + return; + } + + + // the action was set up on a previous call. update the targets. + var radius = Vec3.distance(this.currentObjectPosition, handControllerPosition) * + this.radiusScalar * DISTANCE_HOLDING_RADIUS_FACTOR; + if (radius < 1.0) { + radius = 1.0; + } + + // how far did avatar move this timestep? + var currentPosition = MyAvatar.position; + var avatarDeltaPosition = Vec3.subtract(currentPosition, this.currentAvatarPosition); + this.currentAvatarPosition = currentPosition; + + // How far did the avatar turn this timestep? + // Note: The following code is too long because we need a Quat.quatBetween() function + // that returns the minimum quaternion between two quaternions. + var currentOrientation = MyAvatar.orientation; + if (Quat.dot(currentOrientation, this.currentAvatarOrientation) < 0.0) { + var negativeCurrentOrientation = { + x: -currentOrientation.x, + y: -currentOrientation.y, + z: -currentOrientation.z, + w: -currentOrientation.w + }; + var avatarDeltaOrientation = Quat.multiply(negativeCurrentOrientation, Quat.inverse(this.currentAvatarOrientation)); + } else { + var avatarDeltaOrientation = Quat.multiply(currentOrientation, Quat.inverse(this.currentAvatarOrientation)); + } + var handToAvatar = Vec3.subtract(handControllerPosition, this.currentAvatarPosition); + var objectToAvatar = Vec3.subtract(this.currentObjectPosition, this.currentAvatarPosition); + var handMovementFromTurning = Vec3.subtract(Quat.multiply(avatarDeltaOrientation, handToAvatar), handToAvatar); + var objectMovementFromTurning = Vec3.subtract(Quat.multiply(avatarDeltaOrientation, objectToAvatar), objectToAvatar); + this.currentAvatarOrientation = currentOrientation; + + // how far did hand move this timestep? + var handMoved = Vec3.subtract(handToAvatar, this.handRelativePreviousPosition); + this.handRelativePreviousPosition = handToAvatar; + + // magnify the hand movement but not the change from avatar movement & rotation + handMoved = Vec3.subtract(handMoved, handMovementFromTurning); + var superHandMoved = Vec3.multiply(handMoved, radius); + + // Move the object by the magnified amount and then by amount from avatar movement & rotation + var newObjectPosition = Vec3.sum(this.currentObjectPosition, superHandMoved); + newObjectPosition = Vec3.sum(newObjectPosition, avatarDeltaPosition); + newObjectPosition = Vec3.sum(newObjectPosition, objectMovementFromTurning); + + var deltaPosition = Vec3.subtract(newObjectPosition, this.currentObjectPosition); // meters + var now = Date.now(); + var deltaTime = (now - this.currentObjectTime) / MSEC_PER_SEC; // convert to seconds + + this.currentObjectPosition = newObjectPosition; + this.currentObjectTime = now; + + // this doubles hand rotation + var handChange = Quat.multiply(Quat.slerp(this.handPreviousRotation, + handRotation, + DISTANCE_HOLDING_ROTATION_EXAGGERATION_FACTOR), + Quat.inverse(this.handPreviousRotation)); + this.handPreviousRotation = handRotation; + this.currentObjectRotation = Quat.multiply(handChange, this.currentObjectRotation); + + Entities.callEntityMethod(this.grabbedEntity, "continueDistantGrab"); + + // mix in head motion + if (MOVE_WITH_HEAD) { + var objDistance = Vec3.length(objectToAvatar); + var before = Vec3.multiplyQbyV(this.currentCameraOrientation, { + x: 0.0, + y: 0.0, + z: objDistance + }); + var after = Vec3.multiplyQbyV(Camera.orientation, { + x: 0.0, + y: 0.0, + z: objDistance + }); + var change = Vec3.subtract(before, after); + this.currentCameraOrientation = Camera.orientation; + this.currentObjectPosition = Vec3.sum(this.currentObjectPosition, change); + } + + + //visualizations + if (USE_ENTITY_LINES_FOR_MOVING === true) { + this.lineOn(handPosition, Vec3.subtract(grabbedProperties.position, handPosition), INTERSECT_COLOR); + } + if (USE_OVERLAY_LINES_FOR_MOVING === true) { + this.overlayLineOn(handPosition, grabbedProperties.position, INTERSECT_COLOR); + } + if (USE_PARTICLE_BEAM_FOR_MOVING === true) { + this.handleDistantParticleBeam(handPosition, grabbedProperties.position, this.currentObjectRotation, INTERSECT_COLOR) + } + if (USE_POINTLIGHT === true) { + this.handlePointLight(this.grabbedEntity); + } + if (USE_SPOTLIGHT === true) { + this.handleSpotlight(this.grabbedEntity); + } + + Entities.updateAction(this.grabbedEntity, this.actionID, { + targetPosition: this.currentObjectPosition, + linearTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME, + targetRotation: this.currentObjectRotation, + angularTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME, + ttl: ACTION_TTL + }); + + this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC); + }; + + this.nearGrabbing = function() { + var now = Date.now(); + var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); + + if (this.state == STATE_NEAR_GRABBING && this.triggerSmoothedReleased()) { + this.setState(STATE_RELEASE); + Entities.callEntityMethod(this.grabbedEntity, "releaseGrab"); + return; + } + + this.turnOffVisualizations(); + + var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); + this.activateEntity(this.grabbedEntity, grabbedProperties); + if (grabbedProperties.collisionsWillMove && NEAR_GRABBING_KINEMATIC) { + Entities.editEntity(this.grabbedEntity, { + collisionsWillMove: false + }); + } + + var handRotation = this.getHandRotation(); + var handPosition = this.getHandPosition(); + + var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); + + if (this.state != STATE_NEAR_GRABBING && grabbableData.spatialKey) { + // if an object is "equipped" and has a spatialKey, use it. + this.ignoreIK = grabbableData.spatialKey.ignoreIK ? grabbableData.spatialKey.ignoreIK : false; + this.offsetPosition = getSpatialOffsetPosition(this.hand, grabbableData.spatialKey); + this.offsetRotation = getSpatialOffsetRotation(this.hand, grabbableData.spatialKey); + } else { + this.ignoreIK = false; + + var objectRotation = grabbedProperties.rotation; + this.offsetRotation = Quat.multiply(Quat.inverse(handRotation), objectRotation); + + var currentObjectPosition = grabbedProperties.position; + var offset = Vec3.subtract(currentObjectPosition, handPosition); + this.offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, this.offsetRotation)), offset); + } + + this.actionID = NULL_ACTION_ID; + this.actionID = Entities.addAction("hold", this.grabbedEntity, { + hand: this.hand === RIGHT_HAND ? "right" : "left", + timeScale: NEAR_GRABBING_ACTION_TIMEFRAME, + relativePosition: this.offsetPosition, + relativeRotation: this.offsetRotation, + ttl: ACTION_TTL, + kinematic: NEAR_GRABBING_KINEMATIC, + kinematicSetVelocity: true, + ignoreIK: this.ignoreIK + }); + if (this.actionID === NULL_ACTION_ID) { + this.actionID = null; + } else { + this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC); + if (this.state == STATE_NEAR_GRABBING) { + this.setState(STATE_CONTINUE_NEAR_GRABBING); + } else { + // equipping + Entities.callEntityMethod(this.grabbedEntity, "startEquip", [JSON.stringify(this.hand)]); + this.startHandGrasp(); + + this.setState(STATE_CONTINUE_EQUIP_BD); + } + + if (this.hand === RIGHT_HAND) { + Entities.callEntityMethod(this.grabbedEntity, "setRightHand"); + } else { + Entities.callEntityMethod(this.grabbedEntity, "setLeftHand"); + } + + Entities.callEntityMethod(this.grabbedEntity, "setHand", [this.hand]); + + Entities.callEntityMethod(this.grabbedEntity, "startNearGrab"); + + } + + this.currentHandControllerTipPosition = + (this.hand === RIGHT_HAND) ? MyAvatar.rightHandTipPosition : MyAvatar.leftHandTipPosition; + + this.currentObjectTime = Date.now(); + }; + + this.continueNearGrabbing = function() { + if (this.state == STATE_CONTINUE_NEAR_GRABBING && this.triggerSmoothedReleased()) { + this.setState(STATE_RELEASE); + Entities.callEntityMethod(this.grabbedEntity, "releaseGrab"); + return; + } + if (this.state == STATE_CONTINUE_EQUIP_BD && this.bumperReleased()) { + this.setState(STATE_CONTINUE_EQUIP); + return; + } + if (this.state == STATE_CONTINUE_EQUIP && this.bumperSqueezed()) { + this.setState(STATE_WAITING_FOR_BUMPER_RELEASE); + return; + } + if (this.state == STATE_CONTINUE_NEAR_GRABBING && this.bumperSqueezed()) { + this.setState(STATE_CONTINUE_EQUIP_BD); + Entities.callEntityMethod(this.grabbedEntity, "startEquip", [JSON.stringify(this.hand)]); + return; + } + + // Keep track of the fingertip velocity to impart when we release the object. + // Note that the idea of using a constant 'tip' velocity regardless of the + // object's actual held offset is an idea intended to make it easier to throw things: + // Because we might catch something or transfer it between hands without a good idea + // of it's actual offset, let's try imparting a velocity which is at a fixed radius + // from the palm. + + var handControllerPosition = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandPosition : MyAvatar.leftHandPosition; + var now = Date.now(); + + var deltaPosition = Vec3.subtract(handControllerPosition, this.currentHandControllerTipPosition); // meters + var deltaTime = (now - this.currentObjectTime) / MSEC_PER_SEC; // convert to seconds + + this.currentHandControllerTipPosition = handControllerPosition; + this.currentObjectTime = now; + Entities.callEntityMethod(this.grabbedEntity, "continueNearGrab"); + + if (this.state === STATE_CONTINUE_EQUIP_BD) { + Entities.callEntityMethod(this.grabbedEntity, "continueEquip"); + } + + if (this.actionTimeout - now < ACTION_TTL_REFRESH * MSEC_PER_SEC) { + // if less than a 5 seconds left, refresh the actions ttl + Entities.updateAction(this.grabbedEntity, this.actionID, { + hand: this.hand === RIGHT_HAND ? "right" : "left", + timeScale: NEAR_GRABBING_ACTION_TIMEFRAME, + relativePosition: this.offsetPosition, + relativeRotation: this.offsetRotation, + ttl: ACTION_TTL, + kinematic: NEAR_GRABBING_KINEMATIC, + kinematicSetVelocity: true, + ignoreIK: this.ignoreIK + }); + this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC); + } + }; + + this.waitingForBumperRelease = function() { + if (this.bumperReleased()) { + this.setState(STATE_RELEASE); + Entities.callEntityMethod(this.grabbedEntity, "releaseGrab"); + Entities.callEntityMethod(this.grabbedEntity, "unequip"); + this.endHandGrasp(); + + } + }; + + this.pullTowardEquipPosition = function() { + + this.turnOffVisualizations(); + + var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); + var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); + + // use a spring to pull the object to where it will be when equipped + var relativeRotation = getSpatialOffsetRotation(this.hand, grabbableData.spatialKey); + var relativePosition = getSpatialOffsetPosition(this.hand, grabbableData.spatialKey); + var ignoreIK = grabbableData.spatialKey.ignoreIK ? grabbableData.spatialKey.ignoreIK : false; + var handRotation = this.getHandRotation(); + var handPosition = this.getHandPosition(); + var targetRotation = Quat.multiply(handRotation, relativeRotation); + var offset = Vec3.multiplyQbyV(targetRotation, relativePosition); + var targetPosition = Vec3.sum(handPosition, offset); + + if (typeof this.equipSpringID === 'undefined' || + this.equipSpringID === null || + this.equipSpringID === NULL_ACTION_ID) { + this.equipSpringID = Entities.addAction("spring", this.grabbedEntity, { + targetPosition: targetPosition, + linearTimeScale: EQUIP_SPRING_TIMEFRAME, + targetRotation: targetRotation, + angularTimeScale: EQUIP_SPRING_TIMEFRAME, + ttl: ACTION_TTL, + ignoreIK: ignoreIK + }); + if (this.equipSpringID === NULL_ACTION_ID) { + this.equipSpringID = null; + this.setState(STATE_OFF); + return; + } + } else { + Entities.updateAction(this.grabbedEntity, this.equipSpringID, { + targetPosition: targetPosition, + linearTimeScale: EQUIP_SPRING_TIMEFRAME, + targetRotation: targetRotation, + angularTimeScale: EQUIP_SPRING_TIMEFRAME, + ttl: ACTION_TTL, + ignoreIK: ignoreIK + }); + } + + if (Vec3.distance(grabbedProperties.position, targetPosition) < EQUIP_SPRING_SHUTOFF_DISTANCE) { + Entities.deleteAction(this.grabbedEntity, this.equipSpringID); + this.equipSpringID = null; + this.setState(STATE_EQUIP); + } + }; + + this.nearTrigger = function() { + if (this.triggerSmoothedReleased()) { + this.setState(STATE_RELEASE); + Entities.callEntityMethod(this.grabbedEntity, "stopNearTrigger"); + return; + } + if (this.hand === RIGHT_HAND) { + Entities.callEntityMethod(this.grabbedEntity, "setRightHand"); + } else { + Entities.callEntityMethod(this.grabbedEntity, "setLeftHand"); + } + + Entities.callEntityMethod(this.grabbedEntity, "setHand", [this.hand]); + + Entities.callEntityMethod(this.grabbedEntity, "startNearTrigger"); + this.setState(STATE_CONTINUE_NEAR_TRIGGER); + }; + + this.farTrigger = function() { + if (this.triggerSmoothedReleased()) { + this.setState(STATE_RELEASE); + Entities.callEntityMethod(this.grabbedEntity, "stopFarTrigger"); + return; + } + + if (this.hand === RIGHT_HAND) { + Entities.callEntityMethod(this.grabbedEntity, "setRightHand"); + } else { + Entities.callEntityMethod(this.grabbedEntity, "setLeftHand"); + } + Entities.callEntityMethod(this.grabbedEntity, "setHand", [this.hand]); + Entities.callEntityMethod(this.grabbedEntity, "startFarTrigger"); + this.setState(STATE_CONTINUE_FAR_TRIGGER); + }; + + this.continueNearTrigger = function() { + if (this.triggerSmoothedReleased()) { + this.setState(STATE_RELEASE); + Entities.callEntityMethod(this.grabbedEntity, "stopNearTrigger"); + return; + } + + Entities.callEntityMethod(this.grabbedEntity, "continueNearTrigger"); + }; + + this.continueFarTrigger = function() { + if (this.triggerSmoothedReleased()) { + this.setState(STATE_RELEASE); + Entities.callEntityMethod(this.grabbedEntity, "stopNearTrigger"); + return; + } + + var handPosition = this.getHandPosition(); + var pickRay = { + origin: handPosition, + direction: Quat.getUp(this.getHandRotation()) + }; + + var now = Date.now(); + if (now - this.lastPickTime > MSECS_PER_SEC / PICKS_PER_SECOND_PER_HAND) { + var intersection = Entities.findRayIntersection(pickRay, true); + this.lastPickTime = now; + if (intersection.entityID != this.grabbedEntity) { + this.setState(STATE_RELEASE); + Entities.callEntityMethod(this.grabbedEntity, "stopFarTrigger"); + return; + } + } + + this.lineOn(pickRay.origin, Vec3.multiply(pickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); + Entities.callEntityMethod(this.grabbedEntity, "continueFarTrigger"); + }; + + _this.allTouchedIDs = {}; + + this.touchTest = function() { + var maxDistance = 0.05; + var leftHandPosition = MyAvatar.getLeftPalmPosition(); + var rightHandPosition = MyAvatar.getRightPalmPosition(); + var leftEntities = Entities.findEntities(leftHandPosition, maxDistance); + var rightEntities = Entities.findEntities(rightHandPosition, maxDistance); + var ids = []; + + if (leftEntities.length !== 0) { + leftEntities.forEach(function(entity) { + ids.push(entity); + }); + + } + + if (rightEntities.length !== 0) { + rightEntities.forEach(function(entity) { + ids.push(entity); + }); + } + + ids.forEach(function(id) { + + var props = Entities.getEntityProperties(id, ["boundingBox", "name"]); + if (props.name === 'pointer') { + return; + } else { + var entityMinPoint = props.boundingBox.brn; + var entityMaxPoint = props.boundingBox.tfl; + var leftIsTouching = pointInExtents(leftHandPosition, entityMinPoint, entityMaxPoint); + var rightIsTouching = pointInExtents(rightHandPosition, entityMinPoint, entityMaxPoint); + + if ((leftIsTouching || rightIsTouching) && _this.allTouchedIDs[id] === undefined) { + // we haven't been touched before, but either right or left is touching us now + _this.allTouchedIDs[id] = true; + _this.startTouch(id); + } else if ((leftIsTouching || rightIsTouching) && _this.allTouchedIDs[id]) { + // we have been touched before and are still being touched + // continue touch + _this.continueTouch(id); + } else if (_this.allTouchedIDs[id]) { + delete _this.allTouchedIDs[id]; + _this.stopTouch(id); + + } else { + //we are in another state + return; + } + } + + }); + + }; + + this.startTouch = function(entityID) { + Entities.callEntityMethod(entityID, "startTouch"); + }; + + this.continueTouch = function(entityID) { + Entities.callEntityMethod(entityID, "continueTouch"); + }; + + this.stopTouch = function(entityID) { + Entities.callEntityMethod(entityID, "stopTouch"); + }; + + this.release = function() { + + this.turnLightsOff(); + this.turnOffVisualizations(); + + if (this.grabbedEntity !== null) { + if (this.actionID !== null) { + Entities.deleteAction(this.grabbedEntity, this.actionID); + } + } + + this.deactivateEntity(this.grabbedEntity); + + this.grabbedEntity = null; + this.actionID = null; + this.setState(STATE_OFF); + }; + + this.cleanup = function() { + this.release(); + this.endHandGrasp(); + Entities.deleteEntity(this.particleBeam); + Entities.deleteEntity(this.spotLight); + Entities.deleteEntity(this.pointLight); + }; + + this.activateEntity = function(entityID, grabbedProperties) { + var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, entityID, DEFAULT_GRABBABLE_DATA); + var invertSolidWhileHeld = grabbableData["invertSolidWhileHeld"]; + var data = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, {}); + data["activated"] = true; + data["avatarId"] = MyAvatar.sessionUUID; + data["refCount"] = data["refCount"] ? data["refCount"] + 1 : 1; + // zero gravity and set ignoreForCollisions in a way that lets us put them back, after all grabs are done + if (data["refCount"] == 1) { + data["gravity"] = grabbedProperties.gravity; + data["ignoreForCollisions"] = grabbedProperties.ignoreForCollisions; + data["collisionsWillMove"] = grabbedProperties.collisionsWillMove; + var whileHeldProperties = { + gravity: { + x: 0, + y: 0, + z: 0 + } + }; + if (invertSolidWhileHeld) { + whileHeldProperties["ignoreForCollisions"] = !grabbedProperties.ignoreForCollisions; + } + Entities.editEntity(entityID, whileHeldProperties); + } + + setEntityCustomData(GRAB_USER_DATA_KEY, entityID, data); + return data; + }; + + this.deactivateEntity = function(entityID) { + var data = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, {}); + if (data && data["refCount"]) { + data["refCount"] = data["refCount"] - 1; + if (data["refCount"] < 1) { + Entities.editEntity(entityID, { + gravity: data["gravity"], + ignoreForCollisions: data["ignoreForCollisions"], + collisionsWillMove: data["collisionsWillMove"] + }); + data = null; + } + } else { + data = null; + } + setEntityCustomData(GRAB_USER_DATA_KEY, entityID, data); + }; + + + //this is our handler, where we do the actual work of changing animation settings + this.graspHand = function(animationProperties) { + var result = {}; + //full alpha on overlay for this hand + //set grab to true + //set idle to false + //full alpha on the blend btw open and grab + if (_this.hand === RIGHT_HAND) { + result['rightHandOverlayAlpha'] = 1.0; + result['isRightHandGrab'] = true; + result['isRightHandIdle'] = false; + result['rightHandGrabBlend'] = 1.0; + } else if (_this.hand === LEFT_HAND) { + result['leftHandOverlayAlpha'] = 1.0; + result['isLeftHandGrab'] = true; + result['isLeftHandIdle'] = false; + result['leftHandGrabBlend'] = 1.0; + } + //return an object with our updated settings + return result; + }; + + this.graspHandler = null + + this.startHandGrasp = function() { + if (this.hand === RIGHT_HAND) { + this.graspHandler = MyAvatar.addAnimationStateHandler(this.graspHand, ['isRightHandGrab']); + } else if (this.hand === LEFT_HAND) { + this.graspHandler = MyAvatar.addAnimationStateHandler(this.graspHand, ['isLeftHandGrab']); + } + }; + + this.endHandGrasp = function() { + // Tell the animation system we don't need any more callbacks. + MyAvatar.removeAnimationStateHandler(this.graspHandler); + }; + +}; + +var rightController = new MyController(RIGHT_HAND); +var leftController = new MyController(LEFT_HAND); + +//preload the particle beams so that they are full length when you start searching +if (USE_PARTICLE_BEAM_FOR_SEARCHING === true || USE_PARTICLE_BEAM_FOR_MOVING === true) { + rightController.createParticleBeam(); + leftController.createParticleBeam(); +} + +var MAPPING_NAME = "com.highfidelity.handControllerGrab"; + +var mapping = Controller.newMapping(MAPPING_NAME); +mapping.from([Controller.Standard.RT]).peek().to(rightController.triggerPress); +mapping.from([Controller.Standard.LT]).peek().to(leftController.triggerPress); + +mapping.from([Controller.Standard.RB]).peek().to(rightController.bumperPress); +mapping.from([Controller.Standard.LB]).peek().to(leftController.bumperPress); + +Controller.enableMapping(MAPPING_NAME); + +//the section below allows the grab script to listen for messages that disable either one or both hands. useful for two handed items +var handToDisable = 'none'; + +function update() { + if (handToDisable !== LEFT_HAND && handToDisable !== 'both') { + leftController.update(); + } + if (handToDisable !== RIGHT_HAND && handToDisable !== 'both') { + rightController.update(); + } +} + +Messages.subscribe('Hifi-Hand-Disabler'); + +handleHandDisablerMessages = function(channel, message, sender) { + + if (sender === MyAvatar.sessionUUID) { + if (message === 'left') { + handToDisable = LEFT_HAND; + } + if (message === 'right') { + handToDisable = RIGHT_HAND; + } + if (message === 'both') { + handToDisable = 'both'; + } + if (message === 'none') { + handToDisable = 'none'; + } + } + +} + +Messages.messageReceived.connect(handleHandDisablerMessages); + +function cleanup() { + rightController.cleanup(); + leftController.cleanup(); + Controller.disableMapping(MAPPING_NAME); +} + +Script.scriptEnding.connect(cleanup); +Script.update.connect(update); \ No newline at end of file diff --git a/examples/controllers/handControllerGrab-spotlight.js b/examples/controllers/handControllerGrab-spotlight.js new file mode 100644 index 0000000000..ea05a44f78 --- /dev/null +++ b/examples/controllers/handControllerGrab-spotlight.js @@ -0,0 +1,1649 @@ +// handControllerGrab.js +// +// Created by Eric Levin on 9/2/15 +// Additions by James B. Pollack @imgntn on 9/24/2015 +// Additions By Seth Alves on 10/20/2015 +// Copyright 2015 High Fidelity, Inc. +// +// Grabs physically moveable entities with hydra-like controllers; it works for either near or far objects. +// Also supports touch and equipping objects. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +/*global print, MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, Audio, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt, pointInExtents, vec3equal, setEntityCustomData, getEntityCustomData */ + +Script.include("../libraries/utils.js"); + +// +// add lines where the hand ray picking is happening +// +var WANT_DEBUG = false; + +// +// these tune time-averaging and "on" value for analog trigger +// + +var TRIGGER_SMOOTH_RATIO = 0.1; // 0.0 disables smoothing of trigger value +var TRIGGER_ON_VALUE = 0.4; +var TRIGGER_OFF_VALUE = 0.15; + +var BUMPER_ON_VALUE = 0.5; + +// +// distant manipulation +// + +var DISTANCE_HOLDING_RADIUS_FACTOR = 3.5; // multiplied by distance between hand and object +var DISTANCE_HOLDING_ACTION_TIMEFRAME = 0.1; // how quickly objects move to their new position +var DISTANCE_HOLDING_ROTATION_EXAGGERATION_FACTOR = 2.0; // object rotates this much more than hand did +var MOVE_WITH_HEAD = true; // experimental head-controll of distantly held objects + +var NO_INTERSECT_COLOR = { + red: 10, + green: 10, + blue: 255 +}; // line color when pick misses +var INTERSECT_COLOR = { + red: 250, + green: 10, + blue: 10 +}; // line color when pick hits +var LINE_ENTITY_DIMENSIONS = { + x: 1000, + y: 1000, + z: 1000 +}; + +var LINE_LENGTH = 500; +var PICK_MAX_DISTANCE = 500; // max length of pick-ray + +// +// near grabbing +// + +var GRAB_RADIUS = 0.03; // if the ray misses but an object is this close, it will still be selected +var NEAR_GRABBING_ACTION_TIMEFRAME = 0.05; // how quickly objects move to their new position +var NEAR_GRABBING_VELOCITY_SMOOTH_RATIO = 1.0; // adjust time-averaging of held object's velocity. 1.0 to disable. +var NEAR_PICK_MAX_DISTANCE = 0.3; // max length of pick-ray for close grabbing to be selected +var RELEASE_VELOCITY_MULTIPLIER = 1.5; // affects throwing things +var PICK_BACKOFF_DISTANCE = 0.2; // helps when hand is intersecting the grabble object +var NEAR_GRABBING_KINEMATIC = true; // force objects to be kinematic when near-grabbed + +// +// equip +// + +var EQUIP_SPRING_SHUTOFF_DISTANCE = 0.05; +var EQUIP_SPRING_TIMEFRAME = 0.4; // how quickly objects move to their new position + +// +// other constants +// + +var RIGHT_HAND = 1; +var LEFT_HAND = 0; + +var ZERO_VEC = { + x: 0, + y: 0, + z: 0 +}; + +var NULL_ACTION_ID = "{00000000-0000-0000-000000000000}"; +var MSEC_PER_SEC = 1000.0; + +// these control how long an abandoned pointer line or action will hang around +var LIFETIME = 10; +var ACTION_TTL = 15; // seconds +var ACTION_TTL_REFRESH = 5; +var PICKS_PER_SECOND_PER_HAND = 5; +var MSECS_PER_SEC = 1000.0; +var GRABBABLE_PROPERTIES = [ + "position", + "rotation", + "gravity", + "ignoreForCollisions", + "collisionsWillMove", + "locked", + "name" +]; + +var GRABBABLE_DATA_KEY = "grabbableKey"; // shared with grab.js +var GRAB_USER_DATA_KEY = "grabKey"; // shared with grab.js + +var DEFAULT_GRABBABLE_DATA = { + grabbable: true, + invertSolidWhileHeld: false +}; + +//we've created various ways of visualizing looking for and moving distant objects +var USE_ENTITY_LINES_FOR_SEARCHING = false; +var USE_OVERLAY_LINES_FOR_SEARCHING = true; +var USE_PARTICLE_BEAM_FOR_SEARCHING = false; + +var USE_ENTITY_LINES_FOR_MOVING = true; +var USE_OVERLAY_LINES_FOR_MOVING = false; +var USE_PARTICLE_BEAM_FOR_MOVING = false; + +var USE_SPOTLIGHT = true; +var USE_POINTLIGHT = false; + +// states for the state machine +var STATE_OFF = 0; +var STATE_SEARCHING = 1; +var STATE_DISTANCE_HOLDING = 2; +var STATE_CONTINUE_DISTANCE_HOLDING = 3; +var STATE_NEAR_GRABBING = 4; +var STATE_CONTINUE_NEAR_GRABBING = 5; +var STATE_NEAR_TRIGGER = 6; +var STATE_CONTINUE_NEAR_TRIGGER = 7; +var STATE_FAR_TRIGGER = 8; +var STATE_CONTINUE_FAR_TRIGGER = 9; +var STATE_RELEASE = 10; +var STATE_EQUIP_SEARCHING = 11; +var STATE_EQUIP = 12 +var STATE_CONTINUE_EQUIP_BD = 13; // equip while bumper is still held down +var STATE_CONTINUE_EQUIP = 14; +var STATE_WAITING_FOR_BUMPER_RELEASE = 15; +var STATE_EQUIP_SPRING = 16; + + + +function stateToName(state) { + switch (state) { + case STATE_OFF: + return "off"; + case STATE_SEARCHING: + return "searching"; + case STATE_DISTANCE_HOLDING: + return "distance_holding"; + case STATE_CONTINUE_DISTANCE_HOLDING: + return "continue_distance_holding"; + case STATE_NEAR_GRABBING: + return "near_grabbing"; + case STATE_CONTINUE_NEAR_GRABBING: + return "continue_near_grabbing"; + case STATE_NEAR_TRIGGER: + return "near_trigger"; + case STATE_CONTINUE_NEAR_TRIGGER: + return "continue_near_trigger"; + case STATE_FAR_TRIGGER: + return "far_trigger"; + case STATE_CONTINUE_FAR_TRIGGER: + return "continue_far_trigger"; + case STATE_RELEASE: + return "release"; + case STATE_EQUIP_SEARCHING: + return "equip_searching"; + case STATE_EQUIP: + return "equip"; + case STATE_CONTINUE_EQUIP_BD: + return "continue_equip_bd"; + case STATE_CONTINUE_EQUIP: + return "continue_equip"; + case STATE_WAITING_FOR_BUMPER_RELEASE: + return "waiting_for_bumper_release"; + case STATE_EQUIP_SPRING: + return "state_equip_spring"; + } + + return "unknown"; +} + +function getTag() { + return "grab-" + MyAvatar.sessionUUID; +} + +function entityIsGrabbedByOther(entityID) { + // by convention, a distance grab sets the tag of its action to be grab-*owner-session-id*. + var actionIDs = Entities.getActionIDs(entityID); + for (var actionIndex = 0; actionIndex < actionIDs.length; actionIndex++) { + var actionID = actionIDs[actionIndex]; + var actionArguments = Entities.getActionArguments(entityID, actionID); + var tag = actionArguments["tag"]; + if (tag == getTag()) { + // we see a grab-*uuid* shaped tag, but it's our tag, so that's okay. + continue; + } + if (tag.slice(0, 5) == "grab-") { + // we see a grab-*uuid* shaped tag and it's not ours, so someone else is grabbing it. + return true; + } + } + return false; +} + +function getSpatialOffsetPosition(hand, spatialKey) { + var position = Vec3.ZERO; + + if (hand !== RIGHT_HAND && spatialKey.leftRelativePosition) { + position = spatialKey.leftRelativePosition; + } + if (hand === RIGHT_HAND && spatialKey.rightRelativePosition) { + position = spatialKey.rightRelativePosition; + } + if (spatialKey.relativePosition) { + position = spatialKey.relativePosition; + } + + return position; +} + +var yFlip = Quat.angleAxis(180, Vec3.UNIT_Y); + +function getSpatialOffsetRotation(hand, spatialKey) { + var rotation = Quat.IDENTITY; + + if (hand !== RIGHT_HAND && spatialKey.leftRelativeRotation) { + rotation = spatialKey.leftRelativeRotation; + } + if (hand === RIGHT_HAND && spatialKey.rightRelativeRotation) { + rotation = spatialKey.rightRelativeRotation; + } + if (spatialKey.relativeRotation) { + rotation = spatialKey.relativeRotation; + } + + // Flip left hand + if (hand !== RIGHT_HAND) { + rotation = Quat.multiply(yFlip, rotation); + } + + return rotation; +} + +function MyController(hand) { + this.hand = hand; + if (this.hand === RIGHT_HAND) { + this.getHandPosition = MyAvatar.getRightPalmPosition; + this.getHandRotation = MyAvatar.getRightPalmRotation; + } else { + this.getHandPosition = MyAvatar.getLeftPalmPosition; + this.getHandRotation = MyAvatar.getLeftPalmRotation; + } + + var SPATIAL_CONTROLLERS_PER_PALM = 2; + var TIP_CONTROLLER_OFFSET = 1; + this.palm = SPATIAL_CONTROLLERS_PER_PALM * hand; + this.tip = SPATIAL_CONTROLLERS_PER_PALM * hand + TIP_CONTROLLER_OFFSET; + + this.actionID = null; // action this script created... + this.grabbedEntity = null; // on this entity. + this.state = STATE_OFF; + this.pointer = null; // entity-id of line object + this.triggerValue = 0; // rolling average of trigger value + this.rawTriggerValue = 0; + this.rawBumperValue = 0; + + //for visualizations + this.overlayLine = null; + this.particleBeam = null; + + //for lights + this.spotlight = null; + this.pointlight = null; + + this.ignoreIK = false; + this.offsetPosition = Vec3.ZERO; + this.offsetRotation = Quat.IDENTITY; + + var _this = this; + + this.update = function() { + + this.updateSmoothedTrigger(); + + switch (this.state) { + case STATE_OFF: + this.off(); + this.touchTest(); + break; + case STATE_SEARCHING: + this.search(); + break; + case STATE_EQUIP_SEARCHING: + this.search(); + break; + case STATE_DISTANCE_HOLDING: + this.distanceHolding(); + break; + case STATE_CONTINUE_DISTANCE_HOLDING: + this.continueDistanceHolding(); + break; + case STATE_NEAR_GRABBING: + case STATE_EQUIP: + this.nearGrabbing(); + break; + case STATE_WAITING_FOR_BUMPER_RELEASE: + this.waitingForBumperRelease(); + break; + case STATE_EQUIP_SPRING: + this.pullTowardEquipPosition() + break; + case STATE_CONTINUE_NEAR_GRABBING: + case STATE_CONTINUE_EQUIP_BD: + case STATE_CONTINUE_EQUIP: + this.continueNearGrabbing(); + break; + case STATE_NEAR_TRIGGER: + this.nearTrigger(); + break; + case STATE_CONTINUE_NEAR_TRIGGER: + this.continueNearTrigger(); + break; + case STATE_FAR_TRIGGER: + this.farTrigger(); + break; + case STATE_CONTINUE_FAR_TRIGGER: + this.continueFarTrigger(); + break; + case STATE_RELEASE: + this.release(); + break; + } + }; + + this.setState = function(newState) { + if (WANT_DEBUG) { + print("STATE: " + stateToName(this.state) + " --> " + stateToName(newState) + ", hand: " + this.hand); + } + this.state = newState; + }; + + this.debugLine = function(closePoint, farPoint, color) { + Entities.addEntity({ + type: "Line", + name: "Grab Debug Entity", + dimensions: LINE_ENTITY_DIMENSIONS, + visible: true, + position: closePoint, + linePoints: [ZERO_VEC, farPoint], + color: color, + lifetime: 0.1, + collisionsWillMove: false, + ignoreForCollisions: true, + userData: JSON.stringify({ + grabbableKey: { + grabbable: false + } + }) + }); + }; + + this.lineOn = function(closePoint, farPoint, color) { + // draw a line + if (this.pointer === null) { + this.pointer = Entities.addEntity({ + type: "Line", + name: "grab pointer", + dimensions: LINE_ENTITY_DIMENSIONS, + visible: true, + position: closePoint, + linePoints: [ZERO_VEC, farPoint], + color: color, + lifetime: LIFETIME, + collisionsWillMove: false, + ignoreForCollisions: true, + userData: JSON.stringify({ + grabbableKey: { + grabbable: false + } + }) + }); + } else { + var age = Entities.getEntityProperties(this.pointer, "age").age; + this.pointer = Entities.editEntity(this.pointer, { + position: closePoint, + linePoints: [ZERO_VEC, farPoint], + color: color, + lifetime: age + LIFETIME + }); + } + }; + + this.overlayLineOn = function(closePoint, farPoint, color) { + if (this.overlayLine === null) { + var lineProperties = { + lineWidth: 5, + start: closePoint, + end: farPoint, + color: color, + ignoreRayIntersection: true, // always ignore this + visible: true, + alpha: 1 + }; + + this.overlayLine = Overlays.addOverlay("line3d", lineProperties); + + } else { + var success = Overlays.editOverlay(this.overlayLine, { + lineWidth: 5, + start: closePoint, + end: farPoint, + color: color, + visible: true, + ignoreRayIntersection: true, // always ignore this + alpha: 1 + }); + } + }; + + this.handleParticleBeam = function(position, orientation, color) { + + var rotation = Quat.angleAxis(0, { + x: 1, + y: 0, + z: 0 + }); + + var finalRotation = Quat.multiply(orientation, rotation); + + if (this.particleBeam === null) { + this.createParticleBeam(position, finalRotation, color); + } else { + this.updateParticleBeam(position, finalRotation, color); + } + }; + + this.handleDistantParticleBeam = function(handPosition, objectPosition, objectRotation, color) { + + var handToObject = Vec3.subtract(objectPosition, handPosition); + var finalRotation = Quat.rotationBetween(Vec3.multiply(-1, Vec3.UP), handToObject); + + var distance = Vec3.distance(handPosition, objectPosition); + var speed = distance * 1; + + var lifepsan = distance / speed; + var lifespan = 1; + + if (this.particleBeam === null) { + this.createParticleBeam(objectPosition, finalRotation, color, speed); + } else { + this.updateParticleBeam(objectPosition, finalRotation, color, speed, lifepsan); + } + }; + + this.createParticleBeam = function(position, orientation, color, speed, lifepsan) { + + var particleBeamProperties = { + type: "ParticleEffect", + isEmitting: true, + position: position, + visible: false, + //rotation:Quat.fromPitchYawRollDegrees(-90.0, 0.0, 0.0), + "name": "Particle Beam", + "color": color, + "maxParticles": 2000, + "lifespan": LINE_LENGTH / 10, + "emitRate": 50, + "emitSpeed": 5, + "speedSpread": 2, + "emitOrientation": { + "x": -1, + "y": 0, + "z": 0, + "w": 1 + }, + "emitDimensions": { + "x": 0, + "y": 0, + "z": 0 + }, + "emitRadiusStart": 0.5, + "polarStart": 0, + "polarFinish": 0, + "azimuthStart": -3.1415927410125732, + "azimuthFinish": 3.1415927410125732, + "emitAcceleration": { + x: 0, + y: 0, + z: 0 + }, + "accelerationSpread": { + "x": 0, + "y": 0, + "z": 0 + }, + "particleRadius": 0.01, + "radiusSpread": 0, + // "radiusStart": 0.01, + // "radiusFinish": 0.01, + // "colorSpread": { + // "red": 0, + // "green": 0, + // "blue": 0 + // }, + // "colorStart": color, + // "colorFinish": color, + "alpha": 1, + "alphaSpread": 0, + "alphaStart": 1, + "alphaFinish": 1, + "additiveBlending": 1, + "textures": "https://hifi-content.s3.amazonaws.com/alan/dev/textures/grabsprite-3.png" + } + + this.particleBeam = Entities.addEntity(particleBeamProperties); + }; + + this.updateParticleBeam = function(position, orientation, color, speed, lifepsan) { + + Entities.editEntity(this.particleBeam, { + rotation: orientation, + position: position, + visible: true, + color: color, + emitSpeed: speed, + lifepsan: lifepsan + + }) + + }; + + this.evalLightWorldTransform = function(modelPos, modelRot) { + + var MODEL_LIGHT_POSITION = { + x: 0, + y: -0.3, + z: 0 + }; + + var MODEL_LIGHT_ROTATION = Quat.angleAxis(-90, { + x: 1, + y: 0, + z: 0 + }); + + return { + p: Vec3.sum(modelPos, Vec3.multiplyQbyV(modelRot, MODEL_LIGHT_POSITION)), + q: Quat.multiply(modelRot, MODEL_LIGHT_ROTATION) + }; + }; + + this.handleSpotlight = function(parentID, position) { + var LIFETIME = 100; + + var modelProperties = Entities.getEntityProperties(parentID, ['position', 'rotation']); + + var lightTransform = this.evalLightWorldTransform(modelProperties.position, modelProperties.rotation); + var lightProperties = { + type: "Light", + isSpotlight: true, + dimensions: { + x: 2, + y: 2, + z: 20 + }, + parentID: parentID, + color: { + red: 255, + green: 255, + blue: 255 + }, + intensity: 2, + exponent: 0.3, + cutoff: 20, + lifetime: LIFETIME, + position: lightTransform.p, + }; + + if (this.spotlight === null) { + this.spotlight = Entities.addEntity(lightProperties); + } else { + Entities.editEntity(this.spotlight, { + //without this, this light would maintain rotation with its parent + rotation: Quat.fromPitchYawRollDegrees(-90, 0, 0), + }) + } + }; + + this.handlePointLight = function(parentID, position) { + var LIFETIME = 100; + + var modelProperties = Entities.getEntityProperties(parentID, ['position', 'rotation']); + var lightTransform = this.evalLightWorldTransform(modelProperties.position, modelProperties.rotation); + + var lightProperties = { + type: "Light", + isSpotlight: false, + dimensions: { + x: 2, + y: 2, + z: 20 + }, + parentID: parentID, + color: { + red: 255, + green: 255, + blue: 255 + }, + intensity: 2, + exponent: 0.3, + cutoff: 20, + lifetime: LIFETIME, + position: lightTransform.p, + }; + + if (this.pointlight === null) { + this.pointlight = Entities.addEntity(lightProperties); + } else { + + } + }; + + this.lineOff = function() { + if (this.pointer !== null) { + Entities.deleteEntity(this.pointer); + } + this.pointer = null; + }; + + this.overlayLineOff = function() { + if (this.overlayLine !== null) { + Overlays.deleteOverlay(this.overlayLine); + } + this.overlayLine = null; + }; + + this.particleBeamOff = function() { + if (this.particleBeam !== null) { + Entities.editEntity(this.particleBeam, { + visible: false + }) + } + } + + this.turnLightsOff = function() { + if (this.spotlight !== null) { + Entities.deleteEntity(this.spotlight); + this.spotlight = null; + } + + if (this.pointlight !== null) { + Entities.deleteEntity(this.pointlight); + this.pointlight = null; + } + }; + + + this.turnOffVisualizations = function() { + if (USE_ENTITY_LINES_FOR_SEARCHING === true || USE_ENTITY_LINES_FOR_MOVING === true) { + this.lineOff(); + } + + if (USE_OVERLAY_LINES_FOR_SEARCHING === true || USE_OVERLAY_LINES_FOR_MOVING === true) { + this.overlayLineOff(); + } + + if (USE_PARTICLE_BEAM_FOR_SEARCHING === true || USE_PARTICLE_BEAM_FOR_MOVING === true) { + this.particleBeamOff(); + } + }; + + this.triggerPress = function(value) { + _this.rawTriggerValue = value; + }; + + this.bumperPress = function(value) { + _this.rawBumperValue = value; + }; + + this.updateSmoothedTrigger = function() { + var triggerValue = this.rawTriggerValue; + // smooth out trigger value + this.triggerValue = (this.triggerValue * TRIGGER_SMOOTH_RATIO) + + (triggerValue * (1.0 - TRIGGER_SMOOTH_RATIO)); + }; + + this.triggerSmoothedSqueezed = function() { + return this.triggerValue > TRIGGER_ON_VALUE; + }; + + this.triggerSmoothedReleased = function() { + return this.triggerValue < TRIGGER_OFF_VALUE; + }; + + this.triggerSqueezed = function() { + var triggerValue = this.rawTriggerValue; + return triggerValue > TRIGGER_ON_VALUE; + }; + + this.bumperSqueezed = function() { + return _this.rawBumperValue > BUMPER_ON_VALUE; + }; + + this.bumperReleased = function() { + return _this.rawBumperValue < BUMPER_ON_VALUE; + }; + + this.off = function() { + if (this.triggerSmoothedSqueezed()) { + this.lastPickTime = 0; + this.setState(STATE_SEARCHING); + return; + } + if (this.bumperSqueezed()) { + this.lastPickTime = 0; + this.setState(STATE_EQUIP_SEARCHING); + return; + } + }; + + this.search = function() { + this.grabbedEntity = null; + + if (this.state == STATE_SEARCHING ? this.triggerSmoothedReleased() : this.bumperReleased()) { + this.setState(STATE_RELEASE); + return; + } + + // the trigger is being pressed, do a ray test + var handPosition = this.getHandPosition(); + var distantPickRay = { + origin: handPosition, + direction: Quat.getUp(this.getHandRotation()), + length: PICK_MAX_DISTANCE + }; + + // don't pick 60x per second. + var pickRays = []; + var now = Date.now(); + if (now - this.lastPickTime > MSECS_PER_SEC / PICKS_PER_SECOND_PER_HAND) { + pickRays = [distantPickRay]; + this.lastPickTime = now; + } + + for (var index = 0; index < pickRays.length; ++index) { + var pickRay = pickRays[index]; + var directionNormalized = Vec3.normalize(pickRay.direction); + var directionBacked = Vec3.multiply(directionNormalized, PICK_BACKOFF_DISTANCE); + var pickRayBacked = { + origin: Vec3.subtract(pickRay.origin, directionBacked), + direction: pickRay.direction + }; + + if (WANT_DEBUG) { + this.debugLine(pickRayBacked.origin, Vec3.multiply(pickRayBacked.direction, NEAR_PICK_MAX_DISTANCE), { + red: 0, + green: 255, + blue: 0 + }) + } + + var intersection = Entities.findRayIntersection(pickRayBacked, true); + + if (intersection.intersects) { + // the ray is intersecting something we can move. + var intersectionDistance = Vec3.distance(pickRay.origin, intersection.intersection); + + var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, intersection.entityID, DEFAULT_GRABBABLE_DATA); + + if (intersection.properties.name == "Grab Debug Entity") { + continue; + } + + if (typeof grabbableData.grabbable !== 'undefined' && !grabbableData.grabbable) { + continue; + } + if (intersectionDistance > pickRay.length) { + // too far away for this ray. + continue; + } + if (intersectionDistance <= NEAR_PICK_MAX_DISTANCE) { + // the hand is very close to the intersected object. go into close-grabbing mode. + if (grabbableData.wantsTrigger) { + this.grabbedEntity = intersection.entityID; + this.setState(STATE_NEAR_TRIGGER); + return; + } else if (!intersection.properties.locked) { + this.grabbedEntity = intersection.entityID; + if (this.state == STATE_SEARCHING) { + this.setState(STATE_NEAR_GRABBING); + } else { // equipping + if (typeof grabbableData.spatialKey !== 'undefined') { + // TODO + // if we go to STATE_EQUIP_SPRING the item will be pulled to the hand and will then switch + // to STATE_EQUIP. This needs some debugging, so just jump straight to STATE_EQUIP here. + // this.setState(STATE_EQUIP_SPRING); + this.setState(STATE_EQUIP); + } else { + this.setState(STATE_EQUIP); + } + } + return; + } + } else if (!entityIsGrabbedByOther(intersection.entityID)) { + // don't allow two people to distance grab the same object + if (intersection.properties.collisionsWillMove && !intersection.properties.locked) { + // the hand is far from the intersected object. go into distance-holding mode + this.grabbedEntity = intersection.entityID; + if (typeof grabbableData.spatialKey !== 'undefined' && this.state == STATE_EQUIP_SEARCHING) { + // if a distance pick in equip mode hits something with a spatialKey, equip it + // TODO use STATE_EQUIP_SPRING here once it works right. + // this.setState(STATE_EQUIP_SPRING); + this.setState(STATE_EQUIP); + return; + } else if (this.state == STATE_SEARCHING) { + this.setState(STATE_DISTANCE_HOLDING); + return; + } + } else if (grabbableData.wantsTrigger) { + this.grabbedEntity = intersection.entityID; + this.setState(STATE_FAR_TRIGGER); + return; + } + } + } + } + + // forward ray test failed, try sphere test. + if (WANT_DEBUG) { + Entities.addEntity({ + type: "Sphere", + name: "Grab Debug Entity", + dimensions: { + x: GRAB_RADIUS, + y: GRAB_RADIUS, + z: GRAB_RADIUS + }, + visible: true, + position: handPosition, + color: { + red: 0, + green: 255, + blue: 0 + }, + lifetime: 0.1, + collisionsWillMove: false, + ignoreForCollisions: true, + userData: JSON.stringify({ + grabbableKey: { + grabbable: false + } + }) + }); + } + + var nearbyEntities = Entities.findEntities(handPosition, GRAB_RADIUS); + var minDistance = PICK_MAX_DISTANCE; + var i, props, distance, grabbableData; + this.grabbedEntity = null; + for (i = 0; i < nearbyEntities.length; i++) { + var grabbableDataForCandidate = + getEntityCustomData(GRABBABLE_DATA_KEY, nearbyEntities[i], DEFAULT_GRABBABLE_DATA); + if (typeof grabbableDataForCandidate.grabbable !== 'undefined' && !grabbableDataForCandidate.grabbable) { + continue; + } + var propsForCandidate = Entities.getEntityProperties(nearbyEntities[i], GRABBABLE_PROPERTIES); + + if (propsForCandidate.type == 'Unknown') { + continue; + } + + if (propsForCandidate.type == 'Light') { + continue; + } + + if (propsForCandidate.type == 'ParticleEffect') { + continue; + } + + if (propsForCandidate.type == 'PolyLine') { + continue; + } + + if (propsForCandidate.type == 'Zone') { + continue; + } + + if (propsForCandidate.locked && !grabbableDataForCandidate.wantsTrigger) { + continue; + } + + if (propsForCandidate.name == "Grab Debug Entity") { + continue; + } + + if (propsForCandidate.name == "grab pointer") { + continue; + } + + distance = Vec3.distance(propsForCandidate.position, handPosition); + if (distance < minDistance) { + this.grabbedEntity = nearbyEntities[i]; + minDistance = distance; + props = propsForCandidate; + grabbableData = grabbableDataForCandidate; + } + } + if (this.grabbedEntity !== null) { + if (grabbableData.wantsTrigger) { + this.setState(STATE_NEAR_TRIGGER); + return; + } else if (!props.locked && props.collisionsWillMove) { + this.setState(this.state == STATE_SEARCHING ? STATE_NEAR_GRABBING : STATE_EQUIP) + return; + } + } + + //search line visualizations + if (USE_ENTITY_LINES_FOR_SEARCHING === true) { + this.lineOn(distantPickRay.origin, Vec3.multiply(distantPickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); + } + + if (USE_OVERLAY_LINES_FOR_SEARCHING === true) { + this.overlayLineOn(distantPickRay.origin, Vec3.sum(distantPickRay.origin, Vec3.multiply(distantPickRay.direction, LINE_LENGTH)), NO_INTERSECT_COLOR); + } + + if (USE_PARTICLE_BEAM_FOR_SEARCHING === true) { + this.handleParticleBeam(distantPickRay.origin, this.getHandRotation(), NO_INTERSECT_COLOR); + } + + }; + + this.distanceHolding = function() { + var handControllerPosition = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandPosition : MyAvatar.leftHandPosition; + var controllerHandInput = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; + var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getPoseValue(controllerHandInput).rotation); + var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); + var now = Date.now(); + + // add the action and initialize some variables + this.currentObjectPosition = grabbedProperties.position; + this.currentObjectRotation = grabbedProperties.rotation; + this.currentObjectTime = now; + this.handRelativePreviousPosition = Vec3.subtract(handControllerPosition, MyAvatar.position); + this.handPreviousRotation = handRotation; + this.currentCameraOrientation = Camera.orientation; + + // compute a constant based on the initial conditions which we use below to exagerate hand motion onto the held object + this.radiusScalar = Math.log(Vec3.distance(this.currentObjectPosition, handControllerPosition) + 1.0); + if (this.radiusScalar < 1.0) { + this.radiusScalar = 1.0; + } + + this.actionID = NULL_ACTION_ID; + this.actionID = Entities.addAction("spring", this.grabbedEntity, { + targetPosition: this.currentObjectPosition, + linearTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME, + targetRotation: this.currentObjectRotation, + angularTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME, + tag: getTag(), + ttl: ACTION_TTL + }); + if (this.actionID === NULL_ACTION_ID) { + this.actionID = null; + } + this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC); + + if (this.actionID !== null) { + this.setState(STATE_CONTINUE_DISTANCE_HOLDING); + this.activateEntity(this.grabbedEntity, grabbedProperties); + if (this.hand === RIGHT_HAND) { + Entities.callEntityMethod(this.grabbedEntity, "setRightHand"); + } else { + Entities.callEntityMethod(this.grabbedEntity, "setLeftHand"); + } + Entities.callEntityMethod(this.grabbedEntity, "setHand", [this.hand]); + Entities.callEntityMethod(this.grabbedEntity, "startDistantGrab"); + } + + this.currentAvatarPosition = MyAvatar.position; + this.currentAvatarOrientation = MyAvatar.orientation; + + this.turnOffVisualizations(); + }; + + this.continueDistanceHolding = function() { + if (this.triggerSmoothedReleased()) { + this.setState(STATE_RELEASE); + Entities.callEntityMethod(this.grabbedEntity, "releaseGrab"); + return; + } + + var handPosition = this.getHandPosition(); + var handControllerPosition = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandPosition : MyAvatar.leftHandPosition; + var controllerHandInput = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; + var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getPoseValue(controllerHandInput).rotation); + var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); + var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); + + if (this.state == STATE_CONTINUE_DISTANCE_HOLDING && this.bumperSqueezed() && + typeof grabbableData.spatialKey !== 'undefined') { + var saveGrabbedID = this.grabbedEntity; + this.release(); + this.setState(STATE_EQUIP); + this.grabbedEntity = saveGrabbedID; + return; + } + + + // the action was set up on a previous call. update the targets. + var radius = Vec3.distance(this.currentObjectPosition, handControllerPosition) * + this.radiusScalar * DISTANCE_HOLDING_RADIUS_FACTOR; + if (radius < 1.0) { + radius = 1.0; + } + + // how far did avatar move this timestep? + var currentPosition = MyAvatar.position; + var avatarDeltaPosition = Vec3.subtract(currentPosition, this.currentAvatarPosition); + this.currentAvatarPosition = currentPosition; + + // How far did the avatar turn this timestep? + // Note: The following code is too long because we need a Quat.quatBetween() function + // that returns the minimum quaternion between two quaternions. + var currentOrientation = MyAvatar.orientation; + if (Quat.dot(currentOrientation, this.currentAvatarOrientation) < 0.0) { + var negativeCurrentOrientation = { + x: -currentOrientation.x, + y: -currentOrientation.y, + z: -currentOrientation.z, + w: -currentOrientation.w + }; + var avatarDeltaOrientation = Quat.multiply(negativeCurrentOrientation, Quat.inverse(this.currentAvatarOrientation)); + } else { + var avatarDeltaOrientation = Quat.multiply(currentOrientation, Quat.inverse(this.currentAvatarOrientation)); + } + var handToAvatar = Vec3.subtract(handControllerPosition, this.currentAvatarPosition); + var objectToAvatar = Vec3.subtract(this.currentObjectPosition, this.currentAvatarPosition); + var handMovementFromTurning = Vec3.subtract(Quat.multiply(avatarDeltaOrientation, handToAvatar), handToAvatar); + var objectMovementFromTurning = Vec3.subtract(Quat.multiply(avatarDeltaOrientation, objectToAvatar), objectToAvatar); + this.currentAvatarOrientation = currentOrientation; + + // how far did hand move this timestep? + var handMoved = Vec3.subtract(handToAvatar, this.handRelativePreviousPosition); + this.handRelativePreviousPosition = handToAvatar; + + // magnify the hand movement but not the change from avatar movement & rotation + handMoved = Vec3.subtract(handMoved, handMovementFromTurning); + var superHandMoved = Vec3.multiply(handMoved, radius); + + // Move the object by the magnified amount and then by amount from avatar movement & rotation + var newObjectPosition = Vec3.sum(this.currentObjectPosition, superHandMoved); + newObjectPosition = Vec3.sum(newObjectPosition, avatarDeltaPosition); + newObjectPosition = Vec3.sum(newObjectPosition, objectMovementFromTurning); + + var deltaPosition = Vec3.subtract(newObjectPosition, this.currentObjectPosition); // meters + var now = Date.now(); + var deltaTime = (now - this.currentObjectTime) / MSEC_PER_SEC; // convert to seconds + + this.currentObjectPosition = newObjectPosition; + this.currentObjectTime = now; + + // this doubles hand rotation + var handChange = Quat.multiply(Quat.slerp(this.handPreviousRotation, + handRotation, + DISTANCE_HOLDING_ROTATION_EXAGGERATION_FACTOR), + Quat.inverse(this.handPreviousRotation)); + this.handPreviousRotation = handRotation; + this.currentObjectRotation = Quat.multiply(handChange, this.currentObjectRotation); + + Entities.callEntityMethod(this.grabbedEntity, "continueDistantGrab"); + + // mix in head motion + if (MOVE_WITH_HEAD) { + var objDistance = Vec3.length(objectToAvatar); + var before = Vec3.multiplyQbyV(this.currentCameraOrientation, { + x: 0.0, + y: 0.0, + z: objDistance + }); + var after = Vec3.multiplyQbyV(Camera.orientation, { + x: 0.0, + y: 0.0, + z: objDistance + }); + var change = Vec3.subtract(before, after); + this.currentCameraOrientation = Camera.orientation; + this.currentObjectPosition = Vec3.sum(this.currentObjectPosition, change); + } + + + //visualizations + if (USE_ENTITY_LINES_FOR_MOVING === true) { + this.lineOn(handPosition, Vec3.subtract(grabbedProperties.position, handPosition), INTERSECT_COLOR); + } + if (USE_OVERLAY_LINES_FOR_MOVING === true) { + this.overlayLineOn(handPosition, grabbedProperties.position, INTERSECT_COLOR); + } + if (USE_PARTICLE_BEAM_FOR_MOVING === true) { + this.handleDistantParticleBeam(handPosition, grabbedProperties.position, this.currentObjectRotation, INTERSECT_COLOR) + } + if (USE_POINTLIGHT === true) { + this.handlePointLight(this.grabbedEntity); + } + if (USE_SPOTLIGHT === true) { + this.handleSpotlight(this.grabbedEntity); + } + + Entities.updateAction(this.grabbedEntity, this.actionID, { + targetPosition: this.currentObjectPosition, + linearTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME, + targetRotation: this.currentObjectRotation, + angularTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME, + ttl: ACTION_TTL + }); + + this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC); + }; + + this.nearGrabbing = function() { + var now = Date.now(); + var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); + + if (this.state == STATE_NEAR_GRABBING && this.triggerSmoothedReleased()) { + this.setState(STATE_RELEASE); + Entities.callEntityMethod(this.grabbedEntity, "releaseGrab"); + return; + } + + this.turnOffVisualizations(); + + var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); + this.activateEntity(this.grabbedEntity, grabbedProperties); + if (grabbedProperties.collisionsWillMove && NEAR_GRABBING_KINEMATIC) { + Entities.editEntity(this.grabbedEntity, { + collisionsWillMove: false + }); + } + + var handRotation = this.getHandRotation(); + var handPosition = this.getHandPosition(); + + var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); + + if (this.state != STATE_NEAR_GRABBING && grabbableData.spatialKey) { + // if an object is "equipped" and has a spatialKey, use it. + this.ignoreIK = grabbableData.spatialKey.ignoreIK ? grabbableData.spatialKey.ignoreIK : false; + this.offsetPosition = getSpatialOffsetPosition(this.hand, grabbableData.spatialKey); + this.offsetRotation = getSpatialOffsetRotation(this.hand, grabbableData.spatialKey); + } else { + this.ignoreIK = false; + + var objectRotation = grabbedProperties.rotation; + this.offsetRotation = Quat.multiply(Quat.inverse(handRotation), objectRotation); + + var currentObjectPosition = grabbedProperties.position; + var offset = Vec3.subtract(currentObjectPosition, handPosition); + this.offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, this.offsetRotation)), offset); + } + + this.actionID = NULL_ACTION_ID; + this.actionID = Entities.addAction("hold", this.grabbedEntity, { + hand: this.hand === RIGHT_HAND ? "right" : "left", + timeScale: NEAR_GRABBING_ACTION_TIMEFRAME, + relativePosition: this.offsetPosition, + relativeRotation: this.offsetRotation, + ttl: ACTION_TTL, + kinematic: NEAR_GRABBING_KINEMATIC, + kinematicSetVelocity: true, + ignoreIK: this.ignoreIK + }); + if (this.actionID === NULL_ACTION_ID) { + this.actionID = null; + } else { + this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC); + if (this.state == STATE_NEAR_GRABBING) { + this.setState(STATE_CONTINUE_NEAR_GRABBING); + } else { + // equipping + Entities.callEntityMethod(this.grabbedEntity, "startEquip", [JSON.stringify(this.hand)]); + this.startHandGrasp(); + + this.setState(STATE_CONTINUE_EQUIP_BD); + } + + if (this.hand === RIGHT_HAND) { + Entities.callEntityMethod(this.grabbedEntity, "setRightHand"); + } else { + Entities.callEntityMethod(this.grabbedEntity, "setLeftHand"); + } + + Entities.callEntityMethod(this.grabbedEntity, "setHand", [this.hand]); + + Entities.callEntityMethod(this.grabbedEntity, "startNearGrab"); + + } + + this.currentHandControllerTipPosition = + (this.hand === RIGHT_HAND) ? MyAvatar.rightHandTipPosition : MyAvatar.leftHandTipPosition; + + this.currentObjectTime = Date.now(); + }; + + this.continueNearGrabbing = function() { + if (this.state == STATE_CONTINUE_NEAR_GRABBING && this.triggerSmoothedReleased()) { + this.setState(STATE_RELEASE); + Entities.callEntityMethod(this.grabbedEntity, "releaseGrab"); + return; + } + if (this.state == STATE_CONTINUE_EQUIP_BD && this.bumperReleased()) { + this.setState(STATE_CONTINUE_EQUIP); + return; + } + if (this.state == STATE_CONTINUE_EQUIP && this.bumperSqueezed()) { + this.setState(STATE_WAITING_FOR_BUMPER_RELEASE); + return; + } + if (this.state == STATE_CONTINUE_NEAR_GRABBING && this.bumperSqueezed()) { + this.setState(STATE_CONTINUE_EQUIP_BD); + Entities.callEntityMethod(this.grabbedEntity, "startEquip", [JSON.stringify(this.hand)]); + return; + } + + // Keep track of the fingertip velocity to impart when we release the object. + // Note that the idea of using a constant 'tip' velocity regardless of the + // object's actual held offset is an idea intended to make it easier to throw things: + // Because we might catch something or transfer it between hands without a good idea + // of it's actual offset, let's try imparting a velocity which is at a fixed radius + // from the palm. + + var handControllerPosition = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandPosition : MyAvatar.leftHandPosition; + var now = Date.now(); + + var deltaPosition = Vec3.subtract(handControllerPosition, this.currentHandControllerTipPosition); // meters + var deltaTime = (now - this.currentObjectTime) / MSEC_PER_SEC; // convert to seconds + + this.currentHandControllerTipPosition = handControllerPosition; + this.currentObjectTime = now; + Entities.callEntityMethod(this.grabbedEntity, "continueNearGrab"); + + if (this.state === STATE_CONTINUE_EQUIP_BD) { + Entities.callEntityMethod(this.grabbedEntity, "continueEquip"); + } + + if (this.actionTimeout - now < ACTION_TTL_REFRESH * MSEC_PER_SEC) { + // if less than a 5 seconds left, refresh the actions ttl + Entities.updateAction(this.grabbedEntity, this.actionID, { + hand: this.hand === RIGHT_HAND ? "right" : "left", + timeScale: NEAR_GRABBING_ACTION_TIMEFRAME, + relativePosition: this.offsetPosition, + relativeRotation: this.offsetRotation, + ttl: ACTION_TTL, + kinematic: NEAR_GRABBING_KINEMATIC, + kinematicSetVelocity: true, + ignoreIK: this.ignoreIK + }); + this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC); + } + }; + + this.waitingForBumperRelease = function() { + if (this.bumperReleased()) { + this.setState(STATE_RELEASE); + Entities.callEntityMethod(this.grabbedEntity, "releaseGrab"); + Entities.callEntityMethod(this.grabbedEntity, "unequip"); + this.endHandGrasp(); + + } + }; + + this.pullTowardEquipPosition = function() { + + this.turnOffVisualizations(); + + var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); + var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); + + // use a spring to pull the object to where it will be when equipped + var relativeRotation = getSpatialOffsetRotation(this.hand, grabbableData.spatialKey); + var relativePosition = getSpatialOffsetPosition(this.hand, grabbableData.spatialKey); + var ignoreIK = grabbableData.spatialKey.ignoreIK ? grabbableData.spatialKey.ignoreIK : false; + var handRotation = this.getHandRotation(); + var handPosition = this.getHandPosition(); + var targetRotation = Quat.multiply(handRotation, relativeRotation); + var offset = Vec3.multiplyQbyV(targetRotation, relativePosition); + var targetPosition = Vec3.sum(handPosition, offset); + + if (typeof this.equipSpringID === 'undefined' || + this.equipSpringID === null || + this.equipSpringID === NULL_ACTION_ID) { + this.equipSpringID = Entities.addAction("spring", this.grabbedEntity, { + targetPosition: targetPosition, + linearTimeScale: EQUIP_SPRING_TIMEFRAME, + targetRotation: targetRotation, + angularTimeScale: EQUIP_SPRING_TIMEFRAME, + ttl: ACTION_TTL, + ignoreIK: ignoreIK + }); + if (this.equipSpringID === NULL_ACTION_ID) { + this.equipSpringID = null; + this.setState(STATE_OFF); + return; + } + } else { + Entities.updateAction(this.grabbedEntity, this.equipSpringID, { + targetPosition: targetPosition, + linearTimeScale: EQUIP_SPRING_TIMEFRAME, + targetRotation: targetRotation, + angularTimeScale: EQUIP_SPRING_TIMEFRAME, + ttl: ACTION_TTL, + ignoreIK: ignoreIK + }); + } + + if (Vec3.distance(grabbedProperties.position, targetPosition) < EQUIP_SPRING_SHUTOFF_DISTANCE) { + Entities.deleteAction(this.grabbedEntity, this.equipSpringID); + this.equipSpringID = null; + this.setState(STATE_EQUIP); + } + }; + + this.nearTrigger = function() { + if (this.triggerSmoothedReleased()) { + this.setState(STATE_RELEASE); + Entities.callEntityMethod(this.grabbedEntity, "stopNearTrigger"); + return; + } + if (this.hand === RIGHT_HAND) { + Entities.callEntityMethod(this.grabbedEntity, "setRightHand"); + } else { + Entities.callEntityMethod(this.grabbedEntity, "setLeftHand"); + } + + Entities.callEntityMethod(this.grabbedEntity, "setHand", [this.hand]); + + Entities.callEntityMethod(this.grabbedEntity, "startNearTrigger"); + this.setState(STATE_CONTINUE_NEAR_TRIGGER); + }; + + this.farTrigger = function() { + if (this.triggerSmoothedReleased()) { + this.setState(STATE_RELEASE); + Entities.callEntityMethod(this.grabbedEntity, "stopFarTrigger"); + return; + } + + if (this.hand === RIGHT_HAND) { + Entities.callEntityMethod(this.grabbedEntity, "setRightHand"); + } else { + Entities.callEntityMethod(this.grabbedEntity, "setLeftHand"); + } + Entities.callEntityMethod(this.grabbedEntity, "setHand", [this.hand]); + Entities.callEntityMethod(this.grabbedEntity, "startFarTrigger"); + this.setState(STATE_CONTINUE_FAR_TRIGGER); + }; + + this.continueNearTrigger = function() { + if (this.triggerSmoothedReleased()) { + this.setState(STATE_RELEASE); + Entities.callEntityMethod(this.grabbedEntity, "stopNearTrigger"); + return; + } + + Entities.callEntityMethod(this.grabbedEntity, "continueNearTrigger"); + }; + + this.continueFarTrigger = function() { + if (this.triggerSmoothedReleased()) { + this.setState(STATE_RELEASE); + Entities.callEntityMethod(this.grabbedEntity, "stopNearTrigger"); + return; + } + + var handPosition = this.getHandPosition(); + var pickRay = { + origin: handPosition, + direction: Quat.getUp(this.getHandRotation()) + }; + + var now = Date.now(); + if (now - this.lastPickTime > MSECS_PER_SEC / PICKS_PER_SECOND_PER_HAND) { + var intersection = Entities.findRayIntersection(pickRay, true); + this.lastPickTime = now; + if (intersection.entityID != this.grabbedEntity) { + this.setState(STATE_RELEASE); + Entities.callEntityMethod(this.grabbedEntity, "stopFarTrigger"); + return; + } + } + + this.lineOn(pickRay.origin, Vec3.multiply(pickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); + Entities.callEntityMethod(this.grabbedEntity, "continueFarTrigger"); + }; + + _this.allTouchedIDs = {}; + + this.touchTest = function() { + var maxDistance = 0.05; + var leftHandPosition = MyAvatar.getLeftPalmPosition(); + var rightHandPosition = MyAvatar.getRightPalmPosition(); + var leftEntities = Entities.findEntities(leftHandPosition, maxDistance); + var rightEntities = Entities.findEntities(rightHandPosition, maxDistance); + var ids = []; + + if (leftEntities.length !== 0) { + leftEntities.forEach(function(entity) { + ids.push(entity); + }); + + } + + if (rightEntities.length !== 0) { + rightEntities.forEach(function(entity) { + ids.push(entity); + }); + } + + ids.forEach(function(id) { + + var props = Entities.getEntityProperties(id, ["boundingBox", "name"]); + if (props.name === 'pointer') { + return; + } else { + var entityMinPoint = props.boundingBox.brn; + var entityMaxPoint = props.boundingBox.tfl; + var leftIsTouching = pointInExtents(leftHandPosition, entityMinPoint, entityMaxPoint); + var rightIsTouching = pointInExtents(rightHandPosition, entityMinPoint, entityMaxPoint); + + if ((leftIsTouching || rightIsTouching) && _this.allTouchedIDs[id] === undefined) { + // we haven't been touched before, but either right or left is touching us now + _this.allTouchedIDs[id] = true; + _this.startTouch(id); + } else if ((leftIsTouching || rightIsTouching) && _this.allTouchedIDs[id]) { + // we have been touched before and are still being touched + // continue touch + _this.continueTouch(id); + } else if (_this.allTouchedIDs[id]) { + delete _this.allTouchedIDs[id]; + _this.stopTouch(id); + + } else { + //we are in another state + return; + } + } + + }); + + }; + + this.startTouch = function(entityID) { + Entities.callEntityMethod(entityID, "startTouch"); + }; + + this.continueTouch = function(entityID) { + Entities.callEntityMethod(entityID, "continueTouch"); + }; + + this.stopTouch = function(entityID) { + Entities.callEntityMethod(entityID, "stopTouch"); + }; + + this.release = function() { + + this.turnLightsOff(); + this.turnOffVisualizations(); + + if (this.grabbedEntity !== null) { + if (this.actionID !== null) { + Entities.deleteAction(this.grabbedEntity, this.actionID); + } + } + + this.deactivateEntity(this.grabbedEntity); + + this.grabbedEntity = null; + this.actionID = null; + this.setState(STATE_OFF); + }; + + this.cleanup = function() { + this.release(); + this.endHandGrasp(); + Entities.deleteEntity(this.particleBeam); + Entities.deleteEntity(this.spotLight); + Entities.deleteEntity(this.pointLight); + }; + + this.activateEntity = function(entityID, grabbedProperties) { + var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, entityID, DEFAULT_GRABBABLE_DATA); + var invertSolidWhileHeld = grabbableData["invertSolidWhileHeld"]; + var data = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, {}); + data["activated"] = true; + data["avatarId"] = MyAvatar.sessionUUID; + data["refCount"] = data["refCount"] ? data["refCount"] + 1 : 1; + // zero gravity and set ignoreForCollisions in a way that lets us put them back, after all grabs are done + if (data["refCount"] == 1) { + data["gravity"] = grabbedProperties.gravity; + data["ignoreForCollisions"] = grabbedProperties.ignoreForCollisions; + data["collisionsWillMove"] = grabbedProperties.collisionsWillMove; + var whileHeldProperties = { + gravity: { + x: 0, + y: 0, + z: 0 + } + }; + if (invertSolidWhileHeld) { + whileHeldProperties["ignoreForCollisions"] = !grabbedProperties.ignoreForCollisions; + } + Entities.editEntity(entityID, whileHeldProperties); + } + + setEntityCustomData(GRAB_USER_DATA_KEY, entityID, data); + return data; + }; + + this.deactivateEntity = function(entityID) { + var data = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, {}); + if (data && data["refCount"]) { + data["refCount"] = data["refCount"] - 1; + if (data["refCount"] < 1) { + Entities.editEntity(entityID, { + gravity: data["gravity"], + ignoreForCollisions: data["ignoreForCollisions"], + collisionsWillMove: data["collisionsWillMove"] + }); + data = null; + } + } else { + data = null; + } + setEntityCustomData(GRAB_USER_DATA_KEY, entityID, data); + }; + + + //this is our handler, where we do the actual work of changing animation settings + this.graspHand = function(animationProperties) { + var result = {}; + //full alpha on overlay for this hand + //set grab to true + //set idle to false + //full alpha on the blend btw open and grab + if (_this.hand === RIGHT_HAND) { + result['rightHandOverlayAlpha'] = 1.0; + result['isRightHandGrab'] = true; + result['isRightHandIdle'] = false; + result['rightHandGrabBlend'] = 1.0; + } else if (_this.hand === LEFT_HAND) { + result['leftHandOverlayAlpha'] = 1.0; + result['isLeftHandGrab'] = true; + result['isLeftHandIdle'] = false; + result['leftHandGrabBlend'] = 1.0; + } + //return an object with our updated settings + return result; + }; + + this.graspHandler = null + + this.startHandGrasp = function() { + if (this.hand === RIGHT_HAND) { + this.graspHandler = MyAvatar.addAnimationStateHandler(this.graspHand, ['isRightHandGrab']); + } else if (this.hand === LEFT_HAND) { + this.graspHandler = MyAvatar.addAnimationStateHandler(this.graspHand, ['isLeftHandGrab']); + } + }; + + this.endHandGrasp = function() { + // Tell the animation system we don't need any more callbacks. + MyAvatar.removeAnimationStateHandler(this.graspHandler); + }; + +}; + +var rightController = new MyController(RIGHT_HAND); +var leftController = new MyController(LEFT_HAND); + +//preload the particle beams so that they are full length when you start searching +if (USE_PARTICLE_BEAM_FOR_SEARCHING === true || USE_PARTICLE_BEAM_FOR_MOVING === true) { + rightController.createParticleBeam(); + leftController.createParticleBeam(); +} + +var MAPPING_NAME = "com.highfidelity.handControllerGrab"; + +var mapping = Controller.newMapping(MAPPING_NAME); +mapping.from([Controller.Standard.RT]).peek().to(rightController.triggerPress); +mapping.from([Controller.Standard.LT]).peek().to(leftController.triggerPress); + +mapping.from([Controller.Standard.RB]).peek().to(rightController.bumperPress); +mapping.from([Controller.Standard.LB]).peek().to(leftController.bumperPress); + +Controller.enableMapping(MAPPING_NAME); + +//the section below allows the grab script to listen for messages that disable either one or both hands. useful for two handed items +var handToDisable = 'none'; + +function update() { + if (handToDisable !== LEFT_HAND && handToDisable !== 'both') { + leftController.update(); + } + if (handToDisable !== RIGHT_HAND && handToDisable !== 'both') { + rightController.update(); + } +} + +Messages.subscribe('Hifi-Hand-Disabler'); + +handleHandDisablerMessages = function(channel, message, sender) { + + if (sender === MyAvatar.sessionUUID) { + if (message === 'left') { + handToDisable = LEFT_HAND; + } + if (message === 'right') { + handToDisable = RIGHT_HAND; + } + if (message === 'both') { + handToDisable = 'both'; + } + if (message === 'none') { + handToDisable = 'none'; + } + } + +} + +Messages.messageReceived.connect(handleHandDisablerMessages); + +function cleanup() { + rightController.cleanup(); + leftController.cleanup(); + Controller.disableMapping(MAPPING_NAME); +} + +Script.scriptEnding.connect(cleanup); +Script.update.connect(update); \ No newline at end of file From 8d691f2132f4acdeb7f98692d1ec97236b77b086 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Mon, 14 Dec 2015 11:44:42 -0800 Subject: [PATCH 27/94] light ball --- examples/flowArts/lightBall/lightBall.js | 0 .../flowArts/lightBall/lightBallSpawner.js | 51 +++++++++++++++++++ .../entities/src/ParticleEffectEntityItem.cpp | 4 +- 3 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 examples/flowArts/lightBall/lightBall.js create mode 100644 examples/flowArts/lightBall/lightBallSpawner.js diff --git a/examples/flowArts/lightBall/lightBall.js b/examples/flowArts/lightBall/lightBall.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/examples/flowArts/lightBall/lightBallSpawner.js b/examples/flowArts/lightBall/lightBallSpawner.js new file mode 100644 index 0000000000..f758c26bbe --- /dev/null +++ b/examples/flowArts/lightBall/lightBallSpawner.js @@ -0,0 +1,51 @@ +var center = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(Camera.getOrientation()))); + +var lightBall = Entities.addEntity({ + position: center, + type: "ParticleEffect", + isEmitting: true, + "name": "ParticlesTest Emitter", + "colorStart": {red: 20, green: 20, blue: 255}, + color: {red: 10, green: 0, blue: 255}, + "colorFinish": {red: 250, green: 200, blue:255}, + // "maxParticles": 100000, + "lifespan": 1, + "emitRate": 1000, + "emitSpeed": .1, + "speedSpread": .01, + "emitDimensions": { + "x": 0, + "y": 0, + "z": 0 + }, + "polarStart": 0, + "polarFinish": 3, + "azimuthStart": -3.1415927410125732, + "azimuthFinish": 3.1415927410125732, + "emitAcceleration": { + "x": 0, + "y": 0, + "z": 0 + }, + "accelerationSpread": { + "x": .01, + "y": .01, + "z": .01 + }, + "particleRadius": 0.04, + "radiusSpread": 0, + "radiusStart": 0.05, + "radiusFinish": 0.0003, + "alpha": 0, + "alphaSpread": .5, + "alphaStart": 0, + "alphaFinish": 0.5, + "additiveBlending": 0, + "textures": "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png" +}) + +function cleanup() { + Entities.deleteEntity(lightBall); +} + +Script.scriptEnding.connect(cleanup); \ No newline at end of file diff --git a/libraries/entities/src/ParticleEffectEntityItem.cpp b/libraries/entities/src/ParticleEffectEntityItem.cpp index 5600c85650..f4210f4486 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.cpp +++ b/libraries/entities/src/ParticleEffectEntityItem.cpp @@ -52,13 +52,13 @@ const float ParticleEffectEntityItem::MINIMUM_ALPHA = 0.0f; const float ParticleEffectEntityItem::MAXIMUM_ALPHA = 1.0f; const quint32 ParticleEffectEntityItem::DEFAULT_MAX_PARTICLES = 1000; const quint32 ParticleEffectEntityItem::MINIMUM_MAX_PARTICLES = 1; -const quint32 ParticleEffectEntityItem::MAXIMUM_MAX_PARTICLES = 10000; +const quint32 ParticleEffectEntityItem::MAXIMUM_MAX_PARTICLES = 100000; const float ParticleEffectEntityItem::DEFAULT_LIFESPAN = 3.0f; const float ParticleEffectEntityItem::MINIMUM_LIFESPAN = 0.0f; const float ParticleEffectEntityItem::MAXIMUM_LIFESPAN = 86400.0f; // 1 day const float ParticleEffectEntityItem::DEFAULT_EMIT_RATE = 15.0f; const float ParticleEffectEntityItem::MINIMUM_EMIT_RATE = 0.0f; -const float ParticleEffectEntityItem::MAXIMUM_EMIT_RATE = 1000.0f; +const float ParticleEffectEntityItem::MAXIMUM_EMIT_RATE = 100000.0f; const float ParticleEffectEntityItem::DEFAULT_EMIT_SPEED = 5.0f; const float ParticleEffectEntityItem::MINIMUM_EMIT_SPEED = 0.0f; const float ParticleEffectEntityItem::MAXIMUM_EMIT_SPEED = 1000.0f; // Approx mach 3 From 76487bca7ac69c8fdaa2ec9e8eaaa281871b378e Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Mon, 14 Dec 2015 13:01:03 -0800 Subject: [PATCH 28/94] updates --- .../handControllerGrab-particles.js | 47 +++++++++++-------- .../handControllerGrab-pointlight.js | 47 +++++++++++-------- .../handControllerGrab-spotlight.js | 47 +++++++++++-------- examples/controllers/handControllerGrab.js | 47 +++++++++++-------- 4 files changed, 108 insertions(+), 80 deletions(-) diff --git a/examples/controllers/handControllerGrab-particles.js b/examples/controllers/handControllerGrab-particles.js index d9ecb18b01..bfe51927d0 100644 --- a/examples/controllers/handControllerGrab-particles.js +++ b/examples/controllers/handControllerGrab-particles.js @@ -437,47 +437,49 @@ function MyController(hand) { }); var finalRotation = Quat.multiply(orientation, rotation); - + var lifespan = LINE_LENGTH / 10; + var speed = 5; + var spread = 2; if (this.particleBeam === null) { - this.createParticleBeam(position, finalRotation, color); + this.createParticleBeam(position, finalRotation, color, speed, spread, lifespan); } else { - this.updateParticleBeam(position, finalRotation, color); + this.updateParticleBeam(position, finalRotation, color, speed, spread, lifespan); } }; - this.handleDistantParticleBeam = function(handPosition, objectPosition, objectRotation, color) { + this.handleDistantParticleBeam = function(handPosition, objectPosition, color) { var handToObject = Vec3.subtract(objectPosition, handPosition); var finalRotation = Quat.rotationBetween(Vec3.multiply(-1, Vec3.UP), handToObject); var distance = Vec3.distance(handPosition, objectPosition); - var speed = distance * 1; + var speed = 5; + var spread = 0; + + var lifespan = distance / speed; - var lifepsan = distance / speed; - var lifespan = 1; if (this.particleBeam === null) { - this.createParticleBeam(objectPosition, finalRotation, color, speed); + this.createParticleBeam(objectPosition, finalRotation, color, speed, spread, lifespan); } else { - this.updateParticleBeam(objectPosition, finalRotation, color, speed, lifepsan); + this.updateParticleBeam(objectPosition, finalRotation, color, speed, spread, lifespan); } }; - this.createParticleBeam = function(position, orientation, color, speed, lifepsan) { + this.createParticleBeam = function(position, orientation, color, speed, spread, lifespan) { var particleBeamProperties = { type: "ParticleEffect", isEmitting: true, position: position, visible: false, - //rotation:Quat.fromPitchYawRollDegrees(-90.0, 0.0, 0.0), "name": "Particle Beam", "color": color, "maxParticles": 2000, - "lifespan": LINE_LENGTH / 10, + "lifespan": lifespan, "emitRate": 50, - "emitSpeed": 5, - "speedSpread": 2, + "emitSpeed": speed, + "speedSpread": spread, "emitOrientation": { "x": -1, "y": 0, @@ -519,22 +521,23 @@ function MyController(hand) { "alphaSpread": 0, "alphaStart": 1, "alphaFinish": 1, - "additiveBlending": 1, + "additiveBlending": 0, "textures": "https://hifi-content.s3.amazonaws.com/alan/dev/textures/grabsprite-3.png" } this.particleBeam = Entities.addEntity(particleBeamProperties); }; - this.updateParticleBeam = function(position, orientation, color, speed, lifepsan) { - + this.updateParticleBeam = function(position, orientation, color, speed, spread, lifespan) { + print('lifespan::' + lifespan); Entities.editEntity(this.particleBeam, { rotation: orientation, position: position, visible: true, color: color, emitSpeed: speed, - lifepsan: lifepsan + speedSpread:spread, + lifespan: lifespan }) @@ -1108,7 +1111,8 @@ function MyController(hand) { this.overlayLineOn(handPosition, grabbedProperties.position, INTERSECT_COLOR); } if (USE_PARTICLE_BEAM_FOR_MOVING === true) { - this.handleDistantParticleBeam(handPosition, grabbedProperties.position, this.currentObjectRotation, INTERSECT_COLOR) + this.handleDistantParticleBeam(handPosition,grabbedProperties.position, INTERSECT_COLOR) + // this.handleDistantParticleBeam(handPosition, this.currentObjectPosition, INTERSECT_COLOR) } if (USE_POINTLIGHT === true) { this.handlePointLight(this.grabbedEntity); @@ -1399,7 +1403,10 @@ function MyController(hand) { } } - this.lineOn(pickRay.origin, Vec3.multiply(pickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); + if (USE_ENTITY_LINES_FOR_MOVING === true) { + this.lineOn(pickRay.origin, Vec3.multiply(pickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); + } + Entities.callEntityMethod(this.grabbedEntity, "continueFarTrigger"); }; diff --git a/examples/controllers/handControllerGrab-pointlight.js b/examples/controllers/handControllerGrab-pointlight.js index fc14e6026e..e7bb4e3e9f 100644 --- a/examples/controllers/handControllerGrab-pointlight.js +++ b/examples/controllers/handControllerGrab-pointlight.js @@ -437,47 +437,49 @@ function MyController(hand) { }); var finalRotation = Quat.multiply(orientation, rotation); - + var lifespan = LINE_LENGTH / 10; + var speed = 5; + var spread = 2; if (this.particleBeam === null) { - this.createParticleBeam(position, finalRotation, color); + this.createParticleBeam(position, finalRotation, color, speed, spread, lifespan); } else { - this.updateParticleBeam(position, finalRotation, color); + this.updateParticleBeam(position, finalRotation, color, speed, spread, lifespan); } }; - this.handleDistantParticleBeam = function(handPosition, objectPosition, objectRotation, color) { + this.handleDistantParticleBeam = function(handPosition, objectPosition, color) { var handToObject = Vec3.subtract(objectPosition, handPosition); var finalRotation = Quat.rotationBetween(Vec3.multiply(-1, Vec3.UP), handToObject); var distance = Vec3.distance(handPosition, objectPosition); - var speed = distance * 1; + var speed = 5; + var spread = 0; + + var lifespan = distance / speed; - var lifepsan = distance / speed; - var lifespan = 1; if (this.particleBeam === null) { - this.createParticleBeam(objectPosition, finalRotation, color, speed); + this.createParticleBeam(objectPosition, finalRotation, color, speed, spread, lifespan); } else { - this.updateParticleBeam(objectPosition, finalRotation, color, speed, lifepsan); + this.updateParticleBeam(objectPosition, finalRotation, color, speed, spread, lifespan); } }; - this.createParticleBeam = function(position, orientation, color, speed, lifepsan) { + this.createParticleBeam = function(position, orientation, color, speed, spread, lifespan) { var particleBeamProperties = { type: "ParticleEffect", isEmitting: true, position: position, visible: false, - //rotation:Quat.fromPitchYawRollDegrees(-90.0, 0.0, 0.0), "name": "Particle Beam", "color": color, "maxParticles": 2000, - "lifespan": LINE_LENGTH / 10, + "lifespan": lifespan, "emitRate": 50, - "emitSpeed": 5, - "speedSpread": 2, + "emitSpeed": speed, + "speedSpread": spread, "emitOrientation": { "x": -1, "y": 0, @@ -519,22 +521,23 @@ function MyController(hand) { "alphaSpread": 0, "alphaStart": 1, "alphaFinish": 1, - "additiveBlending": 1, + "additiveBlending": 0, "textures": "https://hifi-content.s3.amazonaws.com/alan/dev/textures/grabsprite-3.png" } this.particleBeam = Entities.addEntity(particleBeamProperties); }; - this.updateParticleBeam = function(position, orientation, color, speed, lifepsan) { - + this.updateParticleBeam = function(position, orientation, color, speed, spread, lifespan) { + print('lifespan::' + lifespan); Entities.editEntity(this.particleBeam, { rotation: orientation, position: position, visible: true, color: color, emitSpeed: speed, - lifepsan: lifepsan + speedSpread:spread, + lifespan: lifespan }) @@ -1108,7 +1111,8 @@ function MyController(hand) { this.overlayLineOn(handPosition, grabbedProperties.position, INTERSECT_COLOR); } if (USE_PARTICLE_BEAM_FOR_MOVING === true) { - this.handleDistantParticleBeam(handPosition, grabbedProperties.position, this.currentObjectRotation, INTERSECT_COLOR) + this.handleDistantParticleBeam(handPosition,grabbedProperties.position, INTERSECT_COLOR) + // this.handleDistantParticleBeam(handPosition, this.currentObjectPosition, INTERSECT_COLOR) } if (USE_POINTLIGHT === true) { this.handlePointLight(this.grabbedEntity); @@ -1399,7 +1403,10 @@ function MyController(hand) { } } - this.lineOn(pickRay.origin, Vec3.multiply(pickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); + if (USE_ENTITY_LINES_FOR_MOVING === true) { + this.lineOn(pickRay.origin, Vec3.multiply(pickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); + } + Entities.callEntityMethod(this.grabbedEntity, "continueFarTrigger"); }; diff --git a/examples/controllers/handControllerGrab-spotlight.js b/examples/controllers/handControllerGrab-spotlight.js index ea05a44f78..ee6dcfa681 100644 --- a/examples/controllers/handControllerGrab-spotlight.js +++ b/examples/controllers/handControllerGrab-spotlight.js @@ -437,47 +437,49 @@ function MyController(hand) { }); var finalRotation = Quat.multiply(orientation, rotation); - + var lifespan = LINE_LENGTH / 10; + var speed = 5; + var spread = 2; if (this.particleBeam === null) { - this.createParticleBeam(position, finalRotation, color); + this.createParticleBeam(position, finalRotation, color, speed, spread, lifespan); } else { - this.updateParticleBeam(position, finalRotation, color); + this.updateParticleBeam(position, finalRotation, color, speed, spread, lifespan); } }; - this.handleDistantParticleBeam = function(handPosition, objectPosition, objectRotation, color) { + this.handleDistantParticleBeam = function(handPosition, objectPosition, color) { var handToObject = Vec3.subtract(objectPosition, handPosition); var finalRotation = Quat.rotationBetween(Vec3.multiply(-1, Vec3.UP), handToObject); var distance = Vec3.distance(handPosition, objectPosition); - var speed = distance * 1; + var speed = 5; + var spread = 0; + + var lifespan = distance / speed; - var lifepsan = distance / speed; - var lifespan = 1; if (this.particleBeam === null) { - this.createParticleBeam(objectPosition, finalRotation, color, speed); + this.createParticleBeam(objectPosition, finalRotation, color, speed, spread, lifespan); } else { - this.updateParticleBeam(objectPosition, finalRotation, color, speed, lifepsan); + this.updateParticleBeam(objectPosition, finalRotation, color, speed, spread, lifespan); } }; - this.createParticleBeam = function(position, orientation, color, speed, lifepsan) { + this.createParticleBeam = function(position, orientation, color, speed, spread, lifespan) { var particleBeamProperties = { type: "ParticleEffect", isEmitting: true, position: position, visible: false, - //rotation:Quat.fromPitchYawRollDegrees(-90.0, 0.0, 0.0), "name": "Particle Beam", "color": color, "maxParticles": 2000, - "lifespan": LINE_LENGTH / 10, + "lifespan": lifespan, "emitRate": 50, - "emitSpeed": 5, - "speedSpread": 2, + "emitSpeed": speed, + "speedSpread": spread, "emitOrientation": { "x": -1, "y": 0, @@ -519,22 +521,23 @@ function MyController(hand) { "alphaSpread": 0, "alphaStart": 1, "alphaFinish": 1, - "additiveBlending": 1, + "additiveBlending": 0, "textures": "https://hifi-content.s3.amazonaws.com/alan/dev/textures/grabsprite-3.png" } this.particleBeam = Entities.addEntity(particleBeamProperties); }; - this.updateParticleBeam = function(position, orientation, color, speed, lifepsan) { - + this.updateParticleBeam = function(position, orientation, color, speed, spread, lifespan) { + print('lifespan::' + lifespan); Entities.editEntity(this.particleBeam, { rotation: orientation, position: position, visible: true, color: color, emitSpeed: speed, - lifepsan: lifepsan + speedSpread:spread, + lifespan: lifespan }) @@ -1108,7 +1111,8 @@ function MyController(hand) { this.overlayLineOn(handPosition, grabbedProperties.position, INTERSECT_COLOR); } if (USE_PARTICLE_BEAM_FOR_MOVING === true) { - this.handleDistantParticleBeam(handPosition, grabbedProperties.position, this.currentObjectRotation, INTERSECT_COLOR) + this.handleDistantParticleBeam(handPosition,grabbedProperties.position, INTERSECT_COLOR) + // this.handleDistantParticleBeam(handPosition, this.currentObjectPosition, INTERSECT_COLOR) } if (USE_POINTLIGHT === true) { this.handlePointLight(this.grabbedEntity); @@ -1399,7 +1403,10 @@ function MyController(hand) { } } - this.lineOn(pickRay.origin, Vec3.multiply(pickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); + if (USE_ENTITY_LINES_FOR_MOVING === true) { + this.lineOn(pickRay.origin, Vec3.multiply(pickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); + } + Entities.callEntityMethod(this.grabbedEntity, "continueFarTrigger"); }; diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index cd82db22b9..b6f1a82c3f 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -437,47 +437,49 @@ function MyController(hand) { }); var finalRotation = Quat.multiply(orientation, rotation); - + var lifespan = LINE_LENGTH / 10; + var speed = 5; + var spread = 2; if (this.particleBeam === null) { - this.createParticleBeam(position, finalRotation, color); + this.createParticleBeam(position, finalRotation, color, speed, spread, lifespan); } else { - this.updateParticleBeam(position, finalRotation, color); + this.updateParticleBeam(position, finalRotation, color, speed, spread, lifespan); } }; - this.handleDistantParticleBeam = function(handPosition, objectPosition, objectRotation, color) { + this.handleDistantParticleBeam = function(handPosition, objectPosition, color) { var handToObject = Vec3.subtract(objectPosition, handPosition); var finalRotation = Quat.rotationBetween(Vec3.multiply(-1, Vec3.UP), handToObject); var distance = Vec3.distance(handPosition, objectPosition); - var speed = distance * 1; + var speed = 5; + var spread = 0; + + var lifespan = distance / speed; - var lifepsan = distance / speed; - var lifespan = 1; if (this.particleBeam === null) { - this.createParticleBeam(objectPosition, finalRotation, color, speed); + this.createParticleBeam(objectPosition, finalRotation, color, speed, spread, lifespan); } else { - this.updateParticleBeam(objectPosition, finalRotation, color, speed, lifepsan); + this.updateParticleBeam(objectPosition, finalRotation, color, speed, spread, lifespan); } }; - this.createParticleBeam = function(position, orientation, color, speed, lifepsan) { + this.createParticleBeam = function(position, orientation, color, speed, spread, lifespan) { var particleBeamProperties = { type: "ParticleEffect", isEmitting: true, position: position, visible: false, - //rotation:Quat.fromPitchYawRollDegrees(-90.0, 0.0, 0.0), "name": "Particle Beam", "color": color, "maxParticles": 2000, - "lifespan": LINE_LENGTH / 10, + "lifespan": lifespan, "emitRate": 50, - "emitSpeed": 5, - "speedSpread": 2, + "emitSpeed": speed, + "speedSpread": spread, "emitOrientation": { "x": -1, "y": 0, @@ -519,22 +521,23 @@ function MyController(hand) { "alphaSpread": 0, "alphaStart": 1, "alphaFinish": 1, - "additiveBlending": 1, + "additiveBlending": 0, "textures": "https://hifi-content.s3.amazonaws.com/alan/dev/textures/grabsprite-3.png" } this.particleBeam = Entities.addEntity(particleBeamProperties); }; - this.updateParticleBeam = function(position, orientation, color, speed, lifepsan) { - + this.updateParticleBeam = function(position, orientation, color, speed, spread, lifespan) { + print('lifespan::' + lifespan); Entities.editEntity(this.particleBeam, { rotation: orientation, position: position, visible: true, color: color, emitSpeed: speed, - lifepsan: lifepsan + speedSpread:spread, + lifespan: lifespan }) @@ -1108,7 +1111,8 @@ function MyController(hand) { this.overlayLineOn(handPosition, grabbedProperties.position, INTERSECT_COLOR); } if (USE_PARTICLE_BEAM_FOR_MOVING === true) { - this.handleDistantParticleBeam(handPosition, grabbedProperties.position, this.currentObjectRotation, INTERSECT_COLOR) + this.handleDistantParticleBeam(handPosition,grabbedProperties.position, INTERSECT_COLOR) + // this.handleDistantParticleBeam(handPosition, this.currentObjectPosition, INTERSECT_COLOR) } if (USE_POINTLIGHT === true) { this.handlePointLight(this.grabbedEntity); @@ -1399,7 +1403,10 @@ function MyController(hand) { } } - this.lineOn(pickRay.origin, Vec3.multiply(pickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); + if (USE_ENTITY_LINES_FOR_MOVING === true) { + this.lineOn(pickRay.origin, Vec3.multiply(pickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); + } + Entities.callEntityMethod(this.grabbedEntity, "continueFarTrigger"); }; From e3e1bc1dc611b0bbb00860807a04018aa04c7105 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Mon, 14 Dec 2015 13:01:44 -0800 Subject: [PATCH 29/94] updates --- .../handControllerGrab-all-overlays.js | 47 +++++++++++-------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/examples/controllers/handControllerGrab-all-overlays.js b/examples/controllers/handControllerGrab-all-overlays.js index acb47b2260..67e2b1f0a7 100644 --- a/examples/controllers/handControllerGrab-all-overlays.js +++ b/examples/controllers/handControllerGrab-all-overlays.js @@ -437,47 +437,49 @@ function MyController(hand) { }); var finalRotation = Quat.multiply(orientation, rotation); - + var lifespan = LINE_LENGTH / 10; + var speed = 5; + var spread = 2; if (this.particleBeam === null) { - this.createParticleBeam(position, finalRotation, color); + this.createParticleBeam(position, finalRotation, color, speed, spread, lifespan); } else { - this.updateParticleBeam(position, finalRotation, color); + this.updateParticleBeam(position, finalRotation, color, speed, spread, lifespan); } }; - this.handleDistantParticleBeam = function(handPosition, objectPosition, objectRotation, color) { + this.handleDistantParticleBeam = function(handPosition, objectPosition, color) { var handToObject = Vec3.subtract(objectPosition, handPosition); var finalRotation = Quat.rotationBetween(Vec3.multiply(-1, Vec3.UP), handToObject); var distance = Vec3.distance(handPosition, objectPosition); - var speed = distance * 1; + var speed = 5; + var spread = 0; + + var lifespan = distance / speed; - var lifepsan = distance / speed; - var lifespan = 1; if (this.particleBeam === null) { - this.createParticleBeam(objectPosition, finalRotation, color, speed); + this.createParticleBeam(objectPosition, finalRotation, color, speed, spread, lifespan); } else { - this.updateParticleBeam(objectPosition, finalRotation, color, speed, lifepsan); + this.updateParticleBeam(objectPosition, finalRotation, color, speed, spread, lifespan); } }; - this.createParticleBeam = function(position, orientation, color, speed, lifepsan) { + this.createParticleBeam = function(position, orientation, color, speed, spread, lifespan) { var particleBeamProperties = { type: "ParticleEffect", isEmitting: true, position: position, visible: false, - //rotation:Quat.fromPitchYawRollDegrees(-90.0, 0.0, 0.0), "name": "Particle Beam", "color": color, "maxParticles": 2000, - "lifespan": LINE_LENGTH / 10, + "lifespan": lifespan, "emitRate": 50, - "emitSpeed": 5, - "speedSpread": 2, + "emitSpeed": speed, + "speedSpread": spread, "emitOrientation": { "x": -1, "y": 0, @@ -519,22 +521,23 @@ function MyController(hand) { "alphaSpread": 0, "alphaStart": 1, "alphaFinish": 1, - "additiveBlending": 1, + "additiveBlending": 0, "textures": "https://hifi-content.s3.amazonaws.com/alan/dev/textures/grabsprite-3.png" } this.particleBeam = Entities.addEntity(particleBeamProperties); }; - this.updateParticleBeam = function(position, orientation, color, speed, lifepsan) { - + this.updateParticleBeam = function(position, orientation, color, speed, spread, lifespan) { + print('lifespan::' + lifespan); Entities.editEntity(this.particleBeam, { rotation: orientation, position: position, visible: true, color: color, emitSpeed: speed, - lifepsan: lifepsan + speedSpread:spread, + lifespan: lifespan }) @@ -1108,7 +1111,8 @@ function MyController(hand) { this.overlayLineOn(handPosition, grabbedProperties.position, INTERSECT_COLOR); } if (USE_PARTICLE_BEAM_FOR_MOVING === true) { - this.handleDistantParticleBeam(handPosition, grabbedProperties.position, this.currentObjectRotation, INTERSECT_COLOR) + this.handleDistantParticleBeam(handPosition,grabbedProperties.position, INTERSECT_COLOR) + // this.handleDistantParticleBeam(handPosition, this.currentObjectPosition, INTERSECT_COLOR) } if (USE_POINTLIGHT === true) { this.handlePointLight(this.grabbedEntity); @@ -1399,7 +1403,10 @@ function MyController(hand) { } } - this.lineOn(pickRay.origin, Vec3.multiply(pickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); + if (USE_ENTITY_LINES_FOR_MOVING === true) { + this.lineOn(pickRay.origin, Vec3.multiply(pickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); + } + Entities.callEntityMethod(this.grabbedEntity, "continueFarTrigger"); }; From 761dfeaccd72edbd4cd79ce66bb9d9f517b229d1 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Mon, 14 Dec 2015 14:53:08 -0800 Subject: [PATCH 30/94] 2 balls --- .../flowArts/lightBall/lightBallSpawner.js | 73 ++++++++++++++++++- 1 file changed, 70 insertions(+), 3 deletions(-) diff --git a/examples/flowArts/lightBall/lightBallSpawner.js b/examples/flowArts/lightBall/lightBallSpawner.js index f758c26bbe..da09829257 100644 --- a/examples/flowArts/lightBall/lightBallSpawner.js +++ b/examples/flowArts/lightBall/lightBallSpawner.js @@ -1,16 +1,80 @@ -var center = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(Camera.getOrientation()))); +var center = Vec3.sum(MyAvatar.position, Vec3.multiply(1, Quat.getFront(Camera.getOrientation()))); + +var containerBall = Entities.addEntity({ + type: "Sphere", + position: center, + dimensions: {x: .1, y: .1, z: .1}, + color: {red: 50, green: 10, blue: 50}, + collisionsWillMove: true, + gravity: {x: 0, y: -.1, z: 0} +}); var lightBall = Entities.addEntity({ position: center, type: "ParticleEffect", + parentID: containerBall, isEmitting: true, "name": "ParticlesTest Emitter", "colorStart": {red: 20, green: 20, blue: 255}, color: {red: 10, green: 0, blue: 255}, "colorFinish": {red: 250, green: 200, blue:255}, - // "maxParticles": 100000, + "maxParticles": 20000, "lifespan": 1, - "emitRate": 1000, + "emitRate": 10000, + "emitSpeed": .1, + "speedSpread": .01, + "emitDimensions": { + "x": 0, + "y": 0, + "z": 0 + }, + "polarStart": 0, + "polarFinish": 3, + "azimuthStart": -3.1415927410125732, + "azimuthFinish": 3.1415927410125732, + "emitAcceleration": { + "x": 0, + "y": 0, + "z": 0 + }, + "accelerationSpread": { + "x": .01, + "y": .01, + "z": .01 + }, + "particleRadius": 0.04, + "radiusSpread": 0, + "radiusStart": 0.05, + "radiusFinish": 0.0003, + "alpha": 0, + "alphaSpread": .5, + "alphaStart": 0, + "alphaFinish": 0.5, + "additiveBlending": 0, + "textures": "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png" +}) + +var containerBall2 = Entities.addEntity({ + type: "Sphere", + position: Vec3.sum(center, {x: 0.5, y: 0, z: 0}), + dimensions: {x: .1, y: .1, z: .1}, + color: {red: 200, green: 10, blue: 50}, + collisionsWillMove: true, + gravity: {x: 0, y: -.1, z: 0} +}); + +var lightBall2 = Entities.addEntity({ + position: Vec3.sum(center, {x: 0.5, y: 0, z: 0}), + type: "ParticleEffect", + parentID: containerBall2, + isEmitting: true, + "name": "ParticlesTest Emitter", + "colorStart": {red: 200, green: 20, blue: 0}, + color: {red: 255, green: 0, blue: 10}, + "colorFinish": {red: 250, green: 200, blue:255}, + "maxParticles": 100000, + "lifespan": 1, + "emitRate": 10000, "emitSpeed": .1, "speedSpread": .01, "emitDimensions": { @@ -46,6 +110,9 @@ var lightBall = Entities.addEntity({ function cleanup() { Entities.deleteEntity(lightBall); + Entities.deleteEntity(lightBall2); + Entities.deleteEntity(containerBall); + Entities.deleteEntity(containerBall2); } Script.scriptEnding.connect(cleanup); \ No newline at end of file From c561a53f7c283a7c80e7aa7d921409f73a4971d7 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Tue, 15 Dec 2015 09:56:28 -0800 Subject: [PATCH 31/94] Going to change additive blending to world position prop --- .../flowArts/lightBall/lightBallSpawner.js | 151 ++++++++++-------- .../RenderableParticleEffectEntityItem.cpp | 4 +- .../entities/src/ParticleEffectEntityItem.cpp | 17 +- 3 files changed, 102 insertions(+), 70 deletions(-) diff --git a/examples/flowArts/lightBall/lightBallSpawner.js b/examples/flowArts/lightBall/lightBallSpawner.js index da09829257..8f220129b2 100644 --- a/examples/flowArts/lightBall/lightBallSpawner.js +++ b/examples/flowArts/lightBall/lightBallSpawner.js @@ -1,81 +1,101 @@ + +Script.include("../../libraries/utils.js"); + var center = Vec3.sum(MyAvatar.position, Vec3.multiply(1, Quat.getFront(Camera.getOrientation()))); +var modelURL = "file:///C:/Users/Eric/Desktop/RaveRoom.fbx?v1" + Math.random(); + +var raveRoom = Entities.addEntity({ + type: "Model", + modelURL: modelURL, + position: center, + visible:false +}); + +var colorPalette = [{ + red: 250, + green: 137, + blue: 162 +}, { + red: 204, + green: 244, + blue: 249 +}, { + red: 146, + green: 206, + blue: 116 +}, { + red: 240, + green: 87, + blue: 129 +}]; + var containerBall = Entities.addEntity({ type: "Sphere", position: center, dimensions: {x: .1, y: .1, z: .1}, - color: {red: 50, green: 10, blue: 50}, + color: {red: 1500, green: 10, blue: 50}, collisionsWillMove: true, - gravity: {x: 0, y: -.1, z: 0} + userData: JSON.stringify({ + grabbableKey: { + spatialKey: { + relativePosition: { + x: 0, + y: 2, + z: 0 + } + }, + invertSolidWhileHeld: true + } + }) + // gravity: {x: 0, y: -.1, z: 0} }); +var lightZone = Entities.addEntity({ + type: "Zone", + shapeType: 'box', + keyLightIntensity: 0.2, + keyLightColor: { + red: 50, + green: 0, + blue: 50 + }, + keyLightAmbientIntensity: .2, + position: MyAvatar.position, + dimensions: { + x: 100, + y: 100, + z: 100 + } +}); + +var light = Entities.addEntity({ + type: 'Light', + position: center, + parentID: containerBall, + dimensions: { + x: 30, + y: 30, + z: 30 + }, + color: colorPalette[randInt(0, colorPalette.length)], + intensity: 5 + }); + + var lightBall = Entities.addEntity({ position: center, type: "ParticleEffect", parentID: containerBall, isEmitting: true, "name": "ParticlesTest Emitter", - "colorStart": {red: 20, green: 20, blue: 255}, + "colorStart": {red: 200, green: 20, blue: 40}, color: {red: 10, green: 0, blue: 255}, "colorFinish": {red: 250, green: 200, blue:255}, - "maxParticles": 20000, - "lifespan": 1, - "emitRate": 10000, - "emitSpeed": .1, - "speedSpread": .01, - "emitDimensions": { - "x": 0, - "y": 0, - "z": 0 - }, - "polarStart": 0, - "polarFinish": 3, - "azimuthStart": -3.1415927410125732, - "azimuthFinish": 3.1415927410125732, - "emitAcceleration": { - "x": 0, - "y": 0, - "z": 0 - }, - "accelerationSpread": { - "x": .01, - "y": .01, - "z": .01 - }, - "particleRadius": 0.04, - "radiusSpread": 0, - "radiusStart": 0.05, - "radiusFinish": 0.0003, - "alpha": 0, - "alphaSpread": .5, - "alphaStart": 0, - "alphaFinish": 0.5, - "additiveBlending": 0, - "textures": "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png" -}) - -var containerBall2 = Entities.addEntity({ - type: "Sphere", - position: Vec3.sum(center, {x: 0.5, y: 0, z: 0}), - dimensions: {x: .1, y: .1, z: .1}, - color: {red: 200, green: 10, blue: 50}, - collisionsWillMove: true, - gravity: {x: 0, y: -.1, z: 0} -}); - -var lightBall2 = Entities.addEntity({ - position: Vec3.sum(center, {x: 0.5, y: 0, z: 0}), - type: "ParticleEffect", - parentID: containerBall2, - isEmitting: true, - "name": "ParticlesTest Emitter", - "colorStart": {red: 200, green: 20, blue: 0}, - color: {red: 255, green: 0, blue: 10}, - "colorFinish": {red: 250, green: 200, blue:255}, "maxParticles": 100000, - "lifespan": 1, + "lifespan": 7, "emitRate": 10000, - "emitSpeed": .1, + "emitSpeed": .02, "speedSpread": .01, "emitDimensions": { "x": 0, @@ -83,9 +103,9 @@ var lightBall2 = Entities.addEntity({ "z": 0 }, "polarStart": 0, - "polarFinish": 3, - "azimuthStart": -3.1415927410125732, - "azimuthFinish": 3.1415927410125732, + "polarFinish": Math.PI, + "azimuthStart": -Math.PI, + "azimuthFinish": Math.PI, "emitAcceleration": { "x": 0, "y": 0, @@ -108,11 +128,14 @@ var lightBall2 = Entities.addEntity({ "textures": "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png" }) + + function cleanup() { Entities.deleteEntity(lightBall); - Entities.deleteEntity(lightBall2); Entities.deleteEntity(containerBall); - Entities.deleteEntity(containerBall2); + Entities.deleteEntity(raveRoom); + Entities.deleteEntity(lightZone) + Entities.deleteEntity(light); } Script.scriptEnding.connect(cleanup); \ No newline at end of file diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index 4abd8dbafd..42e9fa446c 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -240,8 +240,8 @@ void RenderableParticleEffectEntityItem::updateRenderItem() { auto position = getPosition(); auto rotation = getRotation(); Transform transform; - transform.setTranslation(position); - transform.setRotation(rotation); + //transform.setTranslation(position); + // transform.setRotation(rotation); render::PendingChanges pendingChanges; pendingChanges.updateItem(_renderItemId, [=](ParticlePayloadData& payload) { diff --git a/libraries/entities/src/ParticleEffectEntityItem.cpp b/libraries/entities/src/ParticleEffectEntityItem.cpp index f4210f4486..8f6f60fa26 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.cpp +++ b/libraries/entities/src/ParticleEffectEntityItem.cpp @@ -33,6 +33,8 @@ #include #include #include +#include +#include #include "EntityTree.h" #include "EntityTreeElement.h" @@ -640,12 +642,18 @@ void ParticleEffectEntityItem::stepSimulation(float deltaTime) { ParticleEffectEntityItem::Particle ParticleEffectEntityItem::createParticle() { Particle particle; - particle.seed = randFloatInRange(0.0f, 1.0f); - + std::random_device rd; + + std::mt19937_64 el(rd()); + std::uniform_real_distribution uniform_dist(-1.0, 1.0); + + particle.seed = randFloatInRange(-1.0f, 1.0f); + particle.position = getPosition(); // Position, velocity, and acceleration if (_polarStart == 0.0f && _polarFinish == 0.0f && _emitDimensions.z == 0.0f) { // Emit along z-axis from position - particle.velocity = (_emitSpeed + randFloatInRange(-1.0f, 1.0f) * _speedSpread) * (_emitOrientation * Vectors::UNIT_Z); + + particle.velocity = (_emitSpeed + 0.2f * _speedSpread) * (_emitOrientation * Vectors::UNIT_Z); particle.acceleration = _emitAcceleration + randFloatInRange(-1.0f, 1.0f) * _accelerationSpread; } else { @@ -691,10 +699,11 @@ ParticleEffectEntityItem::Particle ParticleEffectEntityItem::createParticle() { radii.z > 0.0f ? z / (radii.z * radii.z) : 0.0f )); - particle.position = _emitOrientation * emitPosition; + particle.position += _emitOrientation * emitPosition; } particle.velocity = (_emitSpeed + randFloatInRange(-1.0f, 1.0f) * _speedSpread) * (_emitOrientation * emitDirection); + // particle.velocity = (_emitSpeed + uniform_dist(el) * _speedSpread) * (_emitOrientation * emitDirection); particle.acceleration = _emitAcceleration + randFloatInRange(-1.0f, 1.0f) * _accelerationSpread; } From dca83a054ea3b278e1ca2a8f1c1cb23e1740ff19 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Tue, 15 Dec 2015 10:30:57 -0800 Subject: [PATCH 32/94] Changed additiveBlending to emitterShouldTrail --- .../flowArts/lightBall/lightBallSpawner.js | 9 ++++---- .../RenderableParticleEffectEntityItem.cpp | 6 +++-- .../entities/src/EntityItemProperties.cpp | 16 ++++++------- libraries/entities/src/EntityItemProperties.h | 2 +- libraries/entities/src/EntityPropertyFlags.h | 2 +- .../entities/src/ParticleEffectEntityItem.cpp | 23 ++++++++++++------- .../entities/src/ParticleEffectEntityItem.h | 10 ++++---- 7 files changed, 39 insertions(+), 29 deletions(-) diff --git a/examples/flowArts/lightBall/lightBallSpawner.js b/examples/flowArts/lightBall/lightBallSpawner.js index 8f220129b2..7744c1210e 100644 --- a/examples/flowArts/lightBall/lightBallSpawner.js +++ b/examples/flowArts/lightBall/lightBallSpawner.js @@ -41,7 +41,7 @@ var containerBall = Entities.addEntity({ spatialKey: { relativePosition: { x: 0, - y: 2, + y: 1, z: 0 } }, @@ -93,9 +93,9 @@ var lightBall = Entities.addEntity({ color: {red: 10, green: 0, blue: 255}, "colorFinish": {red: 250, green: 200, blue:255}, "maxParticles": 100000, - "lifespan": 7, + "lifespan": 2, "emitRate": 10000, - "emitSpeed": .02, + "emitSpeed": .2, "speedSpread": .01, "emitDimensions": { "x": 0, @@ -125,7 +125,8 @@ var lightBall = Entities.addEntity({ "alphaStart": 0, "alphaFinish": 0.5, "additiveBlending": 0, - "textures": "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png" + "textures": "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png", + emitterShouldTrail: false }) diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index 42e9fa446c..91a4811011 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -240,8 +240,10 @@ void RenderableParticleEffectEntityItem::updateRenderItem() { auto position = getPosition(); auto rotation = getRotation(); Transform transform; - //transform.setTranslation(position); - // transform.setRotation(rotation); + if (!getEmitterShouldTrail()) { + transform.setTranslation(position); + transform.setRotation(rotation); + } render::PendingChanges pendingChanges; pendingChanges.updateItem(_renderItemId, [=](ParticlePayloadData& payload) { diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 98a099fcaa..3a2fdf55d4 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -195,7 +195,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_ALPHA_SPREAD, alphaSpread); CHECK_PROPERTY_CHANGE(PROP_ALPHA_START, alphaStart); CHECK_PROPERTY_CHANGE(PROP_ALPHA_FINISH, alphaFinish); - CHECK_PROPERTY_CHANGE(PROP_ADDITIVE_BLENDING, additiveBlending); + CHECK_PROPERTY_CHANGE(PROP_EMITTER_SHOULD_TRAIL, emitterShouldTrail); CHECK_PROPERTY_CHANGE(PROP_MODEL_URL, modelURL); CHECK_PROPERTY_CHANGE(PROP_COMPOUND_SHAPE_URL, compoundShapeURL); CHECK_PROPERTY_CHANGE(PROP_VISIBLE, visible); @@ -354,7 +354,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA_SPREAD, alphaSpread); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA_START, alphaStart); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA_FINISH, alphaFinish); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ADDITIVE_BLENDING, additiveBlending); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_EMITTER_SHOULD_TRAIL, emitterShouldTrail); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LOCAL_POSITION, localPosition); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LOCAL_ROTATION, localRotation); } @@ -515,7 +515,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE(alphaSpread, float, setAlphaSpread); COPY_PROPERTY_FROM_QSCRIPTVALUE(alphaStart, float, setAlphaStart); COPY_PROPERTY_FROM_QSCRIPTVALUE(alphaFinish, float, setAlphaFinish); - COPY_PROPERTY_FROM_QSCRIPTVALUE(additiveBlending, bool, setAdditiveBlending); + COPY_PROPERTY_FROM_QSCRIPTVALUE(emitterShouldTrail , bool, setEmitterShouldTrail); COPY_PROPERTY_FROM_QSCRIPTVALUE(modelURL, QString, setModelURL); COPY_PROPERTY_FROM_QSCRIPTVALUE(compoundShapeURL, QString, setCompoundShapeURL); COPY_PROPERTY_FROM_QSCRIPTVALUE(glowLevel, float, setGlowLevel); @@ -670,7 +670,7 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue ADD_PROPERTY_TO_MAP(PROP_ALPHA_SPREAD, AlphaSpread, alphaSpread, float); ADD_PROPERTY_TO_MAP(PROP_ALPHA_START, AlphaStart, alphaStart, float); ADD_PROPERTY_TO_MAP(PROP_ALPHA_FINISH, AlphaFinish, alphaFinish, float); - ADD_PROPERTY_TO_MAP(PROP_ADDITIVE_BLENDING, AdditiveBlending, additiveBlending, bool); + ADD_PROPERTY_TO_MAP(PROP_EMITTER_SHOULD_TRAIL, EmitterShouldTrail, emitterShouldTrail, bool); ADD_PROPERTY_TO_MAP(PROP_MODEL_URL, ModelURL, modelURL, QString); ADD_PROPERTY_TO_MAP(PROP_COMPOUND_SHAPE_URL, CompoundShapeURL, compoundShapeURL, QString); ADD_PROPERTY_TO_MAP(PROP_REGISTRATION_POINT, RegistrationPoint, registrationPoint, glm::vec3); @@ -980,7 +980,7 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem APPEND_ENTITY_PROPERTY(PROP_ALPHA_SPREAD, properties.getAlphaSpread()); APPEND_ENTITY_PROPERTY(PROP_ALPHA_START, properties.getAlphaStart()); APPEND_ENTITY_PROPERTY(PROP_ALPHA_FINISH, properties.getAlphaFinish()); - APPEND_ENTITY_PROPERTY(PROP_ADDITIVE_BLENDING, properties.getAdditiveBlending()); + APPEND_ENTITY_PROPERTY(PROP_EMITTER_SHOULD_TRAIL, properties.getEmitterShouldTrail()); } if (properties.getType() == EntityTypes::Zone) { @@ -1265,7 +1265,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA_SPREAD, float, setAlphaSpread); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA_START, float, setAlphaStart); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA_FINISH, float, setAlphaFinish); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ADDITIVE_BLENDING, bool, setAdditiveBlending); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMITTER_SHOULD_TRAIL, bool, setEmitterShouldTrail); } if (properties.getType() == EntityTypes::Zone) { @@ -1608,8 +1608,8 @@ QList EntityItemProperties::listChangedProperties() { if (alphaFinishChanged()) { out += "alphaFinish"; } - if (additiveBlendingChanged()) { - out += "additiveBlending"; + if (emitterShouldTrailChanged()) { + out += "emitterShouldTrail"; } if (modelURLChanged()) { out += "modelURL"; diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index c13519996a..5420e75aed 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -161,7 +161,7 @@ public: DEFINE_PROPERTY(PROP_RADIUS_SPREAD, RadiusSpread, radiusSpread, float, ParticleEffectEntityItem::DEFAULT_RADIUS_SPREAD); DEFINE_PROPERTY(PROP_RADIUS_START, RadiusStart, radiusStart, float, ParticleEffectEntityItem::DEFAULT_RADIUS_START); DEFINE_PROPERTY(PROP_RADIUS_FINISH, RadiusFinish, radiusFinish, float, ParticleEffectEntityItem::DEFAULT_RADIUS_FINISH); - DEFINE_PROPERTY(PROP_ADDITIVE_BLENDING, AdditiveBlending, additiveBlending, bool, ParticleEffectEntityItem::DEFAULT_ADDITIVE_BLENDING); + DEFINE_PROPERTY(PROP_EMITTER_SHOULD_TRAIL, EmitterShouldTrail, emitterShouldTrail, bool, ParticleEffectEntityItem::DEFAULT_EMITTER_SHOULD_TRAIL); DEFINE_PROPERTY_REF(PROP_MARKETPLACE_ID, MarketplaceID, marketplaceID, QString, ENTITY_ITEM_DEFAULT_MARKETPLACE_ID); DEFINE_PROPERTY_GROUP(KeyLight, keyLight, KeyLightPropertyGroup); DEFINE_PROPERTY_REF(PROP_VOXEL_VOLUME_SIZE, VoxelVolumeSize, voxelVolumeSize, glm::vec3, PolyVoxEntityItem::DEFAULT_VOXEL_VOLUME_SIZE); diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index 3bca911a56..2ba86a491e 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -149,7 +149,7 @@ enum EntityPropertyList { PROP_ANIMATION_HOLD, PROP_ANIMATION_START_AUTOMATICALLY, - PROP_ADDITIVE_BLENDING, + PROP_EMITTER_SHOULD_TRAIL, PROP_PARENT_ID, PROP_PARENT_JOINT_INDEX, diff --git a/libraries/entities/src/ParticleEffectEntityItem.cpp b/libraries/entities/src/ParticleEffectEntityItem.cpp index 8f6f60fa26..7d9af11862 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.cpp +++ b/libraries/entities/src/ParticleEffectEntityItem.cpp @@ -93,7 +93,7 @@ const float ParticleEffectEntityItem::DEFAULT_RADIUS_SPREAD = 0.0f; const float ParticleEffectEntityItem::DEFAULT_RADIUS_START = DEFAULT_PARTICLE_RADIUS; const float ParticleEffectEntityItem::DEFAULT_RADIUS_FINISH = DEFAULT_PARTICLE_RADIUS; const QString ParticleEffectEntityItem::DEFAULT_TEXTURES = ""; -const bool ParticleEffectEntityItem::DEFAULT_ADDITIVE_BLENDING = false; +const bool ParticleEffectEntityItem::DEFAULT_EMITTER_SHOULD_TRAIL = false; EntityItemPointer ParticleEffectEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { @@ -333,7 +333,7 @@ EntityItemProperties ParticleEffectEntityItem::getProperties(EntityPropertyFlags COPY_ENTITY_PROPERTY_TO_PROPERTIES(alphaStart, getAlphaStart); COPY_ENTITY_PROPERTY_TO_PROPERTIES(alphaFinish, getAlphaFinish); COPY_ENTITY_PROPERTY_TO_PROPERTIES(textures, getTextures); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(additiveBlending, getAdditiveBlending); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(emitterShouldTrail, getEmitterShouldTrail); return properties; @@ -372,7 +372,7 @@ bool ParticleEffectEntityItem::setProperties(const EntityItemProperties& propert SET_ENTITY_PROPERTY_FROM_PROPERTIES(alphaStart, setAlphaStart); SET_ENTITY_PROPERTY_FROM_PROPERTIES(alphaFinish, setAlphaFinish); SET_ENTITY_PROPERTY_FROM_PROPERTIES(textures, setTextures); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(additiveBlending, setAdditiveBlending); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(emitterShouldTrail, setEmitterShouldTrail); if (somethingChanged) { bool wantDebug = false; @@ -465,7 +465,7 @@ int ParticleEffectEntityItem::readEntitySubclassDataFromBuffer(const unsigned ch } if (args.bitstreamVersion >= VERSION_ENTITIES_PARTICLES_ADDITIVE_BLENDING) { - READ_ENTITY_PROPERTY(PROP_ADDITIVE_BLENDING, bool, setAdditiveBlending); + READ_ENTITY_PROPERTY(PROP_EMITTER_SHOULD_TRAIL, bool, setEmitterShouldTrail); } return bytesRead; @@ -505,7 +505,7 @@ EntityPropertyFlags ParticleEffectEntityItem::getEntityProperties(EncodeBitstrea requestedProperties += PROP_POLAR_FINISH; requestedProperties += PROP_AZIMUTH_START; requestedProperties += PROP_AZIMUTH_FINISH; - requestedProperties += PROP_ADDITIVE_BLENDING; + requestedProperties += PROP_EMITTER_SHOULD_TRAIL; return requestedProperties; } @@ -548,7 +548,7 @@ void ParticleEffectEntityItem::appendSubclassData(OctreePacketData* packetData, APPEND_ENTITY_PROPERTY(PROP_POLAR_FINISH, getPolarFinish()); APPEND_ENTITY_PROPERTY(PROP_AZIMUTH_START, getAzimuthStart()); APPEND_ENTITY_PROPERTY(PROP_AZIMUTH_FINISH, getAzimuthFinish()); - APPEND_ENTITY_PROPERTY(PROP_ADDITIVE_BLENDING, getAdditiveBlending()); + APPEND_ENTITY_PROPERTY(PROP_EMITTER_SHOULD_TRAIL, getEmitterShouldTrail()); } bool ParticleEffectEntityItem::isEmittingParticles() const { @@ -648,7 +648,9 @@ ParticleEffectEntityItem::Particle ParticleEffectEntityItem::createParticle() { std::uniform_real_distribution uniform_dist(-1.0, 1.0); particle.seed = randFloatInRange(-1.0f, 1.0f); - particle.position = getPosition(); + if (getEmitterShouldTrail()) { + particle.position = getPosition(); + } // Position, velocity, and acceleration if (_polarStart == 0.0f && _polarFinish == 0.0f && _emitDimensions.z == 0.0f) { // Emit along z-axis from position @@ -699,7 +701,12 @@ ParticleEffectEntityItem::Particle ParticleEffectEntityItem::createParticle() { radii.z > 0.0f ? z / (radii.z * radii.z) : 0.0f )); - particle.position += _emitOrientation * emitPosition; + if (getEmitterShouldTrail()) { + particle.position += _emitOrientation * emitPosition; + } + else { + particle.position = _emitOrientation * emitPosition; + } } particle.velocity = (_emitSpeed + randFloatInRange(-1.0f, 1.0f) * _speedSpread) * (_emitOrientation * emitDirection); diff --git a/libraries/entities/src/ParticleEffectEntityItem.h b/libraries/entities/src/ParticleEffectEntityItem.h index c35a45baeb..7bedbde6d8 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.h +++ b/libraries/entities/src/ParticleEffectEntityItem.h @@ -207,10 +207,10 @@ public: } } - static const bool DEFAULT_ADDITIVE_BLENDING; - bool getAdditiveBlending() const { return _additiveBlending; } - void setAdditiveBlending(bool additiveBlending) { - _additiveBlending = additiveBlending; + static const bool DEFAULT_EMITTER_SHOULD_TRAIL; + bool getEmitterShouldTrail() const { return _emitterShouldTrail; } + void setEmitterShouldTrail(bool emitterShouldTrail) { + _emitterShouldTrail = emitterShouldTrail; } virtual bool supportsDetailedRayIntersection() const { return false; } @@ -280,7 +280,7 @@ protected: float _timeUntilNextEmit { 0.0f }; - bool _additiveBlending { DEFAULT_ADDITIVE_BLENDING }; + bool _emitterShouldTrail { DEFAULT_EMITTER_SHOULD_TRAIL }; }; #endif // hifi_ParticleEffectEntityItem_h From d9c93130e5a148a2758e9c7016a16467cbd7b5b3 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Tue, 15 Dec 2015 18:02:11 -0800 Subject: [PATCH 33/94] In a state where non uniform random vals are clear --- .../flowArts/lightBall/lightBallSpawner.js | 39 +++++++------------ .../entities/src/ParticleEffectEntityItem.cpp | 2 +- 2 files changed, 14 insertions(+), 27 deletions(-) diff --git a/examples/flowArts/lightBall/lightBallSpawner.js b/examples/flowArts/lightBall/lightBallSpawner.js index 7744c1210e..dd521b4fd4 100644 --- a/examples/flowArts/lightBall/lightBallSpawner.js +++ b/examples/flowArts/lightBall/lightBallSpawner.js @@ -12,21 +12,9 @@ var raveRoom = Entities.addEntity({ }); var colorPalette = [{ - red: 250, - green: 137, + red: 25, + green: 20, blue: 162 -}, { - red: 204, - green: 244, - blue: 249 -}, { - red: 146, - green: 206, - blue: 116 -}, { - red: 240, - green: 87, - blue: 129 }]; @@ -34,7 +22,7 @@ var containerBall = Entities.addEntity({ type: "Sphere", position: center, dimensions: {x: .1, y: .1, z: .1}, - color: {red: 1500, green: 10, blue: 50}, + color: {red: 15, green: 10, blue: 150}, collisionsWillMove: true, userData: JSON.stringify({ grabbableKey: { @@ -90,13 +78,13 @@ var lightBall = Entities.addEntity({ isEmitting: true, "name": "ParticlesTest Emitter", "colorStart": {red: 200, green: 20, blue: 40}, - color: {red: 10, green: 0, blue: 255}, - "colorFinish": {red: 250, green: 200, blue:255}, + color: {red: 200, green: 200, blue: 255}, + "colorFinish": {red: 25, green: 20, blue:255}, "maxParticles": 100000, - "lifespan": 2, - "emitRate": 10000, - "emitSpeed": .2, - "speedSpread": .01, + "lifespan": 5, + "emitRate": 500, + "emitSpeed": .02, + "speedSpread": .0, "emitDimensions": { "x": 0, "y": 0, @@ -112,9 +100,9 @@ var lightBall = Entities.addEntity({ "z": 0 }, "accelerationSpread": { - "x": .01, - "y": .01, - "z": .01 + "x": .00, + "y": .00, + "z": .00 }, "particleRadius": 0.04, "radiusSpread": 0, @@ -124,9 +112,8 @@ var lightBall = Entities.addEntity({ "alphaSpread": .5, "alphaStart": 0, "alphaFinish": 0.5, - "additiveBlending": 0, "textures": "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png", - emitterShouldTrail: false + emitterShouldTrail: true }) diff --git a/libraries/entities/src/ParticleEffectEntityItem.cpp b/libraries/entities/src/ParticleEffectEntityItem.cpp index 7d9af11862..954074ec17 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.cpp +++ b/libraries/entities/src/ParticleEffectEntityItem.cpp @@ -645,7 +645,7 @@ ParticleEffectEntityItem::Particle ParticleEffectEntityItem::createParticle() { std::random_device rd; std::mt19937_64 el(rd()); - std::uniform_real_distribution uniform_dist(-1.0, 1.0); + std::uniform_real_distribution uniform_dist(0.0, 1.0); particle.seed = randFloatInRange(-1.0f, 1.0f); if (getEmitterShouldTrail()) { From 61bad36d5c1b0a0ad40747724372c121040e60d5 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Tue, 15 Dec 2015 18:49:04 -0800 Subject: [PATCH 34/94] tweaks --- examples/flowArts/lightBall/lightBallSpawner.js | 5 +++-- libraries/entities/src/ParticleEffectEntityItem.cpp | 7 ++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/examples/flowArts/lightBall/lightBallSpawner.js b/examples/flowArts/lightBall/lightBallSpawner.js index dd521b4fd4..505df7d83b 100644 --- a/examples/flowArts/lightBall/lightBallSpawner.js +++ b/examples/flowArts/lightBall/lightBallSpawner.js @@ -24,6 +24,7 @@ var containerBall = Entities.addEntity({ dimensions: {x: .1, y: .1, z: .1}, color: {red: 15, green: 10, blue: 150}, collisionsWillMove: true, + visible: false, userData: JSON.stringify({ grabbableKey: { spatialKey: { @@ -82,8 +83,8 @@ var lightBall = Entities.addEntity({ "colorFinish": {red: 25, green: 20, blue:255}, "maxParticles": 100000, "lifespan": 5, - "emitRate": 500, - "emitSpeed": .02, + "emitRate": 5000, + "emitSpeed": .1, "speedSpread": .0, "emitDimensions": { "x": 0, diff --git a/libraries/entities/src/ParticleEffectEntityItem.cpp b/libraries/entities/src/ParticleEffectEntityItem.cpp index 954074ec17..4d46439554 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.cpp +++ b/libraries/entities/src/ParticleEffectEntityItem.cpp @@ -666,13 +666,14 @@ ParticleEffectEntityItem::Particle ParticleEffectEntityItem::createParticle() { float elevationMinZ = sin(PI_OVER_TWO - _polarFinish); float elevationMaxZ = sin(PI_OVER_TWO - _polarStart); - float elevation = asin(elevationMinZ + (elevationMaxZ - elevationMinZ) * randFloat()); + // float elevation = asin(elevationMinZ + (elevationMaxZ - elevationMinZ) * randFloat()); + float elevation = asin(elevationMinZ + (elevationMaxZ - elevationMinZ) * uniform_dist(el)); float azimuth; if (_azimuthFinish >= _azimuthStart) { - azimuth = _azimuthStart + (_azimuthFinish - _azimuthStart) * randFloat(); + azimuth = _azimuthStart + (_azimuthFinish - _azimuthStart) * uniform_dist(el); } else { - azimuth = _azimuthStart + (TWO_PI + _azimuthFinish - _azimuthStart) * randFloat(); + azimuth = _azimuthStart + (TWO_PI + _azimuthFinish - _azimuthStart) * uniform_dist(el); } glm::vec3 emitDirection; From fcfa3969125e956b983401983a5964a86721968a Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Wed, 16 Dec 2015 13:58:55 -0800 Subject: [PATCH 35/94] syntax tweak --- examples/flowArts/lightBall/lightBallSpawner.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/flowArts/lightBall/lightBallSpawner.js b/examples/flowArts/lightBall/lightBallSpawner.js index 505df7d83b..20a9c312fc 100644 --- a/examples/flowArts/lightBall/lightBallSpawner.js +++ b/examples/flowArts/lightBall/lightBallSpawner.js @@ -24,7 +24,6 @@ var containerBall = Entities.addEntity({ dimensions: {x: .1, y: .1, z: .1}, color: {red: 15, green: 10, blue: 150}, collisionsWillMove: true, - visible: false, userData: JSON.stringify({ grabbableKey: { spatialKey: { @@ -85,7 +84,7 @@ var lightBall = Entities.addEntity({ "lifespan": 5, "emitRate": 5000, "emitSpeed": .1, - "speedSpread": .0, + "speedSpread": 0.0, "emitDimensions": { "x": 0, "y": 0, From b29a6b16a954f4e5427fdbe96517c65153d355ed Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Wed, 16 Dec 2015 14:40:56 -0800 Subject: [PATCH 36/94] modularizing flow art tools --- examples/flowArts/flowArtsHutSpawner.js | 77 ++++++ .../flowArts/lightBall/lightBallSpawner.js | 239 +++++++++--------- 2 files changed, 194 insertions(+), 122 deletions(-) create mode 100644 examples/flowArts/flowArtsHutSpawner.js diff --git a/examples/flowArts/flowArtsHutSpawner.js b/examples/flowArts/flowArtsHutSpawner.js new file mode 100644 index 0000000000..d3c60548ea --- /dev/null +++ b/examples/flowArts/flowArtsHutSpawner.js @@ -0,0 +1,77 @@ +// +// flowArtsHutSpawner.js +// examples +// +// Created by Eric Levin on 5/14/15. +// Copyright 2014 High Fidelity, Inc. +// +// This script creates a special flow arts hut with a numch of flow art toys people can go in and play with +// +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + + +Script.include("../../libraries/utils.js"); +Script.include("lightBall/lightBallSpawner.js"); + +var basePosition = Vec3.sum(MyAvatar.position, Vec3.multiply(1, Quat.getFront(Camera.getOrientation()))); +basePosition.y = MyAvatar.position.y + 1 +var lightBall = LightBallSpawner(basePosition); +var modelURL = "file:///C:/Users/Eric/Desktop/RaveRoom.fbx?v1" + Math.random(); + +var roomDimensions = {x: 30.58, y: 15.29, z: 30.58}; + +var raveRoom = Entities.addEntity({ + type: "Model", + modelURL: modelURL, + position: basePosition, + dimensions:roomDimensions, + visible: true +}); + +var floor = Entities.addEntity({ + type: "Box", + position: Vec3.sum(basePosition, {x: 0, y: -1.5, z: 0}), + dimensions: {x: roomDimensions.x, y: 0.6, z: roomDimensions.z}, + color: {red: 50, green: 10, blue: 100}, + shapeType: 'box' +}); + + + + + +var lightZone = Entities.addEntity({ + type: "Zone", + shapeType: 'box', + keyLightIntensity: 0.2, + keyLightColor: { + red: 50, + green: 0, + blue: 50 + }, + keyLightAmbientIntensity: .2, + position: MyAvatar.position, + dimensions: { + x: 100, + y: 100, + z: 100 + } +}); + + + + + + +function cleanup() { + + Entities.deleteEntity(raveRoom); + Entities.deleteEntity(lightZone) + Entities.deleteEntity(floor); + lightBall.cleanup(); +} + +Script.scriptEnding.connect(cleanup); \ No newline at end of file diff --git a/examples/flowArts/lightBall/lightBallSpawner.js b/examples/flowArts/lightBall/lightBallSpawner.js index 20a9c312fc..7325f16a64 100644 --- a/examples/flowArts/lightBall/lightBallSpawner.js +++ b/examples/flowArts/lightBall/lightBallSpawner.js @@ -1,129 +1,124 @@ + Script.include("../../libraries/utils.js"); -Script.include("../../libraries/utils.js"); + LightBallSpawner = function(basePosition) { -var center = Vec3.sum(MyAvatar.position, Vec3.multiply(1, Quat.getFront(Camera.getOrientation()))); -var modelURL = "file:///C:/Users/Eric/Desktop/RaveRoom.fbx?v1" + Math.random(); - -var raveRoom = Entities.addEntity({ - type: "Model", - modelURL: modelURL, - position: center, - visible:false -}); - -var colorPalette = [{ - red: 25, - green: 20, - blue: 162 -}]; + var modelURL = "file:///C:/Users/Eric/Desktop/RaveRoom.fbx?v1" + Math.random(); + var colorPalette = [{ + red: 25, + green: 20, + blue: 162 + }]; -var containerBall = Entities.addEntity({ - type: "Sphere", - position: center, - dimensions: {x: .1, y: .1, z: .1}, - color: {red: 15, green: 10, blue: 150}, - collisionsWillMove: true, - userData: JSON.stringify({ - grabbableKey: { - spatialKey: { - relativePosition: { - x: 0, - y: 1, - z: 0 - } + var containerBall = Entities.addEntity({ + type: "Sphere", + position: Vec3.sum(basePosition, { + x: 0, + y: .5, + z: 0 + }), + dimensions: { + x: .1, + y: .1, + z: .1 }, - invertSolidWhileHeld: true + color: { + red: 15, + green: 10, + blue: 150 + }, + collisionsWillMove: true, + userData: JSON.stringify({ + grabbableKey: { + spatialKey: { + relativePosition: { + x: 0, + y: 1, + z: 0 + } + }, + invertSolidWhileHeld: true + } + }) + }); + + + var light = Entities.addEntity({ + type: 'Light', + parentID: containerBall, + dimensions: { + x: 30, + y: 30, + z: 30 + }, + color: colorPalette[randInt(0, colorPalette.length)], + intensity: 5 + }); + + + var lightBall = Entities.addEntity({ + type: "ParticleEffect", + parentID: containerBall, + isEmitting: true, + "name": "ParticlesTest Emitter", + "colorStart": { + red: 200, + green: 20, + blue: 40 + }, + color: { + red: 200, + green: 200, + blue: 255 + }, + "colorFinish": { + red: 25, + green: 20, + blue: 255 + }, + "maxParticles": 100000, + "lifespan": 5, + "emitRate": 5000, + "emitSpeed": .1, + "speedSpread": 0.0, + "emitDimensions": { + "x": 0, + "y": 0, + "z": 0 + }, + "polarStart": 0, + "polarFinish": Math.PI, + "azimuthStart": -Math.PI, + "azimuthFinish": Math.PI, + "emitAcceleration": { + "x": 0, + "y": 0, + "z": 0 + }, + "accelerationSpread": { + "x": .00, + "y": .00, + "z": .00 + }, + "particleRadius": 0.04, + "radiusSpread": 0, + "radiusStart": 0.05, + "radiusFinish": 0.0003, + "alpha": 0, + "alphaSpread": .5, + "alphaStart": 0, + "alphaFinish": 0.5, + "textures": "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png", + emitterShouldTrail: true + }) + + + + function cleanup() { + Entities.deleteEntity(lightBall); + Entities.deleteEntity(containerBall); + Entities.deleteEntity(light); } - }) - // gravity: {x: 0, y: -.1, z: 0} -}); -var lightZone = Entities.addEntity({ - type: "Zone", - shapeType: 'box', - keyLightIntensity: 0.2, - keyLightColor: { - red: 50, - green: 0, - blue: 50 - }, - keyLightAmbientIntensity: .2, - position: MyAvatar.position, - dimensions: { - x: 100, - y: 100, - z: 100 - } -}); - -var light = Entities.addEntity({ - type: 'Light', - position: center, - parentID: containerBall, - dimensions: { - x: 30, - y: 30, - z: 30 - }, - color: colorPalette[randInt(0, colorPalette.length)], - intensity: 5 - }); - - -var lightBall = Entities.addEntity({ - position: center, - type: "ParticleEffect", - parentID: containerBall, - isEmitting: true, - "name": "ParticlesTest Emitter", - "colorStart": {red: 200, green: 20, blue: 40}, - color: {red: 200, green: 200, blue: 255}, - "colorFinish": {red: 25, green: 20, blue:255}, - "maxParticles": 100000, - "lifespan": 5, - "emitRate": 5000, - "emitSpeed": .1, - "speedSpread": 0.0, - "emitDimensions": { - "x": 0, - "y": 0, - "z": 0 - }, - "polarStart": 0, - "polarFinish": Math.PI, - "azimuthStart": -Math.PI, - "azimuthFinish": Math.PI, - "emitAcceleration": { - "x": 0, - "y": 0, - "z": 0 - }, - "accelerationSpread": { - "x": .00, - "y": .00, - "z": .00 - }, - "particleRadius": 0.04, - "radiusSpread": 0, - "radiusStart": 0.05, - "radiusFinish": 0.0003, - "alpha": 0, - "alphaSpread": .5, - "alphaStart": 0, - "alphaFinish": 0.5, - "textures": "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png", - emitterShouldTrail: true -}) - - - -function cleanup() { - Entities.deleteEntity(lightBall); - Entities.deleteEntity(containerBall); - Entities.deleteEntity(raveRoom); - Entities.deleteEntity(lightZone) - Entities.deleteEntity(light); -} - -Script.scriptEnding.connect(cleanup); \ No newline at end of file + this.cleanup = cleanup; + } \ No newline at end of file From 4155e3af71a9efc3bc79772364e475864d2187d6 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Wed, 16 Dec 2015 15:22:56 -0800 Subject: [PATCH 37/94] refactoring --- examples/flowArts/flowArtsHutSpawner.js | 8 +- examples/flowArts/lightBall/lightBall.js | 124 ++++++++++++++++++ .../flowArts/lightBall/lightBallSpawner.js | 124 ------------------ .../flowArts/raveStick/createRaveStick.js | 0 4 files changed, 126 insertions(+), 130 deletions(-) delete mode 100644 examples/flowArts/lightBall/lightBallSpawner.js create mode 100644 examples/flowArts/raveStick/createRaveStick.js diff --git a/examples/flowArts/flowArtsHutSpawner.js b/examples/flowArts/flowArtsHutSpawner.js index d3c60548ea..c8f7dbed78 100644 --- a/examples/flowArts/flowArtsHutSpawner.js +++ b/examples/flowArts/flowArtsHutSpawner.js @@ -14,11 +14,11 @@ Script.include("../../libraries/utils.js"); -Script.include("lightBall/lightBallSpawner.js"); +Script.include("lightBall/LightBall.js"); var basePosition = Vec3.sum(MyAvatar.position, Vec3.multiply(1, Quat.getFront(Camera.getOrientation()))); basePosition.y = MyAvatar.position.y + 1 -var lightBall = LightBallSpawner(basePosition); +var lightBall = LightBall(basePosition); var modelURL = "file:///C:/Users/Eric/Desktop/RaveRoom.fbx?v1" + Math.random(); var roomDimensions = {x: 30.58, y: 15.29, z: 30.58}; @@ -62,10 +62,6 @@ var lightZone = Entities.addEntity({ }); - - - - function cleanup() { Entities.deleteEntity(raveRoom); diff --git a/examples/flowArts/lightBall/lightBall.js b/examples/flowArts/lightBall/lightBall.js index e69de29bb2..6f44ed6d74 100644 --- a/examples/flowArts/lightBall/lightBall.js +++ b/examples/flowArts/lightBall/lightBall.js @@ -0,0 +1,124 @@ + Script.include("../../libraries/utils.js"); + + LightBall = function(basePosition) { + + var colorPalette = [{ + red: 25, + green: 20, + blue: 162 + }]; + + + var containerBall = Entities.addEntity({ + type: "Sphere", + position: Vec3.sum(basePosition, { + x: 0, + y: .5, + z: 0 + }), + dimensions: { + x: .1, + y: .1, + z: .1 + }, + color: { + red: 15, + green: 10, + blue: 150 + }, + collisionsWillMove: true, + userData: JSON.stringify({ + grabbableKey: { + spatialKey: { + relativePosition: { + x: 0, + y: 1, + z: 0 + } + }, + invertSolidWhileHeld: true + } + }) + }); + + + var light = Entities.addEntity({ + type: 'Light', + parentID: containerBall, + dimensions: { + x: 30, + y: 30, + z: 30 + }, + color: colorPalette[randInt(0, colorPalette.length)], + intensity: 5 + }); + + + var lightBall = Entities.addEntity({ + type: "ParticleEffect", + parentID: containerBall, + isEmitting: true, + "name": "ParticlesTest Emitter", + "colorStart": { + red: 200, + green: 20, + blue: 40 + }, + color: { + red: 200, + green: 200, + blue: 255 + }, + "colorFinish": { + red: 25, + green: 20, + blue: 255 + }, + "maxParticles": 100000, + "lifespan": 5, + "emitRate": 5000, + "emitSpeed": .1, + "speedSpread": 0.0, + "emitDimensions": { + "x": 0, + "y": 0, + "z": 0 + }, + "polarStart": 0, + "polarFinish": Math.PI, + "azimuthStart": -Math.PI, + "azimuthFinish": Math.PI, + "emitAcceleration": { + "x": 0, + "y": 0, + "z": 0 + }, + "accelerationSpread": { + "x": .00, + "y": .00, + "z": .00 + }, + "particleRadius": 0.04, + "radiusSpread": 0, + "radiusStart": 0.05, + "radiusFinish": 0.0003, + "alpha": 0, + "alphaSpread": .5, + "alphaStart": 0, + "alphaFinish": 0.5, + // "textures": "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png", + "textures": "file:///C:/Users/Eric/Desktop/Particle-Sprite-Smoke-1.png?v1" + Math.random(), + emitterShouldTrail: true + }) + + + + function cleanup() { + Entities.deleteEntity(lightBall); + Entities.deleteEntity(containerBall); + Entities.deleteEntity(light); + } + + this.cleanup = cleanup; + } \ No newline at end of file diff --git a/examples/flowArts/lightBall/lightBallSpawner.js b/examples/flowArts/lightBall/lightBallSpawner.js deleted file mode 100644 index 7325f16a64..0000000000 --- a/examples/flowArts/lightBall/lightBallSpawner.js +++ /dev/null @@ -1,124 +0,0 @@ - Script.include("../../libraries/utils.js"); - - LightBallSpawner = function(basePosition) { - - var modelURL = "file:///C:/Users/Eric/Desktop/RaveRoom.fbx?v1" + Math.random(); - var colorPalette = [{ - red: 25, - green: 20, - blue: 162 - }]; - - - var containerBall = Entities.addEntity({ - type: "Sphere", - position: Vec3.sum(basePosition, { - x: 0, - y: .5, - z: 0 - }), - dimensions: { - x: .1, - y: .1, - z: .1 - }, - color: { - red: 15, - green: 10, - blue: 150 - }, - collisionsWillMove: true, - userData: JSON.stringify({ - grabbableKey: { - spatialKey: { - relativePosition: { - x: 0, - y: 1, - z: 0 - } - }, - invertSolidWhileHeld: true - } - }) - }); - - - var light = Entities.addEntity({ - type: 'Light', - parentID: containerBall, - dimensions: { - x: 30, - y: 30, - z: 30 - }, - color: colorPalette[randInt(0, colorPalette.length)], - intensity: 5 - }); - - - var lightBall = Entities.addEntity({ - type: "ParticleEffect", - parentID: containerBall, - isEmitting: true, - "name": "ParticlesTest Emitter", - "colorStart": { - red: 200, - green: 20, - blue: 40 - }, - color: { - red: 200, - green: 200, - blue: 255 - }, - "colorFinish": { - red: 25, - green: 20, - blue: 255 - }, - "maxParticles": 100000, - "lifespan": 5, - "emitRate": 5000, - "emitSpeed": .1, - "speedSpread": 0.0, - "emitDimensions": { - "x": 0, - "y": 0, - "z": 0 - }, - "polarStart": 0, - "polarFinish": Math.PI, - "azimuthStart": -Math.PI, - "azimuthFinish": Math.PI, - "emitAcceleration": { - "x": 0, - "y": 0, - "z": 0 - }, - "accelerationSpread": { - "x": .00, - "y": .00, - "z": .00 - }, - "particleRadius": 0.04, - "radiusSpread": 0, - "radiusStart": 0.05, - "radiusFinish": 0.0003, - "alpha": 0, - "alphaSpread": .5, - "alphaStart": 0, - "alphaFinish": 0.5, - "textures": "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png", - emitterShouldTrail: true - }) - - - - function cleanup() { - Entities.deleteEntity(lightBall); - Entities.deleteEntity(containerBall); - Entities.deleteEntity(light); - } - - this.cleanup = cleanup; - } \ No newline at end of file diff --git a/examples/flowArts/raveStick/createRaveStick.js b/examples/flowArts/raveStick/createRaveStick.js new file mode 100644 index 0000000000..e69de29bb2 From 6a77ae5db2cbead0c901ca40e788a73929ad8bc5 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Wed, 16 Dec 2015 15:47:34 -0800 Subject: [PATCH 38/94] adding rave stick --- examples/flowArts/flowArtsHutSpawner.js | 9 +- examples/flowArts/lightBall/lightBall.js | 232 +++++++++--------- examples/flowArts/raveStick/RaveStick.js | 24 ++ .../flowArts/raveStick/createRaveStick.js | 0 4 files changed, 145 insertions(+), 120 deletions(-) create mode 100644 examples/flowArts/raveStick/RaveStick.js delete mode 100644 examples/flowArts/raveStick/createRaveStick.js diff --git a/examples/flowArts/flowArtsHutSpawner.js b/examples/flowArts/flowArtsHutSpawner.js index c8f7dbed78..ffd73dbdca 100644 --- a/examples/flowArts/flowArtsHutSpawner.js +++ b/examples/flowArts/flowArtsHutSpawner.js @@ -15,10 +15,12 @@ Script.include("../../libraries/utils.js"); Script.include("lightBall/LightBall.js"); +Script.include("raveStick/RaveStick.js"); var basePosition = Vec3.sum(MyAvatar.position, Vec3.multiply(1, Quat.getFront(Camera.getOrientation()))); -basePosition.y = MyAvatar.position.y + 1 -var lightBall = LightBall(basePosition); +basePosition.y = MyAvatar.position.y + 1; +var lightBall = new LightBall(basePosition); +var raveStick = new RaveStick(Vec3.sum(basePosition, {x: 1, y: 1, z: 1})); var modelURL = "file:///C:/Users/Eric/Desktop/RaveRoom.fbx?v1" + Math.random(); var roomDimensions = {x: 30.58, y: 15.29, z: 30.58}; @@ -41,8 +43,6 @@ var floor = Entities.addEntity({ - - var lightZone = Entities.addEntity({ type: "Zone", shapeType: 'box', @@ -68,6 +68,7 @@ function cleanup() { Entities.deleteEntity(lightZone) Entities.deleteEntity(floor); lightBall.cleanup(); + raveStick.cleanup(); } Script.scriptEnding.connect(cleanup); \ No newline at end of file diff --git a/examples/flowArts/lightBall/lightBall.js b/examples/flowArts/lightBall/lightBall.js index 6f44ed6d74..e2d65874fa 100644 --- a/examples/flowArts/lightBall/lightBall.js +++ b/examples/flowArts/lightBall/lightBall.js @@ -1,124 +1,124 @@ - Script.include("../../libraries/utils.js"); +Script.include("../../libraries/utils.js"); - LightBall = function(basePosition) { +LightBall = function(spawnPosition) { - var colorPalette = [{ + var colorPalette = [{ + red: 25, + green: 20, + blue: 162 + }]; + + + var containerBall = Entities.addEntity({ + type: "Sphere", + position: Vec3.sum(spawnPosition, { + x: 0, + y: .5, + z: 0 + }), + dimensions: { + x: .1, + y: .1, + z: .1 + }, + color: { + red: 15, + green: 10, + blue: 150 + }, + collisionsWillMove: true, + userData: JSON.stringify({ + grabbableKey: { + spatialKey: { + relativePosition: { + x: 0, + y: .7, + z: 0 + } + }, + invertSolidWhileHeld: true + } + }) + }); + + + var light = Entities.addEntity({ + type: 'Light', + parentID: containerBall, + dimensions: { + x: 30, + y: 30, + z: 30 + }, + color: colorPalette[randInt(0, colorPalette.length)], + intensity: 5 + }); + + + var lightBall = Entities.addEntity({ + type: "ParticleEffect", + parentID: containerBall, + isEmitting: true, + "name": "ParticlesTest Emitter", + "colorStart": { + red: 200, + green: 20, + blue: 40 + }, + color: { + red: 200, + green: 200, + blue: 255 + }, + "colorFinish": { red: 25, green: 20, - blue: 162 - }]; - - - var containerBall = Entities.addEntity({ - type: "Sphere", - position: Vec3.sum(basePosition, { - x: 0, - y: .5, - z: 0 - }), - dimensions: { - x: .1, - y: .1, - z: .1 - }, - color: { - red: 15, - green: 10, - blue: 150 - }, - collisionsWillMove: true, - userData: JSON.stringify({ - grabbableKey: { - spatialKey: { - relativePosition: { - x: 0, - y: 1, - z: 0 - } - }, - invertSolidWhileHeld: true - } - }) - }); - - - var light = Entities.addEntity({ - type: 'Light', - parentID: containerBall, - dimensions: { - x: 30, - y: 30, - z: 30 - }, - color: colorPalette[randInt(0, colorPalette.length)], - intensity: 5 - }); - - - var lightBall = Entities.addEntity({ - type: "ParticleEffect", - parentID: containerBall, - isEmitting: true, - "name": "ParticlesTest Emitter", - "colorStart": { - red: 200, - green: 20, - blue: 40 - }, - color: { - red: 200, - green: 200, - blue: 255 - }, - "colorFinish": { - red: 25, - green: 20, - blue: 255 - }, - "maxParticles": 100000, - "lifespan": 5, - "emitRate": 5000, - "emitSpeed": .1, - "speedSpread": 0.0, - "emitDimensions": { - "x": 0, - "y": 0, - "z": 0 - }, - "polarStart": 0, - "polarFinish": Math.PI, - "azimuthStart": -Math.PI, - "azimuthFinish": Math.PI, - "emitAcceleration": { - "x": 0, - "y": 0, - "z": 0 - }, - "accelerationSpread": { - "x": .00, - "y": .00, - "z": .00 - }, - "particleRadius": 0.04, - "radiusSpread": 0, - "radiusStart": 0.05, - "radiusFinish": 0.0003, - "alpha": 0, - "alphaSpread": .5, - "alphaStart": 0, - "alphaFinish": 0.5, - // "textures": "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png", - "textures": "file:///C:/Users/Eric/Desktop/Particle-Sprite-Smoke-1.png?v1" + Math.random(), - emitterShouldTrail: true - }) + blue: 255 + }, + "maxParticles": 100000, + "lifespan": 2, + "emitRate": 10000, + "emitSpeed": .1, + "speedSpread": 0.0, + "emitDimensions": { + "x": 0, + "y": 0, + "z": 0 + }, + "polarStart": 0, + "polarFinish": Math.PI, + "azimuthStart": -Math.PI, + "azimuthFinish": Math.PI, + "emitAcceleration": { + "x": 0, + "y": 0, + "z": 0 + }, + "accelerationSpread": { + "x": .00, + "y": .00, + "z": .00 + }, + "particleRadius": 0.02, + "radiusSpread": 0, + "radiusStart": 0.03, + "radiusFinish": 0.0003, + "alpha": 0, + "alphaSpread": .5, + "alphaStart": 0, + "alphaFinish": 0.5, + // "textures": "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png", + "textures": "file:///C:/Users/Eric/Desktop/Particle-Sprite-Smoke-1.png?v1" + Math.random(), + emitterShouldTrail: true + }) - function cleanup() { - Entities.deleteEntity(lightBall); - Entities.deleteEntity(containerBall); - Entities.deleteEntity(light); - } + function cleanup() { + Entities.deleteEntity(lightBall); + Entities.deleteEntity(containerBall); + Entities.deleteEntity(light); + } - this.cleanup = cleanup; - } \ No newline at end of file + this.cleanup = cleanup; +} \ No newline at end of file diff --git a/examples/flowArts/raveStick/RaveStick.js b/examples/flowArts/raveStick/RaveStick.js new file mode 100644 index 0000000000..2703436bec --- /dev/null +++ b/examples/flowArts/raveStick/RaveStick.js @@ -0,0 +1,24 @@ +Script.include("../../libraries/utils.js"); +var modelURL = "file:///C:/Users/Eric/Desktop/raveStick.fbx?v1" + Math.random(); + +RaveStick = function(spawnPosition) { + + var stick = Entities.addEntity({ + type: "Model", + modelURL: modelURL, + position: spawnPosition, + shapeType: 'box', + userData: JSON.stringify({ + grabbableKey: { + invertSolidWhileHeld: true + } + }) + }); + + + function cleanup() { + Entities.deleteEntity(stick); + } + + this.cleanup = cleanup; +} \ No newline at end of file diff --git a/examples/flowArts/raveStick/createRaveStick.js b/examples/flowArts/raveStick/createRaveStick.js deleted file mode 100644 index e69de29bb2..0000000000 From 263557cc1ac008e5ae98ed1c5ea977cc20aae6fa Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Wed, 16 Dec 2015 16:18:57 -0800 Subject: [PATCH 39/94] setting dimensions for rave stick --- examples/flowArts/lightBall/lightBall.js | 3 ++- examples/flowArts/raveStick/RaveStick.js | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/flowArts/lightBall/lightBall.js b/examples/flowArts/lightBall/lightBall.js index e2d65874fa..698c62a71d 100644 --- a/examples/flowArts/lightBall/lightBall.js +++ b/examples/flowArts/lightBall/lightBall.js @@ -27,12 +27,13 @@ LightBall = function(spawnPosition) { blue: 150 }, collisionsWillMove: true, + gravity: {x: 0, y: -.5, z: 0}, userData: JSON.stringify({ grabbableKey: { spatialKey: { relativePosition: { x: 0, - y: .7, + y: .1, z: 0 } }, diff --git a/examples/flowArts/raveStick/RaveStick.js b/examples/flowArts/raveStick/RaveStick.js index 2703436bec..bee5c995f9 100644 --- a/examples/flowArts/raveStick/RaveStick.js +++ b/examples/flowArts/raveStick/RaveStick.js @@ -8,6 +8,7 @@ RaveStick = function(spawnPosition) { modelURL: modelURL, position: spawnPosition, shapeType: 'box', + dimensions: {x: 0.17, y: 0.48, z: 0.17}, userData: JSON.stringify({ grabbableKey: { invertSolidWhileHeld: true From d8c877831664a37949932217b75be6937236c468 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Wed, 16 Dec 2015 16:50:27 -0800 Subject: [PATCH 40/94] rave stick in right orientation --- examples/flowArts/flowArtsHutSpawner.js | 6 +- examples/flowArts/raveStick/RaveStick.js | 16 +++- .../raveStick/raveStickEntityScript.js | 81 +++++++++++++++++++ 3 files changed, 100 insertions(+), 3 deletions(-) create mode 100644 examples/flowArts/raveStick/raveStickEntityScript.js diff --git a/examples/flowArts/flowArtsHutSpawner.js b/examples/flowArts/flowArtsHutSpawner.js index ffd73dbdca..8e39a4b1d8 100644 --- a/examples/flowArts/flowArtsHutSpawner.js +++ b/examples/flowArts/flowArtsHutSpawner.js @@ -19,8 +19,12 @@ Script.include("raveStick/RaveStick.js"); var basePosition = Vec3.sum(MyAvatar.position, Vec3.multiply(1, Quat.getFront(Camera.getOrientation()))); basePosition.y = MyAvatar.position.y + 1; + +// RAVE ITEMS var lightBall = new LightBall(basePosition); -var raveStick = new RaveStick(Vec3.sum(basePosition, {x: 1, y: 1, z: 1})); +var raveStick = new RaveStick(Vec3.sum(basePosition, {x: 1, y: 0.5, z: 1})); + + var modelURL = "file:///C:/Users/Eric/Desktop/RaveRoom.fbx?v1" + Math.random(); var roomDimensions = {x: 30.58, y: 15.29, z: 30.58}; diff --git a/examples/flowArts/raveStick/RaveStick.js b/examples/flowArts/raveStick/RaveStick.js index bee5c995f9..046932c473 100644 --- a/examples/flowArts/raveStick/RaveStick.js +++ b/examples/flowArts/raveStick/RaveStick.js @@ -1,6 +1,6 @@ Script.include("../../libraries/utils.js"); var modelURL = "file:///C:/Users/Eric/Desktop/raveStick.fbx?v1" + Math.random(); - +var scriptURL = Script.resolvePath("raveStickEntityScript.js"); RaveStick = function(spawnPosition) { var stick = Entities.addEntity({ @@ -8,9 +8,21 @@ RaveStick = function(spawnPosition) { modelURL: modelURL, position: spawnPosition, shapeType: 'box', - dimensions: {x: 0.17, y: 0.48, z: 0.17}, + dimensions: { + x: 0.06, + y: 0.06, + z: 0.8 + }, userData: JSON.stringify({ grabbableKey: { + spatialKey: { + relativePosition: { + x: 0, + y: 0, + z: -0.4 + }, + relativeRotation: Quat.fromPitchYawRollDegrees(90, 90, 0) + }, invertSolidWhileHeld: true } }) diff --git a/examples/flowArts/raveStick/raveStickEntityScript.js b/examples/flowArts/raveStick/raveStickEntityScript.js new file mode 100644 index 0000000000..cfe1e3a2fd --- /dev/null +++ b/examples/flowArts/raveStick/raveStickEntityScript.js @@ -0,0 +1,81 @@ +// raveStickEntityScript.js +// +// Script Type: Entity +// Created by Eric Levin on 9/21/15. +// Additions by James B. Pollack @imgntn on 9/24/15 +// Copyright 2015 High Fidelity, Inc. +// +// This entity script create light trails on a given object as it moves. +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +(function() { + Script.include("../../libraries/utils.js"); + var _this; + // this is the "constructor" for the entity as a JS object we don't do much here + var Doll = function() { + _this = this; + this.screamSounds = [SoundCache.getSound("https://hifi-public.s3.amazonaws.com/sounds/KenDoll_1%2303.wav")]; + }; + + Doll.prototype = { + audioInjector: null, + isGrabbed: false, + setLeftHand: function() { + this.hand = 'left'; + }, + + setRightHand: function() { + this.hand = 'right'; + }, + + startNearGrab: function() { + Entities.editEntity(this.entityID, { + animation: { + url: "https://hifi-public.s3.amazonaws.com/models/Bboys/zombie_scream.fbx", + running: true + } + }); + + var position = Entities.getEntityProperties(this.entityID, "position").position; + this.audioInjector = Audio.playSound(this.screamSounds[randInt(0, this.screamSounds.length)], { + position: position, + volume: 0.1 + }); + + this.isGrabbed = true; + this.initialHand = this.hand; + }, + + continueNearGrab: function() { + var props = Entities.getEntityProperties(this.entityID, ["position"]); + var audioOptions = { + position: props.position + }; + this.audioInjector.options = audioOptions; + }, + + releaseGrab: function() { + if (this.isGrabbed === true && this.hand === this.initialHand) { + this.audioInjector.stop(); + Entities.editEntity(this.entityID, { + animation: { + // Providing actual model fbx for animation used to work, now contorts doll into a weird ball + // See bug: https://app.asana.com/0/26225263936266/70097355490098 + // url: "http://hifi-public.s3.amazonaws.com/models/Bboys/bboy2/bboy2.fbx", + running: false, + } + }); + + this.isGrabbed = false; + } + }, + + preload: function(entityID) { + this.entityID = entityID; + }, + }; + // entity scripts always need to return a newly constructed object of our type + return new Doll(); +}); From f9f496ef5ab2d0e65e9d20500fb6f6fe521492d2 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Wed, 16 Dec 2015 17:32:30 -0800 Subject: [PATCH 41/94] adding beam effect --- examples/flowArts/flowArtsHutSpawner.js | 2 +- examples/flowArts/raveStick/RaveStick.js | 5 +- .../raveStick/raveStickEntityScript.js | 128 +++++++++++------- 3 files changed, 85 insertions(+), 50 deletions(-) diff --git a/examples/flowArts/flowArtsHutSpawner.js b/examples/flowArts/flowArtsHutSpawner.js index 8e39a4b1d8..aa87ddbd10 100644 --- a/examples/flowArts/flowArtsHutSpawner.js +++ b/examples/flowArts/flowArtsHutSpawner.js @@ -50,7 +50,7 @@ var floor = Entities.addEntity({ var lightZone = Entities.addEntity({ type: "Zone", shapeType: 'box', - keyLightIntensity: 0.2, + keyLightIntensity: 0.4, keyLightColor: { red: 50, green: 0, diff --git a/examples/flowArts/raveStick/RaveStick.js b/examples/flowArts/raveStick/RaveStick.js index 046932c473..5151b528cc 100644 --- a/examples/flowArts/raveStick/RaveStick.js +++ b/examples/flowArts/raveStick/RaveStick.js @@ -8,10 +8,11 @@ RaveStick = function(spawnPosition) { modelURL: modelURL, position: spawnPosition, shapeType: 'box', + script: scriptURL, dimensions: { x: 0.06, y: 0.06, - z: 0.8 + z: 0.31 }, userData: JSON.stringify({ grabbableKey: { @@ -19,7 +20,7 @@ RaveStick = function(spawnPosition) { relativePosition: { x: 0, y: 0, - z: -0.4 + z: -0.1 }, relativeRotation: Quat.fromPitchYawRollDegrees(90, 90, 0) }, diff --git a/examples/flowArts/raveStick/raveStickEntityScript.js b/examples/flowArts/raveStick/raveStickEntityScript.js index cfe1e3a2fd..67571e49fa 100644 --- a/examples/flowArts/raveStick/raveStickEntityScript.js +++ b/examples/flowArts/raveStick/raveStickEntityScript.js @@ -1,8 +1,7 @@ // raveStickEntityScript.js // // Script Type: Entity -// Created by Eric Levin on 9/21/15. -// Additions by James B. Pollack @imgntn on 9/24/15 +// Created by Eric Levin on 12/16/15. // Copyright 2015 High Fidelity, Inc. // // This entity script create light trails on a given object as it moves. @@ -14,68 +13,103 @@ Script.include("../../libraries/utils.js"); var _this; // this is the "constructor" for the entity as a JS object we don't do much here - var Doll = function() { + var RaveStick = function() { _this = this; - this.screamSounds = [SoundCache.getSound("https://hifi-public.s3.amazonaws.com/sounds/KenDoll_1%2303.wav")]; }; - Doll.prototype = { - audioInjector: null, + RaveStick.prototype = { isGrabbed: false, - setLeftHand: function() { - this.hand = 'left'; - }, - - setRightHand: function() { - this.hand = 'right'; - }, startNearGrab: function() { - Entities.editEntity(this.entityID, { - animation: { - url: "https://hifi-public.s3.amazonaws.com/models/Bboys/zombie_scream.fbx", - running: true - } - }); - - var position = Entities.getEntityProperties(this.entityID, "position").position; - this.audioInjector = Audio.playSound(this.screamSounds[randInt(0, this.screamSounds.length)], { - position: position, - volume: 0.1 - }); - - this.isGrabbed = true; - this.initialHand = this.hand; + // this.createBeam(); }, continueNearGrab: function() { - var props = Entities.getEntityProperties(this.entityID, ["position"]); - var audioOptions = { - position: props.position - }; - this.audioInjector.options = audioOptions; + }, releaseGrab: function() { - if (this.isGrabbed === true && this.hand === this.initialHand) { - this.audioInjector.stop(); - Entities.editEntity(this.entityID, { - animation: { - // Providing actual model fbx for animation used to work, now contorts doll into a weird ball - // See bug: https://app.asana.com/0/26225263936266/70097355490098 - // url: "http://hifi-public.s3.amazonaws.com/models/Bboys/bboy2/bboy2.fbx", - running: false, - } - }); - this.isGrabbed = false; - } }, preload: function(entityID) { this.entityID = entityID; + this.createBeam(); }, + + unload: function() { + Entities.deleteEntity(this.beam); + }, + + createBeam: function() { + + var props = Entities.getEntityProperties(this.entityID, ["position", "rotation"]); + var forwardVec = Quat.getFront(Quat.multiply(props.rotation, Quat.fromPitchYawRollDegrees(-90, 0, 0))); + forwardVec = Vec3.normalize(forwardVec); + var forwardQuat = orientationOf(forwardVec); + var position = Vec3.sum(props.position, Vec3.multiply(Quat.getFront(props.rotation), 0.1)); + position.z += 0.1; + position.x += -0.035; + var props = { + type: "ParticleEffect", + position: position, + parentID: this.entityID, + isEmitting: true, + "name": "ParticlesTest Emitter", + "colorStart": { + red: 0, + green: 200, + blue: 40 + }, + color: { + red: 200, + green: 200, + blue: 255 + }, + "colorFinish": { + red: 25, + green: 200, + blue: 5 + }, + "maxParticles": 100000, + "lifespan": 2, + "emitRate": 1000, + emitOrientation: forwardQuat, + "emitSpeed": .4, + "speedSpread": 0.0, + // "emitDimensions": { + // "x": .1, + // "y": .1, + // "z": .1 + // }, + "polarStart": 0, + "polarFinish": .0, + "azimuthStart": .1, + "azimuthFinish": .01, + "emitAcceleration": { + "x": 0, + "y": 0, + "z": 0 + }, + "accelerationSpread": { + "x": .00, + "y": .00, + "z": .00 + }, + "radiusStart": 0.03, + radiusFinish: 0.025, + "alpha": 0.7, + "alphaSpread": .1, + "alphaStart": 0.5, + "alphaFinish": 0.5, + // "textures": "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png", + "textures": "file:///C:/Users/Eric/Desktop/beamParticle.png?v1" + Math.random(), + emitterShouldTrail: false + } + this.beam = Entities.addEntity(props); + + } }; // entity scripts always need to return a newly constructed object of our type - return new Doll(); -}); + return new RaveStick(); +}); \ No newline at end of file From bb67ca87e238a4db2b613e15c3de787d93544955 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Wed, 16 Dec 2015 17:52:39 -0800 Subject: [PATCH 42/94] lightsabers --- examples/flowArts/flowArtsHutSpawner.js | 2 ++ .../raveStick/raveStickEntityScript.js | 26 ++++++++++++------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/examples/flowArts/flowArtsHutSpawner.js b/examples/flowArts/flowArtsHutSpawner.js index aa87ddbd10..b9d89859df 100644 --- a/examples/flowArts/flowArtsHutSpawner.js +++ b/examples/flowArts/flowArtsHutSpawner.js @@ -23,6 +23,7 @@ basePosition.y = MyAvatar.position.y + 1; // RAVE ITEMS var lightBall = new LightBall(basePosition); var raveStick = new RaveStick(Vec3.sum(basePosition, {x: 1, y: 0.5, z: 1})); +var raveStick2 = new RaveStick(Vec3.sum(basePosition, {x: 2, y: 0.5, z: 1})); var modelURL = "file:///C:/Users/Eric/Desktop/RaveRoom.fbx?v1" + Math.random(); @@ -73,6 +74,7 @@ function cleanup() { Entities.deleteEntity(floor); lightBall.cleanup(); raveStick.cleanup(); + raveStick2.cleanup(); } Script.scriptEnding.connect(cleanup); \ No newline at end of file diff --git a/examples/flowArts/raveStick/raveStickEntityScript.js b/examples/flowArts/raveStick/raveStickEntityScript.js index 67571e49fa..0372fd388f 100644 --- a/examples/flowArts/raveStick/raveStickEntityScript.js +++ b/examples/flowArts/raveStick/raveStickEntityScript.js @@ -15,6 +15,15 @@ // this is the "constructor" for the entity as a JS object we don't do much here var RaveStick = function() { _this = this; + this.colorPalette = [{ + red: 0, + green: 200, + blue: 40 + }, { + red: 200, + green: 10, + blue: 40 + }]; }; RaveStick.prototype = { @@ -39,6 +48,7 @@ unload: function() { Entities.deleteEntity(this.beam); + // Entities.deleteEntity(this.beamTrail); }, createBeam: function() { @@ -50,27 +60,20 @@ var position = Vec3.sum(props.position, Vec3.multiply(Quat.getFront(props.rotation), 0.1)); position.z += 0.1; position.x += -0.035; + var color = this.colorPalette[randInt(0, this.colorPalette.length)]; var props = { type: "ParticleEffect", position: position, parentID: this.entityID, isEmitting: true, "name": "ParticlesTest Emitter", - "colorStart": { - red: 0, - green: 200, - blue: 40 - }, + "colorStart": color, color: { red: 200, green: 200, blue: 255 }, - "colorFinish": { - red: 25, - green: 200, - blue: 5 - }, + "colorFinish": color, "maxParticles": 100000, "lifespan": 2, "emitRate": 1000, @@ -108,6 +111,9 @@ } this.beam = Entities.addEntity(props); + // props.emitterShouldTrail = true; + // this.beamTrail = Entities.addEntity(props); + } }; // entity scripts always need to return a newly constructed object of our type From b127b6a9e887874b0cfa253c9dcb447a2e91f7d4 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Thu, 17 Dec 2015 08:55:57 -0800 Subject: [PATCH 43/94] separating out rave stick --- examples/flowArts/flowArtsHutSpawner.js | 5 + examples/flowArts/lightSaber/LightSaber.js | 38 ++++++ .../lightSaber/lightSaberEntityScript.js | 121 ++++++++++++++++++ 3 files changed, 164 insertions(+) create mode 100644 examples/flowArts/lightSaber/LightSaber.js create mode 100644 examples/flowArts/lightSaber/lightSaberEntityScript.js diff --git a/examples/flowArts/flowArtsHutSpawner.js b/examples/flowArts/flowArtsHutSpawner.js index b9d89859df..920fcc90ea 100644 --- a/examples/flowArts/flowArtsHutSpawner.js +++ b/examples/flowArts/flowArtsHutSpawner.js @@ -16,6 +16,9 @@ Script.include("../../libraries/utils.js"); Script.include("lightBall/LightBall.js"); Script.include("raveStick/RaveStick.js"); +Script.include("lightSaber/LightSaber.js"); + + var basePosition = Vec3.sum(MyAvatar.position, Vec3.multiply(1, Quat.getFront(Camera.getOrientation()))); basePosition.y = MyAvatar.position.y + 1; @@ -24,6 +27,7 @@ basePosition.y = MyAvatar.position.y + 1; var lightBall = new LightBall(basePosition); var raveStick = new RaveStick(Vec3.sum(basePosition, {x: 1, y: 0.5, z: 1})); var raveStick2 = new RaveStick(Vec3.sum(basePosition, {x: 2, y: 0.5, z: 1})); +var lightSaber = new LightSaber(Vec3.sum(basePosition, {x: 3, y: 0.5, z: 1})); var modelURL = "file:///C:/Users/Eric/Desktop/RaveRoom.fbx?v1" + Math.random(); @@ -75,6 +79,7 @@ function cleanup() { lightBall.cleanup(); raveStick.cleanup(); raveStick2.cleanup(); + lightSaber.cleanup(); } Script.scriptEnding.connect(cleanup); \ No newline at end of file diff --git a/examples/flowArts/lightSaber/LightSaber.js b/examples/flowArts/lightSaber/LightSaber.js new file mode 100644 index 0000000000..12ac3f3b60 --- /dev/null +++ b/examples/flowArts/lightSaber/LightSaber.js @@ -0,0 +1,38 @@ +Script.include("../../libraries/utils.js"); +var modelURL = "file:///C:/Users/Eric/Desktop/lightSaber.fbx?v1" + Math.random(); +var scriptURL = Script.resolvePath("lightSaberEntityScript.js"); +LightSaber = function(spawnPosition) { + + var stick = Entities.addEntity({ + type: "Model", + modelURL: modelURL, + position: spawnPosition, + shapeType: 'box', + script: scriptURL, + dimensions: { + x: 0.06, + y: 0.06, + z: 0.31 + }, + userData: JSON.stringify({ + grabbableKey: { + spatialKey: { + relativePosition: { + x: 0, + y: 0, + z: -0.1 + }, + relativeRotation: Quat.fromPitchYawRollDegrees(90, 90, 0) + }, + invertSolidWhileHeld: true + } + }) + }); + + + function cleanup() { + Entities.deleteEntity(stick); + } + + this.cleanup = cleanup; +} \ No newline at end of file diff --git a/examples/flowArts/lightSaber/lightSaberEntityScript.js b/examples/flowArts/lightSaber/lightSaberEntityScript.js new file mode 100644 index 0000000000..5ff3c65afd --- /dev/null +++ b/examples/flowArts/lightSaber/lightSaberEntityScript.js @@ -0,0 +1,121 @@ +// lightSaberEntityScript.js +// +// Script Type: Entity +// Created by Eric Levin on 12/16/15. +// Copyright 2015 High Fidelity, Inc. +// +// This entity script creates a lightsaber. +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +(function() { + Script.include("../../libraries/utils.js"); + var _this; + // this is the "constructor" for the entity as a JS object we don't do much here + var LightSaber = function() { + _this = this; + this.colorPalette = [{ + red: 0, + green: 200, + blue: 40 + }, { + red: 200, + green: 10, + blue: 40 + }]; + }; + + LightSaber.prototype = { + isGrabbed: false, + + startNearGrab: function() { + // this.createBeam(); + this.createBeam(); + }, + + continueNearGrab: function() { + + }, + + releaseGrab: function() { + + }, + + preload: function(entityID) { + this.entityID = entityID; + }, + + unload: function() { + Entities.deleteEntity(this.beam); + // Entities.deleteEntity(this.beamTrail); + }, + + createBeam: function() { + + var props = Entities.getEntityProperties(this.entityID, ["position", "rotation"]); + var forwardVec = Quat.getFront(Quat.multiply(props.rotation, Quat.fromPitchYawRollDegrees(-90, 0, 0))); + forwardVec = Vec3.normalize(forwardVec); + var forwardQuat = orientationOf(forwardVec); + var position = Vec3.sum(props.position, Vec3.multiply(Quat.getFront(props.rotation), 0.1)); + position.z += 0.1; + position.x += -0.035; + var color = this.colorPalette[randInt(0, this.colorPalette.length)]; + var props = { + type: "ParticleEffect", + position: position, + parentID: this.entityID, + isEmitting: true, + "name": "ParticlesTest Emitter", + "colorStart": color, + color: { + red: 200, + green: 200, + blue: 255 + }, + "colorFinish": color, + "maxParticles": 100000, + "lifespan": 2, + "emitRate": 1000, + emitOrientation: forwardQuat, + "emitSpeed": .4, + "speedSpread": 0.0, + // "emitDimensions": { + // "x": .1, + // "y": .1, + // "z": .1 + // }, + "polarStart": 0, + "polarFinish": .0, + "azimuthStart": .1, + "azimuthFinish": .01, + "emitAcceleration": { + "x": 0, + "y": 0, + "z": 0 + }, + "accelerationSpread": { + "x": .00, + "y": .00, + "z": .00 + }, + "radiusStart": 0.03, + radiusFinish: 0.025, + "alpha": 0.7, + "alphaSpread": .1, + "alphaStart": 0.5, + "alphaFinish": 0.5, + // "textures": "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png", + "textures": "file:///C:/Users/Eric/Desktop/beamParticle.png?v1" + Math.random(), + emitterShouldTrail: false + } + this.beam = Entities.addEntity(props); + + // props.emitterShouldTrail = true; + // this.beamTrail = Entities.addEntity(props); + + } + }; + // entity scripts always need to return a newly constructed object of our type + return new LightSaber(); +}); \ No newline at end of file From 064c05e98cb3f421e36ed3c827af4b643d86996a Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Thu, 17 Dec 2015 10:04:51 -0800 Subject: [PATCH 44/94] light trails --- examples/flowArts/flowArtsHutSpawner.js | 2 +- examples/flowArts/lightBall/particles.js | 129 ++++++++++++++++++ .../lightSaber/lightSaberEntityScript.js | 21 +-- .../raveStick/raveStickEntityScript.js | 49 ++++++- 4 files changed, 189 insertions(+), 12 deletions(-) create mode 100644 examples/flowArts/lightBall/particles.js diff --git a/examples/flowArts/flowArtsHutSpawner.js b/examples/flowArts/flowArtsHutSpawner.js index 920fcc90ea..1ed3325b9c 100644 --- a/examples/flowArts/flowArtsHutSpawner.js +++ b/examples/flowArts/flowArtsHutSpawner.js @@ -27,7 +27,7 @@ basePosition.y = MyAvatar.position.y + 1; var lightBall = new LightBall(basePosition); var raveStick = new RaveStick(Vec3.sum(basePosition, {x: 1, y: 0.5, z: 1})); var raveStick2 = new RaveStick(Vec3.sum(basePosition, {x: 2, y: 0.5, z: 1})); -var lightSaber = new LightSaber(Vec3.sum(basePosition, {x: 3, y: 0.5, z: 1})); +// var lightSaber = new LightSaber(Vec3.sum(basePosition, {x: 3, y: 0.5, z: 1})); var modelURL = "file:///C:/Users/Eric/Desktop/RaveRoom.fbx?v1" + Math.random(); diff --git a/examples/flowArts/lightBall/particles.js b/examples/flowArts/lightBall/particles.js new file mode 100644 index 0000000000..86616d5099 --- /dev/null +++ b/examples/flowArts/lightBall/particles.js @@ -0,0 +1,129 @@ +Script.include("../../libraries/utils.js"); + +LightBall = function(spawnPosition) { + + var colorPalette = [{ + red: 25, + green: 20, + blue: 162 + }]; + + + var containerBall = Entities.addEntity({ + type: "Sphere", + position: Vec3.sum(spawnPosition, { + x: 0, + y: .5, + z: 0 + }), + dimensions: { + x: .1, + y: .1, + z: .1 + }, + color: { + red: 15, + green: 10, + blue: 150 + }, + collisionsWillMove: true, + gravity: {x: 0, y: -.5, z: 0}, + visible: false, + userData: JSON.stringify({ + grabbableKey: { + spatialKey: { + relativePosition: { + x: 0, + y: .1, + z: 0 + } + }, + invertSolidWhileHeld: true + } + }) + }); + + + var light = Entities.addEntity({ + type: 'Light', + parentID: containerBall, + dimensions: { + x: 30, + y: 30, + z: 30 + }, + color: colorPalette[randInt(0, colorPalette.length)], + intensity: 5 + }); + + + var lightBall = Entities.addEntity({ + type: "ParticleEffect", + position: spawnPosition, + // parentID: containerBall, + isEmitting: true, + "name": "ParticlesTest Emitter", + "colorStart": { + red: 200, + green: 20, + blue: 40 + }, + color: { + red: 200, + green: 200, + blue: 255 + }, + "colorFinish": { + red: 25, + green: 20, + blue: 255 + }, + "maxParticles": 100000, + "lifespan": 2, + "emitRate": 10000, + "emitSpeed": .0, + "speedSpread": 0.0, + "emitDimensions": { + "x": 5, + "y":0 , + "z": 0 + }, + // "polarStart": 0, + // "polarFinish": Math.PI, + // "azimuthStart": -Math.PI, + // "azimuthFinish": Math.PI, + "emitAcceleration": { + "x": 0, + "y": 0, + "z": 0 + }, + "accelerationSpread": { + "x": .00, + "y": .0, + "z": .00 + }, + "particleRadius": 0.02, + "radiusSpread": 0, + "radiusStart": 0.03, + "radiusFinish": 0.0003, + "alpha": 0, + "alphaSpread": .5, + "alphaStart": 0, + "alphaFinish": 0.5, + // "textures": "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png", + "textures": "file:///C:/Users/Eric/Desktop/Particle-Sprite-Smoke-1.png?v1" + Math.random(), + emitterShouldTrail: true + }) + + + + function cleanup() { + Entities.deleteEntity(lightBall); + Entities.deleteEntity(containerBall); + Entities.deleteEntity(light); + } + + this.cleanup = cleanup; +} + +LightBall(); \ No newline at end of file diff --git a/examples/flowArts/lightSaber/lightSaberEntityScript.js b/examples/flowArts/lightSaber/lightSaberEntityScript.js index 5ff3c65afd..ea1f408abd 100644 --- a/examples/flowArts/lightSaber/lightSaberEntityScript.js +++ b/examples/flowArts/lightSaber/lightSaberEntityScript.js @@ -31,7 +31,7 @@ startNearGrab: function() { // this.createBeam(); - this.createBeam(); + }, continueNearGrab: function() { @@ -44,6 +44,7 @@ preload: function(entityID) { this.entityID = entityID; + this.createBeam(); }, unload: function() { @@ -53,11 +54,12 @@ createBeam: function() { - var props = Entities.getEntityProperties(this.entityID, ["position", "rotation"]); - var forwardVec = Quat.getFront(Quat.multiply(props.rotation, Quat.fromPitchYawRollDegrees(-90, 0, 0))); + + this.props = Entities.getEntityProperties(this.entityID, ["position", "rotation"]); + var forwardVec = Quat.getFront(Quat.multiply(this.props.rotation, Quat.fromPitchYawRollDegrees(-90, 0, 0))); forwardVec = Vec3.normalize(forwardVec); var forwardQuat = orientationOf(forwardVec); - var position = Vec3.sum(props.position, Vec3.multiply(Quat.getFront(props.rotation), 0.1)); + var position = Vec3.sum(this.props.position, Vec3.multiply(Quat.getFront(this.props.rotation), 0.1)); position.z += 0.1; position.x += -0.035; var color = this.colorPalette[randInt(0, this.colorPalette.length)]; @@ -66,7 +68,6 @@ position: position, parentID: this.entityID, isEmitting: true, - "name": "ParticlesTest Emitter", "colorStart": color, color: { red: 200, @@ -80,11 +81,11 @@ emitOrientation: forwardQuat, "emitSpeed": .4, "speedSpread": 0.0, - // "emitDimensions": { - // "x": .1, - // "y": .1, - // "z": .1 - // }, + "emitDimensions": { + "x": 0, + "y": 0, + "z": 0 + }, "polarStart": 0, "polarFinish": .0, "azimuthStart": .1, diff --git a/examples/flowArts/raveStick/raveStickEntityScript.js b/examples/flowArts/raveStick/raveStickEntityScript.js index 0372fd388f..95e37ee79b 100644 --- a/examples/flowArts/raveStick/raveStickEntityScript.js +++ b/examples/flowArts/raveStick/raveStickEntityScript.js @@ -13,6 +13,11 @@ Script.include("../../libraries/utils.js"); var _this; // this is the "constructor" for the entity as a JS object we don't do much here + var LIFETIME = 6000; + var DRAWING_DEPTH = 0.8; + var LINE_DIMENSIONS = 100; + var MAX_POINTS_PER_LINE = 50; + var MIN_POINT_DISTANCE = 0.02; var RaveStick = function() { _this = this; this.colorPalette = [{ @@ -24,6 +29,21 @@ green: 10, blue: 40 }]; + var texture = "https://s3.amazonaws.com/hifi-public/eric/textures/paintStrokes/trails.png"; + this.trail = Entities.addEntity({ + type: "PolyLine", + dimensions: {x: LINE_DIMENSIONS, y: LINE_DIMENSIONS, z: LINE_DIMENSIONS}, + color: { + red: 255, + green: 255, + blue: 255 + }, + textures: texture, + lifetime: LIFETIME + }) + this.points = []; + this.normals = []; + this.strokeWidths = []; }; RaveStick.prototype = { @@ -31,9 +51,31 @@ startNearGrab: function() { // this.createBeam(); + this.trailBasePosition = Entities.getEntityProperties(this.entityID, "position").position; + Entities.editEntity(this.trail, { + position: this.trailBasePosition + }); + this.points = []; + this.normals = []; + this.strokeWidths = []; }, continueNearGrab: function() { + var position = Entities.getEntityProperties(this.entityID, "position").position; + var localPoint = Vec3.subtract(position, this.trailBasePosition); + if (this.points.length >=1 && Vec3.distance(localPoint, this.points[this.points.length - 1]) < MIN_POINT_DISTANCE) { + //Need a minimum distance to avoid binormal NANs + return; + } + this.points.push(localPoint); + var normal = computeNormal(position, Camera.getPosition()); + this.normals.push(normal); + this.strokeWidths.push(0.04); + Entities.editEntity(this.trail, { + linePoints: this.points, + normals: this.normals, + strokeWidths: this.strokeWidths + }) }, @@ -48,6 +90,7 @@ unload: function() { Entities.deleteEntity(this.beam); + Entities.deleteEntity(this.trail); // Entities.deleteEntity(this.beamTrail); }, @@ -60,7 +103,7 @@ var position = Vec3.sum(props.position, Vec3.multiply(Quat.getFront(props.rotation), 0.1)); position.z += 0.1; position.x += -0.035; - var color = this.colorPalette[randInt(0, this.colorPalette.length)]; + var color = this.colorPalette[randInt(0, this.colorPalette.length)]; var props = { type: "ParticleEffect", position: position, @@ -118,4 +161,8 @@ }; // entity scripts always need to return a newly constructed object of our type return new RaveStick(); + + function computeNormal(p1, p2) { + return Vec3.normalize(Vec3.subtract(p2, p1)); + } }); \ No newline at end of file From f7811cf5dc5bc33af18788a21b8c46d9ad8d8fe7 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Thu, 17 Dec 2015 10:25:51 -0800 Subject: [PATCH 45/94] light trails from rave stick --- .../raveStick/raveStickEntityScript.js | 61 ++++++++++++++----- 1 file changed, 46 insertions(+), 15 deletions(-) diff --git a/examples/flowArts/raveStick/raveStickEntityScript.js b/examples/flowArts/raveStick/raveStickEntityScript.js index 95e37ee79b..7b84f473ea 100644 --- a/examples/flowArts/raveStick/raveStickEntityScript.js +++ b/examples/flowArts/raveStick/raveStickEntityScript.js @@ -18,6 +18,8 @@ var LINE_DIMENSIONS = 100; var MAX_POINTS_PER_LINE = 50; var MIN_POINT_DISTANCE = 0.02; + var STROKE_WIDTH = 0.05 + var ugLSD = 25; var RaveStick = function() { _this = this; this.colorPalette = [{ @@ -32,7 +34,11 @@ var texture = "https://s3.amazonaws.com/hifi-public/eric/textures/paintStrokes/trails.png"; this.trail = Entities.addEntity({ type: "PolyLine", - dimensions: {x: LINE_DIMENSIONS, y: LINE_DIMENSIONS, z: LINE_DIMENSIONS}, + dimensions: { + x: LINE_DIMENSIONS, + y: LINE_DIMENSIONS, + z: LINE_DIMENSIONS + }, color: { red: 255, green: 255, @@ -58,29 +64,58 @@ this.points = []; this.normals = []; this.strokeWidths = []; + this.setupEraseInterval(); }, continueNearGrab: function() { - var position = Entities.getEntityProperties(this.entityID, "position").position; + var props = Entities.getEntityProperties(this.entityID, ["position", "rotation"]); + var forwardVec = Quat.getFront(Quat.multiply(props.rotation, Quat.fromPitchYawRollDegrees(-90, 0, 0))); + forwardVec = Vec3.normalize(forwardVec); + var forwardQuat = orientationOf(forwardVec); + var position = Vec3.sum(props.position, Vec3.multiply(Quat.getFront(props.rotation), 0.2)); + // position.z += 0.1; + // position.x += -0.035; var localPoint = Vec3.subtract(position, this.trailBasePosition); - if (this.points.length >=1 && Vec3.distance(localPoint, this.points[this.points.length - 1]) < MIN_POINT_DISTANCE) { + if (this.points.length >= 1 && Vec3.distance(localPoint, this.points[this.points.length - 1]) < MIN_POINT_DISTANCE) { //Need a minimum distance to avoid binormal NANs return; } + if (this.points.length === MAX_POINTS_PER_LINE) { + this.points.shift(); + this.normals.shift(); + this.strokeWidths.shift(); + } + this.points.push(localPoint); - var normal = computeNormal(position, Camera.getPosition()); + var normal = Quat.getUp(props.rotation); this.normals.push(normal); - this.strokeWidths.push(0.04); + this.strokeWidths.push(STROKE_WIDTH); Entities.editEntity(this.trail, { linePoints: this.points, normals: this.normals, strokeWidths: this.strokeWidths - }) + }); + }, - releaseGrab: function() { + setupEraseInterval: function() { + this.trailEraseInterval = Script.setInterval(function() { + if (_this.points.length > 0) { + _this.points.shift(); + _this.normals.shift(); + _this.strokeWidths.shift(); + Entities.editEntity(_this.trail, { + linePoints: _this.points, + strokeWidths: _this.strokeWidths, + normals: _this.normals + }); + } + }, ugLSD); + }, + releaseGrab: function() { + Script.clearInterval(this.trailEraseInterval); }, preload: function(entityID) { @@ -91,7 +126,7 @@ unload: function() { Entities.deleteEntity(this.beam); Entities.deleteEntity(this.trail); - // Entities.deleteEntity(this.beamTrail); + Script.clearInterval(this.trailEraseInterval); }, createBeam: function() { @@ -111,6 +146,7 @@ isEmitting: true, "name": "ParticlesTest Emitter", "colorStart": color, + colorSpread: {red: 200, green : 10, blue: 10}, color: { red: 200, green: 200, @@ -118,16 +154,11 @@ }, "colorFinish": color, "maxParticles": 100000, - "lifespan": 2, + "lifespan": 1, "emitRate": 1000, emitOrientation: forwardQuat, - "emitSpeed": .4, + "emitSpeed": .2, "speedSpread": 0.0, - // "emitDimensions": { - // "x": .1, - // "y": .1, - // "z": .1 - // }, "polarStart": 0, "polarFinish": .0, "azimuthStart": .1, From 57e51afd46c661db3545cef4345327137abc2338 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Thu, 17 Dec 2015 10:35:20 -0800 Subject: [PATCH 46/94] light stick --- examples/flowArts/flowArtsHutSpawner.js | 4 +--- examples/flowArts/lightSaber/lightSaberEntityScript.js | 6 +++--- examples/flowArts/raveStick/raveStickEntityScript.js | 5 ++++- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/examples/flowArts/flowArtsHutSpawner.js b/examples/flowArts/flowArtsHutSpawner.js index 1ed3325b9c..2f9b2052d1 100644 --- a/examples/flowArts/flowArtsHutSpawner.js +++ b/examples/flowArts/flowArtsHutSpawner.js @@ -26,8 +26,7 @@ basePosition.y = MyAvatar.position.y + 1; // RAVE ITEMS var lightBall = new LightBall(basePosition); var raveStick = new RaveStick(Vec3.sum(basePosition, {x: 1, y: 0.5, z: 1})); -var raveStick2 = new RaveStick(Vec3.sum(basePosition, {x: 2, y: 0.5, z: 1})); -// var lightSaber = new LightSaber(Vec3.sum(basePosition, {x: 3, y: 0.5, z: 1})); +var lightSaber = new LightSaber(Vec3.sum(basePosition, {x: 3, y: 0.5, z: 1})); var modelURL = "file:///C:/Users/Eric/Desktop/RaveRoom.fbx?v1" + Math.random(); @@ -78,7 +77,6 @@ function cleanup() { Entities.deleteEntity(floor); lightBall.cleanup(); raveStick.cleanup(); - raveStick2.cleanup(); lightSaber.cleanup(); } diff --git a/examples/flowArts/lightSaber/lightSaberEntityScript.js b/examples/flowArts/lightSaber/lightSaberEntityScript.js index ea1f408abd..5175de25bb 100644 --- a/examples/flowArts/lightSaber/lightSaberEntityScript.js +++ b/examples/flowArts/lightSaber/lightSaberEntityScript.js @@ -31,7 +31,7 @@ startNearGrab: function() { // this.createBeam(); - + }, continueNearGrab: function() { @@ -44,7 +44,7 @@ preload: function(entityID) { this.entityID = entityID; - this.createBeam(); + this.createBeam(); }, unload: function() { @@ -62,7 +62,7 @@ var position = Vec3.sum(this.props.position, Vec3.multiply(Quat.getFront(this.props.rotation), 0.1)); position.z += 0.1; position.x += -0.035; - var color = this.colorPalette[randInt(0, this.colorPalette.length)]; + var color = this.colorPalette[randInt(0, this.colorPalette.length)]; var props = { type: "ParticleEffect", position: position, diff --git a/examples/flowArts/raveStick/raveStickEntityScript.js b/examples/flowArts/raveStick/raveStickEntityScript.js index 7b84f473ea..20c21d3a79 100644 --- a/examples/flowArts/raveStick/raveStickEntityScript.js +++ b/examples/flowArts/raveStick/raveStickEntityScript.js @@ -116,6 +116,7 @@ releaseGrab: function() { Script.clearInterval(this.trailEraseInterval); + this.trailEraseInterval = null; }, preload: function(entityID) { @@ -126,7 +127,9 @@ unload: function() { Entities.deleteEntity(this.beam); Entities.deleteEntity(this.trail); - Script.clearInterval(this.trailEraseInterval); + if(this.trailEraseInterval) { + Script.clearInterval(this.trailEraseInterval); + } }, createBeam: function() { From d4481818b2e991512afb6e8819ed5c910d7cf801 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Thu, 17 Dec 2015 10:50:58 -0800 Subject: [PATCH 47/94] lightsaber oriented right --- .../flowArts/lightSaber/lightSaberEntityScript.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/examples/flowArts/lightSaber/lightSaberEntityScript.js b/examples/flowArts/lightSaber/lightSaberEntityScript.js index 5175de25bb..2faf0abff9 100644 --- a/examples/flowArts/lightSaber/lightSaberEntityScript.js +++ b/examples/flowArts/lightSaber/lightSaberEntityScript.js @@ -30,8 +30,9 @@ isGrabbed: false, startNearGrab: function() { - // this.createBeam(); - + Entities.editEntity(this.beam, { + isEmitting: true + }); }, continueNearGrab: function() { @@ -39,7 +40,9 @@ }, releaseGrab: function() { - + Entities.editEntity(this.beam, { + isEmitting: false + }); }, preload: function(entityID) { @@ -57,7 +60,7 @@ this.props = Entities.getEntityProperties(this.entityID, ["position", "rotation"]); var forwardVec = Quat.getFront(Quat.multiply(this.props.rotation, Quat.fromPitchYawRollDegrees(-90, 0, 0))); - forwardVec = Vec3.normalize(forwardVec); + // forwardVec = Vec3.normalize(forwardVec); var forwardQuat = orientationOf(forwardVec); var position = Vec3.sum(this.props.position, Vec3.multiply(Quat.getFront(this.props.rotation), 0.1)); position.z += 0.1; @@ -67,7 +70,7 @@ type: "ParticleEffect", position: position, parentID: this.entityID, - isEmitting: true, + isEmitting: false, "colorStart": color, color: { red: 200, From 2aaefbcfcbbcc0d6e3ed7d4cfbb932467131d4a4 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Thu, 17 Dec 2015 11:32:48 -0800 Subject: [PATCH 48/94] refactoring, adding headers --- examples/flowArts/flowArtsHutSpawner.js | 13 +-- examples/flowArts/lightBall/lightBall.js | 90 +++++++++++-------- examples/flowArts/lightSaber/LightSaber.js | 21 ++++- .../lightSaber/lightSaberEntityScript.js | 74 +++++++-------- examples/flowArts/lightTrails.js | 23 +---- examples/flowArts/raveStick/RaveStick.js | 41 ++++++++- .../raveStick/raveStickEntityScript.js | 67 +++++++------- .../RenderableParticleEffectEntityItem.cpp | 1 + .../entities/src/ParticleEffectEntityItem.cpp | 11 +-- 9 files changed, 195 insertions(+), 146 deletions(-) diff --git a/examples/flowArts/flowArtsHutSpawner.js b/examples/flowArts/flowArtsHutSpawner.js index 2f9b2052d1..03c0ca112f 100644 --- a/examples/flowArts/flowArtsHutSpawner.js +++ b/examples/flowArts/flowArtsHutSpawner.js @@ -1,11 +1,11 @@ // // flowArtsHutSpawner.js -// examples +// examples/flowArts // -// Created by Eric Levin on 5/14/15. +// Created by Eric Levin on 12/17/15. // Copyright 2014 High Fidelity, Inc. // -// This script creates a special flow arts hut with a numch of flow art toys people can go in and play with +// This script creates a special flow arts hut with a bunch of flow art toys people can go in and play with // // // Distributed under the Apache License, Version 2.0. @@ -29,12 +29,13 @@ var raveStick = new RaveStick(Vec3.sum(basePosition, {x: 1, y: 0.5, z: 1})); var lightSaber = new LightSaber(Vec3.sum(basePosition, {x: 3, y: 0.5, z: 1})); -var modelURL = "file:///C:/Users/Eric/Desktop/RaveRoom.fbx?v1" + Math.random(); +var modelURL = "https://s3.amazonaws.com/hifi-public/eric/models/rave/RaveRoom.fbx"; var roomDimensions = {x: 30.58, y: 15.29, z: 30.58}; var raveRoom = Entities.addEntity({ type: "Model", + name: "Rave Hut Room", modelURL: modelURL, position: basePosition, dimensions:roomDimensions, @@ -43,7 +44,8 @@ var raveRoom = Entities.addEntity({ var floor = Entities.addEntity({ type: "Box", - position: Vec3.sum(basePosition, {x: 0, y: -1.5, z: 0}), + name: "Rave Floor", + position: Vec3.sum(basePosition, {x: 0, y: -1.2, z: 0}), dimensions: {x: roomDimensions.x, y: 0.6, z: roomDimensions.z}, color: {red: 50, green: 10, blue: 100}, shapeType: 'box' @@ -53,6 +55,7 @@ var floor = Entities.addEntity({ var lightZone = Entities.addEntity({ type: "Zone", + name: "Rave Hut Zone", shapeType: 'box', keyLightIntensity: 0.4, keyLightColor: { diff --git a/examples/flowArts/lightBall/lightBall.js b/examples/flowArts/lightBall/lightBall.js index 698c62a71d..a8cdf894d1 100644 --- a/examples/flowArts/lightBall/lightBall.js +++ b/examples/flowArts/lightBall/lightBall.js @@ -1,3 +1,17 @@ +// +// LightBall.js +// examples/lightBall +// +// Created by Eric Levin on 12/17/15. +// Copyright 2014 High Fidelity, Inc. +// +// This script creats a particle light ball which makes particle trails as you move it. +// +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + Script.include("../../libraries/utils.js"); LightBall = function(spawnPosition) { @@ -11,6 +25,7 @@ LightBall = function(spawnPosition) { var containerBall = Entities.addEntity({ type: "Sphere", + name: "containerBall", position: Vec3.sum(spawnPosition, { x: 0, y: .5, @@ -27,7 +42,11 @@ LightBall = function(spawnPosition) { blue: 150 }, collisionsWillMove: true, - gravity: {x: 0, y: -.5, z: 0}, + gravity: { + x: 0, + y: -.5, + z: 0 + }, userData: JSON.stringify({ grabbableKey: { spatialKey: { @@ -45,6 +64,7 @@ LightBall = function(spawnPosition) { var light = Entities.addEntity({ type: 'Light', + name: "ballLight", parentID: containerBall, dimensions: { x: 30, @@ -60,8 +80,8 @@ LightBall = function(spawnPosition) { type: "ParticleEffect", parentID: containerBall, isEmitting: true, - "name": "ParticlesTest Emitter", - "colorStart": { + name: "particleBall", + colorStart: { red: 200, green: 20, blue: 40 @@ -71,45 +91,45 @@ LightBall = function(spawnPosition) { green: 200, blue: 255 }, - "colorFinish": { + colorFinish: { red: 25, green: 20, blue: 255 }, - "maxParticles": 100000, - "lifespan": 2, - "emitRate": 10000, - "emitSpeed": .1, - "speedSpread": 0.0, - "emitDimensions": { - "x": 0, - "y": 0, - "z": 0 + maxParticles: 100000, + lifespan: 2, + emitRate: 10000, + emitSpeed: .1, + lifetime: -1, + speedSpread: 0.0, + emitDimensions: { + x: 0, + y: 0, + z: 0 }, - "polarStart": 0, - "polarFinish": Math.PI, - "azimuthStart": -Math.PI, - "azimuthFinish": Math.PI, - "emitAcceleration": { - "x": 0, - "y": 0, - "z": 0 + polarStart: 0, + polarFinish: Math.PI, + azimuthStart: -Math.PI, + azimuthFinish: Math.PI, + emitAcceleration: { + x: 0, + y: 0, + z: 0 }, - "accelerationSpread": { - "x": .00, - "y": .00, - "z": .00 + accelerationSpread: { + x: .00, + y: .00, + z: .00 }, - "particleRadius": 0.02, - "radiusSpread": 0, - "radiusStart": 0.03, - "radiusFinish": 0.0003, - "alpha": 0, - "alphaSpread": .5, - "alphaStart": 0, - "alphaFinish": 0.5, - // "textures": "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png", - "textures": "file:///C:/Users/Eric/Desktop/Particle-Sprite-Smoke-1.png?v1" + Math.random(), + particleRadius: 0.02, + radiusSpread: 0, + radiusStart: 0.03, + radiusFinish: 0.0003, + alpha: 0, + alphaSpread: .5, + alphaStart: 0, + alphaFinish: 0.5, + textures: "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png", emitterShouldTrail: true }) diff --git a/examples/flowArts/lightSaber/LightSaber.js b/examples/flowArts/lightSaber/LightSaber.js index 12ac3f3b60..8a4f3f2902 100644 --- a/examples/flowArts/lightSaber/LightSaber.js +++ b/examples/flowArts/lightSaber/LightSaber.js @@ -1,10 +1,25 @@ +// +// LightSaber.js +// examples +// +// Created by Eric Levin on 12/17/15. +// Copyright 2014 High Fidelity, Inc. +// +// This script creates a lightsaber which activates on grab +// +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + Script.include("../../libraries/utils.js"); -var modelURL = "file:///C:/Users/Eric/Desktop/lightSaber.fbx?v1" + Math.random(); +var modelURL = "https://s3.amazonaws.com/hifi-public/eric/models/rave/lightSaber.fbx"; var scriptURL = Script.resolvePath("lightSaberEntityScript.js"); LightSaber = function(spawnPosition) { - var stick = Entities.addEntity({ + var saberHandle = Entities.addEntity({ type: "Model", + name: "LightSaber Handle", modelURL: modelURL, position: spawnPosition, shapeType: 'box', @@ -31,7 +46,7 @@ LightSaber = function(spawnPosition) { function cleanup() { - Entities.deleteEntity(stick); + Entities.deleteEntity(saberHandle); } this.cleanup = cleanup; diff --git a/examples/flowArts/lightSaber/lightSaberEntityScript.js b/examples/flowArts/lightSaber/lightSaberEntityScript.js index 2faf0abff9..6f396bf6e3 100644 --- a/examples/flowArts/lightSaber/lightSaberEntityScript.js +++ b/examples/flowArts/lightSaber/lightSaberEntityScript.js @@ -1,10 +1,10 @@ // lightSaberEntityScript.js // // Script Type: Entity -// Created by Eric Levin on 12/16/15. +// Created by Eric Levin on 12/17/15. // Copyright 2015 High Fidelity, Inc. // -// This entity script creates a lightsaber. +// This entity script creates the logic for displaying the lightsaber beam. // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // @@ -35,10 +35,6 @@ }); }, - continueNearGrab: function() { - - }, - releaseGrab: function() { Entities.editEntity(this.beam, { isEmitting: false @@ -52,7 +48,6 @@ unload: function() { Entities.deleteEntity(this.beam); - // Entities.deleteEntity(this.beamTrail); }, createBeam: function() { @@ -68,56 +63,53 @@ var color = this.colorPalette[randInt(0, this.colorPalette.length)]; var props = { type: "ParticleEffect", + name: "LightSaber Beam", position: position, parentID: this.entityID, isEmitting: false, - "colorStart": color, + colorStart: color, color: { red: 200, green: 200, blue: 255 }, - "colorFinish": color, - "maxParticles": 100000, - "lifespan": 2, - "emitRate": 1000, + colorFinish: color, + maxParticles: 100000, + lifespan: 2, + emitRate: 1000, emitOrientation: forwardQuat, - "emitSpeed": .4, - "speedSpread": 0.0, - "emitDimensions": { - "x": 0, - "y": 0, - "z": 0 + emitSpeed: .4, + speedSpread: 0.0, + emitDimensions: { + x: 0, + y: 0, + z: 0 }, - "polarStart": 0, - "polarFinish": .0, - "azimuthStart": .1, - "azimuthFinish": .01, - "emitAcceleration": { - "x": 0, - "y": 0, - "z": 0 + polarStart: 0, + polarFinish: .0, + azimuthStart: .1, + azimuthFinish: .01, + emitAcceleration: { + x: 0, + y: 0, + z: 0 }, - "accelerationSpread": { - "x": .00, - "y": .00, - "z": .00 + accelerationSpread: { + x: .00, + y: .00, + z: .00 }, - "radiusStart": 0.03, - radiusFinish: 0.025, - "alpha": 0.7, - "alphaSpread": .1, - "alphaStart": 0.5, - "alphaFinish": 0.5, - // "textures": "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png", - "textures": "file:///C:/Users/Eric/Desktop/beamParticle.png?v1" + Math.random(), + radiusStart: 0.03, + adiusFinish: 0.025, + alpha: 0.7, + alphaSpread: .1, + alphaStart: 0.5, + alphaFinish: 0.5, + textures: "https://s3.amazonaws.com/hifi-public/eric/textures/particleSprites/beamParticle.png", emitterShouldTrail: false } this.beam = Entities.addEntity(props); - // props.emitterShouldTrail = true; - // this.beamTrail = Entities.addEntity(props); - } }; // entity scripts always need to return a newly constructed object of our type diff --git a/examples/flowArts/lightTrails.js b/examples/flowArts/lightTrails.js index 8e0dc98bbb..522ecce851 100644 --- a/examples/flowArts/lightTrails.js +++ b/examples/flowArts/lightTrails.js @@ -1,11 +1,11 @@ // -// hydraPaint.js +// lightTrails.js // examples // // Created by Eric Levin on 5/14/15. // Copyright 2014 High Fidelity, Inc. // -// This script allows you to paint with the hydra! +// This script creates light trails as you move your hydra hands // // // Distributed under the Apache License, Version 2.0. @@ -25,24 +25,6 @@ var LIFETIME = 6000; var DRAWING_DEPTH = 0.8; var LINE_DIMENSIONS = 100; -var lightZone = Entities.addEntity({ - type: "Zone", - shapeType: 'box', - keyLightIntensity: 0.02, - keyLightColor: { - red: 5, - green: 0, - blue: 5 - }, - keyLightAmbientIntensity: .05, - position: MyAvatar.position, - dimensions: { - x: 100, - y: 100, - z: 100 - } -}); - var MIN_POINT_DISTANCE = 0.02; @@ -192,7 +174,6 @@ function update(deltaTime) { function scriptEnding() { leftController.cleanup(); rightController.cleanup(); - Entities.deleteEntity(lightZone); } function vectorIsZero(v) { diff --git a/examples/flowArts/raveStick/RaveStick.js b/examples/flowArts/raveStick/RaveStick.js index 5151b528cc..fd3c98b6e5 100644 --- a/examples/flowArts/raveStick/RaveStick.js +++ b/examples/flowArts/raveStick/RaveStick.js @@ -1,10 +1,32 @@ +// +// RaveStick.js +// examples/flowArats/raveStick +// +// Created by Eric Levin on 12/17/15. +// Copyright 2014 High Fidelity, Inc. +// +// This script creates a rave stick which makes pretty light trails as you paint +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + Script.include("../../libraries/utils.js"); -var modelURL = "file:///C:/Users/Eric/Desktop/raveStick.fbx?v1" + Math.random(); +var modelURL = "https://s3.amazonaws.com/hifi-public/eric/models/rave/raveStick.fbx"; var scriptURL = Script.resolvePath("raveStickEntityScript.js"); RaveStick = function(spawnPosition) { - + var colorPalette = [{ + red: 0, + green: 200, + blue: 40 + }, { + red: 200, + green: 10, + blue: 40 + }]; var stick = Entities.addEntity({ type: "Model", + name: "raveStick", modelURL: modelURL, position: spawnPosition, shapeType: 'box', @@ -29,9 +51,24 @@ RaveStick = function(spawnPosition) { }) }); + var light = Entities.addEntity({ + type: 'Light', + name: "raveLight", + parentID: stick, + dimensions: { + x: 30, + y: 30, + z: 30 + }, + color: colorPalette[randInt(0, colorPalette.length)], + intensity: 5 + }); + + function cleanup() { Entities.deleteEntity(stick); + Entities.deleteEntity(light); } this.cleanup = cleanup; diff --git a/examples/flowArts/raveStick/raveStickEntityScript.js b/examples/flowArts/raveStick/raveStickEntityScript.js index 20c21d3a79..2c484e601d 100644 --- a/examples/flowArts/raveStick/raveStickEntityScript.js +++ b/examples/flowArts/raveStick/raveStickEntityScript.js @@ -46,7 +46,9 @@ }, textures: texture, lifetime: LIFETIME - }) + }); + + this.points = []; this.normals = []; this.strokeWidths = []; @@ -127,8 +129,8 @@ unload: function() { Entities.deleteEntity(this.beam); Entities.deleteEntity(this.trail); - if(this.trailEraseInterval) { - Script.clearInterval(this.trailEraseInterval); + if (this.trailEraseInterval) { + Script.clearInterval(this.trailEraseInterval); } }, @@ -147,43 +149,46 @@ position: position, parentID: this.entityID, isEmitting: true, - "name": "ParticlesTest Emitter", - "colorStart": color, - colorSpread: {red: 200, green : 10, blue: 10}, + name: "raveBeam", + colorStart: color, + colorSpread: { + red: 200, + green: 10, + blue: 10 + }, color: { red: 200, green: 200, blue: 255 }, - "colorFinish": color, - "maxParticles": 100000, - "lifespan": 1, - "emitRate": 1000, + colorFinish: color, + maxParticles: 100000, + lifespan: 1, + emitRate: 1000, emitOrientation: forwardQuat, - "emitSpeed": .2, - "speedSpread": 0.0, - "polarStart": 0, - "polarFinish": .0, - "azimuthStart": .1, - "azimuthFinish": .01, - "emitAcceleration": { - "x": 0, - "y": 0, - "z": 0 + emitSpeed: .2, + speedSpread: 0.0, + polarStart: 0, + polarFinish: .0, + azimuthStart: .1, + azimuthFinish: .01, + emitAcceleration: { + x: 0, + y: 0, + z: 0 }, - "accelerationSpread": { - "x": .00, - "y": .00, - "z": .00 + accelerationSpread: { + x: .00, + y: .00, + z: .00 }, - "radiusStart": 0.03, + radiusStart: 0.03, radiusFinish: 0.025, - "alpha": 0.7, - "alphaSpread": .1, - "alphaStart": 0.5, - "alphaFinish": 0.5, - // "textures": "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png", - "textures": "file:///C:/Users/Eric/Desktop/beamParticle.png?v1" + Math.random(), + alpha: 0.7, + alphaSpread: .1, + alphaStart: 0.5, + alphaFinish: 0.5, + textures: "https://s3.amazonaws.com/hifi-public/eric/textures/particleSprites/beamParticle.png", emitterShouldTrail: false } this.beam = Entities.addEntity(props); diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index 91a4811011..43b9ae7870 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -245,6 +245,7 @@ void RenderableParticleEffectEntityItem::updateRenderItem() { transform.setRotation(rotation); } + render::PendingChanges pendingChanges; pendingChanges.updateItem(_renderItemId, [=](ParticlePayloadData& payload) { payload.setVisibleFlag(true); diff --git a/libraries/entities/src/ParticleEffectEntityItem.cpp b/libraries/entities/src/ParticleEffectEntityItem.cpp index 4d46439554..413604dbdf 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.cpp +++ b/libraries/entities/src/ParticleEffectEntityItem.cpp @@ -641,11 +641,7 @@ void ParticleEffectEntityItem::stepSimulation(float deltaTime) { ParticleEffectEntityItem::Particle ParticleEffectEntityItem::createParticle() { Particle particle; - - std::random_device rd; - std::mt19937_64 el(rd()); - std::uniform_real_distribution uniform_dist(0.0, 1.0); particle.seed = randFloatInRange(-1.0f, 1.0f); if (getEmitterShouldTrail()) { @@ -667,13 +663,13 @@ ParticleEffectEntityItem::Particle ParticleEffectEntityItem::createParticle() { float elevationMinZ = sin(PI_OVER_TWO - _polarFinish); float elevationMaxZ = sin(PI_OVER_TWO - _polarStart); // float elevation = asin(elevationMinZ + (elevationMaxZ - elevationMinZ) * randFloat()); - float elevation = asin(elevationMinZ + (elevationMaxZ - elevationMinZ) * uniform_dist(el)); + float elevation = asin(elevationMinZ + (elevationMaxZ - elevationMinZ) *randFloat()); float azimuth; if (_azimuthFinish >= _azimuthStart) { - azimuth = _azimuthStart + (_azimuthFinish - _azimuthStart) * uniform_dist(el); + azimuth = _azimuthStart + (_azimuthFinish - _azimuthStart) * randFloat(); } else { - azimuth = _azimuthStart + (TWO_PI + _azimuthFinish - _azimuthStart) * uniform_dist(el); + azimuth = _azimuthStart + (TWO_PI + _azimuthFinish - _azimuthStart) * randFloat(); } glm::vec3 emitDirection; @@ -711,7 +707,6 @@ ParticleEffectEntityItem::Particle ParticleEffectEntityItem::createParticle() { } particle.velocity = (_emitSpeed + randFloatInRange(-1.0f, 1.0f) * _speedSpread) * (_emitOrientation * emitDirection); - // particle.velocity = (_emitSpeed + uniform_dist(el) * _speedSpread) * (_emitOrientation * emitDirection); particle.acceleration = _emitAcceleration + randFloatInRange(-1.0f, 1.0f) * _accelerationSpread; } From 8d25a666d11dd6e3498e55fcae021f9c886edc4c Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Thu, 17 Dec 2015 11:40:06 -0800 Subject: [PATCH 49/94] fixed bug where trail would remain after user released rave stick --- examples/flowArts/flowArtsHutSpawner.js | 1 - examples/flowArts/lightBall/lightBall.js | 2 +- .../flowArts/raveStick/raveStickEntityScript.js | 13 +++++-------- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/examples/flowArts/flowArtsHutSpawner.js b/examples/flowArts/flowArtsHutSpawner.js index 03c0ca112f..3762c67fe4 100644 --- a/examples/flowArts/flowArtsHutSpawner.js +++ b/examples/flowArts/flowArtsHutSpawner.js @@ -72,7 +72,6 @@ var lightZone = Entities.addEntity({ } }); - function cleanup() { Entities.deleteEntity(raveRoom); diff --git a/examples/flowArts/lightBall/lightBall.js b/examples/flowArts/lightBall/lightBall.js index a8cdf894d1..1898f86eef 100644 --- a/examples/flowArts/lightBall/lightBall.js +++ b/examples/flowArts/lightBall/lightBall.js @@ -44,7 +44,7 @@ LightBall = function(spawnPosition) { collisionsWillMove: true, gravity: { x: 0, - y: -.5, + y: -0.5, z: 0 }, userData: JSON.stringify({ diff --git a/examples/flowArts/raveStick/raveStickEntityScript.js b/examples/flowArts/raveStick/raveStickEntityScript.js index 2c484e601d..171b53e0cf 100644 --- a/examples/flowArts/raveStick/raveStickEntityScript.js +++ b/examples/flowArts/raveStick/raveStickEntityScript.js @@ -19,7 +19,7 @@ var MAX_POINTS_PER_LINE = 50; var MIN_POINT_DISTANCE = 0.02; var STROKE_WIDTH = 0.05 - var ugLSD = 25; + var ugLSD = 35; var RaveStick = function() { _this = this; this.colorPalette = [{ @@ -117,8 +117,10 @@ }, releaseGrab: function() { - Script.clearInterval(this.trailEraseInterval); - this.trailEraseInterval = null; + Script.setTimeout(function() { + Script.clearInterval(_this.trailEraseInterval); + _this.trailEraseInterval = null; + }, 3000); }, preload: function(entityID) { @@ -192,13 +194,8 @@ emitterShouldTrail: false } this.beam = Entities.addEntity(props); - - // props.emitterShouldTrail = true; - // this.beamTrail = Entities.addEntity(props); - } }; - // entity scripts always need to return a newly constructed object of our type return new RaveStick(); function computeNormal(p1, p2) { From 6f82d680b9cb7873f091614fde07c6fd055a0ccd Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Thu, 17 Dec 2015 12:38:25 -0800 Subject: [PATCH 50/94] removed unwanted file --- examples/flowArts/lightBall/particles.js | 129 ----------------------- 1 file changed, 129 deletions(-) delete mode 100644 examples/flowArts/lightBall/particles.js diff --git a/examples/flowArts/lightBall/particles.js b/examples/flowArts/lightBall/particles.js deleted file mode 100644 index 86616d5099..0000000000 --- a/examples/flowArts/lightBall/particles.js +++ /dev/null @@ -1,129 +0,0 @@ -Script.include("../../libraries/utils.js"); - -LightBall = function(spawnPosition) { - - var colorPalette = [{ - red: 25, - green: 20, - blue: 162 - }]; - - - var containerBall = Entities.addEntity({ - type: "Sphere", - position: Vec3.sum(spawnPosition, { - x: 0, - y: .5, - z: 0 - }), - dimensions: { - x: .1, - y: .1, - z: .1 - }, - color: { - red: 15, - green: 10, - blue: 150 - }, - collisionsWillMove: true, - gravity: {x: 0, y: -.5, z: 0}, - visible: false, - userData: JSON.stringify({ - grabbableKey: { - spatialKey: { - relativePosition: { - x: 0, - y: .1, - z: 0 - } - }, - invertSolidWhileHeld: true - } - }) - }); - - - var light = Entities.addEntity({ - type: 'Light', - parentID: containerBall, - dimensions: { - x: 30, - y: 30, - z: 30 - }, - color: colorPalette[randInt(0, colorPalette.length)], - intensity: 5 - }); - - - var lightBall = Entities.addEntity({ - type: "ParticleEffect", - position: spawnPosition, - // parentID: containerBall, - isEmitting: true, - "name": "ParticlesTest Emitter", - "colorStart": { - red: 200, - green: 20, - blue: 40 - }, - color: { - red: 200, - green: 200, - blue: 255 - }, - "colorFinish": { - red: 25, - green: 20, - blue: 255 - }, - "maxParticles": 100000, - "lifespan": 2, - "emitRate": 10000, - "emitSpeed": .0, - "speedSpread": 0.0, - "emitDimensions": { - "x": 5, - "y":0 , - "z": 0 - }, - // "polarStart": 0, - // "polarFinish": Math.PI, - // "azimuthStart": -Math.PI, - // "azimuthFinish": Math.PI, - "emitAcceleration": { - "x": 0, - "y": 0, - "z": 0 - }, - "accelerationSpread": { - "x": .00, - "y": .0, - "z": .00 - }, - "particleRadius": 0.02, - "radiusSpread": 0, - "radiusStart": 0.03, - "radiusFinish": 0.0003, - "alpha": 0, - "alphaSpread": .5, - "alphaStart": 0, - "alphaFinish": 0.5, - // "textures": "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png", - "textures": "file:///C:/Users/Eric/Desktop/Particle-Sprite-Smoke-1.png?v1" + Math.random(), - emitterShouldTrail: true - }) - - - - function cleanup() { - Entities.deleteEntity(lightBall); - Entities.deleteEntity(containerBall); - Entities.deleteEntity(light); - } - - this.cleanup = cleanup; -} - -LightBall(); \ No newline at end of file From 92f96cbb2f403a556a62bae17326def1e90c4c8f Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Thu, 17 Dec 2015 12:49:30 -0800 Subject: [PATCH 51/94] Removed un-needed libraries from particle effect entity --- libraries/entities/src/ParticleEffectEntityItem.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/libraries/entities/src/ParticleEffectEntityItem.cpp b/libraries/entities/src/ParticleEffectEntityItem.cpp index da8e369841..16196aa129 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.cpp +++ b/libraries/entities/src/ParticleEffectEntityItem.cpp @@ -33,8 +33,6 @@ #include #include #include -#include -#include #include "EntityTree.h" #include "EntityTreeElement.h" From 035ca25b485d6d712abeac8975bd9eecda455fe1 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Thu, 17 Dec 2015 13:31:27 -0800 Subject: [PATCH 52/94] arc ball finds other balls --- examples/flowArts/arcBall/arcBall.js | 149 ++++++++++++++++++ .../flowArts/arcBall/arcBallEntityScript.js | 45 ++++++ examples/flowArts/flowArtsHutSpawner.js | 9 +- examples/flowArts/lightBall/lightBall.js | 10 +- 4 files changed, 206 insertions(+), 7 deletions(-) create mode 100644 examples/flowArts/arcBall/arcBall.js create mode 100644 examples/flowArts/arcBall/arcBallEntityScript.js diff --git a/examples/flowArts/arcBall/arcBall.js b/examples/flowArts/arcBall/arcBall.js new file mode 100644 index 0000000000..3853830863 --- /dev/null +++ b/examples/flowArts/arcBall/arcBall.js @@ -0,0 +1,149 @@ +// +// arcBall.js +// examples/arcBall +// +// Created by Eric Levin on 12/17/15. +// Copyright 2014 High Fidelity, Inc. +// +// This script creats a particle light ball which makes particle trails as you move it. +// +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +Script.include("../../libraries/utils.js"); + + +var scriptURL = Script.resolvePath("arcBallEntityScript.js"); +ArcBall = function(spawnPosition) { + + var colorPalette = [{ + red: 25, + green: 20, + blue: 162 + }]; + + + var containerBall = Entities.addEntity({ + type: "Sphere", + name: "Arc Ball", + script: scriptURL, + position: Vec3.sum(spawnPosition, { + x: 0, + y: .5, + z: 0 + }), + dimensions: { + x: .1, + y: .1, + z: .1 + }, + color: { + red: 15, + green: 10, + blue: 150 + }, + mass: 10, + collisionsWillMove: true, + // gravity: { + // x: 0, + // y: -0.5, + // z: 0 + // }, + userData: JSON.stringify({ + grabbableKey: { + spatialKey: { + relativePosition: { + x: 0, + y: .1, + z: 0 + } + }, + invertSolidWhileHeld: true + } + }) + }); + + + var light = Entities.addEntity({ + type: 'Light', + name: "ballLight", + parentID: containerBall, + dimensions: { + x: 30, + y: 30, + z: 30 + }, + color: colorPalette[randInt(0, colorPalette.length)], + intensity: 5 + }); + + + var arcBall = Entities.addEntity({ + type: "ParticleEffect", + parentID: containerBall, + isEmitting: true, + name: "Arc Ball Particle Effect", + colorStart: { + red: 200, + green: 20, + blue: 40 + }, + color: { + red: 200, + green: 200, + blue: 255 + }, + colorFinish: { + red: 25, + green: 20, + blue: 255 + }, + maxParticles: 100000, + lifespan: 2, + emitRate: 10000, + emitSpeed: .1, + lifetime: -1, + speedSpread: 0.0, + emitDimensions: { + x: 0, + y: 0, + z: 0 + }, + polarStart: 0, + polarFinish: Math.PI, + azimuthStart: -Math.PI, + azimuthFinish: Math.PI, + emitAcceleration: { + x: 0, + y: 0, + z: 0 + }, + accelerationSpread: { + x: .00, + y: .00, + z: .00 + }, + particleRadius: 0.02, + radiusSpread: 0, + radiusStart: 0.03, + radiusFinish: 0.0003, + alpha: 0, + alphaSpread: .5, + alphaStart: 0, + alphaFinish: 0.5, + textures: "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png", + emitterShouldTrail: true + }) + + + + function cleanup() { + Entities.deleteEntity(arcBall); + Entities.deleteEntity(containerBall); + Entities.deleteEntity(light); + } + + this.cleanup = cleanup; +} \ No newline at end of file diff --git a/examples/flowArts/arcBall/arcBallEntityScript.js b/examples/flowArts/arcBall/arcBallEntityScript.js new file mode 100644 index 0000000000..bcdb2bc5a9 --- /dev/null +++ b/examples/flowArts/arcBall/arcBallEntityScript.js @@ -0,0 +1,45 @@ +// arcBallEntityScript.js +// +// Script Type: Entity +// Created by Eric Levin on 12/17/15. +// Copyright 2015 High Fidelity, Inc. +// +// This entity script handles the logic for the arcBall rave toy +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +(function() { + Script.include("../../libraries/utils.js"); + var _this; + var ArcBall = function() { + _this = this; + }; + + ArcBall.prototype = { + isGrabbed: false, + startNearGrab: function() { + //Search for nearby balls and create an arc to it if one is found + var position = Entities.getEntityProperties(this.entityID, "position").position + var entities = Entities.findEntities(position, 10); + entities.forEach(function(entity) { + var props = Entities.getEntityProperties(entity, ["position", "name"]); + if (props.name === "Arc Ball" && JSON.stringify(_this.entityID) !== JSON.stringify(entity)) { + print ("WE FOUND ANOTHER ARC BALL"); + } + }) + + }, + + continueNearGrab: function() { + }, + + releaseGrab: function() { + }, + + preload: function(entityID) { + this.entityID = entityID; + }, + }; + return new ArcBall(); +}); diff --git a/examples/flowArts/flowArtsHutSpawner.js b/examples/flowArts/flowArtsHutSpawner.js index 3762c67fe4..742b239981 100644 --- a/examples/flowArts/flowArtsHutSpawner.js +++ b/examples/flowArts/flowArtsHutSpawner.js @@ -17,6 +17,7 @@ Script.include("../../libraries/utils.js"); Script.include("lightBall/LightBall.js"); Script.include("raveStick/RaveStick.js"); Script.include("lightSaber/LightSaber.js"); +Script.include("arcBall/ArcBall.js"); @@ -24,7 +25,10 @@ var basePosition = Vec3.sum(MyAvatar.position, Vec3.multiply(1, Quat.getFront(Ca basePosition.y = MyAvatar.position.y + 1; // RAVE ITEMS -var lightBall = new LightBall(basePosition); +//var lightBall = new LightBall(basePosition); + +var arcBall = new ArcBall(basePosition); +var arcBall2 = new ArcBall(Vec3.sum(basePosition, {x: -1, y: 0, z: 0})); var raveStick = new RaveStick(Vec3.sum(basePosition, {x: 1, y: 0.5, z: 1})); var lightSaber = new LightSaber(Vec3.sum(basePosition, {x: 3, y: 0.5, z: 1})); @@ -77,7 +81,8 @@ function cleanup() { Entities.deleteEntity(raveRoom); Entities.deleteEntity(lightZone) Entities.deleteEntity(floor); - lightBall.cleanup(); + arcBall.cleanup(); + arcBall2.cleanup(); raveStick.cleanup(); lightSaber.cleanup(); } diff --git a/examples/flowArts/lightBall/lightBall.js b/examples/flowArts/lightBall/lightBall.js index 1898f86eef..df62b1b786 100644 --- a/examples/flowArts/lightBall/lightBall.js +++ b/examples/flowArts/lightBall/lightBall.js @@ -42,11 +42,11 @@ LightBall = function(spawnPosition) { blue: 150 }, collisionsWillMove: true, - gravity: { - x: 0, - y: -0.5, - z: 0 - }, + // gravity: { + // x: 0, + // y: -0.5, + // z: 0 + // }, userData: JSON.stringify({ grabbableKey: { spatialKey: { From 3e6ae7c5710e99f2508022bb80082b4fcc73911b Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Thu, 17 Dec 2015 15:02:25 -0800 Subject: [PATCH 53/94] added lightball back --- examples/flowArts/arcBall/arcBall.js | 8 +- .../flowArts/arcBall/arcBallEntityScript.js | 80 +++++++++++++++++-- examples/flowArts/flowArtsHutSpawner.js | 13 +-- .../lightSaber/lightSaberEntityScript.js | 3 +- 4 files changed, 88 insertions(+), 16 deletions(-) diff --git a/examples/flowArts/arcBall/arcBall.js b/examples/flowArts/arcBall/arcBall.js index 3853830863..34578cce76 100644 --- a/examples/flowArts/arcBall/arcBall.js +++ b/examples/flowArts/arcBall/arcBall.js @@ -35,9 +35,9 @@ ArcBall = function(spawnPosition) { z: 0 }), dimensions: { - x: .1, - y: .1, - z: .1 + x: .2, + y: .2, + z: .2 }, color: { red: 15, @@ -102,7 +102,7 @@ ArcBall = function(spawnPosition) { }, maxParticles: 100000, lifespan: 2, - emitRate: 10000, + emitRate: 1000, emitSpeed: .1, lifetime: -1, speedSpread: 0.0, diff --git a/examples/flowArts/arcBall/arcBallEntityScript.js b/examples/flowArts/arcBall/arcBallEntityScript.js index bcdb2bc5a9..f1ba9aeb95 100644 --- a/examples/flowArts/arcBall/arcBallEntityScript.js +++ b/examples/flowArts/arcBall/arcBallEntityScript.js @@ -25,16 +25,86 @@ entities.forEach(function(entity) { var props = Entities.getEntityProperties(entity, ["position", "name"]); if (props.name === "Arc Ball" && JSON.stringify(_this.entityID) !== JSON.stringify(entity)) { - print ("WE FOUND ANOTHER ARC BALL"); + _this.createBeam(position, props.position); } - }) + }); }, - continueNearGrab: function() { + createBeam: function(startPosition, endPosition) { + print("CREATE BEAM") + // Creates particle arc from start position to end position + var sourceToTargetVec = Vec3.subtract(endPosition, startPosition); + var emitOrientation = Quat.rotationBetween(Vec3.UNIT_Z, sourceToTargetVec); + + testBox = Entities.addEntity({ + type: "Box", + dimensions: {x: .1, y: .1, z: 1}, + color: {red: 200, green: 10, blue: 10}, + position: startPosition, + rotation: emitOrientation + }); + var color = { + red: 200, + green: 10, + blue: 10 + }; + var props = { + type: "ParticleEffect", + name: "Particle Arc", + parentID: this.entityID, + isEmitting: true, + colorStart: color, + color: { + red: 200, + green: 200, + blue: 255 + }, + colorFinish: color, + maxParticles: 100000, + lifespan: 2, + emitRate: 1000, + emitOrientation: emitOrientation, + emitSpeed: .4, + speedSpread: 0.0, + emitDimensions: { + x: 0, + y: 0, + z: 0 + }, + polarStart: 0, + polarFinish: .0, + azimuthStart: .1, + azimuthFinish: .01, + emitAcceleration: { + x: 0, + y: 0, + z: 0 + }, + accelerationSpread: { + x: .00, + y: .00, + z: .00 + }, + radiusStart: 0.03, + adiusFinish: 0.025, + alpha: 0.7, + alphaSpread: .1, + alphaStart: 0.5, + alphaFinish: 0.5, + textures: "https://s3.amazonaws.com/hifi-public/eric/textures/particleSprites/beamParticle.png", + emitterShouldTrail: false + } + this.particleArc = Entities.addEntity(props); }, - releaseGrab: function() { + continueNearGrab: function() {}, + + releaseGrab: function() {}, + + unload: function() { + Entities.deleteEntity(this.particleArc); + Entities.deleteEntity(testBox); }, preload: function(entityID) { @@ -42,4 +112,4 @@ }, }; return new ArcBall(); -}); +}); \ No newline at end of file diff --git a/examples/flowArts/flowArtsHutSpawner.js b/examples/flowArts/flowArtsHutSpawner.js index 742b239981..e03d2bf0e8 100644 --- a/examples/flowArts/flowArtsHutSpawner.js +++ b/examples/flowArts/flowArtsHutSpawner.js @@ -25,10 +25,10 @@ var basePosition = Vec3.sum(MyAvatar.position, Vec3.multiply(1, Quat.getFront(Ca basePosition.y = MyAvatar.position.y + 1; // RAVE ITEMS -//var lightBall = new LightBall(basePosition); +var lightBall = new LightBall(basePosition); -var arcBall = new ArcBall(basePosition); -var arcBall2 = new ArcBall(Vec3.sum(basePosition, {x: -1, y: 0, z: 0})); +// var arcBall = new ArcBall(basePosition); +// var arcBall2 = new ArcBall(Vec3.sum(basePosition, {x: -1, y: 0, z: 0})); var raveStick = new RaveStick(Vec3.sum(basePosition, {x: 1, y: 0.5, z: 1})); var lightSaber = new LightSaber(Vec3.sum(basePosition, {x: 3, y: 0.5, z: 1})); @@ -79,10 +79,11 @@ var lightZone = Entities.addEntity({ function cleanup() { Entities.deleteEntity(raveRoom); - Entities.deleteEntity(lightZone) + Entities.deleteEntity(lightZone); Entities.deleteEntity(floor); - arcBall.cleanup(); - arcBall2.cleanup(); + lightBall.cleanup(); + // arcBall.cleanup(); + // arcBall2.cleanup(); raveStick.cleanup(); lightSaber.cleanup(); } diff --git a/examples/flowArts/lightSaber/lightSaberEntityScript.js b/examples/flowArts/lightSaber/lightSaberEntityScript.js index 6f396bf6e3..794e241924 100644 --- a/examples/flowArts/lightSaber/lightSaberEntityScript.js +++ b/examples/flowArts/lightSaber/lightSaberEntityScript.js @@ -56,7 +56,8 @@ this.props = Entities.getEntityProperties(this.entityID, ["position", "rotation"]); var forwardVec = Quat.getFront(Quat.multiply(this.props.rotation, Quat.fromPitchYawRollDegrees(-90, 0, 0))); // forwardVec = Vec3.normalize(forwardVec); - var forwardQuat = orientationOf(forwardVec); + // var forwardQuat = orientationOf(forwardVec); + var forwardQuat = Quat.rotationBetween(Vec3.UNIT_Z, forwardVec); var position = Vec3.sum(this.props.position, Vec3.multiply(Quat.getFront(this.props.rotation), 0.1)); position.z += 0.1; position.x += -0.035; From aa1f77ab1c6e0294983e2f8f4c18bfa7dbef2772 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Thu, 17 Dec 2015 15:42:52 -0800 Subject: [PATCH 54/94] adding ravestick to toybox demo room --- examples/flowArts/raveStick/RaveStick.js | 62 +++++++++ .../raveStick/raveStickEntityScript.js | 62 --------- unpublishedScripts/hiddenEntityReset.js | 115 ++++++++++++++++- unpublishedScripts/masterReset.js | 118 +++++++++++++++++- 4 files changed, 292 insertions(+), 65 deletions(-) diff --git a/examples/flowArts/raveStick/RaveStick.js b/examples/flowArts/raveStick/RaveStick.js index fd3c98b6e5..3e4cf16136 100644 --- a/examples/flowArts/raveStick/RaveStick.js +++ b/examples/flowArts/raveStick/RaveStick.js @@ -64,11 +64,73 @@ RaveStick = function(spawnPosition) { intensity: 5 }); + var rotation = Quat.fromPitchYawRollDegrees(0, 0, 0) + var forwardVec = Quat.getFront(Quat.multiply(rotation, Quat.fromPitchYawRollDegrees(-90, 0, 0))); + forwardVec = Vec3.normalize(forwardVec); + var forwardQuat = orientationOf(forwardVec); + var position = Vec3.sum(spawnPosition, Vec3.multiply(Quat.getFront(rotation), 0.1)); + position.z += 0.1; + position.x += -0.035; + var color = { + red: 0, + green: 200, + blue: 40 + }; + var props = { + type: "ParticleEffect", + position: position, + parentID: stick, + isEmitting: true, + name: "raveBeam", + colorStart: color, + colorSpread: { + red: 200, + green: 10, + blue: 10 + }, + color: { + red: 200, + green: 200, + blue: 255 + }, + colorFinish: color, + maxParticles: 100000, + lifespan: 1, + emitRate: 1000, + emitOrientation: forwardQuat, + emitSpeed: .2, + speedSpread: 0.0, + polarStart: 0, + polarFinish: .0, + azimuthStart: .1, + azimuthFinish: .01, + emitAcceleration: { + x: 0, + y: 0, + z: 0 + }, + accelerationSpread: { + x: .00, + y: .00, + z: .00 + }, + radiusStart: 0.03, + radiusFinish: 0.025, + alpha: 0.7, + alphaSpread: .1, + alphaStart: 0.5, + alphaFinish: 0.5, + textures: "https://s3.amazonaws.com/hifi-public/eric/textures/particleSprites/beamParticle.png", + emitterShouldTrail: false, + } + var beam = Entities.addEntity(props); + function cleanup() { Entities.deleteEntity(stick); Entities.deleteEntity(light); + Entities.deleteEntity(beam); } this.cleanup = cleanup; diff --git a/examples/flowArts/raveStick/raveStickEntityScript.js b/examples/flowArts/raveStick/raveStickEntityScript.js index 171b53e0cf..2e174c78f0 100644 --- a/examples/flowArts/raveStick/raveStickEntityScript.js +++ b/examples/flowArts/raveStick/raveStickEntityScript.js @@ -58,7 +58,6 @@ isGrabbed: false, startNearGrab: function() { - // this.createBeam(); this.trailBasePosition = Entities.getEntityProperties(this.entityID, "position").position; Entities.editEntity(this.trail, { position: this.trailBasePosition @@ -125,7 +124,6 @@ preload: function(entityID) { this.entityID = entityID; - this.createBeam(); }, unload: function() { @@ -134,66 +132,6 @@ if (this.trailEraseInterval) { Script.clearInterval(this.trailEraseInterval); } - }, - - createBeam: function() { - - var props = Entities.getEntityProperties(this.entityID, ["position", "rotation"]); - var forwardVec = Quat.getFront(Quat.multiply(props.rotation, Quat.fromPitchYawRollDegrees(-90, 0, 0))); - forwardVec = Vec3.normalize(forwardVec); - var forwardQuat = orientationOf(forwardVec); - var position = Vec3.sum(props.position, Vec3.multiply(Quat.getFront(props.rotation), 0.1)); - position.z += 0.1; - position.x += -0.035; - var color = this.colorPalette[randInt(0, this.colorPalette.length)]; - var props = { - type: "ParticleEffect", - position: position, - parentID: this.entityID, - isEmitting: true, - name: "raveBeam", - colorStart: color, - colorSpread: { - red: 200, - green: 10, - blue: 10 - }, - color: { - red: 200, - green: 200, - blue: 255 - }, - colorFinish: color, - maxParticles: 100000, - lifespan: 1, - emitRate: 1000, - emitOrientation: forwardQuat, - emitSpeed: .2, - speedSpread: 0.0, - polarStart: 0, - polarFinish: .0, - azimuthStart: .1, - azimuthFinish: .01, - emitAcceleration: { - x: 0, - y: 0, - z: 0 - }, - accelerationSpread: { - x: .00, - y: .00, - z: .00 - }, - radiusStart: 0.03, - radiusFinish: 0.025, - alpha: 0.7, - alphaSpread: .1, - alphaStart: 0.5, - alphaFinish: 0.5, - textures: "https://s3.amazonaws.com/hifi-public/eric/textures/particleSprites/beamParticle.png", - emitterShouldTrail: false - } - this.beam = Entities.addEntity(props); } }; return new RaveStick(); diff --git a/unpublishedScripts/hiddenEntityReset.js b/unpublishedScripts/hiddenEntityReset.js index ee53e36f9e..e4cf434bf8 100644 --- a/unpublishedScripts/hiddenEntityReset.js +++ b/unpublishedScripts/hiddenEntityReset.js @@ -24,6 +24,7 @@ var lightsScriptURL = Script.resolvePath("../examples/toybox/lights/lightSwitch.js"); var targetsScriptURL = Script.resolvePath('../examples/toybox/ping_pong_gun/wallTarget.js'); var bowScriptURL = Script.resolvePath('../examples/toybox/bow/bow.js'); + var raveStickEntityScriptURL = Script.resolvePath("../examples/flowarts/raveStick/raveStickEntityScript.js"); var basketballResetterScriptURL = Script.resolvePath('basketballsResetter.js'); var targetsResetterScriptURL = Script.resolvePath('targetsResetter.js'); @@ -106,6 +107,13 @@ z: 505.78 }); + createRaveStick({ + x: 547.4, + y: 495.4, + z: 504.5 + }); + + createCombinedArmChair({ x: 549.29, y: 494.9, @@ -160,6 +168,111 @@ }); } + function createRaveStick(position) { + var modelURL = "https://s3.amazonaws.com/hifi-public/eric/models/rave/raveStick.fbx"; + var stick = Entities.addEntity({ + type: "Model", + name: "raveStick", + modelURL: modelURL, + position: position, + shapeType: 'box', + collisionsWillMove: true, + script: raveStickEntityScriptURL, + dimensions: { + x: 0.06, + y: 0.06, + z: 0.31 + }, + gravity: { + x: 0, + y: -3, + z: 0 + }, + userData: JSON.stringify({ + resetMe: { + resetMe: true + }, + grabbableKey: { + spatialKey: { + relativePosition: { + x: 0, + y: 0, + z: -0.1 + }, + relativeRotation: Quat.fromPitchYawRollDegrees(90, 90, 0) + }, + invertSolidWhileHeld: true + } + }) + }); + var rotation = Quat.fromPitchYawRollDegrees(0, 0, 0) + var forwardVec = Quat.getFront(Quat.multiply(rotation, Quat.fromPitchYawRollDegrees(-90, 0, 0))); + forwardVec = Vec3.normalize(forwardVec); + var forwardQuat = orientationOf(forwardVec); + position = Vec3.sum(position, Vec3.multiply(Quat.getFront(rotation), 0.1)); + position.z += 0.1; + position.x += -0.035; + var color = { + red: 0, + green: 200, + blue: 40 + }; + var props = { + type: "ParticleEffect", + position: position, + parentID: stick, + isEmitting: true, + name: "raveBeam", + colorStart: color, + colorSpread: { + red: 200, + green: 10, + blue: 10 + }, + color: { + red: 200, + green: 200, + blue: 255 + }, + colorFinish: color, + maxParticles: 100000, + lifespan: 1, + emitRate: 1000, + emitOrientation: forwardQuat, + emitSpeed: .2, + speedSpread: 0.0, + polarStart: 0, + polarFinish: .0, + azimuthStart: .1, + azimuthFinish: .01, + emitAcceleration: { + x: 0, + y: 0, + z: 0 + }, + accelerationSpread: { + x: .00, + y: .00, + z: .00 + }, + radiusStart: 0.03, + radiusFinish: 0.025, + alpha: 0.7, + alphaSpread: .1, + alphaStart: 0.5, + alphaFinish: 0.5, + textures: "https://s3.amazonaws.com/hifi-public/eric/textures/particleSprites/beamParticle.png", + emitterShouldTrail: false, + userData: JSON.stringify({ + resetMe: { + resetMe: true + } + }) + } + var beam = Entities.addEntity(props); + + } + function createGun(position) { var modelURL = "https://s3.amazonaws.com/hifi-public/eric/models/gun.fbx"; @@ -1391,4 +1504,4 @@ }; // entity scripts always need to return a newly constructed object of our type return new ResetSwitch(); -}); +}); \ No newline at end of file diff --git a/unpublishedScripts/masterReset.js b/unpublishedScripts/masterReset.js index 1837f4d656..66c8c74edd 100644 --- a/unpublishedScripts/masterReset.js +++ b/unpublishedScripts/masterReset.js @@ -23,10 +23,12 @@ var wandScriptURL = Script.resolvePath("../examples/toybox/bubblewand/wand.js"); var dollScriptURL = Script.resolvePath("../examples/toybox/doll/doll.js"); var lightsScriptURL = Script.resolvePath("../examples/toybox/lights/lightSwitch.js"); var bowScriptURL = Script.resolvePath("../examples/toybox/bow/bow.js"); +var raveStickEntityScriptURL = Script.resolvePath("../examples/flowarts/raveStick/raveStickEntityScript.js"); var targetsScriptURL = Script.resolvePath('../examples/toybox/ping_pong_gun/wallTarget.js'); var basketballResetterScriptURL = Script.resolvePath('basketballsResetter.js'); var targetsResetterScriptURL = Script.resolvePath('targetsResetter.js'); + MasterReset = function() { var resetKey = "resetMe"; @@ -80,6 +82,12 @@ MasterReset = function() { z: 505.78 }); + createRaveStick({ + x: 547.4, + y: 495.4, + z: 504.5 + }); + createCombinedArmChair({ @@ -94,6 +102,8 @@ MasterReset = function() { z: 504.53 }); + + createPingPongBallGun(); createTargets(); createTargetResetter(); @@ -137,6 +147,110 @@ MasterReset = function() { }); } + function createRaveStick(position) { + var modelURL = "https://s3.amazonaws.com/hifi-public/eric/models/rave/raveStick.fbx"; + var stick = Entities.addEntity({ + type: "Model", + name: "raveStick", + modelURL: modelURL, + position: position, + shapeType: 'box', + collisionsWillMove: true, + script: raveStickEntityScriptURL, + dimensions: { + x: 0.06, + y: 0.06, + z: 0.31 + }, + gravity: { + x: 0, + y: -3, + z: 0 + }, + userData: JSON.stringify({ + resetMe: { + resetMe: true + }, + grabbableKey: { + spatialKey: { + relativePosition: { + x: 0, + y: 0, + z: -0.1 + }, + relativeRotation: Quat.fromPitchYawRollDegrees(90, 90, 0) + }, + invertSolidWhileHeld: true + } + }) + }); + var rotation = Quat.fromPitchYawRollDegrees(0, 0, 0) + var forwardVec = Quat.getFront(Quat.multiply(rotation, Quat.fromPitchYawRollDegrees(-90, 0, 0))); + forwardVec = Vec3.normalize(forwardVec); + var forwardQuat = orientationOf(forwardVec); + position = Vec3.sum(position, Vec3.multiply(Quat.getFront(rotation), 0.1)); + position.z += 0.1; + position.x += -0.035; + var color = { + red: 0, + green: 200, + blue: 40 + }; + var props = { + type: "ParticleEffect", + position: position, + parentID: stick, + isEmitting: true, + name: "raveBeam", + colorStart: color, + colorSpread: { + red: 200, + green: 10, + blue: 10 + }, + color: { + red: 200, + green: 200, + blue: 255 + }, + colorFinish: color, + maxParticles: 100000, + lifespan: 1, + emitRate: 1000, + emitOrientation: forwardQuat, + emitSpeed: .2, + speedSpread: 0.0, + polarStart: 0, + polarFinish: .0, + azimuthStart: .1, + azimuthFinish: .01, + emitAcceleration: { + x: 0, + y: 0, + z: 0 + }, + accelerationSpread: { + x: .00, + y: .00, + z: .00 + }, + radiusStart: 0.03, + radiusFinish: 0.025, + alpha: 0.7, + alphaSpread: .1, + alphaStart: 0.5, + alphaFinish: 0.5, + textures: "https://s3.amazonaws.com/hifi-public/eric/textures/particleSprites/beamParticle.png", + emitterShouldTrail: false, + userData: JSON.stringify({ + resetMe: { + resetMe: true + } + }) + } + var beam = Entities.addEntity(props); + + } function createGun(position) { var modelURL = "https://s3.amazonaws.com/hifi-public/eric/models/gun.fbx"; @@ -1349,7 +1463,7 @@ MasterReset = function() { resetMe: true }, grabbableKey: { - invertSolidWhileHeld: true + invertSolidWhileHeld: true } }) }); @@ -1366,4 +1480,4 @@ MasterReset = function() { Script.scriptEnding.connect(cleanup); } -}; +}; \ No newline at end of file From 17af34a3be36fdc30ae832f602d5f260358e5cb4 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Thu, 17 Dec 2015 15:48:05 -0800 Subject: [PATCH 55/94] fixed spelling error --- unpublishedScripts/hiddenEntityReset.js | 2 +- unpublishedScripts/masterReset.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/unpublishedScripts/hiddenEntityReset.js b/unpublishedScripts/hiddenEntityReset.js index e4cf434bf8..cc4037beb4 100644 --- a/unpublishedScripts/hiddenEntityReset.js +++ b/unpublishedScripts/hiddenEntityReset.js @@ -24,7 +24,7 @@ var lightsScriptURL = Script.resolvePath("../examples/toybox/lights/lightSwitch.js"); var targetsScriptURL = Script.resolvePath('../examples/toybox/ping_pong_gun/wallTarget.js'); var bowScriptURL = Script.resolvePath('../examples/toybox/bow/bow.js'); - var raveStickEntityScriptURL = Script.resolvePath("../examples/flowarts/raveStick/raveStickEntityScript.js"); + var raveStickEntityScriptURL = Script.resolvePath("../examples/flowArts/raveStick/raveStickEntityScript.js"); var basketballResetterScriptURL = Script.resolvePath('basketballsResetter.js'); var targetsResetterScriptURL = Script.resolvePath('targetsResetter.js'); diff --git a/unpublishedScripts/masterReset.js b/unpublishedScripts/masterReset.js index 66c8c74edd..a1545a3b67 100644 --- a/unpublishedScripts/masterReset.js +++ b/unpublishedScripts/masterReset.js @@ -23,7 +23,7 @@ var wandScriptURL = Script.resolvePath("../examples/toybox/bubblewand/wand.js"); var dollScriptURL = Script.resolvePath("../examples/toybox/doll/doll.js"); var lightsScriptURL = Script.resolvePath("../examples/toybox/lights/lightSwitch.js"); var bowScriptURL = Script.resolvePath("../examples/toybox/bow/bow.js"); -var raveStickEntityScriptURL = Script.resolvePath("../examples/flowarts/raveStick/raveStickEntityScript.js"); +var raveStickEntityScriptURL = Script.resolvePath("../examples/flowArts/raveStick/raveStickEntityScript.js"); var targetsScriptURL = Script.resolvePath('../examples/toybox/ping_pong_gun/wallTarget.js'); var basketballResetterScriptURL = Script.resolvePath('basketballsResetter.js'); var targetsResetterScriptURL = Script.resolvePath('targetsResetter.js'); From 77264b3f99283694ef126d859680760a10e39032 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Fri, 18 Dec 2015 14:52:39 -0800 Subject: [PATCH 56/94] Ping pong gun lines up with hand --- examples/flowArts/lightSaber/LightSaber.js | 13 +++++++++++++ unpublishedScripts/hiddenEntityReset.js | 8 ++++++++ unpublishedScripts/masterReset.js | 8 ++++++++ 3 files changed, 29 insertions(+) diff --git a/examples/flowArts/lightSaber/LightSaber.js b/examples/flowArts/lightSaber/LightSaber.js index 8a4f3f2902..50984110bc 100644 --- a/examples/flowArts/lightSaber/LightSaber.js +++ b/examples/flowArts/lightSaber/LightSaber.js @@ -44,6 +44,19 @@ LightSaber = function(spawnPosition) { }) }); + var light = Entities.addEntity({ + type: 'Light', + name: "raveLight", + parentID: saberHandle, + dimensions: { + x: 30, + y: 30, + z: 30 + }, + color: {red: 200, green: 10, blue: 200}, + intensity: 5 + }); + function cleanup() { Entities.deleteEntity(saberHandle); diff --git a/unpublishedScripts/hiddenEntityReset.js b/unpublishedScripts/hiddenEntityReset.js index cc4037beb4..c8a2c97b0f 100644 --- a/unpublishedScripts/hiddenEntityReset.js +++ b/unpublishedScripts/hiddenEntityReset.js @@ -1153,6 +1153,14 @@ resetMe: true }, grabbableKey: { + spatialKey: { + relativePosition: { + x: -0.05, + y: 0, + z: 0.0 + }, + relativeRotation: Quat.fromPitchYawRollDegrees(0, -90, -90) + }, invertSolidWhileHeld: true } diff --git a/unpublishedScripts/masterReset.js b/unpublishedScripts/masterReset.js index a1545a3b67..3d985e9a92 100644 --- a/unpublishedScripts/masterReset.js +++ b/unpublishedScripts/masterReset.js @@ -1134,6 +1134,14 @@ MasterReset = function() { resetMe: true }, grabbableKey: { + spatialKey: { + relativePosition: { + x: -0.05, + y: 0, + z: 0.0 + }, + relativeRotation: Quat.fromPitchYawRollDegrees(0,-90, -90) + }, invertSolidWhileHeld: true } From 2dbb3bd84abf25e2d5d6f0fc0f90ae3d02682012 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Fri, 18 Dec 2015 15:12:46 -0800 Subject: [PATCH 57/94] Added grab offsets to createPingPongGun.js --- examples/toybox/ping_pong_gun/createPingPongGun.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/examples/toybox/ping_pong_gun/createPingPongGun.js b/examples/toybox/ping_pong_gun/createPingPongGun.js index 4b93842896..7835dbf7f9 100644 --- a/examples/toybox/ping_pong_gun/createPingPongGun.js +++ b/examples/toybox/ping_pong_gun/createPingPongGun.js @@ -38,6 +38,14 @@ var pingPongGun = Entities.addEntity({ collisionSoundURL: COLLISION_SOUND_URL, userData: JSON.stringify({ grabbableKey: { + spatialKey: { + relativePosition: { + x: -0.05, + y: 0, + z: 0.0 + }, + relativeRotation: Quat.fromPitchYawRollDegrees(0, -90, -90) + }, invertSolidWhileHeld: true } }) From 2c02e7ad631ddec997bbcca99a462b701554d92b Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Fri, 18 Dec 2015 15:22:23 -0800 Subject: [PATCH 58/94] grabbing works with left hand for ping pong gun now --- unpublishedScripts/hiddenEntityReset.js | 2 +- unpublishedScripts/masterReset.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/unpublishedScripts/hiddenEntityReset.js b/unpublishedScripts/hiddenEntityReset.js index c8a2c97b0f..21971a18ad 100644 --- a/unpublishedScripts/hiddenEntityReset.js +++ b/unpublishedScripts/hiddenEntityReset.js @@ -1125,7 +1125,7 @@ z: 503.39 }; - var rotation = Quat.fromPitchYawRollDegrees(0, 36, 0); + var rotation = Quat.fromPitchYawRollDegrees(0, 0, 0); var pingPongGun = Entities.addEntity({ type: "Model", diff --git a/unpublishedScripts/masterReset.js b/unpublishedScripts/masterReset.js index 3d985e9a92..3d0773ca10 100644 --- a/unpublishedScripts/masterReset.js +++ b/unpublishedScripts/masterReset.js @@ -1106,7 +1106,7 @@ MasterReset = function() { z: 503.39 }; - var rotation = Quat.fromPitchYawRollDegrees(0, 36, 0); + var rotation = Quat.fromPitchYawRollDegrees(0, 0, 0); var pingPongGun = Entities.addEntity({ type: "Model", From 514cb7a329b835c282cdf448807a221143796eb0 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Fri, 18 Dec 2015 15:26:21 -0800 Subject: [PATCH 59/94] Beams arc to each other. BAM --- examples/flowArts/arcBall/arcBall.js | 11 ++--------- examples/flowArts/arcBall/arcBallEntityScript.js | 5 ++++- examples/flowArts/flowArtsHutSpawner.js | 12 ++++++------ 3 files changed, 12 insertions(+), 16 deletions(-) diff --git a/examples/flowArts/arcBall/arcBall.js b/examples/flowArts/arcBall/arcBall.js index 34578cce76..e39a9af2d6 100644 --- a/examples/flowArts/arcBall/arcBall.js +++ b/examples/flowArts/arcBall/arcBall.js @@ -26,7 +26,7 @@ ArcBall = function(spawnPosition) { var containerBall = Entities.addEntity({ - type: "Sphere", + type: "Box", name: "Arc Ball", script: scriptURL, position: Vec3.sum(spawnPosition, { @@ -37,7 +37,7 @@ ArcBall = function(spawnPosition) { dimensions: { x: .2, y: .2, - z: .2 + z: .5 }, color: { red: 15, @@ -53,13 +53,6 @@ ArcBall = function(spawnPosition) { // }, userData: JSON.stringify({ grabbableKey: { - spatialKey: { - relativePosition: { - x: 0, - y: .1, - z: 0 - } - }, invertSolidWhileHeld: true } }) diff --git a/examples/flowArts/arcBall/arcBallEntityScript.js b/examples/flowArts/arcBall/arcBallEntityScript.js index f1ba9aeb95..8c37f170bb 100644 --- a/examples/flowArts/arcBall/arcBallEntityScript.js +++ b/examples/flowArts/arcBall/arcBallEntityScript.js @@ -42,7 +42,8 @@ dimensions: {x: .1, y: .1, z: 1}, color: {red: 200, green: 10, blue: 10}, position: startPosition, - rotation: emitOrientation + rotation: emitOrientation, + visible: false }); var color = { red: 200, @@ -53,6 +54,8 @@ type: "ParticleEffect", name: "Particle Arc", parentID: this.entityID, + parentJointIndex: -1, + position: startPosition, isEmitting: true, colorStart: color, color: { diff --git a/examples/flowArts/flowArtsHutSpawner.js b/examples/flowArts/flowArtsHutSpawner.js index e03d2bf0e8..39dcf15698 100644 --- a/examples/flowArts/flowArtsHutSpawner.js +++ b/examples/flowArts/flowArtsHutSpawner.js @@ -25,10 +25,10 @@ var basePosition = Vec3.sum(MyAvatar.position, Vec3.multiply(1, Quat.getFront(Ca basePosition.y = MyAvatar.position.y + 1; // RAVE ITEMS -var lightBall = new LightBall(basePosition); +// var lightBall = new LightBall(basePosition); -// var arcBall = new ArcBall(basePosition); -// var arcBall2 = new ArcBall(Vec3.sum(basePosition, {x: -1, y: 0, z: 0})); +var arcBall = new ArcBall(basePosition); +var arcBall2 = new ArcBall(Vec3.sum(basePosition, {x: -1, y: 0, z: 0})); var raveStick = new RaveStick(Vec3.sum(basePosition, {x: 1, y: 0.5, z: 1})); var lightSaber = new LightSaber(Vec3.sum(basePosition, {x: 3, y: 0.5, z: 1})); @@ -81,9 +81,9 @@ function cleanup() { Entities.deleteEntity(raveRoom); Entities.deleteEntity(lightZone); Entities.deleteEntity(floor); - lightBall.cleanup(); - // arcBall.cleanup(); - // arcBall2.cleanup(); + // lightBall.cleanup(); + arcBall.cleanup(); + arcBall2.cleanup(); raveStick.cleanup(); lightSaber.cleanup(); } From fffe6dbb9bd32ec6dcb7ce71a8c787dcbc00df66 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Fri, 18 Dec 2015 15:38:01 -0800 Subject: [PATCH 60/94] relative rotation --- examples/flowArts/arcBall/arcBall.js | 13 ++++--------- examples/flowArts/arcBall/arcBallEntityScript.js | 4 +++- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/examples/flowArts/arcBall/arcBall.js b/examples/flowArts/arcBall/arcBall.js index e39a9af2d6..736920ddd5 100644 --- a/examples/flowArts/arcBall/arcBall.js +++ b/examples/flowArts/arcBall/arcBall.js @@ -35,22 +35,17 @@ ArcBall = function(spawnPosition) { z: 0 }), dimensions: { - x: .2, - y: .2, - z: .5 + x: .1, + y: .1, + z: .2 }, color: { red: 15, green: 10, blue: 150 }, - mass: 10, + damping: 0.8, collisionsWillMove: true, - // gravity: { - // x: 0, - // y: -0.5, - // z: 0 - // }, userData: JSON.stringify({ grabbableKey: { invertSolidWhileHeld: true diff --git a/examples/flowArts/arcBall/arcBallEntityScript.js b/examples/flowArts/arcBall/arcBallEntityScript.js index 8c37f170bb..4ad0d66257 100644 --- a/examples/flowArts/arcBall/arcBallEntityScript.js +++ b/examples/flowArts/arcBall/arcBallEntityScript.js @@ -34,8 +34,10 @@ createBeam: function(startPosition, endPosition) { print("CREATE BEAM") // Creates particle arc from start position to end position + var rotation = Entities.getEntityProperties(this.entityID, "rotation").rotation; var sourceToTargetVec = Vec3.subtract(endPosition, startPosition); var emitOrientation = Quat.rotationBetween(Vec3.UNIT_Z, sourceToTargetVec); + emitOrientation = Quat.multiply(Quat.inverse(rotation), emitOrientation); testBox = Entities.addEntity({ type: "Box", @@ -65,7 +67,7 @@ }, colorFinish: color, maxParticles: 100000, - lifespan: 2, + lifespan: 6, emitRate: 1000, emitOrientation: emitOrientation, emitSpeed: .4, From 1beec0dbe7c46327bc0553734fa5871bbf3fc90d Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Fri, 18 Dec 2015 18:15:05 -0800 Subject: [PATCH 61/94] balls --- examples/controllers/handControllerGrab.js | 21 ++++++---- examples/flowArts/arcBall/arcBall.js | 11 ++++- .../flowArts/arcBall/arcBallEntityScript.js | 42 ++++++++++++------- 3 files changed, 49 insertions(+), 25 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 07894b46d1..fb7ba8a4f6 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -861,20 +861,25 @@ function MyController(hand) { var handPosition = this.getHandPosition(); var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); - + var objectRotation = grabbedProperties.rotation; + var currentObjectPosition = grabbedProperties.position; + var offset = Vec3.subtract(currentObjectPosition, handPosition); if (this.state != STATE_NEAR_GRABBING && grabbableData.spatialKey) { // if an object is "equipped" and has a spatialKey, use it. this.ignoreIK = grabbableData.spatialKey.ignoreIK ? grabbableData.spatialKey.ignoreIK : false; - this.offsetPosition = getSpatialOffsetPosition(this.hand, grabbableData.spatialKey); - this.offsetRotation = getSpatialOffsetRotation(this.hand, grabbableData.spatialKey); + if (grabbableData.spatialKey.relativePosition) { + this.offsetPosition = getSpatialOffsetPosition(this.hand, grabbableData.spatialKey); + } else { + this.offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, this.offsetRotation)), offset); + } + if (grabbableData.spatialKey.relativeRotation) { + this.offsetRotation = getSpatialOffsetRotation(this.hand, grabbableData.spatialKey); + } else { + this.offsetRotation = Quat.multiply(Quat.inverse(handRotation), objectRotation); + } } else { this.ignoreIK = false; - - var objectRotation = grabbedProperties.rotation; this.offsetRotation = Quat.multiply(Quat.inverse(handRotation), objectRotation); - - var currentObjectPosition = grabbedProperties.position; - var offset = Vec3.subtract(currentObjectPosition, handPosition); this.offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, this.offsetRotation)), offset); } diff --git a/examples/flowArts/arcBall/arcBall.js b/examples/flowArts/arcBall/arcBall.js index 736920ddd5..f779f65643 100644 --- a/examples/flowArts/arcBall/arcBall.js +++ b/examples/flowArts/arcBall/arcBall.js @@ -26,7 +26,7 @@ ArcBall = function(spawnPosition) { var containerBall = Entities.addEntity({ - type: "Box", + type: "Sphere", name: "Arc Ball", script: scriptURL, position: Vec3.sum(spawnPosition, { @@ -37,7 +37,7 @@ ArcBall = function(spawnPosition) { dimensions: { x: .1, y: .1, - z: .2 + z: .1 }, color: { red: 15, @@ -48,6 +48,13 @@ ArcBall = function(spawnPosition) { collisionsWillMove: true, userData: JSON.stringify({ grabbableKey: { + spatialKey: { + relativePosition: { + x: 0, + y: 0, + z: -0.5 + }, + }, invertSolidWhileHeld: true } }) diff --git a/examples/flowArts/arcBall/arcBallEntityScript.js b/examples/flowArts/arcBall/arcBallEntityScript.js index 4ad0d66257..7cae0a9cb0 100644 --- a/examples/flowArts/arcBall/arcBallEntityScript.js +++ b/examples/flowArts/arcBall/arcBallEntityScript.js @@ -25,6 +25,7 @@ entities.forEach(function(entity) { var props = Entities.getEntityProperties(entity, ["position", "name"]); if (props.name === "Arc Ball" && JSON.stringify(_this.entityID) !== JSON.stringify(entity)) { + _this.target = entity; _this.createBeam(position, props.position); } }); @@ -32,21 +33,13 @@ }, createBeam: function(startPosition, endPosition) { - print("CREATE BEAM") // Creates particle arc from start position to end position var rotation = Entities.getEntityProperties(this.entityID, "rotation").rotation; var sourceToTargetVec = Vec3.subtract(endPosition, startPosition); var emitOrientation = Quat.rotationBetween(Vec3.UNIT_Z, sourceToTargetVec); emitOrientation = Quat.multiply(Quat.inverse(rotation), emitOrientation); - testBox = Entities.addEntity({ - type: "Box", - dimensions: {x: .1, y: .1, z: 1}, - color: {red: 200, green: 10, blue: 10}, - position: startPosition, - rotation: emitOrientation, - visible: false - }); + var color = { red: 200, green: 10, @@ -57,7 +50,7 @@ name: "Particle Arc", parentID: this.entityID, parentJointIndex: -1, - position: startPosition, + // position: startPosition, isEmitting: true, colorStart: color, color: { @@ -67,7 +60,7 @@ }, colorFinish: color, maxParticles: 100000, - lifespan: 6, + lifespan: 5, emitRate: 1000, emitOrientation: emitOrientation, emitSpeed: .4, @@ -103,13 +96,32 @@ this.particleArc = Entities.addEntity(props); }, - continueNearGrab: function() {}, + updateBeam: function(startPosition) { + var targetPosition = Entities.getEntityProperties(this.target, "position").position; + print("TARGET position " + JSON.stringify(this.target)); + var rotation = Entities.getEntityProperties(this.entityID, "rotation").rotation; + var sourceToTargetVec = Vec3.subtract(targetPosition, startPosition); + var emitOrientation = Quat.rotationBetween(Vec3.UNIT_Z, sourceToTargetVec); + emitOrientation = Quat.multiply(Quat.inverse(rotation), emitOrientation); + Entities.editEntity(this.particleArc, {emitOrientation: emitOrientation}); + Entities.editEntity(this.testBox, {rotation: emitOrientation}); + }, - releaseGrab: function() {}, + continueNearGrab: function() { + var startPosition = Entities.getEntityProperties(this.entityID, "position").position; + this.updateBeam(startPosition); + }, + + releaseGrab: function() { + Entities.editEntity(this.particleArc, { + isEmitting: false + }); + }, unload: function() { - Entities.deleteEntity(this.particleArc); - Entities.deleteEntity(testBox); + if (this.particleArc) { + Entities.deleteEntity(this.particleArc); + } }, preload: function(entityID) { From 7a7befafb9e4afc78039d4a4f99da1b53763ea5f Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Mon, 21 Dec 2015 10:43:59 -0800 Subject: [PATCH 62/94] arc balls working --- examples/controllers/handControllerGrab.js | 54 ++++++++++++------- examples/flowArts/arcBall/arcBall.js | 27 ++++++---- .../flowArts/arcBall/arcBallEntityScript.js | 47 ++++++++++------ 3 files changed, 81 insertions(+), 47 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 07894b46d1..6cacc2e80a 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -218,6 +218,7 @@ function getSpatialOffsetPosition(hand, spatialKey) { } var yFlip = Quat.angleAxis(180, Vec3.UNIT_Y); + function getSpatialOffsetRotation(hand, spatialKey) { var rotation = Quat.IDENTITY; @@ -261,9 +262,9 @@ function MyController(hand) { this.triggerValue = 0; // rolling average of trigger value this.rawTriggerValue = 0; this.rawBumperValue = 0; - + this.overlayLine = null; - + this.ignoreIK = false; this.offsetPosition = Vec3.ZERO; this.offsetRotation = Quat.IDENTITY; @@ -819,8 +820,16 @@ function MyController(hand) { // mix in head motion if (MOVE_WITH_HEAD) { var objDistance = Vec3.length(objectToAvatar); - var before = Vec3.multiplyQbyV(this.currentCameraOrientation, { x: 0.0, y: 0.0, z: objDistance }); - var after = Vec3.multiplyQbyV(Camera.orientation, { x: 0.0, y: 0.0, z: objDistance }); + var before = Vec3.multiplyQbyV(this.currentCameraOrientation, { + x: 0.0, + y: 0.0, + z: objDistance + }); + var after = Vec3.multiplyQbyV(Camera.orientation, { + x: 0.0, + y: 0.0, + z: objDistance + }); var change = Vec3.subtract(before, after); this.currentCameraOrientation = Camera.orientation; this.currentObjectPosition = Vec3.sum(this.currentObjectPosition, change); @@ -861,20 +870,25 @@ function MyController(hand) { var handPosition = this.getHandPosition(); var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); - + var objectRotation = grabbedProperties.rotation; + var currentObjectPosition = grabbedProperties.position; + var offset = Vec3.subtract(currentObjectPosition, handPosition); if (this.state != STATE_NEAR_GRABBING && grabbableData.spatialKey) { // if an object is "equipped" and has a spatialKey, use it. this.ignoreIK = grabbableData.spatialKey.ignoreIK ? grabbableData.spatialKey.ignoreIK : false; - this.offsetPosition = getSpatialOffsetPosition(this.hand, grabbableData.spatialKey); - this.offsetRotation = getSpatialOffsetRotation(this.hand, grabbableData.spatialKey); + if (grabbableData.spatialKey.relativePosition) { + this.offsetPosition = getSpatialOffsetPosition(this.hand, grabbableData.spatialKey); + } else { + this.offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, this.offsetRotation)), offset); + } + if (grabbableData.spatialKey.relativeRotation) { + this.offsetRotation = getSpatialOffsetRotation(this.hand, grabbableData.spatialKey); + } else { + this.offsetRotation = Quat.multiply(Quat.inverse(handRotation), objectRotation); + } } else { this.ignoreIK = false; - - var objectRotation = grabbedProperties.rotation; this.offsetRotation = Quat.multiply(Quat.inverse(handRotation), objectRotation); - - var currentObjectPosition = grabbedProperties.position; - var offset = Vec3.subtract(currentObjectPosition, handPosition); this.offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, this.offsetRotation)), offset); } @@ -1304,10 +1318,10 @@ Controller.enableMapping(MAPPING_NAME); var handToDisable = 'none'; function update() { - if (handToDisable !== LEFT_HAND && handToDisable!=='both') { + if (handToDisable !== LEFT_HAND && handToDisable !== 'both') { leftController.update(); } - if (handToDisable !== RIGHT_HAND && handToDisable!=='both') { + if (handToDisable !== RIGHT_HAND && handToDisable !== 'both') { rightController.update(); } } @@ -1315,7 +1329,7 @@ function update() { Messages.subscribe('Hifi-Hand-Disabler'); handleHandDisablerMessages = function(channel, message, sender) { - + if (sender === MyAvatar.sessionUUID) { if (message === 'left') { handToDisable = LEFT_HAND; @@ -1323,11 +1337,11 @@ handleHandDisablerMessages = function(channel, message, sender) { if (message === 'right') { handToDisable = RIGHT_HAND; } - if(message==='both'){ - handToDisable='both'; + if (message === 'both') { + handToDisable = 'both'; } - if(message==='none'){ - handToDisable='none'; + if (message === 'none') { + handToDisable = 'none'; } } @@ -1342,4 +1356,4 @@ function cleanup() { } Script.scriptEnding.connect(cleanup); -Script.update.connect(update); +Script.update.connect(update); \ No newline at end of file diff --git a/examples/flowArts/arcBall/arcBall.js b/examples/flowArts/arcBall/arcBall.js index 736920ddd5..54cb16fe15 100644 --- a/examples/flowArts/arcBall/arcBall.js +++ b/examples/flowArts/arcBall/arcBall.js @@ -26,7 +26,7 @@ ArcBall = function(spawnPosition) { var containerBall = Entities.addEntity({ - type: "Box", + type: "Sphere", name: "Arc Ball", script: scriptURL, position: Vec3.sum(spawnPosition, { @@ -35,12 +35,12 @@ ArcBall = function(spawnPosition) { z: 0 }), dimensions: { - x: .1, - y: .1, - z: .2 + x: .05, + y: .05, + z: .05 }, color: { - red: 15, + red: 100, green: 10, blue: 150 }, @@ -48,6 +48,13 @@ ArcBall = function(spawnPosition) { collisionsWillMove: true, userData: JSON.stringify({ grabbableKey: { + spatialKey: { + relativePosition: { + x: 0, + y: 0, + z: -0.5 + }, + }, invertSolidWhileHeld: true } }) @@ -127,11 +134,11 @@ ArcBall = function(spawnPosition) { - function cleanup() { - Entities.deleteEntity(arcBall); - Entities.deleteEntity(containerBall); - Entities.deleteEntity(light); - } + function cleanup() { + Entities.deleteEntity(arcBall); + Entities.deleteEntity(containerBall); + Entities.deleteEntity(light); + } this.cleanup = cleanup; } \ No newline at end of file diff --git a/examples/flowArts/arcBall/arcBallEntityScript.js b/examples/flowArts/arcBall/arcBallEntityScript.js index 4ad0d66257..7bb8472ce9 100644 --- a/examples/flowArts/arcBall/arcBallEntityScript.js +++ b/examples/flowArts/arcBall/arcBallEntityScript.js @@ -25,6 +25,7 @@ entities.forEach(function(entity) { var props = Entities.getEntityProperties(entity, ["position", "name"]); if (props.name === "Arc Ball" && JSON.stringify(_this.entityID) !== JSON.stringify(entity)) { + _this.target = entity; _this.createBeam(position, props.position); } }); @@ -32,21 +33,13 @@ }, createBeam: function(startPosition, endPosition) { - print("CREATE BEAM") - // Creates particle arc from start position to end position + // Creates particle arc from start position to end position var rotation = Entities.getEntityProperties(this.entityID, "rotation").rotation; var sourceToTargetVec = Vec3.subtract(endPosition, startPosition); var emitOrientation = Quat.rotationBetween(Vec3.UNIT_Z, sourceToTargetVec); emitOrientation = Quat.multiply(Quat.inverse(rotation), emitOrientation); - testBox = Entities.addEntity({ - type: "Box", - dimensions: {x: .1, y: .1, z: 1}, - color: {red: 200, green: 10, blue: 10}, - position: startPosition, - rotation: emitOrientation, - visible: false - }); + var color = { red: 200, green: 10, @@ -57,7 +50,7 @@ name: "Particle Arc", parentID: this.entityID, parentJointIndex: -1, - position: startPosition, + // position: startPosition, isEmitting: true, colorStart: color, color: { @@ -67,7 +60,7 @@ }, colorFinish: color, maxParticles: 100000, - lifespan: 6, + lifespan: 5, emitRate: 1000, emitOrientation: emitOrientation, emitSpeed: .4, @@ -98,18 +91,38 @@ alphaStart: 0.5, alphaFinish: 0.5, textures: "https://s3.amazonaws.com/hifi-public/eric/textures/particleSprites/beamParticle.png", - emitterShouldTrail: false + emitterShouldTrail: true } this.particleArc = Entities.addEntity(props); }, - continueNearGrab: function() {}, + updateBeam: function(startPosition) { + var targetPosition = Entities.getEntityProperties(this.target, "position").position; + print("TARGET position " + JSON.stringify(this.target)); + var rotation = Entities.getEntityProperties(this.entityID, "rotation").rotation; + var sourceToTargetVec = Vec3.subtract(targetPosition, startPosition); + var emitOrientation = Quat.rotationBetween(Vec3.UNIT_Z, sourceToTargetVec); + // emitOrientation = Quat.multiply(emitOrientation,Quat.inverse(rotation)); + Entities.editEntity(this.particleArc, { + emitOrientation: emitOrientation + }); + }, - releaseGrab: function() {}, + continueNearGrab: function() { + var startPosition = Entities.getEntityProperties(this.entityID, "position").position; + this.updateBeam(startPosition); + }, + + releaseGrab: function() { + Entities.editEntity(this.particleArc, { + isEmitting: false + }); + }, unload: function() { - Entities.deleteEntity(this.particleArc); - Entities.deleteEntity(testBox); + if (this.particleArc) { + Entities.deleteEntity(this.particleArc); + } }, preload: function(entityID) { From 45ba7a3d35c12e55207acbee615757f022e655e1 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Mon, 21 Dec 2015 10:46:54 -0800 Subject: [PATCH 63/94] merged --- examples/controllers/handControllerGrab.js | 10 --------- .../flowArts/arcBall/arcBallEntityScript.js | 21 ------------------- 2 files changed, 31 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 6337079dc5..6cacc2e80a 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -877,24 +877,14 @@ function MyController(hand) { // if an object is "equipped" and has a spatialKey, use it. this.ignoreIK = grabbableData.spatialKey.ignoreIK ? grabbableData.spatialKey.ignoreIK : false; if (grabbableData.spatialKey.relativePosition) { -<<<<<<< HEAD this.offsetPosition = getSpatialOffsetPosition(this.hand, grabbableData.spatialKey); -======= - this.offsetPosition = getSpatialOffsetPosition(this.hand, grabbableData.spatialKey); ->>>>>>> origin/polylineOptimizations } else { this.offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, this.offsetRotation)), offset); } if (grabbableData.spatialKey.relativeRotation) { -<<<<<<< HEAD this.offsetRotation = getSpatialOffsetRotation(this.hand, grabbableData.spatialKey); } else { this.offsetRotation = Quat.multiply(Quat.inverse(handRotation), objectRotation); -======= - this.offsetRotation = getSpatialOffsetRotation(this.hand, grabbableData.spatialKey); - } else { - this.offsetRotation = Quat.multiply(Quat.inverse(handRotation), objectRotation); ->>>>>>> origin/polylineOptimizations } } else { this.ignoreIK = false; diff --git a/examples/flowArts/arcBall/arcBallEntityScript.js b/examples/flowArts/arcBall/arcBallEntityScript.js index 539bdd7cf0..7bb8472ce9 100644 --- a/examples/flowArts/arcBall/arcBallEntityScript.js +++ b/examples/flowArts/arcBall/arcBallEntityScript.js @@ -33,21 +33,13 @@ }, createBeam: function(startPosition, endPosition) { -<<<<<<< HEAD // Creates particle arc from start position to end position -======= - // Creates particle arc from start position to end position ->>>>>>> origin/polylineOptimizations var rotation = Entities.getEntityProperties(this.entityID, "rotation").rotation; var sourceToTargetVec = Vec3.subtract(endPosition, startPosition); var emitOrientation = Quat.rotationBetween(Vec3.UNIT_Z, sourceToTargetVec); emitOrientation = Quat.multiply(Quat.inverse(rotation), emitOrientation); -<<<<<<< HEAD -======= - ->>>>>>> origin/polylineOptimizations var color = { red: 200, green: 10, @@ -110,7 +102,6 @@ var rotation = Entities.getEntityProperties(this.entityID, "rotation").rotation; var sourceToTargetVec = Vec3.subtract(targetPosition, startPosition); var emitOrientation = Quat.rotationBetween(Vec3.UNIT_Z, sourceToTargetVec); -<<<<<<< HEAD // emitOrientation = Quat.multiply(emitOrientation,Quat.inverse(rotation)); Entities.editEntity(this.particleArc, { emitOrientation: emitOrientation @@ -122,18 +113,6 @@ this.updateBeam(startPosition); }, -======= - emitOrientation = Quat.multiply(Quat.inverse(rotation), emitOrientation); - Entities.editEntity(this.particleArc, {emitOrientation: emitOrientation}); - Entities.editEntity(this.testBox, {rotation: emitOrientation}); - }, - - continueNearGrab: function() { - var startPosition = Entities.getEntityProperties(this.entityID, "position").position; - this.updateBeam(startPosition); - }, - ->>>>>>> origin/polylineOptimizations releaseGrab: function() { Entities.editEntity(this.particleArc, { isEmitting: false From 48ab99c34864f5ecd2734900e4d89b29f964288d Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Mon, 21 Dec 2015 11:00:10 -0800 Subject: [PATCH 64/94] rotation fix --- examples/flowArts/arcBall/arcBall.js | 8 +- .../flowArts/arcBall/arcBallEntityScript.js | 112 +++++++++--------- 2 files changed, 63 insertions(+), 57 deletions(-) diff --git a/examples/flowArts/arcBall/arcBall.js b/examples/flowArts/arcBall/arcBall.js index 54cb16fe15..f6860b54c6 100644 --- a/examples/flowArts/arcBall/arcBall.js +++ b/examples/flowArts/arcBall/arcBall.js @@ -31,7 +31,7 @@ ArcBall = function(spawnPosition) { script: scriptURL, position: Vec3.sum(spawnPosition, { x: 0, - y: .5, + y: .7, z: 0 }), dimensions: { @@ -51,8 +51,8 @@ ArcBall = function(spawnPosition) { spatialKey: { relativePosition: { x: 0, - y: 0, - z: -0.5 + y: -0.5, + z: 0.0 }, }, invertSolidWhileHeld: true @@ -97,7 +97,7 @@ ArcBall = function(spawnPosition) { }, maxParticles: 100000, lifespan: 2, - emitRate: 1000, + emitRate: 400, emitSpeed: .1, lifetime: -1, speedSpread: 0.0, diff --git a/examples/flowArts/arcBall/arcBallEntityScript.js b/examples/flowArts/arcBall/arcBallEntityScript.js index 7bb8472ce9..7edf22ecdb 100644 --- a/examples/flowArts/arcBall/arcBallEntityScript.js +++ b/examples/flowArts/arcBall/arcBallEntityScript.js @@ -14,6 +14,15 @@ var _this; var ArcBall = function() { _this = this; + this.colorPalette = [{ + red: 25, + green: 20, + blue: 162 + }, { + red: 200, + green: 10, + blue: 10 + }]; }; ArcBall.prototype = { @@ -40,59 +49,56 @@ emitOrientation = Quat.multiply(Quat.inverse(rotation), emitOrientation); - var color = { - red: 200, - green: 10, - blue: 10 - }; - var props = { - type: "ParticleEffect", - name: "Particle Arc", - parentID: this.entityID, - parentJointIndex: -1, - // position: startPosition, - isEmitting: true, - colorStart: color, - color: { - red: 200, - green: 200, - blue: 255 - }, - colorFinish: color, - maxParticles: 100000, - lifespan: 5, - emitRate: 1000, - emitOrientation: emitOrientation, - emitSpeed: .4, - speedSpread: 0.0, - emitDimensions: { - x: 0, - y: 0, - z: 0 - }, - polarStart: 0, - polarFinish: .0, - azimuthStart: .1, - azimuthFinish: .01, - emitAcceleration: { - x: 0, - y: 0, - z: 0 - }, - accelerationSpread: { - x: .00, - y: .00, - z: .00 - }, - radiusStart: 0.03, - adiusFinish: 0.025, - alpha: 0.7, - alphaSpread: .1, - alphaStart: 0.5, - alphaFinish: 0.5, - textures: "https://s3.amazonaws.com/hifi-public/eric/textures/particleSprites/beamParticle.png", - emitterShouldTrail: true - } + var color = this.colorPalette[randInt(0, this.colorPalette.length)]; + var props = { + type: "ParticleEffect", + name: "Particle Arc", + parentID: this.entityID, + parentJointIndex: -1, + // position: startPosition, + isEmitting: true, + colorStart: color, + color: { + red: 200, + green: 200, + blue: 255 + }, + colorFinish: color, + maxParticles: 100000, + lifespan: 2, + emitRate: 500, + emitOrientation: emitOrientation, + emitSpeed: .4, + speedSpread: 0.1, + emitDimensions: { + x: 0, + y: 0, + z: 0 + }, + polarStart: 0, + polarFinish: .0, + azimuthStart: .1, + azimuthFinish: .01, + emitAcceleration: { + x: 0, + y: 0, + z: 0 + }, + accelerationSpread: { + x: .00, + y: .00, + z: .00 + }, + radiusStart: 0.03, + radiusFinish: 0.025, + radiusSpread: .01, + alpha: 0.7, + alphaSpread: .1, + alphaStart: 0.5, + alphaFinish: 0.5, + textures: "https://s3.amazonaws.com/hifi-public/eric/textures/particleSprites/beamParticle.png", + emitterShouldTrail: true + } this.particleArc = Entities.addEntity(props); }, From 3c66e3826830a2f9bc84697da5e4d7b61dc77423 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Mon, 21 Dec 2015 11:12:59 -0800 Subject: [PATCH 65/94] test --- examples/flowArts/arcBall/arcBall.js | 7 +++--- .../flowArts/arcBall/arcBallEntityScript.js | 22 +++++++++---------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/examples/flowArts/arcBall/arcBall.js b/examples/flowArts/arcBall/arcBall.js index f6860b54c6..e67dffddff 100644 --- a/examples/flowArts/arcBall/arcBall.js +++ b/examples/flowArts/arcBall/arcBall.js @@ -44,6 +44,7 @@ ArcBall = function(spawnPosition) { green: 10, blue: 150 }, + ignoreForCollisions: true, damping: 0.8, collisionsWillMove: true, userData: JSON.stringify({ @@ -51,11 +52,11 @@ ArcBall = function(spawnPosition) { spatialKey: { relativePosition: { x: 0, - y: -0.5, - z: 0.0 + y: 0.0, + z: -0.5 }, }, - invertSolidWhileHeld: true + // invertSolidWhileHeld: true } }) }); diff --git a/examples/flowArts/arcBall/arcBallEntityScript.js b/examples/flowArts/arcBall/arcBallEntityScript.js index 7edf22ecdb..0906b90b53 100644 --- a/examples/flowArts/arcBall/arcBallEntityScript.js +++ b/examples/flowArts/arcBall/arcBallEntityScript.js @@ -65,15 +65,15 @@ }, colorFinish: color, maxParticles: 100000, - lifespan: 2, - emitRate: 500, + lifespan: 1, + emitRate: 1000, emitOrientation: emitOrientation, - emitSpeed: .4, + emitSpeed: .2, speedSpread: 0.1, emitDimensions: { - x: 0, - y: 0, - z: 0 + x: .1, + y: .1, + z: .1 }, polarStart: 0, polarFinish: .0, @@ -89,13 +89,13 @@ y: .00, z: .00 }, - radiusStart: 0.03, - radiusFinish: 0.025, - radiusSpread: .01, - alpha: 0.7, + radiusStart: 0.01, + radiusFinish: 0.005, + radiusSpread: .005, + alpha: 0.5, alphaSpread: .1, alphaStart: 0.5, - alphaFinish: 0.5, + alphaFinish: 0.0, textures: "https://s3.amazonaws.com/hifi-public/eric/textures/particleSprites/beamParticle.png", emitterShouldTrail: true } From ba3633710a9dfb0663f386a8f532fd5bd83e1c5f Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Mon, 21 Dec 2015 11:29:59 -0800 Subject: [PATCH 66/94] name fixes --- examples/flowArts/arcBall/arcBall.js | 258 +++++++++--------- examples/flowArts/flowArtsHutSpawner.js | 8 +- .../{LightSaber.js => lightSaber.js} | 0 .../raveStick/{RaveStick.js => raveStick.js} | 0 4 files changed, 130 insertions(+), 136 deletions(-) rename examples/flowArts/lightSaber/{LightSaber.js => lightSaber.js} (100%) rename examples/flowArts/raveStick/{RaveStick.js => raveStick.js} (100%) diff --git a/examples/flowArts/arcBall/arcBall.js b/examples/flowArts/arcBall/arcBall.js index e67dffddff..0906b90b53 100644 --- a/examples/flowArts/arcBall/arcBall.js +++ b/examples/flowArts/arcBall/arcBall.js @@ -1,145 +1,139 @@ -// -// arcBall.js -// examples/arcBall -// +// arcBallEntityScript.js +// +// Script Type: Entity // Created by Eric Levin on 12/17/15. -// Copyright 2014 High Fidelity, Inc. -// -// This script creats a particle light ball which makes particle trails as you move it. -// +// Copyright 2015 High Fidelity, Inc. // +// This entity script handles the logic for the arcBall rave toy // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -Script.include("../../libraries/utils.js"); - - -var scriptURL = Script.resolvePath("arcBallEntityScript.js"); -ArcBall = function(spawnPosition) { - - var colorPalette = [{ - red: 25, - green: 20, - blue: 162 - }]; - - - var containerBall = Entities.addEntity({ - type: "Sphere", - name: "Arc Ball", - script: scriptURL, - position: Vec3.sum(spawnPosition, { - x: 0, - y: .7, - z: 0 - }), - dimensions: { - x: .05, - y: .05, - z: .05 - }, - color: { - red: 100, - green: 10, - blue: 150 - }, - ignoreForCollisions: true, - damping: 0.8, - collisionsWillMove: true, - userData: JSON.stringify({ - grabbableKey: { - spatialKey: { - relativePosition: { - x: 0, - y: 0.0, - z: -0.5 - }, - }, - // invertSolidWhileHeld: true - } - }) - }); - - - var light = Entities.addEntity({ - type: 'Light', - name: "ballLight", - parentID: containerBall, - dimensions: { - x: 30, - y: 30, - z: 30 - }, - color: colorPalette[randInt(0, colorPalette.length)], - intensity: 5 - }); - - - var arcBall = Entities.addEntity({ - type: "ParticleEffect", - parentID: containerBall, - isEmitting: true, - name: "Arc Ball Particle Effect", - colorStart: { - red: 200, - green: 20, - blue: 40 - }, - color: { - red: 200, - green: 200, - blue: 255 - }, - colorFinish: { +(function() { + Script.include("../../libraries/utils.js"); + var _this; + var ArcBall = function() { + _this = this; + this.colorPalette = [{ red: 25, green: 20, - blue: 255 + blue: 162 + }, { + red: 200, + green: 10, + blue: 10 + }]; + }; + + ArcBall.prototype = { + isGrabbed: false, + startNearGrab: function() { + //Search for nearby balls and create an arc to it if one is found + var position = Entities.getEntityProperties(this.entityID, "position").position + var entities = Entities.findEntities(position, 10); + entities.forEach(function(entity) { + var props = Entities.getEntityProperties(entity, ["position", "name"]); + if (props.name === "Arc Ball" && JSON.stringify(_this.entityID) !== JSON.stringify(entity)) { + _this.target = entity; + _this.createBeam(position, props.position); + } + }); + }, - maxParticles: 100000, - lifespan: 2, - emitRate: 400, - emitSpeed: .1, - lifetime: -1, - speedSpread: 0.0, - emitDimensions: { - x: 0, - y: 0, - z: 0 - }, - polarStart: 0, - polarFinish: Math.PI, - azimuthStart: -Math.PI, - azimuthFinish: Math.PI, - emitAcceleration: { - x: 0, - y: 0, - z: 0 - }, - accelerationSpread: { - x: .00, - y: .00, - z: .00 - }, - particleRadius: 0.02, - radiusSpread: 0, - radiusStart: 0.03, - radiusFinish: 0.0003, - alpha: 0, - alphaSpread: .5, - alphaStart: 0, - alphaFinish: 0.5, - textures: "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png", - emitterShouldTrail: true - }) + + createBeam: function(startPosition, endPosition) { + // Creates particle arc from start position to end position + var rotation = Entities.getEntityProperties(this.entityID, "rotation").rotation; + var sourceToTargetVec = Vec3.subtract(endPosition, startPosition); + var emitOrientation = Quat.rotationBetween(Vec3.UNIT_Z, sourceToTargetVec); + emitOrientation = Quat.multiply(Quat.inverse(rotation), emitOrientation); + var color = this.colorPalette[randInt(0, this.colorPalette.length)]; + var props = { + type: "ParticleEffect", + name: "Particle Arc", + parentID: this.entityID, + parentJointIndex: -1, + // position: startPosition, + isEmitting: true, + colorStart: color, + color: { + red: 200, + green: 200, + blue: 255 + }, + colorFinish: color, + maxParticles: 100000, + lifespan: 1, + emitRate: 1000, + emitOrientation: emitOrientation, + emitSpeed: .2, + speedSpread: 0.1, + emitDimensions: { + x: .1, + y: .1, + z: .1 + }, + polarStart: 0, + polarFinish: .0, + azimuthStart: .1, + azimuthFinish: .01, + emitAcceleration: { + x: 0, + y: 0, + z: 0 + }, + accelerationSpread: { + x: .00, + y: .00, + z: .00 + }, + radiusStart: 0.01, + radiusFinish: 0.005, + radiusSpread: .005, + alpha: 0.5, + alphaSpread: .1, + alphaStart: 0.5, + alphaFinish: 0.0, + textures: "https://s3.amazonaws.com/hifi-public/eric/textures/particleSprites/beamParticle.png", + emitterShouldTrail: true + } + this.particleArc = Entities.addEntity(props); + }, - function cleanup() { - Entities.deleteEntity(arcBall); - Entities.deleteEntity(containerBall); - Entities.deleteEntity(light); - } + updateBeam: function(startPosition) { + var targetPosition = Entities.getEntityProperties(this.target, "position").position; + print("TARGET position " + JSON.stringify(this.target)); + var rotation = Entities.getEntityProperties(this.entityID, "rotation").rotation; + var sourceToTargetVec = Vec3.subtract(targetPosition, startPosition); + var emitOrientation = Quat.rotationBetween(Vec3.UNIT_Z, sourceToTargetVec); + // emitOrientation = Quat.multiply(emitOrientation,Quat.inverse(rotation)); + Entities.editEntity(this.particleArc, { + emitOrientation: emitOrientation + }); + }, - this.cleanup = cleanup; -} \ No newline at end of file + continueNearGrab: function() { + var startPosition = Entities.getEntityProperties(this.entityID, "position").position; + this.updateBeam(startPosition); + }, + + releaseGrab: function() { + Entities.editEntity(this.particleArc, { + isEmitting: false + }); + }, + + unload: function() { + if (this.particleArc) { + Entities.deleteEntity(this.particleArc); + } + }, + + preload: function(entityID) { + this.entityID = entityID; + }, + }; + return new ArcBall(); +}); \ No newline at end of file diff --git a/examples/flowArts/flowArtsHutSpawner.js b/examples/flowArts/flowArtsHutSpawner.js index 39dcf15698..2c87a27c15 100644 --- a/examples/flowArts/flowArtsHutSpawner.js +++ b/examples/flowArts/flowArtsHutSpawner.js @@ -14,10 +14,10 @@ Script.include("../../libraries/utils.js"); -Script.include("lightBall/LightBall.js"); -Script.include("raveStick/RaveStick.js"); -Script.include("lightSaber/LightSaber.js"); -Script.include("arcBall/ArcBall.js"); +Script.include("lightBall/lightBall.js"); +Script.include("raveStick/raveStick.js"); +Script.include("lightSaber/lightSaber.js"); +Script.include("arcBall/arcBall.js"); diff --git a/examples/flowArts/lightSaber/LightSaber.js b/examples/flowArts/lightSaber/lightSaber.js similarity index 100% rename from examples/flowArts/lightSaber/LightSaber.js rename to examples/flowArts/lightSaber/lightSaber.js diff --git a/examples/flowArts/raveStick/RaveStick.js b/examples/flowArts/raveStick/raveStick.js similarity index 100% rename from examples/flowArts/raveStick/RaveStick.js rename to examples/flowArts/raveStick/raveStick.js From b89e25f8f7f6fe1138c9521c9b309111f164838d Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Mon, 21 Dec 2015 11:35:57 -0800 Subject: [PATCH 67/94] correct file --- examples/flowArts/arcBall/arcBall.js | 256 ++++++++++++++------------- 1 file changed, 131 insertions(+), 125 deletions(-) diff --git a/examples/flowArts/arcBall/arcBall.js b/examples/flowArts/arcBall/arcBall.js index 0906b90b53..e67dffddff 100644 --- a/examples/flowArts/arcBall/arcBall.js +++ b/examples/flowArts/arcBall/arcBall.js @@ -1,139 +1,145 @@ -// arcBallEntityScript.js -// -// Script Type: Entity -// Created by Eric Levin on 12/17/15. -// Copyright 2015 High Fidelity, Inc. // -// This entity script handles the logic for the arcBall rave toy +// arcBall.js +// examples/arcBall +// +// Created by Eric Levin on 12/17/15. +// Copyright 2014 High Fidelity, Inc. +// +// This script creats a particle light ball which makes particle trails as you move it. +// +// // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -(function() { - Script.include("../../libraries/utils.js"); - var _this; - var ArcBall = function() { - _this = this; - this.colorPalette = [{ +Script.include("../../libraries/utils.js"); + + +var scriptURL = Script.resolvePath("arcBallEntityScript.js"); +ArcBall = function(spawnPosition) { + + var colorPalette = [{ + red: 25, + green: 20, + blue: 162 + }]; + + + var containerBall = Entities.addEntity({ + type: "Sphere", + name: "Arc Ball", + script: scriptURL, + position: Vec3.sum(spawnPosition, { + x: 0, + y: .7, + z: 0 + }), + dimensions: { + x: .05, + y: .05, + z: .05 + }, + color: { + red: 100, + green: 10, + blue: 150 + }, + ignoreForCollisions: true, + damping: 0.8, + collisionsWillMove: true, + userData: JSON.stringify({ + grabbableKey: { + spatialKey: { + relativePosition: { + x: 0, + y: 0.0, + z: -0.5 + }, + }, + // invertSolidWhileHeld: true + } + }) + }); + + + var light = Entities.addEntity({ + type: 'Light', + name: "ballLight", + parentID: containerBall, + dimensions: { + x: 30, + y: 30, + z: 30 + }, + color: colorPalette[randInt(0, colorPalette.length)], + intensity: 5 + }); + + + var arcBall = Entities.addEntity({ + type: "ParticleEffect", + parentID: containerBall, + isEmitting: true, + name: "Arc Ball Particle Effect", + colorStart: { + red: 200, + green: 20, + blue: 40 + }, + color: { + red: 200, + green: 200, + blue: 255 + }, + colorFinish: { red: 25, green: 20, - blue: 162 - }, { - red: 200, - green: 10, - blue: 10 - }]; - }; - - ArcBall.prototype = { - isGrabbed: false, - startNearGrab: function() { - //Search for nearby balls and create an arc to it if one is found - var position = Entities.getEntityProperties(this.entityID, "position").position - var entities = Entities.findEntities(position, 10); - entities.forEach(function(entity) { - var props = Entities.getEntityProperties(entity, ["position", "name"]); - if (props.name === "Arc Ball" && JSON.stringify(_this.entityID) !== JSON.stringify(entity)) { - _this.target = entity; - _this.createBeam(position, props.position); - } - }); - + blue: 255 }, - - createBeam: function(startPosition, endPosition) { - // Creates particle arc from start position to end position - var rotation = Entities.getEntityProperties(this.entityID, "rotation").rotation; - var sourceToTargetVec = Vec3.subtract(endPosition, startPosition); - var emitOrientation = Quat.rotationBetween(Vec3.UNIT_Z, sourceToTargetVec); - emitOrientation = Quat.multiply(Quat.inverse(rotation), emitOrientation); - - - var color = this.colorPalette[randInt(0, this.colorPalette.length)]; - var props = { - type: "ParticleEffect", - name: "Particle Arc", - parentID: this.entityID, - parentJointIndex: -1, - // position: startPosition, - isEmitting: true, - colorStart: color, - color: { - red: 200, - green: 200, - blue: 255 - }, - colorFinish: color, - maxParticles: 100000, - lifespan: 1, - emitRate: 1000, - emitOrientation: emitOrientation, - emitSpeed: .2, - speedSpread: 0.1, - emitDimensions: { - x: .1, - y: .1, - z: .1 - }, - polarStart: 0, - polarFinish: .0, - azimuthStart: .1, - azimuthFinish: .01, - emitAcceleration: { - x: 0, - y: 0, - z: 0 - }, - accelerationSpread: { - x: .00, - y: .00, - z: .00 - }, - radiusStart: 0.01, - radiusFinish: 0.005, - radiusSpread: .005, - alpha: 0.5, - alphaSpread: .1, - alphaStart: 0.5, - alphaFinish: 0.0, - textures: "https://s3.amazonaws.com/hifi-public/eric/textures/particleSprites/beamParticle.png", - emitterShouldTrail: true - } - this.particleArc = Entities.addEntity(props); + maxParticles: 100000, + lifespan: 2, + emitRate: 400, + emitSpeed: .1, + lifetime: -1, + speedSpread: 0.0, + emitDimensions: { + x: 0, + y: 0, + z: 0 }, - - updateBeam: function(startPosition) { - var targetPosition = Entities.getEntityProperties(this.target, "position").position; - print("TARGET position " + JSON.stringify(this.target)); - var rotation = Entities.getEntityProperties(this.entityID, "rotation").rotation; - var sourceToTargetVec = Vec3.subtract(targetPosition, startPosition); - var emitOrientation = Quat.rotationBetween(Vec3.UNIT_Z, sourceToTargetVec); - // emitOrientation = Quat.multiply(emitOrientation,Quat.inverse(rotation)); - Entities.editEntity(this.particleArc, { - emitOrientation: emitOrientation - }); + polarStart: 0, + polarFinish: Math.PI, + azimuthStart: -Math.PI, + azimuthFinish: Math.PI, + emitAcceleration: { + x: 0, + y: 0, + z: 0 }, - - continueNearGrab: function() { - var startPosition = Entities.getEntityProperties(this.entityID, "position").position; - this.updateBeam(startPosition); + accelerationSpread: { + x: .00, + y: .00, + z: .00 }, + particleRadius: 0.02, + radiusSpread: 0, + radiusStart: 0.03, + radiusFinish: 0.0003, + alpha: 0, + alphaSpread: .5, + alphaStart: 0, + alphaFinish: 0.5, + textures: "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png", + emitterShouldTrail: true + }) - releaseGrab: function() { - Entities.editEntity(this.particleArc, { - isEmitting: false - }); - }, - unload: function() { - if (this.particleArc) { - Entities.deleteEntity(this.particleArc); - } - }, - preload: function(entityID) { - this.entityID = entityID; - }, - }; - return new ArcBall(); -}); \ No newline at end of file + function cleanup() { + Entities.deleteEntity(arcBall); + Entities.deleteEntity(containerBall); + Entities.deleteEntity(light); + } + + this.cleanup = cleanup; +} \ No newline at end of file From 4b4fe96b64808321652b4ca0e2a8d9ea056d5331 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Mon, 21 Dec 2015 12:47:31 -0800 Subject: [PATCH 68/94] tweaks --- examples/flowArts/arcBall/arcBallEntityScript.js | 14 +++++++------- examples/flowArts/lightSaber/lightSaber.js | 1 + 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/examples/flowArts/arcBall/arcBallEntityScript.js b/examples/flowArts/arcBall/arcBallEntityScript.js index 0906b90b53..84208bf559 100644 --- a/examples/flowArts/arcBall/arcBallEntityScript.js +++ b/examples/flowArts/arcBall/arcBallEntityScript.js @@ -65,19 +65,19 @@ }, colorFinish: color, maxParticles: 100000, - lifespan: 1, + lifespan: 2, emitRate: 1000, emitOrientation: emitOrientation, - emitSpeed: .2, - speedSpread: 0.1, + emitSpeed: .1, + speedSpread: 0.02, emitDimensions: { - x: .1, - y: .1, - z: .1 + x: .01, + y: .01, + z: .01 }, polarStart: 0, polarFinish: .0, - azimuthStart: .1, + azimuthStart: .02, azimuthFinish: .01, emitAcceleration: { x: 0, diff --git a/examples/flowArts/lightSaber/lightSaber.js b/examples/flowArts/lightSaber/lightSaber.js index 50984110bc..a42f81d196 100644 --- a/examples/flowArts/lightSaber/lightSaber.js +++ b/examples/flowArts/lightSaber/lightSaber.js @@ -23,6 +23,7 @@ LightSaber = function(spawnPosition) { modelURL: modelURL, position: spawnPosition, shapeType: 'box', + collisionsWillMove: true, script: scriptURL, dimensions: { x: 0.06, From 4110630d1f074fdf4c710f804d10d14539424a00 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Mon, 21 Dec 2015 13:15:37 -0800 Subject: [PATCH 69/94] fixed ravestick fargrab --- examples/flowArts/raveStick/raveStick.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/flowArts/raveStick/raveStick.js b/examples/flowArts/raveStick/raveStick.js index 3e4cf16136..bcbe44168c 100644 --- a/examples/flowArts/raveStick/raveStick.js +++ b/examples/flowArts/raveStick/raveStick.js @@ -30,7 +30,8 @@ RaveStick = function(spawnPosition) { modelURL: modelURL, position: spawnPosition, shapeType: 'box', - script: scriptURL, + collisionsWillMove: true, + // script: scriptURL, dimensions: { x: 0.06, y: 0.06, From cabc47e7a9914e9e2985e932de6f74310d3bb71f Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Mon, 21 Dec 2015 13:30:52 -0800 Subject: [PATCH 70/94] lightsaber immediately turns off on release --- .../flowArts/arcBall/arcBallEntityScript.js | 128 ++++++++++-------- .../lightSaber/lightSaberEntityScript.js | 4 +- 2 files changed, 76 insertions(+), 56 deletions(-) diff --git a/examples/flowArts/arcBall/arcBallEntityScript.js b/examples/flowArts/arcBall/arcBallEntityScript.js index 84208bf559..1952209c95 100644 --- a/examples/flowArts/arcBall/arcBallEntityScript.js +++ b/examples/flowArts/arcBall/arcBallEntityScript.js @@ -27,7 +27,16 @@ ArcBall.prototype = { isGrabbed: false, - startNearGrab: function() { + startDistanceGrab: function() { + this.searchForNearbyArcBalls(); + + }, + + startFarGrab: function() { + this.searchForNearbyArcBalls(); + }, + + searchForNearbyArcBalls: function() { //Search for nearby balls and create an arc to it if one is found var position = Entities.getEntityProperties(this.entityID, "position").position var entities = Entities.findEntities(position, 10); @@ -36,12 +45,13 @@ if (props.name === "Arc Ball" && JSON.stringify(_this.entityID) !== JSON.stringify(entity)) { _this.target = entity; _this.createBeam(position, props.position); + } }); - }, createBeam: function(startPosition, endPosition) { + // Creates particle arc from start position to end position var rotation = Entities.getEntityProperties(this.entityID, "rotation").rotation; var sourceToTargetVec = Vec3.subtract(endPosition, startPosition); @@ -50,61 +60,65 @@ var color = this.colorPalette[randInt(0, this.colorPalette.length)]; - var props = { - type: "ParticleEffect", - name: "Particle Arc", - parentID: this.entityID, - parentJointIndex: -1, - // position: startPosition, - isEmitting: true, - colorStart: color, - color: { - red: 200, - green: 200, - blue: 255 - }, - colorFinish: color, - maxParticles: 100000, - lifespan: 2, - emitRate: 1000, - emitOrientation: emitOrientation, - emitSpeed: .1, - speedSpread: 0.02, - emitDimensions: { - x: .01, - y: .01, - z: .01 - }, - polarStart: 0, - polarFinish: .0, - azimuthStart: .02, - azimuthFinish: .01, - emitAcceleration: { - x: 0, - y: 0, - z: 0 - }, - accelerationSpread: { - x: .00, - y: .00, - z: .00 - }, - radiusStart: 0.01, - radiusFinish: 0.005, - radiusSpread: .005, - alpha: 0.5, - alphaSpread: .1, - alphaStart: 0.5, - alphaFinish: 0.0, - textures: "https://s3.amazonaws.com/hifi-public/eric/textures/particleSprites/beamParticle.png", - emitterShouldTrail: true - } + var props = { + type: "ParticleEffect", + name: "Particle Arc", + parentID: this.entityID, + parentJointIndex: -1, + // position: startPosition, + isEmitting: true, + colorStart: color, + color: { + red: 200, + green: 200, + blue: 255 + }, + colorFinish: color, + maxParticles: 100000, + lifespan: 2, + emitRate: 1000, + emitOrientation: emitOrientation, + emitSpeed: .1, + speedSpread: 0.02, + emitDimensions: { + x: .01, + y: .01, + z: .01 + }, + polarStart: 0, + polarFinish: .0, + azimuthStart: .02, + azimuthFinish: .01, + emitAcceleration: { + x: 0, + y: 0, + z: 0 + }, + accelerationSpread: { + x: .00, + y: .00, + z: .00 + }, + radiusStart: 0.01, + radiusFinish: 0.005, + radiusSpread: .005, + alpha: 0.5, + alphaSpread: .1, + alphaStart: 0.5, + alphaFinish: 0.0, + textures: "https://s3.amazonaws.com/hifi-public/eric/textures/particleSprites/beamParticle.png", + emitterShouldTrail: true + } this.particleArc = Entities.addEntity(props); }, - updateBeam: function(startPosition) { + updateBeam: function() { + if(!this.target) { + return; + } + var startPosition = Entities.getEntityProperties(this.entityID, "position").position; + var targetPosition = Entities.getEntityProperties(this.target, "position").position; - print("TARGET position " + JSON.stringify(this.target)); var rotation = Entities.getEntityProperties(this.entityID, "rotation").rotation; var sourceToTargetVec = Vec3.subtract(targetPosition, startPosition); var emitOrientation = Quat.rotationBetween(Vec3.UNIT_Z, sourceToTargetVec); @@ -115,14 +129,18 @@ }, continueNearGrab: function() { - var startPosition = Entities.getEntityProperties(this.entityID, "position").position; - this.updateBeam(startPosition); + this.updateBeam(); + }, + + continueDistanceGrab: function() { + this.updateBeam(); }, releaseGrab: function() { Entities.editEntity(this.particleArc, { isEmitting: false }); + this.target = null; }, unload: function() { diff --git a/examples/flowArts/lightSaber/lightSaberEntityScript.js b/examples/flowArts/lightSaber/lightSaberEntityScript.js index 794e241924..cbbcd8a0bf 100644 --- a/examples/flowArts/lightSaber/lightSaberEntityScript.js +++ b/examples/flowArts/lightSaber/lightSaberEntityScript.js @@ -31,12 +31,14 @@ startNearGrab: function() { Entities.editEntity(this.beam, { - isEmitting: true + isEmitting: true, + visible: true }); }, releaseGrab: function() { Entities.editEntity(this.beam, { + visible: false, isEmitting: false }); }, From 876d8ab6b25a427ae16acbe459ea3e4f01b05348 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Mon, 21 Dec 2015 13:59:00 -0800 Subject: [PATCH 71/94] far grabbing arc balls --- examples/flowArts/arcBall/arcBall.js | 12 ++++++------ examples/flowArts/arcBall/arcBallEntityScript.js | 16 ++++++---------- .../lightSaber/lightSaberEntityScript.js | 1 - 3 files changed, 12 insertions(+), 17 deletions(-) diff --git a/examples/flowArts/arcBall/arcBall.js b/examples/flowArts/arcBall/arcBall.js index e67dffddff..5a6b1b47f3 100644 --- a/examples/flowArts/arcBall/arcBall.js +++ b/examples/flowArts/arcBall/arcBall.js @@ -15,7 +15,7 @@ Script.include("../../libraries/utils.js"); -var scriptURL = Script.resolvePath("arcBallEntityScript.js"); +var scriptURL = Script.resolvePath("arcBallEntityScript.js?v1" + Math.random()); ArcBall = function(spawnPosition) { var colorPalette = [{ @@ -50,11 +50,11 @@ ArcBall = function(spawnPosition) { userData: JSON.stringify({ grabbableKey: { spatialKey: { - relativePosition: { - x: 0, - y: 0.0, - z: -0.5 - }, + // relativePosition: { + // x: 0, + // y: -0.5, + // z: 0.0 + // }, }, // invertSolidWhileHeld: true } diff --git a/examples/flowArts/arcBall/arcBallEntityScript.js b/examples/flowArts/arcBall/arcBallEntityScript.js index 1952209c95..102ceca40c 100644 --- a/examples/flowArts/arcBall/arcBallEntityScript.js +++ b/examples/flowArts/arcBall/arcBallEntityScript.js @@ -27,12 +27,11 @@ ArcBall.prototype = { isGrabbed: false, - startDistanceGrab: function() { + startDistantGrab: function() { this.searchForNearbyArcBalls(); - }, - startFarGrab: function() { + startNearGrab: function() { this.searchForNearbyArcBalls(); }, @@ -58,7 +57,6 @@ var emitOrientation = Quat.rotationBetween(Vec3.UNIT_Z, sourceToTargetVec); emitOrientation = Quat.multiply(Quat.inverse(rotation), emitOrientation); - var color = this.colorPalette[randInt(0, this.colorPalette.length)]; var props = { type: "ParticleEffect", @@ -75,10 +73,10 @@ }, colorFinish: color, maxParticles: 100000, - lifespan: 2, + lifespan: 1, emitRate: 1000, emitOrientation: emitOrientation, - emitSpeed: .1, + emitSpeed: 1, speedSpread: 0.02, emitDimensions: { x: .01, @@ -105,7 +103,7 @@ alpha: 0.5, alphaSpread: .1, alphaStart: 0.5, - alphaFinish: 0.0, + alphaFinish: 0.5, textures: "https://s3.amazonaws.com/hifi-public/eric/textures/particleSprites/beamParticle.png", emitterShouldTrail: true } @@ -117,12 +115,10 @@ return; } var startPosition = Entities.getEntityProperties(this.entityID, "position").position; - var targetPosition = Entities.getEntityProperties(this.target, "position").position; var rotation = Entities.getEntityProperties(this.entityID, "rotation").rotation; var sourceToTargetVec = Vec3.subtract(targetPosition, startPosition); var emitOrientation = Quat.rotationBetween(Vec3.UNIT_Z, sourceToTargetVec); - // emitOrientation = Quat.multiply(emitOrientation,Quat.inverse(rotation)); Entities.editEntity(this.particleArc, { emitOrientation: emitOrientation }); @@ -132,7 +128,7 @@ this.updateBeam(); }, - continueDistanceGrab: function() { + continueDistantGrab: function() { this.updateBeam(); }, diff --git a/examples/flowArts/lightSaber/lightSaberEntityScript.js b/examples/flowArts/lightSaber/lightSaberEntityScript.js index cbbcd8a0bf..fd55916cc6 100644 --- a/examples/flowArts/lightSaber/lightSaberEntityScript.js +++ b/examples/flowArts/lightSaber/lightSaberEntityScript.js @@ -54,7 +54,6 @@ createBeam: function() { - this.props = Entities.getEntityProperties(this.entityID, ["position", "rotation"]); var forwardVec = Quat.getFront(Quat.multiply(this.props.rotation, Quat.fromPitchYawRollDegrees(-90, 0, 0))); // forwardVec = Vec3.normalize(forwardVec); From f19d1a3067ccf7e586c17ac59d8e45092db1a907 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Mon, 21 Dec 2015 15:20:36 -0800 Subject: [PATCH 72/94] cleanup --- .../flowArts/arcBall/arcBallEntityScript.js | 18 ++++++++++-------- examples/flowArts/flowArtsHutSpawner.js | 2 +- examples/flowArts/lightSaber/lightSaber.js | 2 +- .../lightSaber/lightSaberEntityScript.js | 10 +++++----- examples/flowArts/raveStick/raveStick.js | 14 +++++++------- .../raveStick/raveStickEntityScript.js | 2 -- 6 files changed, 24 insertions(+), 24 deletions(-) diff --git a/examples/flowArts/arcBall/arcBallEntityScript.js b/examples/flowArts/arcBall/arcBallEntityScript.js index 102ceca40c..987ddc7a31 100644 --- a/examples/flowArts/arcBall/arcBallEntityScript.js +++ b/examples/flowArts/arcBall/arcBallEntityScript.js @@ -23,6 +23,8 @@ green: 10, blue: 10 }]; + + this.searchRadius = 10; }; ArcBall.prototype = { @@ -38,7 +40,7 @@ searchForNearbyArcBalls: function() { //Search for nearby balls and create an arc to it if one is found var position = Entities.getEntityProperties(this.entityID, "position").position - var entities = Entities.findEntities(position, 10); + var entities = Entities.findEntities(position, this.searchRadius); entities.forEach(function(entity) { var props = Entities.getEntityProperties(entity, ["position", "name"]); if (props.name === "Arc Ball" && JSON.stringify(_this.entityID) !== JSON.stringify(entity)) { @@ -84,8 +86,8 @@ z: .01 }, polarStart: 0, - polarFinish: .0, - azimuthStart: .02, + polarFinish: 0, + azimuthStart: 0.02, azimuthFinish: .01, emitAcceleration: { x: 0, @@ -93,15 +95,15 @@ z: 0 }, accelerationSpread: { - x: .00, - y: .00, - z: .00 + x: 0, + y: 0, + z: 0 }, radiusStart: 0.01, radiusFinish: 0.005, - radiusSpread: .005, + radiusSpread: 0.005, alpha: 0.5, - alphaSpread: .1, + alphaSpread: 0.1, alphaStart: 0.5, alphaFinish: 0.5, textures: "https://s3.amazonaws.com/hifi-public/eric/textures/particleSprites/beamParticle.png", diff --git a/examples/flowArts/flowArtsHutSpawner.js b/examples/flowArts/flowArtsHutSpawner.js index 2c87a27c15..59f4e70858 100644 --- a/examples/flowArts/flowArtsHutSpawner.js +++ b/examples/flowArts/flowArtsHutSpawner.js @@ -33,7 +33,7 @@ var raveStick = new RaveStick(Vec3.sum(basePosition, {x: 1, y: 0.5, z: 1})); var lightSaber = new LightSaber(Vec3.sum(basePosition, {x: 3, y: 0.5, z: 1})); -var modelURL = "https://s3.amazonaws.com/hifi-public/eric/models/rave/RaveRoom.fbx"; +var modelURL = "https://s3-us-west-1.amazonaws.com/hifi-content/eric/models/RaveRoom.fbx"; var roomDimensions = {x: 30.58, y: 15.29, z: 30.58}; diff --git a/examples/flowArts/lightSaber/lightSaber.js b/examples/flowArts/lightSaber/lightSaber.js index a42f81d196..f582798546 100644 --- a/examples/flowArts/lightSaber/lightSaber.js +++ b/examples/flowArts/lightSaber/lightSaber.js @@ -13,7 +13,7 @@ // Script.include("../../libraries/utils.js"); -var modelURL = "https://s3.amazonaws.com/hifi-public/eric/models/rave/lightSaber.fbx"; +var modelURL = "https://s3-us-west-1.amazonaws.com/hifi-content/eric/models/lightSaber.fbx"; var scriptURL = Script.resolvePath("lightSaberEntityScript.js"); LightSaber = function(spawnPosition) { diff --git a/examples/flowArts/lightSaber/lightSaberEntityScript.js b/examples/flowArts/lightSaber/lightSaberEntityScript.js index fd55916cc6..fd53eb4c87 100644 --- a/examples/flowArts/lightSaber/lightSaberEntityScript.js +++ b/examples/flowArts/lightSaber/lightSaberEntityScript.js @@ -80,7 +80,7 @@ lifespan: 2, emitRate: 1000, emitOrientation: forwardQuat, - emitSpeed: .4, + emitSpeed: 0.7, speedSpread: 0.0, emitDimensions: { x: 0, @@ -88,9 +88,9 @@ z: 0 }, polarStart: 0, - polarFinish: .0, - azimuthStart: .1, - azimuthFinish: .01, + polarFinish: 0, + azimuthStart: 0.1, + azimuthFinish: 0.01, emitAcceleration: { x: 0, y: 0, @@ -104,7 +104,7 @@ radiusStart: 0.03, adiusFinish: 0.025, alpha: 0.7, - alphaSpread: .1, + alphaSpread: 0.1, alphaStart: 0.5, alphaFinish: 0.5, textures: "https://s3.amazonaws.com/hifi-public/eric/textures/particleSprites/beamParticle.png", diff --git a/examples/flowArts/raveStick/raveStick.js b/examples/flowArts/raveStick/raveStick.js index bcbe44168c..79c281de47 100644 --- a/examples/flowArts/raveStick/raveStick.js +++ b/examples/flowArts/raveStick/raveStick.js @@ -12,7 +12,7 @@ // Script.include("../../libraries/utils.js"); -var modelURL = "https://s3.amazonaws.com/hifi-public/eric/models/rave/raveStick.fbx"; +var modelURL = "https://s3-us-west-1.amazonaws.com/hifi-content/eric/models/raveStick.fbx"; var scriptURL = Script.resolvePath("raveStickEntityScript.js"); RaveStick = function(spawnPosition) { var colorPalette = [{ @@ -31,7 +31,7 @@ RaveStick = function(spawnPosition) { position: spawnPosition, shapeType: 'box', collisionsWillMove: true, - // script: scriptURL, + script: scriptURL, dimensions: { x: 0.06, y: 0.06, @@ -99,12 +99,12 @@ RaveStick = function(spawnPosition) { lifespan: 1, emitRate: 1000, emitOrientation: forwardQuat, - emitSpeed: .2, + emitSpeed: 0.2, speedSpread: 0.0, polarStart: 0, - polarFinish: .0, + polarFinish: 0.0, azimuthStart: .1, - azimuthFinish: .01, + azimuthFinish: 0.01, emitAcceleration: { x: 0, y: 0, @@ -118,10 +118,10 @@ RaveStick = function(spawnPosition) { radiusStart: 0.03, radiusFinish: 0.025, alpha: 0.7, - alphaSpread: .1, + alphaSpread: 0.1, alphaStart: 0.5, alphaFinish: 0.5, - textures: "https://s3.amazonaws.com/hifi-public/eric/textures/particleSprites/beamParticle.png", + textures: "https://s3-us-west-1.amazonaws.com/hifi-content/eric/textures/beamParticle.png", emitterShouldTrail: false, } var beam = Entities.addEntity(props); diff --git a/examples/flowArts/raveStick/raveStickEntityScript.js b/examples/flowArts/raveStick/raveStickEntityScript.js index 2e174c78f0..2fc51f6821 100644 --- a/examples/flowArts/raveStick/raveStickEntityScript.js +++ b/examples/flowArts/raveStick/raveStickEntityScript.js @@ -74,8 +74,6 @@ forwardVec = Vec3.normalize(forwardVec); var forwardQuat = orientationOf(forwardVec); var position = Vec3.sum(props.position, Vec3.multiply(Quat.getFront(props.rotation), 0.2)); - // position.z += 0.1; - // position.x += -0.035; var localPoint = Vec3.subtract(position, this.trailBasePosition); if (this.points.length >= 1 && Vec3.distance(localPoint, this.points[this.points.length - 1]) < MIN_POINT_DISTANCE) { //Need a minimum distance to avoid binormal NANs From 025e769a9f90d80a3c9c8eff956991d8cc18d53b Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Mon, 21 Dec 2015 18:25:30 -0800 Subject: [PATCH 73/94] average both hands, enabled both mouse clicks, and detect base station resting hand --- .../controllers/reticleHandRotationTest.js | 44 ++++++++++++++++--- 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/examples/controllers/reticleHandRotationTest.js b/examples/controllers/reticleHandRotationTest.js index 781dbf66ab..3d903217ef 100644 --- a/examples/controllers/reticleHandRotationTest.js +++ b/examples/controllers/reticleHandRotationTest.js @@ -9,8 +9,6 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -var whichHand = Controller.Standard.RightHand; -var whichTrigger = Controller.Standard.RT; var DEBUGGING = false; Math.clamp=function(a,b,c) { @@ -53,14 +51,49 @@ function moveReticleAbsolute(x, y) { var MAPPING_NAME = "com.highfidelity.testing.reticleWithHandRotation"; var mapping = Controller.newMapping(MAPPING_NAME); -mapping.from(whichTrigger).peek().constrainToInteger().to(Controller.Actions.ReticleClick); -mapping.from(whichHand).peek().to(function(pose) { +mapping.from(Controller.Standard.LT).peek().constrainToInteger().to(Controller.Actions.ReticleClick); +mapping.from(Controller.Standard.RT).peek().constrainToInteger().to(Controller.Actions.ReticleClick); +mapping.enable(); + + +var lastRotatedLeft = Vec3.UNIT_NEG_Y; +var lastRotatedRight = Vec3.UNIT_NEG_Y; + +Script.update.connect(function(deltaTime) { + + var poseRight = Controller.getPoseValue(Controller.Standard.RightHand); + var poseLeft = Controller.getPoseValue(Controller.Standard.LeftHand); // NOTE: hack for now var screenSizeX = 1920; var screenSizeY = 1080; - var rotated = Vec3.multiplyQbyV(pose.rotation, Vec3.UNIT_NEG_Y); // + var rotatedRight = Vec3.multiplyQbyV(poseRight.rotation, Vec3.UNIT_NEG_Y); + var rotatedLeft = Vec3.multiplyQbyV(poseLeft.rotation, Vec3.UNIT_NEG_Y); + + // check to see if hand is on base station + if (Vec3.equal(rotatedLeft, Vec3.UNIT_NEG_Y)) { + rotatedLeft = rotatedRight; + } + if (Vec3.equal(rotatedRight, Vec3.UNIT_NEG_Y)) { + rotatedRight = rotatedLeft; + } + + // Keep track of last rotations, to potentially detect resting (but not on + // base station hands) in the future + lastRotatedLeft = rotatedLeft; + lastRotatedRight = rotatedRight; + + // Average the two hand positions, if either hand is on base station, the + // other hand becomes the only used hand and the average is the hand in use + var rotated = Vec3.multiply(Vec3.sum(rotatedRight,rotatedLeft), 0.5); + + if (DEBUGGING) { + Vec3.print("rotatedRight:", rotatedRight); + Vec3.print("rotatedLeft:", rotatedLeft); + Vec3.print("rotated:", rotated); + } + var absolutePitch = rotated.y; // from 1 down to -1 up ... but note: if you rotate down "too far" it starts to go up again... var absoluteYaw = -rotated.x; // from -1 left to 1 right @@ -97,7 +130,6 @@ mapping.from(whichHand).peek().to(function(pose) { moveReticleAbsolute(x, y); } }); -mapping.enable(); Script.scriptEnding.connect(function(){ mapping.disable(); From 8d80c06f02eedad5c5d7a9395152a87d902d3739 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Mon, 21 Dec 2015 18:26:28 -0800 Subject: [PATCH 74/94] updated ravestick model --- examples/flowArts/raveStick/raveStick.js | 50 +------------------ .../raveStick/raveStickEntityScript.js | 5 +- 2 files changed, 5 insertions(+), 50 deletions(-) diff --git a/examples/flowArts/raveStick/raveStick.js b/examples/flowArts/raveStick/raveStick.js index 79c281de47..1e49c525f2 100644 --- a/examples/flowArts/raveStick/raveStick.js +++ b/examples/flowArts/raveStick/raveStick.js @@ -77,61 +77,13 @@ RaveStick = function(spawnPosition) { green: 200, blue: 40 }; - var props = { - type: "ParticleEffect", - position: position, - parentID: stick, - isEmitting: true, - name: "raveBeam", - colorStart: color, - colorSpread: { - red: 200, - green: 10, - blue: 10 - }, - color: { - red: 200, - green: 200, - blue: 255 - }, - colorFinish: color, - maxParticles: 100000, - lifespan: 1, - emitRate: 1000, - emitOrientation: forwardQuat, - emitSpeed: 0.2, - speedSpread: 0.0, - polarStart: 0, - polarFinish: 0.0, - azimuthStart: .1, - azimuthFinish: 0.01, - emitAcceleration: { - x: 0, - y: 0, - z: 0 - }, - accelerationSpread: { - x: .00, - y: .00, - z: .00 - }, - radiusStart: 0.03, - radiusFinish: 0.025, - alpha: 0.7, - alphaSpread: 0.1, - alphaStart: 0.5, - alphaFinish: 0.5, - textures: "https://s3-us-west-1.amazonaws.com/hifi-content/eric/textures/beamParticle.png", - emitterShouldTrail: false, - } - var beam = Entities.addEntity(props); + function cleanup() { Entities.deleteEntity(stick); Entities.deleteEntity(light); - Entities.deleteEntity(beam); } this.cleanup = cleanup; diff --git a/examples/flowArts/raveStick/raveStickEntityScript.js b/examples/flowArts/raveStick/raveStickEntityScript.js index 2fc51f6821..5a37a9b313 100644 --- a/examples/flowArts/raveStick/raveStickEntityScript.js +++ b/examples/flowArts/raveStick/raveStickEntityScript.js @@ -73,7 +73,7 @@ var forwardVec = Quat.getFront(Quat.multiply(props.rotation, Quat.fromPitchYawRollDegrees(-90, 0, 0))); forwardVec = Vec3.normalize(forwardVec); var forwardQuat = orientationOf(forwardVec); - var position = Vec3.sum(props.position, Vec3.multiply(Quat.getFront(props.rotation), 0.2)); + var position = Vec3.sum(props.position, Vec3.multiply(Quat.getFront(props.rotation), 0.04)); var localPoint = Vec3.subtract(position, this.trailBasePosition); if (this.points.length >= 1 && Vec3.distance(localPoint, this.points[this.points.length - 1]) < MIN_POINT_DISTANCE) { //Need a minimum distance to avoid binormal NANs @@ -114,6 +114,9 @@ }, releaseGrab: function() { + if(!this.trailEraseInterval) { + return; + } Script.setTimeout(function() { Script.clearInterval(_this.trailEraseInterval); _this.trailEraseInterval = null; From ba993eccd4686e5c60b8e636dd3e7b378ee232a7 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Mon, 21 Dec 2015 18:32:07 -0800 Subject: [PATCH 75/94] cleanup --- examples/flowArts/lightSaber/lightSaberEntityScript.js | 2 -- examples/flowArts/raveStick/raveStickEntityScript.js | 2 -- 2 files changed, 4 deletions(-) diff --git a/examples/flowArts/lightSaber/lightSaberEntityScript.js b/examples/flowArts/lightSaber/lightSaberEntityScript.js index fd53eb4c87..fc67015ffb 100644 --- a/examples/flowArts/lightSaber/lightSaberEntityScript.js +++ b/examples/flowArts/lightSaber/lightSaberEntityScript.js @@ -56,8 +56,6 @@ this.props = Entities.getEntityProperties(this.entityID, ["position", "rotation"]); var forwardVec = Quat.getFront(Quat.multiply(this.props.rotation, Quat.fromPitchYawRollDegrees(-90, 0, 0))); - // forwardVec = Vec3.normalize(forwardVec); - // var forwardQuat = orientationOf(forwardVec); var forwardQuat = Quat.rotationBetween(Vec3.UNIT_Z, forwardVec); var position = Vec3.sum(this.props.position, Vec3.multiply(Quat.getFront(this.props.rotation), 0.1)); position.z += 0.1; diff --git a/examples/flowArts/raveStick/raveStickEntityScript.js b/examples/flowArts/raveStick/raveStickEntityScript.js index 5a37a9b313..3f571817d2 100644 --- a/examples/flowArts/raveStick/raveStickEntityScript.js +++ b/examples/flowArts/raveStick/raveStickEntityScript.js @@ -12,7 +12,6 @@ (function() { Script.include("../../libraries/utils.js"); var _this; - // this is the "constructor" for the entity as a JS object we don't do much here var LIFETIME = 6000; var DRAWING_DEPTH = 0.8; var LINE_DIMENSIONS = 100; @@ -48,7 +47,6 @@ lifetime: LIFETIME }); - this.points = []; this.normals = []; this.strokeWidths = []; From 1031489ada2b4053f388823f923d531c0de65e25 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Mon, 21 Dec 2015 19:08:34 -0800 Subject: [PATCH 76/94] changed dates --- examples/flowArts/arcBall/arcBall.js | 2 +- examples/flowArts/flowArtsHutSpawner.js | 2 +- examples/flowArts/lightSaber/lightSaber.js | 2 +- examples/flowArts/lightTrails.js | 2 +- examples/flowArts/raveStick/raveStick.js | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/flowArts/arcBall/arcBall.js b/examples/flowArts/arcBall/arcBall.js index 5a6b1b47f3..12ef2df48a 100644 --- a/examples/flowArts/arcBall/arcBall.js +++ b/examples/flowArts/arcBall/arcBall.js @@ -3,7 +3,7 @@ // examples/arcBall // // Created by Eric Levin on 12/17/15. -// Copyright 2014 High Fidelity, Inc. +// Copyright 2015 High Fidelity, Inc. // // This script creats a particle light ball which makes particle trails as you move it. // diff --git a/examples/flowArts/flowArtsHutSpawner.js b/examples/flowArts/flowArtsHutSpawner.js index 59f4e70858..faa07c186d 100644 --- a/examples/flowArts/flowArtsHutSpawner.js +++ b/examples/flowArts/flowArtsHutSpawner.js @@ -3,7 +3,7 @@ // examples/flowArts // // Created by Eric Levin on 12/17/15. -// Copyright 2014 High Fidelity, Inc. +// Copyright 2015 High Fidelity, Inc. // // This script creates a special flow arts hut with a bunch of flow art toys people can go in and play with // diff --git a/examples/flowArts/lightSaber/lightSaber.js b/examples/flowArts/lightSaber/lightSaber.js index f582798546..3af5b65d16 100644 --- a/examples/flowArts/lightSaber/lightSaber.js +++ b/examples/flowArts/lightSaber/lightSaber.js @@ -3,7 +3,7 @@ // examples // // Created by Eric Levin on 12/17/15. -// Copyright 2014 High Fidelity, Inc. +// Copyright 2015 High Fidelity, Inc. // // This script creates a lightsaber which activates on grab // diff --git a/examples/flowArts/lightTrails.js b/examples/flowArts/lightTrails.js index 522ecce851..bb936d55c2 100644 --- a/examples/flowArts/lightTrails.js +++ b/examples/flowArts/lightTrails.js @@ -3,7 +3,7 @@ // examples // // Created by Eric Levin on 5/14/15. -// Copyright 2014 High Fidelity, Inc. +// Copyright 2015 High Fidelity, Inc. // // This script creates light trails as you move your hydra hands // diff --git a/examples/flowArts/raveStick/raveStick.js b/examples/flowArts/raveStick/raveStick.js index 1e49c525f2..c51e8b5d89 100644 --- a/examples/flowArts/raveStick/raveStick.js +++ b/examples/flowArts/raveStick/raveStick.js @@ -3,7 +3,7 @@ // examples/flowArats/raveStick // // Created by Eric Levin on 12/17/15. -// Copyright 2014 High Fidelity, Inc. +// Copyright 2015 High Fidelity, Inc. // // This script creates a rave stick which makes pretty light trails as you paint // From eff830ad4b36c658075f3ce7557baa10c4ae0d81 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 22 Dec 2015 00:54:48 -0800 Subject: [PATCH 77/94] fix crash on mac when sixense DLL can't be loaded --- plugins/hifiSixense/src/SixenseManager.cpp | 23 ++++++++++++++++++- plugins/hifiSixense/src/SixenseManager.h | 2 ++ plugins/hifiSixense/src/SixenseSupportOSX.cpp | 3 +++ 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/plugins/hifiSixense/src/SixenseManager.cpp b/plugins/hifiSixense/src/SixenseManager.cpp index b534c09055..455fdd2b5e 100644 --- a/plugins/hifiSixense/src/SixenseManager.cpp +++ b/plugins/hifiSixense/src/SixenseManager.cpp @@ -13,6 +13,9 @@ #ifdef HAVE_SIXENSE #include +#else +#define SIXENSE_FAILURE -1 +#define SIXENSE_SUCCESS 0 #endif #include @@ -43,6 +46,14 @@ static const unsigned int BUTTON_TRIGGER = 1U << 8; const glm::vec3 SixenseManager::DEFAULT_AVATAR_POSITION { -0.25f, -0.35f, -0.3f }; // in hydra frame const float SixenseManager::CONTROLLER_THRESHOLD { 0.35f }; +bool SixenseManager::_sixenseLoaded = false; + +#define BAIL_IF_NOT_LOADED \ + if (!_sixenseLoaded) { \ + return; \ + } + + const QString SixenseManager::NAME = "Sixense"; const QString SixenseManager::HYDRA_ID_STRING = "Razer Hydra"; @@ -89,11 +100,12 @@ void SixenseManager::activate() { userInputMapper->registerDevice(_inputDevice); loadSettings(); - sixenseInit(); + _sixenseLoaded = (sixenseInit() == SIXENSE_SUCCESS); #endif } void SixenseManager::deactivate() { + BAIL_IF_NOT_LOADED InputPlugin::deactivate(); #ifdef HAVE_SIXENSE @@ -114,12 +126,14 @@ void SixenseManager::deactivate() { } void SixenseManager::setSixenseFilter(bool filter) { + BAIL_IF_NOT_LOADED #ifdef HAVE_SIXENSE sixenseSetFilterEnabled(filter ? 1 : 0); #endif } void SixenseManager::pluginUpdate(float deltaTime, bool jointsCaptured) { + BAIL_IF_NOT_LOADED _inputDevice->update(deltaTime, jointsCaptured); if (_inputDevice->_requestReset) { _container->requestReset(); @@ -128,6 +142,7 @@ void SixenseManager::pluginUpdate(float deltaTime, bool jointsCaptured) { } void SixenseManager::InputDevice::update(float deltaTime, bool jointsCaptured) { + BAIL_IF_NOT_LOADED #ifdef HAVE_SIXENSE _buttonPressedMap.clear(); @@ -246,6 +261,7 @@ void SixenseManager::InputDevice::update(float deltaTime, bool jointsCaptured) { } void SixenseManager::InputDevice::setDebugDrawRaw(bool flag) { + BAIL_IF_NOT_LOADED _debugDrawRaw = flag; if (!flag) { DebugDraw::getInstance().removeMyAvatarMarker("SIXENSE_RAW_LEFT"); @@ -254,6 +270,7 @@ void SixenseManager::InputDevice::setDebugDrawRaw(bool flag) { } void SixenseManager::InputDevice::setDebugDrawCalibrated(bool flag) { + BAIL_IF_NOT_LOADED _debugDrawCalibrated = flag; if (!flag) { DebugDraw::getInstance().removeMyAvatarMarker("SIXENSE_CALIBRATED_LEFT"); @@ -281,6 +298,7 @@ static bool calibrationRequested(SixenseControllerData* controllers) { } void SixenseManager::InputDevice::updateCalibration(SixenseControllerData* controllers) { + BAIL_IF_NOT_LOADED const SixenseControllerData* dataLeft = controllers; const SixenseControllerData* dataRight = controllers + 1; @@ -365,11 +383,13 @@ void SixenseManager::InputDevice::updateCalibration(SixenseControllerData* contr #endif // HAVE_SIXENSE void SixenseManager::InputDevice::focusOutEvent() { + BAIL_IF_NOT_LOADED _axisStateMap.clear(); _buttonPressedMap.clear(); }; void SixenseManager::InputDevice::handleButtonEvent(unsigned int buttons, bool left) { + BAIL_IF_NOT_LOADED using namespace controller; if (buttons & BUTTON_0) { _buttonPressedMap.insert(left ? BACK : START); @@ -395,6 +415,7 @@ void SixenseManager::InputDevice::handleButtonEvent(unsigned int buttons, bool l } void SixenseManager::InputDevice::handlePoseEvent(float deltaTime, glm::vec3 position, glm::quat rotation, bool left) { + BAIL_IF_NOT_LOADED #ifdef HAVE_SIXENSE auto hand = left ? controller::StandardPoseChannel::LEFT_HAND : controller::StandardPoseChannel::RIGHT_HAND; diff --git a/plugins/hifiSixense/src/SixenseManager.h b/plugins/hifiSixense/src/SixenseManager.h index 0de5fe93aa..37da7358e0 100644 --- a/plugins/hifiSixense/src/SixenseManager.h +++ b/plugins/hifiSixense/src/SixenseManager.h @@ -98,6 +98,8 @@ private: static const QString NAME; static const QString HYDRA_ID_STRING; + + static bool _sixenseLoaded; }; #endif // hifi_SixenseManager_h diff --git a/plugins/hifiSixense/src/SixenseSupportOSX.cpp b/plugins/hifiSixense/src/SixenseSupportOSX.cpp index 85e0f3fe48..fce2ea023b 100644 --- a/plugins/hifiSixense/src/SixenseSupportOSX.cpp +++ b/plugins/hifiSixense/src/SixenseSupportOSX.cpp @@ -63,6 +63,9 @@ void unloadSixense() { // sixense.h wrapper for OSX dynamic linking int sixenseInit() { loadSixense(); + if (!SIXENSE || !SIXENSE->isLoaded()) { + return SIXENSE_FAILURE; + } return FORWARD(); } int sixenseExit() { From 243fba461d3af418171479546b694a442ed5d415 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 21 Dec 2015 18:46:02 -0800 Subject: [PATCH 78/94] Add CMake flag for building only server components --- CMakeLists.txt | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 13d5b2bc79..0da8dc7309 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -197,6 +197,10 @@ if (WIN32) add_paths_to_fixup_libs("${QT_DIR}/bin") endif () +if (NOT DEFINED SERVER_ONLY) + set(SERVER_ONLY 0) +endif() + # add subdirectories for all targets if (NOT ANDROID) add_subdirectory(assignment-client) @@ -205,14 +209,16 @@ if (NOT ANDROID) set_target_properties(domain-server PROPERTIES FOLDER "Apps") add_subdirectory(ice-server) set_target_properties(ice-server PROPERTIES FOLDER "Apps") - add_subdirectory(interface) - set_target_properties(interface PROPERTIES FOLDER "Apps") add_subdirectory(stack-manager) set_target_properties(stack-manager PROPERTIES FOLDER "Apps") - add_subdirectory(tests) - add_subdirectory(plugins) + if (NOT SERVER_ONLY) + add_subdirectory(interface) + set_target_properties(interface PROPERTIES FOLDER "Apps") + add_subdirectory(tests) + add_subdirectory(plugins) + endif() add_subdirectory(tools) -endif () +endif() if (ANDROID OR DESKTOP_GVR) add_subdirectory(gvr-interface) From e0e1ae43f5f5bf7f7d48117345b82513138a576d Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Tue, 22 Dec 2015 09:28:38 -0800 Subject: [PATCH 79/94] Fixing legacy oculus plugin --- plugins/oculusLegacy/CMakeLists.txt | 3 +- .../src/OculusLegacyDisplayPlugin.cpp | 137 +++++++++--------- .../src/OculusLegacyDisplayPlugin.h | 16 +- 3 files changed, 73 insertions(+), 83 deletions(-) diff --git a/plugins/oculusLegacy/CMakeLists.txt b/plugins/oculusLegacy/CMakeLists.txt index 44cee83a7d..bf9d22410d 100644 --- a/plugins/oculusLegacy/CMakeLists.txt +++ b/plugins/oculusLegacy/CMakeLists.txt @@ -6,8 +6,7 @@ # See the accompanying file LICENSE or http:#www.apache.org/licenses/LICENSE-2.0.html # -#if (NOT WIN32) -if (FALSE) +if (NOT WIN32) set(TARGET_NAME oculusLegacy) setup_hifi_plugin() diff --git a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp index 078b6586c1..cc31613beb 100644 --- a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp +++ b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp @@ -39,12 +39,6 @@ uvec2 OculusLegacyDisplayPlugin::getRecommendedRenderSize() const { return _desiredFramebufferSize; } -void OculusLegacyDisplayPlugin::preRender() { - ovrHmd_GetEyePoses(_hmd, _frameIndex, _eyeOffsets, _eyePoses, &_trackingState); - ovrHmd_BeginFrame(_hmd, _frameIndex); - WindowOpenGLDisplayPlugin::preRender(); -} - glm::mat4 OculusLegacyDisplayPlugin::getProjection(Eye eye, const glm::mat4& baseProjection) const { return _eyeProjections[eye]; } @@ -57,12 +51,18 @@ glm::mat4 OculusLegacyDisplayPlugin::getEyeToHeadTransform(Eye eye) const { return toGlm(_eyePoses[eye]); } -// Should NOT be used for rendering as this will mess up timewarp. Use the getModelview() method above for -// any use of head poses for rendering, ensuring you use the correct eye -glm::mat4 OculusLegacyDisplayPlugin::getHeadPose() const { - return toGlm(_trackingState.HeadPose.ThePose); -} +glm::mat4 OculusLegacyDisplayPlugin::getHeadPose(uint32_t frameIndex) const { + static uint32_t lastFrameSeen = 0; + if (frameIndex > lastFrameSeen) { + Lock lock(_mutex); +// auto trackingState = ovr_GetTrackingState(_session, displayTime, frameIndex > lastFrameSeen); +// ovrHmd_GetEyePoses(_hmd, frameIndex, _eyeOffsets, _eyePoses, &_trackingState); + lastFrameSeen = frameIndex; + } +// return toGlm(trackingState.HeadPose.ThePose); + return glm::mat4(); +} bool OculusLegacyDisplayPlugin::isSupported() const { if (!ovr_Initialize(nullptr)) { @@ -92,10 +92,14 @@ bool OculusLegacyDisplayPlugin::isSupported() const { } void OculusLegacyDisplayPlugin::activate() { + WindowOpenGLDisplayPlugin::activate(); + + if (!(ovr_Initialize(nullptr))) { Q_ASSERT(false); qFatal("Failed to Initialize SDK"); } + _hswDismissed = false; _hmd = ovrHmd_Create(0); if (!_hmd) { @@ -107,13 +111,13 @@ void OculusLegacyDisplayPlugin::activate() { _eyeFovs[eye] = _hmd->MaxEyeFov[eye]; ovrEyeRenderDesc erd = _eyeRenderDescs[eye] = ovrHmd_GetRenderDesc(_hmd, eye, _eyeFovs[eye]); ovrMatrix4f ovrPerspectiveProjection = - ovrMatrix4f_Projection(erd.Fov, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, ovrProjection_RightHanded); + ovrMatrix4f_Projection(erd.Fov, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, ovrProjection_RightHanded); _eyeProjections[eye] = toGlm(ovrPerspectiveProjection); - + ovrPerspectiveProjection = - ovrMatrix4f_Projection(erd.Fov, 0.001f, 10.0f, ovrProjection_RightHanded); + ovrMatrix4f_Projection(erd.Fov, 0.001f, 10.0f, ovrProjection_RightHanded); _compositeEyeProjections[eye] = toGlm(ovrPerspectiveProjection); - + _eyeOffsets[eye] = erd.HmdToEyeViewOffset; eyeSizes[eye] = toGlm(ovrHmd_GetFovTextureSize(_hmd, eye, erd.Fov, 1.0f)); }); @@ -121,60 +125,27 @@ void OculusLegacyDisplayPlugin::activate() { combined.LeftTan = std::max(_eyeFovs[Left].LeftTan, _eyeFovs[Right].LeftTan); combined.RightTan = std::max(_eyeFovs[Left].RightTan, _eyeFovs[Right].RightTan); ovrMatrix4f ovrPerspectiveProjection = - ovrMatrix4f_Projection(combined, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, ovrProjection_RightHanded); + ovrMatrix4f_Projection(combined, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, ovrProjection_RightHanded); _eyeProjections[Mono] = toGlm(ovrPerspectiveProjection); - - _desiredFramebufferSize = uvec2( - eyeSizes[0].x + eyeSizes[1].x, - std::max(eyeSizes[0].y, eyeSizes[1].y)); - - _frameIndex = 0; - + + _desiredFramebufferSize = uvec2(eyeSizes[0].x + eyeSizes[1].x, + std::max(eyeSizes[0].y, eyeSizes[1].y)); + if (!ovrHmd_ConfigureTracking(_hmd, - ovrTrackingCap_Orientation | ovrTrackingCap_Position | ovrTrackingCap_MagYawCorrection, 0)) { + ovrTrackingCap_Orientation | ovrTrackingCap_Position | ovrTrackingCap_MagYawCorrection, 0)) { qFatal("Could not attach to sensor device"); } - WindowOpenGLDisplayPlugin::activate(); - int screen = getHmdScreen(); if (screen != -1) { _container->setFullscreen(qApp->screens()[screen]); } _window->installEventFilter(this); - _window->makeCurrent(); - ovrGLConfig config; memset(&config, 0, sizeof(ovrRenderAPIConfig)); - auto& header = config.Config.Header; - header.API = ovrRenderAPI_OpenGL; - header.BackBufferSize = _hmd->Resolution; - header.Multisample = 1; - int distortionCaps = 0 - | ovrDistortionCap_TimeWarp - ; - - memset(_eyeTextures, 0, sizeof(ovrTexture) * 2); - ovr_for_each_eye([&](ovrEyeType eye) { - auto& header = _eyeTextures[eye].Header; - header.API = ovrRenderAPI_OpenGL; - header.TextureSize = { (int)_desiredFramebufferSize.x, (int)_desiredFramebufferSize.y }; - header.RenderViewport.Size = header.TextureSize; - header.RenderViewport.Size.w /= 2; - if (eye == ovrEye_Right) { - header.RenderViewport.Pos.x = header.RenderViewport.Size.w; - } - }); - - #ifndef NDEBUG - ovrBool result = - #endif - ovrHmd_ConfigureRendering(_hmd, &config.Config, distortionCaps, _eyeFovs, _eyeRenderDescs); - assert(result); } void OculusLegacyDisplayPlugin::deactivate() { _window->removeEventFilter(this); - WindowOpenGLDisplayPlugin::deactivate(); QScreen* riftScreen = nullptr; @@ -194,20 +165,46 @@ void OculusLegacyDisplayPlugin::customizeContext() { glewInit(); glGetError(); WindowOpenGLDisplayPlugin::customizeContext(); -} -void OculusLegacyDisplayPlugin::preDisplay() { - _window->makeCurrent(); -} - -void OculusLegacyDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSize) { - ++_frameIndex; + ovrGLConfig config; memset(&config, 0, sizeof(ovrRenderAPIConfig)); + auto& header = config.Config.Header; + header.API = ovrRenderAPI_OpenGL; + header.BackBufferSize = _hmd->Resolution; + header.Multisample = 1; + int distortionCaps = ovrDistortionCap_TimeWarp; + + memset(_eyeTextures, 0, sizeof(ovrTexture) * 2); ovr_for_each_eye([&](ovrEyeType eye) { - reinterpret_cast(_eyeTextures[eye]).OGL.TexId = finalTexture; + auto& header = _eyeTextures[eye].Header; + header.API = ovrRenderAPI_OpenGL; + header.TextureSize = { (int)_desiredFramebufferSize.x, (int)_desiredFramebufferSize.y }; + header.RenderViewport.Size = header.TextureSize; + header.RenderViewport.Size.w /= 2; + if (eye == ovrEye_Right) { + header.RenderViewport.Pos.x = header.RenderViewport.Size.w; + } + }); + +#ifndef NDEBUG + ovrBool result = +#endif + ovrHmd_ConfigureRendering(_hmd, &config.Config, distortionCaps, _eyeFovs, _eyeRenderDescs); + assert(result); +} + +void OculusLegacyDisplayPlugin::uncustomizeContext() { + WindowOpenGLDisplayPlugin::uncustomizeContext(); +} + +void OculusLegacyDisplayPlugin::internalPresent() { + ovrHmd_BeginFrame(_hmd, 0); + ovr_for_each_eye([&](ovrEyeType eye) { + reinterpret_cast(_eyeTextures[eye]).OGL.TexId = _currentSceneTexture; }); ovrHmd_EndFrame(_hmd, _eyePoses, _eyeTextures); } +/* // Pass input events on to the application bool OculusLegacyDisplayPlugin::eventFilter(QObject* receiver, QEvent* event) { if (!_hswDismissed && (event->type() == QEvent::KeyPress)) { @@ -221,17 +218,13 @@ bool OculusLegacyDisplayPlugin::eventFilter(QObject* receiver, QEvent* event) { } return WindowOpenGLDisplayPlugin::eventFilter(receiver, event); } - -// FIXME mirroring tot he main window is diffucult on OSX because it requires that we -// trigger a swap, which causes the client to wait for the v-sync of the main screen running -// at 60 Hz. This would introduce judder. Perhaps we can push mirroring to a separate -// thread -// FIXME If we move to the 'batch rendering on a different thread' we can possibly do this. -// however, we need to make sure it doesn't block the event handling. -void OculusLegacyDisplayPlugin::finishFrame() { - _window->doneCurrent(); -}; +*/ int OculusLegacyDisplayPlugin::getHmdScreen() const { return _hmdScreen; } + +float OculusLegacyDisplayPlugin::getTargetFrameRate() { + return TARGET_RATE_OculusLegacy; +} + diff --git a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h index d3e68585df..28fa4c6118 100644 --- a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h +++ b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h @@ -24,11 +24,9 @@ public: virtual void activate() override; virtual void deactivate() override; - virtual bool eventFilter(QObject* receiver, QEvent* event) override; +// virtual bool eventFilter(QObject* receiver, QEvent* event) override; virtual int getHmdScreen() const override; - virtual float getTargetFrameRate() override { return TARGET_RATE_OculusLegacy; } - // Stereo specific methods virtual bool isHmd() const override { return true; } virtual glm::mat4 getProjection(Eye eye, const glm::mat4& baseProjection) const override; @@ -36,20 +34,20 @@ public: virtual glm::uvec2 getRecommendedUiSize() const override { return uvec2(1920, 1080); } virtual void resetSensors() override; virtual glm::mat4 getEyeToHeadTransform(Eye eye) const override; - virtual glm::mat4 getHeadPose() const override; + virtual glm::mat4 getHeadPose(uint32_t frameIndex) const override; + + virtual float getTargetFrameRate() override; protected: virtual void customizeContext() override; - virtual void preRender() override; - virtual void preDisplay() override; - virtual void display(GLuint finalTexture, const glm::uvec2& sceneSize) override; - // Do not perform swap in finish - virtual void finishFrame() override; + virtual void uncustomizeContext() override; + virtual void internalPresent() override; private: static const QString NAME; ovrHmd _hmd; + std::mutex _statelock; ovrTrackingState _trackingState; ovrEyeRenderDesc _eyeRenderDescs[2]; ovrPosef _eyePoses[2]; From 79d8b20637334c6d2e33fe41bdf14fa7cc585a8f Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Tue, 22 Dec 2015 10:09:45 -0800 Subject: [PATCH 80/94] Simulating Oculus interaction on legacy platforms --- .../src/OculusLegacyDisplayPlugin.cpp | 56 ++++++------------- .../src/OculusLegacyDisplayPlugin.h | 10 ++-- 2 files changed, 21 insertions(+), 45 deletions(-) diff --git a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp index cc31613beb..f0f200fe1a 100644 --- a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp +++ b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp @@ -26,7 +26,7 @@ using namespace oglplus; -const QString OculusLegacyDisplayPlugin::NAME("Oculus Rift (0.5)"); +const QString OculusLegacyDisplayPlugin::NAME("Oculus Rift (0.5) (Simulated)"); const QString & OculusLegacyDisplayPlugin::getName() const { return NAME; @@ -56,12 +56,11 @@ glm::mat4 OculusLegacyDisplayPlugin::getHeadPose(uint32_t frameIndex) const { static uint32_t lastFrameSeen = 0; if (frameIndex > lastFrameSeen) { Lock lock(_mutex); -// auto trackingState = ovr_GetTrackingState(_session, displayTime, frameIndex > lastFrameSeen); -// ovrHmd_GetEyePoses(_hmd, frameIndex, _eyeOffsets, _eyePoses, &_trackingState); + _trackingState = ovrHmd_GetTrackingState(_hmd, ovr_GetTimeInSeconds()); + ovrHmd_GetEyePoses(_hmd, frameIndex, _eyeOffsets, _eyePoses, &_trackingState); lastFrameSeen = frameIndex; } -// return toGlm(trackingState.HeadPose.ThePose); - return glm::mat4(); + return toGlm(_trackingState.HeadPose.ThePose); } bool OculusLegacyDisplayPlugin::isSupported() const { @@ -93,7 +92,6 @@ bool OculusLegacyDisplayPlugin::isSupported() const { void OculusLegacyDisplayPlugin::activate() { WindowOpenGLDisplayPlugin::activate(); - if (!(ovr_Initialize(nullptr))) { Q_ASSERT(false); @@ -135,37 +133,26 @@ void OculusLegacyDisplayPlugin::activate() { ovrTrackingCap_Orientation | ovrTrackingCap_Position | ovrTrackingCap_MagYawCorrection, 0)) { qFatal("Could not attach to sensor device"); } - - int screen = getHmdScreen(); - if (screen != -1) { - _container->setFullscreen(qApp->screens()[screen]); - } - - _window->installEventFilter(this); } void OculusLegacyDisplayPlugin::deactivate() { - _window->removeEventFilter(this); WindowOpenGLDisplayPlugin::deactivate(); - - QScreen* riftScreen = nullptr; - if (_hmdScreen >= 0) { - riftScreen = qApp->screens()[_hmdScreen]; - } - _container->unsetFullscreen(riftScreen); - ovrHmd_Destroy(_hmd); _hmd = nullptr; ovr_Shutdown(); } + // DLL based display plugins MUST initialize GLEW inside the DLL code. void OculusLegacyDisplayPlugin::customizeContext() { - glewExperimental = true; - glewInit(); - glGetError(); + static std::once_flag once; + std::call_once(once, []{ + glewExperimental = true; + glewInit(); + glGetError(); + }); WindowOpenGLDisplayPlugin::customizeContext(); - +#if 0 ovrGLConfig config; memset(&config, 0, sizeof(ovrRenderAPIConfig)); auto& header = config.Config.Header; header.API = ovrRenderAPI_OpenGL; @@ -190,8 +177,11 @@ void OculusLegacyDisplayPlugin::customizeContext() { #endif ovrHmd_ConfigureRendering(_hmd, &config.Config, distortionCaps, _eyeFovs, _eyeRenderDescs); assert(result); +#endif + } +#if 0 void OculusLegacyDisplayPlugin::uncustomizeContext() { WindowOpenGLDisplayPlugin::uncustomizeContext(); } @@ -204,21 +194,7 @@ void OculusLegacyDisplayPlugin::internalPresent() { ovrHmd_EndFrame(_hmd, _eyePoses, _eyeTextures); } -/* -// Pass input events on to the application -bool OculusLegacyDisplayPlugin::eventFilter(QObject* receiver, QEvent* event) { - if (!_hswDismissed && (event->type() == QEvent::KeyPress)) { - static ovrHSWDisplayState hswState; - ovrHmd_GetHSWDisplayState(_hmd, &hswState); - if (hswState.Displayed) { - ovrHmd_DismissHSWDisplay(_hmd); - } else { - _hswDismissed = true; - } - } - return WindowOpenGLDisplayPlugin::eventFilter(receiver, event); -} -*/ +#endif int OculusLegacyDisplayPlugin::getHmdScreen() const { return _hmdScreen; diff --git a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h index 28fa4c6118..bc474634e5 100644 --- a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h +++ b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h @@ -24,7 +24,6 @@ public: virtual void activate() override; virtual void deactivate() override; -// virtual bool eventFilter(QObject* receiver, QEvent* event) override; virtual int getHmdScreen() const override; // Stereo specific methods @@ -40,17 +39,18 @@ public: protected: virtual void customizeContext() override; +#if 0 virtual void uncustomizeContext() override; virtual void internalPresent() override; - +#endif + private: static const QString NAME; ovrHmd _hmd; - std::mutex _statelock; - ovrTrackingState _trackingState; + mutable ovrTrackingState _trackingState; ovrEyeRenderDesc _eyeRenderDescs[2]; - ovrPosef _eyePoses[2]; + mutable ovrPosef _eyePoses[2]; ovrVector3f _eyeOffsets[2]; ovrFovPort _eyeFovs[2]; mat4 _eyeProjections[3]; From 653e3ecdcc0cec85d61cf22b26ac7f6d1efc7780 Mon Sep 17 00:00:00 2001 From: Eric Levin Date: Tue, 22 Dec 2015 11:02:36 -0800 Subject: [PATCH 81/94] fixed offset issue with lightsaber --- examples/flowArts/lightSaber/lightSaberEntityScript.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/examples/flowArts/lightSaber/lightSaberEntityScript.js b/examples/flowArts/lightSaber/lightSaberEntityScript.js index fc67015ffb..a86f471449 100644 --- a/examples/flowArts/lightSaber/lightSaberEntityScript.js +++ b/examples/flowArts/lightSaber/lightSaberEntityScript.js @@ -57,9 +57,8 @@ this.props = Entities.getEntityProperties(this.entityID, ["position", "rotation"]); var forwardVec = Quat.getFront(Quat.multiply(this.props.rotation, Quat.fromPitchYawRollDegrees(-90, 0, 0))); var forwardQuat = Quat.rotationBetween(Vec3.UNIT_Z, forwardVec); - var position = Vec3.sum(this.props.position, Vec3.multiply(Quat.getFront(this.props.rotation), 0.1)); - position.z += 0.1; - position.x += -0.035; + var position = this.props.position; + var color = this.colorPalette[randInt(0, this.colorPalette.length)]; var props = { type: "ParticleEffect", @@ -114,4 +113,4 @@ }; // entity scripts always need to return a newly constructed object of our type return new LightSaber(); -}); \ No newline at end of file +}); From e8803ad9aecd6c66620ecd1de1436887f870988d Mon Sep 17 00:00:00 2001 From: Eric Levin Date: Tue, 22 Dec 2015 11:35:04 -0800 Subject: [PATCH 82/94] Lightsaber looks way better now --- examples/flowArts/lightSaber/lightSaber.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/flowArts/lightSaber/lightSaber.js b/examples/flowArts/lightSaber/lightSaber.js index 3af5b65d16..c4ab49e8e6 100644 --- a/examples/flowArts/lightSaber/lightSaber.js +++ b/examples/flowArts/lightSaber/lightSaber.js @@ -38,7 +38,7 @@ LightSaber = function(spawnPosition) { y: 0, z: -0.1 }, - relativeRotation: Quat.fromPitchYawRollDegrees(90, 90, 0) + relativeRotation: Quat.fromPitchYawRollDegrees(180, 90, 0) }, invertSolidWhileHeld: true } @@ -64,4 +64,4 @@ LightSaber = function(spawnPosition) { } this.cleanup = cleanup; -} \ No newline at end of file +} From 7bc7bddc3aed5b4ab26abe1f1e8a9a2bb60112d2 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Tue, 22 Dec 2015 12:00:45 -0800 Subject: [PATCH 83/94] set defaults --- .../handControllerGrab-all-overlays.js | 1656 ----------------- .../handControllerGrab-particles.js | 1656 ----------------- .../handControllerGrab-pointlight.js | 1656 ----------------- .../handControllerGrab-spotlight.js | 1656 ----------------- examples/controllers/handControllerGrab.js | 8 +- 5 files changed, 4 insertions(+), 6628 deletions(-) delete mode 100644 examples/controllers/handControllerGrab-all-overlays.js delete mode 100644 examples/controllers/handControllerGrab-particles.js delete mode 100644 examples/controllers/handControllerGrab-pointlight.js delete mode 100644 examples/controllers/handControllerGrab-spotlight.js diff --git a/examples/controllers/handControllerGrab-all-overlays.js b/examples/controllers/handControllerGrab-all-overlays.js deleted file mode 100644 index 67e2b1f0a7..0000000000 --- a/examples/controllers/handControllerGrab-all-overlays.js +++ /dev/null @@ -1,1656 +0,0 @@ -// handControllerGrab.js -// -// Created by Eric Levin on 9/2/15 -// Additions by James B. Pollack @imgntn on 9/24/2015 -// Additions By Seth Alves on 10/20/2015 -// Copyright 2015 High Fidelity, Inc. -// -// Grabs physically moveable entities with hydra-like controllers; it works for either near or far objects. -// Also supports touch and equipping objects. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -/*global print, MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, Audio, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt, pointInExtents, vec3equal, setEntityCustomData, getEntityCustomData */ - -Script.include("../libraries/utils.js"); - -// -// add lines where the hand ray picking is happening -// -var WANT_DEBUG = false; - -// -// these tune time-averaging and "on" value for analog trigger -// - -var TRIGGER_SMOOTH_RATIO = 0.1; // 0.0 disables smoothing of trigger value -var TRIGGER_ON_VALUE = 0.4; -var TRIGGER_OFF_VALUE = 0.15; - -var BUMPER_ON_VALUE = 0.5; - -// -// distant manipulation -// - -var DISTANCE_HOLDING_RADIUS_FACTOR = 3.5; // multiplied by distance between hand and object -var DISTANCE_HOLDING_ACTION_TIMEFRAME = 0.1; // how quickly objects move to their new position -var DISTANCE_HOLDING_ROTATION_EXAGGERATION_FACTOR = 2.0; // object rotates this much more than hand did -var MOVE_WITH_HEAD = true; // experimental head-controll of distantly held objects - -var NO_INTERSECT_COLOR = { - red: 10, - green: 10, - blue: 255 -}; // line color when pick misses -var INTERSECT_COLOR = { - red: 250, - green: 10, - blue: 10 -}; // line color when pick hits -var LINE_ENTITY_DIMENSIONS = { - x: 1000, - y: 1000, - z: 1000 -}; - -var LINE_LENGTH = 500; -var PICK_MAX_DISTANCE = 500; // max length of pick-ray - -// -// near grabbing -// - -var GRAB_RADIUS = 0.03; // if the ray misses but an object is this close, it will still be selected -var NEAR_GRABBING_ACTION_TIMEFRAME = 0.05; // how quickly objects move to their new position -var NEAR_GRABBING_VELOCITY_SMOOTH_RATIO = 1.0; // adjust time-averaging of held object's velocity. 1.0 to disable. -var NEAR_PICK_MAX_DISTANCE = 0.3; // max length of pick-ray for close grabbing to be selected -var RELEASE_VELOCITY_MULTIPLIER = 1.5; // affects throwing things -var PICK_BACKOFF_DISTANCE = 0.2; // helps when hand is intersecting the grabble object -var NEAR_GRABBING_KINEMATIC = true; // force objects to be kinematic when near-grabbed - -// -// equip -// - -var EQUIP_SPRING_SHUTOFF_DISTANCE = 0.05; -var EQUIP_SPRING_TIMEFRAME = 0.4; // how quickly objects move to their new position - -// -// other constants -// - -var RIGHT_HAND = 1; -var LEFT_HAND = 0; - -var ZERO_VEC = { - x: 0, - y: 0, - z: 0 -}; - -var NULL_ACTION_ID = "{00000000-0000-0000-000000000000}"; -var MSEC_PER_SEC = 1000.0; - -// these control how long an abandoned pointer line or action will hang around -var LIFETIME = 10; -var ACTION_TTL = 15; // seconds -var ACTION_TTL_REFRESH = 5; -var PICKS_PER_SECOND_PER_HAND = 5; -var MSECS_PER_SEC = 1000.0; -var GRABBABLE_PROPERTIES = [ - "position", - "rotation", - "gravity", - "ignoreForCollisions", - "collisionsWillMove", - "locked", - "name" -]; - -var GRABBABLE_DATA_KEY = "grabbableKey"; // shared with grab.js -var GRAB_USER_DATA_KEY = "grabKey"; // shared with grab.js - -var DEFAULT_GRABBABLE_DATA = { - grabbable: true, - invertSolidWhileHeld: false -}; - -//we've created various ways of visualizing looking for and moving distant objects -var USE_ENTITY_LINES_FOR_SEARCHING = false; -var USE_OVERLAY_LINES_FOR_SEARCHING = true; -var USE_PARTICLE_BEAM_FOR_SEARCHING = false; - -var USE_ENTITY_LINES_FOR_MOVING = false; -var USE_OVERLAY_LINES_FOR_MOVING = true; -var USE_PARTICLE_BEAM_FOR_MOVING = false; - -var USE_SPOTLIGHT = false; -var USE_POINTLIGHT = false; - -// states for the state machine -var STATE_OFF = 0; -var STATE_SEARCHING = 1; -var STATE_DISTANCE_HOLDING = 2; -var STATE_CONTINUE_DISTANCE_HOLDING = 3; -var STATE_NEAR_GRABBING = 4; -var STATE_CONTINUE_NEAR_GRABBING = 5; -var STATE_NEAR_TRIGGER = 6; -var STATE_CONTINUE_NEAR_TRIGGER = 7; -var STATE_FAR_TRIGGER = 8; -var STATE_CONTINUE_FAR_TRIGGER = 9; -var STATE_RELEASE = 10; -var STATE_EQUIP_SEARCHING = 11; -var STATE_EQUIP = 12 -var STATE_CONTINUE_EQUIP_BD = 13; // equip while bumper is still held down -var STATE_CONTINUE_EQUIP = 14; -var STATE_WAITING_FOR_BUMPER_RELEASE = 15; -var STATE_EQUIP_SPRING = 16; - - - -function stateToName(state) { - switch (state) { - case STATE_OFF: - return "off"; - case STATE_SEARCHING: - return "searching"; - case STATE_DISTANCE_HOLDING: - return "distance_holding"; - case STATE_CONTINUE_DISTANCE_HOLDING: - return "continue_distance_holding"; - case STATE_NEAR_GRABBING: - return "near_grabbing"; - case STATE_CONTINUE_NEAR_GRABBING: - return "continue_near_grabbing"; - case STATE_NEAR_TRIGGER: - return "near_trigger"; - case STATE_CONTINUE_NEAR_TRIGGER: - return "continue_near_trigger"; - case STATE_FAR_TRIGGER: - return "far_trigger"; - case STATE_CONTINUE_FAR_TRIGGER: - return "continue_far_trigger"; - case STATE_RELEASE: - return "release"; - case STATE_EQUIP_SEARCHING: - return "equip_searching"; - case STATE_EQUIP: - return "equip"; - case STATE_CONTINUE_EQUIP_BD: - return "continue_equip_bd"; - case STATE_CONTINUE_EQUIP: - return "continue_equip"; - case STATE_WAITING_FOR_BUMPER_RELEASE: - return "waiting_for_bumper_release"; - case STATE_EQUIP_SPRING: - return "state_equip_spring"; - } - - return "unknown"; -} - -function getTag() { - return "grab-" + MyAvatar.sessionUUID; -} - -function entityIsGrabbedByOther(entityID) { - // by convention, a distance grab sets the tag of its action to be grab-*owner-session-id*. - var actionIDs = Entities.getActionIDs(entityID); - for (var actionIndex = 0; actionIndex < actionIDs.length; actionIndex++) { - var actionID = actionIDs[actionIndex]; - var actionArguments = Entities.getActionArguments(entityID, actionID); - var tag = actionArguments["tag"]; - if (tag == getTag()) { - // we see a grab-*uuid* shaped tag, but it's our tag, so that's okay. - continue; - } - if (tag.slice(0, 5) == "grab-") { - // we see a grab-*uuid* shaped tag and it's not ours, so someone else is grabbing it. - return true; - } - } - return false; -} - -function getSpatialOffsetPosition(hand, spatialKey) { - var position = Vec3.ZERO; - - if (hand !== RIGHT_HAND && spatialKey.leftRelativePosition) { - position = spatialKey.leftRelativePosition; - } - if (hand === RIGHT_HAND && spatialKey.rightRelativePosition) { - position = spatialKey.rightRelativePosition; - } - if (spatialKey.relativePosition) { - position = spatialKey.relativePosition; - } - - return position; -} - -var yFlip = Quat.angleAxis(180, Vec3.UNIT_Y); - -function getSpatialOffsetRotation(hand, spatialKey) { - var rotation = Quat.IDENTITY; - - if (hand !== RIGHT_HAND && spatialKey.leftRelativeRotation) { - rotation = spatialKey.leftRelativeRotation; - } - if (hand === RIGHT_HAND && spatialKey.rightRelativeRotation) { - rotation = spatialKey.rightRelativeRotation; - } - if (spatialKey.relativeRotation) { - rotation = spatialKey.relativeRotation; - } - - // Flip left hand - if (hand !== RIGHT_HAND) { - rotation = Quat.multiply(yFlip, rotation); - } - - return rotation; -} - -function MyController(hand) { - this.hand = hand; - if (this.hand === RIGHT_HAND) { - this.getHandPosition = MyAvatar.getRightPalmPosition; - this.getHandRotation = MyAvatar.getRightPalmRotation; - } else { - this.getHandPosition = MyAvatar.getLeftPalmPosition; - this.getHandRotation = MyAvatar.getLeftPalmRotation; - } - - var SPATIAL_CONTROLLERS_PER_PALM = 2; - var TIP_CONTROLLER_OFFSET = 1; - this.palm = SPATIAL_CONTROLLERS_PER_PALM * hand; - this.tip = SPATIAL_CONTROLLERS_PER_PALM * hand + TIP_CONTROLLER_OFFSET; - - this.actionID = null; // action this script created... - this.grabbedEntity = null; // on this entity. - this.state = STATE_OFF; - this.pointer = null; // entity-id of line object - this.triggerValue = 0; // rolling average of trigger value - this.rawTriggerValue = 0; - this.rawBumperValue = 0; - - //for visualizations - this.overlayLine = null; - this.particleBeam = null; - - //for lights - this.spotlight = null; - this.pointlight = null; - - this.ignoreIK = false; - this.offsetPosition = Vec3.ZERO; - this.offsetRotation = Quat.IDENTITY; - - var _this = this; - - this.update = function() { - - this.updateSmoothedTrigger(); - - switch (this.state) { - case STATE_OFF: - this.off(); - this.touchTest(); - break; - case STATE_SEARCHING: - this.search(); - break; - case STATE_EQUIP_SEARCHING: - this.search(); - break; - case STATE_DISTANCE_HOLDING: - this.distanceHolding(); - break; - case STATE_CONTINUE_DISTANCE_HOLDING: - this.continueDistanceHolding(); - break; - case STATE_NEAR_GRABBING: - case STATE_EQUIP: - this.nearGrabbing(); - break; - case STATE_WAITING_FOR_BUMPER_RELEASE: - this.waitingForBumperRelease(); - break; - case STATE_EQUIP_SPRING: - this.pullTowardEquipPosition() - break; - case STATE_CONTINUE_NEAR_GRABBING: - case STATE_CONTINUE_EQUIP_BD: - case STATE_CONTINUE_EQUIP: - this.continueNearGrabbing(); - break; - case STATE_NEAR_TRIGGER: - this.nearTrigger(); - break; - case STATE_CONTINUE_NEAR_TRIGGER: - this.continueNearTrigger(); - break; - case STATE_FAR_TRIGGER: - this.farTrigger(); - break; - case STATE_CONTINUE_FAR_TRIGGER: - this.continueFarTrigger(); - break; - case STATE_RELEASE: - this.release(); - break; - } - }; - - this.setState = function(newState) { - if (WANT_DEBUG) { - print("STATE: " + stateToName(this.state) + " --> " + stateToName(newState) + ", hand: " + this.hand); - } - this.state = newState; - }; - - this.debugLine = function(closePoint, farPoint, color) { - Entities.addEntity({ - type: "Line", - name: "Grab Debug Entity", - dimensions: LINE_ENTITY_DIMENSIONS, - visible: true, - position: closePoint, - linePoints: [ZERO_VEC, farPoint], - color: color, - lifetime: 0.1, - collisionsWillMove: false, - ignoreForCollisions: true, - userData: JSON.stringify({ - grabbableKey: { - grabbable: false - } - }) - }); - }; - - this.lineOn = function(closePoint, farPoint, color) { - // draw a line - if (this.pointer === null) { - this.pointer = Entities.addEntity({ - type: "Line", - name: "grab pointer", - dimensions: LINE_ENTITY_DIMENSIONS, - visible: true, - position: closePoint, - linePoints: [ZERO_VEC, farPoint], - color: color, - lifetime: LIFETIME, - collisionsWillMove: false, - ignoreForCollisions: true, - userData: JSON.stringify({ - grabbableKey: { - grabbable: false - } - }) - }); - } else { - var age = Entities.getEntityProperties(this.pointer, "age").age; - this.pointer = Entities.editEntity(this.pointer, { - position: closePoint, - linePoints: [ZERO_VEC, farPoint], - color: color, - lifetime: age + LIFETIME - }); - } - }; - - this.overlayLineOn = function(closePoint, farPoint, color) { - if (this.overlayLine === null) { - var lineProperties = { - lineWidth: 5, - start: closePoint, - end: farPoint, - color: color, - ignoreRayIntersection: true, // always ignore this - visible: true, - alpha: 1 - }; - - this.overlayLine = Overlays.addOverlay("line3d", lineProperties); - - } else { - var success = Overlays.editOverlay(this.overlayLine, { - lineWidth: 5, - start: closePoint, - end: farPoint, - color: color, - visible: true, - ignoreRayIntersection: true, // always ignore this - alpha: 1 - }); - } - }; - - this.handleParticleBeam = function(position, orientation, color) { - - var rotation = Quat.angleAxis(0, { - x: 1, - y: 0, - z: 0 - }); - - var finalRotation = Quat.multiply(orientation, rotation); - var lifespan = LINE_LENGTH / 10; - var speed = 5; - var spread = 2; - if (this.particleBeam === null) { - this.createParticleBeam(position, finalRotation, color, speed, spread, lifespan); - } else { - this.updateParticleBeam(position, finalRotation, color, speed, spread, lifespan); - } - }; - - this.handleDistantParticleBeam = function(handPosition, objectPosition, color) { - - var handToObject = Vec3.subtract(objectPosition, handPosition); - var finalRotation = Quat.rotationBetween(Vec3.multiply(-1, Vec3.UP), handToObject); - - var distance = Vec3.distance(handPosition, objectPosition); - var speed = 5; - var spread = 0; - - var lifespan = distance / speed; - - - if (this.particleBeam === null) { - this.createParticleBeam(objectPosition, finalRotation, color, speed, spread, lifespan); - } else { - this.updateParticleBeam(objectPosition, finalRotation, color, speed, spread, lifespan); - } - }; - - this.createParticleBeam = function(position, orientation, color, speed, spread, lifespan) { - - var particleBeamProperties = { - type: "ParticleEffect", - isEmitting: true, - position: position, - visible: false, - "name": "Particle Beam", - "color": color, - "maxParticles": 2000, - "lifespan": lifespan, - "emitRate": 50, - "emitSpeed": speed, - "speedSpread": spread, - "emitOrientation": { - "x": -1, - "y": 0, - "z": 0, - "w": 1 - }, - "emitDimensions": { - "x": 0, - "y": 0, - "z": 0 - }, - "emitRadiusStart": 0.5, - "polarStart": 0, - "polarFinish": 0, - "azimuthStart": -3.1415927410125732, - "azimuthFinish": 3.1415927410125732, - "emitAcceleration": { - x: 0, - y: 0, - z: 0 - }, - "accelerationSpread": { - "x": 0, - "y": 0, - "z": 0 - }, - "particleRadius": 0.01, - "radiusSpread": 0, - // "radiusStart": 0.01, - // "radiusFinish": 0.01, - // "colorSpread": { - // "red": 0, - // "green": 0, - // "blue": 0 - // }, - // "colorStart": color, - // "colorFinish": color, - "alpha": 1, - "alphaSpread": 0, - "alphaStart": 1, - "alphaFinish": 1, - "additiveBlending": 0, - "textures": "https://hifi-content.s3.amazonaws.com/alan/dev/textures/grabsprite-3.png" - } - - this.particleBeam = Entities.addEntity(particleBeamProperties); - }; - - this.updateParticleBeam = function(position, orientation, color, speed, spread, lifespan) { - print('lifespan::' + lifespan); - Entities.editEntity(this.particleBeam, { - rotation: orientation, - position: position, - visible: true, - color: color, - emitSpeed: speed, - speedSpread:spread, - lifespan: lifespan - - }) - - }; - - this.evalLightWorldTransform = function(modelPos, modelRot) { - - var MODEL_LIGHT_POSITION = { - x: 0, - y: -0.3, - z: 0 - }; - - var MODEL_LIGHT_ROTATION = Quat.angleAxis(-90, { - x: 1, - y: 0, - z: 0 - }); - - return { - p: Vec3.sum(modelPos, Vec3.multiplyQbyV(modelRot, MODEL_LIGHT_POSITION)), - q: Quat.multiply(modelRot, MODEL_LIGHT_ROTATION) - }; - }; - - this.handleSpotlight = function(parentID, position) { - var LIFETIME = 100; - - var modelProperties = Entities.getEntityProperties(parentID, ['position', 'rotation']); - - var lightTransform = this.evalLightWorldTransform(modelProperties.position, modelProperties.rotation); - var lightProperties = { - type: "Light", - isSpotlight: true, - dimensions: { - x: 2, - y: 2, - z: 20 - }, - parentID: parentID, - color: { - red: 255, - green: 255, - blue: 255 - }, - intensity: 2, - exponent: 0.3, - cutoff: 20, - lifetime: LIFETIME, - position: lightTransform.p, - }; - - if (this.spotlight === null) { - this.spotlight = Entities.addEntity(lightProperties); - } else { - Entities.editEntity(this.spotlight, { - //without this, this light would maintain rotation with its parent - rotation: Quat.fromPitchYawRollDegrees(-90, 0, 0), - }) - } - }; - - this.handlePointLight = function(parentID, position) { - var LIFETIME = 100; - - var modelProperties = Entities.getEntityProperties(parentID, ['position', 'rotation']); - var lightTransform = this.evalLightWorldTransform(modelProperties.position, modelProperties.rotation); - - var lightProperties = { - type: "Light", - isSpotlight: false, - dimensions: { - x: 2, - y: 2, - z: 20 - }, - parentID: parentID, - color: { - red: 255, - green: 255, - blue: 255 - }, - intensity: 2, - exponent: 0.3, - cutoff: 20, - lifetime: LIFETIME, - position: lightTransform.p, - }; - - if (this.pointlight === null) { - this.pointlight = Entities.addEntity(lightProperties); - } else { - - } - }; - - this.lineOff = function() { - if (this.pointer !== null) { - Entities.deleteEntity(this.pointer); - } - this.pointer = null; - }; - - this.overlayLineOff = function() { - if (this.overlayLine !== null) { - Overlays.deleteOverlay(this.overlayLine); - } - this.overlayLine = null; - }; - - this.particleBeamOff = function() { - if (this.particleBeam !== null) { - Entities.editEntity(this.particleBeam, { - visible: false - }) - } - } - - this.turnLightsOff = function() { - if (this.spotlight !== null) { - Entities.deleteEntity(this.spotlight); - this.spotlight = null; - } - - if (this.pointlight !== null) { - Entities.deleteEntity(this.pointlight); - this.pointlight = null; - } - }; - - - this.turnOffVisualizations = function() { - if (USE_ENTITY_LINES_FOR_SEARCHING === true || USE_ENTITY_LINES_FOR_MOVING === true) { - this.lineOff(); - } - - if (USE_OVERLAY_LINES_FOR_SEARCHING === true || USE_OVERLAY_LINES_FOR_MOVING === true) { - this.overlayLineOff(); - } - - if (USE_PARTICLE_BEAM_FOR_SEARCHING === true || USE_PARTICLE_BEAM_FOR_MOVING === true) { - this.particleBeamOff(); - } - }; - - this.triggerPress = function(value) { - _this.rawTriggerValue = value; - }; - - this.bumperPress = function(value) { - _this.rawBumperValue = value; - }; - - this.updateSmoothedTrigger = function() { - var triggerValue = this.rawTriggerValue; - // smooth out trigger value - this.triggerValue = (this.triggerValue * TRIGGER_SMOOTH_RATIO) + - (triggerValue * (1.0 - TRIGGER_SMOOTH_RATIO)); - }; - - this.triggerSmoothedSqueezed = function() { - return this.triggerValue > TRIGGER_ON_VALUE; - }; - - this.triggerSmoothedReleased = function() { - return this.triggerValue < TRIGGER_OFF_VALUE; - }; - - this.triggerSqueezed = function() { - var triggerValue = this.rawTriggerValue; - return triggerValue > TRIGGER_ON_VALUE; - }; - - this.bumperSqueezed = function() { - return _this.rawBumperValue > BUMPER_ON_VALUE; - }; - - this.bumperReleased = function() { - return _this.rawBumperValue < BUMPER_ON_VALUE; - }; - - this.off = function() { - if (this.triggerSmoothedSqueezed()) { - this.lastPickTime = 0; - this.setState(STATE_SEARCHING); - return; - } - if (this.bumperSqueezed()) { - this.lastPickTime = 0; - this.setState(STATE_EQUIP_SEARCHING); - return; - } - }; - - this.search = function() { - this.grabbedEntity = null; - - if (this.state == STATE_SEARCHING ? this.triggerSmoothedReleased() : this.bumperReleased()) { - this.setState(STATE_RELEASE); - return; - } - - // the trigger is being pressed, do a ray test - var handPosition = this.getHandPosition(); - var distantPickRay = { - origin: handPosition, - direction: Quat.getUp(this.getHandRotation()), - length: PICK_MAX_DISTANCE - }; - - // don't pick 60x per second. - var pickRays = []; - var now = Date.now(); - if (now - this.lastPickTime > MSECS_PER_SEC / PICKS_PER_SECOND_PER_HAND) { - pickRays = [distantPickRay]; - this.lastPickTime = now; - } - - for (var index = 0; index < pickRays.length; ++index) { - var pickRay = pickRays[index]; - var directionNormalized = Vec3.normalize(pickRay.direction); - var directionBacked = Vec3.multiply(directionNormalized, PICK_BACKOFF_DISTANCE); - var pickRayBacked = { - origin: Vec3.subtract(pickRay.origin, directionBacked), - direction: pickRay.direction - }; - - if (WANT_DEBUG) { - this.debugLine(pickRayBacked.origin, Vec3.multiply(pickRayBacked.direction, NEAR_PICK_MAX_DISTANCE), { - red: 0, - green: 255, - blue: 0 - }) - } - - var intersection = Entities.findRayIntersection(pickRayBacked, true); - - if (intersection.intersects) { - // the ray is intersecting something we can move. - var intersectionDistance = Vec3.distance(pickRay.origin, intersection.intersection); - - var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, intersection.entityID, DEFAULT_GRABBABLE_DATA); - - if (intersection.properties.name == "Grab Debug Entity") { - continue; - } - - if (typeof grabbableData.grabbable !== 'undefined' && !grabbableData.grabbable) { - continue; - } - if (intersectionDistance > pickRay.length) { - // too far away for this ray. - continue; - } - if (intersectionDistance <= NEAR_PICK_MAX_DISTANCE) { - // the hand is very close to the intersected object. go into close-grabbing mode. - if (grabbableData.wantsTrigger) { - this.grabbedEntity = intersection.entityID; - this.setState(STATE_NEAR_TRIGGER); - return; - } else if (!intersection.properties.locked) { - this.grabbedEntity = intersection.entityID; - if (this.state == STATE_SEARCHING) { - this.setState(STATE_NEAR_GRABBING); - } else { // equipping - if (typeof grabbableData.spatialKey !== 'undefined') { - // TODO - // if we go to STATE_EQUIP_SPRING the item will be pulled to the hand and will then switch - // to STATE_EQUIP. This needs some debugging, so just jump straight to STATE_EQUIP here. - // this.setState(STATE_EQUIP_SPRING); - this.setState(STATE_EQUIP); - } else { - this.setState(STATE_EQUIP); - } - } - return; - } - } else if (!entityIsGrabbedByOther(intersection.entityID)) { - // don't allow two people to distance grab the same object - if (intersection.properties.collisionsWillMove && !intersection.properties.locked) { - // the hand is far from the intersected object. go into distance-holding mode - this.grabbedEntity = intersection.entityID; - if (typeof grabbableData.spatialKey !== 'undefined' && this.state == STATE_EQUIP_SEARCHING) { - // if a distance pick in equip mode hits something with a spatialKey, equip it - // TODO use STATE_EQUIP_SPRING here once it works right. - // this.setState(STATE_EQUIP_SPRING); - this.setState(STATE_EQUIP); - return; - } else if (this.state == STATE_SEARCHING) { - this.setState(STATE_DISTANCE_HOLDING); - return; - } - } else if (grabbableData.wantsTrigger) { - this.grabbedEntity = intersection.entityID; - this.setState(STATE_FAR_TRIGGER); - return; - } - } - } - } - - // forward ray test failed, try sphere test. - if (WANT_DEBUG) { - Entities.addEntity({ - type: "Sphere", - name: "Grab Debug Entity", - dimensions: { - x: GRAB_RADIUS, - y: GRAB_RADIUS, - z: GRAB_RADIUS - }, - visible: true, - position: handPosition, - color: { - red: 0, - green: 255, - blue: 0 - }, - lifetime: 0.1, - collisionsWillMove: false, - ignoreForCollisions: true, - userData: JSON.stringify({ - grabbableKey: { - grabbable: false - } - }) - }); - } - - var nearbyEntities = Entities.findEntities(handPosition, GRAB_RADIUS); - var minDistance = PICK_MAX_DISTANCE; - var i, props, distance, grabbableData; - this.grabbedEntity = null; - for (i = 0; i < nearbyEntities.length; i++) { - var grabbableDataForCandidate = - getEntityCustomData(GRABBABLE_DATA_KEY, nearbyEntities[i], DEFAULT_GRABBABLE_DATA); - if (typeof grabbableDataForCandidate.grabbable !== 'undefined' && !grabbableDataForCandidate.grabbable) { - continue; - } - var propsForCandidate = Entities.getEntityProperties(nearbyEntities[i], GRABBABLE_PROPERTIES); - - if (propsForCandidate.type == 'Unknown') { - continue; - } - - if (propsForCandidate.type == 'Light') { - continue; - } - - if (propsForCandidate.type == 'ParticleEffect') { - continue; - } - - if (propsForCandidate.type == 'PolyLine') { - continue; - } - - if (propsForCandidate.type == 'Zone') { - continue; - } - - if (propsForCandidate.locked && !grabbableDataForCandidate.wantsTrigger) { - continue; - } - - if (propsForCandidate.name == "Grab Debug Entity") { - continue; - } - - if (propsForCandidate.name == "grab pointer") { - continue; - } - - distance = Vec3.distance(propsForCandidate.position, handPosition); - if (distance < minDistance) { - this.grabbedEntity = nearbyEntities[i]; - minDistance = distance; - props = propsForCandidate; - grabbableData = grabbableDataForCandidate; - } - } - if (this.grabbedEntity !== null) { - if (grabbableData.wantsTrigger) { - this.setState(STATE_NEAR_TRIGGER); - return; - } else if (!props.locked && props.collisionsWillMove) { - this.setState(this.state == STATE_SEARCHING ? STATE_NEAR_GRABBING : STATE_EQUIP) - return; - } - } - - //search line visualizations - if (USE_ENTITY_LINES_FOR_SEARCHING === true) { - this.lineOn(distantPickRay.origin, Vec3.multiply(distantPickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); - } - - if (USE_OVERLAY_LINES_FOR_SEARCHING === true) { - this.overlayLineOn(distantPickRay.origin, Vec3.sum(distantPickRay.origin, Vec3.multiply(distantPickRay.direction, LINE_LENGTH)), NO_INTERSECT_COLOR); - } - - if (USE_PARTICLE_BEAM_FOR_SEARCHING === true) { - this.handleParticleBeam(distantPickRay.origin, this.getHandRotation(), NO_INTERSECT_COLOR); - } - - }; - - this.distanceHolding = function() { - var handControllerPosition = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandPosition : MyAvatar.leftHandPosition; - var controllerHandInput = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; - var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getPoseValue(controllerHandInput).rotation); - var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); - var now = Date.now(); - - // add the action and initialize some variables - this.currentObjectPosition = grabbedProperties.position; - this.currentObjectRotation = grabbedProperties.rotation; - this.currentObjectTime = now; - this.handRelativePreviousPosition = Vec3.subtract(handControllerPosition, MyAvatar.position); - this.handPreviousRotation = handRotation; - this.currentCameraOrientation = Camera.orientation; - - // compute a constant based on the initial conditions which we use below to exagerate hand motion onto the held object - this.radiusScalar = Math.log(Vec3.distance(this.currentObjectPosition, handControllerPosition) + 1.0); - if (this.radiusScalar < 1.0) { - this.radiusScalar = 1.0; - } - - this.actionID = NULL_ACTION_ID; - this.actionID = Entities.addAction("spring", this.grabbedEntity, { - targetPosition: this.currentObjectPosition, - linearTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME, - targetRotation: this.currentObjectRotation, - angularTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME, - tag: getTag(), - ttl: ACTION_TTL - }); - if (this.actionID === NULL_ACTION_ID) { - this.actionID = null; - } - this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC); - - if (this.actionID !== null) { - this.setState(STATE_CONTINUE_DISTANCE_HOLDING); - this.activateEntity(this.grabbedEntity, grabbedProperties); - if (this.hand === RIGHT_HAND) { - Entities.callEntityMethod(this.grabbedEntity, "setRightHand"); - } else { - Entities.callEntityMethod(this.grabbedEntity, "setLeftHand"); - } - Entities.callEntityMethod(this.grabbedEntity, "setHand", [this.hand]); - Entities.callEntityMethod(this.grabbedEntity, "startDistantGrab"); - } - - this.currentAvatarPosition = MyAvatar.position; - this.currentAvatarOrientation = MyAvatar.orientation; - - this.turnOffVisualizations(); - }; - - this.continueDistanceHolding = function() { - if (this.triggerSmoothedReleased()) { - this.setState(STATE_RELEASE); - Entities.callEntityMethod(this.grabbedEntity, "releaseGrab"); - return; - } - - var handPosition = this.getHandPosition(); - var handControllerPosition = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandPosition : MyAvatar.leftHandPosition; - var controllerHandInput = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; - var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getPoseValue(controllerHandInput).rotation); - var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); - var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); - - if (this.state == STATE_CONTINUE_DISTANCE_HOLDING && this.bumperSqueezed() && - typeof grabbableData.spatialKey !== 'undefined') { - var saveGrabbedID = this.grabbedEntity; - this.release(); - this.setState(STATE_EQUIP); - this.grabbedEntity = saveGrabbedID; - return; - } - - - // the action was set up on a previous call. update the targets. - var radius = Vec3.distance(this.currentObjectPosition, handControllerPosition) * - this.radiusScalar * DISTANCE_HOLDING_RADIUS_FACTOR; - if (radius < 1.0) { - radius = 1.0; - } - - // how far did avatar move this timestep? - var currentPosition = MyAvatar.position; - var avatarDeltaPosition = Vec3.subtract(currentPosition, this.currentAvatarPosition); - this.currentAvatarPosition = currentPosition; - - // How far did the avatar turn this timestep? - // Note: The following code is too long because we need a Quat.quatBetween() function - // that returns the minimum quaternion between two quaternions. - var currentOrientation = MyAvatar.orientation; - if (Quat.dot(currentOrientation, this.currentAvatarOrientation) < 0.0) { - var negativeCurrentOrientation = { - x: -currentOrientation.x, - y: -currentOrientation.y, - z: -currentOrientation.z, - w: -currentOrientation.w - }; - var avatarDeltaOrientation = Quat.multiply(negativeCurrentOrientation, Quat.inverse(this.currentAvatarOrientation)); - } else { - var avatarDeltaOrientation = Quat.multiply(currentOrientation, Quat.inverse(this.currentAvatarOrientation)); - } - var handToAvatar = Vec3.subtract(handControllerPosition, this.currentAvatarPosition); - var objectToAvatar = Vec3.subtract(this.currentObjectPosition, this.currentAvatarPosition); - var handMovementFromTurning = Vec3.subtract(Quat.multiply(avatarDeltaOrientation, handToAvatar), handToAvatar); - var objectMovementFromTurning = Vec3.subtract(Quat.multiply(avatarDeltaOrientation, objectToAvatar), objectToAvatar); - this.currentAvatarOrientation = currentOrientation; - - // how far did hand move this timestep? - var handMoved = Vec3.subtract(handToAvatar, this.handRelativePreviousPosition); - this.handRelativePreviousPosition = handToAvatar; - - // magnify the hand movement but not the change from avatar movement & rotation - handMoved = Vec3.subtract(handMoved, handMovementFromTurning); - var superHandMoved = Vec3.multiply(handMoved, radius); - - // Move the object by the magnified amount and then by amount from avatar movement & rotation - var newObjectPosition = Vec3.sum(this.currentObjectPosition, superHandMoved); - newObjectPosition = Vec3.sum(newObjectPosition, avatarDeltaPosition); - newObjectPosition = Vec3.sum(newObjectPosition, objectMovementFromTurning); - - var deltaPosition = Vec3.subtract(newObjectPosition, this.currentObjectPosition); // meters - var now = Date.now(); - var deltaTime = (now - this.currentObjectTime) / MSEC_PER_SEC; // convert to seconds - - this.currentObjectPosition = newObjectPosition; - this.currentObjectTime = now; - - // this doubles hand rotation - var handChange = Quat.multiply(Quat.slerp(this.handPreviousRotation, - handRotation, - DISTANCE_HOLDING_ROTATION_EXAGGERATION_FACTOR), - Quat.inverse(this.handPreviousRotation)); - this.handPreviousRotation = handRotation; - this.currentObjectRotation = Quat.multiply(handChange, this.currentObjectRotation); - - Entities.callEntityMethod(this.grabbedEntity, "continueDistantGrab"); - - // mix in head motion - if (MOVE_WITH_HEAD) { - var objDistance = Vec3.length(objectToAvatar); - var before = Vec3.multiplyQbyV(this.currentCameraOrientation, { - x: 0.0, - y: 0.0, - z: objDistance - }); - var after = Vec3.multiplyQbyV(Camera.orientation, { - x: 0.0, - y: 0.0, - z: objDistance - }); - var change = Vec3.subtract(before, after); - this.currentCameraOrientation = Camera.orientation; - this.currentObjectPosition = Vec3.sum(this.currentObjectPosition, change); - } - - - //visualizations - if (USE_ENTITY_LINES_FOR_MOVING === true) { - this.lineOn(handPosition, Vec3.subtract(grabbedProperties.position, handPosition), INTERSECT_COLOR); - } - if (USE_OVERLAY_LINES_FOR_MOVING === true) { - this.overlayLineOn(handPosition, grabbedProperties.position, INTERSECT_COLOR); - } - if (USE_PARTICLE_BEAM_FOR_MOVING === true) { - this.handleDistantParticleBeam(handPosition,grabbedProperties.position, INTERSECT_COLOR) - // this.handleDistantParticleBeam(handPosition, this.currentObjectPosition, INTERSECT_COLOR) - } - if (USE_POINTLIGHT === true) { - this.handlePointLight(this.grabbedEntity); - } - if (USE_SPOTLIGHT === true) { - this.handleSpotlight(this.grabbedEntity); - } - - Entities.updateAction(this.grabbedEntity, this.actionID, { - targetPosition: this.currentObjectPosition, - linearTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME, - targetRotation: this.currentObjectRotation, - angularTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME, - ttl: ACTION_TTL - }); - - this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC); - }; - - this.nearGrabbing = function() { - var now = Date.now(); - var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); - - if (this.state == STATE_NEAR_GRABBING && this.triggerSmoothedReleased()) { - this.setState(STATE_RELEASE); - Entities.callEntityMethod(this.grabbedEntity, "releaseGrab"); - return; - } - - this.turnOffVisualizations(); - - var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); - this.activateEntity(this.grabbedEntity, grabbedProperties); - if (grabbedProperties.collisionsWillMove && NEAR_GRABBING_KINEMATIC) { - Entities.editEntity(this.grabbedEntity, { - collisionsWillMove: false - }); - } - - var handRotation = this.getHandRotation(); - var handPosition = this.getHandPosition(); - - var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); - - if (this.state != STATE_NEAR_GRABBING && grabbableData.spatialKey) { - // if an object is "equipped" and has a spatialKey, use it. - this.ignoreIK = grabbableData.spatialKey.ignoreIK ? grabbableData.spatialKey.ignoreIK : false; - this.offsetPosition = getSpatialOffsetPosition(this.hand, grabbableData.spatialKey); - this.offsetRotation = getSpatialOffsetRotation(this.hand, grabbableData.spatialKey); - } else { - this.ignoreIK = false; - - var objectRotation = grabbedProperties.rotation; - this.offsetRotation = Quat.multiply(Quat.inverse(handRotation), objectRotation); - - var currentObjectPosition = grabbedProperties.position; - var offset = Vec3.subtract(currentObjectPosition, handPosition); - this.offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, this.offsetRotation)), offset); - } - - this.actionID = NULL_ACTION_ID; - this.actionID = Entities.addAction("hold", this.grabbedEntity, { - hand: this.hand === RIGHT_HAND ? "right" : "left", - timeScale: NEAR_GRABBING_ACTION_TIMEFRAME, - relativePosition: this.offsetPosition, - relativeRotation: this.offsetRotation, - ttl: ACTION_TTL, - kinematic: NEAR_GRABBING_KINEMATIC, - kinematicSetVelocity: true, - ignoreIK: this.ignoreIK - }); - if (this.actionID === NULL_ACTION_ID) { - this.actionID = null; - } else { - this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC); - if (this.state == STATE_NEAR_GRABBING) { - this.setState(STATE_CONTINUE_NEAR_GRABBING); - } else { - // equipping - Entities.callEntityMethod(this.grabbedEntity, "startEquip", [JSON.stringify(this.hand)]); - this.startHandGrasp(); - - this.setState(STATE_CONTINUE_EQUIP_BD); - } - - if (this.hand === RIGHT_HAND) { - Entities.callEntityMethod(this.grabbedEntity, "setRightHand"); - } else { - Entities.callEntityMethod(this.grabbedEntity, "setLeftHand"); - } - - Entities.callEntityMethod(this.grabbedEntity, "setHand", [this.hand]); - - Entities.callEntityMethod(this.grabbedEntity, "startNearGrab"); - - } - - this.currentHandControllerTipPosition = - (this.hand === RIGHT_HAND) ? MyAvatar.rightHandTipPosition : MyAvatar.leftHandTipPosition; - - this.currentObjectTime = Date.now(); - }; - - this.continueNearGrabbing = function() { - if (this.state == STATE_CONTINUE_NEAR_GRABBING && this.triggerSmoothedReleased()) { - this.setState(STATE_RELEASE); - Entities.callEntityMethod(this.grabbedEntity, "releaseGrab"); - return; - } - if (this.state == STATE_CONTINUE_EQUIP_BD && this.bumperReleased()) { - this.setState(STATE_CONTINUE_EQUIP); - return; - } - if (this.state == STATE_CONTINUE_EQUIP && this.bumperSqueezed()) { - this.setState(STATE_WAITING_FOR_BUMPER_RELEASE); - return; - } - if (this.state == STATE_CONTINUE_NEAR_GRABBING && this.bumperSqueezed()) { - this.setState(STATE_CONTINUE_EQUIP_BD); - Entities.callEntityMethod(this.grabbedEntity, "startEquip", [JSON.stringify(this.hand)]); - return; - } - - // Keep track of the fingertip velocity to impart when we release the object. - // Note that the idea of using a constant 'tip' velocity regardless of the - // object's actual held offset is an idea intended to make it easier to throw things: - // Because we might catch something or transfer it between hands without a good idea - // of it's actual offset, let's try imparting a velocity which is at a fixed radius - // from the palm. - - var handControllerPosition = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandPosition : MyAvatar.leftHandPosition; - var now = Date.now(); - - var deltaPosition = Vec3.subtract(handControllerPosition, this.currentHandControllerTipPosition); // meters - var deltaTime = (now - this.currentObjectTime) / MSEC_PER_SEC; // convert to seconds - - this.currentHandControllerTipPosition = handControllerPosition; - this.currentObjectTime = now; - Entities.callEntityMethod(this.grabbedEntity, "continueNearGrab"); - - if (this.state === STATE_CONTINUE_EQUIP_BD) { - Entities.callEntityMethod(this.grabbedEntity, "continueEquip"); - } - - if (this.actionTimeout - now < ACTION_TTL_REFRESH * MSEC_PER_SEC) { - // if less than a 5 seconds left, refresh the actions ttl - Entities.updateAction(this.grabbedEntity, this.actionID, { - hand: this.hand === RIGHT_HAND ? "right" : "left", - timeScale: NEAR_GRABBING_ACTION_TIMEFRAME, - relativePosition: this.offsetPosition, - relativeRotation: this.offsetRotation, - ttl: ACTION_TTL, - kinematic: NEAR_GRABBING_KINEMATIC, - kinematicSetVelocity: true, - ignoreIK: this.ignoreIK - }); - this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC); - } - }; - - this.waitingForBumperRelease = function() { - if (this.bumperReleased()) { - this.setState(STATE_RELEASE); - Entities.callEntityMethod(this.grabbedEntity, "releaseGrab"); - Entities.callEntityMethod(this.grabbedEntity, "unequip"); - this.endHandGrasp(); - - } - }; - - this.pullTowardEquipPosition = function() { - - this.turnOffVisualizations(); - - var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); - var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); - - // use a spring to pull the object to where it will be when equipped - var relativeRotation = getSpatialOffsetRotation(this.hand, grabbableData.spatialKey); - var relativePosition = getSpatialOffsetPosition(this.hand, grabbableData.spatialKey); - var ignoreIK = grabbableData.spatialKey.ignoreIK ? grabbableData.spatialKey.ignoreIK : false; - var handRotation = this.getHandRotation(); - var handPosition = this.getHandPosition(); - var targetRotation = Quat.multiply(handRotation, relativeRotation); - var offset = Vec3.multiplyQbyV(targetRotation, relativePosition); - var targetPosition = Vec3.sum(handPosition, offset); - - if (typeof this.equipSpringID === 'undefined' || - this.equipSpringID === null || - this.equipSpringID === NULL_ACTION_ID) { - this.equipSpringID = Entities.addAction("spring", this.grabbedEntity, { - targetPosition: targetPosition, - linearTimeScale: EQUIP_SPRING_TIMEFRAME, - targetRotation: targetRotation, - angularTimeScale: EQUIP_SPRING_TIMEFRAME, - ttl: ACTION_TTL, - ignoreIK: ignoreIK - }); - if (this.equipSpringID === NULL_ACTION_ID) { - this.equipSpringID = null; - this.setState(STATE_OFF); - return; - } - } else { - Entities.updateAction(this.grabbedEntity, this.equipSpringID, { - targetPosition: targetPosition, - linearTimeScale: EQUIP_SPRING_TIMEFRAME, - targetRotation: targetRotation, - angularTimeScale: EQUIP_SPRING_TIMEFRAME, - ttl: ACTION_TTL, - ignoreIK: ignoreIK - }); - } - - if (Vec3.distance(grabbedProperties.position, targetPosition) < EQUIP_SPRING_SHUTOFF_DISTANCE) { - Entities.deleteAction(this.grabbedEntity, this.equipSpringID); - this.equipSpringID = null; - this.setState(STATE_EQUIP); - } - }; - - this.nearTrigger = function() { - if (this.triggerSmoothedReleased()) { - this.setState(STATE_RELEASE); - Entities.callEntityMethod(this.grabbedEntity, "stopNearTrigger"); - return; - } - if (this.hand === RIGHT_HAND) { - Entities.callEntityMethod(this.grabbedEntity, "setRightHand"); - } else { - Entities.callEntityMethod(this.grabbedEntity, "setLeftHand"); - } - - Entities.callEntityMethod(this.grabbedEntity, "setHand", [this.hand]); - - Entities.callEntityMethod(this.grabbedEntity, "startNearTrigger"); - this.setState(STATE_CONTINUE_NEAR_TRIGGER); - }; - - this.farTrigger = function() { - if (this.triggerSmoothedReleased()) { - this.setState(STATE_RELEASE); - Entities.callEntityMethod(this.grabbedEntity, "stopFarTrigger"); - return; - } - - if (this.hand === RIGHT_HAND) { - Entities.callEntityMethod(this.grabbedEntity, "setRightHand"); - } else { - Entities.callEntityMethod(this.grabbedEntity, "setLeftHand"); - } - Entities.callEntityMethod(this.grabbedEntity, "setHand", [this.hand]); - Entities.callEntityMethod(this.grabbedEntity, "startFarTrigger"); - this.setState(STATE_CONTINUE_FAR_TRIGGER); - }; - - this.continueNearTrigger = function() { - if (this.triggerSmoothedReleased()) { - this.setState(STATE_RELEASE); - Entities.callEntityMethod(this.grabbedEntity, "stopNearTrigger"); - return; - } - - Entities.callEntityMethod(this.grabbedEntity, "continueNearTrigger"); - }; - - this.continueFarTrigger = function() { - if (this.triggerSmoothedReleased()) { - this.setState(STATE_RELEASE); - Entities.callEntityMethod(this.grabbedEntity, "stopNearTrigger"); - return; - } - - var handPosition = this.getHandPosition(); - var pickRay = { - origin: handPosition, - direction: Quat.getUp(this.getHandRotation()) - }; - - var now = Date.now(); - if (now - this.lastPickTime > MSECS_PER_SEC / PICKS_PER_SECOND_PER_HAND) { - var intersection = Entities.findRayIntersection(pickRay, true); - this.lastPickTime = now; - if (intersection.entityID != this.grabbedEntity) { - this.setState(STATE_RELEASE); - Entities.callEntityMethod(this.grabbedEntity, "stopFarTrigger"); - return; - } - } - - if (USE_ENTITY_LINES_FOR_MOVING === true) { - this.lineOn(pickRay.origin, Vec3.multiply(pickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); - } - - Entities.callEntityMethod(this.grabbedEntity, "continueFarTrigger"); - }; - - _this.allTouchedIDs = {}; - - this.touchTest = function() { - var maxDistance = 0.05; - var leftHandPosition = MyAvatar.getLeftPalmPosition(); - var rightHandPosition = MyAvatar.getRightPalmPosition(); - var leftEntities = Entities.findEntities(leftHandPosition, maxDistance); - var rightEntities = Entities.findEntities(rightHandPosition, maxDistance); - var ids = []; - - if (leftEntities.length !== 0) { - leftEntities.forEach(function(entity) { - ids.push(entity); - }); - - } - - if (rightEntities.length !== 0) { - rightEntities.forEach(function(entity) { - ids.push(entity); - }); - } - - ids.forEach(function(id) { - - var props = Entities.getEntityProperties(id, ["boundingBox", "name"]); - if (props.name === 'pointer') { - return; - } else { - var entityMinPoint = props.boundingBox.brn; - var entityMaxPoint = props.boundingBox.tfl; - var leftIsTouching = pointInExtents(leftHandPosition, entityMinPoint, entityMaxPoint); - var rightIsTouching = pointInExtents(rightHandPosition, entityMinPoint, entityMaxPoint); - - if ((leftIsTouching || rightIsTouching) && _this.allTouchedIDs[id] === undefined) { - // we haven't been touched before, but either right or left is touching us now - _this.allTouchedIDs[id] = true; - _this.startTouch(id); - } else if ((leftIsTouching || rightIsTouching) && _this.allTouchedIDs[id]) { - // we have been touched before and are still being touched - // continue touch - _this.continueTouch(id); - } else if (_this.allTouchedIDs[id]) { - delete _this.allTouchedIDs[id]; - _this.stopTouch(id); - - } else { - //we are in another state - return; - } - } - - }); - - }; - - this.startTouch = function(entityID) { - Entities.callEntityMethod(entityID, "startTouch"); - }; - - this.continueTouch = function(entityID) { - Entities.callEntityMethod(entityID, "continueTouch"); - }; - - this.stopTouch = function(entityID) { - Entities.callEntityMethod(entityID, "stopTouch"); - }; - - this.release = function() { - - this.turnLightsOff(); - this.turnOffVisualizations(); - - if (this.grabbedEntity !== null) { - if (this.actionID !== null) { - Entities.deleteAction(this.grabbedEntity, this.actionID); - } - } - - this.deactivateEntity(this.grabbedEntity); - - this.grabbedEntity = null; - this.actionID = null; - this.setState(STATE_OFF); - }; - - this.cleanup = function() { - this.release(); - this.endHandGrasp(); - Entities.deleteEntity(this.particleBeam); - Entities.deleteEntity(this.spotLight); - Entities.deleteEntity(this.pointLight); - }; - - this.activateEntity = function(entityID, grabbedProperties) { - var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, entityID, DEFAULT_GRABBABLE_DATA); - var invertSolidWhileHeld = grabbableData["invertSolidWhileHeld"]; - var data = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, {}); - data["activated"] = true; - data["avatarId"] = MyAvatar.sessionUUID; - data["refCount"] = data["refCount"] ? data["refCount"] + 1 : 1; - // zero gravity and set ignoreForCollisions in a way that lets us put them back, after all grabs are done - if (data["refCount"] == 1) { - data["gravity"] = grabbedProperties.gravity; - data["ignoreForCollisions"] = grabbedProperties.ignoreForCollisions; - data["collisionsWillMove"] = grabbedProperties.collisionsWillMove; - var whileHeldProperties = { - gravity: { - x: 0, - y: 0, - z: 0 - } - }; - if (invertSolidWhileHeld) { - whileHeldProperties["ignoreForCollisions"] = !grabbedProperties.ignoreForCollisions; - } - Entities.editEntity(entityID, whileHeldProperties); - } - - setEntityCustomData(GRAB_USER_DATA_KEY, entityID, data); - return data; - }; - - this.deactivateEntity = function(entityID) { - var data = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, {}); - if (data && data["refCount"]) { - data["refCount"] = data["refCount"] - 1; - if (data["refCount"] < 1) { - Entities.editEntity(entityID, { - gravity: data["gravity"], - ignoreForCollisions: data["ignoreForCollisions"], - collisionsWillMove: data["collisionsWillMove"] - }); - data = null; - } - } else { - data = null; - } - setEntityCustomData(GRAB_USER_DATA_KEY, entityID, data); - }; - - - //this is our handler, where we do the actual work of changing animation settings - this.graspHand = function(animationProperties) { - var result = {}; - //full alpha on overlay for this hand - //set grab to true - //set idle to false - //full alpha on the blend btw open and grab - if (_this.hand === RIGHT_HAND) { - result['rightHandOverlayAlpha'] = 1.0; - result['isRightHandGrab'] = true; - result['isRightHandIdle'] = false; - result['rightHandGrabBlend'] = 1.0; - } else if (_this.hand === LEFT_HAND) { - result['leftHandOverlayAlpha'] = 1.0; - result['isLeftHandGrab'] = true; - result['isLeftHandIdle'] = false; - result['leftHandGrabBlend'] = 1.0; - } - //return an object with our updated settings - return result; - }; - - this.graspHandler = null - - this.startHandGrasp = function() { - if (this.hand === RIGHT_HAND) { - this.graspHandler = MyAvatar.addAnimationStateHandler(this.graspHand, ['isRightHandGrab']); - } else if (this.hand === LEFT_HAND) { - this.graspHandler = MyAvatar.addAnimationStateHandler(this.graspHand, ['isLeftHandGrab']); - } - }; - - this.endHandGrasp = function() { - // Tell the animation system we don't need any more callbacks. - MyAvatar.removeAnimationStateHandler(this.graspHandler); - }; - -}; - -var rightController = new MyController(RIGHT_HAND); -var leftController = new MyController(LEFT_HAND); - -//preload the particle beams so that they are full length when you start searching -if (USE_PARTICLE_BEAM_FOR_SEARCHING === true || USE_PARTICLE_BEAM_FOR_MOVING === true) { - rightController.createParticleBeam(); - leftController.createParticleBeam(); -} - -var MAPPING_NAME = "com.highfidelity.handControllerGrab"; - -var mapping = Controller.newMapping(MAPPING_NAME); -mapping.from([Controller.Standard.RT]).peek().to(rightController.triggerPress); -mapping.from([Controller.Standard.LT]).peek().to(leftController.triggerPress); - -mapping.from([Controller.Standard.RB]).peek().to(rightController.bumperPress); -mapping.from([Controller.Standard.LB]).peek().to(leftController.bumperPress); - -Controller.enableMapping(MAPPING_NAME); - -//the section below allows the grab script to listen for messages that disable either one or both hands. useful for two handed items -var handToDisable = 'none'; - -function update() { - if (handToDisable !== LEFT_HAND && handToDisable !== 'both') { - leftController.update(); - } - if (handToDisable !== RIGHT_HAND && handToDisable !== 'both') { - rightController.update(); - } -} - -Messages.subscribe('Hifi-Hand-Disabler'); - -handleHandDisablerMessages = function(channel, message, sender) { - - if (sender === MyAvatar.sessionUUID) { - if (message === 'left') { - handToDisable = LEFT_HAND; - } - if (message === 'right') { - handToDisable = RIGHT_HAND; - } - if (message === 'both') { - handToDisable = 'both'; - } - if (message === 'none') { - handToDisable = 'none'; - } - } - -} - -Messages.messageReceived.connect(handleHandDisablerMessages); - -function cleanup() { - rightController.cleanup(); - leftController.cleanup(); - Controller.disableMapping(MAPPING_NAME); -} - -Script.scriptEnding.connect(cleanup); -Script.update.connect(update); \ No newline at end of file diff --git a/examples/controllers/handControllerGrab-particles.js b/examples/controllers/handControllerGrab-particles.js deleted file mode 100644 index bfe51927d0..0000000000 --- a/examples/controllers/handControllerGrab-particles.js +++ /dev/null @@ -1,1656 +0,0 @@ -// handControllerGrab.js -// -// Created by Eric Levin on 9/2/15 -// Additions by James B. Pollack @imgntn on 9/24/2015 -// Additions By Seth Alves on 10/20/2015 -// Copyright 2015 High Fidelity, Inc. -// -// Grabs physically moveable entities with hydra-like controllers; it works for either near or far objects. -// Also supports touch and equipping objects. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -/*global print, MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, Audio, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt, pointInExtents, vec3equal, setEntityCustomData, getEntityCustomData */ - -Script.include("../libraries/utils.js"); - -// -// add lines where the hand ray picking is happening -// -var WANT_DEBUG = false; - -// -// these tune time-averaging and "on" value for analog trigger -// - -var TRIGGER_SMOOTH_RATIO = 0.1; // 0.0 disables smoothing of trigger value -var TRIGGER_ON_VALUE = 0.4; -var TRIGGER_OFF_VALUE = 0.15; - -var BUMPER_ON_VALUE = 0.5; - -// -// distant manipulation -// - -var DISTANCE_HOLDING_RADIUS_FACTOR = 3.5; // multiplied by distance between hand and object -var DISTANCE_HOLDING_ACTION_TIMEFRAME = 0.1; // how quickly objects move to their new position -var DISTANCE_HOLDING_ROTATION_EXAGGERATION_FACTOR = 2.0; // object rotates this much more than hand did -var MOVE_WITH_HEAD = true; // experimental head-controll of distantly held objects - -var NO_INTERSECT_COLOR = { - red: 10, - green: 10, - blue: 255 -}; // line color when pick misses -var INTERSECT_COLOR = { - red: 250, - green: 10, - blue: 10 -}; // line color when pick hits -var LINE_ENTITY_DIMENSIONS = { - x: 1000, - y: 1000, - z: 1000 -}; - -var LINE_LENGTH = 500; -var PICK_MAX_DISTANCE = 500; // max length of pick-ray - -// -// near grabbing -// - -var GRAB_RADIUS = 0.03; // if the ray misses but an object is this close, it will still be selected -var NEAR_GRABBING_ACTION_TIMEFRAME = 0.05; // how quickly objects move to their new position -var NEAR_GRABBING_VELOCITY_SMOOTH_RATIO = 1.0; // adjust time-averaging of held object's velocity. 1.0 to disable. -var NEAR_PICK_MAX_DISTANCE = 0.3; // max length of pick-ray for close grabbing to be selected -var RELEASE_VELOCITY_MULTIPLIER = 1.5; // affects throwing things -var PICK_BACKOFF_DISTANCE = 0.2; // helps when hand is intersecting the grabble object -var NEAR_GRABBING_KINEMATIC = true; // force objects to be kinematic when near-grabbed - -// -// equip -// - -var EQUIP_SPRING_SHUTOFF_DISTANCE = 0.05; -var EQUIP_SPRING_TIMEFRAME = 0.4; // how quickly objects move to their new position - -// -// other constants -// - -var RIGHT_HAND = 1; -var LEFT_HAND = 0; - -var ZERO_VEC = { - x: 0, - y: 0, - z: 0 -}; - -var NULL_ACTION_ID = "{00000000-0000-0000-000000000000}"; -var MSEC_PER_SEC = 1000.0; - -// these control how long an abandoned pointer line or action will hang around -var LIFETIME = 10; -var ACTION_TTL = 15; // seconds -var ACTION_TTL_REFRESH = 5; -var PICKS_PER_SECOND_PER_HAND = 5; -var MSECS_PER_SEC = 1000.0; -var GRABBABLE_PROPERTIES = [ - "position", - "rotation", - "gravity", - "ignoreForCollisions", - "collisionsWillMove", - "locked", - "name" -]; - -var GRABBABLE_DATA_KEY = "grabbableKey"; // shared with grab.js -var GRAB_USER_DATA_KEY = "grabKey"; // shared with grab.js - -var DEFAULT_GRABBABLE_DATA = { - grabbable: true, - invertSolidWhileHeld: false -}; - -//we've created various ways of visualizing looking for and moving distant objects -var USE_ENTITY_LINES_FOR_SEARCHING = false; -var USE_OVERLAY_LINES_FOR_SEARCHING = false; -var USE_PARTICLE_BEAM_FOR_SEARCHING = true; - -var USE_ENTITY_LINES_FOR_MOVING = false; -var USE_OVERLAY_LINES_FOR_MOVING = false; -var USE_PARTICLE_BEAM_FOR_MOVING = true; - -var USE_SPOTLIGHT = false; -var USE_POINTLIGHT = false; - -// states for the state machine -var STATE_OFF = 0; -var STATE_SEARCHING = 1; -var STATE_DISTANCE_HOLDING = 2; -var STATE_CONTINUE_DISTANCE_HOLDING = 3; -var STATE_NEAR_GRABBING = 4; -var STATE_CONTINUE_NEAR_GRABBING = 5; -var STATE_NEAR_TRIGGER = 6; -var STATE_CONTINUE_NEAR_TRIGGER = 7; -var STATE_FAR_TRIGGER = 8; -var STATE_CONTINUE_FAR_TRIGGER = 9; -var STATE_RELEASE = 10; -var STATE_EQUIP_SEARCHING = 11; -var STATE_EQUIP = 12 -var STATE_CONTINUE_EQUIP_BD = 13; // equip while bumper is still held down -var STATE_CONTINUE_EQUIP = 14; -var STATE_WAITING_FOR_BUMPER_RELEASE = 15; -var STATE_EQUIP_SPRING = 16; - - - -function stateToName(state) { - switch (state) { - case STATE_OFF: - return "off"; - case STATE_SEARCHING: - return "searching"; - case STATE_DISTANCE_HOLDING: - return "distance_holding"; - case STATE_CONTINUE_DISTANCE_HOLDING: - return "continue_distance_holding"; - case STATE_NEAR_GRABBING: - return "near_grabbing"; - case STATE_CONTINUE_NEAR_GRABBING: - return "continue_near_grabbing"; - case STATE_NEAR_TRIGGER: - return "near_trigger"; - case STATE_CONTINUE_NEAR_TRIGGER: - return "continue_near_trigger"; - case STATE_FAR_TRIGGER: - return "far_trigger"; - case STATE_CONTINUE_FAR_TRIGGER: - return "continue_far_trigger"; - case STATE_RELEASE: - return "release"; - case STATE_EQUIP_SEARCHING: - return "equip_searching"; - case STATE_EQUIP: - return "equip"; - case STATE_CONTINUE_EQUIP_BD: - return "continue_equip_bd"; - case STATE_CONTINUE_EQUIP: - return "continue_equip"; - case STATE_WAITING_FOR_BUMPER_RELEASE: - return "waiting_for_bumper_release"; - case STATE_EQUIP_SPRING: - return "state_equip_spring"; - } - - return "unknown"; -} - -function getTag() { - return "grab-" + MyAvatar.sessionUUID; -} - -function entityIsGrabbedByOther(entityID) { - // by convention, a distance grab sets the tag of its action to be grab-*owner-session-id*. - var actionIDs = Entities.getActionIDs(entityID); - for (var actionIndex = 0; actionIndex < actionIDs.length; actionIndex++) { - var actionID = actionIDs[actionIndex]; - var actionArguments = Entities.getActionArguments(entityID, actionID); - var tag = actionArguments["tag"]; - if (tag == getTag()) { - // we see a grab-*uuid* shaped tag, but it's our tag, so that's okay. - continue; - } - if (tag.slice(0, 5) == "grab-") { - // we see a grab-*uuid* shaped tag and it's not ours, so someone else is grabbing it. - return true; - } - } - return false; -} - -function getSpatialOffsetPosition(hand, spatialKey) { - var position = Vec3.ZERO; - - if (hand !== RIGHT_HAND && spatialKey.leftRelativePosition) { - position = spatialKey.leftRelativePosition; - } - if (hand === RIGHT_HAND && spatialKey.rightRelativePosition) { - position = spatialKey.rightRelativePosition; - } - if (spatialKey.relativePosition) { - position = spatialKey.relativePosition; - } - - return position; -} - -var yFlip = Quat.angleAxis(180, Vec3.UNIT_Y); - -function getSpatialOffsetRotation(hand, spatialKey) { - var rotation = Quat.IDENTITY; - - if (hand !== RIGHT_HAND && spatialKey.leftRelativeRotation) { - rotation = spatialKey.leftRelativeRotation; - } - if (hand === RIGHT_HAND && spatialKey.rightRelativeRotation) { - rotation = spatialKey.rightRelativeRotation; - } - if (spatialKey.relativeRotation) { - rotation = spatialKey.relativeRotation; - } - - // Flip left hand - if (hand !== RIGHT_HAND) { - rotation = Quat.multiply(yFlip, rotation); - } - - return rotation; -} - -function MyController(hand) { - this.hand = hand; - if (this.hand === RIGHT_HAND) { - this.getHandPosition = MyAvatar.getRightPalmPosition; - this.getHandRotation = MyAvatar.getRightPalmRotation; - } else { - this.getHandPosition = MyAvatar.getLeftPalmPosition; - this.getHandRotation = MyAvatar.getLeftPalmRotation; - } - - var SPATIAL_CONTROLLERS_PER_PALM = 2; - var TIP_CONTROLLER_OFFSET = 1; - this.palm = SPATIAL_CONTROLLERS_PER_PALM * hand; - this.tip = SPATIAL_CONTROLLERS_PER_PALM * hand + TIP_CONTROLLER_OFFSET; - - this.actionID = null; // action this script created... - this.grabbedEntity = null; // on this entity. - this.state = STATE_OFF; - this.pointer = null; // entity-id of line object - this.triggerValue = 0; // rolling average of trigger value - this.rawTriggerValue = 0; - this.rawBumperValue = 0; - - //for visualizations - this.overlayLine = null; - this.particleBeam = null; - - //for lights - this.spotlight = null; - this.pointlight = null; - - this.ignoreIK = false; - this.offsetPosition = Vec3.ZERO; - this.offsetRotation = Quat.IDENTITY; - - var _this = this; - - this.update = function() { - - this.updateSmoothedTrigger(); - - switch (this.state) { - case STATE_OFF: - this.off(); - this.touchTest(); - break; - case STATE_SEARCHING: - this.search(); - break; - case STATE_EQUIP_SEARCHING: - this.search(); - break; - case STATE_DISTANCE_HOLDING: - this.distanceHolding(); - break; - case STATE_CONTINUE_DISTANCE_HOLDING: - this.continueDistanceHolding(); - break; - case STATE_NEAR_GRABBING: - case STATE_EQUIP: - this.nearGrabbing(); - break; - case STATE_WAITING_FOR_BUMPER_RELEASE: - this.waitingForBumperRelease(); - break; - case STATE_EQUIP_SPRING: - this.pullTowardEquipPosition() - break; - case STATE_CONTINUE_NEAR_GRABBING: - case STATE_CONTINUE_EQUIP_BD: - case STATE_CONTINUE_EQUIP: - this.continueNearGrabbing(); - break; - case STATE_NEAR_TRIGGER: - this.nearTrigger(); - break; - case STATE_CONTINUE_NEAR_TRIGGER: - this.continueNearTrigger(); - break; - case STATE_FAR_TRIGGER: - this.farTrigger(); - break; - case STATE_CONTINUE_FAR_TRIGGER: - this.continueFarTrigger(); - break; - case STATE_RELEASE: - this.release(); - break; - } - }; - - this.setState = function(newState) { - if (WANT_DEBUG) { - print("STATE: " + stateToName(this.state) + " --> " + stateToName(newState) + ", hand: " + this.hand); - } - this.state = newState; - }; - - this.debugLine = function(closePoint, farPoint, color) { - Entities.addEntity({ - type: "Line", - name: "Grab Debug Entity", - dimensions: LINE_ENTITY_DIMENSIONS, - visible: true, - position: closePoint, - linePoints: [ZERO_VEC, farPoint], - color: color, - lifetime: 0.1, - collisionsWillMove: false, - ignoreForCollisions: true, - userData: JSON.stringify({ - grabbableKey: { - grabbable: false - } - }) - }); - }; - - this.lineOn = function(closePoint, farPoint, color) { - // draw a line - if (this.pointer === null) { - this.pointer = Entities.addEntity({ - type: "Line", - name: "grab pointer", - dimensions: LINE_ENTITY_DIMENSIONS, - visible: true, - position: closePoint, - linePoints: [ZERO_VEC, farPoint], - color: color, - lifetime: LIFETIME, - collisionsWillMove: false, - ignoreForCollisions: true, - userData: JSON.stringify({ - grabbableKey: { - grabbable: false - } - }) - }); - } else { - var age = Entities.getEntityProperties(this.pointer, "age").age; - this.pointer = Entities.editEntity(this.pointer, { - position: closePoint, - linePoints: [ZERO_VEC, farPoint], - color: color, - lifetime: age + LIFETIME - }); - } - }; - - this.overlayLineOn = function(closePoint, farPoint, color) { - if (this.overlayLine === null) { - var lineProperties = { - lineWidth: 5, - start: closePoint, - end: farPoint, - color: color, - ignoreRayIntersection: true, // always ignore this - visible: true, - alpha: 1 - }; - - this.overlayLine = Overlays.addOverlay("line3d", lineProperties); - - } else { - var success = Overlays.editOverlay(this.overlayLine, { - lineWidth: 5, - start: closePoint, - end: farPoint, - color: color, - visible: true, - ignoreRayIntersection: true, // always ignore this - alpha: 1 - }); - } - }; - - this.handleParticleBeam = function(position, orientation, color) { - - var rotation = Quat.angleAxis(0, { - x: 1, - y: 0, - z: 0 - }); - - var finalRotation = Quat.multiply(orientation, rotation); - var lifespan = LINE_LENGTH / 10; - var speed = 5; - var spread = 2; - if (this.particleBeam === null) { - this.createParticleBeam(position, finalRotation, color, speed, spread, lifespan); - } else { - this.updateParticleBeam(position, finalRotation, color, speed, spread, lifespan); - } - }; - - this.handleDistantParticleBeam = function(handPosition, objectPosition, color) { - - var handToObject = Vec3.subtract(objectPosition, handPosition); - var finalRotation = Quat.rotationBetween(Vec3.multiply(-1, Vec3.UP), handToObject); - - var distance = Vec3.distance(handPosition, objectPosition); - var speed = 5; - var spread = 0; - - var lifespan = distance / speed; - - - if (this.particleBeam === null) { - this.createParticleBeam(objectPosition, finalRotation, color, speed, spread, lifespan); - } else { - this.updateParticleBeam(objectPosition, finalRotation, color, speed, spread, lifespan); - } - }; - - this.createParticleBeam = function(position, orientation, color, speed, spread, lifespan) { - - var particleBeamProperties = { - type: "ParticleEffect", - isEmitting: true, - position: position, - visible: false, - "name": "Particle Beam", - "color": color, - "maxParticles": 2000, - "lifespan": lifespan, - "emitRate": 50, - "emitSpeed": speed, - "speedSpread": spread, - "emitOrientation": { - "x": -1, - "y": 0, - "z": 0, - "w": 1 - }, - "emitDimensions": { - "x": 0, - "y": 0, - "z": 0 - }, - "emitRadiusStart": 0.5, - "polarStart": 0, - "polarFinish": 0, - "azimuthStart": -3.1415927410125732, - "azimuthFinish": 3.1415927410125732, - "emitAcceleration": { - x: 0, - y: 0, - z: 0 - }, - "accelerationSpread": { - "x": 0, - "y": 0, - "z": 0 - }, - "particleRadius": 0.01, - "radiusSpread": 0, - // "radiusStart": 0.01, - // "radiusFinish": 0.01, - // "colorSpread": { - // "red": 0, - // "green": 0, - // "blue": 0 - // }, - // "colorStart": color, - // "colorFinish": color, - "alpha": 1, - "alphaSpread": 0, - "alphaStart": 1, - "alphaFinish": 1, - "additiveBlending": 0, - "textures": "https://hifi-content.s3.amazonaws.com/alan/dev/textures/grabsprite-3.png" - } - - this.particleBeam = Entities.addEntity(particleBeamProperties); - }; - - this.updateParticleBeam = function(position, orientation, color, speed, spread, lifespan) { - print('lifespan::' + lifespan); - Entities.editEntity(this.particleBeam, { - rotation: orientation, - position: position, - visible: true, - color: color, - emitSpeed: speed, - speedSpread:spread, - lifespan: lifespan - - }) - - }; - - this.evalLightWorldTransform = function(modelPos, modelRot) { - - var MODEL_LIGHT_POSITION = { - x: 0, - y: -0.3, - z: 0 - }; - - var MODEL_LIGHT_ROTATION = Quat.angleAxis(-90, { - x: 1, - y: 0, - z: 0 - }); - - return { - p: Vec3.sum(modelPos, Vec3.multiplyQbyV(modelRot, MODEL_LIGHT_POSITION)), - q: Quat.multiply(modelRot, MODEL_LIGHT_ROTATION) - }; - }; - - this.handleSpotlight = function(parentID, position) { - var LIFETIME = 100; - - var modelProperties = Entities.getEntityProperties(parentID, ['position', 'rotation']); - - var lightTransform = this.evalLightWorldTransform(modelProperties.position, modelProperties.rotation); - var lightProperties = { - type: "Light", - isSpotlight: true, - dimensions: { - x: 2, - y: 2, - z: 20 - }, - parentID: parentID, - color: { - red: 255, - green: 255, - blue: 255 - }, - intensity: 2, - exponent: 0.3, - cutoff: 20, - lifetime: LIFETIME, - position: lightTransform.p, - }; - - if (this.spotlight === null) { - this.spotlight = Entities.addEntity(lightProperties); - } else { - Entities.editEntity(this.spotlight, { - //without this, this light would maintain rotation with its parent - rotation: Quat.fromPitchYawRollDegrees(-90, 0, 0), - }) - } - }; - - this.handlePointLight = function(parentID, position) { - var LIFETIME = 100; - - var modelProperties = Entities.getEntityProperties(parentID, ['position', 'rotation']); - var lightTransform = this.evalLightWorldTransform(modelProperties.position, modelProperties.rotation); - - var lightProperties = { - type: "Light", - isSpotlight: false, - dimensions: { - x: 2, - y: 2, - z: 20 - }, - parentID: parentID, - color: { - red: 255, - green: 255, - blue: 255 - }, - intensity: 2, - exponent: 0.3, - cutoff: 20, - lifetime: LIFETIME, - position: lightTransform.p, - }; - - if (this.pointlight === null) { - this.pointlight = Entities.addEntity(lightProperties); - } else { - - } - }; - - this.lineOff = function() { - if (this.pointer !== null) { - Entities.deleteEntity(this.pointer); - } - this.pointer = null; - }; - - this.overlayLineOff = function() { - if (this.overlayLine !== null) { - Overlays.deleteOverlay(this.overlayLine); - } - this.overlayLine = null; - }; - - this.particleBeamOff = function() { - if (this.particleBeam !== null) { - Entities.editEntity(this.particleBeam, { - visible: false - }) - } - } - - this.turnLightsOff = function() { - if (this.spotlight !== null) { - Entities.deleteEntity(this.spotlight); - this.spotlight = null; - } - - if (this.pointlight !== null) { - Entities.deleteEntity(this.pointlight); - this.pointlight = null; - } - }; - - - this.turnOffVisualizations = function() { - if (USE_ENTITY_LINES_FOR_SEARCHING === true || USE_ENTITY_LINES_FOR_MOVING === true) { - this.lineOff(); - } - - if (USE_OVERLAY_LINES_FOR_SEARCHING === true || USE_OVERLAY_LINES_FOR_MOVING === true) { - this.overlayLineOff(); - } - - if (USE_PARTICLE_BEAM_FOR_SEARCHING === true || USE_PARTICLE_BEAM_FOR_MOVING === true) { - this.particleBeamOff(); - } - }; - - this.triggerPress = function(value) { - _this.rawTriggerValue = value; - }; - - this.bumperPress = function(value) { - _this.rawBumperValue = value; - }; - - this.updateSmoothedTrigger = function() { - var triggerValue = this.rawTriggerValue; - // smooth out trigger value - this.triggerValue = (this.triggerValue * TRIGGER_SMOOTH_RATIO) + - (triggerValue * (1.0 - TRIGGER_SMOOTH_RATIO)); - }; - - this.triggerSmoothedSqueezed = function() { - return this.triggerValue > TRIGGER_ON_VALUE; - }; - - this.triggerSmoothedReleased = function() { - return this.triggerValue < TRIGGER_OFF_VALUE; - }; - - this.triggerSqueezed = function() { - var triggerValue = this.rawTriggerValue; - return triggerValue > TRIGGER_ON_VALUE; - }; - - this.bumperSqueezed = function() { - return _this.rawBumperValue > BUMPER_ON_VALUE; - }; - - this.bumperReleased = function() { - return _this.rawBumperValue < BUMPER_ON_VALUE; - }; - - this.off = function() { - if (this.triggerSmoothedSqueezed()) { - this.lastPickTime = 0; - this.setState(STATE_SEARCHING); - return; - } - if (this.bumperSqueezed()) { - this.lastPickTime = 0; - this.setState(STATE_EQUIP_SEARCHING); - return; - } - }; - - this.search = function() { - this.grabbedEntity = null; - - if (this.state == STATE_SEARCHING ? this.triggerSmoothedReleased() : this.bumperReleased()) { - this.setState(STATE_RELEASE); - return; - } - - // the trigger is being pressed, do a ray test - var handPosition = this.getHandPosition(); - var distantPickRay = { - origin: handPosition, - direction: Quat.getUp(this.getHandRotation()), - length: PICK_MAX_DISTANCE - }; - - // don't pick 60x per second. - var pickRays = []; - var now = Date.now(); - if (now - this.lastPickTime > MSECS_PER_SEC / PICKS_PER_SECOND_PER_HAND) { - pickRays = [distantPickRay]; - this.lastPickTime = now; - } - - for (var index = 0; index < pickRays.length; ++index) { - var pickRay = pickRays[index]; - var directionNormalized = Vec3.normalize(pickRay.direction); - var directionBacked = Vec3.multiply(directionNormalized, PICK_BACKOFF_DISTANCE); - var pickRayBacked = { - origin: Vec3.subtract(pickRay.origin, directionBacked), - direction: pickRay.direction - }; - - if (WANT_DEBUG) { - this.debugLine(pickRayBacked.origin, Vec3.multiply(pickRayBacked.direction, NEAR_PICK_MAX_DISTANCE), { - red: 0, - green: 255, - blue: 0 - }) - } - - var intersection = Entities.findRayIntersection(pickRayBacked, true); - - if (intersection.intersects) { - // the ray is intersecting something we can move. - var intersectionDistance = Vec3.distance(pickRay.origin, intersection.intersection); - - var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, intersection.entityID, DEFAULT_GRABBABLE_DATA); - - if (intersection.properties.name == "Grab Debug Entity") { - continue; - } - - if (typeof grabbableData.grabbable !== 'undefined' && !grabbableData.grabbable) { - continue; - } - if (intersectionDistance > pickRay.length) { - // too far away for this ray. - continue; - } - if (intersectionDistance <= NEAR_PICK_MAX_DISTANCE) { - // the hand is very close to the intersected object. go into close-grabbing mode. - if (grabbableData.wantsTrigger) { - this.grabbedEntity = intersection.entityID; - this.setState(STATE_NEAR_TRIGGER); - return; - } else if (!intersection.properties.locked) { - this.grabbedEntity = intersection.entityID; - if (this.state == STATE_SEARCHING) { - this.setState(STATE_NEAR_GRABBING); - } else { // equipping - if (typeof grabbableData.spatialKey !== 'undefined') { - // TODO - // if we go to STATE_EQUIP_SPRING the item will be pulled to the hand and will then switch - // to STATE_EQUIP. This needs some debugging, so just jump straight to STATE_EQUIP here. - // this.setState(STATE_EQUIP_SPRING); - this.setState(STATE_EQUIP); - } else { - this.setState(STATE_EQUIP); - } - } - return; - } - } else if (!entityIsGrabbedByOther(intersection.entityID)) { - // don't allow two people to distance grab the same object - if (intersection.properties.collisionsWillMove && !intersection.properties.locked) { - // the hand is far from the intersected object. go into distance-holding mode - this.grabbedEntity = intersection.entityID; - if (typeof grabbableData.spatialKey !== 'undefined' && this.state == STATE_EQUIP_SEARCHING) { - // if a distance pick in equip mode hits something with a spatialKey, equip it - // TODO use STATE_EQUIP_SPRING here once it works right. - // this.setState(STATE_EQUIP_SPRING); - this.setState(STATE_EQUIP); - return; - } else if (this.state == STATE_SEARCHING) { - this.setState(STATE_DISTANCE_HOLDING); - return; - } - } else if (grabbableData.wantsTrigger) { - this.grabbedEntity = intersection.entityID; - this.setState(STATE_FAR_TRIGGER); - return; - } - } - } - } - - // forward ray test failed, try sphere test. - if (WANT_DEBUG) { - Entities.addEntity({ - type: "Sphere", - name: "Grab Debug Entity", - dimensions: { - x: GRAB_RADIUS, - y: GRAB_RADIUS, - z: GRAB_RADIUS - }, - visible: true, - position: handPosition, - color: { - red: 0, - green: 255, - blue: 0 - }, - lifetime: 0.1, - collisionsWillMove: false, - ignoreForCollisions: true, - userData: JSON.stringify({ - grabbableKey: { - grabbable: false - } - }) - }); - } - - var nearbyEntities = Entities.findEntities(handPosition, GRAB_RADIUS); - var minDistance = PICK_MAX_DISTANCE; - var i, props, distance, grabbableData; - this.grabbedEntity = null; - for (i = 0; i < nearbyEntities.length; i++) { - var grabbableDataForCandidate = - getEntityCustomData(GRABBABLE_DATA_KEY, nearbyEntities[i], DEFAULT_GRABBABLE_DATA); - if (typeof grabbableDataForCandidate.grabbable !== 'undefined' && !grabbableDataForCandidate.grabbable) { - continue; - } - var propsForCandidate = Entities.getEntityProperties(nearbyEntities[i], GRABBABLE_PROPERTIES); - - if (propsForCandidate.type == 'Unknown') { - continue; - } - - if (propsForCandidate.type == 'Light') { - continue; - } - - if (propsForCandidate.type == 'ParticleEffect') { - continue; - } - - if (propsForCandidate.type == 'PolyLine') { - continue; - } - - if (propsForCandidate.type == 'Zone') { - continue; - } - - if (propsForCandidate.locked && !grabbableDataForCandidate.wantsTrigger) { - continue; - } - - if (propsForCandidate.name == "Grab Debug Entity") { - continue; - } - - if (propsForCandidate.name == "grab pointer") { - continue; - } - - distance = Vec3.distance(propsForCandidate.position, handPosition); - if (distance < minDistance) { - this.grabbedEntity = nearbyEntities[i]; - minDistance = distance; - props = propsForCandidate; - grabbableData = grabbableDataForCandidate; - } - } - if (this.grabbedEntity !== null) { - if (grabbableData.wantsTrigger) { - this.setState(STATE_NEAR_TRIGGER); - return; - } else if (!props.locked && props.collisionsWillMove) { - this.setState(this.state == STATE_SEARCHING ? STATE_NEAR_GRABBING : STATE_EQUIP) - return; - } - } - - //search line visualizations - if (USE_ENTITY_LINES_FOR_SEARCHING === true) { - this.lineOn(distantPickRay.origin, Vec3.multiply(distantPickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); - } - - if (USE_OVERLAY_LINES_FOR_SEARCHING === true) { - this.overlayLineOn(distantPickRay.origin, Vec3.sum(distantPickRay.origin, Vec3.multiply(distantPickRay.direction, LINE_LENGTH)), NO_INTERSECT_COLOR); - } - - if (USE_PARTICLE_BEAM_FOR_SEARCHING === true) { - this.handleParticleBeam(distantPickRay.origin, this.getHandRotation(), NO_INTERSECT_COLOR); - } - - }; - - this.distanceHolding = function() { - var handControllerPosition = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandPosition : MyAvatar.leftHandPosition; - var controllerHandInput = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; - var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getPoseValue(controllerHandInput).rotation); - var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); - var now = Date.now(); - - // add the action and initialize some variables - this.currentObjectPosition = grabbedProperties.position; - this.currentObjectRotation = grabbedProperties.rotation; - this.currentObjectTime = now; - this.handRelativePreviousPosition = Vec3.subtract(handControllerPosition, MyAvatar.position); - this.handPreviousRotation = handRotation; - this.currentCameraOrientation = Camera.orientation; - - // compute a constant based on the initial conditions which we use below to exagerate hand motion onto the held object - this.radiusScalar = Math.log(Vec3.distance(this.currentObjectPosition, handControllerPosition) + 1.0); - if (this.radiusScalar < 1.0) { - this.radiusScalar = 1.0; - } - - this.actionID = NULL_ACTION_ID; - this.actionID = Entities.addAction("spring", this.grabbedEntity, { - targetPosition: this.currentObjectPosition, - linearTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME, - targetRotation: this.currentObjectRotation, - angularTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME, - tag: getTag(), - ttl: ACTION_TTL - }); - if (this.actionID === NULL_ACTION_ID) { - this.actionID = null; - } - this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC); - - if (this.actionID !== null) { - this.setState(STATE_CONTINUE_DISTANCE_HOLDING); - this.activateEntity(this.grabbedEntity, grabbedProperties); - if (this.hand === RIGHT_HAND) { - Entities.callEntityMethod(this.grabbedEntity, "setRightHand"); - } else { - Entities.callEntityMethod(this.grabbedEntity, "setLeftHand"); - } - Entities.callEntityMethod(this.grabbedEntity, "setHand", [this.hand]); - Entities.callEntityMethod(this.grabbedEntity, "startDistantGrab"); - } - - this.currentAvatarPosition = MyAvatar.position; - this.currentAvatarOrientation = MyAvatar.orientation; - - this.turnOffVisualizations(); - }; - - this.continueDistanceHolding = function() { - if (this.triggerSmoothedReleased()) { - this.setState(STATE_RELEASE); - Entities.callEntityMethod(this.grabbedEntity, "releaseGrab"); - return; - } - - var handPosition = this.getHandPosition(); - var handControllerPosition = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandPosition : MyAvatar.leftHandPosition; - var controllerHandInput = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; - var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getPoseValue(controllerHandInput).rotation); - var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); - var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); - - if (this.state == STATE_CONTINUE_DISTANCE_HOLDING && this.bumperSqueezed() && - typeof grabbableData.spatialKey !== 'undefined') { - var saveGrabbedID = this.grabbedEntity; - this.release(); - this.setState(STATE_EQUIP); - this.grabbedEntity = saveGrabbedID; - return; - } - - - // the action was set up on a previous call. update the targets. - var radius = Vec3.distance(this.currentObjectPosition, handControllerPosition) * - this.radiusScalar * DISTANCE_HOLDING_RADIUS_FACTOR; - if (radius < 1.0) { - radius = 1.0; - } - - // how far did avatar move this timestep? - var currentPosition = MyAvatar.position; - var avatarDeltaPosition = Vec3.subtract(currentPosition, this.currentAvatarPosition); - this.currentAvatarPosition = currentPosition; - - // How far did the avatar turn this timestep? - // Note: The following code is too long because we need a Quat.quatBetween() function - // that returns the minimum quaternion between two quaternions. - var currentOrientation = MyAvatar.orientation; - if (Quat.dot(currentOrientation, this.currentAvatarOrientation) < 0.0) { - var negativeCurrentOrientation = { - x: -currentOrientation.x, - y: -currentOrientation.y, - z: -currentOrientation.z, - w: -currentOrientation.w - }; - var avatarDeltaOrientation = Quat.multiply(negativeCurrentOrientation, Quat.inverse(this.currentAvatarOrientation)); - } else { - var avatarDeltaOrientation = Quat.multiply(currentOrientation, Quat.inverse(this.currentAvatarOrientation)); - } - var handToAvatar = Vec3.subtract(handControllerPosition, this.currentAvatarPosition); - var objectToAvatar = Vec3.subtract(this.currentObjectPosition, this.currentAvatarPosition); - var handMovementFromTurning = Vec3.subtract(Quat.multiply(avatarDeltaOrientation, handToAvatar), handToAvatar); - var objectMovementFromTurning = Vec3.subtract(Quat.multiply(avatarDeltaOrientation, objectToAvatar), objectToAvatar); - this.currentAvatarOrientation = currentOrientation; - - // how far did hand move this timestep? - var handMoved = Vec3.subtract(handToAvatar, this.handRelativePreviousPosition); - this.handRelativePreviousPosition = handToAvatar; - - // magnify the hand movement but not the change from avatar movement & rotation - handMoved = Vec3.subtract(handMoved, handMovementFromTurning); - var superHandMoved = Vec3.multiply(handMoved, radius); - - // Move the object by the magnified amount and then by amount from avatar movement & rotation - var newObjectPosition = Vec3.sum(this.currentObjectPosition, superHandMoved); - newObjectPosition = Vec3.sum(newObjectPosition, avatarDeltaPosition); - newObjectPosition = Vec3.sum(newObjectPosition, objectMovementFromTurning); - - var deltaPosition = Vec3.subtract(newObjectPosition, this.currentObjectPosition); // meters - var now = Date.now(); - var deltaTime = (now - this.currentObjectTime) / MSEC_PER_SEC; // convert to seconds - - this.currentObjectPosition = newObjectPosition; - this.currentObjectTime = now; - - // this doubles hand rotation - var handChange = Quat.multiply(Quat.slerp(this.handPreviousRotation, - handRotation, - DISTANCE_HOLDING_ROTATION_EXAGGERATION_FACTOR), - Quat.inverse(this.handPreviousRotation)); - this.handPreviousRotation = handRotation; - this.currentObjectRotation = Quat.multiply(handChange, this.currentObjectRotation); - - Entities.callEntityMethod(this.grabbedEntity, "continueDistantGrab"); - - // mix in head motion - if (MOVE_WITH_HEAD) { - var objDistance = Vec3.length(objectToAvatar); - var before = Vec3.multiplyQbyV(this.currentCameraOrientation, { - x: 0.0, - y: 0.0, - z: objDistance - }); - var after = Vec3.multiplyQbyV(Camera.orientation, { - x: 0.0, - y: 0.0, - z: objDistance - }); - var change = Vec3.subtract(before, after); - this.currentCameraOrientation = Camera.orientation; - this.currentObjectPosition = Vec3.sum(this.currentObjectPosition, change); - } - - - //visualizations - if (USE_ENTITY_LINES_FOR_MOVING === true) { - this.lineOn(handPosition, Vec3.subtract(grabbedProperties.position, handPosition), INTERSECT_COLOR); - } - if (USE_OVERLAY_LINES_FOR_MOVING === true) { - this.overlayLineOn(handPosition, grabbedProperties.position, INTERSECT_COLOR); - } - if (USE_PARTICLE_BEAM_FOR_MOVING === true) { - this.handleDistantParticleBeam(handPosition,grabbedProperties.position, INTERSECT_COLOR) - // this.handleDistantParticleBeam(handPosition, this.currentObjectPosition, INTERSECT_COLOR) - } - if (USE_POINTLIGHT === true) { - this.handlePointLight(this.grabbedEntity); - } - if (USE_SPOTLIGHT === true) { - this.handleSpotlight(this.grabbedEntity); - } - - Entities.updateAction(this.grabbedEntity, this.actionID, { - targetPosition: this.currentObjectPosition, - linearTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME, - targetRotation: this.currentObjectRotation, - angularTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME, - ttl: ACTION_TTL - }); - - this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC); - }; - - this.nearGrabbing = function() { - var now = Date.now(); - var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); - - if (this.state == STATE_NEAR_GRABBING && this.triggerSmoothedReleased()) { - this.setState(STATE_RELEASE); - Entities.callEntityMethod(this.grabbedEntity, "releaseGrab"); - return; - } - - this.turnOffVisualizations(); - - var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); - this.activateEntity(this.grabbedEntity, grabbedProperties); - if (grabbedProperties.collisionsWillMove && NEAR_GRABBING_KINEMATIC) { - Entities.editEntity(this.grabbedEntity, { - collisionsWillMove: false - }); - } - - var handRotation = this.getHandRotation(); - var handPosition = this.getHandPosition(); - - var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); - - if (this.state != STATE_NEAR_GRABBING && grabbableData.spatialKey) { - // if an object is "equipped" and has a spatialKey, use it. - this.ignoreIK = grabbableData.spatialKey.ignoreIK ? grabbableData.spatialKey.ignoreIK : false; - this.offsetPosition = getSpatialOffsetPosition(this.hand, grabbableData.spatialKey); - this.offsetRotation = getSpatialOffsetRotation(this.hand, grabbableData.spatialKey); - } else { - this.ignoreIK = false; - - var objectRotation = grabbedProperties.rotation; - this.offsetRotation = Quat.multiply(Quat.inverse(handRotation), objectRotation); - - var currentObjectPosition = grabbedProperties.position; - var offset = Vec3.subtract(currentObjectPosition, handPosition); - this.offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, this.offsetRotation)), offset); - } - - this.actionID = NULL_ACTION_ID; - this.actionID = Entities.addAction("hold", this.grabbedEntity, { - hand: this.hand === RIGHT_HAND ? "right" : "left", - timeScale: NEAR_GRABBING_ACTION_TIMEFRAME, - relativePosition: this.offsetPosition, - relativeRotation: this.offsetRotation, - ttl: ACTION_TTL, - kinematic: NEAR_GRABBING_KINEMATIC, - kinematicSetVelocity: true, - ignoreIK: this.ignoreIK - }); - if (this.actionID === NULL_ACTION_ID) { - this.actionID = null; - } else { - this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC); - if (this.state == STATE_NEAR_GRABBING) { - this.setState(STATE_CONTINUE_NEAR_GRABBING); - } else { - // equipping - Entities.callEntityMethod(this.grabbedEntity, "startEquip", [JSON.stringify(this.hand)]); - this.startHandGrasp(); - - this.setState(STATE_CONTINUE_EQUIP_BD); - } - - if (this.hand === RIGHT_HAND) { - Entities.callEntityMethod(this.grabbedEntity, "setRightHand"); - } else { - Entities.callEntityMethod(this.grabbedEntity, "setLeftHand"); - } - - Entities.callEntityMethod(this.grabbedEntity, "setHand", [this.hand]); - - Entities.callEntityMethod(this.grabbedEntity, "startNearGrab"); - - } - - this.currentHandControllerTipPosition = - (this.hand === RIGHT_HAND) ? MyAvatar.rightHandTipPosition : MyAvatar.leftHandTipPosition; - - this.currentObjectTime = Date.now(); - }; - - this.continueNearGrabbing = function() { - if (this.state == STATE_CONTINUE_NEAR_GRABBING && this.triggerSmoothedReleased()) { - this.setState(STATE_RELEASE); - Entities.callEntityMethod(this.grabbedEntity, "releaseGrab"); - return; - } - if (this.state == STATE_CONTINUE_EQUIP_BD && this.bumperReleased()) { - this.setState(STATE_CONTINUE_EQUIP); - return; - } - if (this.state == STATE_CONTINUE_EQUIP && this.bumperSqueezed()) { - this.setState(STATE_WAITING_FOR_BUMPER_RELEASE); - return; - } - if (this.state == STATE_CONTINUE_NEAR_GRABBING && this.bumperSqueezed()) { - this.setState(STATE_CONTINUE_EQUIP_BD); - Entities.callEntityMethod(this.grabbedEntity, "startEquip", [JSON.stringify(this.hand)]); - return; - } - - // Keep track of the fingertip velocity to impart when we release the object. - // Note that the idea of using a constant 'tip' velocity regardless of the - // object's actual held offset is an idea intended to make it easier to throw things: - // Because we might catch something or transfer it between hands without a good idea - // of it's actual offset, let's try imparting a velocity which is at a fixed radius - // from the palm. - - var handControllerPosition = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandPosition : MyAvatar.leftHandPosition; - var now = Date.now(); - - var deltaPosition = Vec3.subtract(handControllerPosition, this.currentHandControllerTipPosition); // meters - var deltaTime = (now - this.currentObjectTime) / MSEC_PER_SEC; // convert to seconds - - this.currentHandControllerTipPosition = handControllerPosition; - this.currentObjectTime = now; - Entities.callEntityMethod(this.grabbedEntity, "continueNearGrab"); - - if (this.state === STATE_CONTINUE_EQUIP_BD) { - Entities.callEntityMethod(this.grabbedEntity, "continueEquip"); - } - - if (this.actionTimeout - now < ACTION_TTL_REFRESH * MSEC_PER_SEC) { - // if less than a 5 seconds left, refresh the actions ttl - Entities.updateAction(this.grabbedEntity, this.actionID, { - hand: this.hand === RIGHT_HAND ? "right" : "left", - timeScale: NEAR_GRABBING_ACTION_TIMEFRAME, - relativePosition: this.offsetPosition, - relativeRotation: this.offsetRotation, - ttl: ACTION_TTL, - kinematic: NEAR_GRABBING_KINEMATIC, - kinematicSetVelocity: true, - ignoreIK: this.ignoreIK - }); - this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC); - } - }; - - this.waitingForBumperRelease = function() { - if (this.bumperReleased()) { - this.setState(STATE_RELEASE); - Entities.callEntityMethod(this.grabbedEntity, "releaseGrab"); - Entities.callEntityMethod(this.grabbedEntity, "unequip"); - this.endHandGrasp(); - - } - }; - - this.pullTowardEquipPosition = function() { - - this.turnOffVisualizations(); - - var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); - var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); - - // use a spring to pull the object to where it will be when equipped - var relativeRotation = getSpatialOffsetRotation(this.hand, grabbableData.spatialKey); - var relativePosition = getSpatialOffsetPosition(this.hand, grabbableData.spatialKey); - var ignoreIK = grabbableData.spatialKey.ignoreIK ? grabbableData.spatialKey.ignoreIK : false; - var handRotation = this.getHandRotation(); - var handPosition = this.getHandPosition(); - var targetRotation = Quat.multiply(handRotation, relativeRotation); - var offset = Vec3.multiplyQbyV(targetRotation, relativePosition); - var targetPosition = Vec3.sum(handPosition, offset); - - if (typeof this.equipSpringID === 'undefined' || - this.equipSpringID === null || - this.equipSpringID === NULL_ACTION_ID) { - this.equipSpringID = Entities.addAction("spring", this.grabbedEntity, { - targetPosition: targetPosition, - linearTimeScale: EQUIP_SPRING_TIMEFRAME, - targetRotation: targetRotation, - angularTimeScale: EQUIP_SPRING_TIMEFRAME, - ttl: ACTION_TTL, - ignoreIK: ignoreIK - }); - if (this.equipSpringID === NULL_ACTION_ID) { - this.equipSpringID = null; - this.setState(STATE_OFF); - return; - } - } else { - Entities.updateAction(this.grabbedEntity, this.equipSpringID, { - targetPosition: targetPosition, - linearTimeScale: EQUIP_SPRING_TIMEFRAME, - targetRotation: targetRotation, - angularTimeScale: EQUIP_SPRING_TIMEFRAME, - ttl: ACTION_TTL, - ignoreIK: ignoreIK - }); - } - - if (Vec3.distance(grabbedProperties.position, targetPosition) < EQUIP_SPRING_SHUTOFF_DISTANCE) { - Entities.deleteAction(this.grabbedEntity, this.equipSpringID); - this.equipSpringID = null; - this.setState(STATE_EQUIP); - } - }; - - this.nearTrigger = function() { - if (this.triggerSmoothedReleased()) { - this.setState(STATE_RELEASE); - Entities.callEntityMethod(this.grabbedEntity, "stopNearTrigger"); - return; - } - if (this.hand === RIGHT_HAND) { - Entities.callEntityMethod(this.grabbedEntity, "setRightHand"); - } else { - Entities.callEntityMethod(this.grabbedEntity, "setLeftHand"); - } - - Entities.callEntityMethod(this.grabbedEntity, "setHand", [this.hand]); - - Entities.callEntityMethod(this.grabbedEntity, "startNearTrigger"); - this.setState(STATE_CONTINUE_NEAR_TRIGGER); - }; - - this.farTrigger = function() { - if (this.triggerSmoothedReleased()) { - this.setState(STATE_RELEASE); - Entities.callEntityMethod(this.grabbedEntity, "stopFarTrigger"); - return; - } - - if (this.hand === RIGHT_HAND) { - Entities.callEntityMethod(this.grabbedEntity, "setRightHand"); - } else { - Entities.callEntityMethod(this.grabbedEntity, "setLeftHand"); - } - Entities.callEntityMethod(this.grabbedEntity, "setHand", [this.hand]); - Entities.callEntityMethod(this.grabbedEntity, "startFarTrigger"); - this.setState(STATE_CONTINUE_FAR_TRIGGER); - }; - - this.continueNearTrigger = function() { - if (this.triggerSmoothedReleased()) { - this.setState(STATE_RELEASE); - Entities.callEntityMethod(this.grabbedEntity, "stopNearTrigger"); - return; - } - - Entities.callEntityMethod(this.grabbedEntity, "continueNearTrigger"); - }; - - this.continueFarTrigger = function() { - if (this.triggerSmoothedReleased()) { - this.setState(STATE_RELEASE); - Entities.callEntityMethod(this.grabbedEntity, "stopNearTrigger"); - return; - } - - var handPosition = this.getHandPosition(); - var pickRay = { - origin: handPosition, - direction: Quat.getUp(this.getHandRotation()) - }; - - var now = Date.now(); - if (now - this.lastPickTime > MSECS_PER_SEC / PICKS_PER_SECOND_PER_HAND) { - var intersection = Entities.findRayIntersection(pickRay, true); - this.lastPickTime = now; - if (intersection.entityID != this.grabbedEntity) { - this.setState(STATE_RELEASE); - Entities.callEntityMethod(this.grabbedEntity, "stopFarTrigger"); - return; - } - } - - if (USE_ENTITY_LINES_FOR_MOVING === true) { - this.lineOn(pickRay.origin, Vec3.multiply(pickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); - } - - Entities.callEntityMethod(this.grabbedEntity, "continueFarTrigger"); - }; - - _this.allTouchedIDs = {}; - - this.touchTest = function() { - var maxDistance = 0.05; - var leftHandPosition = MyAvatar.getLeftPalmPosition(); - var rightHandPosition = MyAvatar.getRightPalmPosition(); - var leftEntities = Entities.findEntities(leftHandPosition, maxDistance); - var rightEntities = Entities.findEntities(rightHandPosition, maxDistance); - var ids = []; - - if (leftEntities.length !== 0) { - leftEntities.forEach(function(entity) { - ids.push(entity); - }); - - } - - if (rightEntities.length !== 0) { - rightEntities.forEach(function(entity) { - ids.push(entity); - }); - } - - ids.forEach(function(id) { - - var props = Entities.getEntityProperties(id, ["boundingBox", "name"]); - if (props.name === 'pointer') { - return; - } else { - var entityMinPoint = props.boundingBox.brn; - var entityMaxPoint = props.boundingBox.tfl; - var leftIsTouching = pointInExtents(leftHandPosition, entityMinPoint, entityMaxPoint); - var rightIsTouching = pointInExtents(rightHandPosition, entityMinPoint, entityMaxPoint); - - if ((leftIsTouching || rightIsTouching) && _this.allTouchedIDs[id] === undefined) { - // we haven't been touched before, but either right or left is touching us now - _this.allTouchedIDs[id] = true; - _this.startTouch(id); - } else if ((leftIsTouching || rightIsTouching) && _this.allTouchedIDs[id]) { - // we have been touched before and are still being touched - // continue touch - _this.continueTouch(id); - } else if (_this.allTouchedIDs[id]) { - delete _this.allTouchedIDs[id]; - _this.stopTouch(id); - - } else { - //we are in another state - return; - } - } - - }); - - }; - - this.startTouch = function(entityID) { - Entities.callEntityMethod(entityID, "startTouch"); - }; - - this.continueTouch = function(entityID) { - Entities.callEntityMethod(entityID, "continueTouch"); - }; - - this.stopTouch = function(entityID) { - Entities.callEntityMethod(entityID, "stopTouch"); - }; - - this.release = function() { - - this.turnLightsOff(); - this.turnOffVisualizations(); - - if (this.grabbedEntity !== null) { - if (this.actionID !== null) { - Entities.deleteAction(this.grabbedEntity, this.actionID); - } - } - - this.deactivateEntity(this.grabbedEntity); - - this.grabbedEntity = null; - this.actionID = null; - this.setState(STATE_OFF); - }; - - this.cleanup = function() { - this.release(); - this.endHandGrasp(); - Entities.deleteEntity(this.particleBeam); - Entities.deleteEntity(this.spotLight); - Entities.deleteEntity(this.pointLight); - }; - - this.activateEntity = function(entityID, grabbedProperties) { - var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, entityID, DEFAULT_GRABBABLE_DATA); - var invertSolidWhileHeld = grabbableData["invertSolidWhileHeld"]; - var data = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, {}); - data["activated"] = true; - data["avatarId"] = MyAvatar.sessionUUID; - data["refCount"] = data["refCount"] ? data["refCount"] + 1 : 1; - // zero gravity and set ignoreForCollisions in a way that lets us put them back, after all grabs are done - if (data["refCount"] == 1) { - data["gravity"] = grabbedProperties.gravity; - data["ignoreForCollisions"] = grabbedProperties.ignoreForCollisions; - data["collisionsWillMove"] = grabbedProperties.collisionsWillMove; - var whileHeldProperties = { - gravity: { - x: 0, - y: 0, - z: 0 - } - }; - if (invertSolidWhileHeld) { - whileHeldProperties["ignoreForCollisions"] = !grabbedProperties.ignoreForCollisions; - } - Entities.editEntity(entityID, whileHeldProperties); - } - - setEntityCustomData(GRAB_USER_DATA_KEY, entityID, data); - return data; - }; - - this.deactivateEntity = function(entityID) { - var data = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, {}); - if (data && data["refCount"]) { - data["refCount"] = data["refCount"] - 1; - if (data["refCount"] < 1) { - Entities.editEntity(entityID, { - gravity: data["gravity"], - ignoreForCollisions: data["ignoreForCollisions"], - collisionsWillMove: data["collisionsWillMove"] - }); - data = null; - } - } else { - data = null; - } - setEntityCustomData(GRAB_USER_DATA_KEY, entityID, data); - }; - - - //this is our handler, where we do the actual work of changing animation settings - this.graspHand = function(animationProperties) { - var result = {}; - //full alpha on overlay for this hand - //set grab to true - //set idle to false - //full alpha on the blend btw open and grab - if (_this.hand === RIGHT_HAND) { - result['rightHandOverlayAlpha'] = 1.0; - result['isRightHandGrab'] = true; - result['isRightHandIdle'] = false; - result['rightHandGrabBlend'] = 1.0; - } else if (_this.hand === LEFT_HAND) { - result['leftHandOverlayAlpha'] = 1.0; - result['isLeftHandGrab'] = true; - result['isLeftHandIdle'] = false; - result['leftHandGrabBlend'] = 1.0; - } - //return an object with our updated settings - return result; - }; - - this.graspHandler = null - - this.startHandGrasp = function() { - if (this.hand === RIGHT_HAND) { - this.graspHandler = MyAvatar.addAnimationStateHandler(this.graspHand, ['isRightHandGrab']); - } else if (this.hand === LEFT_HAND) { - this.graspHandler = MyAvatar.addAnimationStateHandler(this.graspHand, ['isLeftHandGrab']); - } - }; - - this.endHandGrasp = function() { - // Tell the animation system we don't need any more callbacks. - MyAvatar.removeAnimationStateHandler(this.graspHandler); - }; - -}; - -var rightController = new MyController(RIGHT_HAND); -var leftController = new MyController(LEFT_HAND); - -//preload the particle beams so that they are full length when you start searching -if (USE_PARTICLE_BEAM_FOR_SEARCHING === true || USE_PARTICLE_BEAM_FOR_MOVING === true) { - rightController.createParticleBeam(); - leftController.createParticleBeam(); -} - -var MAPPING_NAME = "com.highfidelity.handControllerGrab"; - -var mapping = Controller.newMapping(MAPPING_NAME); -mapping.from([Controller.Standard.RT]).peek().to(rightController.triggerPress); -mapping.from([Controller.Standard.LT]).peek().to(leftController.triggerPress); - -mapping.from([Controller.Standard.RB]).peek().to(rightController.bumperPress); -mapping.from([Controller.Standard.LB]).peek().to(leftController.bumperPress); - -Controller.enableMapping(MAPPING_NAME); - -//the section below allows the grab script to listen for messages that disable either one or both hands. useful for two handed items -var handToDisable = 'none'; - -function update() { - if (handToDisable !== LEFT_HAND && handToDisable !== 'both') { - leftController.update(); - } - if (handToDisable !== RIGHT_HAND && handToDisable !== 'both') { - rightController.update(); - } -} - -Messages.subscribe('Hifi-Hand-Disabler'); - -handleHandDisablerMessages = function(channel, message, sender) { - - if (sender === MyAvatar.sessionUUID) { - if (message === 'left') { - handToDisable = LEFT_HAND; - } - if (message === 'right') { - handToDisable = RIGHT_HAND; - } - if (message === 'both') { - handToDisable = 'both'; - } - if (message === 'none') { - handToDisable = 'none'; - } - } - -} - -Messages.messageReceived.connect(handleHandDisablerMessages); - -function cleanup() { - rightController.cleanup(); - leftController.cleanup(); - Controller.disableMapping(MAPPING_NAME); -} - -Script.scriptEnding.connect(cleanup); -Script.update.connect(update); \ No newline at end of file diff --git a/examples/controllers/handControllerGrab-pointlight.js b/examples/controllers/handControllerGrab-pointlight.js deleted file mode 100644 index e7bb4e3e9f..0000000000 --- a/examples/controllers/handControllerGrab-pointlight.js +++ /dev/null @@ -1,1656 +0,0 @@ -// handControllerGrab.js -// -// Created by Eric Levin on 9/2/15 -// Additions by James B. Pollack @imgntn on 9/24/2015 -// Additions By Seth Alves on 10/20/2015 -// Copyright 2015 High Fidelity, Inc. -// -// Grabs physically moveable entities with hydra-like controllers; it works for either near or far objects. -// Also supports touch and equipping objects. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -/*global print, MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, Audio, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt, pointInExtents, vec3equal, setEntityCustomData, getEntityCustomData */ - -Script.include("../libraries/utils.js"); - -// -// add lines where the hand ray picking is happening -// -var WANT_DEBUG = false; - -// -// these tune time-averaging and "on" value for analog trigger -// - -var TRIGGER_SMOOTH_RATIO = 0.1; // 0.0 disables smoothing of trigger value -var TRIGGER_ON_VALUE = 0.4; -var TRIGGER_OFF_VALUE = 0.15; - -var BUMPER_ON_VALUE = 0.5; - -// -// distant manipulation -// - -var DISTANCE_HOLDING_RADIUS_FACTOR = 3.5; // multiplied by distance between hand and object -var DISTANCE_HOLDING_ACTION_TIMEFRAME = 0.1; // how quickly objects move to their new position -var DISTANCE_HOLDING_ROTATION_EXAGGERATION_FACTOR = 2.0; // object rotates this much more than hand did -var MOVE_WITH_HEAD = true; // experimental head-controll of distantly held objects - -var NO_INTERSECT_COLOR = { - red: 10, - green: 10, - blue: 255 -}; // line color when pick misses -var INTERSECT_COLOR = { - red: 250, - green: 10, - blue: 10 -}; // line color when pick hits -var LINE_ENTITY_DIMENSIONS = { - x: 1000, - y: 1000, - z: 1000 -}; - -var LINE_LENGTH = 500; -var PICK_MAX_DISTANCE = 500; // max length of pick-ray - -// -// near grabbing -// - -var GRAB_RADIUS = 0.03; // if the ray misses but an object is this close, it will still be selected -var NEAR_GRABBING_ACTION_TIMEFRAME = 0.05; // how quickly objects move to their new position -var NEAR_GRABBING_VELOCITY_SMOOTH_RATIO = 1.0; // adjust time-averaging of held object's velocity. 1.0 to disable. -var NEAR_PICK_MAX_DISTANCE = 0.3; // max length of pick-ray for close grabbing to be selected -var RELEASE_VELOCITY_MULTIPLIER = 1.5; // affects throwing things -var PICK_BACKOFF_DISTANCE = 0.2; // helps when hand is intersecting the grabble object -var NEAR_GRABBING_KINEMATIC = true; // force objects to be kinematic when near-grabbed - -// -// equip -// - -var EQUIP_SPRING_SHUTOFF_DISTANCE = 0.05; -var EQUIP_SPRING_TIMEFRAME = 0.4; // how quickly objects move to their new position - -// -// other constants -// - -var RIGHT_HAND = 1; -var LEFT_HAND = 0; - -var ZERO_VEC = { - x: 0, - y: 0, - z: 0 -}; - -var NULL_ACTION_ID = "{00000000-0000-0000-000000000000}"; -var MSEC_PER_SEC = 1000.0; - -// these control how long an abandoned pointer line or action will hang around -var LIFETIME = 10; -var ACTION_TTL = 15; // seconds -var ACTION_TTL_REFRESH = 5; -var PICKS_PER_SECOND_PER_HAND = 5; -var MSECS_PER_SEC = 1000.0; -var GRABBABLE_PROPERTIES = [ - "position", - "rotation", - "gravity", - "ignoreForCollisions", - "collisionsWillMove", - "locked", - "name" -]; - -var GRABBABLE_DATA_KEY = "grabbableKey"; // shared with grab.js -var GRAB_USER_DATA_KEY = "grabKey"; // shared with grab.js - -var DEFAULT_GRABBABLE_DATA = { - grabbable: true, - invertSolidWhileHeld: false -}; - -//we've created various ways of visualizing looking for and moving distant objects -var USE_ENTITY_LINES_FOR_SEARCHING = false; -var USE_OVERLAY_LINES_FOR_SEARCHING = true; -var USE_PARTICLE_BEAM_FOR_SEARCHING = false; - -var USE_ENTITY_LINES_FOR_MOVING = true; -var USE_OVERLAY_LINES_FOR_MOVING = false; -var USE_PARTICLE_BEAM_FOR_MOVING = false; - -var USE_SPOTLIGHT = false; -var USE_POINTLIGHT = true; - -// states for the state machine -var STATE_OFF = 0; -var STATE_SEARCHING = 1; -var STATE_DISTANCE_HOLDING = 2; -var STATE_CONTINUE_DISTANCE_HOLDING = 3; -var STATE_NEAR_GRABBING = 4; -var STATE_CONTINUE_NEAR_GRABBING = 5; -var STATE_NEAR_TRIGGER = 6; -var STATE_CONTINUE_NEAR_TRIGGER = 7; -var STATE_FAR_TRIGGER = 8; -var STATE_CONTINUE_FAR_TRIGGER = 9; -var STATE_RELEASE = 10; -var STATE_EQUIP_SEARCHING = 11; -var STATE_EQUIP = 12 -var STATE_CONTINUE_EQUIP_BD = 13; // equip while bumper is still held down -var STATE_CONTINUE_EQUIP = 14; -var STATE_WAITING_FOR_BUMPER_RELEASE = 15; -var STATE_EQUIP_SPRING = 16; - - - -function stateToName(state) { - switch (state) { - case STATE_OFF: - return "off"; - case STATE_SEARCHING: - return "searching"; - case STATE_DISTANCE_HOLDING: - return "distance_holding"; - case STATE_CONTINUE_DISTANCE_HOLDING: - return "continue_distance_holding"; - case STATE_NEAR_GRABBING: - return "near_grabbing"; - case STATE_CONTINUE_NEAR_GRABBING: - return "continue_near_grabbing"; - case STATE_NEAR_TRIGGER: - return "near_trigger"; - case STATE_CONTINUE_NEAR_TRIGGER: - return "continue_near_trigger"; - case STATE_FAR_TRIGGER: - return "far_trigger"; - case STATE_CONTINUE_FAR_TRIGGER: - return "continue_far_trigger"; - case STATE_RELEASE: - return "release"; - case STATE_EQUIP_SEARCHING: - return "equip_searching"; - case STATE_EQUIP: - return "equip"; - case STATE_CONTINUE_EQUIP_BD: - return "continue_equip_bd"; - case STATE_CONTINUE_EQUIP: - return "continue_equip"; - case STATE_WAITING_FOR_BUMPER_RELEASE: - return "waiting_for_bumper_release"; - case STATE_EQUIP_SPRING: - return "state_equip_spring"; - } - - return "unknown"; -} - -function getTag() { - return "grab-" + MyAvatar.sessionUUID; -} - -function entityIsGrabbedByOther(entityID) { - // by convention, a distance grab sets the tag of its action to be grab-*owner-session-id*. - var actionIDs = Entities.getActionIDs(entityID); - for (var actionIndex = 0; actionIndex < actionIDs.length; actionIndex++) { - var actionID = actionIDs[actionIndex]; - var actionArguments = Entities.getActionArguments(entityID, actionID); - var tag = actionArguments["tag"]; - if (tag == getTag()) { - // we see a grab-*uuid* shaped tag, but it's our tag, so that's okay. - continue; - } - if (tag.slice(0, 5) == "grab-") { - // we see a grab-*uuid* shaped tag and it's not ours, so someone else is grabbing it. - return true; - } - } - return false; -} - -function getSpatialOffsetPosition(hand, spatialKey) { - var position = Vec3.ZERO; - - if (hand !== RIGHT_HAND && spatialKey.leftRelativePosition) { - position = spatialKey.leftRelativePosition; - } - if (hand === RIGHT_HAND && spatialKey.rightRelativePosition) { - position = spatialKey.rightRelativePosition; - } - if (spatialKey.relativePosition) { - position = spatialKey.relativePosition; - } - - return position; -} - -var yFlip = Quat.angleAxis(180, Vec3.UNIT_Y); - -function getSpatialOffsetRotation(hand, spatialKey) { - var rotation = Quat.IDENTITY; - - if (hand !== RIGHT_HAND && spatialKey.leftRelativeRotation) { - rotation = spatialKey.leftRelativeRotation; - } - if (hand === RIGHT_HAND && spatialKey.rightRelativeRotation) { - rotation = spatialKey.rightRelativeRotation; - } - if (spatialKey.relativeRotation) { - rotation = spatialKey.relativeRotation; - } - - // Flip left hand - if (hand !== RIGHT_HAND) { - rotation = Quat.multiply(yFlip, rotation); - } - - return rotation; -} - -function MyController(hand) { - this.hand = hand; - if (this.hand === RIGHT_HAND) { - this.getHandPosition = MyAvatar.getRightPalmPosition; - this.getHandRotation = MyAvatar.getRightPalmRotation; - } else { - this.getHandPosition = MyAvatar.getLeftPalmPosition; - this.getHandRotation = MyAvatar.getLeftPalmRotation; - } - - var SPATIAL_CONTROLLERS_PER_PALM = 2; - var TIP_CONTROLLER_OFFSET = 1; - this.palm = SPATIAL_CONTROLLERS_PER_PALM * hand; - this.tip = SPATIAL_CONTROLLERS_PER_PALM * hand + TIP_CONTROLLER_OFFSET; - - this.actionID = null; // action this script created... - this.grabbedEntity = null; // on this entity. - this.state = STATE_OFF; - this.pointer = null; // entity-id of line object - this.triggerValue = 0; // rolling average of trigger value - this.rawTriggerValue = 0; - this.rawBumperValue = 0; - - //for visualizations - this.overlayLine = null; - this.particleBeam = null; - - //for lights - this.spotlight = null; - this.pointlight = null; - - this.ignoreIK = false; - this.offsetPosition = Vec3.ZERO; - this.offsetRotation = Quat.IDENTITY; - - var _this = this; - - this.update = function() { - - this.updateSmoothedTrigger(); - - switch (this.state) { - case STATE_OFF: - this.off(); - this.touchTest(); - break; - case STATE_SEARCHING: - this.search(); - break; - case STATE_EQUIP_SEARCHING: - this.search(); - break; - case STATE_DISTANCE_HOLDING: - this.distanceHolding(); - break; - case STATE_CONTINUE_DISTANCE_HOLDING: - this.continueDistanceHolding(); - break; - case STATE_NEAR_GRABBING: - case STATE_EQUIP: - this.nearGrabbing(); - break; - case STATE_WAITING_FOR_BUMPER_RELEASE: - this.waitingForBumperRelease(); - break; - case STATE_EQUIP_SPRING: - this.pullTowardEquipPosition() - break; - case STATE_CONTINUE_NEAR_GRABBING: - case STATE_CONTINUE_EQUIP_BD: - case STATE_CONTINUE_EQUIP: - this.continueNearGrabbing(); - break; - case STATE_NEAR_TRIGGER: - this.nearTrigger(); - break; - case STATE_CONTINUE_NEAR_TRIGGER: - this.continueNearTrigger(); - break; - case STATE_FAR_TRIGGER: - this.farTrigger(); - break; - case STATE_CONTINUE_FAR_TRIGGER: - this.continueFarTrigger(); - break; - case STATE_RELEASE: - this.release(); - break; - } - }; - - this.setState = function(newState) { - if (WANT_DEBUG) { - print("STATE: " + stateToName(this.state) + " --> " + stateToName(newState) + ", hand: " + this.hand); - } - this.state = newState; - }; - - this.debugLine = function(closePoint, farPoint, color) { - Entities.addEntity({ - type: "Line", - name: "Grab Debug Entity", - dimensions: LINE_ENTITY_DIMENSIONS, - visible: true, - position: closePoint, - linePoints: [ZERO_VEC, farPoint], - color: color, - lifetime: 0.1, - collisionsWillMove: false, - ignoreForCollisions: true, - userData: JSON.stringify({ - grabbableKey: { - grabbable: false - } - }) - }); - }; - - this.lineOn = function(closePoint, farPoint, color) { - // draw a line - if (this.pointer === null) { - this.pointer = Entities.addEntity({ - type: "Line", - name: "grab pointer", - dimensions: LINE_ENTITY_DIMENSIONS, - visible: true, - position: closePoint, - linePoints: [ZERO_VEC, farPoint], - color: color, - lifetime: LIFETIME, - collisionsWillMove: false, - ignoreForCollisions: true, - userData: JSON.stringify({ - grabbableKey: { - grabbable: false - } - }) - }); - } else { - var age = Entities.getEntityProperties(this.pointer, "age").age; - this.pointer = Entities.editEntity(this.pointer, { - position: closePoint, - linePoints: [ZERO_VEC, farPoint], - color: color, - lifetime: age + LIFETIME - }); - } - }; - - this.overlayLineOn = function(closePoint, farPoint, color) { - if (this.overlayLine === null) { - var lineProperties = { - lineWidth: 5, - start: closePoint, - end: farPoint, - color: color, - ignoreRayIntersection: true, // always ignore this - visible: true, - alpha: 1 - }; - - this.overlayLine = Overlays.addOverlay("line3d", lineProperties); - - } else { - var success = Overlays.editOverlay(this.overlayLine, { - lineWidth: 5, - start: closePoint, - end: farPoint, - color: color, - visible: true, - ignoreRayIntersection: true, // always ignore this - alpha: 1 - }); - } - }; - - this.handleParticleBeam = function(position, orientation, color) { - - var rotation = Quat.angleAxis(0, { - x: 1, - y: 0, - z: 0 - }); - - var finalRotation = Quat.multiply(orientation, rotation); - var lifespan = LINE_LENGTH / 10; - var speed = 5; - var spread = 2; - if (this.particleBeam === null) { - this.createParticleBeam(position, finalRotation, color, speed, spread, lifespan); - } else { - this.updateParticleBeam(position, finalRotation, color, speed, spread, lifespan); - } - }; - - this.handleDistantParticleBeam = function(handPosition, objectPosition, color) { - - var handToObject = Vec3.subtract(objectPosition, handPosition); - var finalRotation = Quat.rotationBetween(Vec3.multiply(-1, Vec3.UP), handToObject); - - var distance = Vec3.distance(handPosition, objectPosition); - var speed = 5; - var spread = 0; - - var lifespan = distance / speed; - - - if (this.particleBeam === null) { - this.createParticleBeam(objectPosition, finalRotation, color, speed, spread, lifespan); - } else { - this.updateParticleBeam(objectPosition, finalRotation, color, speed, spread, lifespan); - } - }; - - this.createParticleBeam = function(position, orientation, color, speed, spread, lifespan) { - - var particleBeamProperties = { - type: "ParticleEffect", - isEmitting: true, - position: position, - visible: false, - "name": "Particle Beam", - "color": color, - "maxParticles": 2000, - "lifespan": lifespan, - "emitRate": 50, - "emitSpeed": speed, - "speedSpread": spread, - "emitOrientation": { - "x": -1, - "y": 0, - "z": 0, - "w": 1 - }, - "emitDimensions": { - "x": 0, - "y": 0, - "z": 0 - }, - "emitRadiusStart": 0.5, - "polarStart": 0, - "polarFinish": 0, - "azimuthStart": -3.1415927410125732, - "azimuthFinish": 3.1415927410125732, - "emitAcceleration": { - x: 0, - y: 0, - z: 0 - }, - "accelerationSpread": { - "x": 0, - "y": 0, - "z": 0 - }, - "particleRadius": 0.01, - "radiusSpread": 0, - // "radiusStart": 0.01, - // "radiusFinish": 0.01, - // "colorSpread": { - // "red": 0, - // "green": 0, - // "blue": 0 - // }, - // "colorStart": color, - // "colorFinish": color, - "alpha": 1, - "alphaSpread": 0, - "alphaStart": 1, - "alphaFinish": 1, - "additiveBlending": 0, - "textures": "https://hifi-content.s3.amazonaws.com/alan/dev/textures/grabsprite-3.png" - } - - this.particleBeam = Entities.addEntity(particleBeamProperties); - }; - - this.updateParticleBeam = function(position, orientation, color, speed, spread, lifespan) { - print('lifespan::' + lifespan); - Entities.editEntity(this.particleBeam, { - rotation: orientation, - position: position, - visible: true, - color: color, - emitSpeed: speed, - speedSpread:spread, - lifespan: lifespan - - }) - - }; - - this.evalLightWorldTransform = function(modelPos, modelRot) { - - var MODEL_LIGHT_POSITION = { - x: 0, - y: -0.3, - z: 0 - }; - - var MODEL_LIGHT_ROTATION = Quat.angleAxis(-90, { - x: 1, - y: 0, - z: 0 - }); - - return { - p: Vec3.sum(modelPos, Vec3.multiplyQbyV(modelRot, MODEL_LIGHT_POSITION)), - q: Quat.multiply(modelRot, MODEL_LIGHT_ROTATION) - }; - }; - - this.handleSpotlight = function(parentID, position) { - var LIFETIME = 100; - - var modelProperties = Entities.getEntityProperties(parentID, ['position', 'rotation']); - - var lightTransform = this.evalLightWorldTransform(modelProperties.position, modelProperties.rotation); - var lightProperties = { - type: "Light", - isSpotlight: true, - dimensions: { - x: 2, - y: 2, - z: 20 - }, - parentID: parentID, - color: { - red: 255, - green: 255, - blue: 255 - }, - intensity: 2, - exponent: 0.3, - cutoff: 20, - lifetime: LIFETIME, - position: lightTransform.p, - }; - - if (this.spotlight === null) { - this.spotlight = Entities.addEntity(lightProperties); - } else { - Entities.editEntity(this.spotlight, { - //without this, this light would maintain rotation with its parent - rotation: Quat.fromPitchYawRollDegrees(-90, 0, 0), - }) - } - }; - - this.handlePointLight = function(parentID, position) { - var LIFETIME = 100; - - var modelProperties = Entities.getEntityProperties(parentID, ['position', 'rotation']); - var lightTransform = this.evalLightWorldTransform(modelProperties.position, modelProperties.rotation); - - var lightProperties = { - type: "Light", - isSpotlight: false, - dimensions: { - x: 2, - y: 2, - z: 20 - }, - parentID: parentID, - color: { - red: 255, - green: 255, - blue: 255 - }, - intensity: 2, - exponent: 0.3, - cutoff: 20, - lifetime: LIFETIME, - position: lightTransform.p, - }; - - if (this.pointlight === null) { - this.pointlight = Entities.addEntity(lightProperties); - } else { - - } - }; - - this.lineOff = function() { - if (this.pointer !== null) { - Entities.deleteEntity(this.pointer); - } - this.pointer = null; - }; - - this.overlayLineOff = function() { - if (this.overlayLine !== null) { - Overlays.deleteOverlay(this.overlayLine); - } - this.overlayLine = null; - }; - - this.particleBeamOff = function() { - if (this.particleBeam !== null) { - Entities.editEntity(this.particleBeam, { - visible: false - }) - } - } - - this.turnLightsOff = function() { - if (this.spotlight !== null) { - Entities.deleteEntity(this.spotlight); - this.spotlight = null; - } - - if (this.pointlight !== null) { - Entities.deleteEntity(this.pointlight); - this.pointlight = null; - } - }; - - - this.turnOffVisualizations = function() { - if (USE_ENTITY_LINES_FOR_SEARCHING === true || USE_ENTITY_LINES_FOR_MOVING === true) { - this.lineOff(); - } - - if (USE_OVERLAY_LINES_FOR_SEARCHING === true || USE_OVERLAY_LINES_FOR_MOVING === true) { - this.overlayLineOff(); - } - - if (USE_PARTICLE_BEAM_FOR_SEARCHING === true || USE_PARTICLE_BEAM_FOR_MOVING === true) { - this.particleBeamOff(); - } - }; - - this.triggerPress = function(value) { - _this.rawTriggerValue = value; - }; - - this.bumperPress = function(value) { - _this.rawBumperValue = value; - }; - - this.updateSmoothedTrigger = function() { - var triggerValue = this.rawTriggerValue; - // smooth out trigger value - this.triggerValue = (this.triggerValue * TRIGGER_SMOOTH_RATIO) + - (triggerValue * (1.0 - TRIGGER_SMOOTH_RATIO)); - }; - - this.triggerSmoothedSqueezed = function() { - return this.triggerValue > TRIGGER_ON_VALUE; - }; - - this.triggerSmoothedReleased = function() { - return this.triggerValue < TRIGGER_OFF_VALUE; - }; - - this.triggerSqueezed = function() { - var triggerValue = this.rawTriggerValue; - return triggerValue > TRIGGER_ON_VALUE; - }; - - this.bumperSqueezed = function() { - return _this.rawBumperValue > BUMPER_ON_VALUE; - }; - - this.bumperReleased = function() { - return _this.rawBumperValue < BUMPER_ON_VALUE; - }; - - this.off = function() { - if (this.triggerSmoothedSqueezed()) { - this.lastPickTime = 0; - this.setState(STATE_SEARCHING); - return; - } - if (this.bumperSqueezed()) { - this.lastPickTime = 0; - this.setState(STATE_EQUIP_SEARCHING); - return; - } - }; - - this.search = function() { - this.grabbedEntity = null; - - if (this.state == STATE_SEARCHING ? this.triggerSmoothedReleased() : this.bumperReleased()) { - this.setState(STATE_RELEASE); - return; - } - - // the trigger is being pressed, do a ray test - var handPosition = this.getHandPosition(); - var distantPickRay = { - origin: handPosition, - direction: Quat.getUp(this.getHandRotation()), - length: PICK_MAX_DISTANCE - }; - - // don't pick 60x per second. - var pickRays = []; - var now = Date.now(); - if (now - this.lastPickTime > MSECS_PER_SEC / PICKS_PER_SECOND_PER_HAND) { - pickRays = [distantPickRay]; - this.lastPickTime = now; - } - - for (var index = 0; index < pickRays.length; ++index) { - var pickRay = pickRays[index]; - var directionNormalized = Vec3.normalize(pickRay.direction); - var directionBacked = Vec3.multiply(directionNormalized, PICK_BACKOFF_DISTANCE); - var pickRayBacked = { - origin: Vec3.subtract(pickRay.origin, directionBacked), - direction: pickRay.direction - }; - - if (WANT_DEBUG) { - this.debugLine(pickRayBacked.origin, Vec3.multiply(pickRayBacked.direction, NEAR_PICK_MAX_DISTANCE), { - red: 0, - green: 255, - blue: 0 - }) - } - - var intersection = Entities.findRayIntersection(pickRayBacked, true); - - if (intersection.intersects) { - // the ray is intersecting something we can move. - var intersectionDistance = Vec3.distance(pickRay.origin, intersection.intersection); - - var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, intersection.entityID, DEFAULT_GRABBABLE_DATA); - - if (intersection.properties.name == "Grab Debug Entity") { - continue; - } - - if (typeof grabbableData.grabbable !== 'undefined' && !grabbableData.grabbable) { - continue; - } - if (intersectionDistance > pickRay.length) { - // too far away for this ray. - continue; - } - if (intersectionDistance <= NEAR_PICK_MAX_DISTANCE) { - // the hand is very close to the intersected object. go into close-grabbing mode. - if (grabbableData.wantsTrigger) { - this.grabbedEntity = intersection.entityID; - this.setState(STATE_NEAR_TRIGGER); - return; - } else if (!intersection.properties.locked) { - this.grabbedEntity = intersection.entityID; - if (this.state == STATE_SEARCHING) { - this.setState(STATE_NEAR_GRABBING); - } else { // equipping - if (typeof grabbableData.spatialKey !== 'undefined') { - // TODO - // if we go to STATE_EQUIP_SPRING the item will be pulled to the hand and will then switch - // to STATE_EQUIP. This needs some debugging, so just jump straight to STATE_EQUIP here. - // this.setState(STATE_EQUIP_SPRING); - this.setState(STATE_EQUIP); - } else { - this.setState(STATE_EQUIP); - } - } - return; - } - } else if (!entityIsGrabbedByOther(intersection.entityID)) { - // don't allow two people to distance grab the same object - if (intersection.properties.collisionsWillMove && !intersection.properties.locked) { - // the hand is far from the intersected object. go into distance-holding mode - this.grabbedEntity = intersection.entityID; - if (typeof grabbableData.spatialKey !== 'undefined' && this.state == STATE_EQUIP_SEARCHING) { - // if a distance pick in equip mode hits something with a spatialKey, equip it - // TODO use STATE_EQUIP_SPRING here once it works right. - // this.setState(STATE_EQUIP_SPRING); - this.setState(STATE_EQUIP); - return; - } else if (this.state == STATE_SEARCHING) { - this.setState(STATE_DISTANCE_HOLDING); - return; - } - } else if (grabbableData.wantsTrigger) { - this.grabbedEntity = intersection.entityID; - this.setState(STATE_FAR_TRIGGER); - return; - } - } - } - } - - // forward ray test failed, try sphere test. - if (WANT_DEBUG) { - Entities.addEntity({ - type: "Sphere", - name: "Grab Debug Entity", - dimensions: { - x: GRAB_RADIUS, - y: GRAB_RADIUS, - z: GRAB_RADIUS - }, - visible: true, - position: handPosition, - color: { - red: 0, - green: 255, - blue: 0 - }, - lifetime: 0.1, - collisionsWillMove: false, - ignoreForCollisions: true, - userData: JSON.stringify({ - grabbableKey: { - grabbable: false - } - }) - }); - } - - var nearbyEntities = Entities.findEntities(handPosition, GRAB_RADIUS); - var minDistance = PICK_MAX_DISTANCE; - var i, props, distance, grabbableData; - this.grabbedEntity = null; - for (i = 0; i < nearbyEntities.length; i++) { - var grabbableDataForCandidate = - getEntityCustomData(GRABBABLE_DATA_KEY, nearbyEntities[i], DEFAULT_GRABBABLE_DATA); - if (typeof grabbableDataForCandidate.grabbable !== 'undefined' && !grabbableDataForCandidate.grabbable) { - continue; - } - var propsForCandidate = Entities.getEntityProperties(nearbyEntities[i], GRABBABLE_PROPERTIES); - - if (propsForCandidate.type == 'Unknown') { - continue; - } - - if (propsForCandidate.type == 'Light') { - continue; - } - - if (propsForCandidate.type == 'ParticleEffect') { - continue; - } - - if (propsForCandidate.type == 'PolyLine') { - continue; - } - - if (propsForCandidate.type == 'Zone') { - continue; - } - - if (propsForCandidate.locked && !grabbableDataForCandidate.wantsTrigger) { - continue; - } - - if (propsForCandidate.name == "Grab Debug Entity") { - continue; - } - - if (propsForCandidate.name == "grab pointer") { - continue; - } - - distance = Vec3.distance(propsForCandidate.position, handPosition); - if (distance < minDistance) { - this.grabbedEntity = nearbyEntities[i]; - minDistance = distance; - props = propsForCandidate; - grabbableData = grabbableDataForCandidate; - } - } - if (this.grabbedEntity !== null) { - if (grabbableData.wantsTrigger) { - this.setState(STATE_NEAR_TRIGGER); - return; - } else if (!props.locked && props.collisionsWillMove) { - this.setState(this.state == STATE_SEARCHING ? STATE_NEAR_GRABBING : STATE_EQUIP) - return; - } - } - - //search line visualizations - if (USE_ENTITY_LINES_FOR_SEARCHING === true) { - this.lineOn(distantPickRay.origin, Vec3.multiply(distantPickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); - } - - if (USE_OVERLAY_LINES_FOR_SEARCHING === true) { - this.overlayLineOn(distantPickRay.origin, Vec3.sum(distantPickRay.origin, Vec3.multiply(distantPickRay.direction, LINE_LENGTH)), NO_INTERSECT_COLOR); - } - - if (USE_PARTICLE_BEAM_FOR_SEARCHING === true) { - this.handleParticleBeam(distantPickRay.origin, this.getHandRotation(), NO_INTERSECT_COLOR); - } - - }; - - this.distanceHolding = function() { - var handControllerPosition = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandPosition : MyAvatar.leftHandPosition; - var controllerHandInput = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; - var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getPoseValue(controllerHandInput).rotation); - var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); - var now = Date.now(); - - // add the action and initialize some variables - this.currentObjectPosition = grabbedProperties.position; - this.currentObjectRotation = grabbedProperties.rotation; - this.currentObjectTime = now; - this.handRelativePreviousPosition = Vec3.subtract(handControllerPosition, MyAvatar.position); - this.handPreviousRotation = handRotation; - this.currentCameraOrientation = Camera.orientation; - - // compute a constant based on the initial conditions which we use below to exagerate hand motion onto the held object - this.radiusScalar = Math.log(Vec3.distance(this.currentObjectPosition, handControllerPosition) + 1.0); - if (this.radiusScalar < 1.0) { - this.radiusScalar = 1.0; - } - - this.actionID = NULL_ACTION_ID; - this.actionID = Entities.addAction("spring", this.grabbedEntity, { - targetPosition: this.currentObjectPosition, - linearTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME, - targetRotation: this.currentObjectRotation, - angularTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME, - tag: getTag(), - ttl: ACTION_TTL - }); - if (this.actionID === NULL_ACTION_ID) { - this.actionID = null; - } - this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC); - - if (this.actionID !== null) { - this.setState(STATE_CONTINUE_DISTANCE_HOLDING); - this.activateEntity(this.grabbedEntity, grabbedProperties); - if (this.hand === RIGHT_HAND) { - Entities.callEntityMethod(this.grabbedEntity, "setRightHand"); - } else { - Entities.callEntityMethod(this.grabbedEntity, "setLeftHand"); - } - Entities.callEntityMethod(this.grabbedEntity, "setHand", [this.hand]); - Entities.callEntityMethod(this.grabbedEntity, "startDistantGrab"); - } - - this.currentAvatarPosition = MyAvatar.position; - this.currentAvatarOrientation = MyAvatar.orientation; - - this.turnOffVisualizations(); - }; - - this.continueDistanceHolding = function() { - if (this.triggerSmoothedReleased()) { - this.setState(STATE_RELEASE); - Entities.callEntityMethod(this.grabbedEntity, "releaseGrab"); - return; - } - - var handPosition = this.getHandPosition(); - var handControllerPosition = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandPosition : MyAvatar.leftHandPosition; - var controllerHandInput = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; - var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getPoseValue(controllerHandInput).rotation); - var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); - var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); - - if (this.state == STATE_CONTINUE_DISTANCE_HOLDING && this.bumperSqueezed() && - typeof grabbableData.spatialKey !== 'undefined') { - var saveGrabbedID = this.grabbedEntity; - this.release(); - this.setState(STATE_EQUIP); - this.grabbedEntity = saveGrabbedID; - return; - } - - - // the action was set up on a previous call. update the targets. - var radius = Vec3.distance(this.currentObjectPosition, handControllerPosition) * - this.radiusScalar * DISTANCE_HOLDING_RADIUS_FACTOR; - if (radius < 1.0) { - radius = 1.0; - } - - // how far did avatar move this timestep? - var currentPosition = MyAvatar.position; - var avatarDeltaPosition = Vec3.subtract(currentPosition, this.currentAvatarPosition); - this.currentAvatarPosition = currentPosition; - - // How far did the avatar turn this timestep? - // Note: The following code is too long because we need a Quat.quatBetween() function - // that returns the minimum quaternion between two quaternions. - var currentOrientation = MyAvatar.orientation; - if (Quat.dot(currentOrientation, this.currentAvatarOrientation) < 0.0) { - var negativeCurrentOrientation = { - x: -currentOrientation.x, - y: -currentOrientation.y, - z: -currentOrientation.z, - w: -currentOrientation.w - }; - var avatarDeltaOrientation = Quat.multiply(negativeCurrentOrientation, Quat.inverse(this.currentAvatarOrientation)); - } else { - var avatarDeltaOrientation = Quat.multiply(currentOrientation, Quat.inverse(this.currentAvatarOrientation)); - } - var handToAvatar = Vec3.subtract(handControllerPosition, this.currentAvatarPosition); - var objectToAvatar = Vec3.subtract(this.currentObjectPosition, this.currentAvatarPosition); - var handMovementFromTurning = Vec3.subtract(Quat.multiply(avatarDeltaOrientation, handToAvatar), handToAvatar); - var objectMovementFromTurning = Vec3.subtract(Quat.multiply(avatarDeltaOrientation, objectToAvatar), objectToAvatar); - this.currentAvatarOrientation = currentOrientation; - - // how far did hand move this timestep? - var handMoved = Vec3.subtract(handToAvatar, this.handRelativePreviousPosition); - this.handRelativePreviousPosition = handToAvatar; - - // magnify the hand movement but not the change from avatar movement & rotation - handMoved = Vec3.subtract(handMoved, handMovementFromTurning); - var superHandMoved = Vec3.multiply(handMoved, radius); - - // Move the object by the magnified amount and then by amount from avatar movement & rotation - var newObjectPosition = Vec3.sum(this.currentObjectPosition, superHandMoved); - newObjectPosition = Vec3.sum(newObjectPosition, avatarDeltaPosition); - newObjectPosition = Vec3.sum(newObjectPosition, objectMovementFromTurning); - - var deltaPosition = Vec3.subtract(newObjectPosition, this.currentObjectPosition); // meters - var now = Date.now(); - var deltaTime = (now - this.currentObjectTime) / MSEC_PER_SEC; // convert to seconds - - this.currentObjectPosition = newObjectPosition; - this.currentObjectTime = now; - - // this doubles hand rotation - var handChange = Quat.multiply(Quat.slerp(this.handPreviousRotation, - handRotation, - DISTANCE_HOLDING_ROTATION_EXAGGERATION_FACTOR), - Quat.inverse(this.handPreviousRotation)); - this.handPreviousRotation = handRotation; - this.currentObjectRotation = Quat.multiply(handChange, this.currentObjectRotation); - - Entities.callEntityMethod(this.grabbedEntity, "continueDistantGrab"); - - // mix in head motion - if (MOVE_WITH_HEAD) { - var objDistance = Vec3.length(objectToAvatar); - var before = Vec3.multiplyQbyV(this.currentCameraOrientation, { - x: 0.0, - y: 0.0, - z: objDistance - }); - var after = Vec3.multiplyQbyV(Camera.orientation, { - x: 0.0, - y: 0.0, - z: objDistance - }); - var change = Vec3.subtract(before, after); - this.currentCameraOrientation = Camera.orientation; - this.currentObjectPosition = Vec3.sum(this.currentObjectPosition, change); - } - - - //visualizations - if (USE_ENTITY_LINES_FOR_MOVING === true) { - this.lineOn(handPosition, Vec3.subtract(grabbedProperties.position, handPosition), INTERSECT_COLOR); - } - if (USE_OVERLAY_LINES_FOR_MOVING === true) { - this.overlayLineOn(handPosition, grabbedProperties.position, INTERSECT_COLOR); - } - if (USE_PARTICLE_BEAM_FOR_MOVING === true) { - this.handleDistantParticleBeam(handPosition,grabbedProperties.position, INTERSECT_COLOR) - // this.handleDistantParticleBeam(handPosition, this.currentObjectPosition, INTERSECT_COLOR) - } - if (USE_POINTLIGHT === true) { - this.handlePointLight(this.grabbedEntity); - } - if (USE_SPOTLIGHT === true) { - this.handleSpotlight(this.grabbedEntity); - } - - Entities.updateAction(this.grabbedEntity, this.actionID, { - targetPosition: this.currentObjectPosition, - linearTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME, - targetRotation: this.currentObjectRotation, - angularTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME, - ttl: ACTION_TTL - }); - - this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC); - }; - - this.nearGrabbing = function() { - var now = Date.now(); - var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); - - if (this.state == STATE_NEAR_GRABBING && this.triggerSmoothedReleased()) { - this.setState(STATE_RELEASE); - Entities.callEntityMethod(this.grabbedEntity, "releaseGrab"); - return; - } - - this.turnOffVisualizations(); - - var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); - this.activateEntity(this.grabbedEntity, grabbedProperties); - if (grabbedProperties.collisionsWillMove && NEAR_GRABBING_KINEMATIC) { - Entities.editEntity(this.grabbedEntity, { - collisionsWillMove: false - }); - } - - var handRotation = this.getHandRotation(); - var handPosition = this.getHandPosition(); - - var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); - - if (this.state != STATE_NEAR_GRABBING && grabbableData.spatialKey) { - // if an object is "equipped" and has a spatialKey, use it. - this.ignoreIK = grabbableData.spatialKey.ignoreIK ? grabbableData.spatialKey.ignoreIK : false; - this.offsetPosition = getSpatialOffsetPosition(this.hand, grabbableData.spatialKey); - this.offsetRotation = getSpatialOffsetRotation(this.hand, grabbableData.spatialKey); - } else { - this.ignoreIK = false; - - var objectRotation = grabbedProperties.rotation; - this.offsetRotation = Quat.multiply(Quat.inverse(handRotation), objectRotation); - - var currentObjectPosition = grabbedProperties.position; - var offset = Vec3.subtract(currentObjectPosition, handPosition); - this.offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, this.offsetRotation)), offset); - } - - this.actionID = NULL_ACTION_ID; - this.actionID = Entities.addAction("hold", this.grabbedEntity, { - hand: this.hand === RIGHT_HAND ? "right" : "left", - timeScale: NEAR_GRABBING_ACTION_TIMEFRAME, - relativePosition: this.offsetPosition, - relativeRotation: this.offsetRotation, - ttl: ACTION_TTL, - kinematic: NEAR_GRABBING_KINEMATIC, - kinematicSetVelocity: true, - ignoreIK: this.ignoreIK - }); - if (this.actionID === NULL_ACTION_ID) { - this.actionID = null; - } else { - this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC); - if (this.state == STATE_NEAR_GRABBING) { - this.setState(STATE_CONTINUE_NEAR_GRABBING); - } else { - // equipping - Entities.callEntityMethod(this.grabbedEntity, "startEquip", [JSON.stringify(this.hand)]); - this.startHandGrasp(); - - this.setState(STATE_CONTINUE_EQUIP_BD); - } - - if (this.hand === RIGHT_HAND) { - Entities.callEntityMethod(this.grabbedEntity, "setRightHand"); - } else { - Entities.callEntityMethod(this.grabbedEntity, "setLeftHand"); - } - - Entities.callEntityMethod(this.grabbedEntity, "setHand", [this.hand]); - - Entities.callEntityMethod(this.grabbedEntity, "startNearGrab"); - - } - - this.currentHandControllerTipPosition = - (this.hand === RIGHT_HAND) ? MyAvatar.rightHandTipPosition : MyAvatar.leftHandTipPosition; - - this.currentObjectTime = Date.now(); - }; - - this.continueNearGrabbing = function() { - if (this.state == STATE_CONTINUE_NEAR_GRABBING && this.triggerSmoothedReleased()) { - this.setState(STATE_RELEASE); - Entities.callEntityMethod(this.grabbedEntity, "releaseGrab"); - return; - } - if (this.state == STATE_CONTINUE_EQUIP_BD && this.bumperReleased()) { - this.setState(STATE_CONTINUE_EQUIP); - return; - } - if (this.state == STATE_CONTINUE_EQUIP && this.bumperSqueezed()) { - this.setState(STATE_WAITING_FOR_BUMPER_RELEASE); - return; - } - if (this.state == STATE_CONTINUE_NEAR_GRABBING && this.bumperSqueezed()) { - this.setState(STATE_CONTINUE_EQUIP_BD); - Entities.callEntityMethod(this.grabbedEntity, "startEquip", [JSON.stringify(this.hand)]); - return; - } - - // Keep track of the fingertip velocity to impart when we release the object. - // Note that the idea of using a constant 'tip' velocity regardless of the - // object's actual held offset is an idea intended to make it easier to throw things: - // Because we might catch something or transfer it between hands without a good idea - // of it's actual offset, let's try imparting a velocity which is at a fixed radius - // from the palm. - - var handControllerPosition = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandPosition : MyAvatar.leftHandPosition; - var now = Date.now(); - - var deltaPosition = Vec3.subtract(handControllerPosition, this.currentHandControllerTipPosition); // meters - var deltaTime = (now - this.currentObjectTime) / MSEC_PER_SEC; // convert to seconds - - this.currentHandControllerTipPosition = handControllerPosition; - this.currentObjectTime = now; - Entities.callEntityMethod(this.grabbedEntity, "continueNearGrab"); - - if (this.state === STATE_CONTINUE_EQUIP_BD) { - Entities.callEntityMethod(this.grabbedEntity, "continueEquip"); - } - - if (this.actionTimeout - now < ACTION_TTL_REFRESH * MSEC_PER_SEC) { - // if less than a 5 seconds left, refresh the actions ttl - Entities.updateAction(this.grabbedEntity, this.actionID, { - hand: this.hand === RIGHT_HAND ? "right" : "left", - timeScale: NEAR_GRABBING_ACTION_TIMEFRAME, - relativePosition: this.offsetPosition, - relativeRotation: this.offsetRotation, - ttl: ACTION_TTL, - kinematic: NEAR_GRABBING_KINEMATIC, - kinematicSetVelocity: true, - ignoreIK: this.ignoreIK - }); - this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC); - } - }; - - this.waitingForBumperRelease = function() { - if (this.bumperReleased()) { - this.setState(STATE_RELEASE); - Entities.callEntityMethod(this.grabbedEntity, "releaseGrab"); - Entities.callEntityMethod(this.grabbedEntity, "unequip"); - this.endHandGrasp(); - - } - }; - - this.pullTowardEquipPosition = function() { - - this.turnOffVisualizations(); - - var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); - var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); - - // use a spring to pull the object to where it will be when equipped - var relativeRotation = getSpatialOffsetRotation(this.hand, grabbableData.spatialKey); - var relativePosition = getSpatialOffsetPosition(this.hand, grabbableData.spatialKey); - var ignoreIK = grabbableData.spatialKey.ignoreIK ? grabbableData.spatialKey.ignoreIK : false; - var handRotation = this.getHandRotation(); - var handPosition = this.getHandPosition(); - var targetRotation = Quat.multiply(handRotation, relativeRotation); - var offset = Vec3.multiplyQbyV(targetRotation, relativePosition); - var targetPosition = Vec3.sum(handPosition, offset); - - if (typeof this.equipSpringID === 'undefined' || - this.equipSpringID === null || - this.equipSpringID === NULL_ACTION_ID) { - this.equipSpringID = Entities.addAction("spring", this.grabbedEntity, { - targetPosition: targetPosition, - linearTimeScale: EQUIP_SPRING_TIMEFRAME, - targetRotation: targetRotation, - angularTimeScale: EQUIP_SPRING_TIMEFRAME, - ttl: ACTION_TTL, - ignoreIK: ignoreIK - }); - if (this.equipSpringID === NULL_ACTION_ID) { - this.equipSpringID = null; - this.setState(STATE_OFF); - return; - } - } else { - Entities.updateAction(this.grabbedEntity, this.equipSpringID, { - targetPosition: targetPosition, - linearTimeScale: EQUIP_SPRING_TIMEFRAME, - targetRotation: targetRotation, - angularTimeScale: EQUIP_SPRING_TIMEFRAME, - ttl: ACTION_TTL, - ignoreIK: ignoreIK - }); - } - - if (Vec3.distance(grabbedProperties.position, targetPosition) < EQUIP_SPRING_SHUTOFF_DISTANCE) { - Entities.deleteAction(this.grabbedEntity, this.equipSpringID); - this.equipSpringID = null; - this.setState(STATE_EQUIP); - } - }; - - this.nearTrigger = function() { - if (this.triggerSmoothedReleased()) { - this.setState(STATE_RELEASE); - Entities.callEntityMethod(this.grabbedEntity, "stopNearTrigger"); - return; - } - if (this.hand === RIGHT_HAND) { - Entities.callEntityMethod(this.grabbedEntity, "setRightHand"); - } else { - Entities.callEntityMethod(this.grabbedEntity, "setLeftHand"); - } - - Entities.callEntityMethod(this.grabbedEntity, "setHand", [this.hand]); - - Entities.callEntityMethod(this.grabbedEntity, "startNearTrigger"); - this.setState(STATE_CONTINUE_NEAR_TRIGGER); - }; - - this.farTrigger = function() { - if (this.triggerSmoothedReleased()) { - this.setState(STATE_RELEASE); - Entities.callEntityMethod(this.grabbedEntity, "stopFarTrigger"); - return; - } - - if (this.hand === RIGHT_HAND) { - Entities.callEntityMethod(this.grabbedEntity, "setRightHand"); - } else { - Entities.callEntityMethod(this.grabbedEntity, "setLeftHand"); - } - Entities.callEntityMethod(this.grabbedEntity, "setHand", [this.hand]); - Entities.callEntityMethod(this.grabbedEntity, "startFarTrigger"); - this.setState(STATE_CONTINUE_FAR_TRIGGER); - }; - - this.continueNearTrigger = function() { - if (this.triggerSmoothedReleased()) { - this.setState(STATE_RELEASE); - Entities.callEntityMethod(this.grabbedEntity, "stopNearTrigger"); - return; - } - - Entities.callEntityMethod(this.grabbedEntity, "continueNearTrigger"); - }; - - this.continueFarTrigger = function() { - if (this.triggerSmoothedReleased()) { - this.setState(STATE_RELEASE); - Entities.callEntityMethod(this.grabbedEntity, "stopNearTrigger"); - return; - } - - var handPosition = this.getHandPosition(); - var pickRay = { - origin: handPosition, - direction: Quat.getUp(this.getHandRotation()) - }; - - var now = Date.now(); - if (now - this.lastPickTime > MSECS_PER_SEC / PICKS_PER_SECOND_PER_HAND) { - var intersection = Entities.findRayIntersection(pickRay, true); - this.lastPickTime = now; - if (intersection.entityID != this.grabbedEntity) { - this.setState(STATE_RELEASE); - Entities.callEntityMethod(this.grabbedEntity, "stopFarTrigger"); - return; - } - } - - if (USE_ENTITY_LINES_FOR_MOVING === true) { - this.lineOn(pickRay.origin, Vec3.multiply(pickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); - } - - Entities.callEntityMethod(this.grabbedEntity, "continueFarTrigger"); - }; - - _this.allTouchedIDs = {}; - - this.touchTest = function() { - var maxDistance = 0.05; - var leftHandPosition = MyAvatar.getLeftPalmPosition(); - var rightHandPosition = MyAvatar.getRightPalmPosition(); - var leftEntities = Entities.findEntities(leftHandPosition, maxDistance); - var rightEntities = Entities.findEntities(rightHandPosition, maxDistance); - var ids = []; - - if (leftEntities.length !== 0) { - leftEntities.forEach(function(entity) { - ids.push(entity); - }); - - } - - if (rightEntities.length !== 0) { - rightEntities.forEach(function(entity) { - ids.push(entity); - }); - } - - ids.forEach(function(id) { - - var props = Entities.getEntityProperties(id, ["boundingBox", "name"]); - if (props.name === 'pointer') { - return; - } else { - var entityMinPoint = props.boundingBox.brn; - var entityMaxPoint = props.boundingBox.tfl; - var leftIsTouching = pointInExtents(leftHandPosition, entityMinPoint, entityMaxPoint); - var rightIsTouching = pointInExtents(rightHandPosition, entityMinPoint, entityMaxPoint); - - if ((leftIsTouching || rightIsTouching) && _this.allTouchedIDs[id] === undefined) { - // we haven't been touched before, but either right or left is touching us now - _this.allTouchedIDs[id] = true; - _this.startTouch(id); - } else if ((leftIsTouching || rightIsTouching) && _this.allTouchedIDs[id]) { - // we have been touched before and are still being touched - // continue touch - _this.continueTouch(id); - } else if (_this.allTouchedIDs[id]) { - delete _this.allTouchedIDs[id]; - _this.stopTouch(id); - - } else { - //we are in another state - return; - } - } - - }); - - }; - - this.startTouch = function(entityID) { - Entities.callEntityMethod(entityID, "startTouch"); - }; - - this.continueTouch = function(entityID) { - Entities.callEntityMethod(entityID, "continueTouch"); - }; - - this.stopTouch = function(entityID) { - Entities.callEntityMethod(entityID, "stopTouch"); - }; - - this.release = function() { - - this.turnLightsOff(); - this.turnOffVisualizations(); - - if (this.grabbedEntity !== null) { - if (this.actionID !== null) { - Entities.deleteAction(this.grabbedEntity, this.actionID); - } - } - - this.deactivateEntity(this.grabbedEntity); - - this.grabbedEntity = null; - this.actionID = null; - this.setState(STATE_OFF); - }; - - this.cleanup = function() { - this.release(); - this.endHandGrasp(); - Entities.deleteEntity(this.particleBeam); - Entities.deleteEntity(this.spotLight); - Entities.deleteEntity(this.pointLight); - }; - - this.activateEntity = function(entityID, grabbedProperties) { - var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, entityID, DEFAULT_GRABBABLE_DATA); - var invertSolidWhileHeld = grabbableData["invertSolidWhileHeld"]; - var data = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, {}); - data["activated"] = true; - data["avatarId"] = MyAvatar.sessionUUID; - data["refCount"] = data["refCount"] ? data["refCount"] + 1 : 1; - // zero gravity and set ignoreForCollisions in a way that lets us put them back, after all grabs are done - if (data["refCount"] == 1) { - data["gravity"] = grabbedProperties.gravity; - data["ignoreForCollisions"] = grabbedProperties.ignoreForCollisions; - data["collisionsWillMove"] = grabbedProperties.collisionsWillMove; - var whileHeldProperties = { - gravity: { - x: 0, - y: 0, - z: 0 - } - }; - if (invertSolidWhileHeld) { - whileHeldProperties["ignoreForCollisions"] = !grabbedProperties.ignoreForCollisions; - } - Entities.editEntity(entityID, whileHeldProperties); - } - - setEntityCustomData(GRAB_USER_DATA_KEY, entityID, data); - return data; - }; - - this.deactivateEntity = function(entityID) { - var data = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, {}); - if (data && data["refCount"]) { - data["refCount"] = data["refCount"] - 1; - if (data["refCount"] < 1) { - Entities.editEntity(entityID, { - gravity: data["gravity"], - ignoreForCollisions: data["ignoreForCollisions"], - collisionsWillMove: data["collisionsWillMove"] - }); - data = null; - } - } else { - data = null; - } - setEntityCustomData(GRAB_USER_DATA_KEY, entityID, data); - }; - - - //this is our handler, where we do the actual work of changing animation settings - this.graspHand = function(animationProperties) { - var result = {}; - //full alpha on overlay for this hand - //set grab to true - //set idle to false - //full alpha on the blend btw open and grab - if (_this.hand === RIGHT_HAND) { - result['rightHandOverlayAlpha'] = 1.0; - result['isRightHandGrab'] = true; - result['isRightHandIdle'] = false; - result['rightHandGrabBlend'] = 1.0; - } else if (_this.hand === LEFT_HAND) { - result['leftHandOverlayAlpha'] = 1.0; - result['isLeftHandGrab'] = true; - result['isLeftHandIdle'] = false; - result['leftHandGrabBlend'] = 1.0; - } - //return an object with our updated settings - return result; - }; - - this.graspHandler = null - - this.startHandGrasp = function() { - if (this.hand === RIGHT_HAND) { - this.graspHandler = MyAvatar.addAnimationStateHandler(this.graspHand, ['isRightHandGrab']); - } else if (this.hand === LEFT_HAND) { - this.graspHandler = MyAvatar.addAnimationStateHandler(this.graspHand, ['isLeftHandGrab']); - } - }; - - this.endHandGrasp = function() { - // Tell the animation system we don't need any more callbacks. - MyAvatar.removeAnimationStateHandler(this.graspHandler); - }; - -}; - -var rightController = new MyController(RIGHT_HAND); -var leftController = new MyController(LEFT_HAND); - -//preload the particle beams so that they are full length when you start searching -if (USE_PARTICLE_BEAM_FOR_SEARCHING === true || USE_PARTICLE_BEAM_FOR_MOVING === true) { - rightController.createParticleBeam(); - leftController.createParticleBeam(); -} - -var MAPPING_NAME = "com.highfidelity.handControllerGrab"; - -var mapping = Controller.newMapping(MAPPING_NAME); -mapping.from([Controller.Standard.RT]).peek().to(rightController.triggerPress); -mapping.from([Controller.Standard.LT]).peek().to(leftController.triggerPress); - -mapping.from([Controller.Standard.RB]).peek().to(rightController.bumperPress); -mapping.from([Controller.Standard.LB]).peek().to(leftController.bumperPress); - -Controller.enableMapping(MAPPING_NAME); - -//the section below allows the grab script to listen for messages that disable either one or both hands. useful for two handed items -var handToDisable = 'none'; - -function update() { - if (handToDisable !== LEFT_HAND && handToDisable !== 'both') { - leftController.update(); - } - if (handToDisable !== RIGHT_HAND && handToDisable !== 'both') { - rightController.update(); - } -} - -Messages.subscribe('Hifi-Hand-Disabler'); - -handleHandDisablerMessages = function(channel, message, sender) { - - if (sender === MyAvatar.sessionUUID) { - if (message === 'left') { - handToDisable = LEFT_HAND; - } - if (message === 'right') { - handToDisable = RIGHT_HAND; - } - if (message === 'both') { - handToDisable = 'both'; - } - if (message === 'none') { - handToDisable = 'none'; - } - } - -} - -Messages.messageReceived.connect(handleHandDisablerMessages); - -function cleanup() { - rightController.cleanup(); - leftController.cleanup(); - Controller.disableMapping(MAPPING_NAME); -} - -Script.scriptEnding.connect(cleanup); -Script.update.connect(update); \ No newline at end of file diff --git a/examples/controllers/handControllerGrab-spotlight.js b/examples/controllers/handControllerGrab-spotlight.js deleted file mode 100644 index ee6dcfa681..0000000000 --- a/examples/controllers/handControllerGrab-spotlight.js +++ /dev/null @@ -1,1656 +0,0 @@ -// handControllerGrab.js -// -// Created by Eric Levin on 9/2/15 -// Additions by James B. Pollack @imgntn on 9/24/2015 -// Additions By Seth Alves on 10/20/2015 -// Copyright 2015 High Fidelity, Inc. -// -// Grabs physically moveable entities with hydra-like controllers; it works for either near or far objects. -// Also supports touch and equipping objects. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -/*global print, MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, Audio, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt, pointInExtents, vec3equal, setEntityCustomData, getEntityCustomData */ - -Script.include("../libraries/utils.js"); - -// -// add lines where the hand ray picking is happening -// -var WANT_DEBUG = false; - -// -// these tune time-averaging and "on" value for analog trigger -// - -var TRIGGER_SMOOTH_RATIO = 0.1; // 0.0 disables smoothing of trigger value -var TRIGGER_ON_VALUE = 0.4; -var TRIGGER_OFF_VALUE = 0.15; - -var BUMPER_ON_VALUE = 0.5; - -// -// distant manipulation -// - -var DISTANCE_HOLDING_RADIUS_FACTOR = 3.5; // multiplied by distance between hand and object -var DISTANCE_HOLDING_ACTION_TIMEFRAME = 0.1; // how quickly objects move to their new position -var DISTANCE_HOLDING_ROTATION_EXAGGERATION_FACTOR = 2.0; // object rotates this much more than hand did -var MOVE_WITH_HEAD = true; // experimental head-controll of distantly held objects - -var NO_INTERSECT_COLOR = { - red: 10, - green: 10, - blue: 255 -}; // line color when pick misses -var INTERSECT_COLOR = { - red: 250, - green: 10, - blue: 10 -}; // line color when pick hits -var LINE_ENTITY_DIMENSIONS = { - x: 1000, - y: 1000, - z: 1000 -}; - -var LINE_LENGTH = 500; -var PICK_MAX_DISTANCE = 500; // max length of pick-ray - -// -// near grabbing -// - -var GRAB_RADIUS = 0.03; // if the ray misses but an object is this close, it will still be selected -var NEAR_GRABBING_ACTION_TIMEFRAME = 0.05; // how quickly objects move to their new position -var NEAR_GRABBING_VELOCITY_SMOOTH_RATIO = 1.0; // adjust time-averaging of held object's velocity. 1.0 to disable. -var NEAR_PICK_MAX_DISTANCE = 0.3; // max length of pick-ray for close grabbing to be selected -var RELEASE_VELOCITY_MULTIPLIER = 1.5; // affects throwing things -var PICK_BACKOFF_DISTANCE = 0.2; // helps when hand is intersecting the grabble object -var NEAR_GRABBING_KINEMATIC = true; // force objects to be kinematic when near-grabbed - -// -// equip -// - -var EQUIP_SPRING_SHUTOFF_DISTANCE = 0.05; -var EQUIP_SPRING_TIMEFRAME = 0.4; // how quickly objects move to their new position - -// -// other constants -// - -var RIGHT_HAND = 1; -var LEFT_HAND = 0; - -var ZERO_VEC = { - x: 0, - y: 0, - z: 0 -}; - -var NULL_ACTION_ID = "{00000000-0000-0000-000000000000}"; -var MSEC_PER_SEC = 1000.0; - -// these control how long an abandoned pointer line or action will hang around -var LIFETIME = 10; -var ACTION_TTL = 15; // seconds -var ACTION_TTL_REFRESH = 5; -var PICKS_PER_SECOND_PER_HAND = 5; -var MSECS_PER_SEC = 1000.0; -var GRABBABLE_PROPERTIES = [ - "position", - "rotation", - "gravity", - "ignoreForCollisions", - "collisionsWillMove", - "locked", - "name" -]; - -var GRABBABLE_DATA_KEY = "grabbableKey"; // shared with grab.js -var GRAB_USER_DATA_KEY = "grabKey"; // shared with grab.js - -var DEFAULT_GRABBABLE_DATA = { - grabbable: true, - invertSolidWhileHeld: false -}; - -//we've created various ways of visualizing looking for and moving distant objects -var USE_ENTITY_LINES_FOR_SEARCHING = false; -var USE_OVERLAY_LINES_FOR_SEARCHING = true; -var USE_PARTICLE_BEAM_FOR_SEARCHING = false; - -var USE_ENTITY_LINES_FOR_MOVING = true; -var USE_OVERLAY_LINES_FOR_MOVING = false; -var USE_PARTICLE_BEAM_FOR_MOVING = false; - -var USE_SPOTLIGHT = true; -var USE_POINTLIGHT = false; - -// states for the state machine -var STATE_OFF = 0; -var STATE_SEARCHING = 1; -var STATE_DISTANCE_HOLDING = 2; -var STATE_CONTINUE_DISTANCE_HOLDING = 3; -var STATE_NEAR_GRABBING = 4; -var STATE_CONTINUE_NEAR_GRABBING = 5; -var STATE_NEAR_TRIGGER = 6; -var STATE_CONTINUE_NEAR_TRIGGER = 7; -var STATE_FAR_TRIGGER = 8; -var STATE_CONTINUE_FAR_TRIGGER = 9; -var STATE_RELEASE = 10; -var STATE_EQUIP_SEARCHING = 11; -var STATE_EQUIP = 12 -var STATE_CONTINUE_EQUIP_BD = 13; // equip while bumper is still held down -var STATE_CONTINUE_EQUIP = 14; -var STATE_WAITING_FOR_BUMPER_RELEASE = 15; -var STATE_EQUIP_SPRING = 16; - - - -function stateToName(state) { - switch (state) { - case STATE_OFF: - return "off"; - case STATE_SEARCHING: - return "searching"; - case STATE_DISTANCE_HOLDING: - return "distance_holding"; - case STATE_CONTINUE_DISTANCE_HOLDING: - return "continue_distance_holding"; - case STATE_NEAR_GRABBING: - return "near_grabbing"; - case STATE_CONTINUE_NEAR_GRABBING: - return "continue_near_grabbing"; - case STATE_NEAR_TRIGGER: - return "near_trigger"; - case STATE_CONTINUE_NEAR_TRIGGER: - return "continue_near_trigger"; - case STATE_FAR_TRIGGER: - return "far_trigger"; - case STATE_CONTINUE_FAR_TRIGGER: - return "continue_far_trigger"; - case STATE_RELEASE: - return "release"; - case STATE_EQUIP_SEARCHING: - return "equip_searching"; - case STATE_EQUIP: - return "equip"; - case STATE_CONTINUE_EQUIP_BD: - return "continue_equip_bd"; - case STATE_CONTINUE_EQUIP: - return "continue_equip"; - case STATE_WAITING_FOR_BUMPER_RELEASE: - return "waiting_for_bumper_release"; - case STATE_EQUIP_SPRING: - return "state_equip_spring"; - } - - return "unknown"; -} - -function getTag() { - return "grab-" + MyAvatar.sessionUUID; -} - -function entityIsGrabbedByOther(entityID) { - // by convention, a distance grab sets the tag of its action to be grab-*owner-session-id*. - var actionIDs = Entities.getActionIDs(entityID); - for (var actionIndex = 0; actionIndex < actionIDs.length; actionIndex++) { - var actionID = actionIDs[actionIndex]; - var actionArguments = Entities.getActionArguments(entityID, actionID); - var tag = actionArguments["tag"]; - if (tag == getTag()) { - // we see a grab-*uuid* shaped tag, but it's our tag, so that's okay. - continue; - } - if (tag.slice(0, 5) == "grab-") { - // we see a grab-*uuid* shaped tag and it's not ours, so someone else is grabbing it. - return true; - } - } - return false; -} - -function getSpatialOffsetPosition(hand, spatialKey) { - var position = Vec3.ZERO; - - if (hand !== RIGHT_HAND && spatialKey.leftRelativePosition) { - position = spatialKey.leftRelativePosition; - } - if (hand === RIGHT_HAND && spatialKey.rightRelativePosition) { - position = spatialKey.rightRelativePosition; - } - if (spatialKey.relativePosition) { - position = spatialKey.relativePosition; - } - - return position; -} - -var yFlip = Quat.angleAxis(180, Vec3.UNIT_Y); - -function getSpatialOffsetRotation(hand, spatialKey) { - var rotation = Quat.IDENTITY; - - if (hand !== RIGHT_HAND && spatialKey.leftRelativeRotation) { - rotation = spatialKey.leftRelativeRotation; - } - if (hand === RIGHT_HAND && spatialKey.rightRelativeRotation) { - rotation = spatialKey.rightRelativeRotation; - } - if (spatialKey.relativeRotation) { - rotation = spatialKey.relativeRotation; - } - - // Flip left hand - if (hand !== RIGHT_HAND) { - rotation = Quat.multiply(yFlip, rotation); - } - - return rotation; -} - -function MyController(hand) { - this.hand = hand; - if (this.hand === RIGHT_HAND) { - this.getHandPosition = MyAvatar.getRightPalmPosition; - this.getHandRotation = MyAvatar.getRightPalmRotation; - } else { - this.getHandPosition = MyAvatar.getLeftPalmPosition; - this.getHandRotation = MyAvatar.getLeftPalmRotation; - } - - var SPATIAL_CONTROLLERS_PER_PALM = 2; - var TIP_CONTROLLER_OFFSET = 1; - this.palm = SPATIAL_CONTROLLERS_PER_PALM * hand; - this.tip = SPATIAL_CONTROLLERS_PER_PALM * hand + TIP_CONTROLLER_OFFSET; - - this.actionID = null; // action this script created... - this.grabbedEntity = null; // on this entity. - this.state = STATE_OFF; - this.pointer = null; // entity-id of line object - this.triggerValue = 0; // rolling average of trigger value - this.rawTriggerValue = 0; - this.rawBumperValue = 0; - - //for visualizations - this.overlayLine = null; - this.particleBeam = null; - - //for lights - this.spotlight = null; - this.pointlight = null; - - this.ignoreIK = false; - this.offsetPosition = Vec3.ZERO; - this.offsetRotation = Quat.IDENTITY; - - var _this = this; - - this.update = function() { - - this.updateSmoothedTrigger(); - - switch (this.state) { - case STATE_OFF: - this.off(); - this.touchTest(); - break; - case STATE_SEARCHING: - this.search(); - break; - case STATE_EQUIP_SEARCHING: - this.search(); - break; - case STATE_DISTANCE_HOLDING: - this.distanceHolding(); - break; - case STATE_CONTINUE_DISTANCE_HOLDING: - this.continueDistanceHolding(); - break; - case STATE_NEAR_GRABBING: - case STATE_EQUIP: - this.nearGrabbing(); - break; - case STATE_WAITING_FOR_BUMPER_RELEASE: - this.waitingForBumperRelease(); - break; - case STATE_EQUIP_SPRING: - this.pullTowardEquipPosition() - break; - case STATE_CONTINUE_NEAR_GRABBING: - case STATE_CONTINUE_EQUIP_BD: - case STATE_CONTINUE_EQUIP: - this.continueNearGrabbing(); - break; - case STATE_NEAR_TRIGGER: - this.nearTrigger(); - break; - case STATE_CONTINUE_NEAR_TRIGGER: - this.continueNearTrigger(); - break; - case STATE_FAR_TRIGGER: - this.farTrigger(); - break; - case STATE_CONTINUE_FAR_TRIGGER: - this.continueFarTrigger(); - break; - case STATE_RELEASE: - this.release(); - break; - } - }; - - this.setState = function(newState) { - if (WANT_DEBUG) { - print("STATE: " + stateToName(this.state) + " --> " + stateToName(newState) + ", hand: " + this.hand); - } - this.state = newState; - }; - - this.debugLine = function(closePoint, farPoint, color) { - Entities.addEntity({ - type: "Line", - name: "Grab Debug Entity", - dimensions: LINE_ENTITY_DIMENSIONS, - visible: true, - position: closePoint, - linePoints: [ZERO_VEC, farPoint], - color: color, - lifetime: 0.1, - collisionsWillMove: false, - ignoreForCollisions: true, - userData: JSON.stringify({ - grabbableKey: { - grabbable: false - } - }) - }); - }; - - this.lineOn = function(closePoint, farPoint, color) { - // draw a line - if (this.pointer === null) { - this.pointer = Entities.addEntity({ - type: "Line", - name: "grab pointer", - dimensions: LINE_ENTITY_DIMENSIONS, - visible: true, - position: closePoint, - linePoints: [ZERO_VEC, farPoint], - color: color, - lifetime: LIFETIME, - collisionsWillMove: false, - ignoreForCollisions: true, - userData: JSON.stringify({ - grabbableKey: { - grabbable: false - } - }) - }); - } else { - var age = Entities.getEntityProperties(this.pointer, "age").age; - this.pointer = Entities.editEntity(this.pointer, { - position: closePoint, - linePoints: [ZERO_VEC, farPoint], - color: color, - lifetime: age + LIFETIME - }); - } - }; - - this.overlayLineOn = function(closePoint, farPoint, color) { - if (this.overlayLine === null) { - var lineProperties = { - lineWidth: 5, - start: closePoint, - end: farPoint, - color: color, - ignoreRayIntersection: true, // always ignore this - visible: true, - alpha: 1 - }; - - this.overlayLine = Overlays.addOverlay("line3d", lineProperties); - - } else { - var success = Overlays.editOverlay(this.overlayLine, { - lineWidth: 5, - start: closePoint, - end: farPoint, - color: color, - visible: true, - ignoreRayIntersection: true, // always ignore this - alpha: 1 - }); - } - }; - - this.handleParticleBeam = function(position, orientation, color) { - - var rotation = Quat.angleAxis(0, { - x: 1, - y: 0, - z: 0 - }); - - var finalRotation = Quat.multiply(orientation, rotation); - var lifespan = LINE_LENGTH / 10; - var speed = 5; - var spread = 2; - if (this.particleBeam === null) { - this.createParticleBeam(position, finalRotation, color, speed, spread, lifespan); - } else { - this.updateParticleBeam(position, finalRotation, color, speed, spread, lifespan); - } - }; - - this.handleDistantParticleBeam = function(handPosition, objectPosition, color) { - - var handToObject = Vec3.subtract(objectPosition, handPosition); - var finalRotation = Quat.rotationBetween(Vec3.multiply(-1, Vec3.UP), handToObject); - - var distance = Vec3.distance(handPosition, objectPosition); - var speed = 5; - var spread = 0; - - var lifespan = distance / speed; - - - if (this.particleBeam === null) { - this.createParticleBeam(objectPosition, finalRotation, color, speed, spread, lifespan); - } else { - this.updateParticleBeam(objectPosition, finalRotation, color, speed, spread, lifespan); - } - }; - - this.createParticleBeam = function(position, orientation, color, speed, spread, lifespan) { - - var particleBeamProperties = { - type: "ParticleEffect", - isEmitting: true, - position: position, - visible: false, - "name": "Particle Beam", - "color": color, - "maxParticles": 2000, - "lifespan": lifespan, - "emitRate": 50, - "emitSpeed": speed, - "speedSpread": spread, - "emitOrientation": { - "x": -1, - "y": 0, - "z": 0, - "w": 1 - }, - "emitDimensions": { - "x": 0, - "y": 0, - "z": 0 - }, - "emitRadiusStart": 0.5, - "polarStart": 0, - "polarFinish": 0, - "azimuthStart": -3.1415927410125732, - "azimuthFinish": 3.1415927410125732, - "emitAcceleration": { - x: 0, - y: 0, - z: 0 - }, - "accelerationSpread": { - "x": 0, - "y": 0, - "z": 0 - }, - "particleRadius": 0.01, - "radiusSpread": 0, - // "radiusStart": 0.01, - // "radiusFinish": 0.01, - // "colorSpread": { - // "red": 0, - // "green": 0, - // "blue": 0 - // }, - // "colorStart": color, - // "colorFinish": color, - "alpha": 1, - "alphaSpread": 0, - "alphaStart": 1, - "alphaFinish": 1, - "additiveBlending": 0, - "textures": "https://hifi-content.s3.amazonaws.com/alan/dev/textures/grabsprite-3.png" - } - - this.particleBeam = Entities.addEntity(particleBeamProperties); - }; - - this.updateParticleBeam = function(position, orientation, color, speed, spread, lifespan) { - print('lifespan::' + lifespan); - Entities.editEntity(this.particleBeam, { - rotation: orientation, - position: position, - visible: true, - color: color, - emitSpeed: speed, - speedSpread:spread, - lifespan: lifespan - - }) - - }; - - this.evalLightWorldTransform = function(modelPos, modelRot) { - - var MODEL_LIGHT_POSITION = { - x: 0, - y: -0.3, - z: 0 - }; - - var MODEL_LIGHT_ROTATION = Quat.angleAxis(-90, { - x: 1, - y: 0, - z: 0 - }); - - return { - p: Vec3.sum(modelPos, Vec3.multiplyQbyV(modelRot, MODEL_LIGHT_POSITION)), - q: Quat.multiply(modelRot, MODEL_LIGHT_ROTATION) - }; - }; - - this.handleSpotlight = function(parentID, position) { - var LIFETIME = 100; - - var modelProperties = Entities.getEntityProperties(parentID, ['position', 'rotation']); - - var lightTransform = this.evalLightWorldTransform(modelProperties.position, modelProperties.rotation); - var lightProperties = { - type: "Light", - isSpotlight: true, - dimensions: { - x: 2, - y: 2, - z: 20 - }, - parentID: parentID, - color: { - red: 255, - green: 255, - blue: 255 - }, - intensity: 2, - exponent: 0.3, - cutoff: 20, - lifetime: LIFETIME, - position: lightTransform.p, - }; - - if (this.spotlight === null) { - this.spotlight = Entities.addEntity(lightProperties); - } else { - Entities.editEntity(this.spotlight, { - //without this, this light would maintain rotation with its parent - rotation: Quat.fromPitchYawRollDegrees(-90, 0, 0), - }) - } - }; - - this.handlePointLight = function(parentID, position) { - var LIFETIME = 100; - - var modelProperties = Entities.getEntityProperties(parentID, ['position', 'rotation']); - var lightTransform = this.evalLightWorldTransform(modelProperties.position, modelProperties.rotation); - - var lightProperties = { - type: "Light", - isSpotlight: false, - dimensions: { - x: 2, - y: 2, - z: 20 - }, - parentID: parentID, - color: { - red: 255, - green: 255, - blue: 255 - }, - intensity: 2, - exponent: 0.3, - cutoff: 20, - lifetime: LIFETIME, - position: lightTransform.p, - }; - - if (this.pointlight === null) { - this.pointlight = Entities.addEntity(lightProperties); - } else { - - } - }; - - this.lineOff = function() { - if (this.pointer !== null) { - Entities.deleteEntity(this.pointer); - } - this.pointer = null; - }; - - this.overlayLineOff = function() { - if (this.overlayLine !== null) { - Overlays.deleteOverlay(this.overlayLine); - } - this.overlayLine = null; - }; - - this.particleBeamOff = function() { - if (this.particleBeam !== null) { - Entities.editEntity(this.particleBeam, { - visible: false - }) - } - } - - this.turnLightsOff = function() { - if (this.spotlight !== null) { - Entities.deleteEntity(this.spotlight); - this.spotlight = null; - } - - if (this.pointlight !== null) { - Entities.deleteEntity(this.pointlight); - this.pointlight = null; - } - }; - - - this.turnOffVisualizations = function() { - if (USE_ENTITY_LINES_FOR_SEARCHING === true || USE_ENTITY_LINES_FOR_MOVING === true) { - this.lineOff(); - } - - if (USE_OVERLAY_LINES_FOR_SEARCHING === true || USE_OVERLAY_LINES_FOR_MOVING === true) { - this.overlayLineOff(); - } - - if (USE_PARTICLE_BEAM_FOR_SEARCHING === true || USE_PARTICLE_BEAM_FOR_MOVING === true) { - this.particleBeamOff(); - } - }; - - this.triggerPress = function(value) { - _this.rawTriggerValue = value; - }; - - this.bumperPress = function(value) { - _this.rawBumperValue = value; - }; - - this.updateSmoothedTrigger = function() { - var triggerValue = this.rawTriggerValue; - // smooth out trigger value - this.triggerValue = (this.triggerValue * TRIGGER_SMOOTH_RATIO) + - (triggerValue * (1.0 - TRIGGER_SMOOTH_RATIO)); - }; - - this.triggerSmoothedSqueezed = function() { - return this.triggerValue > TRIGGER_ON_VALUE; - }; - - this.triggerSmoothedReleased = function() { - return this.triggerValue < TRIGGER_OFF_VALUE; - }; - - this.triggerSqueezed = function() { - var triggerValue = this.rawTriggerValue; - return triggerValue > TRIGGER_ON_VALUE; - }; - - this.bumperSqueezed = function() { - return _this.rawBumperValue > BUMPER_ON_VALUE; - }; - - this.bumperReleased = function() { - return _this.rawBumperValue < BUMPER_ON_VALUE; - }; - - this.off = function() { - if (this.triggerSmoothedSqueezed()) { - this.lastPickTime = 0; - this.setState(STATE_SEARCHING); - return; - } - if (this.bumperSqueezed()) { - this.lastPickTime = 0; - this.setState(STATE_EQUIP_SEARCHING); - return; - } - }; - - this.search = function() { - this.grabbedEntity = null; - - if (this.state == STATE_SEARCHING ? this.triggerSmoothedReleased() : this.bumperReleased()) { - this.setState(STATE_RELEASE); - return; - } - - // the trigger is being pressed, do a ray test - var handPosition = this.getHandPosition(); - var distantPickRay = { - origin: handPosition, - direction: Quat.getUp(this.getHandRotation()), - length: PICK_MAX_DISTANCE - }; - - // don't pick 60x per second. - var pickRays = []; - var now = Date.now(); - if (now - this.lastPickTime > MSECS_PER_SEC / PICKS_PER_SECOND_PER_HAND) { - pickRays = [distantPickRay]; - this.lastPickTime = now; - } - - for (var index = 0; index < pickRays.length; ++index) { - var pickRay = pickRays[index]; - var directionNormalized = Vec3.normalize(pickRay.direction); - var directionBacked = Vec3.multiply(directionNormalized, PICK_BACKOFF_DISTANCE); - var pickRayBacked = { - origin: Vec3.subtract(pickRay.origin, directionBacked), - direction: pickRay.direction - }; - - if (WANT_DEBUG) { - this.debugLine(pickRayBacked.origin, Vec3.multiply(pickRayBacked.direction, NEAR_PICK_MAX_DISTANCE), { - red: 0, - green: 255, - blue: 0 - }) - } - - var intersection = Entities.findRayIntersection(pickRayBacked, true); - - if (intersection.intersects) { - // the ray is intersecting something we can move. - var intersectionDistance = Vec3.distance(pickRay.origin, intersection.intersection); - - var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, intersection.entityID, DEFAULT_GRABBABLE_DATA); - - if (intersection.properties.name == "Grab Debug Entity") { - continue; - } - - if (typeof grabbableData.grabbable !== 'undefined' && !grabbableData.grabbable) { - continue; - } - if (intersectionDistance > pickRay.length) { - // too far away for this ray. - continue; - } - if (intersectionDistance <= NEAR_PICK_MAX_DISTANCE) { - // the hand is very close to the intersected object. go into close-grabbing mode. - if (grabbableData.wantsTrigger) { - this.grabbedEntity = intersection.entityID; - this.setState(STATE_NEAR_TRIGGER); - return; - } else if (!intersection.properties.locked) { - this.grabbedEntity = intersection.entityID; - if (this.state == STATE_SEARCHING) { - this.setState(STATE_NEAR_GRABBING); - } else { // equipping - if (typeof grabbableData.spatialKey !== 'undefined') { - // TODO - // if we go to STATE_EQUIP_SPRING the item will be pulled to the hand and will then switch - // to STATE_EQUIP. This needs some debugging, so just jump straight to STATE_EQUIP here. - // this.setState(STATE_EQUIP_SPRING); - this.setState(STATE_EQUIP); - } else { - this.setState(STATE_EQUIP); - } - } - return; - } - } else if (!entityIsGrabbedByOther(intersection.entityID)) { - // don't allow two people to distance grab the same object - if (intersection.properties.collisionsWillMove && !intersection.properties.locked) { - // the hand is far from the intersected object. go into distance-holding mode - this.grabbedEntity = intersection.entityID; - if (typeof grabbableData.spatialKey !== 'undefined' && this.state == STATE_EQUIP_SEARCHING) { - // if a distance pick in equip mode hits something with a spatialKey, equip it - // TODO use STATE_EQUIP_SPRING here once it works right. - // this.setState(STATE_EQUIP_SPRING); - this.setState(STATE_EQUIP); - return; - } else if (this.state == STATE_SEARCHING) { - this.setState(STATE_DISTANCE_HOLDING); - return; - } - } else if (grabbableData.wantsTrigger) { - this.grabbedEntity = intersection.entityID; - this.setState(STATE_FAR_TRIGGER); - return; - } - } - } - } - - // forward ray test failed, try sphere test. - if (WANT_DEBUG) { - Entities.addEntity({ - type: "Sphere", - name: "Grab Debug Entity", - dimensions: { - x: GRAB_RADIUS, - y: GRAB_RADIUS, - z: GRAB_RADIUS - }, - visible: true, - position: handPosition, - color: { - red: 0, - green: 255, - blue: 0 - }, - lifetime: 0.1, - collisionsWillMove: false, - ignoreForCollisions: true, - userData: JSON.stringify({ - grabbableKey: { - grabbable: false - } - }) - }); - } - - var nearbyEntities = Entities.findEntities(handPosition, GRAB_RADIUS); - var minDistance = PICK_MAX_DISTANCE; - var i, props, distance, grabbableData; - this.grabbedEntity = null; - for (i = 0; i < nearbyEntities.length; i++) { - var grabbableDataForCandidate = - getEntityCustomData(GRABBABLE_DATA_KEY, nearbyEntities[i], DEFAULT_GRABBABLE_DATA); - if (typeof grabbableDataForCandidate.grabbable !== 'undefined' && !grabbableDataForCandidate.grabbable) { - continue; - } - var propsForCandidate = Entities.getEntityProperties(nearbyEntities[i], GRABBABLE_PROPERTIES); - - if (propsForCandidate.type == 'Unknown') { - continue; - } - - if (propsForCandidate.type == 'Light') { - continue; - } - - if (propsForCandidate.type == 'ParticleEffect') { - continue; - } - - if (propsForCandidate.type == 'PolyLine') { - continue; - } - - if (propsForCandidate.type == 'Zone') { - continue; - } - - if (propsForCandidate.locked && !grabbableDataForCandidate.wantsTrigger) { - continue; - } - - if (propsForCandidate.name == "Grab Debug Entity") { - continue; - } - - if (propsForCandidate.name == "grab pointer") { - continue; - } - - distance = Vec3.distance(propsForCandidate.position, handPosition); - if (distance < minDistance) { - this.grabbedEntity = nearbyEntities[i]; - minDistance = distance; - props = propsForCandidate; - grabbableData = grabbableDataForCandidate; - } - } - if (this.grabbedEntity !== null) { - if (grabbableData.wantsTrigger) { - this.setState(STATE_NEAR_TRIGGER); - return; - } else if (!props.locked && props.collisionsWillMove) { - this.setState(this.state == STATE_SEARCHING ? STATE_NEAR_GRABBING : STATE_EQUIP) - return; - } - } - - //search line visualizations - if (USE_ENTITY_LINES_FOR_SEARCHING === true) { - this.lineOn(distantPickRay.origin, Vec3.multiply(distantPickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); - } - - if (USE_OVERLAY_LINES_FOR_SEARCHING === true) { - this.overlayLineOn(distantPickRay.origin, Vec3.sum(distantPickRay.origin, Vec3.multiply(distantPickRay.direction, LINE_LENGTH)), NO_INTERSECT_COLOR); - } - - if (USE_PARTICLE_BEAM_FOR_SEARCHING === true) { - this.handleParticleBeam(distantPickRay.origin, this.getHandRotation(), NO_INTERSECT_COLOR); - } - - }; - - this.distanceHolding = function() { - var handControllerPosition = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandPosition : MyAvatar.leftHandPosition; - var controllerHandInput = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; - var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getPoseValue(controllerHandInput).rotation); - var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); - var now = Date.now(); - - // add the action and initialize some variables - this.currentObjectPosition = grabbedProperties.position; - this.currentObjectRotation = grabbedProperties.rotation; - this.currentObjectTime = now; - this.handRelativePreviousPosition = Vec3.subtract(handControllerPosition, MyAvatar.position); - this.handPreviousRotation = handRotation; - this.currentCameraOrientation = Camera.orientation; - - // compute a constant based on the initial conditions which we use below to exagerate hand motion onto the held object - this.radiusScalar = Math.log(Vec3.distance(this.currentObjectPosition, handControllerPosition) + 1.0); - if (this.radiusScalar < 1.0) { - this.radiusScalar = 1.0; - } - - this.actionID = NULL_ACTION_ID; - this.actionID = Entities.addAction("spring", this.grabbedEntity, { - targetPosition: this.currentObjectPosition, - linearTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME, - targetRotation: this.currentObjectRotation, - angularTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME, - tag: getTag(), - ttl: ACTION_TTL - }); - if (this.actionID === NULL_ACTION_ID) { - this.actionID = null; - } - this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC); - - if (this.actionID !== null) { - this.setState(STATE_CONTINUE_DISTANCE_HOLDING); - this.activateEntity(this.grabbedEntity, grabbedProperties); - if (this.hand === RIGHT_HAND) { - Entities.callEntityMethod(this.grabbedEntity, "setRightHand"); - } else { - Entities.callEntityMethod(this.grabbedEntity, "setLeftHand"); - } - Entities.callEntityMethod(this.grabbedEntity, "setHand", [this.hand]); - Entities.callEntityMethod(this.grabbedEntity, "startDistantGrab"); - } - - this.currentAvatarPosition = MyAvatar.position; - this.currentAvatarOrientation = MyAvatar.orientation; - - this.turnOffVisualizations(); - }; - - this.continueDistanceHolding = function() { - if (this.triggerSmoothedReleased()) { - this.setState(STATE_RELEASE); - Entities.callEntityMethod(this.grabbedEntity, "releaseGrab"); - return; - } - - var handPosition = this.getHandPosition(); - var handControllerPosition = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandPosition : MyAvatar.leftHandPosition; - var controllerHandInput = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; - var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getPoseValue(controllerHandInput).rotation); - var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); - var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); - - if (this.state == STATE_CONTINUE_DISTANCE_HOLDING && this.bumperSqueezed() && - typeof grabbableData.spatialKey !== 'undefined') { - var saveGrabbedID = this.grabbedEntity; - this.release(); - this.setState(STATE_EQUIP); - this.grabbedEntity = saveGrabbedID; - return; - } - - - // the action was set up on a previous call. update the targets. - var radius = Vec3.distance(this.currentObjectPosition, handControllerPosition) * - this.radiusScalar * DISTANCE_HOLDING_RADIUS_FACTOR; - if (radius < 1.0) { - radius = 1.0; - } - - // how far did avatar move this timestep? - var currentPosition = MyAvatar.position; - var avatarDeltaPosition = Vec3.subtract(currentPosition, this.currentAvatarPosition); - this.currentAvatarPosition = currentPosition; - - // How far did the avatar turn this timestep? - // Note: The following code is too long because we need a Quat.quatBetween() function - // that returns the minimum quaternion between two quaternions. - var currentOrientation = MyAvatar.orientation; - if (Quat.dot(currentOrientation, this.currentAvatarOrientation) < 0.0) { - var negativeCurrentOrientation = { - x: -currentOrientation.x, - y: -currentOrientation.y, - z: -currentOrientation.z, - w: -currentOrientation.w - }; - var avatarDeltaOrientation = Quat.multiply(negativeCurrentOrientation, Quat.inverse(this.currentAvatarOrientation)); - } else { - var avatarDeltaOrientation = Quat.multiply(currentOrientation, Quat.inverse(this.currentAvatarOrientation)); - } - var handToAvatar = Vec3.subtract(handControllerPosition, this.currentAvatarPosition); - var objectToAvatar = Vec3.subtract(this.currentObjectPosition, this.currentAvatarPosition); - var handMovementFromTurning = Vec3.subtract(Quat.multiply(avatarDeltaOrientation, handToAvatar), handToAvatar); - var objectMovementFromTurning = Vec3.subtract(Quat.multiply(avatarDeltaOrientation, objectToAvatar), objectToAvatar); - this.currentAvatarOrientation = currentOrientation; - - // how far did hand move this timestep? - var handMoved = Vec3.subtract(handToAvatar, this.handRelativePreviousPosition); - this.handRelativePreviousPosition = handToAvatar; - - // magnify the hand movement but not the change from avatar movement & rotation - handMoved = Vec3.subtract(handMoved, handMovementFromTurning); - var superHandMoved = Vec3.multiply(handMoved, radius); - - // Move the object by the magnified amount and then by amount from avatar movement & rotation - var newObjectPosition = Vec3.sum(this.currentObjectPosition, superHandMoved); - newObjectPosition = Vec3.sum(newObjectPosition, avatarDeltaPosition); - newObjectPosition = Vec3.sum(newObjectPosition, objectMovementFromTurning); - - var deltaPosition = Vec3.subtract(newObjectPosition, this.currentObjectPosition); // meters - var now = Date.now(); - var deltaTime = (now - this.currentObjectTime) / MSEC_PER_SEC; // convert to seconds - - this.currentObjectPosition = newObjectPosition; - this.currentObjectTime = now; - - // this doubles hand rotation - var handChange = Quat.multiply(Quat.slerp(this.handPreviousRotation, - handRotation, - DISTANCE_HOLDING_ROTATION_EXAGGERATION_FACTOR), - Quat.inverse(this.handPreviousRotation)); - this.handPreviousRotation = handRotation; - this.currentObjectRotation = Quat.multiply(handChange, this.currentObjectRotation); - - Entities.callEntityMethod(this.grabbedEntity, "continueDistantGrab"); - - // mix in head motion - if (MOVE_WITH_HEAD) { - var objDistance = Vec3.length(objectToAvatar); - var before = Vec3.multiplyQbyV(this.currentCameraOrientation, { - x: 0.0, - y: 0.0, - z: objDistance - }); - var after = Vec3.multiplyQbyV(Camera.orientation, { - x: 0.0, - y: 0.0, - z: objDistance - }); - var change = Vec3.subtract(before, after); - this.currentCameraOrientation = Camera.orientation; - this.currentObjectPosition = Vec3.sum(this.currentObjectPosition, change); - } - - - //visualizations - if (USE_ENTITY_LINES_FOR_MOVING === true) { - this.lineOn(handPosition, Vec3.subtract(grabbedProperties.position, handPosition), INTERSECT_COLOR); - } - if (USE_OVERLAY_LINES_FOR_MOVING === true) { - this.overlayLineOn(handPosition, grabbedProperties.position, INTERSECT_COLOR); - } - if (USE_PARTICLE_BEAM_FOR_MOVING === true) { - this.handleDistantParticleBeam(handPosition,grabbedProperties.position, INTERSECT_COLOR) - // this.handleDistantParticleBeam(handPosition, this.currentObjectPosition, INTERSECT_COLOR) - } - if (USE_POINTLIGHT === true) { - this.handlePointLight(this.grabbedEntity); - } - if (USE_SPOTLIGHT === true) { - this.handleSpotlight(this.grabbedEntity); - } - - Entities.updateAction(this.grabbedEntity, this.actionID, { - targetPosition: this.currentObjectPosition, - linearTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME, - targetRotation: this.currentObjectRotation, - angularTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME, - ttl: ACTION_TTL - }); - - this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC); - }; - - this.nearGrabbing = function() { - var now = Date.now(); - var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); - - if (this.state == STATE_NEAR_GRABBING && this.triggerSmoothedReleased()) { - this.setState(STATE_RELEASE); - Entities.callEntityMethod(this.grabbedEntity, "releaseGrab"); - return; - } - - this.turnOffVisualizations(); - - var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); - this.activateEntity(this.grabbedEntity, grabbedProperties); - if (grabbedProperties.collisionsWillMove && NEAR_GRABBING_KINEMATIC) { - Entities.editEntity(this.grabbedEntity, { - collisionsWillMove: false - }); - } - - var handRotation = this.getHandRotation(); - var handPosition = this.getHandPosition(); - - var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); - - if (this.state != STATE_NEAR_GRABBING && grabbableData.spatialKey) { - // if an object is "equipped" and has a spatialKey, use it. - this.ignoreIK = grabbableData.spatialKey.ignoreIK ? grabbableData.spatialKey.ignoreIK : false; - this.offsetPosition = getSpatialOffsetPosition(this.hand, grabbableData.spatialKey); - this.offsetRotation = getSpatialOffsetRotation(this.hand, grabbableData.spatialKey); - } else { - this.ignoreIK = false; - - var objectRotation = grabbedProperties.rotation; - this.offsetRotation = Quat.multiply(Quat.inverse(handRotation), objectRotation); - - var currentObjectPosition = grabbedProperties.position; - var offset = Vec3.subtract(currentObjectPosition, handPosition); - this.offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, this.offsetRotation)), offset); - } - - this.actionID = NULL_ACTION_ID; - this.actionID = Entities.addAction("hold", this.grabbedEntity, { - hand: this.hand === RIGHT_HAND ? "right" : "left", - timeScale: NEAR_GRABBING_ACTION_TIMEFRAME, - relativePosition: this.offsetPosition, - relativeRotation: this.offsetRotation, - ttl: ACTION_TTL, - kinematic: NEAR_GRABBING_KINEMATIC, - kinematicSetVelocity: true, - ignoreIK: this.ignoreIK - }); - if (this.actionID === NULL_ACTION_ID) { - this.actionID = null; - } else { - this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC); - if (this.state == STATE_NEAR_GRABBING) { - this.setState(STATE_CONTINUE_NEAR_GRABBING); - } else { - // equipping - Entities.callEntityMethod(this.grabbedEntity, "startEquip", [JSON.stringify(this.hand)]); - this.startHandGrasp(); - - this.setState(STATE_CONTINUE_EQUIP_BD); - } - - if (this.hand === RIGHT_HAND) { - Entities.callEntityMethod(this.grabbedEntity, "setRightHand"); - } else { - Entities.callEntityMethod(this.grabbedEntity, "setLeftHand"); - } - - Entities.callEntityMethod(this.grabbedEntity, "setHand", [this.hand]); - - Entities.callEntityMethod(this.grabbedEntity, "startNearGrab"); - - } - - this.currentHandControllerTipPosition = - (this.hand === RIGHT_HAND) ? MyAvatar.rightHandTipPosition : MyAvatar.leftHandTipPosition; - - this.currentObjectTime = Date.now(); - }; - - this.continueNearGrabbing = function() { - if (this.state == STATE_CONTINUE_NEAR_GRABBING && this.triggerSmoothedReleased()) { - this.setState(STATE_RELEASE); - Entities.callEntityMethod(this.grabbedEntity, "releaseGrab"); - return; - } - if (this.state == STATE_CONTINUE_EQUIP_BD && this.bumperReleased()) { - this.setState(STATE_CONTINUE_EQUIP); - return; - } - if (this.state == STATE_CONTINUE_EQUIP && this.bumperSqueezed()) { - this.setState(STATE_WAITING_FOR_BUMPER_RELEASE); - return; - } - if (this.state == STATE_CONTINUE_NEAR_GRABBING && this.bumperSqueezed()) { - this.setState(STATE_CONTINUE_EQUIP_BD); - Entities.callEntityMethod(this.grabbedEntity, "startEquip", [JSON.stringify(this.hand)]); - return; - } - - // Keep track of the fingertip velocity to impart when we release the object. - // Note that the idea of using a constant 'tip' velocity regardless of the - // object's actual held offset is an idea intended to make it easier to throw things: - // Because we might catch something or transfer it between hands without a good idea - // of it's actual offset, let's try imparting a velocity which is at a fixed radius - // from the palm. - - var handControllerPosition = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandPosition : MyAvatar.leftHandPosition; - var now = Date.now(); - - var deltaPosition = Vec3.subtract(handControllerPosition, this.currentHandControllerTipPosition); // meters - var deltaTime = (now - this.currentObjectTime) / MSEC_PER_SEC; // convert to seconds - - this.currentHandControllerTipPosition = handControllerPosition; - this.currentObjectTime = now; - Entities.callEntityMethod(this.grabbedEntity, "continueNearGrab"); - - if (this.state === STATE_CONTINUE_EQUIP_BD) { - Entities.callEntityMethod(this.grabbedEntity, "continueEquip"); - } - - if (this.actionTimeout - now < ACTION_TTL_REFRESH * MSEC_PER_SEC) { - // if less than a 5 seconds left, refresh the actions ttl - Entities.updateAction(this.grabbedEntity, this.actionID, { - hand: this.hand === RIGHT_HAND ? "right" : "left", - timeScale: NEAR_GRABBING_ACTION_TIMEFRAME, - relativePosition: this.offsetPosition, - relativeRotation: this.offsetRotation, - ttl: ACTION_TTL, - kinematic: NEAR_GRABBING_KINEMATIC, - kinematicSetVelocity: true, - ignoreIK: this.ignoreIK - }); - this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC); - } - }; - - this.waitingForBumperRelease = function() { - if (this.bumperReleased()) { - this.setState(STATE_RELEASE); - Entities.callEntityMethod(this.grabbedEntity, "releaseGrab"); - Entities.callEntityMethod(this.grabbedEntity, "unequip"); - this.endHandGrasp(); - - } - }; - - this.pullTowardEquipPosition = function() { - - this.turnOffVisualizations(); - - var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); - var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); - - // use a spring to pull the object to where it will be when equipped - var relativeRotation = getSpatialOffsetRotation(this.hand, grabbableData.spatialKey); - var relativePosition = getSpatialOffsetPosition(this.hand, grabbableData.spatialKey); - var ignoreIK = grabbableData.spatialKey.ignoreIK ? grabbableData.spatialKey.ignoreIK : false; - var handRotation = this.getHandRotation(); - var handPosition = this.getHandPosition(); - var targetRotation = Quat.multiply(handRotation, relativeRotation); - var offset = Vec3.multiplyQbyV(targetRotation, relativePosition); - var targetPosition = Vec3.sum(handPosition, offset); - - if (typeof this.equipSpringID === 'undefined' || - this.equipSpringID === null || - this.equipSpringID === NULL_ACTION_ID) { - this.equipSpringID = Entities.addAction("spring", this.grabbedEntity, { - targetPosition: targetPosition, - linearTimeScale: EQUIP_SPRING_TIMEFRAME, - targetRotation: targetRotation, - angularTimeScale: EQUIP_SPRING_TIMEFRAME, - ttl: ACTION_TTL, - ignoreIK: ignoreIK - }); - if (this.equipSpringID === NULL_ACTION_ID) { - this.equipSpringID = null; - this.setState(STATE_OFF); - return; - } - } else { - Entities.updateAction(this.grabbedEntity, this.equipSpringID, { - targetPosition: targetPosition, - linearTimeScale: EQUIP_SPRING_TIMEFRAME, - targetRotation: targetRotation, - angularTimeScale: EQUIP_SPRING_TIMEFRAME, - ttl: ACTION_TTL, - ignoreIK: ignoreIK - }); - } - - if (Vec3.distance(grabbedProperties.position, targetPosition) < EQUIP_SPRING_SHUTOFF_DISTANCE) { - Entities.deleteAction(this.grabbedEntity, this.equipSpringID); - this.equipSpringID = null; - this.setState(STATE_EQUIP); - } - }; - - this.nearTrigger = function() { - if (this.triggerSmoothedReleased()) { - this.setState(STATE_RELEASE); - Entities.callEntityMethod(this.grabbedEntity, "stopNearTrigger"); - return; - } - if (this.hand === RIGHT_HAND) { - Entities.callEntityMethod(this.grabbedEntity, "setRightHand"); - } else { - Entities.callEntityMethod(this.grabbedEntity, "setLeftHand"); - } - - Entities.callEntityMethod(this.grabbedEntity, "setHand", [this.hand]); - - Entities.callEntityMethod(this.grabbedEntity, "startNearTrigger"); - this.setState(STATE_CONTINUE_NEAR_TRIGGER); - }; - - this.farTrigger = function() { - if (this.triggerSmoothedReleased()) { - this.setState(STATE_RELEASE); - Entities.callEntityMethod(this.grabbedEntity, "stopFarTrigger"); - return; - } - - if (this.hand === RIGHT_HAND) { - Entities.callEntityMethod(this.grabbedEntity, "setRightHand"); - } else { - Entities.callEntityMethod(this.grabbedEntity, "setLeftHand"); - } - Entities.callEntityMethod(this.grabbedEntity, "setHand", [this.hand]); - Entities.callEntityMethod(this.grabbedEntity, "startFarTrigger"); - this.setState(STATE_CONTINUE_FAR_TRIGGER); - }; - - this.continueNearTrigger = function() { - if (this.triggerSmoothedReleased()) { - this.setState(STATE_RELEASE); - Entities.callEntityMethod(this.grabbedEntity, "stopNearTrigger"); - return; - } - - Entities.callEntityMethod(this.grabbedEntity, "continueNearTrigger"); - }; - - this.continueFarTrigger = function() { - if (this.triggerSmoothedReleased()) { - this.setState(STATE_RELEASE); - Entities.callEntityMethod(this.grabbedEntity, "stopNearTrigger"); - return; - } - - var handPosition = this.getHandPosition(); - var pickRay = { - origin: handPosition, - direction: Quat.getUp(this.getHandRotation()) - }; - - var now = Date.now(); - if (now - this.lastPickTime > MSECS_PER_SEC / PICKS_PER_SECOND_PER_HAND) { - var intersection = Entities.findRayIntersection(pickRay, true); - this.lastPickTime = now; - if (intersection.entityID != this.grabbedEntity) { - this.setState(STATE_RELEASE); - Entities.callEntityMethod(this.grabbedEntity, "stopFarTrigger"); - return; - } - } - - if (USE_ENTITY_LINES_FOR_MOVING === true) { - this.lineOn(pickRay.origin, Vec3.multiply(pickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); - } - - Entities.callEntityMethod(this.grabbedEntity, "continueFarTrigger"); - }; - - _this.allTouchedIDs = {}; - - this.touchTest = function() { - var maxDistance = 0.05; - var leftHandPosition = MyAvatar.getLeftPalmPosition(); - var rightHandPosition = MyAvatar.getRightPalmPosition(); - var leftEntities = Entities.findEntities(leftHandPosition, maxDistance); - var rightEntities = Entities.findEntities(rightHandPosition, maxDistance); - var ids = []; - - if (leftEntities.length !== 0) { - leftEntities.forEach(function(entity) { - ids.push(entity); - }); - - } - - if (rightEntities.length !== 0) { - rightEntities.forEach(function(entity) { - ids.push(entity); - }); - } - - ids.forEach(function(id) { - - var props = Entities.getEntityProperties(id, ["boundingBox", "name"]); - if (props.name === 'pointer') { - return; - } else { - var entityMinPoint = props.boundingBox.brn; - var entityMaxPoint = props.boundingBox.tfl; - var leftIsTouching = pointInExtents(leftHandPosition, entityMinPoint, entityMaxPoint); - var rightIsTouching = pointInExtents(rightHandPosition, entityMinPoint, entityMaxPoint); - - if ((leftIsTouching || rightIsTouching) && _this.allTouchedIDs[id] === undefined) { - // we haven't been touched before, but either right or left is touching us now - _this.allTouchedIDs[id] = true; - _this.startTouch(id); - } else if ((leftIsTouching || rightIsTouching) && _this.allTouchedIDs[id]) { - // we have been touched before and are still being touched - // continue touch - _this.continueTouch(id); - } else if (_this.allTouchedIDs[id]) { - delete _this.allTouchedIDs[id]; - _this.stopTouch(id); - - } else { - //we are in another state - return; - } - } - - }); - - }; - - this.startTouch = function(entityID) { - Entities.callEntityMethod(entityID, "startTouch"); - }; - - this.continueTouch = function(entityID) { - Entities.callEntityMethod(entityID, "continueTouch"); - }; - - this.stopTouch = function(entityID) { - Entities.callEntityMethod(entityID, "stopTouch"); - }; - - this.release = function() { - - this.turnLightsOff(); - this.turnOffVisualizations(); - - if (this.grabbedEntity !== null) { - if (this.actionID !== null) { - Entities.deleteAction(this.grabbedEntity, this.actionID); - } - } - - this.deactivateEntity(this.grabbedEntity); - - this.grabbedEntity = null; - this.actionID = null; - this.setState(STATE_OFF); - }; - - this.cleanup = function() { - this.release(); - this.endHandGrasp(); - Entities.deleteEntity(this.particleBeam); - Entities.deleteEntity(this.spotLight); - Entities.deleteEntity(this.pointLight); - }; - - this.activateEntity = function(entityID, grabbedProperties) { - var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, entityID, DEFAULT_GRABBABLE_DATA); - var invertSolidWhileHeld = grabbableData["invertSolidWhileHeld"]; - var data = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, {}); - data["activated"] = true; - data["avatarId"] = MyAvatar.sessionUUID; - data["refCount"] = data["refCount"] ? data["refCount"] + 1 : 1; - // zero gravity and set ignoreForCollisions in a way that lets us put them back, after all grabs are done - if (data["refCount"] == 1) { - data["gravity"] = grabbedProperties.gravity; - data["ignoreForCollisions"] = grabbedProperties.ignoreForCollisions; - data["collisionsWillMove"] = grabbedProperties.collisionsWillMove; - var whileHeldProperties = { - gravity: { - x: 0, - y: 0, - z: 0 - } - }; - if (invertSolidWhileHeld) { - whileHeldProperties["ignoreForCollisions"] = !grabbedProperties.ignoreForCollisions; - } - Entities.editEntity(entityID, whileHeldProperties); - } - - setEntityCustomData(GRAB_USER_DATA_KEY, entityID, data); - return data; - }; - - this.deactivateEntity = function(entityID) { - var data = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, {}); - if (data && data["refCount"]) { - data["refCount"] = data["refCount"] - 1; - if (data["refCount"] < 1) { - Entities.editEntity(entityID, { - gravity: data["gravity"], - ignoreForCollisions: data["ignoreForCollisions"], - collisionsWillMove: data["collisionsWillMove"] - }); - data = null; - } - } else { - data = null; - } - setEntityCustomData(GRAB_USER_DATA_KEY, entityID, data); - }; - - - //this is our handler, where we do the actual work of changing animation settings - this.graspHand = function(animationProperties) { - var result = {}; - //full alpha on overlay for this hand - //set grab to true - //set idle to false - //full alpha on the blend btw open and grab - if (_this.hand === RIGHT_HAND) { - result['rightHandOverlayAlpha'] = 1.0; - result['isRightHandGrab'] = true; - result['isRightHandIdle'] = false; - result['rightHandGrabBlend'] = 1.0; - } else if (_this.hand === LEFT_HAND) { - result['leftHandOverlayAlpha'] = 1.0; - result['isLeftHandGrab'] = true; - result['isLeftHandIdle'] = false; - result['leftHandGrabBlend'] = 1.0; - } - //return an object with our updated settings - return result; - }; - - this.graspHandler = null - - this.startHandGrasp = function() { - if (this.hand === RIGHT_HAND) { - this.graspHandler = MyAvatar.addAnimationStateHandler(this.graspHand, ['isRightHandGrab']); - } else if (this.hand === LEFT_HAND) { - this.graspHandler = MyAvatar.addAnimationStateHandler(this.graspHand, ['isLeftHandGrab']); - } - }; - - this.endHandGrasp = function() { - // Tell the animation system we don't need any more callbacks. - MyAvatar.removeAnimationStateHandler(this.graspHandler); - }; - -}; - -var rightController = new MyController(RIGHT_HAND); -var leftController = new MyController(LEFT_HAND); - -//preload the particle beams so that they are full length when you start searching -if (USE_PARTICLE_BEAM_FOR_SEARCHING === true || USE_PARTICLE_BEAM_FOR_MOVING === true) { - rightController.createParticleBeam(); - leftController.createParticleBeam(); -} - -var MAPPING_NAME = "com.highfidelity.handControllerGrab"; - -var mapping = Controller.newMapping(MAPPING_NAME); -mapping.from([Controller.Standard.RT]).peek().to(rightController.triggerPress); -mapping.from([Controller.Standard.LT]).peek().to(leftController.triggerPress); - -mapping.from([Controller.Standard.RB]).peek().to(rightController.bumperPress); -mapping.from([Controller.Standard.LB]).peek().to(leftController.bumperPress); - -Controller.enableMapping(MAPPING_NAME); - -//the section below allows the grab script to listen for messages that disable either one or both hands. useful for two handed items -var handToDisable = 'none'; - -function update() { - if (handToDisable !== LEFT_HAND && handToDisable !== 'both') { - leftController.update(); - } - if (handToDisable !== RIGHT_HAND && handToDisable !== 'both') { - rightController.update(); - } -} - -Messages.subscribe('Hifi-Hand-Disabler'); - -handleHandDisablerMessages = function(channel, message, sender) { - - if (sender === MyAvatar.sessionUUID) { - if (message === 'left') { - handToDisable = LEFT_HAND; - } - if (message === 'right') { - handToDisable = RIGHT_HAND; - } - if (message === 'both') { - handToDisable = 'both'; - } - if (message === 'none') { - handToDisable = 'none'; - } - } - -} - -Messages.messageReceived.connect(handleHandDisablerMessages); - -function cleanup() { - rightController.cleanup(); - leftController.cleanup(); - Controller.disableMapping(MAPPING_NAME); -} - -Script.scriptEnding.connect(cleanup); -Script.update.connect(update); \ No newline at end of file diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index b6f1a82c3f..bfe51927d0 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -118,12 +118,12 @@ var DEFAULT_GRABBABLE_DATA = { //we've created various ways of visualizing looking for and moving distant objects var USE_ENTITY_LINES_FOR_SEARCHING = false; -var USE_OVERLAY_LINES_FOR_SEARCHING = true; -var USE_PARTICLE_BEAM_FOR_SEARCHING = false; +var USE_OVERLAY_LINES_FOR_SEARCHING = false; +var USE_PARTICLE_BEAM_FOR_SEARCHING = true; -var USE_ENTITY_LINES_FOR_MOVING = true; +var USE_ENTITY_LINES_FOR_MOVING = false; var USE_OVERLAY_LINES_FOR_MOVING = false; -var USE_PARTICLE_BEAM_FOR_MOVING = false; +var USE_PARTICLE_BEAM_FOR_MOVING = true; var USE_SPOTLIGHT = false; var USE_POINTLIGHT = false; From 6fc98bd5773d7aa93c9b29692665e59a9c577a36 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Tue, 22 Dec 2015 12:03:03 -0800 Subject: [PATCH 84/94] fixed ravestick, merged --- libraries/entities-renderer/src/paintStroke.slf | 2 ++ libraries/entities-renderer/src/paintStroke.slv | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/libraries/entities-renderer/src/paintStroke.slf b/libraries/entities-renderer/src/paintStroke.slf index f6beca811b..fc659d5928 100644 --- a/libraries/entities-renderer/src/paintStroke.slf +++ b/libraries/entities-renderer/src/paintStroke.slf @@ -21,6 +21,7 @@ uniform sampler2D originalTexture; // the interpolated normal in vec3 interpolatedNormal; in vec2 varTexcoord; +in vec4 varColor; struct PolyLineUniforms { vec3 color; @@ -35,6 +36,7 @@ void main(void) { vec4 texel = texture(originalTexture, varTexcoord); int frontCondition = 1 -int(gl_FrontFacing) * 2; + vec3 color = varColor.rgb; packDeferredFragmentTranslucent( interpolatedNormal * frontCondition, texel.a, diff --git a/libraries/entities-renderer/src/paintStroke.slv b/libraries/entities-renderer/src/paintStroke.slv index 43eac49e3d..769b87f2a9 100644 --- a/libraries/entities-renderer/src/paintStroke.slv +++ b/libraries/entities-renderer/src/paintStroke.slv @@ -23,9 +23,13 @@ out vec3 interpolatedNormal; //the diffuse texture out vec2 varTexcoord; +out vec4 varColor; + void main(void) { varTexcoord = inTexCoord0.st; + + // pass along the diffuse color varColor = colorToLinearRGBA(inColor); From 51630734dfac1a9c992df67e6d83b9a2cde42a1e Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Tue, 22 Dec 2015 12:07:02 -0800 Subject: [PATCH 85/94] Update masterReset.js add initial zeroes --- unpublishedScripts/masterReset.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/unpublishedScripts/masterReset.js b/unpublishedScripts/masterReset.js index 3d0773ca10..9afbd0a68b 100644 --- a/unpublishedScripts/masterReset.js +++ b/unpublishedScripts/masterReset.js @@ -218,26 +218,26 @@ MasterReset = function() { lifespan: 1, emitRate: 1000, emitOrientation: forwardQuat, - emitSpeed: .2, + emitSpeed: 0.2, speedSpread: 0.0, polarStart: 0, - polarFinish: .0, - azimuthStart: .1, - azimuthFinish: .01, + polarFinish: 0.0, + azimuthStart: 0.1, + azimuthFinish: 0.01, emitAcceleration: { x: 0, y: 0, z: 0 }, accelerationSpread: { - x: .00, - y: .00, - z: .00 + x: 0.00, + y: 0.00, + z: 0.00 }, radiusStart: 0.03, radiusFinish: 0.025, alpha: 0.7, - alphaSpread: .1, + alphaSpread: 0.1, alphaStart: 0.5, alphaFinish: 0.5, textures: "https://s3.amazonaws.com/hifi-public/eric/textures/particleSprites/beamParticle.png", @@ -1488,4 +1488,4 @@ MasterReset = function() { Script.scriptEnding.connect(cleanup); } -}; \ No newline at end of file +}; From 5e0a428dec71bc493736587b73784a1f6abae0d7 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Tue, 22 Dec 2015 12:07:57 -0800 Subject: [PATCH 86/94] Update hiddenEntityReset.js add zeroes --- unpublishedScripts/hiddenEntityReset.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/unpublishedScripts/hiddenEntityReset.js b/unpublishedScripts/hiddenEntityReset.js index 21971a18ad..94d815796b 100644 --- a/unpublishedScripts/hiddenEntityReset.js +++ b/unpublishedScripts/hiddenEntityReset.js @@ -239,26 +239,26 @@ lifespan: 1, emitRate: 1000, emitOrientation: forwardQuat, - emitSpeed: .2, + emitSpeed: 0.2, speedSpread: 0.0, polarStart: 0, - polarFinish: .0, - azimuthStart: .1, - azimuthFinish: .01, + polarFinish: 0.0, + azimuthStart: 0.1, + azimuthFinish: 0.01, emitAcceleration: { x: 0, y: 0, z: 0 }, accelerationSpread: { - x: .00, - y: .00, - z: .00 + x: 0.00, + y: 0.00, + z: 0.00 }, radiusStart: 0.03, radiusFinish: 0.025, alpha: 0.7, - alphaSpread: .1, + alphaSpread:0.1, alphaStart: 0.5, alphaFinish: 0.5, textures: "https://s3.amazonaws.com/hifi-public/eric/textures/particleSprites/beamParticle.png", @@ -1512,4 +1512,4 @@ }; // entity scripts always need to return a newly constructed object of our type return new ResetSwitch(); -}); \ No newline at end of file +}); From 52e6b849684f2f1766687c534061570faa63d364 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Tue, 22 Dec 2015 12:09:16 -0800 Subject: [PATCH 87/94] Update lightBall.js zeroes --- examples/flowArts/lightBall/lightBall.js | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/examples/flowArts/lightBall/lightBall.js b/examples/flowArts/lightBall/lightBall.js index df62b1b786..b2ed00f326 100644 --- a/examples/flowArts/lightBall/lightBall.js +++ b/examples/flowArts/lightBall/lightBall.js @@ -28,13 +28,13 @@ LightBall = function(spawnPosition) { name: "containerBall", position: Vec3.sum(spawnPosition, { x: 0, - y: .5, + y: 0.5, z: 0 }), dimensions: { - x: .1, - y: .1, - z: .1 + x: 0.1, + y: 0.1, + z: 0.1 }, color: { red: 15, @@ -52,7 +52,7 @@ LightBall = function(spawnPosition) { spatialKey: { relativePosition: { x: 0, - y: .1, + y: 0.1, z: 0 } }, @@ -99,7 +99,7 @@ LightBall = function(spawnPosition) { maxParticles: 100000, lifespan: 2, emitRate: 10000, - emitSpeed: .1, + emitSpeed: 0.1, lifetime: -1, speedSpread: 0.0, emitDimensions: { @@ -117,16 +117,16 @@ LightBall = function(spawnPosition) { z: 0 }, accelerationSpread: { - x: .00, - y: .00, - z: .00 + x: 0.00, + y: 0.00, + z: 0.00 }, particleRadius: 0.02, radiusSpread: 0, radiusStart: 0.03, radiusFinish: 0.0003, alpha: 0, - alphaSpread: .5, + alphaSpread: 0.5, alphaStart: 0, alphaFinish: 0.5, textures: "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png", @@ -142,4 +142,4 @@ LightBall = function(spawnPosition) { } this.cleanup = cleanup; -} \ No newline at end of file +} From f9070ee9894e166a590487eead143ac49a24944d Mon Sep 17 00:00:00 2001 From: samcake Date: Tue, 22 Dec 2015 12:41:31 -0800 Subject: [PATCH 88/94] Cleanup cruft in the shaders, glowIntensity and alphaThreshold not needed anymore --- .../src/RenderableProceduralItemShader.h | 6 ++---- .../render-utils/src/DebugDeferredBuffer.cpp | 2 +- .../render-utils/src/DeferredBufferWrite.slh | 17 +++++++---------- .../render-utils/src/DeferredLightingEffect.cpp | 4 ---- libraries/render-utils/src/MeshPartPayload.cpp | 10 +++------- libraries/render-utils/src/ModelRender.cpp | 15 ++------------- libraries/render-utils/src/ModelRender.h | 8 +++----- .../render-utils/src/RenderDeferredTask.cpp | 15 +-------------- libraries/render-utils/src/SkyFromSpace.slf | 3 ++- libraries/render-utils/src/simple.slf | 4 ++-- libraries/render-utils/src/simple_textured.slf | 2 +- .../src/simple_textured_emisive.slf | 2 +- libraries/shared/src/RenderArgs.h | 2 -- tests/gpu-test/src/unlit.slf | 2 +- 14 files changed, 26 insertions(+), 66 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableProceduralItemShader.h b/libraries/entities-renderer/src/RenderableProceduralItemShader.h index 8c9e7c77dd..a01a6b8571 100644 --- a/libraries/entities-renderer/src/RenderableProceduralItemShader.h +++ b/libraries/entities-renderer/src/RenderableProceduralItemShader.h @@ -23,8 +23,6 @@ layout(location = 0) out vec4 _fragColor0; layout(location = 1) out vec4 _fragColor1; layout(location = 2) out vec4 _fragColor2; -// the glow intensity -uniform float glowIntensity; // the alpha threshold uniform float alphaThreshold; uniform sampler2D normalFittingMap; @@ -318,8 +316,8 @@ const QString SHADER_TEMPLATE_V1 = SHADER_COMMON + R"SCRIBE( void main(void) { vec4 emissive = getProceduralColor(); - float alpha = glowIntensity * emissive.a; - if (alpha != glowIntensity) { + float alpha = emissive.a; + if (alpha != 1.0) { discard; } diff --git a/libraries/render-utils/src/DebugDeferredBuffer.cpp b/libraries/render-utils/src/DebugDeferredBuffer.cpp index 23150a1779..ca678770fb 100644 --- a/libraries/render-utils/src/DebugDeferredBuffer.cpp +++ b/libraries/render-utils/src/DebugDeferredBuffer.cpp @@ -56,7 +56,7 @@ static const std::string DEEFAULT_ROUGHNESS_SHADER { }; static const std::string DEEFAULT_NORMAL_SHADER { "vec4 getFragmentColor() {" - " return vec4(texture(normalMap, uv).xyz, 1.0);" + " return vec4(normalize(texture(normalMap, uv).xyz), 1.0);" " }" }; static const std::string DEEFAULT_DEPTH_SHADER { diff --git a/libraries/render-utils/src/DeferredBufferWrite.slh b/libraries/render-utils/src/DeferredBufferWrite.slh index d3a5ff1e31..1045c4afc7 100755 --- a/libraries/render-utils/src/DeferredBufferWrite.slh +++ b/libraries/render-utils/src/DeferredBufferWrite.slh @@ -15,12 +15,6 @@ layout(location = 0) out vec4 _fragColor0; layout(location = 1) out vec4 _fragColor1; layout(location = 2) out vec4 _fragColor2; -// the glow intensity -uniform float glowIntensity; - -// the alpha threshold -uniform float alphaThreshold; - uniform sampler2D normalFittingMap; vec3 bestFitNormal(vec3 normal) { @@ -39,15 +33,18 @@ vec3 bestFitNormal(vec3 normal) { return (cN * 0.5 + 0.5); } + +// the alpha threshold +const float alphaThreshold = 0.5; float evalOpaqueFinalAlpha(float alpha, float mapAlpha) { - return mix(alpha * glowIntensity, 1.0 - alpha * glowIntensity, step(mapAlpha, alphaThreshold)); + return mix(alpha, 1.0 - alpha, step(mapAlpha, alphaThreshold)); } const vec3 DEFAULT_SPECULAR = vec3(0.1); const float DEFAULT_SHININESS = 10; void packDeferredFragment(vec3 normal, float alpha, vec3 diffuse, vec3 specular, float shininess) { - if (alpha != glowIntensity) { + if (alpha != 1.0) { discard; } @@ -57,7 +54,7 @@ void packDeferredFragment(vec3 normal, float alpha, vec3 diffuse, vec3 specular, } void packDeferredFragmentLightmap(vec3 normal, float alpha, vec3 diffuse, vec3 specular, float shininess, vec3 emissive) { - if (alpha != glowIntensity) { + if (alpha != 1.0) { discard; } @@ -67,7 +64,7 @@ void packDeferredFragmentLightmap(vec3 normal, float alpha, vec3 diffuse, vec3 s } void packDeferredFragmentTranslucent(vec3 normal, float alpha, vec3 diffuse, vec3 specular, float shininess) { - if (alpha <= alphaThreshold) { + if (alpha <= 0.0) { discard; } diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index 4e4799612d..db0e47de5e 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -40,8 +40,6 @@ #include "point_light_frag.h" #include "spot_light_frag.h" -static const std::string glowIntensityShaderHandle = "glowIntensity"; - struct LightLocations { int radius; int ambientSphere; @@ -134,8 +132,6 @@ gpu::PipelinePointer DeferredLightingEffect::bindSimpleProgram(gpu::Batch& batch batch.setPipeline(pipeline); gpu::ShaderPointer program = (config.isEmissive()) ? _emissiveShader : _simpleShader; - int glowIntensity = program->getUniforms().findLocation("glowIntensity"); - batch._glUniform1f(glowIntensity, 1.0f); if (!config.isTextured()) { // If it is not textured, bind white texture and keep using textured pipeline diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 2fae09a158..414ad9cf97 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -199,8 +199,6 @@ void MeshPartPayload::render(RenderArgs* args) const { gpu::Batch& batch = *(args->_batch); auto mode = args->_renderMode; - auto alphaThreshold = args->_alphaThreshold; //translucent ? TRANSPARENT_ALPHA_THRESHOLD : OPAQUE_ALPHA_THRESHOLD; // FIX ME - model::MaterialKey drawMaterialKey; if (_drawMaterial) { drawMaterialKey = _drawMaterial->getKey(); @@ -217,7 +215,7 @@ void MeshPartPayload::render(RenderArgs* args) const { } ModelRender::Locations* locations = nullptr; - ModelRender::pickPrograms(batch, mode, translucentMesh, alphaThreshold, hasLightmap, hasTangents, hasSpecular, isSkinned, wireframe, + ModelRender::pickPrograms(batch, mode, translucentMesh, hasLightmap, hasTangents, hasSpecular, isSkinned, wireframe, args, locations); @@ -395,9 +393,7 @@ void ModelMeshPartPayload::render(RenderArgs* args) const { gpu::Batch& batch = *(args->_batch); auto mode = args->_renderMode; - - auto alphaThreshold = args->_alphaThreshold; //translucent ? TRANSPARENT_ALPHA_THRESHOLD : OPAQUE_ALPHA_THRESHOLD; // FIX ME - + const FBXGeometry& geometry = _model->_geometry->getFBXGeometry(); const std::vector>& networkMeshes = _model->_geometry->getMeshes(); @@ -467,7 +463,7 @@ void ModelMeshPartPayload::render(RenderArgs* args) const { } ModelRender::Locations* locations = nullptr; - ModelRender::pickPrograms(batch, mode, translucentMesh, alphaThreshold, hasLightmap, hasTangents, hasSpecular, isSkinned, wireframe, + ModelRender::pickPrograms(batch, mode, translucentMesh, hasLightmap, hasTangents, hasSpecular, isSkinned, wireframe, args, locations); if (!locations) { // the pipeline could not be found diff --git a/libraries/render-utils/src/ModelRender.cpp b/libraries/render-utils/src/ModelRender.cpp index 10c9d738d2..312d34e41b 100644 --- a/libraries/render-utils/src/ModelRender.cpp +++ b/libraries/render-utils/src/ModelRender.cpp @@ -228,10 +228,8 @@ void ModelRender::RenderPipelineLib::addRenderPipeline(ModelRender::RenderKey ke void ModelRender::RenderPipelineLib::initLocations(gpu::ShaderPointer& program, ModelRender::Locations& locations) { - locations.alphaThreshold = program->getUniforms().findLocation("alphaThreshold"); locations.texcoordMatrices = program->getUniforms().findLocation("texcoordMatrices"); locations.emissiveParams = program->getUniforms().findLocation("emissiveParams"); - locations.glowIntensity = program->getUniforms().findLocation("glowIntensity"); locations.normalFittingMapUnit = program->getTextures().findLocation("normalFittingMap"); locations.diffuseTextureUnit = program->getTextures().findLocation("diffuseMap"); locations.normalTextureUnit = program->getTextures().findLocation("normalMap"); @@ -244,14 +242,14 @@ void ModelRender::RenderPipelineLib::initLocations(gpu::ShaderPointer& program, } -void ModelRender::pickPrograms(gpu::Batch& batch, RenderArgs::RenderMode mode, bool translucent, float alphaThreshold, +void ModelRender::pickPrograms(gpu::Batch& batch, RenderArgs::RenderMode mode, bool translucent, bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned, bool isWireframe, RenderArgs* args, Locations*& locations) { PerformanceTimer perfTimer("Model::pickPrograms"); getRenderPipelineLib(); - RenderKey key(mode, translucent, alphaThreshold, hasLightmap, hasTangents, hasSpecular, isSkinned, isWireframe); + RenderKey key(mode, translucent, hasLightmap, hasTangents, hasSpecular, isSkinned, isWireframe); auto pipeline = _renderPipelineLib.find(key.getRaw()); if (pipeline == _renderPipelineLib.end()) { qDebug() << "No good, couldn't find a pipeline from the key ?" << key.getRaw(); @@ -266,15 +264,6 @@ void ModelRender::pickPrograms(gpu::Batch& batch, RenderArgs::RenderMode mode, b // Setup the One pipeline batch.setPipeline((*pipeline).second._pipeline); - if ((locations->alphaThreshold > -1) && (mode != RenderArgs::SHADOW_RENDER_MODE)) { - batch._glUniform1f(locations->alphaThreshold, alphaThreshold); - } - - if ((locations->glowIntensity > -1) && (mode != RenderArgs::SHADOW_RENDER_MODE)) { - const float DEFAULT_GLOW_INTENSITY = 1.0f; // FIXME - glow is removed - batch._glUniform1f(locations->glowIntensity, DEFAULT_GLOW_INTENSITY); - } - if ((locations->normalFittingMapUnit > -1)) { batch.setResourceTexture(locations->normalFittingMapUnit, DependencyManager::get()->getNormalFittingTexture()); diff --git a/libraries/render-utils/src/ModelRender.h b/libraries/render-utils/src/ModelRender.h index 39fe05378d..8331440fb0 100644 --- a/libraries/render-utils/src/ModelRender.h +++ b/libraries/render-utils/src/ModelRender.h @@ -29,21 +29,19 @@ public: class Locations { public: - int alphaThreshold; int texcoordMatrices; int diffuseTextureUnit; int normalTextureUnit; int specularTextureUnit; int emissiveTextureUnit; int emissiveParams; - int glowIntensity; int normalFittingMapUnit; int skinClusterBufferUnit; int materialBufferUnit; int lightBufferUnit; }; - static void pickPrograms(gpu::Batch& batch, RenderArgs::RenderMode mode, bool translucent, float alphaThreshold, + static void pickPrograms(gpu::Batch& batch, RenderArgs::RenderMode mode, bool translucent, bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned, bool isWireframe, RenderArgs* args, Locations*& locations); @@ -111,9 +109,9 @@ public: ) {} RenderKey(RenderArgs::RenderMode mode, - bool translucent, float alphaThreshold, bool hasLightmap, + bool translucent, bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned, bool isWireframe) : - RenderKey(((translucent && (alphaThreshold == 0.0f) && (mode != RenderArgs::SHADOW_RENDER_MODE)) ? IS_TRANSLUCENT : 0) + RenderKey(((translucent && (mode != RenderArgs::SHADOW_RENDER_MODE)) ? IS_TRANSLUCENT : 0) | (hasLightmap && (mode != RenderArgs::SHADOW_RENDER_MODE) ? HAS_LIGHTMAP : 0) // Lightmap, tangents and specular don't matter for depthOnly | (hasTangents && (mode != RenderArgs::SHADOW_RENDER_MODE) ? HAS_TANGENTS : 0) | (hasSpecular && (mode != RenderArgs::SHADOW_RENDER_MODE) ? HAS_SPECULAR : 0) diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 4dcda425a1..cf60fcfe37 100755 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -126,12 +126,6 @@ RenderDeferredTask::RenderDeferredTask() : Task() { _jobs.push_back(Job(new HitEffect::JobModel("HitEffect"))); _jobs.back().setEnabled(false); _drawHitEffectJobIndex = (int)_jobs.size() -1; - - // Give ourselves 3 frmaes of timer queries - _timerQueries.push_back(std::make_shared()); - _timerQueries.push_back(std::make_shared()); - _timerQueries.push_back(std::make_shared()); - _currentTimerQueryIndex = 0; } RenderDeferredTask::~RenderDeferredTask() { @@ -197,10 +191,6 @@ void DrawOpaqueDeferred::run(const SceneContextPointer& sceneContext, const Rend batch.setProjectionTransform(projMat); batch.setViewTransform(viewMat); - { - const float OPAQUE_ALPHA_THRESHOLD = 0.5f; - args->_alphaThreshold = OPAQUE_ALPHA_THRESHOLD; - } renderItems(sceneContext, renderContext, inItems, opaque.maxDrawn); args->_batch = nullptr; }); @@ -226,10 +216,7 @@ void DrawTransparentDeferred::run(const SceneContextPointer& sceneContext, const batch.setProjectionTransform(projMat); batch.setViewTransform(viewMat); - - const float TRANSPARENT_ALPHA_THRESHOLD = 0.0f; - args->_alphaThreshold = TRANSPARENT_ALPHA_THRESHOLD; - + renderItems(sceneContext, renderContext, inItems, transparent.maxDrawn); args->_batch = nullptr; }); diff --git a/libraries/render-utils/src/SkyFromSpace.slf b/libraries/render-utils/src/SkyFromSpace.slf index e3569a2914..42282fe08b 100755 --- a/libraries/render-utils/src/SkyFromSpace.slf +++ b/libraries/render-utils/src/SkyFromSpace.slf @@ -114,5 +114,6 @@ void main (void) vec3 finalColor = color + fMiePhase * secondaryColor; outFragColor.a = finalColor.b; - outFragColor.rgb = pow(finalColor.rgb, vec3(1.0/2.2)); + // outFragColor.rgb = pow(finalColor.rgb, vec3(1.0/2.2)); + outFragColor.rgb = finalColor.rgb; } diff --git a/libraries/render-utils/src/simple.slf b/libraries/render-utils/src/simple.slf index 8c93ee564e..b24e4f92ff 100644 --- a/libraries/render-utils/src/simple.slf +++ b/libraries/render-utils/src/simple.slf @@ -51,9 +51,9 @@ void main(void) { if (emissiveAmount > 0.0) { packDeferredFragmentLightmap( - normal, glowIntensity, diffuse, specular, shininess, specular); + normal, 1.0, diffuse, specular, shininess, specular); } else { packDeferredFragment( - normal, glowIntensity, diffuse, specular, shininess); + normal, 1.0, diffuse, specular, shininess); } } diff --git a/libraries/render-utils/src/simple_textured.slf b/libraries/render-utils/src/simple_textured.slf index d567b043f4..727c029bbe 100644 --- a/libraries/render-utils/src/simple_textured.slf +++ b/libraries/render-utils/src/simple_textured.slf @@ -29,7 +29,7 @@ void main(void) { packDeferredFragment( normalize(_normal.xyz), - glowIntensity * texel.a, + texel.a, _color.rgb * texel.rgb, DEFAULT_SPECULAR, DEFAULT_SHININESS); } \ No newline at end of file diff --git a/libraries/render-utils/src/simple_textured_emisive.slf b/libraries/render-utils/src/simple_textured_emisive.slf index 96119e98f0..1dd3d667a6 100644 --- a/libraries/render-utils/src/simple_textured_emisive.slf +++ b/libraries/render-utils/src/simple_textured_emisive.slf @@ -27,7 +27,7 @@ void main(void) { packDeferredFragmentLightmap( normalize(_normal), - glowIntensity * texel.a, + texel.a, _color.rgb, DEFAULT_SPECULAR, DEFAULT_SHININESS, texel.rgb); diff --git a/libraries/shared/src/RenderArgs.h b/libraries/shared/src/RenderArgs.h index 061e07c600..399b94bcef 100644 --- a/libraries/shared/src/RenderArgs.h +++ b/libraries/shared/src/RenderArgs.h @@ -115,8 +115,6 @@ public: std::shared_ptr _whiteTexture; RenderDetails _details; - - float _alphaThreshold = 0.5f; }; #endif // hifi_RenderArgs_h diff --git a/tests/gpu-test/src/unlit.slf b/tests/gpu-test/src/unlit.slf index 350190180a..77d28aa7e9 100644 --- a/tests/gpu-test/src/unlit.slf +++ b/tests/gpu-test/src/unlit.slf @@ -22,7 +22,7 @@ in vec3 _color; void main(void) { packDeferredFragment( normalize(_normal.xyz), - glowIntensity, + 1.0, _color.rgb, DEFAULT_SPECULAR, DEFAULT_SHININESS); } From 119f3e9895e46ff412bb83a9c03b06f245c6c84e Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 22 Dec 2015 13:35:16 -0800 Subject: [PATCH 89/94] make both hands and mouse work --- .../controllers/reticleHandRotationTest.js | 136 ++++++++++++++++-- 1 file changed, 126 insertions(+), 10 deletions(-) diff --git a/examples/controllers/reticleHandRotationTest.js b/examples/controllers/reticleHandRotationTest.js index 3d903217ef..9ca1aa661d 100644 --- a/examples/controllers/reticleHandRotationTest.js +++ b/examples/controllers/reticleHandRotationTest.js @@ -32,14 +32,14 @@ function moveReticleAbsolute(x, y) { // some debugging to see if position is jumping around on us... var distanceSinceLastMove = length(lastPos, globalPos); if (distanceSinceLastMove > EXPECTED_CHANGE) { - print("------------------ distanceSinceLastMove:" + distanceSinceLastMove + "----------------------------"); + debugPrint("------------------ distanceSinceLastMove:" + distanceSinceLastMove + "----------------------------"); } if (Math.abs(dX) > EXPECTED_CHANGE) { - print("surpressing unexpectedly large change dX:" + dX + "----------------------------"); + debugPrint("surpressing unexpectedly large change dX:" + dX + "----------------------------"); } if (Math.abs(dY) > EXPECTED_CHANGE) { - print("surpressing unexpectedly large change dY:" + dY + "----------------------------"); + debugPrint("surpressing unexpectedly large change dY:" + dY + "----------------------------"); } globalPos.x = x; @@ -59,6 +59,31 @@ mapping.enable(); var lastRotatedLeft = Vec3.UNIT_NEG_Y; var lastRotatedRight = Vec3.UNIT_NEG_Y; +function debugPrint(message) { + if (DEBUGGING) { + print(message); + } +} + +var MAX_WAKE_UP_DISTANCE = 0.005; +var MIN_WAKE_UP_DISTANCE = 0.001; +var INITIAL_WAKE_UP_DISTANCE = MIN_WAKE_UP_DISTANCE; +var INCREMENTAL_WAKE_UP_DISTANCE = 0.001; + +var MAX_SLEEP_DISTANCE = 0.0004; +var MIN_SLEEP_DISTANCE = 0.00002; +var INITIAL_SLEEP_DISTANCE = MIN_SLEEP_DISTANCE; +var INCREMENTAL_SLEEP_DISTANCE = 0.00002; + +var leftAsleep = true; +var rightAsleep = true; + +var leftWakeUpDistance = INITIAL_WAKE_UP_DISTANCE; +var rightWakeUpDistance = INITIAL_WAKE_UP_DISTANCE; + +var leftSleepDistance = INITIAL_SLEEP_DISTANCE; +var rightSleepDistance = INITIAL_SLEEP_DISTANCE; + Script.update.connect(function(deltaTime) { var poseRight = Controller.getPoseValue(Controller.Standard.RightHand); @@ -71,19 +96,110 @@ Script.update.connect(function(deltaTime) { var rotatedRight = Vec3.multiplyQbyV(poseRight.rotation, Vec3.UNIT_NEG_Y); var rotatedLeft = Vec3.multiplyQbyV(poseLeft.rotation, Vec3.UNIT_NEG_Y); - // check to see if hand is on base station - if (Vec3.equal(rotatedLeft, Vec3.UNIT_NEG_Y)) { - rotatedLeft = rotatedRight; + var suppressRight = false; + var suppressLeft = false; + + // What I really want to do is to slowly increase the epsilon you have to move it + // to wake up, the longer you go without moving it + var leftDistance = Vec3.distance(rotatedLeft, lastRotatedLeft); + var rightDistance = Vec3.distance(rotatedRight, lastRotatedRight); + + // check to see if hand should wakeup or sleep + if (leftAsleep) { + if (leftDistance > leftWakeUpDistance) { + leftAsleep = false; + leftSleepDistance = INITIAL_SLEEP_DISTANCE; + leftWakeUpDistance = INITIAL_WAKE_UP_DISTANCE; + } else { + // grow the wake up distance to make it harder to wake up + leftWakeUpDistance = Math.min(leftWakeUpDistance + INCREMENTAL_WAKE_UP_DISTANCE, MAX_WAKE_UP_DISTANCE); + } + } else { + // we are awake, determine if we should fall asleep, if we haven't moved + // at least as much as our sleep distance then we sleep + if (leftDistance < leftSleepDistance) { + leftAsleep = true; + leftSleepDistance = INITIAL_SLEEP_DISTANCE; + leftWakeUpDistance = INITIAL_WAKE_UP_DISTANCE; + } else { + // if we moved more than the sleep amount, but we moved less than the max sleep + // amount, then increase our liklihood of sleep. + if (leftDistance < MAX_SLEEP_DISTANCE) { + print("growing sleep...."); + leftSleepDistance = Math.max(leftSleepDistance + INCREMENTAL_SLEEP_DISTANCE, MAX_SLEEP_DISTANCE); + } else { + // otherwise reset it to initial + leftSleepDistance = INITIAL_SLEEP_DISTANCE; + } + } } - if (Vec3.equal(rotatedRight, Vec3.UNIT_NEG_Y)) { - rotatedRight = rotatedLeft; + if (leftAsleep) { + suppressLeft = true; + debugPrint("suppressing left not moving enough"); } - // Keep track of last rotations, to potentially detect resting (but not on - // base station hands) in the future + // check to see if hand should wakeup or sleep + if (rightAsleep) { + if (rightDistance > rightWakeUpDistance) { + rightAsleep = false; + rightSleepDistance = INITIAL_SLEEP_DISTANCE; + rightWakeUpDistance = INITIAL_WAKE_UP_DISTANCE; + } else { + // grow the wake up distance to make it harder to wake up + rightWakeUpDistance = Math.min(rightWakeUpDistance + INCREMENTAL_WAKE_UP_DISTANCE, MAX_WAKE_UP_DISTANCE); + } + } else { + // we are awake, determine if we should fall asleep, if we haven't moved + // at least as much as our sleep distance then we sleep + if (rightDistance < rightSleepDistance) { + rightAsleep = true; + rightSleepDistance = INITIAL_SLEEP_DISTANCE; + rightWakeUpDistance = INITIAL_WAKE_UP_DISTANCE; + } else { + // if we moved more than the sleep amount, but we moved less than the max sleep + // amount, then increase our liklihood of sleep. + if (rightDistance < MAX_SLEEP_DISTANCE) { + print("growing sleep...."); + rightSleepDistance = Math.max(rightSleepDistance + INCREMENTAL_SLEEP_DISTANCE, MAX_SLEEP_DISTANCE); + } else { + // otherwise reset it to initial + rightSleepDistance = INITIAL_SLEEP_DISTANCE; + } + } + } + if (rightAsleep) { + suppressRight = true; + debugPrint("suppressing right not moving enough"); + } + + // check to see if hand is on base station + if (Vec3.equal(rotatedLeft, Vec3.UNIT_NEG_Y)) { + suppressLeft = true; + debugPrint("suppressing left on base station"); + } + if (Vec3.equal(rotatedRight, Vec3.UNIT_NEG_Y)) { + suppressRight = true; + debugPrint("suppressing right on base station"); + } + + // Keep track of last rotations, to detect resting (but not on base station hands) in the future lastRotatedLeft = rotatedLeft; lastRotatedRight = rotatedRight; + if (suppressLeft && suppressRight) { + debugPrint("both hands suppressed bail out early"); + return; + } + + if (suppressLeft) { + debugPrint("right only"); + rotatedLeft = rotatedRight; + } + if (suppressRight) { + debugPrint("left only"); + rotatedRight = rotatedLeft; + } + // Average the two hand positions, if either hand is on base station, the // other hand becomes the only used hand and the average is the hand in use var rotated = Vec3.multiply(Vec3.sum(rotatedRight,rotatedLeft), 0.5); From 9a3d72c7b61674f12558a9e796cc3662d6a35b03 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 22 Dec 2015 13:55:01 -0800 Subject: [PATCH 90/94] tweak sleep cycle slightly --- examples/controllers/reticleHandRotationTest.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/controllers/reticleHandRotationTest.js b/examples/controllers/reticleHandRotationTest.js index 9ca1aa661d..ece9283deb 100644 --- a/examples/controllers/reticleHandRotationTest.js +++ b/examples/controllers/reticleHandRotationTest.js @@ -71,9 +71,9 @@ var INITIAL_WAKE_UP_DISTANCE = MIN_WAKE_UP_DISTANCE; var INCREMENTAL_WAKE_UP_DISTANCE = 0.001; var MAX_SLEEP_DISTANCE = 0.0004; -var MIN_SLEEP_DISTANCE = 0.00002; +var MIN_SLEEP_DISTANCE = 0.00001; //0.00002; var INITIAL_SLEEP_DISTANCE = MIN_SLEEP_DISTANCE; -var INCREMENTAL_SLEEP_DISTANCE = 0.00002; +var INCREMENTAL_SLEEP_DISTANCE = 0.000002; // 0.00002; var leftAsleep = true; var rightAsleep = true; From 60f2314469db50a6ca06c9f1b3a163d5dbf5d28a Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Tue, 22 Dec 2015 14:38:05 -0800 Subject: [PATCH 91/94] remove debugging print --- examples/controllers/handControllerGrab.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index bfe51927d0..61e7e1d16a 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -529,7 +529,6 @@ function MyController(hand) { }; this.updateParticleBeam = function(position, orientation, color, speed, spread, lifespan) { - print('lifespan::' + lifespan); Entities.editEntity(this.particleBeam, { rotation: orientation, position: position, @@ -538,7 +537,6 @@ function MyController(hand) { emitSpeed: speed, speedSpread:spread, lifespan: lifespan - }) }; From edd11a670a8d48688a4b545d90793a3ee5bc2584 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Tue, 22 Dec 2015 14:38:53 -0800 Subject: [PATCH 92/94] minor adjustments to particles --- examples/controllers/handControllerGrab.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 61e7e1d16a..8f291b509a 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -506,8 +506,8 @@ function MyController(hand) { "y": 0, "z": 0 }, - "particleRadius": 0.01, - "radiusSpread": 0, + "particleRadius": 0.015, + "radiusSpread": 0.005, // "radiusStart": 0.01, // "radiusFinish": 0.01, // "colorSpread": { From df78f895a67f810357766082ff103c912c39110a Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Tue, 22 Dec 2015 14:53:18 -0800 Subject: [PATCH 93/94] Reorder RenderContext initializer to avoid -Wreorder --- libraries/render/src/render/Engine.cpp | 7 ++++--- libraries/render/src/render/Engine.h | 8 ++++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/libraries/render/src/render/Engine.cpp b/libraries/render/src/render/Engine.cpp index c38ceda034..907c836347 100644 --- a/libraries/render/src/render/Engine.cpp +++ b/libraries/render/src/render/Engine.cpp @@ -14,9 +14,10 @@ using namespace render; RenderContext::RenderContext(ItemsConfig items, Tone tone, int drawStatus, bool drawHitEffect, glm::vec4 deferredDebugSize, int deferredDebugMode) - : _args{ nullptr }, _items{ items }, _tone{ tone }, - _drawStatus{ drawStatus }, _drawHitEffect{ drawHitEffect }, - _deferredDebugSize{ deferredDebugSize }, _deferredDebugMode{ deferredDebugMode } {}; + : _deferredDebugMode{ deferredDebugMode }, _deferredDebugSize{ deferredDebugSize }, + _args{ nullptr }, + _drawStatus{ drawStatus }, _drawHitEffect{ drawHitEffect }, + _items{ items }, _tone{ tone } {} void RenderContext::setOptions(bool occlusion, bool fxaa, bool showOwned) { _occlusionStatus = occlusion; diff --git a/libraries/render/src/render/Engine.h b/libraries/render/src/render/Engine.h index 02f3bf3b2c..4192dd3ed9 100644 --- a/libraries/render/src/render/Engine.h +++ b/libraries/render/src/render/Engine.h @@ -91,15 +91,15 @@ public: void setOptions(bool occlusion, bool fxaa, bool showOwned); // Debugging - int _deferredDebugMode = -1; - glm::vec4 _deferredDebugSize { 0.0f, -1.0f, 1.0f, 1.0f }; + int _deferredDebugMode; + glm::vec4 _deferredDebugSize; protected: RenderArgs* _args; // Options - int _drawStatus = 0; // bitflag - bool _drawHitEffect = false; + int _drawStatus; // bitflag + bool _drawHitEffect; bool _occlusionStatus = false; bool _fxaaStatus = false; From 4115138b5b9920d615420dd81cce9ee86eee82f4 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Tue, 22 Dec 2015 17:22:55 -0800 Subject: [PATCH 94/94] fix merge problems --- examples/controllers/handControllerGrab.js | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 04b52ebb50..9e7623b483 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -274,8 +274,6 @@ function MyController(hand) { this.triggerValue = 0; // rolling average of trigger value this.rawTriggerValue = 0; this.rawBumperValue = 0; - -<<<<<<< HEAD //for visualizations this.overlayLine = null; this.particleBeam = null; @@ -283,9 +281,7 @@ function MyController(hand) { //for lights this.spotlight = null; this.pointlight = null; -======= this.overlayLine = null; ->>>>>>> master this.ignoreIK = false; this.offsetPosition = Vec3.ZERO; @@ -510,7 +506,7 @@ function MyController(hand) { "y": 0, "z": 0 }, - "particleRadius": 0.015, + "particleRadius": 0.015, "radiusSpread": 0.005, // "radiusStart": 0.01, // "radiusFinish": 0.01, @@ -539,7 +535,7 @@ function MyController(hand) { visible: true, color: color, emitSpeed: speed, - speedSpread:spread, + speedSpread: spread, lifespan: lifespan }) @@ -1085,7 +1081,6 @@ function MyController(hand) { this.currentObjectRotation = Quat.multiply(handChange, this.currentObjectRotation); Entities.callEntityMethod(this.grabbedEntity, "continueDistantGrab"); - // mix in head motion if (MOVE_WITH_HEAD) { var objDistance = Vec3.length(objectToAvatar); @@ -1113,8 +1108,8 @@ function MyController(hand) { this.overlayLineOn(handPosition, grabbedProperties.position, INTERSECT_COLOR); } if (USE_PARTICLE_BEAM_FOR_MOVING === true) { - this.handleDistantParticleBeam(handPosition,grabbedProperties.position, INTERSECT_COLOR) - // this.handleDistantParticleBeam(handPosition, this.currentObjectPosition, INTERSECT_COLOR) + this.handleDistantParticleBeam(handPosition, grabbedProperties.position, INTERSECT_COLOR) + // this.handleDistantParticleBeam(handPosition, this.currentObjectPosition, INTERSECT_COLOR) } if (USE_POINTLIGHT === true) { this.handlePointLight(this.grabbedEntity);