1 module armos.graphics.material;
2 import armos.types;
3 import armos.math;
4 import armos.graphics;
5 
6 /++
7 材質を表すinterfaceです.
8 +/
9 interface Material{
10     public{
11         ///
12         Material begin();
13 
14         ///
15         Material end();
16         
17         ///
18         Material uniform(in string name, in int i);
19         
20         ///
21         Material uniform(in string name, in float f);
22 
23         ///
24         Material uniform(in string name, in Color c);
25         
26         ///
27         Material uniform(in string name, in Vector2f v);
28         
29         ///
30         Material uniform(in string name, in Vector3f v);
31         
32         ///
33         Material uniform(in string name, in Vector4f v);
34         
35         ///
36         ref Vector4f uniform(in string name);
37         
38         ///
39         Material texture(in string name, armos.graphics.Texture tex);
40 
41         ///
42         armos.graphics.Texture texture(in string name);
43 
44         ///
45         armos.graphics.Shader shader();
46         
47         ///
48         Material shader(armos.graphics.Shader s);
49         
50         ///
51         Material loadImage(in string pathInDataDir, in string name);
52     }//public
53 
54 }//interface Material
55 
56 ///
57 mixin template MaterialImpl(){
58     alias T = typeof(this);
59     import armos.math;
60     import armos.types:Color;
61     public{
62         ///
63         T begin(){
64             beginDefault;
65             return this;
66         }
67 
68         ///
69         T end(){
70             endDefault;
71             return this;
72         }
73         
74         ///
75         T uniform(in string Name, in int v){
76             _uniformsI[Name] = v;
77             return this;
78         }
79         
80         ///
81         T uniform(in string Name, in float v){
82             _uniformsF[Name] = v;
83             return this;
84         }
85         
86         ///
87         T uniform(in string Name, in Vector2f v){
88             _uniformsV2f[Name] = v;
89             return this;
90         }
91 
92         ///
93         T uniform(in string Name, in Vector3f v){
94             _uniformsV3f[Name] = v;
95             return this;
96         }
97         
98         ///
99         T uniform(in string Name, in Vector4f v){
100             _uniformsV4f[Name] = v;
101             return this;
102         }
103         
104         ///
105         T uniform(in string Name, in Color c){
106             import std.conv;
107             _uniformsV4f[Name] = Vector4f(c.r.to!float/c.limit, c.g.to!float/c.limit, c.b.to!float/c.limit, c.a.to!float/c.limit);
108             return this;
109         }
110 
111         ///
112         ref Vector4f uniform(in string name){
113             return _uniformsV4f[name];
114         }
115 
116         ///
117         T texture(in string name, armos.graphics.Texture tex){
118             _textures[name] = tex;
119             return this;
120         }
121 
122         ///
123         armos.graphics.Texture texture(in string name){return _textures[name];}
124 
125         ///
126         armos.graphics.Shader shader(){
127             return _shader;
128         }
129 
130         ///
131         T shader(armos.graphics.Shader s){
132             _shader = s;
133             return this;
134         }
135 
136         ///
137         T loadImage(in string pathInDataDir, in string name){
138             auto image = new armos.graphics.Image();
139             image.load(pathInDataDir);
140             texture(name, image.texture);
141             return this;
142         }
143     }//public
144 
145     private{
146         int[string] _uniformsI;
147         float[string] _uniformsF;
148         Vector2f[string] _uniformsV2f;
149         Vector3f[string] _uniformsV3f;
150         Vector4f[string] _uniformsV4f;
151         armos.graphics.Texture[string] _textures;
152         armos.graphics.Shader _shader;
153 
154         ///
155         T beginDefault(){
156             import armos.graphics:pushMaterialStack;
157             pushMaterialStack(this);
158             _shader.begin;
159             _textures.begin;
160             sendUniformsToShader;
161             return this;
162         }
163 
164         ///
165         T endDefault(){
166             _textures.end;
167             _shader.end;
168             
169             import armos.graphics:popMaterialStack;
170             popMaterialStack;
171             return this;
172         }
173 
174         T sendUniformsToShader(){
175             _uniformsF.sendTo(_shader);
176             _uniformsI.sendTo(_shader);
177             _uniformsV2f.sendTo(_shader);
178             _uniformsV3f.sendTo(_shader);
179             _uniformsV4f.sendTo(_shader);
180             _textures.sendTo(_shader);
181             return this;
182         }
183     }//private
184    
185 }
186 
187 void begin(armos.graphics.Texture[string] textures){
188     foreach (string key; textures.keys) {
189         auto texture = textures[key];
190         if(texture){
191             texture.begin;
192         }
193     }
194 }
195 
196 void end(armos.graphics.Texture[string] textures){
197     foreach_reverse (string key; textures.keys) {
198         auto texture = textures[key];
199         if(texture){
200             texture.end;
201         }
202     }
203 }
204 
205 void sendTo(U:E[K], E, K)(U uniform, armos.graphics.Shader shader){
206     foreach (int i, string key; uniform.keys) {
207         static if(is(E == armos.graphics.Texture)){
208             shader.uniformTexture(key, uniform[key], i);
209         }else{
210             shader.uniform(key, uniform[key]);
211         }
212     }
213 }
214 
215 ///
216 class DefaultMaterial : Material{
217     mixin MaterialImpl;
218     
219     ///
220     this(){
221         _shader = new armos.graphics.Shader;
222         _shader.loadSources(defaultVertexShaderSource, "", defaultFragmentShaderSource);
223     }
224 }//class DefaultMaterial
225 
226 /++
227 +/
228 class CustomShaderMaterial : Material{
229     mixin MaterialImpl;
230     static CustomShaderMaterial loadFiles(in string shaderName){
231         auto mat = new CustomShaderMaterial();
232         mat.shader = new armos.graphics.Shader;
233         mat.shader.load(shaderName);
234         return mat;
235     }
236 
237     static CustomShaderMaterial loadFiles(in string vertShaderPath, in string geomShaderPath, in string fragShaderPath){
238         auto mat = new CustomShaderMaterial();
239         mat.shader = new armos.graphics.Shader;
240         mat.shader.loadFiles(vertShaderPath, geomShaderPath, fragShaderPath);
241         return mat;
242     }
243 
244     static CustomShaderMaterial loadString(in string vertShaderPath, in string geomShaderPath, in string fragShaderPath){
245         auto mat = new CustomShaderMaterial();
246         mat.shader = new armos.graphics.Shader;
247         mat.shader.loadSources(vertShaderPath, geomShaderPath, fragShaderPath);
248         return mat;
249     }
250 }//class CustomShaderMaterial
251 
252 ///
253 class AutoReloadMaterial : Material{
254     mixin MaterialImpl;
255     
256     import fswatch;
257     
258     ///
259     this(in string shaderPath){
260         _shaderName = shaderPath;
261         
262         import armos.utils.file;
263         _watcher = FileWatch(absolutePath("."), true);
264         
265         _shader = new armos.graphics.Shader;
266         _shader.load(_shaderName);
267     }
268 
269     ///
270     bool hasReloaded()const{
271         return _hasReloaded;
272     }
273 
274     ///
275     T checkAndUpdateShader(){
276         _hasReloaded = false;
277         bool hasGotEvents;
278         FileChangeEvent[] events;
279         do{
280             hasGotEvents = true;
281             import std.exception;
282             try{
283                 events = _watcher.getEvents();
284             }catch(Exception e){
285                 hasGotEvents = false;
286             }
287         }while(!hasGotEvents);
288 
289         foreach (event; events){
290             if(event.type == FileChangeEventType.modify && (event.path == _shaderName ~ ".frag" ||
291                         event.path == _shaderName ~ ".geom" ||
292                         event.path == _shaderName ~ ".vert") 
293               ){
294                 _shader.clearLog
295                        .load(_shaderName);
296                 _hasReloaded = _hasReloaded||true;
297             }
298         }
299         return this;
300     }
301 
302     private{
303         string _shaderName;
304         bool _hasReloaded = false;
305         FileWatch _watcher;
306     }
307 }//class DefaultMaterial
308 
309 immutable string defaultVertexShaderSource = q{
310 #version 330
311 
312 uniform mat4 modelViewMatrix;
313 uniform mat4 projectionMatrix;
314 uniform mat4 modelViewProjectionMatrix;
315 uniform mat4 textureMatrix;
316 
317 in vec4 vertex;
318 in vec3 normal;
319 in vec3 tangent;
320 in vec4 texCoord0;
321 in vec4 texCoord1;
322 in vec4 color;
323 
324 out vec4 f_color;
325 out vec2 outtexCoord0;
326 out vec2 outtexCoord1;
327 
328 void main(void) {
329     gl_Position = modelViewProjectionMatrix * vertex;
330     f_color = color;
331     outtexCoord0 = texCoord0.xy;
332     outtexCoord1 = texCoord1.xy;
333 }
334 };
335 
336 immutable string defaultFragmentShaderSource = q{
337 #version 330
338     
339 in vec4 f_color;
340 in vec2 outtexCoord0;
341 in vec2 outtexCoord1;
342 
343 out vec4 fragColor;
344 
345 uniform vec4      diffuse;
346 uniform sampler2D tex0;
347 uniform sampler2D tex1;
348 
349 void main(void) {
350     fragColor = texture(tex0, outtexCoord0)*diffuse;
351 }
352 };