aboutsummaryrefslogtreecommitdiff
path: root/Demos/Balls/Main.hs
diff options
context:
space:
mode:
Diffstat (limited to 'Demos/Balls/Main.hs')
-rw-r--r--Demos/Balls/Main.hs176
1 files changed, 176 insertions, 0 deletions
diff --git a/Demos/Balls/Main.hs b/Demos/Balls/Main.hs
new file mode 100644
index 0000000..d266d85
--- /dev/null
+++ b/Demos/Balls/Main.hs
@@ -0,0 +1,176 @@
1{-# LANGUAGE MultiParamTypeClasses #-}
2--{-# LANGUAGE NoImplicitPrelude #-}
3{-# LANGUAGE FlexibleContexts #-}
4{-# LANGUAGE TypeSynonymInstances #-}
5
6module Main where
7
8import Spear.App
9import Spear.Game
10import Spear.Math.AABB
11import qualified Spear.Math.Matrix3 as Matrix3
12import qualified Spear.Math.Matrix4 as Matrix4
13import Spear.Math.Spatial
14import Spear.Math.Spatial2
15import Spear.Math.Vector
16import Spear.Physics.Collision
17--import Spear.Prelude
18import Spear.Render.Core.Pipeline
19import Spear.Render.Core.State
20import Spear.Render.Immediate
21import Spear.Sound.Sound
22import Spear.Sound.State
23import Spear.Window
24
25import Control.Monad (when)
26
27
28ballSize = 0.01
29numBalls = 1000
30
31data Ball = Ball
32 { ballPosition :: {-# UNPACK #-} !Vector2
33 , ballVelocity :: {-# UNPACK #-} !Vector2
34 }
35
36instance Positional Ball Vector2 where
37 setPosition p ball = ball { ballPosition = p }
38 position = ballPosition
39 translate v ball = ball { ballPosition = v + ballPosition ball }
40
41instance Bounded2 Ball where
42 boundingVolume ball = aabb2Volume $ translate (ballPosition ball) (AABB2 (-size) size)
43 where size = vec2 s s
44 s = ballSize / (2::Float)
45
46data World = World
47 { viewProjection :: Matrix4.Matrix4
48 , balls :: [Ball]
49 }
50
51type GameState = AppState World
52
53
54options = defaultAppOptions { title = "Balls" }
55
56app = App options initGame endGame step render resize
57
58
59main :: IO ()
60main = runApp app
61
62initGame :: Game AppContext World
63initGame =
64 let
65 world = zipWith Ball positions velocities
66 positions = (+vec2 0.5 0.5) . makePosition <$> numbers
67 makePosition i = radius * vec2 (sin (f*i)) (cos (f*i))
68 velocities = makeVelocity <$> numbers
69 makeVelocity i = scale speed $ vec2 (sin (f*i)) (cos (f*i))
70 numbers = [1..numBalls]
71 f = 2*pi / numBalls
72 radius = 0.05
73 speed = 0.4
74 in
75 return $ World Matrix4.id world
76
77endGame :: Game GameState ()
78endGame = return ()
79
80
81step :: Elapsed -> Dt -> [InputEvent] -> Game GameState Bool
82step elapsed dt inputEvents = do
83 modifyGameState $ \world -> world
84 { balls = moveBalls dt $ balls world
85 }
86 return (not $ exitRequested inputEvents)
87
88exitRequested = elem (KeyDown KEY_ESC)
89
90moveBalls :: Elapsed -> [Ball] -> [Ball]
91moveBalls dt = (bounceBall dt . moveBall dt <$>)
92
93moveBall :: Elapsed -> Ball -> Ball
94moveBall dt ball = translate (scale (realToFrac dt) $ ballVelocity ball) ball
95
96bounceBall :: Elapsed -> Ball -> Ball
97bounceBall dt ball =
98 let
99 (AABB2Volume (AABB2 pmin pmax)) = boundingVolume ball
100 sideCollision = x pmin < 0 || x pmax > 1
101 backCollision = y pmin < 0 || y pmax > 1
102 flipX v@(Vector2 x y) = if sideCollision then vec2 (-x) y else v
103 flipY v@(Vector2 x y) = if backCollision then vec2 x (-y) else v
104 velocity = ballVelocity ball
105 velocity'
106 = flipX
107 . flipY
108 $ velocity
109 collision = velocity' /= velocity
110 -- Apply offset when collision occurs to avoid sticky collisions.
111 delta = if collision then 1 else 0
112 dt' = realToFrac dt
113 in
114 ball
115 { ballPosition = ballPosition ball + scale (delta * dt') velocity'
116 , ballVelocity = velocity'
117 }
118
119
120render :: Game GameState ()
121render = do
122 gameState <- getGameState
123 siblingGame $ do
124 immStart
125 immSetViewProjectionMatrix (viewProjection gameState)
126 -- Clear the background to a different colour than the playable area to make
127 -- the latter distinguishable.
128 setClearColour (0.2, 0.2, 0.2, 0.0)
129 clearBuffers [ColourBuffer]
130 render' $ balls gameState
131 immEnd
132
133render' :: [Ball] -> Game ImmRenderState ()
134render' balls = do
135 immLoadIdentity
136 renderBackground
137 -- Draw objects.
138 immSetColour (vec4 1.0 1.0 1.0 1.0)
139 mapM_ renderBall balls
140
141renderBackground :: Game ImmRenderState ()
142renderBackground =
143 let pmin = 0 :: Float
144 pmax = 1 :: Float
145 in do
146 immSetColour (vec4 0.0 0.25 0.41 1.0)
147 immDrawQuads2d [
148 (vec2 pmin pmin
149 ,vec2 pmax pmin
150 ,vec2 pmax pmax
151 ,vec2 pmin pmax)]
152
153renderBall :: Ball -> Game ImmRenderState ()
154renderBall ball =
155 let (AABB2Volume (AABB2 (Vector2 xmin ymin) (Vector2 xmax ymax))) = boundingVolume ball
156 in
157 immDrawQuads2d [
158 (vec2 xmin ymin
159 ,vec2 xmax ymin
160 ,vec2 xmax ymax
161 ,vec2 xmin ymax)]
162
163
164resize :: WindowEvent -> Game GameState ()
165resize (ResizeEvent w h) =
166 let r = fromIntegral w / fromIntegral h
167 pad = if r > 1 then (r-1) / 2 else (1/r - 1) / 2
168 left = if r > 1 then -pad else 0
169 right = if r > 1 then 1 + pad else 1
170 bottom = if r > 1 then 0 else -pad
171 top = if r > 1 then 1 else 1 + pad
172 in do
173 setViewport 0 0 w h
174 modifyGameState $ \pong -> pong {
175 viewProjection = Matrix4.ortho left right bottom top (-1) 1
176 }