From 47832170e1b107b6a937e9d46bf07d7502abe159 Mon Sep 17 00:00:00 2001 From: Marc Sunet Date: Fri, 31 Aug 2012 22:26:56 +0200 Subject: Added ability to choose how 2d rotations should be mapped to 3d --- Spear.lkshs | 10 ++++---- Spear.lkshw | 2 +- Spear/Math/MatrixUtils.hs | 59 +++++++++++++++++++++++++---------------------- Spear/Scene/GameObject.hs | 53 +++++++++++++++++++++++++++++++++--------- Spear/Scene/Loader.hs | 18 +++++++++++---- 5 files changed, 92 insertions(+), 50 deletions(-) diff --git a/Spear.lkshs b/Spear.lkshs index 5ee07cf..4b0d469 100644 --- a/Spear.lkshs +++ b/Spear.lkshs @@ -1,18 +1,18 @@ Version of session file format: 1 Time of storage: - "Fri Aug 31 16:57:11 CEST 2012" -Layout: VerticalP (TerminalP {paneGroups = fromList [], paneTabs = Just TopP, currentPage = 4, detachedId = Nothing, detachedSize = Nothing}) (HorizontalP (TerminalP {paneGroups = fromList [("Browser",HorizontalP (TerminalP {paneGroups = fromList [], paneTabs = Nothing, currentPage = 0, detachedId = Nothing, detachedSize = Nothing}) (HorizontalP (TerminalP {paneGroups = fromList [], paneTabs = Nothing, currentPage = 0, detachedId = Nothing, detachedSize = Nothing}) (TerminalP {paneGroups = fromList [], paneTabs = Nothing, currentPage = 0, detachedId = Nothing, detachedSize = Nothing}) 290) 209),("Debug",HorizontalP (TerminalP {paneGroups = fromList [], paneTabs = Nothing, currentPage = 0, detachedId = Nothing, detachedSize = Nothing}) (TerminalP {paneGroups = fromList [], paneTabs = Just TopP, currentPage = 1, detachedId = Nothing, detachedSize = Nothing}) 246)], paneTabs = Just BottomP, currentPage = 0, detachedId = Nothing, detachedSize = Nothing}) (TerminalP {paneGroups = fromList [], paneTabs = Nothing, currentPage = 0, detachedId = Nothing, detachedSize = Nothing}) 639) 954 -Population: [(Just (BufferSt (BufferState "/home/jeanne/programming/haskell/Spear/demos/simple-scene/Game/Program/Box.hs" 1712)),[SplitP LeftP]),(Just (BreakpointsSt BreakpointsState),[SplitP RightP,SplitP TopP,GroupP "Debug",SplitP BottomP]),(Just (ErrorsSt ErrorsState),[SplitP RightP,SplitP TopP]),(Just (BufferSt (BufferState "/home/jeanne/programming/haskell/Spear/demos/simple-scene/Game/GameObject/Factory.hs" 1309)),[SplitP LeftP]),(Just (FilesSt FilesState),[SplitP RightP,SplitP TopP]),(Just (BufferSt (BufferState "/home/jeanne/programming/haskell/Spear/Spear/GLSL.hs" 13235)),[SplitP LeftP]),(Just (BufferSt (BufferState "/home/jeanne/programming/haskell/Spear/Spear/Scene/GameObject.hs" 4522)),[SplitP LeftP]),(Just (BufferSt (BufferState "/home/jeanne/programming/haskell/Spear/demos/simple-scene/Game/GameObject.hs" 1652)),[SplitP LeftP]),(Just (BufferSt (BufferState "/home/jeanne/programming/haskell/Spear/demos/simple-scene/Game/GameState.hs" 584)),[SplitP LeftP]),(Just (InfoSt (InfoState Nothing)),[SplitP RightP,SplitP TopP,GroupP "Browser",SplitP BottomP,SplitP BottomP]),(Just (LogSt LogState),[SplitP RightP,SplitP BottomP]),(Just (BufferSt (BufferState "/home/jeanne/programming/haskell/Spear/Spear/Assets/Model.hsc" 15715)),[SplitP LeftP]),(Just (ModulesSt (ModulesState 328 (PackageScope False,False) (Just (ModuleName ["Game","GameState"]),Nothing) (ExpanderState {packageExp = ([],[]), packageExpNoBlack = ([[0]],[]), packageDExp = ([],[]), packageDExpNoBlack = ([],[]), workspaceExp = ([],[]), workspaceExpNoBlack = ([],[]), workspaceDExp = ([],[]), workspaceDExpNoBlack = ([],[]), systemExp = ([],[]), systemExpNoBlack = ([],[])}))),[SplitP RightP,SplitP TopP,GroupP "Browser",SplitP BottomP,SplitP TopP]),(Just (SearchSt (SearchState {searchString = "putStrLn", searchScope = PackageScope False, searchMode = Prefix {caseSense = False}})),[SplitP RightP,SplitP TopP]),(Just (TraceSt TraceState),[SplitP RightP,SplitP TopP,GroupP "Debug",SplitP BottomP]),(Just (BufferSt (BufferState "/home/jeanne/programming/haskell/Spear/demos/simple-scene/Game/GameObject/Utils.hs" 726)),[SplitP LeftP]),(Just (VariablesSt VariablesState),[SplitP RightP,SplitP TopP,GroupP "Debug",SplitP BottomP]),(Just (WorkspaceSt WorkspaceState),[SplitP RightP,SplitP TopP,GroupP "Browser",SplitP TopP]),(Just (BufferSt (BufferStateTrans "_Eval.hs" "\n" 0)),[SplitP RightP,SplitP TopP,GroupP "Debug",SplitP TopP]),(Just (BufferSt (BufferState "/home/jeanne/programming/haskell/Spear/demos/simple-scene/main.hs" 2287)),[SplitP LeftP])] + "Fri Aug 31 22:25:44 CEST 2012" +Layout: VerticalP (TerminalP {paneGroups = fromList [], paneTabs = Just TopP, currentPage = 1, detachedId = Nothing, detachedSize = Nothing}) (HorizontalP (TerminalP {paneGroups = fromList [("Browser",HorizontalP (TerminalP {paneGroups = fromList [], paneTabs = Nothing, currentPage = 0, detachedId = Nothing, detachedSize = Nothing}) (HorizontalP (TerminalP {paneGroups = fromList [], paneTabs = Nothing, currentPage = 0, detachedId = Nothing, detachedSize = Nothing}) (TerminalP {paneGroups = fromList [], paneTabs = Nothing, currentPage = 0, detachedId = Nothing, detachedSize = Nothing}) 302) 217),("Debug",HorizontalP (TerminalP {paneGroups = fromList [], paneTabs = Nothing, currentPage = 0, detachedId = Nothing, detachedSize = Nothing}) (TerminalP {paneGroups = fromList [], paneTabs = Just TopP, currentPage = 1, detachedId = Nothing, detachedSize = Nothing}) 256)], paneTabs = Just BottomP, currentPage = 0, detachedId = Nothing, detachedSize = Nothing}) (TerminalP {paneGroups = fromList [], paneTabs = Nothing, currentPage = 0, detachedId = Nothing, detachedSize = Nothing}) 663) 954 +Population: [(Just (BreakpointsSt BreakpointsState),[SplitP RightP,SplitP TopP,GroupP "Debug",SplitP BottomP]),(Just (ErrorsSt ErrorsState),[SplitP RightP,SplitP TopP]),(Just (BufferSt (BufferState "/home/jeanne/programming/haskell/Spear/demos/simple-scene/Game/GameObject/Factory.hs" 1205)),[SplitP LeftP]),(Just (FilesSt FilesState),[SplitP RightP,SplitP TopP]),(Just (BufferSt (BufferState "/home/jeanne/programming/haskell/Spear/Spear/Scene/GameObject.hs" 3114)),[SplitP LeftP]),(Just (BufferSt (BufferState "/home/jeanne/programming/haskell/Spear/demos/simple-scene/Game/GameObject.hs" 1432)),[SplitP LeftP]),(Just (InfoSt (InfoState Nothing)),[SplitP RightP,SplitP TopP,GroupP "Browser",SplitP BottomP,SplitP BottomP]),(Just (BufferSt (BufferState "/home/jeanne/programming/haskell/Spear/Spear/Scene/Loader.hs" 17178)),[SplitP LeftP]),(Just (LogSt LogState),[SplitP RightP,SplitP BottomP]),(Just (BufferSt (BufferState "/home/jeanne/programming/haskell/Spear/Spear/Math/MatrixUtils.hs" 1235)),[SplitP LeftP]),(Just (ModulesSt (ModulesState 328 (PackageScope False,False) (Nothing,Nothing) (ExpanderState {packageExp = ([],[]), packageExpNoBlack = ([[0]],[]), packageDExp = ([],[]), packageDExpNoBlack = ([],[]), workspaceExp = ([],[]), workspaceExpNoBlack = ([],[]), workspaceDExp = ([],[]), workspaceDExpNoBlack = ([],[]), systemExp = ([],[]), systemExpNoBlack = ([],[])}))),[SplitP RightP,SplitP TopP,GroupP "Browser",SplitP BottomP,SplitP TopP]),(Just (BufferSt (BufferState "/home/jeanne/programming/haskell/Spear/demos/simple-scene/Game/GameObject/Player.hs" 2249)),[SplitP LeftP]),(Just (SearchSt (SearchState {searchString = "putStrLn", searchScope = PackageScope False, searchMode = Prefix {caseSense = False}})),[SplitP RightP,SplitP TopP]),(Just (TraceSt TraceState),[SplitP RightP,SplitP TopP,GroupP "Debug",SplitP BottomP]),(Just (BufferSt (BufferState "/home/jeanne/programming/haskell/Spear/demos/simple-scene/Game/GameObject/Utils.hs" 587)),[SplitP LeftP]),(Just (VariablesSt VariablesState),[SplitP RightP,SplitP TopP,GroupP "Debug",SplitP BottomP]),(Just (WorkspaceSt WorkspaceState),[SplitP RightP,SplitP TopP,GroupP "Browser",SplitP TopP]),(Just (BufferSt (BufferStateTrans "_Eval.hs" "\n" 0)),[SplitP RightP,SplitP TopP,GroupP "Debug",SplitP TopP])] Window size: (1820,944) Completion size: (750,399) Workspace: Just "/home/jeanne/programming/haskell/Spear/Spear.lkshw" -Active pane: Just "GameObject.hs(1)" +Active pane: Just "GameObject.hs" Toolbar visible: True FindbarState: (False,FindState {entryStr = "986", entryHist = ["986","drawElements","56","drawArray","drawVA","f","SV","VAO","'VAO'","\170","\\","^"], replaceStr = "V3.", replaceHist = [], caseSensitive = True, entireWord = False, wrapAround = False, regex = False, lineNr = 1}) Recently opened files: - ["/home/jeanne/programming/haskell/Spear/demos/simple-scene/main.hs","/home/jeanne/programming/haskell/Spear/demos/simple-scene/Game/Program/Box.hs","/home/jeanne/programming/haskell/Spear/demos/simple-scene/Game/GameState.hs","/home/jeanne/programming/haskell/Spear/Spear/Setup.hs","/home/jeanne/programming/haskell/Spear/Spear/Scene/SceneResources.hs","/home/jeanne/programming/haskell/Spear/Spear/Scene/Loader.hs","/home/jeanne/programming/haskell/Spear/Spear/GLSL.hs","/home/jeanne/programming/haskell/Spear/demos/simple-scene/Game/GameObject/Utils.hs","/home/jeanne/programming/haskell/Spear/Spear/Render/Texture.hs","/home/jeanne/programming/haskell/Spear/Spear/Render/AnimatedModel.hs","/home/jeanne/programming/haskell/Spear/Spear/Render/StaticModel.hs","/home/jeanne/programming/haskell/Spear/Spear/Render/Program.hs"] + ["/home/jeanne/programming/haskell/Spear/Spear/Math/Spatial2.hs","/home/jeanne/programming/haskell/Spear/Spear/Math/Matrix4.hs","/home/jeanne/programming/haskell/Spear/Spear/Assets/Model/Model.c","/home/jeanne/programming/haskell/Spear/Spear/Assets/Model.hsc","/home/jeanne/programming/haskell/Spear/Spear/Assets/Model/Model.h","/home/jeanne/programming/haskell/Spear/Spear/Assets/Model/OBJ/OBJ_load.c","/home/jeanne/programming/haskell/Spear/demos/simple-scene/main.hs","/home/jeanne/programming/haskell/Spear/demos/simple-scene/Game/Program/Box.hs","/home/jeanne/programming/haskell/Spear/demos/simple-scene/Game/GameState.hs","/home/jeanne/programming/haskell/Spear/Spear/Setup.hs","/home/jeanne/programming/haskell/Spear/Spear/Scene/SceneResources.hs","/home/jeanne/programming/haskell/Spear/Spear/Scene/Loader.hs"] Recently opened workspaces: ["/home/jeanne/programming/haskell/hagen/hagen.lkshw","/home/jeanne/programming/haskell/foo/foo.lkshw","/home/jeanne/programming/haskell/Spear/Spear.lkshw","/home/jeanne/programming/haskell/nexus/nexus.lkshw","/home/jeanne/leksah.lkshw"] \ No newline at end of file diff --git a/Spear.lkshw b/Spear.lkshw index 9220bd8..79c18ef 100644 --- a/Spear.lkshw +++ b/Spear.lkshw @@ -1,7 +1,7 @@ Version of workspace file format: 1 Time of storage: - "Fri Aug 31 18:36:39 CEST 2012" + "Fri Aug 31 20:53:49 CEST 2012" Name of the workspace: "Spear" File paths of contained packages: diff --git a/Spear/Math/MatrixUtils.hs b/Spear/Math/MatrixUtils.hs index 68ad6cd..2c1d083 100644 --- a/Spear/Math/MatrixUtils.hs +++ b/Spear/Math/MatrixUtils.hs @@ -1,6 +1,7 @@ module Spear.Math.MatrixUtils ( - fastNormalMatrix + Rotation(..) +, fastNormalMatrix , rpgTransform , pltTransform , rpgInverse @@ -15,6 +16,9 @@ import Spear.Math.Vector2 as V2 import Spear.Math.Vector3 as V3 +data Rotation = Yaw | Pitch | Roll deriving Eq + + -- | Compute the normal matrix of the given matrix. fastNormalMatrix :: Matrix4 -> Matrix3 fastNormalMatrix m = @@ -26,13 +30,26 @@ fastNormalMatrix m = -- | Maps the given 2D transformation matrix to a 3D transformation matrix. -rpgTransform :: Float -- ^ The height above the ground. - -> Matrix3 -> Matrix4 -rpgTransform h mat = - let r = let r' = M3.right mat in vec3 (V2.x r') (V2.y r') 0 +rpgTransform + :: Float -- ^ The height above the ground. + -> Float -- ^ Angle of rotation. + -> Rotation -- ^ How the 2D rotation should be interpreted in 3D. + -> Matrix3 + -> Matrix4 +rpgTransform h a rtype mat = + {-let r = let r' = M3.right mat in vec3 (V2.x r') (V2.y r') 0 u = V3.unity f = let f' = M3.forward mat in vec3 (V2.x f') 0 (V2.y f') - t = (vec3 0 h 0) + let t' = M3.position mat in vec3 (V2.x t') 0 (V2.y t') + t = (vec3 0 h 0) + let t' = M3.position mat in vec3 (V2.x t') 0 (V2.y t')-} + let rot = case rtype of + Yaw -> rotY + Pitch -> rotX + Roll -> rotZ + mat' = rot a + r = M4.right mat' + u = M4.up mat' + f = M4.forward mat' + t = vec3 0 h 0 + let t' = M3.position mat in vec3 (V2.x t') 0 (V2.y t') in mat4 (V3.x r) (V3.x u) (V3.x f) (V3.x t) (V3.y r) (V3.y u) (V3.y f) (V3.y t) @@ -61,18 +78,13 @@ pltTransform mat = -- The XY plane in 2D translates to the X(-Z) plane in 3D. -- -- Use this in games such as RPGs and RTSs. -rpgInverse :: Float -- ^ Height above the ground. - -> Matrix3 -> Matrix4 -rpgInverse h mat = - let r = let r' = M3.right mat in vec3 (V2.x r') (V2.y r') 0 - u = V3.unity - f = let f' = M3.forward mat in vec3 (V2.x f') 0 (-V2.y f') - t = (vec3 0 h 0) + let t' = M3.position mat in -(vec3 (V2.x t') 0 (-V2.y t')) - in mat4 - (V3.x r) (V3.y r) (V3.z r) (t `V3.dot` r) - (V3.x u) (V3.y u) (V3.z u) (t `V3.dot` u) - (V3.x f) (V3.y f) (V3.z f) (t `V3.dot` f) - 0 0 0 1 +rpgInverse + :: Float -- ^ The height above the ground. + -> Float -- ^ Angle of rotation. + -> Rotation -- ^ How the 2D rotation should be interpreted in 3D. + -> Matrix3 + -> Matrix4 +rpgInverse h a rot = M4.inverseTransform . rpgTransform h a rot -- | Compute the inverse transform of the given transformation matrix. @@ -83,13 +95,4 @@ rpgInverse h mat = -- -- Use this in games like platformers and space invaders style games. pltInverse :: Matrix3 -> Matrix4 -pltInverse mat = - let r = let r' = M3.right mat in vec3 (V2.x r') (V2.y r') 0 - u = let u' = M3.up mat in vec3 (V2.x u') (V2.y u') 0 - f = V3.unitz - t = let t' = M3.position mat in vec3 (V2.x t') (V2.y t') 0 - in mat4 - (V3.x r) (V3.y r) (V3.z r) (t `V3.dot` r) - (V3.x u) (V3.y u) (V3.z u) (t `V3.dot` u) - (V3.x f) (V3.y f) (V3.z f) (t `V3.dot` f) - 0 0 0 1 +pltInverse = M4.inverseTransform . pltTransform diff --git a/Spear/Scene/GameObject.hs b/Spear/Scene/GameObject.hs index e012122..f29ee09 100644 --- a/Spear/Scene/GameObject.hs +++ b/Spear/Scene/GameObject.hs @@ -3,14 +3,16 @@ module Spear.Scene.GameObject GameObject , GameStyle(..) , AM.AnimationSpeed +, Rotation(..) -- * Construction , goNew -- * Accessors -, renderer , currentAnimation -, numCollisioners , goAABB , goAABBs +, go3Dtransform +, numCollisioners +, renderer -- * Manipulation , goUpdate , setAnimation @@ -39,6 +41,7 @@ import qualified Spear.Render.AnimatedModel as AM import Spear.Render.Program import Spear.Render.StaticModel as SM +import Data.Fixed (mod') import Data.List (foldl') @@ -51,9 +54,11 @@ data GameStyle -- | An object in the game scene. data GameObject = GameObject { gameStyle :: !GameStyle + , rotation :: !Rotation , renderer :: !(Either StaticModelRenderer AM.AnimatedModelRenderer) , collisioners :: ![Collisioner] , transform :: !M3.Matrix3 + , angle :: Float } @@ -96,7 +101,11 @@ instance S2.Spatial2 GameObject where , transform = M3.translv v * m } - rotate angle go = go { transform = transform go * M3.rot angle } + rotate a go = + go + { transform = transform go * M3.rot a + , angle = (angle go + a) `mod'` 360 + } pos go = M3.position . transform $ go @@ -113,20 +122,31 @@ instance S2.Spatial2 GameObject where setPos pos go = let m = transform go in go { transform = M3.transform (M3.right m) (M3.forward m) pos } + + lookAt p go = + let position = S2.pos go + fwd = V2.normalise $ p - position + r = perp fwd + in + go + { transform = M3.transform r fwd position + , angle = acos $ r `V2.dot` V2.unitx + } -- | Create a new game object. goNew :: GameStyle + -> Rotation -> Either StaticModelResource AM.AnimatedModelResource -> [Collisioner] -> M3.Matrix3 -> GameObject -goNew style (Left smr) cols transf = - GameObject style (Left $ SM.staticModelRenderer smr) cols transf +goNew style rtype (Left smr) cols transf = + GameObject style rtype (Left $ SM.staticModelRenderer smr) cols transf 0 -goNew style (Right amr) cols transf = - GameObject style (Right $ AM.animatedModelRenderer 1 amr) cols transf +goNew style rtype (Right amr) cols transf = + GameObject style rtype (Right $ AM.animatedModelRenderer 1 amr) cols transf 0 goUpdate :: Float -> GameObject -> GameObject @@ -190,6 +210,11 @@ goAABBs :: GameObject -> [AABB] goAABBs = fmap goAABB' . collisioners +-- | Get the game object's 3D transform. +go3Dtransform :: GameObject -> M4.Matrix4 +go3Dtransform go = rpgTransform 0 (angle go) (rotation go) . S2.transform $ go + + -- | Render the game object. goRender :: StaticProgram -> AnimatedProgram -> Cam.Camera -> GameObject -> IO () goRender sprog aprog cam go = @@ -197,9 +222,13 @@ goRender sprog aprog cam go = apu = animatedProgramUniforms aprog mat = S2.transform go style = gameStyle go + rtype = rotation go + a = angle go in case renderer go of - Left smr -> goRender' style sprog spu mat cam (SM.bind spu smr) (SM.render spu smr) - Right amr -> goRender' style aprog apu mat cam (AM.bind apu amr) (AM.render apu amr) + Left smr -> + goRender' style a rtype sprog spu mat cam (SM.bind spu smr) (SM.render spu smr) + Right amr -> + goRender' style a rtype aprog apu mat cam (AM.bind apu amr) (AM.render apu amr) type Bind = IO () @@ -209,6 +238,8 @@ type Render = IO () goRender' :: (ProgramUniforms u, Program p) => GameStyle + -> Float + -> Rotation -> p -> u -> M3.Matrix3 @@ -216,10 +247,10 @@ goRender' :: (ProgramUniforms u, Program p) -> Bind -> Render -> IO () -goRender' style prog uniforms model cam bindRenderer render = +goRender' style a rtype prog uniforms model cam bindRenderer render = let view = M4.inverseTransform $ Cam.transform cam modelview = case style of - RPG -> view * rpgTransform 0 model + RPG -> view * rpgTransform 0 a rtype model PLT -> view * pltTransform model normalmat = fastNormalMatrix modelview in do diff --git a/Spear/Scene/Loader.hs b/Spear/Scene/Loader.hs index e3b9546..ea08385 100644 --- a/Spear/Scene/Loader.hs +++ b/Spear/Scene/Loader.hs @@ -31,7 +31,7 @@ import Spear.Render.AnimatedModel as AM import Spear.Render.Material import Spear.Render.Program import Spear.Render.StaticModel as SM -import Spear.Scene.GameObject as GO +import qualified Spear.Scene.GameObject as GO import Spear.Scene.Graph import Spear.Scene.Light import Spear.Scene.SceneResources @@ -342,17 +342,18 @@ newLight _ = return () -- Object Loading -- -------------------- -loadGO :: GameStyle -> SceneResources -> [Property] -> Matrix3 -> Setup GameObject +loadGO :: GO.GameStyle -> SceneResources -> [Property] -> Matrix3 -> Setup GO.GameObject loadGO style sceneRes props transf = do - modelName <- asString . mandatory "model" $ props + modelName <- asString . mandatory "model" $ props + rtype <- asGORotation . mandatory "rotation" $ props let animSpeed = asFloat . value "animation-speed" $ props go <- case getAnimatedModel sceneRes modelName of Just model -> - return $ goNew style (Right model) [] transf + return $ GO.goNew style rtype (Right model) [] transf Nothing -> case getStaticModel sceneRes modelName of Just model -> - return $ goNew style (Left model) [] transf + return $ GO.goNew style rtype (Left model) [] transf Nothing -> setupError $ "model " ++ modelName ++ " not found" return $ case animSpeed of @@ -466,6 +467,13 @@ asRotation val = fmap parseRotation val where parseRotation (ax:ay:az:order:_) = Rotation (read ax) (read ay) (read az) (readOrder order) +asGORotation :: Functor f => f [String] -> f GO.Rotation +asGORotation val = fmap parseRotation val + where parseRotation ["yaw"] = GO.Yaw + parseRotation ["pitch"] = GO.Pitch + parseRotation ["roll"] = GO.Roll + + data Rotation = Rotation { ax :: Float , ay :: Float -- cgit v1.2.3