1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368
|
{-
Brick.hs (adapted from ogl2brick.c which is (c) 3Dlabs Inc. Ltd.)
Copyright (c) Sven Panne 2006 <sven.panne@aedion.de>
This file is part of HOpenGL and distributed under a BSD-style license
See the file libraries/GLUT/LICENSE
-}
import Prelude hiding ( sum )
import Control.Applicative
import Control.Monad
import Data.Foldable ( Foldable, sum )
import Data.IORef
import System.Exit
import Graphics.UI.GLUT
infixl 6 $+, $-
infixl 7 $*
inertiaThreshold, inertiaFactor :: GLfloat
inertiaThreshold = 1
inertiaFactor = 0.5
scaleFactor, scaleIncrement :: GLfloat
scaleFactor = 0.01
scaleIncrement = 0.5
timerFrequencyMillis :: Timeout
timerFrequencyMillis = 20
clearColors :: [Color4 GLclampf]
clearColors = [
Color4 0.0 0.0 0.0 1,
Color4 0.2 0.2 0.3 1,
Color4 0.7 0.7 0.7 1 ]
models :: [IO ()]
models = [
drawCube,
renderObject Solid (Teapot 0.6),
renderObject Solid (Sphere' 0.6 64 64),
renderObject Solid (Torus 0.2 0.6 64 64) ]
initialDiff :: Vector3 GLfloat
initialDiff = Vector3 206 16 10
initialInertia :: Vector3 GLfloat
initialInertia = Vector3 (-0.5) 0 0
data State = State {
diff :: IORef (Vector3 GLfloat),
lastIncr :: IORef (Vector3 GLfloat),
inertia :: IORef (Vector3 GLfloat),
inertiaOld :: IORef (Vector3 GLfloat),
theScale :: IORef GLfloat,
lastPosition :: IORef Position,
shouldRotate :: IORef Bool,
colorCycle :: IORef [Color4 GLclampf],
modelCycle :: IORef [IO ()],
modifiers :: IORef Modifiers
}
makeState :: IO State
makeState = do
di <- newIORef initialDiff
li <- newIORef (pure 0)
ia <- newIORef initialInertia
io <- newIORef (pure 0)
sc <- newIORef 1
lp <- newIORef (Position (-1) (-1))
sr <- newIORef True
cc <- newIORef (cycle clearColors)
mc <- newIORef (cycle models)
mo <- newIORef (Modifiers Up Up Up)
return $ State {
diff = di,
lastIncr = li,
inertia = ia,
inertiaOld = io,
theScale = sc,
lastPosition = lp,
shouldRotate = sr,
colorCycle = cc,
modelCycle = mc,
modifiers = mo
}
-- Our tiny vector math library...
($+), ($-), ($*) :: (Applicative t, Num a) => t a -> t a -> t a
($+) = liftA2 (+)
($-) = liftA2 (-)
($*) = liftA2 (*)
step :: (Applicative t, Num a, Ord a) => t a -> t a -> t a
step = liftA2 (\e x -> if x < e then 0 else 1)
dot :: (Applicative t, Foldable t, Num a) => t a -> t a -> a
dot v1 v2 = sum (v1 $* v2)
drawFace :: Normal3 GLfloat -> Vertex3 GLfloat -> Vertex3 GLfloat
-> Vertex3 GLfloat -> Vertex3 GLfloat -> IO ()
drawFace p q r s t = do
let texCoord2f = texCoord :: TexCoord2 GLfloat -> IO ()
normal p
texCoord2f (TexCoord2 1 1)
vertex q
texCoord2f (TexCoord2 0 1)
vertex r
texCoord2f (TexCoord2 0 0)
vertex s
texCoord2f (TexCoord2 1 0)
vertex t
drawCube :: IO ()
drawCube = do
let size = 1
sc = 0.2
delta = 0.1
a = Vertex3 size size ( size * sc + delta)
b = Vertex3 size size (-size * sc + delta)
c = Vertex3 size (-size) (-size * sc)
d = Vertex3 size (-size) ( size * sc)
e = Vertex3 (-size) size ( size * sc + delta)
f = Vertex3 (-size) size (-size * sc + delta)
g = Vertex3 (-size) (-size) (-size * sc)
h = Vertex3 (-size) (-size) ( size * sc)
i = Normal3 1 0 0
k = Normal3 (-1) 0 0
l = Normal3 0 0 (-1)
m = Normal3 0 0 1
n = Normal3 0 1 0
o = Normal3 0 (-1) 0
renderPrimitive Quads $ do
drawFace i d c b a
drawFace k g h e f
drawFace l c g f b
drawFace m h d a e
drawFace n e a b f
drawFace o g c d h
display :: State -> DisplayCallback
display state = do
loadIdentity
translate (Vector3 0 0 (-5 :: GLfloat))
Vector3 xDiff yDiff zDiff <- get (diff state)
rotate yDiff (Vector3 1 0 0)
rotate xDiff (Vector3 0 1 0)
rotate zDiff (Vector3 0 0 1)
sc <- get (theScale state)
scale sc sc sc
clear [ ColorBuffer, DepthBuffer ]
(drawModel:_) <- get (modelCycle state)
drawModel
flush
swapBuffers
nextClearColor :: State -> IO ()
nextClearColor state = do
cc <- get (colorCycle state)
clearColor $= head cc
colorCycle state $~ tail
toggleRotation :: State -> IO ()
toggleRotation state = do
rot <- get (shouldRotate state)
shouldRotate state $~ not
if rot
then do
ia <- get (inertia state)
inertiaOld state $= ia
else do
io <- get (inertiaOld state)
inertia state $= io
-- To prevent confusion, force some rotation
when (dot io io == 0) $
inertia state $= initialInertia
printHelp :: IO ()
printHelp = mapM_ putStrLn [
"",
"Keyboard commands:",
"",
"b - Toggle among background clear colors",
"q, <esc> - Quit",
"t - Toggle among models to render",
"? - Help",
"<home> - reset zoom and rotation",
"<space> or <click> - stop rotation",
"<+>, <-> or <ctrl + drag> - zoom model",
"<arrow keys> or <drag> - rotate model",
""]
resetState :: State -> IO ()
resetState state = do
diff state $= initialDiff
lastIncr state $= pure 0
inertia state $= initialInertia
theScale state $= 1
calcInertia :: State -> IO ()
calcInertia state = do
lastPosition state $= Position (-1) (-1)
li <- get (lastIncr state)
ia <- get (inertia state)
let t = pure inertiaThreshold
f = pure inertiaFactor
l = (pure 1 $- (step (fmap negate t) li)) $* ((li $+ t) $* f $- ia)
r = (step t li) $* ((li $- t) $* f $- ia)
inertia state $= l $+ ia $+ r
lastIncr state $= pure 0
keyboard :: State -> KeyboardMouseCallback
keyboard state key keyState mods _ = do
modifiers state $= mods
postRedisplay Nothing
case (key, keyState) of
(Char 'b', Down) -> nextClearColor state
(Char 'q', Down) -> exitWith ExitSuccess
(Char '\27', Down) -> exitWith ExitSuccess
(Char 't', Down) -> modelCycle state $~ tail
(Char ' ', Down) -> toggleRotation state
(Char '+', Down) -> theScale state $~ (+ scaleIncrement)
(Char '-', Down) -> theScale state $~ (+ (- scaleIncrement))
(Char _, Down) -> printHelp
(SpecialKey KeyHome, Down) -> resetState state
(SpecialKey KeyLeft, Down) -> diff state $~ ($- Vector3 1 0 0)
(SpecialKey KeyRight, Down) -> diff state $~ ($+ Vector3 1 0 0)
(SpecialKey KeyUp, Down) -> diff state $~ ($- Vector3 0 1 0)
(SpecialKey KeyDown, Down) -> diff state $~ ($+ Vector3 0 1 0)
(MouseButton LeftButton, Down) -> do
inertia state $= pure 0
lastIncr state $= pure 0
(MouseButton LeftButton, Up) -> calcInertia state
(_, _) -> return ()
motion :: State -> MotionCallback
motion state pos@(Position x y) = do
postRedisplay Nothing
Position xt yt <- get (lastPosition state)
lastPosition state $= pos
when (xt /= -1 || yt /= -1) $ do
let li@(Vector3 xl yl _) = Vector3 (fromIntegral (x - xt)) (fromIntegral (y - yt)) 0
lastIncr state $= li
when (xt /= -1) $ do
mods <- get (modifiers state)
if ctrl mods == Down
then do diff state $~ ($+ Vector3 0 0 xl)
theScale state $~ (+ (yl * scaleFactor))
else diff state $~ ($+ li)
timer :: State -> TimerCallback
timer state = do
rot <- get (shouldRotate state)
when rot $ do
ia <- get (inertia state)
diff state $~ ($+ ia)
postRedisplay Nothing
addTimerCallback timerFrequencyMillis (timer state)
reshape :: ReshapeCallback
reshape size@(Size w h) = do
let vp = 0.8
aspect = fromIntegral w / fromIntegral h
viewport $= (Position 0 0, size)
matrixMode $= Projection
loadIdentity
frustum (-vp) vp (-vp / aspect) (vp / aspect) 3 10
matrixMode $= Modelview 0
loadIdentity
translate (Vector3 0 0 (-5 :: GLfloat))
-- Make sure that GLSL is supported by the driver, either directly by the core
-- or via an extension.
checkGLSLSupport :: IO ()
checkGLSLSupport = do
version <- get (majorMinor glVersion)
unless (version >= (2,0)) $ do
extensions <- get glExtensions
unless ("GL_ARB_shading_language_100" `elem` extensions) $
ioError (userError "No GLSL support found.")
readAndCompileShader :: Shader s => FilePath -> IO s
readAndCompileShader filePath = do
src <- readFile filePath
[shader] <- genObjectNames 1
shaderSource shader $= [src]
compileShader shader
reportErrors
ok <- get (compileStatus shader)
infoLog <- get (shaderInfoLog shader)
mapM_ putStrLn ["Shader info log for '" ++ filePath ++ "':", infoLog, ""]
unless ok $ do
deleteObjectNames [shader]
ioError (userError "shader compilation failed")
return shader
installBrickShaders :: [VertexShader] -> [FragmentShader] -> IO ()
installBrickShaders vs fs = do
[brickProg] <- genObjectNames 1
attachedShaders brickProg $= (vs, fs)
linkProgram brickProg
reportErrors
ok <- get (linkStatus brickProg)
infoLog <- get (programInfoLog brickProg)
mapM_ putStrLn ["Program info log:", infoLog, ""]
unless ok $ do
deleteObjectNames [brickProg]
ioError (userError "linking failed")
currentProgram $= Just brickProg
let setUniform var val = do
location <- get (uniformLocation brickProg var)
reportErrors
uniform location $= val
setUniform "BrickColor" (Color3 1.0 0.3 (0.2 :: GLfloat))
setUniform "MortarColor" (Color3 0.85 0.86 (0.84 :: GLfloat))
setUniform "BrickSize" (Vertex2 0.30 (0.15 :: GLfloat))
setUniform "BrickPct" (Vertex2 0.90 (0.85 :: GLfloat))
setUniform "LightPosition" (Vertex3 0 0 (4 :: GLfloat))
main :: IO ()
main = do
getArgsAndInitialize
initialDisplayMode $= [ RGBMode, WithDepthBuffer, DoubleBuffered ]
initialWindowSize $= Size 500 500
createWindow "3Dlabs Brick Shader"
-- Note: We don't use an idle callback, we redisplay more intelligently.
state <- makeState
displayCallback $= display state
keyboardMouseCallback $= Just (keyboard state)
reshapeCallback $= Just reshape
motionCallback $= Just (motion state)
addTimerCallback timerFrequencyMillis (timer state)
catch
(do checkGLSLSupport
vs <- readAndCompileShader "Brick.vert"
fs <- readAndCompileShader "Brick.frag"
installBrickShaders [vs] [fs])
(\exception -> do
print exception
putStrLn "Using fixed function pipeline."
materialDiffuse Front $= Color4 1 0.3 0.2 1
materialSpecular Front $= Color4 0.3 0.3 0.3 1
materialShininess Front $= 16
position (Light 0) $= Vertex4 0 0 4 0
lighting $= Enabled
light (Light 0) $= Enabled)
depthFunc $= Just Less
nextClearColor state
-- display help
keyboard state (Char '?') Down (Modifiers Up Up Up) (Position 0 0)
mainLoop
|