今度はアニメーションをしてみます。アニメーションをするには、addTimerCallbackでタイマーを設定し、一定時間ごとに状態を変更してやり、その後にpostRedisplayでウィンドウに再描画の要求を出します。描画時には、状態を見て描く内容を決めます。
ちらつきなく書き換えるためにダブルバッファリングを有効にしますが、それにはinitialDisplayModeにDoubleBufferedを追加し、描画時にはflushの代わりにswapBuffersを使ってバッファを切り替えるようにします。
以下の例では100msごとに30度ずつ正四面体を回転させています。回転する角度をIORefとして確保し、タイマーのコールバック関数で更新し、描画関数で参照しています。
import Control.Monad
import Data.IORef
import Graphics.Rendering.OpenGL
import Graphics.UI.GLUT
main =
do (progName, _) <- getArgsAndInitialize
initialDisplayMode $= [RGBAMode, WithDepthBuffer, DoubleBuffered]
window <- createWindow "Window"
clearColor $= Color4 1.0 1.0 1.0 0.0
depthFunc $= Just Less
cullFace $= Just Back
rot <- newIORef 0
displayCallback $= display rot
reshapeCallback $= Just reshape
addTimerCallback 100 $ timer window (modifyIORef rot (\x -> (x + 30) `mod` 360))
mainLoop
display rot =
do clear [ColorBuffer, DepthBuffer]
color $ (Color3 0.0 0.0 0.0 :: Color3 GLfloat)
let points :: [(GLfloat, GLfloat, GLfloat)]
points = [(0.0, 0.0, 0.0),
(1.0, 0.0, 0.0),
(1.0, 1.0, 0.0),
(0.0, 1.0, 0.0),
(0.0, 0.0, 1.0),
(1.0, 0.0, 1.0),
(1.0, 1.0, 1.0),
(0.0, 1.0, 1.0)]
faces :: [((Int, Int, Int, Int), Color3 GLfloat)]
faces = [((3, 2, 1, 0), Color3 1.0 0.0 0.0),
((2, 6, 5, 1), Color3 0.0 1.0 0.0),
((6, 7, 4, 5), Color3 0.0 0.0 1.0),
((7, 3, 0, 4), Color3 1.0 1.0 0.0),
((0, 1, 5, 4), Color3 1.0 0.0 1.0),
((7, 6, 2, 3), Color3 0.0 1.0 1.0)]
preservingMatrix $
do r <- readIORef rot
rotate (fromIntegral r) (Vector3 1.0 1.0 0.0 :: Vector3 GLfloat)
renderPrimitive Quads $
forM_ faces $ \((p, q, r, s), c) ->
do let v n = let (x, y, z) = points !! n
in Vertex3 x y z
color c
mapM_ (vertex . v) [p, q, r, s]
swapBuffers
reshape size@(Size w h) =
do viewport $= (Position 0 0, size)
matrixMode $= Projection
loadIdentity
perspective 30.0 (fromIntegral w / fromIntegral h) 1.0 100.0
lookAt (Vertex3 3.0 4.0 5.0) (Vertex3 0.0 0.0 0.0) (Vector3 0.0 1.0 0.0)
timer window action =
do action
postRedisplay $ Just window
addTimerCallback 100 (timer window action)