Codebase list ruby-fxruby / a7648430-de30-43e3-bfa2-62f1cace7689/upstream examples / gltest.rb
a7648430-de30-43e3-bfa2-62f1cace7689/upstream

Tree @a7648430-de30-43e3-bfa2-62f1cace7689/upstream (Download .tar.gz)

gltest.rb @a7648430-de30-43e3-bfa2-62f1cace7689/upstream

ddfc60e
 
 
 
 
 
 
 
7b0be03
 
ddfc60e
 
 
 
 
 
7b0be03
 
 
ddfc60e
 
 
 
 
 
7b0be03
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ddfc60e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7b0be03
ddfc60e
7b0be03
 
 
ddfc60e
7b0be03
ddfc60e
7b0be03
 
 
ddfc60e
7b0be03
 
 
ddfc60e
7b0be03
 
 
 
 
 
ddfc60e
7b0be03
 
ddfc60e
7b0be03
 
ddfc60e
 
7b0be03
 
ddfc60e
7b0be03
 
 
ddfc60e
7b0be03
ddfc60e
7b0be03
 
 
ddfc60e
7b0be03
ddfc60e
7b0be03
 
 
 
ddfc60e
7b0be03
ddfc60e
7b0be03
 
 
 
ddfc60e
7b0be03
ddfc60e
7b0be03
 
 
 
ddfc60e
7b0be03
ddfc60e
7b0be03
 
 
 
ddfc60e
7b0be03
ddfc60e
7b0be03
ddfc60e
 
 
 
 
 
 
 
 
 
 
 
 
 
7b0be03
 
ddfc60e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7b0be03
ddfc60e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7b0be03
ddfc60e
 
 
 
 
 
 
 
 
 
#!/usr/bin/env ruby

require 'fox16'
begin
  require 'opengl'
rescue LoadError
  require 'fox16/missingdep'
  MSG = <<EOM
  Sorry, this example depends on the opengl-bindings gems. Please execute:
    gem install opengl-bindings
EOM
  missingDependency(MSG)
end



class GLTestWindow < Fox::FXMainWindow
  include Fox
  include OpenGL

  # How often our timer will fire (in milliseconds)
  TIMER_INTERVAL = 100

  # Draws a simple box using the given corners
  def drawBox(xmin, ymin, zmin, xmax, ymax, zmax)
    glBegin(GL_TRIANGLE_STRIP)
      glNormal3d(0.0, 0.0, -1.0)
      glVertex3d(xmin, ymin, zmin)
      glVertex3d(xmin, ymax, zmin)
      glVertex3d(xmax, ymin, zmin)
      glVertex3d(xmax, ymax, zmin)
    glEnd()

    glBegin(GL_TRIANGLE_STRIP)
      glNormal3d(1.0, 0.0, 0.0)
      glVertex3d(xmax, ymin, zmin)
      glVertex3d(xmax, ymax, zmin)
      glVertex3d(xmax, ymin, zmax)
      glVertex3d(xmax, ymax, zmax)
    glEnd()

    glBegin(GL_TRIANGLE_STRIP)
      glNormal3d(0.0, 0.0, 1.0)
      glVertex3d(xmax, ymin, zmax)
      glVertex3d(xmax, ymax, zmax)
      glVertex3d(xmin, ymin, zmax)
      glVertex3d(xmin, ymax, zmax)
    glEnd()

    glBegin(GL_TRIANGLE_STRIP)
      glNormal3d(-1.0, 0.0, 0.0)
      glVertex3d(xmin, ymin, zmax)
      glVertex3d(xmin, ymax, zmax)
      glVertex3d(xmin, ymin, zmin)
      glVertex3d(xmin, ymax, zmin)
    glEnd()

    glBegin(GL_TRIANGLE_STRIP)
      glNormal3d(0.0, 1.0, 0.0)
      glVertex3d(xmin, ymax, zmin)
      glVertex3d(xmin, ymax, zmax)
      glVertex3d(xmax, ymax, zmin)
      glVertex3d(xmax, ymax, zmax)
    glEnd()

    glBegin(GL_TRIANGLE_STRIP)
      glNormal3d(0.0, -1.0, 0.0)
      glVertex3d(xmax, ymin, zmax)
      glVertex3d(xmax, ymin, zmin)
      glVertex3d(xmin, ymin, zmax)
      glVertex3d(xmin, ymin, zmin)
    glEnd()
  end

  def gluPerspective(fovY, aspect, zNear, zFar )
    fH = Math.tan( fovY / 360 * Math::PI ) * zNear;
    fW = fH * aspect;

    glFrustum( -fW, fW, -fH, fH, zNear, zFar );
  end

  # Define our own version of gluLookAt() to avoid dependency to deprecated GLU library
  def gluLookAt(ex, ey, ez,
           lx, ly, lz,
           ux, uy, uz )
    z0 = ex - lx
    z1 = ey - ly
    z2 = ez - lz
    l = z0*z0+z1*z1+z2*z2
    if l != 0 then
      l = 1.0 / Math.sqrt(l)
      z0 *= l
      z1 *= l
      z2 *= l
    end

    x0 = uy*z2-uz*z1
    x1 = -ux*z2+uz*z0
    x2 = ux*z1-uy*z0
    l = x0*x0+x1*x1+x2*x2
    if l != 0 then
      l = 1.0 / Math.sqrt(l)
      x0 *= l
      x1 *= l
      x2 *= l
    end

    y0 = z1*x2-z2*x1
    y1 = -z0*x2+z2*x0
    y2 = z0*x1-z1*x0
    l = y0*y0+y1*y1+y2*y2
    if l != 0 then
      l = 1.0 / Math.sqrt(l)
      y0 *= l
      y1 *= l
      y2 *= l
    end

    m4x4 = [
      x0,y0,z0,0,
      x1,y1,z1,0,
      x2,y2,z2,0,
      0,  0, 0,1,
    ].pack("d*")
    glMultMatrixd m4x4
    glTranslated -ex,-ey,-ez
  end

  # Draw the GL scene
  def drawScene
    lightPosition = [15.0, 10.0, 5.0, 1.0]
    lightAmbient  = [ 0.1,  0.1, 0.1, 1.0]
    lightDiffuse  = [ 0.9,  0.9, 0.9, 1.0]
    redMaterial   = [ 1.0,  0.0, 0.0, 1.0]
    blueMaterial  = [ 0.0,  0.0, 1.0, 1.0]

    width = @glcanvas.width.to_f
    height = @glcanvas.height.to_f
    aspect = width/height

    # Make context current
    @glcanvas.makeCurrent()

    glViewport(0, 0, @glcanvas.width, @glcanvas.height)

    glClearColor(1.0, 1.0, 1.0, 1.0)
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
    glEnable(GL_DEPTH_TEST)

    glDisable(GL_DITHER)

    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    gluPerspective(30.0, aspect, 1.0, 100.0)

    glMatrixMode(GL_MODELVIEW)
    glLoadIdentity()
    gluLookAt(5.0, 10.0, 15.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0)

    glShadeModel(GL_SMOOTH)
    glLightfv(GL_LIGHT0, GL_POSITION, lightPosition.pack("f*"))
    glLightfv(GL_LIGHT0, GL_AMBIENT, lightAmbient.pack("f*"))
    glLightfv(GL_LIGHT0, GL_DIFFUSE, lightDiffuse.pack("f*"))
    glEnable(GL_LIGHT0)
    glEnable(GL_LIGHTING)

    glMaterialfv(GL_FRONT, GL_AMBIENT, blueMaterial.pack("f*"))
    glMaterialfv(GL_FRONT, GL_DIFFUSE, blueMaterial.pack("f*"))

    glPushMatrix()
    glRotated(@angle, 0.0, 1.0, 0.0)
    drawBox(-1, -1, -1, 1, 1, 1)

    glMaterialfv(GL_FRONT, GL_AMBIENT, redMaterial.pack("f*"))
    glMaterialfv(GL_FRONT, GL_DIFFUSE, redMaterial.pack("f*"))

    glPushMatrix()
    glTranslated(0.0, 1.75, 0.0)
    glRotated(@angle, 0.0, 1.0, 0.0)
    drawBox(-0.5, -0.5, -0.5, 0.5, 0.5, 0.5)
    glPopMatrix()

    glPushMatrix()
    glTranslated(0.0, -1.75, 0.0)
    glRotated(@angle, 0.0, 1.0, 0.0)
    drawBox(-0.5, -0.5, -0.5, 0.5, 0.5, 0.5)
    glPopMatrix()

    glPushMatrix()
    glRotated(90.0, 1.0, 0.0, 0.0)
    glTranslated(0.0, 1.75, 0.0)
    glRotated(@angle, 0.0, 1.0, 0.0)
    drawBox(-0.5,-0.5,-0.5,0.5,0.5,0.5)
    glPopMatrix()

    glPushMatrix()
    glRotated(90.0, -1.0, 0.0, 0.0)
    glTranslated(0.0,1.75,0.0)
    glRotated(@angle, 0.0, 1.0, 0.0)
    drawBox(-0.5,-0.5,-0.5,0.5,0.5,0.5)
    glPopMatrix()

    glPushMatrix()
    glRotated(90.0, 0.0, 0.0, 1.0)
    glTranslated(0.0,1.75,0.0)
    glRotated(@angle, 0.0, 1.0, 0.0)
    drawBox(-0.5,-0.5,-0.5,0.5,0.5,0.5)
    glPopMatrix()

    glPushMatrix()
    glRotated(90.0, 0.0, 0.0, -1.0)
    glTranslated(0.0,1.75,0.0)
    glRotated(@angle, 0.0, 1.0, 0.0)
    drawBox(-0.5,-0.5,-0.5,0.5,0.5,0.5)
    glPopMatrix()

    glPopMatrix()

    # Swap if it is double-buffered
    if @glvisual.isDoubleBuffer
      @glcanvas.swapBuffers
    end

    # Make context non-current
    @glcanvas.makeNonCurrent
  end

  def initialize(app)
    # Invoke the base class initializer
    super(app, "OpenGL Test Application", :opts => DECOR_ALL, :width => 800, :height => 600)

    OpenGL.load_lib

    # Construct the main window elements
    frame = FXHorizontalFrame.new(self, LAYOUT_SIDE_TOP|LAYOUT_FILL_X|LAYOUT_FILL_Y)
    frame.padLeft, frame.padRight = 0, 0
    frame.padTop, frame.padBottom = 0, 0

    # Left pane to contain the glcanvas
    glcanvasFrame = FXVerticalFrame.new(frame,
      LAYOUT_FILL_X|LAYOUT_FILL_Y|LAYOUT_TOP|LAYOUT_LEFT)
    glcanvasFrame.padLeft, glcanvasFrame.padRight = 10, 10
    glcanvasFrame.padTop, glcanvasFrame.padBottom = 10, 10

    # Label above the glcanvas
    FXLabel.new(glcanvasFrame, "OpenGL Canvas Frame", nil,
      JUSTIFY_CENTER_X|LAYOUT_FILL_X)

    # Horizontal divider line
    FXHorizontalSeparator.new(glcanvasFrame, SEPARATOR_GROOVE|LAYOUT_FILL_X)

    # Drawing glcanvas
    glpanel = FXVerticalFrame.new(glcanvasFrame, (FRAME_SUNKEN|FRAME_THICK|
      LAYOUT_FILL_X|LAYOUT_FILL_Y|LAYOUT_TOP|LAYOUT_LEFT))
    glpanel.padLeft, glpanel.padRight = 0, 0
    glpanel.padTop, glpanel.padBottom = 0, 0

    # A visual to draw OpenGL
    @glvisual = FXGLVisual.new(getApp(), VISUAL_DOUBLEBUFFER)

    # Drawing glcanvas
    @glcanvas = FXGLCanvas.new(glpanel, @glvisual, :opts => LAYOUT_FILL_X|LAYOUT_FILL_Y|LAYOUT_TOP|LAYOUT_LEFT)
    @glcanvas.connect(SEL_PAINT) { drawScene }
    @glcanvas.connect(SEL_CONFIGURE) do
      if @glcanvas.makeCurrent
        glViewport(0, 0, @glcanvas.width, @glcanvas.height)
        @glcanvas.makeNonCurrent
      end
    end

    # Right pane for the buttons
    buttonFrame = FXVerticalFrame.new(frame, LAYOUT_FILL_Y|LAYOUT_TOP|LAYOUT_LEFT)
    buttonFrame.padLeft, buttonFrame.padRight = 10, 10
    buttonFrame.padTop, buttonFrame.padBottom = 10, 10

    # Label above the buttons
    FXLabel.new(buttonFrame, "Button Frame", nil,
      JUSTIFY_CENTER_X|LAYOUT_FILL_X)

    # Horizontal divider line
    FXHorizontalSeparator.new(buttonFrame, SEPARATOR_RIDGE|LAYOUT_FILL_X)

    # Spin according to timer
    spinTimerBtn = FXButton.new(buttonFrame, "Spin &Timer\tSpin using interval timers\nNote the app blocks until the interal has elapsed...",nil,nil,0,FRAME_THICK|FRAME_RAISED|LAYOUT_FILL_X|LAYOUT_TOP|LAYOUT_LEFT)
    spinTimerBtn.padLeft, spinTimerBtn.padRight = 10, 10
    spinTimerBtn.padTop, spinTimerBtn.padBottom = 5, 5
    spinTimerBtn.connect(SEL_COMMAND) do
      @spinning = true
      @timer = getApp().addTimeout(TIMER_INTERVAL, :repeat => true) do
        @angle += 2.0
        if @angle > 360.0
          @angle -= 360.0
        end
        drawScene()
      end
    end
    spinTimerBtn.connect(SEL_UPDATE) do |sender, sel, ptr|
      @spinning ? sender.disable : sender.enable
    end

    # Spin according to chore
    spinChoreBtn = FXButton.new(buttonFrame,
      "Spin &Chore\tSpin as fast as possible using chores\nNote even though the
      app is very responsive, it never blocks;\nthere is always something to
      do...", :opts => FRAME_THICK|FRAME_RAISED|LAYOUT_FILL_X|LAYOUT_TOP|LAYOUT_LEFT)
    spinChoreBtn.padLeft, spinChoreBtn.padRight = 10, 10
    spinChoreBtn.padTop, spinChoreBtn.padBottom = 5, 5
    spinChoreBtn.connect(SEL_COMMAND) do
      @spinning = true
      @chore = getApp().addChore(:repeat => true) do
        @angle += 2.0
        if @angle > 360.0
          @angle -= 360.0
        end
        drawScene()
      end
    end
    spinChoreBtn.connect(SEL_UPDATE) do |sender, sel, ptr|
      @spinning ? sender.disable : sender.enable
    end

    # Stop spinning
    stopBtn = FXButton.new(buttonFrame,
      "&Stop Spin\tStop this mad spinning, I'm getting dizzy",
      :opts => FRAME_THICK|FRAME_RAISED|LAYOUT_FILL_X|LAYOUT_TOP|LAYOUT_LEFT)
    stopBtn.padLeft, stopBtn.padRight = 10, 10
    stopBtn.padTop, stopBtn.padBottom = 5, 5
    stopBtn.connect(SEL_COMMAND) do
      @spinning = false
      if @timer
        getApp().removeTimeout(@timer)
        @timer = nil
      end
      if @chore
        getApp().removeChore(@chore)
        @chore = nil
      end
    end
    stopBtn.connect(SEL_UPDATE) do |sender, sel, ptr|
      @spinning ? sender.enable : sender.disable
    end

    # Exit button
    exitBtn = FXButton.new(buttonFrame, "&Exit\tExit the application", nil,
      getApp(), FXApp::ID_QUIT,
      FRAME_THICK|FRAME_RAISED|LAYOUT_FILL_X|LAYOUT_TOP|LAYOUT_LEFT)
    exitBtn.padLeft, exitBtn.padRight = 10, 10
    exitBtn.padTop, exitBtn.padBottom = 5, 5

    # Make a tooltip
    FXToolTip.new(getApp())

    # Initialize private variables
    @spinning = false
    @chore = nil
    @timer = nil
    @angle = 0.0
  end

  # Create and initialize
  def create
    super
    show(PLACEMENT_SCREEN)
  end
end

if __FILE__ == $0
  # Construct the application
  application = Fox::FXApp.new("GLTest", "FoxTest")

  # Construct the main window
  GLTestWindow.new(application)

  # Create the app's windows
  application.create

  # Run the application
  application.run
end