diff --git a/CMakeLists.txt b/CMakeLists.txt index dff49df7d7..d7803259c2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -125,6 +125,6 @@ if (NOT ANDROID) add_subdirectory(interface) add_subdirectory(tests) add_subdirectory(tools) -else () +elseif (ANDROID OR DESKTOP_GVR) add_subdirectory(gvr-interface) endif () \ No newline at end of file diff --git a/assignment-client/src/metavoxels/MetavoxelServer.cpp b/assignment-client/src/metavoxels/MetavoxelServer.cpp index 5c4591cf61..669972cbee 100644 --- a/assignment-client/src/metavoxels/MetavoxelServer.cpp +++ b/assignment-client/src/metavoxels/MetavoxelServer.cpp @@ -271,9 +271,10 @@ void MetavoxelSession::handleMessage(const QVariant& message) { _lodPacketNumber = _sequencer.getIncomingPacketNumber(); } else if (userType == MetavoxelEditMessage::Type) { - QMetaObject::invokeMethod(_sender->getServer(), "applyEdit", Q_ARG(const MetavoxelEditMessage&, - message.value())); - + if (_node->getCanAdjustLocks()) { + QMetaObject::invokeMethod(_sender->getServer(), "applyEdit", + Q_ARG(const MetavoxelEditMessage&, message.value())); + } } else if (userType == QMetaType::QVariantList) { foreach (const QVariant& element, message.toList()) { handleMessage(element); diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index 83b270c772..1ff82f58eb 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -6,7 +6,7 @@ { "name": "access_token", "label": "Access Token", - "help": "This is an access token generated on the My Security page of your High Fidelity account.
Generate a token with the 'domains' scope and paste it here.
This is required to associate this domain-server with a domain in your account." + "help": "This is an access token generated on the My Security page of your High Fidelity account.
Generate a token with the 'domains' scope and paste it here.
This is required to associate this domain-server with a domain in your account." }, { "name": "id", @@ -30,7 +30,7 @@ }, { "value": "disabled", - "label": "None: use the network information I have entered for this domain at data.highfidelity.io" + "label": "None: use the network information I have entered for this domain at metaverse.highfidelity.io" } ] }, diff --git a/domain-server/resources/web/js/settings.js b/domain-server/resources/web/js/settings.js index bdd80df9ec..f62515c863 100644 --- a/domain-server/resources/web/js/settings.js +++ b/domain-server/resources/web/js/settings.js @@ -652,7 +652,7 @@ function chooseFromHighFidelityDomains(clickedButton) { clickedButton.attr('disabled', 'disabled') // get a list of user domains from data-web - data_web_domains_url = "https://data.highfidelity.io/api/v1/domains?access_token=" + data_web_domains_url = "https://metaverse.highfidelity.io/api/v1/domains?access_token=" $.getJSON(data_web_domains_url + Settings.initialValues.metaverse.access_token, function(data){ modal_buttons = { @@ -682,7 +682,7 @@ function chooseFromHighFidelityDomains(clickedButton) { modal_buttons["success"] = { label: 'Create new domain', callback: function() { - window.open("https://data.highfidelity.io/user/domains", '_blank'); + window.open("https://metaverse.highfidelity.io/user/domains", '_blank'); } } modal_body = "

You do not have any domains in your High Fidelity account." + diff --git a/examples/editEntities.js b/examples/editEntities.js index 83550811f1..a9b5bfeb16 100644 --- a/examples/editEntities.js +++ b/examples/editEntities.js @@ -105,15 +105,7 @@ var toolBar = (function () { newSphereButton, newLightButton, newTextButton, - browseModelsButton, - loadURLMenuItem, - loadFileMenuItem, - menuItemWidth, - menuItemOffset, - menuItemHeight, - menuItemMargin = 5, - menuTextColor = { red: 255, green: 255, blue: 255 }, - menuBackgroundColor = { red: 18, green: 66, blue: 66 }; + browseModelsButton; function initialize() { toolBar = new ToolBar(0, 0, ToolBar.VERTICAL); @@ -145,34 +137,6 @@ var toolBar = (function () { visible: true }); - menuItemOffset = toolBar.height / 3 + 2; - menuItemHeight = Tool.IMAGE_HEIGHT / 2 - 2; - - loadURLMenuItem = Overlays.addOverlay("text", { - height: menuItemHeight, - backgroundColor: menuBackgroundColor, - topMargin: menuItemMargin, - text: "Model URL", - alpha: 0.9, - backgroundAlpha: 0.9, - visible: false - }); - - loadFileMenuItem = Overlays.addOverlay("text", { - height: menuItemHeight, - backgroundColor: menuBackgroundColor, - topMargin: menuItemMargin, - text: "Model File", - alpha: 0.9, - backgroundAlpha: 0.9, - visible: false - }); - - menuItemWidth = Math.max(Overlays.textSize(loadURLMenuItem, "Model URL").width, - Overlays.textSize(loadFileMenuItem, "Model File").width) + 20; - Overlays.editOverlay(loadURLMenuItem, { width: menuItemWidth }); - Overlays.editOverlay(loadFileMenuItem, { width: menuItemWidth }); - newCubeButton = toolBar.addTool({ imageURL: toolIconUrl + "add-cube.svg", subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, @@ -211,17 +175,6 @@ var toolBar = (function () { } - function toggleNewModelButton(active) { - if (active === undefined) { - active = !toolBar.toolSelected(newModelButton); - } - toolBar.selectTool(newModelButton, active); - - Overlays.editOverlay(loadURLMenuItem, { visible: active }); - Overlays.editOverlay(loadFileMenuItem, { visible: active }); - } - - that.setActive = function(active) { if (active != isActive) { isActive = active; @@ -237,6 +190,7 @@ var toolBar = (function () { cameraManager.enable(); entityListTool.setVisible(true); gridTool.setVisible(true); + grid.setEnabled(true); propertiesTool.setVisible(true); Window.setFocus(); } @@ -245,7 +199,7 @@ var toolBar = (function () { }; var RESIZE_INTERVAL = 50; - var RESIZE_TIMEOUT = 20000; + var RESIZE_TIMEOUT = 120000; // 2 minutes var RESIZE_MAX_CHECKS = RESIZE_TIMEOUT / RESIZE_INTERVAL; function addModel(url) { var position; @@ -306,9 +260,6 @@ var toolBar = (function () { toolsY = (windowDimensions.y - toolBar.height) / 2; toolBar.move(toolsX, toolsY); - - Overlays.editOverlay(loadURLMenuItem, { x: toolsX - menuItemWidth, y: toolsY + menuItemOffset }); - Overlays.editOverlay(loadFileMenuItem, { x: toolsX - menuItemWidth, y: toolsY + menuItemOffset + menuItemHeight }); }; that.mousePressEvent = function (event) { @@ -324,12 +275,6 @@ var toolBar = (function () { } if (newModelButton === toolBar.clicked(clickedOverlay)) { - toggleNewModelButton(); - return true; - } - - if (clickedOverlay === loadURLMenuItem) { - toggleNewModelButton(false); url = Window.prompt("Model URL", modelURLs[Math.floor(Math.random() * modelURLs.length)]); if (url !== null && url !== "") { addModel(url); @@ -337,22 +282,7 @@ var toolBar = (function () { return true; } - if (clickedOverlay === loadFileMenuItem) { - toggleNewModelButton(false); - - file = Window.browse("Select your model file ...", - Settings.getValue("LastModelUploadLocation").path(), - "Model files (*.fst *.fbx)"); - //"Model files (*.fst *.fbx *.svo)"); - if (file !== null) { - Settings.setValue("LastModelUploadLocation", file); - modelUploader.upload(file, addModel); - } - return true; - } - if (browseModelsButton === toolBar.clicked(clickedOverlay)) { - toggleNewModelButton(false); url = Window.s3Browse(".*(fbx|FBX)"); if (url !== null && url !== "") { addModel(url); @@ -443,8 +373,6 @@ var toolBar = (function () { that.cleanup = function () { toolBar.cleanup(); - Overlays.deleteOverlay(loadURLMenuItem); - Overlays.deleteOverlay(loadFileMenuItem); }; return that; diff --git a/examples/html/entityProperties.html b/examples/html/entityProperties.html index 08fbb1cea8..84a8d23a74 100644 --- a/examples/html/entityProperties.html +++ b/examples/html/entityProperties.html @@ -155,6 +155,7 @@ var elModelAnimationSettings = document.getElementById("property-model-animation-settings"); var elModelTextures = document.getElementById("property-model-textures"); var elModelOriginalTextures = document.getElementById("property-model-original-textures"); + var elModelShapeType = document.getElementById("property-model-shape"); var elTextSections = document.querySelectorAll(".text-section"); var elTextText = document.getElementById("property-text-text"); @@ -258,6 +259,7 @@ elModelAnimationSettings.value = properties.animationSettings; elModelTextures.value = properties.textures; elModelOriginalTextures.value = properties.originalTextures; + elModelShapeType.value = properties.shapeType; } if (properties.type != "Text") { @@ -404,6 +406,7 @@ elModelAnimationFrame.addEventListener('change', createEmitNumberPropertyUpdateFunction('animationFrameIndex')); elModelAnimationSettings.addEventListener('change', createEmitTextPropertyUpdateFunction('animationSettings')); elModelTextures.addEventListener('change', createEmitTextPropertyUpdateFunction('textures')); + elModelShapeType.addEventListener('change', createEmitNumberPropertyUpdateFunction('shapeType')); elTextText.addEventListener('change', createEmitTextPropertyUpdateFunction('text')); elTextLineHeight.addEventListener('change', createEmitNumberPropertyUpdateFunction('lineHeight')); @@ -664,6 +667,17 @@ +

+
Shape Type
+
+ +
+
+
Text
diff --git a/examples/html/gridControls.html b/examples/html/gridControls.html index 7e354c5bec..941a4b5c2a 100644 --- a/examples/html/gridControls.html +++ b/examples/html/gridControls.html @@ -30,8 +30,8 @@ elPosY.value = origin.y.toFixed(2); } - if (data.minorGridSpacing !== undefined) { - elMinorSpacing.value = data.minorGridSpacing; + if (data.minorGridWidth !== undefined) { + elMinorSpacing.value = data.minorGridWidth; } if (data.majorGridEvery !== undefined) { @@ -57,7 +57,7 @@ origin: { y: elPosY.value, }, - minorGridSpacing: elMinorSpacing.value, + minorGridWidth: elMinorSpacing.value, majorGridEvery: elMajorSpacing.value, gridColor: gridColor, colorIndex: gridColorIndex, diff --git a/examples/libraries/modelUploader.js b/examples/libraries/modelUploader.js index 7f575a54ef..fcc96854ab 100644 --- a/examples/libraries/modelUploader.js +++ b/examples/libraries/modelUploader.js @@ -21,7 +21,7 @@ modelUploader = (function () { //svoBuffer, mapping, geometry, - API_URL = "https://data.highfidelity.io/api/v1/models", + API_URL = "https://metaverse.highfidelity.io/api/v1/models", MODEL_URL = "http://public.highfidelity.io/models/content", NAME_FIELD = "name", SCALE_FIELD = "scale", diff --git a/examples/lobby.js b/examples/lobby.js index fcac7a490b..1936a4e531 100644 --- a/examples/lobby.js +++ b/examples/lobby.js @@ -152,7 +152,7 @@ var places = {}; function changeLobbyTextures() { var req = new XMLHttpRequest(); - req.open("GET", "https://data.highfidelity.io/api/v1/places?limit=21", false); + req.open("GET", "https://metaverse.highfidelity.io/api/v1/places?limit=21", false); req.send(); places = JSON.parse(req.responseText).data.places; diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 1462d7bcb2..6aa417de1b 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -516,7 +516,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : void Application::aboutToQuit() { _aboutToQuit = true; - setFullscreen(false); // if you exit while in full screen, you'll get bad behavior when you restart. } Application::~Application() { diff --git a/interface/src/MainWindow.cpp b/interface/src/MainWindow.cpp index f40f0ce751..41b1249af1 100644 --- a/interface/src/MainWindow.cpp +++ b/interface/src/MainWindow.cpp @@ -24,7 +24,8 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), - _windowGeometry("WindowGeometry") + _windowGeometry("WindowGeometry"), + _windowState("WindowState", 0) { } @@ -34,13 +35,24 @@ void MainWindow::restoreGeometry() { QRect geometry = _windowGeometry.get(qApp->desktop()->availableGeometry()); move(geometry.topLeft()); resize(geometry.size()); + + // Restore to maximized or full screen after restoring to windowed so that going windowed goes to good position and sizes. + Qt::WindowStates state = (Qt::WindowStates)_windowState.get(Qt::WindowNoState); + if (state != Qt::WindowNoState) { + setWindowState(state); + } } void MainWindow::saveGeometry() { // Did not use geometry() on purpose, // see http://doc.qt.io/qt-5/qsettings.html#restoring-the-state-of-a-gui-application - QRect geometry(pos(), size()); - _windowGeometry.set(geometry); + _windowState.set((int)windowState()); + + // Save position and size only if windowed so that have good values for windowed after starting maximized or full screen. + if (windowState() == Qt::WindowNoState) { + QRect geometry(pos(), size()); + _windowGeometry.set(geometry); + } } void MainWindow::moveEvent(QMoveEvent* event) { diff --git a/interface/src/MainWindow.h b/interface/src/MainWindow.h index e2927fb4e2..4437fa6a1f 100644 --- a/interface/src/MainWindow.h +++ b/interface/src/MainWindow.h @@ -38,6 +38,7 @@ protected: private: Setting::Handle _windowGeometry; + Setting::Handle _windowState; }; #endif /* defined(__hifi__MainWindow__) */ diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 296ff6fee9..6e207f9703 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -760,19 +760,19 @@ void SkeletonModel::buildShapes() { Shape::Type type = joint.shapeType; int parentIndex = joint.parentIndex; if (parentIndex == -1 || radius < EPSILON) { - type = SHAPE_TYPE_UNKNOWN; - } else if (type == SHAPE_TYPE_CAPSULE && halfHeight < EPSILON) { + type = INVALID_SHAPE; + } else if (type == CAPSULE_SHAPE && halfHeight < EPSILON) { // this shape is forced to be a sphere - type = SHAPE_TYPE_SPHERE; + type = SPHERE_SHAPE; } Shape* shape = NULL; - if (type == SHAPE_TYPE_SPHERE) { + if (type == SPHERE_SHAPE) { shape = new VerletSphereShape(radius, &(points[i])); shape->setEntity(this); float mass = massScale * glm::max(MIN_JOINT_MASS, DENSITY_OF_WATER * shape->getVolume()); points[i].setMass(mass); totalMass += mass; - } else if (type == SHAPE_TYPE_CAPSULE) { + } else if (type == CAPSULE_SHAPE) { assert(parentIndex != -1); shape = new VerletCapsuleShape(radius, &(points[parentIndex]), &(points[i])); shape->setEntity(this); diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 18b9943e29..f9437221e5 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -788,13 +788,12 @@ void ApplicationOverlay::renderAudioMeter() { // Audio VU Meter and Mute Icon const int MUTE_ICON_SIZE = 24; - const int AUDIO_METER_INSET = 2; const int MUTE_ICON_PADDING = 10; - const int AUDIO_METER_WIDTH = MIRROR_VIEW_WIDTH - MUTE_ICON_SIZE - AUDIO_METER_INSET - MUTE_ICON_PADDING; - const int AUDIO_METER_SCALE_WIDTH = AUDIO_METER_WIDTH - 2 * AUDIO_METER_INSET; + const int AUDIO_METER_WIDTH = MIRROR_VIEW_WIDTH - MUTE_ICON_SIZE - MUTE_ICON_PADDING; + const int AUDIO_METER_SCALE_WIDTH = AUDIO_METER_WIDTH - 2 ; const int AUDIO_METER_HEIGHT = 8; const int AUDIO_METER_GAP = 5; - const int AUDIO_METER_X = MIRROR_VIEW_LEFT_PADDING + MUTE_ICON_SIZE + AUDIO_METER_INSET + AUDIO_METER_GAP; + const int AUDIO_METER_X = MIRROR_VIEW_LEFT_PADDING + MUTE_ICON_SIZE + AUDIO_METER_GAP; int audioMeterY; bool smallMirrorVisible = Menu::getInstance()->isOptionChecked(MenuOption::Mirror) && !OculusManager::isConnected(); @@ -847,7 +846,7 @@ void ApplicationOverlay::renderAudioMeter() { // Draw audio meter background Quad DependencyManager::get()->renderQuad(AUDIO_METER_X, audioMeterY, AUDIO_METER_WIDTH, AUDIO_METER_HEIGHT, - glm::vec4(0, 0, 0, 1)); + glm::vec4(0.298f, 0.757f, 0.722f, 1)); if (audioLevel > AUDIO_RED_START) { glm::vec4 quadColor; @@ -857,10 +856,10 @@ void ApplicationOverlay::renderAudioMeter() { quadColor = glm::vec4(1, 1, 1, 1); } // Draw Red Quad - DependencyManager::get()->renderQuad(AUDIO_METER_X + AUDIO_METER_INSET + AUDIO_RED_START, - audioMeterY + AUDIO_METER_INSET, + DependencyManager::get()->renderQuad(AUDIO_METER_X + AUDIO_RED_START, + audioMeterY, audioLevel - AUDIO_RED_START, - AUDIO_METER_HEIGHT - AUDIO_METER_INSET, quadColor, + AUDIO_METER_HEIGHT, quadColor, _audioRedQuad); audioLevel = AUDIO_RED_START; @@ -874,26 +873,28 @@ void ApplicationOverlay::renderAudioMeter() { quadColor = glm::vec4(1, 1, 1, 1); } // Draw Green Quad - DependencyManager::get()->renderQuad(AUDIO_METER_X + AUDIO_METER_INSET + AUDIO_GREEN_START, - audioMeterY + AUDIO_METER_INSET, + DependencyManager::get()->renderQuad(AUDIO_METER_X + AUDIO_GREEN_START, + audioMeterY, audioLevel - AUDIO_GREEN_START, - AUDIO_METER_HEIGHT - AUDIO_METER_INSET, quadColor, + AUDIO_METER_HEIGHT, quadColor, _audioGreenQuad); audioLevel = AUDIO_GREEN_START; } - // Draw Blue Quad - glm::vec4 quadColor; - if (!isClipping) { - quadColor = AUDIO_METER_BLUE; - } else { - quadColor = glm::vec4(1, 1, 1, 1); + + if (audioLevel >= 0) { + glm::vec4 quadColor; + if (!isClipping) { + quadColor = AUDIO_METER_BLUE; + } else { + quadColor = glm::vec4(1, 1, 1, 1); + } + // Draw Blue (low level) quad + DependencyManager::get()->renderQuad(AUDIO_METER_X, + audioMeterY, + audioLevel, AUDIO_METER_HEIGHT, quadColor, + _audioBlueQuad); } - // Draw Blue (low level) quad - DependencyManager::get()->renderQuad(AUDIO_METER_X + AUDIO_METER_INSET, - audioMeterY + AUDIO_METER_INSET, - audioLevel, AUDIO_METER_HEIGHT - AUDIO_METER_INSET, quadColor, - _audioBlueQuad); } void ApplicationOverlay::renderStatsAndLogs() { diff --git a/interface/src/ui/LoginDialog.cpp b/interface/src/ui/LoginDialog.cpp index 3fd6e8e3cd..049e5bd1cd 100644 --- a/interface/src/ui/LoginDialog.cpp +++ b/interface/src/ui/LoginDialog.cpp @@ -22,7 +22,7 @@ #include "ui_loginDialog.h" #include "LoginDialog.h" -const QString FORGOT_PASSWORD_URL = "https://data.highfidelity.io/users/password/new"; +const QString FORGOT_PASSWORD_URL = "https://metaverse.highfidelity.io/users/password/new"; LoginDialog::LoginDialog(QWidget* parent) : FramelessDialog(parent, 0, FramelessDialog::POSITION_TOP), diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index ca4a88600e..7617adf01c 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -37,7 +37,6 @@ using namespace std; const int STATS_PELS_PER_LINE = 20; const int STATS_GENERAL_MIN_WIDTH = 165; -const int STATS_BANDWIDTH_MIN_WIDTH = 250; const int STATS_PING_MIN_WIDTH = 190; const int STATS_GEO_MIN_WIDTH = 240; const int STATS_OCTREE_MIN_WIDTH = 410; @@ -52,7 +51,6 @@ Stats::Stats(): _recentMaxPackets(0), _resetRecentMaxPacketsSoon(true), _generalStatsWidth(STATS_GENERAL_MIN_WIDTH), - _bandwidthStatsWidth(STATS_BANDWIDTH_MIN_WIDTH), _pingStatsWidth(STATS_PING_MIN_WIDTH), _geoStatsWidth(STATS_GEO_MIN_WIDTH), _octreeStatsWidth(STATS_OCTREE_MIN_WIDTH), @@ -133,7 +131,6 @@ void Stats::resetWidth(int width, int horizontalOffset) { auto glCanvas = DependencyManager::get(); int extraSpace = glCanvas->width() - horizontalOffset -2 - STATS_GENERAL_MIN_WIDTH - - STATS_BANDWIDTH_MIN_WIDTH - (Menu::getInstance()->isOptionChecked(MenuOption::TestPing) ? STATS_PING_MIN_WIDTH -1 : 0) - STATS_GEO_MIN_WIDTH - STATS_OCTREE_MIN_WIDTH; @@ -141,7 +138,6 @@ void Stats::resetWidth(int width, int horizontalOffset) { int panels = 4; _generalStatsWidth = STATS_GENERAL_MIN_WIDTH; - _bandwidthStatsWidth = STATS_BANDWIDTH_MIN_WIDTH; if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) { _pingStatsWidth = STATS_PING_MIN_WIDTH; } else { @@ -153,13 +149,12 @@ void Stats::resetWidth(int width, int horizontalOffset) { if (extraSpace > panels) { _generalStatsWidth += (int) extraSpace / panels; - _bandwidthStatsWidth += (int) extraSpace / panels; if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) { _pingStatsWidth += (int) extraSpace / panels; } _geoStatsWidth += (int) extraSpace / panels; _octreeStatsWidth += glCanvas->width() - - (_generalStatsWidth + _bandwidthStatsWidth + _pingStatsWidth + _geoStatsWidth + 3); + (_generalStatsWidth + _pingStatsWidth + _geoStatsWidth + 3); } } @@ -231,14 +226,14 @@ void Stats::display( int totalAvatars = DependencyManager::get()->size() - 1; int totalServers = DependencyManager::get()->size(); - lines = _expanded ? 5 : 3; + lines = 5; int columnOneWidth = _generalStatsWidth; PerformanceTimer::tallyAllTimerRecords(); // do this even if we're not displaying them, so they don't stack up if (_expanded && Menu::getInstance()->isOptionChecked(MenuOption::DisplayTimingDetails)) { - columnOneWidth = _generalStatsWidth + _bandwidthStatsWidth + _pingStatsWidth + _geoStatsWidth; // 4 columns wide... + columnOneWidth = _generalStatsWidth + _pingStatsWidth + _geoStatsWidth; // 3 columns wide... // we will also include room for 1 line per timing record and a header of 4 lines lines += 4; @@ -267,6 +262,17 @@ void Stats::display( drawText(horizontalOffset, verticalOffset, scale, rotation, font, avatarNodes.toUtf8().constData(), color); verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, scale, rotation, font, framesPerSecond.toUtf8().constData(), color); + + QString packetsPerSecondString = QString("Packets In/Out: %1/%2").arg(inPacketsPerSecond).arg(outPacketsPerSecond); + QString averageMegabitsPerSecond = QString("Mbps In/Out: %1/%2"). + arg((float)inKbitsPerSecond * 1.0f / 1000.0f). + arg((float)outKbitsPerSecond * 1.0f / 1000.0f); + + verticalOffset += STATS_PELS_PER_LINE; + drawText(horizontalOffset, verticalOffset, scale, rotation, font, packetsPerSecondString.toUtf8().constData(), color); + verticalOffset += STATS_PELS_PER_LINE; + drawText(horizontalOffset, verticalOffset, scale, rotation, font, averageMegabitsPerSecond.toUtf8().constData(), color); + // TODO: the display of these timing details should all be moved to JavaScript if (_expanded && Menu::getInstance()->isOptionChecked(MenuOption::DisplayTimingDetails)) { @@ -309,30 +315,9 @@ void Stats::display( } - verticalOffset = 0; horizontalOffset = _lastHorizontalOffset + _generalStatsWidth + 1; - if (columnOneWidth == _generalStatsWidth) { - drawBackground(backgroundColor, horizontalOffset, 0, _bandwidthStatsWidth, lines * STATS_PELS_PER_LINE + 10); - } - horizontalOffset += 5; - - QString packetsPerSecondString = QString("Packets In/Out: %1/%2").arg(inPacketsPerSecond).arg(outPacketsPerSecond); - QString averageMegabitsPerSecond = QString("Mbps In/Out: %1/%2"). - arg((float)inKbitsPerSecond * 1.0f / 1000.0f). - arg((float)outKbitsPerSecond * 1.0f / 1000.0f); - - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, packetsPerSecondString.toUtf8().constData(), color); - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, averageMegabitsPerSecond.toUtf8().constData(), color); - - - - verticalOffset = 0; - horizontalOffset = _lastHorizontalOffset + _generalStatsWidth + _bandwidthStatsWidth +1; - if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) { int pingAudio = -1, pingAvatar = -1, pingVoxel = -1, pingOctreeMax = -1; @@ -412,7 +397,7 @@ void Stats::display( } verticalOffset = 0; - horizontalOffset = _lastHorizontalOffset + _generalStatsWidth + _bandwidthStatsWidth + _pingStatsWidth + 2; + horizontalOffset = _lastHorizontalOffset + _generalStatsWidth + _pingStatsWidth + 2; } MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); @@ -492,7 +477,7 @@ void Stats::display( } verticalOffset = 0; - horizontalOffset = _lastHorizontalOffset + _generalStatsWidth + _bandwidthStatsWidth + _pingStatsWidth + _geoStatsWidth + 3; + horizontalOffset = _lastHorizontalOffset + _generalStatsWidth + _pingStatsWidth + _geoStatsWidth + 3; lines = _expanded ? 14 : 3; diff --git a/interface/src/ui/overlays/Text3DOverlay.cpp b/interface/src/ui/overlays/Text3DOverlay.cpp index 0a803043cc..2cf8e6d006 100644 --- a/interface/src/ui/overlays/Text3DOverlay.cpp +++ b/interface/src/ui/overlays/Text3DOverlay.cpp @@ -20,6 +20,7 @@ const xColor DEFAULT_BACKGROUND_COLOR = { 0, 0, 0 }; const float DEFAULT_BACKGROUND_ALPHA = 0.7f; const float DEFAULT_MARGIN = 0.1f; const int FIXED_FONT_POINT_SIZE = 40; +const int FIXED_FONT_SCALING_RATIO = FIXED_FONT_POINT_SIZE * 40.0f; // this is a ratio determined through experimentation const float LINE_SCALE_RATIO = 1.2f; Text3DOverlay::Text3DOverlay() : @@ -104,8 +105,6 @@ void Text3DOverlay::render(RenderArgs* args) { glm::vec3 bottomRight(halfDimensions.x, halfDimensions.y, SLIGHTLY_BEHIND); DependencyManager::get()->renderQuad(topLeft, bottomRight, quadColor); - const int FIXED_FONT_SCALING_RATIO = FIXED_FONT_POINT_SIZE * 40.0f; // this is a ratio determined through experimentation - // Same font properties as textSize() TextRenderer* textRenderer = TextRenderer::getInstance(SANS_FONT_FAMILY, FIXED_FONT_POINT_SIZE); float maxHeight = (float)textRenderer->computeExtent("Xy").y * LINE_SCALE_RATIO; @@ -229,22 +228,12 @@ Text3DOverlay* Text3DOverlay::createClone() const { } QSizeF Text3DOverlay::textSize(const QString& text) const { + auto textRenderer = TextRenderer::getInstance(SANS_FONT_FAMILY, FIXED_FONT_POINT_SIZE); + auto extents = textRenderer->computeExtent(text); - QFont font(SANS_FONT_FAMILY, FIXED_FONT_POINT_SIZE); // Same font properties as render() - QFontMetrics fontMetrics(font); - const float TEXT_SCALE_ADJUST = 1.025f; // Experimentally detemined for the specified font - const int TEXT_HEIGHT_ADJUST = -10; - float scaleFactor = _lineHeight * TEXT_SCALE_ADJUST * LINE_SCALE_RATIO / (float)FIXED_FONT_POINT_SIZE; + float maxHeight = (float)textRenderer->computeExtent("Xy").y * LINE_SCALE_RATIO; + float pointToWorldScale = (maxHeight / FIXED_FONT_SCALING_RATIO) * _lineHeight; - QStringList lines = text.split(QRegExp("\r\n|\r|\n")); - - float width = 0.0f; - for (int i = 0; i < lines.count(); i += 1) { - width = std::max(width, scaleFactor * (float)fontMetrics.width(qPrintable(lines[i]))); - } - - float height = lines.count() * scaleFactor * (float)(fontMetrics.height() + TEXT_HEIGHT_ADJUST); - - return QSizeF(width, height); + return QSizeF(extents.x, extents.y) * pointToWorldScale; } diff --git a/interface/src/ui/overlays/TextOverlay.cpp b/interface/src/ui/overlays/TextOverlay.cpp index 2f4b0d355d..f3b05b7300 100644 --- a/interface/src/ui/overlays/TextOverlay.cpp +++ b/interface/src/ui/overlays/TextOverlay.cpp @@ -165,19 +165,8 @@ QScriptValue TextOverlay::getProperty(const QString& property) { } QSizeF TextOverlay::textSize(const QString& text) const { + auto textRenderer = TextRenderer::getInstance(SANS_FONT_FAMILY, _fontSize, DEFAULT_FONT_WEIGHT); + auto extents = textRenderer->computeExtent(text); - QFont font(SANS_FONT_FAMILY, _fontSize, DEFAULT_FONT_WEIGHT); // Same font properties as render() - QFontMetrics fontMetrics(font); - const int TEXT_HEIGHT_ADJUST = -2; // Experimentally determined for the specified font - - QStringList lines = text.split(QRegExp("\r\n|\r|\n")); - - int width = 0; - for (int i = 0; i < lines.count(); i += 1) { - width = std::max(width, fontMetrics.width(qPrintable(lines[i]))); - } - - int height = lines.count() * (fontMetrics.height() + TEXT_HEIGHT_ADJUST); - - return QSizeF(width, height); + return QSizeF(extents.x, extents.y); } diff --git a/interface/ui/loginDialog.ui b/interface/ui/loginDialog.ui index 73d466b68a..c986db7f50 100644 --- a/interface/ui/loginDialog.ui +++ b/interface/ui/loginDialog.ui @@ -136,7 +136,7 @@ <style type="text/css"> a { text-decoration: none; color: #267077;} </style> -Invalid username or password. <a href="https://data.highfidelity.io/password/new">Recover?</a> +Invalid username or password. <a href="https://metaverse.highfidelity.io/password/new">Recover?</a> Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter @@ -458,7 +458,7 @@ border-radius: 4px; padding-top: 1px; <style type="text/css"> a { text-decoration: none; color: #267077;} </style> -<a href="https://data.highfidelity.io/password/new">Recover password?</a> +<a href="https://metaverse.highfidelity.io/password/new">Recover password?</a> true diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp index 4dc2a07249..b1b3678dc9 100644 --- a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp @@ -13,6 +13,7 @@ #include +#include #include #include #include @@ -49,7 +50,12 @@ void RenderableTextEntityItem::render(RenderArgs* args) { glm::vec3 topLeft(-halfDimensions.x, -halfDimensions.y, SLIGHTLY_BEHIND); glm::vec3 bottomRight(halfDimensions.x, halfDimensions.y, SLIGHTLY_BEHIND); + + // TODO: Determine if we want these entities to have the deferred lighting effect? I think we do, so that the color + // used for a sphere, or box have the same look as those used on a text entity. + DependencyManager::get()->bindSimpleProgram(); DependencyManager::get()->renderQuad(topLeft, bottomRight, glm::vec4(toGlm(getBackgroundColorX()), alpha)); + DependencyManager::get()->releaseSimpleProgram(); TextRenderer* textRenderer = TextRenderer::getInstance(SANS_FONT_FAMILY, FIXED_FONT_POINT_SIZE / 2.0f); diff --git a/libraries/entities/src/BoxEntityItem.cpp b/libraries/entities/src/BoxEntityItem.cpp index 25ef2e6aaf..1e8c811122 100644 --- a/libraries/entities/src/BoxEntityItem.cpp +++ b/libraries/entities/src/BoxEntityItem.cpp @@ -96,11 +96,6 @@ void BoxEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitst APPEND_ENTITY_PROPERTY(PROP_COLOR, appendColor, getColor()); } -void BoxEntityItem::computeShapeInfo(ShapeInfo& info) const { - glm::vec3 halfExtents = 0.5f * getDimensionsInMeters(); - info.setBox(halfExtents); -} - void BoxEntityItem::debugDump() const { quint64 now = usecTimestampNow(); qDebug() << " BOX EntityItem id:" << getEntityItemID() << "---------------------------------------------"; diff --git a/libraries/entities/src/BoxEntityItem.h b/libraries/entities/src/BoxEntityItem.h index 8d68a13158..e8459e7dbb 100644 --- a/libraries/entities/src/BoxEntityItem.h +++ b/libraries/entities/src/BoxEntityItem.h @@ -51,7 +51,7 @@ public: _color[BLUE_INDEX] = value.blue; } - void computeShapeInfo(ShapeInfo& info) const; + virtual ShapeType getShapeType() const { return SHAPE_TYPE_BOX; } virtual void debugDump() const; diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 9717143ec0..15d46603b5 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -57,8 +57,6 @@ void EntityItem::initFromEntityItemID(const EntityItemID& entityItemID) { _collisionsWillMove = ENTITY_ITEM_DEFAULT_COLLISIONS_WILL_MOVE; _locked = ENTITY_ITEM_DEFAULT_LOCKED; _userData = ENTITY_ITEM_DEFAULT_USER_DATA; - - recalculateCollisionShape(); } EntityItem::EntityItem(const EntityItemID& entityItemID) { @@ -70,7 +68,6 @@ EntityItem::EntityItem(const EntityItemID& entityItemID) { _lastEditedFromRemote = 0; _lastEditedFromRemoteInRemoteTime = 0; _created = UNKNOWN_CREATED_TIME; - _physicsInfo = NULL; _dirtyFlags = 0; _changedOnServer = 0; _element = NULL; @@ -86,7 +83,6 @@ EntityItem::EntityItem(const EntityItemID& entityItemID, const EntityItemPropert _lastEditedFromRemote = 0; _lastEditedFromRemoteInRemoteTime = 0; _created = UNKNOWN_CREATED_TIME; - _physicsInfo = NULL; _dirtyFlags = 0; _changedOnServer = 0; _element = NULL; @@ -541,7 +537,6 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef bytesRead += readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, propertyFlags, overwriteLocalData); - recalculateCollisionShape(); if (overwriteLocalData && (getDirtyFlags() & (EntityItem::DIRTY_POSITION | EntityItem::DIRTY_VELOCITY))) { // NOTE: This code is attempting to "repair" the old data we just got from the server to make it more // closely match where the entities should be if they'd stepped forward in time to "now". The server @@ -1003,14 +998,7 @@ float EntityItem::getRadius() const { } void EntityItem::computeShapeInfo(ShapeInfo& info) const { - info.clear(); -} - -void EntityItem::recalculateCollisionShape() { - AACube entityAACube = getMinimumAACube(); - entityAACube.scale(TREE_SCALE); // scale to meters - _collisionShape.setTranslation(entityAACube.calcCenter()); - _collisionShape.setScale(entityAACube.getScale()); + info.setParams(getShapeType(), 0.5f * getDimensionsInMeters()); } const float MIN_POSITION_DELTA = 0.0001f; @@ -1023,7 +1011,6 @@ const float MIN_SPIN_DELTA = 0.0003f; void EntityItem::updatePosition(const glm::vec3& value) { if (glm::distance(_position, value) * (float)TREE_SCALE > MIN_POSITION_DELTA) { _position = value; - recalculateCollisionShape(); _dirtyFlags |= EntityItem::DIRTY_POSITION; } } @@ -1032,7 +1019,6 @@ void EntityItem::updatePositionInMeters(const glm::vec3& value) { glm::vec3 position = glm::clamp(value / (float) TREE_SCALE, 0.0f, 1.0f); if (glm::distance(_position, position) * (float)TREE_SCALE > MIN_POSITION_DELTA) { _position = position; - recalculateCollisionShape(); _dirtyFlags |= EntityItem::DIRTY_POSITION; } } @@ -1040,7 +1026,6 @@ void EntityItem::updatePositionInMeters(const glm::vec3& value) { void EntityItem::updateDimensions(const glm::vec3& value) { if (_dimensions != value) { _dimensions = glm::abs(value); - recalculateCollisionShape(); _dirtyFlags |= (EntityItem::DIRTY_SHAPE | EntityItem::DIRTY_MASS); } } @@ -1049,7 +1034,6 @@ void EntityItem::updateDimensionsInMeters(const glm::vec3& value) { glm::vec3 dimensions = glm::abs(value) / (float) TREE_SCALE; if (_dimensions != dimensions) { _dimensions = dimensions; - recalculateCollisionShape(); _dirtyFlags |= (EntityItem::DIRTY_SHAPE | EntityItem::DIRTY_MASS); } } @@ -1057,7 +1041,6 @@ void EntityItem::updateDimensionsInMeters(const glm::vec3& value) { void EntityItem::updateRotation(const glm::quat& rotation) { if (glm::dot(_rotation, rotation) < MIN_ALIGNMENT_DOT) { _rotation = rotation; - recalculateCollisionShape(); _dirtyFlags |= EntityItem::DIRTY_POSITION; } } diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 27aba8e148..a26783fc7c 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -16,7 +16,6 @@ #include -#include #include // for Animation, AnimationCache, and AnimationPointer classes #include #include // for EncodeBitstreamParams class @@ -150,7 +149,7 @@ public: glm::vec3 getPositionInMeters() const { return _position * (float) TREE_SCALE; } /// get position in meters /// set position in domain scale units (0.0 - 1.0) - void setPosition(const glm::vec3& value) { _position = value; recalculateCollisionShape(); } + void setPosition(const glm::vec3& value) { _position = value; } void setPositionInMeters(const glm::vec3& value) /// set position in meter units (0.0 - TREE_SCALE) { setPosition(glm::clamp(value / (float) TREE_SCALE, 0.0f, 1.0f)); } @@ -162,13 +161,13 @@ public: float getLargestDimension() const { return glm::length(_dimensions); } /// get the largest possible dimension /// set dimensions in domain scale units (0.0 - 1.0) this will also reset radius appropriately - virtual void setDimensions(const glm::vec3& value) { _dimensions = value; recalculateCollisionShape(); } + virtual void setDimensions(const glm::vec3& value) { _dimensions = value; } /// set dimensions in meter units (0.0 - TREE_SCALE) this will also reset radius appropriately void setDimensionsInMeters(const glm::vec3& value) { setDimensions(value / (float) TREE_SCALE); } const glm::quat& getRotation() const { return _rotation; } - void setRotation(const glm::quat& rotation) { _rotation = rotation; recalculateCollisionShape(); } + void setRotation(const glm::quat& rotation) { _rotation = rotation; } float getGlowLevel() const { return _glowLevel; } void setGlowLevel(float glowLevel) { _glowLevel = glowLevel; } @@ -225,7 +224,7 @@ public: /// registration point as ratio of entity void setRegistrationPoint(const glm::vec3& value) - { _registrationPoint = glm::clamp(value, 0.0f, 1.0f); recalculateCollisionShape(); } + { _registrationPoint = glm::clamp(value, 0.0f, 1.0f); } const glm::vec3& getAngularVelocity() const { return _angularVelocity; } void setAngularVelocity(const glm::vec3& value) { _angularVelocity = value; } @@ -254,11 +253,12 @@ public: // TODO: We need to get rid of these users of getRadius()... float getRadius() const; - void applyHardCollision(const CollisionInfo& collisionInfo); - virtual const Shape& getCollisionShapeInMeters() const { return _collisionShape; } virtual bool contains(const glm::vec3& point) const { return getAABox().contains(point); } virtual void computeShapeInfo(ShapeInfo& info) const; + /// return preferred shape type (actual physical shape may differ) + virtual ShapeType getShapeType() const { return SHAPE_TYPE_NONE; } + // updateFoo() methods to be used when changes need to be accumulated in the _dirtyFlags void updatePosition(const glm::vec3& value); void updatePositionInMeters(const glm::vec3& value); @@ -277,6 +277,7 @@ public: void updateIgnoreForCollisions(bool value); void updateCollisionsWillMove(bool value); void updateLifetime(float value); + virtual void updateShapeType(ShapeType type) { /* do nothing */ } uint32_t getDirtyFlags() const { return _dirtyFlags; } void clearDirtyFlags(uint32_t mask = 0xffff) { _dirtyFlags &= ~mask; } @@ -297,7 +298,6 @@ protected: static bool _sendPhysicsUpdates; virtual void initFromEntityItemID(const EntityItemID& entityItemID); // maybe useful to allow subclasses to init - virtual void recalculateCollisionShape(); EntityTypes::EntityType _type; QUuid _id; @@ -353,11 +353,9 @@ protected: /// set radius in domain scale units (0.0 - 1.0) this will also reset dimensions to be equal for each axis void setRadius(float value); - AACubeShape _collisionShape; - // _physicsInfo is a hook reserved for use by the EntitySimulation, which is guaranteed to set _physicsInfo // to a non-NULL value when the EntityItem has a representation in the physics engine. - void* _physicsInfo; // only set by EntitySimulation + void* _physicsInfo = NULL; // only set by EntitySimulation // DirtyFlags are set whenever a property changes that the EntitySimulation needs to know about. uint32_t _dirtyFlags; // things that have changed from EXTERNAL changes (via script or packet) but NOT from simulation diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 4da903f6ba..353aab0a64 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -64,6 +64,7 @@ EntityItemProperties::EntityItemProperties() : CONSTRUCT_PROPERTY(lineHeight, TextEntityItem::DEFAULT_LINE_HEIGHT), CONSTRUCT_PROPERTY(textColor, TextEntityItem::DEFAULT_TEXT_COLOR), CONSTRUCT_PROPERTY(backgroundColor, TextEntityItem::DEFAULT_BACKGROUND_COLOR), + CONSTRUCT_PROPERTY(shapeType, SHAPE_TYPE_NONE), _id(UNKNOWN_ENTITY_ID), _idSet(false), @@ -210,6 +211,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_LINE_HEIGHT, lineHeight); CHECK_PROPERTY_CHANGE(PROP_TEXT_COLOR, textColor); CHECK_PROPERTY_CHANGE(PROP_BACKGROUND_COLOR, backgroundColor); + CHECK_PROPERTY_CHANGE(PROP_SHAPE_TYPE, shapeType); return changedProperties; } @@ -268,6 +270,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine) cons COPY_PROPERTY_TO_QSCRIPTVALUE(lineHeight); COPY_PROPERTY_TO_QSCRIPTVALUE_COLOR_GETTER(textColor, getTextColor()); COPY_PROPERTY_TO_QSCRIPTVALUE_COLOR_GETTER(backgroundColor, getBackgroundColor()); + COPY_PROPERTY_TO_QSCRIPTVALUE(shapeType); // Sitting properties support QScriptValue sittingPoints = engine->newObject(); @@ -347,6 +350,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object) { COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(lineHeight, setLineHeight); COPY_PROPERTY_FROM_QSCRIPTVALUE_COLOR(textColor, setTextColor); COPY_PROPERTY_FROM_QSCRIPTVALUE_COLOR(backgroundColor, setBackgroundColor); + COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(shapeType, setShapeType, ShapeType); _lastEdited = usecTimestampNow(); } @@ -510,6 +514,7 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem APPEND_ENTITY_PROPERTY(PROP_ANIMATION_PLAYING, appendValue, properties.getAnimationIsPlaying()); APPEND_ENTITY_PROPERTY(PROP_TEXTURES, appendValue, properties.getTextures()); APPEND_ENTITY_PROPERTY(PROP_ANIMATION_SETTINGS, appendValue, properties.getAnimationSettings()); + APPEND_ENTITY_PROPERTY(PROP_SHAPE_TYPE, appendValue, (uint32_t)(properties.getShapeType())); } if (properties.getType() == EntityTypes::Light) { @@ -731,6 +736,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANIMATION_PLAYING, bool, setAnimationIsPlaying); READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_TEXTURES, setTextures); READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_ANIMATION_SETTINGS, setAnimationSettings); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SHAPE_TYPE, ShapeType, setShapeType); } if (properties.getType() == EntityTypes::Light) { @@ -820,6 +826,7 @@ void EntityItemProperties::markAllChanged() { _lineHeightChanged = true; _textColorChanged = true; _backgroundColorChanged = true; + _shapeTypeChanged = true; } AACube EntityItemProperties::getMaximumAACubeInTreeUnits() const { diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 8d4bf98862..4db6cd5d79 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -26,7 +26,7 @@ #include // for SittingPoint #include #include - +#include #include "EntityItemID.h" #include "EntityItemPropertiesMacros.h" @@ -82,8 +82,10 @@ enum EntityPropertyList { PROP_TEXTURES, PROP_ANIMATION_SETTINGS, PROP_USER_DATA, - - PROP_LAST_ITEM = PROP_USER_DATA, + PROP_SHAPE_TYPE, + + // NOTE: add new properties ABOVE this line and then modify PROP_LAST_ITEM below + PROP_LAST_ITEM = PROP_SHAPE_TYPE, // These properties of TextEntity piggy back off of properties of ModelEntities, the type doesn't matter // since the derived class knows how to interpret it's own properties and knows the types it expects @@ -179,6 +181,7 @@ public: DEFINE_PROPERTY(PROP_LINE_HEIGHT, LineHeight, lineHeight, float); DEFINE_PROPERTY_REF(PROP_TEXT_COLOR, TextColor, textColor, xColor); DEFINE_PROPERTY_REF(PROP_BACKGROUND_COLOR, BackgroundColor, backgroundColor, xColor); + DEFINE_PROPERTY_REF(PROP_SHAPE_TYPE, ShapeType, shapeType, ShapeType); public: float getMaxDimension() const { return glm::max(_dimensions.x, _dimensions.y, _dimensions.z); } diff --git a/libraries/entities/src/EntityItemPropertiesMacros.h b/libraries/entities/src/EntityItemPropertiesMacros.h index 9f86156422..518035c0d3 100644 --- a/libraries/entities/src/EntityItemPropertiesMacros.h +++ b/libraries/entities/src/EntityItemPropertiesMacros.h @@ -180,6 +180,15 @@ #define COPY_PROPERTY_TO_QSCRIPTVALUE(P) \ properties.setProperty(#P, _##P); +#define COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(P, S, E) \ + QScriptValue P = object.property(#P); \ + if (P.isValid()) { \ + E newValue = (E)(P.toVariant().toInt()); \ + if (_defaultSettings || newValue != _##P) { \ + S(newValue); \ + } \ + } + #define COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(P, S) \ QScriptValue P = object.property(#P); \ if (P.isValid()) { \ diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index aff6e64f57..ecd90e5942 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -11,9 +11,6 @@ #include -#include -#include - #include #include @@ -570,33 +567,6 @@ bool EntityTreeElement::findSpherePenetration(const glm::vec3& center, float rad return false; } -bool EntityTreeElement::findShapeCollisions(const Shape* shape, CollisionList& collisions) const { - bool atLeastOneCollision = false; - QList::iterator entityItr = _entityItems->begin(); - QList::const_iterator entityEnd = _entityItems->end(); - while(entityItr != entityEnd) { - EntityItem* entity = (*entityItr); - - // entities that are set for ignore for collisions then don't consider them for collision - const Shape* otherCollisionShape = &entity->getCollisionShapeInMeters(); - - bool ignoreForCollisions = entity->getIgnoreForCollisions(); - if (shape != otherCollisionShape && !ignoreForCollisions) { - if (ShapeCollider::collideShapes(shape, otherCollisionShape, collisions)) { - CollisionInfo* lastCollision = collisions.getLastCollision(); - if (lastCollision) { - lastCollision->_extraData = entity; - atLeastOneCollision = true; - } else { - qDebug() << "UNEXPECTED - ShapeCollider::collideShapes() returned true, but no lastCollision."; - } - } - } - ++entityItr; - } - return atLeastOneCollision; -} - void EntityTreeElement::updateEntityItemID(const EntityItemID& creatorTokenEntityID, const EntityItemID& knownIDEntityID) { uint16_t numberOfEntities = _entityItems->size(); for (uint16_t i = 0; i < numberOfEntities; i++) { diff --git a/libraries/entities/src/EntityTreeElement.h b/libraries/entities/src/EntityTreeElement.h index 4fbe9db323..0b28dd30d0 100644 --- a/libraries/entities/src/EntityTreeElement.h +++ b/libraries/entities/src/EntityTreeElement.h @@ -142,8 +142,6 @@ public: virtual bool findSpherePenetration(const glm::vec3& center, float radius, glm::vec3& penetration, void** penetratedObject) const; - virtual bool findShapeCollisions(const Shape* shape, CollisionList& collisions) const; - const QList& getEntities() const { return *_entityItems; } QList& getEntities() { return *_entityItems; } bool hasEntities() const { return _entityItems ? _entityItems->size() > 0 : false; } diff --git a/libraries/entities/src/LightEntityItem.cpp b/libraries/entities/src/LightEntityItem.cpp index c125364e63..4a8dc2d582 100644 --- a/libraries/entities/src/LightEntityItem.cpp +++ b/libraries/entities/src/LightEntityItem.cpp @@ -42,16 +42,11 @@ LightEntityItem::LightEntityItem(const EntityItemID& entityItemID, const EntityI _cutoff = PI; setProperties(properties); - - // a light is not collide-able so we make it's shape be a tiny sphere at origin - _emptyShape.setTranslation(glm::vec3(0.0f, 0.0f, 0.0f)); - _emptyShape.setRadius(0.0f); } void LightEntityItem::setDimensions(const glm::vec3& value) { float maxDimension = glm::max(value.x, value.y, value.z); _dimensions = glm::vec3(maxDimension, maxDimension, maxDimension); - recalculateCollisionShape(); } diff --git a/libraries/entities/src/LightEntityItem.h b/libraries/entities/src/LightEntityItem.h index 249a4c8472..5e39ba26a2 100644 --- a/libraries/entities/src/LightEntityItem.h +++ b/libraries/entities/src/LightEntityItem.h @@ -12,7 +12,6 @@ #ifndef hifi_LightEntityItem_h #define hifi_LightEntityItem_h -#include #include "EntityItem.h" class LightEntityItem : public EntityItem { @@ -98,13 +97,10 @@ public: float getCutoff() const { return _cutoff; } void setCutoff(float value) { _cutoff = value; } - virtual const Shape& getCollisionShapeInMeters() const { return _emptyShape; } - static bool getLightsArePickable() { return _lightsArePickable; } static void setLightsArePickable(bool value) { _lightsArePickable = value; } protected: - virtual void recalculateCollisionShape() { /* nothing to do */ } // properties of a light rgbColor _ambientColor; @@ -117,9 +113,6 @@ protected: float _exponent; float _cutoff; - // used for collision detection - SphereShape _emptyShape; - static bool _lightsArePickable; }; diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp index bfab8871a5..4d8e741cc6 100644 --- a/libraries/entities/src/ModelEntityItem.cpp +++ b/libraries/entities/src/ModelEntityItem.cpp @@ -51,6 +51,7 @@ EntityItemProperties ModelEntityItem::getProperties() const { COPY_ENTITY_PROPERTY_TO_PROPERTIES(glowLevel, getGlowLevel); COPY_ENTITY_PROPERTY_TO_PROPERTIES(textures, getTextures); COPY_ENTITY_PROPERTY_TO_PROPERTIES(animationSettings, getAnimationSettings); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(shapeType, getShapeType); return properties; } @@ -66,6 +67,7 @@ bool ModelEntityItem::setProperties(const EntityItemProperties& properties) { SET_ENTITY_PROPERTY_FROM_PROPERTIES(animationFPS, setAnimationFPS); SET_ENTITY_PROPERTY_FROM_PROPERTIES(textures, setTextures); SET_ENTITY_PROPERTY_FROM_PROPERTIES(animationSettings, setAnimationSettings); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(shapeType, updateShapeType); if (somethingChanged) { bool wantDebug = false; @@ -116,6 +118,7 @@ int ModelEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, READ_ENTITY_PROPERTY_STRING(PROP_TEXTURES, setTextures); READ_ENTITY_PROPERTY_STRING(PROP_ANIMATION_SETTINGS, setAnimationSettings); + READ_ENTITY_PROPERTY_SETTER(PROP_SHAPE_TYPE, ShapeType, updateShapeType); return bytesRead; } @@ -131,6 +134,7 @@ EntityPropertyFlags ModelEntityItem::getEntityProperties(EncodeBitstreamParams& requestedProperties += PROP_ANIMATION_PLAYING; requestedProperties += PROP_ANIMATION_SETTINGS; requestedProperties += PROP_TEXTURES; + requestedProperties += PROP_SHAPE_TYPE; return requestedProperties; } @@ -153,6 +157,7 @@ void ModelEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit APPEND_ENTITY_PROPERTY(PROP_ANIMATION_PLAYING, appendValue, getAnimationIsPlaying()); APPEND_ENTITY_PROPERTY(PROP_TEXTURES, appendValue, getTextures()); APPEND_ENTITY_PROPERTY(PROP_ANIMATION_SETTINGS, appendValue, getAnimationSettings()); + APPEND_ENTITY_PROPERTY(PROP_SHAPE_TYPE, appendValue, (uint32_t)getShapeType()); } @@ -245,21 +250,6 @@ bool ModelEntityItem::needsToCallUpdate() const { return isAnimatingSomething() ? true : EntityItem::needsToCallUpdate(); } -void ModelEntityItem::computeShapeInfo(ShapeInfo& info) const { - // HACK: Default first first approximation is to boxify the entity... but only if it is small enough. - // The limit here is chosen to something that most avatars could not comfortably fit inside - // to prevent houses from getting boxified... we don't want the things inside houses to - // collide with a house as if it were a giant solid block. - const float MAX_SIZE_FOR_BOXIFICATION_HACK = 3.0f; - float diagonal = glm::length(getDimensionsInMeters()); - if (diagonal < MAX_SIZE_FOR_BOXIFICATION_HACK) { - glm::vec3 halfExtents = 0.5f * getDimensionsInMeters(); - info.setBox(halfExtents); - } else { - info.clear(); - } -} - void ModelEntityItem::update(const quint64& now) { // only advance the frame index if we're playing if (getAnimationIsPlaying()) { @@ -280,6 +270,13 @@ void ModelEntityItem::debugDump() const { qDebug() << " model URL:" << getModelURL(); } +void ModelEntityItem::updateShapeType(ShapeType type) { + if (type != _shapeType) { + _shapeType = type; + _dirtyFlags |= EntityItem::DIRTY_SHAPE | EntityItem::DIRTY_MASS; + } +} + void ModelEntityItem::setAnimationURL(const QString& url) { _dirtyFlags |= EntityItem::DIRTY_UPDATEABLE; _animationURL = url; diff --git a/libraries/entities/src/ModelEntityItem.h b/libraries/entities/src/ModelEntityItem.h index 3db2a40db0..94d262fc9f 100644 --- a/libraries/entities/src/ModelEntityItem.h +++ b/libraries/entities/src/ModelEntityItem.h @@ -46,9 +46,10 @@ public: virtual void update(const quint64& now); virtual bool needsToCallUpdate() const; - void computeShapeInfo(ShapeInfo& info) const; virtual void debugDump() const; + void updateShapeType(ShapeType type); + virtual ShapeType getShapeType() const { return _shapeType; } // TODO: Move these to subclasses, or other appropriate abstraction // getters/setters applicable to models and particles @@ -126,6 +127,7 @@ protected: AnimationLoop _animationLoop; QString _animationSettings; QString _textures; + ShapeType _shapeType = SHAPE_TYPE_NONE; // used on client side bool _jointMappingCompleted; diff --git a/libraries/entities/src/SphereEntityItem.cpp b/libraries/entities/src/SphereEntityItem.cpp index 181e5851f6..7d5fb83a4e 100644 --- a/libraries/entities/src/SphereEntityItem.cpp +++ b/libraries/entities/src/SphereEntityItem.cpp @@ -94,19 +94,6 @@ void SphereEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBi APPEND_ENTITY_PROPERTY(PROP_COLOR, appendColor, getColor()); } -void SphereEntityItem::recalculateCollisionShape() { - _sphereShape.setTranslation(getCenterInMeters()); - glm::vec3 dimensionsInMeters = getDimensionsInMeters(); - float largestDiameter = glm::max(dimensionsInMeters.x, dimensionsInMeters.y, dimensionsInMeters.z); - _sphereShape.setRadius(largestDiameter / 2.0f); -} - -void SphereEntityItem::computeShapeInfo(ShapeInfo& info) const { - glm::vec3 halfExtents = 0.5f * getDimensionsInMeters(); - // TODO: support ellipsoid shapes - info.setSphere(halfExtents.x); -} - bool SphereEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face, void** intersectedObject, bool precisionPicking) const { diff --git a/libraries/entities/src/SphereEntityItem.h b/libraries/entities/src/SphereEntityItem.h index f76c9f5600..f79a2db7ff 100644 --- a/libraries/entities/src/SphereEntityItem.h +++ b/libraries/entities/src/SphereEntityItem.h @@ -12,7 +12,6 @@ #ifndef hifi_SphereEntityItem_h #define hifi_SphereEntityItem_h -#include #include "EntityItem.h" class SphereEntityItem : public EntityItem { @@ -51,12 +50,10 @@ public: _color[BLUE_INDEX] = value.blue; } - virtual const Shape& getCollisionShapeInMeters() const { return _sphereShape; } - // TODO: implement proper contains for 3D ellipsoid //virtual bool contains(const glm::vec3& point) const; - void computeShapeInfo(ShapeInfo& info) const; + virtual ShapeType getShapeType() const { return SHAPE_TYPE_SPHERE; } virtual bool supportsDetailedRayIntersection() const { return true; } virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, @@ -66,10 +63,8 @@ public: virtual void debugDump() const; protected: - virtual void recalculateCollisionShape(); rgbColor _color; - SphereShape _sphereShape; }; #endif // hifi_SphereEntityItem_h diff --git a/libraries/entities/src/TextEntityItem.cpp b/libraries/entities/src/TextEntityItem.cpp index c43638f827..ac9a080119 100644 --- a/libraries/entities/src/TextEntityItem.cpp +++ b/libraries/entities/src/TextEntityItem.cpp @@ -44,7 +44,6 @@ void TextEntityItem::setDimensions(const glm::vec3& value) { // NOTE: Text Entities always have a "depth" of 1cm. float fixedDepth = 0.01f / (float)TREE_SCALE; _dimensions = glm::vec3(value.x, value.y, fixedDepth); - recalculateCollisionShape(); } EntityItemProperties TextEntityItem::getProperties() const { diff --git a/libraries/entities/src/TextEntityItem.h b/libraries/entities/src/TextEntityItem.h index 1f0bd7d85e..d57b5442d6 100644 --- a/libraries/entities/src/TextEntityItem.h +++ b/libraries/entities/src/TextEntityItem.h @@ -24,6 +24,7 @@ public: /// set dimensions in domain scale units (0.0 - 1.0) this will also reset radius appropriately virtual void setDimensions(const glm::vec3& value); + virtual ShapeType getShapeType() const { return SHAPE_TYPE_BOX; } // methods for getting/setting all properties of an entity virtual EntityItemProperties getProperties() const; diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index b79520f99d..72d153289a 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include "FBXReader.h" @@ -1951,7 +1952,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping, joint.inverseBindRotation = joint.inverseDefaultRotation; joint.name = model.name; joint.shapePosition = glm::vec3(0.0f); - joint.shapeType = SHAPE_TYPE_UNKNOWN; + joint.shapeType = INVALID_SHAPE; foreach (const QString& childID, childMap.values(modelID)) { QString type = typeFlags.value(childID); @@ -2375,10 +2376,10 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping, if (collideLikeCapsule) { joint.shapeRotation = rotationBetween(defaultCapsuleAxis, jointShapeInfo.boneBegin); joint.shapePosition = 0.5f * jointShapeInfo.boneBegin; - joint.shapeType = SHAPE_TYPE_CAPSULE; + joint.shapeType = CAPSULE_SHAPE; } else { // collide the joint like a sphere - joint.shapeType = SHAPE_TYPE_SPHERE; + joint.shapeType = SPHERE_SHAPE; if (jointShapeInfo.numVertices > 0) { jointShapeInfo.averageVertex /= (float)jointShapeInfo.numVertices; joint.shapePosition = jointShapeInfo.averageVertex; @@ -2398,8 +2399,8 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping, if (distanceFromEnd > joint.distanceToParent && distanceFromBegin > joint.distanceToParent) { // The shape is further from both joint endpoints than the endpoints are from each other // which probably means the model has a bad transform somewhere. We disable this shape - // by setting its type to SHAPE_TYPE_UNKNOWN. - joint.shapeType = SHAPE_TYPE_UNKNOWN; + // by setting its type to INVALID_SHAPE. + joint.shapeType = INVALID_SHAPE; } } } diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h index b706549c3e..3f6f836950 100644 --- a/libraries/fbx/src/FBXReader.h +++ b/libraries/fbx/src/FBXReader.h @@ -18,12 +18,13 @@ #include #include -#include -#include - #include #include +#include +#include +#include + #include #include @@ -53,12 +54,6 @@ public: QVector normals; }; -enum ShapeType { - SHAPE_TYPE_SPHERE = 0, - SHAPE_TYPE_CAPSULE = 1, - SHAPE_TYPE_UNKNOWN = 2 -}; - /// A single joint (transformation node) extracted from an FBX document. class FBXJoint { public: @@ -83,7 +78,7 @@ public: QString name; glm::vec3 shapePosition; // in joint frame glm::quat shapeRotation; // in joint frame - ShapeType shapeType; + quint8 shapeType; bool isSkeletonJoint; }; diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index 5fa6d43b9a..13bb2b1ad8 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -36,7 +36,7 @@ const char SOLO_NODE_TYPES[2] = { NodeType::AudioMixer }; -const QUrl DEFAULT_NODE_AUTH_URL = QUrl("https://data.highfidelity.io"); +const QUrl DEFAULT_NODE_AUTH_URL = QUrl("https://metaverse.highfidelity.io"); LimitedNodeList::LimitedNodeList(unsigned short socketListenPort, unsigned short dtlsListenPort) : _sessionUUID(), diff --git a/libraries/networking/src/PacketHeaders.cpp b/libraries/networking/src/PacketHeaders.cpp index d2806f3db5..2eee540fff 100644 --- a/libraries/networking/src/PacketHeaders.cpp +++ b/libraries/networking/src/PacketHeaders.cpp @@ -72,7 +72,7 @@ PacketVersion versionForPacketType(PacketType type) { return 1; case PacketTypeEntityAddOrEdit: case PacketTypeEntityData: - return VERSION_ENTITIES_HAS_LAST_SIMULATED_TIME; + return VERSION_MODEL_ENTITIES_SUPPORT_SHAPE_TYPE; case PacketTypeEntityErase: return 2; case PacketTypeAudioStreamStats: diff --git a/libraries/networking/src/PacketHeaders.h b/libraries/networking/src/PacketHeaders.h index f0d21ca9f8..e593955b51 100644 --- a/libraries/networking/src/PacketHeaders.h +++ b/libraries/networking/src/PacketHeaders.h @@ -127,6 +127,7 @@ const PacketVersion VERSION_ENTITIES_SUPPORT_DIMENSIONS = 4; const PacketVersion VERSION_ENTITIES_MODELS_HAVE_ANIMATION_SETTINGS = 5; const PacketVersion VERSION_ENTITIES_HAVE_USER_DATA = 6; const PacketVersion VERSION_ENTITIES_HAS_LAST_SIMULATED_TIME = 7; +const PacketVersion VERSION_MODEL_ENTITIES_SUPPORT_SHAPE_TYPE = 8; const PacketVersion VERSION_OCTREE_HAS_FILE_BREAKS = 1; #endif // hifi_PacketHeaders_h diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index 9abf0ec7a1..e925ea5aba 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -27,7 +27,6 @@ #include #include #include -#include #include "CoverageMap.h" #include "OctreeConstants.h" @@ -856,26 +855,6 @@ bool findCapsulePenetrationOp(OctreeElement* element, void* extraData) { return false; } -bool findShapeCollisionsOp(OctreeElement* element, void* extraData) { - ShapeArgs* args = static_cast(extraData); - // coarse check against bounds - AACube cube = element->getAACube(); - cube.scale(TREE_SCALE); - if (!cube.expandedContains(args->shape->getTranslation(), args->shape->getBoundingRadius())) { - return false; - } - if (element->hasContent()) { - if (element->findShapeCollisions(args->shape, args->collisions)) { - args->found = true; - return true; - } - } - if (!element->isLeaf()) { - return true; // recurse on children - } - return false; -} - uint qHash(const glm::vec3& point) { // NOTE: TREE_SCALE = 16384 (15 bits) and multiplier is 1024 (11 bits), // so each component (26 bits) uses more than its alloted 21 bits. @@ -947,37 +926,6 @@ bool Octree::findCapsulePenetration(const glm::vec3& start, const glm::vec3& end return args.found; } -bool Octree::findShapeCollisions(const Shape* shape, CollisionList& collisions, - Octree::lockType lockType, bool* accurateResult) { - - ShapeArgs args = { shape, collisions, false }; - - bool gotLock = false; - if (lockType == Octree::Lock) { - lockForRead(); - gotLock = true; - } else if (lockType == Octree::TryLock) { - gotLock = tryLockForRead(); - if (!gotLock) { - if (accurateResult) { - *accurateResult = false; // if user asked to accuracy or result, let them know this is inaccurate - } - return args.found; // if we wanted to tryLock, and we couldn't then just bail... - } - } - - recurseTreeWithOperation(findShapeCollisionsOp, &args); - - if (gotLock) { - unlock(); - } - - if (accurateResult) { - *accurateResult = true; // if user asked to accuracy or result, let them know this is accurate - } - return args.found; -} - bool Octree::findContentInCube(const AACube& cube, CubeList& cubes) { if (!tryLockForRead()) { return false; diff --git a/libraries/octree/src/Octree.h b/libraries/octree/src/Octree.h index cde8565ca2..870e18983e 100644 --- a/libraries/octree/src/Octree.h +++ b/libraries/octree/src/Octree.h @@ -308,9 +308,6 @@ public: bool findCapsulePenetration(const glm::vec3& start, const glm::vec3& end, float radius, glm::vec3& penetration, Octree::lockType lockType = Octree::TryLock, bool* accurateResult = NULL); - bool findShapeCollisions(const Shape* shape, CollisionList& collisions, - Octree::lockType = Octree::TryLock, bool* accurateResult = NULL); - bool findContentInCube(const AACube& cube, CubeList& cubes); OctreeElement* getElementEnclosingPoint(const glm::vec3& point, diff --git a/libraries/octree/src/OctreeElement.cpp b/libraries/octree/src/OctreeElement.cpp index e74b6619bd..93792678e2 100644 --- a/libraries/octree/src/OctreeElement.cpp +++ b/libraries/octree/src/OctreeElement.cpp @@ -20,7 +20,6 @@ #include #include #include -#include #include "AACube.h" #include "OctalCode.h" @@ -1396,12 +1395,6 @@ bool OctreeElement::findSpherePenetration(const glm::vec3& center, float radius, return _cube.findSpherePenetration(center, radius, penetration); } -bool OctreeElement::findShapeCollisions(const Shape* shape, CollisionList& collisions) const { - AACube cube = getAACube(); - cube.scale(TREE_SCALE); - return ShapeCollider::collideShapeWithAACubeLegacy(shape, cube.calcCenter(), cube.getScale(), collisions); -} - // TODO: consider removing this, or switching to using getOrCreateChildElementContaining(const AACube& box)... OctreeElement* OctreeElement::getOrCreateChildElementAt(float x, float y, float z, float s) { OctreeElement* child = NULL; diff --git a/libraries/octree/src/OctreeElement.h b/libraries/octree/src/OctreeElement.h index 434342372f..8c83d9976e 100644 --- a/libraries/octree/src/OctreeElement.h +++ b/libraries/octree/src/OctreeElement.h @@ -128,8 +128,6 @@ public: virtual bool findSpherePenetration(const glm::vec3& center, float radius, glm::vec3& penetration, void** penetratedObject) const; - virtual bool findShapeCollisions(const Shape* shape, CollisionList& collisions) const; - // Base class methods you don't need to implement const unsigned char* getOctalCode() const { return (_octcodePointer) ? _octalCode.pointer : &_octalCode.buffer[0]; } OctreeElement* getChildAtIndex(int childIndex) const; diff --git a/libraries/physics/src/ShapeInfoUtil.cpp b/libraries/physics/src/ShapeInfoUtil.cpp index 0e9dc55a8c..e98bb07052 100644 --- a/libraries/physics/src/ShapeInfoUtil.cpp +++ b/libraries/physics/src/ShapeInfoUtil.cpp @@ -9,7 +9,6 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include // for FOO_SHAPE types #include // for MILLIMETERS_PER_METER #include "ShapeInfoUtil.h" @@ -18,36 +17,30 @@ int ShapeInfoUtil::toBulletShapeType(int shapeInfoType) { int bulletShapeType = INVALID_SHAPE_PROXYTYPE; switch(shapeInfoType) { - case BOX_SHAPE: + case SHAPE_TYPE_BOX: bulletShapeType = BOX_SHAPE_PROXYTYPE; break; - case SPHERE_SHAPE: + case SHAPE_TYPE_SPHERE: bulletShapeType = SPHERE_SHAPE_PROXYTYPE; break; - case CAPSULE_SHAPE: + case SHAPE_TYPE_CAPSULE_Y: bulletShapeType = CAPSULE_SHAPE_PROXYTYPE; break; - case CYLINDER_SHAPE: - bulletShapeType = CYLINDER_SHAPE_PROXYTYPE; - break; } return bulletShapeType; } int ShapeInfoUtil::fromBulletShapeType(int bulletShapeType) { - int shapeInfoType = INVALID_SHAPE; + int shapeInfoType = SHAPE_TYPE_NONE; switch(bulletShapeType) { case BOX_SHAPE_PROXYTYPE: - shapeInfoType = BOX_SHAPE; + shapeInfoType = SHAPE_TYPE_BOX; break; case SPHERE_SHAPE_PROXYTYPE: - shapeInfoType = SPHERE_SHAPE; + shapeInfoType = SHAPE_TYPE_SPHERE; break; case CAPSULE_SHAPE_PROXYTYPE: - shapeInfoType = CAPSULE_SHAPE; - break; - case CYLINDER_SHAPE_PROXYTYPE: - shapeInfoType = CYLINDER_SHAPE; + shapeInfoType = SHAPE_TYPE_CAPSULE_Y; break; } return shapeInfoType; @@ -57,29 +50,16 @@ void ShapeInfoUtil::collectInfoFromShape(const btCollisionShape* shape, ShapeInf if (shape) { int type = ShapeInfoUtil::fromBulletShapeType(shape->getShapeType()); switch(type) { - case BOX_SHAPE: { + case SHAPE_TYPE_BOX: { const btBoxShape* boxShape = static_cast(shape); info.setBox(bulletToGLM(boxShape->getHalfExtentsWithMargin())); } break; - case SPHERE_SHAPE: { + case SHAPE_TYPE_SPHERE: { const btSphereShape* sphereShape = static_cast(shape); info.setSphere(sphereShape->getRadius()); } break; - case CYLINDER_SHAPE: { - // NOTE: we only support cylinders along yAxis - const btCylinderShape* cylinderShape = static_cast(shape); - btVector3 halfExtents = cylinderShape->getHalfExtentsWithMargin(); - info.setCylinder(halfExtents.getX(), halfExtents.getY()); - } - break; - case CAPSULE_SHAPE: { - // NOTE: we only support capsules along yAxis - const btCapsuleShape* capsuleShape = static_cast(shape); - info.setCapsule(capsuleShape->getRadius(), capsuleShape->getHalfHeight()); - } - break; default: info.clear(); break; @@ -91,77 +71,23 @@ void ShapeInfoUtil::collectInfoFromShape(const btCollisionShape* shape, ShapeInf btCollisionShape* ShapeInfoUtil::createShapeFromInfo(const ShapeInfo& info) { btCollisionShape* shape = NULL; - const QVector& data = info.getData(); switch(info.getType()) { - case BOX_SHAPE: { - // data[0] is halfExtents - shape = new btBoxShape(glmToBullet(data[0])); + case SHAPE_TYPE_BOX: { + shape = new btBoxShape(glmToBullet(info.getHalfExtents())); } break; - case SPHERE_SHAPE: { - float radius = data[0].z; + case SHAPE_TYPE_SPHERE: { + float radius = info.getHalfExtents().x; shape = new btSphereShape(radius); } break; - case CYLINDER_SHAPE: { - // NOTE: default cylinder has (UpAxis = 1) axis along yAxis and radius stored in X - // data[0] = btVector3(radius, halfHeight, unused) - shape = new btCylinderShape(glmToBullet(data[0])); - } - break; - case CAPSULE_SHAPE: { - float radius = data[0].x; - float height = 2.0f * data[0].y; + case SHAPE_TYPE_CAPSULE_Y: { + glm::vec3 halfExtents = info.getHalfExtents(); + float radius = halfExtents.x; + float height = 2.0f * halfExtents.y; shape = new btCapsuleShape(radius, height); } break; } return shape; } - -DoubleHashKey ShapeInfoUtil::computeHash(const ShapeInfo& info) { - DoubleHashKey key; - // compute hash - // scramble the bits of the type - // TODO?: provide lookup table for hash of info._type rather than recompute? - int primeIndex = 0; - unsigned int hash = DoubleHashKey::hashFunction((unsigned int)info.getType(), primeIndex++); - const QVector& data = info.getData(); - - glm::vec3 tmpData; - int numData = data.size(); - for (int i = 0; i < numData; ++i) { - tmpData = data[i]; - for (int j = 0; j < 3; ++j) { - // NOTE: 0.49f is used to bump the float up almost half a millimeter - // so the cast to int produces a round() effect rather than a floor() - unsigned int floatHash = - DoubleHashKey::hashFunction((int)(tmpData[j] * MILLIMETERS_PER_METER + copysignf(1.0f, tmpData[j]) * 0.49f), primeIndex++); - hash ^= floatHash; - } - } - key._hash = (int)hash; - - // compute hash2 - // scramble the bits of the type - // TODO?: provide lookup table for hash2 of info._type rather than recompute? - hash = DoubleHashKey::hashFunction2((unsigned int)info.getType()); - - for (int i = 0; i < numData; ++i) { - tmpData = data[i]; - for (int j = 0; j < 3; ++j) { - // NOTE: 0.49f is used to bump the float up almost half a millimeter - // so the cast to int produces a round() effect rather than a floor() - unsigned int floatHash = - DoubleHashKey::hashFunction2((int)(tmpData[j] * MILLIMETERS_PER_METER + copysignf(1.0f, tmpData[j]) * 0.49f)); - hash += ~(floatHash << 17); - hash ^= (floatHash >> 11); - hash += (floatHash << 4); - hash ^= (floatHash >> 7); - hash += ~(floatHash << 10); - hash = (hash << 16) | (hash >> 16); - } - } - key._hash2 = (int)hash; - return key; -} diff --git a/libraries/physics/src/ShapeInfoUtil.h b/libraries/physics/src/ShapeInfoUtil.h index 438e3df715..fb59f30c69 100644 --- a/libraries/physics/src/ShapeInfoUtil.h +++ b/libraries/physics/src/ShapeInfoUtil.h @@ -17,8 +17,6 @@ #include -#include "DoubleHashKey.h" - // translates between ShapeInfo and btShape namespace ShapeInfoUtil { @@ -26,8 +24,6 @@ namespace ShapeInfoUtil { btCollisionShape* createShapeFromInfo(const ShapeInfo& info); - DoubleHashKey computeHash(const ShapeInfo& info); - // TODO? just use bullet shape types everywhere? int toBulletShapeType(int shapeInfoType); int fromBulletShapeType(int bulletShapeType); diff --git a/libraries/physics/src/ShapeManager.cpp b/libraries/physics/src/ShapeManager.cpp index b153150ce6..02228bf7aa 100644 --- a/libraries/physics/src/ShapeManager.cpp +++ b/libraries/physics/src/ShapeManager.cpp @@ -27,14 +27,17 @@ ShapeManager::~ShapeManager() { } btCollisionShape* ShapeManager::getShape(const ShapeInfo& info) { + if (info.getType() == SHAPE_TYPE_NONE) { + return NULL; + } // Very small or large objects are not supported. - float diagonal = glm::length2(info.getBoundingBoxDiagonal()); + float diagonal = 4.0f * glm::length2(info.getHalfExtents()); const float MIN_SHAPE_DIAGONAL_SQUARED = 3.0e-4f; // 1 cm cube const float MAX_SHAPE_DIAGONAL_SQUARED = 3.0e4f; // 100 m cube if (diagonal < MIN_SHAPE_DIAGONAL_SQUARED || diagonal > MAX_SHAPE_DIAGONAL_SQUARED) { return NULL; } - DoubleHashKey key = ShapeInfoUtil::computeHash(info); + DoubleHashKey key = info.getHash(); ShapeReference* shapeRef = _shapeMap.find(key); if (shapeRef) { shapeRef->_refCount++; @@ -51,7 +54,7 @@ btCollisionShape* ShapeManager::getShape(const ShapeInfo& info) { } bool ShapeManager::releaseShape(const ShapeInfo& info) { - DoubleHashKey key = ShapeInfoUtil::computeHash(info); + DoubleHashKey key = info.getHash(); ShapeReference* shapeRef = _shapeMap.find(key); if (shapeRef) { if (shapeRef->_refCount > 0) { @@ -95,7 +98,7 @@ void ShapeManager::collectGarbage() { } int ShapeManager::getNumReferences(const ShapeInfo& info) const { - DoubleHashKey key = ShapeInfoUtil::computeHash(info); + DoubleHashKey key = info.getHash(); const ShapeReference* shapeRef = _shapeMap.find(key); if (shapeRef) { return shapeRef->_refCount; diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index 3258571f06..dffadc730f 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -999,12 +999,12 @@ void GeometryCache::renderBevelCornersRect(int x, int y, int width, int height, void GeometryCache::renderQuad(const glm::vec2& minCorner, const glm::vec2& maxCorner, const glm::vec4& color, int id) { bool registered = (id != UNKNOWN_ID); - Vec2Pair key(minCorner, maxCorner); + Vec4Pair key(glm::vec4(minCorner.x, minCorner.y, maxCorner.x, maxCorner.y), color); BatchItemDetails& details = registered ? _registeredQuad2D[id] : _quad2D[key]; // if this is a registered quad, and we have buffers, then check to see if the geometry changed and rebuild if needed if (registered && details.isCreated) { - Vec2Pair& lastKey = _lastRegisteredQuad2D[id]; + Vec4Pair & lastKey = _lastRegisteredQuad2D[id]; if (lastKey != key) { details.clear(); _lastRegisteredQuad2D[id] = key; @@ -1082,12 +1082,14 @@ void GeometryCache::renderQuad(const glm::vec2& minCorner, const glm::vec2& maxC const glm::vec4& color, int id) { bool registered = (id != UNKNOWN_ID); - Vec2PairPair key(Vec2Pair(minCorner, maxCorner), Vec2Pair(texCoordMinCorner, texCoordMaxCorner)); + Vec4PairVec4 key(Vec4Pair(glm::vec4(minCorner.x, minCorner.y, maxCorner.x, maxCorner.y), + glm::vec4(texCoordMinCorner.x, texCoordMinCorner.y, texCoordMaxCorner.x, texCoordMaxCorner.y)), + color); BatchItemDetails& details = registered ? _registeredQuad2DTextures[id] : _quad2DTextures[key]; // if this is a registered quad, and we have buffers, then check to see if the geometry changed and rebuild if needed if (registered && details.isCreated) { - Vec2PairPair& lastKey = _lastRegisteredQuad2DTexture[id]; + Vec4PairVec4& lastKey = _lastRegisteredQuad2DTexture[id]; if (lastKey != key) { details.clear(); _lastRegisteredQuad2DTexture[id] = key; @@ -1172,12 +1174,12 @@ void GeometryCache::renderQuad(const glm::vec2& minCorner, const glm::vec2& maxC void GeometryCache::renderQuad(const glm::vec3& minCorner, const glm::vec3& maxCorner, const glm::vec4& color, int id) { bool registered = (id != UNKNOWN_ID); - Vec3Pair key(minCorner, maxCorner); + Vec3PairVec4 key(Vec3Pair(minCorner, maxCorner), color); BatchItemDetails& details = registered ? _registeredQuad3D[id] : _quad3D[key]; // if this is a registered quad, and we have buffers, then check to see if the geometry changed and rebuild if needed if (registered && details.isCreated) { - Vec3Pair& lastKey = _lastRegisteredQuad3D[id]; + Vec3PairVec4& lastKey = _lastRegisteredQuad3D[id]; if (lastKey != key) { details.clear(); _lastRegisteredQuad3D[id] = key; diff --git a/libraries/render-utils/src/GeometryCache.h b/libraries/render-utils/src/GeometryCache.h index 04cea4f9f8..864c5ff24c 100644 --- a/libraries/render-utils/src/GeometryCache.h +++ b/libraries/render-utils/src/GeometryCache.h @@ -41,7 +41,9 @@ typedef QPair Vec2PairPair; typedef QPair Vec3Pair; typedef QPair Vec4Pair; typedef QPair Vec3PairVec2Pair; +typedef QPair Vec3PairVec4; typedef QPair Vec3PairVec4Pair; +typedef QPair Vec4PairVec4; typedef QPair Vec4PairVec4Pair; inline uint qHash(const glm::vec2& v, uint seed) { @@ -87,6 +89,14 @@ inline uint qHash(const Vec3PairVec2Pair& v, uint seed) { 5077 * v.second.second.x + 5081 * v.second.second.y, seed); } +inline uint qHash(const Vec3PairVec4& v, uint seed) { + // multiply by prime numbers greater than the possible size + return qHash(v.first.first.x + 5009 * v.first.first.y + 5011 * v.first.first.z + + 5021 * v.first.second.x + 5023 * v.first.second.y + 5039 * v.first.second.z + + 5051 * v.second.x + 5059 * v.second.y + 5077 * v.second.z + 5081 * v.second.w, seed); +} + + inline uint qHash(const Vec3PairVec4Pair& v, uint seed) { // multiply by prime numbers greater than the possible size return qHash(v.first.first.x + 5009 * v.first.first.y + 5011 * v.first.first.z @@ -96,6 +106,14 @@ inline uint qHash(const Vec3PairVec4Pair& v, uint seed) { seed); } +inline uint qHash(const Vec4PairVec4& v, uint seed) { + // multiply by prime numbers greater than the possible size + return qHash(v.first.first.x + 5009 * v.first.first.y + 5011 * v.first.first.z + 5021 * v.first.first.w + + 5023 * v.first.second.x + 5039 * v.first.second.y + 5051 * v.first.second.z + 5059 * v.first.second.w + + 5077 * v.second.x + 5081 * v.second.y + 5087 * v.second.z + 5099 * v.second.w, + seed); +} + inline uint qHash(const Vec4PairVec4Pair& v, uint seed) { // multiply by prime numbers greater than the possible size return qHash(v.first.first.x + 5009 * v.first.first.y + 5011 * v.first.first.z + 5021 * v.first.first.w @@ -236,16 +254,16 @@ private: QHash _quad3DTextures; QHash _registeredQuad3DTextures; - QHash _lastRegisteredQuad2DTexture; - QHash _quad2DTextures; + QHash _lastRegisteredQuad2DTexture; + QHash _quad2DTextures; QHash _registeredQuad2DTextures; - QHash _lastRegisteredQuad3D; - QHash _quad3D; + QHash _lastRegisteredQuad3D; + QHash _quad3D; QHash _registeredQuad3D; - QHash _lastRegisteredQuad2D; - QHash _quad2D; + QHash _lastRegisteredQuad2D; + QHash _quad2D; QHash _registeredQuad2D; QHash _lastRegisteredBevelRects; diff --git a/libraries/render-utils/src/TextRenderer.cpp b/libraries/render-utils/src/TextRenderer.cpp index 5a1d09a148..fb720b3040 100644 --- a/libraries/render-utils/src/TextRenderer.cpp +++ b/libraries/render-utils/src/TextRenderer.cpp @@ -136,10 +136,12 @@ public: glm::vec2 drawString(float x, float y, const QString & str, const glm::vec4& color, TextRenderer::EffectType effectType, - const glm::vec2& bound) const; + const glm::vec2& bound); private: QStringList tokenizeForWrapping(const QString & str) const; + + bool _initialized; }; static QHash LOADED_FONTS; @@ -186,7 +188,7 @@ Font* loadFont(const QString& family) { return LOADED_FONTS[family]; } -Font::Font() { +Font::Font() : _initialized(false) { static bool fontResourceInitComplete = false; if (!fontResourceInitComplete) { Q_INIT_RESOURCE(fonts); @@ -255,8 +257,6 @@ void Font::read(QIODevice& in) { // store in the character to glyph hash _glyphs[g.c] = g; }; - - setupGL(); } struct TextureVertex { @@ -291,6 +291,11 @@ QRectF Glyph::textureBounds(const glm::vec2 & textureSize) const { } void Font::setupGL() { + if (_initialized) { + return; + } + _initialized = true; + _texture = TexturePtr( new QOpenGLTexture(_image, QOpenGLTexture::GenerateMipMaps)); _program = ProgramPtr(new QOpenGLShaderProgram()); @@ -411,7 +416,9 @@ glm::vec2 Font::computeExtent(const QString & str) const { // even without explicit line feeds. glm::vec2 Font::drawString(float x, float y, const QString & str, const glm::vec4& color, TextRenderer::EffectType effectType, - const glm::vec2& bounds) const { + const glm::vec2& bounds) { + + setupGL(); // Stores how far we've moved from the start of the string, in DTP units glm::vec2 advance(0, -_rowHeight - _descent); @@ -481,9 +488,12 @@ glm::vec2 Font::drawString(float x, float y, const QString & str, } _vao->release(); + _texture->release(); // TODO: Brad & Sam, let's discuss this. Without this non-textured quads get their colors borked. _program->release(); // FIXME, needed? // glDisable(GL_TEXTURE_2D); + + return advance; } diff --git a/libraries/script-engine/src/XMLHttpRequestClass.cpp b/libraries/script-engine/src/XMLHttpRequestClass.cpp index cd2e2f3732..116548db61 100644 --- a/libraries/script-engine/src/XMLHttpRequestClass.cpp +++ b/libraries/script-engine/src/XMLHttpRequestClass.cpp @@ -207,7 +207,7 @@ void XMLHttpRequestClass::open(const QString& method, const QString& url, bool a notImplemented(); } } else { - if (url.toLower().left(33) == "https://data.highfidelity.io/api/") { + if (url.toLower().left(33) == "https://metaverse.highfidelity.io/api/") { AccountManager& accountManager = AccountManager::getInstance(); if (accountManager.hasValidAccessToken()) { diff --git a/libraries/physics/src/DoubleHashKey.cpp b/libraries/shared/src/DoubleHashKey.cpp similarity index 66% rename from libraries/physics/src/DoubleHashKey.cpp rename to libraries/shared/src/DoubleHashKey.cpp index fae1d715fb..794604d21b 100644 --- a/libraries/physics/src/DoubleHashKey.cpp +++ b/libraries/shared/src/DoubleHashKey.cpp @@ -1,6 +1,6 @@ // // DoubleHashKey.cpp -// libraries/physcis/src +// libraries/shared/src // // Created by Andrew Meadows 2014.11.02 // Copyright 2014 High Fidelity, Inc. @@ -11,8 +11,8 @@ #include "DoubleHashKey.h" -const int NUM_PRIMES = 64; -const unsigned int PRIMES[] = { +const uint32_t NUM_PRIMES = 64; +const uint32_t PRIMES[] = { 4194301U, 4194287U, 4194277U, 4194271U, 4194247U, 4194217U, 4194199U, 4194191U, 4194187U, 4194181U, 4194173U, 4194167U, 4194143U, 4194137U, 4194131U, 4194107U, 4194103U, 4194023U, 4194011U, 4194007U, 4193977U, 4193971U, 4193963U, 4193957U, @@ -23,8 +23,8 @@ const unsigned int PRIMES[] = { 4193353U, 4193327U, 4193309U, 4193303U, 4193297U, 4193279U, 4193269U, 4193263U }; -unsigned int DoubleHashKey::hashFunction(unsigned int value, int primeIndex) { - unsigned int hash = PRIMES[primeIndex % NUM_PRIMES] * (value + 1U); +uint32_t DoubleHashKey::hashFunction(uint32_t value, uint32_t primeIndex) { + uint32_t hash = PRIMES[primeIndex % NUM_PRIMES] * (value + 1U); hash += ~(hash << 15); hash ^= (hash >> 10); hash += (hash << 3); @@ -33,11 +33,16 @@ unsigned int DoubleHashKey::hashFunction(unsigned int value, int primeIndex) { return hash ^ (hash >> 16); } -unsigned int DoubleHashKey::hashFunction2(unsigned int value) { - unsigned hash = 0x811c9dc5U; - for (int i = 0; i < 4; i++ ) { - unsigned int byte = (value << (i * 8)) >> (24 - i * 8); +uint32_t DoubleHashKey::hashFunction2(uint32_t value) { + uint32_t hash = 0x811c9dc5U; + for (uint32_t i = 0; i < 4; i++ ) { + uint32_t byte = (value << (i * 8)) >> (24 - i * 8); hash = ( hash ^ byte ) * 0x01000193U; } return hash; } + +void DoubleHashKey::computeHash(uint32_t value, uint32_t primeIndex) { + _hash = DoubleHashKey::hashFunction(value, primeIndex); + _hash2 = DoubleHashKey::hashFunction2(value); +} diff --git a/libraries/physics/src/DoubleHashKey.h b/libraries/shared/src/DoubleHashKey.h similarity index 51% rename from libraries/physics/src/DoubleHashKey.h rename to libraries/shared/src/DoubleHashKey.h index ebacf6c96a..3b08bf7c1a 100644 --- a/libraries/physics/src/DoubleHashKey.h +++ b/libraries/shared/src/DoubleHashKey.h @@ -1,6 +1,6 @@ // // DoubleHashKey.h -// libraries/physcis/src +// libraries/shared/src // // Created by Andrew Meadows 2014.11.02 // Copyright 2014 High Fidelity, Inc. @@ -12,27 +12,38 @@ #ifndef hifi_DoubleHashKey_h #define hifi_DoubleHashKey_h +#include + // DoubleHashKey for use with btHashMap class DoubleHashKey { public: - static unsigned int hashFunction(unsigned int value, int primeIndex); - static unsigned int hashFunction2(unsigned int value); + static uint32_t hashFunction(uint32_t value, uint32_t primeIndex); + static uint32_t hashFunction2(uint32_t value); DoubleHashKey() : _hash(0), _hash2(0) { } - DoubleHashKey(unsigned int value, int primeIndex = 0) : + DoubleHashKey(uint32_t value, uint32_t primeIndex = 0) : _hash(hashFunction(value, primeIndex)), _hash2(hashFunction2(value)) { } + void clear() { _hash = 0; _hash2 = 0; } + bool isNull() const { return _hash == 0 && _hash2 == 0; } + bool equals(const DoubleHashKey& other) const { return _hash == other._hash && _hash2 == other._hash2; } - unsigned int getHash() const { return (unsigned int)_hash; } + void computeHash(uint32_t value, uint32_t primeIndex = 0); + uint32_t getHash() const { return _hash; } + uint32_t getHash2() const { return _hash2; } - int _hash; - int _hash2; + void setHash(uint32_t hash) { _hash = hash; } + void setHash2(uint32_t hash2) { _hash2 = hash2; } + +private: + uint32_t _hash; + uint32_t _hash2; }; #endif // hifi_DoubleHashKey_h diff --git a/libraries/shared/src/ShapeInfo.cpp b/libraries/shared/src/ShapeInfo.cpp index a1e72cdca0..8b2a3e3a73 100644 --- a/libraries/shared/src/ShapeInfo.cpp +++ b/libraries/shared/src/ShapeInfo.cpp @@ -13,78 +13,82 @@ #include "SharedUtil.h" // for MILLIMETERS_PER_METER -//#include "DoubleHashKey.h" #include "ShapeInfo.h" void ShapeInfo::clear() { - _type = INVALID_SHAPE; - _data.clear(); + _type = SHAPE_TYPE_NONE; + _halfExtents = glm::vec3(0.0f); + _doubleHashKey.clear(); + _externalData = NULL; +} + +void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QVector* data) { + _type = type; + switch(type) { + case SHAPE_TYPE_NONE: + _halfExtents = glm::vec3(0.0f); + break; + case SHAPE_TYPE_BOX: + _halfExtents = halfExtents; + break; + case SHAPE_TYPE_SPHERE: { + // sphere radius is max of halfExtents + float radius = glm::max(glm::max(halfExtents.x, halfExtents.y), halfExtents.z); + _halfExtents = glm::vec3(radius); + break; + } + default: + _halfExtents = halfExtents; + } + _externalData = data; } void ShapeInfo::setBox(const glm::vec3& halfExtents) { - _type = BOX_SHAPE; - _data.clear(); - // _data[0] = < halfX, halfY, halfZ > - _data.push_back(halfExtents); + _type = SHAPE_TYPE_BOX; + _halfExtents = halfExtents; + _doubleHashKey.clear(); } void ShapeInfo::setSphere(float radius) { - _type = SPHERE_SHAPE; - _data.clear(); - // _data[0] = < radius, radius, radius > - _data.push_back(glm::vec3(radius)); + _type = SHAPE_TYPE_SPHERE; + _halfExtents = glm::vec3(radius, radius, radius); + _doubleHashKey.clear(); } -void ShapeInfo::setCylinder(float radius, float halfHeight) { - _type = CYLINDER_SHAPE; - _data.clear(); - // _data[0] = < radius, halfHeight, radius > - // NOTE: default cylinder has (UpAxis = 1) axis along yAxis and radius stored in X - _data.push_back(glm::vec3(radius, halfHeight, radius)); +void ShapeInfo::setEllipsoid(const glm::vec3& halfExtents) { + _type = SHAPE_TYPE_ELLIPSOID; + _halfExtents = halfExtents; + _doubleHashKey.clear(); } -void ShapeInfo::setCapsule(float radius, float halfHeight) { - _type = CAPSULE_SHAPE; - _data.clear(); - // _data[0] = < radius, halfHeight, radius > - _data.push_back(glm::vec3(radius, halfHeight, radius)); -} - -glm::vec3 ShapeInfo::getBoundingBoxDiagonal() const { - switch(_type) { - case BOX_SHAPE: - case SPHERE_SHAPE: - case CYLINDER_SHAPE: - case CAPSULE_SHAPE: - return 2.0f * _data[0]; - default: - break; - } - return glm::vec3(0.0f); +void ShapeInfo::setCapsuleY(float radius, float halfHeight) { + _type = SHAPE_TYPE_CAPSULE_Y; + _halfExtents = glm::vec3(radius, halfHeight, radius); + _doubleHashKey.clear(); } float ShapeInfo::computeVolume() const { const float DEFAULT_VOLUME = 1.0f; float volume = DEFAULT_VOLUME; switch(_type) { - case BOX_SHAPE: { - // factor of 8.0 because the components of _data[0] are all halfExtents - volume = 8.0f * _data[0].x * _data[0].y * _data[0].z; + case SHAPE_TYPE_BOX: { + // factor of 8.0 because the components of _halfExtents are all halfExtents + volume = 8.0f * _halfExtents.x * _halfExtents.y * _halfExtents.z; break; } - case SPHERE_SHAPE: { - float radius = _data[0].x; + case SHAPE_TYPE_SPHERE: { + float radius = _halfExtents.x; volume = 4.0f * PI * radius * radius * radius / 3.0f; break; } - case CYLINDER_SHAPE: { - float radius = _data[0].x; - volume = PI * radius * radius * 2.0f * _data[0].y; + case SHAPE_TYPE_CYLINDER_Y: { + float radius = _halfExtents.x; + volume = PI * radius * radius * 2.0f * _halfExtents.y; break; } - case CAPSULE_SHAPE: { - float radius = _data[0].x; - volume = PI * radius * radius * (2.0f * _data[0].y + 4.0f * radius / 3.0f); + case SHAPE_TYPE_CAPSULE_Y: { + float radius = _halfExtents.x; + volume = PI * radius * radius * (2.0f * _halfExtents.y + 4.0f * radius / 3.0f); break; } default: @@ -93,3 +97,85 @@ float ShapeInfo::computeVolume() const { assert(volume > 0.0f); return volume; } + +const DoubleHashKey& ShapeInfo::getHash() const { + // NOTE: we cache the hash so we only ever need to compute it once for any valid ShapeInfo instance. + if (_doubleHashKey.isNull() && _type != SHAPE_TYPE_NONE) { + // cast this to non-const pointer so we can do our dirty work + ShapeInfo* thisPtr = const_cast(this); + // compute hash1 + // TODO?: provide lookup table for hash/hash2 of _type rather than recompute? + uint32_t primeIndex = 0; + thisPtr->_doubleHashKey.computeHash((uint32_t)_type, primeIndex++); + + const QVector* data = getData(); + if (data) { + // if externalData exists we use it to continue the hash + + // compute hash + uint32_t hash = _doubleHashKey.getHash(); + + glm::vec3 tmpData; + int numData = data->size(); + for (int i = 0; i < numData; ++i) { + tmpData = (*data)[i]; + for (int j = 0; j < 3; ++j) { + // NOTE: 0.49f is used to bump the float up almost half a millimeter + // so the cast to int produces a round() effect rather than a floor() + uint32_t floatHash = + DoubleHashKey::hashFunction((uint32_t)(tmpData[j] * MILLIMETERS_PER_METER + copysignf(1.0f, tmpData[j]) * 0.49f), primeIndex++); + hash ^= floatHash; + } + } + thisPtr->_doubleHashKey.setHash(hash); + + // compute hash2 + hash = _doubleHashKey.getHash2(); + for (int i = 0; i < numData; ++i) { + tmpData = (*data)[i]; + for (int j = 0; j < 3; ++j) { + // NOTE: 0.49f is used to bump the float up almost half a millimeter + // so the cast to int produces a round() effect rather than a floor() + uint32_t floatHash = + DoubleHashKey::hashFunction2((uint32_t)(tmpData[j] * MILLIMETERS_PER_METER + copysignf(1.0f, tmpData[j]) * 0.49f)); + hash += ~(floatHash << 17); + hash ^= (floatHash >> 11); + hash += (floatHash << 4); + hash ^= (floatHash >> 7); + hash += ~(floatHash << 10); + hash = (hash << 16) | (hash >> 16); + } + } + thisPtr->_doubleHashKey.setHash2(hash); + } else { + // this shape info has no external data so type+extents should be enough to generate a unique hash + // compute hash1 + uint32_t hash = _doubleHashKey.getHash(); + for (int j = 0; j < 3; ++j) { + // NOTE: 0.49f is used to bump the float up almost half a millimeter + // so the cast to int produces a round() effect rather than a floor() + uint32_t floatHash = + DoubleHashKey::hashFunction((uint32_t)(_halfExtents[j] * MILLIMETERS_PER_METER + copysignf(1.0f, _halfExtents[j]) * 0.49f), primeIndex++); + hash ^= floatHash; + } + thisPtr->_doubleHashKey.setHash(hash); + + // compute hash2 + hash = _doubleHashKey.getHash2(); + for (int j = 0; j < 3; ++j) { + // NOTE: 0.49f is used to bump the float up almost half a millimeter + // so the cast to int produces a round() effect rather than a floor() + uint32_t floatHash = + DoubleHashKey::hashFunction2((uint32_t)(_halfExtents[j] * MILLIMETERS_PER_METER + copysignf(1.0f, _halfExtents[j]) * 0.49f)); + hash += ~(floatHash << 17); + hash ^= (floatHash >> 11); + hash += (floatHash << 4); + hash ^= (floatHash >> 7); + hash += ~(floatHash << 10); + hash = (hash << 16) | (hash >> 16); + } + thisPtr->_doubleHashKey.setHash2(hash); + } + } + return _doubleHashKey; +} diff --git a/libraries/shared/src/ShapeInfo.h b/libraries/shared/src/ShapeInfo.h index 9b4c587c3f..b6dc88e4b7 100644 --- a/libraries/shared/src/ShapeInfo.h +++ b/libraries/shared/src/ShapeInfo.h @@ -15,28 +15,51 @@ #include #include -#include "Shape.h" +#include "DoubleHashKey.h" + +enum ShapeType { + SHAPE_TYPE_NONE, + SHAPE_TYPE_BOX, + SHAPE_TYPE_SPHERE, + SHAPE_TYPE_ELLIPSOID, + SHAPE_TYPE_HULL, + SHAPE_TYPE_PLANE, + SHAPE_TYPE_COMPOUND, + SHAPE_TYPE_CAPSULE_X, + SHAPE_TYPE_CAPSULE_Y, + SHAPE_TYPE_CAPSULE_Z, + SHAPE_TYPE_CYLINDER_X, + SHAPE_TYPE_CYLINDER_Y, + SHAPE_TYPE_CYLINDER_Z +}; class ShapeInfo { public: - ShapeInfo() : _type(INVALID_SHAPE) {} - void clear(); + void setParams(ShapeType type, const glm::vec3& halfExtents, QVector* data = NULL); void setBox(const glm::vec3& halfExtents); void setSphere(float radius); - void setCylinder(float radius, float halfHeight); - void setCapsule(float radius, float halfHeight); + void setEllipsoid(const glm::vec3& halfExtents); + //void setHull(); // TODO: implement this + void setCapsuleY(float radius, float halfHeight); const int getType() const { return _type; } - const QVector& getData() const { return _data; } - glm::vec3 getBoundingBoxDiagonal() const; + const glm::vec3& getHalfExtents() const { return _halfExtents; } + + void setData(const QVector* data) { _externalData = data; } + const QVector* getData() const { return _externalData; } + float computeVolume() const; + const DoubleHashKey& getHash() const; + protected: - int _type; - QVector _data; + ShapeType _type = SHAPE_TYPE_NONE; + glm::vec3 _halfExtents = glm::vec3(0.0f); + DoubleHashKey _doubleHashKey; + const QVector* _externalData = NULL; }; #endif // hifi_ShapeInfo_h diff --git a/tests/physics/src/ShapeInfoTests.cpp b/tests/physics/src/ShapeInfoTests.cpp index cbef53f2eb..bf2a98eb10 100644 --- a/tests/physics/src/ShapeInfoTests.cpp +++ b/tests/physics/src/ShapeInfoTests.cpp @@ -24,10 +24,10 @@ void ShapeInfoTests::testHashFunctions() { int maxTests = 10000000; ShapeInfo info; - btHashMap hashes; + btHashMap hashes; - int bits[32]; - unsigned int masks[32]; + uint32_t bits[32]; + uint32_t masks[32]; for (int i = 0; i < 32; ++i) { bits[i] = 0; masks[i] = 1U << i; @@ -45,26 +45,27 @@ void ShapeInfoTests::testHashFunctions() { // test sphere info.setSphere(radiusX); ++testCount; - DoubleHashKey key = ShapeInfoUtil::computeHash(info); - int* hashPtr = hashes.find(key._hash); - if (hashPtr && *hashPtr == key._hash2) { + DoubleHashKey key = info.getHash(); + uint32_t* hashPtr = hashes.find(key.getHash()); + if (hashPtr && *hashPtr == key.getHash2()) { std::cout << testCount << " hash collision radiusX = " << radiusX - << " h1 = 0x" << std::hex << (unsigned int)(key._hash) - << " h2 = 0x" << std::hex << (unsigned int)(key._hash2) + << " h1 = 0x" << std::hex << key.getHash() + << " h2 = 0x" << std::hex << key.getHash2() << std::endl; ++numCollisions; assert(false); } else { - hashes.insert(key._hash, key._hash2); + hashes.insert(key.getHash(), key.getHash2()); } for (int k = 0; k < 32; ++k) { - if (masks[k] & key._hash2) { + if (masks[k] & key.getHash2()) { ++bits[k]; } } for (int y = 1; y < numSteps && testCount < maxTests; ++y) { float radiusY = (float)y * deltaLength; + /* TODO: reimplement Cylinder and Capsule shapes // test cylinder and capsule int types[] = { CYLINDER_SHAPE_PROXYTYPE, CAPSULE_SHAPE_PROXYTYPE }; for (int i = 0; i < 2; ++i) { @@ -74,58 +75,59 @@ void ShapeInfoTests::testHashFunctions() { break; } case CAPSULE_SHAPE_PROXYTYPE: { - info.setCapsule(radiusX, radiusY); + info.setCapsuleY(radiusX, radiusY); break; } } ++testCount; - key = ShapeInfoUtil::computeHash(info); - hashPtr = hashes.find(key._hash); - if (hashPtr && *hashPtr == key._hash2) { + key = info.getHash(); + hashPtr = hashes.find(key.getHash()); + if (hashPtr && *hashPtr == key.getHash2()) { std::cout << testCount << " hash collision radiusX = " << radiusX << " radiusY = " << radiusY - << " h1 = 0x" << std::hex << (unsigned int)(key._hash) - << " h2 = 0x" << std::hex << (unsigned int)(key._hash2) + << " h1 = 0x" << std::hex << key.getHash() + << " h2 = 0x" << std::hex << key.getHash2() << std::endl; ++numCollisions; assert(false); } else { - hashes.insert(key._hash, key._hash2); + hashes.insert(key.getHash(), key.getHash2()); } for (int k = 0; k < 32; ++k) { - if (masks[k] & key._hash2) { + if (masks[k] & key.getHash2()) { ++bits[k]; } } } + */ for (int z = 1; z < numSteps && testCount < maxTests; ++z) { float radiusZ = (float)z * deltaLength; // test box info.setBox(glm::vec3(radiusX, radiusY, radiusZ)); ++testCount; - DoubleHashKey key = ShapeInfoUtil::computeHash(info); - hashPtr = hashes.find(key._hash); - if (hashPtr && *hashPtr == key._hash2) { + DoubleHashKey key = info.getHash(); + hashPtr = hashes.find(key.getHash()); + if (hashPtr && *hashPtr == key.getHash2()) { std::cout << testCount << " hash collision radiusX = " << radiusX << " radiusY = " << radiusY << " radiusZ = " << radiusZ - << " h1 = 0x" << std::hex << (unsigned int)(key._hash) - << " h2 = 0x" << std::hex << (unsigned int)(key._hash2) + << " h1 = 0x" << std::hex << key.getHash() + << " h2 = 0x" << std::hex << key.getHash2() << std::endl; ++numCollisions; assert(false); } else { - hashes.insert(key._hash, key._hash2); + hashes.insert(key.getHash(), key.getHash2()); } for (int k = 0; k < 32; ++k) { - if (masks[k] & key._hash2) { + if (masks[k] & key.getHash2()) { ++bits[k]; } } } } } - unsigned long int msec = timer.getTimeMilliseconds(); + uint64_t msec = timer.getTimeMilliseconds(); std::cout << msec << " msec with " << numCollisions << " collisions out of " << testCount << " hashes" << std::endl; // print out distribution of bits @@ -138,7 +140,7 @@ void ShapeInfoTests::testBoxShape() { ShapeInfo info; glm::vec3 halfExtents(1.23f, 4.56f, 7.89f); info.setBox(halfExtents); - DoubleHashKey key = ShapeInfoUtil::computeHash(info); + DoubleHashKey key = info.getHash(); btCollisionShape* shape = ShapeInfoUtil::createShapeFromInfo(info); if (!shape) { @@ -148,15 +150,15 @@ void ShapeInfoTests::testBoxShape() { ShapeInfo otherInfo; ShapeInfoUtil::collectInfoFromShape(shape, otherInfo); - DoubleHashKey otherKey = ShapeInfoUtil::computeHash(otherInfo); - if (key._hash != otherKey._hash) { + DoubleHashKey otherKey = otherInfo.getHash(); + if (key.getHash() != otherKey.getHash()) { std::cout << __FILE__ << ":" << __LINE__ - << " ERROR: expected Box shape hash = " << key._hash << " but found hash = " << otherKey._hash << std::endl; + << " ERROR: expected Box shape hash = " << key.getHash() << " but found hash = " << otherKey.getHash() << std::endl; } - if (key._hash2 != otherKey._hash2) { + if (key.getHash2() != otherKey.getHash2()) { std::cout << __FILE__ << ":" << __LINE__ - << " ERROR: expected Box shape hash2 = " << key._hash2 << " but found hash2 = " << otherKey._hash2 << std::endl; + << " ERROR: expected Box shape hash2 = " << key.getHash2() << " but found hash2 = " << otherKey.getHash2() << std::endl; } delete shape; @@ -166,74 +168,78 @@ void ShapeInfoTests::testSphereShape() { ShapeInfo info; float radius = 1.23f; info.setSphere(radius); - DoubleHashKey key = ShapeInfoUtil::computeHash(info); + DoubleHashKey key = info.getHash(); btCollisionShape* shape = ShapeInfoUtil::createShapeFromInfo(info); ShapeInfo otherInfo; ShapeInfoUtil::collectInfoFromShape(shape, otherInfo); - DoubleHashKey otherKey = ShapeInfoUtil::computeHash(otherInfo); - if (key._hash != otherKey._hash) { + DoubleHashKey otherKey = otherInfo.getHash(); + if (key.getHash() != otherKey.getHash()) { std::cout << __FILE__ << ":" << __LINE__ - << " ERROR: expected Sphere shape hash = " << key._hash << " but found hash = " << otherKey._hash << std::endl; + << " ERROR: expected Sphere shape hash = " << key.getHash() << " but found hash = " << otherKey.getHash() << std::endl; } - if (key._hash2 != otherKey._hash2) { + if (key.getHash2() != otherKey.getHash2()) { std::cout << __FILE__ << ":" << __LINE__ - << " ERROR: expected Sphere shape hash2 = " << key._hash2 << " but found hash2 = " << otherKey._hash2 << std::endl; + << " ERROR: expected Sphere shape hash2 = " << key.getHash2() << " but found hash2 = " << otherKey.getHash2() << std::endl; } delete shape; } void ShapeInfoTests::testCylinderShape() { + /* TODO: reimplement Cylinder shape ShapeInfo info; float radius = 1.23f; float height = 4.56f; info.setCylinder(radius, height); - DoubleHashKey key = ShapeInfoUtil::computeHash(info); + DoubleHashKey key = info.getHash(); btCollisionShape* shape = ShapeInfoUtil::createShapeFromInfo(info); ShapeInfo otherInfo; ShapeInfoUtil::collectInfoFromShape(shape, otherInfo); - DoubleHashKey otherKey = ShapeInfoUtil::computeHash(otherInfo); - if (key._hash != otherKey._hash) { + DoubleHashKey otherKey = otherInfo.getHash(); + if (key.getHash() != otherKey.getHash()) { std::cout << __FILE__ << ":" << __LINE__ - << " ERROR: expected Cylinder shape hash = " << key._hash << " but found hash = " << otherKey._hash << std::endl; + << " ERROR: expected Cylinder shape hash = " << key.getHash() << " but found hash = " << otherKey.getHash() << std::endl; } - if (key._hash2 != otherKey._hash2) { + if (key.getHash2() != otherKey.getHash2()) { std::cout << __FILE__ << ":" << __LINE__ - << " ERROR: expected Cylinder shape hash2 = " << key._hash2 << " but found hash2 = " << otherKey._hash2 << std::endl; + << " ERROR: expected Cylinder shape hash2 = " << key.getHash2() << " but found hash2 = " << otherKey.getHash2() << std::endl; } delete shape; + */ } void ShapeInfoTests::testCapsuleShape() { + /* TODO: reimplement Capsule shape ShapeInfo info; float radius = 1.23f; float height = 4.56f; info.setCapsule(radius, height); - DoubleHashKey key = ShapeInfoUtil::computeHash(info); + DoubleHashKey key = info.getHash(); btCollisionShape* shape = ShapeInfoUtil::createShapeFromInfo(info); ShapeInfo otherInfo; ShapeInfoUtil::collectInfoFromShape(shape, otherInfo); - DoubleHashKey otherKey = ShapeInfoUtil::computeHash(otherInfo); - if (key._hash != otherKey._hash) { + DoubleHashKey otherKey = otherInfo.getHash(); + if (key.getHash() != otherKey.getHash()) { std::cout << __FILE__ << ":" << __LINE__ - << " ERROR: expected Capsule shape hash = " << key._hash << " but found hash = " << otherKey._hash << std::endl; + << " ERROR: expected Capsule shape hash = " << key.getHash() << " but found hash = " << otherKey.getHash() << std::endl; } - if (key._hash2 != otherKey._hash2) { + if (key.getHash2() != otherKey.getHash2()) { std::cout << __FILE__ << ":" << __LINE__ - << " ERROR: expected Capsule shape hash2 = " << key._hash2 << " but found hash2 = " << otherKey._hash2 << std::endl; + << " ERROR: expected Capsule shape hash2 = " << key.getHash2() << " but found hash2 = " << otherKey.getHash2() << std::endl; } delete shape; + */ } void ShapeInfoTests::runAllTests() { diff --git a/tests/physics/src/ShapeManagerTests.cpp b/tests/physics/src/ShapeManagerTests.cpp index e49b9b8063..6dfae15e78 100644 --- a/tests/physics/src/ShapeManagerTests.cpp +++ b/tests/physics/src/ShapeManagerTests.cpp @@ -188,6 +188,7 @@ void ShapeManagerTests::addSphereShape() { } void ShapeManagerTests::addCylinderShape() { + /* TODO: reimplement Cylinder shape ShapeInfo info; float radius = 1.23f; float height = 4.56f; @@ -204,9 +205,11 @@ void ShapeManagerTests::addCylinderShape() { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: Cylinder ShapeInfo --> shape --> ShapeInfo --> shape did not work" << std::endl; } + */ } void ShapeManagerTests::addCapsuleShape() { + /* TODO: reimplement Capsule shape ShapeInfo info; float radius = 1.23f; float height = 4.56f; @@ -223,6 +226,7 @@ void ShapeManagerTests::addCapsuleShape() { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: Capsule ShapeInfo --> shape --> ShapeInfo --> shape did not work" << std::endl; } + */ } void ShapeManagerTests::runAllTests() {