mirror of
https://github.com/overte-org/overte.git
synced 2025-08-09 07:58:59 +02:00
Merge branch 'master' into ericrius1-toybox
This commit is contained in:
commit
7bcb4e4ae3
30 changed files with 524 additions and 142 deletions
|
@ -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() {
|
||||||
_scriptEngine->stop();
|
setIsAvatar(false);// will stop timers for sending billboards and identity packets
|
||||||
|
if (_scriptEngine) {
|
||||||
|
_scriptEngine->stop();
|
||||||
|
}
|
||||||
|
|
||||||
_pingTimer->stop();
|
if (_pingTimer) {
|
||||||
delete _pingTimer;
|
_pingTimer->stop();
|
||||||
|
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);
|
||||||
|
|
|
@ -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,18 +271,19 @@ int OctreeInboundPacketProcessor::sendNackPackets() {
|
||||||
|
|
||||||
auto it = missingSequenceNumbers.constBegin();
|
auto it = missingSequenceNumbers.constBegin();
|
||||||
|
|
||||||
while (it != missingSequenceNumbers.constEnd()) {
|
if (it != missingSequenceNumbers.constEnd()) {
|
||||||
unsigned short int sequenceNumber = *it;
|
auto nackPacketList = NLPacketList::create(_myServer->getMyEditNackType());
|
||||||
nackPacketList->writePrimitive(sequenceNumber);
|
|
||||||
++it;
|
while (it != missingSequenceNumbers.constEnd()) {
|
||||||
}
|
unsigned short int sequenceNumber = *it;
|
||||||
|
nackPacketList->writePrimitive(sequenceNumber);
|
||||||
|
++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();
|
||||||
|
|
||||||
// send the list of nack packets
|
// send the list of nack packets
|
||||||
nodeList->sendPacketList(std::move(nackPacketList), *destinationNode);
|
nodeList->sendPacketList(std::move(nackPacketList), *destinationNode);
|
||||||
}
|
}
|
||||||
|
|
146
examples/actionInspector.js
Normal file
146
examples/actionInspector.js
Normal 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);
|
|
@ -66,6 +66,7 @@ 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_OFF = 0;
|
var STATE_OFF = 0;
|
||||||
|
@ -81,14 +82,25 @@ 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";
|
||||||
|
|
||||||
// HACK -- until we have collision groups, don't allow held object to collide with avatar
|
function getTag() {
|
||||||
var AVATAR_COLLISIONS_MENU_ITEM = "Enable avatar collisions";
|
return "grab-" + MyAvatar.sessionUUID;
|
||||||
|
}
|
||||||
|
|
||||||
|
function entityIsGrabbedByOther(entityID) {
|
||||||
// HACK -- until we have collision groups, don't allow held object to collide with avatar
|
var actionIDs = Entities.getActionIDs(entityID);
|
||||||
var initialAvatarCollisionsMenu = Menu.isOptionChecked(AVATAR_COLLISIONS_MENU_ITEM);
|
for (var actionIndex = 0; actionIndex < actionIDs.length; actionIndex++) {
|
||||||
var currentAvatarCollisionsMenu = initialAvatarCollisionsMenu;
|
var actionID = actionIDs[actionIndex];
|
||||||
var noCollisionsCount = 0; // how many hands want collisions disabled?
|
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) {
|
||||||
|
@ -152,28 +164,6 @@ function MyController(hand, triggerAction) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// HACK -- until we have collision groups, don't allow held object to collide with avatar
|
|
||||||
this.disableAvatarCollisions = function() {
|
|
||||||
noCollisionsCount += 1;
|
|
||||||
if (currentAvatarCollisionsMenu != false) {
|
|
||||||
currentAvatarCollisionsMenu = false;
|
|
||||||
Menu.setIsOptionChecked(AVATAR_COLLISIONS_MENU_ITEM, false);
|
|
||||||
MyAvatar.updateMotionBehaviorFromMenu();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// HACK -- until we have collision groups, don't allow held object to collide with avatar
|
|
||||||
this.revertAvatarCollisions = function() {
|
|
||||||
noCollisionsCount -= 1;
|
|
||||||
if (noCollisionsCount < 1) {
|
|
||||||
if (currentAvatarCollisionsMenu != initialAvatarCollisionsMenu) {
|
|
||||||
currentAvatarCollisionsMenu = initialAvatarCollisionsMenu;
|
|
||||||
Menu.setIsOptionChecked(AVATAR_COLLISIONS_MENU_ITEM, initialAvatarCollisionsMenu);
|
|
||||||
MyAvatar.updateMotionBehaviorFromMenu();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.lineOn = function(closePoint, farPoint, color) {
|
this.lineOn = function(closePoint, farPoint, color) {
|
||||||
// draw a line
|
// draw a line
|
||||||
if (this.pointer === null) {
|
if (this.pointer === null) {
|
||||||
|
@ -267,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);
|
||||||
|
@ -306,9 +300,6 @@ function MyController(hand, triggerAction) {
|
||||||
|
|
||||||
this.distanceHolding = function() {
|
this.distanceHolding = function() {
|
||||||
|
|
||||||
// HACK -- until we have collision groups, don't allow held object to collide with avatar
|
|
||||||
this.disableAvatarCollisions();
|
|
||||||
|
|
||||||
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"]);
|
||||||
|
@ -320,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;
|
||||||
|
@ -348,9 +342,6 @@ function MyController(hand, triggerAction) {
|
||||||
|
|
||||||
this.continueDistanceHolding = function() {
|
this.continueDistanceHolding = function() {
|
||||||
if (this.triggerSmoothedReleased()) {
|
if (this.triggerSmoothedReleased()) {
|
||||||
// HACK -- until we have collision groups, don't allow held object to collide with avatar
|
|
||||||
this.revertAvatarCollisions();
|
|
||||||
|
|
||||||
this.state = STATE_RELEASE;
|
this.state = STATE_RELEASE;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -390,11 +381,11 @@ function MyController(hand, triggerAction) {
|
||||||
var handMovementFromTurning = Vec3.subtract(Quat.multiply(avatarDeltaOrientation, handToAvatar), handToAvatar);
|
var handMovementFromTurning = Vec3.subtract(Quat.multiply(avatarDeltaOrientation, handToAvatar), handToAvatar);
|
||||||
var objectMovementFromTurning = Vec3.subtract(Quat.multiply(avatarDeltaOrientation, objectToAvatar), objectToAvatar);
|
var objectMovementFromTurning = Vec3.subtract(Quat.multiply(avatarDeltaOrientation, objectToAvatar), objectToAvatar);
|
||||||
this.currentAvatarOrientation = currentOrientation;
|
this.currentAvatarOrientation = currentOrientation;
|
||||||
|
|
||||||
// how far did hand move this timestep?
|
// 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
|
// magnify the hand movement but not the change from avatar movement & rotation
|
||||||
handMoved = Vec3.subtract(handMoved, avatarDeltaPosition);
|
handMoved = Vec3.subtract(handMoved, avatarDeltaPosition);
|
||||||
handMoved = Vec3.subtract(handMoved, handMovementFromTurning);
|
handMoved = Vec3.subtract(handMoved, handMovementFromTurning);
|
||||||
|
@ -424,19 +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() {
|
||||||
|
|
||||||
// HACK -- until we have collision groups, don't allow held object to collide with avatar
|
|
||||||
this.disableAvatarCollisions();
|
|
||||||
|
|
||||||
if (this.triggerSmoothedReleased()) {
|
if (this.triggerSmoothedReleased()) {
|
||||||
// HACK -- until we have collision groups, don't allow held object to collide with avatar
|
|
||||||
this.revertAvatarCollisions();
|
|
||||||
|
|
||||||
this.state = STATE_RELEASE;
|
this.state = STATE_RELEASE;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -457,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;
|
||||||
|
@ -483,9 +471,6 @@ function MyController(hand, triggerAction) {
|
||||||
|
|
||||||
this.continueNearGrabbing = function() {
|
this.continueNearGrabbing = function() {
|
||||||
if (this.triggerSmoothedReleased()) {
|
if (this.triggerSmoothedReleased()) {
|
||||||
// HACK -- until we have collision groups, don't allow held object to collide with avatar
|
|
||||||
this.revertAvatarCollisions();
|
|
||||||
|
|
||||||
this.state = STATE_RELEASE;
|
this.state = STATE_RELEASE;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -495,7 +480,7 @@ function MyController(hand, triggerAction) {
|
||||||
// object's actual held offset is an idea intended to make it easier to throw things:
|
// object's actual held offset is an idea intended to make it easier to throw things:
|
||||||
// Because we might catch something or transfer it between hands without a good idea
|
// Because we might catch something or transfer it between hands without a good idea
|
||||||
// of it's actual offset, let's try imparting a velocity which is at a fixed radius
|
// of it's actual offset, let's try imparting a velocity which is at a fixed radius
|
||||||
// from the palm.
|
// from the palm.
|
||||||
|
|
||||||
var handControllerPosition = Controller.getSpatialControlPosition(this.tip);
|
var handControllerPosition = Controller.getSpatialControlPosition(this.tip);
|
||||||
var now = Date.now();
|
var now = Date.now();
|
||||||
|
@ -507,6 +492,8 @@ function MyController(hand, triggerAction) {
|
||||||
this.currentHandControllerTipPosition = 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() {
|
||||||
|
|
|
@ -13,7 +13,27 @@
|
||||||
var MAX_SOLID_ANGLE = 0.01; // objects that appear smaller than this can't be grabbed
|
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 ZERO_VEC3 = {x: 0, y: 0, z: 0};
|
||||||
var IDENTITY_QUAT = {x: 0, y: 0, z: 0, w: 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);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
// helper function
|
// helper function
|
||||||
function mouseIntersectionWithPlane(pointOnPlane, planeNormal, event, maxDistance) {
|
function mouseIntersectionWithPlane(pointOnPlane, planeNormal, event, maxDistance) {
|
||||||
|
@ -288,7 +308,7 @@ 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();
|
||||||
|
@ -303,7 +323,7 @@ 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 = {targetRotation: this.lastRotation, angularTimeScale: 0.1};
|
actionArgs = {targetRotation: this.lastRotation, angularTimeScale: 0.1, tag: getTag(), lifetime: ACTION_LIFETIME};
|
||||||
} else {
|
} else {
|
||||||
var newPointOnPlane;
|
var newPointOnPlane;
|
||||||
if (this.mode === "verticalCylinder") {
|
if (this.mode === "verticalCylinder") {
|
||||||
|
@ -327,13 +347,15 @@ Grabber.prototype.moveEvent = function(event) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.targetPosition = Vec3.subtract(newPointOnPlane, this.offset);
|
this.targetPosition = Vec3.subtract(newPointOnPlane, this.offset);
|
||||||
actionArgs = {targetPosition: this.targetPosition, linearTimeScale: 0.1};
|
actionArgs = {targetPosition: this.targetPosition, linearTimeScale: 0.1, tag: getTag(), lifetime: ACTION_LIFETIME};
|
||||||
|
|
||||||
beacon.updatePosition(this.targetPosition);
|
beacon.updatePosition(this.targetPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.actionID) {
|
if (!this.actionID) {
|
||||||
this.actionID = Entities.addAction("spring", this.entityID, actionArgs);
|
if (!entityIsGrabbedByOther(this.entityID)) {
|
||||||
|
this.actionID = Entities.addAction("spring", this.entityID, actionArgs);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Entities.updateAction(this.entityID, this.actionID, actionArgs);
|
Entities.updateAction(this.entityID, this.actionID, actionArgs);
|
||||||
}
|
}
|
||||||
|
|
43
examples/toys/basketball_hoop/createHoop.js
Normal file
43
examples/toys/basketball_hoop/createHoop.js
Normal 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
|
||||||
|
});
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -271,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.
|
||||||
|
@ -289,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;
|
||||||
|
@ -1854,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);
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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");
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,6 +49,11 @@ const NLPacket* SentPacketHistory::getPacket(uint16_t sequenceNumber) const {
|
||||||
if (seqDiff < 0) {
|
if (seqDiff < 0) {
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -161,13 +161,16 @@ void DefaultCC::onLoss(SequenceNumber rangeStart, SequenceNumber rangeEnd) {
|
||||||
|
|
||||||
_lastDecreaseMaxSeq = _sendCurrSeqNum;
|
_lastDecreaseMaxSeq = _sendCurrSeqNum;
|
||||||
|
|
||||||
// avoid synchronous rate decrease across connections using randomization
|
if (_avgNAKNum < 1) {
|
||||||
std::random_device rd;
|
_randomDecreaseThreshold = 1;
|
||||||
std::mt19937 generator(rd());
|
} else {
|
||||||
std::uniform_int_distribution<> distribution(1, _avgNAKNum);
|
// avoid synchronous rate decrease across connections using randomization
|
||||||
|
std::random_device rd;
|
||||||
_randomDecreaseThreshold = distribution(generator);
|
std::mt19937 generator(rd());
|
||||||
|
std::uniform_int_distribution<> distribution(1, std::max(1, _avgNAKNum));
|
||||||
|
|
||||||
|
_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
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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; }
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
@ -90,7 +93,7 @@ bool ObjectActionOffset::updateArguments(QVariantMap arguments) {
|
||||||
ok = true;
|
ok = true;
|
||||||
float linearTimeScale =
|
float linearTimeScale =
|
||||||
EntityActionInterface::extractFloatArgument("offset action", arguments, "linearTimeScale", ok, false);
|
EntityActionInterface::extractFloatArgument("offset action", arguments, "linearTimeScale", ok, false);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
linearTimeScale = _linearTimeScale;
|
linearTimeScale = _linearTimeScale;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue