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 	}
75 	
76 	package this(sfShader* shader)
77 	{
78 		sfPtr = shader;
79 	}
80 	
81 	~this()
82 	{
83 		import dsfml.system.config;
84 		mixin(destructorOutput);
85 		sfShader_destroy(sfPtr);
86 	}
87 
88 	/**
89 	 * Load either the vertex or fragment shader from a file.
90 	 * 
91 	 * 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.
92 	 * 
93 	 * Params:
94 	 * 		filename	= Path of the vertex or fragment shader file to load
95 	 * 		type		= Type of shader (vertex or fragment)
96 	 * 
97 	 * Returns: True if loading succeeded, false if it failed.
98 	 */
99 	bool loadFromFile(string filename, Type type)
100 	{
101 		import dsfml.system..string;
102 
103 		bool ret;
104 
105 		if(type == Type.Vertex)
106 		{
107 			ret = sfShader_loadFromFile(sfPtr, toStringz(filename) , null);
108 		}
109 		else
110 		{
111 			ret = sfShader_loadFromFile(sfPtr, null , toStringz(filename) );
112 		}
113 
114 		if(!ret)
115 		{
116 			err.write(toString(sfErr_getOutput()));
117 		}
118 
119 		return ret;
120 	}
121 
122 	/**
123 	 * Load both the vertex and fragment shaders from files.
124 	 * 
125 	 * 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.
126 	 * 
127 	 * Params:
128 	 * 		vertexShaderFilename	= Path of the vertex shader file to load
129 	 * 		fragmentShaderFilename	= Path of the fragment shader file to load
130 	 * 
131 	 * Returns: True if loading succeeded, false if it failed.
132 	 */
133 	bool loadFromFile(string vertexShaderFilename, string fragmentShaderFilename)
134 	{
135 		import dsfml.system..string;
136 
137 		bool ret = sfShader_loadFromFile(sfPtr, toStringz(vertexShaderFilename) , toStringz(fragmentShaderFilename));
138 		if(!ret)
139 		{
140 			err.write(toString(sfErr_getOutput()));
141 		}
142 
143 		return ret;
144 	}
145 
146 	/**
147 	 * Load either the vertex or fragment shader from a source code in memory.
148 	 * 
149 	 * 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.
150 	 * 
151 	 * Params:
152 	 * 		shader	= String containing the source code of the shader
153 	 * 		type	= Type of shader (vertex or fragment)
154 	 * 
155 	 * Returns: True if loading succeeded, false if it failed.
156 	 */
157 	bool loadFromMemory(string shader, Type type)
158 	{
159 		import dsfml.system..string;
160 
161 		bool ret;
162 
163 		if(type == Type.Vertex)
164 		{
165 			ret = sfShader_loadFromMemory(sfPtr, toStringz(shader) , null);
166 		}
167 		else
168 		{
169 			ret = sfShader_loadFromMemory(sfPtr, null , toStringz(shader) );
170 		}
171 		if(!ret)
172 		{
173 			err.write(toString(sfErr_getOutput()));
174 		}
175 		return ret;
176 	}
177 	
178 	/**
179 	 * Load both the vertex and fragment shaders from source codes in memory.
180 	 * 
181 	 * 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.
182 	 * 
183 	 * Params:
184 	 * 		vertexShader	= String containing the source code of the vertex shader
185 	 * 		fragmentShader	= String containing the source code of the fragment shader
186 	 * 
187 	 * Returns: True if loading succeeded, false if it failed.
188 	 */
189 	bool loadFromMemory(string vertexShader, string fragmentShader)
190 	{
191 		import dsfml.system..string;
192 
193 		bool ret = sfShader_loadFromMemory(sfPtr, toStringz(vertexShader) , toStringz(fragmentShader));
194 		if(!ret)
195 		{
196 			err.write(toString(sfErr_getOutput()));
197 		}
198 
199 		return ret;
200 	}
201 	
202 	/**
203 	 * Load either the vertex or fragment shader from a custom stream.
204 	 * 
205 	 * 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.
206 	 * 
207 	 * Params:
208 	 * 		stream	= Source stream to read from
209 	 * 		type	= Type of shader (vertex or fragment)
210 	 * 
211 	 * Returns: True if loading succeeded, false if it failed.
212 	 */
213 	bool loadFromStream(InputStream stream, Type type)
214 	{
215 		import dsfml.system..string;
216 
217 		bool ret;
218 
219 		if(type == Type.Vertex)
220 		{
221 			ret = sfShader_loadFromStream(sfPtr, new shaderStream(stream) , null);
222 		}
223 		else
224 		{
225 			ret = sfShader_loadFromStream(sfPtr, null , new shaderStream(stream));
226 		}
227 		if(!ret)
228 		{
229 			err.write(toString(sfErr_getOutput()));
230 		}
231 
232 		return ret;
233 	}
234 
235 	/**
236 	 * Load both the vertex and fragment shaders from custom streams.
237 	 * 
238 	 * 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.
239 	 * 
240 	 * Params:
241 	 * 		vertexShaderStream		= Source stream to read the vertex shader from
242 	 * 		fragmentShaderStream	= Source stream to read the fragment shader from
243 	 * 
244 	 * Returns: True if loading succeeded, false if it failed.
245 	 */
246 	bool loadFromStream(InputStream vertexShaderStream, InputStream fragmentShaderStream)
247 	{
248 		import dsfml.system..string;
249 
250 		bool ret = sfShader_loadFromStream(sfPtr, new shaderStream(vertexShaderStream), new shaderStream(fragmentShaderStream));
251 		if(!ret)
252 		{
253 			err.write(toString(sfErr_getOutput()));
254 		}
255 		return ret;
256 	}
257 
258 	/**
259 	 * Change a float parameter of the shader.
260 	 * 
261 	 * Params:
262 	 * 		name	= The name of the variable to change in the shader. The corresponding parameter in the shader must be a float (float GLSL type).
263 	 * 		x		= Value to assign
264 	 */
265 	 void setParameter(string name, float x)
266 	{
267 		import dsfml.system..string;
268 		sfShader_setFloatParameter(sfPtr, toStringz(name), x);
269 	}
270 
271 	///ditto
272 	void opIndexAssign(float x, string name)
273 	{
274 		import dsfml.system..string;
275 		sfShader_setFloatParameter(sfPtr, toStringz(name), x);
276 	}
277 
278 	/**
279 	 * Change a 2-components vector parameter of the shader.
280 	 * 
281 	 * Params:
282 	 * 		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).
283 	 * 		x		= First component of the value to assign
284 	 * 		y		= Second component of the value to assign
285 	 */
286 	void setParameter(string name, float x, float y)
287 	{
288 		import dsfml.system..string;
289 		sfShader_setFloat2Parameter(sfPtr, toStringz(name), x, y);
290 	}
291 
292 	/**
293 	 * Change a 3-components vector parameter of the shader.
294 	 * 
295 	 * Params:
296 	 * 		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).
297 	 * 		x		= First component of the value to assign
298 	 * 		y		= Second component of the value to assign
299 	 * 		z		= Third component of the value to assign
300 	 */
301 	void setParameter(string name, float x, float y, float z)
302 	{
303 		import dsfml.system..string;
304 		sfShader_setFloat3Parameter(sfPtr, toStringz(name), x,y,z);
305 	}
306 
307 	/**
308 	 * Change a 4-components vector parameter of the shader.
309 	 * 
310 	 * Params:
311 	 * 		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).
312 	 * 		x		= First component of the value to assign
313 	 * 		y		= Second component of the value to assign
314 	 * 		z		= Third component of the value to assign
315 	 * 		w		= Fourth component of the value to assign
316 	 */
317 	void setParameter(string name, float x, float y, float z, float w)
318 	{
319 		import dsfml.system..string;
320 		sfShader_setFloat4Parameter(sfPtr, toStringz(name), x, y, z, w);
321 	}
322 
323 	/**
324 	 * Change variable length vector parameter of the shader. The length of the set of floats must be between 1 and 4.
325 	 * 
326 	 * Params:
327 	 * 		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).
328 	 * 		val 	= The set of floats to assign.
329 	 */
330 	void opIndexAssign(float[] val, string name)
331 	{
332 		import dsfml.system..string;
333 		//assert to make sure that val is of proper length at run time
334 		assert((val.length >0) && (val.length <= 4));
335 
336 		if(val.length == 1)
337 			sfShader_setFloatParameter(sfPtr, toStringz(name), val[0]);
338 		else if(val.length == 2)
339 			sfShader_setFloat2Parameter(sfPtr, toStringz(name), val[0], val[1]);
340 		else if(val.length == 3)
341 			sfShader_setFloat3Parameter(sfPtr, toStringz(name), val[0], val[1], val[2]);
342 		else if(val.length >= 4)
343 			sfShader_setFloat4Parameter(sfPtr, toStringz(name), val[0], val[1], val[2], val[3]);
344 	}
345 
346 	/**
347 	 * Change a 2-components vector parameter of the shader.
348 	 * 
349 	 * Params:
350 	 * 		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).
351 	 * 		vector	= Vector to assign
352 	 */
353 	void setParameter(string name, Vector2f vector)
354 	{
355 		import dsfml.system..string;
356 		sfShader_setFloat2Parameter(sfPtr, toStringz(name), vector.x, vector.y);
357 	}
358 
359 	///ditto
360 	void opIndexAssign(Vector2f vector, string name)
361 	{
362 		import dsfml.system..string;
363 		sfShader_setFloat2Parameter(sfPtr, toStringz(name), vector.x, vector.y);
364 	}
365 
366 	/**
367 	 * Change a 3-components vector parameter of the shader.
368 	 * 
369 	 * Params:
370 	 * 		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).
371 	 * 		vector	= Vector to assign
372 	 */
373 	void setParameter(string name, Vector3f vector)
374 	{
375 		import dsfml.system..string;
376 		sfShader_setFloat3Parameter(sfPtr, toStringz(name), vector.x, vector.y, vector.z);
377 	}
378 	///ditto
379 	void opIndexAssign(Vector3f vector, string name)
380 	{
381 		import dsfml.system..string;
382 		sfShader_setFloat3Parameter(sfPtr, toStringz(name), vector.x, vector.y, vector.z);
383 	}
384 
385 	/**
386 	 * Change a color vector parameter of the shader.
387 	 * 
388 	 * 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.
389 	 * 
390 	 * Params:
391 	 * 		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).
392 	 * 		color	= Color to assign
393 	 */
394 	void setParameter(string name, Color color)
395 	{
396 		import dsfml.system..string;
397 		sfShader_setColorParameter(sfPtr, toStringz(name), color.r, color.g, color.b, color.a);
398 	}
399 	///ditto
400 	void opIndexAssign(Color color, string name)
401 	{
402 		import dsfml.system..string;
403 		sfShader_setColorParameter(sfPtr, toStringz(name), color.r, color.g, color.b, color.a);
404 	}
405 
406 	/**
407 	 * Change a matrix parameter of the shader.
408 	 * 
409 	 * Params:
410 	 * 		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).
411 	 * 		transform	= Transform to assign
412 	 */
413 	void setParameter(string name, Transform transform)
414 	{
415 		import dsfml.system..string;
416 		sfShader_setTransformParameter(sfPtr, toStringz(name), transform.m_matrix.ptr);
417 	}
418 	///ditto
419 	void opIndexAssign(Transform transform, string name)
420 	{
421 		import dsfml.system..string;
422 		sfShader_setTransformParameter(sfPtr, toStringz(name), transform.m_matrix.ptr);
423 	}
424 
425 	/**
426 	 * Change a texture parameter of the shader.
427 	 * 
428 	 * It is important to note that the texture parameter must remain alive as long as the shader uses it - no copoy is made internally.
429 	 * 
430 	 * To use the texture of the object being draw, which cannot be known in advance, you can pass the special value Shader.CurrentTexture.
431 	 * 
432 	 * Params:
433 	 * 		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).
434 	 * 		texture	= Texture to assign
435 	 */
436 	void setParameter(string name, const(Texture) texture)
437 	{
438 		import dsfml.system..string;
439 		sfShader_setTextureParameter(sfPtr, toStringz(name), texture.sfPtr);
440 		err.write(toString(sfErr_getOutput()));
441 	}
442 	///ditto
443 	void opIndexAssign(const(Texture) texture, string name)
444 	{
445 		import dsfml.system..string;
446 		sfShader_setTextureParameter(sfPtr, toStringz(name), texture.sfPtr);
447 		err.write(toString(sfErr_getOutput()));
448 	}
449 
450 
451 	/**
452 	 * Change a texture parameter of the shader.
453 	 * 
454 	 * 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.
455 	 * 
456 	 * Params:
457 	 * 		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).
458 	 */
459 	void setParameter(string name, CurrentTextureType)
460 	{
461 		import dsfml.system..string;
462 		sfShader_setCurrentTextureParameter(sfPtr, toStringz(name));
463 	}
464 
465 	/**
466 	 * Change a texture parameter of the shader.
467 	 * 
468 	 * 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.
469 	 * 
470 	 * Params:
471 	 * 		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).
472 	 */
473 	void opIndexAssign(CurrentTextureType, string name)
474 	{
475 		import dsfml.system..string;
476 		sfShader_setCurrentTextureParameter(sfPtr, toStringz(name));
477 	}
478 
479 	/**
480 	 * Bind a shader for rendering.
481 	 * 
482 	 * 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.
483 	 * 
484 	 * Params:
485 	 * 		shader	= Shader to bind. Can be null to use no shader.
486 	 */
487 	static void bind(Shader shader)
488 	{
489 		(shader is null)?sfShader_bind(null):sfShader_bind(shader.sfPtr);
490 	}
491 
492 	/**
493 	 * Tell whether or not the system supports shaders.
494 	 * 
495 	 * This function should always be called before using the shader features. If it returns false, then any attempt to use DSFML Shader will fail.
496 	 * 
497 	 * Returns: True if shaders are supported, false otherwise
498 	 */
499 	static bool isAvailable()
500 	{
501 		import dsfml.system..string;
502 		bool toReturn = sfShader_isAvailable();
503 		err.write(toString(sfErr_getOutput()));
504 		return toReturn;
505 	}
506 }
507 
508 unittest
509 {
510 	//find some examples of interesting shaders and use them here
511 }
512 
513 private extern(C++) interface sfmlInputStream
514 {
515 	long read(void* data, long size);
516 	
517 	long seek(long position);
518 	
519 	long tell();
520 	
521 	long getSize();
522 }
523 
524 
525 private class shaderStream:sfmlInputStream
526 {
527 	private InputStream myStream;
528 	
529 	this(InputStream stream)
530 	{
531 		myStream = stream;
532 	}
533 	
534 	extern(C++)long read(void* data, long size)
535 	{
536 		return myStream.read(data[0..cast(size_t)size]);
537 	}
538 	
539 	extern(C++)long seek(long position)
540 	{
541 		return myStream.seek(position);
542 	}
543 	
544 	extern(C++)long tell()
545 	{
546 		return myStream.tell();
547 	}
548 	
549 	extern(C++)long getSize()
550 	{
551 		return myStream.getSize();
552 	}
553 }
554 
555 package extern(C):
556 struct sfShader;
557 
558 private extern(C):
559 
560 //Construct a new shader
561 sfShader* sfShader_construct();
562 
563 //Load both the vertex and fragment shaders from files
564 bool sfShader_loadFromFile(sfShader* shader, const(char)* vertexShaderFilename, const char* fragmentShaderFilename);
565 
566 //Load both the vertex and fragment shaders from source codes in memory
567 bool sfShader_loadFromMemory(sfShader* shader, const(char)* vertexShader, const char* fragmentShader);
568 
569 //Load both the vertex and fragment shaders from custom streams
570 bool sfShader_loadFromStream(sfShader* shader, sfmlInputStream vertexShaderStream, sfmlInputStream fragmentShaderStream);
571 
572 //Destroy an existing shader
573 void sfShader_destroy(sfShader* shader);
574 
575 //Change a float parameter of a shader
576 void sfShader_setFloatParameter(sfShader* shader, const char* name, float x);
577 
578 //Change a 2-components vector parameter of a shader
579 void sfShader_setFloat2Parameter(sfShader* shader, const char* name, float x, float y);
580 
581 //Change a 3-components vector parameter of a shader
582 void sfShader_setFloat3Parameter(sfShader* shader, const char* name, float x, float y, float z);
583 
584 //Change a 4-components vector parameter of a shader
585 void sfShader_setFloat4Parameter(sfShader* shader, const char* name, float x, float y, float z, float w);
586 
587 //Change a color parameter of a shader
588 void sfShader_setColorParameter(sfShader* shader, const char* name, ubyte r, ubyte g, ubyte b, ubyte a);
589 
590 //Change a matrix parameter of a shader
591 void sfShader_setTransformParameter(sfShader* shader, const char* name, float* transform);
592 
593 //Change a texture parameter of a shader
594 void sfShader_setTextureParameter(sfShader* shader, const char* name, const sfTexture* texture);
595 
596 //Change a texture parameter of a shader
597 void sfShader_setCurrentTextureParameter(sfShader* shader, const char* name);
598 
599 //Bind a shader for rendering (activate it)
600 void sfShader_bind(const sfShader* shader);
601 
602 //Tell whether or not the system supports shaders
603 bool sfShader_isAvailable();
604 
605 const(char)* sfErr_getOutput();