Skip to content

Commit

Permalink
feat(flame_3d): Make shader api more useful (#3085)
Browse files Browse the repository at this point in the history
Co-authored-by: Luan Nico <[email protected]>
  • Loading branch information
wolfenrain and luanpotter authored Jul 22, 2024
1 parent 3091b12 commit c9cf609
Show file tree
Hide file tree
Showing 50 changed files with 964 additions and 319 deletions.
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

0 comments on commit c9cf609

Please sign in to comment.