This commit is contained in:
Nick Fisher
2023-06-22 13:47:24 +08:00
25 changed files with 941 additions and 681 deletions

View File

@@ -1,13 +1,13 @@
import 'package:polyvox_filament/animations/animations.dart';
import 'package:polyvox_filament/animations/bone_animation_data.dart';
import 'package:polyvox_filament/animations/morph_animation_data.dart';
import 'package:polyvox_filament/filament_controller.dart';
import 'package:tuple/tuple.dart';
import 'package:flutter/foundation.dart';
import 'package:vector_math/vector_math.dart';
class AnimationBuilder {
final FilamentController controller;
DartBoneAnimation? dartBoneAnimation;
// BoneAnimationData? BoneAnimationData;
double _frameLengthInMs = 0;
double _duration = 0;
@@ -16,7 +16,7 @@ class AnimationBuilder {
double? _interpMorphStartValue;
double? _interpMorphEndValue;
List<DartBoneAnimation>? _dartBoneAnimations = null;
// List<BoneAnimationData>? _BoneAnimationDatas = null;
FilamentEntity asset;
String meshName;
@@ -53,11 +53,11 @@ class AnimationBuilder {
}
var morphAnimation =
MorphAnimation(meshName, morphData, morphNames, _frameLengthInMs);
print("SETTING!");
controller.setMorphAnimation(asset, morphAnimation);
// return Tuple2<MorphAnimation, List<DartBoneAnimation>>(
// morphAnimation, _dartBoneAnimations!);
MorphAnimationData(meshName, morphData, morphNames, _frameLengthInMs);
controller.setMorphAnimationData(asset, morphAnimation);
// return Tuple2<MorphAnimationData, List<BoneAnimationData>>(
// morphAnimation, _BoneAnimationDatas!);
}
AnimationBuilder setDuration(double secs) {
@@ -118,15 +118,40 @@ class AnimationBuilder {
// var boneFrameData = BoneTransformFrameData(translations, quats);
// _DartBoneAnimations ??= <DartBoneAnimation>[];
// _BoneAnimationDatas ??= <BoneAnimationData>[];
// var frameData = List<List<double>>.generate(
// numFrames, (index) => boneFrameData.getFrameData(index).toList());
// var animData = Float32List.fromList(frameData.expand((x) => x).toList());
// _DartBoneAnimations!.add(DartDartBoneAnimation([boneName], [meshName], animData));
// _BoneAnimationDatas!.add(DartBoneAnimationData([boneName], [meshName], animData));
return this;
}
}
class BoneTransformFrameData {
final List<Vector3> 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.
///
BoneTransformFrameData(this.translations, this.quaternions) {
if (translations.length != quaternions.length) {
throw Exception("Length of translation/quaternion frames must match");
}
}
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;
}
}

View File

@@ -1,61 +0,0 @@
import 'dart:typed_data';
import 'package:vector_math/vector_math.dart';
class DartBoneAnimation {
final String boneName;
final String meshName;
final Float32List frameData;
double frameLengthInMs;
DartBoneAnimation(
this.boneName, this.meshName, this.frameData, this.frameLengthInMs);
}
//
// Frame weights for the morph targets specified in [morphNames] attached to mesh [meshName].
// morphData is laid out as numFrames x numMorphTargets
// where the weights are in the same order as [morphNames].
// [morphNames] must be provided but is not used directly; this is only used to check that the eventual asset being animated contains the same morph targets in the same order.
//
class MorphAnimation {
final String meshName;
final List<String> morphNames;
final Float32List data;
MorphAnimation(
this.meshName, this.data, this.morphNames, this.frameLengthInMs) {
assert(data.length == morphNames.length * numFrames);
}
int get numMorphWeights => morphNames.length;
int get numFrames => data.length ~/ numMorphWeights;
final double frameLengthInMs;
}
class BoneTransformFrameData {
final List<Vector3> 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.
///
BoneTransformFrameData(this.translations, this.quaternions) {
if (translations.length != quaternions.length) {
throw Exception("Length of translation/quaternion frames must match");
}
}
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;
}
}

View File

@@ -0,0 +1,17 @@
import 'dart:typed_data';
import 'package:vector_math/vector_math.dart';
///
/// Model class for bone animation frame data.
/// To create dynamic/runtime bone animations (as distinct from animations embedded in a glTF asset), create an instance containing the relevant
/// data and pass to the [setBoneAnimation] method on a [FilamentController].
/// [frameData] is laid out as [locX, locY, locZ, rotW, rotX, rotY, rotZ]
///
class BoneAnimationData {
final String boneName;
final List<String> meshNames;
final Float32List frameData;
double frameLengthInMs;
BoneAnimationData(
this.boneName, this.meshNames, this.frameData, this.frameLengthInMs);
}

View File

@@ -0,0 +1,86 @@
import 'dart:convert';
import 'package:vector_math/vector_math.dart';
import 'package:flutter/foundation.dart';
import 'package:vector_math/vector_math.dart';
///
/// Some animation data may be specified as blendshape weights (say, between -1 and 1)
/// but at runtime we want to retarget this to drive a bone translation/rotation (say, between -pi/2 and pi/2).
/// A [BoneDriver] is our mechanism for translating the former to the latter, containing:
/// 1) a blendshape name
/// 2) a bone name
/// 3) min/max translation values (corresponding to -1/1 on the blendshape), and
/// 4) min/max rotation values (corresponding to -1/1 on the blendshape)
///
class Transformation {
final Quaternion rotation;
late final Vector3 translation;
Transformation(this.rotation, {Vector3? translation}) {
this.translation = translation ?? Vector3.zero();
}
}
class BoneDriver {
final String bone;
final Map<String, Transformation>
transformations; // maps a blendshape key to a Transformation
BoneDriver(this.bone, this.transformations);
//
// Accepts a Float32List containing [numFrames] frames of data for a single morph target weight (for efficiency, this must be unravelled to a single contiguous Float32List).
// Returns a generator that yields [numFrames] Quaternions, each representing the (weighted) rotation/translation specified by the mapping of this BoneDriver.
//
Iterable<Quaternion> transform(
Map<String, List<double>> morphTargetFrameData) sync* {
assert(setEquals(
morphTargetFrameData.keys.toSet(), transformations.keys.toSet()));
var numFrames = morphTargetFrameData.values.first.length;
assert(morphTargetFrameData.values.every((x) => x.length == numFrames));
for (int frameNum = 0; frameNum < numFrames; frameNum++) {
var rotations = transformations.keys.map((blendshape) {
var weight = morphTargetFrameData[blendshape]![frameNum];
var rotation = transformations[blendshape]!.rotation.clone();
rotation.x *= weight;
rotation.y *= weight;
rotation.z *= weight;
rotation.w = 1;
return rotation;
}).toList();
if (frameNum == 0) {
print(rotations);
}
var result = rotations.fold(
rotations.first, (Quaternion a, Quaternion b) => a + b);
result.w = 1;
print("RESULT $result");
yield result;
// .normalized();
// todo - bone translations
}
}
factory BoneDriver.fromJsonObject(dynamic jsonObject) {
throw Exception("TODO");
// return BoneDriver(
// jsonObject["bone"],
// Map<String,Transformation>.fromIterable(jsonObject["blendshape"].map((bsName, quats) {
// var q = quats.map(())
// MapEntry(k,
}
}
// }
// yield Quaternion(
// rotMin.x + (weight * (rotMax.x - rotMin.x)),
// rotMin.y + (weight * (rotMax.y - rotMin.y)),
// rotMin.z + (weight * (rotMax.z - rotMin.z)),
// 1.0);

View File

@@ -0,0 +1,129 @@
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'package:tuple/tuple.dart';
import 'package:polyvox_filament/animations/bone_animation_data.dart';
import 'package:polyvox_filament/animations/bone_driver.dart';
import 'package:polyvox_filament/animations/morph_animation_data.dart';
import 'package:vector_math/vector_math.dart';
///
/// A class for loading animation data from a single CSV and allocating between morph/bone animation with help.
///
class DynamicAnimation {
final MorphAnimationData? morphAnimation;
final List<BoneAnimationData> boneAnimation;
factory DynamicAnimation.load(String? meshName, String csvPath,
{List<BoneDriver>? boneDrivers,
List<String>? boneMeshes,
String? boneDriverConfigPath,
double? framerate}) {
// create a MorphAnimationData instance from the given CSV
var llf = _loadLiveLinkFaceCSV(csvPath);
var frameLengthInMs = 1000 / (framerate ?? 60.0);
var morphNames = llf
.item1; //.where((name) => !boneDrivers.any((element) => element.blendshape == name));
var morphAnimationData = MorphAnimationData(
meshName ?? "NULL", llf.item2, morphNames, frameLengthInMs);
final boneAnimations = <BoneAnimationData>[];
// if applicable, load the bone driver config
if (boneDriverConfigPath != null) {
if (boneDrivers != null) {
throw Exception(
"Specify either boneDrivers, or the config path, not both");
}
boneDrivers = [
json
.decode(File(boneDriverConfigPath).readAsStringSync())
.map(BoneDriver.fromJsonObject)
.toList()
];
}
// iterate over every bone driver
if (boneDrivers != null) {
for (var driver in boneDrivers) {
// collect the frame data for the blendshapes that this driver uses
var morphData = driver.transformations
.map((String blendshape, Transformation transformation) {
return MapEntry(
blendshape, morphAnimationData.getData(blendshape).toList());
});
// apply the driver to the frame data
var transformedQ = driver.transform(morphData).toList();
// transform the quaternion to a Float32List
var transformedF = _quaternionToFloatList(transformedQ);
// add to the list of boneAnimations
boneAnimations.add(BoneAnimationData(
driver.bone, boneMeshes!, transformedF, frameLengthInMs));
}
}
return DynamicAnimation(morphAnimationData, boneAnimations);
}
static Float32List _quaternionToFloatList(List<Quaternion> quats) {
var data = Float32List(quats.length * 7);
int i = 0;
for (var quat in quats) {
data.setRange(i, i + 7, [0, 0, 0, quat.w, quat.x, quat.y, quat.z]);
i += 7;
}
return data;
}
DynamicAnimation(this.morphAnimation, this.boneAnimation);
///
/// Load visemes fom a CSV file formatted according to the following header:
/// "Timecode,BlendShapeCount,EyeBlinkLeft,EyeLookDownLeft,EyeLookInLeft,EyeLookOutLeft,EyeLookUpLeft,EyeSquintLeft,EyeWideLeft,EyeBlinkRight,EyeLookDownRight,EyeLookInRight,EyeLookOutRight,EyeLookUpRight,EyeSquintRight,EyeWideRight,JawForward,JawRight,JawLeft,JawOpen,MouthClose,MouthFunnel,MouthPucker,MouthRight,MouthLeft,MouthSmileLeft,MouthSmileRight,MouthFrownLeft,MouthFrownRight,MouthDimpleLeft,MouthDimpleRight,MouthStretchLeft,MouthStretchRight,MouthRollLower,MouthRollUpper,MouthShrugLower,MouthShrugUpper,MouthPressLeft,MouthPressRight,MouthLowerDownLeft,MouthLowerDownRight,MouthUpperUpLeft,MouthUpperUpRight,BrowDownLeft,BrowDownRight,BrowInnerUp,BrowOuterUpLeft,BrowOuterUpRight,CheekPuff,CheekSquintLeft,CheekSquintRight,NoseSneerLeft,NoseSneerRight,TongueOut,HeadYaw,HeadPitch,HeadRoll,LeftEyeYaw,LeftEyePitch,LeftEyeRoll,RightEyeYaw,RightEyePitch,RightEyeRoll"
/// Returns only those specified by [targetNames].
///
static Tuple2<List<String>, Float32List> _loadLiveLinkFaceCSV(String path) {
final data = File(path)
.readAsLinesSync()
.where((l) => l.length > 1)
.map((l) => l.split(","));
final header = data.first;
final numBlendShapes = header.length - 2;
final _data = <double>[];
for (var frame in data.skip(1)) {
int numFrameWeights = frame.length - 2;
// CSVs may contain rows where the "BlendShapeCount" column is set to "0" and/or the weight columns are simply missing.
// This can happen when something went wrong while recording via an app (e.g. LiveLinkFace)
// Whenever we encounter this type of row, we consider that all weights should be set to zero for that frame.
if (numFrameWeights != int.parse(frame[1])) {
_data.addAll(List<double>.filled(numBlendShapes, 0.0));
continue;
}
//
// Now, we check that the actual number of weight columns matches the header
// we ignore the "BlendShapeCount" column (and just count the number of columns)
// This is due to some legacy issues where we generated CSVs that had 61 weight columns, but accidentally left the "BlendShapeCount" column at 55
// This is probably fine as we always have either zero weights (handled above), or all weights (handled below).
// In other words, if this throws, we have a serious problem.
if (numFrameWeights != numBlendShapes) {
throw Exception(
"Malformed CSV, header specifies ${numBlendShapes} columns but frame specified only $numFrameWeights weights");
}
_data.addAll(frame
.skip(2)
.map((weight) => double.parse(weight))
.cast<double>()
.toList());
}
return Tuple2(header.skip(2).toList(), Float32List.fromList(_data));
}
}

View File

@@ -0,0 +1,13 @@
import 'dart:math';
import 'package:polyvox_filament/animations/bone_driver.dart';
import 'package:vector_math/vector_math.dart';
BoneDriver getLiveLinkFaceBoneDrivers(String bone) {
return BoneDriver(bone, {
"HeadPitch":
Transformation(Quaternion.axisAngle(Vector3(0, 0, -1), pi / 3)),
"HeadRoll": Transformation(Quaternion.axisAngle(Vector3(1, 0, 0), pi / 2)),
"HeadYaw": Transformation(Quaternion.axisAngle(Vector3(0, 1, 0), pi / 2)),
});
}

View File

@@ -0,0 +1,32 @@
//
// Frame weights for the morph targets specified in [morphNames] attached to mesh [meshName].
// morphData is laid out as numFrames x numMorphTargets
// where the weights are in the same order as [morphNames].
// [morphNames] must be provided but is not used directly; this is only used to check that the eventual asset being animated contains the same morph targets in the same order.
//
import 'dart:typed_data';
class MorphAnimationData {
final String meshName;
final List<String> morphNames;
final Float32List data;
MorphAnimationData(
this.meshName, this.data, this.morphNames, this.frameLengthInMs) {
assert(data.length == morphNames.length * numFrames);
}
int get numMorphWeights => morphNames.length;
int get numFrames => data.length ~/ numMorphWeights;
final double frameLengthInMs;
Iterable<double> getData(String morphName) sync* {
int index = morphNames.indexOf(morphName);
for (int i = 0; i < numFrames; i++) {
yield data[(i * numMorphWeights) + index];
}
}
}

View File

@@ -9,10 +9,10 @@ import 'package:flutter/animation.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter/services.dart';
import 'package:polyvox_filament/animations/bone_animation_data.dart';
import 'package:polyvox_filament/animations/morph_animation_data.dart';
import 'package:polyvox_filament/generated_bindings.dart';
import 'animations/animations.dart';
typedef AssetManager = Pointer<Void>;
typedef FilamentViewer = Pointer<Void>;
typedef FilamentEntity = int;
@@ -306,8 +306,8 @@ class FilamentController {
/// [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.
///
void setMorphAnimation(FilamentEntity asset, MorphAnimation animation) async {
print("Setting morph animation");
void setMorphAnimationData(
FilamentEntity asset, MorphAnimationData animation) async {
var data = calloc<Float>(animation.data.length);
for (int i = 0; i < animation.data.length; i++) {
data.elementAt(i).value = animation.data[i];
@@ -327,40 +327,38 @@ class FilamentController {
/// 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.
/// for now we only allow animating a single bone (though multiple skinned targets are supported)
///
void setBoneAnimation(
FilamentEntity asset, List<DartBoneAnimation> animations) async {
var data =
calloc<Float>(animations.length * animations.first.frameData.length);
FilamentEntity asset, BoneAnimationData animation) async {
var data = calloc<Float>(animation.frameData.length);
int offset = 0;
var numFrames = animations.first.frameData.length;
var meshNames = calloc<Pointer<Char>>(animations.length);
var boneNames = calloc<Pointer<Char>>(animations.length);
int animIdx = 0;
for (var animation in animations) {
if (animation.frameData.length != numFrames) {
throw Exception(
"All bone animations must share the same animation frame data length.");
}
for (int i = 0; i < animation.frameData.length; i++) {
data.elementAt(offset).value = animation.frameData[i];
offset += 1;
}
meshNames.elementAt(animIdx).value =
animation.meshName.toNativeUtf8().cast<Char>();
boneNames.elementAt(animIdx).value =
animation.boneName.toNativeUtf8().cast<Char>();
var numFrames = animation.frameData.length ~/ 7;
var boneNames = calloc<Pointer<Char>>(1);
boneNames.elementAt(0).value =
animation.boneName.toNativeUtf8().cast<Char>();
var meshNames = calloc<Pointer<Char>>(animation.meshNames.length);
for (int i = 0; i < animation.meshNames.length; i++) {
meshNames.elementAt(i).value =
animation.meshNames[i].toNativeUtf8().cast<Char>();
}
for (int i = 0; i < animation.frameData.length; i++) {
data.elementAt(offset).value = animation.frameData[i];
offset += 1;
}
_nativeLibrary.set_bone_animation(
_assetManager,
asset,
animations.length,
boneNames,
meshNames,
data,
numFrames,
animations.first.frameLengthInMs);
1,
boneNames,
meshNames,
animation.meshNames.length,
animation.frameLengthInMs);
calloc.free(data);
}
@@ -386,7 +384,6 @@ class FilamentController {
void playAnimation(FilamentEntity asset, int index,
{bool loop = false, bool reverse = false}) async {
print("LOOP $loop");
_nativeLibrary.play_animation(
_assetManager, asset, index, loop ? 1 : 0, reverse ? 1 : 0);
}

View File

@@ -36,7 +36,6 @@ class _FilamentGestureDetectorState extends State<FilamentGestureDetector> {
};
bool _rotating = false;
bool _scaling = false;
// to avoid duplicating code for pan/rotate (panStart, panUpdate, panEnd, rotateStart, rotateUpdate etc)
// we have only a single function for start/update/end.
@@ -45,8 +44,6 @@ class _FilamentGestureDetectorState extends State<FilamentGestureDetector> {
late Function(double x, double y) _functionUpdate;
late Function() _functionEnd;
double _lastScale = 0;
@override
void initState() {
_setFunction();
@@ -85,101 +82,135 @@ class _FilamentGestureDetectorState extends State<FilamentGestureDetector> {
Timer? _scrollTimer;
Widget _desktop() {
return Listener(
onPointerSignal: !widget.enableControls
? null
: (pointerSignal) async {
// scroll-wheel zoom on desktop
if (pointerSignal is PointerScrollEvent) {
_scrollTimer?.cancel();
widget.controller.zoomBegin();
widget.controller
.zoomUpdate(pointerSignal.scrollDelta.dy > 0 ? 10 : -10);
_scrollTimer = Timer(Duration(milliseconds: 100), () {
widget.controller.zoomEnd();
_scrollTimer = null;
});
}
},
onPointerPanZoomStart: !widget.enableControls
? null
: (pzs) {
print("PAN ZOOM START");
},
onPointerDown: !widget.enableControls
? null
: (d) async {
print("a");
// if (d.buttons == kTertiaryButton || _rotating) {
// widget.controller
// .rotateStart(d.localPosition.dx, d.localPosition.dy);
// } else {
// widget.controller.panStart(d.focalPoint.dx, d.focalPoint.dy);
// }
},
onPointerMove: !widget.enableControls
? null
: (PointerMoveEvent d) async {
// if (d.buttons == kTertiaryButton || _rotating) {
// widget.controller
// .rotateUpdate(d.localPosition.dx, d.localPosition.dy);
// } else {
// widget.controller.panUpdate(d.focalPoint.dx, d.focalPoint.dy);
// }
},
onPointerUp: !widget.enableControls
? null
: (d) async {
// if (d.buttons == kTertiaryButton || _rotating) {
// widget.controller.rotateEnd();
// } else {
// widget.controller.panEnd(d.focalPoint.dx, d.focalPoint.dy);
// }
},
child: widget.child);
}
bool _scaling = false;
double _lastScale = 0;
DateTime _lastUpdate = DateTime.now();
Widget _mobile() {
return GestureDetector(
behavior: HitTestBehavior.opaque,
onDoubleTap: () {
_rotating = !_rotating;
print("Set rotating to $_rotating");
},
onScaleStart: !widget.enableControls
? null
: (d) async {
if (d.pointerCount == 2) {
_lastScale = 0;
_scaling = true;
widget.controller.zoomBegin();
} else if (!_scaling) {
if (_rotating) {
widget.controller
.rotateStart(d.focalPoint.dx, d.focalPoint.dy);
} else {
widget.controller
.panStart(d.focalPoint.dx, d.focalPoint.dy);
}
}
},
onScaleEnd: !widget.enableControls
? null
: (d) async {
if (d.pointerCount == 2) {
widget.controller.zoomEnd();
_lastScale = 0;
_scaling = false;
} else if (!_scaling) {
if (_rotating) {
widget.controller.rotateEnd();
} else {
widget.controller.panEnd();
}
}
},
onScaleUpdate: !widget.enableControls
? null
: (ScaleUpdateDetails d) async {
if (d.pointerCount == 2) {
// var scale = d.horizontalScale - _lastScale;
// print(scale);
widget.controller
.zoomUpdate(d.horizontalScale > 1 ? 0.1 : -0.1);
_lastScale = d.horizontalScale;
} else if (!_scaling) {
if (_rotating) {
widget.controller
.rotateUpdate(d.focalPoint.dx, d.focalPoint.dy);
} else {
widget.controller
.panUpdate(d.focalPoint.dx, d.focalPoint.dy);
}
}
},
child: widget.child);
}
@override
Widget build(BuildContext context) {
print(widget.enableControls);
return Stack(children: [
Positioned.fill(
// pinch zoom on mobile
// couldn't find any equivalent for pointerCount in Listener so we use two widgets:
// - outer is a GestureDetector only for pinch zoom
// - inner is a Listener for all other gestures (including scroll zoom on desktop)
child: GestureDetector(
onDoubleTap: () {
_rotating = !_rotating;
print("Set rotating to $_rotating");
},
onScaleStart: !widget.enableControls
? null
: (d) async {
_scaling = true;
if (d.pointerCount == 2) {
widget.controller.zoomEnd();
widget.controller.zoomBegin();
}
},
onScaleEnd: !widget.enableControls
? null
: (d) async {
_scaling = false;
if (d.pointerCount == 2) {
_lastScale = 0;
widget.controller.zoomEnd();
}
},
onScaleUpdate: !widget.enableControls
? null
: (d) async {
if (d.pointerCount == 2) {
if (_lastScale != 0) {
widget.controller.zoomUpdate(Platform.isIOS
? 1000 * (_lastScale - d.scale)
: 100 * (_lastScale - d.scale));
}
}
_lastScale = d.scale;
},
child: Listener(
onPointerSignal: !widget.enableControls
? null
: (pointerSignal) async {
// scroll-wheel zoom on desktop
if (pointerSignal is PointerScrollEvent) {
_scrollTimer?.cancel();
widget.controller.zoomBegin();
widget.controller.zoomUpdate(
pointerSignal.scrollDelta.dy > 0 ? 10 : -10);
_scrollTimer =
Timer(Duration(milliseconds: 100), () {
widget.controller.zoomEnd();
_scrollTimer = null;
});
}
},
onPointerPanZoomStart:
!widget.enableControls ? null : (pzs) {},
onPointerDown: !widget.enableControls
? null
: (d) async {
if (d.buttons == kTertiaryButton || _rotating) {
widget.controller.rotateStart(
d.localPosition.dx, d.localPosition.dy);
} else {
_functionStart(
d.localPosition.dx, d.localPosition.dy);
}
},
onPointerMove: !widget.enableControls
? null
: (d) async {
if (d.buttons == kTertiaryButton || _rotating) {
widget.controller.rotateUpdate(
d.localPosition.dx, d.localPosition.dy);
} else {
_functionUpdate(
d.localPosition.dx, d.localPosition.dy);
}
},
onPointerUp: !widget.enableControls
? null
: (d) async {
if (d.buttons == kTertiaryButton || _rotating) {
widget.controller.rotateEnd();
} else {
_functionEnd();
}
},
child: widget.child))),
child:
Platform.isLinux || Platform.isWindows ? _desktop() : _mobile()),
widget.showControlOverlay
? Align(
alignment: Alignment.bottomRight,

View File

@@ -118,7 +118,8 @@ class _FilamentWidgetState extends State<FilamentWidget> {
? Container()
: Transform(
alignment: Alignment.center,
transform: Matrix4.rotationX(pi),
transform: Matrix4.rotationX(
pi), // TODO - this rotation is due to OpenGL texture coordinate working in a different space from Flutter, can we move this to the C++ side somewhere?
child: texture)
: texture))));
});

View File

@@ -684,21 +684,23 @@ class NativeLibrary {
void set_bone_animation(
ffi.Pointer<ffi.Void> assetManager,
int asset,
int length,
ffi.Pointer<ffi.Pointer<ffi.Char>> boneNames,
ffi.Pointer<ffi.Pointer<ffi.Char>> meshNames,
ffi.Pointer<ffi.Float> frameData,
int numFrames,
int numBones,
ffi.Pointer<ffi.Pointer<ffi.Char>> boneNames,
ffi.Pointer<ffi.Pointer<ffi.Char>> meshName,
int numMeshTargets,
double frameLengthInMs,
) {
return _set_bone_animation(
assetManager,
asset,
length,
boneNames,
meshNames,
frameData,
numFrames,
numBones,
boneNames,
meshName,
numMeshTargets,
frameLengthInMs,
);
}
@@ -708,20 +710,22 @@ class NativeLibrary {
ffi.Void Function(
ffi.Pointer<ffi.Void>,
EntityId,
ffi.Pointer<ffi.Float>,
ffi.Int,
ffi.Int,
ffi.Pointer<ffi.Pointer<ffi.Char>>,
ffi.Pointer<ffi.Pointer<ffi.Char>>,
ffi.Pointer<ffi.Float>,
ffi.Int,
ffi.Float)>>('set_bone_animation');
late final _set_bone_animation = _set_bone_animationPtr.asFunction<
void Function(
ffi.Pointer<ffi.Void>,
int,
ffi.Pointer<ffi.Float>,
int,
int,
ffi.Pointer<ffi.Pointer<ffi.Char>>,
ffi.Pointer<ffi.Pointer<ffi.Char>>,
ffi.Pointer<ffi.Float>,
int,
double)>();
@@ -1151,96 +1155,9 @@ class NativeLibrary {
late final _ios_dummy = _ios_dummyPtr.asFunction<void Function()>();
}
class __mbstate_t extends ffi.Union {
@ffi.Array.multi([128])
external ffi.Array<ffi.Char> __mbstate8;
@ffi.LongLong()
external int _mbstateL;
}
class __darwin_pthread_handler_rec extends ffi.Struct {
external ffi
.Pointer<ffi.NativeFunction<ffi.Void Function(ffi.Pointer<ffi.Void>)>>
__routine;
external ffi.Pointer<ffi.Void> __arg;
external ffi.Pointer<__darwin_pthread_handler_rec> __next;
}
class _opaque_pthread_attr_t extends ffi.Struct {
@ffi.Long()
external int __sig;
@ffi.Array.multi([56])
external ffi.Array<ffi.Char> __opaque;
}
class _opaque_pthread_cond_t extends ffi.Struct {
@ffi.Long()
external int __sig;
@ffi.Array.multi([40])
external ffi.Array<ffi.Char> __opaque;
}
class _opaque_pthread_condattr_t extends ffi.Struct {
@ffi.Long()
external int __sig;
@ffi.Array.multi([8])
external ffi.Array<ffi.Char> __opaque;
}
class _opaque_pthread_mutex_t extends ffi.Struct {
@ffi.Long()
external int __sig;
@ffi.Array.multi([56])
external ffi.Array<ffi.Char> __opaque;
}
class _opaque_pthread_mutexattr_t extends ffi.Struct {
@ffi.Long()
external int __sig;
@ffi.Array.multi([8])
external ffi.Array<ffi.Char> __opaque;
}
class _opaque_pthread_once_t extends ffi.Struct {
@ffi.Long()
external int __sig;
@ffi.Array.multi([8])
external ffi.Array<ffi.Char> __opaque;
}
class _opaque_pthread_rwlock_t extends ffi.Struct {
@ffi.Long()
external int __sig;
@ffi.Array.multi([192])
external ffi.Array<ffi.Char> __opaque;
}
class _opaque_pthread_rwlockattr_t extends ffi.Struct {
@ffi.Long()
external int __sig;
@ffi.Array.multi([16])
external ffi.Array<ffi.Char> __opaque;
}
class _opaque_pthread_t extends ffi.Struct {
@ffi.Long()
external int __sig;
external ffi.Pointer<__darwin_pthread_handler_rec> __cleanup_stack;
@ffi.Array.multi([8176])
external ffi.Array<ffi.Char> __opaque;
class __fsid_t extends ffi.Struct {
@ffi.Array.multi([2])
external ffi.Array<ffi.Int> __val;
}
class ResourceBuffer extends ffi.Struct {
@@ -1277,75 +1194,127 @@ typedef FreeResourceFromOwner = ffi.Pointer<
ffi.Void Function(ResourceBuffer, ffi.Pointer<ffi.Void>)>>;
typedef EntityId = ffi.Int32;
const int _STDINT_H = 1;
const int _FEATURES_H = 1;
const int _DEFAULT_SOURCE = 1;
const int __GLIBC_USE_ISOC2X = 1;
const int __USE_ISOC11 = 1;
const int __USE_ISOC99 = 1;
const int __USE_ISOC95 = 1;
const int _POSIX_SOURCE = 1;
const int _POSIX_C_SOURCE = 200809;
const int __USE_POSIX = 1;
const int __USE_POSIX2 = 1;
const int __USE_POSIX199309 = 1;
const int __USE_POSIX199506 = 1;
const int __USE_XOPEN2K = 1;
const int __USE_XOPEN2K8 = 1;
const int _ATFILE_SOURCE = 1;
const int __WORDSIZE = 64;
const int __DARWIN_ONLY_64_BIT_INO_T = 1;
const int __WORDSIZE_TIME64_COMPAT32 = 1;
const int __DARWIN_ONLY_UNIX_CONFORMANCE = 1;
const int __SYSCALL_WORDSIZE = 64;
const int __DARWIN_ONLY_VERS_1050 = 1;
const int __TIMESIZE = 64;
const int __DARWIN_UNIX03 = 1;
const int __USE_MISC = 1;
const int __DARWIN_64_BIT_INO_T = 1;
const int __USE_ATFILE = 1;
const int __DARWIN_VERS_1050 = 1;
const int __USE_FORTIFY_LEVEL = 0;
const int __DARWIN_NON_CANCELABLE = 0;
const int __GLIBC_USE_DEPRECATED_GETS = 0;
const String __DARWIN_SUF_EXTSN = '\$DARWIN_EXTSN';
const int __GLIBC_USE_DEPRECATED_SCANF = 0;
const int __DARWIN_C_ANSI = 4096;
const int _STDC_PREDEF_H = 1;
const int __DARWIN_C_FULL = 900000;
const int __STDC_IEC_559__ = 1;
const int __DARWIN_C_LEVEL = 900000;
const int __STDC_IEC_60559_BFP__ = 201404;
const int __STDC_WANT_LIB_EXT1__ = 1;
const int __STDC_IEC_559_COMPLEX__ = 1;
const int __DARWIN_NO_LONG_LONG = 0;
const int __STDC_IEC_60559_COMPLEX__ = 201404;
const int _DARWIN_FEATURE_64_BIT_INODE = 1;
const int __STDC_ISO_10646__ = 201706;
const int _DARWIN_FEATURE_ONLY_64_BIT_INODE = 1;
const int __GNU_LIBRARY__ = 6;
const int _DARWIN_FEATURE_ONLY_VERS_1050 = 1;
const int __GLIBC__ = 2;
const int _DARWIN_FEATURE_ONLY_UNIX_CONFORMANCE = 1;
const int __GLIBC_MINOR__ = 37;
const int _DARWIN_FEATURE_UNIX_CONFORMANCE = 3;
const int _SYS_CDEFS_H = 1;
const int __has_ptrcheck = 0;
const int __THROW = 1;
const int __DARWIN_NULL = 0;
const int __THROWNL = 1;
const int __PTHREAD_SIZE__ = 8176;
const int __glibc_c99_flexarr_available = 1;
const int __PTHREAD_ATTR_SIZE__ = 56;
const int __LDOUBLE_REDIRECTS_TO_FLOAT128_ABI = 0;
const int __PTHREAD_MUTEXATTR_SIZE__ = 8;
const int __HAVE_GENERIC_SELECTION = 0;
const int __PTHREAD_MUTEX_SIZE__ = 56;
const int __GLIBC_USE_LIB_EXT2 = 1;
const int __PTHREAD_CONDATTR_SIZE__ = 8;
const int __GLIBC_USE_IEC_60559_BFP_EXT = 1;
const int __PTHREAD_COND_SIZE__ = 40;
const int __GLIBC_USE_IEC_60559_BFP_EXT_C2X = 1;
const int __PTHREAD_ONCE_SIZE__ = 8;
const int __GLIBC_USE_IEC_60559_EXT = 1;
const int __PTHREAD_RWLOCK_SIZE__ = 192;
const int __GLIBC_USE_IEC_60559_FUNCS_EXT = 1;
const int __PTHREAD_RWLOCKATTR_SIZE__ = 16;
const int __GLIBC_USE_IEC_60559_FUNCS_EXT_C2X = 1;
const int USER_ADDR_NULL = 0;
const int __GLIBC_USE_IEC_60559_TYPES_EXT = 1;
const int INT8_MAX = 127;
const int _BITS_TYPES_H = 1;
const int INT16_MAX = 32767;
const int _BITS_TYPESIZES_H = 1;
const int INT32_MAX = 2147483647;
const int __OFF_T_MATCHES_OFF64_T = 1;
const int INT64_MAX = 9223372036854775807;
const int __INO_T_MATCHES_INO64_T = 1;
const int __RLIM_T_MATCHES_RLIM64_T = 1;
const int __STATFS_MATCHES_STATFS64 = 1;
const int __KERNEL_OLD_TIMEVAL_MATCHES_TIMEVAL64 = 1;
const int __FD_SETSIZE = 1024;
const int _BITS_TIME64_H = 1;
const int _BITS_WCHAR_H = 1;
const int __WCHAR_MAX = 2147483647;
const int __WCHAR_MIN = -2147483648;
const int _BITS_STDINT_INTN_H = 1;
const int _BITS_STDINT_UINTN_H = 1;
const int INT8_MIN = -128;
@@ -1355,6 +1324,14 @@ const int INT32_MIN = -2147483648;
const int INT64_MIN = -9223372036854775808;
const int INT8_MAX = 127;
const int INT16_MAX = 32767;
const int INT32_MAX = 2147483647;
const int INT64_MAX = 9223372036854775807;
const int UINT8_MAX = 255;
const int UINT16_MAX = 65535;
@@ -1389,66 +1366,54 @@ const int UINT_LEAST64_MAX = -1;
const int INT_FAST8_MIN = -128;
const int INT_FAST16_MIN = -32768;
const int INT_FAST16_MIN = -9223372036854775808;
const int INT_FAST32_MIN = -2147483648;
const int INT_FAST32_MIN = -9223372036854775808;
const int INT_FAST64_MIN = -9223372036854775808;
const int INT_FAST8_MAX = 127;
const int INT_FAST16_MAX = 32767;
const int INT_FAST16_MAX = 9223372036854775807;
const int INT_FAST32_MAX = 2147483647;
const int INT_FAST32_MAX = 9223372036854775807;
const int INT_FAST64_MAX = 9223372036854775807;
const int UINT_FAST8_MAX = 255;
const int UINT_FAST16_MAX = 65535;
const int UINT_FAST16_MAX = -1;
const int UINT_FAST32_MAX = 4294967295;
const int UINT_FAST32_MAX = -1;
const int UINT_FAST64_MAX = -1;
const int INTPTR_MAX = 9223372036854775807;
const int INTPTR_MIN = -9223372036854775808;
const int INTPTR_MAX = 9223372036854775807;
const int UINTPTR_MAX = -1;
const int INTMAX_MIN = -9223372036854775808;
const int INTMAX_MAX = 9223372036854775807;
const int UINTMAX_MAX = -1;
const int INTMAX_MIN = -9223372036854775808;
const int PTRDIFF_MIN = -9223372036854775808;
const int PTRDIFF_MAX = 9223372036854775807;
const int SIZE_MAX = -1;
const int RSIZE_MAX = 9223372036854775807;
const int WCHAR_MAX = 2147483647;
const int WCHAR_MIN = -2147483648;
const int WINT_MIN = -2147483648;
const int WINT_MAX = 2147483647;
const int SIG_ATOMIC_MIN = -2147483648;
const int SIG_ATOMIC_MAX = 2147483647;
const int __DARWIN_WCHAR_MAX = 2147483647;
const int SIZE_MAX = -1;
const int __DARWIN_WCHAR_MIN = -2147483648;
const int WCHAR_MIN = -2147483648;
const int __DARWIN_WEOF = -1;
const int WCHAR_MAX = 2147483647;
const int _FORTIFY_SOURCE = 2;
const int WINT_MIN = 0;
const int NULL = 0;
const int WINT_MAX = 4294967295;

View File

@@ -1,63 +0,0 @@
// import 'package:flutter/widgets.dart';
// import 'package:polyvox_filament/filament_controller.dart';
// import 'package:polyvox_filament/filament_controller.dart';
// import 'package:vector_math/vector_math_64.dart';
// class Position {
// final double x;
// final double y;
// final double z;
// Position(this.x, this.y, this.z);
// Position copy({double? x, double? y, double? z}) {
// return Position(x ?? this.x, y ?? this.y, z ?? this.z);
// }
// factory Position.zero() {
// return Position(0,0,0);
// }
// }
// class Rotation {
// final double rads;
// final double x;
// final double y;
// final double z;
// Rotation(this.x, this.y, this.z, this.rads);
// Rotation copy({double? rads, double? x, double? y, double? z}) {
// return Rotation(x ?? this.x, y ?? this.y, z ?? this.z, rads ?? this.rads);
// }
// factory Rotation.zero() {
// return Rotation(0, 1,0,0);
// }
// }
// ///
// /// Handles local transforms for assets/cameras.
// ///
// class TransformManager {
// final FilamentController _controller;
// Matrix4 transform = Matrix4.identity();
// TransformManager(this._controller);
// void scale(double scale) {
// transform.scale(scale, scale, scale);
// }
// void translate(double x, double y, double z) {
// transform.translate(x,y,z);
// }
// void rotate(double x, double y, double z) {
// transform.rotate(Vector3(x,y,z));
// }
// }