/* rbSFML - Copyright (c) 2010 Henrik Valter Vogelius Hansson - groogy@groogy.se * This software is provided 'as-is', without any express or * implied warranty. In no event will the authors be held * liable for any damages arising from the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute * it freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; * you must not claim that you wrote the original software. * If you use this software in a product, an acknowledgment * in the product documentation would be appreciated but * is not required. * * 2. Altered source versions must be plainly marked as such, * and must not be misrepresented as being the original software. * * 3. This notice may not be removed or altered from any * source distribution. */ #include "Shader.hpp" #include "Vector2.hpp" #include "Vector3.hpp" #include "Image.hpp" #include "main.hpp" #include VALUE globalShaderClass; /* External classes */ extern VALUE globalVector2Class; extern VALUE globalVector3Class; extern VALUE globalImageClass; static void Shader_Free( sf::Shader *anObject ) { delete anObject; } /* call-seq: * shader.loadFromFile( filename ) -> true or false * * Load the shader from a file. * * The source must be a text file containing a valid fragment 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. */ static VALUE Shader_LoadFromFile( VALUE self, VALUE aFileName ) { sf::Shader *object = NULL; Data_Get_Struct( self, sf::Shader, object ); if( object->LoadFromFile( rb_string_value_cstr( &aFileName ) ) == true ) { return Qtrue; } else { return Qfalse; } } /* call-seq: * shader.loadFromMemory( filename ) -> true or false * * Load the shader from a source code in memory. * * The source code must be a valid fragment 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. */ static VALUE Shader_LoadFromMemory( VALUE self, VALUE aShader ) { sf::Shader *object = NULL; Data_Get_Struct( self, sf::Shader, object ); if( object->LoadFromMemory( rb_string_value_cstr( &aShader ) ) == true ) { return Qtrue; } else { return Qfalse; } } /* call-seq: * shader.setParameter( name, x ) * shader.setParameter( name, x, y ) * shader.setParameter( name, x, y, z ) * shader.setParameter( name, x, y, z, w ) * shader.setParameter( name, vector2 ) * shader.setParameter( name, vector3 ) * * Change a vector parameter of the shader. */ static VALUE Shader_SetParameter( int argc, VALUE *args, VALUE self ) { sf::Shader *object = NULL; Data_Get_Struct( self, sf::Shader, object ); const char * name; switch( argc ) { case 2: { name = rb_string_value_cstr( &args[0] ); if( rb_obj_is_kind_of( args[1], rb_cFloat ) == Qtrue ) { object->SetParameter( name, NUM2DBL( args[1] ) ); } else if( rb_obj_is_kind_of( args[1], globalVector2Class ) == Qtrue || ( rb_obj_is_kind_of( args[1], rb_cArray ) == Qtrue && FIX2INT( rb_funcall( args[1], rb_intern( "size" ), 0 ) ) == 2 ) ) { VALUE arg1 = Vector2_ForceType( args[1] ); sf::Vector2f vector; vector.x = NUM2DBL( Vector2_GetX( args[1] ) ); vector.y = NUM2DBL( Vector2_GetY( args[1] ) ); object->SetParameter( name, vector ); } else if( rb_obj_is_kind_of( args[1], globalVector3Class ) == Qtrue || ( rb_obj_is_kind_of( args[1], rb_cArray ) == Qtrue && FIX2INT( rb_funcall( args[1], rb_intern( "size" ), 0 ) ) == 3 ) ) { VALUE arg1 = Vector3_ForceType( args[1] ); sf::Vector3f vector; vector.x = NUM2DBL( Vector3_GetX( args[1] ) ); vector.y = NUM2DBL( Vector3_GetY( args[1] ) ); vector.z = NUM2DBL( Vector3_GetZ( args[1] ) ); object->SetParameter( name, vector ); } break; } case 3: name = rb_string_value_cstr( &args[0] ); object->SetParameter( name, NUM2DBL( args[1] ), NUM2DBL( args[2] ) ); break; case 4: name = rb_string_value_cstr( &args[0] ); object->SetParameter( name, NUM2DBL( args[1] ), NUM2DBL( args[2] ), NUM2DBL( args[3] ) ); break; case 5: name = rb_string_value_cstr( &args[0] ); object->SetParameter( name, NUM2DBL( args[1] ), NUM2DBL( args[2] ), NUM2DBL( args[3] ), NUM2DBL( args[4] ) ); break; default: rb_raise( rb_eArgError, "Expected 2..5 arguments but was given %d", argc ); } return Qnil; } /* call-seq: * shader.setParameter( name, texture ) * * Change a texture parameter of the shader. * * name is the name of the texture to change in the shader. To tell the shader to use the current texture of the object * being drawn, pass Shader::CurrentTexture. Example: * * # These are the variables in the pixel shader * uniform sampler2D current; * uniform sampler2D other; * * image = SFML::Image.new * ... * shader.setParameter( "current", SFML::Shader::CurrentTexture ) * shader.setParameter( "other", image ) * * It is important to note that texture must remain alive as long as the shader uses it, no copy is made internally. */ static VALUE Shader_SetTexture( VALUE self, VALUE aName, VALUE aTexture ) { VALIDATE_CLASS( aName, rb_cString, "name" ); VALIDATE_CLASS( aTexture, globalImageClass, "texture" ); sf::Image *texture = NULL; Data_Get_Struct( self, sf::Image, texture ); const char * name = rb_string_value_cstr( &aName ); sf::Shader *object = NULL; Data_Get_Struct( self, sf::Shader, object ); object->SetTexture( name, *texture ); return Qnil; } /* call-seq: * shader.bind() * * Bind the shader for rendering (activate it). * * This function is normally for internal use only, unless you want to use the shader with a custom OpenGL rendering * instead of a SFML drawable. */ static VALUE Shader_Bind( VALUE self ) { sf::Shader *object = NULL; Data_Get_Struct( self, sf::Shader, object ); object->Bind(); return Qnil; } /* call-seq: * shader.unbind() * * Unbind the shader (deactivate it). * * This function is normally for internal use only, unless you want to use the shader with a custom OpenGL rendering * instead of a SFML drawable. */ static VALUE Shader_Unbind( VALUE self ) { sf::Shader *object = NULL; Data_Get_Struct( self, sf::Shader, object ); object->Unbind(); return Qnil; } /* call-seq: * Shader.new() * * Create a new shader. */ static VALUE Shader_New( int argc, VALUE *args, VALUE aKlass ) { sf::Shader *object = new sf::Shader(); VALUE rbData = Data_Wrap_Struct( aKlass, 0, Shader_Free, object ); rb_obj_call_init( rbData, argc, args ); return rbData; } /* call-seq: * Shader.isAvailable() * * Tell whether or not the system supports shaders. * * This function should always be called before using the shader features. If it returns false, then any attempt to * use SFML::Shader will fail. */ static VALUE Shader_IsAvailable( VALUE aKlass ) { return ( sf::Shader::IsAvailable() == true ? Qtrue : Qfalse ); } static VALUE CreateCurrentTextureWrapper( void ) { sf::Image * image = const_cast< sf::Image * >( &sf::Shader::CurrentTexture ); VALUE rbData = Data_Wrap_Struct( globalImageClass, 0, 0, image ); rb_obj_call_init( rbData, 0, 0 ); return rbData; } void Init_Shader( void ) { /* SFML namespace which contains the classes of this module. */ VALUE sfml = rb_define_module( "SFML" ); /* Pixel/fragment shader class. * * Pixel shaders (or fragment shaders) are programs written using a specific language, executed directly by the * graphics card and allowing to apply per-pixel real-time operations to the rendered entities. * * Pixel 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. * * Like any program, a shader has its own variables that you can set from your Ruby application. SFML::Shader * handles 3 different types of variables: * * - floats * - vectors (2, 3 or 4 components) * - textures * * The value of the variables can be changed at any time with either SFML::Shader#setParameter or SFML::Shader#SetTexture: * * shader.setParameter( "offset", 2.0 ) * shader.setParameter( "color", 0.5, 0.8, 0.3 ) * shader.setTexture( "image", image ) # image is a SFML::Image * shader.setTexture( "current", SFML::Shader::CurrentTexture ) * * Shader::CurrentTexture is a special value that represents the texture that the object being drawn is using. * * To apply a shader to a drawable, you must pass it as an additional parameter to the Draw function: * * window.draw( sprite, shader ) * * Shaders can be used on any drawable, but they are mainly made for SFML::Sprite. Using a shader on a SFML::String is * more limited, because the texture of the string is not the actual text that you see on screen, it is a big image * containing all the characters of the font in an arbitrary order. Thus, texture lookups on pixels other than the * current one may not give you the expected result. Using a shader with SFML::Shape is even more limited, as shapes * don't use any texture. * * Shaders can also be used to apply global post-effects to the current contents of the target * (like the old sf::PostFx class in SFML 1). This can be done in two different ways: * * - draw everything to a SFML::RenderImage, then draw it to the main target using the shader * - draw everything directly to the main target, then use SFML::Image::CopyScreen to copy its contents to an image * and draw it to the main target using the shader * * The first technique is more optimized because it doesn't involve retrieving the target's pixels to system memory, * but the second one doesn't impact the rendering process and can be easily inserted anywhere. * * Like SFML::Image that can be used as a raw OpenGL texture, SFML::Shader can also be used directly as a raw fragment * shader for custom OpenGL geometry. * * window.setActive() * shader.bind() * # ... render OpenGL geometry ... * shader.unbind() * */ globalShaderClass = rb_define_class_under( sfml, "Shader", rb_cObject ); // Class methods rb_define_singleton_method( globalShaderClass, "new", Shader_New, -1 ); rb_define_singleton_method( globalShaderClass, "isAvailable", Shader_IsAvailable, 0 ); // Class Constants rb_define_const( globalShaderClass, "CurrentTexture", CreateCurrentTextureWrapper() ); // Instance methods rb_define_method( globalShaderClass, "loadFromFile", Shader_LoadFromFile, 1 ); rb_define_method( globalShaderClass, "loadFromMemory", Shader_LoadFromMemory, 1 ); rb_define_method( globalShaderClass, "setParameter", Shader_SetParameter, -1 ); rb_define_method( globalShaderClass, "setTexture", Shader_SetTexture, 2 ); rb_define_method( globalShaderClass, "bind", Shader_Bind, 0 ); rb_define_method( globalShaderClass, "unbind", Shader_Unbind, 0 ); // Class Aliases rb_define_alias( CLASS_OF( globalShaderClass ), "is_available", "isAvailable" ); rb_define_alias( CLASS_OF( globalShaderClass ), "available?", "isAvailable" ); // Instance Aliases rb_define_alias( globalShaderClass, "load_from_file", "loadFromFile" ); rb_define_alias( globalShaderClass, "loadFile", "loadFromFile" ); rb_define_alias( globalShaderClass, "load_file", "loadFromFile" ); rb_define_alias( globalShaderClass, "load_from_memory", "loadFromMemory" ); rb_define_alias( globalShaderClass, "loadMemory", "loadFromMemory" ); rb_define_alias( globalShaderClass, "load_memory", "loadFromMemory" ); rb_define_alias( globalShaderClass, "set_parameter", "setParameter" ); rb_define_alias( globalShaderClass, "[]=", "setParameter" ); rb_define_alias( globalShaderClass, "set_texture", "setTexture" ); }