Fix merge conflicts and lint the conflict code

This commit is contained in:
James Pollack 2015-10-01 15:32:22 -07:00
commit dfab1fc685
51 changed files with 1064 additions and 227 deletions

View file

@ -46,6 +46,9 @@ if (WIN32)
# Caveats: http://stackoverflow.com/questions/2288728/drawbacks-of-using-largeaddressaware-for-32-bit-windows-executables # Caveats: http://stackoverflow.com/questions/2288728/drawbacks-of-using-largeaddressaware-for-32-bit-windows-executables
# TODO: Remove when building 64-bit. # TODO: Remove when building 64-bit.
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /LARGEADDRESSAWARE") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /LARGEADDRESSAWARE")
# always produce symbols as PDB files
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zi")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /DEBUG /OPT:REF /OPT:ICF")
else () else ()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -fno-strict-aliasing -Wno-unused-parameter") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -fno-strict-aliasing -Wno-unused-parameter")
if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)

View file

@ -233,8 +233,8 @@ void Agent::setIsAvatar(bool isAvatar) {
} }
if (_avatarBillboardTimer) { if (_avatarBillboardTimer) {
_avatarIdentityTimer->stop(); _avatarBillboardTimer->stop();
delete _avatarIdentityTimer; delete _avatarBillboardTimer;
_avatarBillboardTimer = nullptr; _avatarBillboardTimer = nullptr;
} }
} }
@ -364,10 +364,15 @@ void Agent::processAgentAvatarAndAudio(float deltaTime) {
} }
void Agent::aboutToFinish() { void Agent::aboutToFinish() {
setIsAvatar(false);// will stop timers for sending billboards and identity packets
if (_scriptEngine) {
_scriptEngine->stop(); _scriptEngine->stop();
}
if (_pingTimer) {
_pingTimer->stop(); _pingTimer->stop();
delete _pingTimer; delete _pingTimer;
}
// our entity tree is going to go away so tell that to the EntityScriptingInterface // our entity tree is going to go away so tell that to the EntityScriptingInterface
DependencyManager::get<EntityScriptingInterface>()->setEntityTree(NULL); DependencyManager::get<EntityScriptingInterface>()->setEntityTree(NULL);

View file

@ -238,7 +238,6 @@ int OctreeInboundPacketProcessor::sendNackPackets() {
return 0; return 0;
} }
auto nackPacketList = NLPacketList::create(_myServer->getMyEditNackType());
auto nodeList = DependencyManager::get<NodeList>(); auto nodeList = DependencyManager::get<NodeList>();
int packetsSent = 0; int packetsSent = 0;
@ -272,14 +271,15 @@ int OctreeInboundPacketProcessor::sendNackPackets() {
auto it = missingSequenceNumbers.constBegin(); auto it = missingSequenceNumbers.constBegin();
if (it != missingSequenceNumbers.constEnd()) {
auto nackPacketList = NLPacketList::create(_myServer->getMyEditNackType());
while (it != missingSequenceNumbers.constEnd()) { while (it != missingSequenceNumbers.constEnd()) {
unsigned short int sequenceNumber = *it; unsigned short int sequenceNumber = *it;
nackPacketList->writePrimitive(sequenceNumber); nackPacketList->writePrimitive(sequenceNumber);
++it; ++it;
} }
if (nackPacketList->getNumPackets()) {
qDebug() << "NACK Sent back to editor/client... destinationNode=" << nodeUUID; qDebug() << "NACK Sent back to editor/client... destinationNode=" << nodeUUID;
packetsSent += nackPacketList->getNumPackets(); packetsSent += nackPacketList->getNumPackets();

View file

@ -37,7 +37,7 @@ macro(COPY_DLLS_BESIDE_WINDOWS_EXECUTABLE)
add_custom_command( add_custom_command(
TARGET ${TARGET_NAME} TARGET ${TARGET_NAME}
POST_BUILD POST_BUILD
COMMAND CMD /C "SET PATH=%PATH%;${QT_DIR}/bin && ${WINDEPLOYQT_COMMAND} $<TARGET_FILE:${TARGET_NAME}>" COMMAND CMD /C "SET PATH=%PATH%;${QT_DIR}/bin && ${WINDEPLOYQT_COMMAND} $<$<OR:$<CONFIG:Release>,$<CONFIG:MinSizeRel>,$<CONFIG:RelWithDebInfo>>:--release> $<TARGET_FILE:${TARGET_NAME}>"
) )
endif () endif ()
endmacro() endmacro()

View file

@ -39,11 +39,13 @@ var ZERO_VEC = {
}; };
var totalTime = 0; var totalTime = 0;
var lastUpdate = 0;
var UPDATE_INTERVAL = 1 / 30; // 30fps
var MINIMUM_LIGHT_INTENSITY = 0.75; var MINIMUM_LIGHT_INTENSITY = 0.75;
var MAXIMUM_LIGHT_INTENSITY = 2.75; var MAXIMUM_LIGHT_INTENSITY = 2.75;
var LIGHT_INTENSITY_RANDOMNESS = 0.3; var LIGHT_INTENSITY_RANDOMNESS = 0.3;
var EPHEMERAL_LIFETIME = 10; // ephemeral entities will live for 10 seconds after script stops running var EPHEMERAL_LIFETIME = 60; // ephemeral entities will live for 60 seconds after script stops running
var LightMaker = { var LightMaker = {
light: null, light: null,
@ -74,10 +76,17 @@ function update(deltaTime) {
LightMaker.spawnLight(); LightMaker.spawnLight();
} else { } else {
totalTime += deltaTime; 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) {
var intensity = (MINIMUM_LIGHT_INTENSITY + (MAXIMUM_LIGHT_INTENSITY + (Math.sin(totalTime) * MAXIMUM_LIGHT_INTENSITY))); var intensity = (MINIMUM_LIGHT_INTENSITY + (MAXIMUM_LIGHT_INTENSITY + (Math.sin(totalTime) * MAXIMUM_LIGHT_INTENSITY)));
intensity += randFloat(-LIGHT_INTENSITY_RANDOMNESS, LIGHT_INTENSITY_RANDOMNESS); intensity += randFloat(-LIGHT_INTENSITY_RANDOMNESS, LIGHT_INTENSITY_RANDOMNESS);
var properties = Entities.getEntityProperties(LightMaker.light, "age"); var properties = Entities.getEntityProperties(LightMaker.light, "age");
Entities.editEntity(LightMaker.light, { intensity: intensity, lifetime: properties.age + EPHEMERAL_LIFETIME }); var newLifetime = properties.age + EPHEMERAL_LIFETIME;
Entities.editEntity(LightMaker.light, { type: "Light", intensity: intensity, lifetime: newLifetime });
lastUpdate = totalTime;
}
} }
} }

146
examples/actionInspector.js Normal file
View file

@ -0,0 +1,146 @@
//
// actionInspector.js
// examples
//
// Created by Seth Alves on 2015-9-30.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
Script.include("libraries/utils.js");
var INSPECT_RADIUS = 10;
var overlays = {};
var toType = function(obj) {
return ({}).toString.call(obj).match(/\s([a-zA-Z]+)/)[1].toLowerCase()
}
function actionArgumentsToString(actionArguments) {
var result = "type: " + actionArguments["type"] + "\n";
for (var argumentName in actionArguments) {
if (actionArguments.hasOwnProperty(argumentName)) {
if (argumentName == "type") {
continue;
}
var arg = actionArguments[argumentName];
var argType = toType(arg);
var argString = arg;
if (argType == "object") {
if (Object.keys(arg).length == 3) {
argString = vec3toStr(arg, 1);
}
} else if (argType == "number") {
argString = arg.toFixed(2);
}
result += argumentName + ": "
// + toType(arg) + " -- "
+ argString + "\n";
}
}
return result;
}
function updateOverlay(entityID, actionText) {
var properties = Entities.getEntityProperties(entityID, ["position", "dimensions"]);
var position = Vec3.sum(properties.position, {x:0, y:properties.dimensions.y, z:0});
// print("position: " + vec3toStr(position) + " " + actionText);
if (entityID in overlays) {
var overlay = overlays[entityID];
Overlays.editOverlay(overlay, {
text: actionText,
position: position
});
} else {
var lines = actionText.split(/\r\n|\r|\n/);
var maxLineLength = lines[0].length;
for (var i = 1; i < lines.length; i++) {
if (lines[i].length > maxLineLength) {
maxLineLength = lines[i].length;
}
}
var textWidth = maxLineLength * 0.034; // XXX how to know this?
var textHeight = .5;
var numberOfLines = lines.length;
var textMargin = 0.05;
var lineHeight = (textHeight - (2 * textMargin)) / numberOfLines;
overlays[entityID] = Overlays.addOverlay("text3d", {
position: position,
dimensions: { x: textWidth, y: textHeight },
backgroundColor: { red: 0, green: 0, blue: 0},
color: { red: 255, green: 255, blue: 255},
topMargin: textMargin,
leftMargin: textMargin,
bottomMargin: textMargin,
rightMargin: textMargin,
text: actionText,
lineHeight: lineHeight,
alpha: 0.9,
backgroundAlpha: 0.9,
ignoreRayIntersection: true,
visible: true,
isFacingAvatar: true
});
}
}
function cleanup() {
for (var entityID in overlays) {
Overlays.deleteOverlay(overlays[entityID]);
}
}
Script.setInterval(function() {
var nearbyEntities = Entities.findEntities(MyAvatar.position, INSPECT_RADIUS);
for (var entityIndex = 0; entityIndex < nearbyEntities.length; entityIndex++) {
var entityID = nearbyEntities[entityIndex];
var actionIDs = Entities.getActionIDs(entityID);
var actionText = ""
for (var actionIndex = 0; actionIndex < actionIDs.length; actionIndex++) {
var actionID = actionIDs[actionIndex];
var actionArguments = Entities.getActionArguments(entityID, actionID);
var actionArgumentText = actionArgumentsToString(actionArguments);
if (actionArgumentText != "") {
actionText += "-----------------\n";
actionText += actionArgumentText;
}
}
if (actionText != "") {
updateOverlay(entityID, actionText);
}
// if an entity no longer has an action, remove its overlay
if (actionIDs.length == 0) {
if (entityID in overlays) {
Overlays.deleteOverlay(overlays[entityID]);
delete overlays[entityID];
}
}
}
// if an entity is too far away, remove its overlay
for (var entityID in overlays) {
var position = Entities.getEntityProperties(entityID, ["position"]).position;
if (Vec3.distance(position, MyAvatar.position) > INSPECT_RADIUS) {
Overlays.deleteOverlay(overlays[entityID]);
delete overlays[entityID];
}
}
}, 100);
Script.scriptEnding.connect(cleanup);

View file

@ -19,7 +19,8 @@ Script.include("../libraries/utils.js");
// //
var TRIGGER_SMOOTH_RATIO = 0.1; // 0.0 disables smoothing of trigger value var TRIGGER_SMOOTH_RATIO = 0.1; // 0.0 disables smoothing of trigger value
var TRIGGER_ON_VALUE = 0.2; var TRIGGER_ON_VALUE = 0.4;
var TRIGGER_OFF_VALUE = 0.15;
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// //
@ -54,27 +55,54 @@ var RELEASE_VELOCITY_MULTIPLIER = 1.5; // affects throwing things
var RIGHT_HAND = 1; var RIGHT_HAND = 1;
var LEFT_HAND = 0; var LEFT_HAND = 0;
var ZERO_VEC = { x: 0, y: 0, z: 0}; var ZERO_VEC = {
x: 0,
y: 0,
z: 0
};
var NULL_ACTION_ID = "{00000000-0000-0000-000000000000}"; var NULL_ACTION_ID = "{00000000-0000-0000-000000000000}";
var MSEC_PER_SEC = 1000.0; var MSEC_PER_SEC = 1000.0;
// these control how long an abandoned pointer line will hang around // these control how long an abandoned pointer line will hang around
var startTime = Date.now(); var startTime = Date.now();
var LIFETIME = 10; var LIFETIME = 10;
var ACTION_LIFETIME = 10; // seconds
// states for the state machine // states for the state machine
var STATE_SEARCHING = 0; var STATE_OFF = 0;
var STATE_DISTANCE_HOLDING = 1; var STATE_SEARCHING = 1;
var STATE_CONTINUE_DISTANCE_HOLDING = 2; var STATE_DISTANCE_HOLDING = 2;
var STATE_NEAR_GRABBING = 3; var STATE_CONTINUE_DISTANCE_HOLDING = 3;
var STATE_CONTINUE_NEAR_GRABBING = 4; var STATE_NEAR_GRABBING = 4;
var STATE_NEAR_GRABBING_NON_COLLIDING = 5; var STATE_CONTINUE_NEAR_GRABBING = 5;
var STATE_CONTINUE_NEAR_GRABBING_NON_COLLIDING = 6; var STATE_NEAR_GRABBING_NON_COLLIDING = 6;
var STATE_RELEASE = 7; var STATE_CONTINUE_NEAR_GRABBING_NON_COLLIDING = 7;
var STATE_RELEASE = 8;
var GRAB_USER_DATA_KEY = "grabKey"; var GRAB_USER_DATA_KEY = "grabKey";
var GRABBABLE_DATA_KEY = "grabbableKey"; var GRABBABLE_DATA_KEY = "grabbableKey";
function getTag() {
return "grab-" + MyAvatar.sessionUUID;
}
function entityIsGrabbedByOther(entityID) {
var actionIDs = Entities.getActionIDs(entityID);
for (var actionIndex = 0; actionIndex < actionIDs.length; actionIndex++) {
var actionID = actionIDs[actionIndex];
var actionArguments = Entities.getActionArguments(entityID, actionID);
var tag = actionArguments["tag"];
if (tag == getTag()) {
continue;
}
if (tag.slice(0, 5) == "grab-") {
return true;
}
}
return false;
}
function MyController(hand, triggerAction) { function MyController(hand, triggerAction) {
this.hand = hand; this.hand = hand;
if (this.hand === RIGHT_HAND) { if (this.hand === RIGHT_HAND) {
@ -85,23 +113,32 @@ function MyController(hand, triggerAction) {
this.getHandRotation = MyAvatar.getLeftPalmRotation; this.getHandRotation = MyAvatar.getLeftPalmRotation;
} }
var SPATIAL_CONTROLLERS_PER_PALM = 2;
var TIP_CONTROLLER_OFFSET = 1;
this.triggerAction = triggerAction; this.triggerAction = triggerAction;
this.palm = 2 * hand; this.palm = SPATIAL_CONTROLLERS_PER_PALM * hand;
// this.tip = 2 * hand + 1; // unused, but I'm leaving this here for fear it will be needed this.tip = SPATIAL_CONTROLLERS_PER_PALM * hand + TIP_CONTROLLER_OFFSET;
this.actionID = null; // action this script created... this.actionID = null; // action this script created...
this.grabbedEntity = null; // on this entity. this.grabbedEntity = null; // on this entity.
this.grabbedVelocity = ZERO_VEC; // rolling average of held object's velocity this.grabbedVelocity = ZERO_VEC; // rolling average of held object's velocity
this.state = 0; this.state = STATE_OFF;
this.pointer = null; // entity-id of line object this.pointer = null; // entity-id of line object
this.triggerValue = 0; // rolling average of trigger value this.triggerValue = 0; // rolling average of trigger value
var _this = this; var _this = this;
this.update = function() { this.update = function() {
this.updateSmoothedTrigger();
switch (this.state) { switch (this.state) {
case STATE_OFF:
this.off();
this.touchTest();
break;
case STATE_SEARCHING: case STATE_SEARCHING:
this.search(); this.search();
this.touchTest();
break; break;
case STATE_DISTANCE_HOLDING: case STATE_DISTANCE_HOLDING:
this.distanceHolding(); this.distanceHolding();
@ -158,21 +195,35 @@ function MyController(hand, triggerAction) {
this.pointer = null; this.pointer = null;
}; };
this.triggerSmoothedSqueezed = function() { this.updateSmoothedTrigger = function() {
var triggerValue = Controller.getActionValue(this.triggerAction); var triggerValue = Controller.getActionValue(this.triggerAction);
// smooth out trigger value // smooth out trigger value
this.triggerValue = (this.triggerValue * TRIGGER_SMOOTH_RATIO) + this.triggerValue = (this.triggerValue * TRIGGER_SMOOTH_RATIO) +
(triggerValue * (1.0 - TRIGGER_SMOOTH_RATIO)); (triggerValue * (1.0 - TRIGGER_SMOOTH_RATIO));
}
this.triggerSmoothedSqueezed = function() {
return this.triggerValue > TRIGGER_ON_VALUE; return this.triggerValue > TRIGGER_ON_VALUE;
}; };
this.triggerSmoothedReleased = function() {
return this.triggerValue < TRIGGER_OFF_VALUE;
};
this.triggerSqueezed = function() { this.triggerSqueezed = function() {
var triggerValue = Controller.getActionValue(this.triggerAction); var triggerValue = Controller.getActionValue(this.triggerAction);
return triggerValue > TRIGGER_ON_VALUE; return triggerValue > TRIGGER_ON_VALUE;
}; };
this.off = function() {
if (this.triggerSmoothedSqueezed()) {
this.state = STATE_SEARCHING;
return;
}
}
this.search = function() { this.search = function() {
if (!this.triggerSmoothedSqueezed()) { if (this.triggerSmoothedReleased()) {
this.state = STATE_RELEASE; this.state = STATE_RELEASE;
return; return;
} }
@ -206,6 +257,10 @@ function MyController(hand, triggerAction) {
this.state = STATE_NEAR_GRABBING; this.state = STATE_NEAR_GRABBING;
} else { } else {
if (entityIsGrabbedByOther(intersection.entityID)) {
// don't allow two people to distance grab the same object
return;
}
// the hand is far from the intersected object. go into distance-holding mode // the hand is far from the intersected object. go into distance-holding mode
this.state = STATE_DISTANCE_HOLDING; this.state = STATE_DISTANCE_HOLDING;
this.lineOn(pickRay.origin, Vec3.multiply(pickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); this.lineOn(pickRay.origin, Vec3.multiply(pickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR);
@ -244,6 +299,7 @@ function MyController(hand, triggerAction) {
}; };
this.distanceHolding = function() { this.distanceHolding = function() {
var handControllerPosition = Controller.getSpatialControlPosition(this.palm); var handControllerPosition = Controller.getSpatialControlPosition(this.palm);
var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(this.palm)); var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(this.palm));
var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, ["position", "rotation"]); var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, ["position", "rotation"]);
@ -255,11 +311,14 @@ function MyController(hand, triggerAction) {
this.handPreviousPosition = handControllerPosition; this.handPreviousPosition = handControllerPosition;
this.handPreviousRotation = handRotation; this.handPreviousRotation = handRotation;
this.actionID = NULL_ACTION_ID;
this.actionID = Entities.addAction("spring", this.grabbedEntity, { this.actionID = Entities.addAction("spring", this.grabbedEntity, {
targetPosition: this.currentObjectPosition, targetPosition: this.currentObjectPosition,
linearTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME, linearTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME,
targetRotation: this.currentObjectRotation, targetRotation: this.currentObjectRotation,
angularTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME angularTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME,
tag: getTag(),
lifetime: ACTION_LIFETIME
}); });
if (this.actionID === NULL_ACTION_ID) { if (this.actionID === NULL_ACTION_ID) {
this.actionID = null; this.actionID = null;
@ -276,10 +335,13 @@ function MyController(hand, triggerAction) {
Entities.callEntityMethod(this.grabbedEntity, "startDistantGrab"); Entities.callEntityMethod(this.grabbedEntity, "startDistantGrab");
} }
this.currentAvatarPosition = MyAvatar.position;
this.currentAvatarOrientation = MyAvatar.orientation;
}; };
this.continueDistanceHolding = function() { this.continueDistanceHolding = function() {
if (!this.triggerSmoothedSqueezed()) { if (this.triggerSmoothedReleased()) {
this.state = STATE_RELEASE; this.state = STATE_RELEASE;
return; return;
} }
@ -294,11 +356,46 @@ function MyController(hand, triggerAction) {
// the action was set up on a previous call. update the targets. // the action was set up on a previous call. update the targets.
var radius = Math.max(Vec3.distance(this.currentObjectPosition, handControllerPosition) * DISTANCE_HOLDING_RADIUS_FACTOR, DISTANCE_HOLDING_RADIUS_FACTOR); var radius = Math.max(Vec3.distance(this.currentObjectPosition, handControllerPosition) * DISTANCE_HOLDING_RADIUS_FACTOR, DISTANCE_HOLDING_RADIUS_FACTOR);
// how far did avatar move this timestep?
var currentPosition = MyAvatar.position;
var avatarDeltaPosition = Vec3.subtract(currentPosition, this.currentAvatarPosition);
this.currentAvatarPosition = currentPosition;
// How far did the avatar turn this timestep?
// Note: The following code is too long because we need a Quat.quatBetween() function
// that returns the minimum quaternion between two quaternions.
var currentOrientation = MyAvatar.orientation;
if (Quat.dot(currentOrientation, this.currentAvatarOrientation) < 0.0) {
var negativeCurrentOrientation = {
x: -currentOrientation.x,
y: -currentOrientation.y,
z: -currentOrientation.z,
w: -currentOrientation.w
};
var avatarDeltaOrientation = Quat.multiply(negativeCurrentOrientation, Quat.inverse(this.currentAvatarOrientation));
} else {
var avatarDeltaOrientation = Quat.multiply(currentOrientation, Quat.inverse(this.currentAvatarOrientation));
}
var handToAvatar = Vec3.subtract(handControllerPosition, this.currentAvatarPosition);
var objectToAvatar = Vec3.subtract(this.currentObjectPosition, this.currentAvatarPosition);
var handMovementFromTurning = Vec3.subtract(Quat.multiply(avatarDeltaOrientation, handToAvatar), handToAvatar);
var objectMovementFromTurning = Vec3.subtract(Quat.multiply(avatarDeltaOrientation, objectToAvatar), objectToAvatar);
this.currentAvatarOrientation = currentOrientation;
// how far did hand move this timestep?
var handMoved = Vec3.subtract(handControllerPosition, this.handPreviousPosition); var handMoved = Vec3.subtract(handControllerPosition, this.handPreviousPosition);
this.handPreviousPosition = handControllerPosition; this.handPreviousPosition = handControllerPosition;
// magnify the hand movement but not the change from avatar movement & rotation
handMoved = Vec3.subtract(handMoved, avatarDeltaPosition);
handMoved = Vec3.subtract(handMoved, handMovementFromTurning);
var superHandMoved = Vec3.multiply(handMoved, radius); var superHandMoved = Vec3.multiply(handMoved, radius);
// Move the object by the magnified amount and then by amount from avatar movement & rotation
var newObjectPosition = Vec3.sum(this.currentObjectPosition, superHandMoved); var newObjectPosition = Vec3.sum(this.currentObjectPosition, superHandMoved);
newObjectPosition = Vec3.sum(newObjectPosition, avatarDeltaPosition);
newObjectPosition = Vec3.sum(newObjectPosition, objectMovementFromTurning);
var deltaPosition = Vec3.subtract(newObjectPosition, this.currentObjectPosition); // meters var deltaPosition = Vec3.subtract(newObjectPosition, this.currentObjectPosition); // meters
var now = Date.now(); var now = Date.now();
var deltaTime = (now - this.currentObjectTime) / MSEC_PER_SEC; // convert to seconds var deltaTime = (now - this.currentObjectTime) / MSEC_PER_SEC; // convert to seconds
@ -318,12 +415,14 @@ function MyController(hand, triggerAction) {
targetPosition: this.currentObjectPosition, targetPosition: this.currentObjectPosition,
linearTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME, linearTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME,
targetRotation: this.currentObjectRotation, targetRotation: this.currentObjectRotation,
angularTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME angularTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME,
lifetime: ACTION_LIFETIME
}); });
}; };
this.nearGrabbing = function() { this.nearGrabbing = function() {
if (!this.triggerSmoothedSqueezed()) {
if (this.triggerSmoothedReleased()) {
this.state = STATE_RELEASE; this.state = STATE_RELEASE;
return; return;
} }
@ -344,11 +443,13 @@ function MyController(hand, triggerAction) {
var offset = Vec3.subtract(currentObjectPosition, handPosition); var offset = Vec3.subtract(currentObjectPosition, handPosition);
var offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, offsetRotation)), offset); var offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, offsetRotation)), offset);
this.actionID = NULL_ACTION_ID;
this.actionID = Entities.addAction("hold", this.grabbedEntity, { this.actionID = Entities.addAction("hold", this.grabbedEntity, {
hand: this.hand === RIGHT_HAND ? "right" : "left", hand: this.hand === RIGHT_HAND ? "right" : "left",
timeScale: NEAR_GRABBING_ACTION_TIMEFRAME, timeScale: NEAR_GRABBING_ACTION_TIMEFRAME,
relativePosition: offsetPosition, relativePosition: offsetPosition,
relativeRotation: offsetRotation relativeRotation: offsetRotation,
lifetime: ACTION_LIFETIME
}); });
if (this.actionID === NULL_ACTION_ID) { if (this.actionID === NULL_ACTION_ID) {
this.actionID = null; this.actionID = null;
@ -363,40 +464,54 @@ function MyController(hand, triggerAction) {
} }
this.currentHandControllerPosition = Controller.getSpatialControlPosition(this.palm); this.currentHandControllerTipPosition = Controller.getSpatialControlPosition(this.tip);
this.currentObjectTime = Date.now(); this.currentObjectTime = Date.now();
}; };
this.continueNearGrabbing = function() { this.continueNearGrabbing = function() {
if (!this.triggerSmoothedSqueezed()) { if (this.triggerSmoothedReleased()) {
this.state = STATE_RELEASE; this.state = STATE_RELEASE;
return; return;
} }
// keep track of the measured velocity of the held object // Keep track of the fingertip velocity to impart when we release the object
var handControllerPosition = Controller.getSpatialControlPosition(this.palm); // 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
// of it's actual offset, let's try imparting a velocity which is at a fixed radius
// from the palm.
var handControllerPosition = Controller.getSpatialControlPosition(this.tip);
var now = Date.now(); var now = Date.now();
var deltaPosition = Vec3.subtract(handControllerPosition, this.currentHandControllerPosition); // meters var deltaPosition = Vec3.subtract(handControllerPosition, this.currentHandControllerTipPosition); // meters
var deltaTime = (now - this.currentObjectTime) / MSEC_PER_SEC; // convert to seconds var deltaTime = (now - this.currentObjectTime) / MSEC_PER_SEC; // convert to seconds
this.computeReleaseVelocity(deltaPosition, deltaTime, true); this.computeReleaseVelocity(deltaPosition, deltaTime, true);
this.currentHandControllerPosition = handControllerPosition; this.currentHandControllerTipPosition = handControllerPosition;
this.currentObjectTime = now; this.currentObjectTime = now;
Entities.callEntityMethod(this.grabbedEntity, "continueNearGrab"); Entities.callEntityMethod(this.grabbedEntity, "continueNearGrab");
Entities.updateAction(this.grabbedEntity, this.actionID, {lifetime: ACTION_LIFETIME});
}; };
this.nearGrabbingNonColliding = function() { this.nearGrabbingNonColliding = function() {
if (!this.triggerSmoothedSqueezed()) { if (this.triggerSmoothedReleased()) {
this.state = STATE_RELEASE; this.state = STATE_RELEASE;
return; return;
} }
if (this.hand === RIGHT_HAND) {
Entities.callEntityMethod(this.grabbedEntity, "setRightHand");
} else {
Entities.callEntityMethod(this.grabbedEntity, "setLeftHand");
}
Entities.callEntityMethod(this.grabbedEntity, "startNearGrabNonColliding"); Entities.callEntityMethod(this.grabbedEntity, "startNearGrabNonColliding");
this.state = STATE_CONTINUE_NEAR_GRABBING_NON_COLLIDING; this.state = STATE_CONTINUE_NEAR_GRABBING_NON_COLLIDING;
}; };
this.continueNearGrabbingNonColliding = function() { this.continueNearGrabbingNonColliding = function() {
if (!this.triggerSmoothedSqueezed()) { if (this.triggerSmoothedReleased()) {
this.state = STATE_RELEASE; this.state = STATE_RELEASE;
return; return;
} }
@ -459,17 +574,14 @@ function MyController(hand, triggerAction) {
}; };
this.startTouch = function(entityID) { this.startTouch = function(entityID) {
// print('START TOUCH' + entityID);
Entities.callEntityMethod(entityID, "startTouch"); Entities.callEntityMethod(entityID, "startTouch");
}; };
this.continueTouch = function(entityID) { this.continueTouch = function(entityID) {
// print('CONTINUE TOUCH' + entityID);
Entities.callEntityMethod(entityID, "continueTouch"); Entities.callEntityMethod(entityID, "continueTouch");
}; };
this.stopTouch = function(entityID) { this.stopTouch = function(entityID) {
// print('STOP TOUCH' + entityID);
Entities.callEntityMethod(entityID, "stopTouch"); Entities.callEntityMethod(entityID, "stopTouch");
}; };
@ -491,10 +603,13 @@ function MyController(hand, triggerAction) {
}; };
this.release = function() { this.release = function() {
this.lineOff(); this.lineOff();
if (this.grabbedEntity !== null && this.actionID !== null) { if (this.grabbedEntity !== null) {
if(this.actionID !== null) {
Entities.deleteAction(this.grabbedEntity, this.actionID); Entities.deleteAction(this.grabbedEntity, this.actionID);
}
Entities.callEntityMethod(this.grabbedEntity, "releaseGrab"); Entities.callEntityMethod(this.grabbedEntity, "releaseGrab");
} }
@ -508,7 +623,7 @@ function MyController(hand, triggerAction) {
this.grabbedVelocity = ZERO_VEC; this.grabbedVelocity = ZERO_VEC;
this.grabbedEntity = null; this.grabbedEntity = null;
this.actionID = null; this.actionID = null;
this.state = STATE_SEARCHING; this.state = STATE_OFF;
}; };
this.cleanup = function() { this.cleanup = function() {

View file

@ -0,0 +1,10 @@
float getProceduralColors(inout vec3 diffuse, inout vec3 specular, inout float shininess) {
specular = _modelNormal.rgb;
if (any(lessThan(specular, vec3(0.0)))) {
specular = vec3(1.0) + specular;
}
diffuse = vec3(1.0, 1.0, 1.0);
return 1.0;
}

View file

@ -0,0 +1,42 @@
function avatarRelativePosition(position) {
return Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, position));
}
ColorCube = function() {};
ColorCube.prototype.NAME = "ColorCube";
ColorCube.prototype.POSITION = { x: 0, y: 0.5, z: -0.5 };
ColorCube.prototype.USER_DATA = { ProceduralEntity: {
version: 2, shaderUrl: Script.resolvePath("colorCube.fs"),
} };
// Clear any previous entities within 50 meters
ColorCube.prototype.clear = function() {
var ids = Entities.findEntities(MyAvatar.position, 50);
var that = this;
ids.forEach(function(id) {
var properties = Entities.getEntityProperties(id);
if (properties.name == that.NAME) {
Entities.deleteEntity(id);
}
}, this);
}
ColorCube.prototype.create = function() {
var that = this;
var size = HMD.ipd;
var id = Entities.addEntity({
type: "Box",
position: avatarRelativePosition(that.POSITION),
name: that.NAME,
color: that.COLOR,
ignoreCollisions: true,
collisionsWillMove: false,
dimensions: { x: size, y: size, z: size },
lifetime: 3600,
userData: JSON.stringify(that.USER_DATA)
});
}
var colorCube = new ColorCube();
colorCube.clear();
colorCube.create();

View file

@ -9,7 +9,7 @@
// Distributed under the Apache License, Version 2.0. // Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// //
/*global print, MouseMyAvatar, 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, pointInExtents, vec3equal, setEntityCustomData, getEntityCustomData */ /*global print, Mouse, 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, pointInExtents, vec3equal, setEntityCustomData, getEntityCustomData */
Script.include("libraries/utils.js"); Script.include("libraries/utils.js");
// objects that appear smaller than this can't be grabbed // objects that appear smaller than this can't be grabbed
@ -32,6 +32,45 @@ var defaultGrabbableData = {
grabbable: true grabbable: true
}; };
var MAX_SOLID_ANGLE = 0.01; // objects that appear smaller than this can't be grabbed
var ZERO_VEC3 = {
x: 0,
y: 0,
z: 0
};
var IDENTITY_QUAT = {
x: 0,
y: 0,
z: 0,
w: 0
};
var ACTION_LIFETIME = 120; // 2 minutes
function getTag() {
return "grab-" + MyAvatar.sessionUUID;
}
function entityIsGrabbedByOther(entityID) {
var actionIDs = Entities.getActionIDs(entityID);
var actionIndex;
var actionID;
var actionArguments;
var tag;
for (actionIndex = 0; actionIndex < actionIDs.length; actionIndex++) {
actionID = actionIDs[actionIndex];
actionArguments = Entities.getActionArguments(entityID, actionID);
tag = actionArguments.tag;
if (tag === getTag()) {
continue;
}
if (tag.slice(0, 5) === "grab-") {
return true;
}
}
return false;
}
// helper function // helper function
function mouseIntersectionWithPlane(pointOnPlane, planeNormal, event, maxDistance) { function mouseIntersectionWithPlane(pointOnPlane, planeNormal, event, maxDistance) {
var cameraPosition = Camera.getPosition(); var cameraPosition = Camera.getPosition();
@ -371,7 +410,10 @@ Grabber.prototype.moveEvent = function(event) {
} }
this.currentPosition = entityProperties.position; this.currentPosition = entityProperties.position;
var actionArgs = {}; var actionArgs = {
tag: getTag(),
lifetime: ACTION_LIFETIME
};
if (this.mode === "rotate") { if (this.mode === "rotate") {
var drag = mouse.getDrag(); var drag = mouse.getDrag();
@ -386,10 +428,14 @@ Grabber.prototype.moveEvent = function(event) {
// var qZero = entityProperties.rotation; // var qZero = entityProperties.rotation;
//var qZero = this.lastRotation; //var qZero = this.lastRotation;
this.lastRotation = Quat.multiply(deltaQ, this.lastRotation); this.lastRotation = Quat.multiply(deltaQ, this.lastRotation);
actionArgs = { actionArgs = {
targetRotation: this.lastRotation, targetRotation: this.lastRotation,
angularTimeScale: 0.1 angularTimeScale: 0.1,
tag: getTag(),
lifetime: ACTION_LIFETIME
}; };
} else { } else {
var newPointOnPlane; var newPointOnPlane;
if (this.mode === "verticalCylinder") { if (this.mode === "verticalCylinder") {
@ -417,16 +463,22 @@ Grabber.prototype.moveEvent = function(event) {
} }
} }
this.targetPosition = Vec3.subtract(newPointOnPlane, this.offset); this.targetPosition = Vec3.subtract(newPointOnPlane, this.offset);
actionArgs = { actionArgs = {
targetPosition: this.targetPosition, targetPosition: this.targetPosition,
linearTimeScale: 0.1 linearTimeScale: 0.1,
tag: getTag(),
lifetime: ACTION_LIFETIME
}; };
beacon.updatePosition(this.targetPosition); beacon.updatePosition(this.targetPosition);
} }
if (!this.actionID) { if (!this.actionID) {
if (!entityIsGrabbedByOther(this.entityID)) {
this.actionID = Entities.addAction("spring", this.entityID, actionArgs); this.actionID = Entities.addAction("spring", this.entityID, actionArgs);
}
} else { } else {
Entities.updateAction(this.entityID, this.actionID, actionArgs); Entities.updateAction(this.entityID, this.actionID, actionArgs);
} }

View file

@ -0,0 +1,43 @@
//
// createHoop.js
// examples/entityScripts
//
// Created by James B. Pollack on 9/29/2015
// Copyright 2015 High Fidelity, Inc.
//
// This is a script that creates a persistent basketball hoop with a working collision hull. Feel free to move it.
// Run basketball.js to make a basketball.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
/*global MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt */
var hoopURL = "http://hifi-public.s3.amazonaws.com/models/basketball_hoop/basketball_hoop.fbx";
var hoopCollisionHullURL = "http://hifi-public.s3.amazonaws.com/models/basketball_hoop/basketball_hoop_collision_hull.obj";
var hoopStartPosition =
Vec3.sum(MyAvatar.position,
Vec3.multiplyQbyV(MyAvatar.orientation, {
x: 0,
y: 0.0,
z: -2
}));
var hoop = Entities.addEntity({
type: "Model",
modelURL: hoopURL,
position: hoopStartPosition,
shapeType: 'compound',
gravity: {
x: 0,
y: -9.8,
z: 0
},
dimensions: {
x: 1.89,
y: 3.99,
z: 3.79
},
compoundShapeURL: hoopCollisionHullURL
});

View file

@ -0,0 +1,43 @@
// createPingPongGun.js
//
// Script Type: Entity Spawner
// Created by James B. Pollack on 9/30/2015
// Copyright 2015 High Fidelity, Inc.
//
// This script creates a gun that shoots ping pong balls when you pull the trigger on a hand controller.
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
/*global MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt */
Script.include("../../utilities.js");
var scriptURL = Script.resolvePath('pingPongGun.js');
var MODEL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/ping_pong_gun.fbx'
var COLLISION_HULL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/ping_pong_gun_collision_hull.obj';
var center = Vec3.sum(Vec3.sum(MyAvatar.position, {
x: 0,
y: 0.5,
z: 0
}), Vec3.multiply(0.5, Quat.getFront(Camera.getOrientation())));
var pingPongGun = Entities.addEntity({
type: "Model",
modelURL: MODEL_URL,
shapeType: 'compound',
compoundShapeURL: COLLISION_HULL_URL,
script: scriptURL,
position: center,
dimensions: {
x:0.67,
y: 0.14,
z: 0.09
},
collisionsWillMove: true,
});
function cleanUp() {
Entities.deleteEntity(pingPongGun);
}
Script.scriptEnding.connect(cleanUp);

View file

@ -0,0 +1,160 @@
// pingPongGun.js
//
// Script Type: Entity
// Created by James B. Pollack @imgntn on 9/21/2015
// Copyright 2015 High Fidelity, Inc.
//
// This script shoots a ping pong ball.
// 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 */
(function() {
Script.include("../../libraries/utils.js");
var SHOOTING_SOUND_URL = 'http://hifi-public.s3.amazonaws.com/sounds/ping_pong_gun/pong_sound.wav';
function PingPongGun() {
return;
}
//if the trigger value goes below this value, reload the gun.
var RELOAD_THRESHOLD = 0.95;
var GUN_TIP_FWD_OFFSET = 0.45;
var GUN_TIP_UP_OFFSET = 0.040;
var GUN_FORCE = 15;
var BALL_RESTITUTION = 0.6;
var BALL_LINEAR_DAMPING = 0.4;
var BALL_GRAVITY = {
x: 0,
y: -9.8,
z: 0
};
var BALL_DIMENSIONS = {
x: 0.04,
y: 0.04,
z: 0.04
}
var BALL_COLOR = {
red: 255,
green: 255,
blue: 255
}
PingPongGun.prototype = {
hand: null,
whichHand: null,
gunTipPosition: null,
canShoot: false,
canShootTimeout: null,
setRightHand: function() {
this.hand = 'RIGHT';
},
setLeftHand: function() {
this.hand = 'LEFT';
},
startNearGrab: function() {
this.setWhichHand();
},
setWhichHand: function() {
this.whichHand = this.hand;
},
continueNearGrab: function() {
if (this.whichHand === null) {
//only set the active hand once -- if we always read the current hand, our 'holding' hand will get overwritten
this.setWhichHand();
} else {
if (this.canShootTimeout !== null) {
Script.clearTimeout(this.canShootTimeout);
}
this.checkTriggerPressure(this.whichHand);
}
},
releaseGrab: function() {
var _t = this;
this.canShootTimeout = Script.setTimeout(function() {
_t.canShoot = false;
}, 250)
},
checkTriggerPressure: function(gunHand) {
var handClickString = gunHand + "_HAND_CLICK";
var handClick = Controller.findAction(handClickString);
this.triggerValue = Controller.getActionValue(handClick);
if (this.triggerValue < RELOAD_THRESHOLD) {
// print('RELOAD');
this.canShoot = true;
} else if (this.triggerValue >= RELOAD_THRESHOLD && this.canShoot === true) {
var gunProperties = Entities.getEntityProperties(this.entityID, ["position", "rotation"]);
this.shootBall(gunProperties);
this.canShoot = false;
}
return;
},
shootBall: function(gunProperties) {
var forwardVec = Quat.getFront(Quat.multiply(gunProperties.rotation, Quat.fromPitchYawRollDegrees(0, -90, 0)));
forwardVec = Vec3.normalize(forwardVec);
forwardVec = Vec3.multiply(forwardVec, GUN_FORCE);
var properties = {
type: 'Sphere',
color: BALL_COLOR,
dimensions: BALL_DIMENSIONS,
linearDamping: BALL_LINEAR_DAMPING,
gravity: BALL_GRAVITY,
restitution: BALL_RESTITUTION,
collisionsWillMove: true,
rotation: gunProperties.rotation,
position: this.getGunTipPosition(gunProperties),
velocity: forwardVec,
lifetime: 10
};
Entities.addEntity(properties);
this.playSoundAtCurrentPosition(gunProperties.position);
},
playSoundAtCurrentPosition: function(position) {
var audioProperties = {
volume: 0.1,
position: position
};
Audio.playSound(this.SHOOTING_SOUND, audioProperties);
},
getGunTipPosition: function(properties) {
//the tip of the gun is going to be in a different place than the center, so we move in space relative to the model to find that position
var frontVector = Quat.getRight(properties.rotation);
var frontOffset = Vec3.multiply(frontVector, GUN_TIP_FWD_OFFSET);
var upVector = Quat.getRight(properties.rotation);
var upOffset = Vec3.multiply(upVector, GUN_TIP_UP_OFFSET);
var gunTipPosition = Vec3.sum(properties.position, frontOffset);
gunTipPosition = Vec3.sum(gunTipPosition, upOffset);
return gunTipPosition;
},
preload: function(entityID) {
this.entityID = entityID;
this.SHOOTING_SOUND = SoundCache.getSound(SHOOTING_SOUND_URL);
}
};
// entity scripts always need to return a newly constructed object of our type
return new PingPongGun();
});

View file

@ -10,22 +10,21 @@
// //
var array = []; var array = [];
var buffer = "\n\n\n\n\n======= JS API list =======";
function listKeys(string, object) { function listKeys(string, object) {
if (string == "listKeys" || string == "array" || string == "buffer" || string == "i") { if (string === "listKeys" || string === "array" || string === "buffer" || string === "i") {
return; return;
} }
if (typeof(object) != "object") { if (typeof(object) !== "object" || object === null) {
array.push(string + " " + typeof(object)); array.push(string + " " + typeof(object));
return; return;
} }
var keys = Object.keys(object); var keys = Object.keys(object);
for (var i = 0; i < keys.length; ++i) { for (var i = 0; i < keys.length; ++i) {
if (string == "") { if (string === "") {
listKeys(keys[i], object[keys[i]]); listKeys(keys[i], object[keys[i]]);
} else { } else if (keys[i] !== "parent") {
listKeys(string + "." + keys[i], object[keys[i]]); listKeys(string + "." + keys[i], object[keys[i]]);
} }
} }
@ -34,9 +33,10 @@ function listKeys(string, object) {
listKeys("", this); listKeys("", this);
array.sort(); array.sort();
var buffer = "\n======= JS API list =======";
for (var i = 0; i < array.length; ++i) { for (var i = 0; i < array.length; ++i) {
buffer = buffer + "\n" + array[i]; buffer += "\n" + array[i];
} }
buffer = buffer + "\n========= API END =========\n\n\n\n\n"; buffer += "\n========= API END =========\n";
print(buffer); print(buffer);

View file

@ -26,13 +26,14 @@
{ {
"jointName": "Neck", "jointName": "Neck",
"positionVar": "neckPosition", "positionVar": "neckPosition",
"rotationVar": "neckRotation" "rotationVar": "neckRotation",
"typeVar": "headAndNeckType"
}, },
{ {
"jointName": "Head", "jointName": "Head",
"positionVar": "headPosition", "positionVar": "headPosition",
"rotationVar": "headRotation", "rotationVar": "headRotation",
"typeVar": "headType" "typeVar": "headAndNeckType"
} }
] ]
}, },

View file

@ -196,7 +196,6 @@ Hifi.VrMenu {
function insertItem(menu, beforeItem, newMenuItem) { function insertItem(menu, beforeItem, newMenuItem) {
for (var i = 0; i < menu.items.length; ++i) { for (var i = 0; i < menu.items.length; ++i) {
console.log(menu.items[i]);
if (menu.items[i] === beforeItem) { if (menu.items[i] === beforeItem) {
return menu.insertItem(i, newMenuItem); return menu.insertItem(i, newMenuItem);
} }

View file

@ -43,6 +43,9 @@ EntityActionPointer InterfaceActionFactory::factory(EntityActionType type,
if (action) { if (action) {
bool ok = action->updateArguments(arguments); bool ok = action->updateArguments(arguments);
if (ok) { if (ok) {
if (action->lifetimeIsOver()) {
return nullptr;
}
return action; return action;
} }
} }
@ -63,5 +66,9 @@ EntityActionPointer InterfaceActionFactory::factoryBA(EntityItemPointer ownerEnt
if (action) { if (action) {
action->deserialize(data); action->deserialize(data);
} }
if (action->lifetimeIsOver()) {
return nullptr;
}
return action; return action;
} }

View file

@ -446,9 +446,9 @@ Menu::Menu() {
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::FixGaze, 0, false); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::FixGaze, 0, false);
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::EnableAvatarUpdateThreading, 0, false, addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::EnableAvatarUpdateThreading, 0, false,
qApp, SLOT(setAvatarUpdateThreading(bool))); qApp, SLOT(setAvatarUpdateThreading(bool)));
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::EnableRigAnimations, 0, true, addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::EnableRigAnimations, 0, false,
avatar, SLOT(setEnableRigAnimations(bool))); avatar, SLOT(setEnableRigAnimations(bool)));
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::EnableAnimGraph, 0, false, addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::EnableAnimGraph, 0, true,
avatar, SLOT(setEnableAnimGraph(bool))); avatar, SLOT(setEnableAnimGraph(bool)));
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AnimDebugDrawBindPose, 0, false, addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AnimDebugDrawBindPose, 0, false,
avatar, SLOT(setEnableDebugDrawBindPose(bool))); avatar, SLOT(setEnableDebugDrawBindPose(bool)));

View file

@ -84,6 +84,9 @@ void AvatarActionHold::updateActionWorker(float deltaTimeStep) {
bool AvatarActionHold::updateArguments(QVariantMap arguments) { bool AvatarActionHold::updateArguments(QVariantMap arguments) {
if (!ObjectAction::updateArguments(arguments)) {
return false;
}
bool ok = true; bool ok = true;
glm::vec3 relativePosition = glm::vec3 relativePosition =
EntityActionInterface::extractVec3Argument("hold", arguments, "relativePosition", ok, false); EntityActionInterface::extractVec3Argument("hold", arguments, "relativePosition", ok, false);
@ -134,7 +137,7 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) {
QVariantMap AvatarActionHold::getArguments() { QVariantMap AvatarActionHold::getArguments() {
QVariantMap arguments; QVariantMap arguments = ObjectAction::getArguments();
withReadLock([&]{ withReadLock([&]{
if (!_mine) { if (!_mine) {
arguments = ObjectActionSpring::getArguments(); arguments = ObjectActionSpring::getArguments();

View file

@ -153,7 +153,8 @@ void MyAvatar::reset() {
// This should be simpler when we have only graph animations always on. // This should be simpler when we have only graph animations always on.
bool isRig = _rig->getEnableRig(); bool isRig = _rig->getEnableRig();
bool isGraph = _rig->getEnableAnimGraph(); // seting rig animation to true, below, will clear the graph animation menu item, so grab it now.
bool isGraph = _rig->getEnableAnimGraph() || Menu::getInstance()->isOptionChecked(MenuOption::EnableAnimGraph);
qApp->setRawAvatarUpdateThreading(false); qApp->setRawAvatarUpdateThreading(false);
_rig->disableHands = true; _rig->disableHands = true;
setEnableRigAnimations(true); setEnableRigAnimations(true);
@ -270,6 +271,25 @@ glm::mat4 MyAvatar::getSensorToWorldMatrix() const {
return _sensorToWorldMatrix; return _sensorToWorldMatrix;
} }
// returns true if pos is OUTSIDE of the vertical capsule
// where the middle cylinder length is defined by capsuleLen and the radius by capsuleRad.
static bool capsuleCheck(const glm::vec3& pos, float capsuleLen, float capsuleRad) {
const float halfCapsuleLen = capsuleLen / 2.0f;
if (fabs(pos.y) <= halfCapsuleLen) {
// cylinder check for middle capsule
glm::vec2 horizPos(pos.x, pos.z);
return glm::length(horizPos) > capsuleRad;
} else if (pos.y > halfCapsuleLen) {
glm::vec3 center(0.0f, halfCapsuleLen, 0.0f);
return glm::length(center - pos) > capsuleRad;
} else if (pos.y < halfCapsuleLen) {
glm::vec3 center(0.0f, -halfCapsuleLen, 0.0f);
return glm::length(center - pos) > capsuleRad;
} else {
return false;
}
}
// Pass a recent sample of the HMD to the avatar. // Pass a recent sample of the HMD to the avatar.
// This can also update the avatar's position to follow the HMD // This can also update the avatar's position to follow the HMD
// as it moves through the world. // as it moves through the world.
@ -288,11 +308,14 @@ void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) {
_hmdSensorOrientation = glm::quat_cast(hmdSensorMatrix); _hmdSensorOrientation = glm::quat_cast(hmdSensorMatrix);
const float STRAIGHTING_LEAN_DURATION = 0.5f; // seconds const float STRAIGHTING_LEAN_DURATION = 0.5f; // seconds
const float STRAIGHTING_LEAN_THRESHOLD = 0.2f; // meters
// define a vertical capsule
const float STRAIGHTING_LEAN_CAPSULE_RADIUS = 0.2f; // meters
const float STRAIGHTING_LEAN_CAPSULE_LENGTH = 0.05f; // length of the cylinder part of the capsule in meters.
auto newBodySensorMatrix = deriveBodyFromHMDSensor(); auto newBodySensorMatrix = deriveBodyFromHMDSensor();
glm::vec3 diff = extractTranslation(newBodySensorMatrix) - extractTranslation(_bodySensorMatrix); glm::vec3 diff = extractTranslation(newBodySensorMatrix) - extractTranslation(_bodySensorMatrix);
if (!_straightingLean && glm::length(diff) > STRAIGHTING_LEAN_THRESHOLD) { if (!_straightingLean && capsuleCheck(diff, STRAIGHTING_LEAN_CAPSULE_LENGTH, STRAIGHTING_LEAN_CAPSULE_RADIUS)) {
// begin homing toward derived body position. // begin homing toward derived body position.
_straightingLean = true; _straightingLean = true;
@ -1764,6 +1787,12 @@ void MyAvatar::goToLocation(const glm::vec3& newPosition,
} }
void MyAvatar::updateMotionBehaviorFromMenu() { void MyAvatar::updateMotionBehaviorFromMenu() {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "updateMotionBehaviorFromMenu");
return;
}
Menu* menu = Menu::getInstance(); Menu* menu = Menu::getInstance();
if (menu->isOptionChecked(MenuOption::KeyboardMotorControl)) { if (menu->isOptionChecked(MenuOption::KeyboardMotorControl)) {
_motionBehaviors |= AVATAR_MOTION_KEYBOARD_MOTOR_ENABLED; _motionBehaviors |= AVATAR_MOTION_KEYBOARD_MOTOR_ENABLED;
@ -1847,13 +1876,39 @@ glm::mat4 MyAvatar::deriveBodyFromHMDSensor() const {
const glm::quat hmdOrientation = getHMDSensorOrientation(); const glm::quat hmdOrientation = getHMDSensorOrientation();
const glm::quat hmdOrientationYawOnly = cancelOutRollAndPitch(hmdOrientation); const glm::quat hmdOrientationYawOnly = cancelOutRollAndPitch(hmdOrientation);
// In sensor space, figure out where the avatar body should be, const glm::vec3 DEFAULT_RIGHT_EYE_POS(-0.3f, 1.6f, 0.0f);
// by applying offsets from the avatar's neck & head joints. const glm::vec3 DEFAULT_LEFT_EYE_POS(0.3f, 1.6f, 0.0f);
vec3 localEyes = _skeletonModel.getDefaultEyeModelPosition(); const glm::vec3 DEFAULT_NECK_POS(0.0f, 1.5f, 0.0f);
vec3 localNeck(0.0f, 0.48f, 0.0f); // start with some kind of guess if the skeletonModel is not loaded yet. const glm::vec3 DEFAULT_HIPS_POS(0.0f, 1.0f, 0.0f);
_skeletonModel.getLocalNeckPosition(localNeck);
vec3 localEyes, localNeck;
if (!_debugDrawSkeleton) {
const glm::quat rotY180 = glm::angleAxis((float)PI, glm::vec3(0.0f, 1.0f, 0.0f));
localEyes = rotY180 * (((DEFAULT_RIGHT_EYE_POS + DEFAULT_LEFT_EYE_POS) / 2.0f) - DEFAULT_HIPS_POS);
localNeck = rotY180 * (DEFAULT_NECK_POS - DEFAULT_HIPS_POS);
} else {
// TODO: At the moment MyAvatar does not have access to the rig, which has the skeleton, which has the bind poses.
// for now use the _debugDrawSkeleton, which is initialized with the same FBX model as the rig.
// TODO: cache these indices.
int rightEyeIndex = _debugDrawSkeleton->nameToJointIndex("RightEye");
int leftEyeIndex = _debugDrawSkeleton->nameToJointIndex("LeftEye");
int neckIndex = _debugDrawSkeleton->nameToJointIndex("Neck");
int hipsIndex = _debugDrawSkeleton->nameToJointIndex("Hips");
glm::vec3 absRightEyePos = rightEyeIndex != -1 ? _debugDrawSkeleton->getAbsoluteBindPose(rightEyeIndex).trans : DEFAULT_RIGHT_EYE_POS;
glm::vec3 absLeftEyePos = leftEyeIndex != -1 ? _debugDrawSkeleton->getAbsoluteBindPose(leftEyeIndex).trans : DEFAULT_LEFT_EYE_POS;
glm::vec3 absNeckPos = neckIndex != -1 ? _debugDrawSkeleton->getAbsoluteBindPose(neckIndex).trans : DEFAULT_NECK_POS;
glm::vec3 absHipsPos = neckIndex != -1 ? _debugDrawSkeleton->getAbsoluteBindPose(hipsIndex).trans : DEFAULT_HIPS_POS;
const glm::quat rotY180 = glm::angleAxis((float)PI, glm::vec3(0.0f, 1.0f, 0.0f));
localEyes = rotY180 * (((absRightEyePos + absLeftEyePos) / 2.0f) - absHipsPos);
localNeck = rotY180 * (absNeckPos - absHipsPos);
}
// apply simplistic head/neck model // apply simplistic head/neck model
// figure out where the avatar body should be by applying offsets from the avatar's neck & head joints.
// eyeToNeck offset is relative full HMD orientation. // eyeToNeck offset is relative full HMD orientation.
// while neckToRoot offset is only relative to HMDs yaw. // while neckToRoot offset is only relative to HMDs yaw.
glm::vec3 eyeToNeck = hmdOrientation * (localNeck - localEyes); glm::vec3 eyeToNeck = hmdOrientation * (localNeck - localEyes);

View file

@ -193,7 +193,7 @@ public slots:
glm::vec3 getThrust() { return _thrust; }; glm::vec3 getThrust() { return _thrust; };
void setThrust(glm::vec3 newThrust) { _thrust = newThrust; } void setThrust(glm::vec3 newThrust) { _thrust = newThrust; }
void updateMotionBehaviorFromMenu(); Q_INVOKABLE void updateMotionBehaviorFromMenu();
glm::vec3 getLeftPalmPosition(); glm::vec3 getLeftPalmPosition();
glm::vec3 getLeftPalmVelocity(); glm::vec3 getLeftPalmVelocity();

View file

@ -10,7 +10,7 @@
// //
#include "HMDScriptingInterface.h" #include "HMDScriptingInterface.h"
#include "display-plugins/DisplayPlugin.h"
#include <avatar/AvatarManager.h> #include <avatar/AvatarManager.h>
HMDScriptingInterface& HMDScriptingInterface::getInstance() { HMDScriptingInterface& HMDScriptingInterface::getInstance() {
@ -53,3 +53,7 @@ QScriptValue HMDScriptingInterface::getHUDLookAtPosition3D(QScriptContext* conte
} }
return QScriptValue::NullValue; return QScriptValue::NullValue;
} }
float HMDScriptingInterface::getIPD() const {
return Application::getInstance()->getActiveDisplayPlugin()->getIPD();
}

View file

@ -20,6 +20,7 @@ class HMDScriptingInterface : public QObject {
Q_OBJECT Q_OBJECT
Q_PROPERTY(bool magnifier READ getMagnifier) Q_PROPERTY(bool magnifier READ getMagnifier)
Q_PROPERTY(bool active READ isHMDMode) Q_PROPERTY(bool active READ isHMDMode)
Q_PROPERTY(float ipd READ getIPD)
public: public:
static HMDScriptingInterface& getInstance(); static HMDScriptingInterface& getInstance();
@ -33,6 +34,7 @@ private:
HMDScriptingInterface() {}; HMDScriptingInterface() {};
bool getMagnifier() const { return Application::getInstance()->getApplicationCompositor().hasMagnifier(); }; bool getMagnifier() const { return Application::getInstance()->getApplicationCompositor().hasMagnifier(); };
bool isHMDMode() const { return Application::getInstance()->isHMDMode(); } bool isHMDMode() const { return Application::getInstance()->isHMDMode(); }
float getIPD() const;
bool getHUDLookAtPosition3D(glm::vec3& result) const; bool getHUDLookAtPosition3D(glm::vec3& result) const;

View file

@ -313,6 +313,8 @@ void ApplicationCompositor::displayOverlayTextureHmd(RenderArgs* renderArgs, int
glm::mat4 overlayXfm; glm::mat4 overlayXfm;
_modelTransform.getMatrix(overlayXfm); _modelTransform.getMatrix(overlayXfm);
// Only render the hand pointers if the HandMouseInput is enabled
if (Menu::getInstance()->isOptionChecked(MenuOption::HandMouseInput)) {
MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar(); MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
for (int i = 0; i < (int)myAvatar->getHand()->getNumPalms(); i++) { for (int i = 0; i < (int)myAvatar->getHand()->getNumPalms(); i++) {
PalmData& palm = myAvatar->getHand()->getPalms()[i]; PalmData& palm = myAvatar->getHand()->getPalms()[i];
@ -327,6 +329,7 @@ void ApplicationCompositor::displayOverlayTextureHmd(RenderArgs* renderArgs, int
geometryCache->renderUnitQuad(batch, glm::vec4(1), _reticleQuad); geometryCache->renderUnitQuad(batch, glm::vec4(1), _reticleQuad);
} }
} }
}
//Mouse Pointer //Mouse Pointer
if (_reticleActive[MOUSE]) { if (_reticleActive[MOUSE]) {

View file

@ -89,7 +89,7 @@ static int findRootJointInSkeleton(AnimSkeleton::ConstPointer skeleton, int inde
return rootIndex; return rootIndex;
} }
void AnimInverseKinematics::computeTargets(const AnimVariantMap& animVars, std::vector<IKTarget>& targets) { void AnimInverseKinematics::computeTargets(const AnimVariantMap& animVars, std::vector<IKTarget>& targets, const AnimPoseVec& underPoses) {
// build a list of valid targets from _targetVarVec and animVars // build a list of valid targets from _targetVarVec and animVars
_maxTargetIndex = -1; _maxTargetIndex = -1;
bool removeUnfoundJoints = false; bool removeUnfoundJoints = false;
@ -107,7 +107,7 @@ void AnimInverseKinematics::computeTargets(const AnimVariantMap& animVars, std::
} }
} else { } else {
IKTarget target; IKTarget target;
AnimPose defaultPose = _skeleton->getAbsolutePose(targetVar.jointIndex, _relativePoses); AnimPose defaultPose = _skeleton->getAbsolutePose(targetVar.jointIndex, underPoses);
target.pose.trans = animVars.lookup(targetVar.positionVar, defaultPose.trans); target.pose.trans = animVars.lookup(targetVar.positionVar, defaultPose.trans);
target.pose.rot = animVars.lookup(targetVar.rotationVar, defaultPose.rot); target.pose.rot = animVars.lookup(targetVar.rotationVar, defaultPose.rot);
target.setType(animVars.lookup(targetVar.typeVar, QString(""))); target.setType(animVars.lookup(targetVar.typeVar, QString("")));
@ -154,7 +154,6 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vector<I
do { do {
int lowestMovedIndex = _relativePoses.size(); int lowestMovedIndex = _relativePoses.size();
for (auto& target: targets) { for (auto& target: targets) {
int tipIndex = target.index;
if (target.type == IKTarget::Type::RotationOnly) { if (target.type == IKTarget::Type::RotationOnly) {
// the final rotation will be enforced after the iterations // the final rotation will be enforced after the iterations
continue; continue;
@ -162,6 +161,7 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vector<I
AnimPose targetPose = target.pose; AnimPose targetPose = target.pose;
// cache tip absolute transform // cache tip absolute transform
int tipIndex = target.index;
glm::vec3 tipPosition = absolutePoses[tipIndex].trans; glm::vec3 tipPosition = absolutePoses[tipIndex].trans;
glm::quat tipRotation = absolutePoses[tipIndex].rot; glm::quat tipRotation = absolutePoses[tipIndex].rot;
@ -288,6 +288,17 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vector<I
} }
} while (numLoops < MAX_IK_LOOPS); } while (numLoops < MAX_IK_LOOPS);
/* KEEP: example code for measuring endeffector error of IK solution
for (uint32_t i = 0; i < targets.size(); ++i) {
auto& target = targets[i];
if (target.type == IKTarget::Type::RotationOnly) {
continue;
}
glm::vec3 tipPosition = absolutePoses[target.index].trans;
std::cout << i << " IK error = " << glm::distance(tipPosition, target.pose.trans) << std::endl; // adebug
}
*/
// finally set the relative rotation of each tip to agree with absolute target rotation // finally set the relative rotation of each tip to agree with absolute target rotation
for (auto& target: targets) { for (auto& target: targets) {
int tipIndex = target.index; int tipIndex = target.index;
@ -312,25 +323,8 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vector<I
//virtual //virtual
const AnimPoseVec& AnimInverseKinematics::evaluate(const AnimVariantMap& animVars, float dt, AnimNode::Triggers& triggersOut) { const AnimPoseVec& AnimInverseKinematics::evaluate(const AnimVariantMap& animVars, float dt, AnimNode::Triggers& triggersOut) {
if (!_relativePoses.empty()) { // don't call this function, call overlay() instead
// build a list of targets from _targetVarVec assert(false);
std::vector<IKTarget> targets;
computeTargets(animVars, targets);
if (targets.empty()) {
// no IK targets but still need to enforce constraints
std::map<int, RotationConstraint*>::iterator constraintItr = _constraints.begin();
while (constraintItr != _constraints.end()) {
int index = constraintItr->first;
glm::quat rotation = _relativePoses[index].rot;
constraintItr->second->apply(rotation);
_relativePoses[index].rot = rotation;
++constraintItr;
}
} else {
solveWithCyclicCoordinateDescent(targets);
}
}
return _relativePoses; return _relativePoses;
} }
@ -356,7 +350,27 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars
} }
} }
} }
return evaluate(animVars, dt, triggersOut);
if (!_relativePoses.empty()) {
// build a list of targets from _targetVarVec
std::vector<IKTarget> targets;
computeTargets(animVars, targets, underPoses);
if (targets.empty()) {
// no IK targets but still need to enforce constraints
std::map<int, RotationConstraint*>::iterator constraintItr = _constraints.begin();
while (constraintItr != _constraints.end()) {
int index = constraintItr->first;
glm::quat rotation = _relativePoses[index].rot;
constraintItr->second->apply(rotation);
_relativePoses[index].rot = rotation;
++constraintItr;
}
} else {
solveWithCyclicCoordinateDescent(targets);
}
}
return _relativePoses;
} }
RotationConstraint* AnimInverseKinematics::getConstraint(int index) { RotationConstraint* AnimInverseKinematics::getConstraint(int index) {

View file

@ -50,7 +50,7 @@ protected:
void setType(const QString& typeVar) { type = ((typeVar == "RotationOnly") ? Type::RotationOnly : Type::RotationAndPosition); } void setType(const QString& typeVar) { type = ((typeVar == "RotationOnly") ? Type::RotationOnly : Type::RotationAndPosition); }
}; };
void computeTargets(const AnimVariantMap& animVars, std::vector<IKTarget>& targets); void computeTargets(const AnimVariantMap& animVars, std::vector<IKTarget>& targets, const AnimPoseVec& underPoses);
void solveWithCyclicCoordinateDescent(const std::vector<IKTarget>& targets); void solveWithCyclicCoordinateDescent(const std::vector<IKTarget>& targets);
virtual void setSkeletonInternal(AnimSkeleton::ConstPointer skeleton); virtual void setSkeletonInternal(AnimSkeleton::ConstPointer skeleton);

View file

@ -93,9 +93,9 @@ void AnimStateMachine::switchState(const AnimVariantMap& animVars, State::Pointe
const float dt = 0.0f; const float dt = 0.0f;
Triggers triggers; Triggers triggers;
_nextPoses = nextStateNode->evaluate(animVars, dt, triggers); _nextPoses = nextStateNode->evaluate(animVars, dt, triggers);
#if WANT_DEBUGa
qCDebug(animation) << "AnimStateMachine::switchState:" << _currentState->getID() << "->" << desiredState->getID() << "duration =" << duration << "targetFrame =" << desiredState->_interpTarget; qCDebug(animation) << "AnimStateMachine::switchState:" << _currentState->getID() << "->" << desiredState->getID() << "duration =" << duration << "targetFrame =" << desiredState->_interpTarget;
#endif
_currentState = desiredState; _currentState = desiredState;
} }

View file

@ -184,6 +184,8 @@ public:
case AnimVariant::Type::String: case AnimVariant::Type::String:
qCDebug(animation) << " " << pair.first << "=" << pair.second.getString(); qCDebug(animation) << " " << pair.first << "=" << pair.second.getString();
break; break;
default:
assert("AnimVariant::Type" == "valid");
} }
} }
} }

View file

@ -1026,12 +1026,21 @@ static void computeHeadNeckAnimVars(AnimSkeleton::ConstPointer skeleton, const A
const glm::quat hmdOrientation = hmdPose.rot * rotY180; // rotY180 will make z forward not -z const glm::quat hmdOrientation = hmdPose.rot * rotY180; // rotY180 will make z forward not -z
// TODO: cache jointIndices // TODO: cache jointIndices
int rightEyeIndex = skeleton->nameToJointIndex("RightEye");
int leftEyeIndex = skeleton->nameToJointIndex("LeftEye");
int headIndex = skeleton->nameToJointIndex("Head");
int neckIndex = skeleton->nameToJointIndex("Neck");
const glm::vec3 DEFAULT_RIGHT_EYE_POS(-0.3f, 1.6f, 0.0f);
const glm::vec3 DEFAULT_LEFT_EYE_POS(0.3f, 1.6f, 0.0f);
const glm::vec3 DEFAULT_HEAD_POS(0.0f, 1.55f, 0.0f);
const glm::vec3 DEFAULT_NECK_POS(0.0f, 1.5f, 0.0f);
// Use absolute bindPose positions just in case the relBindPose have rotations we don't expect. // Use absolute bindPose positions just in case the relBindPose have rotations we don't expect.
glm::vec3 absRightEyePos = skeleton->getAbsoluteBindPose(skeleton->nameToJointIndex("RightEye")).trans; glm::vec3 absRightEyePos = rightEyeIndex != -1 ? skeleton->getAbsoluteBindPose(rightEyeIndex).trans : DEFAULT_RIGHT_EYE_POS;
glm::vec3 absLeftEyePos = skeleton->getAbsoluteBindPose(skeleton->nameToJointIndex("LeftEye")).trans; glm::vec3 absLeftEyePos = leftEyeIndex != -1 ? skeleton->getAbsoluteBindPose(leftEyeIndex).trans : DEFAULT_LEFT_EYE_POS;
glm::vec3 absHeadPos = skeleton->getAbsoluteBindPose(skeleton->nameToJointIndex("Head")).trans; glm::vec3 absHeadPos = headIndex != -1 ? skeleton->getAbsoluteBindPose(headIndex).trans : DEFAULT_HEAD_POS;
glm::vec3 absNeckPos = skeleton->getAbsoluteBindPose(skeleton->nameToJointIndex("Neck")).trans; glm::vec3 absNeckPos = neckIndex != -1 ? skeleton->getAbsoluteBindPose(neckIndex).trans : DEFAULT_NECK_POS;
glm::vec3 absCenterEyePos = (absRightEyePos + absLeftEyePos) / 2.0f; glm::vec3 absCenterEyePos = (absRightEyePos + absLeftEyePos) / 2.0f;
glm::vec3 eyeOffset = absCenterEyePos - absHeadPos; glm::vec3 eyeOffset = absCenterEyePos - absHeadPos;
@ -1079,7 +1088,7 @@ void Rig::updateNeckJoint(int index, const HeadParameters& params) {
_animVars.set("headPosition", headPos); _animVars.set("headPosition", headPos);
_animVars.set("headRotation", headRot); _animVars.set("headRotation", headRot);
_animVars.set("headType", QString("RotationAndPosition")); _animVars.set("headAndNeckType", QString("RotationAndPosition"));
_animVars.set("neckPosition", neckPos); _animVars.set("neckPosition", neckPos);
_animVars.set("neckRotation", neckRot); _animVars.set("neckRotation", neckRot);
@ -1092,7 +1101,7 @@ void Rig::updateNeckJoint(int index, const HeadParameters& params) {
_animVars.unset("headPosition"); _animVars.unset("headPosition");
_animVars.set("headRotation", realLocalHeadOrientation); _animVars.set("headRotation", realLocalHeadOrientation);
_animVars.set("headType", QString("RotationOnly")); _animVars.set("headAndNeckType", QString("RotationOnly"));
_animVars.unset("neckPosition"); _animVars.unset("neckPosition");
_animVars.unset("neckRotation"); _animVars.unset("neckRotation");
} }

View file

@ -966,7 +966,8 @@ bool AvatarData::hasIdentityChangedAfterParsing(NLPacket& packet) {
QByteArray AvatarData::identityByteArray() { QByteArray AvatarData::identityByteArray() {
QByteArray identityData; QByteArray identityData;
QDataStream identityStream(&identityData, QIODevice::Append); QDataStream identityStream(&identityData, QIODevice::Append);
const QUrl& urlToSend = (_skeletonModelURL == AvatarData::defaultFullAvatarModelUrl()) ? QUrl("") : _skeletonModelURL; QUrl emptyURL("");
const QUrl& urlToSend = (_skeletonModelURL == AvatarData::defaultFullAvatarModelUrl()) ? emptyURL : _skeletonModelURL;
identityStream << QUuid() << _faceModelURL << urlToSend << _attachmentData << _displayName; identityStream << QUuid() << _faceModelURL << urlToSend << _attachmentData << _displayName;
@ -989,8 +990,11 @@ void AvatarData::setFaceModelURL(const QUrl& faceModelURL) {
} }
void AvatarData::setSkeletonModelURL(const QUrl& skeletonModelURL) { void AvatarData::setSkeletonModelURL(const QUrl& skeletonModelURL) {
_skeletonModelURL = skeletonModelURL.isEmpty() ? AvatarData::defaultFullAvatarModelUrl() : skeletonModelURL; const QUrl& expanded = skeletonModelURL.isEmpty() ? AvatarData::defaultFullAvatarModelUrl() : skeletonModelURL;
if (expanded == _skeletonModelURL) {
return;
}
_skeletonModelURL = expanded;
qCDebug(avatars) << "Changing skeleton model for avatar to" << _skeletonModelURL.toString(); qCDebug(avatars) << "Changing skeleton model for avatar to" << _skeletonModelURL.toString();
updateJointMappings(); updateJointMappings();

View file

@ -121,6 +121,8 @@ public:
static const glm::mat4 pose; return pose; static const glm::mat4 pose; return pose;
} }
virtual float getIPD() const { return 0.0f; }
virtual void abandonCalibration() {} virtual void abandonCalibration() {}
virtual void resetSensors() {} virtual void resetSensors() {}
virtual float devicePixelRatio() { return 1.0; } virtual float devicePixelRatio() { return 1.0; }

View file

@ -149,3 +149,11 @@ void OculusBaseDisplayPlugin::deactivate() {
void OculusBaseDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSize) { void OculusBaseDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSize) {
++_frameIndex; ++_frameIndex;
} }
float OculusBaseDisplayPlugin::getIPD() const {
float result = 0.0f;
#if (OVR_MAJOR_VERSION >= 6)
result = ovr_GetFloat(_hmd, OVR_KEY_IPD, OVR_DEFAULT_IPD);
#endif
return result;
}

View file

@ -31,6 +31,7 @@ public:
virtual void resetSensors() override final; virtual void resetSensors() override final;
virtual glm::mat4 getEyePose(Eye eye) const override final; virtual glm::mat4 getEyePose(Eye eye) const override final;
virtual glm::mat4 getHeadPose() const override final; virtual glm::mat4 getHeadPose() const override final;
virtual float getIPD() const override final;
protected: protected:
virtual void preRender() override final; virtual void preRender() override final;

View file

@ -86,18 +86,18 @@ void RenderablePolyLineEntityItem::updateGeometry() {
int numTailStrips = 5; int numTailStrips = 5;
int numHeadStrips = 10; int numHeadStrips = 10;
int startHeadIndex = _normals.size() - numHeadStrips; int startHeadIndex = _vertices.size() / 2 - numHeadStrips;
for (int i = 0; i < _normals.size(); i++) { for (int i = 0; i < _vertices.size() / 2; i++) {
uCoord = 0.26f; uCoord = 0.26f;
vCoord = 0.0f; vCoord = 0.0f;
//tail //tail
if(i < numTailStrips) { if (i < numTailStrips) {
uCoord = float(i)/numTailStrips * tailLength + tailStart; uCoord = float(i) / numTailStrips * tailLength + tailStart;
} }
//head //head
if( i > startHeadIndex) { if (i > startHeadIndex) {
uCoord = float( (i+ 1) - startHeadIndex)/numHeadStrips * headLength + headStart; uCoord = float((i + 1) - startHeadIndex) / numHeadStrips * headLength + headStart;
} }
uv = vec2(uCoord, vCoord); uv = vec2(uCoord, vCoord);
@ -115,7 +115,7 @@ void RenderablePolyLineEntityItem::updateGeometry() {
_verticesBuffer->append(sizeof(glm::vec2), (const gpu::Byte*)&uv); _verticesBuffer->append(sizeof(glm::vec2), (const gpu::Byte*)&uv);
vertexIndex++; vertexIndex++;
_numVertices +=2; _numVertices += 2;
} }
_pointsChanged = false; _pointsChanged = false;
@ -124,7 +124,7 @@ void RenderablePolyLineEntityItem::updateGeometry() {
void RenderablePolyLineEntityItem::render(RenderArgs* args) { void RenderablePolyLineEntityItem::render(RenderArgs* args) {
QWriteLocker lock(&_quadReadWriteLock); QWriteLocker lock(&_quadReadWriteLock);
if (_points.size() < 2 || _vertices.size() != _normals.size() * 2) { if (_points.size() < 2 || _normals.size () < 2 || _vertices.size() < 2) {
return; return;
} }

View file

@ -46,6 +46,8 @@ public:
static EntityActionType actionTypeFromString(QString actionTypeString); static EntityActionType actionTypeFromString(QString actionTypeString);
static QString actionTypeToString(EntityActionType actionType); static QString actionTypeToString(EntityActionType actionType);
virtual bool lifetimeIsOver() { return false; }
protected: protected:
virtual glm::vec3 getPosition() = 0; virtual glm::vec3 getPosition() = 0;
virtual void setPosition(glm::vec3 position) = 0; virtual void setPosition(glm::vec3 position) = 0;

View file

@ -27,12 +27,12 @@ const int PolyLineEntityItem::MAX_POINTS_PER_LINE = 70;
EntityItemPointer PolyLineEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { EntityItemPointer PolyLineEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
EntityItemPointer result { new PolyLineEntityItem(entityID, properties) }; EntityItemPointer result{ new PolyLineEntityItem(entityID, properties) };
return result; return result;
} }
PolyLineEntityItem::PolyLineEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) : PolyLineEntityItem::PolyLineEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) :
EntityItem(entityItemID) , EntityItem(entityItemID),
_lineWidth(DEFAULT_LINE_WIDTH), _lineWidth(DEFAULT_LINE_WIDTH),
_pointsChanged(true), _pointsChanged(true),
_points(QVector<glm::vec3>(0.0f)), _points(QVector<glm::vec3>(0.0f)),
@ -93,7 +93,7 @@ bool PolyLineEntityItem::appendPoint(const glm::vec3& point) {
return false; return false;
} }
glm::vec3 halfBox = getDimensions() * 0.5f; glm::vec3 halfBox = getDimensions() * 0.5f;
if ( (point.x < - halfBox.x || point.x > halfBox.x) || (point.y < -halfBox.y || point.y > halfBox.y) || (point.z < - halfBox.z || point.z > halfBox.z) ) { if ((point.x < -halfBox.x || point.x > halfBox.x) || (point.y < -halfBox.y || point.y > halfBox.y) || (point.z < -halfBox.z || point.z > halfBox.z)) {
qDebug() << "Point is outside entity's bounding box"; qDebug() << "Point is outside entity's bounding box";
return false; return false;
} }
@ -102,14 +102,14 @@ bool PolyLineEntityItem::appendPoint(const glm::vec3& point) {
return true; return true;
} }
bool PolyLineEntityItem::setStrokeWidths(const QVector<float>& strokeWidths ) { bool PolyLineEntityItem::setStrokeWidths(const QVector<float>& strokeWidths) {
_strokeWidths = strokeWidths; _strokeWidths = strokeWidths;
return true; return true;
} }
bool PolyLineEntityItem::setNormals(const QVector<glm::vec3>& normals) { bool PolyLineEntityItem::setNormals(const QVector<glm::vec3>& normals) {
_normals = normals; _normals = normals;
if (_points.size () < 2 || _normals.size() < 2) { if (_points.size() < 2 || _normals.size() < 2) {
return false; return false;
} }
@ -124,11 +124,11 @@ bool PolyLineEntityItem::setNormals(const QVector<glm::vec3>& normals) {
_vertices.clear(); _vertices.clear();
glm::vec3 v1, v2, tangent, binormal, point; glm::vec3 v1, v2, tangent, binormal, point;
for (int i = 0; i < minVectorSize-1; i++) { for (int i = 0; i < minVectorSize - 1; i++) {
float width = _strokeWidths.at(i); float width = _strokeWidths.at(i);
point = _points.at(i); point = _points.at(i);
tangent = _points.at(i+1) - point; tangent = _points.at(i + 1) - point;
glm::vec3 normal = normals.at(i); glm::vec3 normal = normals.at(i);
binormal = glm::normalize(glm::cross(tangent, normal)) * width; binormal = glm::normalize(glm::cross(tangent, normal)) * width;
@ -139,7 +139,7 @@ bool PolyLineEntityItem::setNormals(const QVector<glm::vec3>& normals) {
_vertices << v1 << v2; _vertices << v1 << v2;
} }
//for last point we can just assume binormals are same since it represents last two vertices of quad //for last point we can just assume binormals are same since it represents last two vertices of quad
point = _points.at(_points.size() - 1); point = _points.at(minVectorSize - 1);
v1 = point + binormal; v1 = point + binormal;
v2 = point - binormal; v2 = point - binormal;
_vertices << v1 << v2; _vertices << v1 << v2;
@ -157,7 +157,7 @@ bool PolyLineEntityItem::setLinePoints(const QVector<glm::vec3>& points) {
//Check to see if points actually changed. If they haven't, return before doing anything else //Check to see if points actually changed. If they haven't, return before doing anything else
else if (points.size() == _points.size()) { else if (points.size() == _points.size()) {
//same number of points, so now compare every point //same number of points, so now compare every point
for (int i = 0; i < points.size(); i++ ) { for (int i = 0; i < points.size(); i++) {
if (points.at(i) != _points.at(i)){ if (points.at(i) != _points.at(i)){
_pointsChanged = true; _pointsChanged = true;
break; break;
@ -171,9 +171,9 @@ bool PolyLineEntityItem::setLinePoints(const QVector<glm::vec3>& points) {
for (int i = 0; i < points.size(); i++) { for (int i = 0; i < points.size(); i++) {
glm::vec3 point = points.at(i); glm::vec3 point = points.at(i);
glm::vec3 halfBox = getDimensions() * 0.5f; glm::vec3 halfBox = getDimensions() * 0.5f;
if ((point.x < - halfBox.x || point.x > halfBox.x) || if ((point.x < -halfBox.x || point.x > halfBox.x) ||
(point.y < -halfBox.y || point.y > halfBox.y) || (point.y < -halfBox.y || point.y > halfBox.y) ||
(point.z < - halfBox.z || point.z > halfBox.z)) { (point.z < -halfBox.z || point.z > halfBox.z)) {
qDebug() << "Point is outside entity's bounding box"; qDebug() << "Point is outside entity's bounding box";
return false; return false;
} }

View file

@ -23,7 +23,8 @@ std::unique_ptr<NLPacketList> NLPacketList::create(PacketType packetType, QByteA
} }
std::unique_ptr<NLPacketList> NLPacketList::fromPacketList(std::unique_ptr<PacketList> packetList) { std::unique_ptr<NLPacketList> NLPacketList::fromPacketList(std::unique_ptr<PacketList> packetList) {
auto nlPacketList = std::unique_ptr<NLPacketList>(new NLPacketList(std::move(*packetList.release()))); nlPacketList->open(ReadOnly); auto nlPacketList = std::unique_ptr<NLPacketList>(new NLPacketList(std::move(*packetList.release())));
nlPacketList->open(ReadOnly);
return nlPacketList; return nlPacketList;
} }

View file

@ -35,6 +35,7 @@ void SentPacketHistory::packetSent(uint16_t sequenceNumber, const NLPacket& pack
} }
_newestSequenceNumber = sequenceNumber; _newestSequenceNumber = sequenceNumber;
QWriteLocker locker(&_packetsLock);
_sentPackets.insert(NLPacket::createCopy(packet)); _sentPackets.insert(NLPacket::createCopy(packet));
} }
@ -49,5 +50,10 @@ const NLPacket* SentPacketHistory::getPacket(uint16_t sequenceNumber) const {
seqDiff += UINT16_RANGE; seqDiff += UINT16_RANGE;
} }
return _sentPackets.get(seqDiff)->get(); QReadLocker locker(&_packetsLock);
auto packet = _sentPackets.get(seqDiff);
if (packet) {
return packet->get();
}
return nullptr;
} }

View file

@ -12,7 +12,9 @@
#define hifi_SentPacketHistory_h #define hifi_SentPacketHistory_h
#include <stdint.h> #include <stdint.h>
#include <qbytearray.h>
#include <QtCore/QByteArray>
#include <QtCore/QReadWriteLock>
#include "NLPacket.h" #include "NLPacket.h"
#include "RingBufferHistory.h" #include "RingBufferHistory.h"
@ -29,6 +31,7 @@ public:
const NLPacket* getPacket(uint16_t sequenceNumber) const; const NLPacket* getPacket(uint16_t sequenceNumber) const;
private: private:
mutable QReadWriteLock _packetsLock { QReadWriteLock::Recursive };
RingBufferHistory<std::unique_ptr<NLPacket>> _sentPackets; // circular buffer RingBufferHistory<std::unique_ptr<NLPacket>> _sentPackets; // circular buffer
uint16_t _newestSequenceNumber; uint16_t _newestSequenceNumber;

View file

@ -49,7 +49,8 @@ void ThreadedAssignment::setFinished(bool isFinished) {
} }
if (_statsTimer) { if (_statsTimer) {
_statsTimer->stop(); _statsTimer->deleteLater();
_statsTimer = nullptr;
} }
// call our virtual aboutToFinish method - this gives the ThreadedAssignment subclass a chance to cleanup // call our virtual aboutToFinish method - this gives the ThreadedAssignment subclass a chance to cleanup
@ -105,7 +106,7 @@ void ThreadedAssignment::sendStatsPacket() {
void ThreadedAssignment::startSendingStats() { void ThreadedAssignment::startSendingStats() {
// send the stats packet every 1s // send the stats packet every 1s
if (!_statsTimer) { if (!_statsTimer) {
_statsTimer = new QTimer(); _statsTimer = new QTimer;
connect(_statsTimer, &QTimer::timeout, this, &ThreadedAssignment::sendStatsPacket); connect(_statsTimer, &QTimer::timeout, this, &ThreadedAssignment::sendStatsPacket);
} }

View file

@ -161,13 +161,16 @@ void DefaultCC::onLoss(SequenceNumber rangeStart, SequenceNumber rangeEnd) {
_lastDecreaseMaxSeq = _sendCurrSeqNum; _lastDecreaseMaxSeq = _sendCurrSeqNum;
if (_avgNAKNum < 1) {
_randomDecreaseThreshold = 1;
} else {
// avoid synchronous rate decrease across connections using randomization // avoid synchronous rate decrease across connections using randomization
std::random_device rd; std::random_device rd;
std::mt19937 generator(rd()); std::mt19937 generator(rd());
std::uniform_int_distribution<> distribution(1, _avgNAKNum); std::uniform_int_distribution<> distribution(1, std::max(1, _avgNAKNum));
_randomDecreaseThreshold = distribution(generator); _randomDecreaseThreshold = distribution(generator);
}
} else if ((_decreaseCount++ < MAX_DECREASES_PER_CONGESTION_EPOCH) && ((++_nakCount % _randomDecreaseThreshold) == 0)) { } else if ((_decreaseCount++ < MAX_DECREASES_PER_CONGESTION_EPOCH) && ((++_nakCount % _randomDecreaseThreshold) == 0)) {
// there have been fewer than MAX_DECREASES_PER_CONGESTION_EPOCH AND this NAK matches the random count at which we // there have been fewer than MAX_DECREASES_PER_CONGESTION_EPOCH AND this NAK matches the random count at which we
// decided we would decrease the packet send period // decided we would decrease the packet send period

View file

@ -32,7 +32,7 @@ Connection::Connection(Socket* parentSocket, HifiSockAddr destination, std::uniq
_destination(destination), _destination(destination),
_congestionControl(move(congestionControl)) _congestionControl(move(congestionControl))
{ {
Q_ASSERT_X(socket, "Connection::Connection", "Must be called with a valid Socket*"); Q_ASSERT_X(parentSocket, "Connection::Connection", "Must be called with a valid Socket*");
Q_ASSERT_X(_congestionControl, "Connection::Connection", "Must be called with a valid CongestionControl object"); Q_ASSERT_X(_congestionControl, "Connection::Connection", "Must be called with a valid CongestionControl object");
_congestionControl->init(); _congestionControl->init();

View file

@ -28,7 +28,8 @@ class Packet;
class PacketList : public QIODevice { class PacketList : public QIODevice {
Q_OBJECT Q_OBJECT
public: public:
static std::unique_ptr<PacketList> create(PacketType packetType, QByteArray extendedHeader = QByteArray(), bool isReliable = false, bool isOrdered = false); static std::unique_ptr<PacketList> create(PacketType packetType, QByteArray extendedHeader = QByteArray(),
bool isReliable = false, bool isOrdered = false);
static std::unique_ptr<PacketList> fromReceivedPackets(std::list<std::unique_ptr<Packet>>&& packets); static std::unique_ptr<PacketList> fromReceivedPackets(std::list<std::unique_ptr<Packet>>&& packets);
bool isReliable() const { return _isReliable; } bool isReliable() const { return _isReliable; }

View file

@ -346,6 +346,7 @@ void OctreeEditPacketSender::processNackPacket(NLPacket& packet, SharedNodePoint
if (_sentPacketHistories.count(sendingNode->getUUID()) == 0) { if (_sentPacketHistories.count(sendingNode->getUUID()) == 0) {
return; return;
} }
const SentPacketHistory& sentPacketHistory = _sentPacketHistories[sendingNode->getUUID()]; const SentPacketHistory& sentPacketHistory = _sentPacketHistories[sendingNode->getUUID()];
// read sequence numbers and queue packets for resend // read sequence numbers and queue packets for resend

View file

@ -30,6 +30,18 @@ void ObjectAction::updateAction(btCollisionWorld* collisionWorld, btScalar delta
dynamicsWorld->removeAction(this); dynamicsWorld->removeAction(this);
return; return;
} }
if (_expires > 0) {
quint64 now = usecTimestampNow();
if (now > _expires) {
EntityItemPointer ownerEntity = _ownerEntity.lock();
_active = false;
if (ownerEntity) {
ownerEntity->removeAction(nullptr, getID());
}
}
}
if (!_active) { if (!_active) {
return; return;
} }
@ -37,6 +49,40 @@ void ObjectAction::updateAction(btCollisionWorld* collisionWorld, btScalar delta
updateActionWorker(deltaTimeStep); updateActionWorker(deltaTimeStep);
} }
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 tagSet = true;
QString tag = EntityActionInterface::extractStringArgument("action", arguments, "tag", tagSet, false);
if (tagSet) {
_tag = tag;
} else {
tag = "";
}
return true;
}
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;
return arguments;
}
void ObjectAction::debugDraw(btIDebugDraw* debugDrawer) { void ObjectAction::debugDraw(btIDebugDraw* debugDrawer) {
} }
@ -136,3 +182,14 @@ void ObjectAction::activateBody() {
} }
} }
bool ObjectAction::lifetimeIsOver() {
if (_expires == 0) {
return false;
}
quint64 now = usecTimestampNow();
if (now >= _expires) {
return true;
}
return false;
}

View file

@ -33,8 +33,8 @@ public:
virtual EntityItemWeakPointer getOwnerEntity() const { return _ownerEntity; } virtual EntityItemWeakPointer getOwnerEntity() const { return _ownerEntity; }
virtual void setOwnerEntity(const EntityItemPointer ownerEntity) { _ownerEntity = ownerEntity; } virtual void setOwnerEntity(const EntityItemPointer ownerEntity) { _ownerEntity = ownerEntity; }
virtual bool updateArguments(QVariantMap arguments) = 0; virtual bool updateArguments(QVariantMap arguments);
virtual QVariantMap getArguments() = 0; virtual QVariantMap getArguments();
// this is called from updateAction and should be overridden by subclasses // this is called from updateAction and should be overridden by subclasses
virtual void updateActionWorker(float deltaTimeStep) = 0; virtual void updateActionWorker(float deltaTimeStep) = 0;
@ -46,6 +46,8 @@ public:
virtual QByteArray serialize() const = 0; virtual QByteArray serialize() const = 0;
virtual void deserialize(QByteArray serializedArguments) = 0; virtual void deserialize(QByteArray serializedArguments) = 0;
virtual bool lifetimeIsOver();
protected: protected:
virtual btRigidBody* getRigidBody(); virtual btRigidBody* getRigidBody();
@ -59,11 +61,11 @@ protected:
virtual void setAngularVelocity(glm::vec3 angularVelocity); virtual void setAngularVelocity(glm::vec3 angularVelocity);
virtual void activateBody(); virtual void activateBody();
private:
protected:
bool _active; bool _active;
EntityItemWeakPointer _ownerEntity; EntityItemWeakPointer _ownerEntity;
quint64 _expires; // in seconds since epoch
QString _tag;
}; };
#endif // hifi_ObjectAction_h #endif // hifi_ObjectAction_h

View file

@ -80,6 +80,9 @@ void ObjectActionOffset::updateActionWorker(btScalar deltaTimeStep) {
bool ObjectActionOffset::updateArguments(QVariantMap arguments) { bool ObjectActionOffset::updateArguments(QVariantMap arguments) {
if (!ObjectAction::updateArguments(arguments)) {
return false;
}
bool ok = true; bool ok = true;
glm::vec3 pointToOffsetFrom = glm::vec3 pointToOffsetFrom =
EntityActionInterface::extractVec3Argument("offset action", arguments, "pointToOffsetFrom", ok, true); EntityActionInterface::extractVec3Argument("offset action", arguments, "pointToOffsetFrom", ok, true);
@ -119,7 +122,7 @@ bool ObjectActionOffset::updateArguments(QVariantMap arguments) {
} }
QVariantMap ObjectActionOffset::getArguments() { QVariantMap ObjectActionOffset::getArguments() {
QVariantMap arguments; QVariantMap arguments = ObjectAction::getArguments();
withReadLock([&] { withReadLock([&] {
arguments["pointToOffsetFrom"] = glmToQMap(_pointToOffsetFrom); arguments["pointToOffsetFrom"] = glmToQMap(_pointToOffsetFrom);
arguments["linearTimeScale"] = _linearTimeScale; arguments["linearTimeScale"] = _linearTimeScale;
@ -140,6 +143,9 @@ QByteArray ObjectActionOffset::serialize() const {
dataStream << _linearTimeScale; dataStream << _linearTimeScale;
dataStream << _positionalTargetSet; dataStream << _positionalTargetSet;
dataStream << _expires;
dataStream << _tag;
return ba; return ba;
} }
@ -165,5 +171,8 @@ void ObjectActionOffset::deserialize(QByteArray serializedArguments) {
dataStream >> _linearTimeScale; dataStream >> _linearTimeScale;
dataStream >> _positionalTargetSet; dataStream >> _positionalTargetSet;
dataStream >> _expires;
dataStream >> _tag;
_active = true; _active = true;
} }

View file

@ -109,6 +109,9 @@ void ObjectActionSpring::updateActionWorker(btScalar deltaTimeStep) {
const float MIN_TIMESCALE = 0.1f; const float MIN_TIMESCALE = 0.1f;
bool ObjectActionSpring::updateArguments(QVariantMap arguments) { bool ObjectActionSpring::updateArguments(QVariantMap arguments) {
if (!ObjectAction::updateArguments(arguments)) {
return false;
}
// targets are required, spring-constants are optional // targets are required, spring-constants are optional
bool ok = true; bool ok = true;
glm::vec3 positionalTarget = glm::vec3 positionalTarget =
@ -155,7 +158,7 @@ bool ObjectActionSpring::updateArguments(QVariantMap arguments) {
} }
QVariantMap ObjectActionSpring::getArguments() { QVariantMap ObjectActionSpring::getArguments() {
QVariantMap arguments; QVariantMap arguments = ObjectAction::getArguments();
withReadLock([&] { withReadLock([&] {
arguments["linearTimeScale"] = _linearTimeScale; arguments["linearTimeScale"] = _linearTimeScale;
arguments["targetPosition"] = glmToQMap(_positionalTarget); arguments["targetPosition"] = glmToQMap(_positionalTarget);
@ -182,6 +185,9 @@ QByteArray ObjectActionSpring::serialize() const {
dataStream << _angularTimeScale; dataStream << _angularTimeScale;
dataStream << _rotationalTargetSet; dataStream << _rotationalTargetSet;
dataStream << _expires;
dataStream << _tag;
return serializedActionArguments; return serializedActionArguments;
} }
@ -210,5 +216,8 @@ void ObjectActionSpring::deserialize(QByteArray serializedArguments) {
dataStream >> _angularTimeScale; dataStream >> _angularTimeScale;
dataStream >> _rotationalTargetSet; dataStream >> _rotationalTargetSet;
dataStream >> _expires;
dataStream >> _tag;
_active = true; _active = true;
} }

View file

@ -442,7 +442,7 @@ void DeferredLightingEffect::render(RenderArgs* args) {
deferredTransforms[i].projection = projMats[i]; deferredTransforms[i].projection = projMats[i];
auto sideViewMat = eyeViews[i] * monoViewMat; auto sideViewMat = monoViewMat * glm::inverse(eyeViews[i]);
viewTransforms[i].evalFromRawMatrix(sideViewMat); viewTransforms[i].evalFromRawMatrix(sideViewMat);
deferredTransforms[i].viewInverse = sideViewMat; deferredTransforms[i].viewInverse = sideViewMat;
@ -575,9 +575,7 @@ void DeferredLightingEffect::render(RenderArgs* args) {
for (auto lightID : _pointLights) { for (auto lightID : _pointLights) {
auto& light = _allocatedLights[lightID]; auto& light = _allocatedLights[lightID];
// IN DEBUG: light->setShowContour(true); // IN DEBUG: light->setShowContour(true);
if (_pointLightLocations->lightBufferUnit >= 0) {
batch.setUniformBuffer(_pointLightLocations->lightBufferUnit, light->getSchemaBuffer()); batch.setUniformBuffer(_pointLightLocations->lightBufferUnit, light->getSchemaBuffer());
}
float expandedRadius = light->getMaximumRadius() * (1.0f + SCALE_EXPANSION); float expandedRadius = light->getMaximumRadius() * (1.0f + SCALE_EXPANSION);
// TODO: We shouldn;t have to do that test and use a different volume geometry for when inside the vlight volume, // TODO: We shouldn;t have to do that test and use a different volume geometry for when inside the vlight volume,
@ -613,7 +611,6 @@ void DeferredLightingEffect::render(RenderArgs* args) {
for (auto lightID : _spotLights) { for (auto lightID : _spotLights) {
auto light = _allocatedLights[lightID]; auto light = _allocatedLights[lightID];
// IN DEBUG: light->setShowContour(true); // IN DEBUG: light->setShowContour(true);
batch.setUniformBuffer(_spotLightLocations->lightBufferUnit, light->getSchemaBuffer()); batch.setUniformBuffer(_spotLightLocations->lightBufferUnit, light->getSchemaBuffer());
auto eyeLightPos = eyePoint - light->getPosition(); auto eyeLightPos = eyePoint - light->getPosition();

View file

@ -17,6 +17,7 @@
// the interpolated normal // the interpolated normal
in vec3 _normal; in vec3 _normal;
in vec3 _modelNormal;
in vec3 _color; in vec3 _color;
in vec2 _texCoord0; in vec2 _texCoord0;
in vec4 _position; in vec4 _position;

View file

@ -22,6 +22,7 @@ uniform bool Instanced = false;
// the interpolated normal // the interpolated normal
out vec3 _normal; out vec3 _normal;
out vec3 _modelNormal;
out vec3 _color; out vec3 _color;
out vec2 _texCoord0; out vec2 _texCoord0;
out vec4 _position; out vec4 _position;
@ -30,6 +31,7 @@ void main(void) {
_color = inColor.rgb; _color = inColor.rgb;
_texCoord0 = inTexCoord0.st; _texCoord0 = inTexCoord0.st;
_position = inPosition; _position = inPosition;
_modelNormal = inNormal.xyz;
// standard transform // standard transform
TransformCamera cam = getTransformCamera(); TransformCamera cam = getTransformCamera();