merge with plugins

This commit is contained in:
Anthony J. Thibault 2015-07-14 12:30:47 -07:00
commit dbc18ce51f
135 changed files with 3958 additions and 1518 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);

View file

@ -65,70 +65,55 @@ function removeLine() {
function createOrUpdateLine(event) {
var pickRay = Camera.computePickRay(event.x, event.y);
var intersection = Entities.findRayIntersection(pickRay, true); // accurate picking
var props = Entities.getEntityProperties(intersection.entityID);
var pickRay = Camera.computePickRay(event.x, event.y);
var intersection = Entities.findRayIntersection(pickRay, true); // accurate picking
var props = Entities.getEntityProperties(intersection.entityID);
if (intersection.intersects && userCanPoint) {
var points = [nearLinePoint(intersection.intersection), intersection.intersection]
if (lineIsRezzed) {
Entities.editEntity(lineEntityID, {
position: nearLinePoint(intersection.intersection),
linePoints: points,
dimensions: {
x: 1,
y: 1,
z: 1
},
lifetime: 15 + props.lifespan // renew lifetime
});
if (intersection.intersects && userCanPoint) {
var points = [Vec3.subtract(nearLinePoint(intersection.intersection), MyAvatar.position),
Vec3.subtract(intersection.intersection, MyAvatar.position)];
if (lineIsRezzed) {
Entities.editEntity(lineEntityID, {
linePoints: points,
position: MyAvatar.position,
lifetime: 15 + props.lifespan // renew lifetime
});
// Entities.setAllPoints(lineEntityID, points);
} else {
lineIsRezzed = true;
lineEntityID = Entities.addEntity({
type: "Line",
position: MyAvatar.position,
linePoints: points,
dimensions: { x: 100, y: 100, z: 100 },
color: { red: 255, green: 255, blue: 255 },
lifetime: 15 // if someone crashes while pointing, don't leave the line there forever.
});
}
} else {
lineIsRezzed = true;
lineEntityID = Entities.addEntity({
type: "Line",
position: nearLinePoint(intersection.intersection),
linePoints: points,
dimensions: {
x: 1,
y: 1,
z: 1
},
color: {
red: 255,
green: 255,
blue: 255
},
lifetime: 15 // if someone crashes while pointing, don't leave the line there forever.
});
removeLine();
}
} else {
removeLine();
}
}
function mousePressEvent(event) {
if (!event.isLeftButton) {
return;
}
createOrUpdateLine(event);
var clickedOverlay = Overlays.getOverlayAtPoint({
x: event.x,
y: event.y
});
if (clickedOverlay == pointerButton) {
userCanPoint = !userCanPoint;
if (userCanPoint === true) {
Overlays.editOverlay(pointerButton, {
color: buttonOnColor
});
} else {
Overlays.editOverlay(pointerButton, {
color: buttonOffColor
});
if (!event.isLeftButton) {
return;
}
var clickedOverlay = Overlays.getOverlayAtPoint({
x: event.x,
y: event.y
});
if (clickedOverlay == pointerButton) {
userCanPoint = !userCanPoint;
if (userCanPoint === true) {
Overlays.editOverlay(pointerButton, { color: buttonOnColor });
} else {
Overlays.editOverlay(pointerButton, { color: buttonOffColor });
}
}
}
}

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

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 261 261" enable-background="new 0 0 261 261" xml:space="preserve">
<g>
<g>
<circle fill="#149ABB" cx="130.5" cy="130.5" r="130.5"/>
</g>
<g>
<path fill="#0C9AB2" d="M251,80.6c27.5,66.6-4.3,142.9-70.9,170.4S37.1,246.7,9.6,180"/>
</g>
<g opacity="0.21">
<g>
<path fill="#FFFFFF" d="M130.5,1.8c17.4,0,34.2,3.4,50.1,10.1c15.3,6.5,29.1,15.8,40.9,27.6s21.1,25.6,27.6,40.9
c6.7,15.9,10.1,32.7,10.1,50.1s-3.4,34.2-10.1,50.1c-6.5,15.3-15.8,29.1-27.6,40.9c-11.8,11.8-25.6,21.1-40.9,27.6
c-15.9,6.7-32.7,10.1-50.1,10.1s-34.2-3.4-50.1-10.1c-15.3-6.5-29.1-15.8-40.9-27.6c-11.8-11.8-21.1-25.6-27.6-40.9
c-6.7-15.9-10.1-32.7-10.1-50.1s3.4-34.2,10.1-50.1c6.5-15.3,15.8-29.1,27.6-40.9s25.6-21.1,40.9-27.6
C96.3,5.2,113.1,1.8,130.5,1.8 M130.5,0C58.4,0,0,58.4,0,130.5S58.4,261,130.5,261S261,202.6,261,130.5S202.6,0,130.5,0L130.5,0z
"/>
</g>
</g>
<rect x="100.8" y="65.2" fill="#FFFFFF" width="59.4" height="98"/>
<rect x="3.4" y="169.4" fill="#FFFFFF" width="254.2" height="1.9"/>
<rect x="3.4" y="176" fill="#FFFFFF" width="254.2" height="1.9"/>
<rect x="3.4" y="184.7" fill="#FFFFFF" width="254.2" height="1.9"/>
<rect x="3.4" y="197.9" fill="#FFFFFF" width="254.2" height="1.9"/>
<path fill="#FFFFFF" d="M93.7,169.5l-14.7,75.8c0,0,47.4,23.4,101.2,0.5c33.3-14.2,51.5-41.5,51.5-41.5L154,170.2L93.7,169.5z"/>
<path fill="#FFFFFF" d="M166,169.5l14.7,75.8c0,0-47.4,23.4-101.2,0.5c-33.3-14.2-51.5-41.5-51.5-41.5l77.5-34.1L166,169.5z"/>
<rect x="85.8" y="184.7" fill="#149ABB" width="89.4" height="1.9"/>
<rect x="98" y="176" fill="#149ABB" width="65.1" height="1.9"/>
<rect x="56.4" y="197.9" fill="#149ABB" width="148.2" height="1.9"/>
<rect x="51.4" y="219.4" fill="#149ABB" width="158.3" height="1.9"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2 KiB

View file

@ -41,8 +41,6 @@ DialogContainer {
readonly property int closeMargin: 16
readonly property real tan30: 0.577 // tan(30°)
readonly property int inputSpacing: 16
property int maximumX: parent ? parent.width - width : 0
property int maximumY: parent ? parent.height - height : 0
Rectangle {
id: backgroundRectangle

View file

@ -1,164 +1,176 @@
import Hifi 1.0
import QtQuick 2.3
import QtQuick.Controls 1.3
import QtQuick.Controls.Styles 1.3
import QtGraphicalEffects 1.0
import "controls"
import "styles"
DialogContainer {
HifiConstants { id: hifi }
id: root
HifiConstants { id: hifi }
objectName: "UpdateDialog"
implicitWidth: updateDialog.width
implicitHeight: updateDialog.height
implicitWidth: updateDialog.implicitWidth
implicitHeight: updateDialog.implicitHeight
x: parent ? parent.width / 2 - width / 2 : 0
y: parent ? parent.height / 2 - height / 2 : 0
property int maximumX: parent ? parent.width - width : 0
property int maximumY: parent ? parent.height - height : 0
UpdateDialog {
id: updateDialog
implicitWidth: backgroundRectangle.width
implicitHeight: backgroundRectangle.height
readonly property int inputWidth: 500
readonly property int inputHeight: 60
readonly property int contentWidth: 500
readonly property int logoSize: 60
readonly property int borderWidth: 30
readonly property int closeMargin: 16
readonly property int inputSpacing: 16
readonly property int buttonWidth: 150
readonly property int buttonHeight: 50
readonly property int buttonRadius: 15
readonly property int buttonWidth: 100
readonly property int buttonHeight: 30
readonly property int noticeHeight: 15 * inputSpacing
readonly property string fontFamily: Qt.platform.os === "windows" ? "Trebuchet MS" : "Trebuchet"
signal triggerBuildDownload
signal closeUpdateDialog
Rectangle {
id: backgroundRectangle
color: "#ffffff"
width: updateDialog.contentWidth + updateDialog.borderWidth * 2
height: mainContent.height + updateDialog.borderWidth * 2 - updateDialog.closeMargin / 2
MouseArea {
width: parent.width
height: parent.height
anchors {
horizontalCenter: parent.horizontalCenter
verticalCenter: parent.verticalCenter
}
drag {
target: root
minimumX: 0
minimumY: 0
maximumX: root.parent ? root.maximumX : 0
maximumY: root.parent ? root.maximumY : 0
}
}
}
Image {
id: logo
source: "../images/interface-logo.svg"
width: updateDialog.logoSize
height: updateDialog.logoSize
anchors {
top: mainContent.top
right: mainContent.right
}
}
Column {
id: mainContent
width: updateDialog.inputWidth
width: updateDialog.contentWidth
spacing: updateDialog.inputSpacing
anchors {
horizontalCenter: parent.horizontalCenter
verticalCenter: parent.verticalCenter
topMargin: updateDialog.borderWidth
top: parent.top
}
Rectangle {
id: backgroundRectangle
color: "#2c86b1"
opacity: 0.85
radius: updateDialog.closeMargin * 2
width: updateDialog.inputWidth + updateDialog.borderWidth * 2
height: updateDialog.inputHeight * 6 + updateDialog.closeMargin * 2
Rectangle {
id: dialogTitle
width: updateDialog.inputWidth
height: updateDialog.inputHeight
radius: height / 2
color: "#ebebeb"
id: header
width: parent.width - updateDialog.logoSize - updateDialog.inputSpacing
height: updateAvailable.height + versionDetails.height
Text {
id: updateAvailable
text: "Update Available"
font {
family: updateDialog.fontFamily
pixelSize: hifi.fonts.pixelSize * 1.5
weight: Font.DemiBold
}
color: "#303030"
}
Text {
id: versionDetails
text: updateDialog.updateAvailableDetails
font {
family: updateDialog.fontFamily
pixelSize: hifi.fonts.pixelSize * 0.6
letterSpacing: -0.5
}
color: hifi.colors.text
anchors {
top: parent.top
topMargin: updateDialog.inputSpacing
horizontalCenter: parent.horizontalCenter
}
Text {
id: updateAvailableText
text: "Update Available"
anchors {
verticalCenter: parent.verticalCenter
left: parent.left
leftMargin: updateDialog.inputSpacing
}
}
Text {
text: updateDialog.updateAvailableDetails
font.pixelSize: 14
color: hifi.colors.text
anchors {
verticalCenter: parent.verticalCenter
left: updateAvailableText.right
leftMargin: 13
}
top: updateAvailable.bottom
}
}
Flickable {
}
Rectangle {
width: parent.width
height: updateDialog.noticeHeight
border {
width: 1
color: "#a0a0a0"
}
ScrollView {
id: scrollArea
anchors {
top: dialogTitle.bottom
}
contentWidth: updateDialog.inputWidth
contentHeight: backgroundRectangle.height - (dialogTitle.height * 2.5)
width: updateDialog.inputWidth
height: backgroundRectangle.height - (dialogTitle.height * 2.5)
flickableDirection: Flickable.VerticalFlick
clip: true
TextEdit {
id: releaseNotes
wrapMode: TextEdit.Wrap
width: parent.width
readOnly: true
text: updateDialog.releaseNotes
font.pixelSize: 14
color: hifi.colors.text
anchors {
left: parent.left
leftMargin: updateDialog.borderWidth
}
}
}
Rectangle {
id: downloadButton
width: updateDialog.buttonWidth
height: updateDialog.buttonHeight
radius: updateDialog.buttonRadius
color: "green"
anchors {
top: scrollArea.bottom
topMargin: 10
right: backgroundRectangle.right
rightMargin: 15
}
width: parent.width - updateDialog.closeMargin
height: parent.height
horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff
verticalScrollBarPolicy: Qt.ScrollBarAsNeeded
anchors.right: parent.right
Text {
text: "Upgrade"
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
id: releaseNotes
wrapMode: Text.Wrap
width: parent.width - updateDialog.closeMargin
text: updateDialog.releaseNotes
color: hifi.colors.text
font {
family: updateDialog.fontFamily
pixelSize: hifi.fonts.pixelSize * 0.65
}
}
MouseArea {
id: downloadButtonAction
anchors.fill: parent
onClicked: updateDialog.triggerUpgrade()
cursorShape: "PointingHandCursor"
}
}
}
Row {
anchors.right: parent.right
spacing: updateDialog.inputSpacing
height: updateDialog.buttonHeight + updateDialog.closeMargin / 2
Rectangle {
id: cancelButton
width: updateDialog.buttonWidth
height: updateDialog.buttonHeight
radius: updateDialog.buttonRadius
color: "red"
anchors {
top: scrollArea.bottom
topMargin: 10
right: downloadButton.left
rightMargin: 15
}
anchors.bottom: parent.bottom
Text {
text: "Cancel"
color: "#0c9ab4" // Same as logo
font {
family: updateDialog.fontFamily
pixelSize: hifi.fonts.pixelSize * 1.2
weight: Font.DemiBold
}
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
}
MouseArea {
id: cancelButtonAction
anchors.fill: parent
@ -166,7 +178,35 @@ DialogContainer {
cursorShape: "PointingHandCursor"
}
}
Rectangle {
id: updateButton
width: updateDialog.buttonWidth
height: updateDialog.buttonHeight
anchors.bottom: parent.bottom
Text {
text: "Update"
color: "#0c9ab4" // Same as logo
font {
family: updateDialog.fontFamily
pixelSize: hifi.fonts.pixelSize * 1.2
weight: Font.DemiBold
}
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
}
MouseArea {
id: updateButtonAction
anchors.fill: parent
onClicked: updateDialog.triggerUpgrade()
cursorShape: "PointingHandCursor"
}
}
}
}
}
}
}

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

@ -413,6 +413,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);
@ -1022,6 +1027,7 @@ void Application::paintGL() {
// Primary scene rendering
{
PROFILE_RANGE(__FUNCTION__ "/mainRender");
if (displayPlugin->isStereo()) {
QRect r(QPoint(0, 0), QSize(size.width() / 2, size.height()));
glEnable(GL_SCISSOR_TEST);
@ -1083,7 +1089,7 @@ void Application::paintGL() {
#if 0
renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE;
if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) {
renderRearViewMirror(&renderArgs, _mirrorViewRect);
renderRearViewMirror(&renderArgs, _mirrorViewRect);
}
renderArgs._renderMode = RenderArgs::NORMAL_RENDER_MODE;
#endif
@ -1102,8 +1108,17 @@ void Application::paintGL() {
// FIXME? make the sync a parameter to preDisplay and let the plugin manage this
glWaitSync(sync, 0, GL_TIMEOUT_IGNORED);
glDeleteSync(sync);
displayPlugin->display(finalTexture, finalSize);
displayPlugin->finishFrame();
{
PROFILE_RANGE(__FUNCTION__ "/pluginDisplay");
displayPlugin->display(finalTexture, finalSize);
}
{
PROFILE_RANGE(__FUNCTION__ "/bufferSwap");
displayPlugin->finishFrame();
}
Q_ASSERT(!QOpenGLContext::currentContext());
_offscreenContext->makeCurrent();
_frameCount++;
@ -1113,6 +1128,7 @@ void Application::paintGL() {
void Application::runTests() {
runTimingTests();
runUnitTests();
}
void Application::audioMuteToggled() {
@ -1143,8 +1159,8 @@ void Application::resizeEvent(QResizeEvent * event) {
}
void Application::resizeGL() {
PROFILE_RANGE(__FUNCTION__);
auto displayPlugin = getActiveDisplayPlugin();
// Set the desired FBO texture size. If it hasn't changed, this does nothing.
// Otherwise, it must rebuild the FBOs
uvec2 framebufferSize = getActiveDisplayPlugin()->getRecommendedRenderSize();
@ -1641,6 +1657,7 @@ void Application::focusOutEvent(QFocusEvent* event) {
}
void Application::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) {
PROFILE_RANGE(__FUNCTION__);
// Used by application overlay to determine how to draw cursor(s)
_lastMouseMoveWasSimulated = deviceID > 0;
if (!_lastMouseMoveWasSimulated) {
@ -1897,12 +1914,10 @@ void Application::checkFPS() {
_frameCount = 0;
_datagramProcessor->resetCounters();
_timerStart.start();
// ask the node list to check in with the domain server
DependencyManager::get<NodeList>()->sendDomainServerCheckIn();
}
void Application::idle() {
PROFILE_RANGE(__FUNCTION__);
static SimpleAverage<float> interIdleDurations;
static uint64_t lastIdleEnd{ 0 };
@ -1944,6 +1959,7 @@ void Application::idle() {
PerformanceTimer perfTimer("update");
PerformanceWarning warn(showWarnings, "Application::idle()... update()");
const float BIGGEST_DELTA_TIME_SECS = 0.25f;
PROFILE_RANGE(__FUNCTION__ "/idleUpdate");
update(glm::clamp((float)timeSinceLastUpdate / 1000.0f, 0.0f, BIGGEST_DELTA_TIME_SECS));
}
{
@ -2422,36 +2438,41 @@ void Application::updateMyAvatarLookAtPosition() {
lookAtSpot = _myCamera.getPosition();
#if 0
// FIXME is this really necessary?
// When I am in mirror mode, just look right at the camera (myself); don't switch gaze points because when physically
// looking in a mirror one's eyes appear steady.
if (isHMDMode()) {
if (_myAvatar->isLookingAtLeftEye()) {
lookAtSpot = OculusManager::getLeftEyePosition();
} else {
lookAtSpot = OculusManager::getRightEyePosition();
}
lookAtSpot = _myCamera.getPosition() + OculusManager::getMidEyePosition();
}
#endif
} else {
AvatarSharedPointer lookingAt = _myAvatar->getLookAtTargetAvatar().lock();
if (lookingAt && _myAvatar != lookingAt.get()) {
isLookingAtSomeone = true;
// If I am looking at someone else, look directly at one of their eyes
if (tracker && !tracker->isMuted()) {
// If a face tracker is active, look at the eye for the side my gaze is biased toward
if (tracker->getEstimatedEyeYaw() > _myAvatar->getHead()->getFinalYaw()) {
// Look at their right eye
lookAtSpot = static_cast<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
@ -2459,14 +2480,13 @@ void Application::updateMyAvatarLookAtPosition() {
(_myAvatar->getHead()->getFinalOrientationInWorldFrame() * glm::vec3(0.0f, 0.0f, -TREE_SCALE));
}
}
//
// Deflect the eyes a bit to match the detected Gaze from 3D camera if active
//
if (tracker && !tracker->isMuted()) {
// Deflect the eyes a bit to match the detected gaze from Faceshift if active.
// DDE doesn't track eyes.
if (tracker && typeid(*tracker) == typeid(Faceshift) && !tracker->isMuted()) {
float eyePitch = tracker->getEstimatedEyePitch();
float eyeYaw = tracker->getEstimatedEyeYaw();
const float GAZE_DEFLECTION_REDUCTION_DURING_EYE_CONTACT = 0.1f;
// deflect using Faceshift gaze data
glm::vec3 origin = _myAvatar->getHead()->getEyePosition();
float pitchSign = (_myCamera.getMode() == CAMERA_MODE_MIRROR) ? -1.0f : 1.0f;
float deflection = DependencyManager::get<Faceshift>()->getEyeDeflection();
@ -3569,7 +3589,7 @@ namespace render {
const float APPROXIMATE_DISTANCE_FROM_HORIZON = 0.1f;
const float DOUBLE_APPROXIMATE_DISTANCE_FROM_HORIZON = 0.2f;
glm::vec3 sunDirection = (args->_viewFrustum->getPosition()/*getAvatarPosition()*/ - closestData.getSunLocation())
glm::vec3 sunDirection = (args->_viewFrustum->getPosition()/*getAvatarPosition()*/ - closestData.getSunLocation())
/ closestData.getAtmosphereOuterRadius();
float height = glm::distance(args->_viewFrustum->getPosition()/*theCamera.getPosition()*/, closestData.getAtmosphereCenter());
if (height < closestData.getAtmosphereInnerRadius()) {
@ -3577,8 +3597,8 @@ namespace render {
alpha = 0.0f;
if (sunDirection.y > -APPROXIMATE_DISTANCE_FROM_HORIZON) {
float directionY = glm::clamp(sunDirection.y,
-APPROXIMATE_DISTANCE_FROM_HORIZON, APPROXIMATE_DISTANCE_FROM_HORIZON)
float directionY = glm::clamp(sunDirection.y,
-APPROXIMATE_DISTANCE_FROM_HORIZON, APPROXIMATE_DISTANCE_FROM_HORIZON)
+ APPROXIMATE_DISTANCE_FROM_HORIZON;
alpha = (directionY / DOUBLE_APPROXIMATE_DISTANCE_FROM_HORIZON);
}
@ -3589,8 +3609,8 @@ namespace render {
(closestData.getAtmosphereOuterRadius() - closestData.getAtmosphereInnerRadius());
if (sunDirection.y > -APPROXIMATE_DISTANCE_FROM_HORIZON) {
float directionY = glm::clamp(sunDirection.y,
-APPROXIMATE_DISTANCE_FROM_HORIZON, APPROXIMATE_DISTANCE_FROM_HORIZON)
float directionY = glm::clamp(sunDirection.y,
-APPROXIMATE_DISTANCE_FROM_HORIZON, APPROXIMATE_DISTANCE_FROM_HORIZON)
+ APPROXIMATE_DISTANCE_FROM_HORIZON;
alpha = (directionY / DOUBLE_APPROXIMATE_DISTANCE_FROM_HORIZON);
}
@ -3652,14 +3672,6 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
glPushMatrix();
glLoadMatrixf(glm::value_ptr(glm::inverse(theCamera.getTransform())));
// FIXME just flip the texture coordinates
// flip x if in mirror mode (also requires reversing winding order for backface culling)
if (theCamera.getMode() == CAMERA_MODE_MIRROR) {
glScalef(-1.0f, 1.0f, 1.0f);
glFrontFace(GL_CW);
} else {
glFrontFace(GL_CCW);
}
glm::quat rotation = theCamera.getRotation();
// Equivalent to what is happening with _untranslatedViewMatrix and the _viewMatrixTranslation
@ -3668,10 +3680,6 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
Transform viewTransform;
viewTransform.setTranslation(theCamera.getPosition());
viewTransform.setRotation(rotation);
if (theCamera.getMode() == CAMERA_MODE_MIRROR) {
viewTransform.setScale(Transform::Vec3(-1.0f, 1.0f, 1.0f));
}
setViewTransform(viewTransform);
// Setup 3D lights (after the camera transform, so that they are positioned in world space)
@ -3717,9 +3725,8 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
// Assuming nothing get's rendered through that
if (!selfAvatarOnly) {
// render models...
if (DependencyManager::get<SceneScriptingInterface>()->shouldRenderEntities()) {
// render models...
PerformanceTimer perfTimer("entities");
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
"Application::displaySide() ... entities...");
@ -3727,11 +3734,11 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
RenderArgs::DebugFlags renderDebugFlags = RenderArgs::RENDER_DEBUG_NONE;
if (Menu::getInstance()->isOptionChecked(MenuOption::PhysicsShowHulls)) {
renderDebugFlags = (RenderArgs::DebugFlags) (renderDebugFlags | (int) RenderArgs::RENDER_DEBUG_HULLS);
renderDebugFlags = (RenderArgs::DebugFlags) (renderDebugFlags | (int)RenderArgs::RENDER_DEBUG_HULLS);
}
if (Menu::getInstance()->isOptionChecked(MenuOption::PhysicsShowOwned)) {
renderDebugFlags =
(RenderArgs::DebugFlags) (renderDebugFlags | (int) RenderArgs::RENDER_DEBUG_SIMULATION_OWNERSHIP);
(RenderArgs::DebugFlags) (renderDebugFlags | (int)RenderArgs::RENDER_DEBUG_SIMULATION_OWNERSHIP);
}
renderArgs->_debugFlags = renderDebugFlags;
_entities.render(renderArgs);
@ -3757,8 +3764,8 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
pendingChanges.resetItem(WorldBoxRenderData::_item, worldBoxRenderPayload);
} else {
pendingChanges.updateItem<WorldBoxRenderData>(WorldBoxRenderData::_item,
[](WorldBoxRenderData& payload) {
pendingChanges.updateItem<WorldBoxRenderData>(WorldBoxRenderData::_item,
[](WorldBoxRenderData& payload) {
payload._val++;
});
}
@ -3778,7 +3785,7 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
}
{
PerformanceTimer perfTimer("SceneProcessPendingChanges");
PerformanceTimer perfTimer("SceneProcessPendingChanges");
_main3DScene->enqueuePendingChanges(pendingChanges);
_main3DScene->processPendingChangesQueue();

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

@ -460,6 +460,7 @@ Menu::Menu() {
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderBoundingCollisionShapes);
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderLookAtVectors, 0, false);
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderFocusIndicator, 0, false);
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ShowWhosLookingAtMe, 0, false);
MenuWrapper* handOptionsMenu = developerMenu->addMenu("Hands");
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::AlignForearmsWithWrists, 0, false);

View file

@ -270,6 +270,7 @@ namespace MenuOption {
const QString ShowBordersEntityNodes = "Show Entity Nodes";
const QString ShowIKConstraints = "Show IK Constraints";
const QString ShowRealtimeEntityStats = "Show Realtime Entity Stats";
const QString ShowWhosLookingAtMe = "Show Who's Looking at Me";
const QString SimpleShadows = "Simple";
const QString SixenseEnabled = "Enable Hydra Support";
const QString ShiftHipsForIdleAnimations = "Shift hips for idle animations";

View file

@ -21,6 +21,7 @@
#include <QThread>
#include <ByteCountCoding.h>
#include <SharedUtil.h>
#include <TextRenderer.h>
@ -256,6 +257,43 @@ void runTimingTests() {
elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC;
qCDebug(interfaceapp, "vec3 assign and dot() usecs: %f, last result:%f",
(double)(elapsedUsecs / numTests), (double)result);
quint64 BYTE_CODE_MAX_TEST_VALUE = 99999999;
quint64 BYTE_CODE_TESTS_SKIP = 999;
QByteArray extraJunk;
const int EXTRA_JUNK_SIZE = 200;
extraJunk.append((unsigned char)255);
for (int i = 0; i < EXTRA_JUNK_SIZE; i++) {
extraJunk.append(QString("junk"));
}
{
startTime.start();
quint64 tests = 0;
quint64 failed = 0;
for (quint64 value = 0; value < BYTE_CODE_MAX_TEST_VALUE; value += BYTE_CODE_TESTS_SKIP) {
quint64 valueA = value; // usecTimestampNow();
ByteCountCoded<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,
@ -298,3 +336,39 @@ bool pointInSphere(glm::vec3& point, glm::vec3& sphereCenter, double sphereRadiu
}
return false;
}
void runUnitTests() {
quint64 LAST_TEST = 10;
quint64 SKIP_BY = 1;
for (quint64 value = 0; value <= LAST_TEST; value += SKIP_BY) {
qDebug() << "value:" << value;
ByteCountCoded<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

@ -453,22 +453,36 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, boo
}
}
// Stack indicator spheres
float indicatorOffset = 0.0f;
if (!_displayName.isEmpty() && _displayNameAlpha != 0.0f) {
const float DISPLAY_NAME_INDICATOR_OFFSET = 0.22f;
indicatorOffset = DISPLAY_NAME_INDICATOR_OFFSET;
}
const float INDICATOR_RADIUS = 0.03f;
const float INDICATOR_INDICATOR_OFFSET = 3.0f * INDICATOR_RADIUS;
// If this is the avatar being looked at, render a little ball above their head
if (_isLookAtTarget && Menu::getInstance()->isOptionChecked(MenuOption::RenderFocusIndicator)) {
const float LOOK_AT_INDICATOR_RADIUS = 0.03f;
const float LOOK_AT_INDICATOR_OFFSET = 0.22f;
const glm::vec4 LOOK_AT_INDICATOR_COLOR = { 0.8f, 0.0f, 0.0f, 0.75f };
glm::vec3 position;
if (_displayName.isEmpty() || _displayNameAlpha == 0.0f) {
position = glm::vec3(_position.x, getDisplayNamePosition().y, _position.z);
} else {
position = glm::vec3(_position.x, getDisplayNamePosition().y + LOOK_AT_INDICATOR_OFFSET, _position.z);
}
glm::vec3 position = glm::vec3(_position.x, getDisplayNamePosition().y + indicatorOffset, _position.z);
Transform transform;
transform.setTranslation(position);
batch.setModelTransform(transform);
DependencyManager::get<DeferredLightingEffect>()->renderSolidSphere(batch, LOOK_AT_INDICATOR_RADIUS
, 15, 15, LOOK_AT_INDICATOR_COLOR);
DependencyManager::get<DeferredLightingEffect>()->renderSolidSphere(batch, INDICATOR_RADIUS,
15, 15, LOOK_AT_INDICATOR_COLOR);
indicatorOffset += INDICATOR_INDICATOR_OFFSET;
}
// If the avatar is looking at me, render an indication that they area
if (getHead()->getIsLookingAtMe() && Menu::getInstance()->isOptionChecked(MenuOption::ShowWhosLookingAtMe)) {
const glm::vec4 LOOKING_AT_ME_COLOR = { 0.8f, 0.65f, 0.0f, 0.1f };
glm::vec3 position = glm::vec3(_position.x, getDisplayNamePosition().y + indicatorOffset, _position.z);
Transform transform;
transform.setTranslation(position);
batch.setModelTransform(transform);
DependencyManager::get<DeferredLightingEffect>()->renderSolidSphere(batch, INDICATOR_RADIUS,
15, 15, LOOKING_AT_ME_COLOR);
}
// quick check before falling into the code below:
@ -644,7 +658,7 @@ void Avatar::renderBillboard(RenderArgs* renderArgs) {
glm::vec2 texCoordBottomRight(1.0f, 1.0f);
gpu::Batch& batch = *renderArgs->_batch;
batch.setUniformTexture(0, _billboardTexture->getGPUTexture());
batch.setResourceTexture(0, _billboardTexture->getGPUTexture());
DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(batch, true);
DependencyManager::get<GeometryCache>()->renderQuad(batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight,
glm::vec4(1.0f, 1.0f, 1.0f, 1.0f));
@ -750,7 +764,7 @@ void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& frustum) co
const int text_y = -nameDynamicRect.height() / 2;
// Compute background position/size
static const float SLIGHTLY_BEHIND = -0.05f;
static const float SLIGHTLY_IN_FRONT = 0.1f;
const int border = 0.1f * nameDynamicRect.height();
const int left = text_x - border;
const int bottom = text_y - border;
@ -765,16 +779,16 @@ void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& frustum) co
// Compute display name transform
auto textTransform = calculateDisplayNameTransform(frustum, renderer->getFontSize());
// Render background slightly behind to avoid z-fighting
auto backgroundTransform = textTransform;
backgroundTransform.postTranslate(glm::vec3(0.0f, 0.0f, SLIGHTLY_BEHIND));
batch.setModelTransform(backgroundTransform);
DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(batch);
batch.setModelTransform(textTransform);
DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(batch, false, true, true, true);
DependencyManager::get<GeometryCache>()->renderBevelCornersRect(batch, left, bottom, width, height,
bevelDistance, backgroundColor);
// Render actual name
QByteArray nameUTF8 = renderedDisplayName.toLocal8Bit();
// Render text slightly in front to avoid z-fighting
textTransform.postTranslate(glm::vec3(0.0f, 0.0f, SLIGHTLY_IN_FRONT * renderer->getFontSize()));
batch.setModelTransform(textTransform);
renderer->draw(batch, text_x, -text_y, nameUTF8.data(), textColor);
}

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

@ -302,7 +302,7 @@ void Head::relaxLean(float deltaTime) {
void Head::render(RenderArgs* renderArgs, float alpha, ViewFrustum* renderFrustum, bool postLighting) {
if (_renderLookatVectors) {
renderLookatVectors(renderArgs, _leftEyePosition, _rightEyePosition, getCorrectedLookAtPosition());
renderLookatVectors(renderArgs, _leftEyePosition, _rightEyePosition, getCorrectedLookAtPosition());
}
}
@ -330,7 +330,7 @@ glm::vec3 Head::getCorrectedLookAtPosition() {
}
void Head::setCorrectedLookAtPosition(glm::vec3 correctedLookAtPosition) {
_isLookingAtMe = true;
_isLookingAtMe = true;
_correctedLookAtPosition = correctedLookAtPosition;
}

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,8 @@
#include <TextRenderer.h>
#include <UserActivityLogger.h>
#include "devices/Faceshift.h"
#include "Application.h"
#include "AvatarManager.h"
#include "Environment.h"
@ -42,7 +44,6 @@
#include "MyAvatar.h"
#include "Physics.h"
#include "Recorder.h"
#include "devices/Faceshift.h"
#include "Util.h"
#include "InterfaceLogging.h"
@ -99,7 +100,7 @@ MyAvatar::MyAvatar() :
_shouldRender(true),
_billboardValid(false),
_feetTouchFloor(true),
_isLookingAtLeftEye(true),
_eyeContactTarget(LEFT_EYE),
_realWorldFieldOfView("realWorldFieldOfView",
DEFAULT_REAL_WORLD_FIELD_OF_VIEW_DEGREES),
_firstPersonSkeletonModel(this),
@ -393,6 +394,12 @@ glm::vec3 MyAvatar::getLeftPalmPosition() {
return leftHandPosition;
}
glm::quat MyAvatar::getLeftPalmRotation() {
glm::quat leftRotation;
getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getLeftHandJointIndex(), leftRotation);
return leftRotation;
}
glm::vec3 MyAvatar::getRightPalmPosition() {
glm::vec3 rightHandPosition;
getSkeletonModel().getRightHandPosition(rightHandPosition);
@ -402,6 +409,12 @@ glm::vec3 MyAvatar::getRightPalmPosition() {
return rightHandPosition;
}
glm::quat MyAvatar::getRightPalmRotation() {
glm::quat rightRotation;
getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getRightHandJointIndex(), rightRotation);
return rightRotation;
}
void MyAvatar::clearReferential() {
changeReferential(NULL);
}
@ -908,7 +921,6 @@ void MyAvatar::updateLookAtTargetAvatar() {
const float KEEP_LOOKING_AT_CURRENT_ANGLE_FACTOR = 1.3f;
const float GREATEST_LOOKING_AT_DISTANCE = 10.0f;
int howManyLookingAtMe = 0;
foreach (const AvatarSharedPointer& avatarPointer, DependencyManager::get<AvatarManager>()->getAvatarHash()) {
Avatar* avatar = static_cast<Avatar*>(avatarPointer.get());
bool isCurrentTarget = avatar->getIsLookAtTarget();
@ -921,17 +933,20 @@ void MyAvatar::updateLookAtTargetAvatar() {
_targetAvatarPosition = avatarPointer->getPosition();
smallestAngleTo = angleTo;
}
// Check if this avatar is looking at me, and fix their gaze on my camera if so
if (Application::getInstance()->isLookingAtMyAvatar(avatar)) {
howManyLookingAtMe++;
// Have that avatar look directly at my camera
// Philip TODO: correct to look at left/right eye
if (qApp->isHMDMode()) {
avatar->getHead()->setCorrectedLookAtPosition(Application::getInstance()->getViewFrustum()->getPosition());
// FIXME what is the point of this?
// avatar->getHead()->setCorrectedLookAtPosition(OculusManager::getLeftEyePosition());
// Alter their gaze to look directly at my camera; this looks more natural than looking at my avatar's face.
// Offset their gaze according to whether they're looking at one of my eyes or my mouth.
glm::vec3 gazeOffset = avatar->getHead()->getLookAtPosition() - getHead()->getEyePosition();
const float HUMAN_EYE_SEPARATION = 0.065f;
float myEyeSeparation = glm::length(getHead()->getLeftEyePosition() - getHead()->getRightEyePosition());
gazeOffset = gazeOffset * HUMAN_EYE_SEPARATION / myEyeSeparation;
if (Application::getInstance()->isHMDMode()) {
avatar->getHead()->setCorrectedLookAtPosition(Application::getInstance()->getViewFrustum()->getPosition()
+ glm::vec3(qApp->getHMDSensorPose()[3]) + gazeOffset);
} else {
avatar->getHead()->setCorrectedLookAtPosition(Application::getInstance()->getViewFrustum()->getPosition());
avatar->getHead()->setCorrectedLookAtPosition(Application::getInstance()->getViewFrustum()->getPosition()
+ gazeOffset);
}
} else {
avatar->getHead()->clearCorrectedLookAtPosition();
@ -948,12 +963,24 @@ void MyAvatar::clearLookAtTargetAvatar() {
_lookAtTargetAvatar.reset();
}
bool MyAvatar::isLookingAtLeftEye() {
float const CHANCE_OF_CHANGING_EYE = 0.01f;
if (randFloat() < CHANCE_OF_CHANGING_EYE) {
_isLookingAtLeftEye = !_isLookingAtLeftEye;
eyeContactTarget MyAvatar::getEyeContactTarget() {
float const CHANCE_OF_CHANGING_TARGET = 0.01f;
if (randFloat() < CHANCE_OF_CHANGING_TARGET) {
float const FIFTY_FIFTY_CHANCE = 0.5f;
switch (_eyeContactTarget) {
case LEFT_EYE:
_eyeContactTarget = (randFloat() < FIFTY_FIFTY_CHANCE) ? MOUTH : RIGHT_EYE;
break;
case RIGHT_EYE:
_eyeContactTarget = (randFloat() < FIFTY_FIFTY_CHANCE) ? LEFT_EYE : MOUTH;
break;
case MOUTH:
_eyeContactTarget = (randFloat() < FIFTY_FIFTY_CHANCE) ? RIGHT_EYE : LEFT_EYE;
break;
}
}
return _isLookingAtLeftEye;
return _eyeContactTarget;
}
glm::vec3 MyAvatar::getDefaultEyePosition() const {

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)
@ -106,6 +112,7 @@ public:
bool isMyAvatar() const { return true; }
bool isLookingAtLeftEye();
eyeContactTarget getEyeContactTarget();
virtual int parseDataAtOffset(const QByteArray& packet, int offset);
@ -202,7 +209,9 @@ public slots:
void updateMotionBehavior();
glm::vec3 getLeftPalmPosition();
glm::quat getLeftPalmRotation();
glm::vec3 getRightPalmPosition();
glm::quat getRightPalmRotation();
void clearReferential();
bool setModelReferential(const QUuid& id);
@ -220,6 +229,7 @@ public slots:
signals:
void transformChanged();
void newCollisionSoundURL(const QUrl& url);
void collisionWithEntity(const Collision& collision);
private:
@ -273,7 +283,7 @@ private:
QList<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

@ -21,6 +21,7 @@
#include "Application.h"
#include "../octree/OctreePacketProcessor.h"
#include "ui/OctreeStatsDialog.h"
OctreeStatsDialog::OctreeStatsDialog(QWidget* parent, NodeToOctreeSceneStats* model) :
@ -53,7 +54,7 @@ OctreeStatsDialog::OctreeStatsDialog(QWidget* parent, NodeToOctreeSceneStats* mo
_localElementsMemory = AddStatItem("Elements Memory");
_sendingMode = AddStatItem("Sending Mode");
_processedPackets = AddStatItem("Processed Packets");
_processedPackets = AddStatItem("Entity Packets");
_processedPacketsElements = AddStatItem("Processed Packets Elements");
_processedPacketsEntities = AddStatItem("Processed Packets Entities");
_processedPacketsTiming = AddStatItem("Processed Packets Timing");
@ -155,6 +156,8 @@ void OctreeStatsDialog::paintEvent(QPaintEvent* event) {
if (sinceLastRefresh < REFRESH_AFTER) {
return QDialog::paintEvent(event);
}
const int FLOATING_POINT_PRECISION = 3;
_lastRefresh = now;
// Update labels
@ -245,7 +248,6 @@ void OctreeStatsDialog::paintEvent(QPaintEvent* event) {
auto averageElementsPerPacket = entities->getAverageElementsPerPacket();
auto averageEntitiesPerPacket = entities->getAverageEntitiesPerPacket();
auto averagePacketsPerSecond = entities->getAveragePacketsPerSecond();
auto averageElementsPerSecond = entities->getAverageElementsPerSecond();
auto averageEntitiesPerSecond = entities->getAverageEntitiesPerSecond();
@ -253,21 +255,32 @@ void OctreeStatsDialog::paintEvent(QPaintEvent* event) {
auto averageUncompressPerPacket = entities->getAverageUncompressPerPacket();
auto averageReadBitstreamPerPacket = entities->getAverageReadBitstreamPerPacket();
QString averageElementsPerPacketString = locale.toString(averageElementsPerPacket);
QString averageEntitiesPerPacketString = locale.toString(averageEntitiesPerPacket);
QString averageElementsPerPacketString = locale.toString(averageElementsPerPacket, 'f', FLOATING_POINT_PRECISION);
QString averageEntitiesPerPacketString = locale.toString(averageEntitiesPerPacket, 'f', FLOATING_POINT_PRECISION);
QString averagePacketsPerSecondString = locale.toString(averagePacketsPerSecond);
QString averageElementsPerSecondString = locale.toString(averageElementsPerSecond);
QString averageEntitiesPerSecondString = locale.toString(averageEntitiesPerSecond);
QString averageElementsPerSecondString = locale.toString(averageElementsPerSecond, 'f', FLOATING_POINT_PRECISION);
QString averageEntitiesPerSecondString = locale.toString(averageEntitiesPerSecond, 'f', FLOATING_POINT_PRECISION);
QString averageWaitLockPerPacketString = locale.toString(averageWaitLockPerPacket);
QString averageUncompressPerPacketString = locale.toString(averageUncompressPerPacket);
QString averageReadBitstreamPerPacketString = locale.toString(averageReadBitstreamPerPacket);
label = _labels[_processedPackets];
const OctreePacketProcessor& entitiesPacketProcessor = Application::getInstance()->getOctreePacketProcessor();
auto incomingPPS = entitiesPacketProcessor.getIncomingPPS();
auto processedPPS = entitiesPacketProcessor.getProcessedPPS();
auto treeProcessedPPS = entities->getAveragePacketsPerSecond();
QString incomingPPSString = locale.toString(incomingPPS, 'f', FLOATING_POINT_PRECISION);
QString processedPPSString = locale.toString(processedPPS, 'f', FLOATING_POINT_PRECISION);
QString treeProcessedPPSString = locale.toString(treeProcessedPPS, 'f', FLOATING_POINT_PRECISION);
statsValue.str("");
statsValue <<
"" << qPrintable(averagePacketsPerSecondString) << " per second";
"Network IN: " << qPrintable(incomingPPSString) << " PPS / " <<
"Queue OUT: " << qPrintable(processedPPSString) << " PPS / " <<
"Tree IN: " << qPrintable(treeProcessedPPSString) << " PPS";
label->setText(statsValue.str().c_str());
@ -321,7 +334,7 @@ void OctreeStatsDialog::paintEvent(QPaintEvent* event) {
}
QString totalTrackedEditsString = locale.toString((uint)totalTrackedEdits);
QString updatesPerSecondString = locale.toString(updatesPerSecond);
QString updatesPerSecondString = locale.toString(updatesPerSecond, 'f', FLOATING_POINT_PRECISION);
QString bytesPerEditString = locale.toString(bytesPerEdit);
statsValue.str("");

View file

@ -24,9 +24,18 @@ UpdateDialog::UpdateDialog(QQuickItem* parent) :
int currentVersion = QCoreApplication::applicationVersion().toInt();
int latestVersion = applicationUpdater.data()->getBuildData().lastKey();
int versionsBehind = latestVersion - currentVersion;
_updateAvailableDetails = "v" + QString::number(latestVersion) + " released on " + applicationUpdater.data()->getBuildData()[latestVersion]["releaseTime"];
_updateAvailableDetails += "\nYou are " + QString::number(versionsBehind) + " versions behind";
_releaseNotes = applicationUpdater.data()->getBuildData()[latestVersion]["releaseNotes"];
_updateAvailableDetails = "v" + QString::number(latestVersion) + " released on "
+ QString(applicationUpdater.data()->getBuildData()[latestVersion]["releaseTime"]).replace(" ", " ");
_updateAvailableDetails += "\nYou are " + QString::number(versionsBehind) + " version"
+ (versionsBehind > 1 ? "s" : "") + " behind";
_releaseNotes = "";
for (int i = latestVersion; i > currentVersion; i--) {
QString releaseNotes = applicationUpdater.data()->getBuildData()[i]["releaseNotes"];
releaseNotes.remove("<br />");
releaseNotes.remove(QRegExp("^\n+"));
_releaseNotes += "\n" + QString().sprintf("%d", i) + "\n" + releaseNotes + "\n";
}
}
const QString& UpdateDialog::updateAvailableDetails() const {

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

@ -40,14 +40,12 @@ void LocalModelsOverlay::render(RenderArgs* args) {
auto batch = args ->_batch;
Application* app = Application::getInstance();
glm::vec3 oldTranslation = app->getCurrentViewFrustum()->getPosition();
glm::vec3 oldTranslation = app->getViewFrustum()->getPosition();
Transform transform = Transform();
transform.setTranslation(oldTranslation + getPosition());
batch->setViewTransform(transform);
_entityTreeRenderer->render(args);
transform.setTranslation(oldTranslation);
batch->setViewTransform(transform);
if (glower) {
delete glower;
}

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

@ -35,14 +35,13 @@ void Rectangle3DOverlay::render(RenderArgs* args) {
if (!_visible) {
return; // do nothing if we're not visible
}
float alpha = getAlpha();
xColor color = getColor();
const float MAX_COLOR = 255.0f;
glm::vec4 rectangleColor(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha);
glm::vec3 position = getPosition();
glm::vec3 center = getCenter();
glm::vec2 dimensions = getDimensions();
glm::vec2 halfDimensions = dimensions * 0.5f;
glm::quat rotation = getRotation();
@ -67,7 +66,7 @@ void Rectangle3DOverlay::render(RenderArgs* args) {
glm::vec3 point2(halfDimensions.x, -halfDimensions.y, 0.0f);
glm::vec3 point3(halfDimensions.x, halfDimensions.y, 0.0f);
glm::vec3 point4(-halfDimensions.x, halfDimensions.y, 0.0f);
geometryCache->renderDashedLine(*batch, point1, point2, rectangleColor);
geometryCache->renderDashedLine(*batch, point2, point3, rectangleColor);
geometryCache->renderDashedLine(*batch, point3, point4, rectangleColor);

View file

@ -13,6 +13,7 @@
#include "Text3DOverlay.h"
#include <DeferredLightingEffect.h>
#include <RenderDeferredTask.h>
#include <TextRenderer3D.h>
@ -114,6 +115,7 @@ void Text3DOverlay::render(RenderArgs* args) {
glm::vec3 topLeft(-halfDimensions.x, -halfDimensions.y, SLIGHTLY_BEHIND);
glm::vec3 bottomRight(halfDimensions.x, halfDimensions.y, SLIGHTLY_BEHIND);
DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(batch, false, true, false, true);
DependencyManager::get<GeometryCache>()->renderQuad(batch, topLeft, bottomRight, quadColor);
// Same font properties as textSize()

33
interface/ui/temp.qml Normal file
View file

@ -0,0 +1,33 @@
import QtQuick 2.4
import QtQuick.Controls 2.3
import QtQuick.Controls.Styles 1.3
Item {
implicitHeight: 200
implicitWidth: 800
TextArea {
id: gutter
anchors.left: parent.left
anchors.top: parent.top
anchors.bottom: parent.bottom
style: TextAreaStyle {
backgroundColor: "grey"
}
width: 16
text: ">"
font.family: "Lucida Console"
}
TextArea {
anchors.left: gutter.right
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.right: parent.right
text: "undefined"
font.family: "Lucida Console"
}
}

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

@ -192,6 +192,11 @@ void Oculus_0_6_DisplayPlugin::activate(PluginContainer * container) {
ovrSizei & size = sceneLayer.Viewport[eye].Size = ovrHmd_GetFovTextureSize(_hmd, eye, fov, 1.0f);
sceneLayer.Viewport[eye].Pos = { eye == ovrEye_Left ? 0 : size.w, 0 };
});
// We're rendering both eyes to the same texture, so only one of the
// pointers is populated
sceneLayer.ColorTexture[0] = _sceneFbo->color;
// not needed since the structure was zeroed on init, but explicit
sceneLayer.ColorTexture[1] = nullptr;
PerformanceTimer::setActive(true);
@ -213,13 +218,6 @@ void Oculus_0_6_DisplayPlugin::customizeContext(PluginContainer * container) {
_sceneFbo = SwapFboPtr(new SwapFramebufferWrapper(_hmd));
_sceneFbo->Init(getRecommendedRenderSize());
// We're rendering both eyes to the same texture, so only one of the
// pointers is populated
ovrLayerEyeFov& sceneLayer = getSceneLayer();
sceneLayer.ColorTexture[0] = _sceneFbo->color;
// not needed since the structure was zeroed on init, but explicit
sceneLayer.ColorTexture[1] = nullptr;
}
void Oculus_0_6_DisplayPlugin::deactivate() {
@ -274,6 +272,7 @@ void Oculus_0_6_DisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sc
PerformanceTimer("OculusSubmit");
ovrLayerHeader* layers = &sceneLayer.Header;
ovrResult result = ovrHmd_SubmitFrame(_hmd, _frameIndex, nullptr, &layers, 1);
qDebug() << result;
}
_sceneFbo->Increment();

View file

@ -93,16 +93,18 @@ void EntityTreeRenderer::clear() {
foreach (const EntityItemID& entityID, _entityScripts.keys()) {
checkAndCallUnload(entityID);
}
OctreeRenderer::clear();
_entityScripts.clear();
auto scene = _viewState->getMain3DScene();
render::PendingChanges pendingChanges;
foreach(auto entity, _entitiesInScene) {
entity->removeFromScene(entity, scene, pendingChanges);
}
scene->enqueuePendingChanges(pendingChanges);
_entitiesInScene.clear();
OctreeRenderer::clear();
}
void EntityTreeRenderer::init() {
@ -802,6 +804,8 @@ void EntityTreeRenderer::connectSignalsToSlots(EntityScriptingInterface* entityS
connect(this, &EntityTreeRenderer::enterEntity, entityScriptingInterface, &EntityScriptingInterface::enterEntity);
connect(this, &EntityTreeRenderer::leaveEntity, entityScriptingInterface, &EntityScriptingInterface::leaveEntity);
connect(this, &EntityTreeRenderer::collisionWithEntity, entityScriptingInterface, &EntityScriptingInterface::collisionWithEntity);
connect(&(*DependencyManager::get<SceneScriptingInterface>()), &SceneScriptingInterface::shouldRenderEntitiesChanged, this, &EntityTreeRenderer::updateEntityRenderStatus, Qt::QueuedConnection);
}
QScriptValueList EntityTreeRenderer::createMouseEventArgs(const EntityItemID& entityID, QMouseEvent* event, unsigned int deviceID) {
@ -1001,7 +1005,7 @@ void EntityTreeRenderer::deletingEntity(const EntityItemID& entityID) {
checkAndCallUnload(entityID);
}
_entityScripts.remove(entityID);
// here's where we remove the entity payload from the scene
if (_entitiesInScene.contains(entityID)) {
auto entity = _entitiesInScene.take(entityID);
@ -1152,3 +1156,18 @@ void EntityTreeRenderer::entityCollisionWithEntity(const EntityItemID& idA, cons
entityScriptB.property("collisionWithEntity").call(entityScriptA, args);
}
}
void EntityTreeRenderer::updateEntityRenderStatus(bool shouldRenderEntities) {
if (DependencyManager::get<SceneScriptingInterface>()->shouldRenderEntities()) {
for (auto entityID : _entityIDsLastInScene) {
addingEntity(entityID);
}
_entityIDsLastInScene.clear();
} else {
_entityIDsLastInScene = _entitiesInScene.keys();
for (auto entityID : _entityIDsLastInScene) {
// FIXME - is this really right? do we want to do the deletingEntity() code or just remove from the scene.
deletingEntity(entityID);
}
}
}

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);
}
}
@ -188,6 +189,7 @@ void makeEntityItemStatusGetters(RenderableModelEntityItem* entity, render::Item
bool RenderableModelEntityItem::addToScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene,
render::PendingChanges& pendingChanges) {
_myMetaItem = scene->allocateID();
auto renderData = RenderableModelEntityItemMeta::Pointer(new RenderableModelEntityItemMeta(self));
@ -198,7 +200,10 @@ bool RenderableModelEntityItem::addToScene(EntityItemPointer self, std::shared_p
if (_model) {
render::Item::Status::Getters statusGetters;
makeEntityItemStatusGetters(this, statusGetters);
return _model->addToScene(scene, pendingChanges, statusGetters);
// note: we don't care if the model fails to add items, we always added our meta item and therefore we return
// true so that the system knows our meta item is in the scene!
_model->addToScene(scene, pendingChanges, statusGetters);
}
return true;

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

@ -36,10 +36,6 @@ void RenderableTextEntityItem::render(RenderArgs* args) {
glm::vec4 backgroundColor = glm::vec4(toGlm(getBackgroundColorX()), 1.0f);
glm::vec3 dimensions = getDimensions();
Transform transformToTopLeft = getTransformToCenter();
transformToTopLeft.postTranslate(glm::vec3(-0.5f, 0.5f, 0.0f)); // Go to the top left
transformToTopLeft.setScale(1.0f); // Use a scale of one so that the text is not deformed
// Render background
glm::vec3 minCorner = glm::vec3(0.0f, -dimensions.y, SLIGHTLY_BEHIND);
glm::vec3 maxCorner = glm::vec3(dimensions.x, 0.0f, SLIGHTLY_BEHIND);
@ -48,15 +44,22 @@ void RenderableTextEntityItem::render(RenderArgs* args) {
// Batch render calls
Q_ASSERT(args->_batch);
gpu::Batch& batch = *args->_batch;
Transform transformToTopLeft = getTransformToCenter();
if (getFaceCamera()) {
//rotate about vertical to face the camera
glm::vec3 dPosition = args->_viewFrustum->getPosition() - getPosition();
// If x and z are 0, atan(x, z) is undefined, so default to 0 degrees
float yawRotation = dPosition.x == 0.0f && dPosition.z == 0.0f ? 0.0f : glm::atan(dPosition.x, dPosition.z);
glm::quat orientation = glm::quat(glm::vec3(0.0f, yawRotation, 0.0f));
transformToTopLeft.setRotation(orientation);
}
transformToTopLeft.postTranslate(glm::vec3(-0.5f, 0.5f, 0.0f)); // Go to the top left
transformToTopLeft.setScale(1.0f); // Use a scale of one so that the text is not deformed
batch.setModelTransform(transformToTopLeft);
//rotate about vertical to face the camera
if (getFaceCamera()) {
transformToTopLeft.postRotate(args->_viewFrustum->getOrientation());
batch.setModelTransform(transformToTopLeft);
}
DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(batch, false, false);
DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(batch, false, false, false, true);
DependencyManager::get<GeometryCache>()->renderQuad(batch, minCorner, maxCorner, backgroundColor);
float scale = _lineHeight / _textRenderer->getFontSize();

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() == (unsigned int) 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() == (unsigned int) bytesRead);
#endif
bool overwriteLocalData = true; // assume the new content overwrites our local data
quint64 now = usecTimestampNow();
// _created
quint64 createdFromBuffer = 0;
memcpy(&createdFromBuffer, dataAt, sizeof(createdFromBuffer));
dataAt += sizeof(createdFromBuffer);
bytesRead += sizeof(createdFromBuffer);
quint64 now = usecTimestampNow();
if (_created == UNKNOWN_CREATED_TIME) {
// we don't yet have a _created timestamp, so we accept this one
createdFromBuffer -= clockSkew;
if (createdFromBuffer > now || createdFromBuffer == UNKNOWN_CREATED_TIME) {
createdFromBuffer = now;
{
quint64 createdFromBuffer = 0;
parser.readValue(createdFromBuffer);
#ifdef VALIDATE_ENTITY_ITEM_PARSER
{
quint64 createdFromBuffer2 = 0;
memcpy(&createdFromBuffer2, dataAt, sizeof(createdFromBuffer2));
dataAt += sizeof(createdFromBuffer2);
bytesRead += sizeof(createdFromBuffer2);
Q_ASSERT(createdFromBuffer2 == createdFromBuffer);
Q_ASSERT(parser.offset() == (unsigned int) bytesRead);
}
#endif
if (_created == UNKNOWN_CREATED_TIME) {
// we don't yet have a _created timestamp, so we accept this one
createdFromBuffer -= clockSkew;
if (createdFromBuffer > now || createdFromBuffer == UNKNOWN_CREATED_TIME) {
createdFromBuffer = now;
}
_created = createdFromBuffer;
}
_created = createdFromBuffer;
}
#ifdef WANT_DEBUG
@ -417,15 +447,21 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
#endif
quint64 lastEditedFromBuffer = 0;
quint64 lastEditedFromBufferAdjusted = 0;
// TODO: we could make this encoded as a delta from _created
// _lastEdited
memcpy(&lastEditedFromBuffer, dataAt, sizeof(lastEditedFromBuffer));
dataAt += sizeof(lastEditedFromBuffer);
bytesRead += sizeof(lastEditedFromBuffer);
lastEditedFromBufferAdjusted = lastEditedFromBuffer - clockSkew;
parser.readValue(lastEditedFromBuffer);
#ifdef VALIDATE_ENTITY_ITEM_PARSER
{
quint64 lastEditedFromBuffer2 = 0;
memcpy(&lastEditedFromBuffer2, dataAt, sizeof(lastEditedFromBuffer2));
dataAt += sizeof(lastEditedFromBuffer2);
bytesRead += sizeof(lastEditedFromBuffer2);
Q_ASSERT(lastEditedFromBuffer2 == lastEditedFromBuffer);
Q_ASSERT(parser.offset() == (unsigned int) bytesRead);
}
#endif
quint64 lastEditedFromBufferAdjusted = lastEditedFromBuffer - clockSkew;
if (lastEditedFromBufferAdjusted > now) {
lastEditedFromBufferAdjusted = now;
}
@ -487,9 +523,21 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
}
// last updated is stored as ByteCountCoded delta from lastEdited
QByteArray encodedUpdateDelta = originalDataBuffer.mid(bytesRead); // maximum possible size
ByteCountCoded<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() == (unsigned int) bytesRead);
}
#endif
if (overwriteLocalData) {
_lastUpdated = lastEditedFromBufferAdjusted + updateDelta; // don't adjust for clock skew since we already did that
#ifdef WANT_DEBUG
@ -499,17 +547,25 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
#endif
}
encodedUpdateDelta = updateDeltaCoder; // determine true length
dataAt += encodedUpdateDelta.size();
bytesRead += encodedUpdateDelta.size();
// Newer bitstreams will have a last simulated and a last updated value
quint64 lastSimulatedFromBufferAdjusted = now;
if (args.bitstreamVersion >= VERSION_ENTITIES_HAS_LAST_SIMULATED_TIME) {
// last simulated is stored as ByteCountCoded delta from lastEdited
QByteArray encodedSimulatedDelta = originalDataBuffer.mid(bytesRead); // maximum possible size
ByteCountCoded<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() == (unsigned int) bytesRead);
}
#endif
if (overwriteLocalData) {
lastSimulatedFromBufferAdjusted = lastEditedFromBufferAdjusted + simulatedDelta; // don't adjust for clock skew since we already did that
if (lastSimulatedFromBufferAdjusted > now) {
@ -521,9 +577,6 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
qCDebug(entities) << " lastSimulatedFromBufferAdjusted:" << debugTime(lastSimulatedFromBufferAdjusted, now);
#endif
}
encodedSimulatedDelta = simulatedDeltaCoder; // determine true length
dataAt += encodedSimulatedDelta.size();
bytesRead += encodedSimulatedDelta.size();
}
#ifdef WANT_DEBUG
@ -537,10 +590,26 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
// Property Flags
QByteArray encodedPropertyFlags = originalDataBuffer.mid(bytesRead); // maximum possible size
EntityPropertyFlags propertyFlags = encodedPropertyFlags;
dataAt += propertyFlags.getEncodedLength();
bytesRead += propertyFlags.getEncodedLength();
EntityPropertyFlags propertyFlags;
parser.readFlags(propertyFlags);
#ifdef VALIDATE_ENTITY_ITEM_PARSER
{
QByteArray encodedPropertyFlags = originalDataBuffer.mid(bytesRead); // maximum possible size
EntityPropertyFlags propertyFlags2 = encodedPropertyFlags;
dataAt += propertyFlags.getEncodedLength();
bytesRead += propertyFlags.getEncodedLength();
Q_ASSERT(propertyFlags2 == propertyFlags);
Q_ASSERT(parser.offset() == (unsigned int)bytesRead);
}
#endif
#ifdef VALIDATE_ENTITY_ITEM_PARSER
Q_ASSERT(parser.data() + parser.offset() == dataAt);
#else
const unsigned char* dataAt = parser.data() + parser.offset();
int bytesRead = parser.offset();
#endif
if (args.bitstreamVersion >= VERSION_ENTITIES_HAVE_SIMULATION_OWNER_AND_ACTIONS_OVER_WIRE) {
// pack SimulationOwner and terse update properties near each other
@ -549,6 +618,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
// even when we would otherwise ignore the rest of the packet.
if (propertyFlags.getHasProperty(PROP_SIMULATION_OWNER)) {
QByteArray simOwnerData;
int bytes = OctreePacketData::unpackDataFromBytes(dataAt, simOwnerData);
SimulationOwner newSimOwner;
@ -1419,20 +1489,22 @@ void EntityItem::clearSimulationOwnership() {
}
bool EntityItem::addAction(EntitySimulation* simulation, EntityActionPointer action) {
lockForWrite();
checkWaitingToRemove(simulation);
if (!checkWaitingActionData(simulation)) {
return false;
}
bool result = addActionInternal(simulation, action);
if (!result) {
removeAction(simulation, action->getID());
removeActionInternal(action->getID());
}
unlock();
return result;
}
bool EntityItem::addActionInternal(EntitySimulation* simulation, EntityActionPointer action) {
assertLocked();
assert(action);
assert(simulation);
auto actionOwnerEntity = action->getOwnerEntity().lock();
@ -1448,41 +1520,44 @@ bool EntityItem::addActionInternal(EntitySimulation* simulation, EntityActionPoi
QByteArray newDataCache = serializeActions(success);
if (success) {
_allActionsDataCache = newDataCache;
_dirtyFlags |= EntityItem::DIRTY_PHYSICS_ACTIVATION;
}
return success;
}
bool EntityItem::updateAction(EntitySimulation* simulation, const QUuid& actionID, const QVariantMap& arguments) {
lockForWrite();
checkWaitingToRemove(simulation);
if (!checkWaitingActionData(simulation)) {
return false;
}
if (!_objectActions.contains(actionID)) {
unlock();
return false;
}
EntityActionPointer action = _objectActions[actionID];
bool success = action->updateArguments(arguments);
bool success = action->updateArguments(arguments);
if (success) {
_allActionsDataCache = serializeActions(success);
_dirtyFlags |= EntityItem::DIRTY_PHYSICS_ACTIVATION;
} else {
qDebug() << "EntityItem::updateAction failed";
}
unlock();
return success;
}
bool EntityItem::removeAction(EntitySimulation* simulation, const QUuid& actionID) {
lockForWrite();
checkWaitingToRemove(simulation);
if (!checkWaitingActionData(simulation)) {
return false;;
}
return removeActionInternal(actionID);
bool success = removeActionInternal(actionID);
unlock();
return success;
}
bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulation* simulation) {
assertWriteLocked();
if (_objectActions.contains(actionID)) {
if (!simulation) {
EntityTree* entityTree = _element ? _element->getTree() : nullptr;
@ -1499,13 +1574,14 @@ bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulation* s
bool success = true;
_allActionsDataCache = serializeActions(success);
_dirtyFlags |= EntityItem::DIRTY_PHYSICS_ACTIVATION;
return success;
}
return false;
}
bool EntityItem::clearActions(EntitySimulation* simulation) {
_waitingActionData.clear();
lockForWrite();
QHash<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),
@ -201,7 +204,6 @@ void GLBackend::do_drawInstanced(Batch& batch, uint32 paramOffset) {
GLenum mode = _primitiveToGLmode[primitiveType];
uint32 numVertices = batch._params[paramOffset + 2]._uint;
uint32 startVertex = batch._params[paramOffset + 1]._uint;
uint32 startInstance = batch._params[paramOffset + 0]._uint;
glDrawArraysInstancedARB(mode, startVertex, numVertices, numInstances);
(void) CHECK_GL_ERROR();
@ -233,17 +235,34 @@ void GLBackend::do_clearFramebuffer(Batch& batch, uint32 paramOffset) {
glmask |= GL_DEPTH_BUFFER_BIT;
}
std::vector<GLenum> drawBuffers;
if (masks & Framebuffer::BUFFER_COLORS) {
glClearColor(color.x, color.y, color.z, color.w);
glmask |= GL_COLOR_BUFFER_BIT;
for (unsigned int i = 0; i < Framebuffer::MAX_NUM_RENDER_BUFFERS; i++) {
if (masks & (1 << i)) {
drawBuffers.push_back(GL_COLOR_ATTACHMENT0 + i);
}
}
if (!drawBuffers.empty()) {
glDrawBuffers(drawBuffers.size(), drawBuffers.data());
glClearColor(color.x, color.y, color.z, color.w);
glmask |= GL_COLOR_BUFFER_BIT;
}
}
glClear(glmask);
// Restore the color draw buffers only if a frmaebuffer is bound
if (_output._framebuffer && !drawBuffers.empty()) {
auto glFramebuffer = syncGPUObject(*_output._framebuffer);
if (glFramebuffer) {
glDrawBuffers(glFramebuffer->_colorBuffers.size(), glFramebuffer->_colorBuffers.data());
}
}
(void) CHECK_GL_ERROR();
}
// TODO: As long as we have gl calls explicitely issued from interface
// code, we need to be able to record and batch these calls. THe long
// term strategy is to get rid of any GL calls in favor of the HIFI GPU API
@ -598,10 +617,11 @@ void GLBackend::do_glUniform4fv(Batch& batch, uint32 paramOffset) {
return;
}
updatePipeline();
glUniform4fv(
batch._params[paramOffset + 2]._int,
batch._params[paramOffset + 1]._uint,
(const GLfloat*)batch.editData(batch._params[paramOffset + 0]._uint));
GLint location = batch._params[paramOffset + 2]._int;
GLsizei count = batch._params[paramOffset + 1]._uint;
const GLfloat* value = (const GLfloat*)batch.editData(batch._params[paramOffset + 0]._uint);
glUniform4fv(location, count, value);
(void) CHECK_GL_ERROR();
}

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;
}
}
@ -589,7 +589,7 @@ void GLBackend::do_setStateAntialiasedLineEnable(bool enable) {
void GLBackend::do_setStateDepthBias(Vec2 bias) {
if ( (bias.x != _pipeline._stateCache.depthBias) || (bias.y != _pipeline._stateCache.depthBiasSlopeScale)) {
if ((bias.x != 0.f) || (bias.y != 0.f)) {
if ((bias.x != 0.0f) || (bias.y != 0.0f)) {
glEnable(GL_POLYGON_OFFSET_FILL);
glEnable(GL_POLYGON_OFFSET_LINE);
glEnable(GL_POLYGON_OFFSET_POINT);

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

@ -9,10 +9,17 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <NumericalConstants.h>
#include "NodeList.h"
#include "ReceivedPacketProcessor.h"
#include "SharedUtil.h"
ReceivedPacketProcessor::ReceivedPacketProcessor() {
_lastWindowAt = usecTimestampNow();
}
void ReceivedPacketProcessor::terminating() {
_hasPackets.wakeAll();
}
@ -25,6 +32,7 @@ void ReceivedPacketProcessor::queueReceivedPacket(const SharedNodePointer& sendi
lock();
_packets.push_back(networkPacket);
_nodePacketCounts[sendingNode->getUUID()]++;
_lastWindowIncomingPackets++;
unlock();
// Make sure to wake our actual processing thread because we now have packets for it to process.
@ -32,6 +40,24 @@ void ReceivedPacketProcessor::queueReceivedPacket(const SharedNodePointer& sendi
}
bool ReceivedPacketProcessor::process() {
quint64 now = usecTimestampNow();
quint64 sinceLastWindow = now - _lastWindowAt;
if (sinceLastWindow > USECS_PER_SECOND) {
lock();
float secondsSinceLastWindow = sinceLastWindow / USECS_PER_SECOND;
float incomingPacketsPerSecondInWindow = (float)_lastWindowIncomingPackets / secondsSinceLastWindow;
_incomingPPS.updateAverage(incomingPacketsPerSecondInWindow);
float processedPacketsPerSecondInWindow = (float)_lastWindowIncomingPackets / secondsSinceLastWindow;
_processedPPS.updateAverage(processedPacketsPerSecondInWindow);
_lastWindowAt = now;
_lastWindowIncomingPackets = 0;
_lastWindowProcessedPackets = 0;
unlock();
}
if (_packets.size() == 0) {
_waitingOnPacketsMutex.lock();
@ -51,6 +77,7 @@ bool ReceivedPacketProcessor::process() {
foreach(auto& packet, currentPackets) {
processPacket(packet.getNode(), packet.getByteArray());
_lastWindowProcessedPackets++;
midProcess();
}

View file

@ -21,7 +21,7 @@
class ReceivedPacketProcessor : public GenericThread {
Q_OBJECT
public:
ReceivedPacketProcessor() { }
ReceivedPacketProcessor();
/// Add packet from network receive thread to the processing queue.
void queueReceivedPacket(const SharedNodePointer& sendingNode, const QByteArray& packet);
@ -47,6 +47,9 @@ public:
/// How many received packets waiting are to be processed
int packetsToProcessCount() const { return _packets.size(); }
float getIncomingPPS() const { return _incomingPPS.getAverage(); }
float getProcessedPPS() const { return _processedPPS.getAverage(); }
virtual void terminating();
public slots:
@ -80,6 +83,12 @@ protected:
QWaitCondition _hasPackets;
QMutex _waitingOnPacketsMutex;
quint64 _lastWindowAt = 0;
int _lastWindowIncomingPackets = 0;
int _lastWindowProcessedPackets = 0;
SimpleMovingAverage _incomingPPS;
SimpleMovingAverage _processedPPS;
};
#endif // hifi_ReceivedPacketProcessor_h

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;

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