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