Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(flame_3d): Make shader api more useful #3085

Merged
merged 3 commits into from
Jul 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/.cspell/gamedev_dictionary.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ goldens # test files used as reference for Golden Tests
hitbox # the collision box around objects for the purposes of collision detection
hitboxes # plural of hitbox
ints # short for integers
impellerc # Flutter's impeller compiler
jank # stutter or inconsistent gap or timing
lerp # short for linear interpolation
LTRB # left top right bottom
Expand All @@ -49,15 +50,18 @@ respawn # when the player character dies and is brought back after some time and
retarget # to direct (something) toward a different target
RGBA # red green blue alpha
RGBO # red green blue opacity
BGRA # blue green red alpha
rrect # rounded rect
scos # cosine of a rotation multiplied by the scale factor
shaderbundle # a file extension used to bundle shaders for GLSL
spritesheet # a single image packing multiple sprites, normally in a grid
ssin # sine of a rotation multiplied by the scale factor
stylesheet # name of a CSS style file
subfolders # plural of subfolders
sublist # any sub-set of elements of a given list
sublists # plural of sublist
subrange # a range entirely contained on a given range
struct # a type of data model in programming that aggregates fields
SVGs # plural of SVG
tileset # image with a collection of tiles. in games, tiles are small square sprites laid out in a grid to form the game map
tilesets # plural of tileset
Expand Down
1 change: 1 addition & 0 deletions .github/.cspell/people_usernames.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# specific people's names and/or usernames
akida # github.com/akida
bdero # github.com/bdero
bluefireteam # github.com/bluefireteam
erayzesen # erayzesen.itch.io
erickzanardo # github.com/erickzanardo
Expand Down
Binary file not shown.
Binary file modified packages/flame_3d/assets/shaders/standard_material.shaderbundle
Binary file not shown.
22 changes: 16 additions & 6 deletions packages/flame_3d/bin/build_shaders.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,28 @@ import 'dart:io';
///
/// Note: this script should be run from the root of the package:
/// packages/flame_3d
void main() async {
void main(List<String> arguments) async {
final root = Directory.current;

final assets = Directory.fromUri(root.uri.resolve('assets/shaders'));
final shaders = Directory.fromUri(root.uri.resolve('shaders'));

await compute(assets, shaders);
if (arguments.contains('watch')) {
stdout.writeln('Running in watch mode');
shaders.watch(recursive: true).listen((event) {
compute(assets, shaders);
});
}
}

Future<void> compute(Directory assets, Directory shaders) async {
// Delete all the bundled shaders so we can replace them with new ones.
if (assets.existsSync()) {
assets.deleteSync(recursive: true);
}
// Create if not exists.
assets.createSync(recursive: true);

// Directory where our unbundled shaders are stored.
final shaders = Directory.fromUri(root.uri.resolve('shaders'));
if (!shaders.existsSync()) {
return stderr.writeln('Missing shader directory');
}
Expand All @@ -39,14 +48,15 @@ void main() async {
final bundle = {
'TextureFragment': {
'type': 'fragment',
'file': '${root.path}/shaders/$name.frag',
'file': '${shaders.path}/$name.frag',
},
'TextureVertex': {
'type': 'vertex',
'file': '${root.path}/shaders/$name.vert',
'file': '${shaders.path}/$name.vert',
},
};

stdout.writeln('Computing shader "$name"');
final result = await Process.run(impellerC, [
'--sl=${assets.path}/$name.shaderbundle',
'--shader-bundle=${jsonEncode(bundle)}',
Expand Down
27 changes: 6 additions & 21 deletions packages/flame_3d/example/.metadata
Original file line number Diff line number Diff line change
Expand Up @@ -4,35 +4,20 @@
# This file should be version controlled and should not be manually edited.

version:
revision: "1b197762c51e993cb77d7fafe9729ef2506e2bf7"
channel: "beta"
revision: "bcdd1b2c481bca0647beff690238efaae68ca5ac"
channel: "[user-branch]"

project_type: app

# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: 1b197762c51e993cb77d7fafe9729ef2506e2bf7
base_revision: 1b197762c51e993cb77d7fafe9729ef2506e2bf7
- platform: android
create_revision: 1b197762c51e993cb77d7fafe9729ef2506e2bf7
base_revision: 1b197762c51e993cb77d7fafe9729ef2506e2bf7
create_revision: bcdd1b2c481bca0647beff690238efaae68ca5ac
base_revision: bcdd1b2c481bca0647beff690238efaae68ca5ac
- platform: ios
create_revision: 1b197762c51e993cb77d7fafe9729ef2506e2bf7
base_revision: 1b197762c51e993cb77d7fafe9729ef2506e2bf7
- platform: linux
create_revision: 1b197762c51e993cb77d7fafe9729ef2506e2bf7
base_revision: 1b197762c51e993cb77d7fafe9729ef2506e2bf7
- platform: macos
create_revision: 1b197762c51e993cb77d7fafe9729ef2506e2bf7
base_revision: 1b197762c51e993cb77d7fafe9729ef2506e2bf7
- platform: web
create_revision: 1b197762c51e993cb77d7fafe9729ef2506e2bf7
base_revision: 1b197762c51e993cb77d7fafe9729ef2506e2bf7
- platform: windows
create_revision: 1b197762c51e993cb77d7fafe9729ef2506e2bf7
base_revision: 1b197762c51e993cb77d7fafe9729ef2506e2bf7
create_revision: bcdd1b2c481bca0647beff690238efaae68ca5ac
base_revision: bcdd1b2c481bca0647beff690238efaae68ca5ac

# User provided section

Expand Down
2 changes: 1 addition & 1 deletion packages/flame_3d/example/lib/crate.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class Crate extends MeshComponent {
FutureOr<void> onLoad() async {
final crateTexture = await Flame.images.loadTexture('crate.jpg');
mesh.updateSurfaces((surfaces) {
surfaces[0].material = StandardMaterial(
surfaces[0].material = SpatialMaterial(
albedoTexture: crateTexture,
);
});
Expand Down
16 changes: 9 additions & 7 deletions packages/flame_3d/example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'dart:math';
import 'package:example/crate.dart';
import 'package:example/keyboard_controlled_camera.dart';
import 'package:example/player_box.dart';
import 'package:example/rotating_light.dart';
import 'package:example/simple_hud.dart';
import 'package:flame/events.dart';
import 'package:flame/extensions.dart' as v64 show Vector2;
Expand Down Expand Up @@ -35,6 +36,8 @@ class ExampleGame3D extends FlameGame<World3D>
@override
FutureOr<void> onLoad() async {
world.addAll([
RotatingLight(),

// Add a player box
PlayerBox(),

Expand All @@ -46,7 +49,7 @@ class ExampleGame3D extends FlameGame<World3D>
position: Vector3(5, 5, 5),
mesh: SphereMesh(
radius: 1,
material: StandardMaterial(
material: SpatialMaterial(
albedoTexture: ColorTexture(Colors.purple),
),
),
Expand All @@ -56,7 +59,7 @@ class ExampleGame3D extends FlameGame<World3D>
MeshComponent(
mesh: PlaneMesh(
size: Vector2(32, 32),
material: StandardMaterial(albedoTexture: ColorTexture(Colors.grey)),
material: SpatialMaterial(albedoTexture: ColorTexture(Colors.grey)),
),
),

Expand All @@ -65,8 +68,7 @@ class ExampleGame3D extends FlameGame<World3D>
position: Vector3(16.5, 2.5, 0),
mesh: CuboidMesh(
size: Vector3(1, 5, 32),
material:
StandardMaterial(albedoTexture: ColorTexture(Colors.yellow)),
material: SpatialMaterial(albedoTexture: ColorTexture(Colors.yellow)),
),
),

Expand All @@ -75,7 +77,7 @@ class ExampleGame3D extends FlameGame<World3D>
position: Vector3(0, 2.5, 16.5),
mesh: CuboidMesh(
size: Vector3(32, 5, 1),
material: StandardMaterial(albedoTexture: ColorTexture(Colors.blue)),
material: SpatialMaterial(albedoTexture: ColorTexture(Colors.blue)),
),
),

Expand All @@ -84,7 +86,7 @@ class ExampleGame3D extends FlameGame<World3D>
position: Vector3(0, 2.5, -16.5),
mesh: CuboidMesh(
size: Vector3(32, 5, 1),
material: StandardMaterial(albedoTexture: ColorTexture(Colors.lime)),
material: SpatialMaterial(albedoTexture: ColorTexture(Colors.lime)),
),
),
]);
Expand All @@ -98,7 +100,7 @@ class ExampleGame3D extends FlameGame<World3D>
position: Vector3(rnd.range(-15, 15), height / 2, rnd.range(-15, 15)),
mesh: CuboidMesh(
size: Vector3(1, height, 1),
material: StandardMaterial(
material: SpatialMaterial(
albedoTexture: ColorTexture(
Color.fromRGBO(rnd.iRange(20, 255), rnd.iRange(10, 55), 30, 1),
),
Expand Down
2 changes: 1 addition & 1 deletion packages/flame_3d/example/lib/player_box.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class PlayerBox extends MeshComponent with HasGameReference<ExampleGame3D> {
mesh: CuboidMesh(
size: Vector3.all(0.5),
material:
StandardMaterial(albedoTexture: ColorTexture(Colors.purple)),
SpatialMaterial(albedoTexture: ColorTexture(Colors.purple)),
),
);

Expand Down
20 changes: 20 additions & 0 deletions packages/flame_3d/example/lib/rotating_light.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import 'dart:math';

import 'package:flame_3d/components.dart';
import 'package:flame_3d/game.dart';

class RotatingLight extends LightComponent {
RotatingLight()
: super.spot(
position: Vector3.zero(),
);

@override
void update(double dt) {
const radius = 15;
final angle = DateTime.now().millisecondsSinceEpoch / 4000;
final x = cos(angle) * radius;
final z = sin(angle) * radius;
position.setValues(x, 10, z);
}
}
2 changes: 2 additions & 0 deletions packages/flame_3d/lib/components.dart
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
export 'src/components/component_3d.dart';
export 'src/components/light_component.dart';
export 'src/components/mesh_component.dart';
export 'src/components/object_3d.dart';
10 changes: 9 additions & 1 deletion packages/flame_3d/lib/core.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
export 'package:vector_math/vector_math.dart'
show Vector2, Vector3, Vector4, Matrix3, Matrix4, Quaternion, Aabb3;
show
Vector2,
Vector3,
Vector4,
Matrix2,
Matrix3,
Matrix4,
Quaternion,
Aabb3;

export 'extensions.dart';
2 changes: 2 additions & 0 deletions packages/flame_3d/lib/resources.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import 'package:flame/cache.dart';
import 'package:flame_3d/resources.dart';

export 'src/resources/light.dart';
export 'src/resources/material.dart';
export 'src/resources/mesh.dart';
export 'src/resources/resource.dart';
export 'src/resources/shader.dart';
export 'src/resources/texture.dart';

extension TextureCache on Images {
Expand Down
11 changes: 8 additions & 3 deletions packages/flame_3d/lib/src/camera/camera_component_3d.dart
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,19 @@ class CameraComponent3D extends CameraComponent {
..setAsPerspective(fovY, aspectRatio, distanceNear, distanceFar),
CameraProjection.orthographic => _projectionMatrix
..setAsOrthographic(fovY, aspectRatio, distanceNear, distanceFar)
}
..multiply(viewMatrix);
};
}

final Matrix4 _projectionMatrix = Matrix4.zero();

Matrix4 get viewProjectionMatrix => _viewProjectionMatrix
..setFrom(projectionMatrix)
..multiply(viewMatrix);
final Matrix4 _viewProjectionMatrix = Matrix4.zero();

final Frustum _frustum = Frustum();

Frustum get frustum => _frustum..setFromMatrix(_projectionMatrix);
Frustum get frustum => _frustum..setFromMatrix(viewProjectionMatrix);

void moveForward(double distance, {bool moveInWorldPlane = false}) {
final forward = this.forward..scale(distance);
Expand Down
52 changes: 40 additions & 12 deletions packages/flame_3d/lib/src/camera/world_3d.dart
Original file line number Diff line number Diff line change
@@ -1,48 +1,76 @@
import 'dart:ui';

import 'package:flame/components.dart' as flame;
import 'package:flame_3d/camera.dart';
import 'package:flame_3d/components.dart';
import 'package:flame_3d/graphics.dart';
import 'package:flame_3d/resources.dart';
import 'package:flutter/widgets.dart';
import 'package:meta/meta.dart';

/// {@template world_3d}
/// The root component for all 3D world elements.
///
/// The primary feature of this component is that it allows [Component3D]s to
/// render directly to a [GraphicsDevice] instead of the regular rendering.
/// {@endtemplate}
class World3D extends flame.World {
class World3D extends flame.World with flame.HasGameReference {
/// {@macro world_3d}
World3D({
super.children,
super.priority,
Color clearColor = const Color(0x00000000),
}) : graphics = GraphicsDevice(clearValue: clearColor);
}) : device = GraphicsDevice(clearValue: clearColor) {
children.register<LightComponent>();
}

/// The graphical device attached to this world.
final GraphicsDevice graphics;
@internal
final GraphicsDevice device;

Iterable<Light> get lights =>
children.query<LightComponent>().map((component) => component.light);

final _paint = Paint();

@internal
@override
void renderFromCamera(Canvas canvas) {
final camera = CameraComponent3D.currentCamera!;

final viewport = camera.viewport;
graphics.begin(
Size(viewport.virtualSize.x, viewport.virtualSize.y),
transformMatrix: camera.projectionMatrix,

final devicePixelRatio = MediaQuery.of(game.buildContext!).devicePixelRatio;
final size = Size(
viewport.virtualSize.x * devicePixelRatio,
viewport.virtualSize.y * devicePixelRatio,
);

device
// Set the view matrix
..view.setFrom(camera.viewMatrix)
// Set the projection matrix
..projection.setFrom(camera.projectionMatrix)
..begin(size);

culled = 0;

_prepareDevice();
// ignore: invalid_use_of_internal_member
super.renderFromCamera(canvas);

final image = graphics.end();
canvas.drawImage(image, (-viewport.virtualSize / 2).toOffset(), _paint);
final image = device.end();
canvas.drawImageRect(
image,
Offset.zero & size,
(-viewport.virtualSize / 2).toOffset() &
Size(viewport.virtualSize.x, viewport.virtualSize.y),
_paint,
);
image.dispose();
}

// TODO(wolfen): this is only here for testing purposes
void _prepareDevice() {
device.lights = lights;
}

// TODO(wolfenrain): this is only here for testing purposes
int culled = 0;
}
Loading
Loading