add integration tests and update README
This commit is contained in:
98
README.md
98
README.md
@@ -1,46 +1,38 @@
|
||||
# Flutter Filament
|
||||
|
||||
Cross-platform, Physically-based rendering inside Flutter applications.
|
||||
Cross-platform, 3D PBR rendering and animation for Flutter.
|
||||
|
||||
Flutter plugin wrapping the Filament renderer https://github.com/google/filament.
|
||||
Wraps the [the Filament rendering library](https://github.com/google/filament).
|
||||
|
||||
Powers the Polyvox and odd-io engines.
|
||||
|
||||
# Sponsors
|
||||
|Feature|Supported|
|
||||
|---|---|
|
||||
|glTF|Partial - see Known Issues|
|
||||
|glb|✅|
|
||||
|Camera movement (mouse/finger gesture)|✅|
|
||||
|Skinning|✅|
|
||||
|Morph targets|✅|
|
||||
|Runtime material changes|Partial|
|
||||
|Viewport entity selection|✅|
|
||||
|Entity transform manipulation (mouse/finger gesture)|Partial|
|
||||
|Entity/transform parenting|Planned|
|
||||
|Entity material manipulation|Partial|
|
||||
|
||||
Thank you to odd-io for sponsoring work on supporting Windows, raycasting, testing and documentation.
|
||||
Special thanks to odd-io for sponsoring work on supporting Windows, raycasting, testing and documentation.
|
||||
|
||||
# Overview
|
||||
PRs are welcome but please create a placeholder PR to discuss before writing any code. This will help avoid
|
||||
|
||||
## Versioning
|
||||
## Getting Started
|
||||
|
||||
This package is currently only tested on Flutter `3.15.0-15.2.pre`, so you will need to first switch to the `beta` channel:
|
||||
|
||||
Last tested on Flutter `3.15.0-15.2.pre`. This is on the Flutter beta channel, so run:
|
||||
```
|
||||
flutter channel beta
|
||||
flutter upgrade
|
||||
```
|
||||
|
||||
||Android|iOS|MacOS|Windows|Linux|WebGL
|
||||
|---|---|---|---|---|---||
|
||||
|Filament|v1.43.1 (arm64/armeabi-v7a/x86/x86_64)|v1.43.1* (arm64)|v1.43.1 (arm64)|v1.32.4 (x86_64)|TODO**|TODO***|
|
||||
|Flutter||3.15.0-15.2.pre|3.15.0-15.2.pre|3.15.0-15.2.pre
|
||||
|
||||
* iOS release build has a skybox bug so the debug versions are currently shipped on iOS
|
||||
** (Waiting for https://github.com/google/filament/issues/7078 to be resolved before upgrading, not sure exactly when the bug was introduced but it was somewhere between v1.32.4 and v1.40.0)
|
||||
*** Texture widget not currently supported on web in Flutter.
|
||||
|
||||
## Features
|
||||
|
||||
|Feature|Supported|
|
||||
|---|---|
|
||||
|glTF|Y|
|
||||
|glb|Y|
|
||||
|
||||
# Basic Setup
|
||||
|
||||
## Clone flutter_filament
|
||||
|
||||
This plugin is not yet published to pub.dev. To use in your project, simply clone the repository and pull the latest binaries from Git LFS:
|
||||
Next, clone this repository and pull the latest binaries from Git LFS:
|
||||
|
||||
```
|
||||
cd $HOME
|
||||
@@ -48,17 +40,17 @@ git clone <repo> && cd flutter_filament
|
||||
git lfs pull
|
||||
```
|
||||
|
||||
You *do not need to build Filament yourself*. The repository is bundled with all necessary headers/static libraries (`windows/lib`, `ios/lib`, `macos/lib` and `linux/lib`) and the Flutter plugin has been configured to link at build time.
|
||||
(these instructions will be updated after the plugin is published to pub.dev).
|
||||
> [!NOTE]
|
||||
> You *do not need to build Filament yourself*. The repository is bundled with all necessary headers/static libraries (`windows/lib`, `ios/lib`, `macos/lib` and `linux/lib`) and the Flutter plugin has been configured to link at build time.
|
||||
|
||||
If you want to run the example project to check:
|
||||
Run the example project to check:
|
||||
|
||||
```
|
||||
cd example && flutter run -d <macos/windows/Your iPhone/etc>
|
||||
```
|
||||
|
||||
## Add dependency
|
||||
|
||||
Add the plugin as a dependency in the pubspec.yaml for your application:
|
||||
To use the plugin in your own project, add the plugin to your pubspec.yaml:
|
||||
|
||||
```
|
||||
name: your_project
|
||||
@@ -71,11 +63,11 @@ dependencies:
|
||||
path: <path where you cloned the repository>
|
||||
```
|
||||
|
||||
# Basic Usage
|
||||
## Basic Usage
|
||||
|
||||
See the `example` project for a complete sample of the below steps.
|
||||
|
||||
## Creating the viewport widget and controller
|
||||
### Creating the viewport widget and controller
|
||||
|
||||
To embed a viewport in your app, create an instance of `FilamentControllerFFI` somewhere in your app:
|
||||
|
||||
@@ -147,12 +139,12 @@ You can also pass a URI to indicate that the glTF file should be loaded from the
|
||||
var entity = _filamentController.loadGlb("file:///tmp/bob.glb");
|
||||
```
|
||||
|
||||
The return type `FilamentEntity` is simply an integer handle that be used to manipulate the entity in the scene.
|
||||
|
||||
For example, to remove the asset:
|
||||
The return type `FilamentEntity` is simply an integer handle that be used to manipulate the entity in the scene. For example, to remove the asset:
|
||||
```
|
||||
_filamentController.removeAsset(entity);
|
||||
entity = null; // see note* below
|
||||
```
|
||||
* Removing an entity from the scene will invalidate the corresponding `FilamentEntity` handle, so ensure you don't retain any references to it after calling `removeAsset` or `clearAssets`. Removing one `FilamentEntity` does not invalidate/change any other `FilamentEntity` handles; you can continue to safely manipulate these via the `FilamentController`.
|
||||
|
||||
To set the world space position of the asset:
|
||||
```
|
||||
@@ -161,7 +153,6 @@ _filamentController.setPositon(entity, 1.0, 1.0, 1.0);
|
||||
|
||||
On desktop, you can also click any renderable object in the viewport to retrieve its associated FilamentEntity (see below).
|
||||
|
||||
Whenever an entity is removed from the scene (or you've called another method that clears all entities), the corresponding `FilamentEntity` handle(s) are invalidated and should not be used.
|
||||
|
||||
### Camera movement
|
||||
|
||||
@@ -257,7 +248,36 @@ uberz -TSHADINGMODEL=lit -TBLENDING=opaque -o lit_opaque_43.uberz lit_opaque
|
||||
|
||||
(note that the number in the filename corresponds to the Material version, not the Filament version. Not every Filament version requires a new Material version).
|
||||
|
||||
## Versioning
|
||||
|
||||
||Android|iOS|MacOS|Windows|Linux|WebGL|
|
||||
|---|---|---|---|---|---||
|
||||
|Filament|v1.43.1 (arm64/armeabi-v7a/x86/x86_64)|v1.43.1* (arm64)|v1.43.1 (arm64)|v1.32.4 (x86_64)|TODO**|TODO***|
|
||||
|Flutter||3.15.0-15.2.pre|3.15.0-15.2.pre|3.15.0-15.2.pre
|
||||
|
||||
* iOS release build has a skybox bug so the debug versions are currently shipped on iOS
|
||||
** (Waiting for https://github.com/google/filament/issues/7078 to be resolved before upgrading, not sure exactly when the bug was introduced but it was somewhere between v1.32.4 and v1.40.0)
|
||||
*** Texture widget not currently supported on web in Flutter.
|
||||
|
||||
## Testing
|
||||
|
||||
We automate testing by running the example project on actual iOS/Android/MacOS/Windows devices and executing various operations.
|
||||
|
||||
Eventually we want to compare screenshots after each operation to a set of goldens for every platform.
|
||||
|
||||
Currently this is only possible on iOS (see https://github.com/flutter/flutter/issues/123063 and https://github.com/flutter/flutter/issues/127306).
|
||||
|
||||
To re-generate the golden screenshots for a given device:
|
||||
|
||||
```
|
||||
./regenerate_goldens.sh <your_device_id>
|
||||
```
|
||||
To run the tests and compare against those goldens:
|
||||
```
|
||||
./compare_goldens.sh <your_device_id>
|
||||
```
|
||||
|
||||
The results will depend on the actual device used to generate the golden, therefore if you are using a different device (which is likely), your results may not be the same. This is expected.
|
||||
|
||||
# Building Filament from source
|
||||
|
||||
|
||||
16
example/.gitattributes
vendored
16
example/.gitattributes
vendored
@@ -3,3 +3,19 @@ assets/BusterDrone filter=lfs diff=lfs merge=lfs -text
|
||||
assets/FlightHelmet filter=lfs diff=lfs merge=lfs -text
|
||||
assets/lit_opaque_32.uberz filter=lfs diff=lfs merge=lfs -text
|
||||
windows/lib/**/*.* filter=lfs diff=lfs merge=lfs -text
|
||||
integration_test/goldens/ios/12_Settonemappingtolinear.png filter=lfs diff=lfs merge=lfs -text
|
||||
integration_test/goldens/ios/3_loadIBL.png filter=lfs diff=lfs merge=lfs -text
|
||||
integration_test/goldens/ios/6_zoomin.png filter=lfs diff=lfs merge=lfs -text
|
||||
integration_test/goldens/ios/13_Movecameratoasset.png filter=lfs diff=lfs merge=lfs -text
|
||||
integration_test/goldens/ios/14_movecamerato.png filter=lfs diff=lfs merge=lfs -text
|
||||
integration_test/goldens/ios/7_rotate.png filter=lfs diff=lfs merge=lfs -text
|
||||
integration_test/goldens/ios/9_transformtounitcube.png filter=lfs diff=lfs merge=lfs -text
|
||||
integration_test/goldens/ios/0_fresh.png filter=lfs diff=lfs merge=lfs -text
|
||||
integration_test/goldens/ios/10_setshapespositionto.png filter=lfs diff=lfs merge=lfs -text
|
||||
integration_test/goldens/ios/2_loadskybox.png filter=lfs diff=lfs merge=lfs -text
|
||||
integration_test/goldens/ios/4_Renderingfalse.png filter=lfs diff=lfs merge=lfs -text
|
||||
integration_test/goldens/ios/8_pan.png filter=lfs diff=lfs merge=lfs -text
|
||||
integration_test/goldens/ios/11_Disablefrustumculling.png filter=lfs diff=lfs merge=lfs -text
|
||||
integration_test/goldens/ios/15_setcameratofirstcamerainshapesGLB.png filter=lfs diff=lfs merge=lfs -text
|
||||
integration_test/goldens/ios/1_createviewerdefaultubershader.png filter=lfs diff=lfs merge=lfs -text
|
||||
integration_test/goldens/ios/5_loadshapesGLB.png filter=lfs diff=lfs merge=lfs -text
|
||||
|
||||
9
example/compare_goldens.sh
Normal file
9
example/compare_goldens.sh
Normal file
@@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
device=$1
|
||||
if [ -z "$device" ]; then
|
||||
echo "Usage: $0 <device_id>"
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
rm integration_test/goldens/{ios,macos,windows,android}/diffs/*.png
|
||||
flutter drive --driver=test_driver/integration_test.dart -d $1 --target=integration_test/plugin_integration_test.dart
|
||||
3
example/integration_test/goldens/ios/0_fresh.png
Normal file
3
example/integration_test/goldens/ios/0_fresh.png
Normal file
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:556955b3ea80e461ab1d19de856276b286e964ea6ffbf5b4f788b03b4d1f1d41
|
||||
size 258315
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:7eaf3e2e5faab0fff2947e6747f44a29a27441fed30dfa5af586f5d18e4c313f
|
||||
size 1876179
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:1abf2fbc19655ce0d99f15a5659268a16c1ef6d2cf2554e07b70fc06505c22e1
|
||||
size 1867991
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:91f82e50232a672de1604da9555c4a62996c577a9ec9d7a037dfcc989b27a2c2
|
||||
size 1743987
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:3c4408b417f5e6fd119c0d7edc6cfd66d44fb1d6d1f03943b7485ab131d3fcbb
|
||||
size 2283182
|
||||
3
example/integration_test/goldens/ios/14_movecamerato.png
Normal file
3
example/integration_test/goldens/ios/14_movecamerato.png
Normal file
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:4347cade24c7c55b0ca449b3a346fa4bae0fc8c43ca0018d622b8393ad4d7f5c
|
||||
size 1957365
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:eb73e1d9d7c5dfa5cef180d1f1d930da417f9b164e237399c8a7394dd60415e8
|
||||
size 1892497
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:4f7d232df983168399a0aae4b5424d6493ce460794101015f05c07a929268698
|
||||
size 310421
|
||||
3
example/integration_test/goldens/ios/2_loadskybox.png
Normal file
3
example/integration_test/goldens/ios/2_loadskybox.png
Normal file
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:891223fabbace04e3394dd15852180e443460111cf96a92b7f3a45d5600318cf
|
||||
size 306407
|
||||
3
example/integration_test/goldens/ios/3_loadIBL.png
Normal file
3
example/integration_test/goldens/ios/3_loadIBL.png
Normal file
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:df8157a13a07023837f36645a517a77e837fa5983ddd3a92b883cf96b8098f65
|
||||
size 304380
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:4e85d3916e42afe77a9854c3297d5d85ce1fa8f973883f5d48bf5efd99bf60e9
|
||||
size 309699
|
||||
3
example/integration_test/goldens/ios/5_loadshapesGLB.png
Normal file
3
example/integration_test/goldens/ios/5_loadshapesGLB.png
Normal file
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:ad4b4bd59ff6ffa870d3fffb0f9151ccc51392906a6fbdc8832e31b4965c64d6
|
||||
size 300230
|
||||
3
example/integration_test/goldens/ios/6_zoomin.png
Normal file
3
example/integration_test/goldens/ios/6_zoomin.png
Normal file
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:a297bad3e356e06fb68c53b8718a72924dfd88ca2c5ed1f486fffe5780c77084
|
||||
size 1944342
|
||||
3
example/integration_test/goldens/ios/7_rotate.png
Normal file
3
example/integration_test/goldens/ios/7_rotate.png
Normal file
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:19bc90955c575a28d9b8e4112c24fe9b65f3ba6df7a3579f036761fe6a54d065
|
||||
size 1937031
|
||||
3
example/integration_test/goldens/ios/8_pan.png
Normal file
3
example/integration_test/goldens/ios/8_pan.png
Normal file
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:72b73825e31bf5d6ae3c880a17fe578f9351749d68a9b1fc710b838985b833fa
|
||||
size 1938760
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:a3a78e7ec65d33d62105938de4a14f50a91ed4009beed588f17379ad3d7df9b6
|
||||
size 1941399
|
||||
@@ -1,25 +1,152 @@
|
||||
// This is a basic Flutter integration test.
|
||||
//
|
||||
// Since integration tests run in a full Flutter application, they can interact
|
||||
// with the host side of a plugin implementation, unlike Dart unit tests.
|
||||
//
|
||||
// For more information about Flutter integration tests, please see
|
||||
// https://docs.flutter.dev/cookbook/testing/integration/introduction
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import 'package:polyvox_filament/polyvox_filament.dart';
|
||||
import 'package:golden_toolkit/golden_toolkit.dart';
|
||||
import 'package:polyvox_filament/widgets/filament_widget.dart';
|
||||
import '../lib/main.dart' as app;
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
final binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized()
|
||||
as IntegrationTestWidgetsFlutterBinding;
|
||||
|
||||
testWidgets('getPlatformVersion test', (WidgetTester tester) async {
|
||||
final PolyvoxFilament plugin = PolyvoxFilament();
|
||||
final String? version = await plugin.getPlatformVersion();
|
||||
// The version string depends on the host platform running the test, so
|
||||
// just assert that some non-empty string is returned.
|
||||
expect(version?.isNotEmpty, true);
|
||||
late String platformIdentifier;
|
||||
if (Platform.isIOS) {
|
||||
platformIdentifier = "ios";
|
||||
} else if (Platform.isAndroid) {
|
||||
platformIdentifier = "android";
|
||||
} else if (Platform.isMacOS) {
|
||||
platformIdentifier = "macos";
|
||||
} else if (Platform.isWindows) {
|
||||
platformIdentifier = "windows";
|
||||
} else if (Platform.isLinux) {
|
||||
platformIdentifier = "linux";
|
||||
} else {
|
||||
throw Exception("Unexpected platform");
|
||||
}
|
||||
|
||||
int _counter = 0;
|
||||
|
||||
Future _snapshot(WidgetTester tester, Device device, String label,
|
||||
[int seconds = 0]) async {
|
||||
for (int i = 0; i < seconds; i++) {
|
||||
await Future.delayed(Duration(seconds: 1));
|
||||
await tester.pumpAndSettle();
|
||||
}
|
||||
await Future.delayed(Duration(milliseconds: 100));
|
||||
await tester.pumpAndSettle();
|
||||
var screenshotPath = '$platformIdentifier/${_counter}_$label';
|
||||
if (Platform.isIOS) {
|
||||
// this is currently hanging on Android
|
||||
// see https://github.com/flutter/flutter/issues/127306
|
||||
// it is also not yet implemented on Windows or MacOS
|
||||
await binding.convertFlutterSurfaceToImage();
|
||||
final bytes = await binding.takeScreenshot(screenshotPath);
|
||||
}
|
||||
_counter++;
|
||||
}
|
||||
|
||||
late Device device;
|
||||
|
||||
Future tap(WidgetTester tester, String label, [int seconds = 0]) async {
|
||||
var target = find.text(label).first;
|
||||
await tester.dragUntilVisible(
|
||||
target,
|
||||
find.byType(SingleChildScrollView),
|
||||
// widget you want to scroll
|
||||
const Offset(0, 500), // delta to move
|
||||
duration: Duration(milliseconds: 10));
|
||||
await tester.tap(target);
|
||||
await _snapshot(
|
||||
tester, device, label.replaceAll(RegExp("[ -:]"), ""), seconds);
|
||||
}
|
||||
|
||||
Future<void> pumpUntilFound(
|
||||
WidgetTester tester,
|
||||
Finder finder, {
|
||||
Duration timeout = const Duration(seconds: 30),
|
||||
}) async {
|
||||
bool timerDone = false;
|
||||
final timer = Timer(
|
||||
timeout, () => throw TimeoutException("Pump until has timed out"));
|
||||
while (timerDone != true) {
|
||||
await tester.pump();
|
||||
|
||||
final found = tester.any(finder);
|
||||
if (found) {
|
||||
timerDone = true;
|
||||
}
|
||||
}
|
||||
timer.cancel();
|
||||
}
|
||||
|
||||
testGoldens('test', (WidgetTester tester) async {
|
||||
app.main();
|
||||
await pumpUntilFound(tester, find.byType(app.ExampleWidget));
|
||||
device = Device(size: Size(800, 600), name: "desktop");
|
||||
await _snapshot(tester, device, "fresh");
|
||||
await tap(tester, "create viewer (default ubershader)", 4);
|
||||
|
||||
await tap(tester, "load skybox");
|
||||
await tap(tester, "load IBL");
|
||||
await tap(tester, "Rendering: false");
|
||||
await tap(tester, "load shapes GLB");
|
||||
|
||||
final Offset pointerLocation =
|
||||
tester.getCenter(find.byType(FilamentWidget));
|
||||
TestPointer testPointer = TestPointer(1, PointerDeviceKind.mouse);
|
||||
|
||||
// scroll/zoom
|
||||
testPointer.hover(pointerLocation);
|
||||
await tester.sendEventToBinding(testPointer.scroll(const Offset(0.0, 1.0)));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.sendEventToBinding(testPointer.scroll(const Offset(0.0, 1.0)));
|
||||
await tester.pumpAndSettle();
|
||||
await _snapshot(tester, device, "zoomin");
|
||||
|
||||
// rotate
|
||||
testPointer =
|
||||
TestPointer(1, PointerDeviceKind.mouse, null, kTertiaryButton);
|
||||
testPointer.hover(pointerLocation);
|
||||
await tester.sendEventToBinding(testPointer.down(pointerLocation));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.sendEventToBinding(
|
||||
testPointer.move(pointerLocation + Offset(10.0, 10.0)));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.sendEventToBinding(
|
||||
testPointer.move(pointerLocation + Offset(20.0, 20.0)));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.sendEventToBinding(testPointer.up());
|
||||
|
||||
await _snapshot(tester, device, "rotate", 2);
|
||||
|
||||
// pan
|
||||
testPointer = TestPointer(1, PointerDeviceKind.mouse, null, kPrimaryButton);
|
||||
testPointer.hover(pointerLocation);
|
||||
await tester.sendEventToBinding(testPointer.down(pointerLocation));
|
||||
await tester
|
||||
.sendEventToBinding(testPointer.move(pointerLocation + Offset(0, 1.0)));
|
||||
|
||||
for (int i = 0; i < 60; i++) {
|
||||
await tester.sendEventToBinding(testPointer
|
||||
.move(pointerLocation + Offset(i.toDouble() * 2, i.toDouble() * 2)));
|
||||
await tester.pumpAndSettle();
|
||||
}
|
||||
await tester.sendEventToBinding(testPointer.up());
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await _snapshot(tester, device, "pan");
|
||||
|
||||
await tap(tester, "transform to unit cube");
|
||||
await tap(tester, "set shapes position to 1, 1, -1");
|
||||
await tap(tester, "Disable frustum culling");
|
||||
await tap(tester, "Set tone mapping to linear");
|
||||
await tap(tester, "Move camera to asset");
|
||||
await tap(tester, "move camera to 1, 1, -1");
|
||||
await tap(tester, 'set camera to first camera in shapes GLB');
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,21 +1,34 @@
|
||||
PODS:
|
||||
- Flutter (1.0.0)
|
||||
- integration_test (0.0.1):
|
||||
- Flutter
|
||||
- path_provider_foundation (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- polyvox_filament (0.0.1):
|
||||
- Flutter
|
||||
|
||||
DEPENDENCIES:
|
||||
- Flutter (from `Flutter`)
|
||||
- integration_test (from `.symlinks/plugins/integration_test/ios`)
|
||||
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
|
||||
- polyvox_filament (from `.symlinks/plugins/polyvox_filament/ios`)
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
Flutter:
|
||||
:path: Flutter
|
||||
integration_test:
|
||||
:path: ".symlinks/plugins/integration_test/ios"
|
||||
path_provider_foundation:
|
||||
:path: ".symlinks/plugins/path_provider_foundation/darwin"
|
||||
polyvox_filament:
|
||||
:path: ".symlinks/plugins/polyvox_filament/ios"
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
|
||||
polyvox_filament: 99047b2e0d56e073f5db603dd6152a1598c2a345
|
||||
integration_test: 13825b8a9334a850581300559b8839134b124670
|
||||
path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943
|
||||
polyvox_filament: 35fece7761e74c973afd80fe3aa0ca225eaace32
|
||||
|
||||
PODFILE CHECKSUM: 7adbc9d59f05e1b01f554ea99b6c79e97f2214a2
|
||||
|
||||
|
||||
@@ -138,6 +138,7 @@
|
||||
97C146EC1CF9000F007C117D /* Resources */,
|
||||
9705A1C41CF9048500538489 /* Embed Frameworks */,
|
||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
||||
F9FAB8A67CF505858CCDA424 /* [CP] Embed Pods Frameworks */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
@@ -249,6 +250,23 @@
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
|
||||
};
|
||||
F9FAB8A67CF505858CCDA424 /* [CP] Embed Pods Frameworks */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
|
||||
);
|
||||
name = "[CP] Embed Pods Frameworks";
|
||||
outputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
|
||||
@@ -48,7 +48,7 @@ class _ExampleWidgetState extends State<ExampleWidget> {
|
||||
List<String>? _animations;
|
||||
FilamentEntity? _light;
|
||||
|
||||
late StreamSubscription _pickResultListener;
|
||||
StreamSubscription? _pickResultListener;
|
||||
String? picked;
|
||||
|
||||
final weights = List.filled(255, 0.0);
|
||||
@@ -63,19 +63,10 @@ class _ExampleWidgetState extends State<ExampleWidget> {
|
||||
bool _coneHidden = false;
|
||||
bool _frustumCulling = true;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
getApplicationSupportDirectory().then((dir) {
|
||||
print(dir);
|
||||
});
|
||||
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
_pickResultListener.cancel();
|
||||
_pickResultListener?.cancel();
|
||||
}
|
||||
|
||||
Widget _item(void Function() onTap, String text) {
|
||||
@@ -136,7 +127,7 @@ class _ExampleWidgetState extends State<ExampleWidget> {
|
||||
_rendering = !_rendering;
|
||||
_filamentController!.setRendering(_rendering);
|
||||
});
|
||||
}, "Rendering: $_rendering "),
|
||||
}, "Rendering: $_rendering"),
|
||||
_item(() {
|
||||
setState(() {
|
||||
_framerate = _framerate == 60 ? 30 : 60;
|
||||
@@ -207,8 +198,8 @@ class _ExampleWidgetState extends State<ExampleWidget> {
|
||||
_filamentController!.setPosition(_shapes!, 1.0, 1.0, -1.0);
|
||||
}, 'set shapes position to 1, 1, -1'),
|
||||
_item(() async {
|
||||
_filamentController!.setPosition(_shapes!, 1.0, 1.0, -1.0);
|
||||
}, 'move camera to shapes position'),
|
||||
_filamentController!.setCameraPosition(1.0, 1.0, -1.0);
|
||||
}, 'move camera to 1, 1, -1'),
|
||||
_item(() async {
|
||||
var frameData = Float32List.fromList(
|
||||
List<double>.generate(120, (i) => i / 120).expand((x) {
|
||||
@@ -350,7 +341,7 @@ class _ExampleWidgetState extends State<ExampleWidget> {
|
||||
right: 0,
|
||||
height: 200,
|
||||
child: Container(
|
||||
color: Colors.white,
|
||||
color: Colors.white.withOpacity(0.75),
|
||||
child: SingleChildScrollView(
|
||||
child: Wrap(children: children
|
||||
// _item(24 () async { 'rotate by pi around Y axis'),
|
||||
|
||||
@@ -1,21 +1,28 @@
|
||||
PODS:
|
||||
- FlutterMacOS (1.0.0)
|
||||
- path_provider_foundation (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- polyvox_filament (0.0.1):
|
||||
- FlutterMacOS
|
||||
|
||||
DEPENDENCIES:
|
||||
- FlutterMacOS (from `Flutter/ephemeral`)
|
||||
- path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`)
|
||||
- polyvox_filament (from `Flutter/ephemeral/.symlinks/plugins/polyvox_filament/macos`)
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
FlutterMacOS:
|
||||
:path: Flutter/ephemeral
|
||||
path_provider_foundation:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin
|
||||
polyvox_filament:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/polyvox_filament/macos
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
|
||||
polyvox_filament: 59a161de3df49c867bc2003d4ff73b8a304d2feb
|
||||
path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943
|
||||
polyvox_filament: 9aa36ae5e5654ff1576534086cbd618b239b75d6
|
||||
|
||||
PODFILE CHECKSUM: 9cc8fc8fc62b1d9a89fd6f974ad4157b35254030
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.app-sandbox</key>
|
||||
<true/>
|
||||
<false/>
|
||||
<key>com.apple.security.cs.allow-jit</key>
|
||||
<true/>
|
||||
<key>com.apple.security.network.server</key>
|
||||
|
||||
@@ -28,5 +28,7 @@
|
||||
<string>MainMenu</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>NSApplication</string>
|
||||
<key>com.apple.security.app-sandbox</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -26,7 +26,12 @@ dependencies:
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
integration_test:
|
||||
sdk: flutter
|
||||
flutter_lints: ^1.0.0
|
||||
golden_toolkit: ^0.15.0
|
||||
crypto:
|
||||
image_compare: ^1.1.2
|
||||
|
||||
|
||||
# For information on the generic Dart part of this file, see the
|
||||
|
||||
8
example/regenerate_goldens.sh
Normal file
8
example/regenerate_goldens.sh
Normal file
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
device=$1
|
||||
if [ -z "$device" ]; then
|
||||
echo "Usage: $0 <device_id>"
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
flutter drive --driver=test_driver/integration_test_update_goldens.dart -d $1 --target=integration_test/plugin_integration_test.dart
|
||||
58
example/test_driver/integration_test.dart
Normal file
58
example/test_driver/integration_test.dart
Normal file
@@ -0,0 +1,58 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:integration_test/integration_test_driver_extended.dart';
|
||||
import 'package:image_compare/image_compare.dart';
|
||||
|
||||
Future<void> main() async {
|
||||
await integrationDriver(
|
||||
onScreenshot: (
|
||||
String screenshotName,
|
||||
List<int> screenshotBytes, [
|
||||
Map<String, Object?>? args,
|
||||
]) async {
|
||||
final dir = screenshotName.split("/")[0];
|
||||
final name = screenshotName.split("/")[1];
|
||||
final File golden = await File('integration_test/goldens/$dir/$name.png');
|
||||
|
||||
if (!golden.existsSync()) {
|
||||
throw Exception(
|
||||
"Golden image doesn't exist yet. Make sure you have run integraton_test_update_goldens.dart first");
|
||||
}
|
||||
|
||||
var result = await compareImages(
|
||||
src1: screenshotBytes,
|
||||
src2: golden.readAsBytesSync(),
|
||||
algorithm: ChiSquareDistanceHistogram());
|
||||
|
||||
print(result);
|
||||
|
||||
// TODO - it would be preferable to use Flutter's GoldenFileComparator here, e.g.
|
||||
//
|
||||
// ```var comparator = LocalFileComparator(testImage.uri);
|
||||
// comparator.compare(imageBytes, golden)
|
||||
// comparator.getFailureFile(failure, golden, basedir)
|
||||
// var result = await comparator.compare(
|
||||
// Uint8List.fromList(screenshotBytes), golden.uri);
|
||||
// if (!result.passed) {
|
||||
// for (var key in result.diffs!.keys) {
|
||||
// var byteData = await result.diffs![key]!.toByteData();
|
||||
// File('integration_test/goldens/$dir/diffs/$name.png')
|
||||
// .writeAsBytesSync(
|
||||
// byteData!.buffer.asUint8List(byteData!.offsetInBytes));
|
||||
// }
|
||||
// return false;
|
||||
// }```
|
||||
// but this is only available via a Flutter shell which is currently unavailable (this script is run as a plain Dart file I guess).
|
||||
// let's revisit if/when this changes
|
||||
// see https://github.com/flutter/flutter/issues/51890 and https://github.com/flutter/flutter/issues/103222
|
||||
|
||||
if (result > 0.005) {
|
||||
File('integration_test/goldens/$dir/diffs/$name.png')
|
||||
.writeAsBytesSync(screenshotBytes);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
);
|
||||
}
|
||||
22
example/test_driver/integration_test_update_goldens.dart
Normal file
22
example/test_driver/integration_test_update_goldens.dart
Normal file
@@ -0,0 +1,22 @@
|
||||
import 'dart:io';
|
||||
import 'package:crypto/crypto.dart';
|
||||
import 'package:integration_test/integration_test_driver_extended.dart';
|
||||
|
||||
Future<void> main() async {
|
||||
await integrationDriver(
|
||||
onScreenshot: (
|
||||
String screenshotName,
|
||||
List<int> screenshotBytes, [
|
||||
Map<String, Object?>? args,
|
||||
]) async {
|
||||
final dir = screenshotName.split("/")[0];
|
||||
final name = screenshotName.split("/")[1];
|
||||
final File image = await File('integration_test/goldens/$dir/$name.png')
|
||||
.create(recursive: true);
|
||||
|
||||
image.writeAsBytesSync(screenshotBytes);
|
||||
|
||||
return true;
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -198,9 +198,16 @@ class FilamentControllerFFI extends FilamentController {
|
||||
///
|
||||
@override
|
||||
Future resize(int width, int height, {double scaleFactor = 1.0}) async {
|
||||
if (_textureId == null) {
|
||||
print("No texture created, ignoring call to resize.");
|
||||
return;
|
||||
}
|
||||
_resizing = true;
|
||||
setRendering(false);
|
||||
_lib.destroy_swap_chain(_viewer!);
|
||||
if (_viewer != null) {
|
||||
_lib.destroy_swap_chain(_viewer!);
|
||||
}
|
||||
|
||||
await destroyTexture();
|
||||
size = ui.Size(width * _pixelRatio, height * _pixelRatio);
|
||||
|
||||
|
||||
@@ -183,6 +183,7 @@ class _FilamentWidgetState extends State<FilamentWidget> {
|
||||
_resizeTimer?.cancel();
|
||||
|
||||
_resizeTimer = Timer(Duration(milliseconds: 500), () async {
|
||||
print("Resizing to $newSize");
|
||||
await widget.controller
|
||||
.resize(newSize.width.toInt(), newSize.height.toInt());
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||
|
||||
Reference in New Issue
Block a user