Class: GlimR::GLUTWindow

Inherits:
Object show all
Defined in:
lib/glimr/renderer/glutwindow.rb

Overview

The GLUTWindow provides a stand-alone OpenGL context for GlimR.

When rendering, a GLUTWindow first sends a frame event to its viewport, then clears the buffer and tells its viewport to draw itself.

The GLUTWindow is also responsible for mapping GLUT events into GlimR events and sending them to the viewport.

Constant Summary collapse

MOUSE_BUTTONS =
[:left, :middle, :right, :wheel_up, :wheel_down]

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(w = 400, h = 300, title = "GlimR - #{$0}", x = nil, y = nil) ⇒ GLUTWindow

Creates a window with width w and height h.



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/glimr/renderer/glutwindow.rb', line 78

def initialize(w=400,h=300,title="GlimR - #{$0}",x=nil,y=nil)
  @viewport = Viewport.new
  @viewport.background = [1,1,1,1]
  @viewport.clear_depth_buffer = true
  @visible = true
  @display_mode = GLUT::DOUBLE | GLUT::RGB | GLUT::DEPTH
  @x = x
  @y = y
  @viewport.x = 0
  @viewport.y = 0
  @frame_event = Event.new(:frame)
  @reshape_event = Event.new(:resize)
  @frame_time = @last_frame_time = Time.now.to_f
  @frame = @frames_drawn = 0
  @title = title
  @event_queue = {}
  @mouse_buttons_down = {}
  @mouse_properties = {}
  @modifiers = {}
  reshape(w,h)
  init_glut
end

Instance Attribute Details

#display_modeObject (readonly)

Returns the value of attribute display_mode.



73
74
75
# File 'lib/glimr/renderer/glutwindow.rb', line 73

def display_mode
  @display_mode
end

#frameObject (readonly)

Returns the value of attribute frame.



73
74
75
# File 'lib/glimr/renderer/glutwindow.rb', line 73

def frame
  @frame
end

#frames_drawnObject (readonly)

Returns the value of attribute frames_drawn.



73
74
75
# File 'lib/glimr/renderer/glutwindow.rb', line 73

def frames_drawn
  @frames_drawn
end

#heightObject (readonly)

Returns the value of attribute height.



73
74
75
# File 'lib/glimr/renderer/glutwindow.rb', line 73

def height
  @height
end

#titleObject (readonly)

Returns the value of attribute title.



73
74
75
# File 'lib/glimr/renderer/glutwindow.rb', line 73

def title
  @title
end

#viewportObject

Returns the value of attribute viewport.



71
72
73
# File 'lib/glimr/renderer/glutwindow.rb', line 71

def viewport
  @viewport
end

#widthObject (readonly)

Returns the value of attribute width.



73
74
75
# File 'lib/glimr/renderer/glutwindow.rb', line 73

def width
  @width
end

#window_idObject

Returns the value of attribute window_id.



72
73
74
# File 'lib/glimr/renderer/glutwindow.rb', line 72

def window_id
  @window_id
end

#xObject (readonly)

Returns the value of attribute x.



73
74
75
# File 'lib/glimr/renderer/glutwindow.rb', line 73

def x
  @x
end

#yObject (readonly)

Returns the value of attribute y.



73
74
75
# File 'lib/glimr/renderer/glutwindow.rb', line 73

def y
  @y
end

Class Method Details

.create_window(window) ⇒ Object

Creates a GLUT window with the title and adds it to windows list.



51
52
53
54
55
56
57
58
# File 'lib/glimr/renderer/glutwindow.rb', line 51

def self.create_window(window)
  GLUT.InitDisplayMode(window.display_mode)
  GLUT.InitWindowSize(window.width, window.height)
  GLUT.InitWindowPosition(window.x, window.y) if window.x and window.y
  window.window_id = GLUT.CreateWindow window.title
  windows << window
  window.window_id
end

.current_windowObject

Id of current GLUT window.



34
35
36
# File 'lib/glimr/renderer/glutwindow.rb', line 34

def self.current_window
  windows[window_index]
end

.destroy_window(w) ⇒ Object

Destroys the GLUT window w and deletes it from the windows list. Exits if the window in question was the last remaining window.



62
63
64
65
66
# File 'lib/glimr/renderer/glutwindow.rb', line 62

def self.destroy_window(w)
  windows.delete w
  GLUT.DestroyWindow w.window_id
  exit if windows.empty?
end

.next_windowObject

Sets the active GLUT window to the next window in order.



39
40
41
42
43
44
45
46
47
48
# File 'lib/glimr/renderer/glutwindow.rb', line 39

def self.next_window
  if @window_index == 0
    slp = 0.033 - (Time.now.to_f - @frame_time)
    sleep(slp) if slp > 0
    @frame_time = Time.now.to_f
  end
  @window_index = (window_index + 1)%windows.size
  GLUT.SetWindow(current_window.window_id)
  current_window.heartbeat
end

.window_indexObject

Index of the current window.



24
25
26
# File 'lib/glimr/renderer/glutwindow.rb', line 24

def self.window_index
  @window_index ||= 0
end

.windowsObject

Array of windows created.



29
30
31
# File 'lib/glimr/renderer/glutwindow.rb', line 29

def self.windows
  @windows ||= []
end

Instance Method Details

#button_box(*a) ⇒ Object

GLUT ButtonBoxFunc.



305
306
# File 'lib/glimr/renderer/glutwindow.rb', line 305

def button_box(*a)
end

#changed?Boolean

Returns:

  • (Boolean)


134
135
136
# File 'lib/glimr/renderer/glutwindow.rb', line 134

def changed?
  viewport.changed?
end

#close(*a) ⇒ Object

Asks GLUTWindow class to destroy the window.



181
182
183
# File 'lib/glimr/renderer/glutwindow.rb', line 181

def close(*a)
  self.class.destroy_window(self)
end

#damaged?Boolean

Returns:

  • (Boolean)


130
131
132
# File 'lib/glimr/renderer/glutwindow.rb', line 130

def damaged?
  GLUT.LayerGet(GLUT::NORMAL_DAMAGED) == GL::TRUE
end

#displayObject

GLUT DisplayFunc.

Displays the GLUTWindow. Sleeps to maintain a maximum framerate of 33fps, then displays previously rendered frame. Then sends a frame event to the viewport.

If the GLUTWindow is visible, clears the buffer and tells viewport to draw itself. Finally increments the frame counter.



173
174
175
176
177
178
# File 'lib/glimr/renderer/glutwindow.rb', line 173

def display
  GC.disable
  viewport.render
  GLUT.SwapBuffers
  @frames_drawn += 1
end

#entry(enter) ⇒ Object

GLUT EntryFunc.



268
269
270
271
272
273
274
275
# File 'lib/glimr/renderer/glutwindow.rb', line 268

def entry(enter)
  @mouse_over_window = (enter == 1)
  if @mouse_over_window
    @mouse_x, @mouse_y = @prev_mouse_x, @prev_mouse_y
  else
    @mouse_x = @mouse_y = nil
  end
end

#fullscreen(*a) ⇒ Object

Toggles fullscreen mode.



186
187
188
189
190
191
192
193
194
195
196
197
198
# File 'lib/glimr/renderer/glutwindow.rb', line 186

def fullscreen(*a)
  if not @fullscreen
    @fullscreen = %w(X Y WIDTH HEIGHT).map{|cn|
      GLUT.Get(GLUT.const_get("WINDOW_#{cn}"))
    }
    GLUT.FullScreen
  else
    x,y,w,h = *@fullscreen
    GLUT.ReshapeWindow(w,h)
    GLUT.PositionWindow(x,y)
    @fullscreen = nil
  end
end

#heartbeatObject



138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/glimr/renderer/glutwindow.rb', line 138

def heartbeat
  GC.disable
  @frame_time = Time.now.to_f
  @frame_event.time = @frame_time
  @frame_event.bubbles = false
  @event_queue.values.each{|evt|
    viewport.dispatch_event(evt)
  }
  @event_queue.clear
  viewport.multicast_event(@frame_event)
  GLUT.PostRedisplay if need_redraw?
  GC.enable
  @frame += 1
end

#init_glutObject

Initialize GLUT state for the window, then ask GLUTWindow class to create the window.



103
104
105
106
# File 'lib/glimr/renderer/glutwindow.rb', line 103

def init_glut
  self.class.create_window(self)
  set_callback_funcs
end

#keyboard(key, x, y) ⇒ Object

GLUT KeyboardFunc. Fired when key pressed. Multicasts key_down and key_up.

Note: there is no “Hold button down”-functionality in GLUT.



343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
# File 'lib/glimr/renderer/glutwindow.rb', line 343

def keyboard(key, x, y)
  event_properties = update_coords(x,y).merge(:key => key)
  ev = Event.new(:key_down,  event_properties)
  @viewport.multicast_event(ev)
  ev = Event.new(:key_up, event_properties)
  @viewport.multicast_event(ev)
  handler = keyhandlers[key] if @modifiers[:ctrl] and not (@modifiers[:shift] or @modifiers[:alt])
  if handler and not (ev.stopped or ev.cancelled)
    if handler.respond_to? :call
      handler.call(key, x, y)
    else
      __send__(handler, key, x, y)
    end
  end
end

#keyhandlersObject

Default key handlers.

ESC closes window, f toggles fullscreen, p enters profiler, s takes screenshot.



376
377
378
379
380
381
382
383
# File 'lib/glimr/renderer/glutwindow.rb', line 376

def keyhandlers
  @keyhandlers ||= {
    17 => :close,
    6 => :fullscreen,
    16 => :profiler,
    20 => :take_screenshot,
  }
end

#motion(x, y) ⇒ Object

GLUT MotionFunc. Moving mouse with mouse button depressed.



299
300
301
302
# File 'lib/glimr/renderer/glutwindow.rb', line 299

def motion(x,y)
  event_properties = update_coords(x,y,false)
  queue_event Event.new(:mouse_move, event_properties)
end

#mouse(button, down, x, y) ⇒ Object

GLUT MouseFunc. Fired when mouse button pressed or released.



318
319
320
321
322
323
324
325
326
# File 'lib/glimr/renderer/glutwindow.rb', line 318

def mouse(button, down, x, y)
  down = (down == 0)
  button = MOUSE_BUTTONS[button]
  @mouse_buttons_down[button] = down
  event_type = (down ? :mouse_down : :mouse_up)
  event_properties = update_coords(x,y)
  event_properties[:button] = button
  queue_event Event.new(event_type, event_properties)
end

#need_redraw?Boolean

Returns:

  • (Boolean)


126
127
128
# File 'lib/glimr/renderer/glutwindow.rb', line 126

def need_redraw?
  @visible and (changed? or damaged?)
end

#passive_motion(x, y) ⇒ Object

GLUT PassiveMotionFunc. Moving mouse without buttons depressed



293
294
295
296
# File 'lib/glimr/renderer/glutwindow.rb', line 293

def passive_motion(x,y)
  event_properties = update_coords(x,y,false)
  queue_event Event.new(:mouse_move, event_properties)
end

#profiler(*a) ⇒ Object

Enters the profiler, lets run for 5 seconds, then exits.



207
208
209
210
211
212
213
214
# File 'lib/glimr/renderer/glutwindow.rb', line 207

def profiler(*a)
  Thread.new{
    puts "Entering profiler"
    require 'profile'
    sleep 5
    exit
  }
end

#queue_event(evt) ⇒ Object



153
154
155
156
157
158
159
160
161
162
# File 'lib/glimr/renderer/glutwindow.rb', line 153

def queue_event evt
  if evt.type == :mouse_move and @event_queue[:mouse_move]
    evt.dx += @event_queue[:mouse_move].dx
    evt.dy += @event_queue[:mouse_move].dy
    evt.params[:dx] = evt.dx
    evt.params[:dy] = evt.dy
    evt.params[:time] = evt.time = Time.now.to_f
  end
  @event_queue[evt.type] = evt
end

#reshape(w, h) ⇒ Object

GLUT ReshapeFunc. Sets viewport width and height to w and h, respectively, and sends a resize event to the viewport.



111
112
113
114
115
116
117
118
119
# File 'lib/glimr/renderer/glutwindow.rb', line 111

def reshape(w,h)
  @width = w
  @height = h
  @reshape_event.time = Time.now.to_f
  @reshape_event.bubbles = false
  @reshape_event.width = w
  @reshape_event.height = h
  viewport.multicast_event(@reshape_event)
end

#runObject

Enters GLUT.MainLoop.



122
123
124
# File 'lib/glimr/renderer/glutwindow.rb', line 122

def run
  GLUT.MainLoop
end

#set_callback_funcsObject

Set GLUT callback functions to the object’s methods that have the rubyized name of the corresponding callback function. That’s not very clear, is it. Maybe this example sheds some light on it.

GLUT::ReshapeFunc is set to "reshape"-method
GLUT::SpaceballMotionFunc is set to "spaceball_motion"-method
GLUT::MouseFunc is set to "mouse"-method
... and so on for every GLUT method that ends with Func.

If there is no corresponding method (e.g. there’s no “idle”-method), the callback won’t be set.

List of callback names (avoid these in non-callback method names):

ButtonBoxFunc       => button_box
DialsFunc           => dials
DisplayFunc         => display
EntryFunc           => entry
IdleFunc            => idle
KeyboardFunc        => keyboard
MotionFunc          => motion
MouseFunc           => mouse
OverlayDisplayFunc  => overlay_display
PassiveMotionFunc   => passive_motion
ReshapeFunc         => reshape
SpaceballButtonFunc => spaceball_button
SpaceballMotionFunc => spaceball_motion
SpaceballRotateFunc => spaceball_rotate
SpecialFunc         => special
TabletButtonFunc    => tablet_button
TabletMotionFunc    => tablet_motion
TimerFunc           => timer
VisibilityFunc      => visibility
WindowStatusFunc    => window_status


251
252
253
254
255
256
257
258
259
260
261
262
263
# File 'lib/glimr/renderer/glutwindow.rb', line 251

def set_callback_funcs
  GLUT.methods.grep(/Func$/).sort.each{|callback_setter_name|
    # Change FooBarFunc into foo_bar

    callback_method_name = callback_setter_name[0,1].downcase +
                           callback_setter_name[1..-5].
                           gsub(/[A-Z]/){|cap| "_" + cap.downcase}
    if respond_to?(callback_method_name.to_sym)
      callback_method = method(callback_method_name).to_proc
      GLUT.__send__(callback_setter_name, callback_method)
    end
  }
  @mouse_over_window = true
end

#special(key, x, y) ⇒ Object

Handles special key presses.

Converts the special key to a symbol by stripping KEY_ off the front and downcasing the remains, e.g. :f1, :left, :right.

Delegates the symbol to #keyboard.



366
367
368
369
370
# File 'lib/glimr/renderer/glutwindow.rb', line 366

def special(key, x, y)
  cn = GLUT.constants.grep(/^KEY_/).find{|k| GLUT.const_get(k) == key}
  key = cn.sub(/^KEY_/, '').downcase.to_sym
  keyboard(key, x, y)
end

#take_screenshot(*a) ⇒ Object

Takes screenshot (not implemented.)



201
202
203
# File 'lib/glimr/renderer/glutwindow.rb', line 201

def take_screenshot(*a)
  #screenshot

end

#update_coords(x, y, update_modifier_keys = true) ⇒ Object

Updates current mouse coordinates.



278
279
280
281
282
283
284
285
286
287
288
289
290
# File 'lib/glimr/renderer/glutwindow.rb', line 278

def update_coords(x,y,update_modifier_keys=true)
  props = {
    :target => @target,
    :x => x, :y => y,
    :dx => (@prev_mouse_x ? x-@prev_mouse_x : 0),
    :dy => (@prev_mouse_y ? y-@prev_mouse_y : 0),
    :buttons => @mouse_buttons_down
  }
  @prev_mouse_x, @prev_mouse_y = x, y
  @mouse_x, @mouse_y = x, y if @mouse_over_window
  update_modifiers if update_modifier_keys
  @mouse_properties = @mouse_properties.merge(props).merge(@modifiers)
end

#update_modifiersObject

Gets modifier key states and assigns them to @modifiers-Hash and returns the Hash.



330
331
332
333
334
335
336
# File 'lib/glimr/renderer/glutwindow.rb', line 330

def update_modifiers
  modifier_bitfield = GLUT.GetModifiers
  GLUT.constants.grep(/^ACTIVE_/).each{|cn|
    @modifiers[cn[7..-1].downcase.to_sym] = (modifier_bitfield & GLUT.const_get(cn) > 0)
  }
  @modifiers
end

#visibility(visible) ⇒ Object

GLUT VisibilityFunc. Toggles window visibility variable based on the argument.



309
310
311
# File 'lib/glimr/renderer/glutwindow.rb', line 309

def visibility(visible)
  @visible = (visible == GLUT::VISIBLE)
end

#window_status(*a) ⇒ Object

GLUT WindowStatusFunc.



314
315
# File 'lib/glimr/renderer/glutwindow.rb', line 314

def window_status(*a)
end