From e35c6be45eacf19201f8e3a048cf5d4cd6795235 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 20 Jan 2015 11:13:08 -0800 Subject: [PATCH 01/44] Lint: White space --- examples/notifications.js | 126 +++++++++++++++++++------------------- 1 file changed, 64 insertions(+), 62 deletions(-) diff --git a/examples/notifications.js b/examples/notifications.js index 9a6fbbce29..f6ff675cfb 100644 --- a/examples/notifications.js +++ b/examples/notifications.js @@ -58,12 +58,11 @@ // createNotification(welcome); // } - var width = 340.0; //width of notification overlay var height = 40.0; // height of a single line notification overlay var windowDimensions = Controller.getViewportDimensions(); // get the size of the interface window var overlayLocationX = (windowDimensions.x - (width + 20.0));// positions window 20px from the right of the interface window -var buttonLocationX = overlayLocationX + (width - 28.0); +var buttonLocationX = overlayLocationX + (width - 28.0); var locationY = 20.0; // position down from top of interface window var topMargin = 13.0; var leftMargin = 10.0; @@ -74,16 +73,16 @@ var fontSize = 12.0; var persistTime = 10.0; // time in seconds before notification fades var clickedText = false; var frame = 0; -var ourWidth = Window.innerWidth; -var ourHeight = Window.innerHeight; +var ourWidth = Window.innerWidth; +var ourHeight = Window.innerHeight; var text = "placeholder"; var last_users = GlobalServices.onlineUsers; var users = []; var ctrlIsPressed = false; var ready = true; - + // When our script shuts down, we should clean up all of our overlays -function scriptEnding() { +function scriptEnding() { for (i = 0; i < notifications.length; i++) { Overlays.deleteOverlay(notifications[i]); Overlays.deleteOverlay(buttons[i]); @@ -92,7 +91,7 @@ function scriptEnding() { Script.scriptEnding.connect(scriptEnding); var notifications = []; -var buttons = []; +var buttons = []; var times = []; var heights = []; var myAlpha = []; @@ -117,7 +116,7 @@ function createNotification(text) { height = height + extraLine; var overlayProperties = { x: overlayLocationX, - y: level, + y: level, width: width, height: height, color: textColor, @@ -126,10 +125,10 @@ function createNotification(text) { topMargin: topMargin, leftMargin: leftMargin, font: {size: fontSize}, - text: text, - }; + text: text, + }; var bLevel = level + 12.0; - var buttonProperties = { + var buttonProperties = { x: buttonLocationX, y: bLevel, width: 10.0, @@ -139,19 +138,17 @@ function createNotification(text) { color: { red: 255, green: 255, blue: 255}, visible: true, alpha: backgroundAlpha, - }; - - Notify(overlayProperties, buttonProperties, height); - + }; + + Notify(overlayProperties, buttonProperties, height); } // Pushes data to each array and sets up data for 2nd dimension array // to handle auxiliary data not carried by the overlay class // specifically notification "heights", "times" of creation, and . -function Notify(notice, button, height){ - +function Notify(notice, button, height) { notifications.push((Overlays.addOverlay("text", notice))); - buttons.push((Overlays.addOverlay("image",button))); + buttons.push((Overlays.addOverlay("image", button))); times.push(new Date().getTime() / 1000); height = height + 1.0; heights.push(height); @@ -165,11 +162,11 @@ function fadeIn(noticeIn, buttonIn) { var myLength = arrays.length; var q = 0; var pauseTimer = null; - pauseTimer = Script.setInterval(function() { + pauseTimer = Script.setInterval(function () { q++; qFade = q / 10.0; - Overlays.editOverlay(noticeIn, {alpha: qFade}); - Overlays.editOverlay(buttonIn, {alpha: qFade}); + Overlays.editOverlay(noticeIn, {alpha: qFade}); + Overlays.editOverlay(buttonIn, {alpha: qFade}); if (q >= 9.0) { Script.clearInterval(pauseTimer); } @@ -178,24 +175,25 @@ function fadeIn(noticeIn, buttonIn) { // push data from above to the 2 dimensional array -function createArrays(notice, button, createTime, height, myAlpha) { - arrays.push([notice, button, createTime, height, myAlpha]); +function createArrays(notice, button, createTime, height, myAlpha) { + arrays.push([notice, button, createTime, height, myAlpha]); } + // handles mouse clicks on buttons -function mousePressEvent(event) { +function mousePressEvent(event) { var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); //identify which overlay was clicked for (i = 0; i < buttons.length; i++) { //if user clicked a button - if(clickedOverlay == buttons[i]) { + if (clickedOverlay == buttons[i]) { Overlays.deleteOverlay(notifications[i]); Overlays.deleteOverlay(buttons[i]); notifications.splice(i, 1); buttons.splice(i, 1); times.splice(i, 1); heights.splice(i, 1); - myAlpha.splice(i, 1); + myAlpha.splice(i, 1); arrays.splice(i, 1); } - } + } } // Control key remains active only while key is held down @@ -204,20 +202,21 @@ function keyReleaseEvent(key) { ctrlIsPressed = false; } } - + // Triggers notification on specific key driven events function keyPressEvent(key) { if (key.key == 16777249) { ctrlIsPressed = true; } - if (key.text == "q") { //queries number of users online - var numUsers = GlobalServices.onlineUsers.length; - var welcome = "There are " + numUsers + " users online now."; - createNotification(welcome); - } - if (key.text == "s") { - if (ctrlIsPressed == true){ + if (key.text == "q") { //queries number of users online + var numUsers = GlobalServices.onlineUsers.length; + var welcome = "There are " + numUsers + " users online now."; + createNotification(welcome); + } + + if (key.text == "s") { + if (ctrlIsPressed == true) { var noteString = "Snapshot taken."; createNotification(noteString); } @@ -229,6 +228,7 @@ function wordWrap(str) { var result = stringDivider(str, 43.0, "\n"); createNotification(result); } + // wraps whole word to newline function stringDivider(str, slotWidth, spaceReplacer) { if (str.length > slotWidth) { @@ -241,15 +241,15 @@ function stringDivider(str, slotWidth, spaceReplacer) { return left + spaceReplacer + stringDivider(right, slotWidth, spaceReplacer); } } - return str; + return str; } // This fires a notification on window resize -function checkSize(){ - if((Window.innerWidth != ourWidth)||(Window.innerHeight != ourHeight)) { +function checkSize() { + if ((Window.innerWidth != ourWidth) || (Window.innerHeight != ourHeight)) { var windowResize = "Window has been resized"; ourWidth = Window.innerWidth; - ourHeight = Window.innerHeight; + ourHeight = Window.innerHeight; windowDimensions = Controller.getViewportDimensions(); overlayLocationX = (windowDimensions.x - (width + 60.0)); buttonLocationX = overlayLocationX + (width - 35.0); @@ -262,27 +262,30 @@ function onOnlineUsersChanged(users) { if (!isStartingUp()) { // Skip user notifications at startup. for (user in users) { if (last_users.indexOf(users[user]) == -1.0) { - createNotification(users[user] + " has joined"); + createNotification(users[user] + " has joined"); } } + for (user in last_users) { if (users.indexOf(last_users[user]) == -1.0) { - createNotification(last_users[user] + " has left"); + createNotification(last_users[user] + " has left"); } } } + last_users = users; } // Triggers notification if @MyUserName is mentioned in chat and returns the message to the notification. function onIncomingMessage(user, message) { - var myMessage = message; - var alertMe = "@" + GlobalServices.myUsername; - var thisAlert = user + ": " + myMessage; - if (myMessage.indexOf(alertMe) > -1.0) { + var myMessage = message; + var alertMe = "@" + GlobalServices.myUsername; + var thisAlert = user + ": " + myMessage; + if (myMessage.indexOf(alertMe) > -1.0) { wordWrap(thisAlert); } } + // Triggers mic mute notification function onMuteStateChanged() { var muteState = AudioDevice.getMuted() ? "muted" : "unmuted"; @@ -290,26 +293,26 @@ function onMuteStateChanged() { createNotification(muteString); } -function update(){ +function update() { frame++; if ((frame % 60.0) == 0) { // only update once a second checkSize(); // checks for size change to trigger windowResize notification locationY = 20.0; for (var i = 0; i < arrays.length; i++) { //repositions overlays as others fade - var nextOverlay = Overlays.getOverlayAtPoint({x: overlayLocationX, y: locationY}); - Overlays.editOverlay(notifications[i], { x:overlayLocationX, y:locationY}); - Overlays.editOverlay(buttons[i], { x:buttonLocationX, y:locationY + 12.0}); + var nextOverlay = Overlays.getOverlayAtPoint({ x: overlayLocationX, y: locationY }); + Overlays.editOverlay(notifications[i], { x: overlayLocationX, y: locationY}); + Overlays.editOverlay(buttons[i], { x: buttonLocationX, y: locationY + 12.0}); locationY = locationY + arrays[i][3]; } } -// This checks the age of the notification and prepares to fade it after 9.0 seconds (var persistTime - 1) - for (var i = 0; i < arrays.length; i++) { - if (ready){ - var j = arrays[i][2]; - var k = j + persistTime; + // This checks the age of the notification and prepares to fade it after 9.0 seconds (var persistTime - 1) + for (var i = 0; i < arrays.length; i++) { + if (ready) { + var j = arrays[i][2]; + var k = j + persistTime; if (k < (new Date().getTime() / 1000)) { - ready = false; + ready = false; noticeOut = arrays[i][0]; buttonOut = arrays[i][1]; var arraysOut = i; @@ -324,11 +327,11 @@ function fadeOut(noticeOut, buttonOut, arraysOut) { var myLength = arrays.length; var r = 9.0; var pauseTimer = null; - pauseTimer = Script.setInterval(function() { + pauseTimer = Script.setInterval(function () { r--; - rFade = r / 10.0; - Overlays.editOverlay(noticeOut, {alpha: rFade}); - Overlays.editOverlay(buttonOut, {alpha: rFade}); + rFade = r / 10.0; + Overlays.editOverlay(noticeOut, { alpha: rFade }); + Overlays.editOverlay(buttonOut, { alpha: rFade }); if (r < 0) { dismiss(noticeOut, buttonOut, arraysOut); arrays.splice(arraysOut, 1); @@ -339,7 +342,7 @@ function fadeOut(noticeOut, buttonOut, arraysOut) { } // This handles the final dismissal of a notification after fading -function dismiss(firstNoteOut, firstButOut, firstOut) { +function dismiss(firstNoteOut, firstButOut, firstOut) { var working = firstOut Overlays.deleteOverlay(firstNoteOut); Overlays.deleteOverlay(firstButOut); @@ -347,7 +350,7 @@ function dismiss(firstNoteOut, firstButOut, firstOut) { buttons.splice(firstOut, 1); times.splice(firstOut, 1); heights.splice(firstOut, 1); - myAlpha.splice(firstOut,1); + myAlpha.splice(firstOut, 1); } // This reports the number of users online at startup @@ -378,7 +381,6 @@ function isStartingUp() { return startingUp; } - AudioDevice.muteToggled.connect(onMuteStateChanged); Controller.keyPressEvent.connect(keyPressEvent); Controller.mousePressEvent.connect(mousePressEvent); From c4b024e51005c3dd064959489f7903a3aa3ed67e Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 20 Jan 2015 11:59:43 -0800 Subject: [PATCH 02/44] Lint: Variable declarations --- examples/notifications.js | 147 ++++++++++++++++++++++++-------------- 1 file changed, 95 insertions(+), 52 deletions(-) diff --git a/examples/notifications.js b/examples/notifications.js index f6ff675cfb..35dd09f7f7 100644 --- a/examples/notifications.js +++ b/examples/notifications.js @@ -52,16 +52,16 @@ // 2. Declare a text string. // 3. Call createNotifications(text) parsing the text. // example: +// var welcome; // if (key.text == "q") { //queries number of users online -// var numUsers = GlobalServices.onlineUsers.length; -// var welcome = "There are " + numUsers + " users online now."; -// createNotification(welcome); +// var welcome = "There are " + GlobalServices.onlineUsers.length + " users online now."; +// createNotification(welcome); // } var width = 340.0; //width of notification overlay var height = 40.0; // height of a single line notification overlay var windowDimensions = Controller.getViewportDimensions(); // get the size of the interface window -var overlayLocationX = (windowDimensions.x - (width + 20.0));// positions window 20px from the right of the interface window +var overlayLocationX = (windowDimensions.x - (width + 20.0)); // positions window 20px from the right of the interface window var buttonLocationX = overlayLocationX + (width - 28.0); var locationY = 20.0; // position down from top of interface window var topMargin = 13.0; @@ -80,16 +80,6 @@ var last_users = GlobalServices.onlineUsers; var users = []; var ctrlIsPressed = false; var ready = true; - -// When our script shuts down, we should clean up all of our overlays -function scriptEnding() { - for (i = 0; i < notifications.length; i++) { - Overlays.deleteOverlay(notifications[i]); - Overlays.deleteOverlay(buttons[i]); - } -} -Script.scriptEnding.connect(scriptEnding); - var notifications = []; var buttons = []; var times = []; @@ -97,24 +87,42 @@ var heights = []; var myAlpha = []; var arrays = []; +// When our script shuts down, we should clean up all of our overlays +function scriptEnding() { + var i; + + for (i = 0; i < notifications.length; i++) { + Overlays.deleteOverlay(notifications[i]); + Overlays.deleteOverlay(buttons[i]); + } +} +Script.scriptEnding.connect(scriptEnding); + // This function creates and sizes the overlays function createNotification(text) { - var count = (text.match(/\n/g) || []).length; - var breakPoint = 43.0; // length when new line is added - var extraLine = 0; - var breaks = 0; - var height = 40.0; - var stack = 0; + var count = (text.match(/\n/g) || []).length, + breakPoint = 43.0, // length when new line is added + extraLine = 0, + breaks = 0, + height = 40.0, + stack = 0, + level, + overlayProperties, + bLevel, + buttonProperties, + i; + if (text.length >= breakPoint) { breaks = count; } - var extraLine = breaks * 16.0; + extraLine = breaks * 16.0; for (i = 0; i < heights.length; i++) { stack = stack + heights[i]; } - var level = (stack + 20.0); + + level = (stack + 20.0); height = height + extraLine; - var overlayProperties = { + overlayProperties = { x: overlayLocationX, y: level, width: width, @@ -127,8 +135,9 @@ function createNotification(text) { font: {size: fontSize}, text: text, }; - var bLevel = level + 12.0; - var buttonProperties = { + + bLevel = level + 12.0; + buttonProperties = { x: buttonLocationX, y: bLevel, width: 10.0, @@ -159,9 +168,10 @@ function Notify(notice, button, height) { } function fadeIn(noticeIn, buttonIn) { - var myLength = arrays.length; - var q = 0; - var pauseTimer = null; + var q = 0, + qFade, + pauseTimer = null; + pauseTimer = Script.setInterval(function () { q++; qFade = q / 10.0; @@ -181,7 +191,11 @@ function createArrays(notice, button, createTime, height, myAlpha) { // handles mouse clicks on buttons function mousePressEvent(event) { - var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); //identify which overlay was clicked + var clickedOverlay, + i; + + clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); //identify which overlay was clicked + for (i = 0; i < buttons.length; i++) { //if user clicked a button if (clickedOverlay == buttons[i]) { Overlays.deleteOverlay(notifications[i]); @@ -205,19 +219,23 @@ function keyReleaseEvent(key) { // Triggers notification on specific key driven events function keyPressEvent(key) { + var numUsers, + welcome, + noteString; + if (key.key == 16777249) { ctrlIsPressed = true; } if (key.text == "q") { //queries number of users online - var numUsers = GlobalServices.onlineUsers.length; - var welcome = "There are " + numUsers + " users online now."; + numUsers = GlobalServices.onlineUsers.length; + welcome = "There are " + numUsers + " users online now."; createNotification(welcome); } if (key.text == "s") { if (ctrlIsPressed == true) { - var noteString = "Snapshot taken."; + noteString = "Snapshot taken."; createNotification(noteString); } } @@ -231,13 +249,17 @@ function wordWrap(str) { // wraps whole word to newline function stringDivider(str, slotWidth, spaceReplacer) { + var p, + left, + right; + if (str.length > slotWidth) { - var p = slotWidth; + p = slotWidth; for (; p > 0 && str[p] != ' '; p--) { } if (p > 0) { - var left = str.substring(0, p); - var right = str.substring(p + 1); + left = str.substring(0, p); + right = str.substring(p + 1); return left + spaceReplacer + stringDivider(right, slotWidth, spaceReplacer); } } @@ -259,6 +281,8 @@ function checkSize() { // Triggers notification if a user logs on or off function onOnlineUsersChanged(users) { + var user; + if (!isStartingUp()) { // Skip user notifications at startup. for (user in users) { if (last_users.indexOf(users[user]) == -1.0) { @@ -278,9 +302,14 @@ function onOnlineUsersChanged(users) { // Triggers notification if @MyUserName is mentioned in chat and returns the message to the notification. function onIncomingMessage(user, message) { - var myMessage = message; - var alertMe = "@" + GlobalServices.myUsername; - var thisAlert = user + ": " + myMessage; + var myMessage, + alertMe, + thisAlert; + + myMessage = message; + alertMe = "@" + GlobalServices.myUsername; + thisAlert = user + ": " + myMessage; + if (myMessage.indexOf(alertMe) > -1.0) { wordWrap(thisAlert); } @@ -288,18 +317,29 @@ function onIncomingMessage(user, message) { // Triggers mic mute notification function onMuteStateChanged() { - var muteState = AudioDevice.getMuted() ? "muted" : "unmuted"; - var muteString = "Microphone is now " + muteState; + var muteState, + muteString; + + muteState = AudioDevice.getMuted() ? "muted" : "unmuted"; + muteString = "Microphone is now " + muteState; createNotification(muteString); } function update() { + var nextOverlay, + noticeOut, + buttonOut, + arraysOut, + i, + j, + k; + frame++; if ((frame % 60.0) == 0) { // only update once a second checkSize(); // checks for size change to trigger windowResize notification locationY = 20.0; - for (var i = 0; i < arrays.length; i++) { //repositions overlays as others fade - var nextOverlay = Overlays.getOverlayAtPoint({ x: overlayLocationX, y: locationY }); + for (i = 0; i < arrays.length; i++) { //repositions overlays as others fade + nextOverlay = Overlays.getOverlayAtPoint({ x: overlayLocationX, y: locationY }); Overlays.editOverlay(notifications[i], { x: overlayLocationX, y: locationY}); Overlays.editOverlay(buttons[i], { x: buttonLocationX, y: locationY + 12.0}); locationY = locationY + arrays[i][3]; @@ -307,15 +347,15 @@ function update() { } // This checks the age of the notification and prepares to fade it after 9.0 seconds (var persistTime - 1) - for (var i = 0; i < arrays.length; i++) { + for (i = 0; i < arrays.length; i++) { if (ready) { - var j = arrays[i][2]; - var k = j + persistTime; + j = arrays[i][2]; + k = j + persistTime; if (k < (new Date().getTime() / 1000)) { ready = false; noticeOut = arrays[i][0]; buttonOut = arrays[i][1]; - var arraysOut = i; + arraysOut = i; fadeOut(noticeOut, buttonOut, arraysOut); } } @@ -324,9 +364,10 @@ function update() { // this fades the notification ready for dismissal, and removes it from the arrays function fadeOut(noticeOut, buttonOut, arraysOut) { - var myLength = arrays.length; - var r = 9.0; - var pauseTimer = null; + var r = 9.0, + rFade, + pauseTimer = null; + pauseTimer = Script.setInterval(function () { r--; rFade = r / 10.0; @@ -343,7 +384,6 @@ function fadeOut(noticeOut, buttonOut, arraysOut) { // This handles the final dismissal of a notification after fading function dismiss(firstNoteOut, firstButOut, firstOut) { - var working = firstOut Overlays.deleteOverlay(firstNoteOut); Overlays.deleteOverlay(firstButOut); notifications.splice(firstOut, 1); @@ -355,8 +395,11 @@ function dismiss(firstNoteOut, firstButOut, firstOut) { // This reports the number of users online at startup function reportUsers() { - var numUsers = GlobalServices.onlineUsers.length; - var welcome = "Welcome! There are " + numUsers + " users online now."; + var numUsers, + welcome; + + numUsers = GlobalServices.onlineUsers.length; + welcome = "Welcome! There are " + numUsers + " users online now."; createNotification(welcome); } From ce0c91628a5e788032d88f1c47965378a73599b0 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 20 Jan 2015 12:15:27 -0800 Subject: [PATCH 03/44] Lint: Declaration order --- examples/notifications.js | 377 +++++++++++++++++++------------------- 1 file changed, 187 insertions(+), 190 deletions(-) diff --git a/examples/notifications.js b/examples/notifications.js index 35dd09f7f7..6e73e67233 100644 --- a/examples/notifications.js +++ b/examples/notifications.js @@ -87,16 +87,72 @@ var heights = []; var myAlpha = []; var arrays = []; -// When our script shuts down, we should clean up all of our overlays -function scriptEnding() { - var i; - - for (i = 0; i < notifications.length; i++) { - Overlays.deleteOverlay(notifications[i]); - Overlays.deleteOverlay(buttons[i]); - } +// push data from above to the 2 dimensional array +function createArrays(notice, button, createTime, height, myAlpha) { + arrays.push([notice, button, createTime, height, myAlpha]); +} + +// This handles the final dismissal of a notification after fading +function dismiss(firstNoteOut, firstButOut, firstOut) { + Overlays.deleteOverlay(firstNoteOut); + Overlays.deleteOverlay(firstButOut); + notifications.splice(firstOut, 1); + buttons.splice(firstOut, 1); + times.splice(firstOut, 1); + heights.splice(firstOut, 1); + myAlpha.splice(firstOut, 1); +} + +function fadeIn(noticeIn, buttonIn) { + var q = 0, + qFade, + pauseTimer = null; + + pauseTimer = Script.setInterval(function () { + q++; + qFade = q / 10.0; + Overlays.editOverlay(noticeIn, { alpha: qFade }); + Overlays.editOverlay(buttonIn, { alpha: qFade }); + if (q >= 9.0) { + Script.clearInterval(pauseTimer); + } + }, 10); +} + +// this fades the notification ready for dismissal, and removes it from the arrays +function fadeOut(noticeOut, buttonOut, arraysOut) { + var r = 9.0, + rFade, + pauseTimer = null; + + pauseTimer = Script.setInterval(function () { + r--; + rFade = r / 10.0; + Overlays.editOverlay(noticeOut, { alpha: rFade }); + Overlays.editOverlay(buttonOut, { alpha: rFade }); + if (r < 0) { + dismiss(noticeOut, buttonOut, arraysOut); + arrays.splice(arraysOut, 1); + ready = true; + Script.clearInterval(pauseTimer); + } + }, 20); +} + +// Pushes data to each array and sets up data for 2nd dimension array +// to handle auxiliary data not carried by the overlay class +// specifically notification "heights", "times" of creation, and . +function Notify(notice, button, height) { + notifications.push((Overlays.addOverlay("text", notice))); + buttons.push((Overlays.addOverlay("image", button))); + times.push(new Date().getTime() / 1000); + height = height + 1.0; + heights.push(height); + myAlpha.push(0); + var last = notifications.length - 1; + createArrays(notifications[last], buttons[last], times[last], heights[last], myAlpha[last]); + fadeIn(notifications[last], buttons[last]) } -Script.scriptEnding.connect(scriptEnding); // This function creates and sizes the overlays function createNotification(text) { @@ -152,101 +208,6 @@ function createNotification(text) { Notify(overlayProperties, buttonProperties, height); } -// Pushes data to each array and sets up data for 2nd dimension array -// to handle auxiliary data not carried by the overlay class -// specifically notification "heights", "times" of creation, and . -function Notify(notice, button, height) { - notifications.push((Overlays.addOverlay("text", notice))); - buttons.push((Overlays.addOverlay("image", button))); - times.push(new Date().getTime() / 1000); - height = height + 1.0; - heights.push(height); - myAlpha.push(0); - var last = notifications.length - 1; - createArrays(notifications[last], buttons[last], times[last], heights[last], myAlpha[last]); - fadeIn(notifications[last], buttons[last]) -} - -function fadeIn(noticeIn, buttonIn) { - var q = 0, - qFade, - pauseTimer = null; - - pauseTimer = Script.setInterval(function () { - q++; - qFade = q / 10.0; - Overlays.editOverlay(noticeIn, {alpha: qFade}); - Overlays.editOverlay(buttonIn, {alpha: qFade}); - if (q >= 9.0) { - Script.clearInterval(pauseTimer); - } - }, 10); -} - - -// push data from above to the 2 dimensional array -function createArrays(notice, button, createTime, height, myAlpha) { - arrays.push([notice, button, createTime, height, myAlpha]); -} - -// handles mouse clicks on buttons -function mousePressEvent(event) { - var clickedOverlay, - i; - - clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); //identify which overlay was clicked - - for (i = 0; i < buttons.length; i++) { //if user clicked a button - if (clickedOverlay == buttons[i]) { - Overlays.deleteOverlay(notifications[i]); - Overlays.deleteOverlay(buttons[i]); - notifications.splice(i, 1); - buttons.splice(i, 1); - times.splice(i, 1); - heights.splice(i, 1); - myAlpha.splice(i, 1); - arrays.splice(i, 1); - } - } -} - -// Control key remains active only while key is held down -function keyReleaseEvent(key) { - if (key.key == 16777249) { - ctrlIsPressed = false; - } -} - -// Triggers notification on specific key driven events -function keyPressEvent(key) { - var numUsers, - welcome, - noteString; - - if (key.key == 16777249) { - ctrlIsPressed = true; - } - - if (key.text == "q") { //queries number of users online - numUsers = GlobalServices.onlineUsers.length; - welcome = "There are " + numUsers + " users online now."; - createNotification(welcome); - } - - if (key.text == "s") { - if (ctrlIsPressed == true) { - noteString = "Snapshot taken."; - createNotification(noteString); - } - } -} - -// formats string to add newline every 43 chars -function wordWrap(str) { - var result = stringDivider(str, 43.0, "\n"); - createNotification(result); -} - // wraps whole word to newline function stringDivider(str, slotWidth, spaceReplacer) { var p, @@ -266,6 +227,12 @@ function stringDivider(str, slotWidth, spaceReplacer) { return str; } +// formats string to add newline every 43 chars +function wordWrap(str) { + var result = stringDivider(str, 43.0, "\n"); + createNotification(result); +} + // This fires a notification on window resize function checkSize() { if ((Window.innerWidth != ourWidth) || (Window.innerHeight != ourHeight)) { @@ -279,6 +246,72 @@ function checkSize() { } } +function update() { + var nextOverlay, + noticeOut, + buttonOut, + arraysOut, + i, + j, + k; + + frame++; + if ((frame % 60.0) == 0) { // only update once a second + checkSize(); // checks for size change to trigger windowResize notification + locationY = 20.0; + for (i = 0; i < arrays.length; i++) { //repositions overlays as others fade + nextOverlay = Overlays.getOverlayAtPoint({ x: overlayLocationX, y: locationY }); + Overlays.editOverlay(notifications[i], { x: overlayLocationX, y: locationY }); + Overlays.editOverlay(buttons[i], { x: buttonLocationX, y: locationY + 12.0 }); + locationY = locationY + arrays[i][3]; + } + } + + // This checks the age of the notification and prepares to fade it after 9.0 seconds (var persistTime - 1) + for (i = 0; i < arrays.length; i++) { + if (ready) { + j = arrays[i][2]; + k = j + persistTime; + if (k < (new Date().getTime() / 1000)) { + ready = false; + noticeOut = arrays[i][0]; + buttonOut = arrays[i][1]; + arraysOut = i; + fadeOut(noticeOut, buttonOut, arraysOut); + } + } + } +} + +var STARTUP_TIMEOUT = 500, // ms + startingUp = true, + startupTimer = null; + +// This reports the number of users online at startup +function reportUsers() { + var welcome; + + welcome = "Welcome! There are " + GlobalServices.onlineUsers.length + " users online now."; + createNotification(welcome); +} + +function finishStartup() { + startingUp = false; + Script.clearTimeout(startupTimer); + reportUsers(); +} + +function isStartingUp() { + // Is starting up until get no checks that it is starting up for STARTUP_TIMEOUT + if (startingUp) { + if (startupTimer) { + Script.clearTimeout(startupTimer); + } + startupTimer = Script.setTimeout(finishStartup, STARTUP_TIMEOUT); + } + return startingUp; +} + // Triggers notification if a user logs on or off function onOnlineUsersChanged(users) { var user; @@ -325,103 +358,66 @@ function onMuteStateChanged() { createNotification(muteString); } -function update() { - var nextOverlay, - noticeOut, - buttonOut, - arraysOut, - i, - j, - k; +// handles mouse clicks on buttons +function mousePressEvent(event) { + var clickedOverlay, + i; - frame++; - if ((frame % 60.0) == 0) { // only update once a second - checkSize(); // checks for size change to trigger windowResize notification - locationY = 20.0; - for (i = 0; i < arrays.length; i++) { //repositions overlays as others fade - nextOverlay = Overlays.getOverlayAtPoint({ x: overlayLocationX, y: locationY }); - Overlays.editOverlay(notifications[i], { x: overlayLocationX, y: locationY}); - Overlays.editOverlay(buttons[i], { x: buttonLocationX, y: locationY + 12.0}); - locationY = locationY + arrays[i][3]; - } - } + clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y }); //identify which overlay was clicked - // This checks the age of the notification and prepares to fade it after 9.0 seconds (var persistTime - 1) - for (i = 0; i < arrays.length; i++) { - if (ready) { - j = arrays[i][2]; - k = j + persistTime; - if (k < (new Date().getTime() / 1000)) { - ready = false; - noticeOut = arrays[i][0]; - buttonOut = arrays[i][1]; - arraysOut = i; - fadeOut(noticeOut, buttonOut, arraysOut); - } + for (i = 0; i < buttons.length; i++) { //if user clicked a button + if (clickedOverlay == buttons[i]) { + Overlays.deleteOverlay(notifications[i]); + Overlays.deleteOverlay(buttons[i]); + notifications.splice(i, 1); + buttons.splice(i, 1); + times.splice(i, 1); + heights.splice(i, 1); + myAlpha.splice(i, 1); + arrays.splice(i, 1); } } } -// this fades the notification ready for dismissal, and removes it from the arrays -function fadeOut(noticeOut, buttonOut, arraysOut) { - var r = 9.0, - rFade, - pauseTimer = null; - - pauseTimer = Script.setInterval(function () { - r--; - rFade = r / 10.0; - Overlays.editOverlay(noticeOut, { alpha: rFade }); - Overlays.editOverlay(buttonOut, { alpha: rFade }); - if (r < 0) { - dismiss(noticeOut, buttonOut, arraysOut); - arrays.splice(arraysOut, 1); - ready = true; - Script.clearInterval(pauseTimer); - } - }, 20); +// Control key remains active only while key is held down +function keyReleaseEvent(key) { + if (key.key == 16777249) { + ctrlIsPressed = false; + } } -// This handles the final dismissal of a notification after fading -function dismiss(firstNoteOut, firstButOut, firstOut) { - Overlays.deleteOverlay(firstNoteOut); - Overlays.deleteOverlay(firstButOut); - notifications.splice(firstOut, 1); - buttons.splice(firstOut, 1); - times.splice(firstOut, 1); - heights.splice(firstOut, 1); - myAlpha.splice(firstOut, 1); -} - -// This reports the number of users online at startup -function reportUsers() { +// Triggers notification on specific key driven events +function keyPressEvent(key) { var numUsers, - welcome; + welcome, + noteString; - numUsers = GlobalServices.onlineUsers.length; - welcome = "Welcome! There are " + numUsers + " users online now."; - createNotification(welcome); -} - -var STARTUP_TIMEOUT = 500, // ms - startingUp = true, - startupTimer = null; - -function finishStartup() { - startingUp = false; - Script.clearTimeout(startupTimer); - reportUsers(); -} - -function isStartingUp() { - // Is starting up until get no checks that it is starting up for STARTUP_TIMEOUT - if (startingUp) { - if (startupTimer) { - Script.clearTimeout(startupTimer); - } - startupTimer = Script.setTimeout(finishStartup, STARTUP_TIMEOUT); + if (key.key == 16777249) { + ctrlIsPressed = true; + } + + if (key.text == "q") { //queries number of users online + numUsers = GlobalServices.onlineUsers.length; + welcome = "There are " + numUsers + " users online now."; + createNotification(welcome); + } + + if (key.text == "s") { + if (ctrlIsPressed == true) { + noteString = "Snapshot taken."; + createNotification(noteString); + } + } +} + +// When our script shuts down, we should clean up all of our overlays +function scriptEnding() { + var i; + + for (i = 0; i < notifications.length; i++) { + Overlays.deleteOverlay(notifications[i]); + Overlays.deleteOverlay(buttons[i]); } - return startingUp; } AudioDevice.muteToggled.connect(onMuteStateChanged); @@ -431,3 +427,4 @@ GlobalServices.onlineUsersChanged.connect(onOnlineUsersChanged); GlobalServices.incomingMessage.connect(onIncomingMessage); Controller.keyReleaseEvent.connect(keyReleaseEvent); Script.update.connect(update); +Script.scriptEnding.connect(scriptEnding); From 63310fac503b8d5f8b8baa3f931c977c7ac68369 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 20 Jan 2015 12:18:58 -0800 Subject: [PATCH 04/44] Lint: Syntax --- examples/notifications.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/notifications.js b/examples/notifications.js index 6e73e67233..d1d69e448d 100644 --- a/examples/notifications.js +++ b/examples/notifications.js @@ -151,7 +151,7 @@ function Notify(notice, button, height) { myAlpha.push(0); var last = notifications.length - 1; createArrays(notifications[last], buttons[last], times[last], heights[last], myAlpha[last]); - fadeIn(notifications[last], buttons[last]) + fadeIn(notifications[last], buttons[last]); } // This function creates and sizes the overlays @@ -189,7 +189,7 @@ function createNotification(text) { topMargin: topMargin, leftMargin: leftMargin, font: {size: fontSize}, - text: text, + text: text }; bLevel = level + 12.0; @@ -202,7 +202,7 @@ function createNotification(text) { imageURL: "http://hifi-public.s3.amazonaws.com/images/close-small-light.svg", color: { red: 255, green: 255, blue: 255}, visible: true, - alpha: backgroundAlpha, + alpha: backgroundAlpha }; Notify(overlayProperties, buttonProperties, height); @@ -242,7 +242,7 @@ function checkSize() { windowDimensions = Controller.getViewportDimensions(); overlayLocationX = (windowDimensions.x - (width + 60.0)); buttonLocationX = overlayLocationX + (width - 35.0); - createNotification(windowResize) + createNotification(windowResize); } } From adba46f90c7cc581bc72df94c691ca208211e531 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 20 Jan 2015 12:23:38 -0800 Subject: [PATCH 05/44] Lint: Preferred operators --- examples/notifications.js | 40 +++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/examples/notifications.js b/examples/notifications.js index d1d69e448d..a08a8b473e 100644 --- a/examples/notifications.js +++ b/examples/notifications.js @@ -109,7 +109,7 @@ function fadeIn(noticeIn, buttonIn) { pauseTimer = null; pauseTimer = Script.setInterval(function () { - q++; + q += 1; qFade = q / 10.0; Overlays.editOverlay(noticeIn, { alpha: qFade }); Overlays.editOverlay(buttonIn, { alpha: qFade }); @@ -126,7 +126,7 @@ function fadeOut(noticeOut, buttonOut, arraysOut) { pauseTimer = null; pauseTimer = Script.setInterval(function () { - r--; + r -= 1; rFade = r / 10.0; Overlays.editOverlay(noticeOut, { alpha: rFade }); Overlays.editOverlay(buttonOut, { alpha: rFade }); @@ -142,7 +142,7 @@ function fadeOut(noticeOut, buttonOut, arraysOut) { // Pushes data to each array and sets up data for 2nd dimension array // to handle auxiliary data not carried by the overlay class // specifically notification "heights", "times" of creation, and . -function Notify(notice, button, height) { +function notify(notice, button, height) { notifications.push((Overlays.addOverlay("text", notice))); buttons.push((Overlays.addOverlay("image", button))); times.push(new Date().getTime() / 1000); @@ -172,7 +172,7 @@ function createNotification(text) { breaks = count; } extraLine = breaks * 16.0; - for (i = 0; i < heights.length; i++) { + for (i = 0; i < heights.length; i += 1) { stack = stack + heights[i]; } @@ -205,7 +205,7 @@ function createNotification(text) { alpha: backgroundAlpha }; - Notify(overlayProperties, buttonProperties, height); + notify(overlayProperties, buttonProperties, height); } // wraps whole word to newline @@ -235,7 +235,7 @@ function wordWrap(str) { // This fires a notification on window resize function checkSize() { - if ((Window.innerWidth != ourWidth) || (Window.innerHeight != ourHeight)) { + if ((Window.innerWidth !== ourWidth) || (Window.innerHeight !== ourHeight)) { var windowResize = "Window has been resized"; ourWidth = Window.innerWidth; ourHeight = Window.innerHeight; @@ -255,11 +255,11 @@ function update() { j, k; - frame++; - if ((frame % 60.0) == 0) { // only update once a second + frame += 1; + if ((frame % 60.0) === 0) { // only update once a second checkSize(); // checks for size change to trigger windowResize notification locationY = 20.0; - for (i = 0; i < arrays.length; i++) { //repositions overlays as others fade + for (i = 0; i < arrays.length; i += 1) { //repositions overlays as others fade nextOverlay = Overlays.getOverlayAtPoint({ x: overlayLocationX, y: locationY }); Overlays.editOverlay(notifications[i], { x: overlayLocationX, y: locationY }); Overlays.editOverlay(buttons[i], { x: buttonLocationX, y: locationY + 12.0 }); @@ -268,7 +268,7 @@ function update() { } // This checks the age of the notification and prepares to fade it after 9.0 seconds (var persistTime - 1) - for (i = 0; i < arrays.length; i++) { + for (i = 0; i < arrays.length; i += 1) { if (ready) { j = arrays[i][2]; k = j + persistTime; @@ -318,13 +318,13 @@ function onOnlineUsersChanged(users) { if (!isStartingUp()) { // Skip user notifications at startup. for (user in users) { - if (last_users.indexOf(users[user]) == -1.0) { + if (last_users.indexOf(users[user]) === -1.0) { createNotification(users[user] + " has joined"); } } for (user in last_users) { - if (users.indexOf(last_users[user]) == -1.0) { + if (users.indexOf(last_users[user]) === -1.0) { createNotification(last_users[user] + " has left"); } } @@ -365,8 +365,8 @@ function mousePressEvent(event) { clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y }); //identify which overlay was clicked - for (i = 0; i < buttons.length; i++) { //if user clicked a button - if (clickedOverlay == buttons[i]) { + for (i = 0; i < buttons.length; i += 1) { //if user clicked a button + if (clickedOverlay === buttons[i]) { Overlays.deleteOverlay(notifications[i]); Overlays.deleteOverlay(buttons[i]); notifications.splice(i, 1); @@ -381,7 +381,7 @@ function mousePressEvent(event) { // Control key remains active only while key is held down function keyReleaseEvent(key) { - if (key.key == 16777249) { + if (key.key === 16777249) { ctrlIsPressed = false; } } @@ -392,18 +392,18 @@ function keyPressEvent(key) { welcome, noteString; - if (key.key == 16777249) { + if (key.key === 16777249) { ctrlIsPressed = true; } - if (key.text == "q") { //queries number of users online + if (key.text === "q") { //queries number of users online numUsers = GlobalServices.onlineUsers.length; welcome = "There are " + numUsers + " users online now."; createNotification(welcome); } - if (key.text == "s") { - if (ctrlIsPressed == true) { + if (key.text === "s") { + if (ctrlIsPressed === true) { noteString = "Snapshot taken."; createNotification(noteString); } @@ -414,7 +414,7 @@ function keyPressEvent(key) { function scriptEnding() { var i; - for (i = 0; i < notifications.length; i++) { + for (i = 0; i < notifications.length; i += 1) { Overlays.deleteOverlay(notifications[i]); Overlays.deleteOverlay(buttons[i]); } From 42b9ee8e77904b48f301cb74e9ca17674330d9ae Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 20 Jan 2015 12:51:34 -0800 Subject: [PATCH 06/44] Lint: Preferred control constructs --- examples/notifications.js | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/examples/notifications.js b/examples/notifications.js index a08a8b473e..2929c61a64 100644 --- a/examples/notifications.js +++ b/examples/notifications.js @@ -216,8 +216,10 @@ function stringDivider(str, slotWidth, spaceReplacer) { if (str.length > slotWidth) { p = slotWidth; - for (; p > 0 && str[p] != ' '; p--) { + while (p > 0 && str[p] !== ' ') { + p -= 1; } + if (p > 0) { left = str.substring(0, p); right = str.substring(p + 1); @@ -314,18 +316,20 @@ function isStartingUp() { // Triggers notification if a user logs on or off function onOnlineUsersChanged(users) { - var user; + var user, + i; if (!isStartingUp()) { // Skip user notifications at startup. - for (user in users) { - if (last_users.indexOf(users[user]) === -1.0) { - createNotification(users[user] + " has joined"); + for (i = 0; i < users.length; i += 1) { + if (last_users.indexOf(users[i]) === -1.0) { + createNotification(users[i] + " has joined"); } + } - for (user in last_users) { - if (users.indexOf(last_users[user]) === -1.0) { - createNotification(last_users[user] + " has left"); + for (i = 0; i < last_users.length; i += 1) { + if (users.indexOf(last_users[i]) === -1.0) { + createNotification(last_users[i] + " has left"); } } } From 504bd5e1b1ac5b00940f1cde1b42a05d12655ec3 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 20 Jan 2015 18:34:47 -0800 Subject: [PATCH 07/44] Expose default eye position to JavaScript --- interface/src/avatar/MyAvatar.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index a4a01373a0..e549abbc96 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -51,7 +51,7 @@ public: // getters float getLeanScale() const { return _leanScale; } glm::vec3 getGravity() const { return _gravity; } - glm::vec3 getDefaultEyePosition() const; + Q_INVOKABLE glm::vec3 getDefaultEyePosition() const; bool getShouldRenderLocally() const { return _shouldRender; } const QList& getAnimationHandles() const { return _animationHandles; } From be6c6ed8ca3ee83b67f2e016d40c6d3f8826517f Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 21 Jan 2015 16:10:33 -0800 Subject: [PATCH 08/44] Add 3D notifications on a simple vertical plane --- examples/notifications.js | 110 ++++++++++++++++++++++++++++++-------- 1 file changed, 88 insertions(+), 22 deletions(-) diff --git a/examples/notifications.js b/examples/notifications.js index 2929c61a64..0aad7c78d8 100644 --- a/examples/notifications.js +++ b/examples/notifications.js @@ -59,7 +59,6 @@ // } var width = 340.0; //width of notification overlay -var height = 40.0; // height of a single line notification overlay var windowDimensions = Controller.getViewportDimensions(); // get the size of the interface window var overlayLocationX = (windowDimensions.x - (width + 20.0)); // positions window 20px from the right of the interface window var buttonLocationX = overlayLocationX + (width - 28.0); @@ -86,6 +85,12 @@ var times = []; var heights = []; var myAlpha = []; var arrays = []; +var isOnHMD = false, + ENABLE_VR_MODE = "Enable VR Mode", + NOTIFICATIONS_3D_DISTANCE = 0.6, // Distance from avatar position. + NOTIFICATIONS_3D_ELEVATION = 0.0, // Height of top middle of top notification relative to avatar eyes. + NOTIFICATION_3D_SCALE = 0.002, // Multiplier that converts 2D overlay dimensions to 3D overlay dimensions. + NOTIFICATION_3D_BUTTON_WIDTH = 40 * NOTIFICATION_3D_SCALE; // Need a little more room for button in 3D. // push data from above to the 2 dimensional array function createArrays(notice, button, createTime, height, myAlpha) { @@ -143,13 +148,58 @@ function fadeOut(noticeOut, buttonOut, arraysOut) { // to handle auxiliary data not carried by the overlay class // specifically notification "heights", "times" of creation, and . function notify(notice, button, height) { - notifications.push((Overlays.addOverlay("text", notice))); - buttons.push((Overlays.addOverlay("image", button))); - times.push(new Date().getTime() / 1000); + var noticeWidth, + noticeHeight, + noticeY, + position3D, + rotation3D, + last; + + if (isOnHMD) { + // Calculate 3D values using 2D overlay properties. + + rotation3D = MyAvatar.orientation; + + noticeWidth = notice.width * NOTIFICATION_3D_SCALE + NOTIFICATION_3D_BUTTON_WIDTH; + noticeHeight = notice.height * NOTIFICATION_3D_SCALE; + noticeY = NOTIFICATIONS_3D_ELEVATION - notice.y * NOTIFICATION_3D_SCALE - noticeHeight / 2; + + notice.size = { x: noticeWidth, y: noticeHeight }; + notice.topMargin = 0.75 * notice.topMargin * NOTIFICATION_3D_SCALE; + notice.leftMargin = 2 * notice.leftMargin * NOTIFICATION_3D_SCALE; + notice.bottomMargin = 0; + notice.rightMargin = 0; + notice.lineHeight = 10.0 * (fontSize / 12.0) * NOTIFICATION_3D_SCALE; + notice.isFacingAvatar = false; + + position3D = { x: 0, y: noticeY, z: -NOTIFICATIONS_3D_DISTANCE }; + position3D = Vec3.multiplyQbyV(rotation3D, position3D); + position3D = Vec3.sum(MyAvatar.getDefaultEyePosition(), position3D); + notice.position = position3D; + notice.rotation = rotation3D; + + button.url = button.imageURL; + button.scale = button.width * NOTIFICATION_3D_SCALE; + button.isFacingAvatar = false; + + position3D = { x: (noticeWidth - NOTIFICATION_3D_BUTTON_WIDTH) / 2, y: noticeY, z: -NOTIFICATIONS_3D_DISTANCE + 0.001 }; + position3D = Vec3.multiplyQbyV(rotation3D, position3D); + position3D = Vec3.sum(MyAvatar.getDefaultEyePosition(), position3D); + button.position = position3D; + button.rotation = rotation3D; + + notifications.push((Overlays.addOverlay("text3d", notice))); + buttons.push((Overlays.addOverlay("billboard", button))); + } else { + notifications.push((Overlays.addOverlay("text", notice))); + buttons.push((Overlays.addOverlay("image", button))); + } + height = height + 1.0; heights.push(height); + times.push(new Date().getTime() / 1000); myAlpha.push(0); - var last = notifications.length - 1; + last = notifications.length - 1; createArrays(notifications[last], buttons[last], times[last], heights[last], myAlpha[last]); fadeIn(notifications[last], buttons[last]); } @@ -163,7 +213,7 @@ function createNotification(text) { height = 40.0, stack = 0, level, - overlayProperties, + noticeProperties, bLevel, buttonProperties, i; @@ -178,7 +228,7 @@ function createNotification(text) { level = (stack + 20.0); height = height + extraLine; - overlayProperties = { + noticeProperties = { x: overlayLocationX, y: level, width: width, @@ -205,7 +255,18 @@ function createNotification(text) { alpha: backgroundAlpha }; - notify(overlayProperties, buttonProperties, height); + notify(noticeProperties, buttonProperties, height); +} + +function deleteNotification(index) { + Overlays.deleteOverlay(notifications[index]); + Overlays.deleteOverlay(buttons[index]); + notifications.splice(index, 1); + buttons.splice(index, 1); + times.splice(index, 1); + heights.splice(index, 1); + myAlpha.splice(index, 1); + arrays.splice(index, 1); } // wraps whole word to newline @@ -257,15 +318,27 @@ function update() { j, k; + if (isOnHMD !== Menu.isOptionChecked(ENABLE_VR_MODE)) { + while (arrays.length > 0) { + deleteNotification(0); + } + isOnHMD = !isOnHMD; + return; + } + frame += 1; if ((frame % 60.0) === 0) { // only update once a second checkSize(); // checks for size change to trigger windowResize notification - locationY = 20.0; - for (i = 0; i < arrays.length; i += 1) { //repositions overlays as others fade - nextOverlay = Overlays.getOverlayAtPoint({ x: overlayLocationX, y: locationY }); - Overlays.editOverlay(notifications[i], { x: overlayLocationX, y: locationY }); - Overlays.editOverlay(buttons[i], { x: buttonLocationX, y: locationY + 12.0 }); - locationY = locationY + arrays[i][3]; + if (isOnHMD) { + // TODO + } else { + locationY = 20.0; + for (i = 0; i < arrays.length; i += 1) { //repositions overlays as others fade + nextOverlay = Overlays.getOverlayAtPoint({ x: overlayLocationX, y: locationY }); + Overlays.editOverlay(notifications[i], { x: overlayLocationX, y: locationY }); + Overlays.editOverlay(buttons[i], { x: buttonLocationX, y: locationY + 12.0 }); + locationY = locationY + arrays[i][3]; + } } } @@ -371,14 +444,7 @@ function mousePressEvent(event) { for (i = 0; i < buttons.length; i += 1) { //if user clicked a button if (clickedOverlay === buttons[i]) { - Overlays.deleteOverlay(notifications[i]); - Overlays.deleteOverlay(buttons[i]); - notifications.splice(i, 1); - buttons.splice(i, 1); - times.splice(i, 1); - heights.splice(i, 1); - myAlpha.splice(i, 1); - arrays.splice(i, 1); + deleteNotification(i); } } } From 567ac9eb2face9f817b4e24a08b1c15827a5273a Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 21 Jan 2015 17:08:31 -0800 Subject: [PATCH 09/44] Update 3D notification positions as avatar moves --- examples/notifications.js | 39 ++++++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/examples/notifications.js b/examples/notifications.js index 0aad7c78d8..fcafe3a8b2 100644 --- a/examples/notifications.js +++ b/examples/notifications.js @@ -90,7 +90,8 @@ var isOnHMD = false, NOTIFICATIONS_3D_DISTANCE = 0.6, // Distance from avatar position. NOTIFICATIONS_3D_ELEVATION = 0.0, // Height of top middle of top notification relative to avatar eyes. NOTIFICATION_3D_SCALE = 0.002, // Multiplier that converts 2D overlay dimensions to 3D overlay dimensions. - NOTIFICATION_3D_BUTTON_WIDTH = 40 * NOTIFICATION_3D_SCALE; // Need a little more room for button in 3D. + NOTIFICATION_3D_BUTTON_WIDTH = 40 * NOTIFICATION_3D_SCALE, // Need a little more room for button in 3D. + overlay3DPositions = []; // push data from above to the 2 dimensional array function createArrays(notice, button, createTime, height, myAlpha) { @@ -106,6 +107,7 @@ function dismiss(firstNoteOut, firstButOut, firstOut) { times.splice(firstOut, 1); heights.splice(firstOut, 1); myAlpha.splice(firstOut, 1); + overlay3DPositions.splice(firstOut, 1); } function fadeIn(noticeIn, buttonIn) { @@ -151,6 +153,8 @@ function notify(notice, button, height) { var noticeWidth, noticeHeight, noticeY, + notificationPosition, + buttonPosition, position3D, rotation3D, last; @@ -172,8 +176,8 @@ function notify(notice, button, height) { notice.lineHeight = 10.0 * (fontSize / 12.0) * NOTIFICATION_3D_SCALE; notice.isFacingAvatar = false; - position3D = { x: 0, y: noticeY, z: -NOTIFICATIONS_3D_DISTANCE }; - position3D = Vec3.multiplyQbyV(rotation3D, position3D); + notificationPosition = { x: 0, y: noticeY, z: -NOTIFICATIONS_3D_DISTANCE }; + position3D = Vec3.multiplyQbyV(rotation3D, notificationPosition); position3D = Vec3.sum(MyAvatar.getDefaultEyePosition(), position3D); notice.position = position3D; notice.rotation = rotation3D; @@ -182,14 +186,19 @@ function notify(notice, button, height) { button.scale = button.width * NOTIFICATION_3D_SCALE; button.isFacingAvatar = false; - position3D = { x: (noticeWidth - NOTIFICATION_3D_BUTTON_WIDTH) / 2, y: noticeY, z: -NOTIFICATIONS_3D_DISTANCE + 0.001 }; - position3D = Vec3.multiplyQbyV(rotation3D, position3D); + buttonPosition = { + x: (noticeWidth - NOTIFICATION_3D_BUTTON_WIDTH) / 2, + y: noticeY, + z: -NOTIFICATIONS_3D_DISTANCE + 0.001 + }; + position3D = Vec3.multiplyQbyV(rotation3D, buttonPosition); position3D = Vec3.sum(MyAvatar.getDefaultEyePosition(), position3D); button.position = position3D; button.rotation = rotation3D; notifications.push((Overlays.addOverlay("text3d", notice))); buttons.push((Overlays.addOverlay("billboard", button))); + overlay3DPositions.push({ notification: notificationPosition, button: buttonPosition }); } else { notifications.push((Overlays.addOverlay("text", notice))); buttons.push((Overlays.addOverlay("image", button))); @@ -266,6 +275,7 @@ function deleteNotification(index) { times.splice(index, 1); heights.splice(index, 1); myAlpha.splice(index, 1); + overlay3DPositions.splice(index, 1); arrays.splice(index, 1); } @@ -314,6 +324,10 @@ function update() { noticeOut, buttonOut, arraysOut, + defaultEyePosition, + avatarOrientation, + notificationPosition, + buttonPosition, i, j, k; @@ -356,6 +370,21 @@ function update() { } } } + + if (isOnHMD && notifications.length > 0) { + // Update 3D overlays to maintain positions relative to avatar + defaultEyePosition = MyAvatar.getDefaultEyePosition(); + avatarOrientation = MyAvatar.orientation; + + for (i = 0; i < notifications.length; i += 1) { + notificationPosition = Vec3.sum(defaultEyePosition, + Vec3.multiplyQbyV(avatarOrientation, overlay3DPositions[i].notification)); + buttonPosition = Vec3.sum(defaultEyePosition, + Vec3.multiplyQbyV(avatarOrientation, overlay3DPositions[i].button)); + Overlays.editOverlay(notifications[i], { position: notificationPosition, rotation: avatarOrientation }); + Overlays.editOverlay(buttons[i], { position: buttonPosition, rotation: avatarOrientation }); + } + } } var STARTUP_TIMEOUT = 500, // ms From f3147ab3e4b05772ecb809aede47e4aacc890730 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 21 Jan 2015 17:58:50 -0800 Subject: [PATCH 10/44] Move 3D notifications up as old ones are deleted --- examples/notifications.js | 81 +++++++++++++++++++++++++-------------- 1 file changed, 52 insertions(+), 29 deletions(-) diff --git a/examples/notifications.js b/examples/notifications.js index fcafe3a8b2..ecd95428aa 100644 --- a/examples/notifications.js +++ b/examples/notifications.js @@ -91,7 +91,7 @@ var isOnHMD = false, NOTIFICATIONS_3D_ELEVATION = 0.0, // Height of top middle of top notification relative to avatar eyes. NOTIFICATION_3D_SCALE = 0.002, // Multiplier that converts 2D overlay dimensions to 3D overlay dimensions. NOTIFICATION_3D_BUTTON_WIDTH = 40 * NOTIFICATION_3D_SCALE, // Need a little more room for button in 3D. - overlay3DPositions = []; + overlay3DDetails = []; // push data from above to the 2 dimensional array function createArrays(notice, button, createTime, height, myAlpha) { @@ -107,7 +107,7 @@ function dismiss(firstNoteOut, firstButOut, firstOut) { times.splice(firstOut, 1); heights.splice(firstOut, 1); myAlpha.splice(firstOut, 1); - overlay3DPositions.splice(firstOut, 1); + overlay3DDetails.splice(firstOut, 1); } function fadeIn(noticeIn, buttonIn) { @@ -146,15 +146,34 @@ function fadeOut(noticeOut, buttonOut, arraysOut) { }, 20); } +function calculate3DOverlayPositions(noticeWidth, noticeHeight, y) { + var noticeY, + notificationPosition, + buttonPosition; + + noticeY = NOTIFICATIONS_3D_ELEVATION - y * NOTIFICATION_3D_SCALE - noticeHeight / 2; + notificationPosition = { x: 0, y: noticeY, z: -NOTIFICATIONS_3D_DISTANCE }; + buttonPosition = { + x: (noticeWidth - NOTIFICATION_3D_BUTTON_WIDTH) / 2, + y: noticeY, + z: -NOTIFICATIONS_3D_DISTANCE + 0.001 + }; + + return { + notificationPosition: notificationPosition, + buttonPosition: buttonPosition + }; +} + // Pushes data to each array and sets up data for 2nd dimension array // to handle auxiliary data not carried by the overlay class // specifically notification "heights", "times" of creation, and . function notify(notice, button, height) { var noticeWidth, noticeHeight, - noticeY, notificationPosition, buttonPosition, + positions, position3D, rotation3D, last; @@ -162,11 +181,8 @@ function notify(notice, button, height) { if (isOnHMD) { // Calculate 3D values using 2D overlay properties. - rotation3D = MyAvatar.orientation; - noticeWidth = notice.width * NOTIFICATION_3D_SCALE + NOTIFICATION_3D_BUTTON_WIDTH; noticeHeight = notice.height * NOTIFICATION_3D_SCALE; - noticeY = NOTIFICATIONS_3D_ELEVATION - notice.y * NOTIFICATION_3D_SCALE - noticeHeight / 2; notice.size = { x: noticeWidth, y: noticeHeight }; notice.topMargin = 0.75 * notice.topMargin * NOTIFICATION_3D_SCALE; @@ -176,21 +192,21 @@ function notify(notice, button, height) { notice.lineHeight = 10.0 * (fontSize / 12.0) * NOTIFICATION_3D_SCALE; notice.isFacingAvatar = false; - notificationPosition = { x: 0, y: noticeY, z: -NOTIFICATIONS_3D_DISTANCE }; + button.url = button.imageURL; + button.scale = button.width * NOTIFICATION_3D_SCALE; + button.isFacingAvatar = false; + + positions = calculate3DOverlayPositions(noticeWidth, noticeHeight, notice.y); + notificationPosition = positions.notificationPosition; + buttonPosition = positions.buttonPosition; + + rotation3D = MyAvatar.orientation; + position3D = Vec3.multiplyQbyV(rotation3D, notificationPosition); position3D = Vec3.sum(MyAvatar.getDefaultEyePosition(), position3D); notice.position = position3D; notice.rotation = rotation3D; - button.url = button.imageURL; - button.scale = button.width * NOTIFICATION_3D_SCALE; - button.isFacingAvatar = false; - - buttonPosition = { - x: (noticeWidth - NOTIFICATION_3D_BUTTON_WIDTH) / 2, - y: noticeY, - z: -NOTIFICATIONS_3D_DISTANCE + 0.001 - }; position3D = Vec3.multiplyQbyV(rotation3D, buttonPosition); position3D = Vec3.sum(MyAvatar.getDefaultEyePosition(), position3D); button.position = position3D; @@ -198,7 +214,12 @@ function notify(notice, button, height) { notifications.push((Overlays.addOverlay("text3d", notice))); buttons.push((Overlays.addOverlay("billboard", button))); - overlay3DPositions.push({ notification: notificationPosition, button: buttonPosition }); + overlay3DDetails.push({ + notificationPosition: notificationPosition, + buttonPosition: buttonPosition, + width: noticeWidth, + height: noticeHeight + }); } else { notifications.push((Overlays.addOverlay("text", notice))); buttons.push((Overlays.addOverlay("image", button))); @@ -275,7 +296,7 @@ function deleteNotification(index) { times.splice(index, 1); heights.splice(index, 1); myAlpha.splice(index, 1); - overlay3DPositions.splice(index, 1); + overlay3DDetails.splice(index, 1); arrays.splice(index, 1); } @@ -328,6 +349,7 @@ function update() { avatarOrientation, notificationPosition, buttonPosition, + positions, i, j, k; @@ -343,16 +365,17 @@ function update() { frame += 1; if ((frame % 60.0) === 0) { // only update once a second checkSize(); // checks for size change to trigger windowResize notification - if (isOnHMD) { - // TODO - } else { - locationY = 20.0; - for (i = 0; i < arrays.length; i += 1) { //repositions overlays as others fade - nextOverlay = Overlays.getOverlayAtPoint({ x: overlayLocationX, y: locationY }); - Overlays.editOverlay(notifications[i], { x: overlayLocationX, y: locationY }); - Overlays.editOverlay(buttons[i], { x: buttonLocationX, y: locationY + 12.0 }); - locationY = locationY + arrays[i][3]; + locationY = 20.0; + for (i = 0; i < arrays.length; i += 1) { //repositions overlays as others fade + nextOverlay = Overlays.getOverlayAtPoint({ x: overlayLocationX, y: locationY }); + Overlays.editOverlay(notifications[i], { x: overlayLocationX, y: locationY }); + Overlays.editOverlay(buttons[i], { x: buttonLocationX, y: locationY + 12.0 }); + if (isOnHMD) { + positions = calculate3DOverlayPositions(overlay3DDetails[i].width, overlay3DDetails[i].height, locationY); + overlay3DDetails[i].notificationPosition = positions.notificationPosition; + overlay3DDetails[i].buttonPosition = positions.buttonPosition; } + locationY = locationY + arrays[i][3]; } } @@ -378,9 +401,9 @@ function update() { for (i = 0; i < notifications.length; i += 1) { notificationPosition = Vec3.sum(defaultEyePosition, - Vec3.multiplyQbyV(avatarOrientation, overlay3DPositions[i].notification)); + Vec3.multiplyQbyV(avatarOrientation, overlay3DDetails[i].notificationPosition)); buttonPosition = Vec3.sum(defaultEyePosition, - Vec3.multiplyQbyV(avatarOrientation, overlay3DPositions[i].button)); + Vec3.multiplyQbyV(avatarOrientation, overlay3DDetails[i].buttonPosition)); Overlays.editOverlay(notifications[i], { position: notificationPosition, rotation: avatarOrientation }); Overlays.editOverlay(buttons[i], { position: buttonPosition, rotation: avatarOrientation }); } From 00de7062cc4bad4e9ab9b5bdc6b2b1f504114872 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 21 Jan 2015 18:03:00 -0800 Subject: [PATCH 11/44] Longer persist time for 3D notifications --- examples/notifications.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/examples/notifications.js b/examples/notifications.js index ecd95428aa..d02e31cd0f 100644 --- a/examples/notifications.js +++ b/examples/notifications.js @@ -69,7 +69,9 @@ var textColor = { red: 228, green: 228, blue: 228}; // text color var backColor = { red: 2, green: 2, blue: 2}; // background color was 38,38,38 var backgroundAlpha = 0; var fontSize = 12.0; -var persistTime = 10.0; // time in seconds before notification fades +var PERSIST_TIME_2D = 10.0; // Time in seconds before notification fades +var PERSIST_TIME_3D = 15.0; +var persistTime = PERSIST_TIME_2D; var clickedText = false; var frame = 0; var ourWidth = Window.innerWidth; @@ -359,6 +361,7 @@ function update() { deleteNotification(0); } isOnHMD = !isOnHMD; + persistTime = isOnHMD ? PERSIST_TIME_3D : PERSIST_TIME_2D; return; } From ef7ea0eaa68ca6ffa323d8185eb7398f2bea67cf Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 21 Jan 2015 21:34:34 -0800 Subject: [PATCH 12/44] Position and angle 3D notifications in lower part of view --- examples/notifications.js | 67 ++++++++++++++++++++------------------- 1 file changed, 35 insertions(+), 32 deletions(-) diff --git a/examples/notifications.js b/examples/notifications.js index d02e31cd0f..682750f56f 100644 --- a/examples/notifications.js +++ b/examples/notifications.js @@ -89,8 +89,11 @@ var myAlpha = []; var arrays = []; var isOnHMD = false, ENABLE_VR_MODE = "Enable VR Mode", - NOTIFICATIONS_3D_DISTANCE = 0.6, // Distance from avatar position. - NOTIFICATIONS_3D_ELEVATION = 0.0, // Height of top middle of top notification relative to avatar eyes. + NOTIFICATIONS_3D_DIRECTION = 0.0, // Degrees from avatar orientation. + NOTIFICATIONS_3D_DISTANCE = 0.6, // Horizontal distance from avatar position. + NOTIFICATIONS_3D_ELEVATION = -0.8, // Height of top middle of top notification relative to avatar eyes. + NOTIFICATIONS_3D_YAW = 0.0, // Degrees relative to notifications direction. + NOTIFICATIONS_3D_PITCH = -60.0, // Degrees from vertical. NOTIFICATION_3D_SCALE = 0.002, // Multiplier that converts 2D overlay dimensions to 3D overlay dimensions. NOTIFICATION_3D_BUTTON_WIDTH = 40 * NOTIFICATION_3D_SCALE, // Need a little more room for button in 3D. overlay3DDetails = []; @@ -149,19 +152,33 @@ function fadeOut(noticeOut, buttonOut, arraysOut) { } function calculate3DOverlayPositions(noticeWidth, noticeHeight, y) { + // Calculates overlay positions and orientations in avatar coordinates. var noticeY, + originOffset, + notificationOrientation, notificationPosition, buttonPosition; - noticeY = NOTIFICATIONS_3D_ELEVATION - y * NOTIFICATION_3D_SCALE - noticeHeight / 2; - notificationPosition = { x: 0, y: noticeY, z: -NOTIFICATIONS_3D_DISTANCE }; - buttonPosition = { - x: (noticeWidth - NOTIFICATION_3D_BUTTON_WIDTH) / 2, - y: noticeY, - z: -NOTIFICATIONS_3D_DISTANCE + 0.001 - }; + // Notification plane positions + noticeY = -y * NOTIFICATION_3D_SCALE - noticeHeight / 2; + notificationPosition = { x: 0, y: noticeY, z: 0 }; + buttonPosition = { x: (noticeWidth - NOTIFICATION_3D_BUTTON_WIDTH) / 2, y: noticeY, z: 0.001 }; + + // Rotate plane + notificationOrientation = Quat.fromPitchYawRollDegrees(NOTIFICATIONS_3D_PITCH, + NOTIFICATIONS_3D_DIRECTION + NOTIFICATIONS_3D_YAW, 0); + notificationPosition = Vec3.multiplyQbyV(notificationOrientation, notificationPosition); + buttonPosition = Vec3.multiplyQbyV(notificationOrientation, buttonPosition); + + // Translate plane + originOffset = Vec3.multiplyQbyV(Quat.fromPitchYawRollDegrees(0, NOTIFICATIONS_3D_DIRECTION, 0), + { x: 0, y: 0, z: -NOTIFICATIONS_3D_DISTANCE }); + originOffset.y += NOTIFICATIONS_3D_ELEVATION; + notificationPosition = Vec3.sum(originOffset, notificationPosition); + buttonPosition = Vec3.sum(originOffset, buttonPosition); return { + notificationOrientation: notificationOrientation, notificationPosition: notificationPosition, buttonPosition: buttonPosition }; @@ -173,15 +190,11 @@ function calculate3DOverlayPositions(noticeWidth, noticeHeight, y) { function notify(notice, button, height) { var noticeWidth, noticeHeight, - notificationPosition, - buttonPosition, positions, - position3D, - rotation3D, last; if (isOnHMD) { - // Calculate 3D values using 2D overlay properties. + // Calculate 3D values from 2D overlay properties. noticeWidth = notice.width * NOTIFICATION_3D_SCALE + NOTIFICATION_3D_BUTTON_WIDTH; noticeHeight = notice.height * NOTIFICATION_3D_SCALE; @@ -199,26 +212,13 @@ function notify(notice, button, height) { button.isFacingAvatar = false; positions = calculate3DOverlayPositions(noticeWidth, noticeHeight, notice.y); - notificationPosition = positions.notificationPosition; - buttonPosition = positions.buttonPosition; - - rotation3D = MyAvatar.orientation; - - position3D = Vec3.multiplyQbyV(rotation3D, notificationPosition); - position3D = Vec3.sum(MyAvatar.getDefaultEyePosition(), position3D); - notice.position = position3D; - notice.rotation = rotation3D; - - position3D = Vec3.multiplyQbyV(rotation3D, buttonPosition); - position3D = Vec3.sum(MyAvatar.getDefaultEyePosition(), position3D); - button.position = position3D; - button.rotation = rotation3D; notifications.push((Overlays.addOverlay("text3d", notice))); buttons.push((Overlays.addOverlay("billboard", button))); overlay3DDetails.push({ - notificationPosition: notificationPosition, - buttonPosition: buttonPosition, + notificationOrientation: positions.notificationOrientation, + notificationPosition: positions.notificationPosition, + buttonPosition: positions.buttonPosition, width: noticeWidth, height: noticeHeight }); @@ -350,6 +350,7 @@ function update() { defaultEyePosition, avatarOrientation, notificationPosition, + notificationOrientation, buttonPosition, positions, i, @@ -375,6 +376,7 @@ function update() { Overlays.editOverlay(buttons[i], { x: buttonLocationX, y: locationY + 12.0 }); if (isOnHMD) { positions = calculate3DOverlayPositions(overlay3DDetails[i].width, overlay3DDetails[i].height, locationY); + overlay3DDetails[i].notificationOrientation = positions.notificationOrientation; overlay3DDetails[i].notificationPosition = positions.notificationPosition; overlay3DDetails[i].buttonPosition = positions.buttonPosition; } @@ -405,10 +407,11 @@ function update() { for (i = 0; i < notifications.length; i += 1) { notificationPosition = Vec3.sum(defaultEyePosition, Vec3.multiplyQbyV(avatarOrientation, overlay3DDetails[i].notificationPosition)); + notificationOrientation = Quat.multiply(avatarOrientation, overlay3DDetails[i].notificationOrientation); buttonPosition = Vec3.sum(defaultEyePosition, Vec3.multiplyQbyV(avatarOrientation, overlay3DDetails[i].buttonPosition)); - Overlays.editOverlay(notifications[i], { position: notificationPosition, rotation: avatarOrientation }); - Overlays.editOverlay(buttons[i], { position: buttonPosition, rotation: avatarOrientation }); + Overlays.editOverlay(notifications[i], { position: notificationPosition, rotation: notificationOrientation }); + Overlays.editOverlay(buttons[i], { position: buttonPosition, rotation: notificationOrientation }); } } } From 7d1cc1f20dc807a00392865fff529895c9d41060 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 21 Jan 2015 21:44:55 -0800 Subject: [PATCH 13/44] Make 3D "x" button work --- examples/notifications.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/examples/notifications.js b/examples/notifications.js index 682750f56f..525d438fe6 100644 --- a/examples/notifications.js +++ b/examples/notifications.js @@ -495,12 +495,18 @@ function onMuteStateChanged() { // handles mouse clicks on buttons function mousePressEvent(event) { - var clickedOverlay, + var pickRay, + clickedOverlay, i; - clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y }); //identify which overlay was clicked + if (isOnHMD) { + pickRay = Camera.computePickRay(event.x, event.y); + clickedOverlay = Overlays.findRayIntersection(pickRay).overlayID; + } else { + clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y }); + } - for (i = 0; i < buttons.length; i += 1) { //if user clicked a button + for (i = 0; i < buttons.length; i += 1) { if (clickedOverlay === buttons[i]) { deleteNotification(i); } From 958d853b565237dc2c8aa649f62a1bfdf6cbd5aa Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 22 Jan 2015 14:13:54 -0800 Subject: [PATCH 14/44] Add BatchLoader --- libraries/script-engine/src/BatchLoader.cpp | 82 +++++++++++++++++++++ libraries/script-engine/src/BatchLoader.h | 45 +++++++++++ 2 files changed, 127 insertions(+) create mode 100644 libraries/script-engine/src/BatchLoader.cpp create mode 100644 libraries/script-engine/src/BatchLoader.h diff --git a/libraries/script-engine/src/BatchLoader.cpp b/libraries/script-engine/src/BatchLoader.cpp new file mode 100644 index 0000000000..c44742c0b6 --- /dev/null +++ b/libraries/script-engine/src/BatchLoader.cpp @@ -0,0 +1,82 @@ +// +// BatchLoader.cpp +// libraries/script-engine/src +// +// Created by Ryan Huffman on 01/22/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 +// + +#include +#include + +#include +#include "BatchLoader.h" +#include + +BatchLoader::BatchLoader(const QList& urls) + : QObject(), + _finished(false), + _urls(urls.toSet()), + _started(false), + _data() { +} + +void BatchLoader::start() { + if (_started) { + return; + } + + _started = true; + QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); + for (QUrl url : _urls) { + if (url.scheme() == "http" || url.scheme() == "https" || url.scheme() == "ftp") { + QNetworkReply* reply = networkAccessManager.get(QNetworkRequest(url)); + + qDebug() << "Downloading file at" << url; + + connect(reply, &QNetworkReply::finished, [=]() { + if (reply->error()) { + emit errorLoadingFile(url); + _data.insert(url, QString()); + } else { + _data.insert(url, reply->readAll()); + } + reply->deleteLater(); + checkFinished(); + }); + + // If we end up being destroyed before the reply finishes, clean it up + connect(this, &QObject::destroyed, reply, &QObject::deleteLater); + + } else { +#ifdef _WIN32 + QString fileName = url.toString(); +#else + QString fileName = url.toLocalFile(); +#endif + + qDebug() << "Reading file at " << fileName; + + QFile scriptFile(fileName); + if (scriptFile.open(QFile::ReadOnly | QFile::Text)) { + QTextStream in(&scriptFile); + _data.insert(url, in.readAll()); + // includeContents = in.readAll(); + } else { + emit errorLoadingFile(url); + _data.insert(url, QString()); + } + } + } + checkFinished(); +} + +void BatchLoader::checkFinished() { + if (!_finished && _urls.size() == _data.size()) { + _finished = true; + emit finished(_data); + } +} diff --git a/libraries/script-engine/src/BatchLoader.h b/libraries/script-engine/src/BatchLoader.h new file mode 100644 index 0000000000..f165aafed4 --- /dev/null +++ b/libraries/script-engine/src/BatchLoader.h @@ -0,0 +1,45 @@ +// +// BatchLoader.h +// libraries/script-engine/src +// +// Created by Ryan Huffman on 01/22/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 +// + +#ifndef hifi_BatchLoader_h +#define hifi_BatchLoader_h + +#include +#include +#include +#include +#include +#include + +#include + +class BatchLoader : public QObject { + Q_OBJECT +public: + BatchLoader(const QList& urls) ; + + void start(); + bool isFinished() const { return _finished; }; + +signals: + void finished(const QMap& data); + void errorLoadingFile(QUrl url); + +private: + void checkFinished(); + + bool _finished; + bool _started; + QSet _urls; + QMap _data; +}; + +#endif // hifi_BatchLoader_h From 38b38eb063422d625afa3137ae094fb0a3984ebe Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 22 Jan 2015 14:14:21 -0800 Subject: [PATCH 15/44] Update ScriptEngine to use BatchLoader --- libraries/script-engine/src/ScriptEngine.cpp | 77 +++++++++++--------- libraries/script-engine/src/ScriptEngine.h | 1 + 2 files changed, 43 insertions(+), 35 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 0f860208f4..a505b63b69 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -31,6 +31,7 @@ #include "AnimationObject.h" #include "ArrayBufferViewClass.h" +#include "BatchLoader.h" #include "DataViewClass.h" #include "EventTypes.h" #include "MenuItemProperties.h" @@ -304,7 +305,7 @@ QScriptValue ScriptEngine::evaluate(const QString& program, const QString& fileN QScriptValue result = QScriptEngine::evaluate(program, fileName, lineNumber); if (hasUncaughtException()) { int line = uncaughtExceptionLineNumber(); - qDebug() << "Uncaught exception at (" << _fileNameString << ") line" << line << ": " << result.toString(); + qDebug() << "Uncaught exception at (" << _fileNameString << " : " << fileName << ") line" << line << ": " << result.toString(); } emit evaluationFinished(result, hasUncaughtException()); clearExceptions(); @@ -595,46 +596,52 @@ void ScriptEngine::print(const QString& message) { emit printedMessage(message); } -void ScriptEngine::include(const QString& includeFile) { - QUrl url = resolvePath(includeFile); - QString includeContents; +void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callback) { + QList urls; + for (QString file : includeFiles) { + urls.append(resolvePath(file)); + } - if (url.scheme() == "http" || url.scheme() == "https" || url.scheme() == "ftp") { - QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); - QNetworkReply* reply = networkAccessManager.get(QNetworkRequest(url)); - qDebug() << "Downloading included script at" << includeFile; - QEventLoop loop; - QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit())); - loop.exec(); - includeContents = reply->readAll(); - reply->deleteLater(); - } else { -#ifdef _WIN32 - QString fileName = url.toString(); -#else - QString fileName = url.toLocalFile(); -#endif - - QFile scriptFile(fileName); - if (scriptFile.open(QFile::ReadOnly | QFile::Text)) { - qDebug() << "Including file:" << fileName; - QTextStream in(&scriptFile); - includeContents = in.readAll(); - } else { - qDebug() << "ERROR Including file:" << fileName; - emit errorMessage("ERROR Including file:" + fileName); + BatchLoader* loader = new BatchLoader(urls); + + auto evaluateScripts = [=](const QMap& data) { + for (QUrl url : urls) { + QString contents = data[url]; + qDebug() << "About to load: " << url; + if (contents.isNull()) { + qDebug() << "Error loading file: " << url; + } else { + QScriptValue result = evaluate(contents, url.toString()); + } } - } - QScriptValue result = evaluate(includeContents); - if (hasUncaughtException()) { - int line = uncaughtExceptionLineNumber(); - qDebug() << "Uncaught exception at (" << includeFile << ") line" << line << ":" << result.toString(); - emit errorMessage("Uncaught exception at (" + includeFile + ") line" + QString::number(line) + ":" + result.toString()); - clearExceptions(); + if (callback.isFunction()) { + QScriptValue(callback).call(); + } + + loader->deleteLater(); + }; + + connect(loader, &BatchLoader::finished, this, evaluateScripts); + + // If we are destroyed before the loader completes, make sure to clean it up + connect(this, &QObject::destroyed, loader, &QObject::deleteLater); + + loader->start(); + + if (!callback.isFunction() && !loader->isFinished()) { + QEventLoop loop; + QObject::connect(loader, &BatchLoader::finished, &loop, &QEventLoop::quit); + loop.exec(); } } +void ScriptEngine::include(const QString& includeFile) { + QStringList urls; + urls.append(includeFile); + include(urls); +} + void ScriptEngine::load(const QString& loadFile) { QUrl url = resolvePath(loadFile); emit loadScript(url.toString(), false); diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 10f419937a..126e943982 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -96,6 +96,7 @@ public slots: QObject* setTimeout(const QScriptValue& function, int timeoutMS); void clearInterval(QObject* timer) { stopTimer(reinterpret_cast(timer)); } void clearTimeout(QObject* timer) { stopTimer(reinterpret_cast(timer)); } + void include(const QStringList& includeFiles, QScriptValue callback = QScriptValue()); void include(const QString& includeFile); void load(const QString& loadfile); void print(const QString& message); From 4fd5f743651479c33db04c8e29ffb7d1a16bfc10 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 22 Jan 2015 14:14:49 -0800 Subject: [PATCH 16/44] Update editEntities.js to load includes in parallel --- examples/editEntities.js | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/examples/editEntities.js b/examples/editEntities.js index 9187b624fd..f9d881f0bd 100644 --- a/examples/editEntities.js +++ b/examples/editEntities.js @@ -12,34 +12,37 @@ // HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; -Script.include("libraries/stringHelpers.js"); -Script.include("libraries/dataviewHelpers.js"); -Script.include("libraries/httpMultiPart.js"); -Script.include("libraries/modelUploader.js"); -Script.include("libraries/toolBars.js"); -Script.include("libraries/progressDialog.js"); -Script.include("libraries/entitySelectionTool.js"); +Script.include([ + "http://public.highfidelity.io/scripts/libraries/stringHelpers.js", + "http://public.highfidelity.io/scripts/libraries/dataviewHelpers.js", + "http://public.highfidelity.io/scripts/libraries/httpMultiPart.js", + "http://public.highfidelity.io/scripts/libraries/modelUploader.js", + "http://public.highfidelity.io/scripts/libraries/toolBars.js", + "http://public.highfidelity.io/scripts/libraries/progressDialog.js", + + "http://public.highfidelity.io/scripts/libraries/entitySelectionTool.js", + "http://public.highfidelity.io/scripts/libraries/ModelImporter.js", + + "http://public.highfidelity.io/scripts/libraries/ExportMenu.js", + "http://public.highfidelity.io/scripts/libraries/ToolTip.js", + + "http://public.highfidelity.io/scripts/libraries/entityPropertyDialogBox.js", + "http://public.highfidelity.io/scripts/libraries/entityCameraTool.js", + "http://public.highfidelity.io/scripts/libraries/gridTool.js", + "http://public.highfidelity.io/scripts/libraries/entityList.js", +]); + var selectionDisplay = SelectionDisplay; var selectionManager = SelectionManager; - -Script.include("libraries/ModelImporter.js"); var modelImporter = new ModelImporter(); - -Script.include("libraries/ExportMenu.js"); -Script.include("libraries/ToolTip.js"); - -Script.include("libraries/entityPropertyDialogBox.js"); var entityPropertyDialogBox = EntityPropertyDialogBox; -Script.include("libraries/entityCameraTool.js"); var cameraManager = new CameraManager(); -Script.include("libraries/gridTool.js"); var grid = Grid(); gridTool = GridTool({ horizontalGrid: grid }); -Script.include("libraries/entityList.js"); var entityListTool = EntityListTool(); var hasShownPropertiesTool = false; From b4132f1e7dd596b384d1e61c2677971a63bb3b7e Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 22 Jan 2015 14:16:36 -0800 Subject: [PATCH 17/44] Remove commented line --- libraries/script-engine/src/BatchLoader.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/script-engine/src/BatchLoader.cpp b/libraries/script-engine/src/BatchLoader.cpp index c44742c0b6..ed6591423f 100644 --- a/libraries/script-engine/src/BatchLoader.cpp +++ b/libraries/script-engine/src/BatchLoader.cpp @@ -64,7 +64,6 @@ void BatchLoader::start() { if (scriptFile.open(QFile::ReadOnly | QFile::Text)) { QTextStream in(&scriptFile); _data.insert(url, in.readAll()); - // includeContents = in.readAll(); } else { emit errorLoadingFile(url); _data.insert(url, QString()); From e27db48e262c6297d3956bb36dcfa2de7a1c9c24 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 22 Jan 2015 14:20:42 -0800 Subject: [PATCH 18/44] Remove qDebug and unnecessary include --- libraries/script-engine/src/BatchLoader.h | 2 -- libraries/script-engine/src/ScriptEngine.cpp | 1 - 2 files changed, 3 deletions(-) diff --git a/libraries/script-engine/src/BatchLoader.h b/libraries/script-engine/src/BatchLoader.h index f165aafed4..6d19b2322f 100644 --- a/libraries/script-engine/src/BatchLoader.h +++ b/libraries/script-engine/src/BatchLoader.h @@ -19,8 +19,6 @@ #include #include -#include - class BatchLoader : public QObject { Q_OBJECT public: diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index a505b63b69..246f9d3be3 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -607,7 +607,6 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac auto evaluateScripts = [=](const QMap& data) { for (QUrl url : urls) { QString contents = data[url]; - qDebug() << "About to load: " << url; if (contents.isNull()) { qDebug() << "Error loading file: " << url; } else { From 22cdf1cb226928fd806ebb415e1b191f3dace368 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 22 Jan 2015 14:24:20 -0800 Subject: [PATCH 19/44] Remove BatchLoader::errorLoadingFile and reorder properties --- libraries/script-engine/src/BatchLoader.cpp | 4 +--- libraries/script-engine/src/BatchLoader.h | 3 +-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/libraries/script-engine/src/BatchLoader.cpp b/libraries/script-engine/src/BatchLoader.cpp index ed6591423f..e2c345ce16 100644 --- a/libraries/script-engine/src/BatchLoader.cpp +++ b/libraries/script-engine/src/BatchLoader.cpp @@ -18,9 +18,9 @@ BatchLoader::BatchLoader(const QList& urls) : QObject(), + _started(false), _finished(false), _urls(urls.toSet()), - _started(false), _data() { } @@ -39,7 +39,6 @@ void BatchLoader::start() { connect(reply, &QNetworkReply::finished, [=]() { if (reply->error()) { - emit errorLoadingFile(url); _data.insert(url, QString()); } else { _data.insert(url, reply->readAll()); @@ -65,7 +64,6 @@ void BatchLoader::start() { QTextStream in(&scriptFile); _data.insert(url, in.readAll()); } else { - emit errorLoadingFile(url); _data.insert(url, QString()); } } diff --git a/libraries/script-engine/src/BatchLoader.h b/libraries/script-engine/src/BatchLoader.h index 6d19b2322f..cda040d219 100644 --- a/libraries/script-engine/src/BatchLoader.h +++ b/libraries/script-engine/src/BatchLoader.h @@ -29,13 +29,12 @@ public: signals: void finished(const QMap& data); - void errorLoadingFile(QUrl url); private: void checkFinished(); - bool _finished; bool _started; + bool _finished; QSet _urls; QMap _data; }; From 806c1851be9f36d47cb77f409d55ece6a5f6a65f Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Fri, 23 Jan 2015 16:09:45 -0800 Subject: [PATCH 20/44] add save/restore of gravity --- examples/controllers/hydra/hydraGrab.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/examples/controllers/hydra/hydraGrab.js b/examples/controllers/hydra/hydraGrab.js index 4d0b873fd2..7b65db94a3 100644 --- a/examples/controllers/hydra/hydraGrab.js +++ b/examples/controllers/hydra/hydraGrab.js @@ -74,6 +74,7 @@ function controller(wichSide) { this.positionAtGrab; this.rotationAtGrab; + this.gravityAtGrab; this.modelPositionAtGrab; this.rotationAtGrab; this.jointsIntersectingFromStart = []; @@ -125,6 +126,7 @@ function controller(wichSide) { this.entityID = entityID; this.modelURL = properties.modelURL; + this.oldModelPosition = properties.position; this.oldModelRotation = properties.rotation; this.oldModelHalfDiagonal = Vec3.length(properties.dimensions) / 2.0; @@ -133,6 +135,10 @@ function controller(wichSide) { this.rotationAtGrab = this.rotation; this.modelPositionAtGrab = properties.position; this.rotationAtGrab = properties.rotation; + this.gravityAtGrab = properties.gravity; + + Entities.editEntity(entityID, { gravity: { x: 0, y: 0, z: 0 }, velocity: { x: 0, y: 0, z: 0 } }); + this.jointsIntersectingFromStart = []; for (var i = 0; i < jointList.length; i++) { var distance = Vec3.distance(MyAvatar.getJointPosition(jointList[i]), this.oldModelPosition); @@ -145,6 +151,9 @@ function controller(wichSide) { this.release = function () { if (this.grabbing) { + + Entities.editEntity(entityID, { gravity: this.gravityAtGrab }); + jointList = MyAvatar.getJointNames(); var closestJointIndex = -1; From 901044fc537a0dfe7bfdc1c4cb8175d9f238aede Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Sun, 25 Jan 2015 21:40:16 -0800 Subject: [PATCH 21/44] First version, popcorn machine --- examples/controllers/hydra/gun.js | 4 +- examples/controllers/hydra/hydraGrab.js | 8 +- examples/controllers/hydra/paddleBall.js | 20 ++- examples/popcorn.js | 182 +++++++++++++++++++++++ 4 files changed, 201 insertions(+), 13 deletions(-) create mode 100644 examples/popcorn.js diff --git a/examples/controllers/hydra/gun.js b/examples/controllers/hydra/gun.js index 0c0740e12b..549be9bccb 100644 --- a/examples/controllers/hydra/gun.js +++ b/examples/controllers/hydra/gun.js @@ -304,10 +304,12 @@ function makePlatform(gravity, scale, size) { } function entityCollisionWithEntity(entity1, entity2, collision) { - + cTime = new Date().getTime(); + //print("Collision at " + cTime); if (((entity1.id == bulletID.id) || (entity1.id == targetID.id)) && ((entity2.id == bulletID.id) || (entity2.id == targetID.id))) { score++; + print("Hit Target!"); if (showScore) { Overlays.editOverlay(text, { text: "Score: " + score } ); } diff --git a/examples/controllers/hydra/hydraGrab.js b/examples/controllers/hydra/hydraGrab.js index 8450b15758..ff51583599 100644 --- a/examples/controllers/hydra/hydraGrab.js +++ b/examples/controllers/hydra/hydraGrab.js @@ -14,7 +14,7 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -Script.include("libraries/entityPropertyDialogBox.js"); +Script.include("../../libraries/entityPropertyDialogBox.js"); var entityPropertyDialogBox = EntityPropertyDialogBox; var LASER_WIDTH = 4; @@ -23,8 +23,8 @@ var LASER_LENGTH_FACTOR = 500; var MIN_ANGULAR_SIZE = 2; var MAX_ANGULAR_SIZE = 45; -var allowLargeModels = false; -var allowSmallModels = false; +var allowLargeModels = true; +var allowSmallModels = true; var wantEntityGlow = false; var LEFT = 0; @@ -152,7 +152,7 @@ function controller(wichSide) { this.release = function () { if (this.grabbing) { - Entities.editEntity(entityID, { gravity: this.gravityAtGrab }); + Entities.editEntity(this.entityID, { gravity: this.gravityAtGrab }); jointList = MyAvatar.getJointNames(); diff --git a/examples/controllers/hydra/paddleBall.js b/examples/controllers/hydra/paddleBall.js index 85b025e4cd..a6b0a52c5f 100644 --- a/examples/controllers/hydra/paddleBall.js +++ b/examples/controllers/hydra/paddleBall.js @@ -16,12 +16,14 @@ var PADDLE_THICKNESS = 0.06; var PADDLE_COLOR = { red: 184, green: 134, blue: 11 }; var BALL_COLOR = { red: 255, green: 0, blue: 0 }; var LINE_COLOR = { red: 255, green: 255, blue: 0 }; -var PADDLE_OFFSET = { x: 0.05, y: 0.0, z: 0.0 }; +var PADDLE_BOX_OFFSET = { x: 0.05, y: 0.0, z: 0.0 }; +var HOLD_POSITION_OFFSET = { x: -0.2, y: 0.0, z: -0.25 }; +var PADDLE_ORIENTATION = Quat.fromPitchYawRollDegrees(0,0,0); var GRAVITY = 0.0; var SPRING_FORCE = 15.0; var lastSoundTime = 0; var gameOn = false; -var leftHanded = false; +var leftHanded = true; var controllerID; if (leftHanded) { @@ -73,7 +75,7 @@ function createEntities() { modelURL = "http://public.highfidelity.io/models/attachments/pong_paddle.fbx"; paddleModel = Entities.addEntity( { type: "Model", - position: Vec3.sum(Controller.getSpatialControlPosition(controllerID), PADDLE_OFFSET), + position: Vec3.sum(Controller.getSpatialControlPosition(controllerID), PADDLE_BOX_OFFSET), dimensions: { x: PADDLE_SIZE * 1.5, y: PADDLE_THICKNESS, z: PADDLE_SIZE * 1.25 }, color: PADDLE_COLOR, gravity: { x: 0, y: 0, z: 0 }, @@ -120,18 +122,20 @@ function update(deltaTime) { if (!ball.isKnownID) { ball = Entities.identifyEntity(ball); } else { + var paddleWorldOrientation = Quat.multiply(Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(controllerID)), PADDLE_ORIENTATION); + var holdPosition = Vec3.sum(palmPosition, Vec3.multiplyQbyV(paddleWorldOrientation, HOLD_POSITION_OFFSET)); var props = Entities.getEntityProperties(ball); - var spring = Vec3.subtract(palmPosition, props.position); - var paddleWorldOrientation = Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(controllerID)); + var spring = Vec3.subtract(holdPosition, props.position); var springLength = Vec3.length(spring); + spring = Vec3.normalize(spring); var ballVelocity = Vec3.sum(props.velocity, Vec3.multiply(springLength * SPRING_FORCE * deltaTime, spring)); Entities.editEntity(ball, { velocity: ballVelocity }); - Overlays.editOverlay(line, { start: props.position, end: palmPosition }); - Entities.editEntity(paddle, { position: palmPosition, + Overlays.editOverlay(line, { start: props.position, end: holdPosition }); + Entities.editEntity(paddle, { position: holdPosition, velocity: Controller.getSpatialControlVelocity(controllerID), rotation: paddleWorldOrientation }); - Entities.editEntity(paddleModel, { position: Vec3.sum(palmPosition, Vec3.multiplyQbyV(paddleWorldOrientation, PADDLE_OFFSET)), + Entities.editEntity(paddleModel, { position: Vec3.sum(holdPosition, Vec3.multiplyQbyV(paddleWorldOrientation, PADDLE_BOX_OFFSET)), velocity: Controller.getSpatialControlVelocity(controllerID), rotation: paddleWorldOrientation }); } diff --git a/examples/popcorn.js b/examples/popcorn.js new file mode 100644 index 0000000000..567953ac60 --- /dev/null +++ b/examples/popcorn.js @@ -0,0 +1,182 @@ +// +// popcorn.js +// examples +// +// Created by Philip Rosedale on January 25, 2014 +// Copyright 2015 High Fidelity, Inc. +// +// Creates a bunch of physical balls trapped in a box with a rotating wall in the middle that smacks them around, +// and a periodic 'pop' force that shoots them into the air. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +var BALL_SIZE = 0.07; +var WALL_THICKNESS = 0.10; +var SCALE = 1.0; + +var GRAVITY = -1.0; +var LIFETIME = 600; +var DAMPING = 0.50; + +var center = Vec3.sum(MyAvatar.position, Vec3.multiply(SCALE * 3.0, Quat.getFront(Camera.getOrientation()))); + +var floor = Entities.addEntity( + { type: "Box", + position: Vec3.subtract(center, { x: 0, y: SCALE / 2.0, z: 0 }), + dimensions: { x: SCALE, y: WALL_THICKNESS, z: SCALE }, + color: { red: 0, green: 255, blue: 0 }, + gravity: { x: 0, y: 0, z: 0 }, + ignoreCollisions: false, + lifetime: LIFETIME }); + +var ceiling = Entities.addEntity( + { type: "Box", + position: Vec3.sum(center, { x: 0, y: SCALE / 2.0, z: 0 }), + dimensions: { x: SCALE, y: WALL_THICKNESS, z: SCALE }, + color: { red: 128, green: 128, blue: 128 }, + gravity: { x: 0, y: 0, z: 0 }, + ignoreCollisions: false, + visible: true, + lifetime: LIFETIME }); + +var wall1 = Entities.addEntity( + { type: "Box", + position: Vec3.sum(center, { x: SCALE / 2.0, y: 0, z: 0 }), + dimensions: { x: WALL_THICKNESS, y: SCALE, z: SCALE }, + color: { red: 0, green: 255, blue: 0 }, + gravity: { x: 0, y: 0, z: 0 }, + ignoreCollisions: false, + visible: false, + lifetime: LIFETIME }); + +var wall2 = Entities.addEntity( + { type: "Box", + position: Vec3.subtract(center, { x: SCALE / 2.0, y: 0, z: 0 }), + dimensions: { x: WALL_THICKNESS, y: SCALE, z: SCALE }, + color: { red: 0, green: 255, blue: 0 }, + gravity: { x: 0, y: 0, z: 0 }, + ignoreCollisions: false, + visible: false, + lifetime: LIFETIME }); + +var wall3 = Entities.addEntity( + { type: "Box", + position: Vec3.subtract(center, { x: 0, y: 0, z: SCALE / 2.0 }), + dimensions: { x: SCALE, y: SCALE, z: WALL_THICKNESS }, + color: { red: 0, green: 255, blue: 0 }, + gravity: { x: 0, y: 0, z: 0 }, + ignoreCollisions: false, + visible: false, + lifetime: LIFETIME }); + +var wall4 = Entities.addEntity( + { type: "Box", + position: Vec3.sum(center, { x: 0, y: 0, z: SCALE / 2.0 }), + dimensions: { x: SCALE, y: SCALE, z: WALL_THICKNESS }, + color: { red: 0, green: 255, blue: 0 }, + gravity: { x: 0, y: 0, z: 0 }, + ignoreCollisions: false, + visible: false, + lifetime: LIFETIME }); + +var corner1 = Entities.addEntity( + { type: "Box", + position: Vec3.sum(center, { x: -SCALE / 2.0, y: 0, z: SCALE / 2.0 }), + dimensions: { x: WALL_THICKNESS, y: SCALE, z: WALL_THICKNESS }, + color: { red: 128, green: 128, blue: 128 }, + ignoreCollisions: false, + visible: true, + lifetime: LIFETIME }); + +var corner2 = Entities.addEntity( + { type: "Box", + position: Vec3.sum(center, { x: -SCALE / 2.0, y: 0, z: -SCALE / 2.0 }), + dimensions: { x: WALL_THICKNESS, y: SCALE, z: WALL_THICKNESS }, + color: { red: 128, green: 128, blue: 128 }, + ignoreCollisions: false, + visible: true, + lifetime: LIFETIME }); + +var corner3 = Entities.addEntity( + { type: "Box", + position: Vec3.sum(center, { x: SCALE / 2.0, y: 0, z: SCALE / 2.0 }), + dimensions: { x: WALL_THICKNESS, y: SCALE, z: WALL_THICKNESS }, + color: { red: 128, green: 128, blue: 128 }, + ignoreCollisions: false, + visible: true, + lifetime: LIFETIME }); + +var corner4 = Entities.addEntity( + { type: "Box", + position: Vec3.sum(center, { x: SCALE / 2.0, y: 0, z: -SCALE / 2.0 }), + dimensions: { x: WALL_THICKNESS, y: SCALE, z: WALL_THICKNESS }, + color: { red: 128, green: 128, blue: 128 }, + ignoreCollisions: false, + visible: true, + lifetime: LIFETIME }); + +var spinner = Entities.addEntity( + { type: "Box", + position: center, + dimensions: { x: SCALE / 1.5, y: SCALE / 3.0, z: SCALE / 8.0 }, + color: { red: 255, green: 0, blue: 0 }, + angularVelocity: { x: 0, y: 360, z: 0 }, + angularDamping: 0.0, + gravity: { x: 0, y: 0, z: 0 }, + ignoreCollisions: false, + visible: true, + lifetime: LIFETIME }); + +var NUM_BALLS = 70; + +balls = []; + +for (var i = 0; i < NUM_BALLS; i++) { + balls.push(Entities.addEntity( + { type: "Sphere", + position: { x: center.x + (Math.random() - 0.5) * (SCALE - BALL_SIZE - WALL_THICKNESS), + y: center.y + (Math.random() - 0.5) * (SCALE - BALL_SIZE - WALL_THICKNESS) , + z: center.z + (Math.random() - 0.5) * (SCALE - BALL_SIZE - WALL_THICKNESS) }, + dimensions: { x: BALL_SIZE, y: BALL_SIZE, z: BALL_SIZE }, + color: { red: Math.random() * 255, green: Math.random() * 255, blue: Math.random() * 255 }, + gravity: { x: 0, y: GRAVITY, z: 0 }, + ignoreCollisions: false, + damping: DAMPING, + lifetime: LIFETIME, + collisionsWillMove: true })); +} + +var VEL_MAG = 2.0; +var CHANCE_OF_POP = 0.007; // 0.01; +function update(deltaTime) { + for (var i = 0; i < NUM_BALLS; i++) { + if (Math.random() < CHANCE_OF_POP) { + Entities.editEntity(balls[i], { velocity: { x: (Math.random() - 0.5) * VEL_MAG, y: Math.random() * VEL_MAG, z: (Math.random() - 0.5) * VEL_MAG }}); + } + } + +} + + +function scriptEnding() { + Entities.deleteEntity(wall1); + Entities.deleteEntity(wall2); + Entities.deleteEntity(wall3); + Entities.deleteEntity(wall4); + Entities.deleteEntity(corner1); + Entities.deleteEntity(corner2); + Entities.deleteEntity(corner3); + Entities.deleteEntity(corner4); + Entities.deleteEntity(floor); + Entities.deleteEntity(ceiling); + Entities.deleteEntity(spinner); + + for (var i = 0; i < NUM_BALLS; i++) { + Entities.deleteEntity(balls[i]); + } +} + +Script.scriptEnding.connect(scriptEnding); +Script.update.connect(update); \ No newline at end of file From 67632fa589399da975762d83a0dd72abe5f9ff6c Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Sun, 25 Jan 2015 21:51:12 -0800 Subject: [PATCH 22/44] remove debug --- examples/controllers/hydra/gun.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/examples/controllers/hydra/gun.js b/examples/controllers/hydra/gun.js index 549be9bccb..bd6cf6f9f1 100644 --- a/examples/controllers/hydra/gun.js +++ b/examples/controllers/hydra/gun.js @@ -304,8 +304,6 @@ function makePlatform(gravity, scale, size) { } function entityCollisionWithEntity(entity1, entity2, collision) { - cTime = new Date().getTime(); - //print("Collision at " + cTime); if (((entity1.id == bulletID.id) || (entity1.id == targetID.id)) && ((entity2.id == bulletID.id) || (entity2.id == targetID.id))) { score++; From 09527cc2648fd074eded39814359c18227a1ca49 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Sun, 25 Jan 2015 21:52:08 -0800 Subject: [PATCH 23/44] hit target --- examples/controllers/hydra/gun.js | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/controllers/hydra/gun.js b/examples/controllers/hydra/gun.js index bd6cf6f9f1..b8fc15d75f 100644 --- a/examples/controllers/hydra/gun.js +++ b/examples/controllers/hydra/gun.js @@ -307,7 +307,6 @@ function entityCollisionWithEntity(entity1, entity2, collision) { if (((entity1.id == bulletID.id) || (entity1.id == targetID.id)) && ((entity2.id == bulletID.id) || (entity2.id == targetID.id))) { score++; - print("Hit Target!"); if (showScore) { Overlays.editOverlay(text, { text: "Score: " + score } ); } From c8a755df1bb902f96dfa42ffa9dd746d3a130181 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Mon, 26 Jan 2015 08:32:23 -0800 Subject: [PATCH 24/44] Remove unused variable --- examples/notifications.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/notifications.js b/examples/notifications.js index 525d438fe6..1b512634d7 100644 --- a/examples/notifications.js +++ b/examples/notifications.js @@ -447,8 +447,7 @@ function isStartingUp() { // Triggers notification if a user logs on or off function onOnlineUsersChanged(users) { - var user, - i; + var i; if (!isStartingUp()) { // Skip user notifications at startup. for (i = 0; i < users.length; i += 1) { From bfdbe2e675b92a2fcc4341b5098889612e2a2254 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Mon, 26 Jan 2015 16:25:07 -0800 Subject: [PATCH 25/44] Detach both guns on shutdown --- examples/controllers/hydra/gun.js | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/controllers/hydra/gun.js b/examples/controllers/hydra/gun.js index b8fc15d75f..b5d015637c 100644 --- a/examples/controllers/hydra/gun.js +++ b/examples/controllers/hydra/gun.js @@ -500,6 +500,7 @@ function scriptEnding() { Overlays.deleteOverlay(pointer[1]); Overlays.deleteOverlay(text); MyAvatar.detachOne(gunModel); + MyAvatar.detachOne(gunModel); clearPose(); } From a1babe3a333eed514a1d620e1d2c3fc5a0060c73 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Mon, 26 Jan 2015 17:20:31 -0800 Subject: [PATCH 26/44] Put paddle exactly in hand, add animation of fingers gripping paddle --- examples/controllers/hydra/paddleBall.js | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/examples/controllers/hydra/paddleBall.js b/examples/controllers/hydra/paddleBall.js index a6b0a52c5f..8c9af68498 100644 --- a/examples/controllers/hydra/paddleBall.js +++ b/examples/controllers/hydra/paddleBall.js @@ -10,6 +10,12 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; + +hitSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Collisions-ballhitsandcatches/billiards/collision1.wav"); +var rightHandAnimation = HIFI_PUBLIC_BUCKET + "animations/RightHandAnimPhilip.fbx"; +var leftHandAnimation = HIFI_PUBLIC_BUCKET + "animations/LeftHandAnimPhilip.fbx"; + var BALL_SIZE = 0.08; var PADDLE_SIZE = 0.20; var PADDLE_THICKNESS = 0.06; @@ -17,7 +23,8 @@ var PADDLE_COLOR = { red: 184, green: 134, blue: 11 }; var BALL_COLOR = { red: 255, green: 0, blue: 0 }; var LINE_COLOR = { red: 255, green: 255, blue: 0 }; var PADDLE_BOX_OFFSET = { x: 0.05, y: 0.0, z: 0.0 }; -var HOLD_POSITION_OFFSET = { x: -0.2, y: 0.0, z: -0.25 }; + +var HOLD_POSITION_OFFSET = { x: -0.15, y: 0.05, z: -0.05 }; var PADDLE_ORIENTATION = Quat.fromPitchYawRollDegrees(0,0,0); var GRAVITY = 0.0; var SPRING_FORCE = 15.0; @@ -33,8 +40,7 @@ if (leftHanded) { } -HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; -hitSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Collisions-ballhitsandcatches/billiards/collision1.wav"); + var screenSize = Controller.getViewportDimensions(); var offButton = Overlays.addOverlay("image", { @@ -92,6 +98,9 @@ function createEntities() { alpha: 1, visible: true, lineWidth: 2 }); + + MyAvatar.stopAnimation(leftHanded ? leftHandAnimation: rightHandAnimation); + MyAvatar.startAnimation(leftHanded ? leftHandAnimation: rightHandAnimation, 15.0, 1.0, false, true, 0.0, 6); } function deleteEntities() { @@ -99,6 +108,7 @@ function deleteEntities() { Entities.deleteEntity(paddle); Entities.deleteEntity(paddleModel); Overlays.deleteOverlay(line); + MyAvatar.stopAnimation(leftHanded ? leftHandAnimation: rightHandAnimation); } function update(deltaTime) { @@ -123,7 +133,9 @@ function update(deltaTime) { ball = Entities.identifyEntity(ball); } else { var paddleWorldOrientation = Quat.multiply(Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(controllerID)), PADDLE_ORIENTATION); - var holdPosition = Vec3.sum(palmPosition, Vec3.multiplyQbyV(paddleWorldOrientation, HOLD_POSITION_OFFSET)); + var holdPosition = Vec3.sum(leftHanded ? MyAvatar.getLeftPalmPosition() : MyAvatar.getRightPalmPosition(), + Vec3.multiplyQbyV(paddleWorldOrientation, HOLD_POSITION_OFFSET)); + var props = Entities.getEntityProperties(ball); var spring = Vec3.subtract(holdPosition, props.position); var springLength = Vec3.length(spring); @@ -168,6 +180,7 @@ function scriptEnding() { deleteEntities(); } Overlays.deleteOverlay(offButton); + MyAvatar.stopAnimation(leftHanded ? leftHandAnimation: rightHandAnimation); } Entities.entityCollisionWithEntity.connect(entityCollisionWithEntity); From e0c9af853c6e132979df2c0c7c931aab94d009f6 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Mon, 26 Jan 2015 20:19:46 -0800 Subject: [PATCH 27/44] remove 'STICKS' mode, add drop line to better tell what something you are holding is above --- examples/controllers/hydra/hydraGrab.js | 173 +++++++++--------------- 1 file changed, 67 insertions(+), 106 deletions(-) diff --git a/examples/controllers/hydra/hydraGrab.js b/examples/controllers/hydra/hydraGrab.js index 2ebe4eb9b6..9f250f9208 100644 --- a/examples/controllers/hydra/hydraGrab.js +++ b/examples/controllers/hydra/hydraGrab.js @@ -28,13 +28,12 @@ var RIGHT = 1; var jointList = MyAvatar.getJointNames(); -var STICKS = 0; -var MAPPED = 1; -var mode = STICKS; +var LASER_WIDTH = 3; +var LASER_COLOR = { red: 50, green: 150, blue: 200 }; +var DROP_COLOR = { red: 200, green: 200, blue: 200 }; +var DROP_WIDTH = 4; +var DROP_DISTANCE = 5.0; -var LASER_WIDTH = 4; -var LASER_COLOR = [{ red: 200, green: 150, blue: 50 }, // STICKS - { red: 50, green: 150, blue: 200 }]; // MAPPED var LASER_LENGTH_FACTOR = 500; var lastAccurateIntersection = null; @@ -115,13 +114,21 @@ function controller(wichSide) { this.laser = Overlays.addOverlay("line3d", { start: { x: 0, y: 0, z: 0 }, end: { x: 0, y: 0, z: 0 }, - color: LASER_COLOR[mode], + color: LASER_COLOR, alpha: 1, visible: false, lineWidth: LASER_WIDTH, anchor: "MyAvatar" }); + this.dropLine = Overlays.addOverlay("line3d", { + start: { x: 0, y: 0, z: 0 }, + end: { x: 0, y: 0, z: 0 }, + color: DROP_COLOR, + alpha: 1, + visible: false, + lineWidth: DROP_WIDTH }); + this.guideScale = 0.02; this.ball = Overlays.addOverlay("sphere", { position: { x: 0, y: 0, z: 0 }, @@ -180,6 +187,7 @@ function controller(wichSide) { } } this.showLaser(false); + Overlays.editOverlay(this.dropLine, { visible: true }); } this.release = function () { @@ -225,6 +233,8 @@ function controller(wichSide) { Entities.deleteEntity(this.entityID); } } + + Overlays.editOverlay(this.dropLine, { visible: false }); } this.grabbing = false; @@ -297,7 +307,6 @@ function controller(wichSide) { end: endPosition }); - Overlays.editOverlay(this.ball, { position: endPosition }); @@ -309,7 +318,7 @@ function controller(wichSide) { start: Vec3.sum(endPosition, Vec3.multiply(this.up, 2 * this.guideScale)), end: Vec3.sum(endPosition, Vec3.multiply(this.up, -2 * this.guideScale)) }); - this.showLaser(!this.grabbing || mode == STICKS); + this.showLaser(!this.grabbing); if (this.glowedIntersectingModel.isKnownID) { Entities.editEntity(this.glowedIntersectingModel, { glowLevel: 0.0 }); @@ -352,49 +361,33 @@ function controller(wichSide) { } var newPosition; var newRotation; + + var forward = Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -1 }); + var d = Vec3.dot(forward, MyAvatar.position); - switch (mode) { - case STICKS: - newPosition = Vec3.sum(this.palmPosition, - Vec3.multiply(this.front, this.x)); - newPosition = Vec3.sum(newPosition, - Vec3.multiply(this.up, this.y)); - newPosition = Vec3.sum(newPosition, - Vec3.multiply(this.right, this.z)); + var factor1 = Vec3.dot(forward, this.positionAtGrab) - d; + var factor2 = Vec3.dot(forward, this.modelPositionAtGrab) - d; + var vector = Vec3.subtract(this.palmPosition, this.positionAtGrab); - - newRotation = Quat.multiply(this.rotation, - Quat.inverse(this.oldRotation)); - newRotation = Quat.multiply(newRotation, - this.oldModelRotation); - break; - case MAPPED: - var forward = Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -1 }); - var d = Vec3.dot(forward, MyAvatar.position); - - var factor1 = Vec3.dot(forward, this.positionAtGrab) - d; - var factor2 = Vec3.dot(forward, this.modelPositionAtGrab) - d; - var vector = Vec3.subtract(this.palmPosition, this.positionAtGrab); - - if (factor2 < 0) { - factor2 = 0; - } - if (factor1 <= 0) { - factor1 = 1; - factor2 = 1; - } - - newPosition = Vec3.sum(this.modelPositionAtGrab, - Vec3.multiply(vector, - factor2 / factor1)); - - newRotation = Quat.multiply(this.rotation, - Quat.inverse(this.rotationAtGrab)); - newRotation = Quat.multiply(newRotation, newRotation); - newRotation = Quat.multiply(newRotation, - this.modelRotationAtGrab); - break; + if (factor2 < 0) { + factor2 = 0; } + if (factor1 <= 0) { + factor1 = 1; + factor2 = 1; + } + + newPosition = Vec3.sum(this.modelPositionAtGrab, + Vec3.multiply(vector, + factor2 / factor1)); + + newRotation = Quat.multiply(this.rotation, + Quat.inverse(this.rotationAtGrab)); + newRotation = Quat.multiply(newRotation, newRotation); + newRotation = Quat.multiply(newRotation, + this.modelRotationAtGrab); + + Entities.editEntity(this.entityID, { position: newPosition, rotation: newRotation @@ -402,6 +395,8 @@ function controller(wichSide) { this.oldModelRotation = newRotation; this.oldModelPosition = newPosition; + Overlays.editOverlay(this.dropLine, { start: newPosition, end: Vec3.sum(newPosition, { x: 0, y: -DROP_DISTANCE, z: 0 }) }); + var indicesToRemove = []; for (var i = 0; i < this.jointsIntersectingFromStart.length; ++i) { var distance = Vec3.distance(MyAvatar.getJointPosition(this.jointsIntersectingFromStart[i]), this.oldModelPosition); @@ -437,15 +432,6 @@ function controller(wichSide) { this.triggerValue = Controller.getTriggerValue(this.trigger); var bumperValue = Controller.isButtonPressed(this.bumper); - if (bumperValue && !this.bumperValue) { - if (mode === STICKS) { - mode = MAPPED; - } else if (mode === MAPPED) { - mode = STICKS; - } - Overlays.editOverlay(leftController.laser, { color: LASER_COLOR[mode] }); - Overlays.editOverlay(rightController.laser, { color: LASER_COLOR[mode] }); - } this.bumperValue = bumperValue; @@ -563,55 +549,31 @@ function moveEntities() { var rotation = leftController.oldModelRotation; var ratio = 1; + var u = Vec3.normalize(Vec3.subtract(rightController.oldPalmPosition, leftController.oldPalmPosition)); + var v = Vec3.normalize(Vec3.subtract(rightController.palmPosition, leftController.palmPosition)); - switch (mode) { - case STICKS: - var oldLeftPoint = Vec3.sum(leftController.oldPalmPosition, Vec3.multiply(leftController.oldFront, leftController.x)); - var oldRightPoint = Vec3.sum(rightController.oldPalmPosition, Vec3.multiply(rightController.oldFront, rightController.x)); - - var oldMiddle = Vec3.multiply(Vec3.sum(oldLeftPoint, oldRightPoint), 0.5); - var oldLength = Vec3.length(Vec3.subtract(oldLeftPoint, oldRightPoint)); - - - var leftPoint = Vec3.sum(leftController.palmPosition, Vec3.multiply(leftController.front, leftController.x)); - var rightPoint = Vec3.sum(rightController.palmPosition, Vec3.multiply(rightController.front, rightController.x)); - - var middle = Vec3.multiply(Vec3.sum(leftPoint, rightPoint), 0.5); - var length = Vec3.length(Vec3.subtract(leftPoint, rightPoint)); - - - ratio = length / oldLength; - newPosition = Vec3.sum(middle, - Vec3.multiply(Vec3.subtract(leftController.oldModelPosition, oldMiddle), ratio)); - break; - case MAPPED: - var u = Vec3.normalize(Vec3.subtract(rightController.oldPalmPosition, leftController.oldPalmPosition)); - var v = Vec3.normalize(Vec3.subtract(rightController.palmPosition, leftController.palmPosition)); - - var cos_theta = Vec3.dot(u, v); - if (cos_theta > 1) { - cos_theta = 1; - } - var angle = Math.acos(cos_theta) / Math.PI * 180; - if (angle < 0.1) { - return; - - } - var w = Vec3.normalize(Vec3.cross(u, v)); - - rotation = Quat.multiply(Quat.angleAxis(angle, w), leftController.oldModelRotation); - - - leftController.positionAtGrab = leftController.palmPosition; - leftController.rotationAtGrab = leftController.rotation; - leftController.modelPositionAtGrab = leftController.oldModelPosition; - leftController.modelRotationAtGrab = rotation; - rightController.positionAtGrab = rightController.palmPosition; - rightController.rotationAtGrab = rightController.rotation; - rightController.modelPositionAtGrab = rightController.oldModelPosition; - rightController.modelRotationAtGrab = rotation; - break; + var cos_theta = Vec3.dot(u, v); + if (cos_theta > 1) { + cos_theta = 1; } + var angle = Math.acos(cos_theta) / Math.PI * 180; + if (angle < 0.1) { + return; + } + var w = Vec3.normalize(Vec3.cross(u, v)); + + rotation = Quat.multiply(Quat.angleAxis(angle, w), leftController.oldModelRotation); + + + leftController.positionAtGrab = leftController.palmPosition; + leftController.rotationAtGrab = leftController.rotation; + leftController.modelPositionAtGrab = leftController.oldModelPosition; + leftController.modelRotationAtGrab = rotation; + rightController.positionAtGrab = rightController.palmPosition; + rightController.rotationAtGrab = rightController.rotation; + rightController.modelPositionAtGrab = rightController.oldModelPosition; + rightController.modelRotationAtGrab = rotation; + Entities.editEntity(leftController.entityID, { position: newPosition, rotation: rotation, @@ -621,7 +583,6 @@ function moveEntities() { y: leftController.oldModelHalfDiagonal * ratio, z: leftController.oldModelHalfDiagonal * ratio } - }); leftController.oldModelPosition = newPosition; leftController.oldModelRotation = rotation; From 005cd2ba53a70c639b73f8299e72d9c2871d3420 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Mon, 26 Jan 2015 20:44:47 -0800 Subject: [PATCH 28/44] Add block building toolset, first version --- examples/blocks.js | 114 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 examples/blocks.js diff --git a/examples/blocks.js b/examples/blocks.js new file mode 100644 index 0000000000..69d4dbc4d4 --- /dev/null +++ b/examples/blocks.js @@ -0,0 +1,114 @@ +// +// Blocks.js +// +// Created by Philip Rosedale on January 26, 2015 +// Copyright 2015 High Fidelity, Inc. +// +// Create a bunch of building blocks and drop them onto a playing surface in front of you. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +var FLOOR_SIZE = 5.0; +var FLOOR_THICKNESS = 0.10; +var EDGE_THICKESS = 0.25; +var SCALE = 0.25; + +var GRAVITY = -1.0; +var LIFETIME = 6000; +var DAMPING = 0.50; + +var blockTypes = []; +blockTypes.push({ x: 1, y: 1, z: 1, red: 255, green: 0, blue: 0 }); +blockTypes.push({ x: 1, y: 1, z: 2, red: 0, green: 255, blue: 0 }); +blockTypes.push({ x: 1, y: 2, z: 5, red: 0, green: 0, blue: 255 }); +blockTypes.push({ x: 1, y: 2, z: 2, red: 255, green: 255, blue: 0 }); + + +var center = Vec3.sum(MyAvatar.position, Vec3.multiply(FLOOR_SIZE * 2.0, Quat.getFront(Camera.getOrientation()))); + +var floor = Entities.addEntity( + { type: "Box", + position: Vec3.subtract(center, { x: 0, y: SCALE / 2.0, z: 0 }), + dimensions: { x: FLOOR_SIZE, y: FLOOR_THICKNESS, z: FLOOR_SIZE }, + color: { red: 128, green: 128, blue: 128 }, + gravity: { x: 0, y: 0, z: 0 }, + ignoreCollisions: false, + locked: true, + lifetime: LIFETIME }); + +var edge1 = Entities.addEntity( + { type: "Box", + position: Vec3.sum(center, { x: FLOOR_SIZE / 2.0, y: FLOOR_THICKNESS / 2.0, z: 0 }), + dimensions: { x: EDGE_THICKESS, y: EDGE_THICKESS, z: FLOOR_SIZE }, + color: { red: 128, green: 128, blue: 128 }, + gravity: { x: 0, y: 0, z: 0 }, + ignoreCollisions: false, + visible: true, + lifetime: LIFETIME }); + +var edge2 = Entities.addEntity( + { type: "Box", + position: Vec3.sum(center, { x: -FLOOR_SIZE / 2.0, y: FLOOR_THICKNESS / 2.0, z: 0 }), + dimensions: { x: EDGE_THICKESS, y: EDGE_THICKESS, z: FLOOR_SIZE }, + color: { red: 128, green: 128, blue: 128 }, + gravity: { x: 0, y: 0, z: 0 }, + ignoreCollisions: false, + visible: true, + lifetime: LIFETIME }); + +var edge3 = Entities.addEntity( + { type: "Box", + position: Vec3.sum(center, { x: 0, y: FLOOR_THICKNESS / 2.0, z: -FLOOR_SIZE / 2.0 }), + dimensions: { x: FLOOR_SIZE, y: EDGE_THICKESS, z: EDGE_THICKESS }, + color: { red: 128, green: 128, blue: 128 }, + gravity: { x: 0, y: 0, z: 0 }, + ignoreCollisions: false, + visible: true, + lifetime: LIFETIME }); + +var edge4 = Entities.addEntity( + { type: "Box", + position: Vec3.sum(center, { x: 0, y: FLOOR_THICKNESS / 2.0, z: FLOOR_SIZE / 2.0 }), + dimensions: { x: FLOOR_SIZE, y: EDGE_THICKESS, z: EDGE_THICKESS }, + color: { red: 128, green: 128, blue: 128 }, + gravity: { x: 0, y: 0, z: 0 }, + ignoreCollisions: false, + visible: true, + lifetime: LIFETIME }); + +var NUM_BLOCKS = 20; +var DROP_HEIGHT = FLOOR_SIZE / 3; + +blocks = []; + +for (var i = 0; i < NUM_BLOCKS; i++) { + var which = Math.floor(Math.random() * blockTypes.length); + var type = blockTypes[which]; + blocks.push(Entities.addEntity( + { type: "Box", + position: { x: center.x + (Math.random() - 0.5) * (FLOOR_SIZE * 0.75), + y: center.y + DROP_HEIGHT, + z: center.z + (Math.random() - 0.5) * (FLOOR_SIZE * 0.75) }, + dimensions: { x: type.x * SCALE, y: type.y * SCALE, z: type.z * SCALE }, + color: { red: type.red, green: type.green, blue: type.blue }, + gravity: { x: 0, y: GRAVITY, z: 0 }, + ignoreCollisions: false, + damping: DAMPING, + lifetime: LIFETIME, + collisionsWillMove: true })); +} + +function scriptEnding() { + Entities.deleteEntity(edge1); + Entities.deleteEntity(edge2); + Entities.deleteEntity(edge3); + Entities.deleteEntity(edge4); + Entities.deleteEntity(floor); + + for (var i = 0; i < NUM_BLOCKS; i++) { + Entities.deleteEntity(blocks[i]); + } +} + +Script.scriptEnding.connect(scriptEnding); \ No newline at end of file From 8b5a45188695f19755d7ceb6ab81175b420aeaf6 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Mon, 26 Jan 2015 21:09:21 -0800 Subject: [PATCH 29/44] Grab now correctly adds linear velocity so that you can grab and throw things --- examples/controllers/hydra/hydraGrab.js | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/examples/controllers/hydra/hydraGrab.js b/examples/controllers/hydra/hydraGrab.js index 9f250f9208..5ba8613ad9 100644 --- a/examples/controllers/hydra/hydraGrab.js +++ b/examples/controllers/hydra/hydraGrab.js @@ -36,6 +36,8 @@ var DROP_DISTANCE = 5.0; var LASER_LENGTH_FACTOR = 500; +var velocity = { x: 0, y: 0, z: 0 }; + var lastAccurateIntersection = null; var accurateIntersections = 0; var totalIntersections = 0; @@ -350,7 +352,7 @@ function controller(wichSide) { Overlays.editOverlay(this.leftRight, { visible: show }); Overlays.editOverlay(this.topDown, { visible: show }); } - this.moveEntity = function () { + this.moveEntity = function (deltaTime) { if (this.grabbing) { if (!this.entityID.isKnownID) { print("Unknown grabbed ID " + this.entityID.id + ", isKnown: " + this.entityID.isKnownID); @@ -386,11 +388,13 @@ function controller(wichSide) { newRotation = Quat.multiply(newRotation, newRotation); newRotation = Quat.multiply(newRotation, this.modelRotationAtGrab); - + velocity = Vec3.multiply(1.0 / deltaTime, Vec3.subtract(newPosition, this.oldModelPosition)); + Entities.editEntity(this.entityID, { position: newPosition, - rotation: newRotation + rotation: newRotation, + velocity: velocity }); this.oldModelRotation = newRotation; this.oldModelPosition = newPosition; @@ -543,7 +547,7 @@ function controller(wichSide) { var leftController = new controller(LEFT); var rightController = new controller(RIGHT); -function moveEntities() { +function moveEntities(deltaTime) { if (leftController.grabbing && rightController.grabbing && rightController.entityID.id == leftController.entityID.id) { var newPosition = leftController.oldModelPosition; var rotation = leftController.oldModelRotation; @@ -593,8 +597,8 @@ function moveEntities() { rightController.oldModelHalfDiagonal *= ratio; return; } - leftController.moveEntity(); - rightController.moveEntity(); + leftController.moveEntity(deltaTime); + rightController.moveEntity(deltaTime); } var hydraConnected = false; @@ -612,7 +616,7 @@ function checkController(deltaTime) { leftController.update(); rightController.update(); - moveEntities(); + moveEntities(deltaTime); } else { if (hydraConnected) { hydraConnected = false; From e8f4783beadc8f158dad398e819d6cc1b392e259 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Mon, 26 Jan 2015 22:06:59 -0800 Subject: [PATCH 30/44] border, new size for blocks, smoother hydra grab action --- examples/blocks.js | 14 +++++++++----- examples/controllers/hydra/hydraGrab.js | 23 +++++++---------------- 2 files changed, 16 insertions(+), 21 deletions(-) diff --git a/examples/blocks.js b/examples/blocks.js index 69d4dbc4d4..30c2126096 100644 --- a/examples/blocks.js +++ b/examples/blocks.js @@ -9,11 +9,14 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -var FLOOR_SIZE = 5.0; +var FLOOR_SIZE = 7.5; var FLOOR_THICKNESS = 0.10; var EDGE_THICKESS = 0.25; var SCALE = 0.25; +var NUM_BLOCKS = 25; +var DROP_HEIGHT = SCALE * 8.0; + var GRAVITY = -1.0; var LIFETIME = 6000; var DAMPING = 0.50; @@ -23,7 +26,7 @@ blockTypes.push({ x: 1, y: 1, z: 1, red: 255, green: 0, blue: 0 }); blockTypes.push({ x: 1, y: 1, z: 2, red: 0, green: 255, blue: 0 }); blockTypes.push({ x: 1, y: 2, z: 5, red: 0, green: 0, blue: 255 }); blockTypes.push({ x: 1, y: 2, z: 2, red: 255, green: 255, blue: 0 }); - +blockTypes.push({ x: 1, y: 1, z: 5, red: 0, green: 255, blue: 255 }); var center = Vec3.sum(MyAvatar.position, Vec3.multiply(FLOOR_SIZE * 2.0, Quat.getFront(Camera.getOrientation()))); @@ -45,6 +48,7 @@ var edge1 = Entities.addEntity( gravity: { x: 0, y: 0, z: 0 }, ignoreCollisions: false, visible: true, + locked: true, lifetime: LIFETIME }); var edge2 = Entities.addEntity( @@ -55,6 +59,7 @@ var edge2 = Entities.addEntity( gravity: { x: 0, y: 0, z: 0 }, ignoreCollisions: false, visible: true, + locked: true, lifetime: LIFETIME }); var edge3 = Entities.addEntity( @@ -65,6 +70,7 @@ var edge3 = Entities.addEntity( gravity: { x: 0, y: 0, z: 0 }, ignoreCollisions: false, visible: true, + locked: true, lifetime: LIFETIME }); var edge4 = Entities.addEntity( @@ -75,11 +81,9 @@ var edge4 = Entities.addEntity( gravity: { x: 0, y: 0, z: 0 }, ignoreCollisions: false, visible: true, + locked: true, lifetime: LIFETIME }); -var NUM_BLOCKS = 20; -var DROP_HEIGHT = FLOOR_SIZE / 3; - blocks = []; for (var i = 0; i < NUM_BLOCKS; i++) { diff --git a/examples/controllers/hydra/hydraGrab.js b/examples/controllers/hydra/hydraGrab.js index 5ba8613ad9..dc8cd14eaa 100644 --- a/examples/controllers/hydra/hydraGrab.js +++ b/examples/controllers/hydra/hydraGrab.js @@ -363,25 +363,16 @@ function controller(wichSide) { } var newPosition; var newRotation; - - var forward = Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -1 }); - var d = Vec3.dot(forward, MyAvatar.position); - var factor1 = Vec3.dot(forward, this.positionAtGrab) - d; - var factor2 = Vec3.dot(forward, this.modelPositionAtGrab) - d; - var vector = Vec3.subtract(this.palmPosition, this.positionAtGrab); - - if (factor2 < 0) { - factor2 = 0; - } - if (factor1 <= 0) { - factor1 = 1; - factor2 = 1; + var CONSTANT_SCALING_FACTOR = 5.0; + var MINIMUM_SCALING_DISTANCE = 2.0; + var distanceToModel = Vec3.length(Vec3.subtract(this.oldModelPosition, this.palmPosition)); + if (distanceToModel < MINIMUM_SCALING_DISTANCE) { + distanceToModel = MINIMUM_SCALING_DISTANCE; } - newPosition = Vec3.sum(this.modelPositionAtGrab, - Vec3.multiply(vector, - factor2 / factor1)); + var deltaPalm = Vec3.multiply(distanceToModel * CONSTANT_SCALING_FACTOR, Vec3.subtract(this.palmPosition, this.oldPalmPosition)); + newPosition = Vec3.sum(this.oldModelPosition, deltaPalm); newRotation = Quat.multiply(this.rotation, Quat.inverse(this.rotationAtGrab)); From 117e6abb46e979c14aa41a42b3d600e53ef71e86 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Wed, 28 Jan 2015 09:34:15 -0800 Subject: [PATCH 31/44] remove debug and PR fixes --- examples/billiards.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/billiards.js b/examples/billiards.js index 3e2be52d0f..fbd41e8939 100644 --- a/examples/billiards.js +++ b/examples/billiards.js @@ -139,8 +139,6 @@ function makeBalls(pos) { } ballPosition.x += (BALL_GAP + Math.sqrt(3.0) / 2.0 * BALL_SIZE) * SCALE; } - print(balls.length + " Object balls made."); - print(isObjectBall(balls[1].id)); // Cue Ball cuePosition = { x: pos.x - (LENGTH / 4.0) * SCALE, y: pos.y + HEIGHT / 2.0 + DROP_HEIGHT, z: pos.z }; @@ -160,8 +158,9 @@ function makeBalls(pos) { function isObjectBall(id) { for (var i; i < balls.length; i++) { - if (balls[i].id == id) + if (balls[i].id == id) { return true; + } } return false; } @@ -243,6 +242,7 @@ function update(deltaTime) { function entityCollisionWithEntity(entity1, entity2, collision) { /* + NOT WORKING YET if ((entity1.id == cueBall.id) || (entity2.id == cueBall.id)) { print("Cue ball collision!"); //audioOptions.position = Vec3.sum(Camera.getPosition(), Quat.getFront(Camera.getOrientation())); From a85c6aa956e0ce7030103a246214fee68e2430a4 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Wed, 28 Jan 2015 09:49:45 -0800 Subject: [PATCH 32/44] fix tabs? --- examples/controllers/hydra/paddleBall.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/examples/controllers/hydra/paddleBall.js b/examples/controllers/hydra/paddleBall.js index 8c9af68498..d6c39131c8 100644 --- a/examples/controllers/hydra/paddleBall.js +++ b/examples/controllers/hydra/paddleBall.js @@ -39,9 +39,6 @@ if (leftHanded) { controllerID = 3; } - - - var screenSize = Controller.getViewportDimensions(); var offButton = Overlays.addOverlay("image", { x: screenSize.x - 48, @@ -98,8 +95,8 @@ function createEntities() { alpha: 1, visible: true, lineWidth: 2 }); - - MyAvatar.stopAnimation(leftHanded ? leftHandAnimation: rightHandAnimation); + + MyAvatar.stopAnimation(leftHanded ? leftHandAnimation: rightHandAnimation); MyAvatar.startAnimation(leftHanded ? leftHandAnimation: rightHandAnimation, 15.0, 1.0, false, true, 0.0, 6); } From e1ca6d5048c786bed4b462a9a476187ab9f7b3d9 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 28 Jan 2015 12:16:28 -0800 Subject: [PATCH 33/44] Disable editentities auto-off when moving avatar --- examples/editEntities.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/examples/editEntities.js b/examples/editEntities.js index e2c7a6d435..10cc7df894 100644 --- a/examples/editEntities.js +++ b/examples/editEntities.js @@ -820,9 +820,8 @@ function handeMenuEvent(menuItem) { Menu.menuItemEvent.connect(handeMenuEvent); Controller.keyPressEvent.connect(function(event) { - if (event.text == 'w' || event.text == 'a' || event.text == 's' || event.text == 'd' - || event.text == 'UP' || event.text == 'DOWN' || event.text == 'LEFT' || event.text == 'RIGHT') { - toolBar.setActive(false); + if (isActive) { + cameraManager.keyPressEvent(event); } }); From 5633be0e92d8020e0ca56e64d7785747698773c4 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 28 Jan 2015 12:19:42 -0800 Subject: [PATCH 34/44] Add ability to move orbit camera using keys --- examples/editEntities.js | 3 + examples/libraries/entityCameraTool.js | 83 ++++++++++++++++++++++++++ 2 files changed, 86 insertions(+) diff --git a/examples/editEntities.js b/examples/editEntities.js index 10cc7df894..883362c4a4 100644 --- a/examples/editEntities.js +++ b/examples/editEntities.js @@ -826,6 +826,9 @@ Controller.keyPressEvent.connect(function(event) { }); Controller.keyReleaseEvent.connect(function (event) { + if (isActive) { + cameraManager.keyReleaseEvent(event); + } // since sometimes our menu shortcut keys don't work, trap our menu items here also and fire the appropriate menu items if (event.text == "BACKSPACE" || event.text == "DELETE") { deleteSelectedEntities(); diff --git a/examples/libraries/entityCameraTool.js b/examples/libraries/entityCameraTool.js index f5095bb149..ddbb6fafd5 100644 --- a/examples/libraries/entityCameraTool.js +++ b/examples/libraries/entityCameraTool.js @@ -15,6 +15,9 @@ var MOUSE_SENSITIVITY = 0.9; var SCROLL_SENSITIVITY = 0.05; var PAN_ZOOM_SCALE_RATIO = 0.4; +var KEY_ORBIT_SENSITIVITY = 40; +var KEY_ZOOM_SENSITIVITY = 10; + // Scaling applied based on the size of the object being focused var FOCUS_ZOOM_SCALE = 1.3; @@ -43,6 +46,10 @@ var easeOutCubic = function(t) { EASE_TIME = 0.5; +function clamp(value, minimum, maximum) { + return Math.min(Math.max(value, minimum), maximum); +} + function mergeObjects(obj1, obj2) { var newObj = {}; for (key in obj1) { @@ -60,6 +67,49 @@ CameraManager = function() { that.enabled = false; that.mode = MODE_INACTIVE; + var actions = { + orbitLeft: 0, + orbitRight: 0, + orbitUp: 0, + orbitDown: 0, + orbitForward: 0, + orbitBackward: 0, + } + + var keyToActionMapping = { + "a": "orbitLeft", + "d": "orbitRight", + "w": "orbitForward", + "s": "orbitBackward", + "e": "orbitUp", + "c": "orbitDown", + + "LEFT": "orbitLeft", + "RIGHT": "orbitRight", + "UP": "orbitForward", + "DOWN": "orbitBackward", + } + + var CAPTURED_KEYS = []; + for (key in keyToActionMapping) { + CAPTURED_KEYS.push(key); + } + + function getActionForKeyEvent(event) { + var action = keyToActionMapping[event.text]; + if (action !== undefined) { + if (event.isShifted) { + if (action == "orbitForward") { + action = "orbitUp"; + } else if (action == "orbitBackward") { + action = "orbitDown"; + } + } + return action; + } + return null; + } + that.zoomDistance = INITIAL_ZOOM_DISTANCE; that.targetZoomDistance = INITIAL_ZOOM_DISTANCE; @@ -82,6 +132,11 @@ CameraManager = function() { that.enable = function() { if (Camera.mode == "independent" || that.enabled) return; + for (var i = 0; i < CAPTURED_KEYS.length; i++) { + print("capturing: " + CAPTURED_KEYS[i]); + Controller.captureKeyEvents({ text: CAPTURED_KEYS[i] }); + } + that.enabled = true; that.mode = MODE_INACTIVE; @@ -112,6 +167,11 @@ CameraManager = function() { that.disable = function(ignoreCamera) { if (!that.enabled) return; + + for (var i = 0; i < CAPTURED_KEYS.length; i++) { + Controller.releaseKeyEvents({ text: CAPTURED_KEYS[i] }); + } + that.enabled = false; that.mode = MODE_INACTIVE; @@ -280,6 +340,20 @@ CameraManager = function() { that.mode = MODE_INACTIVE; } + that.keyPressEvent = function(event) { + var action = getActionForKeyEvent(event); + if (action) { + actions[action] = 1; + } + }; + + that.keyReleaseEvent = function(event) { + var action = getActionForKeyEvent(event); + if (action) { + actions[action] = 0; + } + }; + that.wheelEvent = function(event) { if (!that.enabled) return; @@ -333,6 +407,14 @@ CameraManager = function() { return; } + // Update based on current actions + that.targetYaw += (actions.orbitRight - actions.orbitLeft) * dt * KEY_ORBIT_SENSITIVITY; + that.targetPitch += (actions.orbitUp - actions.orbitDown) * dt * KEY_ORBIT_SENSITIVITY; + that.targetPitch = clamp(that.targetPitch, -90, 90); + var addZoom = (actions.orbitBackward - actions.orbitForward) * dt * KEY_ZOOM_SENSITIVITY; + that.targetZoomDistance = clamp(that.targetZoomDistance + addZoom, MIN_ZOOM_DISTANCE, MAX_ZOOM_DISTANCE); + + if (easing) { easingTime = Math.min(EASE_TIME, easingTime + dt); } @@ -384,6 +466,7 @@ CameraManager = function() { }); Script.update.connect(that.update); + Script.scriptEnding.connect(that.disable); Controller.wheelEvent.connect(that.wheelEvent); From 135c8c1bdf2ea17c4043d9c7243d615957f511c7 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 28 Jan 2015 12:19:54 -0800 Subject: [PATCH 35/44] Disable move entity by arrows --- examples/editEntities.js | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/examples/editEntities.js b/examples/editEntities.js index 883362c4a4..f88a0e44d9 100644 --- a/examples/editEntities.js +++ b/examples/editEntities.js @@ -856,23 +856,23 @@ Controller.keyReleaseEvent.connect(function (event) { var delta = null; var increment = event.isShifted ? grid.getMajorIncrement() : grid.getMinorIncrement(); - if (event.text == 'UP') { - if (event.isControl || event.isAlt) { - delta = { x: 0, y: increment, z: 0 }; - } else { - delta = { x: 0, y: 0, z: -increment }; - } - } else if (event.text == 'DOWN') { - if (event.isControl || event.isAlt) { - delta = { x: 0, y: -increment, z: 0 }; - } else { - delta = { x: 0, y: 0, z: increment }; - } - } else if (event.text == 'LEFT') { - delta = { x: -increment, y: 0, z: 0 }; - } else if (event.text == 'RIGHT') { - delta = { x: increment, y: 0, z: 0 }; - } + // if (event.text == 'UP') { + // if (event.isControl || event.isAlt) { + // delta = { x: 0, y: increment, z: 0 }; + // } else { + // delta = { x: 0, y: 0, z: -increment }; + // } + // } else if (event.text == 'DOWN') { + // if (event.isControl || event.isAlt) { + // delta = { x: 0, y: -increment, z: 0 }; + // } else { + // delta = { x: 0, y: 0, z: increment }; + // } + // } else if (event.text == 'LEFT') { + // delta = { x: -increment, y: 0, z: 0 }; + // } else if (event.text == 'RIGHT') { + // delta = { x: increment, y: 0, z: 0 }; + // } if (delta != null) { // Adjust delta so that movements are relative to the current camera orientation From 55f41eb452c1115f8872285408578dc819bf7a2a Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 28 Jan 2015 12:20:08 -0800 Subject: [PATCH 36/44] Update gridTool to not use clone --- examples/libraries/gridTool.js | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/examples/libraries/gridTool.js b/examples/libraries/gridTool.js index 87b7f907e2..6fd3a8a660 100644 --- a/examples/libraries/gridTool.js +++ b/examples/libraries/gridTool.js @@ -292,7 +292,6 @@ GridTool = function(opts) { ]; // Add all overlays from spritesheet - var baseOverlay = null; var x = 0; for (var i = 0; i < UI_SPRITE_LIST.length; i++) { var info = UI_SPRITE_LIST[i]; @@ -306,17 +305,7 @@ GridTool = function(opts) { visible: false, }; - var overlay; - if (baseOverlay == null) { - overlay = Overlays.addOverlay("image", { - imageURL: UI_URL, - }); - baseOverlay = overlay; - } else { - overlay = Overlays.cloneOverlay(baseOverlay); - } - - Overlays.editOverlay(overlay, props); + var overlay = Overlays.addOverlay("image", props); addUIOverlay(info.name, overlay, x, 0, info.width, UI_HEIGHT); From a775476cfa87c1268604bc13f05d2748148423d9 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 28 Jan 2015 12:21:50 -0800 Subject: [PATCH 37/44] Remove editEntities move-by-arrow-key completely --- examples/editEntities.js | 49 ---------------------------------------- 1 file changed, 49 deletions(-) diff --git a/examples/editEntities.js b/examples/editEntities.js index f88a0e44d9..083887819f 100644 --- a/examples/editEntities.js +++ b/examples/editEntities.js @@ -852,55 +852,6 @@ Controller.keyReleaseEvent.connect(function (event) { newPosition = Vec3.subtract(newPosition, { x: 0, y: selectionManager.worldDimensions.y * 0.5, z: 0 }); grid.setPosition(newPosition); } - } else if (isActive) { - var delta = null; - var increment = event.isShifted ? grid.getMajorIncrement() : grid.getMinorIncrement(); - - // if (event.text == 'UP') { - // if (event.isControl || event.isAlt) { - // delta = { x: 0, y: increment, z: 0 }; - // } else { - // delta = { x: 0, y: 0, z: -increment }; - // } - // } else if (event.text == 'DOWN') { - // if (event.isControl || event.isAlt) { - // delta = { x: 0, y: -increment, z: 0 }; - // } else { - // delta = { x: 0, y: 0, z: increment }; - // } - // } else if (event.text == 'LEFT') { - // delta = { x: -increment, y: 0, z: 0 }; - // } else if (event.text == 'RIGHT') { - // delta = { x: increment, y: 0, z: 0 }; - // } - - if (delta != null) { - // Adjust delta so that movements are relative to the current camera orientation - var lookDirection = Quat.getFront(Camera.getOrientation()); - lookDirection.z *= -1; - - var angle = Math.atan2(lookDirection.z, lookDirection.x); - angle -= (Math.PI / 4); - - var rotation = Math.floor(angle / (Math.PI / 2)) * (Math.PI / 2); - var rotator = Quat.fromPitchYawRollRadians(0, rotation, 0); - - delta = Vec3.multiplyQbyV(rotator, delta); - - SelectionManager.saveProperties(); - - for (var i = 0; i < selectionManager.selections.length; i++) { - var entityID = selectionManager.selections[i]; - var properties = Entities.getEntityProperties(entityID); - Entities.editEntity(entityID, { - position: Vec3.sum(properties.position, delta) - }); - } - - pushCommandForSelections(); - - selectionManager._update(); - } } }); From 09af7ed10faed02dec6a363b844a9165d81caabd Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 28 Jan 2015 12:21:58 -0800 Subject: [PATCH 38/44] Remove extra print statement --- examples/libraries/entityCameraTool.js | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/libraries/entityCameraTool.js b/examples/libraries/entityCameraTool.js index ddbb6fafd5..df5daa561f 100644 --- a/examples/libraries/entityCameraTool.js +++ b/examples/libraries/entityCameraTool.js @@ -133,7 +133,6 @@ CameraManager = function() { if (Camera.mode == "independent" || that.enabled) return; for (var i = 0; i < CAPTURED_KEYS.length; i++) { - print("capturing: " + CAPTURED_KEYS[i]); Controller.captureKeyEvents({ text: CAPTURED_KEYS[i] }); } From 1b81ee17ab35a5f27b3fcd4a4e160d6b414e93ec Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 28 Jan 2015 12:23:54 -0800 Subject: [PATCH 39/44] Update how orbit-by-key is appleid --- examples/libraries/entityCameraTool.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/libraries/entityCameraTool.js b/examples/libraries/entityCameraTool.js index df5daa561f..609c1db076 100644 --- a/examples/libraries/entityCameraTool.js +++ b/examples/libraries/entityCameraTool.js @@ -410,8 +410,8 @@ CameraManager = function() { that.targetYaw += (actions.orbitRight - actions.orbitLeft) * dt * KEY_ORBIT_SENSITIVITY; that.targetPitch += (actions.orbitUp - actions.orbitDown) * dt * KEY_ORBIT_SENSITIVITY; that.targetPitch = clamp(that.targetPitch, -90, 90); - var addZoom = (actions.orbitBackward - actions.orbitForward) * dt * KEY_ZOOM_SENSITIVITY; - that.targetZoomDistance = clamp(that.targetZoomDistance + addZoom, MIN_ZOOM_DISTANCE, MAX_ZOOM_DISTANCE); + that.targetZoomDistance += (actions.orbitBackward - actions.orbitForward) * dt * KEY_ZOOM_SENSITIVITY; + that.targetZoomDistance = clamp(that.targetZoomDistance, MIN_ZOOM_DISTANCE, MAX_ZOOM_DISTANCE); if (easing) { From ce9e8910e48c464c7e89f407370a9db52f2361d3 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 29 Jan 2015 09:04:57 -0800 Subject: [PATCH 40/44] Add callback to QScriptEngine::include(QString) --- libraries/script-engine/src/ScriptEngine.cpp | 4 ++-- libraries/script-engine/src/ScriptEngine.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 246f9d3be3..b400dd5cfb 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -635,10 +635,10 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac } } -void ScriptEngine::include(const QString& includeFile) { +void ScriptEngine::include(const QString& includeFile, QScriptValue callback) { QStringList urls; urls.append(includeFile); - include(urls); + include(urls, callback); } void ScriptEngine::load(const QString& loadFile) { diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 126e943982..f78a14bffa 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -97,7 +97,7 @@ public slots: void clearInterval(QObject* timer) { stopTimer(reinterpret_cast(timer)); } void clearTimeout(QObject* timer) { stopTimer(reinterpret_cast(timer)); } void include(const QStringList& includeFiles, QScriptValue callback = QScriptValue()); - void include(const QString& includeFile); + void include(const QString& includeFile, QScriptValue callback = QScriptValue()); void load(const QString& loadfile); void print(const QString& message); QUrl resolvePath(const QString& path) const; From e6caefa023f1e04095c23c76ae593557a07d64fc Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 29 Jan 2015 09:08:55 -0800 Subject: [PATCH 41/44] Add comment to ScriptEngine::include() --- libraries/script-engine/src/ScriptEngine.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index b400dd5cfb..a002950d46 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -112,7 +112,7 @@ void ScriptEngine::setIsAvatar(bool isAvatar) { _avatarIdentityTimer->start(AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS); _avatarBillboardTimer->start(AVATAR_BILLBOARD_PACKET_SEND_INTERVAL_MSECS); } - + if (!_isAvatar) { delete _avatarIdentityTimer; _avatarIdentityTimer = NULL; @@ -596,6 +596,12 @@ void ScriptEngine::print(const QString& message) { emit printedMessage(message); } +/** + * If a callback is specified, the included files will be loaded asynchronously and the callback will be called + * when all of the files have finished loading. + * If no callback is specified, the included files will be loaded synchronously and will block execution until + * all of the files have finished loading. + */ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callback) { QList urls; for (QString file : includeFiles) { From 0c3f4b894ce4d3819582753867a1bd5e7136733e Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Thu, 29 Jan 2015 10:53:50 -0800 Subject: [PATCH 42/44] Menu for handedness, that really works. --- examples/controllers/hydra/paddleBall.js | 43 +++++++++++++++++++----- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/examples/controllers/hydra/paddleBall.js b/examples/controllers/hydra/paddleBall.js index d6c39131c8..fb312739f4 100644 --- a/examples/controllers/hydra/paddleBall.js +++ b/examples/controllers/hydra/paddleBall.js @@ -24,7 +24,8 @@ var BALL_COLOR = { red: 255, green: 0, blue: 0 }; var LINE_COLOR = { red: 255, green: 255, blue: 0 }; var PADDLE_BOX_OFFSET = { x: 0.05, y: 0.0, z: 0.0 }; -var HOLD_POSITION_OFFSET = { x: -0.15, y: 0.05, z: -0.05 }; +var HOLD_POSITION_LEFT_OFFSET = { x: -0.15, y: 0.05, z: -0.05 }; +var HOLD_POSITION_RIGHT_OFFSET = { x: -0.15, y: 0.05, z: 0.05 }; var PADDLE_ORIENTATION = Quat.fromPitchYawRollDegrees(0,0,0); var GRAVITY = 0.0; var SPRING_FORCE = 15.0; @@ -33,12 +34,19 @@ var gameOn = false; var leftHanded = true; var controllerID; -if (leftHanded) { - controllerID = 1; -} else { - controllerID = 3; + +function setControllerID() { + if (leftHanded) { + controllerID = 1; + } else { + controllerID = 3; + } } +setControllerID(); +Menu.addMenu("PaddleBall"); +Menu.addMenuItem({ menuName: "PaddleBall", menuItemName: "Left-Handed", isCheckable: true, isChecked: true }); + var screenSize = Controller.getViewportDimensions(); var offButton = Overlays.addOverlay("image", { x: screenSize.x - 48, @@ -96,7 +104,8 @@ function createEntities() { visible: true, lineWidth: 2 }); - MyAvatar.stopAnimation(leftHanded ? leftHandAnimation: rightHandAnimation); + MyAvatar.stopAnimation(leftHandAnimation); + MyAvatar.stopAnimation(rightHandAnimation); MyAvatar.startAnimation(leftHanded ? leftHandAnimation: rightHandAnimation, 15.0, 1.0, false, true, 0.0, 6); } @@ -129,9 +138,10 @@ function update(deltaTime) { if (!ball.isKnownID) { ball = Entities.identifyEntity(ball); } else { - var paddleWorldOrientation = Quat.multiply(Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(controllerID)), PADDLE_ORIENTATION); + var paddleOrientation = leftHanded ? PADDLE_ORIENTATION : Quat.multiply(PADDLE_ORIENTATION, Quat.fromPitchYawRollDegrees(0, 180, 0)); + var paddleWorldOrientation = Quat.multiply(Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(controllerID)), paddleOrientation); var holdPosition = Vec3.sum(leftHanded ? MyAvatar.getLeftPalmPosition() : MyAvatar.getRightPalmPosition(), - Vec3.multiplyQbyV(paddleWorldOrientation, HOLD_POSITION_OFFSET)); + Vec3.multiplyQbyV(paddleWorldOrientation, leftHanded ? HOLD_POSITION_LEFT_OFFSET : HOLD_POSITION_RIGHT_OFFSET )); var props = Entities.getEntityProperties(ball); var spring = Vec3.subtract(holdPosition, props.position); @@ -172,15 +182,30 @@ function mousePressEvent(event) { } } +function menuItemEvent(menuItem) { + oldHanded = leftHanded; + if (menuItem == "Left-Handed") { + leftHanded = Menu.isOptionChecked("Left-Handed"); + } + if ((leftHanded != oldHanded) && gameOn) { + setControllerID(); + deleteEntities(); + createEntities(); + } +} + function scriptEnding() { if (gameOn) { deleteEntities(); } Overlays.deleteOverlay(offButton); - MyAvatar.stopAnimation(leftHanded ? leftHandAnimation: rightHandAnimation); + MyAvatar.stopAnimation(leftHandAnimation); + MyAvatar.stopAnimation(rightHandAnimation); + Menu.removeMenu("PaddleBall"); } Entities.entityCollisionWithEntity.connect(entityCollisionWithEntity); +Menu.menuItemEvent.connect(menuItemEvent); Controller.mousePressEvent.connect(mousePressEvent); Script.scriptEnding.connect(scriptEnding); Script.update.connect(update); From b2f16be92a19bc16869d4d4f53081b8bca25c911 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Thu, 29 Jan 2015 15:17:55 -0800 Subject: [PATCH 43/44] Fixing the non loading of some texture because the QImage bit size is not exactly what's expected --- interface/src/ui/overlays/ImageOverlay.cpp | 14 ++++++++++++-- libraries/gpu/src/gpu/Texture.cpp | 11 ++++++++++- libraries/render-utils/src/TextureCache.cpp | 2 +- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/interface/src/ui/overlays/ImageOverlay.cpp b/interface/src/ui/overlays/ImageOverlay.cpp index 0c5d9a7737..73da8cc9c0 100644 --- a/interface/src/ui/overlays/ImageOverlay.cpp +++ b/interface/src/ui/overlays/ImageOverlay.cpp @@ -45,7 +45,6 @@ ImageOverlay::~ImageOverlay() { // TODO: handle setting image multiple times, how do we manage releasing the bound texture? void ImageOverlay::setImageURL(const QUrl& url) { _imageURL = url; - if (url.isEmpty()) { _isLoaded = true; _renderImage = false; @@ -57,6 +56,8 @@ void ImageOverlay::setImageURL(const QUrl& url) { } void ImageOverlay::render(RenderArgs* args) { + QString problem("http://s3.amazonaws.com/hifi-public/images/tools/grid-toolbar.svg"); + bool theONE = (_imageURL == problem); if (!_isLoaded && _renderImage) { _isLoaded = true; _texture = DependencyManager::get()->getTexture(_imageURL); @@ -69,8 +70,17 @@ void ImageOverlay::render(RenderArgs* args) { } if (_renderImage) { + glEnable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, _texture->getID()); + if (theONE) { + std::string name = _imageURL.toString().toStdString(); + } + GLuint texID = _texture->getID(); + if (texID == 27) { + std::string name = _imageURL.toString().toStdString(); + glBindTexture(GL_TEXTURE_2D, texID); + } else + glBindTexture(GL_TEXTURE_2D, _texture->getID()); } const float MAX_COLOR = 255.0f; diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index d0779afb42..f9fbcb72df 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -254,7 +254,16 @@ bool Texture::assignStoredMip(uint16 level, const Element& format, Size size, co } // THen check that the mem buffer passed make sense with its format - if (size == evalStoredMipSize(level, format)) { + Size expectedSize = evalStoredMipSize(level, format); + if (size == expectedSize) { + _storage->assignMipData(level, format, size, bytes); + _stamp++; + return true; + } else if (size > expectedSize) { + // NOTE: We are facing this case sometime because apparently QImage (from where we get the bits) is generating images + // and alligning the line of pixels to 32 bits. + // We should probably consider something a bit more smart to get the correct result but for now (UI elements) + // it seems to work... _storage->assignMipData(level, format, size, bytes); _stamp++; return true; diff --git a/libraries/render-utils/src/TextureCache.cpp b/libraries/render-utils/src/TextureCache.cpp index 3644ded81c..aac2ec1b8c 100644 --- a/libraries/render-utils/src/TextureCache.cpp +++ b/libraries/render-utils/src/TextureCache.cpp @@ -387,7 +387,7 @@ NetworkTexture::NetworkTexture(const QUrl& url, TextureType type, const QByteArr if (!url.isValid()) { _loaded = true; } - + // default to white/blue/black /* glBindTexture(GL_TEXTURE_2D, getID()); switch (type) { From ec54ac7401a5186e882bbeaa5f4acea6a100af4f Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Thu, 29 Jan 2015 15:24:36 -0800 Subject: [PATCH 44/44] Fixing the non loading of some texture because the QImage bit size is not exactly what's expected --- interface/src/ui/overlays/ImageOverlay.cpp | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/interface/src/ui/overlays/ImageOverlay.cpp b/interface/src/ui/overlays/ImageOverlay.cpp index 73da8cc9c0..e354ea539e 100644 --- a/interface/src/ui/overlays/ImageOverlay.cpp +++ b/interface/src/ui/overlays/ImageOverlay.cpp @@ -56,8 +56,6 @@ void ImageOverlay::setImageURL(const QUrl& url) { } void ImageOverlay::render(RenderArgs* args) { - QString problem("http://s3.amazonaws.com/hifi-public/images/tools/grid-toolbar.svg"); - bool theONE = (_imageURL == problem); if (!_isLoaded && _renderImage) { _isLoaded = true; _texture = DependencyManager::get()->getTexture(_imageURL); @@ -70,17 +68,8 @@ void ImageOverlay::render(RenderArgs* args) { } if (_renderImage) { - glEnable(GL_TEXTURE_2D); - if (theONE) { - std::string name = _imageURL.toString().toStdString(); - } - GLuint texID = _texture->getID(); - if (texID == 27) { - std::string name = _imageURL.toString().toStdString(); - glBindTexture(GL_TEXTURE_2D, texID); - } else - glBindTexture(GL_TEXTURE_2D, _texture->getID()); + glBindTexture(GL_TEXTURE_2D, _texture->getID()); } const float MAX_COLOR = 255.0f;