support relative setPosition, add hardware keyboard listener + control, log FPS
This commit is contained in:
116
lib/entities/entity_transform_controller.dart
Normal file
116
lib/entities/entity_transform_controller.dart
Normal file
@@ -0,0 +1,116 @@
|
||||
import 'dart:async';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flutter_filament/filament_controller.dart';
|
||||
import 'package:vector_math/vector_math_64.dart' as v;
|
||||
|
||||
class EntityTransformController {
|
||||
final FilamentController controller;
|
||||
final FilamentEntity _entity;
|
||||
|
||||
late Timer _ticker;
|
||||
|
||||
double translationSpeed;
|
||||
double rotationRadsPerSecond;
|
||||
|
||||
bool _forward = false;
|
||||
bool _strafeLeft = false;
|
||||
bool _strafeRight = false;
|
||||
bool _back = false;
|
||||
bool _rotateLeft = false;
|
||||
bool _rotateRight = false;
|
||||
|
||||
EntityTransformController(this.controller, this._entity,
|
||||
{this.translationSpeed = 1, this.rotationRadsPerSecond = pi / 2}) {
|
||||
var translationSpeedPerTick = translationSpeed / (1000 / 16.667);
|
||||
_ticker = Timer.periodic(const Duration(milliseconds: 16), (timer) {
|
||||
_update(translationSpeedPerTick);
|
||||
});
|
||||
}
|
||||
|
||||
void _update(double translationSpeedPerTick) async {
|
||||
var _position = v.Vector3.zero();
|
||||
var _rotation = v.Quaternion.identity();
|
||||
bool requiresUpdate = false;
|
||||
if (_forward) {
|
||||
_position.add(v.Vector3(0, 0, -translationSpeedPerTick));
|
||||
requiresUpdate = true;
|
||||
}
|
||||
if (_back) {
|
||||
_position.add(v.Vector3(0, 0, translationSpeedPerTick));
|
||||
requiresUpdate = true;
|
||||
}
|
||||
if (_strafeLeft) {
|
||||
_position.add(v.Vector3(-translationSpeedPerTick, 0, 0));
|
||||
requiresUpdate = true;
|
||||
}
|
||||
if (_strafeRight) {
|
||||
_position.add(v.Vector3(translationSpeedPerTick, 0, 0));
|
||||
requiresUpdate = true;
|
||||
}
|
||||
|
||||
// todo - better to use pitch/yaw/roll
|
||||
if (_rotateLeft) {}
|
||||
if (_rotateRight) {}
|
||||
|
||||
if (requiresUpdate) {
|
||||
await controller.setPosition(
|
||||
_entity, _position.x, _position.y, _position.z,
|
||||
relative: true);
|
||||
}
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
_ticker.cancel();
|
||||
}
|
||||
|
||||
void forwardPressed() {
|
||||
print("forward");
|
||||
_forward = true;
|
||||
}
|
||||
|
||||
Timer? _forwardTimer;
|
||||
Timer? _backwardsTimer;
|
||||
Timer? _strafeLeftTimer;
|
||||
Timer? _strafeRightTimer;
|
||||
|
||||
void forwardReleased() async {
|
||||
_forwardTimer?.cancel();
|
||||
_forwardTimer = Timer(Duration(milliseconds: 50), () {
|
||||
_forward = false;
|
||||
});
|
||||
}
|
||||
|
||||
void backPressed() {
|
||||
_back = true;
|
||||
}
|
||||
|
||||
void backReleased() async {
|
||||
_backwardsTimer?.cancel();
|
||||
_backwardsTimer = Timer(Duration(milliseconds: 50), () {
|
||||
_back = false;
|
||||
});
|
||||
}
|
||||
|
||||
void strafeLeftPressed() {
|
||||
_strafeLeft = true;
|
||||
}
|
||||
|
||||
void strafeLeftReleased() async {
|
||||
_strafeLeftTimer?.cancel();
|
||||
_strafeLeftTimer = Timer(Duration(milliseconds: 50), () {
|
||||
_strafeLeft = false;
|
||||
});
|
||||
}
|
||||
|
||||
void strafeRightPressed() {
|
||||
_strafeRight = true;
|
||||
}
|
||||
|
||||
void strafeRightReleased() async {
|
||||
_strafeRightTimer?.cancel();
|
||||
_strafeRightTimer = Timer(Duration(milliseconds: 50), () {
|
||||
_strafeRight = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -450,7 +450,8 @@ abstract class FilamentController {
|
||||
///
|
||||
/// Sets the world space position for [entity] to the given coordinates.
|
||||
///
|
||||
Future setPosition(FilamentEntity entity, double x, double y, double z);
|
||||
Future setPosition(FilamentEntity entity, double x, double y, double z,
|
||||
{bool relative = false});
|
||||
|
||||
///
|
||||
/// Enable/disable postprocessing.
|
||||
@@ -523,4 +524,9 @@ abstract class FilamentController {
|
||||
/// Sets the output directory where recorded PNGs will be placed.
|
||||
///
|
||||
Future setRecordingOutputDirectory(String outputDirectory);
|
||||
|
||||
// Stream get keyboardFocusRequested;
|
||||
// void requestKeyboardFocus();
|
||||
|
||||
void control(FilamentEntity entity, {double? translationSpeed});
|
||||
}
|
||||
|
||||
@@ -6,11 +6,13 @@ import 'dart:developer' as dev;
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:ffi/ffi.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_filament/entities/entity_transform_controller.dart';
|
||||
|
||||
import 'package:flutter_filament/filament_controller.dart';
|
||||
|
||||
import 'package:flutter_filament/animations/animation_data.dart';
|
||||
import 'package:flutter_filament/generated_bindings.dart';
|
||||
import 'package:flutter_filament/hardware/hardware_keyboard_listener.dart';
|
||||
import 'package:flutter_filament/rendering_surface.dart';
|
||||
import 'package:vector_math/vector_math_64.dart';
|
||||
|
||||
@@ -1049,12 +1051,12 @@ class FilamentControllerFFI extends FilamentController {
|
||||
}
|
||||
|
||||
@override
|
||||
Future setPosition(
|
||||
FilamentEntity entity, double x, double y, double z) async {
|
||||
Future setPosition(FilamentEntity entity, double x, double y, double z,
|
||||
{bool relative = false}) async {
|
||||
if (_viewer == null) {
|
||||
throw Exception("No viewer available, ignoring");
|
||||
}
|
||||
set_position(_assetManager!, entity, x, y, z);
|
||||
set_position(_assetManager!, entity, x, y, z, relative);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -1321,4 +1323,12 @@ class FilamentControllerFFI extends FilamentController {
|
||||
set_recording_output_directory(_viewer!, pathPtr.cast<Char>());
|
||||
allocator.free(pathPtr);
|
||||
}
|
||||
|
||||
HardwareKeyboardListener? _keyboardListener;
|
||||
void control(FilamentEntity entity, {double? translationSpeed}) {
|
||||
_keyboardListener?.dispose();
|
||||
_keyboardListener = HardwareKeyboardListener(EntityTransformController(
|
||||
this, entity,
|
||||
translationSpeed: translationSpeed ?? 1.0));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -541,14 +541,20 @@ external void transform_to_unit_cube(
|
||||
);
|
||||
|
||||
@ffi.Native<
|
||||
ffi.Void Function(ffi.Pointer<ffi.Void>, EntityId, ffi.Float, ffi.Float,
|
||||
ffi.Float)>(symbol: 'set_position', assetId: 'flutter_filament_plugin')
|
||||
ffi.Void Function(
|
||||
ffi.Pointer<ffi.Void>,
|
||||
EntityId,
|
||||
ffi.Float,
|
||||
ffi.Float,
|
||||
ffi.Float,
|
||||
ffi.Bool)>(symbol: 'set_position', assetId: 'flutter_filament_plugin')
|
||||
external void set_position(
|
||||
ffi.Pointer<ffi.Void> assetManager,
|
||||
int asset,
|
||||
double x,
|
||||
double y,
|
||||
double z,
|
||||
bool relative,
|
||||
);
|
||||
|
||||
@ffi.Native<
|
||||
|
||||
54
lib/hardware/hardware_keyboard_listener.dart
Normal file
54
lib/hardware/hardware_keyboard_listener.dart
Normal file
@@ -0,0 +1,54 @@
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_filament/entities/entity_transform_controller.dart';
|
||||
import 'package:flutter_filament/filament_controller.dart';
|
||||
|
||||
class HardwareKeyboardListener {
|
||||
final EntityTransformController _controller;
|
||||
HardwareKeyboardListener(this._controller) {
|
||||
// Get the global handler.
|
||||
final KeyMessageHandler? existing =
|
||||
ServicesBinding.instance.keyEventManager.keyMessageHandler;
|
||||
// The handler is guaranteed non-null since
|
||||
// `FallbackKeyEventRegistrar.instance` is only called during
|
||||
// `Focus.onFocusChange`, at which time `ServicesBinding.instance` must
|
||||
// have been called somewhere.
|
||||
assert(existing != null);
|
||||
// Assign the global handler with a patched handler.
|
||||
ServicesBinding.instance.keyEventManager.keyMessageHandler = (keyMessage) {
|
||||
if (keyMessage.rawEvent == null) {
|
||||
return false;
|
||||
}
|
||||
var event = keyMessage.rawEvent!;
|
||||
switch (event.logicalKey) {
|
||||
case LogicalKeyboardKey.keyW:
|
||||
(event is RawKeyDownEvent)
|
||||
? _controller.forwardPressed()
|
||||
: _controller.forwardReleased();
|
||||
break;
|
||||
case LogicalKeyboardKey.keyA:
|
||||
event is RawKeyDownEvent
|
||||
? _controller.strafeLeftPressed()
|
||||
: _controller.strafeLeftReleased();
|
||||
break;
|
||||
case LogicalKeyboardKey.keyS:
|
||||
event is RawKeyDownEvent
|
||||
? _controller.backPressed()
|
||||
: _controller.backReleased();
|
||||
break;
|
||||
case LogicalKeyboardKey.keyD:
|
||||
event is RawKeyDownEvent
|
||||
? _controller.strafeRightPressed()
|
||||
: _controller.strafeRightReleased();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
ServicesBinding.instance.keyEventManager.keyMessageHandler = null;
|
||||
_controller.dispose();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user