decouple assets from viewer to allow independent addition/removal/animation/etc

This commit is contained in:
Nick Fisher
2022-08-13 00:25:56 +10:00
parent 485e2bc0f4
commit e51577cf6b
30 changed files with 1124 additions and 993 deletions

View File

@@ -13,9 +13,11 @@ add_library(
src/main/cpp/KtxReader1.cpp src/main/cpp/KtxReader1.cpp
src/main/cpp/StbProvider.cpp src/main/cpp/StbProvider.cpp
src/main/cpp/JobSystem.cpp src/main/cpp/JobSystem.cpp
../ios/src/SceneAssetLoader.cpp
../ios/src/FilamentViewer.cpp ../ios/src/FilamentViewer.cpp
../ios/src/streambuf.cpp ../ios/src/StreamBufferAdapter.cpp
../ios/src/imagematerial.c ../ios/src/image/imagematerial.c
../ios/src/SceneAsset.cpp
) )
target_link_libraries( target_link_libraries(

View File

@@ -1,4 +1,5 @@
#include "FilamentViewer.hpp" #include "FilamentViewer.hpp"
#include "SceneAsset.hpp"
#include <android/asset_manager.h> #include <android/asset_manager.h>
#include <android/asset_manager_jni.h> #include <android/asset_manager_jni.h>
#include <android/native_window_jni.h> #include <android/native_window_jni.h>
@@ -41,6 +42,8 @@ static void freeResource(ResourceBuffer rb) {
AAsset* asset = _assets[rb.id]; AAsset* asset = _assets[rb.id];
if(asset) { if(asset) {
AAsset_close(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; _assets[rb.id] = nullptr;
} }
@@ -68,16 +71,16 @@ extern "C" {
((FilamentViewer*)viewer)->removeIbl(); ((FilamentViewer*)viewer)->removeIbl();
} }
void load_glb(void* viewer, const char* assetPath) { void* load_glb(void* viewer, const char* assetPath) {
((FilamentViewer*)viewer)->loadGlb(assetPath); return ((FilamentViewer*)viewer)->loadGlb(assetPath);
} }
void load_gltf(void* viewer, const char* assetPath, const char* relativePath) { void* load_gltf(void* viewer, const char* assetPath, const char* relativePath) {
((FilamentViewer*)viewer)->loadGltf(assetPath, relativePath); return ((FilamentViewer*)viewer)->loadGltf(assetPath, relativePath);
} }
bool set_camera(void* viewer, const char* nodeName) { bool set_camera(void* viewer, void* asset, const char* nodeName) {
return ((FilamentViewer*)viewer)->setCamera(nodeName); return ((FilamentViewer*)viewer)->setCamera((SceneAsset*)asset, nodeName);
} }
void* filament_viewer_new( void* filament_viewer_new(
@@ -141,22 +144,22 @@ extern "C" {
((FilamentViewer*)viewer)->manipulator->grabEnd(); ((FilamentViewer*)viewer)->manipulator->grabEnd();
} }
void apply_weights(void* viewer, float* weights, int count) { void apply_weights(void* asset, float* weights, int count) {
((FilamentViewer*)viewer)->applyWeights(weights, 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); __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); __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) { char** get_animation_names(void* asset, int* countPtr) {
auto names = ((FilamentViewer*)viewer)->getAnimationNames(); auto names = ((SceneAsset*)asset)->getAnimationNames();
__android_log_print(ANDROID_LOG_VERBOSE, "filament_api", "Got %d animation names", names->size()); __android_log_print(ANDROID_LOG_VERBOSE, "filament_api", "Got %d animation names", names->size());
char** names_c; char** names_c;
names_c = new char*[names->size()]; names_c = new char*[names->size()];
@@ -168,8 +171,8 @@ extern "C" {
return names_c; return names_c;
} }
char** get_target_names(void* viewer, char* meshName, int* countPtr ) { char** get_target_names(void* asset, char* meshName, int* countPtr ) {
unique_ptr<vector<string>> names = ((FilamentViewer*)viewer)->getTargetNames(meshName); unique_ptr<vector<string>> names = ((SceneAsset*)asset)->getTargetNames(meshName);
__android_log_print(ANDROID_LOG_VERBOSE, "filament_api", "Got %d names", names->size()); __android_log_print(ANDROID_LOG_VERBOSE, "filament_api", "Got %d names", names->size());
@@ -188,8 +191,8 @@ extern "C" {
free(ptr); free(ptr);
} }
void remove_asset(void* viewer) { void remove_asset(void* viewer, void* asset) {
((FilamentViewer*)viewer)->removeAsset(); ((FilamentViewer*)viewer)->removeAsset((SceneAsset*)asset);
} }
} }

View File

@@ -32,7 +32,7 @@ interface FilamentInterop : Library {
fun load_gltf(viewer:Pointer, uri:String, relativeResourcePath:String) : Pointer; 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); fun render(viewer:Pointer);
@@ -50,21 +50,19 @@ interface FilamentInterop : Library {
fun grab_end(viewer:Pointer) 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 free_pointer(ptr:Pointer, size:Int);
fun release_source_assets(viewer:Pointer); fun remove_asset(viewer:Pointer, asset:Pointer);
fun remove_asset(viewer:Pointer);
fun remove_skybox(viewer:Pointer); fun remove_skybox(viewer:Pointer);

View File

@@ -192,32 +192,31 @@ PlatformView {
val key = loader.getLookupKeyForAsset(call.arguments as String) val key = loader.getLookupKeyForAsset(call.arguments as String)
val key2 = loader.getLookupKeyForAsset(call.arguments as String, context.packageName) val key2 = loader.getLookupKeyForAsset(call.arguments as String, context.packageName)
val path = loader.findAppBundlePath() val path = loader.findAppBundlePath()
Log.v(TAG, "key ${key} key2 ${key2} path : ${path}")
_lib.load_glb( val assetPtr = _lib.load_glb(
_viewer!!, _viewer!!,
key key
) )
result.success("OK"); result.success(Pointer.nativeValue(assetPtr));
} }
"loadGltf" -> { "loadGltf" -> {
if (_viewer == null) if (_viewer == null)
return; return;
val args = call.arguments as ArrayList<Any?> val args = call.arguments as ArrayList<Any?>
val loader = FlutterInjector.instance().flutterLoader() val loader = FlutterInjector.instance().flutterLoader()
_lib.load_gltf( val assetPtr = _lib.load_gltf(
_viewer!!, _viewer!!,
loader.getLookupKeyForAsset(args[0] as String), loader.getLookupKeyForAsset(args[0] as String),
loader.getLookupKeyForAsset(args[1] as String) loader.getLookupKeyForAsset(args[1] as String)
) )
result.success("OK"); result.success(Pointer.nativeValue(assetPtr));
} }
"setCamera" -> { "setCamera" -> {
if (_viewer == null) val args = call.arguments as ArrayList<*>
return;
val success = _lib.set_camera( val success = _lib.set_camera(
_viewer!!, _viewer!!,
call.arguments as String Pointer(args[0] as Long),
args[1] as String,
) )
if(success) { if(success) {
result.success("OK"); result.success("OK");
@@ -236,9 +235,10 @@ PlatformView {
return; return;
val countPtr = IntByReference(); 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) { for(i in 0..countPtr.value-1) {
Log.v(TAG, "Got target names ${names[i]} ${names[i].length}") Log.v(TAG, "Got target names ${names[i]} ${names[i].length}")
@@ -246,16 +246,14 @@ PlatformView {
val namesAsList = names.toCollection(ArrayList()) val namesAsList = names.toCollection(ArrayList())
_lib.free_pointer(arrPtr, countPtr.getValue()) _lib.free_pointer(namesPtr, countPtr.getValue())
result.success(namesAsList) result.success(namesAsList)
} }
"getAnimationNames" -> { "getAnimationNames" -> {
if(_viewer == null) val assetPtr = Pointer(call.arguments as Long)
return;
val countPtr = IntByReference(); 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); val names = arrPtr.getStringArray(0, countPtr.value);
@@ -269,23 +267,22 @@ PlatformView {
result.success(names.toCollection(ArrayList())) result.success(names.toCollection(ArrayList()))
} }
"applyWeights" -> { "applyWeights" -> {
if(_viewer == null) val args = call.arguments as ArrayList<*>
return; val assetPtr = Pointer(args[0] as Long)
val weights = call.arguments as ArrayList<Float>; 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"); result.success("OK");
} }
"animateWeights" -> { "animateWeights" -> {
if(_viewer == null)
return;
val args = call.arguments as ArrayList<Any?> val args = call.arguments as ArrayList<Any?>
val frames = args[0] as ArrayList<Float>; val assetPtr = Pointer(args[0] as Long)
val numWeights = args[1] as Int val frames = args[1] as ArrayList<Float>;
val numFrames = args[2] as Int val numWeights = args[2] as Int
val frameLenInMs = args[3] as Double 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"); result.success("OK");
} }
"panStart" -> { "panStart" -> {
@@ -331,12 +328,12 @@ PlatformView {
result.success("OK"); result.success("OK");
} }
"removeAsset" -> { "removeAsset" -> {
_lib.remove_asset(_viewer!!) _lib.remove_asset(_viewer!!, Pointer(call.arguments as Long))
result.success("OK"); result.success("OK");
} }
"playAnimation" -> { "playAnimation" -> {
val args = call.arguments as ArrayList<Any?> 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") result.success("OK")
} }
else -> { else -> {

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c38fa36cbbbe969a2ae015fe56752343e8d06007c6eea28c56e22ffd56a8db1a
size 2570831

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:9fb09944d65a155fc5b6522f296dd875df02fc2944733a35eb09bec23bbabdcd
size 813192

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:674e74fbef9b017aa071cd985bcc34c34c96fc57051a0724b6c23de78cd22db8 oid sha256:1f53c13e2ee7cd36156fa3bcfcb9a199b5999c3cb15d7f964a326105ac7d0419
size 2352 size 11032

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:defdd358826b3b8f58237c38e36b2a03d768f2307407152c79621d3358001f71 oid sha256:0245800fa846c2a3c15cb1d2aa6ded86fcf8355e568907dcd50e073c589f85d6
size 2095464 size 2095464

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:b7e5e33a6bbaee9f0d89f8525fa9c3723f9ace8ebaaf00b3a9dc8cdf7c6c1095 oid sha256:e4050117d400c027dd47baa7b4a9ed46f7174b0690def9d70643d3c364faf758
size 1572932 size 1572932

View File

@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:ee52c238ccf3188a21533d6e096f74cadab26720d0fa710ad9737484db8ee4cb
size 98372

View File

@@ -16,6 +16,9 @@ class MyApp extends StatefulWidget {
class _MyAppState extends State<MyApp> { class _MyAppState extends State<MyApp> {
final FilamentController _filamentController = PolyvoxFilamentController(); final FilamentController _filamentController = PolyvoxFilamentController();
FilamentAsset? _cube;
FilamentAsset? _flightHelmet;
final weights = List.filled(255, 0.0); final weights = List.filled(255, 0.0);
List<String> _targets = []; List<String> _targets = [];
bool _loop = false; bool _loop = false;
@@ -32,22 +35,24 @@ class _MyAppState extends State<MyApp> {
home: Scaffold( home: Scaffold(
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
body: Column(children: [ body: Column(children: [
Expanded(child:SizedBox( Expanded(
height:200, width:200, child: SizedBox(
height: 200,
width: 200,
child: FilamentWidget( child: FilamentWidget(
controller: _filamentController, controller: _filamentController,
))), ))),
Expanded( Expanded(
child: SingleChildScrollView(child:Wrap( child: SingleChildScrollView(
child: Wrap(
alignment: WrapAlignment.end, alignment: WrapAlignment.end,
crossAxisAlignment: WrapCrossAlignment.end, crossAxisAlignment: WrapCrossAlignment.end,
children: [ children: [
ElevatedButton( ElevatedButton(
child: const Text('load background image'), child: const Text('load background image'),
onPressed: () async { onPressed: () async {
await _filamentController.setBackgroundImage( await _filamentController
'assets/background3.png'); .setBackgroundImage('assets/background.png');
}), }),
ElevatedButton( ElevatedButton(
child: const Text('load skybox'), child: const Text('load skybox'),
@@ -61,46 +66,57 @@ class _MyAppState extends State<MyApp> {
child: const Text('remove skybox'), child: const Text('remove skybox'),
onPressed: () async { onPressed: () async {
await _filamentController.removeSkybox(); await _filamentController.removeSkybox();
} }),
),
ElevatedButton( ElevatedButton(
child: const Text('load cube'), child: const Text('load cube GLB'),
onPressed:_cube != null ? null : () async {
_cube = await _filamentController
.loadGlb('assets/cube.glb');
print(await _filamentController
.getAnimationNames(_cube!));
}),
ElevatedButton(
child: const Text('load cube GLTF'),
onPressed:() async { onPressed:() async {
await _filamentController.loadGltf( if(_cube != null) {
'assets/cube.glb' ,"assets"); await _filamentController.removeAsset(_cube!);
print(await _filamentController.getAnimationNames()); }
_cube = await _filamentController
.loadGltf('assets/cube.gltf', 'assets');
print(await _filamentController
.getAnimationNames(_cube!));
}), }),
ElevatedButton( ElevatedButton(
child: const Text('load flight helmet'), child: const Text('load flight helmet'),
onPressed: () async { onPressed:_flightHelmet != null ? null : () async {
await _filamentController.loadGltf( _flightHelmet = await _filamentController.loadGltf(
'assets/FlightHelmet/FlightHelmet.gltf', 'assets/FlightHelmet'); 'assets/FlightHelmet/FlightHelmet.gltf',
'assets/FlightHelmet');
}), }),
ElevatedButton( ElevatedButton(
child: const Text('remove asset'), child: const Text('remove cube'),
onPressed: () async { onPressed: () async {
await _filamentController await _filamentController.removeAsset(_cube!);
.removeAsset();
}), }),
ElevatedButton( ElevatedButton(
child: const Text('set all weights to 1'), child: const Text('set all weights to 1'),
onPressed: () async { onPressed: () async {
await _filamentController await _filamentController
.applyWeights(List.filled(8, 1.0)); .applyWeights(_cube!, List.filled(8, 1.0));
}), }),
ElevatedButton( ElevatedButton(
child: const Text('set all weights to 0'), child: const Text('set all weights to 0'),
onPressed: () async { onPressed: () async {
await _filamentController await _filamentController
.applyWeights(List.filled(8, 0)); .applyWeights(_cube!, List.filled(8, 0));
}), }),
ElevatedButton( ElevatedButton(
onPressed: () => onPressed: () =>
_filamentController.playAnimation(0, loop: _loop), _filamentController.playAnimation(_cube!, 0, loop: _loop),
child: const Text('play animation')), child: const Text('play animation')),
ElevatedButton( ElevatedButton(
onPressed: () { onPressed: () {
_filamentController.stopAnimation(); _filamentController.stopAnimation(_cube!);
}, },
child: const Text('stop animation')), child: const Text('stop animation')),
Checkbox( Checkbox(
@@ -120,7 +136,7 @@ class _MyAppState extends State<MyApp> {
child: const Text('zoom out')), child: const Text('zoom out')),
ElevatedButton( ElevatedButton(
onPressed: () { onPressed: () {
_filamentController.setCamera("Camera_Orientation"); _filamentController.setCamera(_cube!, "Camera_Orientation");
}, },
child: const Text('set camera')), child: const Text('set camera')),
ElevatedButton( ElevatedButton(
@@ -135,6 +151,7 @@ class _MyAppState extends State<MyApp> {
List.filled(numWeights, frame / totalFrames)); List.filled(numWeights, frame / totalFrames));
_filamentController.animate( _filamentController.animate(
_cube!,
frames.reduce((a, b) => a + b), frames.reduce((a, b) => a + b),
numWeights, numWeights,
totalFrames, totalFrames,
@@ -145,7 +162,7 @@ class _MyAppState extends State<MyApp> {
builder: (innerCtx) => ElevatedButton( builder: (innerCtx) => ElevatedButton(
onPressed: () async { onPressed: () async {
final names = await _filamentController final names = await _filamentController
.getTargetNames("Cube"); .getTargetNames(_cube!, "Cube");
await showDialog( await showDialog(
builder: (ctx) { builder: (ctx) {
@@ -173,7 +190,7 @@ class _MyAppState extends State<MyApp> {
builder: (innerCtx) => ElevatedButton( builder: (innerCtx) => ElevatedButton(
onPressed: () async { onPressed: () async {
final names = final names =
await _filamentController.getAnimationNames(); await _filamentController.getAnimationNames(_cube!);
await showDialog( await showDialog(
builder: (ctx) { 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();
// }),

View File

@@ -16,7 +16,7 @@
#include "FilamentViewer.hpp" #include "FilamentViewer.hpp"
#include "streambuf.hpp" #include "StreamBufferAdapter.hpp"
#include <filament/Camera.h> #include <filament/Camera.h>
#include <filament/ColorGrading.h> #include <filament/ColorGrading.h>
@@ -35,12 +35,13 @@
#include <filament/IndirectLight.h> #include <filament/IndirectLight.h>
#include <filament/LightManager.h> #include <filament/LightManager.h>
#include <gltfio/Animator.h>
#include <gltfio/AssetLoader.h> #include <gltfio/AssetLoader.h>
#include <gltfio/FilamentAsset.h> #include <gltfio/FilamentAsset.h>
#include <gltfio/ResourceLoader.h> #include <gltfio/ResourceLoader.h>
#include <gltfio/Animator.h>
#include <gltfio/TextureProvider.h> #include <gltfio/TextureProvider.h>
#include <gltfio/materials/uberarchive.h> #include <gltfio/materials/uberarchive.h>
#include <camutils/Manipulator.h> #include <camutils/Manipulator.h>
@@ -59,30 +60,27 @@
#include <ktxreader/Ktx1Reader.h> #include <ktxreader/Ktx1Reader.h>
#include <chrono>
#include <iostream> #include <iostream>
#include <mutex> #include <mutex>
#include "Log.h" #include "Log.hpp"
#include "SceneResources.hpp"
#include "imagematerial.h" #include "image/imagematerial.h"
using namespace filament; using namespace filament;
using namespace filament::math; using namespace filament::math;
using namespace gltfio; using namespace gltfio;
using namespace utils; using namespace utils;
using namespace std::chrono;
using namespace image; using namespace image;
namespace filament namespace filament {
{
class IndirectLight; class IndirectLight;
class LightManager; class LightManager;
} } // namespace filament
namespace polyvox namespace polyvox {
{
const double kNearPlane = 0.05; // 5 cm const double kNearPlane = 0.05; // 5 cm
const double kFarPlane = 1000.0; // 1 km const double kFarPlane = 1000.0; // 1 km
@@ -91,41 +89,39 @@ namespace polyvox
const float kShutterSpeed = 1.0f / 125.0f; const float kShutterSpeed = 1.0f / 125.0f;
const float kSensitivity = 100.0f; const float kSensitivity = 100.0f;
filament::math::mat4f composeMatrix(const filament::math::float3 &translation, // filament::math::mat4f composeMatrix(const filament::math::float3
const filament::math::quatf &rotation, const filament::math::float3 &scale) // &translation,
{ // const filament::math::quatf &rotation,
float tx = translation[0]; // const filament::math::float3 &scale)
float ty = translation[1]; // {
float tz = translation[2]; // float tx = translation[0];
float qx = rotation[0]; // float ty = translation[1];
float qy = rotation[1]; // float tz = translation[2];
float qz = rotation[2]; // float qx = rotation[0];
float qw = rotation[3]; // float qy = rotation[1];
float sx = scale[0]; // float qz = rotation[2];
float sy = scale[1]; // float qw = rotation[3];
float sz = scale[2]; // float sx = scale[0];
return filament::math::mat4f( // float sy = scale[1];
(1 - 2 * qy * qy - 2 * qz * qz) * sx, // float sz = scale[2];
(2 * qx * qy + 2 * qz * qw) * sx, // return filament::math::mat4f(
(2 * qx * qz - 2 * qy * qw) * sx, // (1 - 2 * qy * qy - 2 * qz * qz) * sx,
0.f, // (2 * qx * qy + 2 * qz * qw) * sx,
(2 * qx * qy - 2 * qz * qw) * sy, // (2 * qx * qz - 2 * qy * qw) * sx,
(1 - 2 * qx * qx - 2 * qz * qz) * sy, // 0.f,
(2 * qy * qz + 2 * qx * qw) * sy, // (2 * qx * qy - 2 * qz * qw) * sy,
0.f, // (1 - 2 * qx * qx - 2 * qz * qz) * sy,
(2 * qx * qz + 2 * qy * qw) * sz, // (2 * qy * qz + 2 * qx * qw) * sy,
(2 * qy * qz - 2 * qx * qw) * sz, // 0.f,
(1 - 2 * qx * qx - 2 * qy * qy) * sz, // (2 * qx * qz + 2 * qy * qw) * sz,
0.f, tx, ty, tz, 1.f); // (2 * qy * qz - 2 * qx * qw) * sz,
} // (1 - 2 * qx * qx - 2 * qy * qy) * sz,
// 0.f, tx, ty, tz, 1.f);
// }
FilamentViewer::FilamentViewer( FilamentViewer::FilamentViewer(void *layer, LoadResource loadResource,
void *layer, FreeResource freeResource)
LoadResource loadResource, : _layer(layer), _loadResource(loadResource), _freeResource(freeResource) {
FreeResource freeResource) : _layer(layer),
_loadResource(loadResource),
_freeResource(freeResource)
{
_engine = Engine::create(Engine::Backend::OPENGL); _engine = Engine::create(Engine::Backend::OPENGL);
_renderer = _engine->createRenderer(); _renderer = _engine->createRenderer();
@@ -134,6 +130,7 @@ namespace polyvox
.presentationDeadlineNanos = (uint64_t)0, .presentationDeadlineNanos = (uint64_t)0,
.vsyncOffsetNanos = (uint64_t)0}); .vsyncOffsetNanos = (uint64_t)0});
_scene = _engine->createScene(); _scene = _engine->createScene();
Entity camera = EntityManager::get().create(); Entity camera = EntityManager::get().create();
_mainCamera = _engine->createCamera(camera); _mainCamera = _engine->createCamera(camera);
_view = _engine->createView(); _view = _engine->createView();
@@ -160,19 +157,22 @@ namespace polyvox
_view->setMultiSampleAntiAliasingOptions(multiSampleAntiAliasingOptions); _view->setMultiSampleAntiAliasingOptions(multiSampleAntiAliasingOptions);
_materialProvider = gltfio::createUbershaderProvider(_engine, UBERARCHIVE_DEFAULT_DATA, UBERARCHIVE_DEFAULT_SIZE); _materialProvider = gltfio::createUbershaderProvider(
_engine, UBERARCHIVE_DEFAULT_DATA, UBERARCHIVE_DEFAULT_SIZE);
EntityManager &em = EntityManager::get(); EntityManager &em = EntityManager::get();
_ncm = new NameComponentManager(em); _ncm = new NameComponentManager(em);
_assetLoader = AssetLoader::create({_engine, _materialProvider, _ncm, &em}); _assetLoader = AssetLoader::create({_engine, _materialProvider, _ncm, &em});
_resourceLoader = new ResourceLoader( _resourceLoader = new ResourceLoader({.engine = _engine,
{.engine = _engine, .normalizeSkinningWeights = true, .recomputeBoundingBoxes = true}); .normalizeSkinningWeights = true,
.recomputeBoundingBoxes = true});
_stbDecoder = createStbProvider(_engine); _stbDecoder = createStbProvider(_engine);
_resourceLoader->addTextureProvider("image/png", _stbDecoder); _resourceLoader->addTextureProvider("image/png", _stbDecoder);
_resourceLoader->addTextureProvider("image/jpeg", _stbDecoder); _resourceLoader->addTextureProvider("image/jpeg", _stbDecoder);
manipulator = manipulator = Manipulator<float>::Builder()
Manipulator<float>::Builder().orbitHomePosition(0.0f, 0.0f, 0.05f).targetPosition(0.0f, 0.0f, 0.0f).build(Mode::ORBIT); .orbitHomePosition(0.0f, 0.0f, 0.05f)
_asset = nullptr; .targetPosition(0.0f, 0.0f, 0.0f)
.build(Mode::ORBIT);
// Always add a direct light source since it is required for shadowing. // Always add a direct light source since it is required for shadowing.
_sun = EntityManager::get().create(); _sun = EntityManager::get().create();
@@ -184,6 +184,15 @@ namespace polyvox
// .castShadows(true) // .castShadows(true)
.build(*_engine, _sun); .build(*_engine, _sun);
_scene->addEntity(_sun); _scene->addEntity(_sun);
_sceneAssetLoader = new SceneAssetLoader(_loadResource,
_freeResource,
_assetLoader,
_resourceLoader,
_ncm,
_engine,
_scene);
} }
static constexpr float4 sFullScreenTriangleVertices[3] = { static constexpr float4 sFullScreenTriangleVertices[3] = {
@@ -193,39 +202,42 @@ namespace polyvox
static const uint16_t sFullScreenTriangleIndices[3] = {0, 1, 2}; static const uint16_t sFullScreenTriangleIndices[3] = {0, 1, 2};
void FilamentViewer::createImageRenderable() void FilamentViewer::createImageRenderable() {
{
if (_imageEntity) if (_imageEntity)
return; return;
auto &em = EntityManager::get(); auto &em = EntityManager::get();
_imageMaterial = Material::Builder() _imageMaterial =
Material::Builder()
.package(IMAGEMATERIAL_IMAGE_DATA, IMAGEMATERIAL_IMAGE_SIZE) .package(IMAGEMATERIAL_IMAGE_DATA, IMAGEMATERIAL_IMAGE_SIZE)
.build(*_engine); .build(*_engine);
_imageVb = VertexBuffer::Builder() _imageVb = VertexBuffer::Builder()
.vertexCount(3) .vertexCount(3)
.bufferCount(1) .bufferCount(1)
.attribute(VertexAttribute::POSITION, 0, VertexBuffer::AttributeType::FLOAT4, 0) .attribute(VertexAttribute::POSITION, 0,
VertexBuffer::AttributeType::FLOAT4, 0)
.build(*_engine); .build(*_engine);
_imageVb->setBufferAt( _imageVb->setBufferAt(
*_engine, 0, {sFullScreenTriangleVertices, sizeof(sFullScreenTriangleVertices)}); *_engine, 0,
{sFullScreenTriangleVertices, sizeof(sFullScreenTriangleVertices)});
_imageIb = IndexBuffer::Builder() _imageIb = IndexBuffer::Builder()
.indexCount(3) .indexCount(3)
.bufferType(IndexBuffer::IndexType::USHORT) .bufferType(IndexBuffer::IndexType::USHORT)
.build(*_engine); .build(*_engine);
_imageIb->setBuffer(*_engine, _imageIb->setBuffer(*_engine, {sFullScreenTriangleIndices,
{sFullScreenTriangleIndices, sizeof(sFullScreenTriangleIndices)}); sizeof(sFullScreenTriangleIndices)});
Entity imageEntity = em.create(); Entity imageEntity = em.create();
RenderableManager::Builder(1) RenderableManager::Builder(1)
.boundingBox({{}, {1.0f, 1.0f, 1.0f}}) .boundingBox({{}, {1.0f, 1.0f, 1.0f}})
.material(0, _imageMaterial->getDefaultInstance()) .material(0, _imageMaterial->getDefaultInstance())
.geometry(0, RenderableManager::PrimitiveType::TRIANGLES, _imageVb, _imageIb, 0, 3) .geometry(0, RenderableManager::PrimitiveType::TRIANGLES, _imageVb,
_imageIb, 0, 3)
.culling(false) .culling(false)
.build(*_engine, imageEntity); .build(*_engine, imageEntity);
@@ -241,44 +253,39 @@ namespace polyvox
.sampler(Texture::Sampler::SAMPLER_2D) .sampler(Texture::Sampler::SAMPLER_2D)
.build(*_engine); .build(*_engine);
static uint32_t pixel = 0; static uint32_t pixel = 0;
Texture::PixelBufferDescriptor buffer(&pixel, 4, Texture::Format::RGBA, Texture::Type::UBYTE); Texture::PixelBufferDescriptor buffer(&pixel, 4, Texture::Format::RGBA,
Texture::Type::UBYTE);
texture->setImage(*_engine, 0, std::move(buffer)); texture->setImage(*_engine, 0, std::move(buffer));
} }
void FilamentViewer::setBackgroundImage(const char *resourcePath) void FilamentViewer::setBackgroundImage(const char *resourcePath) {
{
if (colorGrading) if (colorGrading) {
{
_engine->destroy(colorGrading); _engine->destroy(colorGrading);
} }
ToneMapper *tm = new LinearToneMapper(); ToneMapper *tm = new LinearToneMapper();
colorGrading = ColorGrading::Builder() colorGrading = ColorGrading::Builder().toneMapper(tm).build(*_engine);
.toneMapper(tm)
.build(*_engine);
delete tm; delete tm;
_view->setColorGrading(colorGrading); _view->setColorGrading(colorGrading);
createImageRenderable(); createImageRenderable();
if (_imageTexture) if (_imageTexture) {
{
_engine->destroy(_imageTexture); _engine->destroy(_imageTexture);
_imageTexture = nullptr; _imageTexture = nullptr;
} }
ResourceBuffer bg = _loadResource(resourcePath); ResourceBuffer bg = _loadResource(resourcePath);
polyvox::streambuf sb((char *)bg.data, (char *)bg.data + bg.size); polyvox::StreamBufferAdapter sb((char *)bg.data, (char *)bg.data + bg.size);
std::istream *inputStream = new std::istream(&sb); std::istream *inputStream = new std::istream(&sb);
LinearImage *image = new LinearImage(ImageDecoder::decode( LinearImage *image = new LinearImage(ImageDecoder::decode(
*inputStream, resourcePath, ImageDecoder::ColorSpace::SRGB)); *inputStream, resourcePath, ImageDecoder::ColorSpace::SRGB));
if (!image->isValid()) if (!image->isValid()) {
{
Log("Invalid image : %s", resourcePath); Log("Invalid image : %s", resourcePath);
return; return;
} }
@@ -294,245 +301,121 @@ namespace polyvox
.width(w) .width(w)
.height(h) .height(h)
.levels(0xff) .levels(0xff)
.format(channels == 3 ? Texture::InternalFormat::RGB16F : Texture::InternalFormat::RGBA16F) .format(channels == 3 ? Texture::InternalFormat::RGB16F
: Texture::InternalFormat::RGBA16F)
.sampler(Texture::Sampler::SAMPLER_2D) .sampler(Texture::Sampler::SAMPLER_2D)
.build(*_engine); .build(*_engine);
Texture::PixelBufferDescriptor::Callback freeCallback = [](void *buf, size_t, void *data) Texture::PixelBufferDescriptor::Callback freeCallback = [](void *buf, size_t,
{ void *data) {
delete reinterpret_cast<LinearImage *>(data); delete reinterpret_cast<LinearImage *>(data);
}; };
Texture::PixelBufferDescriptor buffer( Texture::PixelBufferDescriptor buffer(
image->getPixelRef(), image->getPixelRef(), size_t(w * h * channels * sizeof(float)),
size_t(w * h * channels * sizeof(float)),
channels == 3 ? Texture::Format::RGB : Texture::Format::RGBA, channels == 3 ? Texture::Format::RGB : Texture::Format::RGBA,
Texture::Type::FLOAT, Texture::Type::FLOAT, freeCallback);
freeCallback);
_imageTexture->setImage(*_engine, 0, std::move(buffer)); _imageTexture->setImage(*_engine, 0, std::move(buffer));
// _imageTexture->generateMipmaps(*_engine); _imageTexture->generateMipmaps(*_engine);
float srcWidth = _imageTexture->getWidth(); float srcWidth = _imageTexture->getWidth();
float srcHeight = _imageTexture->getHeight(); float srcHeight = _imageTexture->getHeight();
float dstWidth = _view->getViewport().width; float dstWidth = _view->getViewport().width;
float dstHeight = _view->getViewport().height; float dstHeight = _view->getViewport().height;
mat3f transform( mat3f transform(1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f);
1.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 1.0f);
_imageMaterial->setDefaultParameter("transform", transform); _imageMaterial->setDefaultParameter("transform", transform);
_imageMaterial->setDefaultParameter( _imageMaterial->setDefaultParameter("image", _imageTexture, _imageSampler);
"image", _imageTexture, _imageSampler);
_imageMaterial->setDefaultParameter("showImage", 1); _imageMaterial->setDefaultParameter("showImage", 1);
_imageMaterial->setDefaultParameter( _imageMaterial->setDefaultParameter("backgroundColor", RgbType::sRGB,
"backgroundColor", RgbType::sRGB, float3(1.0f)); float3(1.0f));
} }
FilamentViewer::~FilamentViewer() FilamentViewer::~FilamentViewer() { cleanup(); }
{
cleanup();
}
Renderer *FilamentViewer::getRenderer() Renderer *FilamentViewer::getRenderer() { return _renderer; }
{
return _renderer;
}
void FilamentViewer::createSwapChain(void *surface) void FilamentViewer::createSwapChain(void *surface) {
{
_swapChain = _engine->createSwapChain(surface); _swapChain = _engine->createSwapChain(surface);
Log("swapchain created."); Log("swapchain created.");
} }
void FilamentViewer::destroySwapChain() void FilamentViewer::destroySwapChain() {
{ if (_swapChain) {
if (_swapChain)
{
_engine->destroy(_swapChain); _engine->destroy(_swapChain);
_swapChain = nullptr; _swapChain = nullptr;
Log("Swapchain destroyed."); Log("Swapchain destroyed.");
} }
} }
void FilamentViewer::applyWeights(float *weights, int count) SceneAsset *FilamentViewer::loadGlb(const char *const uri) {
{
for (size_t i = 0, c = _asset->getEntityCount(); i != c; ++i)
{
RenderableManager &rm = _engine->getRenderableManager();
auto inst = rm.getInstance(_asset->getEntities()[i]);
rm.setMorphWeights(
inst,
weights,
count);
}
}
void FilamentViewer::loadResources(string relativeResourcePath)
{
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 = relativeResourcePath + 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);
}
_animator = _asset->getAnimator();
_scene->addEntities(_asset->getEntities(), _asset->getEntityCount());
};
void FilamentViewer::loadGlb(const char *const uri)
{
Log("Loading GLB at URI %s", uri); Log("Loading GLB at URI %s", uri);
SceneAsset *asset = _sceneAssetLoader->fromGlb(uri);
ResourceBuffer rbuf = _loadResource(uri); if (!asset) {
Log("Unknown error loading asset.");
_asset = _assetLoader->createAssetFromBinary( } else {
(const uint8_t *)rbuf.data, rbuf.size); _assets.push_back(asset);
}
if (!_asset) return asset;
{
Log("Unknown error loading GLB asset.");
exit(1);
} }
int entityCount = _asset->getEntityCount(); SceneAsset *FilamentViewer::loadGltf(const char *const uri,
const char *const relativeResourcePath) {
_scene->addEntities(_asset->getEntities(), entityCount); Log("Loading GLTF at URI %s with relativeResourcePath %s", uri,
relativeResourcePath);
Log("Added %d entities to scene", entityCount); SceneAsset *asset = _sceneAssetLoader->fromGltf(uri, relativeResourcePath);
_resourceLoader->loadResources(_asset); if (!asset) {
_animator = _asset->getAnimator(); Log("Unknown error loading asset.");
} else {
const Entity *entities = _asset->getEntities(); _assets.push_back(asset);
RenderableManager &rm = _engine->getRenderableManager(); }
for (int i = 0; i < _asset->getEntityCount(); i++) return asset;
{
Entity e = entities[i];
auto inst = rm.getInstance(e);
rm.setCulling(inst, false);
}
_freeResource(rbuf);
_animator->updateBoneMatrices();
// transformToUnitCube();
_asset->releaseSourceData();
Log("Successfully loaded GLB.");
}
void FilamentViewer::loadGltf(const char *const uri, const char *const relativeResourcePath)
{
Log("Loading GLTF at URI %s", uri);
_asset = nullptr;
_animator = nullptr;
ResourceBuffer rbuf = _loadResource(uri);
// Parse the glTF file and create Filament entities.
Log("Creating asset from JSON");
_asset = _assetLoader->createAssetFromJson((uint8_t *)rbuf.data, rbuf.size);
Log("Created asset from JSON");
if (!_asset)
{
Log("Unable to parse asset");
exit(1);
}
Log("Loading relative resources");
loadResources(string(relativeResourcePath) + string("/"));
Log("Loaded relative resources");
_asset->releaseSourceData();
Log("Load complete for GLTF at URI %s", uri);
// transformToUnitCube();
}
void FilamentViewer::removeAsset()
{
if (!_asset)
{
Log("No asset loaded, ignoring call.");
return;
} }
void FilamentViewer::removeAsset(SceneAsset *asset) {
mtx.lock(); mtx.lock();
_sceneAssetLoader->remove(asset);
_resourceLoader->evictResourceData();
_scene->removeEntities(_asset->getEntities(), _asset->getEntityCount());
_assetLoader->destroyAsset(_asset);
_asset = nullptr;
_animator = nullptr;
_morphAnimationBuffer = nullptr;
_embeddedAnimationBuffer = nullptr;
_view->setCamera(_mainCamera); _view->setCamera(_mainCamera);
bool erased = false;
for (auto it = _assets.begin(); it != _assets.end();) {
if (*it == asset) {
_assets.erase(it);
erased = true;
break;
}
}
if (!erased) {
Log("Error removing asset from scene : not found");
}
mtx.unlock(); mtx.unlock();
} }
/// ///
/// Sets the active camera to the GLTF camera node specified by [name]. /// Sets the active camera to the GLTF camera node specified by [name].
/// N.B. Blender will generally export a three-node hierarchy - Camera1->Camera_Orientation->Camera2. /// N.B. Blender will generally export a three-node hierarchy -
/// The correct name will be the grandchild (i.e. Camera2 in this scenario). /// Camera1->Camera_Orientation->Camera2. The correct name will be the
/// grandchild (i.e. Camera2 in this scenario).
/// ///
bool FilamentViewer::setCamera(const char *cameraName) bool FilamentViewer::setCamera(SceneAsset *asset, const char *cameraName) {
{
Log("Attempting to set camera to %s.", cameraName); Log("Attempting to set camera to %s.", cameraName);
size_t count = _asset->getCameraEntityCount(); size_t count = asset->getCameraEntityCount();
if (count == 0) if (count == 0) {
{
Log("Failed, no cameras found in current asset."); Log("Failed, no cameras found in current asset.");
return false; return false;
} }
const utils::Entity *cameras = _asset->getCameraEntities(); const utils::Entity *cameras = asset->getCameraEntities();
Log("%zu cameras found in current asset", count); Log("%zu cameras found in asset", count);
for (int i = 0; i < count; i++) for (int i = 0; i < count; i++) {
{
auto inst = _ncm->getInstance(cameras[i]); auto inst = _ncm->getInstance(cameras[i]);
const char *name = _ncm->getName(inst); const char *name = _ncm->getName(inst);
Log("Camera %d : %s", i, name); Log("Camera %d : %s", i, name);
if (strcmp(name, cameraName) == 0) if (strcmp(name, cameraName) == 0) {
{
Camera *camera = _engine->getCameraComponent(cameras[i]); Camera *camera = _engine->getCameraComponent(cameras[i]);
_view->setCamera(camera); _view->setCamera(camera);
@@ -540,7 +423,8 @@ namespace polyvox
const Viewport &vp = _view->getViewport(); const Viewport &vp = _view->getViewport();
const double aspect = (double)vp.width / vp.height; const double aspect = (double)vp.width / vp.height;
Log("Camera focal length : %f aspect %f", camera->getFocalLength(), aspect); Log("Camera focal length : %f aspect %f", camera->getFocalLength(),
aspect);
camera->setScaling({1.0 / aspect, 1.0}); camera->setScaling({1.0 / aspect, 1.0});
Log("Successfully set camera."); Log("Successfully set camera.");
return true; return true;
@@ -550,107 +434,46 @@ namespace polyvox
return false; return false;
} }
unique_ptr<vector<string>> FilamentViewer::getAnimationNames() void FilamentViewer::loadSkybox(const char *const skyboxPath) {
{ if (!skyboxPath) {
if (!_asset)
{
Log("No asset, ignoring call.");
return nullptr;
}
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>> FilamentViewer::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 FilamentViewer::loadSkybox(const char *const skyboxPath)
{
if (!skyboxPath)
{
_scene->setSkybox(nullptr); _scene->setSkybox(nullptr);
} } else {
else
{
ResourceBuffer skyboxBuffer = _loadResource(skyboxPath); ResourceBuffer skyboxBuffer = _loadResource(skyboxPath);
image::Ktx1Bundle *skyboxBundle = image::Ktx1Bundle *skyboxBundle =
new image::Ktx1Bundle(static_cast<const uint8_t *>(skyboxBuffer.data), static_cast<uint32_t>(skyboxBuffer.size)); new image::Ktx1Bundle(static_cast<const uint8_t *>(skyboxBuffer.data),
_skyboxTexture = ktxreader::Ktx1Reader::createTexture(_engine, skyboxBundle, false); static_cast<uint32_t>(skyboxBuffer.size));
_skybox = filament::Skybox::Builder().environment(_skyboxTexture).build(*_engine); _skyboxTexture =
ktxreader::Ktx1Reader::createTexture(_engine, skyboxBundle, false);
_skybox =
filament::Skybox::Builder().environment(_skyboxTexture).build(*_engine);
_scene->setSkybox(_skybox); _scene->setSkybox(_skybox);
_freeResource(skyboxBuffer); _freeResource(skyboxBuffer);
} }
} }
void FilamentViewer::removeSkybox() void FilamentViewer::removeSkybox() { _scene->setSkybox(nullptr); }
{
_scene->setSkybox(nullptr);
}
void FilamentViewer::removeIbl() void FilamentViewer::removeIbl() { _scene->setIndirectLight(nullptr); }
{
_scene->setIndirectLight(nullptr);
}
void FilamentViewer::loadIbl(const char *const iblPath) void FilamentViewer::loadIbl(const char *const iblPath) {
{ if (!iblPath) {
if (!iblPath)
{
_scene->setIndirectLight(nullptr); _scene->setIndirectLight(nullptr);
} } else {
else
{
Log("Loading IBL from %s", iblPath); Log("Loading IBL from %s", iblPath);
// Load IBL. // Load IBL.
ResourceBuffer iblBuffer = _loadResource(iblPath); ResourceBuffer iblBuffer = _loadResource(iblPath);
image::Ktx1Bundle *iblBundle = new image::Ktx1Bundle( image::Ktx1Bundle *iblBundle =
static_cast<const uint8_t *>(iblBuffer.data), static_cast<uint32_t>(iblBuffer.size)); new image::Ktx1Bundle(static_cast<const uint8_t *>(iblBuffer.data),
static_cast<uint32_t>(iblBuffer.size));
math::float3 harmonics[9]; math::float3 harmonics[9];
iblBundle->getSphericalHarmonics(harmonics); iblBundle->getSphericalHarmonics(harmonics);
_iblTexture = ktxreader::Ktx1Reader::createTexture(_engine, iblBundle, false); _iblTexture =
ktxreader::Ktx1Reader::createTexture(_engine, iblBundle, false);
_indirectLight = IndirectLight::Builder() _indirectLight = IndirectLight::Builder()
.reflections(_iblTexture) .reflections(_iblTexture)
.irradiance(3, harmonics) .irradiance(3, harmonics)
@@ -664,45 +487,22 @@ namespace polyvox
} }
} }
void FilamentViewer::transformToUnitCube() void FilamentViewer::cleanup() {
{
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);
}
void FilamentViewer::cleanup()
{
_resourceLoader->asyncCancelLoad(); _resourceLoader->asyncCancelLoad();
_assetLoader->destroyAsset(_asset);
_materialProvider->destroyMaterials(); _materialProvider->destroyMaterials();
AssetLoader::destroy(&_assetLoader); AssetLoader::destroy(&_assetLoader);
}; };
void FilamentViewer::render() void FilamentViewer::render() {
{
if (!_view || !_mainCamera || !_swapChain) if (!_view || !_mainCamera || !_swapChain) {
{
Log("Not ready for rendering"); Log("Not ready for rendering");
return; return;
} }
mtx.lock(); mtx.lock();
if (_asset) for (auto &asset : _assets) {
{ asset->updateAnimations();
updateMorphAnimation();
updateEmbeddedAnimation();
} }
math::float3 eye, target, upward; math::float3 eye, target, upward;
@@ -710,18 +510,16 @@ namespace polyvox
_mainCamera->lookAt(eye, target, upward); _mainCamera->lookAt(eye, target, upward);
// Render the scene, unless the renderer wants to skip the frame. // Render the scene, unless the renderer wants to skip the frame.
if (_renderer->beginFrame(_swapChain)) if (_renderer->beginFrame(_swapChain)) {
{
_renderer->render(_view); _renderer->render(_view);
_renderer->endFrame(); _renderer->endFrame();
} }
mtx.unlock(); mtx.unlock();
} }
void FilamentViewer::updateViewportAndCameraProjection(int width, int height, float contentScaleFactor) void FilamentViewer::updateViewportAndCameraProjection(
{ int width, int height, float contentScaleFactor) {
if (!_view || !_mainCamera) if (!_view || !_mainCamera) {
{
Log("Skipping camera update, no view or camrea"); Log("Skipping camera update, no view or camrea");
return; return;
} }
@@ -731,101 +529,11 @@ namespace polyvox
_view->setViewport({0, 0, _width, _height}); _view->setViewport({0, 0, _width, _height});
const double aspect = (double)width / height; const double aspect = (double)width / height;
_mainCamera->setLensProjection(_cameraFocalLength, aspect, kNearPlane, kFarPlane); _mainCamera->setLensProjection(_cameraFocalLength, aspect, kNearPlane,
kFarPlane);
Log("Set viewport to width: %d height: %d scaleFactor : %f", width, height, contentScaleFactor); Log("Set viewport to width: %d height: %d scaleFactor : %f", width, height,
contentScaleFactor);
} }
void FilamentViewer::animateWeights(float *data, int numWeights, int numFrames, float frameLengthInMs) } // namespace polyvox
{
Log("Making morph animation buffer with %d weights across %d frames and frame length %f ms ", numWeights, numFrames, frameLengthInMs);
_morphAnimationBuffer = std::make_unique<MorphAnimationBuffer>(data, numWeights, numFrames, frameLengthInMs);
}
void FilamentViewer::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 FilamentViewer::playAnimation(int index, bool loop)
{
if (index > _animator->getAnimationCount() - 1)
{
Log("Asset does not contain an animation at index %d", index);
}
else
{
_embeddedAnimationBuffer = make_unique<EmbeddedAnimationBuffer>(index, _animator->getAnimationDuration(index), loop);
}
}
void FilamentViewer::stopAnimation()
{
// TODO - does this need to be threadsafe?
_embeddedAnimationBuffer = nullptr;
}
void FilamentViewer::updateEmbeddedAnimation()
{
if (!_embeddedAnimationBuffer)
{
return;
}
duration<double> dur = duration_cast<duration<double>>(high_resolution_clock::now() - _embeddedAnimationBuffer->lastTime);
float startTime = 0;
if (!_embeddedAnimationBuffer->hasStarted)
{
_embeddedAnimationBuffer->hasStarted = true;
_embeddedAnimationBuffer->lastTime = high_resolution_clock::now();
}
else if (dur.count() >= _embeddedAnimationBuffer->duration)
{
if (_embeddedAnimationBuffer->loop)
{
_embeddedAnimationBuffer->lastTime = high_resolution_clock::now();
}
else
{
_embeddedAnimationBuffer = nullptr;
return;
}
}
else
{
startTime = dur.count();
}
_animator->applyAnimation(_embeddedAnimationBuffer->animationIndex, startTime);
_animator->updateBoneMatrices();
}
}

View File

@@ -30,6 +30,10 @@
#include <string> #include <string>
#include <chrono> #include <chrono>
#include "SceneAssetLoader.hpp"
#include "SceneAsset.hpp"
#include "SceneResources.hpp"
using namespace std; using namespace std;
using namespace filament; using namespace filament;
using namespace filament::math; using namespace filament::math;
@@ -39,55 +43,6 @@ using namespace camutils;
namespace polyvox { 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 { class FilamentViewer {
public: public:
FilamentViewer(void* layer, LoadResource loadResource, FreeResource freeResource); FilamentViewer(void* layer, LoadResource loadResource, FreeResource freeResource);
@@ -99,42 +54,16 @@ namespace polyvox {
void loadIbl(const char* const iblUri); void loadIbl(const char* const iblUri);
void removeIbl(); void removeIbl();
void loadGlb(const char* const uri); SceneAsset* loadGlb(const char* const uri);
void loadGltf(const char* const uri, const char* relativeResourcePath); SceneAsset* loadGltf(const char* const uri, const char* relativeResourcePath);
void removeAsset(); void removeAsset(SceneAsset* asset);
void updateViewportAndCameraProjection(int height, int width, float scaleFactor); void updateViewportAndCameraProjection(int height, int width, float scaleFactor);
void render(); void render();
unique_ptr<vector<string>> getTargetNames(const char* meshName);
unique_ptr<vector<string>> getAnimationNames();
Manipulator<float>* manipulator; Manipulator<float>* manipulator;
bool setCamera(SceneAsset* asset, const char* nodeName);
///
/// 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);
void destroySwapChain(); void destroySwapChain();
void createSwapChain(void* surface); void createSwapChain(void* surface);
@@ -161,10 +90,10 @@ namespace polyvox {
SwapChain* _swapChain = nullptr; SwapChain* _swapChain = nullptr;
Animator* _animator; vector<SceneAsset*> _assets;
AssetLoader* _assetLoader; AssetLoader* _assetLoader;
FilamentAsset* _asset = nullptr; SceneAssetLoader* _sceneAssetLoader;
NameComponentManager* _ncm; NameComponentManager* _ncm;
std::mutex mtx; // mutex to ensure thread safety when removing assets std::mutex mtx; // mutex to ensure thread safety when removing assets
@@ -184,24 +113,15 @@ namespace polyvox {
float _cameraFocalLength = 0.0f; float _cameraFocalLength = 0.0f;
void updateMorphAnimation(); // these flags relate to the textured quad we use for rendering unlit background images
void updateEmbeddedAnimation();
// animation flags;
bool isAnimating;
unique_ptr<MorphAnimationBuffer> _morphAnimationBuffer;
unique_ptr<EmbeddedAnimationBuffer> _embeddedAnimationBuffer;
Texture* _imageTexture = nullptr; Texture* _imageTexture = nullptr;
Entity* _imageEntity = nullptr; Entity* _imageEntity = nullptr;
VertexBuffer* _imageVb = nullptr; VertexBuffer* _imageVb = nullptr;
IndexBuffer* _imageIb = nullptr; IndexBuffer* _imageIb = nullptr;
Material* _imageMaterial = nullptr; Material* _imageMaterial = nullptr;
TextureSampler _imageSampler; TextureSampler _imageSampler;
ColorGrading *colorGrading = nullptr; ColorGrading *colorGrading = nullptr;
}; };

View File

@@ -1,3 +1,8 @@
#pragma once
#ifndef POLYVOX_FILAMENT_LOG_H
#define POLYVOX_FILAMENT_LOG_H
#ifdef __OBJC__ #ifdef __OBJC__
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
#elif defined __ANDROID__ #elif defined __ANDROID__
@@ -7,7 +12,7 @@
#include <stdio.h> #include <stdio.h>
#endif #endif
void Log(const char *fmt, ...) { static void Log(const char *fmt, ...) {
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
@@ -23,3 +28,5 @@ void Log(const char *fmt, ...) {
va_end(args); va_end(args);
} }
#endif

193
ios/src/SceneAsset.cpp Normal file
View 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
View 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;
};
}

View 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

View 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;
};
}

View 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;
};
}

View File

@@ -7,11 +7,11 @@ using namespace std;
namespace polyvox { namespace polyvox {
class streambuf : public std::streambuf class StreamBufferAdapter : public std::streambuf
{ {
public: public:
streambuf(const char *begin, const char *end); StreamBufferAdapter(const char *begin, const char *end);
~streambuf() { ~StreamBufferAdapter() {
} }
streamsize size(); 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); setg((char*)begin, (char*)begin, (char*)end);
} }
streamsize streambuf::size() { streamsize StreamBufferAdapter::size() {
return egptr() - eback(); return egptr() - eback();
} }
streambuf::int_type streambuf::underflow() streambuf::int_type StreamBufferAdapter::underflow()
{ {
if (gptr() == egptr()) { if (gptr() == egptr()) {
return traits_type::eof(); return traits_type::eof();
@@ -43,7 +43,7 @@ streambuf::int_type streambuf::underflow()
return *(gptr()); return *(gptr());
} }
streambuf::int_type streambuf::uflow() streambuf::int_type StreamBufferAdapter::uflow()
{ {
if (gptr() == egptr()) { if (gptr() == egptr()) {
return traits_type::eof(); return traits_type::eof();
@@ -53,7 +53,7 @@ streambuf::int_type streambuf::uflow()
return *(gptr()); 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])) if (gptr() == eback() || (ch != traits_type::eof() && ch != gptr()[-1]))
return traits_type::eof(); return traits_type::eof();
@@ -61,12 +61,12 @@ streambuf::int_type streambuf::pbackfail(int_type ch)
return *(gptr()); return *(gptr());
} }
streamsize streambuf::showmanyc() streamsize StreamBufferAdapter::showmanyc()
{ {
return egptr() - gptr(); 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) { if(way == ios_base::beg) {
setg(eback(), eback()+off, egptr()); setg(eback(), eback()+off, egptr());
} else if(way == ios_base::cur) { } 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(); 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); return seekoff(sp - pos_type(off_type(0)), std::ios_base::beg, which);
} }
} }

View File

@@ -5,11 +5,15 @@
namespace polyvox { 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: public:
streambuf(const char *begin, const char *end); StreamBufferAdapter(const char *begin, const char *end);
~streambuf() { ~StreamBufferAdapter() {
} }
streamsize size(); streamsize size();

View File

@@ -2,6 +2,8 @@ import 'dart:async';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
typedef FilamentAsset = int;
abstract class FilamentController { abstract class FilamentController {
void onFilamentViewCreated(int id); void onFilamentViewCreated(int id);
Future setBackgroundImage(String path); Future setBackgroundImage(String path);
@@ -9,22 +11,21 @@ abstract class FilamentController {
Future removeSkybox(); Future removeSkybox();
Future loadIbl(String path); Future loadIbl(String path);
Future removeIbl(); Future removeIbl();
Future loadGlb(String path); Future<FilamentAsset> loadGlb(String path);
Future loadGltf(String path, String relativeResourcePath); Future<FilamentAsset> loadGltf(String path, String relativeResourcePath);
Future panStart(double x, double y); Future panStart(double x, double y);
Future panUpdate(double x, double y); Future panUpdate(double x, double y);
Future panEnd(); Future panEnd();
Future rotateStart(double x, double y); Future rotateStart(double x, double y);
Future rotateUpdate(double x, double y); Future rotateUpdate(double x, double y);
Future rotateEnd(); Future rotateEnd();
Future applyWeights(List<double> weights); Future applyWeights(FilamentAsset asset, List<double> weights);
Future<List<String>> getTargetNames(String meshName); Future<List<String>> getTargetNames(FilamentAsset asset, String meshName);
Future<List<String>> getAnimationNames(); Future<List<String>> getAnimationNames(FilamentAsset asset);
Future releaseSourceAssets(); Future removeAsset(FilamentAsset asset);
Future removeAsset(); Future playAnimation(FilamentAsset asset, int index, {bool loop = false});
Future playAnimation(int index, {bool loop = false}); Future stopAnimation(FilamentAsset asset);
Future stopAnimation(); Future setCamera(FilamentAsset asset, String name);
Future setCamera(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]. /// 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. /// 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 /// 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); List<double> data, int numWeights, int numFrames, double frameLengthInMs);
Future createMorpher(String meshName, List<int> primitives);
Future zoom(double z); Future zoom(double z);
} }
@@ -86,15 +86,18 @@ class PolyvoxFilamentController extends FilamentController {
await _channel.invokeMethod("removeIbl"); await _channel.invokeMethod("removeIbl");
} }
Future loadGlb(String path) async { Future<FilamentAsset> loadGlb(String path) async {
print("Loading GLB at $path "); 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( print(
"Loading GLTF at $path with relative resource path $relativeResourcePath"); "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 { Future panStart(double x, double y) async {
@@ -121,53 +124,45 @@ class PolyvoxFilamentController extends FilamentController {
await _channel.invokeMethod("rotateEnd"); await _channel.invokeMethod("rotateEnd");
} }
Future applyWeights(List<double> weights) async { Future applyWeights(FilamentAsset asset, List<double> weights) async {
await _channel.invokeMethod("applyWeights", weights); await _channel.invokeMethod("applyWeights", [asset, weights]);
} }
Future<List<String>> getTargetNames(String meshName) async { Future<List<String>> getTargetNames(FilamentAsset asset, String meshName) async {
var result = (await _channel.invokeMethod("getTargetNames", meshName)) var result = (await _channel.invokeMethod("getTargetNames", [asset, meshName]))
.cast<String>(); .cast<String>();
return result; return result;
} }
Future<List<String>> getAnimationNames() async { Future<List<String>> getAnimationNames(FilamentAsset asset) async {
var result = var result =
(await _channel.invokeMethod("getAnimationNames")).cast<String>(); (await _channel.invokeMethod("getAnimationNames", asset)).cast<String>();
return result; 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 { double frameLengthInMs) async {
await _channel.invokeMethod( await _channel.invokeMethod(
"animateWeights", [weights, numWeights, numFrames, frameLengthInMs]); "animateWeights", [asset, weights, numWeights, numFrames, frameLengthInMs]);
} }
Future releaseSourceAssets() async { Future removeAsset(FilamentAsset asset) async {
await _channel.invokeMethod("releaseSourceAssets"); await _channel.invokeMethod("removeAsset", asset);
}
Future removeAsset() async {
await _channel.invokeMethod("removeAsset");
} }
Future zoom(double z) async { Future zoom(double z) async {
await _channel.invokeMethod("zoom", z); await _channel.invokeMethod("zoom", z);
} }
Future createMorpher(String meshName, List<int> primitives) async { Future playAnimation(FilamentAsset asset, int index, {bool loop = false}) async {
await _channel.invokeMethod("createMorpher", [meshName, primitives]); await _channel.invokeMethod("playAnimation", [asset, index, loop]);
} }
Future playAnimation(int index, {bool loop = false}) async { Future stopAnimation(FilamentAsset asset) async {
await _channel.invokeMethod("playAnimation", [index, loop]);
}
Future stopAnimation() async {
await _channel.invokeMethod("stopAnimation"); await _channel.invokeMethod("stopAnimation");
} }
Future setCamera(String name) async { Future setCamera(FilamentAsset asset, String name) async {
await _channel.invokeMethod("setCamera", name); await _channel.invokeMethod("setCamera", [asset, name]);
} }
} }

View File

@@ -4,7 +4,7 @@ version: 0.0.1
homepage: homepage:
environment: environment:
sdk: ">=2.12.0 <3.0.0" sdk: ">=2.17.1 <3.0.0"
flutter: ">=1.20.0" flutter: ">=1.20.0"
dependencies: dependencies: