サイログ。

~雑多な記事置き場~

RubyでOpenGL続き

こないだから、久しぶりにruby-openglのプログラムを書いてみたいと思っていたので、書いてみた。
Ruby1.9.2+Ruby/SDL2.1.1+ruby-opengl0.61を使用。
(ruby-openglのビルド方法については回答できかねます…)

出来上がったのはこんな感じ。
「何このキャラ!?」と思われた方、ゲームのサンプル用に、何も考えずに手抜きで書いたものなので、ツッコミはスルーします(^^;

以下、ソースコードと画像

require 'sdl'
require 'gl'
require 'glu'

include Gl
include Glu

SCREEN_WIDTH = 640
SCREEN_HEIGHT = 480

module GLBase
  @screen = nil
  @w = 0
  @h = 0

  def GLBase.w
    @w
  end

  def GLBase.h
    @h
  end

  def GLBase.resize(w, h, theta=30.0, top=[0.0,1.0,0.0])
    # 0 < theta < 90.0
    w2 = w / 2
    h2 = h / 2
    gluPerspective(theta, w/h, -1.0, 1.0)

    # radian=2*PI*((theta/2)/360)
    # z = h * (cos(radian)/sin(radian)) = h * (1/tan(radian))
    gluLookAt(w2,h2,-(h2 / Math.tan(Math::PI * theta / 360.0)),w2,h2,0.0,*top)
  end

  def GLBase.init(w, h, theta=30.0, top=[0.0,1.0,0.0])
    @w = w
    @h = h

    SDL.init(SDL::INIT_VIDEO|SDL::INIT_AUDIO)

    SDL::GL.set_attr(SDL::GL::RED_SIZE, 8)
    SDL::GL.set_attr(SDL::GL::GREEN_SIZE, 8)
    SDL::GL.set_attr(SDL::GL::BLUE_SIZE, 8)
    SDL::GL.set_attr(SDL::GL::ALPHA_SIZE, 8)
#    SDL::GL.set_attr(SDL::GL::DEPTH_SIZE, 8)
    SDL::GL.set_attr(SDL::GL::DOUBLEBUFFER, 1)

    @screen = SDL::Screen.open(SCREEN_WIDTH,
                               SCREEN_HEIGHT,
                               32,
                               SDL::HWSURFACE | SDL::OPENGL)

    glClearColor(0.0, 0.0, 0.0, 0.0)
    glPixelStorei(GL_UNPACK_ALIGNMENT, 4)
    GLBase.resize(@w, @h, theta, top)
  end

  def GLBase.enable?
    glGetString(GL_VERSION) < "2.0"
  end

  def GLBase.main_loop
    flag = true
    while flag
      while event = SDL::Event.poll
        case event
        when SDL::Event::KeyDown
          case event.sym
          when SDL::Key::Q
          when SDL::Key::ESCAPE
            flag = false
          end
        end
      end
      glClear(GL_COLOR_BUFFER_BIT)
      yield
      SDL::GL.swap_buffers
    end
  end
end

class GLSprite
  GL_TEXTURE_RECTANGLE_EXT = 0x84F5

  def self.create(file_name)
    self.new(SDL::Surface.load(file_name))
  end

  def initialize(image)
    @image = image

    @w = @image.w
    @h = @image.h
    @ow = @w
    @oh = @h
    @ox = 0
    @oy = 0
    @oright = @ow-1
    @obottom = @oh-1
    @x  = 0
    @y  = 0
    @z  = 0
    @rot_0 = 0.0
    @rot_1 = 0.0
    @rot_2 = 0.0
    @cx = @ow / 2
    @cy = @oh / 2
    @sca_x = 1.0
    @sca_y = 1.0
    @texture = glGenTextures(1)

    glEnable(GL_TEXTURE_RECTANGLE_EXT)
    glBindTexture(GL_TEXTURE_RECTANGLE_EXT, @texture[0])
    glTexParameteri(GL_TEXTURE_RECTANGLE_EXT,GL_TEXTURE_MAG_FILTER,GL_LINEAR)
    glTexParameteri(GL_TEXTURE_RECTANGLE_EXT,GL_TEXTURE_MIN_FILTER,GL_LINEAR)
    glTexImage2D(GL_TEXTURE_RECTANGLE_EXT, 0, GL_RGBA, @w, @h, 0, 
                 GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, @image.pixels)
    glDisable(GL_TEXTURE_RECTANGLE_EXT)
  end

  def move(x,y,z=0)
    @x += x
    @y += y
    @z += z
  end

  def move_to(x,y,z=0)
    @x, @y, @z = x, y, z
  end

  def center(x,y)
    @cx, @cy = x, y
  end

  def rotate(r0,r1=0,r2=0)
    @rot_0, @rot_1, @rot_2 = r0, r1, r2
  end

  def scale(x,y)
    @sca_x, @sca_y = x, y
  end

  def resize(x,y,w,h)
    @ox,@oy,@ow,@oh=x,y,w,h
    @oright = @ox+@ow-1
    @obottom = @oy+@oh-1
  end

  def render_inner(param)
    glBegin(GL_QUADS)
    yield
    glEnd
  end

  def render
    return unless @texture

#    sw = GLBase.w
#    sh = GLBase.h

#    x2 = sw-@x
#    y2 = sh-@y
    x2 = GLBase.w-@x
    y2 = GLBase.h-@y

    r2 = x2-@ow+1
    b2 = y2-@oh+1

    glPushMatrix
    glEnable(GL_TEXTURE_2D)
    glEnable(GL_TEXTURE_RECTANGLE_EXT)
    glEnable(GL_BLEND)
    glBindTexture(GL_TEXTURE_RECTANGLE_EXT, @texture[0])
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)

#    glTranslated(sw-(@x+@cx),sh-(@y+@cy),0)
    glTranslated(x2-@cx,y2-@cy,0)

    glRotated(@rot_0, 0.0, 0.0, 1.0)
    glRotated(@rot_1, 0.0, 1.0, 0.0)
    glRotated(@rot_2, 1.0, 0.0, 0.0)

    glScaled(@sca_x, @sca_y, 1.0)

#    glTranslated((@x+@cx-sw,(@y+@cy)-sh,0)
    glTranslated(@cx-x2,@cy-y2,0)

    render_inner(GL_QUADS){
      glTexCoord2i(@ox, @obottom)
#      glVertex3i(sw-@x,sh-(@y+@oh-1),@z)
      glVertex3i(x2,b2,@z)

      glTexCoord2i(@oright, @obottom)
#      glVertex3i(sw-(@x+@ow-1),sh-(@y+@oh-1),@z)
      glVertex3i(r2,b2,@z)

      glTexCoord2i(@oright, @oy)
#      glVertex3i(sw-(@x+@ow-1),sh-@y,@z)
      glVertex3i(r2,y2,@z)

      glTexCoord2i(@ox, @oy)
#      glVertex3i(sw-@x,sh-@y,@z)
      glVertex3i(x2,y2,@z)
    }

    glDisable(GL_BLEND)
    glDisable(GL_TEXTURE_RECTANGLE_EXT)
    glDisable(GL_TEXTURE_2D)
    glPopMatrix
  end

  def dispose
    glDeleteTextures(@texture)
    @texture = nil
  end
end

w = SCREEN_WIDTH.to_f
h = SCREEN_HEIGHT.to_f

GLBase.init(w, h)
exit if GLBase.enable?

sprites = [1,2,3].map{|n| GLSprite.create("a#{n}.png")}

sprites[0].move_to(16,16)
sprites[1].move_to(128,16)
sprites[2].move_to(256,128)

#sprites[0].center(64,64)
angle = 0
angle_amt = -10

scale = 1.0
scale_amt = 0.1

r = 80
rangle = 0
rangle_amt = 4

GLBase.main_loop do
  sprites[0].rotate(angle)

  sprites[1].scale(scale,scale)

  radian = rangle * Math::PI * 2 / 360.0
  sprites[2].move_to(256+r*Math.sin(radian), 128+r*Math.cos(radian))

  sprites[0].render
  sprites[1].render
  sprites[2].render
  angle = (angle + angle_amt) % 360
  rangle = (rangle + rangle_amt) % 360
  scale = scale + scale_amt
  scale_amt = -scale_amt if scale <= 0.0 or scale >= 2.0
end

sprites.each{|sprite| sprite.dispose }

a1.png:

a2.png:

a3.png: