add animation ticker inside render loop
This commit is contained in:
@@ -102,6 +102,7 @@ static void* freeResourceGlobal(void* mem, size_t size, void* misc) {
|
||||
*(framesArr+i) = [[frameData objectAtIndex:i] floatValue];
|
||||
}
|
||||
_viewer->animateWeights((float*)framesArr, [numWeights intValue], [frameData count], [frameRate floatValue]);
|
||||
result(@"OK");
|
||||
} else if([@"createMorpher" isEqualToString:call.method]) {
|
||||
const char* meshName = [call.arguments[0] UTF8String];
|
||||
NSArray* primitiveIndices = call.arguments[1];
|
||||
|
||||
@@ -61,6 +61,8 @@ using namespace filament;
|
||||
using namespace filament::math;
|
||||
using namespace gltfio;
|
||||
using namespace utils;
|
||||
using namespace std::chrono;
|
||||
|
||||
|
||||
namespace filament {
|
||||
class IndirectLight;
|
||||
@@ -148,13 +150,19 @@ FilamentViewer::FilamentViewer(
|
||||
manipulator =
|
||||
Manipulator<float>::Builder().orbitHomePosition(0.0f, 0.0f, 0.0f).targetPosition(0.0f, 0.0f, 0).build(Mode::ORBIT);
|
||||
_asset = nullptr;
|
||||
|
||||
|
||||
}
|
||||
|
||||
FilamentViewer::~FilamentViewer() {
|
||||
|
||||
}
|
||||
|
||||
void printWeights(float* weights, int numWeights) {
|
||||
for(int i =0; i < numWeights; i++) {
|
||||
// std::cout << weights[i];
|
||||
}
|
||||
}
|
||||
|
||||
void FilamentViewer::loadResources(string relativeResourcePath) {
|
||||
const char* const* const resourceUris = _asset->getResourceUris();
|
||||
const size_t resourceUriCount = _asset->getResourceUriCount();
|
||||
@@ -186,47 +194,10 @@ void FilamentViewer::releaseSourceAssets() {
|
||||
_freeResource((void*)materialProviderResources.data, materialProviderResources.size, nullptr);
|
||||
}
|
||||
|
||||
void FilamentViewer::animateWeightsInternal(float* data, int numWeights, int length, float frameRate) {
|
||||
int frameIndex = 0;
|
||||
int numFrames = length / numWeights;
|
||||
float frameLength = 1000 / frameRate;
|
||||
|
||||
applyWeights(data, numWeights);
|
||||
auto animationStartTime = std::chrono::high_resolution_clock::now();
|
||||
while(frameIndex < numFrames) {
|
||||
duration dur = std::chrono::high_resolution_clock::now() - animationStartTime;
|
||||
int msElapsed = dur.count();
|
||||
if(msElapsed > frameLength) {
|
||||
std::cout << "frame" << frameIndex << std::endl;
|
||||
frameIndex++;
|
||||
applyWeights(data + (frameIndex * numWeights), numWeights);
|
||||
animationStartTime = std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void FilamentViewer::animateWeights(float* data, int numWeights, int length, float frameRate) {
|
||||
int numFrames = length / numWeights;
|
||||
float frameLength = 1000 / frameRate;
|
||||
|
||||
thread* t = new thread(
|
||||
[=](){
|
||||
int frameIndex = 0;
|
||||
|
||||
applyWeights(data, numWeights);
|
||||
auto animationStartTime = std::chrono::high_resolution_clock::now();
|
||||
while(frameIndex < numFrames) {
|
||||
duration dur = std::chrono::high_resolution_clock::now() - animationStartTime;
|
||||
int msElapsed = dur.count();
|
||||
if(msElapsed > frameLength) {
|
||||
frameIndex++;
|
||||
applyWeights(data + (frameIndex * numWeights), numWeights);
|
||||
animationStartTime = std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
}
|
||||
});
|
||||
transformToUnitCube();
|
||||
morphAnimationBuffer = std::make_unique<MorphAnimationBuffer>(data, numWeights, length / numWeights, 1000 / frameRate );
|
||||
}
|
||||
|
||||
void FilamentViewer::loadGltf(const char* const uri, const char* const relativeResourcePath) {
|
||||
@@ -254,8 +225,6 @@ void FilamentViewer::loadGltf(const char* const uri, const char* const relativeR
|
||||
|
||||
transformToUnitCube();
|
||||
|
||||
startTime = std::chrono::high_resolution_clock::now();
|
||||
|
||||
}
|
||||
|
||||
StringList FilamentViewer::getTargetNames(const char* meshName) {
|
||||
@@ -307,9 +276,8 @@ void FilamentViewer::animateBones() {
|
||||
}
|
||||
|
||||
void FilamentViewer::playAnimation(int index) {
|
||||
_activeAnimation = index;
|
||||
embeddedAnimationBuffer = make_unique<EmbeddedAnimationBuffer>(index, _animator->getAnimationDuration(index));
|
||||
}
|
||||
|
||||
|
||||
void FilamentViewer::loadSkybox(const char* const skyboxPath, const char* const iblPath) {
|
||||
ResourceBuffer skyboxBuffer = _loadResource(skyboxPath);
|
||||
@@ -380,23 +348,106 @@ void FilamentViewer::render() {
|
||||
manipulator->getLookAt(&eye, &target, &upward);
|
||||
|
||||
_mainCamera->lookAt(eye, target, upward);
|
||||
|
||||
if(_animator) {
|
||||
|
||||
duration dur = std::chrono::high_resolution_clock::now() - startTime;
|
||||
if (_activeAnimation >= 0 && _animator->getAnimationCount() > 0) {
|
||||
_animator->applyAnimation(_activeAnimation, dur.count() / 1000);
|
||||
_animator->updateBoneMatrices();
|
||||
}
|
||||
|
||||
if(morphAnimationBuffer) {
|
||||
updateMorphAnimation();
|
||||
}
|
||||
|
||||
if(embeddedAnimationBuffer) {
|
||||
updateEmbeddedAnimation();
|
||||
}
|
||||
|
||||
// Render the scene, unless the renderer wants to skip the frame.
|
||||
if (_renderer->beginFrame(_swapChain)) {
|
||||
_renderer->render(_view);
|
||||
_renderer->endFrame();
|
||||
} else {
|
||||
std::cout << "Skipping frame" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
//void FilamentViewer::updateAnimation(AnimationBuffer animation, std::function<void(int)> moo) {
|
||||
// if(morphAnimationBuffer.frameIndex >= animation.numFrames) {
|
||||
// this.animation = null;
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// if(animation.frameIndex == -1) {
|
||||
// animation->frameIndex++;
|
||||
// animation->lastTime = std::chrono::high_resolution_clock::now();
|
||||
// callback(); // applyWeights(morphAnimationBuffer->frameData, morphAnimationBuffer->numWeights);
|
||||
// } else {
|
||||
// duration dur = std::chrono::high_resolution_clock::now() - morphAnimationBuffer->lastTime;
|
||||
// float msElapsed = dur.count();
|
||||
// if(msElapsed > animation->frameLength) {
|
||||
// animation->frameIndex++;
|
||||
// animation->lastTime = std::chrono::high_resolution_clock::now();
|
||||
// callback(); // applyWeights(frameData + (frameIndex * numWeights), numWeights);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
void FilamentViewer::updateMorphAnimation() {
|
||||
|
||||
if(morphAnimationBuffer->frameIndex >= morphAnimationBuffer->numFrames) {
|
||||
morphAnimationBuffer = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
if(morphAnimationBuffer->frameIndex == -1) {
|
||||
morphAnimationBuffer->frameIndex++;
|
||||
morphAnimationBuffer->lastTime = std::chrono::high_resolution_clock::now();
|
||||
applyWeights(morphAnimationBuffer->frameData, morphAnimationBuffer->numWeights);
|
||||
} else {
|
||||
duration dur = std::chrono::high_resolution_clock::now() - morphAnimationBuffer->lastTime;
|
||||
float microsElapsed = dur.count();
|
||||
if(microsElapsed > (morphAnimationBuffer->frameLength * 1000000)) {
|
||||
morphAnimationBuffer->frameIndex++;
|
||||
morphAnimationBuffer->lastTime = std::chrono::high_resolution_clock::now();
|
||||
applyWeights(morphAnimationBuffer->frameData + (morphAnimationBuffer->frameIndex * morphAnimationBuffer->numWeights), morphAnimationBuffer->numWeights);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void FilamentViewer::updateEmbeddedAnimation() {
|
||||
duration<double> dur = duration_cast<duration<double>>(std::chrono::high_resolution_clock::now() - embeddedAnimationBuffer->lastTime);
|
||||
float startTime = 0;
|
||||
if(!embeddedAnimationBuffer->hasStarted) {
|
||||
embeddedAnimationBuffer->hasStarted = true;
|
||||
embeddedAnimationBuffer->lastTime = std::chrono::high_resolution_clock::now();
|
||||
} else if(dur.count() >= embeddedAnimationBuffer->duration) {
|
||||
embeddedAnimationBuffer = nullptr;
|
||||
return;
|
||||
} else {
|
||||
startTime = dur.count();
|
||||
}
|
||||
|
||||
|
||||
_animator->applyAnimation(embeddedAnimationBuffer->animationIndex, startTime);
|
||||
_animator->updateBoneMatrices();
|
||||
|
||||
}
|
||||
//
|
||||
//if(morphAnimationBuffer.frameIndex >= morphAnimationBuffer.numFrames) {
|
||||
// this.morphAnimationBuffer = null;
|
||||
// return;
|
||||
//}
|
||||
//
|
||||
//if(morphAnimationBuffer.frameIndex == -1) {
|
||||
// applyWeights(morphAnimationBuffer->frameData, morphAnimationBuffer->numWeights);
|
||||
// morphAnimationBuffer->frameIndex++;
|
||||
// morphAnimationBuffer->lastTime = std::chrono::high_resolution_clock::now();
|
||||
//} else {
|
||||
// duration dur = std::chrono::high_resolution_clock::now() - morphAnimationBuffer->lastTime;
|
||||
// float msElapsed = dur.count();
|
||||
// if(msElapsed > morphAnimationBuffer->frameLength) {
|
||||
// frameIndex++;
|
||||
// applyWeights(frameData + (frameIndex * numWeights), numWeights);
|
||||
// morphAnimationBuffer->lastTime = std::chrono::high_resolution_clock::now();
|
||||
// }
|
||||
//}
|
||||
|
||||
void FilamentViewer::updateViewportAndCameraProjection(int width, int height, float contentScaleFactor) {
|
||||
if (!_view || !_mainCamera || !manipulator) {
|
||||
return;
|
||||
|
||||
@@ -42,6 +42,8 @@ using namespace camutils;
|
||||
|
||||
namespace mimetic {
|
||||
|
||||
typedef std::chrono::time_point<std::chrono::high_resolution_clock> time_point_t;
|
||||
|
||||
struct StringList {
|
||||
StringList(const char** strings, const int count) : strings(strings), count(count) {};
|
||||
const char** strings;
|
||||
@@ -61,7 +63,31 @@ namespace mimetic {
|
||||
uint64_t size;
|
||||
};
|
||||
|
||||
typedef std::chrono::duration<float, std::milli> duration;
|
||||
struct MorphAnimationBuffer {
|
||||
|
||||
MorphAnimationBuffer(float* frameData,
|
||||
int numWeights,
|
||||
int numFrames,
|
||||
float frameLength) : frameData(frameData), numWeights(numWeights), numFrames(numFrames), frameLength(frameLength) {
|
||||
}
|
||||
|
||||
int frameIndex = -1;
|
||||
int numFrames;
|
||||
float frameLength;
|
||||
time_point_t lastTime;
|
||||
|
||||
float* frameData;
|
||||
int numWeights;
|
||||
};
|
||||
|
||||
struct EmbeddedAnimationBuffer {
|
||||
|
||||
EmbeddedAnimationBuffer(int animationIndex, float duration) : animationIndex(animationIndex), duration(duration) {}
|
||||
bool hasStarted = false;
|
||||
int animationIndex;
|
||||
float duration = 0;
|
||||
time_point_t lastTime;
|
||||
};
|
||||
|
||||
using LoadResource = std::function<ResourceBuffer(const char* uri)>;
|
||||
using FreeResource = std::function<void * (void *mem, size_t s, void *)>;
|
||||
@@ -88,7 +114,14 @@ namespace mimetic {
|
||||
void loadResources(std::string relativeResourcePath);
|
||||
void transformToUnitCube();
|
||||
void cleanup();
|
||||
void animateWeightsInternal(float* data, int numWeights, int length, float frameRate);
|
||||
void updateMorphAnimation();
|
||||
void updateEmbeddedAnimation();
|
||||
|
||||
// animation flags;
|
||||
bool isAnimating;
|
||||
unique_ptr<MorphAnimationBuffer> morphAnimationBuffer;
|
||||
unique_ptr<EmbeddedAnimationBuffer> embeddedAnimationBuffer;
|
||||
|
||||
void* _layer;
|
||||
|
||||
LoadResource _loadResource;
|
||||
@@ -96,10 +129,6 @@ namespace mimetic {
|
||||
|
||||
ResourceBuffer materialProviderResources;
|
||||
|
||||
std::chrono::high_resolution_clock::time_point startTime;
|
||||
|
||||
int _activeAnimation = -1;
|
||||
|
||||
Scene* _scene;
|
||||
View* _view;
|
||||
Engine* _engine;
|
||||
@@ -114,7 +143,6 @@ namespace mimetic {
|
||||
FilamentAsset* _asset = nullptr;
|
||||
NameComponentManager* _ncm;
|
||||
|
||||
|
||||
Entity _sun;
|
||||
Texture* _skyboxTexture;
|
||||
Skybox* _skybox;
|
||||
@@ -131,6 +159,7 @@ namespace mimetic {
|
||||
float _cameraFocalLength = 0.0f;
|
||||
|
||||
GPUMorphHelper* morphHelper;
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
@@ -57,12 +57,14 @@ namespace gltfio {
|
||||
cgltf_mesh const *mesh = node->mesh;
|
||||
|
||||
if (mesh) {
|
||||
std::cout << "Mesh " << mesh->name << " with " << mesh->weights_count << " weights " << std::endl;
|
||||
std::cout << "Mesh " << mesh->name << " with " << mesh->weights_count << " weights and " << mesh->primitives_count << " primitives." << std::endl;
|
||||
if(strcmp(meshName, mesh->name) == 0) {
|
||||
targetMesh = mesh;
|
||||
std::cout << "Adding primitive to mesh with " << mesh->primitives_count << " primitives." << std::endl;
|
||||
for(int i = 0; i < numPrimitives; i++)
|
||||
addPrimitive(mesh, primitiveIndices[i]);
|
||||
for(int i = 0; i < numPrimitives; i++) {
|
||||
int primitiveIndex = primitiveIndices[i];
|
||||
std::cout << "Adding primitive at index " << primitiveIndex << " to morpher " << std::endl;
|
||||
addPrimitive(mesh, primitiveIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ abstract class FilamentController {
|
||||
Future playAnimation(int index);
|
||||
|
||||
// Weights is expected to be a contiguous sequence of floats of size W*F, where W is the number of weights and F is the number of frames
|
||||
void animate(List<double> weights, int numWeights, double frameRate);
|
||||
Future animate(List<double> weights, int numWeights, double frameRate);
|
||||
Future createMorpher(String meshName, List<int> primitives);
|
||||
Future zoom(double z);
|
||||
}
|
||||
@@ -102,8 +102,9 @@ class MimeticFilamentController extends FilamentController {
|
||||
return result;
|
||||
}
|
||||
|
||||
void animate(List<double> weights, int numWeights, double frameRate) async {
|
||||
_channel.invokeMethod("animateWeights", [weights, numWeights, frameRate]);
|
||||
Future animate(List<double> weights, int numWeights, double frameRate) async {
|
||||
await _channel
|
||||
.invokeMethod("animateWeights", [weights, numWeights, frameRate]);
|
||||
}
|
||||
|
||||
Future releaseSourceAssets() async {
|
||||
|
||||
@@ -4,8 +4,10 @@ import 'package:mimetic_filament/view/filament_widget.dart';
|
||||
|
||||
class GestureDetectingFilamentView extends StatefulWidget {
|
||||
final FilamentController controller;
|
||||
final bool rotate;
|
||||
|
||||
const GestureDetectingFilamentView({Key? key, required this.controller})
|
||||
const GestureDetectingFilamentView(
|
||||
{Key? key, required this.controller, this.rotate = false})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
@@ -14,7 +16,6 @@ class GestureDetectingFilamentView extends StatefulWidget {
|
||||
|
||||
class _GestureDetectingFilamentViewState
|
||||
extends State<GestureDetectingFilamentView> {
|
||||
bool _rotate = false;
|
||||
int _primitiveIndex = 0;
|
||||
double _weight = 0.0;
|
||||
|
||||
@@ -23,21 +24,23 @@ class _GestureDetectingFilamentViewState
|
||||
return GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onPanDown: (details) {
|
||||
_rotate
|
||||
widget.rotate
|
||||
? widget.controller.rotateStart(
|
||||
details.localPosition.dx, details.localPosition.dy)
|
||||
: widget.controller
|
||||
.panStart(details.localPosition.dx, details.localPosition.dy);
|
||||
},
|
||||
onPanUpdate: (details) {
|
||||
_rotate
|
||||
widget.rotate
|
||||
? widget.controller.rotateUpdate(
|
||||
details.localPosition.dx, details.localPosition.dy)
|
||||
: widget.controller.panUpdate(
|
||||
details.localPosition.dx, details.localPosition.dy);
|
||||
},
|
||||
onPanEnd: (d) {
|
||||
_rotate ? widget.controller.rotateEnd() : widget.controller.panEnd();
|
||||
widget.rotate
|
||||
? widget.controller.rotateEnd()
|
||||
: widget.controller.panEnd();
|
||||
},
|
||||
child: FilamentWidget(controller: widget.controller));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user