From b1832c237fec0ae3eed26441e3adc8d3f4f838ee Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Sat, 17 Dec 2022 16:31:20 +0800 Subject: [PATCH] finalize bone transform animation --- example/lib/main.dart | 13 +- ios/include/FilamentViewer.hpp | 2 +- ios/include/PolyvoxFilamentApi.h | 172 ++++++------ ios/include/ResourceManagement.hpp | 27 ++ ios/include/SceneAsset.hpp | 45 ++-- ...eResources.hpp => SceneAssetAnimation.hpp} | 107 ++++---- ios/include/SceneAssetLoader.hpp | 4 +- ios/include/gltfio/FilamentInstance.h | 2 + ios/src/FilamentViewer.cpp | 2 +- ios/src/PolyvoxFilamentApi.cpp | 67 +++-- ios/src/SceneAsset.cpp | 244 +++++++++++------- ios/src/ios/PolyvoxFilamentIOSApi.cpp | 2 +- lib/animations/animation_builder.dart | 106 +++----- lib/animations/animations.dart | 113 ++++---- lib/filament_controller.dart | 39 ++- linux/polyvox_filament_plugin.cc | 110 +++++--- pubspec.yaml | 2 +- 17 files changed, 581 insertions(+), 476 deletions(-) create mode 100644 ios/include/ResourceManagement.hpp rename ios/include/{SceneResources.hpp => SceneAssetAnimation.hpp} (64%) diff --git a/example/lib/main.dart b/example/lib/main.dart index f68167f8..81e85392 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -208,12 +208,13 @@ class _MyAppState extends State { await _filamentController.clearLights(); break; case 32: - await _filamentController.setBoneTransform( - _cube!, - "Bone.001", - "Cube.001", - BoneTransform([Vec3(x: 0, y: 0.0, z: 0.0)], - [Quaternion(x: 1, y: 1, z: 1, w: 1)])); + + // await _filamentController.setBoneTransform( + // _cube!, + // "Bone.001", + // "Cube.001", + // BoneTransform([Vec3(x: 0, y: 0.0, z: 0.0)], + // [Quaternion(x: 1, y: 1, z: 1, w: 1)])); break; } } diff --git a/ios/include/FilamentViewer.hpp b/ios/include/FilamentViewer.hpp index 25590683..66afb8a0 100644 --- a/ios/include/FilamentViewer.hpp +++ b/ios/include/FilamentViewer.hpp @@ -33,7 +33,7 @@ #include "SceneAssetLoader.hpp" #include "SceneAsset.hpp" -#include "SceneResources.hpp" +#include "ResourceManagement.hpp" using namespace std; using namespace filament; diff --git a/ios/include/PolyvoxFilamentApi.h b/ios/include/PolyvoxFilamentApi.h index 71e25a46..4ef25acb 100644 --- a/ios/include/PolyvoxFilamentApi.h +++ b/ios/include/PolyvoxFilamentApi.h @@ -5,91 +5,105 @@ typedef struct ResourceBuffer ResourceBuffer; -//ResourceBuffer create_resource_buffer(const void* data, const uint32_t size, const uint32_t id); -void* filament_viewer_new(void* context, ResourceBuffer (*loadResource)(const char*), void (*freeResource)(uint32_t)); -void* filament_viewer_delete(void* viewer); -void create_render_target(void* viewer, uint32_t textureId, uint32_t width, uint32_t height); -void set_background_image(void* viewer, const char* path); -void set_background_image_position(void* viewer, float x, float y, bool clamp); -void load_skybox(void* viewer, const char* skyboxPath); -void load_ibl(void* viewer, const char* iblPath); -void remove_skybox(void* viewer); -void remove_ibl(void* viewer); -int32_t add_light(void* viewer, uint8_t type, float colour, float intensity, float posX, float posY, float posZ, float dirX, float dirY, float dirZ, bool shadows); -void remove_light(void* viewer, int32_t entityId); -void clear_lights(void* viewer); -void* load_glb(void* viewer, const char* assetPath); -void* load_gltf(void* viewer, const char* assetPath, const char* relativePath); -bool set_camera(void* viewer, void* asset, const char* nodeName); -void render(void* viewer, uint64_t frameTimeInNanos); -void create_swap_chain(void* viewer, void* surface, uint32_t width, uint32_t height); -void destroy_swap_chain(void* viewer); -void set_frame_interval(void* viewer, float interval); -void* get_renderer(void* viewer); -void update_viewport_and_camera_projection(void* viewer, int width, int height, float scaleFactor); - -void scroll_begin(void* viewer); -void scroll_update(void* viewer, float x, float y , float z); -void scroll_end(void* viewer); - -void grab_begin(void* viewer, float x, float y, bool pan); -void grab_update(void* viewer, float x, float y); -void grab_end(void* viewer); +/// +/// A wrapper for a single set of frame-data that may animate multiples bones/mesh nodes. +/// [data] +/// +struct BoneAnimation { + const char** boneNames; + const char** meshNames; + float* data; + size_t numBones; + size_t numMeshTargets; +}; -void apply_weights(void* asset, float* const weights, int count); - -void set_animation( - void* asset, - float* morphData, - int numMorphWeights, - float* boneData, - const char** boneNames, - const char** meshNames, - int numBones, - int numFrames, - float frameLengthInMs -); +typedef struct BoneAnimation BoneAnimation; -void set_bone_transform( - void* asset, - const char* boneName, - const char* entityName, - float transX, - float transY, - float transZ, - float quatX, - float quatY, - float quatZ, - float quatW -); - -void play_animation(void* asset, int index, bool loop, bool reverse); -void stop_animation(void* asset, int index); -int get_animation_count(void* asset); +extern "C" { + void* filament_viewer_new(void* context, ResourceBuffer (*loadResource)(const char*), void (*freeResource)(uint32_t)); + void filament_viewer_delete(void* viewer); + void create_render_target(void* viewer, uint32_t textureId, uint32_t width, uint32_t height); + void set_background_image(void* viewer, const char* path); + void set_background_image_position(void* viewer, float x, float y, bool clamp); + void load_skybox(void* viewer, const char* skyboxPath); + void load_ibl(void* viewer, const char* iblPath); + void remove_skybox(void* viewer); + void remove_ibl(void* viewer); + int32_t add_light(void* viewer, uint8_t type, float colour, float intensity, float posX, float posY, float posZ, float dirX, float dirY, float dirZ, bool shadows); + void remove_light(void* viewer, int32_t entityId); + void clear_lights(void* viewer); + void* load_glb(void* viewer, const char* assetPath); + void* load_gltf(void* viewer, const char* assetPath, const char* relativePath); + bool set_camera(void* viewer, void* asset, const char* nodeName); + void render(void* viewer, uint64_t frameTimeInNanos); + void create_swap_chain(void* viewer, void* surface, uint32_t width, uint32_t height); + void destroy_swap_chain(void* viewer); + void set_frame_interval(void* viewer, float interval); + void* get_renderer(void* viewer); + void update_viewport_and_camera_projection(void* viewer, int width, int height, float scaleFactor); -void get_animation_name(void* asset, char* const outPtr, int index); + void scroll_begin(void* viewer); + void scroll_update(void* viewer, float x, float y , float z); + void scroll_end(void* viewer); + + void grab_begin(void* viewer, float x, float y, bool pan); + void grab_update(void* viewer, float x, float y); + void grab_end(void* viewer); -void get_morph_target_name(void* asset, const char* meshName, char* const outPtr, int index ); + void apply_weights(void* asset, float* const weights, int count); + + void set_animation( + void* asset, + float* morphData, + int numMorphWeights, + BoneAnimation* boneAnimations, + int numBoneAnimations, + int numFrames, + float frameLengthInMs + ); -int get_morph_target_name_count(void* asset, const char* meshName); - -void remove_asset(void* viewer, void* asset); - -void clear_assets(void* viewer); - -void load_texture(void* asset, const char* assetPath, int renderableIndex); -void set_texture(void* asset); - -void transform_to_unit_cube(void* asset); - -void set_position(void* asset, float x, float y, float z); -void set_rotation(void* asset, float rads, float x, float y, float z); -void set_scale(void* asset, float scale); + // void set_bone_transform( + // void* asset, + // const char* boneName, + // const char* entityName, + // float transX, + // float transY, + // float transZ, + // float quatX, + // float quatY, + // float quatZ, + // float quatW + // ); + + void play_animation(void* asset, int index, bool loop, bool reverse); + void stop_animation(void* asset, int index); -void set_camera_position(void* viewer, float x, float y, float z); -void set_camera_rotation(void* viewer, float rads, float x, float y, float z); -void set_camera_focal_length(void* viewer, float focalLength); -void set_camera_focus_distance(void* viewer, float focusDistance); + int get_animation_count(void* asset); + + void get_animation_name(void* asset, char* const outPtr, int index); + + void get_morph_target_name(void* asset, const char* meshName, char* const outPtr, int index ); + + int get_morph_target_name_count(void* asset, const char* meshName); + + void remove_asset(void* viewer, void* asset); + + void clear_assets(void* viewer); + + void load_texture(void* asset, const char* assetPath, int renderableIndex); + void set_texture(void* asset); + + void transform_to_unit_cube(void* asset); + + void set_position(void* asset, float x, float y, float z); + void set_rotation(void* asset, float rads, float x, float y, float z); + void set_scale(void* asset, float scale); + + void set_camera_position(void* viewer, float x, float y, float z); + void set_camera_rotation(void* viewer, float rads, float x, float y, float z); + void set_camera_focal_length(void* viewer, float focalLength); + void set_camera_focus_distance(void* viewer, float focusDistance); +} #endif diff --git a/ios/include/ResourceManagement.hpp b/ios/include/ResourceManagement.hpp new file mode 100644 index 00000000..51fe7f2d --- /dev/null +++ b/ios/include/ResourceManagement.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "ResourceBuffer.hpp" + +namespace polyvox { + + using namespace std; + + // + // Typedef for a function that loads a resource into a ResourceBuffer from an asset URI. + // + using LoadResource = function; + + // + // Typedef for a function that frees an ID associated with a ResourceBuffer. + // + using FreeResource = function; + + +} + diff --git a/ios/include/SceneAsset.hpp b/ios/include/SceneAsset.hpp index c83bf2e7..cc778bbb 100644 --- a/ios/include/SceneAsset.hpp +++ b/ios/include/SceneAsset.hpp @@ -17,14 +17,16 @@ #include -#include "SceneResources.hpp" +#include "ResourceManagement.hpp" +#include "SceneAssetAnimation.hpp" +#include "PolyvoxFilamentApi.h" namespace polyvox { using namespace filament; using namespace filament::gltfio; using namespace utils; - using namespace std; + class SceneAsset { friend class SceneAssetLoader; public: @@ -71,25 +73,16 @@ namespace polyvox { void setAnimation( float* morphData, int numMorphWeights, - float* boneData, - const char** boneNames, - const char** meshNames, - int numBones, + BoneAnimation* targets, + int numBoneAnimations, int numFrames, float frameLengthInMs ); - void setBoneTransform( - const char* boneName, - const char* meshName, - float transX, - float transY, - float transZ, - float quatX, - float quatY, - float quatZ, - float quatW - ); + void fillEntitiesByName(const char** name, int count, vector& out); + size_t getBoneIndex(const char* name); + + Entity getNode(const char* name); void transformToUnitCube(); @@ -97,9 +90,7 @@ namespace polyvox { void setPosition(float x, float y, float z); - void setRotation(float rads, float x, float y, float z); - - + void setRotation(float rads, float x, float y, float z); const utils::Entity* getCameraEntities(); @@ -116,9 +107,17 @@ namespace polyvox { Engine* _engine = nullptr; NameComponentManager* _ncm; - void updateRuntimeAnimation(); - void updateEmbeddedAnimations(); + void setBoneTransform( + uint8_t skinIndex, + const vector& boneIndices, + const vector& targets, + const vector data, + int frameNumber + ); + void updateRuntimeAnimation(); + + void updateEmbeddedAnimations(); Animator* _animator; @@ -143,4 +142,4 @@ namespace polyvox { void updateTransform(); }; -} \ No newline at end of file +} diff --git a/ios/include/SceneResources.hpp b/ios/include/SceneAssetAnimation.hpp similarity index 64% rename from ios/include/SceneResources.hpp rename to ios/include/SceneAssetAnimation.hpp index a86d8c67..71af091a 100644 --- a/ios/include/SceneResources.hpp +++ b/ios/include/SceneAssetAnimation.hpp @@ -1,27 +1,12 @@ -#pragma once +#ifndef SCENE_ASSET_ANIMATION_H_ +#define SCENE_ASSET_ANIMATION_H_ -#include -#include -#include -#include -#include +#include "utils/Entity.h" -#include "ResourceBuffer.hpp" - -namespace polyvox { +namespace polyvox { using namespace std; - // - // Typedef for a function that loads a resource into a ResourceBuffer from an asset URI. - // - using LoadResource = function; - - // - // Typedef for a function that frees an ID associated with a ResourceBuffer. - // - using FreeResource = function; - typedef std::chrono::time_point time_point_t; // @@ -66,48 +51,29 @@ namespace polyvox { }; + /// + /// Holds a single set of frame data that may be used to animate multiple bones/meshes. + /// + struct BoneTransformTarget { + + size_t skinIndex = 0; + unique_ptr> mBoneIndices; + unique_ptr> mMeshTargets; + unique_ptr> mBoneData; + + BoneTransformTarget( + unique_ptr>& boneIndices, + unique_ptr>& meshTargets, + unique_ptr>& boneData) : mBoneIndices(move(boneIndices)), mMeshTargets(move(meshTargets)), mBoneData(move(boneData)) {} + + }; + // // An animation created by manually passing frame data for morph weights/bone transforms. // struct RuntimeAnimation { - - RuntimeAnimation(float* morphData, - int numMorphWeights, - float* boneData, - const char** boneNames, - const char** meshNames, - int numBones, - int numFrames, - float frameLengthInMs) : - mNumFrames(numFrames), - mFrameLengthInMs(frameLengthInMs), - mNumMorphWeights(numMorphWeights), - mNumBones(numBones) { - if(numMorphWeights > 0) { - size_t morphSize = numMorphWeights * mNumFrames * sizeof(float); - mMorphFrameData = (float*)malloc(morphSize); - memcpy(mMorphFrameData, morphData, morphSize); - } - - if(numBones > 0) { - size_t boneSize = numBones * numFrames * 7 * sizeof(float); - mBoneFrameData = (float*)malloc(boneSize); - memcpy(mBoneFrameData, boneData, boneSize); - } - - for(int i =0; i < numBones; i++) { - mBoneNames.push_back(string(boneNames[i])); - mMeshNames.push_back(string(meshNames[i])); - } - } - - ~RuntimeAnimation() { - delete(mMorphFrameData); - delete(mBoneFrameData); - } - - int frameIndex = -1; + int frameNumber = -1; int mNumFrames = -1; float mFrameLengthInMs = 0; time_point_t startTime; @@ -115,13 +81,30 @@ namespace polyvox { float* mMorphFrameData = nullptr; int mNumMorphWeights = 0; - float* mBoneFrameData = nullptr; - int mNumBones = 0; - - vector mBoneNames; - vector mMeshNames; + unique_ptr> mTargets; + + RuntimeAnimation(float* morphData, + int numMorphWeights, + unique_ptr>& targets, + int numFrames, + float frameLengthInMs) : + mNumFrames(numFrames), + mFrameLengthInMs(frameLengthInMs), + mNumMorphWeights(numMorphWeights), + mTargets(move(targets)) { + if(numMorphWeights > 0) { + size_t morphSize = numMorphWeights * mNumFrames * sizeof(float); + mMorphFrameData = (float*)malloc(morphSize); + memcpy(mMorphFrameData, morphData, morphSize); + } + } + + ~RuntimeAnimation() { + delete(mMorphFrameData); + } }; - + } +#endif \ No newline at end of file diff --git a/ios/include/SceneAssetLoader.hpp b/ios/include/SceneAssetLoader.hpp index eeffe134..2b9d2ca2 100644 --- a/ios/include/SceneAssetLoader.hpp +++ b/ios/include/SceneAssetLoader.hpp @@ -6,7 +6,7 @@ #include #include -#include "SceneResources.hpp" +#include "ResourceManagement.hpp" #include "SceneAsset.hpp" #include "ResourceBuffer.hpp" @@ -39,4 +39,4 @@ namespace polyvox { Scene* _scene; }; -} \ No newline at end of file +} diff --git a/ios/include/gltfio/FilamentInstance.h b/ios/include/gltfio/FilamentInstance.h index 3e5da38b..5653a813 100644 --- a/ios/include/gltfio/FilamentInstance.h +++ b/ios/include/gltfio/FilamentInstance.h @@ -126,6 +126,8 @@ public: */ void detachSkin(size_t skinIndex, utils::Entity target) noexcept; + const math::mat4f* getInverseBindMatricesAt(size_t skinIndex) const noexcept; + /** * Resets the AABB on all renderables by manually computing the bounding box. * diff --git a/ios/src/FilamentViewer.cpp b/ios/src/FilamentViewer.cpp index 7b7e191f..d8f743f6 100644 --- a/ios/src/FilamentViewer.cpp +++ b/ios/src/FilamentViewer.cpp @@ -70,7 +70,7 @@ #include #include "Log.hpp" -#include "SceneResources.hpp" +#include "ResourceManagement.hpp" extern "C" { #include "material/image_material.h" diff --git a/ios/src/PolyvoxFilamentApi.cpp b/ios/src/PolyvoxFilamentApi.cpp index 88149988..929143d4 100644 --- a/ios/src/PolyvoxFilamentApi.cpp +++ b/ios/src/PolyvoxFilamentApi.cpp @@ -1,4 +1,5 @@ #include "ResourceBuffer.hpp" + #include "FilamentViewer.hpp" #include "filament/LightManager.h" #include "Log.hpp" @@ -6,9 +7,8 @@ using namespace polyvox; extern "C" { -// ResourceBuffer create_resource_buffer(const void* data, const uint32_t size, const uint32_t id) { -// return ResourceBuffer {data, size, id }; -// } + + #include "PolyvoxFilamentApi.h" void* filament_viewer_new(void* context, ResourceBuffer (*loadResource)(char const*), void (*freeResource)(unsigned int)) { FilamentViewer* v = new FilamentViewer(context, loadResource, freeResource); @@ -148,50 +148,47 @@ extern "C" { void* asset, float* morphData, int numMorphWeights, - float* boneData, - const char** boneNames, - const char** meshNames, - int numBones, + BoneAnimation* boneAnimations, + int numBoneAnimations, int numFrames, float frameLengthInMs) { ((SceneAsset*)asset)->setAnimation( morphData, numMorphWeights, - boneData, - boneNames, - meshNames, - numBones, + boneAnimations, + numBoneAnimations, numFrames, frameLengthInMs ); } - void set_bone_transform( - void* asset, - const char* boneName, - const char* entityName, - float transX, - float transY, - float transZ, - float quatX, - float quatY, - float quatZ, - float quatW -) { - ((SceneAsset*)asset)->setBoneTransform( - boneName, - entityName, - transX, - transY, - transZ, - quatX, - quatY, - quatZ, - quatW - ); +// void set_bone_transform( +// void* asset, +// const char* boneName, +// const char* entityName, +// float transX, +// float transY, +// float transZ, +// float quatX, +// float quatY, +// float quatZ, +// float quatW +// ) { +// ((SceneAsset*)asset)->setBoneTransform( +// boneName, +// entityName, +// transX, +// transY, +// transZ, +// quatX, +// quatY, +// quatZ, +// quatW, +// false +// ); - } +// } void play_animation(void* asset, int index, bool loop, bool reverse) { diff --git a/ios/src/SceneAsset.cpp b/ios/src/SceneAsset.cpp index 7d82422d..a89ebc5d 100644 --- a/ios/src/SceneAsset.cpp +++ b/ios/src/SceneAsset.cpp @@ -16,7 +16,8 @@ #include "StreamBufferAdapter.hpp" #include "SceneAsset.hpp" #include "Log.hpp" -#include "SceneResources.hpp" +#include "ResourceManagement.hpp" +#include "SceneAssetAnimation.hpp" using namespace std::chrono; @@ -58,19 +59,58 @@ void SceneAsset::setMorphTargetWeights(float *weights, int count) { void SceneAsset::setAnimation( float* morphData, int numMorphWeights, - float* boneData, - const char** boneNames, - const char** meshNames, - int numBones, + BoneAnimation* boneAnimations, + int numBoneAnimations, int numFrames, float frameLengthInMs) { + + auto filamentInstance = _asset->getInstance(); + + size_t skinCount = filamentInstance->getSkinCount(); + + if(skinCount > 1) { + Log("WARNING - skin count > 1 not currently implemented. This will probably not work"); + } + + auto transforms = make_unique>(); + + auto numFloats = numFrames * 7; + + for(int i = 0; i < numBoneAnimations; i++) { + + auto boneIndices = make_unique>(); + boneIndices->resize(boneAnimations[i].numBones); + for(int j = 0; j < boneAnimations[i].numBones; j++) { + boneIndices->at(j) = getBoneIndex(boneAnimations[i].boneNames[j]); + } + + auto meshTargets = make_unique>(); + for(int j = 0; j < _asset->getEntityCount(); j++) { + for(int k = 0; k < boneAnimations[i].numMeshTargets;k++) { + auto meshName = boneAnimations[i].meshNames[k]; + auto entity = _asset->getEntities()[j]; + auto nameInstance = _ncm->getInstance(entity); + if(strcmp(meshName,_ncm->getName(nameInstance))==0) { + meshTargets->push_back(entity); + } + } + } + + auto frameData = make_unique>( + boneAnimations[i].data, + boneAnimations[i].data + (numFloats * sizeof(float)) + ); + + transforms->push_back(BoneTransformTarget( + boneIndices, + meshTargets, + frameData + )); + } _runtimeAnimationBuffer = std::make_unique( morphData, numMorphWeights, - boneData, - boneNames, - meshNames, - numBones, + transforms, numFrames, frameLengthInMs ); @@ -87,50 +127,115 @@ void SceneAsset::updateRuntimeAnimation() { return; } - if (_runtimeAnimationBuffer->frameIndex == -1) { + if (_runtimeAnimationBuffer->frameNumber == -1) { _runtimeAnimationBuffer->startTime = high_resolution_clock::now(); } duration dur = high_resolution_clock::now() - _runtimeAnimationBuffer->startTime; - int frameIndex = + int frameNumber = static_cast(dur.count() / _runtimeAnimationBuffer->mFrameLengthInMs); // if the animation has finished, return early - if (frameIndex >= _runtimeAnimationBuffer->mNumFrames) { + if (frameNumber >= _runtimeAnimationBuffer->mNumFrames) { _runtimeAnimationBuffer = nullptr; return; } - if (frameIndex > _runtimeAnimationBuffer->frameIndex) { - _runtimeAnimationBuffer->frameIndex = frameIndex; + if (frameNumber > _runtimeAnimationBuffer->frameNumber) { + _runtimeAnimationBuffer->frameNumber = frameNumber; if(_runtimeAnimationBuffer->mMorphFrameData) { - auto morphFramePtrOffset = frameIndex * _runtimeAnimationBuffer->mNumMorphWeights; + auto morphFramePtrOffset = frameNumber * _runtimeAnimationBuffer->mNumMorphWeights; setMorphTargetWeights(_runtimeAnimationBuffer->mMorphFrameData + morphFramePtrOffset, _runtimeAnimationBuffer->mNumMorphWeights); } - if(_runtimeAnimationBuffer->mBoneFrameData) { - - for(int i = 0; i < _runtimeAnimationBuffer->mNumBones; i++) { - // auto boneFramePtrOffset = (frameIndex * _runtimeAnimationBuffer->mNumBones * 7) + (i*7); - const char* boneName = _runtimeAnimationBuffer->mBoneNames[i].c_str(); - const char* meshName = _runtimeAnimationBuffer->mMeshNames[i].c_str(); - // float* frame = _runtimeAnimationBuffer->mBoneFrameData + boneFramePtrOffset; - - float transX = 0.0f;//frame[0]; - float transY = 0.0f;//frame[1]; - float transZ = 0.0f;//frame[2]; - float quatX = 0.0f; //frame[3]; - float quatY = 0.0f;//frame[4]; - float quatZ = 0.0f; //frame[5]; - float quatW = 1.0;//frame[6]; - setBoneTransform(boneName, meshName, transX, transY, transZ, quatX, quatY, quatZ, quatW); + if(_runtimeAnimationBuffer->mTargets->size() > 0) { + for(auto& target : *(_runtimeAnimationBuffer->mTargets)) { + + setBoneTransform( + target.skinIndex, + *(target.mBoneIndices), + *(target.mMeshTargets), + *(target.mBoneData), + frameNumber + ); } } } } +size_t SceneAsset::getBoneIndex(const char* name) { + + auto filamentInstance = _asset->getInstance(); + + int skinIndex = 0; + const utils::Entity* joints = filamentInstance->getJointsAt(skinIndex); + size_t numJoints = filamentInstance->getJointCountAt(skinIndex); + + int boneIndex = -1; + for(int i =0; i < numJoints; i++) { + const char* jointName = _ncm->getName(_ncm->getInstance(joints[i])); + if(strcmp(jointName, name) == 0) { + boneIndex = i; + break; + } + } + if(boneIndex == -1) { + Log("Failed to find bone index %d for bone %s", name); + } + return boneIndex; +} + +void SceneAsset::setBoneTransform( + uint8_t skinIndex, + const vector& boneIndices, + const vector& targets, + const vector data, + int frameNumber) { + + auto filamentInstance = _asset->getInstance(); + + RenderableManager &rm = _engine->getRenderableManager(); + TransformManager &transformManager = _engine->getTransformManager(); + + auto frameDataOffset = frameNumber * 7; + + for(auto& target : targets) { + + auto renderable = rm.getInstance(target); + + math::mat4f inverseGlobalTransform = inverse( + transformManager.getWorldTransform( + transformManager.getInstance(target) + ) + ); + + for(auto boneIndex : boneIndices) { + + utils::Entity joint = filamentInstance->getJointsAt(skinIndex)[boneIndex]; + + math::mat4f localTransform(math::quatf{ + data[frameDataOffset+6], + data[frameDataOffset+3], + data[frameDataOffset+4], + data[frameDataOffset+5] + }); + + const math::mat4f& inverseBindMatrix = filamentInstance->getInverseBindMatricesAt(skinIndex)[boneIndex]; + auto jointInstance = transformManager.getInstance(joint); + math::mat4f globalJointTransform = transformManager.getWorldTransform(jointInstance); + + math::mat4f boneTransform = inverseGlobalTransform * globalJointTransform * localTransform * inverseBindMatrix; + + rm.setBones( + renderable, + &boneTransform, + 1, boneIndex); + } + } +} + void SceneAsset::playAnimation(int index, bool loop, bool reverse) { if (index > _animator->getAnimationCount() - 1) { Log("Asset does not contain an animation at index %d", index); @@ -353,66 +458,6 @@ void SceneAsset::setRotation(float rads, float x, float y, float z) { updateTransform(); } -void SceneAsset::setBoneTransform( - const char* boneName, - const char* entityName, - float transX, - float transY, - float transZ, - float quatX, - float quatY, - float quatZ, - float quatW) { - - auto filamentInstance = _asset->getInstance(); - - if(filamentInstance->getSkinCount()) { - // Log("WARNING - skin count > 1 not currently implemented. This will probably not work"); - } - - // filamentInstance->getAnimator()->resetBoneMatrices(); - - int skinIndex = 0; - const utils::Entity* joints = filamentInstance->getJointsAt(skinIndex); - size_t numJoints = filamentInstance->getJointCountAt(skinIndex); - - int boneIndex = -1; - for(int i =0; i < numJoints; i++) { - const char* jointName = _ncm->getName(_ncm->getInstance(joints[i])); - if(strcmp(jointName, boneName) == 0) { - boneIndex = i; - // Log("Found bone index %d for bone %s", boneIndex, boneName); - break; - } - } - if(boneIndex == -1) { - Log("Failed to find bone index %d for bone %s", boneName); - return; - } - - RenderableManager &rm = _engine->getRenderableManager(); - - // RenderableManager::Bone transform = { - // .unitQuaternion={quatX,quatY,quatZ,quatW}, - // .translation={transX,transY,transZ} - // }; - - for(int j = 0; j < _asset->getEntityCount(); j++) { - Entity e = _asset->getEntities()[j]; - if(strcmp(entityName,_ncm->getName(_ncm->getInstance(e)))==0) { - - // Log("Setting bone transform on entity %s", _ncm->getName(_ncm->getInstance(e))); - - auto inst = rm.getInstance(e); - if(!inst) { - Log("No renderable instance"); - } - // rm.setBones(inst, &transform, 1, boneIndex); - } - } -} - - const utils::Entity *SceneAsset::getCameraEntities() { return _asset->getCameraEntities(); } @@ -431,3 +476,20 @@ size_t SceneAsset::getLightEntityCount() const noexcept { } // namespace polyvox + + + // const utils::Entity* joints = filamentInstance->getJointsAt(skinIndex); + // size_t numJoints = filamentInstance->getJointCountAt(skinIndex); + // int boneIndex = -1; + // for(int i =0; i < numJoints; i++) { + // const char* jointName = _ncm->getName(_ncm->getInstance(joints[i])); + // if(strcmp(jointName, boneName) == 0) { + // boneIndex = i; + // joint = joints[i]; + // break; + // } + // } + // if(boneIndex == -1) { + // Log("Failed to find bone index %d for bone %s", boneName); + // return; + // } \ No newline at end of file diff --git a/ios/src/ios/PolyvoxFilamentIOSApi.cpp b/ios/src/ios/PolyvoxFilamentIOSApi.cpp index f12bf46f..b3ab7a0f 100644 --- a/ios/src/ios/PolyvoxFilamentIOSApi.cpp +++ b/ios/src/ios/PolyvoxFilamentIOSApi.cpp @@ -1,7 +1,7 @@ #include "PolyvoxFilamentApi.h" #include "FilamentViewer.hpp" #include "ResourceBuffer.hpp" -#include "SceneResources.hpp" +#include "ResourceManagement.hpp" #include using namespace polyvox; diff --git a/lib/animations/animation_builder.dart b/lib/animations/animation_builder.dart index bcdd9283..20f50ae1 100644 --- a/lib/animations/animation_builder.dart +++ b/lib/animations/animation_builder.dart @@ -1,44 +1,7 @@ import 'animations.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; - -class Animation { - late final Float32List? morphData; - final int numMorphWeights; - - final int numFrames; - final double frameLengthInMs; - - final List? boneNames; - final List? meshNames; - - final Float32List? boneTransforms; - - Animation(this.morphData, this.numMorphWeights, this.boneTransforms, - this.boneNames, this.meshNames, this.numFrames, this.frameLengthInMs) { - if (morphData != null && morphData!.length != numFrames * numMorphWeights) { - throw Exception("Mismatched animation data with frame length"); - } - } - - Animation.from( - {required List> morphData, - required this.numMorphWeights, - this.boneTransforms, - this.boneNames, - this.meshNames, - required this.numFrames, - required this.frameLengthInMs}) { - if (morphData.length != numFrames) { - throw Exception("Mismatched animation data with frame length"); - } - this.morphData = Float32List(numMorphWeights * numFrames); - for (int i = 0; i < numFrames; i++) { - this.morphData!.setRange((i * numMorphWeights), - (i * numMorphWeights) + numMorphWeights, morphData[i]); - } - } -} +import 'package:vector_math/vector_math.dart'; class AnimationBuilder { BoneAnimation? boneAnimation; @@ -53,7 +16,7 @@ class AnimationBuilder { final List _boneNames = []; final List _meshNames = []; - final List _boneTransforms = []; + final List _boneTransforms = []; Animation build() { if (_numMorphWeights == 0 || _duration == 0 || _frameLengthInMs == 0) @@ -79,22 +42,31 @@ class AnimationBuilder { print( "Created morphWeights of size ${morphData.length} (${morphData.lengthInBytes} for ${numFrames} frames"); - final boneTransforms = Float32List(numFrames * _boneTransforms.length * 7); - print( - "Creating bone transforms of size ${numFrames * _boneTransforms.length * 7}"); - for (int i = 0; i < numFrames; i++) { - for (int j = 0; j < _boneTransforms.length; j++) { - var frameData = _boneTransforms[j].getFrameData(i).toList(); - var rngStart = ((i * _boneTransforms.length) + j) * 7; - var rngEnd = rngStart + 7; - boneTransforms.setRange(rngStart, rngEnd, frameData); - } - print( - "frameData for frame $i ${boneTransforms.sublist(i * _boneTransforms.length * 7, (i * _boneTransforms.length * 7) + 7)}"); + List? boneAnimations; + + if (_boneTransforms.isNotEmpty) { + throw Exception("TODO"); + boneAnimations = []; + + final boneTransforms = + Float32List(numFrames * _boneTransforms.length * 7); + + // print( + // "Creating bone transforms of size ${numFrames * _boneTransforms.length * 7}"); + // for (int i = 0; i < numFrames; i++) { + // for (int j = 0; j < _boneTransforms.length; j++) { + // var frameData = _boneTransforms[j].getFrameData(i).toList(); + // var rngStart = ((i * _boneTransforms.length) + j) * 7; + // var rngEnd = rngStart + 7; + // boneTransforms.setRange(rngStart, rngEnd, frameData); + // } + // print( + // "frameData for frame $i ${boneTransforms.sublist(i * _boneTransforms.length * 7, (i * _boneTransforms.length * 7) + 7)}"); + // } } - return Animation(morphData, _numMorphWeights, boneTransforms, _boneNames, - _meshNames, numFrames, _frameLengthInMs); + return Animation(morphData, _numMorphWeights, boneAnimations, numFrames, + _frameLengthInMs); } AnimationBuilder setFramerate(int framerate) { @@ -126,11 +98,11 @@ class AnimationBuilder { String meshName, double start, double end, - Vec3 transStart, - Vec3 transEnd, + Vector3 transStart, + Vector3 transEnd, Quaternion quatStart, Quaternion quatEnd) { - var translations = []; + var translations = []; var quats = []; var frameStart = (start * 1000) ~/ _frameLengthInMs; var frameEnd = (end * 1000) ~/ _frameLengthInMs; @@ -143,25 +115,25 @@ class AnimationBuilder { if (i >= frameStart && i < frameEnd) { var linear = (i - frameStart) / (frameEnd - frameStart); - translations.add(Vec3( - x: ((1 - linear) * transStart.x) + (linear * transEnd.x), - y: ((1 - linear) * transStart.y) + (linear * transEnd.y), - z: ((1 - linear) * transStart.z) + (linear * transEnd.z), + translations.add(Vector3( + ((1 - linear) * transStart.x) + (linear * transEnd.x), + ((1 - linear) * transStart.y) + (linear * transEnd.y), + ((1 - linear) * transStart.z) + (linear * transEnd.z), )); quats.add(Quaternion( - x: ((1 - linear) * quatStart.x) + (linear * quatEnd.x), - y: ((1 - linear) * quatStart.y) + (linear * quatEnd.y), - z: ((1 - linear) * quatStart.z) + (linear * quatEnd.z), - w: ((1 - linear) * quatStart.w) + (linear * quatEnd.w), + ((1 - linear) * quatStart.x) + (linear * quatEnd.x), + ((1 - linear) * quatStart.y) + (linear * quatEnd.y), + ((1 - linear) * quatStart.z) + (linear * quatEnd.z), + ((1 - linear) * quatStart.w) + (linear * quatEnd.w), )); } else { - translations.add(Vec3()); - quats.add(Quaternion()); + translations.add(Vector3.zero()); + quats.add(Quaternion.identity()); } } - _boneTransforms.add(BoneTransform(translations, quats)); + _boneTransforms.add(BoneTransformFrameData(translations, quats)); _boneNames.add(boneName); _meshNames.add(meshName); diff --git a/lib/animations/animations.dart b/lib/animations/animations.dart index 402c6143..47f70318 100644 --- a/lib/animations/animations.dart +++ b/lib/animations/animations.dart @@ -1,47 +1,87 @@ -class Vec3 { - final double x; - final double y; - final double z; +import 'dart:typed_data'; - Vec3({this.x = 0, this.y = 0, this.z = 0}); +import 'package:vector_math/vector_math.dart'; - factory Vec3.from(List vals) => - Vec3(x: vals[0], y: vals[1], z: vals[2]); +// class Vec3 { +// final double x; +// final double y; +// final double z; + +// Vec3({this.x = 0, this.y = 0, this.z = 0}); + +// factory Vec3.from(List vals) => +// Vec3(x: vals[0], y: vals[1], z: vals[2]); +// } + +// class Quaternion { +// double x = 0; +// double y = 0; +// double z = 0; +// double w = 1; + +// Quaternion({this.x = 0, this.y = 0, this.z = 0, this.w = 1.0}); + +// factory Quaternion.from(List vals) => +// Quaternion(x: vals[0], y: vals[1], z: vals[2], w: vals[3]); +// } + +class BoneAnimation { + final List boneNames; + final List meshNames; + final Float32List frameData; + + BoneAnimation(this.boneNames, this.meshNames, this.frameData); + + List toList() { + return [boneNames, meshNames, frameData]; + } } -class Quaternion { - double x = 0; - double y = 0; - double z = 0; - double w = 1; +class Animation { + late final Float32List? morphData; + final int numMorphWeights; - Quaternion({this.x = 0, this.y = 0, this.z = 0, this.w = 1.0}); + final int numFrames; + final double frameLengthInMs; - factory Quaternion.from(List vals) => - Quaternion(x: vals[0], y: vals[1], z: vals[2], w: vals[3]); + final List? boneAnimations; + + Animation(this.morphData, this.numMorphWeights, this.boneAnimations, + this.numFrames, this.frameLengthInMs) { + if (morphData != null && morphData!.length != numFrames * numMorphWeights) { + throw Exception("Mismatched animation data with frame length"); + } + } + + Animation.from( + {required List> morphData, + required this.numMorphWeights, + this.boneAnimations, + required this.numFrames, + required this.frameLengthInMs}) { + if (morphData.length != numFrames) { + throw Exception("Mismatched animation data with frame length"); + } + this.morphData = Float32List(numMorphWeights * numFrames); + for (int i = 0; i < numFrames; i++) { + this.morphData!.setRange((i * numMorphWeights), + (i * numMorphWeights) + numMorphWeights, morphData[i]); + } + } } -class BoneTransform { - final List translations; +class BoneTransformFrameData { + final List translations; final List quaternions; /// /// The length of [translations] and [quaternions] must be the same; /// each entry represents the Vec3/Quaternion for the given frame. /// - BoneTransform(this.translations, this.quaternions) { + BoneTransformFrameData(this.translations, this.quaternions) { if (translations.length != quaternions.length) { throw Exception("Length of translation/quaternion frames must match"); } - // for (int i = 0; i < quaternions.length; i++) { - // _frameData.add(translations[i].x); - // _frameData.add(translations[i].y); - // _frameData.add(translations[i].z); - // _frameData.add(quaternions[i].x); - // _frameData.add(quaternions[i].y); - // _frameData.add(quaternions[i].z); - // _frameData.add(quaternions[i].w); - // } } Iterable getFrameData(int frame) sync* { @@ -54,22 +94,3 @@ class BoneTransform { yield quaternions[frame].w; } } - -class BoneAnimation { - final List boneTransforms; - final List boneNames; - final List meshNames; - - final int numFrames; - - BoneAnimation( - this.boneTransforms, this.boneNames, this.meshNames, this.numFrames); - - Iterable toFrameData() sync* { - for (int i = 0; i < numFrames; i++) { - for (int j = 0; j < boneTransforms.length; j++) { - yield* boneTransforms[j].getFrameData(i); - } - } - } -} diff --git a/lib/filament_controller.dart b/lib/filament_controller.dart index e6107d5e..1dbad46c 100644 --- a/lib/filament_controller.dart +++ b/lib/filament_controller.dart @@ -80,8 +80,8 @@ abstract class FilamentController { Future setPosition(FilamentAsset asset, double x, double y, double z); Future setRotation( FilamentAsset asset, double rads, double x, double y, double z); - Future setBoneTransform(FilamentAsset asset, String boneName, String meshName, - BoneTransform transform); + // Future setBoneTransform(FilamentAsset asset, String boneName, String meshName, + // BoneTransform transform); Future setScale(FilamentAsset asset, double scale); Future setCameraFocalLength(double focalLength); Future setCameraFocusDistance(double focusDistance); @@ -282,14 +282,11 @@ class PolyvoxFilamentController extends FilamentController { } Future setAnimation(FilamentAsset asset, Animation animation) async { - print("Frmael en ${animation.frameLengthInMs}"); await _channel.invokeMethod("setAnimation", [ asset, animation.morphData!, animation.numMorphWeights, - animation.boneTransforms ?? Float32List(0), - animation.boneNames ?? [], - animation.meshNames ?? [], + animation.boneAnimations?.map((a) => a.toList()).toList() ?? [], animation.numFrames, animation.frameLengthInMs ]); @@ -367,21 +364,21 @@ class PolyvoxFilamentController extends FilamentController { await _channel.invokeMethod("setPosition", [asset, x, y, z]); } - Future setBoneTransform(FilamentAsset asset, String boneName, String meshName, - BoneTransform transform) async { - await _channel.invokeMethod("setBoneTransform", [ - asset, - boneName, - meshName, - transform.translations[0].x, - transform.translations[0].y, - transform.translations[0].z, - transform.quaternions[0].x, - transform.quaternions[0].y, - transform.quaternions[0].z, - transform.quaternions[0].w - ]); - } + // Future setBoneTransform(FilamentAsset asset, String boneName, String meshName, + // BoneTransform transform) async { + // await _channel.invokeMethod("setBoneTransform", [ + // asset, + // boneName, + // meshName, + // transform.translations[0].x, + // transform.translations[0].y, + // transform.translations[0].z, + // transform.quaternions[0].x, + // transform.quaternions[0].y, + // transform.quaternions[0].z, + // transform.quaternions[0].w + // ]); + // } Future setScale(FilamentAsset asset, double scale) async { await _channel.invokeMethod("setScale", [asset, scale]); diff --git a/linux/polyvox_filament_plugin.cc b/linux/polyvox_filament_plugin.cc index 44450318..5642d197 100644 --- a/linux/polyvox_filament_plugin.cc +++ b/linux/polyvox_filament_plugin.cc @@ -265,27 +265,27 @@ static FlMethodResponse* _set_position(PolyvoxFilamentPlugin* self, FlMethodCall return FL_METHOD_RESPONSE(fl_method_success_response_new(result)); } -static FlMethodResponse* _set_bone_transform(PolyvoxFilamentPlugin* self, FlMethodCall* method_call) { - FlValue* args = fl_method_call_get_args(method_call); - auto assetPtr = (void*)fl_value_get_int(fl_value_get_list_value(args, 0)); - auto boneName = fl_value_get_string(fl_value_get_list_value(args, 1)); - auto meshName = fl_value_get_string(fl_value_get_list_value(args, 2)); +// static FlMethodResponse* _set_bone_transform(PolyvoxFilamentPlugin* self, FlMethodCall* method_call) { +// FlValue* args = fl_method_call_get_args(method_call); +// auto assetPtr = (void*)fl_value_get_int(fl_value_get_list_value(args, 0)); +// auto boneName = fl_value_get_string(fl_value_get_list_value(args, 1)); +// auto meshName = fl_value_get_string(fl_value_get_list_value(args, 2)); - set_bone_transform( - assetPtr, - boneName, - meshName, - (float)fl_value_get_float(fl_value_get_list_value(args, 3)), // transX - (float)fl_value_get_float(fl_value_get_list_value(args, 4)), // transY - (float)fl_value_get_float(fl_value_get_list_value(args, 5)), // transZ - (float)fl_value_get_float(fl_value_get_list_value(args, 6)), // quatX - (float)fl_value_get_float(fl_value_get_list_value(args, 7)), // quatY - (float)fl_value_get_float(fl_value_get_list_value(args, 8)), // quatZ - (float)fl_value_get_float(fl_value_get_list_value(args, 9)) // quatW - ); - g_autoptr(FlValue) result = fl_value_new_string("OK"); - return FL_METHOD_RESPONSE(fl_method_success_response_new(result)); -} +// set_bone_transform( +// assetPtr, +// boneName, +// meshName, +// (float)fl_value_get_float(fl_value_get_list_value(args, 3)), // transX +// (float)fl_value_get_float(fl_value_get_list_value(args, 4)), // transY +// (float)fl_value_get_float(fl_value_get_list_value(args, 5)), // transZ +// (float)fl_value_get_float(fl_value_get_list_value(args, 6)), // quatX +// (float)fl_value_get_float(fl_value_get_list_value(args, 7)), // quatY +// (float)fl_value_get_float(fl_value_get_list_value(args, 8)), // quatZ +// (float)fl_value_get_float(fl_value_get_list_value(args, 9)) // quatW +// ); +// g_autoptr(FlValue) result = fl_value_new_string("OK"); +// return FL_METHOD_RESPONSE(fl_method_success_response_new(result)); +// } static FlMethodResponse* _set_camera_position(PolyvoxFilamentPlugin* self, FlMethodCall* method_call) { FlValue* args = fl_method_call_get_args(method_call); @@ -379,32 +379,62 @@ static FlMethodResponse* _set_animation(PolyvoxFilamentPlugin* self, FlMethodCal int64_t numMorphWeights = fl_value_get_int(fl_value_get_list_value(args, 2)); - float* const boneData = (float* const) fl_value_get_float32_list(fl_value_get_list_value(args, 3)); + FlValue* flBoneAnimations = fl_value_get_list_value(args, 3); - FlValue* boneNamesValue = fl_value_get_list_value(args, 4); - int64_t numBones = fl_value_get_length(boneNamesValue); + size_t numBoneAnimations = fl_value_get_length(flBoneAnimations); - const char** boneNames = nullptr; - const char** meshNames = nullptr; - if(numBones > 0) { - boneNames = new const char*[numBones]; - meshNames = new const char*[numBones]; + vector boneAnimations; + boneAnimations.resize(numBoneAnimations); + + for(int i = 0; i < numBoneAnimations; i++) { - - for(int i=0; i< numBones;i++) { - boneNames[i] = fl_value_get_string(fl_value_get_list_value(boneNamesValue, i)); + FlValue* flBoneAnimation = fl_value_get_list_value(flBoneAnimations, i); + + FlValue* flBoneNames = fl_value_get_list_value(flBoneAnimation, 0); + FlValue* flMeshNames = fl_value_get_list_value(flBoneAnimation, 1); + float* const frameData = (float* const) fl_value_get_float32_list(fl_value_get_list_value(flBoneAnimation, 2)); + + vector boneNames; + boneNames.resize(fl_value_get_length(flBoneNames)); + + for(int i=0; i < boneNames.size(); i++) { + boneNames[i] = fl_value_get_string(fl_value_get_list_value(flBoneNames, i)) ; + std::cout << boneNames[i] << std::endl; } - - FlValue* meshNamesValue = fl_value_get_list_value(args, 5); - - for(int i=0; i< numBones;i++) { - meshNames[i] = fl_value_get_string(fl_value_get_list_value(meshNamesValue, i)); + + vector meshNames; + meshNames.resize(fl_value_get_length(flMeshNames)); + for(int i=0; i < meshNames.size(); i++) { + meshNames[i] = fl_value_get_string(fl_value_get_list_value(flMeshNames, i)); } + + auto animation = BoneAnimation(); + animation.boneNames = (const char**)malloc(boneNames.size() * sizeof(char*)); + memcpy(animation.boneNames, boneNames.data(), boneNames.size() * sizeof(char*)); + animation.numBones = boneNames.size(); + animation.meshNames = (const char**)malloc(meshNames.size() * sizeof(char*)); + memcpy(animation.meshNames, meshNames.data(), meshNames.size() * sizeof(char*)); + animation.numMeshTargets = meshNames.size(); + animation.data = frameData; + + boneAnimations[i] = animation; } + + int64_t numFrames = fl_value_get_int(fl_value_get_list_value(args, 4)); - int64_t numFrames = fl_value_get_int(fl_value_get_list_value(args, 6)); - float frameLengthInMs = fl_value_get_float(fl_value_get_list_value(args, 7)); - set_animation(assetPtr, morphData, numMorphWeights, boneData, boneNames, meshNames, numBones, numFrames, frameLengthInMs); + float frameLengthInMs = fl_value_get_float(fl_value_get_list_value(args, 5)); + + auto boneAnimationsPointer = boneAnimations.data(); + auto boneAnimationsSize = boneAnimations.size(); + + set_animation( + assetPtr, + morphData, + numMorphWeights, + boneAnimationsPointer, + boneAnimationsSize, + numFrames, + frameLengthInMs); g_autoptr(FlValue) result = fl_value_new_string("OK"); return FL_METHOD_RESPONSE(fl_method_success_response_new(result)); @@ -536,7 +566,7 @@ static void polyvox_filament_plugin_handle_method_call( } else if(strcmp(method, "setPosition") == 0) { response = _set_position(self, method_call); } else if(strcmp(method, "setBoneTransform") == 0) { - response = _set_bone_transform(self, method_call); + // response = _set_bone_transform(self, method_call); } else { response = FL_METHOD_RESPONSE(fl_method_not_implemented_response_new()); } diff --git a/pubspec.yaml b/pubspec.yaml index cf4f3c30..b3a96e6e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -10,7 +10,7 @@ environment: dependencies: flutter: sdk: flutter - + vector_math: ^2.1.2 plugin_platform_interface: ^2.0.0 dev_dependencies: