ashton 0.0.1alpha
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +0 -0
- data/LICENSE +21 -0
- data/README.md +68 -0
- data/Rakefile +24 -0
- data/examples/framebuffer_example.rb +50 -0
- data/examples/media/Earth.png +0 -0
- data/examples/media/LargeStar.png +0 -0
- data/examples/media/Star.png +0 -0
- data/examples/media/Starfighter.bmp +0 -0
- data/examples/media/simple.png +0 -0
- data/examples/output/README.txt +1 -0
- data/examples/pixelate_example.rb +50 -0
- data/examples/radial_blur_example.rb +63 -0
- data/examples/shader_image_example.rb +42 -0
- data/examples/shockwave2_example.rb +76 -0
- data/examples/tv_screen_and_noise_example.rb +60 -0
- data/lib/ashton/base_shader.rb +172 -0
- data/lib/ashton/framebuffer.rb +183 -0
- data/lib/ashton/gosu_ext/color.rb +12 -0
- data/lib/ashton/gosu_ext/image.rb +32 -0
- data/lib/ashton/gosu_ext/window.rb +36 -0
- data/lib/ashton/image_stub.rb +37 -0
- data/lib/ashton/include/simplex.glsl +63 -0
- data/lib/ashton/post_process/contrast.frag +16 -0
- data/lib/ashton/post_process/default.vert +9 -0
- data/lib/ashton/post_process/fade.frag +11 -0
- data/lib/ashton/post_process/mezzotint.frag +24 -0
- data/lib/ashton/post_process/noise.frag +27 -0
- data/lib/ashton/post_process/pixelate.frag +48 -0
- data/lib/ashton/post_process/radial_blur.frag +31 -0
- data/lib/ashton/post_process/sepia.frag +19 -0
- data/lib/ashton/post_process/shockwave.frag +40 -0
- data/lib/ashton/post_process/shockwave2.frag +35 -0
- data/lib/ashton/post_process/tv_screen.frag +32 -0
- data/lib/ashton/post_process.rb +83 -0
- data/lib/ashton/shader/default.frag +19 -0
- data/lib/ashton/shader/default.vert +14 -0
- data/lib/ashton/shader.rb +62 -0
- data/lib/ashton/version.rb +3 -0
- data/lib/ashton.rb +26 -0
- metadata +201 -0
data/CHANGELOG
ADDED
File without changes
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2012 Bil Bas
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
Ashton
|
2
|
+
======
|
3
|
+
|
4
|
+
Description
|
5
|
+
-----------
|
6
|
+
|
7
|
+
Add extra visual effects to the Gosu game-development library, utilising OpenGL shaders (using shader model 1.0, for maximum compatibility) and frame-buffers.
|
8
|
+
|
9
|
+
"Ashton" is named after [Clark Ashton Smith](http://en.wikipedia.org/wiki/Clark_Ashton_Smith), an fantasy/horror author
|
10
|
+
with a particularly colourful imagination.
|
11
|
+
|
12
|
+
- Author: Bil Bas (Spooner)
|
13
|
+
- License: MIT
|
14
|
+
|
15
|
+
Usage
|
16
|
+
-----
|
17
|
+
|
18
|
+
gem install ashton --pre
|
19
|
+
|
20
|
+
Features
|
21
|
+
--------
|
22
|
+
|
23
|
+
- Gosu::Font
|
24
|
+
* [TODO] #draw - Added :shader hash option to choose optional shader to use.
|
25
|
+
* [TODO] #draw_rel - Added :shader hash option to choose optional shader to use.
|
26
|
+
|
27
|
+
- Gosu::Image
|
28
|
+
* #draw - Added :shader hash option to choose optional shader to use.
|
29
|
+
* #draw_rot - Added :shader hash option to choose optional shader to use.
|
30
|
+
* [TODO] #flip!, #flip, #mirror!, #mirror, #scale!, #scale, etc.
|
31
|
+
* [TODO] #resize (Well, create another image which is smaller/larger).
|
32
|
+
* [TODO] #to_framebuffer
|
33
|
+
|
34
|
+
- Gosu::Window
|
35
|
+
* #post_process {} - Draws contents of block into a back-buffer, then applies a shader as it draws that onto the screen.
|
36
|
+
* [TODO] #to_framebuffer - Copy the contents of the window as a {Ashton::Framebuffer}.
|
37
|
+
* [TODO] #to_image - Create Gosu::Image from window contents.
|
38
|
+
* [TODO] #draw_line - Added :shader hash option
|
39
|
+
* [TODO] #draw_quad - Added :shader hash option
|
40
|
+
* [TODO] #draw_triangle - Added :shader hash option
|
41
|
+
|
42
|
+
- {Ashton::Shader}
|
43
|
+
* #use {} - Inside the block, all draw operations are affected by the shader.
|
44
|
+
* Supports vertex and fragment shaders.
|
45
|
+
|
46
|
+
- {Ashton::PostProcess}
|
47
|
+
* A specific type of shader to perform full-screen post-processing.
|
48
|
+
* #process - Used to post-process the entire Gosu::Window at once, after some, or all, drawing is complete.
|
49
|
+
* Supports fragment shaders.
|
50
|
+
* [TODO] Includes a small library of example shaders (:blur, :simplex, etc).
|
51
|
+
|
52
|
+
- {Ashton::Framebuffer}
|
53
|
+
* #use {} - Inside the block, draw operations go into the framebuffer, rather than onto the window.
|
54
|
+
* #to_image - Convert to Gosu::Image.
|
55
|
+
* #draw - Draw directly onto a Gosu::Window.
|
56
|
+
* [TODO] #flip!, #flip - Invert framebuffer's vertical orientation.
|
57
|
+
|
58
|
+
Similar Libraries
|
59
|
+
-----------------
|
60
|
+
|
61
|
+
- [TexPlay](https://github.com/banister/texplay) - Deals with Gosu::Image manipulation, such as per-pixel editing and drawing. It is compatible with, and complementary to, this gem.
|
62
|
+
|
63
|
+
Credits
|
64
|
+
-------
|
65
|
+
|
66
|
+
- simplex.glsl - simplex noise implementation - Copyright (C) 2011 Ashima Arts - MIT license.
|
67
|
+
|
68
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'rake/clean'
|
2
|
+
require 'rspec/core/rake_task'
|
3
|
+
require 'yard'
|
4
|
+
require 'redcloth'
|
5
|
+
require 'launchy'
|
6
|
+
require 'rubygems/package_task'
|
7
|
+
|
8
|
+
|
9
|
+
spec = Gem::Specification.load Dir['*.gemspec'].first
|
10
|
+
|
11
|
+
Gem::PackageTask.new spec do
|
12
|
+
end
|
13
|
+
|
14
|
+
YARD::Rake::YardocTask.new
|
15
|
+
|
16
|
+
task :default => :spec
|
17
|
+
|
18
|
+
RSpec::Core::RakeTask.new do |t|
|
19
|
+
end
|
20
|
+
|
21
|
+
desc "Open yard docs in browser"
|
22
|
+
task :browse_yard => :yard do
|
23
|
+
Launchy.open "doc/index.html" rescue nil
|
24
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# Use of GLSL shader in Gosu.
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'rubygems'
|
5
|
+
rescue LoadError
|
6
|
+
end
|
7
|
+
|
8
|
+
$LOAD_PATH.unshift File.expand_path('../lib/', File.dirname(__FILE__))
|
9
|
+
require "ashton"
|
10
|
+
|
11
|
+
def media_path(file); File.expand_path "media/#{file}", File.dirname(__FILE__) end
|
12
|
+
|
13
|
+
class GameWindow < Gosu::Window
|
14
|
+
def initialize
|
15
|
+
super 800, 600, false
|
16
|
+
self.caption = "Ashton::Framebuffer example - composing an image inside a buffer - hold <LMB> to draw - <delete> to clear"
|
17
|
+
|
18
|
+
@font = Gosu::Font.new(self, Gosu::default_font_name, 20)
|
19
|
+
@star = Gosu::Image.new(self, media_path("LargeStar.png"), true)
|
20
|
+
@framebuffer = Ashton::Framebuffer.new width, height
|
21
|
+
end
|
22
|
+
|
23
|
+
def update
|
24
|
+
if button_down? Gosu::MsLeft
|
25
|
+
# Draw into the framebuffer, rather than onto the screen.
|
26
|
+
@framebuffer.use do
|
27
|
+
@star.draw_rot mouse_x, mouse_y, 0, 0, 0.5, 0.5
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def draw
|
33
|
+
@framebuffer.draw 0, 0 # Draws immediately.
|
34
|
+
@star.draw_rot mouse_x, mouse_y, 0, 0, 0.5, 0.5
|
35
|
+
|
36
|
+
@font.draw "FPS: #{Gosu::fps}", 0, 0, 0
|
37
|
+
end
|
38
|
+
|
39
|
+
def button_down(id)
|
40
|
+
case id
|
41
|
+
when Gosu::KbDelete
|
42
|
+
@framebuffer.clear
|
43
|
+
when Gosu::KbEscape
|
44
|
+
close
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
window = GameWindow.new
|
50
|
+
window.show
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1 @@
|
|
1
|
+
Output from the examples will be saved here, to prove that some functionality works.
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# Use of GLSL shader in Gosu to post-process the entire screen.
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'rubygems'
|
5
|
+
rescue LoadError
|
6
|
+
end
|
7
|
+
|
8
|
+
$LOAD_PATH.unshift File.expand_path('../lib/', File.dirname(__FILE__))
|
9
|
+
require "ashton"
|
10
|
+
|
11
|
+
def shader(file); File.read File.expand_path("../lib/ashton/post_process/#{file}", File.dirname(__FILE__)) end
|
12
|
+
def media_path(file); File.expand_path "media/#{file}", File.dirname(__FILE__) end
|
13
|
+
|
14
|
+
class TestWindow < Gosu::Window
|
15
|
+
def initialize
|
16
|
+
super 640, 480, false
|
17
|
+
self.caption = "Post-processing with 'pixelate' - 1..9 affect pixel size"
|
18
|
+
|
19
|
+
@pixelate = Ashton::PostProcess.new shader('pixelate.frag')
|
20
|
+
@pixelate['in_PixelSize'] = @pixel_size = 4
|
21
|
+
|
22
|
+
@font = Gosu::Font.new self, Gosu::default_font_name, 40
|
23
|
+
@star = Gosu::Image.new(self, media_path("LargeStar.png"), true)
|
24
|
+
|
25
|
+
update # Ensure the values are initially set.
|
26
|
+
end
|
27
|
+
|
28
|
+
def button_down(id)
|
29
|
+
if (Gosu::Kb1..Gosu::Kb9).include? id
|
30
|
+
@pixel_size = (id - Gosu::Kb1 + 1) ** 2
|
31
|
+
@pixelate['in_PixelSize'] = @pixel_size
|
32
|
+
elsif id == Gosu::KbEscape
|
33
|
+
close
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def draw
|
38
|
+
post_process @pixelate do
|
39
|
+
@font.draw_rel "Hello world!", 350, 50, 0, 0.5, 0.5
|
40
|
+
@font.draw_rel "Goodbye world!", 400, 350, 0, 0.5, 0.5
|
41
|
+
@star.draw 0, 0, 0
|
42
|
+
@star.draw 200, 100, 0
|
43
|
+
end
|
44
|
+
|
45
|
+
# Drawing after the effect isn't processed, which is useful for GUI elements.
|
46
|
+
@font.draw "Pixel ratio: 1:#{@pixel_size}", 0, 0, 0
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
TestWindow.new.show
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# Use of GLSL shader in Gosu to post-process the entire screen.
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'rubygems'
|
5
|
+
rescue LoadError
|
6
|
+
end
|
7
|
+
|
8
|
+
$LOAD_PATH.unshift File.expand_path('../lib/', File.dirname(__FILE__))
|
9
|
+
require "ashton"
|
10
|
+
|
11
|
+
def shader(file); File.read File.expand_path("../lib/ashton/post_process/#{file}", File.dirname(__FILE__)) end
|
12
|
+
|
13
|
+
class TestWindow < Gosu::Window
|
14
|
+
def initialize
|
15
|
+
super 640, 480, false
|
16
|
+
self.caption = "Post-processing with 'radial_blur' - hold space to disable; 1..9 sets blurriness"
|
17
|
+
|
18
|
+
@blur = Ashton::PostProcess.new shader('radial_blur.frag')
|
19
|
+
@blur['in_BrightFactor'] = 1.5
|
20
|
+
@blur['in_BlurFactor'] = @blur_factor = 2.0
|
21
|
+
@blur['in_Passes'] = 20 # Quite a lot of work, but just proves how fast shader are!
|
22
|
+
@font = Gosu::Font.new self, Gosu::default_font_name, 40
|
23
|
+
|
24
|
+
update # Ensure the values are initially set.
|
25
|
+
end
|
26
|
+
|
27
|
+
def update
|
28
|
+
# Wiggle the blur about a bit each frame!
|
29
|
+
@blur['in_OriginX'] = mouse_x
|
30
|
+
@blur['in_OriginY'] = mouse_y
|
31
|
+
end
|
32
|
+
|
33
|
+
def button_down(id)
|
34
|
+
if (Gosu::Kb1..Gosu::Kb9).include? id
|
35
|
+
@blur_factor = (id - Gosu::Kb1 + 1).to_f
|
36
|
+
@blur['in_BlurFactor'] = @blur_factor
|
37
|
+
elsif id == Gosu::KbEscape
|
38
|
+
close
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def draw_scene
|
43
|
+
@font.draw_rel "Hello world!", 100, 100, 0, 0, 0.5, 0.5
|
44
|
+
@font.draw_rel "Goodbye world!", 400, 280, 0, 0.5, 0.5
|
45
|
+
|
46
|
+
@font.draw_rel "X", mouse_x, mouse_y, 0, 0.5, 0.5
|
47
|
+
end
|
48
|
+
|
49
|
+
def draw
|
50
|
+
if button_down? Gosu::KbSpace
|
51
|
+
draw_scene
|
52
|
+
else
|
53
|
+
post_process @blur do
|
54
|
+
draw_scene
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Drawing after the effect isn't processed, which is useful for GUI elements.
|
59
|
+
@font.draw "Blur Factor: #{@blur_factor}", 0, 0, 0
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
TestWindow.new.show
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# Use of GLSL shader in Gosu.
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'rubygems'
|
5
|
+
rescue LoadError
|
6
|
+
end
|
7
|
+
|
8
|
+
$LOAD_PATH.unshift File.expand_path('../lib/', File.dirname(__FILE__))
|
9
|
+
require "ashton"
|
10
|
+
|
11
|
+
def media_path(file); File.expand_path "media/#{file}", File.dirname(__FILE__) end
|
12
|
+
def output_path(file); File.expand_path "output/#{file}", File.dirname(__FILE__) end
|
13
|
+
|
14
|
+
class GameWindow < Gosu::Window
|
15
|
+
def initialize
|
16
|
+
super 640, 480, false
|
17
|
+
self.caption = "Gosu & OpenGL Integration Demo (SHADERS)"
|
18
|
+
|
19
|
+
@font = Gosu::Font.new self, Gosu::default_font_name, 20
|
20
|
+
@image = Gosu::Image.new self, media_path("Earth.png"), true
|
21
|
+
@shader = Ashton::Shader.new # Just use default shader for now.
|
22
|
+
end
|
23
|
+
|
24
|
+
def draw
|
25
|
+
# draw, with and without colour.
|
26
|
+
@image.draw 10, 10, 0, :shader => @shader
|
27
|
+
@image.draw 10, @image.height + 20, 0, 1, 1, Gosu::Color::RED, :shader => @shader
|
28
|
+
|
29
|
+
# draw#rot, with and without colour.
|
30
|
+
@image.draw_rot 280, 0, 0, 10, 0, 0, :shader => @shader
|
31
|
+
@image.draw_rot 280, @image.height + 10, 0, 10, 0, 0, 1, 1, Gosu::Color::RED, :shader => @shader
|
32
|
+
end
|
33
|
+
|
34
|
+
def button_down(id)
|
35
|
+
if id == Gosu::KbEscape
|
36
|
+
close
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
window = GameWindow.new
|
42
|
+
window.show
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# Use of GLSL shader in Gosu to post-process the entire screen.
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'rubygems'
|
5
|
+
rescue LoadError
|
6
|
+
end
|
7
|
+
|
8
|
+
$LOAD_PATH.unshift File.expand_path('../lib/', File.dirname(__FILE__))
|
9
|
+
require "ashton"
|
10
|
+
|
11
|
+
def shader(file); File.read File.expand_path("../lib/ashton/post_process/#{file}", File.dirname(__FILE__)) end
|
12
|
+
def media_path(file); File.expand_path "media/#{file}", File.dirname(__FILE__) end
|
13
|
+
|
14
|
+
class Shockwave
|
15
|
+
def age; Gosu::milliseconds - @start_time; end
|
16
|
+
|
17
|
+
def process
|
18
|
+
@shader.process
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize(x, y)
|
22
|
+
@shader = Ashton::PostProcess.new shader('shockwave2.frag')
|
23
|
+
@shader['in_ShockParams'] = [10.0, 0.8, 0.1]
|
24
|
+
@shader['in_Center'] = [x, $window.height - y]
|
25
|
+
@start_time = Gosu::milliseconds
|
26
|
+
end
|
27
|
+
|
28
|
+
def update
|
29
|
+
@shader['in_Time'] = age / 1000.0
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class TestWindow < Gosu::Window
|
34
|
+
def initialize
|
35
|
+
super 640, 480, false
|
36
|
+
self.caption = "Post-processing with 'shockwave2.frag' - Click on window to create a splash"
|
37
|
+
|
38
|
+
@font = Gosu::Font.new self, Gosu::default_font_name, 40
|
39
|
+
@background = Gosu::Image.new(self, media_path("Earth.png"), true)
|
40
|
+
@waves = []
|
41
|
+
end
|
42
|
+
|
43
|
+
def update
|
44
|
+
@waves.each {|w| w.update }
|
45
|
+
@waves.delete_if {|w| w.age > 3000 }
|
46
|
+
end
|
47
|
+
|
48
|
+
def needs_cursor?
|
49
|
+
true
|
50
|
+
end
|
51
|
+
|
52
|
+
def button_down(id)
|
53
|
+
case id
|
54
|
+
when Gosu::MsLeft
|
55
|
+
@waves << Shockwave.new(mouse_x, mouse_y)
|
56
|
+
when Gosu::KbEscape
|
57
|
+
close
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def draw
|
62
|
+
@background.draw 0, 0, 0, width.fdiv(@background.width), height.fdiv(@background.height)
|
63
|
+
@font.draw_rel "Hello world!", 350, 50, 0, 0.5, 0.5
|
64
|
+
@font.draw_rel "Goodbye world!", 400, 350, 0, 0.5, 0.5
|
65
|
+
|
66
|
+
# Since we want to process multiple post-processing effects...
|
67
|
+
@waves.each do |wave|
|
68
|
+
wave.process
|
69
|
+
end
|
70
|
+
|
71
|
+
# Drawing after the effect isn't processed, which is useful for GUI elements.
|
72
|
+
@font.draw "FPS: #{Gosu::fps} Waves: #{@waves.size}", 0, 0, 0
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
TestWindow.new.show
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# Use of GLSL shader in Gosu to post-process the entire screen.
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'rubygems'
|
5
|
+
rescue LoadError
|
6
|
+
end
|
7
|
+
|
8
|
+
$LOAD_PATH.unshift File.expand_path('../lib/', File.dirname(__FILE__))
|
9
|
+
require "ashton"
|
10
|
+
|
11
|
+
def shader(file); File.read File.expand_path("../lib/ashton/post_process/#{file}", File.dirname(__FILE__)) end
|
12
|
+
def media_path(file); File.expand_path "media/#{file}", File.dirname(__FILE__) end
|
13
|
+
|
14
|
+
class TestWindow < Gosu::Window
|
15
|
+
def initialize
|
16
|
+
super 640, 480, false
|
17
|
+
self.caption = "Post-processing with both 'tv_screen.frag' and 'noise.frag'"
|
18
|
+
|
19
|
+
@screen = Ashton::PostProcess.new shader('tv_screen.frag')
|
20
|
+
@screen['in_ColumnWidth'] = 1.0
|
21
|
+
|
22
|
+
@noise = Ashton::PostProcess.new shader('noise.frag')
|
23
|
+
|
24
|
+
|
25
|
+
@font = Gosu::Font.new self, Gosu::default_font_name, 40
|
26
|
+
@star = Gosu::Image.new(self, media_path("LargeStar.png"), true)
|
27
|
+
@background = Gosu::Image.new(self, media_path("Earth.png"), true)
|
28
|
+
|
29
|
+
update # Ensure the values are initially set.
|
30
|
+
end
|
31
|
+
|
32
|
+
def update
|
33
|
+
@noise['in_T'] = Math::sin(Gosu::milliseconds / 500.0) * 1000
|
34
|
+
@noise['in_Intensity'] = Math::sin(Gosu::milliseconds / 2345.0) + 1.5
|
35
|
+
end
|
36
|
+
|
37
|
+
def draw
|
38
|
+
@background.draw 0, 0, 0, width.fdiv(@background.width), height.fdiv(@background.height)
|
39
|
+
|
40
|
+
@font.draw_rel "Hello world!", 350, 50, 0, 0.5, 0.5, 1, 1, Gosu::Color::GREEN
|
41
|
+
@font.draw_rel "Goodbye world!", 400, 350, 0, 0.5, 0.5, 2, 2, Gosu::Color::BLUE
|
42
|
+
|
43
|
+
@star.draw 0, 0, 0
|
44
|
+
@star.draw 200, 100, 0
|
45
|
+
|
46
|
+
@noise.process
|
47
|
+
@screen.process
|
48
|
+
|
49
|
+
# Drawing after the effect isn't processed, which is useful for GUI elements.
|
50
|
+
@font.draw "FPS: #{Gosu::fps}", 0, 0, 0
|
51
|
+
end
|
52
|
+
|
53
|
+
def button_down(id)
|
54
|
+
if id == Gosu::KbEscape
|
55
|
+
close
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
TestWindow.new.show
|
@@ -0,0 +1,172 @@
|
|
1
|
+
module Ashton
|
2
|
+
# @abstract
|
3
|
+
class BaseShader
|
4
|
+
INVALID_LOCATION = -1
|
5
|
+
MIN_OPENGL_VERSION = 2.0
|
6
|
+
|
7
|
+
attr_reader :fragment_source, :vertex_source
|
8
|
+
|
9
|
+
# Todo: Pass in a filename (String) or name of built-in pp shader (Symbol)
|
10
|
+
#
|
11
|
+
# @option options [String] :vertex Source code for vertex shader.
|
12
|
+
# @option options [String] :vert equivalent to :vertex
|
13
|
+
# @option options [String] :fragment Source code for fragment shader.
|
14
|
+
# @option options [String] :frag equivalent to :fragment
|
15
|
+
def initialize(vertex_source, fragment_source)
|
16
|
+
raise "Can't instantiate abstract class" if self.class == BaseShader
|
17
|
+
|
18
|
+
unless GL.version_supported? MIN_OPENGL_VERSION
|
19
|
+
raise NotSupportedError, "Ashton requires OpenGL #{MIN_OPENGL_VERSION} support to utilise shaders"
|
20
|
+
end
|
21
|
+
|
22
|
+
@uniform_locations = {}
|
23
|
+
@attribute_locations = {}
|
24
|
+
@program = nil
|
25
|
+
|
26
|
+
@vertex_source = vertex_source
|
27
|
+
@fragment_source = fragment_source
|
28
|
+
|
29
|
+
@vertex = compile GL_VERTEX_SHADER, vertex_source
|
30
|
+
@fragment = compile GL_FRAGMENT_SHADER, fragment_source
|
31
|
+
|
32
|
+
link
|
33
|
+
|
34
|
+
glBindFragDataLocationEXT @program, 0, "out_FragColor"
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
# Creates a copy of the shader program, recompiling the source,
|
39
|
+
# but not preserving the uniform values.
|
40
|
+
def dup
|
41
|
+
self.class.new :vertex => @vertex_source, :fragment => @fragment_source
|
42
|
+
end
|
43
|
+
|
44
|
+
# Make this the current shader program.
|
45
|
+
def use
|
46
|
+
previous_program = glGetIntegerv GL_CURRENT_PROGRAM
|
47
|
+
glUseProgram @program
|
48
|
+
|
49
|
+
if block_given?
|
50
|
+
result = yield self
|
51
|
+
$window.flush # TODO: need to work out how to make shader affect delayed draws.
|
52
|
+
glUseProgram previous_program
|
53
|
+
end
|
54
|
+
|
55
|
+
result
|
56
|
+
end
|
57
|
+
|
58
|
+
# Disable the shader program (not needed in block version of #use).
|
59
|
+
def disable
|
60
|
+
glUseProgram 0 # Disable the shader!
|
61
|
+
end
|
62
|
+
|
63
|
+
# Is this the current shader program?
|
64
|
+
def current?
|
65
|
+
glGetIntegerv(GL_CURRENT_PROGRAM) == @program
|
66
|
+
end
|
67
|
+
|
68
|
+
# Set the value of a uniform.
|
69
|
+
def []=(name, value)
|
70
|
+
use do
|
71
|
+
case value
|
72
|
+
when true, GL_TRUE
|
73
|
+
glUniform1i uniform(name), 1
|
74
|
+
|
75
|
+
when false, GL_FALSE
|
76
|
+
glUniform1i uniform(name), 0
|
77
|
+
|
78
|
+
when Float
|
79
|
+
glUniform1f uniform(name), value
|
80
|
+
|
81
|
+
when Integer
|
82
|
+
glUniform1i uniform(name), value
|
83
|
+
|
84
|
+
when Array
|
85
|
+
size = value.size
|
86
|
+
|
87
|
+
raise ArgumentError, "Empty array not supported for uniform data" if size.zero?
|
88
|
+
raise ArgumentError, "Only support uniforms up to 4 elements" if size > 4
|
89
|
+
|
90
|
+
case value[0]
|
91
|
+
when Float
|
92
|
+
GL.send "glUniform#{size}f", uniform(name), *value
|
93
|
+
|
94
|
+
when Integer
|
95
|
+
GL.send "glUniform#{size}i", uniform(name), *value
|
96
|
+
|
97
|
+
else
|
98
|
+
raise ArgumentError, "Uniform data type not supported for element of type: #{value[0].class}"
|
99
|
+
end
|
100
|
+
|
101
|
+
else
|
102
|
+
raise ArgumentError, "Uniform data type not supported for type: #{value.class}"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
|
108
|
+
|
109
|
+
def uniform(name)
|
110
|
+
location = @uniform_locations[name]
|
111
|
+
if location
|
112
|
+
location
|
113
|
+
else
|
114
|
+
location = glGetUniformLocation @program, name.to_s
|
115
|
+
raise ShaderUniformError, "No #{name} uniform specified in program" if location == INVALID_LOCATION
|
116
|
+
@uniform_locations[name] = location
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
protected
|
121
|
+
def attribute(name)
|
122
|
+
location = @attribute_locations[name]
|
123
|
+
if location
|
124
|
+
location
|
125
|
+
else
|
126
|
+
location = glGetAttribLocation @program, name.to_s
|
127
|
+
raise ShaderAttributeError, "No #{name} attribute specified in program" if location == INVALID_LOCATION
|
128
|
+
@attribute_locations[name] = location
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
protected
|
133
|
+
def compile(type, source)
|
134
|
+
shader = glCreateShader type
|
135
|
+
glShaderSource shader, source
|
136
|
+
glCompileShader shader
|
137
|
+
|
138
|
+
unless glGetShaderiv shader, GL_COMPILE_STATUS
|
139
|
+
error = glGetShaderInfoLog shader
|
140
|
+
error_lines = error.scan(/0\((\d+)\)+/m).map {|num| num.first.to_i }.uniq
|
141
|
+
|
142
|
+
if type == GL_VERTEX_SHADER
|
143
|
+
type_name = "Vertex"
|
144
|
+
source = @vertex_source
|
145
|
+
else
|
146
|
+
type_name = "Fragment"
|
147
|
+
source = @fragment_source
|
148
|
+
end
|
149
|
+
|
150
|
+
source_lines = source.split("\n")
|
151
|
+
lines = error_lines.map {|i| "#{i.to_s.rjust 3}: #{source_lines[i - 1].rstrip}" }.join "\n"
|
152
|
+
raise ShaderCompileError, "#{type_name} shader error: #{glGetShaderInfoLog(shader)}\n#{lines}"
|
153
|
+
end
|
154
|
+
|
155
|
+
shader
|
156
|
+
end
|
157
|
+
|
158
|
+
protected
|
159
|
+
def link
|
160
|
+
@program = glCreateProgram
|
161
|
+
glAttachShader @program, @vertex
|
162
|
+
glAttachShader @program, @fragment
|
163
|
+
glLinkProgram @program
|
164
|
+
|
165
|
+
unless glGetProgramiv @program, GL_LINK_STATUS
|
166
|
+
raise ShaderLinkError, "Shader link error: #{glGetProgramInfoLog(@program)}"
|
167
|
+
end
|
168
|
+
|
169
|
+
nil
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|