From 8ebf34e93fed02bb5771cb0ec2d5e137eec3a828 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 6 Aug 2015 15:15:11 -0700 Subject: [PATCH 01/49] Fix BillboardOverlay breaking when changing url --- interface/src/ui/overlays/BillboardOverlay.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/ui/overlays/BillboardOverlay.cpp b/interface/src/ui/overlays/BillboardOverlay.cpp index 58790c4722..9f1ffd619b 100644 --- a/interface/src/ui/overlays/BillboardOverlay.cpp +++ b/interface/src/ui/overlays/BillboardOverlay.cpp @@ -53,12 +53,12 @@ void BillboardOverlay::update(float deltatime) { } void BillboardOverlay::render(RenderArgs* args) { - if (!_texture) { + if (!_isLoaded) { _isLoaded = true; _texture = DependencyManager::get()->getTexture(_url); } - if (!_visible || !_texture || !_texture->isLoaded()) { + if (!_visible || !_texture->isLoaded()) { return; } From dd2c0be2d292a6a78875d180ae538beefdf21063 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 6 Aug 2015 15:43:02 -0700 Subject: [PATCH 02/49] Add check in BillboardOverlay::render to make sure _texture is valid --- interface/src/ui/overlays/BillboardOverlay.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/ui/overlays/BillboardOverlay.cpp b/interface/src/ui/overlays/BillboardOverlay.cpp index 9f1ffd619b..891969e86b 100644 --- a/interface/src/ui/overlays/BillboardOverlay.cpp +++ b/interface/src/ui/overlays/BillboardOverlay.cpp @@ -58,7 +58,7 @@ void BillboardOverlay::render(RenderArgs* args) { _texture = DependencyManager::get()->getTexture(_url); } - if (!_visible || !_texture->isLoaded()) { + if (!_visible || !_texture || !_texture->isLoaded()) { return; } From 23fea1f2217df45a90a93ca81b9eb8953f29f797 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Fri, 7 Aug 2015 16:59:50 -0700 Subject: [PATCH 03/49] working on toybox script, squeezeHands, and default mappings --- .../{squeezeHands2.js => squeezeHands.js} | 0 .../controllers/{handGrab.js => toybox.js} | 128 +++++++++++++----- .../src/input-plugins/SixenseManager.cpp | 4 +- .../input-plugins/ViveControllerManager.cpp | 4 +- 4 files changed, 101 insertions(+), 35 deletions(-) rename examples/controllers/{squeezeHands2.js => squeezeHands.js} (100%) rename examples/controllers/{handGrab.js => toybox.js} (72%) diff --git a/examples/controllers/squeezeHands2.js b/examples/controllers/squeezeHands.js similarity index 100% rename from examples/controllers/squeezeHands2.js rename to examples/controllers/squeezeHands.js diff --git a/examples/controllers/handGrab.js b/examples/controllers/toybox.js similarity index 72% rename from examples/controllers/handGrab.js rename to examples/controllers/toybox.js index 224e66e323..bf7bd87795 100644 --- a/examples/controllers/handGrab.js +++ b/examples/controllers/toybox.js @@ -11,6 +11,8 @@ // Script.include("http://s3.amazonaws.com/hifi-public/scripts/libraries/toolBars.js"); +HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; + var nullActionID = "00000000-0000-0000-0000-000000000000"; var controllerID; var controllerActive; @@ -20,7 +22,7 @@ var leftHandActionID = nullActionID; var rightHandActionID = nullActionID; var TRIGGER_THRESHOLD = 0.2; -var GRAB_RADIUS = 0.25; +var GRAB_RADIUS = 0.15; var LEFT_HAND_CLICK = Controller.findAction("LEFT_HAND_CLICK"); var RIGHT_HAND_CLICK = Controller.findAction("RIGHT_HAND_CLICK"); @@ -32,7 +34,7 @@ var leftHandGrabAction = LEFT_HAND_CLICK; var rightHandGrabValue = 0; var leftHandGrabValue = 0; -var prevRightHandGrabValue = 0; +var prevRightHandGrabValue = 0 var prevLeftHandGrabValue = 0; var grabColor = { red: 0, green: 255, blue: 0}; @@ -46,8 +48,8 @@ var toolBar = new ToolBar(0, 0, ToolBar.vertical, "highfidelity.toybox.toolbar", }); var BUTTON_SIZE = 32; -var SWORD_IMAGE = "https://hifi-public.s3.amazonaws.com/images/sword/sword.svg"; // replace this with a table icon -var CLEANUP_IMAGE = "http://s3.amazonaws.com/hifi-public/images/delete.png"; // cleanup table +var SWORD_IMAGE = "https://hifi-public.s3.amazonaws.com/images/sword/sword.svg"; // TODO: replace this with a table icon +var CLEANUP_IMAGE = "http://s3.amazonaws.com/hifi-public/images/delete.png"; var tableButton = toolBar.addOverlay("image", { width: BUTTON_SIZE, height: BUTTON_SIZE, @@ -61,20 +63,25 @@ var cleanupButton = toolBar.addOverlay("image", { alpha: 1 }); -var leftHandOverlay = Overlays.addOverlay("sphere", { +var overlays = true; +var leftHandOverlay; +var rightHandOverlay; +if (overlays) { + leftHandOverlay = Overlays.addOverlay("sphere", { position: MyAvatar.getLeftPalmPosition(), size: GRAB_RADIUS, color: releaseColor, alpha: 0.5, solid: false }); -var rightHandOverlay = Overlays.addOverlay("sphere", { + rightHandOverlay = Overlays.addOverlay("sphere", { position: MyAvatar.getRightPalmPosition(), size: GRAB_RADIUS, color: releaseColor, alpha: 0.5, solid: false }); +} var OBJECT_HEIGHT_OFFSET = 0.5; var MIN_OBJECT_SIZE = 0.05; @@ -86,9 +93,9 @@ var TABLE_DIMENSIONS = { }; var GRAVITY = { - x: 0, - y: -2, - z: 0 + x: 0.0, + y: -2.0, + z: 0.0 } var LEFT = 0; @@ -101,6 +108,8 @@ var tableEntities = Array(NUM_OBJECTS + 1); // Also includes table var VELOCITY_MAG = 0.3; +var entitiesToResize = []; + var MODELS = Array( { modelURL: "https://hifi-public.s3.amazonaws.com/ozan/props/sword/sword.fbx" }, { modelURL: "https://s3.amazonaws.com/hifi-public/marketplace/hificontent/Vehicles/clara/spaceshuttle.fbx" }, @@ -113,11 +122,20 @@ var MODELS = Array( { modelURL: "https://hifi-public.s3.amazonaws.com/marketplace/contents/029db3d4-da2c-4cb2-9c08-b9612ba576f5/02949063e7c4aed42ad9d1a58461f56d.fst?1427169842" }, { modelURL: "https://hifi-public.s3.amazonaws.com/models/props/MidCenturyModernLivingRoom/Interior/Bar.fbx" }, { modelURL: "https://hifi-public.s3.amazonaws.com/marketplace/contents/96124d04-d603-4707-a5b3-e03bf47a53b2/1431770eba362c1c25c524126f2970fb.fst?1436924721" } + // Complex models: // { modelURL: "https://s3.amazonaws.com/hifi-public/marketplace/hificontent/Architecture/sketchfab/cudillero.fbx" }, // { modelURL: "https://hifi-public.s3.amazonaws.com/ozan/sets/musicality/musicality.fbx" }, // { modelURL: "https://hifi-public.s3.amazonaws.com/ozan/sets/statelyHome/statelyHome.fbx" } ); +var COLLISION_SOUNDS = Array( + "http://public.highfidelity.io/sounds/Collisions-ballhitsandcatches/pingpong_TableBounceMono.wav", + "http://public.highfidelity.io/sounds/Collisions-ballhitsandcatches/billiards/collision1.wav" + ); + +var RESIZE_TIMER = 0.0; +var RESIZE_WAIT = 0.05; // 50 milliseconds + function letGo(hand) { var actionIDToRemove = (hand == LEFT) ? leftHandActionID : rightHandActionID; var entityIDToEdit = (hand == LEFT) ? leftHandObjectID : rightHandObjectID; @@ -126,6 +144,7 @@ function letGo(hand) { MyAvatar.getRightPalmAngularVelocity(); if (actionIDToRemove != nullActionID && entityIDToEdit != null) { Entities.deleteAction(entityIDToEdit, actionIDToRemove); + // TODO: upon successful letGo, restore collision groups if (hand == LEFT) { leftHandObjectID = null; leftHandActionID = nullActionID; @@ -143,11 +162,11 @@ function setGrabbedObject(hand) { var minDistance = GRAB_RADIUS; for (var i = 0; i < entities.length; i++) { if ((hand == LEFT && entities[i] == rightHandObjectID) || - (hand == RIGHT) && entities[i] == leftHandObjectID) { + (hand == RIGHT && entities[i] == leftHandObjectID)) { continue; } else { var distance = Vec3.distance(Entities.getEntityProperties(entities[i]).position, handPosition); - if (distance < minDistance) { + if (distance <= minDistance) { objectID = entities[i]; minDistance = distance; } @@ -170,15 +189,17 @@ function grab(hand) { } var objectID = (hand == LEFT) ? leftHandObjectID : rightHandObjectID; var handRotation = (hand == LEFT) ? MyAvatar.getLeftPalmRotation() : MyAvatar.getRightPalmRotation(); + var handPosition = (hand == LEFT) ? MyAvatar.getLeftPalmPosition() : MyAvatar.getRightPalmPosition(); var objectRotation = Entities.getEntityProperties(objectID).rotation; var offsetRotation = Quat.multiply(Quat.inverse(handRotation), objectRotation); + + var objectPosition = Entities.getEntityProperties(objectID).position; + var offset = Vec3.subtract(objectPosition, handPosition); + var offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, offsetRotation)), offset); + // print(JSON.stringify(offsetPosition)); var actionID = Entities.addAction("hold", objectID, { - relativePosition: { - x: 0.0, - y: 0.0, - z: 0.0 - }, + relativePosition: { x: 0, y: 0, z: 0 }, relativeRotation: offsetRotation, hand: (hand == LEFT) ? "left" : "right", timeScale: 0.05 @@ -190,7 +211,7 @@ function grab(hand) { rightHandObjectID = null; } } else { - // Entities.editEntity(objectID, { ignore}); + // TODO: upon successful grab, add to collision group so object doesn't collide with immovable entities if (hand == LEFT) { leftHandActionID = actionID; } else { @@ -199,28 +220,65 @@ function grab(hand) { } } -function update() { - Overlays.editOverlay(leftHandOverlay, { position: MyAvatar.getLeftPalmPosition() }); - Overlays.editOverlay(rightHandOverlay, { position: MyAvatar.getRightPalmPosition() }); +function resizeModels() { + var newEntitiesToResize = []; + for (var i = 0; i < entitiesToResize.length; i++) { + var naturalDimensions = Entities.getEntityProperties(entitiesToResize[i]).naturalDimensions; + if (naturalDimensions.x != 1.0 || naturalDimensions.y != 1.0 || naturalDimensions.z != 1.0) { + // bigger range of sizes for models + var dimensions = Vec3.multiply(randFloat(MIN_OBJECT_SIZE, 3.0*MAX_OBJECT_SIZE), Vec3.normalize(naturalDimensions)); + Entities.editEntity(entitiesToResize[i], { + dimensions: dimensions, + shapeType: "box" + }); + } else { + newEntitiesToResize.push(entitiesToResize[i]); + } + + } + entitiesToResize = newEntitiesToResize; +} + +function update(deltaTime) { + if (overlays) { + Overlays.editOverlay(leftHandOverlay, { position: MyAvatar.getLeftPalmPosition() }); + Overlays.editOverlay(rightHandOverlay, { position: MyAvatar.getRightPalmPosition() }); + } + + // if (tableCreated && RESIZE_TIMER < RESIZE_WAIT) { + // RESIZE_TIMER += deltaTime; + // } else if (tableCreated) { + // resizeModels(); + // } rightHandGrabValue = Controller.getActionValue(rightHandGrabAction); leftHandGrabValue = Controller.getActionValue(leftHandGrabAction); - if (rightHandGrabValue > TRIGGER_THRESHOLD && rightHandObjectID == null) { - Overlays.editOverlay(rightHandOverlay, { color: grabColor }); + if (rightHandGrabValue > TRIGGER_THRESHOLD && + prevRightHandGrabValue < TRIGGER_THRESHOLD) { + if (overlays) { + Overlays.editOverlay(rightHandOverlay, { color: grabColor }); + } grab(RIGHT); } else if (rightHandGrabValue < TRIGGER_THRESHOLD && prevRightHandGrabValue > TRIGGER_THRESHOLD) { - Overlays.editOverlay(rightHandOverlay, { color: releaseColor }); + if (overlays) { + Overlays.editOverlay(rightHandOverlay, { color: releaseColor }); + } letGo(RIGHT); } - if (leftHandGrabValue > TRIGGER_THRESHOLD && leftHandObjectID == null) { - Overlays.editOverlay(leftHandOverlay, { color: grabColor }); + if (leftHandGrabValue > TRIGGER_THRESHOLD && + prevLeftHandGrabValue < TRIGGER_THRESHOLD) { + if (overlays) { + Overlays.editOverlay(leftHandOverlay, { color: grabColor }); + } grab(LEFT); } else if (leftHandGrabValue < TRIGGER_THRESHOLD && prevLeftHandGrabValue > TRIGGER_THRESHOLD) { - Overlays.editOverlay(leftHandOverlay, { color: releaseColor }); + if (overlays) { + Overlays.editOverlay(leftHandOverlay, { color: releaseColor }); + } letGo(LEFT); } @@ -231,8 +289,10 @@ function update() { function cleanUp() { letGo(RIGHT); letGo(LEFT); - Overlays.deleteOverlay(leftHandOverlay); - Overlays.deleteOverlay(rightHandOverlay); + if (overlays) { + Overlays.deleteOverlay(leftHandOverlay); + Overlays.deleteOverlay(rightHandOverlay); + } removeTable(); toolBar.cleanup(); } @@ -268,11 +328,14 @@ randInt = function(low, high) { function createTable() { var tablePosition = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(MyAvatar.orientation))); tableEntities[0] = Entities.addEntity( { - type: "Box", + type: "Model", + shapeType: 'box', position: tablePosition, dimensions: TABLE_DIMENSIONS, rotation: MyAvatar.orientation, - color: { red: 255, green: 0, blue: 0 } + // color: { red: 102, green: 51, blue: 0 }, + modelURL: HIFI_PUBLIC_BUCKET + 'eric/models/woodFloor.fbx', + collisionSoundURL: "http://public.highfidelity.io/sounds/dice/diceCollide.wav" }); for (var i = 1; i < NUM_OBJECTS + 1; i++) { @@ -308,7 +371,8 @@ function createTable() { restitution: 0.01, density: 0.5, collisionsWillMove: true, - color: { red: randInt(0, 255), green: randInt(0, 255), blue: randInt(0, 255) } + color: { red: randInt(0, 255), green: randInt(0, 255), blue: randInt(0, 255) }, + // collisionSoundURL: COLLISION_SOUNDS[randInt(0, COLLISION_SOUNDS.length)] }); if (type == "Model") { var randModel = randInt(0, MODELS.length); @@ -316,11 +380,13 @@ function createTable() { shapeType: "box", modelURL: MODELS[randModel].modelURL }); + entitiesToResize.push(tableEntities[i]); } } } function removeTable() { + RESIZE_TIMER = 0.0; for (var i = 0; i < tableEntities.length; i++) { Entities.deleteEntity(tableEntities[i]); } diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp index 8283847918..e47521111c 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp @@ -535,8 +535,8 @@ void SixenseManager::assignDefaultInputMapping(UserInputMapper& mapper) { mapper.addInputChannel(UserInputMapper::LEFT_HAND, makeInput(LEFT_HAND)); mapper.addInputChannel(UserInputMapper::RIGHT_HAND, makeInput(RIGHT_HAND)); - mapper.addInputChannel(UserInputMapper::LEFT_HAND_CLICK, makeInput(BUTTON_FWD, 0)); - mapper.addInputChannel(UserInputMapper::RIGHT_HAND_CLICK, makeInput(BUTTON_FWD, 1)); + mapper.addInputChannel(UserInputMapper::LEFT_HAND_CLICK, makeInput(BACK_TRIGGER, 0)); + mapper.addInputChannel(UserInputMapper::RIGHT_HAND_CLICK, makeInput(BACK_TRIGGER, 1)); } diff --git a/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp b/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp index 6455b312e0..d871b8262f 100644 --- a/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp +++ b/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp @@ -394,8 +394,8 @@ void ViveControllerManager::assignDefaultInputMapping(UserInputMapper& mapper) { mapper.addInputChannel(UserInputMapper::ACTION1, makeInput(GRIP_BUTTON, 0)); mapper.addInputChannel(UserInputMapper::ACTION2, makeInput(GRIP_BUTTON, 1)); - mapper.addInputChannel(UserInputMapper::LEFT_HAND_CLICK, makeInput(TRIGGER_BUTTON, 0)); - mapper.addInputChannel(UserInputMapper::RIGHT_HAND_CLICK, makeInput(TRIGGER_BUTTON, 1)); + mapper.addInputChannel(UserInputMapper::LEFT_HAND_CLICK, makeInput(BACK_TRIGGER, 0)); + mapper.addInputChannel(UserInputMapper::RIGHT_HAND_CLICK, makeInput(BACK_TRIGGER, 1)); // Hands mapper.addInputChannel(UserInputMapper::LEFT_HAND, makeInput(LEFT_HAND)); From b77de1b598d442902a574bd8fe5a680496691176 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Fri, 7 Aug 2015 18:07:14 -0700 Subject: [PATCH 04/49] added fists to toybox --- examples/controllers/toybox.js | 37 ++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/examples/controllers/toybox.js b/examples/controllers/toybox.js index bf7bd87795..4c0925f3b6 100644 --- a/examples/controllers/toybox.js +++ b/examples/controllers/toybox.js @@ -63,7 +63,7 @@ var cleanupButton = toolBar.addOverlay("image", { alpha: 1 }); -var overlays = true; +var overlays = false; var leftHandOverlay; var rightHandOverlay; if (overlays) { @@ -136,6 +136,27 @@ var COLLISION_SOUNDS = Array( var RESIZE_TIMER = 0.0; var RESIZE_WAIT = 0.05; // 50 milliseconds +var leftFist = Entities.addEntity( { + type: "Sphere", + shapeType: 'sphere', + position: MyAvatar.getLeftPalmPosition(), + dimensions: { x: GRAB_RADIUS, y: GRAB_RADIUS, z: GRAB_RADIUS }, + rotation: MyAvatar.getLeftPalmRotation(), + visible: false, + collisionsWillMove: false, + ignoreForCollisions: true + }); +var rightFist = Entities.addEntity( { + type: "Sphere", + shapeType: 'sphere', + position: MyAvatar.getRightPalmPosition(), + dimensions: { x: GRAB_RADIUS, y: GRAB_RADIUS, z: GRAB_RADIUS }, + rotation: MyAvatar.getRightPalmRotation(), + visible: false, + collisionsWillMove: false, + ignoreForCollisions: true + }); + function letGo(hand) { var actionIDToRemove = (hand == LEFT) ? leftHandActionID : rightHandActionID; var entityIDToEdit = (hand == LEFT) ? leftHandObjectID : rightHandObjectID; @@ -161,8 +182,11 @@ function setGrabbedObject(hand) { var objectID = null; var minDistance = GRAB_RADIUS; for (var i = 0; i < entities.length; i++) { + // Don't grab the object in your other hands, your fists, or the table if ((hand == LEFT && entities[i] == rightHandObjectID) || - (hand == RIGHT && entities[i] == leftHandObjectID)) { + (hand == RIGHT && entities[i] == leftHandObjectID) || + entities[i] == leftFist || entities[i] == rightFist || + (tableCreated && entities[i] == tableEntities[0])) { continue; } else { var distance = Vec3.distance(Entities.getEntityProperties(entities[i]).position, handPosition); @@ -185,6 +209,8 @@ function setGrabbedObject(hand) { function grab(hand) { if (!setGrabbedObject(hand)) { + // If you don't grab an object, make a fist + Entities.editEntity((hand == LEFT) ? leftFist : rightFist, { ignoreForCollisions: false } ); return; } var objectID = (hand == LEFT) ? leftHandObjectID : rightHandObjectID; @@ -254,6 +280,9 @@ function update(deltaTime) { rightHandGrabValue = Controller.getActionValue(rightHandGrabAction); leftHandGrabValue = Controller.getActionValue(leftHandGrabAction); + Entities.editEntity(leftFist, { position: MyAvatar.getLeftPalmPosition() }); + Entities.editEntity(rightFist, { position: MyAvatar.getRightPalmPosition() }); + if (rightHandGrabValue > TRIGGER_THRESHOLD && prevRightHandGrabValue < TRIGGER_THRESHOLD) { if (overlays) { @@ -262,6 +291,7 @@ function update(deltaTime) { grab(RIGHT); } else if (rightHandGrabValue < TRIGGER_THRESHOLD && prevRightHandGrabValue > TRIGGER_THRESHOLD) { + Entities.editEntity(rightFist, { ignoreForCollisions: true } ); if (overlays) { Overlays.editOverlay(rightHandOverlay, { color: releaseColor }); } @@ -276,6 +306,7 @@ function update(deltaTime) { grab(LEFT); } else if (leftHandGrabValue < TRIGGER_THRESHOLD && prevLeftHandGrabValue > TRIGGER_THRESHOLD) { + Entities.editEntity(leftFist, { ignoreForCollisions: true } ); if (overlays) { Overlays.editOverlay(leftHandOverlay, { color: releaseColor }); } @@ -293,6 +324,8 @@ function cleanUp() { Overlays.deleteOverlay(leftHandOverlay); Overlays.deleteOverlay(rightHandOverlay); } + Entities.deleteEntity(leftFist); + Entities.deleteEntity(rightFist); removeTable(); toolBar.cleanup(); } From 488f6aa7d85f2c64f39408ac21a5bdfd46c7dd62 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 12 Aug 2015 13:34:23 -0700 Subject: [PATCH 05/49] fix bug in ElbowConstraint::apply() --- libraries/animation/src/ElbowConstraint.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/animation/src/ElbowConstraint.cpp b/libraries/animation/src/ElbowConstraint.cpp index 8c7fec7b6f..a6b2210644 100644 --- a/libraries/animation/src/ElbowConstraint.cpp +++ b/libraries/animation/src/ElbowConstraint.cpp @@ -67,7 +67,7 @@ bool ElbowConstraint::apply(glm::quat& rotation) const { // update rotation const float MIN_SWING_REAL_PART = 0.99999f; - if (twistWasClamped || fabsf(swingRotation.w < MIN_SWING_REAL_PART)) { + if (twistWasClamped || fabsf(swingRotation.w) < MIN_SWING_REAL_PART) { if (twistWasClamped) { twistRotation = glm::angleAxis(clampedTwistAngle, _axis); } From ba5346aee7e52e43fb1dcc69aeee84d76128faa4 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 12 Aug 2015 13:37:59 -0700 Subject: [PATCH 06/49] move GlmTestUtils.h to GLMTestUtils.h --- tests/physics/src/BulletUtilTests.cpp | 2 +- tests/physics/src/{GlmTestUtils.h => GLMTestUtils.h} | 6 +++--- tests/physics/src/MeshMassPropertiesTests.cpp | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) rename tests/physics/src/{GlmTestUtils.h => GLMTestUtils.h} (89%) diff --git a/tests/physics/src/BulletUtilTests.cpp b/tests/physics/src/BulletUtilTests.cpp index 181d22327e..a52e407aab 100644 --- a/tests/physics/src/BulletUtilTests.cpp +++ b/tests/physics/src/BulletUtilTests.cpp @@ -17,7 +17,7 @@ #include // Add additional qtest functionality (the include order is important!) -#include "GlmTestUtils.h" +#include "GLMTestUtils.h" #include "../QTestExtensions.h" // Constants diff --git a/tests/physics/src/GlmTestUtils.h b/tests/physics/src/GLMTestUtils.h similarity index 89% rename from tests/physics/src/GlmTestUtils.h rename to tests/physics/src/GLMTestUtils.h index 20bc14d5e0..ef63689419 100644 --- a/tests/physics/src/GlmTestUtils.h +++ b/tests/physics/src/GLMTestUtils.h @@ -1,5 +1,5 @@ // -// GlmTestUtils.h +// GLMTestUtils.h // tests/physics/src // // Created by Seiji Emery on 6/22/15 @@ -9,8 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#ifndef hifi_GlmTestUtils_h -#define hifi_GlmTestUtils_h +#ifndef hifi_GLMTestUtils_h +#define hifi_GLMTestUtils_h #include #include diff --git a/tests/physics/src/MeshMassPropertiesTests.cpp b/tests/physics/src/MeshMassPropertiesTests.cpp index e88bcae1b7..8295abfef8 100644 --- a/tests/physics/src/MeshMassPropertiesTests.cpp +++ b/tests/physics/src/MeshMassPropertiesTests.cpp @@ -16,7 +16,7 @@ // Add additional qtest functionality (the include order is important!) #include "BulletTestUtils.h" -#include "GlmTestUtils.h" +#include "GLMTestUtils.h" #include "../QTestExtensions.h" const btScalar acceptableRelativeError(1.0e-5f); From cae77cfd767c2788f15f791528c991096acc98fe Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 12 Aug 2015 13:51:40 -0700 Subject: [PATCH 07/49] move GLMTestUtils.h where all tests can find it --- tests/CMakeLists.txt | 4 ++-- tests/{physics/src => }/GLMTestUtils.h | 1 + tests/QTestExtensions.h | 2 ++ tests/physics/src/BulletUtilTests.cpp | 1 - tests/physics/src/MeshMassPropertiesTests.cpp | 1 - 5 files changed, 5 insertions(+), 4 deletions(-) rename tests/{physics/src => }/GLMTestUtils.h (99%) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index c352b55515..1593b649a0 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -12,7 +12,7 @@ foreach(DIR ${TEST_SUBDIRS}) endif() endforeach() -file(GLOB SHARED_TEST_HEADER_FILES "${CMAKE_CURRENT_SOURCE_DIR}/*.h" "${CMAKE_CURRENT_SOURCE_DIR}/*.hpp") +file(GLOB SHARED_TEST_HEADER_FILES "${CMAKE_CURRENT_SOURCE_DIR}/*.h " "${CMAKE_CURRENT_SOURCE_DIR}/*.hpp") add_custom_target("test-extensions" SOURCES "${SHARED_TEST_HEADER_FILES}") @@ -34,4 +34,4 @@ add_custom_target("all-tests" set_target_properties("all-tests" PROPERTIES FOLDER "hidden/test-targets") set_target_properties("all-tests" PROPERTIES EXCLUDE_FROM_DEFAULT_BUILD TRUE - EXCLUDE_FROM_ALL TRUE) \ No newline at end of file + EXCLUDE_FROM_ALL TRUE) diff --git a/tests/physics/src/GLMTestUtils.h b/tests/GLMTestUtils.h similarity index 99% rename from tests/physics/src/GLMTestUtils.h rename to tests/GLMTestUtils.h index ef63689419..6559e9f3c9 100644 --- a/tests/physics/src/GLMTestUtils.h +++ b/tests/GLMTestUtils.h @@ -20,6 +20,7 @@ inline float getErrorDifference(const glm::vec3& a, const glm::vec3& b) { return glm::distance(a, b); } + inline QTextStream& operator<<(QTextStream& stream, const glm::vec3& v) { return stream << "glm::vec3 { " << v.x << ", " << v.y << ", " << v.z << " }"; } diff --git a/tests/QTestExtensions.h b/tests/QTestExtensions.h index c034e9c290..d1918ebed8 100644 --- a/tests/QTestExtensions.h +++ b/tests/QTestExtensions.h @@ -15,6 +15,8 @@ #include #include +#include "GLMTestUtils.h" + // Implements several extensions to QtTest. // // Problems with QtTest: diff --git a/tests/physics/src/BulletUtilTests.cpp b/tests/physics/src/BulletUtilTests.cpp index a52e407aab..132d655274 100644 --- a/tests/physics/src/BulletUtilTests.cpp +++ b/tests/physics/src/BulletUtilTests.cpp @@ -17,7 +17,6 @@ #include // Add additional qtest functionality (the include order is important!) -#include "GLMTestUtils.h" #include "../QTestExtensions.h" // Constants diff --git a/tests/physics/src/MeshMassPropertiesTests.cpp b/tests/physics/src/MeshMassPropertiesTests.cpp index 8295abfef8..825721641f 100644 --- a/tests/physics/src/MeshMassPropertiesTests.cpp +++ b/tests/physics/src/MeshMassPropertiesTests.cpp @@ -16,7 +16,6 @@ // Add additional qtest functionality (the include order is important!) #include "BulletTestUtils.h" -#include "GLMTestUtils.h" #include "../QTestExtensions.h" const btScalar acceptableRelativeError(1.0e-5f); From 6dfbf7f70a01666ef822fb63fb9c361c6d23b517 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 12 Aug 2015 14:13:28 -0700 Subject: [PATCH 08/49] coalesce scattered dupes of unit-test utilities --- tests/GLMTestUtils.h | 34 +++++++++++++++++++ tests/QTestExtensions.h | 21 ++++++++++++ .../animation/src/RotationConstraintTests.cpp | 26 -------------- tests/shared/src/AngularConstraintTests.cpp | 10 ++---- tests/shared/src/GeometryUtilTests.cpp | 7 ---- tests/shared/src/MovingMinMaxAvgTests.cpp | 5 +++ tests/shared/src/MovingMinMaxAvgTests.h | 9 ----- tests/shared/src/TransformTests.cpp | 7 ++-- tests/shared/src/TransformTests.h | 27 --------------- 9 files changed, 67 insertions(+), 79 deletions(-) diff --git a/tests/GLMTestUtils.h b/tests/GLMTestUtils.h index 6559e9f3c9..7dbc4a840f 100644 --- a/tests/GLMTestUtils.h +++ b/tests/GLMTestUtils.h @@ -14,15 +14,49 @@ #include #include +#include // Implements functionality in QTestExtensions.h for glm types +// Computes the error value between two quaternions (using glm::dot) +float getErrorDifference(const glm::quat& a, const glm::quat& b) { + return fabsf(glm::dot(a, b)) - 1.0f; +} + inline float getErrorDifference(const glm::vec3& a, const glm::vec3& b) { return glm::distance(a, b); } +inline float getErrorDifference(const glm::mat4& a, const glm::mat4& b) { + float maxDiff = 0; + for (int i = 0; i < 4; ++i) { + for (int j = 0; j < 4; ++j) { + float diff = fabs(a[i][j] - b[i][j]); + maxDiff = std::max(diff, maxDiff); + } + } + return maxDiff; +} + inline QTextStream& operator<<(QTextStream& stream, const glm::vec3& v) { return stream << "glm::vec3 { " << v.x << ", " << v.y << ", " << v.z << " }"; } +QTextStream& operator<<(QTextStream& stream, const glm::quat& q) { + return stream << "glm::quat { " << q.x << ", " << q.y << ", " << q.z << ", " << q.w << " }"; +} + +inline QTextStream& operator<< (QTextStream& stream, const glm::mat4& matrix) { + stream << "[\n\t\t"; + stream.setFieldWidth(15); + for (int r = 0; r < 4; ++r) { + for (int c = 0; c < 4; ++c) { + stream << matrix[c][r]; + } + stream << "\n\t\t"; + } + stream.setFieldWidth(0); + stream << "]\n\t"; // hacky as hell, but this should work... + return stream; +} #endif diff --git a/tests/QTestExtensions.h b/tests/QTestExtensions.h index d1918ebed8..16e51b41ee 100644 --- a/tests/QTestExtensions.h +++ b/tests/QTestExtensions.h @@ -37,6 +37,10 @@ // +float getErrorDifference(const float& a, const float& b) { + return fabsf(a - b); +} + // Generates a QCOMPARE-style failure message that can be passed to QTest::qFail. // // Formatting looks like this: @@ -281,3 +285,20 @@ bool compareData (const char* data, const char* expectedData, size_t length) { #define COMPARE_DATA(actual, expected, length) \ QCOMPARE_WITH_EXPR((ByteData ( actual, length )), (ByteData ( expected, length )), compareData(actual, expected, length)) + + +// Produces a relative error test for float usable QCOMPARE_WITH_LAMBDA. +inline auto errorTest (float actual, float expected, float acceptableRelativeError) +-> std::function { + return [actual, expected, acceptableRelativeError] () { + if (fabsf(expected) <= acceptableRelativeError) { + return fabsf(actual - expected) < fabsf(acceptableRelativeError); + } + return fabsf((actual - expected) / expected) < fabsf(acceptableRelativeError); + }; +} + +#define QCOMPARE_WITH_RELATIVE_ERROR(actual, expected, relativeError) \ + QCOMPARE_WITH_LAMBDA(actual, expected, errorTest(actual, expected, relativeError)) + + diff --git a/tests/animation/src/RotationConstraintTests.cpp b/tests/animation/src/RotationConstraintTests.cpp index 2cdb44ee8f..4aac875f2a 100644 --- a/tests/animation/src/RotationConstraintTests.cpp +++ b/tests/animation/src/RotationConstraintTests.cpp @@ -16,34 +16,8 @@ #include #include -// HACK -- these helper functions need to be defined BEFORE including magic inside QTestExtensions.h -// TODO: fix QTestExtensions so we don't need to do this in every test. - -// Computes the error value between two quaternions (using glm::dot) -float getErrorDifference(const glm::quat& a, const glm::quat& b) { - return fabsf(glm::dot(a, b)) - 1.0f; -} - -QTextStream& operator<<(QTextStream& stream, const glm::quat& q) { - return stream << "glm::quat { " << q.x << ", " << q.y << ", " << q.z << ", " << q.w << " }"; -} - -// Produces a relative error test for float usable QCOMPARE_WITH_LAMBDA. -inline auto errorTest (float actual, float expected, float acceptableRelativeError) --> std::function { - return [actual, expected, acceptableRelativeError] () { - if (fabsf(expected) <= acceptableRelativeError) { - return fabsf(actual - expected) < fabsf(acceptableRelativeError); - } - return fabsf((actual - expected) / expected) < fabsf(acceptableRelativeError); - }; -} - #include "../QTestExtensions.h" -#define QCOMPARE_WITH_RELATIVE_ERROR(actual, expected, relativeError) \ - QCOMPARE_WITH_LAMBDA(actual, expected, errorTest(actual, expected, relativeError)) - QTEST_MAIN(RotationConstraintTests) diff --git a/tests/shared/src/AngularConstraintTests.cpp b/tests/shared/src/AngularConstraintTests.cpp index 4dcf3d7296..a711bac709 100644 --- a/tests/shared/src/AngularConstraintTests.cpp +++ b/tests/shared/src/AngularConstraintTests.cpp @@ -9,22 +9,16 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include "AngularConstraintTests.h" + #include #include #include #include -#include "AngularConstraintTests.h" #include "../QTestExtensions.h" -// Computes the error value between two quaternions (using glm::dot) -float getErrorDifference(const glm::quat& a, const glm::quat& b) { - return fabsf(glm::dot(a, b) - 1.0f); -} -QTextStream& operator<<(QTextStream& stream, const glm::quat& q) { - return stream << "glm::quat { " << q.x << ", " << q.y << ", " << q.z << ", " << q.w << " }"; -} QTEST_MAIN(AngularConstraintTests) diff --git a/tests/shared/src/GeometryUtilTests.cpp b/tests/shared/src/GeometryUtilTests.cpp index 44a540b478..eaf1e7cd8a 100644 --- a/tests/shared/src/GeometryUtilTests.cpp +++ b/tests/shared/src/GeometryUtilTests.cpp @@ -22,13 +22,6 @@ QTEST_MAIN(GeometryUtilTests) -float getErrorDifference(const float& a, const float& b) { - return fabsf(a - b); -} - -float getErrorDifference(const glm::vec3& a, const glm::vec3& b) { - return glm::distance(a, b); -} void GeometryUtilTests::testLocalRayRectangleIntersection() { glm::vec3 xAxis(1.0f, 0.0f, 0.0f); diff --git a/tests/shared/src/MovingMinMaxAvgTests.cpp b/tests/shared/src/MovingMinMaxAvgTests.cpp index 48c3c59058..f3ba3239ee 100644 --- a/tests/shared/src/MovingMinMaxAvgTests.cpp +++ b/tests/shared/src/MovingMinMaxAvgTests.cpp @@ -13,7 +13,12 @@ #include +#include #include +#include + +#include "../QTestExtensions.h" + QTEST_MAIN(MovingMinMaxAvgTests) diff --git a/tests/shared/src/MovingMinMaxAvgTests.h b/tests/shared/src/MovingMinMaxAvgTests.h index c64b087c15..1922bd207a 100644 --- a/tests/shared/src/MovingMinMaxAvgTests.h +++ b/tests/shared/src/MovingMinMaxAvgTests.h @@ -14,15 +14,6 @@ #include -inline float getErrorDifference(float a, float b) { - return fabsf(a - b); -} - -#include "../QTestExtensions.h" - -#include "MovingMinMaxAvg.h" -#include "SharedUtil.h" - class MovingMinMaxAvgTests : public QObject { private slots: diff --git a/tests/shared/src/TransformTests.cpp b/tests/shared/src/TransformTests.cpp index be22914b9d..b936d45555 100644 --- a/tests/shared/src/TransformTests.cpp +++ b/tests/shared/src/TransformTests.cpp @@ -10,10 +10,13 @@ #include "TransformTests.h" +#include +#include + +#include #include -#include "SharedLogging.h" -#include <../QTestExtensions.h> +#include "../QTestExtensions.h" using namespace glm; diff --git a/tests/shared/src/TransformTests.h b/tests/shared/src/TransformTests.h index a4d9b2a6c0..06cdbcd3cb 100644 --- a/tests/shared/src/TransformTests.h +++ b/tests/shared/src/TransformTests.h @@ -12,33 +12,6 @@ #define hifi_TransformTests_h #include -#include -#include - -inline float getErrorDifference(const glm::mat4& a, const glm::mat4& b) { - float maxDiff = 0; - for (int i = 0; i < 4; ++i) { - for (int j = 0; j < 4; ++j) { - float diff = fabs(a[i][j] - b[i][j]); - maxDiff = std::max(diff, maxDiff); - } - } - return maxDiff; -} - -inline QTextStream& operator<< (QTextStream& stream, const glm::mat4& matrix) { - stream << "[\n\t\t"; - stream.setFieldWidth(15); - for (int r = 0; r < 4; ++r) { - for (int c = 0; c < 4; ++c) { - stream << matrix[c][r]; - } - stream << "\n\t\t"; - } - stream.setFieldWidth(0); - stream << "]\n\t"; // hacky as hell, but this should work... - return stream; -} class TransformTests : public QObject { Q_OBJECT From f4e77b82002a6a6012e8f3615b34924dde155d2d Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 12 Aug 2015 14:15:49 -0700 Subject: [PATCH 09/49] remove another dupe unit-test util --- tests/physics/src/BulletTestUtils.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/physics/src/BulletTestUtils.h b/tests/physics/src/BulletTestUtils.h index 01a4fd5973..9166f80ba1 100644 --- a/tests/physics/src/BulletTestUtils.h +++ b/tests/physics/src/BulletTestUtils.h @@ -23,10 +23,6 @@ // (used by QCOMPARE_WITH_RELATIVE_ERROR via QCOMPARE_WITH_LAMBDA) // (this is only used by btMatrix3x3 in MeshMassPropertiesTests.cpp, so it's only defined for the Mat3 type) -// Return the error between values a and b; used to implement QCOMPARE_WITH_ABS_ERROR -inline btScalar getErrorDifference(const btScalar& a, const btScalar& b) { - return fabs(a - b); -} // Return the error between values a and b; used to implement QCOMPARE_WITH_ABS_ERROR inline btScalar getErrorDifference(const btVector3& a, const btVector3& b) { return (a - b).length(); From 0fa304423146df9afe0099c163d1e2737db9eca4 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 11 Aug 2015 16:44:24 -0700 Subject: [PATCH 10/49] QML framerate improvments --- interface/src/Application.cpp | 1 + interface/src/ui/ApplicationOverlay.cpp | 7 +- .../src/RenderableWebEntityItem.cpp | 51 +- .../src/RenderableWebEntityItem.h | 3 +- libraries/render-utils/CMakeLists.txt | 8 + libraries/render-utils/src/GLEscrow.cpp | 9 + libraries/render-utils/src/GLEscrow.h | 222 ++++++++ .../render-utils/src/OffscreenQmlSurface.cpp | 475 ++++++++++++------ .../render-utils/src/OffscreenQmlSurface.h | 23 +- libraries/render-utils/src/OglplusHelpers.cpp | 71 +++ libraries/render-utils/src/OglplusHelpers.h | 32 ++ libraries/ui/src/OffscreenUi.cpp | 4 +- tests/ui/src/main.cpp | 6 - 13 files changed, 714 insertions(+), 198 deletions(-) create mode 100644 libraries/render-utils/src/GLEscrow.cpp create mode 100644 libraries/render-utils/src/GLEscrow.h diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index dba2ed3234..ddef3396a2 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -77,6 +77,7 @@ #include #include #include +#include #include #include #include diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 80f5ce2b69..6f18cac127 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -46,12 +46,7 @@ ApplicationOverlay::ApplicationOverlay() // then release it back to the UI for re-use auto offscreenUi = DependencyManager::get(); connect(offscreenUi.data(), &OffscreenUi::textureUpdated, this, [&](GLuint textureId) { - auto offscreenUi = DependencyManager::get(); - offscreenUi->lockTexture(textureId); - std::swap(_uiTexture, textureId); - if (textureId) { - offscreenUi->releaseTexture(textureId); - } + _uiTexture = textureId; }); } diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index 5a08abbb22..ce5b389333 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -43,20 +43,12 @@ RenderableWebEntityItem::~RenderableWebEntityItem() { if (_webSurface) { _webSurface->pause(); _webSurface->disconnect(_connection); - // After the disconnect, ensure that we have the latest texture by acquiring the - // lock used when updating the _texture value - _textureLock.lock(); - _textureLock.unlock(); // The lifetime of the QML surface MUST be managed by the main thread // Additionally, we MUST use local variables copied by value, rather than // member variables, since they would implicitly refer to a this that // is no longer valid auto webSurface = _webSurface; - auto texture = _texture; - AbstractViewStateInterface::instance()->postLambdaEvent([webSurface, texture] { - if (texture) { - webSurface->releaseTexture(texture); - } + AbstractViewStateInterface::instance()->postLambdaEvent([webSurface] { webSurface->deleteLater(); }); } @@ -74,23 +66,7 @@ void RenderableWebEntityItem::render(RenderArgs* args) { _webSurface->resume(); _webSurface->getRootItem()->setProperty("url", _sourceUrl); _connection = QObject::connect(_webSurface, &OffscreenQmlSurface::textureUpdated, [&](GLuint textureId) { - _webSurface->lockTexture(textureId); - assert(!glGetError()); - // TODO change to atomic? - withLock(_textureLock, [&] { - std::swap(_texture, textureId); - }); - if (textureId) { - _webSurface->releaseTexture(textureId); - } - if (_texture) { - _webSurface->makeCurrent(); - glBindTexture(GL_TEXTURE_2D, _texture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glBindTexture(GL_TEXTURE_2D, 0); - _webSurface->doneCurrent(); - } + _texture = textureId; }); auto forwardMouseEvent = [=](const RayToEntityIntersectionResult& intersection, const QMouseEvent* event, unsigned int deviceId) { @@ -145,6 +121,19 @@ void RenderableWebEntityItem::render(RenderArgs* args) { point += 0.5f; point.y = 1.0f - point.y; point *= getDimensions() * METERS_TO_INCHES * DPI; + + if (event->button() == Qt::MouseButton::LeftButton) { + if (event->type() == QEvent::MouseButtonPress) { + this->_pressed = true; + this->_lastMove = ivec2((int)point.x, (int)point.y); + } else if (event->type() == QEvent::MouseButtonRelease) { + this->_pressed = false; + } + } + if (event->type() == QEvent::MouseMove) { + this->_lastMove = ivec2((int)point.x, (int)point.y); + } + // Forward the mouse event. QMouseEvent mappedEvent(event->type(), QPoint((int)point.x, (int)point.y), @@ -158,6 +147,16 @@ void RenderableWebEntityItem::render(RenderArgs* args) { QObject::connect(renderer, &EntityTreeRenderer::mousePressOnEntity, forwardMouseEvent); QObject::connect(renderer, &EntityTreeRenderer::mouseReleaseOnEntity, forwardMouseEvent); QObject::connect(renderer, &EntityTreeRenderer::mouseMoveOnEntity, forwardMouseEvent); + QObject::connect(renderer, &EntityTreeRenderer::hoverLeaveEntity, [=](const EntityItemID& entityItemID, const MouseEvent& event) { + if (this->_pressed && this->getID() == entityItemID) { + // If the user mouses off the entity while the button is down, simulate a mouse release + QMouseEvent mappedEvent(QEvent::MouseButtonRelease, + QPoint(_lastMove.x, _lastMove.y), + Qt::MouseButton::LeftButton, + Qt::MouseButtons(), Qt::KeyboardModifiers()); + QCoreApplication::sendEvent(_webSurface->getWindow(), &mappedEvent); + } + }); } glm::vec2 dims = glm::vec2(getDimensions()); diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.h b/libraries/entities-renderer/src/RenderableWebEntityItem.h index 47e847a166..ee9c2531f1 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.h +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.h @@ -34,7 +34,8 @@ private: QMetaObject::Connection _connection; uint32_t _texture{ 0 }; ivec2 _lastPress{ INT_MIN }; - QMutex _textureLock; + bool _pressed{ false }; + ivec2 _lastMove{ INT_MIN }; }; diff --git a/libraries/render-utils/CMakeLists.txt b/libraries/render-utils/CMakeLists.txt index 973e8b562a..0ea71e54e3 100644 --- a/libraries/render-utils/CMakeLists.txt +++ b/libraries/render-utils/CMakeLists.txt @@ -32,4 +32,12 @@ if (WIN32) endif() endif (WIN32) +add_dependency_external_projects(boostconfig) +find_package(BoostConfig REQUIRED) +target_include_directories(${TARGET_NAME} PUBLIC ${BOOSTCONFIG_INCLUDE_DIRS}) + +add_dependency_external_projects(oglplus) +find_package(OGLPLUS REQUIRED) +target_include_directories(${TARGET_NAME} PUBLIC ${OGLPLUS_INCLUDE_DIRS}) + link_hifi_libraries(animation fbx shared gpu model render environment) diff --git a/libraries/render-utils/src/GLEscrow.cpp b/libraries/render-utils/src/GLEscrow.cpp new file mode 100644 index 0000000000..253af35d92 --- /dev/null +++ b/libraries/render-utils/src/GLEscrow.cpp @@ -0,0 +1,9 @@ +// +// Created by Bradley Austin Davis on 2015/08/06. +// Copyright 2015 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 +// + +#include "GLEscrow.h" diff --git a/libraries/render-utils/src/GLEscrow.h b/libraries/render-utils/src/GLEscrow.h new file mode 100644 index 0000000000..54b124ae3c --- /dev/null +++ b/libraries/render-utils/src/GLEscrow.h @@ -0,0 +1,222 @@ +// +// Created by Bradley Austin Davis on 2015/08/06. +// Copyright 2015 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 +// + +#pragma once +#ifndef hifi_GLEscrow_h +#define hifi_GLEscrow_h + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +// The GLEscrow class provides a simple mechanism for producer GL contexts to provide +// content to a consumer where the consumer is assumed to be connected to a display and +// therefore must never be blocked. +// +// So we need to accomplish a few things. +// +// First the producer context needs to be able to supply content to the primary thread +// in such a way that the consumer only gets it when it's actually valid for reading +// (meaning that the async writing operations have been completed) +// +// Second, the client thread should be able to release the resource when it's finished +// using it (but again the reading of the resource is likely asyncronous) +// +// Finally, blocking operations need to be minimal, and any potentially blocking operations +// that can't be avoided need to be pushed to the submission context to avoid impacting +// the framerate of the consumer +// +// This class acts as a kind of border guard and holding pen between the two contexts +// to hold resources which the CPU is no longer using, but which might still be +// in use by the GPU. Fence sync objects are used to moderate the actual release of +// resources in either direction. +template < + typename T, + // Only accept numeric types + typename = typename std::enable_if::value, T>::type +> +class GLEscrow { +public: + + struct Item { + T _value; + GLsync _sync; + uint64_t _created; + + Item(T value, GLsync sync) : + _value(value), _sync(sync), _created(usecTimestampNow()) + { + } + + uint64_t age() { + return usecTimestampNow() - _created; + } + + bool signaled() { + auto result = glClientWaitSync(_sync, 0, 0); + if (GL_TIMEOUT_EXPIRED != result && GL_WAIT_FAILED != result) { + return true; + } + if (age() > (USECS_PER_SECOND / 2)) { + qWarning() << "Long unsignaled sync"; + } + return false; + } + }; + + using Mutex = std::mutex; + using Lock = std::unique_lock; + using Recycler = std::function; + // deque gives us random access, double ended push & pop and size, all in constant time + using Deque = std::deque; + using List = std::forward_list; + + void setRecycler(Recycler recycler) { + _recycler = recycler; + } + + // Submit a new resource from the producer context + // returns the number of prior submissions that were + // never consumed before becoming available. + // producers should self-limit if they start producing more + // work than is being consumed; + size_t submit(T t, GLsync writeSync = 0) { + if (!writeSync) { + // FIXME should the release and submit actually force the creation of a fence? + writeSync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + glFlush(); + } + + { + Lock lock(_mutex); + _submits.push_back(Item(t, writeSync)); + } + + return cleanTrash(); + } + + // Returns the next available resource provided by the submitter, + // or if none is available (which could mean either the submission + // list is empty or that the first item on the list isn't yet signaled + T fetch() { + T result{0}; + // On the one hand using try_lock() reduces the chance of blocking the consumer thread, + // but if the produce thread is going fast enough, it could effectively + // starve the consumer out of ever actually getting resources. + if (_mutex.try_lock()) { + if (signaled(_submits, 0)) { + result = _submits.at(0)._value; + _submits.pop_front(); + } + _mutex.unlock(); + } + return result; + } + + // If fetch returns a non-zero value, it's the responsibility of the + // client to release it at some point + void release(T t, GLsync readSync = 0) { + if (!readSync) { + // FIXME should the release and submit actually force the creation of a fence? + readSync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + glFlush(); + } + + Lock lock(_mutex); + _releases.push_back(Item(t, readSync)); + } + +private: + size_t cleanTrash() { + size_t wastedWork{ 0 }; + List trash; + { + // We only ever need one ready item available in the list, so if the + // second item is signaled (implying the first is as well, remove the first + // item. Iterate until the SECOND item in the list is not in the ready state + // The signaled function takes care of checking against the deque size + while (signaled(_submits, 1)) { + pop(_submits); + ++wastedWork; + } + + // Stuff in the release queue can be cleared out as soon as it's signaled + while (signaled(_releases, 0)) { + pop(_releases); + } + + trash.swap(_trash); + } + + // FIXME maybe doing a timing on the deleters and warn if it's taking excessive time? + // although we are out of the lock, so it shouldn't be blocking anything + std::for_each(trash.begin(), trash.end(), [&](typename List::const_reference item) { + if (item._value) { + _recycler(item._value); + } + if (item._sync) { + glDeleteSync(item._sync); + } + }); + return wastedWork; + } + + // May be called on any thread, but must be inside a locked section + void pop(Deque& deque) { + auto& item = deque.front(); + _trash.push_front(item); + deque.pop_front(); + } + + // May be called on any thread, but must be inside a locked section + bool signaled(Deque& deque, size_t i) { + if (i >= deque.size()) { + return false; + } + + auto& item = deque.at(i); + // If there's no sync object, either it's not required or it's already been found to be signaled + if (!item._sync) { + return true; + } + + // Check the sync value using a zero timeout to ensure we don't block + // This is critically important as this is the only GL function we'll call + // inside the locked sections, so it cannot have any latency + if (item.signaled()) { + // if the sync is signaled, queue it for deletion + _trash.push_front(Item(0, item._sync)); + // And change the stored value to 0 so we don't check it again + item._sync = 0; + return true; + } + + return false; + } + + Mutex _mutex; + Recycler _recycler; + // Items coming from the submission / writer context + Deque _submits; + // Items coming from the client context. + Deque _releases; + // Items which are no longer in use. + List _trash; +}; + +using GLTextureEscrow = GLEscrow; + +#endif + diff --git a/libraries/render-utils/src/OffscreenQmlSurface.cpp b/libraries/render-utils/src/OffscreenQmlSurface.cpp index d5c495b414..78edb8e899 100644 --- a/libraries/render-utils/src/OffscreenQmlSurface.cpp +++ b/libraries/render-utils/src/OffscreenQmlSurface.cpp @@ -6,21 +6,34 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #include "OffscreenQmlSurface.h" +#include "OglplusHelpers.h" -#include -#include -#include +#include #include #include #include #include #include #include +#include +#include -#include "FboCache.h" #include #include +#include "GLEscrow.h" +#include "OffscreenGlCanvas.h" +#include "AbstractViewStateInterface.h" + +// FIXME move to threaded rendering with Qt 5.5 +// #define QML_THREADED + +// Time between receiving a request to render the offscreen UI actually triggering +// the render. Could possibly be increased depending on the framerate we expect to +// achieve. +// This has the effect of capping the framerate at 200 +static const int MIN_TIMER_MS = 5; + class QMyQuickRenderControl : public QQuickRenderControl { protected: QWindow* renderWindow(QPoint* offset) Q_DECL_OVERRIDE{ @@ -35,119 +48,324 @@ protected: private: QWindow* _renderWindow{ nullptr }; + friend class OffscreenQmlRenderer; friend class OffscreenQmlSurface; }; -#include "AbstractViewStateInterface.h" + Q_DECLARE_LOGGING_CATEGORY(offscreenFocus) Q_LOGGING_CATEGORY(offscreenFocus, "hifi.offscreen.focus") -// Time between receiving a request to render the offscreen UI actually triggering -// the render. Could possibly be increased depending on the framerate we expect to -// achieve. -static const int MAX_QML_FRAMERATE = 10; -static const int MIN_RENDER_INTERVAL_US = USECS_PER_SECOND / MAX_QML_FRAMERATE; -static const int MIN_TIMER_MS = 5; +#ifdef QML_THREADED +static const QEvent::Type INIT = QEvent::Type(QEvent::User + 1); +static const QEvent::Type RENDER = QEvent::Type(QEvent::User + 2); +static const QEvent::Type RESIZE = QEvent::Type(QEvent::User + 3); +static const QEvent::Type STOP = QEvent::Type(QEvent::User + 4); +static const QEvent::Type UPDATE = QEvent::Type(QEvent::User + 5); +#endif + +class OffscreenQmlRenderer : public OffscreenGlCanvas { + friend class OffscreenQmlSurface; +public: + + OffscreenQmlRenderer(OffscreenQmlSurface* surface, QOpenGLContext* shareContext) : _surface(surface) { + OffscreenGlCanvas::create(shareContext); +#ifdef QML_THREADED + // Qt 5.5 + // _renderControl->prepareThread(_renderThread); + _context->moveToThread(&_thread); + moveToThread(&_thread); + _thread.setObjectName("QML Thread"); + _thread.start(); + post(INIT); +#else + init(); +#endif + } + +#ifdef QML_THREADED + bool event(QEvent *e) + { + switch (int(e->type())) { + case INIT: + { + QMutexLocker lock(&_mutex); + init(); + } + return true; + case RENDER: + { + QMutexLocker lock(&_mutex); + render(&lock); + } + return true; + case RESIZE: + { + QMutexLocker lock(&_mutex); + resize(); + } + return true; + case STOP: + { + QMutexLocker lock(&_mutex); + cleanup(); + } + return true; + default: + return QObject::event(e); + } + } + + void post(const QEvent::Type& type) { + QCoreApplication::postEvent(this, new QEvent(type)); + } + +#endif + +private: + + void setupFbo() { + using namespace oglplus; + _textures.setSize(_size); + _depthStencil.reset(new Renderbuffer()); + Context::Bound(Renderbuffer::Target::Renderbuffer, *_depthStencil) + .Storage( + PixelDataInternalFormat::DepthComponent, + _size.x, _size.y); + + _fbo.reset(new Framebuffer()); + _fbo->Bind(Framebuffer::Target::Draw); + _fbo->AttachRenderbuffer(Framebuffer::Target::Draw, + FramebufferAttachment::Depth, *_depthStencil); + DefaultFramebuffer().Bind(Framebuffer::Target::Draw); + } -OffscreenQmlSurface::OffscreenQmlSurface() : - _renderControl(new QMyQuickRenderControl), _fboCache(new FboCache) { + + void init() { + _renderControl = new QMyQuickRenderControl(); + connect(_renderControl, &QQuickRenderControl::renderRequested, _surface, &OffscreenQmlSurface::requestRender); + connect(_renderControl, &QQuickRenderControl::sceneChanged, _surface, &OffscreenQmlSurface::requestUpdate); + + // Create a QQuickWindow that is associated with out render control. Note that this + // window never gets created or shown, meaning that it will never get an underlying + // native (platform) window. + QQuickWindow::setDefaultAlphaBuffer(true); + // Weirdness... QQuickWindow NEEDS to be created on the rendering thread, or it will refuse to render + // because it retains an internal 'context' object that retains the thread it was created on, + // regardless of whether you later move it to another thread. + _quickWindow = new QQuickWindow(_renderControl); + _quickWindow->setColor(QColor(255, 255, 255, 0)); + _quickWindow->setFlags(_quickWindow->flags() | static_cast(Qt::WA_TranslucentBackground)); + +#ifdef QML_THREADED + // However, because we want to use synchronous events with the quickwindow, we need to move it back to the main + // thread after it's created. + _quickWindow->moveToThread(qApp->thread()); +#endif + + if (!makeCurrent()) { + qWarning("Failed to make context current on render thread"); + return; + } + _renderControl->initialize(_context); + setupFbo(); + _escrow.setRecycler([this](GLuint texture){ + _textures.recycleTexture(texture); + }); + doneCurrent(); + } + + void cleanup() { + if (!makeCurrent()) { + qFatal("Failed to make context current on render thread"); + return; + } + _renderControl->invalidate(); + + _fbo.reset(); + _depthStencil.reset(); + _textures.clear(); + + doneCurrent(); + +#ifdef QML_THREADED + _context->moveToThread(QCoreApplication::instance()->thread()); + _cond.wakeOne(); +#endif + } + + void resize(const QSize& newSize) { + // Update our members + if (_quickWindow) { + _quickWindow->setGeometry(QRect(QPoint(), newSize)); + _quickWindow->contentItem()->setSize(newSize); + } + + // Qt bug in 5.4 forces this check of pixel ratio, + // even though we're rendering offscreen. + qreal pixelRatio = 1.0; + if (_renderControl && _renderControl->_renderWindow) { + pixelRatio = _renderControl->_renderWindow->devicePixelRatio(); + } else { + pixelRatio = AbstractViewStateInterface::instance()->getDevicePixelRatio(); + } + + uvec2 newOffscreenSize = toGlm(newSize * pixelRatio); + _textures.setSize(newOffscreenSize); + if (newOffscreenSize == _size) { + return; + } + _size = newOffscreenSize; + + // Clear out any fbos with the old size + if (!makeCurrent()) { + qWarning("Failed to make context current on render thread"); + return; + } + + qDebug() << "Offscreen UI resizing to " << newSize.width() << "x" << newSize.height() << " with pixel ratio " << pixelRatio; + setupFbo(); + doneCurrent(); + } + + void render(QMutexLocker *lock) { + if (_surface->_paused) { + return; + } + + if (!makeCurrent()) { + qWarning("Failed to make context current on render thread"); + return; + } + + Q_ASSERT(toGlm(_quickWindow->geometry().size()) == _size); + //Q_ASSERT(toGlm(_quickWindow->geometry().size()) == _textures._size); + + _renderControl->sync(); +#ifdef QML_THREADED + _cond.wakeOne(); + lock->unlock(); +#endif + + + using namespace oglplus; + + _quickWindow->setRenderTarget(GetName(*_fbo), QSize(_size.x, _size.y)); + + TexturePtr texture = _textures.getNextTexture(); + _fbo->Bind(Framebuffer::Target::Draw); + _fbo->AttachTexture(Framebuffer::Target::Draw, FramebufferAttachment::Color, *texture, 0); + _fbo->Complete(Framebuffer::Target::Draw); + //Context::Clear().ColorBuffer(); + { + _renderControl->render(); + // FIXME The web browsers seem to be leaving GL in an error state. + // Need a debug context with sync logging to figure out why. + // for now just clear the errors + glGetError(); + } + // FIXME probably unecessary + DefaultFramebuffer().Bind(Framebuffer::Target::Draw); + _quickWindow->resetOpenGLState(); + _escrow.submit(GetName(*texture)); + _lastRenderTime = usecTimestampNow(); + } + + void aboutToQuit() { +#ifdef QML_THREADED + QMutexLocker lock(&_quitMutex); + _quit = true; +#endif + } + + void stop() { +#ifdef QML_THREADED + QMutexLocker lock(&_quitMutex); + post(STOP); + _cond.wait(&_mutex); +#else + cleanup(); +#endif + } + + bool allowNewFrame(uint8_t fps) { + auto minRenderInterval = USECS_PER_SECOND / fps; + auto lastInterval = usecTimestampNow() - _lastRenderTime; + return (lastInterval > minRenderInterval); + } + + OffscreenQmlSurface* _surface{ nullptr }; + QQuickWindow* _quickWindow{ nullptr }; + QMyQuickRenderControl* _renderControl{ nullptr }; + +#ifdef QML_THREADED + QThread _thread; + QMutex _mutex; + QWaitCondition _cond; + QMutex _quitMutex; +#endif + + bool _quit; + FramebufferPtr _fbo; + RenderbufferPtr _depthStencil; + uvec2 _size{ 1920, 1080 }; + uint64_t _lastRenderTime{ 0 }; + TextureRecycler _textures; + GLTextureEscrow _escrow; +}; + +OffscreenQmlSurface::OffscreenQmlSurface() { } OffscreenQmlSurface::~OffscreenQmlSurface() { - // Make sure the context is current while doing cleanup. Note that we use the - // offscreen surface here because passing 'this' at this point is not safe: the - // underlying platform window may already be destroyed. To avoid all the trouble, use - // another surface that is valid for sure. - makeCurrent(); - - // Delete the render control first since it will free the scenegraph resources. - // Destroy the QQuickWindow only afterwards. - delete _renderControl; + _renderer->stop(); + delete _renderer; delete _qmlComponent; - delete _quickWindow; delete _qmlEngine; - - doneCurrent(); - delete _fboCache; } void OffscreenQmlSurface::create(QOpenGLContext* shareContext) { - OffscreenGlCanvas::create(shareContext); + _renderer = new OffscreenQmlRenderer(this, shareContext); - makeCurrent(); - - // Create a QQuickWindow that is associated with out render control. Note that this - // window never gets created or shown, meaning that it will never get an underlying - // native (platform) window. - QQuickWindow::setDefaultAlphaBuffer(true); - _quickWindow = new QQuickWindow(_renderControl); - _quickWindow->setColor(QColor(255, 255, 255, 0)); - _quickWindow->setFlags(_quickWindow->flags() | static_cast(Qt::WA_TranslucentBackground)); // Create a QML engine. _qmlEngine = new QQmlEngine; if (!_qmlEngine->incubationController()) { - _qmlEngine->setIncubationController(_quickWindow->incubationController()); + _qmlEngine->setIncubationController(_renderer->_quickWindow->incubationController()); } // When Quick says there is a need to render, we will not render immediately. Instead, // a timer with a small interval is used to get better performance. - _updateTimer.setSingleShot(true); + _updateTimer.setInterval(MIN_TIMER_MS); connect(&_updateTimer, &QTimer::timeout, this, &OffscreenQmlSurface::updateQuick); - - // Now hook up the signals. For simplicy we don't differentiate between - // renderRequested (only render is needed, no sync) and sceneChanged (polish and sync - // is needed too). - connect(_renderControl, &QQuickRenderControl::renderRequested, this, &OffscreenQmlSurface::requestRender); - connect(_renderControl, &QQuickRenderControl::sceneChanged, this, &OffscreenQmlSurface::requestUpdate); - -#ifdef DEBUG - connect(_quickWindow, &QQuickWindow::focusObjectChanged, [this]{ - qCDebug(offscreenFocus) << "New focus item " << _quickWindow->focusObject(); - }); - connect(_quickWindow, &QQuickWindow::activeFocusItemChanged, [this] { - qCDebug(offscreenFocus) << "New active focus item " << _quickWindow->activeFocusItem(); - }); -#endif + _updateTimer.start(); _qmlComponent = new QQmlComponent(_qmlEngine); - // Initialize the render control and our OpenGL resources. - makeCurrent(); - _renderControl->initialize(_context); } void OffscreenQmlSurface::resize(const QSize& newSize) { - // Qt bug in 5.4 forces this check of pixel ratio, - // even though we're rendering offscreen. - qreal pixelRatio = 1.0; +#ifdef QML_THREADED + QMutexLocker _locker(&(_renderer->_mutex)); +#endif + if (!_renderer || !_renderer->_quickWindow) { + QSize currentSize = _renderer->_quickWindow->geometry().size(); + if (newSize == currentSize) { + return; + } + } + _qmlEngine->rootContext()->setContextProperty("surfaceSize", newSize); - if (_renderControl && _renderControl->_renderWindow) { - pixelRatio = _renderControl->_renderWindow->devicePixelRatio(); - } else { - pixelRatio = AbstractViewStateInterface::instance()->getDevicePixelRatio(); - } - QSize newOffscreenSize = newSize * pixelRatio; - if (newOffscreenSize == _fboCache->getSize()) { - return; - } - // Clear out any fbos with the old size - makeCurrent(); - qDebug() << "Offscreen UI resizing to " << newSize.width() << "x" << newSize.height() << " with pixel ratio " << pixelRatio; - _fboCache->setSize(newSize * pixelRatio); - - if (_quickWindow) { - _quickWindow->setGeometry(QRect(QPoint(), newSize)); - _quickWindow->contentItem()->setSize(newSize); - } - - // Update our members if (_rootItem) { _rootItem->setSize(newSize); } - doneCurrent(); +#ifdef QML_THREADED + _renderer->post(RESIZE); +#else + _renderer->resize(newSize); +#endif } QQuickItem* OffscreenQmlSurface::getRootItem() { @@ -173,20 +391,11 @@ QObject* OffscreenQmlSurface::load(const QUrl& qmlSource, std::function MIN_RENDER_INTERVAL_US) { - _updateTimer.setInterval(MIN_TIMER_MS); - } else { - _updateTimer.setInterval((MIN_RENDER_INTERVAL_US - lastInterval) / USECS_PER_MSEC); - } - _updateTimer.start(); - } + _render = true; } QObject* OffscreenQmlSurface::finishQmlLoad(std::function f) { @@ -240,54 +449,38 @@ QObject* OffscreenQmlSurface::finishQmlLoad(std::functionsetParentItem(_quickWindow->contentItem()); - _rootItem->setSize(_quickWindow->renderTargetSize()); + _rootItem->setParentItem(_renderer->_quickWindow->contentItem()); + _rootItem->setSize(_renderer->_quickWindow->renderTargetSize()); return _rootItem; } - void OffscreenQmlSurface::updateQuick() { - PerformanceTimer perfTimer("qmlUpdate"); - if (_paused) { - return; - } - - if (!makeCurrent()) { + if (!_renderer || !_renderer->allowNewFrame(_maxFps)) { return; } - // Polish, synchronize and render the next frame (into our fbo). In this example - // everything happens on the same thread and therefore all three steps are performed - // in succession from here. In a threaded setup the render() call would happen on a - // separate thread. if (_polish) { - _renderControl->polishItems(); - _renderControl->sync(); + _renderer->_renderControl->polishItems(); _polish = false; } - QOpenGLFramebufferObject* fbo = _fboCache->getReadyFbo(); + if (_render) { +#ifdef QML_THREADED + _renderer->post(RENDER); +#else + _renderer->render(nullptr); +#endif + _render = false; + } - _quickWindow->setRenderTarget(fbo); - fbo->bind(); - - glClearColor(0, 0, 0, 1); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - _renderControl->render(); - // FIXME The web browsers seem to be leaving GL in an error state. - // Need a debug context with sync logging to figure out why. - // for now just clear the errors - glGetError(); - - _quickWindow->resetOpenGLState(); - - QOpenGLFramebufferObject::bindDefault(); - _lastRenderTime = usecTimestampNow(); - // Force completion of all the operations before we emit the texture as being ready for use - glFinish(); - - emit textureUpdated(fbo->texture()); + GLuint newTexture = _renderer->_escrow.fetch(); + if (newTexture) { + if (_currentTexture) { + _renderer->_escrow.release(_currentTexture); + } + _currentTexture = newTexture; + emit textureUpdated(_currentTexture); + } } QPointF OffscreenQmlSurface::mapWindowToUi(const QPointF& sourcePosition, QObject* sourceObject) { @@ -299,7 +492,7 @@ QPointF OffscreenQmlSurface::mapWindowToUi(const QPointF& sourcePosition, QObjec } vec2 offscreenPosition = toGlm(sourcePosition); offscreenPosition /= sourceSize; - offscreenPosition *= vec2(toGlm(_quickWindow->size())); + offscreenPosition *= vec2(toGlm(_renderer->_quickWindow->size())); return QPointF(offscreenPosition.x, offscreenPosition.y); } @@ -309,7 +502,7 @@ QPointF OffscreenQmlSurface::mapWindowToUi(const QPointF& sourcePosition, QObjec // bool OffscreenQmlSurface::eventFilter(QObject* originalDestination, QEvent* event) { - if (_quickWindow == originalDestination) { + if (_renderer->_quickWindow == originalDestination) { return false; } // Only intercept events while we're in an active state @@ -321,7 +514,7 @@ bool OffscreenQmlSurface::eventFilter(QObject* originalDestination, QEvent* even // Don't intercept our own events, or we enter an infinite recursion QObject* recurseTest = originalDestination; while (recurseTest) { - Q_ASSERT(recurseTest != _rootItem && recurseTest != _quickWindow); + Q_ASSERT(recurseTest != _rootItem && recurseTest != _renderer->_quickWindow); recurseTest = recurseTest->parent(); } #endif @@ -330,7 +523,7 @@ bool OffscreenQmlSurface::eventFilter(QObject* originalDestination, QEvent* even switch (event->type()) { case QEvent::Resize: { QResizeEvent* resizeEvent = static_cast(event); - QGLWidget* widget = dynamic_cast(originalDestination); + QWidget* widget = dynamic_cast(originalDestination); if (widget) { this->resize(resizeEvent->size()); } @@ -340,7 +533,7 @@ bool OffscreenQmlSurface::eventFilter(QObject* originalDestination, QEvent* even case QEvent::KeyPress: case QEvent::KeyRelease: { event->ignore(); - if (QCoreApplication::sendEvent(_quickWindow, event)) { + if (QCoreApplication::sendEvent(_renderer->_quickWindow, event)) { return event->isAccepted(); } break; @@ -353,7 +546,7 @@ bool OffscreenQmlSurface::eventFilter(QObject* originalDestination, QEvent* even wheelEvent->delta(), wheelEvent->buttons(), wheelEvent->modifiers(), wheelEvent->orientation()); mappedEvent.ignore(); - if (QCoreApplication::sendEvent(_quickWindow, &mappedEvent)) { + if (QCoreApplication::sendEvent(_renderer->_quickWindow, &mappedEvent)) { return mappedEvent.isAccepted(); } break; @@ -376,7 +569,7 @@ bool OffscreenQmlSurface::eventFilter(QObject* originalDestination, QEvent* even _qmlEngine->rootContext()->setContextProperty("lastMousePosition", transformedPos); } mappedEvent.ignore(); - if (QCoreApplication::sendEvent(_quickWindow, &mappedEvent)) { + if (QCoreApplication::sendEvent(_renderer->_quickWindow, &mappedEvent)) { return mappedEvent.isAccepted(); } break; @@ -389,14 +582,6 @@ bool OffscreenQmlSurface::eventFilter(QObject* originalDestination, QEvent* even return false; } -void OffscreenQmlSurface::lockTexture(int texture) { - _fboCache->lockTexture(texture); -} - -void OffscreenQmlSurface::releaseTexture(int texture) { - _fboCache->releaseTexture(texture); -} - void OffscreenQmlSurface::pause() { _paused = true; } @@ -411,13 +596,13 @@ bool OffscreenQmlSurface::isPaused() const { } void OffscreenQmlSurface::setProxyWindow(QWindow* window) { - _renderControl->_renderWindow = window; + _renderer->_renderControl->_renderWindow = window; } QQuickWindow* OffscreenQmlSurface::getWindow() { - return _quickWindow; + return _renderer->_quickWindow; } QSize OffscreenQmlSurface::size() const { - return _quickWindow->geometry().size(); + return _renderer->_quickWindow->geometry().size(); } diff --git a/libraries/render-utils/src/OffscreenQmlSurface.h b/libraries/render-utils/src/OffscreenQmlSurface.h index 6eb886044b..1a1827b63f 100644 --- a/libraries/render-utils/src/OffscreenQmlSurface.h +++ b/libraries/render-utils/src/OffscreenQmlSurface.h @@ -17,18 +17,18 @@ #include #include -#include "OffscreenGlCanvas.h" - class QWindow; class QMyQuickRenderControl; +class QOpenGLContext; class QQmlEngine; class QQmlContext; class QQmlComponent; class QQuickWindow; class QQuickItem; -class FboCache; -class OffscreenQmlSurface : public OffscreenGlCanvas { +class OffscreenQmlRenderer; + +class OffscreenQmlSurface : public QObject { Q_OBJECT public: @@ -45,6 +45,7 @@ public: return load(QUrl(qmlSourceFile), f); } + void setMaxFps(uint8_t maxFps) { _maxFps = maxFps; } // Optional values for event handling void setProxyWindow(QWindow* window); void setMouseTranslator(MouseTranslator mouseTranslator) { @@ -67,8 +68,6 @@ signals: public slots: void requestUpdate(); void requestRender(); - void lockTexture(int texture); - void releaseTexture(int texture); private: QObject* finishQmlLoad(std::function f); @@ -77,20 +76,20 @@ private: private slots: void updateQuick(); -protected: - QQuickWindow* _quickWindow{ nullptr }; - private: - QMyQuickRenderControl* _renderControl{ nullptr }; + friend class OffscreenQmlRenderer; + OffscreenQmlRenderer* _renderer{ nullptr }; QQmlEngine* _qmlEngine{ nullptr }; QQmlComponent* _qmlComponent{ nullptr }; QQuickItem* _rootItem{ nullptr }; QTimer _updateTimer; - FboCache* _fboCache; - quint64 _lastRenderTime{ 0 }; + uint32_t _currentTexture{ 0 }; + bool _render{ false }; bool _polish{ true }; bool _paused{ true }; + uint8_t _maxFps{ 60 }; MouseTranslator _mouseTranslator{ [](const QPointF& p) { return p; } }; + }; #endif diff --git a/libraries/render-utils/src/OglplusHelpers.cpp b/libraries/render-utils/src/OglplusHelpers.cpp index 7d4a2f18bf..86769436e6 100644 --- a/libraries/render-utils/src/OglplusHelpers.cpp +++ b/libraries/render-utils/src/OglplusHelpers.cpp @@ -7,6 +7,7 @@ // #include "OglplusHelpers.h" #include +#include using namespace oglplus; using namespace oglplus::shapes; @@ -317,3 +318,73 @@ ShapeWrapperPtr loadSphereSection(ProgramPtr program, float fov, float aspect, i new shapes::ShapeWrapper({ "Position", "TexCoord" }, SphereSection(fov, aspect, slices, stacks), *program) ); } + +void TextureRecycler::setSize(const uvec2& size) { + if (size == _size) { + return; + } + _size = size; + while (!_readyTextures.empty()) { + _readyTextures.pop(); + } + std::set toDelete; + std::for_each(_allTextures.begin(), _allTextures.end(), [&](Map::const_reference item) { + if (!item.second._active && item.second._size != _size) { + toDelete.insert(item.first); + } + }); + std::for_each(toDelete.begin(), toDelete.end(), [&](Map::key_type key) { + _allTextures.erase(key); + }); +} + +void TextureRecycler::clear() { + while (!_readyTextures.empty()) { + _readyTextures.pop(); + } + _allTextures.clear(); +} + +TexturePtr TextureRecycler::getNextTexture() { + using namespace oglplus; + if (_readyTextures.empty()) { + TexturePtr newTexture(new Texture()); + Context::Bound(oglplus::Texture::Target::_2D, *newTexture) + .MinFilter(TextureMinFilter::Linear) + .MagFilter(TextureMagFilter::Linear) + .WrapS(TextureWrap::ClampToEdge) + .WrapT(TextureWrap::ClampToEdge) + .Image2D( + 0, PixelDataInternalFormat::RGBA8, + _size.x, _size.y, + 0, PixelDataFormat::RGB, PixelDataType::UnsignedByte, nullptr + ); + GLuint texId = GetName(*newTexture); + _allTextures[texId] = TexInfo{ newTexture, _size }; + _readyTextures.push(newTexture); + } + + TexturePtr result = _readyTextures.front(); + _readyTextures.pop(); + + GLuint texId = GetName(*result); + auto& item = _allTextures[texId]; + item._active = true; + + return result; +} + +void TextureRecycler::recycleTexture(GLuint texture) { + Q_ASSERT(_allTextures.count(texture)); + auto& item = _allTextures[texture]; + Q_ASSERT(item._active); + item._active = false; + if (item._size != _size) { + // Buh-bye + _allTextures.erase(texture); + return; + } + + _readyTextures.push(item._tex); +} + diff --git a/libraries/render-utils/src/OglplusHelpers.h b/libraries/render-utils/src/OglplusHelpers.h index 569e0be7a3..99232b97cb 100644 --- a/libraries/render-utils/src/OglplusHelpers.h +++ b/libraries/render-utils/src/OglplusHelpers.h @@ -10,6 +10,10 @@ // FIXME support oglplus on all platforms // For now it's a convenient helper for Windows +#include +#include + + #include #include "GLMHelpers.h" @@ -33,6 +37,8 @@ #include "NumericalConstants.h" using FramebufferPtr = std::shared_ptr; +using RenderbufferPtr = std::shared_ptr; +using TexturePtr = std::shared_ptr; using ShapeWrapperPtr = std::shared_ptr; using BufferPtr = std::shared_ptr; using VertexArrayPtr = std::shared_ptr; @@ -151,3 +157,29 @@ protected: }; using BasicFramebufferWrapperPtr = std::shared_ptr; + +class TextureRecycler { +public: + void setSize(const uvec2& size); + void clear(); + TexturePtr getNextTexture(); + void recycleTexture(GLuint texture); + +private: + + struct TexInfo { + TexturePtr _tex; + uvec2 _size; + bool _active{ false }; + + TexInfo() {} + TexInfo(TexturePtr tex, const uvec2& size) : _tex(tex), _size(size) {} + }; + + using Map = std::map; + using Queue = std::queue; + + Map _allTextures; + Queue _readyTextures; + uvec2 _size{ 1920, 1080 }; +}; diff --git a/libraries/ui/src/OffscreenUi.cpp b/libraries/ui/src/OffscreenUi.cpp index d581f12473..a1f00ab5ad 100644 --- a/libraries/ui/src/OffscreenUi.cpp +++ b/libraries/ui/src/OffscreenUi.cpp @@ -38,8 +38,8 @@ public: // so I think it's OK for the time being. bool OffscreenUi::shouldSwallowShortcut(QEvent* event) { Q_ASSERT(event->type() == QEvent::ShortcutOverride); - QObject* focusObject = _quickWindow->focusObject(); - if (focusObject != _quickWindow && focusObject != getRootItem()) { + QObject* focusObject = getWindow()->focusObject(); + if (focusObject != getWindow() && focusObject != getRootItem()) { //qDebug() << "Swallowed shortcut " << static_cast(event)->key(); event->accept(); return true; diff --git a/tests/ui/src/main.cpp b/tests/ui/src/main.cpp index 3fe0f4c11d..dab22999d1 100644 --- a/tests/ui/src/main.cpp +++ b/tests/ui/src/main.cpp @@ -186,13 +186,7 @@ public: auto offscreenUi = DependencyManager::get(); offscreenUi->create(_context); connect(offscreenUi.data(), &OffscreenUi::textureUpdated, this, [this, offscreenUi](int textureId) { - offscreenUi->lockTexture(textureId); - assert(!glGetError()); - GLuint oldTexture = testQmlTexture; testQmlTexture = textureId; - if (oldTexture) { - offscreenUi->releaseTexture(oldTexture); - } }); makeCurrent(); From f64ab8e6b08cd0c552c780e1190b148fbc9bb69d Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 12 Aug 2015 17:52:54 -0700 Subject: [PATCH 11/49] add QCOMPARE_QUATS macro for easier tests --- tests/GLMTestUtils.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/GLMTestUtils.h b/tests/GLMTestUtils.h index 7dbc4a840f..f353255cc1 100644 --- a/tests/GLMTestUtils.h +++ b/tests/GLMTestUtils.h @@ -59,4 +59,8 @@ inline QTextStream& operator<< (QTextStream& stream, const glm::mat4& matrix) { stream << "]\n\t"; // hacky as hell, but this should work... return stream; } + +#define QCOMPARE_QUATS(rotationA, rotationB, angle) \ + QVERIFY(fabsf(1.0f - fabsf(glm::dot(rotationA, rotationB))) < 2.0f * sinf(angle)) + #endif From e27335fc5e1ec2ed35b2d8ec3c1779e1164c483b Mon Sep 17 00:00:00 2001 From: bwent Date: Tue, 4 Aug 2015 11:56:03 -0700 Subject: [PATCH 12/49] Refactoring to use Planet class, set up framework for UI --- examples/example/solarsystem.js | 471 ++++++++++++-------------------- 1 file changed, 172 insertions(+), 299 deletions(-) diff --git a/examples/example/solarsystem.js b/examples/example/solarsystem.js index 8d1f0c81e3..8711014335 100644 --- a/examples/example/solarsystem.js +++ b/examples/example/solarsystem.js @@ -18,39 +18,15 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -Script.include('../utilities/tools/cookies.js'); -Script.include('games/satellite.js'); +Script.include([ + '../utilities/tools/cookies.js'); + 'games/satellite.js' +]); var BASE_URL = "https://s3.amazonaws.com/hifi-public/marketplace/hificontent/Scripts/planets/planets/"; -var NUM_PLANETS = 8; - -var trailsEnabled = true; -var energyConserved = true; -var planetView = false; -var earthView = false; -var satelliteGame; - -var PANEL_X = 850; -var PANEL_Y = 600; -var BUTTON_SIZE = 20; -var PADDING = 20; - -var DAMPING = 0.0; -var LIFETIME = 6000; -var ERROR_THRESH = 2.0; -var TIME_STEP = 70.0; - -var MAX_POINTS_PER_LINE = 5; -var LINE_DIM = 10; -var LINE_WIDTH = 3.0; -var line; -var planetLines = []; -var trails = []; - var BOUNDS = 200; - // Alert user to move if they are too close to domain bounds if (MyAvatar.position.x < BOUNDS || MyAvatar.position.x > TREE_SCALE - BOUNDS || MyAvatar.position.y < BOUNDS || MyAvatar.position.y > TREE_SCALE - BOUNDS || MyAvatar.position.z < BOUNDS || MyAvatar.position.z > TREE_SCALE - BOUNDS) { Window.alert("Please move at least 200m away from domain bounds."); @@ -58,9 +34,21 @@ if (MyAvatar.position.x < BOUNDS || MyAvatar.position.x > TREE_SCALE - BOUNDS || } // Save intiial avatar and camera position -var startingPosition = MyAvatar.position; +var startingPosition = {x: 800, y: 800, z: 800}; +MyAvatar.position = startingPosition; var startFrame = Window.location.href; +/* + + +********************************** +TODO: create initial UI panel here + + + +*/ + + // Place the sun var MAX_RANGE = 80.0; var SUN_SIZE = 8.0; @@ -82,119 +70,124 @@ var theSun = Entities.addEntity({ collisionsWillMove: false }); -var planets = []; -var planet_properties = []; -// Reference values -var radius = 7.0; -var T_ref = 1.0; -var size = 1.0; -var M = 250.0; -var m = M * 0.000000333; -var G = (Math.pow(radius, 3.0) / Math.pow((T_ref / (2.0 * Math.PI)), 2.0)) / M; -var G_ref = G; -// Adjust size and distance as number of planets increases -var DELTA_RADIUS = 1.8; -var DELTA_SIZE = 0.2; +// Reference values for physical constants +var referenceRadius = 7.0; +var referencePeriod = 1.0; +var LARGE_BODY_MASS = 250.0; +var SMALL_BODY_MASS = LARGE_BODY_MASS * 0.000000333; +var GRAVITY = (Math.pow(referenceRadius, 3.0) / Math.pow((referencePeriod / (2.0 * Math.PI)), 2.0)) / LARGE_BODY_MASS; +var REFERENCE_GRAVITY = GRAVITY; -function initPlanets() { - for (var i = 0; i < NUM_PLANETS; ++i) { - var v0 = Math.sqrt((G * M) / radius); - var T = (2.0 * Math.PI) * Math.sqrt(Math.pow(radius, 3.0) / (G * M)); +var planetCount = 0; +var trails = []; - if (i == 0) { - var color = { - red: 255, - green: 255, - blue: 255 - }; - } else if (i == 1) { - var color = { - red: 255, - green: 160, - blue: 110 - }; - } else if (i == 2) { - var color = { - red: 10, - green: 150, - blue: 160 - }; - } else if (i == 3) { - var color = { - red: 180, - green: 70, - blue: 10 - }; - } else if (i == 4) { - var color = { - red: 250, - green: 140, - blue: 0 - }; - } else if (i == 5) { - var color = { - red: 235, - green: 215, - blue: 0 - }; - } else if (i == 6) { - var color = { - red: 135, - green: 205, - blue: 240 - }; - } else if (i == 7) { - var color = { - red: 30, - green: 140, - blue: 255 - }; - } +var Planet = function(name, trailColor, radius, size) { + this.index = 0; + this.name = name; + this.trailColor = trailColor; - var prop = { - radius: radius, - position: Vec3.sum(center, { - x: radius, - y: 0.0, - z: 0.0 - }), - lineColor: color, - period: T, - dimensions: size, - velocity: Vec3.multiply(v0, Vec3.normalize({ + this.radius = radius; + this.position = Vec3.sum(center, { x: this.radius, y: 0.0, z: 0.0 }); + this.period = (2.0 * Math.PI) * Math.sqrt(Math.pow(this.radius, 3.0) / (GRAVITY * LARGE_BODY_MASS)); + this.gravity = GRAVITY; + this.initialVelocity = Math.sqrt((GRAVITY * LARGE_BODY_MASS) / radius); + this.velocity = Vec3.multiply(this.initialVelocity, Vec3.normalize({ x: 0, y: -0.2, z: 0.9 - })) - }; - planet_properties.push(prop); + }); + this.dimensions = size; - planets.push(Entities.addEntity({ + this.planet = Entities.addEntity({ type: "Model", - modelURL: BASE_URL + (i + 1) + ".fbx", - position: prop.position, + modelURL: BASE_URL + name + ".fbx", + position: this.position, dimensions: { - x: prop.dimensions, - y: prop.dimensions, - z: prop.dimensions + x: this.dimensions, + y: this.dimensions, + z: this.dimensions }, - velocity: prop.velocity, + velocity: this.velocity, angularDamping: DAMPING, damping: DAMPING, ignoreCollisions: false, lifetime: LIFETIME, collisionsWillMove: true, - })); + })); - radius *= DELTA_RADIUS; - size += DELTA_SIZE; + this.label = new PlanetLabel(name, this.index); + + + this.computeAcceleration = function() { + var acc = -(this.gravity * LARGE_BODY_MASS) * Math.pow(this.radius, (-2.0)); + return acc; + }; + + this.update = function(deltaTime) { + var between = Vec3.subtract(this.position, center); + var speed = this.computeAcceleration(this.radius) * deltaTime; + var vel = Vec3.multiply(speed, Vec3.normalize(between)); + + // Update velocity and position + this.velocity = Vec3.sum(this.velocity, vel); + this.position = Vec3.sum(this.position, Vec3.multiply(this.velocity, deltaTime)); + Entities.editEntity(this.planet, { + velocity: this..velocity, + position: this..position + }); + }; + + this.resetTrails = function() { + elapsed = 0.0; + + this.trail = []; + this.lineStack = []; + //add the first line to both the line entity stack and the trail + trails.push(newLine(lineStack, this.position, this.period, this.lineColor)); + planetLines.push(lineStack); + + }; + + this.updateTrails = function() { + var point = this.position; + + var prop = Entities.getEntityProperties(this.lineStack[this.lineStack.length - 1]); + var linePos = prop.position; + + trails[index].push(computeLocalPoint(linePos, point)); + + Entities.editEntity(lineStack[lineStack.length - 1], { + linePoints: trails[i] + }); + if (trails[index].length === MAX_POINTS_PER_LINE) { + trails[index] = newLine(lineStack, point, this.period, this.lineColor); + } + }; + + this.adjustPeriod = function(alpha) { + // Update global G constant, period, poke velocity to new value + var ratio = this.last_alpha / alpha; + this.gravity = Math.pow(ratio, 2.0) * GRAVITY; + this.period = ratio * this.period; + this.velocity = Vec3.multiply(ratio, this.velocity); + + this.last_alpha = alpha; } + + this.index++; } -// Initialize planets -initPlanets(); +planets.push(new Planet("Mercury", {red: 255, green: 255, blue: 255}, 7.0, 1.0)); +planets.push(new Planet("Venus", {red: 255, green: 160, blue: 110}, 8.0, 1.2)); +planets.push(new Planet("Earth", {red: 10, green: 150, blue: 160}, 9.2, 1.6)); +planets.push(new Planet("Mars", {red: 180, green: 70, blue: 10}, 11.0, 2.0)); +planets.push(new Planet("Jupiter", {red: 250, green: 140, blue: 0}, 14.5, 4.3)); +planets.push(new Planet("Saturn", {red: 235, green: 215, blue: 0}, 21.0, 3.7)); +planets.push(new Planet("Uranus", {red: 135, green: 205, blue: 240}, 29.0, 4.0)); +planets.push(new Planet("Neptune", {red: 30, green: 140, blue: 255}, 35.0, 4.2)); +planets.push(new Planet("Pluto", {red: 255, green: 255, blue: 255}, 58.0, 3.2)); var labels = []; @@ -205,40 +198,13 @@ var LABEL_Y = 3.0; var LABEL_Z = 1.0; var LABEL_DIST = 8.0; var TEXT_HEIGHT = 1.0; -var sunLabel; -function showLabels() { - labelsShowing = true; - for (var i = 0; i < NUM_PLANETS; i++) { - var properties = planet_properties[i]; - var text; - if (i == 0) { - text = "Mercury"; - } else if (i == 1) { - text = "Venus"; - } else if (i == 2) { - text = "Earth"; - } else if (i == 3) { - text = "Mars"; - } else if (i == 4) { - text = "Jupiter"; - } else if (i == 5) { - text = "Saturn"; - } else if (i == 6) { - text = "Uranus"; - } else if (i == 7) { - text = "Neptune"; - } - - text = text + " Speed: " + Vec3.length(properties.velocity).toFixed(2); - - var labelPos = Vec3.sum(planet_properties[i].position, { - x: 0.0, - y: LABEL_DIST, - z: LABEL_DIST - }); - var linePos = planet_properties[i].position; - labelLines.push(Entities.addEntity({ +var PlanetLabel = function(name, index) { + var text = name + " Speed: " + Vec3.length(planets[index].velocity).toFixed(2); + var labelPos = Vec3.sum(planets[index].position, { x: 0.0, y: LABEL_DIST, z: LABEL_DIST }); + var linePos = planets[i].position; + + this.line = Entities.addEntity({ type: "Line", position: linePos, dimensions: { @@ -246,7 +212,7 @@ function showLabels() { y: 20, z: 20 }, - lineWidth: 3.0, + lineWidth: LINE_WIDTH, color: { red: 255, green: 255, @@ -256,10 +222,12 @@ function showLabels() { x: 0, y: 0, z: 0 - }, computeLocalPoint(linePos, labelPos)] - })); + }, + computeLocalPoint(linePos, labelPos)], + visible: false + }); - labels.push(Entities.addEntity({ + this.label = Entities.addEntity({ type: "Text", text: text, lineHeight: TEXT_HEIGHT, @@ -274,33 +242,33 @@ function showLabels() { green: 10, blue: 10 }, - textColor: { - red: 255, - green: 255, - blue: 255 - }, - faceCamera: true - })); + faceCamera: true, + visible: false + }); + labelLines.push(line); + labels.push(label); + + this.show = function() { + this.showing = true; + Entities.editEntity(this.line, {visible = true}); + Entities.editEntity(this.label, {visible = true}); } -} -function hideLabels() { - labelsShowing = false; - Entities.deleteEntity(sunLabel); - - for (var i = 0; i < NUM_PLANETS; ++i) { - Entities.deleteEntity(labelLines[i]); - Entities.deleteEntity(labels[i]); + this.hide = function() { + this.showing = false; + Entities.editEntity(this.line, {visible = false}); + Entities.editEntity(this.label, {visible = false}); } - labels = []; - labelLines = []; -} +} + var time = 0.0; var elapsed; -var counter = 0; var dt = 1.0 / TIME_STEP; +var planetLines = []; +var trails = []; + function update(deltaTime) { if (paused) { return; @@ -308,51 +276,15 @@ function update(deltaTime) { deltaTime = dt; time++; - for (var i = 0; i < NUM_PLANETS; ++i) { - var properties = planet_properties[i]; - var between = Vec3.subtract(properties.position, center); - var speed = getAcceleration(properties.radius) * deltaTime; - var vel = Vec3.multiply(speed, Vec3.normalize(between)); + for (var i = 0; i < planets.length; ++i) { + planets[i].update(); - // Update velocity and position - properties.velocity = Vec3.sum(properties.velocity, vel); - properties.position = Vec3.sum(properties.position, Vec3.multiply(properties.velocity, deltaTime)); - Entities.editEntity(planets[i], { - velocity: properties.velocity, - position: properties.position - }); - - - // Create new or update current trail if (trailsEnabled) { - var lineStack = planetLines[i]; - var point = properties.position; - var prop = Entities.getEntityProperties(lineStack[lineStack.length - 1]); - var linePos = prop.position; - - trails[i].push(computeLocalPoint(linePos, point)); - - Entities.editEntity(lineStack[lineStack.length - 1], { - linePoints: trails[i] - }); - if (trails[i].length === MAX_POINTS_PER_LINE) { - trails[i] = newLine(lineStack, point, properties.period, properties.lineColor); - } - } - - // Measure total energy every 10 updates, recalibrate velocity if necessary - if (energyConserved) { - if (counter % 10 === 0) { - var error = calcEnergyError(planets[i], properties.radius, properties.v0, properties.velocity, properties.position); - if (Math.abs(error) >= ERROR_THRESH) { - var speed = adjustVelocity(planets[i], properties.position); - properties.velocity = Vec3.multiply(speed, Vec3.normalize(properties.velocity)); - } - } + planets[i].updateTrails(); } + } - counter++; if (time % TIME_STEP == 0) { elapsed++; } @@ -363,23 +295,6 @@ function computeLocalPoint(linePos, worldPoint) { return localPoint; } -function getAcceleration(radius) { - var acc = -(G * M) * Math.pow(radius, (-2.0)); - return acc; -} - -// Create a new trail -function resetTrails(planetIndex) { - elapsed = 0.0; - var properties = planet_properties[planetIndex]; - - var trail = []; - var lineStack = []; - - //add the first line to both the line entity stack and the trail - trails.push(newLine(lineStack, properties.position, properties.period, properties.lineColor)); - planetLines.push(lineStack); -} // Create a new line function newLine(lineStack, point, period, color) { @@ -416,43 +331,6 @@ function newLine(lineStack, point, period, color) { return points; } -// Measure energy error, recalculate velocity to return to initial net energy -var totalEnergy; -var measuredEnergy; -var measuredPE; - -function calcEnergyError(planet, radius, v0, v, pos) { - totalEnergy = 0.5 * M * Math.pow(v0, 2.0) - ((G * M * m) / radius); - measuredEnergy = 0.5 * M * Math.pow(v, 2.0) - ((G * M * m) / Vec3.length(Vec3.subtract(center, pos))); - var error = ((measuredEnergy - totalEnergy) / totalEnergy) * 100; - return error; -} - -function adjustVelocity(planet, pos) { - var measuredPE = -(G * M * m) / Vec3.length(Vec3.subtract(center, pos)); - return Math.sqrt(2 * (totalEnergy - measuredPE) / M); -} - - -// Allow user to toggle pausing the model, switch to planet view -var pauseButton = Overlays.addOverlay("text", { - backgroundColor: { - red: 200, - green: 200, - blue: 255 - }, - text: "Pause", - x: PANEL_X, - y: PANEL_Y - 30, - width: 70, - height: 20, - alpha: 1.0, - backgroundAlpha: 0.5, - visible: true -}); - -var paused = false; - function mousePressEvent(event) { var clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, @@ -506,6 +384,7 @@ function keyPressEvent(event) { } } +//switch to planet view function mouseDoublePressEvent(event) { if (earthView) { return; @@ -543,14 +422,20 @@ function mouseDoublePressEvent(event) { } } +// UI ELEMENTS + + +// USE FLOATING UI PANEL TO IMPROVE UI EXPERIENCE + +var paused = false; + // Create UI panel var panel = new Panel(PANEL_X, PANEL_Y); -var panelItems = []; var g_multiplier = 1.0; -panelItems.push(panel.newSlider("Adjust Gravitational Force: ", 0.1, 5.0, +panel.newSlider("Adjust Gravitational Force: ", 0.1, 5.0, function(value) { g_multiplier = value; G = G_ref * g_multiplier; @@ -565,7 +450,17 @@ panelItems.push(panel.newSlider("Adjust Gravitational Force: ", 0.1, 5.0, var period_multiplier = 1.0; var last_alpha = period_multiplier; -panelItems.push(panel.newSlider("Adjust Orbital Period: ", 0.1, 3.0, + +panel.newSubPanel("Adjust Orbital Periods"); +/* + + TODO: Loop through all planets, creating new sliders and adjusting their respective orbital periods + + +*/ + +for (var i = 0; i < planets.length; ++i) +panel.newSlider("Adjust Orbital Period: ", 0.1, 3.0, function(value) { period_multiplier = value; changePeriod(period_multiplier); @@ -577,7 +472,7 @@ panelItems.push(panel.newSlider("Adjust Orbital Period: ", 0.1, 3.0, return (value).toFixed(2) + "x"; })); -panelItems.push(panel.newCheckbox("Leave Trails: ", +panel.newCheckbox("Leave Trails: ", function(value) { trailsEnabled = value; if (trailsEnabled) { @@ -588,7 +483,7 @@ panelItems.push(panel.newCheckbox("Leave Trails: ", } else if (planetLines.length != 0) { for (var i = 0; i < NUM_PLANETS; ++i) { for (var j = 0; j < planetLines[i].length; ++j) { - Entities.deleteEntity(planetLines[i][j]); + Entities.editEntity(planetLines[i][j], {visible: false}); } planetLines[i] = []; } @@ -601,40 +496,18 @@ panelItems.push(panel.newCheckbox("Leave Trails: ", return value; })); -panelItems.push(panel.newCheckbox("Energy Error Calculations: ", - function(value) { - energyConserved = value; - }, - function() { - return energyConserved; - }, - function(value) { - return value; - })); -// Update global G constant, period, poke velocity to new value -function changePeriod(alpha) { - var ratio = last_alpha / alpha; - G = Math.pow(ratio, 2.0) * G; - for (var i = 0; i < NUM_PLANETS; ++i) { - var properties = planet_properties[i]; - properties.period = ratio * properties.period; - properties.velocity = Vec3.multiply(ratio, properties.velocity); - - } - last_alpha = alpha; -} // Clean up models, UI panels, lines, and button overlays function scriptEnding() { - satelliteGame.endGame(); Entities.deleteEntity(theSun); for (var i = 0; i < NUM_PLANETS; ++i) { Entities.deleteEntity(planets[i]); } + Menu.removeMenu("Developer > Scene"); panel.destroy(); Overlays.deleteOverlay(pauseButton); From 861c9f0ca0f5f38ef3246bde71f045e733f4439e Mon Sep 17 00:00:00 2001 From: bwent Date: Wed, 5 Aug 2015 17:00:15 -0700 Subject: [PATCH 13/49] more refactoring --- examples/example/solarsystem.js | 426 ++++++++++++++++---------------- 1 file changed, 218 insertions(+), 208 deletions(-) diff --git a/examples/example/solarsystem.js b/examples/example/solarsystem.js index 8711014335..861a805a84 100644 --- a/examples/example/solarsystem.js +++ b/examples/example/solarsystem.js @@ -19,36 +19,40 @@ // Script.include([ - '../utilities/tools/cookies.js'); + '../utilities/tools/cookies.js', 'games/satellite.js' ]); +var DAMPING = 0.0; +var LIFETIME = 6000; var BASE_URL = "https://s3.amazonaws.com/hifi-public/marketplace/hificontent/Scripts/planets/planets/"; -var BOUNDS = 200; - -// Alert user to move if they are too close to domain bounds -if (MyAvatar.position.x < BOUNDS || MyAvatar.position.x > TREE_SCALE - BOUNDS || MyAvatar.position.y < BOUNDS || MyAvatar.position.y > TREE_SCALE - BOUNDS || MyAvatar.position.z < BOUNDS || MyAvatar.position.z > TREE_SCALE - BOUNDS) { - Window.alert("Please move at least 200m away from domain bounds."); - return; -} - // Save intiial avatar and camera position var startingPosition = {x: 800, y: 800, z: 800}; MyAvatar.position = startingPosition; var startFrame = Window.location.href; -/* +var panelPosition = {x: 300, y: 300}; +var panelWidth = 100; +var panelHeight = 300; -********************************** -TODO: create initial UI panel here +var panelBackground = Overlays.addOverlay("text", { + backgroundColor: { + red: 200, + green: 200, + blue: 255 + }, + x: panelPosition.x, + y: panelPosition.y, + width: panelWidth, + height: panelHeight, + alpha: 1.0, + backgroundAlpha: 0.5, visible: true +}); -*/ - - // Place the sun var MAX_RANGE = 80.0; var SUN_SIZE = 8.0; @@ -63,6 +67,11 @@ var theSun = Entities.addEntity({ y: SUN_SIZE, z: SUN_SIZE }, + angularVelocity: { + x: 0.0, + y: 0.1, + z: 0.0 + }, angularDamping: DAMPING, damping: DAMPING, ignoreCollisions: false, @@ -80,13 +89,19 @@ var SMALL_BODY_MASS = LARGE_BODY_MASS * 0.000000333; var GRAVITY = (Math.pow(referenceRadius, 3.0) / Math.pow((referencePeriod / (2.0 * Math.PI)), 2.0)) / LARGE_BODY_MASS; var REFERENCE_GRAVITY = GRAVITY; +var planets = []; var planetCount = 0; -var trails = []; + +var TRAILS_ENABLED = true; +var MAX_POINTS_PER_LINE = 20; var Planet = function(name, trailColor, radius, size) { this.index = 0; this.name = name; this.trailColor = trailColor; + + this.trail = []; + this.lineStack = []; this.radius = radius; this.position = Vec3.sum(center, { x: this.radius, y: 0.0, z: 0.0 }); @@ -97,7 +112,7 @@ var Planet = function(name, trailColor, radius, size) { x: 0, y: -0.2, z: 0.9 - }); + })); this.dimensions = size; this.planet = Entities.addEntity({ @@ -115,28 +130,29 @@ var Planet = function(name, trailColor, radius, size) { ignoreCollisions: false, lifetime: LIFETIME, collisionsWillMove: true, - })); - - this.label = new PlanetLabel(name, this.index); + }); - this.computeAcceleration = function() { var acc = -(this.gravity * LARGE_BODY_MASS) * Math.pow(this.radius, (-2.0)); return acc; }; - this.update = function(deltaTime) { + this.update = function(timeStep) { var between = Vec3.subtract(this.position, center); - var speed = this.computeAcceleration(this.radius) * deltaTime; + var speed = this.computeAcceleration(this.radius) * timeStep; var vel = Vec3.multiply(speed, Vec3.normalize(between)); // Update velocity and position this.velocity = Vec3.sum(this.velocity, vel); - this.position = Vec3.sum(this.position, Vec3.multiply(this.velocity, deltaTime)); + this.position = Vec3.sum(this.position, Vec3.multiply(timeStep, this.velocity)); Entities.editEntity(this.planet, { - velocity: this..velocity, - position: this..position + velocity: this.velocity, + position: this.position }); + + if (TRAILS_ENABLED) { + this.updateTrail(); + } }; this.resetTrails = function() { @@ -145,24 +161,23 @@ var Planet = function(name, trailColor, radius, size) { this.trail = []; this.lineStack = []; //add the first line to both the line entity stack and the trail - trails.push(newLine(lineStack, this.position, this.period, this.lineColor)); - planetLines.push(lineStack); + this.trail.push(newLine(this.lineStack, this.position, this.period, this.lineColor)); }; - this.updateTrails = function() { + this.updateTrail = function() { var point = this.position; var prop = Entities.getEntityProperties(this.lineStack[this.lineStack.length - 1]); var linePos = prop.position; - trails[index].push(computeLocalPoint(linePos, point)); + this.trail.push(computeLocalPoint(linePos, point)); - Entities.editEntity(lineStack[lineStack.length - 1], { - linePoints: trails[i] + Entities.editEntity(this.lineStack[this.lineStack.length - 1], { + linePoints: this.trail }); - if (trails[index].length === MAX_POINTS_PER_LINE) { - trails[index] = newLine(lineStack, point, this.period, this.lineColor); + if (this.trail.length === MAX_POINTS_PER_LINE) { + this.trail = newLine(this.lineStack, point, this.period, this.lineColor); } }; @@ -250,41 +265,37 @@ var PlanetLabel = function(name, index) { this.show = function() { this.showing = true; - Entities.editEntity(this.line, {visible = true}); - Entities.editEntity(this.label, {visible = true}); + Entities.editEntity(this.line, {visible: true}); + Entities.editEntity(this.label, {visible: true}); } this.hide = function() { this.showing = false; - Entities.editEntity(this.line, {visible = false}); - Entities.editEntity(this.label, {visible = false}); + Entities.editEntity(this.line, {visible: false}); + Entities.editEntity(this.label, {visible: false}); } } var time = 0.0; var elapsed; +var TIME_STEP = 60.0; var dt = 1.0 / TIME_STEP; var planetLines = []; var trails = []; + function update(deltaTime) { - if (paused) { - return; - } + // if (paused) { + // return; + // } deltaTime = dt; time++; for (var i = 0; i < planets.length; ++i) { - planets[i].update(); - - if (trailsEnabled) { - planets[i].updateTrails(); - } - + planets[i].update(deltaTime); } - if (time % TIME_STEP == 0) { elapsed++; } @@ -295,7 +306,6 @@ function computeLocalPoint(linePos, worldPoint) { return localPoint; } - // Create a new line function newLine(lineStack, point, period, color) { if (elapsed < period) { @@ -331,170 +341,170 @@ function newLine(lineStack, point, period, color) { return points; } -function mousePressEvent(event) { - var clickedOverlay = Overlays.getOverlayAtPoint({ - x: event.x, - y: event.y - }); - if (clickedOverlay == pauseButton) { - paused = !paused; - for (var i = 0; i < NUM_PLANETS; ++i) { - Entities.editEntity(planets[i], { - velocity: { - x: 0.0, - y: 0.0, - z: 0.0 - } - }); - } - if (paused && !labelsShowing) { - Overlays.editOverlay(pauseButton, { - text: "Paused", - backgroundColor: { - red: 255, - green: 50, - blue: 50 - } - }); - showLabels(); - } - if (paused == false && labelsShowing) { - Overlays.editOverlay(pauseButton, { - text: "Pause", - backgroundColor: { - red: 200, - green: 200, - blue: 255 - } - }); - hideLabels(); - } - planetView = false; - } -} +// function mousePressEvent(event) { +// var clickedOverlay = Overlays.getOverlayAtPoint({ +// x: event.x, +// y: event.y +// }); +// if (clickedOverlay == pauseButton) { +// paused = !paused; +// for (var i = 0; i < NUM_PLANETS; ++i) { +// Entities.editEntity(planets[i], { +// velocity: { +// x: 0.0, +// y: 0.0, +// z: 0.0 +// } +// }); +// } +// if (paused && !labelsShowing) { +// Overlays.editOverlay(pauseButton, { +// text: "Paused", +// backgroundColor: { +// red: 255, +// green: 50, +// blue: 50 +// } +// }); +// showLabels(); +// } +// if (paused == false && labelsShowing) { +// Overlays.editOverlay(pauseButton, { +// text: "Pause", +// backgroundColor: { +// red: 200, +// green: 200, +// blue: 255 +// } +// }); +// hideLabels(); +// } +// planetView = false; +// } +// } -function keyPressEvent(event) { - // Jump back to solar system view - if (event.text == "TAB" && planetView) { - if (earthView) { - satelliteGame.endGame(); - earthView = false; - } - MyAvatar.position = startingPosition; - } -} +// function keyPressEvent(event) { +// // Jump back to solar system view +// if (event.text == "TAB" && planetView) { +// if (earthView) { +// satelliteGame.endGame(); +// earthView = false; +// } +// MyAvatar.position = startingPosition; +// } +// } -//switch to planet view -function mouseDoublePressEvent(event) { - if (earthView) { - return; - } - var pickRay = Camera.computePickRay(event.x, event.y) - var rayPickResult = Entities.findRayIntersection(pickRay, true); +// //switch to planet view +// function mouseDoublePressEvent(event) { +// if (earthView) { +// return; +// } +// var pickRay = Camera.computePickRay(event.x, event.y) +// var rayPickResult = Entities.findRayIntersection(pickRay, true); - for (var i = 0; i < NUM_PLANETS; ++i) { - if (rayPickResult.entityID === labels[i]) { - planetView = true; - if (i == 2) { - MyAvatar.position = Vec3.sum(center, { - x: 200, - y: 200, - z: 200 - }); - Camera.setPosition(Vec3.sum(center, { - x: 200, - y: 200, - z: 200 - })); - earthView = true; - satelliteGame = new SatelliteGame(); +// for (var i = 0; i < NUM_PLANETS; ++i) { +// if (rayPickResult.entityID === labels[i]) { +// planetView = true; +// if (i == 2) { +// MyAvatar.position = Vec3.sum(center, { +// x: 200, +// y: 200, +// z: 200 +// }); +// Camera.setPosition(Vec3.sum(center, { +// x: 200, +// y: 200, +// z: 200 +// })); +// earthView = true; +// satelliteGame = new SatelliteGame(); - } else { - MyAvatar.position = Vec3.sum({ - x: 0.0, - y: 0.0, - z: 3.0 - }, planet_properties[i].position); - Camera.lookAt(planet_properties[i].position); - } - break; - } - } -} +// } else { +// MyAvatar.position = Vec3.sum({ +// x: 0.0, +// y: 0.0, +// z: 3.0 +// }, planet_properties[i].position); +// Camera.lookAt(planet_properties[i].position); +// } +// break; +// } +// } +// } -// UI ELEMENTS +// // UI ELEMENTS -// USE FLOATING UI PANEL TO IMPROVE UI EXPERIENCE +// // USE FLOATING UI PANEL TO IMPROVE UI EXPERIENCE -var paused = false; +// var paused = false; -// Create UI panel -var panel = new Panel(PANEL_X, PANEL_Y); +// // Create UI panel +// var panel = new Panel(PANEL_X, PANEL_Y); -var g_multiplier = 1.0; -panel.newSlider("Adjust Gravitational Force: ", 0.1, 5.0, - function(value) { - g_multiplier = value; - G = G_ref * g_multiplier; - }, +// var g_multiplier = 1.0; +// panel.newSlider("Adjust Gravitational Force: ", 0.1, 5.0, +// function(value) { +// g_multiplier = value; +// G = G_ref * g_multiplier; +// }, - function() { - return g_multiplier; - }, - function(value) { - return value.toFixed(1) + "x"; - })); +// function() { +// return g_multiplier; +// }, +// function(value) { +// return value.toFixed(1) + "x"; +// }); -var period_multiplier = 1.0; -var last_alpha = period_multiplier; +// var period_multiplier = 1.0; +// var last_alpha = period_multiplier; -panel.newSubPanel("Adjust Orbital Periods"); -/* +// panel.newSubPanel("Adjust Orbital Periods"); +// /* - TODO: Loop through all planets, creating new sliders and adjusting their respective orbital periods +// TODO: Loop through all planets, creating new sliders and adjusting their respective orbital periods -*/ +// */ -for (var i = 0; i < planets.length; ++i) -panel.newSlider("Adjust Orbital Period: ", 0.1, 3.0, - function(value) { - period_multiplier = value; - changePeriod(period_multiplier); - }, - function() { - return period_multiplier; - }, - function(value) { - return (value).toFixed(2) + "x"; - })); +// for (var i = 0; i < planets.length; ++i) +// panel.newSlider("Adjust Orbital Period: ", 0.1, 3.0, +// function(value) { +// period_multiplier = value; +// changePeriod(period_multiplier); +// }, +// function() { +// return period_multiplier; +// }, +// function(value) { +// return (value).toFixed(2) + "x"; +// })); -panel.newCheckbox("Leave Trails: ", - function(value) { - trailsEnabled = value; - if (trailsEnabled) { - for (var i = 0; i < NUM_PLANETS; ++i) { - resetTrails(i); - } - //if trails are off and we've already created trails, remove existing trails - } else if (planetLines.length != 0) { - for (var i = 0; i < NUM_PLANETS; ++i) { - for (var j = 0; j < planetLines[i].length; ++j) { - Entities.editEntity(planetLines[i][j], {visible: false}); - } - planetLines[i] = []; - } - } - }, - function() { - return trailsEnabled; - }, - function(value) { - return value; - })); +// panel.newCheckbox("Leave Trails: ", +// function(value) { +// trailsEnabled = value; +// if (trailsEnabled) { +// for (var i = 0; i < NUM_PLANETS; ++i) { +// resetTrails(i); +// } +// //if trails are off and we've already created trails, remove existing trails +// } else if (planetLines.length != 0) { +// for (var i = 0; i < NUM_PLANETS; ++i) { +// for (var j = 0; j < planetLines[i].length; ++j) { +// Entities.editEntity(planetLines[i][j], {visible: false}); +// } +// planetLines[i] = []; +// } +// } +// }, +// function() { +// return trailsEnabled; +// }, +// function(value) { +// return value; +// })); @@ -522,21 +532,21 @@ function scriptEnding() { }; -Controller.mouseMoveEvent.connect(function panelMouseMoveEvent(event) { - return panel.mouseMoveEvent(event); -}); -Controller.mousePressEvent.connect(function panelMousePressEvent(event) { - return panel.mousePressEvent(event); -}); -Controller.mouseDoublePressEvent.connect(function panelMouseDoublePressEvent(event) { - return panel.mouseDoublePressEvent(event); -}); -Controller.mouseReleaseEvent.connect(function(event) { - return panel.mouseReleaseEvent(event); -}); -Controller.mousePressEvent.connect(mousePressEvent); -Controller.mouseDoublePressEvent.connect(mouseDoublePressEvent); -Controller.keyPressEvent.connect(keyPressEvent); +// Controller.mouseMoveEvent.connect(function panelMouseMoveEvent(event) { +// return panel.mouseMoveEvent(event); +// }); +// Controller.mousePressEvent.connect(function panelMousePressEvent(event) { +// return panel.mousePressEvent(event); +// }); +// Controller.mouseDoublePressEvent.connect(function panelMouseDoublePressEvent(event) { +// return panel.mouseDoublePressEvent(event); +// }); +// Controller.mouseReleaseEvent.connect(function(event) { +// return panel.mouseReleaseEvent(event); +// }); +// Controller.mousePressEvent.connect(mousePressEvent); +// Controller.mouseDoublePressEvent.connect(mouseDoublePressEvent); +// Controller.keyPressEvent.connect(keyPressEvent); Script.scriptEnding.connect(scriptEnding); Script.update.connect(update); \ No newline at end of file From e61ba997e5e9fc41a03ec3ca6b69a3c8e38f1ae5 Mon Sep 17 00:00:00 2001 From: bwent Date: Thu, 6 Aug 2015 10:04:30 -0700 Subject: [PATCH 14/49] Fix trail functionality with new classes --- examples/example/solarsystem.js | 73 +++++++++++++++++++++------------ 1 file changed, 46 insertions(+), 27 deletions(-) diff --git a/examples/example/solarsystem.js b/examples/example/solarsystem.js index 861a805a84..e873383295 100644 --- a/examples/example/solarsystem.js +++ b/examples/example/solarsystem.js @@ -54,7 +54,7 @@ var panelBackground = Overlays.addOverlay("text", { // Place the sun -var MAX_RANGE = 80.0; +var MAX_RANGE = 50.0; var SUN_SIZE = 8.0; var center = Vec3.sum(startingPosition, Vec3.multiply(MAX_RANGE, Quat.getFront(Camera.getOrientation()))); @@ -93,10 +93,17 @@ var planets = []; var planetCount = 0; var TRAILS_ENABLED = true; -var MAX_POINTS_PER_LINE = 20; +var MAX_POINTS_PER_LINE = 50; +var LINE_DIM = 200; +var LINE_WIDTH = 20; + +var VELOCITY_OFFSET_Y = -0.3; +var VELOCITY_OFFSET_Z = 0.9; + +var index = 0; var Planet = function(name, trailColor, radius, size) { - this.index = 0; + this.name = name; this.trailColor = trailColor; @@ -110,8 +117,8 @@ var Planet = function(name, trailColor, radius, size) { this.initialVelocity = Math.sqrt((GRAVITY * LARGE_BODY_MASS) / radius); this.velocity = Vec3.multiply(this.initialVelocity, Vec3.normalize({ x: 0, - y: -0.2, - z: 0.9 + y: VELOCITY_OFFSET_Y, + z: VELOCITY_OFFSET_Z })); this.dimensions = size; @@ -131,7 +138,7 @@ var Planet = function(name, trailColor, radius, size) { lifetime: LIFETIME, collisionsWillMove: true, }); - + this.computeAcceleration = function() { var acc = -(this.gravity * LARGE_BODY_MASS) * Math.pow(this.radius, (-2.0)); return acc; @@ -161,23 +168,23 @@ var Planet = function(name, trailColor, radius, size) { this.trail = []; this.lineStack = []; //add the first line to both the line entity stack and the trail - this.trail.push(newLine(this.lineStack, this.position, this.period, this.lineColor)); + this.trail.push(newLine(this.lineStack, this.position, this.period, this.trailColor)); }; this.updateTrail = function() { + var point = this.position; - - var prop = Entities.getEntityProperties(this.lineStack[this.lineStack.length - 1]); - var linePos = prop.position; + var linePos = Entities.getEntityProperties(this.lineStack[this.lineStack.length - 1]).position; this.trail.push(computeLocalPoint(linePos, point)); Entities.editEntity(this.lineStack[this.lineStack.length - 1], { - linePoints: this.trail + linePoints: this.trail }); - if (this.trail.length === MAX_POINTS_PER_LINE) { - this.trail = newLine(this.lineStack, point, this.period, this.lineColor); + + if (this.trail.length == MAX_POINTS_PER_LINE) { + this.trail = newLine(this.lineStack, point, this.period, this.trailColor); } }; @@ -191,19 +198,31 @@ var Planet = function(name, trailColor, radius, size) { this.last_alpha = alpha; } - this.index++; + index++; + this.resetTrails(); + } -planets.push(new Planet("Mercury", {red: 255, green: 255, blue: 255}, 7.0, 1.0)); -planets.push(new Planet("Venus", {red: 255, green: 160, blue: 110}, 8.0, 1.2)); -planets.push(new Planet("Earth", {red: 10, green: 150, blue: 160}, 9.2, 1.6)); -planets.push(new Planet("Mars", {red: 180, green: 70, blue: 10}, 11.0, 2.0)); -planets.push(new Planet("Jupiter", {red: 250, green: 140, blue: 0}, 14.5, 4.3)); -planets.push(new Planet("Saturn", {red: 235, green: 215, blue: 0}, 21.0, 3.7)); -planets.push(new Planet("Uranus", {red: 135, green: 205, blue: 240}, 29.0, 4.0)); -planets.push(new Planet("Neptune", {red: 30, green: 140, blue: 255}, 35.0, 4.2)); -planets.push(new Planet("Pluto", {red: 255, green: 255, blue: 255}, 58.0, 3.2)); +var MERCURY_LINE_COLOR = {red: 255, green: 255, blue: 255}; +var VENUS_LINE_COLOR = {red: 255, green: 160, blue: 110}; +var EARTH_LINE_COLOR = {red: 10, green: 150, blue: 160}; +var MARS_LINE_COLOR = {red: 180, green: 70, blue: 10}; +var JUPITER_LINE_COLOR = {red: 250, green: 140, blue: 0}; +var SATURN_LINE_COLOR = {red: 235, green: 215, blue: 0}; +var URANUS_LINE_COLOR = {red: 135, green: 205, blue: 240}; +var NEPTUNE_LINE_COLOR = {red: 30, green: 140, blue: 255}; +var PLUTO_LINE_COLOR = {red: 255, green: 255, blue: 255}; + +planets.push(new Planet("mercury", MERCURY_LINE_COLOR, 7.0, 1.0)); +planets.push(new Planet("venus", VENUS_LINE_COLOR, 8.5, 1.2)); +planets.push(new Planet("earth", EARTH_LINE_COLOR, 10.2, 1.6)); +planets.push(new Planet("mars", MARS_LINE_COLOR, 16.0, 2.0)); +planets.push(new Planet("jupiter", JUPITER_LINE_COLOR, 19.5, 4.3)); +planets.push(new Planet("saturn", SATURN_LINE_COLOR, 29.0, 3.7)); +planets.push(new Planet("uranus", URANUS_LINE_COLOR, 37.0, 4.0)); +planets.push(new Planet("neptune", NEPTUNE_LINE_COLOR, 55.0, 4.2)); +planets.push(new Planet("pluto", PLUTO_LINE_COLOR, 80.0, 3.2)); var labels = []; var labelLines = []; @@ -285,20 +304,20 @@ var dt = 1.0 / TIME_STEP; var planetLines = []; var trails = []; - function update(deltaTime) { // if (paused) { // return; // } deltaTime = dt; time++; + if (time % TIME_STEP == 0) { + elapsed++; + } for (var i = 0; i < planets.length; ++i) { planets[i].update(deltaTime); } - if (time % TIME_STEP == 0) { - elapsed++; - } + } function computeLocalPoint(linePos, worldPoint) { From c585f24e27b41123fccd074e02e5a4ca62d2f690 Mon Sep 17 00:00:00 2001 From: bwent Date: Tue, 11 Aug 2015 17:07:27 -0700 Subject: [PATCH 15/49] Refining UI, introduce tween functionality for zooming into planets --- examples/example/games/satellite.js | 4 +- examples/example/solarsystem.js | 873 ++++++++++++++++++---------- examples/utilities/tools/cookies.js | 2 +- 3 files changed, 562 insertions(+), 317 deletions(-) diff --git a/examples/example/games/satellite.js b/examples/example/games/satellite.js index f362c0c1e4..fd1243fbe0 100644 --- a/examples/example/games/satellite.js +++ b/examples/example/games/satellite.js @@ -20,6 +20,8 @@ Script.include('../utilities/tools/vector.js'); var URL = "https://s3.amazonaws.com/hifi-public/marketplace/hificontent/Scripts/planets/"; SatelliteGame = function() { + print("initializing satellite game"); + var MAX_RANGE = 50.0; var Y_AXIS = { x: 0, @@ -68,7 +70,7 @@ SatelliteGame = function() { this.clouds = Entities.addEntity({ type: "Model", shapeType: 'sphere', - modelURL: URL + "clouds.fbx?i=2", + modelURL: URL + "clouds.fbx", position: position, dimensions: { x: size + CLOUDS_OFFSET, diff --git a/examples/example/solarsystem.js b/examples/example/solarsystem.js index e873383295..196ad57f86 100644 --- a/examples/example/solarsystem.js +++ b/examples/example/solarsystem.js @@ -19,7 +19,6 @@ // Script.include([ - '../utilities/tools/cookies.js', 'games/satellite.js' ]); @@ -28,35 +27,19 @@ var LIFETIME = 6000; var BASE_URL = "https://s3.amazonaws.com/hifi-public/marketplace/hificontent/Scripts/planets/planets/"; // Save intiial avatar and camera position -var startingPosition = {x: 800, y: 800, z: 800}; +var startingPosition = { + x: 8000, + y: 8000, + z: 8000 +}; MyAvatar.position = startingPosition; -var startFrame = Window.location.href; - - -var panelPosition = {x: 300, y: 300}; -var panelWidth = 100; -var panelHeight = 300; - -var panelBackground = Overlays.addOverlay("text", { - backgroundColor: { - red: 200, - green: 200, - blue: 255 - }, - x: panelPosition.x, - y: panelPosition.y, - width: panelWidth, - height: panelHeight, - alpha: 1.0, - backgroundAlpha: 0.5, visible: true -}); - +var cameraStart = Camera.getOrientation(); // Place the sun -var MAX_RANGE = 50.0; -var SUN_SIZE = 8.0; -var center = Vec3.sum(startingPosition, Vec3.multiply(MAX_RANGE, Quat.getFront(Camera.getOrientation()))); +var MAX_RANGE = 80.0; +var center = Vec3.sum(startingPosition, Vec3.multiply(MAX_RANGE, Vec3.normalize(Quat.getFront(Camera.getOrientation())))); +var SUN_SIZE = 7.0; var theSun = Entities.addEntity({ type: "Model", @@ -82,8 +65,9 @@ var theSun = Entities.addEntity({ // Reference values for physical constants -var referenceRadius = 7.0; -var referencePeriod = 1.0; +var referenceRadius = 15.0; +var referenceDiameter = 0.6; +var referencePeriod = 3.0; var LARGE_BODY_MASS = 250.0; var SMALL_BODY_MASS = LARGE_BODY_MASS * 0.000000333; var GRAVITY = (Math.pow(referenceRadius, 3.0) / Math.pow((referencePeriod / (2.0 * Math.PI)), 2.0)) / LARGE_BODY_MASS; @@ -92,8 +76,8 @@ var REFERENCE_GRAVITY = GRAVITY; var planets = []; var planetCount = 0; -var TRAILS_ENABLED = true; -var MAX_POINTS_PER_LINE = 50; +var trailsEnabled = true; +var MAX_POINTS_PER_LINE = 60; var LINE_DIM = 200; var LINE_WIDTH = 20; @@ -102,43 +86,48 @@ var VELOCITY_OFFSET_Z = 0.9; var index = 0; -var Planet = function(name, trailColor, radius, size) { - +var Planet = function(name, trailColor, radiusScale, sizeScale) { + this.name = name; this.trailColor = trailColor; - + this.trail = []; this.lineStack = []; - this.radius = radius; - this.position = Vec3.sum(center, { x: this.radius, y: 0.0, z: 0.0 }); + this.radius = radiusScale * referenceRadius; + this.position = Vec3.sum(center, { + x: this.radius, + y: 0.0, + z: 0.0 + }); this.period = (2.0 * Math.PI) * Math.sqrt(Math.pow(this.radius, 3.0) / (GRAVITY * LARGE_BODY_MASS)); this.gravity = GRAVITY; - this.initialVelocity = Math.sqrt((GRAVITY * LARGE_BODY_MASS) / radius); + this.initialVelocity = Math.sqrt((GRAVITY * LARGE_BODY_MASS) / this.radius); this.velocity = Vec3.multiply(this.initialVelocity, Vec3.normalize({ - x: 0, - y: VELOCITY_OFFSET_Y, - z: VELOCITY_OFFSET_Z - })); - this.dimensions = size; + x: 0, + y: VELOCITY_OFFSET_Y, + z: VELOCITY_OFFSET_Z + })); + this.dimensions = sizeScale * referenceDiameter; + this.sizeScale = sizeScale; this.planet = Entities.addEntity({ - type: "Model", - modelURL: BASE_URL + name + ".fbx", - position: this.position, - dimensions: { - x: this.dimensions, - y: this.dimensions, - z: this.dimensions - }, - velocity: this.velocity, - angularDamping: DAMPING, - damping: DAMPING, - ignoreCollisions: false, - lifetime: LIFETIME, - collisionsWillMove: true, + type: "Model", + modelURL: BASE_URL + name + ".fbx", + position: this.position, + dimensions: { + x: this.dimensions, + y: this.dimensions, + z: this.dimensions + }, + velocity: this.velocity, + angularDamping: DAMPING, + damping: DAMPING, + ignoreCollisions: false, + lifetime: LIFETIME, + collisionsWillMove: true, }); - + this.computeAcceleration = function() { var acc = -(this.gravity * LARGE_BODY_MASS) * Math.pow(this.radius, (-2.0)); return acc; @@ -157,9 +146,9 @@ var Planet = function(name, trailColor, radius, size) { position: this.position }); - if (TRAILS_ENABLED) { + if (trailsEnabled) { this.updateTrail(); - } + } }; this.resetTrails = function() { @@ -173,7 +162,6 @@ var Planet = function(name, trailColor, radius, size) { }; this.updateTrail = function() { - var point = this.position; var linePos = Entities.getEntityProperties(this.lineStack[this.lineStack.length - 1]).position; @@ -188,45 +176,104 @@ var Planet = function(name, trailColor, radius, size) { } }; + this.zoom = function() { + Script.include('file:///Users/bridget/Desktop/tween.js'); + init(this); + // var viewingRange = sizeScale * 2.0; + // var direction = Vec3.subtract(this.position, MyAvatar.position); + // var dist = Vec3.length(direction); + // var timer = 0; + // while (dist > viewingRange && timer < 8000000) { + // timer++; + // if (timer % 10000 == 0) { + // MyAvatar.position = Vec3.sum(MyAvatar.position, Vec3.normalize(direction)); + // direction = Vec3.subtract(this.position, MyAvatar.position); + // dist = Vec3.length(direction); + // } + // } + }; + + this.adjustPeriod = function(alpha) { // Update global G constant, period, poke velocity to new value var ratio = this.last_alpha / alpha; this.gravity = Math.pow(ratio, 2.0) * GRAVITY; this.period = ratio * this.period; this.velocity = Vec3.multiply(ratio, this.velocity); - - this.last_alpha = alpha; - } - index++; - this.resetTrails(); + this.last_alpha = alpha; + }; + + + index++; + this.resetTrails(); } +// rideOrbit = function() { -var MERCURY_LINE_COLOR = {red: 255, green: 255, blue: 255}; -var VENUS_LINE_COLOR = {red: 255, green: 160, blue: 110}; -var EARTH_LINE_COLOR = {red: 10, green: 150, blue: 160}; -var MARS_LINE_COLOR = {red: 180, green: 70, blue: 10}; -var JUPITER_LINE_COLOR = {red: 250, green: 140, blue: 0}; -var SATURN_LINE_COLOR = {red: 235, green: 215, blue: 0}; -var URANUS_LINE_COLOR = {red: 135, green: 205, blue: 240}; -var NEPTUNE_LINE_COLOR = {red: 30, green: 140, blue: 255}; -var PLUTO_LINE_COLOR = {red: 255, green: 255, blue: 255}; +// Script.include('file:///Users/bridget/Desktop/tween.js'); +// init(); + +// } -planets.push(new Planet("mercury", MERCURY_LINE_COLOR, 7.0, 1.0)); -planets.push(new Planet("venus", VENUS_LINE_COLOR, 8.5, 1.2)); -planets.push(new Planet("earth", EARTH_LINE_COLOR, 10.2, 1.6)); -planets.push(new Planet("mars", MARS_LINE_COLOR, 16.0, 2.0)); -planets.push(new Planet("jupiter", JUPITER_LINE_COLOR, 19.5, 4.3)); -planets.push(new Planet("saturn", SATURN_LINE_COLOR, 29.0, 3.7)); -planets.push(new Planet("uranus", URANUS_LINE_COLOR, 37.0, 4.0)); -planets.push(new Planet("neptune", NEPTUNE_LINE_COLOR, 55.0, 4.2)); -planets.push(new Planet("pluto", PLUTO_LINE_COLOR, 80.0, 3.2)); -var labels = []; -var labelLines = []; -var labelsShowing = false; +var MERCURY_LINE_COLOR = { + red: 255, + green: 255, + blue: 255 +}; +var VENUS_LINE_COLOR = { + red: 255, + green: 160, + blue: 110 +}; +var EARTH_LINE_COLOR = { + red: 10, + green: 150, + blue: 160 +}; +var MARS_LINE_COLOR = { + red: 180, + green: 70, + blue: 10 +}; +var JUPITER_LINE_COLOR = { + red: 250, + green: 140, + blue: 0 +}; +var SATURN_LINE_COLOR = { + red: 235, + green: 215, + blue: 0 +}; +var URANUS_LINE_COLOR = { + red: 135, + green: 205, + blue: 240 +}; +var NEPTUNE_LINE_COLOR = { + red: 30, + green: 140, + blue: 255 +}; +var PLUTO_LINE_COLOR = { + red: 255, + green: 255, + blue: 255 +}; + +planets.push(new Planet("mercury", MERCURY_LINE_COLOR, 0.387, 0.383)); +planets.push(new Planet("venus", VENUS_LINE_COLOR, 0.723, 0.949)); +planets.push(new Planet("earth", EARTH_LINE_COLOR, 1.0, 1.0)); +planets.push(new Planet("mars", MARS_LINE_COLOR, 1.52, 0.532)); +planets.push(new Planet("jupiter", JUPITER_LINE_COLOR, 5.20, 11.21)); +planets.push(new Planet("saturn", SATURN_LINE_COLOR, 9.58, 9.45)); +planets.push(new Planet("uranus", URANUS_LINE_COLOR, 19.20, 4.01)); +planets.push(new Planet("neptune", NEPTUNE_LINE_COLOR, 30.05, 3.88)); +planets.push(new Planet("pluto", PLUTO_LINE_COLOR, 39.48, 0.186)); + var LABEL_X = 8.0; var LABEL_Y = 3.0; var LABEL_Z = 1.0; @@ -235,67 +282,78 @@ var TEXT_HEIGHT = 1.0; var PlanetLabel = function(name, index) { var text = name + " Speed: " + Vec3.length(planets[index].velocity).toFixed(2); - var labelPos = Vec3.sum(planets[index].position, { x: 0.0, y: LABEL_DIST, z: LABEL_DIST }); - var linePos = planets[i].position; - + this.labelPos = Vec3.sum(planets[index].position, { + x: 0.0, + y: LABEL_DIST, + z: LABEL_DIST + }); + this.linePos = planets[index].position; + this.line = Entities.addEntity({ - type: "Line", - position: linePos, - dimensions: { - x: 20, - y: 20, - z: 20 - }, - lineWidth: LINE_WIDTH, - color: { - red: 255, - green: 255, - blue: 255 - }, - linePoints: [{ + type: "Line", + position: this.linePos, + dimensions: { + x: 20, + y: 20, + z: 20 + }, + lineWidth: LINE_WIDTH, + color: { + red: 255, + green: 255, + blue: 255 + }, + linePoints: [{ x: 0, y: 0, z: 0 - }, - computeLocalPoint(linePos, labelPos)], - visible: false - }); + }, + computeLocalPoint(this.linePos, this.labelPos) + ], + visible: false + }); this.label = Entities.addEntity({ - type: "Text", - text: text, - lineHeight: TEXT_HEIGHT, - dimensions: { - x: LABEL_X, - y: LABEL_Y, - z: LABEL_Z - }, - position: labelPos, - backgroundColor: { - red: 10, - green: 10, - blue: 10 - }, - faceCamera: true, - visible: false - }); - labelLines.push(line); - labels.push(label); + type: "Text", + text: text, + lineHeight: TEXT_HEIGHT, + dimensions: { + x: LABEL_X, + y: LABEL_Y, + z: LABEL_Z + }, + position: this.labelPos, + backgroundColor: { + red: 10, + green: 10, + blue: 10 + }, + faceCamera: true, + visible: false + }); this.show = function() { this.showing = true; - Entities.editEntity(this.line, {visible: true}); - Entities.editEntity(this.label, {visible: true}); + Entities.editEntity(this.line, { + visible: true + }); + Entities.editEntity(this.label, { + visible: true + }); } this.hide = function() { this.showing = false; - Entities.editEntity(this.line, {visible: false}); - Entities.editEntity(this.label, {visible: false}); + Entities.editEntity(this.line, { + visible: false + }); + Entities.editEntity(this.label, { + visible: false + }); } - } - + + var time = 0.0; var elapsed; var TIME_STEP = 60.0; @@ -303,12 +361,13 @@ var dt = 1.0 / TIME_STEP; var planetLines = []; var trails = []; +var paused = false; function update(deltaTime) { - // if (paused) { - // return; - // } - deltaTime = dt; + if (paused) { + return; + } + //deltaTime = dt; time++; if (time % TIME_STEP == 0) { elapsed++; @@ -317,7 +376,28 @@ function update(deltaTime) { for (var i = 0; i < planets.length; ++i) { planets[i].update(deltaTime); } - +} + +function pause() { + for (var i = 0; i < planets.length; ++i) { + Entities.editEntity(planets[i].planet, { + velocity: { + x: 0.0, + y: 0.0, + z: 0.0 + } + }); + planets[i].label = new PlanetLabel(planets[i].name, i); + planets[i].label.show(); + } + paused = true; +} + +function resume() { + for (var i = 0; i < planets.length; ++i) { + planets[i].label.hide(); + } + paused = false; } function computeLocalPoint(linePos, worldPoint) { @@ -360,187 +440,345 @@ function newLine(lineStack, point, period, color) { return points; } -// function mousePressEvent(event) { -// var clickedOverlay = Overlays.getOverlayAtPoint({ -// x: event.x, -// y: event.y -// }); -// if (clickedOverlay == pauseButton) { -// paused = !paused; -// for (var i = 0; i < NUM_PLANETS; ++i) { -// Entities.editEntity(planets[i], { -// velocity: { -// x: 0.0, -// y: 0.0, -// z: 0.0 -// } -// }); -// } -// if (paused && !labelsShowing) { -// Overlays.editOverlay(pauseButton, { -// text: "Paused", -// backgroundColor: { -// red: 255, -// green: 50, -// blue: 50 -// } -// }); -// showLabels(); -// } -// if (paused == false && labelsShowing) { -// Overlays.editOverlay(pauseButton, { -// text: "Pause", -// backgroundColor: { -// red: 200, -// green: 200, -// blue: 255 -// } -// }); -// hideLabels(); -// } -// planetView = false; -// } -// } -// function keyPressEvent(event) { -// // Jump back to solar system view -// if (event.text == "TAB" && planetView) { -// if (earthView) { -// satelliteGame.endGame(); -// earthView = false; -// } -// MyAvatar.position = startingPosition; -// } -// } -// //switch to planet view -// function mouseDoublePressEvent(event) { -// if (earthView) { -// return; -// } -// var pickRay = Camera.computePickRay(event.x, event.y) -// var rayPickResult = Entities.findRayIntersection(pickRay, true); +var spacing = 8; +var textWidth = 70; +var buttonWidth = 30; +var buttonHeight = 30; -// for (var i = 0; i < NUM_PLANETS; ++i) { -// if (rayPickResult.entityID === labels[i]) { -// planetView = true; -// if (i == 2) { -// MyAvatar.position = Vec3.sum(center, { -// x: 200, -// y: 200, -// z: 200 -// }); -// Camera.setPosition(Vec3.sum(center, { -// x: 200, -// y: 200, -// z: 200 -// })); -// earthView = true; -// satelliteGame = new SatelliteGame(); +var ICONS_URL = 'https://s3.amazonaws.com/hifi-public/marketplace/hificontent/Scripts/planets/images/'; -// } else { -// MyAvatar.position = Vec3.sum({ -// x: 0.0, -// y: 0.0, -// z: 3.0 -// }, planet_properties[i].position); -// Camera.lookAt(planet_properties[i].position); -// } -// break; -// } -// } -// } +var UIPanel = function(x, y, orientation) { + this.visible = false; + this.buttons = []; + this.x = x; + this.y = y; + this.nextX = x + spacing; + this.nextY = y + spacing; + this.width = spacing * 2.0; + this.height = spacing * 2.0; -// // UI ELEMENTS + this.background = Overlays.addOverlay("text", { + backgroundColor: { + red: 240, + green: 240, + blue: 255 + }, + x: this.x, + y: this.y, + width: this.width, + height: this.height, + alpha: 1.0, + backgroundAlpha: 0.7, + visible: false + }); + + this.addIcon = function(iconID) { + var icon = Overlays.addOverlay("image", { + color: { + red: 255, + green: 255, + blue: 255 + }, + imageURL: ICONS_URL + iconID + '.svg', + x: this.nextX, + y: this.nextY, + width: buttonWidth, + height: buttonHeight, + alpha: 1.0, + visible: false + }); + + + if (orientation === 'horizontal') { + this.nextX += buttonWidth + spacing; + this.width += buttonWidth; + + } else if (orientation === 'vertical') { + this.nextY += buttonHeight + spacing; + this.height += buttonHeight; + } + + Overlays.editOverlay(this.background, { + width: buttonWidth + this.width, + height: buttonHeight + this.height + }); + + this.buttons.push(icon); + return icon; + }; + + this.addText = function(text) { + var icon = Overlays.addOverlay("text", { + color: { + red: 240, + green: 240, + blue: 255 + }, + text: text, + x: this.nextX, + y: this.nextY, + width: textWidth, + height: buttonHeight, + visible: false + }); + + if (orientation === 'horizontal') { + this.nextX += textWidth + spacing; + this.width += textWidth; + + } else if (orientation === 'vertical') { + this.nextY += buttonHeight + spacing; + this.height += buttonHeight; + } + + Overlays.editOverlay(this.background, { + width: textWidth + this.width, + height: buttonHeight + this.height + }); + + this.buttons.push(icon); + return icon; + }; + + + this.show = function() { + Overlays.editOverlay(this.background, { + visible: true + }); + for (var i in this.buttons) { + Overlays.editOverlay(this.buttons[i], { + visible: true + }); + } + this.visible = true; + }; + + this.hide = function() { + Overlays.editOverlay(this.background, { + visible: false + }); + for (var i in this.buttons) { + Overlays.editOverlay(this.buttons[i], { + visible: false + }); + } + this.visible = false; + }; + + this.remove = function() { + Overlays.deleteOverlay(this.background); + for (var i in this.buttons) { + Overlays.deleteOverlay(this.buttons[i]); + } + }; + +} + +var panelX = 1250; +var panelY = 500; +var panelWidth = 50; +var panelHeight = 210; + +var mainPanel = new UIPanel(panelX, panelY, 'vertical'); + +var systemViewButton = mainPanel.addIcon('solarsystems'); +var systemViewPanel = new UIPanel(panelX - 140, panelY, 'horizontal'); +var reverseButton = systemViewPanel.addIcon('reverse'); +var pauseButton = systemViewPanel.addIcon('playpause'); +var forwardButton = systemViewPanel.addIcon('forward'); + +var zoomButton = mainPanel.addIcon('magnifier'); +var zoomPanel = new UIPanel(panelX - 900, panelY + (buttonHeight + spacing) * 4.0, 'horizontal'); +for (var i = 0; i < planets.length; ++i) { + zoomPanel.addText(planets[i].name); +} + +var satelliteButton = mainPanel.addIcon('satellite'); +var settingsButton = mainPanel.addIcon('settings'); +var stopButton = mainPanel.addIcon('close'); + +mainPanel.show(); + +Script.include('../utilities/tools/cookies.js'); + + +var satelliteView; +var satelliteGame; + var settings = new Panel(panelX - 610, panelY - 80); + + var g_multiplier = 1.0; + settings.newSlider("Gravitational Force ", 0.1, 5.0, + function(value) { + g_multiplier = value; + GRAVITY = REFERENCE_GRAVITY * g_multiplier; + }, + + function() { + return g_multiplier; + }, + function(value) { + return value.toFixed(1) + "x"; + }); + + + var subPanel = settings.newSubPanel("Orbital Periods"); + + for (var i = 0; i < planets.length; ++i) { + planets[i].period_multiplier = 1.0; + planets[i].last_alpha = planets[i].period_multiplier; + + subPanel.newSlider(planets[i].name, 0.1, 3.0, + function(value) { + planets[i].period_multiplier = value; + planets[i].adjustPeriod(value); + }, + function() { + return planets[i].period_multiplier; + }, + function(value) { + return (value).toFixed(2) + "x"; + }); + } + settings.newCheckbox("Leave Trails: ", + function(value) { + trailsEnabled = value; + if (trailsEnabled) { + for (var i = 0; i < planets.length; ++i) { + planets[i].resetTrails(); + } + //if trails are off and we've already created trails, remove existing trails + } else { + for (var i = 0; i < planets.length; ++i) { + for (var j = 0; j < planets[i].lineStack.length; ++j) { + Entities.editEntity(planets[i].lineStack[j], { + visible: false + }); + } + planets[i].lineStack = []; + } + } + }, + function() { + return trailsEnabled; + }, + function(value) { + return value; + }); + settings.hide(); + + +function mousePressEvent(event) { + + var clicked = Overlays.getOverlayAtPoint({ + x: event.x, + y: event.y + }); + + if (clicked == systemViewButton) { + MyAvatar.position = startingPosition; + Camera.setOrientation(cameraStart); + if (paused) { + resume(); + } + + if (!systemViewPanel.visible) { + systemViewPanel.show(); + } else { + systemViewPanel.hide(); + } + } + var zoomed = false; + if (clicked == zoomButton && !satelliteView) { + if (!zoomPanel.visible) { + zoomPanel.show(); + pause(); + } else { + zoomPanel.hide(); + if (zoomed) { + + MyAvatar.position = startingPosition; + Camera.setOrientation(cameraStart); + resume(); + } + + } + } + + for (var i = 0; i < planets.length; ++i) { + if (zoomPanel.visible && clicked == zoomPanel.buttons[i]) { + planets[i].zoom(); + zoomed = true; + break; + } + } + + if (systemViewPanel.visible && clicked == pauseButton) { + if (!paused) { + pause(); + } else { + resume(); + } + } + if (clicked == satelliteButton) { + if (satelliteView) { + satelliteGame.endGame(); + MyAvatar.position = startingPosition; + satelliteView = false; + resume(); + } else { + pause(); + var confirmed = Window.confirm("Start satellite game?"); + if (!confirmed) { + resume(); + continue; + } + satelliteView = true; + MyAvatar.position = { + x: 200, + y: 200, + z: 200 + }; + Camera.setPosition({ + x: 200, + y: 200, + z: 200 + }); + satelliteGame = new SatelliteGame(); + } + } -// // USE FLOATING UI PANEL TO IMPROVE UI EXPERIENCE - -// var paused = false; - -// // Create UI panel -// var panel = new Panel(PANEL_X, PANEL_Y); - -// var g_multiplier = 1.0; -// panel.newSlider("Adjust Gravitational Force: ", 0.1, 5.0, -// function(value) { -// g_multiplier = value; -// G = G_ref * g_multiplier; -// }, - -// function() { -// return g_multiplier; -// }, -// function(value) { -// return value.toFixed(1) + "x"; -// }); - -// var period_multiplier = 1.0; -// var last_alpha = period_multiplier; - -// panel.newSubPanel("Adjust Orbital Periods"); -// /* - -// TODO: Loop through all planets, creating new sliders and adjusting their respective orbital periods - - -// */ - -// for (var i = 0; i < planets.length; ++i) -// panel.newSlider("Adjust Orbital Period: ", 0.1, 3.0, -// function(value) { -// period_multiplier = value; -// changePeriod(period_multiplier); -// }, -// function() { -// return period_multiplier; -// }, -// function(value) { -// return (value).toFixed(2) + "x"; -// })); - -// panel.newCheckbox("Leave Trails: ", -// function(value) { -// trailsEnabled = value; -// if (trailsEnabled) { -// for (var i = 0; i < NUM_PLANETS; ++i) { -// resetTrails(i); -// } -// //if trails are off and we've already created trails, remove existing trails -// } else if (planetLines.length != 0) { -// for (var i = 0; i < NUM_PLANETS; ++i) { -// for (var j = 0; j < planetLines[i].length; ++j) { -// Entities.editEntity(planetLines[i][j], {visible: false}); -// } -// planetLines[i] = []; -// } -// } -// }, -// function() { -// return trailsEnabled; -// }, -// function(value) { -// return value; -// })); - + if (clicked == settingsButton) { + if (!settings.visible) { + settings.show(); + } else { + settings.hide(); + } + } + if (clicked == stopButton) { + Script.stop(); + } +} // Clean up models, UI panels, lines, and button overlays function scriptEnding() { - satelliteGame.endGame(); - + mainPanel.remove(); + systemViewPanel.remove(); + zoomPanel.remove(); + + settings.destroy(); + Entities.deleteEntity(theSun); - for (var i = 0; i < NUM_PLANETS; ++i) { - Entities.deleteEntity(planets[i]); + for (var i = 0; i < planets.length; ++i) { + Entities.deleteEntity(planets[i].planet); } - Menu.removeMenu("Developer > Scene"); - panel.destroy(); - Overlays.deleteOverlay(pauseButton); - var e = Entities.findEntities(MyAvatar.position, 16000); for (i = 0; i < e.length; i++) { var props = Entities.getEntityProperties(e[i]); @@ -550,22 +788,27 @@ function scriptEnding() { } }; +Controller.mousePressEvent.connect(mousePressEvent); + + Controller.mouseMoveEvent.connect(function panelMouseMoveEvent(event) { + return settings.mouseMoveEvent(event); + }); + Controller.mousePressEvent.connect(function panelMousePressEvent(event) { + return settings.mousePressEvent(event); + }); + Controller.mouseDoublePressEvent.connect(function panelMouseDoublePressEvent(event) { + return settings.mouseDoublePressEvent(event); + }); + Controller.mouseReleaseEvent.connect(function(event) { + return settings.mouseReleaseEvent(event); + }); + Controller.keyPressEvent.connect(function(event) { + return settings.keyPressEvent(event); + }); + + + -// Controller.mouseMoveEvent.connect(function panelMouseMoveEvent(event) { -// return panel.mouseMoveEvent(event); -// }); -// Controller.mousePressEvent.connect(function panelMousePressEvent(event) { -// return panel.mousePressEvent(event); -// }); -// Controller.mouseDoublePressEvent.connect(function panelMouseDoublePressEvent(event) { -// return panel.mouseDoublePressEvent(event); -// }); -// Controller.mouseReleaseEvent.connect(function(event) { -// return panel.mouseReleaseEvent(event); -// }); -// Controller.mousePressEvent.connect(mousePressEvent); -// Controller.mouseDoublePressEvent.connect(mouseDoublePressEvent); -// Controller.keyPressEvent.connect(keyPressEvent); Script.scriptEnding.connect(scriptEnding); Script.update.connect(update); \ No newline at end of file diff --git a/examples/utilities/tools/cookies.js b/examples/utilities/tools/cookies.js index 751008fd99..1a8695188b 100644 --- a/examples/utilities/tools/cookies.js +++ b/examples/utilities/tools/cookies.js @@ -1360,7 +1360,7 @@ var CHECK_MARK_COLOR = { this.nextY = this.y + this.getHeight(); - var item = new CollapsablePanelItem(name, this.x, this.nextY, textWidth, rawHeight, panel); + var item = new CollapsablePanelItem(name, this.x, this.nextY, textWidth, rawHeight); item.isSubPanel = true; this.nextY += 1.5 * item.height; From dc68e12963326329d3fbf03fa69110ba59baa64a Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Wed, 12 Aug 2015 22:01:24 -0700 Subject: [PATCH 16/49] Improve documentation for overlay changes. --- examples/libraries/overlayManager.js | 32 ++++++++++++++----- interface/src/ui/overlays/Overlays.h | 19 +++++------ interface/src/ui/overlays/PanelAttachable.cpp | 2 +- interface/src/ui/overlays/PanelAttachable.h | 20 ++++++++++++ 4 files changed, 55 insertions(+), 18 deletions(-) diff --git a/examples/libraries/overlayManager.js b/examples/libraries/overlayManager.js index bf882c3d78..cf4df04747 100644 --- a/examples/libraries/overlayManager.js +++ b/examples/libraries/overlayManager.js @@ -5,6 +5,9 @@ // Created by Zander Otavka on 7/24/15 // Copyright 2015 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 +// // Manage overlays with object oriented goodness, instead of ugly `Overlays.h` methods. // Instead of: // @@ -22,14 +25,27 @@ // ... // billboard.destroy(); // -// See more on usage below. +// More on usage below. Examples in `examples/example/overlayPanelExample.js`. // -// Note that including this file will delete Overlays from the global scope. All the -// functionality of Overlays is represented here, just better. If you try to use Overlays in -// tandem, there may be performance problems or nasty surprises. +// Note that including this file will delete `Overlays` from the global scope. All the +// functionality of `Overlays` is represented here, just better. If you try to use `Overlays` +// in tandem, there may be performance problems or nasty surprises. // -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// Names added to the global scope: +// OverlayManager +// ImageOverlay +// Image3DOverlay +// TextOverlay +// Text3DOverlay +// Cube3DOverlay +// Sphere3DOverlay +// Circle3DOverlay +// Rectangle3DOverlay +// Line3DOverlay +// Grid3DOverlay +// LocalModelsOverlay +// ModelOverlay +// OverlayPanel // @@ -197,8 +213,8 @@ // billboard.destroy(); // // // Remember, there is a poor orphaned JavaScript object left behind. You should - // // remove any references to it so you don't accidentally try to modify an overlay that - // // isn't there. + // // remove any references to it so you don't accidentally try to modify an overlay + // // that isn't there. // billboard = undefined; // (function() { diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index 212b7b227d..bb4d94cdab 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -5,14 +5,15 @@ // Modified by Zander Otavka on 7/15/15 // Copyright 2014 High Fidelity, Inc. // -// Exposes methods for managing `Overlay`s and `OverlayPanel`s to scripts. -// -// YOU SHOULD NOT USE `Overlays` DIRECTLY, unless you like pain and deprecation. Instead, use the -// object oriented abstraction layer found in `examples/libraries/overlayUtils.js`. -// // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +// Exposes methods to scripts for managing `Overlay`s and `OverlayPanel`s. +// +// YOU SHOULD NOT USE `Overlays` DIRECTLY, unless you like pain and deprecation. Instead, use +// the object oriented API replacement found in `examples/libraries/overlayManager.js`. See +// that file for docs and usage. +// #ifndef hifi_Overlays_h #define hifi_Overlays_h @@ -57,11 +58,11 @@ void RayToOverlayIntersectionResultFromScriptValue(const QScriptValue& object, R class Overlays : public QObject { Q_OBJECT - + public: Overlays(); ~Overlays(); - + void init(); void update(float deltatime); void renderHUD(RenderArgs* renderArgs); @@ -103,7 +104,7 @@ public slots: /// returns details about the closest 3D Overlay hit by the pick ray RayToOverlayIntersectionResult findRayIntersection(const PickRay& ray); - + /// returns whether the overlay's assets are loaded or not bool isLoaded(unsigned int id); @@ -153,5 +154,5 @@ private: }; - + #endif // hifi_Overlays_h diff --git a/interface/src/ui/overlays/PanelAttachable.cpp b/interface/src/ui/overlays/PanelAttachable.cpp index 91a7525257..da3a37db45 100644 --- a/interface/src/ui/overlays/PanelAttachable.cpp +++ b/interface/src/ui/overlays/PanelAttachable.cpp @@ -1,6 +1,6 @@ // // PanelAttachable.cpp -// hifi +// interface/src/ui/overlays // // Created by Zander Otavka on 7/15/15. // Copyright 2015 High Fidelity, Inc. diff --git a/interface/src/ui/overlays/PanelAttachable.h b/interface/src/ui/overlays/PanelAttachable.h index 5242450e5d..f4e982f8e9 100644 --- a/interface/src/ui/overlays/PanelAttachable.h +++ b/interface/src/ui/overlays/PanelAttachable.h @@ -8,6 +8,24 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +// Base class for anything that can attach itself to an `OverlayPanel` as a child. +// `PanelAttachable keeps an `std::shared_ptr` to it's parent panel, and sets its +// transformations and visibility based on the parent. +// +// When subclassing `PanelAttachable`, make sure `applyTransformTo`, `getProperty`, and +// `setProperties are all called in the appropriate places. Look through `Image3DOverlay` and +// `Billboard3DOverlay` for examples. Pay special attention to `applyTransformTo`; it should +// be called in three places for `Overlay`s: `render`, `update`, and `findRayIntersection`. +// +// When overriding `applyTransformTo`, make sure to wrap all of your code, including the call +// to the superclass method, with the following `if` block. Then call the superclass method +// with force = true. +// +// if (force || usecTimestampNow() > _transformExpiry) { +// PanelAttachable::applyTransformTo(transform, true); +// ... +// } +// #ifndef hifi_PanelAttachable_h #define hifi_PanelAttachable_h @@ -42,6 +60,8 @@ protected: QScriptValue getProperty(QScriptEngine* scriptEngine, const QString& property); void setProperties(const QScriptValue& properties); + /// set position, rotation and scale on transform based on offsets, and parent panel offsets + /// if force is false, only apply transform if it hasn't been applied in the last .1 seconds virtual void applyTransformTo(Transform& transform, bool force = false); quint64 _transformExpiry = 0; From b84b8b6119abbcf363c65d33df2f7bf547e152ae Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Wed, 12 Aug 2015 23:30:07 -0700 Subject: [PATCH 17/49] Restructure overlayManager.js to be more readable. --- examples/libraries/overlayManager.js | 426 +++++++++++---------------- 1 file changed, 172 insertions(+), 254 deletions(-) diff --git a/examples/libraries/overlayManager.js b/examples/libraries/overlayManager.js index cf4df04747..0623721375 100644 --- a/examples/libraries/overlayManager.js +++ b/examples/libraries/overlayManager.js @@ -31,46 +31,53 @@ // functionality of `Overlays` is represented here, just better. If you try to use `Overlays` // in tandem, there may be performance problems or nasty surprises. // -// Names added to the global scope: -// OverlayManager -// ImageOverlay -// Image3DOverlay -// TextOverlay -// Text3DOverlay -// Cube3DOverlay -// Sphere3DOverlay -// Circle3DOverlay -// Rectangle3DOverlay -// Line3DOverlay -// Grid3DOverlay -// LocalModelsOverlay -// ModelOverlay -// OverlayPanel -// - (function() { // Delete `Overlays` from the global scope. var Overlays = this.Overlays; delete this.Overlays; + + var ABSTRACT = null; + var overlays = {}; var panels = {}; - var overlayTypes; + var overlayTypes = {}; - // Abstract overlay types - var Overlay, - Overlay2D, - Base3DOverlay, - Planar3DOverlay, - Billboard3DOverlay, - Volume3DOverlay; - // Multiple inheritance mixins - var PanelAttachable, - Billboardable; + function generateOverlayClass(superclass, type, properties) { + var that; + if (type == ABSTRACT) { + that = function(type, params) { + superclass.call(this, type, params); + }; + } else { + that = function(params) { + superclass.call(this, type, params); + }; + overlayTypes[type] = that; + } + that.prototype = new superclass(); + that.prototype.constructor = that; + + properties.forEach(function(prop) { + Object.defineProperty(that.prototype, prop, { + get: function() { + return Overlays.getProperty(this._id, prop); + }, + set: function(newValue) { + var keyValuePair = {}; + keyValuePair[prop] = newValue; + this.setProperties(keyValuePair); + }, + configurable: false + }); + }); + + return that; + } // // Create a new JavaScript object for an overlay of given ID. @@ -153,249 +160,131 @@ } - // - // Perform global scoped operations on overlays, such as finding by ray intersection. - // - OverlayManager = { - findOnRay: function(pickRay, knownOverlaysOnly, searchList) { - var rayPickResult = Overlays.findRayIntersection(pickRay); - if (rayPickResult.intersects) { - return findOverlay(rayPickResult.overlayID, knownOverlaysOnly, searchList); - } - return null; - }, - findAtPoint: function(point, knownOverlaysOnly, searchList) { - var foundID = Overlays.getOverlayAtPoint(point); - if (foundID) { - return findOverlay(foundID, knownOverlaysOnly, searchList); + var Overlay = (function() { + var that = function(type, params) { + if (type && params) { + this._id = Overlays.addOverlay(type, params); + overlays[this._id] = this; } else { - var pickRay = Camera.computePickRay(point.x, point.y); - return OverlayManager.findOnRay(pickRay, knownOverlaysOnly, searchList); + this._id = 0; } - }, - makeSearchList: function(array) { - var searchList = {}; - array.forEach(function(object) { - searchList[object._id] = object; - }); - return searchList; - } - }; + }; + that.prototype.constructor = that; - // - // Object oriented abstraction layer for overlays. - // - // Usage: - // // Create an overlay - // var billboard = new Image3DOverlay({ - // visible: true, - // isFacingAvatar: true, - // ignoreRayIntersections: false - // }); - // - // // Get a property - // var isVisible = billboard.visible; - // - // // Set a single property - // billboard.position = { x: 1, y: 3, z: 2 }; - // - // // Set multiple properties at the same time - // billboard.setProperties({ - // url: "http://images.com/overlayImage.jpg", - // dimensions: { x: 2, y: 2 } - // }); - // - // // Clone an overlay - // var clonedBillboard = billboard.clone(); - // - // // Remove an overlay from the world - // billboard.destroy(); - // - // // Remember, there is a poor orphaned JavaScript object left behind. You should - // // remove any references to it so you don't accidentally try to modify an overlay - // // that isn't there. - // billboard = undefined; - // - (function() { - var ABSTRACT = null; - overlayTypes = {}; - - function generateOverlayClass(superclass, type, properties) { - var that; - if (type == ABSTRACT) { - that = function(type, params) { - superclass.call(this, type, params); - }; - } else { - that = function(params) { - superclass.call(this, type, params); - }; - overlayTypes[type] = that; + Object.defineProperty(that.prototype, "isLoaded", { + get: function() { + return Overlays.isLoaded(this._id); } + }); - that.prototype = new superclass(); - that.prototype.constructor = that; + Object.defineProperty(that.prototype, "parentPanel", { + get: function() { + return findPanel(Overlays.getParentPanel(this._id)); + } + }); - properties.forEach(function(prop) { - Object.defineProperty(that.prototype, prop, { - get: function() { - return Overlays.getProperty(this._id, prop); - }, - set: function(newValue) { - var keyValuePair = {}; - keyValuePair[prop] = newValue; - this.setProperties(keyValuePair); - }, - configurable: false - }); - }); + that.prototype.getTextSize = function(text) { + return Overlays.textSize(this._id, text); + }; - return that; - } + that.prototype.setProperties = function(properties) { + Overlays.editOverlay(this._id, properties); + }; - Overlay = (function() { - var that = function(type, params) { - if (type && params) { - this._id = Overlays.addOverlay(type, params); - overlays[this._id] = this; - } else { - this._id = 0; - } - }; + that.prototype.clone = function() { + return makeOverlayFromId(Overlays.cloneOverlay(this._id)); + }; - that.prototype.constructor = that; + that.prototype.destroy = function() { + Overlays.deleteOverlay(this._id); + }; - Object.defineProperty(that.prototype, "isLoaded", { - get: function() { - return Overlays.isLoaded(this._id); - } - }); + that.prototype.isPanelAttachable = function() { + return false; + }; - Object.defineProperty(that.prototype, "parentPanel", { - get: function() { - return findPanel(Overlays.getParentPanel(this._id)); - } - }); - - that.prototype.getTextSize = function(text) { - return Overlays.textSize(this._id, text); - }; - - that.prototype.setProperties = function(properties) { - Overlays.editOverlay(this._id, properties); - }; - - that.prototype.clone = function() { - return makeOverlayFromId(Overlays.cloneOverlay(this._id)); - }; - - that.prototype.destroy = function() { - Overlays.deleteOverlay(this._id); - }; - - that.prototype.isPanelAttachable = function() { - return false; - }; - - return generateOverlayClass(that, ABSTRACT, [ - "alpha", "glowLevel", "pulseMax", "pulseMin", "pulsePeriod", "glowLevelPulse", - "alphaPulse", "colorPulse", "visible", "anchor" - ]); - })(); - - // Supports multiple inheritance of properties. Just `concat` them onto the end of the - // properties list. - PanelAttachable = ["offsetPosition", "offsetRotation", "offsetScale"]; - Billboardable = ["isFacingAvatar"]; - - Overlay2D = generateOverlayClass(Overlay, ABSTRACT, [ - "bounds", "x", "y", "width", "height" - ]); - - Base3DOverlay = generateOverlayClass(Overlay, ABSTRACT, [ - "position", "lineWidth", "rotation", "isSolid", "isFilled", "isWire", "isDashedLine", - "ignoreRayIntersection", "drawInFront", "drawOnHUD" - ]); - - Planar3DOverlay = generateOverlayClass(Base3DOverlay, ABSTRACT, [ - "dimensions" - ]); - - Billboard3DOverlay = generateOverlayClass(Planar3DOverlay, ABSTRACT, [ - ].concat(PanelAttachable).concat(Billboardable)); - Billboard3DOverlay.prototype.isPanelAttachable = function() { return true; }; - - Volume3DOverlay = generateOverlayClass(Base3DOverlay, ABSTRACT, [ - "dimensions" - ]); - - generateOverlayClass(Overlay2D, "image", [ - "subImage", "imageURL" - ]); - - generateOverlayClass(Billboard3DOverlay, "image3d", [ - "url", "subImage" - ]); - - generateOverlayClass(Overlay2D, "text", [ - "font", "text", "backgroundColor", "backgroundAlpha", "leftMargin", "topMargin" - ]); - - generateOverlayClass(Billboard3DOverlay, "text3d", [ - "text", "backgroundColor", "backgroundAlpha", "lineHeight", "leftMargin", "topMargin", - "rightMargin", "bottomMargin" - ]); - - generateOverlayClass(Volume3DOverlay, "cube", [ - "borderSize" - ]); - - generateOverlayClass(Volume3DOverlay, "sphere", [ - ]); - - generateOverlayClass(Planar3DOverlay, "circle3d", [ - "startAt", "endAt", "outerRadius", "innerRadius", "hasTickMarks", - "majorTickMarksAngle", "minorTickMarksAngle", "majorTickMarksLength", - "minorTickMarksLength", "majorTickMarksColor", "minorTickMarksColor" - ]); - - generateOverlayClass(Planar3DOverlay, "rectangle3d", [ - ]); - - generateOverlayClass(Base3DOverlay, "line3d", [ - "start", "end" - ]); - - generateOverlayClass(Planar3DOverlay, "grid", [ - "minorGridWidth", "majorGridEvery" - ]); - - generateOverlayClass(Volume3DOverlay, "localmodels", [ - ]); - - generateOverlayClass(Volume3DOverlay, "model", [ - "url", "dimensions", "textures" + return generateOverlayClass(that, ABSTRACT, [ + "alpha", "glowLevel", "pulseMax", "pulseMin", "pulsePeriod", "glowLevelPulse", + "alphaPulse", "colorPulse", "visible", "anchor" ]); })(); - ImageOverlay = overlayTypes["image"]; - Image3DOverlay = overlayTypes["image3d"]; - TextOverlay = overlayTypes["text"]; - Text3DOverlay = overlayTypes["text3d"]; - Cube3DOverlay = overlayTypes["cube"]; - Sphere3DOverlay = overlayTypes["sphere"]; - Circle3DOverlay = overlayTypes["circle3d"]; - Rectangle3DOverlay = overlayTypes["rectangle3d"]; - Line3DOverlay = overlayTypes["line3d"]; - Grid3DOverlay = overlayTypes["grid"]; - LocalModelsOverlay = overlayTypes["localmodels"]; - ModelOverlay = overlayTypes["model"]; + // Supports multiple inheritance of properties. Just `concat` them onto the end of the + // properties list. + var PanelAttachable = ["offsetPosition", "offsetRotation", "offsetScale"]; + var Billboardable = ["isFacingAvatar"]; + + var Overlay2D = generateOverlayClass(Overlay, ABSTRACT, [ + "bounds", "x", "y", "width", "height" + ]); + + var Base3DOverlay = generateOverlayClass(Overlay, ABSTRACT, [ + "position", "lineWidth", "rotation", "isSolid", "isFilled", "isWire", "isDashedLine", + "ignoreRayIntersection", "drawInFront", "drawOnHUD" + ]); + + var Planar3DOverlay = generateOverlayClass(Base3DOverlay, ABSTRACT, [ + "dimensions" + ]); + + var Billboard3DOverlay = generateOverlayClass(Planar3DOverlay, ABSTRACT, [ + ].concat(PanelAttachable).concat(Billboardable)); + Billboard3DOverlay.prototype.isPanelAttachable = function() { return true; }; + + var Volume3DOverlay = generateOverlayClass(Base3DOverlay, ABSTRACT, [ + "dimensions" + ]); + + ImageOverlay = generateOverlayClass(Overlay2D, "image", [ + "subImage", "imageURL" + ]); + + Image3DOverlay = generateOverlayClass(Billboard3DOverlay, "image3d", [ + "url", "subImage" + ]); + + TextOverlay = generateOverlayClass(Overlay2D, "text", [ + "font", "text", "backgroundColor", "backgroundAlpha", "leftMargin", "topMargin" + ]); + + Text3DOverlay = generateOverlayClass(Billboard3DOverlay, "text3d", [ + "text", "backgroundColor", "backgroundAlpha", "lineHeight", "leftMargin", "topMargin", + "rightMargin", "bottomMargin" + ]); + + Cube3DOverlay = generateOverlayClass(Volume3DOverlay, "cube", [ + "borderSize" + ]); + + Sphere3DOverlay = generateOverlayClass(Volume3DOverlay, "sphere", [ + ]); + + Circle3DOverlay = generateOverlayClass(Planar3DOverlay, "circle3d", [ + "startAt", "endAt", "outerRadius", "innerRadius", "hasTickMarks", + "majorTickMarksAngle", "minorTickMarksAngle", "majorTickMarksLength", + "minorTickMarksLength", "majorTickMarksColor", "minorTickMarksColor" + ]); + + Rectangle3DOverlay = generateOverlayClass(Planar3DOverlay, "rectangle3d", [ + ]); + + Line3DOverlay = generateOverlayClass(Base3DOverlay, "line3d", [ + "start", "end" + ]); + + Grid3DOverlay = generateOverlayClass(Planar3DOverlay, "grid", [ + "minorGridWidth", "majorGridEvery" + ]); + + LocalModelsOverlay = generateOverlayClass(Volume3DOverlay, "localmodels", [ + ]); + + ModelOverlay = generateOverlayClass(Volume3DOverlay, "model", [ + "url", "dimensions", "textures" + ]); - // - // Object oriented abstraction layer for panels. - // OverlayPanel = (function() { var that = function(params) { this._id = Overlays.addPanel(params); @@ -471,6 +360,35 @@ })(); + OverlayManager = { + findOnRay: function(pickRay, knownOverlaysOnly, searchList) { + var rayPickResult = Overlays.findRayIntersection(pickRay); + if (rayPickResult.intersects) { + return findOverlay(rayPickResult.overlayID, knownOverlaysOnly, searchList); + } + return null; + }, + findAtPoint: function(point, knownOverlaysOnly, searchList) { + var foundID = Overlays.getOverlayAtPoint(point); + if (foundID) { + return findOverlay(foundID, knownOverlaysOnly, searchList); + } else { + var pickRay = Camera.computePickRay(point.x, point.y); + return OverlayManager.findOnRay(pickRay, knownOverlaysOnly, searchList); + } + }, + makeSearchList: function(array) { + var searchList = {}; + array.forEach(function(object) { + searchList[object._id] = object; + }); + return searchList; + } + }; + + + // Threadsafe cleanup of JavaScript objects. + function onOverlayDeleted(id) { if (id in overlays) { if (overlays[id].parentPanel) { From 0daa9d4871336866d6e5b7dbbf2a9b2ea559a205 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 13 Aug 2015 09:38:21 -0700 Subject: [PATCH 18/49] Remove extra rotation in image3d --- interface/src/ui/overlays/Image3DOverlay.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/interface/src/ui/overlays/Image3DOverlay.cpp b/interface/src/ui/overlays/Image3DOverlay.cpp index c13dbbb139..d5ad3d511f 100644 --- a/interface/src/ui/overlays/Image3DOverlay.cpp +++ b/interface/src/ui/overlays/Image3DOverlay.cpp @@ -90,7 +90,6 @@ void Image3DOverlay::render(RenderArgs* args) { applyTransformTo(_transform, true); Transform transform = _transform; transform.postScale(glm::vec3(getDimensions(), 1.0f)); - transform.postRotate(glm::angleAxis(glm::pi(), IDENTITY_UP)); batch->setModelTransform(transform); batch->setResourceTexture(0, _texture->getGPUTexture()); From 8c47bcc141e59ce72bf6f141cfcd4c1ac77b4266 Mon Sep 17 00:00:00 2001 From: bwent Date: Thu, 13 Aug 2015 16:00:55 -0700 Subject: [PATCH 19/49] implement new UI --- examples/example/games/satellite.js | 47 +- examples/example/planets-ui.js | 458 +++++++++++ examples/example/solarsystem.js | 1088 ++++++++++----------------- examples/libraries/uiwidgets.js | 26 +- 4 files changed, 896 insertions(+), 723 deletions(-) create mode 100644 examples/example/planets-ui.js diff --git a/examples/example/games/satellite.js b/examples/example/games/satellite.js index fd1243fbe0..8e37472ea4 100644 --- a/examples/example/games/satellite.js +++ b/examples/example/games/satellite.js @@ -19,7 +19,8 @@ Script.include('../utilities/tools/vector.js'); var URL = "https://s3.amazonaws.com/hifi-public/marketplace/hificontent/Scripts/planets/"; -SatelliteGame = function() { +SatelliteCreator = function() { + print("initializing satellite game"); var MAX_RANGE = 50.0; @@ -38,6 +39,9 @@ SatelliteGame = function() { var ZONE_DIM = 100.0; var LIGHT_INTENSITY = 1.5; + var center, distance; + + Earth = function(position, size) { this.earth = Entities.addEntity({ type: "Model", @@ -109,10 +113,35 @@ SatelliteGame = function() { } } - // Create earth model - var center = Vec3.sum(Camera.getPosition(), Vec3.multiply(MAX_RANGE, Quat.getFront(Camera.getOrientation()))); - var distance = Vec3.length(Vec3.subtract(center, Camera.getPosition())); - var earth = new Earth(center, EARTH_SIZE); + + this.init = function() { + if (this.isActive) { + this.quitGame(); + } + var confirmed = Window.confirm("Start satellite game?"); + if (!confirmed) { + return false; + } + + this.isActive = true; + MyAvatar.position = { + x: 200, + y: 200, + z: 200 + }; + Camera.setPosition({ + x: 200, + y: 200, + z: 200 + }); + + // Create earth model + center = Vec3.sum(Camera.getPosition(), Vec3.multiply(MAX_RANGE, Quat.getFront(Camera.getOrientation()))); + distance = Vec3.length(Vec3.subtract(center, Camera.getPosition())); + var earth = new Earth(center, EARTH_SIZE); + return true; + }; + var satellites = []; var SATELLITE_SIZE = 2.0; @@ -259,11 +288,12 @@ SatelliteGame = function() { } } - this.endGame = function() { + this.quitGame = function() { for (var i = 0; i < satellites.length; i++) { Entities.deleteEntity(satellites[i].satellite); satellites[i].arrow.cleanup(); } + this.isActive = false; earth.cleanup(); } @@ -285,6 +315,7 @@ SatelliteGame = function() { Controller.mouseMoveEvent.connect(mouseMoveEvent); Controller.mouseReleaseEvent.connect(mouseReleaseEvent); Script.update.connect(update); - Script.scriptEnding.connect(this.endGame); + Script.scriptEnding.connect(this.quitGame); + +} -} \ No newline at end of file diff --git a/examples/example/planets-ui.js b/examples/example/planets-ui.js new file mode 100644 index 0000000000..56cf7b18e1 --- /dev/null +++ b/examples/example/planets-ui.js @@ -0,0 +1,458 @@ +// +// widgets-example.js +// games +// +// Copyright 2015 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 +// +var ICONS_URL = 'https://s3.amazonaws.com/hifi-public/marketplace/hificontent/Scripts/planets/images/'; + +var panelX = 1250; +var panelY = 500; +var panelWidth = 50; +var panelHeight = 210; + +Script.include('../libraries/uiwidgets.js'); + +UI.setDefaultVisibility(true); + +var ICON_WIDTH = 40.0; +var ICON_HEIGHT = 40.0; +var ICON_COLOR = UI.rgba(45, 45, 45, 0.7); +var FOCUSED_COLOR = UI.rgba(250, 250, 250, 1.0); + +var PANEL_BACKGROUND_COLOR = UI.rgba(100, 100, 100, 0.7); + +var PANEL_PADDING = 7.0; +var PANEL_BORDER = 12.0; +var SUBPANEL_GAP = 1.0; + +var icons = []; + +function addImage(panel, iconId) { + var icon = panel.add(new UI.Image({ + 'imageURL': ICONS_URL + iconId + '.svg', + 'width': ICON_WIDTH, + 'height': ICON_HEIGHT, + 'color': ICON_COLOR, + 'alpha': ICON_COLOR.a + })); + icons.push(icon); + return icon; +} + + +var panels = []; + +function addPanel(properties) { + properties.background = properties.background || {}; + properties.background.backgroundColor = properties.background.backgroundColor || + PANEL_BACKGROUND_COLOR; + properties.background.backgroundAlpha = properties.background.backgroundAlpha || + PANEL_BACKGROUND_COLOR.a; + properties.padding = properties.padding || { + x: PANEL_PADDING, + y: PANEL_PADDING + }; + properties.border = properties.border || { + x: PANEL_BORDER, + y: PANEL_BORDER + }; + + var panel = new UI.WidgetStack(properties); + panels.push(panel); + return panel; +} + +function makeDraggable(panel, target) { + if (!target) { + target = panel; + } + var dragStart = null; + var initialPos = null; + + panel.addAction('onDragBegin', function(event) { + dragStart = { + x: event.x, + y: event.y + }; + initialPos = { + x: target.position.x, + y: target.position.y + }; + }); + panel.addAction('onDragUpdate', function(event) { + target.setPosition( + initialPos.x + event.x - dragStart.x, + initialPos.y + event.y - dragStart.y + ); + UI.updateLayout(); + }); + panel.addAction('onDragEnd', function() { + dragStart = dragEnd = null; + }); +} + +function setText(text) { + return function() { + demoLabel.setText(text); + UI.updateLayout(); + }; +} + +function join(obj) { + var s = "{"; + var sep = "\n"; + for (var k in obj) { + s += sep + k + ": " + ("" + obj[k]).replace("\n", "\n"); + sep = ",\n"; + } + if (s.length > 1) + return s + " }"; + return s + "}"; +} + +setText = undefined; + +var tooltipWidget = new UI.Label({ + text: "", + width: 500, + height: 20, + visible: false +}); + +function addTooltip(widget, text) { + widget.addAction('onMouseOver', function(event, widget) { + tooltipWidget.setVisible(true); + tooltipWidget.setPosition(widget.position.x + widget.getWidth() + 20, widget.position.y); + tooltipWidget.setText(text); + UI.updateLayout(); + }); + widget.addAction('onMouseExit', function() { + tooltipWidget.setVisible(false); + UI.updateLayout(); + }); +} + +var mainPanel = addPanel({ + dir: '+y' +}); +makeDraggable(mainPanel); +mainPanel.setPosition(1200, 250); +mainPanel.setVisible(true); + +var systemViewButton = addImage(mainPanel, 'solarsystems'); +var zoomButton = addImage(mainPanel, 'magnifier'); +var satelliteButton = addImage(mainPanel, 'satellite'); +var settingsButton = addImage(mainPanel, 'settings'); +var stopButton = addImage(mainPanel, 'close'); + +addTooltip(systemViewButton, "system view"); +addTooltip(zoomButton, "zoom"); +addTooltip(satelliteButton, "satelite view"); +addTooltip(settingsButton, "settings"); +addTooltip(stopButton, "exit"); + +var systemViewPanel = addPanel({ + dir: '+x', + visible: false +}); +var restartButton = addImage(systemViewPanel, 'reverse'); +var pauseButton = addImage(systemViewPanel, 'playpause'); +var rideButton = addImage(systemViewPanel, 'forward'); + +pauseButton.addAction('onClick', function() { + if (!paused) { + pause(); + } else { + resume(); + } +}); + +// rideButton.addAction('onClick', function() { +// Script.include('orbit-ride.js'); +// new OrbitRider(); +// }); + +restartButton.addAction('onClick', function() { + Script.stop(); + var thisScript = Script.resolvePath("solarsystem.js"); + Script.load(thisScript); +}); + +var zoomPanel = addPanel({ + dir: '+x', + visible: false +}); +var zoomButtons = []; +for (var i = 0; i < planets.length; ++i) { + var label = zoomPanel.add(new UI.Label({ + text: planets[i].name, + width: 80, + height: 20, + backgroundAlpha: 1.0 + })); + zoomButtons.push(label); + UI.updateLayout(); +} +UI.updateLayout(); + +zoomButtons.forEach(function(button, i) { + var planet = planets[i]; + button.addAction('onClick', function() { + planet.zoom(); + }); +}); + + +var settingsPanel = addPanel({ + dir: '+y', + visible: false +}); + +function addCheckbox(parent, label, labelWidth, enabled, onValueChanged) { + var layout = parent.add(new UI.WidgetStack({ + dir: '+x', + visible: true, + backgroundAlpha: 0.0 + })); + var label = layout.add(new UI.Label({ + text: label, + width: labelWidth, + height: 20, + backgroundAlpha: 0.0 + })); + + var defaultColor = UI.rgb(10, 10, 10); + + var checkbox = layout.add(new UI.Checkbox({ + width: 20, + height: 20, + padding: { + x: 3, + y: 3 + }, + backgroundColor: defaultColor, + backgroundAlpha: 0.9, + checked: enabled, + onValueChanged: onValueChanged + })); + + checkbox.label = label; + checkbox.layout = layout; + checkbox.setValue = function(value) { + checkbox.setChecked(value); + } + return checkbox; +} + +function addSlider(parent, label, labelWidth, defaultValue, min, max, valueChanged) { + var layout = parent.add(new UI.WidgetStack({ + dir: '+x', + visible: true + })); + var label = layout.add(new UI.Label({ + text: label, + width: labelWidth, + height: 27 + })); + var display = layout.add(new UI.Label({ + text: " ", + width: 50, + height: 27 + })); + var slider = layout.add(new UI.Slider({ + value: defaultValue, + maxValue: max, + minValue: min, + width: 300, + height: 20, + backgroundColor: UI.rgb(10, 10, 10), + backgroundAlpha: 1.0, + slider: { // slider knob + width: 30, + height: 18, + backgroundColor: UI.rgb(120, 120, 120), + backgroundAlpha: 1.0 + } + })); + slider.addAction('onDoubleClick', function() { + slider.setValue(defaultValue); + UI.updateLayout(); + }); + display.setText("" + (+slider.getValue().toFixed(2))); + slider.onValueChanged = function(value) { + valueChanged(value); + display.setText("" + (+value.toFixed(2))); + UI.updateLayout(); + } + slider.label = label; + slider.layout = layout; + return slider; +} + +settingsPanel.showTrailsButton = addCheckbox(settingsPanel, "show trails", 120, trailsEnabled, function(trailsEnabled) { + if (trailsEnabled) { + for (var i = 0; i < planets.length; ++i) { + planets[i].resetTrails(); + } + //if trails are off and we've already created trails, remove existing trails + } else { + for (var i = 0; i < planets.length; ++i) { + planets[i].clearTrails(); + } + } +}); +var g_multiplier = 1.0; +settingsPanel.gravitySlider = addSlider(settingsPanel, "gravity scale ", 200, g_multiplier, 0.0, 5.0, function (value) { + g_multiplier = value; + GRAVITY = REFERENCE_GRAVITY * g_multiplier; +}); + +var period_multiplier = 1.0; +var last_alpha = period_multiplier; + +settingsPanel.periodSlider = addSlider(settingsPanel, "orbital period scale ", 200, period_multiplier, 0.0, 3.0, function (value) { + period_multiplier = value; + changePeriod(period_multiplier); +}); + +function changePeriod(alpha) { + var ratio = last_alpha / alpha; + GRAVITY = Math.pow(ratio, 2.0) * GRAVITY; + for (var i = 0; i < planets.length; ++i) { + planets[i].period = ratio * planets[i].period; + planets[i].velocity = Vec3.multiply(ratio, planets[i].velocity); + planets[i].resetTrails(); + } + last_alpha = alpha; +} + +var satelliteGame; +satelliteButton.addAction('onClick', function() { + if (satelliteGame && satelliteGame.isActive) { + MyAvatar.position = startingPosition; + satelliteGame.quitGame(); + resume(); + } else { + pause(); + satelliteGame = new SatelliteCreator(); + satelliteGame.init(); + } +}); + + +var subpanels = [systemViewPanel, zoomPanel, settingsPanel]; + +function hideSubpanelsExcept(panel) { + subpanels.forEach(function(x) { + if (x != panel) { + x.setVisible(false); + } + }); +} + +function attachPanel(panel, button) { + button.addAction('onClick', function() { + hideSubpanelsExcept(panel); + panel.setVisible(!panel.isVisible()); + UI.updateLayout(); + }) + + UI.addAttachment(panel, button, function(target, rel) { + target.setPosition( + rel.position.x - (target.getWidth() + target.border.x + SUBPANEL_GAP), + rel.position.y - target.border.y + ); + }); +} +attachPanel(systemViewPanel, systemViewButton); +attachPanel(zoomPanel, zoomButton); +attachPanel(settingsPanel, settingsButton); + + +var addColorToggle = function(widget) { + widget.addAction('onMouseOver', function() { + widget.setColor(FOCUSED_COLOR); + }); + widget.addAction('onMouseExit', function() { + widget.setColor(ICON_COLOR); + }); +} + + +systemViewPanel.addAction('onMouseOver', function() { + hideSubpanelsExcept(systemViewPanel); + UI.updateLayout(); +}); + + +zoomButton.addAction('onClick', function() { + hideSubpanelsExcept(zoomPanel); + UI.updateLayout(); +}); +UI.updateLayout(); + +stopButton.addAction('onClick', function() { + teardown(); + Script.stop(); +}); + +// Panel drag behavior +// (click + drag on border to drag) +(function() { + var dragged = null; + this.startDrag = function(dragAction) { + dragged = dragAction; + } + this.updateDrag = function(event) { + if (dragged) { + print("Update drag"); + dragged.updateDrag(event); + } + } + this.clearDrag = function(event) { + if (dragged) + print("End drag"); + dragged = null; + } +})(); + +var buttons = icons; + +buttons.map(addColorToggle); +panels.map(function(panel) { + makeDraggable(panel, mainPanel); +}); + +// Cleanup script resources +function teardown() { + if (satelliteGame) { + satelliteGame.quitGame(); + } + UI.teardown(); +}; + +UI.debug.setVisible(false); + +var inputHandler = { + onMouseMove: function(event) { + updateDrag(event); + UI.handleMouseMove(event); + }, + onMousePress: function(event) { + UI.handleMousePress(event); + }, + onMouseRelease: function(event) { + clearDrag(event); + UI.handleMouseRelease(event); + }, + onMouseDoublePress: function(event) { + UI.handleMouseDoublePress(event); + } +}; +Controller.mousePressEvent.connect(inputHandler.onMousePress); +Controller.mouseMoveEvent.connect(inputHandler.onMouseMove); +Controller.mouseReleaseEvent.connect(inputHandler.onMouseRelease); +Controller.mouseDoublePressEvent.connect(inputHandler.onMouseDoublePress); diff --git a/examples/example/solarsystem.js b/examples/example/solarsystem.js index 196ad57f86..3e4a39ed71 100644 --- a/examples/example/solarsystem.js +++ b/examples/example/solarsystem.js @@ -18,797 +18,463 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -Script.include([ - 'games/satellite.js' -]); +CreateSimulation = function() { + Script.include("https://hifi-public.s3.amazonaws.com/eric/scripts/tween.js"); + Script.include('games/satellite.js'); -var DAMPING = 0.0; -var LIFETIME = 6000; -var BASE_URL = "https://s3.amazonaws.com/hifi-public/marketplace/hificontent/Scripts/planets/planets/"; + var DAMPING = this.DAMPING = 0.0; + var LIFETIME = this.LIFETIME = 6000; + var BASE_URL = this.BASE_URL = "https://s3.amazonaws.com/hifi-public/marketplace/hificontent/Scripts/planets/planets/"; -// Save intiial avatar and camera position -var startingPosition = { - x: 8000, - y: 8000, - z: 8000 -}; -MyAvatar.position = startingPosition; -var cameraStart = Camera.getOrientation(); + // Save intiial avatar and camera position + var startingPosition = this.startingPosition = { + x: 8000, + y: 8000, + z: 8000 + }; + MyAvatar.position = startingPosition; + var cameraStart = this.cameraStart = Camera.getOrientation(); -// Place the sun -var MAX_RANGE = 80.0; -var center = Vec3.sum(startingPosition, Vec3.multiply(MAX_RANGE, Vec3.normalize(Quat.getFront(Camera.getOrientation())))); -var SUN_SIZE = 7.0; + // Place the sun + var MAX_RANGE = this.MAX_RANGE = 80.0; + var center = this.center = Vec3.sum(startingPosition, Vec3.multiply(MAX_RANGE, Vec3.normalize(Quat.getFront(Camera.getOrientation())))); + var SUN_SIZE = this.SUN_SIZE = 7.0; -var theSun = Entities.addEntity({ - type: "Model", - modelURL: BASE_URL + "sun.fbx", - position: center, - dimensions: { - x: SUN_SIZE, - y: SUN_SIZE, - z: SUN_SIZE - }, - angularVelocity: { - x: 0.0, - y: 0.1, - z: 0.0 - }, - angularDamping: DAMPING, - damping: DAMPING, - ignoreCollisions: false, - lifetime: LIFETIME, - collisionsWillMove: false -}); - - - -// Reference values for physical constants -var referenceRadius = 15.0; -var referenceDiameter = 0.6; -var referencePeriod = 3.0; -var LARGE_BODY_MASS = 250.0; -var SMALL_BODY_MASS = LARGE_BODY_MASS * 0.000000333; -var GRAVITY = (Math.pow(referenceRadius, 3.0) / Math.pow((referencePeriod / (2.0 * Math.PI)), 2.0)) / LARGE_BODY_MASS; -var REFERENCE_GRAVITY = GRAVITY; - -var planets = []; -var planetCount = 0; - -var trailsEnabled = true; -var MAX_POINTS_PER_LINE = 60; -var LINE_DIM = 200; -var LINE_WIDTH = 20; - -var VELOCITY_OFFSET_Y = -0.3; -var VELOCITY_OFFSET_Z = 0.9; - -var index = 0; - -var Planet = function(name, trailColor, radiusScale, sizeScale) { - - this.name = name; - this.trailColor = trailColor; - - this.trail = []; - this.lineStack = []; - - this.radius = radiusScale * referenceRadius; - this.position = Vec3.sum(center, { - x: this.radius, - y: 0.0, - z: 0.0 - }); - this.period = (2.0 * Math.PI) * Math.sqrt(Math.pow(this.radius, 3.0) / (GRAVITY * LARGE_BODY_MASS)); - this.gravity = GRAVITY; - this.initialVelocity = Math.sqrt((GRAVITY * LARGE_BODY_MASS) / this.radius); - this.velocity = Vec3.multiply(this.initialVelocity, Vec3.normalize({ - x: 0, - y: VELOCITY_OFFSET_Y, - z: VELOCITY_OFFSET_Z - })); - this.dimensions = sizeScale * referenceDiameter; - this.sizeScale = sizeScale; - - this.planet = Entities.addEntity({ + var theSun = this.theSun = Entities.addEntity({ type: "Model", - modelURL: BASE_URL + name + ".fbx", - position: this.position, + modelURL: BASE_URL + "sun.fbx", + position: center, dimensions: { - x: this.dimensions, - y: this.dimensions, - z: this.dimensions + x: SUN_SIZE, + y: SUN_SIZE, + z: SUN_SIZE + }, + angularVelocity: { + x: 0.0, + y: 0.1, + z: 0.0 }, - velocity: this.velocity, angularDamping: DAMPING, damping: DAMPING, ignoreCollisions: false, lifetime: LIFETIME, - collisionsWillMove: true, + collisionsWillMove: false }); - this.computeAcceleration = function() { - var acc = -(this.gravity * LARGE_BODY_MASS) * Math.pow(this.radius, (-2.0)); - return acc; - }; - this.update = function(timeStep) { - var between = Vec3.subtract(this.position, center); - var speed = this.computeAcceleration(this.radius) * timeStep; - var vel = Vec3.multiply(speed, Vec3.normalize(between)); - // Update velocity and position - this.velocity = Vec3.sum(this.velocity, vel); - this.position = Vec3.sum(this.position, Vec3.multiply(timeStep, this.velocity)); - Entities.editEntity(this.planet, { - velocity: this.velocity, - position: this.position - }); + // Reference values for physical constants + var referenceRadius = this.referenceRadius = 15.0; + var referenceDiameter = this.referenceDiameter = 0.6; + var referencePeriod = this.referencePeriod = 3.0; + var LARGE_BODY_MASS = this.LARGE_BODY_MASS = 250.0; + var SMALL_BODY_MASS = this.SMALL_BODY_MASS = LARGE_BODY_MASS * 0.000000333; + var GRAVITY = this.GRAVITY = (Math.pow(referenceRadius, 3.0) / Math.pow((referencePeriod / (2.0 * Math.PI)), 2.0)) / LARGE_BODY_MASS; + var REFERENCE_GRAVITY = this.REFERENCE_GRAVITY = GRAVITY; + + var planets = this.planets = []; + var trailsEnabled = this.trailsEnabled = true; + var MAX_POINTS_PER_LINE = this.MAX_POINTS_PER_LINE = 60; + var LINE_DIM = this.LINE_DIM = 200; + var LINE_WIDTH = this.LINE_WIDTH = 20; + + var VELOCITY_OFFSET_Y = this.VELOCITY_OFFSET_Y = -0.3; + var VELOCITY_OFFSET_Z = this.VELOCITY_OFFSET_Z = 0.9; + + var index = this.index = 0; + + this.computeLocalPoint = function(linePos, worldPoint) { + var localPoint = Vec3.subtract(worldPoint, linePos); + return localPoint; + } + + // Create a new line + this.newLine = function(lineStack, point, period, color) { + if (elapsed < period) { + var line = Entities.addEntity({ + position: point, + type: "Line", + color: color, + dimensions: { + x: LINE_DIM, + y: LINE_DIM, + z: LINE_DIM + }, + lifetime: LIFETIME, + lineWidth: LINE_WIDTH + }); + lineStack.push(line); + } else { + // Begin overwriting first lines after one full revolution (one period) + var firstLine = lineStack.shift(); + print("editing first line"); + Entities.editEntity(firstLine, { + position: point, + linePoints: [{ + x: 0.0, + y: 0.0, + z: 0.0 + }] + }); + lineStack.push(firstLine); - if (trailsEnabled) { - this.updateTrail(); } - }; + var points = []; + points.push(computeLocalPoint(point, point)); + return points; + } - this.resetTrails = function() { - elapsed = 0.0; + this.pause = function() { + for (var i = 0; i < planets.length; ++i) { + Entities.editEntity(planets[i].planet, { + velocity: { + x: 0.0, + y: 0.0, + z: 0.0 + } + }); + planets[i].label = new PlanetLabel(planets[i].name, i); + planets[i].label.show(); + } + paused = true; + } + + this.resume = function() { + for (var i = 0; i < planets.length; ++i) { + planets[i].label.hide(); + } + paused = false; + } + + var Planet = function(name, trailColor, radiusScale, sizeScale) { + + this.name = name; + this.trailColor = trailColor; this.trail = []; this.lineStack = []; - //add the first line to both the line entity stack and the trail - this.trail.push(newLine(this.lineStack, this.position, this.period, this.trailColor)); - }; + this.radius = radiusScale * referenceRadius; + this.position = Vec3.sum(center, { + x: this.radius, + y: 0.0, + z: 0.0 + }); + this.period = (2.0 * Math.PI) * Math.sqrt(Math.pow(this.radius, 3.0) / (GRAVITY * LARGE_BODY_MASS)); - this.updateTrail = function() { - var point = this.position; - var linePos = Entities.getEntityProperties(this.lineStack[this.lineStack.length - 1]).position; + this.initialVelocity = Math.sqrt((GRAVITY * LARGE_BODY_MASS) / this.radius); - this.trail.push(computeLocalPoint(linePos, point)); + this.velocity = Vec3.multiply(this.initialVelocity, Vec3.normalize({ + x: 0, + y: VELOCITY_OFFSET_Y, + z: VELOCITY_OFFSET_Z + })); + this.dimensions = sizeScale * referenceDiameter; + this.sizeScale = sizeScale; - Entities.editEntity(this.lineStack[this.lineStack.length - 1], { - linePoints: this.trail + this.planet = Entities.addEntity({ + type: "Model", + modelURL: BASE_URL + name + ".fbx", + position: this.position, + dimensions: { + x: this.dimensions, + y: this.dimensions, + z: this.dimensions + }, + velocity: this.velocity, + angularDamping: DAMPING, + damping: DAMPING, + ignoreCollisions: false, + lifetime: LIFETIME, + collisionsWillMove: false, }); - if (this.trail.length == MAX_POINTS_PER_LINE) { - this.trail = newLine(this.lineStack, point, this.period, this.trailColor); + this.computeAcceleration = function() { + var acc = -(GRAVITY * LARGE_BODY_MASS) * Math.pow(this.radius, (-2.0)); + return acc; + }; + + this.update = function(deltaTime) { + var between = Vec3.subtract(this.position, center); + var speed = this.computeAcceleration(this.radius) * deltaTime; + var vel = Vec3.multiply(speed, Vec3.normalize(between)); + + // Update velocity and position + this.velocity = Vec3.sum(this.velocity, vel); + this.position = Vec3.sum(this.position, Vec3.multiply(deltaTime, this.velocity)); + Entities.editEntity(this.planet, { + velocity: this.velocity, + position: this.position + }); + + if (trailsEnabled) { + this.updateTrail(); + } + }; + + this.clearTrails = function() { + + for (var j = 0; j < this.lineStack.length; ++j) { + Entities.editEntity(this.lineStack[j], { + visible: false + }); + } + this.lineStack = []; + } - }; + this.resetTrails = function() { + elapsed = 0.0; - this.zoom = function() { - Script.include('file:///Users/bridget/Desktop/tween.js'); - init(this); - // var viewingRange = sizeScale * 2.0; - // var direction = Vec3.subtract(this.position, MyAvatar.position); - // var dist = Vec3.length(direction); - // var timer = 0; - // while (dist > viewingRange && timer < 8000000) { - // timer++; - // if (timer % 10000 == 0) { - // MyAvatar.position = Vec3.sum(MyAvatar.position, Vec3.normalize(direction)); - // direction = Vec3.subtract(this.position, MyAvatar.position); - // dist = Vec3.length(direction); - // } - // } - }; + this.trail = []; + this.lineStack = []; + //add the first line to both the line entity stack and the trail + this.trail.push(newLine(this.lineStack, this.position, this.period, this.trailColor)); + }; - this.adjustPeriod = function(alpha) { - // Update global G constant, period, poke velocity to new value - var ratio = this.last_alpha / alpha; - this.gravity = Math.pow(ratio, 2.0) * GRAVITY; - this.period = ratio * this.period; - this.velocity = Vec3.multiply(ratio, this.velocity); + this.updateTrail = function() { + var point = this.position; + var linePos = Entities.getEntityProperties(this.lineStack[this.lineStack.length - 1]).position; - this.last_alpha = alpha; - }; + this.trail.push(computeLocalPoint(linePos, point)); + Entities.editEntity(this.lineStack[this.lineStack.length - 1], { + linePoints: this.trail + }); - index++; - this.resetTrails(); + if (this.trail.length == MAX_POINTS_PER_LINE) { + this.trail = newLine(this.lineStack, point, this.period, this.trailColor); + } + }; -} + this.zoom = function() { + pause(); -// rideOrbit = function() { - -// Script.include('file:///Users/bridget/Desktop/tween.js'); -// init(); - -// } - - -var MERCURY_LINE_COLOR = { - red: 255, - green: 255, - blue: 255 -}; -var VENUS_LINE_COLOR = { - red: 255, - green: 160, - blue: 110 -}; -var EARTH_LINE_COLOR = { - red: 10, - green: 150, - blue: 160 -}; -var MARS_LINE_COLOR = { - red: 180, - green: 70, - blue: 10 -}; -var JUPITER_LINE_COLOR = { - red: 250, - green: 140, - blue: 0 -}; -var SATURN_LINE_COLOR = { - red: 235, - green: 215, - blue: 0 -}; -var URANUS_LINE_COLOR = { - red: 135, - green: 205, - blue: 240 -}; -var NEPTUNE_LINE_COLOR = { - red: 30, - green: 140, - blue: 255 -}; -var PLUTO_LINE_COLOR = { - red: 255, - green: 255, - blue: 255 -}; - -planets.push(new Planet("mercury", MERCURY_LINE_COLOR, 0.387, 0.383)); -planets.push(new Planet("venus", VENUS_LINE_COLOR, 0.723, 0.949)); -planets.push(new Planet("earth", EARTH_LINE_COLOR, 1.0, 1.0)); -planets.push(new Planet("mars", MARS_LINE_COLOR, 1.52, 0.532)); -planets.push(new Planet("jupiter", JUPITER_LINE_COLOR, 5.20, 11.21)); -planets.push(new Planet("saturn", SATURN_LINE_COLOR, 9.58, 9.45)); -planets.push(new Planet("uranus", URANUS_LINE_COLOR, 19.20, 4.01)); -planets.push(new Planet("neptune", NEPTUNE_LINE_COLOR, 30.05, 3.88)); -planets.push(new Planet("pluto", PLUTO_LINE_COLOR, 39.48, 0.186)); - -var LABEL_X = 8.0; -var LABEL_Y = 3.0; -var LABEL_Z = 1.0; -var LABEL_DIST = 8.0; -var TEXT_HEIGHT = 1.0; - -var PlanetLabel = function(name, index) { - var text = name + " Speed: " + Vec3.length(planets[index].velocity).toFixed(2); - this.labelPos = Vec3.sum(planets[index].position, { - x: 0.0, - y: LABEL_DIST, - z: LABEL_DIST - }); - this.linePos = planets[index].position; - - this.line = Entities.addEntity({ - type: "Line", - position: this.linePos, - dimensions: { - x: 20, - y: 20, - z: 20 - }, - lineWidth: LINE_WIDTH, - color: { - red: 255, - green: 255, - blue: 255 - }, - linePoints: [{ + this.tweening = true; + var tweenTime = 800; + var startingPosition = MyAvatar.position; + var endingPosition = Vec3.sum({ x: 0, y: 0, - z: 0 - }, - computeLocalPoint(this.linePos, this.labelPos) - ], - visible: false - }); + z: this.sizeScale + }, this.position); + var currentProps = { + x: startingPosition.x, + y: startingPosition.y, + z: startingPosition.z, + }; + var endProps = { + x: endingPosition.x, + y: endingPosition.y, + z: endingPosition.z, + }; - this.label = Entities.addEntity({ - type: "Text", - text: text, - lineHeight: TEXT_HEIGHT, - dimensions: { - x: LABEL_X, - y: LABEL_Y, - z: LABEL_Z - }, - position: this.labelPos, - backgroundColor: { - red: 10, - green: 10, - blue: 10 - }, - faceCamera: true, - visible: false - }); + var moveTween = new TWEEN.Tween(currentProps). + to(endProps, tweenTime). + easing(TWEEN.Easing.Cubic.InOut). + onUpdate(function() { + print(JSON.stringify(currentProps)); + MyAvatar.position = { + x: currentProps.x, + y: currentProps.y, + z: currentProps.z + }; + }).start(); - this.show = function() { - this.showing = true; - Entities.editEntity(this.line, { - visible: true + moveTween.onComplete(function() { + this.tweening = false; + }); + + } + + index++; + this.resetTrails(); + + } + + + var MERCURY_LINE_COLOR = { + red: 255, + green: 255, + blue: 255 + }; + var VENUS_LINE_COLOR = { + red: 255, + green: 160, + blue: 110 + }; + var EARTH_LINE_COLOR = { + red: 10, + green: 150, + blue: 160 + }; + var MARS_LINE_COLOR = { + red: 180, + green: 70, + blue: 10 + }; + var JUPITER_LINE_COLOR = { + red: 250, + green: 140, + blue: 0 + }; + var SATURN_LINE_COLOR = { + red: 235, + green: 215, + blue: 0 + }; + var URANUS_LINE_COLOR = { + red: 135, + green: 205, + blue: 240 + }; + var NEPTUNE_LINE_COLOR = { + red: 30, + green: 140, + blue: 255 + }; + var PLUTO_LINE_COLOR = { + red: 255, + green: 255, + blue: 255 + }; + + planets.push(new Planet("mercury", MERCURY_LINE_COLOR, 0.387, 0.383)); + planets.push(new Planet("venus", VENUS_LINE_COLOR, 0.723, 0.949)); + planets.push(new Planet("earth", EARTH_LINE_COLOR, 1.0, 1.0)); + planets.push(new Planet("mars", MARS_LINE_COLOR, 1.52, 0.532)); + planets.push(new Planet("jupiter", JUPITER_LINE_COLOR, 5.20, 11.21)); + planets.push(new Planet("saturn", SATURN_LINE_COLOR, 9.58, 9.45)); + planets.push(new Planet("uranus", URANUS_LINE_COLOR, 19.20, 4.01)); + planets.push(new Planet("neptune", NEPTUNE_LINE_COLOR, 30.05, 3.88)); + planets.push(new Planet("pluto", PLUTO_LINE_COLOR, 39.48, 0.186)); + + var LABEL_X = 8.0; + var LABEL_Y = 3.0; + var LABEL_Z = 1.0; + var LABEL_DIST = 8.0; + var TEXT_HEIGHT = 1.0; + + var PlanetLabel = function(name, index) { + var text = name + " Speed: " + Vec3.length(planets[index].velocity).toFixed(2); + this.labelPos = Vec3.sum(planets[index].position, { + x: 0.0, + y: LABEL_DIST, + z: LABEL_DIST }); - Entities.editEntity(this.label, { - visible: true - }); - } + this.linePos = planets[index].position; - this.hide = function() { - this.showing = false; - Entities.editEntity(this.line, { - visible: false - }); - Entities.editEntity(this.label, { - visible: false - }); - } -} - - -var time = 0.0; -var elapsed; -var TIME_STEP = 60.0; -var dt = 1.0 / TIME_STEP; - -var planetLines = []; -var trails = []; -var paused = false; - -function update(deltaTime) { - if (paused) { - return; - } - //deltaTime = dt; - time++; - if (time % TIME_STEP == 0) { - elapsed++; - } - - for (var i = 0; i < planets.length; ++i) { - planets[i].update(deltaTime); - } -} - -function pause() { - for (var i = 0; i < planets.length; ++i) { - Entities.editEntity(planets[i].planet, { - velocity: { - x: 0.0, - y: 0.0, - z: 0.0 - } - }); - planets[i].label = new PlanetLabel(planets[i].name, i); - planets[i].label.show(); - } - paused = true; -} - -function resume() { - for (var i = 0; i < planets.length; ++i) { - planets[i].label.hide(); - } - paused = false; -} - -function computeLocalPoint(linePos, worldPoint) { - var localPoint = Vec3.subtract(worldPoint, linePos); - return localPoint; -} - -// Create a new line -function newLine(lineStack, point, period, color) { - if (elapsed < period) { - var line = Entities.addEntity({ - position: point, + this.line = Entities.addEntity({ type: "Line", - color: color, + position: this.linePos, dimensions: { - x: LINE_DIM, - y: LINE_DIM, - z: LINE_DIM + x: 20, + y: 20, + z: 20 }, - lifetime: LIFETIME, - lineWidth: LINE_WIDTH - }); - lineStack.push(line); - } else { - // Begin overwriting first lines after one full revolution (one period) - var firstLine = lineStack.shift(); - Entities.editEntity(firstLine, { - position: point, - linePoints: [{ - x: 0.0, - y: 0.0, - z: 0.0 - }] - }); - lineStack.push(firstLine); - - } - var points = []; - points.push(computeLocalPoint(point, point)); - return points; -} - - - -var spacing = 8; -var textWidth = 70; -var buttonWidth = 30; -var buttonHeight = 30; - -var ICONS_URL = 'https://s3.amazonaws.com/hifi-public/marketplace/hificontent/Scripts/planets/images/'; - -var UIPanel = function(x, y, orientation) { - this.visible = false; - this.buttons = []; - this.x = x; - this.y = y; - this.nextX = x + spacing; - this.nextY = y + spacing; - this.width = spacing * 2.0; - this.height = spacing * 2.0; - - this.background = Overlays.addOverlay("text", { - backgroundColor: { - red: 240, - green: 240, - blue: 255 - }, - x: this.x, - y: this.y, - width: this.width, - height: this.height, - alpha: 1.0, - backgroundAlpha: 0.7, - visible: false - }); - - this.addIcon = function(iconID) { - var icon = Overlays.addOverlay("image", { + lineWidth: LINE_WIDTH, color: { red: 255, green: 255, blue: 255 }, - imageURL: ICONS_URL + iconID + '.svg', - x: this.nextX, - y: this.nextY, - width: buttonWidth, - height: buttonHeight, - alpha: 1.0, + linePoints: [{ + x: 0, + y: 0, + z: 0 + }, + computeLocalPoint(this.linePos, this.labelPos) + ], visible: false }); - - if (orientation === 'horizontal') { - this.nextX += buttonWidth + spacing; - this.width += buttonWidth; - - } else if (orientation === 'vertical') { - this.nextY += buttonHeight + spacing; - this.height += buttonHeight; - } - - Overlays.editOverlay(this.background, { - width: buttonWidth + this.width, - height: buttonHeight + this.height - }); - - this.buttons.push(icon); - return icon; - }; - - this.addText = function(text) { - var icon = Overlays.addOverlay("text", { - color: { - red: 240, - green: 240, - blue: 255 - }, + this.label = Entities.addEntity({ + type: "Text", text: text, - x: this.nextX, - y: this.nextY, - width: textWidth, - height: buttonHeight, + lineHeight: TEXT_HEIGHT, + dimensions: { + x: LABEL_X, + y: LABEL_Y, + z: LABEL_Z + }, + position: this.labelPos, + backgroundColor: { + red: 10, + green: 10, + blue: 10 + }, + faceCamera: true, visible: false }); - if (orientation === 'horizontal') { - this.nextX += textWidth + spacing; - this.width += textWidth; - - } else if (orientation === 'vertical') { - this.nextY += buttonHeight + spacing; - this.height += buttonHeight; - } - - Overlays.editOverlay(this.background, { - width: textWidth + this.width, - height: buttonHeight + this.height - }); - - this.buttons.push(icon); - return icon; - }; - - - this.show = function() { - Overlays.editOverlay(this.background, { - visible: true - }); - for (var i in this.buttons) { - Overlays.editOverlay(this.buttons[i], { + this.show = function() { + this.showing = true; + Entities.editEntity(this.line, { + visible: true + }); + Entities.editEntity(this.label, { visible: true }); } - this.visible = true; - }; - this.hide = function() { - Overlays.editOverlay(this.background, { - visible: false - }); - for (var i in this.buttons) { - Overlays.editOverlay(this.buttons[i], { + this.hide = function() { + this.showing = false; + Entities.editEntity(this.line, { + visible: false + }); + Entities.editEntity(this.label, { visible: false }); } - this.visible = false; - }; + } - this.remove = function() { - Overlays.deleteOverlay(this.background); - for (var i in this.buttons) { - Overlays.deleteOverlay(this.buttons[i]); - } - }; + var planetLines = []; + var trails = this.trails = []; + var paused = this.paused = false; -} - -var panelX = 1250; -var panelY = 500; -var panelWidth = 50; -var panelHeight = 210; - -var mainPanel = new UIPanel(panelX, panelY, 'vertical'); - -var systemViewButton = mainPanel.addIcon('solarsystems'); -var systemViewPanel = new UIPanel(panelX - 140, panelY, 'horizontal'); -var reverseButton = systemViewPanel.addIcon('reverse'); -var pauseButton = systemViewPanel.addIcon('playpause'); -var forwardButton = systemViewPanel.addIcon('forward'); - -var zoomButton = mainPanel.addIcon('magnifier'); -var zoomPanel = new UIPanel(panelX - 900, panelY + (buttonHeight + spacing) * 4.0, 'horizontal'); -for (var i = 0; i < planets.length; ++i) { - zoomPanel.addText(planets[i].name); -} - -var satelliteButton = mainPanel.addIcon('satellite'); -var settingsButton = mainPanel.addIcon('settings'); -var stopButton = mainPanel.addIcon('close'); - -mainPanel.show(); - -Script.include('../utilities/tools/cookies.js'); - - -var satelliteView; -var satelliteGame; - var settings = new Panel(panelX - 610, panelY - 80); - - var g_multiplier = 1.0; - settings.newSlider("Gravitational Force ", 0.1, 5.0, - function(value) { - g_multiplier = value; - GRAVITY = REFERENCE_GRAVITY * g_multiplier; - }, - - function() { - return g_multiplier; - }, - function(value) { - return value.toFixed(1) + "x"; - }); - - - var subPanel = settings.newSubPanel("Orbital Periods"); - - for (var i = 0; i < planets.length; ++i) { - planets[i].period_multiplier = 1.0; - planets[i].last_alpha = planets[i].period_multiplier; - - subPanel.newSlider(planets[i].name, 0.1, 3.0, - function(value) { - planets[i].period_multiplier = value; - planets[i].adjustPeriod(value); - }, - function() { - return planets[i].period_multiplier; - }, - function(value) { - return (value).toFixed(2) + "x"; - }); - } - settings.newCheckbox("Leave Trails: ", - function(value) { - trailsEnabled = value; - if (trailsEnabled) { - for (var i = 0; i < planets.length; ++i) { - planets[i].resetTrails(); - } - //if trails are off and we've already created trails, remove existing trails - } else { - for (var i = 0; i < planets.length; ++i) { - for (var j = 0; j < planets[i].lineStack.length; ++j) { - Entities.editEntity(planets[i].lineStack[j], { - visible: false - }); - } - planets[i].lineStack = []; - } - } - }, - function() { - return trailsEnabled; - }, - function(value) { - return value; - }); - settings.hide(); - - -function mousePressEvent(event) { - - var clicked = Overlays.getOverlayAtPoint({ - x: event.x, - y: event.y - }); - - if (clicked == systemViewButton) { - MyAvatar.position = startingPosition; - Camera.setOrientation(cameraStart); + this.update = function(deltaTime) { if (paused) { - resume(); + return; } - - if (!systemViewPanel.visible) { - systemViewPanel.show(); - } else { - systemViewPanel.hide(); - } - } - var zoomed = false; - if (clicked == zoomButton && !satelliteView) { - if (!zoomPanel.visible) { - zoomPanel.show(); - pause(); - } else { - zoomPanel.hide(); - if (zoomed) { - - MyAvatar.position = startingPosition; - Camera.setOrientation(cameraStart); - resume(); - } - - } - } - - for (var i = 0; i < planets.length; ++i) { - if (zoomPanel.visible && clicked == zoomPanel.buttons[i]) { - planets[i].zoom(); - zoomed = true; - break; - } - } - - if (systemViewPanel.visible && clicked == pauseButton) { - if (!paused) { - pause(); - } else { - resume(); - } - } - - - - if (clicked == satelliteButton) { - if (satelliteView) { - satelliteGame.endGame(); - MyAvatar.position = startingPosition; - satelliteView = false; - resume(); - } else { - pause(); - var confirmed = Window.confirm("Start satellite game?"); - if (!confirmed) { - resume(); + for (var i = 0; i < planets.length; ++i) { + if (planets[i].tweening) { + TWEEN.update(); continue; } - satelliteView = true; - MyAvatar.position = { - x: 200, - y: 200, - z: 200 - }; - Camera.setPosition({ - x: 200, - y: 200, - z: 200 - }); - satelliteGame = new SatelliteGame(); + planets[i].update(deltaTime); } } - if (clicked == settingsButton) { - if (!settings.visible) { - settings.show(); - } else { - settings.hide(); - } - } + Script.include('planets-ui.js'); - if (clicked == stopButton) { - Script.stop(); - } + // Clean up models, UI panels, lines, and button overlays + this.scriptEnding = function() { + Entities.deleteEntity(theSun); + for (var i = 0; i < planets.length; ++i) { + Entities.deleteEntity(planets[i].planet); + } + var e = Entities.findEntities(MyAvatar.position, 10000); + for (i = 0; i < e.length; i++) { + var props = Entities.getEntityProperties(e[i]); + if (props.type === "Line" || props.type === "Text") { + Entities.deleteEntity(e[i]); + } + } + }; + Script.update.connect(update); + } -// Clean up models, UI panels, lines, and button overlays -function scriptEnding() { - mainPanel.remove(); - systemViewPanel.remove(); - zoomPanel.remove(); - - settings.destroy(); - - Entities.deleteEntity(theSun); - for (var i = 0; i < planets.length; ++i) { - Entities.deleteEntity(planets[i].planet); - } +Script.scriptEnding.connect(function() { + scriptEnding(); + teardown(); +}); + +CreateSimulation(); - var e = Entities.findEntities(MyAvatar.position, 16000); - for (i = 0; i < e.length; i++) { - var props = Entities.getEntityProperties(e[i]); - if (props.type === "Line" || props.type === "Text") { - Entities.deleteEntity(e[i]); - } - } -}; - -Controller.mousePressEvent.connect(mousePressEvent); - - Controller.mouseMoveEvent.connect(function panelMouseMoveEvent(event) { - return settings.mouseMoveEvent(event); - }); - Controller.mousePressEvent.connect(function panelMousePressEvent(event) { - return settings.mousePressEvent(event); - }); - Controller.mouseDoublePressEvent.connect(function panelMouseDoublePressEvent(event) { - return settings.mouseDoublePressEvent(event); - }); - Controller.mouseReleaseEvent.connect(function(event) { - return settings.mouseReleaseEvent(event); - }); - Controller.keyPressEvent.connect(function(event) { - return settings.keyPressEvent(event); - }); - -Script.scriptEnding.connect(scriptEnding); -Script.update.connect(update); \ No newline at end of file diff --git a/examples/libraries/uiwidgets.js b/examples/libraries/uiwidgets.js index fa898ea7bc..70eda8e5f2 100644 --- a/examples/libraries/uiwidgets.js +++ b/examples/libraries/uiwidgets.js @@ -501,7 +501,7 @@ Box.prototype.destroy = function () { } } Box.prototype.hasOverlay = function (overlayId) { - return this.overlay && this.overlay.getId() === overlayId; + return /*this.overlay &&*/ this.overlay.getId() === overlayId; } Box.prototype.getOverlay = function () { return this.overlay; @@ -615,7 +615,7 @@ Slider.prototype.toString = function () { } Slider.prototype.applyLayout = function () { if (!this.slider) { - ui.complain("Slider.applyLayout on " + this + " failed"); + // ui.complain("Slider.applyLayout on " + this + " failed"); return; } var val = (this.value - this.minValue) / (this.maxValue - this.minValue); @@ -654,6 +654,7 @@ var Checkbox = UI.Checkbox = function (properties) { this.position.x + (this.width - this.checkMark.width) * 0.5, this.position.y + (this.height - this.checkMark.height) * 0.5 ); + this.checkMark.parent = this; this.onValueChanged = properties.onValueChanged || function () {}; @@ -919,9 +920,16 @@ var getFocusedWidget = function (event) { var dispatchEvent = function (action, event, widget) { function dispatchActions (actions) { + var dispatchedActions = false; + ui.logEvent("dispatching to [" + actions.join(", ") + "]"); actions.forEach(function(action) { action(event, widget); + ui.logEvent("dispatched to " + action); + dispatchedActions = true; }); + if (!dispatchedActions) { + // ui.logEvent("No actions to dispatch"); + } } if (widget.actions[action]) { @@ -963,7 +971,7 @@ UI.handleMouseMove = function (event, canStartDrag) { } UI.handleMousePress = function (event) { - print("Mouse clicked"); + // print("Mouse clicked"); UI.handleMouseMove(event); ui.clickedWidget = ui.focusedWidget; if (ui.clickedWidget) { @@ -971,8 +979,18 @@ UI.handleMousePress = function (event) { } } +UI.handleMouseDoublePress = function (event) { + // print("DOUBLE CLICK!"); + var focused = getFocusedWidget(event); + UI.handleMouseMove(event); + if (focused) { + // print("dispatched onDoubleClick"); + dispatchEvent('onDoubleClick', event, focused); + } +} + UI.handleMouseRelease = function (event) { - print("Mouse released"); + // print("Mouse released"); if (ui.draggedWidget) { dispatchEvent('onDragEnd', event, ui.draggedWidget); From 2dbba322fd116451118d8f4f310b3f145d55134b Mon Sep 17 00:00:00 2001 From: bwent Date: Thu, 13 Aug 2015 18:13:08 -0700 Subject: [PATCH 20/49] bug fixes, ride through system --- examples/example/games/satellite.js | 12 ++--- examples/example/planets-ui.js | 44 ++++++++++----- examples/example/solarsystem.js | 84 +++++++++++++++++++---------- 3 files changed, 93 insertions(+), 47 deletions(-) diff --git a/examples/example/games/satellite.js b/examples/example/games/satellite.js index 8e37472ea4..7f1dc8dd46 100644 --- a/examples/example/games/satellite.js +++ b/examples/example/games/satellite.js @@ -125,14 +125,14 @@ SatelliteCreator = function() { this.isActive = true; MyAvatar.position = { - x: 200, - y: 200, - z: 200 + x: 1000, + y: 1000, + z: 1000 }; Camera.setPosition({ - x: 200, - y: 200, - z: 200 + x: 1000, + y: 1000, + z: 1000 }); // Create earth model diff --git a/examples/example/planets-ui.js b/examples/example/planets-ui.js index 56cf7b18e1..6446e82b0e 100644 --- a/examples/example/planets-ui.js +++ b/examples/example/planets-ui.js @@ -125,7 +125,7 @@ var tooltipWidget = new UI.Label({ function addTooltip(widget, text) { widget.addAction('onMouseOver', function(event, widget) { tooltipWidget.setVisible(true); - tooltipWidget.setPosition(widget.position.x + widget.getWidth() + 20, widget.position.y); + tooltipWidget.setPosition(widget.position.x + widget.getWidth() + 20, widget.position.y + 10); tooltipWidget.setText(text); UI.updateLayout(); }); @@ -170,15 +170,19 @@ pauseButton.addAction('onClick', function() { } }); -// rideButton.addAction('onClick', function() { -// Script.include('orbit-ride.js'); -// new OrbitRider(); -// }); +rideButton.addAction('onClick', function() { + if (!paused) { + pause(); + } + var confirmed = Window.confirm('Ride through the solar system?'); + if (confirmed) { + Script.include('https://hifi-staff.s3.amazonaws.com/bridget/tween.js'); + init(); + } +}); restartButton.addAction('onClick', function() { - Script.stop(); - var thisScript = Script.resolvePath("solarsystem.js"); - Script.load(thisScript); + restart(); }); var zoomPanel = addPanel({ @@ -190,18 +194,27 @@ for (var i = 0; i < planets.length; ++i) { var label = zoomPanel.add(new UI.Label({ text: planets[i].name, width: 80, - height: 20, - backgroundAlpha: 1.0 + height: 20 })); zoomButtons.push(label); UI.updateLayout(); } UI.updateLayout(); + + zoomButtons.forEach(function(button, i) { var planet = planets[i]; button.addAction('onClick', function() { - planet.zoom(); + if (!planets[i].isZoomed) { + planet.zoom(); + planet.isZoomed = true; + } else { + MyAvatar.position = startingPosition; + Camera.setPosition(cameraStart); + planet.isZoomed = false; + } + }); }); @@ -305,7 +318,7 @@ settingsPanel.showTrailsButton = addCheckbox(settingsPanel, "show trails", 120, } }); var g_multiplier = 1.0; -settingsPanel.gravitySlider = addSlider(settingsPanel, "gravity scale ", 200, g_multiplier, 0.0, 5.0, function (value) { +settingsPanel.gravitySlider = addSlider(settingsPanel, "gravity scale ", 200, g_multiplier, 0.0, 5.0, function(value) { g_multiplier = value; GRAVITY = REFERENCE_GRAVITY * g_multiplier; }); @@ -313,7 +326,7 @@ settingsPanel.gravitySlider = addSlider(settingsPanel, "gravity scale ", 200, g_ var period_multiplier = 1.0; var last_alpha = period_multiplier; -settingsPanel.periodSlider = addSlider(settingsPanel, "orbital period scale ", 200, period_multiplier, 0.0, 3.0, function (value) { +settingsPanel.periodSlider = addSlider(settingsPanel, "orbital period scale ", 200, period_multiplier, 0.0, 3.0, function(value) { period_multiplier = value; changePeriod(period_multiplier); }); @@ -428,12 +441,13 @@ panels.map(function(panel) { // Cleanup script resources function teardown() { + UI.teardown(); if (satelliteGame) { satelliteGame.quitGame(); } - UI.teardown(); }; + UI.debug.setVisible(false); var inputHandler = { @@ -456,3 +470,5 @@ Controller.mousePressEvent.connect(inputHandler.onMousePress); Controller.mouseMoveEvent.connect(inputHandler.onMouseMove); Controller.mouseReleaseEvent.connect(inputHandler.onMouseRelease); Controller.mouseDoublePressEvent.connect(inputHandler.onMouseDoublePress); + +Script.scriptEnding.connect(teardown); \ No newline at end of file diff --git a/examples/example/solarsystem.js b/examples/example/solarsystem.js index 3e4a39ed71..bcd7284e14 100644 --- a/examples/example/solarsystem.js +++ b/examples/example/solarsystem.js @@ -37,7 +37,7 @@ CreateSimulation = function() { // Place the sun - var MAX_RANGE = this.MAX_RANGE = 80.0; + var MAX_RANGE = this.MAX_RANGE = 100.0; var center = this.center = Vec3.sum(startingPosition, Vec3.multiply(MAX_RANGE, Vec3.normalize(Quat.getFront(Camera.getOrientation())))); var SUN_SIZE = this.SUN_SIZE = 7.0; @@ -75,7 +75,7 @@ CreateSimulation = function() { var planets = this.planets = []; var trailsEnabled = this.trailsEnabled = true; - var MAX_POINTS_PER_LINE = this.MAX_POINTS_PER_LINE = 60; + var MAX_POINTS_PER_LINE = this.MAX_POINTS_PER_LINE = 40; var LINE_DIM = this.LINE_DIM = 200; var LINE_WIDTH = this.LINE_WIDTH = 20; @@ -108,7 +108,6 @@ CreateSimulation = function() { } else { // Begin overwriting first lines after one full revolution (one period) var firstLine = lineStack.shift(); - print("editing first line"); Entities.editEntity(firstLine, { position: point, linePoints: [{ @@ -148,7 +147,6 @@ CreateSimulation = function() { } var Planet = function(name, trailColor, radiusScale, sizeScale) { - this.name = name; this.trailColor = trailColor; @@ -161,6 +159,7 @@ CreateSimulation = function() { y: 0.0, z: 0.0 }); + this.startPosition = this.position; this.period = (2.0 * Math.PI) * Math.sqrt(Math.pow(this.radius, 3.0) / (GRAVITY * LARGE_BODY_MASS)); this.initialVelocity = Math.sqrt((GRAVITY * LARGE_BODY_MASS) / this.radius); @@ -220,7 +219,7 @@ CreateSimulation = function() { visible: false }); } - this.lineStack = []; + } this.resetTrails = function() { @@ -249,10 +248,11 @@ CreateSimulation = function() { }; this.zoom = function() { - pause(); - + if (!paused) { + pause(); + } this.tweening = true; - var tweenTime = 800; + var tweenTime = 1000; var startingPosition = MyAvatar.position; var endingPosition = Vec3.sum({ x: 0, @@ -274,12 +274,12 @@ CreateSimulation = function() { to(endProps, tweenTime). easing(TWEEN.Easing.Cubic.InOut). onUpdate(function() { - print(JSON.stringify(currentProps)); MyAvatar.position = { x: currentProps.x, y: currentProps.y, z: currentProps.z }; + Camera.lookAt(endingPosition); }).start(); moveTween.onComplete(function() { @@ -433,23 +433,31 @@ CreateSimulation = function() { var trails = this.trails = []; var paused = this.paused = false; + var time = 0; + var elapsed; + var TIME_STEP = 80; + this.update = function(deltaTime) { - if (paused) { - return; - } for (var i = 0; i < planets.length; ++i) { - if (planets[i].tweening) { - TWEEN.update(); - continue; + TWEEN.update(); + if (paused) { + return; } planets[i].update(deltaTime); } + + time++; + if (time % TIME_STEP == 0) { + elapsed++; + } + } Script.include('planets-ui.js'); // Clean up models, UI panels, lines, and button overlays this.scriptEnding = function() { + Entities.deleteEntity(theSun); for (var i = 0; i < planets.length; ++i) { Entities.deleteEntity(planets[i].planet); @@ -461,20 +469,42 @@ CreateSimulation = function() { Entities.deleteEntity(e[i]); } } - }; - Script.update.connect(update); - + }; + + this.restart = function() { + if (paused) { + resume(); + } + for (var i = 0; i < planets.length; ++i) { + Entities.deleteEntity(planets[i].planet); + planets[i].clearTrails(); + } + planets.length = 0; + planets.push(new Planet("mercury", MERCURY_LINE_COLOR, 0.387, 0.383)); + planets.push(new Planet("venus", VENUS_LINE_COLOR, 0.723, 0.949)); + planets.push(new Planet("earth", EARTH_LINE_COLOR, 1.0, 1.0)); + planets.push(new Planet("mars", MARS_LINE_COLOR, 1.52, 0.532)); + planets.push(new Planet("jupiter", JUPITER_LINE_COLOR, 5.20, 11.21)); + planets.push(new Planet("saturn", SATURN_LINE_COLOR, 9.58, 9.45)); + planets.push(new Planet("uranus", URANUS_LINE_COLOR, 19.20, 4.01)); + planets.push(new Planet("neptune", NEPTUNE_LINE_COLOR, 30.05, 3.88)); + planets.push(new Planet("pluto", PLUTO_LINE_COLOR, 39.48, 0.186)); + + + for (var i = 0; i < planets.length; ++i) { + planets[i].resetTrails(); + } + + elapsed = 0.0; + MyAvatar.position = startingPosition; + Camera.setPosition(cameraStart); + }; + + } - -Script.scriptEnding.connect(function() { - scriptEnding(); - teardown(); -}); - CreateSimulation(); - - - +Script.update.connect(update); +Script.scriptEnding.connect(scriptEnding); \ No newline at end of file From 58713c0d57c5c4b821ab83f290798d8aa76a208e Mon Sep 17 00:00:00 2001 From: bwent Date: Fri, 14 Aug 2015 09:55:05 -0700 Subject: [PATCH 21/49] more minor bug fixes --- examples/example/games/satellite.js | 12 +++++- examples/example/planets-ui.js | 58 ++++++++++++++++++++++++++--- examples/example/solarsystem.js | 55 +++++++++++---------------- 3 files changed, 85 insertions(+), 40 deletions(-) diff --git a/examples/example/games/satellite.js b/examples/example/games/satellite.js index 7f1dc8dd46..c0914ebe3c 100644 --- a/examples/example/games/satellite.js +++ b/examples/example/games/satellite.js @@ -40,6 +40,7 @@ SatelliteCreator = function() { var LIGHT_INTENSITY = 1.5; var center, distance; + var earth; Earth = function(position, size) { @@ -107,6 +108,7 @@ SatelliteCreator = function() { }); this.cleanup = function() { + print('cleaning up earth models'); Entities.deleteEntity(this.clouds); Entities.deleteEntity(this.earth); Entities.deleteEntity(this.zone); @@ -138,7 +140,7 @@ SatelliteCreator = function() { // Create earth model center = Vec3.sum(Camera.getPosition(), Vec3.multiply(MAX_RANGE, Quat.getFront(Camera.getOrientation()))); distance = Vec3.length(Vec3.subtract(center, Camera.getPosition())); - var earth = new Earth(center, EARTH_SIZE); + earth = new Earth(center, EARTH_SIZE); return true; }; @@ -289,12 +291,18 @@ SatelliteCreator = function() { } this.quitGame = function() { + if(!this.isActive) { + return; + } + print("ending satellite game"); + this.isActive = false; + for (var i = 0; i < satellites.length; i++) { Entities.deleteEntity(satellites[i].satellite); satellites[i].arrow.cleanup(); } - this.isActive = false; earth.cleanup(); + } diff --git a/examples/example/planets-ui.js b/examples/example/planets-ui.js index 6446e82b0e..f749f97337 100644 --- a/examples/example/planets-ui.js +++ b/examples/example/planets-ui.js @@ -22,7 +22,7 @@ var ICON_HEIGHT = 40.0; var ICON_COLOR = UI.rgba(45, 45, 45, 0.7); var FOCUSED_COLOR = UI.rgba(250, 250, 250, 1.0); -var PANEL_BACKGROUND_COLOR = UI.rgba(100, 100, 100, 0.7); +var PANEL_BACKGROUND_COLOR = UI.rgba(120, 120, 120, 0.8); var PANEL_PADDING = 7.0; var PANEL_BORDER = 12.0; @@ -162,7 +162,17 @@ var restartButton = addImage(systemViewPanel, 'reverse'); var pauseButton = addImage(systemViewPanel, 'playpause'); var rideButton = addImage(systemViewPanel, 'forward'); +var tweening, tweeningPaused; +Script.include('https://hifi-staff.s3.amazonaws.com/bridget/tween.js'); + pauseButton.addAction('onClick', function() { + if (tweening) { + if(!tweeningPaused) { + tweeningPaused = true; + } else { + tweeningPaused = false; + } + } if (!paused) { pause(); } else { @@ -170,19 +180,47 @@ pauseButton.addAction('onClick', function() { } }); +// Allow to toggle pause with spacebar +function keyPressEvent(event) { + if(event.text == "SPACE") { + if (tweening) { + if(!tweeningPaused) { + tweeningPaused = true; + } else { + tweeningPaused = false; + } + } + if (!paused) { + pause(); + } else { + resume(); + } + } +} + rideButton.addAction('onClick', function() { if (!paused) { pause(); + } + if (tweening) { + tweening = false; + tweeningPaused = true; + restart(); + return; } var confirmed = Window.confirm('Ride through the solar system?'); if (confirmed) { - Script.include('https://hifi-staff.s3.amazonaws.com/bridget/tween.js'); init(); + tweening = true; + tweeningPaused = false; } + }); restartButton.addAction('onClick', function() { restart(); + tweening = false; + }); var zoomPanel = addPanel({ @@ -202,17 +240,19 @@ for (var i = 0; i < planets.length; ++i) { UI.updateLayout(); - +var zoomView = false; zoomButtons.forEach(function(button, i) { var planet = planets[i]; button.addAction('onClick', function() { if (!planets[i].isZoomed) { planet.zoom(); planet.isZoomed = true; + zoomView = true; } else { MyAvatar.position = startingPosition; Camera.setPosition(cameraStart); planet.isZoomed = false; + zoomView = false; } }); @@ -305,7 +345,8 @@ function addSlider(parent, label, labelWidth, defaultValue, min, max, valueChang return slider; } -settingsPanel.showTrailsButton = addCheckbox(settingsPanel, "show trails", 120, trailsEnabled, function(trailsEnabled) { +settingsPanel.showTrailsButton = addCheckbox(settingsPanel, "show trails", 120, trailsEnabled, function(value) { + trailsEnabled = value; if (trailsEnabled) { for (var i = 0; i < planets.length; ++i) { planets[i].resetTrails(); @@ -347,7 +388,9 @@ satelliteButton.addAction('onClick', function() { if (satelliteGame && satelliteGame.isActive) { MyAvatar.position = startingPosition; satelliteGame.quitGame(); - resume(); + if(paused) { + resume(); + } } else { pause(); satelliteGame = new SatelliteCreator(); @@ -402,6 +445,9 @@ systemViewPanel.addAction('onMouseOver', function() { zoomButton.addAction('onClick', function() { + if (zoomView) { + restart(); + } hideSubpanelsExcept(zoomPanel); UI.updateLayout(); }); @@ -471,4 +517,6 @@ Controller.mouseMoveEvent.connect(inputHandler.onMouseMove); Controller.mouseReleaseEvent.connect(inputHandler.onMouseRelease); Controller.mouseDoublePressEvent.connect(inputHandler.onMouseDoublePress); +Controller.keyPressEvent.connect(keyPressEvent); + Script.scriptEnding.connect(teardown); \ No newline at end of file diff --git a/examples/example/solarsystem.js b/examples/example/solarsystem.js index bcd7284e14..7c58cfb299 100644 --- a/examples/example/solarsystem.js +++ b/examples/example/solarsystem.js @@ -22,6 +22,9 @@ CreateSimulation = function() { Script.include("https://hifi-public.s3.amazonaws.com/eric/scripts/tween.js"); Script.include('games/satellite.js'); + + trailsEnabled = true; + var DAMPING = this.DAMPING = 0.0; var LIFETIME = this.LIFETIME = 6000; var BASE_URL = this.BASE_URL = "https://s3.amazonaws.com/hifi-public/marketplace/hificontent/Scripts/planets/planets/"; @@ -74,7 +77,6 @@ CreateSimulation = function() { var REFERENCE_GRAVITY = this.REFERENCE_GRAVITY = GRAVITY; var planets = this.planets = []; - var trailsEnabled = this.trailsEnabled = true; var MAX_POINTS_PER_LINE = this.MAX_POINTS_PER_LINE = 40; var LINE_DIM = this.LINE_DIM = 200; var LINE_WIDTH = this.LINE_WIDTH = 20; @@ -213,6 +215,7 @@ CreateSimulation = function() { }; this.clearTrails = function() { + elapsed = 0.0; for (var j = 0; j < this.lineStack.length; ++j) { Entities.editEntity(this.lineStack[j], { @@ -279,10 +282,10 @@ CreateSimulation = function() { y: currentProps.y, z: currentProps.z }; - Camera.lookAt(endingPosition); }).start(); moveTween.onComplete(function() { + Camera.lookAt(endingPosition); this.tweening = false; }); @@ -340,16 +343,19 @@ CreateSimulation = function() { blue: 255 }; - planets.push(new Planet("mercury", MERCURY_LINE_COLOR, 0.387, 0.383)); - planets.push(new Planet("venus", VENUS_LINE_COLOR, 0.723, 0.949)); - planets.push(new Planet("earth", EARTH_LINE_COLOR, 1.0, 1.0)); - planets.push(new Planet("mars", MARS_LINE_COLOR, 1.52, 0.532)); - planets.push(new Planet("jupiter", JUPITER_LINE_COLOR, 5.20, 11.21)); - planets.push(new Planet("saturn", SATURN_LINE_COLOR, 9.58, 9.45)); - planets.push(new Planet("uranus", URANUS_LINE_COLOR, 19.20, 4.01)); - planets.push(new Planet("neptune", NEPTUNE_LINE_COLOR, 30.05, 3.88)); - planets.push(new Planet("pluto", PLUTO_LINE_COLOR, 39.48, 0.186)); - + this.initPlanets = function() { + planets.push(new Planet("mercury", MERCURY_LINE_COLOR, 0.387, 0.383)); + planets.push(new Planet("venus", VENUS_LINE_COLOR, 0.723, 0.949)); + planets.push(new Planet("earth", EARTH_LINE_COLOR, 1.0, 1.0)); + planets.push(new Planet("mars", MARS_LINE_COLOR, 1.52, 0.532)); + planets.push(new Planet("jupiter", JUPITER_LINE_COLOR, 5.20, 11.21)); + planets.push(new Planet("saturn", SATURN_LINE_COLOR, 9.58, 9.45)); + planets.push(new Planet("uranus", URANUS_LINE_COLOR, 19.20, 4.01)); + planets.push(new Planet("neptune", NEPTUNE_LINE_COLOR, 30.05, 3.88)); + planets.push(new Planet("pluto", PLUTO_LINE_COLOR, 39.48, 0.186)); + } + initPlanets(); + var LABEL_X = 8.0; var LABEL_Y = 3.0; var LABEL_Z = 1.0; @@ -439,7 +445,6 @@ CreateSimulation = function() { this.update = function(deltaTime) { for (var i = 0; i < planets.length; ++i) { - TWEEN.update(); if (paused) { return; } @@ -479,32 +484,16 @@ CreateSimulation = function() { Entities.deleteEntity(planets[i].planet); planets[i].clearTrails(); } - planets.length = 0; - planets.push(new Planet("mercury", MERCURY_LINE_COLOR, 0.387, 0.383)); - planets.push(new Planet("venus", VENUS_LINE_COLOR, 0.723, 0.949)); - planets.push(new Planet("earth", EARTH_LINE_COLOR, 1.0, 1.0)); - planets.push(new Planet("mars", MARS_LINE_COLOR, 1.52, 0.532)); - planets.push(new Planet("jupiter", JUPITER_LINE_COLOR, 5.20, 11.21)); - planets.push(new Planet("saturn", SATURN_LINE_COLOR, 9.58, 9.45)); - planets.push(new Planet("uranus", URANUS_LINE_COLOR, 19.20, 4.01)); - planets.push(new Planet("neptune", NEPTUNE_LINE_COLOR, 30.05, 3.88)); - planets.push(new Planet("pluto", PLUTO_LINE_COLOR, 39.48, 0.186)); - - - for (var i = 0; i < planets.length; ++i) { - planets[i].resetTrails(); - } - + time = 0.0; elapsed = 0.0; + planets.length = 0; + initPlanets(); + MyAvatar.position = startingPosition; Camera.setPosition(cameraStart); }; - - } - CreateSimulation(); - Script.update.connect(update); Script.scriptEnding.connect(scriptEnding); \ No newline at end of file From 56501490a12455d460cb893993b6070306031d60 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 14 Aug 2015 10:09:16 -0700 Subject: [PATCH 22/49] Update edit.js mouse click code to be more lenient to micro movements --- examples/edit.js | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/examples/edit.js b/examples/edit.js index 0f9c9fc4dd..510570f136 100644 --- a/examples/edit.js +++ b/examples/edit.js @@ -574,8 +574,14 @@ function findClickedEntity(event) { } var mouseHasMovedSincePress = false; +var mousePressStartTime = 0; +var mousePressStartPosition = { x: 0, y: 0 }; +var mouseDown = false; function mousePressEvent(event) { + mouseDown = true; + mousePressStartPosition = { x: event.x, y: event.y }; + mousePressStartTime = Date.now(); mouseHasMovedSincePress = false; mouseCapturedByTool = false; @@ -595,6 +601,8 @@ var highlightedEntityID = null; var mouseCapturedByTool = false; var lastMousePosition = null; var idleMouseTimerId = null; +var CLICK_TIME_THRESHOLD = 500 * 1000; // 500 ms +var CLICK_MOVE_DISTANCE_THRESHOLD = 3; var IDLE_MOUSE_TIMEOUT = 200; var DEFAULT_ENTITY_DRAG_DROP_DISTANCE = 2.0; @@ -603,7 +611,21 @@ function mouseMoveEventBuffered(event) { lastMouseMoveEvent = event; } function mouseMove(event) { - mouseHasMovedSincePress = true; + if (mouseDown && !mouseHasMovedSincePress) { + var timeSincePressMicro = Date.now() - mousePressStartTime; + + var dX = mousePressStartPosition.x - event.x; + var dY = mousePressStartPosition.y - event.y; + var sqDist = (dX * dX) + (dY * dY); + + // If less than CLICK_TIME_THRESHOLD has passed since the mouse click AND the mouse has moved + // less than CLICK_MOVE_DISTANCE_THRESHOLD distance, then don't register this as a mouse move + // yet. The goal is to provide mouse clicks that are more lenient to small movements. + if (timeSincePressMicro < CLICK_TIME_THRESHOLD && sqDist < CLICK_MOVE_DISTANCE_THRESHOLD) { + return; + } + mouseHasMovedSincePress = true; + } if (placingEntityID) { var pickRay = Camera.computePickRay(event.x, event.y); @@ -670,6 +692,8 @@ function highlightEntityUnderCursor(position, accurateRay) { function mouseReleaseEvent(event) { + mouseDown = false; + if (lastMouseMoveEvent) { mouseMove(lastMouseMoveEvent); lastMouseMoveEvent = null; From ee11b74c8bd9e546d5b14aeb93bad86e45a92442 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 14 Aug 2015 10:13:05 -0700 Subject: [PATCH 23/49] Update click move distance threshold --- examples/edit.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/edit.js b/examples/edit.js index 510570f136..53f56dc03a 100644 --- a/examples/edit.js +++ b/examples/edit.js @@ -602,7 +602,7 @@ var mouseCapturedByTool = false; var lastMousePosition = null; var idleMouseTimerId = null; var CLICK_TIME_THRESHOLD = 500 * 1000; // 500 ms -var CLICK_MOVE_DISTANCE_THRESHOLD = 3; +var CLICK_MOVE_DISTANCE_THRESHOLD = 8; var IDLE_MOUSE_TIMEOUT = 200; var DEFAULT_ENTITY_DRAG_DROP_DISTANCE = 2.0; From 2c5c362957e87d608c559c201350724d28642a48 Mon Sep 17 00:00:00 2001 From: bwent Date: Fri, 14 Aug 2015 10:19:57 -0700 Subject: [PATCH 24/49] adding rocket and refresh icons --- examples/example/planets-ui.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/example/planets-ui.js b/examples/example/planets-ui.js index f749f97337..05e1d134eb 100644 --- a/examples/example/planets-ui.js +++ b/examples/example/planets-ui.js @@ -158,9 +158,9 @@ var systemViewPanel = addPanel({ dir: '+x', visible: false }); -var restartButton = addImage(systemViewPanel, 'reverse'); +var restartButton = addImage(systemViewPanel, 'refresh'); var pauseButton = addImage(systemViewPanel, 'playpause'); -var rideButton = addImage(systemViewPanel, 'forward'); +var rideButton = addImage(systemViewPanel, 'rocket'); var tweening, tweeningPaused; Script.include('https://hifi-staff.s3.amazonaws.com/bridget/tween.js'); From 68fcf4ec8ac29999040b2cffd696f8511d86f970 Mon Sep 17 00:00:00 2001 From: bwent Date: Fri, 14 Aug 2015 11:06:44 -0700 Subject: [PATCH 25/49] formatting --- examples/example/planets-ui.js | 16 ++++++++-------- examples/example/solarsystem.js | 8 ++++---- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/examples/example/planets-ui.js b/examples/example/planets-ui.js index 05e1d134eb..55e58de227 100644 --- a/examples/example/planets-ui.js +++ b/examples/example/planets-ui.js @@ -167,11 +167,11 @@ Script.include('https://hifi-staff.s3.amazonaws.com/bridget/tween.js'); pauseButton.addAction('onClick', function() { if (tweening) { - if(!tweeningPaused) { + if (!tweeningPaused) { tweeningPaused = true; } else { tweeningPaused = false; - } + } } if (!paused) { pause(); @@ -182,13 +182,13 @@ pauseButton.addAction('onClick', function() { // Allow to toggle pause with spacebar function keyPressEvent(event) { - if(event.text == "SPACE") { - if (tweening) { - if(!tweeningPaused) { + if (event.text == "SPACE") { + if (tweening) { + if (!tweeningPaused) { tweeningPaused = true; } else { tweeningPaused = false; - } + } } if (!paused) { pause(); @@ -201,7 +201,7 @@ function keyPressEvent(event) { rideButton.addAction('onClick', function() { if (!paused) { pause(); - } + } if (tweening) { tweening = false; tweeningPaused = true; @@ -388,7 +388,7 @@ satelliteButton.addAction('onClick', function() { if (satelliteGame && satelliteGame.isActive) { MyAvatar.position = startingPosition; satelliteGame.quitGame(); - if(paused) { + if (paused) { resume(); } } else { diff --git a/examples/example/solarsystem.js b/examples/example/solarsystem.js index 7c58cfb299..05f10eeebb 100644 --- a/examples/example/solarsystem.js +++ b/examples/example/solarsystem.js @@ -24,7 +24,7 @@ CreateSimulation = function() { trailsEnabled = true; - + var DAMPING = this.DAMPING = 0.0; var LIFETIME = this.LIFETIME = 6000; var BASE_URL = this.BASE_URL = "https://s3.amazonaws.com/hifi-public/marketplace/hificontent/Scripts/planets/planets/"; @@ -222,7 +222,7 @@ CreateSimulation = function() { visible: false }); } - + } this.resetTrails = function() { @@ -344,7 +344,7 @@ CreateSimulation = function() { }; this.initPlanets = function() { - planets.push(new Planet("mercury", MERCURY_LINE_COLOR, 0.387, 0.383)); + planets.push(new Planet("mercury", MERCURY_LINE_COLOR, 0.387, 0.383)); planets.push(new Planet("venus", VENUS_LINE_COLOR, 0.723, 0.949)); planets.push(new Planet("earth", EARTH_LINE_COLOR, 1.0, 1.0)); planets.push(new Planet("mars", MARS_LINE_COLOR, 1.52, 0.532)); @@ -355,7 +355,7 @@ CreateSimulation = function() { planets.push(new Planet("pluto", PLUTO_LINE_COLOR, 39.48, 0.186)); } initPlanets(); - + var LABEL_X = 8.0; var LABEL_Y = 3.0; var LABEL_Z = 1.0; From bb2c48e3eddbcfd0ee03f932b48caeb035626f6e Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 14 Aug 2015 14:42:32 -0700 Subject: [PATCH 26/49] Add size and position properties, methods, and events to WebWindow --- interface/src/scripting/WebWindowClass.cpp | 40 ++++++++++++++++++++++ interface/src/scripting/WebWindowClass.h | 13 +++++++ 2 files changed, 53 insertions(+) diff --git a/interface/src/scripting/WebWindowClass.cpp b/interface/src/scripting/WebWindowClass.cpp index 5c3cb6c42f..f0fd937a7d 100644 --- a/interface/src/scripting/WebWindowClass.cpp +++ b/interface/src/scripting/WebWindowClass.cpp @@ -58,6 +58,7 @@ WebWindowClass::WebWindowClass(const QString& title, const QString& url, int wid auto dialogWidget = new QDialog(Application::getInstance()->getWindow(), Qt::Window); dialogWidget->setWindowTitle(title); dialogWidget->resize(width, height); + dialogWidget->installEventFilter(this); connect(dialogWidget, &QDialog::finished, this, &WebWindowClass::hasClosed); auto layout = new QVBoxLayout(dialogWidget); @@ -93,6 +94,19 @@ WebWindowClass::WebWindowClass(const QString& title, const QString& url, int wid WebWindowClass::~WebWindowClass() { } +bool WebWindowClass::eventFilter(QObject* sender, QEvent* event) { + if (sender == _windowWidget) { + if (event->type() == QEvent::Move) { + emit moved(getPosition()); + } + if (event->type() == QEvent::Resize) { + emit resized(getSize()); + } + } + + return false; +} + void WebWindowClass::hasClosed() { emit closed(); } @@ -122,6 +136,32 @@ void WebWindowClass::setURL(const QString& url) { _webView->setUrl(url); } +QSizeF WebWindowClass::getSize() const { + QSizeF size = _windowWidget->size(); + return size; +} + +void WebWindowClass::setSize(QSizeF size) { + setSize(size.width(), size.height()); +} + +void WebWindowClass::setSize(int width, int height) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "setSize", Qt::AutoConnection, Q_ARG(int, width), Q_ARG(int, height)); + return; + } + _windowWidget->resize(width, height); +} + +glm::vec2 WebWindowClass::getPosition() const { + QPoint position = _windowWidget->pos(); + return glm::vec2(position.x(), position.y()); +} + +void WebWindowClass::setPosition(glm::vec2 position) { + setPosition(position.x, position.y); +} + void WebWindowClass::setPosition(int x, int y) { if (QThread::currentThread() != thread()) { QMetaObject::invokeMethod(this, "setPosition", Qt::AutoConnection, Q_ARG(int, x), Q_ARG(int, y)); diff --git a/interface/src/scripting/WebWindowClass.h b/interface/src/scripting/WebWindowClass.h index 43dbef0392..3659c08ac2 100644 --- a/interface/src/scripting/WebWindowClass.h +++ b/interface/src/scripting/WebWindowClass.h @@ -35,6 +35,9 @@ class WebWindowClass : public QObject { Q_OBJECT Q_PROPERTY(QObject* eventBridge READ getEventBridge) Q_PROPERTY(QString url READ getURL) + Q_PROPERTY(glm::vec2 position READ getPosition WRITE setPosition); + Q_PROPERTY(QSizeF size READ getSize WRITE setSize); + public: WebWindowClass(const QString& title, const QString& url, int width, int height, bool isToolWindow = false); ~WebWindowClass(); @@ -43,7 +46,12 @@ public: public slots: void setVisible(bool visible); + glm::vec2 getPosition() const; void setPosition(int x, int y); + void setPosition(glm::vec2 position); + QSizeF getSize() const; + void setSize(QSizeF size); + void setSize(int width, int height); QString getURL() const { return _webView->url().url(); } void setURL(const QString& url); void raise(); @@ -52,8 +60,13 @@ public slots: void setTitle(const QString& title); signals: + void moved(glm::vec2 position); + void resized(QSizeF size); void closed(); +protected: + virtual bool eventFilter(QObject* sender, QEvent* event); + private slots: void hasClosed(); From 7bd9e2653ca14aef12940a77f3492c4c42e88686 Mon Sep 17 00:00:00 2001 From: bwent Date: Fri, 14 Aug 2015 15:09:48 -0700 Subject: [PATCH 27/49] more bug fixes --- examples/example/games/satellite.js | 4 ---- examples/example/planets-ui.js | 11 ++--------- examples/example/solarsystem.js | 19 ++++++++++++------- 3 files changed, 14 insertions(+), 20 deletions(-) diff --git a/examples/example/games/satellite.js b/examples/example/games/satellite.js index c0914ebe3c..db65198b87 100644 --- a/examples/example/games/satellite.js +++ b/examples/example/games/satellite.js @@ -20,7 +20,6 @@ Script.include('../utilities/tools/vector.js'); var URL = "https://s3.amazonaws.com/hifi-public/marketplace/hificontent/Scripts/planets/"; SatelliteCreator = function() { - print("initializing satellite game"); var MAX_RANGE = 50.0; @@ -291,9 +290,6 @@ SatelliteCreator = function() { } this.quitGame = function() { - if(!this.isActive) { - return; - } print("ending satellite game"); this.isActive = false; diff --git a/examples/example/planets-ui.js b/examples/example/planets-ui.js index 55e58de227..afca55a65e 100644 --- a/examples/example/planets-ui.js +++ b/examples/example/planets-ui.js @@ -165,6 +165,7 @@ var rideButton = addImage(systemViewPanel, 'rocket'); var tweening, tweeningPaused; Script.include('https://hifi-staff.s3.amazonaws.com/bridget/tween.js'); + pauseButton.addAction('onClick', function() { if (tweening) { if (!tweeningPaused) { @@ -172,6 +173,7 @@ pauseButton.addAction('onClick', function() { } else { tweeningPaused = false; } + return; } if (!paused) { pause(); @@ -183,13 +185,6 @@ pauseButton.addAction('onClick', function() { // Allow to toggle pause with spacebar function keyPressEvent(event) { if (event.text == "SPACE") { - if (tweening) { - if (!tweeningPaused) { - tweeningPaused = true; - } else { - tweeningPaused = false; - } - } if (!paused) { pause(); } else { @@ -214,13 +209,11 @@ rideButton.addAction('onClick', function() { tweening = true; tweeningPaused = false; } - }); restartButton.addAction('onClick', function() { restart(); tweening = false; - }); var zoomPanel = addPanel({ diff --git a/examples/example/solarsystem.js b/examples/example/solarsystem.js index 05f10eeebb..139efe3ad8 100644 --- a/examples/example/solarsystem.js +++ b/examples/example/solarsystem.js @@ -21,9 +21,8 @@ CreateSimulation = function() { Script.include("https://hifi-public.s3.amazonaws.com/eric/scripts/tween.js"); Script.include('games/satellite.js'); - - - trailsEnabled = true; + + var trailsEnabled = this.trailsEnabled = true; var DAMPING = this.DAMPING = 0.0; var LIFETIME = this.LIFETIME = 6000; @@ -127,6 +126,9 @@ CreateSimulation = function() { } this.pause = function() { + if(paused) { + return; + } for (var i = 0; i < planets.length; ++i) { Entities.editEntity(planets[i].planet, { velocity: { @@ -142,6 +144,9 @@ CreateSimulation = function() { } this.resume = function() { + if(!paused) { + return; + } for (var i = 0; i < planets.length; ++i) { planets[i].label.hide(); } @@ -216,18 +221,15 @@ CreateSimulation = function() { this.clearTrails = function() { elapsed = 0.0; - for (var j = 0; j < this.lineStack.length; ++j) { Entities.editEntity(this.lineStack[j], { visible: false }); } - } this.resetTrails = function() { elapsed = 0.0; - this.trail = []; this.lineStack = []; //add the first line to both the line entity stack and the trail @@ -488,12 +490,15 @@ CreateSimulation = function() { elapsed = 0.0; planets.length = 0; initPlanets(); + MyAvatar.position = startingPosition; Camera.setPosition(cameraStart); }; + } CreateSimulation(); Script.update.connect(update); -Script.scriptEnding.connect(scriptEnding); \ No newline at end of file +Script.scriptEnding.connect(scriptEnding); + From 7834a18bcfa5d82b4fd8fab5a7ed14ef7a43535d Mon Sep 17 00:00:00 2001 From: bwent Date: Fri, 14 Aug 2015 15:11:05 -0700 Subject: [PATCH 28/49] add untracked changes --- examples/example/solarsystem.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/examples/example/solarsystem.js b/examples/example/solarsystem.js index 139efe3ad8..0a16bf52f7 100644 --- a/examples/example/solarsystem.js +++ b/examples/example/solarsystem.js @@ -21,7 +21,7 @@ CreateSimulation = function() { Script.include("https://hifi-public.s3.amazonaws.com/eric/scripts/tween.js"); Script.include('games/satellite.js'); - + var trailsEnabled = this.trailsEnabled = true; var DAMPING = this.DAMPING = 0.0; @@ -126,7 +126,7 @@ CreateSimulation = function() { } this.pause = function() { - if(paused) { + if (paused) { return; } for (var i = 0; i < planets.length; ++i) { @@ -144,7 +144,7 @@ CreateSimulation = function() { } this.resume = function() { - if(!paused) { + if (!paused) { return; } for (var i = 0; i < planets.length; ++i) { @@ -490,7 +490,7 @@ CreateSimulation = function() { elapsed = 0.0; planets.length = 0; initPlanets(); - + MyAvatar.position = startingPosition; Camera.setPosition(cameraStart); @@ -500,5 +500,4 @@ CreateSimulation = function() { CreateSimulation(); Script.update.connect(update); -Script.scriptEnding.connect(scriptEnding); - +Script.scriptEnding.connect(scriptEnding); \ No newline at end of file From 45fe6a438c9aadf70377e13d5ccc7d2006ee315c Mon Sep 17 00:00:00 2001 From: bwent Date: Fri, 14 Aug 2015 16:18:07 -0700 Subject: [PATCH 29/49] Example particle scripts --- examples/fireworks.js | 251 ++++++++++++++++++++++++++++++++++++++ examples/particleDance.js | 154 +++++++++++++++++++++++ 2 files changed, 405 insertions(+) create mode 100644 examples/fireworks.js create mode 100644 examples/particleDance.js diff --git a/examples/fireworks.js b/examples/fireworks.js new file mode 100644 index 0000000000..b0a8591fa8 --- /dev/null +++ b/examples/fireworks.js @@ -0,0 +1,251 @@ +HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; +var fireSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Guns/GUN-SHOT2.raw"); +var audioOptions = { + volume: 0.9, + position: Vec3.sum(Camera.getPosition(), Quat.getFront(Camera.getOrientation())) +}; + +var DISTANCE_FROM_CAMERA = 7.0; + +var bluePalette = [{ + red: 0, + green: 206, + blue: 209 +}, { + red: 173, + green: 216, + blue: 230 +}, { + red: 0, + green: 191, + blue: 255 +}]; + +var greenPalette = [{ + red: 152, + green: 251, + blue: 152 +}, { + red: 127, + green: 255, + blue: 0 +}, { + red: 50, + green: 205, + blue: 50 +}]; + +var redPalette = [{ + red: 255, + green: 20, + blue: 147 +}, { + red: 255, + green: 69, + blue: 0 +}, { + red: 255, + green: 90, + blue: 120 +}]; + + +var COLOR_RED = {red: 255, green: 0, blue: 0 }; +var COLOR_GREEN = {red: 0, green: 255, blue: 0}; +var COLOR_BLUE = {red: 0, green: 0, blue: 255}; +var iconsX = 700; +var iconsY = 660; +var ICON_SIZE = 30; + +var redIcon = Overlays.addOverlay("text", { + backgroundColor: COLOR_RED, + x: iconsX, + y: iconsY, + width: ICON_SIZE, + height: ICON_SIZE, + alpha: 0.0, + backgroundAlpha: 1.0, + visible: true + }); +var greenIcon = Overlays.addOverlay("text", { + backgroundColor: COLOR_GREEN, + x: iconsX + 50, + y: iconsY, + width: ICON_SIZE, + height: ICON_SIZE, + alpha: 0.0, + backgroundAlpha: 1.0, + visible: true + }); +var blueIcon = Overlays.addOverlay("text", { + backgroundColor: COLOR_BLUE, + x: iconsX + 100, + y: iconsY, + width: ICON_SIZE, + height: ICON_SIZE, + alpha: 0.0, + backgroundAlpha: 1.0, + visible: true + }); + + +var NUM_BURSTS = 11; +var SPEED = 6.0; + +var rockets = []; +Rocket = function(point, colorPalette) { + //default to blue palette if no palette passed in + this.colors = colorPalette; + this.point = point; + this.bursts = []; + this.burst = false; + + this.emitRate = randInt(80, 120); + this.emitStrength = randInt(5.0, 7.0); + + this.rocket = Entities.addEntity({ + type: "Sphere", + position: this.point, + dimensions: { + x: 0.07, + y: 0.07, + z: 0.07 + }, + color: { + red: 240, + green: 240, + blue: 240 + } + }); + + this.animationSettings = JSON.stringify({ + fps: 40, + frameIndex: 0, + running: true, + firstFrame: 0, + lastFrame: 20, + loop: false + }); + + this.direction = { + x: randFloat(-0.4, 0.4), + y: 1.0, + z: 0.0 + } + + this.time = 0.0; + this.timeout = randInt(15, 40); +}; + +Rocket.prototype.update = function(deltaTime) { + this.time++; + + Entities.editEntity(this.rocket, { + velocity: Vec3.multiply(SPEED, this.direction) + }); + var position = Entities.getEntityProperties(this.rocket).position; + + if (this.time > this.timeout) { + this.explode(position); + return; + } +}; + + +Rocket.prototype.explode = function(position) { + Audio.playSound(fireSound, audioOptions); + Entities.editEntity(this.rocket, { + velocity: { + x: 0, + y: 0, + z: 0 + } + }); + + var colorIndex = 0; + for (var i = 0; i < NUM_BURSTS; ++i) { + var color = this.colors[colorIndex]; + print(JSON.stringify(color)); + this.bursts.push(Entities.addEntity({ + type: "ParticleEffect", + animationSettings: this.animationSettings, + position: position, + textures: 'https://raw.githubusercontent.com/ericrius1/SantasLair/santa/assets/smokeparticle.png', + emitRate: this.emitRate, + emitStrength: this.emitStrength, + emitDirection: { + x: Math.pow(-1, i) * randFloat(0.0, 1.4), + y: 1.0, + z: 0.0 + }, + color: color, + lifespan: 1.0, + visible: true, + locked: false + })); + + if (colorIndex < this.colors.length - 1) { + colorIndex++; + } + } + + this.burst = true; + Entities.deleteEntity(this.rocket); +}; + +//var lastLoudness; +var LOUDNESS_RADIUS_RATIO = 10; + +function update(deltaTime) { + for (var i = 0; i < rockets.length; i++) { + if (!rockets[i].burst) { + rockets[i].update(); + } + } +} + +function randFloat(min, max) { + return Math.random() * (max - min) + min; +} + +function randInt(min, max) { + return Math.floor(Math.random() * (max - min)) + min; +} + +function computeWorldPoint(event) { + var pickRay = Camera.computePickRay(event.x, event.y); + var addVector = Vec3.multiply(Vec3.normalize(pickRay.direction), DISTANCE_FROM_CAMERA); + return Vec3.sum(Camera.getPosition(), addVector); +} + +function mousePressEvent(event) { + var clickedOverlay = Overlays.getOverlayAtPoint({ + x: event.x, + y: event.y + }); + if(clickedOverlay === redIcon) { + rockets.push(new Rocket(computeWorldPoint(event), redPalette)); + } else if (clickedOverlay === greenIcon) { + rockets.push(new Rocket(computeWorldPoint(event), greenPalette)); + } else if (clickedOverlay === blueIcon) { + rockets.push(new Rocket(computeWorldPoint(event), bluePalette)); + } + +} + +function cleanup() { + Overlays.deleteOverlay(redIcon); + Overlays.deleteOverlay(greenIcon); + Overlays.deleteOverlay(blueIcon); + for (var i = 0; i < rockets.length; ++i) { + Entities.deleteEntity(rockets[i].rocket); + for (var j = 0; j < NUM_BURSTS; ++j) { + Entities.deleteEntity(rockets[i].bursts[j]); + } + + } +} + +Script.update.connect(update); +Script.scriptEnding.connect(cleanup); +Controller.mousePressEvent.connect(mousePressEvent); \ No newline at end of file diff --git a/examples/particleDance.js b/examples/particleDance.js new file mode 100644 index 0000000000..d79b0ed17c --- /dev/null +++ b/examples/particleDance.js @@ -0,0 +1,154 @@ +(function() { + var NUM_BURSTS = 3; + var NUM_EMITTERS_PER_BURST = 11; + + var RANGE = 5.0; + var AUDIO_RANGE = 0.5 * RANGE; + var DIST_BETWEEN_BURSTS = 1.0; + + var LOUDNESS_RADIUS_RATIO = 10; + + var TEXTURE_PATH = 'https://raw.githubusercontent.com/ericrius1/SantasLair/santa/assets/smokeparticle.png'; + var cameraAxis = Quat.getFront(Camera.getOrientation()); + var center = Vec3.sum(Camera.getPosition(), Vec3.multiply(RANGE, cameraAxis)); + var audioPosition = Vec3.sum(Camera.getPosition(), Vec3.multiply(AUDIO_RANGE, cameraAxis)); + + var song = SoundCache.getSound("https://hifi-public.s3.amazonaws.com/eric/sounds/songs/Made%20In%20Heights%20-%20Forgiveness.wav"); + var audioOptions = { + volume: 0.9, position: audioPosition + }; + + var DISTANCE_FROM_CAMERA = 7.0; + + var colorPalette = [{ + red: 0, + green: 206, + blue: 209 + }, { + red: 173, + green: 216, + blue: 230 + }, { + red: 0, + green: 191, + blue: 255 + }]; + + var bursts = []; + var audioStats; + + Burst = function(point) { + if (!audioStats) { + audioStats = Audio.playSound(song, audioOptions); + } + + this.point = point; + this.emitters = []; + + this.emitRate = randInt(80, 120); + this.emitStrength = randInt(4.0, 6.0); + + this.animationSettings = JSON.stringify({ + fps: 10, + frameIndex: 0, + running: true, + firstFrame: 0, + lastFrame: 50, + loop: true + }); + + this.direction = { + x: randFloat(-0.3, 0.3), + y: 1.0, + z: 0.0 + } + + this.base = Entities.addEntity({ + type: "Sphere", + position: this.point, + dimensions: { + x: 0.05, + y: 0.05, + z: 0.05 + }, + color: { + red: 240, + green: 240, + blue: 240 + } + }); + for (var i = 0; i < NUM_EMITTERS_PER_BURST; ++i) { + var colorIndex = randInt(0, colorPalette.length - 1); + var color = colorPalette[colorIndex]; + this.emitters.push(Entities.addEntity({ + type: "ParticleEffect", + animationSettings: this.animationSettings, + position: this.point, + textures: TEXTURE_PATH, + emitRate: this.emitRate, + emitStrength: this.emitStrength, + emitDirection: { + x: Math.pow(-1, i) * randFloat(0.0, 0.4), + y: 1.0, + z: 0.0 + }, + color: color, + lifespan: 1.0, + visible: true, + locked: false + })); + } + + }; + + var nextPosition = center; + var posOrNeg = -1; + + for (var i = 0; i < NUM_BURSTS; ++i) { + posOrNeg *= -1; + bursts.push(new Burst(nextPosition)); + var offset = { + x: RANGE/(i+2) * posOrNeg, + y: 0, + z: 0 + }; + var nextPosition = Vec3.sum(nextPosition, offset); + } + + function update(deltaTime) { + for (var i = 0; i < NUM_BURSTS; i++) { + if (audioStats && audioStats.loudness > 0.0) { + for (var j = 0; j < NUM_EMITTERS_PER_BURST; ++j) { + Entities.editEntity(bursts[i].emitters[j], { + particleRadius: audioStats.loudness / LOUDNESS_RADIUS_RATIO + }); + } + } + } + } + + function randFloat(min, max) { + return Math.random() * (max - min) + min; + } + + function randInt(min, max) { + return Math.floor(Math.random() * (max - min)) + min; + } + + this.cleanup = function() { + for (var i = 0; i < NUM_BURSTS; ++i) { + Entities.deleteEntity(bursts[i].base); + for (var j = 0; j < NUM_EMITTERS_PER_BURST; ++j) { + var emitter = bursts[i].emitters[j]; + Entities.deleteEntity(emitter); + } + + } + Audio.stop(); + } + + Script.update.connect(update); + + })(); + + Script.scriptEnding.connect(cleanup); \ No newline at end of file From 208e41efd19d12eff4c2d25f83561315c8f445b9 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 13 Aug 2015 16:22:26 -0700 Subject: [PATCH 30/49] WebEntity keyboard input with mouse hover --- interface/src/Application.cpp | 51 ++++++++++++++++++- interface/src/Application.h | 2 + .../src/RenderableWebEntityItem.cpp | 8 +++ .../src/RenderableWebEntityItem.h | 5 ++ .../render-utils/src/OffscreenQmlSurface.cpp | 4 ++ .../render-utils/src/OffscreenQmlSurface.h | 1 + 6 files changed, 69 insertions(+), 2 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 598498e2d0..e7783c779e 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -98,6 +98,8 @@ #include #include +#include + #include "AudioClient.h" #include "DiscoverabilityManager.h" #include "GLCanvas.h" @@ -146,9 +148,8 @@ #include "ui/AddressBarDialog.h" #include "ui/UpdateDialog.h" -//#include - // ON WIndows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU +// FIXME seems to be broken. #if defined(Q_OS_WIN) extern "C" { _declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001; @@ -671,6 +672,28 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : auto& packetReceiver = nodeList->getPacketReceiver(); packetReceiver.registerListener(PacketType::DomainConnectionDenied, this, "handleDomainConnectionDeniedPacket"); + + auto entityScriptingInterface = DependencyManager::get(); + connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverEnterEntity, [this, entityScriptingInterface](const EntityItemID& entityItemID, const MouseEvent& event) { + if (_keyboardFocusedItem != entityItemID) { + _keyboardFocusedItem = UNKNOWN_ENTITY_ID; + auto properties = entityScriptingInterface->getEntityProperties(entityItemID); + if (EntityTypes::Web == properties.getType() && !properties.getLocked()) { + auto entity = entityScriptingInterface->getEntityTree()->findEntityByID(entityItemID); + RenderableWebEntityItem* webEntity = dynamic_cast(entity.get()); + if (webEntity) { + webEntity->setProxyWindow(_window->windowHandle()); + _keyboardFocusedItem = entityItemID; + } + } + } + }); + + connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverLeaveEntity, [=](const EntityItemID& entityItemID, const MouseEvent& event) { + if (_keyboardFocusedItem == entityItemID) { + _keyboardFocusedItem = UNKNOWN_ENTITY_ID; + } + }); } void Application::aboutToQuit() { @@ -854,6 +877,10 @@ void Application::initializeGL() { InfoView::show(INFO_HELP_PATH, true); } +QWindow* getProxyWindow() { + return qApp->getWindow()->windowHandle(); +} + void Application::initializeUi() { AddressBarDialog::registerType(); ErrorDialog::registerType(); @@ -1232,6 +1259,26 @@ bool Application::event(QEvent* event) { return true; } + if (!_keyboardFocusedItem.isInvalidID()) { + switch (event->type()) { + case QEvent::KeyPress: + case QEvent::KeyRelease: + { + auto entityScriptingInterface = DependencyManager::get(); + auto entity = entityScriptingInterface->getEntityTree()->findEntityByID(_keyboardFocusedItem); + RenderableWebEntityItem* webEntity = dynamic_cast(entity.get()); + if (webEntity && webEntity->getEventHandler()) { + event->setAccepted(false); + QCoreApplication::sendEvent(webEntity->getEventHandler(), event); + if (event->isAccepted()) { + return true; + } + } + } + break; + } + } + switch (event->type()) { case QEvent::MouseMove: mouseMoveEvent((QMouseEvent*)event); diff --git a/interface/src/Application.h b/interface/src/Application.h index 239b440822..cec1325baf 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -678,6 +678,8 @@ private: bool _overlayEnabled = true; QRect _savedGeometry; DialogsManagerScriptingInterface* _dialogsManagerScriptingInterface = new DialogsManagerScriptingInterface(); + + EntityItemID _keyboardFocusedItem; }; #endif // hifi_Application_h diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index ce5b389333..73aa6f2718 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -195,3 +195,11 @@ void RenderableWebEntityItem::setSourceUrl(const QString& value) { } } } + +void RenderableWebEntityItem::setProxyWindow(QWindow* proxyWindow) { + _webSurface->setProxyWindow(proxyWindow); +} + +QObject* RenderableWebEntityItem::getEventHandler() { + return _webSurface->getEventHandler(); +} diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.h b/libraries/entities-renderer/src/RenderableWebEntityItem.h index ee9c2531f1..63418a890f 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.h +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.h @@ -16,6 +16,8 @@ #include "RenderableEntityItem.h" class OffscreenQmlSurface; +class QWindow; +class QObject; class RenderableWebEntityItem : public WebEntityItem { public: @@ -26,6 +28,9 @@ public: virtual void render(RenderArgs* args); virtual void setSourceUrl(const QString& value); + + void setProxyWindow(QWindow* proxyWindow); + QObject* getEventHandler(); SIMPLE_RENDERABLE(); diff --git a/libraries/render-utils/src/OffscreenQmlSurface.cpp b/libraries/render-utils/src/OffscreenQmlSurface.cpp index 78edb8e899..0dfd95a725 100644 --- a/libraries/render-utils/src/OffscreenQmlSurface.cpp +++ b/libraries/render-utils/src/OffscreenQmlSurface.cpp @@ -599,6 +599,10 @@ void OffscreenQmlSurface::setProxyWindow(QWindow* window) { _renderer->_renderControl->_renderWindow = window; } +QObject* OffscreenQmlSurface::getEventHandler() { + return getWindow(); +} + QQuickWindow* OffscreenQmlSurface::getWindow() { return _renderer->_quickWindow; } diff --git a/libraries/render-utils/src/OffscreenQmlSurface.h b/libraries/render-utils/src/OffscreenQmlSurface.h index 1a1827b63f..13e467bccd 100644 --- a/libraries/render-utils/src/OffscreenQmlSurface.h +++ b/libraries/render-utils/src/OffscreenQmlSurface.h @@ -59,6 +59,7 @@ public: void setBaseUrl(const QUrl& baseUrl); QQuickItem* getRootItem(); QQuickWindow* getWindow(); + QObject* getEventHandler(); virtual bool eventFilter(QObject* originalDestination, QEvent* event); From 889d9c54a562231ebd2a2db45f27700b649bdc2d Mon Sep 17 00:00:00 2001 From: bwent Date: Fri, 14 Aug 2015 16:24:11 -0700 Subject: [PATCH 31/49] add newline at end of file --- examples/fireworks.js | 2 +- examples/particleDance.js | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/fireworks.js b/examples/fireworks.js index b0a8591fa8..621931ee36 100644 --- a/examples/fireworks.js +++ b/examples/fireworks.js @@ -248,4 +248,4 @@ function cleanup() { Script.update.connect(update); Script.scriptEnding.connect(cleanup); -Controller.mousePressEvent.connect(mousePressEvent); \ No newline at end of file +Controller.mousePressEvent.connect(mousePressEvent); diff --git a/examples/particleDance.js b/examples/particleDance.js index d79b0ed17c..e920fcfa41 100644 --- a/examples/particleDance.js +++ b/examples/particleDance.js @@ -151,4 +151,5 @@ })(); - Script.scriptEnding.connect(cleanup); \ No newline at end of file + Script.scriptEnding.connect(cleanup); + \ No newline at end of file From 6a802560f252883debefd36b7d45ff32ec28691d Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 14 Aug 2015 16:27:50 -0700 Subject: [PATCH 32/49] remove HMD Tools as menu item, automatically hide/show it when display plugin is an hmd --- interface/src/Application.cpp | 5 +++++ interface/src/Menu.cpp | 10 ---------- interface/src/Menu.h | 1 - interface/src/ui/DialogsManager.cpp | 1 - interface/src/ui/HMDToolsDialog.cpp | 11 +++++------ 5 files changed, 10 insertions(+), 18 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 598498e2d0..f392810598 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4680,6 +4680,11 @@ void Application::updateDisplayMode() { if (oldDisplayPlugin) { oldDisplayPlugin->deactivate(); _offscreenContext->makeCurrent(); + + // if the old plugin was HMD and the new plugin is not HMD, then hide our hmdtools + if (oldDisplayPlugin->isHmd() && !newDisplayPlugin->isHmd()) { + DependencyManager::get()->hmdTools(false); + } } emit activeDisplayPluginChanged(); resetSensors(); diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index a31353fa4c..939d733a13 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -289,16 +289,6 @@ Menu::Menu() { addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::CenterPlayerInView, 0, false, qApp, SLOT(rotationModeChanged())); - addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::HMDTools, -#ifdef Q_OS_MAC - Qt::META | Qt::Key_H, -#else - Qt::CTRL | Qt::Key_H, -#endif - false, - dialogsManager.data(), - SLOT(hmdTools(bool))); - addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::TurnWithHead, 0, false); addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::StandingHMDSensorMode, 0, false, diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 789fccecd8..b0a78fdd83 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -199,7 +199,6 @@ namespace MenuOption { const QString FullscreenMirror = "Fullscreen Mirror"; const QString GlowWhenSpeaking = "Glow When Speaking"; const QString HandMouseInput = "Enable Hand Mouse Input"; - const QString HMDTools = "HMD Tools"; const QString IncreaseAvatarSize = "Increase Avatar Size"; const QString IndependentMode = "Independent Mode"; const QString InputMenu = "Avatar>Input Devices"; diff --git a/interface/src/ui/DialogsManager.cpp b/interface/src/ui/DialogsManager.cpp index ac5e6833fb..8131aa49f2 100644 --- a/interface/src/ui/DialogsManager.cpp +++ b/interface/src/ui/DialogsManager.cpp @@ -173,7 +173,6 @@ void DialogsManager::hmdTools(bool showTools) { } void DialogsManager::hmdToolsClosed() { - Menu::getInstance()->getActionForOption(MenuOption::HMDTools)->setChecked(false); _hmdToolsDialog->hide(); } diff --git a/interface/src/ui/HMDToolsDialog.cpp b/interface/src/ui/HMDToolsDialog.cpp index cc596e5e55..e03e05912c 100644 --- a/interface/src/ui/HMDToolsDialog.cpp +++ b/interface/src/ui/HMDToolsDialog.cpp @@ -30,8 +30,9 @@ static const int WIDTH = 350; static const int HEIGHT = 100; + HMDToolsDialog::HMDToolsDialog(QWidget* parent) : - QDialog(parent, Qt::Window | Qt::WindowCloseButtonHint | Qt::WindowStaysOnTopHint) + QDialog(parent, Qt::Window | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowStaysOnTopHint) { // FIXME do we want to support more than one connected HMD? It seems like a pretty corner case foreach(auto displayPlugin, PluginManager::getInstance()->getDisplayPlugins()) { @@ -171,14 +172,12 @@ void HMDToolsDialog::leaveHMDMode() { } void HMDToolsDialog::reject() { - // Just regularly close upon ESC - close(); + // We don't want this window to be closable from a close icon, just from our "Leave HMD Mode" button } void HMDToolsDialog::closeEvent(QCloseEvent* event) { - // TODO: consider if we want to prevent closing of this window with event->ignore(); - QDialog::closeEvent(event); - emit closed(); + // We don't want this window to be closable from a close icon, just from our "Leave HMD Mode" button + event->ignore(); } void HMDToolsDialog::centerCursorOnWidget(QWidget* widget) { From db95657455363a97db19c86da52bf783e14ba8bd Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 14 Aug 2015 16:40:53 -0700 Subject: [PATCH 33/49] PR comments --- interface/src/Application.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e7783c779e..ca7ee8d98c 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1262,8 +1262,7 @@ bool Application::event(QEvent* event) { if (!_keyboardFocusedItem.isInvalidID()) { switch (event->type()) { case QEvent::KeyPress: - case QEvent::KeyRelease: - { + case QEvent::KeyRelease: { auto entityScriptingInterface = DependencyManager::get(); auto entity = entityScriptingInterface->getEntityTree()->findEntityByID(_keyboardFocusedItem); RenderableWebEntityItem* webEntity = dynamic_cast(entity.get()); @@ -1274,7 +1273,10 @@ bool Application::event(QEvent* event) { return true; } } + break; } + + default: break; } } From d28c555a79c20365ce3cb37f8eea160a67102d4d Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Fri, 14 Aug 2015 16:47:34 -0700 Subject: [PATCH 34/49] Synchronize eye joint and debugging vectors when the other avatar is looking at me. --- interface/src/Application.cpp | 4 ++++ interface/src/avatar/Head.cpp | 16 ++++++++++++++- interface/src/avatar/SkeletonModel.cpp | 27 ++++++++++++++++++-------- libraries/animation/src/Rig.cpp | 9 +++++++-- libraries/animation/src/Rig.h | 2 ++ 5 files changed, 47 insertions(+), 11 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 2f6b038e40..1b8ba2d4cd 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2352,6 +2352,10 @@ void Application::updateMouseRay() { } } +// Called during Application::update immediately before AvatarManager::updateMyAvatar, updating my data that is then sent to everyone. +// (Maybe this code should be moved there?) +// The principal result is to call updateLookAtTargetAvatar() and then setLookAtPosition(). +// Note that it is called BEFORE we update position or joints based on sensors, etc. void Application::updateMyAvatarLookAtPosition() { PerformanceTimer perfTimer("lookAt"); bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index cec9079443..065b655628 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -344,6 +344,20 @@ glm::quat Head::getFinalOrientationInLocalFrame() const { return glm::quat(glm::radians(glm::vec3(getFinalPitch(), getFinalYaw(), getFinalRoll() ))); } +// Everyone else's head keeps track of a lookAtPosition that everybody sees the same, and refers to where that head +// is looking in model space -- e.g., at someone's eyeball, or between their eyes, or mouth, etc. Everyon's Interface +// will have the same value for the lookAtPosition of any given head. +// +// Everyone else's head also keeps track of a correctedLookAtPosition that may be different for the same head within +// different Interfaces. If that head is not looking at me, the correctedLookAtPosition is the same as the lookAtPosition. +// However, if that head is looking at me, then I will attempt to adjust the lookAtPosition by the difference between +// my (singular) eye position and my actual camera position. This adjustment is used on their eyeballs during rendering +// (and also on any lookAt vector display for that head, during rendering). Note that: +// 1. this adjustment can be made directly to the other head's eyeball joints, because we won't be send their joint information to others. +// 2. the corrected position is a separate ivar, so the common/uncorrected value is still available +// +// There is a pun here: The two lookAtPositions will always be the same for my own avatar in my own Interface, because I +// will not be looking at myself. (Even in a mirror, I will be looking at the camera.) glm::vec3 Head::getCorrectedLookAtPosition() { if (isLookingAtMe()) { return _correctedLookAtPosition; @@ -364,7 +378,7 @@ void Head::setCorrectedLookAtPosition(glm::vec3 correctedLookAtPosition) { bool Head::isLookingAtMe() { // Allow for outages such as may be encountered during avatar movement quint64 now = usecTimestampNow(); - const quint64 LOOKING_AT_ME_GAP_ALLOWED = 1000000; // microseconds + const quint64 LOOKING_AT_ME_GAP_ALLOWED = (5 * 1000.0f * 1000.0f) / 60.0f; // n frames, in microseconds return _isLookingAtMe || (now - _wasLastLookingAtMe) < LOOKING_AT_ME_GAP_ALLOWED; } diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index e870ba2140..e11e8996ca 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -97,7 +97,7 @@ void SkeletonModel::initJointStates(QVector states) { } const float PALM_PRIORITY = DEFAULT_PRIORITY; - +// Called within Model::simulate call, below. void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { if (_owningAvatar->isMyAvatar()) { _rig->computeMotionAnimationState(deltaTime, _owningAvatar->getPosition(), _owningAvatar->getVelocity(), _owningAvatar->getOrientation()); @@ -105,26 +105,37 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { Model::updateRig(deltaTime, parentTransform); if (_owningAvatar->isMyAvatar()) { const FBXGeometry& geometry = _geometry->getFBXGeometry(); + Head* head = _owningAvatar->getHead(); Rig::HeadParameters params; params.modelRotation = getRotation(); params.modelTranslation = getTranslation(); - params.leanSideways = _owningAvatar->getHead()->getFinalLeanSideways(); - params.leanForward = _owningAvatar->getHead()->getFinalLeanForward(); - params.torsoTwist = _owningAvatar->getHead()->getTorsoTwist(); - params.localHeadOrientation = _owningAvatar->getHead()->getFinalOrientationInLocalFrame(); - params.worldHeadOrientation = _owningAvatar->getHead()->getFinalOrientationInWorldFrame(); - params.eyeLookAt = _owningAvatar->getHead()->getLookAtPosition(); - params.eyeSaccade = _owningAvatar->getHead()->getSaccade(); + params.leanSideways = head->getFinalLeanSideways(); + params.leanForward = head->getFinalLeanForward(); + params.torsoTwist = head->getTorsoTwist(); + params.localHeadOrientation = head->getFinalOrientationInLocalFrame(); + params.worldHeadOrientation = head->getFinalOrientationInWorldFrame(); + params.eyeLookAt = head->getLookAtPosition(); + params.eyeSaccade = head->getSaccade(); params.leanJointIndex = geometry.leanJointIndex; params.neckJointIndex = geometry.neckJointIndex; params.leftEyeJointIndex = geometry.leftEyeJointIndex; params.rightEyeJointIndex = geometry.rightEyeJointIndex; _rig->updateFromHeadParameters(params); + } else if (true) { //(_owningAvatar->getHead()->isLookingAtMe()) { + // Other avatars joint, including their eyes, will already be set just like any other joints + // from the wire data. But when looking at me, we want the eyes to use the corrected lookAt. + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + Head* head = _owningAvatar->getHead(); + _rig->updateEyeJoints(geometry.leftEyeJointIndex, geometry.rightEyeJointIndex, + getTranslation(), getRotation(), + head->getFinalOrientationInWorldFrame(), head->getCorrectedLookAtPosition()); } } +// Called by Avatar::simulate after it has set the joint states (fullUpdate true if changed), +// but just before head has been simulated. void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { setTranslation(_owningAvatar->getSkeletonPosition()); static const glm::quat refOrientation = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)); diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 1a8bd0bbad..2212e06e31 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -786,8 +786,8 @@ glm::quat Rig::getJointDefaultRotationInParentFrame(int jointIndex) { void Rig::updateFromHeadParameters(const HeadParameters& params) { updateLeanJoint(params.leanJointIndex, params.leanSideways, params.leanForward, params.torsoTwist); updateNeckJoint(params.neckJointIndex, params.localHeadOrientation, params.leanSideways, params.leanForward, params.torsoTwist); - updateEyeJoint(params.leftEyeJointIndex, params.modelTranslation, params.modelRotation, params.worldHeadOrientation, params.eyeLookAt, params.eyeSaccade); - updateEyeJoint(params.rightEyeJointIndex, params.modelTranslation, params.modelRotation, params.worldHeadOrientation, params.eyeLookAt, params.eyeSaccade); + updateEyeJoints(params.leftEyeJointIndex, params.rightEyeJointIndex, params.modelTranslation, params.modelRotation, + params.worldHeadOrientation, params.eyeLookAt, params.eyeSaccade); } void Rig::updateLeanJoint(int index, float leanSideways, float leanForward, float torsoTwist) { @@ -828,6 +828,11 @@ void Rig::updateNeckJoint(int index, const glm::quat& localHeadOrientation, floa } } +void Rig::updateEyeJoints(int leftEyeIndex, int rightEyeIndex, const glm::vec3& modelTranslation, const glm::quat& modelRotation, + const glm::quat& worldHeadOrientation, const glm::vec3& lookAtSpot, const glm::vec3& saccade) { + updateEyeJoint(leftEyeIndex, modelTranslation, modelRotation, worldHeadOrientation, lookAtSpot, saccade); + updateEyeJoint(rightEyeIndex, modelTranslation, modelRotation, worldHeadOrientation, lookAtSpot, saccade); +} void Rig::updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm::quat& modelRotation, const glm::quat& worldHeadOrientation, const glm::vec3& lookAtSpot, const glm::vec3& saccade) { if (index >= 0 && _jointStates[index].getParentIndex() >= 0) { auto& state = _jointStates[index]; diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index face79bc15..8da20062cf 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -157,6 +157,8 @@ public: void setEnableRig(bool isEnabled) { _enableRig = isEnabled; } void updateFromHeadParameters(const HeadParameters& params); + void updateEyeJoints(int leftEyeIndex, int rightEyeIndex, const glm::vec3& modelTranslation, const glm::quat& modelRotation, + const glm::quat& worldHeadOrientation, const glm::vec3& lookAtSpot, const glm::vec3& saccade = glm::vec3(0.0f)); virtual void setHandPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, float scale, float priority) = 0; From 8609129228b3ad1855cf7d27c19e98f3311501f4 Mon Sep 17 00:00:00 2001 From: bwent Date: Fri, 14 Aug 2015 17:07:01 -0700 Subject: [PATCH 35/49] fix header --- examples/example/solarsystem.js | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/examples/example/solarsystem.js b/examples/example/solarsystem.js index 0a16bf52f7..7b9967b001 100644 --- a/examples/example/solarsystem.js +++ b/examples/example/solarsystem.js @@ -1,17 +1,11 @@ // // solarsystem.js -// games +// example // // Created by Bridget Went, 5/28/15. // Copyright 2015 High Fidelity, Inc. // -// The start to a project to build a virtual physics classroom to simulate the solar system, gravity, and orbital physics. -// - A sun with oribiting planets is created in front of the user -// - UI elements allow for adjusting the period, gravity, trails, and energy recalculations -// - Click "PAUSE" to pause the animation and show planet labels -// - In this mode, double-click a planet label to zoom in on that planet -// -Double-clicking on earth label initiates satellite orbiter game -// -Press "TAB" to toggle back to solar system view +// A project to build a virtual physics classroom to simulate the solar system, gravity, and orbital physics. // // // Distributed under the Apache License, Version 2.0. @@ -500,4 +494,4 @@ CreateSimulation = function() { CreateSimulation(); Script.update.connect(update); -Script.scriptEnding.connect(scriptEnding); \ No newline at end of file +Script.scriptEnding.connect(scriptEnding); From dc2ddd0303a1f9ab8c6ada32bd86a0a3a9eae7d4 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 14 Aug 2015 17:12:00 -0700 Subject: [PATCH 36/49] switch statement spacing --- interface/src/Application.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index ca7ee8d98c..b8c9b78cc5 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1263,18 +1263,18 @@ bool Application::event(QEvent* event) { switch (event->type()) { case QEvent::KeyPress: case QEvent::KeyRelease: { - auto entityScriptingInterface = DependencyManager::get(); - auto entity = entityScriptingInterface->getEntityTree()->findEntityByID(_keyboardFocusedItem); - RenderableWebEntityItem* webEntity = dynamic_cast(entity.get()); - if (webEntity && webEntity->getEventHandler()) { - event->setAccepted(false); - QCoreApplication::sendEvent(webEntity->getEventHandler(), event); - if (event->isAccepted()) { - return true; - } + auto entityScriptingInterface = DependencyManager::get(); + auto entity = entityScriptingInterface->getEntityTree()->findEntityByID(_keyboardFocusedItem); + RenderableWebEntityItem* webEntity = dynamic_cast(entity.get()); + if (webEntity && webEntity->getEventHandler()) { + event->setAccepted(false); + QCoreApplication::sendEvent(webEntity->getEventHandler(), event); + if (event->isAccepted()) { + return true; } - break; } + break; + } default: break; From eeeaa8050d75f6fd796832e25b3150ddb435da2d Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Fri, 14 Aug 2015 18:58:49 -0700 Subject: [PATCH 37/49] Fix the rush jobs, so gustavo can build build for me... --- interface/src/avatar/Head.cpp | 2 +- interface/src/avatar/SkeletonModel.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index 065b655628..9ab2c83a79 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -378,7 +378,7 @@ void Head::setCorrectedLookAtPosition(glm::vec3 correctedLookAtPosition) { bool Head::isLookingAtMe() { // Allow for outages such as may be encountered during avatar movement quint64 now = usecTimestampNow(); - const quint64 LOOKING_AT_ME_GAP_ALLOWED = (5 * 1000.0f * 1000.0f) / 60.0f; // n frames, in microseconds + const quint64 LOOKING_AT_ME_GAP_ALLOWED = (5 * 1000 * 1000) / 60; // n frames, in microseconds return _isLookingAtMe || (now - _wasLastLookingAtMe) < LOOKING_AT_ME_GAP_ALLOWED; } diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index e11e8996ca..831fb26abb 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -123,7 +123,7 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { params.rightEyeJointIndex = geometry.rightEyeJointIndex; _rig->updateFromHeadParameters(params); - } else if (true) { //(_owningAvatar->getHead()->isLookingAtMe()) { + } else if (_owningAvatar->getHead()->isLookingAtMe()) { // Other avatars joint, including their eyes, will already be set just like any other joints // from the wire data. But when looking at me, we want the eyes to use the corrected lookAt. const FBXGeometry& geometry = _geometry->getFBXGeometry(); From 82310bb4994a6f5eb19e55780ee310d030710df7 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 13 Aug 2015 15:40:45 -0700 Subject: [PATCH 38/49] Attempting to fix rendering issues on 2015 Macs --- libraries/render-utils/src/OffscreenGlCanvas.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/libraries/render-utils/src/OffscreenGlCanvas.cpp b/libraries/render-utils/src/OffscreenGlCanvas.cpp index 246537dd0e..07256e7731 100644 --- a/libraries/render-utils/src/OffscreenGlCanvas.cpp +++ b/libraries/render-utils/src/OffscreenGlCanvas.cpp @@ -34,11 +34,9 @@ OffscreenGlCanvas::~OffscreenGlCanvas() { void OffscreenGlCanvas::create(QOpenGLContext* sharedContext) { if (nullptr != sharedContext) { sharedContext->doneCurrent(); - _context->setFormat(sharedContext->format()); _context->setShareContext(sharedContext); - } else { - _context->setFormat(getDefaultOpenGlSurfaceFormat()); } + _context->setFormat(getDefaultOpenGlSurfaceFormat()); _context->create(); _offscreenSurface->setFormat(_context->format()); From cd58f012a35d5ad3c7bc5e72cd3f61d58ff0e0a5 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Sat, 15 Aug 2015 09:42:05 -0700 Subject: [PATCH 39/49] Fix for ATI discrete graphics on 2015 macbook pro / mac pro. Fixed incorrect size for camera transform uniforms passed via glBindBufferRange. This is a legitimate bug on our side. I guess Intel and nVidia drivers just happen to ignore this field. This was likely causing many of the shader TransformCamera members to be identity or un-initialized, specifically, the projection matrix values. This was resulting in a black screen on ATI gpus. --- libraries/gpu/src/gpu/GLBackendTransform.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/gpu/src/gpu/GLBackendTransform.cpp b/libraries/gpu/src/gpu/GLBackendTransform.cpp index a9f30fd0fa..23b3dba14e 100755 --- a/libraries/gpu/src/gpu/GLBackendTransform.cpp +++ b/libraries/gpu/src/gpu/GLBackendTransform.cpp @@ -133,7 +133,7 @@ void GLBackend::updateTransform() { if (offset >= 0) { glBindBufferRange(GL_UNIFORM_BUFFER, TRANSFORM_CAMERA_SLOT, _transform._transformCameraBuffer, - offset, sizeof(Backend::TransformObject)); + offset, sizeof(Backend::TransformCamera)); } (void)CHECK_GL_ERROR(); From db4d98ccb5167503c5ecf0bb30a0975d04330ea5 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Sun, 16 Aug 2015 23:21:45 -0700 Subject: [PATCH 40/49] Polyvox shader fix --- libraries/entities-renderer/src/polyvox.slf | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/entities-renderer/src/polyvox.slf b/libraries/entities-renderer/src/polyvox.slf index e5c8a49133..5772a93f7d 100644 --- a/libraries/entities-renderer/src/polyvox.slf +++ b/libraries/entities-renderer/src/polyvox.slf @@ -36,9 +36,9 @@ void main(void) { float inPositionY = (_inPosition.y - 0.5) / voxelVolumeSize.y; float inPositionZ = (_inPosition.z - 0.5) / voxelVolumeSize.z; - vec4 xyDiffuse = texture2D(xMap, vec2(-inPositionX, -inPositionY)); - vec4 xzDiffuse = texture2D(yMap, vec2(-inPositionX, inPositionZ)); - vec4 yzDiffuse = texture2D(zMap, vec2(inPositionZ, -inPositionY)); + vec4 xyDiffuse = texture(xMap, vec2(-inPositionX, -inPositionY)); + vec4 xzDiffuse = texture(yMap, vec2(-inPositionX, inPositionZ)); + vec4 yzDiffuse = texture(zMap, vec2(inPositionZ, -inPositionY)); vec3 xyDiffuseScaled = xyDiffuse.rgb * abs(worldNormal.z); vec3 xzDiffuseScaled = xzDiffuse.rgb * abs(worldNormal.y); From a61d9501c86a328a0e146c20eda0a899a121d4ab Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 17 Aug 2015 02:04:23 -0700 Subject: [PATCH 41/49] Add polyvox to shader tests and clean up test code --- tests/shaders/src/main.cpp | 111 ++++--------------------------------- 1 file changed, 10 insertions(+), 101 deletions(-) diff --git a/tests/shaders/src/main.cpp b/tests/shaders/src/main.cpp index dde7419264..8d12663935 100644 --- a/tests/shaders/src/main.cpp +++ b/tests/shaders/src/main.cpp @@ -20,6 +20,8 @@ #include #include +#include + #include "../model/Skybox_vert.h" #include "../model/Skybox_frag.h" @@ -112,85 +114,22 @@ #include "paintStroke_vert.h" #include "paintStroke_frag.h" -class RateCounter { - std::vector times; - QElapsedTimer timer; -public: - RateCounter() { - timer.start(); - } - - void reset() { - times.clear(); - } - - unsigned int count() const { - return times.size() - 1; - } - - float elapsed() const { - if (times.size() < 1) { - return 0.0f; - } - float elapsed = *times.rbegin() - *times.begin(); - return elapsed; - } - - void increment() { - times.push_back(timer.elapsed() / 1000.0f); - } - - float rate() const { - if (elapsed() == 0.0f) { - return 0.0f; - } - return (float) count() / elapsed(); - } -}; - - -const QString& getQmlDir() { - static QString dir; - if (dir.isEmpty()) { - QDir path(__FILE__); - path.cdUp(); - dir = path.cleanPath(path.absoluteFilePath("../../../interface/resources/qml/")) + "/"; - qDebug() << "Qml Path: " << dir; - } - return dir; -} +#include "polyvox_vert.h" +#include "polyvox_frag.h" // Create a simple OpenGL window that renders text in various ways class QTestWindow : public QWindow { Q_OBJECT - QOpenGLContext* _context{ nullptr }; - QSize _size; - //TextRenderer* _textRenderer[4]; - RateCounter fps; protected: void renderText(); -private: - void resizeWindow(const QSize& size) { - _size = size; - } - public: QTestWindow() { setSurfaceType(QSurface::OpenGLSurface); - - QSurfaceFormat format; - // Qt Quick may need a depth and stencil buffer. Always make sure these are available. - format.setDepthBufferSize(16); - format.setStencilBufferSize(8); - format.setVersion(4, 1); - format.setProfile(QSurfaceFormat::OpenGLContextProfile::CoreProfile); - format.setOption(QSurfaceFormat::DebugContext); - + QSurfaceFormat format = getDefaultOpenGlSurfaceFormat(); setFormat(format); - _context = new QOpenGLContext; _context->setFormat(format); _context->create(); @@ -199,8 +138,6 @@ public: makeCurrent(); gpu::Context::init(); - - { QOpenGLDebugLogger* logger = new QOpenGLDebugLogger(this); logger->initialize(); // initializes in the current context, i.e. ctx @@ -208,25 +145,8 @@ public: connect(logger, &QOpenGLDebugLogger::messageLogged, this, [&](const QOpenGLDebugMessage & debugMessage) { qDebug() << debugMessage; }); - // logger->startLogging(QOpenGLDebugLogger::SynchronousLogging); } - qDebug() << (const char*)glGetString(GL_VERSION); - - //_textRenderer[0] = TextRenderer::getInstance(SANS_FONT_FAMILY, 12, false); - //_textRenderer[1] = TextRenderer::getInstance(SERIF_FONT_FAMILY, 12, false, - // TextRenderer::SHADOW_EFFECT); - //_textRenderer[2] = TextRenderer::getInstance(MONO_FONT_FAMILY, 48, -1, - // false, TextRenderer::OUTLINE_EFFECT); - //_textRenderer[3] = TextRenderer::getInstance(INCONSOLATA_FONT_FAMILY, 24); - - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glClearColor(0.2f, 0.2f, 0.2f, 1); - glDisable(GL_DEPTH_TEST); - makeCurrent(); - -// setFramePosition(QPoint(-1000, 0)); resize(QSize(800, 600)); } @@ -237,18 +157,15 @@ public: void makeCurrent() { _context->makeCurrent(this); } - -protected: - void resizeEvent(QResizeEvent* ev) override { - resizeWindow(ev->size()); - } }; void testShaderBuild(const char* vs_src, const char * fs_src) { auto vs = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(vs_src))); auto fs = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(fs_src))); auto pr = gpu::ShaderPointer(gpu::Shader::createProgram(vs, fs)); - gpu::Shader::makeProgram(*pr); + if (!gpu::Shader::makeProgram(*pr)) { + throw std::runtime_error("Failed to compile shader"); + } } void QTestWindow::draw() { @@ -257,8 +174,8 @@ void QTestWindow::draw() { } makeCurrent(); + glClearColor(0.2f, 0.2f, 0.2f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - glViewport(0, 0, _size.width() * devicePixelRatio(), _size.height() * devicePixelRatio()); static std::once_flag once; std::call_once(once, [&]{ @@ -328,17 +245,10 @@ void QTestWindow::draw() { testShaderBuild(Skybox_vert, Skybox_frag); testShaderBuild(paintStroke_vert,paintStroke_frag); + testShaderBuild(polyvox_vert, polyvox_frag); }); - _context->swapBuffers(this); - glFinish(); - - fps.increment(); - if (fps.elapsed() >= 2.0f) { - qDebug() << "FPS: " << fps.rate(); - fps.reset(); - } } void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) { @@ -352,7 +262,6 @@ void messageHandler(QtMsgType type, const QMessageLogContext& context, const QSt } } - const char * LOG_FILTER_RULES = R"V0G0N( hifi.gpu=true )V0G0N"; From 8cf450cb674d1ea0a7b44fb9417b552f108be9a6 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 17 Aug 2015 11:41:54 -0700 Subject: [PATCH 42/49] first cut at making focus click based --- interface/src/Application.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 979dc99453..a8c66c4689 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -674,9 +674,11 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : packetReceiver.registerListener(PacketType::DomainConnectionDenied, this, "handleDomainConnectionDeniedPacket"); auto entityScriptingInterface = DependencyManager::get(); - connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverEnterEntity, [this, entityScriptingInterface](const EntityItemID& entityItemID, const MouseEvent& event) { + connect(entityScriptingInterface.data(), &EntityScriptingInterface::clickDownOnEntity, + [this, entityScriptingInterface](const EntityItemID& entityItemID, const MouseEvent& event) { if (_keyboardFocusedItem != entityItemID) { _keyboardFocusedItem = UNKNOWN_ENTITY_ID; + qDebug() << "_keyboardFocusedItem:" << UNKNOWN_ENTITY_ID; auto properties = entityScriptingInterface->getEntityProperties(entityItemID); if (EntityTypes::Web == properties.getType() && !properties.getLocked()) { auto entity = entityScriptingInterface->getEntityTree()->findEntityByID(entityItemID); @@ -684,16 +686,21 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : if (webEntity) { webEntity->setProxyWindow(_window->windowHandle()); _keyboardFocusedItem = entityItemID; + qDebug() << "_keyboardFocusedItem:" << entityItemID; } } } }); + /* + // FIXME - need a solution for unfocusing on delayed time connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverLeaveEntity, [=](const EntityItemID& entityItemID, const MouseEvent& event) { if (_keyboardFocusedItem == entityItemID) { _keyboardFocusedItem = UNKNOWN_ENTITY_ID; + qDebug() << "_keyboardFocusedItem:" << UNKNOWN_ENTITY_ID; } }); + */ } void Application::aboutToQuit() { From 941b966dc745fa1c0175feb2a25c4ab4387aa18b Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Mon, 17 Aug 2015 11:57:08 -0700 Subject: [PATCH 43/49] Force eyeball update even when not looking at me. --- interface/src/avatar/SkeletonModel.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 831fb26abb..d58cd8d7c4 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -123,9 +123,15 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { params.rightEyeJointIndex = geometry.rightEyeJointIndex; _rig->updateFromHeadParameters(params); - } else if (_owningAvatar->getHead()->isLookingAtMe()) { - // Other avatars joint, including their eyes, will already be set just like any other joints + } else { + // This is a little more work than we really want. + // + // Other avatars joint, including their eyes, should already be set just like any other joints // from the wire data. But when looking at me, we want the eyes to use the corrected lookAt. + // + // Thus this should really only be ... else if (_owningAvatar->getHead()->isLookingAtMe()) {... + // However, in the !isLookingAtMe case, the eyes aren't rotating the way they should right now. + // (They latch their looking at me position.) We will revisit that as priorities allow. const FBXGeometry& geometry = _geometry->getFBXGeometry(); Head* head = _owningAvatar->getHead(); _rig->updateEyeJoints(geometry.leftEyeJointIndex, geometry.rightEyeJointIndex, From 296a6b1cb831f9a2559dc4ea8000999f6288c3e5 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 17 Aug 2015 12:20:46 -0700 Subject: [PATCH 44/49] re-enabled ctrlaltdavid's eye calibration code --- interface/src/devices/EyeTracker.cpp | 47 +++++++++++++--------------- 1 file changed, 22 insertions(+), 25 deletions(-) diff --git a/interface/src/devices/EyeTracker.cpp b/interface/src/devices/EyeTracker.cpp index 532c1d41f3..b7a0a8c299 100644 --- a/interface/src/devices/EyeTracker.cpp +++ b/interface/src/devices/EyeTracker.cpp @@ -153,21 +153,20 @@ void EyeTracker::onStreamStarted() { qCDebug(interfaceapp) << "Eye Tracker: Started streaming"; } - // TODO: Re-enable once saving / loading calibrations is working - //if (_isStreaming) { - // // Automatically load calibration if one has been saved. - // QString availableCalibrations = QString(smi_getAvailableCalibrations()); - // if (availableCalibrations.contains(HIGH_FIDELITY_EYE_TRACKER_CALIBRATION)) { - // result = smi_loadCalibration(HIGH_FIDELITY_EYE_TRACKER_CALIBRATION); - // if (result != SMI_RET_SUCCESS) { - // qCWarning(interfaceapp) << "Eye Tracker: Error loading calibration:" << smiReturnValueToString(result); - // QMessageBox::warning(nullptr, "Eye Tracker Error", "Error loading calibration" - // + smiReturnValueToString(result)); - // } else { - // qCDebug(interfaceapp) << "Eye Tracker: Loaded calibration"; - // } - // } - //} + if (_isStreaming) { + // Automatically load calibration if one has been saved. + QString availableCalibrations = QString(smi_getAvailableCalibrations()); + if (availableCalibrations.contains(HIGH_FIDELITY_EYE_TRACKER_CALIBRATION)) { + result = smi_loadCalibration(HIGH_FIDELITY_EYE_TRACKER_CALIBRATION); + if (result != SMI_RET_SUCCESS) { + qCWarning(interfaceapp) << "Eye Tracker: Error loading calibration:" << smiReturnValueToString(result); + QMessageBox::warning(nullptr, "Eye Tracker Error", "Error loading calibration" + + smiReturnValueToString(result)); + } else { + qCDebug(interfaceapp) << "Eye Tracker: Loaded calibration"; + } + } + } } #endif @@ -260,11 +259,10 @@ void EyeTracker::calibrate(int points) { if (result != SMI_RET_SUCCESS) { qCWarning(interfaceapp) << "Eye Tracker: Error performing calibration:" << smiReturnValueToString(result); } else { - // TODO: Re - enable once saving / loading calibrations is working - //result = smi_saveCalibration(HIGH_FIDELITY_EYE_TRACKER_CALIBRATION); - //if (result != SMI_RET_SUCCESS) { - // qCWarning(interfaceapp) << "Eye Tracker: Error saving calibration:" << smiReturnValueToString(result); - //} + result = smi_saveCalibration(HIGH_FIDELITY_EYE_TRACKER_CALIBRATION); + if (result != SMI_RET_SUCCESS) { + qCWarning(interfaceapp) << "Eye Tracker: Error saving calibration:" << smiReturnValueToString(result); + } } } @@ -292,11 +290,10 @@ QString EyeTracker::smiReturnValueToString(int value) { return "Eye cameras not available"; case smi_ErrorReturnValue::SMI_ERROR_OCULUS_RUNTIME_NOT_SUPPORTED: return "Oculus runtime not supported"; - // TODO: Re-enable once saving / loading calibrations is working - //case smi_ErrorReturnValue::SMI_ERROR_FILE_NOT_FOUND: - // return "File not found"; - //case smi_ErrorReturnValue::SMI_ERROR_FILE_EMPTY: - // return "File empty"; + case smi_ErrorReturnValue::SMI_ERROR_FILE_NOT_FOUND: + return "File not found"; + case smi_ErrorReturnValue::SMI_ERROR_FILE_EMPTY: + return "File empty"; case smi_ErrorReturnValue::SMI_ERROR_UNKNOWN: return "Unknown error"; default: From b7d9dc444bdf90aaf2e21b2002b5f2a8d7749b7c Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 17 Aug 2015 13:06:32 -0700 Subject: [PATCH 45/49] more work on clicking focus --- interface/src/Application.cpp | 30 ++++++++++++------- interface/src/Application.h | 1 + .../src/EntityTreeRenderer.cpp | 2 ++ .../src/EntityTreeRenderer.h | 1 + .../src/RenderableWebEntityItem.cpp | 9 ++++++ 5 files changed, 33 insertions(+), 10 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index a8c66c4689..5ae0872761 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -673,12 +673,13 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : auto& packetReceiver = nodeList->getPacketReceiver(); packetReceiver.registerListener(PacketType::DomainConnectionDenied, this, "handleDomainConnectionDeniedPacket"); + // If the user clicks an an entity, we will check that it's an unlocked web entity, and if so, set the focus to it auto entityScriptingInterface = DependencyManager::get(); connect(entityScriptingInterface.data(), &EntityScriptingInterface::clickDownOnEntity, [this, entityScriptingInterface](const EntityItemID& entityItemID, const MouseEvent& event) { if (_keyboardFocusedItem != entityItemID) { _keyboardFocusedItem = UNKNOWN_ENTITY_ID; - qDebug() << "_keyboardFocusedItem:" << UNKNOWN_ENTITY_ID; + qDebug() << "clickDownOnEntity.... _keyboardFocusedItem:" << UNKNOWN_ENTITY_ID; auto properties = entityScriptingInterface->getEntityProperties(entityItemID); if (EntityTypes::Web == properties.getType() && !properties.getLocked()) { auto entity = entityScriptingInterface->getEntityTree()->findEntityByID(entityItemID); @@ -686,21 +687,19 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : if (webEntity) { webEntity->setProxyWindow(_window->windowHandle()); _keyboardFocusedItem = entityItemID; - qDebug() << "_keyboardFocusedItem:" << entityItemID; + _lastAcceptedKeyPress = usecTimestampNow(); + qDebug() << "clickDownOnEntity.... _keyboardFocusedItem:" << entityItemID; } } } }); - /* - // FIXME - need a solution for unfocusing on delayed time - connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverLeaveEntity, [=](const EntityItemID& entityItemID, const MouseEvent& event) { - if (_keyboardFocusedItem == entityItemID) { - _keyboardFocusedItem = UNKNOWN_ENTITY_ID; - qDebug() << "_keyboardFocusedItem:" << UNKNOWN_ENTITY_ID; - } + // If the user clicks somewhere where there is NO entity at all, we will release focus + connect(getEntities(), &EntityTreeRenderer::mousePressOffEntity, + [=](const RayToEntityIntersectionResult& entityItemID, const QMouseEvent* event, unsigned int deviceId) { + _keyboardFocusedItem = UNKNOWN_ENTITY_ID; + qDebug() << "mousePressOffEntity... _keyboardFocusedItem:" << UNKNOWN_ENTITY_ID; }); - */ } void Application::aboutToQuit() { @@ -1277,6 +1276,7 @@ bool Application::event(QEvent* event) { event->setAccepted(false); QCoreApplication::sendEvent(webEntity->getEventHandler(), event); if (event->isAccepted()) { + _lastAcceptedKeyPress = usecTimestampNow(); return true; } } @@ -1286,6 +1286,16 @@ bool Application::event(QEvent* event) { default: break; } + + const quint64 LOSE_FOCUS_AFTER_ELAPSED_TIME = 30 * USECS_PER_SECOND; // if idle for 30 seconds, drop focus + quint64 elapsedSinceAcceptedKeyPress = usecTimestampNow() - _lastAcceptedKeyPress; + if (elapsedSinceAcceptedKeyPress > LOSE_FOCUS_AFTER_ELAPSED_TIME) { + _keyboardFocusedItem = UNKNOWN_ENTITY_ID; + qDebug() << "idle for 30 seconds.... _keyboardFocusedItem:" << UNKNOWN_ENTITY_ID; + } else { + qDebug() << "elapsedSinceAcceptedKeyPress:" << elapsedSinceAcceptedKeyPress; + } + } switch (event->type()) { diff --git a/interface/src/Application.h b/interface/src/Application.h index cec1325baf..e0d4fa559d 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -680,6 +680,7 @@ private: DialogsManagerScriptingInterface* _dialogsManagerScriptingInterface = new DialogsManagerScriptingInterface(); EntityItemID _keyboardFocusedItem; + quint64 _lastAcceptedKeyPress = 0; }; #endif // hifi_Application_h diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 50f79e2ff3..72400dcefb 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -861,6 +861,8 @@ void EntityTreeRenderer::mousePressEvent(QMouseEvent* event, unsigned int device if (entityScript.property("clickDownOnEntity").isValid()) { entityScript.property("clickDownOnEntity").call(entityScript, entityScriptArgs); } + } else { + emit mousePressOffEntity(rayPickResult, event, deviceID); } _lastMouseEvent = MouseEvent(*event, deviceID); _lastMouseEventValid = true; diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h index 39f088f06d..25eb87b422 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.h +++ b/libraries/entities-renderer/src/EntityTreeRenderer.h @@ -95,6 +95,7 @@ public: signals: void mousePressOnEntity(const RayToEntityIntersectionResult& entityItemID, const QMouseEvent* event, unsigned int deviceId); + void mousePressOffEntity(const RayToEntityIntersectionResult& entityItemID, const QMouseEvent* event, unsigned int deviceId); void mouseMoveOnEntity(const RayToEntityIntersectionResult& entityItemID, const QMouseEvent* event, unsigned int deviceId); void mouseReleaseOnEntity(const RayToEntityIntersectionResult& entityItemID, const QMouseEvent* event, unsigned int deviceId); diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index 73aa6f2718..c8c5b10a61 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -56,6 +56,15 @@ RenderableWebEntityItem::~RenderableWebEntityItem() { } void RenderableWebEntityItem::render(RenderArgs* args) { + + // debug bounds on mac. + { + gpu::Batch& batch = *args->_batch; + batch.setModelTransform(getTransformToCenter()); // we want to include the scale as well + glm::vec4 cubeColor{ 1.0f, 0.0f, 0.0f, 1.0f}; + DependencyManager::get()->renderWireCube(batch, 1.0f, cubeColor); + } + QOpenGLContext * currentContext = QOpenGLContext::currentContext(); QSurface * currentSurface = currentContext->surface(); if (!_webSurface) { From 47b0e2fda6cb06f030972aa5e741544c93a07e7e Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 17 Aug 2015 14:02:24 -0700 Subject: [PATCH 46/49] more hacking on click to focus --- interface/src/Application.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 5ae0872761..874ffdb1b5 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1277,6 +1277,7 @@ bool Application::event(QEvent* event) { QCoreApplication::sendEvent(webEntity->getEventHandler(), event); if (event->isAccepted()) { _lastAcceptedKeyPress = usecTimestampNow(); + qDebug() << "Application::event() key event... _lastAcceptedKeyPress:" << _lastAcceptedKeyPress; return true; } } @@ -1287,6 +1288,7 @@ bool Application::event(QEvent* event) { break; } + /* const quint64 LOSE_FOCUS_AFTER_ELAPSED_TIME = 30 * USECS_PER_SECOND; // if idle for 30 seconds, drop focus quint64 elapsedSinceAcceptedKeyPress = usecTimestampNow() - _lastAcceptedKeyPress; if (elapsedSinceAcceptedKeyPress > LOSE_FOCUS_AFTER_ELAPSED_TIME) { @@ -1295,6 +1297,7 @@ bool Application::event(QEvent* event) { } else { qDebug() << "elapsedSinceAcceptedKeyPress:" << elapsedSinceAcceptedKeyPress; } + */ } @@ -2003,6 +2006,19 @@ void Application::idle() { return; // bail early, nothing to do here. } + if (!_keyboardFocusedItem.isInvalidID()) { + const quint64 LOSE_FOCUS_AFTER_ELAPSED_TIME = 30 * USECS_PER_SECOND; // if idle for 30 seconds, drop focus + quint64 elapsedSinceAcceptedKeyPress = usecTimestampNow() - _lastAcceptedKeyPress; + if (elapsedSinceAcceptedKeyPress > LOSE_FOCUS_AFTER_ELAPSED_TIME) { + _keyboardFocusedItem = UNKNOWN_ENTITY_ID; + qDebug() << "Application::idle() no key messages for 30 seconds.... _keyboardFocusedItem: UNKNOWN_ENTITY_ID:" << UNKNOWN_ENTITY_ID; + } else { + qDebug() << "Application::idle() _keyboardFocusedItem:" << _keyboardFocusedItem << "elapsedSinceAcceptedKeyPress:" << elapsedSinceAcceptedKeyPress; + } + } else { + qDebug() << "Application::idle() no focused item, who cares..."; + } + // Normally we check PipelineWarnings, but since idle will often take more than 10ms we only show these idle timing // details if we're in ExtraDebugging mode. However, the ::update() and its subcomponents will show their timing // details normally. From 49cf29ce8f19df962fc8e0369cccdb680b0be12e Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 17 Aug 2015 15:57:20 -0700 Subject: [PATCH 47/49] cleanup debugging and dead code --- interface/src/Application.cpp | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 874ffdb1b5..70b9157a21 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -679,7 +679,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : [this, entityScriptingInterface](const EntityItemID& entityItemID, const MouseEvent& event) { if (_keyboardFocusedItem != entityItemID) { _keyboardFocusedItem = UNKNOWN_ENTITY_ID; - qDebug() << "clickDownOnEntity.... _keyboardFocusedItem:" << UNKNOWN_ENTITY_ID; auto properties = entityScriptingInterface->getEntityProperties(entityItemID); if (EntityTypes::Web == properties.getType() && !properties.getLocked()) { auto entity = entityScriptingInterface->getEntityTree()->findEntityByID(entityItemID); @@ -688,7 +687,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : webEntity->setProxyWindow(_window->windowHandle()); _keyboardFocusedItem = entityItemID; _lastAcceptedKeyPress = usecTimestampNow(); - qDebug() << "clickDownOnEntity.... _keyboardFocusedItem:" << entityItemID; } } } @@ -698,7 +696,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : connect(getEntities(), &EntityTreeRenderer::mousePressOffEntity, [=](const RayToEntityIntersectionResult& entityItemID, const QMouseEvent* event, unsigned int deviceId) { _keyboardFocusedItem = UNKNOWN_ENTITY_ID; - qDebug() << "mousePressOffEntity... _keyboardFocusedItem:" << UNKNOWN_ENTITY_ID; }); } @@ -1277,7 +1274,6 @@ bool Application::event(QEvent* event) { QCoreApplication::sendEvent(webEntity->getEventHandler(), event); if (event->isAccepted()) { _lastAcceptedKeyPress = usecTimestampNow(); - qDebug() << "Application::event() key event... _lastAcceptedKeyPress:" << _lastAcceptedKeyPress; return true; } } @@ -1287,18 +1283,6 @@ bool Application::event(QEvent* event) { default: break; } - - /* - const quint64 LOSE_FOCUS_AFTER_ELAPSED_TIME = 30 * USECS_PER_SECOND; // if idle for 30 seconds, drop focus - quint64 elapsedSinceAcceptedKeyPress = usecTimestampNow() - _lastAcceptedKeyPress; - if (elapsedSinceAcceptedKeyPress > LOSE_FOCUS_AFTER_ELAPSED_TIME) { - _keyboardFocusedItem = UNKNOWN_ENTITY_ID; - qDebug() << "idle for 30 seconds.... _keyboardFocusedItem:" << UNKNOWN_ENTITY_ID; - } else { - qDebug() << "elapsedSinceAcceptedKeyPress:" << elapsedSinceAcceptedKeyPress; - } - */ - } switch (event->type()) { @@ -2006,17 +1990,13 @@ void Application::idle() { return; // bail early, nothing to do here. } + // Drop focus from _keyboardFocusedItem if no keyboard messages for 30 seconds if (!_keyboardFocusedItem.isInvalidID()) { const quint64 LOSE_FOCUS_AFTER_ELAPSED_TIME = 30 * USECS_PER_SECOND; // if idle for 30 seconds, drop focus quint64 elapsedSinceAcceptedKeyPress = usecTimestampNow() - _lastAcceptedKeyPress; if (elapsedSinceAcceptedKeyPress > LOSE_FOCUS_AFTER_ELAPSED_TIME) { _keyboardFocusedItem = UNKNOWN_ENTITY_ID; - qDebug() << "Application::idle() no key messages for 30 seconds.... _keyboardFocusedItem: UNKNOWN_ENTITY_ID:" << UNKNOWN_ENTITY_ID; - } else { - qDebug() << "Application::idle() _keyboardFocusedItem:" << _keyboardFocusedItem << "elapsedSinceAcceptedKeyPress:" << elapsedSinceAcceptedKeyPress; } - } else { - qDebug() << "Application::idle() no focused item, who cares..."; } // Normally we check PipelineWarnings, but since idle will often take more than 10ms we only show these idle timing From ae9ccb79ed699a884456dd9be81200f32967eaaa Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 17 Aug 2015 15:58:51 -0700 Subject: [PATCH 48/49] cleanup debugging and dead code --- libraries/entities-renderer/src/RenderableWebEntityItem.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index c8c5b10a61..84707aca3b 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -57,13 +57,14 @@ RenderableWebEntityItem::~RenderableWebEntityItem() { void RenderableWebEntityItem::render(RenderArgs* args) { - // debug bounds on mac. + #ifdef WANT_EXTRA_DEBUGGING { gpu::Batch& batch = *args->_batch; batch.setModelTransform(getTransformToCenter()); // we want to include the scale as well glm::vec4 cubeColor{ 1.0f, 0.0f, 0.0f, 1.0f}; DependencyManager::get()->renderWireCube(batch, 1.0f, cubeColor); } + #endif QOpenGLContext * currentContext = QOpenGLContext::currentContext(); QSurface * currentSurface = currentContext->surface(); From cb5f80bf789667ab2134008bceb93c7a91263371 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 17 Aug 2015 15:56:56 -0700 Subject: [PATCH 49/49] Add highlighting of keyboard focused items --- interface/src/Application.cpp | 35 ++++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 621210cc4e..449afae5d1 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -112,9 +112,7 @@ #include "InterfaceActionFactory.h" #include "avatar/AvatarManager.h" - #include "audio/AudioScope.h" - #include "devices/DdeFaceTracker.h" #include "devices/EyeTracker.h" #include "devices/Faceshift.h" @@ -148,6 +146,8 @@ #include "ui/AddressBarDialog.h" #include "ui/UpdateDialog.h" +#include "ui/overlays/Cube3DOverlay.h" + // ON WIndows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU // FIXME seems to be broken. #if defined(Q_OS_WIN) @@ -172,7 +172,6 @@ public: void call() { _fun(); } }; - using namespace std; // Starfield information @@ -299,6 +298,13 @@ bool setupEssentials(int& argc, char** argv) { return true; } +// FIXME move to header, or better yet, design some kind of UI manager +// to take care of highlighting keyboard focused items, rather than +// continuing to overburden Application.cpp +Cube3DOverlay* _keyboardFocusHighlight{ nullptr }; +int _keyboardFocusHighlightID{ -1 }; + + Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : QApplication(argc, argv), _dependencyManagerIsSetup(setupEssentials(argc, argv)), @@ -687,6 +693,23 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : webEntity->setProxyWindow(_window->windowHandle()); _keyboardFocusedItem = entityItemID; _lastAcceptedKeyPress = usecTimestampNow(); + if (_keyboardFocusHighlightID < 0 || !getOverlays().isAddedOverlay(_keyboardFocusHighlightID)) { + _keyboardFocusHighlight = new Cube3DOverlay(); + _keyboardFocusHighlight->setAlpha(1.0f); + _keyboardFocusHighlight->setBorderSize(1.0f); + _keyboardFocusHighlight->setColor({ 0xFF, 0xEF, 0x00 }); + _keyboardFocusHighlight->setIsSolid(false); + _keyboardFocusHighlight->setPulseMin(0.5); + _keyboardFocusHighlight->setPulseMax(1.0); + _keyboardFocusHighlight->setColorPulse(1.0); + _keyboardFocusHighlight->setIgnoreRayIntersection(true); + _keyboardFocusHighlight->setDrawInFront(true); + } + _keyboardFocusHighlight->setRotation(webEntity->getRotation()); + _keyboardFocusHighlight->setPosition(webEntity->getPosition()); + _keyboardFocusHighlight->setDimensions(webEntity->getDimensions() * 1.05f); + _keyboardFocusHighlight->setVisible(true); + _keyboardFocusHighlightID = getOverlays().addOverlay(_keyboardFocusHighlight); } } } @@ -696,6 +719,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : connect(getEntities(), &EntityTreeRenderer::mousePressOffEntity, [=](const RayToEntityIntersectionResult& entityItemID, const QMouseEvent* event, unsigned int deviceId) { _keyboardFocusedItem = UNKNOWN_ENTITY_ID; + _keyboardFocusHighlight->setVisible(false); }); } @@ -707,6 +731,11 @@ void Application::aboutToQuit() { } void Application::cleanupBeforeQuit() { + if (_keyboardFocusHighlightID > 0) { + getOverlays().deleteOverlay(_keyboardFocusHighlightID); + _keyboardFocusHighlightID = -1; + } + _keyboardFocusHighlight = nullptr; _entities.clear(); // this will allow entity scripts to properly shutdown