1 /*
2 DSFML - The Simple and Fast Multimedia Library for D
3 
4 Copyright (c) 2013 - 2015 Jeremy DeHaan (dehaan.jeremiah@gmail.com)
5 
6 This software is provided 'as-is', without any express or implied warranty.
7 In no event will the authors be held liable for any damages arising from the use of this software.
8 
9 Permission is granted to anyone to use this software for any purpose, including commercial applications,
10 and to alter it and redistribute it freely, subject to the following restrictions:
11 
12 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software.
13 If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
14 
15 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
16 
17 3. This notice may not be removed or altered from any source distribution
18 */
19 
20 module dsfml.graphics.shader;
21 
22 import dsfml.graphics.texture;
23 import dsfml.graphics.transform;
24 import dsfml.graphics.color;
25 
26 import dsfml.system.inputstream;
27 import dsfml.system.vector2;
28 import dsfml.system.vector3;
29 import dsfml.system.err;
30 
31 
32 /++
33  + Shader class (vertex and fragment).
34  + 
35  + Shaders are programs written using a specific language, executed directly by the graphics card and allowing one to apply real-time operations to the rendered entities.
36  + 
37  + There are two kinds of shaders:
38  + - Vertex shaders, that process vertices
39  + - Fragment (pixel) shaders, that process pixels
40  + 
41  + A DSFML Shader can be composed of either a vertex shader alone, a fragment shader alone, or both combined (see the variants of the load functions).
42  + 
43  + Shaders are written in GLSL, which is a C-like language dedicated to OpenGL shaders. You'll probably need to learn its basics before writing your own shaders for SFML.
44  + 
45  + Like any D/C/C++ program, a shader has its own variables that you can set from your D application. DSFML's Shader handles 5 different types of variables:
46  + - floats
47  + - vectors (2, 3, or 4 components)
48  + - colors
49  + - textures
50  + - transforms (matrices)
51  + 
52  + Authors: Laurent Gomila, Jeremy DeHaan
53  + See_Also: http://www.sfml-dev.org/documentation/2.0/classsf_1_1Shader.php#details
54  +/
55 class Shader
56 {
57 	/// Types of shaders.
58 	enum Type
59 	{
60 		Vertex,  /// Vertex shader
61 		Fragment /// Fragment (pixel) shader.
62 	}
63 
64 	package sfShader* sfPtr;
65 
66 	/// Special type/value that can be passed to setParameter, and that represents the texture of the object being drawn.
67 	struct CurrentTextureType {};
68 	static CurrentTextureType CurrentTexture;
69 	
70 
71 	this()
72 	{
73 		//creates an empty shader
74 		sfPtr=sfShader_construct();
75 	}
76 	
77 	package this(sfShader* shader)
78 	{
79 		sfPtr = shader;
80 	}
81 	
82 	~this()
83 	{
84 		import dsfml.system.config;
85 		mixin(destructorOutput);
86 		sfShader_destroy(sfPtr);
87 	}
88 
89 	/**
90 	 * Load either the vertex or fragment shader from a file.
91 	 * 
92 	 * This function loads a single shader, either vertex or fragment, identified by the second argument. The source must be a text file containing a valid shader in GLSL language. GLSL is a C-like language dedicated to OpenGL shaders; you'll probably need to read a good documentation for it before writing your own shaders.
93 	 * 
94 	 * Params:
95 	 * 		filename	= Path of the vertex or fragment shader file to load
96 	 * 		type		= Type of shader (vertex or fragment)
97 	 * 
98 	 * Returns: True if loading succeeded, false if it failed.
99 	 */
100 	bool loadFromFile(string filename, Type type)
101 	{
102 		import dsfml.system..string;
103 
104 		bool ret;
105 
106 		if(type == Type.Vertex)
107 		{
108 			ret = sfShader_loadFromFile(sfPtr, toStringz(filename) , null);
109 		}
110 		else
111 		{
112 			ret = sfShader_loadFromFile(sfPtr, null , toStringz(filename) );
113 		}
114 
115 		if(!ret)
116 		{
117 			err.write(dsfml.system..string.toString(sfErr_getOutput()));
118 		}
119 
120 		return ret;
121 	}
122 
123 	/**
124 	 * Load both the vertex and fragment shaders from files.
125 	 * 
126 	 * This function loads both the vertex and the fragment shaders. If one of them fails to load, the shader is left empty (the valid shader is unloaded). The sources must be text files containing valid shaders in GLSL language. GLSL is a C-like language dedicated to OpenGL shaders; you'll probably need to read a good documentation for it before writing your own shaders.
127 	 * 
128 	 * Params:
129 	 * 		vertexShaderFilename	= Path of the vertex shader file to load
130 	 * 		fragmentShaderFilename	= Path of the fragment shader file to load
131 	 * 
132 	 * Returns: True if loading succeeded, false if it failed.
133 	 */
134 	bool loadFromFile(string vertexShaderFilename, string fragmentShaderFilename)
135 	{
136 		import dsfml.system..string;
137 
138 		bool ret = sfShader_loadFromFile(sfPtr, toStringz(vertexShaderFilename) , toStringz(fragmentShaderFilename));
139 		if(!ret)
140 		{
141 			err.write(dsfml.system..string.toString(sfErr_getOutput()));
142 		}
143 
144 		return ret;
145 	}
146 
147 	/**
148 	 * Load either the vertex or fragment shader from a source code in memory.
149 	 * 
150 	 * This function loads a single shader, either vertex or fragment, identified by the second argument. The source code must be a valid shader in GLSL language. GLSL is a C-like language dedicated to OpenGL shaders; you'll probably need to read a good documentation for it before writing your own shaders.
151 	 * 
152 	 * Params:
153 	 * 		shader	= String containing the source code of the shader
154 	 * 		type	= Type of shader (vertex or fragment)
155 	 * 
156 	 * Returns: True if loading succeeded, false if it failed.
157 	 */
158 	bool loadFromMemory(string shader, Type type)
159 	{
160 		import dsfml.system..string;
161 
162 		bool ret;
163 
164 		if(type == Type.Vertex)
165 		{
166 			ret = sfShader_loadFromMemory(sfPtr, toStringz(shader) , null);
167 		}
168 		else
169 		{
170 			ret = sfShader_loadFromMemory(sfPtr, null , toStringz(shader) );
171 		}
172 		if(!ret)
173 		{
174 			err.write(dsfml.system..string.toString(sfErr_getOutput()));
175 		}
176 		return ret;
177 	}
178 	
179 	/**
180 	 * Load both the vertex and fragment shaders from source codes in memory.
181 	 * 
182 	 * This function loads both the vertex and the fragment shaders. If one of them fails to load, the shader is left empty (the valid shader is unloaded). The sources must be valid shaders in GLSL language. GLSL is a C-like language dedicated to OpenGL shaders; you'll probably need to read a good documentation for it before writing your own shaders.
183 	 * 
184 	 * Params:
185 	 * 		vertexShader	= String containing the source code of the vertex shader
186 	 * 		fragmentShader	= String containing the source code of the fragment shader
187 	 * 
188 	 * Returns: True if loading succeeded, false if it failed.
189 	 */
190 	bool loadFromMemory(string vertexShader, string fragmentShader)
191 	{
192 		import dsfml.system..string;
193 
194 		bool ret = sfShader_loadFromMemory(sfPtr, toStringz(vertexShader) , toStringz(fragmentShader));
195 		if(!ret)
196 		{
197 			err.write(dsfml.system..string.toString(sfErr_getOutput()));
198 		}
199 
200 		return ret;
201 	}
202 	
203 	/**
204 	 * Load either the vertex or fragment shader from a custom stream.
205 	 * 
206 	 * This function loads a single shader, either vertex or fragment, identified by the second argument. The source code must be a valid shader in GLSL language. GLSL is a C-like language dedicated to OpenGL shaders; you'll probably need to read a good documentation for it before writing your own shaders.
207 	 * 
208 	 * Params:
209 	 * 		stream	= Source stream to read from
210 	 * 		type	= Type of shader (vertex or fragment)
211 	 * 
212 	 * Returns: True if loading succeeded, false if it failed.
213 	 */
214 	bool loadFromStream(InputStream stream, Type type)
215 	{
216 		import dsfml.system..string;
217 
218 		bool ret;
219 
220 		if(type == Type.Vertex)
221 		{
222 			ret = sfShader_loadFromStream(sfPtr, new shaderStream(stream) , null);
223 		}
224 		else
225 		{
226 			ret = sfShader_loadFromStream(sfPtr, null , new shaderStream(stream));
227 		}
228 		if(!ret)
229 		{
230 			err.write(dsfml.system..string.toString(sfErr_getOutput()));
231 		}
232 
233 		return ret;
234 	}
235 
236 	/**
237 	 * Load both the vertex and fragment shaders from custom streams.
238 	 * 
239 	 * This function loads a single shader, either vertex or fragment, identified by the second argument. The source code must be a valid shader in GLSL language. GLSL is a C-like language dedicated to OpenGL shaders; you'll probably need to read a good documentation for it before writing your own shaders.
240 	 * 
241 	 * Params:
242 	 * 		vertexShaderStream		= Source stream to read the vertex shader from
243 	 * 		fragmentShaderStream	= Source stream to read the fragment shader from
244 	 * 
245 	 * Returns: True if loading succeeded, false if it failed.
246 	 */
247 	bool loadFromStream(InputStream vertexShaderStream, InputStream fragmentShaderStream)
248 	{
249 		import dsfml.system..string;
250 
251 		bool ret = sfShader_loadFromStream(sfPtr, new shaderStream(vertexShaderStream), new shaderStream(fragmentShaderStream));
252 		if(!ret)
253 		{
254 			err.write(dsfml.system..string.toString(sfErr_getOutput()));
255 		}
256 		return ret;
257 	}
258 
259 	/**
260 	 * Change a float parameter of the shader.
261 	 * 
262 	 * Params:
263 	 * 		name	= The name of the variable to change in the shader. The corresponding parameter in the shader must be a float (float GLSL type).
264 	 * 		x		= Value to assign
265 	 */
266 	 void setParameter(string name, float x)
267 	{
268 		import dsfml.system..string;
269 		sfShader_setFloatParameter(sfPtr, toStringz(name), x);
270 	}
271 
272 	///ditto
273 	void opIndexAssign(float x, string name)
274 	{
275 		import dsfml.system..string;
276 		sfShader_setFloatParameter(sfPtr, toStringz(name), x);
277 	}
278 
279 	/**
280 	 * Change a 2-components vector parameter of the shader.
281 	 * 
282 	 * Params:
283 	 * 		name	= The name of the variable to change in the shader. The corresponding parameter in the shader must be a 2x1 vector (vec2 GLSL type).
284 	 * 		x		= First component of the value to assign
285 	 * 		y		= Second component of the value to assign
286 	 */
287 	void setParameter(string name, float x, float y)
288 	{
289 		import dsfml.system..string;
290 		sfShader_setFloat2Parameter(sfPtr, toStringz(name), x, y);
291 	}
292 
293 	/**
294 	 * Change a 3-components vector parameter of the shader.
295 	 * 
296 	 * Params:
297 	 * 		name	= The name of the variable to change in the shader. The corresponding parameter in the shader must be a 3x1 vector (vec3 GLSL type).
298 	 * 		x		= First component of the value to assign
299 	 * 		y		= Second component of the value to assign
300 	 * 		z		= Third component of the value to assign
301 	 */
302 	void setParameter(string name, float x, float y, float z)
303 	{
304 		import dsfml.system..string;
305 		sfShader_setFloat3Parameter(sfPtr, toStringz(name), x,y,z);
306 	}
307 
308 	/**
309 	 * Change a 4-components vector parameter of the shader.
310 	 * 
311 	 * Params:
312 	 * 		name	= The name of the variable to change in the shader. The corresponding parameter in the shader must be a 4x1 vector (vec4 GLSL type).
313 	 * 		x		= First component of the value to assign
314 	 * 		y		= Second component of the value to assign
315 	 * 		z		= Third component of the value to assign
316 	 * 		w		= Fourth component of the value to assign
317 	 */
318 	void setParameter(string name, float x, float y, float z, float w)
319 	{
320 		import dsfml.system..string;
321 		sfShader_setFloat4Parameter(sfPtr, toStringz(name), x, y, z, w);
322 	}
323 
324 	/**
325 	 * Change variable length vector parameter of the shader. The length of the set of floats must be between 1 and 4.
326 	 * 
327 	 * Params:
328 	 * 		name	= The name of the variable to change in the shader. The corresponding parameter in the shader must be a 4x1 vector (vec4 GLSL type).
329 	 * 		val 	= The set of floats to assign.
330 	 */
331 	void opIndexAssign(float[] val, string name)
332 	{
333 		import dsfml.system..string;
334 		//assert to make sure that val is of proper length at run time
335 		assert((val.length >0) && (val.length <= 4));
336 
337 		if(val.length == 1)
338 			sfShader_setFloatParameter(sfPtr, toStringz(name), val[0]);
339 		else if(val.length == 2)
340 			sfShader_setFloat2Parameter(sfPtr, toStringz(name), val[0], val[1]);
341 		else if(val.length == 3)
342 			sfShader_setFloat3Parameter(sfPtr, toStringz(name), val[0], val[1], val[2]);
343 		else if(val.length >= 4)
344 			sfShader_setFloat4Parameter(sfPtr, toStringz(name), val[0], val[1], val[2], val[3]);
345 	}
346 
347 	/**
348 	 * Change a 2-components vector parameter of the shader.
349 	 * 
350 	 * Params:
351 	 * 		name	= The name of the variable to change in the shader. The corresponding parameter in the shader must be a 2x1 vector (vec2 GLSL type).
352 	 * 		vector	= Vector to assign
353 	 */
354 	void setParameter(string name, Vector2f vector)
355 	{
356 		import dsfml.system..string;
357 		sfShader_setFloat2Parameter(sfPtr, toStringz(name), vector.x, vector.y);
358 	}
359 
360 	///ditto
361 	void opIndexAssign(Vector2f vector, string name)
362 	{
363 		import dsfml.system..string;
364 		sfShader_setFloat2Parameter(sfPtr, toStringz(name), vector.x, vector.y);
365 	}
366 
367 	/**
368 	 * Change a 3-components vector parameter of the shader.
369 	 * 
370 	 * Params:
371 	 * 		name	= The name of the variable to change in the shader. The corresponding parameter in the shader must be a 3x1 vector (vec3 GLSL type).
372 	 * 		vector	= Vector to assign
373 	 */
374 	void setParameter(string name, Vector3f vector)
375 	{
376 		import dsfml.system..string;
377 		sfShader_setFloat3Parameter(sfPtr, toStringz(name), vector.x, vector.y, vector.z);
378 	}
379 	///ditto
380 	void opIndexAssign(Vector3f vector, string name)
381 	{
382 		import dsfml.system..string;
383 		sfShader_setFloat3Parameter(sfPtr, toStringz(name), vector.x, vector.y, vector.z);
384 	}
385 
386 	/**
387 	 * Change a color vector parameter of the shader.
388 	 * 
389 	 * It is important to note that the components of the color are normalized before being passed to the shader. Therefore, they are converted from range [0 .. 255] to range [0 .. 1]. For example, a Color(255, 125, 0, 255) will be transformed to a vec4(1.0, 0.5, 0.0, 1.0) in the shader.
390 	 * 
391 	 * Params:
392 	 * 		name	= The name of the variable to change in the shader. The corresponding parameter in the shader must be a 4x1 vector (vec4 GLSL type).
393 	 * 		color	= Color to assign
394 	 */
395 	void setParameter(string name, Color color)
396 	{
397 		import dsfml.system..string;
398 		sfShader_setColorParameter(sfPtr, toStringz(name), color.r, color.g, color.b, color.a);
399 	}
400 	///ditto
401 	void opIndexAssign(Color color, string name)
402 	{
403 		import dsfml.system..string;
404 		sfShader_setColorParameter(sfPtr, toStringz(name), color.r, color.g, color.b, color.a);
405 	}
406 
407 	/**
408 	 * Change a matrix parameter of the shader.
409 	 * 
410 	 * Params:
411 	 * 		name		= The name of the variable to change in the shader. The corresponding parameter in the shader must be a 4x4 matrix (mat4 GLSL type).
412 	 * 		transform	= Transform to assign
413 	 */
414 	void setParameter(string name, Transform transform)
415 	{
416 		import dsfml.system..string;
417 		sfShader_setTransformParameter(sfPtr, toStringz(name), transform.m_matrix.ptr);
418 	}
419 	///ditto
420 	void opIndexAssign(Transform transform, string name)
421 	{
422 		import dsfml.system..string;
423 		sfShader_setTransformParameter(sfPtr, toStringz(name), transform.m_matrix.ptr);
424 	}
425 
426 	/**
427 	 * Change a texture parameter of the shader.
428 	 * 
429 	 * It is important to note that the texture parameter must remain alive as long as the shader uses it - no copoy is made internally.
430 	 * 
431 	 * To use the texture of the object being draw, which cannot be known in advance, you can pass the special value Shader.CurrentTexture.
432 	 * 
433 	 * Params:
434 	 * 		name	= The name of the variable to change in the shader. The corresponding parameter in the shader must be a 2D texture (sampler2D GLSL type).
435 	 * 		texture	= Texture to assign
436 	 */
437 	void setParameter(string name, const(Texture) texture)
438 	{
439 		import dsfml.system..string;
440 		sfShader_setTextureParameter(sfPtr, toStringz(name), texture.sfPtr);
441 		err.write(dsfml.system..string.toString(sfErr_getOutput()));
442 	}
443 	///ditto
444 	void opIndexAssign(const(Texture) texture, string name)
445 	{
446 		import dsfml.system..string;
447 		sfShader_setTextureParameter(sfPtr, toStringz(name), texture.sfPtr);
448 		err.write(dsfml.system..string.toString(sfErr_getOutput()));
449 	}
450 
451 
452 	/**
453 	 * Change a texture parameter of the shader.
454 	 * 
455 	 * This overload maps a shader texture variable to the texture of the object being drawn, which cannot be known in advance. The second argument must be Shader.CurrentTexture.
456 	 * 
457 	 * Params:
458 	 * 		name	= The name of the variable to change in the shader. The corresponding parameter in the shader must be a 2D texture (sampler2D GLSL type).
459 	 */
460 	void setParameter(string name, CurrentTextureType)
461 	{
462 		import dsfml.system..string;
463 		sfShader_setCurrentTextureParameter(sfPtr, toStringz(name));
464 	}
465 
466 	/**
467 	 * Change a texture parameter of the shader.
468 	 * 
469 	 * This overload maps a shader texture variable to the texture of the object being drawn, which cannot be known in advance. The value given must be Shader.CurrentTexture.
470 	 * 
471 	 * Params:
472 	 * 		name	= The name of the variable to change in the shader. The corresponding parameter in the shader must be a 2D texture (sampler2D GLSL type).
473 	 */
474 	void opIndexAssign(CurrentTextureType, string name)
475 	{
476 		import dsfml.system..string;
477 		sfShader_setCurrentTextureParameter(sfPtr, toStringz(name));
478 	}
479 
480 	/**
481 	 * Bind a shader for rendering.
482 	 * 
483 	 * This function is not part of the graphics API, it mustn't be used when drawing SFML entities. It must be used only if you mix Shader with OpenGL code.
484 	 * 
485 	 * Params:
486 	 * 		shader	= Shader to bind. Can be null to use no shader.
487 	 */
488 	static void bind(Shader shader)
489 	{
490 		(shader is null)?sfShader_bind(null):sfShader_bind(shader.sfPtr);
491 	}
492 
493 	/**
494 	 * Tell whether or not the system supports shaders.
495 	 * 
496 	 * This function should always be called before using the shader features. If it returns false, then any attempt to use DSFML Shader will fail.
497 	 * 
498 	 * Returns: True if shaders are supported, false otherwise
499 	 */
500 	static bool isAvailable()
501 	{
502 		import dsfml.system..string;
503 		bool toReturn = sfShader_isAvailable();
504 		err.write(dsfml.system..string.toString(sfErr_getOutput()));
505 		return toReturn;
506 	}
507 }
508 
509 unittest
510 {
511 	//find some examples of interesting shaders and use them here
512 }
513 
514 private extern(C++) interface shaderInputStream
515 {
516 	long read(void* data, long size);
517 	
518 	long seek(long position);
519 	
520 	long tell();
521 	
522 	long getSize();
523 }
524 
525 
526 private class shaderStream:shaderInputStream
527 {
528 	private InputStream myStream;
529 	
530 	this(InputStream stream)
531 	{
532 		myStream = stream;
533 	}
534 	
535 	extern(C++)long read(void* data, long size)
536 	{
537 		return myStream.read(data[0..cast(size_t)size]);
538 	}
539 	
540 	extern(C++)long seek(long position)
541 	{
542 		return myStream.seek(position);
543 	}
544 	
545 	extern(C++)long tell()
546 	{
547 		return myStream.tell();
548 	}
549 	
550 	extern(C++)long getSize()
551 	{
552 		return myStream.getSize();
553 	}
554 }
555 
556 package extern(C):
557 struct sfShader;
558 
559 private extern(C):
560 
561 //Construct a new shader
562 sfShader* sfShader_construct();
563 
564 //Load both the vertex and fragment shaders from files
565 bool sfShader_loadFromFile(sfShader* shader, const(char)* vertexShaderFilename, const char* fragmentShaderFilename);
566 
567 //Load both the vertex and fragment shaders from source codes in memory
568 bool sfShader_loadFromMemory(sfShader* shader, const(char)* vertexShader, const char* fragmentShader);
569 
570 //Load both the vertex and fragment shaders from custom streams
571 bool sfShader_loadFromStream(sfShader* shader, shaderInputStream vertexShaderStream, shaderInputStream fragmentShaderStream);
572 
573 //Destroy an existing shader
574 void sfShader_destroy(sfShader* shader);
575 
576 //Change a float parameter of a shader
577 void sfShader_setFloatParameter(sfShader* shader, const char* name, float x);
578 
579 //Change a 2-components vector parameter of a shader
580 void sfShader_setFloat2Parameter(sfShader* shader, const char* name, float x, float y);
581 
582 //Change a 3-components vector parameter of a shader
583 void sfShader_setFloat3Parameter(sfShader* shader, const char* name, float x, float y, float z);
584 
585 //Change a 4-components vector parameter of a shader
586 void sfShader_setFloat4Parameter(sfShader* shader, const char* name, float x, float y, float z, float w);
587 
588 //Change a color parameter of a shader
589 void sfShader_setColorParameter(sfShader* shader, const char* name, ubyte r, ubyte g, ubyte b, ubyte a);
590 
591 //Change a matrix parameter of a shader
592 void sfShader_setTransformParameter(sfShader* shader, const char* name, float* transform);
593 
594 //Change a texture parameter of a shader
595 void sfShader_setTextureParameter(sfShader* shader, const char* name, const sfTexture* texture);
596 
597 //Change a texture parameter of a shader
598 void sfShader_setCurrentTextureParameter(sfShader* shader, const char* name);
599 
600 //Bind a shader for rendering (activate it)
601 void sfShader_bind(const sfShader* shader);
602 
603 //Tell whether or not the system supports shaders
604 bool sfShader_isAvailable();
605 
606 const(char)* sfErr_getOutput();