{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE UndecidableInstances #-} module Apecs.Physics.Types where import Apecs import Apecs.Core import Data.Bits import Data.Char (intToDigit) import qualified Data.IntMap as M import qualified Data.IntSet as S import Data.IORef import qualified Data.Map as Map import Data.Monoid ((<>)) import qualified Foreign.C.Types as C import Foreign.ForeignPtr import Foreign.Ptr import Language.C.Inline import Language.C.Inline.Context import qualified Language.C.Types as C import qualified Language.Haskell.TH as TH import Linear.V2 import Numeric (showIntAtBase) phycsCtx :: Context phycsCtx = baseCtx <> funCtx <> ctx where ctx = mempty { ctxTypesTable = phycsTypesTable } phycsTypesTable :: Map.Map C.TypeSpecifier TH.TypeQ phycsTypesTable = Map.fromList [ (C.TypeName "cpArbiter", [t| Collision |]) , (C.TypeName "cpBody", [t| Body |]) , (C.TypeName "cpCollisionHandler", [t| CollisionHandler |]) , (C.TypeName "cpConstraint", [t| Constraint |]) , (C.TypeName "cpDataPointer", [t| C.CUInt |]) , (C.TypeName "cpShape", [t| Shape |]) , (C.TypeName "cpPointQueryInfo", [t| PointQueryResult |]) , (C.TypeName "cpVect", [t| V2 C.CDouble |]) , (C.TypeName "cpSpace", [t| FrnSpace |]) ] -- | Uninhabited, should be added to the world as a component to add a physics space. data Physics -- | Vector type used by the library type Vec = V2 Double -- | Type synonym indicating that a vector is expected to be in body-space coordinates type BVec = Vec -- | Type synonym indicating that a vector is expected to be in world-space coordinates type WVec = Vec -- | Added to a component to add it to the physics space. -- Deleting it will also delete all associated shapes and constraints. -- A body has a number of subcomponents: @Position@, @Velocity@, @Force@, @Torque@, @BodyMass@, @Moment@, @Angle@, @AngularVelocity@, and @CenterOfGravity@. -- These components cannot be added or removed from an entity, but rather are present as long as the entity has a @Body@. data Body = DynamicBody | KinematicBody | StaticBody deriving (Eq, Ord, Enum) newtype Position = Position WVec newtype Velocity = Velocity WVec newtype Force = Force Vec newtype Torque = Torque Double newtype BodyMass = BodyMass Double deriving (Eq, Show) newtype Moment = Moment Double deriving (Eq, Show) newtype Angle = Angle Double deriving (Eq, Show) newtype AngularVelocity = AngularVelocity Double newtype CenterOfGravity = CenterOfGravity BVec -- | The @Shape@s belonging to a body. Read-only. newtype ShapeList = ShapeList [Entity] -- | The @Constraint@s belonging to a body. Read-only. newtype ConstraintList = ConstraintList [Entity] -- | Shape component. -- Adding a shape to an entity that has no @Body@ is a noop. data Shape = Shape Entity Convex -- | A convex polygon. -- Consists of a list of vertices, and a radius. data Convex = Convex [BVec] Double deriving (Eq, Show) newtype Sensor = Sensor Bool deriving (Eq, Show) newtype Elasticity = Elasticity Double deriving (Eq, Show) newtype Mass = Mass Double deriving (Eq, Show) newtype Density = Density Double deriving (Eq, Show) newtype Friction = Friction Double deriving (Eq, Show) newtype SurfaceVelocity = SurfaceVelocity Vec deriving (Eq, Show) newtype CollisionType = CollisionType C.CUIntPtr deriving (Eq, Show) type CollisionGroup = CUInt data CollisionFilter = CollisionFilter { filterGroup :: CollisionGroup , filterCategories :: Bitmask , filterMask :: Bitmask } deriving (Eq, Show) -- | A bitmask used for collision handling newtype Bitmask = Bitmask CUInt deriving (Eq, Bits) instance Show Bitmask where show (Bitmask mask) = "Bitmask 0b" ++ showIntAtBase 2 intToDigit mask "" data FrnSpace data FrnVec data Space c = Space { spBodies :: IOMap BodyRecord , spShapes :: IOMap (Record Shape) , spConstraints :: IOMap (Record Constraint) , spHandlers :: IOMap (Record CollisionHandler) , spacePtr :: SpacePtr } type instance Elem (Space a) = a data BodyRecord = BodyRecord { brPtr :: Ptr Body , brBody :: Body , brShapes :: IORef S.IntSet , brConstraints :: IORef S.IntSet } data Record a = Record { recPtr :: Ptr a , recVal :: a } type IOMap a = IORef (M.IntMap a) type PtrMap a = IOMap (Ptr a) type SpacePtr = ForeignPtr FrnSpace -- | Number of iterations per step, global value newtype Iterations = Iterations Int deriving (Eq, Show) -- | Gravity force vector, global value newtype Gravity = Gravity Vec deriving (Eq, Show) -- | Daming factor, global value newtype Damping = Damping Double deriving (Eq, Show) -- | Speed threshold to be considered idle, and a candidate for being put to sleep. Global value newtype IdleSpeedThreshold = IdleSpeedThreshold Double deriving (Eq, Show) -- | Sleep idle time threshold, global value newtype SleepIdleTime = SleepIdleTime Double deriving (Eq, Show) -- | Collision parameter, global value newtype CollisionSlop = CollisionSlop Double deriving (Eq, Show) -- | Collision parameter, global value newtype CollisionBias = CollisionBias Double deriving (Eq, Show) cast :: Space a -> Space b cast (Space b s c h w) = Space b s c h w -- Constraint subcomponents newtype MaxForce = MaxForce Double deriving (Eq, Show) newtype MaxBias = MaxBias Double deriving (Eq, Show) newtype ErrorBias = ErrorBias Double deriving (Eq, Show) newtype CollideBodies = CollideBodies Bool deriving (Eq, Show) data Constraint = Constraint Entity Entity ConstraintType deriving (Eq, Show) data ConstraintType = PinJoint BVec BVec -- ^ Maintains a fixed distance between two anchor points | SlideJoint BVec BVec Double Double -- ^ A @PinJoint@ with minimum and maximum distance | PivotJoint WVec -- ^ Creates a pivot point at the given world coordinate | PivotJoint2 BVec BVec -- ^ Creates a pivot point at the given body coordinates | GrooveJoint BVec BVec BVec -- ^ The first two vectors are the start and end of the groove on body A, the third argument is the anchor point on body B. | DampedSpring BVec BVec Double Double Double -- ^ Spring between two anchor points, with given rest length, stiffness, and damping. | DampedRotarySpring Double Double Double -- ^ Rotary sping, with given rest angle, stiffness, and damping. | RotaryLimitJoint Double Double -- ^ Joint with minimum and maximum angle | RatchetJoint Double Double -- ^ Rathet joint with given phase and ratchet (distance between clicks). | GearJoint Double Double -- Keeps angular velocity ratio constant. The first argument is phase, the initial offset, the second argument is the ratio | SimpleMotor Double -- ^ Keeps relative angular velocity constant deriving (Eq, Show) -- TODO -- getConstraintImpulse -- getPinJointDistance -- getSlideJointDistance? newtype BeginCB = BeginCB BeginFunc newtype SeparateCB = SeparateCB SeparateFunc newtype PreSolveCB = PreSolveCB PreSolveFunc newtype PostSolveCB = PostSolveCB PostSolveFunc -- Collision, Space, Handler data pointer type BeginFunc = Ptr Collision -> Ptr FrnSpace -> C.CUInt -> IO C.CUChar type SeparateFunc = Ptr Collision -> Ptr FrnSpace -> C.CUInt -> IO () type PreSolveFunc = Ptr Collision -> Ptr FrnSpace -> C.CUInt -> IO C.CUChar type PostSolveFunc = Ptr Collision -> Ptr FrnSpace -> C.CUInt -> IO () data CollisionHandler = CollisionHandler { source :: CollisionSource , beginCB :: Maybe BeginCB , separateCB :: Maybe SeparateCB , preSolveCB :: Maybe PreSolveCB , postSolveCB :: Maybe PostSolveCB } data CollisionSource = Wildcard CollisionGroup | Between CollisionGroup CollisionGroup -- Corresponds to an 'arbiter' in Chipmunk data Collision = Collision { collisionNormal :: Vec , collisionA :: Entity , collisionB :: Entity } deriving (Eq, Show) data CollisionProperties = CollisionProperties { collisionElasticity :: Double , collisionFriction :: Double , collisionSurfaceVelocity :: Vec } deriving (Eq, Show) data SegmentQueryResult = SegmentQueryResult { sqShape :: Entity , sqImpactPoint :: Vec , sqImpactNormal :: Vec , sqImpactAlpha :: Double } deriving (Eq, Show) data PointQueryResult = PointQueryResult { pqShape :: Entity , pqPoint :: WVec , pqDistance :: Double , pqGradient :: Double } deriving (Eq, Show)