Skip to content

Commit 769ef17

Browse files
Kinecting
1 parent 63f17a1 commit 769ef17

File tree

3 files changed

+189
-0
lines changed

3 files changed

+189
-0
lines changed

Setup.hs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
import Distribution.Simple
2+
main = defaultMain

haskell-game-programming.cabal

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
-- Initial haskell-game-programming.cabal generated by cabal init. For
2+
-- further documentation, see http://haskell.org/cabal/users-guide/
3+
4+
name: haskell-game-programming
5+
version: 0.1.0
6+
synopsis: Haskell game programming examples
7+
-- description:
8+
homepage: http://facebook.com/keerastudios
9+
license: AllRightsReserved
10+
license-file: LICENSE
11+
author: Ivan Perez
12+
maintainer: ivan.perez@keera.co.uk
13+
-- copyright:
14+
category: Game
15+
build-type: Simple
16+
extra-source-files: README.md
17+
cabal-version: >=1.10
18+
19+
executable raindrops
20+
main-is: Raindrops.hs
21+
-- other-modules:
22+
hs-source-dirs: examples/raindrops/
23+
-- other-extensions: Arrows, FlexibleInstances, MultiParamTypeClasses,
24+
-- TypeSynonymInstances, FlexibleContexts,
25+
-- FunctionalDependencies, MultiWayIf
26+
build-depends: base,
27+
containers,
28+
SDL,
29+
random,
30+
hcwiid,
31+
SDL-mixer,
32+
SDL-image,
33+
SDL-ttf
34+
-- hs-source-dirs:
35+
default-language: Haskell2010
36+
37+
executable animation
38+
main-is: Main.hs
39+
hs-source-dirs: tutorials/gameconcepts/multimedia/animation/
40+
build-depends: base,
41+
containers,
42+
SDL,
43+
SDL-image
44+
-- hs-source-dirs:
45+
default-language: Haskell2010

tutorials/hardware/kinect/Main.hs

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
import Freenect
2+
import Data.Vector.Storable as V hiding ((++), length, foldr)
3+
import qualified Data.Vector.Storable as V
4+
import Control.Applicative
5+
import Control.Concurrent
6+
import Control.Monad
7+
import Graphics.UI.SDL as SDL
8+
import Graphics.UI.SDL.Primitives as SDL
9+
import Data.IORef
10+
import Data.Word
11+
import Data.Maybe
12+
13+
-- TODO Use these instead of hard-coded values
14+
kinectWidth, kinectHeight :: Int
15+
kinectWidth = 640
16+
kinectHeight = 480
17+
18+
type KinectPosRef = IORef KinectPos
19+
type KinectPos = Maybe (Double, Double)
20+
21+
initializeKinect :: (Double, Double) -> IO KinectPosRef
22+
initializeKinect screenSize = do
23+
lastPos <- newIORef Nothing
24+
_ <- getDepthThread screenSize lastPos
25+
return lastPos
26+
27+
getDepthThread :: (Double, Double) -> KinectPosRef -> IO ThreadId
28+
getDepthThread screenSize lastPos = forkIO $ do
29+
withContext $ \context -> do
30+
setLogLevel LogFatal context
31+
selectSubdevices context devices
32+
withDevice context index $ \device -> do
33+
setDepthMode device Medium ElevenBit
34+
setDepthCallback device $ \payload _timestamp -> do
35+
maybe (print ".") -- Too far or too close
36+
(updatePos lastPos)
37+
(calculateMousePos screenSize payload)
38+
return ()
39+
startDepth device
40+
forever $ processEvents context
41+
42+
where devices = [Camera]
43+
index = 0 :: Integer
44+
45+
updatePos :: IORef (Maybe (Double, Double)) -> (Double, Double) -> IO ()
46+
updatePos lastPosRef newPos@(nx,ny) = do
47+
lastPosM <- readIORef lastPosRef
48+
let (mx, my) = case lastPosM of
49+
Nothing -> newPos
50+
(Just (lx,ly)) -> (adjust 50 lx nx, adjust 50 ly ny)
51+
writeIORef lastPosRef (Just (mx, my))
52+
mx `seq` my `seq` return ()
53+
54+
calculateMousePos :: (Double, Double) -> Vector Word16 -> Maybe (Double, Double)
55+
calculateMousePos (width, height) payload =
56+
fmap g (findFirst payload)
57+
where g (px,py) = (mousex, mousey)
58+
where
59+
pointerx = fromIntegral (640 - px)
60+
pointery = fromIntegral py
61+
mousex = pointerx * adjx
62+
mousey = pointery * adjy
63+
adjx = width / 630.0
64+
adjy = height / 470.0
65+
66+
mat :: Vector Float
67+
mat = V.generate 2048 (\i -> let v :: Float
68+
v = ((fromIntegral i/2048.0)^3)*6.0 in v * 6.0 * 256.0)
69+
70+
findFirst :: Vector Word16 -> Maybe (Int, Int)
71+
findFirst vs = fmap (\v -> (v `mod` 640, v `div` 640)) i
72+
where i = V.findIndex (\x -> mat!(fromIntegral x) < 512) vs
73+
74+
processPayload :: Vector Word16 -> [(Float, Int, Int)]
75+
processPayload ps = [(pval, tx, ty) | i <- [0..640*480-1]
76+
, let pval = mat!(fromIntegral (ps!i))
77+
, pval < 300
78+
, let ty = i `div` 640
79+
tx = i `mod` 640
80+
]
81+
82+
-- Drop the fst elem, calculate the avg of snd and trd over the whole list
83+
avg :: [(Float, Int, Int)] -> (Int, Int)
84+
avg ls = (sumx `div` l, sumy `div` l)
85+
where l = length ls
86+
(sumx, sumy) = foldr (\(_,x,y) (rx,ry) -> (x+rx,y+ry)) (0,0) ls
87+
88+
-- Update a value, with a max cap
89+
adjust :: (Num a, Ord a) => a -> a -> a -> a
90+
adjust maxD old new
91+
| abs (old - new) < maxD = new
92+
| old < new = old + maxD
93+
| otherwise = old - maxD
94+
95+
main :: IO ()
96+
main = do
97+
SDL.init [InitVideo]
98+
99+
screen <- SDL.setVideoMode width height 32 [SWSurface]
100+
101+
putStrLn "Initializing Kinect."
102+
103+
kinectPos <- initializeKinect (width, height)
104+
105+
-- NEW
106+
t <- SDL.getTicks
107+
fpsCounter <- newIORef (0, t)
108+
109+
-- "game" loop
110+
forever $ do
111+
112+
-- Render
113+
let format = surfaceGetPixelFormat screen
114+
white <- mapRGB format 255 255 255
115+
fillRect screen Nothing white
116+
117+
t' <- SDL.getTicks
118+
(n,t) <- readIORef fpsCounter
119+
let td = t' - t
120+
let tpf = fromIntegral td / fromIntegral n / 1000
121+
122+
if td > 1000
123+
then do putStrLn $ "Time per frame (in seconds): " ++ show tpf
124+
putStrLn $ "FPS: " ++ show (1.0 / tpf)
125+
writeIORef fpsCounter (0, t')
126+
else writeIORef fpsCounter (n + 1, t)
127+
128+
(x,y) <- fromMaybe (width / 2, height / 2) <$> readIORef kinectPos
129+
130+
let color = Pixel 0xFF0000FF
131+
x' = round x
132+
y' = round y
133+
filledCircle screen x' y' 30 color
134+
135+
SDL.flip screen
136+
137+
width :: Num a => a
138+
width = 1024
139+
140+
height :: Num a => a
141+
height = 768
142+

0 commit comments

Comments
 (0)