fix dynamic bone animations
This commit is contained in:
@@ -214,9 +214,10 @@ class _MyAppState extends State<MyApp> with SingleTickerProviderStateMixin {
|
|||||||
return vals;
|
return vals;
|
||||||
}).toList());
|
}).toList());
|
||||||
|
|
||||||
_filamentController.setBoneAnimation(_cube!, [
|
_filamentController.setBoneAnimation(
|
||||||
BoneAnimationData("Bone.001", "Cube.001", frameData, 1000.0 / 60.0)
|
_cube!,
|
||||||
]);
|
BoneAnimationData(
|
||||||
|
"Bone.001", ["Cube.001"], frameData, 1000.0 / 60.0));
|
||||||
// ,
|
// ,
|
||||||
// "Bone.001",
|
// "Bone.001",
|
||||||
// "Cube.001",
|
// "Cube.001",
|
||||||
|
|||||||
@@ -54,7 +54,8 @@ namespace polyvox {
|
|||||||
int numFrames,
|
int numFrames,
|
||||||
int numBones,
|
int numBones,
|
||||||
const char** const boneNames,
|
const char** const boneNames,
|
||||||
const char* const meshName,
|
const char** const meshName,
|
||||||
|
int numMeshTargets,
|
||||||
float frameLengthInMs);
|
float frameLengthInMs);
|
||||||
void playAnimation(EntityId e, int index, bool loop, bool reverse);
|
void playAnimation(EntityId e, int index, bool loop, bool reverse);
|
||||||
void stopAnimation(EntityId e, int index);
|
void stopAnimation(EntityId e, int index);
|
||||||
|
|||||||
@@ -64,7 +64,8 @@ void set_bone_animation(
|
|||||||
int numFrames,
|
int numFrames,
|
||||||
int numBones,
|
int numBones,
|
||||||
const char** const boneNames,
|
const char** const boneNames,
|
||||||
const char* const meshName,
|
const char** const meshName,
|
||||||
|
int numMeshTargets,
|
||||||
float frameLengthInMs);
|
float frameLengthInMs);
|
||||||
|
|
||||||
void play_animation(void* assetManager, EntityId asset, int index, bool loop, bool reverse);
|
void play_animation(void* assetManager, EntityId asset, int index, bool loop, bool reverse);
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#include "PolyvoxFilamentApi.h"
|
#include "PolyvoxFilamentApi.h"
|
||||||
}
|
}
|
||||||
|
template class std::vector<float>;
|
||||||
namespace polyvox {
|
namespace polyvox {
|
||||||
using namespace filament;
|
using namespace filament;
|
||||||
using namespace filament::gltfio;
|
using namespace filament::gltfio;
|
||||||
@@ -56,8 +56,12 @@ namespace polyvox {
|
|||||||
// Multiple bones are supported but these must be skinned to a single mesh target.
|
// Multiple bones are supported but these must be skinned to a single mesh target.
|
||||||
//
|
//
|
||||||
struct BoneAnimationBuffer {
|
struct BoneAnimationBuffer {
|
||||||
utils::Entity mMeshTarget;
|
vector<utils::Entity> mMeshTargets;
|
||||||
vector<uint8_t> mBones;
|
vector<uint8_t> mBones;
|
||||||
|
vector<math::mat4f> mBaseTransforms;
|
||||||
|
// vector<math::float3> mBaseTranslations; // these are the base transforms for the bones we will animate; the translations/rotations in mFrameData will be relative to this.
|
||||||
|
// vector<math::quatf> mBaseRotations; // these are the base transforms for the bones we will animate; the translations/rotations in mFrameData will be relative to this.
|
||||||
|
// vector<math::float3> mBaseScales; // these are the base transforms for the bones we will animate; the translations/rotations in mFrameData will be relative to this.
|
||||||
size_t skinIndex = 0;
|
size_t skinIndex = 0;
|
||||||
int mNumFrames = -1;
|
int mNumFrames = -1;
|
||||||
float mFrameLengthInMs = 0;
|
float mFrameLengthInMs = 0;
|
||||||
@@ -99,10 +103,5 @@ namespace polyvox {
|
|||||||
mAnimations[i].mDuration = mAnimator->getAnimationDuration(i);
|
mAnimations[i].mDuration = mAnimator->getAnimationDuration(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1,16 +1,17 @@
|
|||||||
#include "AssetManager.hpp"
|
#include "AssetManager.hpp"
|
||||||
#include "Log.hpp"
|
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <filament/Engine.h>
|
#include <filament/Engine.h>
|
||||||
#include <filament/TransformManager.h>
|
#include <filament/TransformManager.h>
|
||||||
#include <filament/Texture.h>
|
#include <filament/Texture.h>
|
||||||
#include <filament/RenderableManager.h>
|
#include <filament/RenderableManager.h>
|
||||||
|
|
||||||
|
|
||||||
#include <gltfio/Animator.h>
|
#include <gltfio/Animator.h>
|
||||||
#include <gltfio/AssetLoader.h>
|
#include <gltfio/AssetLoader.h>
|
||||||
#include <gltfio/FilamentAsset.h>
|
#include <gltfio/FilamentAsset.h>
|
||||||
#include <gltfio/ResourceLoader.h>
|
#include <gltfio/ResourceLoader.h>
|
||||||
#include <gltfio/TextureProvider.h>
|
#include <gltfio/TextureProvider.h>
|
||||||
|
#include <gltfio/math.h>
|
||||||
|
|
||||||
#include <imageio/ImageDecoder.h>
|
#include <imageio/ImageDecoder.h>
|
||||||
|
|
||||||
@@ -322,8 +323,10 @@ void AssetManager::updateAnimations() {
|
|||||||
frameNumber
|
frameNumber
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
asset.mAnimator->updateBoneMatrices();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssetManager::setBoneTransform(SceneAsset& asset, int frameNumber) {
|
void AssetManager::setBoneTransform(SceneAsset& asset, int frameNumber) {
|
||||||
@@ -336,61 +339,30 @@ void AssetManager::updateAnimations() {
|
|||||||
|
|
||||||
int skinIndex = 0;
|
int skinIndex = 0;
|
||||||
|
|
||||||
math::mat4f inverseGlobalTransform = inverse(
|
|
||||||
transformManager.getWorldTransform(
|
|
||||||
transformManager.getInstance(asset.mBoneAnimationBuffer.mMeshTarget)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
auto renderable = rm.getInstance(asset.mBoneAnimationBuffer.mMeshTarget);
|
|
||||||
|
|
||||||
for(int i = 0; i < asset.mBoneAnimationBuffer.mBones.size(); i++) {
|
for(int i = 0; i < asset.mBoneAnimationBuffer.mBones.size(); i++) {
|
||||||
auto mBoneIndex = asset.mBoneAnimationBuffer.mBones[i];
|
auto mBoneIndex = asset.mBoneAnimationBuffer.mBones[i];
|
||||||
auto frameDataOffset = (frameNumber * asset.mBoneAnimationBuffer.mBones.size() * 7) + asset.mBoneAnimationBuffer.mBones[i];
|
auto frameDataOffset = (frameNumber * asset.mBoneAnimationBuffer.mBones.size() * 7) + (i * 7);
|
||||||
|
|
||||||
utils::Entity joint = filamentInstance->getJointsAt(skinIndex)[mBoneIndex];
|
utils::Entity joint = filamentInstance->getJointsAt(skinIndex)[mBoneIndex];
|
||||||
if(joint.isNull()) {
|
if(joint.isNull()) {
|
||||||
Log("ERROR : joint not found");
|
Log("ERROR : joint not found");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// RenderableManager::Bone bone { math::quatf{
|
|
||||||
// asset.mBoneAnimationBuffer.mFrameData[frameDataOffset+6],
|
|
||||||
// asset.mBoneAnimationBuffer.mFrameData[frameDataOffset+3],
|
|
||||||
// asset.mBoneAnimationBuffer.mFrameData[frameDataOffset+4],
|
|
||||||
// asset.mBoneAnimationBuffer.mFrameData[frameDataOffset+5]
|
|
||||||
// },
|
|
||||||
// math::float3 {
|
|
||||||
// asset.mBoneAnimationBuffer.mFrameData[frameDataOffset+0],
|
|
||||||
// asset.mBoneAnimationBuffer.mFrameData[frameDataOffset+1],
|
|
||||||
// asset.mBoneAnimationBuffer.mFrameData[frameDataOffset+2]
|
|
||||||
// }
|
|
||||||
|
|
||||||
// };
|
vector<float>& fd = asset.mBoneAnimationBuffer.mFrameData;
|
||||||
// rm.setBones(
|
|
||||||
// renderable,
|
math::mat4f localTransform(math::quatf {
|
||||||
// &bone,
|
fd[frameDataOffset+3],
|
||||||
// 1,
|
fd[frameDataOffset+4],
|
||||||
// mBoneIndex
|
fd[frameDataOffset+5],
|
||||||
// );
|
fd[frameDataOffset+6],
|
||||||
const math::mat4f localTransform(math::quatf{
|
|
||||||
asset.mBoneAnimationBuffer.mFrameData[frameDataOffset+3],
|
|
||||||
asset.mBoneAnimationBuffer.mFrameData[frameDataOffset+4],
|
|
||||||
asset.mBoneAnimationBuffer.mFrameData[frameDataOffset+5],
|
|
||||||
asset.mBoneAnimationBuffer.mFrameData[frameDataOffset+6]
|
|
||||||
});
|
});
|
||||||
const math::mat4f& inverseBindMatrix = filamentInstance->getInverseBindMatricesAt(skinIndex)[mBoneIndex];
|
|
||||||
auto jointInstance = transformManager.getInstance(joint);
|
auto jointInstance = transformManager.getInstance(joint);
|
||||||
math::mat4f globalJointTransform = transformManager.getWorldTransform(jointInstance);
|
|
||||||
|
|
||||||
math::mat4f boneTransform = inverseGlobalTransform * globalJointTransform * inverseBindMatrix * localTransform;
|
auto xform = asset.mBoneAnimationBuffer.mBaseTransforms[i];
|
||||||
|
|
||||||
rm.setBones(
|
|
||||||
renderable,
|
|
||||||
&boneTransform,
|
|
||||||
1,
|
|
||||||
mBoneIndex
|
|
||||||
);
|
|
||||||
|
|
||||||
|
transformManager.setTransform(jointInstance, xform * localTransform);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -486,22 +458,18 @@ bool AssetManager::setBoneAnimationBuffer(
|
|||||||
int numFrames,
|
int numFrames,
|
||||||
int numBones,
|
int numBones,
|
||||||
const char** const boneNames,
|
const char** const boneNames,
|
||||||
const char* const meshName,
|
const char** const meshNames,
|
||||||
|
int numMeshTargets,
|
||||||
float frameLengthInMs) {
|
float frameLengthInMs) {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const auto& pos = _entityIdLookup.find(entityId);
|
const auto& pos = _entityIdLookup.find(entityId);
|
||||||
if(pos == _entityIdLookup.end()) {
|
if(pos == _entityIdLookup.end()) {
|
||||||
Log("ERROR: asset not found for entity.");
|
Log("ERROR: asset not found for entity.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
auto& asset = _assets[pos->second];
|
auto& asset = _assets[pos->second];
|
||||||
|
|
||||||
auto entity = findEntityByName(asset, meshName);
|
|
||||||
if(!entity) {
|
|
||||||
Log("Mesh target %s for bone animation could not be found", meshName);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto filamentInstance = asset.mAsset->getInstance();
|
auto filamentInstance = asset.mAsset->getInstance();
|
||||||
|
|
||||||
size_t skinCount = filamentInstance->getSkinCount();
|
size_t skinCount = filamentInstance->getSkinCount();
|
||||||
@@ -510,46 +478,71 @@ bool AssetManager::setBoneAnimationBuffer(
|
|||||||
Log("WARNING - skin count > 1 not currently implemented. This will probably not work");
|
Log("WARNING - skin count > 1 not currently implemented. This will probably not work");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TransformManager &transformManager = _engine->getTransformManager();
|
||||||
|
|
||||||
int skinIndex = 0;
|
int skinIndex = 0;
|
||||||
const utils::Entity* joints = filamentInstance->getJointsAt(skinIndex);
|
const utils::Entity* joints = filamentInstance->getJointsAt(skinIndex);
|
||||||
size_t numJoints = filamentInstance->getJointCountAt(skinIndex);
|
size_t numJoints = filamentInstance->getJointCountAt(skinIndex);
|
||||||
|
|
||||||
asset.mBoneAnimationBuffer.mBones.clear();
|
BoneAnimationBuffer& animationBuffer = asset.mBoneAnimationBuffer;
|
||||||
|
|
||||||
|
// if an animation has already been set, reset the transform for the respective bones
|
||||||
|
for(int i = 0; i < animationBuffer.mBones.size(); i++) {
|
||||||
|
auto boneIndex = animationBuffer.mBones[i];
|
||||||
|
auto jointInstance = transformManager.getInstance(joints[boneIndex]);
|
||||||
|
transformManager.setTransform(jointInstance, animationBuffer.mBaseTransforms[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
asset.mAnimator->resetBoneMatrices();
|
||||||
|
|
||||||
|
animationBuffer.mBones.resize(numBones);
|
||||||
|
animationBuffer.mBaseTransforms.resize(numBones);
|
||||||
|
|
||||||
for(int i = 0; i < numBones; i++) {
|
for(int i = 0; i < numBones; i++) {
|
||||||
Log("Bone %s", boneNames[i]);
|
Log("Bone %s", boneNames[i]);
|
||||||
for(int j = 0; j < numJoints; j++) {
|
for(int j = 0; j < numJoints; j++) {
|
||||||
const char* jointName = _ncm->getName(_ncm->getInstance(joints[j]));
|
const char* jointName = _ncm->getName(_ncm->getInstance(joints[j]));
|
||||||
if(strcmp(jointName, boneNames[i]) == 0) {
|
if(strcmp(jointName, boneNames[i]) == 0) {
|
||||||
asset.mBoneAnimationBuffer.mBones.push_back(j);
|
auto jointInstance = transformManager.getInstance(joints[j]);
|
||||||
|
// auto currentXform = ;
|
||||||
|
auto baseTransform = transformManager.getTransform(jointInstance); // inverse(filamentInstance->getInverseBindMatricesAt(skinIndex)[j]);
|
||||||
|
animationBuffer.mBaseTransforms[i] = baseTransform;
|
||||||
|
animationBuffer.mBones[i] = j;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(asset.mBoneAnimationBuffer.mBones.size() != numBones) {
|
if(animationBuffer.mBones.size() != numBones) {
|
||||||
Log("Failed to find one or more bone indices");
|
Log("Failed to find one or more bone indices");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
asset.mBoneAnimationBuffer.mFrameData.clear();
|
animationBuffer.mFrameData.clear();
|
||||||
// 7 == locX, locY, locZ, rotW, rotX, rotY, rotZ
|
// 7 == locX, locY, locZ, rotW, rotX, rotY, rotZ
|
||||||
asset.mBoneAnimationBuffer.mFrameData.resize(numFrames * numBones * 7);
|
animationBuffer.mFrameData.resize(numFrames * numBones * 7);
|
||||||
asset.mBoneAnimationBuffer.mFrameData.insert(
|
animationBuffer.mFrameData.insert(
|
||||||
asset.mBoneAnimationBuffer.mFrameData.begin(),
|
animationBuffer.mFrameData.begin(),
|
||||||
frameData,
|
frameData,
|
||||||
frameData + numFrames * numBones * 7
|
frameData + numFrames * numBones * 7
|
||||||
);
|
);
|
||||||
|
|
||||||
Log("%d frames for %d bones", numFrames, numBones);
|
Log("%d frames for %d bones", numFrames, numBones);
|
||||||
|
|
||||||
// for(int i = 0; i < numFrames * numBones * 7; i++) {
|
animationBuffer.mFrameLengthInMs = frameLengthInMs;
|
||||||
// Log("Frame data @ %d is %f", i, frameData[i]);
|
animationBuffer.mNumFrames = numFrames;
|
||||||
// }
|
|
||||||
|
|
||||||
asset.mBoneAnimationBuffer.mFrameLengthInMs = frameLengthInMs;
|
animationBuffer.mMeshTargets.clear();
|
||||||
asset.mBoneAnimationBuffer.mNumFrames = numFrames;
|
for(int i = 0; i < numMeshTargets; i++) {
|
||||||
|
auto entity = findEntityByName(asset, meshNames[i]);
|
||||||
|
if(!entity) {
|
||||||
|
Log("Mesh target %s for bone animation could not be found", meshNames[i]);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Log("Added mesh target %s", meshNames[i]);
|
||||||
|
animationBuffer.mMeshTargets.push_back(entity);
|
||||||
|
}
|
||||||
|
|
||||||
asset.mBoneAnimationBuffer.mMeshTarget = entity;
|
|
||||||
auto& animation = asset.mAnimations[asset.mAnimations.size() - 1];
|
auto& animation = asset.mAnimations[asset.mAnimations.size() - 1];
|
||||||
animation.mStart = std::chrono::high_resolution_clock::now();
|
animation.mStart = std::chrono::high_resolution_clock::now();
|
||||||
animation.mAnimating = true;
|
animation.mAnimating = true;
|
||||||
@@ -557,12 +550,6 @@ bool AssetManager::setBoneAnimationBuffer(
|
|||||||
animation.mDuration = (frameLengthInMs * numFrames) / 1000.0f;
|
animation.mDuration = (frameLengthInMs * numFrames) / 1000.0f;
|
||||||
asset.mAnimating = true;
|
asset.mAnimating = true;
|
||||||
|
|
||||||
// // Log(", set start to %f and duration to %f", );
|
|
||||||
// Log("Successfully set bone animation buffer, set start to %d, dur is %f",
|
|
||||||
// std::chrono::duration_cast<std::chrono::milliseconds>(asset.mAnimations[1].mStart.time_since_epoch()).count(),
|
|
||||||
// asset.mAnimations[1].mDuration
|
|
||||||
// );
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -833,3 +820,40 @@ size_t AssetManager::getLightEntityCount(EntityId entity) const noexcept {
|
|||||||
|
|
||||||
|
|
||||||
} // namespace polyvox
|
} // namespace polyvox
|
||||||
|
|
||||||
|
|
||||||
|
// auto& inverseBindMatrix = filamentInstance->getInverseBindMatricesAt(skinIndex)[mBoneIndex];
|
||||||
|
|
||||||
|
// auto globalJointTransform = transformManager.getWorldTransform(jointInstance);
|
||||||
|
|
||||||
|
// for(auto& target : asset.mBoneAnimationBuffer.mMeshTargets) {
|
||||||
|
|
||||||
|
// auto inverseGlobalTransform = inverse(
|
||||||
|
// transformManager.getWorldTransform(
|
||||||
|
// transformManager.getInstance(target)
|
||||||
|
// )
|
||||||
|
// );
|
||||||
|
|
||||||
|
// auto boneTransform = inverseGlobalTransform * globalJointTransform * localTransform * inverseBindMatrix;
|
||||||
|
// auto renderable = rm.getInstance(target);
|
||||||
|
// rm.setBones(
|
||||||
|
// renderable,
|
||||||
|
// &boneTransform,
|
||||||
|
// 1,
|
||||||
|
// mBoneIndex
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 1.0f, 0.0f, 0.0f, 0.0f,
|
||||||
|
// 0.0f, 0.0f, 1.0f, 0.0f,
|
||||||
|
// 0.0f, -1.0f, 0.0f, 0.0f,
|
||||||
|
// 0.0f, 0.0f, 0.0f, 1.0f
|
||||||
|
// };
|
||||||
|
// Log("TRANSFORM");
|
||||||
|
// Log("%f %f %f %f", localTransform[0][0], localTransform[1][0], localTransform[2][0], localTransform[3][0] ) ;
|
||||||
|
// Log("%f %f %f %f", localTransform[0][1], localTransform[1][1], localTransform[2][1], localTransform[3][1] ) ;
|
||||||
|
// Log("%f %f %f %f", localTransform[0][2], localTransform[1][2], localTransform[2][2], localTransform[3][2] ) ;
|
||||||
|
// Log("%f %f %f %f", localTransform[0][3], localTransform[1][3], localTransform[2][3], localTransform[3][3] ) ;
|
||||||
|
// transformManager.getTransform(jointInstance);
|
||||||
@@ -788,7 +788,7 @@ void FilamentViewer::render(uint64_t frameTimeInNanos) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(_frameCount == 60) {
|
if(_frameCount == 60) {
|
||||||
Log("1 sec average for asset animation update %f", _elapsed);
|
Log("1 sec average for asset animation update %f", _elapsed / 60);
|
||||||
_elapsed = 0;
|
_elapsed = 0;
|
||||||
_frameCount = 0;
|
_frameCount = 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -362,7 +362,8 @@ extern "C" {
|
|||||||
int numFrames,
|
int numFrames,
|
||||||
int numBones,
|
int numBones,
|
||||||
const char** const boneNames,
|
const char** const boneNames,
|
||||||
const char* const meshName,
|
const char** const meshNames,
|
||||||
|
int numMeshTargets,
|
||||||
float frameLengthInMs) {
|
float frameLengthInMs) {
|
||||||
//std::packaged_task<void()> lambda([=]() mutable {
|
//std::packaged_task<void()> lambda([=]() mutable {
|
||||||
((AssetManager*)assetManager)->setBoneAnimationBuffer(
|
((AssetManager*)assetManager)->setBoneAnimationBuffer(
|
||||||
@@ -371,7 +372,8 @@ extern "C" {
|
|||||||
numFrames,
|
numFrames,
|
||||||
numBones,
|
numBones,
|
||||||
boneNames,
|
boneNames,
|
||||||
meshName,
|
meshNames,
|
||||||
|
numMeshTargets,
|
||||||
frameLengthInMs
|
frameLengthInMs
|
||||||
);
|
);
|
||||||
//});
|
//});
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ import 'package:vector_math/vector_math.dart';
|
|||||||
///
|
///
|
||||||
class BoneAnimationData {
|
class BoneAnimationData {
|
||||||
final String boneName;
|
final String boneName;
|
||||||
final String meshName;
|
final List<String> meshNames;
|
||||||
final Float32List frameData;
|
final Float32List frameData;
|
||||||
double frameLengthInMs;
|
double frameLengthInMs;
|
||||||
BoneAnimationData(
|
BoneAnimationData(
|
||||||
this.boneName, this.meshName, this.frameData, this.frameLengthInMs);
|
this.boneName, this.meshNames, this.frameData, this.frameLengthInMs);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,11 +46,21 @@ class BoneDriver {
|
|||||||
rotation.x *= weight;
|
rotation.x *= weight;
|
||||||
rotation.y *= weight;
|
rotation.y *= weight;
|
||||||
rotation.z *= weight;
|
rotation.z *= weight;
|
||||||
|
rotation.w = 1;
|
||||||
|
|
||||||
return rotation;
|
return rotation;
|
||||||
}).toList();
|
}).toList();
|
||||||
|
|
||||||
yield rotations.fold(
|
if (frameNum == 0) {
|
||||||
rotations.first, (Quaternion a, Quaternion b) => a * b);
|
print(rotations);
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = rotations.fold(
|
||||||
|
rotations.first, (Quaternion a, Quaternion b) => a + b);
|
||||||
|
result.w = 1;
|
||||||
|
print("RESULT $result");
|
||||||
|
yield result;
|
||||||
|
// .normalized();
|
||||||
// todo - bone translations
|
// todo - bone translations
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,11 +11,12 @@ import 'package:vector_math/vector_math.dart';
|
|||||||
/// A class for loading animation data from a single CSV and allocating between morph/bone animation with help.
|
/// A class for loading animation data from a single CSV and allocating between morph/bone animation with help.
|
||||||
///
|
///
|
||||||
class DynamicAnimation {
|
class DynamicAnimation {
|
||||||
final MorphAnimationData morphAnimation;
|
final MorphAnimationData? morphAnimation;
|
||||||
final List<BoneAnimationData> boneAnimation;
|
final List<BoneAnimationData> boneAnimation;
|
||||||
|
|
||||||
factory DynamicAnimation.load(String meshName, String csvPath,
|
factory DynamicAnimation.load(String? meshName, String csvPath,
|
||||||
{List<BoneDriver>? boneDrivers,
|
{List<BoneDriver>? boneDrivers,
|
||||||
|
List<String>? boneMeshes,
|
||||||
String? boneDriverConfigPath,
|
String? boneDriverConfigPath,
|
||||||
double? framerate}) {
|
double? framerate}) {
|
||||||
// create a MorphAnimationData instance from the given CSV
|
// create a MorphAnimationData instance from the given CSV
|
||||||
@@ -23,8 +24,9 @@ class DynamicAnimation {
|
|||||||
var frameLengthInMs = 1000 / (framerate ?? 60.0);
|
var frameLengthInMs = 1000 / (framerate ?? 60.0);
|
||||||
var morphNames = llf
|
var morphNames = llf
|
||||||
.item1; //.where((name) => !boneDrivers.any((element) => element.blendshape == name));
|
.item1; //.where((name) => !boneDrivers.any((element) => element.blendshape == name));
|
||||||
var morphAnimationData =
|
|
||||||
MorphAnimationData(meshName, llf.item2, morphNames, frameLengthInMs);
|
var morphAnimationData = MorphAnimationData(
|
||||||
|
meshName ?? "NULL", llf.item2, morphNames, frameLengthInMs);
|
||||||
|
|
||||||
final boneAnimations = <BoneAnimationData>[];
|
final boneAnimations = <BoneAnimationData>[];
|
||||||
|
|
||||||
@@ -45,14 +47,14 @@ class DynamicAnimation {
|
|||||||
// iterate over every bone driver
|
// iterate over every bone driver
|
||||||
if (boneDrivers != null) {
|
if (boneDrivers != null) {
|
||||||
for (var driver in boneDrivers) {
|
for (var driver in boneDrivers) {
|
||||||
// get all frames for the single the blendshape
|
// collect the frame data for the blendshapes that this driver uses
|
||||||
var morphData = driver.transformations
|
var morphData = driver.transformations
|
||||||
.map((String blendshape, Transformation transformation) {
|
.map((String blendshape, Transformation transformation) {
|
||||||
return MapEntry(
|
return MapEntry(
|
||||||
blendshape, morphAnimationData.getData(blendshape).toList());
|
blendshape, morphAnimationData.getData(blendshape).toList());
|
||||||
});
|
});
|
||||||
|
|
||||||
// apply the driver to the blendshape weight
|
// apply the driver to the frame data
|
||||||
var transformedQ = driver.transform(morphData).toList();
|
var transformedQ = driver.transform(morphData).toList();
|
||||||
|
|
||||||
// transform the quaternion to a Float32List
|
// transform the quaternion to a Float32List
|
||||||
@@ -60,7 +62,7 @@ class DynamicAnimation {
|
|||||||
|
|
||||||
// add to the list of boneAnimations
|
// add to the list of boneAnimations
|
||||||
boneAnimations.add(BoneAnimationData(
|
boneAnimations.add(BoneAnimationData(
|
||||||
driver.bone, meshName, transformedF, frameLengthInMs));
|
driver.bone, boneMeshes!, transformedF, frameLengthInMs));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,8 +5,9 @@ import 'package:vector_math/vector_math.dart';
|
|||||||
|
|
||||||
BoneDriver getLiveLinkFaceBoneDrivers(String bone) {
|
BoneDriver getLiveLinkFaceBoneDrivers(String bone) {
|
||||||
return BoneDriver(bone, {
|
return BoneDriver(bone, {
|
||||||
"HeadPitch": Transformation(Quaternion.axisAngle(Vector3(1, 0, 0), pi / 2)),
|
"HeadPitch":
|
||||||
"HeadRoll": Transformation(Quaternion.axisAngle(Vector3(0, 0, 1), pi / 2)),
|
Transformation(Quaternion.axisAngle(Vector3(0, 0, -1), pi / 3)),
|
||||||
|
"HeadRoll": Transformation(Quaternion.axisAngle(Vector3(1, 0, 0), pi / 2)),
|
||||||
"HeadYaw": Transformation(Quaternion.axisAngle(Vector3(0, 1, 0), pi / 2)),
|
"HeadYaw": Transformation(Quaternion.axisAngle(Vector3(0, 1, 0), pi / 2)),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -320,43 +320,38 @@ class FilamentController {
|
|||||||
/// Animates morph target weights/bone transforms (where each frame requires a duration of [frameLengthInMs].
|
/// Animates morph target weights/bone transforms (where each frame requires a duration of [frameLengthInMs].
|
||||||
/// [morphWeights] is a list of doubles in frame-major format.
|
/// [morphWeights] is a list of doubles in frame-major format.
|
||||||
/// Each frame is [numWeights] in length, and each entry is the weight to be applied to the morph target located at that index in the mesh primitive at that frame.
|
/// Each frame is [numWeights] in length, and each entry is the weight to be applied to the morph target located at that index in the mesh primitive at that frame.
|
||||||
|
/// for now we only allow animating a single bone (though multiple skinned targets are supported)
|
||||||
///
|
///
|
||||||
void setBoneAnimation(
|
void setBoneAnimation(
|
||||||
FilamentEntity asset, List<BoneAnimationData> animations) async {
|
FilamentEntity asset, BoneAnimationData animation) async {
|
||||||
// for future compatibility, instances of BoneAnimationData can specify individual mesh targets
|
var data = calloc<Float>(animation.frameData.length);
|
||||||
// however on the rendering side we currently only allow one set of frame data for one mesh target (though multiple bones are supported).
|
|
||||||
// this is a check that all animations are targeting the same mesh
|
|
||||||
assert(animations.map((e) => e.meshName).toSet().length == 1);
|
|
||||||
|
|
||||||
var data =
|
|
||||||
calloc<Float>(animations.length * animations.first.frameData.length);
|
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
var numFrames = animations.first.frameData.length ~/ 7;
|
var numFrames = animation.frameData.length ~/ 7;
|
||||||
var boneNames = calloc<Pointer<Char>>(animations.length);
|
var boneNames = calloc<Pointer<Char>>(1);
|
||||||
int animIdx = 0;
|
boneNames.elementAt(0).value =
|
||||||
for (var animation in animations) {
|
animation.boneName.toNativeUtf8().cast<Char>();
|
||||||
if (animation.frameData.length ~/ 7 != numFrames) {
|
|
||||||
throw Exception(
|
var meshNames = calloc<Pointer<Char>>(animation.meshNames.length);
|
||||||
"All bone animations must share the same animation frame data length.");
|
for (int i = 0; i < animation.meshNames.length; i++) {
|
||||||
|
meshNames.elementAt(i).value =
|
||||||
|
animation.meshNames[i].toNativeUtf8().cast<Char>();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < animation.frameData.length; i++) {
|
for (int i = 0; i < animation.frameData.length; i++) {
|
||||||
data.elementAt(offset).value = animation.frameData[i];
|
data.elementAt(offset).value = animation.frameData[i];
|
||||||
offset += 1;
|
offset += 1;
|
||||||
}
|
}
|
||||||
boneNames.elementAt(animIdx).value =
|
|
||||||
animation.boneName.toNativeUtf8().cast<Char>();
|
|
||||||
animIdx++;
|
|
||||||
}
|
|
||||||
|
|
||||||
_nativeLibrary.set_bone_animation(
|
_nativeLibrary.set_bone_animation(
|
||||||
_assetManager,
|
_assetManager,
|
||||||
asset,
|
asset,
|
||||||
data,
|
data,
|
||||||
numFrames,
|
numFrames,
|
||||||
animations.length,
|
1,
|
||||||
boneNames,
|
boneNames,
|
||||||
animations.first.meshName.toNativeUtf8().cast<Char>(),
|
meshNames,
|
||||||
animations.first.frameLengthInMs);
|
animation.meshNames.length,
|
||||||
|
animation.frameLengthInMs);
|
||||||
calloc.free(data);
|
calloc.free(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -659,7 +659,8 @@ class NativeLibrary {
|
|||||||
int numFrames,
|
int numFrames,
|
||||||
int numBones,
|
int numBones,
|
||||||
ffi.Pointer<ffi.Pointer<ffi.Char>> boneNames,
|
ffi.Pointer<ffi.Pointer<ffi.Char>> boneNames,
|
||||||
ffi.Pointer<ffi.Char> meshName,
|
ffi.Pointer<ffi.Pointer<ffi.Char>> meshName,
|
||||||
|
int numMeshTargets,
|
||||||
double frameLengthInMs,
|
double frameLengthInMs,
|
||||||
) {
|
) {
|
||||||
return _set_bone_animation(
|
return _set_bone_animation(
|
||||||
@@ -670,6 +671,7 @@ class NativeLibrary {
|
|||||||
numBones,
|
numBones,
|
||||||
boneNames,
|
boneNames,
|
||||||
meshName,
|
meshName,
|
||||||
|
numMeshTargets,
|
||||||
frameLengthInMs,
|
frameLengthInMs,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -683,7 +685,8 @@ class NativeLibrary {
|
|||||||
ffi.Int,
|
ffi.Int,
|
||||||
ffi.Int,
|
ffi.Int,
|
||||||
ffi.Pointer<ffi.Pointer<ffi.Char>>,
|
ffi.Pointer<ffi.Pointer<ffi.Char>>,
|
||||||
ffi.Pointer<ffi.Char>,
|
ffi.Pointer<ffi.Pointer<ffi.Char>>,
|
||||||
|
ffi.Int,
|
||||||
ffi.Float)>>('set_bone_animation');
|
ffi.Float)>>('set_bone_animation');
|
||||||
late final _set_bone_animation = _set_bone_animationPtr.asFunction<
|
late final _set_bone_animation = _set_bone_animationPtr.asFunction<
|
||||||
void Function(
|
void Function(
|
||||||
@@ -693,7 +696,8 @@ class NativeLibrary {
|
|||||||
int,
|
int,
|
||||||
int,
|
int,
|
||||||
ffi.Pointer<ffi.Pointer<ffi.Char>>,
|
ffi.Pointer<ffi.Pointer<ffi.Char>>,
|
||||||
ffi.Pointer<ffi.Char>,
|
ffi.Pointer<ffi.Pointer<ffi.Char>>,
|
||||||
|
int,
|
||||||
double)>();
|
double)>();
|
||||||
|
|
||||||
void play_animation(
|
void play_animation(
|
||||||
|
|||||||
Reference in New Issue
Block a user