merge in work on controller from webjs branch

This commit is contained in:
Nick Fisher
2024-02-02 18:17:40 +08:00
parent 17e4014b3e
commit 0d9cf8a9ff
10 changed files with 688 additions and 132 deletions

View File

@@ -42,7 +42,8 @@ class FilamentControllerFFI extends FilamentController {
Pointer<Void> _driver = nullptr.cast<Void>();
@override
final rect = ValueNotifier<Rect?>(null);
final _rect = ValueNotifier<Rect?>(null);
final _rectCompleter = Completer<Rect?>();
@override
final hasViewer = ValueNotifier<bool>(false);
@@ -65,6 +66,13 @@ class FilamentControllerFFI extends FilamentController {
final _onUnloadController = StreamController<FilamentEntity>.broadcast();
Stream<FilamentEntity> get onUnload => _onUnloadController.stream;
final allocator = calloc;
void _using(Pointer ptr, Future Function(Pointer ptr) function) async {
await function.call(ptr);
allocator.free(ptr);
}
///
/// This controller uses platform channels to bridge Dart with the C/C++ code for the Filament API.
/// Setting up the context/texture (since this is platform-specific) and the render ticker are platform-specific; all other methods are passed through by the platform channel to the methods specified in FlutterFilamentApi.h.
@@ -83,7 +91,7 @@ class FilamentControllerFFI extends FilamentController {
_resizingWidth = call.arguments[0];
_resizingHeight = call.arguments[1];
_resizeTimer = Timer(const Duration(milliseconds: 500), () async {
rect.value = Offset.zero &
_rect.value = Offset.zero &
ui.Size(_resizingWidth!.toDouble(), _resizingHeight!.toDouble());
await resize();
});
@@ -132,12 +140,15 @@ class FilamentControllerFFI extends FilamentController {
@override
Future setDimensions(Rect rect, double pixelRatio) async {
this.rect.value = Rect.fromLTWH(
this._rect.value = Rect.fromLTWH(
(rect.left * _pixelRatio).floor().toDouble(),
rect.top * _pixelRatio.floor().toDouble(),
(rect.width * _pixelRatio).ceil().toDouble(),
(rect.height * _pixelRatio).ceil().toDouble());
_pixelRatio = pixelRatio;
if (!_rectCompleter.isCompleted) {
_rectCompleter.complete(this._rect.value);
}
}
@override
@@ -169,12 +180,23 @@ class FilamentControllerFFI extends FilamentController {
dev.log("Texture destroyed");
}
bool _creating = false;
///
/// Called by `FilamentWidget`. You do not need to call this yourself.
///
@override
Future createViewer() async {
if (rect.value == null) {
if (_creating) {
throw Exception(
"An existing call to createViewer is pending completion.");
}
_creating = true;
print("Waiting for widget dimensions to become available..");
await _rectCompleter.future;
print("Got widget dimensions : ${_rect.value}");
if (_rect.value == null) {
throw Exception(
"Dimensions have not yet been set by FilamentWidget. You need to wait for at least one frame after FilamentWidget has been inserted into the hierarchy");
}
@@ -209,13 +231,17 @@ class FilamentControllerFFI extends FilamentController {
dev.log("Got rendering surface");
final uberarchivePtr =
uberArchivePath?.toNativeUtf8().cast<Char>() ?? nullptr;
_viewer = create_filament_viewer_ffi(
Pointer<Void>.fromAddress(renderingSurface.sharedContext),
_driver,
uberArchivePath?.toNativeUtf8().cast<Char>() ?? nullptr,
uberarchivePtr,
loader,
renderCallback,
renderCallbackOwner);
allocator.free(uberarchivePtr);
dev.log("Created viewer");
if (_viewer!.address == 0) {
throw Exception("Failed to create viewer. Check logs for details");
@@ -224,31 +250,32 @@ class FilamentControllerFFI extends FilamentController {
_assetManager = get_asset_manager(_viewer!);
create_swap_chain_ffi(_viewer!, renderingSurface.surface,
rect.value!.width.toInt(), rect.value!.height.toInt());
_rect.value!.width.toInt(), _rect.value!.height.toInt());
dev.log("Created swap chain");
if (renderingSurface.textureHandle != 0) {
dev.log(
"Creating render target from native texture ${renderingSurface.textureHandle}");
create_render_target_ffi(_viewer!, renderingSurface.textureHandle,
rect.value!.width.toInt(), rect.value!.height.toInt());
_rect.value!.width.toInt(), _rect.value!.height.toInt());
}
textureDetails.value = TextureDetails(
textureId: renderingSurface.flutterTextureId,
width: rect.value!.width.toInt(),
height: rect.value!.height.toInt());
width: _rect.value!.width.toInt(),
height: _rect.value!.height.toInt());
dev.log("texture details ${textureDetails.value}");
update_viewport_and_camera_projection_ffi(
_viewer!, rect.value!.width.toInt(), rect.value!.height.toInt(), 1.0);
_viewer!, _rect.value!.width.toInt(), _rect.value!.height.toInt(), 1.0);
hasViewer.value = true;
_creating = false;
}
Future<RenderingSurface> _createRenderingSurface() async {
return RenderingSurface.from(await _channel.invokeMethod("createTexture", [
rect.value!.width,
rect.value!.height,
rect.value!.left,
rect.value!.top
_rect.value!.width,
_rect.value!.height,
_rect.value!.left,
_rect.value!.top
]));
}
@@ -342,12 +369,12 @@ class FilamentControllerFFI extends FilamentController {
"destroyTexture", textureDetails.value!.textureId);
}
} else if (Platform.isWindows) {
dev.log("Resizing window with rect $rect");
dev.log("Resizing window with rect ${_rect.value}");
await _channel.invokeMethod("resizeWindow", [
rect.value!.width,
rect.value!.height,
rect.value!.left,
rect.value!.top
_rect.value!.width,
_rect.value!.height,
_rect.value!.left,
_rect.value!.top
]);
}
@@ -361,23 +388,23 @@ class FilamentControllerFFI extends FilamentController {
if (!_usesBackingWindow) {
create_swap_chain_ffi(_viewer!, renderingSurface.surface,
rect.value!.width.toInt(), rect.value!.height.toInt());
_rect.value!.width.toInt(), _rect.value!.height.toInt());
}
if (renderingSurface.textureHandle != 0) {
dev.log(
"Creating render target from native texture ${renderingSurface.textureHandle}");
create_render_target_ffi(_viewer!, renderingSurface.textureHandle,
rect.value!.width.toInt(), rect.value!.height.toInt());
_rect.value!.width.toInt(), _rect.value!.height.toInt());
}
textureDetails.value = TextureDetails(
textureId: renderingSurface.flutterTextureId,
width: rect.value!.width.toInt(),
height: rect.value!.height.toInt());
width: _rect.value!.width.toInt(),
height: _rect.value!.height.toInt());
update_viewport_and_camera_projection_ffi(
_viewer!, rect.value!.width.toInt(), rect.value!.height.toInt(), 1.0);
update_viewport_and_camera_projection_ffi(_viewer!,
_rect.value!.width.toInt(), _rect.value!.height.toInt(), 1.0);
await setRendering(_rendering);
} finally {
@@ -398,8 +425,10 @@ class FilamentControllerFFI extends FilamentController {
if (_viewer == null) {
throw Exception("No viewer available, ignoring");
}
set_background_image_ffi(
_viewer!, path.toNativeUtf8().cast<Char>(), fillHeight);
final pathPtr = path.toNativeUtf8().cast<Char>();
set_background_image_ffi(_viewer!, pathPtr, fillHeight);
allocator.free(pathPtr);
}
@override
@@ -429,7 +458,8 @@ class FilamentControllerFFI extends FilamentController {
if (_viewer == null) {
throw Exception("No viewer available, ignoring");
}
load_skybox_ffi(_viewer!, skyboxPath.toNativeUtf8().cast<Char>());
final pathPtr = skyboxPath.toNativeUtf8().cast<Char>();
load_skybox_ffi(_viewer!, pathPtr);
}
@override
@@ -437,7 +467,21 @@ class FilamentControllerFFI extends FilamentController {
if (_viewer == null) {
throw Exception("No viewer available, ignoring");
}
load_ibl_ffi(_viewer!, lightingPath.toNativeUtf8().cast<Char>(), intensity);
final pathPtr = lightingPath.toNativeUtf8().cast<Char>();
load_ibl_ffi(_viewer!, pathPtr, intensity);
}
@override
Future rotateIbl(Matrix3 rotationMatrix) async {
if (_viewer == nullptr) {
throw Exception("No viewer available, ignoring");
}
var floatPtr = allocator<Float>(9);
for (int i = 0; i < 9; i++) {
floatPtr.elementAt(i).value = rotationMatrix.storage[i];
}
rotate_ibl(_viewer!, floatPtr);
allocator.free(floatPtr);
}
@override
@@ -508,8 +552,9 @@ class FilamentControllerFFI extends FilamentController {
if (unlit) {
throw Exception("Not yet implemented");
}
var entity =
load_glb_ffi(_assetManager!, path.toNativeUtf8().cast<Char>(), unlit);
final pathPtr = path.toNativeUtf8().cast<Char>();
var entity = load_glb_ffi(_assetManager!, pathPtr, unlit);
allocator.free(pathPtr);
if (entity == _FILAMENT_ASSET_ERROR) {
throw Exception("An error occurred loading the asset at $path");
}
@@ -528,8 +573,13 @@ class FilamentControllerFFI extends FilamentController {
if (_viewer == null) {
throw Exception("No viewer available, ignoring");
}
var entity = load_gltf_ffi(_assetManager!, path.toNativeUtf8().cast<Char>(),
relativeResourcePath.toNativeUtf8().cast<Char>());
final pathPtr = path.toNativeUtf8().cast<Char>();
final relativeResourcePathPtr =
relativeResourcePath.toNativeUtf8().cast<Char>();
var entity =
load_gltf_ffi(_assetManager!, pathPtr, relativeResourcePathPtr);
allocator.free(pathPtr);
allocator.free(relativeResourcePathPtr);
if (entity == _FILAMENT_ASSET_ERROR) {
throw Exception("An error occurred loading the asset at $path");
}
@@ -592,16 +642,16 @@ class FilamentControllerFFI extends FilamentController {
if (_viewer == null) {
throw Exception("No viewer available, ignoring");
}
var weightsPtr = calloc<Float>(weights.length);
var weightsPtr = allocator<Float>(weights.length);
for (int i = 0; i < weights.length; i++) {
weightsPtr.elementAt(i).value = weights[i];
}
var meshNamePtr = meshName.toNativeUtf8(allocator: calloc).cast<Char>();
var meshNamePtr = meshName.toNativeUtf8(allocator: allocator).cast<Char>();
set_morph_target_weights_ffi(
_assetManager!, entity, meshNamePtr, weightsPtr, weights.length);
calloc.free(weightsPtr);
calloc.free(meshNamePtr);
allocator.free(weightsPtr);
allocator.free(meshNamePtr);
}
@override
@@ -611,15 +661,16 @@ class FilamentControllerFFI extends FilamentController {
throw Exception("No viewer available, ignoring");
}
var names = <String>[];
var count = get_morph_target_name_count_ffi(
_assetManager!, entity, meshName.toNativeUtf8().cast<Char>());
var outPtr = calloc<Char>(255);
var meshNamePtr = meshName.toNativeUtf8().cast<Char>();
var count =
get_morph_target_name_count_ffi(_assetManager!, entity, meshNamePtr);
var outPtr = allocator<Char>(255);
for (int i = 0; i < count; i++) {
get_morph_target_name(_assetManager!, entity,
meshName.toNativeUtf8().cast<Char>(), outPtr, i);
get_morph_target_name(_assetManager!, entity, meshNamePtr, outPtr, i);
names.add(outPtr.cast<Utf8>().toDartString());
}
calloc.free(outPtr);
allocator.free(outPtr);
allocator.free(meshNamePtr);
return names.cast<String>();
}
@@ -630,7 +681,7 @@ class FilamentControllerFFI extends FilamentController {
}
var animationCount = get_animation_count(_assetManager!, entity);
var names = <String>[];
var outPtr = calloc<Char>(255);
var outPtr = allocator<Char>(255);
for (int i = 0; i < animationCount; i++) {
get_animation_name_ffi(_assetManager!, entity, outPtr, i);
names.add(outPtr.cast<Utf8>().toDartString());
@@ -658,12 +709,12 @@ class FilamentControllerFFI extends FilamentController {
throw Exception("No viewer available, ignoring");
}
var dataPtr = calloc<Float>(animation.data.length);
var dataPtr = allocator<Float>(animation.data.length);
for (int i = 0; i < animation.data.length; i++) {
dataPtr.elementAt(i).value = animation.data[i];
}
Pointer<Int> idxPtr = calloc<Int>(animation.morphTargets.length);
Pointer<Int> idxPtr = allocator<Int>(animation.morphTargets.length);
for (var meshName in animation.meshNames) {
// the morph targets in [animation] might be a subset of those that actually exist in the mesh (and might not have the same order)
@@ -674,15 +725,16 @@ class FilamentControllerFFI extends FilamentController {
for (int i = 0; i < animation.numMorphTargets; i++) {
var index = meshMorphTargets.indexOf(animation.morphTargets[i]);
if (index == -1) {
calloc.free(dataPtr);
calloc.free(idxPtr);
allocator.free(dataPtr);
allocator.free(idxPtr);
throw Exception(
"Morph target ${animation.morphTargets[i]} is specified in the animation but could not be found in the mesh $meshName under entity $entity");
}
idxPtr.elementAt(i).value = index;
}
var meshNamePtr = meshName.toNativeUtf8(allocator: calloc).cast<Char>();
var meshNamePtr =
meshName.toNativeUtf8(allocator: allocator).cast<Char>();
set_morph_animation(
_assetManager!,
@@ -693,47 +745,72 @@ class FilamentControllerFFI extends FilamentController {
animation.numMorphTargets,
animation.numFrames,
(animation.frameLengthInMs));
calloc.free(meshNamePtr);
allocator.free(meshNamePtr);
}
calloc.free(dataPtr);
calloc.free(idxPtr);
allocator.free(dataPtr);
allocator.free(idxPtr);
}
@override
Future addBoneAnimation(
FilamentEntity entity, BoneAnimationData animation) async {
FilamentEntity entity,
BoneAnimationData animation,
) async {
if (_viewer == null) {
throw Exception("No viewer available, ignoring");
}
var numFrames = animation.frameData.length;
var numFrames = animation.rotationFrameData.length;
var meshNames = calloc<Pointer<Char>>(animation.meshNames.length);
var meshNames = allocator<Pointer<Char>>(animation.meshNames.length);
for (int i = 0; i < animation.meshNames.length; i++) {
meshNames.elementAt(i).value =
animation.meshNames[i].toNativeUtf8().cast<Char>();
meshNames.elementAt(i).value = animation.meshNames[i]
.toNativeUtf8(allocator: allocator)
.cast<Char>();
}
var data = calloc<Float>(numFrames * 4);
var data = allocator<Float>(numFrames * 16);
DateTime start = DateTime.now();
for (int i = 0; i < numFrames; i++) {
data.elementAt(i * 4).value = animation.frameData[i].w;
data.elementAt((i * 4) + 1).value = animation.frameData[i].x;
data.elementAt((i * 4) + 2).value = animation.frameData[i].y;
data.elementAt((i * 4) + 3).value = animation.frameData[i].z;
for (var boneIndex = 0; boneIndex < animation.bones.length; boneIndex++) {
var bone = animation.bones[boneIndex];
var boneNamePtr = bone.toNativeUtf8(allocator: allocator).cast<Char>();
for (int i = 0; i < numFrames; i++) {
var rotation = animation.rotationFrameData[i][boneIndex];
var translation = animation.translationFrameData[i][boneIndex];
var mat4 = Matrix4.compose(translation, rotation, Vector3.all(1.0));
for (int j = 0; j < 16; j++) {
data.elementAt((i * 16) + j).value = mat4.storage[j];
}
}
add_bone_animation_ffi(
_assetManager!,
entity,
data,
numFrames,
boneNamePtr,
meshNames,
animation.meshNames.length,
animation.frameLengthInMs,
animation.isModelSpace);
allocator.free(boneNamePtr);
}
allocator.free(data);
for (int i = 0; i < animation.meshNames.length; i++) {
allocator.free(meshNames.elementAt(i).value);
}
allocator.free(meshNames);
}
add_bone_animation(
_assetManager!,
entity,
data,
numFrames,
animation.boneName.toNativeUtf8().cast<Char>(),
meshNames,
animation.meshNames.length,
animation.frameLengthInMs);
calloc.free(data);
calloc.free(meshNames);
@override
Future resetBones(FilamentEntity entity) async {
if (_viewer == nullptr) {
throw Exception("No viewer available, ignoring");
}
reset_to_rest_pose_ffi(_assetManager!, entity);
}
@override
@@ -818,8 +895,9 @@ class FilamentControllerFFI extends FilamentController {
if (_viewer == null) {
throw Exception("No viewer available, ignoring");
}
var result = set_camera(
_viewer!, entity, name?.toNativeUtf8().cast<Char>() ?? nullptr);
var cameraNamePtr = name?.toNativeUtf8().cast<Char>() ?? nullptr;
var result = set_camera(_viewer!, entity, cameraNamePtr);
allocator.free(cameraNamePtr);
if (!result) {
throw Exception("Failed to set camera");
}
@@ -932,12 +1010,12 @@ class FilamentControllerFFI extends FilamentController {
throw Exception("No viewer available, ignoring");
}
assert(matrix.length == 16);
var ptr = calloc<Float>(16);
var ptr = allocator<Float>(16);
for (int i = 0; i < 16; i++) {
ptr.elementAt(i).value = matrix[i];
}
set_camera_model_matrix(_viewer!, ptr);
calloc.free(ptr);
allocator.free(ptr);
}
@override
@@ -946,15 +1024,17 @@ class FilamentControllerFFI extends FilamentController {
if (_viewer == null) {
throw Exception("No viewer available, ignoring");
}
var meshNamePtr = meshName.toNativeUtf8().cast<Char>();
var result = set_material_color(
_assetManager!,
entity,
meshName.toNativeUtf8().cast<Char>(),
meshNamePtr,
materialIndex,
color.red.toDouble() / 255.0,
color.green.toDouble() / 255.0,
color.blue.toDouble() / 255.0,
color.alpha.toDouble() / 255.0);
allocator.free(meshNamePtr);
if (!result) {
throw Exception("Failed to set material color");
}
@@ -999,9 +1079,9 @@ class FilamentControllerFFI extends FilamentController {
if (_viewer == null) {
throw Exception("No viewer available, ignoring");
}
if (hide_mesh(
_assetManager!, entity, meshName.toNativeUtf8().cast<Char>()) !=
1) {}
final meshNamePtr = meshName.toNativeUtf8().cast<Char>();
if (hide_mesh(_assetManager!, entity, meshNamePtr) != 1) {}
allocator.free(meshNamePtr);
}
@override
@@ -1009,9 +1089,11 @@ class FilamentControllerFFI extends FilamentController {
if (_viewer == null) {
throw Exception("No viewer available, ignoring");
}
if (reveal_mesh(
_assetManager!, entity, meshName.toNativeUtf8().cast<Char>()) !=
1) {
final meshNamePtr = meshName.toNativeUtf8().cast<Char>();
final result = reveal_mesh(_assetManager!, entity, meshNamePtr) != 1;
allocator.free(meshNamePtr);
if (!result) {
throw Exception("Failed to reveal mesh $meshName");
}
}
@@ -1030,7 +1112,7 @@ class FilamentControllerFFI extends FilamentController {
if (_viewer == null) {
throw Exception("No viewer available, ignoring");
}
final outPtr = calloc<EntityId>(1);
final outPtr = allocator<EntityId>(1);
outPtr.value = 0;
pick_ffi(_viewer!, x, textureDetails.value!.height - y, outPtr);
@@ -1039,13 +1121,13 @@ class FilamentControllerFFI extends FilamentController {
await Future.delayed(const Duration(milliseconds: 32));
wait++;
if (wait > 10) {
calloc.free(outPtr);
allocator.free(outPtr);
throw Exception("Failed to get picking result");
}
}
var entityId = outPtr.value;
_pickResultController.add(entityId);
calloc.free(outPtr);
allocator.free(outPtr);
}
@override
@@ -1055,7 +1137,7 @@ class FilamentControllerFFI extends FilamentController {
}
var arrayPtr = get_camera_view_matrix(_viewer!);
var viewMatrix = Matrix4.fromList(arrayPtr.asTypedList(16));
calloc.free(arrayPtr);
allocator.free(arrayPtr);
return viewMatrix;
}
@@ -1066,7 +1148,7 @@ class FilamentControllerFFI extends FilamentController {
}
var arrayPtr = get_camera_model_matrix(_viewer!);
var modelMatrix = Matrix4.fromList(arrayPtr.asTypedList(16));
calloc.free(arrayPtr);
allocator.free(arrayPtr);
return modelMatrix;
}
@@ -1179,20 +1261,20 @@ class FilamentControllerFFI extends FilamentController {
@override
Future setBoneTransform(FilamentEntity entity, String meshName,
String boneName, Matrix4 data) async {
var ptr = calloc<Float>(16);
var ptr = allocator<Float>(16);
for (int i = 0; i < 16; i++) {
ptr.elementAt(i).value = data.storage[i];
}
var meshNamePtr = meshName.toNativeUtf8(allocator: calloc).cast<Char>();
var boneNamePtr = boneName.toNativeUtf8(allocator: calloc).cast<Char>();
var meshNamePtr = meshName.toNativeUtf8(allocator: allocator).cast<Char>();
var boneNamePtr = boneName.toNativeUtf8(allocator: allocator).cast<Char>();
var result = set_bone_transform_ffi(
_assetManager!, entity, meshNamePtr, ptr, boneNamePtr);
calloc.free(ptr);
calloc.free(meshNamePtr);
calloc.free(boneNamePtr);
allocator.free(ptr);
allocator.free(meshNamePtr);
allocator.free(boneNamePtr);
if (!result) {
throw Exception("Failed to set bone transform. See logs for details");
}
@@ -1201,18 +1283,31 @@ class FilamentControllerFFI extends FilamentController {
@override
Future<FilamentEntity> getChildEntity(
FilamentEntity parent, String childName) async {
var childNamePtr = childName.toNativeUtf8(allocator: calloc).cast<Char>();
try {
var childEntity =
find_child_entity_by_name(_assetManager!, parent, childNamePtr);
if (childEntity == _FILAMENT_ASSET_ERROR) {
throw Exception(
"Could not find child ${childName} under the specified entity");
}
return childEntity;
} finally {
calloc.free(childNamePtr);
var childNamePtr =
childName.toNativeUtf8(allocator: allocator).cast<Char>();
var childEntity =
find_child_entity_by_name(_assetManager!, parent, childNamePtr);
allocator.free(childNamePtr);
if (childEntity == _FILAMENT_ASSET_ERROR) {
throw Exception(
"Could not find child ${childName} under the specified entity");
}
return childEntity;
}
Future<List<String>> getMeshNames(FilamentEntity entity,
{bool async = false}) async {
var count = get_entity_count(_assetManager!, entity, true);
var names = <String>[];
for (int i = 0; i < count; i++) {
var name = get_entity_name_at(_assetManager!, entity, i, true);
if (name == nullptr) {
throw Exception("Failed to find mesh at index $i");
}
names.add(name.cast<Utf8>().toDartString());
}
return names;
}
@override
@@ -1222,8 +1317,8 @@ class FilamentControllerFFI extends FilamentController {
@override
Future setRecordingOutputDirectory(String outputDir) async {
var pathPtr = outputDir.toNativeUtf8(allocator: calloc);
var pathPtr = outputDir.toNativeUtf8(allocator: allocator);
set_recording_output_directory(_viewer!, pathPtr.cast<Char>());
calloc.free(pathPtr);
allocator.free(pathPtr);
}
}