add manual bone transform animation

This commit is contained in:
Nick Fisher
2022-12-15 16:56:57 +08:00
parent eb705347fc
commit f50b5328db
18 changed files with 1422 additions and 500 deletions

View File

@@ -60,11 +60,11 @@ interface FilamentInterop : Library {
fun apply_weights(asset:Pointer, weights:FloatArray, size:Int);
fun animate_weights(asset:Pointer, frames:FloatArray, numWeights:Int, numFrames:Int, frameRate:Float);
fun set_animation(asset:Pointer, frames:FloatArray, numWeights:Int, numFrames:Int, frameRate:Float);
fun get_target_name_count(asset:Pointer, meshName:String) : Int;
fun get_morph_target_name_count(asset:Pointer, meshName:String) : Int;
fun get_target_name(asset:Pointer, meshName:String, outPtr:Pointer, index:Int);
fun get_morph_target_name(asset:Pointer, meshName:String, outPtr:Pointer, index:Int);
fun get_animation_count(asset:Pointer) : Int;
fun get_animation_name(asset:Pointer, outPtr:Pointer, index:Int);

View File

@@ -423,15 +423,15 @@ class PolyvoxFilamentPlugin: FlutterPlugin, MethodCallHandler, ActivityAware {
result.success("OK");
}
}
"getTargetNames" -> {
"getMorphTargetNames" -> {
executor.execute {
val args = call.arguments as ArrayList<*>
val assetPtr = Pointer(args[0] as Long)
val meshName = args[1] as String
val names = mutableListOf<String>()
val outPtr = Memory(256)
for(i in 0.._lib.get_target_name_count(assetPtr, meshName) - 1) {
_lib.get_target_name(assetPtr, meshName, outPtr, i)
for(i in 0.._lib.get_morph_target_name_count(assetPtr, meshName) - 1) {
_lib.get_morph_target_name(assetPtr, meshName, outPtr, i)
val name = outPtr.getString(0)
names.add(name)
}
@@ -451,7 +451,7 @@ class PolyvoxFilamentPlugin: FlutterPlugin, MethodCallHandler, ActivityAware {
result.success(names)
}
}
"applyWeights" -> {
"setMorphTargetWeights" -> {
executor.execute {
val args = call.arguments as ArrayList<*>
val assetPtr = Pointer(args[0] as Long)
@@ -461,7 +461,7 @@ class PolyvoxFilamentPlugin: FlutterPlugin, MethodCallHandler, ActivityAware {
result.success("OK");
}
}
"animateWeights" -> {
"setAnimation" -> {
executor.execute {
val args = call.arguments as ArrayList<Any?>
val assetPtr = Pointer(args[0] as Long)
@@ -470,7 +470,7 @@ class PolyvoxFilamentPlugin: FlutterPlugin, MethodCallHandler, ActivityAware {
val numFrames = args[3] as Int
val frameLenInMs = args[4] as Double
_lib.animate_weights(assetPtr, frames.toFloatArray(), numWeights, numFrames, frameLenInMs.toFloat())
_lib.set_animation(assetPtr, frames.toFloatArray(), numWeights, numFrames, frameLenInMs.toFloat())
result.success("OK");
}
}

View File

@@ -4,6 +4,8 @@ import 'package:flutter/material.dart';
import 'package:polyvox_filament/filament_controller.dart';
import 'package:polyvox_filament/filament_gesture_detector.dart';
import 'package:polyvox_filament/filament_widget.dart';
import 'package:polyvox_filament/animations/animation_builder.dart';
import 'package:polyvox_filament/animations/animations.dart';
void main() {
runApp(const MyApp());
@@ -36,6 +38,195 @@ class _MyAppState extends State<MyApp> {
super.initState();
}
void onClick(int index) async {
switch (index) {
case -1:
await _filamentController.initialize();
break;
case -2:
await _filamentController.render();
break;
case -4:
setState(() {
_rendering = !_rendering;
_filamentController.setRendering(_rendering);
});
break;
case -5:
setState(() {
_framerate = _framerate == 60 ? 30 : 60;
_filamentController.setFrameRate(_framerate);
});
break;
case 0:
await _filamentController.setBackgroundImage('assets/background.ktx');
break;
case 1:
await _filamentController
.loadSkybox('assets/default_env/default_env_skybox.ktx');
break;
case -3:
await _filamentController
.loadIbl('assets/default_env/default_env_ibl.ktx');
break;
case 2:
await _filamentController.removeSkybox();
break;
case 3:
_cube = await _filamentController.loadGlb('assets/cube.glb');
_animationNames = await _filamentController.getAnimationNames(_cube!);
break;
case 4:
if (_cube != null) {
await _filamentController.removeAsset(_cube!);
}
_cube =
await _filamentController.loadGltf('assets/cube.gltf', 'assets');
print(await _filamentController.getAnimationNames(_cube!));
break;
case 5:
if (_flightHelmet == null) {
_flightHelmet = await _filamentController.loadGltf(
'assets/FlightHelmet/FlightHelmet.gltf', 'assets/FlightHelmet');
}
break;
case 6:
await _filamentController.removeAsset(_cube!);
break;
case 7:
await _filamentController.setMorphTargetWeights(
_cube!, List.filled(8, 1.0));
break;
case 8:
await _filamentController.setMorphTargetWeights(
_cube!, List.filled(8, 0));
break;
case 9:
_filamentController.playAnimations(
_cube!, List.generate(_animationNames.length, (i) => i),
loop: _loop);
break;
case 10:
_filamentController.stopAnimation(_cube!, 0);
break;
case 11:
setState(() {
_loop = !_loop;
});
break;
case 14:
_filamentController.setCamera(_cube!, "Camera_Orientation");
break;
case 15:
final animation = AnimationBuilder()
.setFramerate(30)
.setDuration(4)
.setNumMorphWeights(8)
.interpolateMorphWeights(0, 4, 0, 1)
.interpolateBoneTransform(
"Bone.001",
"Cube.001",
2,
4,
Vec3(),
Vec3(),
// Vec3(x: 1, y: 1, z: 1),
Quaternion(x: 0, y: 0, z: 0, w: 1),
Quaternion(x: 1, y: 1, z: 1, w: 1))
// Quaternion(x: 1, y: 1, z: 1, w: 1))
.build();
_filamentController.setAnimation(_cube!, animation);
break;
case 16:
_targetNames =
await _filamentController.getMorphTargetNames(_cube!, "Cube");
setState(() {});
break;
case 17:
_animationNames = await _filamentController.getAnimationNames(_cube!);
setState(() {});
break;
case 18:
await _filamentController.panStart(1, 1);
await _filamentController.panUpdate(1, 2);
await _filamentController.panEnd();
break;
case 19:
await _filamentController.panStart(1, 1);
await _filamentController.panUpdate(0, 0);
await _filamentController.panEnd();
break;
case 20:
await _filamentController.clearAssets();
break;
case 21:
await _filamentController.setTexture(_cube!, "assets/background.png");
break;
case 22:
await _filamentController.transformToUnitCube(_cube!);
break;
case 23:
await _filamentController.setPosition(_cube!, 1.0, 1.0, -1.0);
break;
case 24:
await _filamentController.setRotation(_cube!, pi / 2, 0.0, 1.0, 0.0);
break;
case 25:
setState(() {
_vertical = !_vertical;
});
break;
case 26:
await _filamentController.setCameraPosition(0, 0, 3);
await _filamentController.setCameraRotation(0, 0, 1, 0);
break;
case 27:
_framerate = _framerate == 60 ? 30 : 60;
await _filamentController.setFrameRate(_framerate);
break;
case 28:
await _filamentController.setBackgroundImagePosition(25, 25);
break;
case 29:
_light = await _filamentController.addLight(
1, 6500, 15000000, 0, 1, 0, 0, -1, 0, true);
_light = await _filamentController.addLight(
2, 6500, 15000000, 0, 0, 1, 0, 0, -1, true);
break;
case 30:
if (_light != null) {
await _filamentController.removeLight(_light!);
}
break;
case 31:
await _filamentController.clearLights();
break;
case 32:
await _filamentController.setBoneTransform(
_cube!,
"Bone.001",
"Cube.001",
BoneTransform([Vec3(x: 0, y: 0.0, z: 0.0)],
[Quaternion(x: 1, y: 1, z: 1, w: 1)]));
break;
}
}
Widget _item({int value = 0, Widget? child = null}) {
return GestureDetector(
onTap: () {
onClick(value);
},
child: Container(
margin: EdgeInsets.symmetric(vertical: 10), child: child));
}
@override
Widget build(BuildContext context) {
return MaterialApp(
@@ -43,356 +234,80 @@ class _MyAppState extends State<MyApp> {
color: Colors.white,
home: Scaffold(
backgroundColor: Colors.white,
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
width: _vertical ? 200 : 400,
height: _vertical ? 400 : 200,
alignment: Alignment.center,
child: SizedBox(
child: FilamentGestureDetector(
showControls: true,
controller: _filamentController,
child: FilamentWidget(
controller: _filamentController,
)),
)),
Text(
"Target names : ${_targetNames.join(",")}, Animation names : ${_animationNames.join(",")}"),
Align(
alignment: Alignment.bottomLeft,
child: Container(
color: Colors.white,
margin: const EdgeInsets.all(15),
child: PopupMenuButton<int>(
padding: EdgeInsets.all(50),
iconSize: 36,
child: const Icon(Icons.menu),
onSelected: (int item) async {
switch (item) {
case -1:
await _filamentController.initialize();
break;
case -2:
await _filamentController.render();
break;
case 0:
await _filamentController
.setBackgroundImage(
'assets/background.ktx');
break;
case 1:
await _filamentController.loadSkybox(
'assets/default_env/default_env_skybox.ktx');
break;
case -3:
await _filamentController.loadIbl(
'assets/default_env/default_env_ibl.ktx');
break;
case 2:
await _filamentController.removeSkybox();
break;
case 3:
_cube = await _filamentController
.loadGlb('assets/cube.glb');
_animationNames = await _filamentController
.getAnimationNames(_cube!);
break;
case 4:
if (_cube != null) {
await _filamentController
.removeAsset(_cube!);
}
_cube = await _filamentController.loadGltf(
'assets/cube.gltf', 'assets');
print(await _filamentController
.getAnimationNames(_cube!));
break;
case 5:
if (_flightHelmet == null) {
_flightHelmet =
await _filamentController.loadGltf(
'assets/FlightHelmet/FlightHelmet.gltf',
'assets/FlightHelmet');
}
break;
case 6:
await _filamentController
.removeAsset(_cube!);
break;
case 7:
await _filamentController.applyWeights(
_cube!, List.filled(8, 1.0));
break;
case 8:
await _filamentController.applyWeights(
_cube!, List.filled(8, 0));
break;
case 9:
_filamentController.playAnimations(
_cube!,
List.generate(
_animationNames.length, (i) => i),
loop: _loop);
break;
case 10:
_filamentController.stopAnimation(
_cube!, 0);
break;
case 11:
setState(() {
_loop = !_loop;
});
break;
case 14:
_filamentController.setCamera(
_cube!, "Camera_Orientation");
break;
case 15:
final framerate = 30;
final totalSecs = 5;
final numWeights = 8;
final totalFrames = framerate * totalSecs;
final frames = List.generate(
totalFrames,
(frame) => List.filled(
numWeights, frame / totalFrames));
_filamentController.animate(
_cube!,
frames.reduce((a, b) => a + b),
numWeights,
totalFrames,
1000 / framerate.toDouble());
break;
case 16:
_targetNames = await _filamentController
.getTargetNames(_cube!, "Cube");
setState(() {});
break;
case 17:
_animationNames = await _filamentController
.getAnimationNames(_cube!);
setState(() {});
break;
case 18:
await _filamentController.panStart(1, 1);
await _filamentController.panUpdate(1, 2);
await _filamentController.panEnd();
break;
case 19:
await _filamentController.panStart(1, 1);
await _filamentController.panUpdate(0, 0);
await _filamentController.panEnd();
break;
case 20:
await _filamentController.clearAssets();
break;
case 21:
await _filamentController.setTexture(
_cube!, "assets/background.png");
break;
case 22:
await _filamentController
.transformToUnitCube(_cube!);
break;
case 23:
await _filamentController.setPosition(
_cube!, 1.0, 1.0, -1.0);
break;
case 24:
await _filamentController.setRotation(
_cube!, pi / 2, 0.0, 1.0, 0.0);
break;
case 25:
setState(() {
_vertical = !_vertical;
});
break;
case 26:
await _filamentController.setCameraPosition(
0, 0, 3);
await _filamentController.setCameraRotation(
0, 0, 1, 0);
break;
case 27:
_framerate = _framerate == 60 ? 30 : 60;
await _filamentController
.setFrameRate(_framerate);
break;
case 28:
await _filamentController
.setBackgroundImagePosition(25, 25);
break;
case 29:
_light = await _filamentController.addLight(
1,
6500,
15000000,
0,
1,
0,
0,
-1,
0,
true);
_light = await _filamentController.addLight(
2,
6500,
15000000,
0,
0,
1,
0,
0,
-1,
true);
break;
case 30:
if (_light != null) {
await _filamentController
.removeLight(_light!);
}
break;
case 31:
await _filamentController.clearLights();
break;
}
},
itemBuilder: (BuildContext context) =>
<PopupMenuEntry<int>>[
const PopupMenuItem(
value: -1, child: Text("initialize")),
const PopupMenuItem(
value: -2, child: Text("render")),
const PopupMenuItem(
value: 0,
child: Text("load background image")),
const PopupMenuItem(
value: 1,
child: Text('load skybox'),
),
const PopupMenuItem(
value: -3,
child: Text('load IBL'),
),
const PopupMenuItem(
value: 2,
child: Text('remove skybox'),
),
const PopupMenuItem(
value: 3, child: Text('load cube GLB')),
const PopupMenuItem(
value: 4,
child: Text('load cube GLTF')),
const PopupMenuItem(
value: 21,
child: Text('swap cube texture')),
const PopupMenuItem(
value: 22,
child: Text('transform to unit cube')),
const PopupMenuItem(
value: 23,
child:
Text('set position to 1, 1, -1')),
const PopupMenuItem(
value: 24,
child:
Text('rotate by pi around Y axis')),
const PopupMenuItem(
value: 5,
child: Text('load flight helmet')),
const PopupMenuItem(
value: 6, child: Text('remove cube')),
const PopupMenuItem(
value: 20,
child: Text('clear all assets')),
const PopupMenuItem(
value: 7,
child: Text('set all weights to 1')),
const PopupMenuItem(
value: 8,
child: Text('set all weights to 0')),
const PopupMenuItem(
value: 9,
child: Text('play all animations')),
const PopupMenuItem(
value: 10,
child: Text('stop animations')),
PopupMenuItem(
value: 11,
child: Text(_loop
? "don't loop animation"
: "loop animation")),
const PopupMenuItem(
value: 14, child: Text('set camera')),
const PopupMenuItem(
value: 15,
child: Text('animate weights')),
const PopupMenuItem(
value: 16,
child: Text('get target names')),
const PopupMenuItem(
value: 17,
child: Text('get animation names')),
const PopupMenuItem(
value: 18, child: Text('pan left')),
const PopupMenuItem(
value: 19, child: Text('pan right')),
PopupMenuItem(
value: 25,
child: Text(_vertical
? 'set horizontal'
: 'set vertical')),
PopupMenuItem(
value: 26,
child: Text('set camera pos to 0,0,3')),
PopupMenuItem(
value: 27,
child: Text('toggle framerate')),
PopupMenuItem(
value: 28,
child: Text('set bg image pos')),
PopupMenuItem(
value: 29, child: Text('add light')),
PopupMenuItem(
value: 30, child: Text('remove light')),
PopupMenuItem(
value: 31,
child: Text('clear all lights')),
]))),
Align(
alignment: Alignment.bottomRight,
child: Row(children: [
GestureDetector(
onTap: () {
setState(() {
_rendering = !_rendering;
_filamentController.setRendering(_rendering);
});
},
child: Container(
color: Colors.white,
padding: const EdgeInsets.all(50),
child: Text("Rendering: $_rendering "))),
GestureDetector(
onTap: () {
setState(() {
_framerate = _framerate == 60 ? 30 : 60;
_filamentController.setFrameRate(_framerate);
});
},
child: Container(
color: Colors.white,
padding: const EdgeInsets.all(50),
child: Text("$_framerate fps")))
]))
])));
body: Row(children: [
SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
"Target names : ${_targetNames.join(",")}, Animation names : ${_animationNames.join(",")}"),
_item(value: -1, child: Text("initialize")),
_item(value: -2, child: Text("render")),
_item(value: -4, child: Text("Rendering: $_rendering ")),
_item(value: -5, child: Text("$_framerate fps")),
_item(value: 0, child: Text("load background image")),
_item(
value: 1,
child: Text('load skybox'),
),
_item(
value: -3,
child: Text('load IBL'),
),
_item(
value: 2,
child: Text('remove skybox'),
),
_item(value: 3, child: Text('load cube GLB')),
_item(value: 4, child: Text('load cube GLTF')),
_item(value: 21, child: Text('swap cube texture')),
_item(value: 22, child: Text('transform to unit cube')),
_item(value: 23, child: Text('set position to 1, 1, -1')),
_item(
value: 32,
child: Text('set bone transform to 1, 1, -1')),
_item(value: 24, child: Text('rotate by pi around Y axis')),
_item(value: 5, child: Text('load flight helmet')),
_item(value: 6, child: Text('remove cube')),
_item(value: 20, child: Text('clear all assets')),
_item(value: 7, child: Text('set all weights to 1')),
_item(value: 8, child: Text('set all weights to 0')),
_item(value: 9, child: Text('play all animations')),
_item(value: 10, child: Text('stop animations')),
_item(
value: 11,
child: Text(
_loop ? "don't loop animation" : "loop animation")),
_item(value: 14, child: Text('set camera')),
_item(value: 15, child: Text('animate weights')),
_item(value: 16, child: Text('get target names')),
_item(value: 17, child: Text('get animation names')),
_item(value: 18, child: Text('pan left')),
_item(value: 19, child: Text('pan right')),
_item(
value: 25,
child: Text(
_vertical ? 'set horizontal' : 'set vertical')),
_item(value: 26, child: Text('set camera pos to 0,0,3')),
_item(value: 27, child: Text('toggle framerate')),
_item(value: 28, child: Text('set bg image pos')),
_item(value: 29, child: Text('add light')),
_item(value: 30, child: Text('remove light')),
_item(value: 31, child: Text('clear all lights')),
])),
Container(
width: _vertical ? 200 : 400,
height: _vertical ? 400 : 200,
alignment: Alignment.center,
child: SizedBox(
child: FilamentGestureDetector(
showControls: true,
controller: _filamentController,
child: FilamentWidget(
controller: _filamentController,
)),
)),
])));
}
}

View File

@@ -168,7 +168,7 @@ public class SwiftPolyvoxFilamentPlugin: NSObject, FlutterPlugin, FlutterTexture
Float(args[8] as! Double),
args[9] as! Bool)
result(entity);
case "animateWeights":
case "setAnimation":
let args = call.arguments as! Array<Any?>
let assetPtr = UnsafeMutableRawPointer.init(bitPattern: args[0] as! Int)
let frameData = args[1] as! Array<Float>
@@ -176,7 +176,7 @@ public class SwiftPolyvoxFilamentPlugin: NSObject, FlutterPlugin, FlutterTexture
let numFrames = args[3] as! Int
let frameLenInMs = args[4] as! Double
frameData.map { Float($0)}.withUnsafeBufferPointer {
animate_weights(assetPtr, UnsafeMutablePointer<Float>.init(mutating:$0.baseAddress), Int32(numWeights), Int32(numFrames), Float(frameLenInMs))
set_animation(assetPtr, UnsafeMutablePointer<Float>.init(mutating:$0.baseAddress), Int32(numWeights), Int32(numFrames), Float(frameLenInMs))
}
result("OK")
case "initialize":
@@ -232,15 +232,15 @@ public class SwiftPolyvoxFilamentPlugin: NSObject, FlutterPlugin, FlutterTexture
let animationIndex = args[1] as! Int32
stop_animation(assetPtr, animationIndex) // TODO
result("OK");
case "getTargetNames":
case "getMorphTargetNames":
let args = call.arguments as! Array<Any?>
let assetPtr = UnsafeMutableRawPointer.init(bitPattern: args[0] as! Int)
let meshName = args[1] as! String
let numNames = get_target_name_count(assetPtr, meshName)
let numNames = get_morph_target_name_count(assetPtr, meshName)
var names = [String]()
for i in 0...numNames - 1{
let outPtr = UnsafeMutablePointer<CChar>.allocate(capacity:256)
get_target_name(assetPtr, meshName, outPtr, i)
get_morph_target_name(assetPtr, meshName, outPtr, i)
names.append(String(cString:outPtr))
}
result(names);
@@ -254,7 +254,7 @@ public class SwiftPolyvoxFilamentPlugin: NSObject, FlutterPlugin, FlutterTexture
names.append(String(cString:outPtr))
}
result(names);
case "applyWeights":
case "setMorphTargetWeights":
let args = call.arguments as! Array<Any?>
let assetPtr = UnsafeMutableRawPointer.init(bitPattern: args[0] as! Int)
let weights = args[1] as! Array<Float>

View File

@@ -38,7 +38,30 @@ void grab_end(void* viewer);
void apply_weights(void* asset, float* const weights, int count);
void animate_weights(void* asset, float* data, int numWeights, int numFrames, float frameRate);
void set_animation(
void* asset,
float* morphData,
int numMorphWeights,
float* boneData,
const char** boneNames,
const char** meshNames,
int numBones,
int numFrames,
float frameLengthInMs
);
void set_bone_transform(
void* asset,
const char* boneName,
const char* entityName,
float transX,
float transY,
float transZ,
float quatX,
float quatY,
float quatZ,
float quatW
);
void play_animation(void* asset, int index, bool loop, bool reverse);
void stop_animation(void* asset, int index);
@@ -47,9 +70,9 @@ int get_animation_count(void* asset);
void get_animation_name(void* asset, char* const outPtr, int index);
void get_target_name(void* asset, const char* meshName, char* const outPtr, int index );
void get_morph_target_name(void* asset, const char* meshName, char* const outPtr, int index );
int get_target_name_count(void* asset, const char* meshName);
int get_morph_target_name_count(void* asset, const char* meshName);
void remove_asset(void* viewer, void* asset);

View File

@@ -31,7 +31,7 @@ namespace polyvox {
SceneAsset(FilamentAsset* asset, Engine* engine, NameComponentManager* ncm, LoadResource loadResource, FreeResource freeResource);
~SceneAsset();
unique_ptr<vector<string>> getTargetNames(const char* meshName);
unique_ptr<vector<string>> getMorphTargetNames(const char* meshName);
unique_ptr<vector<string>> getAnimationNames();
///
@@ -57,18 +57,39 @@ namespace polyvox {
///
/// Manually set the weights for all morph targets in the assets to the provided values.
/// See [animateWeights] if you want to automatically
/// See [setAnimation] if you want to do the same across a number of frames (and extended to bone transforms).
///
void applyWeights(float* weights, int count);
void setMorphTargetWeights(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
/// [data] will be copied; you should ensure this is freed after invoking this function.
/// Animates the asset's morph targets/bone transforms according to the frame weights/transforms specified in [morphData]/[boneData].
/// The duration of each "frame" is specified by [frameLengthInMs] (i.e. this is not the framerate of the renderer).
/// [morphData] is a contiguous chunk of floats whose length will be (numMorphWeights * numFrames).
/// [boneData] is a contiguous chunk of floats whose length will be (numBones * 7 * numFrames) (where 7 is 3 floats for translation, 4 for quat rotation).
/// [morphData] and [boneData] will both be copied, so remember to free these after calling this function.
///
void animateWeights(float* data, int numWeights, int numFrames, float frameLengthInMs);
void setAnimation(
float* morphData,
int numMorphWeights,
float* boneData,
const char** boneNames,
const char** meshNames,
int numBones,
int numFrames,
float frameLengthInMs
);
void setBoneTransform(
const char* boneName,
const char* meshName,
float transX,
float transY,
float transZ,
float quatX,
float quatY,
float quatZ,
float quatW
);
void transformToUnitCube();
@@ -77,6 +98,8 @@ namespace polyvox {
void setPosition(float x, float y, float z);
void setRotation(float rads, float x, float y, float z);
const utils::Entity* getCameraEntities();
@@ -93,15 +116,15 @@ namespace polyvox {
Engine* _engine = nullptr;
NameComponentManager* _ncm;
void updateMorphAnimation();
void updateRuntimeAnimation();
void updateEmbeddedAnimations();
Animator* _animator;
// animation flags;
unique_ptr<MorphAnimationStatus> _morphAnimationBuffer;
vector<EmbeddedAnimationStatus> _embeddedAnimationStatus;
unique_ptr<RuntimeAnimation> _runtimeAnimationBuffer;
vector<GLTFAnimation> _embeddedAnimationStatus;
LoadResource _loadResource;
FreeResource _freeResource;

View File

@@ -4,11 +4,14 @@
#include <memory>
#include <chrono>
#include <iostream>
#include <vector>
#include "ResourceBuffer.hpp"
namespace polyvox {
using namespace std;
using namespace std;
//
// Typedef for a function that loads a resource into a ResourceBuffer from an asset URI.
//
@@ -22,11 +25,13 @@ namespace polyvox {
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.
// Currently, an instance will be constructed for every animation in an asset whenever a SceneAsset is created (and thus will persist for the lifetime of the SceneAsset).
// Holds the current state of a GLTF animation.
// Whenever a SceneAsset is created, an instance of GLTFAnimation will be created for every embedded animation.
// On each frame loop, we check if [play] is true, and if so, advance the animation to the correct frame based on [startedAt].
// The [GLTFAnimation] will persist for the lifetime of the SceneAsset.
//
struct EmbeddedAnimationStatus {
EmbeddedAnimationStatus(bool loop, bool reverse) : loop(loop), reverse(reverse) {}
struct GLTFAnimation {
GLTFAnimation(bool loop, bool reverse) : loop(loop), reverse(reverse) {}
//
// A flag that is checked each frame to determine whether or not the animation should play.
@@ -59,34 +64,64 @@ namespace polyvox {
//
time_point_t startedAt;
};
//
// Holds the current state of a morph-target animation in a GLTF asset.
// An animation created by manually passing frame data for morph weights/bone transforms.
//
struct MorphAnimationStatus {
struct RuntimeAnimation {
MorphAnimationStatus(float* data,
int numWeights,
int numFrames,
float frameLengthInMs) : numFrames(numFrames), frameLengthInMs(frameLengthInMs), numWeights(numWeights) {
size_t size = numWeights * numFrames * sizeof(float);
frameData = (float*)malloc(size);
memcpy(frameData, data, size);
RuntimeAnimation(float* morphData,
int numMorphWeights,
float* boneData,
const char** boneNames,
const char** meshNames,
int numBones,
int numFrames,
float frameLengthInMs) :
mNumFrames(numFrames),
mFrameLengthInMs(frameLengthInMs),
mNumMorphWeights(numMorphWeights),
mNumBones(numBones) {
if(numMorphWeights > 0) {
size_t morphSize = numMorphWeights * mNumFrames * sizeof(float);
mMorphFrameData = (float*)malloc(morphSize);
memcpy(mMorphFrameData, morphData, morphSize);
}
if(numBones > 0) {
size_t boneSize = numBones * numFrames * 7 * sizeof(float);
mBoneFrameData = (float*)malloc(boneSize);
memcpy(mBoneFrameData, boneData, boneSize);
}
for(int i =0; i < numBones; i++) {
mBoneNames.push_back(string(boneNames[i]));
mMeshNames.push_back(string(meshNames[i]));
}
}
~MorphAnimationStatus() {
delete(frameData);
~RuntimeAnimation() {
delete(mMorphFrameData);
delete(mBoneFrameData);
}
int frameIndex = -1;
int numFrames = -1;
float frameLengthInMs = 0;
int mNumFrames = -1;
float mFrameLengthInMs = 0;
time_point_t startTime;
float* frameData = nullptr;
int numWeights = 0;
float* mMorphFrameData = nullptr;
int mNumMorphWeights = 0;
float* mBoneFrameData = nullptr;
int mNumBones = 0;
vector<string> mBoneNames;
vector<string> mMeshNames;
};
}

View File

@@ -170,16 +170,16 @@ FilamentViewer::FilamentViewer(void* context, LoadResource loadResource,
// options.minScale = filament::math::float2{ minScale };
// options.maxScale = filament::math::float2{ maxScale };
// options.sharpness = sharpness;
options.quality = View::QualityLevel::MEDIUM;
;
options.quality = View::QualityLevel::HIGH;
_view->setDynamicResolutionOptions(options);
View::MultiSampleAntiAliasingOptions multiSampleAntiAliasingOptions;
multiSampleAntiAliasingOptions.enabled = false;
//View::MultiSampleAntiAliasingOptions multiSampleAntiAliasingOptions;
//multiSampleAntiAliasingOptions.enabled = true;
_view->setMultiSampleAntiAliasingOptions(multiSampleAntiAliasingOptions);
//_view->setMultiSampleAntiAliasingOptions(multiSampleAntiAliasingOptions);
// _view->setAntiAliasing(AntiAliasing::NONE);
//_view->setAntiAliasing(AntiAliasing::NONE);
_materialProvider =
// new UnlitMaterialProvider(_engine);

View File

@@ -141,13 +141,59 @@ extern "C" {
}
void apply_weights(void* asset, float* const weights, int count) {
((SceneAsset*)asset)->applyWeights(weights, count);
((SceneAsset*)asset)->setMorphTargetWeights(weights, count);
}
void animate_weights(void* asset, float* data, int numWeights, int numFrames, float frameRate) {
((SceneAsset*)asset)->animateWeights((float*)data, numWeights, numFrames, frameRate);
void set_animation(
void* asset,
float* morphData,
int numMorphWeights,
float* boneData,
const char** boneNames,
const char** meshNames,
int numBones,
int numFrames,
float frameLengthInMs) {
((SceneAsset*)asset)->setAnimation(
morphData,
numMorphWeights,
boneData,
boneNames,
meshNames,
numBones,
numFrames,
frameLengthInMs
);
}
void set_bone_transform(
void* asset,
const char* boneName,
const char* entityName,
float transX,
float transY,
float transZ,
float quatX,
float quatY,
float quatZ,
float quatW
) {
((SceneAsset*)asset)->setBoneTransform(
boneName,
entityName,
transX,
transY,
transZ,
quatX,
quatY,
quatZ,
quatW
);
}
void play_animation(void* asset, int index, bool loop, bool reverse) {
((SceneAsset*)asset)->playAnimation(index, loop, reverse);
}
@@ -163,13 +209,13 @@ extern "C" {
strcpy(outPtr, name.c_str());
}
int get_target_name_count(void* asset, const char* meshName) {
unique_ptr<vector<string>> names = ((SceneAsset*)asset)->getTargetNames(meshName);
int get_morph_target_name_count(void* asset, const char* meshName) {
unique_ptr<vector<string>> names = ((SceneAsset*)asset)->getMorphTargetNames(meshName);
return names->size();
}
void get_target_name(void* asset, const char* meshName, char* const outPtr, int index ) {
unique_ptr<vector<string>> names = ((SceneAsset*)asset)->getTargetNames(meshName);
void get_morph_target_name(void* asset, const char* meshName, char* const outPtr, int index ) {
unique_ptr<vector<string>> names = ((SceneAsset*)asset)->getMorphTargetNames(meshName);
string name = names->at(index);
strcpy(outPtr, name.c_str());
}

View File

@@ -34,7 +34,7 @@ SceneAsset::SceneAsset(FilamentAsset *asset, Engine *engine,
_animator = _asset->getInstance()->getAnimator();
for (int i = 0; i < _animator->getAnimationCount(); i++) {
_embeddedAnimationStatus.push_back(
EmbeddedAnimationStatus(false,false));
GLTFAnimation(false,false));
}
Log("Created animation buffers for %d", _embeddedAnimationStatus.size());
}
@@ -47,7 +47,7 @@ SceneAsset::~SceneAsset() {
}
}
void SceneAsset::applyWeights(float *weights, int count) {
void SceneAsset::setMorphTargetWeights(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]);
@@ -55,54 +55,78 @@ void SceneAsset::applyWeights(float *weights, int 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::setAnimation(
float* morphData,
int numMorphWeights,
float* boneData,
const char** boneNames,
const char** meshNames,
int numBones,
int numFrames,
float frameLengthInMs) {
_runtimeAnimationBuffer = std::make_unique<RuntimeAnimation>(
morphData,
numMorphWeights,
boneData,
boneNames,
meshNames,
numBones,
numFrames,
frameLengthInMs
);
}
void SceneAsset::updateAnimations() {
updateMorphAnimation();
updateRuntimeAnimation();
updateEmbeddedAnimations();
}
void SceneAsset::updateMorphAnimation() {
if (!_morphAnimationBuffer) {
void SceneAsset::updateRuntimeAnimation() {
if (!_runtimeAnimationBuffer) {
return;
}
if (_morphAnimationBuffer->frameIndex == -1) {
// auto ms = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
// Log("Frame 1 at %lu", ms);
_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 (_runtimeAnimationBuffer->frameIndex == -1) {
_runtimeAnimationBuffer->startTime = high_resolution_clock::now();
}
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);
duration<double, std::milli> dur =
high_resolution_clock::now() - _runtimeAnimationBuffer->startTime;
int frameIndex =
static_cast<int>(dur.count() / _runtimeAnimationBuffer->mFrameLengthInMs);
// if the animation has finished, return early
if (frameIndex >= _runtimeAnimationBuffer->mNumFrames) {
_runtimeAnimationBuffer = nullptr;
return;
}
if (frameIndex > _runtimeAnimationBuffer->frameIndex) {
_runtimeAnimationBuffer->frameIndex = frameIndex;
if(_runtimeAnimationBuffer->mMorphFrameData) {
auto morphFramePtrOffset = frameIndex * _runtimeAnimationBuffer->mNumMorphWeights;
setMorphTargetWeights(_runtimeAnimationBuffer->mMorphFrameData + morphFramePtrOffset,
_runtimeAnimationBuffer->mNumMorphWeights);
}
if(_runtimeAnimationBuffer->mBoneFrameData) {
for(int i = 0; i < _runtimeAnimationBuffer->mNumBones; i++) {
auto boneFramePtrOffset = (frameIndex * _runtimeAnimationBuffer->mNumBones * 7) + (i*7);
const char* boneName = _runtimeAnimationBuffer->mBoneNames[i].c_str();
const char* meshName = _runtimeAnimationBuffer->mMeshNames[i].c_str();
float* frame = _runtimeAnimationBuffer->mBoneFrameData + boneFramePtrOffset;
float transX = frame[0];
float transY = frame[1];
float transZ = frame[2];
float quatX = frame[3];
float quatY = frame[4];
float quatZ = frame[5];
float quatW = frame[6];
setBoneTransform(boneName, meshName, transX, transY, transZ, quatX, quatY, quatZ, quatW);
}
}
}
}
@@ -201,10 +225,12 @@ void SceneAsset::setTexture() {
void SceneAsset::updateEmbeddedAnimations() {
auto now = high_resolution_clock::now();
int animationIndex = 0;
bool playing = false;
for (auto &status : _embeddedAnimationStatus) {
if (status.play == false) {
continue;
}
playing = true;
float animationLength = _animator->getAnimationDuration(animationIndex);
@@ -241,8 +267,8 @@ void SceneAsset::updateEmbeddedAnimations() {
}
animationIndex++;
}
_animator->updateBoneMatrices();
if(playing)
_animator->updateBoneMatrices();
}
unique_ptr<vector<string>> SceneAsset::getAnimationNames() {
@@ -259,7 +285,7 @@ unique_ptr<vector<string>> SceneAsset::getAnimationNames() {
return names;
}
unique_ptr<vector<string>> SceneAsset::getTargetNames(const char *meshName) {
unique_ptr<vector<string>> SceneAsset::getMorphTargetNames(const char *meshName) {
if (!_asset) {
Log("No asset, ignoring call.");
return nullptr;
@@ -272,7 +298,7 @@ unique_ptr<vector<string>> SceneAsset::getTargetNames(const char *meshName) {
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++) {
@@ -327,6 +353,62 @@ void SceneAsset::setRotation(float rads, float x, float y, float z) {
updateTransform();
}
void SceneAsset::setBoneTransform(
const char* boneName,
const char* entityName,
float transX,
float transY,
float transZ,
float quatX,
float quatY,
float quatZ,
float quatW) {
auto filamentInstance = _asset->getInstance();
if(filamentInstance->getSkinCount()) {
Log("WARNING - skin count > 1 not currently implemented. This will probably not work");
}
filamentInstance->getAnimator()->resetBoneMatrices();
int skinIndex = 0;
const utils::Entity* joints = filamentInstance->getJointsAt(skinIndex);
size_t numJoints = filamentInstance->getJointCountAt(skinIndex);
int boneIndex = -1;
for(int i =0; i < numJoints; i++) {
const char* jointName = _ncm->getName(_ncm->getInstance(joints[i]));
if(strcmp(jointName, boneName) == 0) {
boneIndex = i;
Log("Found bone index %d for bone %s", boneIndex, boneName);
break;
}
}
if(boneIndex == -1) {
Log("Failed to find bone index %d for bone %s", boneName);
return;
}
RenderableManager &rm = _engine->getRenderableManager();
RenderableManager::Bone transform = {.unitQuaternion={quatX,quatY,quatZ,quatW}, .translation={transX,transY,transZ}};
for(int j = 0; j < _asset->getEntityCount(); j++) {
Entity e = _asset->getEntities()[j];
if(strcmp(entityName,_ncm->getName(_ncm->getInstance(e)))==0) {
Log("Setting bone transform on entity %s", _ncm->getName(_ncm->getInstance(e)));
auto inst = rm.getInstance(e);
if(!inst) {
Log("No renderable instance");
}
rm.setBones(inst, &transform, 1, boneIndex);
}
}
}
const utils::Entity *SceneAsset::getCameraEntities() {
return _asset->getCameraEntities();

View File

@@ -106,13 +106,15 @@ SceneAsset *SceneAssetLoader::fromGlb(const char *uri) {
Log("Resources loaded.");
const Entity *entities = asset->getEntities();
RenderableManager &rm = _engine->getRenderableManager();
// why did I need to explicitly enable culling?
for (int i = 0; i < asset->getEntityCount(); i++) {
Entity e = entities[i];
auto inst = rm.getInstance(e);
// check this
rm.setCulling(inst, true);
}
auto entityInstance = rm.getInstance(entities[i]);
rm.setCulling(entityInstance, true);
}
FilamentInstance* inst = asset->getInstance();
inst->getAnimator()->updateBoneMatrices();

479
ios/src/Untitled-1.cpp Normal file
View File

@@ -0,0 +1,479 @@
// // /* GL objects */
// // guint vao;
// // guint program;
// // guint position_index;
// // guint color_index;
// // GError *error = NULL;
// // std::cout << "init shaders" << std::endl;
// // if (!init_shaders (&program,
// // &position_index,
// // &color_index,
// // &error))
// // {
// // std::cout << "EROR" << std::endl;
// // return FALSE;
// // }
// // std::cout << "init buffers" << std::endl;
// // /* initialize the vertex buffers */
// // init_buffers (position_index, color_index, &vao);
// // std::cout << "use prog" << std::endl;
// // GLuint texID = glGetUniformLocation(program, "myTextureSampler");
// // // The framebuffer, which regroups 0, 1, or more textures, and 0 or 1 depth buffer.
// // GLuint FramebufferName = 0;
// // glGenFramebuffers(1, &FramebufferName);
// // glBindFramebuffer(GL_FRAMEBUFFER, FramebufferName);
// // The texture we're going to render to
// glGenTextures(1, &self->texture_id);
// if(self->texture_id == 0) {
// std::cout << "tecxtur eeror" << std::endl;
// }
// // "Bind" the newly created texture : all future texture functions will modify this texture
// glBindTexture(GL_TEXTURE_2D, self->texture_id);
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
// glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, self->width, self->height, 0, GL_RGBA,
// GL_UNSIGNED_BYTE, 0);
// // // Set "renderedTexture" as our colour attachement #0
// // glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, self->texture_id, 0);
// // // Set the list of draw buffers.
// // GLenum DrawBuffers[1] = {GL_COLOR_ATTACHMENT0};
// // glDrawBuffers(1, DrawBuffers); // "1" is the size of DrawBuffers
// // // Always check that our framebuffer is ok
// // if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
// // std::cout << "FB error" << std::endl;
// // return FALSE;
// // }
// // glBindFramebuffer(GL_FRAMEBUFFER, FramebufferName);
// // glViewport(0,0,400,200); // Render on the whole framebuffer, complete from the lower left corner to the upper righ
// // // Clear the screen
// // glClearColor (0.0, 1.0, 0.5, 1.0);
// // glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// // /* load our program */
// // glUseProgram (program);
// // // Bind our texture in Texture Unit 0
// // glActiveTexture(GL_TEXTURE0);
// // glBindTexture(GL_TEXTURE_2D, self->texture_id);
// // // Set our "renderedTexture" sampler to use Texture Unit 0
// // glUniform1i(texID, 0);
// // /* use the buffers in the VAO */
// // glBindVertexArray (vao);
// // /* draw the three vertices as a triangle */
// // glDrawArrays (GL_TRIANGLES, 0, 3);
// // /* we finished using the buffers and program */
// // glBindVertexArray (0);
// // glUseProgram (0);
// // glFlush ();
// // glGenTextures (1, &self->texture_id);
// // glBindTexture (GL_TEXTURE_2D, self->texture_id);
// // // further configuration here.
// /* position and color information for each vertex */
// struct vertex_info {
// float position[3];
// float color[3];
// };
// /* the vertex data is constant */
// static const struct vertex_info vertex_data[] = {
// { { 0.0f, 0.500f, 0.0f }, { 1.f, 0.f, 0.f } },
// { { 0.5f, -0.366f, 0.0f }, { 0.f, 1.f, 0.f } },
// { { -0.5f, -0.366f, 0.0f }, { 0.f, 0.f, 1.f } },
// };
// static void
// init_buffers (guint position_index,
// guint color_index,
// guint *vao_out)
// {
// guint vao, buffer;
// /* we need to create a VAO to store the other buffers */
// glGenVertexArrays (1, &vao);
// glBindVertexArray (vao);
// /* this is the VBO that holds the vertex data */
// glGenBuffers (1, &buffer);
// glBindBuffer (GL_ARRAY_BUFFER, buffer);
// glBufferData (GL_ARRAY_BUFFER, sizeof (vertex_data), vertex_data, GL_STATIC_DRAW);
// /* enable and set the position attribute */
// glEnableVertexAttribArray (position_index);
// glVertexAttribPointer (position_index, 3, GL_FLOAT, GL_FALSE,
// sizeof (struct vertex_info),
// (GLvoid *) (G_STRUCT_OFFSET (struct vertex_info, position)));
// /* enable and set the color attribute */
// glEnableVertexAttribArray (color_index);
// glVertexAttribPointer (color_index, 3, GL_FLOAT, GL_FALSE,
// sizeof (struct vertex_info),
// (GLvoid *) (G_STRUCT_OFFSET (struct vertex_info, color)));
// /* reset the state; we will re-enable the VAO when needed */
// glBindBuffer (GL_ARRAY_BUFFER, 0);
// glBindVertexArray (0);
// /* the VBO is referenced by the VAO */
// glDeleteBuffers (1, &buffer);
// if (vao_out != NULL)
// *vao_out = vao;
// }
// static guint
// create_shader (int shader_type,
// const char *source,
// GError **error,
// guint *shader_out)
// {
// guint shader = glCreateShader (shader_type);
// glShaderSource (shader, 1, &source, NULL);
// glCompileShader (shader);
// int status;
// glGetShaderiv (shader, GL_COMPILE_STATUS, &status);
// if (status == GL_FALSE)
// {
// std::cout << "SHADER IV ERROR" << std::endl;
// }
// if (shader_out != NULL)
// *shader_out = shader;
// return shader != 0;
// }
// static gboolean
// init_shaders (guint *program_out,
// guint *position_location_out,
// guint *color_location_out,
// GError **error)
// {
// const char *vsource = R"POO(#version 330 core
// layout(location=0) in vec3 position;
// layout(location=1) in vec2 vertexUV;
// out vec2 UV;
// void main() {
// gl_Position = vec4(position, 1.0);
// UV = vertexUV;
// })POO";
// const char *fsource = R"POO(#version 330 core
// in vec2 UV;
// // Ouput data
// layout(location = 0) out vec3 color;
// uniform sampler2D myTextureSampler;
// void main() {
// color = vec3(1.0,0.0,0.0);
// // color = texture( myTextureSampler, UV ).rgb;
// })POO";
// // const char *vsource2 = R"POO(#version 130
// // in vec3 position;
// // in vec3 color;
// // uniform mat4 mvp;
// // smooth out vec4 vertexColor;
// // void main() {
// // gl_Position = vec4(position, 1.0);
// // vertexColor = vec4(color, 1.0);
// // })POO";
// // const char *fsource2 = R"POO(#version 130
// // in vec2 UV;
// // out vec3 color;
// // uniform sampler2D renderedTexture;
// // void main() {
// // color = texture(renderedTexture, UV.xyz);
// // })POO";
// guint program = 0;
// // guint program2 = 0;
// guint vertex = 0, fragment = 0;
// // guint vertex2 = 0, fragment2 = 0;
// guint position_location = 0;
// guint color_location = 0;
// /* load the vertex shader */
// create_shader (GL_VERTEX_SHADER, vsource, error, &vertex);
// // g_bytes_unref (source);
// if (vertex == 0) {
// std::cout << "VERTEX ERROR" << std::endl;
// }
// /* load the fragment shader */
// // source = g_resources_lookup_data ("/io/bassi/glarea/glarea-fragment.glsl", 0, NULL);
// create_shader (GL_FRAGMENT_SHADER, fsource, error, &fragment);
// if (fragment == 0)
// std::cout << "FRAF ERROR" << std::endl;
// /* link the vertex and fragment shaders together */
// program = glCreateProgram ();
// if(program == 0) {
// std::cout << "PROG ZERO" << std::endl;
// }
// glAttachShader (program, vertex);
// glAttachShader (program, fragment);
// glLinkProgram (program);
// int status = 0;
// glGetProgramiv (program, GL_LINK_STATUS, &status);
// if (status == GL_FALSE)
// {
// std::cout << "glGetProgramiv ERROR" << std::endl;
// goto out;
// }
// position_location = glGetAttribLocation (program, "position");
// /* get the location of the "position" and "color" attributes */
// color_location = glGetAttribLocation (program, "color");
// /* the individual shaders can be detached and destroyed */
// glDetachShader (program, vertex);
// glDetachShader (program, fragment);
// // program2 = glCreateProgram();
// // create_shader (GL_VERTEX_SHADER, vsource2, error, &vertex2);
// // // g_bytes_unref (source);
// // if (vertex2 == 0) {
// // std::cout << "VERTEX 2ERROR" << std::endl;
// // }
// // create_shader (GL_VERTEX_SHADER, fsource2, error, &fragment2);
// // // g_bytes_unref (source);
// // if (fragment2 == 0) {
// // std::cout << "fragment2 ERROR" << std::endl;
// // }
// // glAttachShader (program2, vertex2);
// // glAttachShader (program2, fragment2);
// // glLinkProgram (program2);
// // status = 0;
// // glGetProgramiv (program2, GL_LINK_STATUS, &status);
// // if (status == GL_FALSE)
// // {
// // std::cout << "glGetProgramiv2 ERROR" << std::endl;
// // goto out;
// // }
// out:
// if (vertex != 0)
// glDeleteShader (vertex);
// if (fragment != 0)
// glDeleteShader (fragment);
// if (program_out != NULL)
// *program_out = program;
// if (position_location_out != NULL)
// *position_location_out = position_location;
// if (color_location_out != NULL)
// *color_location_out = color_location;
// return program != 0;
// }
std::vector<uint8_t> raw_buffer;
uint32_t pixels_w = 400; //w;
uint32_t pixels_h = 200; //h;
raw_buffer.resize(pixels_w*pixels_h * 4);
filament::backend::PixelBufferDescriptor bd(
raw_buffer.data(),
raw_buffer.size(),
backend::PixelDataFormat::RGBA,
backend::PixelDataType::UBYTE,
[](void* buffer, size_t size, void* capture_state) {
uint8_t* foo = (uint8_t*)buffer;
int32_t sum = 0;
for(int i =0; i < size; i++) {
sum += foo[i];
}
Log("SUM : %d", sum);
std::string path = "./out.raw";
std::ofstream stream(path, std::ios::out | std::ios::binary);
if(!stream.good())
{
std::cerr << "Failed to open: " << path << std::endl;
}
else {
std::cerr << "Raw buf size " << size << std::endl;
stream.write((char*)buffer, size);
stream.close();
}
});
render(0);
//after rendering to the target we can now read it back
_renderer->readPixels(_rt,0,0,pixels_w,pixels_h,std::move(bd));
_engine->flushAndWait();
// static void
// gl_init (PolyvoxFilamentPlugin *self)
// {
// std::cout << "GL INIT!" << std::endl;
// /* we need to ensure that the GdkGLContext is set before calling GL API */
// gtk_gl_area_make_current (GTK_GL_AREA (self->gl_drawing_area));
// gtk_gl_area_attach_buffers(GTK_GL_AREA (self->gl_drawing_area));
// gtk_gl_area_set_has_alpha(GTK_GL_AREA (self->gl_drawing_area), TRUE);
// gtk_gl_area_set_has_depth_buffer(GTK_GL_AREA (self->gl_drawing_area), TRUE);
// gtk_gl_area_set_has_stencil_buffer(GTK_GL_AREA (self->gl_drawing_area), TRUE);
// if(!gtk_gl_area_get_has_depth_buffer(GTK_GL_AREA (self->gl_drawing_area))) {
// std::cout << "NO DEPTH" << std::endl;
// }
// if(!gtk_gl_area_get_has_stencil_buffer(GTK_GL_AREA (self->gl_drawing_area))) {
// std::cout << "NO STENCIL" << std::endl;
// }
// /* if the GtkGLArea is in an error state we don't do anything */
// if (gtk_gl_area_get_error (GTK_GL_AREA (self->gl_drawing_area)) != NULL) {
// std::cout << "ERRIOR" << std::endl;
// return;
// }
// auto ctx = gtk_gl_area_get_context ( GTK_GL_AREA(self->gl_drawing_area) );
// // auto foo = gdk_gl_context_get_shared_context(ctx);
// int default_id;
// glGetIntegerv(GL_FRAMEBUFFER_BINDING, &default_id);
// std::cout << "DEFAULT ID " << default_id << std::endl;
// GLuint fbo = 0;
// glGenFramebuffers(1, &fbo);
// glBindFramebuffer(GL_FRAMEBUFFER_EXT, fbo);
// filament_viewer_new(
// (void*)ctx,
// loadResource,
// freeResource
// );
// // if(!viewer) {
// // std::cout << "ERROR" << std::endl;
// // }
// // self->_viewer = viewer;
// // create_swap_chain(self->_viewer);
// // create_render_target(self->_viewer, ((FilamentTextureGL*)texture)->texture_id,400,200);
// }
struct _PolyvoxFilamentPlugin {
GObject parent_instance;
FlTextureRegistrar* texture_registrar;
FlView* fl_view;
FlTexture* texture;
void* _viewer;
GtkWidget* gl_drawing_area ;
/* GL objects */
guint vao;
guint program;
guint position_index;
guint color_index;
};
// GtkWidget *tview;
// GtkTextBuffer *buffer;
// tview = gtk_text_view_new ();
// gtk_widget_show(GTK_WIDGET(tview));
// buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tview));
// gtk_text_buffer_set_text (buffer, "Hello, this is some text", -1);
// GtkCssProvider *provider;
// GtkStyleContext *context;
// provider = gtk_css_provider_new ();
// gtk_css_provider_load_from_data (provider,
// "textview {"
// " font: 15 serif;"
// " color: green;"
// "}",
// -1,
// NULL);
// context = gtk_widget_get_style_context (tview);
// gtk_style_context_add_provider (context,
// GTK_STYLE_PROVIDER (provider),
// GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
// gtk_box_pack_start(box, GTK_WIDGET(tview), true, true,0);
// gtk_widget_grab_focus(GTK_WIDGET(tview));
GtkBox* box = GTK_BOX(gtk_box_new(GTK_ORIENTATION_VERTICAL, 1));
gtk_widget_show(GTK_WIDGET(box));
gtk_widget_show(GTK_WIDGET(view));
gtk_box_pack_start(box, GTK_WIDGET(view), true, true,0);

View File

@@ -0,0 +1,21 @@
// boneMatrices[1] = math::mat4f(1.0, 0.0, 0.0, 0.0, 0.0, 0.26, -0.97, 0.0, 0.0, 0.97, 0.26, 0.0, 0.0, 0.0, 0.0, 1.0);
// Log("Asset instance count : %d asset entity count %d, skin count %d skin name %s joint count @ 0 %d ",
// _asset->getAssetInstanceCount(),
// _asset->getEntityCount(),
// ,
// filamentInstance->getSkinNameAt(0), filamentInstance->getJointCountAt(0));
// for(int i =0; i < numJoints; i++) {
// auto nameInst = _ncm->getInstance(joints[i]);
// const char* jointName = _ncm->getName(nameInst);
// if(strcmp(jointName, boneName) == 0) {
// Log("Set transform for joint %s", jointName);
// // auto boneInst = rm.getInstance(joints[i]);
// }
// }
// bool transformed = false;
// if(!transformed) {
// Log("Failed to find bone %s", boneName);
// }
// Entity root = _asset->getRoot();

View File

@@ -0,0 +1,149 @@
import 'animations.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
class Animation {
final Float32List morphWeights;
final int numMorphWeights;
final int numFrames;
final double frameLengthInMs;
final List<String> boneNames;
final List<String> meshNames;
final Float32List boneTransforms;
Animation(this.morphWeights, this.numMorphWeights, this.boneTransforms,
this.boneNames, this.meshNames, this.numFrames, this.frameLengthInMs);
}
class AnimationBuilder {
BoneAnimation? boneAnimation;
double _frameLengthInMs = 0;
double _duration = 0;
int _numMorphWeights = 0;
double? _interpMorphStart;
double? _interpMorphEnd;
double? _interpMorphStartValue;
double? _interpMorphEndValue;
final List<String> _boneNames = [];
final List<String> _meshNames = [];
final List<BoneTransform> _boneTransforms = [];
Animation build() {
if (_numMorphWeights == 0 || _duration == 0 || _frameLengthInMs == 0)
throw Exception();
int numFrames = _duration * 1000 ~/ _frameLengthInMs;
final _morphWeights = Float32List((numFrames * _numMorphWeights).toInt());
var frameStart = (_interpMorphStart! * 1000) ~/ _frameLengthInMs;
var frameEnd = (_interpMorphEnd! * 1000) ~/ _frameLengthInMs;
for (int i = frameStart; i < frameEnd; i++) {
var linear = (i - frameStart) / frameEnd;
var val = ((1 - linear) * _interpMorphStartValue!) +
(linear * _interpMorphEndValue!);
for (int j = 0; j < _numMorphWeights; j++) {
_morphWeights[(i * _numMorphWeights) + j] = val;
}
}
print(
"Created morphWeights of size ${_morphWeights.length} (${_morphWeights.lengthInBytes} for ${numFrames} frames");
final boneTransforms = Float32List(numFrames * _boneTransforms.length * 7);
print(
"Creating bone transforms of size ${numFrames * _boneTransforms.length * 7}");
for (int i = 0; i < numFrames; i++) {
for (int j = 0; j < _boneTransforms.length; j++) {
var frameData = _boneTransforms[j].getFrameData(i).toList();
var rngStart = ((i * _boneTransforms.length) + j) * 7;
var rngEnd = rngStart + 7;
boneTransforms.setRange(rngStart, rngEnd, frameData);
}
print(
"frameData for frame $i ${boneTransforms.sublist(i * _boneTransforms.length * 7, (i * _boneTransforms.length * 7) + 7)}");
}
return Animation(_morphWeights, _numMorphWeights, boneTransforms,
_boneNames, _meshNames, numFrames, _frameLengthInMs);
}
AnimationBuilder setFramerate(int framerate) {
_frameLengthInMs = 1000 / framerate;
return this;
}
AnimationBuilder setDuration(double secs) {
_duration = secs;
return this;
}
AnimationBuilder setNumMorphWeights(int numMorphWeights) {
_numMorphWeights = numMorphWeights;
return this;
}
AnimationBuilder interpolateMorphWeights(
double start, double end, double startValue, double endValue) {
this._interpMorphStart = start;
this._interpMorphEnd = end;
this._interpMorphStartValue = startValue;
this._interpMorphEndValue = endValue;
return this;
}
AnimationBuilder interpolateBoneTransform(
String boneName,
String meshName,
double start,
double end,
Vec3 transStart,
Vec3 transEnd,
Quaternion quatStart,
Quaternion quatEnd) {
var translations = <Vec3>[];
var quats = <Quaternion>[];
var frameStart = (start * 1000) ~/ _frameLengthInMs;
var frameEnd = (end * 1000) ~/ _frameLengthInMs;
int numFrames = _duration * 1000 ~/ _frameLengthInMs;
if (frameEnd > numFrames) {
throw Exception();
}
for (int i = 0; i < numFrames; i++) {
if (i >= frameStart && i < frameEnd) {
var linear = (i - frameStart) / (frameEnd - frameStart);
translations.add(Vec3(
x: ((1 - linear) * transStart.x) + (linear * transEnd.x),
y: ((1 - linear) * transStart.y) + (linear * transEnd.y),
z: ((1 - linear) * transStart.z) + (linear * transEnd.z),
));
quats.add(Quaternion(
x: ((1 - linear) * quatStart.x) + (linear * quatEnd.x),
y: ((1 - linear) * quatStart.y) + (linear * quatEnd.y),
z: ((1 - linear) * quatStart.z) + (linear * quatEnd.z),
w: ((1 - linear) * quatStart.w) + (linear * quatEnd.w),
));
} else {
translations.add(Vec3());
quats.add(Quaternion());
}
}
_boneTransforms.add(BoneTransform(translations, quats));
_boneNames.add(boneName);
_meshNames.add(meshName);
return this;
}
}

View File

@@ -0,0 +1,69 @@
class Vec3 {
final double x;
final double y;
final double z;
Vec3({this.x = 0, this.y = 0, this.z = 0});
}
class Quaternion {
double x = 0;
double y = 0;
double z = 0;
double w = 1;
Quaternion({this.x = 0, this.y = 0, this.z = 0, this.w = 1.0});
}
class BoneTransform {
final List<Vec3> translations;
final List<Quaternion> quaternions;
///
/// The length of [translations] and [quaternions] must be the same;
/// each entry represents the Vec3/Quaternion for the given frame.
///
BoneTransform(this.translations, this.quaternions) {
if (translations.length != quaternions.length) {
throw Exception("Length of translation/quaternion frames must match");
}
// for (int i = 0; i < quaternions.length; i++) {
// _frameData.add(translations[i].x);
// _frameData.add(translations[i].y);
// _frameData.add(translations[i].z);
// _frameData.add(quaternions[i].x);
// _frameData.add(quaternions[i].y);
// _frameData.add(quaternions[i].z);
// _frameData.add(quaternions[i].w);
// }
}
Iterable<double> getFrameData(int frame) sync* {
yield translations[frame].x;
yield translations[frame].y;
yield translations[frame].z;
yield quaternions[frame].x;
yield quaternions[frame].y;
yield quaternions[frame].z;
yield quaternions[frame].w;
}
}
class BoneAnimation {
final List<BoneTransform> boneTransforms;
final List<String> boneNames;
final List<String> meshNames;
final int numFrames;
BoneAnimation(
this.boneTransforms, this.boneNames, this.meshNames, this.numFrames);
Iterable<double> toFrameData() sync* {
for (int i = 0; i < numFrames; i++) {
for (int j = 0; j < boneTransforms.length; j++) {
yield* boneTransforms[j].getFrameData(i);
}
}
}
}

View File

@@ -1,5 +1,7 @@
import 'dart:async';
import 'dart:ui';
import 'animations/animation_builder.dart';
import 'animations/animations.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
@@ -60,8 +62,9 @@ abstract class FilamentController {
Future rotateStart(double x, double y);
Future rotateUpdate(double x, double y);
Future rotateEnd();
Future applyWeights(FilamentAsset asset, List<double> weights);
Future<List<String>> getTargetNames(FilamentAsset asset, String meshName);
Future setMorphTargetWeights(FilamentAsset asset, List<double> weights);
Future<List<String>> getMorphTargetNames(
FilamentAsset asset, String meshName);
Future<List<String>> getAnimationNames(FilamentAsset asset);
Future removeAsset(FilamentAsset asset);
Future clearAssets();
@@ -77,6 +80,8 @@ abstract class FilamentController {
Future setPosition(FilamentAsset asset, double x, double y, double z);
Future setRotation(
FilamentAsset asset, double rads, double x, double y, double z);
Future setBoneTransform(FilamentAsset asset, String boneName, String meshName,
BoneTransform transform);
Future setScale(FilamentAsset asset, double scale);
Future setCameraFocalLength(double focalLength);
Future setCameraFocusDistance(double focusDistance);
@@ -84,13 +89,11 @@ abstract class FilamentController {
Future setCameraRotation(double rads, double x, double y, double z);
///
/// 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].
/// Accepts a list of doubles representing a sequence of "frames", stacked end-to-end.
/// 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
/// Animates morph target weights/bone transforms (where each frame requires a duration of [frameLengthInMs].
/// [morphWeights] is a list of doubles in frame-major format.
/// Each frame is [numWeights] in length, and each entry is the weight to be applied to the morph target located at that index in the mesh primitive at that frame.
///
Future animate(FilamentAsset asset, List<double> data, int numWeights,
int numFrames, double frameLengthInMs);
Future setAnimation(FilamentAsset asset, Animation animation);
}
class PolyvoxFilamentController extends FilamentController {
@@ -130,6 +133,7 @@ class PolyvoxFilamentController extends FilamentController {
}
void setPixelRatio(double ratio) {
print("Set pixel ratio to $ratio");
_pixelRatio = ratio;
}
@@ -258,15 +262,16 @@ class PolyvoxFilamentController extends FilamentController {
await _channel.invokeMethod("rotateEnd");
}
Future applyWeights(FilamentAsset asset, List<double> weights) async {
await _channel
.invokeMethod("applyWeights", [asset, Float32List.fromList(weights)]);
Future setMorphTargetWeights(
FilamentAsset asset, List<double> weights) async {
await _channel.invokeMethod(
"setMorphTargetWeights", [asset, Float32List.fromList(weights)]);
}
Future<List<String>> getTargetNames(
Future<List<String>> getMorphTargetNames(
FilamentAsset asset, String meshName) async {
var result =
(await _channel.invokeMethod("getTargetNames", [asset, meshName]))
(await _channel.invokeMethod("getMorphTargetNames", [asset, meshName]))
.cast<String>();
return result;
}
@@ -277,14 +282,16 @@ class PolyvoxFilamentController extends FilamentController {
return result;
}
Future animate(FilamentAsset asset, List<double> weights, int numWeights,
int numFrames, double frameLengthInMs) async {
await _channel.invokeMethod("animateWeights", [
Future setAnimation(FilamentAsset asset, Animation animation) async {
await _channel.invokeMethod("setAnimation", [
asset,
Float32List.fromList(weights),
numWeights,
numFrames,
frameLengthInMs
animation.morphWeights,
animation.numMorphWeights,
animation.boneTransforms,
animation.boneNames,
animation.meshNames,
animation.numFrames,
animation.frameLengthInMs
]);
}
@@ -360,6 +367,22 @@ class PolyvoxFilamentController extends FilamentController {
await _channel.invokeMethod("setPosition", [asset, x, y, z]);
}
Future setBoneTransform(FilamentAsset asset, String boneName, String meshName,
BoneTransform transform) async {
await _channel.invokeMethod("setBoneTransform", [
asset,
boneName,
meshName,
transform.translations[0].x,
transform.translations[0].y,
transform.translations[0].z,
transform.quaternions[0].x,
transform.quaternions[0].y,
transform.quaternions[0].z,
transform.quaternions[0].w
]);
}
Future setScale(FilamentAsset asset, double scale) async {
await _channel.invokeMethod("setScale", [asset, scale]);
}

View File

@@ -96,7 +96,7 @@ class _FilamentWidgetState extends State<FilamentWidget> {
}
var texture = Texture(
textureId: widget.controller.textureId,
filterQuality: FilterQuality.none,
filterQuality: FilterQuality.high,
);
return ResizeObserver(
onResized: (Size oldSize, Size newSize) async {

View File

@@ -251,6 +251,42 @@ static FlMethodResponse* _pan_end(PolyvoxFilamentPlugin* self, FlMethodCall* met
return FL_METHOD_RESPONSE(fl_method_success_response_new(result));
}
static FlMethodResponse* _set_position(PolyvoxFilamentPlugin* self, FlMethodCall* method_call) {
FlValue* args = fl_method_call_get_args(method_call);
auto assetPtr = (void*)fl_value_get_int(fl_value_get_list_value(args, 0));
set_position(
assetPtr,
(float)fl_value_get_float(fl_value_get_list_value(args, 1)), // x
(float)fl_value_get_float(fl_value_get_list_value(args, 2)), // y
(float)fl_value_get_float(fl_value_get_list_value(args, 3)) // z
);
g_autoptr(FlValue) result = fl_value_new_string("OK");
return FL_METHOD_RESPONSE(fl_method_success_response_new(result));
}
static FlMethodResponse* _set_bone_transform(PolyvoxFilamentPlugin* self, FlMethodCall* method_call) {
FlValue* args = fl_method_call_get_args(method_call);
auto assetPtr = (void*)fl_value_get_int(fl_value_get_list_value(args, 0));
auto boneName = fl_value_get_string(fl_value_get_list_value(args, 1));
auto meshName = fl_value_get_string(fl_value_get_list_value(args, 2));
set_bone_transform(
assetPtr,
boneName,
meshName,
(float)fl_value_get_float(fl_value_get_list_value(args, 3)), // transX
(float)fl_value_get_float(fl_value_get_list_value(args, 4)), // transY
(float)fl_value_get_float(fl_value_get_list_value(args, 5)), // transZ
(float)fl_value_get_float(fl_value_get_list_value(args, 6)), // quatX
(float)fl_value_get_float(fl_value_get_list_value(args, 7)), // quatY
(float)fl_value_get_float(fl_value_get_list_value(args, 8)), // quatZ
(float)fl_value_get_float(fl_value_get_list_value(args, 9)) // quatW
);
g_autoptr(FlValue) result = fl_value_new_string("OK");
return FL_METHOD_RESPONSE(fl_method_success_response_new(result));
}
static FlMethodResponse* _set_camera_position(PolyvoxFilamentPlugin* self, FlMethodCall* method_call) {
FlValue* args = fl_method_call_get_args(method_call);
auto x = (float)fl_value_get_float(fl_value_get_list_value(args, 0));
@@ -335,33 +371,48 @@ static FlMethodResponse* _apply_weights(PolyvoxFilamentPlugin* self, FlMethodCal
return FL_METHOD_RESPONSE(fl_method_success_response_new(result));
}
static FlMethodResponse* _animate_weights(PolyvoxFilamentPlugin* self, FlMethodCall* method_call) {
static FlMethodResponse* _set_animation(PolyvoxFilamentPlugin* self, FlMethodCall* method_call) {
FlValue* args = fl_method_call_get_args(method_call);
auto assetPtr = (void*)fl_value_get_int(fl_value_get_list_value(args, 0));
auto weightsValue = fl_value_get_list_value(args, 1);
float* const morphData = (float* const) fl_value_get_float32_list(fl_value_get_list_value(args, 1));
int64_t numMorphWeights = fl_value_get_int(fl_value_get_list_value(args, 2));
float* const weights = (float* const) fl_value_get_float32_list(weightsValue);
int64_t numWeights = fl_value_get_int(fl_value_get_list_value(args, 2));
int64_t numFrames = fl_value_get_int(fl_value_get_list_value(args, 3));
float frameLengthInMs = fl_value_get_float(fl_value_get_list_value(args, 4));
float* const boneData = (float* const) fl_value_get_float32_list(fl_value_get_list_value(args, 3));
FlValue* boneNamesValue = fl_value_get_list_value(args, 4);
int64_t numBones = fl_value_get_length(boneNamesValue);
const char* boneNames[numBones];
for(int i=0; i< numBones;i++) {
boneNames[i] = fl_value_get_string(fl_value_get_list_value(boneNamesValue, i));
}
FlValue* meshNamesValue = fl_value_get_list_value(args, 5);
const char* meshNames[numBones];
for(int i=0; i< numBones;i++) {
meshNames[i] = fl_value_get_string(fl_value_get_list_value(meshNamesValue, i));
}
int64_t numFrames = fl_value_get_int(fl_value_get_list_value(args, 6));
float frameLengthInMs = fl_value_get_float(fl_value_get_list_value(args, 7));
animate_weights(assetPtr, weights, numWeights, numFrames, 1000.0f / frameLengthInMs);
set_animation(assetPtr, morphData, numMorphWeights, boneData, boneNames, meshNames, numBones, numFrames, 1000.0f / frameLengthInMs);
g_autoptr(FlValue) result = fl_value_new_string("OK");
return FL_METHOD_RESPONSE(fl_method_success_response_new(result));
}
static FlMethodResponse* _get_target_names(PolyvoxFilamentPlugin* self, FlMethodCall* method_call) {
static FlMethodResponse* _get_morph_target_names(PolyvoxFilamentPlugin* self, FlMethodCall* method_call) {
FlValue* args = fl_method_call_get_args(method_call);
auto assetPtr = (void*)fl_value_get_int(fl_value_get_list_value(args, 0));
auto meshName = fl_value_get_string(fl_value_get_list_value(args, 1));
g_autoptr(FlValue) result = fl_value_new_list();
auto numNames = get_target_name_count(assetPtr, meshName);
auto numNames = get_morph_target_name_count(assetPtr, meshName);
for(int i = 0; i < numNames; i++) {
gchar out[255];
get_target_name(assetPtr, meshName, out, i);
get_morph_target_name(assetPtr, meshName, out, i);
fl_value_append_take (result, fl_value_new_string (out));
}
@@ -468,12 +519,16 @@ static void polyvox_filament_plugin_handle_method_call(
response = _play_animation(self, method_call);
} else if(strcmp(method, "stopAnimation") == 0) {
response = _stop_animation(self, method_call);
} else if(strcmp(method, "applyWeights") == 0) {
} else if(strcmp(method, "setMorphTargetWeights") == 0) {
response = _apply_weights(self, method_call);
} else if(strcmp(method, "animateWeights") == 0) {
response = _animate_weights(self, method_call);
} else if(strcmp(method, "getTargetNames") == 0) {
response = _get_target_names(self, method_call);
} else if(strcmp(method, "setAnimation") == 0) {
response = _set_animation(self, method_call);
} else if(strcmp(method, "getMorphTargetNames") == 0) {
response = _get_morph_target_names(self, method_call);
} else if(strcmp(method, "setPosition") == 0) {
response = _set_position(self, method_call);
} else if(strcmp(method, "setBoneTransform") == 0) {
response = _set_bone_transform(self, method_call);
} else {
response = FL_METHOD_RESPONSE(fl_method_not_implemented_response_new());
}