Envision, Create, Share

Welcome to HBGames, a leading amateur game development forum and Discord server. All are welcome, and amongst our ranks you will find experts in their field from all aspects of video game design and development.

RMVXA-GL - 3D graphics in RPG Maker!

RMVXA-GL
RPG Maker VX Ace OpenGL


This is a re-approach to my last attempt with using GLSL shaders in RMVXA.
The GLSL project had a number of flaws, most of it coming from that it was produced when I had little experience with desktop GL and I had very little experience with Ruby and I was more interested in hacking the Game runtime than creating a usable API.

If you are unfamiliar with my previous attempts; this project is a 3D rendering API so you can call OpenGL rendering commands and write the result to an RPG Maker bitmap.

This also supports loading an RPG Maker bitmap into an OpenGL texture, so you can load bitmaps from RPG Maker and render them in 3D.

In theory this will work with XP and VX, but VXA is what I am developing this on so it's the primary target for now.

System Requirements
Read this!
OpenGL 2.1 support is currently untested. It works with OpenGL 3.1 on NVidia, Intel and AMD GPUs, but 2.1 support is untested (However, it is implemented as best as I could without a 2.1 GPU).
These are the minimum supported GPUs:
  • AMD Radeon HD 6350 or later
  • ATI Radeon HD 2350 or later
  • NVidia GeForce 6100 or later
  • First-generation Intel Core graphics or later
Frame buffer object extension is required.

Download Alpha 2

Screen-shots
Nvg2GFb.png

Super Simple Bur!

w3YC522.png

Gameboy Pixelation and Palette!

hOX5vAa.png

3 colours per component + 1.5x zoom!

rRugR1Q.png

Refraction shader!

vQoH9iC.png

Spinning cube!

This is more of a try-for-your-self thing, so download the demo and have a walk around.

Demo runs slow!
The post-processing effect is choked by the snap_to_bitmap method, which is slow and is not intended for real-time (This method is usually called once for static image processing such as opening a menu or similar, this demo calls it every frame).

The character on the left will advise you to turn off "reduce screen flickering" (Basically turn of the runtime's vsync), doing that for me gives me a solid 60 FPS on my 2011 gaming PC, with it turned on gives me a solid 59 FPS. My 2012 mid-range laptop gets 15 to 45 FPS in this demo.

The actual 3D rendering is very fast; all GPU memory is allocated/destroyed at initialise/terminate so the slowest part of the rendering is uploading and downloading the texture to and from the GPU, which I actually performance tested against on-GPU memory and had very little performance difference.

TLDR: RPG Maker's snap_to_bitmap is slow, someone make me a new map rendering class so I can directly grab the bitmap as it is rendered (Or better yet, a map rendering class that renders directly to OpenGL!).

So what is this?
This will be an API for rendering 3D polygons within RPG Maker, it comes with my DLL which deals with all the OpenGL stuff and I am writing an API for using it.
It's intended for scripters who want to write 3D graphics, so if you don't script then this isn't for you.

What does this actually mean?
This API set will hopefully let us do cool stuff in RPG Maker;
  • 3D battle animations
  • 3D maps
  • 3D battlers and enemies
  • 3D battle-scenes (Like Playstation Final Fantasy titles)
  • Even faster, even cooler "Mode 7" (3D maps, again)
  • Screen-space shaders (Like in this post-processing demo)
  • Map special effects (Per-pixel lighting, bump mapping, water refraction)
  • Weather effects (Pixel shader rain, snow, heat-waves)
  • 3D menu systems (Even stuff like 3D rotating weapon icons)
  • Awesome battle transitions (FFX or FFXIII style)
The possibilities are limited only by your system requirements, so any graphical effect you can imagine is now possible.

Demo is cool, now what?
Open up the shaders folder, open the "fragment.fsh" file in a text editor like Notepad++ and perhaps modify the pixel shader to make some new post-processing effects.
To enable the debug console (Which will log your shader compile errors) uncomment line 408 in RMVXA-GL (#Lib.Console_Show) to enable the console when testing your game.
There is a crash if you output too much stuff to the console, I am working on this.



Give me feedback please.
 
I tried even if I knew it wouldn't work, make sure you don't call a null function. (Make a GL Version test before launching OpenGL because some people will probably redistribute this without saying it won't work with Intel Chipset x))

8f7e714631.png


Code:
>   00000000()  Unknown

    [Frames below may be incorrect and/or missing]  

    RMVXA-GL.dll!67625063() Unknown

    RMVXA-GL.dll!67624716() Unknown

    RGSS301.dll!100faf3d()  Unknown

    RGSS301.dll!10088e7a()  Unknown

    RGSS301.dll!10088f08()  Unknown

    RGSS301.dll!10089011()  Unknown

    RGSS301.dll!1002cb45()  Unknown

    RGSS301.dll!10027cc9()  Unknown

    RGSS301.dll!10028049()  Unknown

    RGSS301.dll!1002d01a()  Unknown

    RGSS301.dll!1002e10b()  Unknown

    RGSS301.dll!1002fd7b()  Unknown

    RGSS301.dll!100308c0()  Unknown

    RGSS301.dll!10030d10()  Unknown

    RGSS301.dll!10030d3d()  Unknown

    RGSS301.dll!1006a534()  Unknown

    RGSS301.dll!10033e7d()  Unknown

    RGSS301.dll!10027cc9()  Unknown

    RGSS301.dll!10028049()  Unknown

    RGSS301.dll!1002d01a()  Unknown

    RGSS301.dll!1002e10b()  Unknown

    RGSS301.dll!1002fd7b()  Unknown

    RGSS301.dll!100300d9()  Unknown

    RGSS301.dll!100303f7()  Unknown

    RGSS301.dll!10027cc9()  Unknown

    RGSS301.dll!10030962()  Unknown

    RGSS301.dll!10030d10()  Unknown

    RGSS301.dll!10030d3d()  Unknown

    RGSS301.dll!1000c573()  Unknown

    RGSS301.dll!1006a2d9()  Unknown

    RGSS301.dll!10087c13()  Unknown

    RGSS301.dll!1000c52e()  Unknown

    RGSS301.dll!1000c5ee()  Unknown

    RGSS301.dll!1000edc9()  Unknown

    KernelBase.dll!_GetVersionExW@4()  Unknown

    propsys.dll!_NdrClientCall2()  Unknown

    001c48cc()  Unknown

    ntdll.dll!_RegisterGuidsApiCallback@8()    Unknown

    rpcrt4.dll!FreeWrapper(void *)  Unknown

    00000048()  Unknown

    rpcrt4.dll!LRPC_BASE_BINDING_HANDLE::FreeCallToCache(enum _LrpcCachedCallTypes,class LRPC_BASE_CCALL *) Unknown

    rpcrt4.dll!LRPC_BASE_CCALL::FreeObject(void)    Unknown

    rpcrt4.dll!_I_RpcFreeBuffer@4()    Unknown

    rpcrt4.dll!_NdrFreeBuffer@4()  Unknown

    ole32.dll!InternalVerifyStackAvailable(unsigned long Size) Line 317 C

    rpcrt4.dll!_NdrClientCall2()   Unknown

    KernelBase.dll!_WaitForMultipleObjectsEx@20()  Unknown

    kernel32.dll!_WaitForMultipleObjectsExImplementation@20()  Unknown

 
 
Nuri Yuri":26varojb said:
I tried even if I knew it wouldn't work, make sure you don't call a null function. (Make a GL Version test before launching OpenGL because some people will probably redistribute this without saying it won't work with Intel Chipset x))

http://puu.sh/cYKfJ/8f7e714631.png

Code:
>   00000000()  Unknown

    [Frames below may be incorrect and/or missing]  

    RMVXA-GL.dll!67625063() Unknown

    RMVXA-GL.dll!67624716() Unknown

    RGSS301.dll!100faf3d()  Unknown

    RGSS301.dll!10088e7a()  Unknown

    RGSS301.dll!10088f08()  Unknown

    RGSS301.dll!10089011()  Unknown

    RGSS301.dll!1002cb45()  Unknown

    RGSS301.dll!10027cc9()  Unknown

    RGSS301.dll!10028049()  Unknown

    RGSS301.dll!1002d01a()  Unknown

    RGSS301.dll!1002e10b()  Unknown

    RGSS301.dll!1002fd7b()  Unknown

    RGSS301.dll!100308c0()  Unknown

    RGSS301.dll!10030d10()  Unknown

    RGSS301.dll!10030d3d()  Unknown

    RGSS301.dll!1006a534()  Unknown

    RGSS301.dll!10033e7d()  Unknown

    RGSS301.dll!10027cc9()  Unknown

    RGSS301.dll!10028049()  Unknown

    RGSS301.dll!1002d01a()  Unknown

    RGSS301.dll!1002e10b()  Unknown

    RGSS301.dll!1002fd7b()  Unknown

    RGSS301.dll!100300d9()  Unknown

    RGSS301.dll!100303f7()  Unknown

    RGSS301.dll!10027cc9()  Unknown

    RGSS301.dll!10030962()  Unknown

    RGSS301.dll!10030d10()  Unknown

    RGSS301.dll!10030d3d()  Unknown

    RGSS301.dll!1000c573()  Unknown

    RGSS301.dll!1006a2d9()  Unknown

    RGSS301.dll!10087c13()  Unknown

    RGSS301.dll!1000c52e()  Unknown

    RGSS301.dll!1000c5ee()  Unknown

    RGSS301.dll!1000edc9()  Unknown

    KernelBase.dll!_GetVersionExW@4()  Unknown

    propsys.dll!_NdrClientCall2()  Unknown

    001c48cc()  Unknown

    ntdll.dll!_RegisterGuidsApiCallback@8()    Unknown

    rpcrt4.dll!FreeWrapper(void *)  Unknown

    00000048()  Unknown

    rpcrt4.dll!LRPC_BASE_BINDING_HANDLE::FreeCallToCache(enum _LrpcCachedCallTypes,class LRPC_BASE_CCALL *) Unknown

    rpcrt4.dll!LRPC_BASE_CCALL::FreeObject(void)    Unknown

    rpcrt4.dll!_I_RpcFreeBuffer@4()    Unknown

    rpcrt4.dll!_NdrFreeBuffer@4()  Unknown

    ole32.dll!InternalVerifyStackAvailable(unsigned long Size) Line 317 C

    rpcrt4.dll!_NdrClientCall2()   Unknown

    KernelBase.dll!_WaitForMultipleObjectsEx@20()  Unknown

    kernel32.dll!_WaitForMultipleObjectsExImplementation@20()  Unknown

 
Lol you scared me! I see you tried on a GL version below 3.3 (So it would have called a null function)

Testing for GL version is exactly what I'm going to do and then reporting upward to RGSS that the device cannot be supported; I don't want to introduce checks everywhere so I'm going to do a check at context creation and report with a visible error, from there it's up to the scripter to avoid calling any of the API rather than up to the API.

There's no error checking at all right now :L

EDIT: I quickly updated the demo, DL link has been replaced in first post with demo 3
  • Now errors and closes when unsupported GL version found (Thanks for the heads up Nuri)
  • RM Bitmaps are now copied by the int32, rather than int8, this means a theoretical 4x faster texture copying on the GPU (But you won't notice any speed improvements, that snap_to_bitmap is still the #1 killer)
  • Activated the SceneManager finalization modification, so the GL context will now be cleaned up properly
 
The demo 3 doesn't seem to detect I'm using a old OpenGL version :/
Probably you should test if the library exists and if the function exists (in the context initialization).
(Or it came from the RGSS that continued to load the other functions but didn't display me anything ^^')
 
Nuri Yuri":1tj550yc said:
The demo 3 doesn't seem to detect I'm using a old OpenGL version :/
Probably you should test if the library exists and if the function exists (in the context initialization).
(Or it came from the RGSS that continued to load the other functions but didn't display me anything ^^')
The way GL works on windows is you have to make an old 1.4 context before making a new context, when it is 1.4 I grab the major/minor version numbers.
I tested on an old Intel and by requesting versions that don't exist yet, it was all fine this end.

I'll test if the functions I use to get version number is a nullptr too, perhaps you're on a really old GL version that doesn't even support querying the hardware??

Edit: yeah I am an idiot, the version check is gl30+! I'll change to the string test
 
I'm on the Intel OpenGL 2.1 but Intel is making a lot of shit with that so it has a lot of problem ^^'

I'll test the next demo if you want :3 (The verification step :d)
 

Ryaryu

Member

Wow, dude, your work is marvellous. Please don't stop it. Always wanted to link OpenGL to RM window but I never knew how.
I'll try to play with what you've released here... since my gpu can handle it very well.
 
My job is a bit crazy right now as I am releasing an app this week and spending 2 weeks doing R&D, so I can only work on this during weekends (of which I've had very few free).

So progress will be slow.

The next things I'll be doing:
  • Setting shader uniforms <- easy
  • Uploading meshes to GPU <- need to think about how to do a Ruby class for this, I don't want to limit the functionality
  • Drawing meshes to bitmap <- easy

Once that is done I will pull in my matrix research that I am doing for my job right now so we can transform meshes, which at that point this little project is feature complete for what I had planned, it would be then working on GL2.1 compatibility and adding bells and whistles.

EDIT: Because uniforms were easy I made a new demo that uses uniforms to animate a refraction shader;
GL33 Shaders 4
Same requirements as before, needs OpenGL 3.3 for now.
 
Please give this a test if you have a spare few minutes. If it works you should see this;
kjDX4zp.png


The system requirements as as followed;
  • AMD Radeon HD 6350 or later
  • ATI Radeon HD 2350 or later
  • NVidia GeForce 6100 or later
  • First-generation Intel Core graphics or later

OpenGL 2.1 is the minimum supported version! I do not have an OpenGL 2.1 GPU on hand right now, I only have a 3.1 GPU so 3.1 is the recommended OpenGL version if you don't want anything crashing.

Your GPU must support the Frame Buffer Objects extension.
Update to your latest GPU driver, on some 2.1 systems the FBO extension will run in software mode, you will get a significant performance drop if this is the case!


This Demo uses snap_to_bitmap every-frame, this function performs horribly and requires a powerful PC if you want to maintain 60 FPS with this function!
If you want to see how this performs without snap_to_bitmap (It performs very well!) then implement the Hello Triangle tutorial below.
snap_to_bitmap is definitely the current bottleneck in this, the actual 3D rendering is blazing fast.

The Script
This script requires RMVXA-GL.dll which you can get from the demo download above.
Ruby:
#==============================================================================

# ** GL

#------------------------------------------------------------------------------

#  Graphics API interface.

# All functions here should be used to deal with 3D rendering.

# By Felix "Xilefian" Jones [Version a.1]

#==============================================================================

module GL

  DEBUG_CONSOLE = 0 # Set to 1 to show console when debugging

  #==============================================================================

  # ** Texture

  #------------------------------------------------------------------------------

  #  GPU texture class.

  #==============================================================================

  class Texture

    attr_reader :name, :width, :height

    #--------------------------------------------------------------------------

    # * Colour components

    #--------------------------------------------------------------------------

    R = 1

    RG = 2

    RGB = 3

    RGBA = 4

    #--------------------------------------------------------------------------

    # * Allocates a texture with a given component count

    #--------------------------------------------------------------------------

    def self.Alloc( components, width, height )

      Console::Print( "Allocating texture [#{width}, #{height}]" )

      tex = new( OpenGL::GenTexture( components, width, height, 0 ) )

      tex.send( :SetSize, width, height )

      return tex

    end

    #--------------------------------------------------------------------------

    # * Allocates a texture with a given bitmap

    #--------------------------------------------------------------------------

    def self.AllocWithBitmap( bitmap )

      width = bitmap.width

      height = bitmap.height

      handle = bitmap.__id__

      

      Console::Print( "Allocating texture from Bitmap [#{width}, #{height}]" )

      tex = new( OpenGL::GenTexture( 0, 0, 0, handle ) )

      tex.send( :SetSize, width, height )

      return tex

    end

    #--------------------------------------------------------------------------

    # * Destroys the texture

    #--------------------------------------------------------------------------

    def Dealloc

      if @name != 0

        Console::Print( "Deallocating texture" )

        OpenGL::DestroyTex( @name )

        @name = 0

        @width = 0

        @height = 0

      end

    end

    #--------------------------------------------------------------------------

    # * Copies a bitmap into glTexture

    #--------------------------------------------------------------------------

    def LoadBitmap( bitmap )

      OpenGL::TexSubBitmap( @name, bitmap.__id__ )

    end

    #--------------------------------------------------------------------------

    # * Sets texture to GPU unit

    #--------------------------------------------------------------------------

    def BindToUnit( unit )

      OpenGL::BindTextureToUnit( @name, unit )

    end

    #--------------------------------------------------------------------------

    # * Sets the texture size (!PRIVATE!)

    #--------------------------------------------------------------------------

    def SetSize( width, height )

      @width = width

      @height = height

    end

    private :SetSize

    #--------------------------------------------------------------------------

    # * Initialize (!PRIVATE!)

    #--------------------------------------------------------------------------

    private_class_method :new

    def initialize( name )

      @name = name

      @width = 0

      @height = 0

    end

  end

  #==============================================================================

  # ** Shader

  #------------------------------------------------------------------------------

  #  Class for compiling shaders.

  #==============================================================================

  class Shader

    attr_reader :name, :type

    #--------------------------------------------------------------------------

    # * Shader types

    #--------------------------------------------------------------------------

    VERTEX = 1

    GEOMETRY = 2

    FRAGMENT = 3

    #--------------------------------------------------------------------------

    # * Creates vertex shader

    #--------------------------------------------------------------------------

    def self.CreateVertex( src )

      Console::Print( "Creating Vertex shader" )

      shader = new( OpenGL::CreateShader( VERTEX ), VERTEX )

      status = shader.send( :Compile, src )

      if status != 0

        shader.Dealloc

        return nil

      else

        return shader

      end

    end

    #--------------------------------------------------------------------------

    # * Creates geometry shader

    #--------------------------------------------------------------------------

    def self.CreateGeometry( src )

      Console::Print( "Creating Geometry shader" )

      shader = new( OpenGL::CreateShader( GEOMETRY ), GEOMETRY )

      status = shader.send( :Compile, src )

      if status != 0

        shader.Dealloc

        return nil

      else

        return shader

      end

    end

    #--------------------------------------------------------------------------

    # * Creates fragment shader

    #--------------------------------------------------------------------------

    def self.CreateFragment( src )

      Console::Print( "Creating Fragment shader" )

      shader = new( OpenGL::CreateShader( FRAGMENT ), FRAGMENT )

      status = shader.send( :Compile, src )

      if status != 0

        shader.Dealloc

        return nil

      else

        return shader

      end

    end

    #--------------------------------------------------------------------------

    # * Deletes shader

    #--------------------------------------------------------------------------

    def Dealloc

      Console::Print( "Deallocating shader" )

      OpenGL::DestroyShader( @name )

      @name = 0

      @type = 0

    end

    #--------------------------------------------------------------------------

    # * Compiles the shader source (!PRIVATE!)

    #--------------------------------------------------------------------------

    def Compile( src )

      return OpenGL::ShaderCompile( @name, src )

    end

    private :Compile

    #--------------------------------------------------------------------------

    # * Initialize (!PRIVATE!)

    #--------------------------------------------------------------------------

    private_class_method :new

    def initialize( name, type )

      @name = name

      @type = type

    end

  end

  #==============================================================================

  # ** ShaderMaterial

  #------------------------------------------------------------------------------

  #  Class for managing shader programs.

  #==============================================================================

  class ShaderMaterial

    @@current_shader = nil

    #--------------------------------------------------------------------------

    # * Current bound shader handle

    #--------------------------------------------------------------------------

    def self.GetCurrentShader

      return @@current_shader

    end

    #--------------------------------------------------------------------------

    # * Current bound shader handle

    #--------------------------------------------------------------------------

    def self.SetCurrentShader( shader )

      @@current_shader = shader

    end

    #--------------------------------------------------------------------------

    # * Creates a shader program

    #--------------------------------------------------------------------------

    def self.Create( vertex, fragment )

      Console::Print( "Creating vertex/fragment program" )

      material = new( OpenGL::CreateProgam() )

      

      material.send( :AttachShaders, vertex, fragment, nil )

      

      return material

    end

    #--------------------------------------------------------------------------

    # * Creates a shader program with a geometry shader

    #--------------------------------------------------------------------------

    def self.CreateWithGeometry( vertex, geometry, fragment )

      Console::Print( "Creating vertex/geometry/fragment program" )

      material = new( OpenGL::CreateProgam() )

      

      material.send( :AttachShaders, vertex, fragment, geometry )

      

      return material

    end

    #--------------------------------------------------------------------------

    # * Attach Shaders

    #--------------------------------------------------------------------------

    def AttachShaders( vsh, fsh, gsh )

      @vsh = vsh

      @fsh = fsh

      @gsh = gsh

    end

    #--------------------------------------------------------------------------

    # * Destroys this program

    #--------------------------------------------------------------------------

    def Dealloc

      Console::Print( "Deallocating program" )

      OpenGL::DestroyProg( @name )

      @name = 0

      @vsh = nil

      @gsh = nil

      @fsh = nil

      @uniformMap = nil

      @uniLocationMap = nil

    end

    #--------------------------------------------------------------------------

    # * Activates this shader for rendering

    #--------------------------------------------------------------------------

    def UseProgram

      ShaderMaterial.SetCurrentShader( self )

      

      if @isLinked == false

        status = self.send( :Link )

        if status != 0

          self.Dealloc

          return

        end

        @isLinked = true

      end

      

      # Use program

      OpenGL::UseProgram( @name )

      

      # Update uniforms

      self.UploadUniforms

    end

    #--------------------------------------------------------------------------

    # * Uploads Uniforms to current shader

    #--------------------------------------------------------------------------

    def UploadUniforms

      @uniformMap.each do |uniformName, value|

        case value

          when Float

            OpenGL::SetUniform1f( uniformName, [value] )

          when Integer

            OpenGL::SetUniform1i( uniformName, value )

          when Array

            # Check first value of array, assume that is what we are uploading

            case value.count

              when 1

                case value[0]

                  when Float

                    OpenGL::SetUniform1f( uniformName, value )

                  when Integer

                    OpenGL::SetUniform1i( uniformName, value )

                end

              when 2

                case value[0]

                  when Float

                    OpenGL::SetUniform2f( uniformName, value )

                  when Integer

                    OpenGL::SetUniform2i( uniformName, value )

                  when Matrix

                    case value[1]

                      when 2

                        value[0].UploadMatrix2( uniformName )

                      when 3

                        value[0].UploadMatrix3( uniformName )

                      when 4

                        value[0].UploadMatrix4( uniformName )

                    end

                end

              when 3

                case value[0]

                  when Float

                    OpenGL::SetUniform3f( uniformName, value )

                  when Integer

                    OpenGL::SetUniform3i( uniformName, value )

                end

              when 4

                case value[0]

                  when Float

                    OpenGL::SetUniform4f( uniformName, value )

                  when Integer

                    OpenGL::SetUniform4i( uniformName, value )

                end

            end

        end

      end

      

      @uniformMap.clear

    end

    #--------------------------------------------------------------------------

    # * Sets a program uniform to a value

    #--------------------------------------------------------------------------

    def SetUniform( uniformName, value )

      if @isLinked == false

        status = self.send( :Link )

        if status != 0

          self.Dealloc

          return

        end

        @isLinked = true

      end

      

      if @uniLocationMap[uniformName] == nil

        intVal = OpenGL::GetUniformLocation( @name, uniformName )

        Console::Print( "Found uniform #{uniformName} == #{intVal}" )

        

        @uniLocationMap[uniformName] = intVal

      else

        intVal = @uniLocationMap[uniformName]

      end

      

      @uniformMap[intVal] = value

      

      if ShaderMaterial.GetCurrentShader() == self

        self.UploadUniforms

      end

    end 

    #--------------------------------------------------------------------------

    # * Sets a program uniform to a mat2

    #--------------------------------------------------------------------------

    def SetUniformMatrix2( uniformName, matrix )

      if @uniLocationMap[uniformName] == nil

        intVal = OpenGL::GetUniformLocation( @name, uniformName )

        

        @uniLocationMap[uniformName] = intVal

      else

        intVal = @uniLocationMap[uniformName]

      end

      

      @uniformMap[intVal] = [matrix, 2]

      

      if ShaderMaterial.GetCurrentShader() == self

        self.UploadUniforms

      end

    end 

    #--------------------------------------------------------------------------

    # * Sets a program uniform to a mat3

    #--------------------------------------------------------------------------

    def SetUniformMatrix3( uniformName, matrix )

      if @uniLocationMap[uniformName] == nil

        intVal = OpenGL::GetUniformLocation( @name, uniformName )

        

        @uniLocationMap[uniformName] = intVal

      else

        intVal = @uniLocationMap[uniformName]

      end

      

      @uniformMap[intVal] = [matrix, 3]

      

      if ShaderMaterial.GetCurrentShader() == self

        self.UploadUniforms

      end

    end 

    #--------------------------------------------------------------------------

    # * Sets a program uniform to a mat4

    #--------------------------------------------------------------------------

    def SetUniformMatrix4( uniformName, matrix )

      if @uniLocationMap[uniformName] == nil

        intVal = OpenGL::GetUniformLocation( @name, uniformName )

        

        @uniLocationMap[uniformName] = intVal

      else

        intVal = @uniLocationMap[uniformName]

      end

      

      @uniformMap[intVal] = [matrix, 4]

      

      if ShaderMaterial.GetCurrentShader() == self

        self.UploadUniforms

      end

    end 

    #--------------------------------------------------------------------------

    # * Sets a program attribute to an index

    #--------------------------------------------------------------------------

    def SetAttributeIndex( attribStr, index )

      if @isLinked == true

        Console::Print( "ERROR - Tried to set attribute location of linked shader!" )

        Console::Print( "- Call .SetAttributeIndex BEFORE linking program!" )

        return

      end

      

      Console::Print( "Setting attribute #{attribStr} to #{index}" )

      OpenGL::BindAttribLocation( @name, index, attribStr )

    end

    #--------------------------------------------------------------------------

    # * Links shaders to this program (!PRIVATE!)

    #--------------------------------------------------------------------------

    def Link

      if @gsh

        return OpenGL::ProgLink( @name, @vsh.name, @gsh.name, @fsh.name )

      else

        return OpenGL::ProgLink( @name, @vsh.name, 0, @fsh.name )

      end

    end

    private :Link

    private :AttachShaders

    #--------------------------------------------------------------------------

    # * Initialize (!PRIVATE!)

    #--------------------------------------------------------------------------

    private_class_method :new

    def initialize( name )

      @name = name

      @vsh = nil

      @gsh = nil

      @fsh = nil

      @isLinked = false

      @uniformMap = Hash.new

      @uniLocationMap = Hash.new

    end

  end

  #==============================================================================

  # ** VertexBuffer

  #------------------------------------------------------------------------------

  #  Class for uploading vertex data to the GPU.

  #==============================================================================

  class VertexBuffer

    #--------------------------------------------------------------------------

    # * Draw Modes

    #--------------------------------------------------------------------------

    DRAW_POINTS = 1 # Point cloud

    DRAW_LINES = 2 # Pairs of verts = line

    DRAW_LINE_STRIP = 3 # Verts = line path

    DRAW_LINE_LOOP = 4 # Verts = line path, ending back at first

    DRAW_TRIANGLES = 5 # 3 verts = triangle

    DRAW_TRIANGLE_STRIP = 6 # Each new vert makes a triangle from the last two

    DRAW_TRIANGLE_FAN = 7 # Each new vert makes a traingle from last + first

    #--------------------------------------------------------------------------

    # * Creates the GPU VBO

    #--------------------------------------------------------------------------

    def self.Alloc

      Console::Print( "Allocating VertexBuffer" )

      return new

    end

    #--------------------------------------------------------------------------

    # * Deletes the GPU handles for this VBO

    #--------------------------------------------------------------------------

    def Dealloc

      Console::Print( "Deallocating VertexBuffer" )

      OpenGL::DestroyVBO( @name )

    end

    #--------------------------------------------------------------------------

    # * Updates this VBO and then renders it

    #--------------------------------------------------------------------------

    def DrawArrays( drawMode )

      OpenGL::BindVBO( @name )

      

      if @vertexArray

        OpenGL::VBOData( @vertexArray );

        

        @vertexArray = nil

      end

            

      index = 0

      offset = 0

      @description.each do |arg|

        OpenGL::VBOAttrib( index, arg, @stride, offset )

        

        offset += arg

        index += 1

      end

      

      OpenGL::DrawVBO( drawMode, @vertexCount )

    end

    #--------------------------------------------------------------------------

    # * Sets how the buffer should be read

    # * eg: vbo.DescribeBuffer( 3, [3, 2, 4] ) # 3 verts = vec3, vec2, vec4

    #--------------------------------------------------------------------------

    def DescribeBuffer( vertexCount, args )

      @stride = 0

      args.each { |a| @stride += a }

      

      @vertexCount = vertexCount

      @description = args

    end

    #--------------------------------------------------------------------------

    # * Uploads a given vertex array to this buffer object

    #--------------------------------------------------------------------------

    def UploadVertexArray( vertexArray )

      @vertexArray = vertexArray.flatten

    end

    #--------------------------------------------------------------------------

    # * Initialize (!PRIVATE!)

    #--------------------------------------------------------------------------

    private_class_method :new

    def initialize

      @name = OpenGL::GenVBO()

      @stride = 0

      @vertexCount = 0

      @vertexArray = nil

      @description = nil

    end

  end

  #==============================================================================

  # ** FrameBuffer

  #------------------------------------------------------------------------------

  #  Class for creating frame buffers to render to.

  #==============================================================================

  class FrameBuffer

    attr_reader :name, :width, :height

    #--------------------------------------------------------------------------

    # * Clear Modes

    #--------------------------------------------------------------------------

    Clear = 1

    #--------------------------------------------------------------------------

    # * Creates the GPU FB with no attachments

    #--------------------------------------------------------------------------

    def self.Alloc( width, height )

      Console::Print( "Allocating FrameBuffer [#{width}, #{height}]" )

      return new( width, height )

    end

    #--------------------------------------------------------------------------

    # * Deletes the GPU handles for this FBO

    #--------------------------------------------------------------------------

    def Dealloc

      Console::Print( "Deallocating FrameBuffer" )

      OpenGL::DestroyFBO( @name )

      @width = 0

      @height = 0

      @colourAttachment0 = nil

      @name = 0

    end

    #--------------------------------------------------------------------------

    # * Copies a frame buffer contents into bitmap

    #--------------------------------------------------------------------------

    def ToBitmap( bitmap )

      OpenGL::GetTexImage( @name, bitmap.__id__ )

    end

    #--------------------------------------------------------------------------

    # * Attaches a texture to this FBO

    #--------------------------------------------------------------------------

    def AttachTexture( texture )

      if texture.width != @width || texture.height != @height

        message = "Expected size [#{@width}, #{@height}] " <<

        "given size [#{texture.width}, #{texture.height}]"

        

        Console::Print( "FrameBuffer attach failure!" )

        Console::Print( message )

      else

        oldAttachment = @colourAttachment0

        @colourAttachment0 = texture

        Console::Print( "Attaching Texture to FrameBuffer" )

        OpenGL::FBOTexure( @name, texture.name )

        if oldAttachment != nil

          return oldAttachment

        end          

      end

    end

    #--------------------------------------------------------------------------

    # * Test method for rendering to FBO

    #--------------------------------------------------------------------------

    def DrawShader( shader )

      shader.UseProgram

      OpenGL::DrawToFBO( @name )

    end

    #--------------------------------------------------------------------------

    # * Makes this FBO the render target

    #--------------------------------------------------------------------------

    def Bind( willClear = 0 )

      OpenGL::BindFBO( @name )

      if willClear == 1

        OpenGL::Clear()

      end

    end

    #--------------------------------------------------------------------------

    # * Initialize (!PRIVATE!)

    #--------------------------------------------------------------------------

    private_class_method :new

    def initialize( width, height )

      @width = width

      @height = height

      @colourAttachment0 = nil

      @name = OpenGL::GenFBO()

    end

  end

  #==============================================================================

  # ** OpenGL

  #------------------------------------------------------------------------------

  #  OpenGL management.

  # Object life-time for 3D elements managed here.

  #==============================================================================

  class OpenGL

    #--------------------------------------------------------------------------

    # * Starts up the Graphics interface

    #--------------------------------------------------------------------------

    def initialize

      initStatus = Lib.XGL_Init

      

      case initStatus

        when 1

          SceneManager.exit_error( "Unsupported GL version\nOpenGL 2.1 Required" )

        when 2

          SceneManager.exit_error( "Missing extension FrameBufferObject" )

      end

    end

    #--------------------------------------------------------------------------

    # * Clears current buffer

    #--------------------------------------------------------------------------

    def self.Clear

      Lib.Graphics_Clear()

    end

    #--------------------------------------------------------------------------

    # * Create a glTexImage

    #--------------------------------------------------------------------------

    def self.DestroyTex( texName )

      Lib.Graphics_DestroyTex( texName )

    end

    #--------------------------------------------------------------------------

    # * Create a glTexImage

    #--------------------------------------------------------------------------

    def self.GenTexture( components, width, height, bmpHandle )

      return Lib.Graphics_GenTexture( components, width, height, bmpHandle )

    end

    #--------------------------------------------------------------------------

    # * Loads a bitmap into a glTexImage

    #--------------------------------------------------------------------------

    def self.TexSubBitmap( texName, bmpHandle )

      return Lib.Graphics_TexSubBitmap( texName, bmpHandle )

    end

    #--------------------------------------------------------------------------

    # * Dumps a glTexImage into a bitmap

    #--------------------------------------------------------------------------

    def self.GetTexImage( texName, bmpHandle )

      return Lib.Graphics_GetTexImage( texName, bmpHandle )

    end

    #--------------------------------------------------------------------------

    # * Creates a new FBO

    #--------------------------------------------------------------------------

    def self.GenFBO

      return Lib.Graphics_GenFBO

    end

    #--------------------------------------------------------------------------

    # * Creates a new FBO

    #--------------------------------------------------------------------------

    def self.DestroyFBO( fboName )

      Lib.Graphics_DestroyFBO( fboName )

    end

    #--------------------------------------------------------------------------

    # * Attaches texture to FBO

    #--------------------------------------------------------------------------

    def self.FBOTexure( fboName, texName )

      Lib.Graphics_FBOTexure( fboName, texName )

    end

    #--------------------------------------------------------------------------

    # * Creates a new shader of given type

    #--------------------------------------------------------------------------

    def self.CreateShader( shaderType )

      return Lib.Graphics_CreateShader( shaderType )

    end

    #--------------------------------------------------------------------------

    # * Deletes glShader

    #--------------------------------------------------------------------------

    def self.DestroyShader( shaderName )

      Lib.Graphics_DestroyShader( shaderName )

    end

    #--------------------------------------------------------------------------

    # * Compiles shader source and returns the result

    #--------------------------------------------------------------------------

    def self.ShaderCompile( shaderName, src )

      return Lib.Graphics_ShaderCompile( shaderName, src )

    end

    #--------------------------------------------------------------------------

    # * Creates a shader program

    #--------------------------------------------------------------------------

    def self.CreateProgam

      return Lib.Graphics_CreateProgam

    end

    #--------------------------------------------------------------------------

    # * Links shaders to this program

    #--------------------------------------------------------------------------

    def self.ProgLink( progName, vsh, gsh, fsh )

      return Lib.Graphics_ProgLink( progName, vsh, gsh, fsh )

    end

    #--------------------------------------------------------------------------

    # * Deletes a shader program

    #--------------------------------------------------------------------------

    def self.DestroyProg( progName )

      return Lib.Graphics_DestroyProg( progName )

    end

    #--------------------------------------------------------------------------

    # * Deletes a shader program

    #--------------------------------------------------------------------------

    def self.GetUniformLocation( progName, uniString )

      return Lib.Graphics_UniformLocation( progName,  uniString )

    end

    #--------------------------------------------------------------------------

    # * Draws texName to fboName using progName

    #--------------------------------------------------------------------------

    def self.DrawToFBO( fboName )

      Lib.Graphics_DrawToFBO( fboName )

    end

    #--------------------------------------------------------------------------

    # * Sets program uniform value at location

    #--------------------------------------------------------------------------

    def self.SetUniform1f( name, args )

      larr = args.pack("f").unpack("l")

      Lib.Graphics_Uniform1f( name, larr[0] )

    end

    #--------------------------------------------------------------------------

    # * Sets program uniform value at location

    #--------------------------------------------------------------------------

    def self.SetUniform2f( name, args )

      larr = args.pack( "ff" ).unpack( "ll" )

      Lib.Graphics_Uniform2f( name, larr[0], larr[1] )

    end

    #--------------------------------------------------------------------------

    # * Sets program uniform value at location

    #--------------------------------------------------------------------------

    def self.SetUniform3f( name, args )

      larr = args.pack( "fff" ).unpack( "lll" )

      Lib.Graphics_Uniform3f( name, larr[0], larr[1], larr[2] )

    end

    #--------------------------------------------------------------------------

    # * Sets program uniform value at location

    #--------------------------------------------------------------------------

    def self.SetUniform4f( name, args )

      larr = args.pack( "ffff" ).unpack( "llll" )

      Lib.Graphics_Uniform4f( name, larr [0], larr [1], larr[2], larr[3] )

    end

    #--------------------------------------------------------------------------

    # * Sets program uniform value at location

    #--------------------------------------------------------------------------

    def self.SetUniform1i( name, args )

      Lib.Graphics_Uniform1i( name, args )

    end

    #--------------------------------------------------------------------------

    # * Sets program uniform value at location

    #--------------------------------------------------------------------------

    def self.SetUniform2i( name, args )

      Lib.Graphics_Uniform2i( name, args[0], args[1] )

    end

    #--------------------------------------------------------------------------

    # * Sets program uniform value at location

    #--------------------------------------------------------------------------

    def self.SetUniform3i( name, args )

      Lib.Graphics_Uniform3i( name, args[0], args[1], args[2] )

    end

    #--------------------------------------------------------------------------

    # * Sets program uniform value at location

    #--------------------------------------------------------------------------

    def self.SetUniform4i( name, args )

      Lib.Graphics_Uniform4i( name, args[0], args[1], args[2], args[3])

    end

    #--------------------------------------------------------------------------

    # * Activates a shader program for usage (And modifying)

    #--------------------------------------------------------------------------

    def self.UseProgram( shaderName )

      Lib.Graphics_UseProgram( shaderName )

    end

    #--------------------------------------------------------------------------

    # * Binds (and activates) a texture to a given unit

    #--------------------------------------------------------------------------

    def self.BindTextureToUnit( textureName, unitIndex )

      Lib.Graphics_BindTexUnit( textureName, unitIndex )

    end

    #--------------------------------------------------------------------------

    # * Creats a new VBO (Must be destroyed!)

    #--------------------------------------------------------------------------

    def self.GenVBO

      return Lib.Graphics_GenVBO

    end

    #--------------------------------------------------------------------------

    # * Destroys a VBO

    #--------------------------------------------------------------------------

    def self.DestroyVBO( vboName )

      Lib.Graphics_DestroyVBO( vboName )

    end

    #--------------------------------------------------------------------------

    # * Binds a VBO (Making it current)

    #--------------------------------------------------------------------------

    def self.BindVBO( vboName )

      Lib.Graphics_BindVBO( vboName )

    end

    #--------------------------------------------------------------------------

    # * Uploads the vertex data for current VBO

    #--------------------------------------------------------------------------

    def self.VBOData( floatArray )

      stringPacker = "f" * floatArray.length

      floatString = floatArray.pack( stringPacker )

 

      Lib.Graphics_VBOData( floatString, floatArray.length )

    end

    #--------------------------------------------------------------------------

    # * Applies the attribute data for current VBO

    #--------------------------------------------------------------------------

    def self.VBOAttrib( index, components, stride, offset )

      Lib.Graphics_VBOAttrib( index, components, stride, offset )

    end

    #--------------------------------------------------------------------------

    # * Draws the current VBO

    #--------------------------------------------------------------------------

    def self.DrawVBO( mode, count )

      Lib.Graphics_DrawVBO( mode, count )

    end

    #--------------------------------------------------------------------------

    # * Draws the current VBO

    #--------------------------------------------------------------------------

    def self.BindAttribLocation( shaderName, index, attribStr )

      Lib.Graphics_BindAttribLocation( shaderName, index, attribStr )

    end

    #--------------------------------------------------------------------------

    # * Binds a FBO (Target render buffer)

    #--------------------------------------------------------------------------

    def self.BindFBO( fboName )

      Lib.Graphics_BindFBO( fboName )

    end

  end

  #==============================================================================

  # ** Matrix

  #------------------------------------------------------------------------------

  #  Manipulates and uploads matrices

  #==============================================================================

  class Matrix

    #--------------------------------------------------------------------------

    # * Wrapper for Matrix.new

    #--------------------------------------------------------------------------

    def self.Alloc

      matrix = new()

      matrix.Push

      return matrix

    end

    #--------------------------------------------------------------------------

    # * Pushes an identity matrix to the top

    #--------------------------------------------------------------------------

    def Push

      Lib.Matrix_Push( @name )

    end

    #--------------------------------------------------------------------------

    # * Removes the top matrix

    #--------------------------------------------------------------------------

    def Pop

      Lib.Matrix_Pop( @name )

    end

    #--------------------------------------------------------------------------

    # * Uploads the top matrix to the current shader's mat2 at location

    #--------------------------------------------------------------------------

    def UploadMatrix2( location )

      Lib.Matrix_UploadMatrix2( @name, location )

    end

    #--------------------------------------------------------------------------

    # * Uploads the top matrix to the current shader's mat3 at location

    #--------------------------------------------------------------------------

    def UploadMatrix3( location )

      Lib.Matrix_UploadMatrix3( @name, location )

    end

    #--------------------------------------------------------------------------

    # * Uploads the top matrix to the current shader's mat4 at location

    #--------------------------------------------------------------------------

    def UploadMatrix4( location )

      Lib.Matrix_UploadMatrix4( @name, location )

    end

    #--------------------------------------------------------------------------

    # * Translates the matrix

    #--------------------------------------------------------------------------

    def Translate( x, y, z )

      larr = [x, y, z].pack("fff").unpack("lll")

      Lib.Matrix_Translate( @name, larr[0], larr[1], larr[2] )

    end

    #--------------------------------------------------------------------------

    # * Scales the matrix

    #--------------------------------------------------------------------------

    def Scale( x, y, z )

      larr = [x, y, z].pack("fff").unpack("lll")

      Lib.Matrix_Scale( @name, larr[0], larr[1], larr[2] )

    end

    #--------------------------------------------------------------------------

    # * Rotates the matrix in the X axis by radian

    #--------------------------------------------------------------------------

    def RotateX( radian )

      larr = [radian].pack("f").unpack("l")

      Lib.Matrix_RotateX( @name, larr[0] )

    end

    #--------------------------------------------------------------------------

    # * Rotates the matrix in the Y axis by radian

    #--------------------------------------------------------------------------

    def RotateY( radian )

      larr = [radian].pack("f").unpack("l")

      Lib.Matrix_RotateY( @name, larr[0] )

    end

    #--------------------------------------------------------------------------

    # * Rotates the matrix in the Z axis by radian

    #--------------------------------------------------------------------------

    def RotateZ( radian )

      larr = [radian].pack("f").unpack("l")

      Lib.Matrix_RotateZ( @name, larr[0] )

    end

    #--------------------------------------------------------------------------

    # * Destroys the matrix

    #--------------------------------------------------------------------------

    def Dealloc

      Lib.Matrix_Destroy( @name )

    end

    #--------------------------------------------------------------------------

    # * Creates the matrix handle

    #--------------------------------------------------------------------------

    private_class_method :new

    def initialize

      @name = Lib.Matrix_Create()

    end

  end

  #==============================================================================

  # ** Console

  #------------------------------------------------------------------------------

  #  Debug console, feel free to ignore.

  #==============================================================================

  class Console

    #--------------------------------------------------------------------------

    # * When in test mode this will show the console

    #--------------------------------------------------------------------------

    def initialize

      if $TEST && DEBUG_CONSOLE == 1

        Lib.Console_Show

      end

    end

    #--------------------------------------------------------------------------

    # * Prints debug text

    #--------------------------------------------------------------------------

    def self.Print( str )

      return Lib.Console_Print( "[RMVXA-GL] #{str}" )

    end

  end

  #==============================================================================

  # ** Lib

  #------------------------------------------------------------------------------

  #  DLL library interface here.

  # This should generally be considered private.

  #==============================================================================

  module Lib

    #--------------------------------------------------------------------------

    # * Library API methods

    #--------------------------------------------------------------------------

    def self.XGL_Init( *args );return @@XGL_Init.call( *args );end

    def self.XGL_Terminate( *args );return @@XGL_Terminate.call( *args );end

      

    def self.Console_Show( *args );return @@Console_Show.call( *args );end

    def self.Console_Hide( *args );return @@Console_Hide.call( *args );end

    def self.Console_Print( *args );return @@Console_Print.call( *args );end

    

    def self.Graphics_Clear( *args );return @@Graphics_Clear.call( *args );end

      

    def self.Graphics_GenTexture( *args );return @@Graphics_GenTexture.call( *args );end

    def self.Graphics_TexSubBitmap( *args );return @@Graphics_TexSubBitmap.call( *args );end

    def self.Graphics_DestroyTex( *args );return @@Graphics_DestroyTex.call( *args );end

    def self.Graphics_BindTexUnit( *args );return @@Graphics_BindTexUnit.call( *args );end

      

    def self.Graphics_GenFBO( *args );return @@Graphics_GenFBO.call( *args );end

    def self.Graphics_FBOTexure( *args );return @@Graphics_FBOTexure.call( *args );end

    def self.Graphics_GetTexImage( *args );return @@Graphics_GetTexImage.call( *args );end

    def self.Graphics_DestroyFBO( *args );return @@Graphics_DestroyFBO.call( *args );end

    def self.Graphics_BindFBO( *args );return @@Graphics_BindFBO.call( *args );end

      

    def self.Graphics_CreateShader( *args );return @@Graphics_CreateShader.call( *args );end

    def self.Graphics_DestroyShader( *args );return @@Graphics_DestroyShader.call( *args );end

    def self.Graphics_ShaderCompile( *args );return @@Graphics_ShaderCompile.call( *args );end

      

    def self.Graphics_CreateProgam( *args );return @@Graphics_CreateProgam.call( *args );end

    def self.Graphics_ProgLink( *args );return @@Graphics_ProgLink.call( *args );end

    def self.Graphics_DestroyProg( *args );return @@Graphics_DestroyProg.call( *args );end

    def self.Graphics_UniformLocation( *args );return @@Graphics_UniformLocation.call( *args );end

    

    def self.Graphics_Uniform1f( *args );return @@Graphics_Uniform1f.call( *args );end

    def self.Graphics_Uniform2f( *args );return @@Graphics_Uniform2f.call( *args );end

    def self.Graphics_Uniform3f( *args );return @@Graphics_Uniform3f.call( *args );end

    def self.Graphics_Uniform4f( *args );return @@Graphics_Uniform4f.call( *args );end

    

    def self.Graphics_Uniform1i( *args );return @@Graphics_Uniform1i.call( *args );end

    def self.Graphics_Uniform2i( *args );return @@Graphics_Uniform2i.call( *args );end

    def self.Graphics_Uniform3i( *args );return @@Graphics_Uniform3i.call( *args );end

    def self.Graphics_Uniform4i( *args );return @@Graphics_Uniform4i.call( *args );end

    

    def self.Graphics_Uniform1ui( *args );return @@Graphics_Uniform1ui.call( *args );end

    def self.Graphics_Uniform2ui( *args );return @@Graphics_Uniform2ui.call( *args );end

    def self.Graphics_Uniform3ui( *args );return @@Graphics_Uniform3ui.call( *args );end

    def self.Graphics_Uniform4ui( *args );return @@Graphics_Uniform4ui.call( *args );end

      

    def self.Graphics_UseProgram( *args );return @@Graphics_UseProgram.call( *args );end

    def self.Graphics_BindAttribLocation( *args );return @@Graphics_BindAttribLocation.call( *args );end

      

    def self.Graphics_GenVBO( *args );return @@Graphics_GenVBO.call( *args );end

    def self.Graphics_DestroyVBO( *args );return @@Graphics_DestroyVBO.call( *args );end

    def self.Graphics_BindVBO( *args );return @@Graphics_BindVBO.call( *args );end

    def self.Graphics_VBOData( *args );return @@Graphics_VBOData.call( *args );end

    def self.Graphics_VBOAttrib( *args );return @@Graphics_VBOAttrib.call( *args );end

    def self.Graphics_DrawVBO( *args );return @@Graphics_DrawVBO.call( *args );end

      

    def self.Matrix_Create( *args );return @@Matrix_Create.call( *args );end

    def self.Matrix_Destroy( *args );return @@Matrix_Destroy.call( *args );end

      

    def self.Matrix_Pop( *args );return @@Matrix_Pop.call( *args );end

    def self.Matrix_Push( *args );return @@Matrix_Push.call( *args );end

    def self.Matrix_Size( *args );return @@Matrix_Size.call( *args );end

      

    def self.Matrix_UploadMatrix2( *args );return @@Matrix_UploadMatrix2.call( *args );end

    def self.Matrix_UploadMatrix3( *args );return @@Matrix_UploadMatrix3.call( *args );end

    def self.Matrix_UploadMatrix4( *args );return @@Matrix_UploadMatrix4.call( *args );end

      

    def self.Matrix_LoadMatrix4( *args );return @@Matrix_LoadMatrix4.call( *args );end

    def self.Matrix_MultiplyMatrix4( *args );return @@Matrix_MultiplyMatrix4.call( *args );end

    def self.Matrix_MultiplyMatrixStack( *args );return @@Matrix_MultiplyMatrixStack.call( *args );end

    def self.Matrix_Rotate( *args );return @@Matrix_Rotate.call( *args );end

    def self.Matrix_RotateX( *args );return @@Matrix_RotateX.call( *args );end

    def self.Matrix_RotateY( *args );return @@Matrix_RotateY.call( *args );end

    def self.Matrix_RotateZ( *args );return @@Matrix_RotateZ.call( *args );end

    def self.Matrix_Scale( *args );return @@Matrix_Scale.call( *args );end

    def self.Matrix_Translate( *args );return @@Matrix_Translate.call( *args );end

    #--------------------------------------------------------------------------

    # * Library name, read as LIB_NAME.dll

    #--------------------------------------------------------------------------

    LIB_NAME = "RMVXA-GL"

    #--------------------------------------------------------------------------

    # * Win32API function pointer bindings

    #--------------------------------------------------------------------------

    @@XGL_Init = Win32API.new( LIB_NAME, "XGL_Init", "v", "i" )

    @@XGL_Terminate = Win32API.new( LIB_NAME, "XGL_Terminate", "v", "v" )

    

    @@Console_Show = Win32API.new( LIB_NAME, "Console_Show", "v", "v" )

    @@Console_Hide = Win32API.new( LIB_NAME, "Console_Hide", "v", "v" )

    @@Console_Print = Win32API.new( LIB_NAME, "Console_Print", "p", "v" )

    

    @@Graphics_Clear = Win32API.new( LIB_NAME, "Graphics_Clear", "v", "v" )

    

    @@Graphics_GenTexture = Win32API.new( LIB_NAME, "Graphics_GenTexture", "iiip", "i" )

    @@Graphics_TexSubBitmap = Win32API.new( LIB_NAME, "Graphics_TexSubBitmap", "ip", "v" )

    @@Graphics_DestroyTex = Win32API.new( LIB_NAME, "Graphics_DestroyTex", "i", "v" )

    @@Graphics_BindTexUnit = Win32API.new( LIB_NAME, "Graphics_BindTexUnit", "ii", "v" )

    

    @@Graphics_GenFBO = Win32API.new( LIB_NAME, "Graphics_GenFBO", "v", "i" )

    @@Graphics_FBOTexure = Win32API.new( LIB_NAME, "Graphics_FBOTexure", "ii", "v" )

    @@Graphics_GetTexImage = Win32API.new( LIB_NAME, "Graphics_GetTexImage", "ip", "v" )

    @@Graphics_DestroyFBO = Win32API.new( LIB_NAME, "Graphics_DestroyFBO", "i", "v" )

    @@Graphics_BindFBO = Win32API.new( LIB_NAME, "Graphics_BindFBO", "i", "v" )

    

    @@Graphics_CreateShader = Win32API.new( LIB_NAME, "Graphics_CreateShader", "i", "i" )

    @@Graphics_DestroyShader = Win32API.new( LIB_NAME, "Graphics_DestroyShader", "i", "v" )

    @@Graphics_ShaderCompile = Win32API.new( LIB_NAME, "Graphics_ShaderCompile", "ip", "i" )

    

    @@Graphics_CreateProgam = Win32API.new( LIB_NAME, "Graphics_CreateProgam", "v", "i" )

    @@Graphics_ProgLink = Win32API.new( LIB_NAME, "Graphics_ProgLink", "iiii", "i" )

    @@Graphics_DestroyProg = Win32API.new( LIB_NAME, "Graphics_DestroyProg", "i", "v" )

    @@Graphics_UniformLocation = Win32API.new( LIB_NAME, "Graphics_UniformLocation", "ip", "i" )

    

    @@Graphics_Uniform1f = Win32API.new( LIB_NAME, "Graphics_Uniform1f", "ii", "v" )

    @@Graphics_Uniform2f = Win32API.new( LIB_NAME, "Graphics_Uniform2f", "iii", "v" )

    @@Graphics_Uniform3f = Win32API.new( LIB_NAME, "Graphics_Uniform3f", "iiii", "v" )

    @@Graphics_Uniform4f = Win32API.new( LIB_NAME, "Graphics_Uniform4f", "iiiii", "v" )

    

    @@Graphics_Uniform1i = Win32API.new( LIB_NAME, "Graphics_Uniform1i", "ii", "v" )

    @@Graphics_Uniform2i = Win32API.new( LIB_NAME, "Graphics_Uniform2i", "iii", "v" )

    @@Graphics_Uniform3i = Win32API.new( LIB_NAME, "Graphics_Uniform3i", "iiii", "v" )

    @@Graphics_Uniform4i = Win32API.new( LIB_NAME, "Graphics_Uniform4i", "iiiii", "v" )

    

    @@Graphics_UseProgram = Win32API.new( LIB_NAME, "Graphics_UseProgram", "i", "v" )

    @@Graphics_BindAttribLocation = Win32API.new( LIB_NAME, "Graphics_BindAttribLocation", "iip", "v" )

    

    @@Graphics_GenVBO = Win32API.new( LIB_NAME, "Graphics_GenVBO", "v", "i" )

    @@Graphics_DestroyVBO = Win32API.new( LIB_NAME, "Graphics_DestroyVBO", "i", "v" )

    @@Graphics_BindVBO = Win32API.new( LIB_NAME, "Graphics_BindVBO", "i", "v" )

    @@Graphics_VBOData = Win32API.new( LIB_NAME, "Graphics_VBOData", "pi", "v" )

    @@Graphics_VBOAttrib = Win32API.new( LIB_NAME, "Graphics_VBOAttrib", "iiii", "v" )

    @@Graphics_DrawVBO = Win32API.new( LIB_NAME, "Graphics_DrawVBO", "ii", "v" )

    

    @@Matrix_Create = Win32API.new( LIB_NAME, "Matrix_Create", "v", "i" )

    @@Matrix_Destroy = Win32API.new( LIB_NAME, "Matrix_Destroy", "i", "v" )

    

    @@Matrix_Pop = Win32API.new( LIB_NAME, "Matrix_Pop", "i", "v" )

    @@Matrix_Push = Win32API.new( LIB_NAME, "Matrix_Push", "i", "v" )

    @@Matrix_Size = Win32API.new( LIB_NAME, "Matrix_Size", "i", "i" )

    

    @@Matrix_Pop = Win32API.new( LIB_NAME, "Matrix_Pop", "i", "v" )

    @@Matrix_Push = Win32API.new( LIB_NAME, "Matrix_Push", "i", "v" )

    @@Matrix_UploadMatrix2 = Win32API.new( LIB_NAME, "Matrix_UploadMatrix2", "ii", "v" )

    @@Matrix_UploadMatrix3 = Win32API.new( LIB_NAME, "Matrix_UploadMatrix3", "ii", "v" )

    @@Matrix_UploadMatrix4 = Win32API.new( LIB_NAME, "Matrix_UploadMatrix4", "ii", "v" )

    

    @@Matrix_LoadMatrix4 = Win32API.new( LIB_NAME, "Matrix_LoadMatrix4", "ip", "v" )

    @@Matrix_MultiplyMatrix4 = Win32API.new( LIB_NAME, "Matrix_MultiplyMatrix4", "ip", "v" )

    @@Matrix_MultiplyMatrixStack = Win32API.new( LIB_NAME, "Matrix_MultiplyMatrixStack", "ii", "v" )

    @@Matrix_Rotate = Win32API.new( LIB_NAME, "Matrix_Rotate", "iiiii", "v" )

    @@Matrix_RotateX = Win32API.new( LIB_NAME, "Matrix_RotateX", "ii", "v" )

    @@Matrix_RotateY = Win32API.new( LIB_NAME, "Matrix_RotateY", "ii", "v" )

    @@Matrix_RotateZ = Win32API.new( LIB_NAME, "Matrix_RotateZ", "ii", "v" )

    @@Matrix_Scale = Win32API.new( LIB_NAME, "Matrix_Scale", "iiii", "v" )

    @@Matrix_Translate = Win32API.new( LIB_NAME, "Matrix_Translate", "iiii", "v" )

    

    @@Matrix_DownloadMatrix2 = Win32API.new( LIB_NAME, "Matrix_DownloadMatrix2", "i", "p" )

    @@Matrix_DownloadMatrix3 = Win32API.new( LIB_NAME, "Matrix_DownloadMatrix3", "i", "p" )

    @@Matrix_DownloadMatrix4 = Win32API.new( LIB_NAME, "Matrix_DownloadMatrix4", "i", "p" )

  end

end

#==============================================================================

# ** SceneManager

#------------------------------------------------------------------------------

#  Adds a finalisation stage to SceneManager.

# Used by XGL to cleanup the OpenGL context.

#==============================================================================

module SceneManager

  @@error = nil

  #--------------------------------------------------------------------------

  # * Override SceneManager

  #--------------------------------------------------------------------------

  class << self

    alias super_exit exit

    alias super_run run

    def run

      GL::Console.new

      GL::OpenGL.new

      if @@error != nil

        messageBox = Win32API.new( "User32", "MessageBox", "lppl", "l" )

        messageBox.call( 0, @@error, "SceneManager Error", 0 )

      else

        super_run

      end

    end

    #--------------------------------------------------------------------------

    # * Pre-Termination Processing

    #--------------------------------------------------------------------------

    def exit

      GL::Lib.XGL_Terminate

      super_exit

    end

  end

  #--------------------------------------------------------------------------

  # * Sets the scene manager to exit with an error message

  #--------------------------------------------------------------------------

  def self.exit_error( str )

    @@error = str

  end

end

Hello Triangle Tutorial
This little tutorial will modifying Spriteset_Map to draw a colourful triangle on the field.

Underneath the RMVXA-GL add a script section and call it "Hello Triangle".
Copy in this framework for overloading the default Spriteset_Map class:

Ruby:
class Spriteset_Map

  include GL

  

  alias super_update update

  alias super_dispose dispose

  

  def initialize

    create_viewports

    create_tilemap

    create_parallax

    create_characters

    create_shadow

    create_weather

    create_pictures

    create_timer

    

    

    

    update

  end

  

  def dispose

  

    super_dispose

  end

  

  def update

    super_update

    

  end

    

end

Notice the include GL, this is so we can use the RMVXA-GL classes without needing GL:: infront of everything.

First we want to load in some shaders to render out our triangle, so add in these two methods:
Ruby:
def create_shader

 

end

 

def destroy_shader

 

end
We will have 3 instance variables for handling the shader; the vertex shader, the fragment shader and the shader program.
(The OpenGL shader system was inspired by C/C++ compilers; the shader is made up of vertex and fragment objects that are linked into the final program)

To create the vertex and fragment shaders we need to write some shader code.
Here's a vertex shader;
Ruby:
vertex_source = "#version 120

  attribute vec2  vertex_position;

  attribute vec3  vertex_colour;

  varying vec3  fragment_colour;

  void main() {

      fragment_colour = vertex_colour;

      gl_Position = vec4( vertex_position, 0.0, 1.0 );

  }"
And here's a fragment shader;
Ruby:
fragment_source = "#version 120

  varying vec3  fragment_colour;

  void main() {

      gl_FragColor = vec4( fragment_colour, 1.0 );

  }"

You can copy those into the create_shader method as we can use these sources directly.

To create each shader we use the Shader.Create* methods with out shader source;
Ruby:
@vertex_shader = Shader.CreateVertex( vertex_source )

@fragment_shader = Shader.CreateVertex( vertex_source )
These can then be linked into the final program and the vertex attributes can be set;
Ruby:
@shader_program = ShaderMaterial.Create( @vertex_shader, @fragment_shader )

@shader_program.SetAttributeIndex( "vertex_position", 0 )

@shader_program.SetAttributeIndex( "vertex_colour", 1 )
The vertex_position attribute is the first attribute in our vertex shader, so we set the index to 0 (Like an array)
The vertex_colour attribute is the second attribute in our vertex shader, so we set that index to 1

The shader program is not yet linked, RMVXA-GL will link the program when it is next bound (When setting uniforms or when activating the program itself).

To clean-up the shader memory (On both the GPU memory and CPU memory) call the Dealloc method;
Ruby:
@shader_program.Dealloc

 

@fragment_shader.Dealloc

@vertex_shader.Dealloc
That can go into the destroy_shader method.


Next is the most important part; setting up the frame buffer for rendering. This is a page in GPU memory that OpenGL will draw to.
Let's make some RGSS bitmaps to draw to;
Ruby:
def create_display_buffer

    @display_sprite = Sprite.new( @viewport3 )

    @display_sprite.bitmap = Bitmap.new( 544, 416 )

    

    

end
RGSS bitmaps require cleaning up, so add that into a destroy method;
Ruby:
def destroy_display_buffer

 

 

    @display_sprite.bitmap.dispose

end

There are 2 parts to a frame buffer;
  • GPU Target
  • GPU Textures
The target is the point where all the textures are collected, the textures are the actual bytes where we will render our image.

To create our texture in GPU memory we use the Texure.Alloc method and parse in the details of our texture.
We want a texture that has red, blue, green and alpha components that is 544x416.
Alpha is needed if we want to see the map underneath our image and the 544x416 is to match the size of the game screen.

So after creating our RGSS bitmap, allocate the GPU memory for our texture, allocate the frame-buffer's target size (This is the size that all frame-buffer textures need to be) and then attach the texture to our frame buffer.
Ruby:
@render_texture = Texture.Alloc( Texture::RGBA, 544, 416 )

 

@frame_buffer = FrameBuffer.Alloc( 544, 416 )

@frame_buffer.AttachTexture( @render_texture )

The cleanup is once again Dealloc for both;
Ruby:
def destroy_display_buffer

    @frame_buffer.Dealloc

 

    @render_texture.Dealloc

 

    @display_sprite.bitmap.dispose

end

The final piece is the triangle that we want to draw.
We want to draw a 2D triangle with each point being a red, green or blue colour.

For this we need: x, y, r, g, b information for each point.
A float array can describe this for us:
Ruby:
def create_vertex_buffer

    triangle_mesh = [ [  0.0,  0.5, 1.0, 0.0, 0.0 ],

                      [  0.5, -0.5, 0.0, 1.0, 0.0 ], 

                      [ -0.5, -0.5, 0.0, 0.0, 1.0 ] ]

 

    

end

The vertices need to be allocated memory on the GPU and uploaded, we also need to describe our triangle to OpenGL.
Ruby:
@vertex_buffer = VertexBuffer.Alloc # Allocate GPU space for vertices

@vertex_buffer.DescribeBuffer( 3, [2, 3] ) # Describe that we have 3 vertices, split into 2 floats then 3 floats [xy,rgb]

@vertex_buffer.UploadVertexArray( triangle_mesh ) # Upload our triangle to the GPU memory

And then we have the cleanup;
Ruby:
def destroy_vertex_buffer

    @vertex_buffer.Dealloc

end

Make one more method called draw_triagle then set up all the calls for our create and destroy methods;
Ruby:
def initialize

    create_viewports

    create_tilemap

    create_parallax

    create_characters

    create_shadow

    create_weather

    create_pictures

    create_timer

 

    create_shader # +@shader_program

    create_display_buffer # +@frame_buffer

    create_vertex_buffer # +@vertex_buffer

 

    update

end

 

def dispose

    destroy_vertex_buffer # -@vertex_buffer

    destroy_display_buffer # -@frame_buffer

    destroy_shader # -@shader_program

 

    super_dispose

end

 

def update

    super_update

 

    draw_triangle # Do the actual rendering

end

 

def draw_triangle

    

end

 

...

Now that we have all the memory allocated and sorted out, drawing the triangle is easy;
Ruby:
def draw_triangle

    @frame_buffer.Bind( FrameBuffer::Clear ) # Bind our frame_buffer and clear any textures on it

 

    @shader_program.UseProgram # Select our shader_program (This will also link it if it is not already linked)

 

    @vertex_buffer.DrawArrays( VertexBuffer::DRAW_TRIANGLES ) # Draw our vertex_buffer, telling OpenGL that we are drawing Triangles

 

    @frame_buffer.ToBitmap( @display_sprite.bitmap ) # Copy the frame_buffer contents to our displayed bitmap

end

And that's it, you should now have a triangle slapped on top of the map.

Next tutorial will be setting textures to a cube and animating it.

Known Problems
  • OpenGL 2.1 support is not tested! - Please test it someone!
  • Some Matrix functions are broken - I don't know which, please report the ones that aren't working

Missing Features
  • Matrix cameras - Not hooked up yet
  • Setting render flags - Backface modes, blend modes, etc cannot be set
  • Depth Buffers and Textures - No depth buffer at all right now
The missing features are planned for the next version (After bug fixing).
 
Ran your demo. I was surprised it stayed at 60fps. I have Nvidia Geforce GT 300m on my laptop so I wasn't sure I'd meet the requirements.
It dropped the 30fps when I turned on the weather effect. Not bad.
 
Here's a screenshot of the spinning cube demo that's coming up next:
vQoH9iC.png


I'll be uploading the demo (Which will be called alpha 2) in a moment.

EDIT:
And here it is;
Alpha 2

This demo does not do screen post-processing, so it will be significantly faster than the other demos. I get full 60 FPS on all my machines with the cube spinning demo.

New features:
  • Camera matrix functions
  • Functions for setting depth IO, culling and blending
  • ViewPort is now set to the frame-buffer's width/height
  • Depth texture can now be attached to frame buffers for depth buffering

Bug fixes:
  • Cross product X/Y were switched
  • Pushing to the matrix stack wasn't copying the top matrix
  • Setting matrix uniforms no longer crashes if shader program hasn't yet been linked (Program is linked when a uniform is set)
 

Injury

Awesome Bro

Holy. Shit.

You are pushing the limits of a platform meant for a very specific commercial target: The masses who are without coding capability and prowess...So you have tread upon unknown territory. I'd like to thing this opens up some more mode7 for me with a designated zoom script so I can make Fractus Stellarum the way it should be...but I doubt that in the same mindset: You have a lot to do, and it doesn't involve us at hbg.

PS: I'd completely remake FS if you made a zoom script and whatnot in regards to what I originally wanted in FS. <3 u felix
 
To do 3D maps I'd need to rewrite the Tilemap class to render with OpenGL instead of bitmap blitting and I don't know anything about the Tilemap class to be able to rewrite it.

I'll need help setting that up, but once that's done you get fast mode 7, zooming, spinning, etc for free.

If you can get someone to make a Tilemapper framework then I can do it
 

Jason

Awesome Bro

That's rather impressive, just downloaded the demo and it ran at a perfect 60fps with no drops... then again it should do with my computer anyways lol, and I don't have any low end computers to try it on sadly, since we're all upgraded in this house, lol. It's quite an interesting idea though, to have OpenGL working within VXAce, but I think it's something for the more advanced RM* users, right?
 
Nuri Yuri":39591sgc said:
OpenGL 2.1 support is not tested! - Please test it someone!
Tested Alpha 2 : still doesn't work.
Do you have the FBO extension?
Can you enable the console in the script and tell me if any errors log or if you can which function call was a nullptr?
 
Ryaryu":2lxzaknw said:
RMVXA-GL will support GL lightning commands?
I presume you mean lighting?

I am not using fixed-function GL, so you can do anything you want with it as long as you write the shader.

It doesn't support the old OpenGL lighting functions, everything is programmable pipeline here.

Basically, anything is possible as long as you have the computing power to do it.
 
The console displays absolutely nothing :\
I don't know if I have the FBO and I don't know where I can download it... It's the jungle :x
 
Nuri Yuri":k2sle1lr said:
The console displays absolutely nothing :\
I don't know if I have the FBO and I don't know where I can download it... It's the jungle :x
You need a tool to check your GL support, one that lists extensions.
If you are 2.1 and missing the extension then perhaps a driver update will add it.

I'll add some real checks for the extension. I miss have missed something for a crash to happen
 

Thank you for viewing

HBGames is a leading amateur video game development forum and Discord server open to all ability levels. Feel free to have a nosey around!

Discord

Join our growing and active Discord server to discuss all aspects of game making in a relaxed environment. Join Us

Content

  • Our Games
  • Games in Development
  • Emoji by Twemoji.
    Top