merge in changes from web/js branch by hand (bone animation updates)

This commit is contained in:
Nick Fisher
2024-02-02 17:16:25 +08:00
parent d2fc342bea
commit 17e4014b3e
9 changed files with 500 additions and 150 deletions

View File

@@ -83,7 +83,9 @@ namespace polyvox
const char *const boneName, const char *const boneName,
const char **const meshName, const char **const meshName,
int numMeshTargets, int numMeshTargets,
float frameLengthInMs); float frameLengthInMs,
bool isModelSpace);
void resetBones(EntityId entityId);
void playAnimation(EntityId e, int index, bool loop, bool reverse, bool replaceActive, float crossfade = 0.3f); void playAnimation(EntityId e, int index, bool loop, bool reverse, bool replaceActive, float crossfade = 0.3f);
void stopAnimation(EntityId e, int index); void stopAnimation(EntityId e, int index);
void setMorphTargetWeights(const char *const entityName, float *weights, int count); void setMorphTargetWeights(const char *const entityName, float *weights, int count);
@@ -95,6 +97,8 @@ namespace polyvox
utils::Entity findChildEntityByName( utils::Entity findChildEntityByName(
EntityId entityId, EntityId entityId,
const char *entityName); const char *entityName);
int getEntityCount(EntityId entity, bool renderableOnly);
const char* getEntityNameAt(EntityId entity, int index, bool renderableOnly);
private: private:
AssetLoader *_assetLoader = nullptr; AssetLoader *_assetLoader = nullptr;
@@ -117,11 +121,5 @@ namespace polyvox
inline void updateTransform(SceneAsset &asset); inline void updateTransform(SceneAsset &asset);
void updateBoneTransformFromAnimationBuffer(
const BoneAnimation& animation,
int frameNumber,
FilamentAsset *asset
);
}; };
} }

View File

@@ -67,6 +67,8 @@ namespace polyvox
void loadIbl(const char *const iblUri, float intensity); void loadIbl(const char *const iblUri, float intensity);
void removeIbl(); void removeIbl();
void rotateIbl(const math::mat3f & matrix);
void removeAsset(EntityId asset); void removeAsset(EntityId asset);
void clearAssets(); void clearAssets();

View File

@@ -40,7 +40,7 @@
#endif /* __STDBOOL_H */ #endif /* __STDBOOL_H */
#ifdef __APPLE__ #if defined(__APPLE__) || defined(__EMSCRIPTEN__)
#include <stddef.h> #include <stddef.h>
#endif #endif
@@ -67,6 +67,7 @@ extern "C"
FLUTTER_PLUGIN_EXPORT void set_bloom(const void *const viewer, float strength); FLUTTER_PLUGIN_EXPORT void set_bloom(const void *const viewer, float strength);
FLUTTER_PLUGIN_EXPORT void load_skybox(const void *const viewer, const char *skyboxPath); FLUTTER_PLUGIN_EXPORT void load_skybox(const void *const viewer, const char *skyboxPath);
FLUTTER_PLUGIN_EXPORT void load_ibl(const void *const viewer, const char *iblPath, float intensity); FLUTTER_PLUGIN_EXPORT void load_ibl(const void *const viewer, const char *iblPath, float intensity);
FLUTTER_PLUGIN_EXPORT void rotate_ibl(const void *const viewer, float* rotationMatrix);
FLUTTER_PLUGIN_EXPORT void remove_skybox(const void *const viewer); FLUTTER_PLUGIN_EXPORT void remove_skybox(const void *const viewer);
FLUTTER_PLUGIN_EXPORT void remove_ibl(const void *const viewer); FLUTTER_PLUGIN_EXPORT void remove_ibl(const void *const viewer);
FLUTTER_PLUGIN_EXPORT EntityId add_light(const void *const viewer, uint8_t type, float colour, float intensity, float posX, float posY, float posZ, float dirX, float dirY, float dirZ, bool shadows); FLUTTER_PLUGIN_EXPORT EntityId add_light(const void *const viewer, uint8_t type, float colour, float intensity, float posX, float posY, float posZ, float dirX, float dirY, float dirZ, bool shadows);
@@ -113,6 +114,10 @@ extern "C"
int numMorphTargets, int numMorphTargets,
int numFrames, int numFrames,
float frameLengthInMs); float frameLengthInMs);
FLUTTER_PLUGIN_EXPORT void reset_to_rest_pose(
void *assetManager,
EntityId asset);
FLUTTER_PLUGIN_EXPORT void add_bone_animation( FLUTTER_PLUGIN_EXPORT void add_bone_animation(
void *assetManager, void *assetManager,
EntityId asset, EntityId asset,
@@ -121,7 +126,8 @@ extern "C"
const char *const boneName, const char *const boneName,
const char **const meshNames, const char **const meshNames,
int numMeshTargets, int numMeshTargets,
float frameLengthInMs); float frameLengthInMs,
bool isModelSpace);
FLUTTER_PLUGIN_EXPORT bool set_bone_transform( FLUTTER_PLUGIN_EXPORT bool set_bone_transform(
void *assetManager, void *assetManager,
EntityId asset, EntityId asset,
@@ -171,6 +177,8 @@ extern "C"
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 const EntityId find_child_entity_by_name(void *const assetManager, const EntityId parent, const char* name);
FLUTTER_PLUGIN_EXPORT int get_entity_count(void *const assetManager, const EntityId target, bool renderableOnly);
FLUTTER_PLUGIN_EXPORT const char* get_entity_name_at(void *const assetManager, const EntityId target, int index, bool renderableOnly);
FLUTTER_PLUGIN_EXPORT void set_recording(void *const viewer, bool recording); FLUTTER_PLUGIN_EXPORT void set_recording(void *const viewer, bool recording);
FLUTTER_PLUGIN_EXPORT void set_recording_output_directory(void *const viewer, const char* outputDirectory); FLUTTER_PLUGIN_EXPORT void set_recording_output_directory(void *const viewer, const char* outputDirectory);
FLUTTER_PLUGIN_EXPORT void ios_dummy(); FLUTTER_PLUGIN_EXPORT void ios_dummy();

View File

@@ -64,14 +64,34 @@ FLUTTER_PLUGIN_EXPORT void set_morph_target_weights_ffi(void* const assetManager
const float *const morphData, const float *const morphData,
int numWeights int numWeights
); );
FLUTTER_PLUGIN_EXPORT bool set_morph_animation_ffi(
void *assetManager,
EntityId asset,
const char *const entityName,
const float *const morphData,
const int *const morphIndices,
int numMorphTargets,
int numFrames,
float frameLengthInMs);
FLUTTER_PLUGIN_EXPORT bool set_bone_transform_ffi( FLUTTER_PLUGIN_EXPORT bool set_bone_transform_ffi(
void *assetManager, void *assetManager,
EntityId asset, EntityId asset,
const char *entityName, const char *entityName,
const float *const transform, const float *const transform,
const char *boneName); const char *boneName);
FLUTTER_PLUGIN_EXPORT void add_bone_animation_ffi(
void *assetManager,
EntityId asset,
const float *const frameData,
int numFrames,
const char *const boneName,
const char **const meshNames,
int numMeshTargets,
float frameLengthInMs,
bool isModelSpace);
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 reset_to_rest_pose_ffi(void* const assetManager, EntityId entityId);
FLUTTER_PLUGIN_EXPORT void ios_dummy_ffi(); FLUTTER_PLUGIN_EXPORT void ios_dummy_ffi();
#ifdef __cplusplus #ifdef __cplusplus

View File

@@ -62,17 +62,17 @@ namespace polyvox {
// Use this to construct a dynamic (i.e. non-glTF embedded) bone/joint animation. // Use this to construct a dynamic (i.e. non-glTF embedded) bone/joint animation.
// //
struct BoneAnimation : AnimationStatus { struct BoneAnimation : AnimationStatus {
uint8_t boneIndex; size_t boneIndex;
vector<utils::Entity> meshTargets; vector<utils::Entity> meshTargets;
size_t skinIndex = 0; size_t skinIndex = 0;
int lengthInFrames; int lengthInFrames;
float frameLengthInMs = 0; float frameLengthInMs = 0;
vector<math::quatf> frameData; vector<math::mat4f> frameData;
}; };
struct SceneAsset { struct SceneAsset {
FilamentAsset* asset = nullptr; FilamentAsset* asset = nullptr;
vector<math::mat4f> initialJointTransforms;
vector<GltfAnimation> gltfAnimations; vector<GltfAnimation> gltfAnimations;
vector<MorphAnimation> morphAnimations; vector<MorphAnimation> morphAnimations;
vector<BoneAnimation> boneAnimations; vector<BoneAnimation> boneAnimations;

View File

@@ -77,7 +77,7 @@ namespace polyvox
EntityManager &em = EntityManager::get(); EntityManager &em = EntityManager::get();
_assetLoader = AssetLoader::create({_engine, _ubershaderProvider, _ncm, &em}); _assetLoader = AssetLoader::create({_engine, _ubershaderProvider, _ncm, &em});
_gltfResourceLoader->addTextureProvider("image/ktx2", _ktxDecoder); _gltfResourceLoader->addTextureProvider ("image/ktx2", _ktxDecoder);
_gltfResourceLoader->addTextureProvider("image/png", _stbDecoder); _gltfResourceLoader->addTextureProvider("image/png", _stbDecoder);
_gltfResourceLoader->addTextureProvider("image/jpeg", _stbDecoder); _gltfResourceLoader->addTextureProvider("image/jpeg", _stbDecoder);
} }
@@ -121,6 +121,19 @@ namespace polyvox
_gltfResourceLoader->addResourceData(resourceUris[i], std::move(b)); _gltfResourceLoader->addResourceData(resourceUris[i], std::move(b));
} }
#ifdef __EMSCRIPTEN__
if (!_gltfResourceLoader->asyncBeginLoad(asset)) {
Log("Unknown error loading glTF asset");
_resourceLoaderWrapper->free(rbuf);
for(auto& rb : resourceBuffers) {
_resourceLoaderWrapper->free(rb);
}
return 0;
}
while(_gltfResourceLoader->asyncGetLoadProgress() < 1.0f) {
_gltfResourceLoader->asyncUpdateLoad();
}
#else
// load resources synchronously // load resources synchronously
if (!_gltfResourceLoader->loadResources(asset)) if (!_gltfResourceLoader->loadResources(asset))
{ {
@@ -132,6 +145,7 @@ namespace polyvox
} }
return 0; return 0;
} }
#endif
_scene->addEntities(asset->getEntities(), asset->getEntityCount()); _scene->addEntities(asset->getEntities(), asset->getEntityCount());
@@ -143,6 +157,17 @@ namespace polyvox
SceneAsset sceneAsset(asset); SceneAsset sceneAsset(asset);
const auto joints = inst->getJointsAt(0);
TransformManager &transformManager = _engine->getTransformManager();
for(int i = 0; i < inst->getJointCountAt(0); i++) {
const auto joint = joints[i];
const auto& jointTransformInstance = transformManager.getInstance(joint);
const auto& jointTransform = transformManager.getTransform(jointTransformInstance);
sceneAsset.initialJointTransforms.push_back(jointTransform);
}
utils::Entity e = EntityManager::get().create(); utils::Entity e = EntityManager::get().create();
EntityId eid = Entity::smuggle(e); EntityId eid = Entity::smuggle(e);
@@ -166,7 +191,7 @@ namespace polyvox
ResourceBuffer rbuf = _resourceLoaderWrapper->load(uri); ResourceBuffer rbuf = _resourceLoaderWrapper->load(uri);
Log("Loaded GLB of size %d at URI %s", rbuf.size, uri); Log("Loaded GLB data (%d bytes) from URI %s", rbuf.size, uri);
FilamentAsset *asset = _assetLoader->createAsset( FilamentAsset *asset = _assetLoader->createAsset(
(const uint8_t *)rbuf.data, rbuf.size); (const uint8_t *)rbuf.data, rbuf.size);
@@ -181,12 +206,23 @@ namespace polyvox
_scene->addEntities(asset->getEntities(), entityCount); _scene->addEntities(asset->getEntities(), entityCount);
#ifdef __EMSCRIPTEN__
if (!_gltfResourceLoader->asyncBeginLoad(asset)) {
Log("Unknown error loading glb asset");
_resourceLoaderWrapper->free(rbuf);
return 0;
}
while(_gltfResourceLoader->asyncGetLoadProgress() < 1.0f) {
_gltfResourceLoader->asyncUpdateLoad();
}
#else
if (!_gltfResourceLoader->loadResources(asset)) if (!_gltfResourceLoader->loadResources(asset))
{ {
Log("Unknown error loading glb asset"); Log("Unknown error loading glb asset");
_resourceLoaderWrapper->free(rbuf); _resourceLoaderWrapper->free(rbuf);
return 0; return 0;
} }
#endif
auto lights = asset->getLightEntities(); auto lights = asset->getLightEntities();
_scene->addEntities(lights, asset->getLightEntityCount()); _scene->addEntities(lights, asset->getLightEntityCount());
@@ -203,6 +239,17 @@ namespace polyvox
SceneAsset sceneAsset(asset); SceneAsset sceneAsset(asset);
const auto joints = inst->getJointsAt(0);
TransformManager &transformManager = _engine->getTransformManager();
for(int i = 0; i < inst->getJointCountAt(0); i++) {
const auto joint = joints[i];
const auto& jointTransformInstance = transformManager.getInstance(joint);
const auto& jointTransform = transformManager.getTransform(jointTransformInstance);
sceneAsset.initialJointTransforms.push_back(jointTransform);
}
utils::Entity e = EntityManager::get().create(); utils::Entity e = EntityManager::get().create();
EntityId eid = Entity::smuggle(e); EntityId eid = Entity::smuggle(e);
@@ -278,7 +325,6 @@ namespace polyvox
void AssetManager::updateAnimations() void AssetManager::updateAnimations()
{ {
std::lock_guard lock(_animationMutex); std::lock_guard lock(_animationMutex);
RenderableManager &rm = _engine->getRenderableManager(); RenderableManager &rm = _engine->getRenderableManager();
@@ -287,7 +333,7 @@ namespace polyvox
for (auto &asset : _assets) for (auto &asset : _assets)
{ {
for (int i = asset.gltfAnimations.size() - 1; i >= 0; i--) { for (int i = ((int)asset.gltfAnimations.size()) - 1; i >= 0; i--) {
auto animationStatus = asset.gltfAnimations[i]; auto animationStatus = asset.gltfAnimations[i];
@@ -295,12 +341,15 @@ namespace polyvox
if (!animationStatus.loop && elapsedInSecs >= animationStatus.durationInSecs) if (!animationStatus.loop && elapsedInSecs >= animationStatus.durationInSecs)
{ {
asset.asset->getInstance()->getAnimator()->applyAnimation(animationStatus.index, animationStatus.durationInSecs - 0.001);
asset.asset->getInstance()->getAnimator()->updateBoneMatrices();
asset.gltfAnimations.erase(asset.gltfAnimations.begin() + i); asset.gltfAnimations.erase(asset.gltfAnimations.begin() + i);
asset.fadeGltfAnimationIndex = -1; asset.fadeGltfAnimationIndex = -1;
continue; continue;
} }
asset.asset->getInstance()->getAnimator()->applyAnimation(animationStatus.index, elapsedInSecs); asset.asset->getInstance()->getAnimator()->applyAnimation(animationStatus.index, elapsedInSecs);
if (asset.fadeGltfAnimationIndex != -1 && elapsedInSecs < asset.fadeDuration) if (asset.fadeGltfAnimationIndex != -1 && elapsedInSecs < asset.fadeDuration)
{ {
// cross-fade // cross-fade
@@ -354,19 +403,45 @@ namespace polyvox
continue; continue;
} }
float frameLengthInMs = animationStatus.frameLengthInMs; float elapsedFrames = elapsedInSecs * 1000.0f / animationStatus.frameLengthInMs;
int frameNumber = static_cast<int>(elapsedInSecs * 1000.0f / frameLengthInMs) % animationStatus.lengthInFrames; int currFrame = static_cast<int>(elapsedFrames) % animationStatus.lengthInFrames;
float delta = elapsedFrames - currFrame;
int nextFrame = currFrame;
auto restLocalTransform = asset.initialJointTransforms[animationStatus.boneIndex];
// offset from the end if reverse // offset from the end if reverse
if (animationStatus.reverse) if (animationStatus.reverse)
{ {
frameNumber = animationStatus.lengthInFrames - frameNumber; currFrame = animationStatus.lengthInFrames - currFrame;
if(currFrame > 0) {
nextFrame = currFrame - 1;
} else {
nextFrame = 0;
} }
updateBoneTransformFromAnimationBuffer( } else {
animationStatus, if(currFrame < animationStatus.lengthInFrames - 1) {
frameNumber, nextFrame = currFrame + 1;
asset.asset); } else {
nextFrame = currFrame;
}
}
// simple linear interpolation
math::mat4f curr = (1 - delta) * (restLocalTransform * animationStatus.frameData[currFrame]);
math::mat4f next = delta * (restLocalTransform * animationStatus.frameData[nextFrame]);
math::mat4f localTransform = curr + next;
auto filamentInstance = asset.asset->getInstance();
TransformManager &transformManager = _engine->getTransformManager();
const Entity joint = filamentInstance->getJointsAt(animationStatus.skinIndex)[animationStatus.boneIndex];
auto jointTransform = transformManager.getInstance(joint);
transformManager.setTransform(jointTransform, localTransform);
asset.asset->getInstance()->getAnimator()->updateBoneMatrices();
if (animationStatus.loop && elapsedInSecs >= animationStatus.durationInSecs) if (animationStatus.loop && elapsedInSecs >= animationStatus.durationInSecs)
{ {
@@ -381,7 +456,6 @@ namespace polyvox
// - 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? // - 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)
{ {
std::lock_guard lock(_animationMutex); std::lock_guard lock(_animationMutex);
const auto &pos = _entityIdLookup.find(entityId); const auto &pos = _entityIdLookup.find(entityId);
@@ -436,7 +510,6 @@ 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())
@@ -447,8 +520,8 @@ namespace polyvox
const auto& inverseBindMatrix = filamentInstance->getInverseBindMatricesAt(skinIndex)[boneIndex]; const auto& inverseBindMatrix = filamentInstance->getInverseBindMatricesAt(skinIndex)[boneIndex];
auto jointTransformInstance = transformManager.getInstance(joint); auto jointTransform = transformManager.getInstance(joint);
auto globalJointTransform = transformManager.getWorldTransform(jointTransformInstance); auto globalJointTransform = transformManager.getWorldTransform(jointTransform);
auto inverseGlobalTransform = inverse( auto inverseGlobalTransform = inverse(
transformManager.getWorldTransform( transformManager.getWorldTransform(
@@ -467,47 +540,6 @@ namespace polyvox
return true; return true;
} }
void AssetManager::updateBoneTransformFromAnimationBuffer(
const BoneAnimation& animation,
int frameNumber,
FilamentAsset *asset)
{
auto filamentInstance = asset->getInstance();
TransformManager &transformManager = _engine->getTransformManager();
RenderableManager &rm = _engine->getRenderableManager();
auto boneIndex = animation.boneIndex;
math::mat4f localTransform(animation.frameData[frameNumber]);
const auto& inverseBindMatrix = filamentInstance->getInverseBindMatricesAt(animation.skinIndex)[boneIndex];
for(const auto& meshTarget : animation.meshTargets) {
const Entity joint = filamentInstance->getJointsAt(animation.skinIndex)[animation.boneIndex];
auto jointInstance = transformManager.getInstance(joint);
auto globalJointTransform = transformManager.getWorldTransform(jointInstance);
auto inverseGlobalTransform = inverse(
transformManager.getWorldTransform(
transformManager.getInstance(meshTarget)
)
);
const auto boneTransform = inverseGlobalTransform * globalJointTransform * localTransform * inverseBindMatrix;
const auto &renderableInstance = rm.getInstance(meshTarget);
rm.setBones(
renderableInstance,
&boneTransform,
1,
boneIndex
);
}
}
void AssetManager::remove(EntityId entityId) void AssetManager::remove(EntityId entityId)
{ {
std::lock_guard lock(_animationMutex); std::lock_guard lock(_animationMutex);
@@ -716,13 +748,45 @@ namespace polyvox
return true; return true;
} }
void AssetManager::resetBones(EntityId entityId) {
std::lock_guard lock(_animationMutex);
const auto &pos = _entityIdLookup.find(entityId);
if (pos == _entityIdLookup.end())
{
Log("ERROR: asset not found for entity.");
return;
}
auto &asset = _assets[pos->second];
auto filamentInstance = asset.asset->getInstance();
filamentInstance->getAnimator()->resetBoneMatrices();
auto skinCount = filamentInstance->getSkinCount();
TransformManager &transformManager = _engine->getTransformManager();
for(int skinIndex = 0; skinIndex < skinCount; skinIndex++) {
for(int i =0; i < filamentInstance->getJointCountAt(skinIndex);i++) {
const Entity joint = filamentInstance->getJointsAt(skinIndex)[i];
auto restLocalTransform = asset.initialJointTransforms[i];
auto jointTransform = transformManager.getInstance(joint);
transformManager.setTransform(jointTransform, restLocalTransform);
}
}
filamentInstance->getAnimator()->updateBoneMatrices();
filamentInstance->getAnimator()->resetBoneMatrices();
}
bool AssetManager::addBoneAnimation(EntityId entityId, bool AssetManager::addBoneAnimation(EntityId entityId,
const float *const frameData, const float *const frameData,
int numFrames, int numFrames,
const char *const boneName, const char *const boneName,
const char **const meshNames, const char **const meshNames,
int numMeshTargets, int numMeshTargets,
float frameLengthInMs) float frameLengthInMs,
bool isModelSpace)
{ {
std::lock_guard lock(_animationMutex); std::lock_guard lock(_animationMutex);
@@ -734,8 +798,6 @@ namespace polyvox
} }
auto &asset = _assets[pos->second]; auto &asset = _assets[pos->second];
asset.asset->getInstance()->getAnimator()->resetBoneMatrices();
auto filamentInstance = asset.asset->getInstance(); auto filamentInstance = asset.asset->getInstance();
size_t skinCount = filamentInstance->getSkinCount(); size_t skinCount = filamentInstance->getSkinCount();
@@ -768,14 +830,50 @@ namespace polyvox
} }
animation.frameData.clear(); animation.frameData.clear();
const auto& tm = _engine->getTransformManager();
const auto& inverseBindMatrix = filamentInstance->getInverseBindMatricesAt(skinIndex)[animation.boneIndex];
const auto& bindMatrix = inverse(inverseBindMatrix);
math::float3 trans;
math::quatf rot;
math::float3 scale;
decomposeMatrix(inverseBindMatrix, &trans, &rot, &scale);
math::float3 btrans;
math::quatf brot;
math::float3 bscale;
decomposeMatrix(bindMatrix, &btrans, &brot, &bscale);
// Log("Bind matrix for bone %s is \n%f %f %f %f\n%f %f %f %f\n%f %f %f %f\n%f %f %f %f\n", boneName, bindMatrix[0][0],bindMatrix[1][0],bindMatrix[2][0],bindMatrix[3][0],
// bindMatrix[0][1],bindMatrix[1][1],bindMatrix[2][1],bindMatrix[3][1],
// bindMatrix[0][2],bindMatrix[1][2],bindMatrix[2][2],bindMatrix[3][2],
// bindMatrix[0][3],bindMatrix[1][3],bindMatrix[2][3],bindMatrix[3][3]);
for(int i = 0; i < numFrames; i++) { for(int i = 0; i < numFrames; i++) {
animation.frameData.push_back(math::quatf( math::mat4f frame(
frameData[i*4], frameData[i*16],
frameData[(i*4)+1], frameData[(i*16)+1],
frameData[(i*4)+2], frameData[(i*16)+2],
frameData[(i*4)+3] frameData[(i*16)+3],
)); frameData[(i*16)+4],
frameData[(i*16)+5],
frameData[(i*16)+6],
frameData[(i*16)+7],
frameData[(i*16)+8],
frameData[(i*16)+9],
frameData[(i*16)+10],
frameData[(i*16)+11],
frameData[(i*16)+12],
frameData[(i*16)+13],
frameData[(i*16)+14],
frameData[(i*16)+15]);
// if(i == numFrames - 1) { Log("Model transform for bone %s is \n%f %f %f %f\n%f %f %f %f\n%f %f %f %f\n%f %f %f %f\n", boneName,
// frame[0][0],frame[1][0],frame[2][0],frame[3][0],
// frame[0][1],frame[1][1],frame[2][1],frame[3][1],
// frame[0][2],frame[1][2],frame[2][2],frame[3][2],
// frame[0][3],frame[1][3],frame[2][3],frame[3][3]);
// }
if(isModelSpace) {
frame = (math::mat4f(rot) * frame) * math::mat4f(brot);
}
animation.frameData.push_back(frame);
} }
animation.frameLengthInMs = frameLengthInMs; animation.frameLengthInMs = frameLengthInMs;
@@ -789,7 +887,6 @@ namespace polyvox
Log("Mesh target %s for bone animation could not be found", meshNames[i]); Log("Mesh target %s for bone animation could not be found", meshNames[i]);
return false; return false;
} }
Log("Added mesh target %s", meshNames[i]);
animation.meshTargets.push_back(entity); animation.meshTargets.push_back(entity);
} }
@@ -798,6 +895,7 @@ namespace polyvox
animation.durationInSecs = (frameLengthInMs * numFrames) / 1000.0f; animation.durationInSecs = (frameLengthInMs * numFrames) / 1000.0f;
animation.lengthInFrames = numFrames; animation.lengthInFrames = numFrames;
animation.frameLengthInMs = frameLengthInMs; animation.frameLengthInMs = frameLengthInMs;
animation.skinIndex = 0;
asset.boneAnimations.push_back(animation); asset.boneAnimations.push_back(animation);
return true; return true;
@@ -1172,4 +1270,63 @@ namespace polyvox
return _ncm->getName(nameInstance); return _ncm->getName(nameInstance);
} }
int AssetManager::getEntityCount(EntityId entityId, bool renderableOnly) {
const auto &pos = _entityIdLookup.find(entityId);
if (pos == _entityIdLookup.end())
{
Log("ERROR: asset not found for entity.");
return 0;
}
auto &asset = _assets[pos->second];
if(renderableOnly) {
int count = 0;
const auto& rm = _engine->getRenderableManager();
const Entity *entities = asset.asset->getEntities();
for(int i=0; i < asset.asset->getEntityCount(); i++) {
if(rm.hasComponent(entities[i])) {
count++;
}
}
return count;
}
return asset.asset->getEntityCount();
}
const char* AssetManager::getEntityNameAt(EntityId entityId, int index, bool renderableOnly) {
const auto &pos = _entityIdLookup.find(entityId);
if (pos == _entityIdLookup.end())
{
Log("ERROR: asset not found for entity.");
return nullptr;
}
auto &asset = _assets[pos->second];
int found = -1;
if(renderableOnly) {
int count = 0;
const auto& rm = _engine->getRenderableManager();
const Entity *entities = asset.asset->getEntities();
for(int i=0; i < asset.asset->getEntityCount(); i++) {
if(rm.hasComponent(entities[i])) {
if(count == index) {
found = i;
break;
}
count++;
}
}
} else {
found = index;
}
if(found >= asset.asset->getEntityCount()) {
Log("ERROR: index %d greater than number of child entities.", found);
return nullptr;
}
const utils::Entity entity = asset.asset->getEntities()[found];
auto inst = _ncm->getInstance(entity);
return _ncm->getName(inst);
}
} // namespace polyvox } // namespace polyvox

View File

@@ -25,6 +25,9 @@
#include <backend/DriverEnums.h> #include <backend/DriverEnums.h>
#include <backend/platforms/OpenGLPlatform.h> #include <backend/platforms/OpenGLPlatform.h>
#ifdef __EMSCRIPTEN__
#include <backend/platforms/PlatformWebGL.h>
#endif
#include <filament/ColorGrading.h> #include <filament/ColorGrading.h>
#include <filament/Engine.h> #include <filament/Engine.h>
#include <filament/IndexBuffer.h> #include <filament/IndexBuffer.h>
@@ -131,6 +134,8 @@ namespace polyvox
#elif TARGET_OS_OSX #elif TARGET_OS_OSX
ASSERT_POSTCONDITION(platform == nullptr, "Custom Platform not supported on macOS"); ASSERT_POSTCONDITION(platform == nullptr, "Custom Platform not supported on macOS");
_engine = Engine::create(Engine::Backend::METAL); _engine = Engine::create(Engine::Backend::METAL);
#elif defined(__EMSCRIPTEN__)
_engine = Engine::create(Engine::Backend::OPENGL, (backend::Platform *)new filament::backend::PlatformWebGL(), (void *)sharedContext, nullptr);
#else #else
_engine = Engine::create(Engine::Backend::OPENGL, (backend::Platform *)platform, (void *)sharedContext, nullptr); _engine = Engine::create(Engine::Backend::OPENGL, (backend::Platform *)platform, (void *)sharedContext, nullptr);
#endif #endif
@@ -154,11 +159,27 @@ namespace polyvox
Log("View created"); Log("View created");
setToneMapping(ToneMapping::ACES); setToneMapping(ToneMapping::ACES);
Log("Set tone mapping"); Log("Set tone mapping");
#ifdef __EMSCRIPTEN__
Log("Bloom is disabled on WebGL builds as it causes instability with certain drivers");
decltype(_view->getBloomOptions()) opts;
opts.enabled = false;
_view->setBloomOptions(opts);
_view->setAmbientOcclusionOptions({.enabled=false});
_view->setDynamicResolutionOptions({.enabled=false});
_view->setDithering(filament::Dithering::NONE);
_view->setAntiAliasing(filament::AntiAliasing::NONE);
_view->setShadowingEnabled(false);
_view->setScreenSpaceRefractionEnabled(false);
#else
setBloom(0.6f); setBloom(0.6f);
Log("Set bloom"); Log("Set bloom");
#endif
_view->setScene(_scene); _view->setScene(_scene);
_view->setCamera(_mainCamera); _view->setCamera(_mainCamera);
@@ -215,7 +236,7 @@ namespace polyvox
.package(IMAGE_IMAGE_DATA, IMAGE_IMAGE_SIZE) .package(IMAGE_IMAGE_DATA, IMAGE_IMAGE_SIZE)
.build(*_engine); .build(*_engine);
_imageMaterial->setDefaultParameter("showImage", 0); _imageMaterial->setDefaultParameter("showImage", 0);
_imageMaterial->setDefaultParameter("backgroundColor", RgbaType::sRGB, float4(0.5f, 0.5f, 0.5f, 1.0f)); _imageMaterial->setDefaultParameter("backgroundColor", RgbaType::sRGB, float4(1.0f, 1.0f, 1.0f, 0.0f));
_imageMaterial->setDefaultParameter("image", _imageTexture, _imageSampler); _imageMaterial->setDefaultParameter("image", _imageTexture, _imageSampler);
} }
catch (...) catch (...)
@@ -265,10 +286,14 @@ namespace polyvox
void FilamentViewer::setBloom(float strength) void FilamentViewer::setBloom(float strength)
{ {
#ifdef __EMSCRIPTEN__
Log("Bloom is disabled on WebGL builds as it causes instability with certain drivers. setBloom will be ignored");
#else
decltype(_view->getBloomOptions()) opts; decltype(_view->getBloomOptions()) opts;
opts.enabled = true; opts.enabled = true;
opts.strength = strength; opts.strength = strength;
_view->setBloomOptions(opts); _view->setBloomOptions(opts);
#endif
} }
void FilamentViewer::setToneMapping(ToneMapping toneMapping) void FilamentViewer::setToneMapping(ToneMapping toneMapping)
@@ -481,6 +506,7 @@ namespace polyvox
void FilamentViewer::setBackgroundColor(const float r, const float g, const float b, const float a) void FilamentViewer::setBackgroundColor(const float r, const float g, const float b, const float a)
{ {
Log("Setting background color to rgba(%f,%f,%f,%f)", r, g, b, a);
_imageMaterial->setDefaultParameter("showImage", 0); _imageMaterial->setDefaultParameter("showImage", 0);
_imageMaterial->setDefaultParameter("backgroundColor", RgbaType::sRGB, float4(r, g, b, a)); _imageMaterial->setDefaultParameter("backgroundColor", RgbaType::sRGB, float4(r, g, b, a));
_imageMaterial->setDefaultParameter("transform", _imageScale); _imageMaterial->setDefaultParameter("transform", _imageScale);
@@ -926,6 +952,11 @@ namespace polyvox
_scene->setIndirectLight(nullptr); _scene->setIndirectLight(nullptr);
} }
void FilamentViewer::rotateIbl(const math::mat3f & matrix) {
_indirectLight->setRotation(matrix);
}
void FilamentViewer::loadIbl(const char *const iblPath, float intensity) void FilamentViewer::loadIbl(const char *const iblPath, float intensity)
{ {
removeIbl(); removeIbl();
@@ -994,9 +1025,10 @@ namespace polyvox
if (_frameCount == 60) if (_frameCount == 60)
{ {
// Log("1 sec average for asset animation update %f", _elapsed / 60); // Log("1 sec average for asset animation update %f", _elapsed / 60);
Log("Skipped frames : %d", _skippedFrames);
_elapsed = 0; _elapsed = 0;
_frameCount = 0; _frameCount = 0;
Log("Skipped frames : %d", _skippedFrames); _skippedFrames = 0;
} }
Timer tmr; Timer tmr;
@@ -1007,16 +1039,33 @@ namespace polyvox
_frameCount++; _frameCount++;
// if a manipulator is active, update the active camera orientation // if a manipulator is active, update the active camera orientation
if (_manipulator) if(_manipulator) {
{
math::double3 eye, target, upward; math::double3 eye, target, upward;
Camera &cam = _view->getCamera(); Camera& cam =_view->getCamera();
_manipulator->getLookAt(&eye, &target, &upward); _manipulator->getLookAt(&eye, &target, &upward);
cam.lookAt(eye, target, upward); cam.lookAt(eye, target, upward);
} }
// // TODO - this was an experiment but probably useful to keep for debugging
// // if pixelBuffer is provided, we will copy the framebuffer into the pixelBuffer.
// if (pixelBuffer)
// {
// auto pbd = Texture::PixelBufferDescriptor(
// pixelBuffer, size_t(1024 * 768 * 4),
// Texture::Format::RGBA,
// Texture::Type::BYTE, nullptr, callback, data);
// _renderer->beginFrame(_swapChain, 0);
// _renderer->render(_view);
// _renderer->readPixels(0, 0, 1024, 768, std::move(pbd));
// _renderer->endFrame();
// }
// else
// {
// Render the scene, unless the renderer wants to skip the frame. // Render the scene, unless the renderer wants to skip the frame.
if (_renderer->beginFrame(_swapChain, frameTimeInNanos)) bool beginFrame = _renderer->beginFrame(_swapChain, frameTimeInNanos);
if (beginFrame)
{ {
_renderer->render(_view); _renderer->render(_view);
@@ -1045,15 +1094,16 @@ namespace polyvox
_renderer->readPixels(_rt, 0, 0, vp.width, vp.height, std::move(pbd)); _renderer->readPixels(_rt, 0, 0, vp.width, vp.height, std::move(pbd));
} }
_renderer->endFrame(); _renderer->endFrame();
} #ifdef __EMSCRIPTEN__
else _engine->execute();
{ #endif
} else {
_skippedFrames++; _skippedFrames++;
} }
} }
void FilamentViewer::savePng(void* buf, size_t size, int frameNumber) { void FilamentViewer::savePng(void* buf, size_t size, int frameNumber) {
std::lock_guard lock(_recordingMutex); std::lock_guard lock(_recordingMutex);
if(!_recording) { if(!_recording) {

View File

@@ -76,6 +76,19 @@ extern "C"
((FilamentViewer *)viewer)->loadIbl(iblPath, intensity); ((FilamentViewer *)viewer)->loadIbl(iblPath, intensity);
} }
FLUTTER_PLUGIN_EXPORT void rotate_ibl(const void *const viewer, float* rotationMatrix) {
math::mat3f matrix(rotationMatrix[0], rotationMatrix[1],
rotationMatrix[2],
rotationMatrix[3],
rotationMatrix[4],
rotationMatrix[5],
rotationMatrix[6],
rotationMatrix[7],
rotationMatrix[8]);
((FilamentViewer*)viewer)->rotateIbl(matrix);
}
FLUTTER_PLUGIN_EXPORT void remove_skybox(const void *const viewer) FLUTTER_PLUGIN_EXPORT void remove_skybox(const void *const viewer)
{ {
((FilamentViewer *)viewer)->removeSkybox(); ((FilamentViewer *)viewer)->removeSkybox();
@@ -330,6 +343,10 @@ extern "C"
return ((AssetManager *)assetManager)->setMorphAnimationBuffer(asset, entityName, morphData, morphIndices, numMorphTargets, numFrames, frameLengthInMs); return ((AssetManager *)assetManager)->setMorphAnimationBuffer(asset, entityName, morphData, morphIndices, numMorphTargets, numFrames, frameLengthInMs);
} }
FLUTTER_PLUGIN_EXPORT void reset_to_rest_pose(void *assetManager, EntityId entityId) {
((AssetManager*)assetManager)->resetBones(entityId);
}
FLUTTER_PLUGIN_EXPORT void add_bone_animation( FLUTTER_PLUGIN_EXPORT void add_bone_animation(
void *assetManager, void *assetManager,
EntityId asset, EntityId asset,
@@ -338,9 +355,10 @@ extern "C"
const char *const boneName, const char *const boneName,
const char **const meshNames, const char **const meshNames,
int numMeshTargets, int numMeshTargets,
float frameLengthInMs) float frameLengthInMs,
bool isModelSpace)
{ {
((AssetManager *)assetManager)->addBoneAnimation(asset, frameData, numFrames, boneName, meshNames, numMeshTargets, frameLengthInMs); ((AssetManager *)assetManager)->addBoneAnimation(asset, frameData, numFrames, boneName, meshNames, numMeshTargets, frameLengthInMs, isModelSpace);
} }
FLUTTER_PLUGIN_EXPORT void set_post_processing(void *const viewer, bool enabled) FLUTTER_PLUGIN_EXPORT void set_post_processing(void *const viewer, bool enabled)
@@ -492,6 +510,15 @@ extern "C"
return ((AssetManager *)assetManager)->getNameForEntity(entityId); return ((AssetManager *)assetManager)->getNameForEntity(entityId);
} }
FLUTTER_PLUGIN_EXPORT int get_entity_count(void *const assetManager, const EntityId target, bool renderableOnly) {
return ((AssetManager *)assetManager)->getEntityCount(target, renderableOnly);
}
FLUTTER_PLUGIN_EXPORT const char* get_entity_name_at(void *const assetManager, const EntityId target, int index, bool renderableOnly) {
return ((AssetManager *)assetManager)->getEntityNameAt(target, index, renderableOnly);
}
FLUTTER_PLUGIN_EXPORT void set_recording(void *const viewer, bool recording) { FLUTTER_PLUGIN_EXPORT void set_recording(void *const viewer, bool recording) {
((FilamentViewer*)viewer)->setRecording(recording); ((FilamentViewer*)viewer)->setRecording(recording);
} }

View File

@@ -9,8 +9,31 @@
#include <functional> #include <functional>
#include <mutex> #include <mutex>
#include <thread> #include <thread>
#include <stdlib.h>
#ifdef __EMSCRIPTEN__
#define GL_GLEXT_PROTOTYPES
#include <GL/gl.h>
#include <GL/glext.h>
#include <emscripten/emscripten.h>
#include <emscripten/html5.h>
#include <emscripten/threading.h>
#include <emscripten/val.h>
using namespace polyvox; using namespace polyvox;
using namespace std::chrono_literals;
#include <emscripten/threading.h>
#include <emscripten/val.h>
extern "C"
{
extern FLUTTER_PLUGIN_EXPORT EMSCRIPTEN_WEBGL_CONTEXT_HANDLE flutter_filament_web_create_gl_context();
}
#endif
#include <pthread.h>
class RenderLoop { class RenderLoop {
public: public:
@@ -63,8 +86,20 @@ public:
void (*renderCallback)(void *), void *const owner) { void (*renderCallback)(void *), void *const owner) {
_renderCallback = renderCallback; _renderCallback = renderCallback;
_renderCallbackOwner = owner; _renderCallbackOwner = owner;
std::packaged_task<FilamentViewer *()> lambda([&]() mutable { std::packaged_task<FilamentViewer *()> lambda([=]() mutable {
return new FilamentViewer(context, loader, platform, uberArchivePath); #ifdef __EMSCRIPTEN__
auto emContext = flutter_filament_web_create_gl_context();
auto success = emscripten_webgl_make_context_current((EMSCRIPTEN_WEBGL_CONTEXT_HANDLE)emContext);
if(success != EMSCRIPTEN_RESULT_SUCCESS) {
std::cout << "Failed to make context current." << std::endl;
return (FilamentViewer*)nullptr;
}
_viewer = new FilamentViewer((void* const) emContext, loader, platform, uberArchivePath);
#else
_viewer = new FilamentViewer(context, loader, platform, uberArchivePath);
#endif
return _viewer;
}); });
auto fut = add_task(lambda); auto fut = add_task(lambda);
fut.wait(); fut.wait();
@@ -90,10 +125,17 @@ public:
} }
void doRender() { void doRender() {
auto now = std::chrono::high_resolution_clock::now();
auto nanos = std::chrono::duration_cast<std::chrono::nanoseconds>(now.time_since_epoch()).count();
render(_viewer, 0, nullptr, nullptr, nullptr); render(_viewer, 0, nullptr, nullptr, nullptr);
_lastRenderTime = std::chrono::high_resolution_clock::now();
if(_renderCallback) { if(_renderCallback) {
_renderCallback(_renderCallbackOwner); _renderCallback(_renderCallbackOwner);
} }
#ifdef __EMSCRIPTEN__
emscripten_webgl_commit_frame();
#endif
} }
void setFrameIntervalInMilliseconds(float frameIntervalInMilliseconds) { void setFrameIntervalInMilliseconds(float frameIntervalInMilliseconds) {
@@ -121,6 +163,7 @@ private:
std::thread *_t = nullptr; std::thread *_t = nullptr;
std::condition_variable _cond; std::condition_variable _cond;
std::deque<std::function<void()>> _tasks; std::deque<std::function<void()>> _tasks;
std::chrono::steady_clock::time_point _lastRenderTime = std::chrono::high_resolution_clock::now();
}; };
extern "C" { extern "C" {
@@ -453,6 +496,25 @@ void set_morph_target_weights_ffi(void *const assetManager,
fut.wait(); fut.wait();
} }
bool set_morph_animation_ffi(
void *assetManager,
EntityId asset,
const char *const entityName,
const float *const morphData,
const int *const morphIndices,
int numMorphTargets,
int numFrames,
float frameLengthInMs) {
std::packaged_task<bool()> lambda(
[&] {
return set_morph_animation(assetManager, asset, entityName, morphData, morphIndices, numMorphTargets, numFrames, frameLengthInMs);
});
auto fut = _rl->add_task(lambda);
fut.wait();
return fut.get();
}
FLUTTER_PLUGIN_EXPORT bool set_bone_transform_ffi( FLUTTER_PLUGIN_EXPORT bool set_bone_transform_ffi(
void *assetManager, void *assetManager,
EntityId asset, EntityId asset,
@@ -466,5 +528,31 @@ FLUTTER_PLUGIN_EXPORT bool set_bone_transform_ffi(
return fut.get(); return fut.get();
} }
FLUTTER_PLUGIN_EXPORT void reset_to_rest_pose_ffi(void* const assetManager, EntityId entityId) {
std::packaged_task<void()> lambda(
[&] { return reset_to_rest_pose(assetManager, entityId); });
auto fut = _rl->add_task(lambda);
fut.wait();
}
FLUTTER_PLUGIN_EXPORT void add_bone_animation_ffi(
void *assetManager,
EntityId asset,
const float *const frameData,
int numFrames,
const char *const boneName,
const char **const meshNames,
int numMeshTargets,
float frameLengthInMs,
bool isModelSpace) {
std::packaged_task<void()> lambda(
[=] {
add_bone_animation(assetManager, asset, frameData, numFrames, boneName, meshNames, numMeshTargets, frameLengthInMs, isModelSpace);
});
auto fut = _rl->add_task(lambda);
fut.wait();
}
FLUTTER_PLUGIN_EXPORT void ios_dummy_ffi() { Log("Dummy called"); } FLUTTER_PLUGIN_EXPORT void ios_dummy_ffi() { Log("Dummy called"); }
} }