restructure render loop to correct timings and expose FFI methods for setBoneTransform/setMorphWeights
This commit is contained in:
@@ -92,6 +92,9 @@ namespace polyvox
|
|||||||
bool hide(EntityId entity, const char *meshName);
|
bool hide(EntityId entity, const char *meshName);
|
||||||
bool reveal(EntityId entity, const char *meshName);
|
bool reveal(EntityId entity, const char *meshName);
|
||||||
const char *getNameForEntity(EntityId entityId);
|
const char *getNameForEntity(EntityId entityId);
|
||||||
|
utils::Entity findChildEntityByName(
|
||||||
|
EntityId entityId,
|
||||||
|
const char *entityName);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
AssetLoader *_assetLoader = nullptr;
|
AssetLoader *_assetLoader = nullptr;
|
||||||
@@ -108,6 +111,8 @@ namespace polyvox
|
|||||||
vector<SceneAsset> _assets;
|
vector<SceneAsset> _assets;
|
||||||
tsl::robin_map<EntityId, int> _entityIdLookup;
|
tsl::robin_map<EntityId, int> _entityIdLookup;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
utils::Entity findEntityByName(
|
utils::Entity findEntityByName(
|
||||||
SceneAsset asset,
|
SceneAsset asset,
|
||||||
const char *entityName);
|
const char *entityName);
|
||||||
|
|||||||
@@ -168,6 +168,7 @@ extern "C"
|
|||||||
FLUTTER_PLUGIN_EXPORT void set_post_processing(void *const viewer, bool enabled);
|
FLUTTER_PLUGIN_EXPORT void set_post_processing(void *const viewer, bool enabled);
|
||||||
FLUTTER_PLUGIN_EXPORT void pick(void *const viewer, int x, int y, EntityId *entityId);
|
FLUTTER_PLUGIN_EXPORT void pick(void *const viewer, int x, int y, EntityId *entityId);
|
||||||
FLUTTER_PLUGIN_EXPORT const char *get_name_for_entity(void *const assetManager, const EntityId entityId);
|
FLUTTER_PLUGIN_EXPORT const char *get_name_for_entity(void *const assetManager, const EntityId entityId);
|
||||||
|
FLUTTER_PLUGIN_EXPORT const EntityId find_child_entity_by_name(void *const assetManager, const EntityId parent, const char* name);
|
||||||
FLUTTER_PLUGIN_EXPORT void ios_dummy();
|
FLUTTER_PLUGIN_EXPORT void ios_dummy();
|
||||||
FLUTTER_PLUGIN_EXPORT void flutter_filament_free(void *ptr);
|
FLUTTER_PLUGIN_EXPORT void flutter_filament_free(void *ptr);
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|||||||
@@ -58,6 +58,18 @@ FLUTTER_PLUGIN_EXPORT int get_animation_count_ffi(void* const assetManager, Enti
|
|||||||
FLUTTER_PLUGIN_EXPORT void get_animation_name_ffi(void* const assetManager, EntityId asset, char *const outPtr, int index);
|
FLUTTER_PLUGIN_EXPORT void get_animation_name_ffi(void* const assetManager, EntityId asset, char *const outPtr, int index);
|
||||||
FLUTTER_PLUGIN_EXPORT void get_morph_target_name_ffi(void* const assetManager, EntityId asset, const char *meshName, char *const outPtr, int index);
|
FLUTTER_PLUGIN_EXPORT void get_morph_target_name_ffi(void* const assetManager, EntityId asset, const char *meshName, char *const outPtr, int index);
|
||||||
FLUTTER_PLUGIN_EXPORT int get_morph_target_name_count_ffi(void* const assetManager, EntityId asset, const char *meshName);
|
FLUTTER_PLUGIN_EXPORT int get_morph_target_name_count_ffi(void* const assetManager, EntityId asset, const char *meshName);
|
||||||
|
FLUTTER_PLUGIN_EXPORT void set_morph_target_weights_ffi(void* const assetManager,
|
||||||
|
EntityId asset,
|
||||||
|
const char *const entityName,
|
||||||
|
const float *const morphData,
|
||||||
|
int numWeights
|
||||||
|
);
|
||||||
|
FLUTTER_PLUGIN_EXPORT bool set_bone_transform_ffi(
|
||||||
|
void *assetManager,
|
||||||
|
EntityId asset,
|
||||||
|
const char *entityName,
|
||||||
|
const float *const transform,
|
||||||
|
const char *boneName);
|
||||||
FLUTTER_PLUGIN_EXPORT void set_post_processing_ffi(void* const viewer, bool enabled);
|
FLUTTER_PLUGIN_EXPORT void set_post_processing_ffi(void* const viewer, bool enabled);
|
||||||
FLUTTER_PLUGIN_EXPORT void pick_ffi(void* const viewer, int x, int y, EntityId* entityId);
|
FLUTTER_PLUGIN_EXPORT void pick_ffi(void* const viewer, int x, int y, EntityId* entityId);
|
||||||
FLUTTER_PLUGIN_EXPORT void ios_dummy_ffi();
|
FLUTTER_PLUGIN_EXPORT void ios_dummy_ffi();
|
||||||
|
|||||||
@@ -280,16 +280,6 @@ namespace polyvox
|
|||||||
return _assets[pos->second].asset;
|
return _assets[pos->second].asset;
|
||||||
}
|
}
|
||||||
|
|
||||||
// vector<int> completedBoneAnimations;
|
|
||||||
|
|
||||||
// for (int i = completed.size() - 1; i >= 0; i--) {
|
|
||||||
// auto completedAnimationIndex = completed[i];
|
|
||||||
// if(asset.animations.mType == AnimationType::BONE) {
|
|
||||||
// completedBoneAnimations.push_back()
|
|
||||||
// }
|
|
||||||
// (completedAnimationIndex);
|
|
||||||
// }
|
|
||||||
|
|
||||||
void AssetManager::updateAnimations()
|
void AssetManager::updateAnimations()
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -390,10 +380,13 @@ namespace polyvox
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO - we really don't want to be looking up the bone index/entity by name every single frame
|
||||||
|
// - could use findChildEntityByName
|
||||||
|
// - or is it better to add an option for "streaming" mode where we can just return a reference to a mat4 and then update the values directly?
|
||||||
bool AssetManager::setBoneTransform(EntityId entityId, const char *entityName, int32_t skinIndex, const char* boneName, math::mat4f localTransform)
|
bool AssetManager::setBoneTransform(EntityId entityId, const char *entityName, int32_t skinIndex, const char* boneName, math::mat4f localTransform)
|
||||||
{
|
{
|
||||||
|
|
||||||
Log("Setting transform for bone %s/skin %d for mesh target %s", boneName, skinIndex, entityName);
|
std::lock_guard lock(_animationMutex);
|
||||||
|
|
||||||
const auto &pos = _entityIdLookup.find(entityId);
|
const auto &pos = _entityIdLookup.find(entityId);
|
||||||
if (pos == _entityIdLookup.end())
|
if (pos == _entityIdLookup.end())
|
||||||
@@ -414,6 +407,11 @@ namespace polyvox
|
|||||||
|
|
||||||
const auto &renderableInstance = rm.getInstance(entity);
|
const auto &renderableInstance = rm.getInstance(entity);
|
||||||
|
|
||||||
|
if(!renderableInstance.isValid()) {
|
||||||
|
Log("Invalid renderable");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
TransformManager &transformManager = _engine->getTransformManager();
|
TransformManager &transformManager = _engine->getTransformManager();
|
||||||
|
|
||||||
const auto &filamentInstance = sceneAsset.asset->getInstance();
|
const auto &filamentInstance = sceneAsset.asset->getInstance();
|
||||||
@@ -442,6 +440,7 @@ namespace polyvox
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
utils::Entity joint = filamentInstance->getJointsAt(skinIndex)[boneIndex];
|
utils::Entity joint = filamentInstance->getJointsAt(skinIndex)[boneIndex];
|
||||||
|
|
||||||
if (joint.isNull())
|
if (joint.isNull())
|
||||||
@@ -577,6 +576,28 @@ namespace polyvox
|
|||||||
count);
|
count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
utils::Entity AssetManager::findChildEntityByName(EntityId entityId, const char *entityName) {
|
||||||
|
std::lock_guard lock(_animationMutex);
|
||||||
|
|
||||||
|
const auto &pos = _entityIdLookup.find(entityId);
|
||||||
|
if (pos == _entityIdLookup.end())
|
||||||
|
{
|
||||||
|
Log("Couldn't find asset under specified entity id.");
|
||||||
|
return utils::Entity();
|
||||||
|
}
|
||||||
|
SceneAsset &sceneAsset = _assets[pos->second];
|
||||||
|
|
||||||
|
const auto entity = findEntityByName(sceneAsset, entityName);
|
||||||
|
|
||||||
|
if(entity.isNull()) {
|
||||||
|
Log("Failed to find entity %s.", entityName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return entity;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
utils::Entity AssetManager::findEntityByName(SceneAsset asset, const char *entityName)
|
utils::Entity AssetManager::findEntityByName(SceneAsset asset, const char *entityName)
|
||||||
{
|
{
|
||||||
utils::Entity entity;
|
utils::Entity entity;
|
||||||
|
|||||||
@@ -962,6 +962,7 @@ namespace polyvox
|
|||||||
|
|
||||||
double _elapsed = 0;
|
double _elapsed = 0;
|
||||||
int _frameCount = 0;
|
int _frameCount = 0;
|
||||||
|
int _skippedFrames = 0;
|
||||||
|
|
||||||
void FilamentViewer::render(
|
void FilamentViewer::render(
|
||||||
uint64_t frameTimeInNanos,
|
uint64_t frameTimeInNanos,
|
||||||
@@ -981,6 +982,7 @@ namespace polyvox
|
|||||||
// Log("1 sec average for asset animation update %f", _elapsed / 60);
|
// Log("1 sec average for asset animation update %f", _elapsed / 60);
|
||||||
_elapsed = 0;
|
_elapsed = 0;
|
||||||
_frameCount = 0;
|
_frameCount = 0;
|
||||||
|
Log("Skipped frames : %d", _skippedFrames);
|
||||||
}
|
}
|
||||||
|
|
||||||
Timer tmr;
|
Timer tmr;
|
||||||
@@ -1023,6 +1025,8 @@ namespace polyvox
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
_skippedFrames++;
|
||||||
|
|
||||||
// std::cout << "Skipped" << std::endl;
|
// std::cout << "Skipped" << std::endl;
|
||||||
// skipped frame
|
// skipped frame
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,24 +16,36 @@ class RenderLoop {
|
|||||||
public:
|
public:
|
||||||
explicit RenderLoop() {
|
explicit RenderLoop() {
|
||||||
_t = new std::thread([this]() {
|
_t = new std::thread([this]() {
|
||||||
|
auto last = std::chrono::high_resolution_clock::now();
|
||||||
while (!_stop) {
|
while (!_stop) {
|
||||||
{
|
|
||||||
if (_rendering) {
|
auto now = std::chrono::high_resolution_clock::now();
|
||||||
doRender();
|
|
||||||
}
|
float elapsed = float(std::chrono::duration_cast<std::chrono::milliseconds>(now - last).count());
|
||||||
}
|
|
||||||
|
while(elapsed < 3 * _frameIntervalInMilliseconds / 4) {
|
||||||
|
|
||||||
std::function<void()> task;
|
std::function<void()> task;
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> lock(_access);
|
std::unique_lock<std::mutex> lock(_access);
|
||||||
if (_tasks.empty()) {
|
if (_tasks.empty()) {
|
||||||
_cond.wait_for(lock, std::chrono::duration<float, std::milli>(
|
_cond.wait_for(lock, std::chrono::duration<float, std::milli>(1));
|
||||||
_frameIntervalInMilliseconds));
|
now = std::chrono::high_resolution_clock::now();
|
||||||
|
elapsed = float(std::chrono::duration_cast<std::chrono::milliseconds>(now - last).count());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
task = std::move(_tasks.front());
|
task = std::move(_tasks.front());
|
||||||
_tasks.pop_front();
|
_tasks.pop_front();
|
||||||
}
|
|
||||||
task();
|
task();
|
||||||
|
|
||||||
|
now = std::chrono::high_resolution_clock::now();
|
||||||
|
elapsed = float(std::chrono::duration_cast<std::chrono::milliseconds>(now - last).count());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_rendering) {
|
||||||
|
doRender();
|
||||||
|
}
|
||||||
|
|
||||||
|
last = now;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -428,5 +440,29 @@ get_name_for_entity_ffi(void *const assetManager, const EntityId entityId) {
|
|||||||
return fut.get();
|
return fut.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void set_morph_target_weights_ffi(void *const assetManager,
|
||||||
|
EntityId asset,
|
||||||
|
const char *const entityName,
|
||||||
|
const float *const morphData,
|
||||||
|
int numWeights) {
|
||||||
|
std::packaged_task<void()> lambda(
|
||||||
|
[&] { return set_morph_target_weights(assetManager, asset, entityName, morphData, numWeights); });
|
||||||
|
auto fut = _rl->add_task(lambda);
|
||||||
|
fut.wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
FLUTTER_PLUGIN_EXPORT bool set_bone_transform_ffi(
|
||||||
|
void *assetManager,
|
||||||
|
EntityId asset,
|
||||||
|
const char *entityName,
|
||||||
|
const float *const transform,
|
||||||
|
const char *boneName) {
|
||||||
|
std::packaged_task<bool()> lambda(
|
||||||
|
[&] { return set_bone_transform(assetManager, asset, entityName, transform, boneName); });
|
||||||
|
auto fut = _rl->add_task(lambda);
|
||||||
|
fut.wait();
|
||||||
|
return fut.get();
|
||||||
|
}
|
||||||
|
|
||||||
FLUTTER_PLUGIN_EXPORT void ios_dummy_ffi() { Log("Dummy called"); }
|
FLUTTER_PLUGIN_EXPORT void ios_dummy_ffi() { Log("Dummy called"); }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -485,4 +485,11 @@ abstract class FilamentController {
|
|||||||
double orbitSpeedX = 0.01,
|
double orbitSpeedX = 0.01,
|
||||||
double orbitSpeedY = 0.01,
|
double orbitSpeedY = 0.01,
|
||||||
double zoomSpeed = 0.01});
|
double zoomSpeed = 0.01});
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Finds the child entity named [childName] associated with the given parent.
|
||||||
|
/// Usually, [parent] will be the return value from [loadGlb]/[loadGltf] and [childName] will be the name of a node/mesh.
|
||||||
|
///
|
||||||
|
Future<FilamentEntity> getChildEntity(
|
||||||
|
FilamentEntity parent, String childName);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -125,7 +125,9 @@ class FilamentControllerFFI extends FilamentController {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Future setFrameRate(int framerate) async {
|
Future setFrameRate(int framerate) async {
|
||||||
set_frame_interval_ffi(1.0 / framerate);
|
final interval = 1000.0 / framerate;
|
||||||
|
set_frame_interval_ffi(interval);
|
||||||
|
dev.log("Set frame interval to $interval");
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -596,7 +598,7 @@ class FilamentControllerFFI extends FilamentController {
|
|||||||
weightsPtr.elementAt(i).value = weights[i];
|
weightsPtr.elementAt(i).value = weights[i];
|
||||||
}
|
}
|
||||||
var meshNamePtr = meshName.toNativeUtf8(allocator: calloc).cast<Char>();
|
var meshNamePtr = meshName.toNativeUtf8(allocator: calloc).cast<Char>();
|
||||||
set_morph_target_weights(
|
set_morph_target_weights_ffi(
|
||||||
_assetManager!, entity, meshNamePtr, weightsPtr, weights.length);
|
_assetManager!, entity, meshNamePtr, weightsPtr, weights.length);
|
||||||
calloc.free(weightsPtr);
|
calloc.free(weightsPtr);
|
||||||
calloc.free(meshNamePtr);
|
calloc.free(meshNamePtr);
|
||||||
@@ -1178,7 +1180,7 @@ class FilamentControllerFFI extends FilamentController {
|
|||||||
var meshNamePtr = meshName.toNativeUtf8(allocator: calloc).cast<Char>();
|
var meshNamePtr = meshName.toNativeUtf8(allocator: calloc).cast<Char>();
|
||||||
var boneNamePtr = boneName.toNativeUtf8(allocator: calloc).cast<Char>();
|
var boneNamePtr = boneName.toNativeUtf8(allocator: calloc).cast<Char>();
|
||||||
|
|
||||||
var result = set_bone_transform(
|
var result = set_bone_transform_ffi(
|
||||||
_assetManager!, entity, meshNamePtr, ptr, boneNamePtr);
|
_assetManager!, entity, meshNamePtr, ptr, boneNamePtr);
|
||||||
|
|
||||||
calloc.free(ptr);
|
calloc.free(ptr);
|
||||||
@@ -1188,4 +1190,21 @@ class FilamentControllerFFI extends FilamentController {
|
|||||||
throw Exception("Failed to set bone transform. See logs for details");
|
throw Exception("Failed to set bone transform. See logs for details");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<FilamentEntity> getChildEntity(
|
||||||
|
FilamentEntity parent, String childName) async {
|
||||||
|
var childNamePtr = childName.toNativeUtf8(allocator: calloc).cast<Char>();
|
||||||
|
try {
|
||||||
|
var childEntity =
|
||||||
|
find_child_entity_by_name(_assetManager!, parent, childNamePtr);
|
||||||
|
if (childEntity == _FILAMENT_ASSET_ERROR) {
|
||||||
|
throw Exception(
|
||||||
|
"Could not find child ${childName} under the specified entity");
|
||||||
|
}
|
||||||
|
return childEntity;
|
||||||
|
} finally {
|
||||||
|
calloc.free(childNamePtr);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -736,6 +736,16 @@ external ffi.Pointer<ffi.Char> get_name_for_entity(
|
|||||||
int entityId,
|
int entityId,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ffi.Native<
|
||||||
|
EntityId Function(
|
||||||
|
ffi.Pointer<ffi.Void>, EntityId, ffi.Pointer<ffi.Char>)>(
|
||||||
|
symbol: 'find_child_entity_by_name', assetId: 'flutter_filament_plugin')
|
||||||
|
external int find_child_entity_by_name(
|
||||||
|
ffi.Pointer<ffi.Void> assetManager,
|
||||||
|
int parent,
|
||||||
|
ffi.Pointer<ffi.Char> name,
|
||||||
|
);
|
||||||
|
|
||||||
@ffi.Native<ffi.Void Function()>(
|
@ffi.Native<ffi.Void Function()>(
|
||||||
symbol: 'ios_dummy', assetId: 'flutter_filament_plugin')
|
symbol: 'ios_dummy', assetId: 'flutter_filament_plugin')
|
||||||
external void ios_dummy();
|
external void ios_dummy();
|
||||||
@@ -1092,6 +1102,34 @@ external int get_morph_target_name_count_ffi(
|
|||||||
ffi.Pointer<ffi.Char> meshName,
|
ffi.Pointer<ffi.Char> meshName,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ffi.Native<
|
||||||
|
ffi.Void Function(ffi.Pointer<ffi.Void>, EntityId,
|
||||||
|
ffi.Pointer<ffi.Char>, ffi.Pointer<ffi.Float>, ffi.Int)>(
|
||||||
|
symbol: 'set_morph_target_weights_ffi', assetId: 'flutter_filament_plugin')
|
||||||
|
external void set_morph_target_weights_ffi(
|
||||||
|
ffi.Pointer<ffi.Void> assetManager,
|
||||||
|
int asset,
|
||||||
|
ffi.Pointer<ffi.Char> entityName,
|
||||||
|
ffi.Pointer<ffi.Float> morphData,
|
||||||
|
int numWeights,
|
||||||
|
);
|
||||||
|
|
||||||
|
@ffi.Native<
|
||||||
|
ffi.Bool Function(
|
||||||
|
ffi.Pointer<ffi.Void>,
|
||||||
|
EntityId,
|
||||||
|
ffi.Pointer<ffi.Char>,
|
||||||
|
ffi.Pointer<ffi.Float>,
|
||||||
|
ffi.Pointer<ffi.Char>)>(
|
||||||
|
symbol: 'set_bone_transform_ffi', assetId: 'flutter_filament_plugin')
|
||||||
|
external bool set_bone_transform_ffi(
|
||||||
|
ffi.Pointer<ffi.Void> assetManager,
|
||||||
|
int asset,
|
||||||
|
ffi.Pointer<ffi.Char> entityName,
|
||||||
|
ffi.Pointer<ffi.Float> transform,
|
||||||
|
ffi.Pointer<ffi.Char> boneName,
|
||||||
|
);
|
||||||
|
|
||||||
@ffi.Native<ffi.Void Function(ffi.Pointer<ffi.Void>, ffi.Bool)>(
|
@ffi.Native<ffi.Void Function(ffi.Pointer<ffi.Void>, ffi.Bool)>(
|
||||||
symbol: 'set_post_processing_ffi', assetId: 'flutter_filament_plugin')
|
symbol: 'set_post_processing_ffi', assetId: 'flutter_filament_plugin')
|
||||||
external void set_post_processing_ffi(
|
external void set_post_processing_ffi(
|
||||||
|
|||||||
Reference in New Issue
Block a user