mirror of
https://github.com/overte-org/overte.git
synced 2025-04-12 09:42:11 +02:00
make agent avatar animations work again, and use them in crowds
This commit is contained in:
parent
abb05bc686
commit
884d22a59b
4 changed files with 40 additions and 24 deletions
|
@ -46,14 +46,21 @@ AnimationDetails ScriptableAvatar::getAnimationDetails() {
|
|||
return _animationDetails;
|
||||
}
|
||||
|
||||
void ScriptableAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) {
|
||||
_bind.reset();
|
||||
_animSkeleton.reset();
|
||||
AvatarData::setSkeletonModelURL(skeletonModelURL);
|
||||
}
|
||||
void ScriptableAvatar::update(float deltatime) {
|
||||
if (_bind.isNull() && !_skeletonFBXURL.isEmpty()) { // AvatarData will parse the .fst, but not get the .fbx skeleton.
|
||||
_bind = DependencyManager::get<AnimationCache>()->getAnimation(_skeletonFBXURL);
|
||||
}
|
||||
|
||||
// Run animation
|
||||
if (_animation && _animation->isLoaded() && _animation->getFrames().size() > 0 && _bind->isLoaded()) {
|
||||
|
||||
if (_animation && _animation->isLoaded() && _animation->getFrames().size() > 0 && !_bind.isNull() && _bind->isLoaded()) {
|
||||
if (!_animSkeleton) {
|
||||
_animSkeleton = std::make_shared<AnimSkeleton>(_bind->getGeometry());
|
||||
}
|
||||
float currentFrame = _animationDetails.currentFrame + deltatime * _animationDetails.fps;
|
||||
if (_animationDetails.loop || currentFrame < _animationDetails.lastFrame) {
|
||||
while (currentFrame >= _animationDetails.lastFrame) {
|
||||
|
@ -64,14 +71,16 @@ void ScriptableAvatar::update(float deltatime) {
|
|||
const QVector<FBXJoint>& modelJoints = _bind->getGeometry().joints;
|
||||
QStringList animationJointNames = _animation->getJointNames();
|
||||
|
||||
if (_jointData.size() != modelJoints.size()) {
|
||||
_jointData.resize(modelJoints.size());
|
||||
const int nJoints = modelJoints.size();
|
||||
if (_jointData.size() != nJoints) {
|
||||
_jointData.resize(nJoints);
|
||||
}
|
||||
|
||||
const int frameCount = _animation->getFrames().size();
|
||||
const FBXAnimationFrame& floorFrame = _animation->getFrames().at((int)glm::floor(currentFrame) % frameCount);
|
||||
const FBXAnimationFrame& ceilFrame = _animation->getFrames().at((int)glm::ceil(currentFrame) % frameCount);
|
||||
const float frameFraction = glm::fract(currentFrame);
|
||||
std::vector<AnimPose> poses = _animSkeleton->getRelativeDefaultPoses();
|
||||
|
||||
for (int i = 0; i < animationJointNames.size(); i++) {
|
||||
const QString& name = animationJointNames[i];
|
||||
|
@ -79,18 +88,21 @@ void ScriptableAvatar::update(float deltatime) {
|
|||
// trusting the .fst (which is sometimes not updated to match changes to .fbx).
|
||||
int mapping = _bind->getGeometry().getJointIndex(name);
|
||||
if (mapping != -1 && !_maskedJoints.contains(name)) {
|
||||
JointData& data = _jointData[mapping];
|
||||
|
||||
auto newRotation = modelJoints[mapping].preRotation *
|
||||
safeMix(floorFrame.rotations.at(i), ceilFrame.rotations.at(i), frameFraction);
|
||||
// We could probably do translations as in interpolation in model space (rather than the parent space that each frame is in),
|
||||
// but we don't do so for MyAvatar yet, so let's not be different here.
|
||||
if (data.rotation != newRotation) {
|
||||
data.rotation = newRotation;
|
||||
data.rotationSet = true;
|
||||
}
|
||||
// Eventually, this should probably deal with post rotations and translations, too.
|
||||
poses[mapping].rot = modelJoints[mapping].preRotation *
|
||||
safeMix(floorFrame.rotations.at(i), ceilFrame.rotations.at(i), frameFraction);;
|
||||
}
|
||||
}
|
||||
_animSkeleton->convertRelativePosesToAbsolute(poses);
|
||||
for (int i = 0; i < nJoints; i++) {
|
||||
JointData& data = _jointData[i];
|
||||
AnimPose& pose = poses[i];
|
||||
if (data.rotation != pose.rot) {
|
||||
data.rotation = pose.rot;
|
||||
data.rotationSet = true;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
_animation.clear();
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#define hifi_ScriptableAvatar_h
|
||||
|
||||
#include <AnimationCache.h>
|
||||
#include <AnimSkeleton.h>
|
||||
#include <AvatarData.h>
|
||||
#include <ScriptEngine.h>
|
||||
|
||||
|
@ -25,6 +26,7 @@ public:
|
|||
bool hold = false, float firstFrame = 0.0f, float lastFrame = FLT_MAX, const QStringList& maskedJoints = QStringList());
|
||||
Q_INVOKABLE void stopAnimation();
|
||||
Q_INVOKABLE AnimationDetails getAnimationDetails();
|
||||
virtual void setSkeletonModelURL(const QUrl& skeletonModelURL) override;
|
||||
|
||||
private slots:
|
||||
void update(float deltatime);
|
||||
|
@ -34,6 +36,7 @@ private:
|
|||
AnimationDetails _animationDetails;
|
||||
QStringList _maskedJoints;
|
||||
AnimationPointer _bind; // a sleazy way to get the skeleton, given the various library/cmake dependencies
|
||||
std::shared_ptr<AnimSkeleton> _animSkeleton;
|
||||
};
|
||||
|
||||
#endif // hifi_ScriptableAvatar_h
|
|
@ -22,6 +22,7 @@ print('crowd-agent version 2');
|
|||
- File urls for AC scripts silently fail. Use a local server (e.g., python SimpleHTTPServer) for development.
|
||||
- URLs are cached regardless of server headers. Must use cache-defeating query parameters.
|
||||
- JSON.stringify(Avatar) silently fails (even when Agent.isAvatar)
|
||||
- If you run from a dev build directory, you must link assignment-client/<target>/resources to ../../interface/<target>/resources
|
||||
*/
|
||||
|
||||
function messageSend(message) {
|
||||
|
|
|
@ -23,6 +23,14 @@ var DENSITY = 0.3; // square meters per person. Some say 10 sq ft is arm's lengt
|
|||
var SOUND_DATA = {url: "http://howard-stearns.github.io/models/sounds/piano1.wav"};
|
||||
var AVATARS_CHATTERING_AT_ONCE = 4; // How many of the agents should we request to play SOUND at once.
|
||||
var NEXT_SOUND_SPREAD = 500; // millisecond range of how long to wait after one sound finishes, before playing the next
|
||||
var ANIMATION_DATA = { // T-pose until we get animations working again.
|
||||
"url": "http://howard-stearns.github.io/models/resources/avatar/animations/idle.fbx",
|
||||
//"url": "http://howard-stearns.github.io/models/resources/avatar/animations/walk_fwd.fbx",
|
||||
"startFrame": 0.0,
|
||||
"endFrame": 300.0,
|
||||
"timeScale": 1.0,
|
||||
"loopFlag": true
|
||||
};
|
||||
|
||||
var spread = Math.sqrt(MINIMUM_AVATARS * DENSITY); // meters
|
||||
var turnSpread = 90; // How many degrees should turn from front range over.
|
||||
|
@ -71,18 +79,10 @@ function messageHandler(channel, messageString, senderID) {
|
|||
rcpt: senderID,
|
||||
position: Vec3.sum(MyAvatar.position, {x: coord(), y: 0, z: coord()}),
|
||||
orientation: Quat.fromPitchYawRollDegrees(0, Quat.safeEulerAngles(MyAvatar.orientation).y + (turnSpread * (Math.random() - 0.5)), 0),
|
||||
soundData: chatter && SOUND_DATA/*
|
||||
soundData: chatter && SOUND_DATA,
|
||||
// No need to specify skeletonModelURL
|
||||
//skeletonModelURL: "file:///c:/Program Files/High Fidelity Release/resources/meshes/being_of_light/being_of_light.fbx",
|
||||
//skeletonModelURL: "file:///c:/Program Files/High Fidelity Release/resources/meshes/defaultAvatar_full.fst"/,
|
||||
animationData: { // T-pose until we get animations working again.
|
||||
"url": "file:///C:/Program Files/High Fidelity Release/resources/avatar/animations/idle.fbx",
|
||||
//"url": "file:///c:/Program Files/High Fidelity Release/resources/avatar/animations/walk_fwd.fbx",
|
||||
"startFrame": 0.0,
|
||||
"endFrame": 300.0,
|
||||
"timeScale": 1.0,
|
||||
"loopFlag": true
|
||||
}*/
|
||||
animationData: ANIMATION_DATA
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
|
Loading…
Reference in a new issue