1fb68b20e9
move gltf animation instantiation to GltfAnimationComponentManager (this helps ensure we are creating the component on the correct entity)
168 lines
5.1 KiB
Dart
168 lines
5.1 KiB
Dart
import 'dart:async';
|
|
import 'package:logging/logging.dart';
|
|
import 'package:flutter/material.dart' hide View;
|
|
import 'package:thermion_flutter/thermion_flutter.dart';
|
|
|
|
void main() {
|
|
runApp(const MyApp());
|
|
Logger.root.onRecord.listen((record) {
|
|
print(record);
|
|
});
|
|
}
|
|
|
|
class MyApp extends StatelessWidget {
|
|
const MyApp({super.key});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return MaterialApp(
|
|
title: 'Thermion Animation Demo',
|
|
theme: ThemeData(
|
|
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
|
|
useMaterial3: true,
|
|
),
|
|
home: const MyHomePage(title: 'Thermion Demo Home Page'),
|
|
);
|
|
}
|
|
}
|
|
|
|
class MyHomePage extends StatefulWidget {
|
|
const MyHomePage({super.key, required this.title});
|
|
final String title;
|
|
|
|
@override
|
|
State<MyHomePage> createState() => _MyHomePageState();
|
|
}
|
|
|
|
class _MyHomePageState extends State<MyHomePage> {
|
|
late DelegateInputHandler _inputHandler;
|
|
ThermionViewer? _thermionViewer;
|
|
|
|
ThermionAsset? _asset;
|
|
final _droneUri = "assets/BusterDrone/scene.gltf";
|
|
final _cubeUri = "assets/cube_with_morph_targets.glb";
|
|
String? _loaded;
|
|
|
|
final gltfAnimations = <String>[];
|
|
int selectedGltfAnimation = -1;
|
|
|
|
Future _load(String? uri) async {
|
|
if (_asset != null) {
|
|
await _thermionViewer!.destroyAsset(_asset!);
|
|
_asset = null;
|
|
}
|
|
|
|
_loaded = uri;
|
|
if (uri != null) {
|
|
_asset = await _thermionViewer!.loadGltf(uri);
|
|
await _asset!.transformToUnitCube();
|
|
final animations = await _asset!.getGltfAnimationNames();
|
|
final durations = await Future.wait(List.generate(
|
|
animations.length, (i) => _asset!.getGltfAnimationDuration(i)));
|
|
|
|
final labels = animations
|
|
.asMap()
|
|
.map((index, animation) =>
|
|
MapEntry(index, "$animation (${durations[index]}s"))
|
|
.values;
|
|
gltfAnimations.clear();
|
|
gltfAnimations.addAll(labels);
|
|
selectedGltfAnimation = 0;
|
|
|
|
}
|
|
setState(() {});
|
|
}
|
|
|
|
Future _playGltfAnimation() async {
|
|
if (selectedGltfAnimation == -1) {
|
|
throw Exception();
|
|
}
|
|
await _asset!.playGltfAnimation(selectedGltfAnimation);
|
|
}
|
|
|
|
Future _stopGltfAnimation() async {
|
|
if (selectedGltfAnimation == -1) {
|
|
throw Exception();
|
|
}
|
|
await _asset!.stopGltfAnimation(selectedGltfAnimation);
|
|
}
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
|
_thermionViewer = await ThermionFlutterPlugin.createViewer();
|
|
|
|
final camera = await _thermionViewer!.getActiveCamera();
|
|
await camera.lookAt(Vector3(0, 0, 10));
|
|
|
|
await _thermionViewer!.loadSkybox("assets/default_env_skybox.ktx");
|
|
await _thermionViewer!.loadIbl("assets/default_env_ibl.ktx");
|
|
await _thermionViewer!.setPostProcessing(true);
|
|
await _thermionViewer!.setRendering(true);
|
|
|
|
_inputHandler = DelegateInputHandler.fixedOrbit(_thermionViewer!);
|
|
await _load(_droneUri);
|
|
|
|
setState(() {});
|
|
});
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
if (_thermionViewer == null) {
|
|
return Container();
|
|
}
|
|
return Stack(children: [
|
|
Positioned.fill(
|
|
child: ThermionListenerWidget(
|
|
inputHandler: _inputHandler,
|
|
child: ThermionWidget(
|
|
viewer: _thermionViewer!,
|
|
))),
|
|
Card(
|
|
child: Padding(
|
|
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
|
|
child: Row(children: [
|
|
const Text("Asset: "),
|
|
DropdownButton<String?>(
|
|
value: _loaded,
|
|
items: [_droneUri, _cubeUri, null]
|
|
.map((uri) => DropdownMenuItem<String?>(
|
|
value: uri,
|
|
child: Text(
|
|
uri ?? "None",
|
|
style: const TextStyle(fontSize: 12),
|
|
)))
|
|
.toList(),
|
|
onChanged: _load),
|
|
const Text("Animation: "),
|
|
DropdownButton<String>(
|
|
value: selectedGltfAnimation == -1
|
|
? null
|
|
: gltfAnimations[selectedGltfAnimation],
|
|
items: gltfAnimations
|
|
.map((animation) => DropdownMenuItem<String>(
|
|
value: animation,
|
|
child: Text(
|
|
animation,
|
|
style: const TextStyle(fontSize: 12),
|
|
)))
|
|
.toList(),
|
|
onChanged: (value) {
|
|
if (value == null) {
|
|
selectedGltfAnimation = -1;
|
|
} else {
|
|
selectedGltfAnimation = gltfAnimations.indexOf(value);
|
|
}
|
|
}),
|
|
IconButton(
|
|
onPressed: _playGltfAnimation,
|
|
icon: const Icon(Icons.play_arrow)),
|
|
IconButton(
|
|
onPressed: _stopGltfAnimation, icon: const Icon(Icons.stop))
|
|
]))),
|
|
]);
|
|
}
|
|
}
|