Merge remote-tracking branch 'upstream/master' into plugins

Conflicts:
	interface/src/Application.cpp
	interface/src/GLCanvas.cpp
	interface/src/devices/OculusManager.cpp
	interface/src/devices/OculusManager.h
	libraries/render-utils/src/DeferredLightingEffect.cpp
This commit is contained in:
Brad Davis 2015-07-13 12:54:19 -07:00
commit 9eb15d3d55
119 changed files with 3487 additions and 1257 deletions

View file

@ -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;
}

View file

@ -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:

View file

@ -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;

View file

@ -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

View file

@ -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 ""

View file

@ -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);

58
examples/cubePerfTest.js Normal file
View file

@ -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);

View file

@ -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");

25
examples/example/games/airHockey.js Normal file → Executable file
View file

@ -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;

View file

@ -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);

View file

@ -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);

73
examples/stick-hydra.js Normal file
View file

@ -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();

View file

@ -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);
}
}

View file

@ -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

View file

@ -410,6 +410,11 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
static_cast<NodeList*>(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);
@ -918,10 +923,6 @@ void Application::paintGL() {
{
PerformanceTimer perfTimer("renderOverlay");
gpu::Context context(new gpu::GLBackend());
RenderArgs renderArgs(&context, nullptr, getViewFrustum(), lodManager->getOctreeSizeScale(),
lodManager->getBoundaryLevelAdjust(), RenderArgs::DEFAULT_RENDER_MODE,
RenderArgs::MONO, RenderArgs::RENDER_DEBUG_NONE);
_applicationOverlay.renderOverlay(&renderArgs);
}
@ -998,6 +999,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);
@ -1059,11 +1061,12 @@ 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
<<<<<<< HEAD
// deliver final composited scene to the display plugin
{
GLuint finalTexture = gpu::GLBackend::getTextureID(finalFbo->getRenderBuffer(0));
@ -1078,8 +1081,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++;
@ -1089,6 +1101,7 @@ void Application::paintGL() {
void Application::runTests() {
runTimingTests();
runUnitTests();
}
void Application::audioMuteToggled() {
@ -1119,8 +1132,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();
@ -1616,6 +1629,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) {
@ -1872,12 +1886,10 @@ void Application::checkFPS() {
_frameCount = 0;
_datagramProcessor->resetCounters();
_timerStart.start();
// ask the node list to check in with the domain server
DependencyManager::get<NodeList>()->sendDomainServerCheckIn();
}
void Application::idle() {
PROFILE_RANGE(__FUNCTION__);
static SimpleAverage<float> interIdleDurations;
static uint64_t lastIdleEnd{ 0 };
@ -1919,6 +1931,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));
}
{
@ -2397,36 +2410,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<Avatar*>(lookingAt.get())->getHead()->getRightEyePosition();
} else {
// Look at their left eye
lookAtSpot = static_cast<Avatar*>(lookingAt.get())->getHead()->getLeftEyePosition();
isLookingAtSomeone = true;
Head* lookingAtHead = static_cast<Avatar*>(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<Avatar*>(lookingAt.get())->getHead()->getLeftEyePosition();
} else {
lookAtSpot = static_cast<Avatar*>(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
@ -2434,14 +2452,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<Faceshift>()->getEyeDeflection();
@ -3032,6 +3049,25 @@ QRect Application::getDesirableApplicationGeometry() {
return applicationGeometry;
}
/////////////////////////////////////////////////////////////////////////////////////
// loadViewFrustum()
//
// Description: this will load the view frustum bounds for EITHER the head
// or the "myCamera".
//
void Application::loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum) {
PROFILE_RANGE(__FUNCTION__);
// We will use these below, from either the camera or head vectors calculated above
viewFrustum.setProjection(camera.getProjection());
// Set the viewFrustum up with the correct position and orientation of the camera
viewFrustum.setPosition(camera.getPosition());
viewFrustum.setOrientation(camera.getRotation());
// Ask the ViewFrustum class to calculate our corners
viewFrustum.calculate();
}
glm::vec3 Application::getSunDirection() {
// Sun direction is in fact just the location of the sun relative to the origin
auto skyStage = DependencyManager::get<SceneScriptingInterface>()->getSkyStage();
@ -3368,7 +3404,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()) {
@ -3376,8 +3412,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);
}
@ -3388,8 +3424,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);
}
@ -3442,23 +3478,9 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
// load the view frustum
theCamera.loadViewFrustum(_displayViewFrustum);
// Load the legacy GL stacks, used by entities (for now)
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadMatrixf(glm::value_ptr(theCamera.getProjection()));
glMatrixMode(GL_MODELVIEW);
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);
}
// transform view according to theCamera
// could be myCamera (if in normal mode)
// or could be viewFrustumOffsetCamera if in offset mode
glm::quat rotation = theCamera.getRotation();
// Equivalent to what is happening with _untranslatedViewMatrix and the _viewMatrixTranslation
@ -3467,8 +3489,11 @@ 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));
if (renderArgs->_renderSide != RenderArgs::MONO) {
glm::mat4 invView = glm::inverse(_untranslatedViewMatrix);
viewTransform.evalFromRawMatrix(invView);
viewTransform.preTranslate(_viewMatrixTranslation);
}
setViewTransform(viewTransform);
@ -3516,9 +3541,8 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
// Assuming nothing get's rendered through that
if (!selfAvatarOnly) {
// render models...
if (DependencyManager::get<SceneScriptingInterface>()->shouldRenderEntities()) {
// render models...
PerformanceTimer perfTimer("entities");
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
"Application::displaySide() ... entities...");
@ -3526,11 +3550,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);
@ -3555,8 +3579,8 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
pendingChanges.resetItem(WorldBoxRenderData::_item, worldBoxRenderPayload);
} else {
pendingChanges.updateItem<WorldBoxRenderData>(WorldBoxRenderData::_item,
[](WorldBoxRenderData& payload) {
pendingChanges.updateItem<WorldBoxRenderData>(WorldBoxRenderData::_item,
[](WorldBoxRenderData& payload) {
payload._val++;
});
}
@ -3576,7 +3600,7 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
}
{
PerformanceTimer perfTimer("SceneProcessPendingChanges");
PerformanceTimer perfTimer("SceneProcessPendingChanges");
_main3DScene->enqueuePendingChanges(pendingChanges);
_main3DScene->processPendingChangesQueue();

View file

@ -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;

View file

@ -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;
}

View file

@ -13,23 +13,24 @@
#define hifi_FileLogger_h
#include "AbstractLoggerInterface.h"
#include <QMutex>
#include <GenericQueueThread.h>
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

View file

@ -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;

View file

@ -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);
};

View file

@ -21,6 +21,7 @@
#include <QThread>
#include <ByteCountCoding.h>
#include <SharedUtil.h>
#include <TextRenderer.h>
@ -229,6 +230,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<quint64> codedValueA = valueA;
QByteArray codedValueABuffer = codedValueA;
codedValueABuffer.append(extraJunk);
ByteCountCoded<quint64> 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<quint64> usecs: " << elapsedUsecs
<< "per test:" << (double) (elapsedUsecs / tests)
<< "tests:" << tests
<< "failed:" << failed;
}
}
bool rayIntersectsSphere(const glm::vec3& rayStarting, const glm::vec3& rayNormalizedDirection,
@ -271,3 +309,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<quint64> 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<quint64> valueDecoder;
size_t bytesConsumed = valueDecoder.decode(codedValueBuffer);
quint64 valueDecoded = valueDecoder;
qDebug() << "valueDecoded:" << valueDecoded;
qDebug() << "bytesConsumed:" << bytesConsumed;
if (value == valueDecoded) {
qDebug() << "SUCCESS!";
} else {
qDebug() << "FAILED!";
}
}
}

View file

@ -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);

View file

@ -16,6 +16,9 @@
#include <AudioClient.h>
#include <AudioConstants.h>
#include <GeometryCache.h>
#include <TextureCache.h>
#include <gpu/Context.h>
#include <GLMHelpers.h>
#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>();
geometryCache->useSimpleDrawPipeline(batch);
auto textureCache = DependencyManager::get<TextureCache>();
batch.setResourceTexture(0, textureCache->getWhiteTexture());
mat4 legacyProjection = glm::ortho<float>(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<GeometryCache>()->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<GeometryCache>()->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,

View file

@ -14,11 +14,14 @@
#include <glm/glm.hpp>
#include <DependencyManager.h>
#include <QByteArray>
#include <QObject>
#include <DependencyManager.h>
#include <gpu/Batch.h>
#include <RenderArgs.h>
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,

View file

@ -644,7 +644,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<DeferredLightingEffect>()->bindSimpleProgram(batch, true);
DependencyManager::get<GeometryCache>()->renderQuad(batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight,
glm::vec4(1.0f, 1.0f, 1.0f, 1.0f));

View file

@ -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);
}

View file

@ -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();

View file

@ -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);
}
}
}

View file

@ -296,7 +296,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());
}
}
@ -324,7 +324,7 @@ glm::vec3 Head::getCorrectedLookAtPosition() {
}
void Head::setCorrectedLookAtPosition(glm::vec3 correctedLookAtPosition) {
_isLookingAtMe = true;
_isLookingAtMe = true;
_correctedLookAtPosition = correctedLookAtPosition;
}

View file

@ -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;

View file

@ -34,6 +34,9 @@
#include <TextRenderer.h>
#include <UserActivityLogger.h>
#include "devices/Faceshift.h"
#include "devices/OculusManager.h"
#include "Application.h"
#include "AvatarManager.h"
#include "Environment.h"
@ -42,7 +45,6 @@
#include "MyAvatar.h"
#include "Physics.h"
#include "Recorder.h"
#include "devices/Faceshift.h"
#include "Util.h"
#include "InterfaceLogging.h"
@ -97,7 +99,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),
@ -369,6 +371,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);
@ -378,6 +386,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);
}
@ -884,7 +898,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<AvatarManager>()->getAvatarHash()) {
Avatar* avatar = static_cast<Avatar*>(avatarPointer.get());
bool isCurrentTarget = avatar->getIsLookAtTarget();
@ -897,17 +910,22 @@ 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()->getCamera()->getPosition()
// + OculusManager::getMidEyePosition() + gazeOffset);
avatar->getHead()->setCorrectedLookAtPosition(Application::getInstance()->getViewFrustum()->getPosition()
+ OculusManager::getMidEyePosition() + gazeOffset);
} else {
avatar->getHead()->setCorrectedLookAtPosition(Application::getInstance()->getViewFrustum()->getPosition());
avatar->getHead()->setCorrectedLookAtPosition(Application::getInstance()->getViewFrustum()->getPosition()
+ gazeOffset);
}
} else {
avatar->getHead()->clearCorrectedLookAtPosition();
@ -924,12 +942,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 {

View file

@ -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)
@ -94,7 +100,7 @@ public:
bool isMyAvatar() const { return true; }
bool isLookingAtLeftEye();
eyeContactTarget getEyeContactTarget();
virtual int parseDataAtOffset(const QByteArray& packet, int offset);
@ -189,10 +195,12 @@ public slots:
void setThrust(glm::vec3 newThrust) { _thrust = newThrust; }
void updateMotionBehavior();
glm::vec3 getLeftPalmPosition();
glm::quat getLeftPalmRotation();
glm::vec3 getRightPalmPosition();
glm::quat getRightPalmRotation();
void clearReferential();
bool setModelReferential(const QUuid& id);
bool setJointReferential(const QUuid& id, int jointIndex);
@ -209,6 +217,7 @@ public slots:
signals:
void transformChanged();
void newCollisionSoundURL(const QUrl& url);
void collisionWithEntity(const Collision& collision);
private:
@ -251,7 +260,7 @@ private:
QList<AnimationHandlePointer> _animationHandles;
bool _feetTouchFloor;
bool _isLookingAtLeftEye;
eyeContactTarget _eyeContactTarget;
RecorderPointer _recorder;

View file

@ -179,11 +179,12 @@ void ApplicationCompositor::bindCursorTexture(gpu::Batch& batch, uint8_t cursorI
_cursors[iconId] = DependencyManager::get<TextureCache>()->
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;
}

View file

@ -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<GeometryCache>();
@ -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<float>(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<AudioScope>()->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<TextureCache>()->getWhiteTexture());
batch.setResourceTexture(0, DependencyManager::get<TextureCache>()->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

View file

@ -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<GeometryCache>()->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
}
}

View file

@ -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<GeometryCache>()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, quadColor);
} else {
fromImage.setX(0);
fromImage.setY(0);
fromImage.setWidth(imageWidth);
fromImage.setHeight(imageHeight);
DependencyManager::get<GeometryCache>()->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<GeometryCache>()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, quadColor);
glDisable(GL_TEXTURE_2D);
} else {
DependencyManager::get<GeometryCache>()->renderQuad(topLeft, bottomRight, quadColor);
}
if (_renderImage) {
glDisable(GL_TEXTURE_2D);
}
}
void ImageOverlay::setProperties(const QScriptValue& properties) {

View file

@ -96,6 +96,7 @@ void Overlays::cleanupOverlaysToDelete() {
}
void Overlays::renderHUD(RenderArgs* renderArgs) {
PROFILE_RANGE(__FUNCTION__);
QReadLocker lock(&_lock);
gpu::Batch batch;
renderArgs->_batch = &batch;

View file

@ -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;
}
}

View file

@ -802,6 +802,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>()), &SceneScriptingInterface::shouldRenderEntitiesChanged, this, &EntityTreeRenderer::updateEntityRenderStatus, Qt::QueuedConnection);
}
QScriptValueList EntityTreeRenderer::createMouseEventArgs(const EntityItemID& entityID, QMouseEvent* event, unsigned int deviceID) {
@ -1152,3 +1154,17 @@ void EntityTreeRenderer::entityCollisionWithEntity(const EntityItemID& idA, cons
entityScriptB.property("collisionWithEntity").call(entityScriptA, args);
}
}
void EntityTreeRenderer::updateEntityRenderStatus(bool shouldRenderEntities) {
if (DependencyManager::get<SceneScriptingInterface>()->shouldRenderEntities()) {
for (auto entityID : _entityIDsLastInScene) {
addingEntity(entityID);
}
_entityIDsLastInScene.clear();
} else {
_entityIDsLastInScene = _entitiesInScene.keys();
for (auto entityID : _entityIDsLastInScene) {
deletingEntity(entityID);
}
}
}

View file

@ -90,6 +90,9 @@ public:
virtual void scriptContentsAvailable(const QUrl& url, const QString& scriptContents);
virtual void errorInLoadingScript(const QUrl& url);
// For Scene.shouldRenderEntities
QList<EntityItemID>& 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<EntityItemID, EntityItemPointer> _entitiesInScene;
// For Scene.shouldRenderEntities
QList<EntityItemID> _entityIDsLastInScene;
};

View file

@ -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);
}
}

View file

@ -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<DeferredLightingEffect>()->bindSimpleProgram(batch, textured);

View file

@ -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; }
};

View file

@ -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;
}

View file

@ -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;
};

View file

@ -12,9 +12,11 @@
#include "EntityItem.h"
#include <QtCore/QObject>
#include <QtEndian>
#include <glm/gtx/transform.hpp>
#include <BufferParser.h>
#include <ByteCountCoding.h>
#include <GLMHelpers.h>
#include <Octree.h>
@ -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() == bytesRead);
}
#endif
// type
parser.readCompressedCount<quint32>((quint32&)_type);
#ifdef VALIDATE_ENTITY_ITEM_PARSER
QByteArray encodedType = originalDataBuffer.mid(bytesRead); // maximum possible size
ByteCountCoded<quint32> 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() == 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() == 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() == 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<quint64> updateDeltaCoder = encodedUpdateDelta;
quint64 updateDelta = updateDeltaCoder;
quint64 updateDelta;
parser.readCompressedCount(updateDelta);
#ifdef VALIDATE_ENTITY_ITEM_PARSER
{
QByteArray encodedUpdateDelta = originalDataBuffer.mid(bytesRead); // maximum possible size
ByteCountCoded<quint64> updateDeltaCoder = encodedUpdateDelta;
quint64 updateDelta2 = updateDeltaCoder;
Q_ASSERT(updateDelta == updateDelta2);
encodedUpdateDelta = updateDeltaCoder; // determine true length
dataAt += encodedUpdateDelta.size();
bytesRead += encodedUpdateDelta.size();
Q_ASSERT(parser.offset() == 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<quint64> simulatedDeltaCoder = encodedSimulatedDelta;
quint64 simulatedDelta = simulatedDeltaCoder;
quint64 simulatedDelta;
parser.readCompressedCount(simulatedDelta);
#ifdef VALIDATE_ENTITY_ITEM_PARSER
{
QByteArray encodedSimulatedDelta = originalDataBuffer.mid(bytesRead); // maximum possible size
ByteCountCoded<quint64> simulatedDeltaCoder = encodedSimulatedDelta;
quint64 simulatedDelta2 = simulatedDeltaCoder;
Q_ASSERT(simulatedDelta2 == simulatedDelta);
encodedSimulatedDelta = simulatedDeltaCoder; // determine true length
dataAt += encodedSimulatedDelta.size();
bytesRead += encodedSimulatedDelta.size();
Q_ASSERT(parser.offset() == 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() == 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<QUuid, EntityActionPointer>::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<QByteArray> 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<QUuid> 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<QByteArray> 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<QUuid> 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<EntityActionFactoryInterface>();
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<EntityActionFactoryInterface>();
// 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<QUuid, EntityActionPointer>::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<QUuid, EntityActionPointer>::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

View file

@ -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<EntityItem> {
// 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<QUuid> 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<QUuid, EntityActionPointer> _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<QUuid> _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

View file

@ -11,7 +11,7 @@
#include <QtCore/QObject>
#include <QDebug>
#include <BufferParser.h>
#include <PacketHeaders.h>
#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;
}

View file

@ -128,6 +128,10 @@ void EntityItemProperties::setSittingPoints(const QVector<SittingPoint>& 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

View file

@ -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<NodeList>();

View file

@ -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();

View file

@ -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);

View file

@ -12,6 +12,17 @@
#include <QDebug>
#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));
}

View file

@ -18,6 +18,7 @@
#include <vector>
#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<Transform>::Vector TransformCaches;
typedef Cache<PipelinePointer>::Vector PipelineCaches;
typedef Cache<FramebufferPointer>::Vector FramebufferCaches;
typedef Cache<QueryPointer>::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:
};

View file

@ -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();
}

View file

@ -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<T*>(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<T*>(query.getGPUObject());
}
protected:
};

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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,
};
};

View file

@ -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),
@ -233,17 +236,34 @@ void GLBackend::do_clearFramebuffer(Batch& batch, uint32 paramOffset) {
glmask |= GL_DEPTH_BUFFER_BIT;
}
std::vector<GLenum> drawBuffers;
if (masks & Framebuffer::BUFFER_COLORS) {
glClearColor(color.x, color.y, color.z, color.w);
glmask |= GL_COLOR_BUFFER_BIT;
for (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 +618,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();
}

View file

@ -181,6 +181,7 @@ public:
class GLFramebuffer : public GPUObject {
public:
GLuint _fbo = 0;
std::vector<GLenum> _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

View file

@ -40,8 +40,7 @@ GLBackend::GLFramebuffer* GLBackend::syncGPUObject(const Framebuffer& framebuffe
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
unsigned int nbColorBuffers = 0;
GLenum colorBuffers[16];
std::vector<GLenum> 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;
}
}

View file

@ -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);

View file

@ -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<GLBackend::GLQuery>(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();
}
}

View file

@ -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) {

View file

@ -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;
}
}

View file

@ -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, &currentMode);
@ -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();
}

View file

@ -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 <QDebug>
using namespace gpu;
Query::Query()
{
}
Query::~Query()
{
}
double Query::getElapsedTime() {
return 0.0;
}

View file

@ -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 <assert.h>
#include <memory>
#include <vector>
#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<Query> QueryPointer;
typedef std::vector< QueryPointer > Queries;
};
#endif

View file

@ -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 ) {

View file

@ -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;
}

View file

@ -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 <assert.h>
#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

View file

@ -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);
}

View file

@ -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,

View file

@ -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

View file

@ -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<gpu::Primitive>(topo); }
protected:
gpu::Stream::FormatPointer _vertexFormat;

View file

@ -77,18 +77,27 @@ void Light::setMaximumRadius(float radius) {
editSchema()._attenuation = Vec4(surfaceRadius, 1.0f/surfaceRadius, CutOffIntensityRatio, radius);
}
#include <math.h>
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<double>()) {
dangle = glm::half_pi<double>();
}
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;
}

View file

@ -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};

View file

@ -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);
}

View file

@ -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) {

View file

@ -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<btDynamicsWorld*>(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);
}

View file

@ -17,8 +17,6 @@
#include <btBulletDynamicsCommon.h>
#include <EntityItem.h>
#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;
};

View file

@ -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<ObjectMotionState*>(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;

View file

@ -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:

View file

@ -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;

View file

@ -12,24 +12,19 @@
#ifndef hifi_ObjectActionSpring_h
#define hifi_ObjectActionSpring_h
#include <QUuid>
#include <EntityItem.h>
#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:

View file

@ -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.
}

View file

@ -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 <gpu/GPUConfig.h>
#include <GLMHelpers.h>
@ -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"
@ -90,26 +91,40 @@ void DeferredLightingEffect::init(AbstractViewStateInterface* viewState) {
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);
@ -131,7 +146,7 @@ void DeferredLightingEffect::bindSimpleProgram(gpu::Batch& batch, bool textured,
if (!config.isTextured()) {
// If it is not textured, bind white texture and keep using textured pipeline
batch.setUniformTexture(0, DependencyManager::get<TextureCache>()->getWhiteTexture());
batch.setResourceTexture(0, DependencyManager::get<TextureCache>()->getWhiteTexture());
}
}
@ -201,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>();
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<TextureCache>();
glBindFramebuffer(GL_FRAMEBUFFER, 0 );
QSize framebufferSize = textureCache->getFrameBufferSize();
// binding the first framebuffer
auto freeFBO = DependencyManager::get<GlowEffect>()->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];
@ -264,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) {
@ -321,221 +323,238 @@ void DeferredLightingEffect::render(RenderArgs* args) {
sh = (*_skybox->getCubemap()->getIrradiance());
}
for (int i =0; i <gpu::SphericalHarmonics::NUM_COEFFICIENTS; i++) {
program->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<GeometryCache>()->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<GeometryCache>();
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<GeometryCache>()->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));
glPushMatrix();
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 * tan(0.5 * light->getSpotAngle()), 1.0f);
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<GeometryCache>()->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<model::Mesh::Part>();
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<TextureCache>();
QSize framebufferSize = textureCache->getFrameBufferSize();
auto freeFBO = DependencyManager::get<GlowEffect>()->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;
@ -548,21 +567,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) {
@ -570,67 +587,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) {
@ -655,3 +667,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;
}

View file

@ -21,6 +21,7 @@
#include "model/Light.h"
#include "model/Stage.h"
#include "model/Geometry.h"
class AbstractViewStateInterface;
class RenderArgs;
@ -94,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);
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<SimpleProgramKey, gpu::PipelinePointer> _simplePrograms;
ProgramObject _directionalSkyboxLight;
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;

View file

@ -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);

View file

@ -301,6 +301,7 @@ private:
};
QHash<IntPair, VerticesIndices> _coneVBOs;
int _nextID;
QHash<int, Vec3PairVec4Pair> _lastRegisteredQuad3DTexture;

View file

@ -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<NetworkGeomet
}
void Blender::run() {
PROFILE_RANGE(__FUNCTION__);
QVector<glm::vec3> 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<int,int>(meshIndex, partIndex)];
_mutex.unlock();
qint64 offset;
{
// FIXME_STUTTER: We should n't have any lock here
_mutex.lock();
offset = _calculatedMeshPartOffset[QPair<int,int>(meshIndex, partIndex)];
_mutex.unlock();
}
if (part.quadIndices.size() > 0) {
batch.drawIndexed(gpu::QUADS, part.quadIndices.size(), offset);

View file

@ -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);

View file

@ -77,6 +77,9 @@ public:
virtual void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext);
gpu::Queries _timerQueries;
int _currentTimerQueryIndex = 0;
};

View file

@ -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);

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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;

View file

@ -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) {

Some files were not shown because too many files have changed in this diff Show more