require 'opengl' require 'glut' require 'mathn' class MyVector < Array def add(v2) MyVector.new zip(v2).map { |s, o| s+o } end def add!(v2) replace add(v2) end def -@() MyVector.new map { |e| -e } end def ^(v2) MyVector.new [[1, 2], [2, 0], [0, 1]].map { |e1, e2| self[e1]*v2[e2] - self[e2]*v2[e1] } end def *(i) MyVector.new map { |e| e*i } end end class Viewer def initialize @pos = MyVector.new [-2.0, 0.0, 0.0] @cur = MyVector.new [1.0, 0.0, 0.0] @up = MyVector.new [0.0, 0.0, 1.0] # @pos = [0.0, 0.0, -2.0] # @cur = [0.0, 0.0, 1.0] # @up = [0.0, 1.0, 0.0] @mvunit = 0.1 @rtunit = 5.0 * Math::PI / 360.0 @cosrt = Math.cos(@rtunit) @sinrt = Math.sin(@rtunit) end def render_cube if false GL.Begin(GL::LINES) GL.Color(1, 0, 0) GL.Vertex(0, 0, 0) GL.Vertex(*@cur) GL.Color(0, 1, 0) GL.Vertex(0, 0, 0) GL.Vertex(*@up) GL.Color(0, 0, 1) y = [[1,2],[2,0],[0,1]].map{ |e1, e2| @cur[e1]*@up[e2] - @cur[e2]*@up[e1] } GL.Vertex(0, 0, 0) GL.Vertex(*y) GL.End return end GL.Material GL::FRONT, GL::AMBIENT, [0, 0, 0, 1] GL.Material GL::FRONT, GL::DIFFUSE, [0.1, 0.35, 0.1, 1] GL.Material GL::FRONT, GL::SPECULAR, [0.45, 0.55, 0.45, 1] GL.Material GL::FRONT, GL::SHININESS, 0.25*128.0 GL.PushMatrix GLUT.SolidCube 1 GL.Translate 0, 0, 2 GLUT.SolidCube 1 GL.PopMatrix end # ag: +/-1 def rotate_z(ag) y = @cur ^ @up @cur = MyVector.new @cur.zip(y).map{ |ex, ey| @cosrt*ex + ag*@sinrt*ey } end def rotate_x(ag) y = @cur ^ @up @up = MyVector.new @up.zip(y).map{ |eu, ey| @cosrt*eu - ag*@sinrt*ey } end def rotate_y(ag) ca = @cosrt sa = ag*@sinrt @cur, @up = @cur.zip(@up).map{ |ec, eu| [ca*ec + sa*eu, -sa*ec + ca*eu] }.transpose @cur = MyVector.new @cur @up = MyVector.new @up end def render_all GL.LoadIdentity GLU.LookAt(*(@pos + (@pos.add @cur) + @up)) #GL.Translate *@pos GL.Light GL::LIGHT0, GL::POSITION, [0, 2, 2, 0] render_cube end def keyboard(key) case key when ?\e: exit 0 when ?j: @pos.add!(-(@cur ^ @up) * @mvunit) when ?l: @pos.add!((@cur ^ @up) * @mvunit) when ?k: @pos.add!(-@up * @mvunit) when ?i: @pos.add!(@up * @mvunit) when ?y: @pos.add!(@cur * @mvunit) when ?h: @pos.add!(-@cur * @mvunit) when ?s: rotate_z -1 when ?f: rotate_z 1 when ?e: rotate_y 1 when ?d: rotate_y -1 when ?t: rotate_x 1 when ?g: rotate_x -1 when ?n: cl = Math.sqrt(@cur.inject(0) { |n, e| e**2 + n }) @cur.map! { |e| e/cl } ul = Math.sqrt(@up.inject(0) { |n, e| e**2 + n }) @up.map! { |e| e/ul } when ?p: puts "cur: #{@cur.inspect}", "up: #{@up.inspect}" when ?r: @cur = [0.0, 0.0, 1.0] @up = [0.0, 1.0, 0.0] end end def run # @font = FTGL::PixmapFont.new('/usr/share/fonts/truetype/arial.ttf') # @font.SetFaceSize 24 GLUT.InitDisplayMode(GLUT::DOUBLE | GLUT::RGBA | GLUT::DEPTH) GLUT.Init GLUT.CreateWindow("3dView") GL.ClearColor(0.1, 0, 0.1, 0) GL.Light GL::LIGHT0, GL::AMBIENT, [0, 0, 0, 1] GL.Light GL::LIGHT0, GL::DIFFUSE, [1, 1, 1, 1] GL.LightModel GL::LIGHT_MODEL_AMBIENT, [0.2, 0.2, 0.2, 1] GL.LightModel GL::LIGHT_MODEL_LOCAL_VIEWER, [0] GL.FrontFace GL::CW GL.Enable GL::LIGHTING GL.Enable GL::LIGHT0 GL.Enable GL::AUTO_NORMAL GL.Enable GL::NORMALIZE GL.Enable GL::DEPTH_TEST GLUT.ReshapeFunc proc { |w, h| GL.Viewport(0, 0, w, h) GL.MatrixMode(GL::PROJECTION) GL.LoadIdentity GLU.Perspective(45.0, (1.0 * w) / h, 0.1, 100.0) GL.MatrixMode(GL::MODELVIEW) } GLUT.DisplayFunc proc { # GL.LoadIdentity GL.Clear(GL::COLOR_BUFFER_BIT | GL::DEPTH_BUFFER_BIT) render_all GLUT.SwapBuffers } GLUT.KeyboardFunc proc { |key, x, y| keyboard key GLUT.PostRedisplay } #GLUT.IdleFunc proc { GLUT.PostRedisplay } GLUT.MainLoop end end Viewer.new.run