From b76c95d25e05f64011a32d0fef4b1603c53d9939 Mon Sep 17 00:00:00 2001 From: Luan Nico Date: Sun, 15 Dec 2024 16:37:01 -0500 Subject: [PATCH] feat: Add Line3D mesh component [flame_3d] --- .../flame_3d/lib/src/components/line_3d.dart | 85 +++++++++++++++++++ .../test/components/line_3d_test.dart | 22 +++++ 2 files changed, 107 insertions(+) create mode 100644 packages/flame_3d/lib/src/components/line_3d.dart create mode 100644 packages/flame_3d/test/components/line_3d_test.dart diff --git a/packages/flame_3d/lib/src/components/line_3d.dart b/packages/flame_3d/lib/src/components/line_3d.dart new file mode 100644 index 00000000000..eb1c6a92d11 --- /dev/null +++ b/packages/flame_3d/lib/src/components/line_3d.dart @@ -0,0 +1,85 @@ +import 'dart:math' as math; +import 'dart:ui'; + +import 'package:flame_3d/components.dart'; +import 'package:flame_3d/game.dart'; +import 'package:flame_3d/resources.dart'; + +class Line3D extends MeshComponent { + Line3D._({ + required double radius, + required double height, + required Color color, + }) : super( + mesh: CylinderMesh( + radius: radius, + height: height, + material: SpatialMaterial()..albedoColor = color, + ), + ); + + factory Line3D.generate({ + required Vector3 start, + required Vector3 end, + required Color color, + double radius = 0.01, + }) { + final line = Line3D._( + radius: radius, + height: start.distanceTo(end), + color: color, + ); + line.transform.setFrom(_calculateTransform(start, end)); + return line; + } + + static Transform3D _calculateTransform( + Vector3 start, + Vector3 end, + ) { + final direction = end - start; + final length = direction.length; + + final bottomCenter = Vector3(0, -length / 2, 0); + final topCenter = Vector3(0, length / 2, 0); + + final translation = start - bottomCenter; + + final rotation = _calculateRotationMatrix( + bottomCenter: translation + bottomCenter, + topCenter: translation + topCenter, + target: end, + ); + + final translationMatrix = Matrix4.translation(translation); + final rotationMatrix = _rotateAroundPoint(rotation, start); + final transform = rotationMatrix.multiplied(translationMatrix); + + return Transform3D.fromMatrix4(transform); + } + + static Matrix4 _calculateRotationMatrix({ + required Vector3 bottomCenter, + required Vector3 topCenter, + required Vector3 target, + }) { + final origin = (topCenter - bottomCenter).normalized(); + final dest = (target - bottomCenter).normalized(); + + final normal = origin.cross(dest); + if (normal.length == 0) { + return Matrix4.identity(); + } + + final dotProduct = origin.dot(dest); + final angle = math.acos(dotProduct); + + return Matrix4.identity()..rotate(normal, angle); + } + + static Matrix4 _rotateAroundPoint(Matrix4 rotation, Vector3 point) { + return Matrix4.translation(point) + .multiplied(rotation) + .multiplied(Matrix4.translation(-point)); + } +} diff --git a/packages/flame_3d/test/components/line_3d_test.dart b/packages/flame_3d/test/components/line_3d_test.dart new file mode 100644 index 00000000000..b516f536d2d --- /dev/null +++ b/packages/flame_3d/test/components/line_3d_test.dart @@ -0,0 +1,22 @@ +import 'dart:ui'; + +import 'package:flame_3d/core.dart'; +import 'package:flame_3d/src/components/line_3d.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('Line 3D Mesh Component', () { + test('can create line', () { + final line = Line3D.generate( + start: Vector3(0, -1, 0), + end: Vector3(0, 1, 0), + color: const Color(0xFFFFFFFF), + ); + + expect( + line.transform.transformMatrix, + equals(Matrix4.identity()), + ); + }); + }); +}