diff --git a/domain-server/src/DomainServerWebSessionData.cpp b/domain-server/src/DomainServerWebSessionData.cpp index 0a921d65c3..3744af77f3 100644 --- a/domain-server/src/DomainServerWebSessionData.cpp +++ b/domain-server/src/DomainServerWebSessionData.cpp @@ -9,6 +9,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include #include #include #include diff --git a/examples/controlPanel.js b/examples/controlPanel.js index 59d66fdaf9..76fbd49577 100644 --- a/examples/controlPanel.js +++ b/examples/controlPanel.js @@ -5,7 +5,7 @@ // Created by Zander Otavka on 7/15/15. // Copyright 2015 High Fidelity, Inc. // -// Shows a few common controls in a FloatingUIPanel on right click. +// Shows a few common controls in a OverlayPanel on right click. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -22,14 +22,13 @@ var MIC_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/tools/mic-toggle.svg"; var FACE_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/tools/face-toggle.svg"; var ADDRESS_BAR_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/tools/address-bar-toggle.svg"; -var panel = new FloatingUIPanel({ - anchorPosition: { - bind: "myAvatar" - }, - offsetPosition: { x: 0, y: 0.4, z: 1 } +var panel = new OverlayPanel({ + anchorPositionBinding: { avatar: "MyAvatar" }, + offsetPosition: { x: 0, y: 0.4, z: -1 }, + visible: false }); -var background = new BillboardOverlay({ +var background = new Image3DOverlay({ url: BG_IMAGE_URL, dimensions: { x: 0.5, @@ -37,11 +36,12 @@ var background = new BillboardOverlay({ }, isFacingAvatar: false, alpha: 1.0, - ignoreRayIntersection: false + ignoreRayIntersection: false, + visible: false }); panel.addChild(background); -var closeButton = new BillboardOverlay({ +var closeButton = new Image3DOverlay({ url: CLOSE_IMAGE_URL, dimensions: { x: 0.15, @@ -51,17 +51,18 @@ var closeButton = new BillboardOverlay({ alpha: 1.0, ignoreRayIntersection: false, offsetPosition: { - x: -0.1, + x: 0.1, y: 0.1, - z: -0.001 - } + z: 0.001 + }, + visible: false }); closeButton.onClick = function(event) { panel.visible = false; }; panel.addChild(closeButton); -var micMuteButton = new BillboardOverlay({ +var micMuteButton = new Image3DOverlay({ url: MIC_IMAGE_URL, subImage: { x: 0, @@ -77,17 +78,18 @@ var micMuteButton = new BillboardOverlay({ alpha: 1.0, ignoreRayIntersection: false, offsetPosition: { - x: 0.1, + x: -0.1, y: 0.1, - z: -0.001 - } + z: 0.001 + }, + visible: false }); micMuteButton.onClick = function(event) { AudioDevice.toggleMute(); }; panel.addChild(micMuteButton); -var faceMuteButton = new BillboardOverlay({ +var faceMuteButton = new Image3DOverlay({ url: FACE_IMAGE_URL, subImage: { x: 0, @@ -102,43 +104,47 @@ var faceMuteButton = new BillboardOverlay({ isFacingAvatar: false, alpha: 1.0, ignoreRayIntersection: false, - offsetPosition: { - x: 0.1, - y: -0.1, - z: -0.001 - } -}); -faceMuteButton.onClick = function(event) { - FaceTracker.toggleMute(); -}; -panel.addChild(faceMuteButton); - -var addressBarButton = new BillboardOverlay({ - url: ADDRESS_BAR_IMAGE_URL, - subImage: { - x: 0, - y: 0, - width: 45, - height: 45 - }, - dimensions: { - x: 0.15, - y: 0.15, - }, - isFacingAvatar: false, - alpha: 1.0, - ignoreRayIntersection: false, offsetPosition: { x: -0.1, y: -0.1, - z: -0.001 - } + z: 0.001 + }, + visible: false +}); +faceMuteButton.onClick = function(event) { + FaceTracker.toggleMute(); +}; +panel.addChild(faceMuteButton); + +var addressBarButton = new Image3DOverlay({ + url: ADDRESS_BAR_IMAGE_URL, + subImage: { + x: 0, + y: 0, + width: 45, + height: 45 + }, + dimensions: { + x: 0.15, + y: 0.15, + }, + isFacingAvatar: false, + alpha: 1.0, + ignoreRayIntersection: false, + offsetPosition: { + x: 0.1, + y: -0.1, + z: 0.001 + }, + visible: false }); addressBarButton.onClick = function(event) { DialogsManager.toggleAddressBar(); }; panel.addChild(addressBarButton); +panel.setChildrenVisible(); + function onMicMuteToggled() { var offset; @@ -181,6 +187,16 @@ function onMouseDown(event) { if (event.isRightButton) { mouseDown.pos = { x: event.x, y: event.y }; } + mouseDown.maxDistance = 0; +} + +function onMouseMove(event) { + if (mouseDown.maxDistance !== undefined) { + var dist = Vec3.distance(mouseDown.pos, { x: event.x, y: event.y }); + if (dist > mouseDown.maxDistance) { + mouseDown.maxDistance = dist; + } + } } function onMouseUp(event) { @@ -190,13 +206,10 @@ function onMouseUp(event) { overlay.onClick(event); } } - if (event.isRightButton && Vec3.distance(mouseDown.pos, { x: event.x, y: event.y }) < 5) { + if (event.isRightButton && mouseDown.maxDistance < 10) { panel.setProperties({ visible: !panel.visible, - offsetRotation: { - bind: "quat", - value: Quat.multiply(MyAvatar.orientation, { x: 0, y: 1, z: 0, w: 0 }) - } + anchorRotation: MyAvatar.orientation }); } @@ -208,6 +221,7 @@ function onScriptEnd(event) { } Controller.mousePressEvent.connect(onMouseDown); +Controller.mouseMoveEvent.connect(onMouseMove); Controller.mouseReleaseEvent.connect(onMouseUp); AudioDevice.muteToggled.connect(onMicMuteToggled); FaceTracker.muteToggled.connect(onFaceMuteToggled); diff --git a/examples/entityScripts/sitOnEntity.js b/examples/entityScripts/sitOnEntity.js index 8338761c41..0732b14486 100644 --- a/examples/entityScripts/sitOnEntity.js +++ b/examples/entityScripts/sitOnEntity.js @@ -178,7 +178,7 @@ modelProperties.sittingPoints[seatIndex].rotation); this.scale = MyAvatar.scale / 3; - this.sphere = Overlays.addOverlay("billboard", { + this.sphere = Overlays.addOverlay("image3d", { subImage: { x: 0, y: buttonHeight, width: buttonWidth, height: buttonHeight}, url: buttonImageUrl, position: this.position, diff --git a/examples/example/games/clonedOverlaysExample.js b/examples/example/games/clonedOverlaysExample.js index aa475ee518..51281aa841 100644 --- a/examples/example/games/clonedOverlaysExample.js +++ b/examples/example/games/clonedOverlaysExample.js @@ -252,7 +252,7 @@ function SpriteBillboard(sprite_properties, overlay) { } var christmastree_loader = null; -christmastree_loader = new OverlayPreloader("billboard", +christmastree_loader = new OverlayPreloader("image3d", {url: CHRISTMAS_TREE_SPRITES_URL, alpha: 0}, function() { for (var i = 0; i < NUM_OF_TREES; i++) { var clonedOverlay = Overlays.cloneOverlay(christmastree_loader.overlay); @@ -269,7 +269,7 @@ christmastree_loader = new OverlayPreloader("billboard", ); var santa_loader = null; -santa_loader = new OverlayPreloader("billboard", +santa_loader = new OverlayPreloader("image3d", {url: SANTA_SPRITES_URL, alpha: 0}, function() { for (var i = 0; i < NUM_OF_SANTAS; i++) { var clonedOverlay = Overlays.cloneOverlay(santa_loader.overlay); diff --git a/examples/example/ui/floatingUIExample.js b/examples/example/ui/overlayPanelExample.js similarity index 54% rename from examples/example/ui/floatingUIExample.js rename to examples/example/ui/overlayPanelExample.js index 1e957fe6b3..c27123c9ad 100644 --- a/examples/example/ui/floatingUIExample.js +++ b/examples/example/ui/overlayPanelExample.js @@ -1,5 +1,5 @@ // -// floatingUI.js +// overlayPanelExample.js // examples/example/ui // // Created by Alexander Otavka @@ -18,19 +18,18 @@ var BG_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/card-bg.svg"; var RED_DOT_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/red-dot.svg"; var BLUE_SQUARE_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/blue-square.svg"; -var mainPanel = new FloatingUIPanel({ - offsetRotation: { - bind: "quat", - value: { w: 1, x: 0, y: 0, z: 0 } - }, - offsetPosition: { x: 0, y: 0.4, z: 1 } +var mainPanel = new OverlayPanel({ + anchorPositionBinding: { avatar: "MyAvatar" }, + offsetPosition: { x: 0, y: 0.4, z: -1 }, + isFacingAvatar: false }); -var bluePanel = mainPanel.addChild(new FloatingUIPanel ({ - offsetPosition: { x: 0.1, y: 0.1, z: -0.2 } +var bluePanel = mainPanel.addChild(new OverlayPanel ({ + offsetPosition: { x: 0.1, y: 0.1, z: 0.2 }, + offsetScale: 0.5 })); -var mainPanelBackground = new BillboardOverlay({ +var mainPanelBackground = new Image3DOverlay({ url: BG_IMAGE_URL, dimensions: { x: 0.5, @@ -42,20 +41,44 @@ var mainPanelBackground = new BillboardOverlay({ offsetPosition: { x: 0, y: 0, - z: 0.001 + z: -0.001 } }); var bluePanelBackground = mainPanelBackground.clone(); -bluePanelBackground.dimensions = { - x: 0.3, - y: 0.3 -}; mainPanel.addChild(mainPanelBackground); bluePanel.addChild(bluePanelBackground); -var redDot = mainPanel.addChild(new BillboardOverlay({ +var textWidth = .25; +var textHeight = .1; +var numberOfLines = 1; +var textMargin = 0.00625; +var lineHeight = (textHeight - (2 * textMargin)) / numberOfLines; + +var text = mainPanel.addChild(new Text3DOverlay({ + text: "TEXT", + isFacingAvatar: false, + alpha: 1.0, + ignoreRayIntersection: false, + offsetPosition: { + x: 0.1, + y: -0.15, + z: 0.001 + }, + dimensions: { x: textWidth, y: textHeight }, + backgroundColor: { red: 0, green: 0, blue: 0 }, + color: { red: 255, green: 255, blue: 255 }, + topMargin: textMargin, + leftMargin: textMargin, + bottomMargin: textMargin, + rightMargin: textMargin, + lineHeight: lineHeight, + alpha: 0.9, + backgroundAlpha: 0.9 +})); + +var redDot = mainPanel.addChild(new Image3DOverlay({ url: RED_DOT_IMAGE_URL, dimensions: { x: 0.1, @@ -71,7 +94,7 @@ var redDot = mainPanel.addChild(new BillboardOverlay({ } })); -var redDot2 = mainPanel.addChild(new BillboardOverlay({ +var redDot2 = mainPanel.addChild(new Image3DOverlay({ url: RED_DOT_IMAGE_URL, dimensions: { x: 0.1, @@ -87,46 +110,45 @@ var redDot2 = mainPanel.addChild(new BillboardOverlay({ } })); -var blueSquare = bluePanel.addChild(new BillboardOverlay({ +var blueSquare = bluePanel.addChild(new Image3DOverlay({ url: BLUE_SQUARE_IMAGE_URL, dimensions: { - x: 0.1, - y: 0.1, + x: 0.15, + y: 0.15, }, isFacingAvatar: false, alpha: 1.0, ignoreRayIntersection: false, offsetPosition: { - x: 0.055, - y: -0.055, + x: 0.09, + y: -0.09, z: 0 } })); -var blueSquare2 = bluePanel.addChild(new BillboardOverlay({ +var blueSquare2 = bluePanel.addChild(new Image3DOverlay({ url: BLUE_SQUARE_IMAGE_URL, dimensions: { - x: 0.1, - y: 0.1, + x: 0.15, + y: 0.15, }, isFacingAvatar: false, alpha: 1.0, ignoreRayIntersection: false, offsetPosition: { - x: 0.055, - y: 0.055, + x: 0.09, + y: 0.09, z: 0 } })); var blueSquare3 = blueSquare2.clone(); blueSquare3.offsetPosition = { - x: -0.055, - y: 0.055, + x: -0.09, + y: 0.09, z: 0 }; - var mouseDown = {}; function onMouseDown(event) { @@ -136,26 +158,30 @@ function onMouseDown(event) { if (event.isRightButton) { mouseDown.pos = { x: event.x, y: event.y }; } + mouseDown.maxDistance = 0; +} + +function onMouseMove(event) { + if (mouseDown.maxDistance !== undefined) { + var dist = Vec3.distance(mouseDown.pos, { x: event.x, y: event.y }); + if (dist > mouseDown.maxDistance) { + mouseDown.maxDistance = dist; + } + } } function onMouseUp(event) { if (event.isLeftButton) { var overlay = OverlayManager.findAtPoint({ x: event.x, y: event.y }); - if (overlay === mouseDown.overlay) { - if (overlay.attachedPanel === bluePanel) { + if (overlay && overlay === mouseDown.overlay) { + if (overlay.parentPanel === bluePanel) { overlay.destroy(); - } else if (overlay) { - var oldPos = overlay.offsetPosition; - var newPos = { - x: Number(oldPos.x), - y: Number(oldPos.y), - z: Number(oldPos.z) + 0.1 - }; - overlay.offsetPosition = newPos; + } else { + overlay.offsetPosition = Vec3.sum(overlay.offsetPosition, { x: 0, y: 0, z: -0.1 }); } } } - if (event.isRightButton && Vec3.distance(mouseDown.pos, { x: event.x, y: event.y }) < 5) { + if (event.isRightButton && mouseDown.maxDistance < 10) { mainPanel.visible = !mainPanel.visible; } } @@ -165,5 +191,6 @@ function onScriptEnd() { } Controller.mousePressEvent.connect(onMouseDown); +Controller.mouseMoveEvent.connect(onMouseMove); Controller.mouseReleaseEvent.connect(onMouseUp); -Script.scriptEnding.connect(onScriptEnd); \ No newline at end of file +Script.scriptEnding.connect(onScriptEnd); diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index 98a71e3b12..b2f6474089 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -340,7 +340,7 @@ SelectionDisplay = (function () { leftMargin: 0, }); - var grabberMoveUp = Overlays.addOverlay("billboard", { + var grabberMoveUp = Overlays.addOverlay("image3d", { url: HIFI_PUBLIC_BUCKET + "images/up-arrow.svg", position: { x:0, y: 0, z: 0}, color: handleColor, @@ -609,7 +609,7 @@ SelectionDisplay = (function () { minorTickMarksColor: { red: 0, green: 0, blue: 0 }, }); - var yawHandle = Overlays.addOverlay("billboard", { + var yawHandle = Overlays.addOverlay("image3d", { url: ROTATE_ARROW_WEST_NORTH_URL, position: { x:0, y: 0, z: 0}, color: handleColor, @@ -622,7 +622,7 @@ SelectionDisplay = (function () { }); - var pitchHandle = Overlays.addOverlay("billboard", { + var pitchHandle = Overlays.addOverlay("image3d", { url: ROTATE_ARROW_WEST_NORTH_URL, position: { x:0, y: 0, z: 0}, color: handleColor, @@ -635,7 +635,7 @@ SelectionDisplay = (function () { }); - var rollHandle = Overlays.addOverlay("billboard", { + var rollHandle = Overlays.addOverlay("image3d", { url: ROTATE_ARROW_WEST_NORTH_URL, position: { x:0, y: 0, z: 0}, color: handleColor, diff --git a/examples/libraries/lightOverlayManager.js b/examples/libraries/lightOverlayManager.js index 9b26cae837..0942fae723 100644 --- a/examples/libraries/lightOverlayManager.js +++ b/examples/libraries/lightOverlayManager.js @@ -61,7 +61,7 @@ LightOverlayManager = function() { // Allocate or get an unused overlay function getOverlay() { if (unusedOverlays.length == 0) { - var overlay = Overlays.addOverlay("billboard", { + var overlay = Overlays.addOverlay("image3d", { }); allOverlays.push(overlay); } else { diff --git a/examples/libraries/overlayManager.js b/examples/libraries/overlayManager.js index cd05cd2a52..bf882c3d78 100644 --- a/examples/libraries/overlayManager.js +++ b/examples/libraries/overlayManager.js @@ -8,7 +8,7 @@ // Manage overlays with object oriented goodness, instead of ugly `Overlays.h` methods. // Instead of: // -// var billboard = Overlays.addOverlay("billboard", { visible: false }); +// var billboard = Overlays.addOverlay("image3d", { visible: false }); // ... // Overlays.editOverlay(billboard, { visible: true }); // ... @@ -16,7 +16,7 @@ // // You can now do: // -// var billboard = new BillboardOverlay({ visible: false }); +// var billboard = new Image3DOverlay({ visible: false }); // ... // billboard.visible = true; // ... @@ -42,7 +42,18 @@ var panels = {}; var overlayTypes; - var Overlay, Overlay2D, Base3DOverlay, Planar3DOverlay, Volume3DOverlay; + + // Abstract overlay types + var Overlay, + Overlay2D, + Base3DOverlay, + Planar3DOverlay, + Billboard3DOverlay, + Volume3DOverlay; + + // Multiple inheritance mixins + var PanelAttachable, + Billboardable; // @@ -55,10 +66,6 @@ } var overlay = new overlayTypes[type](); overlay._id = id; - var panelID = Overlays.getAttachedPanel(id) - if (panelID && panelID in panels) { - panels[panelID].addChild(overlay); - } overlays[id] = overlay; return overlay; } @@ -74,7 +81,7 @@ // function findOverlay(id, knownOverlaysOnly, searchList) { if (id > 0) { - knownOverlaysOnly = Boolean(knownOverlaysOnly) || Boolean(searchList); + knownOverlaysOnly = Boolean(knownOverlaysOnly); searchList = searchList || overlays; var foundOverlay = searchList[id]; if (foundOverlay) { @@ -87,6 +94,48 @@ return null; } + // + // Create a new JavaScript object for a panel of given ID. + // + function makePanelFromId(id) { + if (!Overlays.isAddedPanel(id)) { + return null; + } + var panel = new OverlayPanel(); + panel._id = id; + overlays[id] = overlay; + return overlay; + } + + // + // Get or create a panel object from the id. + // + // @param knownOverlaysOnly (Optional: Boolean) + // If true, a new object will not be created. + // @param searchList (Optional: Object) + // Map of overlay id's and overlay objects. Can be generated with + // `OverlayManager.makeSearchList`. + // + function findPanel(id, knownPanelsOnly, searchList) { + if (id > 0) { + knownPanelsOnly = Boolean(knownPanelsOnly); + searchList = searchList || panels; + var foundPanel = searchList[id]; + if (foundPanel) { + return foundPanel; + } + if (!knownPanelsOnly) { + return makePanelFromId(id); + } + } + return null; + } + + function findOverlayOrPanel(id, knownObjectsOnly, searchList) { + return findOverlay(id, knownObjectsOnly, searchList) || + findPanel(id, knownObjectsOnly, searchList); + } + // // Perform global scoped operations on overlays, such as finding by ray intersection. @@ -94,7 +143,6 @@ OverlayManager = { findOnRay: function(pickRay, knownOverlaysOnly, searchList) { var rayPickResult = Overlays.findRayIntersection(pickRay); - print("raypick " + rayPickResult.overlayID); if (rayPickResult.intersects) { return findOverlay(rayPickResult.overlayID, knownOverlaysOnly, searchList); } @@ -102,7 +150,6 @@ }, findAtPoint: function(point, knownOverlaysOnly, searchList) { var foundID = Overlays.getOverlayAtPoint(point); - print("at point " + foundID); if (foundID) { return findOverlay(foundID, knownOverlaysOnly, searchList); } else { @@ -110,10 +157,10 @@ return OverlayManager.findOnRay(pickRay, knownOverlaysOnly, searchList); } }, - makeSearchList: function(overlayArray) { + makeSearchList: function(array) { var searchList = {}; - overlayArray.forEach(function(overlay){ - searchList[overlay._id] = overlay; + array.forEach(function(object) { + searchList[object._id] = object; }); return searchList; } @@ -125,7 +172,7 @@ // // Usage: // // Create an overlay - // var billboard = new BillboardOverlay({ + // var billboard = new Image3DOverlay({ // visible: true, // isFacingAvatar: true, // ignoreRayIntersections: false @@ -191,10 +238,6 @@ return that; } - // Supports multiple inheritance of properties. Just `concat` them onto the end of the - // properties list. - var PANEL_ATTACHABLE_FIELDS = ["offsetPosition", "facingRotation"]; - Overlay = (function() { var that = function(type, params) { if (type && params) { @@ -203,7 +246,6 @@ } else { this._id = 0; } - this._attachedPanelPointer = null; }; that.prototype.constructor = that; @@ -214,9 +256,9 @@ } }); - Object.defineProperty(that.prototype, "attachedPanel", { + Object.defineProperty(that.prototype, "parentPanel", { get: function() { - return this._attachedPanelPointer; + return findPanel(Overlays.getParentPanel(this._id)); } }); @@ -236,12 +278,21 @@ Overlays.deleteOverlay(this._id); }; + that.prototype.isPanelAttachable = function() { + return false; + }; + return generateOverlayClass(that, ABSTRACT, [ "alpha", "glowLevel", "pulseMax", "pulseMin", "pulsePeriod", "glowLevelPulse", "alphaPulse", "colorPulse", "visible", "anchor" ]); })(); + // Supports multiple inheritance of properties. Just `concat` them onto the end of the + // properties list. + PanelAttachable = ["offsetPosition", "offsetRotation", "offsetScale"]; + Billboardable = ["isFacingAvatar"]; + Overlay2D = generateOverlayClass(Overlay, ABSTRACT, [ "bounds", "x", "y", "width", "height" ]); @@ -255,6 +306,10 @@ "dimensions" ]); + Billboard3DOverlay = generateOverlayClass(Planar3DOverlay, ABSTRACT, [ + ].concat(PanelAttachable).concat(Billboardable)); + Billboard3DOverlay.prototype.isPanelAttachable = function() { return true; }; + Volume3DOverlay = generateOverlayClass(Base3DOverlay, ABSTRACT, [ "dimensions" ]); @@ -263,13 +318,17 @@ "subImage", "imageURL" ]); + generateOverlayClass(Billboard3DOverlay, "image3d", [ + "url", "subImage" + ]); + generateOverlayClass(Overlay2D, "text", [ "font", "text", "backgroundColor", "backgroundAlpha", "leftMargin", "topMargin" ]); - generateOverlayClass(Planar3DOverlay, "text3d", [ + generateOverlayClass(Billboard3DOverlay, "text3d", [ "text", "backgroundColor", "backgroundAlpha", "lineHeight", "leftMargin", "topMargin", - "rightMargin", "bottomMargin", "isFacingAvatar" + "rightMargin", "bottomMargin" ]); generateOverlayClass(Volume3DOverlay, "cube", [ @@ -302,13 +361,10 @@ generateOverlayClass(Volume3DOverlay, "model", [ "url", "dimensions", "textures" ]); - - generateOverlayClass(Planar3DOverlay, "billboard", [ - "url", "subImage", "isFacingAvatar" - ].concat(PANEL_ATTACHABLE_FIELDS)); })(); ImageOverlay = overlayTypes["image"]; + Image3DOverlay = overlayTypes["image3d"]; TextOverlay = overlayTypes["text"]; Text3DOverlay = overlayTypes["text3d"]; Cube3DOverlay = overlayTypes["cube"]; @@ -319,25 +375,24 @@ Grid3DOverlay = overlayTypes["grid"]; LocalModelsOverlay = overlayTypes["localmodels"]; ModelOverlay = overlayTypes["model"]; - BillboardOverlay = overlayTypes["billboard"]; // // Object oriented abstraction layer for panels. // - FloatingUIPanel = (function() { + OverlayPanel = (function() { var that = function(params) { this._id = Overlays.addPanel(params); - this._children = []; - this._visible = Boolean(params.visible); panels[this._id] = this; - this._attachedPanelPointer = null; }; that.prototype.constructor = that; - var FIELDS = ["offsetPosition", "offsetRotation", "facingRotation"]; - FIELDS.forEach(function(prop) { + var props = [ + "anchorPosition", "anchorPositionBinding", "anchorRotation", "anchorRotationBinding", "anchorScale", "visible" + ].concat(PanelAttachable).concat(Billboardable) + + props.forEach(function(prop) { Object.defineProperty(that.prototype, prop, { get: function() { return Overlays.getPanelProperty(this._id, prop); @@ -351,78 +406,47 @@ }); }); - var PSEUDO_FIELDS = []; - - PSEUDO_FIELDS.push("children"); - Object.defineProperty(that.prototype, "children", { + Object.defineProperty(that.prototype, "parentPanel", { get: function() { - return this._children.slice(); + return findPanel(Overlays.getParentPanel(this._id)); } }); - PSEUDO_FIELDS.push("visible"); - Object.defineProperty(that.prototype, "visible", { + Object.defineProperty(that.prototype, "children", { get: function() { - return this._visible; - }, - set: function(visible) { - this._visible = visible; - this._children.forEach(function(child) { - child.visible = visible; - }); + var idArray = Overlays.getPanelProperty(this._id, "children"); + var objArray = []; + for (var i = 0; i < idArray.length; i++) { + objArray[i] = findOverlayOrPanel(idArray[i]); + } + return objArray; } }); that.prototype.addChild = function(child) { - if (child instanceof Overlay) { - Overlays.setAttachedPanel(child._id, this._id); - } else if (child instanceof FloatingUIPanel) { - child.setProperties({ - anchorPosition: { - bind: "panel", - value: this._id - }, - offsetRotation: { - bind: "panel", - value: this._id - } - }); - } - child._attachedPanelPointer = this; - child.visible = this.visible; - this._children.push(child); + Overlays.setParentPanel(child._id, this._id); return child; }; that.prototype.removeChild = function(child) { - var i = this._children.indexOf(child); - if (i >= 0) { - if (child instanceof Overlay) { - Overlays.setAttachedPanel(child._id, 0); - } else if (child instanceof FloatingUIPanel) { - child.setProperties({ - anchorPosition: { - bind: "myAvatar" - }, - offsetRotation: { - bind: "myAvatar" - } - }); - } - child._attachedPanelPointer = null; - this._children.splice(i, 1); + if (child.parentPanel === this) { + Overlays.setParentPanel(child._id, 0); } }; that.prototype.setProperties = function(properties) { - for (var i in PSEUDO_FIELDS) { - if (properties[PSEUDO_FIELDS[i]] !== undefined) { - this[PSEUDO_FIELDS[i]] = properties[PSEUDO_FIELDS[i]]; - } - } Overlays.editPanel(this._id, properties); }; + that.prototype.setChildrenVisible = function() { + this.children.forEach(function(child) { + child.visible = true; + if (child.setChildrenVisible !== undefined) { + child.setChildrenVisible(); + } + }); + }; + that.prototype.destroy = function() { Overlays.deletePanel(this._id); }; @@ -433,8 +457,8 @@ function onOverlayDeleted(id) { if (id in overlays) { - if (overlays[id]._attachedPanelPointer) { - overlays[id]._attachedPanelPointer.removeChild(overlays[id]); + if (overlays[id].parentPanel) { + overlays[id].parentPanel.removeChild(overlays[id]); } delete overlays[id]; } @@ -442,10 +466,9 @@ function onPanelDeleted(id) { if (id in panels) { - panels[id]._children.forEach(function(child) { - print(JSON.stringify(child.destroy)); - child.destroy(); - }); + if (panels[id].parentPanel) { + panels[id].parentPanel.removeChild(panels[id]); + } delete panels[id]; } } diff --git a/examples/libraries/virtualKeyboard.js b/examples/libraries/virtualKeyboard.js index a1f952a5eb..7a0ec80a98 100644 --- a/examples/libraries/virtualKeyboard.js +++ b/examples/libraries/virtualKeyboard.js @@ -148,7 +148,7 @@ KeyboardKey = (function(keyboard, keyProperties) { }; for (var i = 0; i < this.bounds.length; i++) { if (THREE_D_MODE) { - this.overlays.push(Overlays.addOverlay("billboard", { + this.overlays.push(Overlays.addOverlay("image3d", { scale: 1, rotation: MyAvatar.rotation, isFacingAvatar: false, @@ -202,7 +202,7 @@ Keyboard = (function(params) { return windowDimensions.y - this.height(); }; if (THREE_D_MODE) { - this.background = Overlays.addOverlay("billboard", { + this.background = Overlays.addOverlay("image3d", { scale: 1, position: MyAvatar.position, rotation: MyAvatar.rotation, diff --git a/examples/notifications.js b/examples/notifications.js index 5ee6874521..461d28cd2c 100644 --- a/examples/notifications.js +++ b/examples/notifications.js @@ -260,7 +260,7 @@ function notify(notice, button, height) { positions = calculate3DOverlayPositions(noticeWidth, noticeHeight, notice.y); notifications.push((Overlays.addOverlay("text3d", notice))); - buttons.push((Overlays.addOverlay("billboard", button))); + buttons.push((Overlays.addOverlay("image3d", button))); overlay3DDetails.push({ notificationOrientation: positions.notificationOrientation, notificationPosition: positions.notificationPosition, diff --git a/examples/progress.js b/examples/progress.js index 55ea15f218..6dd26a9a11 100644 --- a/examples/progress.js +++ b/examples/progress.js @@ -117,7 +117,7 @@ visible: false, ignoreRayIntersection: true }); - bar3D.overlay = Overlays.addOverlay("billboard", { + bar3D.overlay = Overlays.addOverlay("image3d", { url: BAR_URL, subImage: { x: BAR_WIDTH, y: 0, width: BAR_WIDTH, height: BAR_HEIGHT }, scale: SCALE_3D * BAR_WIDTH, diff --git a/examples/sit.js b/examples/sit.js index da499b3b21..7efad487a7 100644 --- a/examples/sit.js +++ b/examples/sit.js @@ -175,7 +175,7 @@ function SeatIndicator(modelProperties, seatIndex) { modelProperties.sittingPoints[seatIndex].rotation); this.scale = MyAvatar.scale / 12; - this.sphere = Overlays.addOverlay("billboard", { + this.sphere = Overlays.addOverlay("image3d", { subImage: { x: 0, y: buttonHeight, width: buttonWidth, height: buttonHeight}, url: buttonImageUrl, position: this.position, diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 503acc2ce3..32aa0283f2 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3335,16 +3335,6 @@ void Application::computeOffAxisFrustum(float& left, float& right, float& bottom } } -bool Application::getShadowsEnabled() { - Menu* menubar = Menu::getInstance(); - return menubar->isOptionChecked(MenuOption::SimpleShadows) || - menubar->isOptionChecked(MenuOption::CascadedShadows); -} - -bool Application::getCascadeShadowsEnabled() { - return Menu::getInstance()->isOptionChecked(MenuOption::CascadedShadows); -} - glm::vec2 Application::getScaledScreenPoint(glm::vec2 projectedPoint) { float horizontalScale = _glWidget->getDeviceWidth() / 2.0f; float verticalScale = _glWidget->getDeviceHeight() / 2.0f; diff --git a/interface/src/Application.h b/interface/src/Application.h index 56c126462a..367efa9924 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -268,8 +268,6 @@ public: float& farVal, glm::vec4& nearClipPlane, glm::vec4& farClipPlane) const; virtual ViewFrustum* getCurrentViewFrustum() { return getDisplayViewFrustum(); } - virtual bool getShadowsEnabled(); - virtual bool getCascadeShadowsEnabled(); virtual QThread* getMainThread() { return thread(); } virtual float getSizeScale() const; virtual int getBoundaryLevelAdjust() const; diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 6fcc46478e..8a037c2d85 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -346,12 +346,6 @@ Menu::Menu() { ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight8, 0, false)); ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight9, 0, false)); - MenuWrapper* shadowMenu = renderOptionsMenu->addMenu("Shadows"); - QActionGroup* shadowGroup = new QActionGroup(shadowMenu); - shadowGroup->addAction(addCheckableActionToQMenuAndActionHash(shadowMenu, "None", 0, true)); - shadowGroup->addAction(addCheckableActionToQMenuAndActionHash(shadowMenu, MenuOption::SimpleShadows, 0, false)); - shadowGroup->addAction(addCheckableActionToQMenuAndActionHash(shadowMenu, MenuOption::CascadedShadows, 0, false)); - { MenuWrapper* framerateMenu = renderOptionsMenu->addMenu(MenuOption::RenderTargetFramerate); QActionGroup* framerateGroup = new QActionGroup(framerateMenu); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index bd6c982d70..89876bccb8 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -154,7 +154,6 @@ namespace MenuOption { const QString BlueSpeechSphere = "Blue Sphere While Speaking"; const QString BookmarkLocation = "Bookmark Location"; const QString Bookmarks = "Bookmarks"; - const QString CascadedShadows = "Cascaded"; const QString CachesSize = "RAM Caches Size"; const QString CalibrateCamera = "Calibrate Camera"; const QString CenterPlayerInView = "Center Player In View"; @@ -271,7 +270,6 @@ namespace MenuOption { const QString ShowIKConstraints = "Show IK Constraints"; const QString ShowRealtimeEntityStats = "Show Realtime Entity Stats"; const QString ShowWhosLookingAtMe = "Show Who's Looking at Me"; - const QString SimpleShadows = "Simple"; const QString SixenseEnabled = "Enable Hydra Support"; const QString SixenseMouseInput = "Enable Sixense Mouse Input"; const QString Stars = "Stars"; diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index b25eaa4030..a5388208c6 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -717,6 +717,29 @@ Transform Avatar::calculateDisplayNameTransform(const ViewFrustum& frustum, floa // Compute correct scale to apply float scale = DESIRED_HIGHT_ON_SCREEN / (fontSize * pixelHeight) * devicePixelRatio; +#ifdef DEBUG + // TODO: Temporary logging to track cause of invalid scale vale; remove once cause has been fixed. + if (scale == 0.0f || glm::isnan(scale) || glm::isinf(scale)) { + if (scale == 0.0f) { + qDebug() << "ASSERT because scale == 0.0f"; + } + if (glm::isnan(scale)) { + qDebug() << "ASSERT because isnan(scale)"; + } + if (glm::isinf(scale)) { + qDebug() << "ASSERT because isinf(scale)"; + } + qDebug() << "windowSizeY =" << windowSizeY; + qDebug() << "p1.y =" << p1.y; + qDebug() << "p1.w =" << p1.w; + qDebug() << "p0.y =" << p0.y; + qDebug() << "p0.w =" << p0.w; + qDebug() << "qApp->getDevicePixelRatio() =" << qApp->getDevicePixelRatio(); + qDebug() << "fontSize =" << fontSize; + qDebug() << "pixelHeight =" << pixelHeight; + qDebug() << "devicePixelRatio =" << devicePixelRatio; + } +#endif // Compute pixel alignment offset float clipToPix = 0.5f * windowSizeY / p1.w; // Got from clip to pixel coordinates diff --git a/interface/src/avatar/FaceModel.cpp b/interface/src/avatar/FaceModel.cpp index 066144a425..e31c804185 100644 --- a/interface/src/avatar/FaceModel.cpp +++ b/interface/src/avatar/FaceModel.cpp @@ -56,12 +56,12 @@ void FaceModel::simulate(float deltaTime, bool fullUpdate) { } } -void FaceModel::maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, int index) { +void FaceModel::maybeUpdateNeckRotation(const JointState& parentState, const JointState& state, int index) { // get the rotation axes in joint space and use them to adjust the rotation glm::mat3 axes = glm::mat3_cast(glm::quat()); glm::mat3 inverse = glm::mat3(glm::inverse(parentState.getTransform() * glm::translate(_rig->getJointDefaultTranslationInConstrainedFrame(index)) * - joint.preTransform * glm::mat4_cast(joint.preRotation))); + state.getPreTransform() * glm::mat4_cast(state.getPreRotation()))); glm::vec3 pitchYawRoll = safeEulerAngles(_owningHead->getFinalOrientationInLocalFrame()); glm::vec3 lean = glm::radians(glm::vec3(_owningHead->getFinalLeanForward(), _owningHead->getTorsoTwist(), @@ -71,15 +71,15 @@ void FaceModel::maybeUpdateNeckRotation(const JointState& parentState, const FBX glm::angleAxis(-pitchYawRoll.z, glm::normalize(inverse * axes[2])) * glm::angleAxis(pitchYawRoll.y, glm::normalize(inverse * axes[1])) * glm::angleAxis(-pitchYawRoll.x, glm::normalize(inverse * axes[0])) - * joint.rotation, DEFAULT_PRIORITY); + * state.getDefaultRotation(), DEFAULT_PRIORITY); } -void FaceModel::maybeUpdateEyeRotation(Model* model, const JointState& parentState, const FBXJoint& joint, int index) { +void FaceModel::maybeUpdateEyeRotation(Model* model, const JointState& parentState, const JointState& state, int index) { // likewise with the eye joints // NOTE: at the moment we do the math in the world-frame, hence the inverse transform is more complex than usual. glm::mat4 inverse = glm::inverse(glm::mat4_cast(model->getRotation()) * parentState.getTransform() * glm::translate(_rig->getJointDefaultTranslationInConstrainedFrame(index)) * - joint.preTransform * glm::mat4_cast(joint.preRotation * joint.rotation)); + state.getPreTransform() * glm::mat4_cast(state.getPreRotation() * state.getDefaultRotation())); glm::vec3 front = glm::vec3(inverse * glm::vec4(_owningHead->getFinalOrientationInWorldFrame() * IDENTITY_FRONT, 0.0f)); glm::vec3 lookAtDelta = _owningHead->getCorrectedLookAtPosition() - model->getTranslation(); glm::vec3 lookAt = glm::vec3(inverse * glm::vec4(lookAtDelta + glm::length(lookAtDelta) * _owningHead->getSaccade(), 1.0f)); @@ -87,22 +87,22 @@ void FaceModel::maybeUpdateEyeRotation(Model* model, const JointState& parentSta const float MAX_ANGLE = 30.0f * RADIANS_PER_DEGREE; _rig->setJointRotationInConstrainedFrame(index, glm::angleAxis(glm::clamp(glm::angle(between), -MAX_ANGLE, MAX_ANGLE), glm::axis(between)) * - joint.rotation, DEFAULT_PRIORITY); + state.getDefaultRotation(), DEFAULT_PRIORITY); } void FaceModel::maybeUpdateNeckAndEyeRotation(int index) { const JointState& state = _rig->getJointState(index); - const FBXJoint& joint = state.getFBXJoint(); const FBXGeometry& geometry = _geometry->getFBXGeometry(); + const int parentIndex = state.getParentIndex(); // guard against out-of-bounds access to _jointStates - if (joint.parentIndex != -1 && joint.parentIndex >= 0 && joint.parentIndex < _rig->getJointStateCount()) { - const JointState& parentState = _rig->getJointState(joint.parentIndex); + if (parentIndex != -1 && parentIndex >= 0 && parentIndex < _rig->getJointStateCount()) { + const JointState& parentState = _rig->getJointState(parentIndex); if (index == geometry.neckJointIndex) { - maybeUpdateNeckRotation(parentState, joint, index); + maybeUpdateNeckRotation(parentState, state, index); } else if (index == geometry.leftEyeJointIndex || index == geometry.rightEyeJointIndex) { - maybeUpdateEyeRotation(this, parentState, joint, index); + maybeUpdateEyeRotation(this, parentState, state, index); } } } diff --git a/interface/src/avatar/FaceModel.h b/interface/src/avatar/FaceModel.h index 54d685c73c..ed1ea28508 100644 --- a/interface/src/avatar/FaceModel.h +++ b/interface/src/avatar/FaceModel.h @@ -26,8 +26,8 @@ public: virtual void simulate(float deltaTime, bool fullUpdate = true); - void maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, int index); - void maybeUpdateEyeRotation(Model* model, const JointState& parentState, const FBXJoint& joint, int index); + void maybeUpdateNeckRotation(const JointState& parentState, const JointState& state, int index); + void maybeUpdateEyeRotation(Model* model, const JointState& parentState, const JointState& state, int index); void maybeUpdateNeckAndEyeRotation(int index); /// Retrieve the positions of up to two eye meshes. diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 83de16ab4f..c0f1913d49 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -402,7 +402,7 @@ bool SkeletonModel::getNeckParentRotationFromDefaultOrientation(glm::quat& neckP glm::quat worldFrameRotation; bool success = getJointRotationInWorldFrame(parentIndex, worldFrameRotation); if (success) { - neckParentRotation = worldFrameRotation * _rig->getJointState(parentIndex).getFBXJoint().inverseDefaultRotation; + neckParentRotation = worldFrameRotation * _rig->getJointState(parentIndex).getInverseDefaultRotation(); } return success; } @@ -478,18 +478,17 @@ void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) { for (int i = 0; i < numStates; i++) { // compute the default transform of this joint const JointState& state = _rig->getJointState(i); - const FBXJoint& joint = state.getFBXJoint(); - int parentIndex = joint.parentIndex; + int parentIndex = state.getParentIndex(); if (parentIndex == -1) { transforms[i] = _rig->getJointTransform(i); } else { - glm::quat modifiedRotation = joint.preRotation * joint.rotation * joint.postRotation; - transforms[i] = transforms[parentIndex] * glm::translate(joint.translation) - * joint.preTransform * glm::mat4_cast(modifiedRotation) * joint.postTransform; + glm::quat modifiedRotation = state.getPreRotation() * state.getDefaultRotation() * state.getPostRotation(); + transforms[i] = transforms[parentIndex] * glm::translate(state.getTranslation()) + * state.getPreTransform() * glm::mat4_cast(modifiedRotation) * state.getPostTransform(); } // Each joint contributes a sphere at its position - glm::vec3 axis(joint.boneRadius); + glm::vec3 axis(state.getBoneRadius()); glm::vec3 jointPosition = extractTranslation(transforms[i]); totalExtents.addPoint(jointPosition + axis); totalExtents.addPoint(jointPosition - axis); diff --git a/interface/src/ui/LodToolsDialog.cpp b/interface/src/ui/LodToolsDialog.cpp index 378a1391f4..4d476fb649 100644 --- a/interface/src/ui/LodToolsDialog.cpp +++ b/interface/src/ui/LodToolsDialog.cpp @@ -115,6 +115,8 @@ void LodToolsDialog::reject() { void LodToolsDialog::closeEvent(QCloseEvent* event) { this->QDialog::closeEvent(event); emit closed(); + +#if RESET_TO_AUTOMATIC_WHEN_YOU_CLOSE_THE_DIALOG_BOX auto lodManager = DependencyManager::get(); // always revert back to automatic LOD adjustment when closed @@ -124,6 +126,7 @@ void LodToolsDialog::closeEvent(QCloseEvent* event) { if (lodManager->getOctreeSizeScale() > DEFAULT_OCTREE_SIZE_SCALE) { lodManager->setOctreeSizeScale(DEFAULT_OCTREE_SIZE_SCALE); } +#endif } diff --git a/interface/src/ui/overlays/Billboard3DOverlay.cpp b/interface/src/ui/overlays/Billboard3DOverlay.cpp new file mode 100644 index 0000000000..0b27673e18 --- /dev/null +++ b/interface/src/ui/overlays/Billboard3DOverlay.cpp @@ -0,0 +1,46 @@ +// +// Billboard3DOverlay.cpp +// hifi/interface/src/ui/overlays +// +// Created by Zander Otavka on 8/4/15. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "Billboard3DOverlay.h" +#include "Application.h" + +Billboard3DOverlay::Billboard3DOverlay(const Billboard3DOverlay* billboard3DOverlay) : + Planar3DOverlay(billboard3DOverlay), + PanelAttachable(*billboard3DOverlay), + Billboardable(*billboard3DOverlay) +{ +} + +void Billboard3DOverlay::setProperties(const QScriptValue &properties) { + Planar3DOverlay::setProperties(properties); + PanelAttachable::setProperties(properties); + Billboardable::setProperties(properties); +} + +QScriptValue Billboard3DOverlay::getProperty(const QString &property) { + QScriptValue value; + value = Billboardable::getProperty(_scriptEngine, property); + if (value.isValid()) { + return value; + } + value = PanelAttachable::getProperty(_scriptEngine, property); + if (value.isValid()) { + return value; + } + return Planar3DOverlay::getProperty(property); +} + +void Billboard3DOverlay::applyTransformTo(Transform& transform, bool force) { + if (force || usecTimestampNow() > _transformExpiry) { + PanelAttachable::applyTransformTo(transform, true); + pointTransformAtCamera(transform, getOffsetRotation()); + } +} diff --git a/interface/src/ui/overlays/Billboard3DOverlay.h b/interface/src/ui/overlays/Billboard3DOverlay.h new file mode 100644 index 0000000000..2f5af1068f --- /dev/null +++ b/interface/src/ui/overlays/Billboard3DOverlay.h @@ -0,0 +1,33 @@ +// +// Billboard3DOverlay.h +// hifi/interface/src/ui/overlays +// +// Created by Zander Otavka on 8/4/15. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_Billboard3DOverlay_h +#define hifi_Billboard3DOverlay_h + +#include "Planar3DOverlay.h" +#include "PanelAttachable.h" +#include "Billboardable.h" + +class Billboard3DOverlay : public Planar3DOverlay, public PanelAttachable, public Billboardable { + Q_OBJECT + +public: + Billboard3DOverlay() {} + Billboard3DOverlay(const Billboard3DOverlay* billboard3DOverlay); + + virtual void setProperties(const QScriptValue& properties); + virtual QScriptValue getProperty(const QString& property); + +protected: + virtual void applyTransformTo(Transform& transform, bool force = false); +}; + +#endif // hifi_Billboard3DOverlay_h diff --git a/interface/src/ui/overlays/Billboardable.cpp b/interface/src/ui/overlays/Billboardable.cpp new file mode 100644 index 0000000000..c6edad501f --- /dev/null +++ b/interface/src/ui/overlays/Billboardable.cpp @@ -0,0 +1,41 @@ +// +// Billboardable.cpp +// interface/src/ui/overlays +// +// Created by Zander Otavka on 8/7/15. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "Billboardable.h" + +#include + +void Billboardable::setProperties(const QScriptValue &properties) { + QScriptValue isFacingAvatar = properties.property("isFacingAvatar"); + if (isFacingAvatar.isValid()) { + setIsFacingAvatar(isFacingAvatar.toVariant().toBool()); + } +} + +QScriptValue Billboardable::getProperty(QScriptEngine* scriptEngine, const QString &property) { + if (property == "isFacingAvatar") { + return isFacingAvatar(); + } + return QScriptValue(); +} + +void Billboardable::pointTransformAtCamera(Transform& transform, glm::quat offsetRotation) { + if (isFacingAvatar()) { + glm::vec3 billboardPos = transform.getTranslation(); + glm::vec3 cameraPos = Application::getInstance()->getCamera()->getPosition(); + glm::vec3 look = cameraPos - billboardPos; + float elevation = -asinf(look.y / glm::length(look)); + float azimuth = atan2f(look.x, look.z); + glm::quat rotation(glm::vec3(elevation, azimuth, 0)); + transform.setRotation(rotation); + transform.postRotate(offsetRotation); + } +} diff --git a/interface/src/ui/overlays/Billboardable.h b/interface/src/ui/overlays/Billboardable.h new file mode 100644 index 0000000000..1388f13e60 --- /dev/null +++ b/interface/src/ui/overlays/Billboardable.h @@ -0,0 +1,35 @@ +// +// Billboardable.h +// interface/src/ui/overlays +// +// Created by Zander Otavka on 8/7/15. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_Billboardable_h +#define hifi_Billboardable_h + +#include +#include + +#include + +class Billboardable { +public: + bool isFacingAvatar() const { return _isFacingAvatar; } + void setIsFacingAvatar(bool isFacingAvatar) { _isFacingAvatar = isFacingAvatar; } + +protected: + void setProperties(const QScriptValue& properties); + QScriptValue getProperty(QScriptEngine* scriptEngine, const QString& property); + + void pointTransformAtCamera(Transform& transform, glm::quat offsetRotation = {1, 0, 0, 0}); + +private: + bool _isFacingAvatar = false; +}; + +#endif // hifi_Billboardable_h diff --git a/interface/src/ui/overlays/FloatingUIPanel.cpp b/interface/src/ui/overlays/FloatingUIPanel.cpp deleted file mode 100644 index eedb4bffd1..0000000000 --- a/interface/src/ui/overlays/FloatingUIPanel.cpp +++ /dev/null @@ -1,197 +0,0 @@ -// -// FloatingUIPanel.cpp -// interface/src/ui/overlays -// -// Created by Zander Otavka on 7/2/15. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "FloatingUIPanel.h" - -#include -#include -#include - -#include "avatar/AvatarManager.h" -#include "avatar/MyAvatar.h" -#include "Application.h" -#include "Base3DOverlay.h" - -std::function const FloatingUIPanel::AVATAR_POSITION = []() -> glm::vec3 { - return DependencyManager::get()->getMyAvatar()->getPosition(); -}; - -std::function const FloatingUIPanel::AVATAR_ORIENTATION = []() -> glm::quat { - return DependencyManager::get()->getMyAvatar()->getOrientation() * - glm::angleAxis(glm::pi(), IDENTITY_UP); -}; - -glm::vec3 FloatingUIPanel::getPosition() const { - return getOffsetRotation() * getOffsetPosition() + getAnchorPosition(); -} - -glm::quat FloatingUIPanel::getRotation() const { - return getOffsetRotation() * getFacingRotation(); -} - -void FloatingUIPanel::setAnchorPosition(const glm::vec3& position) { - setAnchorPosition([position]() -> glm::vec3 { - return position; - }); -} - -void FloatingUIPanel::setOffsetRotation(const glm::quat& rotation) { - setOffsetRotation([rotation]() -> glm::quat { - return rotation; - }); -} - -void FloatingUIPanel::addChild(unsigned int childId) { - if (!_children.contains(childId)) { - _children.append(childId); - } -} - -void FloatingUIPanel::removeChild(unsigned int childId) { - if (_children.contains(childId)) { - _children.removeOne(childId); - } -} - -QScriptValue FloatingUIPanel::getProperty(const QString &property) { - if (property == "anchorPosition") { - return vec3toScriptValue(_scriptEngine, getAnchorPosition()); - } - if (property == "offsetRotation") { - return quatToScriptValue(_scriptEngine, getOffsetRotation()); - } - if (property == "offsetPosition") { - return vec3toScriptValue(_scriptEngine, getOffsetPosition()); - } - if (property == "facingRotation") { - return quatToScriptValue(_scriptEngine, getFacingRotation()); - } - - return QScriptValue(); -} - -void FloatingUIPanel::setProperties(const QScriptValue &properties) { - QScriptValue anchor = properties.property("anchorPosition"); - if (anchor.isValid()) { - QScriptValue bindType = anchor.property("bind"); - QScriptValue value = anchor.property("value"); - - if (bindType.isValid()) { - QString bindTypeString = bindType.toVariant().toString(); - if (bindTypeString == "myAvatar") { - setAnchorPosition(AVATAR_POSITION); - } else if (value.isValid()) { - if (bindTypeString == "overlay") { - Overlay::Pointer overlay = Application::getInstance()->getOverlays() - .getOverlay(value.toVariant().toUInt()); - if (overlay->is3D()) { - auto overlay3D = std::static_pointer_cast(overlay); - setAnchorPosition([&overlay3D]() -> glm::vec3 { - return overlay3D->getPosition(); - }); - } - } else if (bindTypeString == "panel") { - FloatingUIPanel::Pointer panel = Application::getInstance()->getOverlays() - .getPanel(value.toVariant().toUInt()); - setAnchorPosition([panel]() -> glm::vec3 { - return panel->getPosition(); - }); - } else if (bindTypeString == "vec3") { - QScriptValue x = value.property("x"); - QScriptValue y = value.property("y"); - QScriptValue z = value.property("z"); - if (x.isValid() && y.isValid() && z.isValid()) { - glm::vec3 newPosition; - newPosition.x = x.toVariant().toFloat(); - newPosition.y = y.toVariant().toFloat(); - newPosition.z = z.toVariant().toFloat(); - setAnchorPosition(newPosition); - } - } - } - } - } - - QScriptValue offsetRotation = properties.property("offsetRotation"); - if (offsetRotation.isValid()) { - QScriptValue bindType = offsetRotation.property("bind"); - QScriptValue value = offsetRotation.property("value"); - - if (bindType.isValid()) { - QString bindTypeString = bindType.toVariant().toString(); - if (bindTypeString == "myAvatar") { - setOffsetRotation(AVATAR_ORIENTATION); - } else if (value.isValid()) { - if (bindTypeString == "overlay") { - Overlay::Pointer overlay = Application::getInstance()->getOverlays() - .getOverlay(value.toVariant().toUInt()); - if (overlay->is3D()) { - auto overlay3D = std::static_pointer_cast(overlay); - setOffsetRotation([&overlay3D]() -> glm::quat { - return overlay3D->getRotation(); - }); - } - } else if (bindTypeString == "panel") { - FloatingUIPanel::Pointer panel = Application::getInstance()->getOverlays() - .getPanel(value.toVariant().toUInt()); - setOffsetRotation([panel]() -> glm::quat { - return panel->getRotation(); - }); - } else if (bindTypeString == "quat") { - QScriptValue x = value.property("x"); - QScriptValue y = value.property("y"); - QScriptValue z = value.property("z"); - QScriptValue w = value.property("w"); - - if (x.isValid() && y.isValid() && z.isValid() && w.isValid()) { - glm::quat newRotation; - newRotation.x = x.toVariant().toFloat(); - newRotation.y = y.toVariant().toFloat(); - newRotation.z = z.toVariant().toFloat(); - newRotation.w = w.toVariant().toFloat(); - setOffsetRotation(newRotation); - } - } - } - } - } - - QScriptValue offsetPosition = properties.property("offsetPosition"); - if (offsetPosition.isValid()) { - QScriptValue x = offsetPosition.property("x"); - QScriptValue y = offsetPosition.property("y"); - QScriptValue z = offsetPosition.property("z"); - if (x.isValid() && y.isValid() && z.isValid()) { - glm::vec3 newPosition; - newPosition.x = x.toVariant().toFloat(); - newPosition.y = y.toVariant().toFloat(); - newPosition.z = z.toVariant().toFloat(); - setOffsetPosition(newPosition); - } - } - - QScriptValue facingRotation = properties.property("facingRotation"); - if (facingRotation.isValid()) { - QScriptValue x = facingRotation.property("x"); - QScriptValue y = facingRotation.property("y"); - QScriptValue z = facingRotation.property("z"); - QScriptValue w = facingRotation.property("w"); - - if (x.isValid() && y.isValid() && z.isValid() && w.isValid()) { - glm::quat newRotation; - newRotation.x = x.toVariant().toFloat(); - newRotation.y = y.toVariant().toFloat(); - newRotation.z = z.toVariant().toFloat(); - newRotation.w = w.toVariant().toFloat(); - setFacingRotation(newRotation); - } - } -} diff --git a/interface/src/ui/overlays/FloatingUIPanel.h b/interface/src/ui/overlays/FloatingUIPanel.h deleted file mode 100644 index f84ac32fac..0000000000 --- a/interface/src/ui/overlays/FloatingUIPanel.h +++ /dev/null @@ -1,63 +0,0 @@ -// -// FloatingUIPanel.h -// interface/src/ui/overlays -// -// Created by Zander Otavka on 7/2/15. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_FloatingUIPanel_h -#define hifi_FloatingUIPanel_h - -#include -#include - -#include -#include -#include - -class FloatingUIPanel : public QObject { - Q_OBJECT -public: - typedef std::shared_ptr Pointer; - - void init(QScriptEngine* scriptEngine) { _scriptEngine = scriptEngine; } - - glm::vec3 getAnchorPosition() const { return _anchorPosition(); } - glm::quat getOffsetRotation() const { return _offsetRotation(); } - glm::vec3 getOffsetPosition() const { return _offsetPosition; } - glm::quat getFacingRotation() const { return _facingRotation; } - glm::vec3 getPosition() const; - glm::quat getRotation() const; - - void setAnchorPosition(const std::function& func) { _anchorPosition = func; } - void setAnchorPosition(const glm::vec3& position); - void setOffsetRotation(const std::function& func) { _offsetRotation = func; } - void setOffsetRotation(const glm::quat& rotation); - void setOffsetPosition(const glm::vec3& position) { _offsetPosition = position; } - void setFacingRotation(const glm::quat& rotation) { _facingRotation = rotation; } - - const QList& getChildren() { return _children; } - void addChild(unsigned int childId); - void removeChild(unsigned int childId); - unsigned int popLastChild() { return _children.takeLast(); } - - QScriptValue getProperty(const QString& property); - void setProperties(const QScriptValue& properties); - -private: - static std::function const AVATAR_POSITION; - static std::function const AVATAR_ORIENTATION; - - std::function _anchorPosition{AVATAR_POSITION}; - std::function _offsetRotation{AVATAR_ORIENTATION}; - glm::vec3 _offsetPosition{0, 0, 0}; - glm::quat _facingRotation{1, 0, 0, 0}; - QScriptEngine* _scriptEngine; - QList _children; -}; - -#endif // hifi_FloatingUIPanel_h diff --git a/interface/src/ui/overlays/BillboardOverlay.cpp b/interface/src/ui/overlays/Image3DOverlay.cpp similarity index 67% rename from interface/src/ui/overlays/BillboardOverlay.cpp rename to interface/src/ui/overlays/Image3DOverlay.cpp index 58790c4722..c13dbbb139 100644 --- a/interface/src/ui/overlays/BillboardOverlay.cpp +++ b/interface/src/ui/overlays/Image3DOverlay.cpp @@ -1,15 +1,16 @@ // -// BillboardOverlay.cpp +// Image3DOverlay.cpp // // // Created by Clement on 7/1/14. +// Modified and renamed by Zander Otavka on 8/4/15 // Copyright 2014 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "BillboardOverlay.h" +#include "Image3DOverlay.h" #include @@ -17,53 +18,41 @@ #include #include #include -#include #include "Application.h" #include "GeometryUtil.h" -QString const BillboardOverlay::TYPE = "billboard"; +QString const Image3DOverlay::TYPE = "image3d"; -BillboardOverlay::BillboardOverlay() { +Image3DOverlay::Image3DOverlay() { _isLoaded = false; } -BillboardOverlay::BillboardOverlay(const BillboardOverlay* billboardOverlay) : - Planar3DOverlay(billboardOverlay), - PanelAttachable(billboardOverlay), - _url(billboardOverlay->_url), - _texture(billboardOverlay->_texture), - _fromImage(billboardOverlay->_fromImage), - _isFacingAvatar(billboardOverlay->_isFacingAvatar) +Image3DOverlay::Image3DOverlay(const Image3DOverlay* image3DOverlay) : + Billboard3DOverlay(image3DOverlay), + _url(image3DOverlay->_url), + _texture(image3DOverlay->_texture), + _fromImage(image3DOverlay->_fromImage) { } -void BillboardOverlay::setTransforms(Transform& transform) { - PanelAttachable::setTransforms(transform); - if (_isFacingAvatar) { - glm::quat rotation = Application::getInstance()->getCamera()->getOrientation(); - rotation *= glm::angleAxis(glm::pi(), IDENTITY_UP); - setRotation(rotation); - } +void Image3DOverlay::update(float deltatime) { + applyTransformTo(_transform); } -void BillboardOverlay::update(float deltatime) { - setTransforms(_transform); -} - -void BillboardOverlay::render(RenderArgs* args) { +void Image3DOverlay::render(RenderArgs* args) { if (!_texture) { _isLoaded = true; _texture = DependencyManager::get()->getTexture(_url); } - if (!_visible || !_texture || !_texture->isLoaded()) { + if (!_visible || !getParentVisible() || !_texture || !_texture->isLoaded()) { return; } Q_ASSERT(args->_batch); - auto batch = args->_batch; + gpu::Batch* batch = args->_batch; float imageWidth = _texture->getWidth(); float imageHeight = _texture->getHeight(); @@ -98,9 +87,10 @@ void BillboardOverlay::render(RenderArgs* args) { xColor color = getColor(); float alpha = getAlpha(); - setTransforms(_transform); + applyTransformTo(_transform, true); Transform transform = _transform; transform.postScale(glm::vec3(getDimensions(), 1.0f)); + transform.postRotate(glm::angleAxis(glm::pi(), IDENTITY_UP)); batch->setModelTransform(transform); batch->setResourceTexture(0, _texture->getGPUTexture()); @@ -114,15 +104,14 @@ void BillboardOverlay::render(RenderArgs* args) { batch->setResourceTexture(0, args->_whiteTexture); // restore default white color after me } -void BillboardOverlay::setProperties(const QScriptValue &properties) { - Planar3DOverlay::setProperties(properties); - PanelAttachable::setProperties(properties); +void Image3DOverlay::setProperties(const QScriptValue &properties) { + Billboard3DOverlay::setProperties(properties); QScriptValue urlValue = properties.property("url"); if (urlValue.isValid()) { QString newURL = urlValue.toVariant().toString(); if (newURL != _url) { - setBillboardURL(newURL); + setURL(newURL); } } @@ -156,50 +145,34 @@ void BillboardOverlay::setProperties(const QScriptValue &properties) { setClipFromSource(subImageRect); } } - - QScriptValue isFacingAvatarValue = properties.property("isFacingAvatar"); - if (isFacingAvatarValue.isValid()) { - _isFacingAvatar = isFacingAvatarValue.toVariant().toBool(); - } } -QScriptValue BillboardOverlay::getProperty(const QString& property) { +QScriptValue Image3DOverlay::getProperty(const QString& property) { if (property == "url") { return _url; } if (property == "subImage") { return qRectToScriptValue(_scriptEngine, _fromImage); } - if (property == "isFacingAvatar") { - return _isFacingAvatar; - } if (property == "offsetPosition") { return vec3toScriptValue(_scriptEngine, getOffsetPosition()); } - QScriptValue value = PanelAttachable::getProperty(_scriptEngine, property); - if (value.isValid()) { - return value; - } - return Planar3DOverlay::getProperty(property); + return Billboard3DOverlay::getProperty(property); } -void BillboardOverlay::setURL(const QString& url) { - setBillboardURL(url); -} - -void BillboardOverlay::setBillboardURL(const QString& url) { +void Image3DOverlay::setURL(const QString& url) { _url = url; _isLoaded = false; } -bool BillboardOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - float& distance, BoxFace& face) { +bool Image3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + float& distance, BoxFace& face) { if (_texture && _texture->isLoaded()) { // Make sure position and rotation is updated. - setTransforms(_transform); + applyTransformTo(_transform, true); - // Produce the dimensions of the billboard based on the image's aspect ratio and the overlay's scale. + // Produce the dimensions of the overlay based on the image's aspect ratio and the overlay's scale. bool isNull = _fromImage.isNull(); float width = isNull ? _texture->getWidth() : _fromImage.width(); float height = isNull ? _texture->getHeight() : _fromImage.height(); @@ -212,6 +185,6 @@ bool BillboardOverlay::findRayIntersection(const glm::vec3& origin, const glm::v return false; } -BillboardOverlay* BillboardOverlay::createClone() const { - return new BillboardOverlay(this); +Image3DOverlay* Image3DOverlay::createClone() const { + return new Image3DOverlay(this); } diff --git a/interface/src/ui/overlays/BillboardOverlay.h b/interface/src/ui/overlays/Image3DOverlay.h similarity index 60% rename from interface/src/ui/overlays/BillboardOverlay.h rename to interface/src/ui/overlays/Image3DOverlay.h index a034612e71..0c71fef173 100644 --- a/interface/src/ui/overlays/BillboardOverlay.h +++ b/interface/src/ui/overlays/Image3DOverlay.h @@ -1,30 +1,31 @@ // -// BillboardOverlay.h +// Image3DOverlay.h // // // Created by Clement on 7/1/14. +// Modified and renamed by Zander Otavka on 8/4/15 // Copyright 2014 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#ifndef hifi_BillboardOverlay_h -#define hifi_BillboardOverlay_h +#ifndef hifi_Image3DOverlay_h +#define hifi_Image3DOverlay_h #include -#include "Planar3DOverlay.h" -#include "PanelAttachable.h" +#include "Billboard3DOverlay.h" -class BillboardOverlay : public Planar3DOverlay, public PanelAttachable { +class Image3DOverlay : public Billboard3DOverlay { Q_OBJECT + public: static QString const TYPE; virtual QString getType() const { return TYPE; } - BillboardOverlay(); - BillboardOverlay(const BillboardOverlay* billboardOverlay); + Image3DOverlay(); + Image3DOverlay(const Image3DOverlay* image3DOverlay); virtual void render(RenderArgs* args); @@ -32,28 +33,20 @@ public: // setters void setURL(const QString& url); - void setIsFacingAvatar(bool isFacingAvatar) { _isFacingAvatar = isFacingAvatar; } + void setClipFromSource(const QRect& bounds) { _fromImage = bounds; } virtual void setProperties(const QScriptValue& properties); - void setClipFromSource(const QRect& bounds) { _fromImage = bounds; } virtual QScriptValue getProperty(const QString& property); virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face); - - virtual BillboardOverlay* createClone() const; -protected: - virtual void setTransforms(Transform& transform); + virtual Image3DOverlay* createClone() const; private: - void setBillboardURL(const QString& url); - QString _url; NetworkTexturePointer _texture; - - QRect _fromImage; // where from in the image to sample - bool _isFacingAvatar = true; + QRect _fromImage; // where from in the image to sample }; -#endif // hifi_BillboardOverlay_h +#endif // hifi_Image3DOverlay_h diff --git a/interface/src/ui/overlays/OverlayPanel.cpp b/interface/src/ui/overlays/OverlayPanel.cpp new file mode 100644 index 0000000000..db91b7e0e3 --- /dev/null +++ b/interface/src/ui/overlays/OverlayPanel.cpp @@ -0,0 +1,200 @@ +// +// OverlayPanel.cpp +// interface/src/ui/overlays +// +// Created by Zander Otavka on 7/2/15. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "OverlayPanel.h" + +#include +#include +#include +#include + +#include "avatar/AvatarManager.h" +#include "avatar/MyAvatar.h" +#include "Application.h" +#include "Base3DOverlay.h" + +PropertyBinding::PropertyBinding(QString avatar, QUuid entity) : + avatar(avatar), + entity(entity) +{ +} + +QScriptValue propertyBindingToScriptValue(QScriptEngine* engine, const PropertyBinding& value) { + QScriptValue obj = engine->newObject(); + + if (value.avatar == "MyAvatar") { + obj.setProperty("avatar", "MyAvatar"); + } else if (!value.entity.isNull()) { + obj.setProperty("entity", engine->newVariant(value.entity)); + } + + return obj; +} + +void propertyBindingFromScriptValue(const QScriptValue& object, PropertyBinding& value) { + QScriptValue avatar = object.property("avatar"); + QScriptValue entity = object.property("entity"); + + if (avatar.isValid() && !avatar.isNull()) { + value.avatar = avatar.toVariant().toString(); + } else if (entity.isValid() && !entity.isNull()) { + value.entity = entity.toVariant().toUuid(); + } +} + + +void OverlayPanel::addChild(unsigned int childId) { + if (!_children.contains(childId)) { + _children.append(childId); + } +} + +void OverlayPanel::removeChild(unsigned int childId) { + if (_children.contains(childId)) { + _children.removeOne(childId); + } +} + +QScriptValue OverlayPanel::getProperty(const QString &property) { + if (property == "anchorPosition") { + return vec3toScriptValue(_scriptEngine, getAnchorPosition()); + } + if (property == "anchorPositionBinding") { + return propertyBindingToScriptValue(_scriptEngine, + PropertyBinding(_anchorPositionBindMyAvatar ? + "MyAvatar" : "", + _anchorPositionBindEntity)); + } + if (property == "anchorRotation") { + return quatToScriptValue(_scriptEngine, getAnchorRotation()); + } + if (property == "anchorRotationBinding") { + return propertyBindingToScriptValue(_scriptEngine, + PropertyBinding(_anchorRotationBindMyAvatar ? + "MyAvatar" : "", + _anchorRotationBindEntity)); + } + if (property == "anchorScale") { + return vec3toScriptValue(_scriptEngine, getAnchorScale()); + } + if (property == "visible") { + return getVisible(); + } + if (property == "children") { + QScriptValue array = _scriptEngine->newArray(_children.length()); + for (int i = 0; i < _children.length(); i++) { + array.setProperty(i, _children[i]); + } + return array; + } + + QScriptValue value = Billboardable::getProperty(_scriptEngine, property); + if (value.isValid()) { + return value; + } + return PanelAttachable::getProperty(_scriptEngine, property); +} + +void OverlayPanel::setProperties(const QScriptValue &properties) { + PanelAttachable::setProperties(properties); + Billboardable::setProperties(properties); + + QScriptValue anchorPosition = properties.property("anchorPosition"); + if (anchorPosition.isValid() && + anchorPosition.property("x").isValid() && + anchorPosition.property("y").isValid() && + anchorPosition.property("z").isValid()) { + glm::vec3 newPosition; + vec3FromScriptValue(anchorPosition, newPosition); + setAnchorPosition(newPosition); + } + + QScriptValue anchorPositionBinding = properties.property("anchorPositionBinding"); + if (anchorPositionBinding.isValid()) { + PropertyBinding binding = {}; + propertyBindingFromScriptValue(anchorPositionBinding, binding); + _anchorPositionBindMyAvatar = binding.avatar == "MyAvatar"; + _anchorPositionBindEntity = binding.entity; + } + + QScriptValue anchorRotation = properties.property("anchorRotation"); + if (anchorRotation.isValid() && + anchorRotation.property("x").isValid() && + anchorRotation.property("y").isValid() && + anchorRotation.property("z").isValid() && + anchorRotation.property("w").isValid()) { + glm::quat newRotation; + quatFromScriptValue(anchorRotation, newRotation); + setAnchorRotation(newRotation); + } + + QScriptValue anchorRotationBinding = properties.property("anchorRotationBinding"); + if (anchorRotationBinding.isValid()) { + PropertyBinding binding = {}; + propertyBindingFromScriptValue(anchorPositionBinding, binding); + _anchorRotationBindMyAvatar = binding.avatar == "MyAvatar"; + _anchorRotationBindEntity = binding.entity; + } + + QScriptValue anchorScale = properties.property("anchorScale"); + if (anchorScale.isValid()) { + if (anchorScale.property("x").isValid() && + anchorScale.property("y").isValid() && + anchorScale.property("z").isValid()) { + glm::vec3 newScale; + vec3FromScriptValue(anchorScale, newScale); + setAnchorScale(newScale); + } else { + setAnchorScale(anchorScale.toVariant().toFloat()); + } + } + + QScriptValue visible = properties.property("visible"); + if (visible.isValid()) { + setVisible(visible.toVariant().toBool()); + } +} + +void OverlayPanel::applyTransformTo(Transform& transform, bool force) { + if (force || usecTimestampNow() > _transformExpiry) { + PanelAttachable::applyTransformTo(transform, true); + if (!getParentPanel()) { + if (_anchorPositionBindMyAvatar) { + transform.setTranslation(DependencyManager::get()->getMyAvatar() + ->getPosition()); + } else if (!_anchorPositionBindEntity.isNull()) { + transform.setTranslation(DependencyManager::get() + ->getEntityTree()->findEntityByID(_anchorPositionBindEntity) + ->getPosition()); + } else { + transform.setTranslation(getAnchorPosition()); + } + + if (_anchorRotationBindMyAvatar) { + transform.setRotation(DependencyManager::get()->getMyAvatar() + ->getOrientation()); + } else if (!_anchorRotationBindEntity.isNull()) { + transform.setRotation(DependencyManager::get() + ->getEntityTree()->findEntityByID(_anchorRotationBindEntity) + ->getRotation()); + } else { + transform.setRotation(getAnchorRotation()); + } + + transform.setScale(getAnchorScale()); + + transform.postTranslate(getOffsetPosition()); + transform.postRotate(getOffsetRotation()); + transform.postScale(getOffsetScale()); + } + pointTransformAtCamera(transform, getOffsetRotation()); + } +} diff --git a/interface/src/ui/overlays/OverlayPanel.h b/interface/src/ui/overlays/OverlayPanel.h new file mode 100644 index 0000000000..221763fe87 --- /dev/null +++ b/interface/src/ui/overlays/OverlayPanel.h @@ -0,0 +1,83 @@ +// +// OverlayPanel.h +// interface/src/ui/overlays +// +// Created by Zander Otavka on 7/2/15. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_OverlayPanel_h +#define hifi_OverlayPanel_h + +#include + +#include +#include +#include +#include + +#include "PanelAttachable.h" +#include "Billboardable.h" + +class PropertyBinding { +public: + PropertyBinding() {} + PropertyBinding(QString avatar, QUuid entity); + QString avatar; + QUuid entity; +}; + +QScriptValue propertyBindingToScriptValue(QScriptEngine* engine, const PropertyBinding& value); +void propertyBindingFromScriptValue(const QScriptValue& object, PropertyBinding& value); + + +class OverlayPanel : public QObject, public PanelAttachable, public Billboardable { + Q_OBJECT + +public: + typedef std::shared_ptr Pointer; + + void init(QScriptEngine* scriptEngine) { _scriptEngine = scriptEngine; } + + // getters + glm::vec3 getAnchorPosition() const { return _anchorTransform.getTranslation(); } + glm::quat getAnchorRotation() const { return _anchorTransform.getRotation(); } + glm::vec3 getAnchorScale() const { return _anchorTransform.getScale(); } + bool getVisible() const { return _visible; } + + // setters + void setAnchorPosition(const glm::vec3& position) { _anchorTransform.setTranslation(position); } + void setAnchorRotation(const glm::quat& rotation) { _anchorTransform.setRotation(rotation); } + void setAnchorScale(float scale) { _anchorTransform.setScale(scale); } + void setAnchorScale(const glm::vec3& scale) { _anchorTransform.setScale(scale); } + void setVisible(bool visible) { _visible = visible; } + + const QList& getChildren() { return _children; } + void addChild(unsigned int childId); + void removeChild(unsigned int childId); + unsigned int popLastChild() { return _children.takeLast(); } + + QScriptValue getProperty(const QString& property); + void setProperties(const QScriptValue& properties); + + virtual void applyTransformTo(Transform& transform, bool force = false); + +private: + Transform _anchorTransform; + + bool _anchorPositionBindMyAvatar = false; + QUuid _anchorPositionBindEntity; + + bool _anchorRotationBindMyAvatar = false; + QUuid _anchorRotationBindEntity; + + bool _visible = true; + QList _children; + + QScriptEngine* _scriptEngine; +}; + +#endif // hifi_OverlayPanel_h diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index c4acdbb482..cb235b37fb 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -18,7 +18,7 @@ #include #include "Application.h" -#include "BillboardOverlay.h" +#include "Image3DOverlay.h" #include "Circle3DOverlay.h" #include "Cube3DOverlay.h" #include "ImageOverlay.h" @@ -138,6 +138,8 @@ unsigned int Overlays::addOverlay(const QString& type, const QScriptValue& prope if (type == ImageOverlay::TYPE) { thisOverlay = std::make_shared(); + } else if (type == Image3DOverlay::TYPE || type == "billboard") { // "billboard" for backwards compatibility + thisOverlay = std::make_shared(); } else if (type == TextOverlay::TYPE) { thisOverlay = std::make_shared(); } else if (type == Text3DOverlay::TYPE) { @@ -158,8 +160,6 @@ unsigned int Overlays::addOverlay(const QString& type, const QScriptValue& prope thisOverlay = std::make_shared(Application::getInstance()->getEntityClipboardRenderer()); } else if (type == ModelOverlay::TYPE) { thisOverlay = std::make_shared(); - } else if (type == BillboardOverlay::TYPE) { - thisOverlay = std::make_shared(); } if (thisOverlay) { @@ -200,7 +200,12 @@ unsigned int Overlays::cloneOverlay(unsigned int id) { Overlay::Pointer thisOverlay = getOverlay(id); if (thisOverlay) { - return addOverlay(Overlay::Pointer(thisOverlay->createClone())); + unsigned int cloneId = addOverlay(Overlay::Pointer(thisOverlay->createClone())); + auto attachable = std::dynamic_pointer_cast(thisOverlay); + if (attachable && attachable->getParentPanel()) { + attachable->getParentPanel()->addChild(cloneId); + } + return cloneId; } return 0; // Not found @@ -251,9 +256,9 @@ void Overlays::deleteOverlay(unsigned int id) { } auto attachable = std::dynamic_pointer_cast(overlayToDelete); - if (attachable && attachable->getAttachedPanel()) { - attachable->getAttachedPanel()->removeChild(id); - attachable->setAttachedPanel(nullptr); + if (attachable && attachable->getParentPanel()) { + attachable->getParentPanel()->removeChild(id); + attachable->setParentPanel(nullptr); } QWriteLocker lock(&_deleteLock); @@ -270,28 +275,42 @@ QString Overlays::getOverlayType(unsigned int overlayId) const { return ""; } -unsigned int Overlays::getAttachedPanel(unsigned int childId) const { +unsigned int Overlays::getParentPanel(unsigned int childId) const { Overlay::Pointer overlay = getOverlay(childId); auto attachable = std::dynamic_pointer_cast(overlay); if (attachable) { - return _panels.key(attachable->getAttachedPanel()); + return _panels.key(attachable->getParentPanel()); + } else if (_panels.contains(childId)) { + return _panels.key(getPanel(childId)->getParentPanel()); } return 0; } -void Overlays::setAttachedPanel(unsigned int childId, unsigned int panelId) { - Overlay::Pointer overlay = getOverlay(childId); - auto attachable = std::dynamic_pointer_cast(overlay); +void Overlays::setParentPanel(unsigned int childId, unsigned int panelId) { + auto attachable = std::dynamic_pointer_cast(getOverlay(childId)); if (attachable) { if (_panels.contains(panelId)) { - auto panel = _panels[panelId]; + auto panel = getPanel(panelId); panel->addChild(childId); - attachable->setAttachedPanel(panel); + attachable->setParentPanel(panel); } else { - auto panel = attachable->getAttachedPanel(); + auto panel = attachable->getParentPanel(); if (panel) { panel->removeChild(childId); - attachable->setAttachedPanel(nullptr); + attachable->setParentPanel(nullptr); + } + } + } else if (_panels.contains(childId)) { + OverlayPanel::Pointer child = getPanel(childId); + if (_panels.contains(panelId)) { + auto panel = getPanel(panelId); + panel->addChild(childId); + child->setParentPanel(panel); + } else { + auto panel = child->getParentPanel(); + if (panel) { + panel->removeChild(childId); + child->setParentPanel(0); } } } @@ -510,7 +529,7 @@ QSizeF Overlays::textSize(unsigned int id, const QString& text) const { return QSizeF(0.0f, 0.0f); } -unsigned int Overlays::addPanel(FloatingUIPanel::Pointer panel) { +unsigned int Overlays::addPanel(OverlayPanel::Pointer panel) { QWriteLocker lock(&_lock); unsigned int thisID = _nextOverlayID; @@ -521,7 +540,7 @@ unsigned int Overlays::addPanel(FloatingUIPanel::Pointer panel) { } unsigned int Overlays::addPanel(const QScriptValue& properties) { - FloatingUIPanel::Pointer panel = std::make_shared(); + OverlayPanel::Pointer panel = std::make_shared(); panel->init(_scriptEngine); panel->setProperties(properties); return addPanel(panel); @@ -536,7 +555,7 @@ void Overlays::editPanel(unsigned int panelId, const QScriptValue& properties) { OverlayPropertyResult Overlays::getPanelProperty(unsigned int panelId, const QString& property) { OverlayPropertyResult result; if (_panels.contains(panelId)) { - FloatingUIPanel::Pointer thisPanel = _panels[panelId]; + OverlayPanel::Pointer thisPanel = getPanel(panelId); QReadLocker lock(&_lock); result.value = thisPanel->getProperty(property); } @@ -545,7 +564,7 @@ OverlayPropertyResult Overlays::getPanelProperty(unsigned int panelId, const QSt void Overlays::deletePanel(unsigned int panelId) { - FloatingUIPanel::Pointer panelToDelete; + OverlayPanel::Pointer panelToDelete; { QWriteLocker lock(&_lock); @@ -557,8 +576,14 @@ void Overlays::deletePanel(unsigned int panelId) { } while (!panelToDelete->getChildren().isEmpty()) { - deleteOverlay(panelToDelete->popLastChild()); + unsigned int childId = panelToDelete->popLastChild(); + deleteOverlay(childId); + deletePanel(childId); } emit panelDeleted(panelId); } + +bool Overlays::isAddedOverlay(unsigned int id) { + return _overlaysHUD.contains(id) || _overlaysWorld.contains(id); +} diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index ce2c3efeae..7afcf8a9b1 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -5,7 +5,7 @@ // Modified by Zander Otavka on 7/15/15 // Copyright 2014 High Fidelity, Inc. // -// Exposes methods for managing `Overlay`s and `FloatingUIPanel`s to scripts. +// Exposes methods for managing `Overlay`s and `OverlayPanel`s to scripts. // // YOU SHOULD NOT USE `Overlays` DIRECTLY, unless you like pain and deprecation. Instead, use the // object oriented abstraction layer found in `examples/libraries/overlayUtils.js`. @@ -22,7 +22,7 @@ #include "Overlay.h" -#include "FloatingUIPanel.h" +#include "OverlayPanel.h" #include "PanelAttachable.h" class PickRay; @@ -67,7 +67,7 @@ public: void renderHUD(RenderArgs* renderArgs); Overlay::Pointer getOverlay(unsigned int id) const; - FloatingUIPanel::Pointer getPanel(unsigned int id) const { return _panels[id]; } + OverlayPanel::Pointer getPanel(unsigned int id) const { return _panels[id]; } public slots: /// adds an overlay with the specific properties @@ -90,8 +90,8 @@ public slots: /// get the string type of the overlay used in addOverlay QString getOverlayType(unsigned int overlayId) const; - unsigned int getAttachedPanel(unsigned int childId) const; - void setAttachedPanel(unsigned int childId, unsigned int panelId); + unsigned int getParentPanel(unsigned int childId) const; + void setParentPanel(unsigned int childId, unsigned int panelId); /// returns the top most 2D overlay at the screen point, or 0 if not overlay at that point unsigned int getOverlayAtPoint(const glm::vec2& point); @@ -111,7 +111,7 @@ public slots: /// adds a panel that has already been created - unsigned int addPanel(FloatingUIPanel::Pointer panel); + unsigned int addPanel(OverlayPanel::Pointer panel); /// creates and adds a panel based on a set of properties unsigned int addPanel(const QScriptValue& properties); @@ -125,6 +125,12 @@ public slots: /// deletes a panel and all child overlays void deletePanel(unsigned int panelId); + /// return true if there is an overlay with that id else false + bool isAddedOverlay(unsigned int id); + + /// return true if there is a panel with that id else false + bool isAddedPanel(unsigned int id) { return _panels.contains(id); } + signals: void overlayDeleted(unsigned int id); void panelDeleted(unsigned int id); @@ -134,7 +140,7 @@ private: QMap _overlaysHUD; QMap _overlaysWorld; - QMap _panels; + QMap _panels; QList _overlaysToDelete; unsigned int _nextOverlayID; diff --git a/interface/src/ui/overlays/OverlaysPayload.cpp b/interface/src/ui/overlays/OverlaysPayload.cpp index e6b37d693b..5b91ff5b52 100644 --- a/interface/src/ui/overlays/OverlaysPayload.cpp +++ b/interface/src/ui/overlays/OverlaysPayload.cpp @@ -18,7 +18,7 @@ #include #include -#include "BillboardOverlay.h" +#include "Image3DOverlay.h" #include "Circle3DOverlay.h" #include "Cube3DOverlay.h" #include "ImageOverlay.h" diff --git a/interface/src/ui/overlays/PanelAttachable.cpp b/interface/src/ui/overlays/PanelAttachable.cpp index ac4730cfe8..91a7525257 100644 --- a/interface/src/ui/overlays/PanelAttachable.cpp +++ b/interface/src/ui/overlays/PanelAttachable.cpp @@ -13,25 +13,13 @@ #include -PanelAttachable::PanelAttachable() : - _attachedPanel(nullptr), - _facingRotation(1, 0, 0, 0) -{ -} +#include "OverlayPanel.h" -PanelAttachable::PanelAttachable(const PanelAttachable* panelAttachable) : - _attachedPanel(panelAttachable->_attachedPanel), - _offsetPosition(panelAttachable->_offsetPosition), - _facingRotation(panelAttachable->_facingRotation) -{ -} - -void PanelAttachable::setTransforms(Transform& transform) { - if (getAttachedPanel()) { - transform.setTranslation(getAttachedPanel()->getAnchorPosition()); - transform.setRotation(getAttachedPanel()->getOffsetRotation()); - transform.postTranslate(getOffsetPosition() + getAttachedPanel()->getOffsetPosition()); - transform.postRotate(getFacingRotation() * getAttachedPanel()->getFacingRotation()); +bool PanelAttachable::getParentVisible() const { + if (getParentPanel()) { + return getParentPanel()->getVisible() && getParentPanel()->getParentVisible(); + } else { + return true; } } @@ -39,42 +27,60 @@ QScriptValue PanelAttachable::getProperty(QScriptEngine* scriptEngine, const QSt if (property == "offsetPosition") { return vec3toScriptValue(scriptEngine, getOffsetPosition()); } - if (property == "facingRotation") { - return quatToScriptValue(scriptEngine, getFacingRotation()); + if (property == "offsetRotation") { + return quatToScriptValue(scriptEngine, getOffsetRotation()); + } + if (property == "offsetScale") { + return vec3toScriptValue(scriptEngine, getOffsetScale()); } return QScriptValue(); } void PanelAttachable::setProperties(const QScriptValue &properties) { QScriptValue offsetPosition = properties.property("offsetPosition"); - if (offsetPosition.isValid()) { - QScriptValue x = offsetPosition.property("x"); - QScriptValue y = offsetPosition.property("y"); - QScriptValue z = offsetPosition.property("z"); - - if (x.isValid() && y.isValid() && z.isValid()) { - glm::vec3 newPosition; - newPosition.x = x.toVariant().toFloat(); - newPosition.y = y.toVariant().toFloat(); - newPosition.z = z.toVariant().toFloat(); - setOffsetPosition(newPosition); - } + if (offsetPosition.isValid() && + offsetPosition.property("x").isValid() && + offsetPosition.property("y").isValid() && + offsetPosition.property("z").isValid()) { + glm::vec3 newPosition; + vec3FromScriptValue(offsetPosition, newPosition); + setOffsetPosition(newPosition); } - QScriptValue facingRotation = properties.property("facingRotation"); - if (facingRotation.isValid()) { - QScriptValue x = facingRotation.property("x"); - QScriptValue y = facingRotation.property("y"); - QScriptValue z = facingRotation.property("z"); - QScriptValue w = facingRotation.property("w"); + QScriptValue offsetRotation = properties.property("offsetRotation"); + if (offsetRotation.isValid() && + offsetRotation.property("x").isValid() && + offsetRotation.property("y").isValid() && + offsetRotation.property("z").isValid() && + offsetRotation.property("w").isValid()) { + glm::quat newRotation; + quatFromScriptValue(offsetRotation, newRotation); + setOffsetRotation(newRotation); + } - if (x.isValid() && y.isValid() && z.isValid() && w.isValid()) { - glm::quat newRotation; - newRotation.x = x.toVariant().toFloat(); - newRotation.y = y.toVariant().toFloat(); - newRotation.z = z.toVariant().toFloat(); - newRotation.w = w.toVariant().toFloat(); - setFacingRotation(newRotation); + QScriptValue offsetScale = properties.property("offsetScale"); + if (offsetScale.isValid()) { + if (offsetScale.property("x").isValid() && + offsetScale.property("y").isValid() && + offsetScale.property("z").isValid()) { + glm::vec3 newScale; + vec3FromScriptValue(offsetScale, newScale); + setOffsetScale(newScale); + } else { + setOffsetScale(offsetScale.toVariant().toFloat()); + } + } +} + +void PanelAttachable::applyTransformTo(Transform& transform, bool force) { + if (force || usecTimestampNow() > _transformExpiry) { + const quint64 TRANSFORM_UPDATE_PERIOD = 100000; // frequency is 10 Hz + _transformExpiry = usecTimestampNow() + TRANSFORM_UPDATE_PERIOD; + if (getParentPanel()) { + getParentPanel()->applyTransformTo(transform, true); + transform.postTranslate(getOffsetPosition()); + transform.postRotate(getOffsetRotation()); + transform.postScale(getOffsetScale()); } } } diff --git a/interface/src/ui/overlays/PanelAttachable.h b/interface/src/ui/overlays/PanelAttachable.h index 9776ac5ecb..5242450e5d 100644 --- a/interface/src/ui/overlays/PanelAttachable.h +++ b/interface/src/ui/overlays/PanelAttachable.h @@ -12,34 +12,42 @@ #ifndef hifi_PanelAttachable_h #define hifi_PanelAttachable_h -#include "FloatingUIPanel.h" +#include #include #include +#include +#include +#include + +class OverlayPanel; class PanelAttachable { public: - PanelAttachable(); - PanelAttachable(const PanelAttachable* panelAttachable); + // getters + std::shared_ptr getParentPanel() const { return _parentPanel; } + glm::vec3 getOffsetPosition() const { return _offset.getTranslation(); } + glm::quat getOffsetRotation() const { return _offset.getRotation(); } + glm::vec3 getOffsetScale() const { return _offset.getScale(); } + bool getParentVisible() const; - FloatingUIPanel::Pointer getAttachedPanel() const { return _attachedPanel; } - glm::vec3 getOffsetPosition() const { return _offsetPosition; } - glm::quat getFacingRotation() const { return _facingRotation; } - - void setAttachedPanel(FloatingUIPanel::Pointer panel) { _attachedPanel = panel; } - void setOffsetPosition(const glm::vec3& position) { _offsetPosition = position; } - void setFacingRotation(const glm::quat& rotation) { _facingRotation = rotation; } + // setters + void setParentPanel(std::shared_ptr panel) { _parentPanel = panel; } + void setOffsetPosition(const glm::vec3& position) { _offset.setTranslation(position); } + void setOffsetRotation(const glm::quat& rotation) { _offset.setRotation(rotation); } + void setOffsetScale(float scale) { _offset.setScale(scale); } + void setOffsetScale(const glm::vec3& scale) { _offset.setScale(scale); } +protected: QScriptValue getProperty(QScriptEngine* scriptEngine, const QString& property); void setProperties(const QScriptValue& properties); -protected: - virtual void setTransforms(Transform& transform); + virtual void applyTransformTo(Transform& transform, bool force = false); + quint64 _transformExpiry = 0; private: - FloatingUIPanel::Pointer _attachedPanel; - glm::vec3 _offsetPosition; - glm::quat _facingRotation; + std::shared_ptr _parentPanel = nullptr; + Transform _offset; }; #endif // hifi_PanelAttachable_h diff --git a/interface/src/ui/overlays/Text3DOverlay.cpp b/interface/src/ui/overlays/Text3DOverlay.cpp index 00a7d75090..d4df8553c8 100644 --- a/interface/src/ui/overlays/Text3DOverlay.cpp +++ b/interface/src/ui/overlays/Text3DOverlay.cpp @@ -32,14 +32,13 @@ Text3DOverlay::Text3DOverlay() : _leftMargin(DEFAULT_MARGIN), _topMargin(DEFAULT_MARGIN), _rightMargin(DEFAULT_MARGIN), - _bottomMargin(DEFAULT_MARGIN), - _isFacingAvatar(false) + _bottomMargin(DEFAULT_MARGIN) { _textRenderer = TextRenderer3D::getInstance(SANS_FONT_FAMILY, FIXED_FONT_POINT_SIZE); } Text3DOverlay::Text3DOverlay(const Text3DOverlay* text3DOverlay) : - Planar3DOverlay(text3DOverlay), + Billboard3DOverlay(text3DOverlay), _text(text3DOverlay->_text), _backgroundColor(text3DOverlay->_backgroundColor), _backgroundAlpha(text3DOverlay->_backgroundAlpha), @@ -47,8 +46,7 @@ Text3DOverlay::Text3DOverlay(const Text3DOverlay* text3DOverlay) : _leftMargin(text3DOverlay->_leftMargin), _topMargin(text3DOverlay->_topMargin), _rightMargin(text3DOverlay->_rightMargin), - _bottomMargin(text3DOverlay->_bottomMargin), - _isFacingAvatar(text3DOverlay->_isFacingAvatar) + _bottomMargin(text3DOverlay->_bottomMargin) { _textRenderer = TextRenderer3D::getInstance(SANS_FONT_FAMILY, FIXED_FONT_POINT_SIZE); } @@ -76,41 +74,30 @@ xColor Text3DOverlay::getBackgroundColor() { return result; } +void Text3DOverlay::update(float deltatime) { + applyTransformTo(_transform); +} void Text3DOverlay::render(RenderArgs* args) { - if (!_visible) { + if (!_visible || !getParentVisible()) { return; // do nothing if we're not visible } - Q_ASSERT(args->_batch); auto& batch = *args->_batch; - glm::quat rotation; - - if (_isFacingAvatar) { - // rotate about vertical to face the camera - rotation = args->_viewFrustum->getOrientation(); - } else { - rotation = getRotation(); - } - - Transform transform; - transform.setTranslation(getPosition()); - transform.setRotation(rotation); - transform.setScale(getScale()); - - batch.setModelTransform(transform); - + applyTransformTo(_transform, true); + batch.setModelTransform(_transform); + const float MAX_COLOR = 255.0f; xColor backgroundColor = getBackgroundColor(); - glm::vec4 quadColor(backgroundColor.red / MAX_COLOR, backgroundColor.green / MAX_COLOR, backgroundColor.blue / MAX_COLOR, - getBackgroundAlpha()); + glm::vec4 quadColor(backgroundColor.red / MAX_COLOR, backgroundColor.green / MAX_COLOR, + backgroundColor.blue / MAX_COLOR, getBackgroundAlpha()); glm::vec2 dimensions = getDimensions(); glm::vec2 halfDimensions = dimensions * 0.5f; - const float SLIGHTLY_BEHIND = -0.005f; + const float SLIGHTLY_BEHIND = -0.001f; glm::vec3 topLeft(-halfDimensions.x, -halfDimensions.y, SLIGHTLY_BEHIND); glm::vec3 bottomRight(halfDimensions.x, halfDimensions.y, SLIGHTLY_BEHIND); @@ -125,20 +112,20 @@ void Text3DOverlay::render(RenderArgs* args) { glm::vec2 clipMinimum(0.0f, 0.0f); glm::vec2 clipDimensions((dimensions.x - (_leftMargin + _rightMargin)) / scaleFactor, (dimensions.y - (_topMargin + _bottomMargin)) / scaleFactor); - - transform.setTranslation(getPosition()); - transform.postTranslate(glm::vec3(-(halfDimensions.x - _leftMargin) , halfDimensions.y - _topMargin, 0.01f)); + + Transform transform = _transform; + transform.postTranslate(glm::vec3(-(halfDimensions.x - _leftMargin), + halfDimensions.y - _topMargin, 0.001f)); transform.setScale(scaleFactor); batch.setModelTransform(transform); - - glm::vec4 textColor = { _color.red / MAX_COLOR, _color.green / MAX_COLOR, _color.blue / MAX_COLOR, getAlpha() }; + + glm::vec4 textColor = { _color.red / MAX_COLOR, _color.green / MAX_COLOR, + _color.blue / MAX_COLOR, getAlpha() }; _textRenderer->draw(batch, 0, 0, _text, textColor); - - batch.setPipeline(DrawOverlay3D::getOpaquePipeline()); } void Text3DOverlay::setProperties(const QScriptValue& properties) { - Planar3DOverlay::setProperties(properties); + Billboard3DOverlay::setProperties(properties); QScriptValue text = properties.property("text"); if (text.isValid()) { @@ -180,12 +167,6 @@ void Text3DOverlay::setProperties(const QScriptValue& properties) { if (properties.property("bottomMargin").isValid()) { setBottomMargin(properties.property("bottomMargin").toVariant().toFloat()); } - - QScriptValue isFacingAvatarValue = properties.property("isFacingAvatar"); - if (isFacingAvatarValue.isValid()) { - _isFacingAvatar = isFacingAvatarValue.toVariant().toBool(); - } - } QScriptValue Text3DOverlay::getProperty(const QString& property) { @@ -213,10 +194,8 @@ QScriptValue Text3DOverlay::getProperty(const QString& property) { if (property == "bottomMargin") { return _bottomMargin; } - if (property == "isFacingAvatar") { - return _isFacingAvatar; - } - return Planar3DOverlay::getProperty(property); + + return Billboard3DOverlay::getProperty(property); } Text3DOverlay* Text3DOverlay::createClone() const { @@ -232,3 +211,7 @@ QSizeF Text3DOverlay::textSize(const QString& text) const { return QSizeF(extents.x, extents.y) * pointToWorldScale; } +bool Text3DOverlay::findRayIntersection(const glm::vec3 &origin, const glm::vec3 &direction, float &distance, BoxFace &face) { + applyTransformTo(_transform, true); + return Billboard3DOverlay::findRayIntersection(origin, direction, distance, face); +} diff --git a/interface/src/ui/overlays/Text3DOverlay.h b/interface/src/ui/overlays/Text3DOverlay.h index abd5ef54bd..e5e0ff7b96 100644 --- a/interface/src/ui/overlays/Text3DOverlay.h +++ b/interface/src/ui/overlays/Text3DOverlay.h @@ -13,11 +13,11 @@ #include -#include "Planar3DOverlay.h" +#include "Billboard3DOverlay.h" class TextRenderer3D; -class Text3DOverlay : public Planar3DOverlay { +class Text3DOverlay : public Billboard3DOverlay { Q_OBJECT public: @@ -29,6 +29,8 @@ public: ~Text3DOverlay(); virtual void render(RenderArgs* args); + virtual void update(float deltatime); + // getters const QString& getText() const { return _text; } float getLineHeight() const { return _lineHeight; } @@ -36,7 +38,6 @@ public: float getTopMargin() const { return _topMargin; } float getRightMargin() const { return _rightMargin; } float getBottomMargin() const { return _bottomMargin; } - bool getIsFacingAvatar() const { return _isFacingAvatar; } xColor getBackgroundColor(); float getBackgroundAlpha() const { return _backgroundAlpha; } @@ -47,13 +48,14 @@ public: void setTopMargin(float margin) { _topMargin = margin; } void setRightMargin(float margin) { _rightMargin = margin; } void setBottomMargin(float margin) { _bottomMargin = margin; } - void setIsFacingAvatar(bool isFacingAvatar) { _isFacingAvatar = isFacingAvatar; } virtual void setProperties(const QScriptValue& properties); virtual QScriptValue getProperty(const QString& property); QSizeF textSize(const QString& test) const; // Meters + virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face); + virtual Text3DOverlay* createClone() const; private: @@ -67,7 +69,6 @@ private: float _topMargin; float _rightMargin; float _bottomMargin; - bool _isFacingAvatar; }; diff --git a/libraries/animation/src/AvatarRig.cpp b/libraries/animation/src/AvatarRig.cpp index 97fbb82944..ebf0a8795e 100644 --- a/libraries/animation/src/AvatarRig.cpp +++ b/libraries/animation/src/AvatarRig.cpp @@ -17,7 +17,6 @@ void AvatarRig::updateJointState(int index, glm::mat4 parentTransform) { return; // bail } JointState& state = _jointStates[index]; - const FBXJoint& joint = state.getFBXJoint(); // compute model transforms if (index == _rootJointIndex) { @@ -26,7 +25,7 @@ void AvatarRig::updateJointState(int index, glm::mat4 parentTransform) { clearJointTransformTranslation(index); } else { // guard against out-of-bounds access to _jointStates - int parentIndex = joint.parentIndex; + int parentIndex = state.getParentIndex(); if (parentIndex >= 0 && parentIndex < _jointStates.size()) { const JointState& parentState = _jointStates.at(parentIndex); state.computeTransform(parentState.getTransform(), parentState.getTransformChanged()); @@ -53,8 +52,8 @@ void AvatarRig::setHandPosition(int jointIndex, } // precomputed lengths - float upperArmLength = _jointStates[elbowJointIndex].getFBXJoint().distanceToParent * scale; - float lowerArmLength = _jointStates[jointIndex].getFBXJoint().distanceToParent * scale; + float upperArmLength = _jointStates[elbowJointIndex].getDistanceToParent() * scale; + float lowerArmLength = _jointStates[jointIndex].getDistanceToParent() * scale; // first set wrist position glm::vec3 wristPosition = position; diff --git a/libraries/animation/src/EntityRig.cpp b/libraries/animation/src/EntityRig.cpp index 5ed1799671..eddbdd6464 100644 --- a/libraries/animation/src/EntityRig.cpp +++ b/libraries/animation/src/EntityRig.cpp @@ -14,15 +14,14 @@ /// Updates the state of the joint at the specified index. void EntityRig::updateJointState(int index, glm::mat4 parentTransform) { JointState& state = _jointStates[index]; - const FBXJoint& joint = state.getFBXJoint(); // compute model transforms - int parentIndex = joint.parentIndex; + int parentIndex = state.getParentIndex(); if (parentIndex == -1) { state.computeTransform(parentTransform); } else { // guard against out-of-bounds access to _jointStates - if (joint.parentIndex >= 0 && joint.parentIndex < _jointStates.size()) { + if (parentIndex >= 0 && parentIndex < _jointStates.size()) { const JointState& parentState = _jointStates.at(parentIndex); state.computeTransform(parentState.getTransform(), parentState.getTransformChanged()); } diff --git a/libraries/animation/src/JointState.cpp b/libraries/animation/src/JointState.cpp index 1253dc4649..78da30fdcd 100644 --- a/libraries/animation/src/JointState.cpp +++ b/libraries/animation/src/JointState.cpp @@ -18,17 +18,14 @@ #include "JointState.h" -JointState::JointState() : - _animationPriority(0.0f), - _transformChanged(true), - _rotationIsValid(false), - _positionInParentFrame(0.0f), - _distanceToParent(0.0f), - _fbxJoint(NULL), - _constraint(NULL) { +JointState::~JointState() { + if (_constraint) { + delete _constraint; + _constraint = NULL; + } } -JointState::JointState(const JointState& other) : _constraint(NULL) { +void JointState::copyState(const JointState& other) { _transformChanged = other._transformChanged; _transform = other._transform; _rotationIsValid = other._rotationIsValid; @@ -37,17 +34,54 @@ JointState::JointState(const JointState& other) : _constraint(NULL) { _positionInParentFrame = other._positionInParentFrame; _distanceToParent = other._distanceToParent; _animationPriority = other._animationPriority; - _fbxJoint = other._fbxJoint; + + _visibleTransform = other._visibleTransform; + _visibleRotation = extractRotation(_visibleTransform); + _visibleRotationInConstrainedFrame = other._visibleRotationInConstrainedFrame; // DO NOT copy _constraint + _name = other._name; + _isFree = other._isFree; + _boneRadius = other._boneRadius; + _parentIndex = other._parentIndex; + _defaultRotation = other._defaultRotation; + _inverseDefaultRotation = other._inverseDefaultRotation; + _translation = other._translation; + _rotationMin = other._rotationMin; + _rotationMax = other._rotationMax; + _preRotation = other._preRotation; + _postRotation = other._postRotation; + _preTransform = other._preTransform; + _postTransform = other._postTransform; + _inverseBindRotation = other._inverseBindRotation; +} +JointState::JointState(const FBXJoint& joint) { + _rotationInConstrainedFrame = joint.rotation; + _name = joint.name; + _isFree = joint.isFree; + _boneRadius = joint.boneRadius; + _parentIndex = joint.parentIndex; + _translation = joint.translation; + _defaultRotation = joint.rotation; + _inverseDefaultRotation = joint.inverseDefaultRotation; + _rotationMin = joint.rotationMin; + _rotationMax = joint.rotationMax; + _preRotation = joint.preRotation; + _postRotation = joint.postRotation; + _preTransform = joint.preTransform; + _postTransform = joint.postTransform; + _inverseBindRotation = joint.inverseBindRotation; } -JointState::~JointState() { - delete _constraint; - _constraint = NULL; +void JointState::buildConstraint() { if (_constraint) { delete _constraint; _constraint = NULL; } + if (glm::distance2(glm::vec3(-PI), _rotationMin) > EPSILON || + glm::distance2(glm::vec3(PI), _rotationMax) > EPSILON ) { + // this joint has rotation constraints + _constraint = AngularConstraint::newAngularConstraint(_rotationMin, _rotationMax); + } } glm::quat JointState::getRotation() const { @@ -59,48 +93,6 @@ glm::quat JointState::getRotation() const { return _rotation; } -void JointState::setFBXJoint(const FBXJoint* joint) { - assert(joint != NULL); - _rotationInConstrainedFrame = joint->rotation; - _transformChanged = true; - _rotationIsValid = false; - - // NOTE: JointState does not own the FBXJoint to which it points. - _fbxJoint = joint; - if (_constraint) { - delete _constraint; - _constraint = NULL; - } -} - -void JointState::buildConstraint() { - if (_constraint) { - delete _constraint; - _constraint = NULL; - } - if (glm::distance2(glm::vec3(-PI), _fbxJoint->rotationMin) > EPSILON || - glm::distance2(glm::vec3(PI), _fbxJoint->rotationMax) > EPSILON ) { - // this joint has rotation constraints - _constraint = AngularConstraint::newAngularConstraint(_fbxJoint->rotationMin, _fbxJoint->rotationMax); - } -} - -void JointState::copyState(const JointState& state) { - _animationPriority = state._animationPriority; - _transformChanged = state._transformChanged; - _transform = state._transform; - _rotationIsValid = state._rotationIsValid; - _rotation = state._rotation; - _rotationInConstrainedFrame = state._rotationInConstrainedFrame; - _positionInParentFrame = state._positionInParentFrame; - _distanceToParent = state._distanceToParent; - - _visibleTransform = state._visibleTransform; - _visibleRotation = extractRotation(_visibleTransform); - _visibleRotationInConstrainedFrame = state._visibleRotationInConstrainedFrame; - // DO NOT copy _fbxJoint or _constraint -} - void JointState::initTransform(const glm::mat4& parentTransform) { computeTransform(parentTransform); _positionInParentFrame = glm::inverse(extractRotation(parentTransform)) * (extractTranslation(_transform) - extractTranslation(parentTransform)); @@ -112,9 +104,9 @@ void JointState::computeTransform(const glm::mat4& parentTransform, bool parentT return; } - glm::quat rotationInParentFrame = _fbxJoint->preRotation * _rotationInConstrainedFrame * _fbxJoint->postRotation; - glm::mat4 transformInParentFrame = _fbxJoint->preTransform * glm::mat4_cast(rotationInParentFrame) * _fbxJoint->postTransform; - glm::mat4 newTransform = parentTransform * glm::translate(_fbxJoint->translation) * transformInParentFrame; + glm::quat rotationInParentFrame = _preRotation * _rotationInConstrainedFrame * _postRotation; + glm::mat4 transformInParentFrame = _preTransform * glm::mat4_cast(rotationInParentFrame) * _postTransform; + glm::mat4 newTransform = parentTransform * glm::translate(_translation) * transformInParentFrame; if (newTransform != _transform) { _transform = newTransform; @@ -124,37 +116,35 @@ void JointState::computeTransform(const glm::mat4& parentTransform, bool parentT } void JointState::computeVisibleTransform(const glm::mat4& parentTransform) { - glm::quat rotationInParentFrame = _fbxJoint->preRotation * _visibleRotationInConstrainedFrame * _fbxJoint->postRotation; - glm::mat4 transformInParentFrame = _fbxJoint->preTransform * glm::mat4_cast(rotationInParentFrame) * _fbxJoint->postTransform; - _visibleTransform = parentTransform * glm::translate(_fbxJoint->translation) * transformInParentFrame; + glm::quat rotationInParentFrame = _preRotation * _visibleRotationInConstrainedFrame * _postRotation; + glm::mat4 transformInParentFrame = _preTransform * glm::mat4_cast(rotationInParentFrame) * _postTransform; + _visibleTransform = parentTransform * glm::translate(_translation) * transformInParentFrame; _visibleRotation = extractRotation(_visibleTransform); } glm::quat JointState::getRotationInBindFrame() const { - return getRotation() * _fbxJoint->inverseBindRotation; + return getRotation() * _inverseBindRotation; } glm::quat JointState::getRotationInParentFrame() const { - return _fbxJoint->preRotation * _rotationInConstrainedFrame * _fbxJoint->postRotation; + return _preRotation * _rotationInConstrainedFrame * _postRotation; } glm::quat JointState::getVisibleRotationInParentFrame() const { - return _fbxJoint->preRotation * _visibleRotationInConstrainedFrame * _fbxJoint->postRotation; + return _preRotation * _visibleRotationInConstrainedFrame * _postRotation; } void JointState::restoreRotation(float fraction, float priority) { - assert(_fbxJoint != NULL); if (priority == _animationPriority || _animationPriority == 0.0f) { - setRotationInConstrainedFrameInternal(safeMix(_rotationInConstrainedFrame, _fbxJoint->rotation, fraction)); + setRotationInConstrainedFrameInternal(safeMix(_rotationInConstrainedFrame, _defaultRotation, fraction)); _animationPriority = 0.0f; } } void JointState::setRotationInBindFrame(const glm::quat& rotation, float priority, bool constrain) { // rotation is from bind- to model-frame - assert(_fbxJoint != NULL); if (priority >= _animationPriority) { - glm::quat targetRotation = _rotationInConstrainedFrame * glm::inverse(getRotation()) * rotation * glm::inverse(_fbxJoint->inverseBindRotation); + glm::quat targetRotation = _rotationInConstrainedFrame * glm::inverse(getRotation()) * rotation * glm::inverse(_inverseBindRotation); if (constrain && _constraint) { _constraint->softClamp(targetRotation, _rotationInConstrainedFrame, 0.5f); } @@ -175,7 +165,6 @@ void JointState::clearTransformTranslation() { void JointState::applyRotationDelta(const glm::quat& delta, bool constrain, float priority) { // NOTE: delta is in model-frame - assert(_fbxJoint != NULL); if (priority < _animationPriority || delta == glm::quat()) { return; } @@ -196,14 +185,13 @@ void JointState::applyRotationDelta(const glm::quat& delta, bool constrain, floa /// This helps keep an IK solution stable. void JointState::mixRotationDelta(const glm::quat& delta, float mixFactor, float priority) { // NOTE: delta is in model-frame - assert(_fbxJoint != NULL); if (priority < _animationPriority) { return; } _animationPriority = priority; glm::quat targetRotation = _rotationInConstrainedFrame * glm::inverse(getRotation()) * delta * getRotation(); if (mixFactor > 0.0f && mixFactor <= 1.0f) { - targetRotation = safeMix(targetRotation, _fbxJoint->rotation, mixFactor); + targetRotation = safeMix(targetRotation, _defaultRotation, mixFactor); } if (_constraint) { _constraint->softClamp(targetRotation, _rotationInConstrainedFrame, 0.5f); @@ -213,10 +201,8 @@ void JointState::mixRotationDelta(const glm::quat& delta, float mixFactor, float void JointState::mixVisibleRotationDelta(const glm::quat& delta, float mixFactor) { // NOTE: delta is in model-frame - assert(_fbxJoint != NULL); glm::quat targetRotation = _visibleRotationInConstrainedFrame * glm::inverse(_visibleRotation) * delta * _visibleRotation; if (mixFactor > 0.0f && mixFactor <= 1.0f) { - //targetRotation = safeMix(targetRotation, _fbxJoint->rotation, mixFactor); targetRotation = safeMix(targetRotation, _rotationInConstrainedFrame, mixFactor); } setVisibleRotationInConstrainedFrame(targetRotation); @@ -225,11 +211,11 @@ void JointState::mixVisibleRotationDelta(const glm::quat& delta, float mixFactor glm::quat JointState::computeParentRotation() const { // R = Rp * Rpre * r * Rpost // Rp = R * (Rpre * r * Rpost)^ - return getRotation() * glm::inverse(_fbxJoint->preRotation * _rotationInConstrainedFrame * _fbxJoint->postRotation); + return getRotation() * glm::inverse(_preRotation * _rotationInConstrainedFrame * _postRotation); } glm::quat JointState::computeVisibleParentRotation() const { - return _visibleRotation * glm::inverse(_fbxJoint->preRotation * _visibleRotationInConstrainedFrame * _fbxJoint->postRotation); + return _visibleRotation * glm::inverse(_preRotation * _visibleRotationInConstrainedFrame * _postRotation); } void JointState::setRotationInConstrainedFrame(glm::quat targetRotation, float priority, bool constrain, float mix) { @@ -249,18 +235,18 @@ void JointState::setRotationInConstrainedFrameInternal(const glm::quat& targetRo _rotationInConstrainedFrame = targetRotation; _transformChanged = true; // R' = Rp * Rpre * r' * Rpost - _rotation = parentRotation * _fbxJoint->preRotation * _rotationInConstrainedFrame * _fbxJoint->postRotation; + _rotation = parentRotation * _preRotation * _rotationInConstrainedFrame * _postRotation; } } void JointState::setVisibleRotationInConstrainedFrame(const glm::quat& targetRotation) { glm::quat parentRotation = computeVisibleParentRotation(); _visibleRotationInConstrainedFrame = targetRotation; - _visibleRotation = parentRotation * _fbxJoint->preRotation * _visibleRotationInConstrainedFrame * _fbxJoint->postRotation; + _visibleRotation = parentRotation * _preRotation * _visibleRotationInConstrainedFrame * _postRotation; } bool JointState::rotationIsDefault(const glm::quat& rotation, float tolerance) const { - glm::quat defaultRotation = _fbxJoint->rotation; + glm::quat defaultRotation = _defaultRotation; return glm::abs(rotation.x - defaultRotation.x) < tolerance && glm::abs(rotation.y - defaultRotation.y) < tolerance && glm::abs(rotation.z - defaultRotation.z) < tolerance && @@ -268,13 +254,12 @@ bool JointState::rotationIsDefault(const glm::quat& rotation, float tolerance) c } glm::quat JointState::getDefaultRotationInParentFrame() const { - // NOTE: the result is constant and could be cached in the FBXJoint - return _fbxJoint->preRotation * _fbxJoint->rotation * _fbxJoint->postRotation; + // NOTE: the result is constant and could be cached + return _preRotation * _defaultRotation * _postRotation; } const glm::vec3& JointState::getDefaultTranslationInConstrainedFrame() const { - assert(_fbxJoint != NULL); - return _fbxJoint->translation; + return _translation; } void JointState::slaveVisibleTransform() { diff --git a/libraries/animation/src/JointState.h b/libraries/animation/src/JointState.h index 93bf83d2c4..694700338d 100644 --- a/libraries/animation/src/JointState.h +++ b/libraries/animation/src/JointState.h @@ -26,16 +26,13 @@ class AngularConstraint; class JointState { public: - JointState(); - JointState(const JointState& other); + JointState() {} + JointState(const JointState& other) : _constraint(NULL) { copyState(other); } + JointState(const FBXJoint& joint); ~JointState(); - - void setFBXJoint(const FBXJoint* joint); - const FBXJoint& getFBXJoint() const { return *_fbxJoint; } - - void buildConstraint(); void copyState(const JointState& state); - + void buildConstraint(); + void initTransform(const glm::mat4& parentTransform); // if synchronousRotationCompute is true, then _transform is still computed synchronously, // but _rotation will be asynchronously extracted @@ -61,7 +58,7 @@ public: const glm::vec3& getPositionInParentFrame() const { return _positionInParentFrame; } float getDistanceToParent() const { return _distanceToParent; } - int getParentIndex() const { return _fbxJoint->parentIndex; } + int getParentIndex() const { return _parentIndex; } /// \param delta is in the model-frame void applyRotationDelta(const glm::quat& delta, bool constrain = true, float priority = 1.0f); @@ -99,35 +96,61 @@ public: void slaveVisibleTransform(); - float _animationPriority; // the priority of the animation affecting this joint - - /// \return parent model-frame rotation + /// \return parent model-frame rotation // (used to keep _rotation consistent when modifying _rotationInWorldFrame directly) glm::quat computeParentRotation() const; glm::quat computeVisibleParentRotation() const; void setTransform(const glm::mat4& transform) { _transform = transform; } void setVisibleTransform(const glm::mat4& transform) { _visibleTransform = transform; } + + const glm::vec3& getTranslation() const { return _translation; } + const glm::mat4& getPreTransform() const { return _preTransform; } + const glm::mat4& getPostTransform() const { return _postTransform; } + const glm::quat& getPreRotation() const { return _preRotation; } + const glm::quat& getPostRotation() const { return _postRotation; } + const glm::quat& getDefaultRotation() const { return _defaultRotation; } + const glm::quat& getInverseDefaultRotation() const { return _inverseDefaultRotation; } + const QString& getName() const { return _name; } + float getBoneRadius() const { return _boneRadius; } + bool getIsFree() const { return _isFree; } + float getAnimationPriority() const { return _animationPriority; } + void setAnimationPriority(float priority) { _animationPriority = priority; } private: void setRotationInConstrainedFrameInternal(const glm::quat& targetRotation); /// debug helper function void loadBindRotation(); - bool _transformChanged; + bool _transformChanged {true}; + bool _rotationIsValid {false}; + glm::vec3 _positionInParentFrame {0.0f}; // only changes when the Model is scaled + float _animationPriority {0.0f}; // the priority of the animation affecting this joint + float _distanceToParent {0.0f}; + AngularConstraint* _constraint{nullptr}; // JointState owns its AngularConstraint + glm::mat4 _transform; // joint- to model-frame - bool _rotationIsValid; glm::quat _rotation; // joint- to model-frame glm::quat _rotationInConstrainedFrame; // rotation in frame where angular constraints would be applied - glm::vec3 _positionInParentFrame; // only changes when the Model is scaled - float _distanceToParent; glm::mat4 _visibleTransform; glm::quat _visibleRotation; glm::quat _visibleRotationInConstrainedFrame; - const FBXJoint* _fbxJoint; // JointState does NOT own its FBXJoint - AngularConstraint* _constraint; // JointState owns its AngularConstraint + glm::quat _defaultRotation; // Not necessarilly bind rotation. See FBXJoint transform/bindTransform + glm::quat _inverseDefaultRotation; + glm::vec3 _translation; + QString _name; + int _parentIndex; + bool _isFree; + float _boneRadius; + glm::vec3 _rotationMin; + glm::vec3 _rotationMax; + glm::quat _preRotation; + glm::quat _postRotation; + glm::mat4 _preTransform; + glm::mat4 _postTransform; + glm::quat _inverseBindRotation; }; #endif // hifi_JointState_h diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 2d1856e41b..b8bb9db6ea 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -224,7 +224,7 @@ float Rig::initJointStates(QVector states, glm::mat4 parentTransform // Should we be using .fst mapping instead/also? int Rig::indexOfJoint(const QString& jointName) { for (int i = 0; i < _jointStates.count(); i++) { - if (_jointStates[i].getFBXJoint().name == jointName) { + if (_jointStates[i].getName() == jointName) { return i; } } @@ -237,8 +237,7 @@ void Rig::initJointTransforms(glm::mat4 parentTransform) { int numStates = _jointStates.size(); for (int i = 0; i < numStates; ++i) { JointState& state = _jointStates[i]; - const FBXJoint& joint = state.getFBXJoint(); - int parentIndex = joint.parentIndex; + int parentIndex = state.getParentIndex(); if (parentIndex == -1) { state.initTransform(parentTransform); } else { @@ -302,20 +301,20 @@ void Rig::clearJointStates() { void Rig::clearJointAnimationPriority(int index) { if (index != -1 && index < _jointStates.size()) { - _jointStates[index]._animationPriority = 0.0f; + _jointStates[index].setAnimationPriority(0.0f); } } float Rig::getJointAnimatinoPriority(int index) { if (index != -1 && index < _jointStates.size()) { - return _jointStates[index]._animationPriority; + return _jointStates[index].getAnimationPriority(); } return 0.0f; } void Rig::setJointAnimatinoPriority(int index, float newPriority) { if (index != -1 && index < _jointStates.size()) { - _jointStates[index]._animationPriority = newPriority; + _jointStates[index].setAnimationPriority(newPriority); } } @@ -532,8 +531,7 @@ bool Rig::setJointPosition(int jointIndex, const glm::vec3& position, const glm: for (int j = 1; freeLineage.at(j - 1) != lastFreeIndex; j++) { int index = freeLineage.at(j); JointState& state = _jointStates[index]; - const FBXJoint& joint = state.getFBXJoint(); - if (!(joint.isFree || allIntermediatesFree)) { + if (!(state.getIsFree() || allIntermediatesFree)) { continue; } glm::vec3 jointPosition = extractTranslation(state.getTransform()); @@ -602,8 +600,7 @@ void Rig::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::q { int index = freeLineage.last(); const JointState& state = _jointStates.at(index); - const FBXJoint& joint = state.getFBXJoint(); - int parentIndex = joint.parentIndex; + int parentIndex = state.getParentIndex(); if (parentIndex == -1) { topParentTransform = parentTransform; } else { @@ -628,8 +625,7 @@ void Rig::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::q for (int j = 1; j < numFree; j++) { int nextIndex = freeLineage.at(j); JointState& nextState = _jointStates[nextIndex]; - FBXJoint nextJoint = nextState.getFBXJoint(); - if (! nextJoint.isFree) { + if (! nextState.getIsFree()) { continue; } @@ -803,20 +799,20 @@ void Rig::updateLeanJoint(int index, float leanSideways, float leanForward, floa glm::angleAxis(- RADIANS_PER_DEGREE * leanSideways, inverse * zAxis) * glm::angleAxis(- RADIANS_PER_DEGREE * leanForward, inverse * xAxis) * glm::angleAxis(RADIANS_PER_DEGREE * torsoTwist, inverse * yAxis) * - getJointState(index).getFBXJoint().rotation, DEFAULT_PRIORITY); + getJointState(index).getDefaultRotation(), DEFAULT_PRIORITY); } } void Rig::updateNeckJoint(int index, const glm::quat& localHeadOrientation, float leanSideways, float leanForward, float torsoTwist) { if (index >= 0 && _jointStates[index].getParentIndex() >= 0) { - auto& parentState = _jointStates[_jointStates[index].getParentIndex()]; - auto joint = _jointStates[index].getFBXJoint(); + auto& state = _jointStates[index]; + auto& parentState = _jointStates[state.getParentIndex()]; // get the rotation axes in joint space and use them to adjust the rotation glm::mat3 axes = glm::mat3_cast(glm::quat()); glm::mat3 inverse = glm::mat3(glm::inverse(parentState.getTransform() * glm::translate(getJointDefaultTranslationInConstrainedFrame(index)) * - joint.preTransform * glm::mat4_cast(joint.preRotation))); + state.getPreTransform() * glm::mat4_cast(state.getPreRotation()))); glm::vec3 pitchYawRoll = safeEulerAngles(localHeadOrientation); glm::vec3 lean = glm::radians(glm::vec3(leanForward, torsoTwist, leanSideways)); pitchYawRoll -= lean; @@ -824,19 +820,19 @@ void Rig::updateNeckJoint(int index, const glm::quat& localHeadOrientation, floa glm::angleAxis(-pitchYawRoll.z, glm::normalize(inverse * axes[2])) * glm::angleAxis(pitchYawRoll.y, glm::normalize(inverse * axes[1])) * glm::angleAxis(-pitchYawRoll.x, glm::normalize(inverse * axes[0])) * - joint.rotation, DEFAULT_PRIORITY); + state.getDefaultRotation(), DEFAULT_PRIORITY); } } void Rig::updateEyeJoint(int index, const glm::quat& worldHeadOrientation, const glm::vec3& lookAt, const glm::vec3& saccade) { if (index >= 0 && _jointStates[index].getParentIndex() >= 0) { - auto& parentState = _jointStates[_jointStates[index].getParentIndex()]; - auto joint = _jointStates[index].getFBXJoint(); + auto& state = _jointStates[index]; + auto& parentState = _jointStates[state.getParentIndex()]; // NOTE: at the moment we do the math in the world-frame, hence the inverse transform is more complex than usual. glm::mat4 inverse = glm::inverse(parentState.getTransform() * glm::translate(getJointDefaultTranslationInConstrainedFrame(index)) * - joint.preTransform * glm::mat4_cast(joint.preRotation * joint.rotation)); + state.getPreTransform() * glm::mat4_cast(state.getPreRotation() * state.getDefaultRotation())); glm::vec3 front = glm::vec3(inverse * glm::vec4(worldHeadOrientation * IDENTITY_FRONT, 0.0f)); glm::vec3 lookAtDelta = lookAt; glm::vec3 lookAt = glm::vec3(inverse * glm::vec4(lookAtDelta + glm::length(lookAtDelta) * saccade, 1.0f)); @@ -844,6 +840,6 @@ void Rig::updateEyeJoint(int index, const glm::quat& worldHeadOrientation, const const float MAX_ANGLE = 30.0f * RADIANS_PER_DEGREE; float angle = glm::clamp(glm::angle(between), -MAX_ANGLE, MAX_ANGLE); glm::quat rot = glm::angleAxis(angle, glm::axis(between)); - setJointRotationInConstrainedFrame(index, rot * joint.rotation, DEFAULT_PRIORITY); + setJointRotationInConstrainedFrame(index, rot * state.getDefaultRotation(), DEFAULT_PRIORITY); } } diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index d3710c6ccc..7e74b3d26c 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -60,7 +60,7 @@ public: virtual OctreeEditPacketSender* createPacketSender() { return new EntityEditPacketSender(); } void setEntityTree(EntityTree* modelTree); - EntityTree* getEntityTree(EntityTree*) { return _entityTree; } + EntityTree* getEntityTree() { return _entityTree; } public slots: diff --git a/libraries/render-utils/src/AbstractViewStateInterface.h b/libraries/render-utils/src/AbstractViewStateInterface.h index 27b4fa6ede..53214b3d5b 100644 --- a/libraries/render-utils/src/AbstractViewStateInterface.h +++ b/libraries/render-utils/src/AbstractViewStateInterface.h @@ -47,9 +47,6 @@ public: /// gets the shadow view frustum for rendering the view state virtual ViewFrustum* getShadowViewFrustum() = 0; - virtual bool getShadowsEnabled() = 0; - virtual bool getCascadeShadowsEnabled() = 0; - virtual QThread* getMainThread() = 0; virtual bool shouldRenderMesh(float largestDimension, float distanceToCamera) = 0; diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index 08ab6e0cf6..4941818bf4 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -277,13 +277,19 @@ void DeferredLightingEffect::render(RenderArgs* args) { auto& program = _directionalLight; const LightLocations* locations = &_directionalLightLocations; - bool shadowsEnabled = _viewState->getShadowsEnabled(); + + // FIXME: Note: we've removed the menu items to enable shadows, so this will always be false for now. + // When we add back shadow support, this old approach may likely be removed and completely replaced + // but I've left it in for now. + bool shadowsEnabled = false; + bool cascadeShadowsEnabled = false; + if (shadowsEnabled) { batch.setResourceTexture(4, framebufferCache->getShadowFramebuffer()->getDepthStencilBuffer()); program = _directionalLightShadowMap; locations = &_directionalLightShadowMapLocations; - if (_viewState->getCascadeShadowsEnabled()) { + if (cascadeShadowsEnabled) { program = _directionalLightCascadedShadowMap; locations = &_directionalLightCascadedShadowMapLocations; if (useSkyboxCubemap) { diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 04f72debd7..3080d965a1 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -227,9 +227,7 @@ QVector Model::createJointStates(const FBXGeometry& geometry) { for (int i = 0; i < geometry.joints.size(); ++i) { const FBXJoint& joint = geometry.joints[i]; // store a pointer to the FBXJoint in the JointState - JointState state; - state.setFBXJoint(&joint); - + JointState state(joint); jointStates.append(state); } return jointStates; diff --git a/tests/animation/src/RigTests.cpp b/tests/animation/src/RigTests.cpp index 8dbba30031..b0e0a53ee5 100644 --- a/tests/animation/src/RigTests.cpp +++ b/tests/animation/src/RigTests.cpp @@ -47,39 +47,9 @@ #include "AvatarRig.h" // We might later test Rig vs AvatarRig separately, but for now, we're concentrating on the main use case. #include "RigTests.h" -QTEST_MAIN(RigTests) - -void RigTests::initTestCase() { -//#define FROM_FILE "/Users/howardstearns/howardHiFi/Zack.fbx" -#ifdef FROM_FILE - QFile file(FROM_FILE); - QCOMPARE(file.open(QIODevice::ReadOnly), true); - FBXGeometry geometry = readFBX(file.readAll(), QVariantHash()); -#else - QUrl fbxUrl("https://s3.amazonaws.com/hifi-public/models/skeletons/Zack/Zack.fbx"); - QNetworkReply* reply = OBJReader().request(fbxUrl, false); // Just a convenience hack for synchronoud http request - auto fbxHttpCode = !reply->isFinished() ? -1 : reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); - QCOMPARE(fbxHttpCode, 200); - FBXGeometry geometry = readFBX(reply->readAll(), QVariantHash()); -#endif - - QVector jointStates; - for (int i = 0; i < geometry.joints.size(); ++i) { - // Note that if the geometry is stack allocated and goes away, so will the joints. Hence the heap copy here. - FBXJoint* joint = new FBXJoint(geometry.joints[i]); - JointState state; - state.setFBXJoint(joint); - jointStates.append(state); - } - - _rig = std::make_shared(); - _rig->initJointStates(jointStates, glm::mat4()); - std::cout << "Rig is ready " << geometry.joints.count() << " joints " << std::endl; - } - static void reportJoint(int index, JointState joint) { // Handy for debugging std::cout << "\n"; - std::cout << index << " " << joint.getFBXJoint().name.toUtf8().data() << "\n"; + std::cout << index << " " << joint.getName().toUtf8().data() << "\n"; std::cout << " pos:" << joint.getPosition() << "/" << joint.getPositionInParentFrame() << " from " << joint.getParentIndex() << "\n"; std::cout << " rot:" << safeEulerAngles(joint.getRotation()) << "/" << safeEulerAngles(joint.getRotationInParentFrame()) << "/" << safeEulerAngles(joint.getRotationInBindFrame()) << "\n"; std::cout << "\n"; @@ -101,7 +71,34 @@ static void reportSome(RigPointer rig) { } } +QTEST_MAIN(RigTests) + +void RigTests::initTestCase() { +//#define FROM_FILE "/Users/howardstearns/howardHiFi/Zack.fbx" +#ifdef FROM_FILE + QFile file(FROM_FILE); + QCOMPARE(file.open(QIODevice::ReadOnly), true); + FBXGeometry geometry = readFBX(file.readAll(), QVariantHash()); +#else + QUrl fbxUrl("https://s3.amazonaws.com/hifi-public/models/skeletons/Zack/Zack.fbx"); + QNetworkReply* reply = OBJReader().request(fbxUrl, false); // Just a convenience hack for synchronoud http request + auto fbxHttpCode = !reply->isFinished() ? -1 : reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + QCOMPARE(fbxHttpCode, 200); + FBXGeometry geometry = readFBX(reply->readAll(), QVariantHash()); +#endif + + QVector jointStates; + for (int i = 0; i < geometry.joints.size(); ++i) { + JointState state(geometry.joints[i]); + jointStates.append(state); + } + + _rig = std::make_shared(); + _rig->initJointStates(jointStates, glm::mat4(), 0, 41, 40, 39, 17, 16, 15); // FIXME? get by name? do we really want to exclude the shoulder blades? + std::cout << "Rig is ready " << geometry.joints.count() << " joints " << std::endl; + reportAll(_rig); + } + void RigTests::initialPoseArmsDown() { - //reportAll(_rig); reportSome(_rig); }