HGamer3D-Graphics3D-0.4.0: HGamer3D/Graphics3D/Internal/Shapes.hs
{-# LANGUAGE FlexibleInstances #-}
{-# 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-2013 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.
-- Graphics3D/Internal/Object3D.hs
-- | Creating and managing 3D objects functionality for Graphics3D module, internal implemenatation module. Public API is HGamer3D.Graphics3D.
module HGamer3D.Graphics3D.Internal.Shapes
where
import HGamer3D.Data
import HGamer3D.Data.HG3DClass
import HGamer3D.Util
import GHC.Ptr
import HGamer3D.Bindings.Ogre.ClassPtr
import HGamer3D.Bindings.Ogre.Utils
import HGamer3D.Bindings.Ogre.StructColour
import HGamer3D.Bindings.Ogre.StructSharedPtr
import HGamer3D.Bindings.Ogre.EnumSceneType
import HGamer3D.Bindings.Ogre.EnumNodeTransformSpace
import HGamer3D.Bindings.Ogre.EnumLightType
import HGamer3D.Bindings.Ogre.ClassLight as Light
import HGamer3D.Bindings.Ogre.ClassNode as Node
import HGamer3D.Bindings.Ogre.ClassSceneManager as SceneManager
import HGamer3D.Bindings.Ogre.ClassSceneNode as SceneNode
import HGamer3D.Bindings.Ogre.ClassRenderTarget as RenderTarget
import HGamer3D.Bindings.Ogre.ClassRenderWindow as RenderWindow
import HGamer3D.Bindings.Ogre.ClassResourceGroupManager as ResourceGroupManager
import HGamer3D.Bindings.Ogre.ClassTextureManager as TextureManager
import HGamer3D.Bindings.Ogre.ClassControllerManager as ControllerManager
import HGamer3D.Bindings.Ogre.ClassViewport as Viewport
import HGamer3D.Bindings.Ogre.ClassFrustum as Frustum
import HGamer3D.Bindings.Ogre.ClassAnimationState as AnimationState
import HGamer3D.Bindings.Ogre.ClassEntity as Entity
import HGamer3D.Bindings.Ogre.ClassControllerManager as ControllerManager
import HGamer3D.Bindings.Ogre.ClassWindowEventUtilities as WindowEventUtilities
import HGamer3D.Bindings.Ogre.ClassHG3DUtilities as HG3DUtils
import HGamer3D.Bindings.Ogre.ClassManualObject as ManualObject
import HGamer3D.Bindings.Ogre.EnumRenderOperationOperationType
import HGamer3D.Bindings.Ogre.StructHG3DClass
import HGamer3D.Bindings.Ogre.EnumSceneManagerPrefabType
import HGamer3D.Data.HG3DClass
import HGamer3D.Data.Transform3D
import HGamer3D.Graphics3D.Internal.Base
import Control.Monad
import Control.Monad.Trans
import Control.Monad.State.Class
import HGamer3D.Graphics3D.Schema.Material
import HGamer3D.Graphics3D.Schema.Geometry
import HGamer3D.Graphics3D.Schema.Figure
import HGamer3D.Graphics3D.Internal.PlatonShapes
{- -------------------------------------------------------------------------------
EngineItem for Geometry
------------------------------------------------------------------------------- -}
-- helper functions
_createGeometry :: Graphics3DSystem -> Geometry -> IO OEntity
_createGeometry g3ds geo = do
let (SceneManager scm) = (g3dsSceneManager g3ds)
entity <- case geo of
ResourceGeometry s -> SceneManager.createEntity3 scm s
Cube -> SceneManager.createEntity6 scm PT_CUBE
Sphere -> SceneManager.createEntity6 scm PT_SPHERE
Ikosaeder -> ikosaederE g3ds white
Dodekaeder -> dodekaederE g3ds white
Plane -> SceneManager.createEntity6 scm PT_PLANE
_ -> error "HGamer3D.Graphics3D.Internal.Shapes._createMesh: Geo not implemented"
let rE = OE entity
if (geo /= Dodekaeder) && (geo /= Ikosaeder) then
_buildTV rE
else return ()
return $ rE
_buildTV :: OEntity -> IO ()
_buildTV (OE entity) = HG3DUtils.buildTangentVectors entity
{- -------------------------------------------------------------------------------
EngineItem for Figure
------------------------------------------------------------------------------- -}
_createResourceFigure :: Graphics3DSystem -> String -> IO OEntity
_createResourceFigure g3ds name = do
let (SceneManager scm) = (g3dsSceneManager g3ds)
-- create the entity from the mesh
entity <- SceneManager.createEntity3 scm name
return $ OE entity
_setMaterial :: OEntity -> Material -> IO ()
_setMaterial (OE entity) material = do
case material of
(ResourceMaterial name) -> do
Entity.setMaterialName entity name "General"
_ -> error "HGamer3D.Graphics3D.Internal.Shapes._setMaterial: Material not implemented"
_setNodePOS n pos ori siz = do
setNodePos n pos
setNodeOri n ori
setNodeSiz n siz
_createFigure :: Graphics3DSystem -> ONode -> Figure -> IO (EngineData)
_createFigure g3ds parent fig = do
node <- _createSubNode parent
case fig of
SimpleFigure geo mat -> do
meshEntity <- _createGeometry g3ds geo
_setMaterial meshEntity mat
_addEntityToNode g3ds node meshEntity
return (EDEntityNode meshEntity node)
ResourceFigure name -> do
meshEntity <- _createResourceFigure g3ds name
_buildTV meshEntity
_addEntityToNode g3ds node meshEntity
return (EDEntityNode meshEntity node)
CombinedFigure arrSubs -> do
resArr <- mapM (\(pos, ori, size, fig) -> do
newData <- _createFigure g3ds node fig
_setNodePOS newData pos ori size
return newData) arrSubs
return (EDNodeAndSub node resArr)
data UpdateActions = ResetMaterial
| ResetGeometry
| ResetResource
| CompareSub
| Rebuild
| DoNothing
deriving (Eq, Show)
_updateFigure :: Graphics3DSystem -> ONode -> EngineData -> Figure -> Figure -> IO (EngineData)
_updateFigure g3ds parent edata oldFig newFig = do
let node = getNode edata
-- check, which action is needed
let action = case oldFig of
SimpleFigure oldGeo oldMat -> case newFig of
SimpleFigure newGeo newMat -> if newGeo == oldGeo
then
if newMat == oldMat
then DoNothing
else ResetMaterial
else ResetGeometry
ResourceFigure name -> ResetResource
CombinedFigure _ -> Rebuild
ResourceFigure oldName -> case newFig of
ResourceFigure newName -> if newName == oldName
then DoNothing
else ResetResource
SimpleFigure newGeo newMat -> ResetGeometry
CombinedFigure _ -> Rebuild
CombinedFigure oldSubs -> case newFig of
SimpleFigure _ _ -> Rebuild
ResourceFigure _ -> Rebuild
CombinedFigure newSubs -> if (length oldSubs) == (length newSubs)
then CompareSub
else Rebuild
edataNew <-
-- perform the single actions, we can keep existing node and single EngineData item form
if action == ResetMaterial || action == ResetGeometry || action == ResetResource
then do
let (EDEntityNode eOld nOld) = edata
case action of
ResetMaterial -> do
let (SimpleFigure geo mat) = newFig
_setMaterial eOld mat
return edata
ResetGeometry -> do
let (SimpleFigure geo mat) = newFig
eNew <- _createGeometry g3ds geo
_setMaterial eNew mat
_exchangeEntityInNode g3ds nOld eOld eNew
return (EDEntityNode eNew nOld)
ResetResource -> do
let (ResourceFigure name) = newFig
eNew <- _createResourceFigure g3ds name
-- _buildTV eNew
_exchangeEntityInNode g3ds nOld eOld eNew
return (EDEntityNode eNew nOld)
-- perform the actions, we compare two equal length subarrays
else if action == CompareSub
then do
let (CombinedFigure oldSubs) = oldFig
let (CombinedFigure newSubs) = newFig
let (EDNodeAndSub node' edOldArr) = edata
outArr <- mapM ( \(oldED, (posO, oriO, sizeO, oldFig), (posN, oriN, sizeN, newFig)) -> do
newED <- _updateFigure g3ds node' oldED oldFig newFig
if posO /= posN then setNodePos newED posN else return ()
if oriO /= oriN then setNodeOri newED oriN else return ()
if sizeO /= sizeN then setNodeSiz newED sizeN else return ()
return newED
) (zip3 edOldArr oldSubs newSubs)
return (EDNodeAndSub node' outArr)
-- perform the actions, do a complete rebuild, since structure does not match
else if action == Rebuild
then do
-- delete old structure (to be done)
_removeFigure g3ds parent edata
-- create new structure, this is the easy part
_createFigure g3ds parent newFig
-- no action needed, all the same
else if action == DoNothing
then return edata
else return $ error ("HGamer3D.Graphics3D.Internal.Shapes._updateFigure: build action not known " ++ (show action))
return edataNew
_removeFigure :: Graphics3DSystem -> ONode -> EngineData -> IO ()
_removeFigure g3ds parent edata = do
case edata of
EDEntityNode e n -> _removeEntityAndNode g3ds parent e n
EDNodeAndSub n subs -> (mapM (\edata' -> _removeFigure g3ds n edata') subs) >> return ()
instance Graphics3DItem Figure where
object3D g3ds fig = do
rootNode <- _getRootNode g3ds
edata <- _createFigure g3ds rootNode fig
return $ Object3D edata fig
update3D g3ds ob3d newFig = do
rootNode <- _getRootNode g3ds
let (Object3D oldEdata oldFig) = ob3d
newEdata <- _updateFigure g3ds rootNode oldEdata oldFig newFig
return $ Object3D newEdata newFig
remove3D g3ds ob3d = do
rootNode <- _getRootNode g3ds
let (Object3D edata fig) = ob3d
_removeFigure g3ds rootNode edata
return ()
{- -------------------------------------------------------------------------------
intelligent constructors for simple geometries
------------------------------------------------------------------------------- -}
sphere :: Graphics3DSystem -> Float -> Material -> IO (Object3D Figure)
sphere g3ds radius material = object3D g3ds (CombinedFigure [
(zeroVec3, unitU, Vec3 radius radius radius,
SimpleFigure Sphere material) ])
cube :: Graphics3DSystem -> Float -> Material -> IO (Object3D Figure)
cube g3ds len material = object3D g3ds (CombinedFigure [
(zeroVec3, unitU, Vec3 len len len,
SimpleFigure Cube material) ])
cuboid :: Graphics3DSystem -> Vec3 -> Material -> IO (Object3D Figure)
cuboid g3ds vec material = object3D g3ds (CombinedFigure [
(zeroVec3, unitU, vec, SimpleFigure Cube material) ])
dodekaeder :: Graphics3DSystem -> Float -> Material -> IO (Object3D Figure)
dodekaeder g3ds len material = object3D g3ds (CombinedFigure [
(zeroVec3, unitU, Vec3 len len len, SimpleFigure Dodekaeder material) ])
ikosaeder :: Graphics3DSystem -> Float -> Material -> IO (Object3D Figure)
ikosaeder g3ds len material = object3D g3ds (CombinedFigure [
(zeroVec3, unitU, Vec3 len len len, SimpleFigure Ikosaeder material) ])
instance HasPosition (Object3D Figure) where
position obj = Node.getPosition (getNode' obj)
positionTo obj pos = Node.setPosition (getNode' obj) pos
instance HasSize (Object3D Figure) where
size obj = Node.getScale (getNode' obj)
sizeTo obj pos = Node.setScale (getNode' obj) pos
instance HasOrientation (Object3D Figure) where
orientation obj = do
q <- Node.getOrientation (getNode' obj)
let uq = mkNormal q
return uq
orientationTo obj uq = do
Node.setOrientation (getNode' obj) (fromNormal uq)
return ()