HGamer3D-0.4.0: HGamer3D/Engine/Internal/SystemGraphics3D.hs
{-# Language StandaloneDeriving #-}
{-# OPTIONS_HADDOCK hide #-}
-- This source file is part of HGamer3D
-- (A project to enable 3D game development in Haskell)
-- For the latest info, see http://www.hgamer3d.org
--
-- (c) 2011-2014 Peter Althainz
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
-- HGamer3D/Internal/ECS/SystemGraphics3D.hs
-- | the Graphics3D System of the Entity-Component-System World
module HGamer3D.Engine.Internal.SystemGraphics3D
where
import Control.Concurrent.MVar
import qualified Data.Map as M
import Data.Maybe
import Data.Typeable
import Data.Dynamic
import HGamer3D.Engine.Internal.Entity
import HGamer3D.Engine.Internal.Component
import HGamer3D.Engine.Internal.ComponentType
import HGamer3D.Engine.Internal.System
import Data.IORef
import Data.Hashable
import qualified Data.HashTable.IO as HT
import Data.List.Split
import qualified HGamer3D.Data as D
import qualified HGamer3D.Internal.Graphics3D as Gr
import qualified HGamer3D.Engine.BaseAPI as E
import qualified HGamer3D.Engine.Internal.Event as Evt
import qualified HGamer3D.GUI.BaseAPI as GU
import qualified HGamer3D.WinEvent.BaseAPI as WinEvt
import HGamer3D.Graphics3D.Schema.Figure
import HGamer3D.Graphics3D.Schema.Geometry
import HGamer3D.Graphics3D.Schema.Material
import HGamer3D.Graphics3D.Schema.Camera
import HGamer3D.Graphics3D.Schema.Light
import HGamer3D.Graphics3D.Schema.Scene
import HGamer3D.Engine.Schema.EventReceiver
import HGamer3D.GUI.Schema.Form
-- the system of entity component system, in general a system has internal state
-- entities can be added to it and the system has a step function, to run it
-- Systems are self-contained, so they can be run in a thread and manage their state themselves
data ECSGraphics3D = ECSGraphics3D {
-- status of graphics engine
g3d :: (Gr.Graphics3DSystem, GU.GUISystem),
receivers :: ListAndCache EventReceiver (),
-- figures
figures :: ListAndCache Figure (Gr.Object3D Figure),
posfig :: ListAndCache D.Position (),
orifig :: ListAndCache D.Orientation (),
-- cameras
cameras :: ListAndCache Camera Gr.Camera,
poscam :: ListAndCache D.Position (),
oricam :: ListAndCache D.Orientation (),
-- lights
lights :: ListAndCache Light Gr.Light,
poslig :: ListAndCache D.Position (),
orilig :: ListAndCache D.Orientation (),
-- gui forms
guiforms :: ListAndCache Form GU.GUIEngineData,
-- scene parameter
scenepars :: ListAndCache SceneParameter (),
-- gametime
gt :: IORef D.GameTime
}
instance System ECSGraphics3D where
addEntity ecsg3d entity = do
lacAdd entity (receivers ecsg3d)
lacAdd entity (figures ecsg3d)
lacAdd entity (posfig ecsg3d)
lacAdd entity (orifig ecsg3d)
lacAdd entity (cameras ecsg3d)
lacAdd entity (poscam ecsg3d)
lacAdd entity (oricam ecsg3d)
lacAdd entity (lights ecsg3d)
lacAdd entity (poslig ecsg3d)
lacAdd entity (orilig ecsg3d)
lacAdd entity (guiforms ecsg3d)
lacAdd entity (scenepars ecsg3d)
return ecsg3d
removeEntity ecsg3d entity = do
lacRemove entity (receivers ecsg3d)
lacRemove entity (figures ecsg3d)
lacRemove entity (posfig ecsg3d)
lacRemove entity (orifig ecsg3d)
lacRemove entity (cameras ecsg3d)
lacRemove entity (poscam ecsg3d)
lacRemove entity (oricam ecsg3d)
lacRemove entity (lights ecsg3d)
lacRemove entity (poslig ecsg3d)
lacRemove entity (orilig ecsg3d)
lacRemove entity (guiforms ecsg3d)
lacRemove entity (scenepars ecsg3d)
return ecsg3d
initializeSystem = do
g3d <- E.initHGamer3D "HGamer3D - System" True True True
let (g3ds, guis, gtime) = g3d
Gr.setAmbientLight g3ds D.white
recv <- lacInitialize CTEvR
figs <- lacInitialize CTFig
posfig <- lacInitialize CTPos
orifig <- lacInitialize CTOri
cams <- lacInitialize CTCam
poscam <- lacInitialize CTPos
oricam <- lacInitialize CTOri
ligs <- lacInitialize CTLig
poslig <- lacInitialize CTPos
orilig <- lacInitialize CTOri
gfos <- lacInitialize CTGFo
scpars <- lacInitialize CTScP
mgt <- newIORef gtime
return $ (ECSGraphics3D (g3ds, guis) recv figs posfig orifig cams poscam oricam ligs poslig orilig gfos scpars mgt)
stepSystem ecsg3d = do
let (g3ds, guis) = (g3d ecsg3d)
-- small helper functions
let update' pos edata schema = D.positionTo edata pos
let update'' pos edata schema = D.orientationTo edata pos
-- receivers
lacApplyChanges (receivers ecsg3d) (\s -> return ()) (\s e -> return ()) (\e -> return ()) lacHandleU2CEvents lacHandleC2UEvents
-- figures
lacApplyChanges (figures ecsg3d) (Gr.object3D g3ds) (Gr.update3D g3ds) (Gr.remove3D g3ds) lacHandleU2CEvents lacHandleC2UEvents
lacApplyOtherChanges (posfig ecsg3d) (figures ecsg3d) update'
lacApplyOtherChanges (orifig ecsg3d) (figures ecsg3d) update''
-- cameras
let handleU2CEvents evts cam = do
mapM (\evt -> case evt of
(E.WindowEvt (WinEvt.EvtWindow _ _ WinEvt.SDL_WINDOWEVENT_SIZE_CHANGED x y)) -> Gr.cameraAdaptAspectRatio cam
_ -> return ()
) evts
return ()
lacApplyChanges (cameras ecsg3d) (Gr.addCamera g3ds) (Gr.updateCamera g3ds) (Gr.removeCamera g3ds) handleU2CEvents lacHandleC2UEvents
lacApplyOtherChanges (poscam ecsg3d) (cameras ecsg3d) update'
lacApplyOtherChanges (oricam ecsg3d) (cameras ecsg3d) update''
-- lights
lacApplyChanges (lights ecsg3d) (Gr.addLight g3ds) (Gr.updateLight g3ds) (Gr.removeLight g3ds) lacHandleU2CEvents lacHandleC2UEvents
lacApplyOtherChanges (poslig ecsg3d) (lights ecsg3d) update'
lacApplyOtherChanges (orilig ecsg3d) (lights ecsg3d) update''
-- gui forms
newFormEvents <- newIORef []
let handleU2CEvents evts form = do
mapM (\evt -> case evt of
(E.FormEvt (E.FormSetValue values)) -> GU.setFormValues form values
(E.GUIEvt (GU.GUIEvent tag sender window)) -> do
values <- GU.getFormValues form
strType <- GU.typeOfGuiEl window >>= return . last . (splitOn "/")
if tag `elem` (map fst values) then do
-- check if button
let newEvt = if strType == "Button"
then E.FormEvt (E.FormButtonClick tag)
else E.FormEvt (E.FormValueChange tag values)
-- show for testing
modifyIORef newFormEvents (\oldList -> (newEvt : oldList))
return ()
else return ()
_ -> return ()
) evts
return ()
lacApplyChanges (guiforms ecsg3d) (GU.createForm guis) (GU.updateForm guis) (GU.removeForm guis) handleU2CEvents lacHandleC2UEvents
-- scene parameters
let updateScene eng new = Gr.setSceneParameter g3ds new
let removeScene eng = return ()
lacApplyChanges (scenepars ecsg3d) (Gr.setSceneParameter g3ds) updateScene removeScene lacHandleU2CEvents lacHandleC2UEvents
-- step graphics system
t <- readIORef (gt ecsg3d)
(evts, nt, qFlag) <- E.stepHGamer3D g3ds guis t
writeIORef (gt ecsg3d) nt
-- handover evts towards the event system
inList <- readIORef (lacList (receivers ecsg3d))
evts' <- readIORef newFormEvents
mapM (\(eid, com) -> do
_pushC2UEvents com evts
_pushC2UEvents com evts'
if qFlag then
_pushC2UEvents com [Evt.AppEvt Evt.AppQuit]
else return ()
) inList
-- send camera resize events, set gui size
let camEvts = filter (\evt -> case evt of
(E.WindowEvt (WinEvt.EvtWindow _ _ WinEvt.SDL_WINDOWEVENT_SIZE_CHANGED x y)) -> True
_ -> False) evts
if length camEvts > 0 then do
camList <- readIORef (lacList (cameras ecsg3d))
mapM (\cam -> _pushU2CEvents (snd cam) camEvts) camList
mapM (\(E.WindowEvt (WinEvt.EvtWindow _ _ WinEvt.SDL_WINDOWEVENT_SIZE_CHANGED x y)) -> GU.notifyDisplaySizeChanged guis (fromIntegral x) (fromIntegral y)) camEvts
else return [()]
-- handle GUI events, create high level gui events, push GUI events to gfos
let guiEvts = filterEventType [GUIEvents] evts
gfoList <- readIORef (lacList (guiforms ecsg3d))
mapM (\gfo -> _pushU2CEvents (snd gfo) guiEvts) gfoList
return (ecsg3d, qFlag)
shutdownSystem ecsg3d = do
let (g3ds, guis) = (g3d ecsg3d)
E.freeHGamer3D g3ds guis
return ()
runSystemGraphics3D :: D.GameTime -> IO ECSGraphics3D
runSystemGraphics3D sleepT = runSystem sleepT