Skip to content
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
1 change: 1 addition & 0 deletions .github/.cspell/people_usernames.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ tavian # tavianator.com
videon # github.com/markvideon
wolfenrain # github.com/wolfenrain
xaha # github.com/xvrh
luan # github.com/luanpotter
Binary file modified packages/flame_3d/assets/shaders/spatial_material.shaderbundle
Binary file not shown.
Binary file not shown.
37 changes: 36 additions & 1 deletion packages/flame_3d/example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,43 @@ class ExampleGame3D extends FlameGame<World3D>
@override
FutureOr<void> onLoad() async {
world.addAll([
LightComponent.ambient(
intensity: 1.0,
),
RotatingLight(),

LightComponent.point(
position: Vector3(0, 0.1, 0),
color: const Color(0xFFFF00FF),
),
MeshComponent(
mesh: SphereMesh(
radius: 0.05,
material: SpatialMaterial(
albedoTexture: ColorTexture(
const Color(0xFFFF00FF),
),
),
),
position: Vector3(0, 0.1, 0),
),

LightComponent.point(
position: Vector3(-2, 3, 2),
color: const Color(0xFFFF2255),
),
MeshComponent(
mesh: SphereMesh(
radius: 0.05,
material: SpatialMaterial(
albedoTexture: ColorTexture(
const Color(0xFFFF2255),
),
),
),
position: Vector3(-2, 4, 2),
),

// Add a player box
PlayerBox(),

Expand All @@ -50,7 +85,7 @@ class ExampleGame3D extends FlameGame<World3D>
mesh: SphereMesh(
radius: 1,
material: SpatialMaterial(
albedoTexture: ColorTexture(Colors.purple),
albedoTexture: ColorTexture(Colors.green),
),
),
),
Expand Down
5 changes: 4 additions & 1 deletion packages/flame_3d/example/lib/rotating_light.dart
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import 'dart:math';
import 'dart:ui';

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

class RotatingLight extends LightComponent {
RotatingLight()
: super.spot(
: super.point(
position: Vector3.zero(),
color: const Color(0xFF00FF00),
intensity: 20.0,
);

@override
Expand Down
3 changes: 2 additions & 1 deletion packages/flame_3d/lib/src/camera/world_3d.dart
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,9 @@ class World3D extends flame.World with flame.HasGameReference {
image.dispose();
}

// TODO(luan): consider making this a fixed-size array later
void _prepareDevice() {
Comment thread
luanpotter marked this conversation as resolved.
device.lights = lights;
device.lightingInfo.lights = lights;
Comment thread
luanpotter marked this conversation as resolved.
}

// TODO(wolfenrain): this is only here for testing purposes
Expand Down
21 changes: 19 additions & 2 deletions packages/flame_3d/lib/src/components/light_component.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'dart:ui';

import 'package:flame_3d/camera.dart';
import 'package:flame_3d/components.dart';
import 'package:flame_3d/game.dart';
Expand All @@ -10,13 +12,28 @@ class LightComponent extends Component3D {
super.position,
});

LightComponent.spot({
LightComponent.point({
Vector3? position,
Color color = const Color(0xFFFFFFFF),
double intensity = 1.0,
}) : this(
source: SpotLight(),
source: PointLight(
color: color,
intensity: intensity,
),
position: position,
);

LightComponent.ambient({
Color color = const Color(0xFFFFFFFF),
double intensity = 0.2,
}) : this(
source: AmbientLight(
color: color,
intensity: intensity,
),
);

final LightSource source;

late final Light _light = Light(
Expand Down
8 changes: 6 additions & 2 deletions packages/flame_3d/lib/src/extensions/color.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ import 'dart:ui';

extension ColorExtension on Color {
/// Returns a Float32List that represents the color as a vector.
Float32List get storage =>
Float32List.fromList([red / 255, green / 255, blue / 255, opacity]);
Float32List get storage => Float32List.fromList([
opacity,
red.toDouble() / 255,
green.toDouble() / 255,
blue.toDouble() / 255,
]);
}
2 changes: 1 addition & 1 deletion packages/flame_3d/lib/src/graphics/graphics_device.dart
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ class GraphicsDevice {

/// Must be set by the rendering pipeline before elements are bound.
/// Can be accessed by elements in their bind method.
Iterable<Light> lights = [];
final LightingInfo lightingInfo = LightingInfo();
Comment thread
luanpotter marked this conversation as resolved.

/// Begin a new rendering batch.
///
Expand Down
4 changes: 3 additions & 1 deletion packages/flame_3d/lib/src/resources/light.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
export 'light/ambient_light.dart';
export 'light/light.dart';
export 'light/light_source.dart';
export 'light/spot_light.dart';
export 'light/lighting_info.dart';
export 'light/point_light.dart';
15 changes: 15 additions & 0 deletions packages/flame_3d/lib/src/resources/light/ambient_light.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import 'dart:ui' show Color;

import 'package:flame_3d/resources.dart';

class AmbientLight extends LightSource {
AmbientLight({
super.color = const Color(0xFFFFFFFF),
super.intensity = 0.2,
});

void apply(Shader shader) {
shader.setColor('AmbientLight.color', color);
shader.setFloat('AmbientLight.intensity', intensity);
}
}
10 changes: 4 additions & 6 deletions packages/flame_3d/lib/src/resources/light/light.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,9 @@ class Light extends Resource<void> {
required this.source,
}) : super(null);

void apply(Shader shader) {
shader.setVector3('Light.position', transform.position);
// apply additional parameters
source.apply(shader);
void apply(int index, Shader shader) {
shader.setVector3('Light$index.position', transform.position);
shader.setColor('Light$index.color', source.color);
shader.setFloat('Light$index.intensity', source.intensity);
}

static UniformSlot shaderSlot = UniformSlot.value('Light', {'position'});
}
14 changes: 11 additions & 3 deletions packages/flame_3d/lib/src/resources/light/light_source.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
import 'dart:ui' show Color;

import 'package:flame_3d/resources.dart';

/// Describes the properties of a light source.
/// There are three types of light sources: directional, point, and spot.
/// Currently only [SpotLight] is implemented.
/// There are three types of light sources: point, directional, and spot.
/// Currently only [PointLight] is implemented.
abstract class LightSource {
void apply(Shader shader);
final Color color;
final double intensity;

LightSource({
required this.color,
required this.intensity,
});
}
48 changes: 48 additions & 0 deletions packages/flame_3d/lib/src/resources/light/lighting_info.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import 'package:flame_3d/resources.dart';

class LightingInfo {
Iterable<Light> lights = [];
Comment thread
luanpotter marked this conversation as resolved.

void apply(Shader shader) {
_applyAmbientLight(shader);
_applyPointLights(shader);
}

void _applyAmbientLight(Shader shader) {
final ambient = _extractAmbientLight(lights);
ambient.apply(shader);
}

void _applyPointLights(Shader shader) {
final pointLights = lights.where((e) => e.source is PointLight);
final numLights = pointLights.length;
if (numLights > 3) {
// temporary, until we support dynamic arrays
throw Exception('At most 3 point lights are allowed');
}

shader.setUint('LightsInfo.numLights', numLights);
for (final (idx, light) in pointLights.indexed) {
light.apply(idx, shader);
}
}

AmbientLight _extractAmbientLight(Iterable<Light> lights) {
final ambient = lights.where((e) => e.source is AmbientLight);
if (ambient.isEmpty) {
return AmbientLight();
}
if (ambient.length > 1) {
throw Exception('At most one ambient light is allowed');
}
return ambient.first.source as AmbientLight;
}

static List<UniformSlot> shaderSlots = [
UniformSlot.value('AmbientLight', {'color', 'intensity'}),
UniformSlot.value('LightsInfo', {'numLights'}),
UniformSlot.value('Light0', {'position', 'color', 'intensity'}),
UniformSlot.value('Light1', {'position', 'color', 'intensity'}),
UniformSlot.value('Light2', {'position', 'color', 'intensity'}),
];
}
9 changes: 9 additions & 0 deletions packages/flame_3d/lib/src/resources/light/point_light.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import 'package:flame_3d/resources.dart';

/// A point light that emits light in all directions equally.
class PointLight extends LightSource {
PointLight({
required super.color,
required super.intensity,
});
}
11 changes: 0 additions & 11 deletions packages/flame_3d/lib/src/resources/light/spot_light.dart

This file was deleted.

17 changes: 4 additions & 13 deletions packages/flame_3d/lib/src/resources/material/spatial_material.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,8 @@ class SpatialMaterial extends Material {
SpatialMaterial({
Texture? albedoTexture,
Color albedoColor = const Color(0xFFFFFFFF),
this.metallic = 0,
this.metallicSpecular = 0.5,
this.roughness = 1.0,
this.metallic = 0.8,
this.roughness = 0.6,
}) : albedoTexture = albedoTexture ?? Texture.standard,
super(
vertexShader: Shader(
Expand All @@ -27,10 +26,9 @@ class SpatialMaterial extends Material {
UniformSlot.value('Material', {
'albedoColor',
'metallic',
'metallicSpecular',
'roughness',
}),
Light.shaderSlot,
...LightingInfo.shaderSlots,
UniformSlot.value('Camera', {'position'}),
],
),
Expand All @@ -53,8 +51,6 @@ class SpatialMaterial extends Material {

double metallic;

double metallicSpecular;

double roughness;

@override
Expand All @@ -77,7 +73,6 @@ class SpatialMaterial extends Material {
..setTexture('albedoTexture', albedoTexture)
..setVector3('Material.albedoColor', _albedoCache)
..setFloat('Material.metallic', metallic)
..setFloat('Material.metallicSpecular', metallicSpecular)
..setFloat('Material.roughness', roughness);
}

Expand All @@ -88,11 +83,7 @@ class SpatialMaterial extends Material {
}

void _applyLights(GraphicsDevice device) {
final light = device.lights.firstOrNull;
if (light == null) {
return;
}
light.apply(fragmentShader);
device.lightingInfo.apply(fragmentShader);
}

static final _library = gpu.ShaderLibrary.fromAsset(
Expand Down
5 changes: 2 additions & 3 deletions packages/flame_3d/lib/src/resources/mesh/vertex.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,8 @@ class Vertex {
_storage = Float32List.fromList([
...position.storage, // 1, 2, 3
...texCoord.storage, // 4, 5
...color.storage, // 6,7,8
// TODO(wolfenrain): fix normals not working properly
...(normal ?? Vector3.zero()).storage, // 9, 10, 11
...color.storage, // 6, 7, 8, 9
...(normal ?? Vector3.zero()).storage, // 10, 11, 12
]);

Float32List get storage => _storage;
Expand Down
17 changes: 16 additions & 1 deletion packages/flame_3d/lib/src/resources/shader/shader.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import 'dart:collection';
import 'dart:typed_data';
import 'dart:ui';

import 'package:flame_3d/game.dart';
import 'package:flame_3d/graphics.dart';
Expand Down Expand Up @@ -36,8 +38,15 @@ class Shader extends Resource<gpu.Shader> {
/// Set a [Vector4] at the given [key] on the buffer.
void setVector4(String key, Vector4 vector) => _setValue(key, vector.storage);

/// Set an [int] (encoded as uint) at the given [key] on the buffer.
void setUint(String key, int value) {
_setValue(key, _encodeUint32(value, Endian.little));
}

/// Set a [double] at the given [key] on the buffer.
void setFloat(String key, double value) => _setValue(key, [value]);
void setFloat(String key, double value) {
_setValue(key, [value]);
}

/// Set a [Matrix2] at the given [key] on the buffer.
void setMatrix2(String key, Matrix2 matrix) => _setValue(key, matrix.storage);
Expand All @@ -48,6 +57,8 @@ class Shader extends Resource<gpu.Shader> {
/// Set a [Matrix4] at the given [key] on the buffer.
void setMatrix4(String key, Matrix4 matrix) => _setValue(key, matrix.storage);

void setColor(String key, Color color) => _setValue(key, color.storage);

void bind(GraphicsDevice device) {
for (final slot in _slots) {
_instances[slot.name]?.bind(device);
Expand Down Expand Up @@ -91,4 +102,8 @@ class Shader extends Resource<gpu.Shader> {

return (_instances[keys.first], keys.elementAtOrNull(1)) as (T, String?);
}

static Float32List _encodeUint32(int value, Endian endian) {
return (ByteData(16)..setUint32(0, value, endian)).buffer.asFloat32List();
}
}
Loading