Files
loop/src/Shader/Poke.hs
T
2023-04-25 10:23:43 +01:00

530 lines
14 KiB
Haskell

module Shader.Poke (
pokeVerxs,
pokeLayVerxs,
pokeArrayOff,
pokeShape,
pokeWallsWindows,
memoTopPrismEdgeIndices,
pokeFloors,
) where
import Shape.Parameters
import Color
import Control.Monad.Primitive
import qualified Data.Vector as V
import qualified Data.Vector.Fusion.Stream.Monadic as VFSM
import qualified Data.Vector.Mutable as MV
import qualified Data.Vector.Unboxed as UV
import qualified Data.Vector.Unboxed.Mutable as UMV
import Dodge.Data.Wall
import Foreign
import Geometry.Data
import Geometry.Triangulate
import Geometry.Vector
import Graphics.GL.Core45
import Linear.V3 (cross)
import Picture.Data
import Shader.Data
import Shader.Parameters
import Shape.Data
pokeVerxs ::
MV.MVector (PrimState IO) (Shader, VBO) ->
UMV.MVector (PrimState IO) Int ->
Picture ->
IO ()
pokeVerxs vbos count = VFSM.mapM_ (pokeVerx vbos count) . VFSM.fromList
pokeVerx :: MV.MVector (PrimState IO) (Shader, VBO) -> UMV.MVector (PrimState IO) Int -> Verx -> IO ()
{-# INLINE pokeVerx #-}
pokeVerx vbos offsets Verx{_vxPos = thePos, _vxCol = theCol, _vxExt = ext, _vxShadNum = shadnum} = do
typeOff <- UMV.unsafeRead offsets sn
basePtr <- _vboPtr . snd <$> MV.unsafeRead vbos sn
let thePtr = plusPtr basePtr (typeOff * pokeStride shadnum * floatSize)
poke34 thePtr thePos theCol
pokeArrayOff thePtr 7 ext
UMV.unsafeModify offsets (+ 1) sn
where
sn = _unShadNum shadnum
pokeWallsWindows ::
Ptr Float ->
Ptr Float ->
[((Point2, Point2), Point4)] ->
[Wall] ->
IO (Int, Int)
pokeWallsWindows wiptr truewlptr wis truewls = do
wlcounts2 <- VFSM.foldlM' (pokeW wiptr) 0 (VFSM.fromList wis)
wlcounts3 <- VFSM.foldlM' (pokeWall truewlptr) 0 (VFSM.fromList truewls)
return (wlcounts2, wlcounts3)
pokeFloors ::
Ptr Float ->
[(Point3, Point3)] ->
IO Int
pokeFloors flptr fls = VFSM.foldlM' (pokeF flptr) 0 (VFSM.fromList fls)
pokeF :: Ptr Float -> Int -> (Point3, Point3) -> IO Int
pokeF ptr i' (V3 a b c, V3 d e f) = do
let i = i' * 6
pokeElemOff ptr (i + 0) a
pokeElemOff ptr (i + 1) b
pokeElemOff ptr (i + 2) c
pokeElemOff ptr (i + 3) d
pokeElemOff ptr (i + 4) e
pokeElemOff ptr (i + 5) f
return $ i' + 1
pokeWall :: Ptr Float -> Int -> Wall -> IO Int
{-# INLINE pokeWall #-}
pokeWall ptr nw wl = do
UV.imapM_ f $ UV.fromList [x, y, x1, y1, t, a, 1, 1]
return $ nw + 1
where
f i = pokeElemOff ptr (nw * 8 + i)
(V2 x y, V2 x1 y1) = _wlLine wl
t = fromIntegral $ _opTexture $ _wlOpacity wl
a = argV (V2 x1 y1 -.- V2 x y)
pokeW :: Ptr Float -> Int -> ((Point2, Point2), Point4) -> IO Int
pokeW ptr i' ((V2 a b, V2 c d), V4 e f g h) = do
let i = i' * 8
pokeElemOff ptr (i + 0) a
pokeElemOff ptr (i + 1) b
pokeElemOff ptr (i + 2) c
pokeElemOff ptr (i + 3) d
pokeElemOff ptr (i + 4) e
pokeElemOff ptr (i + 5) f
pokeElemOff ptr (i + 6) g
pokeElemOff ptr (i + 7) h
return $ i' + 1
--pokeShape' ::
-- Ptr Float ->
-- Ptr GLushort ->
-- Ptr GLushort ->
-- (Int, Int, Int) ->
-- [Surface] ->
-- IO (Int, Int, Int)
--pokeShape' = pokeShape $ const False
pokeShape ::
(Surface -> Bool) ->
Ptr Float ->
Ptr GLushort ->
Ptr GLushort ->
(Int, Int, Int) ->
[Surface] ->
IO (Int, Int, Int)
{-# INLINE pokeShape #-}
pokeShape shadowtest ptr iptr ieptr is =
VFSM.foldlM' (pokeShapeObj shadowtest ptr iptr ieptr) is
. VFSM.fromList
pokeShapeObj ::
(Surface -> Bool) ->
Ptr Float ->
Ptr GLushort ->
Ptr GLushort ->
(Int, Int, Int) ->
Surface ->
IO (Int, Int, Int)
{-# INLINE pokeShapeObj #-}
pokeShapeObj shadowtest ptr iptr ieptr counts surf@(Surface shtype shVerts col _ _) = case shtype of
FlatFaces size -> pokeBox blockshadows col size ptr iptr ieptr counts shVerts
RoundedFaces size -> pokeRoundedFaces blockshadows col size ptr iptr ieptr counts shVerts
Cylinder size -> pokeCylinder blockshadows col size ptr iptr ieptr counts shVerts
where
blockshadows = shadowtest surf
pokeRoundedFaces ::
Bool ->
Point4 ->
Int ->
Ptr Float ->
Ptr GLushort ->
Ptr GLushort ->
(Int, Int, Int) ->
[Point3] ->
IO (Int, Int, Int)
{-# INLINE pokeRoundedFaces #-}
pokeRoundedFaces sfid col size ptr iptr ieptr (nv, nsi, nei) (tc : bc : svs) = do
nv' <- pokeRoundedCurve xdata col ptr tc bc svs nv
nsi' <-
UV.foldM'
(pokeIndex nv iptr)
nsi
(memoTopPrismIndices V.! (size - 3))
nei' <-
if sfid
then return nei
else
UV.foldM' (pokeIndex nv ieptr) nei $
memoTopPrismEdgeIndices V.! (size - 3)
return (nv', nsi', nei')
where
xdata
| sfid = 0 -- this records whether the shadow should be shown or not
-- honestly, I think things where faster without this
| otherwise = 1
pokeRoundedFaces _ _ _ _ _ _ _ _ = undefined
pokeCylinder ::
Bool ->
Point4 ->
Int ->
Ptr Float ->
Ptr GLushort ->
Ptr GLushort ->
(Int, Int, Int) ->
[Point3] ->
IO (Int, Int, Int)
{-# INLINE pokeCylinder #-}
pokeCylinder sfid col size ptr iptr ieptr (nv, nsi, nei) (tc : bc : svs) = do
nv' <- pokeRoundedCurve xdata col ptr tc bc svs nv >>= pokeCylinderCaps xdata col ptr tc bc svs
nsi' <-
UV.foldM'
(pokeIndex nv iptr)
nsi
(memoCylinderIndices V.! (size - 3))
nei' <-
if sfid
then return nei
else
UV.foldM' (pokeIndex nv ieptr) nei $
memoTopPrismEdgeIndices V.! (size - 3)
return (nv', nsi', nei')
where
xdata
| sfid = 0
| otherwise = 1
pokeCylinder _ _ _ _ _ _ _ _ = undefined
pokeRoundedCurve :: Float -> Point4 -> Ptr Float -> Point3 -> Point3 -> [Point3] -> Int -> IO Int
{-# INLINE pokeRoundedCurve #-}
pokeRoundedCurve xdata col ptr tc bc = go True
where
go True (x : xs) n = pokeJustV xdata tc col ptr n x >>= go False xs
go False (x : xs) n = pokeJustV xdata bc col ptr n x >>= go True xs
go _ [] n = return n
pokeCylinderCaps :: Float -> Point4 -> Ptr Float -> Point3 -> Point3 -> [Point3] -> Int -> IO Int
{-# INLINE pokeCylinderCaps #-}
pokeCylinderCaps xdata col ptr tc bc = go True
where
go True (x : xs) n = pokeJustVInvNormal xdata tc col ptr n x >>= go False xs
go False (x : xs) n = pokeJustVInvNormal xdata bc col ptr n x >>= go True xs
go _ [] n = return n
pokeBox ::
Bool ->
Point4 ->
Int ->
Ptr Float ->
Ptr GLushort ->
Ptr GLushort ->
(Int, Int, Int) ->
[Point3] ->
IO (Int, Int, Int)
{-# INLINE pokeBox #-}
pokeBox sfid col size ptr iptr ieptr (nv, nsi, nei) svs = do
nv' <- VFSM.foldM' (pokeBoxSurface xdata col ptr svsv) nv $ VFSM.fromList $ memoBoxSurfaces V.! size
-- nv' <- VFSM.foldM' (pokeBoxSurface xdata col ptr svsv) nv $ VFSM.fromList $ boxSurfaces size
nsi' <-
UV.foldM'
(pokeIndex nv iptr)
nsi
(memoFlatIndices V.! (size -3))
-- VFSM.foldM' (pokeIndex nv iptr) nsi
-- (VFSM.fromList . concatMap polyToTris $ boxSurfacesIndices size)
nei' <-
if sfid
then return nei
else
UV.foldM' (pokeIndex nv ieptr) nei $
memoBoxEdgeIndices V.! (size - 3)
return (nv', nsi', nei')
where
svsv = UV.fromList svs
xdata
| sfid = 0
| otherwise = 1
-- should probably use a vector of Point3
pokeBoxSurface :: Float -> Point4 -> Ptr Float -> UV.Vector Point3 -> Int -> [Int] -> IO Int
{-# INLINE pokeBoxSurface #-}
pokeBoxSurface xdata col ptr vs n is =
UV.foldM'
(pokeFlatV xdata norm col ptr)
n
v
where
v = UV.backpermute vs (UV.fromList is)
x = v UV.! 0
y = v UV.! 1
z = v UV.! 2
norm = negate $ cross (x - y) (z - y)
-- should probably memoize this
boxSurfaces :: Int -> [[Int]]
{-# INLINE boxSurfaces #-}
boxSurfaces size =
[0, 2 .. size * 2 -1] :
[1, 3 .. size * 2] :
map f [0, 2 .. size * 2 - 1]
where
f x = map (`mod` (size * 2)) [x, x + 1, x + 3, x + 2]
memoBoxSurfaces :: V.Vector [[Int]]
{-# INLINE memoBoxSurfaces #-}
memoBoxSurfaces = V.generate 10 boxSurfaces
boxSurfacesIndices :: Int -> [[Int]]
{-# INLINE boxSurfacesIndices #-}
boxSurfacesIndices n =
[0 .. n -1] :
reverse [n .. n * 2 -1] :
[map ((2 * n) +) [4 * i, 4 * i + 1, 4 * i + 2, 4 * i + 3] | i <- [0 .. n -1]]
pokeIndex ::
-- | base index
Int ->
Ptr GLushort ->
-- | number poked
Int ->
-- | index offset
Int ->
IO Int
{-# INLINE pokeIndex #-}
pokeIndex nv eiptr ni ioff = do
pokeElemOff eiptr ni (fromIntegral $ nv + ioff)
return $ ni + 1
memoFlatIndices :: V.Vector (UV.Vector Int)
{-# INLINE memoFlatIndices #-}
memoFlatIndices =
V.generate 10 $
UV.fromList . concatMap polyToTris . boxSurfacesIndices . (+ 3)
memoTopPrismIndices :: V.Vector (UV.Vector Int)
{-# INLINE memoTopPrismIndices #-}
memoTopPrismIndices =
V.generate 10 $
UV.fromList . topPrismIndices . (+ 3)
memoCylinderIndices :: V.Vector (UV.Vector Int)
{-# INLINE memoCylinderIndices #-}
memoCylinderIndices =
V.generate 10 $
UV.fromList . cylinderIndices . (+ 3)
memoTopPrismEdgeIndices :: V.Vector (UV.Vector Int)
{-# INLINE memoTopPrismEdgeIndices #-}
memoTopPrismEdgeIndices =
V.generate 10 $
UV.fromList . topPrismEdgeIndices . (+ 3)
topPrismEdgeIndices ::
-- | the number of vertices on the top surface, ie. half the total
Int ->
[Int]
{-# INLINE topPrismEdgeIndices #-}
topPrismEdgeIndices n = concatMap f [0 .. n -1]
where
f i =
map
g
[ 0 , 2 , 1 , 4
, 0 , 1 , -2 , 3
, 1 , 3 , -1 , 2
]
where
g j = (2 * i + j) `mod` (2 * n)
memoBoxEdgeIndices :: V.Vector (UV.Vector Int)
{-# INLINE memoBoxEdgeIndices #-}
memoBoxEdgeIndices =
V.generate 10 $
UV.fromList . boxEdgeIndices . (+ 3)
boxEdgeIndices ::
-- | the number of vertices on the top surface, ie. half the total
Int ->
[Int]
{-# INLINE boxEdgeIndices #-}
boxEdgeIndices n = concatMap f [0 .. n -1]
where
f i =
[ g 0
, g 1
, n + g 0
, g 2
, g 0
, n + g 0
, n + g (-1)
, g 1
, n + g 0
, n + g 1
, n + g (-1)
, g 1
]
where
g j = (i + j) `mod` n
cylinderIndices :: Int -> [Int]
{-# INLINE cylinderIndices #-}
cylinderIndices n =
cylinderRoundIndices n
++ polyToTris [2 * n, 2 * n + 2 .. 4 * n - 1]
++ polyToTris [2 * n + 1, 2 * n + 3 .. 4 * n - 1]
cylinderRoundIndices :: Int -> [Int]
{-# INLINE cylinderRoundIndices #-}
cylinderRoundIndices n =
[ 2 * n -2
, 2 * n -1
, 1
, 2 * n -2
, 1
, 0 -- last side triangle (applies mod 2n)
]
++ concatMap g [0 .. n -2] -- other triangles on sides
where
g x =
[ 2 * x
, 2 * x + 1
, 2 * x + 3
, 2 * x
, 2 * x + 3
, 2 * x + 2
]
topPrismIndices :: Int -> [Int]
{-# INLINE topPrismIndices #-}
topPrismIndices n =
concatMap f [1 .. n -2] -- triangles on top face
++ concatMap f' [1 .. n -2] -- triangles on bottom face
-- these should be checked
++ [ 2 * n -2
, 2 * n -1
, 1
, 2 * n -2
, 1
, 0 -- last side triangle (applies mod 2n)
]
++ concatMap g [0 .. n -2] -- other triangles on sides
where
f x = [0, 2 * x, 2 * x + 2]
f' x = [1, 2 * x + 3, 2 * x + 1]
g x =
[ 2 * x
, 2 * x + 1
, 2 * x + 3
, 2 * x
, 2 * x + 3
, 2 * x + 2
]
-- consider changing the position to a vec4
pokeJustV ::
Float ->
Point3 ->
Point4 ->
Ptr Float ->
Int ->
Point3 ->
IO Int
{-# INLINE pokeJustV #-}
pokeJustV xdata cp col ptr nv sh = do
pokeByteOff ptr (nv * shapeVerxSize) (V4 x y z xdata)
pokeByteOff ptr (nv *shapeVerxSize + 4 * floatSize) (toColor8 col)
pokeByteOff ptr (nv *shapeVerxSize + 4 * (floatSize + ubyteSize)) (V4 nx ny nz 1)
return (nv + 1)
where
V3 x y z = sh
V3 nx ny nz = cp
pokeJustVInvNormal ::
Float ->
Point3 ->
Point4 ->
Ptr Float ->
Int ->
Point3 ->
IO Int
{-# INLINE pokeJustVInvNormal #-}
pokeJustVInvNormal xdata cp col ptr nv sh = do
pokeByteOff ptr (nv * shapeVerxSize) (V4 x y z xdata)
pokeByteOff ptr (nv *shapeVerxSize + 4 * floatSize) (toColor8 col)
pokeByteOff ptr (nv *shapeVerxSize + 4 * (floatSize + ubyteSize)) (V4 nx ny nz 1)
return (nv + 1)
where
V3 x y z = sh
V3 nx ny nz = (2 * sh) - cp
pokeFlatV ::
Float ->
Point3 ->
Point4 ->
Ptr Float ->
Int ->
Point3 ->
IO Int
{-# INLINE pokeFlatV #-}
pokeFlatV xdata norm col ptr nv sh = do
pokeByteOff ptr (nv * shapeVerxSize) (V4 x y z xdata)
pokeByteOff ptr (nv *shapeVerxSize + 4 * floatSize) (toColor8 col)
pokeByteOff ptr (nv *shapeVerxSize + 4 * (floatSize + ubyteSize)) (V4 nx ny nz 1)
return (nv + 1)
where
V3 x y z = sh
V3 nx ny nz = sh - norm
pokeLayVerxs ::
MV.MVector (PrimState IO) (Shader, VBO) ->
UMV.MVector (PrimState IO) Int ->
Picture ->
IO ()
pokeLayVerxs vbos counts = VFSM.mapM_ (pokeLayVerx vbos counts) . VFSM.fromList
pokeLayVerx :: MV.MVector (PrimState IO) (Shader, VBO) -> UMV.MVector (PrimState IO) Int -> Verx -> IO ()
{-# INLINE pokeLayVerx #-}
pokeLayVerx vbos counts vx = do
theOff <- UMV.unsafeRead counts vecPos
basePtr <- _vboPtr . snd <$> MV.unsafeRead vbos sn
let thePtr = plusPtr basePtr ((theOff + layOff) * theStride * floatSize)
poke34 thePtr thePos theCol
pokeArrayOff thePtr 7 (_vxExt vx)
UMV.unsafeModify counts (+ 1) vecPos
where
sn = _unShadNum shadnum
shadnum = _vxShadNum vx
vecPos = theLayer * numLayers + sn
theLayer = layerNum $ _vxLayer vx
thePos = _vxPos vx
theCol = _vxCol vx
layOff = theLayer * numSubElements
theStride = pokeStride shadnum
pokeStride :: ShadNum -> Int
{-# INLINE pokeStride #-}
pokeStride PolyShad = 7
pokeStride TextShad = 11
pokeStride ArcShad = 10
pokeStride EllShad = 7
poke34 :: Ptr Float -> Point3 -> Point4 -> IO ()
{-# INLINE poke34 #-}
poke34 ptr (V3 a b c) (V4 d e f g) = do
pokeElemOff ptr 0 a
pokeElemOff ptr 1 b
pokeElemOff ptr 2 c
pokeElemOff ptr 3 d
pokeElemOff ptr 4 e
pokeElemOff ptr 5 f
pokeElemOff ptr 6 g
pokeArrayOff :: Ptr Float -> Int -> [Float] -> IO ()
{-# INLINE pokeArrayOff #-}
pokeArrayOff ptr i = pokeArray (plusPtr ptr (floatSize * i))