glimr 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. data/lib/glimr.rb +2 -0
  2. data/lib/glimr/configurable.rb +37 -0
  3. data/lib/glimr/default_theme/button_bg.png +0 -0
  4. data/lib/glimr/default_theme/button_bg_down.png +0 -0
  5. data/lib/glimr/default_theme/button_cover.png +0 -0
  6. data/lib/glimr/default_theme/button_cover_down.png +0 -0
  7. data/lib/glimr/default_theme/button_focus.png +0 -0
  8. data/lib/glimr/default_theme/checkbox_bg.png +0 -0
  9. data/lib/glimr/default_theme/checkbox_checked_bg.png +0 -0
  10. data/lib/glimr/default_theme/font.ttf +0 -0
  11. data/lib/glimr/default_theme/hscroller_bg.png +0 -0
  12. data/lib/glimr/default_theme/radiobutton_bg.png +0 -0
  13. data/lib/glimr/default_theme/radiobutton_checked_bg.png +0 -0
  14. data/lib/glimr/default_theme/resizer_down.png +0 -0
  15. data/lib/glimr/default_theme/resizer_up.png +0 -0
  16. data/lib/glimr/default_theme/scroll_down_down.png +0 -0
  17. data/lib/glimr/default_theme/scroll_down_up.png +0 -0
  18. data/lib/glimr/default_theme/scroll_hknob_down.png +0 -0
  19. data/lib/glimr/default_theme/scroll_hknob_up.png +0 -0
  20. data/lib/glimr/default_theme/scroll_left_down.png +0 -0
  21. data/lib/glimr/default_theme/scroll_left_up.png +0 -0
  22. data/lib/glimr/default_theme/scroll_right_down.png +0 -0
  23. data/lib/glimr/default_theme/scroll_right_up.png +0 -0
  24. data/lib/glimr/default_theme/scroll_up_down.png +0 -0
  25. data/lib/glimr/default_theme/scroll_up_up.png +0 -0
  26. data/lib/glimr/default_theme/scroll_vknob_down.png +0 -0
  27. data/lib/glimr/default_theme/scroll_vknob_up.png +0 -0
  28. data/lib/glimr/default_theme/text_cursor.png +0 -0
  29. data/lib/glimr/default_theme/text_cursor_insert.png +0 -0
  30. data/lib/glimr/default_theme/text_input_bg.png +0 -0
  31. data/lib/glimr/default_theme/vscroller_bg.png +0 -0
  32. data/lib/glimr/event.rb +41 -0
  33. data/lib/glimr/eventlistener.rb +209 -0
  34. data/lib/glimr/layoutable.rb +520 -0
  35. data/lib/glimr/renderer.rb +2 -0
  36. data/lib/glimr/renderer/camera.rb +63 -0
  37. data/lib/glimr/renderer/geometry.rb +194 -0
  38. data/lib/glimr/renderer/glutwindow.rb +387 -0
  39. data/lib/glimr/renderer/light.rb +43 -0
  40. data/lib/glimr/renderer/material.rb +66 -0
  41. data/lib/glimr/renderer/model.rb +103 -0
  42. data/lib/glimr/renderer/orthoprojection.rb +21 -0
  43. data/lib/glimr/renderer/raw.rb +34 -0
  44. data/lib/glimr/renderer/sceneobject.rb +279 -0
  45. data/lib/glimr/renderer/shader.rb +14 -0
  46. data/lib/glimr/renderer/texture.rb +280 -0
  47. data/lib/glimr/renderer/transform.rb +322 -0
  48. data/lib/glimr/renderer/viewport.rb +349 -0
  49. data/lib/glimr/renderer_core.rb +10 -0
  50. data/lib/glimr/util.rb +247 -0
  51. data/lib/glimr/widget.rb +87 -0
  52. data/lib/glimr/widgets.rb +37 -0
  53. data/lib/glimr/widgets/button.rb +277 -0
  54. data/lib/glimr/widgets/checkbox.rb +82 -0
  55. data/lib/glimr/widgets/container.rb +84 -0
  56. data/lib/glimr/widgets/image.rb +82 -0
  57. data/lib/glimr/widgets/label.rb +91 -0
  58. data/lib/glimr/widgets/layout.rb +227 -0
  59. data/lib/glimr/widgets/list.rb +28 -0
  60. data/lib/glimr/widgets/radiogroup.rb +118 -0
  61. data/lib/glimr/widgets/resizer.rb +31 -0
  62. data/lib/glimr/widgets/scrollable_container.rb +67 -0
  63. data/lib/glimr/widgets/scrollbar.rb +496 -0
  64. data/lib/glimr/widgets/stretchable_image.rb +135 -0
  65. data/lib/glimr/widgets/text_editor.rb +349 -0
  66. data/tests/assets/datatowers_crop.jpg +0 -0
  67. data/tests/assets/download_progress_meter.png +0 -0
  68. data/tests/assets/metalwing2.png +0 -0
  69. data/tests/assets/redhairgreeneyes3.jpg +0 -0
  70. data/tests/demo_apps/spinning_ruby.rb +37 -0
  71. data/tests/integration_tests/run_all.rb +8 -0
  72. data/tests/integration_tests/test_button.rb +22 -0
  73. data/tests/integration_tests/test_checkbox.rb +21 -0
  74. data/tests/integration_tests/test_container.rb +22 -0
  75. data/tests/integration_tests/test_label.rb +12 -0
  76. data/tests/integration_tests/test_layout.rb +43 -0
  77. data/tests/integration_tests/test_radiogroup.rb +16 -0
  78. data/tests/integration_tests/test_renderer.rb +44 -0
  79. data/tests/integration_tests/test_renderer2.rb +36 -0
  80. data/tests/integration_tests/test_scrollable_container.rb +34 -0
  81. data/tests/integration_tests/test_scrollbar.rb +20 -0
  82. data/tests/integration_tests/test_stretchable_image.rb +34 -0
  83. data/tests/integration_tests/test_text_input.rb +20 -0
  84. data/tests/integration_tests/test_zsort.rb +18 -0
  85. data/tests/unit_tests/test_button.rb +93 -0
  86. data/tests/unit_tests/test_checkbox.rb +35 -0
  87. data/tests/unit_tests/test_label.rb +36 -0
  88. data/tests/unit_tests/test_layout.rb +229 -0
  89. data/tests/unit_tests/test_widget.rb +3 -0
  90. metadata +139 -0
@@ -0,0 +1,43 @@
1
+ require 'glimr/renderer/sceneobject'
2
+
3
+
4
+ module GlimR
5
+
6
+
7
+ class Light < SceneObject
8
+
9
+ LIGHTS = GL::constants.grep(/^LIGHT[0-9]+/).map{|l| GL.const_get l }
10
+
11
+ def self.reserve_light_id
12
+ @index ||= 0
13
+ return false if LIGHTS.size <= @index
14
+ l = LIGHTS[@index]
15
+ @index += 1
16
+ l
17
+ end
18
+
19
+ def self.reset_lights
20
+ @index = 0
21
+ LIGHTS.each{|l| Disable(l) }
22
+ end
23
+
24
+ attr_accessor :position, :diffuse, :specular, :ambient
25
+ attr_accessor :constant_attenuation, :linear_attenuation, :quadratic_attenuation
26
+
27
+ def setup
28
+ light_id = self.class.reserve_light_id
29
+ return unless light_id
30
+ Enable(light_id)
31
+ Lightfv(light_id, POSITION, position ) if position
32
+ Lightfv(light_id, DIFFUSE, diffuse ) if diffuse
33
+ Lightfv( light_id, SPECULAR, specular ) if specular
34
+ Lightfv( light_id, AMBIENT, ambient ) if ambient
35
+ Lightf( light_id, CONSTANT_ATTENUATION, constant_attenuation) if constant_attenuation
36
+ Lightf( light_id, LINEAR_ATTENUATION, linear_attenuation) if linear_attenuation
37
+ Lightf( light_id, QUADRATIC_ATTENUATION, quadratic_attenuation) if quadratic_attenuation
38
+ end
39
+
40
+ end
41
+
42
+
43
+ end
@@ -0,0 +1,66 @@
1
+ require 'glimr/renderer/sceneobject'
2
+
3
+
4
+ module GlimR
5
+
6
+
7
+ # A Material node controls OpenGL surface material parameters.
8
+ # It has accessors for ambient, diffuse, specular, shininess and emission.
9
+ #
10
+ # Look up glMaterial and Blinn-Phong shading to find out more.
11
+ #
12
+ class Material < SceneObject
13
+
14
+ touching_accessor :ambient, :diffuse, :specular, :shininess, :emission
15
+
16
+ # The absolute material of a Material is the parent's absolute material
17
+ # merged with the Material.
18
+ def absolute_material
19
+ @__pama ||= (
20
+ pam = parent.absolute_material
21
+ pam.ambient = ambient if ambient
22
+ pam.diffuse = diffuse if diffuse
23
+ pam.specular = specular if specular
24
+ pam.shininess = shininess if shininess
25
+ pam.emission = emission if emission
26
+ pam
27
+ )
28
+ end
29
+
30
+ # Applies the material by calling GL::Material to set the material properties.
31
+ def apply
32
+ Material(FRONT_AND_BACK, AMBIENT, ambient) if ambient
33
+ Material(FRONT_AND_BACK, DIFFUSE, diffuse) if diffuse
34
+ Material(FRONT_AND_BACK, SPECULAR, specular) if specular
35
+ Material(FRONT_AND_BACK, SHININESS, shininess) if shininess
36
+ Material(FRONT_AND_BACK, EMISSION, emission) if emission
37
+ end
38
+
39
+ # Pushes the LIGHTING_BIT to attrib stack.
40
+ def push_state
41
+ PushAttrib(LIGHTING_BIT)
42
+ end
43
+
44
+ # Pops the attrib stack.
45
+ def pop_state
46
+ PopAttrib()
47
+ end
48
+
49
+ # Replaces the instance variables of the Material with
50
+ # the other's.
51
+ def replace!(other)
52
+ @ambient = other.ambient
53
+ @diffuse = other.diffuse
54
+ @specular = other.specular
55
+ @shininess = other.shininess
56
+ @emission = other.emission
57
+ self
58
+ end
59
+
60
+ touching :touch!, :replace!
61
+
62
+ end
63
+
64
+
65
+ end
66
+
@@ -0,0 +1,103 @@
1
+ require 'glimr/renderer/sceneobject'
2
+ require 'glimr/renderer/transform'
3
+ require 'glimr/renderer/texture'
4
+ require 'glimr/renderer/geometry'
5
+ require 'glimr/renderer/material'
6
+ require 'glimr/renderer/shader'
7
+
8
+
9
+ module GlimR
10
+
11
+ # A Model is a convenience SceneObject that
12
+ # has its own transform, material, texture, shader, and geometry.
13
+ #
14
+ # The state nodes are attached to the Model after super initialization
15
+ # and form a five-node subtree ordered like this (from top to bottom):
16
+ # transform - material - texture - shader - geometry.
17
+ #
18
+ # Model delegates many accessors to the corresponding state nodes
19
+ #
20
+ # Example:
21
+ # m = Model.new
22
+ # m.texture.load('foo.jpg')
23
+ # m.position = [10, 20, 30]
24
+ # m.texcoords = make texcoords
25
+ # m.vertices = make vertices
26
+ #
27
+ class Model < SceneObject
28
+
29
+ attr_reader :texture, :geometry, :transform, :material, :shader
30
+
31
+ delegate_accessor :geometry, :type, :vertices, :texcoords, :colors, :normals
32
+ delegate_accessor :transform, :position, :rotation, :scale, :matrix, :x, :y, :z, :angle
33
+ delegate_accessor :material, :ambient, :diffuse, :specular, :shininess, :emission
34
+
35
+ def initialize(*a,&b)
36
+ @transform = Transform.new
37
+ @material = Material.new
38
+ @texture = Texture.new
39
+ @shader = Shader.new
40
+ @geometry = Geometry.new
41
+ super
42
+ attach @material
43
+ @material.attach @texture
44
+ @texture.attach @shader
45
+ @shader.attach @geometry
46
+ end
47
+
48
+ def push_state
49
+ @transform.push_state
50
+ super
51
+ end
52
+
53
+ def pop_state
54
+ @transform.pop_state
55
+ super
56
+ end
57
+
58
+ def apply
59
+ @transform.apply
60
+ super
61
+ end
62
+
63
+ def absolute_transform
64
+ @transform.instance_variable_set :@parent , @parent
65
+ @transform.absolute_transform
66
+ end
67
+
68
+ # Replaces transform with t using Transform#replace!
69
+ # If t is a matrix, creates a Transform with t as the collapsed matrix.
70
+ def transform= t
71
+ unless t.is_a? Transform
72
+ t = Transform.new(:collapsed_matrix => t, :position => nil, :rotation => nil, :scale => nil, :matrix => nil)
73
+ end
74
+ @transform.replace! t
75
+ end
76
+
77
+ # Replaces texture with t. Uses Texture#replace!
78
+ def texture= t
79
+ @texture.replace! t
80
+ end
81
+
82
+ # Replaces geometry with g using Geometry#replace!
83
+ def geometry= g
84
+ @geometry.replace! g
85
+ end
86
+
87
+ # Replaces material with m using Material#replace!
88
+ def material= m
89
+ @material.replace! m
90
+ end
91
+
92
+ # Replaces shader with s using Shader#replace!
93
+ def shader= s
94
+ @shader.replace! s
95
+ end
96
+
97
+ def inspect
98
+ super[0..-2] + "::#{ [@transform, @material, @texture, @shader, @geometry].map{|i|i.inspect[9..-2]}.join("::") }>"
99
+ end
100
+
101
+ end
102
+
103
+ end
@@ -0,0 +1,21 @@
1
+ require 'glimr/renderer/sceneobject'
2
+
3
+
4
+ module GlimR
5
+
6
+ class OrthoProjection < SceneObject
7
+
8
+ attr_accessor :width, :height, :x, :y, :near, :far
9
+
10
+ def apply(x=x, y=y, width=width, height=height)
11
+ MatrixMode(PROJECTION)
12
+ LoadIdentity()
13
+ Ortho(x,width, y,height, near,far)
14
+ MatrixMode(MODELVIEW)
15
+ LoadIdentity()
16
+ end
17
+
18
+ end
19
+
20
+ end
21
+
@@ -0,0 +1,34 @@
1
+ require 'glimr/renderer/sceneobject'
2
+
3
+
4
+ module GlimR
5
+
6
+
7
+ # Raw is a SceneObject that calls its #renderer
8
+ # when applied.
9
+ #
10
+ # Use for doing custom OpenGL calls and such.
11
+ #
12
+ # Raw doesn't work with the Viewport's collapsing #render,
13
+ # so is pretty much useless at the moment.
14
+ #
15
+ # Example:
16
+ #
17
+ # r = Raw.new
18
+ # r.renderer = lambda do
19
+ # puts 'Hi, I was just applied!'
20
+ # end
21
+ #
22
+ class Raw < SceneObject
23
+
24
+ attr_accessor :renderer
25
+
26
+ # Calls #renderer if renderer is set.
27
+ def apply
28
+ renderer.call if renderer
29
+ end
30
+
31
+ end
32
+
33
+
34
+ end
@@ -0,0 +1,279 @@
1
+ require 'glimr/eventlistener'
2
+ require 'glimr/util'
3
+ require 'opengl'
4
+ require 'set'
5
+
6
+
7
+ module GlimR
8
+
9
+
10
+ # The SceneObject is the root class of the GlimR scene graph class hierarchy.
11
+ #
12
+ # A SceneObject may have a parent and children, and usually will.
13
+ # All actual OpenGL state manipulation should be done with its
14
+ # specific subclasses like Transform, Geometry and Texture.
15
+ #
16
+ # The SceneObject is an EventListener, and as such you can tell
17
+ # it to listen and respond to events.
18
+ #
19
+ # SceneObjects also maintain a list of drawable objects underneath them.
20
+ # This is mainly used to speed up rendering by removing the need to traverse
21
+ # the whole scene graph to draw the scene.
22
+ #
23
+ # Example:
24
+ # s = SceneObject.new
25
+ # t = Texture.new
26
+ # s.attach t
27
+ # s.detach t
28
+ #
29
+ class SceneObject
30
+ include GL
31
+ include EventListener
32
+ include Configurable
33
+
34
+ attr_accessor :parent, :children, :viewport, :drawables
35
+ attr_reader :mtime
36
+ touching_accessor :visible
37
+
38
+ def default_config
39
+ super.merge(
40
+ :visible => true,
41
+ :mtime => Time.now.to_f
42
+ )
43
+ end
44
+
45
+ # Configurable initialize.
46
+ def initialize(*arg_hash, &config_block)
47
+ @children = []
48
+ @drawables = Set.new
49
+ super
50
+ end
51
+
52
+ # Returns the viewport or possible parent's viewport.
53
+ def viewport
54
+ @viewport ||= (parent and parent.viewport)
55
+ end
56
+
57
+ # Returns the scene root.
58
+ # This is either the viewport,
59
+ # the parent's scene root if there is no viewport,
60
+ # or self if there is no parent.
61
+ #
62
+ def root
63
+ (viewport || (parent ? parent.root : self))
64
+ end
65
+
66
+ # Sets viewport to vp.
67
+ # Passes viewport change to children.
68
+ #
69
+ def viewport= vp
70
+ if vp != @viewport
71
+ @viewport = vp
72
+ children.each{|c| c.viewport = vp}
73
+ end
74
+ end
75
+
76
+ # Attaches new_children to the scene graph below self.
77
+ def attach(*new_children)
78
+ new_children.each{|c|
79
+ adopt(c)
80
+ }
81
+ end
82
+
83
+ # Attach single child.
84
+ def <<(new_child)
85
+ attach new_child
86
+ self
87
+ end
88
+
89
+ # Detaches children_to_detach from self.
90
+ def detach(*children_to_detach)
91
+ children_to_detach.each{|c|
92
+ orphan(c)
93
+ }
94
+ end
95
+
96
+ # Detaches self from parent if there is a parent.
97
+ def detach_self
98
+ parent.detach(self) if parent
99
+ end
100
+
101
+ # Sets parent to p.
102
+ #
103
+ # Causes viewport to be set to nil and drawables to
104
+ # be added to parent's drawables.
105
+ def parent= new_parent
106
+ #~ if @parent
107
+ #~ puts object_id, "reparenting #{self} from #{@parent} to #{new_parent.inspect}"
108
+ #~ d = @parent
109
+ #~ end
110
+ @parent.remove_drawables drawables if @parent
111
+ @parent = new_parent
112
+ self.viewport = nil
113
+ @parent.add_drawables drawables if @parent
114
+ #~ p [:after, d.drawables] if d
115
+ #~ puts '','' if d
116
+ end
117
+
118
+ # A node is visible if its @visible is true and its parent is visible (if it has a parent.)
119
+ def visible
120
+ @visible and (parent ? parent.visible : true)
121
+ end
122
+
123
+ # Adds d to drawables and parent's drawables.
124
+ def add_drawables(d)
125
+ parent.add_drawables d if parent
126
+ @drawables += d
127
+ end
128
+
129
+ # Removes d from drawables and parent's drawables.
130
+ def remove_drawables(d)
131
+ parent.remove_drawables d if parent
132
+ @drawables -= d
133
+ end
134
+
135
+ # Sets drawables to d and updates parent's drawables.
136
+ def drawables= d
137
+ if (d != @drawables) and parent
138
+ parent.drawables = ((parent.drawables - @drawables) + d)
139
+ end
140
+ @drawables = d
141
+ end
142
+
143
+ # Replaces orig node in scene graph with replacement by
144
+ # Attaching the replacement to orig's parent, and attaching
145
+ # orig's children to replacement.
146
+ #
147
+ # If no replacement is given, removes orig from scenegraph
148
+ # and attaches its children to its parent. If there is no parent or
149
+ # replacement, does nothing.
150
+ #
151
+ def replace_node(orig, replacement=nil)
152
+ return unless orig
153
+ replacement ||= orig.parent
154
+ return unless replacement
155
+ replacement.detach(*orig.children)
156
+ replacement.attach(*orig.children)
157
+ orig.detach(*orig.children)
158
+ orig.parent.attach(replacement) if orig.parent
159
+ orig.detach_self
160
+ end
161
+
162
+ # Renders the SceneObject by pushing state, applying, calling render
163
+ # for each child, and popping state.
164
+ #
165
+ # Not used with collapsing Viewport #render.
166
+ def render(*a)
167
+ push_state
168
+ apply
169
+ children.each{|c| c.render if c.visible }
170
+ pop_state
171
+ end
172
+
173
+ # The collapsed state of a SceneObject is its parent's collapsed state,
174
+ # which fits in with SceneObjects not editing OpenGL state.
175
+ def absolute_transform
176
+ parent.absolute_transform
177
+ end
178
+
179
+ def absolute_transform_for_drawing
180
+ absolute_transform
181
+ end
182
+
183
+ def absolute_material
184
+ #~ unless parent
185
+ #~ puts object_id, geometry.object_id, self, geometry
186
+ #~ ObjectSpace.each_object(Button){|b|
187
+ #~ if b.drawables.include? geometry
188
+ #~ puts b
189
+ #~ p b.drawables.to_a, b.children
190
+ #~ puts
191
+ #~ puts
192
+ #~ cc = b.children.find{|c|
193
+ #~ c.geometry == geometry if c.is_a? Model
194
+ #~ }
195
+ #~ p cc
196
+ #~ end
197
+ #~ }
198
+ #~ end
199
+ parent.absolute_material
200
+ end
201
+
202
+ def absolute_texture
203
+ parent.absolute_texture
204
+ end
205
+
206
+ def absolute_geometry
207
+ parent.absolute_geometry
208
+ end
209
+
210
+ def absolute_shader
211
+ parent.absolute_shader
212
+ end
213
+
214
+ # Applies the SceneObject to OpenGL state.
215
+ # For SceneObject, this is empty.
216
+ def apply
217
+ end
218
+
219
+ # Pushes the relevant OpenGL state stack.
220
+ # Empty for SceneObject.
221
+ def push_state
222
+ end
223
+
224
+ # Pops the relevant OpenGL state stack.
225
+ # Empty for SceneObject.
226
+ def pop_state
227
+ end
228
+
229
+ # Creates a clone of the SceneObject.
230
+ # If deep is set to true, also clones the children.
231
+ # If deep is false, clears children.
232
+ #
233
+ def clone(deep=false)
234
+ c = super()
235
+ if deep
236
+ c.children = []
237
+ c.drawables.clear
238
+ cchildren = c.children.map{|ch| ch.clone(deep) }
239
+ c.attach *cchildren
240
+ else
241
+ c.children = []
242
+ c.drawables.clear
243
+ end
244
+ c.parent = nil
245
+ c
246
+ end
247
+
248
+ def inspect
249
+ "#<#{self.class.name}:#{object_id}>"
250
+ end
251
+
252
+ def touch!(mtime=Time.now.to_f)
253
+ @mtime = mtime
254
+ parent.touch!(@mtime) if parent
255
+ nil
256
+ end
257
+
258
+ private
259
+
260
+ def adopt(c)
261
+ children.push c
262
+ c.parent = self
263
+ c.listener_count.each{|k,v| increment_listener_count k, v }
264
+ touch!
265
+ end
266
+
267
+ def orphan(c)
268
+ if children.include? c
269
+ children.delete c
270
+ c.parent = nil
271
+ c.listener_count.each{|k,v| decrement_listener_count k, v }
272
+ touch!
273
+ end
274
+ end
275
+
276
+ end
277
+
278
+
279
+ end