From 56540ac3ca48cc77da0e86389fe550ede1c1081e Mon Sep 17 00:00:00 2001
From: Marc Sunet <jeannekamikaze@gmail.com>
Date: Sat, 8 Sep 2012 12:39:14 +0200
Subject: Fixed 3d rotation; added rpgUnproject

---
 Spear.lkshs               | 12 ++++----
 Spear.lkshw               |  4 +--
 Spear/Math/MatrixUtils.hs | 48 +++++++++++++++++++++++++++++++-
 Spear/Scene/GameObject.hs | 70 +++++++++++++++++++++++++++++++++--------------
 4 files changed, 104 insertions(+), 30 deletions(-)

diff --git a/Spear.lkshs b/Spear.lkshs
index 3af403b..2c83290 100644
--- a/Spear.lkshs
+++ b/Spear.lkshs
@@ -1,18 +1,18 @@
 Version of session file format:
                1
 Time of storage:
-               "Fri Sep  7 14:26:09 CEST 2012"
-Layout:        VerticalP (TerminalP {paneGroups = fromList [], paneTabs = Just TopP, currentPage = 6, 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}) 350) 153),("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}) 239)], paneTabs = Just BottomP, currentPage = 0, detachedId = Nothing, detachedSize = Nothing}) (TerminalP {paneGroups = fromList [], paneTabs = Nothing, currentPage = 0, detachedId = Nothing, detachedSize = Nothing}) 640) 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" 0)),[SplitP LeftP]),(Just (FilesSt FilesState),[SplitP RightP,SplitP TopP]),(Just (BufferSt (BufferState "/home/jeanne/programming/haskell/Spear/Spear/Scene/GameObject.hs" 46)),[SplitP LeftP]),(Just (BufferSt (BufferState "/home/jeanne/programming/haskell/Spear/demos/simple-scene/Game/GameObject.hs" 39)),[SplitP LeftP]),(Just (BufferSt (BufferState "/home/jeanne/programming/haskell/Spear/demos/simple-scene/Game/GameState.hs" 1011)),[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/Math/MatrixUtils.hs" 616)),[SplitP LeftP]),(Just (ModulesSt (ModulesState 328 (PackageScope False,False) (Nothing,Nothing) (ExpanderState {packageExp = ([],[]), packageExpNoBlack = ([],[]), 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" 611)),[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 (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" 4966)),[SplitP LeftP])]
+               "Sat Sep  8 12:03:07 CEST 2012"
+Layout:        VerticalP (TerminalP {paneGroups = fromList [], paneTabs = Just TopP, currentPage = 0, 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}) 335) 148),("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}) 234)], paneTabs = Just BottomP, currentPage = 0, detachedId = Nothing, detachedSize = Nothing}) (TerminalP {paneGroups = fromList [], paneTabs = Nothing, currentPage = 0, detachedId = Nothing, detachedSize = Nothing}) 621) 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" 1138)),[SplitP LeftP]),(Just (FilesSt FilesState),[SplitP RightP,SplitP TopP]),(Just (BufferSt (BufferState "/home/jeanne/programming/haskell/Spear/Spear/Scene/GameObject.hs" 3926)),[SplitP LeftP]),(Just (BufferSt (BufferState "/home/jeanne/programming/haskell/Spear/demos/simple-scene/Game/GameObject.hs" 39)),[SplitP LeftP]),(Just (BufferSt (BufferState "/home/jeanne/programming/haskell/Spear/demos/simple-scene/Game/GameState.hs" 1011)),[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/Math/MatrixUtils.hs" 2151)),[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" 2818)),[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 (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" 4399)),[SplitP LeftP])]
 Window size:   (1820,944)
 Completion size:
                (750,399)
 Workspace:     Just "/home/jeanne/programming/haskell/Spear/Spear.lkshw"
-Active pane:   Just "main.hs"
+Active pane:   Just "Factory.hs"
 Toolbar visible:
                True
-FindbarState:  (False,FindState {entryStr = "Sphere", entryHist = ["Sphere","boxFrom","sphere","asdad","m[15]","m[14]","m[1]","m[13]","m[12]","m[11]","m[10]","m[9]"], replaceStr = "a01", replaceHist = [], caseSensitive = True, entireWord = False, wrapAround = False, regex = False, lineNr = 1})
+FindbarState:  (False,FindState {entryStr = "asdads", entryHist = ["asdads","let scene","scene","Sphere","boxFrom","sphere","m[15]","m[14]","m[1]","m[13]","m[12]","m[11]"], replaceStr = "a01", replaceHist = [], caseSensitive = True, entireWord = False, wrapAround = False, regex = False, lineNr = 1})
 Recently opened files:
-               ["/home/jeanne/programming/haskell/Spear/demos/simple-scene/Game/GameObject/AnimatedGO.hs","/home/jeanne/programming/haskell/Spear/demos/simple-scene/Game/GameObject/StaticGO.hs","/home/jeanne/programming/haskell/Spear/Spear/Math/Vector2.hs","/home/jeanne/programming/haskell/Spear/Spear/Render/StaticModel.hs","/home/jeanne/programming/haskell/Spear/Spear/Render/AnimatedModel.hs","/home/jeanne/programming/haskell/Spear/Spear/Collision.hs","/home/jeanne/programming/haskell/Spear/demos/simple-scene/main.hs","/home/jeanne/programming/haskell/Spear/Spear/Math/Camera.hs","/home/jeanne/programming/haskell/Spear/demos/simple-scene/Game/GameObject/Factory.hs","/home/jeanne/programming/haskell/Spear/Spear/Math/Matrix3.hs","/home/jeanne/programming/haskell/Spear/demos/simple-scene/Game/Program/Box.hs","/home/jeanne/programming/haskell/Spear/demos/simple-scene/Game/Program/Line.hs"]
+               ["/home/jeanne/programming/haskell/Spear/demos/simple-scene/Game/GameObject.hs","/home/jeanne/programming/haskell/Spear/Spear/Math/Matrix4.hs","/home/jeanne/programming/haskell/Spear/demos/simple-scene/Game/GameObject/AnimatedGO.hs","/home/jeanne/programming/haskell/Spear/demos/simple-scene/Game/GameObject/StaticGO.hs","/home/jeanne/programming/haskell/Spear/Spear/Math/Vector2.hs","/home/jeanne/programming/haskell/Spear/Spear/Render/StaticModel.hs","/home/jeanne/programming/haskell/Spear/Spear/Render/AnimatedModel.hs","/home/jeanne/programming/haskell/Spear/Spear/Collision.hs","/home/jeanne/programming/haskell/Spear/demos/simple-scene/main.hs","/home/jeanne/programming/haskell/Spear/Spear/Math/Camera.hs","/home/jeanne/programming/haskell/Spear/demos/simple-scene/Game/GameObject/Factory.hs","/home/jeanne/programming/haskell/Spear/Spear/Math/Matrix3.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 cc2deed..b2416a3 100644
--- a/Spear.lkshw
+++ b/Spear.lkshw
@@ -1,10 +1,10 @@
 Version of workspace file format:
                1
 Time of storage:
-               "Fri Sep  7 17:11:58 CEST 2012"
+               "Sat Sep  8 12:34:51 CEST 2012"
 Name of the workspace:
                "Spear"
 File paths of contained packages:
                ["demos/simple-scene/simple-scene.cabal","Spear.cabal"]
 Maybe file path of an active package:
-               Just "demos/simple-scene/simple-scene.cabal"
\ No newline at end of file
+               Just "Spear.cabal"
\ No newline at end of file
diff --git a/Spear/Math/MatrixUtils.hs b/Spear/Math/MatrixUtils.hs
index 354c840..629b73c 100644
--- a/Spear/Math/MatrixUtils.hs
+++ b/Spear/Math/MatrixUtils.hs
@@ -1,6 +1,8 @@
 module Spear.Math.MatrixUtils
 (
     fastNormalMatrix
+,   unproject
+,   rpgUnproject
 ,   rpgTransform
 ,   pltTransform
 ,   rpgInverse
@@ -27,6 +29,50 @@ fastNormalMatrix m =
         (M4.m02 m') (M4.m12 m') (M4.m22 m')
 
 
+-- | Transform the given point in window coordinates to object coordinates.
+unproject :: Matrix4 -- ^ Inverse projection matrix
+          -> Matrix4 -- ^ Inverse modelview matrix.
+          -> Float   -- ^ Viewport x
+          -> Float   -- ^ Viewport y
+          -> Float   -- ^ Viewport width
+          -> Float   -- ^ Viewport height
+          -> Float   -- ^ Window x
+          -> Float   -- ^ Window y
+          -> Float   -- ^ Window z
+          -> V3.Vector3
+unproject projI modelviewI vpx vpy w h x y z =
+    let
+        xmouse = 2*(x-vpx)/w - 1
+        ymouse = 2*(y-vpy)/h - 1
+        zmouse = 2*z - 1
+    in
+        (modelviewI * projI) `M4.mulp` V3.vec3 xmouse ymouse zmouse
+
+
+-- | Transform the given point in window coordinates to 2d coordinates.
+--
+-- The line defined by the given point in window space is intersected with
+-- the XZ plane in world space to yield the resulting 2d point.
+rpgUnproject
+    :: Matrix4 -- ^ Inverse projection matrix
+    -> Matrix4 -- ^ Inverse viewI matrix.
+    -> Float   -- ^ Viewport x
+    -> Float   -- ^ Viewport y
+    -> Float   -- ^ Viewport width
+    -> Float   -- ^ Viewport height
+    -> Float   -- ^ Window x
+    -> Float   -- ^ Window y
+    -> Vector2
+rpgUnproject projI viewI vpx vpy w h x y =
+    let
+        p1 = unproject projI viewI vpx vpy w h x y 0
+        p2 = unproject projI viewI vpx vpy w h x y (-1)
+        lambda = (V3.y p1 / (V3.y p1 - V3.y p2))
+        p' = p1 + V3.scale lambda (p2 - p1)
+    in
+        vec2 (V3.x p') (-V3.z p')
+
+
 -- | Map an object's transform in view space to world space.
 rpgTransform
     :: Float   -- ^ The height above the ground
@@ -37,7 +83,7 @@ rpgTransform
     -> Matrix4
 rpgTransform h a axis pos viewI =
     let p1 = viewI `M4.mulp` (vec3 (V2.x pos) (V2.y pos) 0)
-        p2 = viewI `M4.mulp` (vec3 (V2.x pos) (V2.y pos) (-100))
+        p2 = viewI `M4.mulp` (vec3 (V2.x pos) (V2.y pos) (-1))
         lambda  = (V3.y p1 / (V3.y p1 - V3.y p2))
         p  = p1 + V3.scale lambda (p2 - p1)
         mat' = axisAngle axis a
diff --git a/Spear/Scene/GameObject.hs b/Spear/Scene/GameObject.hs
index 53a03b5..a43a2a4 100644
--- a/Spear/Scene/GameObject.hs
+++ b/Spear/Scene/GameObject.hs
@@ -2,6 +2,7 @@ module Spear.Scene.GameObject
 (
     GameObject
 ,   GameStyle(..)
+,   Window(..)
 ,   AM.AnimationSpeed
     -- * Construction
 ,   goNew
@@ -13,6 +14,7 @@ module Spear.Scene.GameObject
 ,   goRPGtransform
 ,   numCollisioners
 ,   renderer
+,   window
     -- * Manipulation
 ,   goUpdate
 ,   setAnimation
@@ -20,7 +22,7 @@ module Spear.Scene.GameObject
 ,   setAxis
 ,   withCollisioners
 ,   setCollisioners
-,   setViewInverse
+,   setWindow
     -- * Rendering
 ,   goRender
     -- * Collision
@@ -53,15 +55,28 @@ data GameStyle
     | PLT -- ^ Platformer or space invaders style game.
 
 
+data Window = Window
+    { projInv :: !M4.Matrix4
+    , viewInv :: !M4.Matrix4
+    , vpx     :: !Float
+    , vpy     :: !Float
+    , width   :: !Float
+    , height  :: !Float
+    }
+
+
+dummyWindow = Window M4.id M4.id 0 0 640 480
+
+
 -- | An object in the game scene.
 data GameObject = GameObject
     { gameStyle    :: !GameStyle
     , renderer     :: !(Either StaticModelRenderer AM.AnimatedModelRenderer)
     , collisioners :: ![Collisioner]
     , transform    :: !M3.Matrix3
-    , axis         :: Vector3
-    , angle        :: Float
-    , viewInv      :: !M4.Matrix4
+    , axis         :: !Vector3
+    , angle        :: !Float
+    , window       :: !Window -- ^ Get the game object's window.
     }
 
 
@@ -133,17 +148,28 @@ instance S2.Spatial2 GameObject where
         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
-            toDeg     = (*(180/pi))
+        let position = S2.pos go
+            fwd      = V2.normalise $ p - position
+            r        = perp fwd
+            toDeg = (*(180/pi))
+            wnd = window go
+            viewI = viewInv wnd
+            vpx'  = vpx wnd
+            vpy'  = vpy wnd
+            w     = width wnd
+            h     = height wnd
+            p1'   = position
+            p2'   = position + fwd
+            p1    = rpgUnproject M4.id viewI vpx' vpy' w h (V2.x p1') (V2.y p1')
+            p2    = rpgUnproject M4.id viewI vpx' vpy' w h (V2.x p2') (V2.y p2')
+            f     = V2.normalise $ p2 - p1
         in
             go
             { transform = M3.transform r fwd position
-            , angle = (-180) +
-                if V2.y r > 0
-                then toDeg . acos $ r `V2.dot` V2.unitx
-                else (+180) . toDeg . acos $ r `V2.dot` (-V2.unitx)
+            , angle = 180 -
+                if V2.x f > 0
+                then toDeg . acos $ f `V2.dot` V2.unity
+                else (+180) . toDeg . acos $ f `V2.dot` (-V2.unity)
             }
 
 
@@ -155,11 +181,11 @@ goNew :: GameStyle
       -> Vector3 -- ^ Axis of rotation
       -> GameObject
 
-goNew style (Left smr) cols transf axis =
-    GameObject style (Left $ SM.staticModelRenderer smr) cols transf axis 0 M4.id
+goNew style (Left smr) cols transf axis = GameObject
+    style (Left $ SM.staticModelRenderer smr) cols transf axis 0 dummyWindow
 
-goNew style (Right amr) cols transf axis =
-    GameObject style (Right $ AM.animatedModelRenderer 1 amr) cols transf axis 0 M4.id
+goNew style (Right amr) cols transf axis = GameObject
+    style (Right $ AM.animatedModelRenderer 1 amr) cols transf axis 0 dummyWindow
 
 
 goUpdate :: Float -> GameObject -> GameObject
@@ -185,7 +211,9 @@ goAABBs = fmap getAABB . collisioners
 
 -- | Get the game object's 3D transform.
 goRPGtransform :: GameObject -> M4.Matrix4
-goRPGtransform go = rpgTransform 0 (angle go) (axis go) (S2.pos go) (viewInv go)
+goRPGtransform go =
+    let viewI = viewInv . window $ go
+    in rpgTransform 0 (angle go) (axis go) (S2.pos go) viewI
 
 
 -- | Get the game object's current animation.
@@ -224,9 +252,9 @@ setCollisioners :: GameObject -> [Collisioner] -> GameObject
 setCollisioners go cols = go { collisioners = cols }
 
 
--- | Set the game object's view inverse matrix.
-setViewInverse :: M4.Matrix4 -> GameObject -> GameObject
-setViewInverse mat go = go { viewInv = mat }
+-- | Set the game object's window.
+setWindow :: Window -> GameObject -> GameObject
+setWindow wnd go = go { window = wnd }
 
 
 -- | Manipulate the game object's collisioners.
@@ -242,7 +270,7 @@ goRender sprog aprog cam go =
         style  = gameStyle go
         axis'  = axis go
         a      = angle go
-        viewI  = viewInv go
+        viewI  = viewInv . window $ go
         proj   = Cam.projection cam
         view   = M4.inverseTransform $ Cam.transform cam
         transf = S2.transform go
-- 
cgit v1.2.3