import 'dart:math' as math; import 'dart:typed_data'; import 'package:flutter_scene/scene.dart'; import 'package:vector_math/vector_math.dart' as vm; const cardWidth = 2.8571428571; const cardHeight = 4.0; const cardThickness = 0.0181405896; const cardCornerRadius = 0.1269841270; const _cornerSegments = 12; List _roundedPerimeter() { final points = []; final halfWidth = cardWidth / 2; final halfHeight = cardHeight / 2; final centers = [ vm.Vector2(halfWidth - cardCornerRadius, halfHeight - cardCornerRadius), vm.Vector2(-halfWidth + cardCornerRadius, halfHeight - cardCornerRadius), vm.Vector2(-halfWidth + cardCornerRadius, -halfHeight + cardCornerRadius), vm.Vector2(halfWidth - cardCornerRadius, -halfHeight + cardCornerRadius), ]; for (var corner = 0; corner < centers.length; corner++) { final startAngle = corner * math.pi / 2; for (var step = 0; step <= _cornerSegments; step++) { final angle = startAngle + step / _cornerSegments * math.pi / 2; points.add( centers[corner] + vm.Vector2(math.cos(angle), math.sin(angle)) * cardCornerRadius, ); } } return points; } MeshGeometry buildCardFaceGeometry({ required bool front, }) { final perimeter = _roundedPerimeter(); final z = front ? -cardThickness / 2 : cardThickness / 2; final positions = [0, 0, z]; final normals = [0, 0, front ? -1 : 1]; final texCoords = [0.5, 0.5]; for (final point in perimeter) { positions.addAll([point.x, point.y, z]); normals.addAll([0, 0, front ? -1 : 1]); final u = front ? point.x / cardWidth + 0.5 : 0.5 - point.x / cardWidth; texCoords.addAll([u, 0.5 - point.y / cardHeight]); } final indices = []; for (var index = 0; index < perimeter.length; index++) { final current = index + 1; final next = (index + 1) % perimeter.length + 1; if (front) { indices.addAll([0, next, current]); } else { indices.addAll([0, current, next]); } } return MeshGeometry.fromArrays( positions: Float32List.fromList(positions), normals: Float32List.fromList(normals), texCoords: Float32List.fromList(texCoords), indices: indices, ); } MeshGeometry buildCardEdgeGeometry() { final perimeter = _roundedPerimeter(); final positions = []; final normals = []; final indices = []; for (var index = 0; index < perimeter.length; index++) { final current = perimeter[index]; final next = perimeter[(index + 1) % perimeter.length]; final normal = (current + next).normalized(); final base = positions.length ~/ 3; positions.addAll([ current.x, current.y, -cardThickness / 2, current.x, current.y, cardThickness / 2, next.x, next.y, cardThickness / 2, next.x, next.y, -cardThickness / 2, ]); for (var vertex = 0; vertex < 4; vertex++) { normals.addAll([normal.x, normal.y, 0]); } indices.addAll([ base, base + 2, base + 1, base, base + 3, base + 2, ]); } return MeshGeometry.fromArrays( positions: Float32List.fromList(positions), normals: Float32List.fromList(normals), indices: indices, ); }