decouple assets from viewer to allow independent addition/removal/animation/etc
This commit is contained in:
@@ -13,9 +13,11 @@ add_library(
|
||||
src/main/cpp/KtxReader1.cpp
|
||||
src/main/cpp/StbProvider.cpp
|
||||
src/main/cpp/JobSystem.cpp
|
||||
../ios/src/SceneAssetLoader.cpp
|
||||
../ios/src/FilamentViewer.cpp
|
||||
../ios/src/streambuf.cpp
|
||||
../ios/src/imagematerial.c
|
||||
../ios/src/StreamBufferAdapter.cpp
|
||||
../ios/src/image/imagematerial.c
|
||||
../ios/src/SceneAsset.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "FilamentViewer.hpp"
|
||||
#include "SceneAsset.hpp"
|
||||
#include <android/asset_manager.h>
|
||||
#include <android/asset_manager_jni.h>
|
||||
#include <android/native_window_jni.h>
|
||||
@@ -41,6 +42,8 @@ static void freeResource(ResourceBuffer rb) {
|
||||
AAsset* asset = _assets[rb.id];
|
||||
if(asset) {
|
||||
AAsset_close(asset);
|
||||
} else {
|
||||
__android_log_print(ANDROID_LOG_VERBOSE, "filament_api", "Attempting to free resource at index [ %d ] that has already been released.", rb.id);
|
||||
}
|
||||
_assets[rb.id] = nullptr;
|
||||
}
|
||||
@@ -68,16 +71,16 @@ extern "C" {
|
||||
((FilamentViewer*)viewer)->removeIbl();
|
||||
}
|
||||
|
||||
void load_glb(void* viewer, const char* assetPath) {
|
||||
((FilamentViewer*)viewer)->loadGlb(assetPath);
|
||||
void* load_glb(void* viewer, const char* assetPath) {
|
||||
return ((FilamentViewer*)viewer)->loadGlb(assetPath);
|
||||
}
|
||||
|
||||
void load_gltf(void* viewer, const char* assetPath, const char* relativePath) {
|
||||
((FilamentViewer*)viewer)->loadGltf(assetPath, relativePath);
|
||||
void* load_gltf(void* viewer, const char* assetPath, const char* relativePath) {
|
||||
return ((FilamentViewer*)viewer)->loadGltf(assetPath, relativePath);
|
||||
}
|
||||
|
||||
bool set_camera(void* viewer, const char* nodeName) {
|
||||
return ((FilamentViewer*)viewer)->setCamera(nodeName);
|
||||
bool set_camera(void* viewer, void* asset, const char* nodeName) {
|
||||
return ((FilamentViewer*)viewer)->setCamera((SceneAsset*)asset, nodeName);
|
||||
}
|
||||
|
||||
void* filament_viewer_new(
|
||||
@@ -141,22 +144,22 @@ extern "C" {
|
||||
((FilamentViewer*)viewer)->manipulator->grabEnd();
|
||||
}
|
||||
|
||||
void apply_weights(void* viewer, float* weights, int count) {
|
||||
((FilamentViewer*)viewer)->applyWeights(weights, count);
|
||||
void apply_weights(void* asset, float* weights, int count) {
|
||||
((SceneAsset*)asset)->applyWeights(weights, count);
|
||||
}
|
||||
|
||||
void animate_weights(void* viewer, float* data, int numWeights, int numFrames, float frameRate) {
|
||||
void animate_weights(void* asset, float* data, int numWeights, int numFrames, float frameRate) {
|
||||
__android_log_print(ANDROID_LOG_VERBOSE, "filament_api", "Animating %d frames, each with %d weights", numFrames, numWeights);
|
||||
((FilamentViewer*)viewer)->animateWeights((float*)data, numWeights, numFrames, frameRate);
|
||||
((SceneAsset*)asset)->animateWeights((float*)data, numWeights, numFrames, frameRate);
|
||||
}
|
||||
|
||||
void play_animation(void* viewer, int index, bool loop) {
|
||||
void play_animation(void* asset, int index, bool loop) {
|
||||
__android_log_print(ANDROID_LOG_VERBOSE, "filament_api", "Playing embedded animation %d", index);
|
||||
((FilamentViewer*)viewer)->playAnimation(index, loop);
|
||||
((SceneAsset*)asset)->playAnimation(index, loop);
|
||||
}
|
||||
|
||||
char** get_animation_names(void* viewer, int* countPtr) {
|
||||
auto names = ((FilamentViewer*)viewer)->getAnimationNames();
|
||||
char** get_animation_names(void* asset, int* countPtr) {
|
||||
auto names = ((SceneAsset*)asset)->getAnimationNames();
|
||||
__android_log_print(ANDROID_LOG_VERBOSE, "filament_api", "Got %d animation names", names->size());
|
||||
char** names_c;
|
||||
names_c = new char*[names->size()];
|
||||
@@ -168,8 +171,8 @@ extern "C" {
|
||||
return names_c;
|
||||
}
|
||||
|
||||
char** get_target_names(void* viewer, char* meshName, int* countPtr ) {
|
||||
unique_ptr<vector<string>> names = ((FilamentViewer*)viewer)->getTargetNames(meshName);
|
||||
char** get_target_names(void* asset, char* meshName, int* countPtr ) {
|
||||
unique_ptr<vector<string>> names = ((SceneAsset*)asset)->getTargetNames(meshName);
|
||||
|
||||
__android_log_print(ANDROID_LOG_VERBOSE, "filament_api", "Got %d names", names->size());
|
||||
|
||||
@@ -188,8 +191,8 @@ extern "C" {
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
void remove_asset(void* viewer) {
|
||||
((FilamentViewer*)viewer)->removeAsset();
|
||||
void remove_asset(void* viewer, void* asset) {
|
||||
((FilamentViewer*)viewer)->removeAsset((SceneAsset*)asset);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ interface FilamentInterop : Library {
|
||||
|
||||
fun load_gltf(viewer:Pointer, uri:String, relativeResourcePath:String) : Pointer;
|
||||
|
||||
fun set_camera(viewer:Pointer, nodeName:String) : Boolean;
|
||||
fun set_camera(viewer:Pointer, asset:Pointer, nodeName:String) : Boolean;
|
||||
|
||||
fun render(viewer:Pointer);
|
||||
|
||||
@@ -50,21 +50,19 @@ interface FilamentInterop : Library {
|
||||
|
||||
fun grab_end(viewer:Pointer)
|
||||
|
||||
fun apply_weights(viewer:Pointer, weights:FloatArray, size:Int);
|
||||
fun apply_weights(asset:Pointer, weights:FloatArray, size:Int);
|
||||
|
||||
fun animate_weights(viewer:Pointer, frames:FloatArray, numWeights:Int, numFrames:Int, frameRate:Float);
|
||||
fun animate_weights(asset:Pointer, frames:FloatArray, numWeights:Int, numFrames:Int, frameRate:Float);
|
||||
|
||||
fun get_target_names(viewer:Pointer, meshName:String, outLen:IntByReference) : Pointer;
|
||||
fun get_target_names(asset:Pointer, meshName:String, outLen:IntByReference) : Pointer;
|
||||
|
||||
fun get_animation_names(viewer:Pointer, outLen:IntByReference) : Pointer;
|
||||
fun get_animation_names(asset:Pointer, outLen:IntByReference) : Pointer;
|
||||
|
||||
fun play_animation(viewer:Pointer, index:Int, loop:Boolean);
|
||||
fun play_animation(asset:Pointer, index:Int, loop:Boolean);
|
||||
|
||||
fun free_pointer(ptr:Pointer, size:Int);
|
||||
|
||||
fun release_source_assets(viewer:Pointer);
|
||||
|
||||
fun remove_asset(viewer:Pointer);
|
||||
fun remove_asset(viewer:Pointer, asset:Pointer);
|
||||
|
||||
fun remove_skybox(viewer:Pointer);
|
||||
|
||||
|
||||
@@ -192,32 +192,31 @@ PlatformView {
|
||||
val key = loader.getLookupKeyForAsset(call.arguments as String)
|
||||
val key2 = loader.getLookupKeyForAsset(call.arguments as String, context.packageName)
|
||||
val path = loader.findAppBundlePath()
|
||||
Log.v(TAG, "key ${key} key2 ${key2} path : ${path}")
|
||||
|
||||
_lib.load_glb(
|
||||
val assetPtr = _lib.load_glb(
|
||||
_viewer!!,
|
||||
key
|
||||
)
|
||||
result.success("OK");
|
||||
result.success(Pointer.nativeValue(assetPtr));
|
||||
}
|
||||
"loadGltf" -> {
|
||||
if (_viewer == null)
|
||||
return;
|
||||
val args = call.arguments as ArrayList<Any?>
|
||||
val loader = FlutterInjector.instance().flutterLoader()
|
||||
_lib.load_gltf(
|
||||
val assetPtr = _lib.load_gltf(
|
||||
_viewer!!,
|
||||
loader.getLookupKeyForAsset(args[0] as String),
|
||||
loader.getLookupKeyForAsset(args[1] as String)
|
||||
)
|
||||
result.success("OK");
|
||||
result.success(Pointer.nativeValue(assetPtr));
|
||||
}
|
||||
"setCamera" -> {
|
||||
if (_viewer == null)
|
||||
return;
|
||||
val args = call.arguments as ArrayList<*>
|
||||
val success = _lib.set_camera(
|
||||
_viewer!!,
|
||||
call.arguments as String
|
||||
Pointer(args[0] as Long),
|
||||
args[1] as String,
|
||||
)
|
||||
if(success) {
|
||||
result.success("OK");
|
||||
@@ -236,9 +235,10 @@ PlatformView {
|
||||
return;
|
||||
|
||||
val countPtr = IntByReference();
|
||||
val arrPtr = _lib.get_target_names(_viewer!!, call.arguments as String, countPtr)
|
||||
val args = call.arguments as ArrayList<*>
|
||||
val namesPtr = _lib.get_target_names(Pointer(args[0] as Long), args[1] as String, countPtr)
|
||||
|
||||
val names = arrPtr.getStringArray(0, countPtr.value);
|
||||
val names = namesPtr.getStringArray(0, countPtr.value);
|
||||
|
||||
for(i in 0..countPtr.value-1) {
|
||||
Log.v(TAG, "Got target names ${names[i]} ${names[i].length}")
|
||||
@@ -246,16 +246,14 @@ PlatformView {
|
||||
|
||||
val namesAsList = names.toCollection(ArrayList())
|
||||
|
||||
_lib.free_pointer(arrPtr, countPtr.getValue())
|
||||
_lib.free_pointer(namesPtr, countPtr.getValue())
|
||||
|
||||
result.success(namesAsList)
|
||||
}
|
||||
"getAnimationNames" -> {
|
||||
if(_viewer == null)
|
||||
return;
|
||||
|
||||
val assetPtr = Pointer(call.arguments as Long)
|
||||
val countPtr = IntByReference();
|
||||
val arrPtr = _lib.get_animation_names(_viewer!!, countPtr)
|
||||
val arrPtr = _lib.get_animation_names(assetPtr, countPtr)
|
||||
|
||||
val names = arrPtr.getStringArray(0, countPtr.value);
|
||||
|
||||
@@ -269,23 +267,22 @@ PlatformView {
|
||||
result.success(names.toCollection(ArrayList()))
|
||||
}
|
||||
"applyWeights" -> {
|
||||
if(_viewer == null)
|
||||
return;
|
||||
val weights = call.arguments as ArrayList<Float>;
|
||||
val args = call.arguments as ArrayList<*>
|
||||
val assetPtr = Pointer(args[0] as Long)
|
||||
val weights = args[1] as ArrayList<Float>;
|
||||
|
||||
_lib.apply_weights(_viewer!!, weights.toFloatArray(), weights.size)
|
||||
_lib.apply_weights(assetPtr, weights.toFloatArray(), weights.size)
|
||||
result.success("OK");
|
||||
}
|
||||
"animateWeights" -> {
|
||||
if(_viewer == null)
|
||||
return;
|
||||
val args = call.arguments as ArrayList<Any?>
|
||||
val frames = args[0] as ArrayList<Float>;
|
||||
val numWeights = args[1] as Int
|
||||
val numFrames = args[2] as Int
|
||||
val frameLenInMs = args[3] as Double
|
||||
val assetPtr = Pointer(args[0] as Long)
|
||||
val frames = args[1] as ArrayList<Float>;
|
||||
val numWeights = args[2] as Int
|
||||
val numFrames = args[3] as Int
|
||||
val frameLenInMs = args[4] as Double
|
||||
|
||||
_lib.animate_weights(_viewer!!, frames.toFloatArray(), numWeights, numFrames, frameLenInMs.toFloat())
|
||||
_lib.animate_weights(assetPtr, frames.toFloatArray(), numWeights, numFrames, frameLenInMs.toFloat())
|
||||
result.success("OK");
|
||||
}
|
||||
"panStart" -> {
|
||||
@@ -331,12 +328,12 @@ PlatformView {
|
||||
result.success("OK");
|
||||
}
|
||||
"removeAsset" -> {
|
||||
_lib.remove_asset(_viewer!!)
|
||||
_lib.remove_asset(_viewer!!, Pointer(call.arguments as Long))
|
||||
result.success("OK");
|
||||
}
|
||||
"playAnimation" -> {
|
||||
val args = call.arguments as ArrayList<Any?>
|
||||
_lib.play_animation(_viewer!!, args[0] as Int, args[1] as Boolean)
|
||||
_lib.play_animation(Pointer(args[0] as Long), args[1] as Int, args[2] as Boolean)
|
||||
result.success("OK")
|
||||
}
|
||||
else -> {
|
||||
|
||||
3
example/assets/background.png
Normal file
3
example/assets/background.png
Normal file
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:c38fa36cbbbe969a2ae015fe56752343e8d06007c6eea28c56e22ffd56a8db1a
|
||||
size 2570831
|
||||
3
example/assets/cube.blend
Normal file
3
example/assets/cube.blend
Normal file
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:9fb09944d65a155fc5b6522f296dd875df02fc2944733a35eb09bec23bbabdcd
|
||||
size 813192
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:674e74fbef9b017aa071cd985bcc34c34c96fc57051a0724b6c23de78cd22db8
|
||||
size 2352
|
||||
oid sha256:1f53c13e2ee7cd36156fa3bcfcb9a199b5999c3cb15d7f964a326105ac7d0419
|
||||
size 11032
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:defdd358826b3b8f58237c38e36b2a03d768f2307407152c79621d3358001f71
|
||||
oid sha256:0245800fa846c2a3c15cb1d2aa6ded86fcf8355e568907dcd50e073c589f85d6
|
||||
size 2095464
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:b7e5e33a6bbaee9f0d89f8525fa9c3723f9ace8ebaaf00b3a9dc8cdf7c6c1095
|
||||
oid sha256:e4050117d400c027dd47baa7b4a9ed46f7174b0690def9d70643d3c364faf758
|
||||
size 1572932
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:ee52c238ccf3188a21533d6e096f74cadab26720d0fa710ad9737484db8ee4cb
|
||||
size 98372
|
||||
@@ -16,6 +16,9 @@ class MyApp extends StatefulWidget {
|
||||
class _MyAppState extends State<MyApp> {
|
||||
final FilamentController _filamentController = PolyvoxFilamentController();
|
||||
|
||||
FilamentAsset? _cube;
|
||||
FilamentAsset? _flightHelmet;
|
||||
|
||||
final weights = List.filled(255, 0.0);
|
||||
List<String> _targets = [];
|
||||
bool _loop = false;
|
||||
@@ -32,22 +35,24 @@ class _MyAppState extends State<MyApp> {
|
||||
home: Scaffold(
|
||||
backgroundColor: Colors.transparent,
|
||||
body: Column(children: [
|
||||
Expanded(child:SizedBox(
|
||||
height:200, width:200,
|
||||
child:FilamentWidget(
|
||||
controller: _filamentController,
|
||||
))),
|
||||
|
||||
Expanded(
|
||||
child: SingleChildScrollView(child:Wrap(
|
||||
child: SizedBox(
|
||||
height: 200,
|
||||
width: 200,
|
||||
child: FilamentWidget(
|
||||
controller: _filamentController,
|
||||
))),
|
||||
Expanded(
|
||||
child: SingleChildScrollView(
|
||||
child: Wrap(
|
||||
alignment: WrapAlignment.end,
|
||||
crossAxisAlignment: WrapCrossAlignment.end,
|
||||
children: [
|
||||
ElevatedButton(
|
||||
children: [
|
||||
ElevatedButton(
|
||||
child: const Text('load background image'),
|
||||
onPressed: () async {
|
||||
await _filamentController.setBackgroundImage(
|
||||
'assets/background3.png');
|
||||
await _filamentController
|
||||
.setBackgroundImage('assets/background.png');
|
||||
}),
|
||||
ElevatedButton(
|
||||
child: const Text('load skybox'),
|
||||
@@ -61,46 +66,57 @@ class _MyAppState extends State<MyApp> {
|
||||
child: const Text('remove skybox'),
|
||||
onPressed: () async {
|
||||
await _filamentController.removeSkybox();
|
||||
}
|
||||
),
|
||||
}),
|
||||
ElevatedButton(
|
||||
child: const Text('load cube'),
|
||||
onPressed: () async {
|
||||
await _filamentController.loadGltf(
|
||||
'assets/cube.glb' ,"assets");
|
||||
print(await _filamentController.getAnimationNames());
|
||||
child: const Text('load cube GLB'),
|
||||
onPressed:_cube != null ? null : () async {
|
||||
_cube = await _filamentController
|
||||
.loadGlb('assets/cube.glb');
|
||||
print(await _filamentController
|
||||
.getAnimationNames(_cube!));
|
||||
}),
|
||||
ElevatedButton(
|
||||
ElevatedButton(
|
||||
child: const Text('load cube GLTF'),
|
||||
onPressed:() async {
|
||||
if(_cube != null) {
|
||||
await _filamentController.removeAsset(_cube!);
|
||||
}
|
||||
_cube = await _filamentController
|
||||
.loadGltf('assets/cube.gltf', 'assets');
|
||||
print(await _filamentController
|
||||
.getAnimationNames(_cube!));
|
||||
}),
|
||||
ElevatedButton(
|
||||
child: const Text('load flight helmet'),
|
||||
onPressed: () async {
|
||||
await _filamentController.loadGltf(
|
||||
'assets/FlightHelmet/FlightHelmet.gltf', 'assets/FlightHelmet');
|
||||
onPressed:_flightHelmet != null ? null : () async {
|
||||
_flightHelmet = await _filamentController.loadGltf(
|
||||
'assets/FlightHelmet/FlightHelmet.gltf',
|
||||
'assets/FlightHelmet');
|
||||
}),
|
||||
ElevatedButton(
|
||||
child: const Text('remove asset'),
|
||||
ElevatedButton(
|
||||
child: const Text('remove cube'),
|
||||
onPressed: () async {
|
||||
await _filamentController
|
||||
.removeAsset();
|
||||
await _filamentController.removeAsset(_cube!);
|
||||
}),
|
||||
ElevatedButton(
|
||||
child: const Text('set all weights to 1'),
|
||||
onPressed: () async {
|
||||
await _filamentController
|
||||
.applyWeights(List.filled(8, 1.0));
|
||||
.applyWeights(_cube!, List.filled(8, 1.0));
|
||||
}),
|
||||
ElevatedButton(
|
||||
child: const Text('set all weights to 0'),
|
||||
onPressed: () async {
|
||||
await _filamentController
|
||||
.applyWeights(List.filled(8, 0));
|
||||
.applyWeights(_cube!, List.filled(8, 0));
|
||||
}),
|
||||
ElevatedButton(
|
||||
onPressed: () =>
|
||||
_filamentController.playAnimation(0, loop: _loop),
|
||||
_filamentController.playAnimation(_cube!, 0, loop: _loop),
|
||||
child: const Text('play animation')),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
_filamentController.stopAnimation();
|
||||
_filamentController.stopAnimation(_cube!);
|
||||
},
|
||||
child: const Text('stop animation')),
|
||||
Checkbox(
|
||||
@@ -120,7 +136,7 @@ class _MyAppState extends State<MyApp> {
|
||||
child: const Text('zoom out')),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
_filamentController.setCamera("Camera_Orientation");
|
||||
_filamentController.setCamera(_cube!, "Camera_Orientation");
|
||||
},
|
||||
child: const Text('set camera')),
|
||||
ElevatedButton(
|
||||
@@ -135,6 +151,7 @@ class _MyAppState extends State<MyApp> {
|
||||
List.filled(numWeights, frame / totalFrames));
|
||||
|
||||
_filamentController.animate(
|
||||
_cube!,
|
||||
frames.reduce((a, b) => a + b),
|
||||
numWeights,
|
||||
totalFrames,
|
||||
@@ -145,7 +162,7 @@ class _MyAppState extends State<MyApp> {
|
||||
builder: (innerCtx) => ElevatedButton(
|
||||
onPressed: () async {
|
||||
final names = await _filamentController
|
||||
.getTargetNames("Cube");
|
||||
.getTargetNames(_cube!, "Cube");
|
||||
|
||||
await showDialog(
|
||||
builder: (ctx) {
|
||||
@@ -173,7 +190,7 @@ class _MyAppState extends State<MyApp> {
|
||||
builder: (innerCtx) => ElevatedButton(
|
||||
onPressed: () async {
|
||||
final names =
|
||||
await _filamentController.getAnimationNames();
|
||||
await _filamentController.getAnimationNames(_cube!);
|
||||
|
||||
await showDialog(
|
||||
builder: (ctx) {
|
||||
@@ -219,52 +236,4 @@ class _MyAppState extends State<MyApp> {
|
||||
}
|
||||
}
|
||||
|
||||
// ElevatedButton(
|
||||
// child: Text('load skybox'),
|
||||
// onPressed: () {
|
||||
// _filamentController.loadSkybox(
|
||||
// 'assets/default_env/default_env_skybox.ktx',
|
||||
// 'assets/default_env/default_env_ibl.ktx');
|
||||
// }),
|
||||
// ElevatedButton(
|
||||
// child: Text('load gltf'),
|
||||
// onPressed: () {
|
||||
// _filamentController.loadGltf(
|
||||
// 'assets/guy.gltf', 'assets', 'Material');
|
||||
// }),
|
||||
// ElevatedButton(
|
||||
// child: Text('create morpher'),
|
||||
// onPressed: () {
|
||||
// _filamentController.createMorpher(
|
||||
// 'CC_Base_Body.003', 'CC_Base_Body.003',
|
||||
// materialName: 'Material');
|
||||
// }),
|
||||
// ])),
|
||||
// Column(
|
||||
// children: _targets
|
||||
// .asMap()
|
||||
// .map((i, t) => MapEntry(
|
||||
// i,
|
||||
// Row(children: [
|
||||
// Text(t),
|
||||
// Slider(
|
||||
// min: 0,
|
||||
// max: 1,
|
||||
// divisions: 10,
|
||||
// value: weights[i],
|
||||
// onChanged: (v) {
|
||||
// setState(() {
|
||||
// weights[i] = v;
|
||||
// _filamentController
|
||||
// .applyWeights(weights);
|
||||
// });
|
||||
// })
|
||||
// ])))
|
||||
// .values
|
||||
// .toList(),
|
||||
// )
|
||||
// ElevatedButton(
|
||||
// child: const Text('init'),
|
||||
// onPressed: () async {
|
||||
// await _filamentController.initialize();
|
||||
// }),
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -30,6 +30,10 @@
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
|
||||
#include "SceneAssetLoader.hpp"
|
||||
#include "SceneAsset.hpp"
|
||||
#include "SceneResources.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace filament;
|
||||
using namespace filament::math;
|
||||
@@ -39,55 +43,6 @@ using namespace camutils;
|
||||
|
||||
|
||||
namespace polyvox {
|
||||
|
||||
typedef std::chrono::time_point<std::chrono::high_resolution_clock> time_point_t;
|
||||
|
||||
struct EmbeddedAnimationBuffer {
|
||||
EmbeddedAnimationBuffer(int animationIndex, float duration, bool loop) : animationIndex(animationIndex), duration(duration), loop(loop) {}
|
||||
bool hasStarted = false;
|
||||
int animationIndex;
|
||||
float duration = 0;
|
||||
time_point_t lastTime;
|
||||
bool loop;
|
||||
};
|
||||
|
||||
|
||||
|
||||
struct ResourceBuffer {
|
||||
ResourceBuffer(const void* data, const uint32_t size, const uint32_t id) : data(data), size(size), id(id) {};
|
||||
|
||||
ResourceBuffer& operator=(ResourceBuffer other)
|
||||
{
|
||||
data = other.data;
|
||||
size = other.size;
|
||||
id = other.id;
|
||||
return *this;
|
||||
}
|
||||
const void* data;
|
||||
uint32_t size;
|
||||
uint32_t id;
|
||||
};
|
||||
|
||||
using LoadResource = std::function<ResourceBuffer(const char* uri)>;
|
||||
using FreeResource = std::function<void (ResourceBuffer)>;
|
||||
|
||||
struct MorphAnimationBuffer {
|
||||
|
||||
MorphAnimationBuffer(float* frameData,
|
||||
int numWeights,
|
||||
int numFrames,
|
||||
float frameLength) : frameData(frameData), numWeights(numWeights), numFrames(numFrames), frameLengthInMs(frameLength) {
|
||||
}
|
||||
|
||||
int frameIndex = -1;
|
||||
int numFrames;
|
||||
float frameLengthInMs;
|
||||
time_point_t startTime;
|
||||
|
||||
float* frameData;
|
||||
int numWeights;
|
||||
};
|
||||
|
||||
class FilamentViewer {
|
||||
public:
|
||||
FilamentViewer(void* layer, LoadResource loadResource, FreeResource freeResource);
|
||||
@@ -99,42 +54,16 @@ namespace polyvox {
|
||||
void loadIbl(const char* const iblUri);
|
||||
void removeIbl();
|
||||
|
||||
void loadGlb(const char* const uri);
|
||||
void loadGltf(const char* const uri, const char* relativeResourcePath);
|
||||
void removeAsset();
|
||||
SceneAsset* loadGlb(const char* const uri);
|
||||
SceneAsset* loadGltf(const char* const uri, const char* relativeResourcePath);
|
||||
void removeAsset(SceneAsset* asset);
|
||||
|
||||
void updateViewportAndCameraProjection(int height, int width, float scaleFactor);
|
||||
void render();
|
||||
unique_ptr<vector<string>> getTargetNames(const char* meshName);
|
||||
unique_ptr<vector<string>> getAnimationNames();
|
||||
|
||||
Manipulator<float>* manipulator;
|
||||
|
||||
|
||||
///
|
||||
/// Manually set the weights for all morph targets in the assets to the provided values.
|
||||
/// See [animateWeights] if you want to automatically
|
||||
///
|
||||
void applyWeights(float* weights, int count);
|
||||
|
||||
///
|
||||
/// Update the asset's morph target weights every "frame" (which is an arbitrary length of time, i.e. this is not the same as a frame at the framerate of the underlying rendering framework).
|
||||
/// Accordingly:
|
||||
/// length(data) = numWeights * numFrames
|
||||
/// total_animation_duration_in_ms = number_of_frames * frameLengthInMs
|
||||
///
|
||||
void animateWeights(float* data, int numWeights, int numFrames, float frameLengthInMs);
|
||||
|
||||
///
|
||||
/// Play an embedded animation (i.e. an animation node embedded in the GLTF asset). If [loop] is true, the animation will repeat indefinitely.
|
||||
///
|
||||
void playAnimation(int index, bool loop);
|
||||
|
||||
///
|
||||
/// Immediately stop the currently playing animation. NOOP if no animation is playing.
|
||||
///
|
||||
void stopAnimation();
|
||||
|
||||
bool setCamera(const char* nodeName);
|
||||
bool setCamera(SceneAsset* asset, const char* nodeName);
|
||||
void destroySwapChain();
|
||||
void createSwapChain(void* surface);
|
||||
|
||||
@@ -161,10 +90,10 @@ namespace polyvox {
|
||||
|
||||
SwapChain* _swapChain = nullptr;
|
||||
|
||||
Animator* _animator;
|
||||
vector<SceneAsset*> _assets;
|
||||
|
||||
AssetLoader* _assetLoader;
|
||||
FilamentAsset* _asset = nullptr;
|
||||
SceneAssetLoader* _sceneAssetLoader;
|
||||
NameComponentManager* _ncm;
|
||||
std::mutex mtx; // mutex to ensure thread safety when removing assets
|
||||
|
||||
@@ -184,24 +113,15 @@ namespace polyvox {
|
||||
|
||||
float _cameraFocalLength = 0.0f;
|
||||
|
||||
void updateMorphAnimation();
|
||||
void updateEmbeddedAnimation();
|
||||
|
||||
// animation flags;
|
||||
bool isAnimating;
|
||||
unique_ptr<MorphAnimationBuffer> _morphAnimationBuffer;
|
||||
unique_ptr<EmbeddedAnimationBuffer> _embeddedAnimationBuffer;
|
||||
|
||||
// these flags relate to the textured quad we use for rendering unlit background images
|
||||
Texture* _imageTexture = nullptr;
|
||||
Entity* _imageEntity = nullptr;
|
||||
VertexBuffer* _imageVb = nullptr;
|
||||
IndexBuffer* _imageIb = nullptr;
|
||||
Material* _imageMaterial = nullptr;
|
||||
TextureSampler _imageSampler;
|
||||
|
||||
ColorGrading *colorGrading = nullptr;
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef POLYVOX_FILAMENT_LOG_H
|
||||
#define POLYVOX_FILAMENT_LOG_H
|
||||
|
||||
#ifdef __OBJC__
|
||||
#import <Foundation/Foundation.h>
|
||||
#elif defined __ANDROID__
|
||||
@@ -7,7 +12,7 @@
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
void Log(const char *fmt, ...) {
|
||||
static void Log(const char *fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
|
||||
@@ -23,3 +28,5 @@ void Log(const char *fmt, ...) {
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
#endif
|
||||
193
ios/src/SceneAsset.cpp
Normal file
193
ios/src/SceneAsset.cpp
Normal file
@@ -0,0 +1,193 @@
|
||||
#include <chrono>
|
||||
#include "SceneResources.hpp"
|
||||
#include "SceneAsset.hpp"
|
||||
#include "Log.hpp"
|
||||
#include <gltfio/Animator.h>
|
||||
#include <gltfio/AssetLoader.h>
|
||||
#include <gltfio/FilamentAsset.h>
|
||||
#include <gltfio/ResourceLoader.h>
|
||||
#include <gltfio/TextureProvider.h>
|
||||
|
||||
#include <filament/TransformManager.h>
|
||||
|
||||
|
||||
using namespace std::chrono;
|
||||
|
||||
namespace polyvox {
|
||||
|
||||
using namespace std;
|
||||
using namespace filament;
|
||||
using namespace filament::gltfio;
|
||||
using namespace utils;
|
||||
|
||||
SceneAsset::SceneAsset(FilamentAsset* asset, Engine* engine, NameComponentManager* ncm)
|
||||
: _asset(asset), _engine(engine), _ncm(ncm) {
|
||||
_animator = _asset->getAnimator();
|
||||
}
|
||||
|
||||
SceneAsset::~SceneAsset() { _asset = nullptr; }
|
||||
|
||||
void SceneAsset::applyWeights(float *weights, int count) {
|
||||
RenderableManager& rm = _engine->getRenderableManager();
|
||||
for (size_t i = 0, c = _asset->getEntityCount(); i != c; ++i) {
|
||||
auto inst = rm.getInstance(_asset->getEntities()[i]);
|
||||
rm.setMorphWeights(inst, weights, count);
|
||||
}
|
||||
}
|
||||
|
||||
void SceneAsset::animateWeights(float *data, int numWeights, int numFrames,
|
||||
float frameLengthInMs) {
|
||||
Log("Making morph animation buffer with %d weights across %d frames and "
|
||||
"frame length %f ms ",
|
||||
numWeights, numFrames, frameLengthInMs);
|
||||
_morphAnimationBuffer = std::make_unique<MorphAnimationStatus>(
|
||||
data, numWeights, numFrames, frameLengthInMs);
|
||||
}
|
||||
|
||||
void SceneAsset::updateAnimations() {
|
||||
updateMorphAnimation();
|
||||
updateEmbeddedAnimation();
|
||||
}
|
||||
|
||||
void SceneAsset::updateMorphAnimation() {
|
||||
if (!_morphAnimationBuffer) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_morphAnimationBuffer->frameIndex == -1) {
|
||||
_morphAnimationBuffer->frameIndex++;
|
||||
_morphAnimationBuffer->startTime = high_resolution_clock::now();
|
||||
applyWeights(_morphAnimationBuffer->frameData,
|
||||
_morphAnimationBuffer->numWeights);
|
||||
} else {
|
||||
duration<double, std::milli> dur =
|
||||
high_resolution_clock::now() - _morphAnimationBuffer->startTime;
|
||||
int frameIndex =
|
||||
static_cast<int>(dur.count() / _morphAnimationBuffer->frameLengthInMs);
|
||||
|
||||
if (frameIndex > _morphAnimationBuffer->numFrames - 1) {
|
||||
duration<double, std::milli> dur =
|
||||
high_resolution_clock::now() - _morphAnimationBuffer->startTime;
|
||||
Log("Morph animation completed in %f ms (%d frames at framerate %f), "
|
||||
"final frame was %d",
|
||||
dur.count(), _morphAnimationBuffer->numFrames,
|
||||
1000 / _morphAnimationBuffer->frameLengthInMs,
|
||||
_morphAnimationBuffer->frameIndex);
|
||||
_morphAnimationBuffer = nullptr;
|
||||
} else if (frameIndex != _morphAnimationBuffer->frameIndex) {
|
||||
Log("Rendering frame %d (of a total %d)", frameIndex,
|
||||
_morphAnimationBuffer->numFrames);
|
||||
_morphAnimationBuffer->frameIndex = frameIndex;
|
||||
auto framePtrOffset = frameIndex * _morphAnimationBuffer->numWeights;
|
||||
applyWeights(_morphAnimationBuffer->frameData + framePtrOffset,
|
||||
_morphAnimationBuffer->numWeights);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SceneAsset::playAnimation(int index, bool loop) {
|
||||
if (index > _animator->getAnimationCount() - 1) {
|
||||
Log("Asset does not contain an animation at index %d", index);
|
||||
} else {
|
||||
_boneAnimationStatus = make_unique<BoneAnimationStatus>(
|
||||
index, _animator->getAnimationDuration(index), loop);
|
||||
}
|
||||
}
|
||||
|
||||
void SceneAsset::stopAnimation() {
|
||||
// TODO - does this need to be threadsafe?
|
||||
_boneAnimationStatus = nullptr;
|
||||
}
|
||||
|
||||
void SceneAsset::updateEmbeddedAnimation() {
|
||||
if (!_boneAnimationStatus) {
|
||||
return;
|
||||
}
|
||||
|
||||
duration<double> dur = duration_cast<duration<double>>(
|
||||
high_resolution_clock::now() - _boneAnimationStatus->lastTime);
|
||||
float startTime = 0;
|
||||
if (!_boneAnimationStatus->hasStarted) {
|
||||
_boneAnimationStatus->hasStarted = true;
|
||||
_boneAnimationStatus->lastTime = high_resolution_clock::now();
|
||||
} else if (dur.count() >= _boneAnimationStatus->duration) {
|
||||
if (_boneAnimationStatus->loop) {
|
||||
_boneAnimationStatus->lastTime = high_resolution_clock::now();
|
||||
} else {
|
||||
_boneAnimationStatus = nullptr;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
startTime = dur.count();
|
||||
}
|
||||
|
||||
_animator->applyAnimation(_boneAnimationStatus->animationIndex, startTime);
|
||||
_animator->updateBoneMatrices();
|
||||
}
|
||||
|
||||
unique_ptr<vector<string>> SceneAsset::getAnimationNames() {
|
||||
size_t count = _animator->getAnimationCount();
|
||||
|
||||
Log("Found %d animations in asset.", count);
|
||||
|
||||
unique_ptr<vector<string>> names = make_unique<vector<string>>();
|
||||
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
names->push_back(_animator->getAnimationName(i));
|
||||
}
|
||||
|
||||
return names;
|
||||
}
|
||||
|
||||
unique_ptr<vector<string>> SceneAsset::getTargetNames(const char *meshName) {
|
||||
if (!_asset) {
|
||||
Log("No asset, ignoring call.");
|
||||
return nullptr;
|
||||
}
|
||||
Log("Retrieving morph target names for mesh %s", meshName);
|
||||
unique_ptr<vector<string>> names = make_unique<vector<string>>();
|
||||
const Entity *entities = _asset->getEntities();
|
||||
RenderableManager &rm = _engine->getRenderableManager();
|
||||
for (int i = 0; i < _asset->getEntityCount(); i++) {
|
||||
Entity e = entities[i];
|
||||
auto inst = _ncm->getInstance(e);
|
||||
const char *name = _ncm->getName(inst);
|
||||
Log("Got entity instance name %s", name);
|
||||
if (strcmp(name, meshName) == 0) {
|
||||
size_t count = _asset->getMorphTargetCountAt(e);
|
||||
for (int j = 0; j < count; j++) {
|
||||
const char *morphName = _asset->getMorphTargetNameAt(e, j);
|
||||
names->push_back(morphName);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
void SceneAsset::transformToUnitCube()
|
||||
{
|
||||
if (!_asset)
|
||||
{
|
||||
Log("No asset, cannot transform.");
|
||||
return;
|
||||
}
|
||||
auto &tm = _engine->getTransformManager();
|
||||
auto aabb = _asset->getBoundingBox();
|
||||
auto center = aabb.center();
|
||||
auto halfExtent = aabb.extent();
|
||||
auto maxExtent = max(halfExtent) * 2;
|
||||
auto scaleFactor = 2.0f / maxExtent;
|
||||
auto transform = math::mat4f::scaling(scaleFactor) * math::mat4f::translation(-center);
|
||||
tm.setTransform(tm.getInstance(_asset->getRoot()), transform);
|
||||
}
|
||||
|
||||
const utils::Entity* SceneAsset::getCameraEntities() {
|
||||
return _asset->getCameraEntities();
|
||||
}
|
||||
|
||||
size_t SceneAsset::getCameraEntityCount() {
|
||||
return _asset->getCameraEntityCount();
|
||||
}
|
||||
|
||||
} // namespace polyvox
|
||||
83
ios/src/SceneAsset.hpp
Normal file
83
ios/src/SceneAsset.hpp
Normal file
@@ -0,0 +1,83 @@
|
||||
#pragma once
|
||||
|
||||
#include <filament/Engine.h>
|
||||
#include <filament/RenderableManager.h>
|
||||
#include <filament/Renderer.h>
|
||||
#include <filament/Scene.h>
|
||||
|
||||
#include <gltfio/AssetLoader.h>
|
||||
#include <gltfio/FilamentAsset.h>
|
||||
#include <gltfio/ResourceLoader.h>
|
||||
|
||||
#include <utils/NameComponentManager.h>
|
||||
|
||||
#include "SceneResources.hpp"
|
||||
|
||||
|
||||
namespace polyvox {
|
||||
using namespace filament;
|
||||
using namespace filament::gltfio;
|
||||
using namespace utils;
|
||||
|
||||
using namespace std;
|
||||
class SceneAsset {
|
||||
friend class SceneAssetLoader;
|
||||
public:
|
||||
SceneAsset(FilamentAsset* asset, Engine* engine, NameComponentManager* ncm);
|
||||
~SceneAsset();
|
||||
|
||||
unique_ptr<vector<string>> getTargetNames(const char* meshName);
|
||||
unique_ptr<vector<string>> getAnimationNames();
|
||||
|
||||
///
|
||||
/// Update the bone/morph target animations to reflect the current frame (if applicable).
|
||||
///
|
||||
void updateAnimations();
|
||||
|
||||
///
|
||||
/// Immediately stop the currently playing animation. NOOP if no animation is playing.
|
||||
///
|
||||
void stopAnimation();
|
||||
|
||||
///
|
||||
/// Play an embedded animation (i.e. an animation node embedded in the GLTF asset). If [loop] is true, the animation will repeat indefinitely.
|
||||
///
|
||||
void playAnimation(int index, bool loop);
|
||||
|
||||
///
|
||||
/// Manually set the weights for all morph targets in the assets to the provided values.
|
||||
/// See [animateWeights] if you want to automatically
|
||||
///
|
||||
void applyWeights(float* weights, int count);
|
||||
|
||||
///
|
||||
/// Update the asset's morph target weights every "frame" (which is an arbitrary length of time, i.e. this is not the same as a frame at the framerate of the underlying rendering framework).
|
||||
/// Accordingly:
|
||||
/// length(data) = numWeights * numFrames
|
||||
/// total_animation_duration_in_ms = number_of_frames * frameLengthInMs
|
||||
///
|
||||
void animateWeights(float* data, int numWeights, int numFrames, float frameLengthInMs);
|
||||
|
||||
void transformToUnitCube();
|
||||
|
||||
const utils::Entity* getCameraEntities();
|
||||
|
||||
size_t getCameraEntityCount();
|
||||
|
||||
|
||||
private:
|
||||
FilamentAsset* _asset = nullptr;
|
||||
Engine* _engine = nullptr;
|
||||
NameComponentManager* _ncm;
|
||||
void updateMorphAnimation();
|
||||
void updateEmbeddedAnimation();
|
||||
|
||||
Animator* _animator;
|
||||
|
||||
// animation flags;
|
||||
bool isAnimating;
|
||||
unique_ptr<MorphAnimationStatus> _morphAnimationBuffer;
|
||||
unique_ptr<BoneAnimationStatus> _boneAnimationStatus;
|
||||
|
||||
};
|
||||
}
|
||||
123
ios/src/SceneAssetLoader.cpp
Normal file
123
ios/src/SceneAssetLoader.cpp
Normal file
@@ -0,0 +1,123 @@
|
||||
#include "SceneAssetLoader.hpp"
|
||||
#include "Log.hpp"
|
||||
|
||||
#include <gltfio/Animator.h>
|
||||
|
||||
namespace polyvox {
|
||||
|
||||
using namespace filament;
|
||||
using namespace filament::gltfio;
|
||||
|
||||
SceneAssetLoader::SceneAssetLoader(LoadResource loadResource,
|
||||
FreeResource freeResource,
|
||||
AssetLoader *assetLoader,
|
||||
ResourceLoader *resourceLoader,
|
||||
NameComponentManager *ncm, Engine *engine,
|
||||
Scene *scene)
|
||||
: _loadResource(loadResource), _freeResource(freeResource),
|
||||
_assetLoader(assetLoader), _resourceLoader(resourceLoader), _ncm(ncm),
|
||||
_engine(engine), _scene(scene) {}
|
||||
|
||||
SceneAsset *SceneAssetLoader::fromGltf(const char *uri,
|
||||
const char *relativeResourcePath) {
|
||||
ResourceBuffer rbuf = _loadResource(uri);
|
||||
|
||||
// Parse the glTF file and create Filament entities.
|
||||
Log("Creating asset from JSON");
|
||||
FilamentAsset *asset =
|
||||
_assetLoader->createAssetFromJson((uint8_t *)rbuf.data, rbuf.size);
|
||||
Log("Created asset from JSON");
|
||||
|
||||
if (!asset) {
|
||||
Log("Unable to parse asset");
|
||||
return nullptr;
|
||||
}
|
||||
Log("Loading relative resources");
|
||||
|
||||
const char *const *const resourceUris = asset->getResourceUris();
|
||||
const size_t resourceUriCount = asset->getResourceUriCount();
|
||||
|
||||
Log("Loading %d resources for asset", resourceUriCount);
|
||||
|
||||
for (size_t i = 0; i < resourceUriCount; i++) {
|
||||
string uri =
|
||||
string(relativeResourcePath) + string("/") + string(resourceUris[i]);
|
||||
Log("Creating resource buffer for resource at %s", uri.c_str());
|
||||
ResourceBuffer buf = _loadResource(uri.c_str());
|
||||
|
||||
// using FunctionCallback = std::function<void(void*, unsigned int, void
|
||||
// *)>; auto cb = [&] (void * ptr, unsigned int len, void * misc) {
|
||||
// };
|
||||
// FunctionCallback fcb = cb;
|
||||
|
||||
ResourceLoader::BufferDescriptor b(buf.data, buf.size);
|
||||
_resourceLoader->addResourceData(resourceUris[i], std::move(b));
|
||||
_freeResource(buf);
|
||||
}
|
||||
|
||||
_resourceLoader->loadResources(asset);
|
||||
const Entity *entities = asset->getEntities();
|
||||
RenderableManager &rm = _engine->getRenderableManager();
|
||||
for (int i = 0; i < asset->getEntityCount(); i++) {
|
||||
Entity e = entities[i];
|
||||
auto inst = rm.getInstance(e);
|
||||
rm.setCulling(inst, false);
|
||||
}
|
||||
|
||||
asset->getAnimator()->updateBoneMatrices();
|
||||
|
||||
_scene->addEntities(asset->getEntities(), asset->getEntityCount());
|
||||
|
||||
Log("Loaded relative resources");
|
||||
asset->releaseSourceData();
|
||||
|
||||
Log("Load complete for GLTF at URI %s", uri);
|
||||
return new SceneAsset(asset, _engine, _ncm);
|
||||
}
|
||||
|
||||
SceneAsset *SceneAssetLoader::fromGlb(const char *uri) {
|
||||
Log("Loading GLB at URI %s", uri);
|
||||
|
||||
ResourceBuffer rbuf = _loadResource(uri);
|
||||
|
||||
FilamentAsset *asset = _assetLoader->createAssetFromBinary(
|
||||
(const uint8_t *)rbuf.data, rbuf.size);
|
||||
|
||||
if (!asset) {
|
||||
Log("Unknown error loading GLB asset.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int entityCount = asset->getEntityCount();
|
||||
|
||||
_scene->addEntities(asset->getEntities(), entityCount);
|
||||
|
||||
Log("Added %d entities to scene", entityCount);
|
||||
_resourceLoader->loadResources(asset);
|
||||
|
||||
const Entity *entities = asset->getEntities();
|
||||
RenderableManager &rm = _engine->getRenderableManager();
|
||||
for (int i = 0; i < asset->getEntityCount(); i++) {
|
||||
Entity e = entities[i];
|
||||
auto inst = rm.getInstance(e);
|
||||
// check this
|
||||
rm.setCulling(inst, false);
|
||||
}
|
||||
|
||||
_freeResource(rbuf);
|
||||
|
||||
asset->getAnimator()->updateBoneMatrices();
|
||||
|
||||
asset->releaseSourceData();
|
||||
|
||||
Log("Successfully loaded GLB.");
|
||||
return new SceneAsset(asset, _engine, _ncm);
|
||||
}
|
||||
|
||||
void SceneAssetLoader::remove(SceneAsset *asset) {
|
||||
_resourceLoader->evictResourceData();
|
||||
_scene->removeEntities(asset->_asset->getEntities(),
|
||||
asset->_asset->getEntityCount());
|
||||
_assetLoader->destroyAsset(asset->_asset);
|
||||
}
|
||||
} // namespace polyvox
|
||||
42
ios/src/SceneAssetLoader.hpp
Normal file
42
ios/src/SceneAssetLoader.hpp
Normal file
@@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
#include <filament/Scene.h>
|
||||
|
||||
#include <gltfio/AssetLoader.h>
|
||||
#include <gltfio/FilamentAsset.h>
|
||||
#include <gltfio/ResourceLoader.h>
|
||||
|
||||
#include "SceneResources.hpp"
|
||||
#include "SceneAsset.hpp"
|
||||
|
||||
namespace polyvox {
|
||||
using namespace filament;
|
||||
using namespace filament::gltfio;
|
||||
using namespace utils;
|
||||
|
||||
class SceneAssetLoader {
|
||||
public:
|
||||
SceneAssetLoader(
|
||||
LoadResource loadResource,
|
||||
FreeResource freeResource,
|
||||
AssetLoader* assetLoader,
|
||||
ResourceLoader* resourceLoader,
|
||||
NameComponentManager* ncm,
|
||||
Engine* engine,
|
||||
Scene* scene);
|
||||
SceneAsset* fromGltf(const char* uri, const char* relativeResourcePath);
|
||||
SceneAsset* fromGlb(const char* uri);
|
||||
void remove(SceneAsset* asset);
|
||||
|
||||
private:
|
||||
LoadResource _loadResource;
|
||||
FreeResource _freeResource;
|
||||
AssetLoader* _assetLoader;
|
||||
ResourceLoader* _resourceLoader;
|
||||
NameComponentManager* _ncm;
|
||||
Scene* _scene;
|
||||
Engine* _engine;
|
||||
|
||||
|
||||
};
|
||||
}
|
||||
84
ios/src/SceneResources.hpp
Normal file
84
ios/src/SceneResources.hpp
Normal file
@@ -0,0 +1,84 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace polyvox {
|
||||
|
||||
//
|
||||
// Pairs a memory buffer with an ID that can be used to unload the backing asset if needed.
|
||||
// Use this when you want to load an asset from a resource that requires more than just `free` on the underlying buffer.
|
||||
// e.g.
|
||||
// ```
|
||||
// uint64_t id = get_next_resource_id();
|
||||
// AAsset *asset = AAssetManager_open(am, name, AASSET_MODE_BUFFER);
|
||||
// off_t length = AAsset_getLength(asset);
|
||||
// const void * buffer = AAsset_getBuffer(asset);
|
||||
// uint8_t *buf = new uint8_t[length ];
|
||||
// memcpy(buf,buffer, length);
|
||||
// ResourceBuffer rb(buf, length, id);
|
||||
// ...
|
||||
// ...
|
||||
// (elsewhere)
|
||||
// AAsset* asset = get_asset_from_id(rb.id);
|
||||
// AAsset_close(asset);
|
||||
// free_asset_id(rb.id);
|
||||
//
|
||||
struct ResourceBuffer {
|
||||
ResourceBuffer(const void* data, const uint32_t size, const uint32_t id) : data(data), size(size), id(id) {};
|
||||
|
||||
ResourceBuffer& operator=(ResourceBuffer other)
|
||||
{
|
||||
data = other.data;
|
||||
size = other.size;
|
||||
id = other.id;
|
||||
return *this;
|
||||
}
|
||||
const void* data;
|
||||
uint32_t size;
|
||||
uint32_t id;
|
||||
};
|
||||
|
||||
//
|
||||
// Typedef for any function that loads a resource into a ResourceBuffer from an asset URI.
|
||||
//
|
||||
using LoadResource = std::function<ResourceBuffer(const char* uri)>;
|
||||
|
||||
//
|
||||
// Typedef for any function that frees a ResourceBuffer.
|
||||
//
|
||||
using FreeResource = std::function<void (ResourceBuffer)>;
|
||||
|
||||
typedef std::chrono::time_point<std::chrono::high_resolution_clock> time_point_t;
|
||||
|
||||
//
|
||||
// Holds the current state of a bone animation embeded in a GLTF asset.
|
||||
//
|
||||
struct BoneAnimationStatus {
|
||||
BoneAnimationStatus(int animationIndex, float duration, bool loop) : animationIndex(animationIndex), duration(duration), loop(loop) {}
|
||||
bool hasStarted = false;
|
||||
int animationIndex;
|
||||
float duration = 0;
|
||||
time_point_t lastTime;
|
||||
bool loop;
|
||||
};
|
||||
|
||||
//
|
||||
// Holds the current state of a morph-target animation in a GLTF asset.
|
||||
//
|
||||
struct MorphAnimationStatus {
|
||||
|
||||
MorphAnimationStatus(float* frameData,
|
||||
int numWeights,
|
||||
int numFrames,
|
||||
float frameLength) : frameData(frameData), numWeights(numWeights), numFrames(numFrames), frameLengthInMs(frameLength) {
|
||||
}
|
||||
|
||||
int frameIndex = -1;
|
||||
int numFrames;
|
||||
float frameLengthInMs;
|
||||
time_point_t startTime;
|
||||
|
||||
float* frameData;
|
||||
int numWeights;
|
||||
};
|
||||
}
|
||||
@@ -7,11 +7,11 @@ using namespace std;
|
||||
|
||||
namespace polyvox {
|
||||
|
||||
class streambuf : public std::streambuf
|
||||
class StreamBufferAdapter : public std::streambuf
|
||||
{
|
||||
public:
|
||||
streambuf(const char *begin, const char *end);
|
||||
~streambuf() {
|
||||
StreamBufferAdapter(const char *begin, const char *end);
|
||||
~StreamBufferAdapter() {
|
||||
|
||||
}
|
||||
streamsize size();
|
||||
@@ -26,16 +26,16 @@ class streambuf : public std::streambuf
|
||||
|
||||
};
|
||||
|
||||
streambuf::streambuf(const char *begin, const char *end)
|
||||
StreamBufferAdapter::StreamBufferAdapter(const char *begin, const char *end)
|
||||
{
|
||||
setg((char*)begin, (char*)begin, (char*)end);
|
||||
}
|
||||
|
||||
streamsize streambuf::size() {
|
||||
streamsize StreamBufferAdapter::size() {
|
||||
return egptr() - eback();
|
||||
}
|
||||
|
||||
streambuf::int_type streambuf::underflow()
|
||||
streambuf::int_type StreamBufferAdapter::underflow()
|
||||
{
|
||||
if (gptr() == egptr()) {
|
||||
return traits_type::eof();
|
||||
@@ -43,7 +43,7 @@ streambuf::int_type streambuf::underflow()
|
||||
return *(gptr());
|
||||
}
|
||||
|
||||
streambuf::int_type streambuf::uflow()
|
||||
streambuf::int_type StreamBufferAdapter::uflow()
|
||||
{
|
||||
if (gptr() == egptr()) {
|
||||
return traits_type::eof();
|
||||
@@ -53,7 +53,7 @@ streambuf::int_type streambuf::uflow()
|
||||
return *(gptr());
|
||||
}
|
||||
|
||||
streambuf::int_type streambuf::pbackfail(int_type ch)
|
||||
streambuf::int_type StreamBufferAdapter::pbackfail(int_type ch)
|
||||
{
|
||||
if (gptr() == eback() || (ch != traits_type::eof() && ch != gptr()[-1]))
|
||||
return traits_type::eof();
|
||||
@@ -61,12 +61,12 @@ streambuf::int_type streambuf::pbackfail(int_type ch)
|
||||
return *(gptr());
|
||||
}
|
||||
|
||||
streamsize streambuf::showmanyc()
|
||||
streamsize StreamBufferAdapter::showmanyc()
|
||||
{
|
||||
return egptr() - gptr();
|
||||
}
|
||||
|
||||
streampos streambuf::seekoff(streamoff off, ios_base::seekdir way, ios_base::openmode which = ios_base::in) {
|
||||
streampos StreamBufferAdapter::seekoff(streamoff off, ios_base::seekdir way, ios_base::openmode which = ios_base::in) {
|
||||
if(way == ios_base::beg) {
|
||||
setg(eback(), eback()+off, egptr());
|
||||
} else if(way == ios_base::cur) {
|
||||
@@ -77,7 +77,7 @@ streampos streambuf::seekoff(streamoff off, ios_base::seekdir way, ios_base::ope
|
||||
return gptr() - eback();
|
||||
}
|
||||
|
||||
streampos streambuf::seekpos(streampos sp, ios_base::openmode which = ios_base::in) {
|
||||
streampos StreamBufferAdapter::seekpos(streampos sp, ios_base::openmode which = ios_base::in) {
|
||||
return seekoff(sp - pos_type(off_type(0)), std::ios_base::beg, which);
|
||||
}
|
||||
}
|
||||
@@ -5,11 +5,15 @@
|
||||
|
||||
namespace polyvox {
|
||||
|
||||
class streambuf : public std::streambuf
|
||||
//
|
||||
// A generic adapter to expose any contiguous section of memory as a std::streambuf.
|
||||
// Mostly for Android/iOS assets which may not be able to be directly loaded as streams.
|
||||
//
|
||||
class StreamBufferAdapter : public std::streambuf
|
||||
{
|
||||
public:
|
||||
streambuf(const char *begin, const char *end);
|
||||
~streambuf() {
|
||||
StreamBufferAdapter(const char *begin, const char *end);
|
||||
~StreamBufferAdapter() {
|
||||
|
||||
}
|
||||
streamsize size();
|
||||
@@ -2,6 +2,8 @@ import 'dart:async';
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
typedef FilamentAsset = int;
|
||||
|
||||
abstract class FilamentController {
|
||||
void onFilamentViewCreated(int id);
|
||||
Future setBackgroundImage(String path);
|
||||
@@ -9,22 +11,21 @@ abstract class FilamentController {
|
||||
Future removeSkybox();
|
||||
Future loadIbl(String path);
|
||||
Future removeIbl();
|
||||
Future loadGlb(String path);
|
||||
Future loadGltf(String path, String relativeResourcePath);
|
||||
Future<FilamentAsset> loadGlb(String path);
|
||||
Future<FilamentAsset> loadGltf(String path, String relativeResourcePath);
|
||||
Future panStart(double x, double y);
|
||||
Future panUpdate(double x, double y);
|
||||
Future panEnd();
|
||||
Future rotateStart(double x, double y);
|
||||
Future rotateUpdate(double x, double y);
|
||||
Future rotateEnd();
|
||||
Future applyWeights(List<double> weights);
|
||||
Future<List<String>> getTargetNames(String meshName);
|
||||
Future<List<String>> getAnimationNames();
|
||||
Future releaseSourceAssets();
|
||||
Future removeAsset();
|
||||
Future playAnimation(int index, {bool loop = false});
|
||||
Future stopAnimation();
|
||||
Future setCamera(String name);
|
||||
Future applyWeights(FilamentAsset asset, List<double> weights);
|
||||
Future<List<String>> getTargetNames(FilamentAsset asset, String meshName);
|
||||
Future<List<String>> getAnimationNames(FilamentAsset asset);
|
||||
Future removeAsset(FilamentAsset asset);
|
||||
Future playAnimation(FilamentAsset asset, int index, {bool loop = false});
|
||||
Future stopAnimation(FilamentAsset asset);
|
||||
Future setCamera(FilamentAsset asset, String name);
|
||||
|
||||
///
|
||||
/// Set the weights of all morph targets in the mesh to the specified weights at successive frames (where each frame requires a duration of [frameLengthInMs].
|
||||
@@ -32,9 +33,8 @@ abstract class FilamentController {
|
||||
/// Each frame is [numWeights] in length, where each entry is the weight to be applied to the morph target located at that index in the mesh primitive at that frame.
|
||||
/// In other words, weights is a contiguous sequence of floats of size W*F, where W is the number of weights and F is the number of frames
|
||||
///
|
||||
Future animate(
|
||||
Future animate(FilamentAsset asset,
|
||||
List<double> data, int numWeights, int numFrames, double frameLengthInMs);
|
||||
Future createMorpher(String meshName, List<int> primitives);
|
||||
Future zoom(double z);
|
||||
}
|
||||
|
||||
@@ -86,15 +86,18 @@ class PolyvoxFilamentController extends FilamentController {
|
||||
await _channel.invokeMethod("removeIbl");
|
||||
}
|
||||
|
||||
Future loadGlb(String path) async {
|
||||
Future<FilamentAsset> loadGlb(String path) async {
|
||||
print("Loading GLB at $path ");
|
||||
await _channel.invokeMethod("loadGlb", path);
|
||||
var asset = await _channel.invokeMethod("loadGlb", path);
|
||||
print("Got asset : $asset ");
|
||||
return asset as FilamentAsset;
|
||||
}
|
||||
|
||||
Future loadGltf(String path, String relativeResourcePath) async {
|
||||
Future<FilamentAsset> loadGltf(String path, String relativeResourcePath) async {
|
||||
print(
|
||||
"Loading GLTF at $path with relative resource path $relativeResourcePath");
|
||||
await _channel.invokeMethod("loadGltf", [path, relativeResourcePath]);
|
||||
var asset = await _channel.invokeMethod("loadGltf", [path, relativeResourcePath]);
|
||||
return asset as FilamentAsset;
|
||||
}
|
||||
|
||||
Future panStart(double x, double y) async {
|
||||
@@ -121,53 +124,45 @@ class PolyvoxFilamentController extends FilamentController {
|
||||
await _channel.invokeMethod("rotateEnd");
|
||||
}
|
||||
|
||||
Future applyWeights(List<double> weights) async {
|
||||
await _channel.invokeMethod("applyWeights", weights);
|
||||
Future applyWeights(FilamentAsset asset, List<double> weights) async {
|
||||
await _channel.invokeMethod("applyWeights", [asset, weights]);
|
||||
}
|
||||
|
||||
Future<List<String>> getTargetNames(String meshName) async {
|
||||
var result = (await _channel.invokeMethod("getTargetNames", meshName))
|
||||
Future<List<String>> getTargetNames(FilamentAsset asset, String meshName) async {
|
||||
var result = (await _channel.invokeMethod("getTargetNames", [asset, meshName]))
|
||||
.cast<String>();
|
||||
return result;
|
||||
}
|
||||
|
||||
Future<List<String>> getAnimationNames() async {
|
||||
Future<List<String>> getAnimationNames(FilamentAsset asset) async {
|
||||
var result =
|
||||
(await _channel.invokeMethod("getAnimationNames")).cast<String>();
|
||||
(await _channel.invokeMethod("getAnimationNames", asset)).cast<String>();
|
||||
return result;
|
||||
}
|
||||
|
||||
Future animate(List<double> weights, int numWeights, int numFrames,
|
||||
Future animate(FilamentAsset asset, List<double> weights, int numWeights, int numFrames,
|
||||
double frameLengthInMs) async {
|
||||
await _channel.invokeMethod(
|
||||
"animateWeights", [weights, numWeights, numFrames, frameLengthInMs]);
|
||||
"animateWeights", [asset, weights, numWeights, numFrames, frameLengthInMs]);
|
||||
}
|
||||
|
||||
Future releaseSourceAssets() async {
|
||||
await _channel.invokeMethod("releaseSourceAssets");
|
||||
}
|
||||
|
||||
Future removeAsset() async {
|
||||
await _channel.invokeMethod("removeAsset");
|
||||
Future removeAsset(FilamentAsset asset) async {
|
||||
await _channel.invokeMethod("removeAsset", asset);
|
||||
}
|
||||
|
||||
Future zoom(double z) async {
|
||||
await _channel.invokeMethod("zoom", z);
|
||||
}
|
||||
|
||||
Future createMorpher(String meshName, List<int> primitives) async {
|
||||
await _channel.invokeMethod("createMorpher", [meshName, primitives]);
|
||||
Future playAnimation(FilamentAsset asset, int index, {bool loop = false}) async {
|
||||
await _channel.invokeMethod("playAnimation", [asset, index, loop]);
|
||||
}
|
||||
|
||||
Future playAnimation(int index, {bool loop = false}) async {
|
||||
await _channel.invokeMethod("playAnimation", [index, loop]);
|
||||
}
|
||||
|
||||
Future stopAnimation() async {
|
||||
Future stopAnimation(FilamentAsset asset) async {
|
||||
await _channel.invokeMethod("stopAnimation");
|
||||
}
|
||||
|
||||
Future setCamera(String name) async {
|
||||
await _channel.invokeMethod("setCamera", name);
|
||||
Future setCamera(FilamentAsset asset, String name) async {
|
||||
await _channel.invokeMethod("setCamera", [asset, name]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ version: 0.0.1
|
||||
homepage:
|
||||
|
||||
environment:
|
||||
sdk: ">=2.12.0 <3.0.0"
|
||||
sdk: ">=2.17.1 <3.0.0"
|
||||
flutter: ">=1.20.0"
|
||||
|
||||
dependencies:
|
||||
|
||||
Reference in New Issue
Block a user