From 3ee6d6a6dbb1b661555a81bcff16e444cd9b464e Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sun, 23 Nov 2014 20:12:52 -0800 Subject: [PATCH] work in making entity script mixable and inheritable --- examples/entityScripts/changeColorOnHover.js | 35 +- .../entityScripts/changeColorOnHoverClass.js | 55 +++ examples/entityScripts/movable.js | 410 +---------------- examples/entityScripts/movableClass.js | 426 ++++++++++++++++++ examples/entityScripts/sitOnEntity.js | 363 +-------------- examples/entityScripts/sittable.js | 15 + examples/entityScripts/sittableClass.js | 381 ++++++++++++++++ 7 files changed, 888 insertions(+), 797 deletions(-) create mode 100644 examples/entityScripts/changeColorOnHoverClass.js create mode 100644 examples/entityScripts/movableClass.js create mode 100644 examples/entityScripts/sittable.js create mode 100644 examples/entityScripts/sittableClass.js diff --git a/examples/entityScripts/changeColorOnHover.js b/examples/entityScripts/changeColorOnHover.js index 638c1bece4..0cf08adf1d 100644 --- a/examples/entityScripts/changeColorOnHover.js +++ b/examples/entityScripts/changeColorOnHover.js @@ -13,34 +13,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -(function(){ - this.oldColor = {}; - this.oldColorKnown = false; - this.storeOldColor = function(entityID) { - var oldProperties = Entities.getEntityProperties(entityID); - this.oldColor = oldProperties.color; - this.oldColorKnown = true; - print("storing old color... this.oldColor=" + this.oldColor.red + "," + this.oldColor.green + "," + this.oldColor.blue); - }; - - this.preload = function(entityID) { - print("preload"); - this.storeOldColor(entityID); - }; - - this.hoverEnterEntity = function(entityID, mouseEvent) { - print("hoverEnterEntity"); - if (!this.oldColorKnown) { - this.storeOldColor(entityID); - } - Entities.editEntity(entityID, { color: { red: 0, green: 255, blue: 255} }); - }; - this.hoverLeaveEntity = function(entityID, mouseEvent) { - print("hoverLeaveEntity"); - if (this.oldColorKnown) { - print("leave restoring old color... this.oldColor=" - + this.oldColor.red + "," + this.oldColor.green + "," + this.oldColor.blue); - Entities.editEntity(entityID, { color: this.oldColor }); - } - }; -}) \ No newline at end of file +(function() { + Script.include("changeColorOnHoverClass.js"); + return new ChangeColorOnHover(); +}) diff --git a/examples/entityScripts/changeColorOnHoverClass.js b/examples/entityScripts/changeColorOnHoverClass.js new file mode 100644 index 0000000000..231469f7e8 --- /dev/null +++ b/examples/entityScripts/changeColorOnHoverClass.js @@ -0,0 +1,55 @@ +// +// changeColorOnHover.js +// examples/entityScripts +// +// Created by Brad Hefta-Gaub on 11/1/14. +// Copyright 2014 High Fidelity, Inc. +// +// This is an example of an entity script which when assigned to a non-model entity like a box or sphere, will +// change the color of the entity when you hover over it. This script uses the JavaScript prototype/class functionality +// to construct an object that has methods for hoverEnterEntity and hoverLeaveEntity; +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + + +ChangeColorOnHover = function(){ + this.oldColor = {}; + this.oldColorKnown = false; +}; + +ChangeColorOnHover.prototype = { + + storeOldColor: function(entityID) { + var oldProperties = Entities.getEntityProperties(entityID); + this.oldColor = oldProperties.color; + this.oldColorKnown = true; + print("storing old color... this.oldColor=" + this.oldColor.red + "," + this.oldColor.green + "," + this.oldColor.blue); + }, + + preload: function(entityID) { + print("preload"); + this.storeOldColor(entityID); + }, + + hoverEnterEntity: function(entityID, mouseEvent) { + print("hoverEnterEntity"); + if (!this.oldColorKnown) { + this.storeOldColor(entityID); + } + Entities.editEntity(entityID, { color: { red: 0, green: 255, blue: 255} }); + }, + + + hoverLeaveEntity: function(entityID, mouseEvent) { + print("hoverLeaveEntity"); + if (this.oldColorKnown) { + print("leave restoring old color... this.oldColor=" + + this.oldColor.red + "," + this.oldColor.green + "," + this.oldColor.blue); + Entities.editEntity(entityID, { color: this.oldColor }); + } + } +}; + + diff --git a/examples/entityScripts/movable.js b/examples/entityScripts/movable.js index 21e6261179..0ea3641c6f 100644 --- a/examples/entityScripts/movable.js +++ b/examples/entityScripts/movable.js @@ -8,412 +8,8 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -(function(){ - - this.entityID = null; - this.properties = null; - this.graboffset = null; - this.clickedAt = null; - this.firstHolding = true; - this.clicked = { x: -1, y: -1}; - this.lastMovedPosition = { x: -1, y: -1}; - this.lastMovedTime = 0; - this.rotateOverlayTarget = null; - this.rotateOverlayInner = null; - this.rotateOverlayOuter = null; - this.rotateOverlayCurrent = null; - this.rotateMode = false; - this.originalRotation = null; - - this.moveSoundURLS = [ - "http://public.highfidelity.io/sounds/MovingFurniture/FurnitureMove1.wav", - "http://public.highfidelity.io/sounds/MovingFurniture/FurnitureMove2.wav", - "http://public.highfidelity.io/sounds/MovingFurniture/FurnitureMove3.wav" - ]; - - this.turnSoundURLS = [ - - "http://public.highfidelity.io/sounds/MovingFurniture/FurnitureMove1.wav", - "http://public.highfidelity.io/sounds/MovingFurniture/FurnitureMove2.wav", - "http://public.highfidelity.io/sounds/MovingFurniture/FurnitureMove3.wav" - - // TODO: determine if these or other turn sounds work better than move sounds. - //"http://public.highfidelity.io/sounds/MovingFurniture/FurnitureTurn1.wav", - //"http://public.highfidelity.io/sounds/MovingFurniture/FurnitureTurn2.wav", - //"http://public.highfidelity.io/sounds/MovingFurniture/FurnitureTurn3.wav" - ]; - - - this.moveSounds = new Array(); - this.turnSounds = new Array(); - this.moveSound = null; - this.turnSound = null; - this.injector = null; - - var debug = false; - var displayRotateTargets = true; // change to false if you don't want the rotate targets - var rotateOverlayTargetSize = 10000; // really big target - var innerSnapAngle = 22.5; // the angle which we snap to on the inner rotation tool - var innerRadius; - var outerRadius; - var yawCenter; - var yawZero; - var rotationNormal; - var yawNormal; - var stopSoundDelay = 100; // number of msecs of not moving to have sound stop - - this.getRandomInt = function(min, max) { - return Math.floor(Math.random() * (max - min + 1)) + min; - } - - this.downloadSounds = function() { - for (var i = 0; i < this.moveSoundURLS.length; i++) { - this.moveSounds[i] = SoundCache.getSound(this.moveSoundURLS[i]); - } - for (var i = 0; i < this.turnSoundURLS.length; i++) { - this.turnSounds[i] = SoundCache.getSound(this.turnSoundURLS[i]); - } - } - - this.pickRandomSounds = function() { - var moveIndex = this.getRandomInt(0, this.moveSounds.length - 1); - var turnIndex = this.getRandomInt(0, this.turnSounds.length - 1); - if (debug) { - print("Random sounds -- turn:" + turnIndex + " move:" + moveIndex); - } - this.moveSound = this.moveSounds[moveIndex]; - this.turnSound = this.turnSounds[turnIndex]; - } - - // Play move sound - this.playMoveSound = function() { - if (debug) { - print("playMoveSound()"); - } - if (this.moveSound && this.moveSound.downloaded) { - if (debug) { - print("playMoveSound() --- calling this.injector = Audio.playSound(this.moveSound...)"); - } - this.injector = Audio.playSound(this.moveSound, { position: this.properties.position, loop: true, volume: 0.1 }); - } - } - - // Play turn sound - this.playTurnSound = function() { - if (debug) { - print("playTurnSound()"); - } - if (this.turnSound && this.turnSound.downloaded) { - if (debug) { - print("playTurnSound() --- calling this.injector = Audio.playSound(this.turnSound...)"); - } - this.injector = Audio.playSound(this.turnSound, { position: this.properties.position, loop: true, volume: 0.1 }); - } - } - - // stop sound - this.stopSound = function() { - if (debug) { - print("stopSound()"); - } - if (this.injector) { - Audio.stopInjector(this.injector); - this.injector = null; - } - } - - // Pr, Vr are respectively the Ray's Point of origin and Vector director - // Pp, Np are respectively the Plane's Point of origin and Normal vector - this.rayPlaneIntersection = function(Pr, Vr, Pp, Np) { - var d = -Vec3.dot(Pp, Np); - var t = -(Vec3.dot(Pr, Np) + d) / Vec3.dot(Vr, Np); - return Vec3.sum(Pr, Vec3.multiply(t, Vr)); - }; - - // updates the piece position based on mouse input - this.updatePosition = function(mouseEvent) { - var pickRay = Camera.computePickRay(mouseEvent.x, mouseEvent.y) - var upVector = { x: 0, y: 1, z: 0 }; - var intersection = this.rayPlaneIntersection(pickRay.origin, pickRay.direction, - this.properties.position, upVector); - - var newPosition = Vec3.sum(intersection, this.graboffset); - Entities.editEntity(this.entityID, { position: newPosition }); - }; - - this.grab = function(mouseEvent) { - // first calculate the offset - var pickRay = Camera.computePickRay(mouseEvent.x, mouseEvent.y) - var upVector = { x: 0, y: 1, z: 0 }; - var intersection = this.rayPlaneIntersection(pickRay.origin, pickRay.direction, - this.properties.position, upVector); - this.graboffset = Vec3.subtract(this.properties.position, intersection); - }; - - this.stopSoundIfNotMoving = function(mouseEvent) { - var nowDate = new Date(); - var nowMSecs = nowDate.getTime(); - if(mouseEvent.x == this.lastMovedPosition.x && mouseEvent.y == this.lastMovedPosition.y) { - var elapsedSinceLastMove = nowMSecs - this.lastMovedMSecs; - if (debug) { - print("elapsedSinceLastMove:" + elapsedSinceLastMove); - } - if (elapsedSinceLastMove > stopSoundDelay) { - if (debug) { - print("calling stopSound()..."); - } - this.stopSound(); - } - } else { - // if we've moved, then track our last move position and time... - this.lastMovedMSecs = nowMSecs; - this.lastMovedPosition.x = mouseEvent.x; - this.lastMovedPosition.y = mouseEvent.y; - } - } - - this.move = function(mouseEvent) { - this.updatePosition(mouseEvent); - if (this.injector === null) { - this.playMoveSound(); - } - }; - - this.release = function(mouseEvent) { - this.updatePosition(mouseEvent); - }; - - this.rotate = function(mouseEvent) { - var pickRay = Camera.computePickRay(mouseEvent.x, mouseEvent.y) - var result = Overlays.findRayIntersection(pickRay); - - if (result.intersects) { - var center = yawCenter; - var zero = yawZero; - var centerToZero = Vec3.subtract(center, zero); - var centerToIntersect = Vec3.subtract(center, result.intersection); - var angleFromZero = Vec3.orientedAngle(centerToZero, centerToIntersect, rotationNormal); - - var distanceFromCenter = Vec3.distance(center, result.intersection); - var snapToInner = false; - // var innerRadius = (Vec3.length(selectionManager.worldDimensions) / 2) * 1.1; - if (distanceFromCenter < innerRadius) { - angleFromZero = Math.floor(angleFromZero/innerSnapAngle) * innerSnapAngle; - snapToInner = true; - } - - var yawChange = Quat.fromVec3Degrees({ x: 0, y: angleFromZero, z: 0 }); - Entities.editEntity(this.entityID, { rotation: Quat.multiply(yawChange, this.originalRotation) }); - - - // update the rotation display accordingly... - var startAtCurrent = 360-angleFromZero; - var endAtCurrent = 360; - var startAtRemainder = 0; - var endAtRemainder = 360-angleFromZero; - if (angleFromZero < 0) { - startAtCurrent = 0; - endAtCurrent = -angleFromZero; - startAtRemainder = -angleFromZero; - endAtRemainder = 360; - } - - if (snapToInner) { - Overlays.editOverlay(this.rotateOverlayOuter, { startAt: 0, endAt: 360 }); - Overlays.editOverlay(this.rotateOverlayInner, { startAt: startAtRemainder, endAt: endAtRemainder }); - Overlays.editOverlay(this.rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: innerRadius, - majorTickMarksAngle: innerSnapAngle, minorTickMarksAngle: 0, - majorTickMarksLength: -0.25, minorTickMarksLength: 0, }); - } else { - Overlays.editOverlay(this.rotateOverlayInner, { startAt: 0, endAt: 360 }); - Overlays.editOverlay(this.rotateOverlayOuter, { startAt: startAtRemainder, endAt: endAtRemainder }); - Overlays.editOverlay(this.rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: outerRadius, - majorTickMarksAngle: 45.0, minorTickMarksAngle: 5, - majorTickMarksLength: 0.25, minorTickMarksLength: 0.1, }); - } - } - - if (this.injector === null) { - this.playTurnSound(); - } - }; - // All callbacks start by updating the properties - this.updateProperties = function(entityID) { - if (this.entityID === null || !this.entityID.isKnownID) { - this.entityID = Entities.identifyEntity(entityID); - } - this.properties = Entities.getEntityProperties(this.entityID); - }; - - this.cleanupRotateOverlay = function() { - Overlays.deleteOverlay(this.rotateOverlayTarget); - Overlays.deleteOverlay(this.rotateOverlayInner); - Overlays.deleteOverlay(this.rotateOverlayOuter); - Overlays.deleteOverlay(this.rotateOverlayCurrent); - this.rotateOverlayTarget = null; - this.rotateOverlayInner = null; - this.rotateOverlayOuter = null; - this.rotateOverlayCurrent = null; - } - - this.displayRotateOverlay = function(mouseEvent) { - var yawOverlayAngles = { x: 90, y: 0, z: 0 }; - var yawOverlayRotation = Quat.fromVec3Degrees(yawOverlayAngles); - - yawNormal = { x: 0, y: 1, z: 0 }; - yawCenter = this.properties.position; - rotationNormal = yawNormal; - - // Size the overlays to the current selection size - var diagonal = (Vec3.length(this.properties.dimensions) / 2) * 1.1; - var halfDimensions = Vec3.multiply(this.properties.dimensions, 0.5); - innerRadius = diagonal; - outerRadius = diagonal * 1.15; - var innerAlpha = 0.2; - var outerAlpha = 0.2; - - this.rotateOverlayTarget = Overlays.addOverlay("circle3d", { - position: this.properties.position, - size: rotateOverlayTargetSize, - color: { red: 0, green: 0, blue: 0 }, - alpha: 0.0, - solid: true, - visible: true, - rotation: yawOverlayRotation, - ignoreRayIntersection: false - }); - - this.rotateOverlayInner = Overlays.addOverlay("circle3d", { - position: this.properties.position, - size: innerRadius, - innerRadius: 0.9, - alpha: innerAlpha, - color: { red: 51, green: 152, blue: 203 }, - solid: true, - visible: displayRotateTargets, - rotation: yawOverlayRotation, - hasTickMarks: true, - majorTickMarksAngle: innerSnapAngle, - minorTickMarksAngle: 0, - majorTickMarksLength: -0.25, - minorTickMarksLength: 0, - majorTickMarksColor: { red: 0, green: 0, blue: 0 }, - minorTickMarksColor: { red: 0, green: 0, blue: 0 }, - ignoreRayIntersection: true, // always ignore this - }); - - this.rotateOverlayOuter = Overlays.addOverlay("circle3d", { - position: this.properties.position, - size: outerRadius, - innerRadius: 0.9, - startAt: 0, - endAt: 360, - alpha: outerAlpha, - color: { red: 51, green: 152, blue: 203 }, - solid: true, - visible: displayRotateTargets, - rotation: yawOverlayRotation, - - hasTickMarks: true, - majorTickMarksAngle: 45.0, - minorTickMarksAngle: 5, - majorTickMarksLength: 0.25, - minorTickMarksLength: 0.1, - majorTickMarksColor: { red: 0, green: 0, blue: 0 }, - minorTickMarksColor: { red: 0, green: 0, blue: 0 }, - ignoreRayIntersection: true, // always ignore this - }); - - this.rotateOverlayCurrent = Overlays.addOverlay("circle3d", { - position: this.properties.position, - size: outerRadius, - startAt: 0, - endAt: 0, - innerRadius: 0.9, - color: { red: 224, green: 67, blue: 36}, - alpha: 0.8, - solid: true, - visible: displayRotateTargets, - rotation: yawOverlayRotation, - ignoreRayIntersection: true, // always ignore this - hasTickMarks: true, - majorTickMarksColor: { red: 0, green: 0, blue: 0 }, - minorTickMarksColor: { red: 0, green: 0, blue: 0 }, - }); - - var pickRay = Camera.computePickRay(mouseEvent.x, mouseEvent.y) - var result = Overlays.findRayIntersection(pickRay); - yawZero = result.intersection; - - }; - - this.preload = function(entityID) { - this.updateProperties(entityID); // All callbacks start by updating the properties - this.downloadSounds(); - }; - - this.clickDownOnEntity = function(entityID, mouseEvent) { - this.updateProperties(entityID); // All callbacks start by updating the properties - this.grab(mouseEvent); - - var nowDate = new Date(); - var nowMSecs = nowDate.getTime(); - this.clickedAt = nowMSecs; - this.firstHolding = true; - - this.clicked.x = mouseEvent.x; - this.clicked.y = mouseEvent.y; - this.lastMovedPosition.x = mouseEvent.x; - this.lastMovedPosition.y = mouseEvent.y; - this.lastMovedMSecs = nowMSecs; - - this.pickRandomSounds(); - }; - - this.holdingClickOnEntity = function(entityID, mouseEvent) { - - this.updateProperties(entityID); // All callbacks start by updating the properties - - if (this.firstHolding) { - // if we haven't moved yet... - if (this.clicked.x == mouseEvent.x && this.clicked.y == mouseEvent.y) { - var d = new Date(); - var now = d.getTime(); - - if (now - this.clickedAt > 500) { - this.displayRotateOverlay(mouseEvent); - this.firstHolding = false; - this.rotateMode = true; - this.originalRotation = this.properties.rotation; - } - } else { - this.firstHolding = false; - } - } - - if (this.rotateMode) { - this.rotate(mouseEvent); - } else { - this.move(mouseEvent); - } - - this.stopSoundIfNotMoving(mouseEvent); - }; - this.clickReleaseOnEntity = function(entityID, mouseEvent) { - this.updateProperties(entityID); // All callbacks start by updating the properties - if (this.rotateMode) { - this.rotate(mouseEvent); - } else { - this.release(mouseEvent); - } - - if (this.rotateOverlayTarget != null) { - this.cleanupRotateOverlay(); - this.rotateMode = false; - } - - this.firstHolding = false; - this.stopSound(); - }; +(function() { + Script.include("movableClass.js"); + return new Movable(); }) diff --git a/examples/entityScripts/movableClass.js b/examples/entityScripts/movableClass.js new file mode 100644 index 0000000000..26fb643826 --- /dev/null +++ b/examples/entityScripts/movableClass.js @@ -0,0 +1,426 @@ +// +// movableClass.js +// examples/entityScripts +// +// Created by Brad Hefta-Gaub on 11/17/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +Movable = function() { + + this.entityID = null; + this.properties = null; + this.graboffset = null; + this.clickedAt = null; + this.firstHolding = true; + this.clicked = { x: -1, y: -1}; + this.lastMovedPosition = { x: -1, y: -1}; + this.lastMovedTime = 0; + this.rotateOverlayTarget = null; + this.rotateOverlayInner = null; + this.rotateOverlayOuter = null; + this.rotateOverlayCurrent = null; + this.rotateMode = false; + this.originalRotation = null; + + this.moveSoundURLS = [ + "http://public.highfidelity.io/sounds/MovingFurniture/FurnitureMove1.wav", + "http://public.highfidelity.io/sounds/MovingFurniture/FurnitureMove2.wav", + "http://public.highfidelity.io/sounds/MovingFurniture/FurnitureMove3.wav" + ]; + + this.turnSoundURLS = [ + + "http://public.highfidelity.io/sounds/MovingFurniture/FurnitureMove1.wav", + "http://public.highfidelity.io/sounds/MovingFurniture/FurnitureMove2.wav", + "http://public.highfidelity.io/sounds/MovingFurniture/FurnitureMove3.wav" + + // TODO: determine if these or other turn sounds work better than move sounds. + //"http://public.highfidelity.io/sounds/MovingFurniture/FurnitureTurn1.wav", + //"http://public.highfidelity.io/sounds/MovingFurniture/FurnitureTurn2.wav", + //"http://public.highfidelity.io/sounds/MovingFurniture/FurnitureTurn3.wav" + ]; + + + this.moveSounds = new Array(); + this.turnSounds = new Array(); + this.moveSound = null; + this.turnSound = null; + this.injector = null; + + var debug = false; + var displayRotateTargets = true; // change to false if you don't want the rotate targets + var rotateOverlayTargetSize = 10000; // really big target + var innerSnapAngle = 22.5; // the angle which we snap to on the inner rotation tool + var innerRadius; + var outerRadius; + var yawCenter; + var yawZero; + var rotationNormal; + var yawNormal; + var stopSoundDelay = 100; // number of msecs of not moving to have sound stop + + this.getRandomInt = function(min, max) { + return Math.floor(Math.random() * (max - min + 1)) + min; + } + + this.downloadSounds = function() { + for (var i = 0; i < this.moveSoundURLS.length; i++) { + this.moveSounds[i] = SoundCache.getSound(this.moveSoundURLS[i]); + } + for (var i = 0; i < this.turnSoundURLS.length; i++) { + this.turnSounds[i] = SoundCache.getSound(this.turnSoundURLS[i]); + } + } + + this.pickRandomSounds = function() { + var moveIndex = this.getRandomInt(0, this.moveSounds.length - 1); + var turnIndex = this.getRandomInt(0, this.turnSounds.length - 1); + if (debug) { + print("Random sounds -- turn:" + turnIndex + " move:" + moveIndex); + } + this.moveSound = this.moveSounds[moveIndex]; + this.turnSound = this.turnSounds[turnIndex]; + } + + // Play move sound + this.playMoveSound = function() { + if (debug) { + print("playMoveSound()"); + } + if (this.moveSound && this.moveSound.downloaded) { + if (debug) { + print("playMoveSound() --- calling this.injector = Audio.playSound(this.moveSound...)"); + } + this.injector = Audio.playSound(this.moveSound, { position: this.properties.position, loop: true, volume: 0.1 }); + } + } + + // Play turn sound + this.playTurnSound = function() { + if (debug) { + print("playTurnSound()"); + } + if (this.turnSound && this.turnSound.downloaded) { + if (debug) { + print("playTurnSound() --- calling this.injector = Audio.playSound(this.turnSound...)"); + } + this.injector = Audio.playSound(this.turnSound, { position: this.properties.position, loop: true, volume: 0.1 }); + } + } + + // stop sound + this.stopSound = function() { + if (debug) { + print("stopSound()"); + } + if (this.injector) { + Audio.stopInjector(this.injector); + this.injector = null; + } + } + + // Pr, Vr are respectively the Ray's Point of origin and Vector director + // Pp, Np are respectively the Plane's Point of origin and Normal vector + this.rayPlaneIntersection = function(Pr, Vr, Pp, Np) { + var d = -Vec3.dot(Pp, Np); + var t = -(Vec3.dot(Pr, Np) + d) / Vec3.dot(Vr, Np); + return Vec3.sum(Pr, Vec3.multiply(t, Vr)); + }; + + // updates the piece position based on mouse input + this.updatePosition = function(mouseEvent) { + var pickRay = Camera.computePickRay(mouseEvent.x, mouseEvent.y) + var upVector = { x: 0, y: 1, z: 0 }; + var intersection = this.rayPlaneIntersection(pickRay.origin, pickRay.direction, + this.properties.position, upVector); + + var newPosition = Vec3.sum(intersection, this.graboffset); + Entities.editEntity(this.entityID, { position: newPosition }); + }; + + this.grab = function(mouseEvent) { + // first calculate the offset + var pickRay = Camera.computePickRay(mouseEvent.x, mouseEvent.y) + var upVector = { x: 0, y: 1, z: 0 }; + var intersection = this.rayPlaneIntersection(pickRay.origin, pickRay.direction, + this.properties.position, upVector); + this.graboffset = Vec3.subtract(this.properties.position, intersection); + }; + + this.stopSoundIfNotMoving = function(mouseEvent) { + var nowDate = new Date(); + var nowMSecs = nowDate.getTime(); + if(mouseEvent.x == this.lastMovedPosition.x && mouseEvent.y == this.lastMovedPosition.y) { + var elapsedSinceLastMove = nowMSecs - this.lastMovedMSecs; + if (debug) { + print("elapsedSinceLastMove:" + elapsedSinceLastMove); + } + if (elapsedSinceLastMove > stopSoundDelay) { + if (debug) { + print("calling stopSound()..."); + } + this.stopSound(); + } + } else { + // if we've moved, then track our last move position and time... + this.lastMovedMSecs = nowMSecs; + this.lastMovedPosition.x = mouseEvent.x; + this.lastMovedPosition.y = mouseEvent.y; + } + } + + this.move = function(mouseEvent) { + this.updatePosition(mouseEvent); + if (this.injector === null) { + this.playMoveSound(); + } + }; + + this.release = function(mouseEvent) { + this.updatePosition(mouseEvent); + }; + + this.rotate = function(mouseEvent) { + var pickRay = Camera.computePickRay(mouseEvent.x, mouseEvent.y) + var result = Overlays.findRayIntersection(pickRay); + + if (result.intersects) { + var center = yawCenter; + var zero = yawZero; + var centerToZero = Vec3.subtract(center, zero); + var centerToIntersect = Vec3.subtract(center, result.intersection); + var angleFromZero = Vec3.orientedAngle(centerToZero, centerToIntersect, rotationNormal); + + var distanceFromCenter = Vec3.distance(center, result.intersection); + var snapToInner = false; + // var innerRadius = (Vec3.length(selectionManager.worldDimensions) / 2) * 1.1; + if (distanceFromCenter < innerRadius) { + angleFromZero = Math.floor(angleFromZero/innerSnapAngle) * innerSnapAngle; + snapToInner = true; + } + + var yawChange = Quat.fromVec3Degrees({ x: 0, y: angleFromZero, z: 0 }); + Entities.editEntity(this.entityID, { rotation: Quat.multiply(yawChange, this.originalRotation) }); + + + // update the rotation display accordingly... + var startAtCurrent = 360-angleFromZero; + var endAtCurrent = 360; + var startAtRemainder = 0; + var endAtRemainder = 360-angleFromZero; + if (angleFromZero < 0) { + startAtCurrent = 0; + endAtCurrent = -angleFromZero; + startAtRemainder = -angleFromZero; + endAtRemainder = 360; + } + + if (snapToInner) { + Overlays.editOverlay(this.rotateOverlayOuter, { startAt: 0, endAt: 360 }); + Overlays.editOverlay(this.rotateOverlayInner, { startAt: startAtRemainder, endAt: endAtRemainder }); + Overlays.editOverlay(this.rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: innerRadius, + majorTickMarksAngle: innerSnapAngle, minorTickMarksAngle: 0, + majorTickMarksLength: -0.25, minorTickMarksLength: 0, }); + } else { + Overlays.editOverlay(this.rotateOverlayInner, { startAt: 0, endAt: 360 }); + Overlays.editOverlay(this.rotateOverlayOuter, { startAt: startAtRemainder, endAt: endAtRemainder }); + Overlays.editOverlay(this.rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: outerRadius, + majorTickMarksAngle: 45.0, minorTickMarksAngle: 5, + majorTickMarksLength: 0.25, minorTickMarksLength: 0.1, }); + } + } + + if (this.injector === null) { + this.playTurnSound(); + } + }; + // All callbacks start by updating the properties + this.updateProperties = function(entityID) { + if (this.entityID === null || !this.entityID.isKnownID) { + this.entityID = Entities.identifyEntity(entityID); + } + this.properties = Entities.getEntityProperties(this.entityID); + }; + + this.cleanupRotateOverlay = function() { + Overlays.deleteOverlay(this.rotateOverlayTarget); + Overlays.deleteOverlay(this.rotateOverlayInner); + Overlays.deleteOverlay(this.rotateOverlayOuter); + Overlays.deleteOverlay(this.rotateOverlayCurrent); + this.rotateOverlayTarget = null; + this.rotateOverlayInner = null; + this.rotateOverlayOuter = null; + this.rotateOverlayCurrent = null; + } + + this.displayRotateOverlay = function(mouseEvent) { + var yawOverlayAngles = { x: 90, y: 0, z: 0 }; + var yawOverlayRotation = Quat.fromVec3Degrees(yawOverlayAngles); + + yawNormal = { x: 0, y: 1, z: 0 }; + yawCenter = this.properties.position; + rotationNormal = yawNormal; + + // Size the overlays to the current selection size + var diagonal = (Vec3.length(this.properties.dimensions) / 2) * 1.1; + var halfDimensions = Vec3.multiply(this.properties.dimensions, 0.5); + innerRadius = diagonal; + outerRadius = diagonal * 1.15; + var innerAlpha = 0.2; + var outerAlpha = 0.2; + + this.rotateOverlayTarget = Overlays.addOverlay("circle3d", { + position: this.properties.position, + size: rotateOverlayTargetSize, + color: { red: 0, green: 0, blue: 0 }, + alpha: 0.0, + solid: true, + visible: true, + rotation: yawOverlayRotation, + ignoreRayIntersection: false + }); + + this.rotateOverlayInner = Overlays.addOverlay("circle3d", { + position: this.properties.position, + size: innerRadius, + innerRadius: 0.9, + alpha: innerAlpha, + color: { red: 51, green: 152, blue: 203 }, + solid: true, + visible: displayRotateTargets, + rotation: yawOverlayRotation, + hasTickMarks: true, + majorTickMarksAngle: innerSnapAngle, + minorTickMarksAngle: 0, + majorTickMarksLength: -0.25, + minorTickMarksLength: 0, + majorTickMarksColor: { red: 0, green: 0, blue: 0 }, + minorTickMarksColor: { red: 0, green: 0, blue: 0 }, + ignoreRayIntersection: true, // always ignore this + }); + + this.rotateOverlayOuter = Overlays.addOverlay("circle3d", { + position: this.properties.position, + size: outerRadius, + innerRadius: 0.9, + startAt: 0, + endAt: 360, + alpha: outerAlpha, + color: { red: 51, green: 152, blue: 203 }, + solid: true, + visible: displayRotateTargets, + rotation: yawOverlayRotation, + + hasTickMarks: true, + majorTickMarksAngle: 45.0, + minorTickMarksAngle: 5, + majorTickMarksLength: 0.25, + minorTickMarksLength: 0.1, + majorTickMarksColor: { red: 0, green: 0, blue: 0 }, + minorTickMarksColor: { red: 0, green: 0, blue: 0 }, + ignoreRayIntersection: true, // always ignore this + }); + + this.rotateOverlayCurrent = Overlays.addOverlay("circle3d", { + position: this.properties.position, + size: outerRadius, + startAt: 0, + endAt: 0, + innerRadius: 0.9, + color: { red: 224, green: 67, blue: 36}, + alpha: 0.8, + solid: true, + visible: displayRotateTargets, + rotation: yawOverlayRotation, + ignoreRayIntersection: true, // always ignore this + hasTickMarks: true, + majorTickMarksColor: { red: 0, green: 0, blue: 0 }, + minorTickMarksColor: { red: 0, green: 0, blue: 0 }, + }); + + var pickRay = Camera.computePickRay(mouseEvent.x, mouseEvent.y) + var result = Overlays.findRayIntersection(pickRay); + yawZero = result.intersection; + + }; +}; + +Movable.prototype = { + preload : function(entityID) { + print("Movable.preload()"); + this.updateProperties(entityID); // All callbacks start by updating the properties + this.downloadSounds(); + }, + + clickDownOnEntity : function(entityID, mouseEvent) { + print("Movable.clickDownOnEntity()"); + + this.updateProperties(entityID); // All callbacks start by updating the properties + this.grab(mouseEvent); + + var nowDate = new Date(); + var nowMSecs = nowDate.getTime(); + this.clickedAt = nowMSecs; + this.firstHolding = true; + + this.clicked.x = mouseEvent.x; + this.clicked.y = mouseEvent.y; + this.lastMovedPosition.x = mouseEvent.x; + this.lastMovedPosition.y = mouseEvent.y; + this.lastMovedMSecs = nowMSecs; + + this.pickRandomSounds(); + }, + + holdingClickOnEntity : function(entityID, mouseEvent) { + print("Movable.holdingClickOnEntity()"); + + this.updateProperties(entityID); // All callbacks start by updating the properties + + if (this.firstHolding) { + // if we haven't moved yet... + if (this.clicked.x == mouseEvent.x && this.clicked.y == mouseEvent.y) { + var d = new Date(); + var now = d.getTime(); + + if (now - this.clickedAt > 500) { + this.displayRotateOverlay(mouseEvent); + this.firstHolding = false; + this.rotateMode = true; + this.originalRotation = this.properties.rotation; + } + } else { + this.firstHolding = false; + } + } + + if (this.rotateMode) { + this.rotate(mouseEvent); + } else { + this.move(mouseEvent); + } + + this.stopSoundIfNotMoving(mouseEvent); + }, + + clickReleaseOnEntity : function(entityID, mouseEvent) { + print("Movable.clickReleaseOnEntity()"); + this.updateProperties(entityID); // All callbacks start by updating the properties + if (this.rotateMode) { + this.rotate(mouseEvent); + } else { + this.release(mouseEvent); + } + + if (this.rotateOverlayTarget != null) { + this.cleanupRotateOverlay(); + this.rotateMode = false; + } + + this.firstHolding = false; + this.stopSound(); + }, +}; diff --git a/examples/entityScripts/sitOnEntity.js b/examples/entityScripts/sitOnEntity.js index d5c4fa9c52..080acf61a8 100644 --- a/examples/entityScripts/sitOnEntity.js +++ b/examples/entityScripts/sitOnEntity.js @@ -11,362 +11,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -(function(){ - - this.entityID = null; - this.properties = null; - this.standUpButton = null; - this.indicatorsAdded = false; - this.indicator = new Array(); - - - var buttonImageUrl = "https://worklist-prod.s3.amazonaws.com/attachment/0aca88e1-9bd8-5c1d.svg"; - var windowDimensions = Controller.getViewportDimensions(); - var buttonWidth = 37; - var buttonHeight = 46; - var buttonPadding = 50; // inside the normal toolbar position - var buttonPositionX = windowDimensions.x - buttonPadding - buttonWidth; - var buttonPositionY = (windowDimensions.y - buttonHeight) / 2 - (buttonHeight + buttonPadding); - - var passedTime = 0.0; - var startPosition = null; - var startRotation = null; - var animationLenght = 2.0; - - var avatarOldPosition = { x: 0, y: 0, z: 0 }; - - var sittingSettingsHandle = "SitJsSittingPosition"; - var sitting = Settings.getValue(sittingSettingsHandle, false) == "true"; - print("Original sitting status: " + sitting); - var frame = 0; - - var seat = new Object(); - var hiddingSeats = false; - - // This is the pose we would like to end up - var pose = [ - {joint:"RightUpLeg", rotation: {x:100.0, y:15.0, z:0.0}}, - {joint:"RightLeg", rotation: {x:-130.0, y:15.0, z:0.0}}, - {joint:"RightFoot", rotation: {x:30, y:15.0, z:0.0}}, - {joint:"LeftUpLeg", rotation: {x:100.0, y:-15.0, z:0.0}}, - {joint:"LeftLeg", rotation: {x:-130.0, y:-15.0, z:0.0}}, - {joint:"LeftFoot", rotation: {x:30, y:15.0, z:0.0}} - ]; - - var startPoseAndTransition = []; - - function storeStartPoseAndTransition() { - for (var i = 0; i < pose.length; i++){ - var startRotation = Quat.safeEulerAngles(MyAvatar.getJointRotation(pose[i].joint)); - var transitionVector = Vec3.subtract( pose[i].rotation, startRotation ); - startPoseAndTransition.push({joint: pose[i].joint, start: startRotation, transition: transitionVector}); - } - } - - function updateJoints(factor){ - for (var i = 0; i < startPoseAndTransition.length; i++){ - var scaledTransition = Vec3.multiply(startPoseAndTransition[i].transition, factor); - var rotation = Vec3.sum(startPoseAndTransition[i].start, scaledTransition); - MyAvatar.setJointData(startPoseAndTransition[i].joint, Quat.fromVec3Degrees( rotation )); - } - } - - var sittingDownAnimation = function(deltaTime) { - - passedTime += deltaTime; - var factor = passedTime/animationLenght; - - if ( passedTime <= animationLenght ) { - updateJoints(factor); - - var pos = { x: startPosition.x - 0.3 * factor, y: startPosition.y - 0.5 * factor, z: startPosition.z}; - MyAvatar.position = pos; - } else { - Script.update.disconnect(sittingDownAnimation); - if (seat.model) { - MyAvatar.setModelReferential(seat.model.id); - } - } - } - - var standingUpAnimation = function(deltaTime) { - - passedTime += deltaTime; - var factor = 1 - passedTime/animationLenght; - - if ( passedTime <= animationLenght ) { - - updateJoints(factor); - - var pos = { x: startPosition.x + 0.3 * (passedTime/animationLenght), y: startPosition.y + 0.5 * (passedTime/animationLenght), z: startPosition.z}; - MyAvatar.position = pos; - } else { - Script.update.disconnect(standingUpAnimation); - - } - } - - var externalThis = this; - - var goToSeatAnimation = function(deltaTime) { - passedTime += deltaTime; - var factor = passedTime/animationLenght; - - if (passedTime <= animationLenght) { - var targetPosition = Vec3.sum(seat.position, { x: 0.3, y: 0.5, z: 0 }); - MyAvatar.position = Vec3.sum(Vec3.multiply(startPosition, 1 - factor), Vec3.multiply(targetPosition, factor)); - } else if (passedTime <= 2 * animationLenght) { - //Quat.print("MyAvatar: ", MyAvatar.orientation); - //Quat.print("Seat: ", seat.rotation); - MyAvatar.orientation = Quat.mix(startRotation, seat.rotation, factor - 1); - } else { - Script.update.disconnect(goToSeatAnimation); - externalThis.sitDown(); - externalThis.showIndicators(false); - } - } - - var globalMouseClick = function(event) { - var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); - if (clickedOverlay == externalThis.standUpButton) { - seat.model = null; - externalThis.standUp(); - Controller.mousePressEvent.disconnect(globalMouseClick); - } - }; - - this.sitDown = function() { - sitting = true; - Settings.setValue(sittingSettingsHandle, sitting); - print("sitDown sitting status: " + Settings.getValue(sittingSettingsHandle, false)); - passedTime = 0.0; - startPosition = MyAvatar.position; - storeStartPoseAndTransition(); - try { - Script.update.disconnect(standingUpAnimation); - } catch(e){ - // no need to handle. if it wasn't connected no harm done - } - Script.update.connect(sittingDownAnimation); - Overlays.editOverlay(this.standUpButton, { visible: true }); - Controller.mousePressEvent.connect(globalMouseClick); - } - - this.standUp = function() { - sitting = false; - Settings.setValue(sittingSettingsHandle, sitting); - print("standUp sitting status: " + Settings.getValue(sittingSettingsHandle, false)); - passedTime = 0.0; - startPosition = MyAvatar.position; - MyAvatar.clearReferential(); - try{ - Script.update.disconnect(sittingDownAnimation); - } catch (e){} - Script.update.connect(standingUpAnimation); - Overlays.editOverlay(this.standUpButton, { visible: false }); - Controller.mousePressEvent.disconnect(globalMouseClick); - } - - function SeatIndicator(modelProperties, seatIndex) { - var halfDiagonal = Vec3.length(modelProperties.dimensions) / 2.0; - - this.position = Vec3.sum(modelProperties.position, - Vec3.multiply(Vec3.multiplyQbyV(modelProperties.rotation, modelProperties.sittingPoints[seatIndex].position), - halfDiagonal)); // hack - - this.orientation = Quat.multiply(modelProperties.rotation, - modelProperties.sittingPoints[seatIndex].rotation); - this.scale = MyAvatar.scale / 3; - - this.sphere = Overlays.addOverlay("billboard", { - subImage: { x: 0, y: buttonHeight, width: buttonWidth, height: buttonHeight}, - url: buttonImageUrl, - position: this.position, - scale: this.scale, - size: this.scale, - solid: true, - color: { red: 255, green: 255, blue: 255 }, - alpha: 0.8, - visible: true, - isFacingAvatar: true - }); - - this.show = function(doShow) { - Overlays.editOverlay(this.sphere, { visible: doShow }); - } - - this.update = function() { - Overlays.editOverlay(this.sphere, { - position: this.position, - size: this.scale - }); - } - - this.cleanup = function() { - Overlays.deleteOverlay(this.sphere); - } - } - - function update(deltaTime){ - var newWindowDimensions = Controller.getViewportDimensions(); - if( newWindowDimensions.x != windowDimensions.x || newWindowDimensions.y != windowDimensions.y ){ - windowDimensions = newWindowDimensions; - var newX = windowDimensions.x - buttonPadding - buttonWidth; - var newY = (windowDimensions.y - buttonHeight) / 2 ; - Overlays.editOverlay( this.standUpButton, {x: newX, y: newY} ); - } - - // For a weird reason avatar joint don't update till the 10th frame - // Set the update frame to 20 to be safe - var UPDATE_FRAME = 20; - if (frame <= UPDATE_FRAME) { - if (frame == UPDATE_FRAME) { - if (sitting == true) { - print("Was seated: " + sitting); - storeStartPoseAndTransition(); - updateJoints(1.0); - } - } - frame++; - } - } - - this.addIndicators = function() { - if (!this.indicatorsAdded) { - if (this.properties.sittingPoints.length > 0) { - for (var i = 0; i < this.properties.sittingPoints.length; ++i) { - this.indicator[i] = new SeatIndicator(this.properties, i); - } - this.indicatorsAdded = true; - } - } - } - - this.removeIndicators = function() { - for (var i = 0; i < this.properties.sittingPoints.length; ++i) { - this.indicator[i].cleanup(); - } - } - - this.showIndicators = function(doShow) { - this.addIndicators(); - if (this.indicatorsAdded) { - for (var i = 0; i < this.properties.sittingPoints.length; ++i) { - this.indicator[i].show(doShow); - } - } - hiddingSeats = !doShow; - } - - function raySphereIntersection(origin, direction, center, radius) { - var A = origin; - var B = Vec3.normalize(direction); - var P = center; - - var x = Vec3.dot(Vec3.subtract(P, A), B); - var X = Vec3.sum(A, Vec3.multiply(B, x)); - var d = Vec3.length(Vec3.subtract(P, X)); - - return (x > 0 && d <= radius); - } - - this.cleanup = function() { - this.standUp(); - MyAvatar.clearReferential(); - for (var i = 0; i < pose.length; i++){ - MyAvatar.clearJointData(pose[i].joint); - } - Overlays.deleteOverlay(this.standUpButton); - for (var i = 0; i < this.indicator.length; ++i) { - this.indicator[i].cleanup(); - } - }; - - - this.createStandupButton = function() { - this.standUpButton = Overlays.addOverlay("image", { - x: buttonPositionX, y: buttonPositionY, width: buttonWidth, height: buttonHeight, - subImage: { x: buttonWidth, y: buttonHeight, width: buttonWidth, height: buttonHeight}, - imageURL: buttonImageUrl, - visible: false, - alpha: 1.0 - }); - }; - - this.handleClickEvent = function(event) { - var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); - - if (clickedOverlay == this.standUpButton) { - seat.model = null; - this.standUp(); - } else { - this.addIndicators(); - if (this.indicatorsAdded) { - var pickRay = Camera.computePickRay(event.x, event.y); - - var clickedOnSeat = false; - - for (var i = 0; i < this.properties.sittingPoints.length; ++i) { - if (raySphereIntersection(pickRay.origin, - pickRay.direction, - this.indicator[i].position, - this.indicator[i].scale / 2)) { - clickedOnSeat = true; - seat.model = this.entityID; // ?? - seat.position = this.indicator[i].position; - seat.rotation = this.indicator[i].orientation; - } - } - - if (clickedOnSeat) { - passedTime = 0.0; - startPosition = MyAvatar.position; - startRotation = MyAvatar.orientation; - try{ Script.update.disconnect(standingUpAnimation); } catch(e){} - try{ Script.update.disconnect(sittingDownAnimation); } catch(e){} - Script.update.connect(goToSeatAnimation); - } - } - } - }; - - - // All callbacks start by updating the properties - this.updateProperties = function(entityID) { - if (this.entityID === null || !this.entityID.isKnownID) { - this.entityID = Entities.identifyEntity(entityID); - } - this.properties = Entities.getEntityProperties(this.entityID); - }; - - this.unload = function(entityID) { - this.cleanup(); - Script.update.disconnect(update); - }; - - this.preload = function(entityID) { - this.updateProperties(entityID); // All callbacks start by updating the properties - this.createStandupButton(); - Script.update.connect(update); - }; - - - this.hoverOverEntity = function(entityID, mouseEvent) { - this.updateProperties(entityID); // All callbacks start by updating the properties - this.showIndicators(true); - }; - this.hoverLeaveEntity = function(entityID, mouseEvent) { - this.updateProperties(entityID); // All callbacks start by updating the properties - this.showIndicators(false); - }; - - this.clickDownOnEntity = function(entityID, mouseEvent) { - this.updateProperties(entityID); // All callbacks start by updating the properties - this.handleClickEvent(mouseEvent); - }; - - this.clickReleaseOnEntity = function(entityID, mouseEvent) { - this.updateProperties(entityID); // All callbacks start by updating the properties - }; - -}) \ No newline at end of file +(function() { + Script.include("sittableClass.js"); + return new Sittable(); +}) diff --git a/examples/entityScripts/sittable.js b/examples/entityScripts/sittable.js new file mode 100644 index 0000000000..0753a46d2a --- /dev/null +++ b/examples/entityScripts/sittable.js @@ -0,0 +1,15 @@ +// +// sittable.js +// examples/entityScripts +// +// Created by Brad Hefta-Gaub on 11/17/14. +// Copyright 2014 High Fidelity, Inc. +// +// 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("sittableClass.js"); + return new Sittable(); +}) diff --git a/examples/entityScripts/sittableClass.js b/examples/entityScripts/sittableClass.js new file mode 100644 index 0000000000..c16e026f96 --- /dev/null +++ b/examples/entityScripts/sittableClass.js @@ -0,0 +1,381 @@ +// +// sitOnEntity.js +// examples/entityScripts +// +// Created by Brad Hefta-Gaub on 11/1/14. +// Copyright 2014 High Fidelity, Inc. +// +// This is an example of an entity script for sitting. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +Sittable = function() { + + this.entityID = null; + this.properties = null; + this.standUpButton = null; + this.indicatorsAdded = false; + this.indicator = new Array(); + + + var buttonImageUrl = "https://worklist-prod.s3.amazonaws.com/attachment/0aca88e1-9bd8-5c1d.svg"; + var windowDimensions = Controller.getViewportDimensions(); + var buttonWidth = 37; + var buttonHeight = 46; + var buttonPadding = 50; // inside the normal toolbar position + var buttonPositionX = windowDimensions.x - buttonPadding - buttonWidth; + var buttonPositionY = (windowDimensions.y - buttonHeight) / 2 - (buttonHeight + buttonPadding); + + var passedTime = 0.0; + var startPosition = null; + var startRotation = null; + var animationLenght = 2.0; + + var avatarOldPosition = { x: 0, y: 0, z: 0 }; + + var sittingSettingsHandle = "SitJsSittingPosition"; + var sitting = Settings.getValue(sittingSettingsHandle, false) == "true"; + print("Original sitting status: " + sitting); + var frame = 0; + + var seat = new Object(); + var hiddingSeats = false; + + // This is the pose we would like to end up + var pose = [ + {joint:"RightUpLeg", rotation: {x:100.0, y:15.0, z:0.0}}, + {joint:"RightLeg", rotation: {x:-130.0, y:15.0, z:0.0}}, + {joint:"RightFoot", rotation: {x:30, y:15.0, z:0.0}}, + {joint:"LeftUpLeg", rotation: {x:100.0, y:-15.0, z:0.0}}, + {joint:"LeftLeg", rotation: {x:-130.0, y:-15.0, z:0.0}}, + {joint:"LeftFoot", rotation: {x:30, y:15.0, z:0.0}} + ]; + + var startPoseAndTransition = []; + + function storeStartPoseAndTransition() { + for (var i = 0; i < pose.length; i++){ + var startRotation = Quat.safeEulerAngles(MyAvatar.getJointRotation(pose[i].joint)); + var transitionVector = Vec3.subtract( pose[i].rotation, startRotation ); + startPoseAndTransition.push({joint: pose[i].joint, start: startRotation, transition: transitionVector}); + } + } + + function updateJoints(factor){ + for (var i = 0; i < startPoseAndTransition.length; i++){ + var scaledTransition = Vec3.multiply(startPoseAndTransition[i].transition, factor); + var rotation = Vec3.sum(startPoseAndTransition[i].start, scaledTransition); + MyAvatar.setJointData(startPoseAndTransition[i].joint, Quat.fromVec3Degrees( rotation )); + } + } + + var sittingDownAnimation = function(deltaTime) { + + passedTime += deltaTime; + var factor = passedTime/animationLenght; + + if ( passedTime <= animationLenght ) { + updateJoints(factor); + + var pos = { x: startPosition.x - 0.3 * factor, y: startPosition.y - 0.5 * factor, z: startPosition.z}; + MyAvatar.position = pos; + } else { + Script.update.disconnect(sittingDownAnimation); + if (seat.model) { + MyAvatar.setModelReferential(seat.model.id); + } + } + } + + var standingUpAnimation = function(deltaTime) { + + passedTime += deltaTime; + var factor = 1 - passedTime/animationLenght; + + if ( passedTime <= animationLenght ) { + + updateJoints(factor); + + var pos = { x: startPosition.x + 0.3 * (passedTime/animationLenght), y: startPosition.y + 0.5 * (passedTime/animationLenght), z: startPosition.z}; + MyAvatar.position = pos; + } else { + Script.update.disconnect(standingUpAnimation); + + } + } + + var externalThis = this; + + var goToSeatAnimation = function(deltaTime) { + passedTime += deltaTime; + var factor = passedTime/animationLenght; + + if (passedTime <= animationLenght) { + var targetPosition = Vec3.sum(seat.position, { x: 0.3, y: 0.5, z: 0 }); + MyAvatar.position = Vec3.sum(Vec3.multiply(startPosition, 1 - factor), Vec3.multiply(targetPosition, factor)); + } else if (passedTime <= 2 * animationLenght) { + //Quat.print("MyAvatar: ", MyAvatar.orientation); + //Quat.print("Seat: ", seat.rotation); + MyAvatar.orientation = Quat.mix(startRotation, seat.rotation, factor - 1); + } else { + Script.update.disconnect(goToSeatAnimation); + externalThis.sitDown(); + externalThis.showIndicators(false); + } + } + + var globalMouseClick = function(event) { + var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); + if (clickedOverlay == externalThis.standUpButton) { + seat.model = null; + externalThis.standUp(); + Controller.mousePressEvent.disconnect(globalMouseClick); + } + }; + + this.sitDown = function() { + sitting = true; + Settings.setValue(sittingSettingsHandle, sitting); + print("sitDown sitting status: " + Settings.getValue(sittingSettingsHandle, false)); + passedTime = 0.0; + startPosition = MyAvatar.position; + storeStartPoseAndTransition(); + try { + Script.update.disconnect(standingUpAnimation); + } catch(e){ + // no need to handle. if it wasn't connected no harm done + } + Script.update.connect(sittingDownAnimation); + Overlays.editOverlay(this.standUpButton, { visible: true }); + Controller.mousePressEvent.connect(globalMouseClick); + } + + this.standUp = function() { + sitting = false; + Settings.setValue(sittingSettingsHandle, sitting); + print("standUp sitting status: " + Settings.getValue(sittingSettingsHandle, false)); + passedTime = 0.0; + startPosition = MyAvatar.position; + MyAvatar.clearReferential(); + try{ + Script.update.disconnect(sittingDownAnimation); + } catch (e){} + Script.update.connect(standingUpAnimation); + Overlays.editOverlay(this.standUpButton, { visible: false }); + Controller.mousePressEvent.disconnect(globalMouseClick); + } + + function SeatIndicator(modelProperties, seatIndex) { + var halfDiagonal = Vec3.length(modelProperties.dimensions) / 2.0; + + this.position = Vec3.sum(modelProperties.position, + Vec3.multiply(Vec3.multiplyQbyV(modelProperties.rotation, modelProperties.sittingPoints[seatIndex].position), + halfDiagonal)); // hack + + this.orientation = Quat.multiply(modelProperties.rotation, + modelProperties.sittingPoints[seatIndex].rotation); + this.scale = MyAvatar.scale / 3; + + this.sphere = Overlays.addOverlay("billboard", { + subImage: { x: 0, y: buttonHeight, width: buttonWidth, height: buttonHeight}, + url: buttonImageUrl, + position: this.position, + scale: this.scale, + size: this.scale, + solid: true, + color: { red: 255, green: 255, blue: 255 }, + alpha: 0.8, + visible: true, + isFacingAvatar: true + }); + + this.show = function(doShow) { + Overlays.editOverlay(this.sphere, { visible: doShow }); + } + + this.update = function() { + Overlays.editOverlay(this.sphere, { + position: this.position, + size: this.scale + }); + } + + this.cleanup = function() { + Overlays.deleteOverlay(this.sphere); + } + } + + function update(deltaTime){ + var newWindowDimensions = Controller.getViewportDimensions(); + if( newWindowDimensions.x != windowDimensions.x || newWindowDimensions.y != windowDimensions.y ){ + windowDimensions = newWindowDimensions; + var newX = windowDimensions.x - buttonPadding - buttonWidth; + var newY = (windowDimensions.y - buttonHeight) / 2 ; + Overlays.editOverlay( this.standUpButton, {x: newX, y: newY} ); + } + + // For a weird reason avatar joint don't update till the 10th frame + // Set the update frame to 20 to be safe + var UPDATE_FRAME = 20; + if (frame <= UPDATE_FRAME) { + if (frame == UPDATE_FRAME) { + if (sitting == true) { + print("Was seated: " + sitting); + storeStartPoseAndTransition(); + updateJoints(1.0); + } + } + frame++; + } + } + + this.addIndicators = function() { + if (!this.indicatorsAdded) { + if (this.properties.sittingPoints.length > 0) { + for (var i = 0; i < this.properties.sittingPoints.length; ++i) { + this.indicator[i] = new SeatIndicator(this.properties, i); + } + this.indicatorsAdded = true; + } + } + } + + this.removeIndicators = function() { + for (var i = 0; i < this.properties.sittingPoints.length; ++i) { + this.indicator[i].cleanup(); + } + } + + this.showIndicators = function(doShow) { + this.addIndicators(); + if (this.indicatorsAdded) { + for (var i = 0; i < this.properties.sittingPoints.length; ++i) { + this.indicator[i].show(doShow); + } + } + hiddingSeats = !doShow; + } + + function raySphereIntersection(origin, direction, center, radius) { + var A = origin; + var B = Vec3.normalize(direction); + var P = center; + + var x = Vec3.dot(Vec3.subtract(P, A), B); + var X = Vec3.sum(A, Vec3.multiply(B, x)); + var d = Vec3.length(Vec3.subtract(P, X)); + + return (x > 0 && d <= radius); + } + + this.cleanup = function() { + this.standUp(); + MyAvatar.clearReferential(); + for (var i = 0; i < pose.length; i++){ + MyAvatar.clearJointData(pose[i].joint); + } + Overlays.deleteOverlay(this.standUpButton); + for (var i = 0; i < this.indicator.length; ++i) { + this.indicator[i].cleanup(); + } + }; + + + this.createStandupButton = function() { + this.standUpButton = Overlays.addOverlay("image", { + x: buttonPositionX, y: buttonPositionY, width: buttonWidth, height: buttonHeight, + subImage: { x: buttonWidth, y: buttonHeight, width: buttonWidth, height: buttonHeight}, + imageURL: buttonImageUrl, + visible: false, + alpha: 1.0 + }); + }; + + this.handleClickEvent = function(event) { + var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); + + if (clickedOverlay == this.standUpButton) { + seat.model = null; + this.standUp(); + } else { + this.addIndicators(); + if (this.indicatorsAdded) { + var pickRay = Camera.computePickRay(event.x, event.y); + + var clickedOnSeat = false; + + for (var i = 0; i < this.properties.sittingPoints.length; ++i) { + if (raySphereIntersection(pickRay.origin, + pickRay.direction, + this.indicator[i].position, + this.indicator[i].scale / 2)) { + clickedOnSeat = true; + seat.model = this.entityID; // ?? + seat.position = this.indicator[i].position; + seat.rotation = this.indicator[i].orientation; + } + } + + if (clickedOnSeat) { + passedTime = 0.0; + startPosition = MyAvatar.position; + startRotation = MyAvatar.orientation; + try{ Script.update.disconnect(standingUpAnimation); } catch(e){} + try{ Script.update.disconnect(sittingDownAnimation); } catch(e){} + Script.update.connect(goToSeatAnimation); + } + } + } + }; + + + // All callbacks start by updating the properties + this.updateProperties = function(entityID) { + if (this.entityID === null || !this.entityID.isKnownID) { + this.entityID = Entities.identifyEntity(entityID); + } + this.properties = Entities.getEntityProperties(this.entityID); + }; +}; + +Sittable.prototype = { + + unload : function(entityID) { + print("Sittable.unload()"); + this.cleanup(); + //Script.update.disconnect(update); + }, + + preload : function(entityID) { + print("Sittable.preload()"); + this.updateProperties(entityID); // All callbacks start by updating the properties + this.createStandupButton(); + //Script.update.connect(update); + }, + + + hoverOverEntity : function(entityID, mouseEvent) { + print("Sittable.hoverOverEntity()"); + this.updateProperties(entityID); // All callbacks start by updating the properties + this.showIndicators(true); + }, + + hoverLeaveEntity : function(entityID, mouseEvent) { + print("Sittable.hoverLeaveEntity()"); + this.updateProperties(entityID); // All callbacks start by updating the properties + this.showIndicators(false); + }, + + clickDownOnEntity : function(entityID, mouseEvent) { + print("Sittable.clickDownOnEntity()"); + this.updateProperties(entityID); // All callbacks start by updating the properties + this.handleClickEvent(mouseEvent); + }, + + clickReleaseOnEntity : function(entityID, mouseEvent) { + print("Sittable.clickReleaseOnEntity()"); + this.updateProperties(entityID); // All callbacks start by updating the properties + } +}; \ No newline at end of file