diff --git a/CMakeLists.txt b/CMakeLists.txt index 7f05548ec4..c283bfff17 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -53,7 +53,7 @@ else () endif () endif(WIN32) -if (NOT MSVC12) +if ((NOT MSVC12) AND (NOT MSVC14)) include(CheckCXXCompilerFlag) CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11) CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X) diff --git a/assignment-client/src/AssignmentClient.cpp b/assignment-client/src/AssignmentClient.cpp index cfee18941c..e125a44783 100644 --- a/assignment-client/src/AssignmentClient.cpp +++ b/assignment-client/src/AssignmentClient.cpp @@ -66,6 +66,9 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri // set the logging target to the the CHILD_TARGET_NAME LogHandler::getInstance().setTargetName(ASSIGNMENT_CLIENT_TARGET_NAME); + // make sure we output process IDs for a child AC otherwise it's insane to parse + LogHandler::getInstance().setShouldOutputPID(true); + // setup our _requestAssignment member variable from the passed arguments _requestAssignment = Assignment(Assignment::RequestCommand, requestAssignmentType, assignmentPool); diff --git a/assignment-client/src/AssignmentClientMonitor.cpp b/assignment-client/src/AssignmentClientMonitor.cpp index d19eb90df6..8c6478b59f 100644 --- a/assignment-client/src/AssignmentClientMonitor.cpp +++ b/assignment-client/src/AssignmentClientMonitor.cpp @@ -39,9 +39,9 @@ AssignmentClientMonitor::AssignmentClientMonitor(const unsigned int numAssignmen _walletUUID(walletUUID), _assignmentServerHostname(assignmentServerHostname), _assignmentServerPort(assignmentServerPort) -{ +{ qDebug() << "_requestAssignmentType =" << _requestAssignmentType; - + // start the Logging class with the parent's target name LogHandler::getInstance().setTargetName(ASSIGNMENT_CLIENT_MONITOR_TARGET_NAME); @@ -77,13 +77,13 @@ void AssignmentClientMonitor::simultaneousWaitOnChildren(int waitMsecs) { while(_childProcesses.size() > 0 && !waitTimer.hasExpired(waitMsecs)) { // continue processing events so we can handle a process finishing up QCoreApplication::processEvents(); - } + } } void AssignmentClientMonitor::childProcessFinished() { QProcess* childProcess = qobject_cast<QProcess*>(sender()); qint64 processID = _childProcesses.key(childProcess); - + if (processID > 0) { qDebug() << "Child process" << processID << "has finished. Removing from internal map."; _childProcesses.remove(processID); @@ -98,17 +98,17 @@ void AssignmentClientMonitor::stopChildProcesses() { qDebug() << "Attempting to terminate child process" << childProcess->processId(); childProcess->terminate(); } - + simultaneousWaitOnChildren(WAIT_FOR_CHILD_MSECS); - + if (_childProcesses.size() > 0) { // ask even more firmly foreach(QProcess* childProcess, _childProcesses) { qDebug() << "Attempting to kill child process" << childProcess->processId(); childProcess->kill(); } - - simultaneousWaitOnChildren(WAIT_FOR_CHILD_MSECS); + + simultaneousWaitOnChildren(WAIT_FOR_CHILD_MSECS); } } @@ -122,7 +122,7 @@ void AssignmentClientMonitor::aboutToQuit() { void AssignmentClientMonitor::spawnChildClient() { QProcess* assignmentClient = new QProcess(this); - + // unparse the parts of the command-line that the child cares about QStringList _childArguments; if (_assignmentPool != "") { @@ -153,7 +153,7 @@ void AssignmentClientMonitor::spawnChildClient() { // make sure that the output from the child process appears in our output assignmentClient->setProcessChannelMode(QProcess::ForwardedChannels); - + assignmentClient->start(QCoreApplication::applicationFilePath(), _childArguments); // make sure we hear that this process has finished when it does @@ -194,7 +194,7 @@ void AssignmentClientMonitor::checkSpares() { qDebug() << "asking child" << aSpareId << "to exit."; SharedNodePointer childNode = nodeList->nodeWithUUID(aSpareId); childNode->activateLocalSocket(); - + QByteArray diePacket = nodeList->byteArrayWithPopulatedHeader(PacketTypeStopNode); nodeList->writeUnverifiedDatagram(diePacket, childNode); } @@ -239,7 +239,7 @@ void AssignmentClientMonitor::readPendingDatagrams() { // update our records about how to reach this child matchingNode->setLocalSocket(senderSockAddr); - QVariantMap packetVariantMap = + QVariantMap packetVariantMap = JSONBreakableMarshal::fromStringBuffer(receivedPacket.mid(numBytesForPacketHeader(receivedPacket))); QJsonObject unpackedStatsJSON = QJsonObject::fromVariantMap(packetVariantMap); diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index ea7ac0648b..c3ca6796bc 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -850,7 +850,9 @@ void AudioMixer::run() { ++_numStatFrames; + // since we're a while loop we need to help Qt's event processing QCoreApplication::processEvents(); + QCoreApplication::sendPostedEvents(this, 0); if (_isFinished) { break; diff --git a/cmake/externals/polyvox/CMakeLists.txt b/cmake/externals/polyvox/CMakeLists.txt index 28aec6dab7..c28a8e1319 100644 --- a/cmake/externals/polyvox/CMakeLists.txt +++ b/cmake/externals/polyvox/CMakeLists.txt @@ -5,7 +5,7 @@ ExternalProject_Add( ${EXTERNAL_NAME} URL http://hifi-public.s3.amazonaws.com/dependencies/polyvox.zip URL_MD5 904b840328278c9b36fa7a14be730c34 - CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> + CMAKE_ARGS -DENABLE_EXAMPLES=OFF -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build LOG_DOWNLOAD 1 LOG_CONFIGURE 1 diff --git a/cmake/macros/SetupExternalsBinaryDir.cmake b/cmake/macros/SetupExternalsBinaryDir.cmake index a118dcf543..118df9c8fa 100644 --- a/cmake/macros/SetupExternalsBinaryDir.cmake +++ b/cmake/macros/SetupExternalsBinaryDir.cmake @@ -14,12 +14,12 @@ macro(SETUP_EXTERNALS_BINARY_DIR) # get a short name for the generator to use in the path STRING(REGEX REPLACE " " "-" CMAKE_GENERATOR_FOLDER_NAME ${CMAKE_GENERATOR}) - if (MSVC12) + if (MSVC12) set(CMAKE_GENERATOR_FOLDER_NAME "vc12") - else () - if (CMAKE_GENERATOR_FOLDER_NAME STREQUAL "Unix-Makefiles") - set(CMAKE_GENERATOR_FOLDER_NAME "makefiles") - endif () + elseif (MSVC14) + set(CMAKE_GENERATOR_FOLDER_NAME "vc14") + elseif(CMAKE_GENERATOR_FOLDER_NAME STREQUAL "Unix-Makefiles") + set(CMAKE_GENERATOR_FOLDER_NAME "makefiles") endif () set(EXTERNALS_BINARY_ROOT_DIR "${CMAKE_CURRENT_BINARY_DIR}/ext") diff --git a/examples/controllers/hydra/gun.js b/examples/controllers/hydra/gun.js index 146f9daca3..7d024e2fd3 100644 --- a/examples/controllers/hydra/gun.js +++ b/examples/controllers/hydra/gun.js @@ -99,6 +99,13 @@ var NUM_BUTTONS = 3; var screenSize = Controller.getViewportDimensions(); var startX = screenSize.x / 2 - (NUM_BUTTONS * (BUTTON_SIZE + PADDING)) / 2; +Script.include(["../../libraries/toolBars.js"]); +const persistKey = "highfidelity.gun.toolbar.position"; +var toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL); +toolBar.save = function () { + Settings.setValue(persistKey, JSON.stringify([toolBar.x, toolBar.y])); +}; +var old = JSON.parse(Settings.getValue(persistKey) || '0'); var reticle = Overlays.addOverlay("image", { x: screenSize.x / 2 - (BUTTON_SIZE / 2), y: screenSize.y / 2 - (BUTTON_SIZE / 2), @@ -108,9 +115,9 @@ var reticle = Overlays.addOverlay("image", { alpha: 1 }); -var offButton = Overlays.addOverlay("image", { - x: startX, - y: screenSize.y - (BUTTON_SIZE + PADDING), +var offButton = toolBar.addOverlay("image", { + x: old ? old[0] : startX, + y: old ? old[1] : (screenSize.y - (BUTTON_SIZE + PADDING)), width: BUTTON_SIZE, height: BUTTON_SIZE, imageURL: HIFI_PUBLIC_BUCKET + "images/gun/close.svg", @@ -118,7 +125,7 @@ var offButton = Overlays.addOverlay("image", { }); startX += BUTTON_SIZE + PADDING; -var platformButton = Overlays.addOverlay("image", { +var platformButton = toolBar.addOverlay("image", { x: startX, y: screenSize.y - (BUTTON_SIZE + PADDING), width: BUTTON_SIZE, @@ -128,7 +135,7 @@ var platformButton = Overlays.addOverlay("image", { }); startX += BUTTON_SIZE + PADDING; -var gridButton = Overlays.addOverlay("image", { +var gridButton = toolBar.addOverlay("image", { x: startX, y: screenSize.y - (BUTTON_SIZE + PADDING), width: BUTTON_SIZE, @@ -493,10 +500,8 @@ function mousePressEvent(event) { } function scriptEnding() { - Overlays.deleteOverlay(reticle); - Overlays.deleteOverlay(offButton); - Overlays.deleteOverlay(platformButton); - Overlays.deleteOverlay(gridButton); + Overlays.deleteOverlay(reticle); + toolBar.cleanup(); Overlays.deleteOverlay(pointer[0]); Overlays.deleteOverlay(pointer[1]); Overlays.deleteOverlay(text); diff --git a/examples/defaultScripts.js b/examples/defaultScripts.js index 8f32b80bba..61bed8d9b1 100644 --- a/examples/defaultScripts.js +++ b/examples/defaultScripts.js @@ -18,3 +18,4 @@ Script.load("notifications.js"); Script.load("users.js"); Script.load("grab.js"); Script.load("pointer.js"); +Script.load("directory.js"); diff --git a/examples/dialTone.js b/examples/dialTone.js index 135acb17f4..b55e654f25 100644 --- a/examples/dialTone.js +++ b/examples/dialTone.js @@ -3,6 +3,7 @@ // examples // // Created by Stephen Birarda on 06/08/15. +// Added disconnect HRS 6/11/15. // Copyright 2015 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. @@ -10,14 +11,19 @@ // // setup the local sound we're going to use -var connectSound = SoundCache.getSound("file://" + Paths.resources + "sounds/short1.wav"); +var connectSound = SoundCache.getSound("file://" + Paths.resources + "sounds/hello.wav"); +var disconnectSound = SoundCache.getSound("file://" + Paths.resources + "sounds/goodbye.wav"); // setup the options needed for that sound var connectSoundOptions = { localOnly: true -} +}; // play the sound locally once we get the first audio packet from a mixer Audio.receivedFirstPacket.connect(function(){ Audio.playSound(connectSound, connectSoundOptions); }); + +Audio.disconnected.connect(function(){ + Audio.playSound(disconnectSound, connectSoundOptions); +}); diff --git a/examples/dice.js b/examples/dice.js index 515019e740..ac5d1b7426 100644 --- a/examples/dice.js +++ b/examples/dice.js @@ -3,6 +3,7 @@ // examples // // Created by Philip Rosedale on February 2, 2015 +// Persist toolbar by HRS 6/11/15. // Copyright 2015 High Fidelity, Inc. // // Press the dice button to throw some dice from the center of the screen. @@ -31,9 +32,16 @@ var screenSize = Controller.getViewportDimensions(); var BUTTON_SIZE = 32; var PADDING = 3; -var offButton = Overlays.addOverlay("image", { - x: screenSize.x / 2 - BUTTON_SIZE * 2 + PADDING, - y: screenSize.y - (BUTTON_SIZE + PADDING), +Script.include(["libraries/toolBars.js"]); +var toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL); +const persistKey = "highfidelity.dice.toolbar.position"; +toolBar.save = function () { + Settings.setValue(persistKey, JSON.stringify([toolBar.x, toolBar.y])); +}; +var old = JSON.parse(Settings.getValue(persistKey) || '0'); +var offButton = toolBar.addOverlay("image", { + x: old ? old[0] : (screenSize.x / 2 - BUTTON_SIZE * 2 + PADDING), + y: old ? old[1] : (screenSize.y - (BUTTON_SIZE + PADDING)), width: BUTTON_SIZE, height: BUTTON_SIZE, imageURL: HIFI_PUBLIC_BUCKET + "images/close.png", @@ -45,7 +53,7 @@ var offButton = Overlays.addOverlay("image", { alpha: 1 }); -var deleteButton = Overlays.addOverlay("image", { +var deleteButton = toolBar.addOverlay("image", { x: screenSize.x / 2 - BUTTON_SIZE, y: screenSize.y - (BUTTON_SIZE + PADDING), width: BUTTON_SIZE, @@ -59,7 +67,7 @@ var deleteButton = Overlays.addOverlay("image", { alpha: 1 }); -var diceButton = Overlays.addOverlay("image", { +var diceButton = toolBar.addOverlay("image", { x: screenSize.x / 2 + PADDING, y: screenSize.y - (BUTTON_SIZE + PADDING), width: BUTTON_SIZE, @@ -140,10 +148,8 @@ function mousePressEvent(event) { } function scriptEnding() { - Overlays.deleteOverlay(offButton); - Overlays.deleteOverlay(diceButton); - Overlays.deleteOverlay(deleteButton); + toolBar.cleanup(); } Controller.mousePressEvent.connect(mousePressEvent); -Script.scriptEnding.connect(scriptEnding); \ No newline at end of file +Script.scriptEnding.connect(scriptEnding); diff --git a/examples/directory.js b/examples/directory.js new file mode 100644 index 0000000000..b1fac19e8b --- /dev/null +++ b/examples/directory.js @@ -0,0 +1,92 @@ +// +// directory.js +// examples +// +// Created by David Rowe on 8 Jun 2015 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +Script.include("libraries/globals.js"); + +var directory = (function () { + + var DIRECTORY_URL = "https://metaverse.highfidelity.com/directory", + directoryWindow, + DIRECTORY_BUTTON_URL = HIFI_PUBLIC_BUCKET + "images/tools/directory.svg", + BUTTON_WIDTH = 50, + BUTTON_HEIGHT = 50, + BUTTON_ALPHA = 0.9, + BUTTON_MARGIN = 8, + directoryButton, + EDIT_TOOLBAR_BUTTONS = 10, // Number of buttons in edit.js toolbar + viewport; + + function updateButtonPosition() { + Overlays.editOverlay(directoryButton, { + x: viewport.x - BUTTON_WIDTH - BUTTON_MARGIN, + y: (viewport.y - (EDIT_TOOLBAR_BUTTONS + 1) * (BUTTON_HEIGHT + BUTTON_MARGIN) - BUTTON_MARGIN) / 2 - 1 + }); + } + + function onMousePressEvent(event) { + var clickedOverlay; + + clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y }); + + if (clickedOverlay === directoryButton) { + if (directoryWindow.url !== DIRECTORY_URL) { + directoryWindow.setURL(DIRECTORY_URL); + } + directoryWindow.setVisible(true); + directoryWindow.raise(); + } + } + + function onDomainChanged() { + directoryWindow.setVisible(false); + } + + function onScriptUpdate() { + var oldViewport = viewport; + + viewport = Controller.getViewportDimensions(); + + if (viewport.x !== oldViewport.x || viewport.y !== oldViewport.y) { + updateButtonPosition(); + } + } + + function setUp() { + viewport = Controller.getViewportDimensions(); + + directoryWindow = new WebWindow('Directory', DIRECTORY_URL, 900, 700, false); + directoryWindow.setVisible(false); + + directoryButton = Overlays.addOverlay("image", { + imageURL: DIRECTORY_BUTTON_URL, + width: BUTTON_WIDTH, + height: BUTTON_HEIGHT, + x: viewport.x - BUTTON_WIDTH - BUTTON_MARGIN, + y: BUTTON_MARGIN, + alpha: BUTTON_ALPHA, + visible: true + }); + + updateButtonPosition(); + + Controller.mousePressEvent.connect(onMousePressEvent); + Window.domainChanged.connect(onDomainChanged); + + Script.update.connect(onScriptUpdate); + } + + function tearDown() { + Overlays.deleteOverlay(directoryButton); + } + + setUp(); + Script.scriptEnding.connect(tearDown); +}()); \ No newline at end of file diff --git a/examples/edit.js b/examples/edit.js index 6055400289..2974397cde 100644 --- a/examples/edit.js +++ b/examples/edit.js @@ -2,6 +2,7 @@ // examples // // Created by Brad Hefta-Gaub on 10/2/14. +// Persist toolbar by HRS 6/11/15. // Copyright 2014 High Fidelity, Inc. // // This script allows you to edit entities with a new UI/UX for mouse and trackpad based editing @@ -320,6 +321,7 @@ var toolBar = (function () { } } + const persistKey = "highfidelity.edit.toolbar.position"; that.move = function () { var newViewPort, toolsX, @@ -330,6 +332,15 @@ var toolBar = (function () { if (toolBar === undefined) { initialize(); + toolBar.save = function () { + Settings.setValue(persistKey, JSON.stringify([toolBar.x, toolBar.y])); + }; + var old = JSON.parse(Settings.getValue(persistKey) || '0'); + if (old) { + windowDimensions = newViewPort; + toolBar.move(old[0], old[1]); + return; + } } else if (windowDimensions.x === newViewPort.x && windowDimensions.y === newViewPort.y) { return; diff --git a/examples/example/scripts/controllerScriptingExamples.js b/examples/example/scripts/controllerScriptingExamples.js new file mode 100644 index 0000000000..26a1999bbb --- /dev/null +++ b/examples/example/scripts/controllerScriptingExamples.js @@ -0,0 +1,96 @@ +// +// controllerScriptingExamples.js +// examples +// +// Created by Sam Gondelman on 6/2/15 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +// Assumes you only have the default keyboard connected + +// Resets every device to its default key bindings: +Controller.resetAllDeviceBindings(); + +// Query all actions +print("All Actions: \n" + Controller.getAllActions()); + +// Each action stores: +// action: int representation of enum +print("Action 5 int: \n" + Controller.getAllActions()[5].action); + +// actionName: string representation of enum +print("Action 5 name: \n" + Controller.getAllActions()[5].actionName); + +// inputChannels: list of all inputchannels that control that action +print("Action 5 input channels: \n" + Controller.getAllActions()[5].inputChannels + "\n"); + + +// Each input channel stores: +// action: Action that this InputChannel maps to +print("Input channel action: \n" + Controller.getAllActions()[5].inputChannels[0].action); + +// scale: sensitivity of input +print("Input channel scale: \n" + Controller.getAllActions()[5].inputChannels[0].scale); + +// input and modifier: Inputs +print("Input channel input and modifier: \n" + Controller.getAllActions()[5].inputChannels[0].input + "\n" + Controller.getAllActions()[5].inputChannels[0].modifier + "\n"); + + +// Each Input stores: +// device: device of input +print("Input device: \n" + Controller.getAllActions()[5].inputChannels[0].input.device); + +// channel: channel of input +print("Input channel: \n" + Controller.getAllActions()[5].inputChannels[0].input.channel); + +// type: type of input (Unknown, Button, Axis, Joint) +print("Input type: \n" + Controller.getAllActions()[5].inputChannels[0].input.type); + +// id: id of input +print("Input id: \n" + Controller.getAllActions()[5].inputChannels[0].input.id + "\n"); + + +// You can get the name of a device from its id +print("Device 1 name: \n" + Controller.getDeviceName(Controller.getAllActions()[5].inputChannels[0].input.id)); + +// You can also get all of a devices input channels +print("Device 1's input channels: \n" + Controller.getAllInputsForDevice(1) + "\n"); + + +// Modifying properties: +// The following code will switch the "w" and "s" key functionality and adjust their scales +var s = Controller.getAllActions()[0].inputChannels[0]; +var w = Controller.getAllActions()[1].inputChannels[0]; + +// You must remove an input controller before modifying it so the old input controller isn't registered anymore +// removeInputChannel and addInputChannel return true if successful, false otherwise +Controller.removeInputChannel(s); +Controller.removeInputChannel(w); +print(s.scale); +s.action = 1; +s.scale = .01; + +w.action = 0; +w.scale = 10000; +Controller.addInputChannel(s); +Controller.addInputChannel(w); +print(s.scale); + +// You can get all the available inputs for any device +// Each AvailableInput has: +// input: the Input itself +// inputName: string representing the input +var availableInputs = Controller.getAvailableInputs(1); +for (i = 0; i < availableInputs.length; i++) { + print(availableInputs[i].inputName); +} + +// You can modify key bindings by using these avaiable inputs +// This will replace e (up) with 6 +var e = Controller.getAllActions()[5].inputChannels[0]; +Controller.removeInputChannel(e); +e.input = availableInputs[6].input; +Controller.addInputChannel(e); \ No newline at end of file diff --git a/examples/grab.js b/examples/grab.js index 306af86c68..f64aaa30ee 100644 --- a/examples/grab.js +++ b/examples/grab.js @@ -4,7 +4,7 @@ // Created by Eric Levin on May 1, 2015 // Copyright 2015 High Fidelity, Inc. // -// Grab's physically moveable entities with the mouse, by applying a spring force. +// Grab's physically moveable entities with the mouse, by applying a spring force. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -20,7 +20,7 @@ var ANGULAR_DAMPING_RATE = 0.40; // NOTE: to improve readability global variable names start with 'g' var gIsGrabbing = false; var gGrabbedEntity = null; -var gPrevMouse = {x: 0, y: 0}; +var gActionID = null; var gEntityProperties; var gStartPosition; var gStartRotation; @@ -31,20 +31,20 @@ var gPlaneNormal = ZERO_VEC3; // gMaxGrabDistance is a function of the size of the object. var gMaxGrabDistance; -// gGrabMode defines the degrees of freedom of the grab target positions -// relative to gGrabStartPosition options include: +// gGrabMode defines the degrees of freedom of the grab target positions +// relative to gGrabStartPosition options include: // xzPlane (default) // verticalCylinder (SHIFT) // rotate (CONTROL) // Modes to eventually support?: -// xyPlane -// yzPlane +// xyPlane +// yzPlane // polar // elevationAzimuth -var gGrabMode = "xzplane"; +var gGrabMode = "xzplane"; -// gGrabOffset allows the user to grab an object off-center. It points from ray's intersection -// with the move-plane to object center (at the moment the grab is initiated). Future target positions +// gGrabOffset allows the user to grab an object off-center. It points from ray's intersection +// with the move-plane to object center (at the moment the grab is initiated). Future target positions // are relative to the ray's intersection by the same offset. var gGrabOffset = { x: 0, y: 0, z: 0 }; @@ -53,13 +53,14 @@ var gTargetRotation; var gLiftKey = false; // SHIFT var gRotateKey = false; // CONTROL +var gInitialMouse = { x: 0, y: 0 }; var gPreviousMouse = { x: 0, y: 0 }; var gMouseCursorLocation = { x: 0, y: 0 }; var gMouseAtRotateStart = { x: 0, y: 0 }; var gBeaconHeight = 0.10; -var gAngularVelocity = ZERO_VEC3; +// var gAngularVelocity = ZERO_VEC3; // TODO: play sounds again when we aren't leaking AudioInjector threads // var grabSound = SoundCache.getSound("https://hifi-public.s3.amazonaws.com/eric/sounds/CloseClamp.wav"); @@ -140,6 +141,10 @@ function mouseIntersectionWithPlane(pointOnPlane, planeNormal, event) { } function computeNewGrabPlane() { + if (!gIsGrabbing) { + return; + } + var maybeResetMousePosition = false; if (gGrabMode !== "rotate") { gMouseAtRotateStart = gMouseCursorLocation; @@ -162,7 +167,7 @@ function computeNewGrabPlane() { var xzOffset = Vec3.subtract(gPointOnPlane, Camera.getPosition()); xzOffset.y = 0; gXzDistanceToGrab = Vec3.length(xzOffset); - + if (gGrabMode !== "rotate" && maybeResetMousePosition) { // we reset the mouse position whenever we stop rotating Window.setCursorPosition(gMouseAtRotateStart.x, gMouseAtRotateStart.y); @@ -173,6 +178,7 @@ function mousePressEvent(event) { if (!event.isLeftButton) { return; } + gInitialMouse = {x: event.x, y: event.y }; gPreviousMouse = {x: event.x, y: event.y }; var pickRay = Camera.computePickRay(event.x, event.y); @@ -189,12 +195,13 @@ function mousePressEvent(event) { var clickedEntity = pickResults.entityID; var entityProperties = Entities.getEntityProperties(clickedEntity) - var objectPosition = entityProperties.position; + gStartPosition = entityProperties.position; + gStartRotation = entityProperties.rotation; var cameraPosition = Camera.getPosition(); gBeaconHeight = Vec3.length(entityProperties.dimensions); gMaxGrabDistance = gBeaconHeight / MAX_SOLID_ANGLE; - if (Vec3.distance(objectPosition, cameraPosition) > gMaxGrabDistance) { + if (Vec3.distance(gStartPosition, cameraPosition) > gMaxGrabDistance) { // don't allow grabs of things far away return; } @@ -205,20 +212,20 @@ function mousePressEvent(event) { gGrabbedEntity = clickedEntity; gCurrentPosition = entityProperties.position; gOriginalGravity = entityProperties.gravity; - gTargetPosition = objectPosition; + gTargetPosition = gStartPosition; // compute the grab point - var nearestPoint = Vec3.subtract(objectPosition, cameraPosition); + var nearestPoint = Vec3.subtract(gStartPosition, cameraPosition); var distanceToGrab = Vec3.dot(nearestPoint, pickRay.direction); nearestPoint = Vec3.multiply(distanceToGrab, pickRay.direction); gPointOnPlane = Vec3.sum(cameraPosition, nearestPoint); // compute the grab offset - gGrabOffset = Vec3.subtract(objectPosition, gPointOnPlane); + gGrabOffset = Vec3.subtract(gStartPosition, gPointOnPlane); computeNewGrabPlane(); - updateDropLine(objectPosition); + updateDropLine(gStartPosition); // TODO: play sounds again when we aren't leaking AudioInjector threads //Audio.playSound(grabSound, { position: entityProperties.position, volume: VOLUME }); @@ -231,6 +238,8 @@ function mouseReleaseEvent() { } gIsGrabbing = false + Entities.deleteAction(gGrabbedEntity, gActionID); + gActionID = null; Overlays.editOverlay(gBeacon, { visible: false }); @@ -250,18 +259,24 @@ function mouseMoveEvent(event) { gOriginalGravity = entityProperties.gravity; } + var actionArgs = {}; + if (gGrabMode === "rotate") { var deltaMouse = { x: 0, y: 0 }; - var dx = event.x - gPreviousMouse.x; - var dy = event.y - gPreviousMouse.y; - + var dx = event.x - gInitialMouse.x; + var dy = event.y - gInitialMouse.y; var orientation = Camera.getOrientation(); var dragOffset = Vec3.multiply(dx, Quat.getRight(orientation)); dragOffset = Vec3.sum(dragOffset, Vec3.multiply(-dy, Quat.getUp(orientation))); var axis = Vec3.cross(dragOffset, Quat.getFront(orientation)); - var axis = Vec3.normalize(axis); - var ROTATE_STRENGTH = 8.0; // magic number tuned by hand - gAngularVelocity = Vec3.multiply(ROTATE_STRENGTH, axis); + axis = Vec3.normalize(axis); + var ROTATE_STRENGTH = 0.4; // magic number tuned by hand + var angle = ROTATE_STRENGTH * Math.sqrt((dx * dx) + (dy * dy)); + var deltaQ = Quat.angleAxis(angle, axis); + // var qZero = entityProperties.rotation; + var qZero = gStartRotation; + var qOne = Quat.multiply(deltaQ, qZero); + actionArgs = {targetRotation: qOne, angularTimeScale: 0.1}; } else { var newTargetPosition; if (gGrabMode === "verticalCylinder") { @@ -284,9 +299,18 @@ function mouseMoveEvent(event) { } } gTargetPosition = Vec3.sum(newTargetPosition, gGrabOffset); + actionArgs = {targetPosition: gTargetPosition, linearTimeScale: 0.1}; } gPreviousMouse = { x: event.x, y: event.y }; gMouseCursorLocation = { x: Window.getCursorPositionX(), y: Window.getCursorPositionY() }; + + if (!gActionID) { + gActionID = Entities.addAction("spring", gGrabbedEntity, actionArgs); + } else { + Entities.updateAction(gGrabbedEntity, gActionID, actionArgs); + } + + updateDropLine(gTargetPosition); } function keyReleaseEvent(event) { @@ -309,38 +333,8 @@ function keyPressEvent(event) { computeNewGrabPlane(); } -function update(deltaTime) { - if (!gIsGrabbing) { - return; - } - - var entityProperties = Entities.getEntityProperties(gGrabbedEntity); - gCurrentPosition = entityProperties.position; - if (gGrabMode === "rotate") { - gAngularVelocity = Vec3.subtract(gAngularVelocity, Vec3.multiply(gAngularVelocity, ANGULAR_DAMPING_RATE)); - Entities.editEntity(gGrabbedEntity, { angularVelocity: gAngularVelocity, }); - } - - // always push toward linear grab position, even when rotating - var newVelocity = ZERO_VEC3; - var dPosition = Vec3.subtract(gTargetPosition, gCurrentPosition); - var delta = Vec3.length(dPosition); - if (delta > CLOSE_ENOUGH) { - var MAX_POSITION_DELTA = 4.0; - if (delta > MAX_POSITION_DELTA) { - dPosition = Vec3.multiply(dPosition, MAX_POSITION_DELTA / delta); - } - // desired speed is proportional to displacement by the inverse of timescale - // (for critically damped motion) - newVelocity = Vec3.multiply(dPosition, INV_MOVE_TIMESCALE); - } - Entities.editEntity(gGrabbedEntity, { velocity: newVelocity, }); - updateDropLine(gTargetPosition); -} - Controller.mouseMoveEvent.connect(mouseMoveEvent); Controller.mousePressEvent.connect(mousePressEvent); Controller.mouseReleaseEvent.connect(mouseReleaseEvent); Controller.keyPressEvent.connect(keyPressEvent); Controller.keyReleaseEvent.connect(keyReleaseEvent); -Script.update.connect(update); diff --git a/examples/html/entityProperties.html b/examples/html/entityProperties.html index d9cad0feff..f029088b1a 100644 --- a/examples/html/entityProperties.html +++ b/examples/html/entityProperties.html @@ -360,6 +360,10 @@ var elVoxelVolumeSizeZ = document.getElementById("property-voxel-volume-size-z"); var elVoxelSurfaceStyle = document.getElementById("property-voxel-surface-style"); + var elHyperlinkHref = document.getElementById("property-hyperlink-href"); + var elHyperlinkDescription = document.getElementById("property-hyperlink-description"); + + if (window.EventBridge !== undefined) { EventBridge.scriptEventReceived.connect(function(data) { @@ -467,6 +471,9 @@ elScriptURL.value = properties.script; elUserData.value = properties.userData; + elHyperlinkHref.value = properties.href; + elHyperlinkDescription.value = properties.description; + for (var i = 0; i < allSections.length; i++) { for (var j = 0; j < allSections[i].length; j++) { allSections[i][j].style.display = 'none'; @@ -612,6 +619,8 @@ elLocked.addEventListener('change', createEmitCheckedPropertyUpdateFunction('locked')); elName.addEventListener('change', createEmitTextPropertyUpdateFunction('name')); + elHyperlinkHref.addEventListener('change', createEmitTextPropertyUpdateFunction('href')); + elHyperlinkDescription.addEventListener('change', createEmitTextPropertyUpdateFunction('description')); elVisible.addEventListener('change', createEmitCheckedPropertyUpdateFunction('visible')); var positionChangeFunction = createEmitVec3PropertyUpdateFunction( @@ -850,7 +859,6 @@ elVoxelVolumeSizeZ.addEventListener('change', voxelVolumeSizeChangeFunction); elVoxelSurfaceStyle.addEventListener('change', createEmitTextPropertyUpdateFunction('voxelSurfaceStyle')); - elMoveSelectionToGrid.addEventListener("click", function() { EventBridge.emitWebEvent(JSON.stringify({ type: "action", @@ -937,6 +945,18 @@ <input type="text" id="property-name"></input> </div> </div> + + <div class="property"> + <div class="label">Hyperlink</div> + <div class="input-area">Href<br></div> + <div class="value"> + <input id="property-hyperlink-href" class="url"></input> + </div> + <div class="input-area">Description<br></div> <div class="value"> + <input id="property-hyperlink-description" class="url"></input> + </div> + </div> + <div class="property"> <span class="label">Locked</span> <span class="value"> diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index d12c9dae3c..d5e2b24f36 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -304,7 +304,8 @@ SelectionDisplay = (function () { visible: false, dashed: true, lineWidth: 2.0, - ignoreRayIntersection: true // this never ray intersects + ignoreRayIntersection: true, // this never ray intersects + drawInFront: true }); var selectionBox = Overlays.addOverlay("cube", { diff --git a/examples/libraries/toolBars.js b/examples/libraries/toolBars.js index 670a69dec7..6e72795ea5 100644 --- a/examples/libraries/toolBars.js +++ b/examples/libraries/toolBars.js @@ -3,6 +3,7 @@ // examples // // Created by Clément Brisset on 5/7/14. +// Persistable drag position by HRS 6/11/15. // Copyright 2014 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. @@ -236,6 +237,7 @@ ToolBar = function(x, y, direction) { y: y - ToolBar.SPACING }); } + this.save(); } this.setAlpha = function(alpha, tool) { @@ -313,9 +315,8 @@ ToolBar = function(x, y, direction) { this.cleanup = function() { for(var tool in this.tools) { this.tools[tool].cleanup(); - delete this.tools[tool]; } - + if (this.back != null) { Overlays.deleteOverlay(this.back); this.back = null; @@ -327,7 +328,71 @@ ToolBar = function(x, y, direction) { this.width = 0; this.height = 0; } + + var that = this; + this.contains = function (xOrPoint, optionalY) { + var x = (optionalY === undefined) ? xOrPoint.x : xOrPoint, + y = (optionalY === undefined) ? xOrPoint.y : optionalY; + return (that.x <= x) && (x <= (that.x + that.width)) && + (that.y <= y) && (y <= (that.y + that.height)); + } + that.hover = function (enable) { + that.isHovering = enable; + if (that.back) { + if (enable) { + that.oldAlpha = Overlays.getProperty(that.back, 'backgroundAlpha'); + } + Overlays.editOverlay(this.back, { + visible: enable, + backgroundAlpha: enable ? 0.5 : that.oldAlpha + }); + } + }; + // These are currently only doing that which is necessary for toolbar hover and toolbar drag. + // They have not yet been extended to tool hover/click/release, etc. + this.mousePressEvent = function (event) { + if (!that.contains(event)) { + that.mightBeDragging = false; + return; + } + that.mightBeDragging = true; + that.dragOffsetX = that.x - event.x; + that.dragOffsetY = that.y - event.y; + }; + this.mouseMove = function (event) { + if (!that.mightBeDragging || !event.isLeftButton) { + that.mightBeDragging = false; + if (!that.contains(event)) { + if (that.isHovering) { + that.hover(false); + } + return; + } + if (!that.isHovering) { + that.hover(true); + } + return; + } + that.move(that.dragOffsetX + event.x, that.dragOffsetY + event.y); + }; + Controller.mousePressEvent.connect(this.mousePressEvent); + Controller.mouseMoveEvent.connect(this.mouseMove); + // Called on move. A different approach would be to have all this on the prototype, + // and let apps extend where needed. Ex. app defines its toolbar.move() to call this.__proto__.move and then save. + this.save = function () { }; + // This compatability hack breaks the model, but makes converting existing scripts easier: + this.addOverlay = function (ignored, oldSchoolProperties) { + var properties = JSON.parse(JSON.stringify(oldSchoolProperties)); // a copy + if (that.numberOfTools() === 0) { + that.move(properties.x, properties.y); + } + delete properties.x; + delete properties.y; + var index = that.addTool(properties); + var id = that.tools[index].overlay(); + return id; + } } ToolBar.SPACING = 4; ToolBar.VERTICAL = 0; -ToolBar.HORIZONTAL = 1; \ No newline at end of file +ToolBar.HORIZONTAL = 1; diff --git a/examples/paint.js b/examples/paint.js index 8bba4e2571..c0cc93afc7 100644 --- a/examples/paint.js +++ b/examples/paint.js @@ -12,8 +12,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // Script.include('lineRider.js') -var MAX_POINTS_PER_LINE = 80; - +var MAX_POINTS_PER_LINE = 30; +var LINE_LIFETIME = 60 * 5 //5 minute lifetime var colorPalette = [{ red: 236, @@ -120,10 +120,12 @@ function MousePaint() { y: 10, z: 10 }, - lineWidth: LINE_WIDTH + lineWidth: LINE_WIDTH, + lifetime: LINE_LIFETIME }); points = []; if (point) { + points.push(point); path.push(point); } @@ -133,22 +135,22 @@ function MousePaint() { function mouseMoveEvent(event) { + if (!isDrawing) { + return; + } var pickRay = Camera.computePickRay(event.x, event.y); var addVector = Vec3.multiply(Vec3.normalize(pickRay.direction), DRAWING_DISTANCE); var point = Vec3.sum(Camera.getPosition(), addVector); + points.push(point); + path.push(point); Entities.editEntity(line, { linePoints: points }); Entities.editEntity(brush, { position: point }); - if (!isDrawing) { - return; - } - points.push(point); - path.push(point); if (points.length === MAX_POINTS_PER_LINE) { //We need to start a new line! @@ -253,7 +255,6 @@ function HydraPaint() { var maxLineWidth = 10; var currentLineWidth = minLineWidth; var MIN_PAINT_TRIGGER_THRESHOLD = .01; - var LINE_LIFETIME = 20; var COLOR_CHANGE_TIME_FACTOR = 0.1; var RIGHT_BUTTON_1 = 7 @@ -330,7 +331,7 @@ function HydraPaint() { z: 10 }, lineWidth: 5, - // lifetime: LINE_LIFETIME + lifetime: LINE_LIFETIME }); this.points = []; if (point) { diff --git a/examples/pointer.js b/examples/pointer.js index cca46709ee..ea6b0c233f 100644 --- a/examples/pointer.js +++ b/examples/pointer.js @@ -3,6 +3,7 @@ // // Created by Seth Alves on May 15th // Modified by Eric Levin on June 4 +// Persist toolbar by HRS 6/11/15. // Copyright 2015 High Fidelity, Inc. // // Provides a pointer with option to draw on surfaces @@ -31,9 +32,16 @@ HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; var screenSize = Controller.getViewportDimensions(); var userCanPoint = false; -var pointerButton = Overlays.addOverlay("image", { - x: screenSize.x / 2 - BUTTON_SIZE * 2 + PADDING, - y: screenSize.y - (BUTTON_SIZE + PADDING), +Script.include(["libraries/toolBars.js"]); +const persistKey = "highfidelity.pointer.toolbar.position"; +var toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL); +toolBar.save = function () { + Settings.setValue(persistKey, JSON.stringify([toolBar.x, toolBar.y])); +}; +var old = JSON.parse(Settings.getValue(persistKey) || '0'); +var pointerButton = toolBar.addOverlay("image", { + x: old ? old[0] : screenSize.x / 2 - BUTTON_SIZE * 2 + PADDING, + y: old ? old[1] : screenSize.y - (BUTTON_SIZE + PADDING), width: BUTTON_SIZE, height: BUTTON_SIZE, imageURL: HIFI_PUBLIC_BUCKET + "images/laser.png", @@ -150,4 +158,4 @@ Script.scriptEnding.connect(cleanup); Controller.mouseMoveEvent.connect(mouseMoveEvent); Controller.mousePressEvent.connect(mousePressEvent); -Controller.mouseReleaseEvent.connect(mouseReleaseEvent); \ No newline at end of file +Controller.mouseReleaseEvent.connect(mouseReleaseEvent); diff --git a/examples/stick.js b/examples/stick.js new file mode 100644 index 0000000000..ea1f3439a9 --- /dev/null +++ b/examples/stick.js @@ -0,0 +1,92 @@ +// stick.js +// examples +// +// Created by Seth Alves on 2015-6-10 +// Copyright 2015 High Fidelity, Inc. +// +// Allow avatar to hold a stick +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +var hand = "left"; +var nullActionID = "00000000-0000-0000-0000-000000000000"; +var controllerID; +var controllerActive; +var stickID = null; +var actionID = nullActionID; +// sometimes if this is run immediately the stick doesn't get created? use a timer. +Script.setTimeout(function() { + stickID = Entities.addEntity({ + type: "Model", + modelURL: "https://hifi-public.s3.amazonaws.com/eric/models/stick.fbx", + compoundShapeURL: "https://hifi-public.s3.amazonaws.com/eric/models/stick.obj", + dimensions: {x: .11, y: .11, z: .59}, + position: MyAvatar.getRightPalmPosition(), // initial position doesn't matter, as long as it's close + rotation: MyAvatar.orientation, + damping: .1, + collisionsWillMove: true + }); + actionID = Entities.addAction("hold", stickID, {relativePosition: {x: 0.0, y: 0.0, z: -0.9}, + hand: hand, + timeScale: 0.15}); +}, 3000); + + +function cleanUp() { + Entities.deleteEntity(stickID); +} + + +function positionStick(stickOrientation) { + var baseOffset = {x: 0.0, y: 0.0, z: -0.9}; + var offset = Vec3.multiplyQbyV(stickOrientation, baseOffset); + Entities.updateAction(stickID, actionID, {relativePosition: offset, + relativeRotation: stickOrientation}); +} + + +function mouseMoveEvent(event) { + if (!stickID || actionID == nullActionID) { + return; + } + var windowCenterX = Window.innerWidth / 2; + var windowCenterY = Window.innerHeight / 2; + var mouseXCenterOffset = event.x - windowCenterX; + var mouseYCenterOffset = event.y - windowCenterY; + var mouseXRatio = mouseXCenterOffset / windowCenterX; + var mouseYRatio = mouseYCenterOffset / windowCenterY; + + var stickOrientation = Quat.fromPitchYawRollDegrees(mouseYRatio * -90, mouseXRatio * -90, 0); + positionStick(stickOrientation); +} + + +function initControls(){ + if (hand == "right") { + controllerID = 3; // right handed + } else { + controllerID = 4; // left handed + } +} + + +function update(deltaTime){ + var palmPosition = Controller.getSpatialControlPosition(controllerID); + controllerActive = (Vec3.length(palmPosition) > 0); + if(!controllerActive){ + return; + } + + stickOrientation = Controller.getSpatialControlRawRotation(controllerID); + var adjustment = Quat.fromPitchYawRollDegrees(180, 0, 0); + stickOrientation = Quat.multiply(stickOrientation, adjustment); + + positionStick(stickOrientation); +} + + +Script.scriptEnding.connect(cleanUp); +Controller.mouseMoveEvent.connect(mouseMoveEvent); +Script.update.connect(update); diff --git a/interface/resources/images/reticleLink.png b/interface/resources/images/reticleLink.png new file mode 100644 index 0000000000..b36ee79d00 Binary files /dev/null and b/interface/resources/images/reticleLink.png differ diff --git a/interface/resources/images/reticleWhite.png b/interface/resources/images/reticleWhite.png new file mode 100644 index 0000000000..4c35a186b2 Binary files /dev/null and b/interface/resources/images/reticleWhite.png differ diff --git a/interface/resources/sounds/goodbye.wav b/interface/resources/sounds/goodbye.wav new file mode 100644 index 0000000000..bd6c51a5c0 Binary files /dev/null and b/interface/resources/sounds/goodbye.wav differ diff --git a/interface/resources/sounds/hello.wav b/interface/resources/sounds/hello.wav new file mode 100644 index 0000000000..6269dab5db Binary files /dev/null and b/interface/resources/sounds/hello.wav differ diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 560dccb238..662a8603d3 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -57,6 +57,7 @@ #include <AccountManager.h> #include <AddressManager.h> +#include <CursorManager.h> #include <AmbientOcclusionEffect.h> #include <AudioInjector.h> #include <AutoUpdater.h> @@ -101,6 +102,7 @@ #include "ModelPackager.h" #include "Util.h" #include "InterfaceLogging.h" +#include "InterfaceActionFactory.h" #include "avatar/AvatarManager.h" @@ -259,6 +261,7 @@ bool setupEssentials(int& argc, char** argv) { DependencyManager::registerInheritance<LimitedNodeList, NodeList>(); DependencyManager::registerInheritance<AvatarHashMap, AvatarManager>(); + DependencyManager::registerInheritance<EntityActionFactoryInterface, InterfaceActionFactory>(); Setting::init(); @@ -296,7 +299,8 @@ bool setupEssentials(int& argc, char** argv) { auto sceneScriptingInterface = DependencyManager::set<SceneScriptingInterface>(); auto offscreenUi = DependencyManager::set<OffscreenUi>(); auto autoUpdater = DependencyManager::set<AutoUpdater>(); - auto pathUtils = DependencyManager::set<PathUtils>(); + auto pathUtils = DependencyManager::set<PathUtils>(); + auto actionFactory = DependencyManager::set<InterfaceActionFactory>(); return true; } @@ -424,6 +428,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : connect(audioIO.data(), &AudioClient::muteToggled, this, &Application::audioMuteToggled); connect(audioIO.data(), &AudioClient::receivedFirstPacket, &AudioScriptingInterface::getInstance(), &AudioScriptingInterface::receivedFirstPacket); + connect(audioIO.data(), &AudioClient::disconnected, + &AudioScriptingInterface::getInstance(), &AudioScriptingInterface::disconnected); audioThread->start(); @@ -527,8 +533,16 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : _window->setVisible(true); _glWidget->setFocusPolicy(Qt::StrongFocus); _glWidget->setFocus(); +#ifdef Q_OS_MAC + // OSX doesn't seem to provide for hiding the cursor only on the GL widget + _window->setCursor(Qt::BlankCursor); +#else + // On windows and linux, hiding the top level cursor also means it's invisible + // when hovering over the window menu, which is a pain, so only hide it for + // the GL surface _glWidget->setCursor(Qt::BlankCursor); - +#endif + // enable mouse tracking; otherwise, we only get drag events _glWidget->setMouseTracking(true); @@ -948,12 +962,15 @@ void Application::paintGL() { displaySide(&renderArgs, _myCamera); glPopMatrix(); + renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE; if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) { _rearMirrorTools->render(&renderArgs, true, _glWidget->mapFromGlobal(QCursor::pos())); } else if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) { renderRearViewMirror(&renderArgs, _mirrorViewRect); } + renderArgs._renderMode = RenderArgs::NORMAL_RENDER_MODE; + auto finalFbo = DependencyManager::get<GlowEffect>()->render(&renderArgs); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); @@ -1234,9 +1251,20 @@ void Application::keyPressEvent(QKeyEvent* event) { } break; - case Qt::Key_Apostrophe: - resetSensors(); + case Qt::Key_Apostrophe: { + if (isMeta) { + auto cursor = Cursor::Manager::instance().getCursor(); + auto curIcon = cursor->getIcon(); + if (curIcon == Cursor::Icon::DEFAULT) { + cursor->setIcon(Cursor::Icon::LINK); + } else { + cursor->setIcon(Cursor::Icon::DEFAULT); + } + } else { + resetSensors(); + } break; + } case Qt::Key_A: if (isShifted) { @@ -1360,12 +1388,27 @@ void Application::keyPressEvent(QKeyEvent* event) { case Qt::Key_Slash: Menu::getInstance()->triggerOption(MenuOption::Stats); break; - case Qt::Key_Plus: - _myAvatar->increaseSize(); + + case Qt::Key_Plus: { + if (isMeta && event->modifiers().testFlag(Qt::KeypadModifier)) { + auto& cursorManager = Cursor::Manager::instance(); + cursorManager.setScale(cursorManager.getScale() * 1.1f); + } else { + _myAvatar->increaseSize(); + } break; - case Qt::Key_Minus: - _myAvatar->decreaseSize(); + } + + case Qt::Key_Minus: { + if (isMeta && event->modifiers().testFlag(Qt::KeypadModifier)) { + auto& cursorManager = Cursor::Manager::instance(); + cursorManager.setScale(cursorManager.getScale() / 1.1f); + } else { + _myAvatar->decreaseSize(); + } break; + } + case Qt::Key_Equal: _myAvatar->resetSize(); break; @@ -3405,7 +3448,6 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se "Application::displaySide() ... entities..."); RenderArgs::DebugFlags renderDebugFlags = RenderArgs::RENDER_DEBUG_NONE; - RenderArgs::RenderMode renderMode = RenderArgs::DEFAULT_RENDER_MODE; if (Menu::getInstance()->isOptionChecked(MenuOption::PhysicsShowHulls)) { renderDebugFlags = (RenderArgs::DebugFlags) (renderDebugFlags | (int) RenderArgs::RENDER_DEBUG_HULLS); @@ -3414,10 +3456,6 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se renderDebugFlags = (RenderArgs::DebugFlags) (renderDebugFlags | (int) RenderArgs::RENDER_DEBUG_SIMULATION_OWNERSHIP); } - if (theCamera.getMode() == CAMERA_MODE_MIRROR) { - renderMode = RenderArgs::MIRROR_RENDER_MODE; - } - renderArgs->_renderMode = renderMode; renderArgs->_debugFlags = renderDebugFlags; _entities.render(renderArgs); } diff --git a/interface/src/Application.h b/interface/src/Application.h index 4e1127f410..c9e5bea76e 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -148,6 +148,7 @@ public: static glm::quat getOrientationForPath() { return getInstance()->_myAvatar->getOrientation(); } static glm::vec3 getPositionForAudio() { return getInstance()->_myAvatar->getHead()->getPosition(); } static glm::quat getOrientationForAudio() { return getInstance()->_myAvatar->getHead()->getFinalOrientationInWorldFrame(); } + static UserInputMapper* getUserInputMapper() { return &getInstance()->_userInputMapper; } static void initPlugins(); static void shutdownPlugins(); diff --git a/interface/src/InterfaceActionFactory.cpp b/interface/src/InterfaceActionFactory.cpp new file mode 100644 index 0000000000..08afc3581c --- /dev/null +++ b/interface/src/InterfaceActionFactory.cpp @@ -0,0 +1,49 @@ +// +// InterfaceActionFactory.cpp +// libraries/entities/src +// +// Created by Seth Alves on 2015-6-2 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + + + +#include <avatar/AvatarActionHold.h> +#include <ObjectActionPullToPoint.h> +#include <ObjectActionSpring.h> + +#include "InterfaceActionFactory.h" + + +EntityActionPointer InterfaceActionFactory::factory(EntitySimulation* simulation, + EntityActionType type, + QUuid id, + EntityItemPointer ownerEntity, + QVariantMap arguments) { + EntityActionPointer action = nullptr; + switch (type) { + case ACTION_TYPE_NONE: + return nullptr; + case ACTION_TYPE_PULL_TO_POINT: + action = (EntityActionPointer) new ObjectActionPullToPoint(id, ownerEntity); + break; + case ACTION_TYPE_SPRING: + action = (EntityActionPointer) new ObjectActionSpring(id, ownerEntity); + break; + case ACTION_TYPE_HOLD: + action = (EntityActionPointer) new AvatarActionHold(id, ownerEntity); + break; + } + + bool ok = action->updateArguments(arguments); + if (ok) { + ownerEntity->addAction(simulation, action); + return action; + } + + action = nullptr; + return action; +} diff --git a/interface/src/InterfaceActionFactory.h b/interface/src/InterfaceActionFactory.h new file mode 100644 index 0000000000..5848df4635 --- /dev/null +++ b/interface/src/InterfaceActionFactory.h @@ -0,0 +1,28 @@ +// +// InterfaceActionFactory.cpp +// interface/src/ +// +// Created by Seth Alves on 2015-6-10 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_InterfaceActionFactory_h +#define hifi_InterfaceActionFactory_h + +#include "EntityActionFactoryInterface.h" + +class InterfaceActionFactory : public EntityActionFactoryInterface { +public: + InterfaceActionFactory() : EntityActionFactoryInterface() { } + virtual ~InterfaceActionFactory() { } + virtual EntityActionPointer factory(EntitySimulation* simulation, + EntityActionType type, + QUuid id, + EntityItemPointer ownerEntity, + QVariantMap arguments); +}; + +#endif // hifi_InterfaceActionFactory_h diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 02af30b426..71f4621205 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -107,6 +107,9 @@ Avatar::Avatar() : } Avatar::~Avatar() { + for(auto attachment : _unusedAttachments) { + delete attachment; + } } const float BILLBOARD_LOD_DISTANCE = 40.0f; @@ -298,6 +301,11 @@ bool Avatar::addToScene(AvatarSharedPointer self, std::shared_ptr<render::Scene> pendingChanges.resetItem(_renderItemID, avatarPayloadPointer); _skeletonModel.addToScene(scene, pendingChanges); getHead()->getFaceModel().addToScene(scene, pendingChanges); + + for (auto attachmentModel : _attachmentModels) { + attachmentModel->addToScene(scene, pendingChanges); + } + return true; } @@ -305,6 +313,9 @@ void Avatar::removeFromScene(AvatarSharedPointer self, std::shared_ptr<render::S pendingChanges.removeItem(_renderItemID); _skeletonModel.removeFromScene(scene, pendingChanges); getHead()->getFaceModel().removeFromScene(scene, pendingChanges); + for (auto attachmentModel : _attachmentModels) { + attachmentModel->removeFromScene(scene, pendingChanges); + } } void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, bool postLighting) { @@ -317,6 +328,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, boo if (postLighting && glm::distance(DependencyManager::get<AvatarManager>()->getMyAvatar()->getPosition(), _position) < 10.0f) { auto geometryCache = DependencyManager::get<GeometryCache>(); + auto deferredLighting = DependencyManager::get<DeferredLightingEffect>(); // render pointing lasers glm::vec3 laserColor = glm::vec3(1.0f, 0.0f, 1.0f); @@ -343,6 +355,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, boo pointerTransform.setTranslation(position); pointerTransform.setRotation(rotation); batch->setModelTransform(pointerTransform); + deferredLighting->bindSimpleProgram(*batch); geometryCache->renderLine(*batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, laserLength, 0.0f), laserColor); } } @@ -365,6 +378,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, boo pointerTransform.setTranslation(position); pointerTransform.setRotation(rotation); batch->setModelTransform(pointerTransform); + deferredLighting->bindSimpleProgram(*batch); geometryCache->renderLine(*batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, laserLength, 0.0f), laserColor); } } @@ -451,7 +465,8 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, boo Transform transform; transform.setTranslation(position); batch->setModelTransform(transform); - DependencyManager::get<GeometryCache>()->renderSphere(*batch, LOOK_AT_INDICATOR_RADIUS, 15, 15, LOOK_AT_INDICATOR_COLOR); + DependencyManager::get<DeferredLightingEffect>()->renderSolidSphere(*batch, LOOK_AT_INDICATOR_RADIUS + , 15, 15, LOOK_AT_INDICATOR_COLOR); } } @@ -483,6 +498,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, boo _voiceSphereID = DependencyManager::get<GeometryCache>()->allocateID(); } + DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(*batch); DependencyManager::get<GeometryCache>()->renderSphere(*batch, sphereRadius, 15, 15, glm::vec4(SPHERE_COLOR[0], SPHERE_COLOR[1], SPHERE_COLOR[2], 1.0f - angle / MAX_SPHERE_ANGLE), true, _voiceSphereID); @@ -517,7 +533,7 @@ glm::quat Avatar::computeRotationFromBodyToWorldUp(float proportion) const { return glm::angleAxis(angle * proportion, axis); } -void Avatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, bool postLighting, float glowLevel) { +void Avatar::fixupModelsInScene() { // check to see if when we added our models to the scene they were ready, if they were not ready, then // fix them up in the scene render::ScenePointer scene = Application::getInstance()->getMain3DScene(); @@ -530,8 +546,24 @@ void Avatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, bool getHead()->getFaceModel().removeFromScene(scene, pendingChanges); getHead()->getFaceModel().addToScene(scene, pendingChanges); } + for (auto attachmentModel : _attachmentModels) { + if (attachmentModel->needsFixupInScene()) { + attachmentModel->removeFromScene(scene, pendingChanges); + attachmentModel->addToScene(scene, pendingChanges); + } + } + for (auto attachmentModelToRemove : _attachmentsToRemove) { + attachmentModelToRemove->removeFromScene(scene, pendingChanges); + _unusedAttachments << attachmentModelToRemove; + } + _attachmentsToRemove.clear(); scene->enqueuePendingChanges(pendingChanges); +} +void Avatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, bool postLighting, float glowLevel) { + + fixupModelsInScene(); + { Glower glower(renderArgs, glowLevel); @@ -545,10 +577,6 @@ void Avatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, bool if (postLighting) { getHand()->render(renderArgs, false); - } else { - // NOTE: we no longer call this here, because we've added all the model parts as renderable items in the scene - //_skeletonModel.render(renderArgs, 1.0f); - renderAttachments(renderArgs); } } getHead()->render(renderArgs, 1.0f, renderFrustum, postLighting); @@ -572,22 +600,14 @@ void Avatar::simulateAttachments(float deltaTime) { _skeletonModel.getJointCombinedRotation(jointIndex, jointRotation)) { model->setTranslation(jointPosition + jointRotation * attachment.translation * _scale); model->setRotation(jointRotation * attachment.rotation); - model->setScaleToFit(true, _scale * attachment.scale); + model->setScaleToFit(true, _scale * attachment.scale, true); // hack to force rescale + model->setSnapModelToCenter(false); // hack to force resnap + model->setSnapModelToCenter(true); model->simulate(deltaTime); } } } -void Avatar::renderAttachments(RenderArgs* args) { - // RenderArgs::RenderMode modelRenderMode = (renderMode == RenderArgs::SHADOW_RENDER_MODE) ? - // RenderArgs::SHADOW_RENDER_MODE : RenderArgs::DEFAULT_RENDER_MODE; - /* - foreach (Model* model, _attachmentModels) { - model->render(args, 1.0f); - } - */ -} - void Avatar::updateJointMappings() { // no-op; joint mappings come from skeleton model } @@ -945,12 +965,18 @@ void Avatar::setAttachmentData(const QVector<AttachmentData>& attachmentData) { } // make sure we have as many models as attachments while (_attachmentModels.size() < attachmentData.size()) { - Model* model = new Model(this); + Model* model = nullptr; + if (_unusedAttachments.size() > 0) { + model = _unusedAttachments.takeFirst(); + } else { + model = new Model(this); + } model->init(); _attachmentModels.append(model); } while (_attachmentModels.size() > attachmentData.size()) { - delete _attachmentModels.takeLast(); + auto attachmentModel = _attachmentModels.takeLast(); + _attachmentsToRemove << attachmentModel; } // update the urls diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index dbc59f7d9c..b198d12a6e 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -195,6 +195,8 @@ protected: SkeletonModel _skeletonModel; glm::vec3 _skeletonOffset; QVector<Model*> _attachmentModels; + QVector<Model*> _attachmentsToRemove; + QVector<Model*> _unusedAttachments; float _bodyYawDelta; // These position histories and derivatives are in the world-frame. @@ -234,9 +236,9 @@ protected: void renderDisplayName(RenderArgs* renderArgs); virtual void renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, bool postLighting, float glowLevel = 0.0f); virtual bool shouldRenderHead(const RenderArgs* renderArgs, const glm::vec3& cameraPosition) const; + virtual void fixupModelsInScene(); void simulateAttachments(float deltaTime); - virtual void renderAttachments(RenderArgs* args); virtual void updateJointMappings(); diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp new file mode 100644 index 0000000000..1fbb01beb3 --- /dev/null +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -0,0 +1,108 @@ +// +// AvatarActionHold.cpp +// interface/src/avatar/ +// +// Created by Seth Alves 2015-6-9 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "avatar/MyAvatar.h" +#include "avatar/AvatarManager.h" + +#include "AvatarActionHold.h" + +AvatarActionHold::AvatarActionHold(QUuid id, EntityItemPointer ownerEntity) : + ObjectActionSpring(id, ownerEntity) { + #if WANT_DEBUG + qDebug() << "AvatarActionHold::AvatarActionHold"; + #endif +} + +AvatarActionHold::~AvatarActionHold() { + #if WANT_DEBUG + qDebug() << "AvatarActionHold::~AvatarActionHold"; + #endif +} + +void AvatarActionHold::updateActionWorker(float deltaTimeStep) { + auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar(); + glm::vec3 palmPosition; + if (_hand == "right") { + palmPosition = myAvatar->getRightPalmPosition(); + } else { + palmPosition = myAvatar->getLeftPalmPosition(); + } + + auto rotation = myAvatar->getWorldAlignedOrientation(); + auto offset = rotation * _relativePosition; + auto position = palmPosition + offset; + rotation *= _relativeRotation; + + lockForWrite(); + _positionalTarget = position; + _rotationalTarget = rotation; + unlock(); + + ObjectActionSpring::updateActionWorker(deltaTimeStep); +} + + +bool AvatarActionHold::updateArguments(QVariantMap arguments) { + bool rPOk = true; + glm::vec3 relativePosition = + EntityActionInterface::extractVec3Argument("hold", arguments, "relativePosition", rPOk, false); + bool rROk = true; + glm::quat relativeRotation = + EntityActionInterface::extractQuatArgument("hold", arguments, "relativeRotation", rROk, false); + bool tSOk = true; + float timeScale = + EntityActionInterface::extractFloatArgument("hold", arguments, "timeScale", tSOk, false); + bool hOk = true; + QString hand = + EntityActionInterface::extractStringArgument("hold", arguments, "hand", hOk, false); + + lockForWrite(); + if (rPOk) { + _relativePosition = relativePosition; + } else if (!_parametersSet) { + _relativePosition = glm::vec3(0.0f, 0.0f, 1.0f); + } + + if (rROk) { + _relativeRotation = relativeRotation; + } else if (!_parametersSet) { + _relativeRotation = glm::quat(0.0f, 0.0f, 0.0f, 1.0f); + } + + if (tSOk) { + _linearTimeScale = timeScale; + _angularTimeScale = timeScale; + } else if (!_parametersSet) { + _linearTimeScale = 0.2; + _angularTimeScale = 0.2; + } + + if (hOk) { + hand = hand.toLower(); + if (hand == "left") { + _hand = "left"; + } else if (hand == "right") { + _hand = "right"; + } else { + qDebug() << "hold action -- invalid hand argument:" << hand; + _hand = "right"; + } + } else if (!_parametersSet) { + _hand = "right"; + } + + _parametersSet = true; + _positionalTargetSet = true; + _rotationalTargetSet = true; + _active = true; + unlock(); + return true; +} diff --git a/interface/src/avatar/AvatarActionHold.h b/interface/src/avatar/AvatarActionHold.h new file mode 100644 index 0000000000..705c751029 --- /dev/null +++ b/interface/src/avatar/AvatarActionHold.h @@ -0,0 +1,35 @@ +// +// AvatarActionHold.h +// interface/src/avatar/ +// +// Created by Seth Alves 2015-6-9 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_AvatarActionHold_h +#define hifi_AvatarActionHold_h + +#include <QUuid> + +#include <EntityItem.h> +#include <ObjectActionSpring.h> + +class AvatarActionHold : public ObjectActionSpring { +public: + AvatarActionHold(QUuid id, EntityItemPointer ownerEntity); + virtual ~AvatarActionHold(); + + virtual bool updateArguments(QVariantMap arguments); + virtual void updateActionWorker(float deltaTimeStep); + +private: + glm::vec3 _relativePosition; + glm::quat _relativeRotation; + QString _hand; + bool _parametersSet = false; +}; + +#endif // hifi_AvatarActionHold_h diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 8a49d69129..c4e08b5dba 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1178,44 +1178,10 @@ void MyAvatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, bo // check to see if when we added our models to the scene they were ready, if they were not ready, then // fix them up in the scene - render::ScenePointer scene = Application::getInstance()->getMain3DScene(); - render::PendingChanges pendingChanges; - if (_skeletonModel.needsFixupInScene()) { - _skeletonModel.removeFromScene(scene, pendingChanges); - _skeletonModel.addToScene(scene, pendingChanges); - } - if (getHead()->getFaceModel().needsFixupInScene()) { - getHead()->getFaceModel().removeFromScene(scene, pendingChanges); - getHead()->getFaceModel().addToScene(scene, pendingChanges); - } - scene->enqueuePendingChanges(pendingChanges); + fixupModelsInScene(); - Camera *camera = Application::getInstance()->getCamera(); - const glm::vec3 cameraPos = camera->getPosition(); + const glm::vec3 cameraPos = Application::getInstance()->getCamera()->getPosition(); - - // HACK: comment this block which possibly change the near and break the rendering 5/6/2015 - // Only tweak the frustum near far if it's not shadow - /* if (renderMode != RenderArgs::SHADOW_RENDER_MODE) { - // Set near clip distance according to skeleton model dimensions if first person and there is no separate head model. - if (shouldRenderHead(cameraPos, renderMode) || !getHead()->getFaceModel().getURL().isEmpty()) { - renderFrustum->setNearClip(DEFAULT_NEAR_CLIP); - } else { - float clipDistance = _skeletonModel.getHeadClipDistance(); - clipDistance = glm::length(getEyePosition() - + camera->getOrientation() * glm::vec3(0.0f, 0.0f, -clipDistance) - cameraPos); - renderFrustum->setNearClip(clipDistance); - } - }*/ - - // Render the body's voxels and head - if (!postLighting) { - - // NOTE: we no longer call this here, because we've added all the model parts as renderable items in the scene - //_skeletonModel.render(renderArgs, 1.0f); - renderAttachments(renderArgs); - } - // Render head so long as the camera isn't inside it if (shouldRenderHead(renderArgs, cameraPos)) { getHead()->render(renderArgs, 1.0f, renderFrustum, postLighting); @@ -1571,27 +1537,6 @@ void MyAvatar::updateMotionBehavior() { _feetTouchFloor = menu->isOptionChecked(MenuOption::ShiftHipsForIdleAnimations); } -void MyAvatar::renderAttachments(RenderArgs* args) { - if (Application::getInstance()->getCamera()->getMode() != CAMERA_MODE_FIRST_PERSON || args->_renderMode == RenderArgs::MIRROR_RENDER_MODE) { - Avatar::renderAttachments(args); - return; - } - const FBXGeometry& geometry = _skeletonModel.getGeometry()->getFBXGeometry(); - QString headJointName = (geometry.headJointIndex == -1) ? QString() : geometry.joints.at(geometry.headJointIndex).name; - // RenderArgs::RenderMode modelRenderMode = (renderMode == RenderArgs::SHADOW_RENDER_MODE) ? - // RenderArgs::SHADOW_RENDER_MODE : RenderArgs::DEFAULT_RENDER_MODE; - - // FIX ME - attachments need to be added to scene too... - /* - for (int i = 0; i < _attachmentData.size(); i++) { - const QString& jointName = _attachmentData.at(i).jointName; - if (jointName != headJointName && jointName != "Head") { - _attachmentModels.at(i)->render(args, 1.0f); - } - } - */ -} - //Renders sixense laser pointers for UI selection with controllers void MyAvatar::renderLaserPointers() { const float PALM_TIP_ROD_RADIUS = 0.002f; diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index c8d16e8cb0..a3dc34e6e0 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -197,9 +197,6 @@ public slots: signals: void transformChanged(); -protected: - virtual void renderAttachments(RenderArgs* args); - private: // These are made private for MyAvatar so that you will use the "use" methods instead diff --git a/interface/src/devices/KeyboardMouseDevice.cpp b/interface/src/devices/KeyboardMouseDevice.cpp index 7b0f8c056c..8a336064e5 100755 --- a/interface/src/devices/KeyboardMouseDevice.cpp +++ b/interface/src/devices/KeyboardMouseDevice.cpp @@ -159,9 +159,25 @@ void KeyboardMouseDevice::registerToUserInputMapper(UserInputMapper& mapper) { // Grab the current free device ID _deviceID = mapper.getFreeDeviceID(); - auto proxy = UserInputMapper::DeviceProxy::Pointer(new UserInputMapper::DeviceProxy()); + auto proxy = UserInputMapper::DeviceProxy::Pointer(new UserInputMapper::DeviceProxy("Keyboard")); proxy->getButton = [this] (const UserInputMapper::Input& input, int timestamp) -> bool { return this->getButton(input._channel); }; proxy->getAxis = [this] (const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input._channel); }; + proxy->getAvailabeInputs = [this] () -> QVector<UserInputMapper::InputPair> { + QVector<UserInputMapper::InputPair> availableInputs; + for (int i = (int) Qt::Key_0; i <= (int) Qt::Key_9; i++) { + availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key(i)), QKeySequence(Qt::Key(i)).toString())); + } + for (int i = (int) Qt::Key_A; i <= (int) Qt::Key_Z; i++) { + availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key(i)), QKeySequence(Qt::Key(i)).toString())); + } + availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key_Space), QKeySequence(Qt::Key_Space).toString())); + return availableInputs; + }; + proxy->resetDeviceBindings = [this, &mapper] () -> bool { + mapper.removeAllInputChannelsForDevice(_deviceID); + this->assignDefaultInputMapping(mapper); + return true; + }; mapper.registerDevice(_deviceID, proxy); } diff --git a/interface/src/scripting/ControllerScriptingInterface.cpp b/interface/src/scripting/ControllerScriptingInterface.cpp index 5f12d73b37..2c747195a7 100644 --- a/interface/src/scripting/ControllerScriptingInterface.cpp +++ b/interface/src/scripting/ControllerScriptingInterface.cpp @@ -28,6 +28,94 @@ ControllerScriptingInterface::ControllerScriptingInterface() : { } +static int actionMetaTypeId = qRegisterMetaType<UserInputMapper::Action>(); +static int inputChannelMetaTypeId = qRegisterMetaType<UserInputMapper::InputChannel>(); +static int inputMetaTypeId = qRegisterMetaType<UserInputMapper::Input>(); +static int inputPairMetaTypeId = qRegisterMetaType<UserInputMapper::InputPair>(); + +QScriptValue inputToScriptValue(QScriptEngine* engine, const UserInputMapper::Input& input); +void inputFromScriptValue(const QScriptValue& object, UserInputMapper::Input& input); +QScriptValue inputChannelToScriptValue(QScriptEngine* engine, const UserInputMapper::InputChannel& inputChannel); +void inputChannelFromScriptValue(const QScriptValue& object, UserInputMapper::InputChannel& inputChannel); +QScriptValue actionToScriptValue(QScriptEngine* engine, const UserInputMapper::Action& action); +void actionFromScriptValue(const QScriptValue& object, UserInputMapper::Action& action); +QScriptValue inputPairToScriptValue(QScriptEngine* engine, const UserInputMapper::InputPair& inputPair); +void inputPairFromScriptValue(const QScriptValue& object, UserInputMapper::InputPair& inputPair); + +QScriptValue inputToScriptValue(QScriptEngine* engine, const UserInputMapper::Input& input) { + QScriptValue obj = engine->newObject(); + obj.setProperty("device", input.getDevice()); + obj.setProperty("channel", input.getChannel()); + obj.setProperty("type", (unsigned short) input.getType()); + obj.setProperty("id", input.getID()); + return obj; +} + +void inputFromScriptValue(const QScriptValue& object, UserInputMapper::Input& input) { + input.setDevice(object.property("device").toUInt16()); + input.setChannel(object.property("channel").toUInt16()); + input.setType(object.property("type").toUInt16()); + input.setID(object.property("id").toInt32()); +} + +QScriptValue inputChannelToScriptValue(QScriptEngine* engine, const UserInputMapper::InputChannel& inputChannel) { + QScriptValue obj = engine->newObject(); + obj.setProperty("input", inputToScriptValue(engine, inputChannel.getInput())); + obj.setProperty("modifier", inputToScriptValue(engine, inputChannel.getModifier())); + obj.setProperty("action", inputChannel.getAction()); + obj.setProperty("scale", inputChannel.getScale()); + return obj; +} + +void inputChannelFromScriptValue(const QScriptValue& object, UserInputMapper::InputChannel& inputChannel) { + UserInputMapper::Input input; + UserInputMapper::Input modifier; + inputFromScriptValue(object.property("input"), input); + inputChannel.setInput(input); + inputFromScriptValue(object.property("modifier"), modifier); + inputChannel.setModifier(modifier); + inputChannel.setAction(UserInputMapper::Action(object.property("action").toVariant().toInt())); + inputChannel.setScale(object.property("scale").toVariant().toFloat()); +} + +QScriptValue actionToScriptValue(QScriptEngine* engine, const UserInputMapper::Action& action) { + QScriptValue obj = engine->newObject(); + QVector<UserInputMapper::InputChannel> inputChannels = Application::getUserInputMapper()->getInputChannelsForAction(action); + QScriptValue _inputChannels = engine->newArray(inputChannels.size()); + for (int i = 0; i < inputChannels.size(); i++) { + _inputChannels.setProperty(i, inputChannelToScriptValue(engine, inputChannels[i])); + } + obj.setProperty("action", (int) action); + obj.setProperty("actionName", Application::getUserInputMapper()->getActionName(action)); + obj.setProperty("inputChannels", _inputChannels); + return obj; +} + +void actionFromScriptValue(const QScriptValue& object, UserInputMapper::Action& action) { + action = UserInputMapper::Action(object.property("action").toVariant().toInt()); +} + +QScriptValue inputPairToScriptValue(QScriptEngine* engine, const UserInputMapper::InputPair& inputPair) { + QScriptValue obj = engine->newObject(); + obj.setProperty("input", inputToScriptValue(engine, inputPair.first)); + obj.setProperty("inputName", inputPair.second); + return obj; +} + +void inputPairFromScriptValue(const QScriptValue& object, UserInputMapper::InputPair& inputPair) { + inputFromScriptValue(object.property("input"), inputPair.first); + inputPair.second = QString(object.property("inputName").toVariant().toString()); +} + +void ControllerScriptingInterface::registerControllerTypes(QScriptEngine* engine) { + qScriptRegisterSequenceMetaType<QVector<UserInputMapper::Action> >(engine); + qScriptRegisterSequenceMetaType<QVector<UserInputMapper::InputChannel> >(engine); + qScriptRegisterSequenceMetaType<QVector<UserInputMapper::InputPair> >(engine); + qScriptRegisterMetaType(engine, actionToScriptValue, actionFromScriptValue); + qScriptRegisterMetaType(engine, inputChannelToScriptValue, inputChannelFromScriptValue); + qScriptRegisterMetaType(engine, inputToScriptValue, inputFromScriptValue); + qScriptRegisterMetaType(engine, inputPairToScriptValue, inputPairFromScriptValue); +} void ControllerScriptingInterface::handleMetaEvent(HFMetaEvent* event) { if (event->type() == HFActionEvent::startType()) { @@ -337,6 +425,37 @@ void ControllerScriptingInterface::updateInputControllers() { } } +QVector<UserInputMapper::Action> ControllerScriptingInterface::getAllActions() { + return Application::getUserInputMapper()->getAllActions(); +} + +QVector<UserInputMapper::InputChannel> ControllerScriptingInterface::getInputChannelsForAction(UserInputMapper::Action action) { + return Application::getUserInputMapper()->getInputChannelsForAction(action); +} + +QString ControllerScriptingInterface::getDeviceName(unsigned int device) { + return Application::getUserInputMapper()->getDeviceName((unsigned short) device); +} + +QVector<UserInputMapper::InputChannel> ControllerScriptingInterface::getAllInputsForDevice(unsigned int device) { + return Application::getUserInputMapper()->getAllInputsForDevice(device); +} + +bool ControllerScriptingInterface::addInputChannel(UserInputMapper::InputChannel inputChannel) { + return Application::getUserInputMapper()->addInputChannel(inputChannel._action, inputChannel._input, inputChannel._modifier, inputChannel._scale); +} + +bool ControllerScriptingInterface::removeInputChannel(UserInputMapper::InputChannel inputChannel) { + return Application::getUserInputMapper()->removeInputChannel(inputChannel); +} + +QVector<UserInputMapper::InputPair> ControllerScriptingInterface::getAvailableInputs(unsigned int device) { + return Application::getUserInputMapper()->getAvailableInputs((unsigned short) device); +} + +void ControllerScriptingInterface::resetAllDeviceBindings() { + Application::getUserInputMapper()->resetAllDeviceBindings(); +} InputController::InputController(int deviceTrackerId, int subTrackerId, QObject* parent) : AbstractInputController(), @@ -373,4 +492,4 @@ const unsigned int INPUTCONTROLLER_KEY_DEVICE_MASK = 16; InputController::Key InputController::getKey() const { return (((_deviceTrackerId & INPUTCONTROLLER_KEY_DEVICE_MASK) << INPUTCONTROLLER_KEY_DEVICE_OFFSET) | _subTrackerId); -} +} \ No newline at end of file diff --git a/interface/src/scripting/ControllerScriptingInterface.h b/interface/src/scripting/ControllerScriptingInterface.h index c088dd6c9a..9414dc887b 100644 --- a/interface/src/scripting/ControllerScriptingInterface.h +++ b/interface/src/scripting/ControllerScriptingInterface.h @@ -14,10 +14,11 @@ #include <QtCore/QObject> +#include "ui/UserInputMapper.h" + #include <AbstractControllerScriptingInterface.h> class PalmData; - class InputController : public AbstractInputController { Q_OBJECT @@ -54,6 +55,9 @@ class ControllerScriptingInterface : public AbstractControllerScriptingInterface public: ControllerScriptingInterface(); + + virtual void registerControllerTypes(QScriptEngine* engine); + void emitKeyPressEvent(QKeyEvent* event) { emit keyPressEvent(KeyEvent(*event)); } void emitKeyReleaseEvent(QKeyEvent* event) { emit keyReleaseEvent(KeyEvent(*event)); } @@ -79,6 +83,14 @@ public: void updateInputControllers(); public slots: + Q_INVOKABLE virtual QVector<UserInputMapper::Action> getAllActions(); + Q_INVOKABLE virtual QVector<UserInputMapper::InputChannel> getInputChannelsForAction(UserInputMapper::Action action); + Q_INVOKABLE virtual QString getDeviceName(unsigned int device); + Q_INVOKABLE virtual QVector<UserInputMapper::InputChannel> getAllInputsForDevice(unsigned int device); + Q_INVOKABLE virtual bool addInputChannel(UserInputMapper::InputChannel inputChannel); + Q_INVOKABLE virtual bool removeInputChannel(UserInputMapper::InputChannel inputChannel); + Q_INVOKABLE virtual QVector<UserInputMapper::InputPair> getAvailableInputs(unsigned int device); + Q_INVOKABLE virtual void resetAllDeviceBindings(); virtual bool isPrimaryButtonPressed() const; virtual glm::vec2 getPrimaryJoystickPosition() const; diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index 3aae6a4d4a..6be67a7261 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -31,7 +31,7 @@ WindowScriptingInterface::WindowScriptingInterface() : _formResult(QDialog::Rejected) { const DomainHandler& domainHandler = DependencyManager::get<NodeList>()->getDomainHandler(); - connect(&domainHandler, &DomainHandler::hostnameChanged, this, &WindowScriptingInterface::domainChanged); + connect(&domainHandler, &DomainHandler::connectedToDomain, this, &WindowScriptingInterface::domainChanged); connect(Application::getInstance(), &Application::svoImportRequested, this, &WindowScriptingInterface::svoImportRequested); connect(Application::getInstance(), &Application::domainConnectionRefused, this, &WindowScriptingInterface::domainConnectionRefused); } diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index bc8047fc98..0c1783f8ac 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -143,7 +143,6 @@ ApplicationOverlay::ApplicationOverlay() : _alpha(1.0f), _oculusUIRadius(1.0f), _trailingAudioLoudness(0.0f), - _crosshairTexture(0), _previousBorderWidth(-1), _previousBorderHeight(-1), _previousMagnifierBottomLeft(), @@ -591,8 +590,8 @@ void ApplicationOverlay::renderPointers() { glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_crosshairTexture)); + //glActiveTexture(GL_TEXTURE0); + //bindCursorTexture(); if (qApp->isHMDMode() && !qApp->getLastMouseMoveWasSimulated() && !qApp->isMouseHidden()) { //If we are in oculus, render reticle later @@ -637,8 +636,8 @@ void ApplicationOverlay::renderPointers() { _magActive[MOUSE] = false; renderControllerPointers(); } - glBindTexture(GL_TEXTURE_2D, 0); - glDisable(GL_TEXTURE_2D); + //glBindTexture(GL_TEXTURE_2D, 0); + //glDisable(GL_TEXTURE_2D); } void ApplicationOverlay::renderControllerPointers() { diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h index ee82c9274a..eb397fe3c6 100644 --- a/interface/src/ui/ApplicationOverlay.h +++ b/interface/src/ui/ApplicationOverlay.h @@ -96,7 +96,6 @@ private: }; float _hmdUIAngularSize = DEFAULT_HMD_UI_ANGULAR_SIZE; - void renderReticle(glm::quat orientation, float alpha); void renderPointers();; void renderMagnifier(glm::vec2 magPos, float sizeMult, bool showBorder); @@ -108,6 +107,7 @@ private: void renderCameraToggle(); void renderStatsAndLogs(); void renderDomainConnectionStatusBorder(); + void bindCursorTexture(gpu::Batch& batch, uint8_t cursorId = 0); TexturedHemisphere _overlays; @@ -125,9 +125,12 @@ private: float _alpha = 1.0f; float _oculusUIRadius; float _trailingAudioLoudness; + + gpu::TexturePointer _crosshairTexture; - gpu::TexturePointer _crosshairTexture; + QMap<uint16_t, gpu::TexturePointer> _cursors; + GLuint _newUiTexture{ 0 }; int _reticleQuad; diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index 32df75c46d..8359480b03 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -161,6 +161,10 @@ void Stats::drawBackground(unsigned int rgba, int x, int y, int width, int heigh ((rgba >> 8) & 0xff) / 255.0f, (rgba & 0xff) / 255.0f); + // FIX ME: is this correct? It seems to work to fix textures bleeding into us... + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); + DependencyManager::get<GeometryCache>()->renderQuad(x, y, width, height, color); } @@ -460,7 +464,7 @@ void Stats::display( verticalOffset = STATS_PELS_INITIALOFFSET; horizontalOffset = _lastHorizontalOffset + _generalStatsWidth + _pingStatsWidth + _geoStatsWidth + 3; - lines = _expanded ? 10 : 2; + lines = _expanded ? 10 : 3; drawBackground(backgroundColor, horizontalOffset, 0, canvasSize.x - horizontalOffset, (lines + 1) * STATS_PELS_PER_LINE); @@ -608,12 +612,10 @@ void Stats::display( } // LOD Details - if (_expanded) { - octreeStats.str(""); - QString displayLODDetails = DependencyManager::get<LODManager>()->getLODFeedbackText(); - octreeStats << "LOD: You can see " << qPrintable(displayLODDetails.trimmed()); - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color); - } + octreeStats.str(""); + QString displayLODDetails = DependencyManager::get<LODManager>()->getLODFeedbackText(); + octreeStats << "LOD: You can see " << qPrintable(displayLODDetails.trimmed()); + verticalOffset += STATS_PELS_PER_LINE; + drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color); } diff --git a/interface/src/ui/UserInputMapper.cpp b/interface/src/ui/UserInputMapper.cpp index 892ab6a9b6..e994b3cf30 100755 --- a/interface/src/ui/UserInputMapper.cpp +++ b/interface/src/ui/UserInputMapper.cpp @@ -13,7 +13,15 @@ // UserInputMapper Class + +// Default contruct allocate the poutput size with the current hardcoded action channels +UserInputMapper::UserInputMapper() { + assignDefaulActionScales(); + createActionNames(); +} + bool UserInputMapper::registerDevice(uint16 deviceID, const DeviceProxy::Pointer& proxy){ + proxy->_name += " (" + QString::number(deviceID) + ")"; _registeredDevices[deviceID] = proxy; return true; } @@ -27,6 +35,12 @@ UserInputMapper::DeviceProxy::Pointer UserInputMapper::getDeviceProxy(const Inpu } } +void UserInputMapper::resetAllDeviceBindings() { + for (auto device : _registeredDevices) { + device.second->resetDeviceBindings(); + } +} + bool UserInputMapper::addInputChannel(Action action, const Input& input, float scale) { return addInputChannel(action, input, Input(), scale); } @@ -37,7 +51,7 @@ bool UserInputMapper::addInputChannel(Action action, const Input& input, const I qDebug() << "UserInputMapper::addInputChannel: The input comes from a device #" << input.getDevice() << "is unknown. no inputChannel mapped."; return false; } - + auto inputChannel = InputChannel(input, modifier, action, scale); // Insert or replace the input to modifiers @@ -61,6 +75,37 @@ int UserInputMapper::addInputChannels(const InputChannels& channels) { return nbAdded; } +bool UserInputMapper::removeInputChannel(InputChannel inputChannel) { + // Remove from Input to Modifiers map + if (inputChannel.hasModifier()) { + _inputToModifiersMap.erase(inputChannel._input.getID()); + } + + // Remove from Action to Inputs map + std::pair<ActionToInputsMap::iterator, ActionToInputsMap::iterator> ret; + ret = _actionToInputsMap.equal_range(inputChannel._action); + for (ActionToInputsMap::iterator it=ret.first; it!=ret.second; ++it) { + if (it->second == inputChannel) { + _actionToInputsMap.erase(it); + return true; + } + } + + return false; +} + +void UserInputMapper::removeAllInputChannels() { + _inputToModifiersMap.clear(); + _actionToInputsMap.clear(); +} + +void UserInputMapper::removeAllInputChannelsForDevice(uint16 device) { + QVector<InputChannel> channels = getAllInputsForDevice(device); + for (auto& channel : channels) { + removeInputChannel(channel); + } +} + int UserInputMapper::getInputChannels(InputChannels& channels) const { for (auto& channel : _actionToInputsMap) { channels.push_back(channel.second); @@ -69,6 +114,20 @@ int UserInputMapper::getInputChannels(InputChannels& channels) const { return _actionToInputsMap.size(); } +QVector<UserInputMapper::InputChannel> UserInputMapper::getAllInputsForDevice(uint16 device) { + InputChannels allChannels; + getInputChannels(allChannels); + + QVector<InputChannel> channels; + for (InputChannel inputChannel : allChannels) { + if (inputChannel._input._device == device) { + channels.push_back(inputChannel); + } + } + + return channels; +} + void UserInputMapper::update(float deltaTime) { // Reset the axis state for next loop @@ -130,6 +189,24 @@ void UserInputMapper::update(float deltaTime) { } } +QVector<UserInputMapper::Action> UserInputMapper::getAllActions() { + QVector<Action> actions; + for (auto i = 0; i < NUM_ACTIONS; i++) { + actions.append(Action(i)); + } + return actions; +} + +QVector<UserInputMapper::InputChannel> UserInputMapper::getInputChannelsForAction(UserInputMapper::Action action) { + QVector<InputChannel> inputChannels; + std::pair <ActionToInputsMap::iterator, ActionToInputsMap::iterator> ret; + ret = _actionToInputsMap.equal_range(action); + for (ActionToInputsMap::iterator it=ret.first; it!=ret.second; ++it) { + inputChannels.append(it->second); + } + return inputChannels; +} + void UserInputMapper::assignDefaulActionScales() { _actionScales[LONGITUDINAL_BACKWARD] = 1.0f; // 1m per unit _actionScales[LONGITUDINAL_FORWARD] = 1.0f; // 1m per unit @@ -144,3 +221,20 @@ void UserInputMapper::assignDefaulActionScales() { _actionScales[BOOM_IN] = 1.0f; // 1m per unit _actionScales[BOOM_OUT] = 1.0f; // 1m per unit } + +// This is only necessary as long as the actions are hardcoded +// Eventually you can just add the string when you add the action +void UserInputMapper::createActionNames() { + _actionNames[LONGITUDINAL_BACKWARD] = "LONGITUDINAL_BACKWARD"; + _actionNames[LONGITUDINAL_FORWARD] = "LONGITUDINAL_FORWARD"; + _actionNames[LATERAL_LEFT] = "LATERAL_LEFT"; + _actionNames[LATERAL_RIGHT] = "LATERAL_RIGHT"; + _actionNames[VERTICAL_DOWN] = "VERTICAL_DOWN"; + _actionNames[VERTICAL_UP] = "VERTICAL_UP"; + _actionNames[YAW_LEFT] = "YAW_LEFT"; + _actionNames[YAW_RIGHT] = "YAW_RIGHT"; + _actionNames[PITCH_DOWN] = "PITCH_DOWN"; + _actionNames[PITCH_UP] = "PITCH_UP"; + _actionNames[BOOM_IN] = "BOOM_IN"; + _actionNames[BOOM_OUT] = "BOOM_OUT"; +} \ No newline at end of file diff --git a/interface/src/ui/UserInputMapper.h b/interface/src/ui/UserInputMapper.h index ab63bdbef7..0a08e277db 100755 --- a/interface/src/ui/UserInputMapper.h +++ b/interface/src/ui/UserInputMapper.h @@ -21,6 +21,7 @@ class UserInputMapper : public QObject { Q_OBJECT + Q_ENUMS(Action) public: typedef unsigned short uint16; typedef unsigned int uint32; @@ -51,8 +52,13 @@ public: uint16 getDevice() const { return _device; } uint16 getChannel() const { return _channel; } uint32 getID() const { return _id; } - ChannelType getType() const { return (ChannelType) _type; } + + void setDevice(uint16 device) { _device = device; } + void setChannel(uint16 channel) { _channel = channel; } + void setType(uint16 type) { _type = type; } + void setID(uint32 ID) { _id = ID; } + bool isButton() const { return getType() == ChannelType::BUTTON; } bool isAxis() const { return getType() == ChannelType::AXIS; } bool isJoint() const { return getType() == ChannelType::JOINT; } @@ -64,6 +70,7 @@ public: explicit Input(uint16 device, uint16 channel, ChannelType type) : _device(device), _channel(channel), _type(uint16(type)) {} Input(const Input& src) : _id(src._id) {} Input& operator = (const Input& src) { _id = src._id; return (*this); } + bool operator ==(const Input& right) const { return _id == right._id; } bool operator < (const Input& src) const { return _id < src._id; } }; @@ -83,22 +90,32 @@ public: typedef std::function<bool (const Input& input, int timestamp)> ButtonGetter; typedef std::function<float (const Input& input, int timestamp)> AxisGetter; typedef std::function<JointValue (const Input& input, int timestamp)> JointGetter; + typedef QPair<Input, QString> InputPair; + typedef std::function<QVector<InputPair> ()> AvailableInputGetter; + typedef std::function<bool ()> ResetBindings; + + typedef QVector<InputPair> AvailableInput; class DeviceProxy { public: - DeviceProxy() {} - - ButtonGetter getButton = [] (const Input& input, int timestamp) -> bool { return false; }; - AxisGetter getAxis = [] (const Input& input, int timestamp) -> bool { return 0.0f; }; - JointGetter getJoint = [] (const Input& input, int timestamp) -> JointValue { return JointValue(); }; - - typedef std::shared_ptr<DeviceProxy> Pointer; + DeviceProxy(QString name) { _name = name; } + + QString _name; + ButtonGetter getButton = [] (const Input& input, int timestamp) -> bool { return false; }; + AxisGetter getAxis = [] (const Input& input, int timestamp) -> bool { return 0.0f; }; + JointGetter getJoint = [] (const Input& input, int timestamp) -> JointValue { return JointValue(); }; + AvailableInputGetter getAvailabeInputs = [] () -> AvailableInput { return QVector<InputPair>(); }; + ResetBindings resetDeviceBindings = [] () -> bool { return true; }; + + typedef std::shared_ptr<DeviceProxy> Pointer; }; // GetFreeDeviceID should be called before registering a device to use an ID not used by a different device. uint16 getFreeDeviceID() { return _nextFreeDeviceID++; } bool registerDevice(uint16 deviceID, const DeviceProxy::Pointer& device); DeviceProxy::Pointer getDeviceProxy(const Input& input); - + QString getDeviceName(uint16 deviceID) { return _registeredDevices[deviceID]->_name; } + QVector<InputPair> getAvailableInputs(uint16 deviceID) { return _registeredDevices[deviceID]->getAvailabeInputs(); } + void resetAllDeviceBindings(); // Actions are the output channels of the Mapper, that's what the InputChannel map to // For now the Actions are hardcoded, this is bad, but we will fix that in the near future @@ -123,7 +140,12 @@ public: NUM_ACTIONS, }; + + std::vector<QString> _actionNames = std::vector<QString>(NUM_ACTIONS); + void createActionNames(); + QVector<Action> getAllActions(); + QString getActionName(Action action) { return UserInputMapper::_actionNames[(int) action]; } float getActionState(Action action) const { return _actionStates[action]; } void assignDefaulActionScales(); @@ -140,27 +162,43 @@ public: Input _modifier = Input(); // make it invalid by default, meaning no modifier Action _action = LONGITUDINAL_BACKWARD; float _scale = 0.0f; + + Input getInput() const { return _input; } + Input getModifier() const { return _modifier; } + Action getAction() const { return _action; } + float getScale() const { return _scale; } + + void setInput(Input input) { _input = input; } + void setModifier(Input modifier) { _modifier = modifier; } + void setAction(Action action) { _action = action; } + void setScale(float scale) { _scale = scale; } InputChannel() {} InputChannel(const Input& input, const Input& modifier, Action action, float scale = 1.0f) : _input(input), _modifier(modifier), _action(action), _scale(scale) {} InputChannel(const InputChannel& src) : InputChannel(src._input, src._modifier, src._action, src._scale) {} InputChannel& operator = (const InputChannel& src) { _input = src._input; _modifier = src._modifier; _action = src._action; _scale = src._scale; return (*this); } - + bool operator ==(const InputChannel& right) const { return _input == right._input && _modifier == right._modifier && _action == right._action && _scale == right._scale; } bool hasModifier() { return _modifier.isValid(); } }; typedef std::vector< InputChannel > InputChannels; // Add a bunch of input channels, return the true number of channels that successfully were added int addInputChannels(const InputChannels& channels); + // Remove the first found instance of the input channel from the input mapper, true if found + bool removeInputChannel(InputChannel channel); + void removeAllInputChannels(); + void removeAllInputChannelsForDevice(uint16 device); //Grab all the input channels currently in use, return the number int getInputChannels(InputChannels& channels) const; + QVector<InputChannel> getAllInputsForDevice(uint16 device); + QVector<InputChannel> getInputChannelsForAction(UserInputMapper::Action action); + std::multimap<Action, InputChannel> getActionToInputsMap() { return _actionToInputsMap; } // Update means go grab all the device input channels and update the output channel values void update(float deltaTime); - - // Default contruct allocate the poutput size with the current hardcoded action channels - UserInputMapper() { assignDefaulActionScales(); } + + UserInputMapper(); protected: typedef std::map<int, DeviceProxy::Pointer> DevicesMap; @@ -177,4 +215,12 @@ protected: std::vector<float> _actionScales = std::vector<float>(NUM_ACTIONS, 1.0f); }; +Q_DECLARE_METATYPE(UserInputMapper::InputPair) +Q_DECLARE_METATYPE(QVector<UserInputMapper::InputPair>) +Q_DECLARE_METATYPE(UserInputMapper::Input) +Q_DECLARE_METATYPE(UserInputMapper::InputChannel) +Q_DECLARE_METATYPE(QVector<UserInputMapper::InputChannel>) +Q_DECLARE_METATYPE(UserInputMapper::Action) +Q_DECLARE_METATYPE(QVector<UserInputMapper::Action>) + #endif // hifi_UserInputMapper_h diff --git a/interface/src/ui/overlays/Overlay.h b/interface/src/ui/overlays/Overlay.h index 1a808bc15c..0264c6e3c0 100644 --- a/interface/src/ui/overlays/Overlay.h +++ b/interface/src/ui/overlays/Overlay.h @@ -123,6 +123,7 @@ protected: namespace render { template <> const ItemKey payloadGetKey(const Overlay::Pointer& overlay); template <> const Item::Bound payloadGetBound(const Overlay::Pointer& overlay); + template <> int payloadGetLayer(const Overlay::Pointer& overlay); template <> void payloadRender(const Overlay::Pointer& overlay, RenderArgs* args); } diff --git a/interface/src/ui/overlays/OverlaysPayload.cpp b/interface/src/ui/overlays/OverlaysPayload.cpp index cf3262d05c..d5e4b34f6b 100644 --- a/interface/src/ui/overlays/OverlaysPayload.cpp +++ b/interface/src/ui/overlays/OverlaysPayload.cpp @@ -37,7 +37,7 @@ namespace render { template <> const ItemKey payloadGetKey(const Overlay::Pointer& overlay) { if (overlay->is3D() && !static_cast<Base3DOverlay*>(overlay.get())->getDrawOnHUD()) { if (static_cast<Base3DOverlay*>(overlay.get())->getDrawInFront()) { - return ItemKey::Builder().withTypeShape().withNoDepthSort().build(); + return ItemKey::Builder().withTypeShape().withLayered().build(); } else { return ItemKey::Builder::opaqueShape(); } @@ -53,6 +53,17 @@ namespace render { return AABox(glm::vec3(bounds.x(), bounds.y(), 0.0f), glm::vec3(bounds.width(), bounds.height(), 0.1f)); } } + template <> int payloadGetLayer(const Overlay::Pointer& overlay) { + // MAgic number while we are defining the layering mechanism: + const int LAYER_2D = 2; + const int LAYER_3D_FRONT = 1; + const int LAYER_3D = 0; + if (overlay->is3D()) { + return (static_cast<Base3DOverlay*>(overlay.get())->getDrawInFront() ? LAYER_3D_FRONT : LAYER_3D); + } else { + return LAYER_2D; + } + } template <> void payloadRender(const Overlay::Pointer& overlay, RenderArgs* args) { if (args) { glPushMatrix(); diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 340ca9374d..6450f25208 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -156,6 +156,7 @@ void AudioClient::audioMixerKilled() { _hasReceivedFirstPacket = false; _outgoingAvatarAudioSequenceNumber = 0; _stats.reset(); + emit disconnected(); } diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index 3b2c1c1ae6..642edde84a 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -186,6 +186,7 @@ signals: void deviceChanged(); void receivedFirstPacket(); + void disconnected(); protected: AudioClient(); diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 98cbc1f845..3e83c6f559 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -1012,7 +1012,9 @@ void EntityTreeRenderer::deletingEntity(const EntityItemID& entityID) { void EntityTreeRenderer::addingEntity(const EntityItemID& entityID) { checkAndCallPreload(entityID); auto entity = static_cast<EntityTree*>(_tree)->findEntityByID(entityID); - addEntityToScene(entity); + if (entity) { + addEntityToScene(entity); + } } void EntityTreeRenderer::addEntityToScene(EntityItemPointer entity) { diff --git a/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp b/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp index b2400b797e..23b93250bc 100644 --- a/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp @@ -9,15 +9,18 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include "RenderableBoxEntityItem.h" + #include <glm/gtx/quaternion.hpp> #include <gpu/GPUConfig.h> #include <gpu/Batch.h> #include <DeferredLightingEffect.h> +#include <ObjectMotionState.h> #include <PerfStat.h> -#include "RenderableBoxEntityItem.h" +#include "RenderableDebugableEntityItem.h" EntityItemPointer RenderableBoxEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { return EntityItemPointer(new RenderableBoxEntityItem(entityID, properties)); @@ -27,23 +30,11 @@ void RenderableBoxEntityItem::render(RenderArgs* args) { PerformanceTimer perfTimer("RenderableBoxEntityItem::render"); Q_ASSERT(getType() == EntityTypes::Box); glm::vec4 cubeColor(toGlm(getXColor()), getLocalRenderAlpha()); - - bool debugSimulationOwnership = args->_debugFlags & RenderArgs::RENDER_DEBUG_SIMULATION_OWNERSHIP; - bool highlightSimulationOwnership = false; - if (debugSimulationOwnership) { - auto nodeList = DependencyManager::get<NodeList>(); - const QUuid& myNodeID = nodeList->getSessionUUID(); - highlightSimulationOwnership = (getSimulatorID() == myNodeID); - } Q_ASSERT(args->_batch); gpu::Batch& batch = *args->_batch; - batch.setModelTransform(getTransformToCenter()); - if (highlightSimulationOwnership) { - DependencyManager::get<DeferredLightingEffect>()->renderWireCube(batch, 1.0f, cubeColor); - } else { - DependencyManager::get<DeferredLightingEffect>()->renderSolidCube(batch, 1.0f, cubeColor); - } + batch.setModelTransform(getTransformToCenter()); // we want to include the scale as well + DependencyManager::get<DeferredLightingEffect>()->renderSolidCube(batch, 1.0f, cubeColor); RenderableDebugableEntityItem::render(this, args); }; diff --git a/libraries/entities-renderer/src/RenderableBoxEntityItem.h b/libraries/entities-renderer/src/RenderableBoxEntityItem.h index 06a62706b9..b14da9ee22 100644 --- a/libraries/entities-renderer/src/RenderableBoxEntityItem.h +++ b/libraries/entities-renderer/src/RenderableBoxEntityItem.h @@ -13,7 +13,6 @@ #define hifi_RenderableBoxEntityItem_h #include <BoxEntityItem.h> -#include "RenderableDebugableEntityItem.h" #include "RenderableEntityItem.h" class RenderableBoxEntityItem : public BoxEntityItem { diff --git a/libraries/entities-renderer/src/RenderableDebugableEntityItem.cpp b/libraries/entities-renderer/src/RenderableDebugableEntityItem.cpp index ca81ae4f2b..6a511e0d30 100644 --- a/libraries/entities-renderer/src/RenderableDebugableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableDebugableEntityItem.cpp @@ -10,58 +10,54 @@ // +#include "RenderableDebugableEntityItem.h" #include <glm/gtx/quaternion.hpp> #include <gpu/GPUConfig.h> #include <gpu/Batch.h> - #include <DeferredLightingEffect.h> -#include <PhysicsEngine.h> - -#include "RenderableDebugableEntityItem.h" +#include <ObjectMotionState.h> void RenderableDebugableEntityItem::renderBoundingBox(EntityItem* entity, RenderArgs* args, float puffedOut, glm::vec4& color) { Q_ASSERT(args->_batch); gpu::Batch& batch = *args->_batch; - batch.setModelTransform(entity->getTransformToCenter()); + batch.setModelTransform(entity->getTransformToCenter()); // we want to include the scale as well DependencyManager::get<DeferredLightingEffect>()->renderWireCube(batch, 1.0f + puffedOut, color); } -void RenderableDebugableEntityItem::renderHoverDot(EntityItem* entity, RenderArgs* args) { - const int SLICES = 8, STACKS = 8; - float radius = 0.05f; - glm::vec4 blueColor(0.0f, 0.0f, 1.0f, 1.0f); - - Q_ASSERT(args->_batch); - gpu::Batch& batch = *args->_batch; - Transform transform = entity->getTransformToCenter(); - // Cancel true dimensions and set scale to 2 * radius (diameter) - transform.postScale(2.0f * glm::vec3(radius, radius, radius) / entity->getDimensions()); - batch.setModelTransform(transform); - DependencyManager::get<DeferredLightingEffect>()->renderSolidSphere(batch, 0.5f, SLICES, STACKS, blueColor); -} - void RenderableDebugableEntityItem::render(EntityItem* entity, RenderArgs* args) { - bool debugSimulationOwnership = args->_debugFlags & RenderArgs::RENDER_DEBUG_SIMULATION_OWNERSHIP; + if (args->_debugFlags & RenderArgs::RENDER_DEBUG_SIMULATION_OWNERSHIP) { + Q_ASSERT(args->_batch); + gpu::Batch& batch = *args->_batch; + + batch.setModelTransform(entity->getTransformToCenter()); // we want to include the scale as well + + auto nodeList = DependencyManager::get<NodeList>(); + const QUuid& myNodeID = nodeList->getSessionUUID(); + bool highlightSimulationOwnership = (entity->getSimulatorID() == myNodeID); + if (highlightSimulationOwnership) { + glm::vec4 greenColor(0.0f, 1.0f, 0.2f, 1.0f); + renderBoundingBox(entity, args, 0.08f, greenColor); + } - if (debugSimulationOwnership) { quint64 now = usecTimestampNow(); if (now - entity->getLastEditedFromRemote() < 0.1f * USECS_PER_SECOND) { glm::vec4 redColor(1.0f, 0.0f, 0.0f, 1.0f); - renderBoundingBox(entity, args, 0.2f, redColor); + renderBoundingBox(entity, args, 0.16f, redColor); } if (now - entity->getLastBroadcast() < 0.2f * USECS_PER_SECOND) { glm::vec4 yellowColor(1.0f, 1.0f, 0.2f, 1.0f); - renderBoundingBox(entity, args, 0.3f, yellowColor); + renderBoundingBox(entity, args, 0.24f, yellowColor); } ObjectMotionState* motionState = static_cast<ObjectMotionState*>(entity->getPhysicsInfo()); if (motionState && motionState->isActive()) { - renderHoverDot(entity, args); + glm::vec4 blueColor(0.0f, 0.0f, 1.0f, 1.0f); + renderBoundingBox(entity, args, 0.32f, blueColor); } } } diff --git a/libraries/entities-renderer/src/RenderableDebugableEntityItem.h b/libraries/entities-renderer/src/RenderableDebugableEntityItem.h index 758bac353b..2680d882f5 100644 --- a/libraries/entities-renderer/src/RenderableDebugableEntityItem.h +++ b/libraries/entities-renderer/src/RenderableDebugableEntityItem.h @@ -17,7 +17,6 @@ class RenderableDebugableEntityItem { public: static void renderBoundingBox(EntityItem* entity, RenderArgs* args, float puffedOut, glm::vec4& color); - static void renderHoverDot(EntityItem* entity, RenderArgs* args); static void render(EntityItem* entity, RenderArgs* args); }; diff --git a/libraries/entities-renderer/src/RenderableLineEntityItem.cpp b/libraries/entities-renderer/src/RenderableLineEntityItem.cpp index 4e5de331bb..65407c74e7 100644 --- a/libraries/entities-renderer/src/RenderableLineEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableLineEntityItem.cpp @@ -49,6 +49,7 @@ void RenderableLineEntityItem::render(RenderArgs* args) { batch._glLineWidth(getLineWidth()); if (getLinePoints().size() > 1) { + DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(batch); DependencyManager::get<GeometryCache>()->renderVertices(batch, gpu::LINE_STRIP, _lineVerticesID); } batch._glLineWidth(1.0f); diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index e0bc493a5c..14a64d289e 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -201,14 +201,6 @@ void RenderableModelEntityItem::render(RenderArgs* args) { glm::vec3 position = getPosition(); glm::vec3 dimensions = getDimensions(); - bool debugSimulationOwnership = args->_debugFlags & RenderArgs::RENDER_DEBUG_SIMULATION_OWNERSHIP; - bool highlightSimulationOwnership = false; - if (debugSimulationOwnership) { - auto nodeList = DependencyManager::get<NodeList>(); - const QUuid& myNodeID = nodeList->getSessionUUID(); - highlightSimulationOwnership = (getSimulatorID() == myNodeID); - } - if (hasModel()) { if (_model) { if (QUrl(getModelURL()) != _model->getURL()) { @@ -274,11 +266,6 @@ void RenderableModelEntityItem::render(RenderArgs* args) { } } } - - if (highlightSimulationOwnership) { - glm::vec4 greenColor(0.0f, 1.0f, 0.0f, 1.0f); - RenderableDebugableEntityItem::renderBoundingBox(this, args, 0.0f, greenColor); - } } else { glm::vec4 greenColor(0.0f, 1.0f, 0.0f, 1.0f); RenderableDebugableEntityItem::renderBoundingBox(this, args, 0.0f, greenColor); diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index 6a50cbf1cb..91c89bb183 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -53,6 +53,7 @@ void RenderableParticleEffectEntityItem::render(RenderArgs* args) { batch.setUniformTexture(0, _texture->getGPUTexture()); } batch.setModelTransform(getTransformToCenter()); + DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(batch); DependencyManager::get<GeometryCache>()->renderVertices(batch, gpu::QUADS, _cacheID); }; diff --git a/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp b/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp index d5cb7d11b8..6d9cb525d6 100644 --- a/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp @@ -9,6 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include "RenderableSphereEntityItem.h" + #include <glm/gtx/quaternion.hpp> #include <gpu/GPUConfig.h> @@ -18,7 +20,7 @@ #include <DeferredLightingEffect.h> #include <PerfStat.h> -#include "RenderableSphereEntityItem.h" +#include "RenderableDebugableEntityItem.h" EntityItemPointer RenderableSphereEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { return EntityItemPointer(new RenderableSphereEntityItem(entityID, properties)); @@ -37,6 +39,8 @@ void RenderableSphereEntityItem::render(RenderArgs* args) { Q_ASSERT(args->_batch); gpu::Batch& batch = *args->_batch; - batch.setModelTransform(getTransformToCenter()); + batch.setModelTransform(getTransformToCenter()); // use a transform with scale, rotation, registration point and translation DependencyManager::get<DeferredLightingEffect>()->renderSolidSphere(batch, 0.5f, SLICES, STACKS, sphereColor); + + RenderableDebugableEntityItem::render(this, args); }; diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp index 8a67bb99f2..d06ffb9400 100644 --- a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp @@ -47,12 +47,13 @@ void RenderableTextEntityItem::render(RenderArgs* args) { glm::vec3 maxCorner = glm::vec3(dimensions.x, 0.0f, SLIGHTLY_BEHIND); DependencyManager::get<DeferredLightingEffect>()->renderQuad(batch, minCorner, maxCorner, backgroundColor); - float scale = _lineHeight / _textRenderer->getRowHeight(); + float scale = _lineHeight / _textRenderer->getFontSize(); transformToTopLeft.setScale(scale); // Scale to have the correct line height batch.setModelTransform(transformToTopLeft); - float leftMargin = 0.5f * _lineHeight, topMargin = 0.5f * _lineHeight; - glm::vec2 bounds = glm::vec2(dimensions.x - 2.0f * leftMargin, dimensions.y - 2.0f * topMargin); + float leftMargin = 0.1f * _lineHeight, topMargin = 0.1f * _lineHeight; + glm::vec2 bounds = glm::vec2(dimensions.x - 2.0f * leftMargin, + dimensions.y - 2.0f * topMargin); _textRenderer->draw(batch, leftMargin / scale, -topMargin / scale, _text, textColor, bounds / scale); } diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp index 9c4f8ae0bb..8c147cac05 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp @@ -14,6 +14,7 @@ #include <gpu/GPUConfig.h> #include <gpu/Batch.h> +#include <AbstractViewStateInterface.h> #include <DeferredLightingEffect.h> #include <DependencyManager.h> #include <GeometryCache.h> @@ -100,10 +101,17 @@ void RenderableZoneEntityItem::render(RenderArgs* args) { case SHAPE_TYPE_COMPOUND: { PerformanceTimer perfTimer("zone->renderCompound"); updateGeometry(); - - if (_model && _model->isActive()) { - // FIX ME: this is no longer available... we need to switch to payloads - //_model->renderInScene(getLocalRenderAlpha(), args); + if (_model && _model->needsFixupInScene()) { + // check to see if when we added our models to the scene they were ready, if they were not ready, then + // fix them up in the scene + render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene(); + render::PendingChanges pendingChanges; + _model->removeFromScene(scene, pendingChanges); + _model->addToScene(scene, pendingChanges); + + scene->enqueuePendingChanges(pendingChanges); + + _model->setVisibleInScene(getVisible(), scene); } break; } @@ -131,6 +139,15 @@ void RenderableZoneEntityItem::render(RenderArgs* args) { break; } } + + if ((!_drawZoneBoundaries || getShapeType() != SHAPE_TYPE_COMPOUND) && + _model && !_model->needsFixupInScene()) { + // If the model is in the scene but doesn't need to be, remove it. + render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene(); + render::PendingChanges pendingChanges; + _model->removeFromScene(scene, pendingChanges); + scene->enqueuePendingChanges(pendingChanges); + } } bool RenderableZoneEntityItem::contains(const glm::vec3& point) const { @@ -145,3 +162,51 @@ bool RenderableZoneEntityItem::contains(const glm::vec3& point) const { return false; } + +class RenderableZoneEntityItemMeta { +public: + RenderableZoneEntityItemMeta(EntityItemPointer entity) : entity(entity){ } + typedef render::Payload<RenderableZoneEntityItemMeta> Payload; + typedef Payload::DataPointer Pointer; + + EntityItemPointer entity; +}; + +namespace render { + template <> const ItemKey payloadGetKey(const RenderableZoneEntityItemMeta::Pointer& payload) { + return ItemKey::Builder::opaqueShape(); + } + + template <> const Item::Bound payloadGetBound(const RenderableZoneEntityItemMeta::Pointer& payload) { + if (payload && payload->entity) { + return payload->entity->getAABox(); + } + return render::Item::Bound(); + } + template <> void payloadRender(const RenderableZoneEntityItemMeta::Pointer& payload, RenderArgs* args) { + if (args) { + if (payload && payload->entity) { + payload->entity->render(args); + } + } + } +} + +bool RenderableZoneEntityItem::addToScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene, + render::PendingChanges& pendingChanges) { + _myMetaItem = scene->allocateID(); + + auto renderData = RenderableZoneEntityItemMeta::Pointer(new RenderableZoneEntityItemMeta(self)); + auto renderPayload = render::PayloadPointer(new RenderableZoneEntityItemMeta::Payload(renderData)); + + pendingChanges.resetItem(_myMetaItem, renderPayload); + return true; +} + +void RenderableZoneEntityItem::removeFromScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene, + render::PendingChanges& pendingChanges) { + pendingChanges.removeItem(_myMetaItem); + if (_model) { + _model->removeFromScene(scene, pendingChanges); + } +} diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.h b/libraries/entities-renderer/src/RenderableZoneEntityItem.h index b2a9791d44..f455ea34de 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.h +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.h @@ -35,6 +35,9 @@ public: virtual void render(RenderArgs* args); virtual bool contains(const glm::vec3& point) const; + virtual bool addToScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges); + virtual void removeFromScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges); + private: Model* getModel(); void initialSimulation(); @@ -45,6 +48,8 @@ private: Model* _model; bool _needsInitialSimulation; + + render::ItemID _myMetaItem; }; #endif // hifi_RenderableZoneEntityItem_h diff --git a/libraries/entities/src/EntityActionFactoryInterface.h b/libraries/entities/src/EntityActionFactoryInterface.h new file mode 100644 index 0000000000..9820313aae --- /dev/null +++ b/libraries/entities/src/EntityActionFactoryInterface.h @@ -0,0 +1,33 @@ +// +// EntityActionFactoryInterface.cpp +// libraries/entities/src +// +// Created by Seth Alves on 2015-6-2 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_EntityActionFactoryInterface_h +#define hifi_EntityActionFactoryInterface_h + +#include <DependencyManager.h> + +#include "EntityActionInterface.h" + +class EntityActionFactoryInterface : public QObject, public Dependency { + Q_OBJECT + SINGLETON_DEPENDENCY + + public: + EntityActionFactoryInterface() { } + virtual ~EntityActionFactoryInterface() { } + virtual EntityActionPointer factory(EntitySimulation* simulation, + EntityActionType type, + QUuid id, + EntityItemPointer ownerEntity, + QVariantMap arguments) { assert(false); return nullptr; } +}; + +#endif // hifi_EntityActionFactoryInterface_h diff --git a/libraries/entities/src/EntityActionInterface.cpp b/libraries/entities/src/EntityActionInterface.cpp index f26dd006ff..e97669686c 100644 --- a/libraries/entities/src/EntityActionInterface.cpp +++ b/libraries/entities/src/EntityActionInterface.cpp @@ -22,6 +22,12 @@ EntityActionType EntityActionInterface::actionTypeFromString(QString actionTypeS if (normalizedActionTypeString == "pulltopoint") { return ACTION_TYPE_PULL_TO_POINT; } + if (normalizedActionTypeString == "spring") { + return ACTION_TYPE_SPRING; + } + if (normalizedActionTypeString == "hold") { + return ACTION_TYPE_HOLD; + } qDebug() << "Warning -- EntityActionInterface::actionTypeFromString got unknown action-type name" << actionTypeString; return ACTION_TYPE_NONE; @@ -33,31 +39,37 @@ QString EntityActionInterface::actionTypeToString(EntityActionType actionType) { return "none"; case ACTION_TYPE_PULL_TO_POINT: return "pullToPoint"; + case ACTION_TYPE_SPRING: + return "spring"; + case ACTION_TYPE_HOLD: + return "hold"; } assert(false); return "none"; } glm::vec3 EntityActionInterface::extractVec3Argument(QString objectName, QVariantMap arguments, - QString argumentName, bool& ok) { + QString argumentName, bool& ok, bool required) { if (!arguments.contains(argumentName)) { - qDebug() << objectName << "requires argument:" << argumentName; + if (required) { + qDebug() << objectName << "requires argument:" << argumentName; + } ok = false; - return vec3(); + return glm::vec3(); } QVariant resultV = arguments[argumentName]; if (resultV.type() != (QVariant::Type) QMetaType::QVariantMap) { qDebug() << objectName << "argument" << argumentName << "must be a map"; ok = false; - return vec3(); + return glm::vec3(); } QVariantMap resultVM = resultV.toMap(); if (!resultVM.contains("x") || !resultVM.contains("y") || !resultVM.contains("z")) { qDebug() << objectName << "argument" << argumentName << "must be a map with keys of x, y, z"; ok = false; - return vec3(); + return glm::vec3(); } QVariant xV = resultVM["x"]; @@ -73,17 +85,65 @@ glm::vec3 EntityActionInterface::extractVec3Argument(QString objectName, QVarian if (!xOk || !yOk || !zOk) { qDebug() << objectName << "argument" << argumentName << "must be a map with keys of x, y, z and values of type float."; ok = false; - return vec3(); + return glm::vec3(); } - return vec3(x, y, z); + return glm::vec3(x, y, z); } +glm::quat EntityActionInterface::extractQuatArgument(QString objectName, QVariantMap arguments, + QString argumentName, bool& ok, bool required) { + if (!arguments.contains(argumentName)) { + if (required) { + qDebug() << objectName << "requires argument:" << argumentName; + } + ok = false; + return glm::quat(); + } + + QVariant resultV = arguments[argumentName]; + if (resultV.type() != (QVariant::Type) QMetaType::QVariantMap) { + qDebug() << objectName << "argument" << argumentName << "must be a map, not" << resultV.typeName(); + ok = false; + return glm::quat(); + } + + QVariantMap resultVM = resultV.toMap(); + if (!resultVM.contains("x") || !resultVM.contains("y") || !resultVM.contains("z")) { + qDebug() << objectName << "argument" << argumentName << "must be a map with keys of x, y, z"; + ok = false; + return glm::quat(); + } + + QVariant xV = resultVM["x"]; + QVariant yV = resultVM["y"]; + QVariant zV = resultVM["z"]; + QVariant wV = resultVM["w"]; + + bool xOk = true; + bool yOk = true; + bool zOk = true; + bool wOk = true; + float x = xV.toFloat(&xOk); + float y = yV.toFloat(&yOk); + float z = zV.toFloat(&zOk); + float w = wV.toFloat(&wOk); + if (!xOk || !yOk || !zOk || !wOk) { + qDebug() << objectName << "argument" << argumentName + << "must be a map with keys of x, y, z, w and values of type float."; + ok = false; + return glm::quat(); + } + + return glm::quat(w, x, y, z); +} float EntityActionInterface::extractFloatArgument(QString objectName, QVariantMap arguments, - QString argumentName, bool& ok) { + QString argumentName, bool& ok, bool required) { if (!arguments.contains(argumentName)) { - qDebug() << objectName << "requires argument:" << argumentName; + if (required) { + qDebug() << objectName << "requires argument:" << argumentName; + } ok = false; return 0.0f; } @@ -99,3 +159,18 @@ float EntityActionInterface::extractFloatArgument(QString objectName, QVariantMa return v; } + +QString EntityActionInterface::extractStringArgument(QString objectName, QVariantMap arguments, + QString argumentName, bool& ok, bool required) { + if (!arguments.contains(argumentName)) { + if (required) { + qDebug() << objectName << "requires argument:" << argumentName; + } + ok = false; + return ""; + } + + QVariant vV = arguments[argumentName]; + QString v = vV.toString(); + return v; +} diff --git a/libraries/entities/src/EntityActionInterface.h b/libraries/entities/src/EntityActionInterface.h index 74efae3239..486d3f5948 100644 --- a/libraries/entities/src/EntityActionInterface.h +++ b/libraries/entities/src/EntityActionInterface.h @@ -14,12 +14,16 @@ #include <QUuid> +#include "EntityItem.h" + class EntitySimulation; enum EntityActionType { // keep these synchronized with actionTypeFromString and actionTypeToString ACTION_TYPE_NONE, - ACTION_TYPE_PULL_TO_POINT + ACTION_TYPE_PULL_TO_POINT, + ACTION_TYPE_SPRING, + ACTION_TYPE_HOLD }; @@ -32,18 +36,35 @@ public: virtual const EntityItemPointer& getOwnerEntity() const = 0; virtual void setOwnerEntity(const EntityItemPointer ownerEntity) = 0; virtual bool updateArguments(QVariantMap arguments) = 0; - // virtual QByteArray serialize() = 0; - // static EntityActionPointer deserialize(EntityItemPointer ownerEntity, QByteArray data); static EntityActionType actionTypeFromString(QString actionTypeString); static QString actionTypeToString(EntityActionType actionType); protected: + virtual glm::vec3 getPosition() = 0; + virtual void setPosition(glm::vec3 position) = 0; + virtual glm::quat getRotation() = 0; + virtual void setRotation(glm::quat rotation) = 0; + virtual glm::vec3 getLinearVelocity() = 0; + virtual void setLinearVelocity(glm::vec3 linearVelocity) = 0; + virtual glm::vec3 getAngularVelocity() = 0; + virtual void setAngularVelocity(glm::vec3 angularVelocity) = 0; + + // these look in the arguments map for a named argument. if it's not found or isn't well formed, + // ok will be set to false (note that it's never set to true -- set it to true before calling these). + // if required is true, failure to extract an argument will cause a warning to be printed. + static glm::vec3 extractVec3Argument (QString objectName, QVariantMap arguments, + QString argumentName, bool& ok, bool required = true); + static glm::quat extractQuatArgument (QString objectName, QVariantMap arguments, + QString argumentName, bool& ok, bool required = true); + static float extractFloatArgument(QString objectName, QVariantMap arguments, + QString argumentName, bool& ok, bool required = true); + static QString extractStringArgument(QString objectName, QVariantMap arguments, + QString argumentName, bool& ok, bool required = true); - static glm::vec3 extractVec3Argument(QString objectName, QVariantMap arguments, QString argumentName, bool& ok); - static float extractFloatArgument(QString objectName, QVariantMap arguments, QString argumentName, bool& ok); }; + typedef std::shared_ptr<EntityActionInterface> EntityActionPointer; #endif // hifi_EntityActionInterface_h diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 4a10aad95d..f64dad0ef4 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -70,7 +70,9 @@ EntityItem::EntityItem(const EntityItemID& entityItemID) : _dirtyFlags(0), _element(nullptr), _physicsInfo(nullptr), - _simulated(false) + _simulated(false), + _href(""), + _description("") { quint64 now = usecTimestampNow(); _lastSimulated = now; @@ -117,6 +119,8 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param requestedProperties += PROP_MARKETPLACE_ID; requestedProperties += PROP_NAME; requestedProperties += PROP_SIMULATOR_ID; + requestedProperties += PROP_HREF; + requestedProperties += PROP_DESCRIPTION; return requestedProperties; } @@ -246,6 +250,9 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet APPEND_ENTITY_PROPERTY(PROP_MARKETPLACE_ID, getMarketplaceID()); APPEND_ENTITY_PROPERTY(PROP_NAME, getName()); APPEND_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, getCollisionSoundURL()); + APPEND_ENTITY_PROPERTY(PROP_HREF, getHref()); + APPEND_ENTITY_PROPERTY(PROP_DESCRIPTION, getDescription()); + appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties, @@ -573,6 +580,9 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef READ_ENTITY_PROPERTY(PROP_NAME, QString, setName); READ_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, QString, setCollisionSoundURL); + READ_ENTITY_PROPERTY(PROP_HREF, QString, setHref); + READ_ENTITY_PROPERTY(PROP_DESCRIPTION, QString, setDescription); + bytesRead += readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, propertyFlags, overwriteLocalData); //////////////////////////////////// @@ -905,6 +915,8 @@ EntityItemProperties EntityItem::getProperties() const { COPY_ENTITY_PROPERTY_TO_PROPERTIES(simulatorID, getSimulatorID); COPY_ENTITY_PROPERTY_TO_PROPERTIES(marketplaceID, getMarketplaceID); COPY_ENTITY_PROPERTY_TO_PROPERTIES(name, getName); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(href, getHref); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(description, getDescription); properties._defaultSettings = false; @@ -963,6 +975,8 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) { SET_ENTITY_PROPERTY_FROM_PROPERTIES(userData, setUserData); SET_ENTITY_PROPERTY_FROM_PROPERTIES(marketplaceID, setMarketplaceID); SET_ENTITY_PROPERTY_FROM_PROPERTIES(name, setName); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(href, setHref); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(description, setDescription); if (somethingChanged) { uint64_t now = usecTimestampNow(); diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 77a6627853..73f9127361 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -28,13 +28,16 @@ #include "EntityItemID.h" #include "EntityItemProperties.h" #include "EntityItemPropertiesDefaults.h" -#include "EntityActionInterface.h" #include "EntityTypes.h" class EntitySimulation; class EntityTreeElement; class EntityTreeElementExtraEncodeData; +class EntityActionInterface; +typedef std::shared_ptr<EntityActionInterface> EntityActionPointer; + + namespace render { class Scene; class PendingChanges; @@ -203,7 +206,14 @@ public: inline const glm::quat& getRotation() const { return _transform.getRotation(); } inline void setRotation(const glm::quat& rotation) { _transform.setRotation(rotation); } - + + // Hyperlink related getters and setters + QString getHref() const { return _href; } + void setHref(QString value) { _href = value; } + + QString getDescription() const { return _description; } + void setDescription(QString value) { _description = value; } + /// Dimensions in meters (0.0 - TREE_SCALE) inline const glm::vec3& getDimensions() const { return _transform.getScale(); } virtual void setDimensions(const glm::vec3& value); @@ -415,6 +425,8 @@ protected: quint64 _simulatorIDChangedTime; // when was _simulatorID last updated? QString _marketplaceID; QString _name; + QString _href; //Hyperlink href + QString _description; //Hyperlink description // NOTE: Damping is applied like this: v *= pow(1 - damping, dt) // diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 90f2b22698..cbb3b1dc31 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -347,6 +347,9 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_VOXEL_SURFACE_STYLE, voxelSurfaceStyle); CHECK_PROPERTY_CHANGE(PROP_LINE_WIDTH, lineWidth); CHECK_PROPERTY_CHANGE(PROP_LINE_POINTS, linePoints); + CHECK_PROPERTY_CHANGE(PROP_HREF, href); + CHECK_PROPERTY_CHANGE(PROP_DESCRIPTION, description); + changedProperties += _stage.getChangedProperties(); changedProperties += _atmosphere.getChangedProperties(); changedProperties += _skybox.getChangedProperties(); @@ -439,7 +442,9 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(voxelSurfaceStyle); COPY_PROPERTY_TO_QSCRIPTVALUE(lineWidth); COPY_PROPERTY_TO_QSCRIPTVALUE(linePoints); - + COPY_PROPERTY_TO_QSCRIPTVALUE(href); + COPY_PROPERTY_TO_QSCRIPTVALUE(description); + // Sitting properties support if (!skipDefaults) { QScriptValue sittingPoints = engine->newObject(); @@ -548,6 +553,9 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE(voxelSurfaceStyle, uint16_t, setVoxelSurfaceStyle); COPY_PROPERTY_FROM_QSCRIPTVALUE(lineWidth, float, setLineWidth); COPY_PROPERTY_FROM_QSCRIPTVALUE(linePoints, qVectorVec3, setLinePoints); + COPY_PROPERTY_FROM_QSCRIPTVALUE(href, QString, setHref); + COPY_PROPERTY_FROM_QSCRIPTVALUE(description, QString, setDescription); + if (!honorReadOnly) { // this is used by the json reader to set things that we don't want javascript to able to affect. @@ -712,6 +720,8 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem APPEND_ENTITY_PROPERTY(PROP_LOCKED, properties.getLocked()); APPEND_ENTITY_PROPERTY(PROP_USER_DATA, properties.getUserData()); APPEND_ENTITY_PROPERTY(PROP_SIMULATOR_ID, properties.getSimulatorID()); + APPEND_ENTITY_PROPERTY(PROP_HREF, properties.getHref()); + APPEND_ENTITY_PROPERTY(PROP_DESCRIPTION, properties.getDescription()); if (properties.getType() == EntityTypes::Web) { APPEND_ENTITY_PROPERTY(PROP_SOURCE_URL, properties.getSourceUrl()); @@ -962,6 +972,8 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LOCKED, bool, setLocked); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_USER_DATA, QString, setUserData); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SIMULATOR_ID, QUuid, setSimulatorID); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_HREF, QString, setHref); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DESCRIPTION, QString, setDescription); if (properties.getType() == EntityTypes::Web) { READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SOURCE_URL, QString, setSourceUrl); @@ -1147,6 +1159,9 @@ void EntityItemProperties::markAllChanged() { _lineWidthChanged = true; _linePointsChanged = true; + _hrefChanged = true; + _descriptionChanged = true; + } /// The maximum bounding cube for the entity, independent of it's rotation. diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 3c8133af8f..068bc98f7e 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -148,6 +148,8 @@ public: DEFINE_PROPERTY_REF(PROP_SOURCE_URL, SourceUrl, sourceUrl, QString); DEFINE_PROPERTY(PROP_LINE_WIDTH, LineWidth, lineWidth, float); DEFINE_PROPERTY_REF(LINE_POINTS, LinePoints, linePoints, QVector<glm::vec3>); + DEFINE_PROPERTY_REF(PROP_HREF, Href, href, QString); + DEFINE_PROPERTY_REF(PROP_DESCRIPTION, Description, description, QString); static QString getBackgroundModeString(BackgroundMode mode); @@ -295,6 +297,8 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) { DEBUG_PROPERTY_IF_CHANGED(debug, properties, VoxelVolumeSize, voxelVolumeSize, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, VoxelData, voxelData, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, VoxelSurfaceStyle, voxelSurfaceStyle, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, Href, href, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, Description, description, ""); properties.getStage().debugDump(); properties.getAtmosphere().debugDump(); diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index 8eb09fece0..f1ebdb8a1f 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -117,6 +117,10 @@ enum EntityPropertyList { //for lines PROP_LINE_WIDTH, PROP_LINE_POINTS, + + // used by hyperlinks + PROP_HREF, + PROP_DESCRIPTION, //////////////////////////////////////////////////////////////////////////////////////////////////// // ATTENTION: add new properties ABOVE this line diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 836e7f5225..a091c786b7 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -17,6 +17,8 @@ #include "ZoneEntityItem.h" #include "EntitiesLogging.h" #include "EntitySimulation.h" +#include "EntityActionInterface.h" +#include "EntityActionFactoryInterface.h" #include "EntityScriptingInterface.h" @@ -491,12 +493,19 @@ QUuid EntityScriptingInterface::addAction(const QString& actionTypeString, const QUuid& entityID, const QVariantMap& arguments) { QUuid actionID = QUuid::createUuid(); + auto actionFactory = DependencyManager::get<EntityActionFactoryInterface>(); bool success = actionWorker(entityID, [&](EntitySimulation* simulation, EntityItemPointer entity) { + // create this action even if the entity doesn't have physics info. it will often be the + // case that a script adds an action immediately after an object is created, and the physicsInfo + // is computed asynchronously. + // if (!entity->getPhysicsInfo()) { + // return false; + // } EntityActionType actionType = EntityActionInterface::actionTypeFromString(actionTypeString); if (actionType == ACTION_TYPE_NONE) { return false; } - if (simulation->actionFactory(actionType, actionID, entity, arguments)) { + if (actionFactory->factory(simulation, actionType, actionID, entity, arguments)) { return true; } return false; diff --git a/libraries/entities/src/EntitySimulation.h b/libraries/entities/src/EntitySimulation.h index 0c9b3efee6..7d244086e5 100644 --- a/libraries/entities/src/EntitySimulation.h +++ b/libraries/entities/src/EntitySimulation.h @@ -18,6 +18,7 @@ #include <PerfStat.h> +#include "EntityActionInterface.h" #include "EntityItem.h" #include "EntityTree.h" @@ -56,10 +57,6 @@ public: friend class EntityTree; - virtual EntityActionPointer actionFactory(EntityActionType type, - QUuid id, - EntityItemPointer ownerEntity, - QVariantMap arguments) { return nullptr; } virtual void addAction(EntityActionPointer action) { _actionsToAdd += action; } virtual void removeAction(const QUuid actionID) { _actionsToRemove += actionID; } virtual void removeActions(QList<QUuid> actionIDsToRemove) { _actionsToRemove += actionIDsToRemove; } diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index f282f7e35d..de8f53c503 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -2646,34 +2646,6 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping, } } geometry.palmDirection = parseVec3(mapping.value("palmDirection", "0, -1, 0").toString()); - - // process attachments - QVariantHash attachments = mapping.value("attach").toHash(); - for (QVariantHash::const_iterator it = attachments.constBegin(); it != attachments.constEnd(); it++) { - FBXAttachment attachment; - attachment.jointIndex = modelIDs.indexOf(processID(it.key())); - attachment.scale = glm::vec3(1.0f, 1.0f, 1.0f); - - QVariantList properties = it->toList(); - if (properties.isEmpty()) { - attachment.url = it->toString(); - } else { - attachment.url = properties.at(0).toString(); - - if (properties.size() >= 2) { - attachment.translation = parseVec3(properties.at(1).toString()); - - if (properties.size() >= 3) { - attachment.rotation = glm::quat(glm::radians(parseVec3(properties.at(2).toString()))); - - if (properties.size() >= 4) { - attachment.scale = parseVec3(properties.at(3).toString()); - } - } - } - } - geometry.attachments.append(attachment); - } // Add sitting points QVariantHash sittingPoints = mapping.value("sit").toHash(); diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h index 08ac0e308c..200cd4a121 100644 --- a/libraries/fbx/src/FBXReader.h +++ b/libraries/fbx/src/FBXReader.h @@ -189,17 +189,6 @@ public: Q_DECLARE_METATYPE(FBXAnimationFrame) Q_DECLARE_METATYPE(QVector<FBXAnimationFrame>) -/// An attachment to an FBX document. -class FBXAttachment { -public: - - int jointIndex; - QUrl url; - glm::vec3 translation; - glm::quat rotation; - glm::vec3 scale; -}; - /// A point where an avatar can sit class SittingPoint { public: @@ -256,9 +245,7 @@ public: Extents meshExtents; QVector<FBXAnimationFrame> animationFrames; - - QVector<FBXAttachment> attachments; - + int getJointIndex(const QString& name) const { return jointIndices.value(name) - 1; } QStringList getJointNames() const; diff --git a/libraries/fbx/src/FSTReader.cpp b/libraries/fbx/src/FSTReader.cpp index 32be82b392..a62c0fcea2 100644 --- a/libraries/fbx/src/FSTReader.cpp +++ b/libraries/fbx/src/FSTReader.cpp @@ -124,7 +124,9 @@ FSTReader::ModelType FSTReader::getTypeFromName(const QString& name) { _namesToTypes["head"] = HEAD_MODEL ; _namesToTypes["body"] = BODY_ONLY_MODEL; _namesToTypes["body+head"] = HEAD_AND_BODY_MODEL; - _namesToTypes["attachment"] = ATTACHMENT_MODEL; + + // NOTE: this is not yet implemented, but will be used to allow you to attach fully independent models to your avatar + _namesToTypes["attachment"] = ATTACHMENT_MODEL; } return _namesToTypes[name]; } diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index 080fb98690..b7fea218d8 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -544,7 +544,6 @@ void fbxDebugDump(const FBXGeometry& fbxgeo) { qCDebug(modelformat) << "---------------- fbxGeometry ----------------"; qCDebug(modelformat) << " hasSkeletonJoints =" << fbxgeo.hasSkeletonJoints; qCDebug(modelformat) << " offset =" << fbxgeo.offset; - qCDebug(modelformat) << " attachments.count() = " << fbxgeo.attachments.count(); qCDebug(modelformat) << " meshes.count() =" << fbxgeo.meshes.count(); foreach (FBXMesh mesh, fbxgeo.meshes) { qCDebug(modelformat) << " vertices.count() =" << mesh.vertices.count(); diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index 642ca4748d..51575bfde5 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -218,7 +218,7 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const const QString DOMAIN_NETWORK_PORT_KEY = "network_port"; const QString DOMAIN_ICE_SERVER_ADDRESS_KEY = "ice_server_address"; - DependencyManager::get<NodeList>()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::HandleAddress); + DependencyManager::get<NodeList>()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::HandleAddress); const QString DOMAIN_ID_KEY = "id"; QString domainIDString = domainObject[DOMAIN_ID_KEY].toString(); diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 23237cab32..186ff40f60 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -180,10 +180,6 @@ btCollisionShape* EntityMotionState::computeNewShape() { return nullptr; } -// RELIABLE_SEND_HACK: until we have truly reliable resends of non-moving updates -// we alwasy resend packets for objects that have stopped moving up to some max limit. -const int MAX_NUM_NON_MOVING_UPDATES = 5; - bool EntityMotionState::isCandidateForOwnership(const QUuid& sessionID) const { if (!_body || !_entity) { return false; @@ -495,6 +491,10 @@ void EntityMotionState::measureBodyAcceleration() { glm::vec3 velocity = bulletToGLM(_body->getLinearVelocity()); _measuredAcceleration = (velocity / powf(1.0f - _body->getLinearDamping(), dt) - _lastVelocity) * invDt; _lastVelocity = velocity; + if (numSubsteps > PHYSICS_ENGINE_MAX_NUM_SUBSTEPS && !_candidateForOwnership) { + _loopsSinceOwnershipBid = 0; + _loopsWithoutOwner = 0; + } } } glm::vec3 EntityMotionState::getObjectLinearVelocityChange() const { diff --git a/libraries/physics/src/ObjectAction.cpp b/libraries/physics/src/ObjectAction.cpp index 6ff4098ba8..ee7ba9ce7c 100644 --- a/libraries/physics/src/ObjectAction.cpp +++ b/libraries/physics/src/ObjectAction.cpp @@ -24,7 +24,14 @@ ObjectAction::~ObjectAction() { } void ObjectAction::updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep) { - qDebug() << "ObjectAction::updateAction called"; + if (!_active) { + return; + } + if (!_ownerEntity) { + qDebug() << "ObjectActionPullToPoint::updateAction no owner entity"; + return; + } + updateActionWorker(deltaTimeStep); } void ObjectAction::debugDraw(btIDebugDraw* debugDrawer) { @@ -33,3 +40,87 @@ void ObjectAction::debugDraw(btIDebugDraw* debugDrawer) { void ObjectAction::removeFromSimulation(EntitySimulation* simulation) const { simulation->removeAction(_id); } + +btRigidBody* ObjectAction::getRigidBody() { + if (!_ownerEntity) { + return nullptr; + } + void* physicsInfo = _ownerEntity->getPhysicsInfo(); + if (!physicsInfo) { + return nullptr; + } + ObjectMotionState* motionState = static_cast<ObjectMotionState*>(physicsInfo); + return motionState->getRigidBody(); +} + +glm::vec3 ObjectAction::getPosition() { + auto rigidBody = getRigidBody(); + if (!rigidBody) { + return glm::vec3(0.0f); + } + return bulletToGLM(rigidBody->getCenterOfMassPosition()); +} + +void ObjectAction::setPosition(glm::vec3 position) { + auto rigidBody = getRigidBody(); + if (!rigidBody) { + return; + } + // XXX + // void setWorldTransform (const btTransform &worldTrans) + assert(false); + rigidBody->activate(); +} + +glm::quat ObjectAction::getRotation() { + auto rigidBody = getRigidBody(); + if (!rigidBody) { + return glm::quat(0.0f, 0.0f, 0.0f, 1.0f); + } + return bulletToGLM(rigidBody->getOrientation()); +} + +void ObjectAction::setRotation(glm::quat rotation) { + auto rigidBody = getRigidBody(); + if (!rigidBody) { + return; + } + // XXX + // void setWorldTransform (const btTransform &worldTrans) + assert(false); + rigidBody->activate(); +} + +glm::vec3 ObjectAction::getLinearVelocity() { + auto rigidBody = getRigidBody(); + if (!rigidBody) { + return glm::vec3(0.0f); + } + return bulletToGLM(rigidBody->getLinearVelocity()); +} + +void ObjectAction::setLinearVelocity(glm::vec3 linearVelocity) { + auto rigidBody = getRigidBody(); + if (!rigidBody) { + return; + } + rigidBody->setLinearVelocity(glmToBullet(glm::vec3(0.0f))); + rigidBody->activate(); +} + +glm::vec3 ObjectAction::getAngularVelocity() { + auto rigidBody = getRigidBody(); + if (!rigidBody) { + return glm::vec3(0.0f); + } + return bulletToGLM(rigidBody->getAngularVelocity()); +} + +void ObjectAction::setAngularVelocity(glm::vec3 angularVelocity) { + auto rigidBody = getRigidBody(); + if (!rigidBody) { + return; + } + rigidBody->setAngularVelocity(glmToBullet(angularVelocity)); + rigidBody->activate(); +} diff --git a/libraries/physics/src/ObjectAction.h b/libraries/physics/src/ObjectAction.h index 10b086c07d..0fd7383e6f 100644 --- a/libraries/physics/src/ObjectAction.h +++ b/libraries/physics/src/ObjectAction.h @@ -13,12 +13,17 @@ #ifndef hifi_ObjectAction_h #define hifi_ObjectAction_h -#include <btBulletDynamicsCommon.h> - #include <QUuid> +#include <btBulletDynamicsCommon.h> + #include <EntityItem.h> +#include "ObjectMotionState.h" +#include "BulletUtil.h" +#include "EntityActionInterface.h" + + class ObjectAction : public btActionInterface, public EntityActionInterface { public: ObjectAction(QUuid id, EntityItemPointer ownerEntity); @@ -30,6 +35,9 @@ public: virtual void setOwnerEntity(const EntityItemPointer ownerEntity) { _ownerEntity = ownerEntity; } virtual bool updateArguments(QVariantMap arguments) { return false; } + // this is called from updateAction and should be overridden by subclasses + virtual void updateActionWorker(float deltaTimeStep) {} + // these are from btActionInterface virtual void updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep); virtual void debugDraw(btIDebugDraw* debugDrawer); @@ -39,6 +47,16 @@ private: QReadWriteLock _lock; protected: + virtual btRigidBody* getRigidBody(); + virtual glm::vec3 getPosition(); + virtual void setPosition(glm::vec3 position); + virtual glm::quat getRotation(); + virtual void setRotation(glm::quat rotation); + virtual glm::vec3 getLinearVelocity(); + virtual void setLinearVelocity(glm::vec3 linearVelocity); + virtual glm::vec3 getAngularVelocity(); + virtual void setAngularVelocity(glm::vec3 angularVelocity); + bool tryLockForRead() { return _lock.tryLockForRead(); } void lockForWrite() { _lock.lockForWrite(); } void unlock() { _lock.unlock(); } diff --git a/libraries/physics/src/ObjectActionPullToPoint.cpp b/libraries/physics/src/ObjectActionPullToPoint.cpp index 78f202a24f..053bef6a03 100644 --- a/libraries/physics/src/ObjectActionPullToPoint.cpp +++ b/libraries/physics/src/ObjectActionPullToPoint.cpp @@ -9,9 +9,6 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "ObjectMotionState.h" -#include "BulletUtil.h" - #include "ObjectActionPullToPoint.h" ObjectActionPullToPoint::ObjectActionPullToPoint(QUuid id, EntityItemPointer ownerEntity) : @@ -27,28 +24,34 @@ ObjectActionPullToPoint::~ObjectActionPullToPoint() { #endif } -void ObjectActionPullToPoint::updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep) { +void ObjectActionPullToPoint::updateActionWorker(btScalar deltaTimeStep) { if (!tryLockForRead()) { // don't risk hanging the thread running the physics simulation return; } - void* physicsInfo = _ownerEntity->getPhysicsInfo(); - if (_active && physicsInfo) { - ObjectMotionState* motionState = static_cast<ObjectMotionState*>(physicsInfo); - btRigidBody* rigidBody = motionState->getRigidBody(); - if (rigidBody) { - glm::vec3 offset = _target - bulletToGLM(rigidBody->getCenterOfMassPosition()); - float offsetLength = glm::length(offset); - if (offsetLength > IGNORE_POSITION_DELTA) { - glm::vec3 newVelocity = glm::normalize(offset) * _speed; - rigidBody->setLinearVelocity(glmToBullet(newVelocity)); - rigidBody->activate(); - } else { - rigidBody->setLinearVelocity(glmToBullet(glm::vec3())); - } - } + void* physicsInfo = _ownerEntity->getPhysicsInfo(); + if (!physicsInfo) { + unlock(); + return; } + ObjectMotionState* motionState = static_cast<ObjectMotionState*>(physicsInfo); + btRigidBody* rigidBody = motionState->getRigidBody(); + if (!rigidBody) { + unlock(); + return; + } + + glm::vec3 offset = _target - bulletToGLM(rigidBody->getCenterOfMassPosition()); + float offsetLength = glm::length(offset); + if (offsetLength > IGNORE_POSITION_DELTA) { + glm::vec3 newVelocity = glm::normalize(offset) * _speed; + rigidBody->setLinearVelocity(glmToBullet(newVelocity)); + rigidBody->activate(); + } else { + rigidBody->setLinearVelocity(glmToBullet(glm::vec3())); + } + unlock(); } diff --git a/libraries/physics/src/ObjectActionPullToPoint.h b/libraries/physics/src/ObjectActionPullToPoint.h index 3aca70d640..2596f13515 100644 --- a/libraries/physics/src/ObjectActionPullToPoint.h +++ b/libraries/physics/src/ObjectActionPullToPoint.h @@ -23,7 +23,7 @@ public: virtual ~ObjectActionPullToPoint(); virtual bool updateArguments(QVariantMap arguments); - virtual void updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep); + virtual void updateActionWorker(float deltaTimeStep); private: diff --git a/libraries/physics/src/ObjectActionSpring.cpp b/libraries/physics/src/ObjectActionSpring.cpp new file mode 100644 index 0000000000..8eb4f7f652 --- /dev/null +++ b/libraries/physics/src/ObjectActionSpring.cpp @@ -0,0 +1,144 @@ +// +// ObjectActionSpring.cpp +// libraries/physics/src +// +// Created by Seth Alves 2015-6-5 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "ObjectActionSpring.h" + +ObjectActionSpring::ObjectActionSpring(QUuid id, EntityItemPointer ownerEntity) : + ObjectAction(id, ownerEntity) { + #if WANT_DEBUG + qDebug() << "ObjectActionSpring::ObjectActionSpring"; + #endif +} + +ObjectActionSpring::~ObjectActionSpring() { + #if WANT_DEBUG + qDebug() << "ObjectActionSpring::~ObjectActionSpring"; + #endif +} + +void ObjectActionSpring::updateActionWorker(btScalar deltaTimeStep) { + if (!tryLockForRead()) { + // don't risk hanging the thread running the physics simulation + qDebug() << "ObjectActionSpring::updateActionWorker lock failed"; + return; + } + + void* physicsInfo = _ownerEntity->getPhysicsInfo(); + if (!physicsInfo) { + unlock(); + return; + } + ObjectMotionState* motionState = static_cast<ObjectMotionState*>(physicsInfo); + btRigidBody* rigidBody = motionState->getRigidBody(); + if (!rigidBody) { + unlock(); + qDebug() << "ObjectActionSpring::updateActionWorker no rigidBody"; + return; + } + + // handle the linear part + if (_positionalTargetSet) { + glm::vec3 offset = _positionalTarget - bulletToGLM(rigidBody->getCenterOfMassPosition()); + float offsetLength = glm::length(offset); + float speed = offsetLength / _linearTimeScale; + + if (offsetLength > IGNORE_POSITION_DELTA) { + glm::vec3 newVelocity = glm::normalize(offset) * speed; + rigidBody->setLinearVelocity(glmToBullet(newVelocity)); + rigidBody->activate(); + } else { + rigidBody->setLinearVelocity(glmToBullet(glm::vec3(0.0f))); + } + } + + // handle rotation + if (_rotationalTargetSet) { + glm::quat bodyRotation = bulletToGLM(rigidBody->getOrientation()); + // if qZero and qOne are too close to each other, we can get NaN for angle. + auto alignmentDot = glm::dot(bodyRotation, _rotationalTarget); + const float almostOne = 0.99999f; + if (glm::abs(alignmentDot) < almostOne) { + glm::quat target = _rotationalTarget; + if (alignmentDot < 0) { + target = -target; + } + glm::quat qZeroInverse = glm::inverse(bodyRotation); + glm::quat deltaQ = target * qZeroInverse; + glm::vec3 axis = glm::axis(deltaQ); + float angle = glm::angle(deltaQ); + assert(!isNaN(angle)); + glm::vec3 newAngularVelocity = (angle / _angularTimeScale) * glm::normalize(axis); + rigidBody->setAngularVelocity(glmToBullet(newAngularVelocity)); + rigidBody->activate(); + } else { + rigidBody->setAngularVelocity(glmToBullet(glm::vec3(0.0f))); + } + } + + unlock(); +} + + +bool ObjectActionSpring::updateArguments(QVariantMap arguments) { + // targets are required, spring-constants are optional + bool ptOk = true; + glm::vec3 positionalTarget = + EntityActionInterface::extractVec3Argument("spring action", arguments, "targetPosition", ptOk, false); + bool pscOk = true; + float linearTimeScale = + EntityActionInterface::extractFloatArgument("spring action", arguments, "linearTimeScale", pscOk, false); + if (ptOk && pscOk && linearTimeScale <= 0.0f) { + qDebug() << "spring action -- linearTimeScale must be greater than zero."; + return false; + } + + bool rtOk = true; + glm::quat rotationalTarget = + EntityActionInterface::extractQuatArgument("spring action", arguments, "targetRotation", rtOk, false); + bool rscOk = true; + float angularTimeScale = + EntityActionInterface::extractFloatArgument("spring action", arguments, "angularTimeScale", rscOk, false); + + if (!ptOk && !rtOk) { + qDebug() << "spring action requires either targetPosition or targetRotation argument"; + return false; + } + + lockForWrite(); + + _positionalTargetSet = _rotationalTargetSet = false; + + if (ptOk) { + _positionalTarget = positionalTarget; + _positionalTargetSet = true; + + if (pscOk) { + _linearTimeScale = linearTimeScale; + } else { + _linearTimeScale = 0.1f; + } + } + + if (rtOk) { + _rotationalTarget = rotationalTarget; + _rotationalTargetSet = true; + + if (rscOk) { + _angularTimeScale = angularTimeScale; + } else { + _angularTimeScale = 0.1f; + } + } + + _active = true; + unlock(); + return true; +} diff --git a/libraries/physics/src/ObjectActionSpring.h b/libraries/physics/src/ObjectActionSpring.h new file mode 100644 index 0000000000..9f3df0fdf8 --- /dev/null +++ b/libraries/physics/src/ObjectActionSpring.h @@ -0,0 +1,39 @@ +// +// ObjectActionSpring.h +// libraries/physics/src +// +// Created by Seth Alves 2015-6-5 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_ObjectActionSpring_h +#define hifi_ObjectActionSpring_h + +#include <QUuid> + +#include <EntityItem.h> +#include "ObjectAction.h" + +class ObjectActionSpring : public ObjectAction { +public: + ObjectActionSpring(QUuid id, EntityItemPointer ownerEntity); + virtual ~ObjectActionSpring(); + + virtual bool updateArguments(QVariantMap arguments); + virtual void updateActionWorker(float deltaTimeStep); + +protected: + + glm::vec3 _positionalTarget; + float _linearTimeScale; + bool _positionalTargetSet; + + glm::quat _rotationalTarget; + float _angularTimeScale; + bool _rotationalTargetSet; +}; + +#endif // hifi_ObjectActionSpring_h diff --git a/libraries/physics/src/ObjectMotionState.h b/libraries/physics/src/ObjectMotionState.h index b17dc67cff..561ce02d62 100644 --- a/libraries/physics/src/ObjectMotionState.h +++ b/libraries/physics/src/ObjectMotionState.h @@ -53,8 +53,6 @@ const uint32_t OUTGOING_DIRTY_PHYSICS_FLAGS = EntityItem::DIRTY_TRANSFORM | Enti class OctreeEditPacketSender; class PhysicsEngine; -extern const int MAX_NUM_NON_MOVING_UPDATES; - class ObjectMotionState : public btMotionState { public: // These poroperties of the PhysicsEngine are "global" within the context of all ObjectMotionStates diff --git a/libraries/physics/src/PhysicalEntitySimulation.cpp b/libraries/physics/src/PhysicalEntitySimulation.cpp index 711c5e49da..c68b993fe2 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.cpp +++ b/libraries/physics/src/PhysicalEntitySimulation.cpp @@ -9,10 +9,11 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // + + #include "PhysicsHelpers.h" #include "PhysicsLogging.h" #include "ShapeManager.h" -#include "ObjectActionPullToPoint.h" #include "PhysicalEntitySimulation.h" @@ -234,29 +235,6 @@ void PhysicalEntitySimulation::handleCollisionEvents(CollisionEvents& collisionE } } -EntityActionPointer PhysicalEntitySimulation::actionFactory(EntityActionType type, - QUuid id, - EntityItemPointer ownerEntity, - QVariantMap arguments) { - EntityActionPointer action = nullptr; - switch (type) { - case ACTION_TYPE_NONE: - return nullptr; - case ACTION_TYPE_PULL_TO_POINT: - action = (EntityActionPointer) new ObjectActionPullToPoint(id, ownerEntity); - break; - } - - bool ok = action->updateArguments(arguments); - if (ok) { - ownerEntity->addAction(this, action); - return action; - } - - action = nullptr; - return action; -} - void PhysicalEntitySimulation::applyActionChanges() { if (_physicsEngine) { foreach (EntityActionPointer actionToAdd, _actionsToAdd) { diff --git a/libraries/physics/src/PhysicalEntitySimulation.h b/libraries/physics/src/PhysicalEntitySimulation.h index 82b0f8ad51..81ab9f5cce 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.h +++ b/libraries/physics/src/PhysicalEntitySimulation.h @@ -32,10 +32,6 @@ public: void init(EntityTree* tree, PhysicsEngine* engine, EntityEditPacketSender* packetSender); - virtual EntityActionPointer actionFactory(EntityActionType type, - QUuid id, - EntityItemPointer ownerEntity, - QVariantMap arguments); virtual void applyActionChanges(); protected: // only called by EntitySimulation diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index e5c974dfbc..55fc5e6295 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -227,8 +227,7 @@ void PhysicsEngine::stepSimulation() { // (3) synchronize outgoing motion states // (4) send outgoing packets - const int MAX_NUM_SUBSTEPS = 4; - const float MAX_TIMESTEP = (float)MAX_NUM_SUBSTEPS * PHYSICS_ENGINE_FIXED_SUBSTEP; + const float MAX_TIMESTEP = (float)PHYSICS_ENGINE_MAX_NUM_SUBSTEPS * PHYSICS_ENGINE_FIXED_SUBSTEP; float dt = 1.0e-6f * (float)(_clock.getTimeMicroseconds()); _clock.reset(); float timeStep = btMin(dt, MAX_TIMESTEP); @@ -245,7 +244,7 @@ void PhysicsEngine::stepSimulation() { _characterController->preSimulation(timeStep); } - int numSubsteps = _dynamicsWorld->stepSimulation(timeStep, MAX_NUM_SUBSTEPS, PHYSICS_ENGINE_FIXED_SUBSTEP); + int numSubsteps = _dynamicsWorld->stepSimulation(timeStep, PHYSICS_ENGINE_MAX_NUM_SUBSTEPS, PHYSICS_ENGINE_FIXED_SUBSTEP); if (numSubsteps > 0) { BT_PROFILE("postSimulation"); _numSubsteps += (uint32_t)numSubsteps; diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 2ae8968571..145f352658 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -405,9 +405,6 @@ void Model::reset() { if (_jointStates.isEmpty()) { return; } - foreach (Model* attachment, _attachments) { - attachment->reset(); - } const FBXGeometry& geometry = _geometry->getFBXGeometry(); for (int i = 0; i < _jointStates.size(); i++) { _jointStates[i].setRotationInConstrainedFrame(geometry.joints.at(i).rotation, 0.0f); @@ -419,14 +416,7 @@ void Model::reset() { } bool Model::updateGeometry() { - // NOTE: this is a recursive call that walks all attachments, and their attachments bool needFullUpdate = false; - for (int i = 0; i < _attachments.size(); i++) { - Model* model = _attachments.at(i); - if (model->updateGeometry()) { - needFullUpdate = true; - } - } bool needToRebuild = false; if (_nextGeometry) { @@ -445,6 +435,7 @@ bool Model::updateGeometry() { QSharedPointer<NetworkGeometry> geometry = _geometry->getLODOrFallback(_lodDistance, _lodHysteresis); if (_geometry != geometry) { + // NOTE: it is theoretically impossible to reach here after passing through the applyNextGeometry() call above. // Which means we don't need to worry about calling deleteGeometry() below immediately after creating new geometry. @@ -499,12 +490,6 @@ bool Model::updateGeometry() { } _blendedVertexBuffers.push_back(buffer); } - foreach (const FBXAttachment& attachment, fbxGeometry.attachments) { - Model* model = new Model(this); - model->init(); - model->setURL(attachment.url); - _attachments.append(model); - } needFullUpdate = true; } return needFullUpdate; @@ -827,71 +812,41 @@ void Model::renderSetup(RenderArgs* args) { } -class TransparentMeshPart { +class MeshPartPayload { public: - TransparentMeshPart(Model* model, int meshIndex, int partIndex) : model(model), meshIndex(meshIndex), partIndex(partIndex) { } - typedef render::Payload<TransparentMeshPart> Payload; + MeshPartPayload(bool transparent, Model* model, int meshIndex, int partIndex) : + transparent(transparent), model(model), url(model->getURL()), meshIndex(meshIndex), partIndex(partIndex) { } + typedef render::Payload<MeshPartPayload> Payload; typedef Payload::DataPointer Pointer; - Model* model; + bool transparent; + Model* model; + QUrl url; int meshIndex; int partIndex; }; namespace render { - template <> const ItemKey payloadGetKey(const TransparentMeshPart::Pointer& payload) { + template <> const ItemKey payloadGetKey(const MeshPartPayload::Pointer& payload) { if (!payload->model->isVisible()) { return ItemKey::Builder().withInvisible().build(); } - return ItemKey::Builder::transparentShape(); + return payload->transparent ? ItemKey::Builder::transparentShape() : ItemKey::Builder::opaqueShape(); } - template <> const Item::Bound payloadGetBound(const TransparentMeshPart::Pointer& payload) { + template <> const Item::Bound payloadGetBound(const MeshPartPayload::Pointer& payload) { if (payload) { return payload->model->getPartBounds(payload->meshIndex, payload->partIndex); } return render::Item::Bound(); } - template <> void payloadRender(const TransparentMeshPart::Pointer& payload, RenderArgs* args) { + template <> void payloadRender(const MeshPartPayload::Pointer& payload, RenderArgs* args) { if (args) { - return payload->model->renderPart(args, payload->meshIndex, payload->partIndex, true); + return payload->model->renderPart(args, payload->meshIndex, payload->partIndex, payload->transparent); } } -} -class OpaqueMeshPart { -public: - OpaqueMeshPart(Model* model, int meshIndex, int partIndex) : model(model), meshIndex(meshIndex), partIndex(partIndex) { } - typedef render::Payload<OpaqueMeshPart> Payload; - typedef Payload::DataPointer Pointer; - - Model* model; - int meshIndex; - int partIndex; -}; - -namespace render { - template <> const ItemKey payloadGetKey(const OpaqueMeshPart::Pointer& payload) { - if (!payload->model->isVisible()) { - return ItemKey::Builder().withInvisible().build(); - } - return ItemKey::Builder::opaqueShape(); - } - - template <> const Item::Bound payloadGetBound(const OpaqueMeshPart::Pointer& payload) { - if (payload) { - Item::Bound result = payload->model->getPartBounds(payload->meshIndex, payload->partIndex); - //qDebug() << "payloadGetBound(OpaqueMeshPart) " << result; - return result; - } - return render::Item::Bound(); - } - template <> void payloadRender(const OpaqueMeshPart::Pointer& payload, RenderArgs* args) { - if (args) { - return payload->model->renderPart(args, payload->meshIndex, payload->partIndex, false); - } - } - /* template <> const model::MaterialKey& shapeGetMaterialKey(const OpaqueMeshPart::Pointer& payload) { + /* template <> const model::MaterialKey& shapeGetMaterialKey(const MeshPartPayload::Pointer& payload) { return payload->model->getPartMaterial(payload->meshIndex, payload->partIndex); }*/ } @@ -916,24 +871,19 @@ bool Model::addToScene(std::shared_ptr<render::Scene> scene, render::PendingChan bool somethingAdded = false; - // allow the attachments to add to scene - foreach (Model* attachment, _attachments) { - bool attachementSomethingAdded = attachment->addToScene(scene, pendingChanges); - somethingAdded = somethingAdded || attachementSomethingAdded; - } - foreach (auto renderItem, _transparentRenderItems) { auto item = scene->allocateID(); - auto renderData = TransparentMeshPart::Pointer(renderItem); - auto renderPayload = render::PayloadPointer(new TransparentMeshPart::Payload(renderData)); + auto renderData = MeshPartPayload::Pointer(renderItem); + auto renderPayload = render::PayloadPointer(new MeshPartPayload::Payload(renderData)); pendingChanges.resetItem(item, renderPayload); _renderItems.insert(item, renderPayload); somethingAdded = true; } + foreach (auto renderItem, _opaqueRenderItems) { auto item = scene->allocateID(); - auto renderData = OpaqueMeshPart::Pointer(renderItem); - auto renderPayload = render::PayloadPointer(new OpaqueMeshPart::Payload(renderData)); + auto renderData = MeshPartPayload::Pointer(renderItem); + auto renderPayload = render::PayloadPointer(new MeshPartPayload::Payload(renderData)); pendingChanges.resetItem(item, renderPayload); _renderItems.insert(item, renderPayload); somethingAdded = true; @@ -945,11 +895,6 @@ bool Model::addToScene(std::shared_ptr<render::Scene> scene, render::PendingChan } void Model::removeFromScene(std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) { - // allow the attachments to remove to scene - foreach (Model* attachment, _attachments) { - attachment->removeFromScene(scene, pendingChanges); - } - foreach (auto item, _renderItems.keys()) { pendingChanges.removeItem(item); } @@ -957,210 +902,6 @@ void Model::removeFromScene(std::shared_ptr<render::Scene> scene, render::Pendin _readyWhenAdded = false; } -bool Model::render(RenderArgs* renderArgs, float alpha) { - return true; // - PROFILE_RANGE(__FUNCTION__); - - // render the attachments - foreach (Model* attachment, _attachments) { - attachment->render(renderArgs, alpha); - } - if (_meshStates.isEmpty()) { - return false; - } - - renderSetup(renderArgs); - return renderCore(renderArgs, alpha); -} - -bool Model::renderCore(RenderArgs* args, float alpha) { - return true; - - PROFILE_RANGE(__FUNCTION__); - if (!_viewState) { - return false; - } - - auto mode = args->_renderMode; - - // Let's introduce a gpu::Batch to capture all the calls to the graphics api - _renderBatch.clear(); - gpu::Batch& batch = _renderBatch; - - // Setup the projection matrix - if (args && args->_viewFrustum) { - glm::mat4 proj; - // If for easier debug depending on the pass - if (mode == RenderArgs::SHADOW_RENDER_MODE) { - args->_viewFrustum->evalProjectionMatrix(proj); - } else { - args->_viewFrustum->evalProjectionMatrix(proj); - } - batch.setProjectionTransform(proj); - } - - // Capture the view matrix once for the rendering of this model - if (_transforms.empty()) { - _transforms.push_back(Transform()); - } - - _transforms[0] = _viewState->getViewTransform(); - - // apply entity translation offset to the viewTransform in one go (it's a preTranslate because viewTransform goes from world to eye space) - _transforms[0].preTranslate(-_translation); - - batch.setViewTransform(_transforms[0]); - - /*DependencyManager::get<TextureCache>()->setPrimaryDrawBuffers( - mode == RenderArgs::DEFAULT_RENDER_MODE || mode == RenderArgs::DIFFUSE_RENDER_MODE, - mode == RenderArgs::DEFAULT_RENDER_MODE || mode == RenderArgs::NORMAL_RENDER_MODE, - mode == RenderArgs::DEFAULT_RENDER_MODE); - */ - /*if (mode != RenderArgs::SHADOW_RENDER_MODE)*/ { - GLenum buffers[3]; - int bufferCount = 0; - - // if (mode == RenderArgs::DEFAULT_RENDER_MODE || mode == RenderArgs::DIFFUSE_RENDER_MODE) { - if (mode != RenderArgs::SHADOW_RENDER_MODE) { - buffers[bufferCount++] = GL_COLOR_ATTACHMENT0; - } - // if (mode == RenderArgs::DEFAULT_RENDER_MODE || mode == RenderArgs::NORMAL_RENDER_MODE) { - if (mode != RenderArgs::SHADOW_RENDER_MODE) { - buffers[bufferCount++] = GL_COLOR_ATTACHMENT1; - } - // if (mode == RenderArgs::DEFAULT_RENDER_MODE) { - if (mode != RenderArgs::SHADOW_RENDER_MODE) { - buffers[bufferCount++] = GL_COLOR_ATTACHMENT2; - } - GLBATCH(glDrawBuffers)(bufferCount, buffers); - // batch.setFramebuffer(DependencyManager::get<TextureCache>()->getPrimaryOpaqueFramebuffer()); - } - - const float DEFAULT_ALPHA_THRESHOLD = 0.5f; - - - //renderMeshes(batch, mode, translucent, alphaThreshold, hasTangents, hasSpecular, isSkinned, args, forceRenderMeshes); - int opaqueMeshPartsRendered = 0; - opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, false, false, false, false, args, true); - opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, false, false, true, false, args, true); - opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, false, true, false, false, args, true); - opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, false, true, true, false, args, true); - opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, true, false, false, false, args, true); - opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, true, false, true, false, args, true); - opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, true, true, false, false, args, true); - opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, true, true, true, false, args, true); - - opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, false, false, false, false, args, true); - opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, false, true, false, false, args, true); - opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, true, false, false, false, args, true); - opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, true, true, false, false, args, true); - - // render translucent meshes afterwards - //DependencyManager::get<TextureCache>()->setPrimaryDrawBuffers(false, true, true); - { - GLenum buffers[2]; - int bufferCount = 0; - buffers[bufferCount++] = GL_COLOR_ATTACHMENT1; - buffers[bufferCount++] = GL_COLOR_ATTACHMENT2; - GLBATCH(glDrawBuffers)(bufferCount, buffers); - } - - int translucentMeshPartsRendered = 0; - const float MOSTLY_OPAQUE_THRESHOLD = 0.75f; - translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, false, false, false, false, args, true); - translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, false, false, true, false, args, true); - translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, false, true, false, false, args, true); - translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, false, true, true, false, args, true); - translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, true, false, false, false, args, true); - translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, true, false, true, false, args, true); - translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, true, true, false, false, args, true); - translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, true, true, true, false, args, true); - - { - GLenum buffers[1]; - int bufferCount = 0; - buffers[bufferCount++] = GL_COLOR_ATTACHMENT0; - GLBATCH(glDrawBuffers)(bufferCount, buffers); - } - - // if (mode == RenderArgs::DEFAULT_RENDER_MODE || mode == RenderArgs::DIFFUSE_RENDER_MODE) { - if (mode != RenderArgs::SHADOW_RENDER_MODE) { - // batch.setFramebuffer(DependencyManager::get<TextureCache>()->getPrimaryTransparentFramebuffer()); - - const float MOSTLY_TRANSPARENT_THRESHOLD = 0.0f; - translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, false, false, false, false, args, true); - translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, false, false, true, false, args, true); - translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, false, true, false, false, args, true); - translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, false, true, true, false, args, true); - translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, true, false, false, false, args, true); - translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, true, false, true, false, args, true); - translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, true, true, false, false, args, true); - translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, true, true, true, false, args, true); - - // batch.setFramebuffer(DependencyManager::get<TextureCache>()->getPrimaryOpaqueFramebuffer()); - } - - GLBATCH(glDepthMask)(true); - GLBATCH(glDepthFunc)(GL_LESS); - GLBATCH(glDisable)(GL_CULL_FACE); - - if (mode == RenderArgs::SHADOW_RENDER_MODE) { - GLBATCH(glCullFace)(GL_BACK); - } - - GLBATCH(glActiveTexture)(GL_TEXTURE0 + 1); - GLBATCH(glBindTexture)(GL_TEXTURE_2D, 0); - GLBATCH(glActiveTexture)(GL_TEXTURE0 + 2); - GLBATCH(glBindTexture)(GL_TEXTURE_2D, 0); - GLBATCH(glActiveTexture)(GL_TEXTURE0 + 3); - GLBATCH(glBindTexture)(GL_TEXTURE_2D, 0); - GLBATCH(glActiveTexture)(GL_TEXTURE0); - GLBATCH(glBindTexture)(GL_TEXTURE_2D, 0); - - // deactivate vertex arrays after drawing - GLBATCH(glDisableClientState)(GL_NORMAL_ARRAY); - GLBATCH(glDisableClientState)(GL_VERTEX_ARRAY); - GLBATCH(glDisableClientState)(GL_TEXTURE_COORD_ARRAY); - GLBATCH(glDisableClientState)(GL_COLOR_ARRAY); - GLBATCH(glDisableVertexAttribArray)(gpu::Stream::TANGENT); - GLBATCH(glDisableVertexAttribArray)(gpu::Stream::SKIN_CLUSTER_INDEX); - GLBATCH(glDisableVertexAttribArray)(gpu::Stream::SKIN_CLUSTER_WEIGHT); - - // bind with 0 to switch back to normal operation - GLBATCH(glBindBuffer)(GL_ARRAY_BUFFER, 0); - GLBATCH(glBindBuffer)(GL_ELEMENT_ARRAY_BUFFER, 0); - GLBATCH(glBindTexture)(GL_TEXTURE_2D, 0); - - // Back to no program - GLBATCH(glUseProgram)(0); - - // Render! - { - PROFILE_RANGE("render Batch"); - - #if defined(ANDROID) - #else - glPushMatrix(); - #endif - - ::gpu::GLBackend::renderBatch(batch, true); // force sync with gl state here - - #if defined(ANDROID) - #else - glPopMatrix(); - #endif - } - - // restore all the default material settings - _viewState->setupWorldLight(); - - #ifdef WANT_DEBUG_MESHBOXES - renderDebugMeshBoxes(); - #endif - - return true; -} - void Model::renderDebugMeshBoxes() { int colorNdx = 0; _mutex.lock(); @@ -1267,12 +1008,12 @@ Extents Model::calculateScaledOffsetExtents(const Extents& extents) const { Extents translatedExtents = { rotatedExtents.minimum + _translation, rotatedExtents.maximum + _translation }; + return translatedExtents; } /// Returns the world space equivalent of some box in model space. AABox Model::calculateScaledOffsetAABox(const AABox& box) const { - return AABox(calculateScaledOffsetExtents(Extents(box))); } @@ -1341,9 +1082,10 @@ void Model::setURL(const QUrl& url, const QUrl& fallback, bool retainCurrent, bo if (_url == url && _geometry && _geometry->getURL() == url) { return; } - + _readyWhenAdded = false; // reset out render items. _needsReload = true; + invalidCalculatedMeshBoxes(); _url = url; @@ -1532,7 +1274,7 @@ void Model::setScaleToFit(bool scaleToFit, const glm::vec3& dimensions) { } } -void Model::setScaleToFit(bool scaleToFit, float largestDimension) { +void Model::setScaleToFit(bool scaleToFit, float largestDimension, bool forceRescale) { // NOTE: if the model is not active, then it means we don't actually know the true/natural dimensions of the // mesh, and so we can't do the needed calculations for scaling to fit to a single largest dimension. In this // case we will record that we do want to do this, but we will stick our desired single dimension into the @@ -1545,7 +1287,7 @@ void Model::setScaleToFit(bool scaleToFit, float largestDimension) { return; } - if (_scaleToFit != scaleToFit || glm::length(_scaleToFitDimensions) != largestDimension) { + if (forceRescale || _scaleToFit != scaleToFit || glm::length(_scaleToFitDimensions) != largestDimension) { _scaleToFit = scaleToFit; // we only need to do this work if we're "turning on" scale to fit. @@ -1555,7 +1297,7 @@ void Model::setScaleToFit(bool scaleToFit, float largestDimension) { float maxScale = largestDimension / maxDimension; glm::vec3 modelMeshDimensions = modelMeshExtents.maximum - modelMeshExtents.minimum; glm::vec3 dimensions = modelMeshDimensions * maxScale; - + _scaleToFitDimensions = dimensions; _scaledToFit = false; // force rescaling } @@ -1626,7 +1368,6 @@ void Model::simulate(float deltaTime, bool fullUpdate) { } void Model::simulateInternal(float deltaTime) { - // NOTE: this is a recursive call that walks all attachments, and their attachments // update the world space transforms for all joints // update animations @@ -1643,31 +1384,7 @@ void Model::simulateInternal(float deltaTime) { _shapesAreDirty = !_shapes.isEmpty(); - // update the attachment transforms and simulate them const FBXGeometry& geometry = _geometry->getFBXGeometry(); - for (int i = 0; i < _attachments.size(); i++) { - const FBXAttachment& attachment = geometry.attachments.at(i); - Model* model = _attachments.at(i); - - glm::vec3 jointTranslation = _translation; - glm::quat jointRotation = _rotation; - if (_showTrueJointTransforms) { - getJointPositionInWorldFrame(attachment.jointIndex, jointTranslation); - getJointRotationInWorldFrame(attachment.jointIndex, jointRotation); - } else { - getVisibleJointPositionInWorldFrame(attachment.jointIndex, jointTranslation); - getVisibleJointRotationInWorldFrame(attachment.jointIndex, jointRotation); - } - - model->setTranslation(jointTranslation + jointRotation * attachment.translation * _scale); - model->setRotation(jointRotation * attachment.rotation); - model->setScale(_scale * attachment.scale); - - if (model->isActive()) { - model->simulateInternal(deltaTime); - } - } - glm::mat4 modelToWorld = glm::mat4_cast(_rotation); for (int i = 0; i < _meshStates.size(); i++) { MeshState& state = _meshStates[i]; @@ -2005,10 +1722,6 @@ void Model::applyNextGeometry() { } void Model::deleteGeometry() { - foreach (Model* attachment, _attachments) { - delete attachment; - } - _attachments.clear(); _blendedVertexBuffers.clear(); _jointStates.clear(); _meshStates.clear(); @@ -2051,13 +1764,38 @@ AABox Model::getPartBounds(int meshIndex, int partIndex) { if (!_calculatedMeshPartBoxesValid) { recalculateMeshBoxes(true); } + + if (meshIndex < _meshStates.size()) { + const MeshState& state = _meshStates.at(meshIndex); + bool isSkinned = state.clusterMatrices.size() > 1; + if (isSkinned) { + // if we're skinned return the entire mesh extents because we can't know for sure our clusters don't move us + return calculateScaledOffsetAABox(_geometry->getFBXGeometry().meshExtents); + } + } + if (_calculatedMeshPartBoxesValid && _calculatedMeshPartBoxes.contains(QPair<int,int>(meshIndex, partIndex))) { - return calculateScaledOffsetAABox(_calculatedMeshPartBoxes[QPair<int,int>(meshIndex, partIndex)]); + + // FIX ME! - This is currently a hack because for some mesh parts our efforts to calculate the bounding + // box of the mesh part fails. It seems to create boxes that are not consistent with where the + // geometry actually renders. If instead we make all the parts share the bounds of the entire subMesh + // things will render properly. + // + // return calculateScaledOffsetAABox(_calculatedMeshPartBoxes[QPair<int,int>(meshIndex, partIndex)]); + // + // NOTE: we also don't want to use the _calculatedMeshBoxes[] because they don't handle avatar moving correctly + // without recalculating them... + // return _calculatedMeshBoxes[meshIndex]; + // + // If we not skinned use the bounds of the subMesh for all it's parts + const FBXMesh& mesh = _geometry->getFBXGeometry().meshes.at(meshIndex); + return calculateScaledOffsetExtents(mesh.meshExtents); } return AABox(); } void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool translucent) { + if (!_readyWhenAdded) { return; // bail asap } @@ -2072,20 +1810,6 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran gpu::Batch& batch = *(args->_batch); auto mode = args->_renderMode; - // render the part bounding box - #ifdef DEBUG_BOUNDING_PARTS - { - glm::vec4 cubeColor(1.0f,0.0f,0.0f,1.0f); - AABox partBounds = getPartBounds(meshIndex, partIndex); - - glm::mat4 translation = glm::translate(partBounds.calcCenter()); - glm::mat4 scale = glm::scale(partBounds.getDimensions()); - glm::mat4 modelToWorldMatrix = translation * scale; - batch.setModelTransform(modelToWorldMatrix); - //qDebug() << "partBounds:" << partBounds; - DependencyManager::get<DeferredLightingEffect>()->renderWireCube(batch, 1.0f, cubeColor); - } - #endif //def DEBUG_BOUNDING_PARTS // Capture the view matrix once for the rendering of this model if (_transforms.empty()) { @@ -2112,6 +1836,29 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran bool hasLightmap = mesh.hasEmissiveTexture(); bool isSkinned = state.clusterMatrices.size() > 1; bool wireframe = isWireframe(); + + // render the part bounding box + #ifdef DEBUG_BOUNDING_PARTS + { + AABox partBounds = getPartBounds(meshIndex, partIndex); + bool inView = args->_viewFrustum->boxInFrustum(partBounds) != ViewFrustum::OUTSIDE; + + glm::vec4 cubeColor; + if (isSkinned) { + cubeColor = glm::vec4(0.0f, 1.0f, 1.0f, 1.0f); + } else if (inView) { + cubeColor = glm::vec4(1.0f, 0.0f, 1.0f, 1.0f); + } else { + cubeColor = glm::vec4(1.0f, 1.0f, 0.0f, 1.0f); + } + + Transform transform; + transform.setTranslation(partBounds.calcCenter()); + transform.setScale(partBounds.getDimensions()); + batch.setModelTransform(transform); + DependencyManager::get<DeferredLightingEffect>()->renderWireCube(batch, 1.0f, cubeColor); + } + #endif //def DEBUG_BOUNDING_PARTS if (wireframe) { translucentMesh = hasTangents = hasSpecular = hasLightmap = isSkinned = false; @@ -2172,16 +1919,18 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran // guard against partially loaded meshes if (partIndex >= networkMesh.parts.size() || partIndex >= mesh.parts.size()) { - return; + return; } const NetworkMeshPart& networkPart = networkMesh.parts.at(partIndex); const FBXMeshPart& part = mesh.parts.at(partIndex); model::MaterialPointer material = part._material; + #ifdef WANT_DEBUG if (material == nullptr) { - // qCDebug(renderutils) << "WARNING: material == nullptr!!!"; + qCDebug(renderutils) << "WARNING: material == nullptr!!!"; } + #endif if (material != nullptr) { @@ -2263,7 +2012,7 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran } qint64 offset = _calculatedMeshPartOffet[QPair<int,int>(meshIndex, partIndex)]; - + if (part.quadIndices.size() > 0) { batch.drawIndexed(gpu::QUADS, part.quadIndices.size(), offset); offset += part.quadIndices.size() * sizeof(int); @@ -2283,8 +2032,6 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran } void Model::segregateMeshGroups() { - _renderBuckets.clear(); - const FBXGeometry& geometry = _geometry->getFBXGeometry(); const QVector<NetworkMesh>& networkMeshes = _geometry->getMeshes(); @@ -2294,6 +2041,9 @@ void Model::segregateMeshGroups() { qDebug() << "WARNING!!!! Mesh Sizes don't match! We will not segregate mesh groups yet."; return; } + + _transparentRenderItems.clear(); + _opaqueRenderItems.clear(); // Run through all of the meshes, and place them into their segregated, but unsorted buckets for (int i = 0; i < networkMeshes.size(); i++) { @@ -2318,67 +2068,23 @@ void Model::segregateMeshGroups() { for (int partIndex = 0; partIndex < totalParts; partIndex++) { // this is a good place to create our renderPayloads if (translucentMesh) { - _transparentRenderItems << std::shared_ptr<TransparentMeshPart>(new TransparentMeshPart(this, i, partIndex)); + _transparentRenderItems << std::shared_ptr<MeshPartPayload>(new MeshPartPayload(true, this, i, partIndex)); } else { - _opaqueRenderItems << std::shared_ptr<OpaqueMeshPart>(new OpaqueMeshPart(this, i, partIndex)); + _opaqueRenderItems << std::shared_ptr<MeshPartPayload>(new MeshPartPayload(false, this, i, partIndex)); } } - - - QString materialID; - - // create a material name from all the parts. If there's one part, this will be a single material and its - // true name. If however the mesh has multiple parts the name will be all the part's materials mashed together - // which will result in those parts being sorted away from single material parts. - QString lastPartMaterialID; - foreach(FBXMeshPart part, mesh.parts) { - if (part.materialID != lastPartMaterialID) { - materialID += part.materialID; - } - lastPartMaterialID = part.materialID; - } - const bool wantDebug = false; - if (wantDebug) { - qCDebug(renderutils) << "materialID:" << materialID << "parts:" << mesh.parts.size(); - } - - RenderKey key(translucentMesh, hasLightmap, hasTangents, hasSpecular, isSkinned, wireframe); - - // reuse or create the bucket corresponding to that key and insert the mesh as unsorted - _renderBuckets[key.getRaw()]._unsortedMeshes.insertMulti(materialID, i); } - - for(auto& b : _renderBuckets) { - foreach(auto i, b.second._unsortedMeshes) { - b.second._meshes.append(i); - } - b.second._unsortedMeshes.clear(); - } - _meshGroupsKnown = true; } -QVector<int>* Model::pickMeshList(bool translucent, float alphaThreshold, bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned, bool isWireframe) { - PROFILE_RANGE(__FUNCTION__); - - // depending on which parameters we were called with, pick the correct mesh group to render - QVector<int>* whichList = NULL; - - RenderKey key(translucent, hasLightmap, hasTangents, hasSpecular, isSkinned, isWireframe); - - auto bucket = _renderBuckets.find(key.getRaw()); - if (bucket != _renderBuckets.end()) { - whichList = &(*bucket).second._meshes; - } - - return whichList; -} - void Model::pickPrograms(gpu::Batch& batch, RenderMode mode, bool translucent, float alphaThreshold, bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned, bool isWireframe, RenderArgs* args, Locations*& locations) { RenderKey key(mode, translucent, alphaThreshold, hasLightmap, hasTangents, hasSpecular, isSkinned, isWireframe); + if (mode == RenderArgs::MIRROR_RENDER_MODE) { + key = RenderKey(key.getRaw() | RenderKey::IS_MIRROR); + } auto pipeline = _renderPipelineLib.find(key.getRaw()); if (pipeline == _renderPipelineLib.end()) { qDebug() << "No good, couldn't find a pipeline from the key ?" << key.getRaw(); @@ -2402,212 +2108,6 @@ void Model::pickPrograms(gpu::Batch& batch, RenderMode mode, bool translucent, f } } -int Model::renderMeshes(gpu::Batch& batch, RenderMode mode, bool translucent, float alphaThreshold, - bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned, bool isWireframe, RenderArgs* args, - bool forceRenderSomeMeshes) { - - PROFILE_RANGE(__FUNCTION__); - int meshPartsRendered = 0; - - //Pick the mesh list with the requested render flags - QVector<int>* whichList = pickMeshList(translucent, alphaThreshold, hasLightmap, hasTangents, hasSpecular, isSkinned, isWireframe); - if (!whichList) { - return 0; - } - QVector<int>& list = *whichList; - - // If this list has nothing to render, then don't bother proceeding. This saves us on binding to programs - if (list.empty()) { - return 0; - } - - Locations* locations = nullptr; - pickPrograms(batch, mode, translucent, alphaThreshold, hasLightmap, hasTangents, hasSpecular, isSkinned, isWireframe, - args, locations); - meshPartsRendered = renderMeshesFromList(list, batch, mode, translucent, alphaThreshold, - args, locations, forceRenderSomeMeshes); - - return meshPartsRendered; -} - - -int Model::renderMeshesFromList(QVector<int>& list, gpu::Batch& batch, RenderMode mode, bool translucent, float alphaThreshold, RenderArgs* args, - Locations* locations, bool forceRenderMeshes) { - PROFILE_RANGE(__FUNCTION__); - - auto textureCache = DependencyManager::get<TextureCache>(); - - QString lastMaterialID; - int meshPartsRendered = 0; - updateVisibleJointStates(); - const FBXGeometry& geometry = _geometry->getFBXGeometry(); - const QVector<NetworkMesh>& networkMeshes = _geometry->getMeshes(); - - // i is the "index" from the original networkMeshes QVector... - foreach (int i, list) { - - // if our index is ever out of range for either meshes or networkMeshes, then skip it, and set our _meshGroupsKnown - // to false to rebuild out mesh groups. - - if (i < 0 || i >= networkMeshes.size() || i > geometry.meshes.size()) { - _meshGroupsKnown = false; // regenerate these lists next time around. - _readyWhenAdded = false; // in case any of our users are using scenes - invalidCalculatedMeshBoxes(); // if we have to reload, we need to assume our mesh boxes are all invalid - continue; - } - - // exit early if the translucency doesn't match what we're drawing - const NetworkMesh& networkMesh = networkMeshes.at(i); - const FBXMesh& mesh = geometry.meshes.at(i); - - batch.setIndexBuffer(gpu::UINT32, (networkMesh._indexBuffer), 0); - int vertexCount = mesh.vertices.size(); - if (vertexCount == 0) { - // sanity check - continue; - } - - // if we got here, then check to see if this mesh is in view - if (args) { - bool shouldRender = true; - if (args->_viewFrustum) { - - shouldRender = forceRenderMeshes || - args->_viewFrustum->boxInFrustum(_calculatedMeshBoxes.at(i)) != ViewFrustum::OUTSIDE; - - if (shouldRender && !forceRenderMeshes) { - float distance = args->_viewFrustum->distanceToCamera(_calculatedMeshBoxes.at(i).calcCenter()); - shouldRender = !_viewState ? false : _viewState->shouldRenderMesh(_calculatedMeshBoxes.at(i).getLargestDimension(), - distance); - } - } - - if (!shouldRender) { - continue; // skip this mesh - } - } - - const MeshState& state = _meshStates.at(i); - if (state.clusterMatrices.size() > 1) { - GLBATCH(glUniformMatrix4fv)(locations->clusterMatrices, state.clusterMatrices.size(), false, - (const float*)state.clusterMatrices.constData()); - batch.setModelTransform(Transform()); - } else { - batch.setModelTransform(Transform(state.clusterMatrices[0])); - } - - if (mesh.blendshapes.isEmpty()) { - batch.setInputFormat(networkMesh._vertexFormat); - batch.setInputStream(0, *networkMesh._vertexStream); - } else { - batch.setInputFormat(networkMesh._vertexFormat); - batch.setInputBuffer(0, _blendedVertexBuffers[i], 0, sizeof(glm::vec3)); - batch.setInputBuffer(1, _blendedVertexBuffers[i], vertexCount * sizeof(glm::vec3), sizeof(glm::vec3)); - batch.setInputStream(2, *networkMesh._vertexStream); - } - - if (mesh.colors.isEmpty()) { - GLBATCH(glColor4f)(1.0f, 1.0f, 1.0f, 1.0f); - } - - qint64 offset = 0; - for (int j = 0; j < networkMesh.parts.size(); j++) { - const NetworkMeshPart& networkPart = networkMesh.parts.at(j); - const FBXMeshPart& part = mesh.parts.at(j); - model::MaterialPointer material = part._material; - if ((networkPart.isTranslucent() || part.opacity != 1.0f) != translucent) { - offset += (part.quadIndices.size() + part.triangleIndices.size()) * sizeof(int); - continue; - } - - // apply material properties - if (mode == RenderArgs::SHADOW_RENDER_MODE) { - /// GLBATCH(glBindTexture)(GL_TEXTURE_2D, 0); - - } else { - if (lastMaterialID != part.materialID) { - const bool wantDebug = false; - if (wantDebug) { - qCDebug(renderutils) << "Material Changed ---------------------------------------------"; - qCDebug(renderutils) << "part INDEX:" << j; - qCDebug(renderutils) << "NEW part.materialID:" << part.materialID; - } - - if (locations->materialBufferUnit >= 0) { - batch.setUniformBuffer(locations->materialBufferUnit, material->getSchemaBuffer()); - } - - Texture* diffuseMap = networkPart.diffuseTexture.data(); - if (mesh.isEye && diffuseMap) { - diffuseMap = (_dilatedTextures[i][j] = - static_cast<DilatableNetworkTexture*>(diffuseMap)->getDilatedTexture(_pupilDilation)).data(); - } - static bool showDiffuse = true; - if (showDiffuse && diffuseMap) { - batch.setUniformTexture(0, diffuseMap->getGPUTexture()); - - } else { - batch.setUniformTexture(0, textureCache->getWhiteTexture()); - } - - if (locations->texcoordMatrices >= 0) { - glm::mat4 texcoordTransform[2]; - if (!part.diffuseTexture.transform.isIdentity()) { - part.diffuseTexture.transform.getMatrix(texcoordTransform[0]); - } - if (!part.emissiveTexture.transform.isIdentity()) { - part.emissiveTexture.transform.getMatrix(texcoordTransform[1]); - } - GLBATCH(glUniformMatrix4fv)(locations->texcoordMatrices, 2, false, (const float*) &texcoordTransform); - } - - if (!mesh.tangents.isEmpty()) { - Texture* normalMap = networkPart.normalTexture.data(); - batch.setUniformTexture(1, !normalMap ? - textureCache->getBlueTexture() : normalMap->getGPUTexture()); - - } - - if (locations->specularTextureUnit >= 0) { - Texture* specularMap = networkPart.specularTexture.data(); - batch.setUniformTexture(locations->specularTextureUnit, !specularMap ? - textureCache->getWhiteTexture() : specularMap->getGPUTexture()); - } - } - - // HACK: For unkwon reason (yet!) this code that should be assigned only if the material changes need to be called for every - // drawcall with an emissive, so let's do it for now. - if (locations->emissiveTextureUnit >= 0) { - // assert(locations->emissiveParams >= 0); // we should have the emissiveParams defined in the shader - float emissiveOffset = part.emissiveParams.x; - float emissiveScale = part.emissiveParams.y; - GLBATCH(glUniform2f)(locations->emissiveParams, emissiveOffset, emissiveScale); - - Texture* emissiveMap = networkPart.emissiveTexture.data(); - batch.setUniformTexture(locations->emissiveTextureUnit, !emissiveMap ? - textureCache->getWhiteTexture() : emissiveMap->getGPUTexture()); - } - - lastMaterialID = part.materialID; - } - - meshPartsRendered++; - - if (part.quadIndices.size() > 0) { - batch.drawIndexed(gpu::QUADS, part.quadIndices.size(), offset); - offset += part.quadIndices.size() * sizeof(int); - } - - if (part.triangleIndices.size() > 0) { - batch.drawIndexed(gpu::TRIANGLES, part.triangleIndices.size(), offset); - offset += part.triangleIndices.size() * sizeof(int); - } - - } - } - - return meshPartsRendered; -} ModelBlender::ModelBlender() : _pendingBlenders(0) { diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 6f751a5f8d..7c1572418e 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -51,17 +51,11 @@ namespace render { class PendingChanges; typedef unsigned int ItemID; } -class OpaqueMeshPart; -class TransparentMeshPart; +class MeshPartPayload; -inline uint qHash(const std::shared_ptr<TransparentMeshPart>& a, uint seed) { +inline uint qHash(const std::shared_ptr<MeshPartPayload>& a, uint seed) { return qHash(a.get(), seed); } -inline uint qHash(const std::shared_ptr<OpaqueMeshPart>& a, uint seed) { - return qHash(a.get(), seed); -} - - /// A generic 3D model displaying geometry loaded from a URL. class Model : public QObject, public PhysicsEntity { @@ -77,7 +71,7 @@ public: virtual ~Model(); /// enables/disables scale to fit behavior, the model will be automatically scaled to the specified largest dimension - void setScaleToFit(bool scaleToFit, float largestDimension = 0.0f); + void setScaleToFit(bool scaleToFit, float largestDimension = 0.0f, bool forceRescale = false); bool getScaleToFit() const { return _scaleToFit; } /// is scale to fit enabled bool getIsScaledToFit() const { return _scaledToFit; } /// is model scaled to fit const glm::vec3& getScaleToFitDimensions() const { return _scaleToFitDimensions; } /// the dimensions model is scaled to @@ -350,8 +344,6 @@ private: QVector<QVector<QSharedPointer<Texture> > > _dilatedTextures; - QVector<Model*> _attachments; - QSet<WeakAnimationHandlePointer> _animationHandles; QList<AnimationHandlePointer> _runningAnimations; @@ -401,18 +393,8 @@ private: int _debugMeshBoxesID = GeometryCache::UNKNOWN_ID; // helper functions used by render() or renderInScene() - bool renderCore(RenderArgs* args, float alpha); - int renderMeshes(gpu::Batch& batch, RenderArgs::RenderMode mode, bool translucent, float alphaThreshold, - bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned, bool isWireframe, RenderArgs* args = NULL, - bool forceRenderMeshes = false); - + void setupBatchTransform(gpu::Batch& batch, RenderArgs* args); - QVector<int>* pickMeshList(bool translucent, float alphaThreshold, bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned, bool isWireframe); - - int renderMeshesFromList(QVector<int>& list, gpu::Batch& batch, RenderArgs::RenderMode mode, bool translucent, float alphaThreshold, - RenderArgs* args, Locations* locations, - bool forceRenderSomeMeshes = false); - static void pickPrograms(gpu::Batch& batch, RenderArgs::RenderMode mode, bool translucent, float alphaThreshold, bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned, bool isWireframe, RenderArgs* args, Locations*& locations); @@ -523,35 +505,15 @@ private: }; static RenderPipelineLib _renderPipelineLib; - - class RenderBucket { - public: - QVector<int> _meshes; - QMap<QString, int> _unsortedMeshes; - }; - typedef std::unordered_map<int, RenderBucket> BaseRenderBucketMap; - class RenderBucketMap : public BaseRenderBucketMap { - public: - typedef RenderKey Key; - }; - RenderBucketMap _renderBuckets; - bool _renderCollisionHull; - QSet<std::shared_ptr<TransparentMeshPart>> _transparentRenderItems; - QSet<std::shared_ptr<OpaqueMeshPart>> _opaqueRenderItems; + QSet<std::shared_ptr<MeshPartPayload>> _transparentRenderItems; + QSet<std::shared_ptr<MeshPartPayload>> _opaqueRenderItems; QMap<render::ItemID, render::PayloadPointer> _renderItems; bool _readyWhenAdded = false; bool _needsReload = true; - -private: - // FIX ME - We want to get rid of this interface for rendering... - // right now the only remaining user are Avatar attachments. - // that usage has been temporarily disabled... - bool render(RenderArgs* renderArgs, float alpha = 1.0f); - }; Q_DECLARE_METATYPE(QPointer<Model>) diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 777d9466a5..d9dda279e0 100755 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -50,6 +50,7 @@ RenderDeferredTask::RenderDeferredTask() : Task() { _jobs.push_back(Job(RenderDeferred())); _jobs.push_back(Job(ResolveDeferred())); _jobs.push_back(Job(DrawTransparentDeferred())); + _jobs.push_back(Job(DrawPostLayered())); _jobs.push_back(Job(ResetGLState())); } @@ -85,7 +86,7 @@ template <> void render::jobRun(const DrawOpaqueDeferred& job, const SceneContex // render opaques auto& scene = sceneContext->_scene; - auto& items = scene->getMasterBucket().at(ItemFilter::Builder::opaqueShape()); + auto& items = scene->getMasterBucket().at(ItemFilter::Builder::opaqueShape().withoutLayered()); auto& renderDetails = renderContext->args->_details; ItemIDsBounds inItems; @@ -135,10 +136,12 @@ template <> void render::jobRun(const DrawOpaqueDeferred& job, const SceneContex Transform viewMat; args->_viewFrustum->evalProjectionMatrix(projMat); args->_viewFrustum->evalViewTransform(viewMat); + if (args->_renderMode == RenderArgs::MIRROR_RENDER_MODE) { + viewMat.postScale(glm::vec3(-1.0f, 1.0f, 1.0f)); + } batch.setProjectionTransform(projMat); batch.setViewTransform(viewMat); - renderContext->args->_renderMode = RenderArgs::NORMAL_RENDER_MODE; { GLenum buffers[3]; int bufferCount = 0; @@ -163,7 +166,7 @@ template <> void render::jobRun(const DrawTransparentDeferred& job, const SceneC // render transparents auto& scene = sceneContext->_scene; - auto& items = scene->getMasterBucket().at(ItemFilter::Builder::transparentShape()); + auto& items = scene->getMasterBucket().at(ItemFilter::Builder::transparentShape().withoutLayered()); auto& renderDetails = renderContext->args->_details; ItemIDsBounds inItems; @@ -203,11 +206,12 @@ template <> void render::jobRun(const DrawTransparentDeferred& job, const SceneC Transform viewMat; args->_viewFrustum->evalProjectionMatrix(projMat); args->_viewFrustum->evalViewTransform(viewMat); + if (args->_renderMode == RenderArgs::MIRROR_RENDER_MODE) { + viewMat.postScale(glm::vec3(-1.0f, 1.0f, 1.0f)); + } batch.setProjectionTransform(projMat); batch.setViewTransform(viewMat); - args->_renderMode = RenderArgs::NORMAL_RENDER_MODE; - const float TRANSPARENT_ALPHA_THRESHOLD = 0.0f; { diff --git a/libraries/render-utils/src/TextRenderer3D.cpp b/libraries/render-utils/src/TextRenderer3D.cpp index d081c0480a..0eb560bf72 100644 --- a/libraries/render-utils/src/TextRenderer3D.cpp +++ b/libraries/render-utils/src/TextRenderer3D.cpp @@ -101,7 +101,7 @@ struct QuadBuilder { texMin); } QuadBuilder(const Glyph3D& glyph, const glm::vec2& offset) : - QuadBuilder(offset + glyph.offset - glm::vec2(0.0f, glyph.size.y), glyph.size, + QuadBuilder(offset + glm::vec2(glyph.offset.x, glyph.offset.y - glyph.size.y), glyph.size, glyph.texOffset, glyph.texSize) {} }; @@ -113,7 +113,7 @@ public: void read(QIODevice& path); glm::vec2 computeExtent(const QString& str) const; - float getRowHeight() const { return _rowHeight; } + float getFontSize() const { return _fontSize; } // Render string to batch void drawString(gpu::Batch& batch, float x, float y, const QString& str, @@ -139,7 +139,6 @@ private: // Font characteristics QString _family; float _fontSize = 0.0f; - float _rowHeight = 0.0f; float _leading = 0.0f; float _ascent = 0.0f; float _descent = 0.0f; @@ -251,13 +250,14 @@ glm::vec2 Font3D::computeTokenExtent(const QString& token) const { glm::vec2 Font3D::computeExtent(const QString& str) const { glm::vec2 extent = glm::vec2(0.0f, 0.0f); - QStringList tokens = splitLines(str); - foreach(const QString& token, tokens) { - glm::vec2 tokenExtent = computeTokenExtent(token); - extent.x = std::max(tokenExtent.x, extent.x); + QStringList lines{ splitLines(str) }; + if (!lines.empty()) { + for(const auto& line : lines) { + glm::vec2 tokenExtent = computeTokenExtent(line); + extent.x = std::max(tokenExtent.x, extent.x); + } + extent.y = lines.count() * _fontSize; } - extent.y = tokens.count() * _rowHeight; - return extent; } @@ -288,8 +288,7 @@ void Font3D::read(QIODevice& in) { readStream(in, _descent); readStream(in, _spaceWidth); _fontSize = _ascent + _descent; - _rowHeight = _fontSize + _leading; - + // Read character count uint16_t count; readStream(in, count); @@ -393,7 +392,7 @@ void Font3D::drawString(gpu::Batch& batch, float x, float y, const QString& str, } if (isNewLine || forceNewLine) { // Character return, move the advance to a new line - advance = glm::vec2(x, advance.y - _rowHeight); + advance = glm::vec2(x, advance.y - _leading); if (isNewLine) { // No need to draw anything, go directly to next token @@ -413,7 +412,7 @@ void Font3D::drawString(gpu::Batch& batch, float x, float y, const QString& str, for (auto c : token) { auto glyph = _glyphs[c]; - QuadBuilder qd(glyph, advance - glm::vec2(0.0f, _fontSize)); + QuadBuilder qd(glyph, advance - glm::vec2(0.0f, _ascent)); _verticesBuffer->append(sizeof(QuadBuilder), (const gpu::Byte*)&qd); _numVertices += 4; @@ -477,9 +476,9 @@ glm::vec2 TextRenderer3D::computeExtent(const QString& str) const { return glm::vec2(0.0f, 0.0f); } -float TextRenderer3D::getRowHeight() const { +float TextRenderer3D::getFontSize() const { if (_font) { - return _font->getRowHeight(); + return _font->getFontSize(); } return 0.0f; } diff --git a/libraries/render-utils/src/TextRenderer3D.h b/libraries/render-utils/src/TextRenderer3D.h index 8f55d0c977..e61203b06f 100644 --- a/libraries/render-utils/src/TextRenderer3D.h +++ b/libraries/render-utils/src/TextRenderer3D.h @@ -50,7 +50,7 @@ public: ~TextRenderer3D(); glm::vec2 computeExtent(const QString& str) const; - float getRowHeight() const; + float getFontSize() const; void draw(gpu::Batch& batch, float x, float y, const QString& str, const glm::vec4& color = glm::vec4(-1.0f), const glm::vec2& bounds = glm::vec2(-1.0f)); diff --git a/libraries/render/src/render/DrawTask.cpp b/libraries/render/src/render/DrawTask.cpp index bfc888ea8a..bb4ba00cee 100755 --- a/libraries/render/src/render/DrawTask.cpp +++ b/libraries/render/src/render/DrawTask.cpp @@ -192,7 +192,6 @@ void render::renderItems(const SceneContextPointer& sceneContext, const RenderCo } } - void addClearStateCommands(gpu::Batch& batch) { batch._glDepthMask(true); batch._glDepthFunc(GL_LESS); @@ -264,7 +263,6 @@ template <> void render::jobRun(const DrawOpaque& job, const SceneContextPointer renderContext->_numDrawnOpaqueItems = renderedItems.size(); - ItemIDsBounds sortedItems; sortedItems.reserve(culledItems.size()); if (renderContext->_sortOpaque) { @@ -281,10 +279,12 @@ template <> void render::jobRun(const DrawOpaque& job, const SceneContextPointer Transform viewMat; args->_viewFrustum->evalProjectionMatrix(projMat); args->_viewFrustum->evalViewTransform(viewMat); + if (args->_renderMode == RenderArgs::MIRROR_RENDER_MODE) { + viewMat.postScale(glm::vec3(-1.0f, 1.0f, 1.0f)); + } batch.setProjectionTransform(projMat); batch.setViewTransform(viewMat); - renderContext->args->_renderMode = RenderArgs::NORMAL_RENDER_MODE; { GLenum buffers[3]; int bufferCount = 0; @@ -348,11 +348,12 @@ template <> void render::jobRun(const DrawTransparent& job, const SceneContextPo Transform viewMat; args->_viewFrustum->evalProjectionMatrix(projMat); args->_viewFrustum->evalViewTransform(viewMat); + if (args->_renderMode == RenderArgs::MIRROR_RENDER_MODE) { + viewMat.postScale(glm::vec3(-1.0f, 1.0f, 1.0f)); + } batch.setProjectionTransform(projMat); batch.setViewTransform(viewMat); - args->_renderMode = RenderArgs::NORMAL_RENDER_MODE; - const float MOSTLY_OPAQUE_THRESHOLD = 0.75f; const float TRANSPARENT_ALPHA_THRESHOLD = 0.0f; @@ -435,6 +436,9 @@ template <> void render::jobRun(const DrawBackground& job, const SceneContextPoi Transform viewMat; args->_viewFrustum->evalProjectionMatrix(projMat); args->_viewFrustum->evalViewTransform(viewMat); + if (args->_renderMode == RenderArgs::MIRROR_RENDER_MODE) { + viewMat.postScale(glm::vec3(-1.0f, 1.0f, 1.0f)); + } batch.setProjectionTransform(projMat); batch.setViewTransform(viewMat); @@ -446,6 +450,51 @@ template <> void render::jobRun(const DrawBackground& job, const SceneContextPoi args->_context->syncCache(); } +template <> void render::jobRun(const DrawPostLayered& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { + PerformanceTimer perfTimer("DrawPostLayered"); + assert(renderContext->args); + assert(renderContext->args->_viewFrustum); + + // render backgrounds + auto& scene = sceneContext->_scene; + auto& items = scene->getMasterBucket().at(ItemFilter::Builder::opaqueShape().withLayered()); + + + ItemIDsBounds inItems; + inItems.reserve(items.size()); + for (auto id : items) { + auto& item = scene->getItem(id); + if (item.getKey().isVisible() && (item.getLayer() > 0)) { + inItems.emplace_back(id); + } + } + if (inItems.empty()) { + return; + } + + RenderArgs* args = renderContext->args; + gpu::Batch batch; + args->_batch = &batch; + + glm::mat4 projMat; + Transform viewMat; + args->_viewFrustum->evalProjectionMatrix(projMat); + args->_viewFrustum->evalViewTransform(viewMat); + if (args->_renderMode == RenderArgs::MIRROR_RENDER_MODE) { + viewMat.postScale(glm::vec3(-1.0f, 1.0f, 1.0f)); + } + batch.setProjectionTransform(projMat); + batch.setViewTransform(viewMat); + + batch.clearFramebuffer(gpu::Framebuffer::BUFFER_DEPTH, glm::vec4(), 1.f, 0); + + renderItems(sceneContext, renderContext, inItems); + args->_context->render((*args->_batch)); + args->_batch = nullptr; + + // Force the context sync + args->_context->syncCache(); +} void ItemMaterialBucketMap::insert(const ItemID& id, const model::MaterialKey& key) { diff --git a/libraries/render/src/render/DrawTask.h b/libraries/render/src/render/DrawTask.h index 1f260583f2..0052c314c0 100755 --- a/libraries/render/src/render/DrawTask.h +++ b/libraries/render/src/render/DrawTask.h @@ -87,6 +87,14 @@ public: }; template <> void jobRun(const DrawBackground& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext); + +class DrawPostLayered { +public: +}; +template <> void jobRun(const DrawPostLayered& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext); + + + class ResetGLState { public: }; diff --git a/libraries/render/src/render/Scene.cpp b/libraries/render/src/render/Scene.cpp index 8615f7cf7a..1d2e54541b 100644 --- a/libraries/render/src/render/Scene.cpp +++ b/libraries/render/src/render/Scene.cpp @@ -45,10 +45,12 @@ void ItemBucketMap::reset(const ItemID& id, const ItemKey& oldKey, const ItemKey } void ItemBucketMap::allocateStandardOpaqueTranparentBuckets() { - (*this)[ItemFilter::Builder::opaqueShape()]; - (*this)[ItemFilter::Builder::transparentShape()]; + (*this)[ItemFilter::Builder::opaqueShape().withoutLayered()]; + (*this)[ItemFilter::Builder::transparentShape().withoutLayered()]; (*this)[ItemFilter::Builder::light()]; (*this)[ItemFilter::Builder::background()]; + (*this)[ItemFilter::Builder::opaqueShape().withLayered()]; + (*this)[ItemFilter::Builder::transparentShape().withLayered()]; } @@ -61,11 +63,6 @@ void Item::resetPayload(const PayloadPointer& payload) { } } -void Item::kill() { - _payload.reset(); - _key._flags.reset(); -} - void PendingChanges::resetItem(ItemID id, const PayloadPointer& payload) { _resetItems.push_back(id); _resetPayloads.push_back(payload); @@ -164,27 +161,3 @@ void Scene::updateItems(const ItemIDs& ids, UpdateFunctors& functors) { _items[(*updateID)].update((*updateFunctor)); } } - -void Scene::registerObserver(ObserverPointer& observer) { - // make sure it's a valid observer - if (observer && (observer->getScene() == nullptr)) { - // Then register the observer - _observers.push_back(observer); - - // And let it do what it wants to do - observer->registerScene(this); - } -} - -void Scene::unregisterObserver(ObserverPointer& observer) { - // make sure it's a valid observer currently registered - if (observer && (observer->getScene() == this)) { - // let it do what it wants to do - observer->unregisterScene(); - - // Then unregister the observer - auto it = std::find(_observers.begin(), _observers.end(), observer); - _observers.erase(it); - } -} - diff --git a/libraries/render/src/render/Scene.h b/libraries/render/src/render/Scene.h index 8cb29609ba..5ec9f0c951 100644 --- a/libraries/render/src/render/Scene.h +++ b/libraries/render/src/render/Scene.h @@ -36,7 +36,6 @@ public: enum FlagBit { TYPE_SHAPE = 0, // Item is a Shape TYPE_LIGHT, // Item is a Light - TYPE_BACKGROUND, // Item is a Background TRANSLUCENT, // Transparent and not opaque, for some odd reason TRANSPARENCY doesn't work... VIEW_SPACE, // Transformed in view space, and not in world space DYNAMIC, // Dynamic and bound will change unlike static item @@ -44,7 +43,7 @@ public: INVISIBLE, // Visible or not? could be just here to cast shadow SHADOW_CASTER, // Item cast shadows PICKABLE, // Item can be picked/selected - NO_DEPTH_SORT, // Item should not be depth sorted + LAYERED, // Item belongs to one of the layers different from the default layer NUM_FLAGS, // Not a valid flag }; @@ -57,6 +56,7 @@ public: ItemKey(const Flags& flags) : _flags(flags) {} class Builder { + friend class ItemKey; Flags _flags{ 0 }; public: Builder() {} @@ -65,7 +65,6 @@ public: Builder& withTypeShape() { _flags.set(TYPE_SHAPE); return (*this); } Builder& withTypeLight() { _flags.set(TYPE_LIGHT); return (*this); } - Builder& withTypeBackground() { _flags.set(TYPE_BACKGROUND); return (*this); } Builder& withTransparent() { _flags.set(TRANSLUCENT); return (*this); } Builder& withViewSpace() { _flags.set(VIEW_SPACE); return (*this); } Builder& withDynamic() { _flags.set(DYNAMIC); return (*this); } @@ -73,14 +72,15 @@ public: Builder& withInvisible() { _flags.set(INVISIBLE); return (*this); } Builder& withShadowCaster() { _flags.set(SHADOW_CASTER); return (*this); } Builder& withPickable() { _flags.set(PICKABLE); return (*this); } - Builder& withNoDepthSort() { _flags.set(NO_DEPTH_SORT); return (*this); } + Builder& withLayered() { _flags.set(LAYERED); return (*this); } // Convenient standard keys that we will keep on using all over the place - static ItemKey opaqueShape() { return Builder().withTypeShape().build(); } - static ItemKey transparentShape() { return Builder().withTypeShape().withTransparent().build(); } - static ItemKey light() { return Builder().withTypeLight().build(); } - static ItemKey background() { return Builder().withTypeBackground().build(); } + static Builder opaqueShape() { return Builder().withTypeShape(); } + static Builder transparentShape() { return Builder().withTypeShape().withTransparent(); } + static Builder light() { return Builder().withTypeLight(); } + static Builder background() { return Builder().withViewSpace().withLayered(); } }; + ItemKey(const Builder& builder) : ItemKey(builder._flags) {} bool isOpaque() const { return !_flags[TRANSLUCENT]; } bool isTransparent() const { return _flags[TRANSLUCENT]; } @@ -101,8 +101,7 @@ public: bool isPickable() const { return _flags[PICKABLE]; } - bool isDepthSort() const { return !_flags[NO_DEPTH_SORT]; } - bool isNoDepthSort() const { return _flags[NO_DEPTH_SORT]; } + bool isLayered() const { return _flags[LAYERED]; } }; inline QDebug operator<<(QDebug debug, const ItemKey& itemKey) { @@ -122,6 +121,7 @@ public: ItemFilter(const ItemKey::Flags& value = ItemKey::Flags(0), const ItemKey::Flags& mask = ItemKey::Flags(0)) : _value(value), _mask(mask) {} class Builder { + friend class ItemFilter; ItemKey::Flags _value{ 0 }; ItemKey::Flags _mask{ 0 }; public: @@ -131,7 +131,6 @@ public: Builder& withTypeShape() { _value.set(ItemKey::TYPE_SHAPE); _mask.set(ItemKey::TYPE_SHAPE); return (*this); } Builder& withTypeLight() { _value.set(ItemKey::TYPE_LIGHT); _mask.set(ItemKey::TYPE_LIGHT); return (*this); } - Builder& withTypeBackground() { _value.set(ItemKey::TYPE_BACKGROUND); _mask.set(ItemKey::TYPE_BACKGROUND); return (*this); } Builder& withOpaque() { _value.reset(ItemKey::TRANSLUCENT); _mask.set(ItemKey::TRANSLUCENT); return (*this); } Builder& withTransparent() { _value.set(ItemKey::TRANSLUCENT); _mask.set(ItemKey::TRANSLUCENT); return (*this); } @@ -153,16 +152,18 @@ public: Builder& withPickable() { _value.set(ItemKey::PICKABLE); _mask.set(ItemKey::PICKABLE); return (*this); } - Builder& withDepthSort() { _value.reset(ItemKey::NO_DEPTH_SORT); _mask.set(ItemKey::NO_DEPTH_SORT); return (*this); } - Builder& withNotDepthSort() { _value.set(ItemKey::NO_DEPTH_SORT); _mask.set(ItemKey::NO_DEPTH_SORT); return (*this); } + Builder& withoutLayered() { _value.reset(ItemKey::LAYERED); _mask.set(ItemKey::LAYERED); return (*this); } + Builder& withLayered() { _value.set(ItemKey::LAYERED); _mask.set(ItemKey::LAYERED); return (*this); } // Convenient standard keys that we will keep on using all over the place - static ItemFilter opaqueShape() { return Builder().withTypeShape().withOpaque().withWorldSpace().build(); } - static ItemFilter transparentShape() { return Builder().withTypeShape().withTransparent().withWorldSpace().build(); } - static ItemFilter light() { return Builder().withTypeLight().build(); } - static ItemFilter background() { return Builder().withTypeBackground().build(); } + static Builder opaqueShape() { return Builder().withTypeShape().withOpaque().withWorldSpace(); } + static Builder transparentShape() { return Builder().withTypeShape().withTransparent().withWorldSpace(); } + static Builder light() { return Builder().withTypeLight(); } + static Builder background() { return Builder().withViewSpace().withLayered(); } }; + ItemFilter(const Builder& builder) : ItemFilter(builder._value, builder._mask) {} + // Item Filter operator testing if a key pass the filter bool test(const ItemKey& key) const { return (key._flags & _mask) == (_value & _mask); } @@ -179,7 +180,7 @@ public: }; inline QDebug operator<<(QDebug debug, const ItemFilter& me) { - debug << "[ItemFilter: opaqueShape:" << me.test(ItemKey::Builder::opaqueShape()) + debug << "[ItemFilter: opaqueShape:" << me.test(ItemKey::Builder::opaqueShape().build()) << "]"; return debug; } @@ -214,38 +215,43 @@ public: public: virtual const ItemKey getKey() const = 0; virtual const Bound getBound() const = 0; - virtual void render(RenderArgs* args) = 0; + virtual int getLayer() const = 0; - virtual void update(const UpdateFunctorPointer& functor) = 0; + virtual void render(RenderArgs* args) = 0; virtual const model::MaterialKey getMaterialKey() const = 0; ~PayloadInterface() {} protected: + friend class Item; + virtual void update(const UpdateFunctorPointer& functor) = 0; }; - - - typedef std::shared_ptr<PayloadInterface> PayloadPointer; - - Item() {} ~Item() {} + // Main scene / item managment interface Reset/Update/Kill void resetPayload(const PayloadPointer& payload); - void kill(); + void update(const UpdateFunctorPointer& updateFunctor) { _payload->update(updateFunctor); } // Communicate update to the payload + void kill() { _payload.reset(); _key._flags.reset(); } // Kill means forget the payload and key // Check heuristic key const ItemKey& getKey() const { return _key; } // Payload Interface + + // Get the bound of the item expressed in world space (or eye space depending on the key.isWorldSpace()) const Bound getBound() const { return _payload->getBound(); } + + // Get the layer where the item belongs. 0 by default meaning NOT LAYERED + int getLayer() const { return _payload->getLayer(); } + + // Render call for the item void render(RenderArgs* args) { _payload->render(args); } - void update(const UpdateFunctorPointer& updateFunctor) { _payload->update(updateFunctor); } // Shape Type Interface - const model::MaterialKey& getMaterialKey() const { return _payload->getMaterialKey(); } + const model::MaterialKey getMaterialKey() const { return _payload->getMaterialKey(); } protected: PayloadPointer _payload; @@ -280,6 +286,7 @@ inline QDebug operator<<(QDebug debug, const Item& item) { // of the Payload interface template <class T> const ItemKey payloadGetKey(const std::shared_ptr<T>& payloadData) { return ItemKey(); } template <class T> const Item::Bound payloadGetBound(const std::shared_ptr<T>& payloadData) { return Item::Bound(); } +template <class T> int payloadGetLayer(const std::shared_ptr<T>& payloadData) { return 0; } template <class T> void payloadRender(const std::shared_ptr<T>& payloadData, RenderArgs* args) { } // Shape type interface @@ -290,19 +297,25 @@ public: typedef std::shared_ptr<T> DataPointer; typedef UpdateFunctor<T> Updater; - virtual void update(const UpdateFunctorPointer& functor) { static_cast<Updater*>(functor.get())->_func((*_data)); } + Payload(const DataPointer& data) : _data(data) {} // Payload general interface virtual const ItemKey getKey() const { return payloadGetKey<T>(_data); } virtual const Item::Bound getBound() const { return payloadGetBound<T>(_data); } + virtual int getLayer() const { return payloadGetLayer<T>(_data); } + + virtual void render(RenderArgs* args) { payloadRender<T>(_data, args); } // Shape Type interface virtual const model::MaterialKey getMaterialKey() const { return shapeGetMaterialKey<T>(_data); } - Payload(const DataPointer& data) : _data(data) {} protected: DataPointer _data; + + // Update mechanics + virtual void update(const UpdateFunctorPointer& functor) { static_cast<Updater*>(functor.get())->_func((*_data)); } + friend class Item; }; // Let's show how to make a simple FooPayload example: @@ -358,6 +371,7 @@ public: typedef std::vector< ItemIDAndBounds > ItemIDsBounds; + // A map of ItemIDSets allowing to create bucket lists of items which are filtering correctly class ItemBucketMap : public std::map<ItemFilter, ItemIDSet, ItemFilter::Less> { public: @@ -374,8 +388,6 @@ public: }; class Engine; -class Observer; - class PendingChanges { public: @@ -411,36 +423,6 @@ typedef std::queue<PendingChanges> PendingChangesQueue; // Items are notified accordingly on any update message happening class Scene { public: - - class Observer { - public: - Observer(Scene* scene) { - - } - ~Observer() {} - - const Scene* getScene() const { return _scene; } - Scene* editScene() { return _scene; } - - protected: - Scene* _scene = nullptr; - virtual void onRegisterScene() {} - virtual void onUnregisterScene() {} - - friend class Scene; - void registerScene(Scene* scene) { - _scene = scene; - onRegisterScene(); - } - - void unregisterScene() { - onUnregisterScene(); - _scene = 0; - } - }; - typedef std::shared_ptr< Observer > ObserverPointer; - typedef std::vector< ObserverPointer > Observers; - Scene(); ~Scene() {} @@ -450,11 +432,6 @@ public: /// Enqueue change batch to the scene void enqueuePendingChanges(const PendingChanges& pendingChanges); - /// Scene Observer listen to any change and get notified - void registerObserver(ObserverPointer& observer); - void unregisterObserver(ObserverPointer& observer); - - /// Access the main bucketmap of items const ItemBucketMap& getMasterBucket() const { return _masterBucketMap; } @@ -483,10 +460,6 @@ protected: void removeItems(const ItemIDs& ids); void updateItems(const ItemIDs& ids, UpdateFunctors& functors); - - // The scene context listening for any change to the database - Observers _observers; - friend class Engine; }; diff --git a/libraries/script-engine/src/AbstractControllerScriptingInterface.h b/libraries/script-engine/src/AbstractControllerScriptingInterface.h index 11755add8e..43076039a9 100644 --- a/libraries/script-engine/src/AbstractControllerScriptingInterface.h +++ b/libraries/script-engine/src/AbstractControllerScriptingInterface.h @@ -52,6 +52,8 @@ class AbstractControllerScriptingInterface : public QObject { Q_OBJECT public slots: + virtual void registerControllerTypes(QScriptEngine* engine) = 0; + virtual bool isPrimaryButtonPressed() const = 0; virtual glm::vec2 getPrimaryJoystickPosition() const = 0; diff --git a/libraries/script-engine/src/AudioScriptingInterface.h b/libraries/script-engine/src/AudioScriptingInterface.h index d74e1ed1e0..470d038196 100644 --- a/libraries/script-engine/src/AudioScriptingInterface.h +++ b/libraries/script-engine/src/AudioScriptingInterface.h @@ -39,6 +39,7 @@ signals: void mutedByMixer(); void environmentMuted(); void receivedFirstPacket(); + void disconnected(); private: AudioScriptingInterface(); diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 30c3fbe8e4..182a0aea8d 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -317,6 +317,7 @@ void ScriptEngine::init() { registerAnimationTypes(this); registerAvatarTypes(this); registerAudioMetaTypes(this); + _controllerScriptingInterface->registerControllerTypes(this); qScriptRegisterMetaType(this, EntityItemPropertiesToScriptValue, EntityItemPropertiesFromScriptValueHonorReadOnly); qScriptRegisterMetaType(this, EntityItemIDtoScriptValue, EntityItemIDfromScriptValue); diff --git a/libraries/shared/src/PhysicsHelpers.h b/libraries/shared/src/PhysicsHelpers.h index bef7275067..0e58ae99f0 100644 --- a/libraries/shared/src/PhysicsHelpers.h +++ b/libraries/shared/src/PhysicsHelpers.h @@ -15,6 +15,7 @@ #include <glm/glm.hpp> #include <glm/gtc/quaternion.hpp> +const int PHYSICS_ENGINE_MAX_NUM_SUBSTEPS = 4; const float PHYSICS_ENGINE_FIXED_SUBSTEP = 1.0f / 60.0f; // return incremental rotation (Bullet-style) caused by angularVelocity over timeStep diff --git a/libraries/ui/src/CursorManager.cpp b/libraries/ui/src/CursorManager.cpp new file mode 100644 index 0000000000..38c746994d --- /dev/null +++ b/libraries/ui/src/CursorManager.cpp @@ -0,0 +1,92 @@ +// +// Created by Bradley Austin Davis on 2015/06/08 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "CursorManager.h" + +#include <QCursor> +#include <QWidget> +#include <QUrl> + +#include <PathUtils.h> + +namespace Cursor { + + void Instance::setIcon(uint16_t icon) { + _icon = icon; + } + + uint16_t Instance::getIcon() const { + return _icon; + } + + + class MouseInstance : public Instance { + Source getType() const { + return Source::MOUSE; + } + + ivec2 getScreenPosition() const { + return toGlm(QCursor::pos()); + } + + ivec2 getWindowPosition(QWidget* widget) const { + return toGlm(widget->mapFromGlobal(QCursor::pos())); + } + + vec2 getRelativePosition(QWidget* widget) const { + vec2 pos = getWindowPosition(widget); + pos /= vec2(toGlm(widget->size())); + return pos; + } + }; + + static QMap<uint16_t, QString> ICONS; + static uint16_t _customIconId = Icon::USER_BASE; + + Manager::Manager() { + ICONS[Icon::DEFAULT] = PathUtils::resourcesPath() + "images/arrow.png"; + ICONS[Icon::LINK] = PathUtils::resourcesPath() + "images/reticleLink.png"; + } + + Manager& Manager::instance() { + static Manager instance; + return instance; + } + + uint8_t Manager::getCount() { + return 1; + } + + Instance* Manager::getCursor(uint8_t index) { + Q_ASSERT(index < getCount()); + static MouseInstance mouseInstance; + if (index == 0) { + return &mouseInstance; + } + return nullptr; + } + + uint16_t Manager::registerIcon(const QString& path) { + ICONS[_customIconId] = path; + return _customIconId++; + } + + const QString& Manager::getIconImage(uint16_t icon) { + Q_ASSERT(ICONS.count(icon)); + return ICONS[icon]; + } + + float Manager::getScale() { + return _scale; + } + + void Manager::setScale(float scale) { + _scale = scale; + } + +} \ No newline at end of file diff --git a/libraries/ui/src/CursorManager.h b/libraries/ui/src/CursorManager.h new file mode 100644 index 0000000000..c5810caf58 --- /dev/null +++ b/libraries/ui/src/CursorManager.h @@ -0,0 +1,61 @@ +// +// Created by Bradley Austin Davis on 2015/06/08 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#pragma once +#include <stdint.h> + +#include <GLMHelpers.h> + +namespace Cursor { + enum class Source { + MOUSE, + LEFT_HAND, + RIGHT_HAND, + UNKNOWN, + }; + + enum Icon { + DEFAULT, + LINK, + GRAB, + + // Add new system cursors here + + // User cursors will have ids over this value + USER_BASE = 0xFF, + }; + + class Instance { + public: + virtual Source getType() const = 0; + virtual ivec2 getWindowPosition(QWidget* widget) const = 0; + virtual vec2 getRelativePosition(QWidget* widget) const = 0; + virtual ivec2 getScreenPosition() const = 0; + virtual void setIcon(uint16_t icon); + virtual uint16_t getIcon() const; + private: + uint16_t _icon; + }; + + class Manager { + Manager(); + Manager(const Manager& other) = delete; + public: + static Manager& instance(); + uint8_t getCount(); + float getScale(); + void setScale(float scale); + Instance* getCursor(uint8_t index = 0); + uint16_t registerIcon(const QString& path); + const QString& getIconImage(uint16_t icon); + private: + float _scale{ 1.0f }; + }; +} + +