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/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 4ee3709f4a..f1ef38ade9 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 1c91f9282c..acdfd8cfc9 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.h b/interface/src/avatar/Avatar.h index 6035c44389..e2711bc34b 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -169,9 +169,6 @@ public: void setMotionState(AvatarMotionState* motionState) { _motionState = motionState; } AvatarMotionState* getMotionState() { return _motionState; } - signals: - void collisionWithAvatar(const QUuid& myUUID, const QUuid& theirUUID, const CollisionInfo& collision); - protected: SkeletonModel _skeletonModel; glm::vec3 _skeletonOffset; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 0c01ce0249..2d7cf4ca5e 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -29,7 +29,6 @@ #include #include #include -#include #include #include #include @@ -104,7 +103,6 @@ MyAvatar::MyAvatar(RigPointer rig) : _rig(rig), _prevShouldDrawHead(true) { - ShapeCollider::initDispatchTable(); for (int i = 0; i < MAX_DRIVE_KEYS; i++) { _driveKeys[i] = 0.0f; } 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/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 78091cd1a6..49321e7d6d 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -10,11 +10,15 @@ // #include "ModelOverlay.h" +#include "EntityRig.h" #include "Application.h" + +QString const ModelOverlay::TYPE = "model"; + ModelOverlay::ModelOverlay() - : _model(nullptr), + : _model(std::make_shared()), _modelTextures(QVariantMap()), _updateModel(false) { @@ -24,7 +28,7 @@ ModelOverlay::ModelOverlay() ModelOverlay::ModelOverlay(const ModelOverlay* modelOverlay) : Volume3DOverlay(modelOverlay), - _model(nullptr), + _model(std::make_shared()), _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/Rig.cpp b/libraries/animation/src/Rig.cpp index 1fe0c6f4bb..bc781feaa5 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -58,15 +58,18 @@ void Rig::removeAnimationHandle(const AnimationHandlePointer& 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; + // This is different than startAnimationByRole, in which we use the existing values if the animation already exists. + // Here we reuse the animation handle if possible, but in any case, we set the values to those given (or defaulted). + AnimationHandlePointer handle = nullptr; foreach (const AnimationHandlePointer& candidate, _animationHandles) { if (candidate->getURL() == url) { - candidate->start(); - return; + handle = candidate; } } - AnimationHandlePointer handle = createAnimationHandle(); - handle->setURL(url); + if (!handle) { + handle = createAnimationHandle(); + handle->setURL(url); + } handle->setFPS(fps); handle->setPriority(priority); handle->setLoop(loop); @@ -90,8 +93,34 @@ void Rig::addAnimationByRole(const QString& role, const QString& url, float fps, } } AnimationHandlePointer handle = createAnimationHandle(); + QString standard = ""; + if (url.isEmpty()) { // Default animations for fight club + const QString& base = "https://hifi-public.s3.amazonaws.com/ozan/"; + if (role == "walk") { + standard = base + "support/FightClubBotTest1/Animations/standard_walk.fbx"; + lastFrame = 60; + } else if (role == "leftTurn") { + standard = base + "support/FightClubBotTest1/Animations/left_turn_noHipRotation.fbx"; + lastFrame = 29; + } else if (role == "rightTurn") { + standard = base + "support/FightClubBotTest1/Animations/right_turn_noHipRotation.fbx"; + lastFrame = 31; + } else if (role == "leftStrafe") { + standard = base + "animations/fightclub_bot_anims/side_step_left_inPlace.fbx"; + lastFrame = 31; + } else if (role == "rightStrafe") { + standard = base + "animations/fightclub_bot_anims/side_step_right_inPlace.fbx"; + lastFrame = 31; + } else if (role == "idle") { + standard = base + "support/FightClubBotTest1/Animations/standard_idle.fbx"; + fps = 25.0f; + } + if (!standard.isEmpty()) { + loop = true; + } + } handle->setRole(role); - handle->setURL(url); + handle->setURL(url.isEmpty() ? standard : url); handle->setFPS(fps); handle->setPriority(priority); handle->setLoop(loop); @@ -135,6 +164,14 @@ void Rig::addRunningAnimation(AnimationHandlePointer animationHandle) { bool Rig::isRunningAnimation(AnimationHandlePointer animationHandle) { return _runningAnimations.contains(animationHandle); } +bool Rig::isRunningRole(const QString& role) { //obviously, there are more efficient ways to do this + for (auto animation : _runningAnimations) { + if (animation->getRole() == role) { + return true; + } + } + return false; +} void Rig::deleteAnimations() { for (auto animation : _animationHandles) { @@ -372,37 +409,38 @@ glm::mat4 Rig::getJointVisibleTransform(int jointIndex) const { } 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; + if (!_enableRig) { + return; } + bool isMoving = false; + glm::vec3 front = worldRotation * IDENTITY_FRONT; + float forwardSpeed = glm::dot(worldVelocity, front); + float rightLateralSpeed = glm::dot(worldVelocity, worldRotation * IDENTITY_RIGHT); + float rightTurningSpeed = glm::orientedAngle(front, _lastFront, IDENTITY_UP) / deltaTime; + auto updateRole = [&](const QString& role, bool isOn) { + isMoving = isMoving || isOn; + if (isOn) { + if (!isRunningRole(role)) { + qCDebug(animation) << "Rig STARTING" << role; + startAnimationByRole(role); + } + } else { + if (isRunningRole(role)) { + qCDebug(animation) << "Rig stopping" << role; + stopAnimationByRole(role); + } + } + }; + updateRole("walk", std::abs(forwardSpeed) > 0.01f); + bool isTurning = std::abs(rightTurningSpeed) > 0.5f; + updateRole("rightTurn", isTurning && (rightTurningSpeed > 0)); + updateRole("leftTurn", isTurning && (rightTurningSpeed < 0)); + bool isStrafing = std::abs(rightLateralSpeed) > 0.01f; + updateRole("rightStrafe", isStrafing && (rightLateralSpeed > 0.0f)); + updateRole("leftStrafe", isStrafing && (rightLateralSpeed < 0.0f)); + updateRole("idle", !isMoving); // Must be last, as it makes isMoving bogus. + _lastFront = front; + _lastPosition = worldPosition; } void Rig::updateAnimations(float deltaTime, glm::mat4 parentTransform) { diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index ede9836bf3..1dbda23760 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -75,6 +75,7 @@ public: bool removeRunningAnimation(AnimationHandlePointer animationHandle); void addRunningAnimation(AnimationHandlePointer animationHandle); bool isRunningAnimation(AnimationHandlePointer animationHandle); + bool isRunningRole(const QString& role); // There can be multiple animations per role, so this is more general than isRunningAnimation. const QList& getRunningAnimations() const { return _runningAnimations; } void deleteAnimations(); const QList& getAnimationHandles() const { return _animationHandles; } @@ -179,9 +180,6 @@ public: QList _runningAnimations; bool _enableRig; - bool _isWalking; - bool _isTurning; - bool _isIdle; glm::vec3 _lastFront; glm::vec3 _lastPosition; }; 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/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index a020be0f7a..565e30b5bd 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -46,7 +46,6 @@ typedef unsigned long long quint64; #include #include -#include #include #include #include @@ -257,10 +256,6 @@ public: const HeadData* getHeadData() const { return _headData; } const HandData* getHandData() const { return _handData; } - virtual bool findSphereCollisions(const glm::vec3& particleCenter, float particleRadius, CollisionList& collisions) { - return false; - } - bool hasIdentityChangedAfterParsing(NLPacket& packet); QByteArray identityByteArray(); 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/src/EntityItem.h b/libraries/entities/src/EntityItem.h index b698a5c511..57f8883cea 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -18,7 +18,6 @@ #include #include // for Animation, AnimationCache, and AnimationPointer classes -#include #include // for EncodeBitstreamParams class #include // for OctreeElement::AppendState #include 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/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index c232437757..d3710c6ccc 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -16,7 +16,6 @@ #include -#include #include #include #include diff --git a/libraries/entities/src/TextEntityItem.cpp b/libraries/entities/src/TextEntityItem.cpp index a851d025fc..3ab15ba6cb 100644 --- a/libraries/entities/src/TextEntityItem.cpp +++ b/libraries/entities/src/TextEntityItem.cpp @@ -15,7 +15,7 @@ #include #include -#include +#include #include "EntityTree.h" #include "EntityTreeElement.h" @@ -128,46 +128,13 @@ void TextEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBits } - bool TextEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face, void** intersectedObject, bool precisionPicking) const { - - RayIntersectionInfo rayInfo; - rayInfo._rayStart = origin; - rayInfo._rayDirection = direction; - rayInfo._rayLength = std::numeric_limits::max(); - - PlaneShape plane; - - const glm::vec3 UNROTATED_NORMAL(0.0f, 0.0f, -1.0f); - glm::vec3 normal = getRotation() * UNROTATED_NORMAL; - plane.setNormal(normal); - plane.setPoint(getPosition()); // the position is definitely a point on our plane - - bool intersects = plane.findRayIntersection(rayInfo); - - if (intersects) { - glm::vec3 hitAt = origin + (direction * rayInfo._hitDistance); - // now we know the point the ray hit our plane - - glm::mat4 rotation = glm::mat4_cast(getRotation()); - glm::mat4 translation = glm::translate(getPosition()); - glm::mat4 entityToWorldMatrix = translation * rotation; - glm::mat4 worldToEntityMatrix = glm::inverse(entityToWorldMatrix); - - glm::vec3 dimensions = getDimensions(); - glm::vec3 registrationPoint = getRegistrationPoint(); - glm::vec3 corner = -(dimensions * registrationPoint); - AABox entityFrameBox(corner, dimensions); - - glm::vec3 entityFrameHitAt = glm::vec3(worldToEntityMatrix * glm::vec4(hitAt, 1.0f)); - - intersects = entityFrameBox.contains(entityFrameHitAt); - } - - if (intersects) { - distance = rayInfo._hitDistance; - } - return intersects; + glm::vec3 dimensions = getDimensions(); + glm::vec2 xyDimensions(dimensions.x, dimensions.y); + glm::quat rotation = getRotation(); + glm::vec3 position = getPosition() + rotation * + (dimensions * (getRegistrationPoint() - ENTITY_ITEM_DEFAULT_REGISTRATION_POINT)); + return findRayRectangleIntersection(origin, direction, rotation, position, xyDimensions, distance); } diff --git a/libraries/entities/src/WebEntityItem.cpp b/libraries/entities/src/WebEntityItem.cpp index 2cb233bbee..c3dc757199 100644 --- a/libraries/entities/src/WebEntityItem.cpp +++ b/libraries/entities/src/WebEntityItem.cpp @@ -13,7 +13,7 @@ #include #include -#include +#include #include "EntityTree.h" #include "EntityTreeElement.h" @@ -98,50 +98,17 @@ void WebEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitst APPEND_ENTITY_PROPERTY(PROP_SOURCE_URL, _sourceUrl); } - bool WebEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face, void** intersectedObject, bool precisionPicking) const { - - RayIntersectionInfo rayInfo; - rayInfo._rayStart = origin; - rayInfo._rayDirection = direction; - rayInfo._rayLength = std::numeric_limits::max(); - - PlaneShape plane; - - const glm::vec3 UNROTATED_NORMAL(0.0f, 0.0f, -1.0f); - glm::vec3 normal = getRotation() * UNROTATED_NORMAL; - plane.setNormal(normal); - plane.setPoint(getPosition()); // the position is definitely a point on our plane - - bool intersects = plane.findRayIntersection(rayInfo); - - if (intersects) { - glm::vec3 hitAt = origin + (direction * rayInfo._hitDistance); - // now we know the point the ray hit our plane - - glm::mat4 rotation = glm::mat4_cast(getRotation()); - glm::mat4 translation = glm::translate(getPosition()); - glm::mat4 entityToWorldMatrix = translation * rotation; - glm::mat4 worldToEntityMatrix = glm::inverse(entityToWorldMatrix); - - glm::vec3 dimensions = getDimensions(); - glm::vec3 registrationPoint = getRegistrationPoint(); - glm::vec3 corner = -(dimensions * registrationPoint); - AABox entityFrameBox(corner, dimensions); - - glm::vec3 entityFrameHitAt = glm::vec3(worldToEntityMatrix * glm::vec4(hitAt, 1.0f)); - - intersects = entityFrameBox.contains(entityFrameHitAt); - } - - if (intersects) { - distance = rayInfo._hitDistance; - } - return intersects; + glm::vec3 dimensions = getDimensions(); + glm::vec2 xyDimensions(dimensions.x, dimensions.y); + glm::quat rotation = getRotation(); + glm::vec3 position = getPosition() + rotation * + (dimensions * (getRegistrationPoint() - ENTITY_ITEM_DEFAULT_REGISTRATION_POINT)); + return findRayRectangleIntersection(origin, direction, rotation, position, xyDimensions, distance); } - + void WebEntityItem::setSourceUrl(const QString& value) { if (_sourceUrl != value) { _sourceUrl = value; diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 35b05ca30f..3b7d18f1d8 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -28,7 +28,6 @@ #include #include #include -#include #include #include @@ -830,6 +829,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 +1138,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); diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h index f931bd5e7e..b8a22b0b80 100644 --- a/libraries/fbx/src/FBXReader.h +++ b/libraries/fbx/src/FBXReader.h @@ -105,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; @@ -122,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 3d7cf6582e..b7ae948490 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -23,7 +23,6 @@ #include #include "FBXReader.h" #include "OBJReader.h" -#include "Shape.h" #include "ModelFormatLogging.h" 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/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index 203ff2b072..48f5f99906 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -38,7 +38,6 @@ #include #include #include -#include #include #include "CoverageMap.h" @@ -791,13 +790,6 @@ public: bool found; }; -class ShapeArgs { -public: - const Shape* shape; - CollisionList& collisions; - bool found; -}; - class ContentArgs { public: AACube cube; diff --git a/libraries/octree/src/OctreeElement.cpp b/libraries/octree/src/OctreeElement.cpp index e11a11fc8e..1143bdf829 100644 --- a/libraries/octree/src/OctreeElement.cpp +++ b/libraries/octree/src/OctreeElement.cpp @@ -19,7 +19,6 @@ #include #include #include -#include #include "AACube.h" #include "OctalCode.h" @@ -640,50 +639,7 @@ OctreeElement* OctreeElement::getOrCreateChildElementAt(float x, float y, float // otherwise, we need to find which of our children we should recurse glm::vec3 ourCenter = _cube.calcCenter(); - int childIndex = CHILD_UNKNOWN; - // left half - if (x > ourCenter.x) { - if (y > ourCenter.y) { - // top left - if (z > ourCenter.z) { - // top left far - childIndex = CHILD_TOP_LEFT_FAR; - } else { - // top left near - childIndex = CHILD_TOP_LEFT_NEAR; - } - } else { - // bottom left - if (z > ourCenter.z) { - // bottom left far - childIndex = CHILD_BOTTOM_LEFT_FAR; - } else { - // bottom left near - childIndex = CHILD_BOTTOM_LEFT_NEAR; - } - } - } else { - // right half - if (y > ourCenter.y) { - // top right - if (z > ourCenter.z) { - // top right far - childIndex = CHILD_TOP_RIGHT_FAR; - } else { - // top right near - childIndex = CHILD_TOP_RIGHT_NEAR; - } - } else { - // bottom right - if (z > ourCenter.z) { - // bottom right far - childIndex = CHILD_BOTTOM_RIGHT_FAR; - } else { - // bottom right near - childIndex = CHILD_BOTTOM_RIGHT_NEAR; - } - } - } + int childIndex = getMyChildContainingPoint(glm::vec3(x, y, z)); // Now, check if we have a child at that location child = getChildAtIndex(childIndex); diff --git a/libraries/octree/src/OctreeElement.h b/libraries/octree/src/OctreeElement.h index 830655242f..d2f3f7d3aa 100644 --- a/libraries/octree/src/OctreeElement.h +++ b/libraries/octree/src/OctreeElement.h @@ -24,7 +24,6 @@ #include "ViewFrustum.h" #include "OctreeConstants.h" -class CollisionList; class EncodeBitstreamParams; class Octree; class OctreeElement; 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 24d52a8b3b..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) { diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index d89c40a0be..2a546f0f29 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -16,20 +16,17 @@ #include #include -#include #include -#include -#include #include #include -#include "PhysicsEntity.h" #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" @@ -95,7 +92,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, @@ -188,13 +185,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");; @@ -247,6 +240,9 @@ QVector Model::createJointStates(const FBXGeometry& geometry) { }; void Model::initJointTransforms() { + if (!_geometry) { + return; + } const FBXGeometry& geometry = _geometry->getFBXGeometry(); glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; _rig->initJointTransforms(parentTransform); @@ -428,6 +424,9 @@ bool Model::updateGeometry() { deleteGeometry(); _dilatedTextures.clear(); + if (!geometry) { + std::cout << "WARNING: no geometry in Model::updateGeometry\n"; + } setGeometry(geometry); _meshGroupsKnown = false; @@ -1774,8 +1773,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/PhysicsEntity.h b/libraries/render-utils/src/PhysicsEntity.h index 3b527c7827..f36473af26 100644 --- a/libraries/render-utils/src/PhysicsEntity.h +++ b/libraries/render-utils/src/PhysicsEntity.h @@ -18,9 +18,6 @@ #include #include -#include -#include - class PhysicsEntity { public: 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/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index d5e727657c..cfd6cda56b 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include @@ -366,8 +365,6 @@ void ScriptEngine::init() { // constants globalObject().setProperty("TREE_SCALE", newVariant(QVariant(TREE_SCALE))); - globalObject().setProperty("COLLISION_GROUP_ENVIRONMENT", newVariant(QVariant(COLLISION_GROUP_ENVIRONMENT))); - globalObject().setProperty("COLLISION_GROUP_AVATARS", newVariant(QVariant(COLLISION_GROUP_AVATARS))); } QScriptValue ScriptEngine::registerGlobalObject(const QString& name, QObject* object) { diff --git a/libraries/shared/src/AACubeShape.cpp b/libraries/shared/src/AACubeShape.cpp deleted file mode 100644 index 5c40b3bb64..0000000000 --- a/libraries/shared/src/AACubeShape.cpp +++ /dev/null @@ -1,76 +0,0 @@ -// -// AACubeShape.cpp -// libraries/shared/src -// -// Created by Andrew Meadows on 2014.08.22 -// 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 -#include - -#include "AACubeShape.h" -#include "NumericalConstants.h" // for SQUARE_ROOT_OF_3 - -glm::vec3 faceNormals[3] = { glm::vec3(1.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f) }; - -bool AACubeShape::findRayIntersection(RayIntersectionInfo& intersection) const { - // A = ray point - // B = cube center - glm::vec3 BA = _translation - intersection._rayStart; - - // check for ray intersection with cube's bounding sphere - // a = distance along line to closest approach to B - float a = glm::dot(intersection._rayDirection, BA); - // b2 = squared distance from cube center to point of closest approach - float b2 = glm::length2(a * intersection._rayDirection - BA); - // r = bounding radius of cube - float halfSide = 0.5f * _scale; - const float r = SQUARE_ROOT_OF_3 * halfSide; - if (b2 > r * r) { - // line doesn't hit cube's bounding sphere - return false; - } - - // check for tuncated/short ray - // maxLength = maximum possible distance between rayStart and center of cube - const float maxLength = glm::min(intersection._rayLength, intersection._hitDistance) + r; - if (a * a + b2 > maxLength * maxLength) { - // ray is not long enough to reach cube's bounding sphere - // NOTE: we don't fall in here when ray's length if FLT_MAX because maxLength^2 will be inf or nan - return false; - } - - // the trivial checks have been exhausted, so must trace to each face - bool hit = false; - for (int i = 0; i < 3; ++i) { - for (float sign = -1.0f; sign < 2.0f; sign += 2.0f) { - glm::vec3 faceNormal = sign * faceNormals[i]; - float rayDotPlane = glm::dot(intersection._rayDirection, faceNormal); - if (glm::abs(rayDotPlane) > EPSILON) { - float distanceToFace = (halfSide + glm::dot(BA, faceNormal)) / rayDotPlane; - if (distanceToFace >= 0.0f) { - glm::vec3 point = distanceToFace * intersection._rayDirection - BA; - int j = (i + 1) % 3; - int k = (i + 2) % 3; - glm::vec3 secondNormal = faceNormals[j]; - glm::vec3 thirdNormal = faceNormals[k]; - if (glm::abs(glm::dot(point, secondNormal)) > halfSide || - glm::abs(glm::dot(point, thirdNormal)) > halfSide) { - continue; - } - if (distanceToFace < intersection._hitDistance && distanceToFace < intersection._rayLength) { - intersection._hitDistance = distanceToFace; - intersection._hitNormal = faceNormal; - intersection._hitShape = const_cast(this); - hit = true; - } - } - } - } - } - return hit; -} diff --git a/libraries/shared/src/AACubeShape.h b/libraries/shared/src/AACubeShape.h deleted file mode 100644 index da7ba9d53f..0000000000 --- a/libraries/shared/src/AACubeShape.h +++ /dev/null @@ -1,50 +0,0 @@ -// -// AACubeShape.h -// libraries/shared/src -// -// Created by Andrew Meadows on 2014.08.22 -// 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_AACubeShape_h -#define hifi_AACubeShape_h - -#include -#include "Shape.h" - -class AACubeShape : public Shape { -public: - AACubeShape() : Shape(AACUBE_SHAPE), _scale(1.0f) { } - AACubeShape(float scale) : Shape(AACUBE_SHAPE), _scale(scale) { } - AACubeShape(float scale, const glm::vec3& position) : Shape(AACUBE_SHAPE, position), _scale(scale) { } - - virtual ~AACubeShape() { } - - float getScale() const { return _scale; } - void setScale(float scale) { _scale = scale; } - - bool findRayIntersection(RayIntersectionInfo& intersection) const; - - float getVolume() const { return _scale * _scale * _scale; } - virtual QDebug& dumpToDebug(QDebug& debugConext) const; - -protected: - float _scale; -}; - -inline QDebug& AACubeShape::dumpToDebug(QDebug& debugConext) const { - debugConext << "AACubeShape[ (" - << "type: " << getType() - << "position: " - << getTranslation().x << ", " << getTranslation().y << ", " << getTranslation().z - << "scale: " - << getScale() - << "]"; - - return debugConext; -} - -#endif // hifi_AACubeShape_h diff --git a/libraries/shared/src/CapsuleShape.cpp b/libraries/shared/src/CapsuleShape.cpp deleted file mode 100644 index ec71ebadbb..0000000000 --- a/libraries/shared/src/CapsuleShape.cpp +++ /dev/null @@ -1,218 +0,0 @@ -// -// CapsuleShape.cpp -// libraries/shared/src -// -// Created by Andrew Meadows on 02/20/2014. -// 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 -#include - -#include "CapsuleShape.h" -#include "GeometryUtil.h" -#include "NumericalConstants.h" - -CapsuleShape::CapsuleShape() : Shape(CAPSULE_SHAPE), _radius(0.0f), _halfHeight(0.0f) {} - -CapsuleShape::CapsuleShape(float radius, float halfHeight) : Shape(CAPSULE_SHAPE), - _radius(radius), _halfHeight(halfHeight) { - updateBoundingRadius(); -} - -CapsuleShape::CapsuleShape(float radius, float halfHeight, const glm::vec3& position, const glm::quat& rotation) : - Shape(CAPSULE_SHAPE, position, rotation), _radius(radius), _halfHeight(halfHeight) { - updateBoundingRadius(); -} - -CapsuleShape::CapsuleShape(float radius, const glm::vec3& startPoint, const glm::vec3& endPoint) : - Shape(CAPSULE_SHAPE), _radius(radius), _halfHeight(0.0f) { - setEndPoints(startPoint, endPoint); -} - -/// \param[out] startPoint is the center of start cap -void CapsuleShape::getStartPoint(glm::vec3& startPoint) const { - startPoint = _translation - _rotation * glm::vec3(0.0f, _halfHeight, 0.0f); -} - -/// \param[out] endPoint is the center of the end cap -void CapsuleShape::getEndPoint(glm::vec3& endPoint) const { - endPoint = _translation + _rotation * glm::vec3(0.0f, _halfHeight, 0.0f); -} - -void CapsuleShape::computeNormalizedAxis(glm::vec3& axis) const { - // default axis of a capsule is along the yAxis - axis = _rotation * DEFAULT_CAPSULE_AXIS; -} - -void CapsuleShape::setRadius(float radius) { - _radius = radius; - updateBoundingRadius(); -} - -void CapsuleShape::setHalfHeight(float halfHeight) { - _halfHeight = halfHeight; - updateBoundingRadius(); -} - -void CapsuleShape::setRadiusAndHalfHeight(float radius, float halfHeight) { - _radius = radius; - _halfHeight = halfHeight; - updateBoundingRadius(); -} - -void CapsuleShape::setEndPoints(const glm::vec3& startPoint, const glm::vec3& endPoint) { - glm::vec3 axis = endPoint - startPoint; - _translation = 0.5f * (endPoint + startPoint); - float height = glm::length(axis); - if (height > EPSILON) { - _halfHeight = 0.5f * height; - axis /= height; - _rotation = computeNewRotation(axis); - } - updateBoundingRadius(); -} - -// helper -bool findRayIntersectionWithCap(const glm::vec3& sphereCenter, float sphereRadius, - const glm::vec3& capsuleCenter, RayIntersectionInfo& intersection) { - float r2 = sphereRadius * sphereRadius; - - // compute closest approach (CA) - float a = glm::dot(sphereCenter - intersection._rayStart, intersection._rayDirection); // a = distance from ray-start to CA - float b2 = glm::distance2(sphereCenter, intersection._rayStart + a * intersection._rayDirection); // b2 = squared distance from sphere-center to CA - if (b2 > r2) { - // ray does not hit sphere - return false; - } - float c = sqrtf(r2 - b2); // c = distance from CA to sphere surface along intersection._rayDirection - float d2 = glm::distance2(intersection._rayStart, sphereCenter); // d2 = squared distance from sphere-center to ray-start - float distance = FLT_MAX; - if (a < 0.0f) { - // ray points away from sphere-center - if (d2 > r2) { - // ray starts outside sphere - return false; - } - // ray starts inside sphere - distance = c + a; - } else if (d2 > r2) { - // ray starts outside sphere - distance = a - c; - } else { - // ray starts inside sphere - distance = a + c; - } - if (distance > 0.0f && distance < intersection._rayLength && distance < intersection._hitDistance) { - glm::vec3 sphereCenterToHitPoint = intersection._rayStart + distance * intersection._rayDirection - sphereCenter; - if (glm::dot(sphereCenterToHitPoint, sphereCenter - capsuleCenter) >= 0.0f) { - intersection._hitDistance = distance; - intersection._hitNormal = glm::normalize(sphereCenterToHitPoint); - return true; - } - } - return false; -} - -bool CapsuleShape::findRayIntersectionWithCaps(const glm::vec3& capsuleCenter, RayIntersectionInfo& intersection) const { - glm::vec3 capCenter; - getStartPoint(capCenter); - bool hit = findRayIntersectionWithCap(capCenter, _radius, capsuleCenter, intersection); - getEndPoint(capCenter); - hit = findRayIntersectionWithCap(capCenter, _radius, capsuleCenter, intersection) || hit; - if (hit) { - intersection._hitShape = const_cast(this); - } - return hit; -} - -bool CapsuleShape::findRayIntersection(RayIntersectionInfo& intersection) const { - // ray is U, capsule is V - glm::vec3 axisV; - computeNormalizedAxis(axisV); - glm::vec3 centerV = getTranslation(); - - // first handle parallel case - float uDotV = glm::dot(axisV, intersection._rayDirection); - glm::vec3 UV = intersection._rayStart - centerV; - if (glm::abs(1.0f - glm::abs(uDotV)) < EPSILON) { - // line and cylinder are parallel - float distanceV = glm::dot(UV, intersection._rayDirection); - if (glm::length2(UV - distanceV * intersection._rayDirection) <= _radius * _radius) { - // ray is inside cylinder's radius and might intersect caps - return findRayIntersectionWithCaps(centerV, intersection); - } - return false; - } - - // Given a line with point 'U' and normalized direction 'u' and - // a cylinder with axial point 'V', radius 'r', and normalized direction 'v' - // the intersection of the two is on the line at distance 't' from 'U'. - // - // Determining the values of t reduces to solving a quadratic equation: At^2 + Bt + C = 0 - // - // where: - // - // UV = U-V - // w = u-(u.v)v - // Q = UV-(UV.v)v - // - // A = w^2 - // B = 2(w.Q) - // C = Q^2 - r^2 - - glm::vec3 w = intersection._rayDirection - uDotV * axisV; - glm::vec3 Q = UV - glm::dot(UV, axisV) * axisV; - - // we save a few multiplies by storing 2*A rather than just A - float A2 = 2.0f * glm::dot(w, w); - float B = 2.0f * glm::dot(w, Q); - - // since C is only ever used once (in the determinant) we compute it inline - float determinant = B * B - 2.0f * A2 * (glm::dot(Q, Q) - _radius * _radius); - if (determinant < 0.0f) { - return false; - } - float hitLow = (-B - sqrtf(determinant)) / A2; - float hitHigh = -(hitLow + 2.0f * B / A2); - - if (hitLow > hitHigh) { - // re-arrange so hitLow is always the smaller value - float temp = hitHigh; - hitHigh = hitLow; - hitLow = temp; - } - if (hitLow < 0.0f) { - if (hitHigh < 0.0f) { - // capsule is completely behind rayStart - return false; - } - hitLow = hitHigh; - } - - glm::vec3 p = intersection._rayStart + hitLow * intersection._rayDirection; - float d = glm::dot(p - centerV, axisV); - if (glm::abs(d) <= getHalfHeight()) { - // we definitely hit the cylinder wall - intersection._hitDistance = hitLow; - intersection._hitNormal = glm::normalize(p - centerV - d * axisV); - intersection._hitShape = const_cast(this); - return true; - } - - // ray still might hit the caps - return findRayIntersectionWithCaps(centerV, intersection); -} - -// static -glm::quat CapsuleShape::computeNewRotation(const glm::vec3& newAxis) { - float angle = glm::angle(newAxis, DEFAULT_CAPSULE_AXIS); - if (angle > EPSILON) { - glm::vec3 rotationAxis = glm::normalize(glm::cross(DEFAULT_CAPSULE_AXIS, newAxis)); - return glm::angleAxis(angle, rotationAxis); - } - return glm::quat(); -} diff --git a/libraries/shared/src/CapsuleShape.h b/libraries/shared/src/CapsuleShape.h deleted file mode 100644 index 32c09696d7..0000000000 --- a/libraries/shared/src/CapsuleShape.h +++ /dev/null @@ -1,62 +0,0 @@ -// -// CapsuleShape.h -// libraries/shared/src -// -// Created by Andrew Meadows on 02/20/2014. -// 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_CapsuleShape_h -#define hifi_CapsuleShape_h - -#include "NumericalConstants.h" -#include "Shape.h" - -// default axis of CapsuleShape is Y-axis -const glm::vec3 DEFAULT_CAPSULE_AXIS(0.0f, 1.0f, 0.0f); - - -class CapsuleShape : public Shape { -public: - CapsuleShape(); - CapsuleShape(float radius, float halfHeight); - CapsuleShape(float radius, float halfHeight, const glm::vec3& position, const glm::quat& rotation); - CapsuleShape(float radius, const glm::vec3& startPoint, const glm::vec3& endPoint); - - virtual ~CapsuleShape() {} - - float getRadius() const { return _radius; } - virtual float getHalfHeight() const { return _halfHeight; } - - /// \param[out] startPoint is the center of start cap - virtual void getStartPoint(glm::vec3& startPoint) const; - - /// \param[out] endPoint is the center of the end cap - virtual void getEndPoint(glm::vec3& endPoint) const; - - virtual void computeNormalizedAxis(glm::vec3& axis) const; - - void setRadius(float radius); - virtual void setHalfHeight(float height); - virtual void setRadiusAndHalfHeight(float radius, float height); - - /// Sets the endpoints and updates center, rotation, and halfHeight to agree. - virtual void setEndPoints(const glm::vec3& startPoint, const glm::vec3& endPoint); - - bool findRayIntersection(RayIntersectionInfo& intersection) const; - - virtual float getVolume() const { return (PI * _radius * _radius) * (1.3333333333f * _radius + getHalfHeight()); } - -protected: - bool findRayIntersectionWithCaps(const glm::vec3& capsuleCenter, RayIntersectionInfo& intersection) const; - virtual void updateBoundingRadius() { _boundingRadius = _radius + getHalfHeight(); } - static glm::quat computeNewRotation(const glm::vec3& newAxis); - - float _radius; - float _halfHeight; -}; - -#endif // hifi_CapsuleShape_h diff --git a/libraries/shared/src/CollisionInfo.cpp b/libraries/shared/src/CollisionInfo.cpp deleted file mode 100644 index 7f145efb20..0000000000 --- a/libraries/shared/src/CollisionInfo.cpp +++ /dev/null @@ -1,110 +0,0 @@ -// -// CollisionInfo.cpp -// libraries/shared/src -// -// Created by Andrew Meadows on 02/14/2014. -// 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 "CollisionInfo.h" -#include "NumericalConstants.h" -#include "Shape.h" - -CollisionInfo::CollisionInfo() : - _data(NULL), - _intData(0), - _shapeA(NULL), - _shapeB(NULL), - _damping(0.0f), - _elasticity(1.0f), - _contactPoint(0.0f), - _penetration(0.0f), - _addedVelocity(0.0f) { -} - -quint64 CollisionInfo::getShapePairKey() const { - if (_shapeB == NULL || _shapeA == NULL) { - // zero is an invalid key - return 0; - } - quint32 idA = _shapeA->getID(); - quint32 idB = _shapeB->getID(); - return idA < idB ? ((quint64)idA << 32) + (quint64)idB : ((quint64)idB << 32) + (quint64)idA; -} - -CollisionList::CollisionList(int maxSize) : - _maxSize(maxSize), - _size(0) { - _collisions.resize(_maxSize); -} - -void CollisionInfo::apply() { - assert(_shapeA); - // NOTE: Shape::computeEffectiveMass() has side effects: computes and caches partial Lagrangian coefficients - Shape* shapeA = const_cast(_shapeA); - float massA = shapeA->computeEffectiveMass(_penetration, _contactPoint); - float massB = MAX_SHAPE_MASS; - float totalMass = massA + massB; - if (_shapeB) { - Shape* shapeB = const_cast(_shapeB); - massB = shapeB->computeEffectiveMass(-_penetration, _contactPoint - _penetration); - totalMass = massA + massB; - if (totalMass < EPSILON) { - massA = massB = 1.0f; - totalMass = 2.0f; - } - // remember that _penetration points from A into B - shapeB->accumulateDelta(massA / totalMass, _penetration); - } - // NOTE: Shape::accumulateDelta() uses the coefficients from previous call to Shape::computeEffectiveMass() - // remember that _penetration points from A into B - shapeA->accumulateDelta(massB / totalMass, -_penetration); -} - -CollisionInfo* CollisionList::getNewCollision() { - // return pointer to existing CollisionInfo, or NULL of list is full - return (_size < _maxSize) ? &(_collisions[_size++]) : NULL; -} - -void CollisionList::deleteLastCollision() { - if (_size > 0) { - --_size; - } -} - -CollisionInfo* CollisionList::getCollision(int index) { - return (index > -1 && index < _size) ? &(_collisions[index]) : NULL; -} - -CollisionInfo* CollisionList::getLastCollision() { - return (_size > 0) ? &(_collisions[_size - 1]) : NULL; -} - -void CollisionList::clear() { - // we rely on the external context to properly set or clear the data members of CollisionInfos - /* - for (int i = 0; i < _size; ++i) { - // we only clear the important stuff - CollisionInfo& collision = _collisions[i]; - //collision._data = NULL; - //collision._intData = 0; - //collision._floatDAta = 0.0f; - //collision._vecData = glm::vec3(0.0f); - //collision._shapeA = NULL; - //collision._shapeB = NULL; - //collision._damping; - //collision._elasticity; - //collision._contactPoint; - //collision._penetration; - //collision._addedVelocity; - } - */ - _size = 0; -} - -CollisionInfo* CollisionList::operator[](int index) { - return (index > -1 && index < _size) ? &(_collisions[index]) : NULL; -} diff --git a/libraries/shared/src/CollisionInfo.h b/libraries/shared/src/CollisionInfo.h deleted file mode 100644 index e6daf949b3..0000000000 --- a/libraries/shared/src/CollisionInfo.h +++ /dev/null @@ -1,101 +0,0 @@ -// -// CollisionInfo.h -// libraries/shared/src -// -// Created by Andrew Meadows on 02/14/2014. -// 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_CollisionInfo_h -#define hifi_CollisionInfo_h - -#include -#include - -#include -#include - -class Shape; - -const quint32 COLLISION_GROUP_ENVIRONMENT = 1U << 0; -const quint32 COLLISION_GROUP_AVATARS = 1U << 1; -const quint32 COLLISION_GROUP_VOXELS = 1U << 2; -const quint32 VALID_COLLISION_GROUPS = 0x0f; - -// CollisionInfo contains details about the collision between two things: BodyA and BodyB. -// The assumption is that the context that analyzes the collision knows about BodyA but -// does not necessarily know about BodyB. Hence the data storred in the CollisionInfo -// is expected to be relative to BodyA (for example the penetration points from A into B). - -class CollisionInfo { -public: - CollisionInfo(); - ~CollisionInfo() {} - - // TODO: Andrew to get rid of these data members - void* _data; - int _intData; - float _floatData; - glm::vec3 _vecData; - - /// accumulates position changes for the shapes in this collision to resolve penetration - void apply(); - - Shape* getShapeA() const { return const_cast(_shapeA); } - Shape* getShapeB() const { return const_cast(_shapeB); } - - /// \return unique key for shape pair - quint64 getShapePairKey() const; - - const Shape* _shapeA; // pointer to shapeA in this collision - const Shape* _shapeB; // pointer to shapeB in this collision - - void* _extraData; // pointer to extraData for this collision, opaque to the collision info, useful for external data - - float _damping; // range [0,1] of friction coeficient - float _elasticity; // range [0,1] of energy conservation - glm::vec3 _contactPoint; // world-frame point on BodyA that is deepest into BodyB - glm::vec3 _penetration; // depth that BodyA penetrates into BodyB - glm::vec3 _addedVelocity; // velocity of BodyB -}; - -// CollisionList is intended to be a recycled container. Fill the CollisionInfo's, -// use them, and then clear them for the next frame or context. - -class CollisionList { -public: - CollisionList(int maxSize); - - /// \return pointer to next collision. NULL if list is full. - CollisionInfo* getNewCollision(); - - /// \forget about collision at the end - void deleteLastCollision(); - - /// \return pointer to collision by index. NULL if index out of bounds. - CollisionInfo* getCollision(int index); - - /// \return pointer to last collision on the list. NULL if list is empty - CollisionInfo* getLastCollision(); - - /// \return true if list is full - bool isFull() const { return _size == _maxSize; } - - /// \return number of valid collisions - int size() const { return _size; } - - /// Clear valid collisions. - void clear(); - - CollisionInfo* operator[](int index); - -private: - int _maxSize; // the container cannot get larger than this - int _size; // the current number of valid collisions in the list - QVector _collisions; -}; - -#endif // hifi_CollisionInfo_h diff --git a/libraries/shared/src/GeometryUtil.cpp b/libraries/shared/src/GeometryUtil.cpp index afb5890d05..b612fe0696 100644 --- a/libraries/shared/src/GeometryUtil.cpp +++ b/libraries/shared/src/GeometryUtil.cpp @@ -11,11 +11,10 @@ #include #include +#include #include "GeometryUtil.h" #include "NumericalConstants.h" -#include "PlaneShape.h" -#include "RayIntersectionInfo.h" glm::vec3 computeVectorFromPointToSegment(const glm::vec3& point, const glm::vec3& start, const glm::vec3& end) { // compute the projection of the point vector onto the segment vector @@ -496,31 +495,45 @@ void PolygonClip::copyCleanArray(int& lengthA, glm::vec2* vertexArrayA, int& len bool findRayRectangleIntersection(const glm::vec3& origin, const glm::vec3& direction, const glm::quat& rotation, const glm::vec3& position, const glm::vec2& dimensions, float& distance) { - RayIntersectionInfo rayInfo; - rayInfo._rayStart = origin; - rayInfo._rayDirection = direction; - rayInfo._rayLength = std::numeric_limits::max(); - - PlaneShape plane; - const glm::vec3 UNROTATED_NORMAL(0.0f, 0.0f, -1.0f); glm::vec3 normal = rotation * UNROTATED_NORMAL; - plane.setNormal(normal); - plane.setPoint(position); // the position is definitely a point on our plane - bool intersects = plane.findRayIntersection(rayInfo); + bool maybeIntersects = false; + float denominator = glm::dot(normal, direction); + glm::vec3 offset = origin - position; + float normDotOffset = glm::dot(offset, normal); + float d = 0.0f; + if (fabsf(denominator) < EPSILON) { + // line is perpendicular to plane + if (fabsf(normDotOffset) < EPSILON) { + // ray starts on the plane + maybeIntersects = true; - if (intersects) { - distance = rayInfo._hitDistance; - - glm::vec3 hitPosition = origin + (distance * direction); - glm::vec3 localHitPosition = glm::inverse(rotation) * (hitPosition - position); - - glm::vec2 halfDimensions = 0.5f * dimensions; - - intersects = -halfDimensions.x <= localHitPosition.x && localHitPosition.x <= halfDimensions.x - && -halfDimensions.y <= localHitPosition.y && localHitPosition.y <= halfDimensions.y; + // compute distance to closest approach + d = - glm::dot(offset, direction); // distance to closest approach of center of rectangle + if (d < 0.0f) { + // ray points away from center of rectangle, so ray's start is the closest approach + d = 0.0f; + } + } + } else { + d = - normDotOffset / denominator; + if (d > 0.0f) { + // ray points toward plane + maybeIntersects = true; + } } - return intersects; + if (maybeIntersects) { + glm::vec3 hitPosition = origin + (d * direction); + glm::vec3 localHitPosition = glm::inverse(rotation) * (hitPosition - position); + glm::vec2 halfDimensions = 0.5f * dimensions; + if (fabsf(localHitPosition.x) < halfDimensions.x && fabsf(localHitPosition.y) < halfDimensions.y) { + // only update distance on intersection + distance = d; + return true; + } + } + + return false; } diff --git a/libraries/shared/src/ListShape.cpp b/libraries/shared/src/ListShape.cpp deleted file mode 100644 index 255330c713..0000000000 --- a/libraries/shared/src/ListShape.cpp +++ /dev/null @@ -1,90 +0,0 @@ -// -// ListShape.cpp -// libraries/shared/src -// -// Created by Andrew Meadows on 02/20/2014. -// 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 "ListShape.h" - -// ListShapeEntry - -void ListShapeEntry::updateTransform(const glm::vec3& rootPosition, const glm::quat& rootRotation) { - _shape->setTranslation(rootPosition + rootRotation * _localPosition); - _shape->setRotation(_localRotation * rootRotation); -} - -// ListShape - -ListShape::~ListShape() { - clear(); -} - -void ListShape::setTranslation(const glm::vec3& position) { - _subShapeTransformsAreDirty = true; - Shape::setTranslation(position); -} - -void ListShape::setRotation(const glm::quat& rotation) { - _subShapeTransformsAreDirty = true; - Shape::setRotation(rotation); -} - -const Shape* ListShape::getSubShape(int index) const { - if (index < 0 || index > _subShapeEntries.size()) { - return NULL; - } - return _subShapeEntries[index]._shape; -} - -void ListShape::updateSubTransforms() { - if (_subShapeTransformsAreDirty) { - for (int i = 0; i < _subShapeEntries.size(); ++i) { - _subShapeEntries[i].updateTransform(_translation, _rotation); - } - _subShapeTransformsAreDirty = false; - } -} - -void ListShape::addShape(Shape* shape, const glm::vec3& localPosition, const glm::quat& localRotation) { - if (shape) { - ListShapeEntry entry; - entry._shape = shape; - entry._localPosition = localPosition; - entry._localRotation = localRotation; - _subShapeEntries.push_back(entry); - } -} - -void ListShape::setShapes(QVector& shapes) { - clear(); - _subShapeEntries.swap(shapes); - // TODO: audit our new list of entries and delete any that have null pointers - computeBoundingRadius(); -} - -void ListShape::clear() { - // the ListShape owns its subShapes, so they need to be deleted - for (int i = 0; i < _subShapeEntries.size(); ++i) { - delete _subShapeEntries[i]._shape; - } - _subShapeEntries.clear(); - setBoundingRadius(0.0f); -} - -void ListShape::computeBoundingRadius() { - float maxRadius = 0.0f; - for (int i = 0; i < _subShapeEntries.size(); ++i) { - ListShapeEntry& entry = _subShapeEntries[i]; - float radius = glm::length(entry._localPosition) + entry._shape->getBoundingRadius(); - if (radius > maxRadius) { - maxRadius = radius; - } - } - setBoundingRadius(maxRadius); -} - diff --git a/libraries/shared/src/ListShape.h b/libraries/shared/src/ListShape.h deleted file mode 100644 index 6352ef3f07..0000000000 --- a/libraries/shared/src/ListShape.h +++ /dev/null @@ -1,72 +0,0 @@ -// -// ListShape.h -// libraries/shared/src -// -// Created by Andrew Meadows on 02/20/2014. -// Copyright 2014 High Fidelity, Inc. -// -// ListShape: A collection of shapes, each with a local transform. -// -// 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_ListShape_h -#define hifi_ListShape_h - -#include - -#include -#include -#include - -#include "Shape.h" - - -class ListShapeEntry { -public: - void updateTransform(const glm::vec3& position, const glm::quat& rotation); - - Shape* _shape; - glm::vec3 _localPosition; - glm::quat _localRotation; -}; - -class ListShape : public Shape { -public: - - ListShape() : Shape(LIST_SHAPE), _subShapeEntries(), _subShapeTransformsAreDirty(false) {} - - ListShape(const glm::vec3& position, const glm::quat& rotation) : - Shape(LIST_SHAPE, position, rotation), _subShapeEntries(), _subShapeTransformsAreDirty(false) {} - - ~ListShape(); - - void setTranslation(const glm::vec3& position); - void setRotation(const glm::quat& rotation); - - const Shape* getSubShape(int index) const; - - void updateSubTransforms(); - - int size() const { return _subShapeEntries.size(); } - - void addShape(Shape* shape, const glm::vec3& localPosition, const glm::quat& localRotation); - - void setShapes(QVector& shapes); - - // TODO: either implement this or remove ListShape altogether - bool findRayIntersection(RayIntersectionInfo& intersection) const { return false; } - -protected: - void clear(); - void computeBoundingRadius(); - - QVector _subShapeEntries; - bool _subShapeTransformsAreDirty; - -private: - ListShape(const ListShape& otherList); // don't implement this -}; - -#endif // hifi_ListShape_h diff --git a/libraries/shared/src/PlaneShape.cpp b/libraries/shared/src/PlaneShape.cpp deleted file mode 100644 index cad04afa42..0000000000 --- a/libraries/shared/src/PlaneShape.cpp +++ /dev/null @@ -1,78 +0,0 @@ -// -// PlaneShape.cpp -// libraries/shared/src -// -// Created by Andrzej Kapolka on 4/10/2014. -// 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 "GLMHelpers.h" -#include "NumericalConstants.h" -#include "PlaneShape.h" - -const glm::vec3 UNROTATED_NORMAL(0.0f, 1.0f, 0.0f); - -PlaneShape::PlaneShape(const glm::vec4& coefficients) : - Shape(PLANE_SHAPE) { - - glm::vec3 normal = glm::vec3(coefficients); - _translation = -normal * coefficients.w; - - float angle = acosf(glm::dot(normal, UNROTATED_NORMAL)); - if (angle > EPSILON) { - if (angle > PI - EPSILON) { - _rotation = glm::angleAxis(PI, glm::vec3(1.0f, 0.0f, 0.0f)); - } else { - _rotation = glm::angleAxis(angle, glm::normalize(glm::cross(UNROTATED_NORMAL, normal))); - } - } -} - -glm::vec3 PlaneShape::getNormal() const { - return _rotation * UNROTATED_NORMAL; -} - -void PlaneShape::setNormal(const glm::vec3& direction) { - glm::vec3 oldTranslation = _translation; - _rotation = rotationBetween(UNROTATED_NORMAL, direction); - glm::vec3 normal = getNormal(); - _translation = glm::dot(oldTranslation, normal) * normal; -} - -void PlaneShape::setPoint(const glm::vec3& point) { - glm::vec3 normal = getNormal(); - _translation = glm::dot(point, normal) * normal; -} - -glm::vec4 PlaneShape::getCoefficients() const { - glm::vec3 normal = _rotation * UNROTATED_NORMAL; - return glm::vec4(normal.x, normal.y, normal.z, -glm::dot(normal, _translation)); -} - -bool PlaneShape::findRayIntersection(RayIntersectionInfo& intersection) const { - glm::vec3 n = getNormal(); - float denominator = glm::dot(n, intersection._rayDirection); - if (fabsf(denominator) < EPSILON) { - // line is parallel to plane - if (glm::dot(_translation - intersection._rayStart, n) < EPSILON) { - // ray starts on the plane - intersection._hitDistance = 0.0f; - intersection._hitNormal = n; - intersection._hitShape = const_cast(this); - return true; - } - } else { - float d = glm::dot(_translation - intersection._rayStart, n) / denominator; - if (d > 0.0f && d < intersection._rayLength && d < intersection._hitDistance) { - // ray points toward plane - intersection._hitDistance = d; - intersection._hitNormal = n; - intersection._hitShape = const_cast(this); - return true; - } - } - return false; -} diff --git a/libraries/shared/src/PlaneShape.h b/libraries/shared/src/PlaneShape.h deleted file mode 100644 index 8d6de326af..0000000000 --- a/libraries/shared/src/PlaneShape.h +++ /dev/null @@ -1,30 +0,0 @@ -// -// PlaneShape.h -// libraries/shared/src -// -// Created by Andrzej Kapolka on 4/9/2014. -// 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_PlaneShape_h -#define hifi_PlaneShape_h - -#include "Shape.h" - -class PlaneShape : public Shape { -public: - PlaneShape(const glm::vec4& coefficients = glm::vec4(0.0f, 1.0f, 0.0f, 0.0f)); - - glm::vec3 getNormal() const; - glm::vec4 getCoefficients() const; - - void setNormal(const glm::vec3& normal); - void setPoint(const glm::vec3& point); - - bool findRayIntersection(RayIntersectionInfo& intersection) const; -}; - -#endif // hifi_PlaneShape_h diff --git a/libraries/shared/src/RayIntersectionInfo.h b/libraries/shared/src/RayIntersectionInfo.h deleted file mode 100644 index 06ec0aceaa..0000000000 --- a/libraries/shared/src/RayIntersectionInfo.h +++ /dev/null @@ -1,37 +0,0 @@ -// -// RayIntersectionInfo.h -// libraries/physcis/src -// -// Created by Andrew Meadows 2014.09.09 -// 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_RayIntersectionInfo_h -#define hifi_RayIntersectionInfo_h - -#include - -class Shape; - -class RayIntersectionInfo { -public: - RayIntersectionInfo() : _rayStart(0.0f), _rayDirection(1.0f, 0.0f, 0.0f), _rayLength(FLT_MAX), - _hitDistance(FLT_MAX), _hitNormal(1.0f, 0.0f, 0.0f), _hitShape(NULL) { } - - glm::vec3 getIntersectionPoint() const { return _rayStart + _hitDistance * _rayDirection; } - - // input - glm::vec3 _rayStart; - glm::vec3 _rayDirection; - float _rayLength; - - // output - float _hitDistance; - glm::vec3 _hitNormal; - Shape* _hitShape; -}; - -#endif // hifi_RayIntersectionInfo_h diff --git a/libraries/shared/src/Shape.h b/libraries/shared/src/Shape.h deleted file mode 100644 index 048b8d7257..0000000000 --- a/libraries/shared/src/Shape.h +++ /dev/null @@ -1,144 +0,0 @@ -// -// Shape.h -// libraries/shared/src -// -// Created by Andrew Meadows on 2014. -// 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_Shape_h -#define hifi_Shape_h - -#include -#include -#include -#include -#include - -#include "RayIntersectionInfo.h" - -class PhysicsEntity; - -const float MAX_SHAPE_MASS = 1.0e18f; // something less than sqrt(FLT_MAX) - -// DANGER: until ShapeCollider goes away the order of these values matter. Specifically, -// UNKNOWN_SHAPE must be equal to the number of shapes that ShapeCollider actually supports. -const quint8 SPHERE_SHAPE = 0; -const quint8 CAPSULE_SHAPE = 1; -const quint8 PLANE_SHAPE = 2; -const quint8 AACUBE_SHAPE = 3; -const quint8 LIST_SHAPE = 4; -const quint8 UNKNOWN_SHAPE = 5; -const quint8 INVALID_SHAPE = 5; - -// new shapes to be supported by Bullet -const quint8 BOX_SHAPE = 7; -const quint8 CYLINDER_SHAPE = 8; - -class Shape { -public: - - typedef quint8 Type; - - static quint32 getNextID() { static quint32 nextID = 0; return ++nextID; } - - Shape() : _type(UNKNOWN_SHAPE), _owningEntity(NULL), _boundingRadius(0.0f), - _translation(0.0f), _rotation(), _mass(MAX_SHAPE_MASS) { - _id = getNextID(); - } - virtual ~Shape() { } - - Type getType() const { return _type; } - quint32 getID() const { return _id; } - - void setEntity(PhysicsEntity* entity) { _owningEntity = entity; } - PhysicsEntity* getEntity() const { return _owningEntity; } - - float getBoundingRadius() const { return _boundingRadius; } - - virtual const glm::quat& getRotation() const { return _rotation; } - virtual void setRotation(const glm::quat& rotation) { _rotation = rotation; } - - virtual void setTranslation(const glm::vec3& translation) { _translation = translation; } - virtual const glm::vec3& getTranslation() const { return _translation; } - - virtual void setMass(float mass) { _mass = mass; } - virtual float getMass() const { return _mass; } - - virtual bool findRayIntersection(RayIntersectionInfo& intersection) const = 0; - - /// \param penetration of collision - /// \param contactPoint of collision - /// \return the effective mass for the collision - /// For most shapes has side effects: computes and caches the partial Lagrangian coefficients which will be - /// used in the next accumulateDelta() call. - virtual float computeEffectiveMass(const glm::vec3& penetration, const glm::vec3& contactPoint) { return _mass; } - - /// \param relativeMassFactor the final ingredient for partial Lagrangian coefficients from computeEffectiveMass() - /// \param penetration the delta movement - virtual void accumulateDelta(float relativeMassFactor, const glm::vec3& penetration) {} - - virtual void applyAccumulatedDelta() {} - - /// \return volume of shape in cubic meters - virtual float getVolume() const { return 1.0; } - - virtual QDebug& dumpToDebug(QDebug& debugConext) const; - -protected: - // these ctors are protected (used by derived classes only) - Shape(Type type) : _type(type), _owningEntity(NULL), - _boundingRadius(0.0f), _translation(0.0f), - _rotation(), _mass(MAX_SHAPE_MASS) { - _id = getNextID(); - } - - Shape(Type type, const glm::vec3& position) : - _type(type), _owningEntity(NULL), - _boundingRadius(0.0f), _translation(position), - _rotation(), _mass(MAX_SHAPE_MASS) { - _id = getNextID(); - } - - Shape(Type type, const glm::vec3& position, const glm::quat& rotation) : _type(type), _owningEntity(NULL), - _boundingRadius(0.0f), _translation(position), - _rotation(rotation), _mass(MAX_SHAPE_MASS) { - _id = getNextID(); - } - - void setBoundingRadius(float radius) { _boundingRadius = radius; } - - Type _type; - quint32 _id; - PhysicsEntity* _owningEntity; - float _boundingRadius; - glm::vec3 _translation; - glm::quat _rotation; - float _mass; -}; - -inline QDebug& Shape::dumpToDebug(QDebug& debugConext) const { - debugConext << "Shape[ (" - << "type: " << getType() - << "position: " - << getTranslation().x << ", " << getTranslation().y << ", " << getTranslation().z - << "radius: " - << getBoundingRadius() - << "]"; - - return debugConext; -} - -inline QDebug operator<<(QDebug debug, const Shape& shape) { - return shape.dumpToDebug(debug); -} - -inline QDebug operator<<(QDebug debug, const Shape* shape) { - return shape->dumpToDebug(debug); -} - - -#endif // hifi_Shape_h diff --git a/libraries/shared/src/ShapeCollider.cpp b/libraries/shared/src/ShapeCollider.cpp deleted file mode 100644 index fa86d8c0fb..0000000000 --- a/libraries/shared/src/ShapeCollider.cpp +++ /dev/null @@ -1,1211 +0,0 @@ -// -// ShapeCollider.cpp -// libraries/shared/src -// -// Created by Andrew Meadows on 02/20/2014. -// 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 - -#include "AACubeShape.h" -#include "CapsuleShape.h" -#include "GeometryUtil.h" -#include "ListShape.h" -#include "NumericalConstants.h" -#include "PlaneShape.h" -#include "ShapeCollider.h" -#include "SphereShape.h" -#include "StreamUtils.h" - - -// NOTE: -// -// * Large ListShape's are inefficient keep the lists short. -// * Collisions between lists of lists work in theory but are not recommended. - -const quint8 NUM_SHAPE_TYPES = UNKNOWN_SHAPE; -const quint8 NUM_DISPATCH_CELLS = NUM_SHAPE_TYPES * NUM_SHAPE_TYPES; - -Shape::Type getDispatchKey(Shape::Type typeA, Shape::Type typeB) { - return typeA + NUM_SHAPE_TYPES * typeB; -} - -// dummy dispatch for any non-implemented pairings -bool notImplemented(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { - return false; -} - -// NOTE: hardcode the number of dispatchTable entries (NUM_SHAPE_TYPES ^2) -bool (*dispatchTable[NUM_DISPATCH_CELLS])(const Shape*, const Shape*, CollisionList&); - -namespace ShapeCollider { - -// NOTE: the dispatch table must be initialized before the ShapeCollider is used. -void initDispatchTable() { - for (Shape::Type i = 0; i < NUM_DISPATCH_CELLS; ++i) { - dispatchTable[i] = ¬Implemented; - } - - dispatchTable[getDispatchKey(SPHERE_SHAPE, SPHERE_SHAPE)] = &sphereVsSphere; - dispatchTable[getDispatchKey(SPHERE_SHAPE, CAPSULE_SHAPE)] = &sphereVsCapsule; - dispatchTable[getDispatchKey(SPHERE_SHAPE, PLANE_SHAPE)] = &sphereVsPlane; - dispatchTable[getDispatchKey(SPHERE_SHAPE, AACUBE_SHAPE)] = &sphereVsAACube; - dispatchTable[getDispatchKey(SPHERE_SHAPE, LIST_SHAPE)] = &shapeVsList; - - dispatchTable[getDispatchKey(CAPSULE_SHAPE, SPHERE_SHAPE)] = &capsuleVsSphere; - dispatchTable[getDispatchKey(CAPSULE_SHAPE, CAPSULE_SHAPE)] = &capsuleVsCapsule; - dispatchTable[getDispatchKey(CAPSULE_SHAPE, PLANE_SHAPE)] = &capsuleVsPlane; - dispatchTable[getDispatchKey(CAPSULE_SHAPE, AACUBE_SHAPE)] = &capsuleVsAACube; - dispatchTable[getDispatchKey(CAPSULE_SHAPE, LIST_SHAPE)] = &shapeVsList; - - dispatchTable[getDispatchKey(PLANE_SHAPE, SPHERE_SHAPE)] = &planeVsSphere; - dispatchTable[getDispatchKey(PLANE_SHAPE, CAPSULE_SHAPE)] = &planeVsCapsule; - dispatchTable[getDispatchKey(PLANE_SHAPE, PLANE_SHAPE)] = &planeVsPlane; - dispatchTable[getDispatchKey(PLANE_SHAPE, AACUBE_SHAPE)] = ¬Implemented; - dispatchTable[getDispatchKey(PLANE_SHAPE, LIST_SHAPE)] = &shapeVsList; - - dispatchTable[getDispatchKey(AACUBE_SHAPE, SPHERE_SHAPE)] = &aaCubeVsSphere; - dispatchTable[getDispatchKey(AACUBE_SHAPE, CAPSULE_SHAPE)] = &aaCubeVsCapsule; - dispatchTable[getDispatchKey(AACUBE_SHAPE, PLANE_SHAPE)] = ¬Implemented; - dispatchTable[getDispatchKey(AACUBE_SHAPE, AACUBE_SHAPE)] = &aaCubeVsAACube; - dispatchTable[getDispatchKey(AACUBE_SHAPE, LIST_SHAPE)] = &shapeVsList; - - dispatchTable[getDispatchKey(LIST_SHAPE, SPHERE_SHAPE)] = &listVsShape; - dispatchTable[getDispatchKey(LIST_SHAPE, CAPSULE_SHAPE)] = &listVsShape; - dispatchTable[getDispatchKey(LIST_SHAPE, PLANE_SHAPE)] = &listVsShape; - dispatchTable[getDispatchKey(LIST_SHAPE, AACUBE_SHAPE)] = &listVsShape; - dispatchTable[getDispatchKey(LIST_SHAPE, LIST_SHAPE)] = &listVsList; - - // all of the UNKNOWN_SHAPE pairings are notImplemented -} - -bool collideShapes(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { - return (*dispatchTable[shapeA->getType() + NUM_SHAPE_TYPES * shapeB->getType()])(shapeA, shapeB, collisions); -} - -static CollisionList tempCollisions(32); - -bool collideShapeWithShapes(const Shape* shapeA, const QVector& shapes, int startIndex, CollisionList& collisions) { - bool collided = false; - if (shapeA) { - int numShapes = shapes.size(); - for (int i = startIndex; i < numShapes; ++i) { - const Shape* shapeB = shapes.at(i); - if (!shapeB) { - continue; - } - if (collideShapes(shapeA, shapeB, collisions)) { - collided = true; - if (collisions.isFull()) { - break; - } - } - } - } - return collided; -} - -bool collideShapesWithShapes(const QVector& shapesA, const QVector& shapesB, CollisionList& collisions) { - bool collided = false; - int numShapesA = shapesA.size(); - for (int i = 0; i < numShapesA; ++i) { - Shape* shapeA = shapesA.at(i); - if (!shapeA) { - continue; - } - if (collideShapeWithShapes(shapeA, shapesB, 0, collisions)) { - collided = true; - if (collisions.isFull()) { - break; - } - } - } - return collided; -} - -bool collideShapeWithAACubeLegacy(const Shape* shapeA, const glm::vec3& cubeCenter, float cubeSide, CollisionList& collisions) { - Shape::Type typeA = shapeA->getType(); - if (typeA == SPHERE_SHAPE) { - return sphereVsAACubeLegacy(static_cast(shapeA), cubeCenter, cubeSide, collisions); - } else if (typeA == CAPSULE_SHAPE) { - return capsuleVsAACubeLegacy(static_cast(shapeA), cubeCenter, cubeSide, collisions); - } else if (typeA == LIST_SHAPE) { - const ListShape* listA = static_cast(shapeA); - bool touching = false; - for (int i = 0; i < listA->size() && !collisions.isFull(); ++i) { - const Shape* subShape = listA->getSubShape(i); - int subType = subShape->getType(); - if (subType == SPHERE_SHAPE) { - touching = sphereVsAACubeLegacy(static_cast(subShape), cubeCenter, cubeSide, collisions) || touching; - } else if (subType == CAPSULE_SHAPE) { - touching = capsuleVsAACubeLegacy(static_cast(subShape), cubeCenter, cubeSide, collisions) || touching; - } - } - return touching; - } - return false; -} - -bool sphereVsSphere(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { - const SphereShape* sphereA = static_cast(shapeA); - const SphereShape* sphereB = static_cast(shapeB); - glm::vec3 BA = sphereB->getTranslation() - sphereA->getTranslation(); - float distanceSquared = glm::dot(BA, BA); - float totalRadius = sphereA->getRadius() + sphereB->getRadius(); - if (distanceSquared < totalRadius * totalRadius) { - // normalize BA - float distance = sqrtf(distanceSquared); - if (distance < EPSILON) { - // the spheres are on top of each other, so we pick an arbitrary penetration direction - BA = glm::vec3(0.0f, 1.0f, 0.0f); - distance = totalRadius; - } else { - BA /= distance; - } - // penetration points from A into B - CollisionInfo* collision = collisions.getNewCollision(); - if (collision) { - collision->_penetration = BA * (totalRadius - distance); - // contactPoint is on surface of A - collision->_contactPoint = sphereA->getTranslation() + sphereA->getRadius() * BA; - collision->_shapeA = shapeA; - collision->_shapeB = shapeB; - return true; - } - } - return false; -} - -bool sphereVsCapsule(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { - const SphereShape* sphereA = static_cast(shapeA); - const CapsuleShape* capsuleB = static_cast(shapeB); - // find sphereA's closest approach to axis of capsuleB - glm::vec3 BA = capsuleB->getTranslation() - sphereA->getTranslation(); - glm::vec3 capsuleAxis; - capsuleB->computeNormalizedAxis(capsuleAxis); - float axialDistance = - glm::dot(BA, capsuleAxis); - float absAxialDistance = fabsf(axialDistance); - float totalRadius = sphereA->getRadius() + capsuleB->getRadius(); - if (absAxialDistance < totalRadius + capsuleB->getHalfHeight()) { - glm::vec3 radialAxis = BA + axialDistance * capsuleAxis; // points from A to axis of B - float radialDistance2 = glm::length2(radialAxis); - float totalRadius2 = totalRadius * totalRadius; - if (radialDistance2 > totalRadius2) { - // sphere is too far from capsule axis - return false; - } - if (absAxialDistance > capsuleB->getHalfHeight()) { - // sphere hits capsule on a cap --> recompute radialAxis to point from spherA to cap center - float sign = (axialDistance > 0.0f) ? 1.0f : -1.0f; - radialAxis = BA + (sign * capsuleB->getHalfHeight()) * capsuleAxis; - radialDistance2 = glm::length2(radialAxis); - if (radialDistance2 > totalRadius2) { - return false; - } - } - if (radialDistance2 > EPSILON * EPSILON) { - CollisionInfo* collision = collisions.getNewCollision(); - if (!collision) { - // collisions list is full - return false; - } - // normalize the radialAxis - float radialDistance = sqrtf(radialDistance2); - radialAxis /= radialDistance; - // penetration points from A into B - collision->_penetration = (totalRadius - radialDistance) * radialAxis; // points from A into B - // contactPoint is on surface of sphereA - collision->_contactPoint = sphereA->getTranslation() + sphereA->getRadius() * radialAxis; - collision->_shapeA = shapeA; - collision->_shapeB = shapeB; - } else { - // A is on B's axis, so the penetration is undefined... - if (absAxialDistance > capsuleB->getHalfHeight()) { - // ...for the cylinder case (for now we pretend the collision doesn't exist) - return false; - } - CollisionInfo* collision = collisions.getNewCollision(); - if (!collision) { - // collisions list is full - return false; - } - // ... but still defined for the cap case - if (axialDistance < 0.0f) { - // we're hitting the start cap, so we negate the capsuleAxis - capsuleAxis *= -1; - } - // penetration points from A into B - float sign = (axialDistance > 0.0f) ? -1.0f : 1.0f; - collision->_penetration = (sign * (totalRadius + capsuleB->getHalfHeight() - absAxialDistance)) * capsuleAxis; - // contactPoint is on surface of sphereA - collision->_contactPoint = sphereA->getTranslation() + (sign * sphereA->getRadius()) * capsuleAxis; - collision->_shapeA = shapeA; - collision->_shapeB = shapeB; - } - return true; - } - return false; -} - -bool sphereVsPlane(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { - const SphereShape* sphereA = static_cast(shapeA); - const PlaneShape* planeB = static_cast(shapeB); - glm::vec3 penetration; - if (findSpherePlanePenetration(sphereA->getTranslation(), sphereA->getRadius(), planeB->getCoefficients(), penetration)) { - CollisionInfo* collision = collisions.getNewCollision(); - if (!collision) { - return false; // collision list is full - } - collision->_penetration = penetration; - collision->_contactPoint = sphereA->getTranslation() + sphereA->getRadius() * glm::normalize(penetration); - collision->_shapeA = shapeA; - collision->_shapeB = shapeB; - return true; - } - return false; -} - -bool capsuleVsSphere(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { - return sphereVsCapsule(shapeB, shapeA, collisions); -} - -/// \param lineP point on line -/// \param lineDir normalized direction of line -/// \param cylinderP point on cylinder axis -/// \param cylinderDir normalized direction of cylinder axis -/// \param cylinderRadius radius of cylinder -/// \param hitLow[out] distance from point on line to first intersection with cylinder -/// \param hitHigh[out] distance from point on line to second intersection with cylinder -/// \return true if line hits cylinder -bool lineCylinder(const glm::vec3& lineP, const glm::vec3& lineDir, - const glm::vec3& cylinderP, const glm::vec3& cylinderDir, float cylinderRadius, - float& hitLow, float& hitHigh) { - - // first handle parallel case - float uDotV = glm::dot(lineDir, cylinderDir); - if (fabsf(1.0f - fabsf(uDotV)) < EPSILON) { - // line and cylinder are parallel - if (glm::distance2(lineP, cylinderP) <= cylinderRadius * cylinderRadius) { - // line is inside cylinder, which we consider a hit - hitLow = 0.0f; - hitHigh = 0.0f; - return true; - } - return false; - } - - // Given a line with point 'p' and normalized direction 'u' and - // a cylinder with axial point 's', radius 'r', and normalized direction 'v' - // the intersection of the two is on the line at distance 't' from 'p'. - // - // Determining the values of t reduces to solving a quadratic equation: At^2 + Bt + C = 0 - // - // where: - // - // P = p-s - // w = u-(u.v)v - // Q = P-(P.v)v - // - // A = w^2 - // B = 2(w.Q) - // C = Q^2 - r^2 - - glm::vec3 P = lineP - cylinderP; - glm::vec3 w = lineDir - uDotV * cylinderDir; - glm::vec3 Q = P - glm::dot(P, cylinderDir) * cylinderDir; - - // we save a few multiplies by storing 2*A rather than just A - float A2 = 2.0f * glm::dot(w, w); - float B = 2.0f * glm::dot(w, Q); - - // since C is only ever used once (in the determinant) we compute it inline - float determinant = B * B - 2.0f * A2 * (glm::dot(Q, Q) - cylinderRadius * cylinderRadius); - if (determinant < 0.0f) { - return false; - } - hitLow = (-B - sqrtf(determinant)) / A2; - hitHigh = -(hitLow + 2.0f * B / A2); - - if (hitLow > hitHigh) { - // re-arrange so hitLow is always the smaller value - float temp = hitHigh; - hitHigh = hitLow; - hitLow = temp; - } - return true; -} - -bool capsuleVsCapsule(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { - const CapsuleShape* capsuleA = static_cast(shapeA); - const CapsuleShape* capsuleB = static_cast(shapeB); - glm::vec3 axisA; - capsuleA->computeNormalizedAxis(axisA); - glm::vec3 axisB; - capsuleB->computeNormalizedAxis(axisB); - glm::vec3 centerA = capsuleA->getTranslation(); - glm::vec3 centerB = capsuleB->getTranslation(); - - // NOTE: The formula for closest approach between two lines is: - // d = [(B - A) . (a - (a.b)b)] / (1 - (a.b)^2) - - float aDotB = glm::dot(axisA, axisB); - float denominator = 1.0f - aDotB * aDotB; - float totalRadius = capsuleA->getRadius() + capsuleB->getRadius(); - if (denominator > EPSILON) { - // perform line-cylinder intesection test between axis of cylinderA and cylinderB with exanded radius - float hitLow = 0.0f; - float hitHigh = 0.0f; - if (!lineCylinder(centerA, axisA, centerB, axisB, totalRadius, hitLow, hitHigh)) { - return false; - } - - float halfHeightA = capsuleA->getHalfHeight(); - if (hitLow > halfHeightA || hitHigh < -halfHeightA) { - // the intersections are off the ends of capsuleA - return false; - } - - // compute nearest approach on axisA of axisB - float distanceA = glm::dot((centerB - centerA), (axisA - (aDotB) * axisB)) / denominator; - // clamp to intersection zone - if (distanceA > hitLow) { - if (distanceA > hitHigh) { - distanceA = hitHigh; - } - } else { - distanceA = hitLow; - } - // clamp to capsule segment - distanceA = glm::clamp(distanceA, -halfHeightA, halfHeightA); - - // find the closest point on capsuleB to sphere on capsuleA - float distanceB = glm::dot(centerA + distanceA * axisA - centerB, axisB); - float halfHeightB = capsuleB->getHalfHeight(); - if (fabsf(distanceB) > halfHeightB) { - // we must clamp distanceB... - distanceB = glm::clamp(distanceB, -halfHeightB, halfHeightB); - // ...and therefore must recompute distanceA - distanceA = glm::clamp(glm::dot(centerB + distanceB * axisB - centerA, axisA), -halfHeightA, halfHeightA); - } - - // collide like two spheres (do most of the math relative to B) - glm::vec3 BA = (centerB + distanceB * axisB) - (centerA + distanceA * axisA); - float distanceSquared = glm::dot(BA, BA); - if (distanceSquared < totalRadius * totalRadius) { - CollisionInfo* collision = collisions.getNewCollision(); - if (!collision) { - // collisions list is full - return false; - } - // normalize BA - float distance = sqrtf(distanceSquared); - if (distance < EPSILON) { - // the contact spheres are on top of each other, so we need to pick a penetration direction... - // try vector between the capsule centers... - BA = centerB - centerA; - distanceSquared = glm::length2(BA); - if (distanceSquared > EPSILON * EPSILON) { - distance = sqrtf(distanceSquared); - BA /= distance; - } else - { - // the capsule centers are on top of each other! - // give up on a valid penetration direction and just use the yAxis - BA = glm::vec3(0.0f, 1.0f, 0.0f); - distance = glm::max(capsuleB->getRadius(), capsuleA->getRadius()); - } - } else { - BA /= distance; - } - // penetration points from A into B - collision->_penetration = BA * (totalRadius - distance); - // contactPoint is on surface of A - collision->_contactPoint = centerA + distanceA * axisA + capsuleA->getRadius() * BA; - collision->_shapeA = shapeA; - collision->_shapeB = shapeB; - return true; - } - } else { - // capsules are approximiately parallel but might still collide - glm::vec3 BA = centerB - centerA; - float axialDistance = glm::dot(BA, axisB); - if (fabsf(axialDistance) > totalRadius + capsuleA->getHalfHeight() + capsuleB->getHalfHeight()) { - return false; - } - BA = BA - axialDistance * axisB; // BA now points from centerA to axisB (perp to axis) - float distanceSquared = glm::length2(BA); - if (distanceSquared < totalRadius * totalRadius) { - CollisionInfo* collision = collisions.getNewCollision(); - if (!collision) { - // collisions list is full - return false; - } - // We have all the info we need to compute the penetration vector... - // normalize BA - float distance = sqrtf(distanceSquared); - if (distance < EPSILON) { - // the spheres are on top of each other, so we pick an arbitrary penetration direction - BA = glm::vec3(0.0f, 1.0f, 0.0f); - } else { - BA /= distance; - } - // penetration points from A into B - collision->_penetration = BA * (totalRadius - distance); - - // However we need some more world-frame info to compute the contactPoint, - // which is on the surface of capsuleA... - // - // Find the overlapping secion of the capsules --> they collide as if there were - // two spheres at the midpoint of this overlapping section. - // So we project all endpoints to axisB, find the interior pair, - // and put A's proxy sphere on axisA at the midpoint of this section. - - // sort the projections as much as possible during calculation - float points[5]; - points[0] = -capsuleB->getHalfHeight(); - points[1] = axialDistance - capsuleA->getHalfHeight(); - points[2] = axialDistance + capsuleA->getHalfHeight(); - points[3] = capsuleB->getHalfHeight(); - - // Since there are only three comparisons to do we unroll the sort algorithm... - // and use a fifth slot as temp during swap. - if (points[1] > points[2]) { - points[4] = points[1]; - points[1] = points[2]; - points[2] = points[4]; - } - if (points[2] > points[3]) { - points[4] = points[2]; - points[2] = points[3]; - points[3] = points[4]; - } - if (points[0] > points[1]) { - points[4] = points[0]; - points[0] = points[1]; - points[1] = points[4]; - } - - // average the internal pair, and then do the math from centerB - collision->_contactPoint = centerB + (0.5f * (points[1] + points[2])) * axisB - + (capsuleA->getRadius() - distance) * BA; - collision->_shapeA = shapeA; - collision->_shapeB = shapeB; - return true; - } - } - return false; -} - -bool capsuleVsPlane(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { - const CapsuleShape* capsuleA = static_cast(shapeA); - const PlaneShape* planeB = static_cast(shapeB); - glm::vec3 start, end, penetration; - capsuleA->getStartPoint(start); - capsuleA->getEndPoint(end); - glm::vec4 plane = planeB->getCoefficients(); - if (findCapsulePlanePenetration(start, end, capsuleA->getRadius(), plane, penetration)) { - CollisionInfo* collision = collisions.getNewCollision(); - if (!collision) { - return false; // collision list is full - } - collision->_penetration = penetration; - glm::vec3 deepestEnd = (glm::dot(start, glm::vec3(plane)) < glm::dot(end, glm::vec3(plane))) ? start : end; - collision->_contactPoint = deepestEnd + capsuleA->getRadius() * glm::normalize(penetration); - collision->_shapeA = shapeA; - collision->_shapeB = shapeB; - return true; - } - return false; -} - -bool planeVsSphere(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { - return sphereVsPlane(shapeB, shapeA, collisions); -} - -bool planeVsCapsule(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { - return capsuleVsPlane(shapeB, shapeA, collisions); -} - -bool planeVsPlane(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { - // technically, planes always collide unless they're parallel and not coincident; however, that's - // not going to give us any useful information - return false; -} - -// helper function -bool sphereVsAACubeLegacy(const glm::vec3& sphereCenter, float sphereRadius, const glm::vec3& cubeCenter, - float cubeSide, CollisionList& collisions) { - // sphere is A - // cube is B - // BA = B - A = from center of A to center of B - float halfCubeSide = 0.5f * cubeSide; - glm::vec3 BA = cubeCenter - sphereCenter; - float distance = glm::length(BA); - if (distance > EPSILON) { - float maxBA = glm::max(glm::max(glm::abs(BA.x), glm::abs(BA.y)), glm::abs(BA.z)); - if (maxBA > halfCubeSide + sphereRadius) { - // sphere misses cube entirely - return false; - } - CollisionInfo* collision = collisions.getNewCollision(); - if (!collision) { - return false; - } - if (maxBA > halfCubeSide) { - // sphere hits cube but its center is outside cube - - // compute contact anti-pole on cube (in cube frame) - glm::vec3 cubeContact = glm::abs(BA); - if (cubeContact.x > halfCubeSide) { - cubeContact.x = halfCubeSide; - } - if (cubeContact.y > halfCubeSide) { - cubeContact.y = halfCubeSide; - } - if (cubeContact.z > halfCubeSide) { - cubeContact.z = halfCubeSide; - } - glm::vec3 signs = glm::sign(BA); - cubeContact.x *= signs.x; - cubeContact.y *= signs.y; - cubeContact.z *= signs.z; - - // compute penetration direction - glm::vec3 direction = BA - cubeContact; - float lengthDirection = glm::length(direction); - if (lengthDirection < EPSILON) { - // sphereCenter is touching cube surface, so we can't use the difference between those two - // points to compute the penetration direction. Instead we use the unitary components of - // cubeContact. - direction = cubeContact / halfCubeSide; - glm::modf(BA, direction); - lengthDirection = glm::length(direction); - } else if (lengthDirection > sphereRadius) { - collisions.deleteLastCollision(); - return false; - } - direction /= lengthDirection; - - // compute collision details - collision->_contactPoint = sphereCenter + sphereRadius * direction; - collision->_penetration = sphereRadius * direction - (BA - cubeContact); - } else { - // sphere center is inside cube - // --> push out nearest face - glm::vec3 direction; - BA /= maxBA; - glm::modf(BA, direction); - float lengthDirection = glm::length(direction); - direction /= lengthDirection; - - // compute collision details - collision->_floatData = cubeSide; - collision->_vecData = cubeCenter; - collision->_penetration = (halfCubeSide * lengthDirection + sphereRadius - maxBA * glm::dot(BA, direction)) * direction; - collision->_contactPoint = sphereCenter + sphereRadius * direction; - } - collision->_floatData = cubeSide; - collision->_vecData = cubeCenter; - collision->_shapeA = NULL; - collision->_shapeB = NULL; - return true; - } else if (sphereRadius + halfCubeSide > distance) { - // NOTE: for cocentric approximation we collide sphere and cube as two spheres which means - // this algorithm will probably be wrong when both sphere and cube are very small (both ~EPSILON) - CollisionInfo* collision = collisions.getNewCollision(); - if (collision) { - // the penetration and contactPoint are undefined, so we pick a penetration direction (-yAxis) - collision->_penetration = (sphereRadius + halfCubeSide) * glm::vec3(0.0f, -1.0f, 0.0f); - // contactPoint is on surface of A - collision->_contactPoint = sphereCenter + collision->_penetration; - - collision->_floatData = cubeSide; - collision->_vecData = cubeCenter; - collision->_shapeA = NULL; - collision->_shapeB = NULL; - return true; - } - } - return false; -} - -// helper function -CollisionInfo* sphereVsAACubeHelper(const glm::vec3& sphereCenter, float sphereRadius, const glm::vec3& cubeCenter, - float cubeSide, CollisionList& collisions) { - // sphere is A - // cube is B - // BA = B - A = from center of A to center of B - float halfCubeSide = 0.5f * cubeSide; - glm::vec3 BA = cubeCenter - sphereCenter; - float distance = glm::length(BA); - if (distance > EPSILON) { - float maxBA = glm::max(glm::max(glm::abs(BA.x), glm::abs(BA.y)), glm::abs(BA.z)); - if (maxBA > halfCubeSide + sphereRadius) { - // sphere misses cube entirely - return NULL; - } - CollisionInfo* collision = collisions.getNewCollision(); - if (!collision) { - return NULL; - } - if (maxBA > halfCubeSide) { - // sphere hits cube but its center is outside cube - - // compute contact anti-pole on cube (in cube frame) - glm::vec3 cubeContact = glm::abs(BA); - if (cubeContact.x > halfCubeSide) { - cubeContact.x = halfCubeSide; - } - if (cubeContact.y > halfCubeSide) { - cubeContact.y = halfCubeSide; - } - if (cubeContact.z > halfCubeSide) { - cubeContact.z = halfCubeSide; - } - glm::vec3 signs = glm::sign(BA); - cubeContact.x *= signs.x; - cubeContact.y *= signs.y; - cubeContact.z *= signs.z; - - // compute penetration direction - glm::vec3 direction = BA - cubeContact; - float lengthDirection = glm::length(direction); - if (lengthDirection < EPSILON) { - // sphereCenter is touching cube surface, so we can't use the difference between those two - // points to compute the penetration direction. Instead we use the unitary components of - // cubeContact. - glm::modf(cubeContact / halfCubeSide, direction); - lengthDirection = glm::length(direction); - } else if (lengthDirection > sphereRadius) { - collisions.deleteLastCollision(); - return NULL; - } - direction /= lengthDirection; - - // compute collision details - collision->_contactPoint = sphereCenter + sphereRadius * direction; - collision->_penetration = sphereRadius * direction - (BA - cubeContact); - } else { - // sphere center is inside cube - // --> push out nearest face - glm::vec3 direction; - BA /= maxBA; - glm::modf(BA, direction); - float lengthDirection = glm::length(direction); - direction /= lengthDirection; - - // compute collision details - collision->_floatData = cubeSide; - collision->_vecData = cubeCenter; - collision->_penetration = (halfCubeSide * lengthDirection + sphereRadius - maxBA * glm::dot(BA, direction)) * direction; - collision->_contactPoint = sphereCenter + sphereRadius * direction; - } - collision->_shapeA = NULL; - collision->_shapeB = NULL; - return collision; - } else if (sphereRadius + halfCubeSide > distance) { - // NOTE: for cocentric approximation we collide sphere and cube as two spheres which means - // this algorithm will probably be wrong when both sphere and cube are very small (both ~EPSILON) - CollisionInfo* collision = collisions.getNewCollision(); - if (collision) { - // the penetration and contactPoint are undefined, so we pick a penetration direction (-yAxis) - collision->_penetration = (sphereRadius + halfCubeSide) * glm::vec3(0.0f, -1.0f, 0.0f); - // contactPoint is on surface of A - collision->_contactPoint = sphereCenter + collision->_penetration; - collision->_shapeA = NULL; - collision->_shapeB = NULL; - return collision; - } - } - return NULL; -} - -bool sphereVsAACube(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { - // BA = B - A = from center of A to center of B - const SphereShape* sphereA = static_cast(shapeA); - const AACubeShape* cubeB = static_cast(shapeB); - - CollisionInfo* collision = sphereVsAACubeHelper( sphereA->getTranslation(), sphereA->getRadius(), - cubeB->getTranslation(), cubeB->getScale(), collisions); - if (collision) { - collision->_shapeA = shapeA; - collision->_shapeB = shapeB; - return true; - } - return false; -} - -glm::vec3 cubeNormals[3] = { glm::vec3(1.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f) }; - -// the wallIndices is a sequence of pairs that represent the OTHER directions for each cube face, -// hence the first pair is (1, 2) because the OTHER faces for xFace are (yFace, zFace) = (1, 2) -int wallIndices[] = { 1, 2, 0, 2, 0, 1 }; - -bool capsuleVsAACubeFace(const CapsuleShape* capsuleA, const AACubeShape* cubeB, int faceIndex, const glm::vec3& faceNormal, CollisionList& collisions) { - // we only fall in here when the capsuleAxis is nearly parallel to the face of a cube - glm::vec3 capsuleAxis; - capsuleA->computeNormalizedAxis(capsuleAxis); - glm::vec3 cubeCenter = cubeB->getTranslation(); - - // Revisualize the capsule as a line segment between two points. We'd like to collide the - // capsule as two spheres located at the endpoints or where the line segment hits the boundary - // of the face. - - // We raytrace forward into the four planes that neigbhor the face to find the boundary - // points of the line segment. - glm::vec3 capsuleStart; - capsuleA->getStartPoint(capsuleStart); - - // translate into cube-relative frame - capsuleStart -= cubeCenter; - float capsuleLength = 2.0f * capsuleA->getHalfHeight(); - float halfCubeSide = 0.5f * cubeB->getScale(); - float capsuleRadius = capsuleA->getRadius(); - - // preload distances with values that work for when the capsuleAxis runs parallel to neighbor face - float distances[] = {FLT_MAX, -FLT_MAX, FLT_MAX, -FLT_MAX, 0.0f}; - - // Loop over the directions that are NOT parallel to face (there are two of them). - // For each direction we'll raytrace against the positive and negative planes to find where - // the axis passes through. - for (int i = 0; i < 2; ++i) { - int wallIndex = wallIndices[2 * faceIndex + i]; - glm::vec3 wallNormal = cubeNormals[wallIndex]; - // each direction has two walls: positive and negative - float axisDotWall = glm::dot(capsuleAxis, wallNormal); - if (fabsf(axisDotWall) > EPSILON) { - // formula for distance to intersection between line (P,p) and plane (V,n) is: (V-P)*n / p*n - distances[2 * i] = (halfCubeSide - glm::dot(capsuleStart, wallNormal)) / axisDotWall; - distances[2 * i + 1] = -(halfCubeSide + glm::dot(capsuleStart, wallNormal)) / axisDotWall; - } - } - - // sort the distances from large to small - int j = 3; - while (j > 0) { - for (int i = 0; i <= j; ++i) { - if (distances[i] < distances[i+1]) { - // swap (using distances[4] as temp space) - distances[4] = distances[i]; - distances[i] = distances[i+1]; - distances[i+1] = distances[4]; - } - } - --j; - } - - // the capsule overlaps when the max of the mins is less than the min of the maxes - distances[0] = glm::min(capsuleLength, distances[1]); // maxDistance - distances[1] = glm::max(0.0f, distances[2]); // minDistance - bool hit = false; - if (distances[1] < distances[0]) { - // if we collide at all it will be at two points - for (int i = 0; i < 2; ++i) { - glm::vec3 sphereCenter = cubeCenter + capsuleStart + distances[i] * capsuleAxis; - // collide like a sphere at point0 with capsuleRadius - CollisionInfo* collision = sphereVsAACubeHelper(sphereCenter, capsuleRadius, - cubeCenter, 2.0f * halfCubeSide, collisions); - if (collision) { - // we hit! so store back pointers to the shapes - collision->_shapeA = capsuleA; - collision->_shapeB = cubeB; - hit = true; - } - } - return hit; - } else if (distances[1] < capsuleLength + capsuleRadius ) { - // we might collide at the end cap - glm::vec3 sphereCenter = cubeCenter + capsuleStart + capsuleLength * capsuleAxis; - // collide like a sphere at point0 with capsuleRadius - CollisionInfo* collision = sphereVsAACubeHelper(sphereCenter, capsuleRadius, - cubeCenter, 2.0f * halfCubeSide, collisions); - if (collision) { - // we hit! so store back pointers to the shapes - collision->_shapeA = capsuleA; - collision->_shapeB = cubeB; - hit = true; - } - } else if (distances[0] > -capsuleLength) { - // we might collide at the start cap - glm::vec3 sphereCenter = cubeCenter + capsuleStart; - // collide like a sphere at point0 with capsuleRadius - CollisionInfo* collision = sphereVsAACubeHelper(sphereCenter, capsuleRadius, - cubeCenter, 2.0f * halfCubeSide, collisions); - if (collision) { - // we hit! so store back pointers to the shapes - collision->_shapeA = capsuleA; - collision->_shapeB = cubeB; - hit = true; - } - } - return hit; -} - -bool capsuleVsAACube(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { - const CapsuleShape* capsuleA = static_cast(shapeA); - const AACubeShape* cubeB = static_cast(shapeB); - - // find nearest approach of capsule's line segment to cube's center - glm::vec3 capsuleAxis; - capsuleA->computeNormalizedAxis(capsuleAxis); - float halfHeight = capsuleA->getHalfHeight(); - glm::vec3 cubeCenter = cubeB->getTranslation(); - glm::vec3 capsuleCenter = capsuleA->getTranslation(); - glm::vec3 BA = cubeCenter - capsuleCenter; - float axialOffset = glm::dot(capsuleAxis, BA); - if (fabsf(axialOffset) > halfHeight) { - axialOffset = (axialOffset < 0.0f) ? -halfHeight : halfHeight; - } - glm::vec3 nearestApproach = capsuleCenter + axialOffset * capsuleAxis; - - // transform nearestApproach into cube-relative frame - nearestApproach -= cubeCenter; - - // determine the face of nearest approach - glm::vec3 signs = glm::sign(nearestApproach); - int faceIndex = 0; - glm::vec3 faceNormal(signs.x, 0.0f, 0.0f); - float maxApproach = fabsf(nearestApproach.x); - if (maxApproach < fabsf(nearestApproach.y)) { - maxApproach = fabsf(nearestApproach.y); - faceIndex = 1; - faceNormal = glm::vec3(0.0f, signs.y, 0.0f); - } - if (maxApproach < fabsf(nearestApproach.z)) { - maxApproach = fabsf(nearestApproach.z); - faceIndex = 2; - faceNormal = glm::vec3(0.0f, 0.0f, signs.z); - } - - if (fabsf(glm::dot(faceNormal, capsuleAxis)) < EPSILON) { - if (glm::dot(nearestApproach, faceNormal) > cubeB->getScale() + capsuleA->getRadius()) { - return false; - } - // we expect this case to be rare but complicated enough that we split it out - // into its own helper function - return capsuleVsAACubeFace(capsuleA, cubeB, faceIndex, faceNormal, collisions); - } - - // Revisualize the capsule as a startPoint and an axis that points toward the cube face. - // We raytrace forward into the four planes that neigbhor the face to find the furthest - // point along the capsule's axis that might hit face. - glm::vec3 capsuleStart; - if (glm::dot(capsuleAxis, faceNormal) < 0.0f) { - capsuleA->getStartPoint(capsuleStart); - } else { - // NOTE: we want dot(capsuleAxis, faceNormal) to be negative which simplifies some - // logic below, so we pretend the end is the start thereby reversing its axis. - capsuleA->getEndPoint(capsuleStart); - capsuleAxis *= -1.0f; - } - // translate into cube-relative frame - capsuleStart -= cubeCenter; - float capsuleLength = 2.0f * halfHeight; - float halfCubeSide = 0.5f * cubeB->getScale(); - float capsuleRadius = capsuleA->getRadius(); - - // Loop over the directions that are NOT parallel to face (there are two of them). - // For each direction we'll raytrace along capsuleAxis to find where the axis passes - // through the furthest face and then we'll clamp to remain on the capsule's line segment - float shortestDistance = capsuleLength; - - for (int i = 0; i < 2; ++i) { - int wallIndex = wallIndices[2 * faceIndex + i]; - // each direction has two walls: positive and negative - for (float wallSign = -1.0f; wallSign < 2.0f; wallSign += 2.0f) { - glm::vec3 wallNormal = wallSign * cubeNormals[wallIndex]; - float axisDotWall = glm::dot(capsuleAxis, wallNormal); - if (axisDotWall > EPSILON) { - // formula for distance to intersection between line (P,p) and plane (V,n) is: (V-P)*n / p*n - float newDistance = (halfCubeSide - glm::dot(capsuleStart, wallNormal)) / axisDotWall; - if (newDistance < 0.0f) { - // The wall is behind the capsule, but there is still a possibility that it collides - // against the edge so we recast against the diagonal plane beteween the two faces. - // NOTE: it is impossible for the startPoint to be in front of the diagonal plane, - // therefore we know we'll get a valid distance. - glm::vec3 thirdNormal = glm::cross(faceNormal, wallNormal); - glm::vec3 diagonalNormal = glm::normalize(glm::cross(glm::cross(capsuleAxis, thirdNormal), thirdNormal)); - newDistance = glm::dot(halfCubeSide * (faceNormal + wallNormal) - capsuleStart, diagonalNormal) / - glm::dot(capsuleAxis, diagonalNormal); - } else if (newDistance < capsuleLength) { - // The wall truncates the capsule axis, but we must check the case where the capsule - // collides with an edge/corner rather than the face. The good news is that this gives us - // an opportunity to check for an early exit case. - float heightOfImpact = glm::dot(capsuleStart + newDistance * capsuleAxis, faceNormal); - if (heightOfImpact > halfCubeSide + SQUARE_ROOT_OF_2 * capsuleRadius) { - // it is impossible for the capsule to hit the face - return false; - } else { - // recast against the diagonal plane between the two faces - glm::vec3 thirdNormal = glm::cross(faceNormal, wallNormal); - glm::vec3 diagonalNormal = glm::normalize(glm::cross(glm::cross(capsuleAxis, thirdNormal), thirdNormal)); - newDistance = glm::dot(halfCubeSide * (faceNormal + wallNormal) - capsuleStart, diagonalNormal) / - glm::dot(capsuleAxis, diagonalNormal); - } - } - if (newDistance < shortestDistance) { - shortestDistance = newDistance; - } - // there can only be one hit per direction - break; - } - } - } - - // chose the point that produces the deepest penetration against face - // and translate back into real frame - glm::vec3 sphereCenter = cubeCenter + capsuleStart + shortestDistance * capsuleAxis; - - // collide like a sphere at point0 with capsuleRadius - CollisionInfo* collision = sphereVsAACubeHelper(sphereCenter, capsuleRadius, - cubeCenter, 2.0f * halfCubeSide, collisions); - if (collision) { - // we hit! so store back pointers to the shapes - collision->_shapeA = shapeA; - collision->_shapeB = shapeB; - return true; - } - return false; -} - -bool aaCubeVsSphere(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { - return sphereVsAACube(shapeB, shapeA, collisions); -} - -bool aaCubeVsCapsule(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { - return capsuleVsAACube(shapeB, shapeA, collisions); -} - -// helper function -CollisionInfo* aaCubeVsAACubeHelper(const glm::vec3& cubeCenterA, float cubeSideA, const glm::vec3& cubeCenterB, - float cubeSideB, CollisionList& collisions) { - // cube is A - // cube is B - // BA = B - A = from center of A to center of B - float halfCubeSideA = 0.5f * cubeSideA; - float halfCubeSideB = 0.5f * cubeSideB; - glm::vec3 BA = cubeCenterB - cubeCenterA; - - float distance = glm::length(BA); - - if (distance > EPSILON) { - float maxBA = glm::max(glm::max(glm::abs(BA.x), glm::abs(BA.y)), glm::abs(BA.z)); - if (maxBA > halfCubeSideB + halfCubeSideA) { - // cube misses cube entirely - return NULL; - } - CollisionInfo* collision = collisions.getNewCollision(); - if (!collision) { - return NULL; // no more room for collisions - } - if (maxBA > halfCubeSideB) { - // cube hits cube but its center is outside cube - // compute contact anti-pole on cube (in cube frame) - glm::vec3 cubeContact = glm::abs(BA); - if (cubeContact.x > halfCubeSideB) { - cubeContact.x = halfCubeSideB; - } - if (cubeContact.y > halfCubeSideB) { - cubeContact.y = halfCubeSideB; - } - if (cubeContact.z > halfCubeSideB) { - cubeContact.z = halfCubeSideB; - } - glm::vec3 signs = glm::sign(BA); - cubeContact.x *= signs.x; - cubeContact.y *= signs.y; - cubeContact.z *= signs.z; - - // compute penetration direction - glm::vec3 direction = BA - cubeContact; - - float lengthDirection = glm::length(direction); - - if (lengthDirection < EPSILON) { - // cubeCenterA is touching cube B surface, so we can't use the difference between those two - // points to compute the penetration direction. Instead we use the unitary components of - // cubeContact. - glm::modf(cubeContact / halfCubeSideB, direction); - lengthDirection = glm::length(direction); - } else if (lengthDirection > halfCubeSideA) { - collisions.deleteLastCollision(); - return NULL; - } - direction /= lengthDirection; - - // compute collision details - collision->_contactPoint = cubeCenterA + halfCubeSideA * direction; - collision->_penetration = halfCubeSideA * direction - (BA - cubeContact); - } else { - // cube center is inside cube - // --> push out nearest face - glm::vec3 direction; - BA /= maxBA; - glm::modf(BA, direction); - float lengthDirection = glm::length(direction); - direction /= lengthDirection; - - // compute collision details - collision->_floatData = cubeSideB; - collision->_vecData = cubeCenterB; - collision->_penetration = (halfCubeSideB * lengthDirection + halfCubeSideA - maxBA * glm::dot(BA, direction)) * direction; - collision->_contactPoint = cubeCenterA + halfCubeSideA * direction; - } - collision->_shapeA = NULL; - collision->_shapeB = NULL; - return collision; - } else if (halfCubeSideA + halfCubeSideB > distance) { - // NOTE: for cocentric approximation we collide sphere and cube as two spheres which means - // this algorithm will probably be wrong when both sphere and cube are very small (both ~EPSILON) - CollisionInfo* collision = collisions.getNewCollision(); - if (collision) { - // the penetration and contactPoint are undefined, so we pick a penetration direction (-yAxis) - collision->_penetration = (halfCubeSideA + halfCubeSideB) * glm::vec3(0.0f, -1.0f, 0.0f); - // contactPoint is on surface of A - collision->_contactPoint = cubeCenterA + collision->_penetration; - collision->_shapeA = NULL; - collision->_shapeB = NULL; - return collision; - } - } - return NULL; -} - -bool aaCubeVsAACube(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { - // BA = B - A = from center of A to center of B - const AACubeShape* cubeA = static_cast(shapeA); - const AACubeShape* cubeB = static_cast(shapeB); - CollisionInfo* collision = aaCubeVsAACubeHelper( cubeA->getTranslation(), cubeA->getScale(), - cubeB->getTranslation(), cubeB->getScale(), collisions); - if (collision) { - collision->_shapeA = shapeA; - collision->_shapeB = shapeB; - return true; - } - return false; -} - -bool shapeVsList(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { - bool touching = false; - const ListShape* listB = static_cast(shapeB); - for (int i = 0; i < listB->size() && !collisions.isFull(); ++i) { - const Shape* subShape = listB->getSubShape(i); - touching = collideShapes(shapeA, subShape, collisions) || touching; - } - return touching; -} - -bool listVsShape(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { - bool touching = false; - const ListShape* listA = static_cast(shapeA); - for (int i = 0; i < listA->size() && !collisions.isFull(); ++i) { - const Shape* subShape = listA->getSubShape(i); - touching = collideShapes(subShape, shapeB, collisions) || touching; - } - return touching; -} - -bool listVsList(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { - bool touching = false; - const ListShape* listA = static_cast(shapeA); - const ListShape* listB = static_cast(shapeB); - for (int i = 0; i < listA->size() && !collisions.isFull(); ++i) { - const Shape* subShape = listA->getSubShape(i); - for (int j = 0; j < listB->size() && !collisions.isFull(); ++j) { - touching = collideShapes(subShape, listB->getSubShape(j), collisions) || touching; - } - } - return touching; -} - -// helper function -/* KEEP THIS CODE -- this is how to collide the cube with stark face normals (no rounding). -* We might want to use this code later for sealing boundaries between adjacent voxels. -bool sphereAACube_StarkAngles(const glm::vec3& sphereCenter, float sphereRadius, const glm::vec3& cubeCenter, - float cubeSide, CollisionList& collisions) { - glm::vec3 BA = cubeCenter - sphereCenter; - float distance = glm::length(BA); - if (distance > EPSILON) { - BA /= distance; // BA is now normalized - // compute the nearest point on sphere - glm::vec3 surfaceA = sphereCenter + sphereRadius * BA; - // compute the nearest point on cube - float maxBA = glm::max(glm::max(fabsf(BA.x), fabsf(BA.y)), fabsf(BA.z)); - glm::vec3 surfaceB = cubeCenter - (0.5f * cubeSide / maxBA) * BA; - // collision happens when "vector to surfaceA from surfaceB" dots with BA to produce a positive value - glm::vec3 surfaceAB = surfaceA - surfaceB; - if (glm::dot(surfaceAB, BA) > 0.0f) { - CollisionInfo* collision = collisions.getNewCollision(); - if (collision) { - // penetration is parallel to box side direction - BA /= maxBA; - glm::vec3 direction; - glm::modf(BA, direction); - direction = glm::normalize(direction); - - // penetration is the projection of surfaceAB on direction - collision->_penetration = glm::dot(surfaceAB, direction) * direction; - // contactPoint is on surface of A - collision->_contactPoint = sphereCenter + sphereRadius * direction; - collision->_shapeA = NULL; - collision->_shapeB = NULL; - return true; - } - } - } else if (sphereRadius + 0.5f * cubeSide > distance) { - // NOTE: for cocentric approximation we collide sphere and cube as two spheres which means - // this algorithm will probably be wrong when both sphere and cube are very small (both ~EPSILON) - CollisionInfo* collision = collisions.getNewCollision(); - if (collision) { - // the penetration and contactPoint are undefined, so we pick a penetration direction (-yAxis) - collision->_penetration = (sphereRadius + 0.5f * cubeSide) * glm::vec3(0.0f, -1.0f, 0.0f); - // contactPoint is on surface of A - collision->_contactPoint = sphereCenter + collision->_penetration; - collision->_shapeA = NULL; - collision->_shapeB = NULL; - return true; - } - } - return false; -} -*/ - -bool sphereVsAACubeLegacy(const SphereShape* sphereA, const glm::vec3& cubeCenter, float cubeSide, CollisionList& collisions) { - return sphereVsAACubeLegacy(sphereA->getTranslation(), sphereA->getRadius(), cubeCenter, cubeSide, collisions); -} - -bool capsuleVsAACubeLegacy(const CapsuleShape* capsuleA, const glm::vec3& cubeCenter, float cubeSide, CollisionList& collisions) { - // find nerest approach of capsule line segment to cube - glm::vec3 capsuleAxis; - capsuleA->computeNormalizedAxis(capsuleAxis); - float offset = glm::dot(cubeCenter - capsuleA->getTranslation(), capsuleAxis); - float halfHeight = capsuleA->getHalfHeight(); - if (offset > halfHeight) { - offset = halfHeight; - } else if (offset < -halfHeight) { - offset = -halfHeight; - } - glm::vec3 nearestApproach = capsuleA->getTranslation() + offset * capsuleAxis; - // collide nearest approach like a sphere at that point - return sphereVsAACubeLegacy(nearestApproach, capsuleA->getRadius(), cubeCenter, cubeSide, collisions); -} - -bool findRayIntersection(const QVector& shapes, RayIntersectionInfo& intersection) { - int numShapes = shapes.size(); - bool hit = false; - for (int i = 0; i < numShapes; ++i) { - Shape* shape = shapes.at(i); - if (shape) { - if (shape->findRayIntersection(intersection)) { - hit = true; - } - } - } - return hit; -} - -} // namespace ShapeCollider diff --git a/libraries/shared/src/ShapeCollider.h b/libraries/shared/src/ShapeCollider.h deleted file mode 100644 index da37adcd49..0000000000 --- a/libraries/shared/src/ShapeCollider.h +++ /dev/null @@ -1,156 +0,0 @@ -// -// ShapeCollider.h -// libraries/shared/src -// -// Created by Andrew Meadows on 02/20/2014. -// 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_ShapeCollider_h -#define hifi_ShapeCollider_h - -#include - -#include "CollisionInfo.h" -#include "RayIntersectionInfo.h" -#include "SharedUtil.h" - - -class Shape; -class SphereShape; -class CapsuleShape; - -namespace ShapeCollider { - - /// MUST CALL this FIRST before using the ShapeCollider - void initDispatchTable(); - - /// \param shapeA pointer to first shape (cannot be NULL) - /// \param shapeB pointer to second shape (cannot be NULL) - /// \param collisions[out] collision details - /// \return true if shapes collide - bool collideShapes(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions); - - bool collideShapeWithShapes(const Shape* shapeA, const QVector& shapes, int startIndex, CollisionList& collisions); - bool collideShapesWithShapes(const QVector& shapesA, const QVector& shapesB, CollisionList& collisions); - - /// \param shapeA a pointer to a shape (cannot be NULL) - /// \param cubeCenter center of cube - /// \param cubeSide lenght of side of cube - /// \param collisions[out] average collision details - /// \return true if shapeA collides with axis aligned cube - bool collideShapeWithAACubeLegacy(const Shape* shapeA, const glm::vec3& cubeCenter, float cubeSide, CollisionList& collisions); - - /// \param sphereA pointer to first shape (cannot be NULL) - /// \param sphereB pointer to second shape (cannot be NULL) - /// \param[out] collisions where to append collision details - /// \return true if shapes collide - bool sphereVsSphere(const Shape* sphereA, const Shape* sphereB, CollisionList& collisions); - - /// \param sphereA pointer to first shape (cannot be NULL) - /// \param capsuleB pointer to second shape (cannot be NULL) - /// \param[out] collisions where to append collision details - /// \return true if shapes collide - bool sphereVsCapsule(const Shape* sphereA, const Shape* capsuleB, CollisionList& collisions); - - /// \param sphereA pointer to first shape (cannot be NULL) - /// \param planeB pointer to second shape (cannot be NULL) - /// \param[out] collisions where to append collision details - /// \return true if shapes collide - bool sphereVsPlane(const Shape* sphereA, const Shape* planeB, CollisionList& collisions); - - /// \param capsuleA pointer to first shape (cannot be NULL) - /// \param sphereB pointer to second shape (cannot be NULL) - /// \param[out] collisions where to append collision details - /// \return true if shapes collide - bool capsuleVsSphere(const Shape* capsuleA, const Shape* sphereB, CollisionList& collisions); - - /// \param capsuleA pointer to first shape (cannot be NULL) - /// \param capsuleB pointer to second shape (cannot be NULL) - /// \param[out] collisions where to append collision details - /// \return true if shapes collide - bool capsuleVsCapsule(const Shape* capsuleA, const Shape* capsuleB, CollisionList& collisions); - - /// \param capsuleA pointer to first shape (cannot be NULL) - /// \param planeB pointer to second shape (cannot be NULL) - /// \param[out] collisions where to append collision details - /// \return true if shapes collide - bool capsuleVsPlane(const Shape* capsuleA, const Shape* planeB, CollisionList& collisions); - - /// \param planeA pointer to first shape (cannot be NULL) - /// \param sphereB pointer to second shape (cannot be NULL) - /// \param[out] collisions where to append collision details - /// \return true if shapes collide - bool planeVsSphere(const Shape* planeA, const Shape* sphereB, CollisionList& collisions); - - /// \param planeA pointer to first shape (cannot be NULL) - /// \param capsuleB pointer to second shape (cannot be NULL) - /// \param[out] collisions where to append collision details - /// \return true if shapes collide - bool planeVsCapsule(const Shape* planeA, const Shape* capsuleB, CollisionList& collisions); - - /// \param planeA pointer to first shape (cannot be NULL) - /// \param planeB pointer to second shape (cannot be NULL) - /// \param[out] collisions where to append collision details - /// \return true if shapes collide - bool planeVsPlane(const Shape* planeA, const Shape* planeB, CollisionList& collisions); - - /// helper function for *VsAACube() methods - /// \param sphereCenter center of sphere - /// \param sphereRadius radius of sphere - /// \param cubeCenter center of AACube - /// \param cubeSide scale of cube - /// \param[out] collisions where to append collision details - /// \return valid pointer to CollisionInfo if sphere and cube overlap or NULL if not - CollisionInfo* sphereVsAACubeHelper(const glm::vec3& sphereCenter, float sphereRadius, - const glm::vec3& cubeCenter, float cubeSide, CollisionList& collisions); - - bool sphereVsAACube(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions); - bool capsuleVsAACube(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions); - bool aaCubeVsSphere(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions); - bool aaCubeVsCapsule(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions); - bool aaCubeVsAACube(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions); - - /// \param shapeA pointer to first shape (cannot be NULL) - /// \param listB pointer to second shape (cannot be NULL) - /// \param[out] collisions where to append collision details - /// \return true if shapes collide - bool shapeVsList(const Shape* shapeA, const Shape* listB, CollisionList& collisions); - - /// \param listA pointer to first shape (cannot be NULL) - /// \param shapeB pointer to second shape (cannot be NULL) - /// \param[out] collisions where to append collision details - /// \return true if shapes collide - bool listVsShape(const Shape* listA, const Shape* shapeB, CollisionList& collisions); - - /// \param listA pointer to first shape (cannot be NULL) - /// \param listB pointer to second shape (cannot be NULL) - /// \param[out] collisions where to append collision details - /// \return true if shapes collide - bool listVsList(const Shape* listA, const Shape* listB, CollisionList& collisions); - - /// \param sphereA pointer to sphere (cannot be NULL) - /// \param cubeCenter center of cube - /// \param cubeSide lenght of side of cube - /// \param[out] collisions where to append collision details - /// \return true if sphereA collides with axis aligned cube - bool sphereVsAACubeLegacy(const SphereShape* sphereA, const glm::vec3& cubeCenter, float cubeSide, CollisionList& collisions); - - /// \param capsuleA pointer to capsule (cannot be NULL) - /// \param cubeCenter center of cube - /// \param cubeSide lenght of side of cube - /// \param[out] collisions where to append collision details - /// \return true if capsuleA collides with axis aligned cube - bool capsuleVsAACubeLegacy(const CapsuleShape* capsuleA, const glm::vec3& cubeCenter, float cubeSide, CollisionList& collisions); - - /// \param shapes list of pointers to shapes (shape pointers may be NULL) - /// \param intersection[out] struct with info about Ray and hit - /// \return true if ray hits any shape in shapes - bool findRayIntersection(const QVector& shapes, RayIntersectionInfo& intersection); - -} // namespace ShapeCollider - -#endif // hifi_ShapeCollider_h diff --git a/libraries/shared/src/SphereShape.cpp b/libraries/shared/src/SphereShape.cpp deleted file mode 100644 index 4c47ae91c0..0000000000 --- a/libraries/shared/src/SphereShape.cpp +++ /dev/null @@ -1,51 +0,0 @@ -// -// SphereShape.cpp -// libraries/shared/src -// -// Created by Andrew Meadows on 2014.06.17 -// 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 - -#include "SphereShape.h" - -bool SphereShape::findRayIntersection(RayIntersectionInfo& intersection) const { - float r2 = _boundingRadius * _boundingRadius; - - // compute closest approach (CA) - float a = glm::dot(_translation - intersection._rayStart, intersection._rayDirection); // a = distance from ray-start to CA - float b2 = glm::distance2(_translation, intersection._rayStart + a * intersection._rayDirection); // b2 = squared distance from sphere-center to CA - if (b2 > r2) { - // ray does not hit sphere - return false; - } - float c = sqrtf(r2 - b2); // c = distance from CA to sphere surface along intersection._rayDirection - float d2 = glm::distance2(intersection._rayStart, _translation); // d2 = squared distance from sphere-center to ray-start - float distance = FLT_MAX; - if (a < 0.0f) { - // ray points away from sphere-center - if (d2 > r2) { - // ray starts outside sphere - return false; - } - // ray starts inside sphere - distance = c + a; - } else if (d2 > r2) { - // ray starts outside sphere - distance = a - c; - } else { - // ray starts inside sphere - distance = a + c; - } - if (distance > 0.0f && distance < intersection._rayLength && distance < intersection._hitDistance) { - intersection._hitDistance = distance; - intersection._hitNormal = glm::normalize(intersection._rayStart + distance * intersection._rayDirection - _translation); - intersection._hitShape = const_cast(this); - return true; - } - return false; -} diff --git a/libraries/shared/src/SphereShape.h b/libraries/shared/src/SphereShape.h deleted file mode 100644 index 964881a715..0000000000 --- a/libraries/shared/src/SphereShape.h +++ /dev/null @@ -1,63 +0,0 @@ -// -// SphereShape.h -// libraries/shared/src -// -// Created by Andrew Meadows on 2014. -// 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_SphereShape_h -#define hifi_SphereShape_h - -#include "NumericalConstants.h" -#include "Shape.h" - -class SphereShape : public Shape { -public: - SphereShape() : Shape(SPHERE_SHAPE) {} - - SphereShape(float radius) : Shape(SPHERE_SHAPE) { - _boundingRadius = radius; - } - - SphereShape(float radius, const glm::vec3& position) : Shape(SPHERE_SHAPE, position) { - _boundingRadius = radius; - } - - virtual ~SphereShape() {} - - float getRadius() const { return _boundingRadius; } - - void setRadius(float radius) { _boundingRadius = radius; } - - bool findRayIntersection(RayIntersectionInfo& intersection) const; - - float getVolume() const { return 1.3333333333f * PI * _boundingRadius * _boundingRadius * _boundingRadius; } -}; - -inline QDebug operator<<(QDebug debug, const SphereShape& shape) { - debug << "SphereShape[ (" - << "position: " - << shape.getTranslation().x << ", " << shape.getTranslation().y << ", " << shape.getTranslation().z - << "radius: " - << shape.getRadius() - << "]"; - - return debug; -} - -inline QDebug operator<<(QDebug debug, const SphereShape* shape) { - debug << "SphereShape[ (" - << "center: " - << shape->getTranslation().x << ", " << shape->getTranslation().y << ", " << shape->getTranslation().z - << "radius: " - << shape->getRadius() - << "]"; - - return debug; -} - -#endif // hifi_SphereShape_h diff --git a/libraries/shared/src/StreamUtils.cpp b/libraries/shared/src/StreamUtils.cpp index 5726728f30..d4378d82b3 100644 --- a/libraries/shared/src/StreamUtils.cpp +++ b/libraries/shared/src/StreamUtils.cpp @@ -65,38 +65,6 @@ QDataStream& operator>>(QDataStream& in, glm::quat& quaternion) { return in >> quaternion.x >> quaternion.y >> quaternion.z >> quaternion.w; } -// less common utils can be enabled with DEBUG -// FIXME, remove the second defined clause once these compile, or remove the -// functions. -#if defined(DEBUG) && defined(FIXED_STREAMS) - -std::ostream& operator<<(std::ostream& s, const CollisionInfo& c) { - s << "{penetration=" << c._penetration - << ", contactPoint=" << c._contactPoint - << ", addedVelocity=" << c._addedVelocity - << "}"; - return s; -} - -std::ostream& operator<<(std::ostream& s, const SphereShape& sphere) { - s << "{type='sphere', center=" << sphere.getPosition() - << ", radius=" << sphere.getRadius() - << "}"; - return s; -} - -std::ostream& operator<<(std::ostream& s, const CapsuleShape& capsule) { - s << "{type='capsule', center=" << capsule.getPosition() - << ", radius=" << capsule.getRadius() - << ", length=" << (2.0f * capsule.getHalfHeight()) - << ", begin=" << capsule.getStartPoint() - << ", end=" << capsule.getEndPoint() - << "}"; - return s; -} - -#endif // DEBUG - #ifndef QT_NO_DEBUG_STREAM #include diff --git a/libraries/shared/src/StreamUtils.h b/libraries/shared/src/StreamUtils.h index fd22f7c068..caf7b565f4 100644 --- a/libraries/shared/src/StreamUtils.h +++ b/libraries/shared/src/StreamUtils.h @@ -37,16 +37,6 @@ QDataStream& operator>>(QDataStream& in, glm::vec3& vector); QDataStream& operator<<(QDataStream& out, const glm::quat& quaternion); QDataStream& operator>>(QDataStream& in, glm::quat& quaternion); -// less common utils can be enabled with DEBUG -#ifdef DEBUG -class CollisionInfo; -class SphereShape; -class CapsuleShape; -std::ostream& operator<<(std::ostream& s, const CollisionInfo& c); -std::ostream& operator<<(std::ostream& s, const SphereShape& shape); -std::ostream& operator<<(std::ostream& s, const CapsuleShape& capsule); -#endif // DEBUG - #ifndef QT_NO_DEBUG_STREAM class QDebug; // Add support for writing these to qDebug(). 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/tests/shared/src/GeometryUtilTests.cpp b/tests/shared/src/GeometryUtilTests.cpp new file mode 100644 index 0000000000..798951e304 --- /dev/null +++ b/tests/shared/src/GeometryUtilTests.cpp @@ -0,0 +1,160 @@ +// +// GeometryUtilTests.cpp +// tests/physics/src +// +// Created by Andrew Meadows on 2015.07.27 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include + +#include "GeometryUtilTests.h" + +#include +#include +#include + +#include <../QTestExtensions.h> + + +QTEST_MAIN(GeometryUtilTests) + +float getErrorDifference(const float& a, const float& b) { + return fabsf(a - b); +} + +float getErrorDifference(const glm::vec3& a, const glm::vec3& b) { + return glm::distance(a, b); +} + +void GeometryUtilTests::testLocalRayRectangleIntersection() { + glm::vec3 xAxis(1.0f, 0.0f, 0.0f); + glm::vec3 yAxis(0.0f, 1.0f, 0.0f); + glm::vec3 zAxis(0.0f, 0.0f, 1.0f); + + // create a rectangle in the local frame on the XY plane with normal along -zAxis + // (this is the assumed local orientation of the rectangle in findRayRectangleIntersection()) + glm::vec2 rectDimensions(3.0f, 5.0f); + glm::vec3 rectCenter(0.0f, 0.0f, 0.0f); + glm::quat rectRotation = glm::quat(); // identity + + // create points for generating rays that hit the plane and don't + glm::vec3 rayStart(1.0f, 2.0f, 3.0f); + float delta = 0.1f; + + { // verify hit + glm::vec3 rayEnd = rectCenter + rectRotation * ((0.5f * rectDimensions.x - delta) * xAxis); + glm::vec3 rayHitDirection = glm::normalize(rayEnd - rayStart); + float expectedDistance = glm::length(rayEnd - rayStart); + + float distance = FLT_MAX; + bool hit = findRayRectangleIntersection(rayStart, rayHitDirection, rectRotation, rectCenter, rectDimensions, distance); + QCOMPARE(hit, true); + QCOMPARE_WITH_ABS_ERROR(distance, expectedDistance, EPSILON); + } + + { // verify miss + glm::vec3 rayEnd = rectCenter + rectRotation * ((0.5f * rectDimensions.y + delta) * yAxis); + glm::vec3 rayMissDirection = glm::normalize(rayEnd - rayStart); + float distance = FLT_MAX; + bool hit = findRayRectangleIntersection(rayStart, rayMissDirection, rectRotation, rectCenter, rectDimensions, distance); + QCOMPARE(hit, false); + QCOMPARE(distance, FLT_MAX); // distance should be unchanged + } + + { // hit with co-planer line + float yFraction = 0.25f; + rayStart = rectCenter + rectRotation * (rectDimensions.x * xAxis + yFraction * rectDimensions.y * yAxis); + + glm::vec3 rayEnd = rectCenter - rectRotation * (rectDimensions.x * xAxis + yFraction * rectDimensions.y * yAxis); + glm::vec3 rayHitDirection = glm::normalize(rayEnd - rayStart); + float expectedDistance = rectDimensions.x; + + float distance = FLT_MAX; + bool hit = findRayRectangleIntersection(rayStart, rayHitDirection, rectRotation, rectCenter, rectDimensions, distance); + QCOMPARE(hit, true); + QCOMPARE_WITH_ABS_ERROR(distance, expectedDistance, EPSILON); + } + + { // miss with co-planer line + float yFraction = 0.75f; + rayStart = rectCenter + rectRotation * (rectDimensions.x * xAxis + (yFraction * rectDimensions.y) * yAxis); + + glm::vec3 rayEnd = rectCenter + rectRotation * (- rectDimensions.x * xAxis + (yFraction * rectDimensions.y) * yAxis); + glm::vec3 rayHitDirection = glm::normalize(rayEnd - rayStart); + + float distance = FLT_MAX; + bool hit = findRayRectangleIntersection(rayStart, rayHitDirection, rectRotation, rectCenter, rectDimensions, distance); + QCOMPARE(hit, false); + QCOMPARE(distance, FLT_MAX); // distance should be unchanged + } +} + +void GeometryUtilTests::testWorldRayRectangleIntersection() { + 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); + + // create a rectangle in the local frame on the XY plane with normal along -zAxis + // (this is the assumed local orientation of the rectangle in findRayRectangleIntersection()) + // and then rotate and shift everything into the world frame + glm::vec2 rectDimensions(3.0f, 5.0f); + glm::vec3 rectCenter(0.7f, 0.5f, -0.3f); + glm::quat rectRotation = glm::quat(); // identity + + + // create points for generating rays that hit the plane and don't + glm::vec3 rayStart(1.0f, 2.0f, 3.0f); + float delta = 0.1f; + + { // verify hit + glm::vec3 rayEnd = rectCenter + rectRotation * ((0.5f * rectDimensions.x - delta) * xAxis); + glm::vec3 rayHitDirection = glm::normalize(rayEnd - rayStart); + float expectedDistance = glm::length(rayEnd - rayStart); + + float distance = FLT_MAX; + bool hit = findRayRectangleIntersection(rayStart, rayHitDirection, rectRotation, rectCenter, rectDimensions, distance); + QCOMPARE(hit, true); + QCOMPARE_WITH_ABS_ERROR(distance, expectedDistance, EPSILON); + } + + { // verify miss + glm::vec3 rayEnd = rectCenter + rectRotation * ((0.5f * rectDimensions.y + delta) * yAxis); + glm::vec3 rayMissDirection = glm::normalize(rayEnd - rayStart); + float distance = FLT_MAX; + bool hit = findRayRectangleIntersection(rayStart, rayMissDirection, rectRotation, rectCenter, rectDimensions, distance); + QCOMPARE(hit, false); + QCOMPARE(distance, FLT_MAX); // distance should be unchanged + } + + { // hit with co-planer line + float yFraction = 0.25f; + rayStart = rectCenter + rectRotation * (rectDimensions.x * xAxis + (yFraction * rectDimensions.y) * yAxis); + + glm::vec3 rayEnd = rectCenter - rectRotation * (rectDimensions.x * xAxis + (yFraction * rectDimensions.y) * yAxis); + glm::vec3 rayHitDirection = glm::normalize(rayEnd - rayStart); + float expectedDistance = rectDimensions.x; + + float distance = FLT_MAX; + bool hit = findRayRectangleIntersection(rayStart, rayHitDirection, rectRotation, rectCenter, rectDimensions, distance); + QCOMPARE(hit, true); + QCOMPARE_WITH_ABS_ERROR(distance, expectedDistance, EPSILON); + } + + { // miss with co-planer line + float yFraction = 0.75f; + rayStart = rectCenter + rectRotation * (rectDimensions.x * xAxis + (yFraction * rectDimensions.y) * yAxis); + + glm::vec3 rayEnd = rectCenter + rectRotation * (-rectDimensions.x * xAxis + (yFraction * rectDimensions.y) * yAxis); + glm::vec3 rayHitDirection = glm::normalize(rayEnd - rayStart); + + float distance = FLT_MAX; + bool hit = findRayRectangleIntersection(rayStart, rayHitDirection, rectRotation, rectCenter, rectDimensions, distance); + QCOMPARE(hit, false); + QCOMPARE(distance, FLT_MAX); // distance should be unchanged + } +} + diff --git a/tests/shared/src/GeometryUtilTests.h b/tests/shared/src/GeometryUtilTests.h new file mode 100644 index 0000000000..d75fce556e --- /dev/null +++ b/tests/shared/src/GeometryUtilTests.h @@ -0,0 +1,28 @@ +// +// GeometryUtilTests.h +// tests/physics/src +// +// Created by Andrew Meadows on 2014.05.30 +// 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_AngularConstraintTests_h +#define hifi_AngularConstraintTests_h + +#include +#include + +class GeometryUtilTests : public QObject { + Q_OBJECT +private slots: + void testLocalRayRectangleIntersection(); + void testWorldRayRectangleIntersection(); +}; + +float getErrorDifference(const float& a, const float& b); +float getErrorDifference(const glm::vec3& a, const glm::vec3& b); + +#endif // hifi_AngularConstraintTests_h diff --git a/tools/vhacd-util/src/VHACDUtil.cpp b/tools/vhacd-util/src/VHACDUtil.cpp index 33029d6844..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. @@ -127,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