1
- import 'dart:collection' ;
2
1
import 'dart:typed_data' ;
3
2
import 'dart:ui' ;
4
3
@@ -10,24 +9,48 @@ import 'package:flutter_gpu/gpu.dart' as gpu;
10
9
/// {@template shader}
11
10
///
12
11
/// {@endtemplate}
13
- class Shader extends Resource <gpu.Shader > {
12
+ class ShaderResource extends Resource <gpu.Shader > {
13
+ final Shader shader;
14
+
14
15
/// {@macro shader}
15
- Shader (
16
+ ShaderResource (
16
17
super .resource, {
18
+ required String name,
17
19
List <UniformSlot > slots = const [],
18
- }) : _slots = slots,
19
- _instances = {} {
20
+ }) : shader = Shader (name: name, slots: slots) {
20
21
for (final slot in slots) {
21
22
slot.resource = resource.getUniformSlot (slot.name);
22
23
}
23
24
}
24
25
25
- final List <UniformSlot > _slots;
26
+ factory ShaderResource .create ({
27
+ required String name,
28
+ required List <UniformSlot > slots,
29
+ }) {
30
+ final shader = _library[name];
31
+ if (shader == null ) {
32
+ throw StateError ('Shader "$name " not found in library' );
33
+ }
34
+ return ShaderResource (shader, name: name, slots: slots);
35
+ }
36
+
37
+ static final _library = gpu.ShaderLibrary .fromAsset (
38
+ 'packages/flame_3d/assets/shaders/spatial_material.shaderbundle' ,
39
+ )! ;
40
+ }
26
41
27
- final Map <String , UniformInstance > _instances;
42
+ class Shader {
43
+ final String name;
44
+ final List <UniformSlot > slots;
45
+ final Map <String , UniformInstance > instances = {};
46
+
47
+ Shader ({
48
+ required this .name,
49
+ required this .slots,
50
+ });
28
51
29
52
/// Set a [Texture] at the given [key] on the buffer.
30
- void setTexture (String key, Texture texture) => _setSampler (key, texture);
53
+ void setTexture (String key, Texture texture) => _setTypedValue (key, texture);
31
54
32
55
/// Set a [Vector2] at the given [key] on the buffer.
33
56
void setVector2 (String key, Vector2 vector) => _setValue (key, vector.storage);
@@ -45,7 +68,7 @@ class Shader extends Resource<gpu.Shader> {
45
68
46
69
/// Set a [double] at the given [key] on the buffer.
47
70
void setFloat (String key, double value) {
48
- _setValue (key, [ value] );
71
+ _setValue (key, _encodeFloat32 ( value) );
49
72
}
50
73
51
74
/// Set a [Matrix2] at the given [key] on the buffer.
@@ -60,50 +83,63 @@ class Shader extends Resource<gpu.Shader> {
60
83
void setColor (String key, Color color) => _setValue (key, color.storage);
61
84
62
85
void bind (GraphicsDevice device) {
63
- for (final slot in _slots ) {
64
- _instances [slot.name]? .bind (device);
86
+ for (final slot in slots ) {
87
+ instances [slot.name]? .bind (device);
65
88
}
66
89
}
67
90
68
91
/// Set the [data] to the [UniformSlot] identified by [key] .
69
- void _setValue (String key, List <double > data) {
70
- final (uniform, field) = _getInstance <UniformValue >(key);
71
- uniform[field! ] = data;
92
+ void _setValue (String key, Float32List data) {
93
+ _setTypedValue (key, data.buffer);
72
94
}
73
95
74
- void _setSampler (String key, Texture data) {
75
- final (uniform, _) = _getInstance <UniformSampler >(key);
76
- uniform.resource = data;
96
+ List <String ?> parseKey (String key) {
97
+ // examples: albedoTexture, Light[2].position, or Foo.bar
98
+ final regex = RegExp (r'^(\w+)(?:\[(\d+)\])?(?:\.(\w+))?$' );
99
+ return regex.firstMatch (key)? .groups ([1 , 2 , 3 ]) ?? [];
77
100
}
78
101
79
102
/// Get the slot for the [key] , it only calculates it once for every unique
80
103
/// [key] .
81
- (T , String ? ) _getInstance <T extends UniformInstance >(String key) {
82
- final keys = key.split ('.' );
83
-
84
- // Check if we already have a uniform instance created.
85
- if (! _instances.containsKey (keys.first)) {
86
- // If the slot or it's property isn't mapped in the uniform it will be
87
- // enforced.
88
- final slot = _slots.firstWhere (
89
- (e) => e.name == keys.first,
90
- orElse: () => throw StateError ('Uniform "$key " is unmapped' ),
91
- );
104
+ void _setTypedValue <K , T >(String key, T value) {
105
+ final groups = parseKey (key);
92
106
93
- final instance = slot.create ();
94
- if (instance is UniformValue &&
95
- keys.length > 1 &&
96
- ! slot.fields.contains (keys[1 ])) {
97
- throw StateError ('Field "${keys [1 ]}" is unmapped for "${keys .first }"' );
98
- }
107
+ final object = groups[0 ]; // e.g. Light, albedoTexture
108
+ final idx = _maybeParseInt (groups[1 ]); // e.g. 2 (optional)
109
+ final field = groups[2 ]; // e.g. position (optional)
99
110
100
- _instances[slot.name] = instance;
111
+ if (object == null ) {
112
+ throw StateError ('Uniform "$key " is missing an object' );
101
113
}
102
114
103
- return (_instances[keys.first], keys.elementAtOrNull (1 )) as (T , String ? );
115
+ final instance = instances.putIfAbsent (object, () {
116
+ final slot = slots.firstWhere (
117
+ (e) => e.name == object,
118
+ orElse: () => throw StateError ('Uniform "$object " is unmapped' ),
119
+ );
120
+ return slot.create ();
121
+ }) as UniformInstance <K , T >;
122
+
123
+ final k = instance.makeKey (idx, field);
124
+ instance.set (k, value);
104
125
}
105
126
106
127
static Float32List _encodeUint32 (int value, Endian endian) {
107
128
return (ByteData (16 )..setUint32 (0 , value, endian)).buffer.asFloat32List ();
108
129
}
130
+
131
+ static Float32List _encodeFloat32 (double value) {
132
+ return Float32List .fromList ([value]);
133
+ }
134
+
135
+ static int ? _maybeParseInt (String ? value) {
136
+ if (value == null ) {
137
+ return null ;
138
+ }
139
+ return int .parse (value);
140
+ }
141
+
142
+ ShaderResource compile () {
143
+ return ShaderResource .create (name: name, slots: slots);
144
+ }
109
145
}
0 commit comments