3
0
Fork 0
mirror of https://github.com/lubosz/overte.git synced 2025-04-27 12:15:25 +02:00

Merge branch 'controllers' of https://github.com/highfidelity/hifi into controllers

This commit is contained in:
samcake 2015-10-16 18:07:25 -07:00
commit 941f95c7d4
102 changed files with 4836 additions and 2982 deletions
assignment-client/src
domain-server/resources
examples
interface
libraries
tests/controllers

View file

@ -24,15 +24,24 @@ AssignmentAction::~AssignmentAction() {
}
void AssignmentAction::removeFromSimulation(EntitySimulation* simulation) const {
simulation->removeAction(_id);
withReadLock([&]{
simulation->removeAction(_id);
simulation->applyActionChanges();
});
}
QByteArray AssignmentAction::serialize() const {
return _data;
QByteArray result;
withReadLock([&]{
result = _data;
});
return result;
}
void AssignmentAction::deserialize(QByteArray serializedArguments) {
_data = serializedArguments;
withWriteLock([&]{
_data = serializedArguments;
});
}
bool AssignmentAction::updateArguments(QVariantMap arguments) {

View file

@ -19,7 +19,7 @@
#include "EntityActionInterface.h"
class AssignmentAction : public EntityActionInterface {
class AssignmentAction : public EntityActionInterface, public ReadWriteLockable {
public:
AssignmentAction(EntityActionType type, const QUuid& id, EntityItemPointer ownerEntity);
virtual ~AssignmentAction();

View file

@ -147,9 +147,13 @@ bool EntityServer::readAdditionalConfiguration(const QJsonObject& settingsSectio
readOptionBool(QString("wantEditLogging"), settingsSectionObject, wantEditLogging);
qDebug("wantEditLogging=%s", debug::valueOf(wantEditLogging));
bool wantTerseEditLogging = false;
readOptionBool(QString("wantTerseEditLogging"), settingsSectionObject, wantTerseEditLogging);
qDebug("wantTerseEditLogging=%s", debug::valueOf(wantTerseEditLogging));
EntityTreePointer tree = std::static_pointer_cast<EntityTree>(_tree);
tree->setWantEditLogging(wantEditLogging);
tree->setWantTerseEditLogging(wantTerseEditLogging);
return true;
}

View file

@ -625,6 +625,8 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
statsString += QString().sprintf("<b>%s Edit Statistics... <a href='/resetStats'>[RESET]</a></b>\r\n",
getMyServerName());
quint64 currentPacketsInQueue = _octreeInboundPacketProcessor->packetsToProcessCount();
float incomingPPS = _octreeInboundPacketProcessor->getIncomingPPS();
float processedPPS = _octreeInboundPacketProcessor->getProcessedPPS();
quint64 averageTransitTimePerPacket = _octreeInboundPacketProcessor->getAverageTransitTimePerPacket();
quint64 averageProcessTimePerPacket = _octreeInboundPacketProcessor->getAverageProcessTimePerPacket();
quint64 averageLockWaitTimePerPacket = _octreeInboundPacketProcessor->getAverageLockWaitTimePerPacket();
@ -639,11 +641,16 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
quint64 averageCreateTime = _tree->getAverageCreateTime();
quint64 averageLoggingTime = _tree->getAverageLoggingTime();
int FLOAT_PRECISION = 3;
float averageElementsPerPacket = totalPacketsProcessed == 0 ? 0 : (float)totalElementsProcessed / totalPacketsProcessed;
statsString += QString(" Current Inbound Packets Queue: %1 packets\r\n")
statsString += QString(" Current Inbound Packets Queue: %1 packets \r\n")
.arg(locale.toString((uint)currentPacketsInQueue).rightJustified(COLUMN_WIDTH, ' '));
statsString += QString(" Packets Queue Network IN: %1 PPS \r\n")
.arg(locale.toString(incomingPPS, 'f', FLOAT_PRECISION).rightJustified(COLUMN_WIDTH, ' '));
statsString += QString(" Packets Queue Processing OUT: %1 PPS \r\n")
.arg(locale.toString(processedPPS, 'f', FLOAT_PRECISION).rightJustified(COLUMN_WIDTH, ' '));
statsString += QString(" Total Inbound Packets: %1 packets\r\n")
.arg(locale.toString((uint)totalPacketsProcessed).rightJustified(COLUMN_WIDTH, ' '));
@ -692,6 +699,16 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
totalElementsProcessed = senderStats.getTotalElementsProcessed();
totalPacketsProcessed = senderStats.getTotalPacketsProcessed();
auto received = senderStats._incomingEditSequenceNumberStats.getReceived();
auto expected = senderStats._incomingEditSequenceNumberStats.getExpectedReceived();
auto unreasonable = senderStats._incomingEditSequenceNumberStats.getUnreasonable();
auto outOfOrder = senderStats._incomingEditSequenceNumberStats.getOutOfOrder();
auto early = senderStats._incomingEditSequenceNumberStats.getEarly();
auto late = senderStats._incomingEditSequenceNumberStats.getLate();
auto lost = senderStats._incomingEditSequenceNumberStats.getLost();
auto recovered = senderStats._incomingEditSequenceNumberStats.getRecovered();
averageElementsPerPacket = totalPacketsProcessed == 0 ? 0 : (float)totalElementsProcessed / totalPacketsProcessed;
statsString += QString(" Total Inbound Packets: %1 packets\r\n")
@ -702,7 +719,7 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
(double)averageElementsPerPacket);
statsString += QString(" Average Transit Time/Packet: %1 usecs\r\n")
.arg(locale.toString((uint)averageTransitTimePerPacket).rightJustified(COLUMN_WIDTH, ' '));
statsString += QString(" Average Process Time/Packet: %1 usecs\r\n")
statsString += QString(" Average Process Time/Packet: %1 usecs\r\n")
.arg(locale.toString((uint)averageProcessTimePerPacket).rightJustified(COLUMN_WIDTH, ' '));
statsString += QString(" Average Wait Lock Time/Packet: %1 usecs\r\n")
.arg(locale.toString((uint)averageLockWaitTimePerPacket).rightJustified(COLUMN_WIDTH, ' '));
@ -711,6 +728,24 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
statsString += QString(" Average Wait Lock Time/Element: %1 usecs\r\n")
.arg(locale.toString((uint)averageLockWaitTimePerElement).rightJustified(COLUMN_WIDTH, ' '));
statsString += QString("\r\n Inbound Edit Packets --------------------------------\r\n");
statsString += QString(" Received: %1\r\n")
.arg(locale.toString(received).rightJustified(COLUMN_WIDTH, ' '));
statsString += QString(" Expected: %1\r\n")
.arg(locale.toString(expected).rightJustified(COLUMN_WIDTH, ' '));
statsString += QString(" Unreasonable: %1\r\n")
.arg(locale.toString(unreasonable).rightJustified(COLUMN_WIDTH, ' '));
statsString += QString(" Out of Order: %1\r\n")
.arg(locale.toString(outOfOrder).rightJustified(COLUMN_WIDTH, ' '));
statsString += QString(" Early: %1\r\n")
.arg(locale.toString(early).rightJustified(COLUMN_WIDTH, ' '));
statsString += QString(" Late: %1\r\n")
.arg(locale.toString(late).rightJustified(COLUMN_WIDTH, ' '));
statsString += QString(" Lost: %1\r\n")
.arg(locale.toString(lost).rightJustified(COLUMN_WIDTH, ' '));
statsString += QString(" Recovered: %1\r\n")
.arg(locale.toString(recovered).rightJustified(COLUMN_WIDTH, ' '));
}
statsString += "\r\n\r\n";

View file

@ -484,6 +484,14 @@
"default": false,
"advanced": true
},
{
"name": "wantTerseEditLogging",
"type": "checkbox",
"label": "Edit Logging (Terse)",
"help": "Logging of all edits to entities",
"default": false,
"advanced": true
},
{
"name": "verboseDebug",
"type": "checkbox",

View file

@ -66,7 +66,8 @@ var MSEC_PER_SEC = 1000.0;
// these control how long an abandoned pointer line will hang around
var startTime = Date.now();
var LIFETIME = 10;
var ACTION_LIFETIME = 10; // seconds
var ACTION_LIFETIME = 15; // seconds
var ACTION_LIFETIME_REFRESH = 5;
var PICKS_PER_SECOND_PER_HAND = 5;
var MSECS_PER_SEC = 1000.0;
@ -339,11 +340,12 @@ function MyController(hand, triggerAction) {
var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(this.palm));
var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, ["position", "rotation",
"gravity", "ignoreForCollisions"]);
var now = Date.now();
// add the action and initialize some variables
this.currentObjectPosition = grabbedProperties.position;
this.currentObjectRotation = grabbedProperties.rotation;
this.currentObjectTime = Date.now();
this.currentObjectTime = now;
this.handPreviousPosition = handControllerPosition;
this.handPreviousRotation = handRotation;
@ -359,6 +361,7 @@ function MyController(hand, triggerAction) {
if (this.actionID === NULL_ACTION_ID) {
this.actionID = null;
}
this.actionTimeout = now + (ACTION_LIFETIME * MSEC_PER_SEC);
if (this.actionID !== null) {
this.setState(STATE_CONTINUE_DISTANCE_HOLDING);
@ -441,7 +444,9 @@ function MyController(hand, triggerAction) {
this.currentObjectTime = now;
// this doubles hand rotation
var handChange = Quat.multiply(Quat.slerp(this.handPreviousRotation, handRotation, DISTANCE_HOLDING_ROTATION_EXAGGERATION_FACTOR), Quat.inverse(this.handPreviousRotation));
var handChange = Quat.multiply(Quat.slerp(this.handPreviousRotation, handRotation,
DISTANCE_HOLDING_ROTATION_EXAGGERATION_FACTOR),
Quat.inverse(this.handPreviousRotation));
this.handPreviousRotation = handRotation;
this.currentObjectRotation = Quat.multiply(handChange, this.currentObjectRotation);
@ -454,9 +459,11 @@ function MyController(hand, triggerAction) {
angularTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME,
lifetime: ACTION_LIFETIME
});
this.actionTimeout = now + (ACTION_LIFETIME * MSEC_PER_SEC);
};
this.nearGrabbing = function() {
var now = Date.now();
if (this.triggerSmoothedReleased()) {
this.setState(STATE_RELEASE);
@ -465,7 +472,7 @@ function MyController(hand, triggerAction) {
this.lineOff();
var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity,
var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity,
["position", "rotation", "gravity", "ignoreForCollisions"]);
this.activateEntity(this.grabbedEntity, grabbedProperties);
@ -473,23 +480,24 @@ function MyController(hand, triggerAction) {
var handPosition = this.getHandPosition();
var objectRotation = grabbedProperties.rotation;
var offsetRotation = Quat.multiply(Quat.inverse(handRotation), objectRotation);
this.offsetRotation = Quat.multiply(Quat.inverse(handRotation), objectRotation);
var currentObjectPosition = grabbedProperties.position;
var offset = Vec3.subtract(currentObjectPosition, handPosition);
var offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, offsetRotation)), offset);
this.offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, this.offsetRotation)), offset);
this.actionID = NULL_ACTION_ID;
this.actionID = Entities.addAction("hold", this.grabbedEntity, {
hand: this.hand === RIGHT_HAND ? "right" : "left",
timeScale: NEAR_GRABBING_ACTION_TIMEFRAME,
relativePosition: offsetPosition,
relativeRotation: offsetRotation,
relativePosition: this.offsetPosition,
relativeRotation: this.offsetRotation,
lifetime: ACTION_LIFETIME
});
if (this.actionID === NULL_ACTION_ID) {
this.actionID = null;
} else {
this.actionTimeout = now + (ACTION_LIFETIME * MSEC_PER_SEC);
this.setState(STATE_CONTINUE_NEAR_GRABBING);
if (this.hand === RIGHT_HAND) {
Entities.callEntityMethod(this.grabbedEntity, "setRightHand");
@ -511,10 +519,10 @@ function MyController(hand, triggerAction) {
return;
}
// Keep track of the fingertip velocity to impart when we release the object
// Note that the idea of using a constant 'tip' velocity regardless of the
// Keep track of the fingertip velocity to impart when we release the object.
// Note that the idea of using a constant 'tip' velocity regardless of the
// object's actual held offset is an idea intended to make it easier to throw things:
// Because we might catch something or transfer it between hands without a good idea
// Because we might catch something or transfer it between hands without a good idea
// of it's actual offset, let's try imparting a velocity which is at a fixed radius
// from the palm.
@ -529,9 +537,17 @@ function MyController(hand, triggerAction) {
this.currentObjectTime = now;
Entities.callEntityMethod(this.grabbedEntity, "continueNearGrab");
Entities.updateAction(this.grabbedEntity, this.actionID, {
lifetime: ACTION_LIFETIME
});
if (this.actionTimeout - now < ACTION_LIFETIME_REFRESH * MSEC_PER_SEC) {
// if less than a 5 seconds left, refresh the actions lifetime
Entities.updateAction(this.grabbedEntity, this.actionID, {
hand: this.hand === RIGHT_HAND ? "right" : "left",
timeScale: NEAR_GRABBING_ACTION_TIMEFRAME,
relativePosition: this.offsetPosition,
relativeRotation: this.offsetRotation,
lifetime: ACTION_LIFETIME
});
this.actionTimeout = now + (ACTION_LIFETIME * MSEC_PER_SEC);
}
};
this.nearGrabbingNonColliding = function() {
@ -568,7 +584,7 @@ function MyController(hand, triggerAction) {
this.setState(STATE_RELEASE);
return;
}
Entities.callEntityMethod(this.grabbedEntity, "continueNearGrabbingNonColliding");
};

View file

@ -253,3 +253,21 @@ orientationOf = function(vector) {
return Quat.multiply(yaw, pitch);
}
randFloat = function(low, high) {
return low + Math.random() * (high - low);
}
randInt = function(low, high) {
return Math.floor(randFloat(low, high));
}
hexToRgb = function(hex) {
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result ? {
red: parseInt(result[1], 16),
green: parseInt(result[2], 16),
blue: parseInt(result[3], 16)
} : null;
}

View file

@ -0,0 +1,23 @@
vec2 iResolution = iWorldScale.xy;
vec2 iMouse = vec2(0);
const float PI = 3.14159265;
float time = iGlobalTime;
vec2 center = vec2(0.5, 0.5);
void mainImage( out vec4 fragColor, in vec2 fragCoord ) {
vec2 position = (fragCoord.xy/iResolution.xy) + 0.5;
float dist = pow(distance(position.xy, center), 3.);
dist = dist / 1.0 + sin(time * 10)/100.0;
vec3 color = vec3(dist, 0.0, dist);
fragColor = vec4(color, 1.0);
}
vec4 getProceduralColor() {
vec4 result;
vec2 position = _position.xy;
mainImage(result, position * iWorldScale.xy);
return result;
}

View file

@ -0,0 +1,42 @@
//
// colorIndicatorEntityScript.js
// examples/painting/whiteboard
//
// Created by Eric Levin on 9/21/15.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
/*global print, MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, Audio, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt */
/*global ColorIndicator */
(function() {
var _this;
ColorIndicator = function() {
_this = this;
};
ColorIndicator.prototype = {
changeColor: function() {
var userData = JSON.parse(Entities.getEntityProperties(this.whiteboard, "userData").userData);
var newColor = userData.color.currentColor;
Entities.editEntity(this.entityID, {
color: newColor
});
},
preload: function(entityID) {
this.entityID = entityID;
var props = Entities.getEntityProperties(this.entityID, ["position", "userData"]);
this.position = props.position;
this.whiteboard = JSON.parse(props.userData).whiteboard;
},
};
// entity scripts always need to return a newly constructed object of our type
return new ColorIndicator();
});

View file

@ -0,0 +1,49 @@
//
// colorSelectorEntityScript.js
// examples/painting/whiteboard
//
// Created by Eric Levin on 9/21/15.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
/*global print, MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, Audio, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt */
/*global ColorSelector */
(function() {
Script.include("../../libraries/utils.js");
var _this;
ColorSelector = function() {
_this = this;
};
ColorSelector.prototype = {
startFarGrabNonColliding: function() {
this.selectColor();
},
clickReleaseOnEntity: function() {
this.selectColor();
},
selectColor: function() {
setEntityCustomData(this.colorKey, this.whiteboard, {currentColor: this.color});
Entities.callEntityMethod(this.whiteboard, "changeColor");
},
preload: function(entityID) {
this.entityID = entityID;
var props = Entities.getEntityProperties(this.entityID, ["position, color, userData"]);
this.position = props.position;
this.color = props.color;
this.colorKey = "color";
this.whiteboard = JSON.parse(props.userData).whiteboard;
},
};
// entity scripts always need to return a newly constructed object of our type
return new ColorSelector();
});

View file

@ -0,0 +1,45 @@
//
// eraseBoardEntityScript.js
// examples/painting/whiteboard
//
// Created by Eric Levin on 9/21/15.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
/*global print, MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, Audio, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt */
/*global BoardEraser */
(function() {
var _this;
BoardEraser = function() {
_this = this;
};
BoardEraser.prototype = {
startFarGrabNonColliding: function() {
this.eraseBoard();
},
clickReleaseOnEntity: function() {
this.eraseBoard();
},
eraseBoard: function() {
Entities.callEntityMethod(this.whiteboard, "eraseBoard");
},
preload: function(entityID) {
this.entityID = entityID;
var props = Entities.getEntityProperties(this.entityID, ["userData"]);
this.whiteboard = JSON.parse(props.userData).whiteboard;
},
};
// entity scripts always need to return a newly constructed object of our type
return new BoardEraser();
});

View file

@ -0,0 +1,250 @@
//
// whiteBoardEntityScript.js
// examples/painting/whiteboard
//
// Created by Eric Levin on 10/12/15.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
/*global print, MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, Audio, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, pointInExtents, vec3equal, setEntityCustomData, getEntityCustomData */
/*global Whiteboard */
(function() {
Script.include("../../libraries/utils.js");
var _this;
var RIGHT_HAND = 1;
var LEFT_HAND = 0;
var MIN_POINT_DISTANCE = 0.01 ;
var MAX_POINT_DISTANCE = 0.5;
var MAX_POINTS_PER_LINE = 40;
var MAX_DISTANCE = 5;
var PAINT_TRIGGER_THRESHOLD = 0.6;
var MIN_STROKE_WIDTH = 0.0005;
var MAX_STROKE_WIDTH = 0.03;
Whiteboard = function() {
_this = this;
};
Whiteboard.prototype = {
setRightHand: function() {
this.hand = RIGHT_HAND;
},
setLeftHand: function() {
this.hand = LEFT_HAND;
},
startFarGrabNonColliding: function() {
if (this.painting) {
return;
}
if (this.hand === RIGHT_HAND) {
this.getHandPosition = MyAvatar.getRightPalmPosition;
this.getHandRotation = MyAvatar.getRightPalmRotation;
this.triggerAction = Controller.findAction("RIGHT_HAND_CLICK");
} else if (this.hand === LEFT_HAND) {
this.getHandPosition = MyAvatar.getLeftPalmPosition;
this.getHandRotation = MyAvatar.getLeftPalmRotation;
this.triggerAction = Controller.findAction("LEFT_HAND_CLICK");
}
Overlays.editOverlay(this.laserPointer, {
visible: true
});
},
continueFarGrabbingNonColliding: function() {
var handPosition = this.getHandPosition();
var pickRay = {
origin: handPosition,
direction: Quat.getUp(this.getHandRotation())
};
this.intersection = Entities.findRayIntersection(pickRay, true, this.whitelist);
//Comment out above line and uncomment below line to see difference in performance between using a whitelist, and not using one
// this.intersection = Entities.findRayIntersection(pickRay, true);
if (this.intersection.intersects) {
var distance = Vec3.distance(handPosition, this.intersection.intersection);
if (distance < MAX_DISTANCE) {
this.triggerValue = Controller.getActionValue(this.triggerAction);
this.currentStrokeWidth = map(this.triggerValue, 0, 1, MIN_STROKE_WIDTH, MAX_STROKE_WIDTH);
var displayPoint = this.intersection.intersection;
displayPoint = Vec3.sum(displayPoint, Vec3.multiply(this.normal, 0.01));
Overlays.editOverlay(this.laserPointer, {
position: displayPoint,
size: {
x: this.currentStrokeWidth,
y: this.currentStrokeWidth
}
});
if (this.triggerValue > PAINT_TRIGGER_THRESHOLD) {
this.paint(this.intersection.intersection, this.intersection.surfaceNormal);
} else {
this.painting = false;
this.oldPosition = null;
}
}
} else if (this.intersection.properties.type !== "Unknown") {
//Sometimes ray will pick against an invisible object with type unkown... so if type is unknown, ignore
this.stopPainting();
}
},
stopPainting: function() {
this.painting = false;
Overlays.editOverlay(this.laserPointer, {
visible: false
});
this.oldPosition = null;
},
paint: function(position, normal) {
if (this.painting === false) {
if (this.oldPosition) {
this.newStroke(this.oldPosition);
} else {
this.newStroke(position);
}
this.painting = true;
}
var localPoint = Vec3.subtract(position, this.strokeBasePosition);
//Move stroke a bit forward along normal each point so it doesnt zfight with mesh its drawing on, or previous part of stroke(s)
localPoint = Vec3.sum(localPoint, Vec3.multiply(this.normal, this.forwardOffset));
this.forwardOffset += 0.00001;
var distance = Vec3.distance(localPoint, this.strokePoints[this.strokePoints.length - 1]);
if (this.strokePoints.length > 0 && distance < MIN_POINT_DISTANCE) {
//need a minimum distance to avoid binormal NANs
return;
}
if (this.strokePoints.length > 0 && distance > MAX_POINT_DISTANCE) {
//Prevents drawing lines accross models
this.painting = false;
return;
}
if (this.strokePoints.length === 0) {
localPoint = {
x: 0,
y: 0,
z: 0
};
}
this.strokePoints.push(localPoint);
this.strokeNormals.push(this.normal);
this.strokeWidths.push(this.currentStrokeWidth);
Entities.editEntity(this.currentStroke, {
linePoints: this.strokePoints,
normals: this.strokeNormals,
strokeWidths: this.strokeWidths
});
if (this.strokePoints.length === MAX_POINTS_PER_LINE) {
this.painting = false;
return;
}
this.oldPosition = position;
},
newStroke: function(position) {
this.strokeBasePosition = position;
this.currentStroke = Entities.addEntity({
position: position,
type: "PolyLine",
name: "paintStroke",
color: this.strokeColor,
dimensions: {
x: 50,
y: 50,
z: 50
},
lifetime: 200,
userData: JSON.stringify({
whiteboard: this.entityID
})
});
this.strokePoints = [];
this.strokeNormals = [];
this.strokeWidths = [];
this.strokes.push(this.currentStroke);
},
releaseGrab: function() {
this.stopPainting();
},
changeColor: function() {
var userData = JSON.parse(Entities.getEntityProperties(this.entityID, ["userData"]).userData);
this.strokeColor = userData.color.currentColor;
this.colorIndicator = userData.colorIndicator;
Overlays.editOverlay(this.laserPointer, {
color: this.strokeColor
});
Entities.callEntityMethod(this.colorIndicator, "changeColor");
},
eraseBoard: function() {
var distance = Math.max(this.dimensions.x, this.dimensions.y);
var entities = Entities.findEntities(this.position, distance);
entities.forEach(function(entity) {
var props = Entities.getEntityProperties(entity, ["name, userData"]);
var name = props.name;
if(!props.userData) {
return;
}
var whiteboardID = JSON.parse(props.userData).whiteboard;
if (name === "paintStroke" && JSON.stringify(whiteboardID) === JSON.stringify(_this.entityID)) {
// This entity is a paintstroke and part of this whiteboard so delete it
Entities.deleteEntity(entity);
}
});
},
preload: function(entityID) {
this.entityID = entityID;
var props = Entities.getEntityProperties(this.entityID, ["position", "rotation", "userData", "dimensions"]);
this.position = props.position;
this.rotation = props.rotation;
this.dimensions = props.dimensions;
this.normal = Vec3.multiply(Quat.getFront(this.rotation), -1);
this.painting = false;
this.strokes = [];
this.whitelist = [this.entityID];
this.laserPointer = Overlays.addOverlay("circle3d", {
color: this.strokeColor,
solid: true,
rotation: this.rotation
});
this.forwardOffset = 0.0005;
this.changeColor();
},
unload: function() {
Overlays.deleteOverlay(this.laserPointer);
// this.eraseBoard();
}
};
// entity scripts always need to return a newly constructed object of our type
return new Whiteboard();
});

View file

@ -0,0 +1,201 @@
//
// whiteBoardSpawner.js
// examples/painting/whiteboard
//
// Created by Eric Levina on 10/12/15.
// Copyright 2015 High Fidelity, Inc.
//
// Run this script to spawn a whiteboard that one can paint on
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
/*global print, MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, Audio, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, pointInExtents, vec3equal, setEntityCustomData, getEntityCustomData */
// Script specific
/*global hexToRgb */
Script.include("../../libraries/utils.js");
var scriptURL = Script.resolvePath("whiteboardEntityScript.js");
var rotation = Quat.safeEulerAngles(Camera.getOrientation());
rotation = Quat.fromPitchYawRollDegrees(0, rotation.y, 0);
var center = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(rotation)));
center.y += 0.4;
var colors = [
hexToRgb("#2F8E84"),
hexToRgb("#66CCB3"),
hexToRgb("#A43C37"),
hexToRgb("#491849"),
hexToRgb("#6AB03B"),
hexToRgb("#993369"),
hexToRgb("#9B47C2")
];
//WHITEBOARD
var whiteboardDimensions = {
x: 2,
y: 1.5,
z: 0.08
};
var whiteboard = Entities.addEntity({
type: "Model",
modelURL: "https://hifi-public.s3.amazonaws.com/ozan/support/for_eric/whiteboard/whiteboard.fbx",
name: "whiteboard",
position: center,
rotation: rotation,
script: scriptURL,
dimensions: whiteboardDimensions,
color: {
red: 255,
green: 255,
blue: 255
}
});
// COLOR INDICATOR BOX
var colorIndicatorDimensions = {
x: whiteboardDimensions.x,
y: 0.05,
z: 0.02
};
scriptURL = Script.resolvePath("colorIndicatorEntityScript.js");
var colorIndicatorPosition = Vec3.sum(center, {
x: 0,
y: whiteboardDimensions.y / 2 + colorIndicatorDimensions.y / 2,
z: 0
});
var colorIndicatorBox = Entities.addEntity({
type: "Box",
name: "Color Indicator",
color: colors[0],
rotation: rotation,
position: colorIndicatorPosition,
dimensions: colorIndicatorDimensions,
script: scriptURL,
userData: JSON.stringify({
whiteboard: whiteboard
})
});
Entities.editEntity(whiteboard, {
userData: JSON.stringify({
color: {
currentColor: colors[0]
},
colorIndicator: colorIndicatorBox
})
});
//COLOR BOXES
var direction = Quat.getRight(rotation);
var colorBoxPosition = Vec3.subtract(center, Vec3.multiply(direction, whiteboardDimensions.x / 2));
var colorBoxes = [];
var colorSquareDimensions = {
x: (whiteboardDimensions.x / 2) / (colors.length - 1),
y: 0.1,
z: 0.05
};
colorBoxPosition.y += whiteboardDimensions.y / 2 + colorIndicatorDimensions.y + colorSquareDimensions.y / 2;
var spaceBetweenColorBoxes = Vec3.multiply(direction, colorSquareDimensions.x * 2);
var scriptURL = Script.resolvePath("colorSelectorEntityScript.js");
for (var i = 0; i < colors.length; i++) {
var colorBox = Entities.addEntity({
type: "Box",
name: "Color Selector",
position: colorBoxPosition,
dimensions: colorSquareDimensions,
rotation: rotation,
color: colors[i],
script: scriptURL,
userData: JSON.stringify({
whiteboard: whiteboard,
colorIndicator: colorIndicatorBox
})
});
colorBoxes.push(colorBox);
colorBoxPosition = Vec3.sum(colorBoxPosition, spaceBetweenColorBoxes);
}
// BLACK BOX
var blackBoxDimensions = {
x: 0.3,
y: 0.3,
z: 0.01
};
colorBoxPosition = Vec3.subtract(center, Vec3.multiply(direction, whiteboardDimensions.x / 2 + blackBoxDimensions.x / 2 - 0.01));
colorBoxPosition.y += 0.3;
var fragShaderURL = Script.resolvePath('blackInk.fs?v1' + Math.random());
var blackBox = Entities.addEntity({
type: 'Box',
name: "Black Color",
position: colorBoxPosition,
dimensions: blackBoxDimensions,
rotation: rotation,
color: {
red: 0,
green: 0,
blue: 0
},
script: scriptURL,
userData: JSON.stringify({
whiteboard: whiteboard,
version: 2,
ProceduralEntity: {
shaderUrl: fragShaderURL
}
})
});
var eraseBoxDimensions = {
x: 0.5,
y: 0.1,
z: 0.01
};
var eraseBoxPosition = Vec3.sum(center, Vec3.multiply(direction, whiteboardDimensions.x / 2 + eraseBoxDimensions.x / 2 + 0.01));
eraseBoxPosition.y += 0.3;
scriptURL = Script.resolvePath("eraseBoardEntityScript.js");
var eraseAllText = Entities.addEntity({
type: "Text",
position: eraseBoxPosition,
name: "Eraser",
script: scriptURL,
rotation: rotation,
dimensions: eraseBoxDimensions,
backgroundColor: {
red: 0,
green: 60,
blue: 0
},
textColor: {
red: 255,
green: 10,
blue: 10
},
text: "ERASE BOARD",
lineHeight: 0.07,
userData: JSON.stringify({
whiteboard: whiteboard
})
});
function cleanup() {
Entities.deleteEntity(whiteboard);
Entities.deleteEntity(eraseAllText);
Entities.deleteEntity(blackBox);
Entities.deleteEntity(colorIndicatorBox);
colorBoxes.forEach(function(colorBox) {
Entities.deleteEntity(colorBox);
});
}
// Uncomment this line to delete whiteboard and all associated entity on script close
Script.scriptEnding.connect(cleanup);

View file

@ -0,0 +1,30 @@
ControllerTest = function() {
print("Actions");
for (var prop in Controller.Actions) {
print("\t" + prop);
}
print("Standard");
for (var prop in Controller.Standard) {
print("\t" + prop);
}
print("Hardware");
for (var prop in Controller.Hardware) {
print("\t" + prop);
for (var prop2 in Controller.Hardware[prop]) {
print("\t\t" + prop2);
}
}
print("Done");
var that = this;
Script.scriptEnding.connect(function() {
that.onCleanup();
});
}
ControllerTest.prototype.onCleanup = function() {
}
new ControllerTest();

View file

@ -0,0 +1,217 @@
//
// originalPositionResetter.js
// toybox
//
// Created by James B. Pollack @imgntn 10/16/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
//
var HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
var TARGET_MODEL_URL = HIFI_PUBLIC_BUCKET + "models/ping_pong_gun/target.fbx";
var TARGET_COLLISION_HULL_URL = HIFI_PUBLIC_BUCKET + "models/ping_pong_gun/target_collision_hull.obj";
var TARGET_DIMENSIONS = {
x: 0.06,
y: 0.42,
z: 0.42
};
var TARGET_ROTATION = Quat.fromPitchYawRollDegrees(0, -55.25, 0);
var targetsScriptURL = Script.resolvePath('../ping_pong_gun/wallTarget.js');
var basketballURL = HIFI_PUBLIC_BUCKET + "models/content/basketball2.fbx";
var NUMBER_OF_BALLS = 4;
var BALL_DIAMETER = 0.30;
var RESET_DISTANCE = 1;
var MINIMUM_MOVE_LENGTH = 0.05;
var totalTime = 0;
var lastUpdate = 0;
var UPDATE_INTERVAL = 1 / 5; // 5fps
var Resetter = {
searchForEntitiesToResetToOriginalPosition: function(searchOrigin, objectName) {
var ids = Entities.findEntities(searchOrigin, 5);
var objects = [];
var i;
var entityID;
var name;
for (i = 0; i < ids.length; i++) {
entityID = ids[i];
name = Entities.getEntityProperties(entityID, "name").name;
if (name === objectName) {
//we found an object to reset
objects.push(entityID);
}
}
return objects;
},
deleteObjects: function(objects) {
while (objects.length > 0) {
Entities.deleteEntity(objects.pop());
}
},
createBasketBalls: function() {
var position = {
x: 542.86,
y: 494.84,
z: 475.06
};
var i;
var ballPosition;
var collidingBall;
for (i = 0; i < NUMBER_OF_BALLS; i++) {
ballPosition = {
x: position.x,
y: position.y + BALL_DIAMETER * 2,
z: position.z + (BALL_DIAMETER) - (BALL_DIAMETER * i)
};
collidingBall = Entities.addEntity({
type: "Model",
name: 'Hifi-Basketball',
shapeType: 'Sphere',
position: ballPosition,
dimensions: {
x: BALL_DIAMETER,
y: BALL_DIAMETER,
z: BALL_DIAMETER
},
restitution: 1.0,
linearDamping: 0.00001,
gravity: {
x: 0,
y: -9.8,
z: 0
},
collisionsWillMove: true,
ignoreForCollisions: false,
modelURL: basketballURL,
userData: JSON.stringify({
originalPositionKey: {
originalPosition: ballPosition
},
resetMe: {
resetMe: true
},
grabbable: {
invertSolidWhileHeld: true
}
})
});
}
},
testBallDistanceFromStart: function(balls) {
var resetCount = 0;
balls.forEach(function(ball, index) {
var properties = Entities.getEntityProperties(ball, ["position", "userData"]);
var currentPosition = properties.position;
var originalPosition = properties.userData.originalPositionKey.originalPosition;
var distance = Vec3.subtract(originalPosition, currentPosition);
var length = Vec3.length(distance);
if (length > RESET_DISTANCE) {
Script.setTimeout(function() {
var newPosition = Entities.getEntityProperties(ball, "position").position;
var moving = Vec3.length(Vec3.subtract(currentPosition, newPosition));
if (moving < MINIMUM_MOVE_LENGTH) {
if (resetCount === balls.length) {
this.deleteObjects(balls);
this.createBasketBalls();
}
}
}, 200);
}
});
},
testTargetDistanceFromStart: function(targets) {
targets.forEach(function(target, index) {
var properties = Entities.getEntityProperties(target, ["position", "userData"]);
var currentPosition = properties.position;
var originalPosition = properties.userData.originalPositionKey.originalPosition;
var distance = Vec3.subtract(originalPosition, currentPosition);
var length = Vec3.length(distance);
if (length > RESET_DISTANCE) {
Script.setTimeout(function() {
var newPosition = Entities.getEntityProperties(target, "position").position;
var moving = Vec3.length(Vec3.subtract(currentPosition, newPosition));
if (moving < MINIMUM_MOVE_LENGTH) {
Entities.deleteEntity(target);
var targetProperties = {
name: 'Hifi-Target',
type: 'Model',
modelURL: TARGET_MODEL_URL,
shapeType: 'compound',
collisionsWillMove: true,
dimensions: TARGET_DIMENSIONS,
compoundShapeURL: TARGET_COLLISION_HULL_URL,
position: originalPosition,
rotation: TARGET_ROTATION,
script: targetsScriptURL,
userData: JSON.stringify({
resetMe: {
resetMe: true
},
grabbableKey: {
grabbable: false
},
originalPositionKey: originalPosition
})
};
Entities.addEntity(targetProperties);
}
}, 200);
}
});
}
};
function update(deltaTime) {
if (!Entities.serversExist() || !Entities.canRez()) {
return;
}
totalTime += deltaTime;
// We don't want to edit the entity EVERY update cycle, because that's just a lot
// of wasted bandwidth and extra effort on the server for very little visual gain
if (totalTime - lastUpdate > UPDATE_INTERVAL) {
//do stuff
var balls = Resetter.searchForEntitiesToResetToOriginalPosition({
x: 542.86,
y: 494.84,
z: 475.06
}, "Hifi-Basketball");
var targets = Resetter.searchForEntitiesToResetToOriginalPosition({
x: 548.68,
y: 497.30,
z: 509.74
}, "Hifi-Target");
if (balls.length !== 0) {
Resetter.testBallDistanceFromStart(balls);
}
if (targets.length !== 0) {
Resetter.testTargetDistanceFromStart(targets);
}
lastUpdate = totalTime;
}
}
Script.update.connect(update);

View file

@ -22,6 +22,12 @@
var ON_SOUND_URL = 'http://hifi-public.s3.amazonaws.com/sounds/Switches%20and%20sliders/flashlight_on.wav';
var OFF_SOUND_URL = 'http://hifi-public.s3.amazonaws.com/sounds/Switches%20and%20sliders/flashlight_off.wav';
//we are creating lights that we don't want to get stranded so lets make sure that we can get rid of them
var startTime = Date.now();
//if you're going to be using this in a dungeon or something and holding it for a long time, increase this lifetime value.
var LIFETIME = 25;
var MSEC_PER_SEC = 1000.0;
// this is the "constructor" for the entity as a JS object we don't do much here, but we do want to remember
// our this object, so we can access it in cases where we're called without a this (like in the case of various global signals)
function Flashlight() {
@ -32,10 +38,22 @@
var DISABLE_LIGHT_THRESHOLD = 0.7;
// These constants define the Spotlight position and orientation relative to the model
var MODEL_LIGHT_POSITION = { x: 0, y: -0.3, z: 0 };
var MODEL_LIGHT_ROTATION = Quat.angleAxis(-90, { x: 1, y: 0, z: 0 });
var MODEL_LIGHT_POSITION = {
x: 0,
y: -0.3,
z: 0
};
var MODEL_LIGHT_ROTATION = Quat.angleAxis(-90, {
x: 1,
y: 0,
z: 0
});
var GLOW_LIGHT_POSITION = { x: 0, y: -0.1, z: 0};
var GLOW_LIGHT_POSITION = {
x: 0,
y: -0.1,
z: 0
};
// Evaluate the world light entity positions and orientations from the model ones
function evalLightWorldTransform(modelPos, modelRot) {
@ -86,7 +104,8 @@
},
intensity: 2,
exponent: 0.3,
cutoff: 20
cutoff: 20,
lifetime: LIFETIME
});
//this light creates the effect of a bulb at the end of the flashlight
@ -105,6 +124,7 @@
},
exponent: 0,
cutoff: 90, // in degrees
lifetime: LIFETIME
});
this.hasSpotlight = true;
@ -151,11 +171,13 @@
Entities.editEntity(this.spotlight, {
position: lightTransform.p,
rotation: lightTransform.q,
lifetime: (Date.now() - startTime) / MSEC_PER_SEC + LIFETIME
});
Entities.editEntity(this.glowLight, {
position: glowLightTransform.p,
rotation: glowLightTransform.q,
lifetime: (Date.now() - startTime) / MSEC_PER_SEC + LIFETIME
});
},

View file

@ -1,60 +0,0 @@
// utilities.js
// examples
//
// Created by Eric Levin on June 8
// Copyright 2015 High Fidelity, Inc.
//
// Common utilities
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
map = function(value, min1, max1, min2, max2) {
return min2 + (max2 - min2) * ((value - min1) / (max1 - min1));
}
hslToRgb = function(hslColor) {
var h = hslColor.hue;
var s = hslColor.sat;
var l = hslColor.light;
var r, g, b;
if (s == 0) {
r = g = b = l; // achromatic
} else {
var hue2rgb = function hue2rgb(p, q, t) {
if (t < 0) t += 1;
if (t > 1) t -= 1;
if (t < 1 / 6) return p + (q - p) * 6 * t;
if (t < 1 / 2) return q;
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
return p;
}
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
var p = 2 * l - q;
r = hue2rgb(p, q, h + 1 / 3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1 / 3);
}
return {
red: Math.round(r * 255),
green: Math.round(g * 255),
blue: Math.round(b * 255)
};
}
randFloat = function(low, high) {
return low + Math.random() * (high - low);
}
randInt = function(low, high) {
return Math.floor(randFloat(low, high));
}

View file

@ -4,61 +4,63 @@
<context>
<name>Application</name>
<message>
<location filename="src/Application.cpp" line="1482"/>
<source>Sparse Voxel Octree Files (*.svo)</source>
<location filename="../src/Application.cpp" line="4014"/>
<source>Yes</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="src/Application.cpp" line="3465"/>
<location filename="../src/Application.cpp" line="4339"/>
<source>Open Script</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="src/Application.cpp" line="3466"/>
<location filename="../src/Application.cpp" line="4341"/>
<source>JavaScript Files (*.js)</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>AssetUploadDialogFactory</name>
<message>
<location filename="../src/ui/AssetUploadDialogFactory.cpp" line="77"/>
<source>Successful Asset Upload</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ChatWindow</name>
<message>
<location filename="ui/chatWindow.ui" line="20"/>
<location filename="../build/interface/ui_chatWindow.h" line="143"/>
<location filename="../ui/chatWindow.ui" line="20"/>
<source>Chat</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="ui/chatWindow.ui" line="50"/>
<location filename="../build/interface/ui_chatWindow.h" line="144"/>
<location filename="../ui/chatWindow.ui" line="64"/>
<source>Connecting to XMPP...</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="ui/chatWindow.ui" line="71"/>
<location filename="../build/interface/ui_chatWindow.h" line="145"/>
<location filename="../ui/chatWindow.ui" line="85"/>
<source> online now:</source>
<translation type="unfinished"></translation>
</message>
<message numerus="yes">
<location filename="src/ui/ChatWindow.cpp" line="128"/>
<source>day</source>
<translation>
<translation type="vanished">
<numerusform>%n day</numerusform>
<numerusform>%n days</numerusform>
</translation>
</message>
<message numerus="yes">
<location filename="src/ui/ChatWindow.cpp" line="128"/>
<source>hour</source>
<translation>
<translation type="vanished">
<numerusform>%n hour</numerusform>
<numerusform>%n hours</numerusform>
</translation>
</message>
<message numerus="yes">
<location filename="src/ui/ChatWindow.cpp" line="128"/>
<source>minute</source>
<translation>
<translation type="vanished">
<numerusform>%n minute</numerusform>
<numerusform>%n minutes</numerusform>
</translation>
@ -70,230 +72,757 @@
<numerusform>%n seconds</numerusform>
</translation>
</message>
</context>
<context>
<name>Console</name>
<message>
<location filename="src/ui/ChatWindow.cpp" line="183"/>
<source>%1 online now:</source>
<location filename="../ui/console.ui" line="14"/>
<source>Dialog</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/console.ui" line="209"/>
<source>&gt;</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Dialog</name>
<name>LoginDialog</name>
<message>
<location filename="ui/updateDialog.ui" line="20"/>
<location filename="ui/updateDialog.ui" line="73"/>
<location filename="../build/interface/ui_updateDialog.h" line="137"/>
<location filename="../build/interface/ui_updateDialog.h" line="138"/>
<source>Update Required</source>
<location filename="../ui/loginDialog.ui" line="20"/>
<source>Dialog</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="ui/updateDialog.ui" line="129"/>
<location filename="../build/interface/ui_updateDialog.h" line="140"/>
<source>Download</source>
<location filename="../ui/loginDialog.ui" line="117"/>
<source>Authenticating...</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="ui/updateDialog.ui" line="151"/>
<location filename="../build/interface/ui_updateDialog.h" line="141"/>
<source>Skip Version</source>
<location filename="../ui/loginDialog.ui" line="136"/>
<source>&lt;style type=&quot;text/css&quot;&gt;
a { text-decoration: none; color: #267077;}
&lt;/style&gt;
Invalid username or password. &lt;a href=&quot;https://metaverse.highfidelity.com/password/new&quot;&gt;Recover?&lt;/a&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="ui/updateDialog.ui" line="173"/>
<location filename="../build/interface/ui_updateDialog.h" line="142"/>
<location filename="../ui/loginDialog.ui" line="361"/>
<source>Username or Email</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/loginDialog.ui" line="395"/>
<source>Password</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/loginDialog.ui" line="429"/>
<source> Login</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/loginDialog.ui" line="458"/>
<source>&lt;style type=&quot;text/css&quot;&gt;
a { text-decoration: none; color: #267077; margin:0;padding:0;}
#create {font-weight:bold;}
p {margin:5px 0;}
&lt;/style&gt;
&lt;p&gt;&lt;a id=&quot;create&quot; href=&quot;%1&quot;&gt;Create account&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;%2&quot;&gt;Recover password&lt;/a&gt;&lt;/p&gt;</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>MessageDialog</name>
<message>
<location filename="../resources/qml/MessageDialog.qml" line="133"/>
<source>OK</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../resources/qml/MessageDialog.qml" line="139"/>
<source>Open</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../resources/qml/MessageDialog.qml" line="145"/>
<source>Save</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../resources/qml/MessageDialog.qml" line="151"/>
<source>Save All</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../resources/qml/MessageDialog.qml" line="157"/>
<source>Retry</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../resources/qml/MessageDialog.qml" line="163"/>
<source>Ignore</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../resources/qml/MessageDialog.qml" line="169"/>
<source>Apply</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../resources/qml/MessageDialog.qml" line="175"/>
<source>Yes</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../resources/qml/MessageDialog.qml" line="181"/>
<source>Yes to All</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../resources/qml/MessageDialog.qml" line="187"/>
<source>No</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../resources/qml/MessageDialog.qml" line="193"/>
<source>No to All</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../resources/qml/MessageDialog.qml" line="199"/>
<source>Discard</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../resources/qml/MessageDialog.qml" line="205"/>
<source>Reset</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../resources/qml/MessageDialog.qml" line="211"/>
<source>Restore Defaults</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../resources/qml/MessageDialog.qml" line="217"/>
<source>Cancel</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../resources/qml/MessageDialog.qml" line="223"/>
<source>Abort</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../resources/qml/MessageDialog.qml" line="229"/>
<source>Close</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Menu</name>
<message>
<location filename="src/Menu.cpp" line="554"/>
<source>Open .ini config file</source>
<location filename="../resources/qml/MessageDialog.qml" line="235"/>
<source>Show Details...</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="src/Menu.cpp" line="556"/>
<location filename="src/Menu.cpp" line="568"/>
<source>Text files (*.ini)</source>
<location filename="../resources/qml/MessageDialog.qml" line="241"/>
<source>Help</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="src/Menu.cpp" line="566"/>
<source>Save .ini config file</source>
<location filename="../resources/qml/MessageDialog.qml" line="295"/>
<source>Hide Details</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PreferencesDialog</name>
<message>
<location filename="ui/preferencesDialog.ui" line="90"/>
<location filename="../build/interface/ui_preferencesDialog.h" line="618"/>
<location filename="../ui/preferencesDialog.ui" line="2880"/>
<source>Cancel</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="ui/preferencesDialog.ui" line="125"/>
<location filename="../build/interface/ui_preferencesDialog.h" line="619"/>
<location filename="../ui/preferencesDialog.ui" line="2895"/>
<source>Save all changes</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="ui/preferencesDialog.ui" line="196"/>
<location filename="ui/preferencesDialog.ui" line="573"/>
<location filename="../build/interface/ui_preferencesDialog.h" line="620"/>
<location filename="../build/interface/ui_preferencesDialog.h" line="629"/>
<source>Avatar</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="ui/preferencesDialog.ui" line="230"/>
<location filename="../build/interface/ui_preferencesDialog.h" line="621"/>
<location filename="../ui/preferencesDialog.ui" line="152"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Avatar display name &lt;span style=&quot; color:#909090;&quot;&gt;(optional)&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="ui/preferencesDialog.ui" line="266"/>
<location filename="../build/interface/ui_preferencesDialog.h" line="622"/>
<location filename="../ui/preferencesDialog.ui" line="184"/>
<source>Not showing a name</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="ui/preferencesDialog.ui" line="294"/>
<location filename="../build/interface/ui_preferencesDialog.h" line="623"/>
<source>Head</source>
<location filename="../ui/preferencesDialog.ui" line="124"/>
<source>Avatar basics</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="ui/preferencesDialog.ui" line="395"/>
<location filename="../build/interface/ui_preferencesDialog.h" line="625"/>
<source>Body</source>
<location filename="../ui/preferencesDialog.ui" line="212"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Avatar collision sound URL &lt;span style=&quot; color:#909090;&quot;&gt;(optional)&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="ui/preferencesDialog.ui" line="506"/>
<location filename="../build/interface/ui_preferencesDialog.h" line="627"/>
<source>Advanced Tuning</source>
<location filename="../ui/preferencesDialog.ui" line="244"/>
<source>Enter the URL of a sound to play when you bump into something</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="ui/preferencesDialog.ui" line="537"/>
<location filename="../build/interface/ui_preferencesDialog.h" line="628"/>
<source>It&apos;s not recomended that you play with these settings unless you&apos;ve looked into exactly what they do.</source>
<location filename="../ui/preferencesDialog.ui" line="272"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Appearance&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="ui/preferencesDialog.ui" line="605"/>
<location filename="../build/interface/ui_preferencesDialog.h" line="630"/>
<location filename="../ui/preferencesDialog.ui" line="323"/>
<source>Change</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/preferencesDialog.ui" line="370"/>
<source>Snapshots</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/preferencesDialog.ui" line="396"/>
<source>Place my Snapshots here:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/preferencesDialog.ui" line="448"/>
<location filename="../ui/preferencesDialog.ui" line="579"/>
<source>Browse</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/preferencesDialog.ui" line="496"/>
<source>Scripts</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/preferencesDialog.ui" line="522"/>
<source>Load scripts from this directory:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/preferencesDialog.ui" line="619"/>
<source>Load Default Scripts</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/preferencesDialog.ui" line="653"/>
<source>Privacy</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/preferencesDialog.ui" line="693"/>
<source>Send data</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/preferencesDialog.ui" line="737"/>
<source>Level of Detail Tuning</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/preferencesDialog.ui" line="770"/>
<source>Minimum Desktop FPS</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/preferencesDialog.ui" line="855"/>
<source>Minimum HMD FPS</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/preferencesDialog.ui" line="947"/>
<source>Avatar tuning</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/preferencesDialog.ui" line="980"/>
<source>Real world vertical field of view (angular size of monitor)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/preferencesDialog.ui" line="1065"/>
<source>Vertical field of view</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="ui/preferencesDialog.ui" line="708"/>
<location filename="../build/interface/ui_preferencesDialog.h" line="631"/>
<location filename="../ui/preferencesDialog.ui" line="1165"/>
<source>Lean scale (applies to Faceshift users)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="ui/preferencesDialog.ui" line="793"/>
<location filename="../build/interface/ui_preferencesDialog.h" line="632"/>
<location filename="../ui/preferencesDialog.ui" line="1241"/>
<source>Avatar scale &lt;span style=&quot; color:#909090;&quot;&gt;(default is 1.0)&lt;/span&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="ui/preferencesDialog.ui" line="875"/>
<location filename="../build/interface/ui_preferencesDialog.h" line="633"/>
<source>Pupil dillation</source>
<location filename="../ui/preferencesDialog.ui" line="1314"/>
<source>Pupil dilation</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="ui/preferencesDialog.ui" line="954"/>
<location filename="../build/interface/ui_preferencesDialog.h" line="634"/>
<source>Audio Jitter Buffer Samples (0 for automatic)</source>
<location filename="../ui/preferencesDialog.ui" line="1390"/>
<source>Camera binary eyelid threshold</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="ui/preferencesDialog.ui" line="1045"/>
<location filename="../build/interface/ui_preferencesDialog.h" line="635"/>
<source>Faceshift eye detection</source>
<location filename="../ui/preferencesDialog.ui" line="1466"/>
<source>Face tracker eye deflection</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="ui/preferencesDialog.ui" line="1125"/>
<location filename="../build/interface/ui_preferencesDialog.h" line="636"/>
<location filename="../ui/preferencesDialog.ui" line="1542"/>
<source>Faceshift hostname</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/preferencesDialog.ui" line="1584"/>
<source>localhost</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/preferencesDialog.ui" line="1612"/>
<source>Avatar Animation JSON</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/preferencesDialog.ui" line="1649"/>
<source>default</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/preferencesDialog.ui" line="1685"/>
<source>Audio</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/preferencesDialog.ui" line="1714"/>
<source>Enable Dynamic Jitter Buffers</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/preferencesDialog.ui" line="1742"/>
<source>Static Jitter Buffer Frames</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/preferencesDialog.ui" line="1824"/>
<source>Max Frames Over Desired</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/preferencesDialog.ui" line="1900"/>
<source>Use Stdev for Dynamic Jitter Calc</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/preferencesDialog.ui" line="1934"/>
<source>Window A Starve Threshold</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/preferencesDialog.ui" line="2022"/>
<source>Window A (raise desired on N starves) Seconds</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/preferencesDialog.ui" line="2110"/>
<source>Window B (desired ceiling) Seconds</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/preferencesDialog.ui" line="2198"/>
<source>Repetition with Fade</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/preferencesDialog.ui" line="2232"/>
<source>Output Buffer Size (Frames)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/preferencesDialog.ui" line="2320"/>
<source>Output Starve Detection (Automatic Buffer Size Increase)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/preferencesDialog.ui" line="2354"/>
<source>Output Starve Detection Threshold</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/preferencesDialog.ui" line="2442"/>
<source>Output Starve Detection Period (ms)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/preferencesDialog.ui" line="2651"/>
<source>Oculus Rift</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/preferencesDialog.ui" line="2680"/>
<source>User Interface Angular Size</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/preferencesDialog.ui" line="2764"/>
<source>Sixense Controllers</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/preferencesDialog.ui" line="2793"/>
<source>Reticle Movement Speed</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/preferencesDialog.ui" line="2538"/>
<source>Octree</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="ui/preferencesDialog.ui" line="1236"/>
<location filename="../build/interface/ui_preferencesDialog.h" line="638"/>
<location filename="../ui/preferencesDialog.ui" line="2567"/>
<source>Max packets sent each second</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/ui/PreferencesDialog.cpp" line="92"/>
<source>Snapshots Location</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/ui/PreferencesDialog.cpp" line="101"/>
<source>Scripts Location</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>QObject</name>
<name>RealSense</name>
<message>
<location filename="src/ui/VoxelImportDialog.cpp" line="24"/>
<source>Loading ...</source>
<location filename="../src/devices/RealSense.cpp" line="253"/>
<source>Open RSSDK clip</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="src/ui/VoxelImportDialog.cpp" line="27"/>
<source>Cancel</source>
<location filename="../src/devices/RealSense.cpp" line="255"/>
<source>RSSDK Recordings (*.rssdk)</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>RunningScriptsWidget</name>
<message>
<location filename="ui/runningScriptsWidget.ui" line="14"/>
<location filename="../build/interface/ui_runningScriptsWidget.h" line="140"/>
<source>Form</source>
<location filename="../ui/runningScriptsWidget.ui" line="20"/>
<source>Running Scripts</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="ui/runningScriptsWidget.ui" line="39"/>
<location filename="../build/interface/ui_runningScriptsWidget.h" line="141"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:18pt;&quot;&gt;Running Scripts&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<location filename="../ui/runningScriptsWidget.ui" line="97"/>
<source>Currently Running</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="ui/runningScriptsWidget.ui" line="63"/>
<location filename="../build/interface/ui_runningScriptsWidget.h" line="142"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Currently running&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="ui/runningScriptsWidget.ui" line="89"/>
<location filename="../build/interface/ui_runningScriptsWidget.h" line="143"/>
<location filename="../ui/runningScriptsWidget.ui" line="159"/>
<source>Reload all</source>
<oldsource>Reload All</oldsource>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="ui/runningScriptsWidget.ui" line="116"/>
<location filename="../build/interface/ui_runningScriptsWidget.h" line="144"/>
<location filename="../ui/runningScriptsWidget.ui" line="172"/>
<source>Stop all</source>
<oldsource>Stop All</oldsource>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="ui/runningScriptsWidget.ui" line="137"/>
<location filename="../build/interface/ui_runningScriptsWidget.h" line="145"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Recently loaded&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<location filename="../ui/runningScriptsWidget.ui" line="240"/>
<source>There are no scripts running.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="ui/runningScriptsWidget.ui" line="154"/>
<location filename="../build/interface/ui_runningScriptsWidget.h" line="146"/>
<source>(click a script or use the 1-9 keys to load and run it)</source>
<location filename="../ui/runningScriptsWidget.ui" line="417"/>
<source>Load Scripts</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="ui/runningScriptsWidget.ui" line="202"/>
<location filename="../build/interface/ui_runningScriptsWidget.h" line="148"/>
<source>There are no scripts currently running.</source>
<location filename="../ui/runningScriptsWidget.ui" line="437"/>
<source>from URL</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/runningScriptsWidget.ui" line="444"/>
<source>from Disk</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/runningScriptsWidget.ui" line="472"/>
<source>filter</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ScriptEditorWidget</name>
<message>
<location filename="../ui/scriptEditorWidget.ui" line="35"/>
<source>Edit Script</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/scriptEditorWidget.ui" line="93"/>
<source>Debug Log:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/scriptEditorWidget.ui" line="112"/>
<source>Run on the fly (Careful: Any valid change made to the code will run immediately) </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/scriptEditorWidget.ui" line="119"/>
<source>Clear</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/ui/ScriptEditorWidget.cpp" line="118"/>
<location filename="../src/ui/ScriptEditorWidget.cpp" line="139"/>
<location filename="../src/ui/ScriptEditorWidget.cpp" line="210"/>
<source>Interface</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/ui/ScriptEditorWidget.cpp" line="118"/>
<source>Cannot write script %1:
%2.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/ui/ScriptEditorWidget.cpp" line="139"/>
<source>Cannot read script %1:
%2.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/ui/ScriptEditorWidget.cpp" line="188"/>
<source>Save script</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/ui/ScriptEditorWidget.cpp" line="190"/>
<source>JavaScript Files (*.js)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/ui/ScriptEditorWidget.cpp" line="211"/>
<source>The script has been modified.
Do you want to save your changes?</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/ui/ScriptEditorWidget.cpp" line="233"/>
<source>This file has been modified outside of the Interface editor.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/ui/ScriptEditorWidget.cpp" line="235"/>
<source>Do you want to reload it and lose the changes you&apos;ve made in the Interface editor?</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/ui/ScriptEditorWidget.cpp" line="236"/>
<source>Do you want to reload it?</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ScriptEditorWindow</name>
<message>
<location filename="../ui/scriptEditorWindow.ui" line="23"/>
<source>Script Editor</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/scriptEditorWindow.ui" line="61"/>
<source>New Script (Ctrl+N)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/scriptEditorWindow.ui" line="64"/>
<source>New</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/scriptEditorWindow.ui" line="89"/>
<source>Load Script (Ctrl+O)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/scriptEditorWindow.ui" line="92"/>
<source>Load</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/scriptEditorWindow.ui" line="132"/>
<source>Save Script (Ctrl+S)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/scriptEditorWindow.ui" line="135"/>
<source>Save</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/scriptEditorWindow.ui" line="154"/>
<source>Toggle Run Script (F5)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/scriptEditorWindow.ui" line="157"/>
<source>Run/Stop</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/scriptEditorWindow.ui" line="186"/>
<source>Automatically reload externally changed files</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/ui/ScriptEditorWindow.cpp" line="92"/>
<location filename="../src/ui/ScriptEditorWindow.cpp" line="192"/>
<source>Interface</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/ui/ScriptEditorWindow.cpp" line="94"/>
<source>JavaScript Files (*.js)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/ui/ScriptEditorWindow.cpp" line="193"/>
<source>There are some unsaved scripts, are you sure you want to close the editor? Changes will be lost!</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ScriptsModel</name>
<message>
<location filename="../src/ScriptsModel.cpp" line="284"/>
<source>/</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SnapshotShareDialog</name>
<message>
<location filename="../ui/shareSnapshot.ui" line="35"/>
<source>Share with Alphas</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/shareSnapshot.ui" line="201"/>
<source>Notes about this image</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/shareSnapshot.ui" line="277"/>
<source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:&apos;Helvetica&apos;; font-size:14px; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/shareSnapshot.ui" line="316"/>
<source>Share</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>TestMenu</name>
<message>
<location filename="../resources/qml/TestMenu.qml" line="12"/>
<source>About Interface</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../resources/qml/TestMenu.qml" line="21"/>
<source>Login</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../resources/qml/TestMenu.qml" line="26"/>
<source>Quit</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../resources/qml/TestMenu.qml" line="50"/>
<source>Animations...</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../resources/qml/TestMenu.qml" line="54"/>
<source>Attachments...</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../resources/qml/TestMenu.qml" line="58"/>
<source>Explode on quit</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../resources/qml/TestMenu.qml" line="64"/>
<source>Freeze on quit</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../resources/qml/TestMenu.qml" line="72"/>
<source>Everyone</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../resources/qml/TestMenu.qml" line="79"/>
<source>Friends</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../resources/qml/TestMenu.qml" line="85"/>
<source>No one</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>UserLocationsDialog</name>
<message>
<location filename="../ui/userLocationsDialog.ui" line="14"/>
<source>Form</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/userLocationsDialog.ui" line="41"/>
<source>My Locations</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/userLocationsDialog.ui" line="61"/>
<source>Refresh</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/userLocationsDialog.ui" line="112"/>
<source>Rename</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui/userLocationsDialog.ui" line="119"/>
<source>Delete</source>
<translation type="unfinished"></translation>
</message>
</context>

View file

@ -0,0 +1,28 @@
{
"name": "Hydra to Standard",
"channels": [
{ "from": "Hydra.LY", "to": "Standard.LY" },
{ "from": "Hydra.LX", "to": "Standard.LX" },
{ "from": "Hydra.LT", "to": "Standard.LT" },
{ "from": "Hydra.RY", "to": "Standard.RY" },
{ "from": "Hydra.RX", "to": "Standard.RX" },
{ "from": "Hydra.RT", "to": "Standard.RT" },
{ "from": "Hydra.LB", "to": "Standard.LB" },
{ "from": "Hydra.LS", "to": "Standard.LS" },
{ "from": "Hydra.RB", "to": "Standard.RB" },
{ "from": "Hydra.RS", "to": "Standard.RS" },
{ "from": "Hydra.L0", "to": "Standard.Back" },
{ "from": "Hydra.L1", "to": "Standard.DL" },
{ "from": "Hydra.L2", "to": "Standard.DD" },
{ "from": "Hydra.L3", "to": "Standard.DR" },
{ "from": "Hydra.L4", "to": "Standard.DU" },
{ "from": "Hydra.R0", "to": "Standard.Start" },
{ "from": "Hydra.R1", "to": "Standard.X" },
{ "from": "Hydra.R2", "to": "Standard.A" },
{ "from": "Hydra.R3", "to": "Standard.B" },
{ "from": "Hydra.R4", "to": "Standard.Y" }
]
}

View file

@ -0,0 +1,29 @@
{
"name": "XBox to Standard",
"channels": [
{ "from": "XBox.LY", "to": "Standard.LY" },
{ "from": "XBox.LX", "to": "Standard.LX" },
{ "from": "XBox.LT", "to": "Standard.LT" },
{ "from": "XBox.LB", "to": "Standard.LB" },
{ "from": "XBox.LS", "to": "Standard.LS" },
{ "from": "XBox.RY", "to": "Standard.RY" },
{ "from": "XBox.RX", "to": "Standard.RX" },
{ "from": "XBox.RT", "to": "Standard.RT" },
{ "from": "XBox.RB", "to": "Standard.RB" },
{ "from": "XBox.RS", "to": "Standard.RS" },
{ "from": "XBox.Back", "to": "Standard.Back" },
{ "from": "XBox.Start", "to": "Standard.Start" },
{ "from": "XBox.DU", "to": "Standard.DU" },
{ "from": "XBox.DD", "to": "Standard.DD" },
{ "from": "XBox.DL", "to": "Standard.DL" },
{ "from": "XBox.DR", "to": "Standard.DR" },
{ "from": "XBox.A", "to": "Standard.A" },
{ "from": "XBox.B", "to": "Standard.B" },
{ "from": "XBox.X", "to": "Standard.X" },
{ "from": "XBox.Y", "to": "Standard.Y" }
]
}

View file

@ -14,7 +14,7 @@
height="45"
id="svg3827"
version="1.1"
inkscape:version="0.48.1 r9760"
inkscape:version="0.48.3.1 r9886"
sodipodi:docname="start-script.svg">
<defs
id="defs3829">
@ -439,16 +439,16 @@
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="5.6"
inkscape:cx="76.804753"
inkscape:cy="13.198134"
inkscape:zoom="22.4"
inkscape:cx="44.04179"
inkscape:cy="22.346221"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1920"
inkscape:window-height="1028"
inkscape:window-width="1680"
inkscape:window-height="997"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-y="21"
inkscape:window-maximized="1" />
<metadata
id="metadata3832">
@ -536,5 +536,15 @@
height="44.57473"
x="84.498352"
y="1050.0748" />
<g
id="run-arrow"
transform="translate(-46.607143,-3.5714285)"
inkscape:label="#run-arrow">
<path
d="m 75.506508,1023.3478 1.372845,5.4631 -14.094975,-1.152 2.35e-4,7.2772 14.094975,-1.152 -1.372845,5.1249 13.761293,-7.6113 -13.761293,-7.9499 z"
id="arrow-rect"
inkscape:connector-curvature="0"
style="fill:#d3d3d3;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:0.77974033;stroke-linecap:round;stroke-linejoin:round" />
</g>
</g>
</svg>

Before

(image error) Size: 15 KiB

After

(image error) Size: 16 KiB

View file

@ -16,7 +16,7 @@ ScriptHighlighting::ScriptHighlighting(QTextDocument* parent) :
QSyntaxHighlighter(parent)
{
_keywordRegex = QRegExp("\\b(break|case|catch|continue|debugger|default|delete|do|else|finally|for|function|if|in|instanceof|new|return|switch|this|throw|try|typeof|var|void|while|with)\\b");
_qoutedTextRegex = QRegExp("\"[^\"]*(\"){0,1}");
_quotedTextRegex = QRegExp("(\"[^\"]*(\"){0,1}|\'[^\']*(\'){0,1})");
_multiLineCommentBegin = QRegExp("/\\*");
_multiLineCommentEnd = QRegExp("\\*/");
_numberRegex = QRegExp("[0-9]+(\\.[0-9]+){0,1}");
@ -29,7 +29,7 @@ void ScriptHighlighting::highlightBlock(const QString& text) {
this->highlightKeywords(text);
this->formatNumbers(text);
this->formatTrueFalse(text);
this->formatQoutedText(text);
this->formatQuotedText(text);
this->formatComments(text);
}
@ -61,14 +61,14 @@ void ScriptHighlighting::formatComments(const QString& text) {
int index = _singleLineComment.indexIn(text);
while (index >= 0) {
int length = _singleLineComment.matchedLength();
int quoted_index = _qoutedTextRegex.indexIn(text);
int quoted_index = _quotedTextRegex.indexIn(text);
bool valid = true;
while (quoted_index >= 0 && valid) {
int quoted_length = _qoutedTextRegex.matchedLength();
int quoted_length = _quotedTextRegex.matchedLength();
if (quoted_index <= index && index <= (quoted_index + quoted_length)) {
valid = false;
}
quoted_index = _qoutedTextRegex.indexIn(text, quoted_index + quoted_length);
quoted_index = _quotedTextRegex.indexIn(text, quoted_index + quoted_length);
}
if (valid) {
@ -78,12 +78,12 @@ void ScriptHighlighting::formatComments(const QString& text) {
}
}
void ScriptHighlighting::formatQoutedText(const QString& text){
int index = _qoutedTextRegex.indexIn(text);
void ScriptHighlighting::formatQuotedText(const QString& text){
int index = _quotedTextRegex.indexIn(text);
while (index >= 0) {
int length = _qoutedTextRegex.matchedLength();
int length = _quotedTextRegex.matchedLength();
setFormat(index, length, Qt::red);
index = _qoutedTextRegex.indexIn(text, index + length);
index = _quotedTextRegex.indexIn(text, index + length);
}
}

View file

@ -29,14 +29,14 @@ protected:
void highlightBlock(const QString& text);
void highlightKeywords(const QString& text);
void formatComments(const QString& text);
void formatQoutedText(const QString& text);
void formatQuotedText(const QString& text);
void formatNumbers(const QString& text);
void formatTrueFalse(const QString& text);
private:
QRegExp _alphacharRegex;
QRegExp _keywordRegex;
QRegExp _qoutedTextRegex;
QRegExp _quotedTextRegex;
QRegExp _multiLineCommentBegin;
QRegExp _multiLineCommentEnd;
QRegExp _numberRegex;

View file

@ -56,6 +56,7 @@ const float DISPLAYNAME_FADE_TIME = 0.5f;
const float DISPLAYNAME_FADE_FACTOR = pow(0.01f, 1.0f / DISPLAYNAME_FADE_TIME);
const float DISPLAYNAME_ALPHA = 1.0f;
const float DISPLAYNAME_BACKGROUND_ALPHA = 0.4f;
const glm::vec3 HAND_TO_PALM_OFFSET(0.0f, 0.12f, 0.08f);
namespace render {
template <> const ItemKey payloadGetKey(const AvatarSharedPointer& avatar) {
@ -1034,7 +1035,7 @@ int Avatar::parseDataFromBuffer(const QByteArray& buffer) {
const float MOVE_DISTANCE_THRESHOLD = 0.001f;
_moving = glm::distance(oldPosition, _position) > MOVE_DISTANCE_THRESHOLD;
if (_moving && _motionState) {
_motionState->addDirtyFlags(EntityItem::DIRTY_POSITION);
_motionState->addDirtyFlags(Simulation::DIRTY_POSITION);
}
endUpdate();
@ -1167,3 +1168,64 @@ void Avatar::rebuildSkeletonBody() {
DependencyManager::get<AvatarManager>()->updateAvatarPhysicsShape(getSessionUUID());
}
glm::vec3 Avatar::getLeftPalmPosition() {
glm::vec3 leftHandPosition;
getSkeletonModel().getLeftHandPosition(leftHandPosition);
glm::quat leftRotation;
getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getLeftHandJointIndex(), leftRotation);
leftHandPosition += HAND_TO_PALM_OFFSET * glm::inverse(leftRotation);
return leftHandPosition;
}
glm::vec3 Avatar::getLeftPalmVelocity() {
const PalmData* palm = getHand()->getPalm(LEFT_HAND_INDEX);
if (palm != NULL) {
return palm->getVelocity();
}
return glm::vec3(0.0f);
}
glm::vec3 Avatar::getLeftPalmAngularVelocity() {
const PalmData* palm = getHand()->getPalm(LEFT_HAND_INDEX);
if (palm != NULL) {
return palm->getRawAngularVelocity();
}
return glm::vec3(0.0f);
}
glm::quat Avatar::getLeftPalmRotation() {
glm::quat leftRotation;
getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getLeftHandJointIndex(), leftRotation);
return leftRotation;
}
glm::vec3 Avatar::getRightPalmPosition() {
glm::vec3 rightHandPosition;
getSkeletonModel().getRightHandPosition(rightHandPosition);
glm::quat rightRotation;
getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getRightHandJointIndex(), rightRotation);
rightHandPosition += HAND_TO_PALM_OFFSET * glm::inverse(rightRotation);
return rightHandPosition;
}
glm::vec3 Avatar::getRightPalmVelocity() {
const PalmData* palm = getHand()->getPalm(RIGHT_HAND_INDEX);
if (palm != NULL) {
return palm->getVelocity();
}
return glm::vec3(0.0f);
}
glm::vec3 Avatar::getRightPalmAngularVelocity() {
const PalmData* palm = getHand()->getPalm(RIGHT_HAND_INDEX);
if (palm != NULL) {
return palm->getRawAngularVelocity();
}
return glm::vec3(0.0f);
}
glm::quat Avatar::getRightPalmRotation() {
glm::quat rightRotation;
getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getRightHandJointIndex(), rightRotation);
return rightRotation;
}

View file

@ -174,6 +174,16 @@ public:
void setMotionState(AvatarMotionState* motionState) { _motionState = motionState; }
AvatarMotionState* getMotionState() { return _motionState; }
public slots:
glm::vec3 getLeftPalmPosition();
glm::vec3 getLeftPalmVelocity();
glm::vec3 getLeftPalmAngularVelocity();
glm::quat getLeftPalmRotation();
glm::vec3 getRightPalmPosition();
glm::vec3 getRightPalmVelocity();
glm::vec3 getRightPalmAngularVelocity();
glm::quat getRightPalmRotation();
protected:
SkeletonModel _skeletonModel;
glm::vec3 _skeletonOffset;

View file

@ -21,8 +21,7 @@ AvatarActionHold::AvatarActionHold(const QUuid& id, EntityItemPointer ownerEntit
ObjectActionSpring(id, ownerEntity),
_relativePosition(glm::vec3(0.0f)),
_relativeRotation(glm::quat()),
_hand("right"),
_mine(false)
_hand("right")
{
_type = ACTION_TYPE_HOLD;
#if WANT_DEBUG
@ -37,17 +36,12 @@ AvatarActionHold::~AvatarActionHold() {
}
void AvatarActionHold::updateActionWorker(float deltaTimeStep) {
if (!_mine) {
// if a local script isn't updating this, then we are just getting spring-action data over the wire.
// let the super-class handle it.
ObjectActionSpring::updateActionWorker(deltaTimeStep);
return;
}
bool gotLock = false;
glm::quat rotation;
glm::vec3 position;
glm::vec3 offset;
bool gotLock = withTryReadLock([&]{
gotLock = withTryReadLock([&]{
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
glm::vec3 palmPosition;
glm::quat palmRotation;
@ -66,17 +60,16 @@ void AvatarActionHold::updateActionWorker(float deltaTimeStep) {
if (gotLock) {
gotLock = withTryWriteLock([&]{
if (_positionalTarget != position || _rotationalTarget != rotation) {
auto ownerEntity = _ownerEntity.lock();
if (ownerEntity) {
ownerEntity->setActionDataDirty(true);
}
_positionalTarget = position;
_rotationalTarget = rotation;
_positionalTarget = position;
_rotationalTarget = rotation;
_positionalTargetSet = true;
_rotationalTargetSet = true;
auto ownerEntity = _ownerEntity.lock();
if (ownerEntity) {
ownerEntity->setActionDataDirty(true);
}
});
}
if (gotLock) {
ObjectActionSpring::updateActionWorker(deltaTimeStep);
}
@ -84,66 +77,76 @@ void AvatarActionHold::updateActionWorker(float deltaTimeStep) {
bool AvatarActionHold::updateArguments(QVariantMap arguments) {
if (!ObjectAction::updateArguments(arguments)) {
return false;
}
bool ok = true;
glm::vec3 relativePosition =
EntityActionInterface::extractVec3Argument("hold", arguments, "relativePosition", ok, false);
if (!ok) {
relativePosition = _relativePosition;
}
glm::vec3 relativePosition;
glm::quat relativeRotation;
float timeScale;
QString hand;
QUuid holderID;
bool needUpdate = false;
ok = true;
glm::quat relativeRotation =
EntityActionInterface::extractQuatArgument("hold", arguments, "relativeRotation", ok, false);
if (!ok) {
relativeRotation = _relativeRotation;
}
ok = true;
float timeScale =
EntityActionInterface::extractFloatArgument("hold", arguments, "timeScale", ok, false);
if (!ok) {
timeScale = _linearTimeScale;
}
bool somethingChanged = ObjectAction::updateArguments(arguments);
withReadLock([&]{
bool ok = true;
relativePosition = EntityActionInterface::extractVec3Argument("hold", arguments, "relativePosition", ok, false);
if (!ok) {
relativePosition = _relativePosition;
}
ok = true;
QString hand =
EntityActionInterface::extractStringArgument("hold", arguments, "hand", ok, false);
if (!ok || !(hand == "left" || hand == "right")) {
hand = _hand;
}
ok = true;
relativeRotation = EntityActionInterface::extractQuatArgument("hold", arguments, "relativeRotation", ok, false);
if (!ok) {
relativeRotation = _relativeRotation;
}
if (relativePosition != _relativePosition
|| relativeRotation != _relativeRotation
|| timeScale != _linearTimeScale
|| hand != _hand) {
ok = true;
timeScale = EntityActionInterface::extractFloatArgument("hold", arguments, "timeScale", ok, false);
if (!ok) {
timeScale = _linearTimeScale;
}
ok = true;
hand = EntityActionInterface::extractStringArgument("hold", arguments, "hand", ok, false);
if (!ok || !(hand == "left" || hand == "right")) {
hand = _hand;
}
ok = true;
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
holderID = myAvatar->getSessionUUID();
if (somethingChanged ||
relativePosition != _relativePosition ||
relativeRotation != _relativeRotation ||
timeScale != _linearTimeScale ||
hand != _hand) {
needUpdate = true;
}
});
if (needUpdate) {
withWriteLock([&] {
_relativePosition = relativePosition;
_relativeRotation = relativeRotation;
const float MIN_TIMESCALE = 0.1f;
_linearTimeScale = glm::min(MIN_TIMESCALE, timeScale);
_linearTimeScale = glm::max(MIN_TIMESCALE, timeScale);
_angularTimeScale = _linearTimeScale;
_hand = hand;
_mine = true;
_active = true;
activateBody();
auto ownerEntity = _ownerEntity.lock();
if (ownerEntity) {
ownerEntity->setActionDataDirty(true);
}
});
activateBody();
}
return true;
}
QVariantMap AvatarActionHold::getArguments() {
QVariantMap arguments = ObjectAction::getArguments();
withReadLock([&]{
if (!_mine) {
arguments = ObjectActionSpring::getArguments();
return;
}
arguments["relativePosition"] = glmToQMap(_relativePosition);
arguments["relativeRotation"] = glmToQMap(_relativeRotation);
arguments["timeScale"] = _linearTimeScale;
@ -152,9 +155,10 @@ QVariantMap AvatarActionHold::getArguments() {
return arguments;
}
QByteArray AvatarActionHold::serialize() const {
return ObjectActionSpring::serialize();
}
void AvatarActionHold::deserialize(QByteArray serializedArguments) {
if (!_mine) {
ObjectActionSpring::deserialize(serializedArguments);
}
assert(false);
}

View file

@ -27,15 +27,17 @@ public:
virtual void updateActionWorker(float deltaTimeStep);
QByteArray serialize() const;
virtual void deserialize(QByteArray serializedArguments);
virtual bool shouldSuppressLocationEdits() { return false; }
private:
static const uint16_t holdVersion;
glm::vec3 _relativePosition;
glm::quat _relativeRotation;
QString _hand;
bool _mine = false;
};
#endif // hifi_AvatarActionHold_h

View file

@ -309,7 +309,7 @@ void AvatarManager::updateAvatarPhysicsShape(const QUuid& id) {
auto avatar = std::static_pointer_cast<Avatar>(avatarItr.value());
AvatarMotionState* motionState = avatar->getMotionState();
if (motionState) {
motionState->addDirtyFlags(EntityItem::DIRTY_SHAPE);
motionState->addDirtyFlags(Simulation::DIRTY_SHAPE);
} else {
ShapeInfo shapeInfo;
avatar->computeShapeInfo(shapeInfo);
@ -343,3 +343,11 @@ void AvatarManager::updateAvatarRenderStatus(bool shouldRenderAvatars) {
}
}
}
AvatarSharedPointer AvatarManager::getAvatarBySessionID(const QUuid& sessionID) {
if (sessionID == _myAvatar->getSessionUUID()) {
return std::static_pointer_cast<Avatar>(_myAvatar);
}
return getAvatarHash()[sessionID];
}

View file

@ -35,7 +35,8 @@ public:
void init();
MyAvatar* getMyAvatar() { return _myAvatar.get(); }
AvatarSharedPointer getAvatarBySessionID(const QUuid& sessionID);
void updateMyAvatar(float deltaTime);
void updateOtherAvatars(float deltaTime);

View file

@ -540,70 +540,6 @@ void MyAvatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) {
}
}
const glm::vec3 HAND_TO_PALM_OFFSET(0.0f, 0.12f, 0.08f);
glm::vec3 MyAvatar::getLeftPalmPosition() {
glm::vec3 leftHandPosition;
getSkeletonModel().getLeftHandPosition(leftHandPosition);
glm::quat leftRotation;
getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getLeftHandJointIndex(), leftRotation);
leftHandPosition += HAND_TO_PALM_OFFSET * glm::inverse(leftRotation);
return leftHandPosition;
}
glm::vec3 MyAvatar::getLeftPalmVelocity() {
const PalmData* palm = getHand()->getPalm(LEFT_HAND_INDEX);
if (palm != NULL) {
return palm->getVelocity();
}
return glm::vec3(0.0f);
}
glm::vec3 MyAvatar::getLeftPalmAngularVelocity() {
const PalmData* palm = getHand()->getPalm(LEFT_HAND_INDEX);
if (palm != NULL) {
return palm->getRawAngularVelocity();
}
return glm::vec3(0.0f);
}
glm::quat MyAvatar::getLeftPalmRotation() {
glm::quat leftRotation;
getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getLeftHandJointIndex(), leftRotation);
return leftRotation;
}
glm::vec3 MyAvatar::getRightPalmPosition() {
glm::vec3 rightHandPosition;
getSkeletonModel().getRightHandPosition(rightHandPosition);
glm::quat rightRotation;
getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getRightHandJointIndex(), rightRotation);
rightHandPosition += HAND_TO_PALM_OFFSET * glm::inverse(rightRotation);
return rightHandPosition;
}
glm::vec3 MyAvatar::getRightPalmVelocity() {
const PalmData* palm = getHand()->getPalm(RIGHT_HAND_INDEX);
if (palm != NULL) {
return palm->getVelocity();
}
return glm::vec3(0.0f);
}
glm::vec3 MyAvatar::getRightPalmAngularVelocity() {
const PalmData* palm = getHand()->getPalm(RIGHT_HAND_INDEX);
if (palm != NULL) {
return palm->getRawAngularVelocity();
}
return glm::vec3(0.0f);
}
glm::quat MyAvatar::getRightPalmRotation() {
glm::quat rightRotation;
getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getRightHandJointIndex(), rightRotation);
return rightRotation;
}
void MyAvatar::clearReferential() {
changeReferential(NULL);
}

View file

@ -199,15 +199,6 @@ public slots:
Q_INVOKABLE void updateMotionBehaviorFromMenu();
glm::vec3 getLeftPalmPosition();
glm::vec3 getLeftPalmVelocity();
glm::vec3 getLeftPalmAngularVelocity();
glm::quat getLeftPalmRotation();
glm::vec3 getRightPalmPosition();
glm::vec3 getRightPalmVelocity();
glm::vec3 getRightPalmAngularVelocity();
glm::quat getRightPalmRotation();
void clearReferential();
bool setModelReferential(const QUuid& id);
bool setJointReferential(const QUuid& id, int jointIndex);

View file

@ -52,7 +52,10 @@ WebWindowClass::WebWindowClass(const QString& title, const QString& url, int wid
dockWidget->setWidget(_webView);
toolWindow->addDockWidget(Qt::RightDockWidgetArea, dockWidget);
auto titleWidget = new QWidget(dockWidget);
dockWidget->setTitleBarWidget(titleWidget);
toolWindow->addDockWidget(Qt::TopDockWidgetArea, dockWidget, Qt::Horizontal);
_windowWidget = dockWidget;
} else {

View file

@ -9,9 +9,11 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <QFuture>
#include <QKeyEvent>
#include <QLabel>
#include <QScrollBar>
#include <QtConcurrent/QtConcurrentRun>
#include <PathUtils.h>
@ -35,7 +37,8 @@ JSConsole::JSConsole(QWidget* parent, ScriptEngine* scriptEngine) :
_ui(new Ui::Console),
_currentCommandInHistory(NO_CURRENT_HISTORY_COMMAND),
_commandHistory(),
_scriptEngine(scriptEngine) {
_ownScriptEngine(scriptEngine == NULL),
_scriptEngine(NULL) {
_ui->setupUi(this);
_ui->promptTextEdit->setLineWrapMode(QTextEdit::NoWrap);
@ -51,23 +54,42 @@ JSConsole::JSConsole(QWidget* parent, ScriptEngine* scriptEngine) :
connect(_ui->scrollArea->verticalScrollBar(), SIGNAL(rangeChanged(int, int)), this, SLOT(scrollToBottom()));
connect(_ui->promptTextEdit, SIGNAL(textChanged()), this, SLOT(resizeTextInput()));
if (_scriptEngine == NULL) {
_scriptEngine = qApp->loadScript(QString(), false);
}
connect(_scriptEngine, SIGNAL(evaluationFinished(QScriptValue, bool)),
this, SLOT(handleEvalutationFinished(QScriptValue, bool)));
connect(_scriptEngine, SIGNAL(printedMessage(const QString&)), this, SLOT(handlePrint(const QString&)));
connect(_scriptEngine, SIGNAL(errorMessage(const QString&)), this, SLOT(handleError(const QString&)));
setScriptEngine(scriptEngine);
resizeTextInput();
connect(&_executeWatcher, SIGNAL(finished()), this, SLOT(commandFinished()));
}
JSConsole::~JSConsole() {
disconnect(_scriptEngine, SIGNAL(printedMessage(const QString&)), this, SLOT(handlePrint(const QString&)));
disconnect(_scriptEngine, SIGNAL(errorMessage(const QString&)), this, SLOT(handleError(const QString&)));
if (_ownScriptEngine) {
_scriptEngine->deleteLater();
}
delete _ui;
}
void JSConsole::setScriptEngine(ScriptEngine* scriptEngine) {
if (_scriptEngine == scriptEngine && scriptEngine != NULL) {
return;
}
if (_scriptEngine != NULL) {
disconnect(_scriptEngine, SIGNAL(printedMessage(const QString&)), this, SLOT(handlePrint(const QString&)));
disconnect(_scriptEngine, SIGNAL(errorMessage(const QString&)), this, SLOT(handleError(const QString&)));
if (_ownScriptEngine) {
_scriptEngine->deleteLater();
}
}
// if scriptEngine is NULL then create one and keep track of it using _ownScriptEngine
_ownScriptEngine = scriptEngine == NULL;
_scriptEngine = _ownScriptEngine ? qApp->loadScript(QString(), false) : scriptEngine;
connect(_scriptEngine, SIGNAL(printedMessage(const QString&)), this, SLOT(handlePrint(const QString&)));
connect(_scriptEngine, SIGNAL(errorMessage(const QString&)), this, SLOT(handleError(const QString&)));
}
void JSConsole::executeCommand(const QString& command) {
_commandHistory.prepend(command);
if (_commandHistory.length() > MAX_HISTORY_SIZE) {
@ -78,23 +100,34 @@ void JSConsole::executeCommand(const QString& command) {
appendMessage(">", "<span style='" + COMMAND_STYLE + "'>" + command.toHtmlEscaped() + "</span>");
QMetaObject::invokeMethod(_scriptEngine, "evaluate", Q_ARG(const QString&, command));
resetCurrentCommandHistory();
QFuture<QScriptValue> future = QtConcurrent::run(this, &JSConsole::executeCommandInWatcher, command);
_executeWatcher.setFuture(future);
}
void JSConsole::handleEvalutationFinished(QScriptValue result, bool isException) {
QScriptValue JSConsole::executeCommandInWatcher(const QString& command) {
QScriptValue result;
QMetaObject::invokeMethod(_scriptEngine, "evaluate", Qt::ConnectionType::BlockingQueuedConnection,
Q_RETURN_ARG(QScriptValue, result), Q_ARG(const QString&, command));
return result;
}
void JSConsole::commandFinished() {
QScriptValue result = _executeWatcher.result();
_ui->promptTextEdit->setDisabled(false);
// Make sure focus is still on this window - some commands are blocking and can take awhile to execute.
if (window()->isActiveWindow()) {
_ui->promptTextEdit->setFocus();
}
QString gutter = (isException || result.isError()) ? GUTTER_ERROR : GUTTER_PREVIOUS_COMMAND;
QString resultColor = (isException || result.isError()) ? RESULT_ERROR_STYLE : RESULT_SUCCESS_STYLE;
QString resultStr = "<span style='" + resultColor + "'>" + result.toString().toHtmlEscaped() + "</span>";
bool error = (_scriptEngine->hasUncaughtException() || result.isError());
QString gutter = error ? GUTTER_ERROR : GUTTER_PREVIOUS_COMMAND;
QString resultColor = error ? RESULT_ERROR_STYLE : RESULT_SUCCESS_STYLE;
QString resultStr = "<span style='" + resultColor + "'>" + result.toString().toHtmlEscaped() + "</span>";
appendMessage(gutter, resultStr);
resetCurrentCommandHistory();
}
void JSConsole::handleError(const QString& message) {
@ -233,3 +266,13 @@ void JSConsole::appendMessage(const QString& gutter, const QString& message) {
_ui->logArea->updateGeometry();
scrollToBottom();
}
void JSConsole::clear() {
QLayoutItem* item;
while ((item = _ui->logArea->layout()->takeAt(0)) != NULL) {
delete item->widget();
delete item;
}
_ui->logArea->updateGeometry();
scrollToBottom();
}

View file

@ -14,6 +14,7 @@
#include <QDialog>
#include <QEvent>
#include <QFutureWatcher>
#include <QObject>
#include <QWidget>
@ -31,13 +32,12 @@ public:
JSConsole(QWidget* parent, ScriptEngine* scriptEngine = NULL);
~JSConsole();
void setScriptEngine(ScriptEngine* scriptEngine = NULL);
void clear();
public slots:
void executeCommand(const QString& command);
signals:
void commandExecuting(const QString& command);
void commandFinished(const QString& result);
protected:
void setAndSelectCommand(const QString& command);
virtual bool eventFilter(QObject* sender, QEvent* event);
@ -47,19 +47,23 @@ protected:
protected slots:
void scrollToBottom();
void resizeTextInput();
void handleEvalutationFinished(QScriptValue result, bool isException);
void handlePrint(const QString& message);
void handleError(const QString& message);
void commandFinished();
private:
void appendMessage(const QString& gutter, const QString& message);
void setToNextCommandInHistory();
void setToPreviousCommandInHistory();
void resetCurrentCommandHistory();
QScriptValue executeCommandInWatcher(const QString& command);
QFutureWatcher<QScriptValue> _executeWatcher;
Ui::Console* _ui;
int _currentCommandInHistory;
QList<QString> _commandHistory;
// Keeps track if the script engine is created inside the JSConsole
bool _ownScriptEngine;
QString _rootCommand;
ScriptEngine* _scriptEngine;
};

View file

@ -54,11 +54,13 @@ OctreeStatsDialog::OctreeStatsDialog(QWidget* parent, NodeToOctreeSceneStats* mo
_localElementsMemory = AddStatItem("Elements Memory");
_sendingMode = AddStatItem("Sending Mode");
_processedPackets = AddStatItem("Entity Packets");
_processedPackets = AddStatItem("Incoming Entity Packets");
_processedPacketsElements = AddStatItem("Processed Packets Elements");
_processedPacketsEntities = AddStatItem("Processed Packets Entities");
_processedPacketsTiming = AddStatItem("Processed Packets Timing");
_outboundEditPackets = AddStatItem("Outbound Entity Packets");
_entityUpdateTime = AddStatItem("Entity Update Time");
_entityUpdates = AddStatItem("Entity Updates");
@ -266,6 +268,7 @@ void OctreeStatsDialog::paintEvent(QPaintEvent* event) {
label = _labels[_processedPackets];
const OctreePacketProcessor& entitiesPacketProcessor = qApp->getOctreePacketProcessor();
auto incomingPacketsDepth = entitiesPacketProcessor.packetsToProcessCount();
auto incomingPPS = entitiesPacketProcessor.getIncomingPPS();
auto processedPPS = entitiesPacketProcessor.getProcessedPPS();
auto treeProcessedPPS = entities->getAveragePacketsPerSecond();
@ -276,6 +279,7 @@ void OctreeStatsDialog::paintEvent(QPaintEvent* event) {
statsValue.str("");
statsValue <<
"Queue Size: " << incomingPacketsDepth << " Packets / " <<
"Network IN: " << qPrintable(incomingPPSString) << " PPS / " <<
"Queue OUT: " << qPrintable(processedPPSString) << " PPS / " <<
"Tree IN: " << qPrintable(treeProcessedPPSString) << " PPS";
@ -301,12 +305,31 @@ void OctreeStatsDialog::paintEvent(QPaintEvent* event) {
label = _labels[_processedPacketsTiming];
statsValue.str("");
statsValue <<
"Lock Wait:" << qPrintable(averageWaitLockPerPacketString) << " (usecs) / " <<
"Uncompress:" << qPrintable(averageUncompressPerPacketString) << " (usecs) / " <<
"Process:" << qPrintable(averageReadBitstreamPerPacketString) << " (usecs)";
"Lock Wait: " << qPrintable(averageWaitLockPerPacketString) << " (usecs) / " <<
"Uncompress: " << qPrintable(averageUncompressPerPacketString) << " (usecs) / " <<
"Process: " << qPrintable(averageReadBitstreamPerPacketString) << " (usecs)";
label->setText(statsValue.str().c_str());
auto entitiesEditPacketSender = qApp->getEntityEditPacketSender();
auto outboundPacketsDepth = entitiesEditPacketSender->packetsToSendCount();
auto outboundQueuedPPS = entitiesEditPacketSender->getLifetimePPSQueued();
auto outboundSentPPS = entitiesEditPacketSender->getLifetimePPS();
QString outboundQueuedPPSString = locale.toString(outboundQueuedPPS, 'f', FLOATING_POINT_PRECISION);
QString outboundSentPPSString = locale.toString(outboundSentPPS, 'f', FLOATING_POINT_PRECISION);
label = _labels[_outboundEditPackets];
statsValue.str("");
statsValue <<
"Queue Size: " << outboundPacketsDepth << " packets / " <<
"Queued IN: " << qPrintable(outboundQueuedPPSString) << " PPS / " <<
"Sent OUT: " << qPrintable(outboundSentPPSString) << " PPS";
label->setText(statsValue.str().c_str());
// Entity Edits update time
label = _labels[_entityUpdateTime];
auto averageEditDelta = entitiesTree->getAverageEditDeltas();

View file

@ -70,10 +70,11 @@ private:
int _processedPacketsElements;
int _processedPacketsEntities;
int _processedPacketsTiming;
int _outboundEditPackets;
const int SAMPLES_PER_SECOND = 10;
SimpleMovingAverage _averageUpdatesPerSecond;
quint64 _lastWindowAt = 0;
quint64 _lastWindowAt = usecTimestampNow();
quint64 _lastKnownTrackedEdits = 0;
quint64 _lastRefresh = 0;

View file

@ -52,10 +52,16 @@ ScriptEditorWidget::ScriptEditorWidget() :
// We create a new ScriptHighligting QObject and provide it with a parent so this is NOT a memory leak.
new ScriptHighlighting(_scriptEditorWidgetUI->scriptEdit->document());
QTimer::singleShot(0, _scriptEditorWidgetUI->scriptEdit, SLOT(setFocus()));
_console = new JSConsole(this);
_console->setFixedHeight(CONSOLE_HEIGHT);
_scriptEditorWidgetUI->verticalLayout->addWidget(_console);
connect(_scriptEditorWidgetUI->clearButton, &QPushButton::clicked, _console, &JSConsole::clear);
}
ScriptEditorWidget::~ScriptEditorWidget() {
delete _scriptEditorWidgetUI;
delete _console;
}
void ScriptEditorWidget::onScriptModified() {
@ -68,6 +74,7 @@ void ScriptEditorWidget::onScriptModified() {
void ScriptEditorWidget::onScriptFinished(const QString& scriptPath) {
_scriptEngine = NULL;
_console->setScriptEngine(NULL);
if (_isRestarting) {
_isRestarting = false;
setRunning(true);
@ -89,8 +96,6 @@ bool ScriptEditorWidget::setRunning(bool run) {
if (_scriptEngine != NULL) {
disconnect(_scriptEngine, &ScriptEngine::runningStateChanged, this, &ScriptEditorWidget::runningStateChanged);
disconnect(_scriptEngine, &ScriptEngine::errorMessage, this, &ScriptEditorWidget::onScriptError);
disconnect(_scriptEngine, &ScriptEngine::printedMessage, this, &ScriptEditorWidget::onScriptPrint);
disconnect(_scriptEngine, &ScriptEngine::update, this, &ScriptEditorWidget::onScriptModified);
disconnect(_scriptEngine, &ScriptEngine::finished, this, &ScriptEditorWidget::onScriptFinished);
}
@ -100,15 +105,15 @@ bool ScriptEditorWidget::setRunning(bool run) {
// Reload script so that an out of date copy is not retrieved from the cache
_scriptEngine = qApp->loadScript(scriptURLString, true, true, false, true);
connect(_scriptEngine, &ScriptEngine::runningStateChanged, this, &ScriptEditorWidget::runningStateChanged);
connect(_scriptEngine, &ScriptEngine::errorMessage, this, &ScriptEditorWidget::onScriptError);
connect(_scriptEngine, &ScriptEngine::printedMessage, this, &ScriptEditorWidget::onScriptPrint);
connect(_scriptEngine, &ScriptEngine::update, this, &ScriptEditorWidget::onScriptModified);
connect(_scriptEngine, &ScriptEngine::finished, this, &ScriptEditorWidget::onScriptFinished);
} else {
connect(_scriptEngine, &ScriptEngine::finished, this, &ScriptEditorWidget::onScriptFinished);
qApp->stopScript(_currentScript);
const QString& scriptURLString = QUrl(_currentScript).toString();
qApp->stopScript(scriptURLString);
_scriptEngine = NULL;
}
_console->setScriptEngine(_scriptEngine);
return true;
}
@ -147,8 +152,6 @@ void ScriptEditorWidget::loadFile(const QString& scriptPath) {
if (_scriptEngine != NULL) {
disconnect(_scriptEngine, &ScriptEngine::runningStateChanged, this, &ScriptEditorWidget::runningStateChanged);
disconnect(_scriptEngine, &ScriptEngine::errorMessage, this, &ScriptEditorWidget::onScriptError);
disconnect(_scriptEngine, &ScriptEngine::printedMessage, this, &ScriptEditorWidget::onScriptPrint);
disconnect(_scriptEngine, &ScriptEngine::update, this, &ScriptEditorWidget::onScriptModified);
disconnect(_scriptEngine, &ScriptEngine::finished, this, &ScriptEditorWidget::onScriptFinished);
}
@ -168,16 +171,14 @@ void ScriptEditorWidget::loadFile(const QString& scriptPath) {
static_cast<ScriptEditorWindow*>(this->parent()->parent()->parent())->terminateCurrentTab();
}
}
const QString& scriptURLString = QUrl(_currentScript).toString();
_scriptEngine = qApp->getScriptEngine(scriptURLString);
if (_scriptEngine != NULL) {
connect(_scriptEngine, &ScriptEngine::runningStateChanged, this, &ScriptEditorWidget::runningStateChanged);
connect(_scriptEngine, &ScriptEngine::errorMessage, this, &ScriptEditorWidget::onScriptError);
connect(_scriptEngine, &ScriptEngine::printedMessage, this, &ScriptEditorWidget::onScriptPrint);
connect(_scriptEngine, &ScriptEngine::update, this, &ScriptEditorWidget::onScriptModified);
connect(_scriptEngine, &ScriptEngine::finished, this, &ScriptEditorWidget::onScriptFinished);
}
_console->setScriptEngine(_scriptEngine);
}
bool ScriptEditorWidget::save() {
@ -210,19 +211,11 @@ bool ScriptEditorWidget::questionSave() {
QMessageBox::StandardButton button = QMessageBox::warning(this, tr("Interface"),
tr("The script has been modified.\nDo you want to save your changes?"), QMessageBox::Save | QMessageBox::Discard |
QMessageBox::Cancel, QMessageBox::Save);
return button == QMessageBox::Save ? save() : (button == QMessageBox::Cancel ? false : true);
return button == QMessageBox::Save ? save() : (button == QMessageBox::Discard);
}
return true;
}
void ScriptEditorWidget::onScriptError(const QString& message) {
_scriptEditorWidgetUI->debugText->appendPlainText("ERROR: " + message);
}
void ScriptEditorWidget::onScriptPrint(const QString& message) {
_scriptEditorWidgetUI->debugText->appendPlainText("> " + message);
}
void ScriptEditorWidget::onWindowActivated() {
if (!_isReloading) {
_isReloading = true;
@ -241,10 +234,8 @@ void ScriptEditorWidget::onWindowActivated() {
setRunning(false);
// Script is restarted once current script instance finishes.
}
}
}
_isReloading = false;
}
}

View file

@ -14,6 +14,7 @@
#include <QDockWidget>
#include "JSConsole.h"
#include "ScriptEngine.h"
namespace Ui {
@ -47,12 +48,11 @@ public slots:
void onWindowActivated();
private slots:
void onScriptError(const QString& message);
void onScriptPrint(const QString& message);
void onScriptModified();
void onScriptFinished(const QString& scriptName);
private:
JSConsole* _console;
Ui::ScriptEditorWidget* _scriptEditorWidgetUI;
ScriptEngine* _scriptEngine;
QString _currentScript;

View file

@ -28,7 +28,6 @@
#include <QWidget>
#include "Application.h"
#include "JSConsole.h"
#include "PathUtils.h"
ScriptEditorWindow::ScriptEditorWindow(QWidget* parent) :
@ -59,10 +58,6 @@ ScriptEditorWindow::ScriptEditorWindow(QWidget* parent) :
_ScriptEditorWindowUI->newButton->setIcon(QIcon(QPixmap(PathUtils::resourcesPath() + "icons/new-script.svg")));
_ScriptEditorWindowUI->saveButton->setIcon(QIcon(QPixmap(PathUtils::resourcesPath() + "icons/save-script.svg")));
_ScriptEditorWindowUI->toggleRunButton->setIcon(QIcon(QPixmap(PathUtils::resourcesPath() + "icons/start-script.svg")));
QWidget* console = new JSConsole(this);
console->setFixedHeight(CONSOLE_HEIGHT);
this->layout()->addWidget(console);
}
ScriptEditorWindow::~ScriptEditorWindow() {
@ -77,10 +72,11 @@ void ScriptEditorWindow::setRunningState(bool run) {
}
void ScriptEditorWindow::updateButtons() {
bool isRunning = _ScriptEditorWindowUI->tabWidget->currentIndex() != -1 &&
static_cast<ScriptEditorWidget*>(_ScriptEditorWindowUI->tabWidget->currentWidget())->isRunning();
_ScriptEditorWindowUI->toggleRunButton->setEnabled(_ScriptEditorWindowUI->tabWidget->currentIndex() != -1);
_ScriptEditorWindowUI->toggleRunButton->setIcon(_ScriptEditorWindowUI->tabWidget->currentIndex() != -1 &&
static_cast<ScriptEditorWidget*>(_ScriptEditorWindowUI->tabWidget->currentWidget())->isRunning() ?
QIcon("../resources/icons/stop-script.svg") : QIcon("../resources/icons/start-script.svg"));
_ScriptEditorWindowUI->toggleRunButton->setIcon(QIcon(QPixmap(PathUtils::resourcesPath() + ((isRunning ?
"icons/stop-script.svg" : "icons/start-script.svg")))));
}
void ScriptEditorWindow::loadScriptMenu(const QString& scriptName) {

View file

@ -22,6 +22,8 @@ ToolWindow::ToolWindow(QWidget* parent) :
_hasShown(false),
_lastGeometry() {
setTabPosition(Qt::TopDockWidgetArea, QTabWidget::TabPosition::North);
# ifndef Q_OS_LINUX
setDockOptions(QMainWindow::ForceTabbedDocks);
# endif
@ -31,6 +33,7 @@ ToolWindow::ToolWindow(QWidget* parent) :
bool ToolWindow::event(QEvent* event) {
QEvent::Type type = event->type();
if (type == QEvent::Show) {
if (!_hasShown) {
_hasShown = true;
@ -40,10 +43,12 @@ bool ToolWindow::event(QEvent* event) {
int titleBarHeight = UIUtil::getWindowTitleBarHeight(this);
int topMargin = titleBarHeight;
_lastGeometry = QRect(mainGeometry.topLeft().x(), mainGeometry.topLeft().y() + topMargin,
DEFAULT_WIDTH, mainGeometry.height() - topMargin);
_lastGeometry = QRect(mainGeometry.topLeft().x(), mainGeometry.topLeft().y() + topMargin,
DEFAULT_WIDTH, mainGeometry.height() - topMargin);
}
setGeometry(_lastGeometry);
return true;
} else if (type == QEvent::Hide) {
_lastGeometry = geometry();
@ -109,6 +114,7 @@ void ToolWindow::addDockWidget(Qt::DockWidgetArea area, QDockWidget* dockWidget)
// We want to force tabbing, so retabify all of our widgets.
QDockWidget* lastDockWidget = dockWidget;
foreach (QDockWidget* nextDockWidget, dockWidgets) {
tabifyDockWidget(lastDockWidget, nextDockWidget);
lastDockWidget = nextDockWidget;
@ -118,8 +124,17 @@ void ToolWindow::addDockWidget(Qt::DockWidgetArea area, QDockWidget* dockWidget)
}
void ToolWindow::addDockWidget(Qt::DockWidgetArea area, QDockWidget* dockWidget, Qt::Orientation orientation) {
QList<QDockWidget*> dockWidgets = findChildren<QDockWidget*>();
QMainWindow::addDockWidget(area, dockWidget, orientation);
QDockWidget* lastDockWidget = dockWidget;
foreach(QDockWidget* nextDockWidget, dockWidgets) {
tabifyDockWidget(lastDockWidget, nextDockWidget);
lastDockWidget = nextDockWidget;
}
connect(dockWidget, &QDockWidget::visibilityChanged, this, &ToolWindow::onChildVisibilityUpdated);
}

View file

@ -128,25 +128,6 @@
</item>
</layout>
</item>
<item>
<widget class="QPlainTextEdit" name="debugText">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="styleSheet">
<string notr="true">font: 15px &quot;Courier&quot;;</string>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
<property name="plainText">
<string/>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
@ -158,22 +139,4 @@
</customwidget>
</customwidgets>
<resources/>
<connections>
<connection>
<sender>clearButton</sender>
<signal>clicked()</signal>
<receiver>debugText</receiver>
<slot>clear()</slot>
<hints>
<hint type="sourcelabel">
<x>663</x>
<y>447</y>
</hint>
<hint type="destinationlabel">
<x>350</x>
<y>501</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View file

@ -57,3 +57,28 @@ UserInputMapper::PoseValue InputDevice::getPose(int channel) const {
return UserInputMapper::PoseValue();
}
}
UserInputMapper::Input InputDevice::makeInput(controller::StandardButtonChannel button) {
return UserInputMapper::Input(_deviceID, button, UserInputMapper::ChannelType::BUTTON);
}
UserInputMapper::Input InputDevice::makeInput(controller::StandardAxisChannel axis) {
return UserInputMapper::Input(_deviceID, axis, UserInputMapper::ChannelType::AXIS);
}
UserInputMapper::Input InputDevice::makeInput(controller::StandardPoseChannel pose) {
return UserInputMapper::Input(_deviceID, pose, UserInputMapper::ChannelType::POSE);
}
UserInputMapper::InputPair InputDevice::makePair(controller::StandardButtonChannel button, const QString& name) {
return UserInputMapper::InputPair(makeInput(button), name);
}
UserInputMapper::InputPair InputDevice::makePair(controller::StandardAxisChannel axis, const QString& name) {
return UserInputMapper::InputPair(makeInput(axis), name);
}
UserInputMapper::InputPair InputDevice::makePair(controller::StandardPoseChannel pose, const QString& name) {
return UserInputMapper::InputPair(makeInput(pose), name);
}

View file

@ -11,6 +11,7 @@
#pragma once
#include "UserInputMapper.h"
#include "StandardControls.h"
// Event types for each controller
const unsigned int CONTROLLER_0_EVENT = 1500U;
@ -33,7 +34,7 @@ public:
UserInputMapper::PoseValue getPose(int channel) const;
virtual void registerToUserInputMapper(UserInputMapper& mapper) = 0;
virtual void assignDefaultInputMapping(UserInputMapper& mapper) = 0;
virtual void assignDefaultInputMapping(UserInputMapper& mapper) {};
// Update call MUST be called once per simulation loop
// It takes care of updating the action states and deltas
@ -49,10 +50,17 @@ public:
static bool getLowVelocityFilter() { return _lowVelocityFilter; };
UserInputMapper::Input makeInput(controller::StandardButtonChannel button);
UserInputMapper::Input makeInput(controller::StandardAxisChannel axis);
UserInputMapper::Input makeInput(controller::StandardPoseChannel pose);
UserInputMapper::InputPair makePair(controller::StandardButtonChannel button, const QString& name);
UserInputMapper::InputPair makePair(controller::StandardAxisChannel button, const QString& name);
UserInputMapper::InputPair makePair(controller::StandardPoseChannel button, const QString& name);
public slots:
static void setLowVelocityFilter(bool newLowVelocityFilter) { _lowVelocityFilter = newLowVelocityFilter; };
protected:
int _deviceID = 0;
QString _name;

View file

@ -482,7 +482,7 @@ void EntityTreeRenderer::deleteReleasedModels() {
}
RayToEntityIntersectionResult EntityTreeRenderer::findRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType,
bool precisionPicking) {
bool precisionPicking, const QVector<QUuid>& entityIdsToInclude) {
RayToEntityIntersectionResult result;
if (_tree) {
EntityTreePointer entityTree = std::static_pointer_cast<EntityTree>(_tree);
@ -490,7 +490,7 @@ RayToEntityIntersectionResult EntityTreeRenderer::findRayIntersectionWorker(cons
OctreeElementPointer element;
EntityItemPointer intersectedEntity = NULL;
result.intersects = entityTree->findRayIntersection(ray.origin, ray.direction, element, result.distance,
result.face, result.surfaceNormal,
result.face, result.surfaceNormal, entityIdsToInclude,
(void**)&intersectedEntity, lockType, &result.accurate,
precisionPicking);
if (result.intersects && intersectedEntity) {

View file

@ -130,7 +130,7 @@ private:
QList<Model*> _releasedModels;
RayToEntityIntersectionResult findRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType,
bool precisionPicking);
bool precisionPicking, const QVector<QUuid>& entityIdsToInclude = QVector<QUuid>());
EntityItemID _currentHoverOverEntityID;
EntityItemID _currentClickingOnEntityID;

View file

@ -29,7 +29,7 @@ EntityItemPointer RenderableModelEntityItem::factory(const EntityItemID& entityI
RenderableModelEntityItem::RenderableModelEntityItem(const EntityItemID& entityItemID,
const EntityItemProperties& properties) :
ModelEntityItem(entityItemID, properties),
_dimensionsInitialized { properties.dimensionsChanged() }
_dimensionsInitialized(properties.getDimensionsInitialized())
{
}

View file

@ -1057,7 +1057,7 @@ void RenderablePolyVoxEntityItem::getMeshAsync() {
gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RAW)));
_meshLock.lockForWrite();
_dirtyFlags |= EntityItem::DIRTY_SHAPE | EntityItem::DIRTY_MASS;
_dirtyFlags |= Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS;
_mesh = mesh;
_meshDirty = true;
_meshLock.unlock();

View file

@ -163,6 +163,19 @@ void AnimationPropertyGroup::debugDump() const {
qDebug() << "currentFrame:" << getCurrentFrame() << " has changed:" << currentFrameChanged();
}
void AnimationPropertyGroup::listChangedProperties(QList<QString>& out) {
if (urlChanged()) {
out << "animation-url";
}
if (fpsChanged()) {
out << "animation-fps";
}
if (currentFrameChanged()) {
out << "animation-currentFrame";
}
}
bool AnimationPropertyGroup::appendToEditPacket(OctreePacketData* packetData,
EntityPropertyFlags& requestedProperties,
EntityPropertyFlags& propertyFlags,

View file

@ -37,6 +37,7 @@ public:
virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const;
virtual void copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings);
virtual void debugDump() const;
virtual void listChangedProperties(QList<QString>& out);
virtual bool appendToEditPacket(OctreePacketData* packetData,
EntityPropertyFlags& requestedProperties,

View file

@ -54,6 +54,30 @@ void AtmospherePropertyGroup::debugDump() const {
qDebug() << " Has Stars:" << getHasStars() << " has changed:" << hasStarsChanged();
}
void AtmospherePropertyGroup::listChangedProperties(QList<QString>& out) {
if (centerChanged()) {
out << "center";
}
if (innerRadiusChanged()) {
out << "innerRadius";
}
if (outerRadiusChanged()) {
out << "outerRadius";
}
if (mieScatteringChanged()) {
out << "mieScattering";
}
if (rayleighScatteringChanged()) {
out << "rayleighScattering";
}
if (scatteringWavelengthsChanged()) {
out << "scatteringWavelengths";
}
if (hasStarsChanged()) {
out << "hasStars";
}
}
bool AtmospherePropertyGroup::appendToEditPacket(OctreePacketData* packetData,
EntityPropertyFlags& requestedProperties,
EntityPropertyFlags& propertyFlags,

View file

@ -53,6 +53,7 @@ public:
virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const;
virtual void copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings);
virtual void debugDump() const;
virtual void listChangedProperties(QList<QString>& out);
virtual bool appendToEditPacket(OctreePacketData* packetData,
EntityPropertyFlags& requestedProperties,
@ -87,7 +88,7 @@ public:
EntityPropertyFlags& propertyFlags, bool overwriteLocalData,
bool& somethingChanged);
static const glm::vec3 DEFAULT_CENTER;
static const float DEFAULT_INNER_RADIUS;
static const float DEFAULT_OUTER_RADIUS;

View file

@ -51,15 +51,7 @@ public:
bool locallyAddedButNotYetReceived = false;
protected:
virtual glm::vec3 getPosition() = 0;
virtual void setPosition(glm::vec3 position) = 0;
virtual glm::quat getRotation() = 0;
virtual void setRotation(glm::quat rotation) = 0;
virtual glm::vec3 getLinearVelocity() = 0;
virtual void setLinearVelocity(glm::vec3 linearVelocity) = 0;
virtual glm::vec3 getAngularVelocity() = 0;
virtual void setAngularVelocity(glm::vec3 angularVelocity) = 0;
virtual bool shouldSuppressLocationEdits() { return false; }
// these look in the arguments map for a named argument. if it's not found or isn't well formed,
// ok will be set to false (note that it's never set to true -- set it to true before calling these).
@ -75,6 +67,16 @@ protected:
static QString extractStringArgument(QString objectName, QVariantMap arguments,
QString argumentName, bool& ok, bool required = true);
protected:
virtual glm::vec3 getPosition() = 0;
virtual void setPosition(glm::vec3 position) = 0;
virtual glm::quat getRotation() = 0;
virtual void setRotation(glm::quat rotation) = 0;
virtual glm::vec3 getLinearVelocity() = 0;
virtual void setLinearVelocity(glm::vec3 linearVelocity) = 0;
virtual glm::vec3 getAngularVelocity() = 0;
virtual void setAngularVelocity(glm::vec3 angularVelocity) = 0;
QUuid _id;
EntityActionType _type;
};

View file

@ -630,7 +630,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
bytesRead += bytes;
if (_simulationOwner.set(newSimOwner)) {
_dirtyFlags |= EntityItem::DIRTY_SIMULATOR_ID;
_dirtyFlags |= Simulation::DIRTY_SIMULATOR_ID;
}
}
{ // When we own the simulation we don't accept updates to the entity's transform/velocities
@ -729,7 +729,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
READ_ENTITY_PROPERTY(PROP_MARKETPLACE_ID, QString, setMarketplaceID);
}
if (overwriteLocalData && (getDirtyFlags() & (EntityItem::DIRTY_TRANSFORM | EntityItem::DIRTY_VELOCITIES))) {
if (overwriteLocalData && (getDirtyFlags() & (Simulation::DIRTY_TRANSFORM | Simulation::DIRTY_VELOCITIES))) {
// NOTE: This code is attempting to "repair" the old data we just got from the server to make it more
// closely match where the entities should be if they'd stepped forward in time to "now". The server
// is sending us data with a known "last simulated" time. That time is likely in the past, and therefore
@ -813,7 +813,7 @@ void EntityItem::updateDensity(float density) {
if (fabsf(_density - clampedDensity) / _density > ACTIVATION_RELATIVE_DENSITY_DELTA) {
// the density has changed enough that we should update the physics simulation
_dirtyFlags |= EntityItem::DIRTY_MASS;
_dirtyFlags |= Simulation::DIRTY_MASS;
}
}
}
@ -905,7 +905,7 @@ void EntityItem::simulateKinematicMotion(float timeElapsed, bool setFlags) {
const float EPSILON_ANGULAR_VELOCITY_LENGTH = 0.0017453f; // 0.0017453 rad/sec = 0.1f degrees/sec
if (angularSpeed < EPSILON_ANGULAR_VELOCITY_LENGTH) {
if (setFlags && angularSpeed > 0.0f) {
_dirtyFlags |= EntityItem::DIRTY_MOTION_TYPE;
_dirtyFlags |= Simulation::DIRTY_MOTION_TYPE;
}
_angularVelocity = ENTITY_ITEM_ZERO_VEC3;
} else {
@ -967,7 +967,7 @@ void EntityItem::simulateKinematicMotion(float timeElapsed, bool setFlags) {
if (speed < EPSILON_LINEAR_VELOCITY_LENGTH) {
setVelocity(ENTITY_ITEM_ZERO_VEC3);
if (setFlags && speed > 0.0f) {
_dirtyFlags |= EntityItem::DIRTY_MOTION_TYPE;
_dirtyFlags |= Simulation::DIRTY_MOTION_TYPE;
}
} else {
setPosition(position);
@ -1134,7 +1134,7 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) {
#endif
setLastEdited(now);
somethingChangedNotification(); // notify derived classes that something has changed
if (getDirtyFlags() & (EntityItem::DIRTY_TRANSFORM | EntityItem::DIRTY_VELOCITIES)) {
if (getDirtyFlags() & (Simulation::DIRTY_TRANSFORM | Simulation::DIRTY_VELOCITIES)) {
// anything that sets the transform or velocity must update _lastSimulated which is used
// for kinematic extrapolation (e.g. we want to extrapolate forward from this moment
// when position and/or velocity was changed).
@ -1302,10 +1302,10 @@ void EntityItem::computeShapeInfo(ShapeInfo& info) {
void EntityItem::updatePosition(const glm::vec3& value) {
auto delta = glm::distance(getPosition(), value);
if (delta > IGNORE_POSITION_DELTA) {
_dirtyFlags |= EntityItem::DIRTY_POSITION;
_dirtyFlags |= Simulation::DIRTY_POSITION;
setPosition(value);
if (delta > ACTIVATION_POSITION_DELTA) {
_dirtyFlags |= EntityItem::DIRTY_PHYSICS_ACTIVATION;
_dirtyFlags |= Simulation::DIRTY_PHYSICS_ACTIVATION;
}
}
}
@ -1316,7 +1316,7 @@ void EntityItem::updateDimensions(const glm::vec3& value) {
setDimensions(value);
if (delta > ACTIVATION_DIMENSIONS_DELTA) {
// rebuilding the shape will always activate
_dirtyFlags |= (EntityItem::DIRTY_SHAPE | EntityItem::DIRTY_MASS);
_dirtyFlags |= (Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS);
}
}
}
@ -1327,10 +1327,10 @@ void EntityItem::updateRotation(const glm::quat& rotation) {
auto alignmentDot = glm::abs(glm::dot(getRotation(), rotation));
if (alignmentDot < IGNORE_ALIGNMENT_DOT) {
_dirtyFlags |= EntityItem::DIRTY_ROTATION;
_dirtyFlags |= Simulation::DIRTY_ROTATION;
}
if (alignmentDot < ACTIVATION_ALIGNMENT_DOT) {
_dirtyFlags |= EntityItem::DIRTY_PHYSICS_ACTIVATION;
_dirtyFlags |= Simulation::DIRTY_PHYSICS_ACTIVATION;
}
}
}
@ -1357,14 +1357,14 @@ void EntityItem::updateMass(float mass) {
if (fabsf(_density - oldDensity) / _density > ACTIVATION_RELATIVE_DENSITY_DELTA) {
// the density has changed enough that we should update the physics simulation
_dirtyFlags |= EntityItem::DIRTY_MASS;
_dirtyFlags |= Simulation::DIRTY_MASS;
}
}
void EntityItem::updateVelocity(const glm::vec3& value) {
auto delta = glm::distance(_velocity, value);
if (delta > IGNORE_LINEAR_VELOCITY_DELTA) {
_dirtyFlags |= EntityItem::DIRTY_LINEAR_VELOCITY;
_dirtyFlags |= Simulation::DIRTY_LINEAR_VELOCITY;
const float MIN_LINEAR_SPEED = 0.001f;
if (glm::length(value) < MIN_LINEAR_SPEED) {
_velocity = ENTITY_ITEM_ZERO_VEC3;
@ -1372,7 +1372,7 @@ void EntityItem::updateVelocity(const glm::vec3& value) {
_velocity = value;
// only activate when setting non-zero velocity
if (delta > ACTIVATION_LINEAR_VELOCITY_DELTA) {
_dirtyFlags |= EntityItem::DIRTY_PHYSICS_ACTIVATION;
_dirtyFlags |= Simulation::DIRTY_PHYSICS_ACTIVATION;
}
}
}
@ -1382,7 +1382,7 @@ void EntityItem::updateDamping(float value) {
auto clampedDamping = glm::clamp(value, 0.0f, 1.0f);
if (fabsf(_damping - clampedDamping) > IGNORE_DAMPING_DELTA) {
_damping = clampedDamping;
_dirtyFlags |= EntityItem::DIRTY_MATERIAL;
_dirtyFlags |= Simulation::DIRTY_MATERIAL;
}
}
@ -1390,9 +1390,9 @@ void EntityItem::updateGravity(const glm::vec3& value) {
auto delta = glm::distance(_gravity, value);
if (delta > IGNORE_GRAVITY_DELTA) {
_gravity = value;
_dirtyFlags |= EntityItem::DIRTY_LINEAR_VELOCITY;
_dirtyFlags |= Simulation::DIRTY_LINEAR_VELOCITY;
if (delta > ACTIVATION_GRAVITY_DELTA) {
_dirtyFlags |= EntityItem::DIRTY_PHYSICS_ACTIVATION;
_dirtyFlags |= Simulation::DIRTY_PHYSICS_ACTIVATION;
}
}
}
@ -1400,7 +1400,7 @@ void EntityItem::updateGravity(const glm::vec3& value) {
void EntityItem::updateAngularVelocity(const glm::vec3& value) {
auto delta = glm::distance(_angularVelocity, value);
if (delta > IGNORE_ANGULAR_VELOCITY_DELTA) {
_dirtyFlags |= EntityItem::DIRTY_ANGULAR_VELOCITY;
_dirtyFlags |= Simulation::DIRTY_ANGULAR_VELOCITY;
const float MIN_ANGULAR_SPEED = 0.0002f;
if (glm::length(value) < MIN_ANGULAR_SPEED) {
_angularVelocity = ENTITY_ITEM_ZERO_VEC3;
@ -1408,7 +1408,7 @@ void EntityItem::updateAngularVelocity(const glm::vec3& value) {
_angularVelocity = value;
// only activate when setting non-zero velocity
if (delta > ACTIVATION_ANGULAR_VELOCITY_DELTA) {
_dirtyFlags |= EntityItem::DIRTY_PHYSICS_ACTIVATION;
_dirtyFlags |= Simulation::DIRTY_PHYSICS_ACTIVATION;
}
}
}
@ -1418,21 +1418,21 @@ void EntityItem::updateAngularDamping(float value) {
auto clampedDamping = glm::clamp(value, 0.0f, 1.0f);
if (fabsf(_angularDamping - clampedDamping) > IGNORE_DAMPING_DELTA) {
_angularDamping = clampedDamping;
_dirtyFlags |= EntityItem::DIRTY_MATERIAL;
_dirtyFlags |= Simulation::DIRTY_MATERIAL;
}
}
void EntityItem::updateIgnoreForCollisions(bool value) {
if (_ignoreForCollisions != value) {
_ignoreForCollisions = value;
_dirtyFlags |= EntityItem::DIRTY_COLLISION_GROUP;
_dirtyFlags |= Simulation::DIRTY_COLLISION_GROUP;
}
}
void EntityItem::updateCollisionsWillMove(bool value) {
if (_collisionsWillMove != value) {
_collisionsWillMove = value;
_dirtyFlags |= EntityItem::DIRTY_MOTION_TYPE;
_dirtyFlags |= Simulation::DIRTY_MOTION_TYPE;
}
}
@ -1440,7 +1440,7 @@ void EntityItem::updateRestitution(float value) {
float clampedValue = glm::max(glm::min(ENTITY_ITEM_MAX_RESTITUTION, value), ENTITY_ITEM_MIN_RESTITUTION);
if (_restitution != clampedValue) {
_restitution = clampedValue;
_dirtyFlags |= EntityItem::DIRTY_MATERIAL;
_dirtyFlags |= Simulation::DIRTY_MATERIAL;
}
}
@ -1448,7 +1448,7 @@ void EntityItem::updateFriction(float value) {
float clampedValue = glm::max(glm::min(ENTITY_ITEM_MAX_FRICTION, value), ENTITY_ITEM_MIN_FRICTION);
if (_friction != clampedValue) {
_friction = clampedValue;
_dirtyFlags |= EntityItem::DIRTY_MATERIAL;
_dirtyFlags |= Simulation::DIRTY_MATERIAL;
}
}
@ -1465,14 +1465,14 @@ void EntityItem::setFriction(float value) {
void EntityItem::updateLifetime(float value) {
if (_lifetime != value) {
_lifetime = value;
_dirtyFlags |= EntityItem::DIRTY_LIFETIME;
_dirtyFlags |= Simulation::DIRTY_LIFETIME;
}
}
void EntityItem::updateCreated(uint64_t value) {
if (_created != value) {
_created = value;
_dirtyFlags |= EntityItem::DIRTY_LIFETIME;
_dirtyFlags |= Simulation::DIRTY_LIFETIME;
}
}
@ -1486,7 +1486,7 @@ void EntityItem::setSimulationOwner(const SimulationOwner& owner) {
void EntityItem::updateSimulatorID(const QUuid& value) {
if (_simulationOwner.setID(value)) {
_dirtyFlags |= EntityItem::DIRTY_SIMULATOR_ID;
_dirtyFlags |= Simulation::DIRTY_SIMULATOR_ID;
}
}
@ -1494,7 +1494,7 @@ void EntityItem::clearSimulationOwnership() {
_simulationOwner.clear();
// don't bother setting the DIRTY_SIMULATOR_ID flag because clearSimulationOwnership()
// is only ever called entity-server-side and the flags are only used client-side
//_dirtyFlags |= EntityItem::DIRTY_SIMULATOR_ID;
//_dirtyFlags |= Simulation::DIRTY_SIMULATOR_ID;
}
@ -1533,7 +1533,7 @@ bool EntityItem::addActionInternal(EntitySimulation* simulation, EntityActionPoi
serializeActions(success, newDataCache);
if (success) {
_allActionsDataCache = newDataCache;
_dirtyFlags |= EntityItem::DIRTY_PHYSICS_ACTIVATION;
_dirtyFlags |= Simulation::DIRTY_PHYSICS_ACTIVATION;
}
return success;
}
@ -1552,7 +1552,7 @@ bool EntityItem::updateAction(EntitySimulation* simulation, const QUuid& actionI
success = action->updateArguments(arguments);
if (success) {
serializeActions(success, _allActionsDataCache);
_dirtyFlags |= EntityItem::DIRTY_PHYSICS_ACTIVATION;
_dirtyFlags |= Simulation::DIRTY_PHYSICS_ACTIVATION;
} else {
qDebug() << "EntityItem::updateAction failed";
}
@ -1579,6 +1579,7 @@ bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulation* s
}
EntityActionPointer action = _objectActions[actionID];
action->setOwnerEntity(nullptr);
_objectActions.remove(actionID);
@ -1588,7 +1589,7 @@ bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulation* s
bool success = true;
serializeActions(success, _allActionsDataCache);
_dirtyFlags |= EntityItem::DIRTY_PHYSICS_ACTIVATION;
_dirtyFlags |= Simulation::DIRTY_PHYSICS_ACTIVATION;
return success;
}
return false;
@ -1607,7 +1608,7 @@ bool EntityItem::clearActions(EntitySimulation* simulation) {
// empty _serializedActions means no actions for the EntityItem
_actionsToRemove.clear();
_allActionsDataCache.clear();
_dirtyFlags |= EntityItem::DIRTY_PHYSICS_ACTIVATION;
_dirtyFlags |= Simulation::DIRTY_PHYSICS_ACTIVATION;
});
return true;
}
@ -1630,8 +1631,6 @@ void EntityItem::deserializeActionsInternal() {
return;
}
// Keep track of which actions got added or updated by the new actionData
EntityTreePointer entityTree = _element ? _element->getTree() : nullptr;
assert(entityTree);
EntitySimulation* simulation = entityTree ? entityTree->getSimulation() : nullptr;
@ -1643,6 +1642,7 @@ void EntityItem::deserializeActionsInternal() {
serializedActionsStream >> serializedActions;
}
// Keep track of which actions got added or updated by the new actionData
QSet<QUuid> updated;
foreach(QByteArray serializedAction, serializedActions) {
@ -1720,9 +1720,11 @@ void EntityItem::setActionData(QByteArray actionData) {
void EntityItem::setActionDataInternal(QByteArray actionData) {
assertWriteLocked();
if (_allActionsDataCache != actionData) {
_allActionsDataCache = actionData;
deserializeActionsInternal();
}
checkWaitingToRemove();
_allActionsDataCache = actionData;
deserializeActionsInternal();
}
void EntityItem::serializeActions(bool& success, QByteArray& result) const {
@ -1788,3 +1790,15 @@ QVariantMap EntityItem::getActionArguments(const QUuid& actionID) const {
return result;
}
bool EntityItem::shouldSuppressLocationEdits() const {
QHash<QUuid, EntityActionPointer>::const_iterator i = _objectActions.begin();
while (i != _objectActions.end()) {
if (i.value()->shouldSuppressLocationEdits()) {
return true;
}
i++;
}
return false;
}

View file

@ -29,6 +29,7 @@
#include "EntityPropertyFlags.h"
#include "EntityTypes.h"
#include "SimulationOwner.h"
#include "SimulationFlags.h"
class EntitySimulation;
class EntityTreeElement;
@ -102,24 +103,6 @@ class EntityItem : public std::enable_shared_from_this<EntityItem>, public ReadW
friend class EntityTreeElement;
friend class EntitySimulation;
public:
enum EntityDirtyFlags {
DIRTY_POSITION = 0x0001,
DIRTY_ROTATION = 0x0002,
DIRTY_LINEAR_VELOCITY = 0x0004,
DIRTY_ANGULAR_VELOCITY = 0x0008,
DIRTY_MASS = 0x0010,
DIRTY_COLLISION_GROUP = 0x0020,
DIRTY_MOTION_TYPE = 0x0040,
DIRTY_SHAPE = 0x0080,
DIRTY_LIFETIME = 0x0100,
DIRTY_UPDATEABLE = 0x0200,
DIRTY_MATERIAL = 0x00400,
DIRTY_PHYSICS_ACTIVATION = 0x0800, // should activate object in physics engine
DIRTY_SIMULATOR_OWNERSHIP = 0x1000, // should claim simulator ownership
DIRTY_SIMULATOR_ID = 0x2000, // the simulatorID has changed
DIRTY_TRANSFORM = DIRTY_POSITION | DIRTY_ROTATION,
DIRTY_VELOCITIES = DIRTY_LINEAR_VELOCITY | DIRTY_ANGULAR_VELOCITY
};
DONT_ALLOW_INSTANTIATION // This class can not be instantiated directly
@ -411,7 +394,7 @@ public:
void getAllTerseUpdateProperties(EntityItemProperties& properties) const;
void flagForOwnership() { _dirtyFlags |= DIRTY_SIMULATOR_OWNERSHIP; }
void flagForOwnership() { _dirtyFlags |= Simulation::DIRTY_SIMULATOR_OWNERSHIP; }
bool addAction(EntitySimulation* simulation, EntityActionPointer action);
bool updateAction(EntitySimulation* simulation, const QUuid& actionID, const QVariantMap& arguments);
@ -424,6 +407,7 @@ public:
QVariantMap getActionArguments(const QUuid& actionID) const;
void deserializeActions();
void setActionDataDirty(bool value) const { _actionDataDirty = value; }
bool shouldSuppressLocationEdits() const;
protected:

View file

@ -1519,3 +1519,254 @@ void EntityItemProperties::setSimulationOwner(const QByteArray& data) {
_simulationOwnerChanged = true;
}
}
QList<QString> EntityItemProperties::listChangedProperties() {
QList<QString> out;
if (containsPositionChange()) {
out += "posistion";
}
if (dimensionsChanged()) {
out += "dimensions";
}
if (velocityChanged()) {
out += "velocity";
}
if (nameChanged()) {
out += "name";
}
if (visibleChanged()) {
out += "visible";
}
if (rotationChanged()) {
out += "rotation";
}
if (densityChanged()) {
out += "density";
}
if (gravityChanged()) {
out += "gravity";
}
if (accelerationChanged()) {
out += "acceleration";
}
if (dampingChanged()) {
out += "damping";
}
if (restitutionChanged()) {
out += "restitution";
}
if (frictionChanged()) {
out += "friction";
}
if (lifetimeChanged()) {
out += "lifetime";
}
if (scriptChanged()) {
out += "script";
}
if (scriptTimestampChanged()) {
out += "scriptTimestamp";
}
if (collisionSoundURLChanged()) {
out += "collisionSoundURL";
}
if (colorChanged()) {
out += "color";
}
if (colorSpreadChanged()) {
out += "colorSpread";
}
if (colorStartChanged()) {
out += "colorStart";
}
if (colorFinishChanged()) {
out += "colorFinish";
}
if (alphaChanged()) {
out += "alpha";
}
if (alphaSpreadChanged()) {
out += "alphaSpread";
}
if (alphaStartChanged()) {
out += "alphaStart";
}
if (alphaFinishChanged()) {
out += "alphaFinish";
}
if (modelURLChanged()) {
out += "modelURL";
}
if (compoundShapeURLChanged()) {
out += "compoundShapeURL";
}
if (registrationPointChanged()) {
out += "registrationPoint";
}
if (angularVelocityChanged()) {
out += "angularVelocity";
}
if (angularDampingChanged()) {
out += "angularDamping";
}
if (ignoreForCollisionsChanged()) {
out += "ignoreForCollisions";
}
if (collisionsWillMoveChanged()) {
out += "collisionsWillMove";
}
if (isSpotlightChanged()) {
out += "isSpotlight";
}
if (intensityChanged()) {
out += "intensity";
}
if (exponentChanged()) {
out += "exponent";
}
if (cutoffChanged()) {
out += "cutoff";
}
if (lockedChanged()) {
out += "locked";
}
if (texturesChanged()) {
out += "textures";
}
if (userDataChanged()) {
out += "userData";
}
if (simulationOwnerChanged()) {
out += "simulationOwner";
}
if (textChanged()) {
out += "text";
}
if (lineHeightChanged()) {
out += "lineHeight";
}
if (textColorChanged()) {
out += "textColor";
}
if (backgroundColorChanged()) {
out += "backgroundColor";
}
if (shapeTypeChanged()) {
out += "shapeType";
}
if (maxParticlesChanged()) {
out += "maxParticles";
}
if (lifespanChanged()) {
out += "lifespan";
}
if (isEmittingChanged()) {
out += "isEmitting";
}
if (emitRateChanged()) {
out += "emitRate";
}
if (emitSpeedChanged()) {
out += "emitSpeed";
}
if (speedSpreadChanged()) {
out += "speedSpread";
}
if (emitOrientationChanged()) {
out += "emitOrientation";
}
if (emitDimensionsChanged()) {
out += "emitDimensions";
}
if (emitRadiusStartChanged()) {
out += "emitRadiusStart";
}
if (polarStartChanged()) {
out += "polarStart";
}
if (polarFinishChanged()) {
out += "polarFinish";
}
if (azimuthStartChanged()) {
out += "azimuthStart";
}
if (azimuthFinishChanged()) {
out += "azimuthFinish";
}
if (emitAccelerationChanged()) {
out += "emitAcceleration";
}
if (accelerationSpreadChanged()) {
out += "accelerationSpread";
}
if (particleRadiusChanged()) {
out += "particleRadius";
}
if (radiusSpreadChanged()) {
out += "radiusSpread";
}
if (radiusStartChanged()) {
out += "radiusStart";
}
if (radiusFinishChanged()) {
out += "radiusFinish";
}
if (marketplaceIDChanged()) {
out += "marketplaceID";
}
if (backgroundModeChanged()) {
out += "backgroundMode";
}
if (voxelVolumeSizeChanged()) {
out += "voxelVolumeSize";
}
if (voxelDataChanged()) {
out += "voxelData";
}
if (voxelSurfaceStyleChanged()) {
out += "voxelSurfaceStyle";
}
if (hrefChanged()) {
out += "href";
}
if (descriptionChanged()) {
out += "description";
}
if (actionDataChanged()) {
out += "actionData";
}
if (xTextureURLChanged()) {
out += "xTextureURL";
}
if (yTextureURLChanged()) {
out += "yTextureURL";
}
if (zTextureURLChanged()) {
out += "zTextureURL";
}
if (xNNeighborIDChanged()) {
out += "xNNeighborID";
}
if (yNNeighborIDChanged()) {
out += "yNNeighborID";
}
if (zNNeighborIDChanged()) {
out += "zNNeighborID";
}
if (xPNeighborIDChanged()) {
out += "xPNeighborID";
}
if (yPNeighborIDChanged()) {
out += "yPNeighborID";
}
if (zPNeighborIDChanged()) {
out += "zPNeighborID";
}
getAnimation().listChangedProperties(out);
getAtmosphere().listChangedProperties(out);
getSkybox().listChangedProperties(out);
getStage().listChangedProperties(out);
return out;
}

View file

@ -253,6 +253,11 @@ public:
void setActionDataDirty() { _actionDataChanged = true; }
QList<QString> listChangedProperties();
bool getDimensionsInitialized() const { return _dimensionsInitialized; }
void setDimensionsInitialized(bool dimensionsInitialized) { _dimensionsInitialized = dimensionsInitialized; }
private:
QUuid _id;
bool _idSet;
@ -265,6 +270,7 @@ private:
bool _glowLevelChanged;
bool _localRenderAlphaChanged;
bool _defaultSettings;
bool _dimensionsInitialized = false; // Only true if creating an entity localy with no dimensions properties
// NOTE: The following are pseudo client only properties. They are only used in clients which can access
// properties of model geometry. But these properties are not serialized like other properties.
@ -370,7 +376,9 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) {
DEBUG_PROPERTY_IF_CHANGED(debug, properties, VoxelSurfaceStyle, voxelSurfaceStyle, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, Href, href, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, Description, description, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, ActionData, actionData, "");
if (properties.actionDataChanged()) {
debug << " " << "actionData" << ":" << properties.getActionData().toHex() << "" << "\n";
}
DEBUG_PROPERTY_IF_CHANGED(debug, properties, XTextureURL, xTextureURL, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, YTextureURL, yTextureURL, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, ZTextureURL, zTextureURL, "");

View file

@ -65,8 +65,8 @@ void EntityScriptingInterface::setEntityTree(EntityTreePointer elementTree) {
}
QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties) {
EntityItemProperties propertiesWithSimID = properties;
propertiesWithSimID.setDimensionsInitialized(properties.dimensionsChanged());
EntityItemID id = EntityItemID(QUuid::createUuid());
@ -279,17 +279,19 @@ QVector<QUuid> EntityScriptingInterface::findEntitiesInBox(const glm::vec3& corn
return result;
}
RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersection(const PickRay& ray, bool precisionPicking) {
return findRayIntersectionWorker(ray, Octree::TryLock, precisionPicking);
RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersection(const PickRay& ray, bool precisionPicking, const QScriptValue& entityIdsToInclude) {
QVector<QUuid> entities = qVectorQUuidFromScriptValue(entityIdsToInclude);
return findRayIntersectionWorker(ray, Octree::TryLock, precisionPicking, entities);
}
RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionBlocking(const PickRay& ray, bool precisionPicking) {
return findRayIntersectionWorker(ray, Octree::Lock, precisionPicking);
RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionBlocking(const PickRay& ray, bool precisionPicking, const QScriptValue& entityIdsToInclude) {
const QVector<QUuid>& entities = qVectorQUuidFromScriptValue(entityIdsToInclude);
return findRayIntersectionWorker(ray, Octree::Lock, precisionPicking, entities);
}
RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionWorker(const PickRay& ray,
Octree::lockType lockType,
bool precisionPicking) {
bool precisionPicking, const QVector<QUuid>& entityIdsToInclude) {
RayToEntityIntersectionResult result;
@ -297,7 +299,7 @@ RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionWorke
OctreeElementPointer element;
EntityItemPointer intersectedEntity = NULL;
result.intersects = _entityTree->findRayIntersection(ray.origin, ray.direction, element, result.distance, result.face,
result.surfaceNormal, (void**)&intersectedEntity, lockType, &result.accurate,
result.surfaceNormal, entityIdsToInclude, (void**)&intersectedEntity, lockType, &result.accurate,
precisionPicking);
if (result.intersects && intersectedEntity) {
result.entityID = intersectedEntity->getEntityItemID();

View file

@ -111,11 +111,11 @@ public slots:
/// If the scripting context has visible entities, this will determine a ray intersection, the results
/// may be inaccurate if the engine is unable to access the visible entities, in which case result.accurate
/// will be false.
Q_INVOKABLE RayToEntityIntersectionResult findRayIntersection(const PickRay& ray, bool precisionPicking = false);
Q_INVOKABLE RayToEntityIntersectionResult findRayIntersection(const PickRay& ray, bool precisionPicking = false, const QScriptValue& entityIdsToInclude = QScriptValue());
/// If the scripting context has visible entities, this will determine a ray intersection, and will block in
/// order to return an accurate result
Q_INVOKABLE RayToEntityIntersectionResult findRayIntersectionBlocking(const PickRay& ray, bool precisionPicking = false);
Q_INVOKABLE RayToEntityIntersectionResult findRayIntersectionBlocking(const PickRay& ray, bool precisionPicking = false, const QScriptValue& entityIdsToInclude = QScriptValue());
Q_INVOKABLE void setLightsArePickable(bool value);
Q_INVOKABLE bool getLightsArePickable() const;
@ -186,7 +186,7 @@ private:
/// actually does the work of finding the ray intersection, can be called in locking mode or tryLock mode
RayToEntityIntersectionResult findRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType,
bool precisionPicking);
bool precisionPicking, const QVector<QUuid>& entityIdsToInclude);
EntityTreePointer _entityTree;
EntitiesScriptEngineProvider* _entitiesScriptEngine = nullptr;

View file

@ -198,7 +198,7 @@ void EntitySimulation::changeEntity(EntityItemPointer entity) {
// we must check for that case here, however we rely on the change event to have set DIRTY_POSITION flag.
bool wasRemoved = false;
uint32_t dirtyFlags = entity->getDirtyFlags();
if (dirtyFlags & EntityItem::DIRTY_POSITION) {
if (dirtyFlags & Simulation::DIRTY_POSITION) {
AACube domainBounds(glm::vec3((float)-HALF_TREE_SCALE), (float)TREE_SCALE);
AACube newCube = entity->getMaximumAACube();
if (!domainBounds.touches(newCube)) {
@ -214,7 +214,7 @@ void EntitySimulation::changeEntity(EntityItemPointer entity) {
}
}
if (!wasRemoved) {
if (dirtyFlags & EntityItem::DIRTY_LIFETIME) {
if (dirtyFlags & Simulation::DIRTY_LIFETIME) {
if (entity->isMortal()) {
_mortalEntities.insert(entity);
quint64 expiry = entity->getExpiry();
@ -224,7 +224,7 @@ void EntitySimulation::changeEntity(EntityItemPointer entity) {
} else {
_mortalEntities.remove(entity);
}
entity->clearDirtyFlags(EntityItem::DIRTY_LIFETIME);
entity->clearDirtyFlags(Simulation::DIRTY_LIFETIME);
}
if (entity->needsToCallUpdate()) {
_entitiesToUpdate.insert(entity);

View file

@ -28,18 +28,18 @@ typedef QVector<EntityItemPointer> VectorOfEntities;
// the EntitySimulation needs to know when these things change on an entity,
// so it can sort EntityItem or relay its state to the PhysicsEngine.
const int DIRTY_SIMULATION_FLAGS =
EntityItem::DIRTY_POSITION |
EntityItem::DIRTY_ROTATION |
EntityItem::DIRTY_LINEAR_VELOCITY |
EntityItem::DIRTY_ANGULAR_VELOCITY |
EntityItem::DIRTY_MASS |
EntityItem::DIRTY_COLLISION_GROUP |
EntityItem::DIRTY_MOTION_TYPE |
EntityItem::DIRTY_SHAPE |
EntityItem::DIRTY_LIFETIME |
EntityItem::DIRTY_UPDATEABLE |
EntityItem::DIRTY_MATERIAL |
EntityItem::DIRTY_SIMULATOR_ID;
Simulation::DIRTY_POSITION |
Simulation::DIRTY_ROTATION |
Simulation::DIRTY_LINEAR_VELOCITY |
Simulation::DIRTY_ANGULAR_VELOCITY |
Simulation::DIRTY_MASS |
Simulation::DIRTY_COLLISION_GROUP |
Simulation::DIRTY_MOTION_TYPE |
Simulation::DIRTY_SHAPE |
Simulation::DIRTY_LIFETIME |
Simulation::DIRTY_UPDATEABLE |
Simulation::DIRTY_MATERIAL |
Simulation::DIRTY_SIMULATOR_ID;
class EntitySimulation : public QObject {
Q_OBJECT

View file

@ -613,6 +613,9 @@ int EntityTree::processEditPacketData(NLPacket& packet, const unsigned char* edi
qCDebug(entities) << "User [" << senderNode->getUUID() << "] editing entity. ID:" << entityItemID;
qCDebug(entities) << " properties:" << properties;
}
if (wantTerseEditLogging()) {
qCDebug(entities) << "edit" << entityItemID.toString() << properties.listChangedProperties();
}
endLogging = usecTimestampNow();
startUpdate = usecTimestampNow();
@ -638,6 +641,9 @@ int EntityTree::processEditPacketData(NLPacket& packet, const unsigned char* edi
<< newEntity->getEntityItemID();
qCDebug(entities) << " properties:" << properties;
}
if (wantTerseEditLogging()) {
qCDebug(entities) << "add" << entityItemID.toString() << properties.listChangedProperties();
}
endLogging = usecTimestampNow();
}
@ -647,8 +653,10 @@ int EntityTree::processEditPacketData(NLPacket& packet, const unsigned char* edi
}
} else {
static QString repeatedMessage =
LogHandler::getInstance().addRepeatedMessageRegex("^Add or Edit failed.*");
qCDebug(entities) << "Add or Edit failed." << packet.getType() << existingEntity.get();
LogHandler::getInstance().addRepeatedMessageRegex("^Edit failed.*");
qCDebug(entities) << "Edit failed. [" << packet.getType() <<"] " <<
"entity id:" << entityItemID <<
"existingEntity pointer:" << existingEntity.get();
}
}
@ -869,7 +877,7 @@ int EntityTree::processEraseMessage(NLPacket& packet, const SharedNodePointer& s
EntityItemID entityItemID(entityID);
entityItemIDsToDelete << entityItemID;
if (wantEditLogging()) {
if (wantEditLogging() || wantTerseEditLogging()) {
qCDebug(entities) << "User [" << sourceNode->getUUID() << "] deleting entity. ID:" << entityItemID;
}
@ -913,7 +921,7 @@ int EntityTree::processEraseMessageDetails(const QByteArray& dataByteArray, cons
EntityItemID entityItemID(entityID);
entityItemIDsToDelete << entityItemID;
if (wantEditLogging()) {
if (wantEditLogging() || wantTerseEditLogging()) {
qCDebug(entities) << "User [" << sourceNode->getUUID() << "] deleting entity. ID:" << entityItemID;
}

View file

@ -175,6 +175,9 @@ public:
bool wantEditLogging() const { return _wantEditLogging; }
void setWantEditLogging(bool value) { _wantEditLogging = value; }
bool wantTerseEditLogging() const { return _wantTerseEditLogging; }
void setWantTerseEditLogging(bool value) { _wantTerseEditLogging = value; }
bool writeToMap(QVariantMap& entityDescription, OctreeElementPointer element, bool skipDefaultValues);
bool readFromMap(QVariantMap& entityDescription);
@ -240,6 +243,7 @@ private:
EntitySimulation* _simulation;
bool _wantEditLogging = false;
bool _wantTerseEditLogging = false;
void maybeNotifyNewCollisionSoundURL(const QString& oldCollisionSoundURL, const QString& newCollisionSoundURL);

View file

@ -495,12 +495,16 @@ bool EntityTreeElement::bestFitBounds(const glm::vec3& minPoint, const glm::vec3
bool EntityTreeElement::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, bool& keepSearching,
OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal,
void** intersectedObject, bool precisionPicking, float distanceToElementCube) {
const QVector<QUuid>& entityIdsToInclude, void** intersectedObject, bool precisionPicking, float distanceToElementCube) {
// only called if we do intersect our bounding cube, but find if we actually intersect with entities...
int entityNumber = 0;
bool somethingIntersected = false;
forEachEntity([&](EntityItemPointer entity) {
if (entityIdsToInclude.size() > 0 && !entityIdsToInclude.contains(entity->getID())) {
return;
}
AABox entityBox = entity->getAABox();
float localDistance;
BoxFace localFace;

View file

@ -144,7 +144,7 @@ public:
virtual bool canRayIntersect() const { return hasEntities(); }
virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
bool& keepSearching, OctreeElementPointer& element, float& distance,
BoxFace& face, glm::vec3& surfaceNormal,
BoxFace& face, glm::vec3& surfaceNormal, const QVector<QUuid>& entityIdsToInclude,
void** intersectedObject, bool precisionPicking, float distanceToElementCube);
virtual bool findSpherePenetration(const glm::vec3& center, float radius,

View file

@ -88,7 +88,9 @@ EntityItemPointer EntityTypes::constructEntityItem(EntityType entityType, const
factory = _factories[entityType];
}
if (factory) {
newEntityItem = factory(entityID, properties);
auto mutableProperties = properties;
mutableProperties.markAllChanged();
newEntityItem = factory(entityID, mutableProperties);
}
return newEntityItem;
}

View file

@ -66,7 +66,7 @@ bool ModelEntityItem::setProperties(const EntityItemProperties& properties) {
bool somethingChangedInAnimations = _animationProperties.setProperties(properties);
if (somethingChangedInAnimations) {
_dirtyFlags |= EntityItem::DIRTY_UPDATEABLE;
_dirtyFlags |= Simulation::DIRTY_UPDATEABLE;
}
somethingChanged = somethingChanged || somethingChangedInAnimations;
@ -128,7 +128,7 @@ int ModelEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
READ_ENTITY_PROPERTY(PROP_SHAPE_TYPE, ShapeType, updateShapeType);
if (animationPropertiesChanged) {
_dirtyFlags |= EntityItem::DIRTY_UPDATEABLE;
_dirtyFlags |= Simulation::DIRTY_UPDATEABLE;
somethingChanged = true;
}
@ -300,7 +300,7 @@ void ModelEntityItem::updateShapeType(ShapeType type) {
if (type != _shapeType) {
_shapeType = type;
_dirtyFlags |= EntityItem::DIRTY_SHAPE | EntityItem::DIRTY_MASS;
_dirtyFlags |= Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS;
}
}
@ -316,13 +316,13 @@ ShapeType ModelEntityItem::getShapeType() const {
void ModelEntityItem::setCompoundShapeURL(const QString& url) {
if (_compoundShapeURL != url) {
_compoundShapeURL = url;
_dirtyFlags |= EntityItem::DIRTY_SHAPE | EntityItem::DIRTY_MASS;
_dirtyFlags |= Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS;
_shapeType = _compoundShapeURL.isEmpty() ? SHAPE_TYPE_NONE : SHAPE_TYPE_COMPOUND;
}
}
void ModelEntityItem::setAnimationURL(const QString& url) {
_dirtyFlags |= EntityItem::DIRTY_UPDATEABLE;
_dirtyFlags |= Simulation::DIRTY_UPDATEABLE;
_animationProperties.setURL(url);
}
@ -388,16 +388,16 @@ void ModelEntityItem::setAnimationSettings(const QString& value) {
setAnimationStartAutomatically(startAutomatically);
}
_dirtyFlags |= EntityItem::DIRTY_UPDATEABLE;
_dirtyFlags |= Simulation::DIRTY_UPDATEABLE;
}
void ModelEntityItem::setAnimationIsPlaying(bool value) {
_dirtyFlags |= EntityItem::DIRTY_UPDATEABLE;
_dirtyFlags |= Simulation::DIRTY_UPDATEABLE;
_animationLoop.setRunning(value);
}
void ModelEntityItem::setAnimationFPS(float value) {
_dirtyFlags |= EntityItem::DIRTY_UPDATEABLE;
_dirtyFlags |= Simulation::DIRTY_UPDATEABLE;
_animationLoop.setFPS(value);
}

View file

@ -597,7 +597,7 @@ void ParticleEffectEntityItem::debugDump() const {
void ParticleEffectEntityItem::updateShapeType(ShapeType type) {
if (type != _shapeType) {
_shapeType = type;
_dirtyFlags |= EntityItem::DIRTY_SHAPE | EntityItem::DIRTY_MASS;
_dirtyFlags |= Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS;
}
}

View file

@ -57,6 +57,7 @@ public:
virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const = 0;
virtual void copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) = 0;
virtual void debugDump() const { }
virtual void listChangedProperties(QList<QString>& out) { }
virtual bool appendToEditPacket(OctreePacketData* packetData,
EntityPropertyFlags& requestedProperties,

View file

@ -0,0 +1,34 @@
//
// SimulationFlags.h
// libraries/physics/src
//
// Created by Andrew Meadows 2015.10.14
// 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_SimulationFlags_h
#define hifi_SimulationFlags_h
namespace Simulation {
const uint32_t DIRTY_POSITION = 0x0001;
const uint32_t DIRTY_ROTATION = 0x0002;
const uint32_t DIRTY_LINEAR_VELOCITY = 0x0004;
const uint32_t DIRTY_ANGULAR_VELOCITY = 0x0008;
const uint32_t DIRTY_MASS = 0x0010;
const uint32_t DIRTY_COLLISION_GROUP = 0x0020;
const uint32_t DIRTY_MOTION_TYPE = 0x0040;
const uint32_t DIRTY_SHAPE = 0x0080;
const uint32_t DIRTY_LIFETIME = 0x0100;
const uint32_t DIRTY_UPDATEABLE = 0x0200;
const uint32_t DIRTY_MATERIAL = 0x00400;
const uint32_t DIRTY_PHYSICS_ACTIVATION = 0x0800; // should activate object in physics engine
const uint32_t DIRTY_SIMULATOR_OWNERSHIP = 0x1000; // should claim simulator ownership
const uint32_t DIRTY_SIMULATOR_ID = 0x2000; // the simulatorID has changed
const uint32_t DIRTY_TRANSFORM = DIRTY_POSITION | DIRTY_ROTATION;
const uint32_t DIRTY_VELOCITIES = DIRTY_LINEAR_VELOCITY | DIRTY_ANGULAR_VELOCITY;
};
#endif // hifi_SimulationFlags_h

View file

@ -33,6 +33,15 @@ void SkyboxPropertyGroup::debugDump() const {
qDebug() << " URL:" << getURL() << " has changed:" << urlChanged();
}
void SkyboxPropertyGroup::listChangedProperties(QList<QString>& out) {
if (colorChanged()) {
out << "skybox-color";
}
if (urlChanged()) {
out << "skybox-url";
}
}
bool SkyboxPropertyGroup::appendToEditPacket(OctreePacketData* packetData,
EntityPropertyFlags& requestedProperties,
EntityPropertyFlags& propertyFlags,

View file

@ -33,6 +33,7 @@ public:
virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const;
virtual void copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings);
virtual void debugDump() const;
virtual void listChangedProperties(QList<QString>& out);
virtual bool appendToEditPacket(OctreePacketData* packetData,
EntityPropertyFlags& requestedProperties,

View file

@ -66,6 +66,27 @@ void StagePropertyGroup::debugDump() const {
qDebug() << " _automaticHourDay:" << _automaticHourDay;
}
void StagePropertyGroup::listChangedProperties(QList<QString>& out) {
if (sunModelEnabledChanged()) {
out << "stage-sunModelEnabled";
}
if (latitudeChanged()) {
out << "stage-latitude";
}
if (altitudeChanged()) {
out << "stage-altitude";
}
if (dayChanged()) {
out << "stage-day";
}
if (hourChanged()) {
out << "stage-hour";
}
if (automaticHourDayChanged()) {
out << "stage-automaticHourDay";
}
}
bool StagePropertyGroup::appendToEditPacket(OctreePacketData* packetData,
EntityPropertyFlags& requestedProperties,
EntityPropertyFlags& propertyFlags,

View file

@ -33,6 +33,7 @@ public:
virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const;
virtual void copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings);
virtual void debugDump() const;
virtual void listChangedProperties(QList<QString>& out);
virtual bool appendToEditPacket(OctreePacketData* packetData,
EntityPropertyFlags& requestedProperties,

View file

@ -143,22 +143,3 @@ void Joystick::registerToUserInputMapper(UserInputMapper& mapper) {
mapper.registerDevice(_deviceID, proxy);
}
void Joystick::assignDefaultInputMapping(UserInputMapper& mapper) {
#ifdef HAVE_SDL2
const float JOYSTICK_MOVE_SPEED = 1.0f;
const float DPAD_MOVE_SPEED = 0.5f;
const float JOYSTICK_YAW_SPEED = 0.5f;
const float JOYSTICK_PITCH_SPEED = 0.25f;
const float BOOM_SPEED = 0.1f;
#endif
}
UserInputMapper::Input Joystick::makeInput(controller::StandardButtonChannel button) {
return UserInputMapper::Input(_deviceID, button, UserInputMapper::ChannelType::BUTTON);
}
UserInputMapper::Input Joystick::makeInput(controller::StandardAxisChannel axis) {
return UserInputMapper::Input(_deviceID, axis, UserInputMapper::ChannelType::AXIS);
}

View file

@ -37,16 +37,12 @@ public:
// Device functions
virtual void registerToUserInputMapper(UserInputMapper& mapper) override;
virtual void assignDefaultInputMapping(UserInputMapper& mapper) override;
virtual void update(float deltaTime, bool jointsCaptured) override;
virtual void focusOutEvent() override;
Joystick() : InputDevice("Joystick") {}
~Joystick();
UserInputMapper::Input makeInput(controller::StandardButtonChannel button);
UserInputMapper::Input makeInput(controller::StandardAxisChannel axis);
#ifdef HAVE_SDL2
Joystick(SDL_JoystickID instanceId, const QString& name, SDL_GameController* sdlGameController);
#endif

View file

@ -123,6 +123,8 @@ void SixenseManager::activate() {
loadSettings();
sixenseInit();
_activated = true;
auto userInputMapper = DependencyManager::get<UserInputMapper>();
registerToUserInputMapper(*userInputMapper);
#endif
}
@ -176,23 +178,13 @@ void SixenseManager::update(float deltaTime, bool jointsCaptured) {
auto userInputMapper = DependencyManager::get<UserInputMapper>();
if (sixenseGetNumActiveControllers() == 0) {
if (_hydrasConnected) {
qCDebug(inputplugins, "hydra disconnected");
}
_hydrasConnected = false;
if (_deviceID != 0) {
userInputMapper->removeDevice(_deviceID);
_deviceID = 0;
_poseStateMap.clear();
}
_poseStateMap.clear();
return;
}
PerformanceTimer perfTimer("sixense");
if (!_hydrasConnected) {
_hydrasConnected = true;
registerToUserInputMapper(*userInputMapper);
assignDefaultInputMapping(*userInputMapper);
UserActivityLogger::getInstance().connectedDevice("spatial_controller", "hydra");
}
@ -226,12 +218,15 @@ void SixenseManager::update(float deltaTime, bool jointsCaptured) {
// NOTE: Sixense API returns pos data in millimeters but we IMMEDIATELY convert to meters.
glm::vec3 position(data->pos[0], data->pos[1], data->pos[2]);
position *= METERS_PER_MILLIMETER;
bool left = i == 0;
using namespace controller;
// Check to see if this hand/controller is on the base
const float CONTROLLER_AT_BASE_DISTANCE = 0.075f;
if (glm::length(position) >= CONTROLLER_AT_BASE_DISTANCE) {
handleButtonEvent(data->buttons, numActiveControllers - 1);
handleAxisEvent(data->joystick_x, data->joystick_y, data->trigger, numActiveControllers - 1);
handleButtonEvent(data->buttons, left);
_axisStateMap[left ? LX : RX] = data->joystick_x;
_axisStateMap[left ? LY : RY] = data->joystick_y;
_axisStateMap[left ? LT : RT] = data->trigger;
if (!jointsCaptured) {
// Rotation of Palm
@ -241,13 +236,8 @@ void SixenseManager::update(float deltaTime, bool jointsCaptured) {
_poseStateMap.clear();
}
} else {
_poseStateMap[(numActiveControllers - 1) == 0 ? LEFT_HAND : RIGHT_HAND] = UserInputMapper::PoseValue();
_poseStateMap[left ? controller::StandardPoseChannel::LEFT : controller::StandardPoseChannel::RIGHT] = UserInputMapper::PoseValue();
}
// // Read controller buttons and joystick into the hand
// palm->setControllerButtons(data->buttons);
// palm->setTrigger(data->trigger);
// palm->setJoystick(data->joystick_x, data->joystick_y);
}
if (numActiveControllers == 2) {
@ -367,39 +357,35 @@ void SixenseManager::focusOutEvent() {
_buttonPressedMap.clear();
};
void SixenseManager::handleAxisEvent(float stickX, float stickY, float trigger, int index) {
_axisStateMap[makeInput(AXIS_Y_POS, index).getChannel()] = (stickY > 0.0f) ? stickY : 0.0f;
_axisStateMap[makeInput(AXIS_Y_NEG, index).getChannel()] = (stickY < 0.0f) ? -stickY : 0.0f;
_axisStateMap[makeInput(AXIS_X_POS, index).getChannel()] = (stickX > 0.0f) ? stickX : 0.0f;
_axisStateMap[makeInput(AXIS_X_NEG, index).getChannel()] = (stickX < 0.0f) ? -stickX : 0.0f;
_axisStateMap[makeInput(BACK_TRIGGER, index).getChannel()] = trigger;
void SixenseManager::handleAxisEvent(float stickX, float stickY, float trigger, bool left) {
}
void SixenseManager::handleButtonEvent(unsigned int buttons, int index) {
void SixenseManager::handleButtonEvent(unsigned int buttons, bool left) {
using namespace controller;
if (buttons & BUTTON_0) {
_buttonPressedMap.insert(makeInput(BUTTON_0, index).getChannel());
_buttonPressedMap.insert(left ? BACK : START);
}
if (buttons & BUTTON_1) {
_buttonPressedMap.insert(makeInput(BUTTON_1, index).getChannel());
_buttonPressedMap.insert(left ? DL : X);
}
if (buttons & BUTTON_2) {
_buttonPressedMap.insert(makeInput(BUTTON_2, index).getChannel());
_buttonPressedMap.insert(left ? DD : A);
}
if (buttons & BUTTON_3) {
_buttonPressedMap.insert(makeInput(BUTTON_3, index).getChannel());
_buttonPressedMap.insert(left ? DR : B);
}
if (buttons & BUTTON_4) {
_buttonPressedMap.insert(makeInput(BUTTON_4, index).getChannel());
_buttonPressedMap.insert(left ? DU : Y);
}
if (buttons & BUTTON_FWD) {
_buttonPressedMap.insert(makeInput(BUTTON_FWD, index).getChannel());
_buttonPressedMap.insert(left ? LB : RB);
}
if (buttons & BUTTON_TRIGGER) {
_buttonPressedMap.insert(makeInput(BUTTON_TRIGGER, index).getChannel());
_buttonPressedMap.insert(left ? LS : RS);
}
}
void SixenseManager::handlePoseEvent(glm::vec3 position, glm::quat rotation, int index) {
void SixenseManager::handlePoseEvent(glm::vec3 position, glm::quat rotation, bool left) {
#ifdef HAVE_SIXENSE
// From ABOVE the sixense coordinate frame looks like this:
//
@ -443,7 +429,7 @@ void SixenseManager::handlePoseEvent(glm::vec3 position, glm::quat rotation, int
// In addition to Qsh each hand has pre-offset introduced by the shape of the sixense controllers
// and how they fit into the hand in their relaxed state. This offset is a quarter turn about
// the sixense's z-axis, with its direction different for the two hands:
float sign = (index == 0) ? 1.0f : -1.0f;
float sign = left ? 1.0f : -1.0f;
const glm::quat preOffset = glm::angleAxis(sign * PI / 2.0f, Vectors::UNIT_Z);
// Finally, there is a post-offset (same for both hands) to get the hand's rest orientation
@ -458,104 +444,46 @@ void SixenseManager::handlePoseEvent(glm::vec3 position, glm::quat rotation, int
// TODO: find a shortcut with fewer rotations.
rotation = _avatarRotation * postOffset * glm::inverse(sixenseToHand) * rotation * preOffset * sixenseToHand;
_poseStateMap[makeInput(JointChannel(index)).getChannel()] = UserInputMapper::PoseValue(position, rotation);
_poseStateMap[left ? controller::StandardPoseChannel::LEFT : controller::StandardPoseChannel::RIGHT] =
UserInputMapper::PoseValue(position, rotation);
#endif // HAVE_SIXENSE
}
void SixenseManager::registerToUserInputMapper(UserInputMapper& mapper) {
// Grab the current free device ID
_deviceID = mapper.getFreeDeviceID();
auto proxy = std::make_shared<UserInputMapper::DeviceProxy>(_name);
proxy->getButton = [this] (const UserInputMapper::Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); };
proxy->getAxis = [this] (const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); };
proxy->getPose = [this](const UserInputMapper::Input& input, int timestamp) -> UserInputMapper::PoseValue { return this->getPose(input.getChannel()); };
proxy->getAvailabeInputs = [this] () -> QVector<UserInputMapper::InputPair> {
using namespace controller;
proxy->getAvailabeInputs = [this]() -> QVector<UserInputMapper::InputPair> {
QVector<UserInputMapper::InputPair> availableInputs;
availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_0, 0), "Left Start"));
availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_1, 0), "Left Button 1"));
availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_2, 0), "Left Button 2"));
availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_3, 0), "Left Button 3"));
availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_4, 0), "Left Button 4"));
availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_FWD, 0), "L1"));
availableInputs.append(UserInputMapper::InputPair(makeInput(BACK_TRIGGER, 0), "L2"));
availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_POS, 0), "Left Stick Up"));
availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_NEG, 0), "Left Stick Down"));
availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_X_POS, 0), "Left Stick Right"));
availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_X_NEG, 0), "Left Stick Left"));
availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_TRIGGER, 0), "Left Trigger Press"));
availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_0, 1), "Right Start"));
availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_1, 1), "Right Button 1"));
availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_2, 1), "Right Button 2"));
availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_3, 1), "Right Button 3"));
availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_4, 1), "Right Button 4"));
availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_FWD, 1), "R1"));
availableInputs.append(UserInputMapper::InputPair(makeInput(BACK_TRIGGER, 1), "R2"));
availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_POS, 1), "Right Stick Up"));
availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_NEG, 1), "Right Stick Down"));
availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_X_POS, 1), "Right Stick Right"));
availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_X_NEG, 1), "Right Stick Left"));
availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_TRIGGER, 1), "Right Trigger Press"));
availableInputs.append(UserInputMapper::InputPair(makeInput(BACK), "L0"));
availableInputs.append(UserInputMapper::InputPair(makeInput(DL), "L1"));
availableInputs.append(UserInputMapper::InputPair(makeInput(DD), "L2"));
availableInputs.append(UserInputMapper::InputPair(makeInput(DR), "L3"));
availableInputs.append(UserInputMapper::InputPair(makeInput(DU), "L4"));
availableInputs.append(UserInputMapper::InputPair(makeInput(LB), "LB"));
availableInputs.append(UserInputMapper::InputPair(makeInput(LS), "LS"));
availableInputs.append(UserInputMapper::InputPair(makeInput(LX), "LX"));
availableInputs.append(UserInputMapper::InputPair(makeInput(LY), "LY"));
availableInputs.append(UserInputMapper::InputPair(makeInput(LT), "LT"));
availableInputs.append(UserInputMapper::InputPair(makeInput(START), "R0"));
availableInputs.append(UserInputMapper::InputPair(makeInput(X), "R1"));
availableInputs.append(UserInputMapper::InputPair(makeInput(A), "R2"));
availableInputs.append(UserInputMapper::InputPair(makeInput(B), "R3"));
availableInputs.append(UserInputMapper::InputPair(makeInput(Y), "R4"));
availableInputs.append(UserInputMapper::InputPair(makeInput(RB), "RB"));
availableInputs.append(UserInputMapper::InputPair(makeInput(RS), "RS"));
availableInputs.append(UserInputMapper::InputPair(makeInput(RX), "RX"));
availableInputs.append(UserInputMapper::InputPair(makeInput(RY), "RY"));
availableInputs.append(UserInputMapper::InputPair(makeInput(RT), "RT"));
return availableInputs;
};
proxy->resetDeviceBindings = [this, &mapper] () -> bool {
mapper.removeAllInputChannelsForDevice(_deviceID);
this->assignDefaultInputMapping(mapper);
return true;
};
mapper.registerDevice(_deviceID, proxy);
}
void SixenseManager::assignDefaultInputMapping(UserInputMapper& mapper) {
const float JOYSTICK_MOVE_SPEED = 1.0f;
const float JOYSTICK_YAW_SPEED = 0.5f;
const float JOYSTICK_PITCH_SPEED = 0.25f;
const float BUTTON_MOVE_SPEED = 1.0f;
const float BOOM_SPEED = 0.1f;
// Left Joystick: Movement, strafing
mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(AXIS_Y_POS, 0), JOYSTICK_MOVE_SPEED);
mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(AXIS_Y_NEG, 0), JOYSTICK_MOVE_SPEED);
mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(AXIS_X_POS, 0), JOYSTICK_MOVE_SPEED);
mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(AXIS_X_NEG, 0), JOYSTICK_MOVE_SPEED);
// Right Joystick: Camera orientation
mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(AXIS_X_POS, 1), JOYSTICK_YAW_SPEED);
mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(AXIS_X_NEG, 1), JOYSTICK_YAW_SPEED);
mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(AXIS_Y_POS, 1), JOYSTICK_PITCH_SPEED);
mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(AXIS_Y_NEG, 1), JOYSTICK_PITCH_SPEED);
// Buttons
mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(BUTTON_3, 0), BOOM_SPEED);
mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(BUTTON_1, 0), BOOM_SPEED);
mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(BUTTON_3, 1), BUTTON_MOVE_SPEED);
mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(BUTTON_1, 1), BUTTON_MOVE_SPEED);
mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(BUTTON_2, 0));
mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(BUTTON_2, 1));
mapper.addInputChannel(UserInputMapper::ACTION1, makeInput(BUTTON_4, 0));
mapper.addInputChannel(UserInputMapper::ACTION2, makeInput(BUTTON_4, 1));
mapper.addInputChannel(UserInputMapper::LEFT_HAND, makeInput(LEFT_HAND));
mapper.addInputChannel(UserInputMapper::RIGHT_HAND, makeInput(RIGHT_HAND));
mapper.addInputChannel(UserInputMapper::LEFT_HAND_CLICK, makeInput(BACK_TRIGGER, 0));
mapper.addInputChannel(UserInputMapper::RIGHT_HAND_CLICK, makeInput(BACK_TRIGGER, 1));
// TODO find a mechanism to allow users to navigate the context menu via
mapper.addInputChannel(UserInputMapper::CONTEXT_MENU, makeInput(BUTTON_0, 0));
mapper.addInputChannel(UserInputMapper::TOGGLE_MUTE, makeInput(BUTTON_0, 1));
}
// virtual
void SixenseManager::saveSettings() const {
Settings settings;
@ -580,15 +508,3 @@ void SixenseManager::loadSettings() {
}
settings.endGroup();
}
UserInputMapper::Input SixenseManager::makeInput(unsigned int button, int index) {
return UserInputMapper::Input(_deviceID, button | (index == 0 ? LEFT_MASK : RIGHT_MASK), UserInputMapper::ChannelType::BUTTON);
}
UserInputMapper::Input SixenseManager::makeInput(SixenseManager::JoystickAxisChannel axis, int index) {
return UserInputMapper::Input(_deviceID, axis | (index == 0 ? LEFT_MASK : RIGHT_MASK), UserInputMapper::ChannelType::AXIS);
}
UserInputMapper::Input SixenseManager::makeInput(JointChannel joint) {
return UserInputMapper::Input(_deviceID, joint, UserInputMapper::ChannelType::POSE);
}

View file

@ -25,6 +25,7 @@
#endif
#include <controllers/InputDevice.h>
#include <controllers/StandardControls.h>
#include "InputPlugin.h"
@ -44,19 +45,6 @@ const bool DEFAULT_INVERT_SIXENSE_MOUSE_BUTTONS = false;
class SixenseManager : public InputPlugin, public InputDevice {
Q_OBJECT
public:
enum JoystickAxisChannel {
AXIS_Y_POS = 1U << 0,
AXIS_Y_NEG = 1U << 3,
AXIS_X_POS = 1U << 4,
AXIS_X_NEG = 1U << 5,
BACK_TRIGGER = 1U << 6,
};
enum JointChannel {
LEFT_HAND = 0,
RIGHT_HAND,
};
SixenseManager();
// Plugin functions
@ -73,14 +61,9 @@ public:
// Device functions
virtual void registerToUserInputMapper(UserInputMapper& mapper) override;
virtual void assignDefaultInputMapping(UserInputMapper& mapper) override;
virtual void update(float deltaTime, bool jointsCaptured) override;
virtual void focusOutEvent() override;
UserInputMapper::Input makeInput(unsigned int button, int index);
UserInputMapper::Input makeInput(JoystickAxisChannel axis, int index);
UserInputMapper::Input makeInput(JointChannel joint);
virtual void saveSettings() const override;
virtual void loadSettings() override;
@ -88,9 +71,9 @@ public slots:
void setSixenseFilter(bool filter);
private:
void handleButtonEvent(unsigned int buttons, int index);
void handleAxisEvent(float x, float y, float trigger, int index);
void handlePoseEvent(glm::vec3 position, glm::quat rotation, int index);
void handleButtonEvent(unsigned int buttons, bool left);
void handleAxisEvent(float x, float y, float trigger, bool left);
void handlePoseEvent(glm::vec3 position, glm::quat rotation, bool left);
void updateCalibration(void* controllers);

View file

@ -703,6 +703,7 @@ public:
float& distance;
BoxFace& face;
glm::vec3& surfaceNormal;
const QVector<QUuid>& entityIdsToInclude;
void** intersectedObject;
bool found;
bool precisionPicking;
@ -712,7 +713,7 @@ bool findRayIntersectionOp(OctreeElementPointer element, void* extraData) {
RayArgs* args = static_cast<RayArgs*>(extraData);
bool keepSearching = true;
if (element->findRayIntersection(args->origin, args->direction, keepSearching,
args->element, args->distance, args->face, args->surfaceNormal,
args->element, args->distance, args->face, args->surfaceNormal, args->entityIdsToInclude,
args->intersectedObject, args->precisionPicking)) {
args->found = true;
}
@ -721,9 +722,9 @@ bool findRayIntersectionOp(OctreeElementPointer element, void* extraData) {
bool Octree::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
OctreeElementPointer& element, float& distance,
BoxFace& face, glm::vec3& surfaceNormal, void** intersectedObject,
BoxFace& face, glm::vec3& surfaceNormal, const QVector<QUuid>& entityIdsToInclude, void** intersectedObject,
Octree::lockType lockType, bool* accurateResult, bool precisionPicking) {
RayArgs args = { origin, direction, element, distance, face, surfaceNormal, intersectedObject, false, precisionPicking};
RayArgs args = { origin, direction, element, distance, face, surfaceNormal, entityIdsToInclude, intersectedObject, false, precisionPicking};
distance = FLT_MAX;
bool requireLock = lockType == Octree::Lock;

View file

@ -300,6 +300,7 @@ public:
bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
OctreeElementPointer& node, float& distance, BoxFace& face, glm::vec3& surfaceNormal,
const QVector<QUuid>& entityIdsToInclude = QVector<QUuid>(),
void** intersectedObject = NULL,
Octree::lockType lockType = Octree::TryLock,
bool* accurateResult = NULL,

View file

@ -575,7 +575,7 @@ void OctreeElement::notifyUpdateHooks() {
bool OctreeElement::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
bool& keepSearching, OctreeElementPointer& element, float& distance,
BoxFace& face, glm::vec3& surfaceNormal,
BoxFace& face, glm::vec3& surfaceNormal, const QVector<QUuid>& entityIdsToInclude,
void** intersectedObject, bool precisionPicking) {
keepSearching = true; // assume that we will continue searching after this.
@ -601,7 +601,7 @@ bool OctreeElement::findRayIntersection(const glm::vec3& origin, const glm::vec3
if (_cube.contains(origin) || distanceToElementCube < distance) {
if (findDetailedRayIntersection(origin, direction, keepSearching, element, distanceToElementDetails,
face, localSurfaceNormal, intersectedObject, precisionPicking, distanceToElementCube)) {
face, localSurfaceNormal, entityIdsToInclude, intersectedObject, precisionPicking, distanceToElementCube)) {
if (distanceToElementDetails < distance) {
distance = distanceToElementDetails;
@ -616,7 +616,7 @@ bool OctreeElement::findRayIntersection(const glm::vec3& origin, const glm::vec3
bool OctreeElement::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
bool& keepSearching, OctreeElementPointer& element, float& distance,
BoxFace& face, glm::vec3& surfaceNormal,
BoxFace& face, glm::vec3& surfaceNormal, const QVector<QUuid>& entityIdsToInclude,
void** intersectedObject, bool precisionPicking, float distanceToElementCube) {
// we did hit this element, so calculate appropriate distances

View file

@ -120,12 +120,12 @@ public:
virtual bool canRayIntersect() const { return isLeaf(); }
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
bool& keepSearching, OctreeElementPointer& node, float& distance,
BoxFace& face, glm::vec3& surfaceNormal,
BoxFace& face, glm::vec3& surfaceNormal, const QVector<QUuid>& entityIdsToInclude,
void** intersectedObject = NULL, bool precisionPicking = false);
virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
bool& keepSearching, OctreeElementPointer& element, float& distance,
BoxFace& face, glm::vec3& surfaceNormal,
BoxFace& face, glm::vec3& surfaceNormal, const QVector<QUuid>& entityIdsToInclude,
void** intersectedObject, bool precisionPicking, float distanceToElementCube);
/// \param center center of sphere in meters

View file

@ -93,13 +93,13 @@ bool EntityMotionState::handleEasyChanges(uint32_t flags, PhysicsEngine* engine)
updateServerPhysicsVariables();
ObjectMotionState::handleEasyChanges(flags, engine);
if (flags & EntityItem::DIRTY_SIMULATOR_ID) {
if (flags & Simulation::DIRTY_SIMULATOR_ID) {
_loopsWithoutOwner = 0;
if (_entity->getSimulatorID().isNull()) {
// simulation ownership is being removed
// remove the ACTIVATION flag because this object is coming to rest
// according to a remote simulation and we don't want to wake it up again
flags &= ~EntityItem::DIRTY_PHYSICS_ACTIVATION;
flags &= ~Simulation::DIRTY_PHYSICS_ACTIVATION;
// hint to Bullet that the object is deactivating
_body->setActivationState(WANTS_DEACTIVATION);
_outgoingPriority = NO_PRORITY;
@ -111,13 +111,13 @@ bool EntityMotionState::handleEasyChanges(uint32_t flags, PhysicsEngine* engine)
}
}
}
if (flags & EntityItem::DIRTY_SIMULATOR_OWNERSHIP) {
if (flags & Simulation::DIRTY_SIMULATOR_OWNERSHIP) {
// (DIRTY_SIMULATOR_OWNERSHIP really means "we should bid for ownership with SCRIPT priority")
// we're manipulating this object directly via script, so we artificially
// manipulate the logic to trigger an immediate bid for ownership
setOutgoingPriority(SCRIPT_EDIT_SIMULATION_PRIORITY);
}
if ((flags & EntityItem::DIRTY_PHYSICS_ACTIVATION) && !_body->isActive()) {
if ((flags & Simulation::DIRTY_PHYSICS_ACTIVATION) && !_body->isActive()) {
_body->activate();
}
@ -291,6 +291,10 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
return true;
}
if (_entity->shouldSuppressLocationEdits()) {
return false;
}
// Else we measure the error between current and extrapolated transform (according to expected behavior
// of remote EntitySimulation) and return true if the error is significant.
@ -503,7 +507,7 @@ uint32_t EntityMotionState::getIncomingDirtyFlags() {
bool isMoving = _entity->isMoving();
if (((bodyFlags & btCollisionObject::CF_STATIC_OBJECT) && isMoving) ||
(bodyFlags & btCollisionObject::CF_KINEMATIC_OBJECT && !isMoving)) {
dirtyFlags |= EntityItem::DIRTY_MOTION_TYPE;
dirtyFlags |= Simulation::DIRTY_MOTION_TYPE;
}
}
return dirtyFlags;

View file

@ -24,20 +24,33 @@ ObjectAction::~ObjectAction() {
}
void ObjectAction::updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep) {
if (_ownerEntity.expired()) {
bool ownerEntityExpired = false;
quint64 expiresWhen = 0;
withReadLock([&]{
ownerEntityExpired = _ownerEntity.expired();
expiresWhen = _expires;
});
if (ownerEntityExpired) {
qDebug() << "warning -- action with no entity removing self from btCollisionWorld.";
btDynamicsWorld* dynamicsWorld = static_cast<btDynamicsWorld*>(collisionWorld);
dynamicsWorld->removeAction(this);
return;
}
if (_expires > 0) {
if (expiresWhen > 0) {
quint64 now = usecTimestampNow();
if (now > _expires) {
EntityItemPointer ownerEntity = _ownerEntity.lock();
_active = false;
if (now > expiresWhen) {
EntityItemPointer ownerEntity = nullptr;
QUuid myID;
withWriteLock([&]{
ownerEntity = _ownerEntity.lock();
_active = false;
myID = getID();
});
if (ownerEntity) {
ownerEntity->removeAction(nullptr, getID());
ownerEntity->removeAction(nullptr, myID);
}
}
}
@ -50,35 +63,48 @@ void ObjectAction::updateAction(btCollisionWorld* collisionWorld, btScalar delta
}
bool ObjectAction::updateArguments(QVariantMap arguments) {
bool lifetimeSet = true;
float lifetime = EntityActionInterface::extractFloatArgument("action", arguments, "lifetime", lifetimeSet, false);
if (lifetimeSet) {
quint64 now = usecTimestampNow();
_expires = now + (quint64)(lifetime * USECS_PER_SECOND);
} else {
_expires = 0;
}
bool somethingChanged = false;
bool tagSet = true;
QString tag = EntityActionInterface::extractStringArgument("action", arguments, "tag", tagSet, false);
if (tagSet) {
_tag = tag;
} else {
tag = "";
}
withWriteLock([&]{
quint64 previousExpires = _expires;
QString previousTag = _tag;
return true;
bool lifetimeSet = true;
float lifetime = EntityActionInterface::extractFloatArgument("action", arguments, "lifetime", lifetimeSet, false);
if (lifetimeSet) {
quint64 now = usecTimestampNow();
_expires = now + (quint64)(lifetime * USECS_PER_SECOND);
} else {
_expires = 0;
}
bool tagSet = true;
QString tag = EntityActionInterface::extractStringArgument("action", arguments, "tag", tagSet, false);
if (tagSet) {
_tag = tag;
} else {
tag = "";
}
if (previousExpires != _expires || previousTag != _tag) {
somethingChanged = true;
}
});
return somethingChanged;
}
QVariantMap ObjectAction::getArguments() {
QVariantMap arguments;
if (_expires == 0) {
arguments["lifetime"] = 0.0f;
} else {
quint64 now = usecTimestampNow();
arguments["lifetime"] = (float)(_expires - now) / (float)USECS_PER_SECOND;
}
arguments["tag"] = _tag;
withReadLock([&]{
if (_expires == 0) {
arguments["lifetime"] = 0.0f;
} else {
quint64 now = usecTimestampNow();
arguments["lifetime"] = (float)(_expires - now) / (float)USECS_PER_SECOND;
}
arguments["tag"] = _tag;
});
return arguments;
}
@ -87,20 +113,30 @@ void ObjectAction::debugDraw(btIDebugDraw* debugDrawer) {
}
void ObjectAction::removeFromSimulation(EntitySimulation* simulation) const {
simulation->removeAction(_id);
QUuid myID;
withReadLock([&]{
myID = _id;
});
simulation->removeAction(myID);
}
btRigidBody* ObjectAction::getRigidBody() {
auto ownerEntity = _ownerEntity.lock();
if (!ownerEntity) {
return nullptr;
ObjectMotionState* motionState = nullptr;
withReadLock([&]{
auto ownerEntity = _ownerEntity.lock();
if (!ownerEntity) {
return;
}
void* physicsInfo = ownerEntity->getPhysicsInfo();
if (!physicsInfo) {
return;
}
motionState = static_cast<ObjectMotionState*>(physicsInfo);
});
if (motionState) {
return motionState->getRigidBody();
}
void* physicsInfo = ownerEntity->getPhysicsInfo();
if (!physicsInfo) {
return nullptr;
}
ObjectMotionState* motionState = static_cast<ObjectMotionState*>(physicsInfo);
return motionState->getRigidBody();
return nullptr;
}
glm::vec3 ObjectAction::getPosition() {

View file

@ -80,44 +80,61 @@ void ObjectActionOffset::updateActionWorker(btScalar deltaTimeStep) {
bool ObjectActionOffset::updateArguments(QVariantMap arguments) {
if (!ObjectAction::updateArguments(arguments)) {
return false;
}
bool ok = true;
glm::vec3 pointToOffsetFrom =
EntityActionInterface::extractVec3Argument("offset action", arguments, "pointToOffsetFrom", ok, true);
if (!ok) {
pointToOffsetFrom = _pointToOffsetFrom;
}
glm::vec3 pointToOffsetFrom;
float linearTimeScale;
float linearDistance;
ok = true;
float linearTimeScale =
EntityActionInterface::extractFloatArgument("offset action", arguments, "linearTimeScale", ok, false);
if (!ok) {
linearTimeScale = _linearTimeScale;
}
bool needUpdate = false;
bool somethingChanged = ObjectAction::updateArguments(arguments);
ok = true;
float linearDistance =
EntityActionInterface::extractFloatArgument("offset action", arguments, "linearDistance", ok, false);
if (!ok) {
linearDistance = _linearDistance;
}
withReadLock([&]{
bool ok = true;
pointToOffsetFrom =
EntityActionInterface::extractVec3Argument("offset action", arguments, "pointToOffsetFrom", ok, true);
if (!ok) {
pointToOffsetFrom = _pointToOffsetFrom;
}
// only change stuff if something actually changed
if (_pointToOffsetFrom != pointToOffsetFrom
|| _linearTimeScale != linearTimeScale
|| _linearDistance != linearDistance) {
ok = true;
linearTimeScale =
EntityActionInterface::extractFloatArgument("offset action", arguments, "linearTimeScale", ok, false);
if (!ok) {
linearTimeScale = _linearTimeScale;
}
ok = true;
linearDistance =
EntityActionInterface::extractFloatArgument("offset action", arguments, "linearDistance", ok, false);
if (!ok) {
linearDistance = _linearDistance;
}
// only change stuff if something actually changed
if (somethingChanged ||
_pointToOffsetFrom != pointToOffsetFrom ||
_linearTimeScale != linearTimeScale ||
_linearDistance != linearDistance) {
needUpdate = true;
}
});
if (needUpdate) {
withWriteLock([&] {
_pointToOffsetFrom = pointToOffsetFrom;
_linearTimeScale = linearTimeScale;
_linearDistance = linearDistance;
_positionalTargetSet = true;
_active = true;
activateBody();
auto ownerEntity = _ownerEntity.lock();
if (ownerEntity) {
ownerEntity->setActionDataDirty(true);
}
});
activateBody();
}
return true;
}
@ -134,17 +151,18 @@ QVariantMap ObjectActionOffset::getArguments() {
QByteArray ObjectActionOffset::serialize() const {
QByteArray ba;
QDataStream dataStream(&ba, QIODevice::WriteOnly);
dataStream << getType();
dataStream << ACTION_TYPE_OFFSET;
dataStream << getID();
dataStream << ObjectActionOffset::offsetVersion;
dataStream << _pointToOffsetFrom;
dataStream << _linearDistance;
dataStream << _linearTimeScale;
dataStream << _positionalTargetSet;
dataStream << _expires;
dataStream << _tag;
withReadLock([&] {
dataStream << _pointToOffsetFrom;
dataStream << _linearDistance;
dataStream << _linearTimeScale;
dataStream << _positionalTargetSet;
dataStream << _expires;
dataStream << _tag;
});
return ba;
}
@ -166,13 +184,13 @@ void ObjectActionOffset::deserialize(QByteArray serializedArguments) {
return;
}
dataStream >> _pointToOffsetFrom;
dataStream >> _linearDistance;
dataStream >> _linearTimeScale;
dataStream >> _positionalTargetSet;
dataStream >> _expires;
dataStream >> _tag;
_active = true;
withWriteLock([&] {
dataStream >> _pointToOffsetFrom;
dataStream >> _linearDistance;
dataStream >> _linearTimeScale;
dataStream >> _positionalTargetSet;
dataStream >> _expires;
dataStream >> _tag;
_active = true;
});
}

View file

@ -38,6 +38,7 @@ ObjectActionSpring::~ObjectActionSpring() {
}
void ObjectActionSpring::updateActionWorker(btScalar deltaTimeStep) {
// don't risk hanging the thread running the physics simulation
auto lockResult = withTryReadLock([&]{
auto ownerEntity = _ownerEntity.lock();
if (!ownerEntity) {
@ -100,60 +101,73 @@ void ObjectActionSpring::updateActionWorker(btScalar deltaTimeStep) {
}
});
if (!lockResult) {
// don't risk hanging the thread running the physics simulation
qDebug() << "ObjectActionSpring::updateActionWorker lock failed";
return;
}
}
const float MIN_TIMESCALE = 0.1f;
bool ObjectActionSpring::updateArguments(QVariantMap arguments) {
if (!ObjectAction::updateArguments(arguments)) {
return false;
}
// targets are required, spring-constants are optional
bool ok = true;
glm::vec3 positionalTarget =
EntityActionInterface::extractVec3Argument("spring action", arguments, "targetPosition", ok, false);
if (!ok) {
positionalTarget = _positionalTarget;
}
ok = true;
float linearTimeScale =
EntityActionInterface::extractFloatArgument("spring action", arguments, "linearTimeScale", ok, false);
if (!ok || linearTimeScale <= 0.0f) {
linearTimeScale = _linearTimeScale;
}
glm::vec3 positionalTarget;
float linearTimeScale;
glm::quat rotationalTarget;
float angularTimeScale;
ok = true;
glm::quat rotationalTarget =
EntityActionInterface::extractQuatArgument("spring action", arguments, "targetRotation", ok, false);
if (!ok) {
rotationalTarget = _rotationalTarget;
}
bool needUpdate = false;
bool somethingChanged = ObjectAction::updateArguments(arguments);
withReadLock([&]{
// targets are required, spring-constants are optional
bool ok = true;
positionalTarget = EntityActionInterface::extractVec3Argument("spring action", arguments, "targetPosition", ok, false);
if (!ok) {
positionalTarget = _positionalTarget;
}
ok = true;
linearTimeScale = EntityActionInterface::extractFloatArgument("spring action", arguments, "linearTimeScale", ok, false);
if (!ok || linearTimeScale <= 0.0f) {
linearTimeScale = _linearTimeScale;
}
ok = true;
float angularTimeScale =
EntityActionInterface::extractFloatArgument("spring action", arguments, "angularTimeScale", ok, false);
if (!ok) {
angularTimeScale = _angularTimeScale;
}
ok = true;
rotationalTarget = EntityActionInterface::extractQuatArgument("spring action", arguments, "targetRotation", ok, false);
if (!ok) {
rotationalTarget = _rotationalTarget;
}
if (positionalTarget != _positionalTarget
|| linearTimeScale != _linearTimeScale
|| rotationalTarget != _rotationalTarget
|| angularTimeScale != _angularTimeScale) {
// something changed
ok = true;
angularTimeScale =
EntityActionInterface::extractFloatArgument("spring action", arguments, "angularTimeScale", ok, false);
if (!ok) {
angularTimeScale = _angularTimeScale;
}
if (somethingChanged ||
positionalTarget != _positionalTarget ||
linearTimeScale != _linearTimeScale ||
rotationalTarget != _rotationalTarget ||
angularTimeScale != _angularTimeScale) {
// something changed
needUpdate = true;
}
});
if (needUpdate) {
withWriteLock([&] {
_positionalTarget = positionalTarget;
_linearTimeScale = glm::max(MIN_TIMESCALE, glm::abs(linearTimeScale));
_rotationalTarget = rotationalTarget;
_angularTimeScale = glm::max(MIN_TIMESCALE, glm::abs(angularTimeScale));
_active = true;
activateBody();
auto ownerEntity = _ownerEntity.lock();
if (ownerEntity) {
ownerEntity->setActionDataDirty(true);
}
});
activateBody();
}
return true;
}
@ -177,16 +191,16 @@ QByteArray ObjectActionSpring::serialize() const {
dataStream << getID();
dataStream << ObjectActionSpring::springVersion;
dataStream << _positionalTarget;
dataStream << _linearTimeScale;
dataStream << _positionalTargetSet;
dataStream << _rotationalTarget;
dataStream << _angularTimeScale;
dataStream << _rotationalTargetSet;
dataStream << _expires;
dataStream << _tag;
withReadLock([&] {
dataStream << _positionalTarget;
dataStream << _linearTimeScale;
dataStream << _positionalTargetSet;
dataStream << _rotationalTarget;
dataStream << _angularTimeScale;
dataStream << _rotationalTargetSet;
dataStream << _expires;
dataStream << _tag;
});
return serializedActionArguments;
}
@ -205,19 +219,22 @@ void ObjectActionSpring::deserialize(QByteArray serializedArguments) {
uint16_t serializationVersion;
dataStream >> serializationVersion;
if (serializationVersion != ObjectActionSpring::springVersion) {
assert(false);
return;
}
dataStream >> _positionalTarget;
dataStream >> _linearTimeScale;
dataStream >> _positionalTargetSet;
withWriteLock([&] {
dataStream >> _positionalTarget;
dataStream >> _linearTimeScale;
dataStream >> _positionalTargetSet;
dataStream >> _rotationalTarget;
dataStream >> _angularTimeScale;
dataStream >> _rotationalTargetSet;
dataStream >> _rotationalTarget;
dataStream >> _angularTimeScale;
dataStream >> _rotationalTargetSet;
dataStream >> _expires;
dataStream >> _tag;
dataStream >> _expires;
dataStream >> _tag;
_active = true;
_active = true;
});
}

View file

@ -126,34 +126,34 @@ void ObjectMotionState::setRigidBody(btRigidBody* body) {
}
bool ObjectMotionState::handleEasyChanges(uint32_t flags, PhysicsEngine* engine) {
if (flags & EntityItem::DIRTY_POSITION) {
if (flags & Simulation::DIRTY_POSITION) {
btTransform worldTrans;
if (flags & EntityItem::DIRTY_ROTATION) {
if (flags & Simulation::DIRTY_ROTATION) {
worldTrans.setRotation(glmToBullet(getObjectRotation()));
} else {
worldTrans = _body->getWorldTransform();
}
worldTrans.setOrigin(glmToBullet(getObjectPosition()));
_body->setWorldTransform(worldTrans);
} else if (flags & EntityItem::DIRTY_ROTATION) {
} else if (flags & Simulation::DIRTY_ROTATION) {
btTransform worldTrans = _body->getWorldTransform();
worldTrans.setRotation(glmToBullet(getObjectRotation()));
_body->setWorldTransform(worldTrans);
}
if (flags & EntityItem::DIRTY_LINEAR_VELOCITY) {
if (flags & Simulation::DIRTY_LINEAR_VELOCITY) {
_body->setLinearVelocity(glmToBullet(getObjectLinearVelocity()));
_body->setGravity(glmToBullet(getObjectGravity()));
}
if (flags & EntityItem::DIRTY_ANGULAR_VELOCITY) {
if (flags & Simulation::DIRTY_ANGULAR_VELOCITY) {
_body->setAngularVelocity(glmToBullet(getObjectAngularVelocity()));
}
if (flags & EntityItem::DIRTY_MATERIAL) {
if (flags & Simulation::DIRTY_MATERIAL) {
updateBodyMaterialProperties();
}
if (flags & EntityItem::DIRTY_MASS) {
if (flags & Simulation::DIRTY_MASS) {
updateBodyMassProperties();
}
@ -161,7 +161,7 @@ bool ObjectMotionState::handleEasyChanges(uint32_t flags, PhysicsEngine* engine)
}
bool ObjectMotionState::handleHardAndEasyChanges(uint32_t flags, PhysicsEngine* engine) {
if (flags & EntityItem::DIRTY_SHAPE) {
if (flags & Simulation::DIRTY_SHAPE) {
// make sure the new shape is valid
if (!isReadyToComputeShape()) {
return false;
@ -170,7 +170,7 @@ bool ObjectMotionState::handleHardAndEasyChanges(uint32_t flags, PhysicsEngine*
if (!newShape) {
qCDebug(physics) << "Warning: failed to generate new shape!";
// failed to generate new shape! --> keep old shape and remove shape-change flag
flags &= ~EntityItem::DIRTY_SHAPE;
flags &= ~Simulation::DIRTY_SHAPE;
// TODO: force this object out of PhysicsEngine rather than just use the old shape
if ((flags & HARD_DIRTY_PHYSICS_FLAGS) == 0) {
// no HARD flags remain, so do any EASY changes
@ -186,7 +186,7 @@ bool ObjectMotionState::handleHardAndEasyChanges(uint32_t flags, PhysicsEngine*
_body->setCollisionShape(_shape);
} else {
// huh... the shape didn't actually change, so we clear the DIRTY_SHAPE flag
flags &= ~EntityItem::DIRTY_SHAPE;
flags &= ~Simulation::DIRTY_SHAPE;
}
}
if (flags & EASY_DIRTY_PHYSICS_FLAGS) {

View file

@ -37,18 +37,18 @@ enum MotionStateType {
// The update flags trigger two varieties of updates: "hard" which require the body to be pulled
// and re-added to the physics engine and "easy" which just updates the body properties.
const uint32_t HARD_DIRTY_PHYSICS_FLAGS = (uint32_t)(EntityItem::DIRTY_MOTION_TYPE | EntityItem::DIRTY_SHAPE |
EntityItem::DIRTY_COLLISION_GROUP);
const uint32_t EASY_DIRTY_PHYSICS_FLAGS = (uint32_t)(EntityItem::DIRTY_TRANSFORM | EntityItem::DIRTY_VELOCITIES |
EntityItem::DIRTY_MASS | EntityItem::DIRTY_MATERIAL |
EntityItem::DIRTY_SIMULATOR_ID | EntityItem::DIRTY_SIMULATOR_OWNERSHIP);
const uint32_t HARD_DIRTY_PHYSICS_FLAGS = (uint32_t)(Simulation::DIRTY_MOTION_TYPE | Simulation::DIRTY_SHAPE |
Simulation::DIRTY_COLLISION_GROUP);
const uint32_t EASY_DIRTY_PHYSICS_FLAGS = (uint32_t)(Simulation::DIRTY_TRANSFORM | Simulation::DIRTY_VELOCITIES |
Simulation::DIRTY_MASS | Simulation::DIRTY_MATERIAL |
Simulation::DIRTY_SIMULATOR_ID | Simulation::DIRTY_SIMULATOR_OWNERSHIP);
// These are the set of incoming flags that the PhysicsEngine needs to hear about:
const uint32_t DIRTY_PHYSICS_FLAGS = (uint32_t)(HARD_DIRTY_PHYSICS_FLAGS | EASY_DIRTY_PHYSICS_FLAGS |
EntityItem::DIRTY_PHYSICS_ACTIVATION);
Simulation::DIRTY_PHYSICS_ACTIVATION);
// These are the outgoing flags that the PhysicsEngine can affect:
const uint32_t OUTGOING_DIRTY_PHYSICS_FLAGS = EntityItem::DIRTY_TRANSFORM | EntityItem::DIRTY_VELOCITIES;
const uint32_t OUTGOING_DIRTY_PHYSICS_FLAGS = Simulation::DIRTY_TRANSFORM | Simulation::DIRTY_VELOCITIES;
class OctreeEditPacketSender;

View file

@ -104,6 +104,21 @@ QVector<float> qVectorFloatFromScriptValue(const QScriptValue& array) {
return newVector;
}
QVector<QUuid> qVectorQUuidFromScriptValue(const QScriptValue& array) {
if (!array.isArray()) {
return QVector<QUuid>();
}
QVector<QUuid> newVector;
int length = array.property("length").toInteger();
newVector.reserve(length);
for (int i = 0; i < length; i++) {
QString uuidAsString = array.property(i).toString();
QUuid fromString(uuidAsString);
newVector << fromString;
}
return newVector;
}
QScriptValue qVectorFloatToScriptValue(QScriptEngine* engine, const QVector<float>& vector) {
QScriptValue array = engine->newArray();
for (int i = 0; i < vector.size(); i++) {

View file

@ -65,6 +65,8 @@ QScriptValue qVectorFloatToScriptValue(QScriptEngine* engine, const QVector<floa
void qVectorFloatFromScriptValue(const QScriptValue& array, QVector<float>& vector);
QVector<float> qVectorFloatFromScriptValue(const QScriptValue& array);
QVector<QUuid> qVectorQUuidFromScriptValue(const QScriptValue& array);
class PickRay {
public:
PickRay() : origin(0.0f), direction(0.0f) { }

View file

@ -62,12 +62,11 @@ QScriptValue variantMapToScriptValue(QVariantMap& variantMap, QScriptEngine& scr
QScriptValue variantListToScriptValue(QVariantList& variantList, QScriptEngine& scriptEngine) {
QScriptValue scriptValue = scriptEngine.newObject();
scriptValue.setProperty("length", variantList.size());
int i = 0;
foreach (QVariant v, variantList) {
scriptValue.setProperty(i++, variantToScriptValue(v, scriptEngine));
QScriptValue scriptValue = scriptEngine.newArray();
for (int i = 0; i < variantList.size(); i++) {
scriptValue.setProperty(i, variantToScriptValue(variantList[i], scriptEngine));
}
return scriptValue;

View file

@ -0,0 +1,35 @@
import QtQuick 2.1
import QtQuick.Controls 1.0
import QtQuick.Layouts 1.0
import QtQuick.Dialogs 1.0
import "./hydra"
import "./controls"
Item {
id: root
width: 480
height: width * 3.0 / 4.0
property var device
property real scale: width / 480
property real rightOffset: (width / 2) * scale
Image {
anchors.fill: parent
source: "hydra/hydra.png"
HydraStick {
leftStick: true
scale: root.scale
device: root.device
}
HydraStick {
leftStick: false
scale: root.scale
device: root.device
}
}
}

View file

@ -8,14 +8,20 @@ import "./controls"
Item {
id: root
property real aspect: 300.0 / 215.0
width: 300
height: width / aspect
property var device
property real scale: 1.0
width: 300 * scale
height: 215 * scale
property string label: ""
property real scale: width / 300.0
Image {
Text {
anchors.left: parent.left
anchors.top: parent.top
text: root.label
visible: root.label != ""
}
anchors.fill: parent
source: "xbox/xbox360-controller-md.png"

View file

@ -10,16 +10,24 @@ Column {
id: root
property var actions: Controllers.Actions
property var standard: Controllers.Standard
property var hydra: null
property var testMapping: null
property var xbox: null
Component.onCompleted: {
var patt = /^X360Controller/;
var xboxRegex = /^X360Controller/;
var hydraRegex = /^Hydra/;
for (var prop in Controllers.Hardware) {
if(patt.test(prop)) {
if(xboxRegex.test(prop)) {
root.xbox = Controllers.Hardware[prop]
break
print("found xbox")
continue
}
if (hydraRegex.test(prop)) {
root.hydra = Controllers.Hardware[prop]
print("found hydra")
continue
}
}
}
@ -79,23 +87,15 @@ Column {
mapping.join(standard.LB, standard.RB).to(actions.Yaw);
mapping.from(actions.Yaw).clamp(0, 1).invert().to(actions.YAW_RIGHT);
mapping.from(actions.Yaw).clamp(-1, 0).to(actions.YAW_LEFT);
// mapping.modifier(keyboard.Ctrl).scale(2.0)
// mapping.from(keyboard.A).to(actions.TranslateLeft)
// mapping.from(keyboard.A, keyboard.Shift).to(actions.TurnLeft)
// mapping.from(keyboard.A, keyboard.Shift, keyboard.Ctrl).scale(2.0).to(actions.TurnLeft)
// // First loopbacks
// // Then non-loopbacks by constraint level (number of inputs)
// mapping.from(xbox.RX).deadZone(0.2).to(xbox.RX)
// mapping.from(standard.RB, standard.LB, keyboard.Shift).to(actions.TurnLeft)
// mapping.from(keyboard.A, keyboard.Shift).to(actions.TurnLeft)
// mapping.from(keyboard.W).when(keyboard.Shift).to(actions.Forward)
testMapping = mapping;
enabled = false
@ -114,12 +114,19 @@ Column {
}
}
Row {
Xbox { device: root.standard; label: "Standard"; width: 360 }
}
Row {
spacing: 8
Xbox { device: root.xbox }
Xbox { device: root.standard }
Xbox { device: root.xbox; label: "XBox"; width: 360 }
}
Row {
spacing: 8
Hydra { device: root.hydra; width: 360 }
}
Row {
spacing: 8

View file

@ -8,6 +8,8 @@ Item {
property int size: 64
width: size
height: size
property bool invertY: false
property int halfSize: size / 2
property var controlIds: [ 0, 0 ]
@ -18,6 +20,9 @@ Item {
Controllers.getValue(controlIds[0]),
Controllers.getValue(controlIds[1])
);
if (root.invertY) {
value.y = value.y * -1.0
}
canvas.requestPaint();
}

View file

@ -0,0 +1,18 @@
import QtQuick 2.1
import QtQuick.Controls 1.0
import QtQuick.Layouts 1.0
import QtQuick.Dialogs 1.0
import "./../controls"
Item {
id: root
width: 72 * scale
height: 48 * scale
property var device
property real scale: 1.0
property bool leftStick: true
}

View file

@ -0,0 +1,91 @@
import QtQuick 2.1
import QtQuick.Controls 1.0
import QtQuick.Layouts 1.0
import QtQuick.Dialogs 1.0
import "./../controls"
Item {
id: root
property var device
property real scale: 1.0
property bool leftStick: true
width: parent.width / 2; height: parent.height
x: leftStick ? 0 : parent.width / 2
Text {
x: parent.width / 2 - width / 2; y: parent.height / 2 - height / 2
text: root.leftStick ? "L" : "R"
color: 'green'
}
// Analog Stick
AnalogStick {
size: 64 * root.scale
x: 127 * root.scale - width / 2; y: 45 * root.scale - width / 2; z: 100
invertY: true
controlIds: [
root.leftStick ? root.device.LX : root.device.RX,
root.leftStick ? root.device.LY : root.device.RY
]
}
// Stick press
ToggleButton {
controlId: root.leftStick ? root.device.LS : root.device.RS
width: 16 * root.scale; height: 16 * root.scale
x: 127 * root.scale - width / 2; y: 45 * root.scale - width / 2;
color: 'yellow'
}
// Trigger
AnalogButton {
controlId: root.leftStick ? root.device.LT : root.device.RT
width: 8 * root.scale ; height: 64 * root.scale
y: 24 * root.scale
x: root.leftStick ? (48 * root.scale) : root.width - (48 * root.scale) - width / 2
}
// Bumper
ToggleButton {
controlId: root.leftStick ? root.device.LB : root.device.RB
height: 16 * root.scale; width: 32 * root.scale
x: 128 * root.scale - width / 2; y: 24 * root.scale
color: 'red'
}
ToggleButton {
controlId: root.leftStick ? root.device.L0 : root.device.R0
height: 16 * root.scale; width: 4 * root.scale
x: 128 * root.scale - width / 2; y: 109 * root.scale
color: 'yellow'
}
ToggleButton {
controlId: root.leftStick ? root.device.L1 : root.device.R1
width: 16 * root.scale; height: 16 * root.scale
x: 103 * root.scale - width / 2; y: 100 * root.scale - height / 2
color: 'yellow'
}
ToggleButton {
controlId: root.leftStick ? root.device.L2 : root.device.R2
width: 16 * root.scale; height: 16 * root.scale
x: 148 * root.scale - width / 2; y: 100 * root.scale - height / 2
color: 'yellow'
}
ToggleButton {
controlId: root.leftStick ? root.device.L3 : root.device.R3
width: 16 * root.scale; height: 16 * root.scale
x: 97 * root.scale - width / 2; y: 76 * root.scale - height / 2
color: 'yellow'
}
ToggleButton {
controlId: root.leftStick ? root.device.L4 : root.device.R4
width: 16 * root.scale; height: 16 * root.scale
x: 155 * root.scale - width / 2; y: 76 * root.scale - height / 2
color: 'yellow'
}
}

Binary file not shown.

After

(image error) Size: 21 KiB

View file

@ -88,14 +88,9 @@ public:
int main(int argc, char** argv) {
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
for (auto path : qApp->libraryPaths()) {
qDebug() << path;
}
for (auto path : qApp->libraryPaths()) {
qDebug() << path;
}
new PluginContainerProxy();
// Simulate our application idle loop
QTimer timer;
QObject::connect(&timer, &QTimer::timeout, [] {
static float last = secTimestampNow();
@ -122,11 +117,12 @@ int main(int argc, char** argv) {
auto keyboardMouseDevice = static_cast<KeyboardMouseDevice*>(inputPlugin.data()); // TODO: this seems super hacky
keyboardMouseDevice->registerToUserInputMapper(*userInputMapper);
}
inputPlugin->pluginUpdate(0, false);
}
//new PluginContainerProxy();
auto rootContext = engine.rootContext();
rootContext->setContextProperty("Controllers", new MyControllerScriptingInterface());
}
engine.load(getQmlDir() + "main.qml");
app.exec();
return 0;

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