diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index da588bc316..d656464c10 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -70,7 +70,7 @@ void Agent::handleOctreePacket(QSharedPointer packet, SharedNodePointe // pull out the piggybacked packet and create a new QSharedPointer for it int piggyBackedSizeWithHeader = packet->getPayloadSize() - statsMessageLength; - std::unique_ptr buffer = std::unique_ptr(new char[piggyBackedSizeWithHeader]); + auto buffer = std::unique_ptr(new char[piggyBackedSizeWithHeader]); memcpy(buffer.get(), packet->getPayload() + statsMessageLength, piggyBackedSizeWithHeader); auto newPacket = NLPacket::fromReceivedPacket(std::move(buffer), piggyBackedSizeWithHeader, packet->getSenderSockAddr()); @@ -107,6 +107,7 @@ void Agent::handleAudioPacket(QSharedPointer packet) { } const QString AGENT_LOGGING_NAME = "agent"; +const int PING_INTERVAL = 1000; void Agent::run() { ThreadedAssignment::commonInit(AGENT_LOGGING_NAME, NodeType::Agent); @@ -118,6 +119,10 @@ void Agent::run() { << NodeType::EntityServer ); + _pingTimer = new QTimer(this); + connect(_pingTimer, SIGNAL(timeout()), SLOT(sendPingRequests())); + _pingTimer->start(PING_INTERVAL); + // figure out the URL for the script for this agent assignment QUrl scriptURL; if (_payload.isEmpty()) { @@ -193,7 +198,27 @@ void Agent::run() { void Agent::aboutToFinish() { _scriptEngine.stop(); - + + _pingTimer->stop(); + delete _pingTimer; + // our entity tree is going to go away so tell that to the EntityScriptingInterface DependencyManager::get()->setEntityTree(NULL); } + +void Agent::sendPingRequests() { + auto nodeList = DependencyManager::get(); + + nodeList->eachMatchingNode([](const SharedNodePointer& node)->bool { + switch (node->getType()) { + case NodeType::AvatarMixer: + case NodeType::AudioMixer: + case NodeType::EntityServer: + return true; + default: + return false; + } + }, [nodeList](const SharedNodePointer& node) { + nodeList->sendPacket(nodeList->constructPingPacket(), *node); + }); +} diff --git a/assignment-client/src/Agent.h b/assignment-client/src/Agent.h index 241e14439c..4c207e59aa 100644 --- a/assignment-client/src/Agent.h +++ b/assignment-client/src/Agent.h @@ -58,11 +58,13 @@ private slots: void handleAudioPacket(QSharedPointer packet); void handleOctreePacket(QSharedPointer packet, SharedNodePointer senderNode); void handleJurisdictionPacket(QSharedPointer packet, SharedNodePointer senderNode); + void sendPingRequests(); private: ScriptEngine _scriptEngine; EntityEditPacketSender _entityEditSender; EntityTreeHeadlessViewer _entityViewer; + QTimer* _pingTimer; MixedAudioStream _receivedAudioStream; float _lastReceivedAudioLoudness; diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index b68332210b..04cbb0b325 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -927,9 +927,9 @@ void AudioMixer::parseSettingsObject(const QJsonObject &settingsObject) { const QString USE_STDEV_FOR_DESIRED_CALC_JSON_KEY = "use_stdev_for_desired_calc"; _streamSettings._useStDevForJitterCalc = audioBufferGroupObject[USE_STDEV_FOR_DESIRED_CALC_JSON_KEY].toBool(); if (_streamSettings._useStDevForJitterCalc) { - qDebug() << "Using Philip's stdev method for jitter calc if dynamic jitter buffers enabled"; + qDebug() << "Using stdev method for jitter calc if dynamic jitter buffers enabled"; } else { - qDebug() << "Using Fred's max-gap method for jitter calc if dynamic jitter buffers enabled"; + qDebug() << "Using max-gap method for jitter calc if dynamic jitter buffers enabled"; } const QString WINDOW_STARVE_THRESHOLD_JSON_KEY = "window_starve_threshold"; diff --git a/cmake/modules/FindGLEW.cmake b/cmake/modules/FindGLEW.cmake index e86db3fdac..b1789fb614 100644 --- a/cmake/modules/FindGLEW.cmake +++ b/cmake/modules/FindGLEW.cmake @@ -38,5 +38,19 @@ if (WIN32) find_package_handle_standard_args(GLEW DEFAULT_MSG GLEW_INCLUDE_DIRS GLEW_LIBRARIES GLEW_DLL_PATH) add_paths_to_fixup_libs(${GLEW_DLL_PATH}) +elseif (APPLE) +else () + find_path(GLEW_INCLUDE_DIR GL/glew.h) + find_library(GLEW_LIBRARY NAMES GLEW glew32 glew glew32s PATH_SUFFIXES lib64) + + set(GLEW_INCLUDE_DIRS ${GLEW_INCLUDE_DIR}) + set(GLEW_LIBRARIES ${GLEW_LIBRARY}) + + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(GLEW + REQUIRED_VARS GLEW_INCLUDE_DIR GLEW_LIBRARY) + + mark_as_advanced(GLEW_INCLUDE_DIR GLEW_LIBRARY) + endif () diff --git a/examples/controlPanel.js b/examples/controlPanel.js new file mode 100644 index 0000000000..59d66fdaf9 --- /dev/null +++ b/examples/controlPanel.js @@ -0,0 +1,214 @@ +// +// controlPanel.js +// examples +// +// Created by Zander Otavka on 7/15/15. +// Copyright 2015 High Fidelity, Inc. +// +// Shows a few common controls in a FloatingUIPanel 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 +// + +Script.include([ + "libraries/globals.js", + "libraries/overlayManager.js", +]); + +var BG_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/card-bg.svg"; +var CLOSE_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/tools/close.svg"; +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 background = new BillboardOverlay({ + url: BG_IMAGE_URL, + dimensions: { + x: 0.5, + y: 0.5, + }, + isFacingAvatar: false, + alpha: 1.0, + ignoreRayIntersection: false +}); +panel.addChild(background); + +var closeButton = new BillboardOverlay({ + url: CLOSE_IMAGE_URL, + dimensions: { + x: 0.15, + y: 0.15, + }, + isFacingAvatar: false, + alpha: 1.0, + ignoreRayIntersection: false, + offsetPosition: { + x: -0.1, + y: 0.1, + z: -0.001 + } +}); +closeButton.onClick = function(event) { + panel.visible = false; +}; +panel.addChild(closeButton); + +var micMuteButton = new BillboardOverlay({ + url: MIC_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 + } +}); +micMuteButton.onClick = function(event) { + AudioDevice.toggleMute(); +}; +panel.addChild(micMuteButton); + +var faceMuteButton = new BillboardOverlay({ + url: FACE_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 + } +}); +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 + } +}); +addressBarButton.onClick = function(event) { + DialogsManager.toggleAddressBar(); +}; +panel.addChild(addressBarButton); + + +function onMicMuteToggled() { + var offset; + if (AudioDevice.getMuted()) { + offset = 45; + } else { + offset = 0; + } + micMuteButton.subImage = { + x: offset, + y: 0, + width: 45, + height: 45 + }; +} +onMicMuteToggled(); + +function onFaceMuteToggled() { + var offset; + if (FaceTracker.getMuted()) { + offset = 45; + } else { + offset = 0; + } + faceMuteButton.subImage = { + x: offset, + y: 0, + width: 45, + height: 45 + }; +} +onFaceMuteToggled(); + +var mouseDown = {}; + +function onMouseDown(event) { + if (event.isLeftButton) { + mouseDown.overlay = OverlayManager.findAtPoint({ x: event.x, y: event.y }); + } + if (event.isRightButton) { + mouseDown.pos = { x: event.x, y: event.y }; + } +} + +function onMouseUp(event) { + if (event.isLeftButton) { + var overlay = OverlayManager.findAtPoint({ x: event.x, y: event.y }); + if (overlay && overlay === mouseDown.overlay && overlay.onClick) { + overlay.onClick(event); + } + } + if (event.isRightButton && Vec3.distance(mouseDown.pos, { x: event.x, y: event.y }) < 5) { + panel.setProperties({ + visible: !panel.visible, + offsetRotation: { + bind: "quat", + value: Quat.multiply(MyAvatar.orientation, { x: 0, y: 1, z: 0, w: 0 }) + } + }); + } + + mouseDown = {}; +} + +function onScriptEnd(event) { + panel.destroy(); +} + +Controller.mousePressEvent.connect(onMouseDown); +Controller.mouseReleaseEvent.connect(onMouseUp); +AudioDevice.muteToggled.connect(onMicMuteToggled); +FaceTracker.muteToggled.connect(onFaceMuteToggled); +Script.scriptEnding.connect(onScriptEnd); diff --git a/examples/example/ui/floatingUIExample.js b/examples/example/ui/floatingUIExample.js new file mode 100644 index 0000000000..1e957fe6b3 --- /dev/null +++ b/examples/example/ui/floatingUIExample.js @@ -0,0 +1,169 @@ +// +// floatingUI.js +// examples/example/ui +// +// Created by Alexander Otavka +// 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 +// + +Script.include([ + "../../libraries/globals.js", + "../../libraries/overlayManager.js", +]); + +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 bluePanel = mainPanel.addChild(new FloatingUIPanel ({ + offsetPosition: { x: 0.1, y: 0.1, z: -0.2 } +})); + +var mainPanelBackground = new BillboardOverlay({ + url: BG_IMAGE_URL, + dimensions: { + x: 0.5, + y: 0.5, + }, + isFacingAvatar: false, + alpha: 1.0, + ignoreRayIntersection: false, + offsetPosition: { + x: 0, + y: 0, + 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({ + url: RED_DOT_IMAGE_URL, + dimensions: { + x: 0.1, + y: 0.1, + }, + isFacingAvatar: false, + alpha: 1.0, + ignoreRayIntersection: false, + offsetPosition: { + x: -0.15, + y: -0.15, + z: 0 + } +})); + +var redDot2 = mainPanel.addChild(new BillboardOverlay({ + url: RED_DOT_IMAGE_URL, + dimensions: { + x: 0.1, + y: 0.1, + }, + isFacingAvatar: false, + alpha: 1.0, + ignoreRayIntersection: false, + offsetPosition: { + x: -0.155, + y: 0.005, + z: 0 + } +})); + +var blueSquare = bluePanel.addChild(new BillboardOverlay({ + url: BLUE_SQUARE_IMAGE_URL, + dimensions: { + x: 0.1, + y: 0.1, + }, + isFacingAvatar: false, + alpha: 1.0, + ignoreRayIntersection: false, + offsetPosition: { + x: 0.055, + y: -0.055, + z: 0 + } +})); + +var blueSquare2 = bluePanel.addChild(new BillboardOverlay({ + url: BLUE_SQUARE_IMAGE_URL, + dimensions: { + x: 0.1, + y: 0.1, + }, + isFacingAvatar: false, + alpha: 1.0, + ignoreRayIntersection: false, + offsetPosition: { + x: 0.055, + y: 0.055, + z: 0 + } +})); + +var blueSquare3 = blueSquare2.clone(); +blueSquare3.offsetPosition = { + x: -0.055, + y: 0.055, + z: 0 +}; + + +var mouseDown = {}; + +function onMouseDown(event) { + if (event.isLeftButton) { + mouseDown.overlay = OverlayManager.findAtPoint({ x: event.x, y: event.y }); + } + if (event.isRightButton) { + mouseDown.pos = { x: event.x, y: event.y }; + } +} + +function onMouseUp(event) { + if (event.isLeftButton) { + var overlay = OverlayManager.findAtPoint({ x: event.x, y: event.y }); + if (overlay === mouseDown.overlay) { + if (overlay.attachedPanel === 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; + } + } + } + if (event.isRightButton && Vec3.distance(mouseDown.pos, { x: event.x, y: event.y }) < 5) { + mainPanel.visible = !mainPanel.visible; + } +} + +function onScriptEnd() { + mainPanel.destroy(); +} + +Controller.mousePressEvent.connect(onMouseDown); +Controller.mouseReleaseEvent.connect(onMouseUp); +Script.scriptEnding.connect(onScriptEnd); \ No newline at end of file diff --git a/examples/hmdControls.js b/examples/hmdControls.js index e14ddca3ef..04c1dade0a 100644 --- a/examples/hmdControls.js +++ b/examples/hmdControls.js @@ -9,7 +9,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -var MOVE_DISTANCE = 10.0; +var MOVE_DISTANCE = 2.0; var PITCH_INCREMENT = 0.5; // degrees var pitchChange = 0; // degrees var YAW_INCREMENT = 0.5; // degrees diff --git a/examples/inspect.js b/examples/inspect.js index 0df90fbac3..555b4105b7 100644 --- a/examples/inspect.js +++ b/examples/inspect.js @@ -23,7 +23,7 @@ var RAD_TO_DEG = 180.0 / PI; var AZIMUTH_RATE = 90.0; var ALTITUDE_RATE = 200.0; var RADIUS_RATE = 1.0 / 100.0; -var PAN_RATE = 50.0; +var PAN_RATE = 250.0; var Y_AXIS = { x: 0, @@ -139,7 +139,7 @@ function handlePanMode(dx, dy) { var right = Quat.getRight(Camera.getOrientation()); var distance = Vec3.length(vector); - var dv = Vec3.sum(Vec3.multiply(up, -distance * dy / PAN_RATE), Vec3.multiply(right, distance * dx / PAN_RATE)); + var dv = Vec3.sum(Vec3.multiply(up, distance * dy / PAN_RATE), Vec3.multiply(right, -distance * dx / PAN_RATE)); center = Vec3.sum(center, dv); position = Vec3.sum(position, dv); diff --git a/examples/libraries/overlayManager.js b/examples/libraries/overlayManager.js new file mode 100644 index 0000000000..cd05cd2a52 --- /dev/null +++ b/examples/libraries/overlayManager.js @@ -0,0 +1,455 @@ +// +// overlayManager.js +// examples/libraries +// +// Created by Zander Otavka on 7/24/15 +// Copyright 2015 High Fidelity, Inc. +// +// Manage overlays with object oriented goodness, instead of ugly `Overlays.h` methods. +// Instead of: +// +// var billboard = Overlays.addOverlay("billboard", { visible: false }); +// ... +// Overlays.editOverlay(billboard, { visible: true }); +// ... +// Overlays.deleteOverlay(billboard); +// +// You can now do: +// +// var billboard = new BillboardOverlay({ visible: false }); +// ... +// billboard.visible = true; +// ... +// billboard.destroy(); +// +// See more on usage below. +// +// Note that including this file will delete Overlays from the global scope. All the +// functionality of Overlays is represented here, just better. If you try to use Overlays in +// tandem, there may be performance problems or nasty surprises. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + + +(function() { + // Delete `Overlays` from the global scope. + var Overlays = this.Overlays; + delete this.Overlays; + + var overlays = {}; + var panels = {}; + + var overlayTypes; + var Overlay, Overlay2D, Base3DOverlay, Planar3DOverlay, Volume3DOverlay; + + + // + // Create a new JavaScript object for an overlay of given ID. + // + function makeOverlayFromId(id) { + var type = Overlays.getOverlayType(id); + if (!type) { + return null; + } + 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; + } + + // + // Get or create an overlay 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 findOverlay(id, knownOverlaysOnly, searchList) { + if (id > 0) { + knownOverlaysOnly = Boolean(knownOverlaysOnly) || Boolean(searchList); + searchList = searchList || overlays; + var foundOverlay = searchList[id]; + if (foundOverlay) { + return foundOverlay; + } + if (!knownOverlaysOnly) { + return makeOverlayFromId(id); + } + } + return null; + } + + + // + // Perform global scoped operations on overlays, such as finding by ray intersection. + // + OverlayManager = { + findOnRay: function(pickRay, knownOverlaysOnly, searchList) { + var rayPickResult = Overlays.findRayIntersection(pickRay); + print("raypick " + rayPickResult.overlayID); + if (rayPickResult.intersects) { + return findOverlay(rayPickResult.overlayID, knownOverlaysOnly, searchList); + } + return null; + }, + findAtPoint: function(point, knownOverlaysOnly, searchList) { + var foundID = Overlays.getOverlayAtPoint(point); + print("at point " + foundID); + if (foundID) { + return findOverlay(foundID, knownOverlaysOnly, searchList); + } else { + var pickRay = Camera.computePickRay(point.x, point.y); + return OverlayManager.findOnRay(pickRay, knownOverlaysOnly, searchList); + } + }, + makeSearchList: function(overlayArray) { + var searchList = {}; + overlayArray.forEach(function(overlay){ + searchList[overlay._id] = overlay; + }); + return searchList; + } + }; + + + // + // Object oriented abstraction layer for overlays. + // + // Usage: + // // Create an overlay + // var billboard = new BillboardOverlay({ + // visible: true, + // isFacingAvatar: true, + // ignoreRayIntersections: false + // }); + // + // // Get a property + // var isVisible = billboard.visible; + // + // // Set a single property + // billboard.position = { x: 1, y: 3, z: 2 }; + // + // // Set multiple properties at the same time + // billboard.setProperties({ + // url: "http://images.com/overlayImage.jpg", + // dimensions: { x: 2, y: 2 } + // }); + // + // // Clone an overlay + // var clonedBillboard = billboard.clone(); + // + // // Remove an overlay from the world + // billboard.destroy(); + // + // // Remember, there is a poor orphaned JavaScript object left behind. You should + // // remove any references to it so you don't accidentally try to modify an overlay that + // // isn't there. + // billboard = undefined; + // + (function() { + var ABSTRACT = null; + overlayTypes = {}; + + function generateOverlayClass(superclass, type, properties) { + var that; + if (type == ABSTRACT) { + that = function(type, params) { + superclass.call(this, type, params); + }; + } else { + that = function(params) { + superclass.call(this, type, params); + }; + overlayTypes[type] = that; + } + + that.prototype = new superclass(); + that.prototype.constructor = that; + + properties.forEach(function(prop) { + Object.defineProperty(that.prototype, prop, { + get: function() { + return Overlays.getProperty(this._id, prop); + }, + set: function(newValue) { + var keyValuePair = {}; + keyValuePair[prop] = newValue; + this.setProperties(keyValuePair); + }, + configurable: false + }); + }); + + return that; + } + + // 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) { + this._id = Overlays.addOverlay(type, params); + overlays[this._id] = this; + } else { + this._id = 0; + } + this._attachedPanelPointer = null; + }; + + that.prototype.constructor = that; + + Object.defineProperty(that.prototype, "isLoaded", { + get: function() { + return Overlays.isLoaded(this._id); + } + }); + + Object.defineProperty(that.prototype, "attachedPanel", { + get: function() { + return this._attachedPanelPointer; + } + }); + + that.prototype.getTextSize = function(text) { + return Overlays.textSize(this._id, text); + }; + + that.prototype.setProperties = function(properties) { + Overlays.editOverlay(this._id, properties); + }; + + that.prototype.clone = function() { + return makeOverlayFromId(Overlays.cloneOverlay(this._id)); + }; + + that.prototype.destroy = function() { + Overlays.deleteOverlay(this._id); + }; + + return generateOverlayClass(that, ABSTRACT, [ + "alpha", "glowLevel", "pulseMax", "pulseMin", "pulsePeriod", "glowLevelPulse", + "alphaPulse", "colorPulse", "visible", "anchor" + ]); + })(); + + Overlay2D = generateOverlayClass(Overlay, ABSTRACT, [ + "bounds", "x", "y", "width", "height" + ]); + + Base3DOverlay = generateOverlayClass(Overlay, ABSTRACT, [ + "position", "lineWidth", "rotation", "isSolid", "isFilled", "isWire", "isDashedLine", + "ignoreRayIntersection", "drawInFront", "drawOnHUD" + ]); + + Planar3DOverlay = generateOverlayClass(Base3DOverlay, ABSTRACT, [ + "dimensions" + ]); + + Volume3DOverlay = generateOverlayClass(Base3DOverlay, ABSTRACT, [ + "dimensions" + ]); + + generateOverlayClass(Overlay2D, "image", [ + "subImage", "imageURL" + ]); + + generateOverlayClass(Overlay2D, "text", [ + "font", "text", "backgroundColor", "backgroundAlpha", "leftMargin", "topMargin" + ]); + + generateOverlayClass(Planar3DOverlay, "text3d", [ + "text", "backgroundColor", "backgroundAlpha", "lineHeight", "leftMargin", "topMargin", + "rightMargin", "bottomMargin", "isFacingAvatar" + ]); + + generateOverlayClass(Volume3DOverlay, "cube", [ + "borderSize" + ]); + + generateOverlayClass(Volume3DOverlay, "sphere", [ + ]); + + generateOverlayClass(Planar3DOverlay, "circle3d", [ + "startAt", "endAt", "outerRadius", "innerRadius", "hasTickMarks", + "majorTickMarksAngle", "minorTickMarksAngle", "majorTickMarksLength", + "minorTickMarksLength", "majorTickMarksColor", "minorTickMarksColor" + ]); + + generateOverlayClass(Planar3DOverlay, "rectangle3d", [ + ]); + + generateOverlayClass(Base3DOverlay, "line3d", [ + "start", "end" + ]); + + generateOverlayClass(Planar3DOverlay, "grid", [ + "minorGridWidth", "majorGridEvery" + ]); + + generateOverlayClass(Volume3DOverlay, "localmodels", [ + ]); + + generateOverlayClass(Volume3DOverlay, "model", [ + "url", "dimensions", "textures" + ]); + + generateOverlayClass(Planar3DOverlay, "billboard", [ + "url", "subImage", "isFacingAvatar" + ].concat(PANEL_ATTACHABLE_FIELDS)); + })(); + + ImageOverlay = overlayTypes["image"]; + TextOverlay = overlayTypes["text"]; + Text3DOverlay = overlayTypes["text3d"]; + Cube3DOverlay = overlayTypes["cube"]; + Sphere3DOverlay = overlayTypes["sphere"]; + Circle3DOverlay = overlayTypes["circle3d"]; + Rectangle3DOverlay = overlayTypes["rectangle3d"]; + Line3DOverlay = overlayTypes["line3d"]; + Grid3DOverlay = overlayTypes["grid"]; + LocalModelsOverlay = overlayTypes["localmodels"]; + ModelOverlay = overlayTypes["model"]; + BillboardOverlay = overlayTypes["billboard"]; + + + // + // Object oriented abstraction layer for panels. + // + FloatingUIPanel = (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) { + Object.defineProperty(that.prototype, prop, { + get: function() { + return Overlays.getPanelProperty(this._id, prop); + }, + set: function(newValue) { + var keyValuePair = {}; + keyValuePair[prop] = newValue; + this.setProperties(keyValuePair); + }, + configurable: false + }); + }); + + var PSEUDO_FIELDS = []; + + PSEUDO_FIELDS.push("children"); + Object.defineProperty(that.prototype, "children", { + get: function() { + return this._children.slice(); + } + }); + + PSEUDO_FIELDS.push("visible"); + Object.defineProperty(that.prototype, "visible", { + get: function() { + return this._visible; + }, + set: function(visible) { + this._visible = visible; + this._children.forEach(function(child) { + child.visible = visible; + }); + } + }); + + 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); + 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); + } + }; + + 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.destroy = function() { + Overlays.deletePanel(this._id); + }; + + return that; + })(); + + + function onOverlayDeleted(id) { + if (id in overlays) { + if (overlays[id]._attachedPanelPointer) { + overlays[id]._attachedPanelPointer.removeChild(overlays[id]); + } + delete overlays[id]; + } + } + + function onPanelDeleted(id) { + if (id in panels) { + panels[id]._children.forEach(function(child) { + print(JSON.stringify(child.destroy)); + child.destroy(); + }); + delete panels[id]; + } + } + + Overlays.overlayDeleted.connect(onOverlayDeleted); + Overlays.panelDeleted.connect(onPanelDeleted); +})(); diff --git a/examples/libraries/overlayUtils.js b/examples/libraries/overlayUtils.js index 636ea40825..a5622ec435 100644 --- a/examples/libraries/overlayUtils.js +++ b/examples/libraries/overlayUtils.js @@ -1,7 +1,17 @@ -/** - * OverlayGroup provides a way to create composite overlays and control their - * position relative to a settable rootPosition and rootRotation. - */ +// +// overlayUtils.js +// examples/libraries +// +// Copyright 2015 High Fidelity, Inc. +// + + +// +// DEPRECATION WARNING: Will be deprecated soon in favor of FloatingUIPanel. +// +// OverlayGroup provides a way to create composite overlays and control their +// position relative to a settable rootPosition and rootRotation. +// OverlayGroup = function(opts) { var that = {}; @@ -59,6 +69,6 @@ OverlayGroup = function(opts) { } overlays = {}; } - + return that; }; diff --git a/ice-server/src/IceServer.cpp b/ice-server/src/IceServer.cpp index 46c1582e7d..2a4e2b4300 100644 --- a/ice-server/src/IceServer.cpp +++ b/ice-server/src/IceServer.cpp @@ -50,7 +50,7 @@ void IceServer::processDatagrams() { while (_serverSocket.hasPendingDatagrams()) { // setup a buffer to read the packet into int packetSizeWithHeader = _serverSocket.pendingDatagramSize(); - std::unique_ptr buffer = std::unique_ptr(new char[packetSizeWithHeader]); + auto buffer = std::unique_ptr(new char[packetSizeWithHeader]); _serverSocket.readDatagram(buffer.get(), packetSizeWithHeader, sendingSockAddr.getAddressPointer(), sendingSockAddr.getPortPointer()); diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 589f6a992f..7c2fb25cd7 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -218,33 +218,13 @@ else (APPLE) "${PROJECT_SOURCE_DIR}/resources" $/resources ) - - find_package(OpenGL REQUIRED) - - if (${OPENGL_INCLUDE_DIR}) - include_directories(SYSTEM "${OPENGL_INCLUDE_DIR}") - endif () - - target_link_libraries(${TARGET_NAME} "${OPENGL_LIBRARY}") - + # link target to external libraries if (WIN32) - add_dependency_external_projects(glew) - find_package(GLEW REQUIRED) - target_include_directories(${TARGET_NAME} PRIVATE ${GLEW_INCLUDE_DIRS}) - - target_link_libraries(${TARGET_NAME} ${GLEW_LIBRARIES} wsock32.lib opengl32.lib Winmm.lib) - - if (USE_NSIGHT) - # try to find the Nsight package and add it to the build if we find it - find_package(NSIGHT) - if (NSIGHT_FOUND) - include_directories(${NSIGHT_INCLUDE_DIRS}) - add_definitions(-DNSIGHT_FOUND) - target_link_libraries(${TARGET_NAME} "${NSIGHT_LIBRARIES}") - endif () - endif() - + # target_link_libraries(${TARGET_NAME} wsock32.lib Winmm.lib) + target_link_libraries(${TARGET_NAME} wsock32.lib Winmm.lib) + else (WIN32) + # Nothing else required on linux apparently endif() endif (APPLE) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 9b155f8b84..1adccafa68 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -772,8 +772,9 @@ void Application::initializeGL() { } #endif - // Where the gpuContext is created and where the TRUE Backend is created and assigned - _gpuContext = std::make_shared(new gpu::GLBackend()); + // Where the gpuContext is initialized and where the TRUE Backend is created and assigned + gpu::Context::init(); + _gpuContext = std::make_shared(); initDisplay(); qCDebug(interfaceapp, "Initialized Display."); @@ -3757,10 +3758,13 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri scriptEngine->registerGlobalObject("AnimationCache", DependencyManager::get().data()); scriptEngine->registerGlobalObject("SoundCache", DependencyManager::get().data()); scriptEngine->registerGlobalObject("Account", AccountScriptingInterface::getInstance()); + scriptEngine->registerGlobalObject("DialogsManager", _dialogsManagerScriptingInterface); scriptEngine->registerGlobalObject("GlobalServices", GlobalServicesScriptingInterface::getInstance()); qScriptRegisterMetaType(scriptEngine, DownloadInfoResultToScriptValue, DownloadInfoResultFromScriptValue); + scriptEngine->registerGlobalObject("FaceTracker", DependencyManager::get().data()); + scriptEngine->registerGlobalObject("AvatarManager", DependencyManager::get().data()); qScriptRegisterMetaType(scriptEngine, joystickToScriptValue, joystickFromScriptValue); diff --git a/interface/src/Application.h b/interface/src/Application.h index d1886862d2..56c126462a 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -49,6 +49,7 @@ #include "avatar/MyAvatar.h" #include "devices/SixenseManager.h" #include "scripting/ControllerScriptingInterface.h" +#include "scripting/DialogsManagerScriptingInterface.h" #include "scripting/WebWindowClass.h" #include "ui/AudioStatsDialog.h" #include "ui/BandwidthDialog.h" @@ -69,6 +70,7 @@ #include "UndoStackScriptingInterface.h" #include "gpu/Context.h" + #include "render/Engine.h" class QGLWidget; @@ -643,6 +645,8 @@ private: ApplicationOverlay _applicationOverlay; ApplicationCompositor _compositor; int _numFramesSinceLastResize = 0; + + DialogsManagerScriptingInterface* _dialogsManagerScriptingInterface = new DialogsManagerScriptingInterface(); }; #endif // hifi_Application_h diff --git a/interface/src/GLCanvas.h b/interface/src/GLCanvas.h index 925061edd0..6c53a17e04 100644 --- a/interface/src/GLCanvas.h +++ b/interface/src/GLCanvas.h @@ -13,7 +13,6 @@ #define hifi_GLCanvas_h #include -#include #include #include diff --git a/interface/src/Stars.cpp b/interface/src/Stars.cpp index c1e65086bf..42b1a3f2e2 100644 --- a/interface/src/Stars.cpp +++ b/interface/src/Stars.cpp @@ -14,8 +14,6 @@ #include #include -#include -#include #include #include #include @@ -208,8 +206,6 @@ void Stars::render(RenderArgs* renderArgs, float alpha) { batch._glUniform1f(_timeSlot, secs); geometryCache->renderUnitCube(batch); - glHint(GL_POINT_SMOOTH_HINT, GL_NICEST); - static const size_t VERTEX_STRIDE = sizeof(StarVertex); size_t offset = offsetof(StarVertex, position); gpu::BufferView posView(vertexBuffer, offset, vertexBuffer->getSize(), VERTEX_STRIDE, positionElement); @@ -218,10 +214,11 @@ void Stars::render(RenderArgs* renderArgs, float alpha) { // Render the stars batch.setPipeline(_starsPipeline); + batch.setInputFormat(streamFormat); batch.setInputBuffer(VERTICES_SLOT, posView); batch.setInputBuffer(COLOR_SLOT, colView); batch.draw(gpu::Primitive::POINTS, STARFIELD_NUM_STARS); - + renderArgs->_context->render(batch); } diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 095a225952..e29e5e4408 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -45,6 +45,7 @@ #include "Util.h" #include "world.h" #include "InterfaceLogging.h" +#include "EntityRig.h" using namespace std; @@ -75,9 +76,9 @@ namespace render { } } -Avatar::Avatar() : +Avatar::Avatar(RigPointer rig) : AvatarData(), - _skeletonModel(this), + _skeletonModel(this, nullptr, rig), _skeletonOffset(0.0f), _bodyYawDelta(0.0f), _positionDeltaAccumulator(0.0f), @@ -430,15 +431,17 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) { } } + /* + // TODO: re-implement these when we have more detailed avatar collision shapes bool renderSkeleton = Menu::getInstance()->isOptionChecked(MenuOption::RenderSkeletonCollisionShapes); - bool renderHead = Menu::getInstance()->isOptionChecked(MenuOption::RenderHeadCollisionShapes); - bool renderBounding = Menu::getInstance()->isOptionChecked(MenuOption::RenderBoundingCollisionShapes); if (renderSkeleton) { - _skeletonModel.renderJointCollisionShapes(0.7f); } + bool renderHead = Menu::getInstance()->isOptionChecked(MenuOption::RenderHeadCollisionShapes); if (renderHead && shouldRenderHead(renderArgs)) { - getHead()->getFaceModel().renderJointCollisionShapes(0.7f); } + */ + + bool renderBounding = Menu::getInstance()->isOptionChecked(MenuOption::RenderBoundingCollisionShapes); if (renderBounding && shouldRenderHead(renderArgs)) { _skeletonModel.renderBoundingCollisionShapes(*renderArgs->_batch, 0.7f); } @@ -793,33 +796,6 @@ void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& frustum, co renderer->draw(batch, text_x, -text_y, nameUTF8.data(), textColor); } -bool Avatar::findRayIntersection(RayIntersectionInfo& intersection) const { - bool hit = _skeletonModel.findRayIntersection(intersection); - hit = getHead()->getFaceModel().findRayIntersection(intersection) || hit; - return hit; -} - -bool Avatar::findSphereCollisions(const glm::vec3& penetratorCenter, float penetratorRadius, CollisionList& collisions) { - return _skeletonModel.findSphereCollisions(penetratorCenter, penetratorRadius, collisions); - // TODO: Andrew to fix: Temporarily disabling collisions against the head - //return getHead()->getFaceModel().findSphereCollisions(penetratorCenter, penetratorRadius, collisions); -} - -bool Avatar::findPlaneCollisions(const glm::vec4& plane, CollisionList& collisions) { - return _skeletonModel.findPlaneCollisions(plane, collisions) || - getHead()->getFaceModel().findPlaneCollisions(plane, collisions); -} - -bool Avatar::findCollisions(const QVector& shapes, CollisionList& collisions) { - // TODO: Andrew to fix: also collide against _skeleton - //bool collided = _skeletonModel.findCollisions(shapes, collisions); - - Model& headModel = getHead()->getFaceModel(); - //collided = headModel.findCollisions(shapes, collisions) || collided; - bool collided = headModel.findCollisions(shapes, collisions); - return collided; -} - void Avatar::setSkeletonOffset(const glm::vec3& offset) { const float MAX_OFFSET_LENGTH = _scale * 0.5f; float offsetLength = glm::length(offset); @@ -843,7 +819,7 @@ QVector Avatar::getJointRotations() const { } QVector jointRotations(_skeletonModel.getJointStateCount()); for (int i = 0; i < _skeletonModel.getJointStateCount(); ++i) { - _skeletonModel.getJointState(i, jointRotations[i]); + _skeletonModel.getJointRotation(i, jointRotations[i]); } return jointRotations; } @@ -853,7 +829,7 @@ glm::quat Avatar::getJointRotation(int index) const { return AvatarData::getJointRotation(index); } glm::quat rotation; - _skeletonModel.getJointState(index, rotation); + _skeletonModel.getJointRotation(index, rotation); return rotation; } @@ -977,7 +953,7 @@ void Avatar::setAttachmentData(const QVector& attachmentData) { if (_unusedAttachments.size() > 0) { model = _unusedAttachments.takeFirst(); } else { - model = new Model(this); + model = new Model(std::make_shared(), this); } model->init(); _attachmentModels.append(model); @@ -1139,9 +1115,8 @@ void Avatar::setShowDisplayName(bool showDisplayName) { // virtual void Avatar::computeShapeInfo(ShapeInfo& shapeInfo) { - const CapsuleShape& capsule = _skeletonModel.getBoundingShape(); - shapeInfo.setCapsuleY(capsule.getRadius(), capsule.getHalfHeight()); - shapeInfo.setOffset(_skeletonModel.getBoundingShapeOffset()); + shapeInfo.setCapsuleY(_skeletonModel.getBoundingCapsuleRadius(), 0.5f * _skeletonModel.getBoundingCapsuleHeight()); + shapeInfo.setOffset(_skeletonModel.getBoundingCapsuleOffset()); } // virtual diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 253c94b0b3..6035c44389 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -72,7 +72,7 @@ class Avatar : public AvatarData { Q_PROPERTY(glm::vec3 skeletonOffset READ getSkeletonOffset WRITE setSkeletonOffset) public: - Avatar(); + Avatar(RigPointer rig = nullptr); ~Avatar(); typedef render::Payload Payload; @@ -110,26 +110,6 @@ public: /// Returns the distance to use as a LOD parameter. float getLODDistance() const; - bool findRayIntersection(RayIntersectionInfo& intersection) const; - - /// \param shapes list of shapes to collide against avatar - /// \param collisions list to store collision results - /// \return true if at least one shape collided with avatar - bool findCollisions(const QVector& shapes, CollisionList& collisions); - - /// Checks for penetration between the a sphere and the avatar's models. - /// \param penetratorCenter the center of the penetration test sphere - /// \param penetratorRadius the radius of the penetration test sphere - /// \param collisions[out] a list to which collisions get appended - /// \return whether or not the sphere penetrated - bool findSphereCollisions(const glm::vec3& penetratorCenter, float penetratorRadius, CollisionList& collisions); - - /// Checks for penetration between the described plane and the avatar. - /// \param plane the penetration plane - /// \param collisions[out] a list to which collisions get appended - /// \return whether or not the plane penetrated - bool findPlaneCollisions(const glm::vec4& plane, CollisionList& collisions); - virtual bool isMyAvatar() const { return false; } virtual QVector getJointRotations() const; @@ -186,9 +166,10 @@ public: virtual void computeShapeInfo(ShapeInfo& shapeInfo); - friend class AvatarManager; + void setMotionState(AvatarMotionState* motionState) { _motionState = motionState; } + AvatarMotionState* getMotionState() { return _motionState; } -signals: + signals: void collisionWithAvatar(const QUuid& myUUID, const QUuid& theirUUID, const CollisionInfo& collision); protected: @@ -218,7 +199,7 @@ protected: glm::vec3 _worldUpDirection; float _stringLength; bool _moving; ///< set when position is changing - + // protected methods... glm::vec3 getBodyRightDirection() const { return getOrientation() * IDENTITY_RIGHT; } glm::vec3 getBodyUpDirection() const { return getOrientation() * IDENTITY_UP; } @@ -243,7 +224,7 @@ protected: virtual void updateJointMappings(); render::ItemID _renderItemID; - + private: bool _initialized; NetworkTexturePointer _billboardTexture; @@ -251,9 +232,9 @@ private: bool _isLookAtTarget; void renderBillboard(RenderArgs* renderArgs); - + float getBillboardSize() const; - + static int _jointConesID; int _voiceSphereID; diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index d5922366e9..ee59a01e07 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -35,6 +35,7 @@ #include "Menu.h" #include "MyAvatar.h" #include "SceneScriptingInterface.h" +#include "AvatarRig.h" // 70 times per second - target is 60hz, but this helps account for any small deviations // in the update loop @@ -65,7 +66,7 @@ AvatarManager::AvatarManager(QObject* parent) : { // register a meta type for the weak pointer we'll use for the owning avatar mixer for each avatar qRegisterMetaType >("NodeWeakPointer"); - _myAvatar = std::make_shared(); + _myAvatar = std::make_shared(std::make_shared()); auto& packetReceiver = DependencyManager::get()->getPacketReceiver(); packetReceiver.registerListener(PacketType::BulkAvatarData, this, "processAvatarDataPacket"); @@ -160,7 +161,7 @@ void AvatarManager::simulateAvatarFades(float deltaTime) { } AvatarSharedPointer AvatarManager::newSharedAvatar() { - return AvatarSharedPointer(std::make_shared()); + return AvatarSharedPointer(std::make_shared(std::make_shared())); } // virtual @@ -178,11 +179,11 @@ AvatarSharedPointer AvatarManager::addAvatar(const QUuid& sessionUUID, const QWe // protected void AvatarManager::removeAvatarMotionState(AvatarSharedPointer avatar) { auto rawPointer = std::static_pointer_cast(avatar); - AvatarMotionState* motionState= rawPointer->_motionState; + AvatarMotionState* motionState = rawPointer->getMotionState(); if (motionState) { // clean up physics stuff motionState->clearObjectBackPointer(); - rawPointer->_motionState = nullptr; + rawPointer->setMotionState(nullptr); _avatarMotionStates.remove(motionState); _motionStatesToAdd.remove(motionState); _motionStatesToDelete.push_back(motionState); @@ -306,7 +307,7 @@ void AvatarManager::updateAvatarPhysicsShape(const QUuid& id) { AvatarHash::iterator avatarItr = _avatarHash.find(id); if (avatarItr != _avatarHash.end()) { auto avatar = std::static_pointer_cast(avatarItr.value()); - AvatarMotionState* motionState = avatar->_motionState; + AvatarMotionState* motionState = avatar->getMotionState(); if (motionState) { motionState->addDirtyFlags(EntityItem::DIRTY_SHAPE); } else { @@ -315,7 +316,7 @@ void AvatarManager::updateAvatarPhysicsShape(const QUuid& id) { btCollisionShape* shape = ObjectMotionState::getShapeManager()->getShape(shapeInfo); if (shape) { AvatarMotionState* motionState = new AvatarMotionState(avatar.get(), shape); - avatar->_motionState = motionState; + avatar->setMotionState(motionState); _motionStatesToAdd.insert(motionState); _avatarMotionStates.insert(motionState); } diff --git a/interface/src/avatar/FaceModel.cpp b/interface/src/avatar/FaceModel.cpp index 26ef621730..066144a425 100644 --- a/interface/src/avatar/FaceModel.cpp +++ b/interface/src/avatar/FaceModel.cpp @@ -16,13 +16,16 @@ #include "Head.h" #include "Menu.h" -FaceModel::FaceModel(Head* owningHead) : +FaceModel::FaceModel(Head* owningHead, RigPointer rig) : + Model(rig, nullptr), _owningHead(owningHead) { + assert(_rig); } void FaceModel::simulate(float deltaTime, bool fullUpdate) { updateGeometry(); + Avatar* owningAvatar = static_cast(_owningHead->_owningAvatar); glm::vec3 neckPosition; if (!owningAvatar->getSkeletonModel().getNeckPosition(neckPosition)) { @@ -35,67 +38,73 @@ void FaceModel::simulate(float deltaTime, bool fullUpdate) { } setRotation(neckParentRotation); setScale(glm::vec3(1.0f, 1.0f, 1.0f) * _owningHead->getScale()); - + setPupilDilation(_owningHead->getPupilDilation()); setBlendshapeCoefficients(_owningHead->getBlendshapeCoefficients()); - + // FIXME - this is very expensive, we shouldn't do it if we don't have to //invalidCalculatedMeshBoxes(); if (isActive()) { setOffset(-_geometry->getFBXGeometry().neckPivot); + + for (int i = 0; i < _rig->getJointStateCount(); i++) { + maybeUpdateNeckAndEyeRotation(i); + } + Model::simulateInternal(deltaTime); } } -void FaceModel::maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) { +void FaceModel::maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, 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(state.getDefaultTranslationInConstrainedFrame()) * + glm::translate(_rig->getJointDefaultTranslationInConstrainedFrame(index)) * joint.preTransform * glm::mat4_cast(joint.preRotation))); glm::vec3 pitchYawRoll = safeEulerAngles(_owningHead->getFinalOrientationInLocalFrame()); glm::vec3 lean = glm::radians(glm::vec3(_owningHead->getFinalLeanForward(), _owningHead->getTorsoTwist(), _owningHead->getFinalLeanSideways())); pitchYawRoll -= lean; - state.setRotationInConstrainedFrame(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); + _rig->setJointRotationInConstrainedFrame(index, + 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); } -void FaceModel::maybeUpdateEyeRotation(Model* model, const JointState& parentState, const FBXJoint& joint, JointState& state) { +void FaceModel::maybeUpdateEyeRotation(Model* model, const JointState& parentState, const FBXJoint& joint, 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(state.getDefaultTranslationInConstrainedFrame()) * - joint.preTransform * glm::mat4_cast(joint.preRotation * joint.rotation)); + 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)); 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)); glm::quat between = rotationBetween(front, lookAt); const float MAX_ANGLE = 30.0f * RADIANS_PER_DEGREE; - state.setRotationInConstrainedFrame(glm::angleAxis(glm::clamp(glm::angle(between), -MAX_ANGLE, MAX_ANGLE), glm::axis(between)) * - joint.rotation, DEFAULT_PRIORITY); + _rig->setJointRotationInConstrainedFrame(index, glm::angleAxis(glm::clamp(glm::angle(between), + -MAX_ANGLE, MAX_ANGLE), glm::axis(between)) * + joint.rotation, DEFAULT_PRIORITY); } -void FaceModel::updateJointState(int index) { - JointState& state = _jointStates[index]; +void FaceModel::maybeUpdateNeckAndEyeRotation(int index) { + const JointState& state = _rig->getJointState(index); const FBXJoint& joint = state.getFBXJoint(); + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + // guard against out-of-bounds access to _jointStates - if (joint.parentIndex != -1 && joint.parentIndex >= 0 && joint.parentIndex < _jointStates.size()) { - const JointState& parentState = _jointStates.at(joint.parentIndex); - const FBXGeometry& geometry = _geometry->getFBXGeometry(); + if (joint.parentIndex != -1 && joint.parentIndex >= 0 && joint.parentIndex < _rig->getJointStateCount()) { + const JointState& parentState = _rig->getJointState(joint.parentIndex); if (index == geometry.neckJointIndex) { - maybeUpdateNeckRotation(parentState, joint, state); - + maybeUpdateNeckRotation(parentState, joint, index); + } else if (index == geometry.leftEyeJointIndex || index == geometry.rightEyeJointIndex) { - maybeUpdateEyeRotation(this, parentState, joint, state); + maybeUpdateEyeRotation(this, parentState, joint, index); } } - - Model::updateJointState(index); } bool FaceModel::getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const { diff --git a/interface/src/avatar/FaceModel.h b/interface/src/avatar/FaceModel.h index 6c14beb587..54d685c73c 100644 --- a/interface/src/avatar/FaceModel.h +++ b/interface/src/avatar/FaceModel.h @@ -19,23 +19,23 @@ class Head; /// A face formed from a linear mix of blendshapes according to a set of coefficients. class FaceModel : public Model { Q_OBJECT - + public: - FaceModel(Head* owningHead); + FaceModel(Head* owningHead, RigPointer rig); virtual void simulate(float deltaTime, bool fullUpdate = true); - - virtual void maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, JointState& state); - virtual void maybeUpdateEyeRotation(Model* model, const JointState& parentState, const FBXJoint& joint, JointState& state); - virtual void updateJointState(int index); + + void maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, int index); + void maybeUpdateEyeRotation(Model* model, const JointState& parentState, const FBXJoint& joint, int index); + void maybeUpdateNeckAndEyeRotation(int index); /// Retrieve the positions of up to two eye meshes. /// \return whether or not both eye meshes were found bool getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const; - + private: - + Head* _owningHead; }; diff --git a/interface/src/avatar/Hand.cpp b/interface/src/avatar/Hand.cpp index 63604d710d..017f11a610 100644 --- a/interface/src/avatar/Hand.cpp +++ b/interface/src/avatar/Hand.cpp @@ -40,65 +40,6 @@ void Hand::simulate(float deltaTime, bool isMine) { } } -// We create a static CollisionList that is recycled for each collision test. -const float MAX_COLLISIONS_PER_AVATAR = 32; -static CollisionList handCollisions(MAX_COLLISIONS_PER_AVATAR); - -void Hand::collideAgainstAvatar(Avatar* avatar, bool isMyHand) { - if (!avatar || avatar == _owningAvatar) { - // don't collide hands against ourself (that is done elsewhere) - return; - } - - const SkeletonModel& skeletonModel = _owningAvatar->getSkeletonModel(); - int jointIndices[2]; - jointIndices[0] = skeletonModel.getLeftHandJointIndex(); - jointIndices[1] = skeletonModel.getRightHandJointIndex(); - - for (size_t i = 0; i < 2; i++) { - int jointIndex = jointIndices[i]; - if (jointIndex < 0) { - continue; - } - - handCollisions.clear(); - QVector shapes; - skeletonModel.getHandShapes(jointIndex, shapes); - - if (avatar->findCollisions(shapes, handCollisions)) { - glm::vec3 totalPenetration(0.0f); - glm::vec3 averageContactPoint; - for (int j = 0; j < handCollisions.size(); ++j) { - CollisionInfo* collision = handCollisions.getCollision(j); - totalPenetration += collision->_penetration; - averageContactPoint += collision->_contactPoint; - } - if (isMyHand) { - // our hand against other avatar - // TODO: resolve this penetration when we don't think the other avatar will yield - //palm.addToPenetration(averagePenetration); - } else { - // someone else's hand against MyAvatar - // TODO: submit collision info to MyAvatar which should lean accordingly - averageContactPoint /= (float)handCollisions.size(); - avatar->applyCollision(averageContactPoint, totalPenetration); - - CollisionInfo collision; - collision._penetration = totalPenetration; - collision._contactPoint = averageContactPoint; - emit avatar->collisionWithAvatar(avatar->getSessionUUID(), _owningAvatar->getSessionUUID(), collision); - } - } - } -} - -void Hand::resolvePenetrations() { - for (size_t i = 0; i < getNumPalms(); ++i) { - PalmData& palm = getPalms()[i]; - palm.resolvePenetrations(); - } -} - void Hand::render(RenderArgs* renderArgs, bool isMine) { gpu::Batch& batch = *renderArgs->_batch; if (renderArgs->_renderMode != RenderArgs::SHADOW_RENDER_MODE && diff --git a/interface/src/avatar/Hand.h b/interface/src/avatar/Hand.h index ddc9d13489..b78470b40d 100644 --- a/interface/src/avatar/Hand.h +++ b/interface/src/avatar/Hand.h @@ -29,10 +29,6 @@ class Avatar; -const float HAND_PADDLE_OFFSET = 0.1f; -const float HAND_PADDLE_THICKNESS = 0.01f; -const float HAND_PADDLE_RADIUS = 0.15f; - class Hand : public HandData { public: Hand(Avatar* owningAvatar); @@ -40,10 +36,6 @@ public: void simulate(float deltaTime, bool isMine); void render(RenderArgs* renderArgs, bool isMine); - void collideAgainstAvatar(Avatar* avatar, bool isMyHand); - - void resolvePenetrations(); - private: // disallow copies of the Hand, copy of owning Avatar is disallowed too Hand(const Hand&); diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index 55f33f57a4..bfa2e437a1 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -23,6 +23,7 @@ #include "Util.h" #include "devices/DdeFaceTracker.h" #include "devices/Faceshift.h" +#include "AvatarRig.h" using namespace std; @@ -57,11 +58,10 @@ Head::Head(Avatar* owningAvatar) : _isLookingAtMe(false), _lookingAtMeStarted(0), _wasLastLookingAtMe(0), - _faceModel(this), + _faceModel(this, std::make_shared()), _leftEyeLookAtID(DependencyManager::get()->allocateID()), _rightEyeLookAtID(DependencyManager::get()->allocateID()) { - } void Head::init() { diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 3ec6a2b1d6..0c01ce0249 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -78,8 +78,8 @@ const float MyAvatar::ZOOM_MIN = 0.5f; const float MyAvatar::ZOOM_MAX = 25.0f; const float MyAvatar::ZOOM_DEFAULT = 1.5f; -MyAvatar::MyAvatar() : - Avatar(), +MyAvatar::MyAvatar(RigPointer rig) : + Avatar(rig), _gravity(0.0f, 0.0f, 0.0f), _wasPushing(false), _isPushing(false), @@ -101,18 +101,14 @@ MyAvatar::MyAvatar() : _eyeContactTarget(LEFT_EYE), _realWorldFieldOfView("realWorldFieldOfView", DEFAULT_REAL_WORLD_FIELD_OF_VIEW_DEGREES), - _firstPersonSkeletonModel(this), + _rig(rig), _prevShouldDrawHead(true) { - _firstPersonSkeletonModel.setIsFirstPerson(true); - ShapeCollider::initDispatchTable(); for (int i = 0; i < MAX_DRIVE_KEYS; i++) { _driveKeys[i] = 0.0f; } - _skeletonModel.setEnableShapes(true); - // connect to AddressManager signal for location jumps connect(DependencyManager::get().data(), &AddressManager::locationChangeRequired, this, &MyAvatar::goToLocation); @@ -139,7 +135,6 @@ QByteArray MyAvatar::toByteArray() { void MyAvatar::reset() { _skeletonModel.reset(); - _firstPersonSkeletonModel.reset(); getHead()->reset(); _targetVelocity = glm::vec3(0.0f); @@ -195,7 +190,6 @@ void MyAvatar::simulate(float deltaTime) { { PerformanceTimer perfTimer("skeleton"); _skeletonModel.simulate(deltaTime); - _firstPersonSkeletonModel.simulate(deltaTime); } if (!_skeletonModel.hasSkeleton()) { @@ -211,10 +205,10 @@ void MyAvatar::simulate(float deltaTime) { { PerformanceTimer perfTimer("joints"); // copy out the skeleton joints from the model - _jointData.resize(_skeletonModel.getJointStateCount()); + _jointData.resize(_rig->getJointStateCount()); for (int i = 0; i < _jointData.size(); i++) { JointData& data = _jointData[i]; - data.valid = _skeletonModel.getJointState(i, data.rotation); + data.valid = _rig->getJointStateRotation(i, data.rotation); } } @@ -491,17 +485,6 @@ void MyAvatar::loadLastRecording() { _player->loadRecording(_recorder->getRecording()); } -AnimationHandlePointer MyAvatar::addAnimationHandle() { - AnimationHandlePointer handle = _skeletonModel.createAnimationHandle(); - _animationHandles.append(handle); - return handle; -} - -void MyAvatar::removeAnimationHandle(const AnimationHandlePointer& handle) { - handle->stop(); - _animationHandles.removeOne(handle); -} - void MyAvatar::startAnimation(const QString& url, float fps, float priority, bool loop, bool hold, float firstFrame, float lastFrame, const QStringList& maskedJoints) { if (QThread::currentThread() != thread()) { @@ -510,16 +493,7 @@ void MyAvatar::startAnimation(const QString& url, float fps, float priority, Q_ARG(float, lastFrame), Q_ARG(const QStringList&, maskedJoints)); return; } - AnimationHandlePointer handle = _skeletonModel.createAnimationHandle(); - handle->setURL(url); - handle->setFPS(fps); - handle->setPriority(priority); - handle->setLoop(loop); - handle->setHold(hold); - handle->setFirstFrame(firstFrame); - handle->setLastFrame(lastFrame); - handle->setMaskedJoints(maskedJoints); - handle->start(); + _rig->startAnimation(url, fps, priority, loop, hold, firstFrame, lastFrame, maskedJoints); } void MyAvatar::startAnimationByRole(const QString& role, const QString& url, float fps, float priority, @@ -530,25 +504,7 @@ void MyAvatar::startAnimationByRole(const QString& role, const QString& url, flo Q_ARG(float, lastFrame), Q_ARG(const QStringList&, maskedJoints)); return; } - // check for a configured animation for the role - foreach (const AnimationHandlePointer& handle, _animationHandles) { - if (handle->getRole() == role) { - handle->start(); - return; - } - } - // no joy; use the parameters provided - AnimationHandlePointer handle = _skeletonModel.createAnimationHandle(); - handle->setRole(role); - handle->setURL(url); - handle->setFPS(fps); - handle->setPriority(priority); - handle->setLoop(loop); - handle->setHold(hold); - handle->setFirstFrame(firstFrame); - handle->setLastFrame(lastFrame); - handle->setMaskedJoints(maskedJoints); - handle->start(); + _rig->startAnimationByRole(role, url, fps, priority, loop, hold, firstFrame, lastFrame, maskedJoints); } void MyAvatar::stopAnimationByRole(const QString& role) { @@ -556,11 +512,7 @@ void MyAvatar::stopAnimationByRole(const QString& role) { QMetaObject::invokeMethod(this, "stopAnimationByRole", Q_ARG(const QString&, role)); return; } - foreach (const AnimationHandlePointer& handle, _skeletonModel.getRunningAnimations()) { - if (handle->getRole() == role) { - handle->stop(); - } - } + _rig->stopAnimationByRole(role); } void MyAvatar::stopAnimation(const QString& url) { @@ -568,11 +520,7 @@ void MyAvatar::stopAnimation(const QString& url) { QMetaObject::invokeMethod(this, "stopAnimation", Q_ARG(const QString&, url)); return; } - foreach (const AnimationHandlePointer& handle, _skeletonModel.getRunningAnimations()) { - if (handle->getURL() == url) { - handle->stop(); - } - } + _rig->stopAnimation(url); } AnimationDetails MyAvatar::getAnimationDetailsByRole(const QString& role) { @@ -583,7 +531,7 @@ AnimationDetails MyAvatar::getAnimationDetailsByRole(const QString& role) { Q_ARG(const QString&, role)); return result; } - foreach (const AnimationHandlePointer& handle, _skeletonModel.getRunningAnimations()) { + foreach (const AnimationHandlePointer& handle, _rig->getRunningAnimations()) { if (handle->getRole() == role) { result = handle->getAnimationDetails(); break; @@ -600,7 +548,7 @@ AnimationDetails MyAvatar::getAnimationDetails(const QString& url) { Q_ARG(const QString&, url)); return result; } - foreach (const AnimationHandlePointer& handle, _skeletonModel.getRunningAnimations()) { + foreach (const AnimationHandlePointer& handle, _rig->getRunningAnimations()) { if (handle->getURL() == url) { result = handle->getAnimationDetails(); break; @@ -646,9 +594,10 @@ void MyAvatar::saveData() { settings.endArray(); settings.beginWriteArray("animationHandles"); - for (int i = 0; i < _animationHandles.size(); i++) { + auto animationHandles = _rig->getAnimationHandles(); + for (int i = 0; i < animationHandles.size(); i++) { settings.setArrayIndex(i); - const AnimationHandlePointer& pointer = _animationHandles.at(i); + const AnimationHandlePointer& pointer = animationHandles.at(i); settings.setValue("role", pointer->getRole()); settings.setValue("url", pointer->getURL()); settings.setValue("fps", pointer->getFPS()); @@ -766,25 +715,19 @@ void MyAvatar::loadData() { setAttachmentData(attachmentData); int animationCount = settings.beginReadArray("animationHandles"); - while (_animationHandles.size() > animationCount) { - _animationHandles.takeLast()->stop(); - } - while (_animationHandles.size() < animationCount) { - addAnimationHandle(); - } + _rig->deleteAnimations(); for (int i = 0; i < animationCount; i++) { settings.setArrayIndex(i); - const AnimationHandlePointer& handle = _animationHandles.at(i); - handle->setRole(settings.value("role", "idle").toString()); - handle->setURL(settings.value("url").toUrl()); - handle->setFPS(loadSetting(settings, "fps", 30.0f)); - handle->setPriority(loadSetting(settings, "priority", 1.0f)); - handle->setLoop(settings.value("loop", true).toBool()); - handle->setHold(settings.value("hold", false).toBool()); - handle->setFirstFrame(settings.value("firstFrame", 0.0f).toFloat()); - handle->setLastFrame(settings.value("lastFrame", INT_MAX).toFloat()); - handle->setMaskedJoints(settings.value("maskedJoints").toStringList()); - handle->setStartAutomatically(settings.value("startAutomatically", true).toBool()); + _rig->addAnimationByRole(settings.value("role", "idle").toString(), + settings.value("url").toString(), + loadSetting(settings, "fps", 30.0f), + loadSetting(settings, "priority", 1.0f), + settings.value("loop", true).toBool(), + settings.value("hold", false).toBool(), + settings.value("firstFrame", 0.0f).toFloat(), + settings.value("lastFrame", INT_MAX).toFloat(), + settings.value("maskedJoints").toStringList(), + settings.value("startAutomatically", true).toBool()); } settings.endArray(); @@ -792,6 +735,7 @@ void MyAvatar::loadData() { setCollisionSoundURL(settings.value("collisionSoundURL", DEFAULT_AVATAR_COLLISION_SOUND_URL).toString()); settings.endGroup(); + _rig->setEnableRig(settings.value("enableRig").toBool()); } void MyAvatar::saveAttachmentData(const AttachmentData& attachment) const { @@ -961,15 +905,15 @@ void MyAvatar::setJointRotations(QVector jointRotations) { void MyAvatar::setJointData(int index, const glm::quat& rotation) { if (QThread::currentThread() == thread()) { // HACK: ATM only JS scripts call setJointData() on MyAvatar so we hardcode the priority - _skeletonModel.setJointState(index, true, rotation, SCRIPT_PRIORITY); + _rig->setJointState(index, true, rotation, SCRIPT_PRIORITY); } } void MyAvatar::clearJointData(int index) { if (QThread::currentThread() == thread()) { // HACK: ATM only JS scripts call clearJointData() on MyAvatar so we hardcode the priority - _skeletonModel.setJointState(index, false, glm::quat(), 0.0f); - _skeletonModel.clearJointAnimationPriority(index); + _rig->setJointState(index, false, glm::quat(), 0.0f); + _rig->clearJointAnimationPriority(index); } } @@ -980,7 +924,7 @@ void MyAvatar::clearJointsData() { void MyAvatar::clearJointAnimationPriorities() { int numStates = _skeletonModel.getJointStateCount(); for (int i = 0; i < numStates; ++i) { - _skeletonModel.clearJointAnimationPriority(i); + _rig->clearJointAnimationPriority(i); } } @@ -1020,19 +964,8 @@ void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { Avatar::setSkeletonModelURL(skeletonModelURL); render::ScenePointer scene = Application::getInstance()->getMain3DScene(); _billboardValid = false; - - if (_useFullAvatar) { - _skeletonModel.setVisibleInScene(_prevShouldDrawHead, scene); - - const QUrl DEFAULT_SKELETON_MODEL_URL = QUrl::fromLocalFile(PathUtils::resourcesPath() + "meshes/defaultAvatar_body.fst"); - _firstPersonSkeletonModel.setURL(_skeletonModelURL, DEFAULT_SKELETON_MODEL_URL, true, !isMyAvatar()); - _firstPersonSkeletonModel.setVisibleInScene(!_prevShouldDrawHead, scene); - } else { - _skeletonModel.setVisibleInScene(true, scene); - - _firstPersonSkeletonModel.setVisibleInScene(false, scene); - _firstPersonSkeletonModel.reset(); - } + _skeletonModel.setVisibleInScene(true, scene); + _headBoneSet.clear(); } void MyAvatar::useFullAvatarURL(const QUrl& fullAvatarURL, const QString& modelName) { @@ -1143,11 +1076,10 @@ glm::vec3 MyAvatar::getSkeletonPosition() const { void MyAvatar::rebuildSkeletonBody() { // compute localAABox - const CapsuleShape& capsule = _skeletonModel.getBoundingShape(); - float radius = capsule.getRadius(); - float height = 2.0f * (capsule.getHalfHeight() + radius); + float radius = _skeletonModel.getBoundingCapsuleRadius(); + float height = _skeletonModel.getBoundingCapsuleHeight() + 2.0f * radius; glm::vec3 corner(-radius, -0.5f * height, -radius); - corner += _skeletonModel.getBoundingShapeOffset(); + corner += _skeletonModel.getBoundingCapsuleOffset(); glm::vec3 scale(2.0f * radius, height, 2.0f * radius); _characterController.setLocalBoundingBox(corner, scale); } @@ -1239,29 +1171,48 @@ void MyAvatar::setVisibleInSceneIfReady(Model* model, render::ScenePointer scene } } +void MyAvatar::initHeadBones() { + int neckJointIndex = -1; + if (_skeletonModel.getGeometry()) { + neckJointIndex = _skeletonModel.getGeometry()->getFBXGeometry().neckJointIndex; + } + if (neckJointIndex == -1) { + return; + } + _headBoneSet.clear(); + std::queue q; + q.push(neckJointIndex); + _headBoneSet.insert(neckJointIndex); + + // fbxJoints only hold links to parents not children, so we have to do a bit of extra work here. + while (q.size() > 0) { + int jointIndex = q.front(); + for (int i = 0; i < _skeletonModel.getJointStateCount(); i++) { + if (jointIndex == _skeletonModel.getParentJointIndex(i)) { + _headBoneSet.insert(i); + q.push(i); + } + } + q.pop(); + } +} + void MyAvatar::preRender(RenderArgs* renderArgs) { render::ScenePointer scene = Application::getInstance()->getMain3DScene(); const bool shouldDrawHead = shouldRenderHead(renderArgs); - _skeletonModel.initWhenReady(scene); - if (_useFullAvatar) { - _firstPersonSkeletonModel.initWhenReady(scene); + if (_skeletonModel.initWhenReady(scene)) { + initHeadBones(); + _skeletonModel.setCauterizeBoneSet(_headBoneSet); } if (shouldDrawHead != _prevShouldDrawHead) { if (_useFullAvatar) { - if (shouldDrawHead) { - _skeletonModel.setVisibleInScene(true, scene); - _firstPersonSkeletonModel.setVisibleInScene(false, scene); - } else { - _skeletonModel.setVisibleInScene(false, scene); - _firstPersonSkeletonModel.setVisibleInScene(true, scene); - } + _skeletonModel.setCauterizeBones(!shouldDrawHead); } else { getHead()->getFaceModel().setVisibleInScene(shouldDrawHead, scene); } - } _prevShouldDrawHead = shouldDrawHead; } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 90d9b47e1c..129a05f93b 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -14,6 +14,7 @@ #include #include +#include #include "Avatar.h" @@ -35,54 +36,43 @@ class MyAvatar : public Avatar { //TODO: make gravity feature work Q_PROPERTY(glm::vec3 gravity READ getGravity WRITE setGravity) public: - MyAvatar(); + MyAvatar(RigPointer rig); ~MyAvatar(); - QByteArray toByteArray(); + void reset(); void update(float deltaTime); - void simulate(float deltaTime); void preRender(RenderArgs* renderArgs); - void updateFromTrackers(float deltaTime); - virtual void render(RenderArgs* renderArgs, const glm::vec3& cameraPositio) override; - virtual void renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, float glowLevel = 0.0f) override; - virtual bool shouldRenderHead(const RenderArgs* renderArgs) const override; - - // setters void setLeanScale(float scale) { _leanScale = scale; } - void setShouldRenderLocally(bool shouldRender) { _shouldRender = shouldRender; } void setRealWorldFieldOfView(float realWorldFov) { _realWorldFieldOfView.set(realWorldFov); } - // getters float getLeanScale() const { return _leanScale; } Q_INVOKABLE glm::vec3 getDefaultEyePosition() const; - bool getShouldRenderLocally() const { return _shouldRender; } + float getRealWorldFieldOfView() { return _realWorldFieldOfView.get(); } - - const QList& getAnimationHandles() const { return _animationHandles; } - AnimationHandlePointer addAnimationHandle(); - void removeAnimationHandle(const AnimationHandlePointer& handle); - + + const QList& getAnimationHandles() const { return _rig->getAnimationHandles(); } + AnimationHandlePointer addAnimationHandle() { return _rig->createAnimationHandle(); } + void removeAnimationHandle(const AnimationHandlePointer& handle) { _rig->removeAnimationHandle(handle); } /// Allows scripts to run animations. Q_INVOKABLE void startAnimation(const QString& url, float fps = 30.0f, float priority = 1.0f, bool loop = false, - bool hold = false, float firstFrame = 0.0f, float lastFrame = FLT_MAX, const QStringList& maskedJoints = QStringList()); - + bool hold = false, float firstFrame = 0.0f, + float lastFrame = FLT_MAX, const QStringList& maskedJoints = QStringList()); /// Stops an animation as identified by a URL. Q_INVOKABLE void stopAnimation(const QString& url); - + /// Starts an animation by its role, using the provided URL and parameters if the avatar doesn't have a custom /// animation for the role. Q_INVOKABLE void startAnimationByRole(const QString& role, const QString& url = QString(), float fps = 30.0f, - float priority = 1.0f, bool loop = false, bool hold = false, float firstFrame = 0.0f, - float lastFrame = FLT_MAX, const QStringList& maskedJoints = QStringList()); - + float priority = 1.0f, bool loop = false, bool hold = false, float firstFrame = 0.0f, + float lastFrame = FLT_MAX, const QStringList& maskedJoints = QStringList()); /// Stops an animation identified by its role. Q_INVOKABLE void stopAnimationByRole(const QString& role); - Q_INVOKABLE AnimationDetails getAnimationDetailsByRole(const QString& role); Q_INVOKABLE AnimationDetails getAnimationDetails(const QString& url); - + void clearJointAnimationPriorities(); + // get/set avatar data void saveData(); void loadData(); @@ -93,32 +83,27 @@ public: // Set what driving keys are being pressed to control thrust levels void clearDriveKeys(); void setDriveKeys(int key, float val) { _driveKeys[key] = val; }; - bool getDriveKeys(int key) { return _driveKeys[key] != 0.0f; }; - void relayDriveKeysToCharacterController(); - bool isMyAvatar() const { return true; } - eyeContactTarget getEyeContactTarget(); - virtual int parseDataFromBuffer(const QByteArray& buffer); - static void sendKillAvatar(); - + Q_INVOKABLE glm::vec3 getTrackedHeadPosition() const { return _trackedHeadPosition; } Q_INVOKABLE glm::vec3 getHeadPosition() const { return getHead()->getPosition(); } Q_INVOKABLE float getHeadFinalYaw() const { return getHead()->getFinalYaw(); } Q_INVOKABLE float getHeadFinalRoll() const { return getHead()->getFinalRoll(); } Q_INVOKABLE float getHeadFinalPitch() const { return getHead()->getFinalPitch(); } Q_INVOKABLE float getHeadDeltaPitch() const { return getHead()->getDeltaPitch(); } - + Q_INVOKABLE glm::vec3 getEyePosition() const { return getHead()->getEyePosition(); } - + Q_INVOKABLE glm::vec3 getTargetAvatarPosition() const { return _targetAvatarPosition; } + AvatarWeakPointer getLookAtTargetAvatar() const { return _lookAtTargetAvatar; } void updateLookAtTargetAvatar(); void clearLookAtTargetAvatar(); - + virtual void setJointRotations(QVector jointRotations); virtual void setJointData(int index, const glm::quat& rotation); virtual void clearJointData(int index); @@ -127,7 +112,8 @@ public: Q_INVOKABLE void useFullAvatarURL(const QUrl& fullAvatarURL, const QString& modelName = QString()); Q_INVOKABLE void useHeadURL(const QUrl& headURL, const QString& modelName = QString()); Q_INVOKABLE void useBodyURL(const QUrl& bodyURL, const QString& modelName = QString()); - Q_INVOKABLE void useHeadAndBodyURLs(const QUrl& headURL, const QUrl& bodyURL, const QString& headName = QString(), const QString& bodyName = QString()); + Q_INVOKABLE void useHeadAndBodyURLs(const QUrl& headURL, const QUrl& bodyURL, + const QString& headName = QString(), const QString& bodyName = QString()); Q_INVOKABLE bool getUseFullAvatar() const { return _useFullAvatar; } Q_INVOKABLE const QUrl& getFullAvatarURLFromPreferences() const { return _fullAvatarURLFromPreferences; } @@ -142,48 +128,30 @@ public: virtual void setAttachmentData(const QVector& attachmentData); - virtual glm::vec3 getSkeletonPosition() const; - void updateLocalAABox(); DynamicCharacterController* getCharacterController() { return &_characterController; } - - void clearJointAnimationPriorities(); - glm::vec3 getScriptedMotorVelocity() const { return _scriptedMotorVelocity; } - float getScriptedMotorTimescale() const { return _scriptedMotorTimescale; } - QString getScriptedMotorFrame() const; - - void setScriptedMotorVelocity(const glm::vec3& velocity); - void setScriptedMotorTimescale(float timescale); - void setScriptedMotorFrame(QString frame); const QString& getCollisionSoundURL() {return _collisionSoundURL; } void setCollisionSoundURL(const QString& url); void clearScriptableSettings(); - virtual void attach(const QString& modelURL, const QString& jointName = QString(), - const glm::vec3& translation = glm::vec3(), const glm::quat& rotation = glm::quat(), float scale = 1.0f, - bool allowDuplicates = false, bool useSaved = true); - /// Renders a laser pointer for UI picking - void renderLaserPointers(gpu::Batch& batch); + glm::vec3 getLaserPointerTipPosition(const PalmData* palm); - - const RecorderPointer getRecorder() const { return _recorder; } - const PlayerPointer getPlayer() const { return _player; } - + float getBoomLength() const { return _boomLength; } void setBoomLength(float boomLength) { _boomLength = boomLength; } - + static const float ZOOM_MIN; static const float ZOOM_MAX; static const float ZOOM_DEFAULT; - + public slots: void increaseSize(); void decreaseSize(); void resetSize(); - + void goToLocation(const glm::vec3& newPosition, bool hasOrientation = false, const glm::quat& newOrientation = glm::quat(), bool shouldFaceLocation = false); @@ -203,7 +171,7 @@ public slots: void clearReferential(); bool setModelReferential(const QUuid& id); bool setJointReferential(const QUuid& id, int jointIndex); - + bool isRecording(); qint64 recorderElapsed(); void startRecording(); @@ -212,7 +180,7 @@ public slots: void loadLastRecording(); virtual void rebuildSkeletonBody(); - + signals: void transformChanged(); void newCollisionSoundURL(const QUrl& url); @@ -220,6 +188,33 @@ signals: private: + QByteArray toByteArray(); + void simulate(float deltaTime); + void updateFromTrackers(float deltaTime); + virtual void render(RenderArgs* renderArgs, const glm::vec3& cameraPositio) override; + virtual void renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, float glowLevel = 0.0f) override; + virtual bool shouldRenderHead(const RenderArgs* renderArgs) const override; + void setShouldRenderLocally(bool shouldRender) { _shouldRender = shouldRender; } + bool getShouldRenderLocally() const { return _shouldRender; } + bool getDriveKeys(int key) { return _driveKeys[key] != 0.0f; }; + bool isMyAvatar() const { return true; } + virtual int parseDataFromBuffer(const QByteArray& buffer); + virtual glm::vec3 getSkeletonPosition() const; + + glm::vec3 getScriptedMotorVelocity() const { return _scriptedMotorVelocity; } + float getScriptedMotorTimescale() const { return _scriptedMotorTimescale; } + QString getScriptedMotorFrame() const; + void setScriptedMotorVelocity(const glm::vec3& velocity); + void setScriptedMotorTimescale(float timescale); + void setScriptedMotorFrame(QString frame); + virtual void attach(const QString& modelURL, const QString& jointName = QString(), + const glm::vec3& translation = glm::vec3(), const glm::quat& rotation = glm::quat(), float scale = 1.0f, + bool allowDuplicates = false, bool useSaved = true); + + void renderLaserPointers(gpu::Batch& batch); + const RecorderPointer getRecorder() const { return _recorder; } + const PlayerPointer getPlayer() const { return _player; } + bool cameraInsideHead() const; // These are made private for MyAvatar so that you will use the "use" methods instead @@ -234,7 +229,7 @@ private: bool _wasPushing; bool _isPushing; bool _isBraking; - + float _boomLength; float _trapDuration; // seconds that avatar has been trapped by collisions @@ -256,37 +251,36 @@ private: bool _billboardValid; float _oculusYawOffset; - QList _animationHandles; - eyeContactTarget _eyeContactTarget; RecorderPointer _recorder; - + glm::vec3 _trackedHeadPosition; - + Setting::Handle _realWorldFieldOfView; - - // private methods + + // private methods void updateOrientation(float deltaTime); glm::vec3 applyKeyboardMotor(float deltaTime, const glm::vec3& velocity, bool isHovering); glm::vec3 applyScriptedMotor(float deltaTime, const glm::vec3& velocity); void updatePosition(float deltaTime); void updateCollisionSound(const glm::vec3& penetration, float deltaTime, float frequency); void maybeUpdateBillboard(); - + void initHeadBones(); + // Avatar Preferences bool _useFullAvatar = false; QUrl _fullAvatarURLFromPreferences; QUrl _headURLFromPreferences; QUrl _skeletonURLFromPreferences; - + QString _headModelName; QString _bodyModelName; QString _fullAvatarModelName; - // used for rendering when in first person view or when in an HMD. - SkeletonModel _firstPersonSkeletonModel; + RigPointer _rig; bool _prevShouldDrawHead; + std::unordered_set _headBoneSet; }; #endif // hifi_MyAvatar_h diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 5deebca638..1b3298c75d 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -12,9 +12,7 @@ #include #include -#include #include -#include #include "Application.h" #include "Avatar.h" @@ -24,29 +22,31 @@ #include "Util.h" #include "InterfaceLogging.h" -SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent) : - Model(parent), +SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent, RigPointer rig) : + Model(rig, parent), _triangleFanID(DependencyManager::get()->allocateID()), _owningAvatar(owningAvatar), - _boundingShape(), - _boundingShapeLocalOffset(0.0f), + _boundingCapsuleLocalOffset(0.0f), + _boundingCapsuleRadius(0.0f), + _boundingCapsuleHeight(0.0f), _defaultEyeModelPosition(glm::vec3(0.0f, 0.0f, 0.0f)), - _headClipDistance(DEFAULT_NEAR_CLIP), - _isFirstPerson(false) + _headClipDistance(DEFAULT_NEAR_CLIP) { + assert(_rig); assert(_owningAvatar); - _enableShapes = true; } SkeletonModel::~SkeletonModel() { } void SkeletonModel::initJointStates(QVector states) { - Model::initJointStates(states); + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; + _boundingRadius = _rig->initJointStates(states, parentTransform); // Determine the default eye position for avatar scale = 1.0 int headJointIndex = _geometry->getFBXGeometry().headJointIndex; - if (0 <= headJointIndex && headJointIndex < _jointStates.size()) { + if (0 <= headJointIndex && headJointIndex < _rig->getJointStateCount()) { glm::vec3 leftEyePosition, rightEyePosition; getEyeModelPositions(leftEyePosition, rightEyePosition); @@ -66,14 +66,11 @@ void SkeletonModel::initJointStates(QVector states) { // the SkeletonModel override of updateJointState() will clear the translation part // of its root joint and we need that done before we try to build shapes hence we // recompute all joint transforms at this time. - for (int i = 0; i < _jointStates.size(); i++) { - updateJointState(i); + for (int i = 0; i < _rig->getJointStateCount(); i++) { + _rig->updateJointState(i, parentTransform); } - clearShapes(); - if (_enableShapes) { - buildShapes(); - } + buildShapes(); Extents meshExtents = getMeshExtents(); _headClipDistance = -(meshExtents.minimum.z / _scale.z - _defaultEyeModelPosition.z); @@ -86,6 +83,29 @@ void SkeletonModel::initJointStates(QVector states) { const float PALM_PRIORITY = DEFAULT_PRIORITY; const float LEAN_PRIORITY = DEFAULT_PRIORITY; +void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { + _rig->computeMotionAnimationState(deltaTime, _owningAvatar->getPosition(), _owningAvatar->getVelocity(), _owningAvatar->getOrientation()); + Model::updateRig(deltaTime, parentTransform); + if (_owningAvatar->isMyAvatar()) { + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + + Rig::HeadParameters params; + params.leanSideways = _owningAvatar->getHead()->getFinalLeanSideways(); + params.leanForward = _owningAvatar->getHead()->getFinalLeanSideways(); + params.torsoTwist = _owningAvatar->getHead()->getTorsoTwist(); + params.localHeadOrientation = _owningAvatar->getHead()->getFinalOrientationInLocalFrame(); + params.worldHeadOrientation = _owningAvatar->getHead()->getFinalOrientationInWorldFrame(); + params.eyeLookAt = _owningAvatar->getHead()->getCorrectedLookAtPosition(); + params.eyeSaccade = _owningAvatar->getHead()->getSaccade(); + params.leanJointIndex = geometry.leanJointIndex; + params.neckJointIndex = geometry.neckJointIndex; + params.leftEyeJointIndex = geometry.leftEyeJointIndex; + params.rightEyeJointIndex = geometry.rightEyeJointIndex; + + _rig->updateFromHeadParameters(params); + } +} + void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { setTranslation(_owningAvatar->getSkeletonPosition()); static const glm::quat refOrientation = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)); @@ -141,49 +161,6 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { restoreRightHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY); } } - - if (_isFirstPerson) { - cauterizeHead(); - updateClusterMatrices(); - } - - _boundingShape.setTranslation(_translation + _rotation * _boundingShapeLocalOffset); - _boundingShape.setRotation(_rotation); -} - -void SkeletonModel::getHandShapes(int jointIndex, QVector& shapes) const { - if (jointIndex < 0 || jointIndex >= int(_shapes.size())) { - return; - } - if (jointIndex == getLeftHandJointIndex() - || jointIndex == getRightHandJointIndex()) { - // get all shapes that have this hand as an ancestor in the skeleton heirarchy - const FBXGeometry& geometry = _geometry->getFBXGeometry(); - for (int i = 0; i < _jointStates.size(); i++) { - const FBXJoint& joint = geometry.joints[i]; - int parentIndex = joint.parentIndex; - Shape* shape = _shapes[i]; - if (i == jointIndex) { - // this shape is the hand - if (shape) { - shapes.push_back(shape); - } - if (parentIndex != -1 && _shapes[parentIndex]) { - // also add the forearm - shapes.push_back(_shapes[parentIndex]); - } - } else if (shape) { - while (parentIndex != -1) { - if (parentIndex == jointIndex) { - // this shape is a child of the hand - shapes.push_back(shape); - break; - } - parentIndex = geometry.joints[parentIndex].parentIndex; - } - } - } - } } void SkeletonModel::renderIKConstraints(gpu::Batch& batch) { @@ -202,7 +179,7 @@ bool operator<(const IndexValue& firstIndex, const IndexValue& secondIndex) { } void SkeletonModel::applyHandPosition(int jointIndex, const glm::vec3& position) { - if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + if (jointIndex == -1 || jointIndex >= _rig->getJointStateCount()) { return; } // NOTE: 'position' is in model-frame @@ -217,16 +194,20 @@ void SkeletonModel::applyHandPosition(int jointIndex, const glm::vec3& position) if (forearmLength < EPSILON) { return; } - JointState& state = _jointStates[jointIndex]; - glm::quat handRotation = state.getRotation(); + glm::quat handRotation; + if (!_rig->getJointStateRotation(jointIndex, handRotation)) { + return; + } // align hand with forearm float sign = (jointIndex == geometry.rightHandJointIndex) ? 1.0f : -1.0f; - state.applyRotationDelta(rotationBetween(handRotation * glm::vec3(-sign, 0.0f, 0.0f), forearmVector), true, PALM_PRIORITY); + _rig->applyJointRotationDelta(jointIndex, + rotationBetween(handRotation * glm::vec3(-sign, 0.0f, 0.0f), forearmVector), + true, PALM_PRIORITY); } void SkeletonModel::applyPalmData(int jointIndex, PalmData& palm) { - if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + if (jointIndex == -1 || jointIndex >= _rig->getJointStateCount()) { return; } const FBXGeometry& geometry = _geometry->getFBXGeometry(); @@ -252,68 +233,16 @@ void SkeletonModel::applyPalmData(int jointIndex, PalmData& palm) { glm::vec3 forearm = palmRotation * glm::vec3(sign * forearmLength, 0.0f, 0.0f); setJointPosition(parentJointIndex, palmPosition + forearm, glm::quat(), false, -1, false, glm::vec3(0.0f, -1.0f, 0.0f), PALM_PRIORITY); - JointState& parentState = _jointStates[parentJointIndex]; - parentState.setRotationInBindFrame(palmRotation, PALM_PRIORITY); + _rig->setJointRotationInBindFrame(parentJointIndex, palmRotation, PALM_PRIORITY); // lock hand to forearm by slamming its rotation (in parent-frame) to identity - _jointStates[jointIndex].setRotationInConstrainedFrame(glm::quat(), PALM_PRIORITY); + _rig->setJointRotationInConstrainedFrame(jointIndex, glm::quat(), PALM_PRIORITY); } else { inverseKinematics(jointIndex, palmPosition, palmRotation, PALM_PRIORITY); } } -void SkeletonModel::updateJointState(int index) { - if (index < 0 && index >= _jointStates.size()) { - return; // bail - } - JointState& state = _jointStates[index]; - const FBXJoint& joint = state.getFBXJoint(); - if (joint.parentIndex >= 0 && joint.parentIndex < _jointStates.size()) { - const JointState& parentState = _jointStates.at(joint.parentIndex); - const FBXGeometry& geometry = _geometry->getFBXGeometry(); - if (index == geometry.leanJointIndex) { - maybeUpdateLeanRotation(parentState, state); - - } else if (index == geometry.neckJointIndex) { - maybeUpdateNeckRotation(parentState, joint, state); - - } else if (index == geometry.leftEyeJointIndex || index == geometry.rightEyeJointIndex) { - maybeUpdateEyeRotation(parentState, joint, state); - } - } - - Model::updateJointState(index); - - if (index == _geometry->getFBXGeometry().rootJointIndex) { - state.clearTransformTranslation(); - } -} - -void SkeletonModel::maybeUpdateLeanRotation(const JointState& parentState, JointState& state) { - if (!_owningAvatar->isMyAvatar()) { - return; - } - // get the rotation axes in joint space and use them to adjust the rotation - glm::vec3 xAxis(1.0f, 0.0f, 0.0f); - glm::vec3 yAxis(0.0f, 1.0f, 0.0f); - glm::vec3 zAxis(0.0f, 0.0f, 1.0f); - glm::quat inverse = glm::inverse(parentState.getRotation() * state.getDefaultRotationInParentFrame()); - state.setRotationInConstrainedFrame( - glm::angleAxis(- RADIANS_PER_DEGREE * _owningAvatar->getHead()->getFinalLeanSideways(), inverse * zAxis) - * glm::angleAxis(- RADIANS_PER_DEGREE * _owningAvatar->getHead()->getFinalLeanForward(), inverse * xAxis) - * glm::angleAxis(RADIANS_PER_DEGREE * _owningAvatar->getHead()->getTorsoTwist(), inverse * yAxis) - * state.getFBXJoint().rotation, LEAN_PRIORITY); -} - -void SkeletonModel::maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) { - _owningAvatar->getHead()->getFaceModel().maybeUpdateNeckRotation(parentState, joint, state); -} - -void SkeletonModel::maybeUpdateEyeRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) { - _owningAvatar->getHead()->getFaceModel().maybeUpdateEyeRotation(this, parentState, joint, state); -} - void SkeletonModel::renderJointConstraints(gpu::Batch& batch, int jointIndex) { - if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + if (jointIndex == -1 || jointIndex >= _rig->getJointStateCount()) { return; } const FBXGeometry& geometry = _geometry->getFBXGeometry(); @@ -322,9 +251,11 @@ void SkeletonModel::renderJointConstraints(gpu::Batch& batch, int jointIndex) { batch._glLineWidth(3.0f); do { const FBXJoint& joint = geometry.joints.at(jointIndex); - const JointState& jointState = _jointStates.at(jointIndex); + const JointState& jointState = _rig->getJointState(jointIndex); glm::vec3 position = _rotation * jointState.getPosition() + _translation; - glm::quat parentRotation = (joint.parentIndex == -1) ? _rotation : _rotation * _jointStates.at(joint.parentIndex).getRotation(); + glm::quat parentRotation = (joint.parentIndex == -1) ? + _rotation : + _rotation * _rig->getJointState(joint.parentIndex).getRotation(); float fanScale = directionSize * 0.75f; Transform transform = Transform(); @@ -457,14 +388,11 @@ void SkeletonModel::setHandPosition(int jointIndex, const glm::vec3& position, c glm::vec3 forwardVector(rightHand ? -1.0f : 1.0f, 0.0f, 0.0f); glm::quat shoulderRotation = rotationBetween(forwardVector, elbowPosition - shoulderPosition); - JointState& shoulderState = _jointStates[shoulderJointIndex]; - shoulderState.setRotationInBindFrame(shoulderRotation, PALM_PRIORITY); - - JointState& elbowState = _jointStates[elbowJointIndex]; - elbowState.setRotationInBindFrame(rotationBetween(shoulderRotation * forwardVector, wristPosition - elbowPosition) * shoulderRotation, PALM_PRIORITY); - - JointState& handState = _jointStates[jointIndex]; - handState.setRotationInBindFrame(rotation, PALM_PRIORITY); + _rig->setJointRotationInBindFrame(shoulderJointIndex, shoulderRotation, PALM_PRIORITY); + _rig->setJointRotationInBindFrame(elbowJointIndex, + rotationBetween(shoulderRotation * forwardVector, wristPosition - elbowPosition) * + shoulderRotation, PALM_PRIORITY); + _rig->setJointRotationInBindFrame(jointIndex, rotation, PALM_PRIORITY); } bool SkeletonModel::getLeftHandPosition(glm::vec3& position) const { @@ -519,7 +447,7 @@ bool SkeletonModel::getNeckParentRotationFromDefaultOrientation(glm::quat& neckP glm::quat worldFrameRotation; bool success = getJointRotationInWorldFrame(parentIndex, worldFrameRotation); if (success) { - neckParentRotation = worldFrameRotation * _jointStates[parentIndex].getFBXJoint().inverseDefaultRotation; + neckParentRotation = worldFrameRotation * _rig->getJointState(parentIndex).getFBXJoint().inverseDefaultRotation; } return success; } @@ -570,7 +498,7 @@ float VERY_BIG_MASS = 1.0e6f; // virtual void SkeletonModel::buildShapes() { - if (_geometry == NULL || _jointStates.isEmpty()) { + if (_geometry == NULL || _rig->jointStatesEmpty()) { return; } @@ -579,46 +507,12 @@ void SkeletonModel::buildShapes() { // rootJointIndex == -1 if the avatar model has no skeleton return; } - - float uniformScale = extractUniformScale(_scale); - const int numStates = _jointStates.size(); - for (int i = 0; i < numStates; i++) { - JointState& state = _jointStates[i]; - const FBXJoint& joint = state.getFBXJoint(); - float radius = uniformScale * joint.boneRadius; - float halfHeight = 0.5f * uniformScale * joint.distanceToParent; - Shape::Type type = joint.shapeType; - int parentIndex = joint.parentIndex; - if (parentIndex == -1 || radius < EPSILON) { - type = INVALID_SHAPE; - } else if (type == CAPSULE_SHAPE && halfHeight < EPSILON) { - // this shape is forced to be a sphere - type = SPHERE_SHAPE; - } - Shape* shape = NULL; - if (type == SPHERE_SHAPE) { - shape = new SphereShape(radius); - shape->setEntity(this); - } else if (type == CAPSULE_SHAPE) { - assert(parentIndex != -1); - shape = new CapsuleShape(radius, halfHeight); - shape->setEntity(this); - } - if (shape && parentIndex != -1) { - // always disable collisions between joint and its parent - disableCollisions(i, parentIndex); - } - _shapes.push_back(shape); - } - - // This method moves the shapes to their default positions in Model frame. computeBoundingShape(geometry); } void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) { // compute default joint transforms - int numStates = _jointStates.size(); - assert(numStates == _shapes.size()); + int numStates = _rig->getJointStateCount(); QVector transforms; transforms.fill(glm::mat4(), numStates); @@ -628,131 +522,63 @@ void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) { totalExtents.addPoint(glm::vec3(0.0f)); for (int i = 0; i < numStates; i++) { // compute the default transform of this joint - JointState& state = _jointStates[i]; + const JointState& state = _rig->getJointState(i); const FBXJoint& joint = state.getFBXJoint(); int parentIndex = joint.parentIndex; if (parentIndex == -1) { - transforms[i] = _jointStates[i].getTransform(); + 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; } - // Each joint contributes its point to the bounding box + // Each joint contributes a sphere at its position + glm::vec3 axis(joint.boneRadius); glm::vec3 jointPosition = extractTranslation(transforms[i]); - totalExtents.addPoint(jointPosition); - - Shape* shape = _shapes[i]; - if (!shape) { - continue; - } - - // Each joint with a shape contributes to the totalExtents: a box - // that contains the sphere centered at the end of the joint with radius of the bone. - - // TODO: skip hand and arm shapes for bounding box calculation - int type = shape->getType(); - if (type == CAPSULE_SHAPE) { - // add the two furthest surface points of the capsule - CapsuleShape* capsule = static_cast(shape); - float radius = capsule->getRadius(); - glm::vec3 axis(radius); - Extents shapeExtents; - shapeExtents.reset(); - shapeExtents.addPoint(jointPosition + axis); - shapeExtents.addPoint(jointPosition - axis); - totalExtents.addExtents(shapeExtents); - } else if (type == SPHERE_SHAPE) { - float radius = shape->getBoundingRadius(); - glm::vec3 axis(radius); - Extents shapeExtents; - shapeExtents.reset(); - shapeExtents.addPoint(jointPosition + axis); - shapeExtents.addPoint(jointPosition - axis); - totalExtents.addExtents(shapeExtents); - } + totalExtents.addPoint(jointPosition + axis); + totalExtents.addPoint(jointPosition - axis); } // compute bounding shape parameters // NOTE: we assume that the longest side of totalExtents is the yAxis... glm::vec3 diagonal = totalExtents.maximum - totalExtents.minimum; // ... and assume the radius is half the RMS of the X and Z sides: - float capsuleRadius = 0.5f * sqrtf(0.5f * (diagonal.x * diagonal.x + diagonal.z * diagonal.z)); - _boundingShape.setRadius(capsuleRadius); - _boundingShape.setHalfHeight(0.5f * diagonal.y - capsuleRadius); + _boundingCapsuleRadius = 0.5f * sqrtf(0.5f * (diagonal.x * diagonal.x + diagonal.z * diagonal.z)); + _boundingCapsuleHeight = diagonal.y - 2.0f * _boundingCapsuleRadius; - glm::vec3 rootPosition = _jointStates[geometry.rootJointIndex].getPosition(); - _boundingShapeLocalOffset = 0.5f * (totalExtents.maximum + totalExtents.minimum) - rootPosition; + glm::vec3 rootPosition = _rig->getJointState(geometry.rootJointIndex).getPosition(); + _boundingCapsuleLocalOffset = 0.5f * (totalExtents.maximum + totalExtents.minimum) - rootPosition; _boundingRadius = 0.5f * glm::length(diagonal); } -void SkeletonModel::resetShapePositionsToDefaultPose() { - // DEBUG method. - // Moves shapes to the joint default locations for debug visibility into - // how the bounding shape is computed. - - if (!_geometry || _shapes.isEmpty()) { - // geometry or joints have not yet been created - return; - } - - const FBXGeometry& geometry = _geometry->getFBXGeometry(); - if (geometry.joints.isEmpty()) { - return; - } - - // The shapes are moved to their default positions in computeBoundingShape(). - computeBoundingShape(geometry); - - // Then we move them into world frame for rendering at the Model's location. - for (int i = 0; i < _shapes.size(); i++) { - Shape* shape = _shapes[i]; - if (shape) { - shape->setTranslation(_translation + _rotation * shape->getTranslation()); - shape->setRotation(_rotation * shape->getRotation()); - } - } - _boundingShape.setTranslation(_translation + _rotation * _boundingShapeLocalOffset); - _boundingShape.setRotation(_rotation); -} - void SkeletonModel::renderBoundingCollisionShapes(gpu::Batch& batch, float alpha) { const int BALL_SUBDIVISIONS = 10; - if (_shapes.isEmpty()) { - // the bounding shape has not been properly computed - // so no need to render it - return; - } auto geometryCache = DependencyManager::get(); auto deferredLighting = DependencyManager::get(); Transform transform; // = Transform(); - // draw a blue sphere at the capsule end point - glm::vec3 endPoint; - _boundingShape.getEndPoint(endPoint); - endPoint = endPoint + _translation; - transform.setTranslation(endPoint); + // draw a blue sphere at the capsule top point + glm::vec3 topPoint = _translation + _boundingCapsuleLocalOffset + (0.5f * _boundingCapsuleHeight) * glm::vec3(0.0f, 1.0f, 0.0f); + transform.setTranslation(topPoint); batch.setModelTransform(transform); deferredLighting->bindSimpleProgram(batch); - geometryCache->renderSphere(batch, _boundingShape.getRadius(), BALL_SUBDIVISIONS, BALL_SUBDIVISIONS, + geometryCache->renderSphere(batch, _boundingCapsuleRadius, BALL_SUBDIVISIONS, BALL_SUBDIVISIONS, glm::vec4(0.6f, 0.6f, 0.8f, alpha)); - // draw a yellow sphere at the capsule start point - glm::vec3 startPoint; - _boundingShape.getStartPoint(startPoint); - startPoint = startPoint + _translation; - glm::vec3 axis = endPoint - startPoint; - transform.setTranslation(startPoint); + // draw a yellow sphere at the capsule bottom point + glm::vec3 bottomPoint = topPoint - glm::vec3(0.0f, -_boundingCapsuleHeight, 0.0f); + glm::vec3 axis = topPoint - bottomPoint; + transform.setTranslation(bottomPoint); batch.setModelTransform(transform); deferredLighting->bindSimpleProgram(batch); - geometryCache->renderSphere(batch, _boundingShape.getRadius(), BALL_SUBDIVISIONS, BALL_SUBDIVISIONS, + geometryCache->renderSphere(batch, _boundingCapsuleRadius, BALL_SUBDIVISIONS, BALL_SUBDIVISIONS, glm::vec4(0.8f, 0.8f, 0.6f, alpha)); // draw a green cylinder between the two points glm::vec3 origin(0.0f); - Avatar::renderJointConnectingCone(batch, origin, axis, _boundingShape.getRadius(), _boundingShape.getRadius(), + Avatar::renderJointConnectingCone(batch, origin, axis, _boundingCapsuleRadius, _boundingCapsuleRadius, glm::vec4(0.6f, 0.8f, 0.6f, alpha)); } @@ -760,56 +586,5 @@ bool SkeletonModel::hasSkeleton() { return isActive() ? _geometry->getFBXGeometry().rootJointIndex != -1 : false; } -void SkeletonModel::initHeadBones() { - _headBones.clear(); - const FBXGeometry& fbxGeometry = _geometry->getFBXGeometry(); - const int neckJointIndex = fbxGeometry.neckJointIndex; - std::queue q; - q.push(neckJointIndex); - _headBones.push_back(neckJointIndex); - - // fbxJoints only hold links to parents not children, so we have to do a bit of extra work here. - while (q.size() > 0) { - int jointIndex = q.front(); - for (int i = 0; i < fbxGeometry.joints.size(); i++) { - const FBXJoint& fbxJoint = fbxGeometry.joints[i]; - if (jointIndex == fbxJoint.parentIndex) { - _headBones.push_back(i); - q.push(i); - } - } - q.pop(); - } -} - -void SkeletonModel::invalidateHeadBones() { - _headBones.clear(); -} - -void SkeletonModel::cauterizeHead() { - if (isActive()) { - const FBXGeometry& geometry = _geometry->getFBXGeometry(); - const int neckJointIndex = geometry.neckJointIndex; - if (neckJointIndex > 0 && neckJointIndex < _jointStates.size()) { - - // lazy init of headBones - if (_headBones.size() == 0) { - initHeadBones(); - } - - // preserve the translation for the neck - glm::vec4 trans = _jointStates[neckJointIndex].getTransform()[3]; - glm::vec4 zero(0, 0, 0, 0); - for (const int &i : _headBones) { - JointState& joint = _jointStates[i]; - glm::mat4 newXform(zero, zero, zero, trans); - joint.setTransform(newXform); - joint.setVisibleTransform(newXform); - } - } - } -} - void SkeletonModel::onInvalidate() { - invalidateHeadBones(); } diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index 6f4dd096ad..ecc5c80118 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -13,7 +13,6 @@ #define hifi_SkeletonModel_h -#include #include class Avatar; @@ -22,72 +21,69 @@ class MuscleConstraint; /// A skeleton loaded from a model. class SkeletonModel : public Model { Q_OBJECT - + public: - SkeletonModel(Avatar* owningAvatar, QObject* parent = NULL); + SkeletonModel(Avatar* owningAvatar, QObject* parent = nullptr, RigPointer rig = nullptr); ~SkeletonModel(); - + virtual void initJointStates(QVector states); - void simulate(float deltaTime, bool fullUpdate = true); - - /// \param jointIndex index of hand joint - /// \param shapes[out] list in which is stored pointers to hand shapes - void getHandShapes(int jointIndex, QVector& shapes) const; + virtual void simulate(float deltaTime, bool fullUpdate = true); + virtual void updateRig(float deltaTime, glm::mat4 parentTransform); void renderIKConstraints(gpu::Batch& batch); - + /// Returns the index of the left hand joint, or -1 if not found. int getLeftHandJointIndex() const { return isActive() ? _geometry->getFBXGeometry().leftHandJointIndex : -1; } - + /// Returns the index of the right hand joint, or -1 if not found. int getRightHandJointIndex() const { return isActive() ? _geometry->getFBXGeometry().rightHandJointIndex : -1; } /// Retrieve the position of the left hand /// \return true whether or not the position was found bool getLeftHandPosition(glm::vec3& position) const; - + /// Retrieve the position of the right hand /// \return true whether or not the position was found bool getRightHandPosition(glm::vec3& position) const; - + /// Restores some fraction of the default position of the left hand. /// \param fraction the fraction of the default position to restore /// \return whether or not the left hand joint was found bool restoreLeftHandPosition(float fraction = 1.0f, float priority = 1.0f); - + /// Gets the position of the left shoulder. /// \return whether or not the left shoulder joint was found bool getLeftShoulderPosition(glm::vec3& position) const; - + /// Returns the extended length from the left hand to its last free ancestor. float getLeftArmLength() const; - + /// Restores some fraction of the default position of the right hand. /// \param fraction the fraction of the default position to restore /// \return whether or not the right hand joint was found bool restoreRightHandPosition(float fraction = 1.0f, float priority = 1.0f); - + /// Gets the position of the right shoulder. /// \return whether or not the right shoulder joint was found bool getRightShoulderPosition(glm::vec3& position) const; - + /// Returns the extended length from the right hand to its first free ancestor. float getRightArmLength() const; /// Returns the position of the head joint. /// \return whether or not the head was found bool getHeadPosition(glm::vec3& headPosition) const; - + /// Returns the position of the neck joint. /// \return whether or not the neck was found bool getNeckPosition(glm::vec3& neckPosition) const; - + /// Returns the rotation of the neck joint's parent from default orientation /// \return whether or not the neck was found bool getNeckParentRotationFromDefaultOrientation(glm::quat& neckParentRotation) const; - + /// Retrieve the positions of up to two eye meshes. /// \return whether or not both eye meshes were found bool getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const; @@ -98,19 +94,14 @@ public: void computeBoundingShape(const FBXGeometry& geometry); void renderBoundingCollisionShapes(gpu::Batch& batch, float alpha); - float getBoundingShapeRadius() const { return _boundingShape.getRadius(); } - const CapsuleShape& getBoundingShape() const { return _boundingShape; } - const glm::vec3 getBoundingShapeOffset() const { return _boundingShapeLocalOffset; } - - void resetShapePositionsToDefaultPose(); // DEBUG method + float getBoundingCapsuleRadius() const { return _boundingCapsuleRadius; } + float getBoundingCapsuleHeight() const { return _boundingCapsuleHeight; } + const glm::vec3 getBoundingCapsuleOffset() const { return _boundingCapsuleLocalOffset; } bool hasSkeleton(); float getHeadClipDistance() const { return _headClipDistance; } - void setIsFirstPerson(bool value) { _isFirstPerson = value; } - bool getIsFirstPerson() const { return _isFirstPerson; } - virtual void onInvalidate() override; signals: @@ -124,26 +115,14 @@ protected: /// \param jointIndex index of joint in model /// \param position position of joint in model-frame void applyHandPosition(int jointIndex, const glm::vec3& position); - + void applyPalmData(int jointIndex, PalmData& palm); - - /// Updates the state of the joint at the specified index. - virtual void updateJointState(int index); - - void maybeUpdateLeanRotation(const JointState& parentState, JointState& state); - void maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, JointState& state); - void maybeUpdateEyeRotation(const JointState& parentState, const FBXJoint& joint, JointState& state); - - void cauterizeHead(); - void initHeadBones(); - void invalidateHeadBones(); - private: void renderJointConstraints(gpu::Batch& batch, int jointIndex); - void renderOrientationDirections(gpu::Batch& batch, int jointIndex, + void renderOrientationDirections(gpu::Batch& batch, int jointIndex, glm::vec3 position, const glm::quat& orientation, float size); - + struct OrientationLineIDs { int _up; int _front; @@ -158,18 +137,16 @@ private: void setHandPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation); bool getEyeModelPositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const; - + Avatar* _owningAvatar; - CapsuleShape _boundingShape; - glm::vec3 _boundingShapeLocalOffset; + glm::vec3 _boundingCapsuleLocalOffset; + float _boundingCapsuleRadius; + float _boundingCapsuleHeight; glm::vec3 _defaultEyeModelPosition; float _headClipDistance; // Near clip distance to use if no separate head model - - bool _isFirstPerson; - std::vector _headBones; }; #endif // hifi_SkeletonModel_h diff --git a/interface/src/devices/FaceTracker.h b/interface/src/devices/FaceTracker.h index 193262d121..7126d19ca8 100644 --- a/interface/src/devices/FaceTracker.h +++ b/interface/src/devices/FaceTracker.h @@ -47,7 +47,6 @@ public: bool isMuted() const { return _isMuted; } void setIsMuted(bool isMuted) { _isMuted = isMuted; } - void toggleMute(); static float getEyeDeflection() { return _eyeDeflection.get(); } static void setEyeDeflection(float eyeDeflection); @@ -57,6 +56,8 @@ signals: public slots: virtual void setEnabled(bool enabled) = 0; + void toggleMute(); + bool getMuted() { return _isMuted; } protected: virtual ~FaceTracker() {}; diff --git a/interface/src/devices/OculusManager.cpp b/interface/src/devices/OculusManager.cpp index c9c70b4417..16685df96f 100644 --- a/interface/src/devices/OculusManager.cpp +++ b/interface/src/devices/OculusManager.cpp @@ -11,16 +11,16 @@ // #include "OculusManager.h" -#include +#include #include #include +#include #include +#include #include #include -#include -#include #include #include diff --git a/interface/src/devices/TV3DManager.cpp b/interface/src/devices/TV3DManager.cpp index fefaf060bd..5dee5988c1 100644 --- a/interface/src/devices/TV3DManager.cpp +++ b/interface/src/devices/TV3DManager.cpp @@ -9,13 +9,14 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include "TV3DManager.h" + #include #include -#include "gpu/GLBackend.h" -#include "Application.h" +#include -#include "TV3DManager.h" +#include "Application.h" #include "Menu.h" int TV3DManager::_screenWidth = 1; @@ -63,6 +64,7 @@ void TV3DManager::setFrustum(Camera& whichCamera) { } void TV3DManager::configureCamera(Camera& whichCamera, int screenWidth, int screenHeight) { +#ifdef THIS_CURRENTLY_BROKEN_WAITING_FOR_DISPLAY_PLUGINS if (screenHeight == 0) { screenHeight = 1; // prevent divide by 0 } @@ -72,6 +74,7 @@ void TV3DManager::configureCamera(Camera& whichCamera, int screenWidth, int scre setFrustum(whichCamera); glViewport (0, 0, _screenWidth, _screenHeight); // sets drawing viewport +#endif } void TV3DManager::display(RenderArgs* renderArgs, Camera& whichCamera) { diff --git a/interface/src/devices/TV3DManager.h b/interface/src/devices/TV3DManager.h index 330a4ee0ee..96ee79f7d1 100644 --- a/interface/src/devices/TV3DManager.h +++ b/interface/src/devices/TV3DManager.h @@ -17,6 +17,7 @@ #include class Camera; +class RenderArgs; struct eyeFrustum { double left; diff --git a/interface/src/octree/OctreePacketProcessor.cpp b/interface/src/octree/OctreePacketProcessor.cpp index 7bb94323b7..1abbb21089 100644 --- a/interface/src/octree/OctreePacketProcessor.cpp +++ b/interface/src/octree/OctreePacketProcessor.cpp @@ -57,7 +57,7 @@ void OctreePacketProcessor::processPacket(QSharedPointer packet, Share if (piggybackBytes) { // construct a new packet from the piggybacked one - std::unique_ptr buffer = std::unique_ptr(new char[piggybackBytes]); + auto buffer = std::unique_ptr(new char[piggybackBytes]); memcpy(buffer.get(), packet->getPayload() + statsMessageLength, piggybackBytes); auto newPacket = NLPacket::fromReceivedPacket(std::move(buffer), piggybackBytes, packet->getSenderSockAddr()); diff --git a/interface/src/scripting/DialogsManagerScriptingInterface.cpp b/interface/src/scripting/DialogsManagerScriptingInterface.cpp new file mode 100644 index 0000000000..80a8b4ac7c --- /dev/null +++ b/interface/src/scripting/DialogsManagerScriptingInterface.cpp @@ -0,0 +1,26 @@ +// +// DialogsManagerScriptingInterface.cpp +// interface/src/scripting +// +// Created by Zander Otavka on 7/17/15. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "DialogsManagerScriptingInterface.h" + +#include + +#include "ui/DialogsManager.h" + +DialogsManagerScriptingInterface::DialogsManagerScriptingInterface() { + connect(DependencyManager::get().data(), &DialogsManager::addressBarToggled, + this, &DialogsManagerScriptingInterface::addressBarToggled); +} + +void DialogsManagerScriptingInterface::toggleAddressBar() { + QMetaObject::invokeMethod(DependencyManager::get().data(), + "toggleAddressBar", Qt::QueuedConnection); +} diff --git a/interface/src/scripting/DialogsManagerScriptingInterface.h b/interface/src/scripting/DialogsManagerScriptingInterface.h new file mode 100644 index 0000000000..ef44e20d61 --- /dev/null +++ b/interface/src/scripting/DialogsManagerScriptingInterface.h @@ -0,0 +1,29 @@ +// +// DialogsManagerScriptingInterface.h +// interface/src/scripting +// +// Created by Zander Otavka on 7/17/15. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_DialogsManagerScriptInterface_h +#define hifi_DialogsManagerScriptInterface_h + +#include + +class DialogsManagerScriptingInterface : public QObject { + Q_OBJECT +public: + DialogsManagerScriptingInterface(); + +public slots: + void toggleAddressBar(); + +signals: + void addressBarToggled(); +}; + +#endif diff --git a/interface/src/ui/AnimationsDialog.cpp b/interface/src/ui/AnimationsDialog.cpp index 0428e79e6f..5960ffc1fa 100644 --- a/interface/src/ui/AnimationsDialog.cpp +++ b/interface/src/ui/AnimationsDialog.cpp @@ -156,7 +156,7 @@ AnimationPanel::AnimationPanel(AnimationsDialog* dialog, const AnimationHandlePo buttons->addWidget(remove); connect(remove, SIGNAL(clicked(bool)), SLOT(removeHandle())); - _stop->connect(_handle.data(), SIGNAL(runningChanged(bool)), SLOT(setEnabled(bool))); + _stop->connect(_handle.get(), SIGNAL(runningChanged(bool)), SLOT(setEnabled(bool))); _stop->setEnabled(_handle->isRunning()); } diff --git a/interface/src/ui/DialogsManager.cpp b/interface/src/ui/DialogsManager.cpp index 04f532c59a..ac5e6833fb 100644 --- a/interface/src/ui/DialogsManager.cpp +++ b/interface/src/ui/DialogsManager.cpp @@ -36,6 +36,7 @@ void DialogsManager::toggleAddressBar() { AddressBarDialog::toggle(); + emit addressBarToggled(); } void DialogsManager::toggleDiskCacheEditor() { diff --git a/interface/src/ui/DialogsManager.h b/interface/src/ui/DialogsManager.h index b15830e35c..09e0274d86 100644 --- a/interface/src/ui/DialogsManager.h +++ b/interface/src/ui/DialogsManager.h @@ -72,6 +72,9 @@ public slots: // Application Update void showUpdateDialog(); +signals: + void addressBarToggled(); + private slots: void toggleToolWindow(); void hmdToolsClosed(); diff --git a/interface/src/ui/overlays/BillboardOverlay.cpp b/interface/src/ui/overlays/BillboardOverlay.cpp index d26ecc5c67..3bd1f56ba3 100644 --- a/interface/src/ui/overlays/BillboardOverlay.cpp +++ b/interface/src/ui/overlays/BillboardOverlay.cpp @@ -13,19 +13,25 @@ #include +#include #include #include #include +#include #include "Application.h" #include "GeometryUtil.h" + +QString const BillboardOverlay::TYPE = "billboard"; + BillboardOverlay::BillboardOverlay() { _isLoaded = false; } BillboardOverlay::BillboardOverlay(const BillboardOverlay* billboardOverlay) : Planar3DOverlay(billboardOverlay), + PanelAttachable(billboardOverlay), _url(billboardOverlay->_url), _texture(billboardOverlay->_texture), _fromImage(billboardOverlay->_fromImage), @@ -33,6 +39,19 @@ BillboardOverlay::BillboardOverlay(const BillboardOverlay* billboardOverlay) : { } +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 BillboardOverlay::update(float deltatime) { + setTransforms(_transform); +} + void BillboardOverlay::render(RenderArgs* args) { if (!_texture) { _isLoaded = true; @@ -43,15 +62,8 @@ void BillboardOverlay::render(RenderArgs* args) { return; } - glm::quat rotation; - if (_isFacingAvatar) { - // rotate about vertical to face the camera - rotation = args->_viewFrustum->getOrientation(); - rotation *= glm::angleAxis(glm::pi(), IDENTITY_UP); - rotation *= getRotation(); - } else { - rotation = getRotation(); - } + Q_ASSERT(args->_batch); + auto batch = args->_batch; float imageWidth = _texture->getWidth(); float imageHeight = _texture->getHeight(); @@ -86,25 +98,25 @@ void BillboardOverlay::render(RenderArgs* args) { xColor color = getColor(); float alpha = getAlpha(); - auto batch = args->_batch; + setTransforms(_transform); + Transform transform = _transform; + transform.postScale(glm::vec3(getDimensions(), 1.0f)); - if (batch) { - Transform transform = _transform; - transform.postScale(glm::vec3(getDimensions(), 1.0f)); - transform.setRotation(rotation); - - batch->setModelTransform(transform); - batch->setResourceTexture(0, _texture->getGPUTexture()); - - DependencyManager::get()->renderQuad(*batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, - glm::vec4(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha)); + batch->setModelTransform(transform); + batch->setResourceTexture(0, _texture->getGPUTexture()); - batch->setResourceTexture(0, args->_whiteTexture); // restore default white color after me - } + DependencyManager::get()->bindSimpleProgram(*batch, true, true, false, true); + DependencyManager::get()->renderQuad( + *batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, + glm::vec4(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha) + ); + + batch->setResourceTexture(0, args->_whiteTexture); // restore default white color after me } void BillboardOverlay::setProperties(const QScriptValue &properties) { Planar3DOverlay::setProperties(properties); + PanelAttachable::setProperties(properties); QScriptValue urlValue = properties.property("url"); if (urlValue.isValid()) { @@ -161,7 +173,14 @@ QScriptValue BillboardOverlay::getProperty(const QString& property) { 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); } @@ -175,15 +194,10 @@ void BillboardOverlay::setBillboardURL(const QString& url) { } bool BillboardOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - float& distance, BoxFace& face) { - + float& distance, BoxFace& face) { if (_texture && _texture->isLoaded()) { - glm::quat rotation = getRotation(); - if (_isFacingAvatar) { - // rotate about vertical to face the camera - rotation = Application::getInstance()->getCamera()->getRotation(); - rotation *= glm::angleAxis(glm::pi(), glm::vec3(0.0f, 1.0f, 0.0f)); - } + // Make sure position and rotation is updated. + setTransforms(_transform); // Produce the dimensions of the billboard based on the image's aspect ratio and the overlay's scale. bool isNull = _fromImage.isNull(); @@ -192,7 +206,7 @@ bool BillboardOverlay::findRayIntersection(const glm::vec3& origin, const glm::v float maxSize = glm::max(width, height); glm::vec2 dimensions = _dimensions * glm::vec2(width / maxSize, height / maxSize); - return findRayRectangleIntersection(origin, direction, rotation, getPosition(), dimensions, distance); + return findRayRectangleIntersection(origin, direction, getRotation(), getPosition(), dimensions, distance); } return false; diff --git a/interface/src/ui/overlays/BillboardOverlay.h b/interface/src/ui/overlays/BillboardOverlay.h index 15be0419a9..a034612e71 100644 --- a/interface/src/ui/overlays/BillboardOverlay.h +++ b/interface/src/ui/overlays/BillboardOverlay.h @@ -15,15 +15,21 @@ #include #include "Planar3DOverlay.h" +#include "PanelAttachable.h" -class BillboardOverlay : public Planar3DOverlay { +class BillboardOverlay : public Planar3DOverlay, public PanelAttachable { Q_OBJECT public: + static QString const TYPE; + virtual QString getType() const { return TYPE; } + BillboardOverlay(); BillboardOverlay(const BillboardOverlay* billboardOverlay); virtual void render(RenderArgs* args); + virtual void update(float deltatime); + // setters void setURL(const QString& url); void setIsFacingAvatar(bool isFacingAvatar) { _isFacingAvatar = isFacingAvatar; } @@ -36,9 +42,12 @@ public: virtual BillboardOverlay* createClone() const; +protected: + virtual void setTransforms(Transform& transform); + private: void setBillboardURL(const QString& url); - + QString _url; NetworkTexturePointer _texture; diff --git a/interface/src/ui/overlays/Circle3DOverlay.cpp b/interface/src/ui/overlays/Circle3DOverlay.cpp index 53f1b4ce21..4e80ac5b63 100644 --- a/interface/src/ui/overlays/Circle3DOverlay.cpp +++ b/interface/src/ui/overlays/Circle3DOverlay.cpp @@ -15,6 +15,8 @@ #include +QString const Circle3DOverlay::TYPE = "circle3d"; + Circle3DOverlay::Circle3DOverlay() : _startAt(0.0f), _endAt(360.0f), @@ -119,19 +121,21 @@ void Circle3DOverlay::render(RenderArgs* args) { float angle = startAt; float angleInRadians = glm::radians(angle); - glm::vec2 firstInnerPoint(cosf(angleInRadians) * innerRadius, sinf(angleInRadians) * innerRadius); - glm::vec2 firstOuterPoint(cosf(angleInRadians) * outerRadius, sinf(angleInRadians) * outerRadius); - - points << firstInnerPoint << firstOuterPoint; + glm::vec2 mostRecentInnerPoint(cosf(angleInRadians) * innerRadius, sinf(angleInRadians) * innerRadius); + glm::vec2 mostRecentOuterPoint(cosf(angleInRadians) * outerRadius, sinf(angleInRadians) * outerRadius); while (angle < endAt) { angleInRadians = glm::radians(angle); glm::vec2 thisInnerPoint(cosf(angleInRadians) * innerRadius, sinf(angleInRadians) * innerRadius); glm::vec2 thisOuterPoint(cosf(angleInRadians) * outerRadius, sinf(angleInRadians) * outerRadius); - points << thisOuterPoint << thisInnerPoint; + points << mostRecentInnerPoint << mostRecentOuterPoint << thisOuterPoint; // first triangle + points << mostRecentInnerPoint << thisInnerPoint << thisOuterPoint; // second triangle angle += SLICE_ANGLE; + + mostRecentInnerPoint = thisInnerPoint; + mostRecentOuterPoint = thisOuterPoint; } // get the last slice portion.... @@ -139,13 +143,14 @@ void Circle3DOverlay::render(RenderArgs* args) { angleInRadians = glm::radians(angle); glm::vec2 lastInnerPoint(cosf(angleInRadians) * innerRadius, sinf(angleInRadians) * innerRadius); glm::vec2 lastOuterPoint(cosf(angleInRadians) * outerRadius, sinf(angleInRadians) * outerRadius); - - points << lastOuterPoint << lastInnerPoint; + + points << mostRecentInnerPoint << mostRecentOuterPoint << lastOuterPoint; // first triangle + points << mostRecentInnerPoint << lastInnerPoint << lastOuterPoint; // second triangle geometryCache->updateVertices(_quadVerticesID, points, color); } - geometryCache->renderVertices(batch, gpu::QUAD_STRIP, _quadVerticesID); + geometryCache->renderVertices(batch, gpu::TRIANGLES, _quadVerticesID); } else { if (_lineVerticesID == GeometryCache::UNKNOWN_ID) { diff --git a/interface/src/ui/overlays/Circle3DOverlay.h b/interface/src/ui/overlays/Circle3DOverlay.h index d83703fcd0..5879fe1701 100644 --- a/interface/src/ui/overlays/Circle3DOverlay.h +++ b/interface/src/ui/overlays/Circle3DOverlay.h @@ -18,6 +18,9 @@ class Circle3DOverlay : public Planar3DOverlay { Q_OBJECT public: + static QString const TYPE; + virtual QString getType() const { return TYPE; } + Circle3DOverlay(); Circle3DOverlay(const Circle3DOverlay* circle3DOverlay); diff --git a/interface/src/ui/overlays/Cube3DOverlay.cpp b/interface/src/ui/overlays/Cube3DOverlay.cpp index 200a1a328f..a306c7c86d 100644 --- a/interface/src/ui/overlays/Cube3DOverlay.cpp +++ b/interface/src/ui/overlays/Cube3DOverlay.cpp @@ -19,6 +19,8 @@ #include #include +QString const Cube3DOverlay::TYPE = "cube"; + Cube3DOverlay::Cube3DOverlay(const Cube3DOverlay* cube3DOverlay) : Volume3DOverlay(cube3DOverlay) { diff --git a/interface/src/ui/overlays/Cube3DOverlay.h b/interface/src/ui/overlays/Cube3DOverlay.h index 397ad77a9e..6f9026a091 100644 --- a/interface/src/ui/overlays/Cube3DOverlay.h +++ b/interface/src/ui/overlays/Cube3DOverlay.h @@ -17,6 +17,9 @@ class Cube3DOverlay : public Volume3DOverlay { Q_OBJECT public: + static QString const TYPE; + virtual QString getType() const { return TYPE; } + Cube3DOverlay() {} Cube3DOverlay(const Cube3DOverlay* cube3DOverlay); diff --git a/interface/src/ui/overlays/FloatingUIPanel.cpp b/interface/src/ui/overlays/FloatingUIPanel.cpp new file mode 100644 index 0000000000..eedb4bffd1 --- /dev/null +++ b/interface/src/ui/overlays/FloatingUIPanel.cpp @@ -0,0 +1,197 @@ +// +// 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 new file mode 100644 index 0000000000..f84ac32fac --- /dev/null +++ b/interface/src/ui/overlays/FloatingUIPanel.h @@ -0,0 +1,63 @@ +// +// 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/Grid3DOverlay.cpp b/interface/src/ui/overlays/Grid3DOverlay.cpp index 956eae35ff..074ad3d17f 100644 --- a/interface/src/ui/overlays/Grid3DOverlay.cpp +++ b/interface/src/ui/overlays/Grid3DOverlay.cpp @@ -18,6 +18,9 @@ #include #include + +QString const Grid3DOverlay::TYPE = "grid"; + Grid3DOverlay::Grid3DOverlay() : _minorGridWidth(1.0), _majorGridEvery(5) { diff --git a/interface/src/ui/overlays/Grid3DOverlay.h b/interface/src/ui/overlays/Grid3DOverlay.h index 80ec4b84b9..5fb3852905 100644 --- a/interface/src/ui/overlays/Grid3DOverlay.h +++ b/interface/src/ui/overlays/Grid3DOverlay.h @@ -18,6 +18,9 @@ class Grid3DOverlay : public Planar3DOverlay { Q_OBJECT public: + static QString const TYPE; + virtual QString getType() const { return TYPE; } + Grid3DOverlay(); Grid3DOverlay(const Grid3DOverlay* grid3DOverlay); diff --git a/interface/src/ui/overlays/ImageOverlay.cpp b/interface/src/ui/overlays/ImageOverlay.cpp index cad9010531..ed88f50981 100644 --- a/interface/src/ui/overlays/ImageOverlay.cpp +++ b/interface/src/ui/overlays/ImageOverlay.cpp @@ -16,6 +16,9 @@ #include #include + +QString const ImageOverlay::TYPE = "image"; + ImageOverlay::ImageOverlay() : _imageURL(), _renderImage(false), diff --git a/interface/src/ui/overlays/ImageOverlay.h b/interface/src/ui/overlays/ImageOverlay.h index 59d4102933..5698a73732 100644 --- a/interface/src/ui/overlays/ImageOverlay.h +++ b/interface/src/ui/overlays/ImageOverlay.h @@ -24,6 +24,9 @@ class ImageOverlay : public Overlay2D { Q_OBJECT public: + static QString const TYPE; + virtual QString getType() const { return TYPE; } + ImageOverlay(); ImageOverlay(const ImageOverlay* imageOverlay); diff --git a/interface/src/ui/overlays/Line3DOverlay.cpp b/interface/src/ui/overlays/Line3DOverlay.cpp index d026e5f56e..0acd7ecc1e 100644 --- a/interface/src/ui/overlays/Line3DOverlay.cpp +++ b/interface/src/ui/overlays/Line3DOverlay.cpp @@ -13,6 +13,9 @@ #include #include + +QString const Line3DOverlay::TYPE = "line3d"; + Line3DOverlay::Line3DOverlay() : _geometryCacheID(DependencyManager::get()->allocateID()) { diff --git a/interface/src/ui/overlays/Line3DOverlay.h b/interface/src/ui/overlays/Line3DOverlay.h index 4a4d8f4d90..05812709dc 100644 --- a/interface/src/ui/overlays/Line3DOverlay.h +++ b/interface/src/ui/overlays/Line3DOverlay.h @@ -17,6 +17,9 @@ class Line3DOverlay : public Base3DOverlay { Q_OBJECT public: + static QString const TYPE; + virtual QString getType() const { return TYPE; } + Line3DOverlay(); Line3DOverlay(const Line3DOverlay* line3DOverlay); ~Line3DOverlay(); diff --git a/interface/src/ui/overlays/LocalModelsOverlay.cpp b/interface/src/ui/overlays/LocalModelsOverlay.cpp index 3f701cbbe6..38e11562da 100644 --- a/interface/src/ui/overlays/LocalModelsOverlay.cpp +++ b/interface/src/ui/overlays/LocalModelsOverlay.cpp @@ -14,6 +14,9 @@ #include #include + +QString const LocalModelsOverlay::TYPE = "localmodels"; + LocalModelsOverlay::LocalModelsOverlay(EntityTreeRenderer* entityTreeRenderer) : Volume3DOverlay(), _entityTreeRenderer(entityTreeRenderer) { diff --git a/interface/src/ui/overlays/LocalModelsOverlay.h b/interface/src/ui/overlays/LocalModelsOverlay.h index c311b2bc1b..011f3dfb11 100644 --- a/interface/src/ui/overlays/LocalModelsOverlay.h +++ b/interface/src/ui/overlays/LocalModelsOverlay.h @@ -19,6 +19,9 @@ class EntityTreeRenderer; class LocalModelsOverlay : public Volume3DOverlay { Q_OBJECT public: + static QString const TYPE; + virtual QString getType() const { return TYPE; } + LocalModelsOverlay(EntityTreeRenderer* entityTreeRenderer); LocalModelsOverlay(const LocalModelsOverlay* localModelsOverlay); diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index ed15e57d43..de8e05323b 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -13,8 +13,11 @@ #include "Application.h" + +QString const ModelOverlay::TYPE = "model"; + ModelOverlay::ModelOverlay() - : _model(), + : _model(nullptr), _modelTextures(QVariantMap()), _updateModel(false) { @@ -24,7 +27,7 @@ ModelOverlay::ModelOverlay() ModelOverlay::ModelOverlay(const ModelOverlay* modelOverlay) : Volume3DOverlay(modelOverlay), - _model(), + _model(nullptr), _modelTextures(QVariantMap()), _url(modelOverlay->_url), _updateModel(false) diff --git a/interface/src/ui/overlays/ModelOverlay.h b/interface/src/ui/overlays/ModelOverlay.h index 1c43f42909..97ecfb642c 100644 --- a/interface/src/ui/overlays/ModelOverlay.h +++ b/interface/src/ui/overlays/ModelOverlay.h @@ -19,6 +19,9 @@ class ModelOverlay : public Volume3DOverlay { Q_OBJECT public: + static QString const TYPE; + virtual QString getType() const { return TYPE; } + ModelOverlay(); ModelOverlay(const ModelOverlay* modelOverlay); diff --git a/interface/src/ui/overlays/Overlay.cpp b/interface/src/ui/overlays/Overlay.cpp index 17e4ae74e1..7824c0c498 100644 --- a/interface/src/ui/overlays/Overlay.cpp +++ b/interface/src/ui/overlays/Overlay.cpp @@ -110,7 +110,8 @@ void Overlay::setProperties(const QScriptValue& properties) { } if (properties.property("visible").isValid()) { - setVisible(properties.property("visible").toVariant().toBool()); + bool visible = properties.property("visible").toVariant().toBool(); + setVisible(visible); } if (properties.property("anchor").isValid()) { diff --git a/interface/src/ui/overlays/Overlay.h b/interface/src/ui/overlays/Overlay.h index 1698d71fc3..9a19c71db6 100644 --- a/interface/src/ui/overlays/Overlay.h +++ b/interface/src/ui/overlays/Overlay.h @@ -44,6 +44,7 @@ public: virtual void removeFromScene(Overlay::Pointer overlay, std::shared_ptr scene, render::PendingChanges& pendingChanges); // getters + virtual QString getType() const = 0; virtual bool is3D() const = 0; bool isLoaded() { return _isLoaded; } bool getVisible() const { return _visible; } diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index ff218db844..bce219b4b4 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -48,6 +48,7 @@ Overlays::~Overlays() { } _overlaysHUD.clear(); _overlaysWorld.clear(); + _panels.clear(); } cleanupOverlaysToDelete(); @@ -124,85 +125,84 @@ void Overlays::renderHUD(RenderArgs* renderArgs) { } } -unsigned int Overlays::addOverlay(const QString& type, const QScriptValue& properties) { - unsigned int thisID = 0; - Overlay* thisOverlay = NULL; - - bool created = true; - if (type == "image") { - thisOverlay = new ImageOverlay(); - } else if (type == "text") { - thisOverlay = new TextOverlay(); - } else if (type == "text3d") { - thisOverlay = new Text3DOverlay(); - } else if (type == "cube") { - thisOverlay = new Cube3DOverlay(); - } else if (type == "sphere") { - thisOverlay = new Sphere3DOverlay(); - } else if (type == "circle3d") { - thisOverlay = new Circle3DOverlay(); - } else if (type == "rectangle3d") { - thisOverlay = new Rectangle3DOverlay(); - } else if (type == "line3d") { - thisOverlay = new Line3DOverlay(); - } else if (type == "grid") { - thisOverlay = new Grid3DOverlay(); - } else if (type == "localmodels") { - thisOverlay = new LocalModelsOverlay(Application::getInstance()->getEntityClipboardRenderer()); - } else if (type == "model") { - thisOverlay = new ModelOverlay(); - } else if (type == "billboard") { - thisOverlay = new BillboardOverlay(); - } else { - created = false; +Overlay::Pointer Overlays::getOverlay(unsigned int id) const { + if (_overlaysHUD.contains(id)) { + return _overlaysHUD[id]; } - - if (created) { - thisOverlay->setProperties(properties); - thisID = addOverlay(thisOverlay); + if (_overlaysWorld.contains(id)) { + return _overlaysWorld[id]; } - - return thisID; + return nullptr; } -unsigned int Overlays::addOverlay(Overlay* overlay) { - Overlay::Pointer overlayPointer(overlay); +unsigned int Overlays::addOverlay(const QString& type, const QScriptValue& properties) { + Overlay::Pointer thisOverlay = nullptr; + + if (type == ImageOverlay::TYPE) { + thisOverlay = std::make_shared(); + } else if (type == TextOverlay::TYPE) { + thisOverlay = std::make_shared(); + } else if (type == Text3DOverlay::TYPE) { + thisOverlay = std::make_shared(); + } else if (type == Cube3DOverlay::TYPE) { + thisOverlay = std::make_shared(); + } else if (type == Sphere3DOverlay::TYPE) { + thisOverlay = std::make_shared(); + } else if (type == Circle3DOverlay::TYPE) { + thisOverlay = std::make_shared(); + } else if (type == Rectangle3DOverlay::TYPE) { + thisOverlay = std::make_shared(); + } else if (type == Line3DOverlay::TYPE) { + thisOverlay = std::make_shared(); + } else if (type == Grid3DOverlay::TYPE) { + thisOverlay = std::make_shared(); + } else if (type == LocalModelsOverlay::TYPE) { + 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) { + thisOverlay->setProperties(properties); + return addOverlay(thisOverlay); + } + return 0; +} + +unsigned int Overlays::addOverlay(Overlay::Pointer overlay) { overlay->init(_scriptEngine); QWriteLocker lock(&_lock); unsigned int thisID = _nextOverlayID; _nextOverlayID++; if (overlay->is3D()) { - Base3DOverlay* overlay3D = static_cast(overlay); + auto overlay3D = std::static_pointer_cast(overlay); if (overlay3D->getDrawOnHUD()) { - _overlaysHUD[thisID] = overlayPointer; + _overlaysHUD[thisID] = overlay; } else { - _overlaysWorld[thisID] = overlayPointer; + _overlaysWorld[thisID] = overlay; render::ScenePointer scene = Application::getInstance()->getMain3DScene(); render::PendingChanges pendingChanges; - overlayPointer->addToScene(overlayPointer, scene, pendingChanges); + overlay->addToScene(overlay, scene, pendingChanges); scene->enqueuePendingChanges(pendingChanges); } } else { - _overlaysHUD[thisID] = overlayPointer; + _overlaysHUD[thisID] = overlay; } - + return thisID; } unsigned int Overlays::cloneOverlay(unsigned int id) { - Overlay::Pointer thisOverlay = NULL; - if (_overlaysHUD.contains(id)) { - thisOverlay = _overlaysHUD[id]; - } else if (_overlaysWorld.contains(id)) { - thisOverlay = _overlaysWorld[id]; - } + Overlay::Pointer thisOverlay = getOverlay(id); if (thisOverlay) { - return addOverlay(thisOverlay->createClone()); + return addOverlay(Overlay::Pointer(thisOverlay->createClone())); } return 0; // Not found @@ -210,14 +210,8 @@ unsigned int Overlays::cloneOverlay(unsigned int id) { bool Overlays::editOverlay(unsigned int id, const QScriptValue& properties) { QWriteLocker lock(&_lock); - Overlay::Pointer thisOverlay; - - if (_overlaysHUD.contains(id)) { - thisOverlay = _overlaysHUD[id]; - } else if (_overlaysWorld.contains(id)) { - thisOverlay = _overlaysWorld[id]; - } + Overlay::Pointer thisOverlay = getOverlay(id); if (thisOverlay) { if (thisOverlay->is3D()) { auto overlay3D = std::static_pointer_cast(thisOverlay); @@ -258,8 +252,51 @@ void Overlays::deleteOverlay(unsigned int id) { } } + auto attachable = std::dynamic_pointer_cast(overlayToDelete); + if (attachable && attachable->getAttachedPanel()) { + attachable->getAttachedPanel()->removeChild(id); + attachable->setAttachedPanel(nullptr); + } + QWriteLocker lock(&_deleteLock); _overlaysToDelete.push_back(overlayToDelete); + + emit overlayDeleted(id); +} + +QString Overlays::getOverlayType(unsigned int overlayId) const { + Overlay::Pointer overlay = getOverlay(overlayId); + if (overlay) { + return overlay->getType(); + } + return ""; +} + +unsigned int Overlays::getAttachedPanel(unsigned int childId) const { + Overlay::Pointer overlay = getOverlay(childId); + auto attachable = std::dynamic_pointer_cast(overlay); + if (attachable) { + return _panels.key(attachable->getAttachedPanel()); + } + return 0; +} + +void Overlays::setAttachedPanel(unsigned int childId, unsigned int panelId) { + Overlay::Pointer overlay = getOverlay(childId); + auto attachable = std::dynamic_pointer_cast(overlay); + if (attachable) { + if (_panels.contains(panelId)) { + auto panel = _panels[panelId]; + panel->addChild(childId); + attachable->setAttachedPanel(panel); + } else { + auto panel = attachable->getAttachedPanel(); + if (panel) { + panel->removeChild(childId); + attachable->setAttachedPanel(nullptr); + } + } + } } unsigned int Overlays::getOverlayAtPoint(const glm::vec2& point) { @@ -302,13 +339,8 @@ unsigned int Overlays::getOverlayAtPoint(const glm::vec2& point) { OverlayPropertyResult Overlays::getProperty(unsigned int id, const QString& property) { OverlayPropertyResult result; - Overlay::Pointer thisOverlay; + Overlay::Pointer thisOverlay = getOverlay(id); QReadLocker lock(&_lock); - if (_overlaysHUD.contains(id)) { - thisOverlay = _overlaysHUD[id]; - } else if (_overlaysWorld.contains(id)) { - thisOverlay = _overlaysWorld[id]; - } if (thisOverlay) { result.value = thisOverlay->getProperty(property); } @@ -456,12 +488,8 @@ void RayToOverlayIntersectionResultFromScriptValue(const QScriptValue& object, R bool Overlays::isLoaded(unsigned int id) { QReadLocker lock(&_lock); - Overlay::Pointer thisOverlay = NULL; - if (_overlaysHUD.contains(id)) { - thisOverlay = _overlaysHUD[id]; - } else if (_overlaysWorld.contains(id)) { - thisOverlay = _overlaysWorld[id]; - } else { + Overlay::Pointer thisOverlay = getOverlay(id); + if (!thisOverlay) { return false; // not found } return thisOverlay->isLoaded(); @@ -483,3 +511,56 @@ QSizeF Overlays::textSize(unsigned int id, const QString& text) const { } return QSizeF(0.0f, 0.0f); } + +unsigned int Overlays::addPanel(FloatingUIPanel::Pointer panel) { + QWriteLocker lock(&_lock); + + unsigned int thisID = _nextOverlayID; + _nextOverlayID++; + _panels[thisID] = panel; + + return thisID; +} + +unsigned int Overlays::addPanel(const QScriptValue& properties) { + FloatingUIPanel::Pointer panel = std::make_shared(); + panel->init(_scriptEngine); + panel->setProperties(properties); + return addPanel(panel); +} + +void Overlays::editPanel(unsigned int panelId, const QScriptValue& properties) { + if (_panels.contains(panelId)) { + _panels[panelId]->setProperties(properties); + } +} + +OverlayPropertyResult Overlays::getPanelProperty(unsigned int panelId, const QString& property) { + OverlayPropertyResult result; + if (_panels.contains(panelId)) { + FloatingUIPanel::Pointer thisPanel = _panels[panelId]; + QReadLocker lock(&_lock); + result.value = thisPanel->getProperty(property); + } + return result; +} + + +void Overlays::deletePanel(unsigned int panelId) { + FloatingUIPanel::Pointer panelToDelete; + + { + QWriteLocker lock(&_lock); + if (_panels.contains(panelId)) { + panelToDelete = _panels.take(panelId); + } else { + return; + } + } + + while (!panelToDelete->getChildren().isEmpty()) { + deleteOverlay(panelToDelete->popLastChild()); + } + + emit panelDeleted(panelId); +} diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index cd5b0f1d10..ce2c3efeae 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -2,8 +2,14 @@ // Overlays.h // interface/src/ui/overlays // +// Modified by Zander Otavka on 7/15/15 // Copyright 2014 High Fidelity, Inc. // +// Exposes methods for managing `Overlay`s and `FloatingUIPanel`s to scripts. +// +// YOU SHOULD NOT USE `Overlays` DIRECTLY, unless you like pain and deprecation. Instead, use the +// object oriented abstraction layer found in `examples/libraries/overlayUtils.js`. +// // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // @@ -16,6 +22,9 @@ #include "Overlay.h" +#include "FloatingUIPanel.h" +#include "PanelAttachable.h" + class PickRay; class OverlayPropertyResult { @@ -57,12 +66,16 @@ public: void update(float deltatime); void renderHUD(RenderArgs* renderArgs); + Overlay::Pointer getOverlay(unsigned int id) const; + FloatingUIPanel::Pointer getPanel(unsigned int id) const { return _panels[id]; } + public slots: /// adds an overlay with the specific properties unsigned int addOverlay(const QString& type, const QScriptValue& properties); /// adds an overlay that's already been created - unsigned int addOverlay(Overlay* overlay); + unsigned int addOverlay(Overlay* overlay) { return addOverlay(Overlay::Pointer(overlay)); } + unsigned int addOverlay(Overlay::Pointer overlay); /// clones an existing overlay unsigned int cloneOverlay(unsigned int id); @@ -74,6 +87,12 @@ public slots: /// deletes a particle void deleteOverlay(unsigned int id); + /// 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); + /// 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); @@ -90,12 +109,35 @@ public slots: /// overlay; in meters if it is a 3D text overlay QSizeF textSize(unsigned int id, const QString& text) const; + + /// adds a panel that has already been created + unsigned int addPanel(FloatingUIPanel::Pointer panel); + + /// creates and adds a panel based on a set of properties + unsigned int addPanel(const QScriptValue& properties); + + /// edit the properties of a panel + void editPanel(unsigned int panelId, const QScriptValue& properties); + + /// get a property of a panel + OverlayPropertyResult getPanelProperty(unsigned int panelId, const QString& property); + + /// deletes a panel and all child overlays + void deletePanel(unsigned int panelId); + +signals: + void overlayDeleted(unsigned int id); + void panelDeleted(unsigned int id); + private: void cleanupOverlaysToDelete(); + QMap _overlaysHUD; QMap _overlaysWorld; + QMap _panels; QList _overlaysToDelete; unsigned int _nextOverlayID; + QReadWriteLock _lock; QReadWriteLock _deleteLock; QScriptEngine* _scriptEngine; diff --git a/interface/src/ui/overlays/PanelAttachable.cpp b/interface/src/ui/overlays/PanelAttachable.cpp new file mode 100644 index 0000000000..ac4730cfe8 --- /dev/null +++ b/interface/src/ui/overlays/PanelAttachable.cpp @@ -0,0 +1,80 @@ +// +// PanelAttachable.cpp +// hifi +// +// Created by Zander Otavka on 7/15/15. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "PanelAttachable.h" + +#include + +PanelAttachable::PanelAttachable() : + _attachedPanel(nullptr), + _facingRotation(1, 0, 0, 0) +{ +} + +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()); + } +} + +QScriptValue PanelAttachable::getProperty(QScriptEngine* scriptEngine, const QString &property) { + if (property == "offsetPosition") { + return vec3toScriptValue(scriptEngine, getOffsetPosition()); + } + if (property == "facingRotation") { + return quatToScriptValue(scriptEngine, getFacingRotation()); + } + 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); + } + } + + 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/PanelAttachable.h b/interface/src/ui/overlays/PanelAttachable.h new file mode 100644 index 0000000000..9776ac5ecb --- /dev/null +++ b/interface/src/ui/overlays/PanelAttachable.h @@ -0,0 +1,45 @@ +// +// PanelAttachable.h +// interface/src/ui/overlays +// +// Created by Zander Otavka on 7/1/15. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_PanelAttachable_h +#define hifi_PanelAttachable_h + +#include "FloatingUIPanel.h" + +#include +#include + +class PanelAttachable { +public: + PanelAttachable(); + PanelAttachable(const PanelAttachable* panelAttachable); + + 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; } + + QScriptValue getProperty(QScriptEngine* scriptEngine, const QString& property); + void setProperties(const QScriptValue& properties); + +protected: + virtual void setTransforms(Transform& transform); + +private: + FloatingUIPanel::Pointer _attachedPanel; + glm::vec3 _offsetPosition; + glm::quat _facingRotation; +}; + +#endif // hifi_PanelAttachable_h diff --git a/interface/src/ui/overlays/Planar3DOverlay.cpp b/interface/src/ui/overlays/Planar3DOverlay.cpp index 0ca092ba23..354991b596 100644 --- a/interface/src/ui/overlays/Planar3DOverlay.cpp +++ b/interface/src/ui/overlays/Planar3DOverlay.cpp @@ -14,8 +14,15 @@ #include #include +Planar3DOverlay::Planar3DOverlay() : + Base3DOverlay(), + _dimensions{1.0f, 1.0f} +{ +} + Planar3DOverlay::Planar3DOverlay(const Planar3DOverlay* planar3DOverlay) : - Base3DOverlay(planar3DOverlay) + Base3DOverlay(planar3DOverlay), + _dimensions(planar3DOverlay->_dimensions) { } diff --git a/interface/src/ui/overlays/Planar3DOverlay.h b/interface/src/ui/overlays/Planar3DOverlay.h index fe8c513efd..08a7121e91 100644 --- a/interface/src/ui/overlays/Planar3DOverlay.h +++ b/interface/src/ui/overlays/Planar3DOverlay.h @@ -17,7 +17,7 @@ class Planar3DOverlay : public Base3DOverlay { Q_OBJECT public: - Planar3DOverlay() {} + Planar3DOverlay(); Planar3DOverlay(const Planar3DOverlay* planar3DOverlay); AABox getBounds() const; @@ -32,7 +32,7 @@ public: virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face); protected: - glm::vec2 _dimensions{1.0f, 1.0f}; + glm::vec2 _dimensions; }; diff --git a/interface/src/ui/overlays/Rectangle3DOverlay.cpp b/interface/src/ui/overlays/Rectangle3DOverlay.cpp index 1d5183b833..64c5e4c819 100644 --- a/interface/src/ui/overlays/Rectangle3DOverlay.cpp +++ b/interface/src/ui/overlays/Rectangle3DOverlay.cpp @@ -13,6 +13,9 @@ #include #include + +QString const Rectangle3DOverlay::TYPE = "rectangle3d"; + Rectangle3DOverlay::Rectangle3DOverlay() : _geometryCacheID(DependencyManager::get()->allocateID()) { diff --git a/interface/src/ui/overlays/Rectangle3DOverlay.h b/interface/src/ui/overlays/Rectangle3DOverlay.h index f59ab21198..e5eba8bce8 100644 --- a/interface/src/ui/overlays/Rectangle3DOverlay.h +++ b/interface/src/ui/overlays/Rectangle3DOverlay.h @@ -17,6 +17,9 @@ class Rectangle3DOverlay : public Planar3DOverlay { Q_OBJECT public: + static QString const TYPE; + virtual QString getType() const { return TYPE; } + Rectangle3DOverlay(); Rectangle3DOverlay(const Rectangle3DOverlay* rectangle3DOverlay); ~Rectangle3DOverlay(); diff --git a/interface/src/ui/overlays/Sphere3DOverlay.cpp b/interface/src/ui/overlays/Sphere3DOverlay.cpp index 307b60b2ce..9712375209 100644 --- a/interface/src/ui/overlays/Sphere3DOverlay.cpp +++ b/interface/src/ui/overlays/Sphere3DOverlay.cpp @@ -15,6 +15,8 @@ #include #include +QString const Sphere3DOverlay::TYPE = "sphere"; + Sphere3DOverlay::Sphere3DOverlay(const Sphere3DOverlay* Sphere3DOverlay) : Volume3DOverlay(Sphere3DOverlay) { diff --git a/interface/src/ui/overlays/Sphere3DOverlay.h b/interface/src/ui/overlays/Sphere3DOverlay.h index b82dc548f1..d371077502 100644 --- a/interface/src/ui/overlays/Sphere3DOverlay.h +++ b/interface/src/ui/overlays/Sphere3DOverlay.h @@ -17,6 +17,9 @@ class Sphere3DOverlay : public Volume3DOverlay { Q_OBJECT public: + static QString const TYPE; + virtual QString getType() const { return TYPE; } + Sphere3DOverlay() {} Sphere3DOverlay(const Sphere3DOverlay* Sphere3DOverlay); diff --git a/interface/src/ui/overlays/Text3DOverlay.cpp b/interface/src/ui/overlays/Text3DOverlay.cpp index 737b2d0bc5..00a7d75090 100644 --- a/interface/src/ui/overlays/Text3DOverlay.cpp +++ b/interface/src/ui/overlays/Text3DOverlay.cpp @@ -23,6 +23,8 @@ const int FIXED_FONT_POINT_SIZE = 40; const int FIXED_FONT_SCALING_RATIO = FIXED_FONT_POINT_SIZE * 80.0f; // this is a ratio determined through experimentation const float LINE_SCALE_RATIO = 1.2f; +QString const Text3DOverlay::TYPE = "text3d"; + Text3DOverlay::Text3DOverlay() : _backgroundColor(DEFAULT_BACKGROUND_COLOR), _backgroundAlpha(DEFAULT_BACKGROUND_ALPHA), diff --git a/interface/src/ui/overlays/Text3DOverlay.h b/interface/src/ui/overlays/Text3DOverlay.h index 666b43d8b1..abd5ef54bd 100644 --- a/interface/src/ui/overlays/Text3DOverlay.h +++ b/interface/src/ui/overlays/Text3DOverlay.h @@ -21,6 +21,9 @@ class Text3DOverlay : public Planar3DOverlay { Q_OBJECT public: + static QString const TYPE; + virtual QString getType() const { return TYPE; } + Text3DOverlay(); Text3DOverlay(const Text3DOverlay* text3DOverlay); ~Text3DOverlay(); diff --git a/interface/src/ui/overlays/TextOverlay.cpp b/interface/src/ui/overlays/TextOverlay.cpp index ea0c4f6026..e9fda2def8 100644 --- a/interface/src/ui/overlays/TextOverlay.cpp +++ b/interface/src/ui/overlays/TextOverlay.cpp @@ -78,6 +78,8 @@ QString toQmlColor(const glm::vec4& v) { arg((int)(v.b * 255), 2, 16, QChar('0')); } +QString const TextOverlay::TYPE = "text"; + TextOverlay::TextOverlay() : _backgroundColor(DEFAULT_BACKGROUND_COLOR), _backgroundAlpha(DEFAULT_BACKGROUND_ALPHA), diff --git a/interface/src/ui/overlays/TextOverlay.h b/interface/src/ui/overlays/TextOverlay.h index b70b95ca3b..a8e6967871 100644 --- a/interface/src/ui/overlays/TextOverlay.h +++ b/interface/src/ui/overlays/TextOverlay.h @@ -29,6 +29,9 @@ class TextOverlay : public Overlay2D { Q_OBJECT public: + static QString const TYPE; + virtual QString getType() const { return TYPE; } + TextOverlay(); TextOverlay(const TextOverlay* textOverlay); ~TextOverlay(); diff --git a/libraries/animation/src/AnimationCache.cpp b/libraries/animation/src/AnimationCache.cpp index 0a76702b5b..d1c4408178 100644 --- a/libraries/animation/src/AnimationCache.cpp +++ b/libraries/animation/src/AnimationCache.cpp @@ -1,6 +1,6 @@ // // AnimationCache.cpp -// libraries/script-engine/src/ +// libraries/animation/src/ // // Created by Andrzej Kapolka on 4/14/14. // Copyright (c) 2014 High Fidelity, Inc. All rights reserved. diff --git a/libraries/animation/src/AnimationCache.h b/libraries/animation/src/AnimationCache.h index c90c4c9225..840d7a0355 100644 --- a/libraries/animation/src/AnimationCache.h +++ b/libraries/animation/src/AnimationCache.h @@ -1,6 +1,6 @@ // // AnimationCache.h -// libraries/script-engine/src/ +// libraries/animation/src/ // // Created by Andrzej Kapolka on 4/14/14. // Copyright (c) 2014 High Fidelity, Inc. All rights reserved. diff --git a/libraries/render-utils/src/AnimationHandle.cpp b/libraries/animation/src/AnimationHandle.cpp similarity index 62% rename from libraries/render-utils/src/AnimationHandle.cpp rename to libraries/animation/src/AnimationHandle.cpp index 6ad6730952..605fb25f1c 100644 --- a/libraries/render-utils/src/AnimationHandle.cpp +++ b/libraries/animation/src/AnimationHandle.cpp @@ -1,6 +1,6 @@ // // AnimationHandle.cpp -// interface/src/renderer +// libraries/animation/src/ // // Created by Andrzej Kapolka on 10/18/13. // Copyright 2013 High Fidelity, Inc. @@ -10,7 +10,7 @@ // #include "AnimationHandle.h" -#include "Model.h" + void AnimationHandle::setURL(const QUrl& url) { if (_url != url) { @@ -20,28 +20,17 @@ void AnimationHandle::setURL(const QUrl& url) { } } -static void insertSorted(QList& handles, const AnimationHandlePointer& handle) { - for (QList::iterator it = handles.begin(); it != handles.end(); it++) { - if (handle->getPriority() > (*it)->getPriority()) { - handles.insert(it, handle); - return; - } - } - handles.append(handle); -} - void AnimationHandle::setPriority(float priority) { if (_priority == priority) { return; } if (isRunning()) { - _model->_runningAnimations.removeOne(_self); + _rig->removeRunningAnimation(getAnimationHandlePointer()); if (priority < _priority) { replaceMatchingPriorities(priority); } _priority = priority; - insertSorted(_model->_runningAnimations, _self); - + _rig->addRunningAnimation(getAnimationHandlePointer()); } else { _priority = priority; } @@ -68,21 +57,21 @@ void AnimationHandle::setRunning(bool running) { } _animationLoop.setRunning(running); if (isRunning()) { - if (!_model->_runningAnimations.contains(_self)) { - insertSorted(_model->_runningAnimations, _self); + if (!_rig->isRunningAnimation(getAnimationHandlePointer())) { + _rig->addRunningAnimation(getAnimationHandlePointer()); } } else { - _model->_runningAnimations.removeOne(_self); + _rig->removeRunningAnimation(getAnimationHandlePointer()); restoreJoints(); replaceMatchingPriorities(0.0f); } emit runningChanged(isRunning()); } -AnimationHandle::AnimationHandle(Model* model) : - QObject(model), - _model(model), - _priority(1.0f) +AnimationHandle::AnimationHandle(RigPointer rig) : + QObject(rig.get()), + _rig(rig), + _priority(1.0f) { } @@ -110,42 +99,61 @@ void AnimationHandle::setAnimationDetails(const AnimationDetails& details) { } +void AnimationHandle::setJointMappings(QVector jointMappings) { + _jointMappings = jointMappings; +} + +QVector AnimationHandle::getJointMappings() { + if (_jointMappings.isEmpty()) { + QVector animationJoints = _animation->getGeometry().joints; + for (int i = 0; i < animationJoints.count(); i++) { + _jointMappings.append(_rig->indexOfJoint(animationJoints.at(i).name)); + } + } + return _jointMappings; +} + void AnimationHandle::simulate(float deltaTime) { if (!_animation || !_animation->isLoaded()) { return; } - + _animationLoop.simulate(deltaTime); - - // update the joint mappings if necessary/possible - if (_jointMappings.isEmpty()) { - if (_model && _model->isActive()) { - _jointMappings = _model->getGeometry()->getJointMappings(_animation); - } - if (_jointMappings.isEmpty()) { - return; - } - if (!_maskedJoints.isEmpty()) { - const FBXGeometry& geometry = _model->getGeometry()->getFBXGeometry(); - for (int i = 0; i < _jointMappings.size(); i++) { - int& mapping = _jointMappings[i]; - if (mapping != -1 && _maskedJoints.contains(geometry.joints.at(mapping).name)) { - mapping = -1; - } - } - } + + if (getJointMappings().isEmpty()) { + qDebug() << "AnimationHandle::simulate -- _jointMappings.isEmpty()"; + return; } - + + // // update the joint mappings if necessary/possible + // if (_jointMappings.isEmpty()) { + // if (_model && _model->isActive()) { + // _jointMappings = _model->getGeometry()->getJointMappings(_animation); + // } + // if (_jointMappings.isEmpty()) { + // return; + // } + // if (!_maskedJoints.isEmpty()) { + // const FBXGeometry& geometry = _model->getGeometry()->getFBXGeometry(); + // for (int i = 0; i < _jointMappings.size(); i++) { + // int& mapping = _jointMappings[i]; + // if (mapping != -1 && _maskedJoints.contains(geometry.joints.at(mapping).name)) { + // mapping = -1; + // } + // } + // } + // } + const FBXGeometry& animationGeometry = _animation->getGeometry(); if (animationGeometry.animationFrames.isEmpty()) { stop(); return; } - + if (_animationLoop.getMaxFrameIndexHint() != animationGeometry.animationFrames.size()) { _animationLoop.setMaxFrameIndexHint(animationGeometry.animationFrames.size()); } - + // blend between the closest two frames applyFrame(getFrameIndex()); } @@ -154,17 +162,23 @@ void AnimationHandle::applyFrame(float frameIndex) { if (!_animation || !_animation->isLoaded()) { return; } - + const FBXGeometry& animationGeometry = _animation->getGeometry(); int frameCount = animationGeometry.animationFrames.size(); const FBXAnimationFrame& floorFrame = animationGeometry.animationFrames.at((int)glm::floor(frameIndex) % frameCount); const FBXAnimationFrame& ceilFrame = animationGeometry.animationFrames.at((int)glm::ceil(frameIndex) % frameCount); float frameFraction = glm::fract(frameIndex); + assert(_rig->getJointStateCount() >= _jointMappings.size()); for (int i = 0; i < _jointMappings.size(); i++) { int mapping = _jointMappings.at(i); if (mapping != -1) { - JointState& state = _model->_jointStates[mapping]; - state.setRotationInConstrainedFrame(safeMix(floorFrame.rotations.at(i), ceilFrame.rotations.at(i), frameFraction), _priority); + _rig->setJointRotationInConstrainedFrame(mapping, + safeMix(floorFrame.rotations.at(i), + ceilFrame.rotations.at(i), + frameFraction), + _priority, + false, + _mix); } } } @@ -173,9 +187,8 @@ void AnimationHandle::replaceMatchingPriorities(float newPriority) { for (int i = 0; i < _jointMappings.size(); i++) { int mapping = _jointMappings.at(i); if (mapping != -1) { - JointState& state = _model->_jointStates[mapping]; - if (_priority == state._animationPriority) { - state._animationPriority = newPriority; + if (_priority == _rig->getJointAnimatinoPriority(mapping)) { + _rig->setJointAnimatinoPriority(mapping, newPriority); } } } @@ -185,9 +198,7 @@ void AnimationHandle::restoreJoints() { for (int i = 0; i < _jointMappings.size(); i++) { int mapping = _jointMappings.at(i); if (mapping != -1) { - JointState& state = _model->_jointStates[mapping]; - state.restoreRotation(1.0f, state._animationPriority); + _rig->restoreJointRotation(mapping, 1.0f, _rig->getJointAnimatinoPriority(mapping)); } } } - diff --git a/libraries/render-utils/src/AnimationHandle.h b/libraries/animation/src/AnimationHandle.h similarity index 65% rename from libraries/render-utils/src/AnimationHandle.h rename to libraries/animation/src/AnimationHandle.h index ca9c2eb6d0..42e564944e 100644 --- a/libraries/render-utils/src/AnimationHandle.h +++ b/libraries/animation/src/AnimationHandle.h @@ -1,6 +1,6 @@ // // AnimationHandle.h -// interface/src/renderer +// libraries/animation/src/ // // Created by Andrzej Kapolka on 10/18/13. // Copyright 2013 High Fidelity, Inc. @@ -18,22 +18,43 @@ #include #include -#include -#include +#include "AnimationCache.h" +#include "AnimationLoop.h" +#include "Rig.h" class AnimationHandle; class Model; -typedef QSharedPointer AnimationHandlePointer; -typedef QWeakPointer WeakAnimationHandlePointer; +typedef std::shared_ptr AnimationHandlePointer; +typedef std::weak_ptr WeakAnimationHandlePointer; +inline uint qHash(const std::shared_ptr& a, uint seed) { + // return qHash(a.get(), seed); + AnimationHandle* strongRef = a ? a.get() : nullptr; + return qHash(strongRef, seed); +} +inline uint qHash(const std::weak_ptr& a, uint seed) { + AnimationHandlePointer strongPointer = a.lock(); + AnimationHandle* strongRef = strongPointer ? strongPointer.get() : nullptr; + return qHash(strongRef, seed); +} -/// Represents a handle to a model animation. -class AnimationHandle : public QObject { +// inline uint qHash(const WeakAnimationHandlePointer& handle, uint seed) { +// return qHash(handle.data(), seed); +// } + + + +/// Represents a handle to a model animation. I.e., an Animation in use by a given Rig. +class AnimationHandle : public QObject, public std::enable_shared_from_this { Q_OBJECT - + public: + AnimationHandle(RigPointer rig); + + AnimationHandlePointer getAnimationHandlePointer() { return shared_from_this(); } + void setRole(const QString& role) { _role = role; } const QString& getRole() const { return _role; } @@ -42,29 +63,30 @@ public: void setPriority(float priority); float getPriority() const { return _priority; } + void setMix(float mix) { _mix = mix; } void setMaskedJoints(const QStringList& maskedJoints); const QStringList& getMaskedJoints() const { return _maskedJoints; } - + void setFPS(float fps) { _animationLoop.setFPS(fps); } float getFPS() const { return _animationLoop.getFPS(); } void setLoop(bool loop) { _animationLoop.setLoop(loop); } bool getLoop() const { return _animationLoop.getLoop(); } - + void setHold(bool hold) { _animationLoop.setHold(hold); } bool getHold() const { return _animationLoop.getHold(); } - + void setStartAutomatically(bool startAutomatically); bool getStartAutomatically() const { return _animationLoop.getStartAutomatically(); } - + void setFirstFrame(float firstFrame) { _animationLoop.setFirstFrame(firstFrame); } float getFirstFrame() const { return _animationLoop.getFirstFrame(); } - + void setLastFrame(float lastFrame) { _animationLoop.setLastFrame(lastFrame); } float getLastFrame() const { return _animationLoop.getLastFrame(); } - + void setRunning(bool running); bool isRunning() const { return _animationLoop.isRunning(); } @@ -74,39 +96,38 @@ public: AnimationDetails getAnimationDetails() const; void setAnimationDetails(const AnimationDetails& details); + void setJointMappings(QVector jointMappings); + QVector getJointMappings(); // computing if necessary + void simulate(float deltaTime); + void applyFrame(float frameIndex); + void replaceMatchingPriorities(float newPriority); + void restoreJoints(); + void clearJoints() { _jointMappings.clear(); } + signals: - + void runningChanged(bool running); public slots: void start() { setRunning(true); } void stop() { setRunning(false); } - + private: - friend class Model; - - AnimationHandle(Model* model); - - void simulate(float deltaTime); - void applyFrame(float frameIndex); - void replaceMatchingPriorities(float newPriority); - void restoreJoints(); - - void clearJoints() { _jointMappings.clear(); } - - Model* _model; - WeakAnimationHandlePointer _self; + RigPointer _rig; AnimationPointer _animation; QString _role; QUrl _url; float _priority; + float _mix; QStringList _maskedJoints; QVector _jointMappings; - + AnimationLoop _animationLoop; + + static QHash, QVector> _jointMappingsCache; }; diff --git a/libraries/animation/src/AnimationLogging.cpp b/libraries/animation/src/AnimationLogging.cpp new file mode 100644 index 0000000000..11ed5ebd35 --- /dev/null +++ b/libraries/animation/src/AnimationLogging.cpp @@ -0,0 +1,14 @@ +// +// AnimationLogging.cpp +// libraries/audio/src +// +// Created by Howard Stearns on 7/24/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 "AnimationLogging.h" + +Q_LOGGING_CATEGORY(animation, "hifi.animation") diff --git a/libraries/animation/src/AnimationLogging.h b/libraries/animation/src/AnimationLogging.h new file mode 100644 index 0000000000..6c56e2dbe4 --- /dev/null +++ b/libraries/animation/src/AnimationLogging.h @@ -0,0 +1,18 @@ +// +// AnimationLogging.h +// libraries/animation/src/ +// +// Created by Howard Stearns on 7/24/15. +// +// 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_AnimationLogging_h +#define hifi_AnimationLogging_h + +#include + +Q_DECLARE_LOGGING_CATEGORY(animation) + +#endif diff --git a/libraries/animation/src/AnimationLoop.cpp b/libraries/animation/src/AnimationLoop.cpp index 43e049f851..a2a27170c2 100644 --- a/libraries/animation/src/AnimationLoop.cpp +++ b/libraries/animation/src/AnimationLoop.cpp @@ -1,6 +1,6 @@ // // AnimationLoop.cpp -// libraries/animation +// libraries/animation/src/ // // Created by Brad Hefta-Gaub on 11/12/14. // Copyright (c) 2014 High Fidelity, Inc. All rights reserved. diff --git a/libraries/animation/src/AnimationLoop.h b/libraries/animation/src/AnimationLoop.h index d4537c4656..02161544ba 100644 --- a/libraries/animation/src/AnimationLoop.h +++ b/libraries/animation/src/AnimationLoop.h @@ -1,6 +1,6 @@ // // AnimationLoop.h -// libraries/script-engine/src/ +// libraries/animation/src/ // // Created by Brad Hefta-Gaub on 11/12/14. // Copyright (c) 2014 High Fidelity, Inc. All rights reserved. diff --git a/libraries/animation/src/AnimationObject.cpp b/libraries/animation/src/AnimationObject.cpp index ede1e82623..25a5743121 100644 --- a/libraries/animation/src/AnimationObject.cpp +++ b/libraries/animation/src/AnimationObject.cpp @@ -1,6 +1,6 @@ // // AnimationObject.cpp -// libraries/script-engine/src/ +// libraries/animation/src/ // // Created by Andrzej Kapolka on 4/17/14. // Copyright (c) 2014 High Fidelity, Inc. All rights reserved. diff --git a/libraries/animation/src/AnimationObject.h b/libraries/animation/src/AnimationObject.h index 078fc31fb3..aa69e78ceb 100644 --- a/libraries/animation/src/AnimationObject.h +++ b/libraries/animation/src/AnimationObject.h @@ -1,6 +1,6 @@ // // AnimationObject.h -// libraries/script-engine/src/ +// libraries/animation/src/ // // Created by Andrzej Kapolka on 4/17/14. // Copyright (c) 2014 High Fidelity, Inc. All rights reserved. diff --git a/libraries/animation/src/AvatarRig.cpp b/libraries/animation/src/AvatarRig.cpp new file mode 100644 index 0000000000..919ea43e7d --- /dev/null +++ b/libraries/animation/src/AvatarRig.cpp @@ -0,0 +1,34 @@ +// +// AvatarRig.cpp +// libraries/animation/src/ +// +// Created by SethAlves on 2015-7-22. +// 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 "AvatarRig.h" + +/// Updates the state of the joint at the specified index. +void AvatarRig::updateJointState(int index, glm::mat4 parentTransform) { + if (index < 0 && index >= _jointStates.size()) { + return; // bail + } + JointState& state = _jointStates[index]; + const FBXJoint& joint = state.getFBXJoint(); + + // compute model transforms + int parentIndex = joint.parentIndex; + if (parentIndex == -1) { + state.computeTransform(parentTransform); + clearJointTransformTranslation(index); + } else { + // guard against out-of-bounds access to _jointStates + if (joint.parentIndex >= 0 && joint.parentIndex < _jointStates.size()) { + const JointState& parentState = _jointStates.at(parentIndex); + state.computeTransform(parentState.getTransform(), parentState.getTransformChanged()); + } + } +} diff --git a/libraries/animation/src/AvatarRig.h b/libraries/animation/src/AvatarRig.h new file mode 100644 index 0000000000..4a111a535b --- /dev/null +++ b/libraries/animation/src/AvatarRig.h @@ -0,0 +1,27 @@ +// +// AvatarRig.h +// libraries/animation/src/ +// +// Created by SethAlves on 2015-7-22. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_AvatarRig_h +#define hifi_AvatarRig_h + +#include + +#include "Rig.h" + +class AvatarRig : public Rig { + Q_OBJECT + + public: + ~AvatarRig() {} + virtual void updateJointState(int index, glm::mat4 parentTransform); +}; + +#endif // hifi_AvatarRig_h diff --git a/libraries/animation/src/EntityRig.cpp b/libraries/animation/src/EntityRig.cpp new file mode 100644 index 0000000000..5ed1799671 --- /dev/null +++ b/libraries/animation/src/EntityRig.cpp @@ -0,0 +1,30 @@ +// +// EntityRig.cpp +// libraries/animation/src/ +// +// Created by SethAlves on 2015-7-22. +// 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 "EntityRig.h" + +/// 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; + if (parentIndex == -1) { + state.computeTransform(parentTransform); + } else { + // guard against out-of-bounds access to _jointStates + if (joint.parentIndex >= 0 && joint.parentIndex < _jointStates.size()) { + const JointState& parentState = _jointStates.at(parentIndex); + state.computeTransform(parentState.getTransform(), parentState.getTransformChanged()); + } + } +} diff --git a/libraries/animation/src/EntityRig.h b/libraries/animation/src/EntityRig.h new file mode 100644 index 0000000000..e8e15a5a28 --- /dev/null +++ b/libraries/animation/src/EntityRig.h @@ -0,0 +1,27 @@ +// +// EntityRig.h +// libraries/animation/src/ +// +// Created by SethAlves on 2015-7-22. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_EntityRig_h +#define hifi_EntityRig_h + +#include + +#include "Rig.h" + +class EntityRig : public Rig { + Q_OBJECT + + public: + ~EntityRig() {} + virtual void updateJointState(int index, glm::mat4 parentTransform); +}; + +#endif // hifi_EntityRig_h diff --git a/libraries/render-utils/src/JointState.cpp b/libraries/animation/src/JointState.cpp similarity index 97% rename from libraries/render-utils/src/JointState.cpp rename to libraries/animation/src/JointState.cpp index a82a57f0ed..3682837719 100644 --- a/libraries/render-utils/src/JointState.cpp +++ b/libraries/animation/src/JointState.cpp @@ -1,6 +1,6 @@ // // JointState.cpp -// interface/src/renderer +// libraries/animation/src/ // // Created by Andrzej Kapolka on 10/18/13. // Copyright 2013 High Fidelity, Inc. @@ -232,12 +232,13 @@ glm::quat JointState::computeVisibleParentRotation() const { return _visibleRotation * glm::inverse(_fbxJoint->preRotation * _visibleRotationInConstrainedFrame * _fbxJoint->postRotation); } -void JointState::setRotationInConstrainedFrame(glm::quat targetRotation, float priority, bool constrain) { +void JointState::setRotationInConstrainedFrame(glm::quat targetRotation, float priority, bool constrain, float mix) { if (priority >= _animationPriority || _animationPriority == 0.0f) { if (constrain && _constraint) { _constraint->softClamp(targetRotation, _rotationInConstrainedFrame, 0.5f); } - setRotationInConstrainedFrameInternal(targetRotation); + auto rotation = (mix == 1.0f) ? targetRotation : safeMix(getRotationInConstrainedFrame(), targetRotation, mix); + setRotationInConstrainedFrameInternal(rotation); _animationPriority = priority; } } diff --git a/libraries/render-utils/src/JointState.h b/libraries/animation/src/JointState.h similarity index 98% rename from libraries/render-utils/src/JointState.h rename to libraries/animation/src/JointState.h index 0ef84e50c4..93bf83d2c4 100644 --- a/libraries/render-utils/src/JointState.h +++ b/libraries/animation/src/JointState.h @@ -1,6 +1,6 @@ // // JointState.h -// interface/src/renderer +// libraries/animation/src/ // // Created by Andrzej Kapolka on 10/18/13. // Copyright 2013 High Fidelity, Inc. @@ -84,7 +84,7 @@ public: /// NOTE: the JointState's model-frame transform/rotation are NOT updated! void setRotationInBindFrame(const glm::quat& rotation, float priority, bool constrain = false); - void setRotationInConstrainedFrame(glm::quat targetRotation, float priority, bool constrain = false); + void setRotationInConstrainedFrame(glm::quat targetRotation, float priority, bool constrain = false, float mix = 1.0f); void setVisibleRotationInConstrainedFrame(const glm::quat& targetRotation); const glm::quat& getRotationInConstrainedFrame() const { return _rotationInConstrainedFrame; } const glm::quat& getVisibleRotationInConstrainedFrame() const { return _visibleRotationInConstrainedFrame; } diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp new file mode 100644 index 0000000000..8406b61d12 --- /dev/null +++ b/libraries/animation/src/Rig.cpp @@ -0,0 +1,754 @@ +// +// Rig.cpp +// libraries/animation/src/ +// +// Created by Howard Stearns, Seth Alves, Anthony Thibault, Andrew Meadows on 7/15/15. +// Copyright (c) 2015 High Fidelity, Inc. All rights reserved. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include +#include + +#include "AnimationHandle.h" +#include "AnimationLogging.h" +#include "Rig.h" + +void Rig::HeadParameters::dump() const { + qCDebug(animation, "HeadParameters ="); + qCDebug(animation, " leanSideways = %0.5f", leanSideways); + qCDebug(animation, " leanForward = %0.5f", leanForward); + qCDebug(animation, " torsoTwist = %0.5f", torsoTwist); + glm::vec3 axis = glm::axis(localHeadOrientation); + float theta = glm::angle(localHeadOrientation); + qCDebug(animation, " localHeadOrientation axis = (%.5f, %.5f, %.5f), theta = %0.5f", axis.x, axis.y, axis.z, theta); + axis = glm::axis(worldHeadOrientation); + theta = glm::angle(worldHeadOrientation); + qCDebug(animation, " worldHeadOrientation axis = (%.5f, %.5f, %.5f), theta = %0.5f", axis.x, axis.y, axis.z, theta); + qCDebug(animation, " eyeLookAt = (%.5f, %.5f, %.5f)", eyeLookAt.x, eyeLookAt.y, eyeLookAt.z); + qCDebug(animation, " eyeSaccade = (%.5f, %.5f, %.5f)", eyeSaccade.x, eyeSaccade.y, eyeSaccade.z); + qCDebug(animation, " leanJointIndex = %.d", leanJointIndex); + qCDebug(animation, " neckJointIndex = %.d", neckJointIndex); + qCDebug(animation, " leftEyeJointIndex = %.d", leftEyeJointIndex); + qCDebug(animation, " rightEyeJointIndex = %.d", rightEyeJointIndex); +} + +void insertSorted(QList& handles, const AnimationHandlePointer& handle) { + for (QList::iterator it = handles.begin(); it != handles.end(); it++) { + if (handle->getPriority() > (*it)->getPriority()) { + handles.insert(it, handle); + return; + } + } + handles.append(handle); +} + +AnimationHandlePointer Rig::createAnimationHandle() { + AnimationHandlePointer handle(new AnimationHandle(getRigPointer())); + _animationHandles.append(handle); + return handle; +} +void Rig::removeAnimationHandle(const AnimationHandlePointer& handle) { + handle->stop(); + // FIXME? Do we need to also animationHandle->clearJoints()? deleteAnimations(), below, was first written to do so, but did not first stop it. + _animationHandles.removeOne(handle); +} + +void Rig::startAnimation(const QString& url, float fps, float priority, + bool loop, bool hold, float firstFrame, float lastFrame, const QStringList& maskedJoints) { + //qCDebug(animation) << "startAnimation" << url << fps << priority << loop << hold << firstFrame << lastFrame << maskedJoints; + foreach (const AnimationHandlePointer& candidate, _animationHandles) { + if (candidate->getURL() == url) { + candidate->start(); + return; + } + } + AnimationHandlePointer handle = createAnimationHandle(); + handle->setURL(url); + handle->setFPS(fps); + handle->setPriority(priority); + handle->setLoop(loop); + handle->setHold(hold); + handle->setFirstFrame(firstFrame); + handle->setLastFrame(lastFrame); + handle->setMaskedJoints(maskedJoints); + handle->start(); +} + +void Rig::addAnimationByRole(const QString& role, const QString& url, float fps, float priority, + bool loop, bool hold, float firstFrame, float lastFrame, const QStringList& maskedJoints, bool startAutomatically) { + // check for a configured animation for the role + //qCDebug(animation) << "addAnimationByRole" << role << url << fps << priority << loop << hold << firstFrame << lastFrame << maskedJoints << startAutomatically; + foreach (const AnimationHandlePointer& candidate, _animationHandles) { + if (candidate->getRole() == role) { + if (startAutomatically) { + candidate->start(); + } + return; + } + } + AnimationHandlePointer handle = createAnimationHandle(); + handle->setRole(role); + handle->setURL(url); + handle->setFPS(fps); + handle->setPriority(priority); + handle->setLoop(loop); + handle->setHold(hold); + handle->setFirstFrame(firstFrame); + handle->setLastFrame(lastFrame); + handle->setMaskedJoints(maskedJoints); + if (startAutomatically) { + handle->start(); + } +} +void Rig::startAnimationByRole(const QString& role, const QString& url, float fps, float priority, + bool loop, bool hold, float firstFrame, float lastFrame, const QStringList& maskedJoints) { + addAnimationByRole(role, url, fps, priority, loop, hold, firstFrame, lastFrame, maskedJoints, true); +} + +void Rig::stopAnimationByRole(const QString& role) { + foreach (const AnimationHandlePointer& handle, getRunningAnimations()) { + if (handle->getRole() == role) { + handle->stop(); + } + } +} + +void Rig::stopAnimation(const QString& url) { + foreach (const AnimationHandlePointer& handle, getRunningAnimations()) { + if (handle->getURL() == url) { + handle->stop(); + } + } +} + +bool Rig::removeRunningAnimation(AnimationHandlePointer animationHandle) { + return _runningAnimations.removeOne(animationHandle); +} + +void Rig::addRunningAnimation(AnimationHandlePointer animationHandle) { + insertSorted(_runningAnimations, animationHandle); +} + +bool Rig::isRunningAnimation(AnimationHandlePointer animationHandle) { + return _runningAnimations.contains(animationHandle); +} + +void Rig::deleteAnimations() { + for (auto animation : _animationHandles) { + removeAnimationHandle(animation); + } + _animationHandles.clear(); +} + +float Rig::initJointStates(QVector states, glm::mat4 parentTransform) { + _jointStates = states; + initJointTransforms(parentTransform); + + int numStates = _jointStates.size(); + float radius = 0.0f; + for (int i = 0; i < numStates; ++i) { + float distance = glm::length(_jointStates[i].getPosition()); + if (distance > radius) { + radius = distance; + } + _jointStates[i].buildConstraint(); + } + for (int i = 0; i < _jointStates.size(); i++) { + _jointStates[i].slaveVisibleTransform(); + } + + return radius; +} + +// We could build and cache a dictionary, too.... +// 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) { + return i; + } + } + return -1; +} + + +void Rig::initJointTransforms(glm::mat4 parentTransform) { + // compute model transforms + int numStates = _jointStates.size(); + for (int i = 0; i < numStates; ++i) { + JointState& state = _jointStates[i]; + const FBXJoint& joint = state.getFBXJoint(); + int parentIndex = joint.parentIndex; + if (parentIndex == -1) { + state.initTransform(parentTransform); + } else { + const JointState& parentState = _jointStates.at(parentIndex); + state.initTransform(parentState.getTransform()); + } + } +} + +void Rig::clearJointTransformTranslation(int jointIndex) { + if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + return; + } + _jointStates[jointIndex].clearTransformTranslation(); +} + +void Rig::reset(const QVector& fbxJoints) { + if (_jointStates.isEmpty()) { + return; + } + for (int i = 0; i < _jointStates.size(); i++) { + _jointStates[i].setRotationInConstrainedFrame(fbxJoints.at(i).rotation, 0.0f); + } +} + +JointState Rig::getJointState(int jointIndex) const { + if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + return JointState(); + } + return _jointStates[jointIndex]; +} + +bool Rig::getJointStateRotation(int index, glm::quat& rotation) const { + if (index == -1 || index >= _jointStates.size()) { + return false; + } + const JointState& state = _jointStates.at(index); + rotation = state.getRotationInConstrainedFrame(); + return !state.rotationIsDefault(rotation); +} + +bool Rig::getVisibleJointState(int index, glm::quat& rotation) const { + if (index == -1 || index >= _jointStates.size()) { + return false; + } + const JointState& state = _jointStates.at(index); + rotation = state.getVisibleRotationInConstrainedFrame(); + return !state.rotationIsDefault(rotation); +} + +void Rig::clearJointState(int index) { + if (index != -1 && index < _jointStates.size()) { + JointState& state = _jointStates[index]; + state.setRotationInConstrainedFrame(glm::quat(), 0.0f); + } +} + +void Rig::clearJointStates() { + _jointStates.clear(); +} + +void Rig::clearJointAnimationPriority(int index) { + if (index != -1 && index < _jointStates.size()) { + _jointStates[index]._animationPriority = 0.0f; + } +} + +float Rig::getJointAnimatinoPriority(int index) { + if (index != -1 && index < _jointStates.size()) { + return _jointStates[index]._animationPriority; + } + return 0.0f; +} + +void Rig::setJointAnimatinoPriority(int index, float newPriority) { + if (index != -1 && index < _jointStates.size()) { + _jointStates[index]._animationPriority = newPriority; + } +} + +void Rig::setJointState(int index, bool valid, const glm::quat& rotation, float priority) { + if (index != -1 && index < _jointStates.size()) { + JointState& state = _jointStates[index]; + if (valid) { + state.setRotationInConstrainedFrame(rotation, priority); + } else { + state.restoreRotation(1.0f, priority); + } + } +} + +void Rig::restoreJointRotation(int index, float fraction, float priority) { + if (index != -1 && index < _jointStates.size()) { + _jointStates[index].restoreRotation(fraction, priority); + } +} + +bool Rig::getJointPositionInWorldFrame(int jointIndex, glm::vec3& position, + glm::vec3 translation, glm::quat rotation) const { + if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + return false; + } + // position is in world-frame + position = translation + rotation * _jointStates[jointIndex].getPosition(); + return true; +} + +bool Rig::getJointPosition(int jointIndex, glm::vec3& position) const { + if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + return false; + } + // position is in model-frame + position = extractTranslation(_jointStates[jointIndex].getTransform()); + return true; +} + +bool Rig::getJointRotationInWorldFrame(int jointIndex, glm::quat& result, const glm::quat& rotation) const { + if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + return false; + } + result = rotation * _jointStates[jointIndex].getRotation(); + return true; +} + +bool Rig::getJointRotation(int jointIndex, glm::quat& rotation) const { + if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + return false; + } + rotation = _jointStates[jointIndex].getRotation(); + return true; +} + +bool Rig::getJointCombinedRotation(int jointIndex, glm::quat& result, const glm::quat& rotation) const { + if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + return false; + } + result = rotation * _jointStates[jointIndex].getRotation(); + return true; +} + + +bool Rig::getVisibleJointPositionInWorldFrame(int jointIndex, glm::vec3& position, + glm::vec3 translation, glm::quat rotation) const { + if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + return false; + } + // position is in world-frame + position = translation + rotation * _jointStates[jointIndex].getVisiblePosition(); + return true; +} + +bool Rig::getVisibleJointRotationInWorldFrame(int jointIndex, glm::quat& result, glm::quat rotation) const { + if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + return false; + } + result = rotation * _jointStates[jointIndex].getVisibleRotation(); + return true; +} + +glm::mat4 Rig::getJointTransform(int jointIndex) const { + if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + return glm::mat4(); + } + return _jointStates[jointIndex].getTransform(); +} + +glm::mat4 Rig::getJointVisibleTransform(int jointIndex) const { + if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + return glm::mat4(); + } + return _jointStates[jointIndex].getVisibleTransform(); +} + +void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation) { + if (_enableRig) { + glm::vec3 front = worldRotation * IDENTITY_FRONT; + float forwardSpeed = glm::dot(worldVelocity, front); + float rotationalSpeed = glm::angle(front, _lastFront) / deltaTime; + bool isWalking = std::abs(forwardSpeed) > 0.01f; + bool isTurning = std::abs(rotationalSpeed) > 0.5f; + + // Crude, until we have blending: + isTurning = isTurning && !isWalking; // Only one of walk/turn, walk wins. + isTurning = false; // FIXME + bool isIdle = !isWalking && !isTurning; + auto singleRole = [](bool walking, bool turning, bool idling) { + return walking ? "walk" : (turning ? "turn" : (idling ? "idle" : "")); + }; + QString toStop = singleRole(_isWalking && !isWalking, _isTurning && !isTurning, _isIdle && !isIdle); + if (!toStop.isEmpty()) { + //qCDebug(animation) << "isTurning" << isTurning << "fronts" << front << _lastFront << glm::angle(front, _lastFront) << rotationalSpeed; + stopAnimationByRole(toStop); + } + QString newRole = singleRole(isWalking && !_isWalking, isTurning && !_isTurning, isIdle && !_isIdle); + if (!newRole.isEmpty()) { + startAnimationByRole(newRole); + qCDebug(animation) << deltaTime << ":" << worldVelocity << "." << front << "=> " << forwardSpeed << newRole; + } + + _lastPosition = worldPosition; + _lastFront = front; + _isWalking = isWalking; + _isTurning = isTurning; + _isIdle = isIdle; + } +} + +void Rig::updateAnimations(float deltaTime, glm::mat4 parentTransform) { + int nAnimationsSoFar = 0; + foreach (const AnimationHandlePointer& handle, _runningAnimations) { + handle->setMix(1.0f / ++nAnimationsSoFar); + handle->setPriority(1.0); + handle->simulate(deltaTime); + } + + for (int i = 0; i < _jointStates.size(); i++) { + updateJointState(i, parentTransform); + } + for (int i = 0; i < _jointStates.size(); i++) { + _jointStates[i].resetTransformChanged(); + } +} + +bool Rig::setJointPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, bool useRotation, + int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment, float priority, + const QVector& freeLineage, glm::mat4 parentTransform) { + if (jointIndex == -1 || _jointStates.isEmpty()) { + return false; + } + if (freeLineage.isEmpty()) { + return false; + } + if (lastFreeIndex == -1) { + lastFreeIndex = freeLineage.last(); + } + + // this is a cyclic coordinate descent algorithm: see + // http://www.ryanjuckett.com/programming/animation/21-cyclic-coordinate-descent-in-2d + const int ITERATION_COUNT = 1; + glm::vec3 worldAlignment = alignment; + for (int i = 0; i < ITERATION_COUNT; i++) { + // first, try to rotate the end effector as close as possible to the target rotation, if any + glm::quat endRotation; + if (useRotation) { + JointState& state = _jointStates[jointIndex]; + + state.setRotationInBindFrame(rotation, priority); + endRotation = state.getRotationInBindFrame(); + } + + // then, we go from the joint upwards, rotating the end as close as possible to the target + glm::vec3 endPosition = extractTranslation(_jointStates[jointIndex].getTransform()); + 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)) { + continue; + } + glm::vec3 jointPosition = extractTranslation(state.getTransform()); + glm::vec3 jointVector = endPosition - jointPosition; + glm::quat oldCombinedRotation = state.getRotation(); + glm::quat combinedDelta; + float combinedWeight; + if (useRotation) { + combinedDelta = safeMix(rotation * glm::inverse(endRotation), + rotationBetween(jointVector, position - jointPosition), 0.5f); + combinedWeight = 2.0f; + + } else { + combinedDelta = rotationBetween(jointVector, position - jointPosition); + combinedWeight = 1.0f; + } + if (alignment != glm::vec3() && j > 1) { + jointVector = endPosition - jointPosition; + glm::vec3 positionSum; + for (int k = j - 1; k > 0; k--) { + int index = freeLineage.at(k); + updateJointState(index, parentTransform); + positionSum += extractTranslation(_jointStates.at(index).getTransform()); + } + glm::vec3 projectedCenterOfMass = glm::cross(jointVector, + glm::cross(positionSum / (j - 1.0f) - jointPosition, jointVector)); + glm::vec3 projectedAlignment = glm::cross(jointVector, glm::cross(worldAlignment, jointVector)); + const float LENGTH_EPSILON = 0.001f; + if (glm::length(projectedCenterOfMass) > LENGTH_EPSILON && glm::length(projectedAlignment) > LENGTH_EPSILON) { + combinedDelta = safeMix(combinedDelta, rotationBetween(projectedCenterOfMass, projectedAlignment), + 1.0f / (combinedWeight + 1.0f)); + } + } + state.applyRotationDelta(combinedDelta, true, priority); + glm::quat actualDelta = state.getRotation() * glm::inverse(oldCombinedRotation); + endPosition = actualDelta * jointVector + jointPosition; + if (useRotation) { + endRotation = actualDelta * endRotation; + } + } + } + + // now update the joint states from the top + for (int j = freeLineage.size() - 1; j >= 0; j--) { + updateJointState(freeLineage.at(j), parentTransform); + } + + return true; +} + +void Rig::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::quat& targetRotation, float priority, + const QVector& freeLineage, glm::mat4 parentTransform) { + // NOTE: targetRotation is from bind- to model-frame + + if (endIndex == -1 || _jointStates.isEmpty()) { + return; + } + + if (freeLineage.isEmpty()) { + return; + } + int numFree = freeLineage.size(); + + // store and remember topmost parent transform + glm::mat4 topParentTransform; + { + int index = freeLineage.last(); + const JointState& state = _jointStates.at(index); + const FBXJoint& joint = state.getFBXJoint(); + int parentIndex = joint.parentIndex; + if (parentIndex == -1) { + topParentTransform = parentTransform; + } else { + topParentTransform = _jointStates[parentIndex].getTransform(); + } + } + + // this is a cyclic coordinate descent algorithm: see + // http://www.ryanjuckett.com/programming/animation/21-cyclic-coordinate-descent-in-2d + + // keep track of the position of the end-effector + JointState& endState = _jointStates[endIndex]; + glm::vec3 endPosition = endState.getPosition(); + float distanceToGo = glm::distance(targetPosition, endPosition); + + const int MAX_ITERATION_COUNT = 2; + const float ACCEPTABLE_IK_ERROR = 0.005f; // 5mm + int numIterations = 0; + do { + ++numIterations; + // moving up, rotate each free joint to get endPosition closer to target + for (int j = 1; j < numFree; j++) { + int nextIndex = freeLineage.at(j); + JointState& nextState = _jointStates[nextIndex]; + FBXJoint nextJoint = nextState.getFBXJoint(); + if (! nextJoint.isFree) { + continue; + } + + glm::vec3 pivot = nextState.getPosition(); + glm::vec3 leverArm = endPosition - pivot; + float leverLength = glm::length(leverArm); + if (leverLength < EPSILON) { + continue; + } + glm::quat deltaRotation = rotationBetween(leverArm, targetPosition - pivot); + + // We want to mix the shortest rotation with one that will pull the system down with gravity + // so that limbs don't float unrealistically. To do this we compute a simplified center of mass + // where each joint has unit mass and we don't bother averaging it because we only need direction. + if (j > 1) { + + glm::vec3 centerOfMass(0.0f); + for (int k = 0; k < j; ++k) { + int massIndex = freeLineage.at(k); + centerOfMass += _jointStates[massIndex].getPosition() - pivot; + } + // the gravitational effect is a rotation that tends to align the two cross products + const glm::vec3 worldAlignment = glm::vec3(0.0f, -1.0f, 0.0f); + glm::quat gravityDelta = rotationBetween(glm::cross(centerOfMass, leverArm), + glm::cross(worldAlignment, leverArm)); + + float gravityAngle = glm::angle(gravityDelta); + const float MIN_GRAVITY_ANGLE = 0.1f; + float mixFactor = 0.5f; + if (gravityAngle < MIN_GRAVITY_ANGLE) { + // the final rotation is a mix of the two + mixFactor = 0.5f * gravityAngle / MIN_GRAVITY_ANGLE; + } + deltaRotation = safeMix(deltaRotation, gravityDelta, mixFactor); + } + + // Apply the rotation, but use mixRotationDelta() which blends a bit of the default pose + // in the process. This provides stability to the IK solution for most models. + glm::quat oldNextRotation = nextState.getRotation(); + float mixFactor = 0.03f; + nextState.mixRotationDelta(deltaRotation, mixFactor, priority); + + // measure the result of the rotation which may have been modified by + // blending and constraints + glm::quat actualDelta = nextState.getRotation() * glm::inverse(oldNextRotation); + endPosition = pivot + actualDelta * leverArm; + } + + // recompute transforms from the top down + glm::mat4 currentParentTransform = topParentTransform; + for (int j = numFree - 1; j >= 0; --j) { + JointState& freeState = _jointStates[freeLineage.at(j)]; + freeState.computeTransform(currentParentTransform); + currentParentTransform = freeState.getTransform(); + } + + // measure our success + endPosition = endState.getPosition(); + distanceToGo = glm::distance(targetPosition, endPosition); + } while (numIterations < MAX_ITERATION_COUNT && distanceToGo < ACCEPTABLE_IK_ERROR); + + // set final rotation of the end joint + endState.setRotationInBindFrame(targetRotation, priority, true); +} + +bool Rig::restoreJointPosition(int jointIndex, float fraction, float priority, const QVector& freeLineage) { + if (jointIndex == -1 || _jointStates.isEmpty()) { + return false; + } + + foreach (int index, freeLineage) { + JointState& state = _jointStates[index]; + state.restoreRotation(fraction, priority); + } + return true; +} + +float Rig::getLimbLength(int jointIndex, const QVector& freeLineage, + const glm::vec3 scale, const QVector& fbxJoints) const { + if (jointIndex == -1 || _jointStates.isEmpty()) { + return 0.0f; + } + float length = 0.0f; + float lengthScale = (scale.x + scale.y + scale.z) / 3.0f; + for (int i = freeLineage.size() - 2; i >= 0; i--) { + length += fbxJoints.at(freeLineage.at(i)).distanceToParent * lengthScale; + } + return length; +} + +glm::quat Rig::setJointRotationInBindFrame(int jointIndex, const glm::quat& rotation, float priority, bool constrain) { + glm::quat endRotation; + if (jointIndex == -1 || _jointStates.isEmpty()) { + return endRotation; + } + JointState& state = _jointStates[jointIndex]; + state.setRotationInBindFrame(rotation, priority, constrain); + endRotation = state.getRotationInBindFrame(); + return endRotation; +} + +glm::vec3 Rig::getJointDefaultTranslationInConstrainedFrame(int jointIndex) { + if (jointIndex == -1 || _jointStates.isEmpty()) { + return glm::vec3(); + } + return _jointStates[jointIndex].getDefaultTranslationInConstrainedFrame(); +} + +glm::quat Rig::setJointRotationInConstrainedFrame(int jointIndex, glm::quat targetRotation, float priority, bool constrain, float mix) { + glm::quat endRotation; + if (jointIndex == -1 || _jointStates.isEmpty()) { + return endRotation; + } + JointState& state = _jointStates[jointIndex]; + state.setRotationInConstrainedFrame(targetRotation, priority, constrain, mix); + endRotation = state.getRotationInConstrainedFrame(); + return endRotation; +} + +void Rig::updateVisibleJointStates() { + for (int i = 0; i < _jointStates.size(); i++) { + _jointStates[i].slaveVisibleTransform(); + } +} + +void Rig::setJointTransform(int jointIndex, glm::mat4 newTransform) { + if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + return; + } + _jointStates[jointIndex].setTransform(newTransform); +} + +void Rig::setJointVisibleTransform(int jointIndex, glm::mat4 newTransform) { + if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + return; + } + _jointStates[jointIndex].setVisibleTransform(newTransform); +} + +void Rig::applyJointRotationDelta(int jointIndex, const glm::quat& delta, bool constrain, float priority) { + if (jointIndex == -1 || _jointStates.isEmpty()) { + return; + } + _jointStates[jointIndex].applyRotationDelta(delta, constrain, priority); +} + +glm::quat Rig::getJointDefaultRotationInParentFrame(int jointIndex) { + if (jointIndex == -1 || _jointStates.isEmpty()) { + return glm::quat(); + } + return _jointStates[jointIndex].getDefaultRotationInParentFrame(); +} + +void Rig::updateFromHeadParameters(const HeadParameters& params) { + updateLeanJoint(params.leanJointIndex, params.leanSideways, params.leanForward, params.torsoTwist); + updateNeckJoint(params.neckJointIndex, params.localHeadOrientation, params.leanSideways, params.leanForward, params.torsoTwist); + updateEyeJoint(params.leftEyeJointIndex, params.worldHeadOrientation, params.eyeLookAt, params.eyeSaccade); + updateEyeJoint(params.rightEyeJointIndex, params.worldHeadOrientation, params.eyeLookAt, params.eyeSaccade); +} + +void Rig::updateLeanJoint(int index, float leanSideways, float leanForward, float torsoTwist) { + if (index >= 0 && _jointStates[index].getParentIndex() >= 0) { + auto& parentState = _jointStates[_jointStates[index].getParentIndex()]; + + // get the rotation axes in joint space and use them to adjust the rotation + glm::vec3 xAxis(1.0f, 0.0f, 0.0f); + glm::vec3 yAxis(0.0f, 1.0f, 0.0f); + glm::vec3 zAxis(0.0f, 0.0f, 1.0f); + glm::quat inverse = glm::inverse(parentState.getRotation() * getJointDefaultRotationInParentFrame(index)); + setJointRotationInConstrainedFrame(index, + 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); + } +} + +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(); + + // 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))); + glm::vec3 pitchYawRoll = safeEulerAngles(localHeadOrientation); + glm::vec3 lean = glm::radians(glm::vec3(leanForward, torsoTwist, leanSideways)); + pitchYawRoll -= lean; + setJointRotationInConstrainedFrame(index, + 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); + } +} + +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(); + + // 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)); + 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)); + glm::quat between = rotationBetween(front, lookAt); + 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); + } +} diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h new file mode 100644 index 0000000000..52db16826a --- /dev/null +++ b/libraries/animation/src/Rig.h @@ -0,0 +1,171 @@ +// +// Rig.h +// libraries/animation/src/ +// +// Produces animation data and hip placement for the current timestamp. +// +// Created by Howard Stearns, Seth Alves, Anthony Thibault, Andrew Meadows on 7/15/15. +// Copyright (c) 2015 High Fidelity, Inc. All rights reserved. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +/* + Things we want to be able to do, that I think we cannot do now: + * Stop an animation at a given time so that it can be examined visually or in a test harness. (I think we can already stop animation and set frame to a computed float? But does that move the bones?) + * Play two animations, blending between them. (Current structure just has one, under script control.) + * Fade in an animation over another. + * Apply IK, lean, head pointing or other overrides relative to previous position. + All of this depends on coordinated state. + + TBD: + - What are responsibilities of Animation/AnimationPointer/AnimationCache/AnimationDetails/AnimationObject/AnimationLoop? + Is there common/copied code (e.g., ScriptableAvatar::update)? + - How do attachments interact with the physics of the attached entity? E.g., do hand joints need to reflect held object + physics? + - Is there any current need (i.e., for initial campatability) to have multiple animations per role (e.g., idle) with the + system choosing randomly? + + - Distribute some doc from here to the right files if it turns out to be correct: + - AnimationDetails is a script-useable copy of animation state, analogous to EntityItemProperties, but without anything + equivalent to editEntity. + But what's the intended difference vs AnimationObjection? Maybe AnimationDetails is to Animation as AnimationObject + is to AnimationPointer? + */ + +#ifndef __hifi__Rig__ +#define __hifi__Rig__ + +#include + +#include "JointState.h" // We might want to change this (later) to something that doesn't depend on gpu, fbx and model. -HRS + +class AnimationHandle; +typedef std::shared_ptr AnimationHandlePointer; +// typedef QWeakPointer WeakAnimationHandlePointer; + +class Rig; +typedef std::shared_ptr RigPointer; + +class Rig : public QObject, public std::enable_shared_from_this { + +public: + + struct HeadParameters { + float leanSideways = 0.0f; // degrees + float leanForward = 0.0f; // degrees + float torsoTwist = 0.0f; // degrees + glm::quat localHeadOrientation = glm::quat(); + glm::quat worldHeadOrientation = glm::quat(); + glm::vec3 eyeLookAt = glm::vec3(); // world space + glm::vec3 eyeSaccade = glm::vec3(); // world space + int leanJointIndex = -1; + int neckJointIndex = -1; + int leftEyeJointIndex = -1; + int rightEyeJointIndex = -1; + + void dump() const; + }; + + virtual ~Rig() {} + + RigPointer getRigPointer() { return shared_from_this(); } + + AnimationHandlePointer createAnimationHandle(); + void removeAnimationHandle(const AnimationHandlePointer& handle); + bool removeRunningAnimation(AnimationHandlePointer animationHandle); + void addRunningAnimation(AnimationHandlePointer animationHandle); + bool isRunningAnimation(AnimationHandlePointer animationHandle); + const QList& getRunningAnimations() const { return _runningAnimations; } + void deleteAnimations(); + const QList& getAnimationHandles() const { return _animationHandles; } + void startAnimation(const QString& url, float fps = 30.0f, float priority = 1.0f, bool loop = false, + bool hold = false, float firstFrame = 0.0f, float lastFrame = FLT_MAX, const QStringList& maskedJoints = QStringList()); + void stopAnimation(const QString& url); + void startAnimationByRole(const QString& role, const QString& url = QString(), float fps = 30.0f, + float priority = 1.0f, bool loop = false, bool hold = false, float firstFrame = 0.0f, + float lastFrame = FLT_MAX, const QStringList& maskedJoints = QStringList()); + void stopAnimationByRole(const QString& role); + void addAnimationByRole(const QString& role, const QString& url = QString(), float fps = 30.0f, + float priority = 1.0f, bool loop = false, bool hold = false, float firstFrame = 0.0f, + float lastFrame = FLT_MAX, const QStringList& maskedJoints = QStringList(), bool startAutomatically = false); + + float initJointStates(QVector states, glm::mat4 parentTransform); + bool jointStatesEmpty() { return _jointStates.isEmpty(); }; + int getJointStateCount() const { return _jointStates.size(); } + int indexOfJoint(const QString& jointName) ; + + void initJointTransforms(glm::mat4 parentTransform); + void clearJointTransformTranslation(int jointIndex); + void reset(const QVector& fbxJoints); + bool getJointStateRotation(int index, glm::quat& rotation) const; + void applyJointRotationDelta(int jointIndex, const glm::quat& delta, bool constrain, float priority); + JointState getJointState(int jointIndex) const; // XXX + bool getVisibleJointState(int index, glm::quat& rotation) const; + void clearJointState(int index); + void clearJointStates(); + void clearJointAnimationPriority(int index); + float getJointAnimatinoPriority(int index); + void setJointAnimatinoPriority(int index, float newPriority); + void setJointState(int index, bool valid, const glm::quat& rotation, float priority); + void restoreJointRotation(int index, float fraction, float priority); + bool getJointPositionInWorldFrame(int jointIndex, glm::vec3& position, + glm::vec3 translation, glm::quat rotation) const; + + bool getJointPosition(int jointIndex, glm::vec3& position) const; + bool getJointRotationInWorldFrame(int jointIndex, glm::quat& result, const glm::quat& rotation) const; + bool getJointRotation(int jointIndex, glm::quat& rotation) const; + bool getJointCombinedRotation(int jointIndex, glm::quat& result, const glm::quat& rotation) const; + bool getVisibleJointPositionInWorldFrame(int jointIndex, glm::vec3& position, + glm::vec3 translation, glm::quat rotation) const; + bool getVisibleJointRotationInWorldFrame(int jointIndex, glm::quat& result, glm::quat rotation) const; + glm::mat4 getJointTransform(int jointIndex) const; + void setJointTransform(int jointIndex, glm::mat4 newTransform); + glm::mat4 getJointVisibleTransform(int jointIndex) const; + void setJointVisibleTransform(int jointIndex, glm::mat4 newTransform); + // Start or stop animations as needed. + void computeMotionAnimationState(float deltaTime, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation); + // Regardless of who started the animations or how many, update the joints. + void updateAnimations(float deltaTime, glm::mat4 parentTransform); + bool setJointPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, bool useRotation, + int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment, float priority, + const QVector& freeLineage, glm::mat4 parentTransform); + void inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::quat& targetRotation, float priority, + const QVector& freeLineage, glm::mat4 parentTransform); + bool restoreJointPosition(int jointIndex, float fraction, float priority, const QVector& freeLineage); + float getLimbLength(int jointIndex, const QVector& freeLineage, + const glm::vec3 scale, const QVector& fbxJoints) const; + + glm::quat setJointRotationInBindFrame(int jointIndex, const glm::quat& rotation, float priority, bool constrain = false); + glm::vec3 getJointDefaultTranslationInConstrainedFrame(int jointIndex); + glm::quat setJointRotationInConstrainedFrame(int jointIndex, glm::quat targetRotation, + float priority, bool constrain = false, float mix = 1.0f); + glm::quat getJointDefaultRotationInParentFrame(int jointIndex); + void updateVisibleJointStates(); + + virtual void updateJointState(int index, glm::mat4 parentTransform) = 0; + + void setEnableRig(bool isEnabled) { _enableRig = isEnabled; } + + void updateFromHeadParameters(const HeadParameters& params); + + protected: + + void updateLeanJoint(int index, float leanSideways, float leanForward, float torsoTwist); + void updateNeckJoint(int index, const glm::quat& localHeadOrientation, float leanSideways, float leanForward, float torsoTwist); + void updateEyeJoint(int index, const glm::quat& worldHeadOrientation, const glm::vec3& lookAt, const glm::vec3& saccade); + + QVector _jointStates; + + QList _animationHandles; + QList _runningAnimations; + + bool _enableRig; + bool _isWalking; + bool _isTurning; + bool _isIdle; + glm::vec3 _lastFront; + glm::vec3 _lastPosition; + }; + +#endif /* defined(__hifi__Rig__) */ diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 908310d504..a3d3a72043 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -1037,9 +1037,14 @@ bool AudioClient::outputLocalInjector(bool isStereo, AudioInjector* injector) { localOutput->moveToThread(injector->getLocalBuffer()->thread()); // have it be stopped when that local buffer is about to close - connect(localOutput, &QAudioOutput::stateChanged, this, &AudioClient::audioStateChanged); - connect(this, &AudioClient::audioFinished, localOutput, &QAudioOutput::stop); - connect(this, &AudioClient::audioFinished, injector, &AudioInjector::stop); + // We don't want to stop this localOutput and injector whenever this AudioClient singleton goes idle, + // only when the localOutput does. But the connection is to localOutput, so that it happens on the right thread. + connect(localOutput, &QAudioOutput::stateChanged, localOutput, [=](QAudio::State state) { + if (state == QAudio::IdleState) { + localOutput->stop(); + injector->stop(); + } + }); connect(injector->getLocalBuffer(), &QIODevice::aboutToClose, localOutput, &QAudioOutput::stop); @@ -1358,9 +1363,3 @@ void AudioClient::saveSettings() { windowSecondsForDesiredReduction.set(_receivedAudioStream.getWindowSecondsForDesiredReduction()); repetitionWithFade.set(_receivedAudioStream.getRepetitionWithFade()); } - -void AudioClient::audioStateChanged(QAudio::State state) { - if (state == QAudio::IdleState) { - emit audioFinished(); - } -} diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index a1d08ec540..3205aeda1d 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -209,9 +209,6 @@ protected: deleteLater(); } -private slots: - void audioStateChanged(QAudio::State state); - private: void outputFormatChanged(); diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index adc6ae9de2..2fcdd7f664 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -40,6 +40,7 @@ #include "RenderablePolyVoxEntityItem.h" #include "EntitiesRendererLogging.h" #include "AddressManager.h" +#include "EntityRig.h" EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterface* viewState, AbstractScriptingServicesInterface* scriptingServices) : @@ -695,7 +696,7 @@ Model* EntityTreeRenderer::allocateModel(const QString& url, const QString& coll return model; } - model = new Model(); + model = new Model(std::make_shared()); model->init(); model->setURL(QUrl(url)); model->setCollisionModelURL(QUrl(collisionUrl)); @@ -728,7 +729,7 @@ Model* EntityTreeRenderer::updateModel(Model* original, const QString& newUrl, c } // create the model and correctly initialize it with the new url - model = new Model(); + model = new Model(std::make_shared()); model->init(); model->setURL(QUrl(newUrl)); model->setCollisionModelURL(QUrl(collisionUrl)); diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index d3ee312311..681702bb07 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -35,7 +35,7 @@ #include #include "model/Geometry.h" -#include "gpu/GLBackend.h" +#include "gpu/Context.h" #include "EntityTreeRenderer.h" #include "RenderablePolyVoxEntityItem.h" diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index ee1fdfae45..5a08abbb22 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -8,7 +8,6 @@ #include "RenderableWebEntityItem.h" -#include #include #include #include @@ -24,7 +23,7 @@ #include #include #include -#include +#include #include "EntityTreeRenderer.h" diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp index 2e03266253..bb0a35f7b0 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp @@ -67,7 +67,7 @@ int RenderableZoneEntityItem::readEntitySubclassDataFromBuffer(const unsigned ch } Model* RenderableZoneEntityItem::getModel() { - Model* model = new Model(); + Model* model = new Model(nullptr); model->setIsWireframe(true); model->init(); return model; diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 61253ba6ba..ebe4ac6014 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -114,7 +114,8 @@ _glowLevelChanged(false), _localRenderAlphaChanged(false), _defaultSettings(true), -_naturalDimensions(1.0f, 1.0f, 1.0f) +_naturalDimensions(1.0f, 1.0f, 1.0f), +_naturalPosition(0.0f, 0.0f, 0.0f) { } @@ -128,6 +129,11 @@ void EntityItemProperties::setSittingPoints(const QVector& sitting } } +void EntityItemProperties::calculateNaturalPosition(const glm::vec3& min, const glm::vec3& max) { + glm::vec3 halfDimension = (max - min) / 2.0f; + _naturalPosition = max - halfDimension; +} + bool EntityItemProperties::animationSettingsChanged() const { return _animationSettingsChanged; } @@ -378,6 +384,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(dimensions); if (!skipDefaults) { COPY_PROPERTY_TO_QSCRIPTVALUE(naturalDimensions); // gettable, but not settable + COPY_PROPERTY_TO_QSCRIPTVALUE(naturalPosition); } COPY_PROPERTY_TO_QSCRIPTVALUE(rotation); COPY_PROPERTY_TO_QSCRIPTVALUE(velocity); diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 4532ffd67b..3ce6040d19 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -192,7 +192,10 @@ public: const glm::vec3& getNaturalDimensions() const { return _naturalDimensions; } void setNaturalDimensions(const glm::vec3& value) { _naturalDimensions = value; } - + + const glm::vec3& getNaturalPosition() const { return _naturalPosition; } + void calculateNaturalPosition(const glm::vec3& min, const glm::vec3& max); + const QStringList& getTextureNames() const { return _textureNames; } void setTextureNames(const QStringList& value) { _textureNames = value; } @@ -232,6 +235,7 @@ private: QVector _sittingPoints; QStringList _textureNames; glm::vec3 _naturalDimensions; + glm::vec3 _naturalPosition; }; Q_DECLARE_METATYPE(EntityItemProperties); diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 36fcc17a71..12f5ffe190 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -118,6 +118,7 @@ EntityItemProperties EntityScriptingInterface::getEntityProperties(QUuid identit results.setSittingPoints(geometry->sittingPoints); Extents meshExtents = geometry->getUnscaledMeshExtents(); results.setNaturalDimensions(meshExtents.maximum - meshExtents.minimum); + results.calculateNaturalPosition(meshExtents.minimum, meshExtents.maximum); } } diff --git a/libraries/entities/src/ParticleEffectEntityItem.cpp b/libraries/entities/src/ParticleEffectEntityItem.cpp index 4dfc9dd436..dc5bbb85ed 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.cpp +++ b/libraries/entities/src/ParticleEffectEntityItem.cpp @@ -155,9 +155,9 @@ void ParticleEffectEntityItem::computeAndUpdateDimensions() { float yMin = std::min(yApex, yEnd); // times 2 because dimensions are diameters not radii. - glm::vec3 dims(2.0f * std::max(fabs(xMin), fabs(xMax)), - 2.0f * std::max(fabs(yMin), fabs(yMax)), - 2.0f * std::max(fabs(zMin), fabs(zMax))); + glm::vec3 dims(2.0f * std::max(fabsf(xMin), fabsf(xMax)), + 2.0f * std::max(fabsf(yMin), fabsf(yMax)), + 2.0f * std::max(fabsf(zMin), fabsf(zMax))); EntityItem::setDimensions(dims); } diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index e18b334264..63855af513 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -830,6 +830,56 @@ public: std::vector attributes; }; +gpu::BufferPointer FBXMeshPart::getTrianglesForQuads() const { + // if we've been asked for our triangulation of the original quads, but we don't yet have them + // then create them now. + if (!trianglesForQuadsAvailable) { + trianglesForQuadsAvailable = true; + + quadsAsTrianglesIndicesBuffer = std::make_shared(); + + // QVector quadIndices; // original indices from the FBX mesh + QVector quadsAsTrianglesIndices; // triangle versions of quads converted when first needed + const int INDICES_PER_ORIGINAL_QUAD = 4; + const int INDICES_PER_TRIANGULATED_QUAD = 6; + int numberOfQuads = quadIndices.size() / INDICES_PER_ORIGINAL_QUAD; + + quadsAsTrianglesIndices.resize(numberOfQuads * INDICES_PER_TRIANGULATED_QUAD); + + int originalIndex = 0; + int triangulatedIndex = 0; + for (int fromQuad = 0; fromQuad < numberOfQuads; fromQuad++) { + int i0 = quadIndices[originalIndex + 0]; + int i1 = quadIndices[originalIndex + 1]; + int i2 = quadIndices[originalIndex + 2]; + int i3 = quadIndices[originalIndex + 3]; + + // Sam's recommended triangle slices + // Triangle tri1 = { v0, v1, v3 }; + // Triangle tri2 = { v1, v2, v3 }; + // NOTE: Random guy on the internet's recommended triangle slices + // Triangle tri1 = { v0, v1, v2 }; + // Triangle tri2 = { v2, v3, v0 }; + + quadsAsTrianglesIndices[triangulatedIndex + 0] = i0; + quadsAsTrianglesIndices[triangulatedIndex + 1] = i1; + quadsAsTrianglesIndices[triangulatedIndex + 2] = i3; + + quadsAsTrianglesIndices[triangulatedIndex + 3] = i1; + quadsAsTrianglesIndices[triangulatedIndex + 4] = i2; + quadsAsTrianglesIndices[triangulatedIndex + 5] = i3; + + originalIndex += INDICES_PER_ORIGINAL_QUAD; + triangulatedIndex += INDICES_PER_TRIANGULATED_QUAD; + } + + trianglesForQuadsIndicesCount = INDICES_PER_TRIANGULATED_QUAD * numberOfQuads; + quadsAsTrianglesIndicesBuffer->append(quadsAsTrianglesIndices.size() * sizeof(quint32), (gpu::Byte*)quadsAsTrianglesIndices.data()); + + } + return quadsAsTrianglesIndicesBuffer; +} + void appendIndex(MeshData& data, QVector& indices, int index) { if (index >= data.polygonIndices.size()) { return; @@ -1089,7 +1139,6 @@ ExtractedMesh extractMesh(const FBXNode& object, unsigned int& meshIndex) { appendIndex(data, part.quadIndices, beginIndex++); appendIndex(data, part.quadIndices, beginIndex++); appendIndex(data, part.quadIndices, beginIndex++); - } else { for (int nextIndex = beginIndex + 1;; ) { appendIndex(data, part.triangleIndices, beginIndex); @@ -1199,7 +1248,7 @@ class JointShapeInfo { public: JointShapeInfo() : numVertices(0), sumVertexWeights(0.0f), sumWeightedRadii(0.0f), numVertexWeights(0), - averageVertex(0.0f), boneBegin(0.0f), averageRadius(0.0f) { + boneBegin(0.0f), averageRadius(0.0f) { } // NOTE: the points here are in the "joint frame" which has the "jointEnd" at the origin @@ -1207,9 +1256,8 @@ public: float sumVertexWeights; // sum of all vertex weights float sumWeightedRadii; // sum of weighted vertices int numVertexWeights; // num vertices that contributed to sums - glm::vec3 averageVertex;// average of all mesh vertices (in joint frame) glm::vec3 boneBegin; // parent joint location (in joint frame) - float averageRadius; // average distance from mesh points to averageVertex + float averageRadius; }; class AnimationCurve { @@ -2231,8 +2279,6 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping, joint.boneRadius = 0.0f; joint.inverseBindRotation = joint.inverseDefaultRotation; joint.name = model.name; - joint.shapePosition = glm::vec3(0.0f); - joint.shapeType = INVALID_SHAPE; foreach (const QString& childID, childMap.values(modelID)) { QString type = typeFlags.value(childID); @@ -2502,7 +2548,6 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping, int jointIndex = fbxCluster.jointIndex; FBXJoint& joint = geometry.joints[jointIndex]; glm::mat4 transformJointToMesh = inverseModelTransform * joint.bindTransform; - glm::quat rotateMeshToJoint = glm::inverse(extractRotation(transformJointToMesh)); glm::vec3 boneEnd = extractTranslation(transformJointToMesh); glm::vec3 boneBegin = boneEnd; glm::vec3 boneDirection; @@ -2536,8 +2581,6 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping, jointShapeInfo.sumWeightedRadii += radiusWeight * radiusScale * glm::distance(vertex, boneEnd - boneDirection * proj); ++jointShapeInfo.numVertexWeights; - glm::vec3 vertexInJointFrame = rotateMeshToJoint * (radiusScale * (vertex - boneEnd)); - jointShapeInfo.averageVertex += vertexInJointFrame; ++jointShapeInfo.numVertices; } @@ -2583,7 +2626,6 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping, JointShapeInfo& jointShapeInfo = jointShapeInfos[jointIndex]; glm::mat4 transformJointToMesh = inverseModelTransform * joint.bindTransform; - glm::quat rotateMeshToJoint = glm::inverse(extractRotation(transformJointToMesh)); glm::vec3 boneEnd = extractTranslation(transformJointToMesh); glm::vec3 boneBegin = boneEnd; @@ -2606,9 +2648,6 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping, jointShapeInfo.sumVertexWeights += radiusWeight; jointShapeInfo.sumWeightedRadii += radiusWeight * radiusScale * glm::distance(vertex, boneEnd - boneDirection * proj); ++jointShapeInfo.numVertexWeights; - - glm::vec3 vertexInJointFrame = rotateMeshToJoint * (radiusScale * (vertex - boneEnd)); - jointShapeInfo.averageVertex += vertexInJointFrame; averageVertex += vertex; } int numVertices = extracted.mesh.vertices.size(); @@ -2641,7 +2680,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping, meshIDsToMeshIndices.insert(it.key(), meshIndex); } - // now that all joints have been scanned, compute a collision shape for each joint + // now that all joints have been scanned, compute a radius for each bone glm::vec3 defaultCapsuleAxis(0.0f, 1.0f, 0.0f); for (int i = 0; i < geometry.joints.size(); ++i) { FBXJoint& joint = geometry.joints[i]; @@ -2659,40 +2698,20 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping, joint.boneRadius = jointShapeInfo.sumWeightedRadii / jointShapeInfo.sumVertexWeights; } - // we use a capsule if the joint had ANY mesh vertices successfully projected onto the bone + // the joint is "capsule-like" if it had ANY mesh vertices successfully projected onto the bone // AND its boneRadius is not too close to zero bool collideLikeCapsule = jointShapeInfo.numVertexWeights > 0 && glm::length(jointShapeInfo.boneBegin) > EPSILON; - if (collideLikeCapsule) { - joint.shapeRotation = rotationBetween(defaultCapsuleAxis, jointShapeInfo.boneBegin); - joint.shapePosition = 0.5f * jointShapeInfo.boneBegin; - joint.shapeType = CAPSULE_SHAPE; - } else { - // collide the joint like a sphere - joint.shapeType = SPHERE_SHAPE; - if (jointShapeInfo.numVertices > 0) { - jointShapeInfo.averageVertex /= (float)jointShapeInfo.numVertices; - joint.shapePosition = jointShapeInfo.averageVertex; - } else { - joint.shapePosition = glm::vec3(0.0f); - } + if (!collideLikeCapsule) { + // this joint's mesh did not successfully project onto the bone axis + // so it isn't "capsule-like" and we need to estimate its radius a different way: + // the average radius to the average point. if (jointShapeInfo.numVertexWeights == 0 && jointShapeInfo.numVertices > 0) { - // the bone projection algorithm was not able to compute the joint radius - // so we use an alternative measure jointShapeInfo.averageRadius /= (float)jointShapeInfo.numVertices; joint.boneRadius = jointShapeInfo.averageRadius; } - - float distanceFromEnd = glm::length(joint.shapePosition); - float distanceFromBegin = glm::distance(joint.shapePosition, jointShapeInfo.boneBegin); - if (distanceFromEnd > joint.distanceToParent && distanceFromBegin > joint.distanceToParent) { - // The shape is further from both joint endpoints than the endpoints are from each other - // which probably means the model has a bad transform somewhere. We disable this shape - // by setting its type to INVALID_SHAPE. - joint.shapeType = INVALID_SHAPE; - } } } geometry.palmDirection = parseVec3(mapping.value("palmDirection", "0, -1, 0").toString()); diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h index 198e2d3534..b8a22b0b80 100644 --- a/libraries/fbx/src/FBXReader.h +++ b/libraries/fbx/src/FBXReader.h @@ -25,7 +25,6 @@ #include #include -#include #include #include @@ -78,9 +77,6 @@ public: glm::quat inverseBindRotation; glm::mat4 bindTransform; QString name; - glm::vec3 shapePosition; // in joint frame - glm::quat shapeRotation; // in joint frame - quint8 shapeType; bool isSkeletonJoint; }; @@ -109,9 +105,10 @@ public: class FBXMeshPart { public: - QVector quadIndices; - QVector triangleIndices; - + QVector quadIndices; // original indices from the FBX mesh + QVector triangleIndices; // original indices from the FBX mesh + mutable gpu::BufferPointer quadsAsTrianglesIndicesBuffer; + glm::vec3 diffuseColor; glm::vec3 specularColor; glm::vec3 emissiveColor; @@ -126,6 +123,10 @@ public: QString materialID; model::MaterialPointer _material; + mutable bool trianglesForQuadsAvailable = false; + mutable int trianglesForQuadsIndicesCount = 0; + + gpu::BufferPointer getTrianglesForQuads() const; }; /// A single mesh (with optional blendshapes) extracted from an FBX document. diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index da63b2f47f..3d7cf6582e 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -433,8 +433,6 @@ FBXGeometry OBJReader::readOBJ(QIODevice* device, const QVariantHash& mapping, Q geometry.joints[0].rotationMin = glm::vec3(0, 0, 0); geometry.joints[0].rotationMax = glm::vec3(0, 0, 0); geometry.joints[0].name = "OBJ"; - geometry.joints[0].shapePosition = glm::vec3(0, 0, 0); - geometry.joints[0].shapeType = SPHERE_SHAPE; geometry.joints[0].isSkeletonJoint = true; geometry.jointIndices["x"] = 1; @@ -617,9 +615,6 @@ void fbxDebugDump(const FBXGeometry& fbxgeo) { qCDebug(modelformat) << " inverseBindRotation" << joint.inverseBindRotation; qCDebug(modelformat) << " bindTransform" << joint.bindTransform; qCDebug(modelformat) << " name" << joint.name; - qCDebug(modelformat) << " shapePosition" << joint.shapePosition; - qCDebug(modelformat) << " shapeRotation" << joint.shapeRotation; - qCDebug(modelformat) << " shapeType" << joint.shapeType; qCDebug(modelformat) << " isSkeletonJoint" << joint.isSkeletonJoint; } diff --git a/libraries/gpu/CMakeLists.txt b/libraries/gpu/CMakeLists.txt index 30949b83e1..89939535b0 100644 --- a/libraries/gpu/CMakeLists.txt +++ b/libraries/gpu/CMakeLists.txt @@ -21,23 +21,26 @@ elseif (WIN32) if (USE_NSIGHT) # try to find the Nsight package and add it to the build if we find it + # note that this will also enable NSIGHT profilers in all the projects linking gpu find_package(NSIGHT) if (NSIGHT_FOUND) - include_directories(${NSIGHT_INCLUDE_DIRS}) - add_definitions(-DNSIGHT_FOUND) + target_include_directories(${TARGET_NAME} PUBLIC ${NSIGHT_INCLUDE_DIRS}) + target_compile_definitions(${TARGET_NAME} PUBLIC NSIGHT_FOUND) target_link_libraries(${TARGET_NAME} "${NSIGHT_LIBRARIES}") endif () endif() elseif (ANDROID) target_link_libraries(${TARGET_NAME} "-lGLESv3" "-lEGL") else () + find_package(GLEW REQUIRED) + target_include_directories(${TARGET_NAME} PUBLIC ${GLEW_INCLUDE_DIRS}) + find_package(OpenGL REQUIRED) if (${OPENGL_INCLUDE_DIR}) include_directories(SYSTEM "${OPENGL_INCLUDE_DIR}") endif () - - target_link_libraries(${TARGET_NAME} "${OPENGL_LIBRARY}") - - target_include_directories(${TARGET_NAME} PUBLIC ${OPENGL_INCLUDE_DIR}) + + target_link_libraries(${TARGET_NAME} "${GLEW_LIBRARIES}" "${OPENGL_LIBRARY}") + endif (APPLE) diff --git a/libraries/gpu/src/gpu/Batch.cpp b/libraries/gpu/src/gpu/Batch.cpp index 8815c1bae1..3ecbc3b2f3 100644 --- a/libraries/gpu/src/gpu/Batch.cpp +++ b/libraries/gpu/src/gpu/Batch.cpp @@ -8,13 +8,9 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include + #include "Batch.h" -#include "GPUConfig.h" - -#include - -#include - #if defined(NSIGHT_FOUND) #include "nvToolsExt.h" @@ -292,15 +288,3 @@ void Batch::resetStages() { ADD_COMMAND(resetStages); } -void push_back(Batch::Params& params, const vec3& v) { - params.push_back(v.x); - params.push_back(v.y); - params.push_back(v.z); -} - -void push_back(Batch::Params& params, const vec4& v) { - params.push_back(v.x); - params.push_back(v.y); - params.push_back(v.z); - params.push_back(v.a); -} diff --git a/libraries/gpu/src/gpu/Config.slh b/libraries/gpu/src/gpu/Config.slh index 76be161822..f24b54e5d5 100644 --- a/libraries/gpu/src/gpu/Config.slh +++ b/libraries/gpu/src/gpu/Config.slh @@ -21,10 +21,9 @@ <@def VERSION_HEADER #version 120 #extension GL_EXT_gpu_shader4 : enable@> <@else@> - <@def GPU_FEATURE_PROFILE GPU_LEGACY@> - <@def GPU_TRANSFORM_PROFILE GPU_LEGACY@> - <@def VERSION_HEADER #version 120 -#extension GL_EXT_gpu_shader4 : enable@> + <@def GPU_FEATURE_PROFILE GPU_CORE@> + <@def GPU_TRANSFORM_PROFILE GPU_CORE@> + <@def VERSION_HEADER #version 430 compatibility@> <@endif@> <@endif@> diff --git a/libraries/gpu/src/gpu/Context.cpp b/libraries/gpu/src/gpu/Context.cpp index 6730be33bb..239c460c77 100644 --- a/libraries/gpu/src/gpu/Context.cpp +++ b/libraries/gpu/src/gpu/Context.cpp @@ -10,13 +10,16 @@ // #include "Context.h" -// this include should disappear! as soon as the gpu::Context is in place -#include "GLBackend.h" - using namespace gpu; -Context::Context(Backend* backend) : - _backend(backend) { +Context::CreateBackend Context::_createBackendCallback = nullptr; +Context::MakeProgram Context::_makeProgramCallback = nullptr; +std::once_flag Context::_initialized; + +Context::Context() { + if (_createBackendCallback) { + _backend.reset(_createBackendCallback()); + } } Context::Context(const Context& context) { @@ -26,8 +29,8 @@ Context::~Context() { } bool Context::makeProgram(Shader& shader, const Shader::BindingSet& bindings) { - if (shader.isProgram()) { - return GLBackend::makeProgram(shader, bindings); + if (shader.isProgram() && _makeProgramCallback) { + return _makeProgramCallback(shader, bindings); } return false; } diff --git a/libraries/gpu/src/gpu/Context.h b/libraries/gpu/src/gpu/Context.h index ab7a1d1c11..7158bd1a6d 100644 --- a/libraries/gpu/src/gpu/Context.h +++ b/libraries/gpu/src/gpu/Context.h @@ -12,6 +12,7 @@ #define hifi_gpu_Context_h #include +#include #include "Batch.h" @@ -26,13 +27,12 @@ namespace gpu { class Backend { public: - virtual~ Backend() {}; + virtual void render(Batch& batch) = 0; virtual void syncCache() = 0; virtual void downloadFramebuffer(const FramebufferPointer& srcFramebuffer, const Vec4i& region, QImage& destImage) = 0; - class TransformObject { public: Mat4 _model; @@ -118,7 +118,21 @@ protected: class Context { public: - Context(Backend* backend); + typedef Backend* (*CreateBackend)(); + typedef bool (*MakeProgram)(Shader& shader, const Shader::BindingSet& bindings); + + + // This one call must happen before any context is created or used (Shader::MakeProgram) in order to setup the Backend and any singleton data needed + template + static void init() { + std::call_once(_initialized, [] { + _createBackendCallback = T::createBackend; + _makeProgramCallback = T::makeProgram; + T::init(); + }); + } + + Context(); ~Context(); void render(Batch& batch); @@ -132,13 +146,17 @@ public: protected: Context(const Context& context); + std::unique_ptr _backend; + // This function can only be called by "static Shader::makeProgram()" // makeProgramShader(...) make a program shader ready to be used in a Batch. // It compiles the sub shaders, link them and defines the Slots and their bindings. // If the shader passed is not a program, nothing happens. - static bool makeProgram(Shader& shader, const Shader::BindingSet& bindings = Shader::BindingSet()); + static bool makeProgram(Shader& shader, const Shader::BindingSet& bindings); - std::unique_ptr _backend; + static CreateBackend _createBackendCallback; + static MakeProgram _makeProgramCallback; + static std::once_flag _initialized; friend class Shader; }; diff --git a/libraries/gpu/src/gpu/Format.h b/libraries/gpu/src/gpu/Format.h index 981a560965..b710a44f39 100644 --- a/libraries/gpu/src/gpu/Format.h +++ b/libraries/gpu/src/gpu/Format.h @@ -213,9 +213,6 @@ enum Primitive { TRIANGLES, TRIANGLE_STRIP, TRIANGLE_FAN, - QUADS, - QUAD_STRIP, - NUM_PRIMITIVES, }; diff --git a/libraries/gpu/src/gpu/GLBackend.cpp b/libraries/gpu/src/gpu/GLBackend.cpp index 8db192b827..c74a94e03e 100644 --- a/libraries/gpu/src/gpu/GLBackend.cpp +++ b/libraries/gpu/src/gpu/GLBackend.cpp @@ -8,9 +8,9 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include -#include "GPULogging.h" #include "GLBackendShared.h" + +#include #include using namespace gpu; @@ -63,12 +63,7 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] = (&::gpu::GLBackend::do_glLineWidth), }; -GLBackend::GLBackend() : - _input(), - _transform(), - _pipeline(), - _output() -{ +void GLBackend::init() { static std::once_flag once; std::call_once(once, [] { qCDebug(gpulogging) << "GL Version: " << QString((const char*) glGetString(GL_VERSION)); @@ -94,6 +89,13 @@ GLBackend::GLBackend() : #endif #if defined(Q_OS_LINUX) + GLenum err = glewInit(); + if (GLEW_OK != err) { + /* Problem: glewInit failed, something is seriously wrong. */ + qCDebug(gpulogging, "Error: %s\n", glewGetErrorString(err)); + } + qCDebug(gpulogging, "Status: Using GLEW %s\n", glewGetString(GLEW_VERSION)); + // TODO: Write the correct code for Linux... /* if (wglewGetExtension("WGL_EXT_swap_control")) { int swapInterval = wglGetSwapIntervalEXT(); @@ -101,7 +103,18 @@ GLBackend::GLBackend() : }*/ #endif }); +} +Backend* GLBackend::createBackend() { + return new GLBackend(); +} + +GLBackend::GLBackend() : + _input(), + _transform(), + _pipeline(), + _output() +{ initInput(); initTransform(); } diff --git a/libraries/gpu/src/gpu/GLBackend.h b/libraries/gpu/src/gpu/GLBackend.h index 215e01689b..c0bae76d78 100644 --- a/libraries/gpu/src/gpu/GLBackend.h +++ b/libraries/gpu/src/gpu/GLBackend.h @@ -18,16 +18,21 @@ #include "GPUConfig.h" #include "Context.h" -#include "Batch.h" - namespace gpu { class GLBackend : public Backend { -public: + + // Context Backend static interface required + friend class Context; + static void init(); + static Backend* createBackend(); + static bool makeProgram(Shader& shader, const Shader::BindingSet& bindings); explicit GLBackend(bool syncCache); GLBackend(); +public: + virtual ~GLBackend(); virtual void render(Batch& batch); @@ -49,7 +54,6 @@ public: static void checkGLStackStable(std::function f); - static bool makeProgram(Shader& shader, const Shader::BindingSet& bindings = Shader::BindingSet()); class GLBuffer : public GPUObject { @@ -91,9 +95,9 @@ public: #if (GPU_TRANSFORM_PROFILE == GPU_CORE) #else - GLuint _transformObject_model = -1; - GLuint _transformCamera_viewInverse = -1; - GLuint _transformCamera_viewport = -1; + GLint _transformObject_model = -1; + GLint _transformCamera_viewInverse = -1; + GLint _transformCamera_viewport = -1; #endif GLShader(); diff --git a/libraries/gpu/src/gpu/GLBackendQuery.cpp b/libraries/gpu/src/gpu/GLBackendQuery.cpp index 2bdf7c86ad..5772a09943 100644 --- a/libraries/gpu/src/gpu/GLBackendQuery.cpp +++ b/libraries/gpu/src/gpu/GLBackendQuery.cpp @@ -8,7 +8,6 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "GPULogging.h" #include "GLBackendShared.h" diff --git a/libraries/gpu/src/gpu/GLBackendShader.cpp b/libraries/gpu/src/gpu/GLBackendShader.cpp index fd5bed2e5e..9c0cf7628d 100755 --- a/libraries/gpu/src/gpu/GLBackendShader.cpp +++ b/libraries/gpu/src/gpu/GLBackendShader.cpp @@ -8,7 +8,6 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "GPULogging.h" #include "GLBackendShared.h" #include "Format.h" @@ -542,7 +541,12 @@ ElementResource getFormatFromGLUniform(GLenum gltype) { }; -int makeUniformSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& uniforms, Shader::SlotSet& textures, Shader::SlotSet& samplers) { +int makeUniformSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, + Shader::SlotSet& uniforms, Shader::SlotSet& textures, Shader::SlotSet& samplers +#if (GPU_FEATURE_PROFILE == GPU_LEGACY) + , Shader::SlotSet& fakeBuffers +#endif +) { GLint uniformsCount = 0; #if (GPU_FEATURE_PROFILE == GPU_LEGACY) @@ -583,6 +587,15 @@ int makeUniformSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, S } if (elementResource._resource == Resource::BUFFER) { +#if (GPU_FEATURE_PROFILE == GPU_LEGACY) + // if in legacy profile, we fake the uniform buffer with an array + // this is where we detect it assuming it's explicitely assinged a binding + auto requestedBinding = slotBindings.find(std::string(sname)); + if (requestedBinding != slotBindings.end()) { + // found one buffer! + fakeBuffers.insert(Shader::Slot(sname, location, elementResource._element, elementResource._resource)); + } +#endif uniforms.insert(Shader::Slot(sname, location, elementResource._element, elementResource._resource)); } else { // For texture/Sampler, the location is the actual binding value @@ -640,14 +653,13 @@ int makeUniformBlockSlots(GLuint glprogram, const Shader::BindingSet& slotBindin GLchar name[NAME_LENGTH]; GLint length = 0; GLint size = 0; - GLenum type = 0; GLint binding = -1; glGetActiveUniformBlockiv(glprogram, i, GL_UNIFORM_BLOCK_NAME_LENGTH, &length); glGetActiveUniformBlockName(glprogram, i, NAME_LENGTH, &length, name); glGetActiveUniformBlockiv(glprogram, i, GL_UNIFORM_BLOCK_BINDING, &binding); glGetActiveUniformBlockiv(glprogram, i, GL_UNIFORM_BLOCK_DATA_SIZE, &size); - + GLuint blockIndex = glGetUniformBlockIndex(glprogram, name); // CHeck if there is a requested binding for this block @@ -738,8 +750,12 @@ bool GLBackend::makeProgram(Shader& shader, const Shader::BindingSet& slotBindin Shader::SlotSet uniforms; Shader::SlotSet textures; Shader::SlotSet samplers; +#if (GPU_FEATURE_PROFILE == GPU_CORE) makeUniformSlots(object->_program, slotBindings, uniforms, textures, samplers); - +#else + makeUniformSlots(object->_program, slotBindings, uniforms, textures, samplers, buffers); +#endif + Shader::SlotSet inputs; makeInputSlots(object->_program, slotBindings, inputs); diff --git a/libraries/gpu/src/gpu/GLBackendShared.h b/libraries/gpu/src/gpu/GLBackendShared.h index 27f58fcbe3..7ce54665be 100644 --- a/libraries/gpu/src/gpu/GLBackendShared.h +++ b/libraries/gpu/src/gpu/GLBackendShared.h @@ -11,11 +11,10 @@ #ifndef hifi_gpu_GLBackend_Shared_h #define hifi_gpu_GLBackend_Shared_h -#include "GLBackend.h" - #include -#include "Batch.h" +#include "GPULogging.h" +#include "GLBackend.h" static const GLenum _primitiveToGLmode[gpu::NUM_PRIMITIVES] = { GL_POINTS, @@ -24,8 +23,6 @@ static const GLenum _primitiveToGLmode[gpu::NUM_PRIMITIVES] = { GL_TRIANGLES, GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN, - GL_QUADS, - GL_QUAD_STRIP, }; static const GLenum _elementTypeToGLType[gpu::NUM_TYPES] = { diff --git a/libraries/gpu/src/gpu/GLBackendState.cpp b/libraries/gpu/src/gpu/GLBackendState.cpp index bd683e0136..22c61b2365 100644 --- a/libraries/gpu/src/gpu/GLBackendState.cpp +++ b/libraries/gpu/src/gpu/GLBackendState.cpp @@ -484,6 +484,7 @@ void GLBackend::syncPipelineStateCache() { glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS); // Point size is always on + glHint(GL_POINT_SMOOTH_HINT, GL_NICEST); glEnable(GL_PROGRAM_POINT_SIZE_EXT); glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); diff --git a/libraries/gpu/src/gpu/GPUConfig.h b/libraries/gpu/src/gpu/GPUConfig.h index 1d092dbc6a..5590c4cc8d 100644 --- a/libraries/gpu/src/gpu/GPUConfig.h +++ b/libraries/gpu/src/gpu/GPUConfig.h @@ -34,11 +34,11 @@ #elif defined(ANDROID) #else -#include -#include -#define GPU_FEATURE_PROFILE GPU_LEGACY -#define GPU_TRANSFORM_PROFILE GPU_LEGACY +#include + +#define GPU_FEATURE_PROFILE GPU_CORE +#define GPU_TRANSFORM_PROFILE GPU_CORE #endif diff --git a/libraries/model/src/model/Geometry.h b/libraries/model/src/model/Geometry.h index 16ebb60b72..5ef414a2d1 100755 --- a/libraries/model/src/model/Geometry.h +++ b/libraries/model/src/model/Geometry.h @@ -71,7 +71,7 @@ public: LINE_STRIP, TRIANGLES, TRIANGLE_STRIP, - QUADS, + QUADS, // NOTE: These must be translated to triangles before rendering QUAD_STRIP, NUM_TOPOLOGIES, diff --git a/libraries/model/src/model/Material.h b/libraries/model/src/model/Material.h index 392fd918a1..a1a17d29e9 100755 --- a/libraries/model/src/model/Material.h +++ b/libraries/model/src/model/Material.h @@ -13,13 +13,13 @@ #include #include +#include #include #include "gpu/Resource.h" #include "gpu/Texture.h" -#include namespace model { diff --git a/libraries/networking/src/NLPacket.cpp b/libraries/networking/src/NLPacket.cpp index 7a6503dbc3..7ec10fc4ce 100644 --- a/libraries/networking/src/NLPacket.cpp +++ b/libraries/networking/src/NLPacket.cpp @@ -46,7 +46,7 @@ std::unique_ptr NLPacket::create(PacketType::Value type, qint64 size) return packet; } -std::unique_ptr NLPacket::fromReceivedPacket(std::unique_ptr data, qint64 size, +std::unique_ptr NLPacket::fromReceivedPacket(std::unique_ptr data, qint64 size, const HifiSockAddr& senderSockAddr) { // Fail with null data Q_ASSERT(data); @@ -85,7 +85,7 @@ NLPacket::NLPacket(const NLPacket& other) : Packet(other) { } -NLPacket::NLPacket(std::unique_ptr data, qint64 size, const HifiSockAddr& senderSockAddr) : +NLPacket::NLPacket(std::unique_ptr data, qint64 size, const HifiSockAddr& senderSockAddr) : Packet(std::move(data), size, senderSockAddr) { adjustPayloadStartAndCapacity(); diff --git a/libraries/networking/src/NLPacket.h b/libraries/networking/src/NLPacket.h index 669278ed65..6ebf15ffec 100644 --- a/libraries/networking/src/NLPacket.h +++ b/libraries/networking/src/NLPacket.h @@ -20,7 +20,7 @@ class NLPacket : public Packet { Q_OBJECT public: static std::unique_ptr create(PacketType::Value type, qint64 size = -1); - static std::unique_ptr fromReceivedPacket(std::unique_ptr data, qint64 size, + static std::unique_ptr fromReceivedPacket(std::unique_ptr data, qint64 size, const HifiSockAddr& senderSockAddr); // Provided for convenience, try to limit use static std::unique_ptr createCopy(const NLPacket& other); @@ -45,7 +45,7 @@ protected: NLPacket(PacketType::Value type); NLPacket(PacketType::Value type, qint64 size); - NLPacket(std::unique_ptr data, qint64 size, const HifiSockAddr& senderSockAddr); + NLPacket(std::unique_ptr data, qint64 size, const HifiSockAddr& senderSockAddr); NLPacket(const NLPacket& other); void readSourceID(); diff --git a/libraries/networking/src/PacketReceiver.cpp b/libraries/networking/src/PacketReceiver.cpp index 5fc327673d..b1a77d4c39 100644 --- a/libraries/networking/src/PacketReceiver.cpp +++ b/libraries/networking/src/PacketReceiver.cpp @@ -243,7 +243,7 @@ void PacketReceiver::processDatagrams() { while (nodeList && nodeList->getNodeSocket().hasPendingDatagrams()) { // setup a buffer to read the packet into int packetSizeWithHeader = nodeList->getNodeSocket().pendingDatagramSize(); - std::unique_ptr buffer = std::unique_ptr(new char[packetSizeWithHeader]); + auto buffer = std::unique_ptr(new char[packetSizeWithHeader]); // if we're supposed to drop this packet then break out here if (_shouldDropPackets) { diff --git a/libraries/networking/src/udt/Packet.cpp b/libraries/networking/src/udt/Packet.cpp index 02a44c4a4f..13f8a39e26 100644 --- a/libraries/networking/src/udt/Packet.cpp +++ b/libraries/networking/src/udt/Packet.cpp @@ -31,7 +31,7 @@ std::unique_ptr Packet::create(PacketType::Value type, qint64 size) { return packet; } -std::unique_ptr Packet::fromReceivedPacket(std::unique_ptr data, qint64 size, const HifiSockAddr& senderSockAddr) { +std::unique_ptr Packet::fromReceivedPacket(std::unique_ptr data, qint64 size, const HifiSockAddr& senderSockAddr) { // Fail with invalid size Q_ASSERT(size >= 0); @@ -82,7 +82,7 @@ Packet::Packet(PacketType::Value type, qint64 size) : } } -Packet::Packet(std::unique_ptr data, qint64 size, const HifiSockAddr& senderSockAddr) : +Packet::Packet(std::unique_ptr data, qint64 size, const HifiSockAddr& senderSockAddr) : _packetSize(size), _packet(std::move(data)), _senderSockAddr(senderSockAddr) @@ -110,7 +110,7 @@ Packet& Packet::operator=(const Packet& other) { _type = other._type; _packetSize = other._packetSize; - _packet = std::unique_ptr(new char[_packetSize]); + _packet = std::unique_ptr(new char[_packetSize]); memcpy(_packet.get(), other._packet.get(), _packetSize); _payloadStart = _packet.get() + (other._payloadStart - other._packet.get()); diff --git a/libraries/networking/src/udt/Packet.h b/libraries/networking/src/udt/Packet.h index b4c53b8165..91b5974e09 100644 --- a/libraries/networking/src/udt/Packet.h +++ b/libraries/networking/src/udt/Packet.h @@ -27,7 +27,7 @@ public: static const qint64 PACKET_WRITE_ERROR; static std::unique_ptr create(PacketType::Value type, qint64 size = -1); - static std::unique_ptr fromReceivedPacket(std::unique_ptr data, qint64 size, const HifiSockAddr& senderSockAddr); + static std::unique_ptr fromReceivedPacket(std::unique_ptr data, qint64 size, const HifiSockAddr& senderSockAddr); // Provided for convenience, try to limit use static std::unique_ptr createCopy(const Packet& other); @@ -88,7 +88,7 @@ public: protected: Packet(PacketType::Value type, qint64 size); - Packet(std::unique_ptr data, qint64 size, const HifiSockAddr& senderSockAddr); + Packet(std::unique_ptr data, qint64 size, const HifiSockAddr& senderSockAddr); Packet(const Packet& other); Packet& operator=(const Packet& other); Packet(Packet&& other); @@ -109,7 +109,7 @@ protected: PacketVersion _version; // Packet version qint64 _packetSize = 0; // Total size of the allocated memory - std::unique_ptr _packet; // Allocated memory + std::unique_ptr _packet; // Allocated memory char* _payloadStart = nullptr; // Start of the payload qint64 _payloadCapacity = 0; // Total capacity of the payload diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 720f765ae4..5f0afd37d1 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -9,15 +9,12 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -// include this before QOpenGLFramebufferObject, which includes an earlier version of OpenGL -#include - -#include #include #include #include +#include #include "gpu/StandardShaderLib.h" #include "AmbientOcclusionEffect.h" @@ -176,7 +173,6 @@ void AmbientOcclusion::run(const render::SceneContextPointer& sceneContext, cons assert(renderContext->args); assert(renderContext->args->_viewFrustum); RenderArgs* args = renderContext->args; - auto& scene = sceneContext->_scene; gpu::Batch batch; diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index c14bbfcb1d..3fc0a4c46a 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -12,12 +12,12 @@ #include "DeferredLightingEffect.h" #include -#include +#include +#include + #include #include #include -#include -#include #include "AbstractViewStateInterface.h" #include "GeometryCache.h" @@ -291,7 +291,7 @@ void DeferredLightingEffect::render(RenderArgs* args) { locations = &_directionalAmbientSphereLightCascadedShadowMapLocations; } batch.setPipeline(program); - batch._glUniform3fv(locations->shadowDistances, 1, (const GLfloat*) &_viewState->getShadowDistances()); + batch._glUniform3fv(locations->shadowDistances, 1, (const float*) &_viewState->getShadowDistances()); } else { if (useSkyboxCubemap) { @@ -325,7 +325,7 @@ void DeferredLightingEffect::render(RenderArgs* args) { sh = (*_skybox->getCubemap()->getIrradiance()); } for (int i =0; i ambientSphere + i, 1, (const GLfloat*) (&sh) + i * 4); + batch._glUniform4fv(locations->ambientSphere + i, 1, (const float*) (&sh) + i * 4); } } @@ -340,7 +340,7 @@ void DeferredLightingEffect::render(RenderArgs* args) { if (_atmosphere && (locations->atmosphereBufferUnit >= 0)) { batch.setUniformBuffer(locations->atmosphereBufferUnit, _atmosphere->getDataBuffer()); } - batch._glUniformMatrix4fv(locations->invViewMat, 1, false, reinterpret_cast< const GLfloat* >(&invViewMat)); + batch._glUniformMatrix4fv(locations->invViewMat, 1, false, reinterpret_cast< const float* >(&invViewMat)); } float left, right, bottom, top, nearVal, farVal; @@ -419,9 +419,9 @@ void DeferredLightingEffect::render(RenderArgs* args) { batch._glUniform2f(_pointLightLocations.depthTexCoordOffset, depthTexCoordOffsetS, depthTexCoordOffsetT); batch._glUniform2f(_pointLightLocations.depthTexCoordScale, depthTexCoordScaleS, depthTexCoordScaleT); - batch._glUniformMatrix4fv(_pointLightLocations.invViewMat, 1, false, reinterpret_cast< const GLfloat* >(&invViewMat)); + batch._glUniformMatrix4fv(_pointLightLocations.invViewMat, 1, false, reinterpret_cast< const float* >(&invViewMat)); - batch._glUniformMatrix4fv(_pointLightLocations.texcoordMat, 1, false, reinterpret_cast< const GLfloat* >(&texcoordMat)); + batch._glUniformMatrix4fv(_pointLightLocations.texcoordMat, 1, false, reinterpret_cast< const float* >(&texcoordMat)); for (auto lightID : _pointLights) { auto& light = _allocatedLights[lightID]; @@ -467,9 +467,9 @@ void DeferredLightingEffect::render(RenderArgs* args) { batch._glUniform2f(_spotLightLocations.depthTexCoordOffset, depthTexCoordOffsetS, depthTexCoordOffsetT); batch._glUniform2f(_spotLightLocations.depthTexCoordScale, depthTexCoordScaleS, depthTexCoordScaleT); - batch._glUniformMatrix4fv(_spotLightLocations.invViewMat, 1, false, reinterpret_cast< const GLfloat* >(&invViewMat)); + batch._glUniformMatrix4fv(_spotLightLocations.invViewMat, 1, false, reinterpret_cast< const float* >(&invViewMat)); - batch._glUniformMatrix4fv(_spotLightLocations.texcoordMat, 1, false, reinterpret_cast< const GLfloat* >(&texcoordMat)); + batch._glUniformMatrix4fv(_spotLightLocations.texcoordMat, 1, false, reinterpret_cast< const float* >(&texcoordMat)); for (auto lightID : _spotLights) { auto light = _allocatedLights[lightID]; @@ -489,7 +489,7 @@ void DeferredLightingEffect::render(RenderArgs* args) { if ((eyeHalfPlaneDistance > -nearRadius) && (glm::distance(eyePoint, glm::vec3(light->getPosition())) < expandedRadius + nearRadius)) { coneParam.w = 0.0f; - batch._glUniform4fv(_spotLightLocations.coneParam, 1, reinterpret_cast< const GLfloat* >(&coneParam)); + batch._glUniform4fv(_spotLightLocations.coneParam, 1, reinterpret_cast< const float* >(&coneParam)); Transform model; model.setTranslation(glm::vec3(0.0f, 0.0f, -1.0f)); @@ -509,7 +509,7 @@ void DeferredLightingEffect::render(RenderArgs* args) { batch.setViewTransform(viewMat); } else { coneParam.w = 1.0f; - batch._glUniform4fv(_spotLightLocations.coneParam, 1, reinterpret_cast< const GLfloat* >(&coneParam)); + batch._glUniform4fv(_spotLightLocations.coneParam, 1, reinterpret_cast< const float* >(&coneParam)); Transform model; model.setTranslation(light->getPosition()); @@ -595,9 +595,9 @@ void DeferredLightingEffect::loadLightProgram(const char* vertSource, const char slotBindings.insert(gpu::Shader::Binding(std::string("depthMap"), 3)); slotBindings.insert(gpu::Shader::Binding(std::string("shadowMap"), 4)); slotBindings.insert(gpu::Shader::Binding(std::string("skyboxMap"), 5)); - const GLint LIGHT_GPU_SLOT = 3; + const int LIGHT_GPU_SLOT = 3; slotBindings.insert(gpu::Shader::Binding(std::string("lightBuffer"), LIGHT_GPU_SLOT)); - const GLint ATMOSPHERE_GPU_SLOT = 4; + const int ATMOSPHERE_GPU_SLOT = 4; slotBindings.insert(gpu::Shader::Binding(std::string("atmosphereBufferUnit"), ATMOSPHERE_GPU_SLOT)); gpu::Shader::makeProgram(*program, slotBindings); @@ -614,13 +614,8 @@ void DeferredLightingEffect::loadLightProgram(const char* vertSource, const char locations.texcoordMat = program->getUniforms().findLocation("texcoordMat"); locations.coneParam = program->getUniforms().findLocation("coneParam"); -#if (GPU_FEATURE_PROFILE == GPU_CORE) locations.lightBufferUnit = program->getBuffers().findLocation("lightBuffer"); locations.atmosphereBufferUnit = program->getBuffers().findLocation("atmosphereBufferUnit"); -#else - locations.lightBufferUnit = program->getUniforms().findLocation("lightBuffer"); - locations.atmosphereBufferUnit = program->getUniforms().findLocation("atmosphereBufferUnit"); -#endif auto state = std::make_shared(); if (lightVolume) { @@ -677,10 +672,10 @@ model::MeshPointer DeferredLightingEffect::getSpotLightMesh() { int ringFloatOffset = slices * 3; - GLfloat* vertexData = new GLfloat[verticesSize]; - GLfloat* vertexRing0 = vertexData; - GLfloat* vertexRing1 = vertexRing0 + ringFloatOffset; - GLfloat* vertexRing2 = vertexRing1 + ringFloatOffset; + float* vertexData = new float[verticesSize]; + float* vertexRing0 = vertexData; + float* vertexRing1 = vertexRing0 + ringFloatOffset; + float* vertexRing2 = vertexRing1 + ringFloatOffset; for (int i = 0; i < slices; i++) { float theta = TWO_PI * i / slices; @@ -746,7 +741,7 @@ model::MeshPointer DeferredLightingEffect::getSpotLightMesh() { *(index++) = capVertex; } - _spotLightMesh->setIndexBuffer(gpu::BufferView(new gpu::Buffer(sizeof(GLushort) * indices, (gpu::Byte*) indexData), gpu::Element::INDEX_UINT16)); + _spotLightMesh->setIndexBuffer(gpu::BufferView(new gpu::Buffer(sizeof(unsigned short) * indices, (gpu::Byte*) indexData), gpu::Element::INDEX_UINT16)); delete[] indexData; model::Mesh::Part part(0, indices, 0, model::Mesh::TRIANGLES); diff --git a/libraries/render-utils/src/FramebufferCache.cpp b/libraries/render-utils/src/FramebufferCache.cpp index 601d99108d..d6ebd001d2 100644 --- a/libraries/render-utils/src/FramebufferCache.cpp +++ b/libraries/render-utils/src/FramebufferCache.cpp @@ -18,7 +18,6 @@ #include #include #include -#include #include "RenderUtilsLogging.h" static QQueue _cachedFramebuffers; diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index 26b9a933c4..c793c9dc82 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -9,22 +9,22 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -// include this before QOpenGLBuffer, which includes an earlier version of OpenGL +#include "GeometryCache.h" + #include #include #include #include -#include -#include - #include #include +#include +#include + #include "TextureCache.h" #include "RenderUtilsLogging.h" -#include "GeometryCache.h" #include "standardTransformPNTC_vert.h" #include "standardDrawTexture_frag.h" @@ -1012,12 +1012,12 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec2& minCorner, co } const int FLOATS_PER_VERTEX = 2; // vertices - const int vertices = 4; + const int VERTICES = 4; // 1 quad = 4 vertices if (!details.isCreated) { details.isCreated = true; - details.vertices = vertices; + details.vertices = VERTICES; details.vertexSize = FLOATS_PER_VERTEX; auto verticesBuffer = std::make_shared(); @@ -1037,11 +1037,12 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec2& minCorner, co details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(1)._stride); - float vertexBuffer[vertices * FLOATS_PER_VERTEX] = { + float vertexBuffer[VERTICES * FLOATS_PER_VERTEX] = { minCorner.x, minCorner.y, maxCorner.x, minCorner.y, + minCorner.x, maxCorner.y, maxCorner.x, maxCorner.y, - minCorner.x, maxCorner.y }; + }; const int NUM_COLOR_SCALARS_PER_QUAD = 4; int compactColor = ((int(color.x * 255.0f) & 0xFF)) | @@ -1050,14 +1051,13 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec2& minCorner, co ((int(color.w * 255.0f) & 0xFF) << 24); int colors[NUM_COLOR_SCALARS_PER_QUAD] = { compactColor, compactColor, compactColor, compactColor }; - details.verticesBuffer->append(sizeof(vertexBuffer), (gpu::Byte*) vertexBuffer); details.colorBuffer->append(sizeof(colors), (gpu::Byte*) colors); } batch.setInputFormat(details.streamFormat); batch.setInputStream(0, *details.stream); - batch.draw(gpu::QUADS, 4, 0); + batch.draw(gpu::TRIANGLE_STRIP, 4, 0); } void GeometryCache::renderUnitCube(gpu::Batch& batch) { @@ -1102,23 +1102,25 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec2& minCorner, co } const int FLOATS_PER_VERTEX = 2 * 2; // text coords & vertices - const int vertices = 4; + const int VERTICES = 4; // 1 quad = 4 vertices const int NUM_POS_COORDS = 2; const int VERTEX_TEXCOORD_OFFSET = NUM_POS_COORDS * sizeof(float); if (!details.isCreated) { details.isCreated = true; - details.vertices = vertices; + details.vertices = VERTICES; details.vertexSize = FLOATS_PER_VERTEX; auto verticesBuffer = std::make_shared(); auto colorBuffer = std::make_shared(); + auto streamFormat = std::make_shared(); auto stream = std::make_shared(); details.verticesBuffer = verticesBuffer; details.colorBuffer = colorBuffer; + details.streamFormat = streamFormat; details.stream = stream; @@ -1130,11 +1132,12 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec2& minCorner, co details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(1)._stride); - float vertexBuffer[vertices * FLOATS_PER_VERTEX] = { + float vertexBuffer[VERTICES * FLOATS_PER_VERTEX] = { minCorner.x, minCorner.y, texCoordMinCorner.x, texCoordMinCorner.y, maxCorner.x, minCorner.y, texCoordMaxCorner.x, texCoordMinCorner.y, + minCorner.x, maxCorner.y, texCoordMinCorner.x, texCoordMaxCorner.y, maxCorner.x, maxCorner.y, texCoordMaxCorner.x, texCoordMaxCorner.y, - minCorner.x, maxCorner.y, texCoordMinCorner.x, texCoordMaxCorner.y }; + }; const int NUM_COLOR_SCALARS_PER_QUAD = 4; @@ -1144,14 +1147,13 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec2& minCorner, co ((int(color.w * 255.0f) & 0xFF) << 24); int colors[NUM_COLOR_SCALARS_PER_QUAD] = { compactColor, compactColor, compactColor, compactColor }; - details.verticesBuffer->append(sizeof(vertexBuffer), (gpu::Byte*) vertexBuffer); details.colorBuffer->append(sizeof(colors), (gpu::Byte*) colors); } batch.setInputFormat(details.streamFormat); batch.setInputStream(0, *details.stream); - batch.draw(gpu::QUADS, 4, 0); + batch.draw(gpu::TRIANGLE_STRIP, 4, 0); } void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& minCorner, const glm::vec3& maxCorner, const glm::vec4& color, int id) { @@ -1177,21 +1179,23 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& minCorner, co } const int FLOATS_PER_VERTEX = 3; // vertices - const int vertices = 4; + const int VERTICES = 4; // 1 quad = 4 vertices if (!details.isCreated) { details.isCreated = true; - details.vertices = vertices; + details.vertices = VERTICES; details.vertexSize = FLOATS_PER_VERTEX; auto verticesBuffer = std::make_shared(); auto colorBuffer = std::make_shared(); + auto streamFormat = std::make_shared(); auto stream = std::make_shared(); details.verticesBuffer = verticesBuffer; details.colorBuffer = colorBuffer; + details.streamFormat = streamFormat; details.stream = stream; @@ -1202,11 +1206,12 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& minCorner, co details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(1)._stride); - float vertexBuffer[vertices * FLOATS_PER_VERTEX] = { + float vertexBuffer[VERTICES * FLOATS_PER_VERTEX] = { minCorner.x, minCorner.y, minCorner.z, maxCorner.x, minCorner.y, minCorner.z, + minCorner.x, maxCorner.y, maxCorner.z, maxCorner.x, maxCorner.y, maxCorner.z, - minCorner.x, maxCorner.y, maxCorner.z }; + }; const int NUM_COLOR_SCALARS_PER_QUAD = 4; int compactColor = ((int(color.x * 255.0f) & 0xFF)) | @@ -1215,14 +1220,13 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& minCorner, co ((int(color.w * 255.0f) & 0xFF) << 24); int colors[NUM_COLOR_SCALARS_PER_QUAD] = { compactColor, compactColor, compactColor, compactColor }; - details.verticesBuffer->append(sizeof(vertexBuffer), (gpu::Byte*) vertexBuffer); details.colorBuffer->append(sizeof(colors), (gpu::Byte*) colors); } batch.setInputFormat(details.streamFormat); batch.setInputStream(0, *details.stream); - batch.draw(gpu::QUADS, 4, 0); + batch.draw(gpu::TRIANGLE_STRIP, 4, 0); } void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& topLeft, const glm::vec3& bottomLeft, @@ -1267,14 +1271,14 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& topLeft, cons } const int FLOATS_PER_VERTEX = 3 + 2; // 3d vertices + text coords - const int vertices = 4; + const int VERTICES = 4; // 1 quad = 4 vertices const int NUM_POS_COORDS = 3; const int VERTEX_TEXCOORD_OFFSET = NUM_POS_COORDS * sizeof(float); if (!details.isCreated) { details.isCreated = true; - details.vertices = vertices; + details.vertices = VERTICES; details.vertexSize = FLOATS_PER_VERTEX; // NOTE: this isn't used for BatchItemDetails maybe we can get rid of it auto verticesBuffer = std::make_shared(); @@ -1295,10 +1299,10 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& topLeft, cons details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(1)._stride); - float vertexBuffer[vertices * FLOATS_PER_VERTEX] = { - topLeft.x, topLeft.y, topLeft.z, texCoordTopLeft.x, texCoordTopLeft.y, + float vertexBuffer[VERTICES * FLOATS_PER_VERTEX] = { bottomLeft.x, bottomLeft.y, bottomLeft.z, texCoordBottomLeft.x, texCoordBottomLeft.y, bottomRight.x, bottomRight.y, bottomRight.z, texCoordBottomRight.x, texCoordBottomRight.y, + topLeft.x, topLeft.y, topLeft.z, texCoordTopLeft.x, texCoordTopLeft.y, topRight.x, topRight.y, topRight.z, texCoordTopRight.x, texCoordTopRight.y, }; @@ -1315,7 +1319,7 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& topLeft, cons batch.setInputFormat(details.streamFormat); batch.setInputStream(0, *details.stream); - batch.draw(gpu::QUADS, 4, 0); + batch.draw(gpu::TRIANGLE_STRIP, 4, 0); } void GeometryCache::renderDashedLine(gpu::Batch& batch, const glm::vec3& start, const glm::vec3& end, const glm::vec4& color, int id) { @@ -1696,7 +1700,7 @@ NetworkGeometry::NetworkGeometry(const QUrl& url, const QSharedPointer(), -1, 0.0f, 0.0f, glm::vec3(), glm::mat4(), glm::quat(), glm::quat(), glm::quat(), glm::mat4(), glm::mat4(), glm::vec3(), glm::vec3(), glm::quat(), glm::quat(), - glm::mat4(), QString(""), glm::vec3(), glm::quat(), SHAPE_TYPE_NONE, false}; + glm::mat4(), QString(""), false}; _geometry.joints.append(joint); _geometry.leftEyeJointIndex = -1; _geometry.rightEyeJointIndex = -1; diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 67eb85edfc..51f737505f 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -18,20 +18,18 @@ #include #include -#include -#include #include #include -#include "PhysicsEntity.h" #include #include #include +#include +#include #include "AbstractViewStateInterface.h" #include "AnimationHandle.h" #include "DeferredLightingEffect.h" #include "Model.h" -#include "RenderUtilsLogging.h" #include "model_vert.h" #include "model_shadow_vert.h" @@ -60,7 +58,7 @@ static int weakNetworkGeometryPointerTypeId = qRegisterMetaType >(); float Model::FAKE_DIMENSION_PLACEHOLDER = -1.0f; -Model::Model(QObject* parent) : +Model::Model(RigPointer rig, QObject* parent) : QObject(parent), _scale(1.0f, 1.0f, 1.0f), _scaleToFit(false), @@ -69,6 +67,7 @@ Model::Model(QObject* parent) : _snapModelToRegistrationPoint(false), _snappedToRegistrationPoint(false), _showTrueJointTransforms(true), + _cauterizeBones(false), _lodDistance(0.0f), _pupilDilation(0.0f), _url("http://invalid.com"), @@ -81,13 +80,13 @@ Model::Model(QObject* parent) : _calculatedMeshTrianglesValid(false), _meshGroupsKnown(false), _isWireframe(false), - _renderCollisionHull(false) { - + _renderCollisionHull(false), + _rig(rig) { // we may have been created in the network thread, but we live in the main thread if (_viewState) { moveToThread(_viewState->getMainThread()); } - + setSnapModelToRegistrationPoint(true, glm::vec3(0.5f)); } @@ -96,7 +95,7 @@ Model::~Model() { } Model::RenderPipelineLib Model::_renderPipelineLib; -const GLint MATERIAL_GPU_SLOT = 3; +const int MATERIAL_GPU_SLOT = 3; void Model::RenderPipelineLib::addRenderPipeline(Model::RenderKey key, gpu::ShaderPointer& vertexShader, @@ -189,13 +188,9 @@ void Model::RenderPipelineLib::initLocations(gpu::ShaderPointer& program, Model: locations.specularTextureUnit = program->getTextures().findLocation("specularMap"); locations.emissiveTextureUnit = program->getTextures().findLocation("emissiveMap"); -#if (GPU_FEATURE_PROFILE == GPU_CORE) locations.materialBufferUnit = program->getBuffers().findLocation("materialBuffer"); locations.lightBufferUnit = program->getBuffers().findLocation("lightBuffer"); -#else - locations.materialBufferUnit = program->getUniforms().findLocation("materialBuffer"); - locations.lightBufferUnit = program->getUniforms().findLocation("lightBuffer"); -#endif + locations.clusterMatrices = program->getUniforms().findLocation("clusterMatrices"); locations.clusterIndices = program->getInputs().findLocation("clusterIndices");; @@ -223,10 +218,6 @@ void Model::setScaleInternal(const glm::vec3& scale) { if (relativeDeltaScale > ONE_PERCENT || scaleLength < EPSILON) { _scale = scale; initJointTransforms(); - if (_shapes.size() > 0) { - clearShapes(); - buildShapes(); - } } } @@ -252,22 +243,12 @@ QVector Model::createJointStates(const FBXGeometry& geometry) { }; void Model::initJointTransforms() { - // compute model transforms - int numStates = _jointStates.size(); - for (int i = 0; i < numStates; ++i) { - JointState& state = _jointStates[i]; - const FBXJoint& joint = state.getFBXJoint(); - int parentIndex = joint.parentIndex; - if (parentIndex == -1) { - const FBXGeometry& geometry = _geometry->getFBXGeometry(); - // NOTE: in practice geometry.offset has a non-unity scale (rather than a translation) - glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; - state.initTransform(parentTransform); - } else { - const JointState& parentState = _jointStates.at(parentIndex); - state.initTransform(parentState.getTransform()); - } + if (!_geometry) { + return; } + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; + _rig->initJointTransforms(parentTransform); } void Model::init() { @@ -396,14 +377,8 @@ void Model::init() { } void Model::reset() { - if (_jointStates.isEmpty()) { - return; - } const FBXGeometry& geometry = _geometry->getFBXGeometry(); - for (int i = 0; i < _jointStates.size(); i++) { - _jointStates[i].setRotationInConstrainedFrame(geometry.joints.at(i).rotation, 0.0f); - } - + _rig->reset(geometry.joints); _meshGroupsKnown = false; _readyWhenAdded = false; // in case any of our users are using scenes invalidCalculatedMeshBoxes(); // if we have to reload, we need to assume our mesh boxes are all invalid @@ -436,28 +411,33 @@ bool Model::updateGeometry() { const FBXGeometry& newGeometry = geometry->getFBXGeometry(); QVector newJointStates = createJointStates(newGeometry); - if (! _jointStates.isEmpty()) { + + if (! _rig->jointStatesEmpty()) { // copy the existing joint states const FBXGeometry& oldGeometry = _geometry->getFBXGeometry(); for (QHash::const_iterator it = oldGeometry.jointIndices.constBegin(); - it != oldGeometry.jointIndices.constEnd(); it++) { + it != oldGeometry.jointIndices.constEnd(); it++) { int oldIndex = it.value() - 1; int newIndex = newGeometry.getJointIndex(it.key()); if (newIndex != -1) { - newJointStates[newIndex].copyState(_jointStates[oldIndex]); + newJointStates[newIndex].copyState(_rig->getJointState(oldIndex)); } } - } + } + deleteGeometry(); _dilatedTextures.clear(); + if (!geometry) { + std::cout << "WARNING: no geometry in Model::updateGeometry\n"; + } setGeometry(geometry); - + _meshGroupsKnown = false; _readyWhenAdded = false; // in case any of our users are using scenes invalidCalculatedMeshBoxes(); // if we have to reload, we need to assume our mesh boxes are all invalid initJointStates(newJointStates); needToRebuild = true; - } else if (_jointStates.isEmpty()) { + } else if (_rig->jointStatesEmpty()) { const FBXGeometry& fbxGeometry = geometry->getFBXGeometry(); if (fbxGeometry.joints.size() > 0) { initJointStates(createJointStates(fbxGeometry)); @@ -475,6 +455,7 @@ bool Model::updateGeometry() { foreach (const FBXMesh& mesh, fbxGeometry.meshes) { MeshState state; state.clusterMatrices.resize(mesh.clusters.size()); + state.cauterizedClusterMatrices.resize(mesh.clusters.size()); _meshStates.append(state); auto buffer = std::make_shared(); @@ -493,22 +474,9 @@ bool Model::updateGeometry() { // virtual void Model::initJointStates(QVector states) { - _jointStates = states; - initJointTransforms(); - - int numStates = _jointStates.size(); - float radius = 0.0f; - for (int i = 0; i < numStates; ++i) { - float distance = glm::length(_jointStates[i].getPosition()); - if (distance > radius) { - radius = distance; - } - _jointStates[i].buildConstraint(); - } - for (int i = 0; i < _jointStates.size(); i++) { - _jointStates[i].slaveVisibleTransform(); - } - _boundingRadius = radius; + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; + _boundingRadius = _rig->initJointStates(states, parentTransform); } bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const glm::vec3& direction, float& distance, @@ -1070,47 +1038,20 @@ glm::vec3 Model::calculateScaledOffsetPoint(const glm::vec3& point) const { return translatedPoint; } - bool Model::getJointState(int index, glm::quat& rotation) const { - if (index == -1 || index >= _jointStates.size()) { - return false; - } - const JointState& state = _jointStates.at(index); - rotation = state.getRotationInConstrainedFrame(); - return !state.rotationIsDefault(rotation); + return _rig->getJointStateRotation(index, rotation); } bool Model::getVisibleJointState(int index, glm::quat& rotation) const { - if (index == -1 || index >= _jointStates.size()) { - return false; - } - const JointState& state = _jointStates.at(index); - rotation = state.getVisibleRotationInConstrainedFrame(); - return !state.rotationIsDefault(rotation); + return _rig->getVisibleJointState(index, rotation); } void Model::clearJointState(int index) { - if (index != -1 && index < _jointStates.size()) { - JointState& state = _jointStates[index]; - state.setRotationInConstrainedFrame(glm::quat(), 0.0f); - } -} - -void Model::clearJointAnimationPriority(int index) { - if (index != -1 && index < _jointStates.size()) { - _jointStates[index]._animationPriority = 0.0f; - } + _rig->clearJointState(index); } void Model::setJointState(int index, bool valid, const glm::quat& rotation, float priority) { - if (index != -1 && index < _jointStates.size()) { - JointState& state = _jointStates[index]; - if (valid) { - state.setRotationInConstrainedFrame(rotation, priority); - } else { - state.restoreRotation(1.0f, priority); - } - } + _rig->setJointState(index, valid, rotation, priority); } int Model::getParentJointIndex(int jointIndex) const { @@ -1185,62 +1126,31 @@ void Model::setCollisionModelURL(const QUrl& url) { } bool Model::getJointPositionInWorldFrame(int jointIndex, glm::vec3& position) const { - if (jointIndex == -1 || jointIndex >= _jointStates.size()) { - return false; - } - // position is in world-frame - position = _translation + _rotation * _jointStates[jointIndex].getPosition(); - return true; + return _rig->getJointPositionInWorldFrame(jointIndex, position, _translation, _rotation); } bool Model::getJointPosition(int jointIndex, glm::vec3& position) const { - if (jointIndex == -1 || jointIndex >= _jointStates.size()) { - return false; - } - // position is in model-frame - position = extractTranslation(_jointStates[jointIndex].getTransform()); - return true; + return _rig->getJointPosition(jointIndex, position); } bool Model::getJointRotationInWorldFrame(int jointIndex, glm::quat& rotation) const { - if (jointIndex == -1 || jointIndex >= _jointStates.size()) { - return false; - } - rotation = _rotation * _jointStates[jointIndex].getRotation(); - return true; + return _rig->getJointRotationInWorldFrame(jointIndex, rotation, _rotation); } bool Model::getJointRotation(int jointIndex, glm::quat& rotation) const { - if (jointIndex == -1 || jointIndex >= _jointStates.size()) { - return false; - } - rotation = _jointStates[jointIndex].getRotation(); - return true; + return _rig->getJointRotation(jointIndex, rotation); } bool Model::getJointCombinedRotation(int jointIndex, glm::quat& rotation) const { - if (jointIndex == -1 || jointIndex >= _jointStates.size()) { - return false; - } - rotation = _rotation * _jointStates[jointIndex].getRotation(); - return true; + return _rig->getJointCombinedRotation(jointIndex, rotation, _rotation); } bool Model::getVisibleJointPositionInWorldFrame(int jointIndex, glm::vec3& position) const { - if (jointIndex == -1 || jointIndex >= _jointStates.size()) { - return false; - } - // position is in world-frame - position = _translation + _rotation * _jointStates[jointIndex].getVisiblePosition(); - return true; + return _rig->getVisibleJointPositionInWorldFrame(jointIndex, position, _translation, _rotation); } bool Model::getVisibleJointRotationInWorldFrame(int jointIndex, glm::quat& rotation) const { - if (jointIndex == -1 || jointIndex >= _jointStates.size()) { - return false; - } - rotation = _rotation * _jointStates[jointIndex].getVisibleRotation(); - return true; + return _rig->getVisibleJointRotationInWorldFrame(jointIndex, rotation, _rotation); } QStringList Model::getJointNames() const { @@ -1253,26 +1163,6 @@ QStringList Model::getJointNames() const { return isActive() ? _geometry->getFBXGeometry().getJointNames() : QStringList(); } -uint qHash(const WeakAnimationHandlePointer& handle, uint seed) { - return qHash(handle.data(), seed); -} - -AnimationHandlePointer Model::createAnimationHandle() { - AnimationHandlePointer handle(new AnimationHandle(this)); - handle->_self = handle; - _animationHandles.insert(handle); - return handle; -} - -// virtual override from PhysicsEntity -void Model::buildShapes() { - // TODO: figure out how to load/build collision shapes for general models -} - -void Model::updateShapePositions() { - // TODO: implement this when we know how to build shapes for regular Models -} - class Blender : public QRunnable { public: @@ -1436,44 +1326,23 @@ void Model::simulate(float deltaTime, bool fullUpdate) { } } -void Model::updateClusterMatrices() { - const FBXGeometry& geometry = _geometry->getFBXGeometry(); - glm::mat4 modelToWorld = glm::mat4_cast(_rotation); - for (int i = 0; i < _meshStates.size(); i++) { - MeshState& state = _meshStates[i]; - const FBXMesh& mesh = geometry.meshes.at(i); - if (_showTrueJointTransforms) { - for (int j = 0; j < mesh.clusters.size(); j++) { - const FBXCluster& cluster = mesh.clusters.at(j); - state.clusterMatrices[j] = modelToWorld * _jointStates[cluster.jointIndex].getTransform() * cluster.inverseBindMatrix; - } - } else { - for (int j = 0; j < mesh.clusters.size(); j++) { - const FBXCluster& cluster = mesh.clusters.at(j); - state.clusterMatrices[j] = modelToWorld * _jointStates[cluster.jointIndex].getVisibleTransform() * cluster.inverseBindMatrix; - } - } - } +//virtual +void Model::updateRig(float deltaTime, glm::mat4 parentTransform) { + _rig->updateAnimations(deltaTime, parentTransform); } - void Model::simulateInternal(float deltaTime) { // update the world space transforms for all joints - - // update animations - foreach (const AnimationHandlePointer& handle, _runningAnimations) { - handle->simulate(deltaTime); - } - for (int i = 0; i < _jointStates.size(); i++) { - updateJointState(i); - } - for (int i = 0; i < _jointStates.size(); i++) { - _jointStates[i].resetTransformChanged(); - } - - _shapesAreDirty = !_shapes.isEmpty(); - const FBXGeometry& geometry = _geometry->getFBXGeometry(); + glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; + updateRig(deltaTime, parentTransform); + + glm::mat4 zeroScale(glm::vec4(0.0f, 0.0f, 0.0f, 0.0f), + glm::vec4(0.0f, 0.0f, 0.0f, 0.0f), + glm::vec4(0.0f, 0.0f, 0.0f, 0.0f), + glm::vec4(0.0f, 0.0f, 0.0f, 1.0f)); + auto cauterizeMatrix = _rig->getJointTransform(geometry.neckJointIndex) * zeroScale; + glm::mat4 modelToWorld = glm::mat4_cast(_rotation); for (int i = 0; i < _meshStates.size(); i++) { MeshState& state = _meshStates[i]; @@ -1481,16 +1350,34 @@ void Model::simulateInternal(float deltaTime) { if (_showTrueJointTransforms) { for (int j = 0; j < mesh.clusters.size(); j++) { const FBXCluster& cluster = mesh.clusters.at(j); - state.clusterMatrices[j] = modelToWorld * _jointStates[cluster.jointIndex].getTransform() * cluster.inverseBindMatrix; + auto jointMatrix =_rig->getJointTransform(cluster.jointIndex); + state.clusterMatrices[j] = modelToWorld * jointMatrix * cluster.inverseBindMatrix; + + // as an optimization, don't build cautrizedClusterMatrices if the boneSet is empty. + if (!_cauterizeBoneSet.empty()) { + if (_cauterizeBoneSet.find(cluster.jointIndex) != _cauterizeBoneSet.end()) { + jointMatrix = cauterizeMatrix; + } + state.cauterizedClusterMatrices[j] = modelToWorld * jointMatrix * cluster.inverseBindMatrix; + } } } else { for (int j = 0; j < mesh.clusters.size(); j++) { const FBXCluster& cluster = mesh.clusters.at(j); - state.clusterMatrices[j] = modelToWorld * _jointStates[cluster.jointIndex].getVisibleTransform() * cluster.inverseBindMatrix; + auto jointMatrix = _rig->getJointVisibleTransform(cluster.jointIndex); + state.clusterMatrices[j] = modelToWorld * jointMatrix * cluster.inverseBindMatrix; + + // as an optimization, don't build cautrizedClusterMatrices if the boneSet is empty. + if (!_cauterizeBoneSet.empty()) { + if (_cauterizeBoneSet.find(cluster.jointIndex) != _cauterizeBoneSet.end()) { + jointMatrix = cauterizeMatrix; + } + state.cauterizedClusterMatrices[j] = modelToWorld * jointMatrix * cluster.inverseBindMatrix; + } } } } - + // post the blender if we're not currently waiting for one to finish if (geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) { _blendedBlendshapeCoefficients = _blendshapeCoefficients; @@ -1498,266 +1385,35 @@ void Model::simulateInternal(float deltaTime) { } } -void Model::updateJointState(int index) { - JointState& state = _jointStates[index]; - const FBXJoint& joint = state.getFBXJoint(); - - // compute model transforms - int parentIndex = joint.parentIndex; - if (parentIndex == -1) { - const FBXGeometry& geometry = _geometry->getFBXGeometry(); - glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; - state.computeTransform(parentTransform); - } else { - // guard against out-of-bounds access to _jointStates - if (joint.parentIndex >= 0 && joint.parentIndex < _jointStates.size()) { - const JointState& parentState = _jointStates.at(parentIndex); - state.computeTransform(parentState.getTransform(), parentState.getTransformChanged()); - } - } -} - -void Model::updateVisibleJointStates() { - if (_showTrueJointTransforms) { - // no need to update visible transforms - return; - } - for (int i = 0; i < _jointStates.size(); i++) { - _jointStates[i].slaveVisibleTransform(); - } -} - bool Model::setJointPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, bool useRotation, - int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment, float priority) { - if (jointIndex == -1 || _jointStates.isEmpty()) { - return false; - } + int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment, float priority) { const FBXGeometry& geometry = _geometry->getFBXGeometry(); const QVector& freeLineage = geometry.joints.at(jointIndex).freeLineage; - if (freeLineage.isEmpty()) { - return false; + glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; + if (_rig->setJointPosition(jointIndex, position, rotation, useRotation, + lastFreeIndex, allIntermediatesFree, alignment, priority, freeLineage, parentTransform)) { + return true; } - if (lastFreeIndex == -1) { - lastFreeIndex = freeLineage.last(); - } - - // this is a cyclic coordinate descent algorithm: see - // http://www.ryanjuckett.com/programming/animation/21-cyclic-coordinate-descent-in-2d - const int ITERATION_COUNT = 1; - glm::vec3 worldAlignment = alignment; - for (int i = 0; i < ITERATION_COUNT; i++) { - // first, try to rotate the end effector as close as possible to the target rotation, if any - glm::quat endRotation; - if (useRotation) { - JointState& state = _jointStates[jointIndex]; - - state.setRotationInBindFrame(rotation, priority); - endRotation = state.getRotationInBindFrame(); - } - - // then, we go from the joint upwards, rotating the end as close as possible to the target - glm::vec3 endPosition = extractTranslation(_jointStates[jointIndex].getTransform()); - 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)) { - continue; - } - glm::vec3 jointPosition = extractTranslation(state.getTransform()); - glm::vec3 jointVector = endPosition - jointPosition; - glm::quat oldCombinedRotation = state.getRotation(); - glm::quat combinedDelta; - float combinedWeight; - if (useRotation) { - combinedDelta = safeMix(rotation * glm::inverse(endRotation), - rotationBetween(jointVector, position - jointPosition), 0.5f); - combinedWeight = 2.0f; - - } else { - combinedDelta = rotationBetween(jointVector, position - jointPosition); - combinedWeight = 1.0f; - } - if (alignment != glm::vec3() && j > 1) { - jointVector = endPosition - jointPosition; - glm::vec3 positionSum; - for (int k = j - 1; k > 0; k--) { - int index = freeLineage.at(k); - updateJointState(index); - positionSum += extractTranslation(_jointStates.at(index).getTransform()); - } - glm::vec3 projectedCenterOfMass = glm::cross(jointVector, - glm::cross(positionSum / (j - 1.0f) - jointPosition, jointVector)); - glm::vec3 projectedAlignment = glm::cross(jointVector, glm::cross(worldAlignment, jointVector)); - const float LENGTH_EPSILON = 0.001f; - if (glm::length(projectedCenterOfMass) > LENGTH_EPSILON && glm::length(projectedAlignment) > LENGTH_EPSILON) { - combinedDelta = safeMix(combinedDelta, rotationBetween(projectedCenterOfMass, projectedAlignment), - 1.0f / (combinedWeight + 1.0f)); - } - } - state.applyRotationDelta(combinedDelta, true, priority); - glm::quat actualDelta = state.getRotation() * glm::inverse(oldCombinedRotation); - endPosition = actualDelta * jointVector + jointPosition; - if (useRotation) { - endRotation = actualDelta * endRotation; - } - } - } - - // now update the joint states from the top - for (int j = freeLineage.size() - 1; j >= 0; j--) { - updateJointState(freeLineage.at(j)); - } - _shapesAreDirty = !_shapes.isEmpty(); - - return true; + return false; } void Model::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::quat& targetRotation, float priority) { - // NOTE: targetRotation is from bind- to model-frame - - if (endIndex == -1 || _jointStates.isEmpty()) { - return; - } - const FBXGeometry& geometry = _geometry->getFBXGeometry(); const QVector& freeLineage = geometry.joints.at(endIndex).freeLineage; - if (freeLineage.isEmpty()) { - return; - } - int numFree = freeLineage.size(); - - // store and remember topmost parent transform - glm::mat4 topParentTransform; - { - int index = freeLineage.last(); - const JointState& state = _jointStates.at(index); - const FBXJoint& joint = state.getFBXJoint(); - int parentIndex = joint.parentIndex; - if (parentIndex == -1) { - const FBXGeometry& geometry = _geometry->getFBXGeometry(); - topParentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; - } else { - topParentTransform = _jointStates[parentIndex].getTransform(); - } - } - - // this is a cyclic coordinate descent algorithm: see - // http://www.ryanjuckett.com/programming/animation/21-cyclic-coordinate-descent-in-2d - - // keep track of the position of the end-effector - JointState& endState = _jointStates[endIndex]; - glm::vec3 endPosition = endState.getPosition(); - float distanceToGo = glm::distance(targetPosition, endPosition); - - const int MAX_ITERATION_COUNT = 2; - const float ACCEPTABLE_IK_ERROR = 0.005f; // 5mm - int numIterations = 0; - do { - ++numIterations; - // moving up, rotate each free joint to get endPosition closer to target - for (int j = 1; j < numFree; j++) { - int nextIndex = freeLineage.at(j); - JointState& nextState = _jointStates[nextIndex]; - FBXJoint nextJoint = nextState.getFBXJoint(); - if (! nextJoint.isFree) { - continue; - } - - glm::vec3 pivot = nextState.getPosition(); - glm::vec3 leverArm = endPosition - pivot; - float leverLength = glm::length(leverArm); - if (leverLength < EPSILON) { - continue; - } - glm::quat deltaRotation = rotationBetween(leverArm, targetPosition - pivot); - - // We want to mix the shortest rotation with one that will pull the system down with gravity - // so that limbs don't float unrealistically. To do this we compute a simplified center of mass - // where each joint has unit mass and we don't bother averaging it because we only need direction. - if (j > 1) { - - glm::vec3 centerOfMass(0.0f); - for (int k = 0; k < j; ++k) { - int massIndex = freeLineage.at(k); - centerOfMass += _jointStates[massIndex].getPosition() - pivot; - } - // the gravitational effect is a rotation that tends to align the two cross products - const glm::vec3 worldAlignment = glm::vec3(0.0f, -1.0f, 0.0f); - glm::quat gravityDelta = rotationBetween(glm::cross(centerOfMass, leverArm), - glm::cross(worldAlignment, leverArm)); - - float gravityAngle = glm::angle(gravityDelta); - const float MIN_GRAVITY_ANGLE = 0.1f; - float mixFactor = 0.5f; - if (gravityAngle < MIN_GRAVITY_ANGLE) { - // the final rotation is a mix of the two - mixFactor = 0.5f * gravityAngle / MIN_GRAVITY_ANGLE; - } - deltaRotation = safeMix(deltaRotation, gravityDelta, mixFactor); - } - - // Apply the rotation, but use mixRotationDelta() which blends a bit of the default pose - // in the process. This provides stability to the IK solution for most models. - glm::quat oldNextRotation = nextState.getRotation(); - float mixFactor = 0.03f; - nextState.mixRotationDelta(deltaRotation, mixFactor, priority); - - // measure the result of the rotation which may have been modified by - // blending and constraints - glm::quat actualDelta = nextState.getRotation() * glm::inverse(oldNextRotation); - endPosition = pivot + actualDelta * leverArm; - } - - // recompute transforms from the top down - glm::mat4 parentTransform = topParentTransform; - for (int j = numFree - 1; j >= 0; --j) { - JointState& freeState = _jointStates[freeLineage.at(j)]; - freeState.computeTransform(parentTransform); - parentTransform = freeState.getTransform(); - } - - // measure our success - endPosition = endState.getPosition(); - distanceToGo = glm::distance(targetPosition, endPosition); - } while (numIterations < MAX_ITERATION_COUNT && distanceToGo < ACCEPTABLE_IK_ERROR); - - // set final rotation of the end joint - endState.setRotationInBindFrame(targetRotation, priority, true); - - _shapesAreDirty = !_shapes.isEmpty(); + glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; + _rig->inverseKinematics(endIndex, targetPosition, targetRotation, priority, freeLineage, parentTransform); } bool Model::restoreJointPosition(int jointIndex, float fraction, float priority) { - if (jointIndex == -1 || _jointStates.isEmpty()) { - return false; - } const FBXGeometry& geometry = _geometry->getFBXGeometry(); const QVector& freeLineage = geometry.joints.at(jointIndex).freeLineage; - - foreach (int index, freeLineage) { - JointState& state = _jointStates[index]; - state.restoreRotation(fraction, priority); - } - return true; + return _rig->restoreJointPosition(jointIndex, fraction, priority, freeLineage); } float Model::getLimbLength(int jointIndex) const { - if (jointIndex == -1 || _jointStates.isEmpty()) { - return 0.0f; - } const FBXGeometry& geometry = _geometry->getFBXGeometry(); const QVector& freeLineage = geometry.joints.at(jointIndex).freeLineage; - float length = 0.0f; - float lengthScale = (_scale.x + _scale.y + _scale.z) / 3.0f; - for (int i = freeLineage.size() - 2; i >= 0; i--) { - length += geometry.joints.at(freeLineage.at(i)).distanceToParent * lengthScale; - } - return length; -} - -void Model::renderJointCollisionShapes(float alpha) { - // implement this when we have shapes for regular models + return _rig->getLimbLength(jointIndex, freeLineage, _scale, geometry.joints); } bool Model::maybeStartBlender() { @@ -1823,24 +1479,15 @@ void Model::applyNextGeometry() { void Model::deleteGeometry() { _blendedVertexBuffers.clear(); - _jointStates.clear(); + _rig->clearJointStates(); _meshStates.clear(); - clearShapes(); - - for (QSet::iterator it = _animationHandles.begin(); it != _animationHandles.end(); ) { - AnimationHandlePointer handle = it->toStrongRef(); - if (handle) { - handle->_jointMappings.clear(); - it++; - } else { - it = _animationHandles.erase(it); - } - } - + + _rig->deleteAnimations(); + if (_geometry) { _geometry->clearLoadPriority(this); } - + _blendedBlendshapeCoefficients.clear(); } @@ -1952,7 +1599,9 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran args, locations); { - updateVisibleJointStates(); + if (!_showTrueJointTransforms) { + _rig->updateVisibleJointStates(); + } // else no need to update visible transforms } // if our index is ever out of range for either meshes or networkMeshes, then skip it, and set our _meshGroupsKnown @@ -1978,12 +1627,21 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran } if (isSkinned) { - batch._glUniformMatrix4fv(locations->clusterMatrices, state.clusterMatrices.size(), false, - (const float*)state.clusterMatrices.constData()); + const float* bones; + if (_cauterizeBones) { + bones = (const float*)state.cauterizedClusterMatrices.constData(); + } else { + bones = (const float*)state.clusterMatrices.constData(); + } + batch._glUniformMatrix4fv(locations->clusterMatrices, state.clusterMatrices.size(), false, bones); _transforms[0] = Transform(); _transforms[0].preTranslate(_translation); } else { - _transforms[0] = Transform(state.clusterMatrices[0]); + if (_cauterizeBones) { + _transforms[0] = Transform(state.cauterizedClusterMatrices[0]); + } else { + _transforms[0] = Transform(state.clusterMatrices[0]); + } _transforms[0].preTranslate(_translation); } batch.setModelTransform(_transforms[0]); @@ -2102,8 +1760,11 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran } if (part.quadIndices.size() > 0) { - batch.drawIndexed(gpu::QUADS, part.quadIndices.size(), offset); + batch.setIndexBuffer(gpu::UINT32, part.getTrianglesForQuads(), 0); + batch.drawIndexed(gpu::TRIANGLES, part.trianglesForQuadsIndicesCount, 0); + offset += part.quadIndices.size() * sizeof(int); + batch.setIndexBuffer(gpu::UINT32, (networkMesh._indexBuffer), 0); // restore this in case there are triangles too } if (part.triangleIndices.size() > 0) { diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index ec0e80fad6..09504c7501 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -18,10 +18,10 @@ #include #include +#include #include #include -#include #include #include #include @@ -39,7 +39,6 @@ class AbstractViewStateInterface; class QScriptEngine; -class Shape; #include "RenderArgs.h" class ViewFrustum; @@ -57,67 +56,16 @@ inline uint qHash(const std::shared_ptr& a, uint seed) { /// A generic 3D model displaying geometry loaded from a URL. class Model : public QObject, public PhysicsEntity { Q_OBJECT - + public: typedef RenderArgs::RenderMode RenderMode; static void setAbstractViewStateInterface(AbstractViewStateInterface* viewState) { _viewState = viewState; } - Model(QObject* parent = NULL); + Model(RigPointer rig, QObject* parent = nullptr); virtual ~Model(); - - /// enables/disables scale to fit behavior, the model will be automatically scaled to the specified largest dimension - void setScaleToFit(bool scaleToFit, float largestDimension = 0.0f, bool forceRescale = false); - bool getScaleToFit() const { return _scaleToFit; } /// is scale to fit enabled - bool getIsScaledToFit() const { return _scaledToFit; } /// is model scaled to fit - const glm::vec3& getScaleToFitDimensions() const { return _scaleToFitDimensions; } /// the dimensions model is scaled to - void setScaleToFit(bool scaleToFit, const glm::vec3& dimensions); - void setSnapModelToCenter(bool snapModelToCenter) { - setSnapModelToRegistrationPoint(snapModelToCenter, glm::vec3(0.5f,0.5f,0.5f)); - }; - bool getSnapModelToCenter() { - return _snapModelToRegistrationPoint && _registrationPoint == glm::vec3(0.5f,0.5f,0.5f); - } - - void setSnapModelToRegistrationPoint(bool snapModelToRegistrationPoint, const glm::vec3& registrationPoint); - bool getSnapModelToRegistrationPoint() { return _snapModelToRegistrationPoint; } - - void setScale(const glm::vec3& scale); - const glm::vec3& getScale() const { return _scale; } - - void setOffset(const glm::vec3& offset); - const glm::vec3& getOffset() const { return _offset; } - - void setPupilDilation(float dilation) { _pupilDilation = dilation; } - float getPupilDilation() const { return _pupilDilation; } - - void setBlendshapeCoefficients(const QVector& coefficients) { _blendshapeCoefficients = coefficients; } - const QVector& getBlendshapeCoefficients() const { return _blendshapeCoefficients; } - - bool isActive() const { return _geometry && _geometry->isLoaded(); } - - bool isRenderable() const { return !_meshStates.isEmpty() || (isActive() && _geometry->getMeshes().isEmpty()); } - - void setVisibleInScene(bool newValue, std::shared_ptr scene); - bool isVisible() const { return _isVisible; } - - bool isLoaded() const { return _geometry && _geometry->isLoaded(); } - bool isLoadedWithTextures() const { return _geometry && _geometry->isLoadedWithTextures(); } - - void init(); - void reset(); - virtual void simulate(float deltaTime, bool fullUpdate = true); - - void renderSetup(RenderArgs* args); - - // new Scene/Engine rendering support - bool needsFixupInScene() { return !_readyWhenAdded && readyToAddToScene(); } - bool readyToAddToScene(RenderArgs* renderArgs = nullptr) { return !_needsReload && isRenderable() && isActive() && isLoaded(); } - bool addToScene(std::shared_ptr scene, render::PendingChanges& pendingChanges); - bool addToScene(std::shared_ptr scene, render::PendingChanges& pendingChanges, render::Item::Status::Getters& statusGetters); - void removeFromScene(std::shared_ptr scene, render::PendingChanges& pendingChanges); /// Sets the URL of the model to render. /// \param fallback the URL of a fallback model to render if the requested model fails to load @@ -127,22 +75,131 @@ public: bool retainCurrent = false, bool delayLoad = false); const QUrl& getURL() const { return _url; } + // new Scene/Engine rendering support + void setVisibleInScene(bool newValue, std::shared_ptr scene); + bool needsFixupInScene() { return !_readyWhenAdded && readyToAddToScene(); } + bool readyToAddToScene(RenderArgs* renderArgs = nullptr) { + return !_needsReload && isRenderable() && isActive() && isLoaded(); + } + bool initWhenReady(render::ScenePointer scene); + bool addToScene(std::shared_ptr scene, render::PendingChanges& pendingChanges); + bool addToScene(std::shared_ptr scene, + render::PendingChanges& pendingChanges, + render::Item::Status::Getters& statusGetters); + void removeFromScene(std::shared_ptr scene, render::PendingChanges& pendingChanges); + void renderSetup(RenderArgs* args); + bool isRenderable() const { return !_meshStates.isEmpty() || (isActive() && _geometry->getMeshes().isEmpty()); } + + bool isVisible() const { return _isVisible; } + + AABox getPartBounds(int meshIndex, int partIndex); + void renderPart(RenderArgs* args, int meshIndex, int partIndex, bool translucent); + + bool maybeStartBlender(); + + /// Sets blended vertices computed in a separate thread. + void setBlendedVertices(int blendNumber, const QWeakPointer& geometry, + const QVector& vertices, const QVector& normals); + + bool isLoaded() const { return _geometry && _geometry->isLoaded(); } + bool isLoadedWithTextures() const { return _geometry && _geometry->isLoadedWithTextures(); } + + void setIsWireframe(bool isWireframe) { _isWireframe = isWireframe; } + bool isWireframe() const { return _isWireframe; } + + void init(); + void reset(); + + void setScaleToFit(bool scaleToFit, const glm::vec3& dimensions); + + void setSnapModelToRegistrationPoint(bool snapModelToRegistrationPoint, const glm::vec3& registrationPoint); + bool getSnapModelToRegistrationPoint() { return _snapModelToRegistrationPoint; } + + virtual void simulate(float deltaTime, bool fullUpdate = true); + + /// Returns a reference to the shared geometry. + const QSharedPointer& getGeometry() const { return _geometry; } + + bool isActive() const { return _geometry && _geometry->isLoaded(); } + + Q_INVOKABLE void setTextureWithNameToURL(const QString& name, const QUrl& url) + { _geometry->setTextureWithNameToURL(name, url); } + + bool convexHullContains(glm::vec3 point); + + QStringList getJointNames() const; + + /// Sets the joint state at the specified index. + void setJointState(int index, bool valid, const glm::quat& rotation = glm::quat(), float priority = 1.0f); + + bool findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const glm::vec3& direction, float& distance, + BoxFace& face, QString& extraInfo, bool pickAgainstTriangles = false); + // Set the model to use for collisions Q_INVOKABLE void setCollisionModelURL(const QUrl& url); const QUrl& getCollisionURL() const { return _collisionUrl; } - - void setIsWireframe(bool isWireframe) { _isWireframe = isWireframe; } - bool isWireframe() const { return _isWireframe; } - + + /// Returns a reference to the shared collision geometry. + const QSharedPointer getCollisionGeometry(bool delayLoad = true); + + void setOffset(const glm::vec3& offset); + const glm::vec3& getOffset() const { return _offset; } + /// Sets the distance parameter used for LOD computations. void setLODDistance(float distance) { _lodDistance = distance; } - + + void setScaleToFit(bool scaleToFit, float largestDimension = 0.0f, bool forceRescale = false); + bool getScaleToFit() const { return _scaleToFit; } /// is scale to fit enabled + + void setSnapModelToCenter(bool snapModelToCenter) { + setSnapModelToRegistrationPoint(snapModelToCenter, glm::vec3(0.5f,0.5f,0.5f)); + }; + bool getSnapModelToCenter() { + return _snapModelToRegistrationPoint && _registrationPoint == glm::vec3(0.5f,0.5f,0.5f); + } + + /// Returns the number of joint states in the model. + int getJointStateCount() const { return _rig->getJointStateCount(); } + bool getJointPositionInWorldFrame(int jointIndex, glm::vec3& position) const; + bool getJointRotationInWorldFrame(int jointIndex, glm::quat& rotation) const; + bool getJointCombinedRotation(int jointIndex, glm::quat& rotation) const; + /// \param jointIndex index of joint in model structure + /// \param rotation[out] rotation of joint in model-frame + /// \return true if joint exists + bool getJointRotation(int jointIndex, glm::quat& rotation) const; + + /// Returns the index of the parent of the indexed joint, or -1 if not found. + int getParentJointIndex(int jointIndex) const; + + void inverseKinematics(int jointIndex, glm::vec3 position, const glm::quat& rotation, float priority); + /// Returns the extents of the model in its bind pose. Extents getBindExtents() const; /// Returns the extents of the model's mesh Extents getMeshExtents() const; + void setScale(const glm::vec3& scale); + const glm::vec3& getScale() const { return _scale; } + + /// enables/disables scale to fit behavior, the model will be automatically scaled to the specified largest dimension + bool getIsScaledToFit() const { return _scaledToFit; } /// is model scaled to fit + const glm::vec3& getScaleToFitDimensions() const { return _scaleToFitDimensions; } /// the dimensions model is scaled to + + void setCauterizeBones(bool flag) { _cauterizeBones = flag; } + bool getCauterizeBones() const { return _cauterizeBones; } + + const std::unordered_set& getCauterizeBoneSet() const { return _cauterizeBoneSet; } + void setCauterizeBoneSet(const std::unordered_set& boneSet) { _cauterizeBoneSet = boneSet; } + +protected: + + void setPupilDilation(float dilation) { _pupilDilation = dilation; } + float getPupilDilation() const { return _pupilDilation; } + + void setBlendshapeCoefficients(const QVector& coefficients) { _blendshapeCoefficients = coefficients; } + const QVector& getBlendshapeCoefficients() const { return _blendshapeCoefficients; } + /// Returns the unscaled extents of the model's mesh Extents getUnscaledMeshExtents() const; @@ -155,15 +212,6 @@ public: /// Returns the scaled equivalent of a point in model space. glm::vec3 calculateScaledOffsetPoint(const glm::vec3& point) const; - /// Returns a reference to the shared geometry. - const QSharedPointer& getGeometry() const { return _geometry; } - - /// Returns a reference to the shared collision geometry. - const QSharedPointer getCollisionGeometry(bool delayLoad = true); - - /// Returns the number of joint states in the model. - int getJointStateCount() const { return _jointStates.size(); } - /// Fetches the joint state at the specified index. /// \return whether or not the joint state is "valid" (that is, non-default) bool getJointState(int index, glm::quat& rotation) const; @@ -171,25 +219,12 @@ public: /// Fetches the visible joint state at the specified index. /// \return whether or not the joint state is "valid" (that is, non-default) bool getVisibleJointState(int index, glm::quat& rotation) const; - + /// Clear the joint states void clearJointState(int index); - - /// Clear the joint animation priority - void clearJointAnimationPriority(int index); - - /// Sets the joint state at the specified index. - void setJointState(int index, bool valid, const glm::quat& rotation = glm::quat(), float priority = 1.0f); - - /// Returns the index of the parent of the indexed joint, or -1 if not found. - int getParentJointIndex(int jointIndex) const; - + /// Returns the index of the last free ancestor of the indexed joint, or -1 if not found. int getLastFreeJointIndex(int jointIndex) const; - - bool getJointPositionInWorldFrame(int jointIndex, glm::vec3& position) const; - bool getJointRotationInWorldFrame(int jointIndex, glm::quat& rotation) const; - bool getJointCombinedRotation(int jointIndex, glm::quat& rotation) const; bool getVisibleJointPositionInWorldFrame(int jointIndex, glm::vec3& position) const; bool getVisibleJointRotationInWorldFrame(int jointIndex, glm::quat& rotation) const; @@ -199,52 +234,11 @@ public: /// \return true if joint exists bool getJointPosition(int jointIndex, glm::vec3& position) const; - /// \param jointIndex index of joint in model structure - /// \param rotation[out] rotation of joint in model-frame - /// \return true if joint exists - bool getJointRotation(int jointIndex, glm::quat& rotation) const; - - QStringList getJointNames() const; - - AnimationHandlePointer createAnimationHandle(); - - const QList& getRunningAnimations() const { return _runningAnimations; } - - // virtual overrides from PhysicsEntity - virtual void buildShapes(); - virtual void updateShapePositions(); - - virtual void renderJointCollisionShapes(float alpha); - - bool maybeStartBlender(); - - /// Sets blended vertices computed in a separate thread. - void setBlendedVertices(int blendNumber, const QWeakPointer& geometry, - const QVector& vertices, const QVector& normals); - void setShowTrueJointTransforms(bool show) { _showTrueJointTransforms = show; } - QVector& getJointStates() { return _jointStates; } - const QVector& getJointStates() const { return _jointStates; } - - void inverseKinematics(int jointIndex, glm::vec3 position, const glm::quat& rotation, float priority); - - Q_INVOKABLE void setTextureWithNameToURL(const QString& name, const QUrl& url) - { _geometry->setTextureWithNameToURL(name, url); } - - bool findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const glm::vec3& direction, float& distance, - BoxFace& face, QString& extraInfo, bool pickAgainstTriangles = false); - bool convexHullContains(glm::vec3 point); - - AABox getPartBounds(int meshIndex, int partIndex); - void renderPart(RenderArgs* args, int meshIndex, int partIndex, bool translucent); - - bool initWhenReady(render::ScenePointer scene); - -protected: QSharedPointer _geometry; void setGeometry(const QSharedPointer& newGeometry); - + glm::vec3 _scale; glm::vec3 _offset; @@ -257,34 +251,31 @@ protected: bool _snapModelToRegistrationPoint; /// is the model's offset automatically adjusted to a registration point in model space bool _snappedToRegistrationPoint; /// are we currently snapped to a registration point glm::vec3 _registrationPoint = glm::vec3(0.5f); /// the point in model space our center is snapped to - + bool _showTrueJointTransforms; - - QVector _jointStates; class MeshState { public: QVector clusterMatrices; + QVector cauterizedClusterMatrices; }; - + QVector _meshStates; - + std::unordered_set _cauterizeBoneSet; + bool _cauterizeBones; + // returns 'true' if needs fullUpdate after geometry change bool updateGeometry(); virtual void initJointStates(QVector states); - + void setScaleInternal(const glm::vec3& scale); void scaleToFit(); void snapToRegistrationPoint(); void simulateInternal(float deltaTime); + virtual void updateRig(float deltaTime, glm::mat4 parentTransform); - /// Updates the state of the joint at the specified index. - virtual void updateJointState(int index); - - virtual void updateVisibleJointStates(); - /// \param jointIndex index of joint in model structure /// \param position position of joint in model-frame /// \param rotation rotation of joint in model-frame @@ -302,11 +293,11 @@ protected: /// the original position /// \return true if the joint was found bool restoreJointPosition(int jointIndex, float fraction = 1.0f, float priority = 0.0f); - + /// Computes and returns the extended length of the limb terminating at the specified joint and starting at the joint's /// first free ancestor. float getLimbLength(int jointIndex) const; - + /// Allow sub classes to force invalidating the bboxes void invalidCalculatedMeshBoxes() { _calculatedMeshBoxesValid = false; @@ -314,34 +305,28 @@ protected: _calculatedMeshTrianglesValid = false; } - // rebuild the clusterMatrices from the current jointStates - void updateClusterMatrices(); - // hook for derived classes to be notified when setUrl invalidates the current model. virtual void onInvalidate() {}; -protected slots: void geometryRefreshed(); - + private: - - friend class AnimationHandle; - + void applyNextGeometry(); void deleteGeometry(); QVector createJointStates(const FBXGeometry& geometry); void initJointTransforms(); - + QSharedPointer _nextGeometry; float _lodDistance; float _lodHysteresis; float _nextLODHysteresis; QSharedPointer _collisionGeometry; - + float _pupilDilation; QVector _blendshapeCoefficients; - + QUrl _url; QUrl _collisionUrl; bool _isVisible; @@ -351,10 +336,6 @@ private: gpu::Batch _renderBatch; QVector > > _dilatedTextures; - - QSet _animationHandles; - - QList _runningAnimations; QVector _blendedBlendshapeCoefficients; int _blendNumber; @@ -379,12 +360,12 @@ private: QHash, AABox> _calculatedMeshPartBoxes; // world coordinate AABoxes for all sub mesh part boxes QHash, qint64> _calculatedMeshPartOffset; bool _calculatedMeshPartOffsetValid; - - + + bool _calculatedMeshPartBoxesValid; QVector _calculatedMeshBoxes; // world coordinate AABoxes for all sub mesh boxes bool _calculatedMeshBoxesValid; - + QVector< QVector > _calculatedMeshTriangles; // world coordinate triangles for all sub meshes bool _calculatedMeshTrianglesValid; QMutex _mutex; @@ -424,10 +405,10 @@ private: IS_SHADOW_FLAG, IS_MIRROR_FLAG, //THis means that the mesh is rendered mirrored, not the same as "Rear view mirror" IS_WIREFRAME_FLAG, - + NUM_FLAGS, }; - + enum Flag { IS_TRANSLUCENT = (1 << IS_TRANSLUCENT_FLAG), HAS_LIGHTMAP = (1 << HAS_LIGHTMAP_FLAG), @@ -493,7 +474,7 @@ private: RenderKey(int bitmask) : _flags(bitmask) {} }; - + class RenderPipeline { public: gpu::PipelinePointer _pipeline; @@ -507,7 +488,7 @@ private: public: typedef RenderKey Key; - + void addRenderPipeline(Key key, gpu::ShaderPointer& vertexShader, gpu::ShaderPointer& pixelShader); void initLocations(gpu::ShaderPointer& program, Locations& locations); @@ -515,13 +496,16 @@ private: static RenderPipelineLib _renderPipelineLib; bool _renderCollisionHull; - - + + QSet> _transparentRenderItems; QSet> _opaqueRenderItems; QMap _renderItems; bool _readyWhenAdded = false; bool _needsReload = true; + +protected: + RigPointer _rig; }; Q_DECLARE_METATYPE(QPointer) diff --git a/libraries/render-utils/src/PhysicsEntity.cpp b/libraries/render-utils/src/PhysicsEntity.cpp index 155b2fe4e0..5d58d87e84 100644 --- a/libraries/render-utils/src/PhysicsEntity.cpp +++ b/libraries/render-utils/src/PhysicsEntity.cpp @@ -11,17 +11,10 @@ #include "PhysicsEntity.h" -#include "PlaneShape.h" -#include "Shape.h" -#include "ShapeCollider.h" -#include "SphereShape.h" - PhysicsEntity::PhysicsEntity() : _translation(0.0f), _rotation(), - _boundingRadius(0.0f), - _shapesAreDirty(true), - _enableShapes(false) { + _boundingRadius(0.0f) { } PhysicsEntity::~PhysicsEntity() { @@ -29,143 +22,13 @@ PhysicsEntity::~PhysicsEntity() { void PhysicsEntity::setTranslation(const glm::vec3& translation) { if (_translation != translation) { - _shapesAreDirty = !_shapes.isEmpty(); _translation = translation; } } void PhysicsEntity::setRotation(const glm::quat& rotation) { if (_rotation != rotation) { - _shapesAreDirty = !_shapes.isEmpty(); _rotation = rotation; } } -void PhysicsEntity::setShapeBackPointers() { - for (int i = 0; i < _shapes.size(); i++) { - Shape* shape = _shapes[i]; - if (shape) { - shape->setEntity(this); - } - } -} - -void PhysicsEntity::setEnableShapes(bool enable) { - if (enable != _enableShapes) { - clearShapes(); - _enableShapes = enable; - if (_enableShapes) { - buildShapes(); - } - } -} - -void PhysicsEntity::clearShapes() { - for (int i = 0; i < _shapes.size(); ++i) { - delete _shapes[i]; - } - _shapes.clear(); -} - -bool PhysicsEntity::findRayIntersection(RayIntersectionInfo& intersection) const { - return ShapeCollider::findRayIntersection(_shapes, intersection); -} - -bool PhysicsEntity::findCollisions(const QVector shapes, CollisionList& collisions) { - bool collided = false; - int numTheirShapes = shapes.size(); - for (int i = 0; i < numTheirShapes; ++i) { - const Shape* theirShape = shapes[i]; - if (!theirShape) { - continue; - } - int numOurShapes = _shapes.size(); - for (int j = 0; j < numOurShapes; ++j) { - const Shape* ourShape = _shapes.at(j); - if (ourShape && ShapeCollider::collideShapes(theirShape, ourShape, collisions)) { - collided = true; - } - } - } - return collided; -} - -bool PhysicsEntity::findSphereCollisions(const glm::vec3& sphereCenter, float sphereRadius, CollisionList& collisions) { - bool collided = false; - SphereShape sphere(sphereRadius, sphereCenter); - for (int i = 0; i < _shapes.size(); i++) { - Shape* shape = _shapes[i]; - if (!shape) { - continue; - } - if (ShapeCollider::collideShapes(&sphere, shape, collisions)) { - CollisionInfo* collision = collisions.getLastCollision(); - collision->_data = (void*)(this); - collision->_intData = i; - collided = true; - } - } - return collided; -} - -bool PhysicsEntity::findPlaneCollisions(const glm::vec4& plane, CollisionList& collisions) { - bool collided = false; - PlaneShape planeShape(plane); - for (int i = 0; i < _shapes.size(); i++) { - if (_shapes.at(i) && ShapeCollider::collideShapes(&planeShape, _shapes.at(i), collisions)) { - CollisionInfo* collision = collisions.getLastCollision(); - collision->_data = (void*)(this); - collision->_intData = i; - collided = true; - } - } - return collided; -} - -// ----------------------------------------------------------- -// TODO: enforce this maximum when shapes are actually built. The gotcha here is -// that the Model class (derived from PhysicsEntity) expects numShapes == numJoints, -// so we have to modify that code to be safe. -const int MAX_SHAPES_PER_ENTITY = 256; - -// the first 256 prime numbers -const int primes[256] = { - 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, - 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, - 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, - 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, - 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, - 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, - 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, - 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, - 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, - 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, - 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, - 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, - 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, - 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, - 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, - 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, - 947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013, - 1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, - 1087, 1091, 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151, - 1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223, - 1229, 1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289, 1291, - 1297, 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373, - 1381, 1399, 1409, 1423, 1427, 1429, 1433, 1439, 1447, 1451, - 1453, 1459, 1471, 1481, 1483, 1487, 1489, 1493, 1499, 1511, - 1523, 1531, 1543, 1549, 1553, 1559, 1567, 1571, 1579, 1583, - 1597, 1601, 1607, 1609, 1613, 1619 }; - -void PhysicsEntity::disableCollisions(int shapeIndexA, int shapeIndexB) { - if (shapeIndexA < MAX_SHAPES_PER_ENTITY && shapeIndexB < MAX_SHAPES_PER_ENTITY) { - _disabledCollisions.insert(primes[shapeIndexA] * primes[shapeIndexB]); - } -} - -bool PhysicsEntity::collisionsAreEnabled(int shapeIndexA, int shapeIndexB) const { - if (shapeIndexA < MAX_SHAPES_PER_ENTITY && shapeIndexB < MAX_SHAPES_PER_ENTITY) { - return !_disabledCollisions.contains(primes[shapeIndexA] * primes[shapeIndexB]); - } - return false; -} diff --git a/libraries/render-utils/src/PhysicsEntity.h b/libraries/render-utils/src/PhysicsEntity.h index f01f1d10a6..3b527c7827 100644 --- a/libraries/render-utils/src/PhysicsEntity.h +++ b/libraries/render-utils/src/PhysicsEntity.h @@ -21,8 +21,6 @@ #include #include -class Shape; - class PhysicsEntity { public: @@ -38,30 +36,10 @@ public: const glm::quat& getRotation() const { return _rotation; } float getBoundingRadius() const { return _boundingRadius; } - void setShapeBackPointers(); - - void setEnableShapes(bool enable); - - virtual void buildShapes() = 0; - virtual void clearShapes(); - const QVector getShapes() const { return _shapes; } - - bool findRayIntersection(RayIntersectionInfo& intersection) const; - bool findCollisions(const QVector shapes, CollisionList& collisions); - bool findSphereCollisions(const glm::vec3& sphereCenter, float sphereRadius, CollisionList& collisions); - bool findPlaneCollisions(const glm::vec4& plane, CollisionList& collisions); - - void disableCollisions(int shapeIndexA, int shapeIndexB); - bool collisionsAreEnabled(int shapeIndexA, int shapeIndexB) const; - protected: glm::vec3 _translation; glm::quat _rotation; float _boundingRadius; - bool _shapesAreDirty; - bool _enableShapes; - QVector _shapes; - QSet _disabledCollisions; }; #endif // hifi_PhysicsEntity_h diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index f4b6d9b0a1..07a8b63e6f 100755 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -11,12 +11,10 @@ // #include "RenderDeferredTask.h" -#include -#include -#include #include #include #include +#include #include "FramebufferCache.h" #include "DeferredLightingEffect.h" diff --git a/libraries/render-utils/src/TextureCache.cpp b/libraries/render-utils/src/TextureCache.cpp index 1a6ea97b64..d6a9bf5b36 100644 --- a/libraries/render-utils/src/TextureCache.cpp +++ b/libraries/render-utils/src/TextureCache.cpp @@ -16,16 +16,15 @@ #include #include -#include -#include -#include - #include #include #include #include #include +#include + + #include "RenderUtilsLogging.h" diff --git a/libraries/render-utils/src/text/Font.cpp b/libraries/render-utils/src/text/Font.cpp index b341f444e6..3cedcdcbc1 100644 --- a/libraries/render-utils/src/text/Font.cpp +++ b/libraries/render-utils/src/text/Font.cpp @@ -18,8 +18,12 @@ struct TextureVertex { TextureVertex(const glm::vec2& pos, const glm::vec2& tex) : pos(pos), tex(tex) {} }; +static const int NUMBER_OF_INDICES_PER_QUAD = 6; // 1 quad = 2 triangles +static const int VERTICES_PER_QUAD = 4; // 1 quad = 4 vertices + struct QuadBuilder { - TextureVertex vertices[4]; + TextureVertex vertices[VERTICES_PER_QUAD]; + QuadBuilder(const glm::vec2& min, const glm::vec2& size, const glm::vec2& texMin, const glm::vec2& texSize) { // min = bottomLeft @@ -249,6 +253,9 @@ void Font::setupGPU() { void Font::rebuildVertices(float x, float y, const QString& str, const glm::vec2& bounds) { _verticesBuffer = std::make_shared(); _numVertices = 0; + _indicesBuffer = std::make_shared(); + _numIndices = 0; + _lastStringRendered = str; _lastBounds = bounds; @@ -284,10 +291,28 @@ void Font::rebuildVertices(float x, float y, const QString& str, const glm::vec2 if (!isNewLine) { for (auto c : token) { auto glyph = _glyphs[c]; + quint16 verticesOffset = _numVertices; QuadBuilder qd(glyph, advance - glm::vec2(0.0f, _ascent)); _verticesBuffer->append(sizeof(QuadBuilder), (const gpu::Byte*)&qd); _numVertices += 4; + + // Sam's recommended triangle slices + // Triangle tri1 = { v0, v1, v3 }; + // Triangle tri2 = { v1, v2, v3 }; + // NOTE: Random guy on the internet's recommended triangle slices + // Triangle tri1 = { v0, v1, v2 }; + // Triangle tri2 = { v2, v3, v0 }; + quint16 indices[NUMBER_OF_INDICES_PER_QUAD]; + indices[0] = verticesOffset + 0; + indices[1] = verticesOffset + 1; + indices[2] = verticesOffset + 3; + indices[3] = verticesOffset + 1; + indices[4] = verticesOffset + 2; + indices[5] = verticesOffset + 3; + _indicesBuffer->append(sizeof(indices), (const gpu::Byte*)indices); + _numIndices += NUMBER_OF_INDICES_PER_QUAD; + // Advance by glyph size advance.x += glyph.d; @@ -318,5 +343,6 @@ void Font::drawString(gpu::Batch& batch, float x, float y, const QString& str, c batch.setInputFormat(_format); batch.setInputBuffer(0, _verticesBuffer, 0, _format->getChannels().at(0)._stride); - batch.draw(gpu::QUADS, _numVertices, 0); + batch.setIndexBuffer(gpu::UINT16, _indicesBuffer, 0); + batch.drawIndexed(gpu::TRIANGLES, _numIndices, 0); } diff --git a/libraries/render-utils/src/text/Font.h b/libraries/render-utils/src/text/Font.h index 55801419f9..e10360d45f 100644 --- a/libraries/render-utils/src/text/Font.h +++ b/libraries/render-utils/src/text/Font.h @@ -64,8 +64,10 @@ private: gpu::TexturePointer _texture; gpu::Stream::FormatPointer _format; gpu::BufferPointer _verticesBuffer; + gpu::BufferPointer _indicesBuffer; gpu::BufferStreamPointer _stream; unsigned int _numVertices = 0; + unsigned int _numIndices = 0; int _fontLoc = -1; int _outlineLoc = -1; diff --git a/libraries/render/src/render/DrawStatus.cpp b/libraries/render/src/render/DrawStatus.cpp index f84d212112..cf3616a83a 100644 --- a/libraries/render/src/render/DrawStatus.cpp +++ b/libraries/render/src/render/DrawStatus.cpp @@ -15,15 +15,11 @@ #include #include - -#include -#include -#include -#include - #include #include +#include + #include "drawItemBounds_vert.h" #include "drawItemBounds_frag.h" #include "drawItemStatus_vert.h" @@ -152,17 +148,17 @@ void DrawStatus::run(const SceneContextPointer& sceneContext, const RenderContex const unsigned int VEC3_ADRESS_OFFSET = 3; for (int i = 0; i < nbItems; i++) { - batch._glUniform3fv(_drawItemBoundPosLoc, 1, (const GLfloat*) (itemAABox + i)); - batch._glUniform3fv(_drawItemBoundDimLoc, 1, ((const GLfloat*) (itemAABox + i)) + VEC3_ADRESS_OFFSET); + batch._glUniform3fv(_drawItemBoundPosLoc, 1, (const float*) (itemAABox + i)); + batch._glUniform3fv(_drawItemBoundDimLoc, 1, ((const float*) (itemAABox + i)) + VEC3_ADRESS_OFFSET); batch.draw(gpu::LINES, 24, 0); } batch.setPipeline(getDrawItemStatusPipeline()); for (int i = 0; i < nbItems; i++) { - batch._glUniform3fv(_drawItemStatusPosLoc, 1, (const GLfloat*) (itemAABox + i)); - batch._glUniform3fv(_drawItemStatusDimLoc, 1, ((const GLfloat*) (itemAABox + i)) + VEC3_ADRESS_OFFSET); - batch._glUniform4iv(_drawItemStatusValueLoc, 1, (const GLint*) (itemStatus + i)); + batch._glUniform3fv(_drawItemStatusPosLoc, 1, (const float*) (itemAABox + i)); + batch._glUniform3fv(_drawItemStatusDimLoc, 1, ((const float*) (itemAABox + i)) + VEC3_ADRESS_OFFSET); + batch._glUniform4iv(_drawItemStatusValueLoc, 1, (const int*) (itemStatus + i)); batch.draw(gpu::TRIANGLES, 24, 0); } @@ -171,4 +167,4 @@ void DrawStatus::run(const SceneContextPointer& sceneContext, const RenderContex args->_context->syncCache(); renderContext->args->_context->syncCache(); args->_context->render((batch)); -} \ No newline at end of file +} diff --git a/libraries/render/src/render/DrawTask.cpp b/libraries/render/src/render/DrawTask.cpp index 0aef913d50..36ff302952 100755 --- a/libraries/render/src/render/DrawTask.cpp +++ b/libraries/render/src/render/DrawTask.cpp @@ -14,12 +14,10 @@ #include #include -#include -#include -#include #include #include #include +#include using namespace render; diff --git a/tests/animation/CMakeLists.txt b/tests/animation/CMakeLists.txt new file mode 100644 index 0000000000..2e9dbc9424 --- /dev/null +++ b/tests/animation/CMakeLists.txt @@ -0,0 +1,9 @@ +# Declare dependencies +macro (setup_testcase_dependencies) + # link in the shared libraries + link_hifi_libraries(shared animation gpu fbx model) + + copy_dlls_beside_windows_executable() +endmacro () + +setup_hifi_testcase() diff --git a/tests/animation/src/RigTests.cpp b/tests/animation/src/RigTests.cpp new file mode 100644 index 0000000000..8dbba30031 --- /dev/null +++ b/tests/animation/src/RigTests.cpp @@ -0,0 +1,107 @@ +// +// RigTests.cpp +// tests/rig/src +// +// Created by Howard Stearns on 6/16/15 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +/* FIXME/TBD: + + The following lower level functionality might be separated out into a separate class, covered by a separate test case class: + - With no input, initial pose is standing, arms at side + - Some single animation produces correct results at a given keyframe time. + - Some single animation produces correct results at a given interpolated time. + - Blend between two animations, started at separate times, produces correct result at a given interpolated time. + - Head orientation can be overridden to produce change that doesn't come from the playing animation. + - Hand position/orientation can be overridden to produce change that doesn't come from the playing animation. + - Hand position/orientation can be overrridden to produce elbow change that doesn't come from the playing animation. + - Respect scaling? (e.g., so that MyAvatar.increase/decreaseSize can alter rig, such that anti-scating and footfalls-on-stairs works) + + Higher level functionality: + - start/stopAnimation adds the animation to that which is playing, blending/fading as needed. + - thrust causes walk role animation to be used. + - turning causes turn role animation to be used. (two tests, correctly symmetric left & right) + - walk/turn do not skate (footfalls match over-ground velocity) + - (Later?) walk up stairs / hills have proper footfall for terrain + - absence of above causes return to idle role animation to be used + - (later?) The lower-level head/hand placements respect previous state. E.g., actual hand movement can be slower than requested. + - (later) The lower-level head/hand placements can move whole skeleton. E.g., turning head past a limit may turn whole body. Reaching up can move shoulders and hips. + + Backward-compatability operations. We should think of this behavior as deprecated: + - clearJointData return to standing. TBD: presumably with idle and all other animations NOT playing, until explicitly reenabled with a new TBD method? + - setJointData applies the given data. Same TBD. + These can be defined true or false, but the tests document the behavior and tells us if something's changed: + - An external change to the original skeleton IS/ISN'T seen by the rig. + - An external change to the original skeleton's head orientation IS/ISN'T seen by the rig. + - An external change to the original skeleton's hand orientiation IS/ISN'T seen by the rig. + */ + +#include + +#include "FBXReader.h" +#include "OBJReader.h" + +#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 << " 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"; +} +static void reportByName(RigPointer rig, const QString& name) { + int jointIndex = rig->indexOfJoint(name); + reportJoint(jointIndex, rig->getJointState(jointIndex)); +} +static void reportAll(RigPointer rig) { + for (int i = 0; i < rig->getJointStateCount(); i++) { + JointState joint = rig->getJointState(i); + reportJoint(i, joint); + } +} +static void reportSome(RigPointer rig) { + QString names[] = {"Head", "Neck", "RightShoulder", "RightArm", "RightForeArm", "RightHand", "Spine2", "Spine1", "Spine", "Hips", "RightUpLeg", "RightLeg", "RightFoot", "RightToeBase", "RightToe_End"}; + for (auto name : names) { + reportByName(rig, name); + } +} + +void RigTests::initialPoseArmsDown() { + //reportAll(_rig); + reportSome(_rig); +} diff --git a/tests/animation/src/RigTests.h b/tests/animation/src/RigTests.h new file mode 100644 index 0000000000..4280c0a8fa --- /dev/null +++ b/tests/animation/src/RigTests.h @@ -0,0 +1,55 @@ +// +// RigTests.h +// tests/rig/src +// +// Created by Howard Stearns on 6/16/15 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_RigTests_h +#define hifi_RigTests_h + +#include +#include + +//#include "../QTestExtensions.h" + + +// The QTest terminology is not consistent with itself or with industry: +// The whole directory, and the rig-tests target, doesn't seem to be a QTest concept, an corresponds roughly to a toplevel suite of suites. +// The directory can contain any number of classes like this one. (Don't forget to wipe your build dir, and rerun cmake when you add one.): +// QTest doc (http://doc.qt.io/qt-5/qtest-overview.html) calls this a "test case". +// The output of QTest's 'ctest' runner calls this a "test" when run in the whole directory (e.g., when reporting success/failure counts). +// The test case (like this class) can contain any number of test slots: +// QTest doc calls these "test functions" +// When you run a single test case executable (e.g., "rig-RigTests"), the (unlabeled) count includes these test functions and also the before method, which is auto generated as initTestCase. + +// To build and run via make: +// make help | grep tests # shows all test targets, including all-tests and rig-tests. +// make all-tests # will compile and then die as soon as any test case dies, even if its not in your directory +// make rig-tests # will compile and run `ctest .` in the tests/rig directory, running all the test cases found there. +// Alas, only summary output is shown on stdout. The real results, including any stdout that your code does, is in tests/rig/Testing/Temporary/LastTest.log, or... +// tests/rig/rig-RigTests (or the executable corresponding to any test case you define here) will run just that case and give output directly. +// +// To build and run via Xcode: +// On some machines, xcode can't find cmake on the path it uses. I did, effectively: sudo ln -s `which cmake` /usr/bin +// Note the above make instructions. +// all-tests, rig-tests, and rig-RigTests are all targets: +// The first two of these show no output at all, but if there's a failure you can see it by clicking on the red failure in the "issue navigator" (or by externally viewing the .log above). +// The last (or any other individual test case executable) does show output in the Xcode output display. + +class RigTests : public QObject { + Q_OBJECT + + private slots: + void initTestCase(); + void initialPoseArmsDown(); + + private: + RigPointer _rig; +}; + +#endif // hifi_RigTests_h diff --git a/tests/render-utils/src/main.cpp b/tests/render-utils/src/main.cpp index 3b7eb18368..9abd533650 100644 --- a/tests/render-utils/src/main.cpp +++ b/tests/render-utils/src/main.cpp @@ -8,30 +8,35 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - #include #include + #include -#include +#include #include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +#include class RateCounter { std::vector times; @@ -119,6 +124,10 @@ public: show(); makeCurrent(); + gpu::Context::init(); + + + { QOpenGLDebugLogger* logger = new QOpenGLDebugLogger(this); logger->initialize(); // initializes in the current context, i.e. ctx @@ -130,23 +139,6 @@ public: } qDebug() << (const char*)glGetString(GL_VERSION); -#ifdef WIN32 - glewExperimental = true; - GLenum err = glewInit(); - if (GLEW_OK != err) { - /* Problem: glewInit failed, something is seriously wrong. */ - const GLubyte * errStr = glewGetErrorString(err); - qDebug("Error: %s\n", errStr); - } - qDebug("Status: Using GLEW %s\n", glewGetString(GLEW_VERSION)); - - if (wglewGetExtension("WGL_EXT_swap_control")) { - int swapInterval = wglGetSwapIntervalEXT(); - qDebug("V-Sync is %s\n", (swapInterval > 0 ? "ON" : "OFF")); - } - glGetError(); -#endif - //_textRenderer[0] = TextRenderer::getInstance(SANS_FONT_FAMILY, 12, false); //_textRenderer[1] = TextRenderer::getInstance(SERIF_FONT_FAMILY, 12, false, // TextRenderer::SHADOW_EFFECT); diff --git a/tools/vhacd-util/src/VHACDUtil.cpp b/tools/vhacd-util/src/VHACDUtil.cpp index 2f8175cbb6..3c02c956e4 100644 --- a/tools/vhacd-util/src/VHACDUtil.cpp +++ b/tools/vhacd-util/src/VHACDUtil.cpp @@ -12,6 +12,8 @@ #include #include "VHACDUtil.h" +const float COLLISION_TETRAHEDRON_SCALE = 0.25f; + // FBXReader jumbles the order of the meshes by reading them back out of a hashtable. This will put // them back in the order in which they appeared in the file. @@ -113,6 +115,8 @@ void vhacd::VHACDUtil::fattenMeshes(const FBXMesh& mesh, FBXMesh& result, int index1 = triangles[i * 3 + 1] + indexStartOffset; int index2 = triangles[i * 3 + 2] + indexStartOffset; + // TODO: skip triangles with a normal that points more negative-y than positive-y + glm::vec3 p0 = result.vertices[index0]; glm::vec3 p1 = result.vertices[index1]; glm::vec3 p2 = result.vertices[index2]; @@ -125,12 +129,14 @@ void vhacd::VHACDUtil::fattenMeshes(const FBXMesh& mesh, FBXMesh& result, continue; } + // from the middle of the triangle, pull a point down to form a tetrahedron. float dropAmount = 0; dropAmount = glm::max(glm::length(p1 - p0), dropAmount); dropAmount = glm::max(glm::length(p2 - p1), dropAmount); dropAmount = glm::max(glm::length(p0 - p2), dropAmount); + dropAmount *= COLLISION_TETRAHEDRON_SCALE; - glm::vec3 p3 = av - glm::vec3(0, dropAmount, 0); // a point 1 meter below the average of this triangle's points + glm::vec3 p3 = av - glm::vec3(0.0f, dropAmount, 0.0f); int index3 = result.vertices.size(); result.vertices << p3; // add the new point to the result mesh