diff --git a/BUILD_OSX.md b/BUILD_OSX.md index 60fbb7cf92..9d1357d672 100644 --- a/BUILD_OSX.md +++ b/BUILD_OSX.md @@ -1,7 +1,7 @@ Please read the [general build guide](BUILD.md) for information on dependencies required for all platforms. Only OS X specific instructions are found in this file. ###Homebrew -[Homebrew](http://brew.sh/) is an excellent package manager for OS X. It makes install of all hifi dependencies very simple. +[Homebrew](http://brew.sh/) is an excellent package manager for OS X. It makes install of all High Fidelity dependencies very simple. brew tap highfidelity/homebrew-formulas brew install cmake openssl 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/html/entityProperties.html b/examples/html/entityProperties.html index 8a15147f16..a2358b40d5 100644 --- a/examples/html/entityProperties.html +++ b/examples/html/entityProperties.html @@ -362,6 +362,9 @@ var elVoxelVolumeSizeY = document.getElementById("property-voxel-volume-size-y"); var elVoxelVolumeSizeZ = document.getElementById("property-voxel-volume-size-z"); var elVoxelSurfaceStyle = document.getElementById("property-voxel-surface-style"); + var elXTextureURL = document.getElementById("property-x-texture-url"); + var elYTextureURL = document.getElementById("property-y-texture-url"); + var elZTextureURL = document.getElementById("property-z-texture-url"); var elHyperlinkHref = document.getElementById("property-hyperlink-href"); var elHyperlinkDescription = document.getElementById("property-hyperlink-description"); @@ -614,6 +617,9 @@ elVoxelVolumeSizeY.value = properties.voxelVolumeSize.y.toFixed(2); elVoxelVolumeSizeZ.value = properties.voxelVolumeSize.z.toFixed(2); elVoxelSurfaceStyle.value = properties.voxelSurfaceStyle; + elXTextureURL.value = properties.xTextureURL; + elYTextureURL.value = properties.yTextureURL; + elZTextureURL.value = properties.zTextureURL; } if (selected) { @@ -867,6 +873,9 @@ elVoxelVolumeSizeY.addEventListener('change', voxelVolumeSizeChangeFunction); elVoxelVolumeSizeZ.addEventListener('change', voxelVolumeSizeChangeFunction); elVoxelSurfaceStyle.addEventListener('change', createEmitTextPropertyUpdateFunction('voxelSurfaceStyle')); + elXTextureURL.addEventListener('change', createEmitTextPropertyUpdateFunction('xTextureURL')); + elYTextureURL.addEventListener('change', createEmitTextPropertyUpdateFunction('yTextureURL')); + elZTextureURL.addEventListener('change', createEmitTextPropertyUpdateFunction('zTextureURL')); elMoveSelectionToGrid.addEventListener("click", function() { EventBridge.emitWebEvent(JSON.stringify({ @@ -1063,7 +1072,22 @@ - + + + +
X-axis Texture URL
+
+ +
+ +
Y-axis Texture URL
+
+ +
+ +
Z-axis Texture URL
+
+
diff --git a/examples/libraries/entityPropertyDialogBox.js b/examples/libraries/entityPropertyDialogBox.js index 1b1a6a9c12..b386953c7c 100644 --- a/examples/libraries/entityPropertyDialogBox.js +++ b/examples/libraries/entityPropertyDialogBox.js @@ -98,8 +98,8 @@ EntityPropertyDialogBox = (function () { index++; } - if (properties.type == "PolyVox") { - array.push({ label: "Voxel Space Size:", type: "header" }); + if (properties.type == "PolyVox") { + array.push({ label: "Voxel Space Size:", type: "header" }); index++; array.push({ label: "X:", value: properties.voxelVolumeSize.x.toFixed(decimals) }); @@ -109,9 +109,16 @@ EntityPropertyDialogBox = (function () { array.push({ label: "Z:", value: properties.voxelVolumeSize.z.toFixed(decimals) }); index++; - array.push({ label: "Surface Extractor", value: properties.voxelSurfaceStyle }); - index++; - } + array.push({ label: "Surface Extractor", value: properties.voxelSurfaceStyle }); + index++; + + array.push({ label: "X-axis Texture URL:", value: properties.xTextureURL }); + index++; + array.push({ label: "Y-axis Texture URL:", value: properties.yTextureURL }); + index++; + array.push({ label: "Z-axis Texture URL:", value: properties.zTextureURL }); + index++; + } array.push({ label: "Position:", type: "header" }); index++; @@ -348,14 +355,17 @@ EntityPropertyDialogBox = (function () { properties.backgroundColor.blue = array[index++].value; } - if (properties.type == "PolyVox") { + if (properties.type == "PolyVox") { properties.shapeType = array[index++].value; - index++; // skip header - properties.voxelVolumeSize.x = array[index++].value; - properties.voxelVolumeSize.y = array[index++].value; - properties.voxelVolumeSize.z = array[index++].value; - properties.voxelSurfaceStyle = array[index++].value; + index++; // skip header + properties.voxelVolumeSize.x = array[index++].value; + properties.voxelVolumeSize.y = array[index++].value; + properties.voxelVolumeSize.z = array[index++].value; + properties.voxelSurfaceStyle = array[index++].value; + properties.xTextureURL = array[index++].value; + properties.yTextureURL = array[index++].value; + properties.zTextureURL = array[index++].value; } index++; // skip header 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/examples/voxels.js b/examples/voxels.js index e110f15260..9627b40701 100644 --- a/examples/voxels.js +++ b/examples/voxels.js @@ -44,7 +44,7 @@ function mousePressEvent(event) { } } - // if the PolyVox entity is empty, we can't pick against its voxel. try picking against its + // if the PolyVox entity is empty, we can't pick against its "on" voxels. try picking against its // bounding box, instead. intersection = Entities.findRayIntersection(pickRay, false); // bounding box picking if (intersection.intersects) { diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index def503e02a..5a5a629fb8 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/AnimationCache.cpp b/libraries/animation/src/AnimationCache.cpp index d1c4408178..634e0589b7 100644 --- a/libraries/animation/src/AnimationCache.cpp +++ b/libraries/animation/src/AnimationCache.cpp @@ -91,6 +91,10 @@ QVector Animation::getFrames() const { return _geometry.animationFrames; } +const QVector& Animation::getFramesReference() const { + return _geometry.animationFrames; +} + void Animation::setGeometry(const FBXGeometry& geometry) { _geometry = geometry; finishedLoading(true); diff --git a/libraries/animation/src/AnimationCache.h b/libraries/animation/src/AnimationCache.h index 840d7a0355..6a0a77f659 100644 --- a/libraries/animation/src/AnimationCache.h +++ b/libraries/animation/src/AnimationCache.h @@ -57,6 +57,8 @@ public: Q_INVOKABLE QStringList getJointNames() const; Q_INVOKABLE QVector getFrames() const; + + const QVector& getFramesReference() const; protected: 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 3682837719..440a1c18b3 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) { @@ -248,17 +234,17 @@ 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 && @@ -266,13 +252,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-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 1576007263..6d2ff30d4b 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -227,24 +227,30 @@ void RenderableModelEntityItem::render(RenderArgs* args) { if (hasModel()) { if (_model) { - if (QUrl(getModelURL()) != _model->getURL()) { + if (getModelURL() != _model->getURLAsString()) { qDebug() << "Updating model URL: " << getModelURL(); _model->setURL(getModelURL()); } + render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene(); + // check to see if when we added our models to the scene they were ready, if they were not ready, then // fix them up in the scene - render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene(); - render::PendingChanges pendingChanges; if (_model->needsFixupInScene()) { + render::PendingChanges pendingChanges; + _model->removeFromScene(scene, pendingChanges); render::Item::Status::Getters statusGetters; makeEntityItemStatusGetters(this, statusGetters); _model->addToScene(scene, pendingChanges, statusGetters); - } - scene->enqueuePendingChanges(pendingChanges); + scene->enqueuePendingChanges(pendingChanges); + } + + // FIXME: this seems like it could be optimized if we tracked our last known visible state in + // the renderable item. As it stands now the model checks it's visible/invisible state + // so most of the time we don't do anything in this function. _model->setVisibleInScene(getVisible(), scene); } @@ -269,7 +275,7 @@ void RenderableModelEntityItem::render(RenderArgs* args) { } if (jointsMapped()) { - QVector frameData = getAnimationFrame(); + auto frameData = getAnimationFrame(); for (int i = 0; i < frameData.size(); i++) { _model->setJointState(i, true, frameData[i]); } diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index 681702bb07..23093b9f3f 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -37,7 +38,11 @@ #include "model/Geometry.h" #include "gpu/Context.h" #include "EntityTreeRenderer.h" -#include "RenderablePolyVoxEntityItem.h" +#include "polyvox_vert.h" +#include "polyvox_frag.h" +#include "RenderablePolyVoxEntityItem.h" + +gpu::PipelinePointer RenderablePolyVoxEntityItem::_pipeline = nullptr; EntityItemPointer RenderablePolyVoxEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { return std::make_shared(entityID, properties); @@ -45,8 +50,10 @@ EntityItemPointer RenderablePolyVoxEntityItem::factory(const EntityItemID& entit RenderablePolyVoxEntityItem::RenderablePolyVoxEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) : - PolyVoxEntityItem(entityItemID, properties) { - + PolyVoxEntityItem(entityItemID, properties), + _xTexture(nullptr), + _yTexture(nullptr), + _zTexture(nullptr) { model::Mesh* mesh = new model::Mesh(); model::MeshPointer meshPtr(mesh); _modelGeometry.setMesh(meshPtr); @@ -275,7 +282,6 @@ void RenderablePolyVoxEntityItem::setAll(uint8_t toValue) { for (int z = 0; z < _voxelVolumeSize.z; z++) { for (int y = 0; y < _voxelVolumeSize.y; y++) { for (int x = 0; x < _voxelVolumeSize.x; x++) { - updateOnCount(x, y, z, toValue); setVoxelInternal(x, y, z, toValue); } } @@ -325,106 +331,6 @@ void RenderablePolyVoxEntityItem::setSphere(glm::vec3 centerWorldCoords, float r setSphereInVolume(glm::vec3(centerVoxelCoords), radiusVoxelCoords, toValue); } -void RenderablePolyVoxEntityItem::getModel() { - // A mesh object to hold the result of surface extraction - PolyVox::SurfaceMesh polyVoxMesh; - - switch (_voxelSurfaceStyle) { - case PolyVoxEntityItem::SURFACE_MARCHING_CUBES: { - PolyVox::MarchingCubesSurfaceExtractor> surfaceExtractor - (_volData, _volData->getEnclosingRegion(), &polyVoxMesh); - surfaceExtractor.execute(); - break; - } - case PolyVoxEntityItem::SURFACE_EDGED_CUBIC: - case PolyVoxEntityItem::SURFACE_CUBIC: { - PolyVox::CubicSurfaceExtractorWithNormals> surfaceExtractor - (_volData, _volData->getEnclosingRegion(), &polyVoxMesh); - surfaceExtractor.execute(); - break; - } - } - - // convert PolyVox mesh to a Sam mesh - auto mesh = _modelGeometry.getMesh(); - - const std::vector& vecIndices = polyVoxMesh.getIndices(); - auto indexBuffer = std::make_shared(vecIndices.size() * sizeof(uint32_t), - (gpu::Byte*)vecIndices.data()); - auto indexBufferPtr = gpu::BufferPointer(indexBuffer); - auto indexBufferView = new gpu::BufferView(indexBufferPtr, gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::RAW)); - mesh->setIndexBuffer(*indexBufferView); - - - const std::vector& vecVertices = polyVoxMesh.getVertices(); - auto vertexBuffer = std::make_shared(vecVertices.size() * sizeof(PolyVox::PositionMaterialNormal), - (gpu::Byte*)vecVertices.data()); - auto vertexBufferPtr = gpu::BufferPointer(vertexBuffer); - auto vertexBufferView = new gpu::BufferView(vertexBufferPtr, - 0, - vertexBufferPtr->getSize() - sizeof(float) * 3, - sizeof(PolyVox::PositionMaterialNormal), - gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RAW)); - mesh->setVertexBuffer(*vertexBufferView); - mesh->addAttribute(gpu::Stream::NORMAL, - gpu::BufferView(vertexBufferPtr, - sizeof(float) * 3, - vertexBufferPtr->getSize() - sizeof(float) * 3, - sizeof(PolyVox::PositionMaterialNormal), - gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RAW))); - - - - // auto normalAttrib = mesh->getAttributeBuffer(gpu::Stream::NORMAL); - // for (auto normal = normalAttrib.begin(); normal != normalAttrib.end(); normal++) { - // (*normal) = -(*normal); - // } - - - // mesh->addAttribute(gpu::Stream::TEXCOORD, - // gpu::BufferView(vertexBufferPtr, - // sizeof(float) * 2, - // vertexBufferPtr->getSize() - sizeof(float) * 2, - // sizeof(PolyVox::PositionMaterialNormal), - // gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::RAW))); - - - - #ifdef WANT_DEBUG - qDebug() << "---- vecIndices.size() =" << vecIndices.size(); - qDebug() << "---- vecVertices.size() =" << vecVertices.size(); - #endif - - _needsModelReload = false; -} - -void RenderablePolyVoxEntityItem::render(RenderArgs* args) { - PerformanceTimer perfTimer("RenderablePolyVoxEntityItem::render"); - assert(getType() == EntityTypes::PolyVox); - - if (_needsModelReload) { - getModel(); - } - - Transform transform(voxelToWorldMatrix()); - - auto mesh = _modelGeometry.getMesh(); - Q_ASSERT(args->_batch); - gpu::Batch& batch = *args->_batch; - DependencyManager::get()->bindSimpleProgram(batch); - batch.setModelTransform(transform); - batch.setInputFormat(mesh->getVertexFormat()); - batch.setInputBuffer(gpu::Stream::POSITION, mesh->getVertexBuffer()); - batch.setInputBuffer(gpu::Stream::NORMAL, - mesh->getVertexBuffer()._buffer, - sizeof(float) * 3, - mesh->getVertexBuffer()._stride); - batch.setIndexBuffer(gpu::UINT32, mesh->getIndexBuffer()._buffer, 0); - batch.drawIndexed(gpu::TRIANGLES, mesh->getNumIndices(), 0); - - RenderableDebugableEntityItem::render(this, args); -} - class RaycastFunctor { public: @@ -709,3 +615,190 @@ void RenderablePolyVoxEntityItem::computeShapeInfo(ShapeInfo& info) { info.setParams(type, collisionModelDimensions, QString(b64)); info.setConvexHulls(_points); } + +void RenderablePolyVoxEntityItem::setXTextureURL(QString xTextureURL) { + PolyVoxEntityItem::setXTextureURL(xTextureURL); +} + +void RenderablePolyVoxEntityItem::setYTextureURL(QString yTextureURL) { + PolyVoxEntityItem::setYTextureURL(yTextureURL); +} + +void RenderablePolyVoxEntityItem::setZTextureURL(QString zTextureURL) { + PolyVoxEntityItem::setZTextureURL(zTextureURL); +} + +void RenderablePolyVoxEntityItem::getModel() { + // A mesh object to hold the result of surface extraction + PolyVox::SurfaceMesh polyVoxMesh; + + switch (_voxelSurfaceStyle) { + case PolyVoxEntityItem::SURFACE_MARCHING_CUBES: { + PolyVox::MarchingCubesSurfaceExtractor> surfaceExtractor + (_volData, _volData->getEnclosingRegion(), &polyVoxMesh); + surfaceExtractor.execute(); + break; + } + case PolyVoxEntityItem::SURFACE_EDGED_CUBIC: + case PolyVoxEntityItem::SURFACE_CUBIC: { + PolyVox::CubicSurfaceExtractorWithNormals> surfaceExtractor + (_volData, _volData->getEnclosingRegion(), &polyVoxMesh); + surfaceExtractor.execute(); + break; + } + } + + // convert PolyVox mesh to a Sam mesh + auto mesh = _modelGeometry.getMesh(); + + const std::vector& vecIndices = polyVoxMesh.getIndices(); + auto indexBuffer = std::make_shared(vecIndices.size() * sizeof(uint32_t), + (gpu::Byte*)vecIndices.data()); + auto indexBufferPtr = gpu::BufferPointer(indexBuffer); + auto indexBufferView = new gpu::BufferView(indexBufferPtr, gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::RAW)); + mesh->setIndexBuffer(*indexBufferView); + + + const std::vector& vecVertices = polyVoxMesh.getVertices(); + auto vertexBuffer = std::make_shared(vecVertices.size() * sizeof(PolyVox::PositionMaterialNormal), + (gpu::Byte*)vecVertices.data()); + auto vertexBufferPtr = gpu::BufferPointer(vertexBuffer); + auto vertexBufferView = new gpu::BufferView(vertexBufferPtr, + 0, + vertexBufferPtr->getSize() - sizeof(float) * 3, + sizeof(PolyVox::PositionMaterialNormal), + gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RAW)); + mesh->setVertexBuffer(*vertexBufferView); + mesh->addAttribute(gpu::Stream::NORMAL, + gpu::BufferView(vertexBufferPtr, + sizeof(float) * 3, + vertexBufferPtr->getSize() - sizeof(float) * 3, + sizeof(PolyVox::PositionMaterialNormal), + gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RAW))); + + #ifdef WANT_DEBUG + qDebug() << "---- vecIndices.size() =" << vecIndices.size(); + qDebug() << "---- vecVertices.size() =" << vecVertices.size(); + #endif + + _needsModelReload = false; +} + +void RenderablePolyVoxEntityItem::render(RenderArgs* args) { + PerformanceTimer perfTimer("RenderablePolyVoxEntityItem::render"); + assert(getType() == EntityTypes::PolyVox); + Q_ASSERT(args->_batch); + + if (!_pipeline) { + gpu::ShaderPointer vertexShader = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(polyvox_vert))); + gpu::ShaderPointer pixelShader = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(polyvox_frag))); + + gpu::Shader::BindingSet slotBindings; + slotBindings.insert(gpu::Shader::Binding(std::string("materialBuffer"), MATERIAL_GPU_SLOT)); + slotBindings.insert(gpu::Shader::Binding(std::string("xMap"), 0)); + slotBindings.insert(gpu::Shader::Binding(std::string("yMap"), 1)); + slotBindings.insert(gpu::Shader::Binding(std::string("zMap"), 2)); + + gpu::ShaderPointer program = gpu::ShaderPointer(gpu::Shader::createProgram(vertexShader, pixelShader)); + gpu::Shader::makeProgram(*program, slotBindings); + + auto state = std::make_shared(); + state->setCullMode(gpu::State::CULL_BACK); + state->setDepthTest(true, true, gpu::LESS_EQUAL); + + _pipeline = gpu::PipelinePointer(gpu::Pipeline::create(program, state)); + } + + if (_needsModelReload) { + getModel(); + } + + gpu::Batch& batch = *args->_batch; + batch.setPipeline(_pipeline); + + auto mesh = _modelGeometry.getMesh(); + Transform transform(voxelToWorldMatrix()); + batch.setModelTransform(transform); + batch.setInputFormat(mesh->getVertexFormat()); + batch.setInputBuffer(gpu::Stream::POSITION, mesh->getVertexBuffer()); + batch.setInputBuffer(gpu::Stream::NORMAL, + mesh->getVertexBuffer()._buffer, + sizeof(float) * 3, + mesh->getVertexBuffer()._stride); + batch.setIndexBuffer(gpu::UINT32, mesh->getIndexBuffer()._buffer, 0); + + if (!_xTextureURL.isEmpty() && !_xTexture) { + _xTexture = DependencyManager::get()->getTexture(_xTextureURL); + } + if (!_yTextureURL.isEmpty() && !_yTexture) { + _yTexture = DependencyManager::get()->getTexture(_yTextureURL); + } + if (!_zTextureURL.isEmpty() && !_zTexture) { + _zTexture = DependencyManager::get()->getTexture(_zTextureURL); + } + + batch._glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + + if (_xTexture) { + batch.setResourceTexture(0, _xTexture->getGPUTexture()); + } else { + batch.setResourceTexture(0, DependencyManager::get()->getWhiteTexture()); + } + if (_yTexture) { + batch.setResourceTexture(1, _yTexture->getGPUTexture()); + } else { + batch.setResourceTexture(1, DependencyManager::get()->getWhiteTexture()); + } + if (_zTexture) { + batch.setResourceTexture(2, _zTexture->getGPUTexture()); + } else { + batch.setResourceTexture(2, DependencyManager::get()->getWhiteTexture()); + } + + int voxelVolumeSizeLocation = _pipeline->getProgram()->getUniforms().findLocation("voxelVolumeSize"); + batch._glUniform3f(voxelVolumeSizeLocation, _voxelVolumeSize.x, _voxelVolumeSize.y, _voxelVolumeSize.z); + + batch.drawIndexed(gpu::TRIANGLES, mesh->getNumIndices(), 0); + + RenderableDebugableEntityItem::render(this, args); +} + +bool RenderablePolyVoxEntityItem::addToScene(EntityItemPointer self, + std::shared_ptr scene, + render::PendingChanges& pendingChanges) { + _myItem = scene->allocateID(); + + auto renderItem = std::make_shared(shared_from_this()); + auto renderData = PolyVoxPayload::Pointer(renderItem); + auto renderPayload = std::make_shared(renderData); + + pendingChanges.resetItem(_myItem, renderPayload); + + return true; +} + +void RenderablePolyVoxEntityItem::removeFromScene(EntityItemPointer self, + std::shared_ptr scene, + render::PendingChanges& pendingChanges) { + pendingChanges.removeItem(_myItem); +} + +namespace render { + template <> const ItemKey payloadGetKey(const PolyVoxPayload::Pointer& payload) { + return ItemKey::Builder::opaqueShape(); + } + + template <> const Item::Bound payloadGetBound(const PolyVoxPayload::Pointer& payload) { + if (payload && payload->_owner) { + auto polyVoxEntity = std::dynamic_pointer_cast(payload->_owner); + return polyVoxEntity->getAABox(); + } + return render::Item::Bound(); + } + + template <> void payloadRender(const PolyVoxPayload::Pointer& payload, RenderArgs* args) { + if (args && payload && payload->_owner) { + payload->_owner->render(args); + } + } +} diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h index 1097ad21be..d495900ce9 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h @@ -13,11 +13,30 @@ #define hifi_RenderablePolyVoxEntityItem_h #include +#include #include "PolyVoxEntityItem.h" #include "RenderableDebugableEntityItem.h" #include "RenderableEntityItem.h" + +class PolyVoxPayload { +public: + PolyVoxPayload(EntityItemPointer owner) : _owner(owner), _bounds(AABox()) { } + typedef render::Payload Payload; + typedef Payload::DataPointer Pointer; + + EntityItemPointer _owner; + AABox _bounds; +}; + +namespace render { + template <> const ItemKey payloadGetKey(const PolyVoxPayload::Pointer& payload); + template <> const Item::Bound payloadGetBound(const PolyVoxPayload::Pointer& payload); + template <> void payloadRender(const PolyVoxPayload::Pointer& payload, RenderArgs* args); +} + + class RenderablePolyVoxEntityItem : public PolyVoxEntityItem { public: static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); @@ -70,7 +89,16 @@ public: virtual void setVoxelInVolume(glm::vec3 position, uint8_t toValue); - SIMPLE_RENDERABLE(); + virtual void setXTextureURL(QString xTextureURL); + virtual void setYTextureURL(QString yTextureURL); + virtual void setZTextureURL(QString zTextureURL); + + virtual bool addToScene(EntityItemPointer self, + std::shared_ptr scene, + render::PendingChanges& pendingChanges); + virtual void removeFromScene(EntityItemPointer self, + std::shared_ptr scene, + render::PendingChanges& pendingChanges); protected: virtual void updateVoxelSurfaceStyle(PolyVoxSurfaceStyle voxelSurfaceStyle); @@ -90,7 +118,15 @@ private: QVector> _points; // XXX + NetworkTexturePointer _xTexture; + NetworkTexturePointer _yTexture; + NetworkTexturePointer _zTexture; + int _onCount = 0; // how many non-zero voxels are in _volData + + const int MATERIAL_GPU_SLOT = 3; + render::ItemID _myItem; + static gpu::PipelinePointer _pipeline; }; diff --git a/libraries/entities-renderer/src/polyvox.slf b/libraries/entities-renderer/src/polyvox.slf new file mode 100644 index 0000000000..e5c8a49133 --- /dev/null +++ b/libraries/entities-renderer/src/polyvox.slf @@ -0,0 +1,54 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// model.frag +// fragment shader +// +// Created by Seth Alves on 2015-8-3 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +<@include gpu/Inputs.slh@> + +layout(location = 0) out vec4 _fragColor0; +layout(location = 1) out vec4 _fragColor1; +layout(location = 2) out vec4 _fragColor2; + +<@include model/Material.slh@> + +in vec3 _normal; +in vec4 _position; +in vec4 _inPosition; + +uniform sampler2D xMap; +uniform sampler2D yMap; +uniform sampler2D zMap; +uniform vec3 voxelVolumeSize; + +void main(void) { + vec3 worldNormal = cross(dFdy(_inPosition.xyz), dFdx(_inPosition.xyz)); + worldNormal = normalize(worldNormal); + + float inPositionX = (_inPosition.x - 0.5) / voxelVolumeSize.x; + float inPositionY = (_inPosition.y - 0.5) / voxelVolumeSize.y; + float inPositionZ = (_inPosition.z - 0.5) / voxelVolumeSize.z; + + vec4 xyDiffuse = texture2D(xMap, vec2(-inPositionX, -inPositionY)); + vec4 xzDiffuse = texture2D(yMap, vec2(-inPositionX, inPositionZ)); + vec4 yzDiffuse = texture2D(zMap, vec2(inPositionZ, -inPositionY)); + + vec3 xyDiffuseScaled = xyDiffuse.rgb * abs(worldNormal.z); + vec3 xzDiffuseScaled = xzDiffuse.rgb * abs(worldNormal.y); + vec3 yzDiffuseScaled = yzDiffuse.rgb * abs(worldNormal.x); + + vec4 diffuse = vec4(xyDiffuseScaled + xzDiffuseScaled + yzDiffuseScaled, 1.0); + + Material mat = getMaterial(); + + _fragColor0 = vec4(diffuse.rgb, 0.0); + _fragColor1 = vec4(_normal, 1.0); + _fragColor2 = vec4(getMaterialSpecular(mat), getMaterialShininess(mat) / 128.0); +} diff --git a/libraries/entities-renderer/src/polyvox.slv b/libraries/entities-renderer/src/polyvox.slv new file mode 100644 index 0000000000..0074993c80 --- /dev/null +++ b/libraries/entities-renderer/src/polyvox.slv @@ -0,0 +1,30 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// polyvox.vert +// vertex shader +// +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +<@include gpu/Inputs.slh@> + +<@include gpu/Transform.slh@> + +<$declareStandardTransform()$> + +out vec4 _position; +out vec4 _inPosition; +out vec3 _normal; + +void main(void) { + // standard transform + TransformCamera cam = getTransformCamera(); + TransformObject obj = getTransformObject(); + <$transformModelToEyeAndClipPos(cam, obj, inPosition, _position, gl_Position)$> + <$transformModelToEyeDir(cam, obj, inNormal.xyz, _normal)$> + _inPosition = inPosition; +} diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 4fe3d3842b..d8b555cb59 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -105,6 +105,9 @@ CONSTRUCT_PROPERTY(faceCamera, TextEntityItem::DEFAULT_FACE_CAMERA), CONSTRUCT_PROPERTY(actionData, QByteArray()), CONSTRUCT_PROPERTY(normals, QVector()), CONSTRUCT_PROPERTY(strokeWidths, QVector()), +CONSTRUCT_PROPERTY(xTextureURL, ""), +CONSTRUCT_PROPERTY(yTextureURL, ""), +CONSTRUCT_PROPERTY(zTextureURL, ""), _id(UNKNOWN_ENTITY_ID), _idSet(false), @@ -369,6 +372,9 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_ACTION_DATA, actionData); CHECK_PROPERTY_CHANGE(PROP_NORMALS, normals); CHECK_PROPERTY_CHANGE(PROP_STROKE_WIDTHS, strokeWidths); + CHECK_PROPERTY_CHANGE(PROP_X_TEXTURE_URL, xTextureURL); + CHECK_PROPERTY_CHANGE(PROP_Y_TEXTURE_URL, yTextureURL); + CHECK_PROPERTY_CHANGE(PROP_Z_TEXTURE_URL, zTextureURL); changedProperties += _stage.getChangedProperties(); changedProperties += _atmosphere.getChangedProperties(); @@ -508,6 +514,10 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool _atmosphere.copyToScriptValue(properties, engine, skipDefaults, defaultEntityProperties); _skybox.copyToScriptValue(properties, engine, skipDefaults, defaultEntityProperties); + COPY_PROPERTY_TO_QSCRIPTVALUE(xTextureURL); + COPY_PROPERTY_TO_QSCRIPTVALUE(yTextureURL); + COPY_PROPERTY_TO_QSCRIPTVALUE(zTextureURL); + return properties; } @@ -600,6 +610,12 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool _stage.copyFromScriptValue(object, _defaultSettings); _atmosphere.copyFromScriptValue(object, _defaultSettings); _skybox.copyFromScriptValue(object, _defaultSettings); + + + COPY_PROPERTY_FROM_QSCRIPTVALUE(xTextureURL, QString, setXTextureURL); + COPY_PROPERTY_FROM_QSCRIPTVALUE(yTextureURL, QString, setYTextureURL); + COPY_PROPERTY_FROM_QSCRIPTVALUE(zTextureURL, QString, setZTextureURL); + _lastEdited = usecTimestampNow(); } @@ -827,6 +843,9 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType::Value command, Ent APPEND_ENTITY_PROPERTY(PROP_VOXEL_VOLUME_SIZE, properties.getVoxelVolumeSize()); APPEND_ENTITY_PROPERTY(PROP_VOXEL_DATA, properties.getVoxelData()); APPEND_ENTITY_PROPERTY(PROP_VOXEL_SURFACE_STYLE, properties.getVoxelSurfaceStyle()); + APPEND_ENTITY_PROPERTY(PROP_X_TEXTURE_URL, properties.getXTextureURL()); + APPEND_ENTITY_PROPERTY(PROP_Y_TEXTURE_URL, properties.getYTextureURL()); + APPEND_ENTITY_PROPERTY(PROP_Z_TEXTURE_URL, properties.getZTextureURL()); } if (properties.getType() == EntityTypes::Line) { @@ -1086,6 +1105,9 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_VOXEL_VOLUME_SIZE, glm::vec3, setVoxelVolumeSize); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_VOXEL_DATA, QByteArray, setVoxelData); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_VOXEL_SURFACE_STYLE, uint16_t, setVoxelSurfaceStyle); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_X_TEXTURE_URL, QString, setXTextureURL); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_Y_TEXTURE_URL, QString, setYTextureURL); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_Z_TEXTURE_URL, QString, setZTextureURL); } if (properties.getType() == EntityTypes::Line) { @@ -1221,6 +1243,10 @@ void EntityItemProperties::markAllChanged() { _normalsChanged = true; _strokeWidthsChanged = true; + + _xTextureURLChanged = true; + _yTextureURLChanged = true; + _zTextureURLChanged = true; } /// The maximum bounding cube for the entity, independent of it's rotation. diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 030b006a9c..8085c24d90 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -157,6 +157,10 @@ public: DEFINE_PROPERTY(PROP_NORMALS, Normals, normals, QVector); DEFINE_PROPERTY(PROP_STROKE_WIDTHS, StrokeWidths, strokeWidths, QVector); DEFINE_PROPERTY_REF(PROP_ACTION_DATA, ActionData, actionData, QByteArray); + DEFINE_PROPERTY_REF(PROP_X_TEXTURE_URL, XTextureURL, xTextureURL, QString); + DEFINE_PROPERTY_REF(PROP_Y_TEXTURE_URL, YTextureURL, yTextureURL, QString); + DEFINE_PROPERTY_REF(PROP_Z_TEXTURE_URL, ZTextureURL, zTextureURL, QString); + static QString getBackgroundModeString(BackgroundMode mode); @@ -319,6 +323,9 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) { DEBUG_PROPERTY_IF_CHANGED(debug, properties, Href, href, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, Description, description, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, ActionData, actionData, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, XTextureURL, xTextureURL, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, YTextureURL, yTextureURL, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, ZTextureURL, zTextureURL, ""); properties.getStage().debugDump(); properties.getAtmosphere().debugDump(); diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index 18bf064aac..2205b6e115 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -127,10 +127,15 @@ enum EntityPropertyList { PROP_ACTION_DATA, - //Used by quad entity + PROP_X_TEXTURE_URL, // used by PolyVox + PROP_Y_TEXTURE_URL, // used by PolyVox + PROP_Z_TEXTURE_URL, // used by PolyVox + + //Used by PolyLine entity PROP_NORMALS, PROP_STROKE_WIDTHS, + //////////////////////////////////////////////////////////////////////////////////////////////////// // ATTENTION: add new properties to end of list just ABOVE this line PROP_AFTER_LAST_ITEM, diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index bb7a11fe62..434134d2cd 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -61,7 +61,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/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp index b0e7dcd022..362f5dc72a 100644 --- a/libraries/entities/src/ModelEntityItem.cpp +++ b/libraries/entities/src/ModelEntityItem.cpp @@ -39,6 +39,7 @@ ModelEntityItem::ModelEntityItem(const EntityItemID& entityItemID, const EntityI setProperties(properties); _lastAnimated = usecTimestampNow(); _jointMappingCompleted = false; + _lastKnownFrameIndex = -1; _color[0] = _color[1] = _color[2] = 0; } @@ -217,15 +218,17 @@ void ModelEntityItem::mapJoints(const QStringList& modelJointNames) { } } -QVector ModelEntityItem::getAnimationFrame() { - QVector frameData; +const QVector& ModelEntityItem::getAnimationFrame() { + if (!hasAnimation() || !_jointMappingCompleted) { - return frameData; + return _lastKnownFrameData; } - AnimationPointer myAnimation = getAnimation(_animationURL); + AnimationPointer myAnimation = getAnimation(_animationURL); // FIXME: this could be optimized if (myAnimation && myAnimation->isLoaded()) { - QVector frames = myAnimation->getFrames(); + + const QVector& frames = myAnimation->getFramesReference(); // NOTE: getFrames() is too heavy + int frameCount = frames.size(); if (frameCount > 0) { int animationFrameIndex = (int)(glm::floor(getAnimationFrameIndex())) % frameCount; @@ -233,18 +236,22 @@ QVector ModelEntityItem::getAnimationFrame() { animationFrameIndex = 0; } - QVector rotations = frames[animationFrameIndex].rotations; + if (animationFrameIndex != _lastKnownFrameIndex) { + _lastKnownFrameIndex = animationFrameIndex; + + const QVector& rotations = frames[animationFrameIndex].rotations; - frameData.resize(_jointMapping.size()); - for (int j = 0; j < _jointMapping.size(); j++) { - int rotationIndex = _jointMapping[j]; - if (rotationIndex != -1 && rotationIndex < rotations.size()) { - frameData[j] = rotations[rotationIndex]; + _lastKnownFrameData.resize(_jointMapping.size()); + for (int j = 0; j < _jointMapping.size(); j++) { + int rotationIndex = _jointMapping[j]; + if (rotationIndex != -1 && rotationIndex < rotations.size()) { + _lastKnownFrameData[j] = rotations[rotationIndex]; + } } } } } - return frameData; + return _lastKnownFrameData; } bool ModelEntityItem::isAnimatingSomething() const { diff --git a/libraries/entities/src/ModelEntityItem.h b/libraries/entities/src/ModelEntityItem.h index ae6a86cde1..e3d42e6b2c 100644 --- a/libraries/entities/src/ModelEntityItem.h +++ b/libraries/entities/src/ModelEntityItem.h @@ -106,7 +106,7 @@ public: float getAnimationLastFrame() const { return _animationLoop.getLastFrame(); } void mapJoints(const QStringList& modelJointNames); - QVector getAnimationFrame(); + const QVector& getAnimationFrame(); bool jointsMapped() const { return _jointMappingCompleted; } bool getAnimationIsPlaying() const { return _animationLoop.isRunning(); } @@ -123,6 +123,9 @@ public: static void cleanupLoadedAnimations(); protected: + QVector _lastKnownFrameData; + int _lastKnownFrameIndex; + bool isAnimatingSomething() const; diff --git a/libraries/entities/src/PolyLineEntityItem.cpp b/libraries/entities/src/PolyLineEntityItem.cpp index 0c068e7789..9ade80f3be 100644 --- a/libraries/entities/src/PolyLineEntityItem.cpp +++ b/libraries/entities/src/PolyLineEntityItem.cpp @@ -35,10 +35,10 @@ PolyLineEntityItem::PolyLineEntityItem(const EntityItemID& entityItemID, const E EntityItem(entityItemID) , _lineWidth(DEFAULT_LINE_WIDTH), _pointsChanged(true), -_points(QVector(0)), -_vertices(QVector(0)), -_normals(QVector(0)), -_strokeWidths(QVector(0)) +_points(QVector(0.0f)), +_vertices(QVector(0.0f)), +_normals(QVector(0.0f)), +_strokeWidths(QVector(0.0f)) { _type = EntityTypes::PolyLine; _created = properties.getCreated(); @@ -109,7 +109,7 @@ bool PolyLineEntityItem::setStrokeWidths(const QVector& strokeWidths ) { bool PolyLineEntityItem::setNormals(const QVector& normals) { _normals = normals; - if (_points.size () < 2) { + if (_points.size () < 2 || _normals.size() < 2) { return false; } diff --git a/libraries/entities/src/PolyVoxEntityItem.cpp b/libraries/entities/src/PolyVoxEntityItem.cpp index 048017de1c..c9f3705712 100644 --- a/libraries/entities/src/PolyVoxEntityItem.cpp +++ b/libraries/entities/src/PolyVoxEntityItem.cpp @@ -26,6 +26,9 @@ const float PolyVoxEntityItem::MAX_VOXEL_DIMENSION = 32.0f; const QByteArray PolyVoxEntityItem::DEFAULT_VOXEL_DATA(PolyVoxEntityItem::makeEmptyVoxelData()); const PolyVoxEntityItem::PolyVoxSurfaceStyle PolyVoxEntityItem::DEFAULT_VOXEL_SURFACE_STYLE = PolyVoxEntityItem::SURFACE_MARCHING_CUBES; +const QString PolyVoxEntityItem::DEFAULT_X_TEXTURE_URL = QString(""); +const QString PolyVoxEntityItem::DEFAULT_Y_TEXTURE_URL = QString(""); +const QString PolyVoxEntityItem::DEFAULT_Z_TEXTURE_URL = QString(""); EntityItemPointer PolyVoxEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { return std::make_shared(entityID, properties); @@ -49,7 +52,10 @@ PolyVoxEntityItem::PolyVoxEntityItem(const EntityItemID& entityItemID, const Ent EntityItem(entityItemID), _voxelVolumeSize(PolyVoxEntityItem::DEFAULT_VOXEL_VOLUME_SIZE), _voxelData(PolyVoxEntityItem::DEFAULT_VOXEL_DATA), - _voxelSurfaceStyle(PolyVoxEntityItem::DEFAULT_VOXEL_SURFACE_STYLE) + _voxelSurfaceStyle(PolyVoxEntityItem::DEFAULT_VOXEL_SURFACE_STYLE), + _xTextureURL(PolyVoxEntityItem::DEFAULT_X_TEXTURE_URL), + _yTextureURL(PolyVoxEntityItem::DEFAULT_Y_TEXTURE_URL), + _zTextureURL(PolyVoxEntityItem::DEFAULT_Z_TEXTURE_URL) { _type = EntityTypes::PolyVox; setProperties(properties); @@ -94,6 +100,9 @@ EntityItemProperties PolyVoxEntityItem::getProperties() const { COPY_ENTITY_PROPERTY_TO_PROPERTIES(voxelVolumeSize, getVoxelVolumeSize); COPY_ENTITY_PROPERTY_TO_PROPERTIES(voxelData, getVoxelData); COPY_ENTITY_PROPERTY_TO_PROPERTIES(voxelSurfaceStyle, getVoxelSurfaceStyle); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(xTextureURL, getXTextureURL); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(yTextureURL, getYTextureURL); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(zTextureURL, getZTextureURL); return properties; } @@ -103,6 +112,9 @@ bool PolyVoxEntityItem::setProperties(const EntityItemProperties& properties) { SET_ENTITY_PROPERTY_FROM_PROPERTIES(voxelVolumeSize, setVoxelVolumeSize); SET_ENTITY_PROPERTY_FROM_PROPERTIES(voxelData, setVoxelData); SET_ENTITY_PROPERTY_FROM_PROPERTIES(voxelSurfaceStyle, setVoxelSurfaceStyle); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(xTextureURL, setXTextureURL); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(yTextureURL, setYTextureURL); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(zTextureURL, setZTextureURL); if (somethingChanged) { bool wantDebug = false; @@ -127,6 +139,9 @@ int PolyVoxEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* dat READ_ENTITY_PROPERTY(PROP_VOXEL_VOLUME_SIZE, glm::vec3, setVoxelVolumeSize); READ_ENTITY_PROPERTY(PROP_VOXEL_DATA, QByteArray, setVoxelData); READ_ENTITY_PROPERTY(PROP_VOXEL_SURFACE_STYLE, uint16_t, setVoxelSurfaceStyle); + READ_ENTITY_PROPERTY(PROP_X_TEXTURE_URL, QString, setXTextureURL); + READ_ENTITY_PROPERTY(PROP_Y_TEXTURE_URL, QString, setYTextureURL); + READ_ENTITY_PROPERTY(PROP_Z_TEXTURE_URL, QString, setZTextureURL); return bytesRead; } @@ -138,6 +153,9 @@ EntityPropertyFlags PolyVoxEntityItem::getEntityProperties(EncodeBitstreamParams requestedProperties += PROP_VOXEL_VOLUME_SIZE; requestedProperties += PROP_VOXEL_DATA; requestedProperties += PROP_VOXEL_SURFACE_STYLE; + requestedProperties += PROP_X_TEXTURE_URL; + requestedProperties += PROP_Y_TEXTURE_URL; + requestedProperties += PROP_Z_TEXTURE_URL; return requestedProperties; } @@ -153,6 +171,10 @@ void PolyVoxEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeB APPEND_ENTITY_PROPERTY(PROP_VOXEL_VOLUME_SIZE, getVoxelVolumeSize()); APPEND_ENTITY_PROPERTY(PROP_VOXEL_DATA, getVoxelData()); APPEND_ENTITY_PROPERTY(PROP_VOXEL_SURFACE_STYLE, (uint16_t) getVoxelSurfaceStyle()); + APPEND_ENTITY_PROPERTY(PROP_X_TEXTURE_URL, getXTextureURL()); + APPEND_ENTITY_PROPERTY(PROP_Y_TEXTURE_URL, getYTextureURL()); + APPEND_ENTITY_PROPERTY(PROP_Z_TEXTURE_URL, getZTextureURL()); + } void PolyVoxEntityItem::debugDump() const { diff --git a/libraries/entities/src/PolyVoxEntityItem.h b/libraries/entities/src/PolyVoxEntityItem.h index 9e20187195..0d0ab060f9 100644 --- a/libraries/entities/src/PolyVoxEntityItem.h +++ b/libraries/entities/src/PolyVoxEntityItem.h @@ -87,6 +87,18 @@ class PolyVoxEntityItem : public EntityItem { static QByteArray makeEmptyVoxelData(quint16 voxelXSize = 16, quint16 voxelYSize = 16, quint16 voxelZSize = 16); + static const QString DEFAULT_X_TEXTURE_URL; + virtual void setXTextureURL(QString xTextureURL) { _xTextureURL = xTextureURL; } + virtual const QString& getXTextureURL() const { return _xTextureURL; } + + static const QString DEFAULT_Y_TEXTURE_URL; + virtual void setYTextureURL(QString yTextureURL) { _yTextureURL = yTextureURL; } + virtual const QString& getYTextureURL() const { return _yTextureURL; } + + static const QString DEFAULT_Z_TEXTURE_URL; + virtual void setZTextureURL(QString zTextureURL) { _zTextureURL = zTextureURL; } + virtual const QString& getZTextureURL() const { return _zTextureURL; } + protected: virtual void updateVoxelSurfaceStyle(PolyVoxSurfaceStyle voxelSurfaceStyle) { _voxelSurfaceStyle = voxelSurfaceStyle; @@ -95,6 +107,11 @@ class PolyVoxEntityItem : public EntityItem { glm::vec3 _voxelVolumeSize; // this is always 3 bytes QByteArray _voxelData; PolyVoxSurfaceStyle _voxelSurfaceStyle; + + QString _xTextureURL; + QString _yTextureURL; + QString _zTextureURL; + }; #endif // hifi_PolyVoxEntityItem_h diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index f3f222f310..a4e9b5f799 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -67,7 +67,7 @@ PacketVersion versionForPacketType(PacketType::Value packetType) { case EntityAdd: case EntityEdit: case EntityData: - return VERSION_ENTITIES_NEW_PROTOCOL_LAYER; + return VERSION_POLYVOX_TEXTURES; default: return 11; } diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 5df5473be8..c1211317fb 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -140,5 +140,6 @@ const PacketVersion VERSION_ENTITIES_SCRIPT_TIMESTAMP = 31; const PacketVersion VERSION_ENTITIES_SCRIPT_TIMESTAMP_FIX = 32; const PacketVersion VERSION_ENTITIES_HAVE_SIMULATION_OWNER_AND_ACTIONS_OVER_WIRE = 33; const PacketVersion VERSION_ENTITIES_NEW_PROTOCOL_LAYER = 35; +const PacketVersion VERSION_POLYVOX_TEXTURES = 36; #endif // hifi_PacketHeaders_h \ No newline at end of file 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 7fab5255fc..6776fa6c79 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -279,13 +279,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 8839a5b463..3080d965a1 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -54,6 +54,7 @@ static int modelPointerTypeId = qRegisterMetaType >(); static int weakNetworkGeometryPointerTypeId = qRegisterMetaType >(); static int vec3VectorTypeId = qRegisterMetaType >(); float Model::FAKE_DIMENSION_PLACEHOLDER = -1.0f; +#define HTTP_INVALID_COM "http://invalid.com" Model::Model(RigPointer rig, QObject* parent) : QObject(parent), @@ -67,7 +68,8 @@ Model::Model(RigPointer rig, QObject* parent) : _cauterizeBones(false), _lodDistance(0.0f), _pupilDilation(0.0f), - _url("http://invalid.com"), + _url(HTTP_INVALID_COM), + _urlAsString(HTTP_INVALID_COM), _isVisible(true), _blendNumber(0), _appliedBlendNumber(0), @@ -181,17 +183,12 @@ void Model::RenderPipelineLib::initLocations(gpu::ShaderPointer& program, Model: locations.texcoordMatrices = program->getUniforms().findLocation("texcoordMatrices"); locations.emissiveParams = program->getUniforms().findLocation("emissiveParams"); locations.glowIntensity = program->getUniforms().findLocation("glowIntensity"); - locations.normalFittingMapUnit = program->getTextures().findLocation("normalFittingMap"); - locations.specularTextureUnit = program->getTextures().findLocation("specularMap"); locations.emissiveTextureUnit = program->getTextures().findLocation("emissiveMap"); - locations.materialBufferUnit = program->getBuffers().findLocation("materialBuffer"); locations.lightBufferUnit = program->getBuffers().findLocation("lightBuffer"); - locations.clusterMatrices = program->getUniforms().findLocation("clusterMatrices"); - locations.clusterIndices = program->getInputs().findLocation("inSkinClusterIndex"); locations.clusterWeights = program->getInputs().findLocation("inSkinClusterWeight"); } @@ -230,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; @@ -1085,6 +1080,7 @@ void Model::setURL(const QUrl& url, const QUrl& fallback, bool retainCurrent, bo invalidCalculatedMeshBoxes(); _url = url; + _urlAsString = _url.toString(); onInvalidate(); @@ -1868,8 +1864,9 @@ void Model::pickPrograms(gpu::Batch& batch, RenderMode mode, bool translucent, f batch._glUniform1f(locations->glowIntensity, DEFAULT_GLOW_INTENSITY); } - if ((locations->normalFittingMapUnit > -1)) { - batch.setResourceTexture(locations->normalFittingMapUnit, DependencyManager::get()->getNormalFittingTexture()); + if ((locations->normalFittingMapUnit > -1)) { + batch.setResourceTexture(locations->normalFittingMapUnit, + DependencyManager::get()->getNormalFittingTexture()); } } diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 9fdb2f3691..c9b63b598e 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -74,6 +74,7 @@ public: Q_INVOKABLE void setURL(const QUrl& url, const QUrl& fallback = QUrl(), bool retainCurrent = false, bool delayLoad = false); const QUrl& getURL() const { return _url; } + const QString& getURLAsString() const { return _urlAsString; } // new Scene/Engine rendering support void setVisibleInScene(bool newValue, std::shared_ptr scene); @@ -328,6 +329,7 @@ private: QVector _blendshapeCoefficients; QUrl _url; + QString _urlAsString; QUrl _collisionUrl; bool _isVisible; 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); }