From 806c1851be9f36d47cb77f409d55ece6a5f6a65f Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Fri, 23 Jan 2015 16:09:45 -0800 Subject: [PATCH 01/12] add save/restore of gravity --- examples/controllers/hydra/hydraGrab.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/examples/controllers/hydra/hydraGrab.js b/examples/controllers/hydra/hydraGrab.js index 4d0b873fd2..7b65db94a3 100644 --- a/examples/controllers/hydra/hydraGrab.js +++ b/examples/controllers/hydra/hydraGrab.js @@ -74,6 +74,7 @@ function controller(wichSide) { this.positionAtGrab; this.rotationAtGrab; + this.gravityAtGrab; this.modelPositionAtGrab; this.rotationAtGrab; this.jointsIntersectingFromStart = []; @@ -125,6 +126,7 @@ function controller(wichSide) { this.entityID = entityID; this.modelURL = properties.modelURL; + this.oldModelPosition = properties.position; this.oldModelRotation = properties.rotation; this.oldModelHalfDiagonal = Vec3.length(properties.dimensions) / 2.0; @@ -133,6 +135,10 @@ function controller(wichSide) { this.rotationAtGrab = this.rotation; this.modelPositionAtGrab = properties.position; this.rotationAtGrab = properties.rotation; + this.gravityAtGrab = properties.gravity; + + Entities.editEntity(entityID, { gravity: { x: 0, y: 0, z: 0 }, velocity: { x: 0, y: 0, z: 0 } }); + this.jointsIntersectingFromStart = []; for (var i = 0; i < jointList.length; i++) { var distance = Vec3.distance(MyAvatar.getJointPosition(jointList[i]), this.oldModelPosition); @@ -145,6 +151,9 @@ function controller(wichSide) { this.release = function () { if (this.grabbing) { + + Entities.editEntity(entityID, { gravity: this.gravityAtGrab }); + jointList = MyAvatar.getJointNames(); var closestJointIndex = -1; From 901044fc537a0dfe7bfdc1c4cb8175d9f238aede Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Sun, 25 Jan 2015 21:40:16 -0800 Subject: [PATCH 02/12] First version, popcorn machine --- examples/controllers/hydra/gun.js | 4 +- examples/controllers/hydra/hydraGrab.js | 8 +- examples/controllers/hydra/paddleBall.js | 20 ++- examples/popcorn.js | 182 +++++++++++++++++++++++ 4 files changed, 201 insertions(+), 13 deletions(-) create mode 100644 examples/popcorn.js diff --git a/examples/controllers/hydra/gun.js b/examples/controllers/hydra/gun.js index 0c0740e12b..549be9bccb 100644 --- a/examples/controllers/hydra/gun.js +++ b/examples/controllers/hydra/gun.js @@ -304,10 +304,12 @@ function makePlatform(gravity, scale, size) { } function entityCollisionWithEntity(entity1, entity2, collision) { - + cTime = new Date().getTime(); + //print("Collision at " + cTime); if (((entity1.id == bulletID.id) || (entity1.id == targetID.id)) && ((entity2.id == bulletID.id) || (entity2.id == targetID.id))) { score++; + print("Hit Target!"); if (showScore) { Overlays.editOverlay(text, { text: "Score: " + score } ); } diff --git a/examples/controllers/hydra/hydraGrab.js b/examples/controllers/hydra/hydraGrab.js index 8450b15758..ff51583599 100644 --- a/examples/controllers/hydra/hydraGrab.js +++ b/examples/controllers/hydra/hydraGrab.js @@ -14,7 +14,7 @@ // 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/entityPropertyDialogBox.js"); +Script.include("../../libraries/entityPropertyDialogBox.js"); var entityPropertyDialogBox = EntityPropertyDialogBox; var LASER_WIDTH = 4; @@ -23,8 +23,8 @@ var LASER_LENGTH_FACTOR = 500; var MIN_ANGULAR_SIZE = 2; var MAX_ANGULAR_SIZE = 45; -var allowLargeModels = false; -var allowSmallModels = false; +var allowLargeModels = true; +var allowSmallModels = true; var wantEntityGlow = false; var LEFT = 0; @@ -152,7 +152,7 @@ function controller(wichSide) { this.release = function () { if (this.grabbing) { - Entities.editEntity(entityID, { gravity: this.gravityAtGrab }); + Entities.editEntity(this.entityID, { gravity: this.gravityAtGrab }); jointList = MyAvatar.getJointNames(); diff --git a/examples/controllers/hydra/paddleBall.js b/examples/controllers/hydra/paddleBall.js index 85b025e4cd..a6b0a52c5f 100644 --- a/examples/controllers/hydra/paddleBall.js +++ b/examples/controllers/hydra/paddleBall.js @@ -16,12 +16,14 @@ var PADDLE_THICKNESS = 0.06; var PADDLE_COLOR = { red: 184, green: 134, blue: 11 }; var BALL_COLOR = { red: 255, green: 0, blue: 0 }; var LINE_COLOR = { red: 255, green: 255, blue: 0 }; -var PADDLE_OFFSET = { x: 0.05, y: 0.0, z: 0.0 }; +var PADDLE_BOX_OFFSET = { x: 0.05, y: 0.0, z: 0.0 }; +var HOLD_POSITION_OFFSET = { x: -0.2, y: 0.0, z: -0.25 }; +var PADDLE_ORIENTATION = Quat.fromPitchYawRollDegrees(0,0,0); var GRAVITY = 0.0; var SPRING_FORCE = 15.0; var lastSoundTime = 0; var gameOn = false; -var leftHanded = false; +var leftHanded = true; var controllerID; if (leftHanded) { @@ -73,7 +75,7 @@ function createEntities() { modelURL = "http://public.highfidelity.io/models/attachments/pong_paddle.fbx"; paddleModel = Entities.addEntity( { type: "Model", - position: Vec3.sum(Controller.getSpatialControlPosition(controllerID), PADDLE_OFFSET), + position: Vec3.sum(Controller.getSpatialControlPosition(controllerID), PADDLE_BOX_OFFSET), dimensions: { x: PADDLE_SIZE * 1.5, y: PADDLE_THICKNESS, z: PADDLE_SIZE * 1.25 }, color: PADDLE_COLOR, gravity: { x: 0, y: 0, z: 0 }, @@ -120,18 +122,20 @@ function update(deltaTime) { if (!ball.isKnownID) { ball = Entities.identifyEntity(ball); } else { + var paddleWorldOrientation = Quat.multiply(Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(controllerID)), PADDLE_ORIENTATION); + var holdPosition = Vec3.sum(palmPosition, Vec3.multiplyQbyV(paddleWorldOrientation, HOLD_POSITION_OFFSET)); var props = Entities.getEntityProperties(ball); - var spring = Vec3.subtract(palmPosition, props.position); - var paddleWorldOrientation = Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(controllerID)); + var spring = Vec3.subtract(holdPosition, props.position); var springLength = Vec3.length(spring); + spring = Vec3.normalize(spring); var ballVelocity = Vec3.sum(props.velocity, Vec3.multiply(springLength * SPRING_FORCE * deltaTime, spring)); Entities.editEntity(ball, { velocity: ballVelocity }); - Overlays.editOverlay(line, { start: props.position, end: palmPosition }); - Entities.editEntity(paddle, { position: palmPosition, + Overlays.editOverlay(line, { start: props.position, end: holdPosition }); + Entities.editEntity(paddle, { position: holdPosition, velocity: Controller.getSpatialControlVelocity(controllerID), rotation: paddleWorldOrientation }); - Entities.editEntity(paddleModel, { position: Vec3.sum(palmPosition, Vec3.multiplyQbyV(paddleWorldOrientation, PADDLE_OFFSET)), + Entities.editEntity(paddleModel, { position: Vec3.sum(holdPosition, Vec3.multiplyQbyV(paddleWorldOrientation, PADDLE_BOX_OFFSET)), velocity: Controller.getSpatialControlVelocity(controllerID), rotation: paddleWorldOrientation }); } diff --git a/examples/popcorn.js b/examples/popcorn.js new file mode 100644 index 0000000000..567953ac60 --- /dev/null +++ b/examples/popcorn.js @@ -0,0 +1,182 @@ +// +// popcorn.js +// examples +// +// Created by Philip Rosedale on January 25, 2014 +// Copyright 2015 High Fidelity, Inc. +// +// Creates a bunch of physical balls trapped in a box with a rotating wall in the middle that smacks them around, +// and a periodic 'pop' force that shoots them into the air. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +var BALL_SIZE = 0.07; +var WALL_THICKNESS = 0.10; +var SCALE = 1.0; + +var GRAVITY = -1.0; +var LIFETIME = 600; +var DAMPING = 0.50; + +var center = Vec3.sum(MyAvatar.position, Vec3.multiply(SCALE * 3.0, Quat.getFront(Camera.getOrientation()))); + +var floor = Entities.addEntity( + { type: "Box", + position: Vec3.subtract(center, { x: 0, y: SCALE / 2.0, z: 0 }), + dimensions: { x: SCALE, y: WALL_THICKNESS, z: SCALE }, + color: { red: 0, green: 255, blue: 0 }, + gravity: { x: 0, y: 0, z: 0 }, + ignoreCollisions: false, + lifetime: LIFETIME }); + +var ceiling = Entities.addEntity( + { type: "Box", + position: Vec3.sum(center, { x: 0, y: SCALE / 2.0, z: 0 }), + dimensions: { x: SCALE, y: WALL_THICKNESS, z: SCALE }, + color: { red: 128, green: 128, blue: 128 }, + gravity: { x: 0, y: 0, z: 0 }, + ignoreCollisions: false, + visible: true, + lifetime: LIFETIME }); + +var wall1 = Entities.addEntity( + { type: "Box", + position: Vec3.sum(center, { x: SCALE / 2.0, y: 0, z: 0 }), + dimensions: { x: WALL_THICKNESS, y: SCALE, z: SCALE }, + color: { red: 0, green: 255, blue: 0 }, + gravity: { x: 0, y: 0, z: 0 }, + ignoreCollisions: false, + visible: false, + lifetime: LIFETIME }); + +var wall2 = Entities.addEntity( + { type: "Box", + position: Vec3.subtract(center, { x: SCALE / 2.0, y: 0, z: 0 }), + dimensions: { x: WALL_THICKNESS, y: SCALE, z: SCALE }, + color: { red: 0, green: 255, blue: 0 }, + gravity: { x: 0, y: 0, z: 0 }, + ignoreCollisions: false, + visible: false, + lifetime: LIFETIME }); + +var wall3 = Entities.addEntity( + { type: "Box", + position: Vec3.subtract(center, { x: 0, y: 0, z: SCALE / 2.0 }), + dimensions: { x: SCALE, y: SCALE, z: WALL_THICKNESS }, + color: { red: 0, green: 255, blue: 0 }, + gravity: { x: 0, y: 0, z: 0 }, + ignoreCollisions: false, + visible: false, + lifetime: LIFETIME }); + +var wall4 = Entities.addEntity( + { type: "Box", + position: Vec3.sum(center, { x: 0, y: 0, z: SCALE / 2.0 }), + dimensions: { x: SCALE, y: SCALE, z: WALL_THICKNESS }, + color: { red: 0, green: 255, blue: 0 }, + gravity: { x: 0, y: 0, z: 0 }, + ignoreCollisions: false, + visible: false, + lifetime: LIFETIME }); + +var corner1 = Entities.addEntity( + { type: "Box", + position: Vec3.sum(center, { x: -SCALE / 2.0, y: 0, z: SCALE / 2.0 }), + dimensions: { x: WALL_THICKNESS, y: SCALE, z: WALL_THICKNESS }, + color: { red: 128, green: 128, blue: 128 }, + ignoreCollisions: false, + visible: true, + lifetime: LIFETIME }); + +var corner2 = Entities.addEntity( + { type: "Box", + position: Vec3.sum(center, { x: -SCALE / 2.0, y: 0, z: -SCALE / 2.0 }), + dimensions: { x: WALL_THICKNESS, y: SCALE, z: WALL_THICKNESS }, + color: { red: 128, green: 128, blue: 128 }, + ignoreCollisions: false, + visible: true, + lifetime: LIFETIME }); + +var corner3 = Entities.addEntity( + { type: "Box", + position: Vec3.sum(center, { x: SCALE / 2.0, y: 0, z: SCALE / 2.0 }), + dimensions: { x: WALL_THICKNESS, y: SCALE, z: WALL_THICKNESS }, + color: { red: 128, green: 128, blue: 128 }, + ignoreCollisions: false, + visible: true, + lifetime: LIFETIME }); + +var corner4 = Entities.addEntity( + { type: "Box", + position: Vec3.sum(center, { x: SCALE / 2.0, y: 0, z: -SCALE / 2.0 }), + dimensions: { x: WALL_THICKNESS, y: SCALE, z: WALL_THICKNESS }, + color: { red: 128, green: 128, blue: 128 }, + ignoreCollisions: false, + visible: true, + lifetime: LIFETIME }); + +var spinner = Entities.addEntity( + { type: "Box", + position: center, + dimensions: { x: SCALE / 1.5, y: SCALE / 3.0, z: SCALE / 8.0 }, + color: { red: 255, green: 0, blue: 0 }, + angularVelocity: { x: 0, y: 360, z: 0 }, + angularDamping: 0.0, + gravity: { x: 0, y: 0, z: 0 }, + ignoreCollisions: false, + visible: true, + lifetime: LIFETIME }); + +var NUM_BALLS = 70; + +balls = []; + +for (var i = 0; i < NUM_BALLS; i++) { + balls.push(Entities.addEntity( + { type: "Sphere", + position: { x: center.x + (Math.random() - 0.5) * (SCALE - BALL_SIZE - WALL_THICKNESS), + y: center.y + (Math.random() - 0.5) * (SCALE - BALL_SIZE - WALL_THICKNESS) , + z: center.z + (Math.random() - 0.5) * (SCALE - BALL_SIZE - WALL_THICKNESS) }, + dimensions: { x: BALL_SIZE, y: BALL_SIZE, z: BALL_SIZE }, + color: { red: Math.random() * 255, green: Math.random() * 255, blue: Math.random() * 255 }, + gravity: { x: 0, y: GRAVITY, z: 0 }, + ignoreCollisions: false, + damping: DAMPING, + lifetime: LIFETIME, + collisionsWillMove: true })); +} + +var VEL_MAG = 2.0; +var CHANCE_OF_POP = 0.007; // 0.01; +function update(deltaTime) { + for (var i = 0; i < NUM_BALLS; i++) { + if (Math.random() < CHANCE_OF_POP) { + Entities.editEntity(balls[i], { velocity: { x: (Math.random() - 0.5) * VEL_MAG, y: Math.random() * VEL_MAG, z: (Math.random() - 0.5) * VEL_MAG }}); + } + } + +} + + +function scriptEnding() { + Entities.deleteEntity(wall1); + Entities.deleteEntity(wall2); + Entities.deleteEntity(wall3); + Entities.deleteEntity(wall4); + Entities.deleteEntity(corner1); + Entities.deleteEntity(corner2); + Entities.deleteEntity(corner3); + Entities.deleteEntity(corner4); + Entities.deleteEntity(floor); + Entities.deleteEntity(ceiling); + Entities.deleteEntity(spinner); + + for (var i = 0; i < NUM_BALLS; i++) { + Entities.deleteEntity(balls[i]); + } +} + +Script.scriptEnding.connect(scriptEnding); +Script.update.connect(update); \ No newline at end of file From 67632fa589399da975762d83a0dd72abe5f9ff6c Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Sun, 25 Jan 2015 21:51:12 -0800 Subject: [PATCH 03/12] remove debug --- examples/controllers/hydra/gun.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/examples/controllers/hydra/gun.js b/examples/controllers/hydra/gun.js index 549be9bccb..bd6cf6f9f1 100644 --- a/examples/controllers/hydra/gun.js +++ b/examples/controllers/hydra/gun.js @@ -304,8 +304,6 @@ function makePlatform(gravity, scale, size) { } function entityCollisionWithEntity(entity1, entity2, collision) { - cTime = new Date().getTime(); - //print("Collision at " + cTime); if (((entity1.id == bulletID.id) || (entity1.id == targetID.id)) && ((entity2.id == bulletID.id) || (entity2.id == targetID.id))) { score++; From 09527cc2648fd074eded39814359c18227a1ca49 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Sun, 25 Jan 2015 21:52:08 -0800 Subject: [PATCH 04/12] hit target --- examples/controllers/hydra/gun.js | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/controllers/hydra/gun.js b/examples/controllers/hydra/gun.js index bd6cf6f9f1..b8fc15d75f 100644 --- a/examples/controllers/hydra/gun.js +++ b/examples/controllers/hydra/gun.js @@ -307,7 +307,6 @@ function entityCollisionWithEntity(entity1, entity2, collision) { if (((entity1.id == bulletID.id) || (entity1.id == targetID.id)) && ((entity2.id == bulletID.id) || (entity2.id == targetID.id))) { score++; - print("Hit Target!"); if (showScore) { Overlays.editOverlay(text, { text: "Score: " + score } ); } From bfdbe2e675b92a2fcc4341b5098889612e2a2254 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Mon, 26 Jan 2015 16:25:07 -0800 Subject: [PATCH 05/12] Detach both guns on shutdown --- examples/controllers/hydra/gun.js | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/controllers/hydra/gun.js b/examples/controllers/hydra/gun.js index b8fc15d75f..b5d015637c 100644 --- a/examples/controllers/hydra/gun.js +++ b/examples/controllers/hydra/gun.js @@ -500,6 +500,7 @@ function scriptEnding() { Overlays.deleteOverlay(pointer[1]); Overlays.deleteOverlay(text); MyAvatar.detachOne(gunModel); + MyAvatar.detachOne(gunModel); clearPose(); } From a1babe3a333eed514a1d620e1d2c3fc5a0060c73 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Mon, 26 Jan 2015 17:20:31 -0800 Subject: [PATCH 06/12] Put paddle exactly in hand, add animation of fingers gripping paddle --- examples/controllers/hydra/paddleBall.js | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/examples/controllers/hydra/paddleBall.js b/examples/controllers/hydra/paddleBall.js index a6b0a52c5f..8c9af68498 100644 --- a/examples/controllers/hydra/paddleBall.js +++ b/examples/controllers/hydra/paddleBall.js @@ -10,6 +10,12 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; + +hitSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Collisions-ballhitsandcatches/billiards/collision1.wav"); +var rightHandAnimation = HIFI_PUBLIC_BUCKET + "animations/RightHandAnimPhilip.fbx"; +var leftHandAnimation = HIFI_PUBLIC_BUCKET + "animations/LeftHandAnimPhilip.fbx"; + var BALL_SIZE = 0.08; var PADDLE_SIZE = 0.20; var PADDLE_THICKNESS = 0.06; @@ -17,7 +23,8 @@ var PADDLE_COLOR = { red: 184, green: 134, blue: 11 }; var BALL_COLOR = { red: 255, green: 0, blue: 0 }; var LINE_COLOR = { red: 255, green: 255, blue: 0 }; var PADDLE_BOX_OFFSET = { x: 0.05, y: 0.0, z: 0.0 }; -var HOLD_POSITION_OFFSET = { x: -0.2, y: 0.0, z: -0.25 }; + +var HOLD_POSITION_OFFSET = { x: -0.15, y: 0.05, z: -0.05 }; var PADDLE_ORIENTATION = Quat.fromPitchYawRollDegrees(0,0,0); var GRAVITY = 0.0; var SPRING_FORCE = 15.0; @@ -33,8 +40,7 @@ if (leftHanded) { } -HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; -hitSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Collisions-ballhitsandcatches/billiards/collision1.wav"); + var screenSize = Controller.getViewportDimensions(); var offButton = Overlays.addOverlay("image", { @@ -92,6 +98,9 @@ function createEntities() { alpha: 1, visible: true, lineWidth: 2 }); + + MyAvatar.stopAnimation(leftHanded ? leftHandAnimation: rightHandAnimation); + MyAvatar.startAnimation(leftHanded ? leftHandAnimation: rightHandAnimation, 15.0, 1.0, false, true, 0.0, 6); } function deleteEntities() { @@ -99,6 +108,7 @@ function deleteEntities() { Entities.deleteEntity(paddle); Entities.deleteEntity(paddleModel); Overlays.deleteOverlay(line); + MyAvatar.stopAnimation(leftHanded ? leftHandAnimation: rightHandAnimation); } function update(deltaTime) { @@ -123,7 +133,9 @@ function update(deltaTime) { ball = Entities.identifyEntity(ball); } else { var paddleWorldOrientation = Quat.multiply(Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(controllerID)), PADDLE_ORIENTATION); - var holdPosition = Vec3.sum(palmPosition, Vec3.multiplyQbyV(paddleWorldOrientation, HOLD_POSITION_OFFSET)); + var holdPosition = Vec3.sum(leftHanded ? MyAvatar.getLeftPalmPosition() : MyAvatar.getRightPalmPosition(), + Vec3.multiplyQbyV(paddleWorldOrientation, HOLD_POSITION_OFFSET)); + var props = Entities.getEntityProperties(ball); var spring = Vec3.subtract(holdPosition, props.position); var springLength = Vec3.length(spring); @@ -168,6 +180,7 @@ function scriptEnding() { deleteEntities(); } Overlays.deleteOverlay(offButton); + MyAvatar.stopAnimation(leftHanded ? leftHandAnimation: rightHandAnimation); } Entities.entityCollisionWithEntity.connect(entityCollisionWithEntity); From e0c9af853c6e132979df2c0c7c931aab94d009f6 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Mon, 26 Jan 2015 20:19:46 -0800 Subject: [PATCH 07/12] remove 'STICKS' mode, add drop line to better tell what something you are holding is above --- examples/controllers/hydra/hydraGrab.js | 173 +++++++++--------------- 1 file changed, 67 insertions(+), 106 deletions(-) diff --git a/examples/controllers/hydra/hydraGrab.js b/examples/controllers/hydra/hydraGrab.js index 2ebe4eb9b6..9f250f9208 100644 --- a/examples/controllers/hydra/hydraGrab.js +++ b/examples/controllers/hydra/hydraGrab.js @@ -28,13 +28,12 @@ var RIGHT = 1; var jointList = MyAvatar.getJointNames(); -var STICKS = 0; -var MAPPED = 1; -var mode = STICKS; +var LASER_WIDTH = 3; +var LASER_COLOR = { red: 50, green: 150, blue: 200 }; +var DROP_COLOR = { red: 200, green: 200, blue: 200 }; +var DROP_WIDTH = 4; +var DROP_DISTANCE = 5.0; -var LASER_WIDTH = 4; -var LASER_COLOR = [{ red: 200, green: 150, blue: 50 }, // STICKS - { red: 50, green: 150, blue: 200 }]; // MAPPED var LASER_LENGTH_FACTOR = 500; var lastAccurateIntersection = null; @@ -115,13 +114,21 @@ function controller(wichSide) { this.laser = Overlays.addOverlay("line3d", { start: { x: 0, y: 0, z: 0 }, end: { x: 0, y: 0, z: 0 }, - color: LASER_COLOR[mode], + color: LASER_COLOR, alpha: 1, visible: false, lineWidth: LASER_WIDTH, anchor: "MyAvatar" }); + this.dropLine = Overlays.addOverlay("line3d", { + start: { x: 0, y: 0, z: 0 }, + end: { x: 0, y: 0, z: 0 }, + color: DROP_COLOR, + alpha: 1, + visible: false, + lineWidth: DROP_WIDTH }); + this.guideScale = 0.02; this.ball = Overlays.addOverlay("sphere", { position: { x: 0, y: 0, z: 0 }, @@ -180,6 +187,7 @@ function controller(wichSide) { } } this.showLaser(false); + Overlays.editOverlay(this.dropLine, { visible: true }); } this.release = function () { @@ -225,6 +233,8 @@ function controller(wichSide) { Entities.deleteEntity(this.entityID); } } + + Overlays.editOverlay(this.dropLine, { visible: false }); } this.grabbing = false; @@ -297,7 +307,6 @@ function controller(wichSide) { end: endPosition }); - Overlays.editOverlay(this.ball, { position: endPosition }); @@ -309,7 +318,7 @@ function controller(wichSide) { start: Vec3.sum(endPosition, Vec3.multiply(this.up, 2 * this.guideScale)), end: Vec3.sum(endPosition, Vec3.multiply(this.up, -2 * this.guideScale)) }); - this.showLaser(!this.grabbing || mode == STICKS); + this.showLaser(!this.grabbing); if (this.glowedIntersectingModel.isKnownID) { Entities.editEntity(this.glowedIntersectingModel, { glowLevel: 0.0 }); @@ -352,49 +361,33 @@ function controller(wichSide) { } var newPosition; var newRotation; + + var forward = Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -1 }); + var d = Vec3.dot(forward, MyAvatar.position); - switch (mode) { - case STICKS: - newPosition = Vec3.sum(this.palmPosition, - Vec3.multiply(this.front, this.x)); - newPosition = Vec3.sum(newPosition, - Vec3.multiply(this.up, this.y)); - newPosition = Vec3.sum(newPosition, - Vec3.multiply(this.right, this.z)); + var factor1 = Vec3.dot(forward, this.positionAtGrab) - d; + var factor2 = Vec3.dot(forward, this.modelPositionAtGrab) - d; + var vector = Vec3.subtract(this.palmPosition, this.positionAtGrab); - - newRotation = Quat.multiply(this.rotation, - Quat.inverse(this.oldRotation)); - newRotation = Quat.multiply(newRotation, - this.oldModelRotation); - break; - case MAPPED: - var forward = Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -1 }); - var d = Vec3.dot(forward, MyAvatar.position); - - var factor1 = Vec3.dot(forward, this.positionAtGrab) - d; - var factor2 = Vec3.dot(forward, this.modelPositionAtGrab) - d; - var vector = Vec3.subtract(this.palmPosition, this.positionAtGrab); - - if (factor2 < 0) { - factor2 = 0; - } - if (factor1 <= 0) { - factor1 = 1; - factor2 = 1; - } - - newPosition = Vec3.sum(this.modelPositionAtGrab, - Vec3.multiply(vector, - factor2 / factor1)); - - newRotation = Quat.multiply(this.rotation, - Quat.inverse(this.rotationAtGrab)); - newRotation = Quat.multiply(newRotation, newRotation); - newRotation = Quat.multiply(newRotation, - this.modelRotationAtGrab); - break; + if (factor2 < 0) { + factor2 = 0; } + if (factor1 <= 0) { + factor1 = 1; + factor2 = 1; + } + + newPosition = Vec3.sum(this.modelPositionAtGrab, + Vec3.multiply(vector, + factor2 / factor1)); + + newRotation = Quat.multiply(this.rotation, + Quat.inverse(this.rotationAtGrab)); + newRotation = Quat.multiply(newRotation, newRotation); + newRotation = Quat.multiply(newRotation, + this.modelRotationAtGrab); + + Entities.editEntity(this.entityID, { position: newPosition, rotation: newRotation @@ -402,6 +395,8 @@ function controller(wichSide) { this.oldModelRotation = newRotation; this.oldModelPosition = newPosition; + Overlays.editOverlay(this.dropLine, { start: newPosition, end: Vec3.sum(newPosition, { x: 0, y: -DROP_DISTANCE, z: 0 }) }); + var indicesToRemove = []; for (var i = 0; i < this.jointsIntersectingFromStart.length; ++i) { var distance = Vec3.distance(MyAvatar.getJointPosition(this.jointsIntersectingFromStart[i]), this.oldModelPosition); @@ -437,15 +432,6 @@ function controller(wichSide) { this.triggerValue = Controller.getTriggerValue(this.trigger); var bumperValue = Controller.isButtonPressed(this.bumper); - if (bumperValue && !this.bumperValue) { - if (mode === STICKS) { - mode = MAPPED; - } else if (mode === MAPPED) { - mode = STICKS; - } - Overlays.editOverlay(leftController.laser, { color: LASER_COLOR[mode] }); - Overlays.editOverlay(rightController.laser, { color: LASER_COLOR[mode] }); - } this.bumperValue = bumperValue; @@ -563,55 +549,31 @@ function moveEntities() { var rotation = leftController.oldModelRotation; var ratio = 1; + var u = Vec3.normalize(Vec3.subtract(rightController.oldPalmPosition, leftController.oldPalmPosition)); + var v = Vec3.normalize(Vec3.subtract(rightController.palmPosition, leftController.palmPosition)); - switch (mode) { - case STICKS: - var oldLeftPoint = Vec3.sum(leftController.oldPalmPosition, Vec3.multiply(leftController.oldFront, leftController.x)); - var oldRightPoint = Vec3.sum(rightController.oldPalmPosition, Vec3.multiply(rightController.oldFront, rightController.x)); - - var oldMiddle = Vec3.multiply(Vec3.sum(oldLeftPoint, oldRightPoint), 0.5); - var oldLength = Vec3.length(Vec3.subtract(oldLeftPoint, oldRightPoint)); - - - var leftPoint = Vec3.sum(leftController.palmPosition, Vec3.multiply(leftController.front, leftController.x)); - var rightPoint = Vec3.sum(rightController.palmPosition, Vec3.multiply(rightController.front, rightController.x)); - - var middle = Vec3.multiply(Vec3.sum(leftPoint, rightPoint), 0.5); - var length = Vec3.length(Vec3.subtract(leftPoint, rightPoint)); - - - ratio = length / oldLength; - newPosition = Vec3.sum(middle, - Vec3.multiply(Vec3.subtract(leftController.oldModelPosition, oldMiddle), ratio)); - break; - case MAPPED: - var u = Vec3.normalize(Vec3.subtract(rightController.oldPalmPosition, leftController.oldPalmPosition)); - var v = Vec3.normalize(Vec3.subtract(rightController.palmPosition, leftController.palmPosition)); - - var cos_theta = Vec3.dot(u, v); - if (cos_theta > 1) { - cos_theta = 1; - } - var angle = Math.acos(cos_theta) / Math.PI * 180; - if (angle < 0.1) { - return; - - } - var w = Vec3.normalize(Vec3.cross(u, v)); - - rotation = Quat.multiply(Quat.angleAxis(angle, w), leftController.oldModelRotation); - - - leftController.positionAtGrab = leftController.palmPosition; - leftController.rotationAtGrab = leftController.rotation; - leftController.modelPositionAtGrab = leftController.oldModelPosition; - leftController.modelRotationAtGrab = rotation; - rightController.positionAtGrab = rightController.palmPosition; - rightController.rotationAtGrab = rightController.rotation; - rightController.modelPositionAtGrab = rightController.oldModelPosition; - rightController.modelRotationAtGrab = rotation; - break; + var cos_theta = Vec3.dot(u, v); + if (cos_theta > 1) { + cos_theta = 1; } + var angle = Math.acos(cos_theta) / Math.PI * 180; + if (angle < 0.1) { + return; + } + var w = Vec3.normalize(Vec3.cross(u, v)); + + rotation = Quat.multiply(Quat.angleAxis(angle, w), leftController.oldModelRotation); + + + leftController.positionAtGrab = leftController.palmPosition; + leftController.rotationAtGrab = leftController.rotation; + leftController.modelPositionAtGrab = leftController.oldModelPosition; + leftController.modelRotationAtGrab = rotation; + rightController.positionAtGrab = rightController.palmPosition; + rightController.rotationAtGrab = rightController.rotation; + rightController.modelPositionAtGrab = rightController.oldModelPosition; + rightController.modelRotationAtGrab = rotation; + Entities.editEntity(leftController.entityID, { position: newPosition, rotation: rotation, @@ -621,7 +583,6 @@ function moveEntities() { y: leftController.oldModelHalfDiagonal * ratio, z: leftController.oldModelHalfDiagonal * ratio } - }); leftController.oldModelPosition = newPosition; leftController.oldModelRotation = rotation; From 005cd2ba53a70c639b73f8299e72d9c2871d3420 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Mon, 26 Jan 2015 20:44:47 -0800 Subject: [PATCH 08/12] Add block building toolset, first version --- examples/blocks.js | 114 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 examples/blocks.js diff --git a/examples/blocks.js b/examples/blocks.js new file mode 100644 index 0000000000..69d4dbc4d4 --- /dev/null +++ b/examples/blocks.js @@ -0,0 +1,114 @@ +// +// Blocks.js +// +// Created by Philip Rosedale on January 26, 2015 +// Copyright 2015 High Fidelity, Inc. +// +// Create a bunch of building blocks and drop them onto a playing surface in front of you. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +var FLOOR_SIZE = 5.0; +var FLOOR_THICKNESS = 0.10; +var EDGE_THICKESS = 0.25; +var SCALE = 0.25; + +var GRAVITY = -1.0; +var LIFETIME = 6000; +var DAMPING = 0.50; + +var blockTypes = []; +blockTypes.push({ x: 1, y: 1, z: 1, red: 255, green: 0, blue: 0 }); +blockTypes.push({ x: 1, y: 1, z: 2, red: 0, green: 255, blue: 0 }); +blockTypes.push({ x: 1, y: 2, z: 5, red: 0, green: 0, blue: 255 }); +blockTypes.push({ x: 1, y: 2, z: 2, red: 255, green: 255, blue: 0 }); + + +var center = Vec3.sum(MyAvatar.position, Vec3.multiply(FLOOR_SIZE * 2.0, Quat.getFront(Camera.getOrientation()))); + +var floor = Entities.addEntity( + { type: "Box", + position: Vec3.subtract(center, { x: 0, y: SCALE / 2.0, z: 0 }), + dimensions: { x: FLOOR_SIZE, y: FLOOR_THICKNESS, z: FLOOR_SIZE }, + color: { red: 128, green: 128, blue: 128 }, + gravity: { x: 0, y: 0, z: 0 }, + ignoreCollisions: false, + locked: true, + lifetime: LIFETIME }); + +var edge1 = Entities.addEntity( + { type: "Box", + position: Vec3.sum(center, { x: FLOOR_SIZE / 2.0, y: FLOOR_THICKNESS / 2.0, z: 0 }), + dimensions: { x: EDGE_THICKESS, y: EDGE_THICKESS, z: FLOOR_SIZE }, + color: { red: 128, green: 128, blue: 128 }, + gravity: { x: 0, y: 0, z: 0 }, + ignoreCollisions: false, + visible: true, + lifetime: LIFETIME }); + +var edge2 = Entities.addEntity( + { type: "Box", + position: Vec3.sum(center, { x: -FLOOR_SIZE / 2.0, y: FLOOR_THICKNESS / 2.0, z: 0 }), + dimensions: { x: EDGE_THICKESS, y: EDGE_THICKESS, z: FLOOR_SIZE }, + color: { red: 128, green: 128, blue: 128 }, + gravity: { x: 0, y: 0, z: 0 }, + ignoreCollisions: false, + visible: true, + lifetime: LIFETIME }); + +var edge3 = Entities.addEntity( + { type: "Box", + position: Vec3.sum(center, { x: 0, y: FLOOR_THICKNESS / 2.0, z: -FLOOR_SIZE / 2.0 }), + dimensions: { x: FLOOR_SIZE, y: EDGE_THICKESS, z: EDGE_THICKESS }, + color: { red: 128, green: 128, blue: 128 }, + gravity: { x: 0, y: 0, z: 0 }, + ignoreCollisions: false, + visible: true, + lifetime: LIFETIME }); + +var edge4 = Entities.addEntity( + { type: "Box", + position: Vec3.sum(center, { x: 0, y: FLOOR_THICKNESS / 2.0, z: FLOOR_SIZE / 2.0 }), + dimensions: { x: FLOOR_SIZE, y: EDGE_THICKESS, z: EDGE_THICKESS }, + color: { red: 128, green: 128, blue: 128 }, + gravity: { x: 0, y: 0, z: 0 }, + ignoreCollisions: false, + visible: true, + lifetime: LIFETIME }); + +var NUM_BLOCKS = 20; +var DROP_HEIGHT = FLOOR_SIZE / 3; + +blocks = []; + +for (var i = 0; i < NUM_BLOCKS; i++) { + var which = Math.floor(Math.random() * blockTypes.length); + var type = blockTypes[which]; + blocks.push(Entities.addEntity( + { type: "Box", + position: { x: center.x + (Math.random() - 0.5) * (FLOOR_SIZE * 0.75), + y: center.y + DROP_HEIGHT, + z: center.z + (Math.random() - 0.5) * (FLOOR_SIZE * 0.75) }, + dimensions: { x: type.x * SCALE, y: type.y * SCALE, z: type.z * SCALE }, + color: { red: type.red, green: type.green, blue: type.blue }, + gravity: { x: 0, y: GRAVITY, z: 0 }, + ignoreCollisions: false, + damping: DAMPING, + lifetime: LIFETIME, + collisionsWillMove: true })); +} + +function scriptEnding() { + Entities.deleteEntity(edge1); + Entities.deleteEntity(edge2); + Entities.deleteEntity(edge3); + Entities.deleteEntity(edge4); + Entities.deleteEntity(floor); + + for (var i = 0; i < NUM_BLOCKS; i++) { + Entities.deleteEntity(blocks[i]); + } +} + +Script.scriptEnding.connect(scriptEnding); \ No newline at end of file From 8b5a45188695f19755d7ceb6ab81175b420aeaf6 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Mon, 26 Jan 2015 21:09:21 -0800 Subject: [PATCH 09/12] Grab now correctly adds linear velocity so that you can grab and throw things --- examples/controllers/hydra/hydraGrab.js | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/examples/controllers/hydra/hydraGrab.js b/examples/controllers/hydra/hydraGrab.js index 9f250f9208..5ba8613ad9 100644 --- a/examples/controllers/hydra/hydraGrab.js +++ b/examples/controllers/hydra/hydraGrab.js @@ -36,6 +36,8 @@ var DROP_DISTANCE = 5.0; var LASER_LENGTH_FACTOR = 500; +var velocity = { x: 0, y: 0, z: 0 }; + var lastAccurateIntersection = null; var accurateIntersections = 0; var totalIntersections = 0; @@ -350,7 +352,7 @@ function controller(wichSide) { Overlays.editOverlay(this.leftRight, { visible: show }); Overlays.editOverlay(this.topDown, { visible: show }); } - this.moveEntity = function () { + this.moveEntity = function (deltaTime) { if (this.grabbing) { if (!this.entityID.isKnownID) { print("Unknown grabbed ID " + this.entityID.id + ", isKnown: " + this.entityID.isKnownID); @@ -386,11 +388,13 @@ function controller(wichSide) { newRotation = Quat.multiply(newRotation, newRotation); newRotation = Quat.multiply(newRotation, this.modelRotationAtGrab); - + velocity = Vec3.multiply(1.0 / deltaTime, Vec3.subtract(newPosition, this.oldModelPosition)); + Entities.editEntity(this.entityID, { position: newPosition, - rotation: newRotation + rotation: newRotation, + velocity: velocity }); this.oldModelRotation = newRotation; this.oldModelPosition = newPosition; @@ -543,7 +547,7 @@ function controller(wichSide) { var leftController = new controller(LEFT); var rightController = new controller(RIGHT); -function moveEntities() { +function moveEntities(deltaTime) { if (leftController.grabbing && rightController.grabbing && rightController.entityID.id == leftController.entityID.id) { var newPosition = leftController.oldModelPosition; var rotation = leftController.oldModelRotation; @@ -593,8 +597,8 @@ function moveEntities() { rightController.oldModelHalfDiagonal *= ratio; return; } - leftController.moveEntity(); - rightController.moveEntity(); + leftController.moveEntity(deltaTime); + rightController.moveEntity(deltaTime); } var hydraConnected = false; @@ -612,7 +616,7 @@ function checkController(deltaTime) { leftController.update(); rightController.update(); - moveEntities(); + moveEntities(deltaTime); } else { if (hydraConnected) { hydraConnected = false; From e8f4783beadc8f158dad398e819d6cc1b392e259 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Mon, 26 Jan 2015 22:06:59 -0800 Subject: [PATCH 10/12] border, new size for blocks, smoother hydra grab action --- examples/blocks.js | 14 +++++++++----- examples/controllers/hydra/hydraGrab.js | 23 +++++++---------------- 2 files changed, 16 insertions(+), 21 deletions(-) diff --git a/examples/blocks.js b/examples/blocks.js index 69d4dbc4d4..30c2126096 100644 --- a/examples/blocks.js +++ b/examples/blocks.js @@ -9,11 +9,14 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -var FLOOR_SIZE = 5.0; +var FLOOR_SIZE = 7.5; var FLOOR_THICKNESS = 0.10; var EDGE_THICKESS = 0.25; var SCALE = 0.25; +var NUM_BLOCKS = 25; +var DROP_HEIGHT = SCALE * 8.0; + var GRAVITY = -1.0; var LIFETIME = 6000; var DAMPING = 0.50; @@ -23,7 +26,7 @@ blockTypes.push({ x: 1, y: 1, z: 1, red: 255, green: 0, blue: 0 }); blockTypes.push({ x: 1, y: 1, z: 2, red: 0, green: 255, blue: 0 }); blockTypes.push({ x: 1, y: 2, z: 5, red: 0, green: 0, blue: 255 }); blockTypes.push({ x: 1, y: 2, z: 2, red: 255, green: 255, blue: 0 }); - +blockTypes.push({ x: 1, y: 1, z: 5, red: 0, green: 255, blue: 255 }); var center = Vec3.sum(MyAvatar.position, Vec3.multiply(FLOOR_SIZE * 2.0, Quat.getFront(Camera.getOrientation()))); @@ -45,6 +48,7 @@ var edge1 = Entities.addEntity( gravity: { x: 0, y: 0, z: 0 }, ignoreCollisions: false, visible: true, + locked: true, lifetime: LIFETIME }); var edge2 = Entities.addEntity( @@ -55,6 +59,7 @@ var edge2 = Entities.addEntity( gravity: { x: 0, y: 0, z: 0 }, ignoreCollisions: false, visible: true, + locked: true, lifetime: LIFETIME }); var edge3 = Entities.addEntity( @@ -65,6 +70,7 @@ var edge3 = Entities.addEntity( gravity: { x: 0, y: 0, z: 0 }, ignoreCollisions: false, visible: true, + locked: true, lifetime: LIFETIME }); var edge4 = Entities.addEntity( @@ -75,11 +81,9 @@ var edge4 = Entities.addEntity( gravity: { x: 0, y: 0, z: 0 }, ignoreCollisions: false, visible: true, + locked: true, lifetime: LIFETIME }); -var NUM_BLOCKS = 20; -var DROP_HEIGHT = FLOOR_SIZE / 3; - blocks = []; for (var i = 0; i < NUM_BLOCKS; i++) { diff --git a/examples/controllers/hydra/hydraGrab.js b/examples/controllers/hydra/hydraGrab.js index 5ba8613ad9..dc8cd14eaa 100644 --- a/examples/controllers/hydra/hydraGrab.js +++ b/examples/controllers/hydra/hydraGrab.js @@ -363,25 +363,16 @@ function controller(wichSide) { } var newPosition; var newRotation; - - var forward = Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -1 }); - var d = Vec3.dot(forward, MyAvatar.position); - var factor1 = Vec3.dot(forward, this.positionAtGrab) - d; - var factor2 = Vec3.dot(forward, this.modelPositionAtGrab) - d; - var vector = Vec3.subtract(this.palmPosition, this.positionAtGrab); - - if (factor2 < 0) { - factor2 = 0; - } - if (factor1 <= 0) { - factor1 = 1; - factor2 = 1; + var CONSTANT_SCALING_FACTOR = 5.0; + var MINIMUM_SCALING_DISTANCE = 2.0; + var distanceToModel = Vec3.length(Vec3.subtract(this.oldModelPosition, this.palmPosition)); + if (distanceToModel < MINIMUM_SCALING_DISTANCE) { + distanceToModel = MINIMUM_SCALING_DISTANCE; } - newPosition = Vec3.sum(this.modelPositionAtGrab, - Vec3.multiply(vector, - factor2 / factor1)); + var deltaPalm = Vec3.multiply(distanceToModel * CONSTANT_SCALING_FACTOR, Vec3.subtract(this.palmPosition, this.oldPalmPosition)); + newPosition = Vec3.sum(this.oldModelPosition, deltaPalm); newRotation = Quat.multiply(this.rotation, Quat.inverse(this.rotationAtGrab)); From 117e6abb46e979c14aa41a42b3d600e53ef71e86 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Wed, 28 Jan 2015 09:34:15 -0800 Subject: [PATCH 11/12] remove debug and PR fixes --- examples/billiards.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/billiards.js b/examples/billiards.js index 3e2be52d0f..fbd41e8939 100644 --- a/examples/billiards.js +++ b/examples/billiards.js @@ -139,8 +139,6 @@ function makeBalls(pos) { } ballPosition.x += (BALL_GAP + Math.sqrt(3.0) / 2.0 * BALL_SIZE) * SCALE; } - print(balls.length + " Object balls made."); - print(isObjectBall(balls[1].id)); // Cue Ball cuePosition = { x: pos.x - (LENGTH / 4.0) * SCALE, y: pos.y + HEIGHT / 2.0 + DROP_HEIGHT, z: pos.z }; @@ -160,8 +158,9 @@ function makeBalls(pos) { function isObjectBall(id) { for (var i; i < balls.length; i++) { - if (balls[i].id == id) + if (balls[i].id == id) { return true; + } } return false; } @@ -243,6 +242,7 @@ function update(deltaTime) { function entityCollisionWithEntity(entity1, entity2, collision) { /* + NOT WORKING YET if ((entity1.id == cueBall.id) || (entity2.id == cueBall.id)) { print("Cue ball collision!"); //audioOptions.position = Vec3.sum(Camera.getPosition(), Quat.getFront(Camera.getOrientation())); From a85c6aa956e0ce7030103a246214fee68e2430a4 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Wed, 28 Jan 2015 09:49:45 -0800 Subject: [PATCH 12/12] fix tabs? --- examples/controllers/hydra/paddleBall.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/examples/controllers/hydra/paddleBall.js b/examples/controllers/hydra/paddleBall.js index 8c9af68498..d6c39131c8 100644 --- a/examples/controllers/hydra/paddleBall.js +++ b/examples/controllers/hydra/paddleBall.js @@ -39,9 +39,6 @@ if (leftHanded) { controllerID = 3; } - - - var screenSize = Controller.getViewportDimensions(); var offButton = Overlays.addOverlay("image", { x: screenSize.x - 48, @@ -98,8 +95,8 @@ function createEntities() { alpha: 1, visible: true, lineWidth: 2 }); - - MyAvatar.stopAnimation(leftHanded ? leftHandAnimation: rightHandAnimation); + + MyAvatar.stopAnimation(leftHanded ? leftHandAnimation: rightHandAnimation); MyAvatar.startAnimation(leftHanded ? leftHandAnimation: rightHandAnimation, 15.0, 1.0, false, true, 0.0, 6); }