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/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/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/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/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 @@ + +
+
Hyperlink
+
Href
+
+ +
+
Description
+ +
+
+
Locked 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/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/src/Application.cpp b/interface/src/Application.cpp index 1763623fa6..750955dc6a 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -100,6 +100,7 @@ #include "ModelPackager.h" #include "Util.h" #include "InterfaceLogging.h" +#include "InterfaceActionFactory.h" #include "avatar/AvatarManager.h" @@ -257,6 +258,7 @@ bool setupEssentials(int& argc, char** argv) { DependencyManager::registerInheritance(); DependencyManager::registerInheritance(); + DependencyManager::registerInheritance(); Setting::init(); @@ -293,7 +295,8 @@ bool setupEssentials(int& argc, char** argv) { auto discoverabilityManager = DependencyManager::set(); auto sceneScriptingInterface = DependencyManager::set(); auto offscreenUi = DependencyManager::set(); - auto pathUtils = DependencyManager::set(); + auto pathUtils = DependencyManager::set(); + auto actionFactory = DependencyManager::set(); return true; } @@ -524,8 +527,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); diff --git a/interface/src/Application.h b/interface/src/Application.h index f63802ce24..68bf60781a 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -149,6 +149,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 +#include +#include + +#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 8708a2b2b0..dbe7d52a3f 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,12 +301,21 @@ bool Avatar::addToScene(AvatarSharedPointer self, std::shared_ptr pendingChanges.resetItem(_renderItemID, avatarPayloadPointer); _skeletonModel.addToScene(scene, pendingChanges); getHead()->getFaceModel().addToScene(scene, pendingChanges); + + for (auto attachmentModel : _attachmentModels) { + attachmentModel->addToScene(scene, pendingChanges); + } + return true; } void Avatar::removeFromScene(AvatarSharedPointer self, std::shared_ptr scene, render::PendingChanges& pendingChanges) { 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) { @@ -516,7 +528,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(); @@ -529,8 +541,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); @@ -544,10 +572,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); @@ -571,22 +595,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 } @@ -944,12 +960,18 @@ void Avatar::setAttachmentData(const QVector& 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 _attachmentModels; + QVector _attachmentsToRemove; + QVector _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()->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 + +#include +#include + +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/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 49a8012cd9..f16173b79e 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -55,7 +55,7 @@ AvatarManager::AvatarManager(QObject* parent) : _avatarFades() { // register a meta type for the weak pointer we'll use for the owning avatar mixer for each avatar qRegisterMetaType >("NodeWeakPointer"); - _myAvatar = std::shared_ptr(new MyAvatar()); + _myAvatar = std::make_shared(); } void AvatarManager::init() { @@ -97,9 +97,9 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { // simulate avatars AvatarHash::iterator avatarIterator = _avatarHash.begin(); while (avatarIterator != _avatarHash.end()) { - Avatar* avatar = reinterpret_cast(avatarIterator.value().get()); + auto avatar = std::dynamic_pointer_cast(avatarIterator.value()); - if (avatar == _myAvatar.get() || !avatar->isInitialized()) { + if (avatar == _myAvatar || !avatar->isInitialized()) { // DO NOT update _myAvatar! Its update has already been done earlier in the main loop. // DO NOT update or fade out uninitialized Avatars ++avatarIterator; @@ -121,26 +121,30 @@ void AvatarManager::simulateAvatarFades(float deltaTime) { const float SHRINK_RATE = 0.9f; const float MIN_FADE_SCALE = 0.001f; - + + render::ScenePointer scene = Application::getInstance()->getMain3DScene(); + render::PendingChanges pendingChanges; while (fadingIterator != _avatarFades.end()) { - Avatar* avatar = static_cast(fadingIterator->get()); + auto avatar = std::static_pointer_cast(*fadingIterator); avatar->setTargetScale(avatar->getScale() * SHRINK_RATE, true); if (avatar->getTargetScale() < MIN_FADE_SCALE) { + avatar->removeFromScene(*fadingIterator, scene, pendingChanges); fadingIterator = _avatarFades.erase(fadingIterator); } else { avatar->simulate(deltaTime); ++fadingIterator; } } + scene->enqueuePendingChanges(pendingChanges); } AvatarSharedPointer AvatarManager::newSharedAvatar() { - return AvatarSharedPointer(new Avatar()); + return AvatarSharedPointer(std::make_shared()); } // virtual AvatarSharedPointer AvatarManager::addAvatar(const QUuid& sessionUUID, const QWeakPointer& mixerWeakPointer) { - std::shared_ptr avatar = std::dynamic_pointer_cast(AvatarHashMap::addAvatar(sessionUUID, mixerWeakPointer)); + auto avatar = std::dynamic_pointer_cast(AvatarHashMap::addAvatar(sessionUUID, mixerWeakPointer)); render::ScenePointer scene = Application::getInstance()->getMain3DScene(); render::PendingChanges pendingChanges; avatar->addToScene(avatar, scene, pendingChanges); @@ -171,10 +175,6 @@ void AvatarManager::removeAvatar(const QUuid& sessionUUID) { _avatarFades.push_back(avatarIterator.value()); _avatarHash.erase(avatarIterator); } - render::ScenePointer scene = Application::getInstance()->getMain3DScene(); - render::PendingChanges pendingChanges; - avatar->removeFromScene(avatar, scene, pendingChanges); - scene->enqueuePendingChanges(pendingChanges); } } @@ -182,12 +182,12 @@ void AvatarManager::clearOtherAvatars() { // clear any avatars that came from an avatar-mixer AvatarHash::iterator avatarIterator = _avatarHash.begin(); while (avatarIterator != _avatarHash.end()) { - Avatar* avatar = reinterpret_cast(avatarIterator.value().get()); - if (avatar == _myAvatar.get() || !avatar->isInitialized()) { + auto avatar = std::static_pointer_cast(avatarIterator.value()); + if (avatar == _myAvatar || !avatar->isInitialized()) { // don't remove myAvatar or uninitialized avatars from the list ++avatarIterator; } else { - removeAvatarMotionState(avatar); + removeAvatarMotionState(avatar.get()); _avatarFades.push_back(avatarIterator.value()); avatarIterator = _avatarHash.erase(avatarIterator); } @@ -250,7 +250,7 @@ void AvatarManager::handleCollisionEvents(CollisionEvents& collisionEvents) { void AvatarManager::updateAvatarPhysicsShape(const QUuid& id) { AvatarHash::iterator avatarItr = _avatarHash.find(id); if (avatarItr != _avatarHash.end()) { - Avatar* avatar = static_cast(avatarItr.value().get()); + auto avatar = std::static_pointer_cast(avatarItr.value()); AvatarMotionState* motionState = avatar->_motionState; if (motionState) { motionState->addDirtyFlags(EntityItem::DIRTY_SHAPE); @@ -259,7 +259,7 @@ void AvatarManager::updateAvatarPhysicsShape(const QUuid& id) { avatar->computeShapeInfo(shapeInfo); btCollisionShape* shape = ObjectMotionState::getShapeManager()->getShape(shapeInfo); if (shape) { - AvatarMotionState* motionState = new AvatarMotionState(avatar, shape); + AvatarMotionState* motionState = new AvatarMotionState(avatar.get(), shape); avatar->_motionState = motionState; _motionStatesToAdd.insert(motionState); _avatarMotionStates.insert(motionState); 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 { + QVector 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(); +static int inputChannelMetaTypeId = qRegisterMetaType(); +static int inputMetaTypeId = qRegisterMetaType(); +static int inputPairMetaTypeId = qRegisterMetaType(); + +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 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 >(engine); + qScriptRegisterSequenceMetaType >(engine); + qScriptRegisterSequenceMetaType >(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 ControllerScriptingInterface::getAllActions() { + return Application::getUserInputMapper()->getAllActions(); +} + +QVector ControllerScriptingInterface::getInputChannelsForAction(UserInputMapper::Action action) { + return Application::getUserInputMapper()->getInputChannelsForAction(action); +} + +QString ControllerScriptingInterface::getDeviceName(unsigned int device) { + return Application::getUserInputMapper()->getDeviceName((unsigned short) device); +} + +QVector 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 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 +#include "ui/UserInputMapper.h" + #include 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 getAllActions(); + Q_INVOKABLE virtual QVector getInputChannelsForAction(UserInputMapper::Action action); + Q_INVOKABLE virtual QString getDeviceName(unsigned int device); + Q_INVOKABLE virtual QVector getAllInputsForDevice(unsigned int device); + Q_INVOKABLE virtual bool addInputChannel(UserInputMapper::InputChannel inputChannel); + Q_INVOKABLE virtual bool removeInputChannel(UserInputMapper::InputChannel inputChannel); + Q_INVOKABLE virtual QVector 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()->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/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 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 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::getAllInputsForDevice(uint16 device) { + InputChannels allChannels; + getInputChannels(allChannels); + + QVector 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::getAllActions() { + QVector actions; + for (auto i = 0; i < NUM_ACTIONS; i++) { + actions.append(Action(i)); + } + return actions; +} + +QVector UserInputMapper::getInputChannelsForAction(UserInputMapper::Action action) { + QVector inputChannels; + std::pair 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 ButtonGetter; typedef std::function AxisGetter; typedef std::function JointGetter; + typedef QPair InputPair; + typedef std::function ()> AvailableInputGetter; + typedef std::function ResetBindings; + + typedef QVector 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 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(); }; + ResetBindings resetDeviceBindings = [] () -> bool { return true; }; + + typedef std::shared_ptr 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 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 _actionNames = std::vector(NUM_ACTIONS); + void createActionNames(); + QVector 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 getAllInputsForDevice(uint16 device); + QVector getInputChannelsForAction(UserInputMapper::Action action); + std::multimap 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 DevicesMap; @@ -177,4 +215,12 @@ protected: std::vector _actionScales = std::vector(NUM_ACTIONS, 1.0f); }; +Q_DECLARE_METATYPE(UserInputMapper::InputPair) +Q_DECLARE_METATYPE(QVector) +Q_DECLARE_METATYPE(UserInputMapper::Input) +Q_DECLARE_METATYPE(UserInputMapper::InputChannel) +Q_DECLARE_METATYPE(QVector) +Q_DECLARE_METATYPE(UserInputMapper::Action) +Q_DECLARE_METATYPE(QVector) + #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(overlay.get())->getDrawOnHUD()) { if (static_cast(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(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/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(_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 #include #include #include +#include #include -#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(); - const QUuid& myNodeID = nodeList->getSessionUUID(); - highlightSimulationOwnership = (getSimulatorID() == myNodeID); - } Q_ASSERT(args->_batch); gpu::Batch& batch = *args->_batch; - batch.setModelTransform(getTransformToCenter()); - if (highlightSimulationOwnership) { - DependencyManager::get()->renderWireCube(batch, 1.0f, cubeColor); - } else { - DependencyManager::get()->renderSolidCube(batch, 1.0f, cubeColor); - } + batch.setModelTransform(getTransformToCenter()); // we want to include the scale as well + DependencyManager::get()->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 -#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 #include #include - #include -#include - -#include "RenderableDebugableEntityItem.h" +#include 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()->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()->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(); + 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(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/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(); - 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/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 #include @@ -18,7 +20,7 @@ #include #include -#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()->renderSolidSphere(batch, 0.5f, SLICES, STACKS, sphereColor); + + RenderableDebugableEntityItem::render(this, args); }; 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 #include +#include #include #include #include @@ -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 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 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 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 scene, render::PendingChanges& pendingChanges); + virtual void removeFromScene(EntityItemPointer self, std::shared_ptr 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 + +#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 +#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 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 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); + 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(); 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 +#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 actionIDsToRemove) { _actionsToRemove += actionIDsToRemove; } diff --git a/libraries/entities/src/LineEntityItem.cpp b/libraries/entities/src/LineEntityItem.cpp index e7e1c90b41..094536f63a 100644 --- a/libraries/entities/src/LineEntityItem.cpp +++ b/libraries/entities/src/LineEntityItem.cpp @@ -18,6 +18,7 @@ #include "EntityTree.h" #include "EntitiesLogging.h" #include "EntityTreeElement.h" +#include "OctreeConstants.h" @@ -90,8 +91,8 @@ void LineEntityItem::setLinePoints(const QVector& points) { for (int i = 0; i < points.size(); i++) { glm::vec3 point = points.at(i); // Make sure all of our points are valid numbers. - // Must be greater than 0 because vector component is set to 0 if it is invalid data - if (point.x > 0 && point.y > 0 && point.z > 0){ + // Must be greater than 0 because vector component is set to 0 if it is invalid data. Also should never be greater than TREE_SCALE + if ( (point.x > 0 && point.x < TREE_SCALE) && (point.y > 0 && point.y < TREE_SCALE) && (point.z > 0 && point.z < TREE_SCALE) ) { sanitizedPoints << point; } else { ++invalidPoints; diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 464deb1059..de8f53c503 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -1902,8 +1902,8 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping, } else { material._material->setDiffuse(material.diffuse); } - material._material->setSpecular(material.specular); - material._material->setShininess(material.shininess); + material._material->setMetallic(glm::length(material.specular)); + material._material->setGloss(material.shininess); if (material.opacity <= 0.0f) { material._material->setOpacity(1.0f); @@ -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) -/// 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 animationFrames; - - QVector 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 4a8a2fc53d..b7fea218d8 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -134,8 +134,8 @@ void setMeshPartDefaults(FBXMeshPart& meshPart, QString materialID) { meshPart._material = model::MaterialPointer(new model::Material()); meshPart._material->setDiffuse(glm::vec3(1.0, 1.0, 1.0)); meshPart._material->setOpacity(1.0); - meshPart._material->setSpecular(glm::vec3(1.0, 1.0, 1.0)); - meshPart._material->setShininess(96.0); + meshPart._material->setMetallic(0.0); + meshPart._material->setGloss(96.0); meshPart._material->setEmissive(glm::vec3(0.0, 0.0, 0.0)); } @@ -481,8 +481,8 @@ FBXGeometry OBJReader::readOBJ(QIODevice* device, const QVariantHash& mapping, Q meshPart.specularTexture.filename = material->specularTextureFilename; // ... and some things are set in the underlying material. meshPart._material->setDiffuse(material->diffuseColor); - meshPart._material->setSpecular(material->specularColor); - meshPart._material->setShininess(material->shininess); + meshPart._material->setMetallic(glm::length(material->specularColor)); + meshPart._material->setGloss(material->shininess); meshPart._material->setOpacity(material->opacity); } // qCDebug(modelformat) << "OBJ Reader part:" << meshPartCount << "name:" << leadFace.groupName << "material:" << groupMaterialName << "diffuse:" << meshPart._material->getDiffuse() << "faces:" << faceGroup.count() << "triangle indices will start with:" << mesh.vertices.count(); @@ -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(); @@ -567,10 +566,10 @@ void fbxDebugDump(const FBXGeometry& fbxgeo) { qCDebug(modelformat) << " quadIndices.count() =" << meshPart.quadIndices.count(); qCDebug(modelformat) << " triangleIndices.count() =" << meshPart.triangleIndices.count(); qCDebug(modelformat) << " diffuseColor =" << meshPart.diffuseColor << "mat =" << meshPart._material->getDiffuse(); - qCDebug(modelformat) << " specularColor =" << meshPart.specularColor << "mat =" << meshPart._material->getSpecular(); + qCDebug(modelformat) << " specularColor =" << meshPart.specularColor << "mat =" << meshPart._material->getMetallic(); qCDebug(modelformat) << " emissiveColor =" << meshPart.emissiveColor << "mat =" << meshPart._material->getEmissive(); qCDebug(modelformat) << " emissiveParams =" << meshPart.emissiveParams; - qCDebug(modelformat) << " shininess =" << meshPart.shininess << "mat =" << meshPart._material->getShininess(); + qCDebug(modelformat) << " gloss =" << meshPart.shininess << "mat =" << meshPart._material->getGloss(); qCDebug(modelformat) << " opacity =" << meshPart.opacity << "mat =" << meshPart._material->getOpacity(); qCDebug(modelformat) << " materialID =" << meshPart.materialID; qCDebug(modelformat) << " diffuse texture =" << meshPart.diffuseTexture.filename; diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 9036f0f6db..d5c3635816 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -437,6 +437,8 @@ public: explicit operator bool() const { return bool(_texture); } bool operator !() const { return (!_texture); } + + bool isValid() const { return bool(_texture); } }; typedef std::vector TextureViews; diff --git a/libraries/model/src/model/Material.cpp b/libraries/model/src/model/Material.cpp index 55572a5122..a3448889b0 100755 --- a/libraries/model/src/model/Material.cpp +++ b/libraries/model/src/model/Material.cpp @@ -14,7 +14,7 @@ using namespace model; using namespace gpu; Material::Material() : - _flags(0), + _key(0), _schemaBuffer(), _textureMap() { @@ -26,13 +26,13 @@ Material::Material() : } Material::Material(const Material& material) : - _flags(material._flags), + _key(material._key), _schemaBuffer(material._schemaBuffer), _textureMap(material._textureMap) { } Material& Material::operator= (const Material& material) { - _flags = (material._flags); + _key = (material._key); _schemaBuffer = (material._schemaBuffer); _textureMap = (material._textureMap); @@ -43,52 +43,32 @@ Material::~Material() { } void Material::setDiffuse(const Color& diffuse) { - if (glm::any(glm::greaterThan(diffuse, Color(0.0f)))) { - _flags.set(DIFFUSE_BIT); - } else { - _flags.reset(DIFFUSE_BIT); - } + _key.setDiffuse(glm::any(glm::greaterThan(diffuse, Color(0.0f)))); _schemaBuffer.edit()._diffuse = diffuse; } -void Material::setSpecular(const Color& specular) { - if (glm::any(glm::greaterThan(specular, Color(0.0f)))) { - _flags.set(SPECULAR_BIT); - } else { - _flags.reset(SPECULAR_BIT); - } - _schemaBuffer.edit()._specular = specular; +void Material::setMetallic(float metallic) { + _key.setMetallic(metallic > 0.0f); + _schemaBuffer.edit()._metallic = glm::vec3(metallic); } void Material::setEmissive(const Color& emissive) { - if (glm::any(glm::greaterThan(emissive, Color(0.0f)))) { - _flags.set(EMISSIVE_BIT); - } else { - _flags.reset(EMISSIVE_BIT); - } + _key.setEmissive(glm::any(glm::greaterThan(emissive, Color(0.0f)))); _schemaBuffer.edit()._emissive = emissive; } -void Material::setShininess(float shininess) { - if (shininess > 0.0f) { - _flags.set(SHININESS_BIT); - } else { - _flags.reset(SHININESS_BIT); - } - _schemaBuffer.edit()._shininess = shininess; +void Material::setGloss(float gloss) { + _key.setGloss((gloss > 0.0f)); + _schemaBuffer.edit()._gloss = gloss; } void Material::setOpacity(float opacity) { - if (opacity >= 1.0f) { - _flags.reset(TRANSPARENT_BIT); - } else { - _flags.set(TRANSPARENT_BIT); - } + _key.setTransparent((opacity < 1.0f)); _schemaBuffer.edit()._opacity = opacity; } void Material::setTextureView(MapChannel channel, const gpu::TextureView& view) { - _flags.set(DIFFUSE_MAP_BIT + channel); + _key.setMapChannel(channel, (view.isValid())); _textureMap[channel] = view; } diff --git a/libraries/model/src/model/Material.h b/libraries/model/src/model/Material.h index ea0ab808e9..392fd918a1 100755 --- a/libraries/model/src/model/Material.h +++ b/libraries/model/src/model/Material.h @@ -23,6 +23,177 @@ namespace model { +// Material Key is a coarse trait description of a material used to classify the materials +class MaterialKey { +public: + enum FlagBit { + EMISSIVE_VAL_BIT = 0, + DIFFUSE_VAL_BIT, + METALLIC_VAL_BIT, + GLOSS_VAL_BIT, + TRANSPARENT_VAL_BIT, + + EMISSIVE_MAP_BIT, + DIFFUSE_MAP_BIT, + METALLIC_MAP_BIT, + GLOSS_MAP_BIT, + TRANSPARENT_MAP_BIT, + NORMAL_MAP_BIT, + + NUM_FLAGS, + }; + typedef std::bitset Flags; + + enum MapChannel { + EMISSIVE_MAP = 0, + DIFFUSE_MAP, + METALLIC_MAP, + GLOSS_MAP, + TRANSPARENT_MAP, + NORMAL_MAP, + + NUM_MAP_CHANNELS, + }; + + // The signature is the Flags + Flags _flags; + + MaterialKey() : _flags(0) {} + MaterialKey(const Flags& flags) : _flags(flags) {} + + class Builder { + Flags _flags{ 0 }; + public: + Builder() {} + + MaterialKey build() const { return MaterialKey(_flags); } + + Builder& withEmissive() { _flags.set(EMISSIVE_VAL_BIT); return (*this); } + Builder& withDiffuse() { _flags.set(DIFFUSE_VAL_BIT); return (*this); } + Builder& withMetallic() { _flags.set(METALLIC_VAL_BIT); return (*this); } + Builder& withGloss() { _flags.set(GLOSS_VAL_BIT); return (*this); } + Builder& withTransparent() { _flags.set(TRANSPARENT_VAL_BIT); return (*this); } + + Builder& withEmissiveMap() { _flags.set(EMISSIVE_MAP_BIT); return (*this); } + Builder& withDiffuseMap() { _flags.set(DIFFUSE_MAP_BIT); return (*this); } + Builder& withMetallicMap() { _flags.set(METALLIC_MAP_BIT); return (*this); } + Builder& withGlossMap() { _flags.set(GLOSS_MAP_BIT); return (*this); } + Builder& withTransparentMap() { _flags.set(TRANSPARENT_MAP_BIT); return (*this); } + + Builder& withNormalMap() { _flags.set(NORMAL_MAP_BIT); return (*this); } + + // Convenient standard keys that we will keep on using all over the place + static MaterialKey opaqueDiffuse() { return Builder().withDiffuse().build(); } + }; + + void setEmissive(bool value) { _flags.set(EMISSIVE_VAL_BIT, value); } + bool isEmissive() const { return _flags[EMISSIVE_VAL_BIT]; } + + void setEmissiveMap(bool value) { _flags.set(EMISSIVE_MAP_BIT, value); } + bool isEmissiveMap() const { return _flags[EMISSIVE_MAP_BIT]; } + + void setDiffuse(bool value) { _flags.set(DIFFUSE_VAL_BIT, value); } + bool isDiffuse() const { return _flags[DIFFUSE_VAL_BIT]; } + + void setDiffuseMap(bool value) { _flags.set(DIFFUSE_MAP_BIT, value); } + bool isDiffuseMap() const { return _flags[DIFFUSE_MAP_BIT]; } + + void setMetallic(bool value) { _flags.set(METALLIC_VAL_BIT, value); } + bool isMetallic() const { return _flags[METALLIC_VAL_BIT]; } + + void setMetallicMap(bool value) { _flags.set(METALLIC_MAP_BIT, value); } + bool isMetallicMap() const { return _flags[METALLIC_MAP_BIT]; } + + void setGloss(bool value) { _flags.set(GLOSS_VAL_BIT, value); } + bool isGloss() const { return _flags[GLOSS_VAL_BIT]; } + + void setGlossMap(bool value) { _flags.set(GLOSS_MAP_BIT, value); } + bool isGlossMap() const { return _flags[GLOSS_MAP_BIT]; } + + void setTransparent(bool value) { _flags.set(TRANSPARENT_VAL_BIT, value); } + bool isTransparent() const { return _flags[TRANSPARENT_VAL_BIT]; } + bool isOpaque() const { return !_flags[TRANSPARENT_VAL_BIT]; } + + void setTransparentMap(bool value) { _flags.set(TRANSPARENT_MAP_BIT, value); } + bool isTransparentMap() const { return _flags[TRANSPARENT_MAP_BIT]; } + + void setNormalMap(bool value) { _flags.set(NORMAL_MAP_BIT, value); } + bool isNormalMap() const { return _flags[NORMAL_MAP_BIT]; } + + void setMapChannel(MapChannel channel, bool value) { _flags.set(EMISSIVE_MAP_BIT + channel, value); } + bool isMapChannel(MapChannel channel) const { return _flags[EMISSIVE_MAP_BIT + channel]; } + +}; + + +class MaterialFilter { +public: + MaterialKey::Flags _value{ 0 }; + MaterialKey::Flags _mask{ 0 }; + + + MaterialFilter(const MaterialKey::Flags& value = MaterialKey::Flags(0), const MaterialKey::Flags& mask = MaterialKey::Flags(0)) : _value(value), _mask(mask) {} + + class Builder { + MaterialKey::Flags _value{ 0 }; + MaterialKey::Flags _mask{ 0 }; + public: + Builder() {} + + MaterialFilter build() const { return MaterialFilter(_value, _mask); } + + Builder& withoutEmissive() { _value.reset(MaterialKey::EMISSIVE_VAL_BIT); _mask.set(MaterialKey::EMISSIVE_VAL_BIT); return (*this); } + Builder& withEmissive() { _value.set(MaterialKey::EMISSIVE_VAL_BIT); _mask.set(MaterialKey::EMISSIVE_VAL_BIT); return (*this); } + + Builder& withoutEmissiveMap() { _value.reset(MaterialKey::EMISSIVE_MAP_BIT); _mask.set(MaterialKey::EMISSIVE_MAP_BIT); return (*this); } + Builder& withEmissiveMap() { _value.set(MaterialKey::EMISSIVE_MAP_BIT); _mask.set(MaterialKey::EMISSIVE_MAP_BIT); return (*this); } + + Builder& withoutDiffuse() { _value.reset(MaterialKey::DIFFUSE_VAL_BIT); _mask.set(MaterialKey::DIFFUSE_VAL_BIT); return (*this); } + Builder& withDiffuse() { _value.set(MaterialKey::DIFFUSE_VAL_BIT); _mask.set(MaterialKey::DIFFUSE_VAL_BIT); return (*this); } + + Builder& withoutDiffuseMap() { _value.reset(MaterialKey::DIFFUSE_MAP_BIT); _mask.set(MaterialKey::DIFFUSE_MAP_BIT); return (*this); } + Builder& withDiffuseMap() { _value.set(MaterialKey::DIFFUSE_MAP_BIT); _mask.set(MaterialKey::DIFFUSE_MAP_BIT); return (*this); } + + Builder& withoutMetallic() { _value.reset(MaterialKey::METALLIC_VAL_BIT); _mask.set(MaterialKey::METALLIC_VAL_BIT); return (*this); } + Builder& withMetallic() { _value.set(MaterialKey::METALLIC_VAL_BIT); _mask.set(MaterialKey::METALLIC_VAL_BIT); return (*this); } + + Builder& withoutMetallicMap() { _value.reset(MaterialKey::METALLIC_MAP_BIT); _mask.set(MaterialKey::METALLIC_MAP_BIT); return (*this); } + Builder& withMetallicMap() { _value.set(MaterialKey::METALLIC_MAP_BIT); _mask.set(MaterialKey::METALLIC_MAP_BIT); return (*this); } + + Builder& withoutGloss() { _value.reset(MaterialKey::GLOSS_VAL_BIT); _mask.set(MaterialKey::GLOSS_VAL_BIT); return (*this); } + Builder& withGloss() { _value.set(MaterialKey::GLOSS_VAL_BIT); _mask.set(MaterialKey::GLOSS_VAL_BIT); return (*this); } + + Builder& withoutGlossMap() { _value.reset(MaterialKey::GLOSS_MAP_BIT); _mask.set(MaterialKey::GLOSS_MAP_BIT); return (*this); } + Builder& withGlossMap() { _value.set(MaterialKey::GLOSS_MAP_BIT); _mask.set(MaterialKey::GLOSS_MAP_BIT); return (*this); } + + Builder& withoutTransparent() { _value.reset(MaterialKey::TRANSPARENT_VAL_BIT); _mask.set(MaterialKey::TRANSPARENT_VAL_BIT); return (*this); } + Builder& withTransparent() { _value.set(MaterialKey::TRANSPARENT_VAL_BIT); _mask.set(MaterialKey::TRANSPARENT_VAL_BIT); return (*this); } + + Builder& withoutTransparentMap() { _value.reset(MaterialKey::TRANSPARENT_MAP_BIT); _mask.set(MaterialKey::TRANSPARENT_MAP_BIT); return (*this); } + Builder& withTransparentMap() { _value.set(MaterialKey::TRANSPARENT_MAP_BIT); _mask.set(MaterialKey::TRANSPARENT_MAP_BIT); return (*this); } + + Builder& withoutNormalMap() { _value.reset(MaterialKey::NORMAL_MAP_BIT); _mask.set(MaterialKey::NORMAL_MAP_BIT); return (*this); } + Builder& withNormalMap() { _value.set(MaterialKey::NORMAL_MAP_BIT); _mask.set(MaterialKey::NORMAL_MAP_BIT); return (*this); } + + // Convenient standard keys that we will keep on using all over the place + static MaterialFilter opaqueDiffuse() { return Builder().withDiffuse().withoutTransparent().build(); } + }; + + // Item Filter operator testing if a key pass the filter + bool test(const MaterialKey& key) const { return (key._flags & _mask) == (_value & _mask); } + + class Less { + public: + bool operator() (const MaterialFilter& left, const MaterialFilter& right) const { + if (left._value.to_ulong() == right._value.to_ulong()) { + return left._mask.to_ulong() < right._mask.to_ulong(); + } else { + return left._value.to_ulong() < right._value.to_ulong(); + } + } + }; +}; + class Material { public: typedef gpu::BufferView UniformBufferView; @@ -30,52 +201,27 @@ public: typedef glm::vec3 Color; - enum MapChannel { - DIFFUSE_MAP = 0, - SPECULAR_MAP, - SHININESS_MAP, - EMISSIVE_MAP, - OPACITY_MAP, - NORMAL_MAP, - - NUM_MAPS, - }; + typedef MaterialKey::MapChannel MapChannel; typedef std::map TextureMap; - typedef std::bitset MapFlags; - - enum FlagBit { - DIFFUSE_BIT = 0, - SPECULAR_BIT, - SHININESS_BIT, - EMISSIVE_BIT, - TRANSPARENT_BIT, - - DIFFUSE_MAP_BIT, - SPECULAR_MAP_BIT, - SHININESS_MAP_BIT, - EMISSIVE_MAP_BIT, - OPACITY_MAP_BIT, - NORMAL_MAP_BIT, - - NUM_FLAGS, - }; - typedef std::bitset Flags; + typedef std::bitset MapFlags; Material(); Material(const Material& material); Material& operator= (const Material& material); virtual ~Material(); + const MaterialKey& getKey() const { return _key; } + const Color& getEmissive() const { return _schemaBuffer.get()._emissive; } const Color& getDiffuse() const { return _schemaBuffer.get()._diffuse; } - const Color& getSpecular() const { return _schemaBuffer.get()._specular; } - float getShininess() const { return _schemaBuffer.get()._shininess; } + float getMetallic() const { return _schemaBuffer.get()._metallic.x; } + float getGloss() const { return _schemaBuffer.get()._gloss; } float getOpacity() const { return _schemaBuffer.get()._opacity; } - void setDiffuse(const Color& diffuse); - void setSpecular(const Color& specular); void setEmissive(const Color& emissive); - void setShininess(float shininess); + void setDiffuse(const Color& diffuse); + void setMetallic(float metallic); + void setGloss(float gloss); void setOpacity(float opacity); // Schema to access the attribute values of the material @@ -84,8 +230,8 @@ public: Color _diffuse{0.5f}; float _opacity{1.f}; - Color _specular{0.03f}; - float _shininess{0.1f}; + Color _metallic{0.03f}; + float _gloss{0.1f}; Color _emissive{0.0f}; float _spare0{0.0f}; glm::vec4 _spareVec4{0.0f}; // for alignment beauty, Material size == Mat4x4 @@ -100,7 +246,7 @@ public: protected: - Flags _flags; + MaterialKey _key; UniformBufferView _schemaBuffer; TextureMap _textureMap; diff --git a/libraries/model/src/model/Skybox.cpp b/libraries/model/src/model/Skybox.cpp index a34a3be3fd..933c737b83 100755 --- a/libraries/model/src/model/Skybox.cpp +++ b/libraries/model/src/model/Skybox.cpp @@ -44,70 +44,73 @@ void Skybox::setCubemap(const gpu::TexturePointer& cubemap) { void Skybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const Skybox& skybox) { - if (skybox.getCubemap() && skybox.getCubemap()->isDefined()) { + if (skybox.getCubemap()) { + if (skybox.getCubemap()->isDefined()) { - static gpu::PipelinePointer thePipeline; - static gpu::BufferPointer theBuffer; - static gpu::Stream::FormatPointer theFormat; - static gpu::BufferPointer theConstants; - int SKYBOX_CONSTANTS_SLOT = 0; // need to be defined by the compilation of the shader - if (!thePipeline) { - auto skyVS = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(Skybox_vert))); - auto skyFS = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(Skybox_frag))); - auto skyShader = gpu::ShaderPointer(gpu::Shader::createProgram(skyVS, skyFS)); + static gpu::PipelinePointer thePipeline; + static gpu::BufferPointer theBuffer; + static gpu::Stream::FormatPointer theFormat; + static gpu::BufferPointer theConstants; + int SKYBOX_CONSTANTS_SLOT = 0; // need to be defined by the compilation of the shader + if (!thePipeline) { + auto skyVS = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(Skybox_vert))); + auto skyFS = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(Skybox_frag))); + auto skyShader = gpu::ShaderPointer(gpu::Shader::createProgram(skyVS, skyFS)); - gpu::Shader::BindingSet bindings; - bindings.insert(gpu::Shader::Binding(std::string("cubeMap"), 0)); - if (!gpu::Shader::makeProgram(*skyShader, bindings)) { + gpu::Shader::BindingSet bindings; + bindings.insert(gpu::Shader::Binding(std::string("cubeMap"), 0)); + if (!gpu::Shader::makeProgram(*skyShader, bindings)) { - } + } - SKYBOX_CONSTANTS_SLOT = skyShader->getBuffers().findLocation("skyboxBuffer"); - if (SKYBOX_CONSTANTS_SLOT == gpu::Shader::INVALID_LOCATION) { - SKYBOX_CONSTANTS_SLOT = skyShader->getUniforms().findLocation("skyboxBuffer"); - } + SKYBOX_CONSTANTS_SLOT = skyShader->getBuffers().findLocation("skyboxBuffer"); + if (SKYBOX_CONSTANTS_SLOT == gpu::Shader::INVALID_LOCATION) { + SKYBOX_CONSTANTS_SLOT = skyShader->getUniforms().findLocation("skyboxBuffer"); + } - auto skyState = gpu::StatePointer(new gpu::State()); + auto skyState = gpu::StatePointer(new gpu::State()); - thePipeline = gpu::PipelinePointer(gpu::Pipeline::create(skyShader, skyState)); + thePipeline = gpu::PipelinePointer(gpu::Pipeline::create(skyShader, skyState)); - const float CLIP = 1.0; - const glm::vec2 vertices[4] = { {-CLIP, -CLIP}, {CLIP, -CLIP}, {-CLIP, CLIP}, {CLIP, CLIP}}; - theBuffer.reset(new gpu::Buffer(sizeof(vertices), (const gpu::Byte*) vertices)); + const float CLIP = 1.0; + const glm::vec2 vertices[4] = { {-CLIP, -CLIP}, {CLIP, -CLIP}, {-CLIP, CLIP}, {CLIP, CLIP}}; + theBuffer.reset(new gpu::Buffer(sizeof(vertices), (const gpu::Byte*) vertices)); - theFormat.reset(new gpu::Stream::Format()); - theFormat->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::XYZ)); + theFormat.reset(new gpu::Stream::Format()); + theFormat->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::XYZ)); - auto color = glm::vec4(1.0f); - theConstants.reset(new gpu::Buffer(sizeof(color), (const gpu::Byte*) &color)); + auto color = glm::vec4(1.0f); + theConstants.reset(new gpu::Buffer(sizeof(color), (const gpu::Byte*) &color)); + } + + glm::mat4 projMat; + viewFrustum.evalProjectionMatrix(projMat); + + Transform viewTransform; + viewFrustum.evalViewTransform(viewTransform); + + if (glm::all(glm::equal(skybox.getColor(), glm::vec3(0.0f)))) { + auto color = glm::vec4(1.0f); + theConstants->setSubData(0, sizeof(color), (const gpu::Byte*) &color); + } else { + theConstants->setSubData(0, sizeof(Color), (const gpu::Byte*) &skybox.getColor()); + } + + batch.setProjectionTransform(projMat); + batch.setViewTransform(viewTransform); + batch.setModelTransform(Transform()); // only for Mac + batch.setPipeline(thePipeline); + batch.setInputBuffer(gpu::Stream::POSITION, theBuffer, 0, 8); + batch.setUniformBuffer(SKYBOX_CONSTANTS_SLOT, theConstants, 0, theConstants->getSize()); + batch.setInputFormat(theFormat); + batch.setUniformTexture(0, skybox.getCubemap()); + batch.draw(gpu::TRIANGLE_STRIP, 4); } - glm::mat4 projMat; - viewFrustum.evalProjectionMatrix(projMat); - - Transform viewTransform; - viewFrustum.evalViewTransform(viewTransform); - - if (glm::all(glm::equal(skybox.getColor(), glm::vec3(0.0f)))) { - auto color = glm::vec4(1.0f); - theConstants->setSubData(0, sizeof(color), (const gpu::Byte*) &color); - } else { - theConstants->setSubData(0, sizeof(Color), (const gpu::Byte*) &skybox.getColor()); - } - - batch.setProjectionTransform(projMat); - batch.setViewTransform(viewTransform); - batch.setModelTransform(Transform()); // only for Mac - batch.setPipeline(thePipeline); - batch.setInputBuffer(gpu::Stream::POSITION, theBuffer, 0, 8); - batch.setUniformBuffer(SKYBOX_CONSTANTS_SLOT, theConstants, 0, theConstants->getSize()); - batch.setInputFormat(theFormat); - batch.setUniformTexture(0, skybox.getCubemap()); - batch.draw(gpu::TRIANGLE_STRIP, 4); } else { // skybox has no cubemap, just clear the color buffer auto color = skybox.getColor(); - batch.clearFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, glm::vec4(color, 1.0f), 0.f, 0); + batch.clearFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, glm::vec4(color, 0.0f), 0.f, 0); } } diff --git a/libraries/model/src/model/TextureStorage.h b/libraries/model/src/model/TextureStorage.h index ebc027298b..a6752d21b2 100755 --- a/libraries/model/src/model/TextureStorage.h +++ b/libraries/model/src/model/TextureStorage.h @@ -24,7 +24,7 @@ typedef glm::vec3 Color; class TextureUsage { public: gpu::Texture::Type _type{ gpu::Texture::TEX_2D }; - Material::MapFlags _materialUsage{ Material::DIFFUSE_MAP }; + Material::MapFlags _materialUsage{ MaterialKey::DIFFUSE_MAP }; int _environmentUsage = 0; }; 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(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 - #include +#include + #include +#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(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(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(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 + +#include +#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 c578015b9f..8d234cdef5 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 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,70 +812,43 @@ 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 Payload; + MeshPartPayload(bool transparent, Model* model, int meshIndex, int partIndex) : + transparent(transparent), model(model), url(model->getURL()), meshIndex(meshIndex), partIndex(partIndex) { } + typedef render::Payload 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 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 MeshPartPayload::Pointer& payload) { + return payload->model->getPartMaterial(payload->meshIndex, payload->partIndex); + }*/ } void Model::setVisibleInScene(bool newValue, std::shared_ptr scene) { @@ -913,24 +871,19 @@ bool Model::addToScene(std::shared_ptr 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; @@ -942,11 +895,6 @@ bool Model::addToScene(std::shared_ptr scene, render::PendingChan } void Model::removeFromScene(std::shared_ptr 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); } @@ -954,210 +902,6 @@ void Model::removeFromScene(std::shared_ptr 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()->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()->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()->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()->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()->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(); @@ -1264,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))); } @@ -1338,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; @@ -1529,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 @@ -1542,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. @@ -1552,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 } @@ -1623,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 @@ -1640,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]; @@ -2002,10 +1722,6 @@ void Model::applyNextGeometry() { } void Model::deleteGeometry() { - foreach (Model* attachment, _attachments) { - delete attachment; - } - _attachments.clear(); _blendedVertexBuffers.clear(); _jointStates.clear(); _meshStates.clear(); @@ -2079,7 +1795,6 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran glm::mat4 scale = glm::scale(partBounds.getDimensions()); glm::mat4 modelToWorldMatrix = translation * scale; batch.setModelTransform(modelToWorldMatrix); - //qDebug() << "partBounds:" << partBounds; DependencyManager::get()->renderWireCube(batch, 1.0f, cubeColor); } #endif //def DEBUG_BOUNDING_PARTS @@ -2169,16 +1884,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) { @@ -2280,8 +1997,6 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran } void Model::segregateMeshGroups() { - _renderBuckets.clear(); - const FBXGeometry& geometry = _geometry->getFBXGeometry(); const QVector& networkMeshes = _geometry->getMeshes(); @@ -2291,6 +2006,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++) { @@ -2315,62 +2033,15 @@ 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(new TransparentMeshPart(this, i, partIndex)); + _transparentRenderItems << std::shared_ptr(new MeshPartPayload(true, this, i, partIndex)); } else { - _opaqueRenderItems << std::shared_ptr(new OpaqueMeshPart(this, i, partIndex)); + _opaqueRenderItems << std::shared_ptr(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* 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* 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) { @@ -2399,212 +2070,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* whichList = pickMeshList(translucent, alphaThreshold, hasLightmap, hasTangents, hasSpecular, isSkinned, isWireframe); - if (!whichList) { - return 0; - } - QVector& 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& list, gpu::Batch& batch, RenderMode mode, bool translucent, float alphaThreshold, RenderArgs* args, - Locations* locations, bool forceRenderMeshes) { - PROFILE_RANGE(__FUNCTION__); - - auto textureCache = DependencyManager::get(); - - QString lastMaterialID; - int meshPartsRendered = 0; - updateVisibleJointStates(); - const FBXGeometry& geometry = _geometry->getFBXGeometry(); - const QVector& 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(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& a, uint seed) { +inline uint qHash(const std::shared_ptr& a, uint seed) { return qHash(a.get(), seed); } -inline uint qHash(const std::shared_ptr& 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 > > _dilatedTextures; - QVector _attachments; - QSet _animationHandles; QList _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* pickMeshList(bool translucent, float alphaThreshold, bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned, bool isWireframe); - - int renderMeshesFromList(QVector& 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 _meshes; - QMap _unsortedMeshes; - }; - typedef std::unordered_map BaseRenderBucketMap; - class RenderBucketMap : public BaseRenderBucketMap { - public: - typedef RenderKey Key; - }; - RenderBucketMap _renderBuckets; - bool _renderCollisionHull; - QSet> _transparentRenderItems; - QSet> _opaqueRenderItems; + QSet> _transparentRenderItems; + QSet> _opaqueRenderItems; QMap _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) diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index f9e78c69bb..3fd7d05666 100755 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -44,12 +44,13 @@ template <> void render::jobRun(const ResolveDeferred& job, const SceneContextPo RenderDeferredTask::RenderDeferredTask() : Task() { _jobs.push_back(Job(PrepareDeferred())); _jobs.push_back(Job(DrawBackground())); - _jobs.push_back(Job(DrawOpaque())); + _jobs.push_back(Job(DrawOpaqueDeferred())); _jobs.push_back(Job(DrawLight())); _jobs.push_back(Job(ResetGLState())); _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())); } @@ -78,6 +79,84 @@ void RenderDeferredTask::run(const SceneContextPointer& sceneContext, const Rend +template <> void render::jobRun(const DrawOpaqueDeferred& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { + PerformanceTimer perfTimer("DrawOpaqueDeferred"); + assert(renderContext->args); + assert(renderContext->args->_viewFrustum); + + // render opaques + auto& scene = sceneContext->_scene; + auto& items = scene->getMasterBucket().at(ItemFilter::Builder::opaqueShape().withoutLayered()); + auto& renderDetails = renderContext->args->_details; + + ItemIDsBounds inItems; + inItems.reserve(items.size()); + for (auto id : items) { + inItems.emplace_back(ItemIDAndBounds(id)); + } + ItemIDsBounds& renderedItems = inItems; + + renderContext->_numFeedOpaqueItems = renderedItems.size(); + + ItemIDsBounds culledItems; + culledItems.reserve(inItems.size()); + if (renderContext->_cullOpaque) { + renderDetails.pointTo(RenderDetails::OPAQUE_ITEM); + cullItems(sceneContext, renderContext, renderedItems, culledItems); + renderDetails.pointTo(RenderDetails::OTHER_ITEM); + renderedItems = culledItems; + } + + renderContext->_numDrawnOpaqueItems = renderedItems.size(); + + + ItemIDsBounds sortedItems; + sortedItems.reserve(culledItems.size()); + if (renderContext->_sortOpaque) { + depthSortItems(sceneContext, renderContext, true, renderedItems, sortedItems); // Sort Front to back opaque items! + renderedItems = sortedItems; + } + + // ItemIDsBounds sortedItems; + /* ItemMaterialBucketMap stateSortedItems; + stateSortedItems.allocateStandardMaterialBuckets(); + if (true) { + for (auto& itemIDAndBound : renderedItems) { + stateSortedItems.insert(itemIDAndBound.id, scene->getItem(itemIDAndBound.id).getMaterialKey()); + } + } +*/ + + if (renderContext->_renderOpaque) { + RenderArgs* args = renderContext->args; + gpu::Batch batch; + args->_batch = &batch; + + glm::mat4 projMat; + Transform viewMat; + args->_viewFrustum->evalProjectionMatrix(projMat); + args->_viewFrustum->evalViewTransform(viewMat); + batch.setProjectionTransform(projMat); + batch.setViewTransform(viewMat); + + renderContext->args->_renderMode = RenderArgs::NORMAL_RENDER_MODE; + { + GLenum buffers[3]; + int bufferCount = 0; + buffers[bufferCount++] = GL_COLOR_ATTACHMENT0; + buffers[bufferCount++] = GL_COLOR_ATTACHMENT1; + buffers[bufferCount++] = GL_COLOR_ATTACHMENT2; + batch._glDrawBuffers(bufferCount, buffers); + } + + renderItems(sceneContext, renderContext, renderedItems, renderContext->_maxDrawnOpaqueItems); + + args->_context->render((*args->_batch)); + args->_batch = nullptr; + } +} + + template <> void render::jobRun(const DrawTransparentDeferred& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { PerformanceTimer perfTimer("DrawTransparentDeferred"); assert(renderContext->args); @@ -85,7 +164,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; diff --git a/libraries/render-utils/src/RenderDeferredTask.h b/libraries/render-utils/src/RenderDeferredTask.h index 17971dbfac..e2cac53c0d 100755 --- a/libraries/render-utils/src/RenderDeferredTask.h +++ b/libraries/render-utils/src/RenderDeferredTask.h @@ -35,6 +35,14 @@ namespace render { template <> void jobRun(const ResolveDeferred& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext); } + +class DrawOpaqueDeferred { +public: +}; +namespace render { +template <> void jobRun(const DrawOpaqueDeferred& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext); +} + class DrawTransparentDeferred { public: }; diff --git a/libraries/render/src/render/DrawTask.cpp b/libraries/render/src/render/DrawTask.cpp index 41e36de37b..53964ac1db 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); @@ -445,3 +444,60 @@ template <> void render::jobRun(const DrawBackground& job, const SceneContextPoi // Force the context sync 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); + 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) { + // Insert the itemID in every bucket where it filters true + for (auto& bucket : (*this)) { + if (bucket.first.test(key)) { + bucket.second.push_back(id); + } + } +} + +void ItemMaterialBucketMap::allocateStandardMaterialBuckets() { + (*this)[model::MaterialFilter::Builder::opaqueDiffuse()]; +} diff --git a/libraries/render/src/render/DrawTask.h b/libraries/render/src/render/DrawTask.h index 687287cd56..0052c314c0 100755 --- a/libraries/render/src/render/DrawTask.h +++ b/libraries/render/src/render/DrawTask.h @@ -62,6 +62,10 @@ void depthSortItems(const SceneContextPointer& sceneContext, const RenderContext void renderItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, int maxDrawnItems = -1); + +void materialSortItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, ItemIDsBounds& outItems); + + class DrawOpaque { public: }; @@ -83,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: }; @@ -101,6 +113,20 @@ public: }; + +// A map of ItemIDs allowing to create bucket lists of SHAPE type items which are filtered by their +// Material +class ItemMaterialBucketMap : public std::map { +public: + + ItemMaterialBucketMap() {} + + void insert(const ItemID& id, const model::MaterialKey& key); + + // standard builders allocating the main buckets + void allocateStandardMaterialBuckets(); +}; + } #endif // hifi_render_Task_h 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 054fbeb602..5ec9f0c951 100644 --- a/libraries/render/src/render/Scene.h +++ b/libraries/render/src/render/Scene.h @@ -24,6 +24,8 @@ #include #include +#include "model/Material.h" + namespace render { class Context; @@ -34,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 @@ -42,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 }; @@ -55,6 +56,7 @@ public: ItemKey(const Flags& flags) : _flags(flags) {} class Builder { + friend class ItemKey; Flags _flags{ 0 }; public: Builder() {} @@ -63,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); } @@ -71,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]; } @@ -99,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) { @@ -120,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: @@ -129,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); } @@ -151,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); } @@ -177,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; } @@ -212,33 +215,43 @@ public: public: virtual const ItemKey getKey() const = 0; virtual const Bound getBound() const = 0; + virtual int getLayer() const = 0; + virtual void render(RenderArgs* args) = 0; - virtual void update(const UpdateFunctorPointer& functor) = 0; + virtual const model::MaterialKey getMaterialKey() const = 0; ~PayloadInterface() {} protected: + friend class Item; + virtual void update(const UpdateFunctorPointer& functor) = 0; }; - - - typedef std::shared_ptr 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(); } protected: PayloadPointer _payload; @@ -273,22 +286,36 @@ inline QDebug operator<<(QDebug debug, const Item& item) { // of the Payload interface template const ItemKey payloadGetKey(const std::shared_ptr& payloadData) { return ItemKey(); } template const Item::Bound payloadGetBound(const std::shared_ptr& payloadData) { return Item::Bound(); } +template int payloadGetLayer(const std::shared_ptr& payloadData) { return 0; } template void payloadRender(const std::shared_ptr& payloadData, RenderArgs* args) { } +// Shape type interface +template const model::MaterialKey shapeGetMaterialKey(const std::shared_ptr& payloadData) { return model::MaterialKey(); } + template class Payload : public Item::PayloadInterface { public: typedef std::shared_ptr DataPointer; typedef UpdateFunctor Updater; + Payload(const DataPointer& data) : _data(data) {} + + // Payload general interface virtual const ItemKey getKey() const { return payloadGetKey(_data); } virtual const Item::Bound getBound() const { return payloadGetBound(_data); } - virtual void render(RenderArgs* args) { payloadRender(_data, args); } - - virtual void update(const UpdateFunctorPointer& functor) { static_cast(functor.get())->_func((*_data)); } + virtual int getLayer() const { return payloadGetLayer(_data); } + + + virtual void render(RenderArgs* args) { payloadRender(_data, args); } + + // Shape Type interface + virtual const model::MaterialKey getMaterialKey() const { return shapeGetMaterialKey(_data); } - Payload(const DataPointer& data) : _data(data) {} protected: DataPointer _data; + + // Update mechanics + virtual void update(const UpdateFunctorPointer& functor) { static_cast(functor.get())->_func((*_data)); } + friend class Item; }; // Let's show how to make a simple FooPayload example: @@ -344,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 { public: @@ -360,8 +388,6 @@ public: }; class Engine; -class Observer; - class PendingChanges { public: @@ -397,36 +423,6 @@ typedef std::queue 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() {} @@ -436,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; } @@ -469,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/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 #include +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