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

Conflicts:
	libraries/render-utils/src/Model.cpp
	libraries/render-utils/src/Model.h
This commit is contained in:
Brad Hefta-Gaub 2015-09-22 20:54:25 -07:00
commit 8d8035a418
9 changed files with 209 additions and 376 deletions

View file

@ -1,54 +0,0 @@
// bubble.js
// part of bubblewand
//
// Created by James B. Pollack @imgntn -- 09/03/2015
// Copyright 2015 High Fidelity, Inc.
//
// example of a nested entity. it doesn't do much now besides delete itself if it collides with something (bubbles are fragile! it would be cool if it sometimes merged with other bubbbles it hit)
// todo: play bubble sounds from the bubble itself instead of the wand.
// blocker: needs some sound fixes and a way to find its own position before unload for spatialization
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
(function() {
// Script.include("https://raw.githubusercontent.com/highfidelity/hifi/master/examples/utilities.js");
// Script.include("https://raw.githubusercontent.com/highfidelity/hifi/master/examples/libraries/utils.js");
//var popSound;
this.preload = function(entityID) {
// print('bubble preload')
this.entityID = entityID;
// popSound = SoundCache.getSound("http://hifi-public.s3.amazonaws.com/james/bubblewand/sounds/pop.wav");
}
this.collisionWithEntity = function(myID, otherID, collision) {
//if(Entites.getEntityProperties(otherID).userData.objectType==='') { merge bubbles?}
// Entities.deleteEntity(myID);
// this.burstBubbleSound(collision.contactPoint)
};
this.unload = function(entityID) {
// this.properties = Entities.getEntityProperties(entityID);
//var location = this.properties.position;
//this.burstBubbleSound();
};
this.burstBubbleSound = function(location) {
// var audioOptions = {
// volume: 0.5,
// position: location
// }
//Audio.playSound(popSound, audioOptions);
}
})

View file

@ -1,42 +1,43 @@
// createWand.js
// part of bubblewand
//
// Script Type: Entity Spawner
// Created by James B. Pollack @imgntn -- 09/03/2015
// Copyright 2015 High Fidelity, Inc.
//
// Loads a wand model and attaches the bubble wand behavior.
//
// Loads a wand model and attaches the bubble wand behavior.
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
/*global MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt */
Script.include("../../utilities.js");
Script.include("../../libraries/utils.js");
var WAND_MODEL = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/models/wand/wand.fbx';
var WAND_COLLISION_SHAPE = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/models/wand/collisionHull.obj';
var WAND_SCRIPT_URL = Script.resolvePath("wand.js");
Script.include("https://raw.githubusercontent.com/highfidelity/hifi/master/examples/utilities.js");
Script.include("https://raw.githubusercontent.com/highfidelity/hifi/master/examples/libraries/utils.js");
//create the wand in front of the avatar
var wandModel = "http://hifi-public.s3.amazonaws.com/james/bubblewand/models/wand/wand.fbx?" + randInt(0, 10000);
var scriptURL = "http://hifi-public.s3.amazonaws.com/james/bubblewand/scripts/wand.js?" + randInt(1, 100500)
var center = Vec3.sum(Vec3.sum(MyAvatar.position, {x: 0, y: 0.5, z: 0}), Vec3.multiply(0.5, Quat.getFront(Camera.getOrientation())));
//create the wand in front of the avatar
var center = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(Camera.getOrientation())));
var wand = Entities.addEntity({
type: "Model",
modelURL: wandModel,
position: center,
dimensions: {
x: 0.1,
y: 1,
z: 0.1
},
//must be enabled to be grabbable in the physics engine
collisionsWillMove: true,
shapeType: 'box',
script: scriptURL
});
function cleanup() {
Entities.deleteEntity(wand);
}
Script.scriptEnding.connect(cleanup);
name: 'Bubble Wand',
type: "Model",
modelURL: WAND_MODEL,
position: center,
gravity: {
x: 0,
y: 0,
z: 0,
},
dimensions: {
x: 0.05,
y: 0.25,
z: 0.05
},
//must be enabled to be grabbable in the physics engine
collisionsWillMove: true,
compoundShapeURL: WAND_COLLISION_SHAPE,
script: WAND_SCRIPT_URL
});

View file

@ -1,317 +1,203 @@
// wand.js
// part of bubblewand
//
// Script Type: Entity Script
// Created by James B. Pollack @imgntn -- 09/03/2015
// Copyright 2015 High Fidelity, Inc.
//
// Makes bubbles when you wave the object around, or hold it near your mouth and make noise into the microphone.
// Makes bubbles when you wave the object around.
//
// For the example, it's attached to a wand -- but you can attach it to whatever entity you want. I dream of BubbleBees :) bzzzz...pop!
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
function convertRange(value, r1, r2) {
return (value - r1[0]) * (r2[1] - r2[0]) / (r1[1] - r1[0]) + r2[0];
}
/*global MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt */
(function() {
Script.include("https://raw.githubusercontent.com/highfidelity/hifi/master/examples/utilities.js");
Script.include("https://raw.githubusercontent.com/highfidelity/hifi/master/examples/libraries/utils.js");
(function () {
var bubbleModel = "http://hifi-public.s3.amazonaws.com/james/bubblewand/models/bubble/bubble.fbx";
var bubbleScript = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/scripts/bubble.js?' + randInt(1, 10000);
var popSound = SoundCache.getSound("http://hifi-public.s3.amazonaws.com/james/bubblewand/sounds/pop.wav");
Script.include("../../utilities.js");
Script.include("../../libraries/utils.js");
var TARGET_SIZE = 0.4;
var TARGET_COLOR = {
red: 128,
green: 128,
blue: 128
};
var TARGET_COLOR_HIT = {
red: 0,
green: 255,
blue: 0
var BUBBLE_MODEL = "http://hifi-public.s3.amazonaws.com/james/bubblewand/models/bubble/bubble.fbx";
var BUBBLE_INITIAL_DIMENSIONS = {
x: 0.01,
y: 0.01,
z: 0.01
};
var HAND_SIZE = 0.25;
var leftCubePosition = MyAvatar.getLeftPalmPosition();
var rightCubePosition = MyAvatar.getRightPalmPosition();
var BUBBLE_LIFETIME_MIN = 3;
var BUBBLE_LIFETIME_MAX = 8;
var BUBBLE_SIZE_MIN = 0.02;
var BUBBLE_SIZE_MAX = 0.1;
var BUBBLE_LINEAR_DAMPING = 0.4;
var BUBBLE_GRAVITY_MIN = 0.1;
var BUBBLE_GRAVITY_MAX = 0.3;
var GROWTH_FACTOR = 0.005;
var SHRINK_FACTOR = 0.001;
var SHRINK_LOWER_LIMIT = 0.02;
var WAND_TIP_OFFSET = 0.095;
var VELOCITY_THRESHOLD = 0.5;
var leftHand = Overlays.addOverlay("cube", {
position: leftCubePosition,
size: HAND_SIZE,
color: {
red: 0,
green: 0,
blue: 255
},
alpha: 1,
solid: false
});
//this helps us get the time passed since the last function call, for use in velocity calculations
function interval() {
var lastTime = new Date().getTime() / 1000;
var rightHand = Overlays.addOverlay("cube", {
position: rightCubePosition,
size: HAND_SIZE,
color: {
red: 255,
green: 0,
blue: 0
},
alpha: 1,
solid: false
});
var gustZoneOverlay = Overlays.addOverlay("cube", {
position: getGustDetectorPosition(),
size: TARGET_SIZE,
color: TARGET_COLOR,
alpha: 1,
solid: false
});
function getGustDetectorPosition() {
//put the zone in front of your avatar's face
var DISTANCE_IN_FRONT = 0.2;
var DISTANCE_UP = 0.5;
var DISTANCE_TO_SIDE = 0.0;
var up = Quat.getUp(MyAvatar.orientation);
var front = Quat.getFront(MyAvatar.orientation);
var right = Quat.getRight(MyAvatar.orientation);
var upOffset = Vec3.multiply(up, DISTANCE_UP);
var rightOffset = Vec3.multiply(right, DISTANCE_TO_SIDE);
var frontOffset = Vec3.multiply(front, DISTANCE_IN_FRONT);
var offset = Vec3.sum(Vec3.sum(rightOffset, frontOffset), upOffset);
var position = Vec3.sum(MyAvatar.position, offset);
return position;
return function getInterval() {
var newTime = new Date().getTime() / 1000;
var delta = newTime - lastTime;
lastTime = newTime;
return delta;
};
}
var checkInterval = interval();
var BUBBLE_GRAVITY = {
x: 0,
y: -0.05,
z: 0
function BubbleWand() {
return;
}
var wandEntity = this;
this.preload = function(entityID) {
// print('PRELOAD')
this.entityID = entityID;
this.properties = Entities.getEntityProperties(this.entityID);
}
this.unload = function(entityID) {
Overlays.deleteOverlay(leftHand);
Overlays.deleteOverlay(rightHand);
Overlays.deleteOverlay(gustZoneOverlay)
Entities.editEntity(entityID, {
name: ""
});
Script.update.disconnect(BubbleWand.update);
Entities.deleteEntity(BubbleWand.currentBubble);
while (BubbleWand.bubbles.length > 0) {
Entities.deleteEntity(BubbleWand.bubbles.pop());
}
};
var BubbleWand = {
bubbles: [],
BubbleWand.prototype = {
timePassed: null,
currentBubble: null,
update: function() {
BubbleWand.internalUpdate();
preload: function (entityID) {
this.entityID = entityID;
},
internalUpdate: function() {
var _t = this;
//get the current position of the wand
var properties = Entities.getEntityProperties(wandEntity.entityID);
getWandTipPosition: function (properties) {
//the tip of the wand is going to be in a different place than the center, so we move in space relative to the model to find that position
var upVector = Quat.getUp(properties.rotation);
var upOffset = Vec3.multiply(upVector, WAND_TIP_OFFSET);
var wandTipPosition = Vec3.sum(properties.position, upOffset);
return wandTipPosition;
},
addCollisionsToBubbleAfterCreation: function (bubble) {
//if the bubble collide immediately, we get weird effects. so we add collisions after release
Entities.editEntity(bubble, {
collisionsWillMove: true
});
},
randomizeBubbleGravity: function () {
//change up the gravity a little bit for variation in floating effects
var randomNumber = randFloat(BUBBLE_GRAVITY_MIN, BUBBLE_GRAVITY_MAX);
var gravity = {
x: 0,
y: -randomNumber,
z: 0
};
return gravity;
},
growBubbleWithWandVelocity: function (properties, deltaTime) {
//get the wand and tip position for calculations
var wandPosition = properties.position;
this.getWandTipPosition(properties);
// velocity = change in position / time
var velocity = Vec3.multiply(Vec3.subtract(wandPosition, this.lastPosition), 1 / deltaTime);
//debug overlays for mouth mode
var leftHandPos = MyAvatar.getLeftPalmPosition();
var rightHandPos = MyAvatar.getRightPalmPosition();
Overlays.editOverlay(leftHand, {
position: leftHandPos
});
Overlays.editOverlay(rightHand, {
position: rightHandPos
});
//if the wand is in the gust detector, activate mouth mode and change the overlay color
var hitTargetWithWand = findSphereSphereHit(wandPosition, HAND_SIZE / 2, getGustDetectorPosition(), TARGET_SIZE / 2)
var mouthMode;
if (hitTargetWithWand) {
Overlays.editOverlay(gustZoneOverlay, {
position: getGustDetectorPosition(),
color: TARGET_COLOR_HIT
})
mouthMode = true;
} else {
Overlays.editOverlay(gustZoneOverlay, {
position: getGustDetectorPosition(),
color: TARGET_COLOR
})
mouthMode = false;
}
var volumeLevel = MyAvatar.audioAverageLoudness;
//volume numbers are pretty large, so lets scale them down.
var convertedVolume = convertRange(volumeLevel, [0, 5000], [0, 10]);
// default is 'wave mode', where waving the object around grows the bubbles
var velocity = Vec3.subtract(wandPosition, BubbleWand.lastPosition)
var velocityStrength = Vec3.length(velocity);
//store the last position of the wand for velocity calculations
_t.lastPosition = wandPosition;
// velocity numbers are pretty small, so lets make them a bit bigger
var velocityStrength = Vec3.length(velocity) * 100;
if (velocityStrength > 10) {
velocityStrength = 10
}
this.lastPosition = wandPosition;
//actually grow the bubble
var dimensions = Entities.getEntityProperties(_t.currentBubble).dimensions;
if (velocityStrength > 1 || convertedVolume > 1) {
var dimensions = Entities.getEntityProperties(this.currentBubble, "dimensions").dimensions;
if (velocityStrength > VELOCITY_THRESHOLD) {
//add some variation in bubble sizes
var bubbleSize = randInt(1, 5);
bubbleSize = bubbleSize / 10;
var bubbleSize = randFloat(BUBBLE_SIZE_MIN, BUBBLE_SIZE_MAX);
//release the bubble if its dimensions are bigger than the bubble size
if (dimensions.x > bubbleSize) {
//bubbles pop after existing for a bit -- so set a random lifetime
var lifetime = randInt(3, 8);
var lifetime = randInt(BUBBLE_LIFETIME_MIN, BUBBLE_LIFETIME_MAX);
//sound is somewhat unstable at the moment so this is commented out. really audio should be played by the bubbles, but there's a blocker.
// Script.setTimeout(function() {
// _t.burstBubbleSound(_t.currentBubble)
// }, lifetime * 1000)
//todo: angular velocity without the controller -- forward velocity for mouth mode bubbles
// var angularVelocity = Controller.getSpatialControlRawAngularVelocity(hands.leftHand.tip);
Entities.editEntity(_t.currentBubble, {
velocity: Vec3.normalize(velocity),
// angularVelocity: Controller.getSpatialControlRawAngularVelocity(hands.leftHand.tip),
lifetime: lifetime
//edit the bubble properties at release
Entities.editEntity(this.currentBubble, {
velocity: velocity,
lifetime: lifetime,
gravity: this.randomizeBubbleGravity()
});
//wait to make the bubbles collidable, so that they dont hit each other and the wand
Script.setTimeout(this.addCollisionsToBubbleAfterCreation(this.currentBubble), lifetime / 2);
//release the bubble -- when we create a new bubble, it will carry on and this update loop will affect the new bubble
BubbleWand.spawnBubble();
return
this.createBubbleAtTipOfWand();
return;
} else {
if (mouthMode) {
dimensions.x += 0.015 * convertedVolume;
dimensions.y += 0.015 * convertedVolume;
dimensions.z += 0.015 * convertedVolume;
} else {
dimensions.x += 0.015 * velocityStrength;
dimensions.y += 0.015 * velocityStrength;
dimensions.z += 0.015 * velocityStrength;
}
//grow small bubbles
dimensions.x += GROWTH_FACTOR * velocityStrength;
dimensions.y += GROWTH_FACTOR * velocityStrength;
dimensions.z += GROWTH_FACTOR * velocityStrength;
}
} else {
if (dimensions.x >= 0.02) {
dimensions.x -= 0.001;
dimensions.y -= 0.001;
dimensions.z -= 0.001;
// if the wand is not moving, make the current bubble smaller
if (dimensions.x >= SHRINK_LOWER_LIMIT) {
dimensions.x -= SHRINK_FACTOR;
dimensions.y -= SHRINK_FACTOR;
dimensions.z -= SHRINK_FACTOR;
}
}
//update the bubble to stay with the wand tip
Entities.editEntity(_t.currentBubble, {
position: _t.wandTipPosition,
//adjust the bubble dimensions
Entities.editEntity(this.currentBubble, {
dimensions: dimensions
});
},
burstBubbleSound: function(bubble) {
//we want to play the sound at the same location and orientation as the bubble
var position = Entities.getEntityProperties(bubble).position;
var orientation = Entities.getEntityProperties(bubble).orientation;
createBubbleAtTipOfWand: function () {
//set the options for the audio injector
var audioOptions = {
volume: 0.5,
position: position,
orientation: orientation
}
//var audioInjector = Audio.playSound(popSound, audioOptions);
//remove this bubble from the array to keep things clean
var i = BubbleWand.bubbles.indexOf(bubble);
if (i != -1) {
BubbleWand.bubbles.splice(i, 1);
}
},
spawnBubble: function() {
var _t = this;
//create a new bubble at the tip of the wand
//the tip of the wand is going to be in a different place than the center, so we move in space relative to the model to find that position
var properties = Entities.getEntityProperties(wandEntity.entityID);
var properties = Entities.getEntityProperties(this.entityID, ["position", "rotation"]);
var wandPosition = properties.position;
var upVector = Quat.getUp(properties.rotation);
var frontVector = Quat.getFront(properties.rotation);
var upOffset = Vec3.multiply(upVector, 0.5);
var forwardOffset = Vec3.multiply(frontVector, 0.1);
var offsetVector = Vec3.sum(upOffset, forwardOffset);
var wandTipPosition = Vec3.sum(wandPosition, offsetVector);
_t.wandTipPosition = wandTipPosition;
//store the position of the tip on spawn for use in velocity calculations
_t.lastPosition = wandTipPosition;
//store the position of the tip for use in velocity calculations
this.lastPosition = wandPosition;
//create a bubble at the wand tip
_t.currentBubble = Entities.addEntity({
this.currentBubble = Entities.addEntity({
name: 'Bubble',
type: 'Model',
modelURL: bubbleModel,
position: wandTipPosition,
dimensions: {
x: 0.01,
y: 0.01,
z: 0.01
},
modelURL: BUBBLE_MODEL,
position: this.getWandTipPosition(properties),
dimensions: BUBBLE_INITIAL_DIMENSIONS,
collisionsWillMove: false,
ignoreForCollisions: true,
gravity: BUBBLE_GRAVITY,
// collisionSoundURL:popSound,
shapeType: "sphere",
script: bubbleScript,
ignoreForCollisions: false,
linearDamping: BUBBLE_LINEAR_DAMPING,
shapeType: "sphere"
});
//add this bubble to an array of bubbles so we can keep track of them
_t.bubbles.push(_t.currentBubble)
},
startNearGrab: function () {
//create a bubble to grow at the start of the grab
if (this.currentBubble === null) {
this.createBubbleAtTipOfWand();
}
},
continueNearGrab: function () {
var deltaTime = checkInterval();
//only get the properties that we need
var properties = Entities.getEntityProperties(this.entityID, ["position", "rotation"]);
var wandTipPosition = this.getWandTipPosition(properties);
//update the bubble to stay with the wand tip
Entities.editEntity(this.currentBubble, {
position: wandTipPosition,
});
this.growBubbleWithWandVelocity(properties, deltaTime);
},
init: function() {
this.spawnBubble();
Script.update.connect(BubbleWand.update);
}
}
releaseGrab: function () {
//delete the current buble and reset state when the wand is released
Entities.deleteEntity(this.currentBubble);
this.currentBubble = null;
},
BubbleWand.init();
};
})
return new BubbleWand();
});

View file

@ -2614,20 +2614,19 @@ void Application::updateMyAvatarLookAtPosition() {
bool isLookingAtSomeone = false;
bool isHMD = _avatarUpdate->isHMDMode();
glm::vec3 lookAtSpot;
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
// looking in a mirror one's eyes appear steady.
lookAtSpot = _myCamera.getPosition();
} else if (eyeTracker->isTracking() && (isHMD || eyeTracker->isSimulating())) {
if (eyeTracker->isTracking() && (isHMD || eyeTracker->isSimulating())) {
// Look at the point that the user is looking at.
glm::vec3 lookAtPosition = eyeTracker->getLookAtPosition();
if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
lookAtPosition.x = -lookAtPosition.x;
}
if (isHMD) {
glm::mat4 headPose = getActiveDisplayPlugin()->getHeadPose();
glm::quat hmdRotation = glm::quat_cast(headPose);
lookAtSpot = _myCamera.getPosition() +
_myAvatar->getOrientation() * (hmdRotation * eyeTracker->getLookAtPosition());
lookAtSpot = _myCamera.getPosition() + _myAvatar->getOrientation() * (hmdRotation * lookAtPosition);
} else {
lookAtSpot = _myAvatar->getHead()->getEyePosition() +
(_myAvatar->getHead()->getFinalOrientationInWorldFrame() * eyeTracker->getLookAtPosition());
lookAtSpot = _myAvatar->getHead()->getEyePosition()
+ (_myAvatar->getHead()->getFinalOrientationInWorldFrame() * lookAtPosition);
}
} else {
AvatarSharedPointer lookingAt = _myAvatar->getLookAtTargetAvatar().lock();

View file

@ -33,8 +33,10 @@ void AnimInverseKinematics::loadPoses(const AnimPoseVec& poses) {
assert(_skeleton && ((poses.size() == 0) || (_skeleton->getNumJoints() == (int)poses.size())));
if (_skeleton->getNumJoints() == (int)poses.size()) {
_relativePoses = poses;
_accumulators.resize(_relativePoses.size());
} else {
_relativePoses.clear();
_accumulators.clear();
}
}
@ -133,8 +135,8 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(std::vector<IKTarge
computeAbsolutePoses(absolutePoses);
// clear the accumulators before we start the IK solver
for (auto& accumulatorPair: _accumulators) {
accumulatorPair.second.clear();
for (auto& accumulator: _accumulators) {
accumulator.clearAndClean();
}
float largestError = 0.0f;
@ -221,11 +223,11 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(std::vector<IKTarge
++numLoops;
// harvest accumulated rotations and apply the average
for (auto& accumulatorPair: _accumulators) {
RotationAccumulator& accumulator = accumulatorPair.second;
if (accumulator.size() > 0) {
_relativePoses[accumulatorPair.first].rot = accumulator.getAverage();
accumulator.clear();
const int numJoints = (int)_accumulators.size();
for (int i = 0; i < numJoints; ++i) {
if (_accumulators[i].size() > 0) {
_relativePoses[i].rot = _accumulators[i].getAverage();
_accumulators[i].clear();
}
}
@ -299,7 +301,11 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars
int numJoints = (int)_relativePoses.size();
for (int i = 0; i < numJoints; ++i) {
float dotSign = copysignf(1.0f, glm::dot(_relativePoses[i].rot, underPoses[i].rot));
_relativePoses[i].rot = glm::normalize(glm::lerp(_relativePoses[i].rot, dotSign * underPoses[i].rot, blend));
if (_accumulators[i].isDirty()) {
_relativePoses[i].rot = glm::normalize(glm::lerp(_relativePoses[i].rot, dotSign * underPoses[i].rot, blend));
} else {
_relativePoses[i].rot = underPoses[i].rot;
}
}
}
return evaluate(animVars, dt, triggersOut);
@ -642,18 +648,3 @@ void AnimInverseKinematics::setSkeletonInternal(AnimSkeleton::ConstPointer skele
clearConstraints();
}
}
void AnimInverseKinematics::relaxTowardDefaults(float dt) {
// NOTE: for now we just use a single relaxation timescale for all joints, but in the future
// we could vary the timescale on a per-joint basis or do other fancy things.
// for each joint: lerp towards the default pose
const float RELAXATION_TIMESCALE = 0.25f;
const float alpha = glm::clamp(dt / RELAXATION_TIMESCALE, 0.0f, 1.0f);
int numJoints = (int)_relativePoses.size();
for (int i = 0; i < numJoints; ++i) {
float dotSign = copysignf(1.0f, glm::dot(_relativePoses[i].rot, _defaultRelativePoses[i].rot));
_relativePoses[i].rot = glm::normalize(glm::lerp(_relativePoses[i].rot, dotSign * _defaultRelativePoses[i].rot, alpha));
}
}

View file

@ -50,8 +50,6 @@ protected:
// for AnimDebugDraw rendering
virtual const AnimPoseVec& getPosesInternal() const override { return _relativePoses; }
void relaxTowardDefaults(float dt);
RotationConstraint* getConstraint(int index);
void clearConstraints();
void initConstraints();
@ -72,7 +70,7 @@ protected:
};
std::map<int, RotationConstraint*> _constraints;
std::map<int, RotationAccumulator> _accumulators; // class-member to exploit temporal coherency
std::vector<RotationAccumulator> _accumulators;
std::vector<IKTargetVar> _targetVarVec;
AnimPoseVec _defaultRelativePoses; // poses of the relaxed state
AnimPoseVec _relativePoses; // current relative poses

View file

@ -11,17 +11,23 @@
#include <glm/gtx/quaternion.hpp>
void RotationAccumulator::add(glm::quat rotation) {
void RotationAccumulator::add(const glm::quat& rotation) {
// make sure both quaternions are on the same hyper-hemisphere before we add them linearly (lerp)
_rotationSum += copysignf(1.0f, glm::dot(_rotationSum, rotation)) * rotation;
++_numRotations;
_isDirty = true;
}
glm::quat RotationAccumulator::getAverage() {
return (_numRotations > 0) ? glm::normalize(_rotationSum) : glm::quat();
}
void RotationAccumulator::clear() {
void RotationAccumulator::clear() {
_rotationSum *= 0.0f;
_numRotations = 0;
}
void RotationAccumulator::clearAndClean() {
clear();
_isDirty = false;
}

View file

@ -14,19 +14,27 @@
class RotationAccumulator {
public:
RotationAccumulator() : _rotationSum(0.0f, 0.0f, 0.0f, 0.0f), _numRotations(0) { }
RotationAccumulator() : _rotationSum(0.0f, 0.0f, 0.0f, 0.0f), _numRotations(0), _isDirty(false) { }
int size() const { return _numRotations; }
void add(glm::quat rotation);
void add(const glm::quat& rotation);
glm::quat getAverage();
/// \return true if any rotations were accumulated
bool isDirty() const { return _isDirty; }
/// \brief clear accumulated rotation but don't change _isDirty
void clear();
/// \brief clear accumulated rotation and set _isDirty to false
void clearAndClean();
private:
glm::quat _rotationSum;
int _numRotations;
bool _isDirty;
};
#endif // hifi_RotationAccumulator_h

View file

@ -1727,9 +1727,7 @@ void Model::segregateMeshGroups() {
// Run through all of the meshes, and place them into their segregated, but unsorted buckets
int shapeID = 0;
for (int i = 0; i < (int)networkMeshes.size(); i++) {
//const NetworkMesh& networkMesh = *(networkMeshes.at(i).get());
const FBXMesh& mesh = geometry.meshes.at(i);
//const MeshState& state = _meshStates.at(i);
// Create the render payloads
int totalParts = mesh.parts.size();