mirror of
https://github.com/overte-org/overte.git
synced 2025-07-23 23:04:03 +02:00
Merge branch 'master' into tony/ik-and-controllers
This commit is contained in:
commit
3149baeefc
52 changed files with 815 additions and 241 deletions
2
cmake/externals/polyvox/CMakeLists.txt
vendored
2
cmake/externals/polyvox/CMakeLists.txt
vendored
|
@ -5,7 +5,7 @@ ExternalProject_Add(
|
||||||
${EXTERNAL_NAME}
|
${EXTERNAL_NAME}
|
||||||
URL http://hifi-public.s3.amazonaws.com/dependencies/polyvox-master-2015-7-15.zip
|
URL http://hifi-public.s3.amazonaws.com/dependencies/polyvox-master-2015-7-15.zip
|
||||||
URL_MD5 9ec6323b87e849ae36e562ae1c7494a9
|
URL_MD5 9ec6323b87e849ae36e562ae1c7494a9
|
||||||
CMAKE_ARGS -DENABLE_EXAMPLES=OFF -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
|
CMAKE_ARGS -DENABLE_EXAMPLES=OFF -DENABLE_BINDINGS=OFF -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
|
||||||
BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build
|
BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build
|
||||||
LOG_DOWNLOAD 1
|
LOG_DOWNLOAD 1
|
||||||
LOG_CONFIGURE 1
|
LOG_CONFIGURE 1
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
|
|
||||||
Script.include("../libraries/utils.js");
|
Script.include("../libraries/utils.js");
|
||||||
|
|
||||||
|
|
||||||
var RIGHT_HAND_CLICK = Controller.findAction("RIGHT_HAND_CLICK");
|
var RIGHT_HAND_CLICK = Controller.findAction("RIGHT_HAND_CLICK");
|
||||||
var rightTriggerAction = RIGHT_HAND_CLICK;
|
var rightTriggerAction = RIGHT_HAND_CLICK;
|
||||||
|
|
||||||
|
@ -22,6 +21,10 @@ var GRAB_USER_DATA_KEY = "grabKey";
|
||||||
var LEFT_HAND_CLICK = Controller.findAction("LEFT_HAND_CLICK");
|
var LEFT_HAND_CLICK = Controller.findAction("LEFT_HAND_CLICK");
|
||||||
var leftTriggerAction = LEFT_HAND_CLICK;
|
var leftTriggerAction = LEFT_HAND_CLICK;
|
||||||
|
|
||||||
|
var LIFETIME = 10;
|
||||||
|
var EXTRA_TIME = 5;
|
||||||
|
var POINTER_CHECK_TIME = 5000;
|
||||||
|
|
||||||
var ZERO_VEC = {
|
var ZERO_VEC = {
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
|
@ -42,7 +45,7 @@ var INTERSECT_COLOR = {
|
||||||
blue: 10
|
blue: 10
|
||||||
};
|
};
|
||||||
|
|
||||||
var GRAB_RADIUS = 1.0;
|
var GRAB_RADIUS = 1.5;
|
||||||
|
|
||||||
var GRAB_COLOR = {
|
var GRAB_COLOR = {
|
||||||
red: 250,
|
red: 250,
|
||||||
|
@ -59,8 +62,15 @@ var TRACTOR_BEAM_VELOCITY_THRESHOLD = 0.5;
|
||||||
|
|
||||||
var RIGHT = 1;
|
var RIGHT = 1;
|
||||||
var LEFT = 0;
|
var LEFT = 0;
|
||||||
var rightController = new controller(RIGHT, rightTriggerAction, right4Action, "right")
|
var rightController = new controller(RIGHT, rightTriggerAction, right4Action, "right");
|
||||||
var leftController = new controller(LEFT, leftTriggerAction, left4Action, "left")
|
var leftController = new controller(LEFT, leftTriggerAction, left4Action, "left");
|
||||||
|
|
||||||
|
|
||||||
|
//Need to wait before calling these methods for some reason...
|
||||||
|
Script.setTimeout(function() {
|
||||||
|
rightController.checkPointer();
|
||||||
|
leftController.checkPointer();
|
||||||
|
}, 100)
|
||||||
|
|
||||||
function controller(side, triggerAction, pullAction, hand) {
|
function controller(side, triggerAction, pullAction, hand) {
|
||||||
this.hand = hand;
|
this.hand = hand;
|
||||||
|
@ -92,7 +102,9 @@ function controller(side, triggerAction, pullAction, hand) {
|
||||||
z: 1000
|
z: 1000
|
||||||
},
|
},
|
||||||
visible: false,
|
visible: false,
|
||||||
|
lifetime: LIFETIME
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -127,6 +139,16 @@ controller.prototype.updateLine = function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
controller.prototype.checkPointer = function() {
|
||||||
|
var self = this;
|
||||||
|
Script.setTimeout(function() {
|
||||||
|
var props = Entities.getEntityProperties(self.pointer);
|
||||||
|
Entities.editEntity(self.pointer, {
|
||||||
|
lifetime: props.age + EXTRA_TIME
|
||||||
|
});
|
||||||
|
self.checkPointer();
|
||||||
|
}, POINTER_CHECK_TIME);
|
||||||
|
}
|
||||||
|
|
||||||
controller.prototype.checkForIntersections = function(origin, direction) {
|
controller.prototype.checkForIntersections = function(origin, direction) {
|
||||||
var pickRay = {
|
var pickRay = {
|
||||||
|
@ -241,14 +263,22 @@ controller.prototype.grabEntity = function() {
|
||||||
this.closeGrabbing = true;
|
this.closeGrabbing = true;
|
||||||
//check if our entity has instructions on how to be grabbed, otherwise, just use default relative position and rotation
|
//check if our entity has instructions on how to be grabbed, otherwise, just use default relative position and rotation
|
||||||
var userData = getEntityUserData(this.grabbedEntity);
|
var userData = getEntityUserData(this.grabbedEntity);
|
||||||
var relativePosition = ZERO_VEC;
|
|
||||||
var relativeRotation = Quat.fromPitchYawRollDegrees(0, 0, 0);
|
var objectRotation = Entities.getEntityProperties(this.grabbedEntity).rotation;
|
||||||
if(userData.spatialKey) {
|
var offsetRotation = Quat.multiply(Quat.inverse(handRotation), objectRotation);
|
||||||
if(userData.spatialKey.relativePosition) {
|
|
||||||
relativePosition = userData.spatialKey.relativePosition;
|
var objectPosition = Entities.getEntityProperties(this.grabbedEntity).position;
|
||||||
|
var offset = Vec3.subtract(objectPosition, handPosition);
|
||||||
|
var offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, offsetRotation)), offset);
|
||||||
|
|
||||||
|
var relativePosition = offsetPosition;
|
||||||
|
var relativeRotation = offsetRotation;
|
||||||
|
if (userData.grabFrame) {
|
||||||
|
if (userData.grabFrame.relativePosition) {
|
||||||
|
relativePosition = userData.grabFrame.relativePosition;
|
||||||
}
|
}
|
||||||
if(userData.spatialKey.relativeRotation) {
|
if (userData.grabFrame.relativeRotation) {
|
||||||
relativeRotation = userData.spatialKey.relativeRotation;
|
relativeRotation = userData.grabFrame.relativeRotation;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.actionID = Entities.addAction("hold", this.grabbedEntity, {
|
this.actionID = Entities.addAction("hold", this.grabbedEntity, {
|
||||||
|
|
|
@ -71,7 +71,7 @@
|
||||||
if (_this.injector == null) {
|
if (_this.injector == null) {
|
||||||
_this.injector = Audio.playSound(_this.song, {
|
_this.injector = Audio.playSound(_this.song, {
|
||||||
position: props.position, // position of boombox entity
|
position: props.position, // position of boombox entity
|
||||||
volume: 0.5,
|
volume: 0.1,
|
||||||
loop: true
|
loop: true
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
// if we're currently being grabbed and if the person grabbing us is the current interfaces avatar.
|
// if we're currently being grabbed and if the person grabbing us is the current interfaces avatar.
|
||||||
// we will watch this for state changes and print out if we're being grabbed or released when it changes.
|
// we will watch this for state changes and print out if we're being grabbed or released when it changes.
|
||||||
update: function() {
|
update: function() {
|
||||||
|
//print("BreakdanceEntity.update() _this.entityID:" + _this.entityID);
|
||||||
var GRAB_USER_DATA_KEY = "grabKey";
|
var GRAB_USER_DATA_KEY = "grabKey";
|
||||||
|
|
||||||
// because the update() signal doesn't have a valid this, we need to use our memorized _this to access our entityID
|
// because the update() signal doesn't have a valid this, we need to use our memorized _this to access our entityID
|
||||||
|
@ -45,14 +46,15 @@
|
||||||
|
|
||||||
// if the grabData says we're being grabbed, and the owner ID is our session, then we are being grabbed by this interface
|
// if the grabData says we're being grabbed, and the owner ID is our session, then we are being grabbed by this interface
|
||||||
if (grabData.activated && grabData.avatarId == MyAvatar.sessionUUID) {
|
if (grabData.activated && grabData.avatarId == MyAvatar.sessionUUID) {
|
||||||
|
//print("BreakdanceEntity.update() [I'm being grabbed] _this.entityID:" + _this.entityID);
|
||||||
if (!_this.beingGrabbed) {
|
if (!_this.beingGrabbed) {
|
||||||
|
print("I'm was grabbed... _this.entityID:" + _this.entityID);
|
||||||
|
|
||||||
// remember we're being grabbed so we can detect being released
|
// remember we're being grabbed so we can detect being released
|
||||||
_this.beingGrabbed = true;
|
_this.beingGrabbed = true;
|
||||||
var props = Entities.getEntityProperties(entityID);
|
var props = Entities.getEntityProperties(entityID);
|
||||||
var puppetPosition = getPuppetPosition(props);
|
var puppetPosition = getPuppetPosition(props);
|
||||||
breakdanceStart(props.modelURL, puppetPosition);
|
breakdanceStart(props.modelURL, puppetPosition);
|
||||||
print("I'm was grabbed...");
|
|
||||||
} else {
|
} else {
|
||||||
breakdanceUpdate();
|
breakdanceUpdate();
|
||||||
}
|
}
|
||||||
|
@ -62,7 +64,7 @@
|
||||||
// if we are not being grabbed, and we previously were, then we were just released, remember that
|
// if we are not being grabbed, and we previously were, then we were just released, remember that
|
||||||
// and print out a message
|
// and print out a message
|
||||||
_this.beingGrabbed = false;
|
_this.beingGrabbed = false;
|
||||||
print("I'm was released...");
|
print("I'm was released... _this.entityID:" + _this.entityID);
|
||||||
breakdanceEnd();
|
breakdanceEnd();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -73,6 +75,7 @@
|
||||||
// * connecting to the update signal so we can check our grabbed state
|
// * connecting to the update signal so we can check our grabbed state
|
||||||
preload: function(entityID) {
|
preload: function(entityID) {
|
||||||
this.entityID = entityID;
|
this.entityID = entityID;
|
||||||
|
print("BreakdanceEntity.preload() this.entityID:" + this.entityID);
|
||||||
Script.update.connect(this.update);
|
Script.update.connect(this.update);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -80,6 +83,7 @@
|
||||||
// or because we've left the domain or quit the application. In all cases we want to unhook our connection
|
// or because we've left the domain or quit the application. In all cases we want to unhook our connection
|
||||||
// to the update signal
|
// to the update signal
|
||||||
unload: function(entityID) {
|
unload: function(entityID) {
|
||||||
|
print("BreakdanceEntity.unload() this.entityID:" + this.entityID);
|
||||||
Script.update.disconnect(this.update);
|
Script.update.disconnect(this.update);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
(function() {
|
(function() {
|
||||||
Script.include("../libraries/utils.js");
|
Script.include("../libraries/utils.js");
|
||||||
SPATIAL_USER_DATA_KEY = "spatialKey";
|
GRAB_FRAME_USER_DATA_KEY = "grabFrame";
|
||||||
this.userData = {};
|
this.userData = {};
|
||||||
|
|
||||||
var TIP_OFFSET_Z = 0.14;
|
var TIP_OFFSET_Z = 0.14;
|
||||||
|
@ -62,13 +62,14 @@
|
||||||
this.sprayStream = function() {
|
this.sprayStream = function() {
|
||||||
var forwardVec = Quat.getFront(self.properties.rotation);
|
var forwardVec = Quat.getFront(self.properties.rotation);
|
||||||
forwardVec = Vec3.multiplyQbyV(Quat.fromPitchYawRollDegrees(0, 90, 0), forwardVec);
|
forwardVec = Vec3.multiplyQbyV(Quat.fromPitchYawRollDegrees(0, 90, 0), forwardVec);
|
||||||
|
forwardVec = Vec3.normalize(forwardVec);
|
||||||
|
|
||||||
var upVec = Quat.getUp(self.properties.rotation);
|
var upVec = Quat.getUp(self.properties.rotation);
|
||||||
var position = Vec3.sum(self.properties.position, Vec3.multiply(forwardVec, TIP_OFFSET_Z));
|
var position = Vec3.sum(self.properties.position, Vec3.multiply(forwardVec, TIP_OFFSET_Z));
|
||||||
position = Vec3.sum(position, Vec3.multiply(upVec, TIP_OFFSET_Y))
|
position = Vec3.sum(position, Vec3.multiply(upVec, TIP_OFFSET_Y))
|
||||||
Entities.editEntity(self.paintStream, {
|
Entities.editEntity(self.paintStream, {
|
||||||
position: position,
|
position: position,
|
||||||
emitVelocity: forwardVec
|
emitVelocity: Vec3.multiply(forwardVec, 4)
|
||||||
});
|
});
|
||||||
|
|
||||||
//Now check for an intersection with an entity
|
//Now check for an intersection with an entity
|
||||||
|
@ -155,12 +156,16 @@
|
||||||
if (this.userData.grabKey && this.userData.grabKey.activated) {
|
if (this.userData.grabKey && this.userData.grabKey.activated) {
|
||||||
this.activated = true;
|
this.activated = true;
|
||||||
}
|
}
|
||||||
if(!this.userData.spatialKey) {
|
if (!this.userData.grabFrame) {
|
||||||
var data = {
|
var data = {
|
||||||
relativePosition: {x: 0, y: 0, z: 0},
|
relativePosition: {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
z: 0
|
||||||
|
},
|
||||||
relativeRotation: Quat.fromPitchYawRollDegrees(0, 0, 0)
|
relativeRotation: Quat.fromPitchYawRollDegrees(0, 0, 0)
|
||||||
}
|
}
|
||||||
setEntityCustomData(SPATIAL_USER_DATA_KEY, this.entityId, data);
|
setEntityCustomData(GRAB_FRAME_USER_DATA_KEY, this.entityId, data);
|
||||||
}
|
}
|
||||||
this.initialize();
|
this.initialize();
|
||||||
}
|
}
|
||||||
|
@ -174,7 +179,6 @@
|
||||||
running: false
|
running: false
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
this.paintStream = Entities.addEntity({
|
this.paintStream = Entities.addEntity({
|
||||||
type: "ParticleEffect",
|
type: "ParticleEffect",
|
||||||
animationSettings: animationSettings,
|
animationSettings: animationSettings,
|
||||||
|
|
7
examples/example/tests/test-includes/a.js
Normal file
7
examples/example/tests/test-includes/a.js
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
// a.js:
|
||||||
|
Script.include('b.js');
|
||||||
|
if (a === undefined) {
|
||||||
|
a = 0;
|
||||||
|
}
|
||||||
|
a++;
|
||||||
|
print('script a:' + a);
|
6
examples/example/tests/test-includes/b.js
Normal file
6
examples/example/tests/test-includes/b.js
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
// b.js:
|
||||||
|
if (b === undefined) {
|
||||||
|
b = 0;
|
||||||
|
}
|
||||||
|
b++;
|
||||||
|
print('script b: ' + b);
|
5
examples/example/tests/test-includes/start.js
Normal file
5
examples/example/tests/test-includes/start.js
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
// start.js:
|
||||||
|
var a, b;
|
||||||
|
print('initially: a:' + a + ' b:' + b);
|
||||||
|
Script.include(['a.js', '../test-includes/a.js', 'b.js', 'a.js']);
|
||||||
|
print('finally a:' + a + ' b:' + b);
|
|
@ -96,21 +96,45 @@ mergeObjects = function(proto, custom) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LOG_WARN = 1;
|
||||||
|
|
||||||
logWarn = function(str) {
|
logWarn = function(str) {
|
||||||
|
if (LOG_WARN) {
|
||||||
print(str);
|
print(str);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_ERROR = 1;
|
||||||
|
|
||||||
logError = function(str) {
|
logError = function(str) {
|
||||||
|
if (LOG_ERROR) {
|
||||||
print(str);
|
print(str);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_INFO = 1;
|
||||||
|
|
||||||
logInfo = function(str) {
|
logInfo = function(str) {
|
||||||
|
if (LOG_INFO) {
|
||||||
print(str);
|
print(str);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DEBUG = 0;
|
||||||
|
|
||||||
logDebug = function(str) {
|
logDebug = function(str) {
|
||||||
|
if (LOG_DEBUG) {
|
||||||
print(str);
|
print(str);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_TRACE = 0;
|
||||||
|
|
||||||
|
logTrace = function(str) {
|
||||||
|
if (LOG_TRACE) {
|
||||||
|
print(str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Computes the penetration between a point and a sphere (centered at the origin)
|
// Computes the penetration between a point and a sphere (centered at the origin)
|
||||||
// if point is inside sphere: returns true and stores the result in 'penetration'
|
// if point is inside sphere: returns true and stores the result in 'penetration'
|
||||||
|
|
|
@ -10,8 +10,7 @@
|
||||||
// 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
|
||||||
//
|
//
|
||||||
|
|
||||||
|
getPuppetPosition = function(props) {
|
||||||
function getPuppetPosition(props) {
|
|
||||||
var MAX_DISTANCE = 10;
|
var MAX_DISTANCE = 10;
|
||||||
var searchPosition = MyAvatar.position;
|
var searchPosition = MyAvatar.position;
|
||||||
|
|
||||||
|
|
45
examples/toys/flashlight/createFlashlight.js
Normal file
45
examples/toys/flashlight/createFlashlight.js
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
//
|
||||||
|
// createFlashligh.js
|
||||||
|
// examples/entityScripts
|
||||||
|
//
|
||||||
|
// Created by Sam Gateau on 9/9/15.
|
||||||
|
// Copyright 2015 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// This is a toy script that create a flashlight entity that lit when grabbed
|
||||||
|
// This can be run from an interface and the flashlight will get deleted from the domain when quitting
|
||||||
|
//
|
||||||
|
// 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("https://hifi-public.s3.amazonaws.com/scripts/utilities.js");
|
||||||
|
|
||||||
|
|
||||||
|
var scriptURL = "https://hifi-public.s3.amazonaws.com/scripts/toys/flashlight/flashlight.js?"+randInt(0,1000);
|
||||||
|
|
||||||
|
var modelURL = "https://hifi-public.s3.amazonaws.com/models/props/flashlight.fbx";
|
||||||
|
|
||||||
|
|
||||||
|
var center = Vec3.sum(Vec3.sum(MyAvatar.position, {x: 0, y: 0.5, z: 0}), Vec3.multiply(0.5, Quat.getFront(Camera.getOrientation())));
|
||||||
|
|
||||||
|
var flashlight = Entities.addEntity({
|
||||||
|
type: "Model",
|
||||||
|
modelURL: modelURL,
|
||||||
|
position: center,
|
||||||
|
dimensions: {
|
||||||
|
x: 0.04,
|
||||||
|
y: 0.15,
|
||||||
|
z: 0.04
|
||||||
|
},
|
||||||
|
collisionsWillMove: true,
|
||||||
|
shapeType: 'box',
|
||||||
|
script: scriptURL
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
function cleanup() {
|
||||||
|
Entities.deleteEntity(flashlight);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Script.scriptEnding.connect(cleanup);
|
154
examples/toys/flashlight/flashlight.js
Normal file
154
examples/toys/flashlight/flashlight.js
Normal file
|
@ -0,0 +1,154 @@
|
||||||
|
//
|
||||||
|
// flashligh.js
|
||||||
|
// examples/entityScripts
|
||||||
|
//
|
||||||
|
// Created by Sam Gateau on 9/9/15.
|
||||||
|
// Copyright 2015 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// This is a toy script that can be added to the Flashlight model entity:
|
||||||
|
// "https://hifi-public.s3.amazonaws.com/models/props/flashlight.fbx"
|
||||||
|
// that creates a spotlight attached with the flashlight model while the entity is grabbed
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
|
||||||
|
function debugPrint(message) {
|
||||||
|
// print(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
Script.include("../../libraries/utils.js");
|
||||||
|
|
||||||
|
var _this;
|
||||||
|
|
||||||
|
// this is the "constructor" for the entity as a JS object we don't do much here, but we do want to remember
|
||||||
|
// our this object, so we can access it in cases where we're called without a this (like in the case of various global signals)
|
||||||
|
Flashlight = function() {
|
||||||
|
_this = this;
|
||||||
|
_this._hasSpotlight = false;
|
||||||
|
_this._spotlight = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
// These constants define the Spotlight position and orientation relative to the model
|
||||||
|
var MODEL_LIGHT_POSITION = {x: 0, y: 0, z: 0};
|
||||||
|
var MODEL_LIGHT_ROTATION = Quat.angleAxis (-90, {x: 1, y: 0, z: 0});
|
||||||
|
|
||||||
|
// Evaluate the world light entity position and orientation from the model ones
|
||||||
|
function evalLightWorldTransform(modelPos, modelRot) {
|
||||||
|
return {p: Vec3.sum(modelPos, Vec3.multiplyQbyV(modelRot, MODEL_LIGHT_POSITION)),
|
||||||
|
q: Quat.multiply(modelRot, MODEL_LIGHT_ROTATION)};
|
||||||
|
};
|
||||||
|
|
||||||
|
Flashlight.prototype = {
|
||||||
|
|
||||||
|
// update() will be called regulary, because we've hooked the update signal in our preload() function
|
||||||
|
// we will check out userData for the grabData. In the case of the hydraGrab script, it will tell us
|
||||||
|
// if we're currently being grabbed and if the person grabbing us is the current interfaces avatar.
|
||||||
|
// we will watch this for state changes and print out if we're being grabbed or released when it changes.
|
||||||
|
update: function() {
|
||||||
|
var GRAB_USER_DATA_KEY = "grabKey";
|
||||||
|
|
||||||
|
// because the update() signal doesn't have a valid this, we need to use our memorized _this to access our entityID
|
||||||
|
var entityID = _this.entityID;
|
||||||
|
|
||||||
|
// we want to assume that if there is no grab data, then we are not being grabbed
|
||||||
|
var defaultGrabData = { activated: false, avatarId: null };
|
||||||
|
|
||||||
|
// this handy function getEntityCustomData() is available in utils.js and it will return just the specific section
|
||||||
|
// of user data we asked for. If it's not available it returns our default data.
|
||||||
|
var grabData = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, defaultGrabData);
|
||||||
|
|
||||||
|
|
||||||
|
// if the grabData says we're being grabbed, and the owner ID is our session, then we are being grabbed by this interface
|
||||||
|
if (grabData.activated && grabData.avatarId == MyAvatar.sessionUUID) {
|
||||||
|
|
||||||
|
// remember we're being grabbed so we can detect being released
|
||||||
|
_this.beingGrabbed = true;
|
||||||
|
|
||||||
|
var modelProperties = Entities.getEntityProperties(entityID);
|
||||||
|
var lightTransform = evalLightWorldTransform(modelProperties.position, modelProperties.rotation);
|
||||||
|
|
||||||
|
// Create the spot light driven by this model if we don;t have one yet
|
||||||
|
// Or make sure to keep it's position in sync
|
||||||
|
if (!_this._hasSpotlight) {
|
||||||
|
|
||||||
|
_this._spotlight = Entities.addEntity({
|
||||||
|
type: "Light",
|
||||||
|
position: lightTransform.p,
|
||||||
|
rotation: lightTransform.q,
|
||||||
|
isSpotlight: true,
|
||||||
|
dimensions: { x: 2, y: 2, z: 20 },
|
||||||
|
color: { red: 255, green: 255, blue: 255 },
|
||||||
|
intensity: 2,
|
||||||
|
exponent: 0.3,
|
||||||
|
cutoff: 20
|
||||||
|
});
|
||||||
|
_this._hasSpotlight = true;
|
||||||
|
|
||||||
|
_this._startModelPosition = modelProperties.position;
|
||||||
|
_this._startModelRotation = modelProperties.rotation;
|
||||||
|
|
||||||
|
debugPrint("Flashlight:: creating a spotlight");
|
||||||
|
} else {
|
||||||
|
// Updating the spotlight
|
||||||
|
Entities.editEntity(_this._spotlight, {position: lightTransform.p, rotation: lightTransform.q});
|
||||||
|
|
||||||
|
debugPrint("Flashlight:: updating the spotlight");
|
||||||
|
}
|
||||||
|
|
||||||
|
debugPrint("I'm being grabbed...");
|
||||||
|
|
||||||
|
} else if (_this.beingGrabbed) {
|
||||||
|
|
||||||
|
if (_this._hasSpotlight) {
|
||||||
|
Entities.deleteEntity(_this._spotlight);
|
||||||
|
debugPrint("Destroying flashlight spotlight...");
|
||||||
|
}
|
||||||
|
_this._hasSpotlight = false;
|
||||||
|
_this._spotlight = null;
|
||||||
|
|
||||||
|
// Reset model to initial position
|
||||||
|
Entities.editEntity(_this.entityID, {position: _this._startModelPosition, rotation: _this._startModelRotation,
|
||||||
|
velocity: {x: 0, y: 0, z: 0}, angularVelocity: {x: 0, y: 0, z: 0}});
|
||||||
|
|
||||||
|
// if we are not being grabbed, and we previously were, then we were just released, remember that
|
||||||
|
// and print out a message
|
||||||
|
_this.beingGrabbed = false;
|
||||||
|
debugPrint("I'm was released...");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// preload() will be called when the entity has become visible (or known) to the interface
|
||||||
|
// it gives us a chance to set our local JavaScript object up. In this case it means:
|
||||||
|
// * remembering our entityID, so we can access it in cases where we're called without an entityID
|
||||||
|
// * connecting to the update signal so we can check our grabbed state
|
||||||
|
preload: function(entityID) {
|
||||||
|
_this.entityID = entityID;
|
||||||
|
|
||||||
|
var modelProperties = Entities.getEntityProperties(entityID);
|
||||||
|
_this._startModelPosition = modelProperties.position;
|
||||||
|
_this._startModelRotation = modelProperties.rotation;
|
||||||
|
|
||||||
|
Script.update.connect(this.update);
|
||||||
|
},
|
||||||
|
|
||||||
|
// unload() will be called when our entity is no longer available. It may be because we were deleted,
|
||||||
|
// or because we've left the domain or quit the application. In all cases we want to unhook our connection
|
||||||
|
// to the update signal
|
||||||
|
unload: function(entityID) {
|
||||||
|
|
||||||
|
if (_this._hasSpotlight) {
|
||||||
|
Entities.deleteEntity(_this._spotlight);
|
||||||
|
}
|
||||||
|
_this._hasSpotlight = false;
|
||||||
|
_this._spotlight = null;
|
||||||
|
|
||||||
|
Script.update.disconnect(this.update);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// entity scripts always need to return a newly constructed object of our type
|
||||||
|
return new Flashlight();
|
||||||
|
})
|
|
@ -52,6 +52,11 @@ Item {
|
||||||
font.pixelSize: root.fontSize
|
font.pixelSize: root.fontSize
|
||||||
text: "Simrate: " + root.simrate
|
text: "Simrate: " + root.simrate
|
||||||
}
|
}
|
||||||
|
Text {
|
||||||
|
color: root.fontColor;
|
||||||
|
font.pixelSize: root.fontSize
|
||||||
|
text: "Avatar Simrate: " + root.avatarSimrate
|
||||||
|
}
|
||||||
Text {
|
Text {
|
||||||
color: root.fontColor;
|
color: root.fontColor;
|
||||||
font.pixelSize: root.fontSize
|
font.pixelSize: root.fontSize
|
||||||
|
|
|
@ -786,6 +786,14 @@ void Application::aboutToQuit() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::cleanupBeforeQuit() {
|
void Application::cleanupBeforeQuit() {
|
||||||
|
// Terminate third party processes so that they're not left running in the event of a subsequent shutdown crash
|
||||||
|
#ifdef HAVE_DDE
|
||||||
|
DependencyManager::destroy<DdeFaceTracker>();
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_IVIEWHMD
|
||||||
|
DependencyManager::destroy<EyeTracker>();
|
||||||
|
#endif
|
||||||
|
|
||||||
if (_keyboardFocusHighlightID > 0) {
|
if (_keyboardFocusHighlightID > 0) {
|
||||||
getOverlays().deleteOverlay(_keyboardFocusHighlightID);
|
getOverlays().deleteOverlay(_keyboardFocusHighlightID);
|
||||||
_keyboardFocusHighlightID = -1;
|
_keyboardFocusHighlightID = -1;
|
||||||
|
@ -802,6 +810,7 @@ void Application::cleanupBeforeQuit() {
|
||||||
|
|
||||||
// first stop all timers directly or by invokeMethod
|
// first stop all timers directly or by invokeMethod
|
||||||
// depending on what thread they run in
|
// depending on what thread they run in
|
||||||
|
_avatarUpdate->terminate();
|
||||||
locationUpdateTimer->stop();
|
locationUpdateTimer->stop();
|
||||||
balanceUpdateTimer->stop();
|
balanceUpdateTimer->stop();
|
||||||
identityPacketTimer->stop();
|
identityPacketTimer->stop();
|
||||||
|
@ -833,13 +842,6 @@ void Application::cleanupBeforeQuit() {
|
||||||
|
|
||||||
// destroy the AudioClient so it and its thread have a chance to go down safely
|
// destroy the AudioClient so it and its thread have a chance to go down safely
|
||||||
DependencyManager::destroy<AudioClient>();
|
DependencyManager::destroy<AudioClient>();
|
||||||
|
|
||||||
#ifdef HAVE_DDE
|
|
||||||
DependencyManager::destroy<DdeFaceTracker>();
|
|
||||||
#endif
|
|
||||||
#ifdef HAVE_IVIEWHMD
|
|
||||||
DependencyManager::destroy<EyeTracker>();
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::emptyLocalCache() {
|
void Application::emptyLocalCache() {
|
||||||
|
@ -1101,6 +1103,7 @@ void Application::paintGL() {
|
||||||
_applicationOverlay.renderOverlay(&renderArgs);
|
_applicationOverlay.renderOverlay(&renderArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_myAvatar->startCapture();
|
||||||
if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON || _myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) {
|
if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON || _myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) {
|
||||||
Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, _myAvatar->getBoomLength() <= MyAvatar::ZOOM_MIN);
|
Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, _myAvatar->getBoomLength() <= MyAvatar::ZOOM_MIN);
|
||||||
Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, !(_myAvatar->getBoomLength() <= MyAvatar::ZOOM_MIN));
|
Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, !(_myAvatar->getBoomLength() <= MyAvatar::ZOOM_MIN));
|
||||||
|
@ -1150,7 +1153,7 @@ void Application::paintGL() {
|
||||||
if (!isHMDMode()) {
|
if (!isHMDMode()) {
|
||||||
_myCamera.update(1.0f / _fps);
|
_myCamera.update(1.0f / _fps);
|
||||||
}
|
}
|
||||||
|
_myAvatar->endCapture();
|
||||||
|
|
||||||
// Primary rendering pass
|
// Primary rendering pass
|
||||||
auto framebufferCache = DependencyManager::get<FramebufferCache>();
|
auto framebufferCache = DependencyManager::get<FramebufferCache>();
|
||||||
|
@ -2172,6 +2175,18 @@ float Application::getAverageSimsPerSecond() {
|
||||||
}
|
}
|
||||||
return _simsPerSecondReport;
|
return _simsPerSecondReport;
|
||||||
}
|
}
|
||||||
|
void Application::setAvatarSimrateSample(float sample) {
|
||||||
|
_avatarSimsPerSecond.updateAverage(sample);
|
||||||
|
}
|
||||||
|
float Application::getAvatarSimrate() {
|
||||||
|
uint64_t now = usecTimestampNow();
|
||||||
|
|
||||||
|
if (now - _lastAvatarSimsPerSecondUpdate > USECS_PER_SECOND) {
|
||||||
|
_avatarSimsPerSecondReport = _avatarSimsPerSecond.getAverage();
|
||||||
|
_lastAvatarSimsPerSecondUpdate = now;
|
||||||
|
}
|
||||||
|
return _avatarSimsPerSecondReport;
|
||||||
|
}
|
||||||
|
|
||||||
void Application::setLowVelocityFilter(bool lowVelocityFilter) {
|
void Application::setLowVelocityFilter(bool lowVelocityFilter) {
|
||||||
InputDevice::setLowVelocityFilter(lowVelocityFilter);
|
InputDevice::setLowVelocityFilter(lowVelocityFilter);
|
||||||
|
@ -2457,8 +2472,20 @@ void Application::init() {
|
||||||
// Make sure any new sounds are loaded as soon as know about them.
|
// Make sure any new sounds are loaded as soon as know about them.
|
||||||
connect(tree, &EntityTree::newCollisionSoundURL, DependencyManager::get<SoundCache>().data(), &SoundCache::getSound);
|
connect(tree, &EntityTree::newCollisionSoundURL, DependencyManager::get<SoundCache>().data(), &SoundCache::getSound);
|
||||||
connect(_myAvatar, &MyAvatar::newCollisionSoundURL, DependencyManager::get<SoundCache>().data(), &SoundCache::getSound);
|
connect(_myAvatar, &MyAvatar::newCollisionSoundURL, DependencyManager::get<SoundCache>().data(), &SoundCache::getSound);
|
||||||
|
|
||||||
|
setAvatarUpdateThreading(Menu::getInstance()->isOptionChecked(MenuOption::EnableAvatarUpdateThreading));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Application::setAvatarUpdateThreading(bool isThreaded) {
|
||||||
|
if (_avatarUpdate) {
|
||||||
|
getMyAvatar()->destroyAnimGraph();
|
||||||
|
_avatarUpdate->terminate();
|
||||||
|
}
|
||||||
|
_avatarUpdate = new AvatarUpdate();
|
||||||
|
_avatarUpdate->initialize(isThreaded);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void Application::closeMirrorView() {
|
void Application::closeMirrorView() {
|
||||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) {
|
if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) {
|
||||||
Menu::getInstance()->triggerOption(MenuOption::Mirror);
|
Menu::getInstance()->triggerOption(MenuOption::Mirror);
|
||||||
|
@ -2535,18 +2562,19 @@ void Application::updateMyAvatarLookAtPosition() {
|
||||||
auto eyeTracker = DependencyManager::get<EyeTracker>();
|
auto eyeTracker = DependencyManager::get<EyeTracker>();
|
||||||
|
|
||||||
bool isLookingAtSomeone = false;
|
bool isLookingAtSomeone = false;
|
||||||
|
bool isHMD = _avatarUpdate->isHMDMode();
|
||||||
glm::vec3 lookAtSpot;
|
glm::vec3 lookAtSpot;
|
||||||
if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
|
if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
|
||||||
// When I am in mirror mode, just look right at the camera (myself); don't switch gaze points because when physically
|
// When I am in mirror mode, just look right at the camera (myself); don't switch gaze points because when physically
|
||||||
// looking in a mirror one's eyes appear steady.
|
// looking in a mirror one's eyes appear steady.
|
||||||
if (!isHMDMode()) {
|
if (!isHMD) {
|
||||||
lookAtSpot = _myCamera.getPosition();
|
lookAtSpot = _myCamera.getPosition();
|
||||||
} else {
|
} else {
|
||||||
lookAtSpot = _myCamera.getPosition() + transformPoint(_myAvatar->getSensorToWorldMatrix(), extractTranslation(getHMDSensorPose()));
|
lookAtSpot = _myCamera.getPosition() + transformPoint(_myAvatar->getSensorToWorldMatrix(), extractTranslation(getHMDSensorPose()));
|
||||||
}
|
}
|
||||||
} else if (eyeTracker->isTracking() && (isHMDMode() || eyeTracker->isSimulating())) {
|
} else if (eyeTracker->isTracking() && (isHMD || eyeTracker->isSimulating())) {
|
||||||
// Look at the point that the user is looking at.
|
// Look at the point that the user is looking at.
|
||||||
if (isHMDMode()) {
|
if (isHMD) {
|
||||||
glm::mat4 headPose = getActiveDisplayPlugin()->getHeadPose();
|
glm::mat4 headPose = getActiveDisplayPlugin()->getHeadPose();
|
||||||
glm::quat hmdRotation = glm::quat_cast(headPose);
|
glm::quat hmdRotation = glm::quat_cast(headPose);
|
||||||
lookAtSpot = _myCamera.getPosition() +
|
lookAtSpot = _myCamera.getPosition() +
|
||||||
|
@ -2589,8 +2617,8 @@ void Application::updateMyAvatarLookAtPosition() {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// I am not looking at anyone else, so just look forward
|
// I am not looking at anyone else, so just look forward
|
||||||
if (isHMDMode()) {
|
if (isHMD) {
|
||||||
glm::mat4 headPose = getActiveDisplayPlugin()->getHeadPose();
|
glm::mat4 headPose = _avatarUpdate->getHeadPose() ;
|
||||||
glm::quat headRotation = glm::quat_cast(headPose);
|
glm::quat headRotation = glm::quat_cast(headPose);
|
||||||
lookAtSpot = _myCamera.getPosition() +
|
lookAtSpot = _myCamera.getPosition() +
|
||||||
_myAvatar->getOrientation() * (headRotation * glm::vec3(0.0f, 0.0f, -TREE_SCALE));
|
_myAvatar->getOrientation() * (headRotation * glm::vec3(0.0f, 0.0f, -TREE_SCALE));
|
||||||
|
@ -2817,9 +2845,6 @@ void Application::update(float deltaTime) {
|
||||||
|
|
||||||
updateThreads(deltaTime); // If running non-threaded, then give the threads some time to process...
|
updateThreads(deltaTime); // If running non-threaded, then give the threads some time to process...
|
||||||
|
|
||||||
//loop through all the other avatars and simulate them...
|
|
||||||
DependencyManager::get<AvatarManager>()->updateOtherAvatars(deltaTime);
|
|
||||||
|
|
||||||
updateCamera(deltaTime); // handle various camera tweaks like off axis projection
|
updateCamera(deltaTime); // handle various camera tweaks like off axis projection
|
||||||
updateDialogs(deltaTime); // update various stats dialogs if present
|
updateDialogs(deltaTime); // update various stats dialogs if present
|
||||||
updateCursor(deltaTime); // Handle cursor updates
|
updateCursor(deltaTime); // Handle cursor updates
|
||||||
|
@ -2891,12 +2916,7 @@ void Application::update(float deltaTime) {
|
||||||
_overlays.update(deltaTime);
|
_overlays.update(deltaTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
_avatarUpdate->synchronousProcess();
|
||||||
PerformanceTimer perfTimer("myAvatar");
|
|
||||||
updateMyAvatarLookAtPosition();
|
|
||||||
// Sample hardware, update view frustum if needed, and send avatar data to mixer/nodes
|
|
||||||
DependencyManager::get<AvatarManager>()->updateMyAvatar(deltaTime);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
{
|
||||||
PerformanceTimer perfTimer("emitSimulating");
|
PerformanceTimer perfTimer("emitSimulating");
|
||||||
|
@ -3477,7 +3497,10 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
|
||||||
|
|
||||||
// FIXME: This preRender call is temporary until we create a separate render::scene for the mirror rendering.
|
// FIXME: This preRender call is temporary until we create a separate render::scene for the mirror rendering.
|
||||||
// Then we can move this logic into the Avatar::simulate call.
|
// Then we can move this logic into the Avatar::simulate call.
|
||||||
|
_myAvatar->startRender();
|
||||||
_myAvatar->preRender(renderArgs);
|
_myAvatar->preRender(renderArgs);
|
||||||
|
_myAvatar->endRender();
|
||||||
|
|
||||||
|
|
||||||
activeRenderingThread = QThread::currentThread();
|
activeRenderingThread = QThread::currentThread();
|
||||||
PROFILE_RANGE(__FUNCTION__);
|
PROFILE_RANGE(__FUNCTION__);
|
||||||
|
@ -3591,7 +3614,9 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
|
||||||
_renderEngine->setRenderContext(renderContext);
|
_renderEngine->setRenderContext(renderContext);
|
||||||
|
|
||||||
// Before the deferred pass, let's try to use the render engine
|
// Before the deferred pass, let's try to use the render engine
|
||||||
|
_myAvatar->startRenderRun();
|
||||||
_renderEngine->run();
|
_renderEngine->run();
|
||||||
|
_myAvatar->endRenderRun();
|
||||||
|
|
||||||
auto engineRC = _renderEngine->getRenderContext();
|
auto engineRC = _renderEngine->getRenderContext();
|
||||||
sceneInterface->setEngineFeedOpaqueItems(engineRC->_numFeedOpaqueItems);
|
sceneInterface->setEngineFeedOpaqueItems(engineRC->_numFeedOpaqueItems);
|
||||||
|
@ -4761,9 +4786,11 @@ void Application::updateDisplayMode() {
|
||||||
qDebug() << "Deferring plugin switch until out of painting";
|
qDebug() << "Deferring plugin switch until out of painting";
|
||||||
// Have the old plugin stop requesting renders
|
// Have the old plugin stop requesting renders
|
||||||
oldDisplayPlugin->stop();
|
oldDisplayPlugin->stop();
|
||||||
QCoreApplication::postEvent(this, new LambdaEvent([this] {
|
QTimer* timer = new QTimer();
|
||||||
|
timer->singleShot(500, [this, timer] {
|
||||||
|
timer->deleteLater();
|
||||||
updateDisplayMode();
|
updateDisplayMode();
|
||||||
}));
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -48,6 +48,7 @@
|
||||||
#include "Menu.h"
|
#include "Menu.h"
|
||||||
#include "Physics.h"
|
#include "Physics.h"
|
||||||
#include "Stars.h"
|
#include "Stars.h"
|
||||||
|
#include "avatar/AvatarUpdate.h"
|
||||||
#include "avatar/Avatar.h"
|
#include "avatar/Avatar.h"
|
||||||
#include "avatar/MyAvatar.h"
|
#include "avatar/MyAvatar.h"
|
||||||
#include "scripting/ControllerScriptingInterface.h"
|
#include "scripting/ControllerScriptingInterface.h"
|
||||||
|
@ -335,6 +336,12 @@ public:
|
||||||
|
|
||||||
const QRect& getMirrorViewRect() const { return _mirrorViewRect; }
|
const QRect& getMirrorViewRect() const { return _mirrorViewRect; }
|
||||||
|
|
||||||
|
void updateMyAvatarLookAtPosition();
|
||||||
|
AvatarUpdate* getAvatarUpdater() { return _avatarUpdate; }
|
||||||
|
MyAvatar* getMyAvatar() { return _myAvatar; }
|
||||||
|
float getAvatarSimrate();
|
||||||
|
void setAvatarSimrateSample(float sample);
|
||||||
|
|
||||||
float getAverageSimsPerSecond();
|
float getAverageSimsPerSecond();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
@ -405,6 +412,7 @@ public slots:
|
||||||
void openUrl(const QUrl& url);
|
void openUrl(const QUrl& url);
|
||||||
|
|
||||||
void updateMyAvatarTransform();
|
void updateMyAvatarTransform();
|
||||||
|
void setAvatarUpdateThreading(bool isThreaded);
|
||||||
|
|
||||||
void domainSettingsReceived(const QJsonObject& domainSettingsObject);
|
void domainSettingsReceived(const QJsonObject& domainSettingsObject);
|
||||||
|
|
||||||
|
@ -482,7 +490,6 @@ private:
|
||||||
// Various helper functions called during update()
|
// Various helper functions called during update()
|
||||||
void updateLOD();
|
void updateLOD();
|
||||||
void updateMouseRay();
|
void updateMouseRay();
|
||||||
void updateMyAvatarLookAtPosition();
|
|
||||||
void updateThreads(float deltaTime);
|
void updateThreads(float deltaTime);
|
||||||
void updateCamera(float deltaTime);
|
void updateCamera(float deltaTime);
|
||||||
void updateDialogs(float deltaTime);
|
void updateDialogs(float deltaTime);
|
||||||
|
@ -551,6 +558,10 @@ private:
|
||||||
|
|
||||||
KeyboardMouseDevice* _keyboardMouseDevice{ nullptr }; // Default input device, the good old keyboard mouse and maybe touchpad
|
KeyboardMouseDevice* _keyboardMouseDevice{ nullptr }; // Default input device, the good old keyboard mouse and maybe touchpad
|
||||||
MyAvatar* _myAvatar; // TODO: move this and relevant code to AvatarManager (or MyAvatar as the case may be)
|
MyAvatar* _myAvatar; // TODO: move this and relevant code to AvatarManager (or MyAvatar as the case may be)
|
||||||
|
AvatarUpdate* _avatarUpdate {nullptr};
|
||||||
|
SimpleMovingAverage _avatarSimsPerSecond {10};
|
||||||
|
int _avatarSimsPerSecondReport {0};
|
||||||
|
quint64 _lastAvatarSimsPerSecondUpdate {0};
|
||||||
Camera _myCamera; // My view onto the world
|
Camera _myCamera; // My view onto the world
|
||||||
Camera _mirrorCamera; // Cammera for mirror view
|
Camera _mirrorCamera; // Cammera for mirror view
|
||||||
QRect _mirrorViewRect;
|
QRect _mirrorViewRect;
|
||||||
|
|
|
@ -447,6 +447,8 @@ Menu::Menu() {
|
||||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderFocusIndicator, 0, false);
|
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderFocusIndicator, 0, false);
|
||||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ShowWhosLookingAtMe, 0, false);
|
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ShowWhosLookingAtMe, 0, false);
|
||||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::FixGaze, 0, false);
|
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::FixGaze, 0, false);
|
||||||
|
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::EnableAvatarUpdateThreading, 0, false,
|
||||||
|
qApp, SLOT(setAvatarUpdateThreading(bool)));
|
||||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::EnableRigAnimations, 0, true,
|
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::EnableRigAnimations, 0, true,
|
||||||
avatar, SLOT(setEnableRigAnimations(bool)));
|
avatar, SLOT(setEnableRigAnimations(bool)));
|
||||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::EnableAnimGraph, 0, false,
|
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::EnableAnimGraph, 0, false,
|
||||||
|
|
|
@ -188,6 +188,7 @@ namespace MenuOption {
|
||||||
const QString EchoServerAudio = "Echo Server Audio";
|
const QString EchoServerAudio = "Echo Server Audio";
|
||||||
const QString EditEntitiesHelp = "Edit Entities Help...";
|
const QString EditEntitiesHelp = "Edit Entities Help...";
|
||||||
const QString Enable3DTVMode = "Enable 3DTV Mode";
|
const QString Enable3DTVMode = "Enable 3DTV Mode";
|
||||||
|
const QString EnableAvatarUpdateThreading = "Enable Avatar Update Threading";
|
||||||
const QString EnableAnimGraph = "Enable Anim Graph";
|
const QString EnableAnimGraph = "Enable Anim Graph";
|
||||||
const QString EnableCharacterController = "Enable avatar collisions";
|
const QString EnableCharacterController = "Enable avatar collisions";
|
||||||
const QString EnableRigAnimations = "Enable Rig Animations";
|
const QString EnableRigAnimations = "Enable Rig Animations";
|
||||||
|
|
|
@ -316,6 +316,7 @@ void Avatar::removeFromScene(AvatarSharedPointer self, std::shared_ptr<render::S
|
||||||
}
|
}
|
||||||
|
|
||||||
void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) {
|
void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) {
|
||||||
|
startRender();
|
||||||
if (_referential) {
|
if (_referential) {
|
||||||
_referential->update();
|
_referential->update();
|
||||||
}
|
}
|
||||||
|
@ -390,6 +391,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (frustum->sphereInFrustum(getPosition(), boundingRadius) == ViewFrustum::OUTSIDE) {
|
if (frustum->sphereInFrustum(getPosition(), boundingRadius) == ViewFrustum::OUTSIDE) {
|
||||||
|
endRender();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -540,6 +542,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) {
|
||||||
if (!isMyAvatar() || cameraMode != CAMERA_MODE_FIRST_PERSON) {
|
if (!isMyAvatar() || cameraMode != CAMERA_MODE_FIRST_PERSON) {
|
||||||
renderDisplayName(batch, *renderArgs->_viewFrustum, renderArgs->_viewport);
|
renderDisplayName(batch, *renderArgs->_viewFrustum, renderArgs->_viewport);
|
||||||
}
|
}
|
||||||
|
endRender();
|
||||||
}
|
}
|
||||||
|
|
||||||
glm::quat Avatar::computeRotationFromBodyToWorldUp(float proportion) const {
|
glm::quat Avatar::computeRotationFromBodyToWorldUp(float proportion) const {
|
||||||
|
@ -1019,6 +1022,7 @@ void Avatar::setBillboard(const QByteArray& billboard) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int Avatar::parseDataFromBuffer(const QByteArray& buffer) {
|
int Avatar::parseDataFromBuffer(const QByteArray& buffer) {
|
||||||
|
startUpdate();
|
||||||
if (!_initialized) {
|
if (!_initialized) {
|
||||||
// now that we have data for this Avatar we are go for init
|
// now that we have data for this Avatar we are go for init
|
||||||
init();
|
init();
|
||||||
|
@ -1034,6 +1038,7 @@ int Avatar::parseDataFromBuffer(const QByteArray& buffer) {
|
||||||
if (_moving && _motionState) {
|
if (_moving && _motionState) {
|
||||||
_motionState->addDirtyFlags(EntityItem::DIRTY_POSITION);
|
_motionState->addDirtyFlags(EntityItem::DIRTY_POSITION);
|
||||||
}
|
}
|
||||||
|
endUpdate();
|
||||||
|
|
||||||
return bytesRead;
|
return bytesRead;
|
||||||
}
|
}
|
||||||
|
|
|
@ -156,6 +156,7 @@ public:
|
||||||
void scaleVectorRelativeToPosition(glm::vec3 &positionToScale) const;
|
void scaleVectorRelativeToPosition(glm::vec3 &positionToScale) const;
|
||||||
|
|
||||||
void slamPosition(const glm::vec3& position);
|
void slamPosition(const glm::vec3& position);
|
||||||
|
virtual void updateAttitude() { _skeletonModel.updateAttitude(); }
|
||||||
|
|
||||||
// Call this when updating Avatar position with a delta. This will allow us to
|
// Call this when updating Avatar position with a delta. This will allow us to
|
||||||
// _accurately_ measure position changes and compute the resulting velocity
|
// _accurately_ measure position changes and compute the resulting velocity
|
||||||
|
|
|
@ -129,7 +129,9 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
||||||
_avatarFades.push_back(avatarIterator.value());
|
_avatarFades.push_back(avatarIterator.value());
|
||||||
avatarIterator = _avatarHash.erase(avatarIterator);
|
avatarIterator = _avatarHash.erase(avatarIterator);
|
||||||
} else {
|
} else {
|
||||||
|
avatar->startUpdate();
|
||||||
avatar->simulate(deltaTime);
|
avatar->simulate(deltaTime);
|
||||||
|
avatar->endUpdate();
|
||||||
++avatarIterator;
|
++avatarIterator;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -148,6 +150,7 @@ void AvatarManager::simulateAvatarFades(float deltaTime) {
|
||||||
render::PendingChanges pendingChanges;
|
render::PendingChanges pendingChanges;
|
||||||
while (fadingIterator != _avatarFades.end()) {
|
while (fadingIterator != _avatarFades.end()) {
|
||||||
auto avatar = std::static_pointer_cast<Avatar>(*fadingIterator);
|
auto avatar = std::static_pointer_cast<Avatar>(*fadingIterator);
|
||||||
|
avatar->startUpdate();
|
||||||
avatar->setTargetScale(avatar->getScale() * SHRINK_RATE, true);
|
avatar->setTargetScale(avatar->getScale() * SHRINK_RATE, true);
|
||||||
if (avatar->getTargetScale() < MIN_FADE_SCALE) {
|
if (avatar->getTargetScale() < MIN_FADE_SCALE) {
|
||||||
avatar->removeFromScene(*fadingIterator, scene, pendingChanges);
|
avatar->removeFromScene(*fadingIterator, scene, pendingChanges);
|
||||||
|
@ -156,6 +159,7 @@ void AvatarManager::simulateAvatarFades(float deltaTime) {
|
||||||
avatar->simulate(deltaTime);
|
avatar->simulate(deltaTime);
|
||||||
++fadingIterator;
|
++fadingIterator;
|
||||||
}
|
}
|
||||||
|
avatar->endUpdate();
|
||||||
}
|
}
|
||||||
scene->enqueuePendingChanges(pendingChanges);
|
scene->enqueuePendingChanges(pendingChanges);
|
||||||
}
|
}
|
||||||
|
|
75
interface/src/avatar/AvatarUpdate.cpp
Normal file
75
interface/src/avatar/AvatarUpdate.cpp
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
//
|
||||||
|
// AvatarUpdate.cpp
|
||||||
|
// interface/src/avatar
|
||||||
|
//
|
||||||
|
// Created by Howard Stearns on 8/18/15.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <DependencyManager.h>
|
||||||
|
#include "Application.h"
|
||||||
|
#include "AvatarManager.h"
|
||||||
|
#include "AvatarUpdate.h"
|
||||||
|
#include <display-plugins/DisplayPlugin.h>
|
||||||
|
#include "InterfaceLogging.h"
|
||||||
|
|
||||||
|
AvatarUpdate::AvatarUpdate() : GenericThread(), _lastAvatarUpdate(0) {
|
||||||
|
setObjectName("Avatar Update"); // GenericThread::initialize uses this to set the thread name.
|
||||||
|
Settings settings;
|
||||||
|
const int DEFAULT_TARGET_AVATAR_SIMRATE = 60;
|
||||||
|
_targetInterval = USECS_PER_SECOND / settings.value("AvatarUpdateTargetSimrate", DEFAULT_TARGET_AVATAR_SIMRATE).toInt();
|
||||||
|
}
|
||||||
|
// We could have the constructor call initialize(), but GenericThread::initialize can take parameters.
|
||||||
|
// Keeping it separately called by the client allows that client to pass those without our
|
||||||
|
// constructor needing to know about them.
|
||||||
|
|
||||||
|
void AvatarUpdate::synchronousProcess() {
|
||||||
|
|
||||||
|
// Keep our own updated value, so that our asynchronous code can consult it.
|
||||||
|
_isHMDMode = Application::getInstance()->isHMDMode();
|
||||||
|
_headPose = Application::getInstance()->getActiveDisplayPlugin()->getHeadPose();
|
||||||
|
|
||||||
|
if (_updateBillboard) {
|
||||||
|
Application::getInstance()->getMyAvatar()->doUpdateBillboard();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isThreaded()) {
|
||||||
|
process();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AvatarUpdate::process() {
|
||||||
|
PerformanceTimer perfTimer("AvatarUpdate");
|
||||||
|
quint64 start = usecTimestampNow();
|
||||||
|
quint64 deltaMicroseconds = start - _lastAvatarUpdate;
|
||||||
|
_lastAvatarUpdate = start;
|
||||||
|
float deltaSeconds = (float) deltaMicroseconds / (float) USECS_PER_SECOND;
|
||||||
|
Application::getInstance()->setAvatarSimrateSample(1.0f / deltaSeconds);
|
||||||
|
|
||||||
|
QSharedPointer<AvatarManager> manager = DependencyManager::get<AvatarManager>();
|
||||||
|
MyAvatar* myAvatar = manager->getMyAvatar();
|
||||||
|
|
||||||
|
//loop through all the other avatars and simulate them...
|
||||||
|
//gets current lookat data, removes missing avatars, etc.
|
||||||
|
manager->updateOtherAvatars(deltaSeconds);
|
||||||
|
|
||||||
|
myAvatar->startUpdate();
|
||||||
|
Application::getInstance()->updateMyAvatarLookAtPosition();
|
||||||
|
// Sample hardware, update view frustum if needed, and send avatar data to mixer/nodes
|
||||||
|
manager->updateMyAvatar(deltaSeconds);
|
||||||
|
myAvatar->endUpdate();
|
||||||
|
|
||||||
|
if (!isThreaded()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
int elapsed = (usecTimestampNow() - start);
|
||||||
|
int usecToSleep = _targetInterval - elapsed;
|
||||||
|
if (usecToSleep < 0) {
|
||||||
|
usecToSleep = 1; // always yield
|
||||||
|
}
|
||||||
|
usleep(usecToSleep);
|
||||||
|
return true;
|
||||||
|
}
|
43
interface/src/avatar/AvatarUpdate.h
Normal file
43
interface/src/avatar/AvatarUpdate.h
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
//
|
||||||
|
// AvatarUpdate.h
|
||||||
|
// interface/src/avatar
|
||||||
|
//
|
||||||
|
// Created by Howard Stearns on 8/18/15.
|
||||||
|
///
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef __hifi__AvatarUpdate__
|
||||||
|
#define __hifi__AvatarUpdate__
|
||||||
|
|
||||||
|
#include <QtCore/QObject>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
// Home for the avatarUpdate operations (e.g., whether on a separate thread, pipelined in various ways, etc.)
|
||||||
|
// This might get folded into AvatarManager.
|
||||||
|
class AvatarUpdate : public GenericThread {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
AvatarUpdate();
|
||||||
|
void synchronousProcess();
|
||||||
|
void setRequestBillboardUpdate(bool needsUpdate) { _updateBillboard = needsUpdate; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
virtual bool process(); // No reason for other classes to invoke this.
|
||||||
|
quint64 _lastAvatarUpdate; // microsoeconds
|
||||||
|
quint64 _targetInterval; // microseconds
|
||||||
|
bool _updateBillboard;
|
||||||
|
|
||||||
|
// Goes away if Application::getActiveDisplayPlugin() and friends are made thread safe:
|
||||||
|
public:
|
||||||
|
bool isHMDMode() { return _isHMDMode; }
|
||||||
|
glm::mat4 getHeadPose() { return _headPose; }
|
||||||
|
private:
|
||||||
|
bool _isHMDMode;
|
||||||
|
glm::mat4 _headPose;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* defined(__hifi__AvatarUpdate__) */
|
|
@ -389,7 +389,7 @@ glm::quat Head::getCameraOrientation() const {
|
||||||
// to change the driving direction while in Oculus mode. It is used to support driving toward where you're
|
// to change the driving direction while in Oculus mode. It is used to support driving toward where you're
|
||||||
// head is looking. Note that in oculus mode, your actual camera view and where your head is looking is not
|
// head is looking. Note that in oculus mode, your actual camera view and where your head is looking is not
|
||||||
// always the same.
|
// always the same.
|
||||||
if (qApp->isHMDMode()) {
|
if (qApp->getAvatarUpdater()->isHMDMode()) {
|
||||||
MyAvatar* myAvatar = dynamic_cast<MyAvatar*>(_owningAvatar);
|
MyAvatar* myAvatar = dynamic_cast<MyAvatar*>(_owningAvatar);
|
||||||
if (myAvatar && myAvatar->getStandingHMDSensorMode()) {
|
if (myAvatar && myAvatar->getStandingHMDSensorMode()) {
|
||||||
return glm::quat_cast(myAvatar->getSensorToWorldMatrix()) * myAvatar->getHMDSensorOrientation();
|
return glm::quat_cast(myAvatar->getSensorToWorldMatrix()) * myAvatar->getHMDSensorOrientation();
|
||||||
|
|
|
@ -266,8 +266,7 @@ void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) {
|
||||||
if (getStandingHMDSensorMode()) {
|
if (getStandingHMDSensorMode()) {
|
||||||
// set the body position/orientation to reflect motion due to the head.
|
// set the body position/orientation to reflect motion due to the head.
|
||||||
auto worldMat = _sensorToWorldMatrix * _bodySensorMatrix;
|
auto worldMat = _sensorToWorldMatrix * _bodySensorMatrix;
|
||||||
setPosition(extractTranslation(worldMat));
|
nextAttitude(extractTranslation(worldMat), glm::quat_cast(worldMat));
|
||||||
setOrientation(glm::quat_cast(worldMat));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -285,7 +284,7 @@ void MyAvatar::updateSensorToWorldMatrix() {
|
||||||
void MyAvatar::updateFromTrackers(float deltaTime) {
|
void MyAvatar::updateFromTrackers(float deltaTime) {
|
||||||
glm::vec3 estimatedPosition, estimatedRotation;
|
glm::vec3 estimatedPosition, estimatedRotation;
|
||||||
|
|
||||||
bool inHmd = qApp->isHMDMode();
|
bool inHmd = qApp->getAvatarUpdater()->isHMDMode();
|
||||||
|
|
||||||
if (isPlaying() && inHmd) {
|
if (isPlaying() && inHmd) {
|
||||||
return;
|
return;
|
||||||
|
@ -705,19 +704,46 @@ float loadSetting(QSettings& settings, const char* name, float defaultValue) {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Resource loading is not yet thread safe. If an animation is not loaded when requested by other than tha main thread,
|
||||||
|
// we block in AnimationHandle::setURL => AnimationCache::getAnimation.
|
||||||
|
// Meanwhile, the main thread will also eventually lock as it tries to render us.
|
||||||
|
// If we demand the animation from the update thread while we're locked, we'll deadlock.
|
||||||
|
// Until we untangle this, code puts the updates back on the main thread temporarilly and starts all the loading.
|
||||||
|
void MyAvatar::safelyLoadAnimations() {
|
||||||
|
qApp->setAvatarUpdateThreading(false);
|
||||||
|
_rig->addAnimationByRole("idle");
|
||||||
|
_rig->addAnimationByRole("walk");
|
||||||
|
_rig->addAnimationByRole("backup");
|
||||||
|
_rig->addAnimationByRole("leftTurn");
|
||||||
|
_rig->addAnimationByRole("rightTurn");
|
||||||
|
_rig->addAnimationByRole("leftStrafe");
|
||||||
|
_rig->addAnimationByRole("rightStrafe");
|
||||||
|
}
|
||||||
|
|
||||||
void MyAvatar::setEnableRigAnimations(bool isEnabled) {
|
void MyAvatar::setEnableRigAnimations(bool isEnabled) {
|
||||||
|
if (isEnabled) {
|
||||||
|
safelyLoadAnimations();
|
||||||
|
}
|
||||||
_rig->setEnableRig(isEnabled);
|
_rig->setEnableRig(isEnabled);
|
||||||
if (!isEnabled) {
|
if (!isEnabled) {
|
||||||
_rig->deleteAnimations();
|
_rig->deleteAnimations();
|
||||||
|
} else if (Menu::getInstance()->isOptionChecked(MenuOption::EnableAvatarUpdateThreading)) {
|
||||||
|
qApp->setAvatarUpdateThreading(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MyAvatar::setEnableAnimGraph(bool isEnabled) {
|
void MyAvatar::setEnableAnimGraph(bool isEnabled) {
|
||||||
|
if (isEnabled) {
|
||||||
|
safelyLoadAnimations();
|
||||||
|
}
|
||||||
_rig->setEnableAnimGraph(isEnabled);
|
_rig->setEnableAnimGraph(isEnabled);
|
||||||
if (isEnabled) {
|
if (isEnabled) {
|
||||||
if (_skeletonModel.readyToAddToScene()) {
|
if (_skeletonModel.readyToAddToScene()) {
|
||||||
initAnimGraph();
|
initAnimGraph();
|
||||||
}
|
}
|
||||||
|
if (Menu::getInstance()->isOptionChecked(MenuOption::EnableAvatarUpdateThreading)) {
|
||||||
|
qApp->setAvatarUpdateThreading(true);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
destroyAnimGraph();
|
destroyAnimGraph();
|
||||||
}
|
}
|
||||||
|
@ -804,6 +830,9 @@ void MyAvatar::loadData() {
|
||||||
|
|
||||||
settings.endGroup();
|
settings.endGroup();
|
||||||
_rig->setEnableRig(Menu::getInstance()->isOptionChecked(MenuOption::EnableRigAnimations));
|
_rig->setEnableRig(Menu::getInstance()->isOptionChecked(MenuOption::EnableRigAnimations));
|
||||||
|
setEnableMeshVisible(Menu::getInstance()->isOptionChecked(MenuOption::MeshVisible));
|
||||||
|
setEnableDebugDrawBindPose(Menu::getInstance()->isOptionChecked(MenuOption::AnimDebugDrawBindPose));
|
||||||
|
setEnableDebugDrawAnimPose(Menu::getInstance()->isOptionChecked(MenuOption::AnimDebugDrawAnimPose));
|
||||||
}
|
}
|
||||||
|
|
||||||
void MyAvatar::saveAttachmentData(const AttachmentData& attachment) const {
|
void MyAvatar::saveAttachmentData(const AttachmentData& attachment) const {
|
||||||
|
@ -1248,7 +1277,7 @@ void MyAvatar::initAnimGraph() {
|
||||||
// or run a local web-server
|
// or run a local web-server
|
||||||
// python -m SimpleHTTPServer&
|
// python -m SimpleHTTPServer&
|
||||||
auto graphUrl = QUrl("https://gist.githubusercontent.com/hyperlogic/e58e0a24cc341ad5d060/raw/2a994bef7726ce8e9efcee7622b8b1a1b6b67490/ik-avatar.json");
|
auto graphUrl = QUrl("https://gist.githubusercontent.com/hyperlogic/e58e0a24cc341ad5d060/raw/2a994bef7726ce8e9efcee7622b8b1a1b6b67490/ik-avatar.json");
|
||||||
_skeletonModel.initAnimGraph(graphUrl, _skeletonModel.getGeometry()->getFBXGeometry());
|
_rig->initAnimGraph(graphUrl, _skeletonModel.getGeometry()->getFBXGeometry());
|
||||||
}
|
}
|
||||||
|
|
||||||
void MyAvatar::destroyAnimGraph() {
|
void MyAvatar::destroyAnimGraph() {
|
||||||
|
@ -1346,7 +1375,7 @@ void MyAvatar::updateOrientation(float deltaTime) {
|
||||||
setOrientation(getOrientation() *
|
setOrientation(getOrientation() *
|
||||||
glm::quat(glm::radians(glm::vec3(0.0f, _bodyYawDelta * deltaTime, 0.0f))));
|
glm::quat(glm::radians(glm::vec3(0.0f, _bodyYawDelta * deltaTime, 0.0f))));
|
||||||
|
|
||||||
if (qApp->isHMDMode()) {
|
if (qApp->getAvatarUpdater()->isHMDMode()) {
|
||||||
glm::quat orientation = glm::quat_cast(getSensorToWorldMatrix()) * getHMDSensorOrientation();
|
glm::quat orientation = glm::quat_cast(getSensorToWorldMatrix()) * getHMDSensorOrientation();
|
||||||
glm::quat bodyOrientation = getWorldBodyOrientation();
|
glm::quat bodyOrientation = getWorldBodyOrientation();
|
||||||
glm::quat localOrientation = glm::inverse(bodyOrientation) * orientation;
|
glm::quat localOrientation = glm::inverse(bodyOrientation) * orientation;
|
||||||
|
@ -1567,6 +1596,7 @@ bool findAvatarAvatarPenetration(const glm::vec3 positionA, float radiusA, float
|
||||||
}
|
}
|
||||||
|
|
||||||
void MyAvatar::maybeUpdateBillboard() {
|
void MyAvatar::maybeUpdateBillboard() {
|
||||||
|
qApp->getAvatarUpdater()->setRequestBillboardUpdate(false);
|
||||||
if (_billboardValid || !(_skeletonModel.isLoadedWithTextures() && getHead()->getFaceModel().isLoadedWithTextures())) {
|
if (_billboardValid || !(_skeletonModel.isLoadedWithTextures() && getHead()->getFaceModel().isLoadedWithTextures())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1575,7 +1605,9 @@ void MyAvatar::maybeUpdateBillboard() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
qApp->getAvatarUpdater()->setRequestBillboardUpdate(true);
|
||||||
|
}
|
||||||
|
void MyAvatar::doUpdateBillboard() {
|
||||||
RenderArgs renderArgs(qApp->getGPUContext());
|
RenderArgs renderArgs(qApp->getGPUContext());
|
||||||
QImage image = qApp->renderAvatarBillboard(&renderArgs);
|
QImage image = qApp->renderAvatarBillboard(&renderArgs);
|
||||||
_billboard.clear();
|
_billboard.clear();
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
#include "Avatar.h"
|
#include "Avatar.h"
|
||||||
|
|
||||||
class ModelItemID;
|
class ModelItemID;
|
||||||
class AnimNode;
|
|
||||||
|
|
||||||
enum eyeContactTarget {
|
enum eyeContactTarget {
|
||||||
LEFT_EYE,
|
LEFT_EYE,
|
||||||
|
@ -151,6 +150,8 @@ public:
|
||||||
static const float ZOOM_DEFAULT;
|
static const float ZOOM_DEFAULT;
|
||||||
|
|
||||||
bool getStandingHMDSensorMode() const { return _standingHMDSensorMode; }
|
bool getStandingHMDSensorMode() const { return _standingHMDSensorMode; }
|
||||||
|
void doUpdateBillboard();
|
||||||
|
void destroyAnimGraph();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void increaseSize();
|
void increaseSize();
|
||||||
|
@ -290,7 +291,7 @@ private:
|
||||||
void maybeUpdateBillboard();
|
void maybeUpdateBillboard();
|
||||||
void initHeadBones();
|
void initHeadBones();
|
||||||
void initAnimGraph();
|
void initAnimGraph();
|
||||||
void destroyAnimGraph();
|
void safelyLoadAnimations();
|
||||||
|
|
||||||
// Avatar Preferences
|
// Avatar Preferences
|
||||||
QUrl _fullAvatarURLFromPreferences;
|
QUrl _fullAvatarURLFromPreferences;
|
||||||
|
|
|
@ -103,13 +103,15 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
|
||||||
_rig->computeMotionAnimationState(deltaTime, _owningAvatar->getPosition(), _owningAvatar->getVelocity(), _owningAvatar->getOrientation());
|
_rig->computeMotionAnimationState(deltaTime, _owningAvatar->getPosition(), _owningAvatar->getVelocity(), _owningAvatar->getOrientation());
|
||||||
}
|
}
|
||||||
Model::updateRig(deltaTime, parentTransform);
|
Model::updateRig(deltaTime, parentTransform);
|
||||||
if (_owningAvatar->isMyAvatar()) {
|
|
||||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
|
||||||
Head* head = _owningAvatar->getHead();
|
Head* head = _owningAvatar->getHead();
|
||||||
|
if (_owningAvatar->isMyAvatar()) {
|
||||||
|
MyAvatar* myAvatar = static_cast<MyAvatar*>(_owningAvatar);
|
||||||
|
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||||
|
|
||||||
Rig::HeadParameters params;
|
Rig::HeadParameters params;
|
||||||
params.modelRotation = getRotation();
|
params.modelRotation = getRotation();
|
||||||
params.modelTranslation = getTranslation();
|
params.modelTranslation = getTranslation();
|
||||||
|
params.enableLean = qApp->getAvatarUpdater()->isHMDMode() && !myAvatar->getStandingHMDSensorMode();
|
||||||
params.leanSideways = head->getFinalLeanSideways();
|
params.leanSideways = head->getFinalLeanSideways();
|
||||||
params.leanForward = head->getFinalLeanForward();
|
params.leanForward = head->getFinalLeanForward();
|
||||||
params.torsoTwist = head->getTorsoTwist();
|
params.torsoTwist = head->getTorsoTwist();
|
||||||
|
@ -133,7 +135,6 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
|
||||||
// However, in the !isLookingAtMe case, the eyes aren't rotating the way they should right now.
|
// However, in the !isLookingAtMe case, the eyes aren't rotating the way they should right now.
|
||||||
// We will revisit that as priorities allow, and particularly after the new rig/animation/joints.
|
// We will revisit that as priorities allow, and particularly after the new rig/animation/joints.
|
||||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||||
Head* head = _owningAvatar->getHead();
|
|
||||||
// If the head is not positioned, updateEyeJoints won't get the math right
|
// If the head is not positioned, updateEyeJoints won't get the math right
|
||||||
glm::quat headOrientation;
|
glm::quat headOrientation;
|
||||||
_rig->getJointRotation(geometry.headJointIndex, headOrientation);
|
_rig->getJointRotation(geometry.headJointIndex, headOrientation);
|
||||||
|
@ -147,13 +148,17 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called by Avatar::simulate after it has set the joint states (fullUpdate true if changed),
|
void SkeletonModel::updateAttitude() {
|
||||||
// but just before head has been simulated.
|
|
||||||
void SkeletonModel::simulate(float deltaTime, bool fullUpdate) {
|
|
||||||
setTranslation(_owningAvatar->getSkeletonPosition());
|
setTranslation(_owningAvatar->getSkeletonPosition());
|
||||||
static const glm::quat refOrientation = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f));
|
static const glm::quat refOrientation = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f));
|
||||||
setRotation(_owningAvatar->getOrientation() * refOrientation);
|
setRotation(_owningAvatar->getOrientation() * refOrientation);
|
||||||
setScale(glm::vec3(1.0f, 1.0f, 1.0f) * _owningAvatar->getScale());
|
setScale(glm::vec3(1.0f, 1.0f, 1.0f) * _owningAvatar->getScale());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called by Avatar::simulate after it has set the joint states (fullUpdate true if changed),
|
||||||
|
// but just before head has been simulated.
|
||||||
|
void SkeletonModel::simulate(float deltaTime, bool fullUpdate) {
|
||||||
|
updateAttitude();
|
||||||
setBlendshapeCoefficients(_owningAvatar->getHead()->getBlendshapeCoefficients());
|
setBlendshapeCoefficients(_owningAvatar->getHead()->getBlendshapeCoefficients());
|
||||||
|
|
||||||
Model::simulate(deltaTime, fullUpdate);
|
Model::simulate(deltaTime, fullUpdate);
|
||||||
|
@ -544,14 +549,24 @@ void SkeletonModel::computeBoundingShape() {
|
||||||
totalExtents.addPoint(glm::vec3(0.0f));
|
totalExtents.addPoint(glm::vec3(0.0f));
|
||||||
int numStates = _rig->getJointStateCount();
|
int numStates = _rig->getJointStateCount();
|
||||||
for (int i = 0; i < numStates; i++) {
|
for (int i = 0; i < numStates; i++) {
|
||||||
// compute the default transform of this joint
|
|
||||||
const JointState& state = _rig->getJointState(i);
|
const JointState& state = _rig->getJointState(i);
|
||||||
|
|
||||||
// Each joint contributes a sphere at its position
|
const glm::mat4& jointTransform = state.getTransform();
|
||||||
glm::vec3 axis(state.getBoneRadius());
|
float scale = extractUniformScale(jointTransform);
|
||||||
glm::vec3 jointPosition = state.getPosition();
|
|
||||||
totalExtents.addPoint(jointPosition + axis);
|
// Each joint contributes a capsule defined by FBXJoint.shapeInfo.
|
||||||
totalExtents.addPoint(jointPosition - axis);
|
// For totalExtents we use the capsule endpoints expanded by the radius.
|
||||||
|
const FBXJointShapeInfo& shapeInfo = geometry.joints.at(i).shapeInfo;
|
||||||
|
for (int j = 0; j < shapeInfo.points.size(); ++j) {
|
||||||
|
glm::vec3 transformedPoint = extractTranslation(jointTransform * glm::translate(shapeInfo.points[j]));
|
||||||
|
vec3 radius(scale * shapeInfo.radius);
|
||||||
|
totalExtents.addPoint(transformedPoint + radius);
|
||||||
|
totalExtents.addPoint(transformedPoint - radius);
|
||||||
|
}
|
||||||
|
// HACK so that default legless robot doesn't knuckle-drag
|
||||||
|
if (shapeInfo.points.size() == 0 && (state.getName() == "LeftFoot" || state.getName() == "RightFoot")) {
|
||||||
|
totalExtents.addPoint(extractTranslation(jointTransform));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// compute bounding shape parameters
|
// compute bounding shape parameters
|
||||||
|
@ -607,6 +622,3 @@ bool SkeletonModel::hasSkeleton() {
|
||||||
void SkeletonModel::onInvalidate() {
|
void SkeletonModel::onInvalidate() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SkeletonModel::initAnimGraph(const QUrl& url, const FBXGeometry& fbxGeometry) {
|
|
||||||
_rig->initAnimGraph(url, fbxGeometry);
|
|
||||||
}
|
|
||||||
|
|
|
@ -31,6 +31,7 @@ public:
|
||||||
|
|
||||||
virtual void simulate(float deltaTime, bool fullUpdate = true);
|
virtual void simulate(float deltaTime, bool fullUpdate = true);
|
||||||
virtual void updateRig(float deltaTime, glm::mat4 parentTransform);
|
virtual void updateRig(float deltaTime, glm::mat4 parentTransform);
|
||||||
|
void updateAttitude();
|
||||||
|
|
||||||
void renderIKConstraints(gpu::Batch& batch);
|
void renderIKConstraints(gpu::Batch& batch);
|
||||||
|
|
||||||
|
@ -105,8 +106,6 @@ public:
|
||||||
|
|
||||||
virtual void onInvalidate() override;
|
virtual void onInvalidate() override;
|
||||||
|
|
||||||
void initAnimGraph(const QUrl& url, const FBXGeometry& fbxGeometry);
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
|
||||||
void skeletonLoaded();
|
void skeletonLoaded();
|
||||||
|
|
|
@ -236,20 +236,17 @@ void DdeFaceTracker::setEnabled(bool enabled) {
|
||||||
cancelCalibration();
|
cancelCalibration();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// isOpen() does not work as one might expect on QUdpSocket; don't test isOpen() before closing socket.
|
// isOpen() does not work as one might expect on QUdpSocket; don't test isOpen() before closing socket.
|
||||||
_udpSocket.close();
|
_udpSocket.close();
|
||||||
if (enabled) {
|
|
||||||
_udpSocket.bind(_host, _serverPort);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Terminate any existing DDE process, perhaps left running after an Interface crash.
|
||||||
|
// Do this even if !enabled in case user reset their settings after crash.
|
||||||
const char* DDE_EXIT_COMMAND = "exit";
|
const char* DDE_EXIT_COMMAND = "exit";
|
||||||
|
_udpSocket.bind(_host, _serverPort);
|
||||||
|
_udpSocket.writeDatagram(DDE_EXIT_COMMAND, DDE_SERVER_ADDR, _controlPort);
|
||||||
|
|
||||||
if (enabled && !_ddeProcess) {
|
if (enabled && !_ddeProcess) {
|
||||||
// Terminate any existing DDE process, perhaps left running after an Interface crash
|
|
||||||
_udpSocket.writeDatagram(DDE_EXIT_COMMAND, DDE_SERVER_ADDR, _controlPort);
|
|
||||||
_ddeStopping = false;
|
_ddeStopping = false;
|
||||||
|
|
||||||
qCDebug(interfaceapp) << "DDE Face Tracker: Starting";
|
qCDebug(interfaceapp) << "DDE Face Tracker: Starting";
|
||||||
_ddeProcess = new QProcess(qApp);
|
_ddeProcess = new QProcess(qApp);
|
||||||
connect(_ddeProcess, SIGNAL(finished(int, QProcess::ExitStatus)), SLOT(processFinished(int, QProcess::ExitStatus)));
|
connect(_ddeProcess, SIGNAL(finished(int, QProcess::ExitStatus)), SLOT(processFinished(int, QProcess::ExitStatus)));
|
||||||
|
@ -258,7 +255,6 @@ void DdeFaceTracker::setEnabled(bool enabled) {
|
||||||
|
|
||||||
if (!enabled && _ddeProcess) {
|
if (!enabled && _ddeProcess) {
|
||||||
_ddeStopping = true;
|
_ddeStopping = true;
|
||||||
_udpSocket.writeDatagram(DDE_EXIT_COMMAND, DDE_SERVER_ADDR, _controlPort);
|
|
||||||
qCDebug(interfaceapp) << "DDE Face Tracker: Stopping";
|
qCDebug(interfaceapp) << "DDE Face Tracker: Stopping";
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -115,6 +115,7 @@ void Stats::updateStats() {
|
||||||
STAT_UPDATE(serverCount, nodeList->size());
|
STAT_UPDATE(serverCount, nodeList->size());
|
||||||
STAT_UPDATE(framerate, (int)qApp->getFps());
|
STAT_UPDATE(framerate, (int)qApp->getFps());
|
||||||
STAT_UPDATE(simrate, (int)Application::getInstance()->getAverageSimsPerSecond());
|
STAT_UPDATE(simrate, (int)Application::getInstance()->getAverageSimsPerSecond());
|
||||||
|
STAT_UPDATE(avatarSimrate, (int)qApp->getAvatarSimrate());
|
||||||
|
|
||||||
auto bandwidthRecorder = DependencyManager::get<BandwidthRecorder>();
|
auto bandwidthRecorder = DependencyManager::get<BandwidthRecorder>();
|
||||||
STAT_UPDATE(packetInCount, bandwidthRecorder->getCachedTotalAverageInputPacketsPerSecond());
|
STAT_UPDATE(packetInCount, bandwidthRecorder->getCachedTotalAverageInputPacketsPerSecond());
|
||||||
|
|
|
@ -31,6 +31,7 @@ class Stats : public QQuickItem {
|
||||||
STATS_PROPERTY(int, serverCount, 0)
|
STATS_PROPERTY(int, serverCount, 0)
|
||||||
STATS_PROPERTY(int, framerate, 0)
|
STATS_PROPERTY(int, framerate, 0)
|
||||||
STATS_PROPERTY(int, simrate, 0)
|
STATS_PROPERTY(int, simrate, 0)
|
||||||
|
STATS_PROPERTY(int, avatarSimrate, 0)
|
||||||
STATS_PROPERTY(int, avatarCount, 0)
|
STATS_PROPERTY(int, avatarCount, 0)
|
||||||
STATS_PROPERTY(int, packetInCount, 0)
|
STATS_PROPERTY(int, packetInCount, 0)
|
||||||
STATS_PROPERTY(int, packetOutCount, 0)
|
STATS_PROPERTY(int, packetOutCount, 0)
|
||||||
|
@ -98,6 +99,7 @@ signals:
|
||||||
void serverCountChanged();
|
void serverCountChanged();
|
||||||
void framerateChanged();
|
void framerateChanged();
|
||||||
void simrateChanged();
|
void simrateChanged();
|
||||||
|
void avatarSimrateChanged();
|
||||||
void avatarCountChanged();
|
void avatarCountChanged();
|
||||||
void packetInCountChanged();
|
void packetInCountChanged();
|
||||||
void packetOutCountChanged();
|
void packetOutCountChanged();
|
||||||
|
|
|
@ -41,7 +41,6 @@ void JointState::copyState(const JointState& other) {
|
||||||
// DO NOT copy _constraint
|
// DO NOT copy _constraint
|
||||||
_name = other._name;
|
_name = other._name;
|
||||||
_isFree = other._isFree;
|
_isFree = other._isFree;
|
||||||
_boneRadius = other._boneRadius;
|
|
||||||
_parentIndex = other._parentIndex;
|
_parentIndex = other._parentIndex;
|
||||||
_defaultRotation = other._defaultRotation;
|
_defaultRotation = other._defaultRotation;
|
||||||
_inverseDefaultRotation = other._inverseDefaultRotation;
|
_inverseDefaultRotation = other._inverseDefaultRotation;
|
||||||
|
@ -58,7 +57,6 @@ JointState::JointState(const FBXJoint& joint) {
|
||||||
_rotationInConstrainedFrame = joint.rotation;
|
_rotationInConstrainedFrame = joint.rotation;
|
||||||
_name = joint.name;
|
_name = joint.name;
|
||||||
_isFree = joint.isFree;
|
_isFree = joint.isFree;
|
||||||
_boneRadius = joint.boneRadius;
|
|
||||||
_parentIndex = joint.parentIndex;
|
_parentIndex = joint.parentIndex;
|
||||||
_translation = joint.translation;
|
_translation = joint.translation;
|
||||||
_defaultRotation = joint.rotation;
|
_defaultRotation = joint.rotation;
|
||||||
|
|
|
@ -118,7 +118,6 @@ public:
|
||||||
const glm::quat& getDefaultRotation() const { return _defaultRotation; }
|
const glm::quat& getDefaultRotation() const { return _defaultRotation; }
|
||||||
const glm::quat& getInverseDefaultRotation() const { return _inverseDefaultRotation; }
|
const glm::quat& getInverseDefaultRotation() const { return _inverseDefaultRotation; }
|
||||||
const QString& getName() const { return _name; }
|
const QString& getName() const { return _name; }
|
||||||
float getBoneRadius() const { return _boneRadius; }
|
|
||||||
bool getIsFree() const { return _isFree; }
|
bool getIsFree() const { return _isFree; }
|
||||||
float getAnimationPriority() const { return _animationPriority; }
|
float getAnimationPriority() const { return _animationPriority; }
|
||||||
void setAnimationPriority(float priority) { _animationPriority = priority; }
|
void setAnimationPriority(float priority) { _animationPriority = priority; }
|
||||||
|
@ -149,7 +148,6 @@ private:
|
||||||
QString _name;
|
QString _name;
|
||||||
int _parentIndex;
|
int _parentIndex;
|
||||||
bool _isFree;
|
bool _isFree;
|
||||||
float _boneRadius;
|
|
||||||
glm::vec3 _rotationMin;
|
glm::vec3 _rotationMin;
|
||||||
glm::vec3 _rotationMax;
|
glm::vec3 _rotationMax;
|
||||||
glm::quat _preRotation;
|
glm::quat _preRotation;
|
||||||
|
|
|
@ -477,7 +477,7 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos
|
||||||
}
|
}
|
||||||
|
|
||||||
if (glm::length(localVel) > moveThresh) {
|
if (glm::length(localVel) > moveThresh) {
|
||||||
if (fabs(forwardSpeed) > 0.5f * fabs(lateralSpeed)) {
|
if (fabsf(forwardSpeed) > 0.5f * fabsf(lateralSpeed)) {
|
||||||
if (forwardSpeed > 0.0f) {
|
if (forwardSpeed > 0.0f) {
|
||||||
// forward
|
// forward
|
||||||
_animVars.set("isMovingForward", true);
|
_animVars.set("isMovingForward", true);
|
||||||
|
@ -501,7 +501,7 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos
|
||||||
}
|
}
|
||||||
_state = RigRole::Move;
|
_state = RigRole::Move;
|
||||||
} else {
|
} else {
|
||||||
if (fabs(turningSpeed) > turnThresh) {
|
if (fabsf(turningSpeed) > turnThresh) {
|
||||||
if (turningSpeed > 0.0f) {
|
if (turningSpeed > 0.0f) {
|
||||||
// turning right
|
// turning right
|
||||||
_animVars.set("isTurningRight", true);
|
_animVars.set("isTurningRight", true);
|
||||||
|
@ -928,13 +928,6 @@ void Rig::updateVisibleJointStates() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Rig::setJointTransform(int jointIndex, glm::mat4 newTransform) {
|
|
||||||
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_jointStates[jointIndex].setTransform(newTransform);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Rig::setJointVisibleTransform(int jointIndex, glm::mat4 newTransform) {
|
void Rig::setJointVisibleTransform(int jointIndex, glm::mat4 newTransform) {
|
||||||
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
|
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
|
||||||
return;
|
return;
|
||||||
|
@ -957,7 +950,9 @@ glm::quat Rig::getJointDefaultRotationInParentFrame(int jointIndex) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Rig::updateFromHeadParameters(const HeadParameters& params) {
|
void Rig::updateFromHeadParameters(const HeadParameters& params) {
|
||||||
|
if (params.enableLean) {
|
||||||
updateLeanJoint(params.leanJointIndex, params.leanSideways, params.leanForward, params.torsoTwist);
|
updateLeanJoint(params.leanJointIndex, params.leanSideways, params.leanForward, params.torsoTwist);
|
||||||
|
}
|
||||||
updateNeckJoint(params.neckJointIndex, params.localHeadOrientation, params.leanSideways, params.leanForward, params.torsoTwist);
|
updateNeckJoint(params.neckJointIndex, params.localHeadOrientation, params.leanSideways, params.leanForward, params.torsoTwist);
|
||||||
updateEyeJoints(params.leftEyeJointIndex, params.rightEyeJointIndex, params.modelTranslation, params.modelRotation,
|
updateEyeJoints(params.leftEyeJointIndex, params.rightEyeJointIndex, params.modelTranslation, params.modelRotation,
|
||||||
params.worldHeadOrientation, params.eyeLookAt, params.eyeSaccade);
|
params.worldHeadOrientation, params.eyeLookAt, params.eyeSaccade);
|
||||||
|
|
|
@ -56,6 +56,7 @@ public:
|
||||||
float leanSideways = 0.0f; // degrees
|
float leanSideways = 0.0f; // degrees
|
||||||
float leanForward = 0.0f; // degrees
|
float leanForward = 0.0f; // degrees
|
||||||
float torsoTwist = 0.0f; // degrees
|
float torsoTwist = 0.0f; // degrees
|
||||||
|
bool enableLean = false;
|
||||||
glm::quat modelRotation = glm::quat();
|
glm::quat modelRotation = glm::quat();
|
||||||
glm::quat localHeadOrientation = glm::quat();
|
glm::quat localHeadOrientation = glm::quat();
|
||||||
glm::quat worldHeadOrientation = glm::quat();
|
glm::quat worldHeadOrientation = glm::quat();
|
||||||
|
@ -132,7 +133,6 @@ public:
|
||||||
glm::vec3 translation, glm::quat rotation) const;
|
glm::vec3 translation, glm::quat rotation) const;
|
||||||
bool getVisibleJointRotationInWorldFrame(int jointIndex, glm::quat& result, glm::quat rotation) const;
|
bool getVisibleJointRotationInWorldFrame(int jointIndex, glm::quat& result, glm::quat rotation) const;
|
||||||
glm::mat4 getJointTransform(int jointIndex) const;
|
glm::mat4 getJointTransform(int jointIndex) const;
|
||||||
void setJointTransform(int jointIndex, glm::mat4 newTransform);
|
|
||||||
glm::mat4 getJointVisibleTransform(int jointIndex) const;
|
glm::mat4 getJointVisibleTransform(int jointIndex) const;
|
||||||
void setJointVisibleTransform(int jointIndex, glm::mat4 newTransform);
|
void setJointVisibleTransform(int jointIndex, glm::mat4 newTransform);
|
||||||
// Start or stop animations as needed.
|
// Start or stop animations as needed.
|
||||||
|
|
|
@ -110,6 +110,53 @@ void AvatarData::setOrientation(const glm::quat& orientation, bool overideRefere
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// There are a number of possible strategies for this set of tools through endRender, below.
|
||||||
|
void AvatarData::nextAttitude(glm::vec3 position, glm::quat orientation) {
|
||||||
|
avatarLock.lock();
|
||||||
|
setPosition(position, true);
|
||||||
|
setOrientation(orientation, true);
|
||||||
|
avatarLock.unlock();
|
||||||
|
}
|
||||||
|
void AvatarData::startCapture() {
|
||||||
|
avatarLock.lock();
|
||||||
|
assert(_nextAllowed);
|
||||||
|
_nextAllowed = false;
|
||||||
|
_nextPosition = getPosition();
|
||||||
|
_nextOrientation = getOrientation();
|
||||||
|
}
|
||||||
|
void AvatarData::endCapture() {
|
||||||
|
avatarLock.unlock();
|
||||||
|
}
|
||||||
|
void AvatarData::startUpdate() {
|
||||||
|
avatarLock.lock();
|
||||||
|
}
|
||||||
|
void AvatarData::endUpdate() {
|
||||||
|
avatarLock.unlock();
|
||||||
|
}
|
||||||
|
void AvatarData::startRenderRun() {
|
||||||
|
// I'd like to get rid of this and just (un)lock at (end-)startRender.
|
||||||
|
// But somehow that causes judder in rotations.
|
||||||
|
avatarLock.lock();
|
||||||
|
}
|
||||||
|
void AvatarData::endRenderRun() {
|
||||||
|
avatarLock.unlock();
|
||||||
|
}
|
||||||
|
void AvatarData::startRender() {
|
||||||
|
glm::vec3 pos = getPosition();
|
||||||
|
glm::quat rot = getOrientation();
|
||||||
|
setPosition(_nextPosition, true);
|
||||||
|
setOrientation(_nextOrientation, true);
|
||||||
|
updateAttitude();
|
||||||
|
_nextPosition = pos;
|
||||||
|
_nextOrientation = rot;
|
||||||
|
}
|
||||||
|
void AvatarData::endRender() {
|
||||||
|
setPosition(_nextPosition, true);
|
||||||
|
setOrientation(_nextOrientation, true);
|
||||||
|
updateAttitude();
|
||||||
|
_nextAllowed = true;
|
||||||
|
}
|
||||||
|
|
||||||
float AvatarData::getTargetScale() const {
|
float AvatarData::getTargetScale() const {
|
||||||
if (_referential) {
|
if (_referential) {
|
||||||
_referential->update();
|
_referential->update();
|
||||||
|
|
|
@ -200,6 +200,17 @@ public:
|
||||||
glm::quat getOrientation() const;
|
glm::quat getOrientation() const;
|
||||||
virtual void setOrientation(const glm::quat& orientation, bool overideReferential = false);
|
virtual void setOrientation(const glm::quat& orientation, bool overideReferential = false);
|
||||||
|
|
||||||
|
void nextAttitude(glm::vec3 position, glm::quat orientation); // Can be safely called at any time.
|
||||||
|
void startCapture(); // start/end of the period in which the latest values are about to be captured for camera, etc.
|
||||||
|
void endCapture();
|
||||||
|
void startUpdate(); // start/end of update iteration
|
||||||
|
void endUpdate();
|
||||||
|
void startRender(); // start/end of rendering of this object
|
||||||
|
void startRenderRun(); // start/end of entire scene.
|
||||||
|
void endRenderRun();
|
||||||
|
void endRender();
|
||||||
|
virtual void updateAttitude() {} // Tell skeleton mesh about changes
|
||||||
|
|
||||||
glm::quat getHeadOrientation() const { return _headData->getOrientation(); }
|
glm::quat getHeadOrientation() const { return _headData->getOrientation(); }
|
||||||
void setHeadOrientation(const glm::quat& orientation) { _headData->setOrientation(orientation); }
|
void setHeadOrientation(const glm::quat& orientation) { _headData->setOrientation(orientation); }
|
||||||
|
|
||||||
|
@ -358,6 +369,10 @@ protected:
|
||||||
float _bodyPitch; // degrees
|
float _bodyPitch; // degrees
|
||||||
float _bodyRoll; // degrees
|
float _bodyRoll; // degrees
|
||||||
|
|
||||||
|
glm::vec3 _nextPosition {};
|
||||||
|
glm::quat _nextOrientation {};
|
||||||
|
bool _nextAllowed {true};
|
||||||
|
|
||||||
// Body scale
|
// Body scale
|
||||||
float _targetScale;
|
float _targetScale;
|
||||||
|
|
||||||
|
@ -407,6 +422,8 @@ protected:
|
||||||
|
|
||||||
SimpleMovingAverage _averageBytesReceived;
|
SimpleMovingAverage _averageBytesReceived;
|
||||||
|
|
||||||
|
QMutex avatarLock; // Name is redundant, but it aids searches.
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static QUrl _defaultFullAvatarModelUrl;
|
static QUrl _defaultFullAvatarModelUrl;
|
||||||
// privatize the copy constructor and assignment operator so they cannot be called
|
// privatize the copy constructor and assignment operator so they cannot be called
|
||||||
|
|
|
@ -1257,21 +1257,7 @@ QString getString(const QVariant& value) {
|
||||||
return list.isEmpty() ? value.toString() : list.at(0).toString();
|
return list.isEmpty() ? value.toString() : list.at(0).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
class JointShapeInfo {
|
typedef std::vector<glm::vec3> ShapeVertices;
|
||||||
public:
|
|
||||||
JointShapeInfo() : numVertices(0),
|
|
||||||
sumVertexWeights(0.0f), sumWeightedRadii(0.0f), numVertexWeights(0),
|
|
||||||
boneBegin(0.0f), averageRadius(0.0f) {
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE: the points here are in the "joint frame" which has the "jointEnd" at the origin
|
|
||||||
int numVertices; // num vertices from contributing meshes
|
|
||||||
float sumVertexWeights; // sum of all vertex weights
|
|
||||||
float sumWeightedRadii; // sum of weighted vertices
|
|
||||||
int numVertexWeights; // num vertices that contributed to sums
|
|
||||||
glm::vec3 boneBegin; // parent joint location (in joint frame)
|
|
||||||
float averageRadius;
|
|
||||||
};
|
|
||||||
|
|
||||||
class AnimationCurve {
|
class AnimationCurve {
|
||||||
public:
|
public:
|
||||||
|
@ -2282,22 +2268,21 @@ FBXGeometry* extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping
|
||||||
joint.postTransform = model.postTransform;
|
joint.postTransform = model.postTransform;
|
||||||
joint.rotationMin = model.rotationMin;
|
joint.rotationMin = model.rotationMin;
|
||||||
joint.rotationMax = model.rotationMax;
|
joint.rotationMax = model.rotationMax;
|
||||||
glm::quat combinedRotation = model.preRotation * model.rotation * model.postRotation;
|
glm::quat combinedRotation = joint.preRotation * joint.rotation * joint.postRotation;
|
||||||
if (joint.parentIndex == -1) {
|
if (joint.parentIndex == -1) {
|
||||||
joint.transform = geometry.offset * glm::translate(model.translation) * model.preTransform *
|
joint.transform = geometry.offset * glm::translate(joint.translation) * joint.preTransform *
|
||||||
glm::mat4_cast(combinedRotation) * model.postTransform;
|
glm::mat4_cast(combinedRotation) * joint.postTransform;
|
||||||
joint.inverseDefaultRotation = glm::inverse(combinedRotation);
|
joint.inverseDefaultRotation = glm::inverse(combinedRotation);
|
||||||
joint.distanceToParent = 0.0f;
|
joint.distanceToParent = 0.0f;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
const FBXJoint& parentJoint = geometry.joints.at(joint.parentIndex);
|
const FBXJoint& parentJoint = geometry.joints.at(joint.parentIndex);
|
||||||
joint.transform = parentJoint.transform * glm::translate(model.translation) *
|
joint.transform = parentJoint.transform * glm::translate(joint.translation) *
|
||||||
model.preTransform * glm::mat4_cast(combinedRotation) * model.postTransform;
|
joint.preTransform * glm::mat4_cast(combinedRotation) * joint.postTransform;
|
||||||
joint.inverseDefaultRotation = glm::inverse(combinedRotation) * parentJoint.inverseDefaultRotation;
|
joint.inverseDefaultRotation = glm::inverse(combinedRotation) * parentJoint.inverseDefaultRotation;
|
||||||
joint.distanceToParent = glm::distance(extractTranslation(parentJoint.transform),
|
joint.distanceToParent = glm::distance(extractTranslation(parentJoint.transform),
|
||||||
extractTranslation(joint.transform));
|
extractTranslation(joint.transform));
|
||||||
}
|
}
|
||||||
joint.boneRadius = 0.0f;
|
|
||||||
joint.inverseBindRotation = joint.inverseDefaultRotation;
|
joint.inverseBindRotation = joint.inverseDefaultRotation;
|
||||||
joint.name = model.name;
|
joint.name = model.name;
|
||||||
|
|
||||||
|
@ -2326,9 +2311,10 @@ FBXGeometry* extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping
|
||||||
zCurve.values.isEmpty() ? defaultValues.z : zCurve.values.at(i % zCurve.values.size()))));
|
zCurve.values.isEmpty() ? defaultValues.z : zCurve.values.at(i % zCurve.values.size()))));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// for each joint we allocate a JointShapeInfo in which we'll store collision shape info
|
|
||||||
QVector<JointShapeInfo> jointShapeInfos;
|
// NOTE: shapeVertices are in joint-frame
|
||||||
jointShapeInfos.resize(geometry.joints.size());
|
QVector<ShapeVertices> shapeVertices;
|
||||||
|
shapeVertices.resize(geometry.joints.size());
|
||||||
|
|
||||||
// find our special joints
|
// find our special joints
|
||||||
geometry.leftEyeJointIndex = modelIDs.indexOf(jointEyeLeftID);
|
geometry.leftEyeJointIndex = modelIDs.indexOf(jointEyeLeftID);
|
||||||
|
@ -2585,8 +2571,10 @@ FBXGeometry* extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping
|
||||||
boneDirection /= boneLength;
|
boneDirection /= boneLength;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
float radiusScale = extractUniformScale(joint.transform * fbxCluster.inverseBindMatrix);
|
|
||||||
JointShapeInfo& jointShapeInfo = jointShapeInfos[jointIndex];
|
float clusterScale = extractUniformScale(fbxCluster.inverseBindMatrix);
|
||||||
|
glm::mat4 meshToJoint = glm::inverse(joint.bindTransform) * modelTransform;
|
||||||
|
ShapeVertices& points = shapeVertices[jointIndex];
|
||||||
|
|
||||||
float totalWeight = 0.0f;
|
float totalWeight = 0.0f;
|
||||||
for (int j = 0; j < cluster.indices.size(); j++) {
|
for (int j = 0; j < cluster.indices.size(); j++) {
|
||||||
|
@ -2595,18 +2583,13 @@ FBXGeometry* extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping
|
||||||
totalWeight += weight;
|
totalWeight += weight;
|
||||||
for (QMultiHash<int, int>::const_iterator it = extracted.newIndices.constFind(oldIndex);
|
for (QMultiHash<int, int>::const_iterator it = extracted.newIndices.constFind(oldIndex);
|
||||||
it != extracted.newIndices.end() && it.key() == oldIndex; it++) {
|
it != extracted.newIndices.end() && it.key() == oldIndex; it++) {
|
||||||
// expand the bone radius for vertices with at least 1/4 weight
|
|
||||||
|
// remember vertices with at least 1/4 weight
|
||||||
const float EXPANSION_WEIGHT_THRESHOLD = 0.25f;
|
const float EXPANSION_WEIGHT_THRESHOLD = 0.25f;
|
||||||
if (weight > EXPANSION_WEIGHT_THRESHOLD) {
|
if (weight > EXPANSION_WEIGHT_THRESHOLD) {
|
||||||
const glm::vec3& vertex = extracted.mesh.vertices.at(it.value());
|
// transform to joint-frame and save for later
|
||||||
float proj = glm::dot(boneDirection, boneEnd - vertex);
|
const glm::mat4 vertexTransform = meshToJoint * glm::translate(extracted.mesh.vertices.at(it.value()));
|
||||||
float radiusWeight = (proj < 0.0f || proj > boneLength) ? 0.5f * weight : weight;
|
points.push_back(extractTranslation(vertexTransform) * clusterScale);
|
||||||
|
|
||||||
jointShapeInfo.sumVertexWeights += radiusWeight;
|
|
||||||
jointShapeInfo.sumWeightedRadii += radiusWeight * radiusScale * glm::distance(vertex, boneEnd - boneDirection * proj);
|
|
||||||
++jointShapeInfo.numVertexWeights;
|
|
||||||
|
|
||||||
++jointShapeInfo.numVertices;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// look for an unused slot in the weights vector
|
// look for an unused slot in the weights vector
|
||||||
|
@ -2649,54 +2632,16 @@ FBXGeometry* extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping
|
||||||
// this is a single-mesh joint
|
// this is a single-mesh joint
|
||||||
int jointIndex = maxJointIndex;
|
int jointIndex = maxJointIndex;
|
||||||
FBXJoint& joint = geometry.joints[jointIndex];
|
FBXJoint& joint = geometry.joints[jointIndex];
|
||||||
JointShapeInfo& jointShapeInfo = jointShapeInfos[jointIndex];
|
|
||||||
|
|
||||||
glm::mat4 transformJointToMesh = inverseModelTransform * joint.bindTransform;
|
// transform cluster vertices to joint-frame and save for later
|
||||||
glm::vec3 boneEnd = extractTranslation(transformJointToMesh);
|
float clusterScale = extractUniformScale(firstFBXCluster.inverseBindMatrix);
|
||||||
glm::vec3 boneBegin = boneEnd;
|
glm::mat4 meshToJoint = glm::inverse(joint.bindTransform) * modelTransform;
|
||||||
|
ShapeVertices& points = shapeVertices[jointIndex];
|
||||||
glm::vec3 boneDirection;
|
|
||||||
float boneLength = 0.0f;
|
|
||||||
if (joint.parentIndex != -1) {
|
|
||||||
boneBegin = extractTranslation(inverseModelTransform * geometry.joints[joint.parentIndex].bindTransform);
|
|
||||||
boneDirection = boneEnd - boneBegin;
|
|
||||||
boneLength = glm::length(boneDirection);
|
|
||||||
if (boneLength > EPSILON) {
|
|
||||||
boneDirection /= boneLength;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
float radiusScale = extractUniformScale(joint.transform * firstFBXCluster.inverseBindMatrix);
|
|
||||||
|
|
||||||
// compute average vertex
|
|
||||||
glm::vec3 averageVertex(0.0f);
|
|
||||||
foreach (const glm::vec3& vertex, extracted.mesh.vertices) {
|
foreach (const glm::vec3& vertex, extracted.mesh.vertices) {
|
||||||
float proj = glm::dot(boneDirection, boneEnd - vertex);
|
const glm::mat4 vertexTransform = meshToJoint * glm::translate(vertex);
|
||||||
float radiusWeight = (proj < 0.0f || proj > boneLength) ? 0.5f : 1.0f;
|
points.push_back(extractTranslation(vertexTransform) * clusterScale);
|
||||||
jointShapeInfo.sumVertexWeights += radiusWeight;
|
|
||||||
jointShapeInfo.sumWeightedRadii += radiusWeight * radiusScale * glm::distance(vertex, boneEnd - boneDirection * proj);
|
|
||||||
++jointShapeInfo.numVertexWeights;
|
|
||||||
averageVertex += vertex;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// compute joint's radius
|
|
||||||
int numVertices = extracted.mesh.vertices.size();
|
|
||||||
jointShapeInfo.numVertices = numVertices;
|
|
||||||
if (numVertices > 0) {
|
|
||||||
// compute average radius
|
|
||||||
averageVertex /= (float)jointShapeInfo.numVertices;
|
|
||||||
float averageRadius = 0.0f;
|
|
||||||
foreach (const glm::vec3& vertex, extracted.mesh.vertices) {
|
|
||||||
averageRadius += glm::distance(vertex, averageVertex);
|
|
||||||
}
|
|
||||||
averageRadius *= radiusScale / (float)jointShapeInfo.numVertices;
|
|
||||||
|
|
||||||
// final radius is minimum of average and weighted
|
|
||||||
float weightedRadius = jointShapeInfo.sumWeightedRadii / jointShapeInfo.sumVertexWeights;
|
|
||||||
jointShapeInfo.averageRadius = glm::min(weightedRadius, averageRadius);
|
|
||||||
}
|
|
||||||
|
|
||||||
// clear sumVertexWeights (this flags it as a single-mesh joint for later)
|
|
||||||
jointShapeInfo.sumVertexWeights = 0.0f;
|
|
||||||
}
|
}
|
||||||
extracted.mesh.isEye = (maxJointIndex == geometry.leftEyeJointIndex || maxJointIndex == geometry.rightEyeJointIndex);
|
extracted.mesh.isEye = (maxJointIndex == geometry.leftEyeJointIndex || maxJointIndex == geometry.rightEyeJointIndex);
|
||||||
|
|
||||||
|
@ -2721,24 +2666,59 @@ FBXGeometry* extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping
|
||||||
glm::vec3 defaultCapsuleAxis(0.0f, 1.0f, 0.0f);
|
glm::vec3 defaultCapsuleAxis(0.0f, 1.0f, 0.0f);
|
||||||
for (int i = 0; i < geometry.joints.size(); ++i) {
|
for (int i = 0; i < geometry.joints.size(); ++i) {
|
||||||
FBXJoint& joint = geometry.joints[i];
|
FBXJoint& joint = geometry.joints[i];
|
||||||
JointShapeInfo& jointShapeInfo = jointShapeInfos[i];
|
|
||||||
|
|
||||||
if (joint.parentIndex == -1) {
|
// NOTE: points are in joint-frame
|
||||||
jointShapeInfo.boneBegin = glm::vec3(0.0f);
|
// compute average point
|
||||||
|
ShapeVertices& points = shapeVertices[i];
|
||||||
|
glm::vec3 avgPoint = glm::vec3(0.0f);
|
||||||
|
for (uint32_t j = 0; j < points.size(); ++j) {
|
||||||
|
avgPoint += points[j];
|
||||||
|
}
|
||||||
|
avgPoint /= (float)points.size();
|
||||||
|
|
||||||
|
// compute axis from begin to avgPoint
|
||||||
|
glm::vec3 begin(0.0f);
|
||||||
|
glm::vec3 end = avgPoint;
|
||||||
|
glm::vec3 axis = end - begin;
|
||||||
|
float axisLength = glm::length(axis);
|
||||||
|
if (axisLength > EPSILON) {
|
||||||
|
axis /= axisLength;
|
||||||
} else {
|
} else {
|
||||||
const FBXJoint& parentJoint = geometry.joints[joint.parentIndex];
|
axis = glm::vec3(0.0f);
|
||||||
glm::quat inverseRotation = glm::inverse(extractRotation(joint.transform));
|
|
||||||
jointShapeInfo.boneBegin = inverseRotation * (extractTranslation(parentJoint.transform) - extractTranslation(joint.transform));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (jointShapeInfo.sumVertexWeights > 0.0f) {
|
// measure average cylindrical radius
|
||||||
// mutiple meshes contributed to the bone radius and now that all
|
float avgRadius = 0.0f;
|
||||||
// contributing meshes are done we can finally compute the boneRadius
|
if (points.size() > 0) {
|
||||||
joint.boneRadius = jointShapeInfo.sumWeightedRadii / jointShapeInfo.sumVertexWeights;
|
float minProjection = FLT_MAX;
|
||||||
} else {
|
float maxProjection = -FLT_MIN;
|
||||||
// single-mesh joint
|
for (uint32_t j = 0; j < points.size(); ++j) {
|
||||||
joint.boneRadius = jointShapeInfo.averageRadius;
|
glm::vec3 offset = points[j] - avgPoint;
|
||||||
|
float projection = glm::dot(offset, axis);
|
||||||
|
maxProjection = glm::max(maxProjection, projection);
|
||||||
|
minProjection = glm::min(minProjection, projection);
|
||||||
|
avgRadius += glm::length(offset - projection * axis);
|
||||||
}
|
}
|
||||||
|
avgRadius /= (float)points.size();
|
||||||
|
|
||||||
|
// compute endpoints of capsule in joint-frame
|
||||||
|
glm::vec3 capsuleBegin = avgPoint;
|
||||||
|
glm::vec3 capsuleEnd = avgPoint;
|
||||||
|
if (maxProjection - minProjection < 2.0f * avgRadius) {
|
||||||
|
// the mesh-as-cylinder approximation is too short to collide as a capsule
|
||||||
|
// so we'll collapse it to a sphere (although that isn't a very good approximation)
|
||||||
|
capsuleBegin = avgPoint + 0.5f * (maxProjection + minProjection) * axis;
|
||||||
|
capsuleEnd = capsuleBegin;
|
||||||
|
} else {
|
||||||
|
capsuleBegin = avgPoint + (minProjection + avgRadius) * axis;
|
||||||
|
capsuleEnd = avgPoint + (maxProjection - avgRadius) * axis;
|
||||||
|
}
|
||||||
|
|
||||||
|
// save points for later
|
||||||
|
joint.shapeInfo.points.push_back(capsuleBegin);
|
||||||
|
joint.shapeInfo.points.push_back(capsuleEnd);
|
||||||
|
}
|
||||||
|
joint.shapeInfo.radius = avgRadius;
|
||||||
}
|
}
|
||||||
geometry.palmDirection = parseVec3(mapping.value("palmDirection", "0, -1, 0").toString());
|
geometry.palmDirection = parseVec3(mapping.value("palmDirection", "0, -1, 0").toString());
|
||||||
|
|
||||||
|
|
|
@ -55,15 +55,21 @@ public:
|
||||||
QVector<glm::vec3> normals;
|
QVector<glm::vec3> normals;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct FBXJointShapeInfo {
|
||||||
|
// same units and frame as FBXJoint.translation
|
||||||
|
QVector<glm::vec3> points;
|
||||||
|
float radius;
|
||||||
|
};
|
||||||
|
|
||||||
/// A single joint (transformation node) extracted from an FBX document.
|
/// A single joint (transformation node) extracted from an FBX document.
|
||||||
class FBXJoint {
|
class FBXJoint {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
bool isFree;
|
FBXJointShapeInfo shapeInfo;
|
||||||
QVector<int> freeLineage;
|
QVector<int> freeLineage;
|
||||||
|
bool isFree;
|
||||||
int parentIndex;
|
int parentIndex;
|
||||||
float distanceToParent;
|
float distanceToParent;
|
||||||
float boneRadius;
|
|
||||||
|
|
||||||
// http://download.autodesk.com/us/fbx/20112/FBX_SDK_HELP/SDKRef/a00209.html
|
// http://download.autodesk.com/us/fbx/20112/FBX_SDK_HELP/SDKRef/a00209.html
|
||||||
|
|
||||||
|
|
|
@ -441,7 +441,6 @@ FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping,
|
||||||
geometry.joints[0].isFree = false;
|
geometry.joints[0].isFree = false;
|
||||||
geometry.joints[0].parentIndex = -1;
|
geometry.joints[0].parentIndex = -1;
|
||||||
geometry.joints[0].distanceToParent = 0;
|
geometry.joints[0].distanceToParent = 0;
|
||||||
geometry.joints[0].boneRadius = 0;
|
|
||||||
geometry.joints[0].translation = glm::vec3(0, 0, 0);
|
geometry.joints[0].translation = glm::vec3(0, 0, 0);
|
||||||
geometry.joints[0].rotationMin = glm::vec3(0, 0, 0);
|
geometry.joints[0].rotationMin = glm::vec3(0, 0, 0);
|
||||||
geometry.joints[0].rotationMax = glm::vec3(0, 0, 0);
|
geometry.joints[0].rotationMax = glm::vec3(0, 0, 0);
|
||||||
|
@ -617,7 +616,6 @@ void fbxDebugDump(const FBXGeometry& fbxgeo) {
|
||||||
qCDebug(modelformat) << " freeLineage" << joint.freeLineage;
|
qCDebug(modelformat) << " freeLineage" << joint.freeLineage;
|
||||||
qCDebug(modelformat) << " parentIndex" << joint.parentIndex;
|
qCDebug(modelformat) << " parentIndex" << joint.parentIndex;
|
||||||
qCDebug(modelformat) << " distanceToParent" << joint.distanceToParent;
|
qCDebug(modelformat) << " distanceToParent" << joint.distanceToParent;
|
||||||
qCDebug(modelformat) << " boneRadius" << joint.boneRadius;
|
|
||||||
qCDebug(modelformat) << " translation" << joint.translation;
|
qCDebug(modelformat) << " translation" << joint.translation;
|
||||||
qCDebug(modelformat) << " preTransform" << joint.preTransform;
|
qCDebug(modelformat) << " preTransform" << joint.preTransform;
|
||||||
qCDebug(modelformat) << " preRotation" << joint.preRotation;
|
qCDebug(modelformat) << " preRotation" << joint.preRotation;
|
||||||
|
|
|
@ -57,7 +57,8 @@ AssetRequest* AssetClient::createRequest(const QString& hash, const QString& ext
|
||||||
SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer);
|
SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer);
|
||||||
|
|
||||||
if (!assetServer) {
|
if (!assetServer) {
|
||||||
qDebug().nospace() << "Could not request " << hash << "." << extension << " since you are not currently connected to an asset-server.";
|
qDebug().nospace() << "Could not request " << hash << "." << extension
|
||||||
|
<< " since you are not currently connected to an asset-server.";
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,10 +97,13 @@ bool AssetClient::getAsset(const QString& hash, const QString& extension, DataOf
|
||||||
SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer);
|
SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer);
|
||||||
|
|
||||||
if (assetServer) {
|
if (assetServer) {
|
||||||
auto packet = NLPacket::create(PacketType::AssetGet);
|
|
||||||
|
|
||||||
auto messageID = ++_currentID;
|
auto messageID = ++_currentID;
|
||||||
|
|
||||||
|
auto payloadSize = sizeof(messageID) + SHA256_HASH_LENGTH + sizeof(uint8_t) + extension.length()
|
||||||
|
+ sizeof(start) + sizeof(end);
|
||||||
|
auto packet = NLPacket::create(PacketType::AssetGet, payloadSize, true);
|
||||||
|
|
||||||
qDebug() << "Requesting data from" << start << "to" << end << "of" << hash << "from asset-server.";
|
qDebug() << "Requesting data from" << start << "to" << end << "of" << hash << "from asset-server.";
|
||||||
|
|
||||||
packet->writePrimitive(messageID);
|
packet->writePrimitive(messageID);
|
||||||
|
@ -127,9 +131,11 @@ bool AssetClient::getAssetInfo(const QString& hash, const QString& extension, Ge
|
||||||
SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer);
|
SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer);
|
||||||
|
|
||||||
if (assetServer) {
|
if (assetServer) {
|
||||||
auto packet = NLPacket::create(PacketType::AssetGetInfo);
|
|
||||||
|
|
||||||
auto messageID = ++_currentID;
|
auto messageID = ++_currentID;
|
||||||
|
|
||||||
|
auto payloadSize = sizeof(messageID) + SHA256_HASH_LENGTH + sizeof(uint8_t) + extension.length();
|
||||||
|
auto packet = NLPacket::create(PacketType::AssetGetInfo, payloadSize, true);
|
||||||
|
|
||||||
packet->writePrimitive(messageID);
|
packet->writePrimitive(messageID);
|
||||||
packet->write(QByteArray::fromHex(hash.toLatin1()));
|
packet->write(QByteArray::fromHex(hash.toLatin1()));
|
||||||
packet->writePrimitive(uint8_t(extension.length()));
|
packet->writePrimitive(uint8_t(extension.length()));
|
||||||
|
@ -207,9 +213,6 @@ bool AssetClient::uploadAsset(const QByteArray& data, const QString& extension,
|
||||||
packetList->writePrimitive(static_cast<uint8_t>(extension.length()));
|
packetList->writePrimitive(static_cast<uint8_t>(extension.length()));
|
||||||
packetList->write(extension.toLatin1().constData(), extension.length());
|
packetList->write(extension.toLatin1().constData(), extension.length());
|
||||||
|
|
||||||
qDebug() << "Extension length: " << extension.length();
|
|
||||||
qDebug() << "Extension: " << extension;
|
|
||||||
|
|
||||||
uint64_t size = data.length();
|
uint64_t size = data.length();
|
||||||
packetList->writePrimitive(size);
|
packetList->writePrimitive(size);
|
||||||
packetList->write(data.constData(), size);
|
packetList->write(data.constData(), size);
|
||||||
|
|
|
@ -41,6 +41,8 @@ void AssetUpload::start() {
|
||||||
// ask the AssetClient to upload the asset and emit the proper signals from the passed callback
|
// ask the AssetClient to upload the asset and emit the proper signals from the passed callback
|
||||||
auto assetClient = DependencyManager::get<AssetClient>();
|
auto assetClient = DependencyManager::get<AssetClient>();
|
||||||
|
|
||||||
|
qDebug() << "Attempting to upload" << _filename << "to asset-server.";
|
||||||
|
|
||||||
assetClient->uploadAsset(data, _extension, [this](AssetServerError error, const QString& hash){
|
assetClient->uploadAsset(data, _extension, [this](AssetServerError error, const QString& hash){
|
||||||
switch (error) {
|
switch (error) {
|
||||||
case AssetServerError::NoError:
|
case AssetServerError::NoError:
|
||||||
|
|
|
@ -242,8 +242,9 @@ bool LimitedNodeList::packetSourceAndHashMatch(const udt::Packet& packet) {
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
static const QString UNKNOWN_REGEX = "Packet of type \\d+ \\([\\sa-zA-Z:]+\\) received from unknown node with UUID";
|
||||||
static QString repeatedMessage
|
static QString repeatedMessage
|
||||||
= LogHandler::getInstance().addRepeatedMessageRegex("Packet of type \\d+ \\([\\sa-zA-Z]+\\) received from unknown node with UUID");
|
= LogHandler::getInstance().addRepeatedMessageRegex(UNKNOWN_REGEX);
|
||||||
|
|
||||||
qCDebug(networking) << "Packet of type" << headerType
|
qCDebug(networking) << "Packet of type" << headerType
|
||||||
<< "received from unknown node with UUID" << qPrintable(uuidStringWithoutCurlyBraces(sourceID));
|
<< "received from unknown node with UUID" << qPrintable(uuidStringWithoutCurlyBraces(sourceID));
|
||||||
|
|
|
@ -25,6 +25,13 @@ ResourceRequest* ResourceManager::createResourceRequest(QObject* parent, const Q
|
||||||
return new HTTPResourceRequest(parent, url);
|
return new HTTPResourceRequest(parent, url);
|
||||||
} else if (scheme == URL_SCHEME_ATP) {
|
} else if (scheme == URL_SCHEME_ATP) {
|
||||||
return new AssetResourceRequest(parent, url);
|
return new AssetResourceRequest(parent, url);
|
||||||
|
} else {
|
||||||
|
// check the degenerative file case: on windows we can often have urls of the form c:/filename
|
||||||
|
// this checks for and works around that case.
|
||||||
|
QUrl urlWithFileScheme { URL_SCHEME_FILE + ":///" + url.toString() };
|
||||||
|
if (!urlWithFileScheme.toLocalFile().isEmpty()) {
|
||||||
|
return new FileResourceRequest(parent, urlWithFileScheme);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
qDebug() << "Unknown scheme (" << scheme << ") for URL: " << url.url();
|
qDebug() << "Unknown scheme (" << scheme << ") for URL: " << url.url();
|
||||||
|
|
|
@ -409,8 +409,7 @@ void DynamicCharacterController::postSimulation() {
|
||||||
glm::quat rotation = bulletToGLM(avatarTransform.getRotation());
|
glm::quat rotation = bulletToGLM(avatarTransform.getRotation());
|
||||||
glm::vec3 position = bulletToGLM(avatarTransform.getOrigin());
|
glm::vec3 position = bulletToGLM(avatarTransform.getOrigin());
|
||||||
|
|
||||||
_avatarData->setOrientation(rotation);
|
_avatarData->nextAttitude(position - rotation * _shapeLocalOffset, rotation);
|
||||||
_avatarData->setPosition(position - rotation * _shapeLocalOffset);
|
|
||||||
_avatarData->setVelocity(bulletToGLM(_rigidBody->getLinearVelocity()));
|
_avatarData->setVelocity(bulletToGLM(_rigidBody->getLinearVelocity()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1291,7 +1291,9 @@ void Model::simulateInternal(float deltaTime) {
|
||||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||||
glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset;
|
glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset;
|
||||||
updateRig(deltaTime, parentTransform);
|
updateRig(deltaTime, parentTransform);
|
||||||
|
}
|
||||||
|
void Model::updateClusterMatrices() {
|
||||||
|
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||||
glm::mat4 zeroScale(glm::vec4(0.0f, 0.0f, 0.0f, 0.0f),
|
glm::mat4 zeroScale(glm::vec4(0.0f, 0.0f, 0.0f, 0.0f),
|
||||||
glm::vec4(0.0f, 0.0f, 0.0f, 0.0f),
|
glm::vec4(0.0f, 0.0f, 0.0f, 0.0f),
|
||||||
glm::vec4(0.0f, 0.0f, 0.0f, 0.0f),
|
glm::vec4(0.0f, 0.0f, 0.0f, 0.0f),
|
||||||
|
@ -1319,7 +1321,7 @@ void Model::simulateInternal(float deltaTime) {
|
||||||
} else {
|
} else {
|
||||||
for (int j = 0; j < mesh.clusters.size(); j++) {
|
for (int j = 0; j < mesh.clusters.size(); j++) {
|
||||||
const FBXCluster& cluster = mesh.clusters.at(j);
|
const FBXCluster& cluster = mesh.clusters.at(j);
|
||||||
auto jointMatrix = _rig->getJointVisibleTransform(cluster.jointIndex);
|
auto jointMatrix = _rig->getJointVisibleTransform(cluster.jointIndex); // differs from above only in using get...VisibleTransform
|
||||||
state.clusterMatrices[j] = modelToWorld * jointMatrix * cluster.inverseBindMatrix;
|
state.clusterMatrices[j] = modelToWorld * jointMatrix * cluster.inverseBindMatrix;
|
||||||
|
|
||||||
// as an optimization, don't build cautrizedClusterMatrices if the boneSet is empty.
|
// as an optimization, don't build cautrizedClusterMatrices if the boneSet is empty.
|
||||||
|
@ -1434,7 +1436,6 @@ AABox Model::getPartBounds(int meshIndex, int partIndex) {
|
||||||
return calculateScaledOffsetAABox(_geometry->getFBXGeometry().meshExtents);
|
return calculateScaledOffsetAABox(_geometry->getFBXGeometry().meshExtents);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_geometry->getFBXGeometry().meshes.size() > meshIndex) {
|
if (_geometry->getFBXGeometry().meshes.size() > meshIndex) {
|
||||||
|
|
||||||
// FIX ME! - This is currently a hack because for some mesh parts our efforts to calculate the bounding
|
// FIX ME! - This is currently a hack because for some mesh parts our efforts to calculate the bounding
|
||||||
|
@ -1489,6 +1490,8 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateClusterMatrices();
|
||||||
|
|
||||||
const NetworkMesh& networkMesh = *(networkMeshes.at(meshIndex).get());
|
const NetworkMesh& networkMesh = *(networkMeshes.at(meshIndex).get());
|
||||||
const FBXMesh& mesh = geometry.meshes.at(meshIndex);
|
const FBXMesh& mesh = geometry.meshes.at(meshIndex);
|
||||||
const MeshState& state = _meshStates.at(meshIndex);
|
const MeshState& state = _meshStates.at(meshIndex);
|
||||||
|
|
|
@ -111,6 +111,7 @@ public:
|
||||||
bool getSnapModelToRegistrationPoint() { return _snapModelToRegistrationPoint; }
|
bool getSnapModelToRegistrationPoint() { return _snapModelToRegistrationPoint; }
|
||||||
|
|
||||||
virtual void simulate(float deltaTime, bool fullUpdate = true);
|
virtual void simulate(float deltaTime, bool fullUpdate = true);
|
||||||
|
void updateClusterMatrices();
|
||||||
|
|
||||||
/// Returns a reference to the shared geometry.
|
/// Returns a reference to the shared geometry.
|
||||||
const QSharedPointer<NetworkGeometry>& getGeometry() const { return _geometry; }
|
const QSharedPointer<NetworkGeometry>& getGeometry() const { return _geometry; }
|
||||||
|
|
|
@ -860,7 +860,14 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac
|
||||||
}
|
}
|
||||||
QList<QUrl> urls;
|
QList<QUrl> urls;
|
||||||
for (QString file : includeFiles) {
|
for (QString file : includeFiles) {
|
||||||
urls.append(resolvePath(file));
|
QUrl thisURL { resolvePath(file) };
|
||||||
|
if (!_includedURLs.contains(thisURL)) {
|
||||||
|
urls.append(thisURL);
|
||||||
|
_includedURLs << thisURL;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
qCDebug(scriptengine) << "Script.include() ignoring previously included url:" << thisURL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BatchLoader* loader = new BatchLoader(urls);
|
BatchLoader* loader = new BatchLoader(urls);
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
#include <QtCore/QObject>
|
#include <QtCore/QObject>
|
||||||
#include <QtCore/QUrl>
|
#include <QtCore/QUrl>
|
||||||
|
#include <QtCore/QSet>
|
||||||
#include <QtCore/QWaitCondition>
|
#include <QtCore/QWaitCondition>
|
||||||
#include <QtScript/QScriptEngine>
|
#include <QtScript/QScriptEngine>
|
||||||
|
|
||||||
|
@ -152,6 +153,7 @@ protected:
|
||||||
Sound* _avatarSound;
|
Sound* _avatarSound;
|
||||||
int _numAvatarSoundSentBytes;
|
int _numAvatarSoundSentBytes;
|
||||||
bool _isAgent = false;
|
bool _isAgent = false;
|
||||||
|
QSet<QUrl> _includedURLs;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void stopAllTimers();
|
void stopAllTimers();
|
||||||
|
|
|
@ -24,7 +24,7 @@ class GenericQueueThread : public GenericThread {
|
||||||
public:
|
public:
|
||||||
using Queue = QQueue<T>;
|
using Queue = QQueue<T>;
|
||||||
GenericQueueThread(QObject* parent = nullptr)
|
GenericQueueThread(QObject* parent = nullptr)
|
||||||
: GenericThread(parent) {}
|
: GenericThread() {}
|
||||||
|
|
||||||
virtual ~GenericQueueThread() {}
|
virtual ~GenericQueueThread() {}
|
||||||
|
|
||||||
|
|
|
@ -14,8 +14,8 @@
|
||||||
#include "GenericThread.h"
|
#include "GenericThread.h"
|
||||||
|
|
||||||
|
|
||||||
GenericThread::GenericThread(QObject* parent) :
|
GenericThread::GenericThread() :
|
||||||
QObject(parent),
|
QObject(),
|
||||||
_stopThread(false),
|
_stopThread(false),
|
||||||
_isThreaded(false) // assume non-threaded, must call initialize()
|
_isThreaded(false) // assume non-threaded, must call initialize()
|
||||||
{
|
{
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
class GenericThread : public QObject {
|
class GenericThread : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
GenericThread(QObject* parent = nullptr);
|
GenericThread();
|
||||||
virtual ~GenericThread();
|
virtual ~GenericThread();
|
||||||
|
|
||||||
/// Call to start the thread.
|
/// Call to start the thread.
|
||||||
|
|
|
@ -127,21 +127,20 @@ void AnimTests::testClipEvaulateWithVars() {
|
||||||
|
|
||||||
void AnimTests::testLoader() {
|
void AnimTests::testLoader() {
|
||||||
auto url = QUrl("https://gist.githubusercontent.com/hyperlogic/857129fe04567cbe670f/raw/8ba57a8f0a76f88b39a11f77f8d9df04af9cec95/test.json");
|
auto url = QUrl("https://gist.githubusercontent.com/hyperlogic/857129fe04567cbe670f/raw/8ba57a8f0a76f88b39a11f77f8d9df04af9cec95/test.json");
|
||||||
|
// NOTE: This will warn about missing "test01.fbx", "test02.fbx", etc. if the resource loading code doesn't handle relative pathnames!
|
||||||
|
// However, the test will proceed.
|
||||||
AnimNodeLoader loader(url);
|
AnimNodeLoader loader(url);
|
||||||
|
|
||||||
const int timeout = 1000;
|
const int timeout = 1000;
|
||||||
QEventLoop loop;
|
QEventLoop loop;
|
||||||
QTimer timer;
|
|
||||||
timer.setInterval(timeout);
|
|
||||||
timer.setSingleShot(true);
|
|
||||||
|
|
||||||
AnimNode::Pointer node = nullptr;
|
AnimNode::Pointer node = nullptr;
|
||||||
connect(&loader, &AnimNodeLoader::success, [&](AnimNode::Pointer nodeIn) { node = nodeIn; });
|
connect(&loader, &AnimNodeLoader::success, [&](AnimNode::Pointer nodeIn) { node = nodeIn; });
|
||||||
|
|
||||||
loop.connect(&loader, SIGNAL(success(AnimNode::Pointer)), SLOT(quit()));
|
loop.connect(&loader, SIGNAL(success(AnimNode::Pointer)), SLOT(quit()));
|
||||||
loop.connect(&loader, SIGNAL(error(int, QString)), SLOT(quit()));
|
loop.connect(&loader, SIGNAL(error(int, QString)), SLOT(quit()));
|
||||||
loop.connect(&timer, SIGNAL(timeout()), SLOT(quit()));
|
QTimer::singleShot(timeout, &loop, SLOT(quit()));
|
||||||
timer.start();
|
|
||||||
loop.exec();
|
loop.exec();
|
||||||
|
|
||||||
QVERIFY((bool)node);
|
QVERIFY((bool)node);
|
||||||
|
@ -184,42 +183,58 @@ void AnimTests::testLoader() {
|
||||||
|
|
||||||
void AnimTests::testVariant() {
|
void AnimTests::testVariant() {
|
||||||
auto defaultVar = AnimVariant();
|
auto defaultVar = AnimVariant();
|
||||||
auto boolVar = AnimVariant(true);
|
auto boolVarTrue = AnimVariant(true);
|
||||||
auto intVar = AnimVariant(1);
|
auto boolVarFalse = AnimVariant(false);
|
||||||
auto floatVar = AnimVariant(1.0f);
|
auto intVarZero = AnimVariant(0);
|
||||||
auto vec3Var = AnimVariant(glm::vec3(1.0f, 2.0f, 3.0f));
|
auto intVarOne = AnimVariant(1);
|
||||||
auto quatVar = AnimVariant(glm::quat(1.0f, 2.0f, 3.0f, 4.0f));
|
auto intVarNegative = AnimVariant(-1);
|
||||||
|
auto floatVarZero = AnimVariant(0.0f);
|
||||||
|
auto floatVarOne = AnimVariant(1.0f);
|
||||||
|
auto floatVarNegative = AnimVariant(-1.0f);
|
||||||
|
auto vec3Var = AnimVariant(glm::vec3(1.0f, -2.0f, 3.0f));
|
||||||
|
auto quatVar = AnimVariant(glm::quat(1.0f, 2.0f, -3.0f, 4.0f));
|
||||||
auto mat4Var = AnimVariant(glm::mat4(glm::vec4(1.0f, 2.0f, 3.0f, 4.0f),
|
auto mat4Var = AnimVariant(glm::mat4(glm::vec4(1.0f, 2.0f, 3.0f, 4.0f),
|
||||||
glm::vec4(5.0f, 6.0f, 7.0f, 8.0f),
|
glm::vec4(5.0f, 6.0f, -7.0f, 8.0f),
|
||||||
glm::vec4(9.0f, 10.0f, 11.0f, 12.0f),
|
glm::vec4(9.0f, 10.0f, 11.0f, 12.0f),
|
||||||
glm::vec4(13.0f, 14.0f, 15.0f, 16.0f)));
|
glm::vec4(13.0f, 14.0f, 15.0f, 16.0f)));
|
||||||
QVERIFY(defaultVar.isBool());
|
QVERIFY(defaultVar.isBool());
|
||||||
QVERIFY(defaultVar.getBool() == false);
|
QVERIFY(defaultVar.getBool() == false);
|
||||||
|
|
||||||
QVERIFY(boolVar.isBool());
|
QVERIFY(boolVarTrue.isBool());
|
||||||
QVERIFY(boolVar.getBool() == true);
|
QVERIFY(boolVarTrue.getBool() == true);
|
||||||
|
QVERIFY(boolVarFalse.isBool());
|
||||||
|
QVERIFY(boolVarFalse.getBool() == false);
|
||||||
|
|
||||||
QVERIFY(intVar.isInt());
|
QVERIFY(intVarZero.isInt());
|
||||||
QVERIFY(intVar.getInt() == 1);
|
QVERIFY(intVarZero.getInt() == 0);
|
||||||
|
QVERIFY(intVarOne.isInt());
|
||||||
|
QVERIFY(intVarOne.getInt() == 1);
|
||||||
|
QVERIFY(intVarNegative.isInt());
|
||||||
|
QVERIFY(intVarNegative.getInt() == -1);
|
||||||
|
|
||||||
QVERIFY(floatVar.isFloat());
|
QVERIFY(floatVarZero.isFloat());
|
||||||
QVERIFY(floatVar.getFloat() == 1.0f);
|
QVERIFY(floatVarZero.getFloat() == 0.0f);
|
||||||
|
QVERIFY(floatVarOne.isFloat());
|
||||||
|
QVERIFY(floatVarOne.getFloat() == 1.0f);
|
||||||
|
QVERIFY(floatVarNegative.isFloat());
|
||||||
|
QVERIFY(floatVarNegative.getFloat() == -1.0f);
|
||||||
|
|
||||||
QVERIFY(vec3Var.isVec3());
|
QVERIFY(vec3Var.isVec3());
|
||||||
auto v = vec3Var.getVec3();
|
auto v = vec3Var.getVec3();
|
||||||
QVERIFY(v.x == 1.0f);
|
QVERIFY(v.x == 1.0f);
|
||||||
QVERIFY(v.y == 2.0f);
|
QVERIFY(v.y == -2.0f);
|
||||||
QVERIFY(v.z == 3.0f);
|
QVERIFY(v.z == 3.0f);
|
||||||
|
|
||||||
QVERIFY(quatVar.isQuat());
|
QVERIFY(quatVar.isQuat());
|
||||||
auto q = quatVar.getQuat();
|
auto q = quatVar.getQuat();
|
||||||
QVERIFY(q.w == 1.0f);
|
QVERIFY(q.w == 1.0f);
|
||||||
QVERIFY(q.x == 2.0f);
|
QVERIFY(q.x == 2.0f);
|
||||||
QVERIFY(q.y == 3.0f);
|
QVERIFY(q.y == -3.0f);
|
||||||
QVERIFY(q.z == 4.0f);
|
QVERIFY(q.z == 4.0f);
|
||||||
|
|
||||||
QVERIFY(mat4Var.isMat4());
|
QVERIFY(mat4Var.isMat4());
|
||||||
auto m = mat4Var.getMat4();
|
auto m = mat4Var.getMat4();
|
||||||
QVERIFY(m[0].x == 1.0f);
|
QVERIFY(m[0].x == 1.0f);
|
||||||
|
QVERIFY(m[1].z == -7.0f);
|
||||||
QVERIFY(m[3].w == 16.0f);
|
QVERIFY(m[3].w == 16.0f);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue