diff --git a/assignment-client/src/AssignmentAction.cpp b/assignment-client/src/AssignmentAction.cpp
index 6cb3c06312..8b5650ee42 100644
--- a/assignment-client/src/AssignmentAction.cpp
+++ b/assignment-client/src/AssignmentAction.cpp
@@ -13,9 +13,8 @@
#include "AssignmentAction.h"
-AssignmentAction::AssignmentAction(EntityActionType type, QUuid id, EntityItemPointer ownerEntity) :
- _id(id),
- _type(type),
+AssignmentAction::AssignmentAction(EntityActionType type, const QUuid& id, EntityItemPointer ownerEntity) :
+ EntityActionInterface(type, id),
_data(QByteArray()),
_active(false),
_ownerEntity(ownerEntity) {
@@ -28,7 +27,7 @@ void AssignmentAction::removeFromSimulation(EntitySimulation* simulation) const
simulation->removeAction(_id);
}
-QByteArray AssignmentAction::serialize() {
+QByteArray AssignmentAction::serialize() const {
return _data;
}
diff --git a/assignment-client/src/AssignmentAction.h b/assignment-client/src/AssignmentAction.h
index cd72c1f277..23720bd465 100644
--- a/assignment-client/src/AssignmentAction.h
+++ b/assignment-client/src/AssignmentAction.h
@@ -21,23 +21,19 @@
class AssignmentAction : public EntityActionInterface {
public:
- AssignmentAction(EntityActionType type, QUuid id, EntityItemPointer ownerEntity);
+ AssignmentAction(EntityActionType type, const QUuid& id, EntityItemPointer ownerEntity);
virtual ~AssignmentAction();
- const QUuid& getID() const { return _id; }
- virtual EntityActionType getType() { return _type; }
virtual void removeFromSimulation(EntitySimulation* simulation) const;
virtual EntityItemWeakPointer getOwnerEntity() const { return _ownerEntity; }
virtual void setOwnerEntity(const EntityItemPointer ownerEntity) { _ownerEntity = ownerEntity; }
virtual bool updateArguments(QVariantMap arguments);
virtual QVariantMap getArguments();
- virtual QByteArray serialize();
+ virtual QByteArray serialize() const;
virtual void deserialize(QByteArray serializedArguments);
private:
- QUuid _id;
- EntityActionType _type;
QByteArray _data;
protected:
diff --git a/assignment-client/src/AssignmentActionFactory.cpp b/assignment-client/src/AssignmentActionFactory.cpp
index ba2692c611..7c404cbd97 100644
--- a/assignment-client/src/AssignmentActionFactory.cpp
+++ b/assignment-client/src/AssignmentActionFactory.cpp
@@ -12,14 +12,13 @@
#include "AssignmentActionFactory.h"
-EntityActionPointer assignmentActionFactory(EntityActionType type, QUuid id, EntityItemPointer ownerEntity) {
+EntityActionPointer assignmentActionFactory(EntityActionType type, const QUuid& id, EntityItemPointer ownerEntity) {
return (EntityActionPointer) new AssignmentAction(type, id, ownerEntity);
}
-EntityActionPointer AssignmentActionFactory::factory(EntitySimulation* simulation,
- EntityActionType type,
- QUuid id,
+EntityActionPointer AssignmentActionFactory::factory(EntityActionType type,
+ const QUuid& id,
EntityItemPointer ownerEntity,
QVariantMap arguments) {
EntityActionPointer action = assignmentActionFactory(type, id, ownerEntity);
@@ -33,9 +32,7 @@ EntityActionPointer AssignmentActionFactory::factory(EntitySimulation* simulatio
}
-EntityActionPointer AssignmentActionFactory::factoryBA(EntitySimulation* simulation,
- EntityItemPointer ownerEntity,
- QByteArray data) {
+EntityActionPointer AssignmentActionFactory::factoryBA(EntityItemPointer ownerEntity, QByteArray data) {
QDataStream serializedActionDataStream(data);
EntityActionType type;
QUuid id;
diff --git a/assignment-client/src/AssignmentActionFactory.h b/assignment-client/src/AssignmentActionFactory.h
index f71d22c0dd..e2d58f3e6a 100644
--- a/assignment-client/src/AssignmentActionFactory.h
+++ b/assignment-client/src/AssignmentActionFactory.h
@@ -19,14 +19,11 @@ class AssignmentActionFactory : public EntityActionFactoryInterface {
public:
AssignmentActionFactory() : EntityActionFactoryInterface() { }
virtual ~AssignmentActionFactory() { }
- virtual EntityActionPointer factory(EntitySimulation* simulation,
- EntityActionType type,
- QUuid id,
+ virtual EntityActionPointer factory(EntityActionType type,
+ const QUuid& id,
EntityItemPointer ownerEntity,
QVariantMap arguments);
- virtual EntityActionPointer factoryBA(EntitySimulation* simulation,
- EntityItemPointer ownerEntity,
- QByteArray data);
+ virtual EntityActionPointer factoryBA(EntityItemPointer ownerEntity, QByteArray data);
};
#endif // hifi_AssignmentActionFactory_h
diff --git a/cmake/externals/oglplus/CMakeLists.txt b/cmake/externals/oglplus/CMakeLists.txt
index a0b91739f6..43730129a0 100644
--- a/cmake/externals/oglplus/CMakeLists.txt
+++ b/cmake/externals/oglplus/CMakeLists.txt
@@ -4,8 +4,8 @@ string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
include(ExternalProject)
ExternalProject_Add(
${EXTERNAL_NAME}
- GIT_REPOSITORY https://github.com/matus-chochlik/oglplus.git
- GIT_TAG a2681383928b1166f176512cbe0f95e96fe68d08
+ URL http://iweb.dl.sourceforge.net/project/oglplus/oglplus-0.63.x/oglplus-0.63.0.zip
+ URL_MD5 de984ab245b185b45c87415c0e052135
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
diff --git a/examples/animationPerfTest.js b/examples/animationPerfTest.js
index 6bf310db23..b832d2e61f 100644
--- a/examples/animationPerfTest.js
+++ b/examples/animationPerfTest.js
@@ -9,9 +9,7 @@
var NUM_MOONS = 20;
// 1 = 60Hz, 2 = 30Hz, 3 = 20Hz, etc
var UPDATE_FREQUENCY_DIVISOR = 2;
-
var MAX_RANGE = 75.0;
-var LIFETIME = 600;
var SCALE = 0.1;
var center = Vec3.sum(MyAvatar.position,
@@ -22,44 +20,47 @@ var PARTICLE_MIN_SIZE = 2.50;
var PARTICLE_MAX_SIZE = 2.50;
-var planet = Entities.addEntity({
- type: "Sphere",
- position: center,
- dimensions: { x: 10 * SCALE, y: 10 * SCALE, z: 10 * SCALE },
- color: { red: 0, green: 0, blue: 255 },
- ignoreCollisions: true,
- collisionsWillMove: false,
- lifetime: LIFETIME
-});
+function deleteAnimationTestEntitites() {
+ var ids = Entities.findEntities(MyAvatar.position, 50);
+ for (var i = 0; i < ids.length; i++) {
+ var id = ids[i];
+ var properties = Entities.getEntityProperties(id);
+ if (properties.name == "AnimationTest") {
+ Entities.deleteEntity(id);
+ }
+ }
+}
+
+deleteAnimationTestEntitites();
var moons = [];
// Create initial test particles that will move according to gravity from the planets
for (var i = 0; i < NUM_MOONS; i++) {
- var radius = PARTICLE_MIN_SIZE + Math.random() * PARTICLE_MAX_SIZE;
- radius *= SCALE;
- var gray = Math.random() * 155;
- var position = { x: 10 , y: i * 3, z: 0 };
- var color = { red: 100 + gray, green: 100 + gray, blue: 100 + gray };
- if (i == 0) {
- color = { red: 255, green: 0, blue: 0 };
- radius = 6 * SCALE
- }
- moons.push(Entities.addEntity({
- type: "Sphere",
- position: Vec3.sum(center, position),
- dimensions: { x: radius, y: radius, z: radius },
- color: color,
- ignoreCollisions: true,
- lifetime: LIFETIME,
- collisionsWillMove: false
- }));
+ var radius = PARTICLE_MIN_SIZE + Math.random() * PARTICLE_MAX_SIZE;
+ radius *= SCALE;
+ var gray = Math.random() * 155;
+ var position = { x: 10 , y: i * 3, z: 0 };
+ var color = { red: 100 + gray, green: 100 + gray, blue: 100 + gray };
+ if (i == 0) {
+ color = { red: 255, green: 0, blue: 0 };
+ radius = 6 * SCALE
+ }
+ moons.push(Entities.addEntity({
+ type: "Sphere",
+ name: "AnimationTest",
+ position: Vec3.sum(center, position),
+ dimensions: { x: radius, y: radius, z: radius },
+ color: color,
+ ignoreCollisions: true,
+ collisionsWillMove: false
+
+ }));
}
Script.update.connect(update);
function scriptEnding() {
- Entities.deleteEntity(planet);
for (var i = 0; i < moons.length; i++) {
Entities.deleteEntity(moons[i]);
}
@@ -70,22 +71,20 @@ var updateCount = 0;
function update(deltaTime) {
// Apply gravitational force from planets
totalTime += deltaTime;
- updateCount++;
- if (0 != updateCount % UPDATE_FREQUENCY_DIVISOR) {
- return;
- }
-
- var planetProperties = Entities.getEntityProperties(planet);
- var center = planetProperties.position;
+ updateCount++;
+ if (0 != updateCount % UPDATE_FREQUENCY_DIVISOR) {
+ return;
+ }
+
var particlePos = Entities.getEntityProperties(moons[0]).position;
var relativePos = Vec3.subtract(particlePos.position, center);
for (var t = 0; t < moons.length; t++) {
- var thetaDelta = (Math.PI * 2.0 / NUM_MOONS) * t;
- var y = Math.sin(totalTime + thetaDelta) * 10.0 * SCALE;
- var x = Math.cos(totalTime + thetaDelta) * 10.0 * SCALE;
+ var thetaDelta = (Math.PI * 2.0 / NUM_MOONS) * t;
+ var y = Math.sin(totalTime + thetaDelta) * 10.0 * SCALE;
+ var x = Math.cos(totalTime + thetaDelta) * 10.0 * SCALE;
var newBasePos = Vec3.sum({ x: 0, y: y, z: x }, center);
Entities.editEntity(moons[t], { position: newBasePos});
}
}
-Script.scriptEnding.connect(scriptEnding);
+Script.scriptEnding.connect(deleteAnimationTestEntitites);
diff --git a/examples/cubePerfTest.js b/examples/cubePerfTest.js
new file mode 100644
index 0000000000..e349b7add7
--- /dev/null
+++ b/examples/cubePerfTest.js
@@ -0,0 +1,58 @@
+//
+// Created by Bradley Austin Davis on 2015/07/01
+// 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
+//
+
+var SIDE_SIZE = 10;
+
+var center = { x: 0, y: 0, z: 0 };
+
+var DEGREES_TO_RADIANS = Math.PI / 180.0;
+var PARTICLE_MIN_SIZE = 2.50;
+var PARTICLE_MAX_SIZE = 2.50;
+var LIFETIME = 600;
+var boxes = [];
+
+var ids = Entities.findEntities({ x: 512, y: 512, z: 512 }, 50);
+for (var i = 0; i < ids.length; i++) {
+ var id = ids[i];
+ var properties = Entities.getEntityProperties(id);
+ if (properties.name == "PerfTest") {
+ Entities.deleteEntity(id);
+ }
+}
+
+
+// Create initial test particles that will move according to gravity from the planets
+for (var x = 0; x < SIDE_SIZE; x++) {
+ for (var y = 0; y < SIDE_SIZE; y++) {
+ for (var z = 0; z < SIDE_SIZE; z++) {
+ var gray = Math.random() * 155;
+ var cube = Math.random() > 0.5;
+ var color = { red: 100 + gray, green: 100 + gray, blue: 100 + gray };
+ var position = { x: 512 + x * 0.2, y: 512 + y * 0.2, z: 512 + z * 0.2};
+ var radius = Math.random() * 0.1;
+ boxes.push(Entities.addEntity({
+ type: cube ? "Box" : "Sphere",
+ name: "PerfTest",
+ position: position,
+ dimensions: { x: radius, y: radius, z: radius },
+ color: color,
+ ignoreCollisions: true,
+ collisionsWillMove: false,
+ lifetime: LIFETIME
+ }));
+ }
+ }
+}
+
+
+function scriptEnding() {
+ for (var i = 0; i < boxes.length; i++) {
+ //Entities.deleteEntity(boxes[i]);
+ }
+}
+Script.scriptEnding.connect(scriptEnding);
diff --git a/examples/defaultScripts.js b/examples/defaultScripts.js
index fb76b62dbe..922047b90c 100644
--- a/examples/defaultScripts.js
+++ b/examples/defaultScripts.js
@@ -12,7 +12,6 @@ Script.load("progress.js");
Script.load("edit.js");
Script.load("selectAudioDevice.js");
Script.load("inspect.js");
-Script.load("lobby.js");
Script.load("notifications.js");
Script.load("users.js");
Script.load("grab.js");
diff --git a/examples/example/games/airHockey.js b/examples/example/games/airHockey.js
old mode 100644
new mode 100755
index a703f379bc..ca7f007df6
--- a/examples/example/games/airHockey.js
+++ b/examples/example/games/airHockey.js
@@ -83,6 +83,30 @@ var puck_name_index = 2;
var light_name_index = 3;
var floor_name_index = 4;
+//Create Spawn and Del. Button Vars.
+
+function updateButtonPosition() {
+ Overlays.editOverlay(spawnButton, {
+ x: screenSize.x / 2 + PADDING,
+ y: screenSize.y - (BUTTON_SIZE * 2 + PADDING),
+ });
+ Overlays.editOverlay(deleteButton, {
+ x: screenSize.x / 2 - BUTTON_SIZE,
+ y: screenSize.y - (BUTTON_SIZE * 2 + PADDING),
+ });
+}
+
+function onScriptUpdate() {
+ var oldScreenSize = screenSize;
+
+ screenSize = Controller.getViewportDimensions();
+
+ if (screenSize.x !== oldScreenSize.x || screenSize.y !== oldScreenSize.y) {
+ updateButtonPosition();
+ }
+}
+
+screenSize = Controller.getViewportDimensions();
var deleteButton = Overlays.addOverlay("image", {
x: screenSize.x / 2 - BUTTON_SIZE,
@@ -112,6 +136,7 @@ var spawnButton = Overlays.addOverlay("image", {
alpha: 1
});
+Script.update.connect(onScriptUpdate);
var floor, edge1, edge2, edge3a, edge3b, edge4a, edge4b, light;
diff --git a/examples/example/games/make-dummy.js b/examples/example/games/make-dummy.js
new file mode 100644
index 0000000000..068a8b7f9a
--- /dev/null
+++ b/examples/example/games/make-dummy.js
@@ -0,0 +1,70 @@
+//
+// make-dummy.js
+// examples
+//
+// Created by Seth Alves on 2015-6-10
+// Copyright 2015 High Fidelity, Inc.
+//
+// Makes a boxing-dummy that responds to collisions.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+//
+"use strict";
+/*jslint vars: true*/
+var Overlays, Entities, Controller, Script, MyAvatar, Vec3; // Referenced globals provided by High Fidelity.
+
+var HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
+
+var rezButton = Overlays.addOverlay("image", {
+ x: 100,
+ y: 350,
+ width: 32,
+ height: 32,
+ imageURL: HIFI_PUBLIC_BUCKET + "images/close.png",
+ color: {
+ red: 255,
+ green: 255,
+ blue: 255
+ },
+ alpha: 1
+});
+
+
+function mousePressEvent(event) {
+ var clickedOverlay = Overlays.getOverlayAtPoint({
+ x: event.x,
+ y: event.y
+ });
+
+ if (clickedOverlay === rezButton) {
+ var boxId;
+
+ var position = Vec3.sum(MyAvatar.position, {x: 1.0, y: 0.4, z: 0.0});
+ boxId = Entities.addEntity({
+ type: "Box",
+ name: "dummy",
+ position: position,
+ dimensions: {x: 0.3, y: 0.7, z: 0.3},
+ gravity: {x: 0.0, y: -3.0, z: 0.0},
+ damping: 0.2,
+ collisionsWillMove: true
+ });
+
+ var pointToOffsetFrom = Vec3.sum(position, {x: 0.0, y: 2.0, z: 0.0});
+ Entities.addAction("offset", boxId, {pointToOffsetFrom: pointToOffsetFrom,
+ linearDistance: 2.0,
+ // linearTimeScale: 0.005
+ linearTimeScale: 0.1
+ });
+ }
+}
+
+
+function scriptEnding() {
+ Overlays.deleteOverlay(rezButton);
+}
+
+Controller.mousePressEvent.connect(mousePressEvent);
+Script.scriptEnding.connect(scriptEnding);
diff --git a/examples/example/games/sword.js b/examples/example/games/sword.js
new file mode 100644
index 0000000000..18d6911f0b
--- /dev/null
+++ b/examples/example/games/sword.js
@@ -0,0 +1,322 @@
+// 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
+//
+"use strict";
+/*jslint vars: true*/
+var Script, Entities, MyAvatar, Window, Overlays, Controller, Vec3, Quat, print, ToolBar, Settings; // Referenced globals provided by High Fidelity.
+Script.include("http://s3.amazonaws.com/hifi-public/scripts/libraries/toolBars.js");
+
+var hand = Settings.getValue("highfidelity.sword.hand", "right");
+var nullActionID = "00000000-0000-0000-0000-000000000000";
+var controllerID;
+var controllerActive;
+var stickID = null;
+var actionID = nullActionID;
+var targetIDs = [];
+var dimensions = { x: 0.3, y: 0.15, z: 2.0 };
+var BUTTON_SIZE = 32;
+
+var stickModel = "https://hifi-public.s3.amazonaws.com/eric/models/stick.fbx";
+var swordModel = "https://hifi-public.s3.amazonaws.com/ozan/props/sword/sword.fbx";
+var swordCollisionShape = "https://hifi-public.s3.amazonaws.com/ozan/props/sword/sword.obj";
+var swordCollisionSoundURL = "http://public.highfidelity.io/sounds/Collisions-hitsandslaps/swordStrike1.wav";
+var avatarCollisionSoundURL = "https://s3.amazonaws.com/hifi-public/sounds/Collisions-hitsandslaps/airhockey_hit1.wav";
+var whichModel = "sword";
+var originalAvatarCollisionSound;
+
+var toolBar = new ToolBar(0, 0, ToolBar.vertical, "highfidelity.sword.toolbar", function () {
+ return {x: 100, y: 380};
+});
+
+var SWORD_IMAGE = "http://s3.amazonaws.com/hifi-public/images/billiardsReticle.png"; // Toggle between brandishing/sheathing sword (creating if necessary)
+var TARGET_IMAGE = "http://s3.amazonaws.com/hifi-public/images/puck.png"; // Create a target dummy
+var CLEANUP_IMAGE = "http://s3.amazonaws.com/hifi-public/images/delete.png"; // Remove sword and all target dummies.f
+var SWITCH_HANDS_IMAGE = "http://s3.amazonaws.com/hifi-public/images/up-arrow.svg"; // Toggle left vs right hand. Persists in settings.
+var swordButton = toolBar.addOverlay("image", {
+ width: BUTTON_SIZE,
+ height: BUTTON_SIZE,
+ imageURL: SWORD_IMAGE,
+ alpha: 1
+});
+var targetButton = toolBar.addOverlay("image", {
+ width: BUTTON_SIZE,
+ height: BUTTON_SIZE,
+ imageURL: TARGET_IMAGE,
+ alpha: 1
+});
+var switchHandsButton = toolBar.addOverlay("image", {
+ width: BUTTON_SIZE,
+ height: BUTTON_SIZE,
+ imageURL: SWITCH_HANDS_IMAGE,
+ alpha: 1
+});
+var cleanupButton = toolBar.addOverlay("image", {
+ width: BUTTON_SIZE,
+ height: BUTTON_SIZE,
+ imageURL: CLEANUP_IMAGE,
+ alpha: 1
+});
+
+var flasher;
+function clearFlash() {
+ if (!flasher) {
+ return;
+ }
+ Script.clearTimeout(flasher.timer);
+ Overlays.deleteOverlay(flasher.overlay);
+ flasher = null;
+}
+function flash(color) {
+ clearFlash();
+ flasher = {};
+ flasher.overlay = Overlays.addOverlay("text", {
+ backgroundColor: color,
+ backgroundAlpha: 0.7,
+ width: Window.innerWidth,
+ height: Window.innerHeight
+ });
+ flasher.timer = Script.setTimeout(clearFlash, 500);
+}
+
+var health = 100;
+var display2d, display3d;
+function trackAvatarWithText() {
+ Entities.editEntity(display3d, {
+ position: Vec3.sum(MyAvatar.position, {x: 0, y: 1.5, z: 0}),
+ rotation: Quat.multiply(MyAvatar.orientation, Quat.fromPitchYawRollDegrees(0, 180, 0))
+ });
+}
+function updateDisplay() {
+ var text = health.toString();
+ if (!display2d) {
+ health = 100;
+ display2d = Overlays.addOverlay("text", {
+ text: text,
+ font: { size: 20 },
+ color: {red: 0, green: 255, blue: 0},
+ backgroundColor: {red: 100, green: 100, blue: 100}, // Why doesn't this and the next work?
+ backgroundAlpha: 0.9,
+ x: toolBar.x - 5, // I'd like to add the score to the toolBar and have it drag with it, but toolBar doesn't support text (just buttons).
+ y: toolBar.y - 30 // So next best thing is to position it each time as if it were on top.
+ });
+ display3d = Entities.addEntity({
+ name: MyAvatar.displayName + " score",
+ textColor: {red: 255, green: 255, blue: 255},
+ type: "Text",
+ text: text,
+ lineHeight: 0.14,
+ backgroundColor: {red: 64, green: 64, blue: 64},
+ dimensions: {x: 0.3, y: 0.2, z: 0.01},
+ });
+ Script.update.connect(trackAvatarWithText);
+ } else {
+ Overlays.editOverlay(display2d, {text: text});
+ Entities.editEntity(display3d, {text: text});
+ }
+}
+function removeDisplay() {
+ if (display2d) {
+ Overlays.deleteOverlay(display2d);
+ display2d = null;
+ Script.update.disconnect(trackAvatarWithText);
+ Entities.deleteEntity(display3d);
+ display3d = null;
+ }
+}
+function computeEnergy(collision, entityID) {
+ var id = entityID || collision.idA || collision.idB;
+ var entity = id && Entities.getEntityProperties(id);
+ var mass = entity ? (entity.density * entity.dimensions.x * entity.dimensions.y * entity.dimensions.z) : 1;
+ var linearVelocityChange = Vec3.length(collision.velocityChange);
+ var energy = 0.5 * mass * linearVelocityChange * linearVelocityChange;
+ return Math.min(Math.max(1.0, Math.round(energy)), 20);
+}
+function gotHit(collision) {
+ var energy = computeEnergy(collision);
+ print("Got hit - " + energy + " from " + collision.idA + " " + collision.idB);
+ health -= energy;
+ flash({red: 255, green: 0, blue: 0});
+ updateDisplay();
+}
+function scoreHit(idA, idB, collision) {
+ var energy = computeEnergy(collision, idA);
+ print("Score + " + energy + " from " + JSON.stringify(idA) + " " + JSON.stringify(idB));
+ health += energy;
+ flash({red: 0, green: 255, blue: 0});
+ updateDisplay();
+}
+
+function isFighting() {
+ return stickID && (actionID !== nullActionID);
+}
+
+function initControls() {
+ print("Sword hand is " + hand);
+ if (hand === "right") {
+ controllerID = 3; // right handed
+ } else {
+ controllerID = 4; // left handed
+ }
+}
+var inHand = false;
+function positionStick(stickOrientation) {
+ var reorient = Quat.fromPitchYawRollDegrees(0, -90, 0);
+ var baseOffset = {x: -dimensions.z * 0.8, y: 0, z: 0};
+ var offset = Vec3.multiplyQbyV(reorient, baseOffset);
+ stickOrientation = Quat.multiply(reorient, stickOrientation);
+ inHand = false;
+ Entities.updateAction(stickID, actionID, {
+ relativePosition: offset,
+ relativeRotation: stickOrientation
+ });
+}
+function resetToHand() { // Maybe coordinate with positionStick?
+ if (inHand) { // Optimization: bail if we're already inHand.
+ return;
+ }
+ print('Reset to hand');
+ Entities.updateAction(stickID, actionID, {
+ relativePosition: {x: 0.0, y: 0.0, z: -dimensions.z * 0.5},
+ relativeRotation: Quat.fromVec3Degrees({x: 45.0, y: 0.0, z: 0.0}),
+ hand: hand, // It should not be necessary to repeat these two, but there seems to be a bug in that that
+ timeScale: 0.05 // they do not retain their earlier values if you don't repeat them.
+ });
+ inHand = true;
+}
+function mouseMoveEvent(event) {
+ if (event.deviceID) { // Not a MOUSE mouse event, but a (e.g., hydra) mouse event, with x/y that is not meaningful for us.
+ resetToHand(); // Can only happen when controller is uncradled, so let's drive with that, resetting our attachement.
+ return;
+ }
+ controllerActive = (Vec3.length(Controller.getSpatialControlPosition(controllerID)) > 0);
+ //print("Mouse move with hand controller " + (controllerActive ? "active" : "inactive") + JSON.stringify(event));
+ if (controllerActive || !isFighting()) {
+ print('Attempting attachment reset');
+ resetToHand();
+ 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 removeSword() {
+ if (stickID) {
+ print('deleting action ' + actionID + ' and entity ' + stickID);
+ Entities.deleteAction(stickID, actionID);
+ Entities.deleteEntity(stickID);
+ stickID = null;
+ actionID = nullActionID;
+ Controller.mouseMoveEvent.disconnect(mouseMoveEvent);
+ MyAvatar.collisionWithEntity.disconnect(gotHit);
+ // removeEventhHandler happens automatically when the entity is deleted.
+ }
+ inHand = false;
+ if (originalAvatarCollisionSound !== undefined) {
+ MyAvatar.collisionSoundURL = originalAvatarCollisionSound;
+ }
+ removeDisplay();
+}
+function cleanUp(leaveButtons) {
+ removeSword();
+ targetIDs.forEach(function (id) {
+ Entities.deleteAction(id.entity, id.action);
+ Entities.deleteEntity(id.entity);
+ });
+ targetIDs = [];
+ if (!leaveButtons) {
+ toolBar.cleanup();
+ }
+}
+function makeSword() {
+ initControls();
+ stickID = Entities.addEntity({
+ type: "Model",
+ modelURL: swordModel,
+ compoundShapeURL: swordCollisionShape,
+ dimensions: dimensions,
+ position: (hand === 'right') ? MyAvatar.getRightPalmPosition() : MyAvatar.getLeftPalmPosition(), // initial position doesn't matter, as long as it's close
+ rotation: MyAvatar.orientation,
+ damping: 0.1,
+ collisionSoundURL: swordCollisionSoundURL,
+ restitution: 0.01,
+ collisionsWillMove: true
+ });
+ actionID = Entities.addAction("hold", stickID, {
+ relativePosition: {x: 0.0, y: 0.0, z: -dimensions.z * 0.5},
+ relativeRotation: Quat.fromVec3Degrees({x: 45.0, y: 0.0, z: 0.0}),
+ hand: hand,
+ timeScale: 0.05
+ });
+ if (actionID === nullActionID) {
+ print('*** FAILED TO MAKE SWORD ACTION ***');
+ cleanUp();
+ }
+ if (originalAvatarCollisionSound === undefined) {
+ originalAvatarCollisionSound = MyAvatar.collisionSoundURL; // We won't get MyAvatar.collisionWithEntity unless there's a sound URL. (Bug.)
+ SoundCache.getSound(avatarCollisionSoundURL); // Interface does not currently "preload" this? (Bug?)
+ }
+ MyAvatar.collisionSoundURL = avatarCollisionSoundURL;
+ Controller.mouseMoveEvent.connect(mouseMoveEvent);
+ MyAvatar.collisionWithEntity.connect(gotHit);
+ Script.addEventHandler(stickID, 'collisionWithEntity', scoreHit);
+ updateDisplay();
+}
+
+function onClick(event) {
+ switch (Overlays.getOverlayAtPoint(event)) {
+ case swordButton:
+ if (!stickID) {
+ makeSword();
+ } else {
+ removeSword();
+ }
+ break;
+ case targetButton:
+ var position = Vec3.sum(MyAvatar.position, {x: 1.0, y: 0.4, z: 0.0});
+ var boxId = Entities.addEntity({
+ type: "Box",
+ name: "dummy",
+ position: position,
+ dimensions: {x: 0.3, y: 0.7, z: 0.3},
+ gravity: {x: 0.0, y: -3.0, z: 0.0},
+ damping: 0.2,
+ collisionsWillMove: true
+ });
+
+ var pointToOffsetFrom = Vec3.sum(position, {x: 0.0, y: 2.0, z: 0.0});
+ var action = Entities.addAction("offset", boxId, {pointToOffsetFrom: pointToOffsetFrom,
+ linearDistance: 2.0,
+ // linearTimeScale: 0.005
+ linearTimeScale: 0.1
+ });
+ targetIDs.push({entity: boxId, action: action});
+ break;
+ case switchHandsButton:
+ cleanUp('leaveButtons');
+ hand = hand === "right" ? "left" : "right";
+ Settings.setValue("highfidelity.sword.hand", hand);
+ makeSword();
+ break;
+ case cleanupButton:
+ cleanUp('leaveButtons');
+ break;
+ }
+}
+
+Script.scriptEnding.connect(cleanUp);
+Controller.mousePressEvent.connect(onClick);
diff --git a/examples/pointer.js b/examples/pointer.js
index 83e2cbf776..2791e06466 100644
--- a/examples/pointer.js
+++ b/examples/pointer.js
@@ -65,70 +65,55 @@ function removeLine() {
function createOrUpdateLine(event) {
- var pickRay = Camera.computePickRay(event.x, event.y);
- var intersection = Entities.findRayIntersection(pickRay, true); // accurate picking
- var props = Entities.getEntityProperties(intersection.entityID);
+ var pickRay = Camera.computePickRay(event.x, event.y);
+ var intersection = Entities.findRayIntersection(pickRay, true); // accurate picking
+ var props = Entities.getEntityProperties(intersection.entityID);
- if (intersection.intersects && userCanPoint) {
- var points = [nearLinePoint(intersection.intersection), intersection.intersection]
- if (lineIsRezzed) {
- Entities.editEntity(lineEntityID, {
- position: nearLinePoint(intersection.intersection),
- linePoints: points,
- dimensions: {
- x: 1,
- y: 1,
- z: 1
- },
- lifetime: 15 + props.lifespan // renew lifetime
- });
+ if (intersection.intersects && userCanPoint) {
+ var points = [Vec3.subtract(nearLinePoint(intersection.intersection), MyAvatar.position),
+ Vec3.subtract(intersection.intersection, MyAvatar.position)];
+ if (lineIsRezzed) {
+ Entities.editEntity(lineEntityID, {
+ linePoints: points,
+ position: MyAvatar.position,
+ lifetime: 15 + props.lifespan // renew lifetime
+ });
+ // Entities.setAllPoints(lineEntityID, points);
+ } else {
+ lineIsRezzed = true;
+ lineEntityID = Entities.addEntity({
+ type: "Line",
+ position: MyAvatar.position,
+ linePoints: points,
+ dimensions: { x: 100, y: 100, z: 100 },
+ color: { red: 255, green: 255, blue: 255 },
+ lifetime: 15 // if someone crashes while pointing, don't leave the line there forever.
+ });
+ }
} else {
- lineIsRezzed = true;
- lineEntityID = Entities.addEntity({
- type: "Line",
- position: nearLinePoint(intersection.intersection),
- linePoints: points,
- dimensions: {
- x: 1,
- y: 1,
- z: 1
- },
- color: {
- red: 255,
- green: 255,
- blue: 255
- },
- lifetime: 15 // if someone crashes while pointing, don't leave the line there forever.
- });
+ removeLine();
}
- } else {
- removeLine();
- }
}
function mousePressEvent(event) {
- if (!event.isLeftButton) {
- return;
- }
-
- createOrUpdateLine(event);
- var clickedOverlay = Overlays.getOverlayAtPoint({
- x: event.x,
- y: event.y
- });
- if (clickedOverlay == pointerButton) {
- userCanPoint = !userCanPoint;
- if (userCanPoint === true) {
- Overlays.editOverlay(pointerButton, {
- color: buttonOnColor
- });
- } else {
- Overlays.editOverlay(pointerButton, {
- color: buttonOffColor
- });
+ if (!event.isLeftButton) {
+ return;
+ }
+
+ var clickedOverlay = Overlays.getOverlayAtPoint({
+ x: event.x,
+ y: event.y
+ });
+
+ if (clickedOverlay == pointerButton) {
+ userCanPoint = !userCanPoint;
+ if (userCanPoint === true) {
+ Overlays.editOverlay(pointerButton, { color: buttonOnColor });
+ } else {
+ Overlays.editOverlay(pointerButton, { color: buttonOffColor });
+ }
}
- }
}
diff --git a/examples/stick-hydra.js b/examples/stick-hydra.js
new file mode 100644
index 0000000000..a74f7954bb
--- /dev/null
+++ b/examples/stick-hydra.js
@@ -0,0 +1,73 @@
+// stick-hydra.js
+// examples
+//
+// Created by Seth Alves on 2015-7-9
+// Copyright 2015 High Fidelity, Inc.
+//
+// Allow avatar to hold a stick and control it with a hand-tracker
+//
+// 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;
+var makingNewStick = false;
+
+function makeNewStick() {
+ if (makingNewStick) {
+ return;
+ }
+ makingNewStick = true;
+ cleanUp();
+ // sometimes if this is run immediately the stick doesn't get created? use a timer.
+ Script.setTimeout(function() {
+ stickID = Entities.addEntity({
+ type: "Model",
+ name: "stick",
+ 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: 1.0},
+ position: MyAvatar.getRightPalmPosition(), // initial position doesn't matter, as long as it's close
+ rotation: MyAvatar.orientation,
+ damping: .1,
+ collisionSoundURL: "http://public.highfidelity.io/sounds/Collisions-hitsandslaps/67LCollision07.wav",
+ restitution: 0.01,
+ collisionsWillMove: true
+ });
+ actionID = Entities.addAction("hold", stickID,
+ {relativePosition: {x: 0.0, y: 0.0, z: -0.5},
+ relativeRotation: Quat.fromVec3Degrees({x: 0.0, y: 90.0, z: 0.0}),
+ hand: hand,
+ timeScale: 0.15});
+ if (actionID == nullActionID) {
+ cleanUp();
+ }
+ makingNewStick = false;
+ }, 3000);
+}
+
+
+function cleanUp() {
+ if (stickID) {
+ Entities.deleteEntity(stickID);
+ stickID = null;
+ }
+}
+
+
+function initControls(){
+ if (hand == "right") {
+ controllerID = 3; // right handed
+ } else {
+ controllerID = 4; // left handed
+ }
+}
+
+
+Script.scriptEnding.connect(cleanUp);
+makeNewStick();
diff --git a/examples/voxels.js b/examples/voxels.js
index 799af04bef..e110f15260 100644
--- a/examples/voxels.js
+++ b/examples/voxels.js
@@ -2,6 +2,32 @@ var controlHeld = false;
var shiftHeld = false;
+function attemptVoxelChange(intersection) {
+ var ids = Entities.findEntities(intersection.intersection, 10);
+ var success = false;
+ for (var i = 0; i < ids.length; i++) {
+ var id = ids[i];
+ if (controlHeld) {
+ // hold control to erase a sphere
+ if (Entities.setVoxelSphere(id, intersection.intersection, 1.0, 0)) {
+ success = true;
+ }
+ } else if (shiftHeld) {
+ // hold shift to set all voxels to 255
+ if (Entities.setAllVoxels(id, 255)) {
+ success = true;
+ }
+ } else {
+ // no modifier key means to add a sphere
+ if (Entities.setVoxelSphere(id, intersection.intersection, 1.0, 255)) {
+ success = true;
+ }
+ }
+ }
+ return success;
+}
+
+
function mousePressEvent(event) {
if (!event.isLeftButton) {
return;
@@ -9,20 +35,21 @@ function mousePressEvent(event) {
var pickRay = Camera.computePickRay(event.x, event.y);
var intersection = Entities.findRayIntersection(pickRay, true); // accurate picking
- // var props = Entities.getEntityProperties(intersection.entityID);
+
+ // we've used a picking ray to decide where to add the new sphere of voxels. If we pick nothing
+ // or if we pick a non-PolyVox entity, we fall through to the next picking attempt.
if (intersection.intersects) {
- var ids = Entities.findEntities(intersection.intersection, 10);
- for (var i = 0; i < ids.length; i++) {
- var id = ids[i];
- if (controlHeld) {
- Entities.setVoxelSphere(id, intersection.intersection, 1.0, 0);
- } else if (shiftHeld) {
- Entities.setAllVoxels(id, 255);
- } else {
- Entities.setVoxelSphere(id, intersection.intersection, 1.0, 255);
- }
+ if (attemptVoxelChange(intersection)) {
+ return;
}
}
+
+ // if the PolyVox entity is empty, we can't pick against its voxel. try picking against its
+ // bounding box, instead.
+ intersection = Entities.findRayIntersection(pickRay, false); // bounding box picking
+ if (intersection.intersects) {
+ attemptVoxelChange(intersection);
+ }
}
diff --git a/interface/resources/images/interface-logo.svg b/interface/resources/images/interface-logo.svg
new file mode 100644
index 0000000000..61fc9d9afb
--- /dev/null
+++ b/interface/resources/images/interface-logo.svg
@@ -0,0 +1,34 @@
+
+
+
diff --git a/interface/resources/qml/LoginDialog.qml b/interface/resources/qml/LoginDialog.qml
index 8d5267f7f8..29264fa608 100644
--- a/interface/resources/qml/LoginDialog.qml
+++ b/interface/resources/qml/LoginDialog.qml
@@ -41,8 +41,6 @@ DialogContainer {
readonly property int closeMargin: 16
readonly property real tan30: 0.577 // tan(30°)
readonly property int inputSpacing: 16
- property int maximumX: parent ? parent.width - width : 0
- property int maximumY: parent ? parent.height - height : 0
Rectangle {
id: backgroundRectangle
diff --git a/interface/resources/qml/UpdateDialog.qml b/interface/resources/qml/UpdateDialog.qml
index e5216ff619..8baf41cd75 100644
--- a/interface/resources/qml/UpdateDialog.qml
+++ b/interface/resources/qml/UpdateDialog.qml
@@ -1,164 +1,176 @@
import Hifi 1.0
import QtQuick 2.3
+import QtQuick.Controls 1.3
import QtQuick.Controls.Styles 1.3
import QtGraphicalEffects 1.0
import "controls"
import "styles"
DialogContainer {
- HifiConstants { id: hifi }
id: root
+ HifiConstants { id: hifi }
+
objectName: "UpdateDialog"
- implicitWidth: updateDialog.width
- implicitHeight: updateDialog.height
+
+ implicitWidth: updateDialog.implicitWidth
+ implicitHeight: updateDialog.implicitHeight
+
x: parent ? parent.width / 2 - width / 2 : 0
y: parent ? parent.height / 2 - height / 2 : 0
-
+ property int maximumX: parent ? parent.width - width : 0
+ property int maximumY: parent ? parent.height - height : 0
+
UpdateDialog {
id: updateDialog
implicitWidth: backgroundRectangle.width
implicitHeight: backgroundRectangle.height
- readonly property int inputWidth: 500
- readonly property int inputHeight: 60
+ readonly property int contentWidth: 500
+ readonly property int logoSize: 60
readonly property int borderWidth: 30
readonly property int closeMargin: 16
readonly property int inputSpacing: 16
- readonly property int buttonWidth: 150
- readonly property int buttonHeight: 50
- readonly property int buttonRadius: 15
-
+ readonly property int buttonWidth: 100
+ readonly property int buttonHeight: 30
+ readonly property int noticeHeight: 15 * inputSpacing
+ readonly property string fontFamily: Qt.platform.os === "windows" ? "Trebuchet MS" : "Trebuchet"
+
signal triggerBuildDownload
signal closeUpdateDialog
+ Rectangle {
+ id: backgroundRectangle
+ color: "#ffffff"
+
+ width: updateDialog.contentWidth + updateDialog.borderWidth * 2
+ height: mainContent.height + updateDialog.borderWidth * 2 - updateDialog.closeMargin / 2
+
+ MouseArea {
+ width: parent.width
+ height: parent.height
+ anchors {
+ horizontalCenter: parent.horizontalCenter
+ verticalCenter: parent.verticalCenter
+ }
+ drag {
+ target: root
+ minimumX: 0
+ minimumY: 0
+ maximumX: root.parent ? root.maximumX : 0
+ maximumY: root.parent ? root.maximumY : 0
+ }
+ }
+ }
+
+ Image {
+ id: logo
+ source: "../images/interface-logo.svg"
+ width: updateDialog.logoSize
+ height: updateDialog.logoSize
+ anchors {
+ top: mainContent.top
+ right: mainContent.right
+ }
+ }
+
Column {
id: mainContent
- width: updateDialog.inputWidth
+ width: updateDialog.contentWidth
spacing: updateDialog.inputSpacing
anchors {
horizontalCenter: parent.horizontalCenter
- verticalCenter: parent.verticalCenter
+ topMargin: updateDialog.borderWidth
+ top: parent.top
}
Rectangle {
- id: backgroundRectangle
- color: "#2c86b1"
- opacity: 0.85
- radius: updateDialog.closeMargin * 2
-
- width: updateDialog.inputWidth + updateDialog.borderWidth * 2
- height: updateDialog.inputHeight * 6 + updateDialog.closeMargin * 2
-
- Rectangle {
- id: dialogTitle
- width: updateDialog.inputWidth
- height: updateDialog.inputHeight
- radius: height / 2
- color: "#ebebeb"
-
+ id: header
+ width: parent.width - updateDialog.logoSize - updateDialog.inputSpacing
+ height: updateAvailable.height + versionDetails.height
+
+ Text {
+ id: updateAvailable
+ text: "Update Available"
+ font {
+ family: updateDialog.fontFamily
+ pixelSize: hifi.fonts.pixelSize * 1.5
+ weight: Font.DemiBold
+ }
+ color: "#303030"
+ }
+
+ Text {
+ id: versionDetails
+ text: updateDialog.updateAvailableDetails
+ font {
+ family: updateDialog.fontFamily
+ pixelSize: hifi.fonts.pixelSize * 0.6
+ letterSpacing: -0.5
+ }
+ color: hifi.colors.text
anchors {
- top: parent.top
- topMargin: updateDialog.inputSpacing
- horizontalCenter: parent.horizontalCenter
- }
-
- Text {
- id: updateAvailableText
- text: "Update Available"
- anchors {
- verticalCenter: parent.verticalCenter
- left: parent.left
- leftMargin: updateDialog.inputSpacing
- }
- }
-
- Text {
- text: updateDialog.updateAvailableDetails
- font.pixelSize: 14
- color: hifi.colors.text
- anchors {
- verticalCenter: parent.verticalCenter
- left: updateAvailableText.right
- leftMargin: 13
- }
+ top: updateAvailable.bottom
}
}
-
- Flickable {
+ }
+
+ Rectangle {
+ width: parent.width
+ height: updateDialog.noticeHeight
+
+ border {
+ width: 1
+ color: "#a0a0a0"
+ }
+
+ ScrollView {
id: scrollArea
- anchors {
- top: dialogTitle.bottom
- }
- contentWidth: updateDialog.inputWidth
- contentHeight: backgroundRectangle.height - (dialogTitle.height * 2.5)
- width: updateDialog.inputWidth
- height: backgroundRectangle.height - (dialogTitle.height * 2.5)
- flickableDirection: Flickable.VerticalFlick
- clip: true
-
- TextEdit {
- id: releaseNotes
- wrapMode: TextEdit.Wrap
- width: parent.width
- readOnly: true
- text: updateDialog.releaseNotes
- font.pixelSize: 14
- color: hifi.colors.text
- anchors {
- left: parent.left
- leftMargin: updateDialog.borderWidth
- }
- }
- }
-
- Rectangle {
- id: downloadButton
- width: updateDialog.buttonWidth
- height: updateDialog.buttonHeight
- radius: updateDialog.buttonRadius
- color: "green"
- anchors {
- top: scrollArea.bottom
- topMargin: 10
- right: backgroundRectangle.right
- rightMargin: 15
- }
+ width: parent.width - updateDialog.closeMargin
+ height: parent.height
+ horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff
+ verticalScrollBarPolicy: Qt.ScrollBarAsNeeded
+ anchors.right: parent.right
+
Text {
- text: "Upgrade"
- anchors {
- verticalCenter: parent.verticalCenter
- horizontalCenter: parent.horizontalCenter
+ id: releaseNotes
+ wrapMode: Text.Wrap
+ width: parent.width - updateDialog.closeMargin
+ text: updateDialog.releaseNotes
+ color: hifi.colors.text
+ font {
+ family: updateDialog.fontFamily
+ pixelSize: hifi.fonts.pixelSize * 0.65
}
}
- MouseArea {
- id: downloadButtonAction
- anchors.fill: parent
- onClicked: updateDialog.triggerUpgrade()
- cursorShape: "PointingHandCursor"
- }
}
-
+ }
+
+ Row {
+ anchors.right: parent.right
+ spacing: updateDialog.inputSpacing
+ height: updateDialog.buttonHeight + updateDialog.closeMargin / 2
+
Rectangle {
id: cancelButton
width: updateDialog.buttonWidth
height: updateDialog.buttonHeight
- radius: updateDialog.buttonRadius
- color: "red"
- anchors {
- top: scrollArea.bottom
- topMargin: 10
- right: downloadButton.left
- rightMargin: 15
- }
-
+ anchors.bottom: parent.bottom
+
Text {
text: "Cancel"
+ color: "#0c9ab4" // Same as logo
+ font {
+ family: updateDialog.fontFamily
+ pixelSize: hifi.fonts.pixelSize * 1.2
+ weight: Font.DemiBold
+ }
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
}
+
MouseArea {
id: cancelButtonAction
anchors.fill: parent
@@ -166,7 +178,35 @@ DialogContainer {
cursorShape: "PointingHandCursor"
}
}
+
+ Rectangle {
+ id: updateButton
+ width: updateDialog.buttonWidth
+ height: updateDialog.buttonHeight
+ anchors.bottom: parent.bottom
+
+ Text {
+ text: "Update"
+ color: "#0c9ab4" // Same as logo
+ font {
+ family: updateDialog.fontFamily
+ pixelSize: hifi.fonts.pixelSize * 1.2
+ weight: Font.DemiBold
+ }
+ anchors {
+ verticalCenter: parent.verticalCenter
+ horizontalCenter: parent.horizontalCenter
+ }
+ }
+
+ MouseArea {
+ id: updateButtonAction
+ anchors.fill: parent
+ onClicked: updateDialog.triggerUpgrade()
+ cursorShape: "PointingHandCursor"
+ }
+ }
}
}
}
-}
\ No newline at end of file
+}
diff --git a/interface/src/AbstractLoggerInterface.h b/interface/src/AbstractLoggerInterface.h
index fe45346e4c..3823060b13 100644
--- a/interface/src/AbstractLoggerInterface.h
+++ b/interface/src/AbstractLoggerInterface.h
@@ -24,7 +24,7 @@ public:
inline bool extraDebugging() { return _extraDebugging; }
inline void setExtraDebugging(bool debugging) { _extraDebugging = debugging; }
- virtual void addMessage(QString) = 0;
+ virtual void addMessage(const QString&) = 0;
virtual QString getLogData() = 0;
virtual void locateLog() = 0;
@@ -32,7 +32,7 @@ signals:
void logReceived(QString message);
private:
- bool _extraDebugging;
+ bool _extraDebugging{ false };
};
#endif // hifi_AbstractLoggerInterface_h
diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 982151df5a..6dad3725ab 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -413,6 +413,11 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
static_cast(dependency)->deleteLater();
});
+ // setup a timer for domain-server check ins
+ QTimer* domainCheckInTimer = new QTimer(nodeList.data());
+ connect(domainCheckInTimer, &QTimer::timeout, nodeList.data(), &NodeList::sendDomainServerCheckIn);
+ domainCheckInTimer->start(DOMAIN_SERVER_CHECK_IN_MSECS);
+
// put the NodeList and datagram processing on the node thread
nodeList->moveToThread(nodeThread);
@@ -1022,6 +1027,7 @@ void Application::paintGL() {
// Primary scene rendering
{
+ PROFILE_RANGE(__FUNCTION__ "/mainRender");
if (displayPlugin->isStereo()) {
QRect r(QPoint(0, 0), QSize(size.width() / 2, size.height()));
glEnable(GL_SCISSOR_TEST);
@@ -1083,7 +1089,7 @@ void Application::paintGL() {
#if 0
renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE;
if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) {
- renderRearViewMirror(&renderArgs, _mirrorViewRect);
+ renderRearViewMirror(&renderArgs, _mirrorViewRect);
}
renderArgs._renderMode = RenderArgs::NORMAL_RENDER_MODE;
#endif
@@ -1102,8 +1108,17 @@ void Application::paintGL() {
// FIXME? make the sync a parameter to preDisplay and let the plugin manage this
glWaitSync(sync, 0, GL_TIMEOUT_IGNORED);
glDeleteSync(sync);
- displayPlugin->display(finalTexture, finalSize);
- displayPlugin->finishFrame();
+
+ {
+ PROFILE_RANGE(__FUNCTION__ "/pluginDisplay");
+ displayPlugin->display(finalTexture, finalSize);
+ }
+
+ {
+ PROFILE_RANGE(__FUNCTION__ "/bufferSwap");
+ displayPlugin->finishFrame();
+ }
+
Q_ASSERT(!QOpenGLContext::currentContext());
_offscreenContext->makeCurrent();
_frameCount++;
@@ -1113,6 +1128,7 @@ void Application::paintGL() {
void Application::runTests() {
runTimingTests();
+ runUnitTests();
}
void Application::audioMuteToggled() {
@@ -1143,8 +1159,8 @@ void Application::resizeEvent(QResizeEvent * event) {
}
void Application::resizeGL() {
+ PROFILE_RANGE(__FUNCTION__);
auto displayPlugin = getActiveDisplayPlugin();
-
// Set the desired FBO texture size. If it hasn't changed, this does nothing.
// Otherwise, it must rebuild the FBOs
uvec2 framebufferSize = getActiveDisplayPlugin()->getRecommendedRenderSize();
@@ -1641,6 +1657,7 @@ void Application::focusOutEvent(QFocusEvent* event) {
}
void Application::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) {
+ PROFILE_RANGE(__FUNCTION__);
// Used by application overlay to determine how to draw cursor(s)
_lastMouseMoveWasSimulated = deviceID > 0;
if (!_lastMouseMoveWasSimulated) {
@@ -1897,12 +1914,10 @@ void Application::checkFPS() {
_frameCount = 0;
_datagramProcessor->resetCounters();
_timerStart.start();
-
- // ask the node list to check in with the domain server
- DependencyManager::get()->sendDomainServerCheckIn();
}
void Application::idle() {
+ PROFILE_RANGE(__FUNCTION__);
static SimpleAverage interIdleDurations;
static uint64_t lastIdleEnd{ 0 };
@@ -1944,6 +1959,7 @@ void Application::idle() {
PerformanceTimer perfTimer("update");
PerformanceWarning warn(showWarnings, "Application::idle()... update()");
const float BIGGEST_DELTA_TIME_SECS = 0.25f;
+ PROFILE_RANGE(__FUNCTION__ "/idleUpdate");
update(glm::clamp((float)timeSinceLastUpdate / 1000.0f, 0.0f, BIGGEST_DELTA_TIME_SECS));
}
{
@@ -2422,36 +2438,41 @@ void Application::updateMyAvatarLookAtPosition() {
lookAtSpot = _myCamera.getPosition();
#if 0
// FIXME is this really necessary?
+ // When I am in mirror mode, just look right at the camera (myself); don't switch gaze points because when physically
+ // looking in a mirror one's eyes appear steady.
if (isHMDMode()) {
- if (_myAvatar->isLookingAtLeftEye()) {
- lookAtSpot = OculusManager::getLeftEyePosition();
- } else {
- lookAtSpot = OculusManager::getRightEyePosition();
- }
+ lookAtSpot = _myCamera.getPosition() + OculusManager::getMidEyePosition();
}
#endif
-
} else {
AvatarSharedPointer lookingAt = _myAvatar->getLookAtTargetAvatar().lock();
if (lookingAt && _myAvatar != lookingAt.get()) {
- isLookingAtSomeone = true;
// If I am looking at someone else, look directly at one of their eyes
- if (tracker && !tracker->isMuted()) {
- // If a face tracker is active, look at the eye for the side my gaze is biased toward
- if (tracker->getEstimatedEyeYaw() > _myAvatar->getHead()->getFinalYaw()) {
- // Look at their right eye
- lookAtSpot = static_cast(lookingAt.get())->getHead()->getRightEyePosition();
- } else {
- // Look at their left eye
- lookAtSpot = static_cast(lookingAt.get())->getHead()->getLeftEyePosition();
+ isLookingAtSomeone = true;
+ Head* lookingAtHead = static_cast(lookingAt.get())->getHead();
+
+ const float MAXIMUM_FACE_ANGLE = 65.0f * RADIANS_PER_DEGREE;
+ glm::vec3 lookingAtFaceOrientation = lookingAtHead->getFinalOrientationInWorldFrame() * IDENTITY_FRONT;
+ glm::vec3 fromLookingAtToMe = glm::normalize(_myAvatar->getHead()->getEyePosition()
+ - lookingAtHead->getEyePosition());
+ float faceAngle = glm::angle(lookingAtFaceOrientation, fromLookingAtToMe);
+
+ if (faceAngle < MAXIMUM_FACE_ANGLE) {
+ // Randomly look back and forth between look targets
+ switch (_myAvatar->getEyeContactTarget()) {
+ case LEFT_EYE:
+ lookAtSpot = lookingAtHead->getLeftEyePosition();
+ break;
+ case RIGHT_EYE:
+ lookAtSpot = lookingAtHead->getRightEyePosition();
+ break;
+ case MOUTH:
+ lookAtSpot = lookingAtHead->getMouthPosition();
+ break;
}
} else {
- // Need to add randomly looking back and forth between left and right eye for case with no tracker
- if (_myAvatar->isLookingAtLeftEye()) {
- lookAtSpot = static_cast(lookingAt.get())->getHead()->getLeftEyePosition();
- } else {
- lookAtSpot = static_cast(lookingAt.get())->getHead()->getRightEyePosition();
- }
+ // Just look at their head (mid point between eyes)
+ lookAtSpot = lookingAtHead->getEyePosition();
}
} else {
// I am not looking at anyone else, so just look forward
@@ -2459,14 +2480,13 @@ void Application::updateMyAvatarLookAtPosition() {
(_myAvatar->getHead()->getFinalOrientationInWorldFrame() * glm::vec3(0.0f, 0.0f, -TREE_SCALE));
}
}
- //
- // Deflect the eyes a bit to match the detected Gaze from 3D camera if active
- //
- if (tracker && !tracker->isMuted()) {
+
+ // Deflect the eyes a bit to match the detected gaze from Faceshift if active.
+ // DDE doesn't track eyes.
+ if (tracker && typeid(*tracker) == typeid(Faceshift) && !tracker->isMuted()) {
float eyePitch = tracker->getEstimatedEyePitch();
float eyeYaw = tracker->getEstimatedEyeYaw();
const float GAZE_DEFLECTION_REDUCTION_DURING_EYE_CONTACT = 0.1f;
- // deflect using Faceshift gaze data
glm::vec3 origin = _myAvatar->getHead()->getEyePosition();
float pitchSign = (_myCamera.getMode() == CAMERA_MODE_MIRROR) ? -1.0f : 1.0f;
float deflection = DependencyManager::get()->getEyeDeflection();
@@ -3569,7 +3589,7 @@ namespace render {
const float APPROXIMATE_DISTANCE_FROM_HORIZON = 0.1f;
const float DOUBLE_APPROXIMATE_DISTANCE_FROM_HORIZON = 0.2f;
- glm::vec3 sunDirection = (args->_viewFrustum->getPosition()/*getAvatarPosition()*/ - closestData.getSunLocation())
+ glm::vec3 sunDirection = (args->_viewFrustum->getPosition()/*getAvatarPosition()*/ - closestData.getSunLocation())
/ closestData.getAtmosphereOuterRadius();
float height = glm::distance(args->_viewFrustum->getPosition()/*theCamera.getPosition()*/, closestData.getAtmosphereCenter());
if (height < closestData.getAtmosphereInnerRadius()) {
@@ -3577,8 +3597,8 @@ namespace render {
alpha = 0.0f;
if (sunDirection.y > -APPROXIMATE_DISTANCE_FROM_HORIZON) {
- float directionY = glm::clamp(sunDirection.y,
- -APPROXIMATE_DISTANCE_FROM_HORIZON, APPROXIMATE_DISTANCE_FROM_HORIZON)
+ float directionY = glm::clamp(sunDirection.y,
+ -APPROXIMATE_DISTANCE_FROM_HORIZON, APPROXIMATE_DISTANCE_FROM_HORIZON)
+ APPROXIMATE_DISTANCE_FROM_HORIZON;
alpha = (directionY / DOUBLE_APPROXIMATE_DISTANCE_FROM_HORIZON);
}
@@ -3589,8 +3609,8 @@ namespace render {
(closestData.getAtmosphereOuterRadius() - closestData.getAtmosphereInnerRadius());
if (sunDirection.y > -APPROXIMATE_DISTANCE_FROM_HORIZON) {
- float directionY = glm::clamp(sunDirection.y,
- -APPROXIMATE_DISTANCE_FROM_HORIZON, APPROXIMATE_DISTANCE_FROM_HORIZON)
+ float directionY = glm::clamp(sunDirection.y,
+ -APPROXIMATE_DISTANCE_FROM_HORIZON, APPROXIMATE_DISTANCE_FROM_HORIZON)
+ APPROXIMATE_DISTANCE_FROM_HORIZON;
alpha = (directionY / DOUBLE_APPROXIMATE_DISTANCE_FROM_HORIZON);
}
@@ -3652,14 +3672,6 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
glPushMatrix();
glLoadMatrixf(glm::value_ptr(glm::inverse(theCamera.getTransform())));
- // FIXME just flip the texture coordinates
- // flip x if in mirror mode (also requires reversing winding order for backface culling)
- if (theCamera.getMode() == CAMERA_MODE_MIRROR) {
- glScalef(-1.0f, 1.0f, 1.0f);
- glFrontFace(GL_CW);
- } else {
- glFrontFace(GL_CCW);
- }
glm::quat rotation = theCamera.getRotation();
// Equivalent to what is happening with _untranslatedViewMatrix and the _viewMatrixTranslation
@@ -3668,10 +3680,6 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
Transform viewTransform;
viewTransform.setTranslation(theCamera.getPosition());
viewTransform.setRotation(rotation);
- if (theCamera.getMode() == CAMERA_MODE_MIRROR) {
- viewTransform.setScale(Transform::Vec3(-1.0f, 1.0f, 1.0f));
- }
-
setViewTransform(viewTransform);
// Setup 3D lights (after the camera transform, so that they are positioned in world space)
@@ -3717,9 +3725,8 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
// Assuming nothing get's rendered through that
if (!selfAvatarOnly) {
-
- // render models...
if (DependencyManager::get()->shouldRenderEntities()) {
+ // render models...
PerformanceTimer perfTimer("entities");
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
"Application::displaySide() ... entities...");
@@ -3727,11 +3734,11 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
RenderArgs::DebugFlags renderDebugFlags = RenderArgs::RENDER_DEBUG_NONE;
if (Menu::getInstance()->isOptionChecked(MenuOption::PhysicsShowHulls)) {
- renderDebugFlags = (RenderArgs::DebugFlags) (renderDebugFlags | (int) RenderArgs::RENDER_DEBUG_HULLS);
+ renderDebugFlags = (RenderArgs::DebugFlags) (renderDebugFlags | (int)RenderArgs::RENDER_DEBUG_HULLS);
}
if (Menu::getInstance()->isOptionChecked(MenuOption::PhysicsShowOwned)) {
renderDebugFlags =
- (RenderArgs::DebugFlags) (renderDebugFlags | (int) RenderArgs::RENDER_DEBUG_SIMULATION_OWNERSHIP);
+ (RenderArgs::DebugFlags) (renderDebugFlags | (int)RenderArgs::RENDER_DEBUG_SIMULATION_OWNERSHIP);
}
renderArgs->_debugFlags = renderDebugFlags;
_entities.render(renderArgs);
@@ -3757,8 +3764,8 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
pendingChanges.resetItem(WorldBoxRenderData::_item, worldBoxRenderPayload);
} else {
- pendingChanges.updateItem(WorldBoxRenderData::_item,
- [](WorldBoxRenderData& payload) {
+ pendingChanges.updateItem(WorldBoxRenderData::_item,
+ [](WorldBoxRenderData& payload) {
payload._val++;
});
}
@@ -3778,7 +3785,7 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
}
{
- PerformanceTimer perfTimer("SceneProcessPendingChanges");
+ PerformanceTimer perfTimer("SceneProcessPendingChanges");
_main3DScene->enqueuePendingChanges(pendingChanges);
_main3DScene->processPendingChangesQueue();
diff --git a/interface/src/DatagramProcessor.cpp b/interface/src/DatagramProcessor.cpp
index f691527186..99308a922f 100644
--- a/interface/src/DatagramProcessor.cpp
+++ b/interface/src/DatagramProcessor.cpp
@@ -29,12 +29,13 @@ DatagramProcessor::DatagramProcessor(QObject* parent) :
}
void DatagramProcessor::processDatagrams() {
- PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
- "DatagramProcessor::processDatagrams()");
if (_isShuttingDown) {
return; // bail early... we're shutting down.
}
+ PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
+ "DatagramProcessor::processDatagrams()");
+
HifiSockAddr senderSockAddr;
diff --git a/interface/src/FileLogger.cpp b/interface/src/FileLogger.cpp
index 4808842036..00da80814b 100644
--- a/interface/src/FileLogger.cpp
+++ b/interface/src/FileLogger.cpp
@@ -21,11 +21,34 @@ const QString FILENAME_FORMAT = "hifi-log_%1_%2.txt";
const QString DATETIME_FORMAT = "yyyy-MM-dd_hh.mm.ss";
const QString LOGS_DIRECTORY = "Logs";
+class FilePersistThread : public GenericQueueThread < QString > {
+public:
+ FilePersistThread(const FileLogger& logger) : _logger(logger) {
+ setObjectName("LogFileWriter");
+ }
+
+protected:
+ virtual bool processQueueItems(const Queue& messages) {
+ QFile file(_logger._fileName);
+ if (file.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) {
+ QTextStream out(&file);
+ foreach(const QString& message, messages) {
+ out << message;
+ }
+ }
+ return true;
+ }
+private:
+ const FileLogger& _logger;
+};
+
+static FilePersistThread* _persistThreadInstance;
+
FileLogger::FileLogger(QObject* parent) :
- AbstractLoggerInterface(parent),
- _logData("")
+ AbstractLoggerInterface(parent)
{
- setExtraDebugging(false);
+ _persistThreadInstance = new FilePersistThread(*this);
+ _persistThreadInstance->initialize(true, QThread::LowestPriority);
_fileName = FileUtils::standardPath(LOGS_DIRECTORY);
QHostAddress clientAddress = getLocalAddress();
@@ -33,18 +56,24 @@ FileLogger::FileLogger(QObject* parent) :
_fileName.append(QString(FILENAME_FORMAT).arg(clientAddress.toString(), now.toString(DATETIME_FORMAT)));
}
-void FileLogger::addMessage(QString message) {
- QMutexLocker locker(&_mutex);
- emit logReceived(message);
- _logData += message;
+FileLogger::~FileLogger() {
+ _persistThreadInstance->terminate();
+}
- QFile file(_fileName);
- if (file.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) {
- QTextStream out(&file);
- out << message;
- }
+void FileLogger::addMessage(const QString& message) {
+ _persistThreadInstance->queueItem(message);
+ emit logReceived(message);
}
void FileLogger::locateLog() {
FileUtils::locateFile(_fileName);
}
+
+QString FileLogger::getLogData() {
+ QString result;
+ QFile f(_fileName);
+ if (f.open(QFile::ReadOnly | QFile::Text)) {
+ result = QTextStream(&f).readAll();
+ }
+ return result;
+}
diff --git a/interface/src/FileLogger.h b/interface/src/FileLogger.h
index 3dbbfd26cd..549654ca5c 100644
--- a/interface/src/FileLogger.h
+++ b/interface/src/FileLogger.h
@@ -13,23 +13,24 @@
#define hifi_FileLogger_h
#include "AbstractLoggerInterface.h"
-#include
+#include
class FileLogger : public AbstractLoggerInterface {
Q_OBJECT
public:
FileLogger(QObject* parent = NULL);
+ virtual ~FileLogger();
- virtual void addMessage(QString);
- virtual QString getLogData() { return _logData; }
- virtual void locateLog();
+ virtual void addMessage(const QString&) override;
+ virtual QString getLogData() override;
+ virtual void locateLog() override;
private:
- QString _logData;
QString _fileName;
- QMutex _mutex;
-
+ friend class FilePersistThread;
};
+
+
#endif // hifi_FileLogger_h
diff --git a/interface/src/InterfaceActionFactory.cpp b/interface/src/InterfaceActionFactory.cpp
index ccff5b4dc6..dca1015ecc 100644
--- a/interface/src/InterfaceActionFactory.cpp
+++ b/interface/src/InterfaceActionFactory.cpp
@@ -18,16 +18,16 @@
#include "InterfaceActionFactory.h"
-EntityActionPointer interfaceActionFactory(EntityActionType type, QUuid id, EntityItemPointer ownerEntity) {
+EntityActionPointer interfaceActionFactory(EntityActionType type, const QUuid& id, EntityItemPointer ownerEntity) {
switch (type) {
case ACTION_TYPE_NONE:
return nullptr;
case ACTION_TYPE_OFFSET:
- return (EntityActionPointer) new ObjectActionOffset(type, id, ownerEntity);
+ return (EntityActionPointer) new ObjectActionOffset(id, ownerEntity);
case ACTION_TYPE_SPRING:
- return (EntityActionPointer) new ObjectActionSpring(type, id, ownerEntity);
+ return (EntityActionPointer) new ObjectActionSpring(id, ownerEntity);
case ACTION_TYPE_HOLD:
- return (EntityActionPointer) new AvatarActionHold(type, id, ownerEntity);
+ return (EntityActionPointer) new AvatarActionHold(id, ownerEntity);
}
assert(false);
@@ -35,9 +35,8 @@ EntityActionPointer interfaceActionFactory(EntityActionType type, QUuid id, Enti
}
-EntityActionPointer InterfaceActionFactory::factory(EntitySimulation* simulation,
- EntityActionType type,
- QUuid id,
+EntityActionPointer InterfaceActionFactory::factory(EntityActionType type,
+ const QUuid& id,
EntityItemPointer ownerEntity,
QVariantMap arguments) {
EntityActionPointer action = interfaceActionFactory(type, id, ownerEntity);
@@ -51,9 +50,7 @@ EntityActionPointer InterfaceActionFactory::factory(EntitySimulation* simulation
}
-EntityActionPointer InterfaceActionFactory::factoryBA(EntitySimulation* simulation,
- EntityItemPointer ownerEntity,
- QByteArray data) {
+EntityActionPointer InterfaceActionFactory::factoryBA(EntityItemPointer ownerEntity, QByteArray data) {
QDataStream serializedArgumentStream(data);
EntityActionType type;
QUuid id;
diff --git a/interface/src/InterfaceActionFactory.h b/interface/src/InterfaceActionFactory.h
index 944e2fb753..2031f5c57a 100644
--- a/interface/src/InterfaceActionFactory.h
+++ b/interface/src/InterfaceActionFactory.h
@@ -18,13 +18,11 @@ class InterfaceActionFactory : public EntityActionFactoryInterface {
public:
InterfaceActionFactory() : EntityActionFactoryInterface() { }
virtual ~InterfaceActionFactory() { }
- virtual EntityActionPointer factory(EntitySimulation* simulation,
- EntityActionType type,
- QUuid id,
+ virtual EntityActionPointer factory(EntityActionType type,
+ const QUuid& id,
EntityItemPointer ownerEntity,
QVariantMap arguments);
- virtual EntityActionPointer factoryBA(EntitySimulation* simulation,
- EntityItemPointer ownerEntity,
+ virtual EntityActionPointer factoryBA(EntityItemPointer ownerEntity,
QByteArray data);
};
diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp
index 2df4d96725..e7f3159536 100644
--- a/interface/src/Menu.cpp
+++ b/interface/src/Menu.cpp
@@ -460,6 +460,7 @@ Menu::Menu() {
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderBoundingCollisionShapes);
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderLookAtVectors, 0, false);
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderFocusIndicator, 0, false);
+ addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ShowWhosLookingAtMe, 0, false);
MenuWrapper* handOptionsMenu = developerMenu->addMenu("Hands");
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::AlignForearmsWithWrists, 0, false);
diff --git a/interface/src/Menu.h b/interface/src/Menu.h
index 55a3538125..4156db8a38 100644
--- a/interface/src/Menu.h
+++ b/interface/src/Menu.h
@@ -270,6 +270,7 @@ namespace MenuOption {
const QString ShowBordersEntityNodes = "Show Entity Nodes";
const QString ShowIKConstraints = "Show IK Constraints";
const QString ShowRealtimeEntityStats = "Show Realtime Entity Stats";
+ const QString ShowWhosLookingAtMe = "Show Who's Looking at Me";
const QString SimpleShadows = "Simple";
const QString SixenseEnabled = "Enable Hydra Support";
const QString ShiftHipsForIdleAnimations = "Shift hips for idle animations";
diff --git a/interface/src/Util.cpp b/interface/src/Util.cpp
index 99b58edaa5..949fcd0796 100644
--- a/interface/src/Util.cpp
+++ b/interface/src/Util.cpp
@@ -21,6 +21,7 @@
#include
+#include
#include
#include
@@ -256,6 +257,43 @@ void runTimingTests() {
elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC;
qCDebug(interfaceapp, "vec3 assign and dot() usecs: %f, last result:%f",
(double)(elapsedUsecs / numTests), (double)result);
+
+
+ quint64 BYTE_CODE_MAX_TEST_VALUE = 99999999;
+ quint64 BYTE_CODE_TESTS_SKIP = 999;
+
+ QByteArray extraJunk;
+ const int EXTRA_JUNK_SIZE = 200;
+ extraJunk.append((unsigned char)255);
+ for (int i = 0; i < EXTRA_JUNK_SIZE; i++) {
+ extraJunk.append(QString("junk"));
+ }
+
+ {
+ startTime.start();
+ quint64 tests = 0;
+ quint64 failed = 0;
+ for (quint64 value = 0; value < BYTE_CODE_MAX_TEST_VALUE; value += BYTE_CODE_TESTS_SKIP) {
+ quint64 valueA = value; // usecTimestampNow();
+ ByteCountCoded codedValueA = valueA;
+ QByteArray codedValueABuffer = codedValueA;
+ codedValueABuffer.append(extraJunk);
+ ByteCountCoded decodedValueA;
+ decodedValueA.decode(codedValueABuffer);
+ quint64 valueADecoded = decodedValueA;
+ tests++;
+ if (valueA != valueADecoded) {
+ qDebug() << "FAILED! value:" << valueA << "decoded:" << valueADecoded;
+ failed++;
+ }
+
+ }
+ elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC;
+ qCDebug(interfaceapp) << "ByteCountCoded usecs: " << elapsedUsecs
+ << "per test:" << (double) (elapsedUsecs / tests)
+ << "tests:" << tests
+ << "failed:" << failed;
+ }
}
bool rayIntersectsSphere(const glm::vec3& rayStarting, const glm::vec3& rayNormalizedDirection,
@@ -298,3 +336,39 @@ bool pointInSphere(glm::vec3& point, glm::vec3& sphereCenter, double sphereRadiu
}
return false;
}
+
+void runUnitTests() {
+
+ quint64 LAST_TEST = 10;
+ quint64 SKIP_BY = 1;
+
+ for (quint64 value = 0; value <= LAST_TEST; value += SKIP_BY) {
+ qDebug() << "value:" << value;
+
+ ByteCountCoded codedValue = value;
+
+ QByteArray codedValueBuffer = codedValue;
+
+ codedValueBuffer.append((unsigned char)255);
+ codedValueBuffer.append(QString("junk"));
+
+ qDebug() << "codedValueBuffer:";
+ outputBufferBits((const unsigned char*)codedValueBuffer.constData(), codedValueBuffer.size());
+
+ ByteCountCoded valueDecoder;
+ size_t bytesConsumed = valueDecoder.decode(codedValueBuffer);
+ quint64 valueDecoded = valueDecoder;
+ qDebug() << "valueDecoded:" << valueDecoded;
+ qDebug() << "bytesConsumed:" << bytesConsumed;
+
+
+ if (value == valueDecoded) {
+ qDebug() << "SUCCESS!";
+ } else {
+ qDebug() << "FAILED!";
+ }
+
+ }
+}
+
+
diff --git a/interface/src/Util.h b/interface/src/Util.h
index ed05209747..d252c26bef 100644
--- a/interface/src/Util.h
+++ b/interface/src/Util.h
@@ -30,6 +30,7 @@ void drawText(int x, int y, float scale, float radians, int mono,
void renderCollisionOverlay(int width, int height, float magnitude, float red = 0, float blue = 0, float green = 0);
void runTimingTests();
+void runUnitTests();
bool rayIntersectsSphere(const glm::vec3& rayStarting, const glm::vec3& rayNormalizedDirection,
const glm::vec3& sphereCenter, float sphereRadius, float& distance);
diff --git a/interface/src/audio/AudioScope.cpp b/interface/src/audio/AudioScope.cpp
index 8cc27341d6..4b4e86e7f4 100644
--- a/interface/src/audio/AudioScope.cpp
+++ b/interface/src/audio/AudioScope.cpp
@@ -16,6 +16,9 @@
#include
#include
#include
+#include
+#include
+#include
#include "AudioScope.h"
@@ -104,7 +107,7 @@ void AudioScope::freeScope() {
}
}
-void AudioScope::render(int width, int height) {
+void AudioScope::render(RenderArgs* renderArgs, int width, int height) {
if (!_isEnabled) {
return;
@@ -122,24 +125,26 @@ void AudioScope::render(int width, int height) {
int y = (height - (int)SCOPE_HEIGHT) / 2;
int w = (int)SCOPE_WIDTH;
int h = (int)SCOPE_HEIGHT;
-
- renderBackground(backgroundColor, x, y, w, h);
- renderGrid(gridColor, x, y, w, h, gridRows, gridCols);
-
- renderLineStrip(_inputID, inputColor, x, y, _samplesPerScope, _scopeInputOffset, _scopeInput);
- renderLineStrip(_outputLeftID, outputLeftColor, x, y, _samplesPerScope, _scopeOutputOffset, _scopeOutputLeft);
- renderLineStrip(_outputRightD, outputRightColor, x, y, _samplesPerScope, _scopeOutputOffset, _scopeOutputRight);
+
+ gpu::Batch batch;
+ auto geometryCache = DependencyManager::get();
+ geometryCache->useSimpleDrawPipeline(batch);
+ auto textureCache = DependencyManager::get();
+ batch.setResourceTexture(0, textureCache->getWhiteTexture());
+ mat4 legacyProjection = glm::ortho(0, width, height, 0, -1000, 1000);
+ batch.setProjectionTransform(legacyProjection);
+ batch.setModelTransform(Transform());
+ batch.setViewTransform(Transform());
+ geometryCache->renderQuad(batch, x, y, w, h, backgroundColor);
+ geometryCache->renderGrid(batch, x, y, w, h, gridRows, gridCols, gridColor, _audioScopeGrid);
+ renderLineStrip(batch, _inputID, inputColor, x, y, _samplesPerScope, _scopeInputOffset, _scopeInput);
+ renderLineStrip(batch, _outputLeftID, outputLeftColor, x, y, _samplesPerScope, _scopeOutputOffset, _scopeOutputLeft);
+ renderLineStrip(batch, _outputRightD, outputRightColor, x, y, _samplesPerScope, _scopeOutputOffset, _scopeOutputRight);
+ renderArgs->_context->syncCache();
+ renderArgs->_context->render(batch);
}
-void AudioScope::renderBackground(const glm::vec4& color, int x, int y, int width, int height) {
- DependencyManager::get()->renderQuad(x, y, width, height, color);
-}
-
-void AudioScope::renderGrid(const glm::vec4& color, int x, int y, int width, int height, int rows, int cols) {
- DependencyManager::get()->renderGrid(x, y, width, height, rows, cols, color, _audioScopeGrid);
-}
-
-void AudioScope::renderLineStrip(int id, const glm::vec4& color, int x, int y, int n, int offset, const QByteArray* byteArray) {
+void AudioScope::renderLineStrip(gpu::Batch& batch, int id, const glm::vec4& color, int x, int y, int n, int offset, const QByteArray* byteArray) {
int16_t sample;
int16_t* samples = ((int16_t*) byteArray->data()) + offset;
@@ -194,7 +199,7 @@ void AudioScope::renderLineStrip(int id, const glm::vec4& color, int x, int y, i
geometryCache->updateVertices(id, points, color);
- geometryCache->renderVertices(gpu::LINE_STRIP, id);
+ geometryCache->renderVertices(batch, gpu::LINE_STRIP, id);
}
int AudioScope::addBufferToScope(QByteArray* byteArray, int frameOffset, const int16_t* source, int sourceSamplesPerChannel,
diff --git a/interface/src/audio/AudioScope.h b/interface/src/audio/AudioScope.h
index cc9367e2d5..4ff4b55c29 100644
--- a/interface/src/audio/AudioScope.h
+++ b/interface/src/audio/AudioScope.h
@@ -14,11 +14,14 @@
#include
-#include
-
#include
#include
+#include
+#include
+#include
+
+
class AudioScope : public QObject, public Dependency {
Q_OBJECT
SINGLETON_DEPENDENCY
@@ -28,7 +31,7 @@ public:
void freeScope();
void reallocateScope(int frames);
- void render(int width, int height);
+ void render(RenderArgs* renderArgs, int width, int height);
public slots:
void toggle();
@@ -48,9 +51,7 @@ private slots:
private:
// Audio scope methods for rendering
- static void renderBackground(const glm::vec4& color, int x, int y, int width, int height);
- void renderGrid(const glm::vec4& color, int x, int y, int width, int height, int rows, int cols);
- void renderLineStrip(int id, const glm::vec4& color, int x, int y, int n, int offset, const QByteArray* byteArray);
+ void renderLineStrip(gpu::Batch& batch, int id, const glm::vec4& color, int x, int y, int n, int offset, const QByteArray* byteArray);
// Audio scope methods for data acquisition
int addBufferToScope(QByteArray* byteArray, int frameOffset, const int16_t* source, int sourceSamples,
diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp
index ff2729bd43..9711d1f469 100644
--- a/interface/src/avatar/Avatar.cpp
+++ b/interface/src/avatar/Avatar.cpp
@@ -453,22 +453,36 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, boo
}
}
+ // Stack indicator spheres
+ float indicatorOffset = 0.0f;
+ if (!_displayName.isEmpty() && _displayNameAlpha != 0.0f) {
+ const float DISPLAY_NAME_INDICATOR_OFFSET = 0.22f;
+ indicatorOffset = DISPLAY_NAME_INDICATOR_OFFSET;
+ }
+ const float INDICATOR_RADIUS = 0.03f;
+ const float INDICATOR_INDICATOR_OFFSET = 3.0f * INDICATOR_RADIUS;
+
// If this is the avatar being looked at, render a little ball above their head
if (_isLookAtTarget && Menu::getInstance()->isOptionChecked(MenuOption::RenderFocusIndicator)) {
- const float LOOK_AT_INDICATOR_RADIUS = 0.03f;
- const float LOOK_AT_INDICATOR_OFFSET = 0.22f;
const glm::vec4 LOOK_AT_INDICATOR_COLOR = { 0.8f, 0.0f, 0.0f, 0.75f };
- glm::vec3 position;
- if (_displayName.isEmpty() || _displayNameAlpha == 0.0f) {
- position = glm::vec3(_position.x, getDisplayNamePosition().y, _position.z);
- } else {
- position = glm::vec3(_position.x, getDisplayNamePosition().y + LOOK_AT_INDICATOR_OFFSET, _position.z);
- }
+ glm::vec3 position = glm::vec3(_position.x, getDisplayNamePosition().y + indicatorOffset, _position.z);
Transform transform;
transform.setTranslation(position);
batch.setModelTransform(transform);
- DependencyManager::get()->renderSolidSphere(batch, LOOK_AT_INDICATOR_RADIUS
- , 15, 15, LOOK_AT_INDICATOR_COLOR);
+ DependencyManager::get()->renderSolidSphere(batch, INDICATOR_RADIUS,
+ 15, 15, LOOK_AT_INDICATOR_COLOR);
+ indicatorOffset += INDICATOR_INDICATOR_OFFSET;
+ }
+
+ // If the avatar is looking at me, render an indication that they area
+ if (getHead()->getIsLookingAtMe() && Menu::getInstance()->isOptionChecked(MenuOption::ShowWhosLookingAtMe)) {
+ const glm::vec4 LOOKING_AT_ME_COLOR = { 0.8f, 0.65f, 0.0f, 0.1f };
+ glm::vec3 position = glm::vec3(_position.x, getDisplayNamePosition().y + indicatorOffset, _position.z);
+ Transform transform;
+ transform.setTranslation(position);
+ batch.setModelTransform(transform);
+ DependencyManager::get()->renderSolidSphere(batch, INDICATOR_RADIUS,
+ 15, 15, LOOKING_AT_ME_COLOR);
}
// quick check before falling into the code below:
@@ -644,7 +658,7 @@ void Avatar::renderBillboard(RenderArgs* renderArgs) {
glm::vec2 texCoordBottomRight(1.0f, 1.0f);
gpu::Batch& batch = *renderArgs->_batch;
- batch.setUniformTexture(0, _billboardTexture->getGPUTexture());
+ batch.setResourceTexture(0, _billboardTexture->getGPUTexture());
DependencyManager::get()->bindSimpleProgram(batch, true);
DependencyManager::get()->renderQuad(batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight,
glm::vec4(1.0f, 1.0f, 1.0f, 1.0f));
@@ -750,7 +764,7 @@ void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& frustum) co
const int text_y = -nameDynamicRect.height() / 2;
// Compute background position/size
- static const float SLIGHTLY_BEHIND = -0.05f;
+ static const float SLIGHTLY_IN_FRONT = 0.1f;
const int border = 0.1f * nameDynamicRect.height();
const int left = text_x - border;
const int bottom = text_y - border;
@@ -765,16 +779,16 @@ void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& frustum) co
// Compute display name transform
auto textTransform = calculateDisplayNameTransform(frustum, renderer->getFontSize());
-
- // Render background slightly behind to avoid z-fighting
- auto backgroundTransform = textTransform;
- backgroundTransform.postTranslate(glm::vec3(0.0f, 0.0f, SLIGHTLY_BEHIND));
- batch.setModelTransform(backgroundTransform);
- DependencyManager::get()->bindSimpleProgram(batch);
+ batch.setModelTransform(textTransform);
+
+ DependencyManager::get()->bindSimpleProgram(batch, false, true, true, true);
DependencyManager::get()->renderBevelCornersRect(batch, left, bottom, width, height,
bevelDistance, backgroundColor);
// Render actual name
QByteArray nameUTF8 = renderedDisplayName.toLocal8Bit();
+
+ // Render text slightly in front to avoid z-fighting
+ textTransform.postTranslate(glm::vec3(0.0f, 0.0f, SLIGHTLY_IN_FRONT * renderer->getFontSize()));
batch.setModelTransform(textTransform);
renderer->draw(batch, text_x, -text_y, nameUTF8.data(), textColor);
}
diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp
index 918521c0da..0c5145b596 100644
--- a/interface/src/avatar/AvatarActionHold.cpp
+++ b/interface/src/avatar/AvatarActionHold.cpp
@@ -17,13 +17,14 @@
const uint16_t AvatarActionHold::holdVersion = 1;
-AvatarActionHold::AvatarActionHold(EntityActionType type, QUuid id, EntityItemPointer ownerEntity) :
- ObjectActionSpring(type, id, ownerEntity),
+AvatarActionHold::AvatarActionHold(const QUuid& id, EntityItemPointer ownerEntity) :
+ ObjectActionSpring(id, ownerEntity),
_relativePosition(glm::vec3(0.0f)),
_relativeRotation(glm::quat()),
_hand("right"),
_mine(false)
{
+ _type = ACTION_TYPE_HOLD;
#if WANT_DEBUG
qDebug() << "AvatarActionHold::AvatarActionHold";
#endif
@@ -51,39 +52,32 @@ void AvatarActionHold::updateActionWorker(float deltaTimeStep) {
}
glm::vec3 palmPosition;
+ glm::quat palmRotation;
if (_hand == "right") {
palmPosition = myAvatar->getRightPalmPosition();
+ palmRotation = myAvatar->getRightPalmRotation();
} else {
palmPosition = myAvatar->getLeftPalmPosition();
+ palmRotation = myAvatar->getLeftPalmRotation();
}
- auto rotation = myAvatar->getWorldAlignedOrientation();
+ auto rotation = palmRotation * _relativeRotation;
auto offset = rotation * _relativePosition;
auto position = palmPosition + offset;
- rotation *= _relativeRotation;
unlock();
if (!tryLockForWrite()) {
return;
}
- // check for NaNs
- if (position.x != position.x ||
- position.y != position.y ||
- position.z != position.z) {
- qDebug() << "AvatarActionHold::updateActionWorker -- target position includes NaN";
- return;
+ if (_positionalTarget != position || _rotationalTarget != rotation) {
+ auto ownerEntity = _ownerEntity.lock();
+ if (ownerEntity) {
+ ownerEntity->setActionDataDirty(true);
+ }
+ _positionalTarget = position;
+ _rotationalTarget = rotation;
}
- if (rotation.x != rotation.x ||
- rotation.y != rotation.y ||
- rotation.z != rotation.z ||
- rotation.w != rotation.w) {
- qDebug() << "AvatarActionHold::updateActionWorker -- target rotation includes NaN";
- return;
- }
-
- _positionalTarget = position;
- _rotationalTarget = rotation;
unlock();
ObjectActionSpring::updateActionWorker(deltaTimeStep);
@@ -91,59 +85,51 @@ void AvatarActionHold::updateActionWorker(float deltaTimeStep) {
bool AvatarActionHold::updateArguments(QVariantMap arguments) {
- bool rPOk = true;
+ bool ok = true;
glm::vec3 relativePosition =
- EntityActionInterface::extractVec3Argument("hold", arguments, "relativePosition", rPOk, false);
- bool rROk = true;
+ EntityActionInterface::extractVec3Argument("hold", arguments, "relativePosition", ok, false);
+ if (!ok) {
+ relativePosition = _relativePosition;
+ }
+
+ ok = true;
glm::quat relativeRotation =
- EntityActionInterface::extractQuatArgument("hold", arguments, "relativeRotation", rROk, false);
- bool tSOk = true;
+ EntityActionInterface::extractQuatArgument("hold", arguments, "relativeRotation", ok, false);
+ if (!ok) {
+ relativeRotation = _relativeRotation;
+ }
+
+ ok = true;
float timeScale =
- EntityActionInterface::extractFloatArgument("hold", arguments, "timeScale", tSOk, false);
- bool hOk = true;
+ EntityActionInterface::extractFloatArgument("hold", arguments, "timeScale", ok, false);
+ if (!ok) {
+ timeScale = _linearTimeScale;
+ }
+
+ ok = true;
QString hand =
- EntityActionInterface::extractStringArgument("hold", arguments, "hand", hOk, false);
+ EntityActionInterface::extractStringArgument("hold", arguments, "hand", ok, false);
+ if (!ok || !(hand == "left" || hand == "right")) {
+ hand = _hand;
+ }
- lockForWrite();
- if (rPOk) {
+ if (relativePosition != _relativePosition
+ || relativeRotation != _relativeRotation
+ || timeScale != _linearTimeScale
+ || hand != _hand) {
+ lockForWrite();
_relativePosition = relativePosition;
- } else {
- _relativePosition = glm::vec3(0.0f, 0.0f, 1.0f);
- }
-
- if (rROk) {
_relativeRotation = relativeRotation;
- } else {
- _relativeRotation = glm::quat(0.0f, 0.0f, 0.0f, 1.0f);
- }
+ const float MIN_TIMESCALE = 0.1f;
+ _linearTimeScale = glm::min(MIN_TIMESCALE, timeScale);
+ _angularTimeScale = _linearTimeScale;
+ _hand = hand;
- if (tSOk) {
- _linearTimeScale = timeScale;
- _angularTimeScale = timeScale;
- } else {
- _linearTimeScale = 0.2f;
- _angularTimeScale = 0.2f;
+ _mine = true;
+ _active = true;
+ activateBody();
+ unlock();
}
-
- 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 {
- _hand = "right";
- }
-
- _mine = true;
- _positionalTargetSet = true;
- _rotationalTargetSet = true;
- _active = true;
- unlock();
return true;
}
@@ -166,8 +152,7 @@ QVariantMap AvatarActionHold::getArguments() {
void AvatarActionHold::deserialize(QByteArray serializedArguments) {
- if (_mine) {
- return;
+ if (!_mine) {
+ ObjectActionSpring::deserialize(serializedArguments);
}
- ObjectActionSpring::deserialize(serializedArguments);
}
diff --git a/interface/src/avatar/AvatarActionHold.h b/interface/src/avatar/AvatarActionHold.h
index a47f1ce05d..3500b5dfa1 100644
--- a/interface/src/avatar/AvatarActionHold.h
+++ b/interface/src/avatar/AvatarActionHold.h
@@ -19,11 +19,9 @@
class AvatarActionHold : public ObjectActionSpring {
public:
- AvatarActionHold(EntityActionType type, QUuid id, EntityItemPointer ownerEntity);
+ AvatarActionHold(const QUuid& id, EntityItemPointer ownerEntity);
virtual ~AvatarActionHold();
- virtual EntityActionType getType() { return ACTION_TYPE_HOLD; }
-
virtual bool updateArguments(QVariantMap arguments);
virtual QVariantMap getArguments();
diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp
index dbd46cbfbd..944f16fd34 100644
--- a/interface/src/avatar/AvatarManager.cpp
+++ b/interface/src/avatar/AvatarManager.cpp
@@ -256,7 +256,6 @@ void AvatarManager::handleOutgoingChanges(VectorOfMotionStates& motionStates) {
}
void AvatarManager::handleCollisionEvents(CollisionEvents& collisionEvents) {
- // TODO: expose avatar collision events to JS
for (Collision collision : collisionEvents) {
// TODO: Current physics uses null idA or idB for non-entities. The plan is to handle MOTIONSTATE_TYPE_AVATAR,
// and then MOTIONSTATE_TYPE_MYAVATAR. As it is, this code only covers the case of my avatar (in which case one
@@ -285,6 +284,7 @@ void AvatarManager::handleCollisionEvents(CollisionEvents& collisionEvents) {
const float AVATAR_STRETCH_FACTOR = 1.0f;
AudioInjector::playSound(collisionSoundURL, energyFactorOfFull, AVATAR_STRETCH_FACTOR, myAvatar->getPosition());
+ myAvatar->collisionWithEntity(collision);
}
}
}
diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp
index c33c888e37..607a7bd0d8 100644
--- a/interface/src/avatar/Head.cpp
+++ b/interface/src/avatar/Head.cpp
@@ -302,7 +302,7 @@ void Head::relaxLean(float deltaTime) {
void Head::render(RenderArgs* renderArgs, float alpha, ViewFrustum* renderFrustum, bool postLighting) {
if (_renderLookatVectors) {
- renderLookatVectors(renderArgs, _leftEyePosition, _rightEyePosition, getCorrectedLookAtPosition());
+ renderLookatVectors(renderArgs, _leftEyePosition, _rightEyePosition, getCorrectedLookAtPosition());
}
}
@@ -330,7 +330,7 @@ glm::vec3 Head::getCorrectedLookAtPosition() {
}
void Head::setCorrectedLookAtPosition(glm::vec3 correctedLookAtPosition) {
- _isLookingAtMe = true;
+ _isLookingAtMe = true;
_correctedLookAtPosition = correctedLookAtPosition;
}
diff --git a/interface/src/avatar/Head.h b/interface/src/avatar/Head.h
index a208574c26..3f839d53bc 100644
--- a/interface/src/avatar/Head.h
+++ b/interface/src/avatar/Head.h
@@ -22,11 +22,6 @@
#include "InterfaceConfig.h"
#include "world.h"
-enum eyeContactTargets {
- LEFT_EYE,
- RIGHT_EYE,
- MOUTH
-};
const float EYE_EAR_GAP = 0.08f;
@@ -77,6 +72,7 @@ public:
const glm::vec3& getLeftEyePosition() const { return _leftEyePosition; }
glm::vec3 getRightEarPosition() const { return _rightEyePosition + (getRightDirection() * EYE_EAR_GAP) + (getFrontDirection() * -EYE_EAR_GAP); }
glm::vec3 getLeftEarPosition() const { return _leftEyePosition + (getRightDirection() * -EYE_EAR_GAP) + (getFrontDirection() * -EYE_EAR_GAP); }
+ glm::vec3 getMouthPosition() const { return _eyePosition - getUpDirection() * glm::length(_rightEyePosition - _leftEyePosition); }
FaceModel& getFaceModel() { return _faceModel; }
const FaceModel& getFaceModel() const { return _faceModel; }
@@ -148,7 +144,7 @@ private:
FaceModel _faceModel;
glm::vec3 _correctedLookAtPosition;
-
+
int _leftEyeLookAtID;
int _rightEyeLookAtID;
diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp
index 5f7af50e16..43c0271010 100644
--- a/interface/src/avatar/MyAvatar.cpp
+++ b/interface/src/avatar/MyAvatar.cpp
@@ -34,6 +34,8 @@
#include
#include
+#include "devices/Faceshift.h"
+
#include "Application.h"
#include "AvatarManager.h"
#include "Environment.h"
@@ -42,7 +44,6 @@
#include "MyAvatar.h"
#include "Physics.h"
#include "Recorder.h"
-#include "devices/Faceshift.h"
#include "Util.h"
#include "InterfaceLogging.h"
@@ -99,7 +100,7 @@ MyAvatar::MyAvatar() :
_shouldRender(true),
_billboardValid(false),
_feetTouchFloor(true),
- _isLookingAtLeftEye(true),
+ _eyeContactTarget(LEFT_EYE),
_realWorldFieldOfView("realWorldFieldOfView",
DEFAULT_REAL_WORLD_FIELD_OF_VIEW_DEGREES),
_firstPersonSkeletonModel(this),
@@ -393,6 +394,12 @@ glm::vec3 MyAvatar::getLeftPalmPosition() {
return leftHandPosition;
}
+glm::quat MyAvatar::getLeftPalmRotation() {
+ glm::quat leftRotation;
+ getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getLeftHandJointIndex(), leftRotation);
+ return leftRotation;
+}
+
glm::vec3 MyAvatar::getRightPalmPosition() {
glm::vec3 rightHandPosition;
getSkeletonModel().getRightHandPosition(rightHandPosition);
@@ -402,6 +409,12 @@ glm::vec3 MyAvatar::getRightPalmPosition() {
return rightHandPosition;
}
+glm::quat MyAvatar::getRightPalmRotation() {
+ glm::quat rightRotation;
+ getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getRightHandJointIndex(), rightRotation);
+ return rightRotation;
+}
+
void MyAvatar::clearReferential() {
changeReferential(NULL);
}
@@ -908,7 +921,6 @@ void MyAvatar::updateLookAtTargetAvatar() {
const float KEEP_LOOKING_AT_CURRENT_ANGLE_FACTOR = 1.3f;
const float GREATEST_LOOKING_AT_DISTANCE = 10.0f;
- int howManyLookingAtMe = 0;
foreach (const AvatarSharedPointer& avatarPointer, DependencyManager::get()->getAvatarHash()) {
Avatar* avatar = static_cast(avatarPointer.get());
bool isCurrentTarget = avatar->getIsLookAtTarget();
@@ -921,17 +933,20 @@ void MyAvatar::updateLookAtTargetAvatar() {
_targetAvatarPosition = avatarPointer->getPosition();
smallestAngleTo = angleTo;
}
- // Check if this avatar is looking at me, and fix their gaze on my camera if so
if (Application::getInstance()->isLookingAtMyAvatar(avatar)) {
- howManyLookingAtMe++;
- // Have that avatar look directly at my camera
- // Philip TODO: correct to look at left/right eye
- if (qApp->isHMDMode()) {
- avatar->getHead()->setCorrectedLookAtPosition(Application::getInstance()->getViewFrustum()->getPosition());
- // FIXME what is the point of this?
- // avatar->getHead()->setCorrectedLookAtPosition(OculusManager::getLeftEyePosition());
+ // Alter their gaze to look directly at my camera; this looks more natural than looking at my avatar's face.
+ // Offset their gaze according to whether they're looking at one of my eyes or my mouth.
+ glm::vec3 gazeOffset = avatar->getHead()->getLookAtPosition() - getHead()->getEyePosition();
+ const float HUMAN_EYE_SEPARATION = 0.065f;
+ float myEyeSeparation = glm::length(getHead()->getLeftEyePosition() - getHead()->getRightEyePosition());
+ gazeOffset = gazeOffset * HUMAN_EYE_SEPARATION / myEyeSeparation;
+
+ if (Application::getInstance()->isHMDMode()) {
+ avatar->getHead()->setCorrectedLookAtPosition(Application::getInstance()->getViewFrustum()->getPosition()
+ + glm::vec3(qApp->getHMDSensorPose()[3]) + gazeOffset);
} else {
- avatar->getHead()->setCorrectedLookAtPosition(Application::getInstance()->getViewFrustum()->getPosition());
+ avatar->getHead()->setCorrectedLookAtPosition(Application::getInstance()->getViewFrustum()->getPosition()
+ + gazeOffset);
}
} else {
avatar->getHead()->clearCorrectedLookAtPosition();
@@ -948,12 +963,24 @@ void MyAvatar::clearLookAtTargetAvatar() {
_lookAtTargetAvatar.reset();
}
-bool MyAvatar::isLookingAtLeftEye() {
- float const CHANCE_OF_CHANGING_EYE = 0.01f;
- if (randFloat() < CHANCE_OF_CHANGING_EYE) {
- _isLookingAtLeftEye = !_isLookingAtLeftEye;
+eyeContactTarget MyAvatar::getEyeContactTarget() {
+ float const CHANCE_OF_CHANGING_TARGET = 0.01f;
+ if (randFloat() < CHANCE_OF_CHANGING_TARGET) {
+ float const FIFTY_FIFTY_CHANCE = 0.5f;
+ switch (_eyeContactTarget) {
+ case LEFT_EYE:
+ _eyeContactTarget = (randFloat() < FIFTY_FIFTY_CHANCE) ? MOUTH : RIGHT_EYE;
+ break;
+ case RIGHT_EYE:
+ _eyeContactTarget = (randFloat() < FIFTY_FIFTY_CHANCE) ? LEFT_EYE : MOUTH;
+ break;
+ case MOUTH:
+ _eyeContactTarget = (randFloat() < FIFTY_FIFTY_CHANCE) ? RIGHT_EYE : LEFT_EYE;
+ break;
+ }
}
- return _isLookingAtLeftEye;
+
+ return _eyeContactTarget;
}
glm::vec3 MyAvatar::getDefaultEyePosition() const {
diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h
index c65a4bce38..7c0019adf4 100644
--- a/interface/src/avatar/MyAvatar.h
+++ b/interface/src/avatar/MyAvatar.h
@@ -19,6 +19,12 @@
class ModelItemID;
+enum eyeContactTarget {
+ LEFT_EYE,
+ RIGHT_EYE,
+ MOUTH
+};
+
class MyAvatar : public Avatar {
Q_OBJECT
Q_PROPERTY(bool shouldRenderLocally READ getShouldRenderLocally WRITE setShouldRenderLocally)
@@ -106,6 +112,7 @@ public:
bool isMyAvatar() const { return true; }
bool isLookingAtLeftEye();
+ eyeContactTarget getEyeContactTarget();
virtual int parseDataAtOffset(const QByteArray& packet, int offset);
@@ -202,7 +209,9 @@ public slots:
void updateMotionBehavior();
glm::vec3 getLeftPalmPosition();
+ glm::quat getLeftPalmRotation();
glm::vec3 getRightPalmPosition();
+ glm::quat getRightPalmRotation();
void clearReferential();
bool setModelReferential(const QUuid& id);
@@ -220,6 +229,7 @@ public slots:
signals:
void transformChanged();
void newCollisionSoundURL(const QUrl& url);
+ void collisionWithEntity(const Collision& collision);
private:
@@ -273,7 +283,7 @@ private:
QList _animationHandles;
bool _feetTouchFloor;
- bool _isLookingAtLeftEye;
+ eyeContactTarget _eyeContactTarget;
RecorderPointer _recorder;
diff --git a/interface/src/ui/ApplicationCompositor.cpp b/interface/src/ui/ApplicationCompositor.cpp
index 1f9a3ae266..5283aaf3ee 100644
--- a/interface/src/ui/ApplicationCompositor.cpp
+++ b/interface/src/ui/ApplicationCompositor.cpp
@@ -179,11 +179,12 @@ void ApplicationCompositor::bindCursorTexture(gpu::Batch& batch, uint8_t cursorI
_cursors[iconId] = DependencyManager::get()->
getImageTexture(iconPath);
}
- batch.setUniformTexture(0, _cursors[iconId]);
+ batch.setResourceTexture(0, _cursors[iconId]);
}
// Draws the FBO texture for the screen
void ApplicationCompositor::displayOverlayTexture(RenderArgs* renderArgs) {
+ PROFILE_RANGE(__FUNCTION__);
if (_alpha == 0.0f) {
return;
}
@@ -253,6 +254,7 @@ vec2 getPolarCoordinates(const PalmData& palm) {
// Draws the FBO texture for Oculus rift.
void ApplicationCompositor::displayOverlayTextureHmd(RenderArgs* renderArgs, int eye) {
+ PROFILE_RANGE(__FUNCTION__);
if (_alpha == 0.0f) {
return;
}
diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp
index e7d220893f..9f742093ee 100644
--- a/interface/src/ui/ApplicationOverlay.cpp
+++ b/interface/src/ui/ApplicationOverlay.cpp
@@ -74,6 +74,7 @@ ApplicationOverlay::~ApplicationOverlay() {
// Renders the overlays either to a texture or to the screen
void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) {
+ PROFILE_RANGE(__FUNCTION__);
CHECK_GL_ERROR();
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "ApplicationOverlay::displayOverlay()");
@@ -98,6 +99,7 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) {
}
void ApplicationOverlay::renderQmlUi(RenderArgs* renderArgs) {
+ PROFILE_RANGE(__FUNCTION__);
if (_uiTexture) {
gpu::Batch batch;
auto geometryCache = DependencyManager::get();
@@ -112,6 +114,7 @@ void ApplicationOverlay::renderQmlUi(RenderArgs* renderArgs) {
}
void ApplicationOverlay::renderOverlays(RenderArgs* renderArgs) {
+ PROFILE_RANGE(__FUNCTION__);
glm::vec2 size = qApp->getCanvasSize();
mat4 legacyProjection = glm::ortho(0, size.x, size.y, 0, ORTHO_NEAR_CLIP, ORTHO_FAR_CLIP);
@@ -129,11 +132,12 @@ void ApplicationOverlay::renderOverlays(RenderArgs* renderArgs) {
emit qApp->renderingOverlay();
qApp->getOverlays().renderHUD(renderArgs);
+ DependencyManager::get()->render(renderArgs, _overlayFramebuffer->size().width(), _overlayFramebuffer->size().height());
+
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
- renderArgs->_context->syncCache();
fboViewport(_overlayFramebuffer);
}
@@ -196,7 +200,7 @@ void ApplicationOverlay::renderDomainConnectionStatusBorder(RenderArgs* renderAr
geometryCache->useSimpleDrawPipeline(batch);
batch.setProjectionTransform(mat4());
batch.setModelTransform(mat4());
- batch.setUniformTexture(0, DependencyManager::get()->getWhiteTexture());
+ batch.setResourceTexture(0, DependencyManager::get()->getWhiteTexture());
batch._glLineWidth(CONNECTION_STATUS_BORDER_LINE_WIDTH);
// TODO animate the disconnect border for some excitement while not connected?
@@ -219,6 +223,7 @@ GLuint ApplicationOverlay::getOverlayTexture() {
}
void ApplicationOverlay::buildFramebufferObject() {
+ PROFILE_RANGE(__FUNCTION__);
QSize fboSize = qApp->getDeviceSize();
if (_overlayFramebuffer && fboSize == _overlayFramebuffer->size()) {
// Already built
diff --git a/interface/src/ui/OctreeStatsDialog.cpp b/interface/src/ui/OctreeStatsDialog.cpp
index 94f91a5ab7..fbfc3ea662 100644
--- a/interface/src/ui/OctreeStatsDialog.cpp
+++ b/interface/src/ui/OctreeStatsDialog.cpp
@@ -21,6 +21,7 @@
#include "Application.h"
+#include "../octree/OctreePacketProcessor.h"
#include "ui/OctreeStatsDialog.h"
OctreeStatsDialog::OctreeStatsDialog(QWidget* parent, NodeToOctreeSceneStats* model) :
@@ -53,7 +54,7 @@ OctreeStatsDialog::OctreeStatsDialog(QWidget* parent, NodeToOctreeSceneStats* mo
_localElementsMemory = AddStatItem("Elements Memory");
_sendingMode = AddStatItem("Sending Mode");
- _processedPackets = AddStatItem("Processed Packets");
+ _processedPackets = AddStatItem("Entity Packets");
_processedPacketsElements = AddStatItem("Processed Packets Elements");
_processedPacketsEntities = AddStatItem("Processed Packets Entities");
_processedPacketsTiming = AddStatItem("Processed Packets Timing");
@@ -155,6 +156,8 @@ void OctreeStatsDialog::paintEvent(QPaintEvent* event) {
if (sinceLastRefresh < REFRESH_AFTER) {
return QDialog::paintEvent(event);
}
+ const int FLOATING_POINT_PRECISION = 3;
+
_lastRefresh = now;
// Update labels
@@ -245,7 +248,6 @@ void OctreeStatsDialog::paintEvent(QPaintEvent* event) {
auto averageElementsPerPacket = entities->getAverageElementsPerPacket();
auto averageEntitiesPerPacket = entities->getAverageEntitiesPerPacket();
- auto averagePacketsPerSecond = entities->getAveragePacketsPerSecond();
auto averageElementsPerSecond = entities->getAverageElementsPerSecond();
auto averageEntitiesPerSecond = entities->getAverageEntitiesPerSecond();
@@ -253,21 +255,32 @@ void OctreeStatsDialog::paintEvent(QPaintEvent* event) {
auto averageUncompressPerPacket = entities->getAverageUncompressPerPacket();
auto averageReadBitstreamPerPacket = entities->getAverageReadBitstreamPerPacket();
- QString averageElementsPerPacketString = locale.toString(averageElementsPerPacket);
- QString averageEntitiesPerPacketString = locale.toString(averageEntitiesPerPacket);
+ QString averageElementsPerPacketString = locale.toString(averageElementsPerPacket, 'f', FLOATING_POINT_PRECISION);
+ QString averageEntitiesPerPacketString = locale.toString(averageEntitiesPerPacket, 'f', FLOATING_POINT_PRECISION);
- QString averagePacketsPerSecondString = locale.toString(averagePacketsPerSecond);
- QString averageElementsPerSecondString = locale.toString(averageElementsPerSecond);
- QString averageEntitiesPerSecondString = locale.toString(averageEntitiesPerSecond);
+ QString averageElementsPerSecondString = locale.toString(averageElementsPerSecond, 'f', FLOATING_POINT_PRECISION);
+ QString averageEntitiesPerSecondString = locale.toString(averageEntitiesPerSecond, 'f', FLOATING_POINT_PRECISION);
QString averageWaitLockPerPacketString = locale.toString(averageWaitLockPerPacket);
QString averageUncompressPerPacketString = locale.toString(averageUncompressPerPacket);
QString averageReadBitstreamPerPacketString = locale.toString(averageReadBitstreamPerPacket);
label = _labels[_processedPackets];
+ const OctreePacketProcessor& entitiesPacketProcessor = Application::getInstance()->getOctreePacketProcessor();
+
+ auto incomingPPS = entitiesPacketProcessor.getIncomingPPS();
+ auto processedPPS = entitiesPacketProcessor.getProcessedPPS();
+ auto treeProcessedPPS = entities->getAveragePacketsPerSecond();
+
+ QString incomingPPSString = locale.toString(incomingPPS, 'f', FLOATING_POINT_PRECISION);
+ QString processedPPSString = locale.toString(processedPPS, 'f', FLOATING_POINT_PRECISION);
+ QString treeProcessedPPSString = locale.toString(treeProcessedPPS, 'f', FLOATING_POINT_PRECISION);
+
statsValue.str("");
statsValue <<
- "" << qPrintable(averagePacketsPerSecondString) << " per second";
+ "Network IN: " << qPrintable(incomingPPSString) << " PPS / " <<
+ "Queue OUT: " << qPrintable(processedPPSString) << " PPS / " <<
+ "Tree IN: " << qPrintable(treeProcessedPPSString) << " PPS";
label->setText(statsValue.str().c_str());
@@ -321,7 +334,7 @@ void OctreeStatsDialog::paintEvent(QPaintEvent* event) {
}
QString totalTrackedEditsString = locale.toString((uint)totalTrackedEdits);
- QString updatesPerSecondString = locale.toString(updatesPerSecond);
+ QString updatesPerSecondString = locale.toString(updatesPerSecond, 'f', FLOATING_POINT_PRECISION);
QString bytesPerEditString = locale.toString(bytesPerEdit);
statsValue.str("");
diff --git a/interface/src/ui/UpdateDialog.cpp b/interface/src/ui/UpdateDialog.cpp
index 69dfc343d9..6fa2d858fb 100644
--- a/interface/src/ui/UpdateDialog.cpp
+++ b/interface/src/ui/UpdateDialog.cpp
@@ -24,9 +24,18 @@ UpdateDialog::UpdateDialog(QQuickItem* parent) :
int currentVersion = QCoreApplication::applicationVersion().toInt();
int latestVersion = applicationUpdater.data()->getBuildData().lastKey();
int versionsBehind = latestVersion - currentVersion;
- _updateAvailableDetails = "v" + QString::number(latestVersion) + " released on " + applicationUpdater.data()->getBuildData()[latestVersion]["releaseTime"];
- _updateAvailableDetails += "\nYou are " + QString::number(versionsBehind) + " versions behind";
- _releaseNotes = applicationUpdater.data()->getBuildData()[latestVersion]["releaseNotes"];
+ _updateAvailableDetails = "v" + QString::number(latestVersion) + " released on "
+ + QString(applicationUpdater.data()->getBuildData()[latestVersion]["releaseTime"]).replace(" ", " ");
+ _updateAvailableDetails += "\nYou are " + QString::number(versionsBehind) + " version"
+ + (versionsBehind > 1 ? "s" : "") + " behind";
+
+ _releaseNotes = "";
+ for (int i = latestVersion; i > currentVersion; i--) {
+ QString releaseNotes = applicationUpdater.data()->getBuildData()[i]["releaseNotes"];
+ releaseNotes.remove("
");
+ releaseNotes.remove(QRegExp("^\n+"));
+ _releaseNotes += "\n" + QString().sprintf("%d", i) + "\n" + releaseNotes + "\n";
+ }
}
const QString& UpdateDialog::updateAvailableDetails() const {
diff --git a/interface/src/ui/overlays/BillboardOverlay.cpp b/interface/src/ui/overlays/BillboardOverlay.cpp
index 988223765a..1bf4f2a9c7 100644
--- a/interface/src/ui/overlays/BillboardOverlay.cpp
+++ b/interface/src/ui/overlays/BillboardOverlay.cpp
@@ -87,12 +87,12 @@ void BillboardOverlay::render(RenderArgs* args) {
transform.postScale(glm::vec3(getDimensions(), 1.0f));
batch->setModelTransform(transform);
- batch->setUniformTexture(0, _texture->getGPUTexture());
+ batch->setResourceTexture(0, _texture->getGPUTexture());
DependencyManager::get()->renderQuad(*batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight,
glm::vec4(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha));
- batch->setUniformTexture(0, args->_whiteTexture); // restore default white color after me
+ batch->setResourceTexture(0, args->_whiteTexture); // restore default white color after me
}
}
diff --git a/interface/src/ui/overlays/ImageOverlay.cpp b/interface/src/ui/overlays/ImageOverlay.cpp
index 399e8a459a..7a0c3c00c3 100644
--- a/interface/src/ui/overlays/ImageOverlay.cpp
+++ b/interface/src/ui/overlays/ImageOverlay.cpp
@@ -75,44 +75,44 @@ void ImageOverlay::render(RenderArgs* args) {
glm::vec2 topLeft(left, top);
glm::vec2 bottomRight(right, bottom);
- float imageWidth = _texture->getWidth();
- float imageHeight = _texture->getHeight();
-
// if for some reason our image is not over 0 width or height, don't attempt to render the image
- if (_renderImage && imageWidth > 0 && imageHeight > 0) {
+ if (_renderImage) {
+ float imageWidth = _texture->getWidth();
+ float imageHeight = _texture->getHeight();
+ if (imageWidth > 0 && imageHeight > 0) {
+ QRect fromImage;
+ if (_wantClipFromImage) {
+ float scaleX = imageWidth / _texture->getOriginalWidth();
+ float scaleY = imageHeight / _texture->getOriginalHeight();
- QRect fromImage;
- if (_wantClipFromImage) {
- float scaleX = imageWidth / _texture->getOriginalWidth();
- float scaleY = imageHeight / _texture->getOriginalHeight();
+ fromImage.setX(scaleX * _fromImage.x());
+ fromImage.setY(scaleY * _fromImage.y());
+ fromImage.setWidth(scaleX * _fromImage.width());
+ fromImage.setHeight(scaleY * _fromImage.height());
+ }
+ else {
+ fromImage.setX(0);
+ fromImage.setY(0);
+ fromImage.setWidth(imageWidth);
+ fromImage.setHeight(imageHeight);
+ }
- fromImage.setX(scaleX * _fromImage.x());
- fromImage.setY(scaleY * _fromImage.y());
- fromImage.setWidth(scaleX * _fromImage.width());
- fromImage.setHeight(scaleY * _fromImage.height());
+ float x = fromImage.x() / imageWidth;
+ float y = fromImage.y() / imageHeight;
+ float w = fromImage.width() / imageWidth; // ?? is this what we want? not sure
+ float h = fromImage.height() / imageHeight;
+
+ glm::vec2 texCoordTopLeft(x, y);
+ glm::vec2 texCoordBottomRight(x + w, y + h);
+
+ DependencyManager::get()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, quadColor);
} else {
- fromImage.setX(0);
- fromImage.setY(0);
- fromImage.setWidth(imageWidth);
- fromImage.setHeight(imageHeight);
+ DependencyManager::get()->renderQuad(topLeft, bottomRight, quadColor);
}
-
- float x = fromImage.x() / imageWidth;
- float y = fromImage.y() / imageHeight;
- float w = fromImage.width() / imageWidth; // ?? is this what we want? not sure
- float h = fromImage.height() / imageHeight;
-
- glm::vec2 texCoordTopLeft(x, y);
- glm::vec2 texCoordBottomRight(x + w, y + h);
-
- DependencyManager::get()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, quadColor);
+ glDisable(GL_TEXTURE_2D);
} else {
DependencyManager::get()->renderQuad(topLeft, bottomRight, quadColor);
}
-
- if (_renderImage) {
- glDisable(GL_TEXTURE_2D);
- }
}
void ImageOverlay::setProperties(const QScriptValue& properties) {
diff --git a/interface/src/ui/overlays/LocalModelsOverlay.cpp b/interface/src/ui/overlays/LocalModelsOverlay.cpp
index bfcdf01dc1..a3ea8527f0 100644
--- a/interface/src/ui/overlays/LocalModelsOverlay.cpp
+++ b/interface/src/ui/overlays/LocalModelsOverlay.cpp
@@ -40,14 +40,12 @@ void LocalModelsOverlay::render(RenderArgs* args) {
auto batch = args ->_batch;
Application* app = Application::getInstance();
- glm::vec3 oldTranslation = app->getCurrentViewFrustum()->getPosition();
+ glm::vec3 oldTranslation = app->getViewFrustum()->getPosition();
Transform transform = Transform();
transform.setTranslation(oldTranslation + getPosition());
batch->setViewTransform(transform);
_entityTreeRenderer->render(args);
- transform.setTranslation(oldTranslation);
- batch->setViewTransform(transform);
-
+
if (glower) {
delete glower;
}
diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp
index fb0a095e13..db1bc2185a 100644
--- a/interface/src/ui/overlays/Overlays.cpp
+++ b/interface/src/ui/overlays/Overlays.cpp
@@ -96,6 +96,7 @@ void Overlays::cleanupOverlaysToDelete() {
}
void Overlays::renderHUD(RenderArgs* renderArgs) {
+ PROFILE_RANGE(__FUNCTION__);
QReadLocker lock(&_lock);
gpu::Batch batch;
renderArgs->_batch = &batch;
diff --git a/interface/src/ui/overlays/Rectangle3DOverlay.cpp b/interface/src/ui/overlays/Rectangle3DOverlay.cpp
index ddab8040b1..dfe5a3ad74 100644
--- a/interface/src/ui/overlays/Rectangle3DOverlay.cpp
+++ b/interface/src/ui/overlays/Rectangle3DOverlay.cpp
@@ -35,14 +35,13 @@ void Rectangle3DOverlay::render(RenderArgs* args) {
if (!_visible) {
return; // do nothing if we're not visible
}
-
+
float alpha = getAlpha();
xColor color = getColor();
const float MAX_COLOR = 255.0f;
glm::vec4 rectangleColor(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha);
glm::vec3 position = getPosition();
- glm::vec3 center = getCenter();
glm::vec2 dimensions = getDimensions();
glm::vec2 halfDimensions = dimensions * 0.5f;
glm::quat rotation = getRotation();
@@ -67,7 +66,7 @@ void Rectangle3DOverlay::render(RenderArgs* args) {
glm::vec3 point2(halfDimensions.x, -halfDimensions.y, 0.0f);
glm::vec3 point3(halfDimensions.x, halfDimensions.y, 0.0f);
glm::vec3 point4(-halfDimensions.x, halfDimensions.y, 0.0f);
-
+
geometryCache->renderDashedLine(*batch, point1, point2, rectangleColor);
geometryCache->renderDashedLine(*batch, point2, point3, rectangleColor);
geometryCache->renderDashedLine(*batch, point3, point4, rectangleColor);
diff --git a/interface/src/ui/overlays/Text3DOverlay.cpp b/interface/src/ui/overlays/Text3DOverlay.cpp
index 37774094de..17dc8d03a8 100644
--- a/interface/src/ui/overlays/Text3DOverlay.cpp
+++ b/interface/src/ui/overlays/Text3DOverlay.cpp
@@ -13,6 +13,7 @@
#include "Text3DOverlay.h"
+#include
#include
#include
@@ -114,6 +115,7 @@ void Text3DOverlay::render(RenderArgs* args) {
glm::vec3 topLeft(-halfDimensions.x, -halfDimensions.y, SLIGHTLY_BEHIND);
glm::vec3 bottomRight(halfDimensions.x, halfDimensions.y, SLIGHTLY_BEHIND);
+ DependencyManager::get()->bindSimpleProgram(batch, false, true, false, true);
DependencyManager::get()->renderQuad(batch, topLeft, bottomRight, quadColor);
// Same font properties as textSize()
diff --git a/interface/ui/temp.qml b/interface/ui/temp.qml
new file mode 100644
index 0000000000..9617a0a8b7
--- /dev/null
+++ b/interface/ui/temp.qml
@@ -0,0 +1,33 @@
+import QtQuick 2.4
+import QtQuick.Controls 2.3
+import QtQuick.Controls.Styles 1.3
+
+
+Item {
+ implicitHeight: 200
+ implicitWidth: 800
+
+
+ TextArea {
+ id: gutter
+ anchors.left: parent.left
+ anchors.top: parent.top
+ anchors.bottom: parent.bottom
+ style: TextAreaStyle {
+ backgroundColor: "grey"
+ }
+ width: 16
+ text: ">"
+ font.family: "Lucida Console"
+ }
+ TextArea {
+ anchors.left: gutter.right
+ anchors.top: parent.top
+ anchors.bottom: parent.bottom
+ anchors.right: parent.right
+ text: "undefined"
+ font.family: "Lucida Console"
+
+ }
+}
+
diff --git a/libraries/animation/src/AnimationLoop.cpp b/libraries/animation/src/AnimationLoop.cpp
index e60df1eaf9..43e049f851 100644
--- a/libraries/animation/src/AnimationLoop.cpp
+++ b/libraries/animation/src/AnimationLoop.cpp
@@ -84,14 +84,14 @@ void AnimationLoop::setStartAutomatically(bool startAutomatically) {
}
void AnimationLoop::setRunning(bool running) {
- if (_running == running) {
+ // don't do anything if the new value is the same as the value we already have
+ if (_running != running) {
+ _running = running;
+
+ // If we just set running to true, then also reset the frame to the first frame
if (running) {
// move back to the beginning
_frameIndex = _firstFrame;
}
- return;
- }
- if ((_running = running)) {
- _frameIndex = _firstFrame;
}
}
diff --git a/libraries/display-plugins/src/display-plugins/oculus/Oculus_0_6_DisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/oculus/Oculus_0_6_DisplayPlugin.cpp
index c3bcc1c4c9..924d15c9c9 100644
--- a/libraries/display-plugins/src/display-plugins/oculus/Oculus_0_6_DisplayPlugin.cpp
+++ b/libraries/display-plugins/src/display-plugins/oculus/Oculus_0_6_DisplayPlugin.cpp
@@ -192,6 +192,11 @@ void Oculus_0_6_DisplayPlugin::activate(PluginContainer * container) {
ovrSizei & size = sceneLayer.Viewport[eye].Size = ovrHmd_GetFovTextureSize(_hmd, eye, fov, 1.0f);
sceneLayer.Viewport[eye].Pos = { eye == ovrEye_Left ? 0 : size.w, 0 };
});
+ // We're rendering both eyes to the same texture, so only one of the
+ // pointers is populated
+ sceneLayer.ColorTexture[0] = _sceneFbo->color;
+ // not needed since the structure was zeroed on init, but explicit
+ sceneLayer.ColorTexture[1] = nullptr;
PerformanceTimer::setActive(true);
@@ -213,13 +218,6 @@ void Oculus_0_6_DisplayPlugin::customizeContext(PluginContainer * container) {
_sceneFbo = SwapFboPtr(new SwapFramebufferWrapper(_hmd));
_sceneFbo->Init(getRecommendedRenderSize());
-
- // We're rendering both eyes to the same texture, so only one of the
- // pointers is populated
- ovrLayerEyeFov& sceneLayer = getSceneLayer();
- sceneLayer.ColorTexture[0] = _sceneFbo->color;
- // not needed since the structure was zeroed on init, but explicit
- sceneLayer.ColorTexture[1] = nullptr;
}
void Oculus_0_6_DisplayPlugin::deactivate() {
@@ -274,6 +272,7 @@ void Oculus_0_6_DisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sc
PerformanceTimer("OculusSubmit");
ovrLayerHeader* layers = &sceneLayer.Header;
ovrResult result = ovrHmd_SubmitFrame(_hmd, _frameIndex, nullptr, &layers, 1);
+ qDebug() << result;
}
_sceneFbo->Increment();
diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp
index 03d88200c5..6be891e147 100644
--- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp
+++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp
@@ -93,16 +93,18 @@ void EntityTreeRenderer::clear() {
foreach (const EntityItemID& entityID, _entityScripts.keys()) {
checkAndCallUnload(entityID);
}
- OctreeRenderer::clear();
_entityScripts.clear();
auto scene = _viewState->getMain3DScene();
render::PendingChanges pendingChanges;
+
foreach(auto entity, _entitiesInScene) {
entity->removeFromScene(entity, scene, pendingChanges);
}
scene->enqueuePendingChanges(pendingChanges);
_entitiesInScene.clear();
+
+ OctreeRenderer::clear();
}
void EntityTreeRenderer::init() {
@@ -802,6 +804,8 @@ void EntityTreeRenderer::connectSignalsToSlots(EntityScriptingInterface* entityS
connect(this, &EntityTreeRenderer::enterEntity, entityScriptingInterface, &EntityScriptingInterface::enterEntity);
connect(this, &EntityTreeRenderer::leaveEntity, entityScriptingInterface, &EntityScriptingInterface::leaveEntity);
connect(this, &EntityTreeRenderer::collisionWithEntity, entityScriptingInterface, &EntityScriptingInterface::collisionWithEntity);
+
+ connect(&(*DependencyManager::get()), &SceneScriptingInterface::shouldRenderEntitiesChanged, this, &EntityTreeRenderer::updateEntityRenderStatus, Qt::QueuedConnection);
}
QScriptValueList EntityTreeRenderer::createMouseEventArgs(const EntityItemID& entityID, QMouseEvent* event, unsigned int deviceID) {
@@ -1001,7 +1005,7 @@ void EntityTreeRenderer::deletingEntity(const EntityItemID& entityID) {
checkAndCallUnload(entityID);
}
_entityScripts.remove(entityID);
-
+
// here's where we remove the entity payload from the scene
if (_entitiesInScene.contains(entityID)) {
auto entity = _entitiesInScene.take(entityID);
@@ -1152,3 +1156,18 @@ void EntityTreeRenderer::entityCollisionWithEntity(const EntityItemID& idA, cons
entityScriptB.property("collisionWithEntity").call(entityScriptA, args);
}
}
+
+void EntityTreeRenderer::updateEntityRenderStatus(bool shouldRenderEntities) {
+ if (DependencyManager::get()->shouldRenderEntities()) {
+ for (auto entityID : _entityIDsLastInScene) {
+ addingEntity(entityID);
+ }
+ _entityIDsLastInScene.clear();
+ } else {
+ _entityIDsLastInScene = _entitiesInScene.keys();
+ for (auto entityID : _entityIDsLastInScene) {
+ // FIXME - is this really right? do we want to do the deletingEntity() code or just remove from the scene.
+ deletingEntity(entityID);
+ }
+ }
+}
diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h
index bd6044516f..28a6a3172c 100644
--- a/libraries/entities-renderer/src/EntityTreeRenderer.h
+++ b/libraries/entities-renderer/src/EntityTreeRenderer.h
@@ -90,6 +90,9 @@ public:
virtual void scriptContentsAvailable(const QUrl& url, const QString& scriptContents);
virtual void errorInLoadingScript(const QUrl& url);
+ // For Scene.shouldRenderEntities
+ QList& getEntitiesLastInScene() { return _entityIDsLastInScene; }
+
signals:
void mousePressOnEntity(const RayToEntityIntersectionResult& entityItemID, const QMouseEvent* event, unsigned int deviceId);
void mouseMoveOnEntity(const RayToEntityIntersectionResult& entityItemID, const QMouseEvent* event, unsigned int deviceId);
@@ -112,6 +115,7 @@ public slots:
void deletingEntity(const EntityItemID& entityID);
void entitySciptChanging(const EntityItemID& entityID, const bool reload);
void entityCollisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision);
+ void updateEntityRenderStatus(bool shouldRenderEntities);
// optional slots that can be wired to menu items
void setDisplayElementChildProxies(bool value) { _displayElementChildProxies = value; }
@@ -188,6 +192,8 @@ private:
int _previousStageDay;
QHash _entitiesInScene;
+ // For Scene.shouldRenderEntities
+ QList _entityIDsLastInScene;
};
diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp
index ae1c97f07f..2deb34d1f8 100644
--- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp
@@ -161,6 +161,7 @@ namespace render {
template <> void payloadRender(const RenderableModelEntityItemMeta::Pointer& payload, RenderArgs* args) {
if (args) {
if (payload && payload->entity) {
+ PROFILE_RANGE("MetaModelRender");
payload->entity->render(args);
}
}
@@ -188,6 +189,7 @@ void makeEntityItemStatusGetters(RenderableModelEntityItem* entity, render::Item
bool RenderableModelEntityItem::addToScene(EntityItemPointer self, std::shared_ptr scene,
render::PendingChanges& pendingChanges) {
+
_myMetaItem = scene->allocateID();
auto renderData = RenderableModelEntityItemMeta::Pointer(new RenderableModelEntityItemMeta(self));
@@ -198,7 +200,10 @@ bool RenderableModelEntityItem::addToScene(EntityItemPointer self, std::shared_p
if (_model) {
render::Item::Status::Getters statusGetters;
makeEntityItemStatusGetters(this, statusGetters);
- return _model->addToScene(scene, pendingChanges, statusGetters);
+
+ // note: we don't care if the model fails to add items, we always added our meta item and therefore we return
+ // true so that the system knows our meta item is in the scene!
+ _model->addToScene(scene, pendingChanges, statusGetters);
}
return true;
diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp
index d00728a9eb..2eb95d1bef 100644
--- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp
@@ -50,7 +50,7 @@ void RenderableParticleEffectEntityItem::render(RenderArgs* args) {
Q_ASSERT(args->_batch);
gpu::Batch& batch = *args->_batch;
if (textured) {
- batch.setUniformTexture(0, _texture->getGPUTexture());
+ batch.setResourceTexture(0, _texture->getGPUTexture());
}
batch.setModelTransform(getTransformToCenter());
DependencyManager::get()->bindSimpleProgram(batch, textured);
diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp
index 7603187e94..0c9b36dd87 100644
--- a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp
@@ -36,10 +36,6 @@ void RenderableTextEntityItem::render(RenderArgs* args) {
glm::vec4 backgroundColor = glm::vec4(toGlm(getBackgroundColorX()), 1.0f);
glm::vec3 dimensions = getDimensions();
- Transform transformToTopLeft = getTransformToCenter();
- transformToTopLeft.postTranslate(glm::vec3(-0.5f, 0.5f, 0.0f)); // Go to the top left
- transformToTopLeft.setScale(1.0f); // Use a scale of one so that the text is not deformed
-
// Render background
glm::vec3 minCorner = glm::vec3(0.0f, -dimensions.y, SLIGHTLY_BEHIND);
glm::vec3 maxCorner = glm::vec3(dimensions.x, 0.0f, SLIGHTLY_BEHIND);
@@ -48,15 +44,22 @@ void RenderableTextEntityItem::render(RenderArgs* args) {
// Batch render calls
Q_ASSERT(args->_batch);
gpu::Batch& batch = *args->_batch;
+
+ Transform transformToTopLeft = getTransformToCenter();
+ if (getFaceCamera()) {
+ //rotate about vertical to face the camera
+ glm::vec3 dPosition = args->_viewFrustum->getPosition() - getPosition();
+ // If x and z are 0, atan(x, z) is undefined, so default to 0 degrees
+ float yawRotation = dPosition.x == 0.0f && dPosition.z == 0.0f ? 0.0f : glm::atan(dPosition.x, dPosition.z);
+ glm::quat orientation = glm::quat(glm::vec3(0.0f, yawRotation, 0.0f));
+ transformToTopLeft.setRotation(orientation);
+ }
+ transformToTopLeft.postTranslate(glm::vec3(-0.5f, 0.5f, 0.0f)); // Go to the top left
+ transformToTopLeft.setScale(1.0f); // Use a scale of one so that the text is not deformed
+
batch.setModelTransform(transformToTopLeft);
- //rotate about vertical to face the camera
- if (getFaceCamera()) {
- transformToTopLeft.postRotate(args->_viewFrustum->getOrientation());
- batch.setModelTransform(transformToTopLeft);
- }
-
- DependencyManager::get()->bindSimpleProgram(batch, false, false);
+ DependencyManager::get()->bindSimpleProgram(batch, false, false, false, true);
DependencyManager::get()->renderQuad(batch, minCorner, maxCorner, backgroundColor);
float scale = _lineHeight / _textRenderer->getFontSize();
diff --git a/libraries/entities/src/EntityActionFactoryInterface.h b/libraries/entities/src/EntityActionFactoryInterface.h
index 5269405d55..adff1a53ba 100644
--- a/libraries/entities/src/EntityActionFactoryInterface.h
+++ b/libraries/entities/src/EntityActionFactoryInterface.h
@@ -23,13 +23,11 @@ class EntityActionFactoryInterface : public QObject, public Dependency {
public:
EntityActionFactoryInterface() { }
virtual ~EntityActionFactoryInterface() { }
- virtual EntityActionPointer factory(EntitySimulation* simulation,
- EntityActionType type,
- QUuid id,
+ virtual EntityActionPointer factory(EntityActionType type,
+ const QUuid& id,
EntityItemPointer ownerEntity,
QVariantMap arguments) { assert(false); return nullptr; }
- virtual EntityActionPointer factoryBA(EntitySimulation* simulation,
- EntityItemPointer ownerEntity,
+ virtual EntityActionPointer factoryBA(EntityItemPointer ownerEntity,
QByteArray data) { assert(false); return nullptr; }
};
diff --git a/libraries/entities/src/EntityActionInterface.cpp b/libraries/entities/src/EntityActionInterface.cpp
index 2b723d4e15..ba7f3afea4 100644
--- a/libraries/entities/src/EntityActionInterface.cpp
+++ b/libraries/entities/src/EntityActionInterface.cpp
@@ -127,21 +127,21 @@ glm::vec3 EntityActionInterface::extractVec3Argument(QString objectName, QVarian
qDebug() << objectName << "requires argument:" << argumentName;
}
ok = false;
- return glm::vec3();
+ return glm::vec3(0.0f);
}
QVariant resultV = arguments[argumentName];
if (resultV.type() != (QVariant::Type) QMetaType::QVariantMap) {
qDebug() << objectName << "argument" << argumentName << "must be a map";
ok = false;
- return glm::vec3();
+ return glm::vec3(0.0f);
}
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";
+ qDebug() << objectName << "argument" << argumentName << "must be a map with keys: x, y, z";
ok = false;
- return glm::vec3();
+ return glm::vec3(0.0f);
}
QVariant xV = resultVM["x"];
@@ -155,9 +155,15 @@ glm::vec3 EntityActionInterface::extractVec3Argument(QString objectName, QVarian
float y = yV.toFloat(&yOk);
float z = zV.toFloat(&zOk);
if (!xOk || !yOk || !zOk) {
- qDebug() << objectName << "argument" << argumentName << "must be a map with keys of x, y, z and values of type float.";
+ qDebug() << objectName << "argument" << argumentName << "must be a map with keys: x, y, and z of type float.";
ok = false;
- return glm::vec3();
+ return glm::vec3(0.0f);
+ }
+
+ if (x != x || y != y || z != z) {
+ // at least one of the values is NaN
+ ok = false;
+ return glm::vec3(0.0f);
}
return glm::vec3(x, y, z);
@@ -181,8 +187,8 @@ glm::quat EntityActionInterface::extractQuatArgument(QString objectName, QVarian
}
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";
+ if (!resultVM.contains("x") || !resultVM.contains("y") || !resultVM.contains("z") || !resultVM.contains("w")) {
+ qDebug() << objectName << "argument" << argumentName << "must be a map with keys: x, y, z, and w";
ok = false;
return glm::quat();
}
@@ -202,12 +208,18 @@ glm::quat EntityActionInterface::extractQuatArgument(QString objectName, QVarian
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.";
+ << "must be a map with keys: x, y, z, and w of type float.";
ok = false;
return glm::quat();
}
- return glm::quat(w, x, y, z);
+ if (x != x || y != y || z != z || w != w) {
+ // at least one of the components is NaN!
+ ok = false;
+ return glm::quat();
+ }
+
+ return glm::normalize(glm::quat(w, x, y, z));
}
float EntityActionInterface::extractFloatArgument(QString objectName, QVariantMap arguments,
@@ -224,7 +236,7 @@ float EntityActionInterface::extractFloatArgument(QString objectName, QVariantMa
bool vOk = true;
float v = vV.toFloat(&vOk);
- if (!vOk) {
+ if (!vOk || v != v) {
ok = false;
return 0.0f;
}
diff --git a/libraries/entities/src/EntityActionInterface.h b/libraries/entities/src/EntityActionInterface.h
index 5693e1fb6f..a4f1c8ea15 100644
--- a/libraries/entities/src/EntityActionInterface.h
+++ b/libraries/entities/src/EntityActionInterface.h
@@ -29,10 +29,10 @@ enum EntityActionType {
class EntityActionInterface {
public:
- EntityActionInterface() { }
+ EntityActionInterface(EntityActionType type, const QUuid& id) : _id(id), _type(type) { }
virtual ~EntityActionInterface() { }
- virtual const QUuid& getID() const = 0;
- virtual EntityActionType getType() { assert(false); return ACTION_TYPE_NONE; }
+ const QUuid& getID() const { return _id; }
+ EntityActionType getType() const { return _type; }
virtual void removeFromSimulation(EntitySimulation* simulation) const = 0;
virtual EntityItemWeakPointer getOwnerEntity() const = 0;
@@ -40,7 +40,7 @@ public:
virtual bool updateArguments(QVariantMap arguments) = 0;
virtual QVariantMap getArguments() = 0;
- virtual QByteArray serialize() = 0;
+ virtual QByteArray serialize() const = 0;
virtual void deserialize(QByteArray serializedArguments) = 0;
static EntityActionType actionTypeFromString(QString actionTypeString);
@@ -68,6 +68,8 @@ protected:
static QString extractStringArgument(QString objectName, QVariantMap arguments,
QString argumentName, bool& ok, bool required = true);
+ QUuid _id;
+ EntityActionType _type;
};
diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp
index 6c27152a97..7354628390 100644
--- a/libraries/entities/src/EntityItem.cpp
+++ b/libraries/entities/src/EntityItem.cpp
@@ -12,9 +12,11 @@
#include "EntityItem.h"
#include
+#include
#include
+#include
#include
#include
#include
@@ -354,50 +356,78 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
// ~27-35 bytes...
const int MINIMUM_HEADER_BYTES = 27;
- int bytesRead = 0;
if (bytesLeftToRead < MINIMUM_HEADER_BYTES) {
return 0;
}
+ int clockSkew = args.sourceNode ? args.sourceNode->getClockSkewUsec() : 0;
+
+ BufferParser parser(data, bytesLeftToRead);
+
+#ifdef DEBUG
+#define VALIDATE_ENTITY_ITEM_PARSER 1
+#endif
+
+#ifdef VALIDATE_ENTITY_ITEM_PARSER
+ int bytesRead = 0;
int originalLength = bytesLeftToRead;
// TODO: figure out a way to avoid the big deep copy below.
QByteArray originalDataBuffer((const char*)data, originalLength); // big deep copy!
-
- int clockSkew = args.sourceNode ? args.sourceNode->getClockSkewUsec() : 0;
-
const unsigned char* dataAt = data;
+#endif
// id
- QByteArray encodedID = originalDataBuffer.mid(bytesRead, NUM_BYTES_RFC4122_UUID); // maximum possible size
- _id = QUuid::fromRfc4122(encodedID);
- dataAt += encodedID.size();
- bytesRead += encodedID.size();
-
+ parser.readUuid(_id);
+#ifdef VALIDATE_ENTITY_ITEM_PARSER
+ {
+ QByteArray encodedID = originalDataBuffer.mid(bytesRead, NUM_BYTES_RFC4122_UUID); // maximum possible size
+ QUuid id = QUuid::fromRfc4122(encodedID);
+ dataAt += encodedID.size();
+ bytesRead += encodedID.size();
+ Q_ASSERT(id == _id);
+ Q_ASSERT(parser.offset() == (unsigned int) bytesRead);
+ }
+#endif
+
// type
+ parser.readCompressedCount((quint32&)_type);
+#ifdef VALIDATE_ENTITY_ITEM_PARSER
QByteArray encodedType = originalDataBuffer.mid(bytesRead); // maximum possible size
ByteCountCoded typeCoder = encodedType;
encodedType = typeCoder; // determine true length
dataAt += encodedType.size();
bytesRead += encodedType.size();
quint32 type = typeCoder;
- _type = (EntityTypes::EntityType)type;
+ EntityTypes::EntityType oldType = (EntityTypes::EntityType)type;
+ Q_ASSERT(oldType == _type);
+ Q_ASSERT(parser.offset() == (unsigned int) bytesRead);
+#endif
bool overwriteLocalData = true; // assume the new content overwrites our local data
+ quint64 now = usecTimestampNow();
// _created
- quint64 createdFromBuffer = 0;
- memcpy(&createdFromBuffer, dataAt, sizeof(createdFromBuffer));
- dataAt += sizeof(createdFromBuffer);
- bytesRead += sizeof(createdFromBuffer);
-
- quint64 now = usecTimestampNow();
- if (_created == UNKNOWN_CREATED_TIME) {
- // we don't yet have a _created timestamp, so we accept this one
- createdFromBuffer -= clockSkew;
- if (createdFromBuffer > now || createdFromBuffer == UNKNOWN_CREATED_TIME) {
- createdFromBuffer = now;
+ {
+ quint64 createdFromBuffer = 0;
+ parser.readValue(createdFromBuffer);
+#ifdef VALIDATE_ENTITY_ITEM_PARSER
+ {
+ quint64 createdFromBuffer2 = 0;
+ memcpy(&createdFromBuffer2, dataAt, sizeof(createdFromBuffer2));
+ dataAt += sizeof(createdFromBuffer2);
+ bytesRead += sizeof(createdFromBuffer2);
+ Q_ASSERT(createdFromBuffer2 == createdFromBuffer);
+ Q_ASSERT(parser.offset() == (unsigned int) bytesRead);
+ }
+#endif
+ if (_created == UNKNOWN_CREATED_TIME) {
+ // we don't yet have a _created timestamp, so we accept this one
+ createdFromBuffer -= clockSkew;
+ if (createdFromBuffer > now || createdFromBuffer == UNKNOWN_CREATED_TIME) {
+ createdFromBuffer = now;
+ }
+ _created = createdFromBuffer;
}
- _created = createdFromBuffer;
}
#ifdef WANT_DEBUG
@@ -417,15 +447,21 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
#endif
quint64 lastEditedFromBuffer = 0;
- quint64 lastEditedFromBufferAdjusted = 0;
// TODO: we could make this encoded as a delta from _created
// _lastEdited
- memcpy(&lastEditedFromBuffer, dataAt, sizeof(lastEditedFromBuffer));
- dataAt += sizeof(lastEditedFromBuffer);
- bytesRead += sizeof(lastEditedFromBuffer);
- lastEditedFromBufferAdjusted = lastEditedFromBuffer - clockSkew;
-
+ parser.readValue(lastEditedFromBuffer);
+#ifdef VALIDATE_ENTITY_ITEM_PARSER
+ {
+ quint64 lastEditedFromBuffer2 = 0;
+ memcpy(&lastEditedFromBuffer2, dataAt, sizeof(lastEditedFromBuffer2));
+ dataAt += sizeof(lastEditedFromBuffer2);
+ bytesRead += sizeof(lastEditedFromBuffer2);
+ Q_ASSERT(lastEditedFromBuffer2 == lastEditedFromBuffer);
+ Q_ASSERT(parser.offset() == (unsigned int) bytesRead);
+ }
+#endif
+ quint64 lastEditedFromBufferAdjusted = lastEditedFromBuffer - clockSkew;
if (lastEditedFromBufferAdjusted > now) {
lastEditedFromBufferAdjusted = now;
}
@@ -487,9 +523,21 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
}
// last updated is stored as ByteCountCoded delta from lastEdited
- QByteArray encodedUpdateDelta = originalDataBuffer.mid(bytesRead); // maximum possible size
- ByteCountCoded updateDeltaCoder = encodedUpdateDelta;
- quint64 updateDelta = updateDeltaCoder;
+ quint64 updateDelta;
+ parser.readCompressedCount(updateDelta);
+#ifdef VALIDATE_ENTITY_ITEM_PARSER
+ {
+ QByteArray encodedUpdateDelta = originalDataBuffer.mid(bytesRead); // maximum possible size
+ ByteCountCoded updateDeltaCoder = encodedUpdateDelta;
+ quint64 updateDelta2 = updateDeltaCoder;
+ Q_ASSERT(updateDelta == updateDelta2);
+ encodedUpdateDelta = updateDeltaCoder; // determine true length
+ dataAt += encodedUpdateDelta.size();
+ bytesRead += encodedUpdateDelta.size();
+ Q_ASSERT(parser.offset() == (unsigned int) bytesRead);
+ }
+#endif
+
if (overwriteLocalData) {
_lastUpdated = lastEditedFromBufferAdjusted + updateDelta; // don't adjust for clock skew since we already did that
#ifdef WANT_DEBUG
@@ -499,17 +547,25 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
#endif
}
- encodedUpdateDelta = updateDeltaCoder; // determine true length
- dataAt += encodedUpdateDelta.size();
- bytesRead += encodedUpdateDelta.size();
-
// Newer bitstreams will have a last simulated and a last updated value
quint64 lastSimulatedFromBufferAdjusted = now;
if (args.bitstreamVersion >= VERSION_ENTITIES_HAS_LAST_SIMULATED_TIME) {
// last simulated is stored as ByteCountCoded delta from lastEdited
- QByteArray encodedSimulatedDelta = originalDataBuffer.mid(bytesRead); // maximum possible size
- ByteCountCoded simulatedDeltaCoder = encodedSimulatedDelta;
- quint64 simulatedDelta = simulatedDeltaCoder;
+ quint64 simulatedDelta;
+ parser.readCompressedCount(simulatedDelta);
+#ifdef VALIDATE_ENTITY_ITEM_PARSER
+ {
+ QByteArray encodedSimulatedDelta = originalDataBuffer.mid(bytesRead); // maximum possible size
+ ByteCountCoded simulatedDeltaCoder = encodedSimulatedDelta;
+ quint64 simulatedDelta2 = simulatedDeltaCoder;
+ Q_ASSERT(simulatedDelta2 == simulatedDelta);
+ encodedSimulatedDelta = simulatedDeltaCoder; // determine true length
+ dataAt += encodedSimulatedDelta.size();
+ bytesRead += encodedSimulatedDelta.size();
+ Q_ASSERT(parser.offset() == (unsigned int) bytesRead);
+ }
+#endif
+
if (overwriteLocalData) {
lastSimulatedFromBufferAdjusted = lastEditedFromBufferAdjusted + simulatedDelta; // don't adjust for clock skew since we already did that
if (lastSimulatedFromBufferAdjusted > now) {
@@ -521,9 +577,6 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
qCDebug(entities) << " lastSimulatedFromBufferAdjusted:" << debugTime(lastSimulatedFromBufferAdjusted, now);
#endif
}
- encodedSimulatedDelta = simulatedDeltaCoder; // determine true length
- dataAt += encodedSimulatedDelta.size();
- bytesRead += encodedSimulatedDelta.size();
}
#ifdef WANT_DEBUG
@@ -537,10 +590,26 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
// Property Flags
- QByteArray encodedPropertyFlags = originalDataBuffer.mid(bytesRead); // maximum possible size
- EntityPropertyFlags propertyFlags = encodedPropertyFlags;
- dataAt += propertyFlags.getEncodedLength();
- bytesRead += propertyFlags.getEncodedLength();
+ EntityPropertyFlags propertyFlags;
+ parser.readFlags(propertyFlags);
+#ifdef VALIDATE_ENTITY_ITEM_PARSER
+ {
+ QByteArray encodedPropertyFlags = originalDataBuffer.mid(bytesRead); // maximum possible size
+ EntityPropertyFlags propertyFlags2 = encodedPropertyFlags;
+ dataAt += propertyFlags.getEncodedLength();
+ bytesRead += propertyFlags.getEncodedLength();
+ Q_ASSERT(propertyFlags2 == propertyFlags);
+ Q_ASSERT(parser.offset() == (unsigned int)bytesRead);
+ }
+#endif
+
+#ifdef VALIDATE_ENTITY_ITEM_PARSER
+ Q_ASSERT(parser.data() + parser.offset() == dataAt);
+#else
+ const unsigned char* dataAt = parser.data() + parser.offset();
+ int bytesRead = parser.offset();
+#endif
+
if (args.bitstreamVersion >= VERSION_ENTITIES_HAVE_SIMULATION_OWNER_AND_ACTIONS_OVER_WIRE) {
// pack SimulationOwner and terse update properties near each other
@@ -549,6 +618,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
// even when we would otherwise ignore the rest of the packet.
if (propertyFlags.getHasProperty(PROP_SIMULATION_OWNER)) {
+
QByteArray simOwnerData;
int bytes = OctreePacketData::unpackDataFromBytes(dataAt, simOwnerData);
SimulationOwner newSimOwner;
@@ -1419,20 +1489,22 @@ void EntityItem::clearSimulationOwnership() {
}
+
bool EntityItem::addAction(EntitySimulation* simulation, EntityActionPointer action) {
+ lockForWrite();
checkWaitingToRemove(simulation);
- if (!checkWaitingActionData(simulation)) {
- return false;
- }
bool result = addActionInternal(simulation, action);
if (!result) {
- removeAction(simulation, action->getID());
+ removeActionInternal(action->getID());
}
+
+ unlock();
return result;
}
bool EntityItem::addActionInternal(EntitySimulation* simulation, EntityActionPointer action) {
+ assertLocked();
assert(action);
assert(simulation);
auto actionOwnerEntity = action->getOwnerEntity().lock();
@@ -1448,41 +1520,44 @@ bool EntityItem::addActionInternal(EntitySimulation* simulation, EntityActionPoi
QByteArray newDataCache = serializeActions(success);
if (success) {
_allActionsDataCache = newDataCache;
+ _dirtyFlags |= EntityItem::DIRTY_PHYSICS_ACTIVATION;
}
return success;
}
bool EntityItem::updateAction(EntitySimulation* simulation, const QUuid& actionID, const QVariantMap& arguments) {
+ lockForWrite();
checkWaitingToRemove(simulation);
- if (!checkWaitingActionData(simulation)) {
- return false;
- }
if (!_objectActions.contains(actionID)) {
+ unlock();
return false;
}
EntityActionPointer action = _objectActions[actionID];
- bool success = action->updateArguments(arguments);
+ bool success = action->updateArguments(arguments);
if (success) {
_allActionsDataCache = serializeActions(success);
+ _dirtyFlags |= EntityItem::DIRTY_PHYSICS_ACTIVATION;
} else {
qDebug() << "EntityItem::updateAction failed";
}
+ unlock();
return success;
}
bool EntityItem::removeAction(EntitySimulation* simulation, const QUuid& actionID) {
+ lockForWrite();
checkWaitingToRemove(simulation);
- if (!checkWaitingActionData(simulation)) {
- return false;;
- }
- return removeActionInternal(actionID);
+ bool success = removeActionInternal(actionID);
+ unlock();
+ return success;
}
bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulation* simulation) {
+ assertWriteLocked();
if (_objectActions.contains(actionID)) {
if (!simulation) {
EntityTree* entityTree = _element ? _element->getTree() : nullptr;
@@ -1499,13 +1574,14 @@ bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulation* s
bool success = true;
_allActionsDataCache = serializeActions(success);
+ _dirtyFlags |= EntityItem::DIRTY_PHYSICS_ACTIVATION;
return success;
}
return false;
}
bool EntityItem::clearActions(EntitySimulation* simulation) {
- _waitingActionData.clear();
+ lockForWrite();
QHash::iterator i = _objectActions.begin();
while (i != _objectActions.end()) {
const QUuid id = i.key();
@@ -1514,85 +1590,85 @@ bool EntityItem::clearActions(EntitySimulation* simulation) {
action->setOwnerEntity(nullptr);
action->removeFromSimulation(simulation);
}
+ // empty _serializedActions means no actions for the EntityItem
_actionsToRemove.clear();
_allActionsDataCache.clear();
+ _dirtyFlags |= EntityItem::DIRTY_PHYSICS_ACTIVATION;
+ unlock();
return true;
}
-bool EntityItem::deserializeActions(QByteArray allActionsData, EntitySimulation* simulation) const {
- bool success = true;
- QVector serializedActions;
- if (allActionsData.size() > 0) {
- QDataStream serializedActionsStream(allActionsData);
- serializedActionsStream >> serializedActions;
+
+void EntityItem::deserializeActions() {
+ assertUnlocked();
+ lockForWrite();
+ deserializeActionsInternal();
+ unlock();
+}
+
+
+void EntityItem::deserializeActionsInternal() {
+ assertWriteLocked();
+
+ if (!_element) {
+ return;
}
// Keep track of which actions got added or updated by the new actionData
- QSet updated;
EntityTree* entityTree = _element ? _element->getTree() : nullptr;
- if (!simulation) {
- simulation = entityTree ? entityTree->getSimulation() : nullptr;
+ assert(entityTree);
+ EntitySimulation* simulation = entityTree ? entityTree->getSimulation() : nullptr;
+ assert(simulation);
+
+ QVector serializedActions;
+ if (_allActionsDataCache.size() > 0) {
+ QDataStream serializedActionsStream(_allActionsDataCache);
+ serializedActionsStream >> serializedActions;
}
- if (simulation && entityTree) {
- foreach(QByteArray serializedAction, serializedActions) {
- QDataStream serializedActionStream(serializedAction);
- EntityActionType actionType;
- QUuid actionID;
- serializedActionStream >> actionType;
- serializedActionStream >> actionID;
- updated << actionID;
+ QSet updated;
- if (_objectActions.contains(actionID)) {
- EntityActionPointer action = _objectActions[actionID];
- // TODO: make sure types match? there isn't currently a way to
- // change the type of an existing action.
- action->deserialize(serializedAction);
- } else {
- auto actionFactory = DependencyManager::get();
- if (simulation) {
- EntityItemPointer entity = entityTree->findEntityByEntityItemID(_id);
- EntityActionPointer action = actionFactory->factoryBA(simulation, entity, serializedAction);
- if (action) {
- entity->addActionInternal(simulation, action);
- }
- } else {
- // we can't yet add the action. This method will be called later.
- success = false;
- }
+ foreach(QByteArray serializedAction, serializedActions) {
+ QDataStream serializedActionStream(serializedAction);
+ EntityActionType actionType;
+ QUuid actionID;
+ serializedActionStream >> actionType;
+ serializedActionStream >> actionID;
+ updated << actionID;
+
+ if (_objectActions.contains(actionID)) {
+ EntityActionPointer action = _objectActions[actionID];
+ // TODO: make sure types match? there isn't currently a way to
+ // change the type of an existing action.
+ action->deserialize(serializedAction);
+ } else {
+ auto actionFactory = DependencyManager::get();
+
+ // EntityItemPointer entity = entityTree->findEntityByEntityItemID(_id, false);
+ EntityItemPointer entity = shared_from_this();
+ EntityActionPointer action = actionFactory->factoryBA(entity, serializedAction);
+ if (action) {
+ entity->addActionInternal(simulation, action);
}
}
+ }
- // remove any actions that weren't included in the new data.
- QHash::const_iterator i = _objectActions.begin();
- while (i != _objectActions.end()) {
- const QUuid id = i.key();
- if (!updated.contains(id)) {
- _actionsToRemove << id;
- }
- i++;
+ // remove any actions that weren't included in the new data.
+ QHash::const_iterator i = _objectActions.begin();
+ while (i != _objectActions.end()) {
+ QUuid id = i.key();
+ if (!updated.contains(id)) {
+ _actionsToRemove << id;
}
- } else {
- // no simulation
- success = false;
+ i++;
}
- return success;
-}
-
-bool EntityItem::checkWaitingActionData(EntitySimulation* simulation) const {
- if (_waitingActionData.size() == 0) {
- return true;
- }
- bool success = deserializeActions(_waitingActionData, simulation);
- if (success) {
- _waitingActionData.clear();
- }
- return success;
+ return;
}
void EntityItem::checkWaitingToRemove(EntitySimulation* simulation) {
+ assertLocked();
foreach(QUuid actionID, _actionsToRemove) {
removeActionInternal(actionID, simulation);
}
@@ -1600,21 +1676,22 @@ void EntityItem::checkWaitingToRemove(EntitySimulation* simulation) {
}
void EntityItem::setActionData(QByteArray actionData) {
+ assertUnlocked();
+ lockForWrite();
+ setActionDataInternal(actionData);
+ unlock();
+}
+
+void EntityItem::setActionDataInternal(QByteArray actionData) {
+ assertWriteLocked();
checkWaitingToRemove();
- bool success = deserializeActions(actionData);
_allActionsDataCache = actionData;
- if (success) {
- _waitingActionData.clear();
- } else {
- _waitingActionData = actionData;
- }
+ deserializeActionsInternal();
}
QByteArray EntityItem::serializeActions(bool& success) const {
+ assertLocked();
QByteArray result;
- if (!checkWaitingActionData()) {
- return _waitingActionData;
- }
if (_objectActions.size() == 0) {
success = true;
@@ -1643,21 +1720,132 @@ QByteArray EntityItem::serializeActions(bool& success) const {
return result;
}
-const QByteArray EntityItem::getActionData() const {
+const QByteArray EntityItem::getActionDataInternal() const {
+ if (_actionDataDirty) {
+ bool success;
+ QByteArray newDataCache = serializeActions(success);
+ if (success) {
+ _allActionsDataCache = newDataCache;
+ }
+ _actionDataDirty = false;
+ }
return _allActionsDataCache;
}
+const QByteArray EntityItem::getActionData() const {
+ assertUnlocked();
+ lockForRead();
+ auto result = getActionDataInternal();
+ unlock();
+ return result;
+}
+
QVariantMap EntityItem::getActionArguments(const QUuid& actionID) const {
QVariantMap result;
-
- if (!checkWaitingActionData()) {
- return result;
- }
+ lockForRead();
if (_objectActions.contains(actionID)) {
EntityActionPointer action = _objectActions[actionID];
result = action->getArguments();
result["type"] = EntityActionInterface::actionTypeToString(action->getType());
}
+ unlock();
return result;
}
+
+
+
+#define ENABLE_LOCKING 1
+
+#ifdef ENABLE_LOCKING
+void EntityItem::lockForRead() const {
+ _lock.lockForRead();
+}
+
+bool EntityItem::tryLockForRead() const {
+ return _lock.tryLockForRead();
+}
+
+void EntityItem::lockForWrite() const {
+ _lock.lockForWrite();
+}
+
+bool EntityItem::tryLockForWrite() const {
+ return _lock.tryLockForWrite();
+}
+
+void EntityItem::unlock() const {
+ _lock.unlock();
+}
+
+bool EntityItem::isLocked() const {
+ bool readSuccess = tryLockForRead();
+ if (readSuccess) {
+ unlock();
+ }
+ bool writeSuccess = tryLockForWrite();
+ if (writeSuccess) {
+ unlock();
+ }
+ if (readSuccess && writeSuccess) {
+ return false; // if we can take both kinds of lock, there was no previous lock
+ }
+ return true; // either read or write failed, so there is some lock in place.
+}
+
+
+bool EntityItem::isWriteLocked() const {
+ bool readSuccess = tryLockForRead();
+ if (readSuccess) {
+ unlock();
+ return false;
+ }
+ bool writeSuccess = tryLockForWrite();
+ if (writeSuccess) {
+ unlock();
+ return false;
+ }
+ return true; // either read or write failed, so there is some lock in place.
+}
+
+
+bool EntityItem::isUnlocked() const {
+ // this can't be sure -- this may get unlucky and hit locks from other threads. what we're actually trying
+ // to discover is if *this* thread hasn't locked the EntityItem. Try repeatedly to take both kinds of lock.
+ bool readSuccess = false;
+ for (int i=0; i<80; i++) {
+ readSuccess = tryLockForRead();
+ if (readSuccess) {
+ unlock();
+ break;
+ }
+ QThread::usleep(200);
+ }
+
+ bool writeSuccess = false;
+ if (readSuccess) {
+ for (int i=0; i<80; i++) {
+ writeSuccess = tryLockForWrite();
+ if (writeSuccess) {
+ unlock();
+ break;
+ }
+ QThread::usleep(300);
+ }
+ }
+
+ if (readSuccess && writeSuccess) {
+ return true; // if we can take both kinds of lock, there was no previous lock
+ }
+ return false;
+}
+#else
+void EntityItem::lockForRead() const { }
+bool EntityItem::tryLockForRead() const { return true; }
+void EntityItem::lockForWrite() const { }
+bool EntityItem::tryLockForWrite() const { return true; }
+void EntityItem::unlock() const { }
+bool EntityItem::isLocked() const { return true; }
+bool EntityItem::isWriteLocked() const { return true; }
+bool EntityItem::isUnlocked() const { return true; }
+#endif
diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h
index bc8901c6b1..de431446e8 100644
--- a/libraries/entities/src/EntityItem.h
+++ b/libraries/entities/src/EntityItem.h
@@ -68,10 +68,28 @@ const float ACTIVATION_ANGULAR_VELOCITY_DELTA = 0.03f;
#define debugTimeOnly(T) qPrintable(QString("%1").arg(T, 16, 10))
#define debugTreeVector(V) V << "[" << V << " in meters ]"
+#if DEBUG
+ #define assertLocked() assert(isLocked())
+#else
+ #define assertLocked()
+#endif
+
+#if DEBUG
+ #define assertWriteLocked() assert(isWriteLocked())
+#else
+ #define assertWriteLocked()
+#endif
+
+#if DEBUG
+ #define assertUnlocked() assert(isUnlocked())
+#else
+ #define assertUnlocked()
+#endif
+
/// EntityItem class this is the base class for all entity types. It handles the basic properties and functionality available
/// to all other entity types. In particular: postion, size, rotation, age, lifetime, velocity, gravity. You can not instantiate
/// one directly, instead you must only construct one of it's derived classes with additional features.
-class EntityItem {
+class EntityItem : public std::enable_shared_from_this {
// These two classes manage lists of EntityItem pointers and must be able to cleanup pointers when an EntityItem is deleted.
// To make the cleanup robust each EntityItem has backpointers to its manager classes (which are only ever set/cleared by
// the managers themselves, hence they are fiends) whose NULL status can be used to determine which managers still need to
@@ -395,9 +413,14 @@ public:
bool hasActions() { return !_objectActions.empty(); }
QList getActionIDs() { return _objectActions.keys(); }
QVariantMap getActionArguments(const QUuid& actionID) const;
+ void deserializeActions();
+ void setActionDataDirty(bool value) const { _actionDataDirty = value; }
protected:
+ const QByteArray getActionDataInternal() const;
+ void setActionDataInternal(QByteArray actionData);
+
static bool _sendPhysicsUpdates;
EntityTypes::EntityType _type;
QUuid _id;
@@ -470,18 +493,28 @@ protected:
bool addActionInternal(EntitySimulation* simulation, EntityActionPointer action);
bool removeActionInternal(const QUuid& actionID, EntitySimulation* simulation = nullptr);
- bool deserializeActions(QByteArray allActionsData, EntitySimulation* simulation = nullptr) const;
+ void deserializeActionsInternal();
QByteArray serializeActions(bool& success) const;
QHash _objectActions;
+
static int _maxActionsDataSize;
mutable QByteArray _allActionsDataCache;
// when an entity-server starts up, EntityItem::setActionData is called before the entity-tree is
// ready. This means we can't find our EntityItemPointer or add the action to the simulation. These
// are used to keep track of and work around this situation.
- bool checkWaitingActionData(EntitySimulation* simulation = nullptr) const;
void checkWaitingToRemove(EntitySimulation* simulation = nullptr);
- mutable QByteArray _waitingActionData;
mutable QSet _actionsToRemove;
+ mutable bool _actionDataDirty = false;
+
+ mutable QReadWriteLock _lock;
+ void lockForRead() const;
+ bool tryLockForRead() const;
+ void lockForWrite() const;
+ bool tryLockForWrite() const;
+ void unlock() const;
+ bool isLocked() const;
+ bool isWriteLocked() const;
+ bool isUnlocked() const;
};
#endif // hifi_EntityItem_h
diff --git a/libraries/entities/src/EntityItemID.cpp b/libraries/entities/src/EntityItemID.cpp
index 4e66f5d156..d882559ee3 100644
--- a/libraries/entities/src/EntityItemID.cpp
+++ b/libraries/entities/src/EntityItemID.cpp
@@ -11,7 +11,7 @@
#include
#include
-
+#include
#include
#include "RegisteredMetaTypes.h"
@@ -33,11 +33,8 @@ EntityItemID::EntityItemID(const QUuid& id) : QUuid(id)
EntityItemID EntityItemID::readEntityItemIDFromBuffer(const unsigned char* data, int bytesLeftToRead) {
EntityItemID result;
-
if (bytesLeftToRead >= NUM_BYTES_RFC4122_UUID) {
- // id
- QByteArray encodedID((const char*)data, NUM_BYTES_RFC4122_UUID);
- result = QUuid::fromRfc4122(encodedID);
+ BufferParser(data, bytesLeftToRead).readUuid(result);
}
return result;
}
diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp
index 9a1a5494b7..106e0a6b8b 100644
--- a/libraries/entities/src/EntityItemProperties.cpp
+++ b/libraries/entities/src/EntityItemProperties.cpp
@@ -128,6 +128,10 @@ void EntityItemProperties::setSittingPoints(const QVector& sitting
}
}
+bool EntityItemProperties::animationSettingsChanged() const {
+ return _animationSettingsChanged;
+}
+
void EntityItemProperties::setAnimationSettings(const QString& value) {
// the animations setting is a JSON string that may contain various animation settings.
// if it includes fps, frameIndex, or running, those values will be parsed out and
diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp
index 7cc2c03dfc..18175da1f6 100644
--- a/libraries/entities/src/EntityScriptingInterface.cpp
+++ b/libraries/entities/src/EntityScriptingInterface.cpp
@@ -543,6 +543,9 @@ bool EntityScriptingInterface::actionWorker(const QUuid& entityID,
}
bool success = actor(simulation, entity);
+ if (success) {
+ _entityTree->entityChanged(entity);
+ }
_entityTree->unlock();
// transmit the change
@@ -574,7 +577,7 @@ QUuid EntityScriptingInterface::addAction(const QString& actionTypeString,
if (actionType == ACTION_TYPE_NONE) {
return false;
}
- EntityActionPointer action = actionFactory->factory(simulation, actionType, actionID, entity, arguments);
+ EntityActionPointer action = actionFactory->factory(actionType, actionID, entity, arguments);
if (action) {
entity->addAction(simulation, action);
auto nodeList = DependencyManager::get();
diff --git a/libraries/entities/src/EntitySimulation.cpp b/libraries/entities/src/EntitySimulation.cpp
index a2d20fe5d5..f2bd1e873e 100644
--- a/libraries/entities/src/EntitySimulation.cpp
+++ b/libraries/entities/src/EntitySimulation.cpp
@@ -146,6 +146,7 @@ void EntitySimulation::sortEntitiesThatMoved() {
void EntitySimulation::addEntity(EntityItemPointer entity) {
assert(entity);
+ entity->deserializeActions();
if (entity->isMortal()) {
_mortalEntities.insert(entity);
quint64 expiry = entity->getExpiry();
diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp
index 3d7fcd8ce5..a951b2bf4f 100644
--- a/libraries/entities/src/EntityTree.cpp
+++ b/libraries/entities/src/EntityTree.cpp
@@ -92,13 +92,11 @@ void EntityTree::postAddEntity(EntityItemPointer entity) {
bool EntityTree::updateEntity(const EntityItemID& entityID, const EntityItemProperties& properties, const SharedNodePointer& senderNode) {
EntityTreeElement* containingElement = getContainingElement(entityID);
if (!containingElement) {
- qCDebug(entities) << "UNEXPECTED!!!! EntityTree::updateEntity() entityID doesn't exist!!! entityID=" << entityID;
return false;
}
EntityItemPointer existingEntity = containingElement->getEntityWithEntityItemID(entityID);
if (!existingEntity) {
- qCDebug(entities) << "UNEXPECTED!!!! don't call updateEntity() on entity items that don't exist. entityID=" << entityID;
return false;
}
@@ -108,8 +106,6 @@ bool EntityTree::updateEntity(const EntityItemID& entityID, const EntityItemProp
bool EntityTree::updateEntity(EntityItemPointer entity, const EntityItemProperties& properties, const SharedNodePointer& senderNode) {
EntityTreeElement* containingElement = getContainingElement(entity->getEntityItemID());
if (!containingElement) {
- qCDebug(entities) << "UNEXPECTED!!!! EntityTree::updateEntity() entity-->element lookup failed!!! entityID="
- << entity->getEntityItemID();
return false;
}
return updateEntityWithElement(entity, properties, containingElement, senderNode);
diff --git a/libraries/gpu/src/gpu/Batch.cpp b/libraries/gpu/src/gpu/Batch.cpp
index ee028e79e6..5fbac3bc6c 100644
--- a/libraries/gpu/src/gpu/Batch.cpp
+++ b/libraries/gpu/src/gpu/Batch.cpp
@@ -12,6 +12,17 @@
#include
+#if defined(NSIGHT_FOUND)
+#include "nvToolsExt.h"
+
+ProfileRange::ProfileRange(const char *name) {
+ nvtxRangePush(name);
+}
+ProfileRange::~ProfileRange() {
+ nvtxRangePop();
+}
+#endif
+
#define ADD_COMMAND(call) _commands.push_back(COMMAND_##call); _commandOffsets.push_back(_params.size());
using namespace gpu;
@@ -103,6 +114,23 @@ void Batch::clearFramebuffer(Framebuffer::Masks targets, const Vec4& color, floa
_params.push_back(targets);
}
+void Batch::clearColorFramebuffer(Framebuffer::Masks targets, const Vec4& color) {
+ clearFramebuffer(targets & Framebuffer::BUFFER_COLORS, color, 1.0f, 0);
+}
+
+void Batch::clearDepthFramebuffer(float depth) {
+ clearFramebuffer(Framebuffer::BUFFER_DEPTH, Vec4(0.0f), depth, 0);
+}
+
+void Batch::clearStencilFramebuffer(int stencil) {
+ clearFramebuffer(Framebuffer::BUFFER_STENCIL, Vec4(0.0f), 1.0f, stencil);
+}
+
+void Batch::clearDepthStencilFramebuffer(float depth, int stencil) {
+ clearFramebuffer(Framebuffer::BUFFER_DEPTHSTENCIL, Vec4(0.0f), depth, stencil);
+}
+
+
void Batch::setInputFormat(const Stream::FormatPointer& format) {
ADD_COMMAND(setInputFormat);
@@ -141,6 +169,10 @@ void Batch::setIndexBuffer(Type type, const BufferPointer& buffer, Offset offset
_params.push_back(type);
}
+void Batch::setIndexBuffer(const BufferView& buffer) {
+ setIndexBuffer(buffer._element.getType(), buffer._buffer, buffer._offset);
+}
+
void Batch::setModelTransform(const Transform& model) {
ADD_COMMAND(setModelTransform);
@@ -159,10 +191,10 @@ void Batch::setProjectionTransform(const Mat4& proj) {
_params.push_back(cacheData(sizeof(Mat4), &proj));
}
-void Batch::setViewportTransform(const Vec4i& viewport) {
- ADD_COMMAND(setViewportTransform);
-
- _params.push_back(cacheData(sizeof(Vec4i), &viewport));
+void Batch::setViewportTransform(const Vec4i& viewport) {
+ ADD_COMMAND(setViewportTransform);
+
+ _params.push_back(cacheData(sizeof(Vec4i), &viewport));
}
void Batch::setPipeline(const PipelinePointer& pipeline) {
@@ -195,21 +227,38 @@ void Batch::setUniformBuffer(uint32 slot, const BufferView& view) {
}
-void Batch::setUniformTexture(uint32 slot, const TexturePointer& texture) {
- ADD_COMMAND(setUniformTexture);
+void Batch::setResourceTexture(uint32 slot, const TexturePointer& texture) {
+ ADD_COMMAND(setResourceTexture);
_params.push_back(_textures.cache(texture));
_params.push_back(slot);
}
-void Batch::setUniformTexture(uint32 slot, const TextureView& view) {
- setUniformTexture(slot, view._texture);
+void Batch::setResourceTexture(uint32 slot, const TextureView& view) {
+ setResourceTexture(slot, view._texture);
}
void Batch::setFramebuffer(const FramebufferPointer& framebuffer) {
- ADD_COMMAND(setUniformTexture);
+ ADD_COMMAND(setFramebuffer);
_params.push_back(_framebuffers.cache(framebuffer));
}
+void Batch::beginQuery(const QueryPointer& query) {
+ ADD_COMMAND(beginQuery);
+
+ _params.push_back(_queries.cache(query));
+}
+
+void Batch::endQuery(const QueryPointer& query) {
+ ADD_COMMAND(endQuery);
+
+ _params.push_back(_queries.cache(query));
+}
+
+void Batch::getQuery(const QueryPointer& query) {
+ ADD_COMMAND(getQuery);
+
+ _params.push_back(_queries.cache(query));
+}
diff --git a/libraries/gpu/src/gpu/Batch.h b/libraries/gpu/src/gpu/Batch.h
index 835e872b4a..abd9982cd0 100644
--- a/libraries/gpu/src/gpu/Batch.h
+++ b/libraries/gpu/src/gpu/Batch.h
@@ -18,6 +18,7 @@
#include
+#include "Query.h"
#include "Stream.h"
#include "Texture.h"
@@ -26,17 +27,11 @@
#include "Framebuffer.h"
#if defined(NSIGHT_FOUND)
- #include "nvToolsExt.h"
class ProfileRange {
public:
- ProfileRange(const char *name) {
- nvtxRangePush(name);
- }
- ~ProfileRange() {
- nvtxRangePop();
- }
+ ProfileRange(const char *name);
+ ~ProfileRange();
};
-
#define PROFILE_RANGE(name) ProfileRange profileRangeThis(name);
#else
#define PROFILE_RANGE(name)
@@ -44,19 +39,6 @@
namespace gpu {
-enum Primitive {
- POINTS = 0,
- LINES,
- LINE_STRIP,
- TRIANGLES,
- TRIANGLE_STRIP,
- TRIANGLE_FAN,
- QUADS,
- QUAD_STRIP,
-
- NUM_PRIMITIVES,
-};
-
enum ReservedSlot {
/* TRANSFORM_OBJECT_SLOT = 6,
TRANSFORM_CAMERA_SLOT = 7,
@@ -82,7 +64,12 @@ public:
void drawIndexedInstanced(uint32 nbInstances, Primitive primitiveType, uint32 nbIndices, uint32 startIndex = 0, uint32 startInstance = 0);
// Clear framebuffer layers
+ // Targets can be any of the render buffers contained in the Framebuffer
void clearFramebuffer(Framebuffer::Masks targets, const Vec4& color, float depth, int stencil);
+ void clearColorFramebuffer(Framebuffer::Masks targets, const Vec4& color); // not a command, just a shortcut for clearFramebuffer, mask out targets to make sure it touches only color targets
+ void clearDepthFramebuffer(float depth); // not a command, just a shortcut for clearFramebuffer, it touches only depth target
+ void clearStencilFramebuffer(int stencil); // not a command, just a shortcut for clearFramebuffer, it touches only stencil target
+ void clearDepthStencilFramebuffer(float depth, int stencil); // not a command, just a shortcut for clearFramebuffer, it touches depth and stencil target
// Input Stage
// InputFormat
@@ -95,6 +82,7 @@ public:
void setInputStream(Slot startChannel, const BufferStream& stream); // not a command, just unroll into a loop of setInputBuffer
void setIndexBuffer(Type type, const BufferPointer& buffer, Offset offset);
+ void setIndexBuffer(const BufferView& buffer); // not a command, just a shortcut from a BufferView
// Transform Stage
// Vertex position is transformed by ModelTransform from object space to world space
@@ -115,12 +103,17 @@ public:
void setUniformBuffer(uint32 slot, const BufferPointer& buffer, Offset offset, Offset size);
void setUniformBuffer(uint32 slot, const BufferView& view); // not a command, just a shortcut from a BufferView
- void setUniformTexture(uint32 slot, const TexturePointer& view);
- void setUniformTexture(uint32 slot, const TextureView& view); // not a command, just a shortcut from a TextureView
+ void setResourceTexture(uint32 slot, const TexturePointer& view);
+ void setResourceTexture(uint32 slot, const TextureView& view); // not a command, just a shortcut from a TextureView
// Framebuffer Stage
void setFramebuffer(const FramebufferPointer& framebuffer);
+ // Query Section
+ void beginQuery(const QueryPointer& query);
+ void endQuery(const QueryPointer& query);
+ void getQuery(const QueryPointer& query);
+
// TODO: As long as we have gl calls explicitely issued from interface
// code, we need to be able to record and batch these calls. THe long
// term strategy is to get rid of any GL calls in favor of the HIFI GPU API
@@ -185,10 +178,14 @@ public:
COMMAND_setStateBlendFactor,
COMMAND_setUniformBuffer,
- COMMAND_setUniformTexture,
+ COMMAND_setResourceTexture,
COMMAND_setFramebuffer,
+ COMMAND_beginQuery,
+ COMMAND_endQuery,
+ COMMAND_getQuery,
+
// TODO: As long as we have gl calls explicitely issued from interface
// code, we need to be able to record and batch these calls. THe long
// term strategy is to get rid of any GL calls in favor of the HIFI GPU API
@@ -292,6 +289,7 @@ public:
typedef Cache::Vector TransformCaches;
typedef Cache::Vector PipelineCaches;
typedef Cache::Vector FramebufferCaches;
+ typedef Cache::Vector QueryCaches;
// Cache Data in a byte array if too big to fit in Param
// FOr example Mat4s are going there
@@ -316,6 +314,7 @@ public:
TransformCaches _transforms;
PipelineCaches _pipelines;
FramebufferCaches _framebuffers;
+ QueryCaches _queries;
protected:
};
diff --git a/libraries/gpu/src/gpu/Context.cpp b/libraries/gpu/src/gpu/Context.cpp
index 9cc6bb3cd7..51335f78df 100644
--- a/libraries/gpu/src/gpu/Context.cpp
+++ b/libraries/gpu/src/gpu/Context.cpp
@@ -33,9 +33,11 @@ bool Context::makeProgram(Shader& shader, const Shader::BindingSet& bindings) {
}
void Context::render(Batch& batch) {
+ PROFILE_RANGE(__FUNCTION__);
_backend->render(batch);
}
void Context::syncCache() {
+ PROFILE_RANGE(__FUNCTION__);
_backend->syncCache();
}
\ No newline at end of file
diff --git a/libraries/gpu/src/gpu/Context.h b/libraries/gpu/src/gpu/Context.h
index 98ddc7fb64..4eb0976e3c 100644
--- a/libraries/gpu/src/gpu/Context.h
+++ b/libraries/gpu/src/gpu/Context.h
@@ -42,7 +42,7 @@ public:
Mat4 _projectionViewUntranslated;
Mat4 _projection;
Mat4 _projectionInverse;
- Vec4 _viewport;
+ Vec4 _viewport; // Public value is int but float in the shader to stay in floats for all the transform computations.
};
template< typename T >
@@ -99,6 +99,15 @@ public:
return reinterpret_cast(framebuffer.getGPUObject());
}
+ template< typename T >
+ static void setGPUObject(const Query& query, T* object) {
+ query.setGPUObject(object);
+ }
+ template< typename T >
+ static T* getGPUObject(const Query& query) {
+ return reinterpret_cast(query.getGPUObject());
+ }
+
protected:
};
diff --git a/libraries/gpu/src/gpu/DrawTexture.slf b/libraries/gpu/src/gpu/DrawTexture.slf
new file mode 100755
index 0000000000..e456c49649
--- /dev/null
+++ b/libraries/gpu/src/gpu/DrawTexture.slf
@@ -0,0 +1,21 @@
+<@include gpu/Config.slh@>
+<$VERSION_HEADER$>
+// Generated on <$_SCRIBE_DATE$>
+//
+// Draw texture 0 fetched at texcoord.xy
+//
+// Created by Sam Gateau on 6/22/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
+//
+
+
+uniform sampler2D colorMap;
+
+varying vec2 varTexcoord;
+
+void main(void) {
+ gl_FragColor = texture2D(colorMap, varTexcoord);
+}
diff --git a/libraries/gpu/src/gpu/DrawTransformUnitQuad.slv b/libraries/gpu/src/gpu/DrawTransformUnitQuad.slv
new file mode 100755
index 0000000000..2d1e4584a7
--- /dev/null
+++ b/libraries/gpu/src/gpu/DrawTransformUnitQuad.slv
@@ -0,0 +1,36 @@
+<@include gpu/Config.slh@>
+<$VERSION_HEADER$>
+// Generated on <$_SCRIBE_DATE$>
+//
+// Draw and transform the unit quad [-1,-1 -> 1,1]
+// Simply draw a Triangle_strip of 2 triangles, no input buffers or index buffer needed
+//
+// Created by Sam Gateau on 6/22/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
+//
+
+<@include gpu/Transform.slh@>
+
+<$declareStandardTransform()$>
+
+varying vec2 varTexcoord;
+
+void main(void) {
+ const vec4 UNIT_QUAD[4] = vec4[4](
+ vec4(-1.0, -1.0, 0.0, 1.0),
+ vec4(1.0, -1.0, 0.0, 1.0),
+ vec4(-1.0, 1.0, 0.0, 1.0),
+ vec4(1.0, 1.0, 0.0, 1.0)
+ );
+ vec4 pos = UNIT_QUAD[gl_VertexID];
+
+ // standard transform
+ TransformCamera cam = getTransformCamera();
+ TransformObject obj = getTransformObject();
+ <$transformModelToClipPos(cam, obj, pos, gl_Position)$>
+
+ varTexcoord = (pos.xy + 1) * 0.5;
+}
diff --git a/libraries/gpu/src/gpu/DrawViewportQuadTransformTexcoord.slv b/libraries/gpu/src/gpu/DrawViewportQuadTransformTexcoord.slv
new file mode 100755
index 0000000000..e91b8a7644
--- /dev/null
+++ b/libraries/gpu/src/gpu/DrawViewportQuadTransformTexcoord.slv
@@ -0,0 +1,38 @@
+<@include gpu/Config.slh@>
+<$VERSION_HEADER$>
+// Generated on <$_SCRIBE_DATE$>
+//
+// Draw the unit quad [-1,-1 -> 1,1] filling in
+// Simply draw a Triangle_strip of 2 triangles, no input buffers or index buffer needed
+//
+// Created by Sam Gateau on 6/22/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
+//
+
+<@include gpu/Transform.slh@>
+
+<$declareStandardTransform()$>
+
+varying vec2 varTexcoord;
+
+void main(void) {
+ const vec4 UNIT_QUAD[4] = vec4[4](
+ vec4(-1.0, -1.0, 0.0, 1.0),
+ vec4(1.0, -1.0, 0.0, 1.0),
+ vec4(-1.0, 1.0, 0.0, 1.0),
+ vec4(1.0, 1.0, 0.0, 1.0)
+ );
+ vec4 pos = UNIT_QUAD[gl_VertexID];
+
+ // standard transform but applied to the Texcoord
+ vec4 tc = vec4((pos.xy + 1) * 0.5, pos.zw);
+
+ TransformObject obj = getTransformObject();
+ <$transformModelToWorldPos(obj, tc, tc)$>
+
+ gl_Position = pos;
+ varTexcoord = tc.xy;
+}
diff --git a/libraries/gpu/src/gpu/Format.h b/libraries/gpu/src/gpu/Format.h
index ac71cc7940..981a560965 100644
--- a/libraries/gpu/src/gpu/Format.h
+++ b/libraries/gpu/src/gpu/Format.h
@@ -182,6 +182,9 @@ public:
}
static const Element COLOR_RGBA_32;
+ static const Element VEC3F_XYZ;
+ static const Element INDEX_UINT16;
+ static const Element PART_DRAWCALL;
protected:
uint8 _semantic;
@@ -203,6 +206,19 @@ enum ComparisonFunction {
NUM_COMPARISON_FUNCS,
};
+enum Primitive {
+ POINTS = 0,
+ LINES,
+ LINE_STRIP,
+ TRIANGLES,
+ TRIANGLE_STRIP,
+ TRIANGLE_FAN,
+ QUADS,
+ QUAD_STRIP,
+
+ NUM_PRIMITIVES,
+};
+
};
diff --git a/libraries/gpu/src/gpu/GLBackend.cpp b/libraries/gpu/src/gpu/GLBackend.cpp
index 302dc0e8be..b79f506544 100644
--- a/libraries/gpu/src/gpu/GLBackend.cpp
+++ b/libraries/gpu/src/gpu/GLBackend.cpp
@@ -35,10 +35,13 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] =
(&::gpu::GLBackend::do_setStateBlendFactor),
(&::gpu::GLBackend::do_setUniformBuffer),
- (&::gpu::GLBackend::do_setUniformTexture),
+ (&::gpu::GLBackend::do_setResourceTexture),
(&::gpu::GLBackend::do_setFramebuffer),
+ (&::gpu::GLBackend::do_beginQuery),
+ (&::gpu::GLBackend::do_endQuery),
+ (&::gpu::GLBackend::do_getQuery),
(&::gpu::GLBackend::do_glEnable),
(&::gpu::GLBackend::do_glDisable),
@@ -201,7 +204,6 @@ void GLBackend::do_drawInstanced(Batch& batch, uint32 paramOffset) {
GLenum mode = _primitiveToGLmode[primitiveType];
uint32 numVertices = batch._params[paramOffset + 2]._uint;
uint32 startVertex = batch._params[paramOffset + 1]._uint;
- uint32 startInstance = batch._params[paramOffset + 0]._uint;
glDrawArraysInstancedARB(mode, startVertex, numVertices, numInstances);
(void) CHECK_GL_ERROR();
@@ -233,17 +235,34 @@ void GLBackend::do_clearFramebuffer(Batch& batch, uint32 paramOffset) {
glmask |= GL_DEPTH_BUFFER_BIT;
}
+ std::vector drawBuffers;
if (masks & Framebuffer::BUFFER_COLORS) {
- glClearColor(color.x, color.y, color.z, color.w);
- glmask |= GL_COLOR_BUFFER_BIT;
+ for (unsigned int i = 0; i < Framebuffer::MAX_NUM_RENDER_BUFFERS; i++) {
+ if (masks & (1 << i)) {
+ drawBuffers.push_back(GL_COLOR_ATTACHMENT0 + i);
+ }
+ }
+
+ if (!drawBuffers.empty()) {
+ glDrawBuffers(drawBuffers.size(), drawBuffers.data());
+ glClearColor(color.x, color.y, color.z, color.w);
+ glmask |= GL_COLOR_BUFFER_BIT;
+ }
}
glClear(glmask);
+ // Restore the color draw buffers only if a frmaebuffer is bound
+ if (_output._framebuffer && !drawBuffers.empty()) {
+ auto glFramebuffer = syncGPUObject(*_output._framebuffer);
+ if (glFramebuffer) {
+ glDrawBuffers(glFramebuffer->_colorBuffers.size(), glFramebuffer->_colorBuffers.data());
+ }
+ }
+
(void) CHECK_GL_ERROR();
}
-
// TODO: As long as we have gl calls explicitely issued from interface
// code, we need to be able to record and batch these calls. THe long
// term strategy is to get rid of any GL calls in favor of the HIFI GPU API
@@ -598,10 +617,11 @@ void GLBackend::do_glUniform4fv(Batch& batch, uint32 paramOffset) {
return;
}
updatePipeline();
- glUniform4fv(
- batch._params[paramOffset + 2]._int,
- batch._params[paramOffset + 1]._uint,
- (const GLfloat*)batch.editData(batch._params[paramOffset + 0]._uint));
+
+ GLint location = batch._params[paramOffset + 2]._int;
+ GLsizei count = batch._params[paramOffset + 1]._uint;
+ const GLfloat* value = (const GLfloat*)batch.editData(batch._params[paramOffset + 0]._uint);
+ glUniform4fv(location, count, value);
(void) CHECK_GL_ERROR();
}
diff --git a/libraries/gpu/src/gpu/GLBackend.h b/libraries/gpu/src/gpu/GLBackend.h
index d798e9aaac..8df55a7f67 100644
--- a/libraries/gpu/src/gpu/GLBackend.h
+++ b/libraries/gpu/src/gpu/GLBackend.h
@@ -181,6 +181,7 @@ public:
class GLFramebuffer : public GPUObject {
public:
GLuint _fbo = 0;
+ std::vector _colorBuffers;
GLFramebuffer();
~GLFramebuffer();
@@ -188,6 +189,18 @@ public:
static GLFramebuffer* syncGPUObject(const Framebuffer& framebuffer);
static GLuint getFramebufferID(const FramebufferPointer& framebuffer);
+ class GLQuery : public GPUObject {
+ public:
+ GLuint _qo = 0;
+ GLuint64 _result = 0;
+
+ GLQuery();
+ ~GLQuery();
+ };
+ static GLQuery* syncGPUObject(const Query& query);
+ static GLuint getQueryID(const QueryPointer& query);
+
+
static const int MAX_NUM_ATTRIBUTES = Stream::NUM_INPUT_SLOTS;
static const int MAX_NUM_INPUT_BUFFERS = 16;
@@ -198,7 +211,7 @@ public:
void do_setStateFillMode(int32 mode);
void do_setStateCullMode(int32 mode);
void do_setStateFrontFaceClockwise(bool isClockwise);
- void do_setStateDepthClipEnable(bool enable);
+ void do_setStateDepthClampEnable(bool enable);
void do_setStateScissorEnable(bool enable);
void do_setStateMultisampleEnable(bool enable);
void do_setStateAntialiasedLineEnable(bool enable);
@@ -298,6 +311,7 @@ protected:
_model(),
_view(),
_projection(),
+ _viewport(0,0,1,1),
_invalidModel(true),
_invalidView(true),
_invalidProj(false),
@@ -307,7 +321,7 @@ protected:
// Uniform Stage
void do_setUniformBuffer(Batch& batch, uint32 paramOffset);
- void do_setUniformTexture(Batch& batch, uint32 paramOffset);
+ void do_setResourceTexture(Batch& batch, uint32 paramOffset);
struct UniformStageState {
@@ -367,6 +381,11 @@ protected:
OutputStageState() {}
} _output;
+ // Query section
+ void do_beginQuery(Batch& batch, uint32 paramOffset);
+ void do_endQuery(Batch& batch, uint32 paramOffset);
+ void do_getQuery(Batch& batch, uint32 paramOffset);
+
// TODO: As long as we have gl calls explicitely issued from interface
// code, we need to be able to record and batch these calls. THe long
// term strategy is to get rid of any GL calls in favor of the HIFI GPU API
diff --git a/libraries/gpu/src/gpu/GLBackendOutput.cpp b/libraries/gpu/src/gpu/GLBackendOutput.cpp
index 903c97f45b..1a7c5d2281 100755
--- a/libraries/gpu/src/gpu/GLBackendOutput.cpp
+++ b/libraries/gpu/src/gpu/GLBackendOutput.cpp
@@ -40,8 +40,7 @@ GLBackend::GLFramebuffer* GLBackend::syncGPUObject(const Framebuffer& framebuffe
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
- unsigned int nbColorBuffers = 0;
- GLenum colorBuffers[16];
+ std::vector colorBuffers;
if (framebuffer.hasColor()) {
static const GLenum colorAttachments[] = {
GL_COLOR_ATTACHMENT0,
@@ -69,8 +68,7 @@ GLBackend::GLFramebuffer* GLBackend::syncGPUObject(const Framebuffer& framebuffe
if (gltexture) {
glFramebufferTexture2D(GL_FRAMEBUFFER, colorAttachments[unit], GL_TEXTURE_2D, gltexture->_texture, 0);
}
- colorBuffers[nbColorBuffers] = colorAttachments[unit];
- nbColorBuffers++;
+ colorBuffers.push_back(colorAttachments[unit]);
unit++;
}
}
@@ -100,8 +98,8 @@ GLBackend::GLFramebuffer* GLBackend::syncGPUObject(const Framebuffer& framebuffe
}
// Last but not least, define where we draw
- if (nbColorBuffers > 0) {
- glDrawBuffers(nbColorBuffers, colorBuffers);
+ if (!colorBuffers.empty()) {
+ glDrawBuffers(colorBuffers.size(), colorBuffers.data());
} else {
glDrawBuffer( GL_NONE );
}
@@ -139,6 +137,7 @@ GLBackend::GLFramebuffer* GLBackend::syncGPUObject(const Framebuffer& framebuffe
// All is green, assign the gpuobject to the Framebuffer
object = new GLFramebuffer();
object->_fbo = fbo;
+ object->_colorBuffers = colorBuffers;
Backend::setGPUObject(framebuffer, object);
}
@@ -167,4 +166,3 @@ void GLBackend::do_setFramebuffer(Batch& batch, uint32 paramOffset) {
_output._framebuffer = framebuffer;
}
}
-
diff --git a/libraries/gpu/src/gpu/GLBackendPipeline.cpp b/libraries/gpu/src/gpu/GLBackendPipeline.cpp
index a770cf89b3..51a3a24e9b 100755
--- a/libraries/gpu/src/gpu/GLBackendPipeline.cpp
+++ b/libraries/gpu/src/gpu/GLBackendPipeline.cpp
@@ -147,9 +147,9 @@ void GLBackend::updatePipeline() {
#if (GPU_TRANSFORM_PROFILE == GPU_CORE)
#else
- // If shader program needs the model we need to provide it
- if (_pipeline._program_transformObject_model >= 0) {
- glUniformMatrix4fv(_pipeline._program_transformObject_model, 1, false, (const GLfloat*) &_transform._transformObject._model);
+ // If shader program needs the model we need to provide it
+ if (_pipeline._program_transformObject_model >= 0) {
+ glUniformMatrix4fv(_pipeline._program_transformObject_model, 1, false, (const GLfloat*) &_transform._transformObject._model);
}
// If shader program needs the inverseView we need to provide it
@@ -188,7 +188,7 @@ void GLBackend::do_setUniformBuffer(Batch& batch, uint32 paramOffset) {
(void) CHECK_GL_ERROR();
}
-void GLBackend::do_setUniformTexture(Batch& batch, uint32 paramOffset) {
+void GLBackend::do_setResourceTexture(Batch& batch, uint32 paramOffset) {
GLuint slot = batch._params[paramOffset + 1]._uint;
TexturePointer uniformTexture = batch._textures.get(batch._params[paramOffset + 0]._uint);
diff --git a/libraries/gpu/src/gpu/GLBackendQuery.cpp b/libraries/gpu/src/gpu/GLBackendQuery.cpp
new file mode 100644
index 0000000000..39db19dafd
--- /dev/null
+++ b/libraries/gpu/src/gpu/GLBackendQuery.cpp
@@ -0,0 +1,106 @@
+//
+// GLBackendQuery.cpp
+// libraries/gpu/src/gpu
+//
+// Created by Sam Gateau on 7/7/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
+//
+#include "GPULogging.h"
+#include "GLBackendShared.h"
+
+
+using namespace gpu;
+
+GLBackend::GLQuery::GLQuery() {}
+
+GLBackend::GLQuery::~GLQuery() {
+ if (_qo != 0) {
+ glDeleteQueries(1, &_qo);
+ }
+}
+
+GLBackend::GLQuery* GLBackend::syncGPUObject(const Query& query) {
+ GLQuery* object = Backend::getGPUObject(query);
+
+ // If GPU object already created and in sync
+ if (object) {
+ return object;
+ }
+
+ // need to have a gpu object?
+ if (!object) {
+ GLuint qo;
+ glGenQueries(1, &qo);
+ (void) CHECK_GL_ERROR();
+ GLuint64 result = -1;
+
+ // All is green, assign the gpuobject to the Query
+ object = new GLQuery();
+ object->_qo = qo;
+ object->_result = result;
+ Backend::setGPUObject(query, object);
+ }
+
+ return object;
+}
+
+
+
+GLuint GLBackend::getQueryID(const QueryPointer& query) {
+ if (!query) {
+ return 0;
+ }
+ GLQuery* object = GLBackend::syncGPUObject(*query);
+ if (object) {
+ return object->_qo;
+ } else {
+ return 0;
+ }
+}
+
+void GLBackend::do_beginQuery(Batch& batch, uint32 paramOffset) {
+ auto query = batch._queries.get(batch._params[paramOffset]._uint);
+ GLQuery* glquery = syncGPUObject(*query);
+ if (glquery) {
+ #if (GPU_FEATURE_PROFILE == GPU_LEGACY)
+ // (EXT_TIMER_QUERY)
+ glBeginQuery(GL_TIME_ELAPSED_EXT, glquery->_qo);
+ #else
+ glBeginQuery(GL_TIME_ELAPSED, glquery->_qo);
+ #endif
+ (void)CHECK_GL_ERROR();
+ }
+}
+
+void GLBackend::do_endQuery(Batch& batch, uint32 paramOffset) {
+ auto query = batch._queries.get(batch._params[paramOffset]._uint);
+ GLQuery* glquery = syncGPUObject(*query);
+ if (glquery) {
+ #if (GPU_FEATURE_PROFILE == GPU_LEGACY)
+ // (EXT_TIMER_QUERY)
+ glEndQuery(GL_TIME_ELAPSED_EXT);
+ #else
+ glEndQuery(GL_TIME_ELAPSED);
+ #endif
+ (void)CHECK_GL_ERROR();
+ }
+}
+
+void GLBackend::do_getQuery(Batch& batch, uint32 paramOffset) {
+ auto query = batch._queries.get(batch._params[paramOffset]._uint);
+ GLQuery* glquery = syncGPUObject(*query);
+ if (glquery) {
+ #if (GPU_FEATURE_PROFILE == GPU_LEGACY)
+ // (EXT_TIMER_QUERY)
+ #if !defined(Q_OS_LINUX)
+ glGetQueryObjectui64vEXT(glquery->_qo, GL_QUERY_RESULT, &glquery->_result);
+ #endif
+ #else
+ glGetQueryObjectui64v(glquery->_qo, GL_QUERY_RESULT, &glquery->_result);
+ #endif
+ (void)CHECK_GL_ERROR();
+ }
+}
diff --git a/libraries/gpu/src/gpu/GLBackendShader.cpp b/libraries/gpu/src/gpu/GLBackendShader.cpp
index ec02c1333b..7902275c27 100755
--- a/libraries/gpu/src/gpu/GLBackendShader.cpp
+++ b/libraries/gpu/src/gpu/GLBackendShader.cpp
@@ -111,10 +111,10 @@ void makeBindings(GLBackend::GLShader* shader) {
shader->_transformCameraSlot = gpu::TRANSFORM_CAMERA_SLOT;
}
#else
- loc = glGetUniformLocation(glprogram, "transformObject_model");
- if (loc >= 0) {
- shader->_transformObject_model = loc;
- }
+ loc = glGetUniformLocation(glprogram, "transformObject_model");
+ if (loc >= 0) {
+ shader->_transformObject_model = loc;
+ }
loc = glGetUniformLocation(glprogram, "transformCamera_viewInverse");
if (loc >= 0) {
diff --git a/libraries/gpu/src/gpu/GLBackendState.cpp b/libraries/gpu/src/gpu/GLBackendState.cpp
index ef272bb708..e898a29245 100644
--- a/libraries/gpu/src/gpu/GLBackendState.cpp
+++ b/libraries/gpu/src/gpu/GLBackendState.cpp
@@ -51,7 +51,7 @@ const GLBackend::GLState::Commands makeResetStateCommands() {
CommandPointer(new Command1I(&GLBackend::do_setStateFillMode, DEFAULT.fillMode)),
CommandPointer(new Command1I(&GLBackend::do_setStateCullMode, DEFAULT.cullMode)),
CommandPointer(new Command1B(&GLBackend::do_setStateFrontFaceClockwise, DEFAULT.frontFaceClockwise)),
- CommandPointer(new Command1B(&GLBackend::do_setStateDepthClipEnable, DEFAULT.depthClipEnable)),
+ CommandPointer(new Command1B(&GLBackend::do_setStateDepthClampEnable, DEFAULT.depthClampEnable)),
CommandPointer(new Command1B(&GLBackend::do_setStateScissorEnable, DEFAULT.scissorEnable)),
CommandPointer(new Command1B(&GLBackend::do_setStateMultisampleEnable, DEFAULT.multisampleEnable)),
CommandPointer(new Command1B(&GLBackend::do_setStateAntialiasedLineEnable, DEFAULT.antialisedLineEnable)),
@@ -89,8 +89,8 @@ void generateFrontFaceClockwise(GLBackend::GLState::Commands& commands, bool isC
commands.push_back(CommandPointer(new Command1B(&GLBackend::do_setStateFrontFaceClockwise, isClockwise)));
}
-void generateDepthClipEnable(GLBackend::GLState::Commands& commands, bool enable) {
- commands.push_back(CommandPointer(new Command1B(&GLBackend::do_setStateDepthClipEnable, enable)));
+void generateDepthClampEnable(GLBackend::GLState::Commands& commands, bool enable) {
+ commands.push_back(CommandPointer(new Command1B(&GLBackend::do_setStateDepthClampEnable, enable)));
}
void generateScissorEnable(GLBackend::GLState::Commands& commands, bool enable) {
@@ -176,8 +176,8 @@ GLBackend::GLState* GLBackend::syncGPUObject(const State& state) {
generateFrontFaceClockwise(object->_commands, state.isFrontFaceClockwise());
break;
}
- case State::DEPTH_CLIP_ENABLE: {
- generateDepthClipEnable(object->_commands, state.isDepthClipEnable());
+ case State::DEPTH_CLAMP_ENABLE: {
+ generateDepthClampEnable(object->_commands, state.isDepthClampEnable());
break;
}
case State::SCISSOR_ENABLE: {
@@ -373,7 +373,7 @@ void GLBackend::getCurrentGLState(State::Data& state) {
GLint winding;
glGetIntegerv(GL_FRONT_FACE, &winding);
state.frontFaceClockwise = (winding == GL_CW);
- state.depthClipEnable = glIsEnabled(GL_DEPTH_CLAMP);
+ state.depthClampEnable = glIsEnabled(GL_DEPTH_CLAMP);
state.scissorEnable = glIsEnabled(GL_SCISSOR_TEST);
state.multisampleEnable = glIsEnabled(GL_MULTISAMPLE);
state.antialisedLineEnable = glIsEnabled(GL_LINE_SMOOTH);
@@ -533,8 +533,8 @@ void GLBackend::do_setStateFrontFaceClockwise(bool isClockwise) {
}
}
-void GLBackend::do_setStateDepthClipEnable(bool enable) {
- if (_pipeline._stateCache.depthClipEnable != enable) {
+void GLBackend::do_setStateDepthClampEnable(bool enable) {
+ if (_pipeline._stateCache.depthClampEnable != enable) {
if (enable) {
glEnable(GL_DEPTH_CLAMP);
} else {
@@ -542,7 +542,7 @@ void GLBackend::do_setStateDepthClipEnable(bool enable) {
}
(void) CHECK_GL_ERROR();
- _pipeline._stateCache.depthClipEnable = enable;
+ _pipeline._stateCache.depthClampEnable = enable;
}
}
@@ -589,7 +589,7 @@ void GLBackend::do_setStateAntialiasedLineEnable(bool enable) {
void GLBackend::do_setStateDepthBias(Vec2 bias) {
if ( (bias.x != _pipeline._stateCache.depthBias) || (bias.y != _pipeline._stateCache.depthBiasSlopeScale)) {
- if ((bias.x != 0.f) || (bias.y != 0.f)) {
+ if ((bias.x != 0.0f) || (bias.y != 0.0f)) {
glEnable(GL_POLYGON_OFFSET_FILL);
glEnable(GL_POLYGON_OFFSET_LINE);
glEnable(GL_POLYGON_OFFSET_POINT);
diff --git a/libraries/gpu/src/gpu/GLBackendTransform.cpp b/libraries/gpu/src/gpu/GLBackendTransform.cpp
index 21a2d57271..48a42fe5f1 100755
--- a/libraries/gpu/src/gpu/GLBackendTransform.cpp
+++ b/libraries/gpu/src/gpu/GLBackendTransform.cpp
@@ -15,7 +15,6 @@
using namespace gpu;
// Transform Stage
-
void GLBackend::do_setModelTransform(Batch& batch, uint32 paramOffset) {
_transform._model = batch._transforms.get(batch._params[paramOffset]._uint);
_transform._invalidModel = true;
@@ -31,11 +30,10 @@ void GLBackend::do_setProjectionTransform(Batch& batch, uint32 paramOffset) {
_transform._invalidProj = true;
}
-void GLBackend::do_setViewportTransform(Batch& batch, uint32 paramOffset) {
- memcpy(&_transform._viewport, batch.editData(batch._params[paramOffset]._uint), sizeof(Vec4i));
- _transform._invalidViewport = true;
-}
-
+void GLBackend::do_setViewportTransform(Batch& batch, uint32 paramOffset) {
+ memcpy(&_transform._viewport, batch.editData(batch._params[paramOffset]._uint), sizeof(Vec4i));
+ _transform._invalidViewport = true;
+}
void GLBackend::initTransform() {
#if (GPU_TRANSFORM_PROFILE == GPU_CORE)
@@ -68,7 +66,7 @@ void GLBackend::syncTransformStateCache() {
_transform._invalidView = true;
_transform._invalidModel = true;
- glGetIntegerv(GL_VIEWPORT, (GLint*) &_transform._viewport);
+ glGetIntegerv(GL_VIEWPORT, (GLint*) &_transform._viewport);
GLint currentMode;
glGetIntegerv(GL_MATRIX_MODE, ¤tMode);
@@ -87,11 +85,11 @@ void GLBackend::updateTransform() {
GLint originalMatrixMode;
glGetIntegerv(GL_MATRIX_MODE, &originalMatrixMode);
// Check all the dirty flags and update the state accordingly
- if (_transform._invalidViewport) {
- _transform._transformCamera._viewport = glm::vec4(_transform._viewport);
-
- // Where we assign the GL viewport
- glViewport(_transform._viewport.x, _transform._viewport.y, _transform._viewport.z, _transform._viewport.w);
+ if (_transform._invalidViewport) {
+ _transform._transformCamera._viewport = glm::vec4(_transform._viewport);
+
+ // Where we assign the GL viewport
+ glViewport(_transform._viewport.x, _transform._viewport.y, _transform._viewport.z, _transform._viewport.w);
}
if (_transform._invalidProj) {
@@ -116,18 +114,18 @@ void GLBackend::updateTransform() {
}
#if (GPU_TRANSFORM_PROFILE == GPU_CORE)
- if (_transform._invalidView || _transform._invalidProj || _transform._invalidViewport) {
+ if (_transform._invalidView || _transform._invalidProj || _transform._invalidViewport) {
glBindBufferBase(GL_UNIFORM_BUFFER, TRANSFORM_CAMERA_SLOT, 0);
glBindBuffer(GL_ARRAY_BUFFER, _transform._transformCameraBuffer);
- glBufferData(GL_ARRAY_BUFFER, sizeof(_transform._transformCamera), (const void*) &_transform._transformCamera, GL_DYNAMIC_DRAW);
+ glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(_transform._transformCamera), (const void*)&_transform._transformCamera);
glBindBuffer(GL_ARRAY_BUFFER, 0);
CHECK_GL_ERROR();
- }
+ }
if (_transform._invalidModel) {
glBindBufferBase(GL_UNIFORM_BUFFER, TRANSFORM_OBJECT_SLOT, 0);
glBindBuffer(GL_ARRAY_BUFFER, _transform._transformObjectBuffer);
- glBufferData(GL_ARRAY_BUFFER, sizeof(_transform._transformObject), (const void*) &_transform._transformObject, GL_DYNAMIC_DRAW);
+ glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(_transform._transformObject), (const void*) &_transform._transformObject);
glBindBuffer(GL_ARRAY_BUFFER, 0);
CHECK_GL_ERROR();
}
diff --git a/libraries/gpu/src/gpu/Query.cpp b/libraries/gpu/src/gpu/Query.cpp
new file mode 100644
index 0000000000..b8ed729c99
--- /dev/null
+++ b/libraries/gpu/src/gpu/Query.cpp
@@ -0,0 +1,27 @@
+//
+// Query.cpp
+// interface/src/gpu
+//
+// Created by Niraj Venkat on 7/7/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
+//
+#include "Query.h"
+
+#include
+
+using namespace gpu;
+
+Query::Query()
+{
+}
+
+Query::~Query()
+{
+}
+
+double Query::getElapsedTime() {
+ return 0.0;
+}
diff --git a/libraries/gpu/src/gpu/Query.h b/libraries/gpu/src/gpu/Query.h
new file mode 100644
index 0000000000..0a4d554e77
--- /dev/null
+++ b/libraries/gpu/src/gpu/Query.h
@@ -0,0 +1,45 @@
+//
+// Query.h
+// interface/src/gpu
+//
+// Created by Niraj Venkat on 7/7/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
+//
+#ifndef hifi_gpu_Query_h
+#define hifi_gpu_Query_h
+
+#include
+#include
+#include
+#include "GPUConfig.h"
+
+#include "Format.h"
+
+namespace gpu {
+
+ class Query {
+ public:
+ Query();
+ ~Query();
+
+ uint32 queryResult;
+
+ double getElapsedTime();
+
+ protected:
+
+ // This shouldn't be used by anything else than the Backend class with the proper casting.
+ mutable GPUObject* _gpuObject = NULL;
+ void setGPUObject(GPUObject* gpuObject) const { _gpuObject = gpuObject; }
+ GPUObject* getGPUObject() const { return _gpuObject; }
+ friend class Backend;
+ };
+
+ typedef std::shared_ptr QueryPointer;
+ typedef std::vector< QueryPointer > Queries;
+};
+
+#endif
diff --git a/libraries/gpu/src/gpu/Resource.cpp b/libraries/gpu/src/gpu/Resource.cpp
index 046cf9fe40..5498e24189 100644
--- a/libraries/gpu/src/gpu/Resource.cpp
+++ b/libraries/gpu/src/gpu/Resource.cpp
@@ -15,6 +15,9 @@
using namespace gpu;
const Element Element::COLOR_RGBA_32 = Element(VEC4, UINT8, RGBA);
+const Element Element::VEC3F_XYZ = Element(VEC3, FLOAT, XYZ);
+const Element Element::INDEX_UINT16 = Element(SCALAR, UINT16, INDEX);
+const Element Element::PART_DRAWCALL = Element(VEC4, UINT32, PART);
Resource::Size Resource::Sysmem::allocateMemory(Byte** dataAllocated, Size size) {
if ( !dataAllocated ) {
diff --git a/libraries/gpu/src/gpu/StandardShaderLib.cpp b/libraries/gpu/src/gpu/StandardShaderLib.cpp
new file mode 100755
index 0000000000..581ce47cde
--- /dev/null
+++ b/libraries/gpu/src/gpu/StandardShaderLib.cpp
@@ -0,0 +1,44 @@
+//
+// StandardShaderLib.cpp
+// libraries/gpu/src/gpu
+//
+// Collection of standard shaders that can be used all over the place
+//
+// Created by Sam Gateau on 6/22/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
+//
+#include "StandardShaderLib.h"
+
+#include "DrawTransformUnitQuad_vert.h"
+#include "DrawViewportQuadTransformTexcoord_vert.h"
+#include "DrawTexture_frag.h"
+
+using namespace gpu;
+
+ShaderPointer StandardShaderLib::_drawTransformUnitQuadVS;
+ShaderPointer StandardShaderLib::_drawViewportQuadTransformTexcoordVS;
+ShaderPointer StandardShaderLib::_drawTexturePS;
+
+ShaderPointer StandardShaderLib::getDrawTransformUnitQuadVS() {
+ if (!_drawTransformUnitQuadVS) {
+ _drawTransformUnitQuadVS = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(DrawTransformUnitQuad_vert)));
+ }
+ return _drawTransformUnitQuadVS;
+}
+
+ShaderPointer StandardShaderLib::getDrawViewportQuadTransformTexcoordVS() {
+ if (!_drawViewportQuadTransformTexcoordVS) {
+ _drawViewportQuadTransformTexcoordVS = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(DrawViewportQuadTransformTexcoord_vert)));
+ }
+ return _drawViewportQuadTransformTexcoordVS;
+}
+
+ShaderPointer StandardShaderLib::getDrawTexturePS() {
+ if (!_drawTexturePS) {
+ _drawTexturePS = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(DrawTexture_frag)));
+ }
+ return _drawTexturePS;
+}
diff --git a/libraries/gpu/src/gpu/StandardShaderLib.h b/libraries/gpu/src/gpu/StandardShaderLib.h
new file mode 100755
index 0000000000..a8fc5126f8
--- /dev/null
+++ b/libraries/gpu/src/gpu/StandardShaderLib.h
@@ -0,0 +1,44 @@
+//
+// StandardShaderLib.h
+// libraries/gpu/src/gpu
+//
+// Collection of standard shaders that can be used all over the place
+//
+// Created by Sam Gateau on 6/22/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
+//
+#ifndef hifi_gpu_StandardShaderLib_h
+#define hifi_gpu_StandardShaderLib_h
+
+#include
+
+#include "Shader.h"
+
+namespace gpu {
+
+class StandardShaderLib {
+public:
+
+ // Shader draw the unit quad objectPos = ([(-1,-1),(1,1)]) and transform it by the full model transform stack (Model, View, Proj).
+ // A texcoord attribute is also generated texcoord = [(0,0),(1,1)]
+ static ShaderPointer getDrawTransformUnitQuadVS();
+
+ // Shader draws the unit quad in the full viewport clipPos = ([(-1,-1),(1,1)]) and transform the texcoord = [(0,0),(1,1)] by the model transform.
+ static ShaderPointer getDrawViewportQuadTransformTexcoordVS();
+
+ static ShaderPointer getDrawTexturePS();
+
+protected:
+
+ static ShaderPointer _drawTransformUnitQuadVS;
+ static ShaderPointer _drawViewportQuadTransformTexcoordVS;
+ static ShaderPointer _drawTexturePS;
+};
+
+
+};
+
+#endif
diff --git a/libraries/gpu/src/gpu/State.cpp b/libraries/gpu/src/gpu/State.cpp
index ca254626e9..da3ab20c7b 100755
--- a/libraries/gpu/src/gpu/State.cpp
+++ b/libraries/gpu/src/gpu/State.cpp
@@ -24,20 +24,20 @@ State::~State() {
// Please make sure to go check makeResetStateCommands() before modifying this value
const State::Data State::DEFAULT = State::Data();
-State::Signature State::evalSignature(const Data& state) {
+State::Signature State::evalSignature(const Data& state) {
Signature signature(0);
if (state.fillMode != State::DEFAULT.fillMode) {
signature.set(State::FILL_MODE);
- }
+ }
if (state.cullMode != State::DEFAULT.cullMode) {
signature.set(State::CULL_MODE);
}
if (state.frontFaceClockwise != State::DEFAULT.frontFaceClockwise) {
signature.set(State::FRONT_FACE_CLOCKWISE);
}
- if (state.depthClipEnable != State::DEFAULT.depthClipEnable) {
- signature.set(State::DEPTH_CLIP_ENABLE);
+ if (state.depthClampEnable != State::DEFAULT.depthClampEnable) {
+ signature.set(State::DEPTH_CLAMP_ENABLE);
}
if (state.scissorEnable != State::DEFAULT.scissorEnable) {
signature.set(State::SCISSOR_ENABLE);
@@ -47,16 +47,16 @@ State::Signature State::evalSignature(const Data& state) {
}
if (state.antialisedLineEnable != State::DEFAULT.antialisedLineEnable) {
signature.set(State::ANTIALISED_LINE_ENABLE);
- }
+ }
if (state.depthBias != State::DEFAULT.depthBias) {
signature.set(State::DEPTH_BIAS);
}
if (state.depthBiasSlopeScale != State::DEFAULT.depthBiasSlopeScale) {
signature.set(State::DEPTH_BIAS_SLOPE_SCALE);
- }
+ }
if (state.depthTest != State::DEFAULT.depthTest) {
signature.set(State::DEPTH_TEST);
- }
+ }
if (state.stencilActivation != State::DEFAULT.stencilActivation) {
signature.set(State::STENCIL_ACTIVATION);
}
@@ -68,21 +68,21 @@ State::Signature State::evalSignature(const Data& state) {
}
if (state.sampleMask != State::DEFAULT.sampleMask) {
signature.set(State::SAMPLE_MASK);
- }
- if (state.alphaToCoverageEnable != State::DEFAULT.alphaToCoverageEnable) {
- signature.set(State::ALPHA_TO_COVERAGE_ENABLE);
- }
- if (state.blendFunction != State::DEFAULT.blendFunction) {
- signature.set(State::BLEND_FUNCTION);
- }
- if (state.colorWriteMask != State::DEFAULT.colorWriteMask) {
- signature.set(State::COLOR_WRITE_MASK);
- }
-
- return signature;
-}
-
-State::State(const Data& values) :
- _values(values) {
- _signature = evalSignature(_values);
-}
+ }
+ if (state.alphaToCoverageEnable != State::DEFAULT.alphaToCoverageEnable) {
+ signature.set(State::ALPHA_TO_COVERAGE_ENABLE);
+ }
+ if (state.blendFunction != State::DEFAULT.blendFunction) {
+ signature.set(State::BLEND_FUNCTION);
+ }
+ if (state.colorWriteMask != State::DEFAULT.colorWriteMask) {
+ signature.set(State::COLOR_WRITE_MASK);
+ }
+
+ return signature;
+}
+
+State::State(const Data& values) :
+ _values(values) {
+ _signature = evalSignature(_values);
+}
diff --git a/libraries/gpu/src/gpu/State.h b/libraries/gpu/src/gpu/State.h
index dce9e50488..39cad1445f 100755
--- a/libraries/gpu/src/gpu/State.h
+++ b/libraries/gpu/src/gpu/State.h
@@ -249,7 +249,7 @@ public:
uint8 colorWriteMask = WRITE_ALL;
bool frontFaceClockwise : 1;
- bool depthClipEnable : 1;
+ bool depthClampEnable : 1;
bool scissorEnable : 1;
bool multisampleEnable : 1;
bool antialisedLineEnable : 1;
@@ -257,7 +257,7 @@ public:
Data() :
frontFaceClockwise(false),
- depthClipEnable(false),
+ depthClampEnable(false),
scissorEnable(false),
multisampleEnable(false),
antialisedLineEnable(false),
@@ -276,8 +276,8 @@ public:
void setFrontFaceClockwise(bool isClockwise) { SET_FIELD(FRONT_FACE_CLOCKWISE, DEFAULT.frontFaceClockwise, isClockwise, _values.frontFaceClockwise); }
bool isFrontFaceClockwise() const { return _values.frontFaceClockwise; }
- void setDepthClipEnable(bool enable) { SET_FIELD(DEPTH_CLIP_ENABLE, DEFAULT.depthClipEnable, enable, _values.depthClipEnable); }
- bool isDepthClipEnable() const { return _values.depthClipEnable; }
+ void setDepthClampEnable(bool enable) { SET_FIELD(DEPTH_CLAMP_ENABLE, DEFAULT.depthClampEnable, enable, _values.depthClampEnable); }
+ bool isDepthClampEnable() const { return _values.depthClampEnable; }
void setScissorEnable(bool enable) { SET_FIELD(SCISSOR_ENABLE, DEFAULT.scissorEnable, enable, _values.scissorEnable); }
bool isScissorEnable() const { return _values.scissorEnable; }
@@ -341,6 +341,7 @@ public:
// Color write mask
void setColorWriteMask(uint8 mask) { SET_FIELD(COLOR_WRITE_MASK, DEFAULT.colorWriteMask, mask, _values.colorWriteMask); }
+ void setColorWriteMask(bool red, bool green, bool blue, bool alpha) { uint32 value = ((WRITE_RED * red) | (WRITE_GREEN * green) | (WRITE_BLUE * blue) | (WRITE_ALPHA * alpha)); SET_FIELD(COLOR_WRITE_MASK, DEFAULT.colorWriteMask, value, _values.colorWriteMask); }
uint8 getColorWriteMask() const { return _values.colorWriteMask; }
// All the possible fields
@@ -348,7 +349,7 @@ public:
FILL_MODE,
CULL_MODE,
FRONT_FACE_CLOCKWISE,
- DEPTH_CLIP_ENABLE,
+ DEPTH_CLAMP_ENABLE,
SCISSOR_ENABLE,
MULTISAMPLE_ENABLE,
ANTIALISED_LINE_ENABLE,
diff --git a/libraries/gpu/src/gpu/Transform.slh b/libraries/gpu/src/gpu/Transform.slh
index 274032a642..a3299ae599 100644
--- a/libraries/gpu/src/gpu/Transform.slh
+++ b/libraries/gpu/src/gpu/Transform.slh
@@ -86,6 +86,7 @@ TransformCamera getTransformCamera() {
return camera;
}
+uniform mat4 transformObject_model;
uniform mat4 transformCamera_viewInverse;
uniform vec4 transformCamera_viewport;
@@ -130,6 +131,16 @@ uniform vec4 transformCamera_viewport;
<@endif@>
<@endfunc@>
+<@func transformModelToWorldPos(objectTransform, modelPos, worldPos)@>
+<@if GPU_TRANSFORM_PROFILE == GPU_CORE@>
+ { // transformModelToWorldPos
+ <$worldPos$> = (<$objectTransform$>._model * <$modelPos$>);
+ }
+<@else@>
+ <$worldPos$> = (transformObject_model * <$modelPos$>);
+<@endif@>
+<@endfunc@>
+
<@func transformModelToEyeDir(cameraTransform, objectTransform, modelDir, eyeDir)@>
<@if GPU_TRANSFORM_PROFILE == GPU_CORE@>
{ // transformModelToEyeDir
diff --git a/libraries/model/src/model/Geometry.h b/libraries/model/src/model/Geometry.h
index ddefaf4e96..16ebb60b72 100755
--- a/libraries/model/src/model/Geometry.h
+++ b/libraries/model/src/model/Geometry.h
@@ -113,6 +113,8 @@ public:
// Generate a BufferStream on the mesh vertices and attributes
const gpu::BufferStream makeBufferStream() const;
+ static gpu::Primitive topologyToPrimitive(Topology topo) { return static_cast(topo); }
+
protected:
gpu::Stream::FormatPointer _vertexFormat;
diff --git a/libraries/model/src/model/Light.cpp b/libraries/model/src/model/Light.cpp
index 60c6f6b3af..b7635b4af3 100755
--- a/libraries/model/src/model/Light.cpp
+++ b/libraries/model/src/model/Light.cpp
@@ -77,18 +77,27 @@ void Light::setMaximumRadius(float radius) {
editSchema()._attenuation = Vec4(surfaceRadius, 1.0f/surfaceRadius, CutOffIntensityRatio, radius);
}
+#include
+
void Light::setSpotAngle(float angle) {
- if (angle <= 0.f) {
- angle = 0.0f;
+ double dangle = angle;
+ if (dangle <= 0.0) {
+ dangle = 0.0;
}
- editSchema()._spot.x = cos(angle);
- editSchema()._spot.y = sin(angle);
- editSchema()._spot.z = angle;
+ if (dangle > glm::half_pi()) {
+ dangle = glm::half_pi();
+ }
+
+ auto cosAngle = cos(dangle);
+ auto sinAngle = sin(dangle);
+ editSchema()._spot.x = (float) std::abs(cosAngle);
+ editSchema()._spot.y = (float) std::abs(sinAngle);
+ editSchema()._spot.z = (float) angle;
}
void Light::setSpotExponent(float exponent) {
if (exponent <= 0.f) {
- exponent = 1.0f;
+ exponent = 0.0f;
}
editSchema()._spot.w = exponent;
}
diff --git a/libraries/model/src/model/Light.h b/libraries/model/src/model/Light.h
index 920549d0f9..1ed07a942c 100755
--- a/libraries/model/src/model/Light.h
+++ b/libraries/model/src/model/Light.h
@@ -81,6 +81,7 @@ public:
bool isSpot() const { return getType() == SPOT; }
void setSpotAngle(float angle);
float getSpotAngle() const { return getSchema()._spot.z; }
+ glm::vec2 getSpotAngleCosSin() const { return glm::vec2(getSchema()._spot.x, getSchema()._spot.y); }
void setSpotExponent(float exponent);
float getSpotExponent() const { return getSchema()._spot.w; }
@@ -107,7 +108,7 @@ public:
Color _color{1.0f};
float _intensity{1.0f};
Vec4 _attenuation{1.0f};
- Vec4 _spot{0.0f, 0.0f, 0.0f, 3.0f};
+ Vec4 _spot{0.0f, 0.0f, 0.0f, 0.0f};
Vec4 _shadow{0.0f};
Vec4 _control{0.0f, 0.0f, 0.0f, 0.0f};
diff --git a/libraries/model/src/model/Skybox.cpp b/libraries/model/src/model/Skybox.cpp
index 10199adda3..0fb2458f01 100755
--- a/libraries/model/src/model/Skybox.cpp
+++ b/libraries/model/src/model/Skybox.cpp
@@ -51,7 +51,7 @@ void Skybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const Sky
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
+ static 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)));
@@ -103,7 +103,7 @@ void Skybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const Sky
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.setResourceTexture(0, skybox.getCubemap());
batch.draw(gpu::TRIANGLE_STRIP, 4);
}
diff --git a/libraries/networking/src/ReceivedPacketProcessor.cpp b/libraries/networking/src/ReceivedPacketProcessor.cpp
index 3c4b32b4ec..94dd04d44b 100644
--- a/libraries/networking/src/ReceivedPacketProcessor.cpp
+++ b/libraries/networking/src/ReceivedPacketProcessor.cpp
@@ -9,10 +9,17 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
+#include
+
#include "NodeList.h"
#include "ReceivedPacketProcessor.h"
#include "SharedUtil.h"
+ReceivedPacketProcessor::ReceivedPacketProcessor() {
+ _lastWindowAt = usecTimestampNow();
+}
+
+
void ReceivedPacketProcessor::terminating() {
_hasPackets.wakeAll();
}
@@ -25,6 +32,7 @@ void ReceivedPacketProcessor::queueReceivedPacket(const SharedNodePointer& sendi
lock();
_packets.push_back(networkPacket);
_nodePacketCounts[sendingNode->getUUID()]++;
+ _lastWindowIncomingPackets++;
unlock();
// Make sure to wake our actual processing thread because we now have packets for it to process.
@@ -32,6 +40,24 @@ void ReceivedPacketProcessor::queueReceivedPacket(const SharedNodePointer& sendi
}
bool ReceivedPacketProcessor::process() {
+ quint64 now = usecTimestampNow();
+ quint64 sinceLastWindow = now - _lastWindowAt;
+
+
+ if (sinceLastWindow > USECS_PER_SECOND) {
+ lock();
+ float secondsSinceLastWindow = sinceLastWindow / USECS_PER_SECOND;
+ float incomingPacketsPerSecondInWindow = (float)_lastWindowIncomingPackets / secondsSinceLastWindow;
+ _incomingPPS.updateAverage(incomingPacketsPerSecondInWindow);
+
+ float processedPacketsPerSecondInWindow = (float)_lastWindowIncomingPackets / secondsSinceLastWindow;
+ _processedPPS.updateAverage(processedPacketsPerSecondInWindow);
+
+ _lastWindowAt = now;
+ _lastWindowIncomingPackets = 0;
+ _lastWindowProcessedPackets = 0;
+ unlock();
+ }
if (_packets.size() == 0) {
_waitingOnPacketsMutex.lock();
@@ -51,6 +77,7 @@ bool ReceivedPacketProcessor::process() {
foreach(auto& packet, currentPackets) {
processPacket(packet.getNode(), packet.getByteArray());
+ _lastWindowProcessedPackets++;
midProcess();
}
diff --git a/libraries/networking/src/ReceivedPacketProcessor.h b/libraries/networking/src/ReceivedPacketProcessor.h
index bcc9f9a1f5..84a954760c 100644
--- a/libraries/networking/src/ReceivedPacketProcessor.h
+++ b/libraries/networking/src/ReceivedPacketProcessor.h
@@ -21,7 +21,7 @@
class ReceivedPacketProcessor : public GenericThread {
Q_OBJECT
public:
- ReceivedPacketProcessor() { }
+ ReceivedPacketProcessor();
/// Add packet from network receive thread to the processing queue.
void queueReceivedPacket(const SharedNodePointer& sendingNode, const QByteArray& packet);
@@ -47,6 +47,9 @@ public:
/// How many received packets waiting are to be processed
int packetsToProcessCount() const { return _packets.size(); }
+ float getIncomingPPS() const { return _incomingPPS.getAverage(); }
+ float getProcessedPPS() const { return _processedPPS.getAverage(); }
+
virtual void terminating();
public slots:
@@ -80,6 +83,12 @@ protected:
QWaitCondition _hasPackets;
QMutex _waitingOnPacketsMutex;
+
+ quint64 _lastWindowAt = 0;
+ int _lastWindowIncomingPackets = 0;
+ int _lastWindowProcessedPackets = 0;
+ SimpleMovingAverage _incomingPPS;
+ SimpleMovingAverage _processedPPS;
};
#endif // hifi_ReceivedPacketProcessor_h
diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp
index a975d21c4d..b06c6f264b 100644
--- a/libraries/physics/src/EntityMotionState.cpp
+++ b/libraries/physics/src/EntityMotionState.cpp
@@ -248,7 +248,7 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
btTransform xform = _body->getWorldTransform();
_serverPosition = bulletToGLM(xform.getOrigin());
_serverRotation = bulletToGLM(xform.getRotation());
- _serverVelocity = bulletToGLM(_body->getLinearVelocity());
+ _serverVelocity = getBodyLinearVelocity();
_serverAngularVelocity = bulletToGLM(_body->getAngularVelocity());
_lastStep = simulationStep;
_serverActionData = _entity->getActionData();
@@ -287,6 +287,7 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
}
if (_serverActionData != _entity->getActionData()) {
+ setOutgoingPriority(SCRIPT_EDIT_SIMULATION_PRIORITY);
return true;
}
@@ -536,7 +537,7 @@ void EntityMotionState::bump(quint8 priority) {
void EntityMotionState::resetMeasuredBodyAcceleration() {
_lastMeasureStep = ObjectMotionState::getWorldSimulationStep();
if (_body) {
- _lastVelocity = bulletToGLM(_body->getLinearVelocity());
+ _lastVelocity = getBodyLinearVelocity();
} else {
_lastVelocity = glm::vec3(0.0f);
}
@@ -555,7 +556,7 @@ void EntityMotionState::measureBodyAcceleration() {
// Note: the integration equation for velocity uses damping: v1 = (v0 + a * dt) * (1 - D)^dt
// hence the equation for acceleration is: a = (v1 / (1 - D)^dt - v0) / dt
- glm::vec3 velocity = bulletToGLM(_body->getLinearVelocity());
+ glm::vec3 velocity = getBodyLinearVelocity();
_measuredAcceleration = (velocity / powf(1.0f - _body->getLinearDamping(), dt) - _lastVelocity) * invDt;
_lastVelocity = velocity;
if (numSubsteps > PHYSICS_ENGINE_MAX_NUM_SUBSTEPS) {
diff --git a/libraries/physics/src/ObjectAction.cpp b/libraries/physics/src/ObjectAction.cpp
index ae29fe79d3..5205e08c62 100644
--- a/libraries/physics/src/ObjectAction.cpp
+++ b/libraries/physics/src/ObjectAction.cpp
@@ -13,9 +13,9 @@
#include "ObjectAction.h"
-ObjectAction::ObjectAction(EntityActionType type, QUuid id, EntityItemPointer ownerEntity) :
+ObjectAction::ObjectAction(EntityActionType type, const QUuid& id, EntityItemPointer ownerEntity) :
btActionInterface(),
- _id(id),
+ EntityActionInterface(type, id),
_active(false),
_ownerEntity(ownerEntity) {
}
@@ -24,15 +24,15 @@ ObjectAction::~ObjectAction() {
}
void ObjectAction::updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep) {
- if (!_active) {
- return;
- }
if (_ownerEntity.expired()) {
qDebug() << "warning -- action with no entity removing self from btCollisionWorld.";
btDynamicsWorld* dynamicsWorld = static_cast(collisionWorld);
dynamicsWorld->removeAction(this);
return;
}
+ if (!_active) {
+ return;
+ }
updateActionWorker(deltaTimeStep);
}
@@ -129,11 +129,10 @@ void ObjectAction::setAngularVelocity(glm::vec3 angularVelocity) {
rigidBody->activate();
}
-QByteArray ObjectAction::serialize() {
- assert(false);
- return QByteArray();
+void ObjectAction::activateBody() {
+ auto rigidBody = getRigidBody();
+ if (rigidBody) {
+ rigidBody->activate();
+ }
}
-void ObjectAction::deserialize(QByteArray serializedArguments) {
- assert(false);
-}
diff --git a/libraries/physics/src/ObjectAction.h b/libraries/physics/src/ObjectAction.h
index 0e982aaacf..f27ed9ab07 100644
--- a/libraries/physics/src/ObjectAction.h
+++ b/libraries/physics/src/ObjectAction.h
@@ -17,8 +17,6 @@
#include
-#include
-
#include "ObjectMotionState.h"
#include "BulletUtil.h"
#include "EntityActionInterface.h"
@@ -26,31 +24,25 @@
class ObjectAction : public btActionInterface, public EntityActionInterface {
public:
- ObjectAction(EntityActionType type, QUuid id, EntityItemPointer ownerEntity);
+ ObjectAction(EntityActionType type, const QUuid& id, EntityItemPointer ownerEntity);
virtual ~ObjectAction();
- const QUuid& getID() const { return _id; }
- virtual EntityActionType getType() { assert(false); return ACTION_TYPE_NONE; }
virtual void removeFromSimulation(EntitySimulation* simulation) const;
virtual EntityItemWeakPointer getOwnerEntity() const { return _ownerEntity; }
virtual void setOwnerEntity(const EntityItemPointer ownerEntity) { _ownerEntity = ownerEntity; }
- virtual bool updateArguments(QVariantMap arguments) { return false; }
- virtual QVariantMap getArguments() { return QVariantMap(); }
+ virtual bool updateArguments(QVariantMap arguments) = 0;
+ virtual QVariantMap getArguments() = 0;
// this is called from updateAction and should be overridden by subclasses
- virtual void updateActionWorker(float deltaTimeStep) {}
+ virtual void updateActionWorker(float deltaTimeStep) = 0;
// these are from btActionInterface
virtual void updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep);
virtual void debugDraw(btIDebugDraw* debugDrawer);
- virtual QByteArray serialize();
- virtual void deserialize(QByteArray serializedArguments);
-
-private:
- QUuid _id;
- QReadWriteLock _lock;
+ virtual QByteArray serialize() const = 0;
+ virtual void deserialize(QByteArray serializedArguments) = 0;
protected:
@@ -63,6 +55,7 @@ protected:
virtual void setLinearVelocity(glm::vec3 linearVelocity);
virtual glm::vec3 getAngularVelocity();
virtual void setAngularVelocity(glm::vec3 angularVelocity);
+ virtual void activateBody();
void lockForRead() { _lock.lockForRead(); }
bool tryLockForRead() { return _lock.tryLockForRead(); }
@@ -70,6 +63,10 @@ protected:
bool tryLockForWrite() { return _lock.tryLockForWrite(); }
void unlock() { _lock.unlock(); }
+private:
+ QReadWriteLock _lock;
+
+protected:
bool _active;
EntityItemWeakPointer _ownerEntity;
};
diff --git a/libraries/physics/src/ObjectActionOffset.cpp b/libraries/physics/src/ObjectActionOffset.cpp
index 22c6b7e0d3..a00bbbd418 100644
--- a/libraries/physics/src/ObjectActionOffset.cpp
+++ b/libraries/physics/src/ObjectActionOffset.cpp
@@ -15,8 +15,12 @@
const uint16_t ObjectActionOffset::offsetVersion = 1;
-ObjectActionOffset::ObjectActionOffset(EntityActionType type, QUuid id, EntityItemPointer ownerEntity) :
- ObjectAction(type, id, ownerEntity) {
+ObjectActionOffset::ObjectActionOffset(const QUuid& id, EntityItemPointer ownerEntity) :
+ ObjectAction(ACTION_TYPE_OFFSET, id, ownerEntity),
+ _pointToOffsetFrom(0.0f),
+ _linearDistance(0.0f),
+ _linearTimeScale(FLT_MAX),
+ _positionalTargetSet(false) {
#if WANT_DEBUG
qDebug() << "ObjectActionOffset::ObjectActionOffset";
#endif
@@ -44,6 +48,7 @@ void ObjectActionOffset::updateActionWorker(btScalar deltaTimeStep) {
unlock();
return;
}
+
ObjectMotionState* motionState = static_cast(physicsInfo);
btRigidBody* rigidBody = motionState->getRigidBody();
if (!rigidBody) {
@@ -52,21 +57,28 @@ void ObjectActionOffset::updateActionWorker(btScalar deltaTimeStep) {
return;
}
- if (_positionalTargetSet) {
- glm::vec3 offset = _pointToOffsetFrom - bulletToGLM(rigidBody->getCenterOfMassPosition());
- float offsetLength = glm::length(offset);
- float offsetError = _linearDistance - offsetLength;
+ const float MAX_LINEAR_TIMESCALE = 600.0f; // 10 minutes is a long time
+ if (_positionalTargetSet && _linearTimeScale < MAX_LINEAR_TIMESCALE) {
+ glm::vec3 objectPosition = bulletToGLM(rigidBody->getCenterOfMassPosition());
+ glm::vec3 springAxis = objectPosition - _pointToOffsetFrom; // from anchor to object
+ float distance = glm::length(springAxis);
+ if (distance > FLT_EPSILON) {
+ springAxis /= distance; // normalize springAxis
- // if (glm::abs(offsetError) > IGNORE_POSITION_DELTA) {
- if (glm::abs(offsetError) > 0.0f) {
- float offsetErrorAbs = glm::abs(offsetError);
- float offsetErrorDirection = - offsetError / offsetErrorAbs;
- glm::vec3 previousVelocity = bulletToGLM(rigidBody->getLinearVelocity());
+ // compute (critically damped) target velocity of spring relaxation
+ glm::vec3 offset = (distance - _linearDistance) * springAxis;
+ glm::vec3 targetVelocity = (-1.0f / _linearTimeScale) * offset;
- glm::vec3 velocityAdjustment = glm::normalize(offset) * offsetErrorDirection * offsetErrorAbs / _linearTimeScale;
- rigidBody->setLinearVelocity(glmToBullet(previousVelocity + velocityAdjustment));
- // rigidBody->setLinearVelocity(glmToBullet(velocityAdjustment));
- rigidBody->activate();
+ // compute current velocity and its parallel component
+ glm::vec3 currentVelocity = bulletToGLM(rigidBody->getLinearVelocity());
+ glm::vec3 parallelVelocity = glm::dot(currentVelocity, springAxis) * springAxis;
+
+ // we blend the parallel component with the spring's target velocity to get the new velocity
+ float blend = deltaTimeStep / _linearTimeScale;
+ if (blend > 1.0f) {
+ blend = 1.0f;
+ }
+ rigidBody->setLinearVelocity(glmToBullet(currentVelocity + blend * (targetVelocity - parallelVelocity)));
}
}
@@ -75,45 +87,40 @@ void ObjectActionOffset::updateActionWorker(btScalar deltaTimeStep) {
bool ObjectActionOffset::updateArguments(QVariantMap arguments) {
- bool pOk0 = true;
+ bool ok = true;
glm::vec3 pointToOffsetFrom =
- EntityActionInterface::extractVec3Argument("offset action", arguments, "pointToOffsetFrom", pOk0, true);
+ EntityActionInterface::extractVec3Argument("offset action", arguments, "pointToOffsetFrom", ok, true);
+ if (!ok) {
+ pointToOffsetFrom = _pointToOffsetFrom;
+ }
- bool pOk1 = true;
+ ok = true;
float linearTimeScale =
- EntityActionInterface::extractFloatArgument("offset action", arguments, "linearTimeScale", pOk1, false);
+ EntityActionInterface::extractFloatArgument("offset action", arguments, "linearTimeScale", ok, false);
+ if (!ok) {
+ linearTimeScale = _linearTimeScale;
+ }
- bool pOk2 = true;
+ ok = true;
float linearDistance =
- EntityActionInterface::extractFloatArgument("offset action", arguments, "linearDistance", pOk2, false);
-
- if (!pOk0) {
- return false;
- }
- if (pOk1 && linearTimeScale <= 0.0f) {
- qDebug() << "offset action -- linearTimeScale must be greater than zero.";
- return false;
+ EntityActionInterface::extractFloatArgument("offset action", arguments, "linearDistance", ok, false);
+ if (!ok) {
+ linearDistance = _linearDistance;
}
- lockForWrite();
-
- _pointToOffsetFrom = pointToOffsetFrom;
- _positionalTargetSet = true;
-
- if (pOk1) {
+ // only change stuff if something actually changed
+ if (_pointToOffsetFrom != pointToOffsetFrom
+ || _linearTimeScale != linearTimeScale
+ || _linearDistance != linearDistance) {
+ lockForWrite();
+ _pointToOffsetFrom = pointToOffsetFrom;
_linearTimeScale = linearTimeScale;
- } else {
- _linearTimeScale = 0.1f;
- }
-
- if (pOk2) {
_linearDistance = linearDistance;
- } else {
- _linearDistance = 1.0f;
+ _positionalTargetSet = true;
+ _active = true;
+ activateBody();
+ unlock();
}
-
- _active = true;
- unlock();
return true;
}
@@ -127,7 +134,7 @@ QVariantMap ObjectActionOffset::getArguments() {
return arguments;
}
-QByteArray ObjectActionOffset::serialize() {
+QByteArray ObjectActionOffset::serialize() const {
QByteArray ba;
QDataStream dataStream(&ba, QIODevice::WriteOnly);
dataStream << getType();
@@ -146,13 +153,14 @@ void ObjectActionOffset::deserialize(QByteArray serializedArguments) {
QDataStream dataStream(serializedArguments);
EntityActionType type;
- QUuid id;
- uint16_t serializationVersion;
-
dataStream >> type;
assert(type == getType());
+
+ QUuid id;
dataStream >> id;
assert(id == getID());
+
+ uint16_t serializationVersion;
dataStream >> serializationVersion;
if (serializationVersion != ObjectActionOffset::offsetVersion) {
return;
diff --git a/libraries/physics/src/ObjectActionOffset.h b/libraries/physics/src/ObjectActionOffset.h
index 28a08c2efe..1918da6996 100644
--- a/libraries/physics/src/ObjectActionOffset.h
+++ b/libraries/physics/src/ObjectActionOffset.h
@@ -19,17 +19,15 @@
class ObjectActionOffset : public ObjectAction {
public:
- ObjectActionOffset(EntityActionType type, QUuid id, EntityItemPointer ownerEntity);
+ ObjectActionOffset(const QUuid& id, EntityItemPointer ownerEntity);
virtual ~ObjectActionOffset();
- virtual EntityActionType getType() { return ACTION_TYPE_OFFSET; }
-
virtual bool updateArguments(QVariantMap arguments);
virtual QVariantMap getArguments();
virtual void updateActionWorker(float deltaTimeStep);
- virtual QByteArray serialize();
+ virtual QByteArray serialize() const;
virtual void deserialize(QByteArray serializedArguments);
private:
diff --git a/libraries/physics/src/ObjectActionSpring.cpp b/libraries/physics/src/ObjectActionSpring.cpp
index fae593f3eb..196cc8d3ea 100644
--- a/libraries/physics/src/ObjectActionSpring.cpp
+++ b/libraries/physics/src/ObjectActionSpring.cpp
@@ -17,14 +17,15 @@ const float SPRING_MAX_SPEED = 10.0f;
const uint16_t ObjectActionSpring::springVersion = 1;
-ObjectActionSpring::ObjectActionSpring(EntityActionType type, QUuid id, EntityItemPointer ownerEntity) :
- ObjectAction(type, id, ownerEntity),
+
+ObjectActionSpring::ObjectActionSpring(const QUuid& id, EntityItemPointer ownerEntity) :
+ ObjectAction(ACTION_TYPE_SPRING, id, ownerEntity),
_positionalTarget(glm::vec3(0.0f)),
- _linearTimeScale(0.2f),
- _positionalTargetSet(false),
+ _linearTimeScale(FLT_MAX),
+ _positionalTargetSet(true),
_rotationalTarget(glm::quat()),
- _angularTimeScale(0.2f),
- _rotationalTargetSet(false) {
+ _angularTimeScale(FLT_MAX),
+ _rotationalTargetSet(true) {
#if WANT_DEBUG
qDebug() << "ObjectActionSpring::ObjectActionSpring";
#endif
@@ -61,130 +62,92 @@ void ObjectActionSpring::updateActionWorker(btScalar deltaTimeStep) {
return;
}
- // handle the linear part
- if (_positionalTargetSet) {
- // check for NaN
- if (_positionalTarget.x != _positionalTarget.x ||
- _positionalTarget.y != _positionalTarget.y ||
- _positionalTarget.z != _positionalTarget.z) {
- qDebug() << "ObjectActionSpring::updateActionWorker -- target position includes NaN";
- unlock();
- lockForWrite();
- _active = false;
- unlock();
- return;
- }
- glm::vec3 offset = _positionalTarget - bulletToGLM(rigidBody->getCenterOfMassPosition());
- float offsetLength = glm::length(offset);
- float speed = offsetLength / _linearTimeScale;
+ const float MAX_TIMESCALE = 600.0f; // 10 min is a long time
+ if (_linearTimeScale < MAX_TIMESCALE) {
+ btVector3 offset = rigidBody->getCenterOfMassPosition() - glmToBullet(_positionalTarget);
+ float offsetLength = offset.length();
+ float speed = (offsetLength > FLT_EPSILON) ? glm::min(offsetLength / _linearTimeScale, SPRING_MAX_SPEED) : 0.0f;
- // cap speed
- if (speed > SPRING_MAX_SPEED) {
- speed = SPRING_MAX_SPEED;
- }
-
- 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)));
- }
+ // this action is aggresively critically damped and defeats the current velocity
+ rigidBody->setLinearVelocity((- speed / offsetLength) * offset);
}
- // handle rotation
- if (_rotationalTargetSet) {
- if (_rotationalTarget.x != _rotationalTarget.x ||
- _rotationalTarget.y != _rotationalTarget.y ||
- _rotationalTarget.z != _rotationalTarget.z ||
- _rotationalTarget.w != _rotationalTarget.w) {
- qDebug() << "AvatarActionHold::updateActionWorker -- target rotation includes NaN";
- unlock();
- lockForWrite();
- _active = false;
- unlock();
- return;
- }
+ if (_angularTimeScale < MAX_TIMESCALE) {
+ btVector3 targetVelocity(0.0f, 0.0f, 0.0f);
- 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) {
+ btQuaternion bodyRotation = rigidBody->getOrientation();
+ auto alignmentDot = bodyRotation.dot(glmToBullet(_rotationalTarget));
+ const float ALMOST_ONE = 0.99999f;
+ if (glm::abs(alignmentDot) < ALMOST_ONE) {
+ btQuaternion target = glmToBullet(_rotationalTarget);
+ if (alignmentDot < 0.0f) {
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)));
+ // if dQ is the incremental rotation that gets an object from Q0 to Q1 then:
+ //
+ // Q1 = dQ * Q0
+ //
+ // solving for dQ gives:
+ //
+ // dQ = Q1 * Q0^
+ btQuaternion deltaQ = target * bodyRotation.inverse();
+ float angle = deltaQ.getAngle();
+ const float MIN_ANGLE = 1.0e-4;
+ if (angle > MIN_ANGLE) {
+ targetVelocity = (angle / _angularTimeScale) * deltaQ.getAxis();
+ }
}
+ // this action is aggresively critically damped and defeats the current velocity
+ rigidBody->setAngularVelocity(targetVelocity);
}
-
unlock();
}
+const float MIN_TIMESCALE = 0.1f;
bool ObjectActionSpring::updateArguments(QVariantMap arguments) {
// targets are required, spring-constants are optional
- bool ptOk = true;
+ bool ok = true;
glm::vec3 positionalTarget =
- EntityActionInterface::extractVec3Argument("spring action", arguments, "targetPosition", ptOk, false);
- bool pscOk = true;
+ EntityActionInterface::extractVec3Argument("spring action", arguments, "targetPosition", ok, false);
+ if (!ok) {
+ positionalTarget = _positionalTarget;
+ }
+ ok = 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;
+ EntityActionInterface::extractFloatArgument("spring action", arguments, "linearTimeScale", ok, false);
+ if (!ok || linearTimeScale <= 0.0f) {
+ linearTimeScale = _linearTimeScale;
}
- bool rtOk = true;
+ ok = true;
glm::quat rotationalTarget =
- EntityActionInterface::extractQuatArgument("spring action", arguments, "targetRotation", rtOk, false);
- bool rscOk = true;
+ EntityActionInterface::extractQuatArgument("spring action", arguments, "targetRotation", ok, false);
+ if (!ok) {
+ rotationalTarget = _rotationalTarget;
+ }
+
+ ok = true;
float angularTimeScale =
- EntityActionInterface::extractFloatArgument("spring action", arguments, "angularTimeScale", rscOk, false);
-
- if (!ptOk && !rtOk) {
- qDebug() << "spring action requires at least one of targetPosition or targetRotation argument";
- return false;
+ EntityActionInterface::extractFloatArgument("spring action", arguments, "angularTimeScale", ok, false);
+ if (!ok) {
+ angularTimeScale = _angularTimeScale;
}
- lockForWrite();
-
- _positionalTargetSet = _rotationalTargetSet = false;
-
- if (ptOk) {
+ if (positionalTarget != _positionalTarget
+ || linearTimeScale != _linearTimeScale
+ || rotationalTarget != _rotationalTarget
+ || angularTimeScale != _angularTimeScale) {
+ // something changed
+ lockForWrite();
_positionalTarget = positionalTarget;
- _positionalTargetSet = true;
-
- if (pscOk) {
- _linearTimeScale = linearTimeScale;
- } else {
- _linearTimeScale = 0.1f;
- }
- }
-
- if (rtOk) {
+ _linearTimeScale = glm::max(MIN_TIMESCALE, glm::abs(linearTimeScale));
_rotationalTarget = rotationalTarget;
- _rotationalTargetSet = true;
-
- if (rscOk) {
- _angularTimeScale = angularTimeScale;
- } else {
- _angularTimeScale = 0.1f;
- }
+ _angularTimeScale = glm::max(MIN_TIMESCALE, glm::abs(angularTimeScale));
+ _active = true;
+ activateBody();
+ unlock();
}
-
- _active = true;
- unlock();
return true;
}
@@ -192,25 +155,21 @@ QVariantMap ObjectActionSpring::getArguments() {
QVariantMap arguments;
lockForRead();
- if (_positionalTargetSet) {
- arguments["linearTimeScale"] = _linearTimeScale;
- arguments["targetPosition"] = glmToQMap(_positionalTarget);
- }
+ arguments["linearTimeScale"] = _linearTimeScale;
+ arguments["targetPosition"] = glmToQMap(_positionalTarget);
- if (_rotationalTargetSet) {
- arguments["targetRotation"] = glmToQMap(_rotationalTarget);
- arguments["angularTimeScale"] = _angularTimeScale;
- }
+ arguments["targetRotation"] = glmToQMap(_rotationalTarget);
+ arguments["angularTimeScale"] = _angularTimeScale;
unlock();
return arguments;
}
-QByteArray ObjectActionSpring::serialize() {
+QByteArray ObjectActionSpring::serialize() const {
QByteArray serializedActionArguments;
QDataStream dataStream(&serializedActionArguments, QIODevice::WriteOnly);
- dataStream << getType();
+ dataStream << ACTION_TYPE_SPRING;
dataStream << getID();
dataStream << ObjectActionSpring::springVersion;
@@ -229,13 +188,14 @@ void ObjectActionSpring::deserialize(QByteArray serializedArguments) {
QDataStream dataStream(serializedArguments);
EntityActionType type;
- QUuid id;
- uint16_t serializationVersion;
-
dataStream >> type;
assert(type == getType());
+
+ QUuid id;
dataStream >> id;
assert(id == getID());
+
+ uint16_t serializationVersion;
dataStream >> serializationVersion;
if (serializationVersion != ObjectActionSpring::springVersion) {
return;
diff --git a/libraries/physics/src/ObjectActionSpring.h b/libraries/physics/src/ObjectActionSpring.h
index c887a046bb..caa64c3d3a 100644
--- a/libraries/physics/src/ObjectActionSpring.h
+++ b/libraries/physics/src/ObjectActionSpring.h
@@ -12,24 +12,19 @@
#ifndef hifi_ObjectActionSpring_h
#define hifi_ObjectActionSpring_h
-#include
-
-#include
#include "ObjectAction.h"
class ObjectActionSpring : public ObjectAction {
public:
- ObjectActionSpring(EntityActionType type, QUuid id, EntityItemPointer ownerEntity);
+ ObjectActionSpring(const QUuid& id, EntityItemPointer ownerEntity);
virtual ~ObjectActionSpring();
- virtual EntityActionType getType() { return ACTION_TYPE_SPRING; }
-
virtual bool updateArguments(QVariantMap arguments);
virtual QVariantMap getArguments();
virtual void updateActionWorker(float deltaTimeStep);
- virtual QByteArray serialize();
+ virtual QByteArray serialize() const;
virtual void deserialize(QByteArray serializedArguments);
protected:
diff --git a/libraries/physics/src/ObjectMotionState.cpp b/libraries/physics/src/ObjectMotionState.cpp
index 7ff119fb79..aeec85f7ea 100644
--- a/libraries/physics/src/ObjectMotionState.cpp
+++ b/libraries/physics/src/ObjectMotionState.cpp
@@ -80,8 +80,19 @@ void ObjectMotionState::setBodyGravity(const glm::vec3& gravity) const {
}
glm::vec3 ObjectMotionState::getBodyLinearVelocity() const {
- return bulletToGLM(_body->getLinearVelocity());
+ // returns the body's velocity unless it is moving too slow in which case returns zero
+ btVector3 velocity = _body->getLinearVelocity();
+
+ // NOTE: the threshold to use here relates to the linear displacement threshold (dX) for sending updates
+ // to objects that are tracked server-side (e.g. entities which use dX = 2mm). Hence an object moving
+ // just under this velocity threshold would trigger an update about V/dX times per second.
+ const float MIN_LINEAR_SPEED_SQUARED = 0.0036f; // 6 mm/sec
+ if (velocity.length2() < MIN_LINEAR_SPEED_SQUARED) {
+ velocity *= 0.0f;
+ }
+ return bulletToGLM(velocity);
}
+
glm::vec3 ObjectMotionState::getObjectLinearVelocityChange() const {
return glm::vec3(0.0f); // Subclasses override where meaningful.
}
diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp
index fee227600e..dfba981d2f 100644
--- a/libraries/render-utils/src/DeferredLightingEffect.cpp
+++ b/libraries/render-utils/src/DeferredLightingEffect.cpp
@@ -9,7 +9,6 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
-// include this before QOpenGLFramebufferObject, which includes an earlier version of OpenGL
#include
#include
@@ -24,7 +23,8 @@
#include "TextureCache.h"
#include "gpu/Batch.h"
-#include "gpu/GLBackend.h"
+#include "gpu/Context.h"
+#include "gpu/StandardShaderLib.h"
#include "simple_vert.h"
#include "simple_textured_frag.h"
@@ -32,6 +32,7 @@
#include "deferred_light_vert.h"
#include "deferred_light_limited_vert.h"
+#include "deferred_light_spot_vert.h"
#include "directional_light_frag.h"
#include "directional_light_shadow_map_frag.h"
@@ -50,59 +51,80 @@
static const std::string glowIntensityShaderHandle = "glowIntensity";
+gpu::PipelinePointer DeferredLightingEffect::getPipeline(SimpleProgramKey config) {
+ auto it = _simplePrograms.find(config);
+ if (it != _simplePrograms.end()) {
+ return it.value();
+ }
+
+ gpu::StatePointer state = gpu::StatePointer(new gpu::State());
+ if (config.isCulled()) {
+ state->setCullMode(gpu::State::CULL_BACK);
+ } else {
+ state->setCullMode(gpu::State::CULL_NONE);
+ }
+ state->setDepthTest(true, true, gpu::LESS_EQUAL);
+ if (config.hasDepthBias()) {
+ state->setDepthBias(1.0f);
+ state->setDepthBiasSlopeScale(1.0f);
+ }
+ state->setBlendFunction(false,
+ gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
+ gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
+
+ gpu::ShaderPointer program = (config.isEmissive()) ? _emissiveShader : _simpleShader;
+ gpu::PipelinePointer pipeline = gpu::PipelinePointer(gpu::Pipeline::create(program, state));
+ _simplePrograms.insert(config, pipeline);
+ return pipeline;
+}
+
void DeferredLightingEffect::init(AbstractViewStateInterface* viewState) {
auto VS = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(simple_vert)));
auto PS = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(simple_textured_frag)));
auto PSEmissive = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(simple_textured_emisive_frag)));
- gpu::ShaderPointer program = gpu::ShaderPointer(gpu::Shader::createProgram(VS, PS));
- gpu::ShaderPointer programEmissive = gpu::ShaderPointer(gpu::Shader::createProgram(VS, PSEmissive));
+ _simpleShader = gpu::ShaderPointer(gpu::Shader::createProgram(VS, PS));
+ _emissiveShader = gpu::ShaderPointer(gpu::Shader::createProgram(VS, PSEmissive));
gpu::Shader::BindingSet slotBindings;
- gpu::Shader::makeProgram(*program, slotBindings);
- gpu::Shader::makeProgram(*programEmissive, slotBindings);
-
- gpu::StatePointer state = gpu::StatePointer(new gpu::State());
- state->setCullMode(gpu::State::CULL_BACK);
- state->setDepthTest(true, true, gpu::LESS_EQUAL);
- state->setBlendFunction(false,
- gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
- gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
-
-
- gpu::StatePointer stateCullNone = gpu::StatePointer(new gpu::State());
- stateCullNone->setCullMode(gpu::State::CULL_NONE);
- stateCullNone->setDepthTest(true, true, gpu::LESS_EQUAL);
- stateCullNone->setBlendFunction(false,
- gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
- gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
-
- _simpleProgram = gpu::PipelinePointer(gpu::Pipeline::create(program, state));
- _simpleProgramCullNone = gpu::PipelinePointer(gpu::Pipeline::create(program, stateCullNone));
- _simpleProgramEmissive = gpu::PipelinePointer(gpu::Pipeline::create(programEmissive, state));
- _simpleProgramEmissiveCullNone = gpu::PipelinePointer(gpu::Pipeline::create(programEmissive, stateCullNone));
+ gpu::Shader::makeProgram(*_simpleShader, slotBindings);
+ gpu::Shader::makeProgram(*_emissiveShader, slotBindings);
_viewState = viewState;
- loadLightProgram(directional_light_frag, false, _directionalLight, _directionalLightLocations);
- loadLightProgram(directional_light_shadow_map_frag, false, _directionalLightShadowMap,
+ loadLightProgram(deferred_light_vert, directional_light_frag, false, _directionalLight, _directionalLightLocations);
+ loadLightProgram(deferred_light_vert, directional_light_shadow_map_frag, false, _directionalLightShadowMap,
_directionalLightShadowMapLocations);
- loadLightProgram(directional_light_cascaded_shadow_map_frag, false, _directionalLightCascadedShadowMap,
+ loadLightProgram(deferred_light_vert, directional_light_cascaded_shadow_map_frag, false, _directionalLightCascadedShadowMap,
_directionalLightCascadedShadowMapLocations);
- loadLightProgram(directional_ambient_light_frag, false, _directionalAmbientSphereLight, _directionalAmbientSphereLightLocations);
- loadLightProgram(directional_ambient_light_shadow_map_frag, false, _directionalAmbientSphereLightShadowMap,
+ loadLightProgram(deferred_light_vert, directional_ambient_light_frag, false, _directionalAmbientSphereLight, _directionalAmbientSphereLightLocations);
+ loadLightProgram(deferred_light_vert, directional_ambient_light_shadow_map_frag, false, _directionalAmbientSphereLightShadowMap,
_directionalAmbientSphereLightShadowMapLocations);
- loadLightProgram(directional_ambient_light_cascaded_shadow_map_frag, false, _directionalAmbientSphereLightCascadedShadowMap,
+ loadLightProgram(deferred_light_vert, directional_ambient_light_cascaded_shadow_map_frag, false, _directionalAmbientSphereLightCascadedShadowMap,
_directionalAmbientSphereLightCascadedShadowMapLocations);
- loadLightProgram(directional_skybox_light_frag, false, _directionalSkyboxLight, _directionalSkyboxLightLocations);
- loadLightProgram(directional_skybox_light_shadow_map_frag, false, _directionalSkyboxLightShadowMap,
+ loadLightProgram(deferred_light_vert, directional_skybox_light_frag, false, _directionalSkyboxLight, _directionalSkyboxLightLocations);
+ loadLightProgram(deferred_light_vert, directional_skybox_light_shadow_map_frag, false, _directionalSkyboxLightShadowMap,
_directionalSkyboxLightShadowMapLocations);
- loadLightProgram(directional_skybox_light_cascaded_shadow_map_frag, false, _directionalSkyboxLightCascadedShadowMap,
+ loadLightProgram(deferred_light_vert, directional_skybox_light_cascaded_shadow_map_frag, false, _directionalSkyboxLightCascadedShadowMap,
_directionalSkyboxLightCascadedShadowMapLocations);
- loadLightProgram(point_light_frag, true, _pointLight, _pointLightLocations);
- loadLightProgram(spot_light_frag, true, _spotLight, _spotLightLocations);
+
+ loadLightProgram(deferred_light_limited_vert, point_light_frag, true, _pointLight, _pointLightLocations);
+ loadLightProgram(deferred_light_spot_vert, spot_light_frag, true, _spotLight, _spotLightLocations);
+
+ {
+ auto VSFS = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS();
+ auto PSBlit = gpu::StandardShaderLib::getDrawTexturePS();
+ auto blitProgram = gpu::ShaderPointer(gpu::Shader::createProgram(VSFS, PSBlit));
+ gpu::Shader::makeProgram(*blitProgram);
+ gpu::StatePointer blitState = gpu::StatePointer(new gpu::State());
+ blitState->setBlendFunction(true,
+ gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
+ gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
+ blitState->setColorWriteMask(true, true, true, false);
+ _blitLightBuffer = gpu::PipelinePointer(gpu::Pipeline::create(blitProgram, blitState));
+ }
// Allocate a global light representing the Global Directional light casting shadow (the sun) and the ambient light
_globalLights.push_back(0);
@@ -117,23 +139,14 @@ void DeferredLightingEffect::init(AbstractViewStateInterface* viewState) {
lp->setAmbientSpherePreset(gpu::SphericalHarmonics::Preset(_ambientLightMode % gpu::SphericalHarmonics::NUM_PRESET));
}
-void DeferredLightingEffect::bindSimpleProgram(gpu::Batch& batch, bool textured, bool culled, bool emmisive) {
- if (emmisive) {
- if (culled) {
- batch.setPipeline(_simpleProgramEmissive);
- } else {
- batch.setPipeline(_simpleProgramEmissiveCullNone);
- }
- } else {
- if (culled) {
- batch.setPipeline(_simpleProgram);
- } else {
- batch.setPipeline(_simpleProgramCullNone);
- }
- }
- if (!textured) {
+void DeferredLightingEffect::bindSimpleProgram(gpu::Batch& batch, bool textured, bool culled,
+ bool emmisive, bool depthBias) {
+ SimpleProgramKey config{textured, culled, emmisive, depthBias};
+ batch.setPipeline(getPipeline(config));
+
+ if (!config.isTextured()) {
// If it is not textured, bind white texture and keep using textured pipeline
- batch.setUniformTexture(0, DependencyManager::get()->getWhiteTexture());
+ batch.setResourceTexture(0, DependencyManager::get()->getWhiteTexture());
}
}
@@ -203,51 +216,40 @@ void DeferredLightingEffect::addSpotLight(const glm::vec3& position, float radiu
}
void DeferredLightingEffect::prepare(RenderArgs* args) {
- // clear the normal and specular buffers
+
auto textureCache = DependencyManager::get();
- textureCache->setPrimaryDrawBuffers(false, true, false);
- glClear(GL_COLOR_BUFFER_BIT);
- textureCache->setPrimaryDrawBuffers(false, false, true);
- // clearing to zero alpha for specular causes problems on my Nvidia card; clear to lowest non-zero value instead
+ gpu::Batch batch;
+
+ // clear the normal and specular buffers
+ batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR1, glm::vec4(0.0f, 0.0f, 0.0f, 0.0f));
const float MAX_SPECULAR_EXPONENT = 128.0f;
- glClearColor(0.0f, 0.0f, 0.0f, 1.0f / MAX_SPECULAR_EXPONENT);
- glClear(GL_COLOR_BUFFER_BIT);
- glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
- textureCache->setPrimaryDrawBuffers(true, false, false);
+ batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR2, glm::vec4(0.0f, 0.0f, 0.0f, 1.0f / MAX_SPECULAR_EXPONENT));
+
+ args->_context->syncCache();
+ args->_context->render(batch);
}
void DeferredLightingEffect::render(RenderArgs* args) {
- // perform deferred lighting, rendering to free fbo
- glDisable(GL_BLEND);
- glDisable(GL_LIGHTING);
- glDisable(GL_DEPTH_TEST);
- glDisable(GL_COLOR_MATERIAL);
- glDepthMask(false);
+ gpu::Batch batch;
+ // perform deferred lighting, rendering to free fbo
auto textureCache = DependencyManager::get();
- glBindFramebuffer(GL_FRAMEBUFFER, 0 );
-
QSize framebufferSize = textureCache->getFrameBufferSize();
// binding the first framebuffer
auto freeFBO = DependencyManager::get()->getFreeFramebuffer();
- glBindFramebuffer(GL_FRAMEBUFFER, gpu::GLBackend::getFramebufferID(freeFBO));
+ batch.setFramebuffer(freeFBO);
- glClear(GL_COLOR_BUFFER_BIT);
- // glEnable(GL_FRAMEBUFFER_SRGB);
+ batch.clearColorFramebuffer(freeFBO->getBufferMask(), glm::vec4(0.0f, 0.0f, 0.0f, 0.0f));
+
+ batch.setResourceTexture(0, textureCache->getPrimaryColorTexture());
- // glBindTexture(GL_TEXTURE_2D, primaryFBO->texture());
- glBindTexture(GL_TEXTURE_2D, textureCache->getPrimaryColorTextureID());
+ batch.setResourceTexture(1, textureCache->getPrimaryNormalTexture());
- glActiveTexture(GL_TEXTURE1);
- glBindTexture(GL_TEXTURE_2D, textureCache->getPrimaryNormalTextureID());
+ batch.setResourceTexture(2, textureCache->getPrimarySpecularTexture());
- glActiveTexture(GL_TEXTURE2);
- glBindTexture(GL_TEXTURE_2D, textureCache->getPrimarySpecularTextureID());
-
- glActiveTexture(GL_TEXTURE3);
- glBindTexture(GL_TEXTURE_2D, textureCache->getPrimaryDepthTextureID());
+ batch.setResourceTexture(3, textureCache->getPrimaryDepthTexture());
// get the viewport side (left, right, both)
int viewport[4];
@@ -266,55 +268,53 @@ void DeferredLightingEffect::render(RenderArgs* args) {
// Fetch the ViewMatrix;
glm::mat4 invViewMat;
- _viewState->getViewTransform().getMatrix(invViewMat);
+ invViewMat = args->_viewFrustum->getView();
- ProgramObject* program = &_directionalLight;
+ auto& program = _directionalLight;
const LightLocations* locations = &_directionalLightLocations;
bool shadowsEnabled = _viewState->getShadowsEnabled();
if (shadowsEnabled) {
- glActiveTexture(GL_TEXTURE4);
- glBindTexture(GL_TEXTURE_2D, textureCache->getShadowDepthTextureID());
+ batch.setResourceTexture(4, textureCache->getShadowFramebuffer()->getDepthStencilBuffer());
- program = &_directionalLightShadowMap;
+ program = _directionalLightShadowMap;
locations = &_directionalLightShadowMapLocations;
if (_viewState->getCascadeShadowsEnabled()) {
- program = &_directionalLightCascadedShadowMap;
+ program = _directionalLightCascadedShadowMap;
locations = &_directionalLightCascadedShadowMapLocations;
if (useSkyboxCubemap) {
- program = &_directionalSkyboxLightCascadedShadowMap;
+ program = _directionalSkyboxLightCascadedShadowMap;
locations = &_directionalSkyboxLightCascadedShadowMapLocations;
} else if (_ambientLightMode > -1) {
- program = &_directionalAmbientSphereLightCascadedShadowMap;
+ program = _directionalAmbientSphereLightCascadedShadowMap;
locations = &_directionalAmbientSphereLightCascadedShadowMapLocations;
}
- program->bind();
- program->setUniform(locations->shadowDistances, _viewState->getShadowDistances());
+ batch.setPipeline(program);
+ batch._glUniform3fv(locations->shadowDistances, 1, (const GLfloat*) &_viewState->getShadowDistances());
} else {
if (useSkyboxCubemap) {
- program = &_directionalSkyboxLightShadowMap;
+ program = _directionalSkyboxLightShadowMap;
locations = &_directionalSkyboxLightShadowMapLocations;
} else if (_ambientLightMode > -1) {
- program = &_directionalAmbientSphereLightShadowMap;
+ program = _directionalAmbientSphereLightShadowMap;
locations = &_directionalAmbientSphereLightShadowMapLocations;
}
- program->bind();
+ batch.setPipeline(program);
}
- program->setUniformValue(locations->shadowScale,
- 1.0f / textureCache->getShadowFramebuffer()->getWidth());
+ batch._glUniform1f(locations->shadowScale, 1.0f / textureCache->getShadowFramebuffer()->getWidth());
} else {
if (useSkyboxCubemap) {
- program = &_directionalSkyboxLight;
+ program = _directionalSkyboxLight;
locations = &_directionalSkyboxLightLocations;
} else if (_ambientLightMode > -1) {
- program = &_directionalAmbientSphereLight;
+ program = _directionalAmbientSphereLight;
locations = &_directionalAmbientSphereLightLocations;
}
- program->bind();
+ batch.setPipeline(program);
}
- {
+ { // Setup the global lighting
auto globalLight = _allocatedLights[_globalLights.front()];
if (locations->ambientSphere >= 0) {
@@ -323,221 +323,239 @@ void DeferredLightingEffect::render(RenderArgs* args) {
sh = (*_skybox->getCubemap()->getIrradiance());
}
for (int i =0; i setUniformValue(locations->ambientSphere + i, *(((QVector4D*) &sh) + i));
+ batch._glUniform4fv(locations->ambientSphere + i, 1, (const GLfloat*) (&sh) + i * 4);
}
}
if (useSkyboxCubemap) {
- glActiveTexture(GL_TEXTURE5);
- glBindTexture(GL_TEXTURE_CUBE_MAP, gpu::GLBackend::getTextureID(_skybox->getCubemap()));
+ batch.setResourceTexture(5, _skybox->getCubemap());
}
if (locations->lightBufferUnit >= 0) {
- gpu::Batch batch;
batch.setUniformBuffer(locations->lightBufferUnit, globalLight->getSchemaBuffer());
- gpu::GLBackend::renderBatch(batch);
}
if (_atmosphere && (locations->atmosphereBufferUnit >= 0)) {
- gpu::Batch batch;
batch.setUniformBuffer(locations->atmosphereBufferUnit, _atmosphere->getDataBuffer());
- gpu::GLBackend::renderBatch(batch);
}
- glUniformMatrix4fv(locations->invViewMat, 1, false, reinterpret_cast< const GLfloat* >(&invViewMat));
+ batch._glUniformMatrix4fv(locations->invViewMat, 1, false, reinterpret_cast< const GLfloat* >(&invViewMat));
}
- auto viewFrustum = _viewState->getCurrentViewFrustum();
+ auto& viewFrustum = args->_viewFrustum;
float left, right, bottom, top, nearVal, farVal;
glm::vec4 nearClipPlane, farClipPlane;
viewFrustum->computeOffAxisFrustum(left, right, bottom, top, nearVal, farVal, nearClipPlane, farClipPlane);
- program->setUniformValue(locations->nearLocation, nearVal);
+ batch._glUniform1f(locations->nearLocation, nearVal);
float depthScale = (farVal - nearVal) / farVal;
- program->setUniformValue(locations->depthScale, depthScale);
+ batch._glUniform1f(locations->depthScale, depthScale);
+
float nearScale = -1.0f / nearVal;
float depthTexCoordScaleS = (right - left) * nearScale / sWidth;
float depthTexCoordScaleT = (top - bottom) * nearScale / tHeight;
float depthTexCoordOffsetS = left * nearScale - sMin * depthTexCoordScaleS;
float depthTexCoordOffsetT = bottom * nearScale - tMin * depthTexCoordScaleT;
- program->setUniformValue(locations->depthTexCoordOffset, depthTexCoordOffsetS, depthTexCoordOffsetT);
- program->setUniformValue(locations->depthTexCoordScale, depthTexCoordScaleS, depthTexCoordScaleT);
+ batch._glUniform2f(locations->depthTexCoordOffset, depthTexCoordOffsetS, depthTexCoordOffsetT);
+ batch._glUniform2f(locations->depthTexCoordScale, depthTexCoordScaleS, depthTexCoordScaleT);
- renderFullscreenQuad(sMin, sMin + sWidth, tMin, tMin + tHeight);
-
- program->release();
+ {
+ Transform model;
+ model.setTranslation(glm::vec3(sMin, tMin, 0.0));
+ model.setScale(glm::vec3(sWidth, tHeight, 1.0));
+ batch.setModelTransform(model);
+
+ batch.setProjectionTransform(glm::mat4());
+ batch.setViewTransform(Transform());
+
+ glm::vec4 color(1.0f, 1.0f, 1.0f, 1.0f);
+ glm::vec2 topLeft(-1.0f, -1.0f);
+ glm::vec2 bottomRight(1.0f, 1.0f);
+ glm::vec2 texCoordTopLeft(sMin, tMin);
+ glm::vec2 texCoordBottomRight(sMin + sWidth, tMin + tHeight);
+
+ DependencyManager::get()->renderQuad(batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, color);
+ }
if (useSkyboxCubemap) {
- glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
- if (!shadowsEnabled) {
- glActiveTexture(GL_TEXTURE3);
- }
+ batch.setResourceTexture(5, nullptr);
}
if (shadowsEnabled) {
- glBindTexture(GL_TEXTURE_2D, 0);
- glActiveTexture(GL_TEXTURE3);
+ batch.setResourceTexture(4, nullptr);
}
-
- // additive blending
- glEnable(GL_BLEND);
- glBlendFunc(GL_ONE, GL_ONE);
-
- glEnable(GL_CULL_FACE);
-
+
glm::vec4 sCoefficients(sWidth / 2.0f, 0.0f, 0.0f, sMin + sWidth / 2.0f);
glm::vec4 tCoefficients(0.0f, tHeight / 2.0f, 0.0f, tMin + tHeight / 2.0f);
- glTexGenfv(GL_S, GL_OBJECT_PLANE, (const GLfloat*)&sCoefficients);
- glTexGenfv(GL_T, GL_OBJECT_PLANE, (const GLfloat*)&tCoefficients);
-
+ auto texcoordMat = glm::mat4();
+ texcoordMat[0] = glm::vec4(sWidth / 2.0f, 0.0f, 0.0f, sMin + sWidth / 2.0f);
+ texcoordMat[1] = glm::vec4(0.0f, tHeight / 2.0f, 0.0f, tMin + tHeight / 2.0f);
+ texcoordMat[2] = glm::vec4(0.0f, 0.0f, 1.0f, 0.0f);
+ texcoordMat[3] = glm::vec4(0.0f, 0.0f, 0.0f, 1.0f);
+
// enlarge the scales slightly to account for tesselation
const float SCALE_EXPANSION = 0.05f;
-
+
const glm::vec3& eyePoint = viewFrustum->getPosition();
float nearRadius = glm::distance(eyePoint, viewFrustum->getNearTopLeft());
auto geometryCache = DependencyManager::get();
+ glm::mat4 projMat;
+ Transform viewMat;
+ args->_viewFrustum->evalProjectionMatrix(projMat);
+ args->_viewFrustum->evalViewTransform(viewMat);
+ if (args->_renderMode == RenderArgs::MIRROR_RENDER_MODE) {
+ viewMat.postScale(glm::vec3(-1.0f, 1.0f, 1.0f));
+ }
+ batch.setProjectionTransform(projMat);
+ batch.setViewTransform(viewMat);
+
if (!_pointLights.empty()) {
- _pointLight.bind();
- _pointLight.setUniformValue(_pointLightLocations.nearLocation, nearVal);
- _pointLight.setUniformValue(_pointLightLocations.depthScale, depthScale);
- _pointLight.setUniformValue(_pointLightLocations.depthTexCoordOffset, depthTexCoordOffsetS, depthTexCoordOffsetT);
- _pointLight.setUniformValue(_pointLightLocations.depthTexCoordScale, depthTexCoordScaleS, depthTexCoordScaleT);
+ batch.setPipeline(_pointLight);
+ batch._glUniform1f(_pointLightLocations.nearLocation, nearVal);
+ batch._glUniform1f(_pointLightLocations.depthScale, depthScale);
+ batch._glUniform2f(_pointLightLocations.depthTexCoordOffset, depthTexCoordOffsetS, depthTexCoordOffsetT);
+ batch._glUniform2f(_pointLightLocations.depthTexCoordScale, depthTexCoordScaleS, depthTexCoordScaleT);
+
+ batch._glUniformMatrix4fv(_pointLightLocations.invViewMat, 1, false, reinterpret_cast< const GLfloat* >(&invViewMat));
+
+ batch._glUniformMatrix4fv(_pointLightLocations.texcoordMat, 1, false, reinterpret_cast< const GLfloat* >(&texcoordMat));
for (auto lightID : _pointLights) {
- auto light = _allocatedLights[lightID];
-
+ auto& light = _allocatedLights[lightID];
+ // IN DEBUG: light->setShowContour(true);
if (_pointLightLocations.lightBufferUnit >= 0) {
- gpu::Batch batch;
batch.setUniformBuffer(_pointLightLocations.lightBufferUnit, light->getSchemaBuffer());
- gpu::GLBackend::renderBatch(batch);
}
- glUniformMatrix4fv(_pointLightLocations.invViewMat, 1, false, reinterpret_cast< const GLfloat* >(&invViewMat));
- glPushMatrix();
-
float expandedRadius = light->getMaximumRadius() * (1.0f + SCALE_EXPANSION);
+ // TODO: We shouldn;t have to do that test and use a different volume geometry for when inside the vlight volume,
+ // we should be able to draw thre same geometry use DepthClamp but for unknown reason it's s not working...
if (glm::distance(eyePoint, glm::vec3(light->getPosition())) < expandedRadius + nearRadius) {
- glLoadIdentity();
- glTranslatef(0.0f, 0.0f, -1.0f);
-
- glMatrixMode(GL_PROJECTION);
- glPushMatrix();
- glLoadIdentity();
-
- renderFullscreenQuad();
-
- glPopMatrix();
- glMatrixMode(GL_MODELVIEW);
+ Transform model;
+ model.setTranslation(glm::vec3(0.0f, 0.0f, -1.0f));
+ batch.setModelTransform(model);
+ batch.setViewTransform(Transform());
+ batch.setProjectionTransform(glm::mat4());
+
+ glm::vec4 color(1.0f, 1.0f, 1.0f, 1.0f);
+ glm::vec2 topLeft(-1.0f, -1.0f);
+ glm::vec2 bottomRight(1.0f, 1.0f);
+ glm::vec2 texCoordTopLeft(sMin, tMin);
+ glm::vec2 texCoordBottomRight(sMin + sWidth, tMin + tHeight);
+
+ DependencyManager::get()->renderQuad(batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, color);
+ batch.setProjectionTransform(projMat);
+ batch.setViewTransform(viewMat);
} else {
- glTranslatef(light->getPosition().x, light->getPosition().y, light->getPosition().z);
- geometryCache->renderSphere(expandedRadius, 32, 32, glm::vec4(1.0f, 1.0f, 1.0f, 1.0f));
+ Transform model;
+ model.setTranslation(glm::vec3(light->getPosition().x, light->getPosition().y, light->getPosition().z));
+ batch.setModelTransform(model);
+ geometryCache->renderSphere(batch, expandedRadius, 32, 32, glm::vec4(1.0f, 1.0f, 1.0f, 1.0f));
}
-
- glPopMatrix();
}
_pointLights.clear();
-
- _pointLight.release();
}
if (!_spotLights.empty()) {
- _spotLight.bind();
- _spotLight.setUniformValue(_spotLightLocations.nearLocation, nearVal);
- _spotLight.setUniformValue(_spotLightLocations.depthScale, depthScale);
- _spotLight.setUniformValue(_spotLightLocations.depthTexCoordOffset, depthTexCoordOffsetS, depthTexCoordOffsetT);
- _spotLight.setUniformValue(_spotLightLocations.depthTexCoordScale, depthTexCoordScaleS, depthTexCoordScaleT);
+ batch.setPipeline(_spotLight);
+ batch._glUniform1f(_spotLightLocations.nearLocation, nearVal);
+ batch._glUniform1f(_spotLightLocations.depthScale, depthScale);
+ batch._glUniform2f(_spotLightLocations.depthTexCoordOffset, depthTexCoordOffsetS, depthTexCoordOffsetT);
+ batch._glUniform2f(_spotLightLocations.depthTexCoordScale, depthTexCoordScaleS, depthTexCoordScaleT);
+ batch._glUniformMatrix4fv(_spotLightLocations.invViewMat, 1, false, reinterpret_cast< const GLfloat* >(&invViewMat));
+
+ batch._glUniformMatrix4fv(_spotLightLocations.texcoordMat, 1, false, reinterpret_cast< const GLfloat* >(&texcoordMat));
+
for (auto lightID : _spotLights) {
auto light = _allocatedLights[lightID];
+ // IN DEBUG: light->setShowContour(true);
- if (_spotLightLocations.lightBufferUnit >= 0) {
- gpu::Batch batch;
- batch.setUniformBuffer(_spotLightLocations.lightBufferUnit, light->getSchemaBuffer());
- gpu::GLBackend::renderBatch(batch);
- }
- glUniformMatrix4fv(_spotLightLocations.invViewMat, 1, false, reinterpret_cast< const GLfloat* >(&invViewMat));
+ batch.setUniformBuffer(_spotLightLocations.lightBufferUnit, light->getSchemaBuffer());
+
+ auto eyeLightPos = eyePoint - light->getPosition();
+ auto eyeHalfPlaneDistance = glm::dot(eyeLightPos, light->getDirection());
+
+ const float TANGENT_LENGTH_SCALE = 0.666f;
+ glm::vec4 coneParam(light->getSpotAngleCosSin(), TANGENT_LENGTH_SCALE * tanf(0.5f * light->getSpotAngle()), 1.0f);
- glPushMatrix();
-
float expandedRadius = light->getMaximumRadius() * (1.0f + SCALE_EXPANSION);
- float edgeRadius = expandedRadius / glm::cos(light->getSpotAngle());
- if (glm::distance(eyePoint, glm::vec3(light->getPosition())) < edgeRadius + nearRadius) {
- glLoadIdentity();
- glTranslatef(0.0f, 0.0f, -1.0f);
+ // TODO: We shouldn;t have to do that test and use a different volume geometry for when inside the vlight volume,
+ // we should be able to draw thre same geometry use DepthClamp but for unknown reason it's s not working...
+ if ((eyeHalfPlaneDistance > -nearRadius) &&
+ (glm::distance(eyePoint, glm::vec3(light->getPosition())) < expandedRadius + nearRadius)) {
+ coneParam.w = 0.0f;
+ batch._glUniform4fv(_spotLightLocations.coneParam, 1, reinterpret_cast< const GLfloat* >(&coneParam));
+
+ Transform model;
+ model.setTranslation(glm::vec3(0.0f, 0.0f, -1.0f));
+ batch.setModelTransform(model);
+ batch.setViewTransform(Transform());
+ batch.setProjectionTransform(glm::mat4());
- glMatrixMode(GL_PROJECTION);
- glPushMatrix();
- glLoadIdentity();
-
- renderFullscreenQuad();
-
- glPopMatrix();
- glMatrixMode(GL_MODELVIEW);
+ glm::vec4 color(1.0f, 1.0f, 1.0f, 1.0f);
+ glm::vec2 topLeft(-1.0f, -1.0f);
+ glm::vec2 bottomRight(1.0f, 1.0f);
+ glm::vec2 texCoordTopLeft(sMin, tMin);
+ glm::vec2 texCoordBottomRight(sMin + sWidth, tMin + tHeight);
+
+ DependencyManager::get()->renderQuad(batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, color);
+ batch.setProjectionTransform(projMat);
+ batch.setViewTransform(viewMat);
} else {
- glTranslatef(light->getPosition().x, light->getPosition().y, light->getPosition().z);
- glm::quat spotRotation = rotationBetween(glm::vec3(0.0f, 0.0f, -1.0f), light->getDirection());
- glm::vec3 axis = glm::axis(spotRotation);
- glRotatef(glm::degrees(glm::angle(spotRotation)), axis.x, axis.y, axis.z);
- glTranslatef(0.0f, 0.0f, -light->getMaximumRadius() * (1.0f + SCALE_EXPANSION * 0.5f));
- geometryCache->renderCone(expandedRadius * glm::tan(light->getSpotAngle()),
- expandedRadius, 32, 1);
+ coneParam.w = 1.0f;
+ batch._glUniform4fv(_spotLightLocations.coneParam, 1, reinterpret_cast< const GLfloat* >(&coneParam));
+
+ Transform model;
+ model.setTranslation(light->getPosition());
+ model.postRotate(light->getOrientation());
+ model.postScale(glm::vec3(expandedRadius, expandedRadius, expandedRadius));
+
+ batch.setModelTransform(model);
+ auto mesh = getSpotLightMesh();
+
+
+ batch.setIndexBuffer(mesh->getIndexBuffer());
+ batch.setInputBuffer(0, mesh->getVertexBuffer());
+ batch.setInputFormat(mesh->getVertexFormat());
+
+ auto& part = mesh->getPartBuffer().get();
+
+ batch.drawIndexed(model::Mesh::topologyToPrimitive(part._topology), part._numIndices, part._startIndex);
}
-
- glPopMatrix();
}
_spotLights.clear();
-
- _spotLight.release();
}
- glBindTexture(GL_TEXTURE_2D, 0);
-
- glActiveTexture(GL_TEXTURE2);
- glBindTexture(GL_TEXTURE_2D, 0);
-
- glActiveTexture(GL_TEXTURE1);
- glBindTexture(GL_TEXTURE_2D, 0);
-
- glActiveTexture(GL_TEXTURE0);
- glBindTexture(GL_TEXTURE_2D, 0);
- // glDisable(GL_FRAMEBUFFER_SRGB);
-
+ // Probably not necessary in the long run because the gpu layer would unbound this texture if used as render target
+ batch.setResourceTexture(0, nullptr);
+ batch.setResourceTexture(1, nullptr);
+ batch.setResourceTexture(2, nullptr);
+ batch.setResourceTexture(3, nullptr);
+
+ args->_context->syncCache();
+ args->_context->render(batch);
+
// End of the Lighting pass
}
void DeferredLightingEffect::copyBack(RenderArgs* args) {
+ gpu::Batch batch;
auto textureCache = DependencyManager::get();
QSize framebufferSize = textureCache->getFrameBufferSize();
auto freeFBO = DependencyManager::get()->getFreeFramebuffer();
- //freeFBO->release();
- glBindFramebuffer(GL_FRAMEBUFFER, 0);
+ batch.setFramebuffer(textureCache->getPrimaryFramebuffer());
+ batch.setPipeline(_blitLightBuffer);
+
+ batch.setResourceTexture(0, freeFBO->getRenderBuffer(0));
-
- glDisable(GL_CULL_FACE);
-
- // now transfer the lit region to the primary fbo
- glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE);
- glColorMask(true, true, true, false);
-
- auto primaryFBO = gpu::GLBackend::getFramebufferID(textureCache->getPrimaryFramebuffer());
- glBindFramebuffer(GL_DRAW_FRAMEBUFFER, primaryFBO);
+ batch.setProjectionTransform(glm::mat4());
+ batch.setViewTransform(Transform());
- //primaryFBO->bind();
-
- glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(freeFBO->getRenderBuffer(0)));
- glEnable(GL_TEXTURE_2D);
-
- glPushMatrix();
- glLoadIdentity();
-
- glMatrixMode(GL_PROJECTION);
- glPushMatrix();
- glLoadIdentity();
-
int viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport);
const int VIEWPORT_X_INDEX = 0;
@@ -550,21 +568,19 @@ void DeferredLightingEffect::copyBack(RenderArgs* args) {
float tMin = viewport[VIEWPORT_Y_INDEX] / (float)framebufferSize.height();
float tHeight = viewport[VIEWPORT_HEIGHT_INDEX] / (float)framebufferSize.height();
- renderFullscreenQuad(sMin, sMin + sWidth, tMin, tMin + tHeight);
-
- glBindTexture(GL_TEXTURE_2D, 0);
- glDisable(GL_TEXTURE_2D);
-
- glColorMask(true, true, true, true);
- glEnable(GL_LIGHTING);
- glEnable(GL_COLOR_MATERIAL);
- glEnable(GL_DEPTH_TEST);
- glDepthMask(true);
-
- glPopMatrix();
-
- glMatrixMode(GL_MODELVIEW);
- glPopMatrix();
+ Transform model;
+ model.setTranslation(glm::vec3(sMin, tMin, 0.0));
+ model.setScale(glm::vec3(sWidth, tHeight, 1.0));
+ batch.setModelTransform(model);
+
+
+ batch.setViewportTransform(glm::ivec4(viewport[0], viewport[1], viewport[2], viewport[3]));
+
+ batch.draw(gpu::TRIANGLE_STRIP, 4);
+
+
+ args->_context->syncCache();
+ args->_context->render(batch);
}
void DeferredLightingEffect::setupTransparent(RenderArgs* args, int lightBufferUnit) {
@@ -572,67 +588,62 @@ void DeferredLightingEffect::setupTransparent(RenderArgs* args, int lightBufferU
args->_batch->setUniformBuffer(lightBufferUnit, globalLight->getSchemaBuffer());
}
-void DeferredLightingEffect::loadLightProgram(const char* fragSource, bool limited, ProgramObject& program, LightLocations& locations) {
- program.addShaderFromSourceCode(QGLShader::Vertex, (limited ? deferred_light_limited_vert : deferred_light_vert));
- program.addShaderFromSourceCode(QGLShader::Fragment, fragSource);
- program.link();
+void DeferredLightingEffect::loadLightProgram(const char* vertSource, const char* fragSource, bool lightVolume, gpu::PipelinePointer& pipeline, LightLocations& locations) {
+ auto VS = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(vertSource)));
+ auto PS = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(fragSource)));
- program.bind();
- program.setUniformValue("diffuseMap", 0);
- program.setUniformValue("normalMap", 1);
- program.setUniformValue("specularMap", 2);
- program.setUniformValue("depthMap", 3);
- program.setUniformValue("shadowMap", 4);
- program.setUniformValue("skyboxMap", 5);
- locations.shadowDistances = program.uniformLocation("shadowDistances");
- locations.shadowScale = program.uniformLocation("shadowScale");
- locations.nearLocation = program.uniformLocation("near");
- locations.depthScale = program.uniformLocation("depthScale");
- locations.depthTexCoordOffset = program.uniformLocation("depthTexCoordOffset");
- locations.depthTexCoordScale = program.uniformLocation("depthTexCoordScale");
- locations.radius = program.uniformLocation("radius");
- locations.ambientSphere = program.uniformLocation("ambientSphere.L00");
- locations.invViewMat = program.uniformLocation("invViewMat");
+ gpu::ShaderPointer program = gpu::ShaderPointer(gpu::Shader::createProgram(VS, PS));
- GLint loc = -1;
-
-#if (GPU_FEATURE_PROFILE == GPU_CORE)
+ gpu::Shader::BindingSet slotBindings;
+ slotBindings.insert(gpu::Shader::Binding(std::string("diffuseMap"), 0));
+ slotBindings.insert(gpu::Shader::Binding(std::string("normalMap"), 1));
+ slotBindings.insert(gpu::Shader::Binding(std::string("specularMap"), 2));
+ slotBindings.insert(gpu::Shader::Binding(std::string("depthMap"), 3));
+ slotBindings.insert(gpu::Shader::Binding(std::string("shadowMap"), 4));
+ slotBindings.insert(gpu::Shader::Binding(std::string("skyboxMap"), 5));
const GLint LIGHT_GPU_SLOT = 3;
- loc = glGetUniformBlockIndex(program.programId(), "lightBuffer");
- if (loc >= 0) {
- glUniformBlockBinding(program.programId(), loc, LIGHT_GPU_SLOT);
- locations.lightBufferUnit = LIGHT_GPU_SLOT;
- } else {
- locations.lightBufferUnit = -1;
- }
-#else
- loc = program.uniformLocation("lightBuffer");
- if (loc >= 0) {
- locations.lightBufferUnit = loc;
- } else {
- locations.lightBufferUnit = -1;
- }
-#endif
+ slotBindings.insert(gpu::Shader::Binding(std::string("lightBuffer"), LIGHT_GPU_SLOT));
+ const GLint ATMOSPHERE_GPU_SLOT = 4;
+ slotBindings.insert(gpu::Shader::Binding(std::string("atmosphereBufferUnit"), ATMOSPHERE_GPU_SLOT));
+
+ gpu::Shader::makeProgram(*program, slotBindings);
+
+ locations.shadowDistances = program->getUniforms().findLocation("shadowDistances");
+ locations.shadowScale = program->getUniforms().findLocation("shadowScale");
+ locations.nearLocation = program->getUniforms().findLocation("near");
+ locations.depthScale = program->getUniforms().findLocation("depthScale");
+ locations.depthTexCoordOffset = program->getUniforms().findLocation("depthTexCoordOffset");
+ locations.depthTexCoordScale = program->getUniforms().findLocation("depthTexCoordScale");
+ locations.radius = program->getUniforms().findLocation("radius");
+ locations.ambientSphere = program->getUniforms().findLocation("ambientSphere.L00");
+ locations.invViewMat = program->getUniforms().findLocation("invViewMat");
+ locations.texcoordMat = program->getUniforms().findLocation("texcoordMat");
+ locations.coneParam = program->getUniforms().findLocation("coneParam");
#if (GPU_FEATURE_PROFILE == GPU_CORE)
- const GLint ATMOSPHERE_GPU_SLOT = 4;
- loc = glGetUniformBlockIndex(program.programId(), "atmosphereBufferUnit");
- if (loc >= 0) {
- glUniformBlockBinding(program.programId(), loc, ATMOSPHERE_GPU_SLOT);
- locations.atmosphereBufferUnit = ATMOSPHERE_GPU_SLOT;
- } else {
- locations.atmosphereBufferUnit = -1;
- }
+ locations.lightBufferUnit = program->getBuffers().findLocation("lightBuffer");
+ locations.atmosphereBufferUnit = program->getBuffers().findLocation("atmosphereBufferUnit");
#else
- loc = program.uniformLocation("atmosphereBufferUnit");
- if (loc >= 0) {
- locations.atmosphereBufferUnit = loc;
- } else {
- locations.atmosphereBufferUnit = -1;
- }
+ locations.lightBufferUnit = program->getUniforms().findLocation("lightBuffer");
+ locations.atmosphereBufferUnit = program->getUniforms().findLocation("atmosphereBufferUnit");
#endif
- program.release();
+ gpu::StatePointer state = gpu::StatePointer(new gpu::State());
+ if (lightVolume) {
+ state->setCullMode(gpu::State::CULL_BACK);
+
+ // No need for z test since the depth buffer is not bound state->setDepthTest(true, false, gpu::LESS_EQUAL);
+ // TODO: We should bind the true depth buffer both as RT and texture for the depth test
+ // TODO: We should use DepthClamp and avoid changing geometry for inside /outside cases
+ state->setDepthClampEnable(true);
+
+ // additive blending
+ state->setBlendFunction(true, gpu::State::ONE, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
+ } else {
+ state->setCullMode(gpu::State::CULL_BACK);
+ }
+ pipeline.reset(gpu::Pipeline::create(program, state));
+
}
void DeferredLightingEffect::setAmbientLightMode(int preset) {
@@ -657,3 +668,100 @@ void DeferredLightingEffect::setGlobalLight(const glm::vec3& direction, const gl
void DeferredLightingEffect::setGlobalSkybox(const model::SkyboxPointer& skybox) {
_skybox = skybox;
}
+
+model::MeshPointer DeferredLightingEffect::getSpotLightMesh() {
+ if (!_spotLightMesh) {
+ _spotLightMesh.reset(new model::Mesh());
+
+ int slices = 32;
+ int rings = 3;
+ int vertices = 2 + rings * slices;
+ int originVertex = vertices - 2;
+ int capVertex = vertices - 1;
+ int verticesSize = vertices * 3 * sizeof(float);
+ int indices = 3 * slices * (1 + 1 + 2 * (rings -1));
+ int ringFloatOffset = slices * 3;
+
+
+ GLfloat* vertexData = new GLfloat[verticesSize];
+ GLfloat* vertexRing0 = vertexData;
+ GLfloat* vertexRing1 = vertexRing0 + ringFloatOffset;
+ GLfloat* vertexRing2 = vertexRing1 + ringFloatOffset;
+
+ for (int i = 0; i < slices; i++) {
+ float theta = TWO_PI * i / slices;
+ auto cosin = glm::vec2(cosf(theta), sinf(theta));
+
+ *(vertexRing0++) = cosin.x;
+ *(vertexRing0++) = cosin.y;
+ *(vertexRing0++) = 0.0f;
+
+ *(vertexRing1++) = cosin.x;
+ *(vertexRing1++) = cosin.y;
+ *(vertexRing1++) = 0.33f;
+
+ *(vertexRing2++) = cosin.x;
+ *(vertexRing2++) = cosin.y;
+ *(vertexRing2++) = 0.66f;
+ }
+
+ *(vertexRing2++) = 0.0f;
+ *(vertexRing2++) = 0.0f;
+ *(vertexRing2++) = -1.0f;
+
+ *(vertexRing2++) = 0.0f;
+ *(vertexRing2++) = 0.0f;
+ *(vertexRing2++) = 1.0f;
+
+ _spotLightMesh->setVertexBuffer(gpu::BufferView(new gpu::Buffer(verticesSize, (gpu::Byte*) vertexData), gpu::Element::VEC3F_XYZ));
+ delete[] vertexData;
+
+ gpu::uint16* indexData = new gpu::uint16[indices];
+ gpu::uint16* index = indexData;
+ for (int i = 0; i < slices; i++) {
+ *(index++) = originVertex;
+
+ int s0 = i;
+ int s1 = ((i + 1) % slices);
+ *(index++) = s0;
+ *(index++) = s1;
+
+ int s2 = s0 + slices;
+ int s3 = s1 + slices;
+ *(index++) = s1;
+ *(index++) = s0;
+ *(index++) = s2;
+
+ *(index++) = s1;
+ *(index++) = s2;
+ *(index++) = s3;
+
+ int s4 = s2 + slices;
+ int s5 = s3 + slices;
+ *(index++) = s3;
+ *(index++) = s2;
+ *(index++) = s4;
+
+ *(index++) = s3;
+ *(index++) = s4;
+ *(index++) = s5;
+
+
+ *(index++) = s5;
+ *(index++) = s4;
+ *(index++) = capVertex;
+ }
+
+ _spotLightMesh->setIndexBuffer(gpu::BufferView(new gpu::Buffer(sizeof(GLushort) * indices, (gpu::Byte*) indexData), gpu::Element::INDEX_UINT16));
+ delete[] indexData;
+
+ model::Mesh::Part part(0, indices, 0, model::Mesh::TRIANGLES);
+ //DEBUG: model::Mesh::Part part(0, indices, 0, model::Mesh::LINE_STRIP);
+
+ _spotLightMesh->setPartBuffer(gpu::BufferView(new gpu::Buffer(sizeof(part), (gpu::Byte*) &part), gpu::Element::PART_DRAWCALL));
+
+ _spotLightMesh->makeBufferStream();
+ }
+ return _spotLightMesh;
+}
+
diff --git a/libraries/render-utils/src/DeferredLightingEffect.h b/libraries/render-utils/src/DeferredLightingEffect.h
index d948f2c305..2c5ef6bb83 100644
--- a/libraries/render-utils/src/DeferredLightingEffect.h
+++ b/libraries/render-utils/src/DeferredLightingEffect.h
@@ -21,9 +21,11 @@
#include "model/Light.h"
#include "model/Stage.h"
+#include "model/Geometry.h"
class AbstractViewStateInterface;
class RenderArgs;
+class SimpleProgramKey;
/// Handles deferred lighting for the bits that require it (voxels...)
class DeferredLightingEffect : public Dependency {
@@ -34,7 +36,8 @@ public:
void init(AbstractViewStateInterface* viewState);
/// Sets up the state necessary to render static untextured geometry with the simple program.
- void bindSimpleProgram(gpu::Batch& batch, bool textured = false, bool culled = true, bool emmisive = false);
+ void bindSimpleProgram(gpu::Batch& batch, bool textured = false, bool culled = true,
+ bool emmisive = false, bool depthBias = false);
//// Renders a solid sphere with the simple program.
void renderSolidSphere(gpu::Batch& batch, float radius, int slices, int stacks, const glm::vec4& color);
@@ -92,41 +95,49 @@ private:
int lightBufferUnit;
int atmosphereBufferUnit;
int invViewMat;
+ int texcoordMat;
+ int coneParam;
};
-
- static void loadLightProgram(const char* fragSource, bool limited, ProgramObject& program, LightLocations& locations);
-
- gpu::PipelinePointer _simpleProgram;
- gpu::PipelinePointer _simpleProgramCullNone;
- gpu::PipelinePointer _simpleProgramEmissive;
- gpu::PipelinePointer _simpleProgramEmissiveCullNone;
- ProgramObject _directionalSkyboxLight;
+ model::MeshPointer _spotLightMesh;
+ model::MeshPointer getSpotLightMesh();
+
+ static void loadLightProgram(const char* vertSource, const char* fragSource, bool lightVolume, gpu::PipelinePointer& program, LightLocations& locations);
+
+ gpu::PipelinePointer getPipeline(SimpleProgramKey config);
+
+ gpu::ShaderPointer _simpleShader;
+ gpu::ShaderPointer _emissiveShader;
+ QHash _simplePrograms;
+
+ gpu::PipelinePointer _blitLightBuffer;
+
+ gpu::PipelinePointer _directionalSkyboxLight;
LightLocations _directionalSkyboxLightLocations;
- ProgramObject _directionalSkyboxLightShadowMap;
+ gpu::PipelinePointer _directionalSkyboxLightShadowMap;
LightLocations _directionalSkyboxLightShadowMapLocations;
- ProgramObject _directionalSkyboxLightCascadedShadowMap;
+ gpu::PipelinePointer _directionalSkyboxLightCascadedShadowMap;
LightLocations _directionalSkyboxLightCascadedShadowMapLocations;
- ProgramObject _directionalAmbientSphereLight;
+ gpu::PipelinePointer _directionalAmbientSphereLight;
LightLocations _directionalAmbientSphereLightLocations;
- ProgramObject _directionalAmbientSphereLightShadowMap;
+ gpu::PipelinePointer _directionalAmbientSphereLightShadowMap;
LightLocations _directionalAmbientSphereLightShadowMapLocations;
- ProgramObject _directionalAmbientSphereLightCascadedShadowMap;
+ gpu::PipelinePointer _directionalAmbientSphereLightCascadedShadowMap;
LightLocations _directionalAmbientSphereLightCascadedShadowMapLocations;
- ProgramObject _directionalLight;
+ gpu::PipelinePointer _directionalLight;
LightLocations _directionalLightLocations;
- ProgramObject _directionalLightShadowMap;
+ gpu::PipelinePointer _directionalLightShadowMap;
LightLocations _directionalLightShadowMapLocations;
- ProgramObject _directionalLightCascadedShadowMap;
+ gpu::PipelinePointer _directionalLightCascadedShadowMap;
LightLocations _directionalLightCascadedShadowMapLocations;
- ProgramObject _pointLight;
+ gpu::PipelinePointer _pointLight;
LightLocations _pointLightLocations;
- ProgramObject _spotLight;
+ gpu::PipelinePointer _spotLight;
LightLocations _spotLightLocations;
-
+
class PointLight {
public:
glm::vec4 position;
@@ -160,4 +171,53 @@ private:
model::SkyboxPointer _skybox;
};
+class SimpleProgramKey {
+public:
+ enum FlagBit {
+ IS_TEXTURED_FLAG = 0,
+ IS_CULLED_FLAG,
+ IS_EMISSIVE_FLAG,
+ HAS_DEPTH_BIAS_FLAG,
+
+ NUM_FLAGS,
+ };
+
+ enum Flag {
+ IS_TEXTURED = (1 << IS_TEXTURED_FLAG),
+ IS_CULLED = (1 << IS_CULLED_FLAG),
+ IS_EMISSIVE = (1 << IS_EMISSIVE_FLAG),
+ HAS_DEPTH_BIAS = (1 << HAS_DEPTH_BIAS_FLAG),
+ };
+ typedef unsigned short Flags;
+
+ bool isFlag(short flagNum) const { return bool((_flags & flagNum) != 0); }
+
+ bool isTextured() const { return isFlag(IS_TEXTURED); }
+ bool isCulled() const { return isFlag(IS_CULLED); }
+ bool isEmissive() const { return isFlag(IS_EMISSIVE); }
+ bool hasDepthBias() const { return isFlag(HAS_DEPTH_BIAS); }
+
+ Flags _flags = 0;
+ short _spare = 0;
+
+ int getRaw() const { return *reinterpret_cast(this); }
+
+
+ SimpleProgramKey(bool textured = false, bool culled = true,
+ bool emissive = false, bool depthBias = false) {
+ _flags = (textured ? IS_TEXTURED : 0) | (culled ? IS_CULLED : 0) |
+ (emissive ? IS_EMISSIVE : 0) | (depthBias ? HAS_DEPTH_BIAS : 0);
+ }
+
+ SimpleProgramKey(int bitmask) : _flags(bitmask) {}
+};
+
+inline uint qHash(const SimpleProgramKey& key, uint seed) {
+ return qHash(key.getRaw(), seed);
+}
+
+inline bool operator==(const SimpleProgramKey& a, const SimpleProgramKey& b) {
+ return a.getRaw() == b.getRaw();
+}
+
#endif // hifi_DeferredLightingEffect_h
diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp
index b0e184d224..02e59bfa3a 100644
--- a/libraries/render-utils/src/GeometryCache.cpp
+++ b/libraries/render-utils/src/GeometryCache.cpp
@@ -279,14 +279,21 @@ void GeometryCache::renderSphere(gpu::Batch& batch, float radius, int slices, in
const int VERTICES_SLOT = 0;
const int NORMALS_SLOT = 1;
const int COLOR_SLOT = 2;
- gpu::Stream::FormatPointer streamFormat(new gpu::Stream::Format()); // 1 for everyone
- streamFormat->setAttribute(gpu::Stream::POSITION, VERTICES_SLOT, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0);
- streamFormat->setAttribute(gpu::Stream::NORMAL, NORMALS_SLOT, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ));
- streamFormat->setAttribute(gpu::Stream::COLOR, COLOR_SLOT, gpu::Element(gpu::VEC4, gpu::UINT8, gpu::RGBA));
+ static gpu::Stream::FormatPointer streamFormat;
+ static gpu::Element positionElement, normalElement, colorElement;
+ if (!streamFormat) {
+ streamFormat.reset(new gpu::Stream::Format()); // 1 for everyone
+ streamFormat->setAttribute(gpu::Stream::POSITION, VERTICES_SLOT, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0);
+ streamFormat->setAttribute(gpu::Stream::NORMAL, NORMALS_SLOT, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ));
+ streamFormat->setAttribute(gpu::Stream::COLOR, COLOR_SLOT, gpu::Element(gpu::VEC4, gpu::UINT8, gpu::RGBA));
+ positionElement = streamFormat->getAttributes().at(gpu::Stream::POSITION)._element;
+ normalElement = streamFormat->getAttributes().at(gpu::Stream::NORMAL)._element;
+ colorElement = streamFormat->getAttributes().at(gpu::Stream::COLOR)._element;
+ }
- gpu::BufferView verticesView(verticesBuffer, streamFormat->getAttributes().at(gpu::Stream::POSITION)._element);
- gpu::BufferView normalsView(verticesBuffer, streamFormat->getAttributes().at(gpu::Stream::NORMAL)._element);
- gpu::BufferView colorView(colorBuffer, streamFormat->getAttributes().at(gpu::Stream::COLOR)._element);
+ gpu::BufferView verticesView(verticesBuffer, positionElement);
+ gpu::BufferView normalsView(verticesBuffer, normalElement);
+ gpu::BufferView colorView(colorBuffer, colorElement);
batch.setInputFormat(streamFormat);
batch.setInputBuffer(VERTICES_SLOT, verticesView);
@@ -401,7 +408,6 @@ void GeometryCache::renderCone(float base, float height, int slices, int stacks)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
-
void GeometryCache::renderGrid(int xDivisions, int yDivisions, const glm::vec4& color) {
gpu::Batch batch;
renderGrid(batch, xDivisions, yDivisions, color);
@@ -900,14 +906,21 @@ void GeometryCache::renderSolidCube(gpu::Batch& batch, float size, const glm::ve
const int VERTICES_SLOT = 0;
const int NORMALS_SLOT = 1;
const int COLOR_SLOT = 2;
- gpu::Stream::FormatPointer streamFormat(new gpu::Stream::Format()); // 1 for everyone
+ static gpu::Stream::FormatPointer streamFormat;
+ static gpu::Element positionElement, normalElement, colorElement;
+ if (!streamFormat) {
+ streamFormat.reset(new gpu::Stream::Format()); // 1 for everyone
+ streamFormat->setAttribute(gpu::Stream::POSITION, VERTICES_SLOT, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0);
+ streamFormat->setAttribute(gpu::Stream::NORMAL, NORMALS_SLOT, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ));
+ streamFormat->setAttribute(gpu::Stream::COLOR, COLOR_SLOT, gpu::Element(gpu::VEC4, gpu::UINT8, gpu::RGBA));
+ positionElement = streamFormat->getAttributes().at(gpu::Stream::POSITION)._element;
+ normalElement = streamFormat->getAttributes().at(gpu::Stream::NORMAL)._element;
+ colorElement = streamFormat->getAttributes().at(gpu::Stream::COLOR)._element;
+ }
- streamFormat->setAttribute(gpu::Stream::POSITION, VERTICES_SLOT, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0);
- streamFormat->setAttribute(gpu::Stream::NORMAL, NORMALS_SLOT, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ));
- streamFormat->setAttribute(gpu::Stream::COLOR, COLOR_SLOT, gpu::Element(gpu::VEC4, gpu::UINT8, gpu::RGBA));
-
- gpu::BufferView verticesView(verticesBuffer, 0, verticesBuffer->getSize(), VERTEX_STRIDE, streamFormat->getAttributes().at(gpu::Stream::POSITION)._element);
- gpu::BufferView normalsView(verticesBuffer, NORMALS_OFFSET, verticesBuffer->getSize(), VERTEX_STRIDE, streamFormat->getAttributes().at(gpu::Stream::NORMAL)._element);
+
+ gpu::BufferView verticesView(verticesBuffer, 0, verticesBuffer->getSize(), VERTEX_STRIDE, positionElement);
+ gpu::BufferView normalsView(verticesBuffer, NORMALS_OFFSET, verticesBuffer->getSize(), VERTEX_STRIDE, normalElement);
gpu::BufferView colorView(colorBuffer, streamFormat->getAttributes().at(gpu::Stream::COLOR)._element);
batch.setInputFormat(streamFormat);
@@ -987,12 +1000,18 @@ void GeometryCache::renderWireCube(gpu::Batch& batch, float size, const glm::vec
const int VERTICES_SLOT = 0;
const int COLOR_SLOT = 1;
- gpu::Stream::FormatPointer streamFormat(new gpu::Stream::Format()); // 1 for everyone
- streamFormat->setAttribute(gpu::Stream::POSITION, VERTICES_SLOT, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0);
- streamFormat->setAttribute(gpu::Stream::COLOR, COLOR_SLOT, gpu::Element(gpu::VEC4, gpu::UINT8, gpu::RGBA));
-
- gpu::BufferView verticesView(verticesBuffer, streamFormat->getAttributes().at(gpu::Stream::POSITION)._element);
- gpu::BufferView colorView(colorBuffer, streamFormat->getAttributes().at(gpu::Stream::COLOR)._element);
+ static gpu::Stream::FormatPointer streamFormat;
+ static gpu::Element positionElement, colorElement;
+ if (!streamFormat) {
+ streamFormat.reset(new gpu::Stream::Format()); // 1 for everyone
+ streamFormat->setAttribute(gpu::Stream::POSITION, VERTICES_SLOT, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0);
+ streamFormat->setAttribute(gpu::Stream::COLOR, COLOR_SLOT, gpu::Element(gpu::VEC4, gpu::UINT8, gpu::RGBA));
+ positionElement = streamFormat->getAttributes().at(gpu::Stream::POSITION)._element;
+ colorElement = streamFormat->getAttributes().at(gpu::Stream::COLOR)._element;
+ }
+
+ gpu::BufferView verticesView(verticesBuffer, positionElement);
+ gpu::BufferView colorView(colorBuffer, colorElement);
batch.setInputFormat(streamFormat);
batch.setInputBuffer(VERTICES_SLOT, verticesView);
diff --git a/libraries/render-utils/src/GeometryCache.h b/libraries/render-utils/src/GeometryCache.h
index f97ab8a773..83891bbf49 100644
--- a/libraries/render-utils/src/GeometryCache.h
+++ b/libraries/render-utils/src/GeometryCache.h
@@ -301,6 +301,7 @@ private:
};
QHash _coneVBOs;
+
int _nextID;
QHash _lastRegisteredQuad3DTexture;
diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp
index 1b94c70e57..ce325d23cb 100644
--- a/libraries/render-utils/src/Model.cpp
+++ b/libraries/render-utils/src/Model.cpp
@@ -417,6 +417,7 @@ void Model::reset() {
}
bool Model::updateGeometry() {
+ PROFILE_RANGE(__FUNCTION__);
bool needFullUpdate = false;
bool needToRebuild = false;
@@ -690,6 +691,7 @@ void Model::recalculateMeshPartOffsets() {
// entity-scripts to call. I think it would be best to do the picking once-per-frame (in cpu, or gpu if possible)
// and then the calls use the most recent such result.
void Model::recalculateMeshBoxes(bool pickAgainstTriangles) {
+ PROFILE_RANGE(__FUNCTION__);
bool calculatedMeshTrianglesNeeded = pickAgainstTriangles && !_calculatedMeshTrianglesValid;
if (!_calculatedMeshBoxesValid || calculatedMeshTrianglesNeeded || (!_calculatedMeshPartBoxesValid && pickAgainstTriangles) ) {
@@ -1281,6 +1283,7 @@ Blender::Blender(Model* model, int blendNumber, const QWeakPointer vertices, normals;
if (!_model.isNull()) {
int offset = 0;
@@ -1392,6 +1395,7 @@ void Model::snapToRegistrationPoint() {
}
void Model::simulate(float deltaTime, bool fullUpdate) {
+ PROFILE_RANGE(__FUNCTION__);
fullUpdate = updateGeometry() || fullUpdate || (_scaleToFit && !_scaledToFit)
|| (_snapModelToRegistrationPoint && !_snappedToRegistrationPoint);
@@ -1829,6 +1833,7 @@ void Model::setupBatchTransform(gpu::Batch& batch, RenderArgs* args) {
}
AABox Model::getPartBounds(int meshIndex, int partIndex) {
+
if (meshIndex < _meshStates.size()) {
const MeshState& state = _meshStates.at(meshIndex);
bool isSkinned = state.clusterMatrices.size() > 1;
@@ -1859,6 +1864,7 @@ AABox Model::getPartBounds(int meshIndex, int partIndex) {
}
void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool translucent) {
+ // PROFILE_RANGE(__FUNCTION__);
PerformanceTimer perfTimer("Model::renderPart");
if (!_readyWhenAdded) {
return; // bail asap
@@ -1933,7 +1939,9 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran
pickPrograms(batch, mode, translucentMesh, alphaThreshold, hasLightmap, hasTangents, hasSpecular, isSkinned, wireframe,
args, locations);
- updateVisibleJointStates();
+ {
+ updateVisibleJointStates();
+ }
// 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.
@@ -2023,10 +2031,10 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran
}
static bool showDiffuse = true;
if (showDiffuse && diffuseMap) {
- batch.setUniformTexture(0, diffuseMap->getGPUTexture());
+ batch.setResourceTexture(0, diffuseMap->getGPUTexture());
} else {
- batch.setUniformTexture(0, textureCache->getWhiteTexture());
+ batch.setResourceTexture(0, textureCache->getWhiteTexture());
}
if (locations->texcoordMatrices >= 0) {
@@ -2042,14 +2050,14 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran
if (!mesh.tangents.isEmpty()) {
Texture* normalMap = networkPart.normalTexture.data();
- batch.setUniformTexture(1, !normalMap ?
+ batch.setResourceTexture(1, !normalMap ?
textureCache->getBlueTexture() : normalMap->getGPUTexture());
}
if (locations->specularTextureUnit >= 0) {
Texture* specularMap = networkPart.specularTexture.data();
- batch.setUniformTexture(locations->specularTextureUnit, !specularMap ?
+ batch.setResourceTexture(locations->specularTextureUnit, !specularMap ?
textureCache->getWhiteTexture() : specularMap->getGPUTexture());
}
@@ -2066,7 +2074,7 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran
GLBATCH(glUniform2f)(locations->emissiveParams, emissiveOffset, emissiveScale);
Texture* emissiveMap = networkPart.emissiveTexture.data();
- batch.setUniformTexture(locations->emissiveTextureUnit, !emissiveMap ?
+ batch.setResourceTexture(locations->emissiveTextureUnit, !emissiveMap ?
textureCache->getWhiteTexture() : emissiveMap->getGPUTexture());
}
@@ -2076,9 +2084,13 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran
}
}
- _mutex.lock();
- qint64 offset = _calculatedMeshPartOffset[QPair(meshIndex, partIndex)];
- _mutex.unlock();
+ qint64 offset;
+ {
+ // FIXME_STUTTER: We should n't have any lock here
+ _mutex.lock();
+ offset = _calculatedMeshPartOffset[QPair(meshIndex, partIndex)];
+ _mutex.unlock();
+ }
if (part.quadIndices.size() > 0) {
batch.drawIndexed(gpu::QUADS, part.quadIndices.size(), offset);
diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp
index 12a6d32ae5..dc0de80e42 100755
--- a/libraries/render-utils/src/RenderDeferredTask.cpp
+++ b/libraries/render-utils/src/RenderDeferredTask.cpp
@@ -40,8 +40,9 @@ void ResolveDeferred::run(const SceneContextPointer& sceneContext, const RenderC
}
RenderDeferredTask::RenderDeferredTask() : Task() {
- _jobs.push_back(Job(new PrepareDeferred::JobModel("PrepareDeferred")));
_jobs.push_back(Job(new DrawBackground::JobModel("DrawBackground")));
+
+ _jobs.push_back(Job(new PrepareDeferred::JobModel("PrepareDeferred")));
_jobs.push_back(Job(new FetchItems::JobModel("FetchOpaque",
FetchItems(
[] (const RenderContextPointer& context, int count) {
@@ -75,6 +76,12 @@ RenderDeferredTask::RenderDeferredTask() : Task() {
_jobs.push_back(Job(new DrawOverlay3D::JobModel("DrawOverlay3D")));
_jobs.push_back(Job(new ResetGLState::JobModel()));
+
+ // Give ourselves 3 frmaes of timer queries
+ _timerQueries.push_back(gpu::QueryPointer(new gpu::Query()));
+ _timerQueries.push_back(gpu::QueryPointer(new gpu::Query()));
+ _timerQueries.push_back(gpu::QueryPointer(new gpu::Query()));
+ _currentTimerQueryIndex = 0;
}
RenderDeferredTask::~RenderDeferredTask() {
@@ -101,6 +108,7 @@ void RenderDeferredTask::run(const SceneContextPointer& sceneContext, const Rend
for (auto job : _jobs) {
job.run(sceneContext, renderContext);
}
+
};
void DrawOpaqueDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems) {
@@ -118,7 +126,7 @@ void DrawOpaqueDeferred::run(const SceneContextPointer& sceneContext, const Rend
args->_viewFrustum->evalProjectionMatrix(projMat);
args->_viewFrustum->evalViewTransform(viewMat);
if (args->_renderMode == RenderArgs::MIRROR_RENDER_MODE) {
- viewMat.postScale(glm::vec3(-1.0f, 1.0f, 1.0f));
+ viewMat.preScale(glm::vec3(-1.0f, 1.0f, 1.0f));
}
batch.setProjectionTransform(projMat);
batch.setViewTransform(viewMat);
@@ -235,7 +243,7 @@ void DrawOverlay3D::run(const SceneContextPointer& sceneContext, const RenderCon
batch.setViewTransform(viewMat);
batch.setPipeline(getOpaquePipeline());
- batch.setUniformTexture(0, args->_whiteTexture);
+ batch.setResourceTexture(0, args->_whiteTexture);
if (!inItems.empty()) {
batch.clearFramebuffer(gpu::Framebuffer::BUFFER_DEPTH, glm::vec4(), 1.f, 0);
diff --git a/libraries/render-utils/src/RenderDeferredTask.h b/libraries/render-utils/src/RenderDeferredTask.h
index 3d11e97634..4040606c62 100755
--- a/libraries/render-utils/src/RenderDeferredTask.h
+++ b/libraries/render-utils/src/RenderDeferredTask.h
@@ -77,6 +77,9 @@ public:
virtual void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext);
+
+ gpu::Queries _timerQueries;
+ int _currentTimerQueryIndex = 0;
};
diff --git a/libraries/render-utils/src/TextRenderer3D.cpp b/libraries/render-utils/src/TextRenderer3D.cpp
index d202d89aba..973cddc4d7 100644
--- a/libraries/render-utils/src/TextRenderer3D.cpp
+++ b/libraries/render-utils/src/TextRenderer3D.cpp
@@ -343,7 +343,7 @@ void Font3D::setupGPU() {
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
state->setCullMode(gpu::State::CULL_BACK);
state->setDepthTest(true, true, gpu::LESS_EQUAL);
- state->setBlendFunction(false,
+ state->setBlendFunction(true,
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
_pipeline = gpu::PipelinePointer(gpu::Pipeline::create(program, state));
@@ -423,7 +423,7 @@ void Font3D::drawString(gpu::Batch& batch, float x, float y, const QString& str,
setupGPU();
batch.setPipeline(_pipeline);
- batch.setUniformTexture(_fontLoc, _texture);
+ batch.setResourceTexture(_fontLoc, _texture);
batch._glUniform1i(_outlineLoc, (effectType == TextRenderer3D::OUTLINE_EFFECT));
batch._glUniform4fv(_colorLoc, 1, (const GLfloat*)color);
diff --git a/libraries/render-utils/src/deferred_light_limited.slv b/libraries/render-utils/src/deferred_light_limited.slv
index e3051d43f7..d57b987b68 100644
--- a/libraries/render-utils/src/deferred_light_limited.slv
+++ b/libraries/render-utils/src/deferred_light_limited.slv
@@ -12,9 +12,19 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
+<@include gpu/Transform.slh@>
+
+<$declareStandardTransform()$>
+
+uniform mat4 texcoordMat;
+
void main(void) {
- gl_Position = ftransform();
+ // standard transform
+ TransformCamera cam = getTransformCamera();
+ TransformObject obj = getTransformObject();
+ <$transformModelToClipPos(cam, obj, gl_Vertex, gl_Position)$>;
+
vec4 projected = gl_Position / gl_Position.w;
- gl_TexCoord[0] = vec4(dot(projected, gl_ObjectPlaneS[3]) * gl_Position.w,
- dot(projected, gl_ObjectPlaneT[3]) * gl_Position.w, 0.0, gl_Position.w);
+ gl_TexCoord[0] = vec4(dot(projected, texcoordMat[0]) * gl_Position.w,
+ dot(projected, texcoordMat[1]) * gl_Position.w, 0.0, gl_Position.w);
}
diff --git a/libraries/render-utils/src/deferred_light_spot.slv b/libraries/render-utils/src/deferred_light_spot.slv
new file mode 100755
index 0000000000..d3ef18fd53
--- /dev/null
+++ b/libraries/render-utils/src/deferred_light_spot.slv
@@ -0,0 +1,47 @@
+<@include gpu/Config.slh@>
+<$VERSION_HEADER$>
+// Generated on <$_SCRIBE_DATE$>
+//
+// deferred_light_spot.vert
+// vertex shader
+//
+// Created by Sam Gateau on 7/8/15.
+// Copyright 2015 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+<@include gpu/Transform.slh@>
+
+<$declareStandardTransform()$>
+
+uniform mat4 texcoordMat;
+uniform vec4 coneParam;
+
+void main(void) {
+ vec4 coneVertex = gl_Vertex;
+ if (coneParam.w != 0.0) {
+ if(coneVertex.z >= 0.0) {
+ // Evaluate the true position of the spot volume
+ vec2 dir = float(coneVertex.z < 0.5f) * (coneParam.xy
+ + vec2(coneParam.y, -coneParam.x) * coneParam.z * float(coneVertex.z > 0.0f))
+ + float(coneVertex.z > 0.5f) * (vec2(1.0, 0.0)
+ + vec2(0.0, coneParam.z) * float(coneVertex.z < 1.0f));
+
+ coneVertex.xy *= dir.y;
+ coneVertex.z = -dir.x;
+ } else {
+ coneVertex.z = 0.0;
+ }
+ }
+
+ // standard transform
+ TransformCamera cam = getTransformCamera();
+ TransformObject obj = getTransformObject();
+ <$transformModelToClipPos(cam, obj, coneVertex, gl_Position)$>;
+
+ vec4 projected = gl_Position / gl_Position.w;
+ gl_TexCoord[0] = vec4(dot(projected, texcoordMat[0]) * gl_Position.w,
+ dot(projected, texcoordMat[1]) * gl_Position.w, 0.0, gl_Position.w);
+}
diff --git a/libraries/render-utils/src/sdf_text3D.slf b/libraries/render-utils/src/sdf_text3D.slf
index d9972417ba..425d6f9b08 100644
--- a/libraries/render-utils/src/sdf_text3D.slf
+++ b/libraries/render-utils/src/sdf_text3D.slf
@@ -18,7 +18,7 @@ uniform vec4 Color;
varying vec4 interpolatedNormal;
const float gamma = 2.2;
-const float smoothing = 64.0;
+const float smoothing = 256.0;
const float interiorCutoff = 0.8;
const float outlineExpansion = 0.2;
diff --git a/libraries/render-utils/src/spot_light.slf b/libraries/render-utils/src/spot_light.slf
index e6c3938b71..cfb2a6da4b 100644
--- a/libraries/render-utils/src/spot_light.slf
+++ b/libraries/render-utils/src/spot_light.slf
@@ -28,7 +28,7 @@ void main(void) {
// Grab the fragment data from the uv
vec2 texCoord = gl_TexCoord[0].st / gl_TexCoord[0].q;
DeferredFragment frag = unpackDeferredFragment(texCoord);
-
+
// Kill if in front of the light volume
float depth = frag.depthVal;
if (depth < gl_FragCoord.z) {
diff --git a/libraries/render/CMakeLists.txt b/libraries/render/CMakeLists.txt
index ee99eb00b9..4d2be949e6 100644
--- a/libraries/render/CMakeLists.txt
+++ b/libraries/render/CMakeLists.txt
@@ -9,4 +9,16 @@ add_dependency_external_projects(glm)
find_package(GLM REQUIRED)
target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS})
-link_hifi_libraries(shared gpu model)
\ No newline at end of file
+link_hifi_libraries(shared gpu model)
+
+if (WIN32)
+ if (USE_NSIGHT)
+ # try to find the Nsight package and add it to the build if we find it
+ find_package(NSIGHT)
+ if (NSIGHT_FOUND)
+ include_directories(${NSIGHT_INCLUDE_DIRS})
+ add_definitions(-DNSIGHT_FOUND)
+ target_link_libraries(${TARGET_NAME} "${NSIGHT_LIBRARIES}")
+ endif ()
+ endif()
+endif (WIN32)
\ No newline at end of file
diff --git a/libraries/render/src/render/Scene.cpp b/libraries/render/src/render/Scene.cpp
index a7145af4b5..1b52145a1c 100644
--- a/libraries/render/src/render/Scene.cpp
+++ b/libraries/render/src/render/Scene.cpp
@@ -11,6 +11,7 @@
#include "Scene.h"
#include
+#include "gpu/Batch.h"
using namespace render;
@@ -75,7 +76,7 @@ void Item::Status::Value::setColor(float hue) {
}
void Item::Status::getPackedValues(glm::ivec4& values) const {
- for (unsigned int i = 0; i < values.length(); i++) {
+ for (unsigned int i = 0; i < (unsigned int)values.length(); i++) {
if (i < _values.size()) {
values[i] = _values[i]().getPackedData();
} else {
@@ -167,6 +168,7 @@ void consolidateChangeQueue(PendingChangesQueue& queue, PendingChanges& singleBa
}
void Scene::processPendingChangesQueue() {
+ PROFILE_RANGE(__FUNCTION__);
_changeQueueMutex.lock();
PendingChanges consolidatedPendingChanges;
consolidateChangeQueue(_changeQueue, consolidatedPendingChanges);
diff --git a/libraries/render/src/render/drawItemStatus.slv b/libraries/render/src/render/drawItemStatus.slv
index 9e2b4919ff..106a5684bb 100644
--- a/libraries/render/src/render/drawItemStatus.slv
+++ b/libraries/render/src/render/drawItemStatus.slv
@@ -22,10 +22,10 @@ uniform vec3 inBoundPos;
uniform vec3 inBoundDim;
uniform ivec4 inStatus;
-vec3 paintRainbow(float nv) {
- float v = nv * 5.f;
+vec3 paintRainbow(float normalizedHue) {
+ float v = normalizedHue * 6.f;
if (v < 0.f) {
- return vec3(0.f, 0.f, 0.f);
+ return vec3(1.f, 0.f, 0.f);
} else if (v < 1.f) {
return vec3(1.f, v, 0.f);
} else if (v < 2.f) {
@@ -36,8 +36,10 @@ vec3 paintRainbow(float nv) {
return vec3(0.f, 1.f - (v-3.f), 1.f );
} else if (v < 5.f) {
return vec3((v-4.f), 0.f, 1.f );
+ } else if (v < 6.f) {
+ return vec3(1.f, 0.f, 1.f - (v-5.f));
} else {
- return vec3(1.f, 1.f, 1.f);
+ return vec3(1.f, 0.f, 0.f);
}
}
diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp
index 55cf9e9890..863cc28a94 100644
--- a/libraries/script-engine/src/ScriptEngine.cpp
+++ b/libraries/script-engine/src/ScriptEngine.cpp
@@ -493,7 +493,7 @@ void ScriptEngine::evaluate() {
QScriptValue result = evaluate(_scriptContents);
- // TODO: why do we check this twice? It seems like the call to clearExcpetions() in the lower level evaluate call
+ // TODO: why do we check this twice? It seems like the call to clearExceptions() in the lower level evaluate call
// will cause this code to never actually run...
if (hasUncaughtException()) {
int line = uncaughtExceptionLineNumber();
@@ -710,7 +710,13 @@ void ScriptEngine::run() {
// since we're in non-threaded mode, call process so that the packets are sent
if (!entityScriptingInterface->getEntityPacketSender()->isThreaded()) {
- entityScriptingInterface->getEntityPacketSender()->process();
+ // wait here till the edit packet sender is completely done sending
+ while (entityScriptingInterface->getEntityPacketSender()->hasPacketsToSend()) {
+ entityScriptingInterface->getEntityPacketSender()->process();
+ QCoreApplication::processEvents();
+ }
+ } else {
+ // FIXME - do we need to have a similar "wait here" loop for non-threaded packet senders?
}
}
diff --git a/libraries/shared/src/BufferParser.h b/libraries/shared/src/BufferParser.h
new file mode 100644
index 0000000000..d60e7127cd
--- /dev/null
+++ b/libraries/shared/src/BufferParser.h
@@ -0,0 +1,119 @@
+//
+// Created by Bradley Austin Davis on 2015/07/08
+// Copyright 2013-2015 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+#pragma once
+#ifndef hifi_BufferParser_h
+#define hifi_BufferParser_h
+
+#include
+
+#include
+#include
+
+#include "GLMHelpers.h"
+#include "ByteCountCoding.h"
+#include "PropertyFlags.h"
+
+class BufferParser {
+public:
+ BufferParser(const uint8_t* data, size_t size, size_t offset = 0) :
+ _offset(offset), _data(data), _size(size) {
+ }
+
+ template
+ inline void readValue(T& result) {
+ Q_ASSERT(remaining() >= sizeof(T));
+ memcpy(&result, _data + _offset, sizeof(T));
+ _offset += sizeof(T);
+ }
+
+ inline void readUuid(QUuid& result) {
+ readValue(result.data1);
+ readValue(result.data2);
+ readValue(result.data3);
+ readValue(result.data4);
+ result.data1 = qFromBigEndian(result.data1);
+ result.data2 = qFromBigEndian(result.data2);
+ result.data3 = qFromBigEndian(result.data3);
+ }
+
+ template
+ inline void readFlags(PropertyFlags& result) {
+ _offset += result.decode(_data + _offset, remaining());
+ }
+
+ template
+ inline void readCompressedCount(T& result) {
+ // FIXME switch to a heapless implementation as soon as Brad provides it.
+ ByteCountCoded codec;
+ _offset += codec.decode(reinterpret_cast(_data + _offset), remaining());
+ result = codec.data;
+ }
+
+ inline size_t remaining() const {
+ return _size - _offset;
+ }
+
+ inline size_t offset() const {
+ return _offset;
+ }
+
+ inline const uint8_t* data() const {
+ return _data;
+ }
+
+private:
+
+ size_t _offset{ 0 };
+ const uint8_t* const _data;
+ const size_t _size;
+};
+
+
+template<>
+inline void BufferParser::readValue(quat& result) {
+ size_t advance = unpackOrientationQuatFromBytes(_data + _offset, result);
+ _offset += advance;
+}
+
+template<>
+inline void BufferParser::readValue(QString& result) {
+ uint16_t length; readValue(length);
+ result = QString((const char*)_data + _offset);
+}
+
+template<>
+inline void BufferParser::readValue(QUuid& result) {
+ uint16_t length; readValue(length);
+ Q_ASSERT(16 == length);
+ readUuid(result);
+}
+
+template<>
+inline void BufferParser::readValue(xColor& result) {
+ readValue(result.red);
+ readValue(result.blue);
+ readValue(result.green);
+}
+
+template<>
+inline void BufferParser::readValue(QVector& result) {
+ uint16_t length; readValue(length);
+ result.resize(length);
+ memcpy(result.data(), _data + _offset, sizeof(glm::vec3) * length);
+ _offset += sizeof(glm::vec3) * length;
+}
+
+template<>
+inline void BufferParser::readValue(QByteArray& result) {
+ uint16_t length; readValue(length);
+ result = QByteArray((char*)_data + _offset, (int)length);
+ _offset += length;
+}
+
+#endif
diff --git a/libraries/shared/src/ByteCountCoding.h b/libraries/shared/src/ByteCountCoding.h
index 2a39ee7a8c..ce6f121ddb 100644
--- a/libraries/shared/src/ByteCountCoding.h
+++ b/libraries/shared/src/ByteCountCoding.h
@@ -21,9 +21,13 @@
#include
#include
+#include
+
#include
#include
+#include "SharedUtil.h"
+
#include "NumericalConstants.h"
template class ByteCountCoded {
@@ -37,7 +41,8 @@ public:
ByteCountCoded(const QByteArray& fromEncoded) : data(0) { decode(fromEncoded); }
QByteArray encode() const;
- void decode(const QByteArray& fromEncoded);
+ size_t decode(const QByteArray& fromEncoded);
+ size_t decode(const char* encodedBuffer, int encodedSize);
bool operator==(const ByteCountCoded& other) const { return data == other.data; }
bool operator!=(const ByteCountCoded& other) const { return data != other.data; }
@@ -110,52 +115,63 @@ template inline QByteArray ByteCountCoded::encode() const {
return output;
}
-template inline void ByteCountCoded::decode(const QByteArray& fromEncodedBytes) {
+template inline size_t ByteCountCoded::decode(const QByteArray& fromEncodedBytes) {
+ return decode(fromEncodedBytes.constData(), fromEncodedBytes.size());
+}
- // first convert the ByteArray into a BitArray...
- QBitArray encodedBits;
- int bitCount = BITS_IN_BYTE * fromEncodedBytes.count();
- encodedBits.resize(bitCount);
+template inline size_t ByteCountCoded::decode(const char* encodedBuffer, int encodedSize) {
+ data = 0; // reset data
+ size_t bytesConsumed = 0;
+ int bitCount = BITS_IN_BYTE * encodedSize;
+
+ int encodedByteCount = 1; // there is at least 1 byte (after the leadBits)
+ int leadBits = 1; // there is always at least 1 lead bit
+ bool inLeadBits = true;
+ int bitAt = 0;
+ int expectedBitCount; // unknown at this point
+ int lastValueBit;
+ T bitValue = 1;
- for(int byte = 0; byte < fromEncodedBytes.count(); byte++) {
- char originalByte = fromEncodedBytes.at(byte);
+ for(int byte = 0; byte < encodedSize; byte++) {
+ char originalByte = encodedBuffer[byte];
+ bytesConsumed++;
+ unsigned char maskBit = 128; // LEFT MOST BIT set
for(int bit = 0; bit < BITS_IN_BYTE; bit++) {
- int shiftBy = BITS_IN_BYTE - (bit + 1);
- char maskBit = ( 1 << shiftBy);
- bool bitValue = originalByte & maskBit;
- encodedBits.setBit(byte * BITS_IN_BYTE + bit, bitValue);
+ bool bitIsSet = originalByte & maskBit;
+
+ // Processing of the lead bits
+ if (inLeadBits) {
+ if (bitIsSet) {
+ encodedByteCount++;
+ leadBits++;
+ } else {
+ inLeadBits = false; // once we hit our first 0, we know we're out of the lead bits
+ expectedBitCount = (encodedByteCount * BITS_IN_BYTE) - leadBits;
+ lastValueBit = expectedBitCount + bitAt;
+
+ // check to see if the remainder of our buffer is sufficient
+ if (expectedBitCount > (bitCount - leadBits)) {
+ break;
+ }
+ }
+ } else {
+ if (bitAt > lastValueBit) {
+ break;
+ }
+
+ if(bitIsSet) {
+ data += bitValue;
+ }
+ bitValue *= 2;
+ }
+ bitAt++;
+ maskBit = maskBit >> 1;
}
- }
-
- // next, read the leading bits to determine the correct number of bytes to decode (may not match the QByteArray)
- int encodedByteCount = 0;
- int leadBits = 1;
- int bitAt;
- for (bitAt = 0; bitAt < bitCount; bitAt++) {
- if (encodedBits.at(bitAt)) {
- encodedByteCount++;
- leadBits++;
- } else {
+ if (!inLeadBits && bitAt > lastValueBit) {
break;
}
}
- encodedByteCount++; // always at least one byte
- int expectedBitCount = encodedByteCount * BITS_IN_BYTE;
-
- T value = 0;
-
- if (expectedBitCount <= (encodedBits.size() - leadBits)) {
- // Now, keep reading...
- int valueStartsAt = bitAt + 1;
- T bitValue = 1;
- for (bitAt = valueStartsAt; bitAt < expectedBitCount; bitAt++) {
- if(encodedBits.at(bitAt)) {
- value += bitValue;
- }
- bitValue *= 2;
- }
- }
- data = value;
+ return bytesConsumed;
}
#endif // hifi_ByteCountCoding_h
diff --git a/libraries/shared/src/GenericQueueThread.h b/libraries/shared/src/GenericQueueThread.h
new file mode 100644
index 0000000000..2a48ff7418
--- /dev/null
+++ b/libraries/shared/src/GenericQueueThread.h
@@ -0,0 +1,72 @@
+//
+// Created by Bradley Austin Davis 2015/07/08.
+// Copyright 2015 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+#pragma once
+#ifndef hifi_GenericQueueThread_h
+#define hifi_GenericQueueThread_h
+
+#include
+
+#include
+#include
+#include
+
+#include "GenericThread.h"
+#include "NumericalConstants.h"
+
+template
+class GenericQueueThread : public GenericThread {
+public:
+ using Queue = QQueue;
+ GenericQueueThread(QObject* parent = nullptr)
+ : GenericThread(parent) {}
+
+ virtual ~GenericQueueThread() {}
+
+ void queueItem(const T& t) {
+ lock();
+ queueItemInternal(t);
+ unlock();
+ _hasItems.wakeAll();
+ }
+
+protected:
+ virtual void queueItemInternal(const T& t) {
+ _items.push_back(t);
+ }
+
+ virtual uint32_t getMaxWait() {
+ return MSECS_PER_SECOND;
+ }
+
+ virtual bool process() {
+ if (!_items.size()) {
+ _hasItemsMutex.lock();
+ _hasItems.wait(&_hasItemsMutex, getMaxWait());
+ _hasItemsMutex.unlock();
+ }
+
+ if (!_items.size()) {
+ return isStillRunning();
+ }
+
+ Queue processItems;
+ lock();
+ processItems.swap(_items);
+ unlock();
+ return processQueueItems(processItems);
+ }
+
+ virtual bool processQueueItems(const Queue& items) = 0;
+
+ Queue _items;
+ QWaitCondition _hasItems;
+ QMutex _hasItemsMutex;
+};
+
+#endif // hifi_GenericQueueThread_h
diff --git a/libraries/shared/src/GenericThread.cpp b/libraries/shared/src/GenericThread.cpp
index 3068ca2ab6..9c1c7c590c 100644
--- a/libraries/shared/src/GenericThread.cpp
+++ b/libraries/shared/src/GenericThread.cpp
@@ -14,7 +14,8 @@
#include "GenericThread.h"
-GenericThread::GenericThread() :
+GenericThread::GenericThread(QObject* parent) :
+ QObject(parent),
_stopThread(false),
_isThreaded(false) // assume non-threaded, must call initialize()
{
@@ -27,13 +28,14 @@ GenericThread::~GenericThread() {
}
}
-void GenericThread::initialize(bool isThreaded) {
+void GenericThread::initialize(bool isThreaded, QThread::Priority priority) {
_isThreaded = isThreaded;
if (_isThreaded) {
_thread = new QThread(this);
// match the thread name to our object name
_thread->setObjectName(objectName());
+ _thread->setPriority(priority);
// when the worker thread is started, call our engine's run..
connect(_thread, SIGNAL(started()), this, SLOT(threadRoutine()));
diff --git a/libraries/shared/src/GenericThread.h b/libraries/shared/src/GenericThread.h
index b2c0eb13db..f261dc5b37 100644
--- a/libraries/shared/src/GenericThread.h
+++ b/libraries/shared/src/GenericThread.h
@@ -23,12 +23,12 @@
class GenericThread : public QObject {
Q_OBJECT
public:
- GenericThread();
+ GenericThread(QObject* parent = nullptr);
virtual ~GenericThread();
/// Call to start the thread.
/// \param bool isThreaded true by default. false for non-threaded mode and caller must call threadRoutine() regularly.
- void initialize(bool isThreaded = true);
+ void initialize(bool isThreaded = true, QThread::Priority priority = QThread::NormalPriority);
/// Call to stop the thread
void terminate();
diff --git a/libraries/shared/src/PropertyFlags.h b/libraries/shared/src/PropertyFlags.h
index de05edc076..0be7b3af93 100644
--- a/libraries/shared/src/PropertyFlags.h
+++ b/libraries/shared/src/PropertyFlags.h
@@ -25,6 +25,7 @@
#include
#include
+#include "ByteCountCoding.h"
#include
templateclass PropertyFlags {
@@ -51,7 +52,8 @@ public:
void setHasProperty(Enum flag, bool value = true);
bool getHasProperty(Enum flag) const;
QByteArray encode();
- void decode(const QByteArray& fromEncoded);
+ size_t decode(const uint8_t* data, size_t length);
+ size_t decode(const QByteArray& fromEncoded);
operator QByteArray() { return encode(); };
@@ -193,51 +195,62 @@ template inline QByteArray PropertyFlags::encode() {
return output;
}
-template inline void PropertyFlags::decode(const QByteArray& fromEncodedBytes) {
-
+template
+inline size_t PropertyFlags::decode(const uint8_t* data, size_t size) {
clear(); // we are cleared out!
- // first convert the ByteArray into a BitArray...
- QBitArray encodedBits;
- int bitCount = BITS_PER_BYTE * fromEncodedBytes.count();
- encodedBits.resize(bitCount);
-
- for(int byte = 0; byte < fromEncodedBytes.count(); byte++) {
- char originalByte = fromEncodedBytes.at(byte);
- for(int bit = 0; bit < BITS_PER_BYTE; bit++) {
- int shiftBy = BITS_PER_BYTE - (bit + 1);
- char maskBit = ( 1 << shiftBy);
- bool bitValue = originalByte & maskBit;
- encodedBits.setBit(byte * BITS_PER_BYTE + bit, bitValue);
+ size_t bytesConsumed = 0;
+ int bitCount = BITS_IN_BYTE * size;
+
+ int encodedByteCount = 1; // there is at least 1 byte (after the leadBits)
+ int leadBits = 1; // there is always at least 1 lead bit
+ bool inLeadBits = true;
+ int bitAt = 0;
+ int expectedBitCount; // unknown at this point
+ int lastValueBit;
+ for (unsigned int byte = 0; byte < size; byte++) {
+ char originalByte = data[byte];
+ bytesConsumed++;
+ unsigned char maskBit = 0x80; // LEFT MOST BIT set
+ for (int bit = 0; bit < BITS_IN_BYTE; bit++) {
+ bool bitIsSet = originalByte & maskBit;
+ // Processing of the lead bits
+ if (inLeadBits) {
+ if (bitIsSet) {
+ encodedByteCount++;
+ leadBits++;
+ } else {
+ inLeadBits = false; // once we hit our first 0, we know we're out of the lead bits
+ expectedBitCount = (encodedByteCount * BITS_IN_BYTE) - leadBits;
+ lastValueBit = expectedBitCount + bitAt;
+
+ // check to see if the remainder of our buffer is sufficient
+ if (expectedBitCount > (bitCount - leadBits)) {
+ break;
+ }
+ }
+ } else {
+ if (bitAt > lastValueBit) {
+ break;
+ }
+
+ if (bitIsSet) {
+ setHasProperty(static_cast(bitAt - leadBits), true);
+ }
+ }
+ bitAt++;
+ maskBit >>= 1;
}
- }
-
- // next, read the leading bits to determine the correct number of bytes to decode (may not match the QByteArray)
- int encodedByteCount = 0;
- int leadBits = 1;
- int bitAt;
- for (bitAt = 0; bitAt < bitCount; bitAt++) {
- if (encodedBits.at(bitAt)) {
- encodedByteCount++;
- leadBits++;
- } else {
+ if (!inLeadBits && bitAt > lastValueBit) {
break;
}
}
- encodedByteCount++; // always at least one byte
- _encodedLength = encodedByteCount;
+ _encodedLength = bytesConsumed;
+ return bytesConsumed;
+}
- int expectedBitCount = encodedByteCount * BITS_PER_BYTE;
-
- // Now, keep reading...
- if (expectedBitCount <= (encodedBits.size() - leadBits)) {
- int flagsStartAt = bitAt + 1;
- for (bitAt = flagsStartAt; bitAt < expectedBitCount; bitAt++) {
- if (encodedBits.at(bitAt)) {
- setHasProperty((Enum)(bitAt - flagsStartAt));
- }
- }
- }
+template inline size_t PropertyFlags::decode(const QByteArray& fromEncodedBytes) {
+ return decode(reinterpret_cast(fromEncodedBytes.data()), fromEncodedBytes.size());
}
template inline void PropertyFlags::debugDumpBits() {
diff --git a/libraries/shared/src/QVariantGLM.cpp b/libraries/shared/src/QVariantGLM.cpp
index 7cebacee8e..7a3ab92cca 100644
--- a/libraries/shared/src/QVariantGLM.cpp
+++ b/libraries/shared/src/QVariantGLM.cpp
@@ -20,7 +20,7 @@ QVariantList glmToQList(const glm::quat& g) {
return QVariantList() << g.x << g.y << g.z << g.w;
}
-QVariantList rgbColorToQList(rgbColor& v) {
+QVariantList rgbColorToQList(const rgbColor& v) {
return QVariantList() << (int)(v[0]) << (int)(v[1]) << (int)(v[2]);
}
@@ -42,12 +42,12 @@ QVariantMap glmToQMap(const glm::quat& glmQuat) {
}
-glm::vec3 qListToGlmVec3(const QVariant q) {
+glm::vec3 qListToGlmVec3(const QVariant& q) {
QVariantList qList = q.toList();
return glm::vec3(qList[RED_INDEX].toFloat(), qList[GREEN_INDEX].toFloat(), qList[BLUE_INDEX].toFloat());
}
-glm::quat qListToGlmQuat(const QVariant q) {
+glm::quat qListToGlmQuat(const QVariant& q) {
QVariantList qList = q.toList();
float x = qList[0].toFloat();
float y = qList[1].toFloat();
@@ -56,7 +56,7 @@ glm::quat qListToGlmQuat(const QVariant q) {
return glm::quat(w, x, y, z);
}
-void qListtoRgbColor(const QVariant q, rgbColor returnValue) {
+void qListtoRgbColor(const QVariant& q, rgbColor& returnValue) {
QVariantList qList = q.toList();
returnValue[RED_INDEX] = qList[RED_INDEX].toInt();
returnValue[GREEN_INDEX] = qList[GREEN_INDEX].toInt();
diff --git a/libraries/shared/src/QVariantGLM.h b/libraries/shared/src/QVariantGLM.h
index 922aa7c4aa..3a91110250 100644
--- a/libraries/shared/src/QVariantGLM.h
+++ b/libraries/shared/src/QVariantGLM.h
@@ -19,11 +19,11 @@
QVariantList glmToQList(const glm::vec3& g);
QVariantList glmToQList(const glm::quat& g);
-QVariantList rgbColorToQList(rgbColor& v);
+QVariantList rgbColorToQList(const rgbColor& v);
QVariantMap glmToQMap(const glm::vec3& glmVector);
QVariantMap glmToQMap(const glm::quat& glmQuat);
-glm::vec3 qListToGlmVec3(const QVariant q);
-glm::quat qListToGlmQuat(const QVariant q);
-void qListtoRgbColor(const QVariant q, rgbColor returnValue);
+glm::vec3 qListToGlmVec3(const QVariant& q);
+glm::quat qListToGlmQuat(const QVariant& q);
+void qListtoRgbColor(const QVariant& q, rgbColor& returnValue);
diff --git a/libraries/shared/src/RegisteredMetaTypes.cpp b/libraries/shared/src/RegisteredMetaTypes.cpp
index 62f2be0512..dce31b2971 100644
--- a/libraries/shared/src/RegisteredMetaTypes.cpp
+++ b/libraries/shared/src/RegisteredMetaTypes.cpp
@@ -229,6 +229,7 @@ QScriptValue collisionToScriptValue(QScriptEngine* engine, const Collision& coll
obj.setProperty("idB", quuidToScriptValue(engine, collision.idB));
obj.setProperty("penetration", vec3toScriptValue(engine, collision.penetration));
obj.setProperty("contactPoint", vec3toScriptValue(engine, collision.contactPoint));
+ obj.setProperty("velocityChange", vec3toScriptValue(engine, collision.velocityChange));
return obj;
}
diff --git a/libraries/shared/src/Transform.h b/libraries/shared/src/Transform.h
index e961f379c8..6f841151c4 100644
--- a/libraries/shared/src/Transform.h
+++ b/libraries/shared/src/Transform.h
@@ -97,6 +97,8 @@ public:
const Vec3& getScale() const;
void setScale(float scale);
void setScale(const Vec3& scale); // [new this] = [this.translation] * [this.rotation] * [scale]
+ void preScale(float scale);
+ void preScale(const Vec3& scale);
void postScale(float scale); // [new this] = [this] * [scale] equivalent to glScale
void postScale(const Vec3& scale); // [new this] = [this] * [scale] equivalent to glScale
@@ -322,6 +324,14 @@ inline void Transform::setScale(const Vec3& scale) {
}
}
+inline void Transform::preScale(float scale) {
+ setScale(getScale() * scale);
+}
+
+inline void Transform::preScale(const Vec3& scale) {
+ setScale(getScale() * scale);
+}
+
inline void Transform::postScale(float scale) {
if (!isValidScale(scale) || scale == 1.0f) {
return;
diff --git a/tests/entities/CMakeLists.txt b/tests/entities/CMakeLists.txt
new file mode 100644
index 0000000000..0077549100
--- /dev/null
+++ b/tests/entities/CMakeLists.txt
@@ -0,0 +1,12 @@
+
+set(TARGET_NAME "entities-test")
+
+# This is not a testcase -- just set it up as a regular hifi project
+setup_hifi_project(Network Script)
+
+set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/")
+
+# link in the shared libraries
+link_hifi_libraries(entities avatars shared octree gpu model fbx networking animation environment)
+
+copy_dlls_beside_windows_executable()
\ No newline at end of file
diff --git a/tests/entities/packet.bin b/tests/entities/packet.bin
new file mode 100644
index 0000000000..295117172d
Binary files /dev/null and b/tests/entities/packet.bin differ
diff --git a/tests/entities/src/main.cpp b/tests/entities/src/main.cpp
new file mode 100644
index 0000000000..a255ffa995
--- /dev/null
+++ b/tests/entities/src/main.cpp
@@ -0,0 +1,173 @@
+//
+// main.cpp
+// tests/render-utils/src
+//
+// Copyright 2014 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+const QString& getTestResourceDir() {
+ static QString dir;
+ if (dir.isEmpty()) {
+ QDir path(__FILE__);
+ path.cdUp();
+ dir = path.cleanPath(path.absoluteFilePath("../")) + "/";
+ qDebug() << "Qml Test Path: " << dir;
+ }
+ return dir;
+}
+
+class StopWatch {
+public:
+ void start() {
+ Q_ASSERT(_start == 0);
+ _start = usecTimestampNow();
+ }
+
+ void stop() {
+ Q_ASSERT(_start != 0);
+ _last = usecTimestampNow() - _start;
+ _start = 0;
+ _total += _last;
+ _count++;
+ }
+
+ quint64 getLast() {
+ return _last;
+ }
+
+ quint64 getTotal() {
+ return _total;
+ }
+
+ float getAverage() {
+ return (float)_total / (float)_count;
+ }
+
+ void reset() {
+ _last = _start = _total = _count = 0;
+ }
+
+private:
+ size_t _count{ 0 };
+ quint64 _total{ 0 };
+ quint64 _start{ 0 };
+ quint64 _last{ 0 };
+};
+
+template
+void testByteCountCodedStable(const T& value) {
+ ByteCountCoded coder((T)value);
+ auto encoded = coder.encode();
+ #ifndef QT_NO_DEBUG
+ auto originalEncodedSize = encoded.size();
+ #endif
+ for (int i = 0; i < 10; ++i) {
+ encoded.append(qrand());
+ }
+ ByteCountCoded decoder;
+ decoder.decode(encoded);
+ Q_ASSERT(decoder.data == coder.data);
+ #ifndef QT_NO_DEBUG
+ auto consumed = decoder.decode(encoded.data(), encoded.size());
+ #endif
+ Q_ASSERT(consumed == (unsigned int)originalEncodedSize);
+
+}
+
+template
+void testByteCountCoded() {
+ testByteCountCodedStable(0);
+ testByteCountCodedStable(1);
+ testByteCountCodedStable(1 << 16);
+ testByteCountCodedStable(std::numeric_limits::max() >> 16);
+ testByteCountCodedStable(std::numeric_limits::max() >> 8);
+ testByteCountCodedStable(std::numeric_limits::max() >> 1);
+ testByteCountCodedStable(std::numeric_limits::max());
+}
+
+void testPropertyFlags(uint32_t value) {
+ EntityPropertyFlags original;
+ original.clear();
+ auto enumSize = sizeof(EntityPropertyList);
+ for (size_t i = 0; i < enumSize * 8; ++i) {
+ original.setHasProperty((EntityPropertyList)i);
+ }
+ QByteArray encoded = original.encode();
+ #ifndef QT_NO_DEBUG
+ auto originalSize = encoded.size();
+ #endif
+ for (size_t i = 0; i < enumSize; ++i) {
+ encoded.append(qrand());
+ }
+
+ EntityPropertyFlags decodeOld, decodeNew;
+ {
+ decodeOld.decode(encoded);
+ Q_ASSERT(decodeOld == original);
+ }
+
+ {
+ #ifndef QT_NO_DEBUG
+ auto decodeSize = decodeNew.decode((const uint8_t*)encoded.data(), encoded.size());
+ #endif
+ Q_ASSERT(originalSize == decodeSize);
+ Q_ASSERT(decodeNew == original);
+ }
+}
+
+void testPropertyFlags() {
+ testPropertyFlags(0);
+ testPropertyFlags(1);
+ testPropertyFlags(1 << 16);
+ testPropertyFlags(0xFFFF);
+}
+
+int main(int argc, char** argv) {
+ QCoreApplication app(argc, argv);
+ {
+ auto start = usecTimestampNow();
+ for (int i = 0; i < 1000; ++i) {
+ testPropertyFlags();
+ testByteCountCoded();
+ testByteCountCoded();
+ testByteCountCoded();
+ testByteCountCoded();
+ }
+ auto duration = usecTimestampNow() - start;
+ qDebug() << duration;
+
+ }
+ DependencyManager::set(NodeType::Unassigned);
+
+ QFile file(getTestResourceDir() + "packet.bin");
+ if (!file.open(QIODevice::ReadOnly)) return -1;
+ QByteArray packet = file.readAll();
+ EntityItemPointer item = BoxEntityItem::factory(EntityItemID(), EntityItemProperties());
+ ReadBitstreamToTreeParams params;
+ params.bitstreamVersion = 33;
+
+ auto start = usecTimestampNow();
+ for (int i = 0; i < 1000; ++i) {
+ item->readEntityDataFromBuffer(reinterpret_cast(packet.constData()), packet.size(), params);
+ }
+ float duration = (usecTimestampNow() - start);
+ qDebug() << (duration / 1000.0f);
+ return 0;
+}
+
+#include "main.moc"
diff --git a/tests/shared/src/TransformTests.cpp b/tests/shared/src/TransformTests.cpp
new file mode 100644
index 0000000000..93b0583aa6
--- /dev/null
+++ b/tests/shared/src/TransformTests.cpp
@@ -0,0 +1,84 @@
+//
+// TransformTests.cpp
+// tests/shared/src
+//
+// Copyright 2013-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 "TransformTests.h"
+#include "SharedLogging.h"
+
+using namespace glm;
+
+const vec3 xAxis(1.0f, 0.0f, 0.0f);
+const vec3 yAxis(0.0f, 1.0f, 0.0f);
+const vec3 zAxis(0.0f, 0.0f, 1.0f);
+const quat rot90 = angleAxis((float)M_PI / 2.0f, yAxis);
+
+QTEST_MAIN(TransformTests)
+
+const float EPSILON = 0.001f;
+
+void TransformTests::getMatrix() {
+
+ const vec3 t(0.0f, 0.0f, 10.0f);
+
+ // create a matrix that is composed of a PI/2 rotation followed by a small z translation
+ const mat4 m(vec4(rot90 * xAxis, 0.0f),
+ vec4(rot90 * yAxis, 0.0f),
+ vec4(rot90 * zAxis, 0.0f),
+ vec4(vec4(t, 1.0f)));
+
+ // postScale by a mirror about the x axis.
+ const mat4 mirrorX(vec4(-1.0f, 0.0f, 0.0f, 0.0f),
+ vec4( 0.0f, 1.0f, 0.0f, 0.0f),
+ vec4( 0.0f, 0.0f, 1.0f, 0.0f),
+ vec4( 0.0f, 0.0f, 0.0f, 1.0f));
+ const mat4 result_a = m * mirrorX;
+
+ Transform xform;
+ xform.setRotation(rot90);
+ xform.setTranslation(t);
+ xform.postScale(vec3(-1.0f, 1.0f, 1.0f));
+ mat4 result_b;
+ xform.getMatrix(result_b);
+
+ QCOMPARE_WITH_ABS_ERROR(result_a, result_b, EPSILON);
+}
+
+void TransformTests::getInverseMatrix() {
+
+ const vec3 t(0.0f, 0.0f, 10.0f);
+
+ // create a matrix that is composed of a PI/2 rotation followed by a small z translation
+ const mat4 m(vec4(rot90 * xAxis, 0.0f),
+ vec4(rot90 * yAxis, 0.0f),
+ vec4(rot90 * zAxis, 0.0f),
+ vec4(vec4(t, 1.0f)));
+
+ // mirror about the x axis.
+ const mat4 mirrorX(vec4(-1.0f, 0.0f, 0.0f, 0.0f),
+ vec4( 0.0f, 1.0f, 0.0f, 0.0f),
+ vec4( 0.0f, 0.0f, 1.0f, 0.0f),
+ vec4( 0.0f, 0.0f, 0.0f, 1.0f));
+ const mat4 result_a = inverse(m * mirrorX);
+
+ Transform xform;
+ xform.setTranslation(t);
+ xform.setRotation(rot90);
+
+ //
+ // change postScale to preScale and the test will pass...
+ //
+
+ xform.postScale(vec3(-1.0f, 1.0f, 1.0f));
+ mat4 result_b;
+ xform.getInverseMatrix(result_b);
+
+ QCOMPARE_WITH_ABS_ERROR(result_a, result_b, EPSILON);
+}
diff --git a/tests/shared/src/TransformTests.h b/tests/shared/src/TransformTests.h
new file mode 100644
index 0000000000..ab5c8cf144
--- /dev/null
+++ b/tests/shared/src/TransformTests.h
@@ -0,0 +1,52 @@
+//
+// TransformTests.h
+// tests/shared/src
+//
+// Copyright 2013-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_TransformTests_h
+#define hifi_TransformTests_h
+
+#include
+#include
+#include
+
+inline float getErrorDifference(const glm::mat4& a, const glm::mat4& b) {
+ float maxDiff = 0;
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ float diff = fabs(a[i][j] - b[i][j]);
+ maxDiff = std::max(diff, maxDiff);
+ }
+ }
+ return maxDiff;
+}
+
+inline QTextStream& operator<< (QTextStream& stream, const glm::mat4& matrix) {
+ stream << "[\n\t\t";
+ stream.setFieldWidth(15);
+ for (int r = 0; r < 4; ++r) {
+ for (int c = 0; c < 4; ++c) {
+ stream << matrix[c][r];
+ }
+ stream << "\n\t\t";
+ }
+ stream.setFieldWidth(0);
+ stream << "]\n\t"; // hacky as hell, but this should work...
+ return stream;
+}
+
+#include <../QTestExtensions.h>
+
+class TransformTests : public QObject {
+ Q_OBJECT
+private slots:
+ void getMatrix();
+ void getInverseMatrix();
+};
+
+#endif // hifi_TransformTests_h