diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index 4000e68..0000000 --- a/.gitattributes +++ /dev/null @@ -1 +0,0 @@ -apecs-physics/Chipmunk2D/* linguist-vendored diff --git a/.stylish-haskell.yaml b/.stylish-haskell.yaml deleted file mode 100644 index 3148122..0000000 --- a/.stylish-haskell.yaml +++ /dev/null @@ -1,6 +0,0 @@ -steps: - - simple_align: {} - - imports: - align: global - - language_pragmas: {} - - trailing_whitespace: {} diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index b327727..0000000 --- a/.travis.yml +++ /dev/null @@ -1,153 +0,0 @@ -sudo: false -language: generic -cache: - directories: - - $HOME/.ghc - - $HOME/.cabal - - $HOME/.stack - - $TRAVIS_BUILD_DIR/.stack-work - -matrix: - include: - - - env: BUILD=cabal GHCVER=8.4.3 CABALVER=2.2 HAPPYVER=1.19.5 ALEXVER=3.1.7 - compiler: ": #GHC 8.4.4" - addons: {apt: {packages: [cabal-install-2.2,ghc-8.4.3,happy-1.19.5,alex-3.1.7,freeglut3-dev], sources: [hvr-ghc]}} - - - env: BUILD=cabal GHCVER=8.6.3 CABALVER=2.4 HAPPYVER=1.19.5 ALEXVER=3.1.7 - compiler: ": #GHC 8.6.5" - addons: {apt: {packages: [cabal-install-2.4,ghc-8.6.3,happy-1.19.5,alex-3.1.7,freeglut3-dev], sources: [hvr-ghc]}} - - - env: BUILD=cabal GHCVER=8.6.3 CABALVER=2.4 HAPPYVER=1.19.5 ALEXVER=3.1.7 - compiler: ": #GHC 8.8.1" - addons: {apt: {packages: [cabal-install-2.4,ghc-8.6.3,happy-1.19.5,alex-3.1.7,freeglut3-dev], sources: [hvr-ghc]}} - - # Allowed failure - - env: BUILD=cabal GHCVER=head CABALVER=head HAPPYVER=1.19.5 ALEXVER=3.1.7 - compiler: ": #GHC HEAD" - addons: {apt: {packages: [cabal-install-head,ghc-head,happy-1.19.5,alex-3.1.7,freeglut3-dev], sources: [hvr-ghc]}} - -### Stack ### - - env: BUILD=stack ARGS="" - compiler: ": #stack default" - addons: {apt: {packages: [libgmp-dev,freeglut3-dev]}} - - - env: BUILD=stack ARGS="--resolver lts-13" - compiler: ": #stack 8.6.5" - addons: {apt: {packages: [libgmp-dev,freeglut3-dev]}} - - - env: BUILD=stack ARGS="--resolver lts-14" - compiler: ": #stack 8.6.5" - addons: {apt: {packages: [libgmp-dev,freeglut3-dev]}} - - # Allowed failure - - env: BUILD=stack ARGS="--resolver nightly" - compiler: ": #stack nightly" - addons: {apt: {packages: [libgmp-dev,freeglut3-dev]}} - - - env: BUILD=stack ARGS="" - compiler: ": #stack default osx" - os: osx - - - env: BUILD=stack ARGS="--resolver lts-13" - compiler: ": #stack 8.6.5 osx" - os: osx - - - env: BUILD=stack ARGS="--resolver lts-14" - compiler: ": #stack 8.6.5 osx" - os: osx - - # Allowed failure - - env: BUILD=stack ARGS="--resolver nightly" - compiler: ": #stack nightly osx" - os: osx - - allow_failures: - - env: BUILD=cabal GHCVER=head CABALVER=head HAPPYVER=1.19.5 ALEXVER=3.1.7 - - env: BUILD=stack ARGS="--resolver nightly" - -before_install: -# Using compiler above sets CC to an invalid value, so unset it -- unset CC - -# Download and unpack the stack executable -- export PATH=/opt/ghc/$GHCVER/bin:/opt/cabal/$CABALVER/bin:$HOME/.local/bin:/opt/alex/$ALEXVER/bin:/opt/happy/$HAPPYVER/bin:$HOME/.cabal/bin:$PATH -- mkdir -p ~/.local/bin -- | - if [ `uname` = "Darwin" ] - then - travis_retry curl --insecure -L https://get.haskellstack.org/stable/osx-x86_64.tar.gz | tar xz --strip-components=1 --include '*/stack' -C ~/.local/bin - else - travis_retry curl -L https://get.haskellstack.org/stable/linux-x86_64.tar.gz | tar xz --wildcards --strip-components=1 -C ~/.local/bin '*/stack' - fi - - # Use the more reliable S3 mirror of Hackage - mkdir -p $HOME/.cabal - echo 'remote-repo: hackage.haskell.org:http://hackage.fpcomplete.com/' > $HOME/.cabal/config - echo 'remote-repo-cache: $HOME/.cabal/packages' >> $HOME/.cabal/config - - -install: -- echo "$(ghc --version) [$(ghc --print-project-git-commit-id 2> /dev/null || echo '?')]" -- stack --version -- if [ -f configure.ac ]; then autoreconf -i; fi -- | - set -ex - case "$BUILD" in - stack) - # Add in extra-deps for older snapshots, as necessary - stack --no-terminal --install-ghc $ARGS test --bench --dry-run || ( \ - stack --no-terminal $ARGS build cabal-install && \ - stack --no-terminal $ARGS solver --update-config) - - # Build the dependencies - stack --no-terminal --install-ghc $ARGS test --bench --only-dependencies - ;; - cabal) - cabal --version - travis_retry cabal update - - # Get the list of packages from the stack.yaml file. Note that - # this will also implicitly run hpack as necessary to generate - # the .cabal files needed by cabal-install. - PACKAGES=$(stack --install-ghc query locals | grep '^ *path' | sed 's@^ *path:@@') - echo $PACKAGES - - cabal install --only-dependencies --enable-tests --enable-benchmarks --force-reinstalls --ghc-options=-O0 --reorder-goals --max-backjumps=-1 $CABALARGS $PACKAGES - ;; - esac - set +ex - -script: -- | - set -ex - case "$BUILD" in - stack) - stack --no-terminal $ARGS test --bench --no-run-benchmarks --haddock --no-haddock-deps - ;; - cabal) - cabal install --enable-tests --enable-benchmarks --force-reinstalls --ghc-options=-O0 --reorder-goals --max-backjumps=-1 $CABALARGS $PACKAGES - - ORIGDIR=$(pwd) - for dir in $PACKAGES - do - cd $dir - cabal check - cabal sdist - PKGVER=$(cabal info . | awk '{print $2;exit}') - SRC_TGZ=$PKGVER.tar.gz - cd dist - tar zxfv "$SRC_TGZ" - cd "$PKGVER" - cabal configure --enable-tests --ghc-options -O0 - cabal build - if [ "$CABALVER" = "1.16" ] || [ "$CABALVER" = "1.18" ]; then - cabal test - else - cabal test --show-details=streaming --log=/dev/stdout - fi - cd $ORIGDIR - done - ;; - esac - set +ex diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..5fccd0a --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,7 @@ +# Changelog + +## 0 + +### [Added] +### [Removed] +### [Changed] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..44c548a --- /dev/null +++ b/LICENSE @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2017, Jonas Carpay +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md deleted file mode 120000 index d4b9ac8..0000000 --- a/README.md +++ /dev/null @@ -1 +0,0 @@ -apecs/README.md \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..e2f20f0 --- /dev/null +++ b/README.md @@ -0,0 +1,131 @@ +# apecs 1.0 + +This is an (experiment for) the next version of `apecs`. +It completely overhauls the internals, and some of the externals as well. +See below for more information about what, how, and why. + +## Todo + - [ ] Stores + - [X] Cache + - [ ] polymorphic in backing vector + I want this so I can have Storable vectors for things I want to send to the GPU + - [ ] Type error when the cached component does not + actually exist in the cached store + - [ ] Module structure + - [ ] Rename Focus + - [X] Benchmarks + - [ ] Profiling comparison + - [ ] Performance tweaks + - [ ] Stack support + - [ ] Redesign newEntity/EntityCounter? + - [ ] Make `newEntity` more general? + - [ ] Make `EntityCounter` less of a special case? + - [ ] Documentation/blog posts + - [ ] How to use + - [ ] How to go fast + - [ ] Internals + - [ ] Feature parity + - [ ] Stores + - [ ] Systems + - [ ] Some things might need to be redesigned + - [ ] Reactivity? + - [ ] Some things might no longer be needed + - [ ] Clean up Nix + - [ ] CI + - [ ] Haddocks + - [ ] Tests + - [ ] port secondary libraries + - [ ] apecs-gloss + - [ ] apecs-physics + - [ ] apecs-stm + - [ ] Fix version bounds + - [ ] Everything else + - [ ] Open questions + - [ ] Multi-component stores? + - [ ] What else? + - [ ] Custom type errors? + - [ ] No Generic or more specific instance + - [ ] `Components` undefined for World member + - [ ] probably others + - [ ] SystemT: Synonym or newtype? + +## More information + +This is the next version of apecs. +The reason I'm (tentatively) calling it 1.0 is that it is _so_ different from 0.9 that calling it 0.10 doesn't really do it justice. + +In short, the difference between this and 0.9 is that this version uses Generics where 0.9 used Template Haskell. +The surface level of the library mostly stays the same, but the implementation is a lot simpler. + +This came about while I was looking at `generic-lens`. +In the past, I tried writing apecs using Generics a number of times, but I was never able to really make it work out in a way I was happy with. +However, `generic-lens` showed me how to do many of the things I was struggling with, and that it can be just as fast as handwritten/TH code. + +It took a while to get to a point where it was an improvement over TH, now that it has, I think it is time to get feedback. + +### Changes +Example.hs contains a rewrite of the example in the apecs readme: + +```haskell +newtype Position = Position Float deriving (Eq, Show) + +newtype Velocity = Velocity Float deriving (Eq, Show) + +data World + = World + (Map Position) + (Map Velocity) + EntityCounter + deriving (Generic, Initialize IO) + +main :: IO () +main = do + (w :: World) <- initialize + flip runReaderT w $ do + newEntity (Position 0, Velocity 3) + newEntity (Position 1, Velocity 4) + newEntity (Position 2, Velocity 5) + cmap $ \(Position p, Velocity v) -> Position (p + v) + cmapM_ $ \(Position p) -> liftIO $ print p +``` + +Instead of providing component declarations and generating the world from them, you now write the world yourself. +As you can see in `main`, the mechanics of using apecs mostly stay the same, but defining components and the world is a lot more transparent. +This makes it so that you can manipulate the `World` directly if you want, like making copies. + +At its core, apecs is about 4 type classes; `Get`, `Set`, `Destroy`, and `Members`. +This separates the concerns of the front-end (providing a safe, high-level language) the backend (fast memory access). +Previously, there were a number of extra classes that were necessary to make this actually work out though, like `Has`, and the `Expl...` family. +Apecs 1.0 manages to do away with the noise however, and the core module truly only consists of the 4 core type classes. +All the heavy lifting is done by the instances. + +The most "magical" of these instances are those that allow a Generic world to inherit the instances of its constituent stores. +This is the part that was copied from/inspired by `generic-lens`. +There are open questions about this part of the design space. +For example, there is first-class support for stores that contain multiple components. +This is nice for `apecs-physics` and reactive stores, but it makes for particularly bad error messages. +We could write custom type errors, but we'll have to talk about whether it's worth the complexity. + +### Performance + +The performance numbers are similar to 0.9. +It appears that some things are slightly faster, and some things are slightly slower. +Profiling or more benchmarks would be nice, and there might be more gains to be made, but it's good to know we're still fast. + +### Ecstasy + +I've been asked before about the differences between apecs and ecstasy. +Ecstasy brands itself as apecs, but with Generics, so you'd think that now that apecs is moving towards Generics they're converging, right? + +In practice however, apecs and ecstasy are so different it's hard to even compare them. +They were already very different when ecstasy was first released, but apecs itself has evolved so much it looks nothing like it did back then. + +I can't tell you which of the two is right for you; my experience with ecstasy is limited to studying its source code, and I'm obviously at least a little bit biased. +That said, I have studied ECS systems a lot, and wrote an (unpublished) paper on them. +In my opinion, the fact that apecs has a small, simple core language puts its design above that of every other ECS system I know. +It also happens to be fast, and that makes for great marketing, but it's mostly just a consequence of having a solid core. +That obviously doesn't make it the best choice for everyone, though; for example, Entitas or DOTS are both embedded in Unity, and therefore part of an industry-standard game development ecosystem. + +Take that however you will. +It's great that there are multiple efforts in ECS development, especially when they're so different. +ECS systems are young, and any development is good development. diff --git a/apecs-gloss/CHANGELOG.md b/apecs-gloss/CHANGELOG.md deleted file mode 100644 index 4bed648..0000000 --- a/apecs-gloss/CHANGELOG.md +++ /dev/null @@ -1,20 +0,0 @@ -## [0.2.4] -### Changed -- Increased upper bound `apecs` dependency - -## [0.2.3] -### Changed -- Small compatibility update for apecs-physics-0.4.3 - -## [0.2.2] -### Changed -- Fixed the `windowToWorld` direction - -## [0.2.1] -### Changed -- Fixed links in cabal file - -## [0.2.0] -### Changed -- Switched from using `Double` to `Float`, for consistency with gloss. This makes interfacing with gloss easier but with apecs-physics harder. -- The cabal file now includes the changelog diff --git a/apecs-gloss/LICENSE b/apecs-gloss/LICENSE deleted file mode 100644 index 32cc34e..0000000 --- a/apecs-gloss/LICENSE +++ /dev/null @@ -1,30 +0,0 @@ -Copyright Jonas Carpay (c) 2017 - -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - - * Neither the name of Jonas Carpay nor the names of other - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/apecs-gloss/README.md b/apecs-gloss/README.md deleted file mode 100644 index d480cfa..0000000 --- a/apecs-gloss/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# apecs-gloss -[![Hackage](https://img.shields.io/hackage/v/apecs-gloss.svg)](https://hackage.haskell.org/package/apecs-gloss) - -Tools for simple [gloss](http://hackage.haskell.org/package/gloss)-based rendering. Used in most of the [examples](../examples). diff --git a/apecs-gloss/Setup.hs b/apecs-gloss/Setup.hs deleted file mode 100644 index 9a994af..0000000 --- a/apecs-gloss/Setup.hs +++ /dev/null @@ -1,2 +0,0 @@ -import Distribution.Simple -main = defaultMain diff --git a/apecs-gloss/apecs-gloss.cabal b/apecs-gloss/apecs-gloss.cabal deleted file mode 100644 index 27fd9bb..0000000 --- a/apecs-gloss/apecs-gloss.cabal +++ /dev/null @@ -1,37 +0,0 @@ -name: apecs-gloss -version: 0.2.4 -synopsis: Simple gloss renderer for apecs -homepage: https://github.com/jonascarpay/apecs/apecs-gloss -license: BSD3 -license-file: LICENSE -author: Jonas Carpay -maintainer: jonascarpay@gmail.com -copyright: MIT -category: Web -build-type: Simple -extra-source-files: README.md -cabal-version: >= 1.10 - -description: - Simple 2D gloss-based rendering for apecs. - Intended for prototyping. - -library - hs-source-dirs: - src - exposed-modules: - Apecs.Gloss - Apecs.Physics.Gloss - build-depends: - apecs >= 0.7 && < 0.10, - apecs-physics >= 0.3 && < 0.5, - base >= 4.9 && < 5, - containers >= 0.5 && < 0.8, - gloss >= 1.11 && < 2, - linear >= 1.20 && < 2 - default-language: - Haskell2010 - -source-repository head - type: git - location: https://github.com/jonascarpay/apecs diff --git a/apecs-gloss/src/Apecs/Gloss.hs b/apecs-gloss/src/Apecs/Gloss.hs deleted file mode 100644 index 0669076..0000000 --- a/apecs-gloss/src/Apecs/Gloss.hs +++ /dev/null @@ -1,67 +0,0 @@ -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE TypeFamilies #-} - -module Apecs.Gloss - ( module Apecs.Gloss - , module G - ) where - -import Data.Semigroup (Semigroup (..)) -import Graphics.Gloss.Interface.IO.Game as G -import Linear.V2 -import Linear.Vector - -import Apecs - -data Camera = Camera - { camOffset :: V2 Float - , camScale :: Float - } deriving (Eq, Show, Read) - -instance Semigroup Camera where - Camera x1 r1 <> Camera x2 r2 = Camera (x1 + x2) (r1 * r2) -instance Monoid Camera where - mempty = Camera 0 1 - mappend = (<>) - --- | Apply a camera transformation to a picture -cameraTransform :: Camera -> Picture -> Picture -cameraTransform (Camera (V2 cx cy) cs) = - Scale cs cs . Translate (-cx) (-cy) - -windowToWorld :: Camera -> (Float,Float) -> V2 Float -windowToWorld (Camera cx cs) (x,y) = V2 x y ^/ cs + cx - -instance Component Camera where - type Storage Camera = Global Camera - -play - :: (Has w IO Camera) - => Display -- ^ Display mode - -> Color -- ^ Background color - -> Int -- ^ Desired FPS - -> System w Picture -- ^ Drawing function - -> (Event -> System w ()) -- ^ Event handling function - -> (Float -> System w ()) -- ^ Stepping function, with a time delta argument. - -> System w () -play disp col fps draw handle step = do - w <- ask - liftIO$ playIO disp col fps w draw' handle' step' - where - handle' event = runSystem $ handle event >> ask - step' dT = runSystem $ step dT >> ask - draw' = runSystem $ do - cam <- get global - cameraTransform cam <$> draw - --- | Renders a picture given a component rendering function. -foldDraw :: (Get w IO c, Members w IO c) - => (c -> Picture) -- ^ Component render function. - -> System w Picture -foldDraw fn = cfold (\pic -> mappend pic . fn) mempty - --- | Monadically renders a picture given a component rendering function. -foldDrawM :: (Get w IO c, Members w IO c) - => (c -> System w Picture) -- ^ Component render function. - -> System w Picture -foldDrawM fn = cfoldM (\pic -> fmap (mappend pic) . fn) mempty diff --git a/apecs-gloss/src/Apecs/Physics/Gloss.hs b/apecs-gloss/src/Apecs/Physics/Gloss.hs deleted file mode 100644 index ba6e1d7..0000000 --- a/apecs-gloss/src/Apecs/Physics/Gloss.hs +++ /dev/null @@ -1,66 +0,0 @@ -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE TypeFamilies #-} - -module Apecs.Physics.Gloss - ( convexToPicture - , Transform, worldTransform, drawBody - , simulate - , module Apecs.Gloss - ) where - -import Control.Monad (foldM) -import Data.Semigroup (Semigroup (..)) -import Graphics.Gloss.Geometry.Angle (radToDeg) -import Graphics.Gloss.Interface.IO.Simulate (simulateIO) - -import Apecs -import Apecs.Physics - -import Apecs.Gloss - -convexToPicture :: Convex -> Picture -convexToPicture (Convex [V2 x y] radius) = Translate (realToFrac x) (realToFrac y) $ Circle (realToFrac radius) -convexToPicture (Convex [a,b] _) = Line [v2ToTuple a, v2ToTuple b] -convexToPicture (Convex verts _) = Polygon (v2ToTuple <$> verts) - -v2ToTuple :: V2 Double -> (Float, Float) -v2ToTuple (V2 x y) = (realToFrac x, realToFrac y) - -type Transform = (Position, Angle) -worldTransform :: Transform -> Picture -> Picture -worldTransform (Position (V2 x y), Angle theta) = - Translate (realToFrac x) (realToFrac y) . - Rotate (negate . radToDeg . realToFrac $ theta) - -drawBody :: Has w IO Physics => (Body, Transform, ShapeList) -> System w Picture -drawBody (btype, transform, ShapeList shapes) = color shColor . worldTransform transform <$> foldM foldfn mempty shapes - where foldfn pic shapeEty = do - Shape _ convex <- get shapeEty - return . mappend pic $ convexToPicture convex - shColor = case btype of - DynamicBody -> red - KinematicBody -> green - StaticBody -> blue - -simulate - :: ( Has w IO Camera - , Has w IO Physics - ) - => Display - -> System w () -simulate disp = do - w <- ask - liftIO $ - simulateIO disp - black - 60 - w - (\_ -> runSystem draw w) - (\_ _ _ -> runSystem (stepPhysics (1/60)) w >> return w) - - where - draw = do - pic <- foldDrawM drawBody - cam <- get global - return $ cameraTransform cam pic diff --git a/apecs-physics/CHANGELOG.md b/apecs-physics/CHANGELOG.md deleted file mode 100644 index 0db6d50..0000000 --- a/apecs-physics/CHANGELOG.md +++ /dev/null @@ -1,46 +0,0 @@ -## [0.4.4] -### Changed -- Increased upper bound `apecs` dependency - -## [0.4.3] -### Added -- `addPostStepCallback` - -### Changed -- Now runs in `MonadIO` rather than `IO` - -## [0.4.2] -### Added -- Query `Impulse` for `Constraints` -- Exposed the `CollisionType` Component - -### Changed -- Fixed `ConstraintList` to actually yield constraints instead of shapes - -## [0.4.1] -### Changed -- Fix Point Query bug, should no longer give memory issues - -## [0.4.0] -### Added -- You now have access to colliding shapes in a `Collision` - -## [0.3.2] -### Changed -- Fixed links and added changelog to cabal file -- Added version bounds for dependencies -- Expanded haddocks - -## [0.3.1] -### Changed -- added `apecs` version bound - -## [0.3.0] -### Added -- `ShapeList` and `ConstraintList` components for bodies, that contain a list of entity indices of their shapes and constraints (read-only). -### Changed -- `Shape`s, `Constraint`s, and `CollisionHandler`s now track their original Haskell representations, and can be meaningfully read. -- `Shape` and `Constraint` now only have a single constructor, that explicitly takes an entity argument indicating what entity it belongs to. Previously, the interface suggested that shapes and constraints were properties of bodies, which was wrong. -- Bodies now track their shapes and constraints in /mutable/ stores -### Removed -- The `ShapeBody` component has been removed. You can find out a shapes body by reading the `Shape` component's `ShapeExtend` constructor directly. diff --git a/apecs-physics/Chipmunk2D/LICENSE.txt b/apecs-physics/Chipmunk2D/LICENSE.txt deleted file mode 100644 index 55c24c9..0000000 --- a/apecs-physics/Chipmunk2D/LICENSE.txt +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2007-2015 Scott Lembcke and Howling Moon Software - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/apecs-physics/Chipmunk2D/VERSION.txt b/apecs-physics/Chipmunk2D/VERSION.txt deleted file mode 100644 index 2b5a2a7..0000000 --- a/apecs-physics/Chipmunk2D/VERSION.txt +++ /dev/null @@ -1,296 +0,0 @@ -What's new in 7.0.2: -* MISC: Merging pull requests. Build fixes and the like. - -What's new in 7.0.1: -* BUG: Remove references to M_PI sinces it's not actually part of C and causes problems with MSVC. -* BUG: Build fixes for Mac/CMake and MSVC 13. -* BUG: Move to using __declspec(dllexport) for Windows builds. -* BUG: Fixed a precision issue with the EPA algorithm that would cause excessive iteration. -* BUG: cpPolyshapeNewRaw() was undefined. -* BUG: Changing gravity will wake up all objects in a space. - -What's new in 7.0.0: -* All features from Chipmunk Pro are now free and open source! (threaded and NEON solver, autogeometry) -* API: Lots of cleanup to the API naming for better consistency. -* API: Renamed nearest point queries to simply point queries. -* API: Removed many deprecated functions. -* API: Struct definitions have become fully opaque instead of mangling names with the CP_PRIVATE() macro. -* API: Replaced templated accessor functions with concrete ones. Should be simpler to deal with for FFIs. -* API: Optional automatic mass properties for shapes. Calculates the moment of inertia and center of gravity for you. -* API: Optional anchor point for bodies that is separate from the center of gravity. -* API: Added radius parameters to many functions dealing with shapes (moment calculation, initialization, etc). -* API: The convex hull and winding is automatically calculated when creating a poly shape. -* API: Added a cpShapesCollide() function to check overlap of arbitrary shapes. -* API: cpShape filter property to supersede layers and groups. -* API: Collision handlers now return a collision handler struct to make it simpler to set up callbacks. -* API: Wildcard collision types. -* API: The cpArbiterTotalImpulseWithFriction() function was renamed to cpArbiterTotalImpulse(). The old useless cpArbiterTotalImpulse() implementation was removed. -* API: Contacts now store the colliding point on the surface of both shapes. -* API: cpArbiterIsRemoval() to check if a separate callback is called due to a removal and not a true separating collision. -* API: Arbiters now only store one normal per pair of colliding shapes. -* API: cpBBNewForExtents(). -* API: Added a concrete kinematic body type to replace the confusing "rogue" body concept. -* API: Added a 2x3 affine transform type, cpTransform. -* API: Added a new debug rendering API. -* MISC: Numerous improvements to the collision detection. -* MISC: cpPolyline structs are passed by reference instead of value. (I've regretted that decision for years!) - -What's new in 6.2.2: -* Fixed some issues on arm64. -* PRO: Added a 64 bit NEON solver to use on arm64. - -What's new in 6.2.1: -* Added Android support to the CMake files. (Thanks Eric Wing!) -* Added a MSVC 2012 project file. (Thanks Leonid Usov!) -* Merged a fix for VAOs on Windows. (Thanks Leonid Usov!) -* Merged a couple of other minor fixes. -* BUG: Fixed a crash issue with the ChipmunkTileCache and ChipmunkPointCloudSampler classes. (Pro only). - -What's new in 6.2.0: -* Collision detection now primarily uses the GJK and EPA algorithms instead of SAT. Internally this was a rather huge change. o_O -* Improved collision point quality and better collision point identification. -* All shape types can now be given a rounding radius. -* Collisions are now guaranteed to have a maximum of 2 collision points. -* Poly to poly collision performance is slightly better when they have a radius. Slightly worse with none. -* Implemented smoothed segment collisions to prevent colliding with the "cracks" between segment shapes. -* API: (Officially) added cpSegmentShapeSetNeighbors() used to enable smoothed line collisions. -* API: Added cpBBCenter() to get the center of a bounding box. -* API: Added cpPolyShapeInit2() and cpPolyShapeNew2() to create poly shapes with a radius. (Horrible names yes, but it will go away in Chipmunk 7) -* API: Added cpBoxShapeInit3() and cpBoxShapeNew3() to create boxes with a radius. -* API: Added cpPolyShapeGetRadius() and cpPolyShapeSetRadius() (the latter only in chipmunk_unsafe.h). -* API: Added cpNearestPointQueryInfo.g which returns the gradient of the signed distance field for the shape. -* BUG: cpMomentForPoly() will now return a correct value for degenerate 2 vertex polygons. -* BUG: Fixed an issue where certain segment query calls would return a t value of 0 instead of 1 for a missed query. -* MISC: Passing cpvzero to cpvnormalize() will now return cpvzero. No need to worry about NaNs or cpvnormalize_safe(). -* MISC: Demo app now uses GLFW instead of GLUT, and has improved drawing and text rendering routines. - -What's new in 6.1.5: -* API: Added cpArbiter*SurfaceVelocity() to allow for custom surface velocity calculation. -* API: Added cpArbiteSetContactPointSet() to allow changing the contact geometry on the fly. -* API: Added cpSpaceConvertBodyToStatic() and cpSpaceConvertBodyToDynamic(). -* API: Added [ChipmunkBody velocityAt*Point:] methods to wrap their C equivalents. (Pro only) -* API: Added overridable [ChipmunkBody updateVelocity:...] and [ChipmunkBody updatePosition:] methods. (Pro only) -* API: Added .space properties to ChipmunkBody, ChipmunkShape and ChipmunkConstaint to wrap their C equivalents. (Pro only) -* API: Added overridable [ChipmunkConstraint preSolve:] and [ChipmunkConstraint postSolve:] methods. (Pro only) -* API: Added an ChipmunkMultiGrab.grabSort property that allows you to prioritize which shape is grabbed when there is overlap. (Pro only) -* MISC: Segment queries started inside of a shape now return t=0 and n=cpvzero instead of being undefined. -* MISC: Cleaned up a lot of common assertion messages to be more clear. -* MISC: Added a new demo called Shatter. -* MISC: Added a crushing force estimation example to the ContactGraph demo and a static/dynamic conversion example to Plink. -* MISC: Modified the Sticky demo to use the new cpArbiteSetContactPointSet() to avoid the use of unnecessary sensor shapes. -* MISC: [ChipmunkSpace addBounds:...] now returns a NSArray of the bounding segments. (Pro only) - -What's new in 6.1.4: -* MISC: Fixed a build script issue that was preventing the documentation from being generated. - -What's new in 6.1.3: -* BUG: Fixed a couple of very specific but fatal bugs that occur when sleeping is enabled and filtering collisions. -* BUG: Fixed an issue with cpvslerp() between very similar vectors. -* BUG: Fixed an issue with grab friction in ChipmunkMultiGrab. (Pro only) -* MISC: Implemented the cpConstraintGetImpulse() functionality for spring joints. -* MISC: Added more functions to chipmunk_ffi.h - -What's new in 6.1.2: -* API: Added a cpArbiter.data pointer. Now you can tag collisions with custom persistent data. -* API: Added segment to segment collisions (thanks to LegoCylon) -* API: cpSpaceAddPostStepCallback() now returns false if the callback was a duplicate. -* API: Added the ChipmunkAbstractSampler.marchThreshold property instead of hardcoding it to 0.5. -* API: Added ChipmunkGrooveJoint properties for the groove and joint anchors. -* API: ChipmunkMultiGrab now returns information about grabbed shapes. -* BUG: Fixed a minor (non-crashing, non-leaking) memory pooling issue with reindexing lots of static shapes. -* BUG: Fixed an issue with the slerp functions that would cause them to return incorrect results when given non-unit length input. -* BUG: Fixed a precision bug with the ChipmunkImage sampler classes that could cause artifacts or miss small features. -* BUG: Fixed a number of properties in Objective-Chipmunk that should have been nonatomic. -* BUG: Fixed a number of types in Objective-Chipmunk that were incorrectly id that should have been cpGroup, cpCollisionType etc. It's now possible to redefine them at compile time if you wish. -* MISC: Dropped armv6 support in favor of armv7s on iOS. (You can switch it back easily if you need.) -* MISC: Updated iOS build scripts to guess the latest SDK. -* MISC: Added the "Sticky Surfaces" demo as a cpArbiter.data example. -* MISC: Updated Objective-Chipmunk build scripts to always use the latest iOS SDK. - -What's new in 6.1.1: -* API: Renamed the new block based iterators as soon as possible to match the Apple convention ("_b" suffix). - -What's new in 6.1.0: -* API: Added a pthread based, multi-threaded solver to accelerate the game on multi-core systems. (Pro only) -* API: Added cpConvexHull() and CP_CONVEX_HULL() for generating convex hulls. -* API: Added cpPolylineConvexDecomposition_BETA() to generate an approximate concave decomposition of a polyline. (Pro only) -* API: Added [ChipmunkPolyline toConvexHull:] to generate approximate convex hulls. (Pro only). -* API: Added [ChipmunkPolylineSet toConvexHulls_BETA:]. (Pro only) -* API: Added nearest point queries. -* API: Added a push mode to ChipmunkMultiGrab so touches can interact with the scene even if they didn't initially touch a shape. (Pro only) -* API: Added optional block based iterators. -* API: Added a space property to cpBody, cpShape and cpConstraint types. -* BUG: Fixed an issue with changing the floating point and vector type on OS X. -* BUG: Fixed a pixel offset in ChipmunkImageSampler that could cause minor sampling artifacts. (Pro only) -* BUG: Fixed an issue where cpShape and cpConstraint structs could have garbage space pointers if cpcalloc() was redefined. -* BUG: Fixed assertions in cpArbiter getters to correctly reflect a contact count of 0 from separate() callbacks. -* BUG: Fixed a regression relating to registering post-step() callbacks from other post-step() callbacks. -* BUG: Fixed a minor memory leak for sleeping bodies when destroying a space. -* MISC: Point queries are now deprecated in preference to point queries. -* MISC: cpSpatialIndexPointQuery() was redundant and has been removed. Use cpSpatialIndexQuery() instead. -* MISC: cpShape*Query() functions now accept a NULL info pointer if you don't want detailed query info. -* MISC: The enableContactGraph property of cpSpace is deprecated and always be true. -* MISC: Added a new demos of the convex hull functions and a self balancing Unicycle. - -What's new in 6.0.3: -* API: Added a cpBBForCircle() convenience function. -* API: Added cpBBSegmentQuery() to check where a segment hits a cpBB. -* API: Added cpBodyGetVelAtWorldPoint() and cpBodyGetVelAtLocalPoint() to get point velocities on a body. -* API: Added cpArbiterTotalKE() to calculate the energy lost due to a collision. Great for calculating damage accurately. -* API: Added methods to get an ObjC pointer from a C chipmunk struct. -* API: Added a CHIPMUNK_ARBITER_GET_BODIES() macro for Objective-Chipmunk. -* API: The Objective-Chipmunk headers are now ARC compatible. -* API: Added a [ChipmunkSpace contains:] method to check if a ChipmunkObject has been added to the space or not. -* API: Added a cpBBNewForCircle() function. -* API: Added a cpBBSegmentQuery() function for raycasting againsts AABBs. -* BUG: Fixed a regression with ChipmunkSpace.bodies and ChipmunkSpace.shapes that caused crashes. -* BUG: Fixed a rare bug with postStep() callbacks and iterators. -* BUG: Fixed a border case in cpBBIntersectsSegment() that could cause missed segment queries. -* MISC: Added some new assertions for error conditions that were previously uncaught. -* MISC: Accelerated segment queries in cpBBTree by sorting the nodes. -* MISC: Added a new "Slice" demo that lets you cut up a polygon. -* MISC: Added NEON optimizations for Chipmunk Pro. Expect running on most ARM platforms to be 25-35% faster for contact heavy simulations. -* MISC: All ChipmunkObject instances added to a space are now retained, even composite ones. - -What's new in 6.0.2: -* API: Added cpSpaceIsLocked() to check if you are in a callback or not. -* API: Removed the long deprecated [ChipmunkSpace addShapeAHandler:] and [ChipmunkSpace addShapeBHandler:] methods. -* API: The ChipmunkObject protocol now can return any id object instead of just an NSSet. -* API: The largely useless [ChipmunkSpace addBaseObjects:] and [ChipmunkSpace removeBaseObjects:] methods were removed. -* API: Added [ChipmunkSpace smartAdd:] and [ChipmunkSpace smartRemove:] methods for a consistent API to remove objects inside and out of callbacks. -* API: Added [ChipmunkSpace addPostStepBlock:key:] to complement [ChipmunkSpace addPostStepCallback:selector:key:]. -* API: Added [ChipmunkSpace addPostStepAddition:]. -* API: Objective-Chipmunk collision handlers no longer retain their target to avoid reference cycles. -* API: Added callbacks to joints. -* BUG: Soft errors (only checked when debug mode is enabled) and warnings were disabled. Whoops. -* BUG: cpShapeIsSensor() was incorrectly named in chipmunk_ffi.h. -* BUG: It should be safe to call cpActivateBody() from an space iterator callback now. -* MISC: Very nice bouyancy demo added based on callbacks. -* MISC: Breakable Joints demo showing how to use the new joint callbacks. -* MISC: Player demo updated and greatly enhanced by Chipmunk 6 features. -* MISC: Changed adding a static body to a space from a warning to a hard error. -* MISC: cpGroup and cpCollisionType now default to uintptr_t so you can safely use pointers instead of ints for these types. -* MISC: Updated the MSVC10 project file. -* MISC: Updated the FFI defs. - -What's new in 6.0.1: -* BUG: Calling cpBodySetPos() on a sleeping body was delaying the Separate() handler callback if one existed. -* BUG: Fixed a bug where Separate() handler callbacks were not occuring when removing shapes. -* BUG: Calling cpBodyApplyForce() or cpBodyResetForces() was not activating sleeping bodies. -* API: Added cpSpaceEachConstraint(). -* API: Added a "CurrentTimeStep" property to cpSpace to retrieve the current (or most recent) timestep. -* MISC: Got rid of anonymous unions so that it is C99 clean again. - -What's new in 6.0.0: -Chipmunk 6.x's API is not quite 100% compatible with 5.x. Make sure you read the list of changes carefully. -Keep in mind that this is a x.0.0 release and that it's likely there are still some bugs I don't know about yet. I've spent a lot of effort rewritting the collision detection, sleeping, and contact graph algorithms that have required large changes and cleanup to the 5.x codebase. I've ironed out all the bugs that I know of, and the beta test went well. So it's finally time for 6! - -* API: Chipmunk now has hard runtime assertions that aren't disabled in release mode for many error conditions. Most people have been using release builds of Chipmunk during development and were missing out on very important error checking. -* API: Access to the private API has been disabled by default now and much of the private API has changed. I've added official APIs for all the uses of the private API I knew of. -* API: Added accessor functions for every property on every type. As Chipmunk's complexity has grown, it's become more difficult to ignore accessors. You are encouraged to use them, but are not required to. -* API: Added cpSpaceEachBody() and cpSpaceEachShape() to iterate bodies/shapes in a space. -* API: Added cpSpaceReindexShapesForBody() to reindex all the shapes attached to a particular body. -* API: Added a 'data' pointer to spaces now too. -* API: cpSpace.staticBody is a pointer to the static body instead of a static reference. -* API: The globals cp_bias_coef, cp_collision_slop, cp_contact_persistence have been moved to properties of a space. (collisionBias, collisionSlop, collisionPersistence respectively) -* API: Added cpBodyActivateStatic() to wake up bodies touching a static body with an optional shape filter parameter. -* API: Added cpBodyEachShape() and cpBodyEachConstraint() iterators to iterate the active shapes/constraints attached to a body. -* API: Added cpBodyEeachArbiter() to iterate the collision pairs a body is involved in. This makes it easy to perform grounding checks or find how much collision force is being applied to an object. -* API: The error correction applied by the collision bias and joint bias is now timestep independent and the units have completely changed. -* FIX: Units of damping for springs are correct regardless of the number of iterations. Previously they were only correct if you had 1 or 2 iterations. -* MISC: Numerous changes to help make Chipmunk work better with variable timesteps. Use of constant timesteps is still highly recommended, but it is now easier to change the time scale without introducing artifacts. -* MISC: Performance! Chipmunk 6 should be way faster than Chipmunk 5 for almost any game. -* MISC: Chipmunk supports multiple spatial indexes and uses a bounding box tree similar to the one found in the Bullet physics library by default. This should provide much better performance for scenes with objects of differening size and works without any tuning for any scale. - - -What's new in 5.3.5 -* FIX: Fixed spelling of cpArbiterGetDepth(). Was cpArbiteGetDepth() before. Apparently nobody ever used this function. -* FIX: Added defines for M_PI and M_E. Apparently these values were never part of the C standard math library. Who knew!? -* FIX: Added a guard to cpBodyActivate() so that it's a noop for rouge bodies. -* FIX: Shape queries now work with (and against) sensor shapes. -* FIX: Fixed an issue where removing a collision handler while a separate() callback was waiting to fire the next step would cause crashes. -* FIX: Fixed an issue where the default callback would not be called for sensor shapes. -* FIX: Resetting or applying forces or impulses on a body causes it to wake up now. -* MISC: Added a check that a space was not locked when adding or removing a callback. -* MISC: Removed cpmalloc from the API and replaced all occurences with cpcalloc -* MISC: Added a benchmarking mode to the demo app. -trial runs it in time trial mode and -bench makes it run some benchmarking demos. - -What's new in 5.3.4: -* FIX: cpBodyActivate() can now be called from collision and query callbacks. This way you can use the setter functions to change properties without indirectly calling cpBodyActivate() and causing an assertion. -* FIX: cpArbiterGetContactPointSet() was returning the collision points for the normals. -* FIX: cpSpaceEachBody() now includes sleeping bodies. -* FIX: Shapes attached to static rogue bodies created with cpBodyNewStatic() are added as static shapes. -* MISC: Applied a user patch to update the MSVC project and add a .def file. - -What's new in 5.3.3: -* API: Added cpArbiteGetCount() to return the number of contact points. -* API: Added helper functions for calculating areas of Chipmunk shapes as well as calculating polygon centroids and centering polygons on their centroid. -* API: Shape queries. Query a shape to test for collisions if it were to be inserted into a space. -* API: cpBodyInitStatic() and cpBodyNewStatic() for creating additional static (rogue) bodies. -* API: cpBodySleepWithGroup() to allow you to create groups of sleeping objects that are woken up together. -* API: Added overloaded *, +, - and == operators for C++ users. -* API: Added cpSpaceActivateShapesTouchingShape() to query for and activate any shapes touching a given shape. Useful if you ever need to move a static body. -* FIX: Fixed an extremely rare memory bug in the collision cache. -* FIX: Fixed a memory leak in Objective-Chipmunk that could cause ChipmunkSpace objects to be leaked. -* MISC: C struct fields and function that are considered private have been explicitly marked as such. Defining CP_ALLOW_PRIVATE_ACCESS to 0 in Chipmunk.h will let you test which parts of the private API that you are using and give me feedback about how to build proper APIs in Chipmunk 6 for what you are trying to do. -* MISC: Allow CGPoints to be used as cpVect on Mac OS X as well as iOS. - - -What's new in 5.3.2: -* FIX: Collision begin callbacks were being called continuously for sensors or collisions rejected from the pre-solve callback. -* FIX: Plugged a nasty memory leak when adding post-step callbacks. -* FIX: Shapes were being added to the spatial hash using an uninitialized bounding box in some cases. -* FIX: Perfectly aligned circle shapes now push each other apart. -* FIX: cpBody setter functions now call cpBodyActivate(). -* FIX: Collision handler targets are released in Objective-Chipmunk when they are no longer needed instead of waiting for the space to be deallocated. -* API: cpSpaceSegmentQuery() no longer returns a boolean. Use cpSpaceSegmentQueryFirst() instead as it's more efficient. -* NEW: cpSpaceRehashShape() Rehash an individual shape, active or static. -* NEW: cpBodySleep() Force a body to fall asleep immediately. -* NEW: cpConstraintGetImpulse() Return the most recent impulse applied by a constraint. -* NEW: Added setter functions for the groove joint endpoints. -* MISC: A number of other minor optimizations and fixes. - -What's new in 5.3.1: - * NEW: Added a brand new tutorial for Objective-Chipmunk: SimpleObjectiveChipmunk that can be found in the Objective-Chipmunk folder. - * NEW: Proper API docs for Objective-Chipmunk. - * NEW: Updated the included Objective-Chipmunk library. - * FIX: Fixed a rare memory crash in the sensor demo. - * FIX: Fixed some warnings that users submitted. - -What's new in 5.3.0: - * FIX: Fixed the source so it can compile as C, C++, Objective-C, and Objective-C++. - * FIX: Fixed cp_contact_persistence. It was broken so that it would forget collision solutions after 1 frame instead of respecting the value set. - * OPTIMIZATION: Several minor optimizations have been added. Though performance should only differ by a few percent. - * OPTIMIZATION: Chipmunk now supports putting bodies to sleep when they become inactive. - * API: Elastic iterations are now deprecated as they should no longer be necessary. - * API: Added API elements to support body sleeping. - * API: Added a statically allocated static body to each space for attaching static shapes to. - * API: Static shapes attached to the space's static body can simply be added to the space using cpSpaceAddShape(). - * NEW: New MSVC projects. - * NEW: Added boolean and time stamp types for clarity. - -What's new in 5.2.0: - * OPTIMIZATION: Chipmunk structs used within the solver are now allocated linearly in large blocks. This is much more CPU cache friendly. Programs have seen up to 50% performance improvements though 15-20% should be expected. - * API: Shape references in cpArbiter structs changed to private_a and private_b to discourage accessing the fields directly and getting them out of order. You should be using cpArbiterGetShapes() or CP_ARBITER_GET_SHAPES() to access the shapes in the correct order. - * API: Added assertion error messages as well as warnings and covered many new assertion cases. - * FIX: separate() callbacks are called before shapes are removed from the space to prevent dangling pointers. - * NEW: Added convenience functions for creating box shapes and calculating moments. - - -What's new in 5.1.0: - * FIX: fixed a NaN issue that was causing raycasts for horizontal or vertical lines to end up in an infinite loop - * FIX: fixed a number of memory leaks - * FIX: fixed warnings for various compiler/OS combinations - * API: Rejecting a collision from a begin() callback permanently rejects the collision until separation - * API: Erroneous collision type parameterns removed from cpSpaceDefaulteCollisionHandler() - * MOVE: FFI declarations of inlined functions into their own header - * MOVE: Rearranged the project structure to separate out the header files into a separate include/ directory. - * NEW: Added a static library target for the iPhone. - * NEW: Type changes when building on the iPhone to make it friendlier to other iPhone APIs - * NEW: Added an AABB query to complement point and segment queries - * NEW: CP_NO_GROUP and CP_ALL_LAYERS constants - -What's new in 5.0.0: - * Brand new Joint/Constraint API: New constraints can be added easily and are much more flexible than the old joint system - * Efficient Segment Queries - Like raycasting, but with line segments. - * Brand new collision callback API: Collision begin/separate events, API for removal of objects within callbacks, more programable control over collision handling. \ No newline at end of file diff --git a/apecs-physics/Chipmunk2D/include/chipmunk/chipmunk.h b/apecs-physics/Chipmunk2D/include/chipmunk/chipmunk.h deleted file mode 100644 index 1280190..0000000 --- a/apecs-physics/Chipmunk2D/include/chipmunk/chipmunk.h +++ /dev/null @@ -1,232 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef CHIPMUNK_H -#define CHIPMUNK_H - -#include -#include - -#ifndef alloca - #ifdef _WIN32 - #include - #elif defined(__FreeBSD__) - /* already included in */ - #else - #include - #endif -#endif - -#ifdef _WIN32 - #define CP_EXPORT __declspec(dllexport) -#else - #define CP_EXPORT -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -CP_EXPORT void cpMessage(const char *condition, const char *file, int line, int isError, int isHardError, const char *message, ...); -#ifdef NDEBUG - #define cpAssertWarn(__condition__, ...) - #define cpAssertSoft(__condition__, ...) -#else - #define cpAssertSoft(__condition__, ...) if(!(__condition__)){cpMessage(#__condition__, __FILE__, __LINE__, 1, 0, __VA_ARGS__); abort();} - #define cpAssertWarn(__condition__, ...) if(!(__condition__)) cpMessage(#__condition__, __FILE__, __LINE__, 0, 0, __VA_ARGS__) -#endif - -// Hard assertions are used in situations where the program definitely will crash anyway, and the reason is inexpensive to detect. -#define cpAssertHard(__condition__, ...) if(!(__condition__)){cpMessage(#__condition__, __FILE__, __LINE__, 1, 1, __VA_ARGS__); abort();} - -#include "chipmunk_types.h" - -/// @defgroup misc Misc -/// @{ - -/// Allocated size for various Chipmunk buffers -#ifndef CP_BUFFER_BYTES - #define CP_BUFFER_BYTES (32*1024) -#endif - -#ifndef cpcalloc - /// Chipmunk calloc() alias. - #define cpcalloc calloc -#endif - -#ifndef cprealloc - /// Chipmunk realloc() alias. - #define cprealloc realloc -#endif - -#ifndef cpfree - /// Chipmunk free() alias. - #define cpfree free -#endif - -typedef struct cpArray cpArray; -typedef struct cpHashSet cpHashSet; - -typedef struct cpBody cpBody; - -typedef struct cpShape cpShape; -typedef struct cpCircleShape cpCircleShape; -typedef struct cpSegmentShape cpSegmentShape; -typedef struct cpPolyShape cpPolyShape; - -typedef struct cpConstraint cpConstraint; -typedef struct cpPinJoint cpPinJoint; -typedef struct cpSlideJoint cpSlideJoint; -typedef struct cpPivotJoint cpPivotJoint; -typedef struct cpGrooveJoint cpGrooveJoint; -typedef struct cpDampedSpring cpDampedSpring; -typedef struct cpDampedRotarySpring cpDampedRotarySpring; -typedef struct cpRotaryLimitJoint cpRotaryLimitJoint; -typedef struct cpRatchetJoint cpRatchetJoint; -typedef struct cpGearJoint cpGearJoint; -typedef struct cpSimpleMotorJoint cpSimpleMotorJoint; - -typedef struct cpCollisionHandler cpCollisionHandler; -typedef struct cpContactPointSet cpContactPointSet; -typedef struct cpArbiter cpArbiter; - -typedef struct cpSpace cpSpace; - -#include "cpVect.h" -#include "cpBB.h" -#include "cpTransform.h" -#include "cpSpatialIndex.h" - -#include "cpArbiter.h" - -#include "cpBody.h" -#include "cpShape.h" -#include "cpPolyShape.h" - -#include "cpConstraint.h" - -#include "cpSpace.h" - -// Chipmunk 7.0.2 -#define CP_VERSION_MAJOR 7 -#define CP_VERSION_MINOR 0 -#define CP_VERSION_RELEASE 2 - -/// Version string. -CP_EXPORT extern const char *cpVersionString; - -/// Calculate the moment of inertia for a circle. -/// @c r1 and @c r2 are the inner and outer diameters. A solid circle has an inner diameter of 0. -CP_EXPORT cpFloat cpMomentForCircle(cpFloat m, cpFloat r1, cpFloat r2, cpVect offset); - -/// Calculate area of a hollow circle. -/// @c r1 and @c r2 are the inner and outer diameters. A solid circle has an inner diameter of 0. -CP_EXPORT cpFloat cpAreaForCircle(cpFloat r1, cpFloat r2); - -/// Calculate the moment of inertia for a line segment. -/// Beveling radius is not supported. -CP_EXPORT cpFloat cpMomentForSegment(cpFloat m, cpVect a, cpVect b, cpFloat radius); - -/// Calculate the area of a fattened (capsule shaped) line segment. -CP_EXPORT cpFloat cpAreaForSegment(cpVect a, cpVect b, cpFloat radius); - -/// Calculate the moment of inertia for a solid polygon shape assuming it's center of gravity is at it's centroid. The offset is added to each vertex. -CP_EXPORT cpFloat cpMomentForPoly(cpFloat m, int count, const cpVect *verts, cpVect offset, cpFloat radius); - -/// Calculate the signed area of a polygon. A Clockwise winding gives positive area. -/// This is probably backwards from what you expect, but matches Chipmunk's the winding for poly shapes. -CP_EXPORT cpFloat cpAreaForPoly(const int count, const cpVect *verts, cpFloat radius); - -/// Calculate the natural centroid of a polygon. -CP_EXPORT cpVect cpCentroidForPoly(const int count, const cpVect *verts); - -/// Calculate the moment of inertia for a solid box. -CP_EXPORT cpFloat cpMomentForBox(cpFloat m, cpFloat width, cpFloat height); - -/// Calculate the moment of inertia for a solid box. -CP_EXPORT cpFloat cpMomentForBox2(cpFloat m, cpBB box); - -/// Calculate the convex hull of a given set of points. Returns the count of points in the hull. -/// @c result must be a pointer to a @c cpVect array with at least @c count elements. If @c verts == @c result, then @c verts will be reduced inplace. -/// @c first is an optional pointer to an integer to store where the first vertex in the hull came from (i.e. verts[first] == result[0]) -/// @c tol is the allowed amount to shrink the hull when simplifying it. A tolerance of 0.0 creates an exact hull. -CP_EXPORT int cpConvexHull(int count, const cpVect *verts, cpVect *result, int *first, cpFloat tol); - -/// Convenience macro to work with cpConvexHull. -/// @c count and @c verts is the input array passed to cpConvexHull(). -/// @c count_var and @c verts_var are the names of the variables the macro creates to store the result. -/// The output vertex array is allocated on the stack using alloca() so it will be freed automatically, but cannot be returned from the current scope. -#define CP_CONVEX_HULL(__count__, __verts__, __count_var__, __verts_var__) \ -cpVect *__verts_var__ = (cpVect *)alloca(__count__*sizeof(cpVect)); \ -int __count_var__ = cpConvexHull(__count__, __verts__, __verts_var__, NULL, 0.0); \ - -/// Returns the closest point on the line segment ab, to the point p. -static inline cpVect -cpClosetPointOnSegment(const cpVect p, const cpVect a, const cpVect b) -{ - cpVect delta = cpvsub(a, b); - cpFloat t = cpfclamp01(cpvdot(delta, cpvsub(p, b))/cpvlengthsq(delta)); - return cpvadd(b, cpvmult(delta, t)); -} - -#if defined(__has_extension) -#if __has_extension(blocks) -// Define alternate block based alternatives for a few of the callback heavy functions. -// Collision handlers are post-step callbacks are not included to avoid memory management issues. -// If you want to use blocks for those and are aware of how to correctly manage the memory, the implementation is trivial. - -void cpSpaceEachBody_b(cpSpace *space, void (^block)(cpBody *body)); -void cpSpaceEachShape_b(cpSpace *space, void (^block)(cpShape *shape)); -void cpSpaceEachConstraint_b(cpSpace *space, void (^block)(cpConstraint *constraint)); - -void cpBodyEachShape_b(cpBody *body, void (^block)(cpShape *shape)); -void cpBodyEachConstraint_b(cpBody *body, void (^block)(cpConstraint *constraint)); -void cpBodyEachArbiter_b(cpBody *body, void (^block)(cpArbiter *arbiter)); - -typedef void (^cpSpacePointQueryBlock)(cpShape *shape, cpVect point, cpFloat distance, cpVect gradient); -void cpSpacePointQuery_b(cpSpace *space, cpVect point, cpFloat maxDistance, cpShapeFilter filter, cpSpacePointQueryBlock block); - -typedef void (^cpSpaceSegmentQueryBlock)(cpShape *shape, cpVect point, cpVect normal, cpFloat alpha); -void cpSpaceSegmentQuery_b(cpSpace *space, cpVect start, cpVect end, cpFloat radius, cpShapeFilter filter, cpSpaceSegmentQueryBlock block); - -typedef void (^cpSpaceBBQueryBlock)(cpShape *shape); -void cpSpaceBBQuery_b(cpSpace *space, cpBB bb, cpShapeFilter filter, cpSpaceBBQueryBlock block); - -typedef void (^cpSpaceShapeQueryBlock)(cpShape *shape, cpContactPointSet *points); -cpBool cpSpaceShapeQuery_b(cpSpace *space, cpShape *shape, cpSpaceShapeQueryBlock block); - -#endif -#endif - - -//@} - -#ifdef __cplusplus -} - -static inline cpVect operator *(const cpVect v, const cpFloat s){return cpvmult(v, s);} -static inline cpVect operator +(const cpVect v1, const cpVect v2){return cpvadd(v1, v2);} -static inline cpVect operator -(const cpVect v1, const cpVect v2){return cpvsub(v1, v2);} -static inline cpBool operator ==(const cpVect v1, const cpVect v2){return cpveql(v1, v2);} -static inline cpVect operator -(const cpVect v){return cpvneg(v);} - -#endif -#endif diff --git a/apecs-physics/Chipmunk2D/include/chipmunk/chipmunk_ffi.h b/apecs-physics/Chipmunk2D/include/chipmunk/chipmunk_ffi.h deleted file mode 100644 index 86e3d9f..0000000 --- a/apecs-physics/Chipmunk2D/include/chipmunk/chipmunk_ffi.h +++ /dev/null @@ -1,105 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifdef CHIPMUNK_FFI - -// Create non static inlined copies of Chipmunk functions, useful for working with dynamic FFIs -// For many languages, it may be faster to reimplement these functions natively instead. -// Note: This file should only be included by chipmunk.c. - -#ifdef _MSC_VER - #if _MSC_VER >= 1600 - #define MAKE_REF(name) CP_EXPORT decltype(name) *_##name = name - #else - #define MAKE_REF(name) - #endif -#else - #define MAKE_REF(name) __typeof__(name) *_##name = name -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -MAKE_REF(cpv); // makes a variable named _cpv that contains the function pointer for cpv() -MAKE_REF(cpveql); -MAKE_REF(cpvadd); -MAKE_REF(cpvneg); -MAKE_REF(cpvsub); -MAKE_REF(cpvmult); -MAKE_REF(cpvdot); -MAKE_REF(cpvcross); -MAKE_REF(cpvperp); -MAKE_REF(cpvrperp); -MAKE_REF(cpvproject); -MAKE_REF(cpvforangle); -MAKE_REF(cpvtoangle); -MAKE_REF(cpvrotate); -MAKE_REF(cpvunrotate); -MAKE_REF(cpvlengthsq); -MAKE_REF(cpvlength); -MAKE_REF(cpvlerp); -MAKE_REF(cpvnormalize); -MAKE_REF(cpvclamp); -MAKE_REF(cpvlerpconst); -MAKE_REF(cpvdist); -MAKE_REF(cpvdistsq); -MAKE_REF(cpvnear); - -MAKE_REF(cpfmax); -MAKE_REF(cpfmin); -MAKE_REF(cpfabs); -MAKE_REF(cpfclamp); -MAKE_REF(cpflerp); -MAKE_REF(cpflerpconst); - -MAKE_REF(cpBBNew); -MAKE_REF(cpBBNewForExtents); -MAKE_REF(cpBBNewForCircle); -MAKE_REF(cpBBIntersects); -MAKE_REF(cpBBContainsBB); -MAKE_REF(cpBBContainsVect); -MAKE_REF(cpBBMerge); -MAKE_REF(cpBBExpand); -MAKE_REF(cpBBCenter); -MAKE_REF(cpBBArea); -MAKE_REF(cpBBMergedArea); -MAKE_REF(cpBBSegmentQuery); -MAKE_REF(cpBBIntersectsSegment); -MAKE_REF(cpBBClampVect); - -MAKE_REF(cpSpatialIndexDestroy); -MAKE_REF(cpSpatialIndexCount); -MAKE_REF(cpSpatialIndexEach); -MAKE_REF(cpSpatialIndexContains); -MAKE_REF(cpSpatialIndexInsert); -MAKE_REF(cpSpatialIndexRemove); -MAKE_REF(cpSpatialIndexReindex); -MAKE_REF(cpSpatialIndexReindexObject); -MAKE_REF(cpSpatialIndexSegmentQuery); -MAKE_REF(cpSpatialIndexQuery); -MAKE_REF(cpSpatialIndexReindexQuery); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/apecs-physics/Chipmunk2D/include/chipmunk/chipmunk_private.h b/apecs-physics/Chipmunk2D/include/chipmunk/chipmunk_private.h deleted file mode 100644 index bea4cc3..0000000 --- a/apecs-physics/Chipmunk2D/include/chipmunk/chipmunk_private.h +++ /dev/null @@ -1,344 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef CHIPMUNK_PRIVATE_H -#define CHIPMUNK_PRIVATE_H - -#include "chipmunk.h" -#include "chipmunk_structs.h" - -#define CP_HASH_COEF (3344921057ul) -#define CP_HASH_PAIR(A, B) ((cpHashValue)(A)*CP_HASH_COEF ^ (cpHashValue)(B)*CP_HASH_COEF) - -// TODO: Eww. Magic numbers. -#define MAGIC_EPSILON 1e-5 - - -//MARK: cpArray - -cpArray *cpArrayNew(int size); - -void cpArrayFree(cpArray *arr); - -void cpArrayPush(cpArray *arr, void *object); -void *cpArrayPop(cpArray *arr); -void cpArrayDeleteObj(cpArray *arr, void *obj); -cpBool cpArrayContains(cpArray *arr, void *ptr); - -void cpArrayFreeEach(cpArray *arr, void (freeFunc)(void*)); - - -//MARK: cpHashSet - -typedef cpBool (*cpHashSetEqlFunc)(void *ptr, void *elt); -typedef void *(*cpHashSetTransFunc)(void *ptr, void *data); - -cpHashSet *cpHashSetNew(int size, cpHashSetEqlFunc eqlFunc); -void cpHashSetSetDefaultValue(cpHashSet *set, void *default_value); - -void cpHashSetFree(cpHashSet *set); - -int cpHashSetCount(cpHashSet *set); -void *cpHashSetInsert(cpHashSet *set, cpHashValue hash, void *ptr, cpHashSetTransFunc trans, void *data); -void *cpHashSetRemove(cpHashSet *set, cpHashValue hash, void *ptr); -void *cpHashSetFind(cpHashSet *set, cpHashValue hash, void *ptr); - -typedef void (*cpHashSetIteratorFunc)(void *elt, void *data); -void cpHashSetEach(cpHashSet *set, cpHashSetIteratorFunc func, void *data); - -typedef cpBool (*cpHashSetFilterFunc)(void *elt, void *data); -void cpHashSetFilter(cpHashSet *set, cpHashSetFilterFunc func, void *data); - - -//MARK: Bodies - -void cpBodyAddShape(cpBody *body, cpShape *shape); -void cpBodyRemoveShape(cpBody *body, cpShape *shape); - -//void cpBodyAccumulateMassForShape(cpBody *body, cpShape *shape); -void cpBodyAccumulateMassFromShapes(cpBody *body); - -void cpBodyRemoveConstraint(cpBody *body, cpConstraint *constraint); - - -//MARK: Spatial Index Functions - -cpSpatialIndex *cpSpatialIndexInit(cpSpatialIndex *index, cpSpatialIndexClass *klass, cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex); - - -//MARK: Arbiters - -cpArbiter* cpArbiterInit(cpArbiter *arb, cpShape *a, cpShape *b); - -static inline struct cpArbiterThread * -cpArbiterThreadForBody(cpArbiter *arb, cpBody *body) -{ - return (arb->body_a == body ? &arb->thread_a : &arb->thread_b); -} - -void cpArbiterUnthread(cpArbiter *arb); - -void cpArbiterUpdate(cpArbiter *arb, struct cpCollisionInfo *info, cpSpace *space); -void cpArbiterPreStep(cpArbiter *arb, cpFloat dt, cpFloat bias, cpFloat slop); -void cpArbiterApplyCachedImpulse(cpArbiter *arb, cpFloat dt_coef); -void cpArbiterApplyImpulse(cpArbiter *arb); - - -//MARK: Shapes/Collisions - -cpShape *cpShapeInit(cpShape *shape, const cpShapeClass *klass, cpBody *body, struct cpShapeMassInfo massInfo); - -static inline cpBool -cpShapeActive(cpShape *shape) -{ - // checks if the shape is added to a shape list. - // TODO could this just check the space now? - return (shape->prev || (shape->body && shape->body->shapeList == shape)); -} - -// Note: This function returns contact points with r1/r2 in absolute coordinates, not body relative. -struct cpCollisionInfo cpCollide(const cpShape *a, const cpShape *b, cpCollisionID id, struct cpContact *contacts); - -static inline void -CircleSegmentQuery(cpShape *shape, cpVect center, cpFloat r1, cpVect a, cpVect b, cpFloat r2, cpSegmentQueryInfo *info) -{ - cpVect da = cpvsub(a, center); - cpVect db = cpvsub(b, center); - cpFloat rsum = r1 + r2; - - cpFloat qa = cpvdot(da, da) - 2.0f*cpvdot(da, db) + cpvdot(db, db); - cpFloat qb = cpvdot(da, db) - cpvdot(da, da); - cpFloat det = qb*qb - qa*(cpvdot(da, da) - rsum*rsum); - - if(det >= 0.0f){ - cpFloat t = (-qb - cpfsqrt(det))/(qa); - if(0.0f<= t && t <= 1.0f){ - cpVect n = cpvnormalize(cpvlerp(da, db, t)); - - info->shape = shape; - info->point = cpvsub(cpvlerp(a, b, t), cpvmult(n, r2)); - info->normal = n; - info->alpha = t; - } - } -} - -static inline cpBool -cpShapeFilterReject(cpShapeFilter a, cpShapeFilter b) -{ - // Reject the collision if: - return ( - // They are in the same non-zero group. - (a.group != 0 && a.group == b.group) || - // One of the category/mask combinations fails. - (a.categories & b.mask) == 0 || - (b.categories & a.mask) == 0 - ); -} - -void cpLoopIndexes(const cpVect *verts, int count, int *start, int *end); - - -//MARK: Constraints -// TODO naming conventions here - -void cpConstraintInit(cpConstraint *constraint, const struct cpConstraintClass *klass, cpBody *a, cpBody *b); - -static inline void -cpConstraintActivateBodies(cpConstraint *constraint) -{ - cpBody *a = constraint->a; cpBodyActivate(a); - cpBody *b = constraint->b; cpBodyActivate(b); -} - -static inline cpVect -relative_velocity(cpBody *a, cpBody *b, cpVect r1, cpVect r2){ - cpVect v1_sum = cpvadd(a->v, cpvmult(cpvperp(r1), a->w)); - cpVect v2_sum = cpvadd(b->v, cpvmult(cpvperp(r2), b->w)); - - return cpvsub(v2_sum, v1_sum); -} - -static inline cpFloat -normal_relative_velocity(cpBody *a, cpBody *b, cpVect r1, cpVect r2, cpVect n){ - return cpvdot(relative_velocity(a, b, r1, r2), n); -} - -static inline void -apply_impulse(cpBody *body, cpVect j, cpVect r){ - body->v = cpvadd(body->v, cpvmult(j, body->m_inv)); - body->w += body->i_inv*cpvcross(r, j); -} - -static inline void -apply_impulses(cpBody *a , cpBody *b, cpVect r1, cpVect r2, cpVect j) -{ - apply_impulse(a, cpvneg(j), r1); - apply_impulse(b, j, r2); -} - -static inline void -apply_bias_impulse(cpBody *body, cpVect j, cpVect r) -{ - body->v_bias = cpvadd(body->v_bias, cpvmult(j, body->m_inv)); - body->w_bias += body->i_inv*cpvcross(r, j); -} - -static inline void -apply_bias_impulses(cpBody *a , cpBody *b, cpVect r1, cpVect r2, cpVect j) -{ - apply_bias_impulse(a, cpvneg(j), r1); - apply_bias_impulse(b, j, r2); -} - -static inline cpFloat -k_scalar_body(cpBody *body, cpVect r, cpVect n) -{ - cpFloat rcn = cpvcross(r, n); - return body->m_inv + body->i_inv*rcn*rcn; -} - -static inline cpFloat -k_scalar(cpBody *a, cpBody *b, cpVect r1, cpVect r2, cpVect n) -{ - cpFloat value = k_scalar_body(a, r1, n) + k_scalar_body(b, r2, n); - cpAssertSoft(value != 0.0, "Unsolvable collision or constraint."); - - return value; -} - -static inline cpMat2x2 -k_tensor(cpBody *a, cpBody *b, cpVect r1, cpVect r2) -{ - cpFloat m_sum = a->m_inv + b->m_inv; - - // start with Identity*m_sum - cpFloat k11 = m_sum, k12 = 0.0f; - cpFloat k21 = 0.0f, k22 = m_sum; - - // add the influence from r1 - cpFloat a_i_inv = a->i_inv; - cpFloat r1xsq = r1.x * r1.x * a_i_inv; - cpFloat r1ysq = r1.y * r1.y * a_i_inv; - cpFloat r1nxy = -r1.x * r1.y * a_i_inv; - k11 += r1ysq; k12 += r1nxy; - k21 += r1nxy; k22 += r1xsq; - - // add the influnce from r2 - cpFloat b_i_inv = b->i_inv; - cpFloat r2xsq = r2.x * r2.x * b_i_inv; - cpFloat r2ysq = r2.y * r2.y * b_i_inv; - cpFloat r2nxy = -r2.x * r2.y * b_i_inv; - k11 += r2ysq; k12 += r2nxy; - k21 += r2nxy; k22 += r2xsq; - - // invert - cpFloat det = k11*k22 - k12*k21; - cpAssertSoft(det != 0.0, "Unsolvable constraint."); - - cpFloat det_inv = 1.0f/det; - return cpMat2x2New( - k22*det_inv, -k12*det_inv, - -k21*det_inv, k11*det_inv - ); -} - -static inline cpFloat -bias_coef(cpFloat errorBias, cpFloat dt) -{ - return 1.0f - cpfpow(errorBias, dt); -} - - -//MARK: Spaces - -#define cpAssertSpaceUnlocked(space) \ - cpAssertHard(!space->locked, \ - "This operation cannot be done safely during a call to cpSpaceStep() or during a query. " \ - "Put these calls into a post-step callback." \ - ); - -void cpSpaceSetStaticBody(cpSpace *space, cpBody *body); - -extern cpCollisionHandler cpCollisionHandlerDoNothing; - -void cpSpaceProcessComponents(cpSpace *space, cpFloat dt); - -void cpSpacePushFreshContactBuffer(cpSpace *space); -struct cpContact *cpContactBufferGetArray(cpSpace *space); -void cpSpacePushContacts(cpSpace *space, int count); - -cpPostStepCallback *cpSpaceGetPostStepCallback(cpSpace *space, void *key); - -cpBool cpSpaceArbiterSetFilter(cpArbiter *arb, cpSpace *space); -void cpSpaceFilterArbiters(cpSpace *space, cpBody *body, cpShape *filter); - -void cpSpaceActivateBody(cpSpace *space, cpBody *body); -void cpSpaceLock(cpSpace *space); -void cpSpaceUnlock(cpSpace *space, cpBool runPostStep); - -static inline void -cpSpaceUncacheArbiter(cpSpace *space, cpArbiter *arb) -{ - const cpShape *a = arb->a, *b = arb->b; - const cpShape *shape_pair[] = {a, b}; - cpHashValue arbHashID = CP_HASH_PAIR((cpHashValue)a, (cpHashValue)b); - cpHashSetRemove(space->cachedArbiters, arbHashID, shape_pair); - cpArrayDeleteObj(space->arbiters, arb); -} - -static inline cpArray * -cpSpaceArrayForBodyType(cpSpace *space, cpBodyType type) -{ - return (type == CP_BODY_TYPE_STATIC ? space->staticBodies : space->dynamicBodies); -} - -void cpShapeUpdateFunc(cpShape *shape, void *unused); -cpCollisionID cpSpaceCollideShapes(cpShape *a, cpShape *b, cpCollisionID id, cpSpace *space); - - -//MARK: Foreach loops - -static inline cpConstraint * -cpConstraintNext(cpConstraint *node, cpBody *body) -{ - return (node->a == body ? node->next_a : node->next_b); -} - -#define CP_BODY_FOREACH_CONSTRAINT(bdy, var)\ - for(cpConstraint *var = bdy->constraintList; var; var = cpConstraintNext(var, bdy)) - -static inline cpArbiter * -cpArbiterNext(cpArbiter *node, cpBody *body) -{ - return (node->body_a == body ? node->thread_a.next : node->thread_b.next); -} - -#define CP_BODY_FOREACH_ARBITER(bdy, var)\ - for(cpArbiter *var = bdy->arbiterList; var; var = cpArbiterNext(var, bdy)) - -#define CP_BODY_FOREACH_SHAPE(body, var)\ - for(cpShape *var = body->shapeList; var; var = var->next) - -#define CP_BODY_FOREACH_COMPONENT(root, var)\ - for(cpBody *var = root; var; var = var->sleeping.next) - -#endif diff --git a/apecs-physics/Chipmunk2D/include/chipmunk/chipmunk_structs.h b/apecs-physics/Chipmunk2D/include/chipmunk/chipmunk_structs.h deleted file mode 100644 index dac8166..0000000 --- a/apecs-physics/Chipmunk2D/include/chipmunk/chipmunk_structs.h +++ /dev/null @@ -1,450 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -// All of the struct definitions for Chipmunk should be considered part of the private API. -// However, it is very valuable to know the struct sizes for preallocating memory. - -#ifndef CHIPMUNK_STRUCTS_H -#define CHIPMUNK_STRUCTS_H - -#include "chipmunk.h" - -struct cpArray { - int num, max; - void **arr; -}; - -struct cpBody { - // Integration functions - cpBodyVelocityFunc velocity_func; - cpBodyPositionFunc position_func; - - // mass and it's inverse - cpFloat m; - cpFloat m_inv; - - // moment of inertia and it's inverse - cpFloat i; - cpFloat i_inv; - - // center of gravity - cpVect cog; - - // position, velocity, force - cpVect p; - cpVect v; - cpVect f; - - // Angle, angular velocity, torque (radians) - cpFloat a; - cpFloat w; - cpFloat t; - - cpTransform transform; - - cpDataPointer userData; - - // "pseudo-velocities" used for eliminating overlap. - // Erin Catto has some papers that talk about what these are. - cpVect v_bias; - cpFloat w_bias; - - cpSpace *space; - - cpShape *shapeList; - cpArbiter *arbiterList; - cpConstraint *constraintList; - - struct { - cpBody *root; - cpBody *next; - cpFloat idleTime; - } sleeping; -}; - -enum cpArbiterState { - // Arbiter is active and its the first collision. - CP_ARBITER_STATE_FIRST_COLLISION, - // Arbiter is active and its not the first collision. - CP_ARBITER_STATE_NORMAL, - // Collision has been explicitly ignored. - // Either by returning false from a begin collision handler or calling cpArbiterIgnore(). - CP_ARBITER_STATE_IGNORE, - // Collison is no longer active. A space will cache an arbiter for up to cpSpace.collisionPersistence more steps. - CP_ARBITER_STATE_CACHED, - // Collison arbiter is invalid because one of the shapes was removed. - CP_ARBITER_STATE_INVALIDATED, -}; - -struct cpArbiterThread { - struct cpArbiter *next, *prev; -}; - -struct cpContact { - cpVect r1, r2; - - cpFloat nMass, tMass; - cpFloat bounce; // TODO: look for an alternate bounce solution. - - cpFloat jnAcc, jtAcc, jBias; - cpFloat bias; - - cpHashValue hash; -}; - -struct cpCollisionInfo { - const cpShape *a, *b; - cpCollisionID id; - - cpVect n; - - int count; - // TODO Should this be a unique struct type? - struct cpContact *arr; -}; - -struct cpArbiter { - cpFloat e; - cpFloat u; - cpVect surface_vr; - - cpDataPointer data; - - const cpShape *a, *b; - cpBody *body_a, *body_b; - struct cpArbiterThread thread_a, thread_b; - - int count; - struct cpContact *contacts; - cpVect n; - - // Regular, wildcard A and wildcard B collision handlers. - cpCollisionHandler *handler, *handlerA, *handlerB; - cpBool swapped; - - cpTimestamp stamp; - enum cpArbiterState state; -}; - -struct cpShapeMassInfo { - cpFloat m; - cpFloat i; - cpVect cog; - cpFloat area; -}; - -typedef enum cpShapeType{ - CP_CIRCLE_SHAPE, - CP_SEGMENT_SHAPE, - CP_POLY_SHAPE, - CP_NUM_SHAPES -} cpShapeType; - -typedef cpBB (*cpShapeCacheDataImpl)(cpShape *shape, cpTransform transform); -typedef void (*cpShapeDestroyImpl)(cpShape *shape); -typedef void (*cpShapePointQueryImpl)(const cpShape *shape, cpVect p, cpPointQueryInfo *info); -typedef void (*cpShapeSegmentQueryImpl)(const cpShape *shape, cpVect a, cpVect b, cpFloat radius, cpSegmentQueryInfo *info); - -typedef struct cpShapeClass cpShapeClass; - -struct cpShapeClass { - cpShapeType type; - - cpShapeCacheDataImpl cacheData; - cpShapeDestroyImpl destroy; - cpShapePointQueryImpl pointQuery; - cpShapeSegmentQueryImpl segmentQuery; -}; - -struct cpShape { - const cpShapeClass *klass; - - cpSpace *space; - cpBody *body; - struct cpShapeMassInfo massInfo; - cpBB bb; - - cpBool sensor; - - cpFloat e; - cpFloat u; - cpVect surfaceV; - - cpDataPointer userData; - - cpCollisionType type; - cpShapeFilter filter; - - cpShape *next; - cpShape *prev; - - cpHashValue hashid; -}; - -struct cpCircleShape { - cpShape shape; - - cpVect c, tc; - cpFloat r; -}; - -struct cpSegmentShape { - cpShape shape; - - cpVect a, b, n; - cpVect ta, tb, tn; - cpFloat r; - - cpVect a_tangent, b_tangent; -}; - -struct cpSplittingPlane { - cpVect v0, n; -}; - -#define CP_POLY_SHAPE_INLINE_ALLOC 6 - -struct cpPolyShape { - cpShape shape; - - cpFloat r; - - int count; - // The untransformed planes are appended at the end of the transformed planes. - struct cpSplittingPlane *planes; - - // Allocate a small number of splitting planes internally for simple poly. - struct cpSplittingPlane _planes[2*CP_POLY_SHAPE_INLINE_ALLOC]; -}; - -typedef void (*cpConstraintPreStepImpl)(cpConstraint *constraint, cpFloat dt); -typedef void (*cpConstraintApplyCachedImpulseImpl)(cpConstraint *constraint, cpFloat dt_coef); -typedef void (*cpConstraintApplyImpulseImpl)(cpConstraint *constraint, cpFloat dt); -typedef cpFloat (*cpConstraintGetImpulseImpl)(cpConstraint *constraint); - -typedef struct cpConstraintClass { - cpConstraintPreStepImpl preStep; - cpConstraintApplyCachedImpulseImpl applyCachedImpulse; - cpConstraintApplyImpulseImpl applyImpulse; - cpConstraintGetImpulseImpl getImpulse; -} cpConstraintClass; - -struct cpConstraint { - const cpConstraintClass *klass; - - cpSpace *space; - - cpBody *a, *b; - cpConstraint *next_a, *next_b; - - cpFloat maxForce; - cpFloat errorBias; - cpFloat maxBias; - - cpBool collideBodies; - - cpConstraintPreSolveFunc preSolve; - cpConstraintPostSolveFunc postSolve; - - cpDataPointer userData; -}; - -struct cpPinJoint { - cpConstraint constraint; - cpVect anchorA, anchorB; - cpFloat dist; - - cpVect r1, r2; - cpVect n; - cpFloat nMass; - - cpFloat jnAcc; - cpFloat bias; -}; - -struct cpSlideJoint { - cpConstraint constraint; - cpVect anchorA, anchorB; - cpFloat min, max; - - cpVect r1, r2; - cpVect n; - cpFloat nMass; - - cpFloat jnAcc; - cpFloat bias; -}; - -struct cpPivotJoint { - cpConstraint constraint; - cpVect anchorA, anchorB; - - cpVect r1, r2; - cpMat2x2 k; - - cpVect jAcc; - cpVect bias; -}; - -struct cpGrooveJoint { - cpConstraint constraint; - cpVect grv_n, grv_a, grv_b; - cpVect anchorB; - - cpVect grv_tn; - cpFloat clamp; - cpVect r1, r2; - cpMat2x2 k; - - cpVect jAcc; - cpVect bias; -}; - -struct cpDampedSpring { - cpConstraint constraint; - cpVect anchorA, anchorB; - cpFloat restLength; - cpFloat stiffness; - cpFloat damping; - cpDampedSpringForceFunc springForceFunc; - - cpFloat target_vrn; - cpFloat v_coef; - - cpVect r1, r2; - cpFloat nMass; - cpVect n; - - cpFloat jAcc; -}; - -struct cpDampedRotarySpring { - cpConstraint constraint; - cpFloat restAngle; - cpFloat stiffness; - cpFloat damping; - cpDampedRotarySpringTorqueFunc springTorqueFunc; - - cpFloat target_wrn; - cpFloat w_coef; - - cpFloat iSum; - cpFloat jAcc; -}; - -struct cpRotaryLimitJoint { - cpConstraint constraint; - cpFloat min, max; - - cpFloat iSum; - - cpFloat bias; - cpFloat jAcc; -}; - -struct cpRatchetJoint { - cpConstraint constraint; - cpFloat angle, phase, ratchet; - - cpFloat iSum; - - cpFloat bias; - cpFloat jAcc; -}; - -struct cpGearJoint { - cpConstraint constraint; - cpFloat phase, ratio; - cpFloat ratio_inv; - - cpFloat iSum; - - cpFloat bias; - cpFloat jAcc; -}; - -struct cpSimpleMotor { - cpConstraint constraint; - cpFloat rate; - - cpFloat iSum; - - cpFloat jAcc; -}; - -typedef struct cpContactBufferHeader cpContactBufferHeader; -typedef void (*cpSpaceArbiterApplyImpulseFunc)(cpArbiter *arb); - -struct cpSpace { - int iterations; - - cpVect gravity; - cpFloat damping; - - cpFloat idleSpeedThreshold; - cpFloat sleepTimeThreshold; - - cpFloat collisionSlop; - cpFloat collisionBias; - cpTimestamp collisionPersistence; - - cpDataPointer userData; - - cpTimestamp stamp; - cpFloat curr_dt; - - cpArray *dynamicBodies; - cpArray *staticBodies; - cpArray *rousedBodies; - cpArray *sleepingComponents; - - cpHashValue shapeIDCounter; - cpSpatialIndex *staticShapes; - cpSpatialIndex *dynamicShapes; - - cpArray *constraints; - - cpArray *arbiters; - cpContactBufferHeader *contactBuffersHead; - cpHashSet *cachedArbiters; - cpArray *pooledArbiters; - - cpArray *allocatedBuffers; - unsigned int locked; - - cpBool usesWildcards; - cpHashSet *collisionHandlers; - cpCollisionHandler defaultHandler; - - cpBool skipPostStep; - cpArray *postStepCallbacks; - - cpBody *staticBody; - cpBody _staticBody; -}; - -typedef struct cpPostStepCallback { - cpPostStepFunc func; - void *key; - void *data; -} cpPostStepCallback; - -#endif diff --git a/apecs-physics/Chipmunk2D/include/chipmunk/chipmunk_types.h b/apecs-physics/Chipmunk2D/include/chipmunk/chipmunk_types.h deleted file mode 100644 index 9544da8..0000000 --- a/apecs-physics/Chipmunk2D/include/chipmunk/chipmunk_types.h +++ /dev/null @@ -1,268 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef CHIPMUNK_TYPES_H -#define CHIPMUNK_TYPES_H - -#include -#include -#include - -#ifdef __APPLE__ - #include "TargetConditionals.h" -#endif - -// Use CGTypes by default on iOS and Mac. -// Also enables usage of doubles on 64 bit. -// Performance is usually very comparable when the CPU cache is well utilised. -#if (TARGET_OS_IPHONE || TARGET_OS_MAC) && (!defined CP_USE_CGTYPES) - #define CP_USE_CGTYPES 1 -#endif - -#if CP_USE_CGTYPES - #if TARGET_OS_IPHONE - #include - #include - #elif TARGET_OS_MAC - #include - #endif - - #if defined(__LP64__) && __LP64__ - #define CP_USE_DOUBLES 1 - #else - #define CP_USE_DOUBLES 0 - #endif -#endif - -#ifndef CP_USE_DOUBLES - // Use doubles by default for higher precision. - #define CP_USE_DOUBLES 1 -#endif - -/// @defgroup basicTypes Basic Types -/// Most of these types can be configured at compile time. -/// @{ - -#if CP_USE_DOUBLES -/// Chipmunk's floating point type. -/// Can be reconfigured at compile time. - typedef double cpFloat; - #define cpfsqrt sqrt - #define cpfsin sin - #define cpfcos cos - #define cpfacos acos - #define cpfatan2 atan2 - #define cpfmod fmod - #define cpfexp exp - #define cpfpow pow - #define cpffloor floor - #define cpfceil ceil - #define CPFLOAT_MIN DBL_MIN -#else - typedef float cpFloat; - #define cpfsqrt sqrtf - #define cpfsin sinf - #define cpfcos cosf - #define cpfacos acosf - #define cpfatan2 atan2f - #define cpfmod fmodf - #define cpfexp expf - #define cpfpow powf - #define cpffloor floorf - #define cpfceil ceilf - #define CPFLOAT_MIN FLT_MIN -#endif - -#ifndef INFINITY - #ifdef _MSC_VER - union MSVC_EVIL_FLOAT_HACK - { - unsigned __int8 Bytes[4]; - float Value; - }; - static union MSVC_EVIL_FLOAT_HACK INFINITY_HACK = {{0x00, 0x00, 0x80, 0x7F}}; - #define INFINITY (INFINITY_HACK.Value) - #endif - - #ifdef __GNUC__ - #define INFINITY (__builtin_inf()) - #endif - - #ifndef INFINITY - #define INFINITY (1e1000) - #endif -#endif - - -#define CP_PI ((cpFloat)3.14159265358979323846264338327950288) - - -/// Return the max of two cpFloats. -static inline cpFloat cpfmax(cpFloat a, cpFloat b) -{ - return (a > b) ? a : b; -} - -/// Return the min of two cpFloats. -static inline cpFloat cpfmin(cpFloat a, cpFloat b) -{ - return (a < b) ? a : b; -} - -/// Return the absolute value of a cpFloat. -static inline cpFloat cpfabs(cpFloat f) -{ - return (f < 0) ? -f : f; -} - -/// Clamp @c f to be between @c min and @c max. -static inline cpFloat cpfclamp(cpFloat f, cpFloat min, cpFloat max) -{ - return cpfmin(cpfmax(f, min), max); -} - -/// Clamp @c f to be between 0 and 1. -static inline cpFloat cpfclamp01(cpFloat f) -{ - return cpfmax(0.0f, cpfmin(f, 1.0f)); -} - - - -/// Linearly interpolate (or extrapolate) between @c f1 and @c f2 by @c t percent. -static inline cpFloat cpflerp(cpFloat f1, cpFloat f2, cpFloat t) -{ - return f1*(1.0f - t) + f2*t; -} - -/// Linearly interpolate from @c f1 to @c f2 by no more than @c d. -static inline cpFloat cpflerpconst(cpFloat f1, cpFloat f2, cpFloat d) -{ - return f1 + cpfclamp(f2 - f1, -d, d); -} - -/// Hash value type. -#ifdef CP_HASH_VALUE_TYPE - typedef CP_HASH_VALUE_TYPE cpHashValue; -#else - typedef uintptr_t cpHashValue; -#endif - -/// Type used internally to cache colliding object info for cpCollideShapes(). -/// Should be at least 32 bits. -typedef uint32_t cpCollisionID; - -// Oh C, how we love to define our own boolean types to get compiler compatibility -/// Chipmunk's boolean type. -#ifdef CP_BOOL_TYPE - typedef CP_BOOL_TYPE cpBool; -#else - typedef unsigned char cpBool; -#endif - -#ifndef cpTrue -/// true value. - #define cpTrue 1 -#endif - -#ifndef cpFalse -/// false value. - #define cpFalse 0 -#endif - -#ifdef CP_DATA_POINTER_TYPE - typedef CP_DATA_POINTER_TYPE cpDataPointer; -#else -/// Type used for user data pointers. - typedef void * cpDataPointer; -#endif - -#ifdef CP_COLLISION_TYPE_TYPE - typedef CP_COLLISION_TYPE_TYPE cpCollisionType; -#else -/// Type used for cpSpace.collision_type. - typedef uintptr_t cpCollisionType; -#endif - -#ifdef CP_GROUP_TYPE - typedef CP_GROUP_TYPE cpGroup; -#else -/// Type used for cpShape.group. - typedef uintptr_t cpGroup; -#endif - -#ifdef CP_BITMASK_TYPE - typedef CP_BITMASK_TYPE cpBitmask; -#else -/// Type used for cpShapeFilter category and mask. - typedef unsigned int cpBitmask; -#endif - -#ifdef CP_TIMESTAMP_TYPE - typedef CP_TIMESTAMP_TYPE cpTimestamp; -#else -/// Type used for various timestamps in Chipmunk. - typedef unsigned int cpTimestamp; -#endif - -#ifndef CP_NO_GROUP -/// Value for cpShape.group signifying that a shape is in no group. - #define CP_NO_GROUP ((cpGroup)0) -#endif - -#ifndef CP_ALL_CATEGORIES -/// Value for cpShape.layers signifying that a shape is in every layer. - #define CP_ALL_CATEGORIES (~(cpBitmask)0) -#endif - -#ifndef CP_WILDCARD_COLLISION_TYPE -/// cpCollisionType value internally reserved for hashing wildcard handlers. - #define CP_WILDCARD_COLLISION_TYPE (~(cpCollisionType)0) -#endif - -/// @} - -// CGPoints are structurally the same, and allow -// easy interoperability with other Cocoa libraries -#if CP_USE_CGTYPES - typedef CGPoint cpVect; -#else -/// Chipmunk's 2D vector type. -/// @addtogroup cpVect - typedef struct cpVect{cpFloat x,y;} cpVect; -#endif - -#if CP_USE_CGTYPES - typedef CGAffineTransform cpTransform; -#else - /// Column major affine transform. - typedef struct cpTransform { - cpFloat a, b, c, d, tx, ty; - } cpTransform; -#endif - -// NUKE -typedef struct cpMat2x2 { - // Row major [[a, b][c d]] - cpFloat a, b, c, d; -} cpMat2x2; - -#endif diff --git a/apecs-physics/Chipmunk2D/include/chipmunk/chipmunk_unsafe.h b/apecs-physics/Chipmunk2D/include/chipmunk/chipmunk_unsafe.h deleted file mode 100644 index 990bd01..0000000 --- a/apecs-physics/Chipmunk2D/include/chipmunk/chipmunk_unsafe.h +++ /dev/null @@ -1,66 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/* This header defines a number of "unsafe" operations on Chipmunk objects. - * In this case "unsafe" is referring to operations which may reduce the - * physical accuracy or numerical stability of the simulation, but will not - * cause crashes. - * - * The prime example is mutating collision shapes. Chipmunk does not support - * this directly. Mutating shapes using this API will caused objects in contact - * to be pushed apart using Chipmunk's overlap solver, but not using real - * persistent velocities. Probably not what you meant, but perhaps close enough. - */ - -/// @defgroup unsafe Chipmunk Unsafe Shape Operations -/// These functions are used for mutating collision shapes. -/// Chipmunk does not have any way to get velocity information on changing shapes, -/// so the results will be unrealistic. You must explicity include the chipmunk_unsafe.h header to use them. -/// @{ - -#ifndef CHIPMUNK_UNSAFE_H -#define CHIPMUNK_UNSAFE_H - -#ifdef __cplusplus -extern "C" { -#endif - -/// Set the radius of a circle shape. -CP_EXPORT void cpCircleShapeSetRadius(cpShape *shape, cpFloat radius); -/// Set the offset of a circle shape. -CP_EXPORT void cpCircleShapeSetOffset(cpShape *shape, cpVect offset); - -/// Set the endpoints of a segment shape. -CP_EXPORT void cpSegmentShapeSetEndpoints(cpShape *shape, cpVect a, cpVect b); -/// Set the radius of a segment shape. -CP_EXPORT void cpSegmentShapeSetRadius(cpShape *shape, cpFloat radius); - -/// Set the vertexes of a poly shape. -CP_EXPORT void cpPolyShapeSetVerts(cpShape *shape, int count, cpVect *verts, cpTransform transform); -CP_EXPORT void cpPolyShapeSetVertsRaw(cpShape *shape, int count, cpVect *verts); -/// Set the radius of a poly shape. -CP_EXPORT void cpPolyShapeSetRadius(cpShape *shape, cpFloat radius); - -#ifdef __cplusplus -} -#endif -#endif -/// @} diff --git a/apecs-physics/Chipmunk2D/include/chipmunk/cpArbiter.h b/apecs-physics/Chipmunk2D/include/chipmunk/cpArbiter.h deleted file mode 100644 index 1dc130a..0000000 --- a/apecs-physics/Chipmunk2D/include/chipmunk/cpArbiter.h +++ /dev/null @@ -1,145 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/// @defgroup cpArbiter cpArbiter -/// The cpArbiter struct tracks pairs of colliding shapes. -/// They are also used in conjuction with collision handler callbacks -/// allowing you to retrieve information on the collision or change it. -/// A unique arbiter value is used for each pair of colliding objects. It persists until the shapes separate. -/// @{ - -#define CP_MAX_CONTACTS_PER_ARBITER 2 - -/// Get the restitution (elasticity) that will be applied to the pair of colliding objects. -CP_EXPORT cpFloat cpArbiterGetRestitution(const cpArbiter *arb); -/// Override the restitution (elasticity) that will be applied to the pair of colliding objects. -CP_EXPORT void cpArbiterSetRestitution(cpArbiter *arb, cpFloat restitution); -/// Get the friction coefficient that will be applied to the pair of colliding objects. -CP_EXPORT cpFloat cpArbiterGetFriction(const cpArbiter *arb); -/// Override the friction coefficient that will be applied to the pair of colliding objects. -CP_EXPORT void cpArbiterSetFriction(cpArbiter *arb, cpFloat friction); - -// Get the relative surface velocity of the two shapes in contact. -CP_EXPORT cpVect cpArbiterGetSurfaceVelocity(cpArbiter *arb); - -// Override the relative surface velocity of the two shapes in contact. -// By default this is calculated to be the difference of the two surface velocities clamped to the tangent plane. -CP_EXPORT void cpArbiterSetSurfaceVelocity(cpArbiter *arb, cpVect vr); - -/// Get the user data pointer associated with this pair of colliding objects. -CP_EXPORT cpDataPointer cpArbiterGetUserData(const cpArbiter *arb); -/// Set a user data point associated with this pair of colliding objects. -/// If you need to perform any cleanup for this pointer, you must do it yourself, in the separate callback for instance. -CP_EXPORT void cpArbiterSetUserData(cpArbiter *arb, cpDataPointer userData); - -/// Calculate the total impulse including the friction that was applied by this arbiter. -/// This function should only be called from a post-solve, post-step or cpBodyEachArbiter callback. -CP_EXPORT cpVect cpArbiterTotalImpulse(const cpArbiter *arb); -/// Calculate the amount of energy lost in a collision including static, but not dynamic friction. -/// This function should only be called from a post-solve, post-step or cpBodyEachArbiter callback. -CP_EXPORT cpFloat cpArbiterTotalKE(const cpArbiter *arb); - -/// Mark a collision pair to be ignored until the two objects separate. -/// Pre-solve and post-solve callbacks will not be called, but the separate callback will be called. -CP_EXPORT cpBool cpArbiterIgnore(cpArbiter *arb); - -/// Return the colliding shapes involved for this arbiter. -/// The order of their cpSpace.collision_type values will match -/// the order set when the collision handler was registered. -CP_EXPORT void cpArbiterGetShapes(const cpArbiter *arb, cpShape **a, cpShape **b); - -/// A macro shortcut for defining and retrieving the shapes from an arbiter. -#define CP_ARBITER_GET_SHAPES(__arb__, __a__, __b__) cpShape *__a__, *__b__; cpArbiterGetShapes(__arb__, &__a__, &__b__); - -/// Return the colliding bodies involved for this arbiter. -/// The order of the cpSpace.collision_type the bodies are associated with values will match -/// the order set when the collision handler was registered. -CP_EXPORT void cpArbiterGetBodies(const cpArbiter *arb, cpBody **a, cpBody **b); - -/// A macro shortcut for defining and retrieving the bodies from an arbiter. -#define CP_ARBITER_GET_BODIES(__arb__, __a__, __b__) cpBody *__a__, *__b__; cpArbiterGetBodies(__arb__, &__a__, &__b__); - -/// A struct that wraps up the important collision data for an arbiter. -struct cpContactPointSet { - /// The number of contact points in the set. - int count; - - /// The normal of the collision. - cpVect normal; - - /// The array of contact points. - struct { - /// The position of the contact on the surface of each shape. - cpVect pointA, pointB; - /// Penetration distance of the two shapes. Overlapping means it will be negative. - /// This value is calculated as cpvdot(cpvsub(point2, point1), normal) and is ignored by cpArbiterSetContactPointSet(). - cpFloat distance; - } points[CP_MAX_CONTACTS_PER_ARBITER]; -}; - -/// Return a contact set from an arbiter. -CP_EXPORT cpContactPointSet cpArbiterGetContactPointSet(const cpArbiter *arb); - -/// Replace the contact point set for an arbiter. -/// This can be a very powerful feature, but use it with caution! -CP_EXPORT void cpArbiterSetContactPointSet(cpArbiter *arb, cpContactPointSet *set); - -/// Returns true if this is the first step a pair of objects started colliding. -CP_EXPORT cpBool cpArbiterIsFirstContact(const cpArbiter *arb); -/// Returns true if the separate callback is due to a shape being removed from the space. -CP_EXPORT cpBool cpArbiterIsRemoval(const cpArbiter *arb); - -/// Get the number of contact points for this arbiter. -CP_EXPORT int cpArbiterGetCount(const cpArbiter *arb); -/// Get the normal of the collision. -CP_EXPORT cpVect cpArbiterGetNormal(const cpArbiter *arb); -/// Get the position of the @c ith contact point on the surface of the first shape. -CP_EXPORT cpVect cpArbiterGetPointA(const cpArbiter *arb, int i); -/// Get the position of the @c ith contact point on the surface of the second shape. -CP_EXPORT cpVect cpArbiterGetPointB(const cpArbiter *arb, int i); -/// Get the depth of the @c ith contact point. -CP_EXPORT cpFloat cpArbiterGetDepth(const cpArbiter *arb, int i); - -/// If you want a custom callback to invoke the wildcard callback for the first collision type, you must call this function explicitly. -/// You must decide how to handle the wildcard's return value since it may disagree with the other wildcard handler's return value or your own. -CP_EXPORT cpBool cpArbiterCallWildcardBeginA(cpArbiter *arb, cpSpace *space); -/// If you want a custom callback to invoke the wildcard callback for the second collision type, you must call this function explicitly. -/// You must decide how to handle the wildcard's return value since it may disagree with the other wildcard handler's return value or your own. -CP_EXPORT cpBool cpArbiterCallWildcardBeginB(cpArbiter *arb, cpSpace *space); - -/// If you want a custom callback to invoke the wildcard callback for the first collision type, you must call this function explicitly. -/// You must decide how to handle the wildcard's return value since it may disagree with the other wildcard handler's return value or your own. -CP_EXPORT cpBool cpArbiterCallWildcardPreSolveA(cpArbiter *arb, cpSpace *space); -/// If you want a custom callback to invoke the wildcard callback for the second collision type, you must call this function explicitly. -/// You must decide how to handle the wildcard's return value since it may disagree with the other wildcard handler's return value or your own. -CP_EXPORT cpBool cpArbiterCallWildcardPreSolveB(cpArbiter *arb, cpSpace *space); - -/// If you want a custom callback to invoke the wildcard callback for the first collision type, you must call this function explicitly. -CP_EXPORT void cpArbiterCallWildcardPostSolveA(cpArbiter *arb, cpSpace *space); -/// If you want a custom callback to invoke the wildcard callback for the second collision type, you must call this function explicitly. -CP_EXPORT void cpArbiterCallWildcardPostSolveB(cpArbiter *arb, cpSpace *space); - -/// If you want a custom callback to invoke the wildcard callback for the first collision type, you must call this function explicitly. -CP_EXPORT void cpArbiterCallWildcardSeparateA(cpArbiter *arb, cpSpace *space); -/// If you want a custom callback to invoke the wildcard callback for the second collision type, you must call this function explicitly. -CP_EXPORT void cpArbiterCallWildcardSeparateB(cpArbiter *arb, cpSpace *space); - -/// @} diff --git a/apecs-physics/Chipmunk2D/include/chipmunk/cpBB.h b/apecs-physics/Chipmunk2D/include/chipmunk/cpBB.h deleted file mode 100644 index 8fc8704..0000000 --- a/apecs-physics/Chipmunk2D/include/chipmunk/cpBB.h +++ /dev/null @@ -1,187 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef CHIPMUNK_BB_H -#define CHIPMUNK_BB_H - -#include "chipmunk_types.h" -#include "cpVect.h" - -/// @defgroup cpBBB cpBB -/// Chipmunk's axis-aligned 2D bounding box type along with a few handy routines. -/// @{ - -/// Chipmunk's axis-aligned 2D bounding box type. (left, bottom, right, top) -typedef struct cpBB{ - cpFloat l, b, r ,t; -} cpBB; - -/// Convenience constructor for cpBB structs. -static inline cpBB cpBBNew(const cpFloat l, const cpFloat b, const cpFloat r, const cpFloat t) -{ - cpBB bb = {l, b, r, t}; - return bb; -} - -/// Constructs a cpBB centered on a point with the given extents (half sizes). -static inline cpBB -cpBBNewForExtents(const cpVect c, const cpFloat hw, const cpFloat hh) -{ - return cpBBNew(c.x - hw, c.y - hh, c.x + hw, c.y + hh); -} - -/// Constructs a cpBB for a circle with the given position and radius. -static inline cpBB cpBBNewForCircle(const cpVect p, const cpFloat r) -{ - return cpBBNewForExtents(p, r, r); -} - -/// Returns true if @c a and @c b intersect. -static inline cpBool cpBBIntersects(const cpBB a, const cpBB b) -{ - return (a.l <= b.r && b.l <= a.r && a.b <= b.t && b.b <= a.t); -} - -/// Returns true if @c other lies completely within @c bb. -static inline cpBool cpBBContainsBB(const cpBB bb, const cpBB other) -{ - return (bb.l <= other.l && bb.r >= other.r && bb.b <= other.b && bb.t >= other.t); -} - -/// Returns true if @c bb contains @c v. -static inline cpBool cpBBContainsVect(const cpBB bb, const cpVect v) -{ - return (bb.l <= v.x && bb.r >= v.x && bb.b <= v.y && bb.t >= v.y); -} - -/// Returns a bounding box that holds both bounding boxes. -static inline cpBB cpBBMerge(const cpBB a, const cpBB b){ - return cpBBNew( - cpfmin(a.l, b.l), - cpfmin(a.b, b.b), - cpfmax(a.r, b.r), - cpfmax(a.t, b.t) - ); -} - -/// Returns a bounding box that holds both @c bb and @c v. -static inline cpBB cpBBExpand(const cpBB bb, const cpVect v){ - return cpBBNew( - cpfmin(bb.l, v.x), - cpfmin(bb.b, v.y), - cpfmax(bb.r, v.x), - cpfmax(bb.t, v.y) - ); -} - -/// Returns the center of a bounding box. -static inline cpVect -cpBBCenter(cpBB bb) -{ - return cpvlerp(cpv(bb.l, bb.b), cpv(bb.r, bb.t), 0.5f); -} - -/// Returns the area of the bounding box. -static inline cpFloat cpBBArea(cpBB bb) -{ - return (bb.r - bb.l)*(bb.t - bb.b); -} - -/// Merges @c a and @c b and returns the area of the merged bounding box. -static inline cpFloat cpBBMergedArea(cpBB a, cpBB b) -{ - return (cpfmax(a.r, b.r) - cpfmin(a.l, b.l))*(cpfmax(a.t, b.t) - cpfmin(a.b, b.b)); -} - -/// Returns the fraction along the segment query the cpBB is hit. Returns INFINITY if it doesn't hit. -static inline cpFloat cpBBSegmentQuery(cpBB bb, cpVect a, cpVect b) -{ - cpVect delta = cpvsub(b, a); - cpFloat tmin = -INFINITY, tmax = INFINITY; - - if(delta.x == 0.0f){ - if(a.x < bb.l || bb.r < a.x) return INFINITY; - } else { - cpFloat t1 = (bb.l - a.x)/delta.x; - cpFloat t2 = (bb.r - a.x)/delta.x; - tmin = cpfmax(tmin, cpfmin(t1, t2)); - tmax = cpfmin(tmax, cpfmax(t1, t2)); - } - - if(delta.y == 0.0f){ - if(a.y < bb.b || bb.t < a.y) return INFINITY; - } else { - cpFloat t1 = (bb.b - a.y)/delta.y; - cpFloat t2 = (bb.t - a.y)/delta.y; - tmin = cpfmax(tmin, cpfmin(t1, t2)); - tmax = cpfmin(tmax, cpfmax(t1, t2)); - } - - if(tmin <= tmax && 0.0f <= tmax && tmin <= 1.0f){ - return cpfmax(tmin, 0.0f); - } else { - return INFINITY; - } -} - -/// Return true if the bounding box intersects the line segment with ends @c a and @c b. -static inline cpBool cpBBIntersectsSegment(cpBB bb, cpVect a, cpVect b) -{ - return (cpBBSegmentQuery(bb, a, b) != INFINITY); -} - -/// Clamp a vector to a bounding box. -static inline cpVect -cpBBClampVect(const cpBB bb, const cpVect v) -{ - return cpv(cpfclamp(v.x, bb.l, bb.r), cpfclamp(v.y, bb.b, bb.t)); -} - -/// Wrap a vector to a bounding box. -static inline cpVect -cpBBWrapVect(const cpBB bb, const cpVect v) -{ - cpFloat dx = cpfabs(bb.r - bb.l); - cpFloat modx = cpfmod(v.x - bb.l, dx); - cpFloat x = (modx > 0.0f) ? modx : modx + dx; - - cpFloat dy = cpfabs(bb.t - bb.b); - cpFloat mody = cpfmod(v.y - bb.b, dy); - cpFloat y = (mody > 0.0f) ? mody : mody + dy; - - return cpv(x + bb.l, y + bb.b); -} - -/// Returns a bounding box offseted by @c v. -static inline cpBB -cpBBOffset(const cpBB bb, const cpVect v) -{ - return cpBBNew( - bb.l + v.x, - bb.b + v.y, - bb.r + v.x, - bb.t + v.y - ); -} - -///@} - -#endif diff --git a/apecs-physics/Chipmunk2D/include/chipmunk/cpBody.h b/apecs-physics/Chipmunk2D/include/chipmunk/cpBody.h deleted file mode 100644 index 7e6943d..0000000 --- a/apecs-physics/Chipmunk2D/include/chipmunk/cpBody.h +++ /dev/null @@ -1,189 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/// @defgroup cpBody cpBody -/// Chipmunk's rigid body type. Rigid bodies hold the physical properties of an object like -/// it's mass, and position and velocity of it's center of gravity. They don't have an shape on their own. -/// They are given a shape by creating collision shapes (cpShape) that point to the body. -/// @{ - -typedef enum cpBodyType { - /// A dynamic body is one that is affected by gravity, forces, and collisions. - /// This is the default body type. - CP_BODY_TYPE_DYNAMIC, - /// A kinematic body is an infinite mass, user controlled body that is not affected by gravity, forces or collisions. - /// Instead the body only moves based on it's velocity. - /// Dynamic bodies collide normally with kinematic bodies, though the kinematic body will be unaffected. - /// Collisions between two kinematic bodies, or a kinematic body and a static body produce collision callbacks, but no collision response. - CP_BODY_TYPE_KINEMATIC, - /// A static body is a body that never (or rarely) moves. If you move a static body, you must call one of the cpSpaceReindex*() functions. - /// Chipmunk uses this information to optimize the collision detection. - /// Static bodies do not produce collision callbacks when colliding with other static bodies. - CP_BODY_TYPE_STATIC, -} cpBodyType; - -/// Rigid body velocity update function type. -typedef void (*cpBodyVelocityFunc)(cpBody *body, cpVect gravity, cpFloat damping, cpFloat dt); -/// Rigid body position update function type. -typedef void (*cpBodyPositionFunc)(cpBody *body, cpFloat dt); - -/// Allocate a cpBody. -CP_EXPORT cpBody* cpBodyAlloc(void); -/// Initialize a cpBody. -CP_EXPORT cpBody* cpBodyInit(cpBody *body, cpFloat mass, cpFloat moment); -/// Allocate and initialize a cpBody. -CP_EXPORT cpBody* cpBodyNew(cpFloat mass, cpFloat moment); - -/// Allocate and initialize a cpBody, and set it as a kinematic body. -CP_EXPORT cpBody* cpBodyNewKinematic(void); -/// Allocate and initialize a cpBody, and set it as a static body. -CP_EXPORT cpBody* cpBodyNewStatic(void); - -/// Destroy a cpBody. -CP_EXPORT void cpBodyDestroy(cpBody *body); -/// Destroy and free a cpBody. -CP_EXPORT void cpBodyFree(cpBody *body); - -// Defined in cpSpace.c -/// Wake up a sleeping or idle body. -CP_EXPORT void cpBodyActivate(cpBody *body); -/// Wake up any sleeping or idle bodies touching a static body. -CP_EXPORT void cpBodyActivateStatic(cpBody *body, cpShape *filter); - -/// Force a body to fall asleep immediately. -CP_EXPORT void cpBodySleep(cpBody *body); -/// Force a body to fall asleep immediately along with other bodies in a group. -CP_EXPORT void cpBodySleepWithGroup(cpBody *body, cpBody *group); - -/// Returns true if the body is sleeping. -CP_EXPORT cpBool cpBodyIsSleeping(const cpBody *body); - -/// Get the type of the body. -CP_EXPORT cpBodyType cpBodyGetType(cpBody *body); -/// Set the type of the body. -CP_EXPORT void cpBodySetType(cpBody *body, cpBodyType type); - -/// Get the space this body is added to. -CP_EXPORT cpSpace* cpBodyGetSpace(const cpBody *body); - -/// Get the mass of the body. -CP_EXPORT cpFloat cpBodyGetMass(const cpBody *body); -/// Set the mass of the body. -CP_EXPORT void cpBodySetMass(cpBody *body, cpFloat m); - -/// Get the moment of inertia of the body. -CP_EXPORT cpFloat cpBodyGetMoment(const cpBody *body); -/// Set the moment of inertia of the body. -CP_EXPORT void cpBodySetMoment(cpBody *body, cpFloat i); - -/// Set the position of a body. -CP_EXPORT cpVect cpBodyGetPosition(const cpBody *body); -/// Set the position of the body. -CP_EXPORT void cpBodySetPosition(cpBody *body, cpVect pos); - -/// Get the offset of the center of gravity in body local coordinates. -CP_EXPORT cpVect cpBodyGetCenterOfGravity(const cpBody *body); -/// Set the offset of the center of gravity in body local coordinates. -CP_EXPORT void cpBodySetCenterOfGravity(cpBody *body, cpVect cog); - -/// Get the velocity of the body. -CP_EXPORT cpVect cpBodyGetVelocity(const cpBody *body); -/// Set the velocity of the body. -CP_EXPORT void cpBodySetVelocity(cpBody *body, cpVect velocity); - -/// Get the force applied to the body for the next time step. -CP_EXPORT cpVect cpBodyGetForce(const cpBody *body); -/// Set the force applied to the body for the next time step. -CP_EXPORT void cpBodySetForce(cpBody *body, cpVect force); - -/// Get the angle of the body. -CP_EXPORT cpFloat cpBodyGetAngle(const cpBody *body); -/// Set the angle of a body. -CP_EXPORT void cpBodySetAngle(cpBody *body, cpFloat a); - -/// Get the angular velocity of the body. -CP_EXPORT cpFloat cpBodyGetAngularVelocity(const cpBody *body); -/// Set the angular velocity of the body. -CP_EXPORT void cpBodySetAngularVelocity(cpBody *body, cpFloat angularVelocity); - -/// Get the torque applied to the body for the next time step. -CP_EXPORT cpFloat cpBodyGetTorque(const cpBody *body); -/// Set the torque applied to the body for the next time step. -CP_EXPORT void cpBodySetTorque(cpBody *body, cpFloat torque); - -/// Get the rotation vector of the body. (The x basis vector of it's transform.) -CP_EXPORT cpVect cpBodyGetRotation(const cpBody *body); - -/// Get the user data pointer assigned to the body. -CP_EXPORT cpDataPointer cpBodyGetUserData(const cpBody *body); -/// Set the user data pointer assigned to the body. -CP_EXPORT void cpBodySetUserData(cpBody *body, cpDataPointer userData); - -/// Set the callback used to update a body's velocity. -CP_EXPORT void cpBodySetVelocityUpdateFunc(cpBody *body, cpBodyVelocityFunc velocityFunc); -/// Set the callback used to update a body's position. -/// NOTE: It's not generally recommended to override this unless you call the default position update function. -CP_EXPORT void cpBodySetPositionUpdateFunc(cpBody *body, cpBodyPositionFunc positionFunc); - -/// Default velocity integration function.. -CP_EXPORT void cpBodyUpdateVelocity(cpBody *body, cpVect gravity, cpFloat damping, cpFloat dt); -/// Default position integration function. -CP_EXPORT void cpBodyUpdatePosition(cpBody *body, cpFloat dt); - -/// Convert body relative/local coordinates to absolute/world coordinates. -CP_EXPORT cpVect cpBodyLocalToWorld(const cpBody *body, const cpVect point); -/// Convert body absolute/world coordinates to relative/local coordinates. -CP_EXPORT cpVect cpBodyWorldToLocal(const cpBody *body, const cpVect point); - -/// Apply a force to a body. Both the force and point are expressed in world coordinates. -CP_EXPORT void cpBodyApplyForceAtWorldPoint(cpBody *body, cpVect force, cpVect point); -/// Apply a force to a body. Both the force and point are expressed in body local coordinates. -CP_EXPORT void cpBodyApplyForceAtLocalPoint(cpBody *body, cpVect force, cpVect point); - -/// Apply an impulse to a body. Both the impulse and point are expressed in world coordinates. -CP_EXPORT void cpBodyApplyImpulseAtWorldPoint(cpBody *body, cpVect impulse, cpVect point); -/// Apply an impulse to a body. Both the impulse and point are expressed in body local coordinates. -CP_EXPORT void cpBodyApplyImpulseAtLocalPoint(cpBody *body, cpVect impulse, cpVect point); - -/// Get the velocity on a body (in world units) at a point on the body in world coordinates. -CP_EXPORT cpVect cpBodyGetVelocityAtWorldPoint(const cpBody *body, cpVect point); -/// Get the velocity on a body (in world units) at a point on the body in local coordinates. -CP_EXPORT cpVect cpBodyGetVelocityAtLocalPoint(const cpBody *body, cpVect point); - -/// Get the amount of kinetic energy contained by the body. -CP_EXPORT cpFloat cpBodyKineticEnergy(const cpBody *body); - -/// Body/shape iterator callback function type. -typedef void (*cpBodyShapeIteratorFunc)(cpBody *body, cpShape *shape, void *data); -/// Call @c func once for each shape attached to @c body and added to the space. -CP_EXPORT void cpBodyEachShape(cpBody *body, cpBodyShapeIteratorFunc func, void *data); - -/// Body/constraint iterator callback function type. -typedef void (*cpBodyConstraintIteratorFunc)(cpBody *body, cpConstraint *constraint, void *data); -/// Call @c func once for each constraint attached to @c body and added to the space. -CP_EXPORT void cpBodyEachConstraint(cpBody *body, cpBodyConstraintIteratorFunc func, void *data); - -/// Body/arbiter iterator callback function type. -typedef void (*cpBodyArbiterIteratorFunc)(cpBody *body, cpArbiter *arbiter, void *data); -/// Call @c func once for each arbiter that is currently active on the body. -CP_EXPORT void cpBodyEachArbiter(cpBody *body, cpBodyArbiterIteratorFunc func, void *data); - -///@} diff --git a/apecs-physics/Chipmunk2D/include/chipmunk/cpConstraint.h b/apecs-physics/Chipmunk2D/include/chipmunk/cpConstraint.h deleted file mode 100644 index b1a439f..0000000 --- a/apecs-physics/Chipmunk2D/include/chipmunk/cpConstraint.h +++ /dev/null @@ -1,95 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/// @defgroup cpConstraint cpConstraint -/// @{ - -/// Callback function type that gets called before solving a joint. -typedef void (*cpConstraintPreSolveFunc)(cpConstraint *constraint, cpSpace *space); -/// Callback function type that gets called after solving a joint. -typedef void (*cpConstraintPostSolveFunc)(cpConstraint *constraint, cpSpace *space); - -/// Destroy a constraint. -CP_EXPORT void cpConstraintDestroy(cpConstraint *constraint); -/// Destroy and free a constraint. -CP_EXPORT void cpConstraintFree(cpConstraint *constraint); - -/// Get the cpSpace this constraint is added to. -CP_EXPORT cpSpace* cpConstraintGetSpace(const cpConstraint *constraint); - -/// Get the first body the constraint is attached to. -CP_EXPORT cpBody* cpConstraintGetBodyA(const cpConstraint *constraint); - -/// Get the second body the constraint is attached to. -CP_EXPORT cpBody* cpConstraintGetBodyB(const cpConstraint *constraint); - -/// Get the maximum force that this constraint is allowed to use. -CP_EXPORT cpFloat cpConstraintGetMaxForce(const cpConstraint *constraint); -/// Set the maximum force that this constraint is allowed to use. (defaults to INFINITY) -CP_EXPORT void cpConstraintSetMaxForce(cpConstraint *constraint, cpFloat maxForce); - -/// Get rate at which joint error is corrected. -CP_EXPORT cpFloat cpConstraintGetErrorBias(const cpConstraint *constraint); -/// Set rate at which joint error is corrected. -/// Defaults to pow(1.0 - 0.1, 60.0) meaning that it will -/// correct 10% of the error every 1/60th of a second. -CP_EXPORT void cpConstraintSetErrorBias(cpConstraint *constraint, cpFloat errorBias); - -/// Get the maximum rate at which joint error is corrected. -CP_EXPORT cpFloat cpConstraintGetMaxBias(const cpConstraint *constraint); -/// Set the maximum rate at which joint error is corrected. (defaults to INFINITY) -CP_EXPORT void cpConstraintSetMaxBias(cpConstraint *constraint, cpFloat maxBias); - -/// Get if the two bodies connected by the constraint are allowed to collide or not. -CP_EXPORT cpBool cpConstraintGetCollideBodies(const cpConstraint *constraint); -/// Set if the two bodies connected by the constraint are allowed to collide or not. (defaults to cpFalse) -CP_EXPORT void cpConstraintSetCollideBodies(cpConstraint *constraint, cpBool collideBodies); - -/// Get the pre-solve function that is called before the solver runs. -CP_EXPORT cpConstraintPreSolveFunc cpConstraintGetPreSolveFunc(const cpConstraint *constraint); -/// Set the pre-solve function that is called before the solver runs. -CP_EXPORT void cpConstraintSetPreSolveFunc(cpConstraint *constraint, cpConstraintPreSolveFunc preSolveFunc); - -/// Get the post-solve function that is called before the solver runs. -CP_EXPORT cpConstraintPostSolveFunc cpConstraintGetPostSolveFunc(const cpConstraint *constraint); -/// Set the post-solve function that is called before the solver runs. -CP_EXPORT void cpConstraintSetPostSolveFunc(cpConstraint *constraint, cpConstraintPostSolveFunc postSolveFunc); - -/// Get the user definable data pointer for this constraint -CP_EXPORT cpDataPointer cpConstraintGetUserData(const cpConstraint *constraint); -/// Set the user definable data pointer for this constraint -CP_EXPORT void cpConstraintSetUserData(cpConstraint *constraint, cpDataPointer userData); - -/// Get the last impulse applied by this constraint. -CP_EXPORT cpFloat cpConstraintGetImpulse(cpConstraint *constraint); - -#include "cpPinJoint.h" -#include "cpSlideJoint.h" -#include "cpPivotJoint.h" -#include "cpGrooveJoint.h" -#include "cpDampedSpring.h" -#include "cpDampedRotarySpring.h" -#include "cpRotaryLimitJoint.h" -#include "cpRatchetJoint.h" -#include "cpGearJoint.h" -#include "cpSimpleMotor.h" - -///@} diff --git a/apecs-physics/Chipmunk2D/include/chipmunk/cpDampedRotarySpring.h b/apecs-physics/Chipmunk2D/include/chipmunk/cpDampedRotarySpring.h deleted file mode 100644 index 6f60e86..0000000 --- a/apecs-physics/Chipmunk2D/include/chipmunk/cpDampedRotarySpring.h +++ /dev/null @@ -1,58 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/// @defgroup cpDampedRotarySpring cpDampedRotarySpring -/// @{ - -/// Check if a constraint is a damped rotary springs. -CP_EXPORT cpBool cpConstraintIsDampedRotarySpring(const cpConstraint *constraint); - -/// Function type used for damped rotary spring force callbacks. -typedef cpFloat (*cpDampedRotarySpringTorqueFunc)(struct cpConstraint *spring, cpFloat relativeAngle); - -/// Allocate a damped rotary spring. -CP_EXPORT cpDampedRotarySpring* cpDampedRotarySpringAlloc(void); -/// Initialize a damped rotary spring. -CP_EXPORT cpDampedRotarySpring* cpDampedRotarySpringInit(cpDampedRotarySpring *joint, cpBody *a, cpBody *b, cpFloat restAngle, cpFloat stiffness, cpFloat damping); -/// Allocate and initialize a damped rotary spring. -CP_EXPORT cpConstraint* cpDampedRotarySpringNew(cpBody *a, cpBody *b, cpFloat restAngle, cpFloat stiffness, cpFloat damping); - -/// Get the rest length of the spring. -CP_EXPORT cpFloat cpDampedRotarySpringGetRestAngle(const cpConstraint *constraint); -/// Set the rest length of the spring. -CP_EXPORT void cpDampedRotarySpringSetRestAngle(cpConstraint *constraint, cpFloat restAngle); - -/// Get the stiffness of the spring in force/distance. -CP_EXPORT cpFloat cpDampedRotarySpringGetStiffness(const cpConstraint *constraint); -/// Set the stiffness of the spring in force/distance. -CP_EXPORT void cpDampedRotarySpringSetStiffness(cpConstraint *constraint, cpFloat stiffness); - -/// Get the damping of the spring. -CP_EXPORT cpFloat cpDampedRotarySpringGetDamping(const cpConstraint *constraint); -/// Set the damping of the spring. -CP_EXPORT void cpDampedRotarySpringSetDamping(cpConstraint *constraint, cpFloat damping); - -/// Get the damping of the spring. -CP_EXPORT cpDampedRotarySpringTorqueFunc cpDampedRotarySpringGetSpringTorqueFunc(const cpConstraint *constraint); -/// Set the damping of the spring. -CP_EXPORT void cpDampedRotarySpringSetSpringTorqueFunc(cpConstraint *constraint, cpDampedRotarySpringTorqueFunc springTorqueFunc); - -/// @} diff --git a/apecs-physics/Chipmunk2D/include/chipmunk/cpDampedSpring.h b/apecs-physics/Chipmunk2D/include/chipmunk/cpDampedSpring.h deleted file mode 100644 index b332fc7..0000000 --- a/apecs-physics/Chipmunk2D/include/chipmunk/cpDampedSpring.h +++ /dev/null @@ -1,68 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/// @defgroup cpDampedSpring cpDampedSpring -/// @{ - -/// Check if a constraint is a slide joint. -CP_EXPORT cpBool cpConstraintIsDampedSpring(const cpConstraint *constraint); - -/// Function type used for damped spring force callbacks. -typedef cpFloat (*cpDampedSpringForceFunc)(cpConstraint *spring, cpFloat dist); - -/// Allocate a damped spring. -CP_EXPORT cpDampedSpring* cpDampedSpringAlloc(void); -/// Initialize a damped spring. -CP_EXPORT cpDampedSpring* cpDampedSpringInit(cpDampedSpring *joint, cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB, cpFloat restLength, cpFloat stiffness, cpFloat damping); -/// Allocate and initialize a damped spring. -CP_EXPORT cpConstraint* cpDampedSpringNew(cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB, cpFloat restLength, cpFloat stiffness, cpFloat damping); - -/// Get the location of the first anchor relative to the first body. -CP_EXPORT cpVect cpDampedSpringGetAnchorA(const cpConstraint *constraint); -/// Set the location of the first anchor relative to the first body. -CP_EXPORT void cpDampedSpringSetAnchorA(cpConstraint *constraint, cpVect anchorA); - -/// Get the location of the second anchor relative to the second body. -CP_EXPORT cpVect cpDampedSpringGetAnchorB(const cpConstraint *constraint); -/// Set the location of the second anchor relative to the second body. -CP_EXPORT void cpDampedSpringSetAnchorB(cpConstraint *constraint, cpVect anchorB); - -/// Get the rest length of the spring. -CP_EXPORT cpFloat cpDampedSpringGetRestLength(const cpConstraint *constraint); -/// Set the rest length of the spring. -CP_EXPORT void cpDampedSpringSetRestLength(cpConstraint *constraint, cpFloat restLength); - -/// Get the stiffness of the spring in force/distance. -CP_EXPORT cpFloat cpDampedSpringGetStiffness(const cpConstraint *constraint); -/// Set the stiffness of the spring in force/distance. -CP_EXPORT void cpDampedSpringSetStiffness(cpConstraint *constraint, cpFloat stiffness); - -/// Get the damping of the spring. -CP_EXPORT cpFloat cpDampedSpringGetDamping(const cpConstraint *constraint); -/// Set the damping of the spring. -CP_EXPORT void cpDampedSpringSetDamping(cpConstraint *constraint, cpFloat damping); - -/// Get the damping of the spring. -CP_EXPORT cpDampedSpringForceFunc cpDampedSpringGetSpringForceFunc(const cpConstraint *constraint); -/// Set the damping of the spring. -CP_EXPORT void cpDampedSpringSetSpringForceFunc(cpConstraint *constraint, cpDampedSpringForceFunc springForceFunc); - -/// @} diff --git a/apecs-physics/Chipmunk2D/include/chipmunk/cpGearJoint.h b/apecs-physics/Chipmunk2D/include/chipmunk/cpGearJoint.h deleted file mode 100644 index 8cd80e0..0000000 --- a/apecs-physics/Chipmunk2D/include/chipmunk/cpGearJoint.h +++ /dev/null @@ -1,45 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/// @defgroup cpGearJoint cpGearJoint -/// @{ - -/// Check if a constraint is a damped rotary springs. -CP_EXPORT cpBool cpConstraintIsGearJoint(const cpConstraint *constraint); - -/// Allocate a gear joint. -CP_EXPORT cpGearJoint* cpGearJointAlloc(void); -/// Initialize a gear joint. -CP_EXPORT cpGearJoint* cpGearJointInit(cpGearJoint *joint, cpBody *a, cpBody *b, cpFloat phase, cpFloat ratio); -/// Allocate and initialize a gear joint. -CP_EXPORT cpConstraint* cpGearJointNew(cpBody *a, cpBody *b, cpFloat phase, cpFloat ratio); - -/// Get the phase offset of the gears. -CP_EXPORT cpFloat cpGearJointGetPhase(const cpConstraint *constraint); -/// Set the phase offset of the gears. -CP_EXPORT void cpGearJointSetPhase(cpConstraint *constraint, cpFloat phase); - -/// Get the angular distance of each ratchet. -CP_EXPORT cpFloat cpGearJointGetRatio(const cpConstraint *constraint); -/// Set the ratio of a gear joint. -CP_EXPORT void cpGearJointSetRatio(cpConstraint *constraint, cpFloat ratio); - -/// @} diff --git a/apecs-physics/Chipmunk2D/include/chipmunk/cpGrooveJoint.h b/apecs-physics/Chipmunk2D/include/chipmunk/cpGrooveJoint.h deleted file mode 100644 index 8bdafc1..0000000 --- a/apecs-physics/Chipmunk2D/include/chipmunk/cpGrooveJoint.h +++ /dev/null @@ -1,50 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/// @defgroup cpGrooveJoint cpGrooveJoint -/// @{ - -/// Check if a constraint is a slide joint. -CP_EXPORT cpBool cpConstraintIsGrooveJoint(const cpConstraint *constraint); - -/// Allocate a groove joint. -CP_EXPORT cpGrooveJoint* cpGrooveJointAlloc(void); -/// Initialize a groove joint. -CP_EXPORT cpGrooveJoint* cpGrooveJointInit(cpGrooveJoint *joint, cpBody *a, cpBody *b, cpVect groove_a, cpVect groove_b, cpVect anchorB); -/// Allocate and initialize a groove joint. -CP_EXPORT cpConstraint* cpGrooveJointNew(cpBody *a, cpBody *b, cpVect groove_a, cpVect groove_b, cpVect anchorB); - -/// Get the first endpoint of the groove relative to the first body. -CP_EXPORT cpVect cpGrooveJointGetGrooveA(const cpConstraint *constraint); -/// Set the first endpoint of the groove relative to the first body. -CP_EXPORT void cpGrooveJointSetGrooveA(cpConstraint *constraint, cpVect grooveA); - -/// Get the first endpoint of the groove relative to the first body. -CP_EXPORT cpVect cpGrooveJointGetGrooveB(const cpConstraint *constraint); -/// Set the first endpoint of the groove relative to the first body. -CP_EXPORT void cpGrooveJointSetGrooveB(cpConstraint *constraint, cpVect grooveB); - -/// Get the location of the second anchor relative to the second body. -CP_EXPORT cpVect cpGrooveJointGetAnchorB(const cpConstraint *constraint); -/// Set the location of the second anchor relative to the second body. -CP_EXPORT void cpGrooveJointSetAnchorB(cpConstraint *constraint, cpVect anchorB); - -/// @} diff --git a/apecs-physics/Chipmunk2D/include/chipmunk/cpHastySpace.h b/apecs-physics/Chipmunk2D/include/chipmunk/cpHastySpace.h deleted file mode 100644 index 6de2283..0000000 --- a/apecs-physics/Chipmunk2D/include/chipmunk/cpHastySpace.h +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2013 Howling Moon Software. All rights reserved. -// See http://chipmunk2d.net/legal.php for more information. - -/// cpHastySpace is exclusive to Chipmunk Pro -/// Currently it enables ARM NEON optimizations in the solver, but in the future will include other optimizations such as -/// a multi-threaded solver and multi-threaded collision broadphases. - -struct cpHastySpace; -typedef struct cpHastySpace cpHastySpace; - -/// Create a new hasty space. -/// On ARM platforms that support NEON, this will enable the vectorized solver. -/// cpHastySpace also supports multiple threads, but runs single threaded by default for determinism. -CP_EXPORT cpSpace *cpHastySpaceNew(void); -CP_EXPORT void cpHastySpaceFree(cpSpace *space); - -/// Set the number of threads to use for the solver. -/// Currently Chipmunk is limited to 2 threads as using more generally provides very minimal performance gains. -/// Passing 0 as the thread count on iOS or OS X will cause Chipmunk to automatically detect the number of threads it should use. -/// On other platforms passing 0 for the thread count will set 1 thread. -CP_EXPORT void cpHastySpaceSetThreads(cpSpace *space, unsigned long threads); - -/// Returns the number of threads the solver is using to run. -CP_EXPORT unsigned long cpHastySpaceGetThreads(cpSpace *space); - -/// When stepping a hasty space, you must use this function. -CP_EXPORT void cpHastySpaceStep(cpSpace *space, cpFloat dt); diff --git a/apecs-physics/Chipmunk2D/include/chipmunk/cpMarch.h b/apecs-physics/Chipmunk2D/include/chipmunk/cpMarch.h deleted file mode 100644 index cc1f5c0..0000000 --- a/apecs-physics/Chipmunk2D/include/chipmunk/cpMarch.h +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2013 Howling Moon Software. All rights reserved. -// See http://chipmunk2d.net/legal.php for more information. - -/// Function type used as a callback from the marching squares algorithm to sample an image function. -/// It passes you the point to sample and your context pointer, and you return the density. -typedef cpFloat (*cpMarchSampleFunc)(cpVect point, void *data); - -/// Function type used as a callback from the marching squares algorithm to output a line segment. -/// It passes you the two endpoints and your context pointer. -typedef void (*cpMarchSegmentFunc)(cpVect v0, cpVect v1, void *data); - -/// Trace an anti-aliased contour of an image along a particular threshold. -/// The given number of samples will be taken and spread across the bounding box area using the sampling function and context. -/// The segment function will be called for each segment detected that lies along the density contour for @c threshold. -CP_EXPORT void cpMarchSoft( - cpBB bb, unsigned long x_samples, unsigned long y_samples, cpFloat threshold, - cpMarchSegmentFunc segment, void *segment_data, - cpMarchSampleFunc sample, void *sample_data -); - -/// Trace an aliased curve of an image along a particular threshold. -/// The given number of samples will be taken and spread across the bounding box area using the sampling function and context. -/// The segment function will be called for each segment detected that lies along the density contour for @c threshold. -CP_EXPORT void cpMarchHard( - cpBB bb, unsigned long x_samples, unsigned long y_samples, cpFloat threshold, - cpMarchSegmentFunc segment, void *segment_data, - cpMarchSampleFunc sample, void *sample_data -); diff --git a/apecs-physics/Chipmunk2D/include/chipmunk/cpPinJoint.h b/apecs-physics/Chipmunk2D/include/chipmunk/cpPinJoint.h deleted file mode 100644 index 45aaa3e..0000000 --- a/apecs-physics/Chipmunk2D/include/chipmunk/cpPinJoint.h +++ /dev/null @@ -1,50 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/// @defgroup cpPinJoint cpPinJoint -/// @{ - -/// Check if a constraint is a pin joint. -CP_EXPORT cpBool cpConstraintIsPinJoint(const cpConstraint *constraint); - -/// Allocate a pin joint. -CP_EXPORT cpPinJoint* cpPinJointAlloc(void); -/// Initialize a pin joint. -CP_EXPORT cpPinJoint* cpPinJointInit(cpPinJoint *joint, cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB); -/// Allocate and initialize a pin joint. -CP_EXPORT cpConstraint* cpPinJointNew(cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB); - -/// Get the location of the first anchor relative to the first body. -CP_EXPORT cpVect cpPinJointGetAnchorA(const cpConstraint *constraint); -/// Set the location of the first anchor relative to the first body. -CP_EXPORT void cpPinJointSetAnchorA(cpConstraint *constraint, cpVect anchorA); - -/// Get the location of the second anchor relative to the second body. -CP_EXPORT cpVect cpPinJointGetAnchorB(const cpConstraint *constraint); -/// Set the location of the second anchor relative to the second body. -CP_EXPORT void cpPinJointSetAnchorB(cpConstraint *constraint, cpVect anchorB); - -/// Get the distance the joint will maintain between the two anchors. -CP_EXPORT cpFloat cpPinJointGetDist(const cpConstraint *constraint); -/// Set the distance the joint will maintain between the two anchors. -CP_EXPORT void cpPinJointSetDist(cpConstraint *constraint, cpFloat dist); - -///@} diff --git a/apecs-physics/Chipmunk2D/include/chipmunk/cpPivotJoint.h b/apecs-physics/Chipmunk2D/include/chipmunk/cpPivotJoint.h deleted file mode 100644 index 4a620ef..0000000 --- a/apecs-physics/Chipmunk2D/include/chipmunk/cpPivotJoint.h +++ /dev/null @@ -1,47 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/// @defgroup cpPivotJoint cpPivotJoint -/// @{ - -/// Check if a constraint is a slide joint. -CP_EXPORT cpBool cpConstraintIsPivotJoint(const cpConstraint *constraint); - -/// Allocate a pivot joint -CP_EXPORT cpPivotJoint* cpPivotJointAlloc(void); -/// Initialize a pivot joint. -CP_EXPORT cpPivotJoint* cpPivotJointInit(cpPivotJoint *joint, cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB); -/// Allocate and initialize a pivot joint. -CP_EXPORT cpConstraint* cpPivotJointNew(cpBody *a, cpBody *b, cpVect pivot); -/// Allocate and initialize a pivot joint with specific anchors. -CP_EXPORT cpConstraint* cpPivotJointNew2(cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB); - -/// Get the location of the first anchor relative to the first body. -CP_EXPORT cpVect cpPivotJointGetAnchorA(const cpConstraint *constraint); -/// Set the location of the first anchor relative to the first body. -CP_EXPORT void cpPivotJointSetAnchorA(cpConstraint *constraint, cpVect anchorA); - -/// Get the location of the second anchor relative to the second body. -CP_EXPORT cpVect cpPivotJointGetAnchorB(const cpConstraint *constraint); -/// Set the location of the second anchor relative to the second body. -CP_EXPORT void cpPivotJointSetAnchorB(cpConstraint *constraint, cpVect anchorB); - -/// @} diff --git a/apecs-physics/Chipmunk2D/include/chipmunk/cpPolyShape.h b/apecs-physics/Chipmunk2D/include/chipmunk/cpPolyShape.h deleted file mode 100644 index 25f688b..0000000 --- a/apecs-physics/Chipmunk2D/include/chipmunk/cpPolyShape.h +++ /dev/null @@ -1,56 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/// @defgroup cpPolyShape cpPolyShape -/// @{ - -/// Allocate a polygon shape. -CP_EXPORT cpPolyShape* cpPolyShapeAlloc(void); -/// Initialize a polygon shape with rounded corners. -/// A convex hull will be created from the vertexes. -CP_EXPORT cpPolyShape* cpPolyShapeInit(cpPolyShape *poly, cpBody *body, int count, const cpVect *verts, cpTransform transform, cpFloat radius); -/// Initialize a polygon shape with rounded corners. -/// The vertexes must be convex with a counter-clockwise winding. -CP_EXPORT cpPolyShape* cpPolyShapeInitRaw(cpPolyShape *poly, cpBody *body, int count, const cpVect *verts, cpFloat radius); -/// Allocate and initialize a polygon shape with rounded corners. -/// A convex hull will be created from the vertexes. -CP_EXPORT cpShape* cpPolyShapeNew(cpBody *body, int count, const cpVect *verts, cpTransform transform, cpFloat radius); -/// Allocate and initialize a polygon shape with rounded corners. -/// The vertexes must be convex with a counter-clockwise winding. -CP_EXPORT cpShape* cpPolyShapeNewRaw(cpBody *body, int count, const cpVect *verts, cpFloat radius); - -/// Initialize a box shaped polygon shape with rounded corners. -CP_EXPORT cpPolyShape* cpBoxShapeInit(cpPolyShape *poly, cpBody *body, cpFloat width, cpFloat height, cpFloat radius); -/// Initialize an offset box shaped polygon shape with rounded corners. -CP_EXPORT cpPolyShape* cpBoxShapeInit2(cpPolyShape *poly, cpBody *body, cpBB box, cpFloat radius); -/// Allocate and initialize a box shaped polygon shape. -CP_EXPORT cpShape* cpBoxShapeNew(cpBody *body, cpFloat width, cpFloat height, cpFloat radius); -/// Allocate and initialize an offset box shaped polygon shape. -CP_EXPORT cpShape* cpBoxShapeNew2(cpBody *body, cpBB box, cpFloat radius); - -/// Get the number of verts in a polygon shape. -CP_EXPORT int cpPolyShapeGetCount(const cpShape *shape); -/// Get the @c ith vertex of a polygon shape. -CP_EXPORT cpVect cpPolyShapeGetVert(const cpShape *shape, int index); -/// Get the radius of a polygon shape. -CP_EXPORT cpFloat cpPolyShapeGetRadius(const cpShape *shape); - -/// @} diff --git a/apecs-physics/Chipmunk2D/include/chipmunk/cpPolyline.h b/apecs-physics/Chipmunk2D/include/chipmunk/cpPolyline.h deleted file mode 100644 index 4e878f0..0000000 --- a/apecs-physics/Chipmunk2D/include/chipmunk/cpPolyline.h +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2013 Howling Moon Software. All rights reserved. -// See http://chipmunk2d.net/legal.php for more information. - -// Polylines are just arrays of vertexes. -// They are looped if the first vertex is equal to the last. -// cpPolyline structs are intended to be passed by value and destroyed when you are done with them. -typedef struct cpPolyline { - int count, capacity; - cpVect verts[]; -} cpPolyline; - -/// Destroy and free a polyline instance. -CP_EXPORT void cpPolylineFree(cpPolyline *line); - -/// Returns true if the first vertex is equal to the last. -CP_EXPORT cpBool cpPolylineIsClosed(cpPolyline *line); - -/** - Returns a copy of a polyline simplified by using the Douglas-Peucker algorithm. - This works very well on smooth or gently curved shapes, but not well on straight edged or angular shapes. -*/ -CP_EXPORT cpPolyline *cpPolylineSimplifyCurves(cpPolyline *line, cpFloat tol); - -/** - Returns a copy of a polyline simplified by discarding "flat" vertexes. - This works well on straigt edged or angular shapes, not as well on smooth shapes. -*/ -CP_EXPORT cpPolyline *cpPolylineSimplifyVertexes(cpPolyline *line, cpFloat tol); - -/// Get the convex hull of a polyline as a looped polyline. -CP_EXPORT cpPolyline *cpPolylineToConvexHull(cpPolyline *line, cpFloat tol); - - -/// Polyline sets are collections of polylines, generally built by cpMarchSoft() or cpMarchHard(). -typedef struct cpPolylineSet { - int count, capacity; - cpPolyline **lines; -} cpPolylineSet; - -/// Allocate a new polyline set. -CP_EXPORT cpPolylineSet *cpPolylineSetAlloc(void); - -/// Initialize a new polyline set. -CP_EXPORT cpPolylineSet *cpPolylineSetInit(cpPolylineSet *set); - -/// Allocate and initialize a polyline set. -CP_EXPORT cpPolylineSet *cpPolylineSetNew(void); - -/// Destroy a polyline set. -CP_EXPORT void cpPolylineSetDestroy(cpPolylineSet *set, cpBool freePolylines); - -/// Destroy and free a polyline set. -CP_EXPORT void cpPolylineSetFree(cpPolylineSet *set, cpBool freePolylines); - -/** - Add a line segment to a polyline set. - A segment will either start a new polyline, join two others, or add to or loop an existing polyline. - This is mostly intended to be used as a callback directly from cpMarchSoft() or cpMarchHard(). -*/ -CP_EXPORT void cpPolylineSetCollectSegment(cpVect v0, cpVect v1, cpPolylineSet *lines); - -/** - Get an approximate convex decomposition from a polyline. - Returns a cpPolylineSet of convex hulls that match the original shape to within 'tol'. - NOTE: If the input is a self intersecting polygon, the output might end up overly simplified. -*/ - -CP_EXPORT cpPolylineSet *cpPolylineConvexDecomposition(cpPolyline *line, cpFloat tol); - -#define cpPolylineConvexDecomposition_BETA cpPolylineConvexDecomposition diff --git a/apecs-physics/Chipmunk2D/include/chipmunk/cpRatchetJoint.h b/apecs-physics/Chipmunk2D/include/chipmunk/cpRatchetJoint.h deleted file mode 100644 index 3ed4c91..0000000 --- a/apecs-physics/Chipmunk2D/include/chipmunk/cpRatchetJoint.h +++ /dev/null @@ -1,50 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/// @defgroup cpRatchetJoint cpRatchetJoint -/// @{ - -/// Check if a constraint is a damped rotary springs. -CP_EXPORT cpBool cpConstraintIsRatchetJoint(const cpConstraint *constraint); - -/// Allocate a ratchet joint. -CP_EXPORT cpRatchetJoint* cpRatchetJointAlloc(void); -/// Initialize a ratched joint. -CP_EXPORT cpRatchetJoint* cpRatchetJointInit(cpRatchetJoint *joint, cpBody *a, cpBody *b, cpFloat phase, cpFloat ratchet); -/// Allocate and initialize a ratchet joint. -CP_EXPORT cpConstraint* cpRatchetJointNew(cpBody *a, cpBody *b, cpFloat phase, cpFloat ratchet); - -/// Get the angle of the current ratchet tooth. -CP_EXPORT cpFloat cpRatchetJointGetAngle(const cpConstraint *constraint); -/// Set the angle of the current ratchet tooth. -CP_EXPORT void cpRatchetJointSetAngle(cpConstraint *constraint, cpFloat angle); - -/// Get the phase offset of the ratchet. -CP_EXPORT cpFloat cpRatchetJointGetPhase(const cpConstraint *constraint); -/// Get the phase offset of the ratchet. -CP_EXPORT void cpRatchetJointSetPhase(cpConstraint *constraint, cpFloat phase); - -/// Get the angular distance of each ratchet. -CP_EXPORT cpFloat cpRatchetJointGetRatchet(const cpConstraint *constraint); -/// Set the angular distance of each ratchet. -CP_EXPORT void cpRatchetJointSetRatchet(cpConstraint *constraint, cpFloat ratchet); - -/// @} diff --git a/apecs-physics/Chipmunk2D/include/chipmunk/cpRobust.h b/apecs-physics/Chipmunk2D/include/chipmunk/cpRobust.h deleted file mode 100644 index 086e488..0000000 --- a/apecs-physics/Chipmunk2D/include/chipmunk/cpRobust.h +++ /dev/null @@ -1,11 +0,0 @@ -#include "cpVect.h" - -// This is a private header for functions (currently just one) that need strict floating point results. -// It was easier to put this in it's own file than to fiddle with 4 different compiler specific pragmas or attributes. -// "Fast math" should be disabled here. - -// Check if c is to the left of segment (a, b). -cpBool cpCheckPointGreater(const cpVect a, const cpVect b, const cpVect c); - -// Check if p is behind one of v0 or v1 on axis n. -cpBool cpCheckAxis(cpVect v0, cpVect v1, cpVect p, cpVect n); diff --git a/apecs-physics/Chipmunk2D/include/chipmunk/cpRotaryLimitJoint.h b/apecs-physics/Chipmunk2D/include/chipmunk/cpRotaryLimitJoint.h deleted file mode 100644 index fac7ad8..0000000 --- a/apecs-physics/Chipmunk2D/include/chipmunk/cpRotaryLimitJoint.h +++ /dev/null @@ -1,45 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/// @defgroup cpRotaryLimitJoint cpRotaryLimitJoint -/// @{ - -/// Check if a constraint is a damped rotary springs. -CP_EXPORT cpBool cpConstraintIsRotaryLimitJoint(const cpConstraint *constraint); - -/// Allocate a damped rotary limit joint. -CP_EXPORT cpRotaryLimitJoint* cpRotaryLimitJointAlloc(void); -/// Initialize a damped rotary limit joint. -CP_EXPORT cpRotaryLimitJoint* cpRotaryLimitJointInit(cpRotaryLimitJoint *joint, cpBody *a, cpBody *b, cpFloat min, cpFloat max); -/// Allocate and initialize a damped rotary limit joint. -CP_EXPORT cpConstraint* cpRotaryLimitJointNew(cpBody *a, cpBody *b, cpFloat min, cpFloat max); - -/// Get the minimum distance the joint will maintain between the two anchors. -CP_EXPORT cpFloat cpRotaryLimitJointGetMin(const cpConstraint *constraint); -/// Set the minimum distance the joint will maintain between the two anchors. -CP_EXPORT void cpRotaryLimitJointSetMin(cpConstraint *constraint, cpFloat min); - -/// Get the maximum distance the joint will maintain between the two anchors. -CP_EXPORT cpFloat cpRotaryLimitJointGetMax(const cpConstraint *constraint); -/// Set the maximum distance the joint will maintain between the two anchors. -CP_EXPORT void cpRotaryLimitJointSetMax(cpConstraint *constraint, cpFloat max); - -/// @} diff --git a/apecs-physics/Chipmunk2D/include/chipmunk/cpShape.h b/apecs-physics/Chipmunk2D/include/chipmunk/cpShape.h deleted file mode 100644 index c78ed05..0000000 --- a/apecs-physics/Chipmunk2D/include/chipmunk/cpShape.h +++ /dev/null @@ -1,199 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/// @defgroup cpShape cpShape -/// The cpShape struct defines the shape of a rigid body. -/// @{ - -/// Point query info struct. -typedef struct cpPointQueryInfo { - /// The nearest shape, NULL if no shape was within range. - const cpShape *shape; - /// The closest point on the shape's surface. (in world space coordinates) - cpVect point; - /// The distance to the point. The distance is negative if the point is inside the shape. - cpFloat distance; - /// The gradient of the signed distance function. - /// The value should be similar to info.p/info.d, but accurate even for very small values of info.d. - cpVect gradient; -} cpPointQueryInfo; - -/// Segment query info struct. -typedef struct cpSegmentQueryInfo { - /// The shape that was hit, or NULL if no collision occured. - const cpShape *shape; - /// The point of impact. - cpVect point; - /// The normal of the surface hit. - cpVect normal; - /// The normalized distance along the query segment in the range [0, 1]. - cpFloat alpha; -} cpSegmentQueryInfo; - -/// Fast collision filtering type that is used to determine if two objects collide before calling collision or query callbacks. -typedef struct cpShapeFilter { - /// Two objects with the same non-zero group value do not collide. - /// This is generally used to group objects in a composite object together to disable self collisions. - cpGroup group; - /// A bitmask of user definable categories that this object belongs to. - /// The category/mask combinations of both objects in a collision must agree for a collision to occur. - cpBitmask categories; - /// A bitmask of user definable category types that this object object collides with. - /// The category/mask combinations of both objects in a collision must agree for a collision to occur. - cpBitmask mask; -} cpShapeFilter; - -/// Collision filter value for a shape that will collide with anything except CP_SHAPE_FILTER_NONE. -static const cpShapeFilter CP_SHAPE_FILTER_ALL = {CP_NO_GROUP, CP_ALL_CATEGORIES, CP_ALL_CATEGORIES}; -/// Collision filter value for a shape that does not collide with anything. -static const cpShapeFilter CP_SHAPE_FILTER_NONE = {CP_NO_GROUP, ~CP_ALL_CATEGORIES, ~CP_ALL_CATEGORIES}; - -/// Create a new collision filter. -static inline cpShapeFilter -cpShapeFilterNew(cpGroup group, cpBitmask categories, cpBitmask mask) -{ - cpShapeFilter filter = {group, categories, mask}; - return filter; -} - -/// Destroy a shape. -CP_EXPORT void cpShapeDestroy(cpShape *shape); -/// Destroy and Free a shape. -CP_EXPORT void cpShapeFree(cpShape *shape); - -/// Update, cache and return the bounding box of a shape based on the body it's attached to. -CP_EXPORT cpBB cpShapeCacheBB(cpShape *shape); -/// Update, cache and return the bounding box of a shape with an explicit transformation. -CP_EXPORT cpBB cpShapeUpdate(cpShape *shape, cpTransform transform); - -/// Perform a nearest point query. It finds the closest point on the surface of shape to a specific point. -/// The value returned is the distance between the points. A negative distance means the point is inside the shape. -CP_EXPORT cpFloat cpShapePointQuery(const cpShape *shape, cpVect p, cpPointQueryInfo *out); - -/// Perform a segment query against a shape. @c info must be a pointer to a valid cpSegmentQueryInfo structure. -CP_EXPORT cpBool cpShapeSegmentQuery(const cpShape *shape, cpVect a, cpVect b, cpFloat radius, cpSegmentQueryInfo *info); - -/// Return contact information about two shapes. -CP_EXPORT cpContactPointSet cpShapesCollide(const cpShape *a, const cpShape *b); - -/// The cpSpace this body is added to. -CP_EXPORT cpSpace* cpShapeGetSpace(const cpShape *shape); - -/// The cpBody this shape is connected to. -CP_EXPORT cpBody* cpShapeGetBody(const cpShape *shape); -/// Set the cpBody this shape is connected to. -/// Can only be used if the shape is not currently added to a space. -CP_EXPORT void cpShapeSetBody(cpShape *shape, cpBody *body); - -/// Get the mass of the shape if you are having Chipmunk calculate mass properties for you. -CP_EXPORT cpFloat cpShapeGetMass(cpShape *shape); -/// Set the mass of this shape to have Chipmunk calculate mass properties for you. -CP_EXPORT void cpShapeSetMass(cpShape *shape, cpFloat mass); - -/// Get the density of the shape if you are having Chipmunk calculate mass properties for you. -CP_EXPORT cpFloat cpShapeGetDensity(cpShape *shape); -/// Set the density of this shape to have Chipmunk calculate mass properties for you. -CP_EXPORT void cpShapeSetDensity(cpShape *shape, cpFloat density); - -/// Get the calculated moment of inertia for this shape. -CP_EXPORT cpFloat cpShapeGetMoment(cpShape *shape); -/// Get the calculated area of this shape. -CP_EXPORT cpFloat cpShapeGetArea(cpShape *shape); -/// Get the centroid of this shape. -CP_EXPORT cpVect cpShapeGetCenterOfGravity(cpShape *shape); - -/// Get the bounding box that contains the shape given it's current position and angle. -CP_EXPORT cpBB cpShapeGetBB(const cpShape *shape); - -/// Get if the shape is set to be a sensor or not. -CP_EXPORT cpBool cpShapeGetSensor(const cpShape *shape); -/// Set if the shape is a sensor or not. -CP_EXPORT void cpShapeSetSensor(cpShape *shape, cpBool sensor); - -/// Get the elasticity of this shape. -CP_EXPORT cpFloat cpShapeGetElasticity(const cpShape *shape); -/// Set the elasticity of this shape. -CP_EXPORT void cpShapeSetElasticity(cpShape *shape, cpFloat elasticity); - -/// Get the friction of this shape. -CP_EXPORT cpFloat cpShapeGetFriction(const cpShape *shape); -/// Set the friction of this shape. -CP_EXPORT void cpShapeSetFriction(cpShape *shape, cpFloat friction); - -/// Get the surface velocity of this shape. -CP_EXPORT cpVect cpShapeGetSurfaceVelocity(const cpShape *shape); -/// Set the surface velocity of this shape. -CP_EXPORT void cpShapeSetSurfaceVelocity(cpShape *shape, cpVect surfaceVelocity); - -/// Get the user definable data pointer of this shape. -CP_EXPORT cpDataPointer cpShapeGetUserData(const cpShape *shape); -/// Set the user definable data pointer of this shape. -CP_EXPORT void cpShapeSetUserData(cpShape *shape, cpDataPointer userData); - -/// Set the collision type of this shape. -CP_EXPORT cpCollisionType cpShapeGetCollisionType(const cpShape *shape); -/// Get the collision type of this shape. -CP_EXPORT void cpShapeSetCollisionType(cpShape *shape, cpCollisionType collisionType); - -/// Get the collision filtering parameters of this shape. -CP_EXPORT cpShapeFilter cpShapeGetFilter(const cpShape *shape); -/// Set the collision filtering parameters of this shape. -CP_EXPORT void cpShapeSetFilter(cpShape *shape, cpShapeFilter filter); - - -/// @} -/// @defgroup cpCircleShape cpCircleShape - -/// Allocate a circle shape. -CP_EXPORT cpCircleShape* cpCircleShapeAlloc(void); -/// Initialize a circle shape. -CP_EXPORT cpCircleShape* cpCircleShapeInit(cpCircleShape *circle, cpBody *body, cpFloat radius, cpVect offset); -/// Allocate and initialize a circle shape. -CP_EXPORT cpShape* cpCircleShapeNew(cpBody *body, cpFloat radius, cpVect offset); - -/// Get the offset of a circle shape. -CP_EXPORT cpVect cpCircleShapeGetOffset(const cpShape *shape); -/// Get the radius of a circle shape. -CP_EXPORT cpFloat cpCircleShapeGetRadius(const cpShape *shape); - -/// @} -/// @defgroup cpSegmentShape cpSegmentShape - -/// Allocate a segment shape. -CP_EXPORT cpSegmentShape* cpSegmentShapeAlloc(void); -/// Initialize a segment shape. -CP_EXPORT cpSegmentShape* cpSegmentShapeInit(cpSegmentShape *seg, cpBody *body, cpVect a, cpVect b, cpFloat radius); -/// Allocate and initialize a segment shape. -CP_EXPORT cpShape* cpSegmentShapeNew(cpBody *body, cpVect a, cpVect b, cpFloat radius); - -/// Let Chipmunk know about the geometry of adjacent segments to avoid colliding with endcaps. -CP_EXPORT void cpSegmentShapeSetNeighbors(cpShape *shape, cpVect prev, cpVect next); - -/// Get the first endpoint of a segment shape. -CP_EXPORT cpVect cpSegmentShapeGetA(const cpShape *shape); -/// Get the second endpoint of a segment shape. -CP_EXPORT cpVect cpSegmentShapeGetB(const cpShape *shape); -/// Get the normal of a segment shape. -CP_EXPORT cpVect cpSegmentShapeGetNormal(const cpShape *shape); -/// Get the first endpoint of a segment shape. -CP_EXPORT cpFloat cpSegmentShapeGetRadius(const cpShape *shape); - -/// @} diff --git a/apecs-physics/Chipmunk2D/include/chipmunk/cpSimpleMotor.h b/apecs-physics/Chipmunk2D/include/chipmunk/cpSimpleMotor.h deleted file mode 100644 index 811b011..0000000 --- a/apecs-physics/Chipmunk2D/include/chipmunk/cpSimpleMotor.h +++ /dev/null @@ -1,43 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/// @defgroup cpSimpleMotor cpSimpleMotor -/// @{ - -/// Opaque struct type for damped rotary springs. -typedef struct cpSimpleMotor cpSimpleMotor; - -/// Check if a constraint is a damped rotary springs. -CP_EXPORT cpBool cpConstraintIsSimpleMotor(const cpConstraint *constraint); - -/// Allocate a simple motor. -CP_EXPORT cpSimpleMotor* cpSimpleMotorAlloc(void); -/// initialize a simple motor. -CP_EXPORT cpSimpleMotor* cpSimpleMotorInit(cpSimpleMotor *joint, cpBody *a, cpBody *b, cpFloat rate); -/// Allocate and initialize a simple motor. -CP_EXPORT cpConstraint* cpSimpleMotorNew(cpBody *a, cpBody *b, cpFloat rate); - -/// Get the rate of the motor. -CP_EXPORT cpFloat cpSimpleMotorGetRate(const cpConstraint *constraint); -/// Set the rate of the motor. -CP_EXPORT void cpSimpleMotorSetRate(cpConstraint *constraint, cpFloat rate); - -/// @} diff --git a/apecs-physics/Chipmunk2D/include/chipmunk/cpSlideJoint.h b/apecs-physics/Chipmunk2D/include/chipmunk/cpSlideJoint.h deleted file mode 100644 index c41f9a4..0000000 --- a/apecs-physics/Chipmunk2D/include/chipmunk/cpSlideJoint.h +++ /dev/null @@ -1,55 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/// @defgroup cpSlideJoint cpSlideJoint -/// @{ - -/// Check if a constraint is a slide joint. -CP_EXPORT cpBool cpConstraintIsSlideJoint(const cpConstraint *constraint); - -/// Allocate a slide joint. -CP_EXPORT cpSlideJoint* cpSlideJointAlloc(void); -/// Initialize a slide joint. -CP_EXPORT cpSlideJoint* cpSlideJointInit(cpSlideJoint *joint, cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB, cpFloat min, cpFloat max); -/// Allocate and initialize a slide joint. -CP_EXPORT cpConstraint* cpSlideJointNew(cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB, cpFloat min, cpFloat max); - -/// Get the location of the first anchor relative to the first body. -CP_EXPORT cpVect cpSlideJointGetAnchorA(const cpConstraint *constraint); -/// Set the location of the first anchor relative to the first body. -CP_EXPORT void cpSlideJointSetAnchorA(cpConstraint *constraint, cpVect anchorA); - -/// Get the location of the second anchor relative to the second body. -CP_EXPORT cpVect cpSlideJointGetAnchorB(const cpConstraint *constraint); -/// Set the location of the second anchor relative to the second body. -CP_EXPORT void cpSlideJointSetAnchorB(cpConstraint *constraint, cpVect anchorB); - -/// Get the minimum distance the joint will maintain between the two anchors. -CP_EXPORT cpFloat cpSlideJointGetMin(const cpConstraint *constraint); -/// Set the minimum distance the joint will maintain between the two anchors. -CP_EXPORT void cpSlideJointSetMin(cpConstraint *constraint, cpFloat min); - -/// Get the maximum distance the joint will maintain between the two anchors. -CP_EXPORT cpFloat cpSlideJointGetMax(const cpConstraint *constraint); -/// Set the maximum distance the joint will maintain between the two anchors. -CP_EXPORT void cpSlideJointSetMax(cpConstraint *constraint, cpFloat max); - -/// @} diff --git a/apecs-physics/Chipmunk2D/include/chipmunk/cpSpace.h b/apecs-physics/Chipmunk2D/include/chipmunk/cpSpace.h deleted file mode 100644 index 7bbabb8..0000000 --- a/apecs-physics/Chipmunk2D/include/chipmunk/cpSpace.h +++ /dev/null @@ -1,319 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/// @defgroup cpSpace cpSpace -/// @{ - -//MARK: Definitions - -/// Collision begin event function callback type. -/// Returning false from a begin callback causes the collision to be ignored until -/// the the separate callback is called when the objects stop colliding. -typedef cpBool (*cpCollisionBeginFunc)(cpArbiter *arb, cpSpace *space, cpDataPointer userData); -/// Collision pre-solve event function callback type. -/// Returning false from a pre-step callback causes the collision to be ignored until the next step. -typedef cpBool (*cpCollisionPreSolveFunc)(cpArbiter *arb, cpSpace *space, cpDataPointer userData); -/// Collision post-solve event function callback type. -typedef void (*cpCollisionPostSolveFunc)(cpArbiter *arb, cpSpace *space, cpDataPointer userData); -/// Collision separate event function callback type. -typedef void (*cpCollisionSeparateFunc)(cpArbiter *arb, cpSpace *space, cpDataPointer userData); - -/// Struct that holds function callback pointers to configure custom collision handling. -/// Collision handlers have a pair of types; when a collision occurs between two shapes that have these types, the collision handler functions are triggered. -struct cpCollisionHandler { - /// Collision type identifier of the first shape that this handler recognizes. - /// In the collision handler callback, the shape with this type will be the first argument. Read only. - const cpCollisionType typeA; - /// Collision type identifier of the second shape that this handler recognizes. - /// In the collision handler callback, the shape with this type will be the second argument. Read only. - const cpCollisionType typeB; - /// This function is called when two shapes with types that match this collision handler begin colliding. - cpCollisionBeginFunc beginFunc; - /// This function is called each step when two shapes with types that match this collision handler are colliding. - /// It's called before the collision solver runs so that you can affect a collision's outcome. - cpCollisionPreSolveFunc preSolveFunc; - /// This function is called each step when two shapes with types that match this collision handler are colliding. - /// It's called after the collision solver runs so that you can read back information about the collision to trigger events in your game. - cpCollisionPostSolveFunc postSolveFunc; - /// This function is called when two shapes with types that match this collision handler stop colliding. - cpCollisionSeparateFunc separateFunc; - /// This is a user definable context pointer that is passed to all of the collision handler functions. - cpDataPointer userData; -}; - -// TODO: Make timestep a parameter? - - -//MARK: Memory and Initialization - -/// Allocate a cpSpace. -CP_EXPORT cpSpace* cpSpaceAlloc(void); -/// Initialize a cpSpace. -CP_EXPORT cpSpace* cpSpaceInit(cpSpace *space); -/// Allocate and initialize a cpSpace. -CP_EXPORT cpSpace* cpSpaceNew(void); - -/// Destroy a cpSpace. -CP_EXPORT void cpSpaceDestroy(cpSpace *space); -/// Destroy and free a cpSpace. -CP_EXPORT void cpSpaceFree(cpSpace *space); - - -//MARK: Properties - -/// Number of iterations to use in the impulse solver to solve contacts and other constraints. -CP_EXPORT int cpSpaceGetIterations(const cpSpace *space); -CP_EXPORT void cpSpaceSetIterations(cpSpace *space, int iterations); - -/// Gravity to pass to rigid bodies when integrating velocity. -CP_EXPORT cpVect cpSpaceGetGravity(const cpSpace *space); -CP_EXPORT void cpSpaceSetGravity(cpSpace *space, cpVect gravity); - -/// Damping rate expressed as the fraction of velocity bodies retain each second. -/// A value of 0.9 would mean that each body's velocity will drop 10% per second. -/// The default value is 1.0, meaning no damping is applied. -/// @note This damping value is different than those of cpDampedSpring and cpDampedRotarySpring. -CP_EXPORT cpFloat cpSpaceGetDamping(const cpSpace *space); -CP_EXPORT void cpSpaceSetDamping(cpSpace *space, cpFloat damping); - -/// Speed threshold for a body to be considered idle. -/// The default value of 0 means to let the space guess a good threshold based on gravity. -CP_EXPORT cpFloat cpSpaceGetIdleSpeedThreshold(const cpSpace *space); -CP_EXPORT void cpSpaceSetIdleSpeedThreshold(cpSpace *space, cpFloat idleSpeedThreshold); - -/// Time a group of bodies must remain idle in order to fall asleep. -/// Enabling sleeping also implicitly enables the the contact graph. -/// The default value of INFINITY disables the sleeping algorithm. -CP_EXPORT cpFloat cpSpaceGetSleepTimeThreshold(const cpSpace *space); -CP_EXPORT void cpSpaceSetSleepTimeThreshold(cpSpace *space, cpFloat sleepTimeThreshold); - -/// Amount of encouraged penetration between colliding shapes. -/// Used to reduce oscillating contacts and keep the collision cache warm. -/// Defaults to 0.1. If you have poor simulation quality, -/// increase this number as much as possible without allowing visible amounts of overlap. -CP_EXPORT cpFloat cpSpaceGetCollisionSlop(const cpSpace *space); -CP_EXPORT void cpSpaceSetCollisionSlop(cpSpace *space, cpFloat collisionSlop); - -/// Determines how fast overlapping shapes are pushed apart. -/// Expressed as a fraction of the error remaining after each second. -/// Defaults to pow(1.0 - 0.1, 60.0) meaning that Chipmunk fixes 10% of overlap each frame at 60Hz. -CP_EXPORT cpFloat cpSpaceGetCollisionBias(const cpSpace *space); -CP_EXPORT void cpSpaceSetCollisionBias(cpSpace *space, cpFloat collisionBias); - -/// Number of frames that contact information should persist. -/// Defaults to 3. There is probably never a reason to change this value. -CP_EXPORT cpTimestamp cpSpaceGetCollisionPersistence(const cpSpace *space); -CP_EXPORT void cpSpaceSetCollisionPersistence(cpSpace *space, cpTimestamp collisionPersistence); - -/// User definable data pointer. -/// Generally this points to your game's controller or game state -/// class so you can access it when given a cpSpace reference in a callback. -CP_EXPORT cpDataPointer cpSpaceGetUserData(const cpSpace *space); -CP_EXPORT void cpSpaceSetUserData(cpSpace *space, cpDataPointer userData); - -/// The Space provided static body for a given cpSpace. -/// This is merely provided for convenience and you are not required to use it. -CP_EXPORT cpBody* cpSpaceGetStaticBody(const cpSpace *space); - -/// Returns the current (or most recent) time step used with the given space. -/// Useful from callbacks if your time step is not a compile-time global. -CP_EXPORT cpFloat cpSpaceGetCurrentTimeStep(const cpSpace *space); - -/// returns true from inside a callback when objects cannot be added/removed. -CP_EXPORT cpBool cpSpaceIsLocked(cpSpace *space); - - -//MARK: Collision Handlers - -/// Create or return the existing collision handler that is called for all collisions that are not handled by a more specific collision handler. -CP_EXPORT cpCollisionHandler *cpSpaceAddDefaultCollisionHandler(cpSpace *space); -/// Create or return the existing collision handler for the specified pair of collision types. -/// If wildcard handlers are used with either of the collision types, it's the responibility of the custom handler to invoke the wildcard handlers. -CP_EXPORT cpCollisionHandler *cpSpaceAddCollisionHandler(cpSpace *space, cpCollisionType a, cpCollisionType b); -/// Create or return the existing wildcard collision handler for the specified type. -CP_EXPORT cpCollisionHandler *cpSpaceAddWildcardHandler(cpSpace *space, cpCollisionType type); - - -//MARK: Add/Remove objects - -/// Add a collision shape to the simulation. -/// If the shape is attached to a static body, it will be added as a static shape. -CP_EXPORT cpShape* cpSpaceAddShape(cpSpace *space, cpShape *shape); -/// Add a rigid body to the simulation. -CP_EXPORT cpBody* cpSpaceAddBody(cpSpace *space, cpBody *body); -/// Add a constraint to the simulation. -CP_EXPORT cpConstraint* cpSpaceAddConstraint(cpSpace *space, cpConstraint *constraint); - -/// Remove a collision shape from the simulation. -CP_EXPORT void cpSpaceRemoveShape(cpSpace *space, cpShape *shape); -/// Remove a rigid body from the simulation. -CP_EXPORT void cpSpaceRemoveBody(cpSpace *space, cpBody *body); -/// Remove a constraint from the simulation. -CP_EXPORT void cpSpaceRemoveConstraint(cpSpace *space, cpConstraint *constraint); - -/// Test if a collision shape has been added to the space. -CP_EXPORT cpBool cpSpaceContainsShape(cpSpace *space, cpShape *shape); -/// Test if a rigid body has been added to the space. -CP_EXPORT cpBool cpSpaceContainsBody(cpSpace *space, cpBody *body); -/// Test if a constraint has been added to the space. -CP_EXPORT cpBool cpSpaceContainsConstraint(cpSpace *space, cpConstraint *constraint); - -//MARK: Post-Step Callbacks - -/// Post Step callback function type. -typedef void (*cpPostStepFunc)(cpSpace *space, void *key, void *data); -/// Schedule a post-step callback to be called when cpSpaceStep() finishes. -/// You can only register one callback per unique value for @c key. -/// Returns true only if @c key has never been scheduled before. -/// It's possible to pass @c NULL for @c func if you only want to mark @c key as being used. -CP_EXPORT cpBool cpSpaceAddPostStepCallback(cpSpace *space, cpPostStepFunc func, void *key, void *data); - - -//MARK: Queries - -// TODO: Queries and iterators should take a cpSpace parametery. -// TODO: They should also be abortable. - -/// Nearest point query callback function type. -typedef void (*cpSpacePointQueryFunc)(cpShape *shape, cpVect point, cpFloat distance, cpVect gradient, void *data); -/// Query the space at a point and call @c func for each shape found. -CP_EXPORT void cpSpacePointQuery(cpSpace *space, cpVect point, cpFloat maxDistance, cpShapeFilter filter, cpSpacePointQueryFunc func, void *data); -/// Query the space at a point and return the nearest shape found. Returns NULL if no shapes were found. -CP_EXPORT cpShape *cpSpacePointQueryNearest(cpSpace *space, cpVect point, cpFloat maxDistance, cpShapeFilter filter, cpPointQueryInfo *out); - -/// Segment query callback function type. -typedef void (*cpSpaceSegmentQueryFunc)(cpShape *shape, cpVect point, cpVect normal, cpFloat alpha, void *data); -/// Perform a directed line segment query (like a raycast) against the space calling @c func for each shape intersected. -CP_EXPORT void cpSpaceSegmentQuery(cpSpace *space, cpVect start, cpVect end, cpFloat radius, cpShapeFilter filter, cpSpaceSegmentQueryFunc func, void *data); -/// Perform a directed line segment query (like a raycast) against the space and return the first shape hit. Returns NULL if no shapes were hit. -CP_EXPORT cpShape *cpSpaceSegmentQueryFirst(cpSpace *space, cpVect start, cpVect end, cpFloat radius, cpShapeFilter filter, cpSegmentQueryInfo *out); - -/// Rectangle Query callback function type. -typedef void (*cpSpaceBBQueryFunc)(cpShape *shape, void *data); -/// Perform a fast rectangle query on the space calling @c func for each shape found. -/// Only the shape's bounding boxes are checked for overlap, not their full shape. -CP_EXPORT void cpSpaceBBQuery(cpSpace *space, cpBB bb, cpShapeFilter filter, cpSpaceBBQueryFunc func, void *data); - -/// Shape query callback function type. -typedef void (*cpSpaceShapeQueryFunc)(cpShape *shape, cpContactPointSet *points, void *data); -/// Query a space for any shapes overlapping the given shape and call @c func for each shape found. -CP_EXPORT cpBool cpSpaceShapeQuery(cpSpace *space, cpShape *shape, cpSpaceShapeQueryFunc func, void *data); - - -//MARK: Iteration - -/// Space/body iterator callback function type. -typedef void (*cpSpaceBodyIteratorFunc)(cpBody *body, void *data); -/// Call @c func for each body in the space. -CP_EXPORT void cpSpaceEachBody(cpSpace *space, cpSpaceBodyIteratorFunc func, void *data); - -/// Space/body iterator callback function type. -typedef void (*cpSpaceShapeIteratorFunc)(cpShape *shape, void *data); -/// Call @c func for each shape in the space. -CP_EXPORT void cpSpaceEachShape(cpSpace *space, cpSpaceShapeIteratorFunc func, void *data); - -/// Space/constraint iterator callback function type. -typedef void (*cpSpaceConstraintIteratorFunc)(cpConstraint *constraint, void *data); -/// Call @c func for each shape in the space. -CP_EXPORT void cpSpaceEachConstraint(cpSpace *space, cpSpaceConstraintIteratorFunc func, void *data); - - -//MARK: Indexing - -/// Update the collision detection info for the static shapes in the space. -CP_EXPORT void cpSpaceReindexStatic(cpSpace *space); -/// Update the collision detection data for a specific shape in the space. -CP_EXPORT void cpSpaceReindexShape(cpSpace *space, cpShape *shape); -/// Update the collision detection data for all shapes attached to a body. -CP_EXPORT void cpSpaceReindexShapesForBody(cpSpace *space, cpBody *body); - -/// Switch the space to use a spatial has as it's spatial index. -CP_EXPORT void cpSpaceUseSpatialHash(cpSpace *space, cpFloat dim, int count); - - -//MARK: Time Stepping - -/// Step the space forward in time by @c dt. -CP_EXPORT void cpSpaceStep(cpSpace *space, cpFloat dt); - - -//MARK: Debug API - -#ifndef CP_SPACE_DISABLE_DEBUG_API - -/// Color type to use with the space debug drawing API. -typedef struct cpSpaceDebugColor { - float r, g, b, a; -} cpSpaceDebugColor; - -/// Callback type for a function that draws a filled, stroked circle. -typedef void (*cpSpaceDebugDrawCircleImpl)(cpVect pos, cpFloat angle, cpFloat radius, cpSpaceDebugColor outlineColor, cpSpaceDebugColor fillColor, cpDataPointer data); -/// Callback type for a function that draws a line segment. -typedef void (*cpSpaceDebugDrawSegmentImpl)(cpVect a, cpVect b, cpSpaceDebugColor color, cpDataPointer data); -/// Callback type for a function that draws a thick line segment. -typedef void (*cpSpaceDebugDrawFatSegmentImpl)(cpVect a, cpVect b, cpFloat radius, cpSpaceDebugColor outlineColor, cpSpaceDebugColor fillColor, cpDataPointer data); -/// Callback type for a function that draws a convex polygon. -typedef void (*cpSpaceDebugDrawPolygonImpl)(int count, const cpVect *verts, cpFloat radius, cpSpaceDebugColor outlineColor, cpSpaceDebugColor fillColor, cpDataPointer data); -/// Callback type for a function that draws a dot. -typedef void (*cpSpaceDebugDrawDotImpl)(cpFloat size, cpVect pos, cpSpaceDebugColor color, cpDataPointer data); -/// Callback type for a function that returns a color for a given shape. This gives you an opportunity to color shapes based on how they are used in your engine. -typedef cpSpaceDebugColor (*cpSpaceDebugDrawColorForShapeImpl)(cpShape *shape, cpDataPointer data); - -typedef enum cpSpaceDebugDrawFlags { - CP_SPACE_DEBUG_DRAW_SHAPES = 1<<0, - CP_SPACE_DEBUG_DRAW_CONSTRAINTS = 1<<1, - CP_SPACE_DEBUG_DRAW_COLLISION_POINTS = 1<<2, -} cpSpaceDebugDrawFlags; - -/// Struct used with cpSpaceDebugDraw() containing drawing callbacks and other drawing settings. -typedef struct cpSpaceDebugDrawOptions { - /// Function that will be invoked to draw circles. - cpSpaceDebugDrawCircleImpl drawCircle; - /// Function that will be invoked to draw line segments. - cpSpaceDebugDrawSegmentImpl drawSegment; - /// Function that will be invoked to draw thick line segments. - cpSpaceDebugDrawFatSegmentImpl drawFatSegment; - /// Function that will be invoked to draw convex polygons. - cpSpaceDebugDrawPolygonImpl drawPolygon; - /// Function that will be invoked to draw dots. - cpSpaceDebugDrawDotImpl drawDot; - - /// Flags that request which things to draw (collision shapes, constraints, contact points). - cpSpaceDebugDrawFlags flags; - /// Outline color passed to the drawing function. - cpSpaceDebugColor shapeOutlineColor; - /// Function that decides what fill color to draw shapes using. - cpSpaceDebugDrawColorForShapeImpl colorForShape; - /// Color passed to drawing functions for constraints. - cpSpaceDebugColor constraintColor; - /// Color passed to drawing functions for collision points. - cpSpaceDebugColor collisionPointColor; - - /// User defined context pointer passed to all of the callback functions as the 'data' argument. - cpDataPointer data; -} cpSpaceDebugDrawOptions; - -/// Debug draw the current state of the space using the supplied drawing options. -CP_EXPORT void cpSpaceDebugDraw(cpSpace *space, cpSpaceDebugDrawOptions *options); - -#endif - -/// @} diff --git a/apecs-physics/Chipmunk2D/include/chipmunk/cpSpatialIndex.h b/apecs-physics/Chipmunk2D/include/chipmunk/cpSpatialIndex.h deleted file mode 100644 index 1f7c68c..0000000 --- a/apecs-physics/Chipmunk2D/include/chipmunk/cpSpatialIndex.h +++ /dev/null @@ -1,227 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/** - @defgroup cpSpatialIndex cpSpatialIndex - - Spatial indexes are data structures that are used to accelerate collision detection - and spatial queries. Chipmunk provides a number of spatial index algorithms to pick from - and they are programmed in a generic way so that you can use them for holding more than - just cpShape structs. - - It works by using @c void pointers to the objects you add and using a callback to ask your code - for bounding boxes when it needs them. Several types of queries can be performed an index as well - as reindexing and full collision information. All communication to the spatial indexes is performed - through callback functions. - - Spatial indexes should be treated as opaque structs. - This meanns you shouldn't be reading any of the struct fields. - @{ -*/ - -//MARK: Spatial Index - -/// Spatial index bounding box callback function type. -/// The spatial index calls this function and passes you a pointer to an object you added -/// when it needs to get the bounding box associated with that object. -typedef cpBB (*cpSpatialIndexBBFunc)(void *obj); -/// Spatial index/object iterator callback function type. -typedef void (*cpSpatialIndexIteratorFunc)(void *obj, void *data); -/// Spatial query callback function type. -typedef cpCollisionID (*cpSpatialIndexQueryFunc)(void *obj1, void *obj2, cpCollisionID id, void *data); -/// Spatial segment query callback function type. -typedef cpFloat (*cpSpatialIndexSegmentQueryFunc)(void *obj1, void *obj2, void *data); - - -typedef struct cpSpatialIndexClass cpSpatialIndexClass; -typedef struct cpSpatialIndex cpSpatialIndex; - -/// @private -struct cpSpatialIndex { - cpSpatialIndexClass *klass; - - cpSpatialIndexBBFunc bbfunc; - - cpSpatialIndex *staticIndex, *dynamicIndex; -}; - - -//MARK: Spatial Hash - -typedef struct cpSpaceHash cpSpaceHash; - -/// Allocate a spatial hash. -CP_EXPORT cpSpaceHash* cpSpaceHashAlloc(void); -/// Initialize a spatial hash. -CP_EXPORT cpSpatialIndex* cpSpaceHashInit(cpSpaceHash *hash, cpFloat celldim, int numcells, cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex); -/// Allocate and initialize a spatial hash. -CP_EXPORT cpSpatialIndex* cpSpaceHashNew(cpFloat celldim, int cells, cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex); - -/// Change the cell dimensions and table size of the spatial hash to tune it. -/// The cell dimensions should roughly match the average size of your objects -/// and the table size should be ~10 larger than the number of objects inserted. -/// Some trial and error is required to find the optimum numbers for efficiency. -CP_EXPORT void cpSpaceHashResize(cpSpaceHash *hash, cpFloat celldim, int numcells); - -//MARK: AABB Tree - -typedef struct cpBBTree cpBBTree; - -/// Allocate a bounding box tree. -CP_EXPORT cpBBTree* cpBBTreeAlloc(void); -/// Initialize a bounding box tree. -CP_EXPORT cpSpatialIndex* cpBBTreeInit(cpBBTree *tree, cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex); -/// Allocate and initialize a bounding box tree. -CP_EXPORT cpSpatialIndex* cpBBTreeNew(cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex); - -/// Perform a static top down optimization of the tree. -CP_EXPORT void cpBBTreeOptimize(cpSpatialIndex *index); - -/// Bounding box tree velocity callback function. -/// This function should return an estimate for the object's velocity. -typedef cpVect (*cpBBTreeVelocityFunc)(void *obj); -/// Set the velocity function for the bounding box tree to enable temporal coherence. -CP_EXPORT void cpBBTreeSetVelocityFunc(cpSpatialIndex *index, cpBBTreeVelocityFunc func); - -//MARK: Single Axis Sweep - -typedef struct cpSweep1D cpSweep1D; - -/// Allocate a 1D sort and sweep broadphase. -CP_EXPORT cpSweep1D* cpSweep1DAlloc(void); -/// Initialize a 1D sort and sweep broadphase. -CP_EXPORT cpSpatialIndex* cpSweep1DInit(cpSweep1D *sweep, cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex); -/// Allocate and initialize a 1D sort and sweep broadphase. -CP_EXPORT cpSpatialIndex* cpSweep1DNew(cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex); - -//MARK: Spatial Index Implementation - -typedef void (*cpSpatialIndexDestroyImpl)(cpSpatialIndex *index); - -typedef int (*cpSpatialIndexCountImpl)(cpSpatialIndex *index); -typedef void (*cpSpatialIndexEachImpl)(cpSpatialIndex *index, cpSpatialIndexIteratorFunc func, void *data); - -typedef cpBool (*cpSpatialIndexContainsImpl)(cpSpatialIndex *index, void *obj, cpHashValue hashid); -typedef void (*cpSpatialIndexInsertImpl)(cpSpatialIndex *index, void *obj, cpHashValue hashid); -typedef void (*cpSpatialIndexRemoveImpl)(cpSpatialIndex *index, void *obj, cpHashValue hashid); - -typedef void (*cpSpatialIndexReindexImpl)(cpSpatialIndex *index); -typedef void (*cpSpatialIndexReindexObjectImpl)(cpSpatialIndex *index, void *obj, cpHashValue hashid); -typedef void (*cpSpatialIndexReindexQueryImpl)(cpSpatialIndex *index, cpSpatialIndexQueryFunc func, void *data); - -typedef void (*cpSpatialIndexQueryImpl)(cpSpatialIndex *index, void *obj, cpBB bb, cpSpatialIndexQueryFunc func, void *data); -typedef void (*cpSpatialIndexSegmentQueryImpl)(cpSpatialIndex *index, void *obj, cpVect a, cpVect b, cpFloat t_exit, cpSpatialIndexSegmentQueryFunc func, void *data); - -struct cpSpatialIndexClass { - cpSpatialIndexDestroyImpl destroy; - - cpSpatialIndexCountImpl count; - cpSpatialIndexEachImpl each; - - cpSpatialIndexContainsImpl contains; - cpSpatialIndexInsertImpl insert; - cpSpatialIndexRemoveImpl remove; - - cpSpatialIndexReindexImpl reindex; - cpSpatialIndexReindexObjectImpl reindexObject; - cpSpatialIndexReindexQueryImpl reindexQuery; - - cpSpatialIndexQueryImpl query; - cpSpatialIndexSegmentQueryImpl segmentQuery; -}; - -/// Destroy and free a spatial index. -CP_EXPORT void cpSpatialIndexFree(cpSpatialIndex *index); -/// Collide the objects in @c dynamicIndex against the objects in @c staticIndex using the query callback function. -CP_EXPORT void cpSpatialIndexCollideStatic(cpSpatialIndex *dynamicIndex, cpSpatialIndex *staticIndex, cpSpatialIndexQueryFunc func, void *data); - -/// Destroy a spatial index. -static inline void cpSpatialIndexDestroy(cpSpatialIndex *index) -{ - if(index->klass) index->klass->destroy(index); -} - -/// Get the number of objects in the spatial index. -static inline int cpSpatialIndexCount(cpSpatialIndex *index) -{ - return index->klass->count(index); -} - -/// Iterate the objects in the spatial index. @c func will be called once for each object. -static inline void cpSpatialIndexEach(cpSpatialIndex *index, cpSpatialIndexIteratorFunc func, void *data) -{ - index->klass->each(index, func, data); -} - -/// Returns true if the spatial index contains the given object. -/// Most spatial indexes use hashed storage, so you must provide a hash value too. -static inline cpBool cpSpatialIndexContains(cpSpatialIndex *index, void *obj, cpHashValue hashid) -{ - return index->klass->contains(index, obj, hashid); -} - -/// Add an object to a spatial index. -/// Most spatial indexes use hashed storage, so you must provide a hash value too. -static inline void cpSpatialIndexInsert(cpSpatialIndex *index, void *obj, cpHashValue hashid) -{ - index->klass->insert(index, obj, hashid); -} - -/// Remove an object from a spatial index. -/// Most spatial indexes use hashed storage, so you must provide a hash value too. -static inline void cpSpatialIndexRemove(cpSpatialIndex *index, void *obj, cpHashValue hashid) -{ - index->klass->remove(index, obj, hashid); -} - -/// Perform a full reindex of a spatial index. -static inline void cpSpatialIndexReindex(cpSpatialIndex *index) -{ - index->klass->reindex(index); -} - -/// Reindex a single object in the spatial index. -static inline void cpSpatialIndexReindexObject(cpSpatialIndex *index, void *obj, cpHashValue hashid) -{ - index->klass->reindexObject(index, obj, hashid); -} - -/// Perform a rectangle query against the spatial index, calling @c func for each potential match. -static inline void cpSpatialIndexQuery(cpSpatialIndex *index, void *obj, cpBB bb, cpSpatialIndexQueryFunc func, void *data) -{ - index->klass->query(index, obj, bb, func, data); -} - -/// Perform a segment query against the spatial index, calling @c func for each potential match. -static inline void cpSpatialIndexSegmentQuery(cpSpatialIndex *index, void *obj, cpVect a, cpVect b, cpFloat t_exit, cpSpatialIndexSegmentQueryFunc func, void *data) -{ - index->klass->segmentQuery(index, obj, a, b, t_exit, func, data); -} - -/// Simultaneously reindex and find all colliding objects. -/// @c func will be called once for each potentially overlapping pair of objects found. -/// If the spatial index was initialized with a static index, it will collide it's objects against that as well. -static inline void cpSpatialIndexReindexQuery(cpSpatialIndex *index, cpSpatialIndexQueryFunc func, void *data) -{ - index->klass->reindexQuery(index, func, data); -} - -///@} diff --git a/apecs-physics/Chipmunk2D/include/chipmunk/cpTransform.h b/apecs-physics/Chipmunk2D/include/chipmunk/cpTransform.h deleted file mode 100644 index 4a6256b..0000000 --- a/apecs-physics/Chipmunk2D/include/chipmunk/cpTransform.h +++ /dev/null @@ -1,198 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef CHIPMUNK_TRANSFORM_H -#define CHIPMUNK_TRANSFORM_H - -#include "chipmunk_types.h" -#include "cpVect.h" -#include "cpBB.h" - -/// Identity transform matrix. -static const cpTransform cpTransformIdentity = {1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f}; - -/// Construct a new transform matrix. -/// (a, b) is the x basis vector. -/// (c, d) is the y basis vector. -/// (tx, ty) is the translation. -static inline cpTransform -cpTransformNew(cpFloat a, cpFloat b, cpFloat c, cpFloat d, cpFloat tx, cpFloat ty) -{ - cpTransform t = {a, b, c, d, tx, ty}; - return t; -} - -/// Construct a new transform matrix in transposed order. -static inline cpTransform -cpTransformNewTranspose(cpFloat a, cpFloat c, cpFloat tx, cpFloat b, cpFloat d, cpFloat ty) -{ - cpTransform t = {a, b, c, d, tx, ty}; - return t; -} - -/// Get the inverse of a transform matrix. -static inline cpTransform -cpTransformInverse(cpTransform t) -{ - cpFloat inv_det = 1.0/(t.a*t.d - t.c*t.b); - return cpTransformNewTranspose( - t.d*inv_det, -t.c*inv_det, (t.c*t.ty - t.tx*t.d)*inv_det, - -t.b*inv_det, t.a*inv_det, (t.tx*t.b - t.a*t.ty)*inv_det - ); -} - -/// Multiply two transformation matrices. -static inline cpTransform -cpTransformMult(cpTransform t1, cpTransform t2) -{ - return cpTransformNewTranspose( - t1.a*t2.a + t1.c*t2.b, t1.a*t2.c + t1.c*t2.d, t1.a*t2.tx + t1.c*t2.ty + t1.tx, - t1.b*t2.a + t1.d*t2.b, t1.b*t2.c + t1.d*t2.d, t1.b*t2.tx + t1.d*t2.ty + t1.ty - ); -} - -/// Transform an absolute point. (i.e. a vertex) -static inline cpVect -cpTransformPoint(cpTransform t, cpVect p) -{ - return cpv(t.a*p.x + t.c*p.y + t.tx, t.b*p.x + t.d*p.y + t.ty); -} - -/// Transform a vector (i.e. a normal) -static inline cpVect -cpTransformVect(cpTransform t, cpVect v) -{ - return cpv(t.a*v.x + t.c*v.y, t.b*v.x + t.d*v.y); -} - -/// Transform a cpBB. -static inline cpBB -cpTransformbBB(cpTransform t, cpBB bb) -{ - cpVect center = cpBBCenter(bb); - cpFloat hw = (bb.r - bb.l)*0.5; - cpFloat hh = (bb.t - bb.b)*0.5; - - cpFloat a = t.a*hw, b = t.c*hh, d = t.b*hw, e = t.d*hh; - cpFloat hw_max = cpfmax(cpfabs(a + b), cpfabs(a - b)); - cpFloat hh_max = cpfmax(cpfabs(d + e), cpfabs(d - e)); - return cpBBNewForExtents(cpTransformPoint(t, center), hw_max, hh_max); -} - -/// Create a transation matrix. -static inline cpTransform -cpTransformTranslate(cpVect translate) -{ - return cpTransformNewTranspose( - 1.0, 0.0, translate.x, - 0.0, 1.0, translate.y - ); -} - -/// Create a scale matrix. -static inline cpTransform -cpTransformScale(cpFloat scaleX, cpFloat scaleY) -{ - return cpTransformNewTranspose( - scaleX, 0.0, 0.0, - 0.0, scaleY, 0.0 - ); -} - -/// Create a rotation matrix. -static inline cpTransform -cpTransformRotate(cpFloat radians) -{ - cpVect rot = cpvforangle(radians); - return cpTransformNewTranspose( - rot.x, -rot.y, 0.0, - rot.y, rot.x, 0.0 - ); -} - -/// Create a rigid transformation matrix. (transation + rotation) -static inline cpTransform -cpTransformRigid(cpVect translate, cpFloat radians) -{ - cpVect rot = cpvforangle(radians); - return cpTransformNewTranspose( - rot.x, -rot.y, translate.x, - rot.y, rot.x, translate.y - ); -} - -/// Fast inverse of a rigid transformation matrix. -static inline cpTransform -cpTransformRigidInverse(cpTransform t) -{ - return cpTransformNewTranspose( - t.d, -t.c, (t.c*t.ty - t.tx*t.d), - -t.b, t.a, (t.tx*t.b - t.a*t.ty) - ); -} - -//MARK: Miscellaneous (but useful) transformation matrices. -// See source for documentation... - -static inline cpTransform -cpTransformWrap(cpTransform outer, cpTransform inner) -{ - return cpTransformMult(cpTransformInverse(outer), cpTransformMult(inner, outer)); -} - -static inline cpTransform -cpTransformWrapInverse(cpTransform outer, cpTransform inner) -{ - return cpTransformMult(outer, cpTransformMult(inner, cpTransformInverse(outer))); -} - -static inline cpTransform -cpTransformOrtho(cpBB bb) -{ - return cpTransformNewTranspose( - 2.0/(bb.r - bb.l), 0.0, -(bb.r + bb.l)/(bb.r - bb.l), - 0.0, 2.0/(bb.t - bb.b), -(bb.t + bb.b)/(bb.t - bb.b) - ); -} - -static inline cpTransform -cpTransformBoneScale(cpVect v0, cpVect v1) -{ - cpVect d = cpvsub(v1, v0); - return cpTransformNewTranspose( - d.x, -d.y, v0.x, - d.y, d.x, v0.y - ); -} - -static inline cpTransform -cpTransformAxialScale(cpVect axis, cpVect pivot, cpFloat scale) -{ - cpFloat A = axis.x*axis.y*(scale - 1.0); - cpFloat B = cpvdot(axis, pivot)*(1.0 - scale); - - return cpTransformNewTranspose( - scale*axis.x*axis.x + axis.y*axis.y, A, axis.x*B, - A, axis.x*axis.x + scale*axis.y*axis.y, axis.y*B - ); -} - -#endif diff --git a/apecs-physics/Chipmunk2D/include/chipmunk/cpVect.h b/apecs-physics/Chipmunk2D/include/chipmunk/cpVect.h deleted file mode 100644 index 8ec02bd..0000000 --- a/apecs-physics/Chipmunk2D/include/chipmunk/cpVect.h +++ /dev/null @@ -1,230 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef CHIPMUNK_VECT_H -#define CHIPMUNK_VECT_H - -#include "chipmunk_types.h" - -/// @defgroup cpVect cpVect -/// Chipmunk's 2D vector type along with a handy 2D vector math lib. -/// @{ - -/// Constant for the zero vector. -static const cpVect cpvzero = {0.0f,0.0f}; - -/// Convenience constructor for cpVect structs. -static inline cpVect cpv(const cpFloat x, const cpFloat y) -{ - cpVect v = {x, y}; - return v; -} - -/// Check if two vectors are equal. (Be careful when comparing floating point numbers!) -static inline cpBool cpveql(const cpVect v1, const cpVect v2) -{ - return (v1.x == v2.x && v1.y == v2.y); -} - -/// Add two vectors -static inline cpVect cpvadd(const cpVect v1, const cpVect v2) -{ - return cpv(v1.x + v2.x, v1.y + v2.y); -} - -/// Subtract two vectors. -static inline cpVect cpvsub(const cpVect v1, const cpVect v2) -{ - return cpv(v1.x - v2.x, v1.y - v2.y); -} - -/// Negate a vector. -static inline cpVect cpvneg(const cpVect v) -{ - return cpv(-v.x, -v.y); -} - -/// Scalar multiplication. -static inline cpVect cpvmult(const cpVect v, const cpFloat s) -{ - return cpv(v.x*s, v.y*s); -} - -/// Vector dot product. -static inline cpFloat cpvdot(const cpVect v1, const cpVect v2) -{ - return v1.x*v2.x + v1.y*v2.y; -} - -/// 2D vector cross product analog. -/// The cross product of 2D vectors results in a 3D vector with only a z component. -/// This function returns the magnitude of the z value. -static inline cpFloat cpvcross(const cpVect v1, const cpVect v2) -{ - return v1.x*v2.y - v1.y*v2.x; -} - -/// Returns a perpendicular vector. (90 degree rotation) -static inline cpVect cpvperp(const cpVect v) -{ - return cpv(-v.y, v.x); -} - -/// Returns a perpendicular vector. (-90 degree rotation) -static inline cpVect cpvrperp(const cpVect v) -{ - return cpv(v.y, -v.x); -} - -/// Returns the vector projection of v1 onto v2. -static inline cpVect cpvproject(const cpVect v1, const cpVect v2) -{ - return cpvmult(v2, cpvdot(v1, v2)/cpvdot(v2, v2)); -} - -/// Returns the unit length vector for the given angle (in radians). -static inline cpVect cpvforangle(const cpFloat a) -{ - return cpv(cpfcos(a), cpfsin(a)); -} - -/// Returns the angular direction v is pointing in (in radians). -static inline cpFloat cpvtoangle(const cpVect v) -{ - return cpfatan2(v.y, v.x); -} - -/// Uses complex number multiplication to rotate v1 by v2. Scaling will occur if v1 is not a unit vector. -static inline cpVect cpvrotate(const cpVect v1, const cpVect v2) -{ - return cpv(v1.x*v2.x - v1.y*v2.y, v1.x*v2.y + v1.y*v2.x); -} - -/// Inverse of cpvrotate(). -static inline cpVect cpvunrotate(const cpVect v1, const cpVect v2) -{ - return cpv(v1.x*v2.x + v1.y*v2.y, v1.y*v2.x - v1.x*v2.y); -} - -/// Returns the squared length of v. Faster than cpvlength() when you only need to compare lengths. -static inline cpFloat cpvlengthsq(const cpVect v) -{ - return cpvdot(v, v); -} - -/// Returns the length of v. -static inline cpFloat cpvlength(const cpVect v) -{ - return cpfsqrt(cpvdot(v, v)); -} - -/// Linearly interpolate between v1 and v2. -static inline cpVect cpvlerp(const cpVect v1, const cpVect v2, const cpFloat t) -{ - return cpvadd(cpvmult(v1, 1.0f - t), cpvmult(v2, t)); -} - -/// Returns a normalized copy of v. -static inline cpVect cpvnormalize(const cpVect v) -{ - // Neat trick I saw somewhere to avoid div/0. - return cpvmult(v, 1.0f/(cpvlength(v) + CPFLOAT_MIN)); -} - -/// Spherical linearly interpolate between v1 and v2. -static inline cpVect -cpvslerp(const cpVect v1, const cpVect v2, const cpFloat t) -{ - cpFloat dot = cpvdot(cpvnormalize(v1), cpvnormalize(v2)); - cpFloat omega = cpfacos(cpfclamp(dot, -1.0f, 1.0f)); - - if(omega < 1e-3){ - // If the angle between two vectors is very small, lerp instead to avoid precision issues. - return cpvlerp(v1, v2, t); - } else { - cpFloat denom = 1.0f/cpfsin(omega); - return cpvadd(cpvmult(v1, cpfsin((1.0f - t)*omega)*denom), cpvmult(v2, cpfsin(t*omega)*denom)); - } -} - -/// Spherical linearly interpolate between v1 towards v2 by no more than angle a radians -static inline cpVect -cpvslerpconst(const cpVect v1, const cpVect v2, const cpFloat a) -{ - cpFloat dot = cpvdot(cpvnormalize(v1), cpvnormalize(v2)); - cpFloat omega = cpfacos(cpfclamp(dot, -1.0f, 1.0f)); - - return cpvslerp(v1, v2, cpfmin(a, omega)/omega); -} - -/// Clamp v to length len. -static inline cpVect cpvclamp(const cpVect v, const cpFloat len) -{ - return (cpvdot(v,v) > len*len) ? cpvmult(cpvnormalize(v), len) : v; -} - -/// Linearly interpolate between v1 towards v2 by distance d. -static inline cpVect cpvlerpconst(cpVect v1, cpVect v2, cpFloat d) -{ - return cpvadd(v1, cpvclamp(cpvsub(v2, v1), d)); -} - -/// Returns the distance between v1 and v2. -static inline cpFloat cpvdist(const cpVect v1, const cpVect v2) -{ - return cpvlength(cpvsub(v1, v2)); -} - -/// Returns the squared distance between v1 and v2. Faster than cpvdist() when you only need to compare distances. -static inline cpFloat cpvdistsq(const cpVect v1, const cpVect v2) -{ - return cpvlengthsq(cpvsub(v1, v2)); -} - -/// Returns true if the distance between v1 and v2 is less than dist. -static inline cpBool cpvnear(const cpVect v1, const cpVect v2, const cpFloat dist) -{ - return cpvdistsq(v1, v2) < dist*dist; -} - -/// @} - -/// @defgroup cpMat2x2 cpMat2x2 -/// 2x2 matrix type used for tensors and such. -/// @{ - -// NUKE -static inline cpMat2x2 -cpMat2x2New(cpFloat a, cpFloat b, cpFloat c, cpFloat d) -{ - cpMat2x2 m = {a, b, c, d}; - return m; -} - -static inline cpVect -cpMat2x2Transform(cpMat2x2 m, cpVect v) -{ - return cpv(v.x*m.a + v.y*m.b, v.x*m.c + v.y*m.d); -} - -///@} - -#endif diff --git a/apecs-physics/Chipmunk2D/include/chipmunk/prime.h b/apecs-physics/Chipmunk2D/include/chipmunk/prime.h deleted file mode 100644 index d470c2c..0000000 --- a/apecs-physics/Chipmunk2D/include/chipmunk/prime.h +++ /dev/null @@ -1,68 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -// Used for resizing hash tables. -// Values approximately double. -// http://planetmath.org/encyclopedia/GoodHashTablePrimes.html -static int primes[] = { - 5, - 13, - 23, - 47, - 97, - 193, - 389, - 769, - 1543, - 3079, - 6151, - 12289, - 24593, - 49157, - 98317, - 196613, - 393241, - 786433, - 1572869, - 3145739, - 6291469, - 12582917, - 25165843, - 50331653, - 100663319, - 201326611, - 402653189, - 805306457, - 1610612741, - 0, -}; - -static inline int -next_prime(int n) -{ - int i = 0; - while(n > primes[i]){ - i++; - cpAssertHard(primes[i], "Tried to resize a hash table to a size greater than 1610612741 O_o"); // realistically this should never happen - } - - return primes[i]; -} diff --git a/apecs-physics/Chipmunk2D/src/CMakeLists.txt b/apecs-physics/Chipmunk2D/src/CMakeLists.txt deleted file mode 100644 index c1f5449..0000000 --- a/apecs-physics/Chipmunk2D/src/CMakeLists.txt +++ /dev/null @@ -1,59 +0,0 @@ -file(GLOB chipmunk_source_files "*.c") -file(GLOB chipmunk_public_header "${chipmunk_SOURCE_DIR}/include/chipmunk/*.h") - -include_directories(${chipmunk_SOURCE_DIR}/include) - -# Chipmunk2D 7.0.2 -set(CHIPMUNK_VERSION_MAJOR 7) -set(CHIPMUNK_VERSION_MINOR 0) -set(CHIPMUNK_VERSION_PATCH 2) -set(CHIPMUNK_VERSION "${CHIPMUNK_VERSION_MAJOR}.${CHIPMUNK_VERSION_MINOR}.${CHIPMUNK_VERSION_PATCH}") -message("Configuring Chipmunk2D version ${CHIPMUNK_VERSION}") - - -if(BUILD_SHARED) - add_library(chipmunk SHARED - ${chipmunk_source_files} - ) - # Tell MSVC to compile the code as C++. - if(MSVC) - set_source_files_properties(${chipmunk_source_files} PROPERTIES LANGUAGE CXX) - set_target_properties(chipmunk PROPERTIES LINKER_LANGUAGE CXX) - endif(MSVC) - # set the lib's version number - # But avoid on Android because symlinks to version numbered .so's don't work with Android's Java-side loadLibrary. - if(NOT ANDROID) - set_target_properties(chipmunk PROPERTIES - SOVERSION ${CHIPMUNK_VERSION_MAJOR} - VERSION ${CHIPMUNK_VERSION}) - endif(NOT ANDROID) - if(ANDROID OR UNIX) - # need to explicitly link to the math library because the CMake/Android toolchains may not do it automatically - target_link_libraries(chipmunk m) - endif(ANDROID OR UNIX) - install(TARGETS chipmunk RUNTIME DESTINATION ${BIN_INSTALL_DIR} - LIBRARY DESTINATION ${LIB_INSTALL_DIR} - ARCHIVE DESTINATION ${LIB_INSTALL_DIR}) -endif(BUILD_SHARED) - -if(BUILD_STATIC) - add_library(chipmunk_static STATIC - ${chipmunk_source_files} - ) - # Tell MSVC to compile the code as C++. - if(MSVC) - set_source_files_properties(${chipmunk_source_files} PROPERTIES LANGUAGE CXX) - set_target_properties(chipmunk_static PROPERTIES LINKER_LANGUAGE CXX) - endif(MSVC) - # Sets chipmunk_static to output "libchipmunk.a" not "libchipmunk_static.a" - set_target_properties(chipmunk_static PROPERTIES OUTPUT_NAME chipmunk) - if(INSTALL_STATIC) - install(TARGETS chipmunk_static ARCHIVE DESTINATION ${LIB_INSTALL_DIR}) - endif(INSTALL_STATIC) -endif(BUILD_STATIC) - -if(BUILD_SHARED OR INSTALL_STATIC) - # FIXME: change to PUBLIC_HEADER to allow building frameworks - install(FILES ${chipmunk_public_header} DESTINATION include/chipmunk) - install(FILES ${chipmunk_constraint_header} DESTINATION include/chipmunk/constraints) -endif(BUILD_SHARED OR INSTALL_STATIC) diff --git a/apecs-physics/Chipmunk2D/src/chipmunk.c b/apecs-physics/Chipmunk2D/src/chipmunk.c deleted file mode 100644 index 238ad6f..0000000 --- a/apecs-physics/Chipmunk2D/src/chipmunk.c +++ /dev/null @@ -1,331 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#if defined(ANDROID) -# include -#endif - -#include "chipmunk_private.h" - -void -cpMessage(const char *condition, const char *file, int line, int isError, int isHardError, const char *message, ...) -{ - fprintf(stderr, (isError ? "Aborting due to Chipmunk error: " : "Chipmunk warning: ")); - - va_list vargs; - va_start(vargs, message); { -#if defined(ANDROID) - __android_log_print( ANDROID_LOG_INFO, "Chipmunk", "%s(%d)", file, line ); - __android_log_print( ANDROID_LOG_INFO, "Chipmunk", message, vargs ); -#else - vfprintf(stderr, message, vargs); - fprintf(stderr, "\n"); -#endif - } va_end(vargs); - -#if defined(ANDROID) - __android_log_print(ANDROID_LOG_INFO, "Chipmunk", "\tFailed condition: %s\n", condition); - __android_log_print(ANDROID_LOG_INFO, "Chipmunk", "\tSource:%s:%d\n", file, line); -#else - fprintf(stderr, "\tFailed condition: %s\n", condition); - fprintf(stderr, "\tSource:%s:%d\n", file, line); -#endif -} - -#define STR(s) #s -#define XSTR(s) STR(s) - -const char *cpVersionString = XSTR(CP_VERSION_MAJOR) "." XSTR(CP_VERSION_MINOR) "." XSTR(CP_VERSION_RELEASE); - -//MARK: Misc Functions - -cpFloat -cpMomentForCircle(cpFloat m, cpFloat r1, cpFloat r2, cpVect offset) -{ - return m*(0.5f*(r1*r1 + r2*r2) + cpvlengthsq(offset)); -} - -cpFloat -cpAreaForCircle(cpFloat r1, cpFloat r2) -{ - return (cpFloat)CP_PI*cpfabs(r1*r1 - r2*r2); -} - -cpFloat -cpMomentForSegment(cpFloat m, cpVect a, cpVect b, cpFloat r) -{ - cpVect offset = cpvlerp(a, b, 0.5f); - - // This approximates the shape as a box for rounded segments, but it's quite close. - cpFloat length = cpvdist(b, a) + 2.0f*r; - return m*((length*length + 4.0f*r*r)/12.0f + cpvlengthsq(offset)); -} - -cpFloat -cpAreaForSegment(cpVect a, cpVect b, cpFloat r) -{ - return r*((cpFloat)CP_PI*r + 2.0f*cpvdist(a, b)); -} - -cpFloat -cpMomentForPoly(cpFloat m, const int count, const cpVect *verts, cpVect offset, cpFloat r) -{ - // TODO account for radius. - if(count == 2) return cpMomentForSegment(m, verts[0], verts[1], 0.0f); - - cpFloat sum1 = 0.0f; - cpFloat sum2 = 0.0f; - for(int i=0; i max.x || (v.x == max.x && v.y > max.y)){ - max = v; - (*end) = i; - } - } -} - -#define SWAP(__A__, __B__) {cpVect __TMP__ = __A__; __A__ = __B__; __B__ = __TMP__;} - -static int -QHullPartition(cpVect *verts, int count, cpVect a, cpVect b, cpFloat tol) -{ - if(count == 0) return 0; - - cpFloat max = 0; - int pivot = 0; - - cpVect delta = cpvsub(b, a); - cpFloat valueTol = tol*cpvlength(delta); - - int head = 0; - for(int tail = count-1; head <= tail;){ - cpFloat value = cpvcross(cpvsub(verts[head], a), delta); - if(value > valueTol){ - if(value > max){ - max = value; - pivot = head; - } - - head++; - } else { - SWAP(verts[head], verts[tail]); - tail--; - } - } - - // move the new pivot to the front if it's not already there. - if(pivot != 0) SWAP(verts[0], verts[pivot]); - return head; -} - -static int -QHullReduce(cpFloat tol, cpVect *verts, int count, cpVect a, cpVect pivot, cpVect b, cpVect *result) -{ - if(count < 0){ - return 0; - } else if(count == 0) { - result[0] = pivot; - return 1; - } else { - int left_count = QHullPartition(verts, count, a, pivot, tol); - int index = QHullReduce(tol, verts + 1, left_count - 1, a, verts[0], pivot, result); - - result[index++] = pivot; - - int right_count = QHullPartition(verts + left_count, count - left_count, pivot, b, tol); - return index + QHullReduce(tol, verts + left_count + 1, right_count - 1, pivot, verts[left_count], b, result + index); - } -} - -// QuickHull seemed like a neat algorithm, and efficient-ish for large input sets. -// My implementation performs an in place reduction using the result array as scratch space. -int -cpConvexHull(int count, const cpVect *verts, cpVect *result, int *first, cpFloat tol) -{ - if(verts != result){ - // Copy the line vertexes into the empty part of the result polyline to use as a scratch buffer. - memcpy(result, verts, count*sizeof(cpVect)); - } - - // Degenerate case, all points are the same. - int start, end; - cpLoopIndexes(verts, count, &start, &end); - if(start == end){ - if(first) (*first) = 0; - return 1; - } - - SWAP(result[0], result[start]); - SWAP(result[1], result[end == 0 ? start : end]); - - cpVect a = result[0]; - cpVect b = result[1]; - - if(first) (*first) = start; - return QHullReduce(tol, result + 2, count - 2, a, b, a, result + 1) + 1; -} - -//MARK: Alternate Block Iterators - -#if defined(__has_extension) -#if __has_extension(blocks) - -static void IteratorFunc(void *ptr, void (^block)(void *ptr)){block(ptr);} - -void cpSpaceEachBody_b(cpSpace *space, void (^block)(cpBody *body)){ - cpSpaceEachBody(space, (cpSpaceBodyIteratorFunc)IteratorFunc, block); -} - -void cpSpaceEachShape_b(cpSpace *space, void (^block)(cpShape *shape)){ - cpSpaceEachShape(space, (cpSpaceShapeIteratorFunc)IteratorFunc, block); -} - -void cpSpaceEachConstraint_b(cpSpace *space, void (^block)(cpConstraint *constraint)){ - cpSpaceEachConstraint(space, (cpSpaceConstraintIteratorFunc)IteratorFunc, block); -} - -static void BodyIteratorFunc(cpBody *body, void *ptr, void (^block)(void *ptr)){block(ptr);} - -void cpBodyEachShape_b(cpBody *body, void (^block)(cpShape *shape)){ - cpBodyEachShape(body, (cpBodyShapeIteratorFunc)BodyIteratorFunc, block); -} - -void cpBodyEachConstraint_b(cpBody *body, void (^block)(cpConstraint *constraint)){ - cpBodyEachConstraint(body, (cpBodyConstraintIteratorFunc)BodyIteratorFunc, block); -} - -void cpBodyEachArbiter_b(cpBody *body, void (^block)(cpArbiter *arbiter)){ - cpBodyEachArbiter(body, (cpBodyArbiterIteratorFunc)BodyIteratorFunc, block); -} - -static void PointQueryIteratorFunc(cpShape *shape, cpVect p, cpFloat d, cpVect g, cpSpacePointQueryBlock block){block(shape, p, d, g);} -void cpSpacePointQuery_b(cpSpace *space, cpVect point, cpFloat maxDistance, cpShapeFilter filter, cpSpacePointQueryBlock block){ - cpSpacePointQuery(space, point, maxDistance, filter, (cpSpacePointQueryFunc)PointQueryIteratorFunc, block); -} - -static void SegmentQueryIteratorFunc(cpShape *shape, cpVect p, cpVect n, cpFloat t, cpSpaceSegmentQueryBlock block){block(shape, p, n, t);} -void cpSpaceSegmentQuery_b(cpSpace *space, cpVect start, cpVect end, cpFloat radius, cpShapeFilter filter, cpSpaceSegmentQueryBlock block){ - cpSpaceSegmentQuery(space, start, end, radius, filter, (cpSpaceSegmentQueryFunc)SegmentQueryIteratorFunc, block); -} - -void cpSpaceBBQuery_b(cpSpace *space, cpBB bb, cpShapeFilter filter, cpSpaceBBQueryBlock block){ - cpSpaceBBQuery(space, bb, filter, (cpSpaceBBQueryFunc)IteratorFunc, block); -} - -static void ShapeQueryIteratorFunc(cpShape *shape, cpContactPointSet *points, cpSpaceShapeQueryBlock block){block(shape, points);} -cpBool cpSpaceShapeQuery_b(cpSpace *space, cpShape *shape, cpSpaceShapeQueryBlock block){ - return cpSpaceShapeQuery(space, shape, (cpSpaceShapeQueryFunc)ShapeQueryIteratorFunc, block); -} - -#endif -#endif - -#include "chipmunk_ffi.h" diff --git a/apecs-physics/Chipmunk2D/src/cpArbiter.c b/apecs-physics/Chipmunk2D/src/cpArbiter.c deleted file mode 100644 index 7e5d911..0000000 --- a/apecs-physics/Chipmunk2D/src/cpArbiter.c +++ /dev/null @@ -1,496 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "chipmunk_private.h" - -// TODO: make this generic so I can reuse it for constraints also. -static inline void -unthreadHelper(cpArbiter *arb, cpBody *body) -{ - struct cpArbiterThread *thread = cpArbiterThreadForBody(arb, body); - cpArbiter *prev = thread->prev; - cpArbiter *next = thread->next; - - if(prev){ - cpArbiterThreadForBody(prev, body)->next = next; - } else if(body->arbiterList == arb) { - // IFF prev is NULL and body->arbiterList == arb, is arb at the head of the list. - // This function may be called for an arbiter that was never in a list. - // In that case, we need to protect it from wiping out the body->arbiterList pointer. - body->arbiterList = next; - } - - if(next) cpArbiterThreadForBody(next, body)->prev = prev; - - thread->prev = NULL; - thread->next = NULL; -} - -void -cpArbiterUnthread(cpArbiter *arb) -{ - unthreadHelper(arb, arb->body_a); - unthreadHelper(arb, arb->body_b); -} - -cpBool cpArbiterIsFirstContact(const cpArbiter *arb) -{ - return arb->state == CP_ARBITER_STATE_FIRST_COLLISION; -} - -cpBool cpArbiterIsRemoval(const cpArbiter *arb) -{ - return arb->state == CP_ARBITER_STATE_INVALIDATED; -} - -int cpArbiterGetCount(const cpArbiter *arb) -{ - // Return 0 contacts if we are in a separate callback. - return (arb->state < CP_ARBITER_STATE_CACHED ? arb->count : 0); -} - -cpVect -cpArbiterGetNormal(const cpArbiter *arb) -{ - return cpvmult(arb->n, arb->swapped ? -1.0f : 1.0); -} - -cpVect -cpArbiterGetPointA(const cpArbiter *arb, int i) -{ - cpAssertHard(0 <= i && i < cpArbiterGetCount(arb), "Index error: The specified contact index is invalid for this arbiter"); - return cpvadd(arb->body_a->p, arb->contacts[i].r1); -} - -cpVect -cpArbiterGetPointB(const cpArbiter *arb, int i) -{ - cpAssertHard(0 <= i && i < cpArbiterGetCount(arb), "Index error: The specified contact index is invalid for this arbiter"); - return cpvadd(arb->body_b->p, arb->contacts[i].r2); -} - -cpFloat -cpArbiterGetDepth(const cpArbiter *arb, int i) -{ - cpAssertHard(0 <= i && i < cpArbiterGetCount(arb), "Index error: The specified contact index is invalid for this arbiter"); - - struct cpContact *con = &arb->contacts[i]; - return cpvdot(cpvadd(cpvsub(con->r2, con->r1), cpvsub(arb->body_b->p, arb->body_a->p)), arb->n); -} - -cpContactPointSet -cpArbiterGetContactPointSet(const cpArbiter *arb) -{ - cpContactPointSet set; - set.count = cpArbiterGetCount(arb); - - cpBool swapped = arb->swapped; - cpVect n = arb->n; - set.normal = (swapped ? cpvneg(n) : n); - - for(int i=0; ibody_a->p, arb->contacts[i].r1); - cpVect p2 = cpvadd(arb->body_b->p, arb->contacts[i].r2); - - set.points[i].pointA = (swapped ? p2 : p1); - set.points[i].pointB = (swapped ? p1 : p2); - set.points[i].distance = cpvdot(cpvsub(p2, p1), n); - } - - return set; -} - -void -cpArbiterSetContactPointSet(cpArbiter *arb, cpContactPointSet *set) -{ - int count = set->count; - cpAssertHard(count == arb->count, "The number of contact points cannot be changed."); - - cpBool swapped = arb->swapped; - arb->n = (swapped ? cpvneg(set->normal) : set->normal); - - for(int i=0; ipoints[i].pointA; - cpVect p2 = set->points[i].pointB; - - arb->contacts[i].r1 = cpvsub(swapped ? p2 : p1, arb->body_a->p); - arb->contacts[i].r2 = cpvsub(swapped ? p1 : p2, arb->body_b->p); - } -} - -cpVect -cpArbiterTotalImpulse(const cpArbiter *arb) -{ - struct cpContact *contacts = arb->contacts; - cpVect n = arb->n; - cpVect sum = cpvzero; - - for(int i=0, count=cpArbiterGetCount(arb); ijnAcc, con->jtAcc))); - } - - return (arb->swapped ? sum : cpvneg(sum)); - return cpvzero; -} - -cpFloat -cpArbiterTotalKE(const cpArbiter *arb) -{ - cpFloat eCoef = (1 - arb->e)/(1 + arb->e); - cpFloat sum = 0.0; - - struct cpContact *contacts = arb->contacts; - for(int i=0, count=cpArbiterGetCount(arb); ijnAcc; - cpFloat jtAcc = con->jtAcc; - - sum += eCoef*jnAcc*jnAcc/con->nMass + jtAcc*jtAcc/con->tMass; - } - - return sum; -} - -cpBool -cpArbiterIgnore(cpArbiter *arb) -{ - arb->state = CP_ARBITER_STATE_IGNORE; - return cpFalse; -} - -cpFloat -cpArbiterGetRestitution(const cpArbiter *arb) -{ - return arb->e; -} - -void -cpArbiterSetRestitution(cpArbiter *arb, cpFloat restitution) -{ - arb->e = restitution; -} - -cpFloat -cpArbiterGetFriction(const cpArbiter *arb) -{ - return arb->u; -} - -void -cpArbiterSetFriction(cpArbiter *arb, cpFloat friction) -{ - arb->u = friction; -} - -cpVect -cpArbiterGetSurfaceVelocity(cpArbiter *arb) -{ - return cpvmult(arb->surface_vr, arb->swapped ? -1.0f : 1.0); -} - -void -cpArbiterSetSurfaceVelocity(cpArbiter *arb, cpVect vr) -{ - arb->surface_vr = cpvmult(vr, arb->swapped ? -1.0f : 1.0); -} - -cpDataPointer -cpArbiterGetUserData(const cpArbiter *arb) -{ - return arb->data; -} - -void -cpArbiterSetUserData(cpArbiter *arb, cpDataPointer userData) -{ - arb->data = userData; -} - -void -cpArbiterGetShapes(const cpArbiter *arb, cpShape **a, cpShape **b) -{ - if(arb->swapped){ - (*a) = (cpShape *)arb->b, (*b) = (cpShape *)arb->a; - } else { - (*a) = (cpShape *)arb->a, (*b) = (cpShape *)arb->b; - } -} - -void cpArbiterGetBodies(const cpArbiter *arb, cpBody **a, cpBody **b) -{ - CP_ARBITER_GET_SHAPES(arb, shape_a, shape_b); - (*a) = shape_a->body; - (*b) = shape_b->body; -} - -cpBool -cpArbiterCallWildcardBeginA(cpArbiter *arb, cpSpace *space) -{ - cpCollisionHandler *handler = arb->handlerA; - return handler->beginFunc(arb, space, handler->userData); -} - -cpBool -cpArbiterCallWildcardBeginB(cpArbiter *arb, cpSpace *space) -{ - cpCollisionHandler *handler = arb->handlerB; - arb->swapped = !arb->swapped; - cpBool retval = handler->beginFunc(arb, space, handler->userData); - arb->swapped = !arb->swapped; - return retval; -} - -cpBool -cpArbiterCallWildcardPreSolveA(cpArbiter *arb, cpSpace *space) -{ - cpCollisionHandler *handler = arb->handlerA; - return handler->preSolveFunc(arb, space, handler->userData); -} - -cpBool -cpArbiterCallWildcardPreSolveB(cpArbiter *arb, cpSpace *space) -{ - cpCollisionHandler *handler = arb->handlerB; - arb->swapped = !arb->swapped; - cpBool retval = handler->preSolveFunc(arb, space, handler->userData); - arb->swapped = !arb->swapped; - return retval; -} - -void -cpArbiterCallWildcardPostSolveA(cpArbiter *arb, cpSpace *space) -{ - cpCollisionHandler *handler = arb->handlerA; - handler->postSolveFunc(arb, space, handler->userData); -} - -void -cpArbiterCallWildcardPostSolveB(cpArbiter *arb, cpSpace *space) -{ - cpCollisionHandler *handler = arb->handlerB; - arb->swapped = !arb->swapped; - handler->postSolveFunc(arb, space, handler->userData); - arb->swapped = !arb->swapped; -} - -void -cpArbiterCallWildcardSeparateA(cpArbiter *arb, cpSpace *space) -{ - cpCollisionHandler *handler = arb->handlerA; - handler->separateFunc(arb, space, handler->userData); -} - -void -cpArbiterCallWildcardSeparateB(cpArbiter *arb, cpSpace *space) -{ - cpCollisionHandler *handler = arb->handlerB; - arb->swapped = !arb->swapped; - handler->separateFunc(arb, space, handler->userData); - arb->swapped = !arb->swapped; -} - -cpArbiter* -cpArbiterInit(cpArbiter *arb, cpShape *a, cpShape *b) -{ - arb->handler = NULL; - arb->swapped = cpFalse; - - arb->handler = NULL; - arb->handlerA = NULL; - arb->handlerB = NULL; - - arb->e = 0.0f; - arb->u = 0.0f; - arb->surface_vr = cpvzero; - - arb->count = 0; - arb->contacts = NULL; - - arb->a = a; arb->body_a = a->body; - arb->b = b; arb->body_b = b->body; - - arb->thread_a.next = NULL; - arb->thread_b.next = NULL; - arb->thread_a.prev = NULL; - arb->thread_b.prev = NULL; - - arb->stamp = 0; - arb->state = CP_ARBITER_STATE_FIRST_COLLISION; - - arb->data = NULL; - - return arb; -} - -static inline cpCollisionHandler * -cpSpaceLookupHandler(cpSpace *space, cpCollisionType a, cpCollisionType b, cpCollisionHandler *defaultValue) -{ - cpCollisionType types[] = {a, b}; - cpCollisionHandler *handler = (cpCollisionHandler *)cpHashSetFind(space->collisionHandlers, CP_HASH_PAIR(a, b), types); - return (handler ? handler : defaultValue); -} - -void -cpArbiterUpdate(cpArbiter *arb, struct cpCollisionInfo *info, cpSpace *space) -{ - const cpShape *a = info->a, *b = info->b; - - // For collisions between two similar primitive types, the order could have been swapped since the last frame. - arb->a = a; arb->body_a = a->body; - arb->b = b; arb->body_b = b->body; - - // Iterate over the possible pairs to look for hash value matches. - for(int i=0; icount; i++){ - struct cpContact *con = &info->arr[i]; - - // r1 and r2 store absolute offsets at init time. - // Need to convert them to relative offsets. - con->r1 = cpvsub(con->r1, a->body->p); - con->r2 = cpvsub(con->r2, b->body->p); - - // Cached impulses are not zeroed at init time. - con->jnAcc = con->jtAcc = 0.0f; - - for(int j=0; jcount; j++){ - struct cpContact *old = &arb->contacts[j]; - - // This could trigger false positives, but is fairly unlikely nor serious if it does. - if(con->hash == old->hash){ - // Copy the persistant contact information. - con->jnAcc = old->jnAcc; - con->jtAcc = old->jtAcc; - } - } - } - - arb->contacts = info->arr; - arb->count = info->count; - arb->n = info->n; - - arb->e = a->e * b->e; - arb->u = a->u * b->u; - - cpVect surface_vr = cpvsub(b->surfaceV, a->surfaceV); - arb->surface_vr = cpvsub(surface_vr, cpvmult(info->n, cpvdot(surface_vr, info->n))); - - cpCollisionType typeA = info->a->type, typeB = info->b->type; - cpCollisionHandler *defaultHandler = &space->defaultHandler; - cpCollisionHandler *handler = arb->handler = cpSpaceLookupHandler(space, typeA, typeB, defaultHandler); - - // Check if the types match, but don't swap for a default handler which use the wildcard for type A. - cpBool swapped = arb->swapped = (typeA != handler->typeA && handler->typeA != CP_WILDCARD_COLLISION_TYPE); - - if(handler != defaultHandler || space->usesWildcards){ - // The order of the main handler swaps the wildcard handlers too. Uffda. - arb->handlerA = cpSpaceLookupHandler(space, (swapped ? typeB : typeA), CP_WILDCARD_COLLISION_TYPE, &cpCollisionHandlerDoNothing); - arb->handlerB = cpSpaceLookupHandler(space, (swapped ? typeA : typeB), CP_WILDCARD_COLLISION_TYPE, &cpCollisionHandlerDoNothing); - } - - // mark it as new if it's been cached - if(arb->state == CP_ARBITER_STATE_CACHED) arb->state = CP_ARBITER_STATE_FIRST_COLLISION; -} - -void -cpArbiterPreStep(cpArbiter *arb, cpFloat dt, cpFloat slop, cpFloat bias) -{ - cpBody *a = arb->body_a; - cpBody *b = arb->body_b; - cpVect n = arb->n; - cpVect body_delta = cpvsub(b->p, a->p); - - for(int i=0; icount; i++){ - struct cpContact *con = &arb->contacts[i]; - - // Calculate the mass normal and mass tangent. - con->nMass = 1.0f/k_scalar(a, b, con->r1, con->r2, n); - con->tMass = 1.0f/k_scalar(a, b, con->r1, con->r2, cpvperp(n)); - - // Calculate the target bias velocity. - cpFloat dist = cpvdot(cpvadd(cpvsub(con->r2, con->r1), body_delta), n); - con->bias = -bias*cpfmin(0.0f, dist + slop)/dt; - con->jBias = 0.0f; - - // Calculate the target bounce velocity. - con->bounce = normal_relative_velocity(a, b, con->r1, con->r2, n)*arb->e; - } -} - -void -cpArbiterApplyCachedImpulse(cpArbiter *arb, cpFloat dt_coef) -{ - if(cpArbiterIsFirstContact(arb)) return; - - cpBody *a = arb->body_a; - cpBody *b = arb->body_b; - cpVect n = arb->n; - - for(int i=0; icount; i++){ - struct cpContact *con = &arb->contacts[i]; - cpVect j = cpvrotate(n, cpv(con->jnAcc, con->jtAcc)); - apply_impulses(a, b, con->r1, con->r2, cpvmult(j, dt_coef)); - } -} - -// TODO: is it worth splitting velocity/position correction? - -void -cpArbiterApplyImpulse(cpArbiter *arb) -{ - cpBody *a = arb->body_a; - cpBody *b = arb->body_b; - cpVect n = arb->n; - cpVect surface_vr = arb->surface_vr; - cpFloat friction = arb->u; - - for(int i=0; icount; i++){ - struct cpContact *con = &arb->contacts[i]; - cpFloat nMass = con->nMass; - cpVect r1 = con->r1; - cpVect r2 = con->r2; - - cpVect vb1 = cpvadd(a->v_bias, cpvmult(cpvperp(r1), a->w_bias)); - cpVect vb2 = cpvadd(b->v_bias, cpvmult(cpvperp(r2), b->w_bias)); - cpVect vr = cpvadd(relative_velocity(a, b, r1, r2), surface_vr); - - cpFloat vbn = cpvdot(cpvsub(vb2, vb1), n); - cpFloat vrn = cpvdot(vr, n); - cpFloat vrt = cpvdot(vr, cpvperp(n)); - - cpFloat jbn = (con->bias - vbn)*nMass; - cpFloat jbnOld = con->jBias; - con->jBias = cpfmax(jbnOld + jbn, 0.0f); - - cpFloat jn = -(con->bounce + vrn)*nMass; - cpFloat jnOld = con->jnAcc; - con->jnAcc = cpfmax(jnOld + jn, 0.0f); - - cpFloat jtMax = friction*con->jnAcc; - cpFloat jt = -vrt*con->tMass; - cpFloat jtOld = con->jtAcc; - con->jtAcc = cpfclamp(jtOld + jt, -jtMax, jtMax); - - apply_bias_impulses(a, b, r1, r2, cpvmult(n, con->jBias - jbnOld)); - apply_impulses(a, b, r1, r2, cpvrotate(n, cpv(con->jnAcc - jnOld, con->jtAcc - jtOld))); - } -} diff --git a/apecs-physics/Chipmunk2D/src/cpArray.c b/apecs-physics/Chipmunk2D/src/cpArray.c deleted file mode 100644 index f035521..0000000 --- a/apecs-physics/Chipmunk2D/src/cpArray.c +++ /dev/null @@ -1,101 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include - -#include "chipmunk_private.h" - - -cpArray * -cpArrayNew(int size) -{ - cpArray *arr = (cpArray *)cpcalloc(1, sizeof(cpArray)); - - arr->num = 0; - arr->max = (size ? size : 4); - arr->arr = (void **)cpcalloc(arr->max, sizeof(void*)); - - return arr; -} - -void -cpArrayFree(cpArray *arr) -{ - if(arr){ - cpfree(arr->arr); - arr->arr = NULL; - - cpfree(arr); - } -} - -void -cpArrayPush(cpArray *arr, void *object) -{ - if(arr->num == arr->max){ - arr->max = 3*(arr->max + 1)/2; - arr->arr = (void **)cprealloc(arr->arr, arr->max*sizeof(void*)); - } - - arr->arr[arr->num] = object; - arr->num++; -} - -void * -cpArrayPop(cpArray *arr) -{ - arr->num--; - - void *value = arr->arr[arr->num]; - arr->arr[arr->num] = NULL; - - return value; -} - -void -cpArrayDeleteObj(cpArray *arr, void *obj) -{ - for(int i=0; inum; i++){ - if(arr->arr[i] == obj){ - arr->num--; - - arr->arr[i] = arr->arr[arr->num]; - arr->arr[arr->num] = NULL; - - return; - } - } -} - -void -cpArrayFreeEach(cpArray *arr, void (freeFunc)(void*)) -{ - for(int i=0; inum; i++) freeFunc(arr->arr[i]); -} - -cpBool -cpArrayContains(cpArray *arr, void *ptr) -{ - for(int i=0; inum; i++) - if(arr->arr[i] == ptr) return cpTrue; - - return cpFalse; -} diff --git a/apecs-physics/Chipmunk2D/src/cpBBTree.c b/apecs-physics/Chipmunk2D/src/cpBBTree.c deleted file mode 100644 index 934cac4..0000000 --- a/apecs-physics/Chipmunk2D/src/cpBBTree.c +++ /dev/null @@ -1,896 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "stdlib.h" -#include "stdio.h" - -#include "chipmunk_private.h" - -static inline cpSpatialIndexClass *Klass(); - -typedef struct Node Node; -typedef struct Pair Pair; - -struct cpBBTree { - cpSpatialIndex spatialIndex; - cpBBTreeVelocityFunc velocityFunc; - - cpHashSet *leaves; - Node *root; - - Node *pooledNodes; - Pair *pooledPairs; - cpArray *allocatedBuffers; - - cpTimestamp stamp; -}; - -struct Node { - void *obj; - cpBB bb; - Node *parent; - - union { - // Internal nodes - struct { Node *a, *b; } children; - - // Leaves - struct { - cpTimestamp stamp; - Pair *pairs; - } leaf; - } node; -}; - -// Can't use anonymous unions and still get good x-compiler compatability -#define A node.children.a -#define B node.children.b -#define STAMP node.leaf.stamp -#define PAIRS node.leaf.pairs - -typedef struct Thread { - Pair *prev; - Node *leaf; - Pair *next; -} Thread; - -struct Pair { - Thread a, b; - cpCollisionID id; -}; - -//MARK: Misc Functions - -static inline cpBB -GetBB(cpBBTree *tree, void *obj) -{ - cpBB bb = tree->spatialIndex.bbfunc(obj); - - cpBBTreeVelocityFunc velocityFunc = tree->velocityFunc; - if(velocityFunc){ - cpFloat coef = 0.1f; - cpFloat x = (bb.r - bb.l)*coef; - cpFloat y = (bb.t - bb.b)*coef; - - cpVect v = cpvmult(velocityFunc(obj), 0.1f); - return cpBBNew(bb.l + cpfmin(-x, v.x), bb.b + cpfmin(-y, v.y), bb.r + cpfmax(x, v.x), bb.t + cpfmax(y, v.y)); - } else { - return bb; - } -} - -static inline cpBBTree * -GetTree(cpSpatialIndex *index) -{ - return (index && index->klass == Klass() ? (cpBBTree *)index : NULL); -} - -static inline Node * -GetRootIfTree(cpSpatialIndex *index){ - return (index && index->klass == Klass() ? ((cpBBTree *)index)->root : NULL); -} - -static inline cpBBTree * -GetMasterTree(cpBBTree *tree) -{ - cpBBTree *dynamicTree = GetTree(tree->spatialIndex.dynamicIndex); - return (dynamicTree ? dynamicTree : tree); -} - -static inline void -IncrementStamp(cpBBTree *tree) -{ - cpBBTree *dynamicTree = GetTree(tree->spatialIndex.dynamicIndex); - if(dynamicTree){ - dynamicTree->stamp++; - } else { - tree->stamp++; - } -} - -//MARK: Pair/Thread Functions - -static void -PairRecycle(cpBBTree *tree, Pair *pair) -{ - // Share the pool of the master tree. - // TODO: would be lovely to move the pairs stuff into an external data structure. - tree = GetMasterTree(tree); - - pair->a.next = tree->pooledPairs; - tree->pooledPairs = pair; -} - -static Pair * -PairFromPool(cpBBTree *tree) -{ - // Share the pool of the master tree. - // TODO: would be lovely to move the pairs stuff into an external data structure. - tree = GetMasterTree(tree); - - Pair *pair = tree->pooledPairs; - - if(pair){ - tree->pooledPairs = pair->a.next; - return pair; - } else { - // Pool is exhausted, make more - int count = CP_BUFFER_BYTES/sizeof(Pair); - cpAssertHard(count, "Internal Error: Buffer size is too small."); - - Pair *buffer = (Pair *)cpcalloc(1, CP_BUFFER_BYTES); - cpArrayPush(tree->allocatedBuffers, buffer); - - // push all but the first one, return the first instead - for(int i=1; ia.leaf == thread.leaf) next->a.prev = prev; else next->b.prev = prev; - } - - if(prev){ - if(prev->a.leaf == thread.leaf) prev->a.next = next; else prev->b.next = next; - } else { - thread.leaf->PAIRS = next; - } -} - -static void -PairsClear(Node *leaf, cpBBTree *tree) -{ - Pair *pair = leaf->PAIRS; - leaf->PAIRS = NULL; - - while(pair){ - if(pair->a.leaf == leaf){ - Pair *next = pair->a.next; - ThreadUnlink(pair->b); - PairRecycle(tree, pair); - pair = next; - } else { - Pair *next = pair->b.next; - ThreadUnlink(pair->a); - PairRecycle(tree, pair); - pair = next; - } - } -} - -static void -PairInsert(Node *a, Node *b, cpBBTree *tree) -{ - Pair *nextA = a->PAIRS, *nextB = b->PAIRS; - Pair *pair = PairFromPool(tree); - Pair temp = {{NULL, a, nextA},{NULL, b, nextB}, 0}; - - a->PAIRS = b->PAIRS = pair; - *pair = temp; - - if(nextA){ - if(nextA->a.leaf == a) nextA->a.prev = pair; else nextA->b.prev = pair; - } - - if(nextB){ - if(nextB->a.leaf == b) nextB->a.prev = pair; else nextB->b.prev = pair; - } -} - - -//MARK: Node Functions - -static void -NodeRecycle(cpBBTree *tree, Node *node) -{ - node->parent = tree->pooledNodes; - tree->pooledNodes = node; -} - -static Node * -NodeFromPool(cpBBTree *tree) -{ - Node *node = tree->pooledNodes; - - if(node){ - tree->pooledNodes = node->parent; - return node; - } else { - // Pool is exhausted, make more - int count = CP_BUFFER_BYTES/sizeof(Node); - cpAssertHard(count, "Internal Error: Buffer size is too small."); - - Node *buffer = (Node *)cpcalloc(1, CP_BUFFER_BYTES); - cpArrayPush(tree->allocatedBuffers, buffer); - - // push all but the first one, return the first instead - for(int i=1; iA = value; - value->parent = node; -} - -static inline void -NodeSetB(Node *node, Node *value) -{ - node->B = value; - value->parent = node; -} - -static Node * -NodeNew(cpBBTree *tree, Node *a, Node *b) -{ - Node *node = NodeFromPool(tree); - - node->obj = NULL; - node->bb = cpBBMerge(a->bb, b->bb); - node->parent = NULL; - - NodeSetA(node, a); - NodeSetB(node, b); - - return node; -} - -static inline cpBool -NodeIsLeaf(Node *node) -{ - return (node->obj != NULL); -} - -static inline Node * -NodeOther(Node *node, Node *child) -{ - return (node->A == child ? node->B : node->A); -} - -static inline void -NodeReplaceChild(Node *parent, Node *child, Node *value, cpBBTree *tree) -{ - cpAssertSoft(!NodeIsLeaf(parent), "Internal Error: Cannot replace child of a leaf."); - cpAssertSoft(child == parent->A || child == parent->B, "Internal Error: Node is not a child of parent."); - - if(parent->A == child){ - NodeRecycle(tree, parent->A); - NodeSetA(parent, value); - } else { - NodeRecycle(tree, parent->B); - NodeSetB(parent, value); - } - - for(Node *node=parent; node; node = node->parent){ - node->bb = cpBBMerge(node->A->bb, node->B->bb); - } -} - -//MARK: Subtree Functions - -static inline cpFloat -cpBBProximity(cpBB a, cpBB b) -{ - return cpfabs(a.l + a.r - b.l - b.r) + cpfabs(a.b + a.t - b.b - b.t); -} - -static Node * -SubtreeInsert(Node *subtree, Node *leaf, cpBBTree *tree) -{ - if(subtree == NULL){ - return leaf; - } else if(NodeIsLeaf(subtree)){ - return NodeNew(tree, leaf, subtree); - } else { - cpFloat cost_a = cpBBArea(subtree->B->bb) + cpBBMergedArea(subtree->A->bb, leaf->bb); - cpFloat cost_b = cpBBArea(subtree->A->bb) + cpBBMergedArea(subtree->B->bb, leaf->bb); - - if(cost_a == cost_b){ - cost_a = cpBBProximity(subtree->A->bb, leaf->bb); - cost_b = cpBBProximity(subtree->B->bb, leaf->bb); - } - - if(cost_b < cost_a){ - NodeSetB(subtree, SubtreeInsert(subtree->B, leaf, tree)); - } else { - NodeSetA(subtree, SubtreeInsert(subtree->A, leaf, tree)); - } - - subtree->bb = cpBBMerge(subtree->bb, leaf->bb); - return subtree; - } -} - -static void -SubtreeQuery(Node *subtree, void *obj, cpBB bb, cpSpatialIndexQueryFunc func, void *data) -{ - if(cpBBIntersects(subtree->bb, bb)){ - if(NodeIsLeaf(subtree)){ - func(obj, subtree->obj, 0, data); - } else { - SubtreeQuery(subtree->A, obj, bb, func, data); - SubtreeQuery(subtree->B, obj, bb, func, data); - } - } -} - - -static cpFloat -SubtreeSegmentQuery(Node *subtree, void *obj, cpVect a, cpVect b, cpFloat t_exit, cpSpatialIndexSegmentQueryFunc func, void *data) -{ - if(NodeIsLeaf(subtree)){ - return func(obj, subtree->obj, data); - } else { - cpFloat t_a = cpBBSegmentQuery(subtree->A->bb, a, b); - cpFloat t_b = cpBBSegmentQuery(subtree->B->bb, a, b); - - if(t_a < t_b){ - if(t_a < t_exit) t_exit = cpfmin(t_exit, SubtreeSegmentQuery(subtree->A, obj, a, b, t_exit, func, data)); - if(t_b < t_exit) t_exit = cpfmin(t_exit, SubtreeSegmentQuery(subtree->B, obj, a, b, t_exit, func, data)); - } else { - if(t_b < t_exit) t_exit = cpfmin(t_exit, SubtreeSegmentQuery(subtree->B, obj, a, b, t_exit, func, data)); - if(t_a < t_exit) t_exit = cpfmin(t_exit, SubtreeSegmentQuery(subtree->A, obj, a, b, t_exit, func, data)); - } - - return t_exit; - } -} - -static void -SubtreeRecycle(cpBBTree *tree, Node *node) -{ - if(!NodeIsLeaf(node)){ - SubtreeRecycle(tree, node->A); - SubtreeRecycle(tree, node->B); - NodeRecycle(tree, node); - } -} - -static inline Node * -SubtreeRemove(Node *subtree, Node *leaf, cpBBTree *tree) -{ - if(leaf == subtree){ - return NULL; - } else { - Node *parent = leaf->parent; - if(parent == subtree){ - Node *other = NodeOther(subtree, leaf); - other->parent = subtree->parent; - NodeRecycle(tree, subtree); - return other; - } else { - NodeReplaceChild(parent->parent, parent, NodeOther(parent, leaf), tree); - return subtree; - } - } -} - -//MARK: Marking Functions - -typedef struct MarkContext { - cpBBTree *tree; - Node *staticRoot; - cpSpatialIndexQueryFunc func; - void *data; -} MarkContext; - -static void -MarkLeafQuery(Node *subtree, Node *leaf, cpBool left, MarkContext *context) -{ - if(cpBBIntersects(leaf->bb, subtree->bb)){ - if(NodeIsLeaf(subtree)){ - if(left){ - PairInsert(leaf, subtree, context->tree); - } else { - if(subtree->STAMP < leaf->STAMP) PairInsert(subtree, leaf, context->tree); - context->func(leaf->obj, subtree->obj, 0, context->data); - } - } else { - MarkLeafQuery(subtree->A, leaf, left, context); - MarkLeafQuery(subtree->B, leaf, left, context); - } - } -} - -static void -MarkLeaf(Node *leaf, MarkContext *context) -{ - cpBBTree *tree = context->tree; - if(leaf->STAMP == GetMasterTree(tree)->stamp){ - Node *staticRoot = context->staticRoot; - if(staticRoot) MarkLeafQuery(staticRoot, leaf, cpFalse, context); - - for(Node *node = leaf; node->parent; node = node->parent){ - if(node == node->parent->A){ - MarkLeafQuery(node->parent->B, leaf, cpTrue, context); - } else { - MarkLeafQuery(node->parent->A, leaf, cpFalse, context); - } - } - } else { - Pair *pair = leaf->PAIRS; - while(pair){ - if(leaf == pair->b.leaf){ - pair->id = context->func(pair->a.leaf->obj, leaf->obj, pair->id, context->data); - pair = pair->b.next; - } else { - pair = pair->a.next; - } - } - } -} - -static void -MarkSubtree(Node *subtree, MarkContext *context) -{ - if(NodeIsLeaf(subtree)){ - MarkLeaf(subtree, context); - } else { - MarkSubtree(subtree->A, context); - MarkSubtree(subtree->B, context); // TODO: Force TCO here? - } -} - -//MARK: Leaf Functions - -static Node * -LeafNew(cpBBTree *tree, void *obj, cpBB bb) -{ - Node *node = NodeFromPool(tree); - node->obj = obj; - node->bb = GetBB(tree, obj); - - node->parent = NULL; - node->STAMP = 0; - node->PAIRS = NULL; - - return node; -} - -static cpBool -LeafUpdate(Node *leaf, cpBBTree *tree) -{ - Node *root = tree->root; - cpBB bb = tree->spatialIndex.bbfunc(leaf->obj); - - if(!cpBBContainsBB(leaf->bb, bb)){ - leaf->bb = GetBB(tree, leaf->obj); - - root = SubtreeRemove(root, leaf, tree); - tree->root = SubtreeInsert(root, leaf, tree); - - PairsClear(leaf, tree); - leaf->STAMP = GetMasterTree(tree)->stamp; - - return cpTrue; - } else { - return cpFalse; - } -} - -static cpCollisionID VoidQueryFunc(void *obj1, void *obj2, cpCollisionID id, void *data){return id;} - -static void -LeafAddPairs(Node *leaf, cpBBTree *tree) -{ - cpSpatialIndex *dynamicIndex = tree->spatialIndex.dynamicIndex; - if(dynamicIndex){ - Node *dynamicRoot = GetRootIfTree(dynamicIndex); - if(dynamicRoot){ - cpBBTree *dynamicTree = GetTree(dynamicIndex); - MarkContext context = {dynamicTree, NULL, NULL, NULL}; - MarkLeafQuery(dynamicRoot, leaf, cpTrue, &context); - } - } else { - Node *staticRoot = GetRootIfTree(tree->spatialIndex.staticIndex); - MarkContext context = {tree, staticRoot, VoidQueryFunc, NULL}; - MarkLeaf(leaf, &context); - } -} - -//MARK: Memory Management Functions - -cpBBTree * -cpBBTreeAlloc(void) -{ - return (cpBBTree *)cpcalloc(1, sizeof(cpBBTree)); -} - -static int -leafSetEql(void *obj, Node *node) -{ - return (obj == node->obj); -} - -static void * -leafSetTrans(void *obj, cpBBTree *tree) -{ - return LeafNew(tree, obj, tree->spatialIndex.bbfunc(obj)); -} - -cpSpatialIndex * -cpBBTreeInit(cpBBTree *tree, cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex) -{ - cpSpatialIndexInit((cpSpatialIndex *)tree, Klass(), bbfunc, staticIndex); - - tree->velocityFunc = NULL; - - tree->leaves = cpHashSetNew(0, (cpHashSetEqlFunc)leafSetEql); - tree->root = NULL; - - tree->pooledNodes = NULL; - tree->allocatedBuffers = cpArrayNew(0); - - tree->stamp = 0; - - return (cpSpatialIndex *)tree; -} - -void -cpBBTreeSetVelocityFunc(cpSpatialIndex *index, cpBBTreeVelocityFunc func) -{ - if(index->klass != Klass()){ - cpAssertWarn(cpFalse, "Ignoring cpBBTreeSetVelocityFunc() call to non-tree spatial index."); - return; - } - - ((cpBBTree *)index)->velocityFunc = func; -} - -cpSpatialIndex * -cpBBTreeNew(cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex) -{ - return cpBBTreeInit(cpBBTreeAlloc(), bbfunc, staticIndex); -} - -static void -cpBBTreeDestroy(cpBBTree *tree) -{ - cpHashSetFree(tree->leaves); - - if(tree->allocatedBuffers) cpArrayFreeEach(tree->allocatedBuffers, cpfree); - cpArrayFree(tree->allocatedBuffers); -} - -//MARK: Insert/Remove - -static void -cpBBTreeInsert(cpBBTree *tree, void *obj, cpHashValue hashid) -{ - Node *leaf = (Node *)cpHashSetInsert(tree->leaves, hashid, obj, (cpHashSetTransFunc)leafSetTrans, tree); - - Node *root = tree->root; - tree->root = SubtreeInsert(root, leaf, tree); - - leaf->STAMP = GetMasterTree(tree)->stamp; - LeafAddPairs(leaf, tree); - IncrementStamp(tree); -} - -static void -cpBBTreeRemove(cpBBTree *tree, void *obj, cpHashValue hashid) -{ - Node *leaf = (Node *)cpHashSetRemove(tree->leaves, hashid, obj); - - tree->root = SubtreeRemove(tree->root, leaf, tree); - PairsClear(leaf, tree); - NodeRecycle(tree, leaf); -} - -static cpBool -cpBBTreeContains(cpBBTree *tree, void *obj, cpHashValue hashid) -{ - return (cpHashSetFind(tree->leaves, hashid, obj) != NULL); -} - -//MARK: Reindex - -static void LeafUpdateWrap(Node *leaf, cpBBTree *tree) {LeafUpdate(leaf, tree);} - -static void -cpBBTreeReindexQuery(cpBBTree *tree, cpSpatialIndexQueryFunc func, void *data) -{ - if(!tree->root) return; - - // LeafUpdate() may modify tree->root. Don't cache it. - cpHashSetEach(tree->leaves, (cpHashSetIteratorFunc)LeafUpdateWrap, tree); - - cpSpatialIndex *staticIndex = tree->spatialIndex.staticIndex; - Node *staticRoot = (staticIndex && staticIndex->klass == Klass() ? ((cpBBTree *)staticIndex)->root : NULL); - - MarkContext context = {tree, staticRoot, func, data}; - MarkSubtree(tree->root, &context); - if(staticIndex && !staticRoot) cpSpatialIndexCollideStatic((cpSpatialIndex *)tree, staticIndex, func, data); - - IncrementStamp(tree); -} - -static void -cpBBTreeReindex(cpBBTree *tree) -{ - cpBBTreeReindexQuery(tree, VoidQueryFunc, NULL); -} - -static void -cpBBTreeReindexObject(cpBBTree *tree, void *obj, cpHashValue hashid) -{ - Node *leaf = (Node *)cpHashSetFind(tree->leaves, hashid, obj); - if(leaf){ - if(LeafUpdate(leaf, tree)) LeafAddPairs(leaf, tree); - IncrementStamp(tree); - } -} - -//MARK: Query - -static void -cpBBTreeSegmentQuery(cpBBTree *tree, void *obj, cpVect a, cpVect b, cpFloat t_exit, cpSpatialIndexSegmentQueryFunc func, void *data) -{ - Node *root = tree->root; - if(root) SubtreeSegmentQuery(root, obj, a, b, t_exit, func, data); -} - -static void -cpBBTreeQuery(cpBBTree *tree, void *obj, cpBB bb, cpSpatialIndexQueryFunc func, void *data) -{ - if(tree->root) SubtreeQuery(tree->root, obj, bb, func, data); -} - -//MARK: Misc - -static int -cpBBTreeCount(cpBBTree *tree) -{ - return cpHashSetCount(tree->leaves); -} - -typedef struct eachContext { - cpSpatialIndexIteratorFunc func; - void *data; -} eachContext; - -static void each_helper(Node *node, eachContext *context){context->func(node->obj, context->data);} - -static void -cpBBTreeEach(cpBBTree *tree, cpSpatialIndexIteratorFunc func, void *data) -{ - eachContext context = {func, data}; - cpHashSetEach(tree->leaves, (cpHashSetIteratorFunc)each_helper, &context); -} - -static cpSpatialIndexClass klass = { - (cpSpatialIndexDestroyImpl)cpBBTreeDestroy, - - (cpSpatialIndexCountImpl)cpBBTreeCount, - (cpSpatialIndexEachImpl)cpBBTreeEach, - - (cpSpatialIndexContainsImpl)cpBBTreeContains, - (cpSpatialIndexInsertImpl)cpBBTreeInsert, - (cpSpatialIndexRemoveImpl)cpBBTreeRemove, - - (cpSpatialIndexReindexImpl)cpBBTreeReindex, - (cpSpatialIndexReindexObjectImpl)cpBBTreeReindexObject, - (cpSpatialIndexReindexQueryImpl)cpBBTreeReindexQuery, - - (cpSpatialIndexQueryImpl)cpBBTreeQuery, - (cpSpatialIndexSegmentQueryImpl)cpBBTreeSegmentQuery, -}; - -static inline cpSpatialIndexClass *Klass(){return &klass;} - - -//MARK: Tree Optimization - -static int -cpfcompare(const cpFloat *a, const cpFloat *b){ - return (*a < *b ? -1 : (*b < *a ? 1 : 0)); -} - -static void -fillNodeArray(Node *node, Node ***cursor){ - (**cursor) = node; - (*cursor)++; -} - -static Node * -partitionNodes(cpBBTree *tree, Node **nodes, int count) -{ - if(count == 1){ - return nodes[0]; - } else if(count == 2) { - return NodeNew(tree, nodes[0], nodes[1]); - } - - // Find the AABB for these nodes - cpBB bb = nodes[0]->bb; - for(int i=1; ibb); - - // Split it on it's longest axis - cpBool splitWidth = (bb.r - bb.l > bb.t - bb.b); - - // Sort the bounds and use the median as the splitting point - cpFloat *bounds = (cpFloat *)cpcalloc(count*2, sizeof(cpFloat)); - if(splitWidth){ - for(int i=0; ibb.l; - bounds[2*i + 1] = nodes[i]->bb.r; - } - } else { - for(int i=0; ibb.b; - bounds[2*i + 1] = nodes[i]->bb.t; - } - } - - qsort(bounds, count*2, sizeof(cpFloat), (int (*)(const void *, const void *))cpfcompare); - cpFloat split = (bounds[count - 1] + bounds[count])*0.5f; // use the medain as the split - cpfree(bounds); - - // Generate the child BBs - cpBB a = bb, b = bb; - if(splitWidth) a.r = b.l = split; else a.t = b.b = split; - - // Partition the nodes - int right = count; - for(int left=0; left < right;){ - Node *node = nodes[left]; - if(cpBBMergedArea(node->bb, b) < cpBBMergedArea(node->bb, a)){ -// if(cpBBProximity(node->bb, b) < cpBBProximity(node->bb, a)){ - right--; - nodes[left] = nodes[right]; - nodes[right] = node; - } else { - left++; - } - } - - if(right == count){ - Node *node = NULL; - for(int i=0; iroot; -// Node *node = root; -// int bit = 0; -// unsigned int path = tree->opath; -// -// while(!NodeIsLeaf(node)){ -// node = (path&(1<a : node->b); -// bit = (bit + 1)&(sizeof(unsigned int)*8 - 1); -// } -// -// root = subtreeRemove(root, node, tree); -// tree->root = subtreeInsert(root, node, tree); -// } -//} - -void -cpBBTreeOptimize(cpSpatialIndex *index) -{ - if(index->klass != &klass){ - cpAssertWarn(cpFalse, "Ignoring cpBBTreeOptimize() call to non-tree spatial index."); - return; - } - - cpBBTree *tree = (cpBBTree *)index; - Node *root = tree->root; - if(!root) return; - - int count = cpBBTreeCount(tree); - Node **nodes = (Node **)cpcalloc(count, sizeof(Node *)); - Node **cursor = nodes; - - cpHashSetEach(tree->leaves, (cpHashSetIteratorFunc)fillNodeArray, &cursor); - - SubtreeRecycle(tree, root); - tree->root = partitionNodes(tree, nodes, count); - cpfree(nodes); -} - -//MARK: Debug Draw - -//#define CP_BBTREE_DEBUG_DRAW -#ifdef CP_BBTREE_DEBUG_DRAW -#include "OpenGL/gl.h" -#include "OpenGL/glu.h" -#include - -static void -NodeRender(Node *node, int depth) -{ - if(!NodeIsLeaf(node) && depth <= 10){ - NodeRender(node->a, depth + 1); - NodeRender(node->b, depth + 1); - } - - cpBB bb = node->bb; - -// GLfloat v = depth/2.0f; -// glColor3f(1.0f - v, v, 0.0f); - glLineWidth(cpfmax(5.0f - depth, 1.0f)); - glBegin(GL_LINES); { - glVertex2f(bb.l, bb.b); - glVertex2f(bb.l, bb.t); - - glVertex2f(bb.l, bb.t); - glVertex2f(bb.r, bb.t); - - glVertex2f(bb.r, bb.t); - glVertex2f(bb.r, bb.b); - - glVertex2f(bb.r, bb.b); - glVertex2f(bb.l, bb.b); - }; glEnd(); -} - -void -cpBBTreeRenderDebug(cpSpatialIndex *index){ - if(index->klass != &klass){ - cpAssertWarn(cpFalse, "Ignoring cpBBTreeRenderDebug() call to non-tree spatial index."); - return; - } - - cpBBTree *tree = (cpBBTree *)index; - if(tree->root) NodeRender(tree->root, 0); -} -#endif diff --git a/apecs-physics/Chipmunk2D/src/cpBody.c b/apecs-physics/Chipmunk2D/src/cpBody.c deleted file mode 100644 index 8663e18..0000000 --- a/apecs-physics/Chipmunk2D/src/cpBody.c +++ /dev/null @@ -1,626 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include - -#include "chipmunk_private.h" - -cpBody* -cpBodyAlloc(void) -{ - return (cpBody *)cpcalloc(1, sizeof(cpBody)); -} - -cpBody * -cpBodyInit(cpBody *body, cpFloat mass, cpFloat moment) -{ - body->space = NULL; - body->shapeList = NULL; - body->arbiterList = NULL; - body->constraintList = NULL; - - body->velocity_func = cpBodyUpdateVelocity; - body->position_func = cpBodyUpdatePosition; - - body->sleeping.root = NULL; - body->sleeping.next = NULL; - body->sleeping.idleTime = 0.0f; - - body->p = cpvzero; - body->v = cpvzero; - body->f = cpvzero; - - body->w = 0.0f; - body->t = 0.0f; - - body->v_bias = cpvzero; - body->w_bias = 0.0f; - - body->userData = NULL; - - // Setters must be called after full initialization so the sanity checks don't assert on garbage data. - cpBodySetMass(body, mass); - cpBodySetMoment(body, moment); - cpBodySetAngle(body, 0.0f); - - return body; -} - -cpBody* -cpBodyNew(cpFloat mass, cpFloat moment) -{ - return cpBodyInit(cpBodyAlloc(), mass, moment); -} - -cpBody* -cpBodyNewKinematic() -{ - cpBody *body = cpBodyNew(0.0f, 0.0f); - cpBodySetType(body, CP_BODY_TYPE_KINEMATIC); - - return body; -} - -cpBody* -cpBodyNewStatic() -{ - cpBody *body = cpBodyNew(0.0f, 0.0f); - cpBodySetType(body, CP_BODY_TYPE_STATIC); - - return body; -} - -void cpBodyDestroy(cpBody *body){} - -void -cpBodyFree(cpBody *body) -{ - if(body){ - cpBodyDestroy(body); - cpfree(body); - } -} - -#ifdef NDEBUG - #define cpAssertSaneBody(body) -#else - static void cpv_assert_nan(cpVect v, char *message){cpAssertHard(v.x == v.x && v.y == v.y, message);} - static void cpv_assert_infinite(cpVect v, char *message){cpAssertHard(cpfabs(v.x) != INFINITY && cpfabs(v.y) != INFINITY, message);} - static void cpv_assert_sane(cpVect v, char *message){cpv_assert_nan(v, message); cpv_assert_infinite(v, message);} - - static void - cpBodySanityCheck(const cpBody *body) - { - cpAssertHard(body->m == body->m && body->m_inv == body->m_inv, "Body's mass is NaN."); - cpAssertHard(body->i == body->i && body->i_inv == body->i_inv, "Body's moment is NaN."); - cpAssertHard(body->m >= 0.0f, "Body's mass is negative."); - cpAssertHard(body->i >= 0.0f, "Body's moment is negative."); - - cpv_assert_sane(body->p, "Body's position is invalid."); - cpv_assert_sane(body->v, "Body's velocity is invalid."); - cpv_assert_sane(body->f, "Body's force is invalid."); - - cpAssertHard(body->a == body->a && cpfabs(body->a) != INFINITY, "Body's angle is invalid."); - cpAssertHard(body->w == body->w && cpfabs(body->w) != INFINITY, "Body's angular velocity is invalid."); - cpAssertHard(body->t == body->t && cpfabs(body->t) != INFINITY, "Body's torque is invalid."); - } - - #define cpAssertSaneBody(body) cpBodySanityCheck(body) -#endif - -cpBool -cpBodyIsSleeping(const cpBody *body) -{ - return (body->sleeping.root != ((cpBody*)0)); -} - -cpBodyType -cpBodyGetType(cpBody *body) -{ - if(body->sleeping.idleTime == INFINITY){ - return CP_BODY_TYPE_STATIC; - } else if(body->m == INFINITY){ - return CP_BODY_TYPE_KINEMATIC; - } else { - return CP_BODY_TYPE_DYNAMIC; - } -} - -void -cpBodySetType(cpBody *body, cpBodyType type) -{ - cpBodyType oldType = cpBodyGetType(body); - if(oldType == type) return; - - // Static bodies have their idle timers set to infinity. - // Non-static bodies should have their idle timer reset. - body->sleeping.idleTime = (type == CP_BODY_TYPE_STATIC ? INFINITY : 0.0f); - - if(type == CP_BODY_TYPE_DYNAMIC){ - body->m = body->i = 0.0f; - body->m_inv = body->i_inv = INFINITY; - - cpBodyAccumulateMassFromShapes(body); - } else { - body->m = body->i = INFINITY; - body->m_inv = body->i_inv = 0.0f; - - body->v = cpvzero; - body->w = 0.0f; - } - - // If the body is added to a space already, we'll need to update some space data structures. - cpSpace *space = cpBodyGetSpace(body); - if(space != NULL){ - cpAssertSpaceUnlocked(space); - - if(oldType == CP_BODY_TYPE_STATIC){ - // TODO This is probably not necessary -// cpBodyActivateStatic(body, NULL); - } else { - cpBodyActivate(body); - } - - // Move the bodies to the correct array. - cpArray *fromArray = cpSpaceArrayForBodyType(space, oldType); - cpArray *toArray = cpSpaceArrayForBodyType(space, type); - if(fromArray != toArray){ - cpArrayDeleteObj(fromArray, body); - cpArrayPush(toArray, body); - } - - // Move the body's shapes to the correct spatial index. - cpSpatialIndex *fromIndex = (oldType == CP_BODY_TYPE_STATIC ? space->staticShapes : space->dynamicShapes); - cpSpatialIndex *toIndex = (type == CP_BODY_TYPE_STATIC ? space->staticShapes : space->dynamicShapes); - if(fromIndex != toIndex){ - CP_BODY_FOREACH_SHAPE(body, shape){ - cpSpatialIndexRemove(fromIndex, shape, shape->hashid); - cpSpatialIndexInsert(toIndex, shape, shape->hashid); - } - } - } -} - - - -// Should *only* be called when shapes with mass info are modified, added or removed. -void -cpBodyAccumulateMassFromShapes(cpBody *body) -{ - if(body == NULL || cpBodyGetType(body) != CP_BODY_TYPE_DYNAMIC) return; - - // Reset the body's mass data. - body->m = body->i = 0.0f; - body->cog = cpvzero; - - // Cache the position to realign it at the end. - cpVect pos = cpBodyGetPosition(body); - - // Accumulate mass from shapes. - CP_BODY_FOREACH_SHAPE(body, shape){ - struct cpShapeMassInfo *info = &shape->massInfo; - cpFloat m = info->m; - - if(m > 0.0f){ - cpFloat msum = body->m + m; - - body->i += m*info->i + cpvdistsq(body->cog, info->cog)*(m*body->m)/msum; - body->cog = cpvlerp(body->cog, info->cog, m/msum); - body->m = msum; - } - } - - // Recalculate the inverses. - body->m_inv = 1.0f/body->m; - body->i_inv = 1.0f/body->i; - - // Realign the body since the CoG has probably moved. - cpBodySetPosition(body, pos); - cpAssertSaneBody(body); -} - -cpSpace * -cpBodyGetSpace(const cpBody *body) -{ - return body->space; -} - -cpFloat -cpBodyGetMass(const cpBody *body) -{ - return body->m; -} - -void -cpBodySetMass(cpBody *body, cpFloat mass) -{ - cpAssertHard(cpBodyGetType(body) == CP_BODY_TYPE_DYNAMIC, "You cannot set the mass of kinematic or static bodies."); - cpAssertHard(0.0f <= mass && mass < INFINITY, "Mass must be positive and finite."); - - cpBodyActivate(body); - body->m = mass; - body->m_inv = 1.0f/mass; - cpAssertSaneBody(body); -} - -cpFloat -cpBodyGetMoment(const cpBody *body) -{ - return body->i; -} - -void -cpBodySetMoment(cpBody *body, cpFloat moment) -{ - cpAssertHard(moment >= 0.0f, "Moment of Inertia must be positive."); - - cpBodyActivate(body); - body->i = moment; - body->i_inv = 1.0f/moment; - cpAssertSaneBody(body); -} - -cpVect -cpBodyGetRotation(const cpBody *body) -{ - return cpv(body->transform.a, body->transform.b); -} - -void -cpBodyAddShape(cpBody *body, cpShape *shape) -{ - cpShape *next = body->shapeList; - if(next) next->prev = shape; - - shape->next = next; - body->shapeList = shape; - - if(shape->massInfo.m > 0.0f){ - cpBodyAccumulateMassFromShapes(body); - } -} - -void -cpBodyRemoveShape(cpBody *body, cpShape *shape) -{ - cpShape *prev = shape->prev; - cpShape *next = shape->next; - - if(prev){ - prev->next = next; - } else { - body->shapeList = next; - } - - if(next){ - next->prev = prev; - } - - shape->prev = NULL; - shape->next = NULL; - - if(cpBodyGetType(body) == CP_BODY_TYPE_DYNAMIC && shape->massInfo.m > 0.0f){ - cpBodyAccumulateMassFromShapes(body); - } -} - -static cpConstraint * -filterConstraints(cpConstraint *node, cpBody *body, cpConstraint *filter) -{ - if(node == filter){ - return cpConstraintNext(node, body); - } else if(node->a == body){ - node->next_a = filterConstraints(node->next_a, body, filter); - } else { - node->next_b = filterConstraints(node->next_b, body, filter); - } - - return node; -} - -void -cpBodyRemoveConstraint(cpBody *body, cpConstraint *constraint) -{ - body->constraintList = filterConstraints(body->constraintList, body, constraint); -} - -// 'p' is the position of the CoG -static void -SetTransform(cpBody *body, cpVect p, cpFloat a) -{ - cpVect rot = cpvforangle(a); - cpVect c = body->cog; - - body->transform = cpTransformNewTranspose( - rot.x, -rot.y, p.x - (c.x*rot.x - c.y*rot.y), - rot.y, rot.x, p.y - (c.x*rot.y + c.y*rot.x) - ); -} - -static inline cpFloat -SetAngle(cpBody *body, cpFloat a) -{ - body->a = a; - cpAssertSaneBody(body); - - return a; -} - -cpVect -cpBodyGetPosition(const cpBody *body) -{ - return cpTransformPoint(body->transform, cpvzero); -} - -void -cpBodySetPosition(cpBody *body, cpVect position) -{ - cpBodyActivate(body); - cpVect p = body->p = cpvadd(cpTransformVect(body->transform, body->cog), position); - cpAssertSaneBody(body); - - SetTransform(body, p, body->a); -} - -cpVect -cpBodyGetCenterOfGravity(const cpBody *body) -{ - return body->cog; -} - -void -cpBodySetCenterOfGravity(cpBody *body, cpVect cog) -{ - cpBodyActivate(body); - body->cog = cog; - cpAssertSaneBody(body); -} - -cpVect -cpBodyGetVelocity(const cpBody *body) -{ - return body->v; -} - -void -cpBodySetVelocity(cpBody *body, cpVect velocity) -{ - cpBodyActivate(body); - body->v = velocity; - cpAssertSaneBody(body); -} - -cpVect -cpBodyGetForce(const cpBody *body) -{ - return body->f; -} - -void -cpBodySetForce(cpBody *body, cpVect force) -{ - cpBodyActivate(body); - body->f = force; - cpAssertSaneBody(body); -} - -cpFloat -cpBodyGetAngle(const cpBody *body) -{ - return body->a; -} - -void -cpBodySetAngle(cpBody *body, cpFloat angle) -{ - cpBodyActivate(body); - SetAngle(body, angle); - - SetTransform(body, body->p, angle); -} - -cpFloat -cpBodyGetAngularVelocity(const cpBody *body) -{ - return body->w; -} - -void -cpBodySetAngularVelocity(cpBody *body, cpFloat angularVelocity) -{ - cpBodyActivate(body); - body->w = angularVelocity; - cpAssertSaneBody(body); -} - -cpFloat -cpBodyGetTorque(const cpBody *body) -{ - return body->t; -} - -void -cpBodySetTorque(cpBody *body, cpFloat torque) -{ - cpBodyActivate(body); - body->t = torque; - cpAssertSaneBody(body); -} - -cpDataPointer -cpBodyGetUserData(const cpBody *body) -{ - return body->userData; -} - -void -cpBodySetUserData(cpBody *body, cpDataPointer userData) -{ - body->userData = userData; -} - -void -cpBodySetVelocityUpdateFunc(cpBody *body, cpBodyVelocityFunc velocityFunc) -{ - body->velocity_func = velocityFunc; -} - -void -cpBodySetPositionUpdateFunc(cpBody *body, cpBodyPositionFunc positionFunc) -{ - body->position_func = positionFunc; -} - -void -cpBodyUpdateVelocity(cpBody *body, cpVect gravity, cpFloat damping, cpFloat dt) -{ - // Skip kinematic bodies. - if(cpBodyGetType(body) == CP_BODY_TYPE_KINEMATIC) return; - - cpAssertSoft(body->m > 0.0f && body->i > 0.0f, "Body's mass and moment must be positive to simulate. (Mass: %f Moment: %f)", body->m, body->i); - - body->v = cpvadd(cpvmult(body->v, damping), cpvmult(cpvadd(gravity, cpvmult(body->f, body->m_inv)), dt)); - body->w = body->w*damping + body->t*body->i_inv*dt; - - // Reset forces. - body->f = cpvzero; - body->t = 0.0f; - - cpAssertSaneBody(body); -} - -void -cpBodyUpdatePosition(cpBody *body, cpFloat dt) -{ - cpVect p = body->p = cpvadd(body->p, cpvmult(cpvadd(body->v, body->v_bias), dt)); - cpFloat a = SetAngle(body, body->a + (body->w + body->w_bias)*dt); - SetTransform(body, p, a); - - body->v_bias = cpvzero; - body->w_bias = 0.0f; - - cpAssertSaneBody(body); -} - -cpVect -cpBodyLocalToWorld(const cpBody *body, const cpVect point) -{ - return cpTransformPoint(body->transform, point); -} - -cpVect -cpBodyWorldToLocal(const cpBody *body, const cpVect point) -{ - return cpTransformPoint(cpTransformRigidInverse(body->transform), point); -} - -void -cpBodyApplyForceAtWorldPoint(cpBody *body, cpVect force, cpVect point) -{ - cpBodyActivate(body); - body->f = cpvadd(body->f, force); - - cpVect r = cpvsub(point, cpTransformPoint(body->transform, body->cog)); - body->t += cpvcross(r, force); -} - -void -cpBodyApplyForceAtLocalPoint(cpBody *body, cpVect force, cpVect point) -{ - cpBodyApplyForceAtWorldPoint(body, cpTransformVect(body->transform, force), cpTransformPoint(body->transform, point)); -} - -void -cpBodyApplyImpulseAtWorldPoint(cpBody *body, cpVect impulse, cpVect point) -{ - cpBodyActivate(body); - - cpVect r = cpvsub(point, cpTransformPoint(body->transform, body->cog)); - apply_impulse(body, impulse, r); -} - -void -cpBodyApplyImpulseAtLocalPoint(cpBody *body, cpVect impulse, cpVect point) -{ - cpBodyApplyImpulseAtWorldPoint(body, cpTransformVect(body->transform, impulse), cpTransformPoint(body->transform, point)); -} - -cpVect -cpBodyGetVelocityAtLocalPoint(const cpBody *body, cpVect point) -{ - cpVect r = cpTransformVect(body->transform, cpvsub(point, body->cog)); - return cpvadd(body->v, cpvmult(cpvperp(r), body->w)); -} - -cpVect -cpBodyGetVelocityAtWorldPoint(const cpBody *body, cpVect point) -{ - cpVect r = cpvsub(point, cpTransformPoint(body->transform, body->cog)); - return cpvadd(body->v, cpvmult(cpvperp(r), body->w)); -} - -cpFloat -cpBodyKineticEnergy(const cpBody *body) -{ - // Need to do some fudging to avoid NaNs - cpFloat vsq = cpvdot(body->v, body->v); - cpFloat wsq = body->w*body->w; - return (vsq ? vsq*body->m : 0.0f) + (wsq ? wsq*body->i : 0.0f); -} - -void -cpBodyEachShape(cpBody *body, cpBodyShapeIteratorFunc func, void *data) -{ - cpShape *shape = body->shapeList; - while(shape){ - cpShape *next = shape->next; - func(body, shape, data); - shape = next; - } -} - -void -cpBodyEachConstraint(cpBody *body, cpBodyConstraintIteratorFunc func, void *data) -{ - cpConstraint *constraint = body->constraintList; - while(constraint){ - cpConstraint *next = cpConstraintNext(constraint, body); - func(body, constraint, data); - constraint = next; - } -} - -void -cpBodyEachArbiter(cpBody *body, cpBodyArbiterIteratorFunc func, void *data) -{ - cpArbiter *arb = body->arbiterList; - while(arb){ - cpArbiter *next = cpArbiterNext(arb, body); - - cpBool swapped = arb->swapped; { - arb->swapped = (body == arb->body_b); - func(body, arb, data); - } arb->swapped = swapped; - - arb = next; - } -} diff --git a/apecs-physics/Chipmunk2D/src/cpCollision.c b/apecs-physics/Chipmunk2D/src/cpCollision.c deleted file mode 100644 index b2f41b1..0000000 --- a/apecs-physics/Chipmunk2D/src/cpCollision.c +++ /dev/null @@ -1,726 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include - -#include "chipmunk_private.h" -#include "cpRobust.h" - -#if DEBUG && 0 -#include "ChipmunkDemo.h" -#define DRAW_ALL 0 -#define DRAW_GJK (0 || DRAW_ALL) -#define DRAW_EPA (0 || DRAW_ALL) -#define DRAW_CLOSEST (0 || DRAW_ALL) -#define DRAW_CLIP (0 || DRAW_ALL) - -#define PRINT_LOG 0 -#endif - -#define MAX_GJK_ITERATIONS 30 -#define MAX_EPA_ITERATIONS 30 -#define WARN_GJK_ITERATIONS 20 -#define WARN_EPA_ITERATIONS 20 - -static inline void -cpCollisionInfoPushContact(struct cpCollisionInfo *info, cpVect p1, cpVect p2, cpHashValue hash) -{ - cpAssertSoft(info->count <= CP_MAX_CONTACTS_PER_ARBITER, "Internal error: Tried to push too many contacts."); - - struct cpContact *con = &info->arr[info->count]; - con->r1 = p1; - con->r2 = p2; - con->hash = hash; - - info->count++; -} - -//MARK: Support Points and Edges: - -// Support points are the maximal points on a shape's perimeter along a certain axis. -// The GJK and EPA algorithms use support points to iteratively sample the surface of the two shapes' minkowski difference. - -static inline int -PolySupportPointIndex(const int count, const struct cpSplittingPlane *planes, const cpVect n) -{ - cpFloat max = -INFINITY; - int index = 0; - - for(int i=0; i max){ - max = d; - index = i; - } - } - - return index; -} - -struct SupportPoint { - cpVect p; - // Save an index of the point so it can be cheaply looked up as a starting point for the next frame. - cpCollisionID index; -}; - -static inline struct SupportPoint -SupportPointNew(cpVect p, cpCollisionID index) -{ - struct SupportPoint point = {p, index}; - return point; -} - -typedef struct SupportPoint (*SupportPointFunc)(const cpShape *shape, const cpVect n); - -static inline struct SupportPoint -CircleSupportPoint(const cpCircleShape *circle, const cpVect n) -{ - return SupportPointNew(circle->tc, 0); -} - -static inline struct SupportPoint -SegmentSupportPoint(const cpSegmentShape *seg, const cpVect n) -{ - if(cpvdot(seg->ta, n) > cpvdot(seg->tb, n)){ - return SupportPointNew(seg->ta, 0); - } else { - return SupportPointNew(seg->tb, 1); - } -} - -static inline struct SupportPoint -PolySupportPoint(const cpPolyShape *poly, const cpVect n) -{ - const struct cpSplittingPlane *planes = poly->planes; - int i = PolySupportPointIndex(poly->count, planes, n); - return SupportPointNew(planes[i].v0, i); -} - -// A point on the surface of two shape's minkowski difference. -struct MinkowskiPoint { - // Cache the two original support points. - cpVect a, b; - // b - a - cpVect ab; - // Concatenate the two support point indexes. - cpCollisionID id; -}; - -static inline struct MinkowskiPoint -MinkowskiPointNew(const struct SupportPoint a, const struct SupportPoint b) -{ - struct MinkowskiPoint point = {a.p, b.p, cpvsub(b.p, a.p), (a.index & 0xFF)<<8 | (b.index & 0xFF)}; - return point; -} - -struct SupportContext { - const cpShape *shape1, *shape2; - SupportPointFunc func1, func2; -}; - -// Calculate the maximal point on the minkowski difference of two shapes along a particular axis. -static inline struct MinkowskiPoint -Support(const struct SupportContext *ctx, const cpVect n) -{ - struct SupportPoint a = ctx->func1(ctx->shape1, cpvneg(n)); - struct SupportPoint b = ctx->func2(ctx->shape2, n); - return MinkowskiPointNew(a, b); -} - -struct EdgePoint { - cpVect p; - // Keep a hash value for Chipmunk's collision hashing mechanism. - cpHashValue hash; -}; - -// Support edges are the edges of a polygon or segment shape that are in contact. -struct Edge { - struct EdgePoint a, b; - cpFloat r; - cpVect n; -}; - -static struct Edge -SupportEdgeForPoly(const cpPolyShape *poly, const cpVect n) -{ - int count = poly->count; - int i1 = PolySupportPointIndex(poly->count, poly->planes, n); - - // TODO: get rid of mod eventually, very expensive on ARM - int i0 = (i1 - 1 + count)%count; - int i2 = (i1 + 1)%count; - - const struct cpSplittingPlane *planes = poly->planes; - cpHashValue hashid = poly->shape.hashid; - if(cpvdot(n, planes[i1].n) > cpvdot(n, planes[i2].n)){ - struct Edge edge = {{planes[i0].v0, CP_HASH_PAIR(hashid, i0)}, {planes[i1].v0, CP_HASH_PAIR(hashid, i1)}, poly->r, planes[i1].n}; - return edge; - } else { - struct Edge edge = {{planes[i1].v0, CP_HASH_PAIR(hashid, i1)}, {planes[i2].v0, CP_HASH_PAIR(hashid, i2)}, poly->r, planes[i2].n}; - return edge; - } -} - -static struct Edge -SupportEdgeForSegment(const cpSegmentShape *seg, const cpVect n) -{ - cpHashValue hashid = seg->shape.hashid; - if(cpvdot(seg->tn, n) > 0.0){ - struct Edge edge = {{seg->ta, CP_HASH_PAIR(hashid, 0)}, {seg->tb, CP_HASH_PAIR(hashid, 1)}, seg->r, seg->tn}; - return edge; - } else { - struct Edge edge = {{seg->tb, CP_HASH_PAIR(hashid, 1)}, {seg->ta, CP_HASH_PAIR(hashid, 0)}, seg->r, cpvneg(seg->tn)}; - return edge; - } -} - -// Find the closest p(t) to (0, 0) where p(t) = a*(1-t)/2 + b*(1+t)/2 -// The range for t is [-1, 1] to avoid floating point issues if the parameters are swapped. -static inline cpFloat -ClosestT(const cpVect a, const cpVect b) -{ - cpVect delta = cpvsub(b, a); - return -cpfclamp(cpvdot(delta, cpvadd(a, b))/cpvlengthsq(delta), -1.0f, 1.0f); -} - -// Basically the same as cpvlerp(), except t = [-1, 1] -static inline cpVect -LerpT(const cpVect a, const cpVect b, const cpFloat t) -{ - cpFloat ht = 0.5f*t; - return cpvadd(cpvmult(a, 0.5f - ht), cpvmult(b, 0.5f + ht)); -} - -// Closest points on the surface of two shapes. -struct ClosestPoints { - // Surface points in absolute coordinates. - cpVect a, b; - // Minimum separating axis of the two shapes. - cpVect n; - // Signed distance between the points. - cpFloat d; - // Concatenation of the id's of the minkoski points. - cpCollisionID id; -}; - -// Calculate the closest points on two shapes given the closest edge on their minkowski difference to (0, 0) -static inline struct ClosestPoints -ClosestPointsNew(const struct MinkowskiPoint v0, const struct MinkowskiPoint v1) -{ - // Find the closest p(t) on the minkowski difference to (0, 0) - cpFloat t = ClosestT(v0.ab, v1.ab); - cpVect p = LerpT(v0.ab, v1.ab, t); - - // Interpolate the original support points using the same 't' value as above. - // This gives you the closest surface points in absolute coordinates. NEAT! - cpVect pa = LerpT(v0.a, v1.a, t); - cpVect pb = LerpT(v0.b, v1.b, t); - cpCollisionID id = (v0.id & 0xFFFF)<<16 | (v1.id & 0xFFFF); - - // First try calculating the MSA from the minkowski difference edge. - // This gives us a nice, accurate MSA when the surfaces are close together. - cpVect delta = cpvsub(v1.ab, v0.ab); - cpVect n = cpvnormalize(cpvrperp(delta)); - cpFloat d = cpvdot(n, p); - - if(d <= 0.0f || (-1.0f < t && t < 1.0f)){ - // If the shapes are overlapping, or we have a regular vertex/edge collision, we are done. - struct ClosestPoints points = {pa, pb, n, d, id}; - return points; - } else { - // Vertex/vertex collisions need special treatment since the MSA won't be shared with an axis of the minkowski difference. - cpFloat d2 = cpvlength(p); - cpVect n2 = cpvmult(p, 1.0f/(d2 + CPFLOAT_MIN)); - - struct ClosestPoints points = {pa, pb, n2, d2, id}; - return points; - } -} - -//MARK: EPA Functions - -static inline cpFloat -ClosestDist(const cpVect v0,const cpVect v1) -{ - return cpvlengthsq(LerpT(v0, v1, ClosestT(v0, v1))); -} - -// Recursive implementation of the EPA loop. -// Each recursion adds a point to the convex hull until it's known that we have the closest point on the surface. -static struct ClosestPoints -EPARecurse(const struct SupportContext *ctx, const int count, const struct MinkowskiPoint *hull, const int iteration) -{ - int mini = 0; - cpFloat minDist = INFINITY; - - // TODO: precalculate this when building the hull and save a step. - // Find the closest segment hull[i] and hull[i + 1] to (0, 0) - for(int j=0, i=count-1; j MAX_GJK_ITERATIONS){ - cpAssertWarn(iteration < WARN_GJK_ITERATIONS, "High GJK iterations: %d", iteration); - return ClosestPointsNew(v0, v1); - } - - if(cpCheckPointGreater(v1.ab, v0.ab, cpvzero)){ - // Origin is behind axis. Flip and try again. - return GJKRecurse(ctx, v1, v0, iteration); - } else { - cpFloat t = ClosestT(v0.ab, v1.ab); - cpVect n = (-1.0f < t && t < 1.0f ? cpvperp(cpvsub(v1.ab, v0.ab)) : cpvneg(LerpT(v0.ab, v1.ab, t))); - struct MinkowskiPoint p = Support(ctx, n); - -#if DRAW_GJK - ChipmunkDebugDrawSegment(v0.ab, v1.ab, RGBAColor(1, 1, 1, 1)); - cpVect c = cpvlerp(v0.ab, v1.ab, 0.5); - ChipmunkDebugDrawSegment(c, cpvadd(c, cpvmult(cpvnormalize(n), 5.0)), RGBAColor(1, 0, 0, 1)); - - ChipmunkDebugDrawDot(5.0, p.ab, LAColor(1, 1)); -#endif - - if(cpCheckPointGreater(p.ab, v0.ab, cpvzero) && cpCheckPointGreater(v1.ab, p.ab, cpvzero)){ - // The triangle v0, p, v1 contains the origin. Use EPA to find the MSA. - cpAssertWarn(iteration < WARN_GJK_ITERATIONS, "High GJK->EPA iterations: %d", iteration); - return EPA(ctx, v0, p, v1); - } else { - if(cpCheckAxis(v0.ab, v1.ab, p.ab, n)){ - // The edge v0, v1 that we already have is the closest to (0, 0) since p was not closer. - cpAssertWarn(iteration < WARN_GJK_ITERATIONS, "High GJK iterations: %d", iteration); - return ClosestPointsNew(v0, v1); - } else { - // p was closer to the origin than our existing edge. - // Need to figure out which existing point to drop. - if(ClosestDist(v0.ab, p.ab) < ClosestDist(p.ab, v1.ab)){ - return GJKRecurse(ctx, v0, p, iteration + 1); - } else { - return GJKRecurse(ctx, p, v1, iteration + 1); - } - } - } - } -} - -// Get a SupportPoint from a cached shape and index. -static struct SupportPoint -ShapePoint(const cpShape *shape, const int i) -{ - switch(shape->klass->type){ - case CP_CIRCLE_SHAPE: { - return SupportPointNew(((cpCircleShape *)shape)->tc, 0); - } case CP_SEGMENT_SHAPE: { - cpSegmentShape *seg = (cpSegmentShape *)shape; - return SupportPointNew(i == 0 ? seg->ta : seg->tb, i); - } case CP_POLY_SHAPE: { - cpPolyShape *poly = (cpPolyShape *)shape; - // Poly shapes may change vertex count. - int index = (i < poly->count ? i : 0); - return SupportPointNew(poly->planes[index].v0, index); - } default: { - return SupportPointNew(cpvzero, 0); - } - } -} - -// Find the closest points between two shapes using the GJK algorithm. -static struct ClosestPoints -GJK(const struct SupportContext *ctx, cpCollisionID *id) -{ -#if DRAW_GJK || DRAW_EPA - int count1 = 1; - int count2 = 1; - - switch(ctx->shape1->klass->type){ - case CP_SEGMENT_SHAPE: count1 = 2; break; - case CP_POLY_SHAPE: count1 = ((cpPolyShape *)ctx->shape1)->count; break; - default: break; - } - - switch(ctx->shape2->klass->type){ - case CP_SEGMENT_SHAPE: count1 = 2; break; - case CP_POLY_SHAPE: count2 = ((cpPolyShape *)ctx->shape2)->count; break; - default: break; - } - - - // draw the minkowski difference origin - cpVect origin = cpvzero; - ChipmunkDebugDrawDot(5.0, origin, RGBAColor(1,0,0,1)); - - int mdiffCount = count1*count2; - cpVect *mdiffVerts = alloca(mdiffCount*sizeof(cpVect)); - - for(int i=0; ishape2, j).p, ShapePoint(ctx->shape1, i).p); - mdiffVerts[i*count2 + j] = v; - ChipmunkDebugDrawDot(2.0, v, RGBAColor(1, 0, 0, 1)); - } - } - - cpVect *hullVerts = alloca(mdiffCount*sizeof(cpVect)); - int hullCount = cpConvexHull(mdiffCount, mdiffVerts, hullVerts, NULL, 0.0); - - ChipmunkDebugDrawPolygon(hullCount, hullVerts, 0.0, RGBAColor(1, 0, 0, 1), RGBAColor(1, 0, 0, 0.25)); -#endif - - struct MinkowskiPoint v0, v1; - if(*id){ - // Use the minkowski points from the last frame as a starting point using the cached indexes. - v0 = MinkowskiPointNew(ShapePoint(ctx->shape1, (*id>>24)&0xFF), ShapePoint(ctx->shape2, (*id>>16)&0xFF)); - v1 = MinkowskiPointNew(ShapePoint(ctx->shape1, (*id>> 8)&0xFF), ShapePoint(ctx->shape2, (*id )&0xFF)); - } else { - // No cached indexes, use the shapes' bounding box centers as a guess for a starting axis. - cpVect axis = cpvperp(cpvsub(cpBBCenter(ctx->shape1->bb), cpBBCenter(ctx->shape2->bb))); - v0 = Support(ctx, axis); - v1 = Support(ctx, cpvneg(axis)); - } - - struct ClosestPoints points = GJKRecurse(ctx, v0, v1, 1); - *id = points.id; - return points; -} - -//MARK: Contact Clipping - -// Given two support edges, find contact point pairs on their surfaces. -static inline void -ContactPoints(const struct Edge e1, const struct Edge e2, const struct ClosestPoints points, struct cpCollisionInfo *info) -{ - cpFloat mindist = e1.r + e2.r; - if(points.d <= mindist){ -#ifdef DRAW_CLIP - ChipmunkDebugDrawFatSegment(e1.a.p, e1.b.p, e1.r, RGBAColor(0, 1, 0, 1), LAColor(0, 0)); - ChipmunkDebugDrawFatSegment(e2.a.p, e2.b.p, e2.r, RGBAColor(1, 0, 0, 1), LAColor(0, 0)); -#endif - cpVect n = info->n = points.n; - - // Distances along the axis parallel to n - cpFloat d_e1_a = cpvcross(e1.a.p, n); - cpFloat d_e1_b = cpvcross(e1.b.p, n); - cpFloat d_e2_a = cpvcross(e2.a.p, n); - cpFloat d_e2_b = cpvcross(e2.b.p, n); - - // TODO + min isn't a complete fix. - cpFloat e1_denom = 1.0f/(d_e1_b - d_e1_a + CPFLOAT_MIN); - cpFloat e2_denom = 1.0f/(d_e2_b - d_e2_a + CPFLOAT_MIN); - - // Project the endpoints of the two edges onto the opposing edge, clamping them as necessary. - // Compare the projected points to the collision normal to see if the shapes overlap there. - { - cpVect p1 = cpvadd(cpvmult(n, e1.r), cpvlerp(e1.a.p, e1.b.p, cpfclamp01((d_e2_b - d_e1_a)*e1_denom))); - cpVect p2 = cpvadd(cpvmult(n, -e2.r), cpvlerp(e2.a.p, e2.b.p, cpfclamp01((d_e1_a - d_e2_a)*e2_denom))); - cpFloat dist = cpvdot(cpvsub(p2, p1), n); - if(dist <= 0.0f){ - cpHashValue hash_1a2b = CP_HASH_PAIR(e1.a.hash, e2.b.hash); - cpCollisionInfoPushContact(info, p1, p2, hash_1a2b); - } - }{ - cpVect p1 = cpvadd(cpvmult(n, e1.r), cpvlerp(e1.a.p, e1.b.p, cpfclamp01((d_e2_a - d_e1_a)*e1_denom))); - cpVect p2 = cpvadd(cpvmult(n, -e2.r), cpvlerp(e2.a.p, e2.b.p, cpfclamp01((d_e1_b - d_e2_a)*e2_denom))); - cpFloat dist = cpvdot(cpvsub(p2, p1), n); - if(dist <= 0.0f){ - cpHashValue hash_1b2a = CP_HASH_PAIR(e1.b.hash, e2.a.hash); - cpCollisionInfoPushContact(info, p1, p2, hash_1b2a); - } - } - } -} - -//MARK: Collision Functions - -typedef void (*CollisionFunc)(const cpShape *a, const cpShape *b, struct cpCollisionInfo *info); - -// Collide circle shapes. -static void -CircleToCircle(const cpCircleShape *c1, const cpCircleShape *c2, struct cpCollisionInfo *info) -{ - cpFloat mindist = c1->r + c2->r; - cpVect delta = cpvsub(c2->tc, c1->tc); - cpFloat distsq = cpvlengthsq(delta); - - if(distsq < mindist*mindist){ - cpFloat dist = cpfsqrt(distsq); - cpVect n = info->n = (dist ? cpvmult(delta, 1.0f/dist) : cpv(1.0f, 0.0f)); - cpCollisionInfoPushContact(info, cpvadd(c1->tc, cpvmult(n, c1->r)), cpvadd(c2->tc, cpvmult(n, -c2->r)), 0); - } -} - -static void -CircleToSegment(const cpCircleShape *circle, const cpSegmentShape *segment, struct cpCollisionInfo *info) -{ - cpVect seg_a = segment->ta; - cpVect seg_b = segment->tb; - cpVect center = circle->tc; - - // Find the closest point on the segment to the circle. - cpVect seg_delta = cpvsub(seg_b, seg_a); - cpFloat closest_t = cpfclamp01(cpvdot(seg_delta, cpvsub(center, seg_a))/cpvlengthsq(seg_delta)); - cpVect closest = cpvadd(seg_a, cpvmult(seg_delta, closest_t)); - - // Compare the radii of the two shapes to see if they are colliding. - cpFloat mindist = circle->r + segment->r; - cpVect delta = cpvsub(closest, center); - cpFloat distsq = cpvlengthsq(delta); - if(distsq < mindist*mindist){ - cpFloat dist = cpfsqrt(distsq); - // Handle coincident shapes as gracefully as possible. - cpVect n = info->n = (dist ? cpvmult(delta, 1.0f/dist) : segment->tn); - - // Reject endcap collisions if tangents are provided. - cpVect rot = cpBodyGetRotation(segment->shape.body); - if( - (closest_t != 0.0f || cpvdot(n, cpvrotate(segment->a_tangent, rot)) >= 0.0) && - (closest_t != 1.0f || cpvdot(n, cpvrotate(segment->b_tangent, rot)) >= 0.0) - ){ - cpCollisionInfoPushContact(info, cpvadd(center, cpvmult(n, circle->r)), cpvadd(closest, cpvmult(n, -segment->r)), 0); - } - } -} - -static void -SegmentToSegment(const cpSegmentShape *seg1, const cpSegmentShape *seg2, struct cpCollisionInfo *info) -{ - struct SupportContext context = {(cpShape *)seg1, (cpShape *)seg2, (SupportPointFunc)SegmentSupportPoint, (SupportPointFunc)SegmentSupportPoint}; - struct ClosestPoints points = GJK(&context, &info->id); - -#if DRAW_CLOSEST -#if PRINT_LOG -// ChipmunkDemoPrintString("Distance: %.2f\n", points.d); -#endif - - ChipmunkDebugDrawDot(6.0, points.a, RGBAColor(1, 1, 1, 1)); - ChipmunkDebugDrawDot(6.0, points.b, RGBAColor(1, 1, 1, 1)); - ChipmunkDebugDrawSegment(points.a, points.b, RGBAColor(1, 1, 1, 1)); - ChipmunkDebugDrawSegment(points.a, cpvadd(points.a, cpvmult(points.n, 10.0)), RGBAColor(1, 0, 0, 1)); -#endif - - cpVect n = points.n; - cpVect rot1 = cpBodyGetRotation(seg1->shape.body); - cpVect rot2 = cpBodyGetRotation(seg2->shape.body); - - // If the closest points are nearer than the sum of the radii... - if( - points.d <= (seg1->r + seg2->r) && ( - // Reject endcap collisions if tangents are provided. - (!cpveql(points.a, seg1->ta) || cpvdot(n, cpvrotate(seg1->a_tangent, rot1)) <= 0.0) && - (!cpveql(points.a, seg1->tb) || cpvdot(n, cpvrotate(seg1->b_tangent, rot1)) <= 0.0) && - (!cpveql(points.b, seg2->ta) || cpvdot(n, cpvrotate(seg2->a_tangent, rot2)) >= 0.0) && - (!cpveql(points.b, seg2->tb) || cpvdot(n, cpvrotate(seg2->b_tangent, rot2)) >= 0.0) - ) - ){ - ContactPoints(SupportEdgeForSegment(seg1, n), SupportEdgeForSegment(seg2, cpvneg(n)), points, info); - } -} - -static void -PolyToPoly(const cpPolyShape *poly1, const cpPolyShape *poly2, struct cpCollisionInfo *info) -{ - struct SupportContext context = {(cpShape *)poly1, (cpShape *)poly2, (SupportPointFunc)PolySupportPoint, (SupportPointFunc)PolySupportPoint}; - struct ClosestPoints points = GJK(&context, &info->id); - -#if DRAW_CLOSEST -#if PRINT_LOG -// ChipmunkDemoPrintString("Distance: %.2f\n", points.d); -#endif - - ChipmunkDebugDrawDot(3.0, points.a, RGBAColor(1, 1, 1, 1)); - ChipmunkDebugDrawDot(3.0, points.b, RGBAColor(1, 1, 1, 1)); - ChipmunkDebugDrawSegment(points.a, points.b, RGBAColor(1, 1, 1, 1)); - ChipmunkDebugDrawSegment(points.a, cpvadd(points.a, cpvmult(points.n, 10.0)), RGBAColor(1, 0, 0, 1)); -#endif - - // If the closest points are nearer than the sum of the radii... - if(points.d - poly1->r - poly2->r <= 0.0){ - ContactPoints(SupportEdgeForPoly(poly1, points.n), SupportEdgeForPoly(poly2, cpvneg(points.n)), points, info); - } -} - -static void -SegmentToPoly(const cpSegmentShape *seg, const cpPolyShape *poly, struct cpCollisionInfo *info) -{ - struct SupportContext context = {(cpShape *)seg, (cpShape *)poly, (SupportPointFunc)SegmentSupportPoint, (SupportPointFunc)PolySupportPoint}; - struct ClosestPoints points = GJK(&context, &info->id); - -#if DRAW_CLOSEST -#if PRINT_LOG -// ChipmunkDemoPrintString("Distance: %.2f\n", points.d); -#endif - - ChipmunkDebugDrawDot(3.0, points.a, RGBAColor(1, 1, 1, 1)); - ChipmunkDebugDrawDot(3.0, points.b, RGBAColor(1, 1, 1, 1)); - ChipmunkDebugDrawSegment(points.a, points.b, RGBAColor(1, 1, 1, 1)); - ChipmunkDebugDrawSegment(points.a, cpvadd(points.a, cpvmult(points.n, 10.0)), RGBAColor(1, 0, 0, 1)); -#endif - - cpVect n = points.n; - cpVect rot = cpBodyGetRotation(seg->shape.body); - - if( - // If the closest points are nearer than the sum of the radii... - points.d - seg->r - poly->r <= 0.0 && ( - // Reject endcap collisions if tangents are provided. - (!cpveql(points.a, seg->ta) || cpvdot(n, cpvrotate(seg->a_tangent, rot)) <= 0.0) && - (!cpveql(points.a, seg->tb) || cpvdot(n, cpvrotate(seg->b_tangent, rot)) <= 0.0) - ) - ){ - ContactPoints(SupportEdgeForSegment(seg, n), SupportEdgeForPoly(poly, cpvneg(n)), points, info); - } -} - -static void -CircleToPoly(const cpCircleShape *circle, const cpPolyShape *poly, struct cpCollisionInfo *info) -{ - struct SupportContext context = {(cpShape *)circle, (cpShape *)poly, (SupportPointFunc)CircleSupportPoint, (SupportPointFunc)PolySupportPoint}; - struct ClosestPoints points = GJK(&context, &info->id); - -#if DRAW_CLOSEST - ChipmunkDebugDrawDot(3.0, points.a, RGBAColor(1, 1, 1, 1)); - ChipmunkDebugDrawDot(3.0, points.b, RGBAColor(1, 1, 1, 1)); - ChipmunkDebugDrawSegment(points.a, points.b, RGBAColor(1, 1, 1, 1)); - ChipmunkDebugDrawSegment(points.a, cpvadd(points.a, cpvmult(points.n, 10.0)), RGBAColor(1, 0, 0, 1)); -#endif - - // If the closest points are nearer than the sum of the radii... - if(points.d <= circle->r + poly->r){ - cpVect n = info->n = points.n; - cpCollisionInfoPushContact(info, cpvadd(points.a, cpvmult(n, circle->r)), cpvadd(points.b, cpvmult(n, poly->r)), 0); - } -} - -static void -CollisionError(const cpShape *circle, const cpShape *poly, struct cpCollisionInfo *info) -{ - cpAssertHard(cpFalse, "Internal Error: Shape types are not sorted."); -} - - -static const CollisionFunc BuiltinCollisionFuncs[9] = { - (CollisionFunc)CircleToCircle, - CollisionError, - CollisionError, - (CollisionFunc)CircleToSegment, - (CollisionFunc)SegmentToSegment, - CollisionError, - (CollisionFunc)CircleToPoly, - (CollisionFunc)SegmentToPoly, - (CollisionFunc)PolyToPoly, -}; -static const CollisionFunc *CollisionFuncs = BuiltinCollisionFuncs; - -struct cpCollisionInfo -cpCollide(const cpShape *a, const cpShape *b, cpCollisionID id, struct cpContact *contacts) -{ - struct cpCollisionInfo info = {a, b, id, cpvzero, 0, contacts}; - - // Make sure the shape types are in order. - if(a->klass->type > b->klass->type){ - info.a = b; - info.b = a; - } - - CollisionFuncs[info.a->klass->type + info.b->klass->type*CP_NUM_SHAPES](info.a, info.b, &info); - -// if(0){ -// for(int i=0; iklass = klass; - - constraint->a = a; - constraint->b = b; - constraint->space = NULL; - - constraint->next_a = NULL; - constraint->next_b = NULL; - - constraint->maxForce = (cpFloat)INFINITY; - constraint->errorBias = cpfpow(1.0f - 0.1f, 60.0f); - constraint->maxBias = (cpFloat)INFINITY; - - constraint->collideBodies = cpTrue; - - constraint->preSolve = NULL; - constraint->postSolve = NULL; -} - -cpSpace * -cpConstraintGetSpace(const cpConstraint *constraint) -{ - return constraint->space; -} - -cpBody * -cpConstraintGetBodyA(const cpConstraint *constraint) -{ - return constraint->a; -} - -cpBody * -cpConstraintGetBodyB(const cpConstraint *constraint) -{ - return constraint->b; -} - -cpFloat -cpConstraintGetMaxForce(const cpConstraint *constraint) -{ - return constraint->maxForce; -} - -void -cpConstraintSetMaxForce(cpConstraint *constraint, cpFloat maxForce) -{ - cpAssertHard(maxForce >= 0.0f, "maxForce must be positive."); - cpConstraintActivateBodies(constraint); - constraint->maxForce = maxForce; -} - -cpFloat -cpConstraintGetErrorBias(const cpConstraint *constraint) -{ - return constraint->errorBias; -} - -void -cpConstraintSetErrorBias(cpConstraint *constraint, cpFloat errorBias) -{ - cpAssertHard(errorBias >= 0.0f, "errorBias must be positive."); - cpConstraintActivateBodies(constraint); - constraint->errorBias = errorBias; -} - -cpFloat -cpConstraintGetMaxBias(const cpConstraint *constraint) -{ - return constraint->maxBias; -} - -void -cpConstraintSetMaxBias(cpConstraint *constraint, cpFloat maxBias) -{ - cpAssertHard(maxBias >= 0.0f, "maxBias must be positive."); - cpConstraintActivateBodies(constraint); - constraint->maxBias = maxBias; -} - -cpBool -cpConstraintGetCollideBodies(const cpConstraint *constraint) -{ - return constraint->collideBodies; -} - -void -cpConstraintSetCollideBodies(cpConstraint *constraint, cpBool collideBodies) -{ - cpConstraintActivateBodies(constraint); - constraint->collideBodies = collideBodies; -} - -cpConstraintPreSolveFunc -cpConstraintGetPreSolveFunc(const cpConstraint *constraint) -{ - return constraint->preSolve; -} - -void -cpConstraintSetPreSolveFunc(cpConstraint *constraint, cpConstraintPreSolveFunc preSolveFunc) -{ - constraint->preSolve = preSolveFunc; -} - -cpConstraintPostSolveFunc -cpConstraintGetPostSolveFunc(const cpConstraint *constraint) -{ - return constraint->postSolve; -} - -void -cpConstraintSetPostSolveFunc(cpConstraint *constraint, cpConstraintPostSolveFunc postSolveFunc) -{ - constraint->postSolve = postSolveFunc; -} - -cpDataPointer -cpConstraintGetUserData(const cpConstraint *constraint) -{ - return constraint->userData; -} - -void -cpConstraintSetUserData(cpConstraint *constraint, cpDataPointer userData) -{ - constraint->userData = userData; -} - - -cpFloat -cpConstraintGetImpulse(cpConstraint *constraint) -{ - return constraint->klass->getImpulse(constraint); -} diff --git a/apecs-physics/Chipmunk2D/src/cpDampedRotarySpring.c b/apecs-physics/Chipmunk2D/src/cpDampedRotarySpring.c deleted file mode 100644 index 68cf754..0000000 --- a/apecs-physics/Chipmunk2D/src/cpDampedRotarySpring.c +++ /dev/null @@ -1,178 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "chipmunk_private.h" - -static cpFloat -defaultSpringTorque(cpDampedRotarySpring *spring, cpFloat relativeAngle){ - return (relativeAngle - spring->restAngle)*spring->stiffness; -} - -static void -preStep(cpDampedRotarySpring *spring, cpFloat dt) -{ - cpBody *a = spring->constraint.a; - cpBody *b = spring->constraint.b; - - cpFloat moment = a->i_inv + b->i_inv; - cpAssertSoft(moment != 0.0, "Unsolvable spring."); - spring->iSum = 1.0f/moment; - - spring->w_coef = 1.0f - cpfexp(-spring->damping*dt*moment); - spring->target_wrn = 0.0f; - - // apply spring torque - cpFloat j_spring = spring->springTorqueFunc((cpConstraint *)spring, a->a - b->a)*dt; - spring->jAcc = j_spring; - - a->w -= j_spring*a->i_inv; - b->w += j_spring*b->i_inv; -} - -static void applyCachedImpulse(cpDampedRotarySpring *spring, cpFloat dt_coef){} - -static void -applyImpulse(cpDampedRotarySpring *spring, cpFloat dt) -{ - cpBody *a = spring->constraint.a; - cpBody *b = spring->constraint.b; - - // compute relative velocity - cpFloat wrn = a->w - b->w;//normal_relative_velocity(a, b, r1, r2, n) - spring->target_vrn; - - // compute velocity loss from drag - // not 100% certain this is derived correctly, though it makes sense - cpFloat w_damp = (spring->target_wrn - wrn)*spring->w_coef; - spring->target_wrn = wrn + w_damp; - - //apply_impulses(a, b, spring->r1, spring->r2, cpvmult(spring->n, v_damp*spring->nMass)); - cpFloat j_damp = w_damp*spring->iSum; - spring->jAcc += j_damp; - - a->w += j_damp*a->i_inv; - b->w -= j_damp*b->i_inv; -} - -static cpFloat -getImpulse(cpDampedRotarySpring *spring) -{ - return spring->jAcc; -} - -static const cpConstraintClass klass = { - (cpConstraintPreStepImpl)preStep, - (cpConstraintApplyCachedImpulseImpl)applyCachedImpulse, - (cpConstraintApplyImpulseImpl)applyImpulse, - (cpConstraintGetImpulseImpl)getImpulse, -}; - -cpDampedRotarySpring * -cpDampedRotarySpringAlloc(void) -{ - return (cpDampedRotarySpring *)cpcalloc(1, sizeof(cpDampedRotarySpring)); -} - -cpDampedRotarySpring * -cpDampedRotarySpringInit(cpDampedRotarySpring *spring, cpBody *a, cpBody *b, cpFloat restAngle, cpFloat stiffness, cpFloat damping) -{ - cpConstraintInit((cpConstraint *)spring, &klass, a, b); - - spring->restAngle = restAngle; - spring->stiffness = stiffness; - spring->damping = damping; - spring->springTorqueFunc = (cpDampedRotarySpringTorqueFunc)defaultSpringTorque; - - spring->jAcc = 0.0f; - - return spring; -} - -cpConstraint * -cpDampedRotarySpringNew(cpBody *a, cpBody *b, cpFloat restAngle, cpFloat stiffness, cpFloat damping) -{ - return (cpConstraint *)cpDampedRotarySpringInit(cpDampedRotarySpringAlloc(), a, b, restAngle, stiffness, damping); -} - -cpBool -cpConstraintIsDampedRotarySpring(const cpConstraint *constraint) -{ - return (constraint->klass == &klass); -} - -cpFloat -cpDampedRotarySpringGetRestAngle(const cpConstraint *constraint) -{ - cpAssertHard(cpConstraintIsDampedRotarySpring(constraint), "Constraint is not a damped rotary spring."); - return ((cpDampedRotarySpring *)constraint)->restAngle; -} - -void -cpDampedRotarySpringSetRestAngle(cpConstraint *constraint, cpFloat restAngle) -{ - cpAssertHard(cpConstraintIsDampedRotarySpring(constraint), "Constraint is not a damped rotary spring."); - cpConstraintActivateBodies(constraint); - ((cpDampedRotarySpring *)constraint)->restAngle = restAngle; -} - -cpFloat -cpDampedRotarySpringGetStiffness(const cpConstraint *constraint) -{ - cpAssertHard(cpConstraintIsDampedRotarySpring(constraint), "Constraint is not a damped rotary spring."); - return ((cpDampedRotarySpring *)constraint)->stiffness; -} - -void -cpDampedRotarySpringSetStiffness(cpConstraint *constraint, cpFloat stiffness) -{ - cpAssertHard(cpConstraintIsDampedRotarySpring(constraint), "Constraint is not a damped rotary spring."); - cpConstraintActivateBodies(constraint); - ((cpDampedRotarySpring *)constraint)->stiffness = stiffness; -} - -cpFloat -cpDampedRotarySpringGetDamping(const cpConstraint *constraint) -{ - cpAssertHard(cpConstraintIsDampedRotarySpring(constraint), "Constraint is not a damped rotary spring."); - return ((cpDampedRotarySpring *)constraint)->damping; -} - -void -cpDampedRotarySpringSetDamping(cpConstraint *constraint, cpFloat damping) -{ - cpAssertHard(cpConstraintIsDampedRotarySpring(constraint), "Constraint is not a damped rotary spring."); - cpConstraintActivateBodies(constraint); - ((cpDampedRotarySpring *)constraint)->damping = damping; -} - -cpDampedRotarySpringTorqueFunc -cpDampedRotarySpringGetSpringTorqueFunc(const cpConstraint *constraint) -{ - cpAssertHard(cpConstraintIsDampedRotarySpring(constraint), "Constraint is not a damped rotary spring."); - return ((cpDampedRotarySpring *)constraint)->springTorqueFunc; -} - -void -cpDampedRotarySpringSetSpringTorqueFunc(cpConstraint *constraint, cpDampedRotarySpringTorqueFunc springTorqueFunc) -{ - cpAssertHard(cpConstraintIsDampedRotarySpring(constraint), "Constraint is not a damped rotary spring."); - cpConstraintActivateBodies(constraint); - ((cpDampedRotarySpring *)constraint)->springTorqueFunc = springTorqueFunc; -} diff --git a/apecs-physics/Chipmunk2D/src/cpDampedSpring.c b/apecs-physics/Chipmunk2D/src/cpDampedSpring.c deleted file mode 100644 index 4e9e193..0000000 --- a/apecs-physics/Chipmunk2D/src/cpDampedSpring.c +++ /dev/null @@ -1,216 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "chipmunk_private.h" - -static cpFloat -defaultSpringForce(cpDampedSpring *spring, cpFloat dist){ - return (spring->restLength - dist)*spring->stiffness; -} - -static void -preStep(cpDampedSpring *spring, cpFloat dt) -{ - cpBody *a = spring->constraint.a; - cpBody *b = spring->constraint.b; - - spring->r1 = cpTransformVect(a->transform, cpvsub(spring->anchorA, a->cog)); - spring->r2 = cpTransformVect(b->transform, cpvsub(spring->anchorB, b->cog)); - - cpVect delta = cpvsub(cpvadd(b->p, spring->r2), cpvadd(a->p, spring->r1)); - cpFloat dist = cpvlength(delta); - spring->n = cpvmult(delta, 1.0f/(dist ? dist : INFINITY)); - - cpFloat k = k_scalar(a, b, spring->r1, spring->r2, spring->n); - cpAssertSoft(k != 0.0, "Unsolvable spring."); - spring->nMass = 1.0f/k; - - spring->target_vrn = 0.0f; - spring->v_coef = 1.0f - cpfexp(-spring->damping*dt*k); - - // apply spring force - cpFloat f_spring = spring->springForceFunc((cpConstraint *)spring, dist); - cpFloat j_spring = spring->jAcc = f_spring*dt; - apply_impulses(a, b, spring->r1, spring->r2, cpvmult(spring->n, j_spring)); -} - -static void applyCachedImpulse(cpDampedSpring *spring, cpFloat dt_coef){} - -static void -applyImpulse(cpDampedSpring *spring, cpFloat dt) -{ - cpBody *a = spring->constraint.a; - cpBody *b = spring->constraint.b; - - cpVect n = spring->n; - cpVect r1 = spring->r1; - cpVect r2 = spring->r2; - - // compute relative velocity - cpFloat vrn = normal_relative_velocity(a, b, r1, r2, n); - - // compute velocity loss from drag - cpFloat v_damp = (spring->target_vrn - vrn)*spring->v_coef; - spring->target_vrn = vrn + v_damp; - - cpFloat j_damp = v_damp*spring->nMass; - spring->jAcc += j_damp; - apply_impulses(a, b, spring->r1, spring->r2, cpvmult(spring->n, j_damp)); -} - -static cpFloat -getImpulse(cpDampedSpring *spring) -{ - return spring->jAcc; -} - -static const cpConstraintClass klass = { - (cpConstraintPreStepImpl)preStep, - (cpConstraintApplyCachedImpulseImpl)applyCachedImpulse, - (cpConstraintApplyImpulseImpl)applyImpulse, - (cpConstraintGetImpulseImpl)getImpulse, -}; - -cpDampedSpring * -cpDampedSpringAlloc(void) -{ - return (cpDampedSpring *)cpcalloc(1, sizeof(cpDampedSpring)); -} - -cpDampedSpring * -cpDampedSpringInit(cpDampedSpring *spring, cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB, cpFloat restLength, cpFloat stiffness, cpFloat damping) -{ - cpConstraintInit((cpConstraint *)spring, &klass, a, b); - - spring->anchorA = anchorA; - spring->anchorB = anchorB; - - spring->restLength = restLength; - spring->stiffness = stiffness; - spring->damping = damping; - spring->springForceFunc = (cpDampedSpringForceFunc)defaultSpringForce; - - spring->jAcc = 0.0f; - - return spring; -} - -cpConstraint * -cpDampedSpringNew(cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB, cpFloat restLength, cpFloat stiffness, cpFloat damping) -{ - return (cpConstraint *)cpDampedSpringInit(cpDampedSpringAlloc(), a, b, anchorA, anchorB, restLength, stiffness, damping); -} - -cpBool -cpConstraintIsDampedSpring(const cpConstraint *constraint) -{ - return (constraint->klass == &klass); -} - -cpVect -cpDampedSpringGetAnchorA(const cpConstraint *constraint) -{ - cpAssertHard(cpConstraintIsDampedSpring(constraint), "Constraint is not a damped spring."); - return ((cpDampedSpring *)constraint)->anchorA; -} - -void -cpDampedSpringSetAnchorA(cpConstraint *constraint, cpVect anchorA) -{ - cpAssertHard(cpConstraintIsDampedSpring(constraint), "Constraint is not a damped spring."); - cpConstraintActivateBodies(constraint); - ((cpDampedSpring *)constraint)->anchorA = anchorA; -} - -cpVect -cpDampedSpringGetAnchorB(const cpConstraint *constraint) -{ - cpAssertHard(cpConstraintIsDampedSpring(constraint), "Constraint is not a damped spring."); - return ((cpDampedSpring *)constraint)->anchorB; -} - -void -cpDampedSpringSetAnchorB(cpConstraint *constraint, cpVect anchorB) -{ - cpAssertHard(cpConstraintIsDampedSpring(constraint), "Constraint is not a damped spring."); - cpConstraintActivateBodies(constraint); - ((cpDampedSpring *)constraint)->anchorB = anchorB; -} - -cpFloat -cpDampedSpringGetRestLength(const cpConstraint *constraint) -{ - cpAssertHard(cpConstraintIsDampedSpring(constraint), "Constraint is not a damped spring."); - return ((cpDampedSpring *)constraint)->restLength; -} - -void -cpDampedSpringSetRestLength(cpConstraint *constraint, cpFloat restLength) -{ - cpAssertHard(cpConstraintIsDampedSpring(constraint), "Constraint is not a damped spring."); - cpConstraintActivateBodies(constraint); - ((cpDampedSpring *)constraint)->restLength = restLength; -} - -cpFloat -cpDampedSpringGetStiffness(const cpConstraint *constraint) -{ - cpAssertHard(cpConstraintIsDampedSpring(constraint), "Constraint is not a damped spring."); - return ((cpDampedSpring *)constraint)->stiffness; -} - -void -cpDampedSpringSetStiffness(cpConstraint *constraint, cpFloat stiffness) -{ - cpAssertHard(cpConstraintIsDampedSpring(constraint), "Constraint is not a damped spring."); - cpConstraintActivateBodies(constraint); - ((cpDampedSpring *)constraint)->stiffness = stiffness; -} - -cpFloat -cpDampedSpringGetDamping(const cpConstraint *constraint) -{ - cpAssertHard(cpConstraintIsDampedSpring(constraint), "Constraint is not a damped spring."); - return ((cpDampedSpring *)constraint)->damping; -} - -void -cpDampedSpringSetDamping(cpConstraint *constraint, cpFloat damping) -{ - cpAssertHard(cpConstraintIsDampedSpring(constraint), "Constraint is not a damped spring."); - cpConstraintActivateBodies(constraint); - ((cpDampedSpring *)constraint)->damping = damping; -} - -cpDampedSpringForceFunc -cpDampedSpringGetSpringForceFunc(const cpConstraint *constraint) -{ - cpAssertHard(cpConstraintIsDampedSpring(constraint), "Constraint is not a damped spring."); - return ((cpDampedSpring *)constraint)->springForceFunc; -} - -void -cpDampedSpringSetSpringForceFunc(cpConstraint *constraint, cpDampedSpringForceFunc springForceFunc) -{ - cpAssertHard(cpConstraintIsDampedSpring(constraint), "Constraint is not a damped spring."); - cpConstraintActivateBodies(constraint); - ((cpDampedSpring *)constraint)->springForceFunc = springForceFunc; -} diff --git a/apecs-physics/Chipmunk2D/src/cpGearJoint.c b/apecs-physics/Chipmunk2D/src/cpGearJoint.c deleted file mode 100644 index b76aa47..0000000 --- a/apecs-physics/Chipmunk2D/src/cpGearJoint.c +++ /dev/null @@ -1,145 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "chipmunk_private.h" - -static void -preStep(cpGearJoint *joint, cpFloat dt) -{ - cpBody *a = joint->constraint.a; - cpBody *b = joint->constraint.b; - - // calculate moment of inertia coefficient. - joint->iSum = 1.0f/(a->i_inv*joint->ratio_inv + joint->ratio*b->i_inv); - - // calculate bias velocity - cpFloat maxBias = joint->constraint.maxBias; - joint->bias = cpfclamp(-bias_coef(joint->constraint.errorBias, dt)*(b->a*joint->ratio - a->a - joint->phase)/dt, -maxBias, maxBias); -} - -static void -applyCachedImpulse(cpGearJoint *joint, cpFloat dt_coef) -{ - cpBody *a = joint->constraint.a; - cpBody *b = joint->constraint.b; - - cpFloat j = joint->jAcc*dt_coef; - a->w -= j*a->i_inv*joint->ratio_inv; - b->w += j*b->i_inv; -} - -static void -applyImpulse(cpGearJoint *joint, cpFloat dt) -{ - cpBody *a = joint->constraint.a; - cpBody *b = joint->constraint.b; - - // compute relative rotational velocity - cpFloat wr = b->w*joint->ratio - a->w; - - cpFloat jMax = joint->constraint.maxForce*dt; - - // compute normal impulse - cpFloat j = (joint->bias - wr)*joint->iSum; - cpFloat jOld = joint->jAcc; - joint->jAcc = cpfclamp(jOld + j, -jMax, jMax); - j = joint->jAcc - jOld; - - // apply impulse - a->w -= j*a->i_inv*joint->ratio_inv; - b->w += j*b->i_inv; -} - -static cpFloat -getImpulse(cpGearJoint *joint) -{ - return cpfabs(joint->jAcc); -} - -static const cpConstraintClass klass = { - (cpConstraintPreStepImpl)preStep, - (cpConstraintApplyCachedImpulseImpl)applyCachedImpulse, - (cpConstraintApplyImpulseImpl)applyImpulse, - (cpConstraintGetImpulseImpl)getImpulse, -}; - -cpGearJoint * -cpGearJointAlloc(void) -{ - return (cpGearJoint *)cpcalloc(1, sizeof(cpGearJoint)); -} - -cpGearJoint * -cpGearJointInit(cpGearJoint *joint, cpBody *a, cpBody *b, cpFloat phase, cpFloat ratio) -{ - cpConstraintInit((cpConstraint *)joint, &klass, a, b); - - joint->phase = phase; - joint->ratio = ratio; - joint->ratio_inv = 1.0f/ratio; - - joint->jAcc = 0.0f; - - return joint; -} - -cpConstraint * -cpGearJointNew(cpBody *a, cpBody *b, cpFloat phase, cpFloat ratio) -{ - return (cpConstraint *)cpGearJointInit(cpGearJointAlloc(), a, b, phase, ratio); -} - -cpBool -cpConstraintIsGearJoint(const cpConstraint *constraint) -{ - return (constraint->klass == &klass); -} - -cpFloat -cpGearJointGetPhase(const cpConstraint *constraint) -{ - cpAssertHard(cpConstraintIsGearJoint(constraint), "Constraint is not a ratchet joint."); - return ((cpGearJoint *)constraint)->phase; -} - -void -cpGearJointSetPhase(cpConstraint *constraint, cpFloat phase) -{ - cpAssertHard(cpConstraintIsGearJoint(constraint), "Constraint is not a ratchet joint."); - cpConstraintActivateBodies(constraint); - ((cpGearJoint *)constraint)->phase = phase; -} - -cpFloat -cpGearJointGetRatio(const cpConstraint *constraint) -{ - cpAssertHard(cpConstraintIsGearJoint(constraint), "Constraint is not a ratchet joint."); - return ((cpGearJoint *)constraint)->ratio; -} - -void -cpGearJointSetRatio(cpConstraint *constraint, cpFloat ratio) -{ - cpAssertHard(cpConstraintIsGearJoint(constraint), "Constraint is not a ratchet joint."); - cpConstraintActivateBodies(constraint); - ((cpGearJoint *)constraint)->ratio = ratio; - ((cpGearJoint *)constraint)->ratio_inv = 1.0f/ratio; -} diff --git a/apecs-physics/Chipmunk2D/src/cpGrooveJoint.c b/apecs-physics/Chipmunk2D/src/cpGrooveJoint.c deleted file mode 100644 index 2a08cbc..0000000 --- a/apecs-physics/Chipmunk2D/src/cpGrooveJoint.c +++ /dev/null @@ -1,197 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "chipmunk_private.h" - -static void -preStep(cpGrooveJoint *joint, cpFloat dt) -{ - cpBody *a = joint->constraint.a; - cpBody *b = joint->constraint.b; - - // calculate endpoints in worldspace - cpVect ta = cpTransformPoint(a->transform, joint->grv_a); - cpVect tb = cpTransformPoint(a->transform, joint->grv_b); - - // calculate axis - cpVect n = cpTransformVect(a->transform, joint->grv_n); - cpFloat d = cpvdot(ta, n); - - joint->grv_tn = n; - joint->r2 = cpTransformVect(b->transform, cpvsub(joint->anchorB, b->cog)); - - // calculate tangential distance along the axis of r2 - cpFloat td = cpvcross(cpvadd(b->p, joint->r2), n); - // calculate clamping factor and r2 - if(td <= cpvcross(ta, n)){ - joint->clamp = 1.0f; - joint->r1 = cpvsub(ta, a->p); - } else if(td >= cpvcross(tb, n)){ - joint->clamp = -1.0f; - joint->r1 = cpvsub(tb, a->p); - } else { - joint->clamp = 0.0f; - joint->r1 = cpvsub(cpvadd(cpvmult(cpvperp(n), -td), cpvmult(n, d)), a->p); - } - - // Calculate mass tensor - joint->k = k_tensor(a, b, joint->r1, joint->r2); - - // calculate bias velocity - cpVect delta = cpvsub(cpvadd(b->p, joint->r2), cpvadd(a->p, joint->r1)); - joint->bias = cpvclamp(cpvmult(delta, -bias_coef(joint->constraint.errorBias, dt)/dt), joint->constraint.maxBias); -} - -static void -applyCachedImpulse(cpGrooveJoint *joint, cpFloat dt_coef) -{ - cpBody *a = joint->constraint.a; - cpBody *b = joint->constraint.b; - - apply_impulses(a, b, joint->r1, joint->r2, cpvmult(joint->jAcc, dt_coef)); -} - -static inline cpVect -grooveConstrain(cpGrooveJoint *joint, cpVect j, cpFloat dt){ - cpVect n = joint->grv_tn; - cpVect jClamp = (joint->clamp*cpvcross(j, n) > 0.0f) ? j : cpvproject(j, n); - return cpvclamp(jClamp, joint->constraint.maxForce*dt); -} - -static void -applyImpulse(cpGrooveJoint *joint, cpFloat dt) -{ - cpBody *a = joint->constraint.a; - cpBody *b = joint->constraint.b; - - cpVect r1 = joint->r1; - cpVect r2 = joint->r2; - - // compute impulse - cpVect vr = relative_velocity(a, b, r1, r2); - - cpVect j = cpMat2x2Transform(joint->k, cpvsub(joint->bias, vr)); - cpVect jOld = joint->jAcc; - joint->jAcc = grooveConstrain(joint, cpvadd(jOld, j), dt); - j = cpvsub(joint->jAcc, jOld); - - // apply impulse - apply_impulses(a, b, joint->r1, joint->r2, j); -} - -static cpFloat -getImpulse(cpGrooveJoint *joint) -{ - return cpvlength(joint->jAcc); -} - -static const cpConstraintClass klass = { - (cpConstraintPreStepImpl)preStep, - (cpConstraintApplyCachedImpulseImpl)applyCachedImpulse, - (cpConstraintApplyImpulseImpl)applyImpulse, - (cpConstraintGetImpulseImpl)getImpulse, -}; - -cpGrooveJoint * -cpGrooveJointAlloc(void) -{ - return (cpGrooveJoint *)cpcalloc(1, sizeof(cpGrooveJoint)); -} - -cpGrooveJoint * -cpGrooveJointInit(cpGrooveJoint *joint, cpBody *a, cpBody *b, cpVect groove_a, cpVect groove_b, cpVect anchorB) -{ - cpConstraintInit((cpConstraint *)joint, &klass, a, b); - - joint->grv_a = groove_a; - joint->grv_b = groove_b; - joint->grv_n = cpvperp(cpvnormalize(cpvsub(groove_b, groove_a))); - joint->anchorB = anchorB; - - joint->jAcc = cpvzero; - - return joint; -} - -cpConstraint * -cpGrooveJointNew(cpBody *a, cpBody *b, cpVect groove_a, cpVect groove_b, cpVect anchorB) -{ - return (cpConstraint *)cpGrooveJointInit(cpGrooveJointAlloc(), a, b, groove_a, groove_b, anchorB); -} - -cpBool -cpConstraintIsGrooveJoint(const cpConstraint *constraint) -{ - return (constraint->klass == &klass); -} - -cpVect -cpGrooveJointGetGrooveA(const cpConstraint *constraint) -{ - cpAssertHard(cpConstraintIsGrooveJoint(constraint), "Constraint is not a groove joint."); - return ((cpGrooveJoint *)constraint)->grv_a; -} - -void -cpGrooveJointSetGrooveA(cpConstraint *constraint, cpVect value) -{ - cpAssertHard(cpConstraintIsGrooveJoint(constraint), "Constraint is not a groove joint."); - cpGrooveJoint *g = (cpGrooveJoint *)constraint; - - g->grv_a = value; - g->grv_n = cpvperp(cpvnormalize(cpvsub(g->grv_b, value))); - - cpConstraintActivateBodies(constraint); -} - -cpVect -cpGrooveJointGetGrooveB(const cpConstraint *constraint) -{ - cpAssertHard(cpConstraintIsGrooveJoint(constraint), "Constraint is not a groove joint."); - return ((cpGrooveJoint *)constraint)->grv_b; -} - -void -cpGrooveJointSetGrooveB(cpConstraint *constraint, cpVect value) -{ - cpAssertHard(cpConstraintIsGrooveJoint(constraint), "Constraint is not a groove joint."); - cpGrooveJoint *g = (cpGrooveJoint *)constraint; - - g->grv_b = value; - g->grv_n = cpvperp(cpvnormalize(cpvsub(value, g->grv_a))); - - cpConstraintActivateBodies(constraint); -} - -cpVect -cpGrooveJointGetAnchorB(const cpConstraint *constraint) -{ - cpAssertHard(cpConstraintIsGrooveJoint(constraint), "Constraint is not a groove joint."); - return ((cpGrooveJoint *)constraint)->anchorB; -} - -void -cpGrooveJointSetAnchorB(cpConstraint *constraint, cpVect anchorB) -{ - cpAssertHard(cpConstraintIsGrooveJoint(constraint), "Constraint is not a groove joint."); - cpConstraintActivateBodies(constraint); - ((cpGrooveJoint *)constraint)->anchorB = anchorB; -} diff --git a/apecs-physics/Chipmunk2D/src/cpHashSet.c b/apecs-physics/Chipmunk2D/src/cpHashSet.c deleted file mode 100644 index 5820035..0000000 --- a/apecs-physics/Chipmunk2D/src/cpHashSet.c +++ /dev/null @@ -1,253 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "chipmunk_private.h" -#include "prime.h" - -typedef struct cpHashSetBin { - void *elt; - cpHashValue hash; - struct cpHashSetBin *next; -} cpHashSetBin; - -struct cpHashSet { - unsigned int entries, size; - - cpHashSetEqlFunc eql; - void *default_value; - - cpHashSetBin **table; - cpHashSetBin *pooledBins; - - cpArray *allocatedBuffers; -}; - -void -cpHashSetFree(cpHashSet *set) -{ - if(set){ - cpfree(set->table); - - cpArrayFreeEach(set->allocatedBuffers, cpfree); - cpArrayFree(set->allocatedBuffers); - - cpfree(set); - } -} - -cpHashSet * -cpHashSetNew(int size, cpHashSetEqlFunc eqlFunc) -{ - cpHashSet *set = (cpHashSet *)cpcalloc(1, sizeof(cpHashSet)); - - set->size = next_prime(size); - set->entries = 0; - - set->eql = eqlFunc; - set->default_value = NULL; - - set->table = (cpHashSetBin **)cpcalloc(set->size, sizeof(cpHashSetBin *)); - set->pooledBins = NULL; - - set->allocatedBuffers = cpArrayNew(0); - - return set; -} - -void -cpHashSetSetDefaultValue(cpHashSet *set, void *default_value) -{ - set->default_value = default_value; -} - -static int -setIsFull(cpHashSet *set) -{ - return (set->entries >= set->size); -} - -static void -cpHashSetResize(cpHashSet *set) -{ - // Get the next approximate doubled prime. - unsigned int newSize = next_prime(set->size + 1); - // Allocate a new table. - cpHashSetBin **newTable = (cpHashSetBin **)cpcalloc(newSize, sizeof(cpHashSetBin *)); - - // Iterate over the chains. - for(unsigned int i=0; isize; i++){ - // Rehash the bins into the new table. - cpHashSetBin *bin = set->table[i]; - while(bin){ - cpHashSetBin *next = bin->next; - - cpHashValue idx = bin->hash%newSize; - bin->next = newTable[idx]; - newTable[idx] = bin; - - bin = next; - } - } - - cpfree(set->table); - - set->table = newTable; - set->size = newSize; -} - -static inline void -recycleBin(cpHashSet *set, cpHashSetBin *bin) -{ - bin->next = set->pooledBins; - set->pooledBins = bin; - bin->elt = NULL; -} - -static cpHashSetBin * -getUnusedBin(cpHashSet *set) -{ - cpHashSetBin *bin = set->pooledBins; - - if(bin){ - set->pooledBins = bin->next; - return bin; - } else { - // Pool is exhausted, make more - int count = CP_BUFFER_BYTES/sizeof(cpHashSetBin); - cpAssertHard(count, "Internal Error: Buffer size is too small."); - - cpHashSetBin *buffer = (cpHashSetBin *)cpcalloc(1, CP_BUFFER_BYTES); - cpArrayPush(set->allocatedBuffers, buffer); - - // push all but the first one, return it instead - for(int i=1; ientries; -} - -void * -cpHashSetInsert(cpHashSet *set, cpHashValue hash, void *ptr, cpHashSetTransFunc trans, void *data) -{ - cpHashValue idx = hash%set->size; - - // Find the bin with the matching element. - cpHashSetBin *bin = set->table[idx]; - while(bin && !set->eql(ptr, bin->elt)) - bin = bin->next; - - // Create it if necessary. - if(!bin){ - bin = getUnusedBin(set); - bin->hash = hash; - bin->elt = (trans ? trans(ptr, data) : data); - - bin->next = set->table[idx]; - set->table[idx] = bin; - - set->entries++; - if(setIsFull(set)) cpHashSetResize(set); - } - - return bin->elt; -} - -void * -cpHashSetRemove(cpHashSet *set, cpHashValue hash, void *ptr) -{ - cpHashValue idx = hash%set->size; - - cpHashSetBin **prev_ptr = &set->table[idx]; - cpHashSetBin *bin = set->table[idx]; - - // Find the bin - while(bin && !set->eql(ptr, bin->elt)){ - prev_ptr = &bin->next; - bin = bin->next; - } - - // Remove it if it exists. - if(bin){ - // Update the previous linked list pointer - (*prev_ptr) = bin->next; - set->entries--; - - void *elt = bin->elt; - recycleBin(set, bin); - - return elt; - } - - return NULL; -} - -void * -cpHashSetFind(cpHashSet *set, cpHashValue hash, void *ptr) -{ - cpHashValue idx = hash%set->size; - cpHashSetBin *bin = set->table[idx]; - while(bin && !set->eql(ptr, bin->elt)) - bin = bin->next; - - return (bin ? bin->elt : set->default_value); -} - -void -cpHashSetEach(cpHashSet *set, cpHashSetIteratorFunc func, void *data) -{ - for(unsigned int i=0; isize; i++){ - cpHashSetBin *bin = set->table[i]; - while(bin){ - cpHashSetBin *next = bin->next; - func(bin->elt, data); - bin = next; - } - } -} - -void -cpHashSetFilter(cpHashSet *set, cpHashSetFilterFunc func, void *data) -{ - for(unsigned int i=0; isize; i++){ - // The rest works similarly to cpHashSetRemove() above. - cpHashSetBin **prev_ptr = &set->table[i]; - cpHashSetBin *bin = set->table[i]; - while(bin){ - cpHashSetBin *next = bin->next; - - if(func(bin->elt, data)){ - prev_ptr = &bin->next; - } else { - (*prev_ptr) = next; - - set->entries--; - recycleBin(set, bin); - } - - bin = next; - } - } -} diff --git a/apecs-physics/Chipmunk2D/src/cpHastySpace.c b/apecs-physics/Chipmunk2D/src/cpHastySpace.c deleted file mode 100644 index 332167e..0000000 --- a/apecs-physics/Chipmunk2D/src/cpHastySpace.c +++ /dev/null @@ -1,694 +0,0 @@ -// Copyright 2013 Howling Moon Software. All rights reserved. -// See http://chipmunk2d.net/legal.php for more information. - -#include -#include - -//TODO: Move all the thread stuff to another file - -//#include -#ifndef _WIN32 -#include -#include -#else -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif - -#ifndef NOMINMAX -#define NOMINMAX -#endif - -#include // _beginthreadex -#include - -#ifndef ETIMEDOUT -#define ETIMEDOUT 1 -#endif - -// Simple pthread implementation for Windows -// Made from scratch to avoid the LGPL licence from pthread-win32 -enum { - SIGNAL = 0, - BROADCAST = 1, - MAX_EVENTS = 2 -}; - -typedef HANDLE pthread_t; -typedef struct -{ - // Based on http://www.cs.wustl.edu/~schmidt/win32-cv-1.html since Windows has no condition variable until NT6 - UINT waiters_count; - // Count of the number of waiters. - - CRITICAL_SECTION waiters_count_lock; - // Serialize access to . - - HANDLE events[MAX_EVENTS]; -} pthread_cond_t; -typedef CRITICAL_SECTION pthread_mutex_t; - -typedef struct {} pthread_condattr_t; // Dummy; - -int pthread_cond_destroy(pthread_cond_t* cv) -{ - CloseHandle(cv->events[BROADCAST]); - CloseHandle(cv->events[SIGNAL]); - - DeleteCriticalSection(&cv->waiters_count_lock); - - return 0; -} - -int pthread_cond_init(pthread_cond_t* cv, const pthread_condattr_t* attr) -{ - // Initialize the count to 0. - cv->waiters_count = 0; - - // Create an auto-reset event. - cv->events[SIGNAL] = CreateEvent(NULL, // no security - FALSE, // auto-reset event - FALSE, // non-signaled initially - NULL); // unnamed - - // Create a manual-reset event. - cv->events[BROADCAST] = CreateEvent(NULL, // no security - TRUE, // manual-reset - FALSE, // non-signaled initially - NULL); // unnamed - - InitializeCriticalSection(&cv->waiters_count_lock); - - return 0; -} - -int pthread_cond_broadcast(pthread_cond_t *cv) -{ - // Avoid race conditions. - EnterCriticalSection(&cv->waiters_count_lock); - int have_waiters = cv->waiters_count > 0; - LeaveCriticalSection(&cv->waiters_count_lock); - - if (have_waiters) - SetEvent(cv->events[BROADCAST]); - - return 0; -} - -int pthread_cond_signal(pthread_cond_t* cv) -{ - // Avoid race conditions. - EnterCriticalSection(&cv->waiters_count_lock); - int have_waiters = cv->waiters_count > 0; - LeaveCriticalSection(&cv->waiters_count_lock); - - if (have_waiters) - SetEvent(cv->events[SIGNAL]); - - return 0; -} - -int pthread_cond_wait(pthread_cond_t* cv, pthread_mutex_t* external_mutex) -{ - // Avoid race conditions. - EnterCriticalSection(&cv->waiters_count_lock); - cv->waiters_count++; - LeaveCriticalSection(&cv->waiters_count_lock); - - // It's ok to release the here since Win32 - // manual-reset events maintain state when used with - // . This avoids the "lost wakeup" bug... - LeaveCriticalSection(external_mutex); - - // Wait for either event to become signaled due to - // being called or being called. - int result = WaitForMultipleObjects(2, cv->events, FALSE, INFINITE); - - EnterCriticalSection(&cv->waiters_count_lock); - cv->waiters_count--; - int last_waiter = - result == WAIT_OBJECT_0 + BROADCAST - && cv->waiters_count == 0; - LeaveCriticalSection(&cv->waiters_count_lock); - - // Some thread called . - if (last_waiter) - // We're the last waiter to be notified or to stop waiting, so - // reset the manual event. - ResetEvent(cv->events[BROADCAST]); - - // Reacquire the . - EnterCriticalSection(external_mutex); - - return result == WAIT_TIMEOUT ? ETIMEDOUT : 0; -} - -typedef struct {} pthread_mutexattr_t; //< Dummy - -int pthread_mutex_init(pthread_mutex_t* mutex, const pthread_mutexattr_t* attr) -{ - InitializeCriticalSection(mutex); - return 0; -} - -int pthread_mutex_destroy(pthread_mutex_t* mutex) -{ - DeleteCriticalSection(mutex); - return 0; -} - -int pthread_mutex_lock(pthread_mutex_t* mutex) -{ - EnterCriticalSection(mutex); - return 0; -} - -int pthread_mutex_unlock(pthread_mutex_t* mutex) -{ - LeaveCriticalSection(mutex); - return 0; -} - -typedef struct {} pthread_attr_t; - -typedef struct -{ - void *(*start_routine) (void *); - void* arg; -} pthread_internal_thread; - -unsigned int __stdcall ThreadProc(void* userdata) -{ - pthread_internal_thread* ud = (pthread_internal_thread*) userdata; - ud->start_routine(ud->arg); - - free(ud); - - return 0; -} - -int pthread_create(pthread_t* thread, const pthread_attr_t* attr, void *(*start_routine) (void *), void *arg) -{ - pthread_internal_thread* ud = (pthread_internal_thread*) malloc(sizeof(pthread_internal_thread)); - ud->start_routine = start_routine; - ud->arg = arg; - - *thread = (HANDLE) (_beginthreadex(NULL, 0, &ThreadProc, ud, 0, NULL)); - if (!*thread) - return 1; - - return 0; -} - -int pthread_join(pthread_t thread, void **value_ptr) -{ - WaitForSingleObject(thread, INFINITE); - CloseHandle(thread); - - return 0; -} - -#endif - -#include "chipmunk_private.h" -#include "cpHastySpace.h" - - -//MARK: ARM NEON Solver - -#if __ARM_NEON__ -#include - -// Tested and known to work fine with Clang 3.0 and GCC 4.2 -// Doesn't work with Clang 1.6, and I have no idea why. -#if defined(__clang_major__) && __clang_major__ < 3 - #error Compiler not supported. -#endif - -#if CP_USE_DOUBLES - #if !__arm64 - #error Cannot use CP_USE_DOUBLES on 32 bit ARM. - #endif - - typedef float64_t cpFloat_t; - typedef float64x2_t cpFloatx2_t; - #define vld vld1q_f64 - #define vdup_n vdupq_n_f64 - #define vst vst1q_f64 - #define vst_lane vst1q_lane_f64 - #define vadd vaddq_f64 - #define vsub vsubq_f64 - #define vpadd vpaddq_f64 - #define vmul vmulq_f64 - #define vmul_n vmulq_n_f64 - #define vneg vnegq_f64 - #define vget_lane vgetq_lane_f64 - #define vset_lane vsetq_lane_f64 - #define vmin vminq_f64 - #define vmax vmaxq_f64 - #define vrev(__a) __builtin_shufflevector(__a, __a, 1, 0) -#else - typedef float32_t cpFloat_t; - typedef float32x2_t cpFloatx2_t; - #define vld vld1_f32 - #define vdup_n vdup_n_f32 - #define vst vst1_f32 - #define vst_lane vst1_lane_f32 - #define vadd vadd_f32 - #define vsub vsub_f32 - #define vpadd vpadd_f32 - #define vmul vmul_f32 - #define vmul_n vmul_n_f32 - #define vneg vneg_f32 - #define vget_lane vget_lane_f32 - #define vset_lane vset_lane_f32 - #define vmin vmin_f32 - #define vmax vmax_f32 - #define vrev vrev64_f32 -#endif - -// TODO could probably do better here, maybe using vcreate? -// especially for the constants -// Maybe use the {} notation for GCC/Clang? -static inline cpFloatx2_t -vmake(cpFloat_t x, cpFloat_t y) -{ -// cpFloatx2_t v = {}; -// v = vset_lane(x, v, 0); -// v = vset_lane(y, v, 1); -// -// return v; - - // This might not be super compatible, but all the NEON headers use it... - return (cpFloatx2_t){x, y}; -} - -static void -cpArbiterApplyImpulse_NEON(cpArbiter *arb) -{ - cpBody *a = arb->body_a; - cpBody *b = arb->body_b; - cpFloatx2_t surface_vr = vld((cpFloat_t *)&arb->surface_vr); - cpFloatx2_t n = vld((cpFloat_t *)&arb->n); - cpFloat_t friction = arb->u; - - int numContacts = arb->count; - struct cpContact *contacts = arb->contacts; - for(int i=0; ir1); - cpFloatx2_t r2 = vld((cpFloat_t *)&con->r2); - - cpFloatx2_t perp = vmake(-1.0, 1.0); - cpFloatx2_t r1p = vmul(vrev(r1), perp); - cpFloatx2_t r2p = vmul(vrev(r2), perp); - - cpFloatx2_t vBias_a = vld((cpFloat_t *)&a->v_bias); - cpFloatx2_t vBias_b = vld((cpFloat_t *)&b->v_bias); - cpFloatx2_t wBias = vmake(a->w_bias, b->w_bias); - - cpFloatx2_t vb1 = vadd(vBias_a, vmul_n(r1p, vget_lane(wBias, 0))); - cpFloatx2_t vb2 = vadd(vBias_b, vmul_n(r2p, vget_lane(wBias, 1))); - cpFloatx2_t vbr = vsub(vb2, vb1); - - cpFloatx2_t v_a = vld((cpFloat_t *)&a->v); - cpFloatx2_t v_b = vld((cpFloat_t *)&b->v); - cpFloatx2_t w = vmake(a->w, b->w); - cpFloatx2_t v1 = vadd(v_a, vmul_n(r1p, vget_lane(w, 0))); - cpFloatx2_t v2 = vadd(v_b, vmul_n(r2p, vget_lane(w, 1))); - cpFloatx2_t vr = vsub(v2, v1); - - cpFloatx2_t vbn_vrn = vpadd(vmul(vbr, n), vmul(vr, n)); - - cpFloatx2_t v_offset = vmake(con->bias, -con->bounce); - cpFloatx2_t jOld = vmake(con->jBias, con->jnAcc); - cpFloatx2_t jbn_jn = vmul_n(vsub(v_offset, vbn_vrn), con->nMass); - jbn_jn = vmax(vadd(jOld, jbn_jn), vdup_n(0.0)); - cpFloatx2_t jApply = vsub(jbn_jn, jOld); - - cpFloatx2_t t = vmul(vrev(n), perp); - cpFloatx2_t vrt_tmp = vmul(vadd(vr, surface_vr), t); - cpFloatx2_t vrt = vpadd(vrt_tmp, vrt_tmp); - - cpFloatx2_t jtOld = {}; jtOld = vset_lane(con->jtAcc, jtOld, 0); - cpFloatx2_t jtMax = vrev(vmul_n(jbn_jn, friction)); - cpFloatx2_t jt = vmul_n(vrt, -con->tMass); - jt = vmax(vneg(jtMax), vmin(vadd(jtOld, jt), jtMax)); - cpFloatx2_t jtApply = vsub(jt, jtOld); - - cpFloatx2_t i_inv = vmake(-a->i_inv, b->i_inv); - cpFloatx2_t nperp = vmake(1.0, -1.0); - - cpFloatx2_t jBias = vmul_n(n, vget_lane(jApply, 0)); - cpFloatx2_t jBiasCross = vmul(vrev(jBias), nperp); - cpFloatx2_t biasCrosses = vpadd(vmul(r1, jBiasCross), vmul(r2, jBiasCross)); - wBias = vadd(wBias, vmul(i_inv, biasCrosses)); - - vBias_a = vsub(vBias_a, vmul_n(jBias, a->m_inv)); - vBias_b = vadd(vBias_b, vmul_n(jBias, b->m_inv)); - - cpFloatx2_t j = vadd(vmul_n(n, vget_lane(jApply, 1)), vmul_n(t, vget_lane(jtApply, 0))); - cpFloatx2_t jCross = vmul(vrev(j), nperp); - cpFloatx2_t crosses = vpadd(vmul(r1, jCross), vmul(r2, jCross)); - w = vadd(w, vmul(i_inv, crosses)); - - v_a = vsub(v_a, vmul_n(j, a->m_inv)); - v_b = vadd(v_b, vmul_n(j, b->m_inv)); - - // TODO would moving these earlier help pipeline them better? - vst((cpFloat_t *)&a->v_bias, vBias_a); - vst((cpFloat_t *)&b->v_bias, vBias_b); - vst_lane((cpFloat_t *)&a->w_bias, wBias, 0); - vst_lane((cpFloat_t *)&b->w_bias, wBias, 1); - - vst((cpFloat_t *)&a->v, v_a); - vst((cpFloat_t *)&b->v, v_b); - vst_lane((cpFloat_t *)&a->w, w, 0); - vst_lane((cpFloat_t *)&b->w, w, 1); - - vst_lane((cpFloat_t *)&con->jBias, jbn_jn, 0); - vst_lane((cpFloat_t *)&con->jnAcc, jbn_jn, 1); - vst_lane((cpFloat_t *)&con->jtAcc, jt, 0); - } -} - -#endif - -//MARK: PThreads - -// Right now using more than 2 threads probably wont help your performance any. -// If you are using a ridiculous number of iterations it could help though. -#define MAX_THREADS 2 - -struct ThreadContext { - pthread_t thread; - cpHastySpace *space; - unsigned long thread_num; -}; - -typedef void (*cpHastySpaceWorkFunction)(cpSpace *space, unsigned long worker, unsigned long worker_count); - -struct cpHastySpace { - cpSpace space; - - // Number of worker threads (including the main thread) - unsigned long num_threads; - - // Number of worker threads currently executing. (also including the main thread) - unsigned long num_working; - - // Number of constraints (plus contacts) that must exist per step to start the worker threads. - unsigned long constraint_count_threshold; - - pthread_mutex_t mutex; - pthread_cond_t cond_work, cond_resume; - - // Work function to invoke. - cpHastySpaceWorkFunction work; - - struct ThreadContext workers[MAX_THREADS - 1]; -}; - -static void * -WorkerThreadLoop(struct ThreadContext *context) -{ - cpHastySpace *hasty = context->space; - - unsigned long thread = context->thread_num; - unsigned long num_threads = hasty->num_threads; - - for(;;){ - pthread_mutex_lock(&hasty->mutex); { - if(--hasty->num_working == 0){ - pthread_cond_signal(&hasty->cond_resume); - } - - pthread_cond_wait(&hasty->cond_work, &hasty->mutex); - } pthread_mutex_unlock(&hasty->mutex); - - cpHastySpaceWorkFunction func = hasty->work; - if(func){ - hasty->work(&hasty->space, thread, num_threads); - } else { - break; - } - } - - return NULL; -} - -static void -RunWorkers(cpHastySpace *hasty, cpHastySpaceWorkFunction func) -{ - hasty->num_working = hasty->num_threads - 1; - hasty->work = func; - - if(hasty->num_working > 0){ - pthread_mutex_lock(&hasty->mutex); { - pthread_cond_broadcast(&hasty->cond_work); - } pthread_mutex_unlock(&hasty->mutex); - - func((cpSpace *)hasty, 0, hasty->num_threads); - - pthread_mutex_lock(&hasty->mutex); { - if(hasty->num_working > 0){ - pthread_cond_wait(&hasty->cond_resume, &hasty->mutex); - } - } pthread_mutex_unlock(&hasty->mutex); - } else { - func((cpSpace *)hasty, 0, hasty->num_threads); - } - - hasty->work = NULL; -} - -static void -Solver(cpSpace *space, unsigned long worker, unsigned long worker_count) -{ - cpArray *constraints = space->constraints; - cpArray *arbiters = space->arbiters; - - cpFloat dt = space->curr_dt; - unsigned long iterations = (space->iterations + worker_count - 1)/worker_count; - - for(unsigned long i=0; inum; j++){ - cpArbiter *arb = (cpArbiter *)arbiters->arr[j]; - #ifdef __ARM_NEON__ - cpArbiterApplyImpulse_NEON(arb); - #else - cpArbiterApplyImpulse(arb); - #endif - } - - for(int j=0; jnum; j++){ - cpConstraint *constraint = (cpConstraint *)constraints->arr[j]; - constraint->klass->applyImpulse(constraint, dt); - } - } -} - -//MARK: Thread Management Functions - -static void -HaltThreads(cpHastySpace *hasty) -{ - pthread_mutex_t *mutex = &hasty->mutex; - pthread_mutex_lock(mutex); { - hasty->work = NULL; // NULL work function means break and exit - pthread_cond_broadcast(&hasty->cond_work); - } pthread_mutex_unlock(mutex); - - for(unsigned long i=0; i<(hasty->num_threads-1); i++){ - pthread_join(hasty->workers[i].thread, NULL); - } -} - -void -cpHastySpaceSetThreads(cpSpace *space, unsigned long threads) -{ -#if TARGET_IPHONE_SIMULATOR == 1 - // Individual values appear to be written non-atomically when compiled as debug for the simulator. - // No idea why, so threads are disabled. - threads = 1; -#endif - - cpHastySpace *hasty = (cpHastySpace *)space; - HaltThreads(hasty); - -#ifdef __APPLE__ - if(threads == 0){ - size_t size = sizeof(threads); - sysctlbyname("hw.ncpu", &threads, &size, NULL, 0); - } -#else - if(threads == 0) threads = 1; -#endif - - hasty->num_threads = (threads < MAX_THREADS ? threads : MAX_THREADS); - hasty->num_working = hasty->num_threads - 1; - - // Create the worker threads and wait for them to signal ready. - if(hasty->num_working > 0){ - pthread_mutex_lock(&hasty->mutex); - for(unsigned long i=0; i<(hasty->num_threads-1); i++){ - hasty->workers[i].space = hasty; - hasty->workers[i].thread_num = i + 1; - - pthread_create(&hasty->workers[i].thread, NULL, (void*(*)(void*))WorkerThreadLoop, &hasty->workers[i]); - } - - pthread_cond_wait(&hasty->cond_resume, &hasty->mutex); - pthread_mutex_unlock(&hasty->mutex); - } -} - -unsigned long -cpHastySpaceGetThreads(cpSpace *space) -{ - return ((cpHastySpace *)space)->num_threads; -} - -//MARK: Overriden cpSpace Functions. - -cpSpace * -cpHastySpaceNew(void) -{ - cpHastySpace *hasty = (cpHastySpace *)cpcalloc(1, sizeof(cpHastySpace)); - cpSpaceInit((cpSpace *)hasty); - - pthread_mutex_init(&hasty->mutex, NULL); - pthread_cond_init(&hasty->cond_work, NULL); - pthread_cond_init(&hasty->cond_resume, NULL); - - // TODO magic number, should test this more thoroughly. - hasty->constraint_count_threshold = 50; - - // Default to 1 thread for determinism. - hasty->num_threads = 1; - cpHastySpaceSetThreads((cpSpace *)hasty, 1); - - return (cpSpace *)hasty; -} - -void -cpHastySpaceFree(cpSpace *space) -{ - cpHastySpace *hasty = (cpHastySpace *)space; - - HaltThreads(hasty); - - pthread_mutex_destroy(&hasty->mutex); - pthread_cond_destroy(&hasty->cond_work); - pthread_cond_destroy(&hasty->cond_resume); - - cpSpaceFree(space); -} - -void -cpHastySpaceStep(cpSpace *space, cpFloat dt) -{ - // don't step if the timestep is 0! - if(dt == 0.0f) return; - - space->stamp++; - - cpFloat prev_dt = space->curr_dt; - space->curr_dt = dt; - - cpArray *bodies = space->dynamicBodies; - cpArray *constraints = space->constraints; - cpArray *arbiters = space->arbiters; - - // Reset and empty the arbiter list. - for(int i=0; inum; i++){ - cpArbiter *arb = (cpArbiter *)arbiters->arr[i]; - arb->state = CP_ARBITER_STATE_NORMAL; - - // If both bodies are awake, unthread the arbiter from the contact graph. - if(!cpBodyIsSleeping(arb->body_a) && !cpBodyIsSleeping(arb->body_b)){ - cpArbiterUnthread(arb); - } - } - arbiters->num = 0; - - cpSpaceLock(space); { - // Integrate positions - for(int i=0; inum; i++){ - cpBody *body = (cpBody *)bodies->arr[i]; - body->position_func(body, dt); - } - - // Find colliding pairs. - cpSpacePushFreshContactBuffer(space); - cpSpatialIndexEach(space->dynamicShapes, (cpSpatialIndexIteratorFunc)cpShapeUpdateFunc, NULL); - cpSpatialIndexReindexQuery(space->dynamicShapes, (cpSpatialIndexQueryFunc)cpSpaceCollideShapes, space); - } cpSpaceUnlock(space, cpFalse); - - // Rebuild the contact graph (and detect sleeping components if sleeping is enabled) - cpSpaceProcessComponents(space, dt); - - cpSpaceLock(space); { - // Clear out old cached arbiters and call separate callbacks - cpHashSetFilter(space->cachedArbiters, (cpHashSetFilterFunc)cpSpaceArbiterSetFilter, space); - - // Prestep the arbiters and constraints. - cpFloat slop = space->collisionSlop; - cpFloat biasCoef = 1.0f - cpfpow(space->collisionBias, dt); - for(int i=0; inum; i++){ - cpArbiterPreStep((cpArbiter *)arbiters->arr[i], dt, slop, biasCoef); - } - - for(int i=0; inum; i++){ - cpConstraint *constraint = (cpConstraint *)constraints->arr[i]; - - cpConstraintPreSolveFunc preSolve = constraint->preSolve; - if(preSolve) preSolve(constraint, space); - - constraint->klass->preStep(constraint, dt); - } - - // Integrate velocities. - cpFloat damping = cpfpow(space->damping, dt); - cpVect gravity = space->gravity; - for(int i=0; inum; i++){ - cpBody *body = (cpBody *)bodies->arr[i]; - body->velocity_func(body, gravity, damping, dt); - } - - // Apply cached impulses - cpFloat dt_coef = (prev_dt == 0.0f ? 0.0f : dt/prev_dt); - for(int i=0; inum; i++){ - cpArbiterApplyCachedImpulse((cpArbiter *)arbiters->arr[i], dt_coef); - } - - for(int i=0; inum; i++){ - cpConstraint *constraint = (cpConstraint *)constraints->arr[i]; - constraint->klass->applyCachedImpulse(constraint, dt_coef); - } - - // Run the impulse solver. - cpHastySpace *hasty = (cpHastySpace *)space; - if((unsigned long)(arbiters->num + constraints->num) > hasty->constraint_count_threshold){ - RunWorkers(hasty, Solver); - } else { - Solver(space, 0, 1); - } - - // Run the constraint post-solve callbacks - for(int i=0; inum; i++){ - cpConstraint *constraint = (cpConstraint *)constraints->arr[i]; - - cpConstraintPostSolveFunc postSolve = constraint->postSolve; - if(postSolve) postSolve(constraint, space); - } - - // run the post-solve callbacks - for(int i=0; inum; i++){ - cpArbiter *arb = (cpArbiter *) arbiters->arr[i]; - - cpCollisionHandler *handler = arb->handler; - handler->postSolveFunc(arb, space, handler->userData); - } - } cpSpaceUnlock(space, cpTrue); -} diff --git a/apecs-physics/Chipmunk2D/src/cpMarch.c b/apecs-physics/Chipmunk2D/src/cpMarch.c deleted file mode 100644 index 6573b83..0000000 --- a/apecs-physics/Chipmunk2D/src/cpMarch.c +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright 2013 Howling Moon Software. All rights reserved. -// See http://chipmunk2d.net/legal.php for more information. - -#include -#include -#include - -#include "chipmunk.h" -#include "cpMarch.h" - - -typedef void (*cpMarchCellFunc)( - cpFloat t, cpFloat a, cpFloat b, cpFloat c, cpFloat d, - cpFloat x0, cpFloat x1, cpFloat y0, cpFloat y1, - cpMarchSegmentFunc segment, void *segment_data -); - -// The looping and sample caching code is shared between cpMarchHard() and cpMarchSoft(). -static void -cpMarchCells( - cpBB bb, unsigned long x_samples, unsigned long y_samples, cpFloat t, - cpMarchSegmentFunc segment, void *segment_data, - cpMarchSampleFunc sample, void *sample_data, - cpMarchCellFunc cell -){ - cpFloat x_denom = 1.0/(cpFloat)(x_samples - 1); - cpFloat y_denom = 1.0/(cpFloat)(y_samples - 1); - - // TODO range assertions and short circuit for 0 sized windows. - - // Keep a copy of the previous row to avoid double lookups. - cpFloat *buffer = (cpFloat *)cpcalloc(x_samples, sizeof(cpFloat)); - for(unsigned long i=0; it)<<0 | (b>t)<<1 | (c>t)<<2 | (d>t)<<3){ - case 0x1: seg(cpv(x0, midlerp(y0,y1,a,c,t)), cpv(midlerp(x0,x1,a,b,t), y0), segment, segment_data); break; - case 0x2: seg(cpv(midlerp(x0,x1,a,b,t), y0), cpv(x1, midlerp(y0,y1,b,d,t)), segment, segment_data); break; - case 0x3: seg(cpv(x0, midlerp(y0,y1,a,c,t)), cpv(x1, midlerp(y0,y1,b,d,t)), segment, segment_data); break; - case 0x4: seg(cpv(midlerp(x0,x1,c,d,t), y1), cpv(x0, midlerp(y0,y1,a,c,t)), segment, segment_data); break; - case 0x5: seg(cpv(midlerp(x0,x1,c,d,t), y1), cpv(midlerp(x0,x1,a,b,t), y0), segment, segment_data); break; - case 0x6: seg(cpv(midlerp(x0,x1,a,b,t), y0), cpv(x1, midlerp(y0,y1,b,d,t)), segment, segment_data); - seg(cpv(midlerp(x0,x1,c,d,t), y1), cpv(x0, midlerp(y0,y1,a,c,t)), segment, segment_data); break; - case 0x7: seg(cpv(midlerp(x0,x1,c,d,t), y1), cpv(x1, midlerp(y0,y1,b,d,t)), segment, segment_data); break; - case 0x8: seg(cpv(x1, midlerp(y0,y1,b,d,t)), cpv(midlerp(x0,x1,c,d,t), y1), segment, segment_data); break; - case 0x9: seg(cpv(x0, midlerp(y0,y1,a,c,t)), cpv(midlerp(x0,x1,a,b,t), y0), segment, segment_data); - seg(cpv(x1, midlerp(y0,y1,b,d,t)), cpv(midlerp(x0,x1,c,d,t), y1), segment, segment_data); break; - case 0xA: seg(cpv(midlerp(x0,x1,a,b,t), y0), cpv(midlerp(x0,x1,c,d,t), y1), segment, segment_data); break; - case 0xB: seg(cpv(x0, midlerp(y0,y1,a,c,t)), cpv(midlerp(x0,x1,c,d,t), y1), segment, segment_data); break; - case 0xC: seg(cpv(x1, midlerp(y0,y1,b,d,t)), cpv(x0, midlerp(y0,y1,a,c,t)), segment, segment_data); break; - case 0xD: seg(cpv(x1, midlerp(y0,y1,b,d,t)), cpv(midlerp(x0,x1,a,b,t), y0), segment, segment_data); break; - case 0xE: seg(cpv(midlerp(x0,x1,a,b,t), y0), cpv(x0, midlerp(y0,y1,a,c,t)), segment, segment_data); break; - default: break; // 0x0 and 0xF - } -} - -void -cpMarchSoft( - cpBB bb, unsigned long x_samples, unsigned long y_samples, cpFloat t, - cpMarchSegmentFunc segment, void *segment_data, - cpMarchSampleFunc sample, void *sample_data -){ - cpMarchCells(bb, x_samples, y_samples, t, segment, segment_data, sample, sample_data, cpMarchCellSoft); -} - - -// TODO should flip this around eventually. -static inline void -segs(cpVect a, cpVect b, cpVect c, cpMarchSegmentFunc f, void *data) -{ - seg(b, c, f, data); - seg(a, b, f, data); -} - -static void -cpMarchCellHard( - cpFloat t, cpFloat a, cpFloat b, cpFloat c, cpFloat d, - cpFloat x0, cpFloat x1, cpFloat y0, cpFloat y1, - cpMarchSegmentFunc segment, void *segment_data -){ - // midpoints - cpFloat xm = cpflerp(x0, x1, 0.5f); - cpFloat ym = cpflerp(y0, y1, 0.5f); - - switch((a>t)<<0 | (b>t)<<1 | (c>t)<<2 | (d>t)<<3){ - case 0x1: segs(cpv(x0, ym), cpv(xm, ym), cpv(xm, y0), segment, segment_data); break; - case 0x2: segs(cpv(xm, y0), cpv(xm, ym), cpv(x1, ym), segment, segment_data); break; - case 0x3: seg(cpv(x0, ym), cpv(x1, ym), segment, segment_data); break; - case 0x4: segs(cpv(xm, y1), cpv(xm, ym), cpv(x0, ym), segment, segment_data); break; - case 0x5: seg(cpv(xm, y1), cpv(xm, y0), segment, segment_data); break; - case 0x6: segs(cpv(xm, y0), cpv(xm, ym), cpv(x0, ym), segment, segment_data); - segs(cpv(xm, y1), cpv(xm, ym), cpv(x1, ym), segment, segment_data); break; - case 0x7: segs(cpv(xm, y1), cpv(xm, ym), cpv(x1, ym), segment, segment_data); break; - case 0x8: segs(cpv(x1, ym), cpv(xm, ym), cpv(xm, y1), segment, segment_data); break; - case 0x9: segs(cpv(x1, ym), cpv(xm, ym), cpv(xm, y0), segment, segment_data); - segs(cpv(x0, ym), cpv(xm, ym), cpv(xm, y1), segment, segment_data); break; - case 0xA: seg(cpv(xm, y0), cpv(xm, y1), segment, segment_data); break; - case 0xB: segs(cpv(x0, ym), cpv(xm, ym), cpv(xm, y1), segment, segment_data); break; - case 0xC: seg(cpv(x1, ym), cpv(x0, ym), segment, segment_data); break; - case 0xD: segs(cpv(x1, ym), cpv(xm, ym), cpv(xm, y0), segment, segment_data); break; - case 0xE: segs(cpv(xm, y0), cpv(xm, ym), cpv(x0, ym), segment, segment_data); break; - default: break; // 0x0 and 0xF - } -} - -void -cpMarchHard( - cpBB bb, unsigned long x_samples, unsigned long y_samples, cpFloat t, - cpMarchSegmentFunc segment, void *segment_data, - cpMarchSampleFunc sample, void *sample_data -){ - cpMarchCells(bb, x_samples, y_samples, t, segment, segment_data, sample, sample_data, cpMarchCellHard); -} diff --git a/apecs-physics/Chipmunk2D/src/cpPinJoint.c b/apecs-physics/Chipmunk2D/src/cpPinJoint.c deleted file mode 100644 index 012cf91..0000000 --- a/apecs-physics/Chipmunk2D/src/cpPinJoint.c +++ /dev/null @@ -1,172 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "chipmunk_private.h" - -static void -preStep(cpPinJoint *joint, cpFloat dt) -{ - cpBody *a = joint->constraint.a; - cpBody *b = joint->constraint.b; - - joint->r1 = cpTransformVect(a->transform, cpvsub(joint->anchorA, a->cog)); - joint->r2 = cpTransformVect(b->transform, cpvsub(joint->anchorB, b->cog)); - - cpVect delta = cpvsub(cpvadd(b->p, joint->r2), cpvadd(a->p, joint->r1)); - cpFloat dist = cpvlength(delta); - joint->n = cpvmult(delta, 1.0f/(dist ? dist : (cpFloat)INFINITY)); - - // calculate mass normal - joint->nMass = 1.0f/k_scalar(a, b, joint->r1, joint->r2, joint->n); - - // calculate bias velocity - cpFloat maxBias = joint->constraint.maxBias; - joint->bias = cpfclamp(-bias_coef(joint->constraint.errorBias, dt)*(dist - joint->dist)/dt, -maxBias, maxBias); -} - -static void -applyCachedImpulse(cpPinJoint *joint, cpFloat dt_coef) -{ - cpBody *a = joint->constraint.a; - cpBody *b = joint->constraint.b; - - cpVect j = cpvmult(joint->n, joint->jnAcc*dt_coef); - apply_impulses(a, b, joint->r1, joint->r2, j); -} - -static void -applyImpulse(cpPinJoint *joint, cpFloat dt) -{ - cpBody *a = joint->constraint.a; - cpBody *b = joint->constraint.b; - cpVect n = joint->n; - - // compute relative velocity - cpFloat vrn = normal_relative_velocity(a, b, joint->r1, joint->r2, n); - - cpFloat jnMax = joint->constraint.maxForce*dt; - - // compute normal impulse - cpFloat jn = (joint->bias - vrn)*joint->nMass; - cpFloat jnOld = joint->jnAcc; - joint->jnAcc = cpfclamp(jnOld + jn, -jnMax, jnMax); - jn = joint->jnAcc - jnOld; - - // apply impulse - apply_impulses(a, b, joint->r1, joint->r2, cpvmult(n, jn)); -} - -static cpFloat -getImpulse(cpPinJoint *joint) -{ - return cpfabs(joint->jnAcc); -} - -static const cpConstraintClass klass = { - (cpConstraintPreStepImpl)preStep, - (cpConstraintApplyCachedImpulseImpl)applyCachedImpulse, - (cpConstraintApplyImpulseImpl)applyImpulse, - (cpConstraintGetImpulseImpl)getImpulse, -}; - - -cpPinJoint * -cpPinJointAlloc(void) -{ - return (cpPinJoint *)cpcalloc(1, sizeof(cpPinJoint)); -} - -cpPinJoint * -cpPinJointInit(cpPinJoint *joint, cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB) -{ - cpConstraintInit((cpConstraint *)joint, &klass, a, b); - - joint->anchorA = anchorA; - joint->anchorB = anchorB; - - // STATIC_BODY_CHECK - cpVect p1 = (a ? cpTransformPoint(a->transform, anchorA) : anchorA); - cpVect p2 = (b ? cpTransformPoint(b->transform, anchorB) : anchorB); - joint->dist = cpvlength(cpvsub(p2, p1)); - - cpAssertWarn(joint->dist > 0.0, "You created a 0 length pin joint. A pivot joint will be much more stable."); - - joint->jnAcc = 0.0f; - - return joint; -} - -cpConstraint * -cpPinJointNew(cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB) -{ - return (cpConstraint *)cpPinJointInit(cpPinJointAlloc(), a, b, anchorA, anchorB); -} - -cpBool -cpConstraintIsPinJoint(const cpConstraint *constraint) -{ - return (constraint->klass == &klass); -} - -cpVect -cpPinJointGetAnchorA(const cpConstraint *constraint) -{ - cpAssertHard(cpConstraintIsPinJoint(constraint), "Constraint is not a pin joint."); - return ((cpPinJoint *)constraint)->anchorA; -} - -void -cpPinJointSetAnchorA(cpConstraint *constraint, cpVect anchorA) -{ - cpAssertHard(cpConstraintIsPinJoint(constraint), "Constraint is not a pin joint."); - cpConstraintActivateBodies(constraint); - ((cpPinJoint *)constraint)->anchorA = anchorA; -} - -cpVect -cpPinJointGetAnchorB(const cpConstraint *constraint) -{ - cpAssertHard(cpConstraintIsPinJoint(constraint), "Constraint is not a pin joint."); - return ((cpPinJoint *)constraint)->anchorB; -} - -void -cpPinJointSetAnchorB(cpConstraint *constraint, cpVect anchorB) -{ - cpAssertHard(cpConstraintIsPinJoint(constraint), "Constraint is not a pin joint."); - cpConstraintActivateBodies(constraint); - ((cpPinJoint *)constraint)->anchorB = anchorB; -} - -cpFloat -cpPinJointGetDist(const cpConstraint *constraint) -{ - cpAssertHard(cpConstraintIsPinJoint(constraint), "Constraint is not a pin joint."); - return ((cpPinJoint *)constraint)->dist; -} - -void -cpPinJointSetDist(cpConstraint *constraint, cpFloat dist) -{ - cpAssertHard(cpConstraintIsPinJoint(constraint), "Constraint is not a pin joint."); - cpConstraintActivateBodies(constraint); - ((cpPinJoint *)constraint)->dist = dist; -} diff --git a/apecs-physics/Chipmunk2D/src/cpPivotJoint.c b/apecs-physics/Chipmunk2D/src/cpPivotJoint.c deleted file mode 100644 index ab586c9..0000000 --- a/apecs-physics/Chipmunk2D/src/cpPivotJoint.c +++ /dev/null @@ -1,152 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "chipmunk_private.h" - -static void -preStep(cpPivotJoint *joint, cpFloat dt) -{ - cpBody *a = joint->constraint.a; - cpBody *b = joint->constraint.b; - - joint->r1 = cpTransformVect(a->transform, cpvsub(joint->anchorA, a->cog)); - joint->r2 = cpTransformVect(b->transform, cpvsub(joint->anchorB, b->cog)); - - // Calculate mass tensor - joint-> k = k_tensor(a, b, joint->r1, joint->r2); - - // calculate bias velocity - cpVect delta = cpvsub(cpvadd(b->p, joint->r2), cpvadd(a->p, joint->r1)); - joint->bias = cpvclamp(cpvmult(delta, -bias_coef(joint->constraint.errorBias, dt)/dt), joint->constraint.maxBias); -} - -static void -applyCachedImpulse(cpPivotJoint *joint, cpFloat dt_coef) -{ - cpBody *a = joint->constraint.a; - cpBody *b = joint->constraint.b; - - apply_impulses(a, b, joint->r1, joint->r2, cpvmult(joint->jAcc, dt_coef)); -} - -static void -applyImpulse(cpPivotJoint *joint, cpFloat dt) -{ - cpBody *a = joint->constraint.a; - cpBody *b = joint->constraint.b; - - cpVect r1 = joint->r1; - cpVect r2 = joint->r2; - - // compute relative velocity - cpVect vr = relative_velocity(a, b, r1, r2); - - // compute normal impulse - cpVect j = cpMat2x2Transform(joint->k, cpvsub(joint->bias, vr)); - cpVect jOld = joint->jAcc; - joint->jAcc = cpvclamp(cpvadd(joint->jAcc, j), joint->constraint.maxForce*dt); - j = cpvsub(joint->jAcc, jOld); - - // apply impulse - apply_impulses(a, b, joint->r1, joint->r2, j); -} - -static cpFloat -getImpulse(cpConstraint *joint) -{ - return cpvlength(((cpPivotJoint *)joint)->jAcc); -} - -static const cpConstraintClass klass = { - (cpConstraintPreStepImpl)preStep, - (cpConstraintApplyCachedImpulseImpl)applyCachedImpulse, - (cpConstraintApplyImpulseImpl)applyImpulse, - (cpConstraintGetImpulseImpl)getImpulse, -}; - -cpPivotJoint * -cpPivotJointAlloc(void) -{ - return (cpPivotJoint *)cpcalloc(1, sizeof(cpPivotJoint)); -} - -cpPivotJoint * -cpPivotJointInit(cpPivotJoint *joint, cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB) -{ - cpConstraintInit((cpConstraint *)joint, &klass, a, b); - - joint->anchorA = anchorA; - joint->anchorB = anchorB; - - joint->jAcc = cpvzero; - - return joint; -} - -cpConstraint * -cpPivotJointNew2(cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB) -{ - return (cpConstraint *)cpPivotJointInit(cpPivotJointAlloc(), a, b, anchorA, anchorB); -} - -cpConstraint * -cpPivotJointNew(cpBody *a, cpBody *b, cpVect pivot) -{ - cpVect anchorA = (a ? cpBodyWorldToLocal(a, pivot) : pivot); - cpVect anchorB = (b ? cpBodyWorldToLocal(b, pivot) : pivot); - return cpPivotJointNew2(a, b, anchorA, anchorB); -} - -cpBool -cpConstraintIsPivotJoint(const cpConstraint *constraint) -{ - return (constraint->klass == &klass); -} - -cpVect -cpPivotJointGetAnchorA(const cpConstraint *constraint) -{ - cpAssertHard(cpConstraintIsPivotJoint(constraint), "Constraint is not a pivot joint."); - return ((cpPivotJoint *)constraint)->anchorA; -} - -void -cpPivotJointSetAnchorA(cpConstraint *constraint, cpVect anchorA) -{ - cpAssertHard(cpConstraintIsPivotJoint(constraint), "Constraint is not a pivot joint."); - cpConstraintActivateBodies(constraint); - ((cpPivotJoint *)constraint)->anchorA = anchorA; -} - -cpVect -cpPivotJointGetAnchorB(const cpConstraint *constraint) -{ - cpAssertHard(cpConstraintIsPivotJoint(constraint), "Constraint is not a pivot joint."); - return ((cpPivotJoint *)constraint)->anchorB; -} - -void -cpPivotJointSetAnchorB(cpConstraint *constraint, cpVect anchorB) -{ - cpAssertHard(cpConstraintIsPivotJoint(constraint), "Constraint is not a pivot joint."); - cpConstraintActivateBodies(constraint); - ((cpPivotJoint *)constraint)->anchorB = anchorB; -} diff --git a/apecs-physics/Chipmunk2D/src/cpPolyShape.c b/apecs-physics/Chipmunk2D/src/cpPolyShape.c deleted file mode 100644 index cc507cc..0000000 --- a/apecs-physics/Chipmunk2D/src/cpPolyShape.c +++ /dev/null @@ -1,323 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "chipmunk_private.h" -#include "chipmunk_unsafe.h" - -cpPolyShape * -cpPolyShapeAlloc(void) -{ - return (cpPolyShape *)cpcalloc(1, sizeof(cpPolyShape)); -} - -static void -cpPolyShapeDestroy(cpPolyShape *poly) -{ - if(poly->count > CP_POLY_SHAPE_INLINE_ALLOC){ - cpfree(poly->planes); - } -} - -static cpBB -cpPolyShapeCacheData(cpPolyShape *poly, cpTransform transform) -{ - int count = poly->count; - struct cpSplittingPlane *dst = poly->planes; - struct cpSplittingPlane *src = dst + count; - - cpFloat l = (cpFloat)INFINITY, r = -(cpFloat)INFINITY; - cpFloat b = (cpFloat)INFINITY, t = -(cpFloat)INFINITY; - - for(int i=0; ir; - return (poly->shape.bb = cpBBNew(l - radius, b - radius, r + radius, t + radius)); -} - -static void -cpPolyShapePointQuery(cpPolyShape *poly, cpVect p, cpPointQueryInfo *info){ - int count = poly->count; - struct cpSplittingPlane *planes = poly->planes; - cpFloat r = poly->r; - - cpVect v0 = planes[count - 1].v0; - cpFloat minDist = INFINITY; - cpVect closestPoint = cpvzero; - cpVect closestNormal = cpvzero; - cpBool outside = cpFalse; - - for(int i=0; i 0.0f); - - cpVect closest = cpClosetPointOnSegment(p, v0, v1); - - cpFloat dist = cpvdist(p, closest); - if(dist < minDist){ - minDist = dist; - closestPoint = closest; - closestNormal = planes[i].n; - } - - v0 = v1; - } - - cpFloat dist = (outside ? minDist : -minDist); - cpVect g = cpvmult(cpvsub(p, closestPoint), 1.0f/dist); - - info->shape = (cpShape *)poly; - info->point = cpvadd(closestPoint, cpvmult(g, r)); - info->distance = dist - r; - - // Use the normal of the closest segment if the distance is small. - info->gradient = (minDist > MAGIC_EPSILON ? g : closestNormal); -} - -static void -cpPolyShapeSegmentQuery(cpPolyShape *poly, cpVect a, cpVect b, cpFloat r2, cpSegmentQueryInfo *info) -{ - struct cpSplittingPlane *planes = poly->planes; - int count = poly->count; - cpFloat r = poly->r; - cpFloat rsum = r + r2; - - for(int i=0; ishape = (cpShape *)poly; - info->point = cpvsub(cpvlerp(a, b, t), cpvmult(n, r2)); - info->normal = n; - info->alpha = t; - } - } - - // Also check against the beveled vertexes. - if(rsum > 0.0f){ - for(int i=0; ishape, planes[i].v0, r, a, b, r2, &circle_info); - if(circle_info.alpha < info->alpha) (*info) = circle_info; - } - } -} - -static void -SetVerts(cpPolyShape *poly, int count, const cpVect *verts) -{ - poly->count = count; - if(count <= CP_POLY_SHAPE_INLINE_ALLOC){ - poly->planes = poly->_planes; - } else { - poly->planes = (struct cpSplittingPlane *)cpcalloc(2*count, sizeof(struct cpSplittingPlane)); - } - - for(int i=0; iplanes[i + count].v0 = b; - poly->planes[i + count].n = n; - } -} - -static struct cpShapeMassInfo -cpPolyShapeMassInfo(cpFloat mass, int count, const cpVect *verts, cpFloat radius) -{ - // TODO moment is approximate due to radius. - - cpVect centroid = cpCentroidForPoly(count, verts); - struct cpShapeMassInfo info = { - mass, cpMomentForPoly(1.0f, count, verts, cpvneg(centroid), radius), - centroid, - cpAreaForPoly(count, verts, radius), - }; - - return info; -} - -static const cpShapeClass polyClass = { - CP_POLY_SHAPE, - (cpShapeCacheDataImpl)cpPolyShapeCacheData, - (cpShapeDestroyImpl)cpPolyShapeDestroy, - (cpShapePointQueryImpl)cpPolyShapePointQuery, - (cpShapeSegmentQueryImpl)cpPolyShapeSegmentQuery, -}; - -cpPolyShape * -cpPolyShapeInit(cpPolyShape *poly, cpBody *body, int count, const cpVect *verts, cpTransform transform, cpFloat radius) -{ - cpVect *hullVerts = (cpVect *)alloca(count*sizeof(cpVect)); - - // Transform the verts before building the hull in case of a negative scale. - for(int i=0; ir = radius; - - return poly; -} - -cpShape * -cpPolyShapeNew(cpBody *body, int count, const cpVect *verts, cpTransform transform, cpFloat radius) -{ - return (cpShape *)cpPolyShapeInit(cpPolyShapeAlloc(), body, count, verts, transform, radius); -} - -cpShape * -cpPolyShapeNewRaw(cpBody *body, int count, const cpVect *verts, cpFloat radius) -{ - return (cpShape *)cpPolyShapeInitRaw(cpPolyShapeAlloc(), body, count, verts, radius); -} - -cpPolyShape * -cpBoxShapeInit(cpPolyShape *poly, cpBody *body, cpFloat width, cpFloat height, cpFloat radius) -{ - cpFloat hw = width/2.0f; - cpFloat hh = height/2.0f; - - return cpBoxShapeInit2(poly, body, cpBBNew(-hw, -hh, hw, hh), radius); -} - -cpPolyShape * -cpBoxShapeInit2(cpPolyShape *poly, cpBody *body, cpBB box, cpFloat radius) -{ - cpVect verts[] = { - cpv(box.r, box.b), - cpv(box.r, box.t), - cpv(box.l, box.t), - cpv(box.l, box.b), - }; - - return cpPolyShapeInitRaw(poly, body, 4, verts, radius); -} - -cpShape * -cpBoxShapeNew(cpBody *body, cpFloat width, cpFloat height, cpFloat radius) -{ - return (cpShape *)cpBoxShapeInit(cpPolyShapeAlloc(), body, width, height, radius); -} - -cpShape * -cpBoxShapeNew2(cpBody *body, cpBB box, cpFloat radius) -{ - return (cpShape *)cpBoxShapeInit2(cpPolyShapeAlloc(), body, box, radius); -} - -int -cpPolyShapeGetCount(const cpShape *shape) -{ - cpAssertHard(shape->klass == &polyClass, "Shape is not a poly shape."); - return ((cpPolyShape *)shape)->count; -} - -cpVect -cpPolyShapeGetVert(const cpShape *shape, int i) -{ - cpAssertHard(shape->klass == &polyClass, "Shape is not a poly shape."); - - int count = cpPolyShapeGetCount(shape); - cpAssertHard(0 <= i && i < count, "Index out of range."); - - return ((cpPolyShape *)shape)->planes[i + count].v0; -} - -cpFloat -cpPolyShapeGetRadius(const cpShape *shape) -{ - cpAssertHard(shape->klass == &polyClass, "Shape is not a poly shape."); - return ((cpPolyShape *)shape)->r; -} - -// Unsafe API (chipmunk_unsafe.h) - -void -cpPolyShapeSetVerts(cpShape *shape, int count, cpVect *verts, cpTransform transform) -{ - cpVect *hullVerts = (cpVect *)alloca(count*sizeof(cpVect)); - - // Transform the verts before building the hull in case of a negative scale. - for(int i=0; iklass == &polyClass, "Shape is not a poly shape."); - cpPolyShape *poly = (cpPolyShape *)shape; - cpPolyShapeDestroy(poly); - - SetVerts(poly, count, verts); - - cpFloat mass = shape->massInfo.m; - shape->massInfo = cpPolyShapeMassInfo(shape->massInfo.m, count, verts, poly->r); - if(mass > 0.0f) cpBodyAccumulateMassFromShapes(shape->body); -} - -void -cpPolyShapeSetRadius(cpShape *shape, cpFloat radius) -{ - cpAssertHard(shape->klass == &polyClass, "Shape is not a poly shape."); - cpPolyShape *poly = (cpPolyShape *)shape; - poly->r = radius; - - - // TODO radius is not handled by moment/area -// cpFloat mass = shape->massInfo.m; -// shape->massInfo = cpPolyShapeMassInfo(shape->massInfo.m, poly->count, poly->verts, poly->r); -// if(mass > 0.0f) cpBodyAccumulateMassFromShapes(shape->body); -} diff --git a/apecs-physics/Chipmunk2D/src/cpPolyline.c b/apecs-physics/Chipmunk2D/src/cpPolyline.c deleted file mode 100644 index 8af65b8..0000000 --- a/apecs-physics/Chipmunk2D/src/cpPolyline.c +++ /dev/null @@ -1,652 +0,0 @@ -// Copyright 2013 Howling Moon Software. All rights reserved. -// See http://chipmunk2d.net/legal.php for more information. - -#include -#include -#include -#include - -#include "chipmunk_private.h" -#include "cpPolyline.h" - - -static inline int Next(int i, int count){return (i+1)%count;} - -//MARK: Polylines - -#define DEFAULT_POLYLINE_CAPACITY 16 - -static int -cpPolylineSizeForCapacity(int capacity) -{ - return sizeof(cpPolyline) + capacity*sizeof(cpVect); -} - -static cpPolyline * -cpPolylineMake(int capacity) -{ - capacity = (capacity > DEFAULT_POLYLINE_CAPACITY ? capacity : DEFAULT_POLYLINE_CAPACITY); - - cpPolyline *line = (cpPolyline *)cpcalloc(1, cpPolylineSizeForCapacity(capacity)); - line->count = 0; - line->capacity = capacity; - - return line; -} - -static cpPolyline * -cpPolylineMake2(int capacity, cpVect a, cpVect b) -{ - cpPolyline *line = cpPolylineMake(capacity); - line->count = 2; - line->verts[0] = a; - line->verts[1] = b; - - return line; -} - -static cpPolyline * -cpPolylineShrink(cpPolyline *line) -{ - line->capacity = line->count; - return (cpPolyline*) cprealloc(line, cpPolylineSizeForCapacity(line->count)); -} - -void -cpPolylineFree(cpPolyline *line) -{ - cpfree(line); -} - -// Grow the allocated memory for a polyline. -static cpPolyline * -cpPolylineGrow(cpPolyline *line, int count) -{ - line->count += count; - - int capacity = line->capacity; - while(line->count > capacity) capacity *= 2; - - if(line->capacity < capacity){ - line->capacity = capacity; - line = (cpPolyline*) cprealloc(line, cpPolylineSizeForCapacity(capacity)); - } - - return line; -} - -// Push v onto the end of line. -static cpPolyline * -cpPolylinePush(cpPolyline *line, cpVect v) -{ - int count = line->count; - line = cpPolylineGrow(line, 1); - line->verts[count] = v; - - return line; -} - -// Push v onto the beginning of line. -static cpPolyline * -cpPolylineEnqueue(cpPolyline *line, cpVect v) -{ - // TODO could optimize this to grow in both directions. - // Probably doesn't matter though. - int count = line->count; - line = cpPolylineGrow(line, 1); - memmove(line->verts + 1, line->verts, count*sizeof(cpVect)); - line->verts[0] = v; - - return line; -} - -// Returns true if the polyline starts and ends with the same vertex. -cpBool -cpPolylineIsClosed(cpPolyline *line) -{ - return (line->count > 1 && cpveql(line->verts[0], line->verts[line->count-1])); -} - -// Check if a cpPolyline is longer than a certain length -// Takes a range which can wrap around if the polyline is looped. -static cpBool -cpPolylineIsShort(cpVect *points, int count, int start, int end, cpFloat min) -{ - cpFloat length = 0.0f; - for(int i=start; i!=end; i=Next(i, count)){ - length += cpvdist(points[i], points[Next(i, count)]); - if(length > min) return cpFalse; - } - - return cpTrue; -} - -//MARK: Polyline Simplification - -static inline cpFloat -Sharpness(cpVect a, cpVect b, cpVect c) -{ - // TODO could speed this up by caching the normals instead of calculating each twice. - return cpvdot(cpvnormalize(cpvsub(a, b)), cpvnormalize(cpvsub(c, b))); -} - -// Join similar adjacent line segments together. Works well for hard edged shapes. -// 'tol' is the minimum anglular difference in radians of a vertex. -cpPolyline * -cpPolylineSimplifyVertexes(cpPolyline *line, cpFloat tol) -{ - cpPolyline *reduced = cpPolylineMake2(0, line->verts[0], line->verts[1]); - - cpFloat minSharp = -cpfcos(tol); - - for(int i=2; icount; i++){ - cpVect vert = line->verts[i]; - cpFloat sharp = Sharpness(reduced->verts[reduced->count - 2], reduced->verts[reduced->count - 1], vert); - - if(sharp <= minSharp){ - reduced->verts[reduced->count - 1] = vert; - } else { - reduced = cpPolylinePush(reduced, vert); - } - } - - if( - cpPolylineIsClosed(line) && - Sharpness(reduced->verts[reduced->count - 2], reduced->verts[0], reduced->verts[1]) < minSharp - ){ - reduced->verts[0] = reduced->verts[reduced->count - 2]; - reduced->count--; - } - - // TODO shrink - return reduced; -} - -// Recursive function used by cpPolylineSimplifyCurves(). -static cpPolyline * -DouglasPeucker( - cpVect *verts, cpPolyline *reduced, - int length, int start, int end, - cpFloat min, cpFloat tol -){ - // Early exit if the points are adjacent - if((end - start + length)%length < 2) return reduced; - - cpVect a = verts[start]; - cpVect b = verts[end]; - - // Check if the length is below the threshold - if(cpvnear(a, b, min) && cpPolylineIsShort(verts, length, start, end, min)) return reduced; - - // Find the maximal vertex to split and recurse on - cpFloat max = 0.0; - int maxi = start; - - cpVect n = cpvnormalize(cpvperp(cpvsub(b, a))); - cpFloat d = cpvdot(n, a); - - for(int i=Next(start, length); i!=end; i=Next(i, length)){ - cpFloat dist = fabs(cpvdot(n, verts[i]) - d); - - if(dist > max){ - max = dist; - maxi = i; - } - } - - if(max > tol){ - reduced = DouglasPeucker(verts, reduced, length, start, maxi, min, tol); - reduced = cpPolylinePush(reduced, verts[maxi]); - reduced = DouglasPeucker(verts, reduced, length, maxi, end, min, tol); - } - - return reduced; -} - -// Recursively reduce the vertex count on a polyline. Works best for smooth shapes. -// 'tol' is the maximum error for the reduction. -// The reduced polyline will never be farther than this distance from the original polyline. -cpPolyline * -cpPolylineSimplifyCurves(cpPolyline *line, cpFloat tol) -{ - cpPolyline *reduced = cpPolylineMake(line->count); - - cpFloat min = tol/2.0f; - - if(cpPolylineIsClosed(line)){ - int start, end; - cpLoopIndexes(line->verts, line->count - 1, &start, &end); - - reduced = cpPolylinePush(reduced, line->verts[start]); - reduced = DouglasPeucker(line->verts, reduced, line->count - 1, start, end, min, tol); - reduced = cpPolylinePush(reduced, line->verts[end]); - reduced = DouglasPeucker(line->verts, reduced, line->count - 1, end, start, min, tol); - reduced = cpPolylinePush(reduced, line->verts[start]); - } else { - reduced = cpPolylinePush(reduced, line->verts[0]); - reduced = DouglasPeucker(line->verts, reduced, line->count, 0, line->count - 1, min, tol); - reduced = cpPolylinePush(reduced, line->verts[line->count - 1]); - } - - return cpPolylineShrink(reduced); -} - -//MARK: Polyline Sets - -cpPolylineSet * -cpPolylineSetAlloc(void) -{ - return (cpPolylineSet *)cpcalloc(1, sizeof(cpPolylineSet)); -} - -cpPolylineSet * -cpPolylineSetInit(cpPolylineSet *set) -{ - set->count = 0; - set->capacity = 8; - set->lines = (cpPolyline**) cpcalloc(set->capacity, sizeof(cpPolyline)); - - return set; -} - - -cpPolylineSet * -cpPolylineSetNew(void) -{ - return cpPolylineSetInit(cpPolylineSetAlloc()); -} - -void -cpPolylineSetDestroy(cpPolylineSet *set, cpBool freePolylines) -{ - if(freePolylines){ - for(int i=0; icount; i++){ - cpPolylineFree(set->lines[i]); - } - } - - cpfree(set->lines); -} - - -void -cpPolylineSetFree(cpPolylineSet *set, cpBool freePolylines) -{ - if(set){ - cpPolylineSetDestroy(set, freePolylines); - cpfree(set); - } -} - -// Find the polyline that ends with v. -static int -cpPolylineSetFindEnds(cpPolylineSet *set, cpVect v){ - int count = set->count; - cpPolyline **lines = set->lines; - - for(int i=0; iverts[line->count - 1], v)) return i; - } - - return -1; -} - -// Find the polyline that starts with v. -static int -cpPolylineSetFindStarts(cpPolylineSet *set, cpVect v){ - int count = set->count; - cpPolyline **lines = set->lines; - - for(int i=0; iverts[0], v)) return i; - } - - return -1; -} - -// Add a new polyline to a polyline set. -static void -cpPolylineSetPush(cpPolylineSet *set, cpPolyline *line) -{ - // grow set - set->count++; - if(set->count > set->capacity){ - set->capacity *= 2; - set->lines = (cpPolyline**) cprealloc(set->lines, set->capacity*sizeof(cpPolyline)); - } - - set->lines[set->count - 1] = line; -} - -// Add a new polyline to a polyline set. -static void -cpPolylineSetAdd(cpPolylineSet *set, cpVect v0, cpVect v1) -{ - cpPolylineSetPush(set, cpPolylineMake2(DEFAULT_POLYLINE_CAPACITY, v0, v1)); -} - -// Join two cpPolylines in a polyline set together. -static void -cpPolylineSetJoin(cpPolylineSet *set, int before, int after) -{ - cpPolyline *lbefore = set->lines[before]; - cpPolyline *lafter = set->lines[after]; - - // append - int count = lbefore->count; - lbefore = cpPolylineGrow(lbefore, lafter->count); - memmove(lbefore->verts + count, lafter->verts, lafter->count*sizeof(cpVect)); - set->lines[before] = lbefore; - - // delete lafter - set->count--; - cpPolylineFree(set->lines[after]); - set->lines[after] = set->lines[set->count]; -} - -// Add a segment to a polyline set. -// A segment will either start a new polyline, join two others, or add to or loop an existing polyline. -void -cpPolylineSetCollectSegment(cpVect v0, cpVect v1, cpPolylineSet *lines) -{ - int before = cpPolylineSetFindEnds(lines, v0); - int after = cpPolylineSetFindStarts(lines, v1); - - if(before >= 0 && after >= 0){ - if(before == after){ - // loop by pushing v1 onto before - lines->lines[before] = cpPolylinePush(lines->lines[before], v1); - } else { - // join before and after - cpPolylineSetJoin(lines, before, after); - } - } else if(before >= 0){ - // push v1 onto before - lines->lines[before] = cpPolylinePush(lines->lines[before], v1); - } else if(after >= 0){ - // enqueue v0 onto after - lines->lines[after] = cpPolylineEnqueue(lines->lines[after], v0); - } else { - // create new line from v0 and v1 - cpPolylineSetAdd(lines, v0, v1); - } -} - -//MARK: Convex Hull Functions - -cpPolyline * -cpPolylineToConvexHull(cpPolyline *line, cpFloat tol) -{ - cpPolyline *hull = cpPolylineMake(line->count + 1); - hull->count = cpConvexHull(line->count, line->verts, hull->verts, NULL, tol); - hull = cpPolylinePush(hull, hull->verts[0]); - - return cpPolylineShrink(hull); -} - -//MARK: Approximate Concave Decompostition - -struct Notch { - int i; - cpFloat d; - cpVect v; - cpVect n; -}; - -static cpFloat -FindSteiner(int count, cpVect *verts, struct Notch notch) -{ - cpFloat min = INFINITY; - cpFloat feature = -1.0; - - for(int i=1; i= 0.0 && dist <= min){ - min = dist; - feature = index + t; - } - } - } - - return feature; -} - -//static cpFloat -//FindSteiner2(cpVect *verts, int count, struct Notch notch) -//{ -// cpVect a = verts[(notch.i + count - 1)%count]; -// cpVect b = verts[(notch.i + 1)%count]; -// cpVect n = cpvnormalize(cpvadd(cpvnormalize(cpvsub(notch.v, a)), cpvnormalize(cpvsub(notch.v, b)))); -// -// cpFloat min = INFINITY; -// cpFloat feature = -1.0; -// -// for(int i=1; i= 0.0 && dist <= min){ -// min = dist; -// feature = index + t; -// } -// } -// } -// -// cpAssertSoft(feature >= 0.0, "No closest features detected. This is likely due to a self intersecting polygon."); -// return feature; -//} - -//struct Range {cpFloat min, max;}; -//static inline struct Range -//clip_range(cpVect delta_a, cpVect delta_b, cpVect clip) -//{ -// cpFloat da = cpvcross(delta_a, clip); -// cpFloat db = cpvcross(delta_b, clip); -// cpFloat clamp = da/(da - db); -// if(da > db){ -// return (struct Range){-INFINITY, clamp}; -// } else if(da < db){ -// return (struct Range){clamp, INFINITY}; -// } else { -// return (struct Range){-INFINITY, INFINITY}; -// } -//} -// -//static cpFloat -//FindSteiner3(cpVect *verts, int count, struct Notch notch) -//{ -// cpFloat min = INFINITY; -// cpFloat feature = -1.0; -// -// cpVect support_a = verts[(notch.i - 1 + count)%count]; -// cpVect support_b = verts[(notch.i + 1)%count]; -// -// cpVect clip_a = cpvlerp(support_a, support_b, 0.1); -// cpVect clip_b = cpvlerp(support_b, support_b, 0.9); -// -// for(int i=1; i 0.0){ -// struct Range range1 = clip_range(delta_a, delta_b, cpvsub(notch.v, clip_a)); -// struct Range range2 = clip_range(delta_a, delta_b, cpvsub(clip_b, notch.v)); -// -// cpFloat min_t = cpfmax(0.0, cpfmax(range1.min, range2.min)); -// cpFloat max_t = cpfmin(1.0, cpfmin(range1.max, range2.max)); -// -// // Ignore if the segment has been completely clipped away. -// if(min_t < max_t){ -// cpVect seg_delta = cpvsub(seg_b, seg_a); -// cpFloat closest_t = cpfclamp(cpvdot(seg_delta, cpvsub(notch.v, seg_a))/cpvlengthsq(seg_delta), min_t, max_t); -// cpVect closest = cpvlerp(seg_a, seg_b, closest_t); -// -// cpFloat dist = cpvdistsq(notch.v, closest); -// if(dist < min){ -// min = dist; -// feature = index + closest_t; -// } -// } -// } -// } -// -// cpAssertWarn(feature >= 0.0, "Internal Error: No closest features detected."); -// return feature; -//} - -//static cpBool -//VertexUnobscured(int count, cpVect *verts, int index, int notch_i) -//{ -// cpVect v = verts[notch_i]; -// cpVect n = cpvnormalize(cpvsub(verts[index], v)); -// -// for(int i=0; i= 0.0, "No closest features detected. This is likely due to a self intersecting polygon."); -// return feature; -//} - -static struct Notch -DeepestNotch(int count, cpVect *verts, int hullCount, cpVect *hullVerts, int first, cpFloat tol) -{ - struct Notch notch = {}; - int j = Next(first, count); - - for(int i=0; i notch.d){ - notch.d = depth; - notch.i = j; - notch.v = v; - notch.n = n; - } - - j = Next(j, count); - v = verts[j]; - } - - j = Next(j, count); - } - - return notch; -} - -static inline int IMAX(int a, int b){return (a > b ? a : b);} - -static void -ApproximateConcaveDecomposition(cpVect *verts, int count, cpFloat tol, cpPolylineSet *set) -{ - int first; - cpVect *hullVerts = (cpVect*) alloca(count*sizeof(cpVect)); - int hullCount = cpConvexHull(count, verts, hullVerts, &first, 0.0); - - if(hullCount != count){ - struct Notch notch = DeepestNotch(count, verts, hullCount, hullVerts, first, tol); - - if(notch.d > tol){ - cpFloat steiner_it = FindSteiner(count, verts, notch); - - if(steiner_it >= 0.0){ - int steiner_i = (int)steiner_it; - cpVect steiner = cpvlerp(verts[steiner_i], verts[Next(steiner_i, count)], steiner_it - steiner_i); - - // Vertex counts NOT including the steiner point. - int sub1_count = (steiner_i - notch.i + count)%count + 1; - int sub2_count = count - (steiner_i - notch.i + count)%count; - cpVect *scratch = (cpVect*) alloca((IMAX(sub1_count, sub2_count) + 1)*sizeof(cpVect)); - - for(int i=0; iverts, hullVerts, hullCount*sizeof(cpVect)); - hull->verts[hullCount] = hullVerts[0]; - hull->count = hullCount + 1; - - cpPolylineSetPush(set, hull); -} - -cpPolylineSet * -cpPolylineConvexDecomposition_BETA(cpPolyline *line, cpFloat tol) -{ - cpAssertSoft(cpPolylineIsClosed(line), "Cannot decompose an open polygon."); - cpAssertSoft(cpAreaForPoly(line->count, line->verts, 0.0) >= 0.0, "Winding is backwards. (Are you passing a hole?)"); - - cpPolylineSet *set = cpPolylineSetNew(); - ApproximateConcaveDecomposition(line->verts, line->count - 1, tol, set); - - return set; -} diff --git a/apecs-physics/Chipmunk2D/src/cpRatchetJoint.c b/apecs-physics/Chipmunk2D/src/cpRatchetJoint.c deleted file mode 100644 index 7463a9f..0000000 --- a/apecs-physics/Chipmunk2D/src/cpRatchetJoint.c +++ /dev/null @@ -1,179 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "chipmunk_private.h" - -static void -preStep(cpRatchetJoint *joint, cpFloat dt) -{ - cpBody *a = joint->constraint.a; - cpBody *b = joint->constraint.b; - - cpFloat angle = joint->angle; - cpFloat phase = joint->phase; - cpFloat ratchet = joint->ratchet; - - cpFloat delta = b->a - a->a; - cpFloat diff = angle - delta; - cpFloat pdist = 0.0f; - - if(diff*ratchet > 0.0f){ - pdist = diff; - } else { - joint->angle = cpffloor((delta - phase)/ratchet)*ratchet + phase; - } - - // calculate moment of inertia coefficient. - joint->iSum = 1.0f/(a->i_inv + b->i_inv); - - // calculate bias velocity - cpFloat maxBias = joint->constraint.maxBias; - joint->bias = cpfclamp(-bias_coef(joint->constraint.errorBias, dt)*pdist/dt, -maxBias, maxBias); - - // If the bias is 0, the joint is not at a limit. Reset the impulse. - if(!joint->bias) joint->jAcc = 0.0f; -} - -static void -applyCachedImpulse(cpRatchetJoint *joint, cpFloat dt_coef) -{ - cpBody *a = joint->constraint.a; - cpBody *b = joint->constraint.b; - - cpFloat j = joint->jAcc*dt_coef; - a->w -= j*a->i_inv; - b->w += j*b->i_inv; -} - -static void -applyImpulse(cpRatchetJoint *joint, cpFloat dt) -{ - if(!joint->bias) return; // early exit - - cpBody *a = joint->constraint.a; - cpBody *b = joint->constraint.b; - - // compute relative rotational velocity - cpFloat wr = b->w - a->w; - cpFloat ratchet = joint->ratchet; - - cpFloat jMax = joint->constraint.maxForce*dt; - - // compute normal impulse - cpFloat j = -(joint->bias + wr)*joint->iSum; - cpFloat jOld = joint->jAcc; - joint->jAcc = cpfclamp((jOld + j)*ratchet, 0.0f, jMax*cpfabs(ratchet))/ratchet; - j = joint->jAcc - jOld; - - // apply impulse - a->w -= j*a->i_inv; - b->w += j*b->i_inv; -} - -static cpFloat -getImpulse(cpRatchetJoint *joint) -{ - return cpfabs(joint->jAcc); -} - -static const cpConstraintClass klass = { - (cpConstraintPreStepImpl)preStep, - (cpConstraintApplyCachedImpulseImpl)applyCachedImpulse, - (cpConstraintApplyImpulseImpl)applyImpulse, - (cpConstraintGetImpulseImpl)getImpulse, -}; - -cpRatchetJoint * -cpRatchetJointAlloc(void) -{ - return (cpRatchetJoint *)cpcalloc(1, sizeof(cpRatchetJoint)); -} - -cpRatchetJoint * -cpRatchetJointInit(cpRatchetJoint *joint, cpBody *a, cpBody *b, cpFloat phase, cpFloat ratchet) -{ - cpConstraintInit((cpConstraint *)joint, &klass, a, b); - - joint->angle = 0.0f; - joint->phase = phase; - joint->ratchet = ratchet; - - // STATIC_BODY_CHECK - joint->angle = (b ? b->a : 0.0f) - (a ? a->a : 0.0f); - - return joint; -} - -cpConstraint * -cpRatchetJointNew(cpBody *a, cpBody *b, cpFloat phase, cpFloat ratchet) -{ - return (cpConstraint *)cpRatchetJointInit(cpRatchetJointAlloc(), a, b, phase, ratchet); -} - -cpBool -cpConstraintIsRatchetJoint(const cpConstraint *constraint) -{ - return (constraint->klass == &klass); -} - -cpFloat -cpRatchetJointGetAngle(const cpConstraint *constraint) -{ - cpAssertHard(cpConstraintIsRatchetJoint(constraint), "Constraint is not a ratchet joint."); - return ((cpRatchetJoint *)constraint)->angle; -} - -void -cpRatchetJointSetAngle(cpConstraint *constraint, cpFloat angle) -{ - cpAssertHard(cpConstraintIsRatchetJoint(constraint), "Constraint is not a ratchet joint."); - cpConstraintActivateBodies(constraint); - ((cpRatchetJoint *)constraint)->angle = angle; -} - -cpFloat -cpRatchetJointGetPhase(const cpConstraint *constraint) -{ - cpAssertHard(cpConstraintIsRatchetJoint(constraint), "Constraint is not a ratchet joint."); - return ((cpRatchetJoint *)constraint)->phase; -} - -void -cpRatchetJointSetPhase(cpConstraint *constraint, cpFloat phase) -{ - cpAssertHard(cpConstraintIsRatchetJoint(constraint), "Constraint is not a ratchet joint."); - cpConstraintActivateBodies(constraint); - ((cpRatchetJoint *)constraint)->phase = phase; -} -cpFloat -cpRatchetJointGetRatchet(const cpConstraint *constraint) -{ - cpAssertHard(cpConstraintIsRatchetJoint(constraint), "Constraint is not a ratchet joint."); - return ((cpRatchetJoint *)constraint)->ratchet; -} - -void -cpRatchetJointSetRatchet(cpConstraint *constraint, cpFloat ratchet) -{ - cpAssertHard(cpConstraintIsRatchetJoint(constraint), "Constraint is not a ratchet joint."); - cpConstraintActivateBodies(constraint); - ((cpRatchetJoint *)constraint)->ratchet = ratchet; -} diff --git a/apecs-physics/Chipmunk2D/src/cpRobust.c b/apecs-physics/Chipmunk2D/src/cpRobust.c deleted file mode 100644 index 12754ab..0000000 --- a/apecs-physics/Chipmunk2D/src/cpRobust.c +++ /dev/null @@ -1,13 +0,0 @@ -#include "cpRobust.h" - - -cpBool -cpCheckPointGreater(const cpVect a, const cpVect b, const cpVect c) -{ - return (b.y - a.y)*(a.x + b.x - 2*c.x) > (b.x - a.x)*(a.y + b.y - 2*c.y); -} - -cpBool -cpCheckAxis(cpVect v0, cpVect v1, cpVect p, cpVect n){ - return cpvdot(p, n) <= cpfmax(cpvdot(v0, n), cpvdot(v1, n)); -} diff --git a/apecs-physics/Chipmunk2D/src/cpRotaryLimitJoint.c b/apecs-physics/Chipmunk2D/src/cpRotaryLimitJoint.c deleted file mode 100644 index 815c5f6..0000000 --- a/apecs-physics/Chipmunk2D/src/cpRotaryLimitJoint.c +++ /dev/null @@ -1,160 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "chipmunk_private.h" - -static void -preStep(cpRotaryLimitJoint *joint, cpFloat dt) -{ - cpBody *a = joint->constraint.a; - cpBody *b = joint->constraint.b; - - cpFloat dist = b->a - a->a; - cpFloat pdist = 0.0f; - if(dist > joint->max) { - pdist = joint->max - dist; - } else if(dist < joint->min) { - pdist = joint->min - dist; - } - - // calculate moment of inertia coefficient. - joint->iSum = 1.0f/(a->i_inv + b->i_inv); - - // calculate bias velocity - cpFloat maxBias = joint->constraint.maxBias; - joint->bias = cpfclamp(-bias_coef(joint->constraint.errorBias, dt)*pdist/dt, -maxBias, maxBias); - - // If the bias is 0, the joint is not at a limit. Reset the impulse. - if(!joint->bias) joint->jAcc = 0.0f; -} - -static void -applyCachedImpulse(cpRotaryLimitJoint *joint, cpFloat dt_coef) -{ - cpBody *a = joint->constraint.a; - cpBody *b = joint->constraint.b; - - cpFloat j = joint->jAcc*dt_coef; - a->w -= j*a->i_inv; - b->w += j*b->i_inv; -} - -static void -applyImpulse(cpRotaryLimitJoint *joint, cpFloat dt) -{ - if(!joint->bias) return; // early exit - - cpBody *a = joint->constraint.a; - cpBody *b = joint->constraint.b; - - // compute relative rotational velocity - cpFloat wr = b->w - a->w; - - cpFloat jMax = joint->constraint.maxForce*dt; - - // compute normal impulse - cpFloat j = -(joint->bias + wr)*joint->iSum; - cpFloat jOld = joint->jAcc; - if(joint->bias < 0.0f){ - joint->jAcc = cpfclamp(jOld + j, 0.0f, jMax); - } else { - joint->jAcc = cpfclamp(jOld + j, -jMax, 0.0f); - } - j = joint->jAcc - jOld; - - // apply impulse - a->w -= j*a->i_inv; - b->w += j*b->i_inv; -} - -static cpFloat -getImpulse(cpRotaryLimitJoint *joint) -{ - return cpfabs(joint->jAcc); -} - -static const cpConstraintClass klass = { - (cpConstraintPreStepImpl)preStep, - (cpConstraintApplyCachedImpulseImpl)applyCachedImpulse, - (cpConstraintApplyImpulseImpl)applyImpulse, - (cpConstraintGetImpulseImpl)getImpulse, -}; - -cpRotaryLimitJoint * -cpRotaryLimitJointAlloc(void) -{ - return (cpRotaryLimitJoint *)cpcalloc(1, sizeof(cpRotaryLimitJoint)); -} - -cpRotaryLimitJoint * -cpRotaryLimitJointInit(cpRotaryLimitJoint *joint, cpBody *a, cpBody *b, cpFloat min, cpFloat max) -{ - cpConstraintInit((cpConstraint *)joint, &klass, a, b); - - joint->min = min; - joint->max = max; - - joint->jAcc = 0.0f; - - return joint; -} - -cpConstraint * -cpRotaryLimitJointNew(cpBody *a, cpBody *b, cpFloat min, cpFloat max) -{ - return (cpConstraint *)cpRotaryLimitJointInit(cpRotaryLimitJointAlloc(), a, b, min, max); -} - -cpBool -cpConstraintIsRotaryLimitJoint(const cpConstraint *constraint) -{ - return (constraint->klass == &klass); -} - -cpFloat -cpRotaryLimitJointGetMin(const cpConstraint *constraint) -{ - cpAssertHard(cpConstraintIsRotaryLimitJoint(constraint), "Constraint is not a rotary limit joint."); - return ((cpRotaryLimitJoint *)constraint)->min; -} - -void -cpRotaryLimitJointSetMin(cpConstraint *constraint, cpFloat min) -{ - cpAssertHard(cpConstraintIsRotaryLimitJoint(constraint), "Constraint is not a rotary limit joint."); - cpConstraintActivateBodies(constraint); - ((cpRotaryLimitJoint *)constraint)->min = min; -} - -cpFloat -cpRotaryLimitJointGetMax(const cpConstraint *constraint) -{ - cpAssertHard(cpConstraintIsRotaryLimitJoint(constraint), "Constraint is not a rotary limit joint."); - return ((cpRotaryLimitJoint *)constraint)->max; -} - -void -cpRotaryLimitJointSetMax(cpConstraint *constraint, cpFloat max) -{ - cpAssertHard(cpConstraintIsRotaryLimitJoint(constraint), "Constraint is not a rotary limit joint."); - cpConstraintActivateBodies(constraint); - ((cpRotaryLimitJoint *)constraint)->max = max; -} diff --git a/apecs-physics/Chipmunk2D/src/cpShape.c b/apecs-physics/Chipmunk2D/src/cpShape.c deleted file mode 100644 index 76aaaeb..0000000 --- a/apecs-physics/Chipmunk2D/src/cpShape.c +++ /dev/null @@ -1,603 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "chipmunk_private.h" -#include "chipmunk_unsafe.h" - -#define CP_DefineShapeGetter(struct, type, member, name) \ -CP_DeclareShapeGetter(struct, type, name){ \ - cpAssertHard(shape->klass == &struct##Class, "shape is not a "#struct); \ - return ((struct *)shape)->member; \ -} - -cpShape * -cpShapeInit(cpShape *shape, const cpShapeClass *klass, cpBody *body, struct cpShapeMassInfo massInfo) -{ - shape->klass = klass; - - shape->body = body; - shape->massInfo = massInfo; - - shape->sensor = 0; - - shape->e = 0.0f; - shape->u = 0.0f; - shape->surfaceV = cpvzero; - - shape->type = 0; - shape->filter.group = CP_NO_GROUP; - shape->filter.categories = CP_ALL_CATEGORIES; - shape->filter.mask = CP_ALL_CATEGORIES; - - shape->userData = NULL; - - shape->space = NULL; - - shape->next = NULL; - shape->prev = NULL; - - return shape; -} - -void -cpShapeDestroy(cpShape *shape) -{ - if(shape->klass && shape->klass->destroy) shape->klass->destroy(shape); -} - -void -cpShapeFree(cpShape *shape) -{ - if(shape){ - cpShapeDestroy(shape); - cpfree(shape); - } -} - -cpSpace * -cpShapeGetSpace(const cpShape *shape) -{ - return shape->space; -} - -cpBody * -cpShapeGetBody(const cpShape *shape) -{ - return shape->body; -} - -void -cpShapeSetBody(cpShape *shape, cpBody *body) -{ - cpAssertHard(!cpShapeActive(shape), "You cannot change the body on an active shape. You must remove the shape from the space before changing the body."); - shape->body = body; -} - -cpFloat cpShapeGetMass(cpShape *shape){ return shape->massInfo.m; } - -void -cpShapeSetMass(cpShape *shape, cpFloat mass){ - cpBody *body = shape->body; - cpBodyActivate(body); - - shape->massInfo.m = mass; - cpBodyAccumulateMassFromShapes(body); -} - -cpFloat cpShapeGetDensity(cpShape *shape){ return shape->massInfo.m/shape->massInfo.area; } -void cpShapeSetDensity(cpShape *shape, cpFloat density){ cpShapeSetMass(shape, density*shape->massInfo.area); } - -cpFloat cpShapeGetMoment(cpShape *shape){ return shape->massInfo.m*shape->massInfo.i; } -cpFloat cpShapeGetArea(cpShape *shape){ return shape->massInfo.area; } -cpVect cpShapeGetCenterOfGravity(cpShape *shape) { return shape->massInfo.cog; } - -cpBB -cpShapeGetBB(const cpShape *shape) -{ - return shape->bb; -} - -cpBool -cpShapeGetSensor(const cpShape *shape) -{ - return shape->sensor; -} - -void -cpShapeSetSensor(cpShape *shape, cpBool sensor) -{ - cpBodyActivate(shape->body); - shape->sensor = sensor; -} - -cpFloat -cpShapeGetElasticity(const cpShape *shape) -{ - return shape->e; -} - -void -cpShapeSetElasticity(cpShape *shape, cpFloat elasticity) -{ - cpAssertHard(elasticity >= 0.0f, "Elasticity must be positive."); - cpBodyActivate(shape->body); - shape->e = elasticity; -} - -cpFloat -cpShapeGetFriction(const cpShape *shape) -{ - return shape->u; -} - -void -cpShapeSetFriction(cpShape *shape, cpFloat friction) -{ - cpAssertHard(friction >= 0.0f, "Friction must be postive."); - cpBodyActivate(shape->body); - shape->u = friction; -} - -cpVect -cpShapeGetSurfaceVelocity(const cpShape *shape) -{ - return shape->surfaceV; -} - -void -cpShapeSetSurfaceVelocity(cpShape *shape, cpVect surfaceVelocity) -{ - cpBodyActivate(shape->body); - shape->surfaceV = surfaceVelocity; -} - -cpDataPointer -cpShapeGetUserData(const cpShape *shape) -{ - return shape->userData; -} - -void -cpShapeSetUserData(cpShape *shape, cpDataPointer userData) -{ - shape->userData = userData; -} - -cpCollisionType -cpShapeGetCollisionType(const cpShape *shape) -{ - return shape->type; -} - -void -cpShapeSetCollisionType(cpShape *shape, cpCollisionType collisionType) -{ - cpBodyActivate(shape->body); - shape->type = collisionType; -} - -cpShapeFilter -cpShapeGetFilter(const cpShape *shape) -{ - return shape->filter; -} - -void -cpShapeSetFilter(cpShape *shape, cpShapeFilter filter) -{ - cpBodyActivate(shape->body); - shape->filter = filter; -} - -cpBB -cpShapeCacheBB(cpShape *shape) -{ - return cpShapeUpdate(shape, shape->body->transform); -} - -cpBB -cpShapeUpdate(cpShape *shape, cpTransform transform) -{ - return (shape->bb = shape->klass->cacheData(shape, transform)); -} - -cpFloat -cpShapePointQuery(const cpShape *shape, cpVect p, cpPointQueryInfo *info) -{ - cpPointQueryInfo blank = {NULL, cpvzero, INFINITY, cpvzero}; - if(info){ - (*info) = blank; - } else { - info = ␣ - } - - shape->klass->pointQuery(shape, p, info); - return info->distance; -} - - -cpBool -cpShapeSegmentQuery(const cpShape *shape, cpVect a, cpVect b, cpFloat radius, cpSegmentQueryInfo *info){ - cpSegmentQueryInfo blank = {NULL, b, cpvzero, 1.0f}; - if(info){ - (*info) = blank; - } else { - info = ␣ - } - - cpPointQueryInfo nearest; - shape->klass->pointQuery(shape, a, &nearest); - if(nearest.distance <= radius){ - info->shape = shape; - info->alpha = 0.0; - info->normal = cpvnormalize(cpvsub(a, nearest.point)); - } else { - shape->klass->segmentQuery(shape, a, b, radius, info); - } - - return (info->shape != NULL); -} - -cpContactPointSet -cpShapesCollide(const cpShape *a, const cpShape *b) -{ - struct cpContact contacts[CP_MAX_CONTACTS_PER_ARBITER]; - struct cpCollisionInfo info = cpCollide(a, b, 0, contacts); - - cpContactPointSet set; - set.count = info.count; - - // cpCollideShapes() may have swapped the contact order. Flip the normal. - cpBool swapped = (a != info.a); - set.normal = (swapped ? cpvneg(info.n) : info.n); - - for(int i=0; itc = cpTransformPoint(transform, circle->c); - return cpBBNewForCircle(c, circle->r); -} - -static void -cpCircleShapePointQuery(cpCircleShape *circle, cpVect p, cpPointQueryInfo *info) -{ - cpVect delta = cpvsub(p, circle->tc); - cpFloat d = cpvlength(delta); - cpFloat r = circle->r; - - info->shape = (cpShape *)circle; - info->point = cpvadd(circle->tc, cpvmult(delta, r/d)); // TODO: div/0 - info->distance = d - r; - - // Use up for the gradient if the distance is very small. - info->gradient = (d > MAGIC_EPSILON ? cpvmult(delta, 1.0f/d) : cpv(0.0f, 1.0f)); -} - -static void -cpCircleShapeSegmentQuery(cpCircleShape *circle, cpVect a, cpVect b, cpFloat radius, cpSegmentQueryInfo *info) -{ - CircleSegmentQuery((cpShape *)circle, circle->tc, circle->r, a, b, radius, info); -} - -static struct cpShapeMassInfo -cpCircleShapeMassInfo(cpFloat mass, cpFloat radius, cpVect center) -{ - struct cpShapeMassInfo info = { - mass, cpMomentForCircle(1.0f, 0.0f, radius, cpvzero), - center, - cpAreaForCircle(0.0f, radius), - }; - - return info; -} - -static const cpShapeClass cpCircleShapeClass = { - CP_CIRCLE_SHAPE, - (cpShapeCacheDataImpl)cpCircleShapeCacheData, - NULL, - (cpShapePointQueryImpl)cpCircleShapePointQuery, - (cpShapeSegmentQueryImpl)cpCircleShapeSegmentQuery, -}; - -cpCircleShape * -cpCircleShapeInit(cpCircleShape *circle, cpBody *body, cpFloat radius, cpVect offset) -{ - circle->c = offset; - circle->r = radius; - - cpShapeInit((cpShape *)circle, &cpCircleShapeClass, body, cpCircleShapeMassInfo(0.0f, radius, offset)); - - return circle; -} - -cpShape * -cpCircleShapeNew(cpBody *body, cpFloat radius, cpVect offset) -{ - return (cpShape *)cpCircleShapeInit(cpCircleShapeAlloc(), body, radius, offset); -} - -cpVect -cpCircleShapeGetOffset(const cpShape *shape) -{ - cpAssertHard(shape->klass == &cpCircleShapeClass, "Shape is not a circle shape."); - return ((cpCircleShape *)shape)->c; -} - -cpFloat -cpCircleShapeGetRadius(const cpShape *shape) -{ - cpAssertHard(shape->klass == &cpCircleShapeClass, "Shape is not a circle shape."); - return ((cpCircleShape *)shape)->r; -} - - -cpSegmentShape * -cpSegmentShapeAlloc(void) -{ - return (cpSegmentShape *)cpcalloc(1, sizeof(cpSegmentShape)); -} - -static cpBB -cpSegmentShapeCacheData(cpSegmentShape *seg, cpTransform transform) -{ - seg->ta = cpTransformPoint(transform, seg->a); - seg->tb = cpTransformPoint(transform, seg->b); - seg->tn = cpTransformVect(transform, seg->n); - - cpFloat l,r,b,t; - - if(seg->ta.x < seg->tb.x){ - l = seg->ta.x; - r = seg->tb.x; - } else { - l = seg->tb.x; - r = seg->ta.x; - } - - if(seg->ta.y < seg->tb.y){ - b = seg->ta.y; - t = seg->tb.y; - } else { - b = seg->tb.y; - t = seg->ta.y; - } - - cpFloat rad = seg->r; - return cpBBNew(l - rad, b - rad, r + rad, t + rad); -} - -static void -cpSegmentShapePointQuery(cpSegmentShape *seg, cpVect p, cpPointQueryInfo *info) -{ - cpVect closest = cpClosetPointOnSegment(p, seg->ta, seg->tb); - - cpVect delta = cpvsub(p, closest); - cpFloat d = cpvlength(delta); - cpFloat r = seg->r; - cpVect g = cpvmult(delta, 1.0f/d); - - info->shape = (cpShape *)seg; - info->point = (d ? cpvadd(closest, cpvmult(g, r)) : closest); - info->distance = d - r; - - // Use the segment's normal if the distance is very small. - info->gradient = (d > MAGIC_EPSILON ? g : seg->n); -} - -static void -cpSegmentShapeSegmentQuery(cpSegmentShape *seg, cpVect a, cpVect b, cpFloat r2, cpSegmentQueryInfo *info) -{ - cpVect n = seg->tn; - cpFloat d = cpvdot(cpvsub(seg->ta, a), n); - cpFloat r = seg->r + r2; - - cpVect flipped_n = (d > 0.0f ? cpvneg(n) : n); - cpVect seg_offset = cpvsub(cpvmult(flipped_n, r), a); - - // Make the endpoints relative to 'a' and move them by the thickness of the segment. - cpVect seg_a = cpvadd(seg->ta, seg_offset); - cpVect seg_b = cpvadd(seg->tb, seg_offset); - cpVect delta = cpvsub(b, a); - - if(cpvcross(delta, seg_a)*cpvcross(delta, seg_b) <= 0.0f){ - cpFloat d_offset = d + (d > 0.0f ? -r : r); - cpFloat ad = -d_offset; - cpFloat bd = cpvdot(delta, n) - d_offset; - - if(ad*bd < 0.0f){ - cpFloat t = ad/(ad - bd); - - info->shape = (cpShape *)seg; - info->point = cpvsub(cpvlerp(a, b, t), cpvmult(flipped_n, r2)); - info->normal = flipped_n; - info->alpha = t; - } - } else if(r != 0.0f){ - cpSegmentQueryInfo info1 = {NULL, b, cpvzero, 1.0f}; - cpSegmentQueryInfo info2 = {NULL, b, cpvzero, 1.0f}; - CircleSegmentQuery((cpShape *)seg, seg->ta, seg->r, a, b, r2, &info1); - CircleSegmentQuery((cpShape *)seg, seg->tb, seg->r, a, b, r2, &info2); - - if(info1.alpha < info2.alpha){ - (*info) = info1; - } else { - (*info) = info2; - } - } -} - -static struct cpShapeMassInfo -cpSegmentShapeMassInfo(cpFloat mass, cpVect a, cpVect b, cpFloat r) -{ - struct cpShapeMassInfo info = { - mass, cpMomentForBox(1.0f, cpvdist(a, b) + 2.0f*r, 2.0f*r), // TODO is an approximation. - cpvlerp(a, b, 0.5f), - cpAreaForSegment(a, b, r), - }; - - return info; -} - -static const cpShapeClass cpSegmentShapeClass = { - CP_SEGMENT_SHAPE, - (cpShapeCacheDataImpl)cpSegmentShapeCacheData, - NULL, - (cpShapePointQueryImpl)cpSegmentShapePointQuery, - (cpShapeSegmentQueryImpl)cpSegmentShapeSegmentQuery, -}; - -cpSegmentShape * -cpSegmentShapeInit(cpSegmentShape *seg, cpBody *body, cpVect a, cpVect b, cpFloat r) -{ - seg->a = a; - seg->b = b; - seg->n = cpvrperp(cpvnormalize(cpvsub(b, a))); - - seg->r = r; - - seg->a_tangent = cpvzero; - seg->b_tangent = cpvzero; - - cpShapeInit((cpShape *)seg, &cpSegmentShapeClass, body, cpSegmentShapeMassInfo(0.0f, a, b, r)); - - return seg; -} - -cpShape* -cpSegmentShapeNew(cpBody *body, cpVect a, cpVect b, cpFloat r) -{ - return (cpShape *)cpSegmentShapeInit(cpSegmentShapeAlloc(), body, a, b, r); -} - -cpVect -cpSegmentShapeGetA(const cpShape *shape) -{ - cpAssertHard(shape->klass == &cpSegmentShapeClass, "Shape is not a segment shape."); - return ((cpSegmentShape *)shape)->a; -} - -cpVect -cpSegmentShapeGetB(const cpShape *shape) -{ - cpAssertHard(shape->klass == &cpSegmentShapeClass, "Shape is not a segment shape."); - return ((cpSegmentShape *)shape)->b; -} - -cpVect -cpSegmentShapeGetNormal(const cpShape *shape) -{ - cpAssertHard(shape->klass == &cpSegmentShapeClass, "Shape is not a segment shape."); - return ((cpSegmentShape *)shape)->n; -} - -cpFloat -cpSegmentShapeGetRadius(const cpShape *shape) -{ - cpAssertHard(shape->klass == &cpSegmentShapeClass, "Shape is not a segment shape."); - return ((cpSegmentShape *)shape)->r; -} - -void -cpSegmentShapeSetNeighbors(cpShape *shape, cpVect prev, cpVect next) -{ - cpAssertHard(shape->klass == &cpSegmentShapeClass, "Shape is not a segment shape."); - cpSegmentShape *seg = (cpSegmentShape *)shape; - - seg->a_tangent = cpvsub(prev, seg->a); - seg->b_tangent = cpvsub(next, seg->b); -} - -// Unsafe API (chipmunk_unsafe.h) - -// TODO setters should wake the shape up? - -void -cpCircleShapeSetRadius(cpShape *shape, cpFloat radius) -{ - cpAssertHard(shape->klass == &cpCircleShapeClass, "Shape is not a circle shape."); - cpCircleShape *circle = (cpCircleShape *)shape; - - circle->r = radius; - - cpFloat mass = shape->massInfo.m; - shape->massInfo = cpCircleShapeMassInfo(mass, circle->r, circle->c); - if(mass > 0.0f) cpBodyAccumulateMassFromShapes(shape->body); -} - -void -cpCircleShapeSetOffset(cpShape *shape, cpVect offset) -{ - cpAssertHard(shape->klass == &cpCircleShapeClass, "Shape is not a circle shape."); - cpCircleShape *circle = (cpCircleShape *)shape; - - circle->c = offset; - - cpFloat mass = shape->massInfo.m; - shape->massInfo = cpCircleShapeMassInfo(shape->massInfo.m, circle->r, circle->c); - if(mass > 0.0f) cpBodyAccumulateMassFromShapes(shape->body); -} - -void -cpSegmentShapeSetEndpoints(cpShape *shape, cpVect a, cpVect b) -{ - cpAssertHard(shape->klass == &cpSegmentShapeClass, "Shape is not a segment shape."); - cpSegmentShape *seg = (cpSegmentShape *)shape; - - seg->a = a; - seg->b = b; - seg->n = cpvperp(cpvnormalize(cpvsub(b, a))); - - cpFloat mass = shape->massInfo.m; - shape->massInfo = cpSegmentShapeMassInfo(shape->massInfo.m, seg->a, seg->b, seg->r); - if(mass > 0.0f) cpBodyAccumulateMassFromShapes(shape->body); -} - -void -cpSegmentShapeSetRadius(cpShape *shape, cpFloat radius) -{ - cpAssertHard(shape->klass == &cpSegmentShapeClass, "Shape is not a segment shape."); - cpSegmentShape *seg = (cpSegmentShape *)shape; - - seg->r = radius; - - cpFloat mass = shape->massInfo.m; - shape->massInfo = cpSegmentShapeMassInfo(shape->massInfo.m, seg->a, seg->b, seg->r); - if(mass > 0.0f) cpBodyAccumulateMassFromShapes(shape->body); -} diff --git a/apecs-physics/Chipmunk2D/src/cpSimpleMotor.c b/apecs-physics/Chipmunk2D/src/cpSimpleMotor.c deleted file mode 100644 index f58a07e..0000000 --- a/apecs-physics/Chipmunk2D/src/cpSimpleMotor.c +++ /dev/null @@ -1,123 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "chipmunk_private.h" - -static void -preStep(cpSimpleMotor *joint, cpFloat dt) -{ - cpBody *a = joint->constraint.a; - cpBody *b = joint->constraint.b; - - // calculate moment of inertia coefficient. - joint->iSum = 1.0f/(a->i_inv + b->i_inv); -} - -static void -applyCachedImpulse(cpSimpleMotor *joint, cpFloat dt_coef) -{ - cpBody *a = joint->constraint.a; - cpBody *b = joint->constraint.b; - - cpFloat j = joint->jAcc*dt_coef; - a->w -= j*a->i_inv; - b->w += j*b->i_inv; -} - -static void -applyImpulse(cpSimpleMotor *joint, cpFloat dt) -{ - cpBody *a = joint->constraint.a; - cpBody *b = joint->constraint.b; - - // compute relative rotational velocity - cpFloat wr = b->w - a->w + joint->rate; - - cpFloat jMax = joint->constraint.maxForce*dt; - - // compute normal impulse - cpFloat j = -wr*joint->iSum; - cpFloat jOld = joint->jAcc; - joint->jAcc = cpfclamp(jOld + j, -jMax, jMax); - j = joint->jAcc - jOld; - - // apply impulse - a->w -= j*a->i_inv; - b->w += j*b->i_inv; -} - -static cpFloat -getImpulse(cpSimpleMotor *joint) -{ - return cpfabs(joint->jAcc); -} - -static const cpConstraintClass klass = { - (cpConstraintPreStepImpl)preStep, - (cpConstraintApplyCachedImpulseImpl)applyCachedImpulse, - (cpConstraintApplyImpulseImpl)applyImpulse, - (cpConstraintGetImpulseImpl)getImpulse, -}; - -cpSimpleMotor * -cpSimpleMotorAlloc(void) -{ - return (cpSimpleMotor *)cpcalloc(1, sizeof(cpSimpleMotor)); -} - -cpSimpleMotor * -cpSimpleMotorInit(cpSimpleMotor *joint, cpBody *a, cpBody *b, cpFloat rate) -{ - cpConstraintInit((cpConstraint *)joint, &klass, a, b); - - joint->rate = rate; - - joint->jAcc = 0.0f; - - return joint; -} - -cpConstraint * -cpSimpleMotorNew(cpBody *a, cpBody *b, cpFloat rate) -{ - return (cpConstraint *)cpSimpleMotorInit(cpSimpleMotorAlloc(), a, b, rate); -} - -cpBool -cpConstraintIsSimpleMotor(const cpConstraint *constraint) -{ - return (constraint->klass == &klass); -} - -cpFloat -cpSimpleMotorGetRate(const cpConstraint *constraint) -{ - cpAssertHard(cpConstraintIsSimpleMotor(constraint), "Constraint is not a pin joint."); - return ((cpSimpleMotor *)constraint)->rate; -} - -void -cpSimpleMotorSetRate(cpConstraint *constraint, cpFloat rate) -{ - cpAssertHard(cpConstraintIsSimpleMotor(constraint), "Constraint is not a pin joint."); - cpConstraintActivateBodies(constraint); - ((cpSimpleMotor *)constraint)->rate = rate; -} diff --git a/apecs-physics/Chipmunk2D/src/cpSlideJoint.c b/apecs-physics/Chipmunk2D/src/cpSlideJoint.c deleted file mode 100644 index 9dbbfa8..0000000 --- a/apecs-physics/Chipmunk2D/src/cpSlideJoint.c +++ /dev/null @@ -1,195 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "chipmunk_private.h" - -static void -preStep(cpSlideJoint *joint, cpFloat dt) -{ - cpBody *a = joint->constraint.a; - cpBody *b = joint->constraint.b; - - joint->r1 = cpTransformVect(a->transform, cpvsub(joint->anchorA, a->cog)); - joint->r2 = cpTransformVect(b->transform, cpvsub(joint->anchorB, b->cog)); - - cpVect delta = cpvsub(cpvadd(b->p, joint->r2), cpvadd(a->p, joint->r1)); - cpFloat dist = cpvlength(delta); - cpFloat pdist = 0.0f; - if(dist > joint->max) { - pdist = dist - joint->max; - joint->n = cpvnormalize(delta); - } else if(dist < joint->min) { - pdist = joint->min - dist; - joint->n = cpvneg(cpvnormalize(delta)); - } else { - joint->n = cpvzero; - joint->jnAcc = 0.0f; - } - - // calculate mass normal - joint->nMass = 1.0f/k_scalar(a, b, joint->r1, joint->r2, joint->n); - - // calculate bias velocity - cpFloat maxBias = joint->constraint.maxBias; - joint->bias = cpfclamp(-bias_coef(joint->constraint.errorBias, dt)*pdist/dt, -maxBias, maxBias); -} - -static void -applyCachedImpulse(cpSlideJoint *joint, cpFloat dt_coef) -{ - cpBody *a = joint->constraint.a; - cpBody *b = joint->constraint.b; - - cpVect j = cpvmult(joint->n, joint->jnAcc*dt_coef); - apply_impulses(a, b, joint->r1, joint->r2, j); -} - -static void -applyImpulse(cpSlideJoint *joint, cpFloat dt) -{ - if(cpveql(joint->n, cpvzero)) return; // early exit - - cpBody *a = joint->constraint.a; - cpBody *b = joint->constraint.b; - - cpVect n = joint->n; - cpVect r1 = joint->r1; - cpVect r2 = joint->r2; - - // compute relative velocity - cpVect vr = relative_velocity(a, b, r1, r2); - cpFloat vrn = cpvdot(vr, n); - - // compute normal impulse - cpFloat jn = (joint->bias - vrn)*joint->nMass; - cpFloat jnOld = joint->jnAcc; - joint->jnAcc = cpfclamp(jnOld + jn, -joint->constraint.maxForce*dt, 0.0f); - jn = joint->jnAcc - jnOld; - - // apply impulse - apply_impulses(a, b, joint->r1, joint->r2, cpvmult(n, jn)); -} - -static cpFloat -getImpulse(cpConstraint *joint) -{ - return cpfabs(((cpSlideJoint *)joint)->jnAcc); -} - -static const cpConstraintClass klass = { - (cpConstraintPreStepImpl)preStep, - (cpConstraintApplyCachedImpulseImpl)applyCachedImpulse, - (cpConstraintApplyImpulseImpl)applyImpulse, - (cpConstraintGetImpulseImpl)getImpulse, -}; - -cpSlideJoint * -cpSlideJointAlloc(void) -{ - return (cpSlideJoint *)cpcalloc(1, sizeof(cpSlideJoint)); -} - -cpSlideJoint * -cpSlideJointInit(cpSlideJoint *joint, cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB, cpFloat min, cpFloat max) -{ - cpConstraintInit((cpConstraint *)joint, &klass, a, b); - - joint->anchorA = anchorA; - joint->anchorB = anchorB; - joint->min = min; - joint->max = max; - - joint->jnAcc = 0.0f; - - return joint; -} - -cpConstraint * -cpSlideJointNew(cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB, cpFloat min, cpFloat max) -{ - return (cpConstraint *)cpSlideJointInit(cpSlideJointAlloc(), a, b, anchorA, anchorB, min, max); -} - -cpBool -cpConstraintIsSlideJoint(const cpConstraint *constraint) -{ - return (constraint->klass == &klass); -} - -cpVect -cpSlideJointGetAnchorA(const cpConstraint *constraint) -{ - cpAssertHard(cpConstraintIsSlideJoint(constraint), "Constraint is not a slide joint."); - return ((cpSlideJoint *)constraint)->anchorA; -} - -void -cpSlideJointSetAnchorA(cpConstraint *constraint, cpVect anchorA) -{ - cpAssertHard(cpConstraintIsSlideJoint(constraint), "Constraint is not a slide joint."); - cpConstraintActivateBodies(constraint); - ((cpSlideJoint *)constraint)->anchorA = anchorA; -} - -cpVect -cpSlideJointGetAnchorB(const cpConstraint *constraint) -{ - cpAssertHard(cpConstraintIsSlideJoint(constraint), "Constraint is not a slide joint."); - return ((cpSlideJoint *)constraint)->anchorB; -} - -void -cpSlideJointSetAnchorB(cpConstraint *constraint, cpVect anchorB) -{ - cpAssertHard(cpConstraintIsSlideJoint(constraint), "Constraint is not a slide joint."); - cpConstraintActivateBodies(constraint); - ((cpSlideJoint *)constraint)->anchorB = anchorB; -} - -cpFloat -cpSlideJointGetMin(const cpConstraint *constraint) -{ - cpAssertHard(cpConstraintIsSlideJoint(constraint), "Constraint is not a slide joint."); - return ((cpSlideJoint *)constraint)->min; -} - -void -cpSlideJointSetMin(cpConstraint *constraint, cpFloat min) -{ - cpAssertHard(cpConstraintIsSlideJoint(constraint), "Constraint is not a slide joint."); - cpConstraintActivateBodies(constraint); - ((cpSlideJoint *)constraint)->min = min; -} - -cpFloat -cpSlideJointGetMax(const cpConstraint *constraint) -{ - cpAssertHard(cpConstraintIsSlideJoint(constraint), "Constraint is not a slide joint."); - return ((cpSlideJoint *)constraint)->max; -} - -void -cpSlideJointSetMax(cpConstraint *constraint, cpFloat max) -{ - cpAssertHard(cpConstraintIsSlideJoint(constraint), "Constraint is not a slide joint."); - cpConstraintActivateBodies(constraint); - ((cpSlideJoint *)constraint)->max = max; -} diff --git a/apecs-physics/Chipmunk2D/src/cpSpace.c b/apecs-physics/Chipmunk2D/src/cpSpace.c deleted file mode 100644 index 2a2a6b4..0000000 --- a/apecs-physics/Chipmunk2D/src/cpSpace.c +++ /dev/null @@ -1,701 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include - -#include "chipmunk_private.h" - - -//MARK: Contact Set Helpers - -// Equal function for arbiterSet. -static cpBool -arbiterSetEql(cpShape **shapes, cpArbiter *arb) -{ - cpShape *a = shapes[0]; - cpShape *b = shapes[1]; - - return ((a == arb->a && b == arb->b) || (b == arb->a && a == arb->b)); -} - -//MARK: Collision Handler Set HelperFunctions - -// Equals function for collisionHandlers. -static cpBool -handlerSetEql(cpCollisionHandler *check, cpCollisionHandler *pair) -{ - return ( - (check->typeA == pair->typeA && check->typeB == pair->typeB) || - (check->typeB == pair->typeA && check->typeA == pair->typeB) - ); -} - -// Transformation function for collisionHandlers. -static void * -handlerSetTrans(cpCollisionHandler *handler, void *unused) -{ - cpCollisionHandler *copy = (cpCollisionHandler *)cpcalloc(1, sizeof(cpCollisionHandler)); - memcpy(copy, handler, sizeof(cpCollisionHandler)); - - return copy; -} - -//MARK: Misc Helper Funcs - -// Default collision functions. - -static cpBool -DefaultBegin(cpArbiter *arb, cpSpace *space, void *data){ - cpBool retA = cpArbiterCallWildcardBeginA(arb, space); - cpBool retB = cpArbiterCallWildcardBeginB(arb, space); - return retA && retB; -} - -static cpBool -DefaultPreSolve(cpArbiter *arb, cpSpace *space, void *data){ - cpBool retA = cpArbiterCallWildcardPreSolveA(arb, space); - cpBool retB = cpArbiterCallWildcardPreSolveB(arb, space); - return retA && retB; -} - -static void -DefaultPostSolve(cpArbiter *arb, cpSpace *space, void *data){ - cpArbiterCallWildcardPostSolveA(arb, space); - cpArbiterCallWildcardPostSolveB(arb, space); -} - -static void -DefaultSeparate(cpArbiter *arb, cpSpace *space, void *data){ - cpArbiterCallWildcardSeparateA(arb, space); - cpArbiterCallWildcardSeparateB(arb, space); -} - -// Use the wildcard identifier since the default handler should never match any type pair. -static cpCollisionHandler cpCollisionHandlerDefault = { - CP_WILDCARD_COLLISION_TYPE, CP_WILDCARD_COLLISION_TYPE, - DefaultBegin, DefaultPreSolve, DefaultPostSolve, DefaultSeparate, NULL -}; - -static cpBool AlwaysCollide(cpArbiter *arb, cpSpace *space, void *data){return cpTrue;} -static void DoNothing(cpArbiter *arb, cpSpace *space, void *data){} - -cpCollisionHandler cpCollisionHandlerDoNothing = { - CP_WILDCARD_COLLISION_TYPE, CP_WILDCARD_COLLISION_TYPE, - AlwaysCollide, AlwaysCollide, DoNothing, DoNothing, NULL -}; - -// function to get the estimated velocity of a shape for the cpBBTree. -static cpVect ShapeVelocityFunc(cpShape *shape){return shape->body->v;} - -// Used for disposing of collision handlers. -static void FreeWrap(void *ptr, void *unused){cpfree(ptr);} - -//MARK: Memory Management Functions - -cpSpace * -cpSpaceAlloc(void) -{ - return (cpSpace *)cpcalloc(1, sizeof(cpSpace)); -} - -cpSpace* -cpSpaceInit(cpSpace *space) -{ -#ifndef NDEBUG - static cpBool done = cpFalse; - if(!done){ - printf("apecs-physics: Initializing new Space, Chipmunk v%s (Debug Enabled)\n", cpVersionString); - printf("apecs-physics: Compile with the -frelease (cabal) or --flag apecs-physics:release (stack) to disable debug mode and runtime assertion checks\n"); - done = cpTrue; - } -#endif - - space->iterations = 10; - - space->gravity = cpvzero; - space->damping = 1.0f; - - space->collisionSlop = 0.1f; - space->collisionBias = cpfpow(1.0f - 0.1f, 60.0f); - space->collisionPersistence = 3; - - space->locked = 0; - space->stamp = 0; - - space->shapeIDCounter = 0; - space->staticShapes = cpBBTreeNew((cpSpatialIndexBBFunc)cpShapeGetBB, NULL); - space->dynamicShapes = cpBBTreeNew((cpSpatialIndexBBFunc)cpShapeGetBB, space->staticShapes); - cpBBTreeSetVelocityFunc(space->dynamicShapes, (cpBBTreeVelocityFunc)ShapeVelocityFunc); - - space->allocatedBuffers = cpArrayNew(0); - - space->dynamicBodies = cpArrayNew(0); - space->staticBodies = cpArrayNew(0); - space->sleepingComponents = cpArrayNew(0); - space->rousedBodies = cpArrayNew(0); - - space->sleepTimeThreshold = INFINITY; - space->idleSpeedThreshold = 0.0f; - - space->arbiters = cpArrayNew(0); - space->pooledArbiters = cpArrayNew(0); - - space->contactBuffersHead = NULL; - space->cachedArbiters = cpHashSetNew(0, (cpHashSetEqlFunc)arbiterSetEql); - - space->constraints = cpArrayNew(0); - - space->usesWildcards = cpFalse; - memcpy(&space->defaultHandler, &cpCollisionHandlerDoNothing, sizeof(cpCollisionHandler)); - space->collisionHandlers = cpHashSetNew(0, (cpHashSetEqlFunc)handlerSetEql); - - space->postStepCallbacks = cpArrayNew(0); - space->skipPostStep = cpFalse; - - cpBody *staticBody = cpBodyInit(&space->_staticBody, 0.0f, 0.0f); - cpBodySetType(staticBody, CP_BODY_TYPE_STATIC); - cpSpaceSetStaticBody(space, staticBody); - - return space; -} - -cpSpace* -cpSpaceNew(void) -{ - return cpSpaceInit(cpSpaceAlloc()); -} - -static void cpBodyActivateWrap(cpBody *body, void *unused){cpBodyActivate(body);} - -void -cpSpaceDestroy(cpSpace *space) -{ - cpSpaceEachBody(space, (cpSpaceBodyIteratorFunc)cpBodyActivateWrap, NULL); - - cpSpatialIndexFree(space->staticShapes); - cpSpatialIndexFree(space->dynamicShapes); - - cpArrayFree(space->dynamicBodies); - cpArrayFree(space->staticBodies); - cpArrayFree(space->sleepingComponents); - cpArrayFree(space->rousedBodies); - - cpArrayFree(space->constraints); - - cpHashSetFree(space->cachedArbiters); - - cpArrayFree(space->arbiters); - cpArrayFree(space->pooledArbiters); - - if(space->allocatedBuffers){ - cpArrayFreeEach(space->allocatedBuffers, cpfree); - cpArrayFree(space->allocatedBuffers); - } - - if(space->postStepCallbacks){ - cpArrayFreeEach(space->postStepCallbacks, cpfree); - cpArrayFree(space->postStepCallbacks); - } - - if(space->collisionHandlers) cpHashSetEach(space->collisionHandlers, FreeWrap, NULL); - cpHashSetFree(space->collisionHandlers); -} - -void -cpSpaceFree(cpSpace *space) -{ - if(space){ - cpSpaceDestroy(space); - cpfree(space); - } -} - - -//MARK: Basic properties: - -int -cpSpaceGetIterations(const cpSpace *space) -{ - return space->iterations; -} - -void -cpSpaceSetIterations(cpSpace *space, int iterations) -{ - cpAssertHard(iterations > 0, "Iterations must be positive and non-zero."); - space->iterations = iterations; -} - -cpVect -cpSpaceGetGravity(const cpSpace *space) -{ - return space->gravity; -} - -void -cpSpaceSetGravity(cpSpace *space, cpVect gravity) -{ - space->gravity = gravity; - - // Wake up all of the bodies since the gravity changed. - cpArray *components = space->sleepingComponents; - for(int i=0; inum; i++){ - cpBodyActivate((cpBody *)components->arr[i]); - } -} - -cpFloat -cpSpaceGetDamping(const cpSpace *space) -{ - return space->damping; -} - -void -cpSpaceSetDamping(cpSpace *space, cpFloat damping) -{ - cpAssertHard(damping >= 0.0, "Damping must be positive."); - space->damping = damping; -} - -cpFloat -cpSpaceGetIdleSpeedThreshold(const cpSpace *space) -{ - return space->idleSpeedThreshold; -} - -void -cpSpaceSetIdleSpeedThreshold(cpSpace *space, cpFloat idleSpeedThreshold) -{ - space->idleSpeedThreshold = idleSpeedThreshold; -} - -cpFloat -cpSpaceGetSleepTimeThreshold(const cpSpace *space) -{ - return space->sleepTimeThreshold; -} - -void -cpSpaceSetSleepTimeThreshold(cpSpace *space, cpFloat sleepTimeThreshold) -{ - space->sleepTimeThreshold = sleepTimeThreshold; -} - -cpFloat -cpSpaceGetCollisionSlop(const cpSpace *space) -{ - return space->collisionSlop; -} - -void -cpSpaceSetCollisionSlop(cpSpace *space, cpFloat collisionSlop) -{ - space->collisionSlop = collisionSlop; -} - -cpFloat -cpSpaceGetCollisionBias(const cpSpace *space) -{ - return space->collisionBias; -} - -void -cpSpaceSetCollisionBias(cpSpace *space, cpFloat collisionBias) -{ - space->collisionBias = collisionBias; -} - -cpTimestamp -cpSpaceGetCollisionPersistence(const cpSpace *space) -{ - return space->collisionPersistence; -} - -void -cpSpaceSetCollisionPersistence(cpSpace *space, cpTimestamp collisionPersistence) -{ - space->collisionPersistence = collisionPersistence; -} - -cpDataPointer -cpSpaceGetUserData(const cpSpace *space) -{ - return space->userData; -} - -void -cpSpaceSetUserData(cpSpace *space, cpDataPointer userData) -{ - space->userData = userData; -} - -cpBody * -cpSpaceGetStaticBody(const cpSpace *space) -{ - return space->staticBody; -} - -cpFloat -cpSpaceGetCurrentTimeStep(const cpSpace *space) -{ - return space->curr_dt; -} - -void -cpSpaceSetStaticBody(cpSpace *space, cpBody *body) -{ - if(space->staticBody != NULL){ - cpAssertHard(space->staticBody->shapeList == NULL, "Internal Error: Changing the designated static body while the old one still had shapes attached."); - space->staticBody->space = NULL; - } - - space->staticBody = body; - body->space = space; -} - -cpBool -cpSpaceIsLocked(cpSpace *space) -{ - return (space->locked > 0); -} - -//MARK: Collision Handler Function Management - -static void -cpSpaceUseWildcardDefaultHandler(cpSpace *space) -{ - // Spaces default to using the slightly faster "do nothing" default handler until wildcards are potentially needed. - if(!space->usesWildcards){ - space->usesWildcards = cpTrue; - memcpy(&space->defaultHandler, &cpCollisionHandlerDefault, sizeof(cpCollisionHandler)); - } -} - -cpCollisionHandler *cpSpaceAddDefaultCollisionHandler(cpSpace *space) -{ - cpSpaceUseWildcardDefaultHandler(space); - return &space->defaultHandler; -} - -cpCollisionHandler *cpSpaceAddCollisionHandler(cpSpace *space, cpCollisionType a, cpCollisionType b) -{ - cpHashValue hash = CP_HASH_PAIR(a, b); - cpCollisionHandler handler = {a, b, DefaultBegin, DefaultPreSolve, DefaultPostSolve, DefaultSeparate, NULL}; - return (cpCollisionHandler*)cpHashSetInsert(space->collisionHandlers, hash, &handler, (cpHashSetTransFunc)handlerSetTrans, NULL); -} - -cpCollisionHandler * -cpSpaceAddWildcardHandler(cpSpace *space, cpCollisionType type) -{ - cpSpaceUseWildcardDefaultHandler(space); - - cpHashValue hash = CP_HASH_PAIR(type, CP_WILDCARD_COLLISION_TYPE); - cpCollisionHandler handler = {type, CP_WILDCARD_COLLISION_TYPE, AlwaysCollide, AlwaysCollide, DoNothing, DoNothing, NULL}; - return (cpCollisionHandler*)cpHashSetInsert(space->collisionHandlers, hash, &handler, (cpHashSetTransFunc)handlerSetTrans, NULL); -} - - -//MARK: Body, Shape, and Joint Management -cpShape * -cpSpaceAddShape(cpSpace *space, cpShape *shape) -{ - cpBody *body = shape->body; - - cpAssertHard(shape->space != space, "You have already added this shape to this space. You must not add it a second time."); - cpAssertHard(!shape->space, "You have already added this shape to another space. You cannot add it to a second."); -// cpAssertHard(body->space == space, "The shape's body must be added to the space before the shape."); - cpAssertSpaceUnlocked(space); - - cpBool isStatic = (cpBodyGetType(body) == CP_BODY_TYPE_STATIC); - if(!isStatic) cpBodyActivate(body); - cpBodyAddShape(body, shape); - - shape->hashid = space->shapeIDCounter++; - cpShapeUpdate(shape, body->transform); - cpSpatialIndexInsert(isStatic ? space->staticShapes : space->dynamicShapes, shape, shape->hashid); - shape->space = space; - - return shape; -} - -cpBody * -cpSpaceAddBody(cpSpace *space, cpBody *body) -{ - cpAssertHard(body->space != space, "You have already added this body to this space. You must not add it a second time."); - cpAssertHard(!body->space, "You have already added this body to another space. You cannot add it to a second."); - cpAssertSpaceUnlocked(space); - - cpArrayPush(cpSpaceArrayForBodyType(space, cpBodyGetType(body)), body); - body->space = space; - - return body; -} - -cpConstraint * -cpSpaceAddConstraint(cpSpace *space, cpConstraint *constraint) -{ - cpAssertHard(constraint->space != space, "You have already added this constraint to this space. You must not add it a second time."); - cpAssertHard(!constraint->space, "You have already added this constraint to another space. You cannot add it to a second."); - cpAssertSpaceUnlocked(space); - - cpBody *a = constraint->a, *b = constraint->b; - cpAssertHard(a != NULL && b != NULL, "Constraint is attached to a NULL body."); -// cpAssertHard(a->space == space && b->space == space, "The constraint's bodies must be added to the space before the constraint."); - - cpBodyActivate(a); - cpBodyActivate(b); - cpArrayPush(space->constraints, constraint); - - // Push onto the heads of the bodies' constraint lists - constraint->next_a = a->constraintList; a->constraintList = constraint; - constraint->next_b = b->constraintList; b->constraintList = constraint; - constraint->space = space; - - return constraint; -} - -struct arbiterFilterContext { - cpSpace *space; - cpBody *body; - cpShape *shape; -}; - -static cpBool -cachedArbitersFilter(cpArbiter *arb, struct arbiterFilterContext *context) -{ - cpShape *shape = context->shape; - cpBody *body = context->body; - - - // Match on the filter shape, or if it's NULL the filter body - if( - (body == arb->body_a && (shape == arb->a || shape == NULL)) || - (body == arb->body_b && (shape == arb->b || shape == NULL)) - ){ - // Call separate when removing shapes. - if(shape && arb->state != CP_ARBITER_STATE_CACHED){ - // Invalidate the arbiter since one of the shapes was removed. - arb->state = CP_ARBITER_STATE_INVALIDATED; - - cpCollisionHandler *handler = arb->handler; - handler->separateFunc(arb, context->space, handler->userData); - } - - cpArbiterUnthread(arb); - cpArrayDeleteObj(context->space->arbiters, arb); - cpArrayPush(context->space->pooledArbiters, arb); - - return cpFalse; - } - - return cpTrue; -} - -void -cpSpaceFilterArbiters(cpSpace *space, cpBody *body, cpShape *filter) -{ - cpSpaceLock(space); { - struct arbiterFilterContext context = {space, body, filter}; - cpHashSetFilter(space->cachedArbiters, (cpHashSetFilterFunc)cachedArbitersFilter, &context); - } cpSpaceUnlock(space, cpTrue); -} - -void -cpSpaceRemoveShape(cpSpace *space, cpShape *shape) -{ - cpBody *body = shape->body; - cpAssertHard(cpSpaceContainsShape(space, shape), "Cannot remove a shape that was not added to the space. (Removed twice maybe?)"); - cpAssertSpaceUnlocked(space); - - cpBool isStatic = (cpBodyGetType(body) == CP_BODY_TYPE_STATIC); - if(isStatic){ - cpBodyActivateStatic(body, shape); - } else { - cpBodyActivate(body); - } - - cpBodyRemoveShape(body, shape); - cpSpaceFilterArbiters(space, body, shape); - cpSpatialIndexRemove(isStatic ? space->staticShapes : space->dynamicShapes, shape, shape->hashid); - shape->space = NULL; - shape->hashid = 0; -} - -void -cpSpaceRemoveBody(cpSpace *space, cpBody *body) -{ - cpAssertHard(body != cpSpaceGetStaticBody(space), "Cannot remove the designated static body for the space."); - cpAssertHard(cpSpaceContainsBody(space, body), "Cannot remove a body that was not added to the space. (Removed twice maybe?)"); -// cpAssertHard(body->shapeList == NULL, "Cannot remove a body from the space before removing the bodies attached to it."); -// cpAssertHard(body->constraintList == NULL, "Cannot remove a body from the space before removing the constraints attached to it."); - cpAssertSpaceUnlocked(space); - - cpBodyActivate(body); -// cpSpaceFilterArbiters(space, body, NULL); - cpArrayDeleteObj(cpSpaceArrayForBodyType(space, cpBodyGetType(body)), body); - body->space = NULL; -} - -void -cpSpaceRemoveConstraint(cpSpace *space, cpConstraint *constraint) -{ - cpAssertHard(cpSpaceContainsConstraint(space, constraint), "Cannot remove a constraint that was not added to the space. (Removed twice maybe?)"); - cpAssertSpaceUnlocked(space); - - cpBodyActivate(constraint->a); - cpBodyActivate(constraint->b); - cpArrayDeleteObj(space->constraints, constraint); - - cpBodyRemoveConstraint(constraint->a, constraint); - cpBodyRemoveConstraint(constraint->b, constraint); - constraint->space = NULL; -} - -cpBool cpSpaceContainsShape(cpSpace *space, cpShape *shape) -{ - return (shape->space == space); -} - -cpBool cpSpaceContainsBody(cpSpace *space, cpBody *body) -{ - return (body->space == space); -} - -cpBool cpSpaceContainsConstraint(cpSpace *space, cpConstraint *constraint) -{ - return (constraint->space == space); -} - -//MARK: Iteration - -void -cpSpaceEachBody(cpSpace *space, cpSpaceBodyIteratorFunc func, void *data) -{ - cpSpaceLock(space); { - cpArray *bodies = space->dynamicBodies; - for(int i=0; inum; i++){ - func((cpBody *)bodies->arr[i], data); - } - - cpArray *otherBodies = space->staticBodies; - for(int i=0; inum; i++){ - func((cpBody *)otherBodies->arr[i], data); - } - - cpArray *components = space->sleepingComponents; - for(int i=0; inum; i++){ - cpBody *root = (cpBody *)components->arr[i]; - - cpBody *body = root; - while(body){ - cpBody *next = body->sleeping.next; - func(body, data); - body = next; - } - } - } cpSpaceUnlock(space, cpTrue); -} - -typedef struct spaceShapeContext { - cpSpaceShapeIteratorFunc func; - void *data; -} spaceShapeContext; - -static void -spaceEachShapeIterator(cpShape *shape, spaceShapeContext *context) -{ - context->func(shape, context->data); -} - -void -cpSpaceEachShape(cpSpace *space, cpSpaceShapeIteratorFunc func, void *data) -{ - cpSpaceLock(space); { - spaceShapeContext context = {func, data}; - cpSpatialIndexEach(space->dynamicShapes, (cpSpatialIndexIteratorFunc)spaceEachShapeIterator, &context); - cpSpatialIndexEach(space->staticShapes, (cpSpatialIndexIteratorFunc)spaceEachShapeIterator, &context); - } cpSpaceUnlock(space, cpTrue); -} - -void -cpSpaceEachConstraint(cpSpace *space, cpSpaceConstraintIteratorFunc func, void *data) -{ - cpSpaceLock(space); { - cpArray *constraints = space->constraints; - - for(int i=0; inum; i++){ - func((cpConstraint *)constraints->arr[i], data); - } - } cpSpaceUnlock(space, cpTrue); -} - -//MARK: Spatial Index Management - -void -cpSpaceReindexStatic(cpSpace *space) -{ - cpAssertHard(!space->locked, "You cannot manually reindex objects while the space is locked. Wait until the current query or step is complete."); - - cpSpatialIndexEach(space->staticShapes, (cpSpatialIndexIteratorFunc)&cpShapeUpdateFunc, NULL); - cpSpatialIndexReindex(space->staticShapes); -} - -void -cpSpaceReindexShape(cpSpace *space, cpShape *shape) -{ - cpAssertHard(!space->locked, "You cannot manually reindex objects while the space is locked. Wait until the current query or step is complete."); - - cpShapeCacheBB(shape); - - // attempt to rehash the shape in both hashes - cpSpatialIndexReindexObject(space->dynamicShapes, shape, shape->hashid); - cpSpatialIndexReindexObject(space->staticShapes, shape, shape->hashid); -} - -void -cpSpaceReindexShapesForBody(cpSpace *space, cpBody *body) -{ - CP_BODY_FOREACH_SHAPE(body, shape) cpSpaceReindexShape(space, shape); -} - - -static void -copyShapes(cpShape *shape, cpSpatialIndex *index) -{ - cpSpatialIndexInsert(index, shape, shape->hashid); -} - -void -cpSpaceUseSpatialHash(cpSpace *space, cpFloat dim, int count) -{ - cpSpatialIndex *staticShapes = cpSpaceHashNew(dim, count, (cpSpatialIndexBBFunc)cpShapeGetBB, NULL); - cpSpatialIndex *dynamicShapes = cpSpaceHashNew(dim, count, (cpSpatialIndexBBFunc)cpShapeGetBB, staticShapes); - - cpSpatialIndexEach(space->staticShapes, (cpSpatialIndexIteratorFunc)copyShapes, staticShapes); - cpSpatialIndexEach(space->dynamicShapes, (cpSpatialIndexIteratorFunc)copyShapes, dynamicShapes); - - cpSpatialIndexFree(space->staticShapes); - cpSpatialIndexFree(space->dynamicShapes); - - space->staticShapes = staticShapes; - space->dynamicShapes = dynamicShapes; -} diff --git a/apecs-physics/Chipmunk2D/src/cpSpaceComponent.c b/apecs-physics/Chipmunk2D/src/cpSpaceComponent.c deleted file mode 100644 index ca686c4..0000000 --- a/apecs-physics/Chipmunk2D/src/cpSpaceComponent.c +++ /dev/null @@ -1,349 +0,0 @@ -/* Copyright (c) 2007 Scott Lembcke - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include - -#include "chipmunk_private.h" - -//MARK: Sleeping Functions - -void -cpSpaceActivateBody(cpSpace *space, cpBody *body) -{ - cpAssertHard(cpBodyGetType(body) == CP_BODY_TYPE_DYNAMIC, "Internal error: Attempting to activate a non-dynamic body."); - - if(space->locked){ - // cpSpaceActivateBody() is called again once the space is unlocked - if(!cpArrayContains(space->rousedBodies, body)) cpArrayPush(space->rousedBodies, body); - } else { - cpAssertSoft(body->sleeping.root == NULL && body->sleeping.next == NULL, "Internal error: Activating body non-NULL node pointers."); - cpArrayPush(space->dynamicBodies, body); - - CP_BODY_FOREACH_SHAPE(body, shape){ - cpSpatialIndexRemove(space->staticShapes, shape, shape->hashid); - cpSpatialIndexInsert(space->dynamicShapes, shape, shape->hashid); - } - - CP_BODY_FOREACH_ARBITER(body, arb){ - cpBody *bodyA = arb->body_a; - - // Arbiters are shared between two bodies that are always woken up together. - // You only want to restore the arbiter once, so bodyA is arbitrarily chosen to own the arbiter. - // The edge case is when static bodies are involved as the static bodies never actually sleep. - // If the static body is bodyB then all is good. If the static body is bodyA, that can easily be checked. - if(body == bodyA || cpBodyGetType(bodyA) == CP_BODY_TYPE_STATIC){ - int numContacts = arb->count; - struct cpContact *contacts = arb->contacts; - - // Restore contact values back to the space's contact buffer memory - arb->contacts = cpContactBufferGetArray(space); - memcpy(arb->contacts, contacts, numContacts*sizeof(struct cpContact)); - cpSpacePushContacts(space, numContacts); - - // Reinsert the arbiter into the arbiter cache - const cpShape *a = arb->a, *b = arb->b; - const cpShape *shape_pair[] = {a, b}; - cpHashValue arbHashID = CP_HASH_PAIR((cpHashValue)a, (cpHashValue)b); - cpHashSetInsert(space->cachedArbiters, arbHashID, shape_pair, NULL, arb); - - // Update the arbiter's state - arb->stamp = space->stamp; - cpArrayPush(space->arbiters, arb); - - cpfree(contacts); - } - } - - CP_BODY_FOREACH_CONSTRAINT(body, constraint){ - cpBody *bodyA = constraint->a; - if(body == bodyA || cpBodyGetType(bodyA) == CP_BODY_TYPE_STATIC) cpArrayPush(space->constraints, constraint); - } - } -} - -static void -cpSpaceDeactivateBody(cpSpace *space, cpBody *body) -{ - cpAssertHard(cpBodyGetType(body) == CP_BODY_TYPE_DYNAMIC, "Internal error: Attempting to deactivate a non-dynamic body."); - - cpArrayDeleteObj(space->dynamicBodies, body); - - CP_BODY_FOREACH_SHAPE(body, shape){ - cpSpatialIndexRemove(space->dynamicShapes, shape, shape->hashid); - cpSpatialIndexInsert(space->staticShapes, shape, shape->hashid); - } - - CP_BODY_FOREACH_ARBITER(body, arb){ - cpBody *bodyA = arb->body_a; - if(body == bodyA || cpBodyGetType(bodyA) == CP_BODY_TYPE_STATIC){ - cpSpaceUncacheArbiter(space, arb); - - // Save contact values to a new block of memory so they won't time out - size_t bytes = arb->count*sizeof(struct cpContact); - struct cpContact *contacts = (struct cpContact *)cpcalloc(1, bytes); - memcpy(contacts, arb->contacts, bytes); - arb->contacts = contacts; - } - } - - CP_BODY_FOREACH_CONSTRAINT(body, constraint){ - cpBody *bodyA = constraint->a; - if(body == bodyA || cpBodyGetType(bodyA) == CP_BODY_TYPE_STATIC) cpArrayDeleteObj(space->constraints, constraint); - } -} - -static inline cpBody * -ComponentRoot(cpBody *body) -{ - return (body ? body->sleeping.root : NULL); -} - -void -cpBodyActivate(cpBody *body) -{ - if(body != NULL && cpBodyGetType(body) == CP_BODY_TYPE_DYNAMIC){ - body->sleeping.idleTime = 0.0f; - - cpBody *root = ComponentRoot(body); - if(root && cpBodyIsSleeping(root)){ - // TODO should cpBodyIsSleeping(root) be an assertion? - cpAssertSoft(cpBodyGetType(root) == CP_BODY_TYPE_DYNAMIC, "Internal Error: Non-dynamic body component root detected."); - - cpSpace *space = root->space; - cpBody *body = root; - while(body){ - cpBody *next = body->sleeping.next; - - body->sleeping.idleTime = 0.0f; - body->sleeping.root = NULL; - body->sleeping.next = NULL; - cpSpaceActivateBody(space, body); - - body = next; - } - - cpArrayDeleteObj(space->sleepingComponents, root); - } - - CP_BODY_FOREACH_ARBITER(body, arb){ - // Reset the idle timer of things the body is touching as well. - // That way things don't get left hanging in the air. - cpBody *other = (arb->body_a == body ? arb->body_b : arb->body_a); - if(cpBodyGetType(other) != CP_BODY_TYPE_STATIC) other->sleeping.idleTime = 0.0f; - } - } -} - -void -cpBodyActivateStatic(cpBody *body, cpShape *filter) -{ - cpAssertHard(cpBodyGetType(body) == CP_BODY_TYPE_STATIC, "cpBodyActivateStatic() called on a non-static body."); - - CP_BODY_FOREACH_ARBITER(body, arb){ - if(!filter || filter == arb->a || filter == arb->b){ - cpBodyActivate(arb->body_a == body ? arb->body_b : arb->body_a); - } - } - - // TODO: should also activate joints? -} - -static inline void -cpBodyPushArbiter(cpBody *body, cpArbiter *arb) -{ - cpAssertSoft(cpArbiterThreadForBody(arb, body)->next == NULL, "Internal Error: Dangling contact graph pointers detected. (A)"); - cpAssertSoft(cpArbiterThreadForBody(arb, body)->prev == NULL, "Internal Error: Dangling contact graph pointers detected. (B)"); - - cpArbiter *next = body->arbiterList; - cpAssertSoft(next == NULL || cpArbiterThreadForBody(next, body)->prev == NULL, "Internal Error: Dangling contact graph pointers detected. (C)"); - cpArbiterThreadForBody(arb, body)->next = next; - - if(next) cpArbiterThreadForBody(next, body)->prev = arb; - body->arbiterList = arb; -} - -static inline void -ComponentAdd(cpBody *root, cpBody *body){ - body->sleeping.root = root; - - if(body != root){ - body->sleeping.next = root->sleeping.next; - root->sleeping.next = body; - } -} - -static inline void -FloodFillComponent(cpBody *root, cpBody *body) -{ - // Kinematic bodies cannot be put to sleep and prevent bodies they are touching from sleeping. - // Static bodies are effectively sleeping all the time. - if(cpBodyGetType(body) == CP_BODY_TYPE_DYNAMIC){ - cpBody *other_root = ComponentRoot(body); - if(other_root == NULL){ - ComponentAdd(root, body); - CP_BODY_FOREACH_ARBITER(body, arb) FloodFillComponent(root, (body == arb->body_a ? arb->body_b : arb->body_a)); - CP_BODY_FOREACH_CONSTRAINT(body, constraint) FloodFillComponent(root, (body == constraint->a ? constraint->b : constraint->a)); - } else { - cpAssertSoft(other_root == root, "Internal Error: Inconsistency dectected in the contact graph."); - } - } -} - -static inline cpBool -ComponentActive(cpBody *root, cpFloat threshold) -{ - CP_BODY_FOREACH_COMPONENT(root, body){ - if(body->sleeping.idleTime < threshold) return cpTrue; - } - - return cpFalse; -} - -void -cpSpaceProcessComponents(cpSpace *space, cpFloat dt) -{ - cpBool sleep = (space->sleepTimeThreshold != INFINITY); - cpArray *bodies = space->dynamicBodies; - -#ifndef NDEBUG - for(int i=0; inum; i++){ - cpBody *body = (cpBody*)bodies->arr[i]; - - cpAssertSoft(body->sleeping.next == NULL, "Internal Error: Dangling next pointer detected in contact graph."); - cpAssertSoft(body->sleeping.root == NULL, "Internal Error: Dangling root pointer detected in contact graph."); - } -#endif - - // Calculate the kinetic energy of all the bodies. - if(sleep){ - cpFloat dv = space->idleSpeedThreshold; - cpFloat dvsq = (dv ? dv*dv : cpvlengthsq(space->gravity)*dt*dt); - - // update idling and reset component nodes - for(int i=0; inum; i++){ - cpBody *body = (cpBody*)bodies->arr[i]; - - // TODO should make a separate array for kinematic bodies. - if(cpBodyGetType(body) != CP_BODY_TYPE_DYNAMIC) continue; - - // Need to deal with infinite mass objects - cpFloat keThreshold = (dvsq ? body->m*dvsq : 0.0f); - body->sleeping.idleTime = (cpBodyKineticEnergy(body) > keThreshold ? 0.0f : body->sleeping.idleTime + dt); - } - } - - // Awaken any sleeping bodies found and then push arbiters to the bodies' lists. - cpArray *arbiters = space->arbiters; - for(int i=0, count=arbiters->num; iarr[i]; - cpBody *a = arb->body_a, *b = arb->body_b; - - if(sleep){ - // TODO checking cpBodyIsSleepin() redundant? - if(cpBodyGetType(b) == CP_BODY_TYPE_KINEMATIC || cpBodyIsSleeping(a)) cpBodyActivate(a); - if(cpBodyGetType(a) == CP_BODY_TYPE_KINEMATIC || cpBodyIsSleeping(b)) cpBodyActivate(b); - } - - cpBodyPushArbiter(a, arb); - cpBodyPushArbiter(b, arb); - } - - if(sleep){ - // Bodies should be held active if connected by a joint to a kinematic. - cpArray *constraints = space->constraints; - for(int i=0; inum; i++){ - cpConstraint *constraint = (cpConstraint *)constraints->arr[i]; - cpBody *a = constraint->a, *b = constraint->b; - - if(cpBodyGetType(b) == CP_BODY_TYPE_KINEMATIC) cpBodyActivate(a); - if(cpBodyGetType(a) == CP_BODY_TYPE_KINEMATIC) cpBodyActivate(b); - } - - // Generate components and deactivate sleeping ones - for(int i=0; inum;){ - cpBody *body = (cpBody*)bodies->arr[i]; - - if(ComponentRoot(body) == NULL){ - // Body not in a component yet. Perform a DFS to flood fill mark - // the component in the contact graph using this body as the root. - FloodFillComponent(body, body); - - // Check if the component should be put to sleep. - if(!ComponentActive(body, space->sleepTimeThreshold)){ - cpArrayPush(space->sleepingComponents, body); - CP_BODY_FOREACH_COMPONENT(body, other) cpSpaceDeactivateBody(space, other); - - // cpSpaceDeactivateBody() removed the current body from the list. - // Skip incrementing the index counter. - continue; - } - } - - i++; - - // Only sleeping bodies retain their component node pointers. - body->sleeping.root = NULL; - body->sleeping.next = NULL; - } - } -} - -void -cpBodySleep(cpBody *body) -{ - cpBodySleepWithGroup(body, NULL); -} - -void -cpBodySleepWithGroup(cpBody *body, cpBody *group){ - cpAssertHard(cpBodyGetType(body) == CP_BODY_TYPE_DYNAMIC, "Non-dynamic bodies cannot be put to sleep."); - - cpSpace *space = body->space; - cpAssertHard(!cpSpaceIsLocked(space), "Bodies cannot be put to sleep during a query or a call to cpSpaceStep(). Put these calls into a post-step callback."); - cpAssertHard(cpSpaceGetSleepTimeThreshold(space) < INFINITY, "Sleeping is not enabled on the space. You cannot sleep a body without setting a sleep time threshold on the space."); - cpAssertHard(group == NULL || cpBodyIsSleeping(group), "Cannot use a non-sleeping body as a group identifier."); - - if(cpBodyIsSleeping(body)){ - cpAssertHard(ComponentRoot(body) == ComponentRoot(group), "The body is already sleeping and it's group cannot be reassigned."); - return; - } - - CP_BODY_FOREACH_SHAPE(body, shape) cpShapeCacheBB(shape); - cpSpaceDeactivateBody(space, body); - - if(group){ - cpBody *root = ComponentRoot(group); - - body->sleeping.root = root; - body->sleeping.next = root->sleeping.next; - body->sleeping.idleTime = 0.0f; - - root->sleeping.next = body; - } else { - body->sleeping.root = body; - body->sleeping.next = NULL; - body->sleeping.idleTime = 0.0f; - - cpArrayPush(space->sleepingComponents, body); - } - - cpArrayDeleteObj(space->dynamicBodies, body); -} diff --git a/apecs-physics/Chipmunk2D/src/cpSpaceDebug.c b/apecs-physics/Chipmunk2D/src/cpSpaceDebug.c deleted file mode 100644 index 12d766f..0000000 --- a/apecs-physics/Chipmunk2D/src/cpSpaceDebug.c +++ /dev/null @@ -1,189 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "chipmunk_private.h" - -#ifndef CP_SPACE_DISABLE_DEBUG_API - -static void -cpSpaceDebugDrawShape(cpShape *shape, cpSpaceDebugDrawOptions *options) -{ - cpBody *body = shape->body; - cpDataPointer data = options->data; - - cpSpaceDebugColor outline_color = options->shapeOutlineColor; - cpSpaceDebugColor fill_color = options->colorForShape(shape, data); - - switch(shape->klass->type){ - case CP_CIRCLE_SHAPE: { - cpCircleShape *circle = (cpCircleShape *)shape; - options->drawCircle(circle->tc, body->a, circle->r, outline_color, fill_color, data); - break; - } - case CP_SEGMENT_SHAPE: { - cpSegmentShape *seg = (cpSegmentShape *)shape; - options->drawFatSegment(seg->ta, seg->tb, seg->r, outline_color, fill_color, data); - break; - } - case CP_POLY_SHAPE: { - cpPolyShape *poly = (cpPolyShape *)shape; - - int count = poly->count; - struct cpSplittingPlane *planes = poly->planes; - cpVect *verts = (cpVect *)alloca(count*sizeof(cpVect)); - - for(int i=0; idrawPolygon(count, verts, poly->r, outline_color, fill_color, data); - break; - } - default: break; - } -} - -static const cpVect spring_verts[] = { - {0.00f, 0.0f}, - {0.20f, 0.0f}, - {0.25f, 3.0f}, - {0.30f,-6.0f}, - {0.35f, 6.0f}, - {0.40f,-6.0f}, - {0.45f, 6.0f}, - {0.50f,-6.0f}, - {0.55f, 6.0f}, - {0.60f,-6.0f}, - {0.65f, 6.0f}, - {0.70f,-3.0f}, - {0.75f, 6.0f}, - {0.80f, 0.0f}, - {1.00f, 0.0f}, -}; -static const int spring_count = sizeof(spring_verts)/sizeof(cpVect); - -static void -cpSpaceDebugDrawConstraint(cpConstraint *constraint, cpSpaceDebugDrawOptions *options) -{ - cpDataPointer data = options->data; - cpSpaceDebugColor color = options->constraintColor; - - cpBody *body_a = constraint->a; - cpBody *body_b = constraint->b; - - if(cpConstraintIsPinJoint(constraint)){ - cpPinJoint *joint = (cpPinJoint *)constraint; - - cpVect a = cpTransformPoint(body_a->transform, joint->anchorA); - cpVect b = cpTransformPoint(body_b->transform, joint->anchorB); - - options->drawDot(5, a, color, data); - options->drawDot(5, b, color, data); - options->drawSegment(a, b, color, data); - } else if(cpConstraintIsSlideJoint(constraint)){ - cpSlideJoint *joint = (cpSlideJoint *)constraint; - - cpVect a = cpTransformPoint(body_a->transform, joint->anchorA); - cpVect b = cpTransformPoint(body_b->transform, joint->anchorB); - - options->drawDot(5, a, color, data); - options->drawDot(5, b, color, data); - options->drawSegment(a, b, color, data); - } else if(cpConstraintIsPivotJoint(constraint)){ - cpPivotJoint *joint = (cpPivotJoint *)constraint; - - cpVect a = cpTransformPoint(body_a->transform, joint->anchorA); - cpVect b = cpTransformPoint(body_b->transform, joint->anchorB); - - options->drawDot(5, a, color, data); - options->drawDot(5, b, color, data); - } else if(cpConstraintIsGrooveJoint(constraint)){ - cpGrooveJoint *joint = (cpGrooveJoint *)constraint; - - cpVect a = cpTransformPoint(body_a->transform, joint->grv_a); - cpVect b = cpTransformPoint(body_a->transform, joint->grv_b); - cpVect c = cpTransformPoint(body_b->transform, joint->anchorB); - - options->drawDot(5, c, color, data); - options->drawSegment(a, b, color, data); - } else if(cpConstraintIsDampedSpring(constraint)){ - cpDampedSpring *spring = (cpDampedSpring *)constraint; - cpDataPointer data = options->data; - cpSpaceDebugColor color = options->constraintColor; - - cpVect a = cpTransformPoint(body_a->transform, spring->anchorA); - cpVect b = cpTransformPoint(body_b->transform, spring->anchorB); - - options->drawDot(5, a, color, data); - options->drawDot(5, b, color, data); - - cpVect delta = cpvsub(b, a); - cpFloat cos = delta.x; - cpFloat sin = delta.y; - cpFloat s = 1.0f/cpvlength(delta); - - cpVect r1 = cpv(cos, -sin*s); - cpVect r2 = cpv(sin, cos*s); - - cpVect *verts = (cpVect *)alloca(spring_count*sizeof(cpVect)); - for(int i=0; idrawSegment(verts[i], verts[i + 1], color, data); - } - } -} - -void -cpSpaceDebugDraw(cpSpace *space, cpSpaceDebugDrawOptions *options) -{ - if(options->flags & CP_SPACE_DEBUG_DRAW_SHAPES){ - cpSpaceEachShape(space, (cpSpaceShapeIteratorFunc)cpSpaceDebugDrawShape, options); - } - - if(options->flags & CP_SPACE_DEBUG_DRAW_CONSTRAINTS){ - cpSpaceEachConstraint(space, (cpSpaceConstraintIteratorFunc)cpSpaceDebugDrawConstraint, options); - } - - if(options->flags & CP_SPACE_DEBUG_DRAW_COLLISION_POINTS){ - cpArray *arbiters = space->arbiters; - cpSpaceDebugColor color = options->collisionPointColor; - cpSpaceDebugDrawSegmentImpl draw_seg = options->drawSegment; - cpDataPointer data = options->data; - - for(int i=0; inum; i++){ - cpArbiter *arb = (cpArbiter*)arbiters->arr[i]; - cpVect n = arb->n; - - for(int j=0; jcount; j++){ - cpVect p1 = cpvadd(arb->body_a->p, arb->contacts[j].r1); - cpVect p2 = cpvadd(arb->body_b->p, arb->contacts[j].r2); - - cpFloat d = 2.0f; - cpVect a = cpvadd(p1, cpvmult(n, -d)); - cpVect b = cpvadd(p2, cpvmult(n, d)); - draw_seg(a, b, color, data); - } - } - } -} - -#endif diff --git a/apecs-physics/Chipmunk2D/src/cpSpaceHash.c b/apecs-physics/Chipmunk2D/src/cpSpaceHash.c deleted file mode 100644 index 315c210..0000000 --- a/apecs-physics/Chipmunk2D/src/cpSpaceHash.c +++ /dev/null @@ -1,634 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "chipmunk_private.h" -#include "prime.h" - -typedef struct cpSpaceHashBin cpSpaceHashBin; -typedef struct cpHandle cpHandle; - -struct cpSpaceHash { - cpSpatialIndex spatialIndex; - - int numcells; - cpFloat celldim; - - cpSpaceHashBin **table; - cpHashSet *handleSet; - - cpSpaceHashBin *pooledBins; - cpArray *pooledHandles; - cpArray *allocatedBuffers; - - cpTimestamp stamp; -}; - - -//MARK: Handle Functions - -struct cpHandle { - void *obj; - int retain; - cpTimestamp stamp; -}; - -static cpHandle* -cpHandleInit(cpHandle *hand, void *obj) -{ - hand->obj = obj; - hand->retain = 0; - hand->stamp = 0; - - return hand; -} - -static inline void cpHandleRetain(cpHandle *hand){hand->retain++;} - -static inline void -cpHandleRelease(cpHandle *hand, cpArray *pooledHandles) -{ - hand->retain--; - if(hand->retain == 0) cpArrayPush(pooledHandles, hand); -} - -static int handleSetEql(void *obj, cpHandle *hand){return (obj == hand->obj);} - -static void * -handleSetTrans(void *obj, cpSpaceHash *hash) -{ - if(hash->pooledHandles->num == 0){ - // handle pool is exhausted, make more - int count = CP_BUFFER_BYTES/sizeof(cpHandle); - cpAssertHard(count, "Internal Error: Buffer size is too small."); - - cpHandle *buffer = (cpHandle *)cpcalloc(1, CP_BUFFER_BYTES); - cpArrayPush(hash->allocatedBuffers, buffer); - - for(int i=0; ipooledHandles, buffer + i); - } - - cpHandle *hand = cpHandleInit((cpHandle *)cpArrayPop(hash->pooledHandles), obj); - cpHandleRetain(hand); - - return hand; -} - -//MARK: Bin Functions - -struct cpSpaceHashBin { - cpHandle *handle; - cpSpaceHashBin *next; -}; - -static inline void -recycleBin(cpSpaceHash *hash, cpSpaceHashBin *bin) -{ - bin->next = hash->pooledBins; - hash->pooledBins = bin; -} - -static inline void -clearTableCell(cpSpaceHash *hash, int idx) -{ - cpSpaceHashBin *bin = hash->table[idx]; - while(bin){ - cpSpaceHashBin *next = bin->next; - - cpHandleRelease(bin->handle, hash->pooledHandles); - recycleBin(hash, bin); - - bin = next; - } - - hash->table[idx] = NULL; -} - -static void -clearTable(cpSpaceHash *hash) -{ - for(int i=0; inumcells; i++) clearTableCell(hash, i); -} - -// Get a recycled or new bin. -static inline cpSpaceHashBin * -getEmptyBin(cpSpaceHash *hash) -{ - cpSpaceHashBin *bin = hash->pooledBins; - - if(bin){ - hash->pooledBins = bin->next; - return bin; - } else { - // Pool is exhausted, make more - int count = CP_BUFFER_BYTES/sizeof(cpSpaceHashBin); - cpAssertHard(count, "Internal Error: Buffer size is too small."); - - cpSpaceHashBin *buffer = (cpSpaceHashBin *)cpcalloc(1, CP_BUFFER_BYTES); - cpArrayPush(hash->allocatedBuffers, buffer); - - // push all but the first one, return the first instead - for(int i=1; itable); - - hash->numcells = numcells; - hash->table = (cpSpaceHashBin **)cpcalloc(numcells, sizeof(cpSpaceHashBin *)); -} - -static inline cpSpatialIndexClass *Klass(); - -cpSpatialIndex * -cpSpaceHashInit(cpSpaceHash *hash, cpFloat celldim, int numcells, cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex) -{ - cpSpatialIndexInit((cpSpatialIndex *)hash, Klass(), bbfunc, staticIndex); - - cpSpaceHashAllocTable(hash, next_prime(numcells)); - hash->celldim = celldim; - - hash->handleSet = cpHashSetNew(0, (cpHashSetEqlFunc)handleSetEql); - - hash->pooledHandles = cpArrayNew(0); - - hash->pooledBins = NULL; - hash->allocatedBuffers = cpArrayNew(0); - - hash->stamp = 1; - - return (cpSpatialIndex *)hash; -} - -cpSpatialIndex * -cpSpaceHashNew(cpFloat celldim, int cells, cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex) -{ - return cpSpaceHashInit(cpSpaceHashAlloc(), celldim, cells, bbfunc, staticIndex); -} - -static void -cpSpaceHashDestroy(cpSpaceHash *hash) -{ - if(hash->table) clearTable(hash); - cpfree(hash->table); - - cpHashSetFree(hash->handleSet); - - cpArrayFreeEach(hash->allocatedBuffers, cpfree); - cpArrayFree(hash->allocatedBuffers); - cpArrayFree(hash->pooledHandles); -} - -//MARK: Helper Functions - -static inline cpBool -containsHandle(cpSpaceHashBin *bin, cpHandle *hand) -{ - while(bin){ - if(bin->handle == hand) return cpTrue; - bin = bin->next; - } - - return cpFalse; -} - -// The hash function itself. -static inline cpHashValue -hash_func(cpHashValue x, cpHashValue y, cpHashValue n) -{ - return (x*1640531513ul ^ y*2654435789ul) % n; -} - -// Much faster than (int)floor(f) -// Profiling showed floor() to be a sizable performance hog -static inline int -floor_int(cpFloat f) -{ - int i = (int)f; - return (f < 0.0f && f != i ? i - 1 : i); -} - -static inline void -hashHandle(cpSpaceHash *hash, cpHandle *hand, cpBB bb) -{ - // Find the dimensions in cell coordinates. - cpFloat dim = hash->celldim; - int l = floor_int(bb.l/dim); // Fix by ShiftZ - int r = floor_int(bb.r/dim); - int b = floor_int(bb.b/dim); - int t = floor_int(bb.t/dim); - - int n = hash->numcells; - for(int i=l; i<=r; i++){ - for(int j=b; j<=t; j++){ - cpHashValue idx = hash_func(i,j,n); - cpSpaceHashBin *bin = hash->table[idx]; - - // Don't add an object twice to the same cell. - if(containsHandle(bin, hand)) continue; - - cpHandleRetain(hand); - // Insert a new bin for the handle in this cell. - cpSpaceHashBin *newBin = getEmptyBin(hash); - newBin->handle = hand; - newBin->next = bin; - hash->table[idx] = newBin; - } - } -} - -//MARK: Basic Operations - -static void -cpSpaceHashInsert(cpSpaceHash *hash, void *obj, cpHashValue hashid) -{ - cpHandle *hand = (cpHandle *)cpHashSetInsert(hash->handleSet, hashid, obj, (cpHashSetTransFunc)handleSetTrans, hash); - hashHandle(hash, hand, hash->spatialIndex.bbfunc(obj)); -} - -static void -cpSpaceHashRehashObject(cpSpaceHash *hash, void *obj, cpHashValue hashid) -{ - cpHandle *hand = (cpHandle *)cpHashSetRemove(hash->handleSet, hashid, obj); - - if(hand){ - hand->obj = NULL; - cpHandleRelease(hand, hash->pooledHandles); - - cpSpaceHashInsert(hash, obj, hashid); - } -} - -static void -rehash_helper(cpHandle *hand, cpSpaceHash *hash) -{ - hashHandle(hash, hand, hash->spatialIndex.bbfunc(hand->obj)); -} - -static void -cpSpaceHashRehash(cpSpaceHash *hash) -{ - clearTable(hash); - cpHashSetEach(hash->handleSet, (cpHashSetIteratorFunc)rehash_helper, hash); -} - -static void -cpSpaceHashRemove(cpSpaceHash *hash, void *obj, cpHashValue hashid) -{ - cpHandle *hand = (cpHandle *)cpHashSetRemove(hash->handleSet, hashid, obj); - - if(hand){ - hand->obj = NULL; - cpHandleRelease(hand, hash->pooledHandles); - } -} - -typedef struct eachContext { - cpSpatialIndexIteratorFunc func; - void *data; -} eachContext; - -static void eachHelper(cpHandle *hand, eachContext *context){context->func(hand->obj, context->data);} - -static void -cpSpaceHashEach(cpSpaceHash *hash, cpSpatialIndexIteratorFunc func, void *data) -{ - eachContext context = {func, data}; - cpHashSetEach(hash->handleSet, (cpHashSetIteratorFunc)eachHelper, &context); -} - -static void -remove_orphaned_handles(cpSpaceHash *hash, cpSpaceHashBin **bin_ptr) -{ - cpSpaceHashBin *bin = *bin_ptr; - while(bin){ - cpHandle *hand = bin->handle; - cpSpaceHashBin *next = bin->next; - - if(!hand->obj){ - // orphaned handle, unlink and recycle the bin - (*bin_ptr) = bin->next; - recycleBin(hash, bin); - - cpHandleRelease(hand, hash->pooledHandles); - } else { - bin_ptr = &bin->next; - } - - bin = next; - } -} - -//MARK: Query Functions - -static inline void -query_helper(cpSpaceHash *hash, cpSpaceHashBin **bin_ptr, void *obj, cpSpatialIndexQueryFunc func, void *data) -{ - restart: - for(cpSpaceHashBin *bin = *bin_ptr; bin; bin = bin->next){ - cpHandle *hand = bin->handle; - void *other = hand->obj; - - if(hand->stamp == hash->stamp || obj == other){ - continue; - } else if(other){ - func(obj, other, 0, data); - hand->stamp = hash->stamp; - } else { - // The object for this handle has been removed - // cleanup this cell and restart the query - remove_orphaned_handles(hash, bin_ptr); - goto restart; // GCC not smart enough/able to tail call an inlined function. - } - } -} - -static void -cpSpaceHashQuery(cpSpaceHash *hash, void *obj, cpBB bb, cpSpatialIndexQueryFunc func, void *data) -{ - // Get the dimensions in cell coordinates. - cpFloat dim = hash->celldim; - int l = floor_int(bb.l/dim); // Fix by ShiftZ - int r = floor_int(bb.r/dim); - int b = floor_int(bb.b/dim); - int t = floor_int(bb.t/dim); - - int n = hash->numcells; - cpSpaceHashBin **table = hash->table; - - // Iterate over the cells and query them. - for(int i=l; i<=r; i++){ - for(int j=b; j<=t; j++){ - query_helper(hash, &table[hash_func(i,j,n)], obj, func, data); - } - } - - hash->stamp++; -} - -// Similar to struct eachPair above. -typedef struct queryRehashContext { - cpSpaceHash *hash; - cpSpatialIndexQueryFunc func; - void *data; -} queryRehashContext; - -// Hashset iterator func used with cpSpaceHashQueryRehash(). -static void -queryRehash_helper(cpHandle *hand, queryRehashContext *context) -{ - cpSpaceHash *hash = context->hash; - cpSpatialIndexQueryFunc func = context->func; - void *data = context->data; - - cpFloat dim = hash->celldim; - int n = hash->numcells; - - void *obj = hand->obj; - cpBB bb = hash->spatialIndex.bbfunc(obj); - - int l = floor_int(bb.l/dim); - int r = floor_int(bb.r/dim); - int b = floor_int(bb.b/dim); - int t = floor_int(bb.t/dim); - - cpSpaceHashBin **table = hash->table; - - for(int i=l; i<=r; i++){ - for(int j=b; j<=t; j++){ - cpHashValue idx = hash_func(i,j,n); - cpSpaceHashBin *bin = table[idx]; - - if(containsHandle(bin, hand)) continue; - - cpHandleRetain(hand); // this MUST be done first in case the object is removed in func() - query_helper(hash, &bin, obj, func, data); - - cpSpaceHashBin *newBin = getEmptyBin(hash); - newBin->handle = hand; - newBin->next = bin; - table[idx] = newBin; - } - } - - // Increment the stamp for each object hashed. - hash->stamp++; -} - -static void -cpSpaceHashReindexQuery(cpSpaceHash *hash, cpSpatialIndexQueryFunc func, void *data) -{ - clearTable(hash); - - queryRehashContext context = {hash, func, data}; - cpHashSetEach(hash->handleSet, (cpHashSetIteratorFunc)queryRehash_helper, &context); - - cpSpatialIndexCollideStatic((cpSpatialIndex *)hash, hash->spatialIndex.staticIndex, func, data); -} - -static inline cpFloat -segmentQuery_helper(cpSpaceHash *hash, cpSpaceHashBin **bin_ptr, void *obj, cpSpatialIndexSegmentQueryFunc func, void *data) -{ - cpFloat t = 1.0f; - - restart: - for(cpSpaceHashBin *bin = *bin_ptr; bin; bin = bin->next){ - cpHandle *hand = bin->handle; - void *other = hand->obj; - - // Skip over certain conditions - if(hand->stamp == hash->stamp){ - continue; - } else if(other){ - t = cpfmin(t, func(obj, other, data)); - hand->stamp = hash->stamp; - } else { - // The object for this handle has been removed - // cleanup this cell and restart the query - remove_orphaned_handles(hash, bin_ptr); - goto restart; // GCC not smart enough/able to tail call an inlined function. - } - } - - return t; -} - -// modified from http://playtechs.blogspot.com/2007/03/raytracing-on-grid.html -static void -cpSpaceHashSegmentQuery(cpSpaceHash *hash, void *obj, cpVect a, cpVect b, cpFloat t_exit, cpSpatialIndexSegmentQueryFunc func, void *data) -{ - a = cpvmult(a, 1.0f/hash->celldim); - b = cpvmult(b, 1.0f/hash->celldim); - - int cell_x = floor_int(a.x), cell_y = floor_int(a.y); - - cpFloat t = 0; - - int x_inc, y_inc; - cpFloat temp_v, temp_h; - - if (b.x > a.x){ - x_inc = 1; - temp_h = (cpffloor(a.x + 1.0f) - a.x); - } else { - x_inc = -1; - temp_h = (a.x - cpffloor(a.x)); - } - - if (b.y > a.y){ - y_inc = 1; - temp_v = (cpffloor(a.y + 1.0f) - a.y); - } else { - y_inc = -1; - temp_v = (a.y - cpffloor(a.y)); - } - - // Division by zero is *very* slow on ARM - cpFloat dx = cpfabs(b.x - a.x), dy = cpfabs(b.y - a.y); - cpFloat dt_dx = (dx ? 1.0f/dx : INFINITY), dt_dy = (dy ? 1.0f/dy : INFINITY); - - // fix NANs in horizontal directions - cpFloat next_h = (temp_h ? temp_h*dt_dx : dt_dx); - cpFloat next_v = (temp_v ? temp_v*dt_dy : dt_dy); - - int n = hash->numcells; - cpSpaceHashBin **table = hash->table; - - while(t < t_exit){ - cpHashValue idx = hash_func(cell_x, cell_y, n); - t_exit = cpfmin(t_exit, segmentQuery_helper(hash, &table[idx], obj, func, data)); - - if (next_v < next_h){ - cell_y += y_inc; - t = next_v; - next_v += dt_dy; - } else { - cell_x += x_inc; - t = next_h; - next_h += dt_dx; - } - } - - hash->stamp++; -} - -//MARK: Misc - -void -cpSpaceHashResize(cpSpaceHash *hash, cpFloat celldim, int numcells) -{ - if(hash->spatialIndex.klass != Klass()){ - cpAssertWarn(cpFalse, "Ignoring cpSpaceHashResize() call to non-cpSpaceHash spatial index."); - return; - } - - clearTable(hash); - - hash->celldim = celldim; - cpSpaceHashAllocTable(hash, next_prime(numcells)); -} - -static int -cpSpaceHashCount(cpSpaceHash *hash) -{ - return cpHashSetCount(hash->handleSet); -} - -static int -cpSpaceHashContains(cpSpaceHash *hash, void *obj, cpHashValue hashid) -{ - return cpHashSetFind(hash->handleSet, hashid, obj) != NULL; -} - -static cpSpatialIndexClass klass = { - (cpSpatialIndexDestroyImpl)cpSpaceHashDestroy, - - (cpSpatialIndexCountImpl)cpSpaceHashCount, - (cpSpatialIndexEachImpl)cpSpaceHashEach, - (cpSpatialIndexContainsImpl)cpSpaceHashContains, - - (cpSpatialIndexInsertImpl)cpSpaceHashInsert, - (cpSpatialIndexRemoveImpl)cpSpaceHashRemove, - - (cpSpatialIndexReindexImpl)cpSpaceHashRehash, - (cpSpatialIndexReindexObjectImpl)cpSpaceHashRehashObject, - (cpSpatialIndexReindexQueryImpl)cpSpaceHashReindexQuery, - - (cpSpatialIndexQueryImpl)cpSpaceHashQuery, - (cpSpatialIndexSegmentQueryImpl)cpSpaceHashSegmentQuery, -}; - -static inline cpSpatialIndexClass *Klass(){return &klass;} - -//MARK: Debug Drawing - -//#define CP_BBTREE_DEBUG_DRAW -#ifdef CP_BBTREE_DEBUG_DRAW -#include "OpenGL/gl.h" -#include "OpenGL/glu.h" -#include - -void -cpSpaceHashRenderDebug(cpSpatialIndex *index) -{ - if(index->klass != &klass){ - cpAssertWarn(cpFalse, "Ignoring cpSpaceHashRenderDebug() call to non-spatial hash spatial index."); - return; - } - - cpSpaceHash *hash = (cpSpaceHash *)index; - cpBB bb = cpBBNew(-320, -240, 320, 240); - - cpFloat dim = hash->celldim; - int n = hash->numcells; - - int l = (int)floor(bb.l/dim); - int r = (int)floor(bb.r/dim); - int b = (int)floor(bb.b/dim); - int t = (int)floor(bb.t/dim); - - for(int i=l; i<=r; i++){ - for(int j=b; j<=t; j++){ - int cell_count = 0; - - int index = hash_func(i,j,n); - for(cpSpaceHashBin *bin = hash->table[index]; bin; bin = bin->next) - cell_count++; - - GLfloat v = 1.0f - (GLfloat)cell_count/10.0f; - glColor3f(v,v,v); - glRectf(i*dim, j*dim, (i + 1)*dim, (j + 1)*dim); - } - } -} -#endif diff --git a/apecs-physics/Chipmunk2D/src/cpSpaceQuery.c b/apecs-physics/Chipmunk2D/src/cpSpaceQuery.c deleted file mode 100644 index 6a98be6..0000000 --- a/apecs-physics/Chipmunk2D/src/cpSpaceQuery.c +++ /dev/null @@ -1,246 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "chipmunk_private.h" - -//MARK: Nearest Point Query Functions - -struct PointQueryContext { - cpVect point; - cpFloat maxDistance; - cpShapeFilter filter; - cpSpacePointQueryFunc func; -}; - -static cpCollisionID -NearestPointQuery(struct PointQueryContext *context, cpShape *shape, cpCollisionID id, void *data) -{ - if( - !cpShapeFilterReject(shape->filter, context->filter) - ){ - cpPointQueryInfo info; - cpShapePointQuery(shape, context->point, &info); - - if(info.shape && info.distance < context->maxDistance) context->func(shape, info.point, info.distance, info.gradient, data); - } - - return id; -} - -void -cpSpacePointQuery(cpSpace *space, cpVect point, cpFloat maxDistance, cpShapeFilter filter, cpSpacePointQueryFunc func, void *data) -{ - struct PointQueryContext context = {point, maxDistance, filter, func}; - cpBB bb = cpBBNewForCircle(point, cpfmax(maxDistance, 0.0f)); - - cpSpaceLock(space); { - cpSpatialIndexQuery(space->dynamicShapes, &context, bb, (cpSpatialIndexQueryFunc)NearestPointQuery, data); - cpSpatialIndexQuery(space->staticShapes, &context, bb, (cpSpatialIndexQueryFunc)NearestPointQuery, data); - } cpSpaceUnlock(space, cpTrue); -} - -static cpCollisionID -NearestPointQueryNearest(struct PointQueryContext *context, cpShape *shape, cpCollisionID id, cpPointQueryInfo *out) -{ - if( - !cpShapeFilterReject(shape->filter, context->filter) && !shape->sensor - ){ - cpPointQueryInfo info; - cpShapePointQuery(shape, context->point, &info); - - if(info.distance < out->distance) (*out) = info; - } - - return id; -} - -cpShape * -cpSpacePointQueryNearest(cpSpace *space, cpVect point, cpFloat maxDistance, cpShapeFilter filter, cpPointQueryInfo *out) -{ - cpPointQueryInfo info = {NULL, cpvzero, maxDistance, cpvzero}; - if(out){ - (*out) = info; - } else { - out = &info; - } - - struct PointQueryContext context = { - point, maxDistance, - filter, - NULL - }; - - cpBB bb = cpBBNewForCircle(point, cpfmax(maxDistance, 0.0f)); - cpSpatialIndexQuery(space->dynamicShapes, &context, bb, (cpSpatialIndexQueryFunc)NearestPointQueryNearest, out); - cpSpatialIndexQuery(space->staticShapes, &context, bb, (cpSpatialIndexQueryFunc)NearestPointQueryNearest, out); - - return (cpShape *)out->shape; -} - - -//MARK: Segment Query Functions - -struct SegmentQueryContext { - cpVect start, end; - cpFloat radius; - cpShapeFilter filter; - cpSpaceSegmentQueryFunc func; -}; - -static cpFloat -SegmentQuery(struct SegmentQueryContext *context, cpShape *shape, void *data) -{ - cpSegmentQueryInfo info; - - if( - !cpShapeFilterReject(shape->filter, context->filter) && - cpShapeSegmentQuery(shape, context->start, context->end, context->radius, &info) - ){ - context->func(shape, info.point, info.normal, info.alpha, data); - } - - return 1.0f; -} - -void -cpSpaceSegmentQuery(cpSpace *space, cpVect start, cpVect end, cpFloat radius, cpShapeFilter filter, cpSpaceSegmentQueryFunc func, void *data) -{ - struct SegmentQueryContext context = { - start, end, - radius, - filter, - func, - }; - - cpSpaceLock(space); { - cpSpatialIndexSegmentQuery(space->staticShapes, &context, start, end, 1.0f, (cpSpatialIndexSegmentQueryFunc)SegmentQuery, data); - cpSpatialIndexSegmentQuery(space->dynamicShapes, &context, start, end, 1.0f, (cpSpatialIndexSegmentQueryFunc)SegmentQuery, data); - } cpSpaceUnlock(space, cpTrue); -} - -static cpFloat -SegmentQueryFirst(struct SegmentQueryContext *context, cpShape *shape, cpSegmentQueryInfo *out) -{ - cpSegmentQueryInfo info; - - if( - !cpShapeFilterReject(shape->filter, context->filter) && !shape->sensor && - cpShapeSegmentQuery(shape, context->start, context->end, context->radius, &info) && - info.alpha < out->alpha - ){ - (*out) = info; - } - - return out->alpha; -} - -cpShape * -cpSpaceSegmentQueryFirst(cpSpace *space, cpVect start, cpVect end, cpFloat radius, cpShapeFilter filter, cpSegmentQueryInfo *out) -{ - cpSegmentQueryInfo info = {NULL, end, cpvzero, 1.0f}; - if(out){ - (*out) = info; - } else { - out = &info; - } - - struct SegmentQueryContext context = { - start, end, - radius, - filter, - NULL - }; - - cpSpatialIndexSegmentQuery(space->staticShapes, &context, start, end, 1.0f, (cpSpatialIndexSegmentQueryFunc)SegmentQueryFirst, out); - cpSpatialIndexSegmentQuery(space->dynamicShapes, &context, start, end, out->alpha, (cpSpatialIndexSegmentQueryFunc)SegmentQueryFirst, out); - - return (cpShape *)out->shape; -} - -//MARK: BB Query Functions - -struct BBQueryContext { - cpBB bb; - cpShapeFilter filter; - cpSpaceBBQueryFunc func; -}; - -static cpCollisionID -BBQuery(struct BBQueryContext *context, cpShape *shape, cpCollisionID id, void *data) -{ - if( - !cpShapeFilterReject(shape->filter, context->filter) && - cpBBIntersects(context->bb, shape->bb) - ){ - context->func(shape, data); - } - - return id; -} - -void -cpSpaceBBQuery(cpSpace *space, cpBB bb, cpShapeFilter filter, cpSpaceBBQueryFunc func, void *data) -{ - struct BBQueryContext context = {bb, filter, func}; - - cpSpaceLock(space); { - cpSpatialIndexQuery(space->dynamicShapes, &context, bb, (cpSpatialIndexQueryFunc)BBQuery, data); - cpSpatialIndexQuery(space->staticShapes, &context, bb, (cpSpatialIndexQueryFunc)BBQuery, data); - } cpSpaceUnlock(space, cpTrue); -} - -//MARK: Shape Query Functions - -struct ShapeQueryContext { - cpSpaceShapeQueryFunc func; - void *data; - cpBool anyCollision; -}; - -// Callback from the spatial hash. -static cpCollisionID -ShapeQuery(cpShape *a, cpShape *b, cpCollisionID id, struct ShapeQueryContext *context) -{ - if(cpShapeFilterReject(a->filter, b->filter) || a == b) return id; - - cpContactPointSet set = cpShapesCollide(a, b); - if(set.count){ - if(context->func) context->func(b, &set, context->data); - context->anyCollision = !(a->sensor || b->sensor); - } - - return id; -} - -cpBool -cpSpaceShapeQuery(cpSpace *space, cpShape *shape, cpSpaceShapeQueryFunc func, void *data) -{ - cpBody *body = shape->body; - cpBB bb = (body ? cpShapeUpdate(shape, body->transform) : shape->bb); - struct ShapeQueryContext context = {func, data, cpFalse}; - - cpSpaceLock(space); { - cpSpatialIndexQuery(space->dynamicShapes, shape, bb, (cpSpatialIndexQueryFunc)ShapeQuery, &context); - cpSpatialIndexQuery(space->staticShapes, shape, bb, (cpSpatialIndexQueryFunc)ShapeQuery, &context); - } cpSpaceUnlock(space, cpTrue); - - return context.anyCollision; -} diff --git a/apecs-physics/Chipmunk2D/src/cpSpaceStep.c b/apecs-physics/Chipmunk2D/src/cpSpaceStep.c deleted file mode 100644 index 7087a60..0000000 --- a/apecs-physics/Chipmunk2D/src/cpSpaceStep.c +++ /dev/null @@ -1,445 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "chipmunk_private.h" - -//MARK: Post Step Callback Functions - -cpPostStepCallback * -cpSpaceGetPostStepCallback(cpSpace *space, void *key) -{ - cpArray *arr = space->postStepCallbacks; - for(int i=0; inum; i++){ - cpPostStepCallback *callback = (cpPostStepCallback *)arr->arr[i]; - if(callback && callback->key == key) return callback; - } - - return NULL; -} - -static void PostStepDoNothing(cpSpace *space, void *obj, void *data){} - -cpBool -cpSpaceAddPostStepCallback(cpSpace *space, cpPostStepFunc func, void *key, void *data) -{ - cpAssertWarn(space->locked, - "Adding a post-step callback when the space is not locked is unnecessary. " - "Post-step callbacks will not called until the end of the next call to cpSpaceStep() or the next query."); - - if(!cpSpaceGetPostStepCallback(space, key)){ - cpPostStepCallback *callback = (cpPostStepCallback *)cpcalloc(1, sizeof(cpPostStepCallback)); - callback->func = (func ? func : PostStepDoNothing); - callback->key = key; - callback->data = data; - - cpArrayPush(space->postStepCallbacks, callback); - return cpTrue; - } else { - return cpFalse; - } -} - -//MARK: Locking Functions - -void -cpSpaceLock(cpSpace *space) -{ - space->locked++; -} - -void -cpSpaceUnlock(cpSpace *space, cpBool runPostStep) -{ - space->locked--; - cpAssertHard(space->locked >= 0, "Internal Error: Space lock underflow."); - - if(space->locked == 0){ - cpArray *waking = space->rousedBodies; - - for(int i=0, count=waking->num; iarr[i]); - waking->arr[i] = NULL; - } - - waking->num = 0; - - if(space->locked == 0 && runPostStep && !space->skipPostStep){ - space->skipPostStep = cpTrue; - - cpArray *arr = space->postStepCallbacks; - for(int i=0; inum; i++){ - cpPostStepCallback *callback = (cpPostStepCallback *)arr->arr[i]; - cpPostStepFunc func = callback->func; - - // Mark the func as NULL in case calling it calls cpSpaceRunPostStepCallbacks() again. - // TODO: need more tests around this case I think. - callback->func = NULL; - if(func) func(space, callback->key, callback->data); - - arr->arr[i] = NULL; - cpfree(callback); - } - - arr->num = 0; - space->skipPostStep = cpFalse; - } - } -} - -//MARK: Contact Buffer Functions - -struct cpContactBufferHeader { - cpTimestamp stamp; - cpContactBufferHeader *next; - unsigned int numContacts; -}; - -#define CP_CONTACTS_BUFFER_SIZE ((CP_BUFFER_BYTES - sizeof(cpContactBufferHeader))/sizeof(struct cpContact)) -typedef struct cpContactBuffer { - cpContactBufferHeader header; - struct cpContact contacts[CP_CONTACTS_BUFFER_SIZE]; -} cpContactBuffer; - -static cpContactBufferHeader * -cpSpaceAllocContactBuffer(cpSpace *space) -{ - cpContactBuffer *buffer = (cpContactBuffer *)cpcalloc(1, sizeof(cpContactBuffer)); - cpArrayPush(space->allocatedBuffers, buffer); - return (cpContactBufferHeader *)buffer; -} - -static cpContactBufferHeader * -cpContactBufferHeaderInit(cpContactBufferHeader *header, cpTimestamp stamp, cpContactBufferHeader *splice) -{ - header->stamp = stamp; - header->next = (splice ? splice->next : header); - header->numContacts = 0; - - return header; -} - -void -cpSpacePushFreshContactBuffer(cpSpace *space) -{ - cpTimestamp stamp = space->stamp; - - cpContactBufferHeader *head = space->contactBuffersHead; - - if(!head){ - // No buffers have been allocated, make one - space->contactBuffersHead = cpContactBufferHeaderInit(cpSpaceAllocContactBuffer(space), stamp, NULL); - } else if(stamp - head->next->stamp > space->collisionPersistence){ - // The tail buffer is available, rotate the ring - cpContactBufferHeader *tail = head->next; - space->contactBuffersHead = cpContactBufferHeaderInit(tail, stamp, tail); - } else { - // Allocate a new buffer and push it into the ring - cpContactBufferHeader *buffer = cpContactBufferHeaderInit(cpSpaceAllocContactBuffer(space), stamp, head); - space->contactBuffersHead = head->next = buffer; - } -} - - -struct cpContact * -cpContactBufferGetArray(cpSpace *space) -{ - if(space->contactBuffersHead->numContacts + CP_MAX_CONTACTS_PER_ARBITER > CP_CONTACTS_BUFFER_SIZE){ - // contact buffer could overflow on the next collision, push a fresh one. - cpSpacePushFreshContactBuffer(space); - } - - cpContactBufferHeader *head = space->contactBuffersHead; - return ((cpContactBuffer *)head)->contacts + head->numContacts; -} - -void -cpSpacePushContacts(cpSpace *space, int count) -{ - cpAssertHard(count <= CP_MAX_CONTACTS_PER_ARBITER, "Internal Error: Contact buffer overflow!"); - space->contactBuffersHead->numContacts += count; -} - -static void -cpSpacePopContacts(cpSpace *space, int count){ - space->contactBuffersHead->numContacts -= count; -} - -//MARK: Collision Detection Functions - -static void * -cpSpaceArbiterSetTrans(cpShape **shapes, cpSpace *space) -{ - if(space->pooledArbiters->num == 0){ - // arbiter pool is exhausted, make more - int count = CP_BUFFER_BYTES/sizeof(cpArbiter); - cpAssertHard(count, "Internal Error: Buffer size too small."); - - cpArbiter *buffer = (cpArbiter *)cpcalloc(1, CP_BUFFER_BYTES); - cpArrayPush(space->allocatedBuffers, buffer); - - for(int i=0; ipooledArbiters, buffer + i); - } - - return cpArbiterInit((cpArbiter *)cpArrayPop(space->pooledArbiters), shapes[0], shapes[1]); -} - -static inline cpBool -QueryRejectConstraint(cpBody *a, cpBody *b) -{ - CP_BODY_FOREACH_CONSTRAINT(a, constraint){ - if( - !constraint->collideBodies && ( - (constraint->a == a && constraint->b == b) || - (constraint->a == b && constraint->b == a) - ) - ) return cpTrue; - } - - return cpFalse; -} - -static inline cpBool -QueryReject(cpShape *a, cpShape *b) -{ - return ( - // BBoxes must overlap - !cpBBIntersects(a->bb, b->bb) - // Don't collide shapes attached to the same body. - || a->body == b->body - // Don't collide shapes that are filtered. - || cpShapeFilterReject(a->filter, b->filter) - // Don't collide bodies if they have a constraint with collideBodies == cpFalse. - || QueryRejectConstraint(a->body, b->body) - ); -} - -// Callback from the spatial hash. -cpCollisionID -cpSpaceCollideShapes(cpShape *a, cpShape *b, cpCollisionID id, cpSpace *space) -{ - // Reject any of the simple cases - if(QueryReject(a,b)) return id; - - // Narrow-phase collision detection. - struct cpCollisionInfo info = cpCollide(a, b, id, cpContactBufferGetArray(space)); - - if(info.count == 0) return info.id; // Shapes are not colliding. - cpSpacePushContacts(space, info.count); - - // Get an arbiter from space->arbiterSet for the two shapes. - // This is where the persistant contact magic comes from. - const cpShape *shape_pair[] = {info.a, info.b}; - cpHashValue arbHashID = CP_HASH_PAIR((cpHashValue)info.a, (cpHashValue)info.b); - cpArbiter *arb = (cpArbiter *)cpHashSetInsert(space->cachedArbiters, arbHashID, shape_pair, (cpHashSetTransFunc)cpSpaceArbiterSetTrans, space); - cpArbiterUpdate(arb, &info, space); - - cpCollisionHandler *handler = arb->handler; - - // Call the begin function first if it's the first step - if(arb->state == CP_ARBITER_STATE_FIRST_COLLISION && !handler->beginFunc(arb, space, handler->userData)){ - cpArbiterIgnore(arb); // permanently ignore the collision until separation - } - - if( - // Ignore the arbiter if it has been flagged - (arb->state != CP_ARBITER_STATE_IGNORE) && - // Call preSolve - handler->preSolveFunc(arb, space, handler->userData) && - // Check (again) in case the pre-solve() callback called cpArbiterIgnored(). - arb->state != CP_ARBITER_STATE_IGNORE && - // Process, but don't add collisions for sensors. - !(a->sensor || b->sensor) && - // Don't process collisions between two infinite mass bodies. - // This includes collisions between two kinematic bodies, or a kinematic body and a static body. - !(a->body->m == INFINITY && b->body->m == INFINITY) - ){ - cpArrayPush(space->arbiters, arb); - } else { - cpSpacePopContacts(space, info.count); - - arb->contacts = NULL; - arb->count = 0; - - // Normally arbiters are set as used after calling the post-solve callback. - // However, post-solve() callbacks are not called for sensors or arbiters rejected from pre-solve. - if(arb->state != CP_ARBITER_STATE_IGNORE) arb->state = CP_ARBITER_STATE_NORMAL; - } - - // Time stamp the arbiter so we know it was used recently. - arb->stamp = space->stamp; - return info.id; -} - -// Hashset filter func to throw away old arbiters. -cpBool -cpSpaceArbiterSetFilter(cpArbiter *arb, cpSpace *space) -{ - cpTimestamp ticks = space->stamp - arb->stamp; - - cpBody *a = arb->body_a, *b = arb->body_b; - - // TODO: should make an arbiter state for this so it doesn't require filtering arbiters for dangling body pointers on body removal. - // Preserve arbiters on sensors and rejected arbiters for sleeping objects. - // This prevents errant separate callbacks from happenening. - if( - (cpBodyGetType(a) == CP_BODY_TYPE_STATIC || cpBodyIsSleeping(a)) && - (cpBodyGetType(b) == CP_BODY_TYPE_STATIC || cpBodyIsSleeping(b)) - ){ - return cpTrue; - } - - // Arbiter was used last frame, but not this one - if(ticks >= 1 && arb->state != CP_ARBITER_STATE_CACHED){ - arb->state = CP_ARBITER_STATE_CACHED; - cpCollisionHandler *handler = arb->handler; - handler->separateFunc(arb, space, handler->userData); - } - - if(ticks >= space->collisionPersistence){ - arb->contacts = NULL; - arb->count = 0; - - cpArrayPush(space->pooledArbiters, arb); - return cpFalse; - } - - return cpTrue; -} - -//MARK: All Important cpSpaceStep() Function - - void -cpShapeUpdateFunc(cpShape *shape, void *unused) -{ - cpShapeCacheBB(shape); -} - -void -cpSpaceStep(cpSpace *space, cpFloat dt) -{ - // don't step if the timestep is 0! - if(dt == 0.0f) return; - - space->stamp++; - - cpFloat prev_dt = space->curr_dt; - space->curr_dt = dt; - - cpArray *bodies = space->dynamicBodies; - cpArray *constraints = space->constraints; - cpArray *arbiters = space->arbiters; - - // Reset and empty the arbiter lists. - for(int i=0; inum; i++){ - cpArbiter *arb = (cpArbiter *)arbiters->arr[i]; - arb->state = CP_ARBITER_STATE_NORMAL; - - // If both bodies are awake, unthread the arbiter from the contact graph. - if(!cpBodyIsSleeping(arb->body_a) && !cpBodyIsSleeping(arb->body_b)){ - cpArbiterUnthread(arb); - } - } - arbiters->num = 0; - - cpSpaceLock(space); { - // Integrate positions - for(int i=0; inum; i++){ - cpBody *body = (cpBody *)bodies->arr[i]; - body->position_func(body, dt); - } - - // Find colliding pairs. - cpSpacePushFreshContactBuffer(space); - cpSpatialIndexEach(space->dynamicShapes, (cpSpatialIndexIteratorFunc)cpShapeUpdateFunc, NULL); - cpSpatialIndexReindexQuery(space->dynamicShapes, (cpSpatialIndexQueryFunc)cpSpaceCollideShapes, space); - } cpSpaceUnlock(space, cpFalse); - - // Rebuild the contact graph (and detect sleeping components if sleeping is enabled) - cpSpaceProcessComponents(space, dt); - - cpSpaceLock(space); { - // Clear out old cached arbiters and call separate callbacks - cpHashSetFilter(space->cachedArbiters, (cpHashSetFilterFunc)cpSpaceArbiterSetFilter, space); - - // Prestep the arbiters and constraints. - cpFloat slop = space->collisionSlop; - cpFloat biasCoef = 1.0f - cpfpow(space->collisionBias, dt); - for(int i=0; inum; i++){ - cpArbiterPreStep((cpArbiter *)arbiters->arr[i], dt, slop, biasCoef); - } - - for(int i=0; inum; i++){ - cpConstraint *constraint = (cpConstraint *)constraints->arr[i]; - - cpConstraintPreSolveFunc preSolve = constraint->preSolve; - if(preSolve) preSolve(constraint, space); - - constraint->klass->preStep(constraint, dt); - } - - // Integrate velocities. - cpFloat damping = cpfpow(space->damping, dt); - cpVect gravity = space->gravity; - for(int i=0; inum; i++){ - cpBody *body = (cpBody *)bodies->arr[i]; - body->velocity_func(body, gravity, damping, dt); - } - - // Apply cached impulses - cpFloat dt_coef = (prev_dt == 0.0f ? 0.0f : dt/prev_dt); - for(int i=0; inum; i++){ - cpArbiterApplyCachedImpulse((cpArbiter *)arbiters->arr[i], dt_coef); - } - - for(int i=0; inum; i++){ - cpConstraint *constraint = (cpConstraint *)constraints->arr[i]; - constraint->klass->applyCachedImpulse(constraint, dt_coef); - } - - // Run the impulse solver. - for(int i=0; iiterations; i++){ - for(int j=0; jnum; j++){ - cpArbiterApplyImpulse((cpArbiter *)arbiters->arr[j]); - } - - for(int j=0; jnum; j++){ - cpConstraint *constraint = (cpConstraint *)constraints->arr[j]; - constraint->klass->applyImpulse(constraint, dt); - } - } - - // Run the constraint post-solve callbacks - for(int i=0; inum; i++){ - cpConstraint *constraint = (cpConstraint *)constraints->arr[i]; - - cpConstraintPostSolveFunc postSolve = constraint->postSolve; - if(postSolve) postSolve(constraint, space); - } - - // run the post-solve callbacks - for(int i=0; inum; i++){ - cpArbiter *arb = (cpArbiter *) arbiters->arr[i]; - - cpCollisionHandler *handler = arb->handler; - handler->postSolveFunc(arb, space, handler->userData); - } - } cpSpaceUnlock(space, cpTrue); -} diff --git a/apecs-physics/Chipmunk2D/src/cpSpatialIndex.c b/apecs-physics/Chipmunk2D/src/cpSpatialIndex.c deleted file mode 100644 index 0656937..0000000 --- a/apecs-physics/Chipmunk2D/src/cpSpatialIndex.c +++ /dev/null @@ -1,69 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "chipmunk_private.h" - -void -cpSpatialIndexFree(cpSpatialIndex *index) -{ - if(index){ - cpSpatialIndexDestroy(index); - cpfree(index); - } -} - -cpSpatialIndex * -cpSpatialIndexInit(cpSpatialIndex *index, cpSpatialIndexClass *klass, cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex) -{ - index->klass = klass; - index->bbfunc = bbfunc; - index->staticIndex = staticIndex; - - if(staticIndex){ - cpAssertHard(!staticIndex->dynamicIndex, "This static index is already associated with a dynamic index."); - staticIndex->dynamicIndex = index; - } - - return index; -} - -typedef struct dynamicToStaticContext { - cpSpatialIndexBBFunc bbfunc; - cpSpatialIndex *staticIndex; - cpSpatialIndexQueryFunc queryFunc; - void *data; -} dynamicToStaticContext; - -static void -dynamicToStaticIter(void *obj, dynamicToStaticContext *context) -{ - cpSpatialIndexQuery(context->staticIndex, obj, context->bbfunc(obj), context->queryFunc, context->data); -} - -void -cpSpatialIndexCollideStatic(cpSpatialIndex *dynamicIndex, cpSpatialIndex *staticIndex, cpSpatialIndexQueryFunc func, void *data) -{ - if(staticIndex && cpSpatialIndexCount(staticIndex) > 0){ - dynamicToStaticContext context = {dynamicIndex->bbfunc, staticIndex, func, data}; - cpSpatialIndexEach(dynamicIndex, (cpSpatialIndexIteratorFunc)dynamicToStaticIter, &context); - } -} - diff --git a/apecs-physics/Chipmunk2D/src/cpSweep1D.c b/apecs-physics/Chipmunk2D/src/cpSweep1D.c deleted file mode 100644 index 4f56724..0000000 --- a/apecs-physics/Chipmunk2D/src/cpSweep1D.c +++ /dev/null @@ -1,254 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "chipmunk_private.h" - -static inline cpSpatialIndexClass *Klass(); - -//MARK: Basic Structures - -typedef struct Bounds { - cpFloat min, max; -} Bounds; - -typedef struct TableCell { - void *obj; - Bounds bounds; -} TableCell; - -struct cpSweep1D -{ - cpSpatialIndex spatialIndex; - - int num; - int max; - TableCell *table; -}; - -static inline cpBool -BoundsOverlap(Bounds a, Bounds b) -{ - return (a.min <= b.max && b.min <= a.max); -} - -static inline Bounds -BBToBounds(cpSweep1D *sweep, cpBB bb) -{ - Bounds bounds = {bb.l, bb.r}; - return bounds; -} - -static inline TableCell -MakeTableCell(cpSweep1D *sweep, void *obj) -{ - TableCell cell = {obj, BBToBounds(sweep, sweep->spatialIndex.bbfunc(obj))}; - return cell; -} - -//MARK: Memory Management Functions - -cpSweep1D * -cpSweep1DAlloc(void) -{ - return (cpSweep1D *)cpcalloc(1, sizeof(cpSweep1D)); -} - -static void -ResizeTable(cpSweep1D *sweep, int size) -{ - sweep->max = size; - sweep->table = (TableCell *)cprealloc(sweep->table, size*sizeof(TableCell)); -} - -cpSpatialIndex * -cpSweep1DInit(cpSweep1D *sweep, cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex) -{ - cpSpatialIndexInit((cpSpatialIndex *)sweep, Klass(), bbfunc, staticIndex); - - sweep->num = 0; - ResizeTable(sweep, 32); - - return (cpSpatialIndex *)sweep; -} - -cpSpatialIndex * -cpSweep1DNew(cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex) -{ - return cpSweep1DInit(cpSweep1DAlloc(), bbfunc, staticIndex); -} - -static void -cpSweep1DDestroy(cpSweep1D *sweep) -{ - cpfree(sweep->table); - sweep->table = NULL; -} - -//MARK: Misc - -static int -cpSweep1DCount(cpSweep1D *sweep) -{ - return sweep->num; -} - -static void -cpSweep1DEach(cpSweep1D *sweep, cpSpatialIndexIteratorFunc func, void *data) -{ - TableCell *table = sweep->table; - for(int i=0, count=sweep->num; itable; - for(int i=0, count=sweep->num; inum == sweep->max) ResizeTable(sweep, sweep->max*2); - - sweep->table[sweep->num] = MakeTableCell(sweep, obj); - sweep->num++; -} - -static void -cpSweep1DRemove(cpSweep1D *sweep, void *obj, cpHashValue hashid) -{ - TableCell *table = sweep->table; - for(int i=0, count=sweep->num; inum; - - table[i] = table[num]; - table[num].obj = NULL; - - return; - } - } -} - -//MARK: Reindexing Functions - -static void -cpSweep1DReindexObject(cpSweep1D *sweep, void *obj, cpHashValue hashid) -{ - // Nothing to do here -} - -static void -cpSweep1DReindex(cpSweep1D *sweep) -{ - // Nothing to do here - // Could perform a sort, but queries are not accelerated anyway. -} - -//MARK: Query Functions - -static void -cpSweep1DQuery(cpSweep1D *sweep, void *obj, cpBB bb, cpSpatialIndexQueryFunc func, void *data) -{ - // Implementing binary search here would allow you to find an upper limit - // but not a lower limit. Probably not worth the hassle. - - Bounds bounds = BBToBounds(sweep, bb); - - TableCell *table = sweep->table; - for(int i=0, count=sweep->num; itable; - for(int i=0, count=sweep->num; ibounds.min < b->bounds.min ? -1 : (a->bounds.min > b->bounds.min ? 1 : 0)); -} - -static void -cpSweep1DReindexQuery(cpSweep1D *sweep, cpSpatialIndexQueryFunc func, void *data) -{ - TableCell *table = sweep->table; - int count = sweep->num; - - // Update bounds and sort - for(int i=0; ispatialIndex.staticIndex, func, data); -} - -static cpSpatialIndexClass klass = { - (cpSpatialIndexDestroyImpl)cpSweep1DDestroy, - - (cpSpatialIndexCountImpl)cpSweep1DCount, - (cpSpatialIndexEachImpl)cpSweep1DEach, - (cpSpatialIndexContainsImpl)cpSweep1DContains, - - (cpSpatialIndexInsertImpl)cpSweep1DInsert, - (cpSpatialIndexRemoveImpl)cpSweep1DRemove, - - (cpSpatialIndexReindexImpl)cpSweep1DReindex, - (cpSpatialIndexReindexObjectImpl)cpSweep1DReindexObject, - (cpSpatialIndexReindexQueryImpl)cpSweep1DReindexQuery, - - (cpSpatialIndexQueryImpl)cpSweep1DQuery, - (cpSpatialIndexSegmentQueryImpl)cpSweep1DSegmentQuery, -}; - -static inline cpSpatialIndexClass *Klass(){return &klass;} - diff --git a/apecs-physics/LICENSE b/apecs-physics/LICENSE deleted file mode 100644 index 32cc34e..0000000 --- a/apecs-physics/LICENSE +++ /dev/null @@ -1,30 +0,0 @@ -Copyright Jonas Carpay (c) 2017 - -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - - * Neither the name of Jonas Carpay nor the names of other - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/apecs-physics/README.md b/apecs-physics/README.md deleted file mode 100644 index 4c4973c..0000000 --- a/apecs-physics/README.md +++ /dev/null @@ -1,157 +0,0 @@ -# apecs-physics - -2D physics library for apecs. -Uses the [Chipmunk](https://github.com/slembcke/Chipmunk2D) physics engine. -The [apecs-gloss](../apecs-gloss) package provides a simple optional [gloss](https://github.com/benl23x5/gloss)-based renderer. - -Feel free to create an issue or PR for suggestions/questions/requests/critiques/spelling fixes/etc. -See [TODO.md](TODO.md) for suggestions if you want to help out with the code. - -The examples directory contains a number of examples, each can be run with `stack build && stack `: - -### helloworld -![Screenshot](helloworld.png) - -```haskell -makeWorld "World" [''Physics, ''BodyPicture, ''Camera] -``` -Generate a world. -The `Physics` component adds a physics space to the world. -The `BodyPicture` contains a gloss `Picture`, which the renderer will match to the `Body`'s position and orientation. -The `Camera` component tracks a camera position and zoom factor. - -```haskell -initialize = do - set global ( Camera (V2 0 1) 60 - , earthGravity ) -``` -Globals can be set with any entity argument, `global` is just an alias for -1. -`earthGravity = V2 0 (-9.81)`, normal earth surface gravity if we assume normal MKS units. -Note that the positive y-axis points upwards. - -```haskell - let ballShape = cCircle 0.5 - newEntity ( DynamicBody - , Shape ballShape - , Position (V2 0 3) - , Density 1 - , Elasticity 0.9 - , BodyPicture . color red . toPicture $ ballShape ) -``` -Still in the initialize function, here we see our first object being instantiated. -The type of ballShape is `Convex`, the apecs-physics format for shapes. -`Convex` is a convex polygon, consisting of a number of vertices and a radius. -In the case of a circle, the polygon consists of a single point with a non-zero radius. -Both Chipmunk and gloss only support convex polygons, `Convex` is used to give them a common interface. - -A `DynamicBody` is one of three types of bodies. -It is a normal body, fully affected by physical forces. -The elasticity of a collision is the product of the elasticities of the colliding shapes. - -The final line shows how to do rendering. -`BodyPicture` expects a gloss `Picture`, in this case we derive one from `ballShape :: Convex` using `toPicture`. -`color red` comes from gloss, and is just one of the many `Picture` manipulation functions. -Alternatively, you can use a `Bitmap` to use actual sprites. - -```haskell - let lineShape = hLine 6 - newEntity ( StaticBody - , Angle (-pi/20) - , Shape lineShape - , Elasticity 0.9 - , BodyPicture . color white . toPicture $ lineShape ) -``` -Static bodies are not affected by physics, and generally rarely move. -They are equivalent to bodies with infinite mass and moment, and zero velocity. -Changing their position triggers an explicit rehash of their shapes, wish is relatively expensive. - -```haskell -main = do - w <- initWorld - runSystem initialize w - defaultSimulate w -``` -`defaultSimulate` is a convenience wrapper around gloss' `simulateIO`. -You can find its definition in `Apecs.Physics.Gloss`, in case you want to change the rendering behavior. - -### tumbler -![Screenshot](tumbler.png) - -```haskell -initialize :: System World () -initialize = do - set global ( Camera 0 50 - , earthGravity ) - - let sides = toEdges $ cRectangle 5 - tumbler <- newEntity ( KinematicBody - , AngularVelocity (-1) - , BodyPicture . color white . foldMap toPicture $ sides ) -``` -As previously stated, both Chipmunk and gloss exclusively have _convex_ polygon primitives. -Our tumbler, however, is obiously not convex. -Fortunately, composing shapes is really easy. -We use `toEdges` to turn a rectangle into an outline of one, and use `foldMap` to make a composite `Picture`. - -A `KinematicBody` is halfway between a `DynamicBody` and a `StaticBody`. -It can have an (angular) velocity, but will not respond to forces. -It can be used for e.g. moving platforms, or in this case. -Note that we did not add any shapes to the tumbler yet. - -```haskell - forM_ sides $ \line -> newEntity (ShapeExtend (cast tumbler) $ setRadius 0.05 line) -``` -The time has come to talk about the destinction between shapes and bodies. -A body can have multiple shapes. -Shapes belonging to the same body _cannot move relative to one another_, i.e. a body is a fixture for multiple shapes. -When using the normal `Shape` data constructor to add a shape to a body, we actually create two Chipmunk structs; one for the body, and one for the shape, even though they are addressed by the same entity in apecs. - -When we want to add multiple shapes to a body, however, we need to make new entities for each individual shape. -The reason for this is that this way, we can still easily change the properties of each individual shape. -`Shape` actually just represents a special case of `ShapeExtend`, the case in which the body has the same entity as the shape. - -When you use a tuple of components in apecs, they are added in the order you list them in the tuple. -This is important to realize, as _adding a shape to an entity wihout a body is a noop_. -Always make sure you first add a body, and then the shapes. -This also comes up when e.g. setting a shape's properties: you can only set a shape's `Mass` or `Density` when there is a shape in the first place. -If you don't, you will get a runtime error about simulating zero-mass `DynamicBodies`. - -```haskell - replicateM_ 200 $ do - x <- liftIO$ randomRIO (-2, 2) - y <- liftIO$ randomRIO (-2, 2) - r <- liftIO$ randomRIO (0.1, 0.2) - let ballshape = cCircle r - let c = (realToFrac x+2)/3 - newEntity ( DynamicBody - , Position (V2 x y) - , Shape ballshape - , BodyPicture . color (makeColor 1 c c 1) . toPicture $ ballshape - , Density 1 ) - - return () -``` -Finally, we randomly add a bunch of balls. - -### constraints -![Screenshot](constraints.png) - -The final example is a gallery of (some of) the available constraints. -Drag shapes around with the left mouse button, create a new box with the right. - -This example is too large to fully include here, but if you have made it this far, I recommend looking at the source. -Aside from demonstrating constraints, queries and interaction it also contains some neat tricks like: -```haskell -let rubber = (Friction 0.5, Elasticity 0.5, Density 1) -newEntity ( DynamicBody - , someShape - , rubber ) -``` -Nesting tuples creates composable and reusable pieces of configuration (this is an apecs thing, not an apecs-physics thing). -This can also be useful if you find yourself needing bigger tuples than the current maximum. - -Constraints are a lot like shapes, but instead of having one associated `Body`, they have two. -It also comes in the varieties `Constraint` and `ConstraintExtend`. - -Dragging an object with the mouse is also done using a constraint. -The mouse position actually controls the position of a static body without shapes, and we use a PinJoint to attach whatever we are dragging to it. diff --git a/apecs-physics/Setup.hs b/apecs-physics/Setup.hs deleted file mode 100644 index f5bb1bc..0000000 --- a/apecs-physics/Setup.hs +++ /dev/null @@ -1,50 +0,0 @@ -{-# LANGUAGE CPP #-} -import Distribution.Simple -import Distribution.Verbosity -import Distribution.PackageDescription -#if defined(MIN_VERSION_Cabal) && MIN_VERSION_Cabal(2,2,0) -import Distribution.PackageDescription.Parsec -#else -import Distribution.PackageDescription.Parse -#endif - - -main = do -#if defined(MIN_VERSION_Cabal) && MIN_VERSION_Cabal(2,0,0) - pkgDescr <- readGenericPackageDescription verbose "apecs-physics.cabal" -#else - pkgDescr <- readPackageDescription verbose "apecs-physics.cabal" -#endif - defaultMainNoRead . addCFilesIfNeeded $ pkgDescr - where - addCFilesIfNeeded = -#if __GLASGOW_HASKELL__ >= 802 - id -#else - addCFiles -#endif - - addCFiles genPkgDescr = genPkgDescr - { condLibrary = fmap addCFiles' pkgLibrary - } - where - pkgLibrary = condLibrary genPkgDescr - - addCFiles' (CondNode lib constraints components) = - CondNode (addCFiles'' lib) constraints components - - addCFiles'' lib = lib - { libBuildInfo = - let buildInfo = libBuildInfo lib in - buildInfo - { cSources = cSources buildInfo ++ - [ "src/Apecs/Physics/Constraint.c" - , "src/Apecs/Physics/Space.c" - , "src/Apecs/Physics/Query.c" - , "src/Apecs/Physics/Body.c" - , "src/Apecs/Physics/Collision.c" - , "src/Apecs/Physics/Shape.c" - ] - } - } - diff --git a/apecs-physics/TODO.md b/apecs-physics/TODO.md deleted file mode 100644 index 338dcd3..0000000 --- a/apecs-physics/TODO.md +++ /dev/null @@ -1,44 +0,0 @@ -### Need -- [x] Make separate example executables -- [x] Constraints -- [x] Collision Handling -- [x] CollideBodies component for Constraints, currently hardcoded to never collide -- [x] Remaining Collision callbacks -- [x] Add Gloss `play` version to allow interactive games -- [x] Implement body force components -- [x] Implement remaining body components -- [ ] Conversion functions between body- and world space -- [x] Implement remaining constraints -- [ ] Free FunPtrs (we currently leak a small amount of memory) -- [x] Add explicitly managed shapes whose properties you can change at runtime -- [x] Check(enforce?) proper deallocation of bodies/shapes/constraints on overwrites etc. -- [ ] Remaining spatial queries -- [x] Fix issues on [travis](https://travis-ci.org/jonascarpay/apecs-physics) -- [x] Hackage -- [x] Presolve/Postsolve collision handlers -- [x] Remaining collision properties -- [x] Wildcard collision handlers -- [x] Explicit gloss Picture component, make shapes write-only -- [ ] Haddocks -- [x] Proper SpacePtr cleanup -- [x] Proper convex polygon rendering -- [ ] Clean up pragmas -- [x] Move examples to separate package - -### Want -- [x] Move rendering to separate package? -- [ ] Solve orphan instances -- [ ] Consistent variable naming -- [ ] Tests -- [x] Render Constraints -- [x] `playWorld` camera tracking? -- [ ] Documentation/tutorials? -- [ ] Switch to normal FFI instead of inline-c? -- [x] Better naming for shapes/convex -- [ ] Get constaint properties (getConstraintImpulse, getPinJointDistance, etc.) -- [ ] Include entity index from data pointer in Chipmunk error messages -- [ ] Implement remaining global properties (errorbias etc.) -- [ ] Apply(Impulse/Force)At(Local/World) -- [ ] Benchmarks -- [ ] Performance tuning (inlining etc.) -- [ ] Conditionally compiled (debug flag) warnings when setting e.g. position without a body, or density without a shape diff --git a/apecs-physics/apecs-physics.cabal b/apecs-physics/apecs-physics.cabal deleted file mode 100644 index 7aa1eaf..0000000 --- a/apecs-physics/apecs-physics.cabal +++ /dev/null @@ -1,131 +0,0 @@ -name: apecs-physics -version: 0.4.4 -synopsis: 2D physics for apecs -description: 2D physics for apecs. Uses Chipmunk physics library under the hood. -homepage: https://github.com/jonascarpay/apecs#readme -license: BSD3 -license-file: LICENSE -author: Jonas Carpay -maintainer: jonascarpay@gmail.com -copyright: MIT -category: Web -build-type: Custom -cabal-version: >=1.10 -extra-source-files: - README.md, - CHANGELOG.md, - Chipmunk2D/include/chipmunk/prime.h, - Chipmunk2D/include/chipmunk/chipmunk_private.h, - Chipmunk2D/include/chipmunk/chipmunk_types.h, - Chipmunk2D/include/chipmunk/cpSlideJoint.h, - Chipmunk2D/include/chipmunk/cpBB.h, - Chipmunk2D/include/chipmunk/cpPolyline.h, - Chipmunk2D/include/chipmunk/cpBody.h, - Chipmunk2D/include/chipmunk/chipmunk_unsafe.h, - Chipmunk2D/include/chipmunk/cpSimpleMotor.h, - Chipmunk2D/include/chipmunk/chipmunk.h, - Chipmunk2D/include/chipmunk/cpSpatialIndex.h, - Chipmunk2D/include/chipmunk/cpRobust.h, - Chipmunk2D/include/chipmunk/chipmunk_structs.h, - Chipmunk2D/include/chipmunk/cpMarch.h, - Chipmunk2D/include/chipmunk/cpTransform.h, - Chipmunk2D/include/chipmunk/cpShape.h, - Chipmunk2D/include/chipmunk/cpConstraint.h, - Chipmunk2D/include/chipmunk/cpGrooveJoint.h, - Chipmunk2D/include/chipmunk/cpHastySpace.h, - Chipmunk2D/include/chipmunk/cpGearJoint.h, - Chipmunk2D/include/chipmunk/chipmunk_ffi.h, - Chipmunk2D/include/chipmunk/cpRatchetJoint.h, - Chipmunk2D/include/chipmunk/cpRotaryLimitJoint.h, - Chipmunk2D/include/chipmunk/cpVect.h, - Chipmunk2D/include/chipmunk/cpArbiter.h, - Chipmunk2D/include/chipmunk/cpSpace.h, - Chipmunk2D/include/chipmunk/cpDampedRotarySpring.h, - Chipmunk2D/include/chipmunk/cpPinJoint.h, - Chipmunk2D/include/chipmunk/cpDampedSpring.h, - Chipmunk2D/include/chipmunk/cpPolyShape.h, - Chipmunk2D/include/chipmunk/cpPivotJoint.h - -source-repository HEAD - type: git - location: git://github.com/jonascarpay/apecs.git - -custom-setup - setup-depends: - base >= 4.9 && < 5, - Cabal >= 1.14 - -flag release - description: Release mode, better performance but no longer provides debug info on the command line. - Manual: True - default: False - -library - hs-source-dirs: - src - default-language: - Haskell2010 - exposed-modules: - Apecs.Physics - other-modules: - Apecs.Physics.Body, - Apecs.Physics.Constraint, - Apecs.Physics.Collision, - Apecs.Physics.Geometry, - Apecs.Physics.Shape, - Apecs.Physics.Space, - Apecs.Physics.Types - Apecs.Physics.Query - build-depends: - apecs >= 0.7 && < 0.10, - base >= 4.9 && < 5, - containers >= 0.5 && < 0.8, - inline-c >= 0.6 && < 1, - linear >= 1.20 && < 2, - template-haskell >= 2.12 && < 3, - vector >= 0.10 && < 0.13 - ghc-options: - -Wall - -O2 - -Wno-orphans - -Wno-unused-do-bind - -Wno-name-shadowing - cc-options: - -std=c99 - if flag(release) - cc-options: -DNDEBUG - include-dirs: - Chipmunk2D/include/chipmunk - c-sources: - Chipmunk2D/src/chipmunk.c - Chipmunk2D/src/cpBody.c - Chipmunk2D/src/cpArbiter.c - Chipmunk2D/src/cpArray.c - Chipmunk2D/src/cpBBTree.c - Chipmunk2D/src/cpCollision.c - Chipmunk2D/src/cpConstraint.c - Chipmunk2D/src/cpDampedRotarySpring.c - Chipmunk2D/src/cpDampedSpring.c - Chipmunk2D/src/cpGearJoint.c - Chipmunk2D/src/cpGrooveJoint.c - Chipmunk2D/src/cpHashSet.c - Chipmunk2D/src/cpHastySpace.c - Chipmunk2D/src/cpMarch.c - Chipmunk2D/src/cpPinJoint.c - Chipmunk2D/src/cpPivotJoint.c - Chipmunk2D/src/cpPolyShape.c - Chipmunk2D/src/cpPolyline.c - Chipmunk2D/src/cpRatchetJoint.c - Chipmunk2D/src/cpRobust.c - Chipmunk2D/src/cpRotaryLimitJoint.c - Chipmunk2D/src/cpShape.c - Chipmunk2D/src/cpSimpleMotor.c - Chipmunk2D/src/cpSlideJoint.c - Chipmunk2D/src/cpSpace.c - Chipmunk2D/src/cpSpaceComponent.c - Chipmunk2D/src/cpSpaceDebug.c - Chipmunk2D/src/cpSpaceHash.c - Chipmunk2D/src/cpSpaceQuery.c - Chipmunk2D/src/cpSpaceStep.c - Chipmunk2D/src/cpSpatialIndex.c - Chipmunk2D/src/cpSweep1D.c diff --git a/apecs-physics/constraints.png b/apecs-physics/constraints.png deleted file mode 100644 index ebb7fb9..0000000 Binary files a/apecs-physics/constraints.png and /dev/null differ diff --git a/apecs-physics/helloworld.png b/apecs-physics/helloworld.png deleted file mode 100644 index ac90440..0000000 Binary files a/apecs-physics/helloworld.png and /dev/null differ diff --git a/apecs-physics/src/Apecs/Physics.hs b/apecs-physics/src/Apecs/Physics.hs deleted file mode 100644 index e46895c..0000000 --- a/apecs-physics/src/Apecs/Physics.hs +++ /dev/null @@ -1,63 +0,0 @@ --- | apecs-physics prelude - -module Apecs.Physics ( - - -- * General - Physics, - - -- * Space - Gravity (..), Iterations (..), - stepPhysics, - earthGravity, - - -- * Body - -- $BODY - Body (..), Position (..), Velocity (..), Angle (..), AngularVelocity (..), Force (..), - BodyMass (..), Moment (..), CenterOfGravity (..), Torque (..), ShapeList (..), ConstraintList (..), - - -- * Shape - Convex (..), Shape (..), - Mass (..), Density (..), Sensor (..), Friction (..), Elasticity (..), SurfaceVelocity (..), - CollisionFilter (..), CollisionType(..), - Bitmask (..), maskAll, maskNone, maskList, defaultFilter, boxShape, - - -- * Constraint - Constraint (..), ConstraintType (..), MaxForce (..), MaxBias (..), ErrorBias (..), Impulse (..), CollideBodies (..), - - -- * Collision - Collision (..), CollisionHandler (..), defaultHandler, - CollisionSource(..), BeginCB, SeparateCB, PreSolveCB, PostSolveCB, - mkBeginCB, mkSeparateCB, mkPreSolveCB, mkPostSolveCB, addPostStepCallback, - - -- * Query - PointQueryResult (..), - pointQuery, - - -- * Geometry - module Apecs.Physics.Geometry, - BVec, WVec, - - module Apecs, - module Linear.V2, - ) where - -import Apecs -import Linear.V2 - -import Apecs.Physics.Body () -import Apecs.Physics.Collision -import Apecs.Physics.Constraint () -import Apecs.Physics.Geometry -import Apecs.Physics.Query -import Apecs.Physics.Shape -import Apecs.Physics.Space -import Apecs.Physics.Types - - --- $BODY --- When you give an entity a @'Body'@ component in 'apecs-physics', the physics engine will --- also give this entity a number of __sub-components__. --- These sub-components may be read and written separately from the actualy @'Body'@ itself, --- which makes the library both more expressive (as you can only write about the parts of a --- physics body you actually want to view or change) and more performant --- (as only the changed parts of a body actually need to be updated when you write to them). diff --git a/apecs-physics/src/Apecs/Physics/Body.hs b/apecs-physics/src/Apecs/Physics/Body.hs deleted file mode 100644 index ecc055a..0000000 --- a/apecs-physics/src/Apecs/Physics/Body.hs +++ /dev/null @@ -1,437 +0,0 @@ -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE GeneralizedNewtypeDeriving #-} -{-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE QuasiQuotes #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE Strict #-} -{-# LANGUAGE TemplateHaskell #-} -{-# LANGUAGE TypeApplications #-} -{-# LANGUAGE TypeFamilies #-} -{-# LANGUAGE UndecidableInstances #-} -{-# LANGUAGE ViewPatterns #-} - -module Apecs.Physics.Body where - -import Apecs -import Apecs.Core -import Control.Monad -import Control.Monad.IO.Class (liftIO, MonadIO) -import qualified Data.IntMap as M -import qualified Data.IntSet as S -import Data.IORef -import qualified Data.Vector.Unboxed as U -import Foreign.ForeignPtr (withForeignPtr) -import Foreign.Ptr -import qualified Language.C.Inline as C -import Linear.V2 - -import Apecs.Physics.Space () -import Apecs.Physics.Shape () -import Apecs.Physics.Constraint () -import Apecs.Physics.Types - -C.context phycsCtx -C.include "" - --- Body -newBody :: SpacePtr -> Int -> IO (Ptr Body) -newBody spacePtr (fromIntegral -> ety) = withForeignPtr spacePtr $ \space -> [C.block| cpBody* { - cpBody* body = cpBodyNew(0,0); - cpSpaceAddBody($(cpSpace* space), body); - cpBodySetUserData(body, (void*) $(intptr_t ety)); - return body; } |] - -setBodyType :: Ptr Body -> Body -> IO () -setBodyType bodyPtr (fromIntegral . fromEnum -> bodyInt) = - [C.exp| void { cpBodySetType($(cpBody* bodyPtr), $(int bodyInt)) } |] - -getBodyType :: Ptr Body -> IO Body -getBodyType bodyPtr = toEnum . fromIntegral <$> [C.exp| int { cpBodyGetType($(cpBody* bodyPtr)) } |] - -destroyBody :: SpacePtr -> Ptr Body -> IO () -destroyBody spacePtr bodyPtr = withForeignPtr spacePtr $ \space -> [C.block| void { - cpBody *body = $(cpBody* bodyPtr); - cpSpaceRemoveBody($(cpSpace* space), body); - cpBodyFree(body); }|] - -instance Component Body where - type Storage Body = Space Body - -instance (MonadIO m, Has w m Physics) => Has w m Body where - getStore = (cast :: Space Physics -> Space Body) <$> getStore - -instance MonadIO m => ExplSet m (Space Body) where - explSet (Space bMap _ _ _ spcPtr) ety btype = liftIO $ do - rd <- M.lookup ety <$> readIORef bMap - bdyPtr <- case rd of - Just (BodyRecord bdyPtr _ _ _) -> return bdyPtr - Nothing -> do - bdyPtr <- newBody spcPtr ety - bsMap <- newIORef mempty - bcMap <- newIORef mempty - modifyIORef' bMap (M.insert ety $ BodyRecord bdyPtr btype bsMap bcMap) - return bdyPtr - setBodyType bdyPtr btype - -instance MonadIO m => ExplDestroy m (Space Body) where - explDestroy sp@(Space bMap _ _ _ spc) ety = liftIO $ do - rd <- M.lookup ety <$> readIORef bMap - modifyIORef' bMap (M.delete ety) - forM_ rd $ \(BodyRecord bPtr _ shapes constraints) -> do - readIORef shapes >>= mapM_ (explDestroy (cast sp :: Space Shape)) . S.toList - readIORef constraints >>= mapM_ (explDestroy (cast sp :: Space Constraint)) . S.toList - destroyBody spc bPtr - -instance MonadIO m => ExplMembers m (Space Body) where - explMembers (Space bMap _ _ _ _) = liftIO $ U.fromList . M.keys <$> readIORef bMap - -instance MonadIO m => ExplGet m (Space Body) where - explExists (Space bMap _ _ _ _) ety = liftIO $ M.member ety <$> readIORef bMap - explGet (Space bMap _ _ _ _) ety = liftIO $ do - Just (BodyRecord _ b _ _) <- M.lookup ety <$> readIORef bMap - return b - --- Position -getPosition :: Ptr Body -> IO (V2 Double) -getPosition bodyPtr = do - x <- [C.exp| double { cpBodyGetPosition ($(cpBody* bodyPtr)).x } |] - y <- [C.exp| double { cpBodyGetPosition ($(cpBody* bodyPtr)).y } |] - return (V2 (realToFrac x) (realToFrac y)) - -setPosition :: Ptr Body -> V2 Double -> IO () -setPosition bodyPtr (V2 (realToFrac -> x) (realToFrac -> y)) = [C.block| void { - const cpVect pos = { $(double x), $(double y) }; - cpBody *body = $(cpBody* bodyPtr); - cpBodySetPosition(body, pos); - if (cpBodyGetType(body) == CP_BODY_TYPE_STATIC) - cpSpaceReindexShapesForBody(cpBodyGetSpace(body), body); - } |] - -instance Component Position where - type Storage Position = Space Position - -instance (MonadIO m, Has w m Physics) => Has w m Position where - getStore = (cast :: Space Physics -> Space Position) <$> getStore - -instance MonadIO m => ExplMembers m (Space Position) where - explMembers s = explMembers (cast s :: Space Body) - -instance MonadIO m => ExplSet m (Space Position) where - explSet (Space bMap _ _ _ _) ety (Position pos) = liftIO $ do - rd <- M.lookup ety <$> readIORef bMap - forM_ rd$ \(BodyRecord b _ _ _) -> setPosition b pos - -instance MonadIO m => ExplGet m (Space Position) where - explExists s ety = explExists (cast s :: Space Body) ety - explGet (Space bMap _ _ _ _) ety = liftIO $ do - Just (BodyRecord b _ _ _) <- M.lookup ety <$> readIORef bMap - Position <$> getPosition b - --- Velocity -getVelocity :: Ptr Body -> IO (V2 Double) -getVelocity bodyPtr = do - x <- [C.exp| double { cpBodyGetVelocity ($(cpBody* bodyPtr)).x } |] - y <- [C.exp| double { cpBodyGetVelocity ($(cpBody* bodyPtr)).y } |] - return (V2 (realToFrac x) (realToFrac y)) - -setVelocity :: Ptr Body -> V2 Double -> IO () -setVelocity bodyPtr (V2 (realToFrac -> x) (realToFrac -> y)) = [C.block| void { - const cpVect vel = { $(double x), $(double y) }; - cpBodySetVelocity($(cpBody* bodyPtr), vel); - } |] - -instance Component Velocity where - type Storage Velocity = Space Velocity - -instance (MonadIO m, Has w m Physics) => Has w m Velocity where - getStore = (cast :: Space Physics -> Space Velocity) <$> getStore - -instance MonadIO m => ExplMembers m (Space Velocity) where - explMembers s = explMembers (cast s :: Space Body) - -instance MonadIO m => ExplSet m (Space Velocity) where - explSet (Space bMap _ _ _ _) ety (Velocity vel) = liftIO $ do - rd <- M.lookup ety <$> readIORef bMap - forM_ rd$ \(BodyRecord b _ _ _) -> setVelocity b vel - -instance MonadIO m => ExplGet m (Space Velocity) where - explExists s ety = explExists (cast s :: Space Body) ety - explGet (Space bMap _ _ _ _) ety = liftIO $ do - Just (BodyRecord b _ _ _) <- M.lookup ety <$> readIORef bMap - Velocity <$> getVelocity b - --- Angle -getAngle :: Ptr Body -> IO Double -getAngle bodyPtr = do - angle <- [C.exp| double { cpBodyGetAngle ($(cpBody* bodyPtr)) } |] - return (realToFrac angle) - -setAngle :: Ptr Body -> Double -> IO () -setAngle bodyPtr (realToFrac -> angle) = [C.block| void { - cpBody *body = $(cpBody* bodyPtr); - cpBodySetAngle(body, $(double angle)); - if (cpBodyGetType(body) == CP_BODY_TYPE_STATIC) - cpSpaceReindexShapesForBody(cpBodyGetSpace(body), body); - } |] - -- FIXME reindex - -instance Component Angle where - type Storage Angle = Space Angle - -instance (MonadIO m, Has w m Physics) => Has w m Angle where - getStore = (cast :: Space Physics -> Space Angle) <$> getStore - -instance MonadIO m => ExplMembers m (Space Angle) where - explMembers s = explMembers (cast s :: Space Body) - -instance MonadIO m => ExplSet m (Space Angle) where - explSet (Space bMap _ _ _ _) ety (Angle angle) = liftIO $ do - rd <- M.lookup ety <$> readIORef bMap - forM_ rd $ \(BodyRecord b _ _ _) -> setAngle b angle - -instance MonadIO m => ExplGet m (Space Angle) where - explExists s ety = explExists (cast s :: Space Body) ety - explGet (Space bMap _ _ _ _) ety = liftIO $ do - Just (BodyRecord b _ _ _) <- M.lookup ety <$> readIORef bMap - Angle <$> getAngle b - --- AngularVelocity -getAngularVelocity :: Ptr Body -> IO Double -getAngularVelocity bodyPtr = do - angle <- [C.exp| double { cpBodyGetAngularVelocity ($(cpBody* bodyPtr)) } |] - return (realToFrac angle) - -setAngularVelocity :: Ptr Body -> Double -> IO () -setAngularVelocity bodyPtr (realToFrac -> angle) = [C.block| void { - cpBody *body = $(cpBody* bodyPtr); - cpBodySetAngularVelocity(body, $(double angle)); - if (cpBodyGetType(body) == CP_BODY_TYPE_STATIC) - cpSpaceReindexShapesForBody(cpBodyGetSpace(body), body); - } |] - -- FIXME reindex - -instance Component AngularVelocity where - type Storage AngularVelocity = Space AngularVelocity - -instance (MonadIO m, Has w m Physics) => Has w m AngularVelocity where - getStore = (cast :: Space Physics -> Space AngularVelocity) <$> getStore - -instance MonadIO m => ExplMembers m (Space AngularVelocity) where - explMembers s = explMembers (cast s :: Space Body) - -instance MonadIO m => ExplSet m (Space AngularVelocity) where - explSet (Space bMap _ _ _ _) ety (AngularVelocity angle) = liftIO $ do - rd <- M.lookup ety <$> readIORef bMap - forM_ rd $ \(BodyRecord b _ _ _) -> setAngularVelocity b angle - -instance MonadIO m => ExplGet m (Space AngularVelocity) where - explExists s ety = explExists (cast s :: Space Body) ety - explGet (Space bMap _ _ _ _) ety = liftIO $ do - Just (BodyRecord b _ _ _) <- M.lookup ety <$> readIORef bMap - AngularVelocity <$> getAngularVelocity b - --- Force -getForce :: Ptr Body -> IO (V2 Double) -getForce bodyPtr = do - x <- [C.exp| double { cpBodyGetForce ($(cpBody* bodyPtr)).x } |] - y <- [C.exp| double { cpBodyGetForce ($(cpBody* bodyPtr)).y } |] - return (V2 (realToFrac x) (realToFrac y)) - -setForce :: Ptr Body -> V2 Double -> IO () -setForce bodyPtr (V2 (realToFrac -> x) (realToFrac -> y)) = [C.block| void { - const cpVect frc = { $(double x), $(double y) }; - cpBodySetForce($(cpBody* bodyPtr), frc); - } |] - -instance Component Force where - type Storage Force = Space Force - -instance (MonadIO m, Has w m Physics) => Has w m Force where - getStore = (cast :: Space Physics -> Space Force) <$> getStore - -instance MonadIO m => ExplMembers m (Space Force) where - explMembers s = explMembers (cast s :: Space Body) - -instance MonadIO m => ExplSet m (Space Force) where - explSet (Space bMap _ _ _ _) ety (Force frc) = liftIO $ do - rd <- M.lookup ety <$> readIORef bMap - forM_ rd$ \(BodyRecord b _ _ _) -> setForce b frc - -instance MonadIO m => ExplGet m (Space Force) where - explExists s ety = explExists (cast s :: Space Body) ety - explGet (Space bMap _ _ _ _) ety = liftIO $ do - Just (BodyRecord b _ _ _) <- M.lookup ety <$> readIORef bMap - Force <$> getForce b - --- BodyMass -getBodyMass :: Ptr Body -> IO Double -getBodyMass bodyPtr = do - angle <- [C.exp| double { cpBodyGetMass ($(cpBody* bodyPtr)) } |] - return (realToFrac angle) - -setBodyMass :: Ptr Body -> Double -> IO () -setBodyMass bodyPtr (realToFrac -> angle) = [C.block| void { - cpBody *body = $(cpBody* bodyPtr); - cpBodySetMass(body, $(double angle)); - if (cpBodyGetType(body) == CP_BODY_TYPE_STATIC) - cpSpaceReindexShapesForBody(cpBodyGetSpace(body), body); - } |] - -- FIXME reindex - -instance Component BodyMass where - type Storage BodyMass = Space BodyMass - -instance (MonadIO m, Has w m Physics) => Has w m BodyMass where - getStore = (cast :: Space Physics -> Space BodyMass) <$> getStore - -instance MonadIO m => ExplMembers m (Space BodyMass) where - explMembers s = explMembers (cast s :: Space Body) - -instance MonadIO m => ExplSet m (Space BodyMass) where - explSet (Space bMap _ _ _ _) ety (BodyMass angle) = liftIO $ do - rd <- M.lookup ety <$> readIORef bMap - forM_ rd $ \(BodyRecord b _ _ _) -> setBodyMass b angle - -instance MonadIO m => ExplGet m (Space BodyMass) where - explExists s ety = explExists (cast s :: Space Body) ety - explGet (Space bMap _ _ _ _) ety = liftIO $ do - Just (BodyRecord b _ _ _) <- M.lookup ety <$> readIORef bMap - BodyMass <$> getBodyMass b - --- Moment -getMoment :: Ptr Body -> IO Double -getMoment bodyPtr = do - angle <- [C.exp| double { cpBodyGetMoment ($(cpBody* bodyPtr)) } |] - return (realToFrac angle) - -setMoment :: Ptr Body -> Double -> IO () -setMoment bodyPtr (realToFrac -> angle) = [C.block| void { - cpBody *body = $(cpBody* bodyPtr); - cpBodySetMoment(body, $(double angle)); - if (cpBodyGetType(body) == CP_BODY_TYPE_STATIC) - cpSpaceReindexShapesForBody(cpBodyGetSpace(body), body); - } |] - -- FIXME reindex - -instance Component Moment where - type Storage Moment = Space Moment - -instance (MonadIO m, Has w m Physics) => Has w m Moment where - getStore = (cast :: Space Physics -> Space Moment) <$> getStore - -instance MonadIO m => ExplMembers m (Space Moment) where - explMembers s = explMembers (cast s :: Space Body) - -instance MonadIO m => ExplSet m (Space Moment) where - explSet (Space bMap _ _ _ _) ety (Moment angle) = liftIO $ do - rd <- M.lookup ety <$> readIORef bMap - forM_ rd $ \(BodyRecord b _ _ _) -> setMoment b angle - -instance MonadIO m => ExplGet m (Space Moment) where - explExists s ety = explExists (cast s :: Space Body) ety - explGet (Space bMap _ _ _ _) ety = liftIO $ do - Just (BodyRecord b _ _ _) <- M.lookup ety <$> readIORef bMap - Moment <$> getMoment b - --- Torque -getTorque :: Ptr Body -> IO Double -getTorque bodyPtr = do - angle <- [C.exp| double { cpBodyGetTorque ($(cpBody* bodyPtr)) } |] - return (realToFrac angle) - -setTorque :: Ptr Body -> Double -> IO () -setTorque bodyPtr (realToFrac -> angle) = [C.block| void { - cpBody *body = $(cpBody* bodyPtr); - cpBodySetTorque(body, $(double angle)); - if (cpBodyGetType(body) == CP_BODY_TYPE_STATIC) - cpSpaceReindexShapesForBody(cpBodyGetSpace(body), body); - } |] - -- FIXME reindex - -instance Component Torque where - type Storage Torque = Space Torque - -instance (MonadIO m, Has w m Physics) => Has w m Torque where - getStore = (cast :: Space Physics -> Space Torque) <$> getStore - -instance MonadIO m => ExplMembers m (Space Torque) where - explMembers s = explMembers (cast s :: Space Body) - -instance MonadIO m => ExplSet m (Space Torque) where - explSet (Space bMap _ _ _ _) ety (Torque angle) = liftIO $ do - rd <- M.lookup ety <$> readIORef bMap - forM_ rd $ \(BodyRecord b _ _ _) -> setTorque b angle - -instance MonadIO m => ExplGet m (Space Torque) where - explExists s ety = explExists (cast s :: Space Body) ety - explGet (Space bMap _ _ _ _) ety = liftIO $ do - Just (BodyRecord b _ _ _) <- M.lookup ety <$> readIORef bMap - Torque <$> getTorque b - --- CenterOfGravity -getCenterOfGravity :: Ptr Body -> IO (V2 Double) -getCenterOfGravity bodyPtr = do - x <- [C.exp| double { cpBodyGetCenterOfGravity ($(cpBody* bodyPtr)).x } |] - y <- [C.exp| double { cpBodyGetCenterOfGravity ($(cpBody* bodyPtr)).y } |] - return (V2 (realToFrac x) (realToFrac y)) - -setCenterOfGravity :: Ptr Body -> V2 Double -> IO () -setCenterOfGravity bodyPtr (V2 (realToFrac -> x) (realToFrac -> y)) = [C.block| void { - const cpVect vel = { $(double x), $(double y) }; - cpBodySetCenterOfGravity($(cpBody* bodyPtr), vel); - } |] - -instance Component CenterOfGravity where - type Storage CenterOfGravity = Space CenterOfGravity - -instance (MonadIO m, Has w m Physics) => Has w m CenterOfGravity where - getStore = (cast :: Space Physics -> Space CenterOfGravity) <$> getStore - -instance MonadIO m => ExplMembers m (Space CenterOfGravity) where - explMembers s = explMembers (cast s :: Space Body) - -instance MonadIO m => ExplSet m (Space CenterOfGravity) where - explSet (Space bMap _ _ _ _) ety (CenterOfGravity vel) = liftIO $ do - rd <- M.lookup ety <$> readIORef bMap - forM_ rd$ \(BodyRecord b _ _ _) -> setCenterOfGravity b vel - -instance MonadIO m => ExplGet m (Space CenterOfGravity) where - explExists s ety = explExists (cast s :: Space Body) ety - explGet (Space bMap _ _ _ _) ety = liftIO $ do - Just (BodyRecord b _ _ _) <- M.lookup ety <$> readIORef bMap - CenterOfGravity <$> getCenterOfGravity b - --- ShapeList -instance Component ShapeList where - type Storage ShapeList = Space ShapeList - -instance (MonadIO m, Has w m Physics) => Has w m ShapeList where - getStore = (cast :: Space Physics -> Space ShapeList) <$> getStore - -instance MonadIO m => ExplMembers m (Space ShapeList) where - explMembers s = explMembers (cast s :: Space Body) - -instance MonadIO m => ExplGet m (Space ShapeList) where - explExists s ety = explExists (cast s :: Space Body) ety - explGet (Space bMap _ _ _ _) ety = liftIO $ do - Just (BodyRecord _ _ sPtr _) <- M.lookup ety <$> readIORef bMap - ShapeList . fmap Entity . S.toList <$> readIORef sPtr - --- ConstraintList -instance Component ConstraintList where - type Storage ConstraintList = Space ConstraintList - -instance (MonadIO m, Has w m Physics) => Has w m ConstraintList where - getStore = (cast :: Space Physics -> Space ConstraintList) <$> getStore - -instance MonadIO m => ExplMembers m (Space ConstraintList) where - explMembers s = explMembers (cast s :: Space Body) - -instance MonadIO m => ExplGet m (Space ConstraintList) where - explExists s ety = explExists (cast s :: Space Body) ety - explGet (Space bMap _ _ _ _) ety = liftIO $ do - Just (BodyRecord _ _ _ cPtr) <- M.lookup ety <$> readIORef bMap - ConstraintList . fmap Entity . S.toList <$> readIORef cPtr diff --git a/apecs-physics/src/Apecs/Physics/Collision.hs b/apecs-physics/src/Apecs/Physics/Collision.hs deleted file mode 100644 index 71fd0bb..0000000 --- a/apecs-physics/src/Apecs/Physics/Collision.hs +++ /dev/null @@ -1,160 +0,0 @@ -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE GeneralizedNewtypeDeriving #-} -{-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE QuasiQuotes #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE Strict #-} -{-# LANGUAGE TemplateHaskell #-} -{-# LANGUAGE TypeApplications #-} -{-# LANGUAGE TypeFamilies #-} -{-# LANGUAGE UndecidableInstances #-} -{-# LANGUAGE ViewPatterns #-} - -module Apecs.Physics.Collision - ( defaultHandler - , mkBeginCB, mkSeparateCB, mkPreSolveCB, mkPostSolveCB - , addPostStepCallback - ) where - -import Apecs -import Apecs.Core -import Control.Monad -import Control.Monad.IO.Class (MonadIO, liftIO) -import qualified Data.IntMap as M -import Data.IORef -import qualified Data.Vector.Unboxed as U -import Foreign.ForeignPtr (withForeignPtr) -import Foreign.Ptr -import qualified Language.C.Inline as C -import Linear.V2 - -import Apecs.Physics.Body () -import Apecs.Physics.Space () -import Apecs.Physics.Types - -C.context (phycsCtx `mappend` C.funCtx) -C.include "" -C.include "" - -defaultHandler :: CollisionHandler -defaultHandler = CollisionHandler (Wildcard 0) Nothing Nothing Nothing Nothing - -mkCollision :: Ptr Collision -> IO Collision -mkCollision arb = do - nx <- realToFrac <$> [C.exp| double { cpArbiterGetNormal($(cpArbiter* arb)).x } |] - ny <- realToFrac <$> [C.exp| double { cpArbiterGetNormal($(cpArbiter* arb)).y } |] - ba <- fromIntegral <$> [C.block| unsigned int { CP_ARBITER_GET_BODIES($(cpArbiter* arb), ba, bb); return (intptr_t) (ba->userData); } |] - bb <- fromIntegral <$> [C.block| unsigned int { CP_ARBITER_GET_BODIES($(cpArbiter* arb), ba, bb); return (intptr_t) (bb->userData); } |] - sa <- fromIntegral <$> [C.block| unsigned int { CP_ARBITER_GET_SHAPES($(cpArbiter* arb), sa, sb); return (intptr_t) (sa->userData); } |] - sb <- fromIntegral <$> [C.block| unsigned int { CP_ARBITER_GET_SHAPES($(cpArbiter* arb), sa, sb); return (intptr_t) (sb->userData); } |] - return $ Collision (V2 nx ny) (Entity ba) (Entity bb) (Entity sa) (Entity sb) - -mkBeginCB :: MonadIO m => (Collision -> SystemT w IO Bool) -> SystemT w m BeginCB -mkBeginCB sys = do - w <- ask - - let cb arb _ _ = do - col <- liftIO $ mkCollision arb - r <- runSystem (sys col) w - return . fromIntegral . fromEnum $ r - - return (BeginCB cb) - -mkSeparateCB :: MonadIO m => (Collision -> SystemT w IO ()) -> SystemT w m SeparateCB -mkSeparateCB sys = do - w <- ask - - let cb arb _ _ = do - col <- liftIO $ mkCollision arb - runSystem (sys col) w - - return (SeparateCB cb) - - -mkPreSolveCB :: MonadIO m => (Collision -> SystemT w IO Bool) -> SystemT w m PreSolveCB -mkPreSolveCB sys = (\(BeginCB cb) -> PreSolveCB cb) <$> mkBeginCB sys - -mkPostSolveCB :: MonadIO m => (Collision -> SystemT w IO ()) -> SystemT w m PostSolveCB -mkPostSolveCB sys = (\(SeparateCB cb) -> PostSolveCB cb) <$> mkSeparateCB sys - -newCollisionHandler :: SpacePtr -> CollisionHandler -> Int -> IO (Ptr CollisionHandler) -newCollisionHandler spcPtr (CollisionHandler source begin separate presolve postsolve) (fromIntegral -> ety) = - withForeignPtr spcPtr $ \space -> do - handler <- case source of - Between (CollisionType cta) (CollisionType ctb) - -> [C.exp| cpCollisionHandler* {cpSpaceAddCollisionHandler($(cpSpace* space), $(uintptr_t cta), $(uintptr_t ctb))}|] - Wildcard (CollisionType ct) - -> [C.exp| cpCollisionHandler* {cpSpaceAddWildcardHandler($(cpSpace* space), $(uintptr_t ct))}|] - - - [C.exp| void { $(cpCollisionHandler* handler)->userData = (void*) $(intptr_t ety) }|] - - forM_ begin$ \(BeginCB cb) -> do - funPtr <- liftIO$ $(C.mkFunPtr [t| BeginFunc |]) cb - let fn = castFunPtrToPtr funPtr - [C.exp| void { $(cpCollisionHandler* handler)->beginFunc = $(void* fn) }|] - - forM_ separate$ \(SeparateCB cb) -> do - funPtr <- liftIO$ $(C.mkFunPtr [t| SeparateFunc |]) cb - let fn = castFunPtrToPtr funPtr - [C.exp| void { $(cpCollisionHandler* handler)->separateFunc = $(void* fn) }|] - - forM_ presolve$ \(PreSolveCB cb) -> do - funPtr <- liftIO$ $(C.mkFunPtr [t| PreSolveFunc |]) cb - let fn = castFunPtrToPtr funPtr - [C.exp| void { $(cpCollisionHandler* handler)->preSolveFunc = $(void* fn) }|] - - forM_ postsolve$ \(PostSolveCB cb) -> do - funPtr <- liftIO$ $(C.mkFunPtr [t| PostSolveFunc |]) cb - let fn = castFunPtrToPtr funPtr - [C.exp| void { $(cpCollisionHandler* handler)->postSolveFunc = $(void* fn) }|] - - return handler - -destroyCollisionHandler :: Ptr CollisionHandler -> IO () -destroyCollisionHandler = error "Destroy CollisionHandler not yet implemented" - -instance Component CollisionHandler where - type Storage CollisionHandler = Space CollisionHandler - -instance (MonadIO m, Has w m Physics) => Has w m CollisionHandler where - getStore = (cast :: Space Physics -> Space CollisionHandler) <$> getStore - -instance (MonadIO m) => ExplSet m (Space CollisionHandler) where - explSet sp@(Space _ _ _ hMap spcPtr) ety handler = liftIO $ do - explDestroy sp ety - hPtr <- newCollisionHandler spcPtr handler ety - modifyIORef' hMap (M.insert ety (Record hPtr handler)) - -instance (MonadIO m) => ExplDestroy m (Space CollisionHandler) where - explDestroy (Space _ _ _ hMap _) ety = liftIO $ do - rd <- M.lookup ety <$> readIORef hMap - forM_ rd$ \(Record c _) -> destroyCollisionHandler c >> modifyIORef' hMap (M.delete ety) - -instance (MonadIO m) => ExplMembers m (Space CollisionHandler) where - explMembers (Space _ _ _ hMap _) = liftIO $ U.fromList . M.keys <$> readIORef hMap - -instance (MonadIO m) => ExplGet m (Space CollisionHandler) where - explExists (Space _ _ _ hMap _) ety = liftIO $ M.member ety <$> readIORef hMap - explGet (Space _ _ _ hMap _) ety = liftIO $ do - Just (Record _ handler) <- M.lookup ety <$> readIORef hMap - return handler - --- | Add an action that will be executed after the physics engine is done processing the current step. Since you generally cannot modify the physics space while the engine is handling collisions, 'addPostStepCallback' is the primary way of making changes to the physics space with a 'CollisionHandler' in a safe manner. --- Please note that you should only use this function for callbacks in conjunction with a 'CollisionHandler'! -addPostStepCallback :: (Has w m Physics, MonadIO m) => Int -> SystemT w IO () -> SystemT w m () -addPostStepCallback (toEnum -> k) systemCallback= do - w <- ask - let callback _ _ _ = runSystem systemCallback w - (Space _ _ _ _ spcPtr) :: Space Physics <- getStore - liftIO $ withForeignPtr spcPtr $ \space -> do - funPtr <- liftIO$ $(C.mkFunPtr [t| Ptr FrnSpace -> Ptr () -> Ptr () -> IO () |]) callback - let fn = castFunPtrToPtr funPtr - [C.block| void { - int *data = 0; - cpSpaceAddPostStepCallback($(cpSpace *space), $(void*fn),$(int k), &data); - } |] - pure () diff --git a/apecs-physics/src/Apecs/Physics/Constraint.hs b/apecs-physics/src/Apecs/Physics/Constraint.hs deleted file mode 100644 index 2b6ee10..0000000 --- a/apecs-physics/src/Apecs/Physics/Constraint.hs +++ /dev/null @@ -1,327 +0,0 @@ -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE GeneralizedNewtypeDeriving #-} -{-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE QuasiQuotes #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE Strict #-} -{-# LANGUAGE TemplateHaskell #-} -{-# LANGUAGE TypeApplications #-} -{-# LANGUAGE TypeFamilies #-} -{-# LANGUAGE UndecidableInstances #-} -{-# LANGUAGE ViewPatterns #-} - -module Apecs.Physics.Constraint - ( - ) where - -import Apecs -import Apecs.Core -import Control.Monad -import Control.Monad.IO.Class (liftIO, MonadIO) -import qualified Data.IntMap as M -import qualified Data.IntSet as S -import Data.IORef -import qualified Data.Vector.Unboxed as U -import Foreign.ForeignPtr (withForeignPtr) -import Foreign.Ptr -import qualified Language.C.Inline as C -import Linear.V2 - -import Apecs.Physics.Space () -import Apecs.Physics.Types - -C.context phycsCtx -C.include "" - --- Constraint -newConstraint :: SpacePtr -> Ptr Body -> Ptr Body -> Int -> ConstraintType -> IO (Ptr Constraint) -newConstraint spacePtr bodyA bodyB (fromIntegral -> ety) - (PinJoint (fmap realToFrac -> V2 ax ay) (fmap realToFrac -> V2 bx by)) = - withForeignPtr spacePtr $ \space -> [C.block| cpConstraint* { - cpVect anchorA = cpv( $(double ax), $(double ay) ); - cpVect anchorB = cpv( $(double bx), $(double by) ); - cpConstraint* constraint = cpPinJointNew($(cpBody* bodyA), $(cpBody* bodyB),anchorA,anchorB); - cpConstraintSetUserData(constraint, (void*) $(intptr_t ety)); - return cpSpaceAddConstraint($(cpSpace* space), constraint); - } |] - -newConstraint spacePtr bodyA bodyB (fromIntegral -> ety) - (SlideJoint (fmap realToFrac -> V2 ax ay) (fmap realToFrac -> V2 bx by) (realToFrac -> min) (realToFrac -> max)) = - withForeignPtr spacePtr $ \space -> [C.block| cpConstraint* { - cpVect anchorA = cpv( $(double ax), $(double ay) ); - cpVect anchorB = cpv( $(double bx), $(double by) ); - cpConstraint* constraint = cpSlideJointNew($(cpBody* bodyA), $(cpBody* bodyB),anchorA,anchorB,$(double min),$(double max)); - cpConstraintSetUserData(constraint, (void*) $(intptr_t ety)); - return cpSpaceAddConstraint($(cpSpace* space), constraint); - } |] - -newConstraint spacePtr bodyA bodyB (fromIntegral -> ety) - (PivotJoint (fmap realToFrac -> V2 x y)) = - withForeignPtr spacePtr $ \space -> [C.block| cpConstraint* { - cpVect anchor = cpv( $(double x), $(double y) ); - cpConstraint* constraint = cpPivotJointNew($(cpBody* bodyA), $(cpBody* bodyB), anchor); - cpConstraintSetUserData(constraint, (void*) $(intptr_t ety)); - return cpSpaceAddConstraint($(cpSpace* space), constraint); - } |] - -newConstraint spacePtr bodyA bodyB (fromIntegral -> ety) - (PivotJoint2 (fmap realToFrac -> V2 ax ay) (fmap realToFrac -> V2 bx by)) = - withForeignPtr spacePtr $ \space -> [C.block| cpConstraint* { - cpVect va = cpv( $(double ax), $(double ay) ); - cpVect vb = cpv( $(double bx), $(double by) ); - cpConstraint* constraint = cpPivotJointNew2($(cpBody* bodyA), $(cpBody* bodyB), va, vb); - cpConstraintSetUserData(constraint, (void*) $(intptr_t ety)); - return cpSpaceAddConstraint($(cpSpace* space), constraint); - } |] - -newConstraint spacePtr bodyA bodyB (fromIntegral -> ety) - (GrooveJoint (fmap realToFrac -> V2 ax ay) (fmap realToFrac -> V2 bx by) (fmap realToFrac -> V2 ancx ancy)) = - withForeignPtr spacePtr $ \space -> [C.block| cpConstraint* { - cpVect va = cpv( $(double ax), $(double ay) ); - cpVect vb = cpv( $(double bx), $(double by) ); - cpVect anchor = cpv( $(double ancx), $(double ancy) ); - cpConstraint* constraint = cpGrooveJointNew($(cpBody* bodyA), $(cpBody* bodyB), va, vb, anchor); - cpConstraintSetUserData(constraint, (void*) $(intptr_t ety)); - return cpSpaceAddConstraint($(cpSpace* space), constraint); - } |] - -newConstraint spacePtr bodyA bodyB (fromIntegral -> ety) - (DampedSpring (fmap realToFrac -> V2 ax ay) (fmap realToFrac -> V2 bx by) (realToFrac -> rl) (realToFrac -> stf) (realToFrac -> damping)) = - withForeignPtr spacePtr $ \space -> [C.block| cpConstraint* { - cpVect va = cpv( $(double ax), $(double ay) ); - cpVect vb = cpv( $(double bx), $(double by) ); - cpConstraint* constraint = cpDampedSpringNew($(cpBody* bodyA), $(cpBody* bodyB), va, vb, $(double rl), $(double stf), $(double damping)); - cpConstraintSetUserData(constraint, (void*) $(intptr_t ety)); - return cpSpaceAddConstraint($(cpSpace* space), constraint); - } |] - -newConstraint spacePtr bodyA bodyB (fromIntegral -> ety) - (DampedRotarySpring (realToFrac -> ra) (realToFrac -> stf) (realToFrac -> damping)) = - withForeignPtr spacePtr $ \space -> [C.block| cpConstraint* { - cpConstraint* constraint = cpDampedRotarySpringNew($(cpBody* bodyA), $(cpBody* bodyB), $(double ra), $(double stf), $(double damping)); - cpConstraintSetUserData(constraint, (void*) $(intptr_t ety)); - return cpSpaceAddConstraint($(cpSpace* space), constraint); - } |] - -newConstraint spacePtr bodyA bodyB (fromIntegral -> ety) - (RotaryLimitJoint (realToFrac -> min) (realToFrac -> max)) = - withForeignPtr spacePtr $ \space -> [C.block| cpConstraint* { - cpConstraint* constraint = cpRotaryLimitJointNew($(cpBody* bodyA), $(cpBody* bodyB), $(double min), $(double max)); - cpConstraintSetUserData(constraint, (void*) $(intptr_t ety)); - return cpSpaceAddConstraint($(cpSpace* space), constraint); - } |] - -newConstraint spacePtr bodyA bodyB (fromIntegral -> ety) - (RatchetJoint (realToFrac -> phase) (realToFrac -> ratchet)) = - withForeignPtr spacePtr $ \space -> [C.block| cpConstraint* { - cpConstraint* constraint = cpRatchetJointNew($(cpBody* bodyA), $(cpBody* bodyB), $(double phase), $(double ratchet)); - cpConstraintSetUserData(constraint, (void*) $(intptr_t ety)); - return cpSpaceAddConstraint($(cpSpace* space), constraint); - } |] - -newConstraint spacePtr bodyA bodyB (fromIntegral -> ety) - (GearJoint (realToFrac -> phase) (realToFrac -> ratio)) = - withForeignPtr spacePtr $ \space -> [C.block| cpConstraint* { - cpConstraint* constraint = cpGearJointNew($(cpBody* bodyA), $(cpBody* bodyB), $(double phase), $(double ratio)); - cpConstraintSetUserData(constraint, (void*) $(intptr_t ety)); - return cpSpaceAddConstraint($(cpSpace* space), constraint); - } |] - -newConstraint spacePtr bodyA bodyB (fromIntegral -> ety) - (SimpleMotor (realToFrac -> rate)) = - withForeignPtr spacePtr $ \space -> [C.block| cpConstraint* { - cpConstraint* constraint = cpSimpleMotorNew($(cpBody* bodyA), $(cpBody* bodyB), $(double rate)); - cpConstraintSetUserData(constraint, (void*) $(intptr_t ety)); - return cpSpaceAddConstraint($(cpSpace* space), constraint); - } |] - -destroyConstraint :: SpacePtr -> Ptr Constraint -> IO () -destroyConstraint spacePtr constraintPtr = withForeignPtr spacePtr $ \space -> [C.block| void { - cpConstraint *constraint = $(cpConstraint* constraintPtr); - cpSpaceRemoveConstraint($(cpSpace* space), constraint); - cpConstraintFree(constraint); }|] - -instance Component Constraint where - type Storage Constraint = Space Constraint - -instance (MonadIO m, Has w m Physics) => Has w m Constraint where - getStore = (cast :: Space Physics -> Space Constraint) <$> getStore - -instance MonadIO m => ExplSet m (Space Constraint) where - explSet sp@(Space bMap _ cMap _ spcPtr) cEty cons@(Constraint (Entity bEtyA) (Entity bEtyB) ctype) = liftIO $ do - explDestroy sp cEty - mBrA <- M.lookup bEtyA <$> readIORef bMap - mBrB <- M.lookup bEtyB <$> readIORef bMap - case (mBrA,mBrB) of - (Just brA, Just brB) -> do - cPtr <- newConstraint spcPtr (brPtr brA) (brPtr brB) cEty ctype - - modifyIORef' cMap (M.insert cEty (Record cPtr cons)) - modifyIORef' (brConstraints brA) (S.insert cEty) - modifyIORef' (brConstraints brB) (S.insert cEty) - - _ -> return () - -instance MonadIO m => ExplDestroy m (Space Constraint) where - explDestroy (Space bMap _ cMap _ spc) cEty = liftIO $ do - rd <- M.lookup cEty <$> readIORef cMap - forM_ rd $ \(Record cPtr (Constraint (Entity bEtyA) (Entity bEtyB) _)) -> do - bMap' <- readIORef bMap - let rmConstraint ref = modifyIORef' (brConstraints ref) (S.delete cEty) - mapM_ rmConstraint (M.lookup bEtyA bMap') - mapM_ rmConstraint (M.lookup bEtyB bMap') - modifyIORef' cMap $ M.delete cEty - destroyConstraint spc cPtr - -instance MonadIO m => ExplMembers m (Space Constraint) where - explMembers (Space _ _ cMap _ _) = liftIO $ U.fromList . M.keys <$> readIORef cMap - -instance MonadIO m => ExplGet m (Space Constraint) where - explExists (Space _ _ cMap _ _) ety = liftIO $ M.member ety <$> readIORef cMap - explGet (Space _ _ cMap _ _) ety = liftIO $ do - Just (Record _ cons) <- M.lookup ety <$> readIORef cMap - return cons - --- MaxForce -getMaxForce :: Ptr Constraint -> IO Double -getMaxForce c = realToFrac <$> [C.exp| double { cpConstraintGetMaxForce ($(cpConstraint* c)) } |] - -setMaxForce :: Ptr Constraint -> Double -> IO () -setMaxForce c (realToFrac -> maxForce) = [C.exp| void { cpConstraintSetMaxForce($(cpConstraint* c), $(double maxForce)); } |] - -instance Component MaxForce where - type Storage MaxForce = Space MaxForce - -instance (MonadIO m, Has w m Physics) => Has w m MaxForce where - getStore = (cast :: Space Physics -> Space MaxForce) <$> getStore - -instance MonadIO m => ExplMembers m (Space MaxForce) where - explMembers s = explMembers (cast s :: Space Constraint) - -instance MonadIO m => ExplSet m (Space MaxForce) where - explSet (Space _ _ cMap _ _) ety (MaxForce vec) = liftIO $ do - rd <- M.lookup ety <$> readIORef cMap - case rd of - Nothing -> return () - Just (Record c _) -> setMaxForce c vec - -instance MonadIO m => ExplGet m (Space MaxForce) where - explExists s ety = explExists (cast s :: Space Constraint) ety - explGet (Space _ _ cMap _ _) ety = liftIO $ do - Just (Record c _) <- M.lookup ety <$> readIORef cMap - MaxForce <$> getMaxForce c - --- MaxBias -getMaxBias :: Ptr Constraint -> IO Double -getMaxBias c = do - maxBias <- [C.exp| double { cpConstraintGetMaxBias ($(cpConstraint* c)) } |] - return (realToFrac maxBias) - -setMaxBias :: Ptr Constraint -> Double -> IO () -setMaxBias c (realToFrac -> maxBias) = [C.exp| void { cpConstraintSetMaxBias($(cpConstraint* c), $(double maxBias)); } |] - -instance Component MaxBias where - type Storage MaxBias = Space MaxBias - -instance (MonadIO m, Has w m Physics) => Has w m MaxBias where - getStore = (cast :: Space Physics -> Space MaxBias) <$> getStore - -instance MonadIO m => ExplMembers m (Space MaxBias) where - explMembers s = explMembers (cast s :: Space Constraint) - -instance MonadIO m => ExplSet m (Space MaxBias) where - explSet (Space _ _ cMap _ _) ety (MaxBias vec) = liftIO $ do - rd <- M.lookup ety <$> readIORef cMap - case rd of - Nothing -> return () - Just (Record c _) -> setMaxBias c vec - -instance MonadIO m => ExplGet m (Space MaxBias) where - explGet (Space _ _ cMap _ _) ety = liftIO $ do - Just (Record c _) <- M.lookup ety <$> readIORef cMap - MaxBias <$> getMaxBias c - explExists s ety = explExists (cast s :: Space Constraint) ety - --- ErrorBias -getErrorBias :: Ptr Constraint -> IO Double -getErrorBias c = liftIO $ do - errorBias <- [C.exp| double { cpConstraintGetErrorBias ($(cpConstraint* c)) } |] - return (realToFrac errorBias) - -setErrorBias :: Ptr Constraint -> Double -> IO () -setErrorBias c (realToFrac -> errorBias) = [C.exp| void { cpConstraintSetErrorBias($(cpConstraint* c), $(double errorBias)); } |] - -instance Component ErrorBias where - type Storage ErrorBias = Space ErrorBias - -instance (MonadIO m, Has w m Physics) => Has w m ErrorBias where - getStore = (cast :: Space Physics -> Space ErrorBias) <$> getStore - -instance MonadIO m => ExplMembers m (Space ErrorBias) where - explMembers s = explMembers (cast s :: Space Constraint) - -instance MonadIO m => ExplSet m (Space ErrorBias) where - explSet (Space _ _ cMap _ _) ety (ErrorBias vec) = liftIO $ do - rd <- M.lookup ety <$> readIORef cMap - case rd of - Nothing -> return () - Just (Record c _) -> setErrorBias c vec - -instance MonadIO m => ExplGet m (Space ErrorBias) where - explExists s ety = explExists (cast s :: Space Constraint) ety - explGet (Space _ _ cMap _ _) ety = liftIO $ do - Just (Record c _) <- M.lookup ety <$> readIORef cMap - ErrorBias <$> getErrorBias c - --- Impulse -getImpulse :: Ptr Constraint -> IO Double -getImpulse c = realToFrac <$> [C.exp| double { cpConstraintGetImpulse ($(cpConstraint* c)) } |] - -instance Component Impulse where - type Storage Impulse = Space Impulse - -instance (MonadIO m, Has w m Physics) => Has w m Impulse where - getStore = (cast :: Space Physics -> Space Impulse) <$> getStore - -instance MonadIO m => ExplMembers m (Space Impulse) where - explMembers s = explMembers (cast s :: Space Constraint) - -instance MonadIO m => ExplGet m (Space Impulse) where - explExists s ety = explExists (cast s :: Space Constraint) ety - explGet (Space _ _ cMap _ _) ety = liftIO $ do - Just (Record c _) <- M.lookup ety <$> readIORef cMap - Impulse <$> getImpulse c - --- CollideBodies -getCollideBodies :: Ptr Constraint -> IO Bool -getCollideBodies c = do - collide <- [C.exp| int { cpConstraintGetCollideBodies ($(cpConstraint* c)) } |] - return . toEnum . fromIntegral $ collide - -setCollideBodies :: Ptr Constraint -> Bool -> IO () -setCollideBodies c (fromIntegral . fromEnum -> collide) = [C.exp| void { cpConstraintSetCollideBodies($(cpConstraint* c), $(int collide)); } |] - -instance Component CollideBodies where - type Storage CollideBodies = Space CollideBodies - -instance (MonadIO m, Has w m Physics) => Has w m CollideBodies where - getStore = (cast :: Space Physics -> Space CollideBodies) <$> getStore - -instance MonadIO m => ExplMembers m (Space CollideBodies) where - explMembers s = explMembers (cast s :: Space Constraint) - -instance MonadIO m => ExplSet m (Space CollideBodies) where - explSet (Space _ _ cMap _ _) ety (CollideBodies vec) = liftIO $ do - rd <- M.lookup ety <$> readIORef cMap - case rd of - Nothing -> return () - Just (Record c _) -> setCollideBodies c vec - -instance MonadIO m => ExplGet m (Space CollideBodies) where - explExists s ety = explExists (cast s :: Space Constraint) ety - explGet (Space _ _ cMap _ _) ety = liftIO $ do - Just (Record c _) <- M.lookup ety <$> readIORef cMap - CollideBodies <$> getCollideBodies c diff --git a/apecs-physics/src/Apecs/Physics/Geometry.hs b/apecs-physics/src/Apecs/Physics/Geometry.hs deleted file mode 100644 index 188cdca..0000000 --- a/apecs-physics/src/Apecs/Physics/Geometry.hs +++ /dev/null @@ -1,64 +0,0 @@ -module Apecs.Physics.Geometry where - -import Apecs.Physics.Types -import Linear - -vertices :: Convex -> [BVec] -vertices (Convex s _) = s - --- | Map a function over all vertices -mapVertices :: (BVec -> BVec) -> Convex -> Convex -mapVertices f (Convex s r) = Convex (f <$> s) r - --- | Translates all vertices. The name shift is to prevent collisions with gloss -shift :: BVec -> Convex -> Convex -shift = mapVertices . (+) - -getRadius :: Convex -> Double -getRadius (Convex _ r) = r - -setRadius :: Double -> Convex -> Convex -setRadius r (Convex s _) = Convex s r - -cCircle, zCircle :: Double -> Convex -cCircle r = oCircle 0 r -zCircle = cCircle - -oCircle :: BVec -> Double -> Convex -oCircle o r = Convex [o] r - -hLine, vLine :: Double -> Convex -hLine l = Convex [V2 (-l/2) 0, V2 (l/2) 0] 0 -vLine l = Convex [V2 0 (l/2), V2 0 (-l/2)] 0 - --- | Centered rectangle with a given size -cRectangle :: BVec -> Convex -cRectangle s = oRectangle (-s*0.5) s - --- | Rectangle with a given origin and size -oRectangle :: BVec -> BVec -> Convex -oRectangle (V2 x y) (V2 w h) = Convex [V2 x y, V2 x (y+h), V2 (x+w) (y+h), V2 (x+w) y] 0 - --- | Rectangle with origin 0 and given size -zRectangle :: BVec -> Convex -zRectangle s = oRectangle 0 s - --- | Split a shape into its edges. Will return no edges for points, but returns 2 for a line (in opposite directions) -toEdges :: Convex -> [Convex] -toEdges (Convex [] _) = [] -toEdges (Convex [_] _) = [] -toEdges (Convex vs r) = zipWith (\h t -> Convex [h,t] r) vs (tail . cycle $ vs) - --- | A set of lines forming a grid. Returns (r + c + 2) segments. -gridLines :: Vec -> Int -> Int -> [Convex] -gridLines size c r = - [ shift (V2 x 0) (vLine h) | x <- xs ] ++ - [ shift (V2 0 y) (hLine w) | y <- ys ] - where - V2 w h = size - V2 x y = -size*0.5 - dx = w/fromIntegral c - dy = h/fromIntegral r - xs = [x + fromIntegral n * dx | n <- [0..c]] - ys = [y + fromIntegral n * dy | n <- [0..r]] - diff --git a/apecs-physics/src/Apecs/Physics/Query.hs b/apecs-physics/src/Apecs/Physics/Query.hs deleted file mode 100644 index 5db86e1..0000000 --- a/apecs-physics/src/Apecs/Physics/Query.hs +++ /dev/null @@ -1,71 +0,0 @@ -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE GeneralizedNewtypeDeriving #-} -{-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE QuasiQuotes #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE Strict #-} -{-# LANGUAGE TemplateHaskell #-} -{-# LANGUAGE TypeApplications #-} -{-# LANGUAGE TypeFamilies #-} -{-# LANGUAGE UndecidableInstances #-} -{-# LANGUAGE ViewPatterns #-} - -module Apecs.Physics.Query where - -import Apecs -import Control.Monad.IO.Class (liftIO, MonadIO) -import Foreign.C.Types -import Foreign.ForeignPtr (withForeignPtr) -import Foreign.Marshal.Alloc -import Foreign.Ptr -import Foreign.Storable -import qualified Language.C.Inline as C -import Linear.V2 - -import Apecs.Physics.Space () -import Apecs.Physics.Types - -C.context phycsCtx -C.include "" - - --- cpFloat cpShapeNearestPointQuery(cpShape *shape, cpVect p, cpPointQueryInfo *out) --- cpShape *cpSpacePointQueryNearest(cpSpace *space, cpVect point, cpFloat maxDistance, cpShapeFilter filter, cpPointQueryInfo *out) - -pointQuery :: (MonadIO m, Has w m Physics) => WVec -> Double -> CollisionFilter -> SystemT w m (Maybe PointQueryResult) -pointQuery (fmap realToFrac -> V2 px py) (realToFrac -> maxDistance) (CollisionFilter gr (Bitmask cs) (Bitmask mk)) = do - Space _ _ _ _ spcPtr :: Space Physics <- getStore - liftIO $ alloca $ \pq -> do - withForeignPtr spcPtr $ \space -> [C.block| void { - cpSpacePointQueryNearest - ( $(cpSpace *space) - , cpv($(double px), $(double py)) - , $(double maxDistance) - , cpShapeFilterNew($(unsigned int gr), $(unsigned int cs), $(unsigned int mk)) - , $(cpPointQueryInfo *pq)); - }|] - res <- peek pq - if unEntity (pqShape res) == -1 - then return Nothing - else return (Just res) - -instance Storable PointQueryResult where - sizeOf ~_ = 48 -- sizeOf (undefined :: Ptr Shape) + sizeOf (undefined :: CDouble) + 2*sizeOf (undefined :: V2 CDouble) - alignment ~_ = 8 - peek ptr = do - sPtr :: Ptr Shape <- peekByteOff ptr 0 - s <- [C.block| intptr_t { - cpShape *shape = $(cpShape *sPtr); - if (shape==NULL) { - return -1; - } else { - return (intptr_t) cpShapeGetUserData(shape); - } }|] - p :: V2 CDouble <- peekByteOff ptr 8 - d :: CDouble <- peekByteOff ptr 24 - g :: V2 CDouble <- peekByteOff ptr 32 - return $ PointQueryResult (Entity . fromIntegral $ s) (realToFrac <$> p) (realToFrac d) (realToFrac <$> g) - poke = undefined diff --git a/apecs-physics/src/Apecs/Physics/Shape.hs b/apecs-physics/src/Apecs/Physics/Shape.hs deleted file mode 100644 index 24e7d9e..0000000 --- a/apecs-physics/src/Apecs/Physics/Shape.hs +++ /dev/null @@ -1,360 +0,0 @@ -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE QuasiQuotes #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE Strict #-} -{-# LANGUAGE TemplateHaskell #-} -{-# LANGUAGE TypeFamilies #-} -{-# LANGUAGE UndecidableInstances #-} -{-# LANGUAGE ViewPatterns #-} - -module Apecs.Physics.Shape where - -import Apecs.Core -import Control.Monad -import Control.Monad.IO.Class (liftIO, MonadIO) -import Data.Bits -import qualified Data.IntMap as M -import qualified Data.IntSet as S -import Data.IORef -import Data.Monoid ((<>)) -import qualified Data.Vector.Storable as V -import qualified Data.Vector.Unboxed as U -import Foreign.ForeignPtr -import Foreign.Ptr -import qualified Language.C.Inline as C -import Linear.V2 - -import Apecs.Physics.Space () -import Apecs.Physics.Types - -C.context (phycsCtx <> C.vecCtx) -C.include "" - -maskAll, maskNone :: Bitmask -maskAll = complement zeroBits -maskNone = zeroBits --- | Makes a bitmask from a list of indices -maskList :: [Int] -> Bitmask -maskList = foldr (flip setBit) maskNone - -defaultFilter :: CollisionFilter -defaultFilter = CollisionFilter 0 maskAll maskAll - --- | A box with the given height, width, and center point -boxShape :: Double -> Double -> Vec -> Convex -boxShape w h offset = Convex ((+offset) <$> verts) 0 - where - w' = w/2 - h' = h/2 - verts = [ V2 (-w') (-h') - , V2 (-w') h' - , V2 w' h' - , V2 w' (-h') ] - -instance Component Shape where - type Storage Shape = Space Shape - -instance (MonadIO m, Has w m Physics) => Has w m Shape where - getStore = (cast :: Space Physics -> Space Shape) <$> getStore - -instance MonadIO m => ExplMembers m (Space Shape) where - explMembers (Space _ sMap _ _ _) = liftIO $ U.fromList . M.keys <$> readIORef sMap - -instance MonadIO m => ExplDestroy m (Space Shape) where - explDestroy (Space bMap sMap _ _ spc) sEty = liftIO $ do - rd <- M.lookup sEty <$> readIORef sMap - forM_ rd $ \(Record sPtr (Shape (Entity bEty) _)) -> do - rd <- M.lookup bEty <$> readIORef bMap - forM_ rd $ \bRec -> modifyIORef' (brShapes bRec) (S.delete sEty) - modifyIORef' sMap (M.delete sEty) - destroyShape spc sPtr - -instance MonadIO m => ExplSet m (Space Shape) where - explSet sp@(Space bMap sMap _ _ spcPtr) sEty shape@(Shape (Entity bEty) sh) = liftIO $ do - explDestroy sp sEty - rd <- M.lookup bEty <$> readIORef bMap - forM_ rd $ \bRec -> do - shPtr <- newShape spcPtr (brPtr bRec) sh sEty - modifyIORef' (brShapes bRec) (S.insert sEty) - modifyIORef' sMap (M.insert sEty (Record shPtr shape)) - -instance MonadIO m => ExplGet m (Space Shape) where - explGet (Space _ sMap _ _ _) ety = liftIO $ do - Just (Record _ s) <- M.lookup ety <$> readIORef sMap - return s - explExists (Space _ sMap _ _ _) ety = liftIO $ M.member ety <$> readIORef sMap - -newShape :: SpacePtr -> Ptr Body -> Convex -> Int -> IO (Ptr Shape) -newShape spacePtr' bodyPtr shape (fromIntegral -> ety) = withForeignPtr spacePtr' (go shape) - where - - go (Convex [fmap realToFrac -> V2 x y] (realToFrac -> radius)) spacePtr = [C.block| cpShape* { - const cpVect vec = { $(double x), $(double y) }; - cpShape* sh = cpCircleShapeNew($(cpBody* bodyPtr), $(double radius), vec); - cpShapeSetUserData(sh, (void*) $(intptr_t ety)); - return cpSpaceAddShape( $(cpSpace* spacePtr), sh); } |] - - go (Convex [ fmap realToFrac -> V2 xa ya - , fmap realToFrac -> V2 xb yb ] - (realToFrac -> radius) - ) spacePtr = [C.block| cpShape* { - const cpVect va = { $(double xa), $(double ya) }; - const cpVect vb = { $(double xb), $(double yb) }; - cpShape* sh = cpSegmentShapeNew($(cpBody* bodyPtr), va, vb, $(double radius)); - cpShapeSetUserData(sh, (void*) $(intptr_t ety)); - return cpSpaceAddShape( $(cpSpace* spacePtr), sh); } |] - - go (Convex ((fmap.fmap) realToFrac -> verts) - (realToFrac -> radius) - ) spacePtr = liftIO $ do - vec <- V.thaw (V.fromList verts) - [C.block| cpShape* { - cpTransform trans = cpTransformIdentity; - cpShape* sh = cpPolyShapeNew($(cpBody* bodyPtr), $vec-len:vec, $vec-ptr:(cpVect *vec), trans, $(double radius)); - cpShapeSetUserData(sh, (void*) $(intptr_t ety)); - return cpSpaceAddShape( $(cpSpace* spacePtr), sh); } |] - -destroyShape :: SpacePtr -> Ptr Shape -> IO () -destroyShape spacePtr shapePtr = withForeignPtr spacePtr $ \space -> [C.block| void { - cpShape *shape = $(cpShape* shapePtr); - cpSpaceRemoveShape($(cpSpace* space), shape); - cpShapeFree (shape); }|] - --- Sensor -getSensor :: Ptr Shape -> IO Bool -getSensor shape = toEnum . fromIntegral <$> [C.exp| int { - cpShapeGetSensor($(cpShape* shape)) }|] - -setSensor :: Ptr Shape -> Bool -> IO () -setSensor shape (fromIntegral . fromEnum -> isSensor) = [C.exp| void { - cpShapeSetSensor($(cpShape* shape), $(int isSensor)) }|] - -instance Component Sensor where - type Storage Sensor = Space Sensor -instance (MonadIO m, Has w m Physics) => Has w m Sensor where - getStore = (cast :: Space Physics -> Space Sensor) <$> getStore - -instance MonadIO m => ExplMembers m (Space Sensor) where - explMembers s = explMembers (cast s :: Space Shape) - -instance MonadIO m => ExplSet m (Space Sensor) where - explSet (Space _ sMap _ _ _) ety (Sensor isSensor) = liftIO $ do - rd <- M.lookup ety <$> readIORef sMap - forM_ rd$ \(Record s _) -> setSensor s isSensor - -instance MonadIO m => ExplGet m (Space Sensor) where - explExists s ety = liftIO $ explExists (cast s :: Space Shape) ety - explGet (Space _ sMap _ _ _) ety = liftIO $ do - Just (Record s _) <- M.lookup ety <$> readIORef sMap - Sensor <$> getSensor s - --- Elasticity -getElasticity :: Ptr Shape -> IO Double -getElasticity shape = realToFrac <$> [C.exp| double { - cpShapeGetElasticity($(cpShape* shape)) }|] - -setElasticity :: Ptr Shape -> Double -> IO () -setElasticity shape (realToFrac -> elasticity) = [C.exp| void { - cpShapeSetElasticity($(cpShape* shape), $(double elasticity)) }|] - -instance Component Elasticity where - type Storage Elasticity = Space Elasticity -instance (MonadIO m, Has w m Physics) => Has w m Elasticity where - getStore = (cast :: Space Physics -> Space Elasticity) <$> getStore - -instance MonadIO m => ExplMembers m (Space Elasticity) where - explMembers s = explMembers (cast s :: Space Shape) - -instance MonadIO m => ExplSet m (Space Elasticity) where - explSet (Space _ sMap _ _ _) ety (Elasticity elasticity) = liftIO $ do - rd <- M.lookup ety <$> readIORef sMap - forM_ rd$ \(Record s _) -> setElasticity s elasticity - -instance MonadIO m => ExplGet m (Space Elasticity) where - explExists s ety = liftIO $ explExists (cast s :: Space Shape) ety - explGet (Space _ sMap _ _ _) ety = liftIO $ do - Just (Record s _) <- M.lookup ety <$> readIORef sMap - Elasticity <$> getElasticity s - --- Mass -getMass :: Ptr Shape -> IO Double -getMass shape = realToFrac <$> [C.exp| double { - cpShapeGetMass($(cpShape* shape)) }|] - -setMass :: Ptr Shape -> Double -> IO () -setMass shape (realToFrac -> mass) = [C.exp| void { - cpShapeSetMass($(cpShape* shape), $(double mass)) }|] - -instance Component Mass where - type Storage Mass = Space Mass -instance (MonadIO m, Has w m Physics) => Has w m Mass where - getStore = (cast :: Space Physics -> Space Mass) <$> getStore - -instance MonadIO m => ExplMembers m (Space Mass) where - explMembers s = explMembers (cast s :: Space Shape) - -instance MonadIO m => ExplSet m (Space Mass) where - explSet (Space _ sMap _ _ _) ety (Mass mass) = liftIO $ do - rd <- M.lookup ety <$> readIORef sMap - forM_ rd$ \(Record s _) -> setMass s mass - -instance MonadIO m => ExplGet m (Space Mass) where - explExists s ety = liftIO $ explExists (cast s :: Space Shape) ety - explGet (Space _ sMap _ _ _) ety = liftIO $ do - Just (Record s _) <- M.lookup ety <$> readIORef sMap - Mass <$> getMass s - --- Density -getDensity :: Ptr Shape -> IO Double -getDensity shape = realToFrac <$> [C.exp| double { - cpShapeGetDensity($(cpShape* shape)) }|] - -setDensity :: Ptr Shape -> Double -> IO () -setDensity shape (realToFrac -> density) = [C.exp| void { - cpShapeSetDensity($(cpShape* shape), $(double density)) }|] - -instance Component Density where - type Storage Density = Space Density -instance (MonadIO m, Has w m Physics) => Has w m Density where - getStore = (cast :: Space Physics -> Space Density) <$> getStore - -instance MonadIO m => ExplMembers m (Space Density) where - explMembers s = explMembers (cast s :: Space Shape) - -instance MonadIO m => ExplSet m (Space Density) where - explSet (Space _ sMap _ _ _) ety (Density density) = liftIO $ do - rd <- M.lookup ety <$> readIORef sMap - forM_ rd$ \(Record s _) -> setDensity s density - -instance MonadIO m => ExplGet m (Space Density) where - explExists s ety = liftIO $ explExists (cast s :: Space Shape) ety - explGet (Space _ sMap _ _ _) ety = liftIO $ do - Just (Record s _) <- M.lookup ety <$> readIORef sMap - Density <$> getDensity s - --- Friction -getFriction :: Ptr Shape -> IO Double -getFriction shape = realToFrac <$> [C.exp| double { - cpShapeGetFriction($(cpShape* shape)) }|] - -setFriction :: Ptr Shape -> Double -> IO () -setFriction shape (realToFrac -> friction) = [C.exp| void { - cpShapeSetFriction($(cpShape* shape), $(double friction)) }|] - -instance Component Friction where - type Storage Friction = Space Friction -instance (MonadIO m, Has w m Physics) => Has w m Friction where - getStore = (cast :: Space Physics -> Space Friction) <$> getStore - -instance MonadIO m => ExplMembers m (Space Friction) where - explMembers s = explMembers (cast s :: Space Shape) - -instance MonadIO m => ExplSet m (Space Friction) where - explSet (Space _ sMap _ _ _) ety (Friction friction) = liftIO $ do - rd <- M.lookup ety <$> readIORef sMap - forM_ rd$ \(Record s _) -> setFriction s friction - -instance MonadIO m => ExplGet m (Space Friction) where - explExists s ety = liftIO $ explExists (cast s :: Space Shape) ety - explGet (Space _ sMap _ _ _) ety = liftIO $ do - Just (Record s _) <- M.lookup ety <$> readIORef sMap - Friction <$> getFriction s - --- SurfaceVelocity -getSurfaceVelocity :: Ptr Shape -> IO Vec -getSurfaceVelocity shape = do - x <- [C.exp| double { cpShapeGetSurfaceVelocity($(cpShape* shape)).x }|] - y <- [C.exp| double { cpShapeGetSurfaceVelocity($(cpShape* shape)).y }|] - return (V2 (realToFrac x) (realToFrac y)) - -setSurfaceVelocity :: Ptr Shape -> Vec -> IO () -setSurfaceVelocity shape (V2 (realToFrac -> x) (realToFrac -> y)) = [C.block| void { - const cpVect vec = { $(double x), $(double y) }; - cpShapeSetSurfaceVelocity($(cpShape* shape), vec); - }|] - -instance Component SurfaceVelocity where - type Storage SurfaceVelocity = Space SurfaceVelocity -instance (MonadIO m, Has w m Physics) => Has w m SurfaceVelocity where - getStore = (cast :: Space Physics -> Space SurfaceVelocity) <$> getStore - -instance MonadIO m => ExplMembers m (Space SurfaceVelocity) where - explMembers s = explMembers (cast s :: Space Shape) - -instance MonadIO m => ExplSet m (Space SurfaceVelocity) where - explSet (Space _ sMap _ _ _) ety (SurfaceVelocity svel) = liftIO $ do - rd <- M.lookup ety <$> readIORef sMap - forM_ rd$ \(Record s _) -> setSurfaceVelocity s svel - -instance MonadIO m => ExplGet m (Space SurfaceVelocity) where - explExists s ety = liftIO $ explExists (cast s :: Space Shape) ety - explGet (Space _ sMap _ _ _) ety = liftIO $ do - Just (Record s _) <- M.lookup ety <$> readIORef sMap - SurfaceVelocity <$> getSurfaceVelocity s - --- CollisionFilter -getFilter :: Ptr Shape -> IO CollisionFilter -getFilter shape = do - group <- [C.exp| unsigned int { cpShapeGetFilter($(cpShape* shape)).group }|] - cats <- [C.exp| unsigned int { cpShapeGetFilter($(cpShape* shape)).categories }|] - mask <- [C.exp| unsigned int { cpShapeGetFilter($(cpShape* shape)).mask }|] - return$ CollisionFilter group (Bitmask cats) (Bitmask mask) - -setFilter :: Ptr Shape -> CollisionFilter -> IO () -setFilter shape (CollisionFilter group (Bitmask cats) (Bitmask mask)) = - [C.block| void { - const cpShapeFilter filter = { $(unsigned int group) - , $(unsigned int cats) - , $(unsigned int mask) }; - cpShapeSetFilter($(cpShape* shape), filter); - }|] - -instance Component CollisionFilter where - type Storage CollisionFilter = Space CollisionFilter -instance (MonadIO m, Has w m Physics) => Has w m CollisionFilter where - getStore = (cast :: Space Physics -> Space CollisionFilter) <$> getStore - -instance MonadIO m => ExplMembers m (Space CollisionFilter) where - explMembers s = explMembers (cast s :: Space Shape) - -instance MonadIO m => ExplSet m (Space CollisionFilter) where - explSet (Space _ sMap _ _ _) ety cfilter = liftIO $ do - rd <- M.lookup ety <$> readIORef sMap - forM_ rd$ \(Record s _) -> setFilter s cfilter - -instance MonadIO m => ExplGet m (Space CollisionFilter) where - explExists s ety = liftIO $ explExists (cast s :: Space Shape) ety - explGet (Space _ sMap _ _ _) ety = liftIO $ do - Just (Record s _) <- M.lookup ety <$> readIORef sMap - getFilter s - --- CollisionType -getCollisionType :: Ptr Shape -> IO C.CUIntPtr -getCollisionType shape = [C.exp| uintptr_t { - cpShapeGetCollisionType($(cpShape* shape)) }|] - -setCollisionType :: Ptr Shape -> C.CUIntPtr -> IO () -setCollisionType shape ctype = [C.exp| void { - cpShapeSetCollisionType($(cpShape* shape), $(uintptr_t ctype)) }|] - -instance Component CollisionType where - type Storage CollisionType = Space CollisionType -instance (MonadIO m, Has w m Physics) => Has w m CollisionType where - getStore = (cast :: Space Physics -> Space CollisionType) <$> getStore - -instance MonadIO m => ExplMembers m (Space CollisionType) where - explMembers s = explMembers (cast s :: Space Shape) - -instance MonadIO m => ExplSet m (Space CollisionType) where - explSet (Space _ sMap _ _ _) ety (CollisionType ctype) = liftIO $ do - rd <- M.lookup ety <$> readIORef sMap - forM_ rd$ \(Record s _) -> setCollisionType s ctype - -instance MonadIO m => ExplGet m (Space CollisionType) where - explExists s ety = liftIO $ explExists (cast s :: Space Shape) ety - explGet (Space _ sMap _ _ _) ety = liftIO $ do - Just (Record s _) <- M.lookup ety <$> readIORef sMap - CollisionType <$> getCollisionType s diff --git a/apecs-physics/src/Apecs/Physics/Space.hs b/apecs-physics/src/Apecs/Physics/Space.hs deleted file mode 100644 index 056a805..0000000 --- a/apecs-physics/src/Apecs/Physics/Space.hs +++ /dev/null @@ -1,111 +0,0 @@ -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE GeneralizedNewtypeDeriving #-} -{-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE QuasiQuotes #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE Strict #-} -{-# LANGUAGE TemplateHaskell #-} -{-# LANGUAGE TypeFamilies #-} -{-# LANGUAGE UndecidableInstances #-} -{-# LANGUAGE ViewPatterns #-} - -module Apecs.Physics.Space where - -import Apecs -import Apecs.Core -import Control.Monad.IO.Class (liftIO, MonadIO) -import Data.IORef -import Foreign.Concurrent -import Foreign.ForeignPtr (withForeignPtr) -import qualified Language.C.Inline as C -import Linear.V2 - -import Apecs.Physics.Types - -C.context phycsCtx -C.include "" - --- Space -newSpace :: IO SpacePtr -newSpace = do - spaceRaw <- [C.exp| cpSpace* { cpSpaceNew() } |] - newForeignPtr spaceRaw [C.exp| void { cpSpaceFree($(cpSpace* spaceRaw)) } |] - -explStepPhysics :: SpacePtr -> Double -> IO () -explStepPhysics spacePtr (realToFrac -> dT) = withForeignPtr spacePtr $ \space -> - [C.exp| void { cpSpaceStep( $(cpSpace* space), $(double dT) ) } |] - -stepPhysics :: MonadIO m => Has w m Physics => Double -> SystemT w m () -stepPhysics dT = do - s :: Space Physics <- getStore - liftIO$ explStepPhysics (spacePtr s) dT - -instance Component Physics where - type Storage Physics = Space Physics - -type instance Elem (Space Physics) = Physics - -instance MonadIO m => ExplInit m (Space Physics) where - explInit = liftIO $ do - spacePtr <- newSpace - bRef <- newIORef mempty - sRef <- newIORef mempty - cRef <- newIORef mempty - hRef <- newIORef mempty - return (Space bRef sRef cRef hRef spacePtr) - --- Gravity -earthGravity :: Gravity -earthGravity = Gravity $ V2 0 (-9.81) - -getGravity :: SpacePtr -> IO (V2 Double) -getGravity spacePtr = withForeignPtr spacePtr $ \space -> do - x <- [C.exp| double { cpSpaceGetGravity ($(cpSpace* space)).x } |] - y <- [C.exp| double { cpSpaceGetGravity ($(cpSpace* space)).y } |] - return (V2 (realToFrac x) (realToFrac y)) - -setGravity :: SpacePtr -> V2 Double -> IO () -setGravity spacePtr (V2 (realToFrac -> x) (realToFrac -> y)) = withForeignPtr spacePtr $ \space -> [C.block| void { - const cpVect vec = { $(double x), $(double y) }; - cpSpaceSetGravity($(cpSpace* space), vec); - } |] - -instance Component Gravity where - type Storage Gravity = Space Gravity - -instance (MonadIO m, Has w m Physics) => Has w m Gravity where - getStore = (cast :: Space Physics -> Space Gravity) <$> getStore - -type instance Elem (Space Gravity) = Gravity - -instance MonadIO m => ExplGet m (Space Gravity) where - explExists _ _ = return True - explGet (Space _ _ _ _ spcPtr) _ = liftIO $ Gravity <$> getGravity spcPtr -instance MonadIO m => ExplSet m (Space Gravity) where - explSet (Space _ _ _ _ spcPtr) _ (Gravity v) = liftIO $ setGravity spcPtr v - --- Iterations -getIterations :: SpacePtr -> IO Int -getIterations spacePtr = withForeignPtr spacePtr $ \space -> fromIntegral <$> [C.exp| int { cpSpaceGetIterations ($(cpSpace* space)) } |] - -setIterations :: SpacePtr -> Int -> IO () -setIterations spacePtr (fromIntegral -> its) = withForeignPtr spacePtr $ \space -> [C.block| void { - cpSpaceSetIterations($(cpSpace* space), $(int its)); - } |] - -instance Component Iterations where - type Storage Iterations = Space Iterations - -instance (MonadIO m, Has w m Physics) => Has w m Iterations where - getStore = (cast :: Space Physics -> Space Iterations) <$> getStore - -type instance Elem (Space Iterations) = Iterations - -instance MonadIO m => ExplGet m (Space Iterations) where - explExists _ _ = return False - explGet (Space _ _ _ _ spcPtr) _ = liftIO $ Iterations <$> getIterations spcPtr -instance MonadIO m => ExplSet m (Space Iterations) where - explSet (Space _ _ _ _ spcPtr) _ (Iterations v) = liftIO $ setIterations spcPtr v diff --git a/apecs-physics/src/Apecs/Physics/Types.hs b/apecs-physics/src/Apecs/Physics/Types.hs deleted file mode 100644 index 9b4a17d..0000000 --- a/apecs-physics/src/Apecs/Physics/Types.hs +++ /dev/null @@ -1,318 +0,0 @@ -{-# 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) - --- | A subcomponent of @Body@ representing where it is in world coordinates. -newtype Position = Position WVec --- | A subcomponent of @Body@ representing where it is going in world coordinates -newtype Velocity = Velocity WVec --- | A component used to apply a force to a @Body@. --- The force is applied to the body's center of gravity. --- This component is reset to @ Vec 0 0 @ after every stimulation step, --- so it is mainly used to apply a force as opposed to being read. -newtype Force = Force Vec --- | A component used to apply a torque to a @Body@. --- The torque is applied to the entire body at once. --- This component is reset to @ 0 @ after every simulation step, so it --- is mainly used to apply a torque as opposed to being read. -newtype Torque = Torque Double --- | A component representing the mass of the @Body@ overall. -newtype BodyMass = BodyMass Double deriving (Eq, Show) --- | The moment of inertia of the @Body@. --- This is basically the body's tendency to resist angular acceleration. -newtype Moment = Moment Double deriving (Eq, Show) -newtype Angle = Angle Double deriving (Eq, Show) -newtype AngularVelocity = AngularVelocity Double --- | Where the @Body@'s center of gravity is, in body-local coordinates. --- Can be read and written to. -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) - --- | If a body is a 'Sensor', it exists only to trigger collision responses. --- It won't phyiscally interact with other bodies in any way, but it __will__ --- cause collision handlers to run. -newtype Sensor = Sensor Bool deriving (Eq, Show) --- | The elasticity of a shape. Higher elasticities will create more --- elastic collisions, IE, will be bouncier. --- --- See for more information. -newtype Elasticity = Elasticity Double deriving (Eq, Show) --- | The mass of a shape is technically a measure of how much resistance it has to --- being accelerated, but it's generally easier to understand it as being how "heavy" something is. --- --- The physics engine lets you set this, and it will calculate the 'Density' and other components --- for you. --- --- See for more information. -newtype Mass = Mass Double deriving (Eq, Show) --- | The density of a shape is a measure of how much mass an object has in a given volume. --- --- The physics engine lets you set this, and it will calculate the 'Mass' and other components for you. --- --- See for more information. -newtype Density = Density Double deriving (Eq, Show) --- | The friction of an object is a measure of how much it resists movement. --- Shapes with high friction will naturally slow down more quickly over time than objects --- with low friction. --- --- See for more information. -newtype Friction = Friction Double deriving (Eq, Show) -newtype SurfaceVelocity = SurfaceVelocity Vec deriving (Eq, Show) - -type CollisionGroup = CUInt - --- | Collision Filters determine what shapes this shape collides with. --- Shapes in the same 'filterGroup' will never collide with one another. --- This is used to ignore collisions between parts on a complex object. --- --- 'filterCategories' is a bitmask that determines what categories a shape belongs to. --- 'filterMask' is a bitmask that determines what categories it collides with. --- See for more information. -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. --- Bodies with a speed less than this will not be simulated until a force acts upon them, --- which can potentially lead to large gains in performance, especially if there's a lot of --- inactive bodies in the simulation. -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) -newtype Impulse = Impulse Double 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 --- 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 - -- ^ A callback called when two bodies start touching for the first time. - -- If it returns 'True', the physics engine will process the collision normally. - -- If it returns 'False', the physics engine will __ignore the collision entirely__. - , separateCB :: Maybe SeparateCB - -- ^ A callback called when two bodies have just stopped touching. This will - -- __always__ be called if 'beginCB' is, regardless of the return value of 'beginCB'. - , preSolveCB :: Maybe PreSolveCB - -- ^ A callback called when two bodies are touching during a physics step. If this function - -- returns 'True', the collision will be processed normally. If it returns 'False, then - -- the physics engine will stop processing the collision for this step. - , postSolveCB :: Maybe PostSolveCB - -- ^ A callback called when two bodies are touching __after__ the response to the collision - -- has been processed. This means that you can determine the collision impulse or kinetic energy - -- in this callback, if you need that for processing. - } - --- | A 'Shape' can have a 'CollisionType'. --- 'CollisionType's are used by callbacks for filtering, also see 'CollisionSource'. --- The difference between 'CollisionType' and 'CollisionFilter' is that a 'CollisionFilter' determines whether --- two shapes in the physics engine collide, or pass through one another, whereas a 'CollisionType' determines --- what callback is called. --- In general, if you don't want any special checks to happen, use 'CollisionFilter'. -newtype CollisionType = CollisionType C.CUIntPtr - deriving (Num, Eq, Show) - --- | A 'CollisionSource' determines what types of collisions a callback handles. --- Also see 'CollisionType' -data CollisionSource - = Wildcard CollisionType - | Between CollisionType CollisionType - --- Corresponds to an 'arbiter' in Chipmunk -data Collision = Collision - { collisionNormal :: Vec - , collisionBodyA :: Entity - , collisionBodyB :: Entity - , collisionShapeA :: Entity - , collisionShapeB :: Entity - } deriving (Eq, Show) - -data CollisionProperties = CollisionProperties - { collisionElasticity :: Double - , collisionFriction :: Double - , collisionSurfaceVelocity :: Vec - } deriving (Eq, Show) - -data SegmentQueryResult = SegmentQueryResult - { sqShape :: Entity - -- ^ What entity did this query connect with? - , sqImpactPoint :: Vec - -- ^ The point that the segment impacted with the shape - , sqImpactNormal :: Vec - -- ^ The normal of the surface that the segment hit - , sqImpactAlpha :: Double - -- ^ The normalized distance along the query segment in the range `[0, 1]`. - -- Multiply it by the length of the segment to get the distance away the shape is. - } deriving (Eq, Show) - -data PointQueryResult = PointQueryResult - { pqShape :: Entity - -- ^ What entity did this query connect with? - , pqPoint :: WVec - -- ^ The closest point on the shape's surface (in world space) - , pqDistance :: Double - -- ^ The distance to the queried point - , pqGradient :: Vec - -- ^ The gradient of the distance function. - -- This should be similar to 'pqPoint'/'pqDistance' but accurate even for - -- very small distances. - } deriving (Eq, Show) diff --git a/apecs-physics/tumbler.png b/apecs-physics/tumbler.png deleted file mode 100644 index 8917cdc..0000000 Binary files a/apecs-physics/tumbler.png and /dev/null differ diff --git a/apecs-stm/CHANGELOG.md b/apecs-stm/CHANGELOG.md deleted file mode 100644 index 6357935..0000000 --- a/apecs-stm/CHANGELOG.md +++ /dev/null @@ -1,12 +0,0 @@ -## [0.1.3] -### Changed -- (#60) Add `Component` type names in non-existent component errors -- Increased upper bound `apecs` dependency - -## [0.1.2] -### Changed -- apecs version bump - -## [0.1.0] -### Added -- Split out the STM parts of the main apecs package diff --git a/apecs-stm/LICENSE b/apecs-stm/LICENSE deleted file mode 100644 index 32cc34e..0000000 --- a/apecs-stm/LICENSE +++ /dev/null @@ -1,30 +0,0 @@ -Copyright Jonas Carpay (c) 2017 - -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - - * Neither the name of Jonas Carpay nor the names of other - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/apecs-stm/README.md b/apecs-stm/README.md deleted file mode 100644 index 68bacfd..0000000 --- a/apecs-stm/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# apecs-stm -Experimental STM stores, allow apecs to be run in the STM monad. -```haskell -atomically . cmap $ \(Position p, Velocity v) -> Position (v+p) -``` diff --git a/apecs-stm/Setup.hs b/apecs-stm/Setup.hs deleted file mode 100644 index 9a994af..0000000 --- a/apecs-stm/Setup.hs +++ /dev/null @@ -1,2 +0,0 @@ -import Distribution.Simple -main = defaultMain diff --git a/apecs-stm/apecs-stm.cabal b/apecs-stm/apecs-stm.cabal deleted file mode 100644 index 03b3470..0000000 --- a/apecs-stm/apecs-stm.cabal +++ /dev/null @@ -1,40 +0,0 @@ -name: apecs-stm -version: 0.1.3 -homepage: https://github.com/jonascarpay/apecs-stm#readme -license: BSD3 -license-file: LICENSE -author: Jonas Carpay -maintainer: jonascarpay@gmail.com -category: Game, Control, Data -build-type: Simple -cabal-version: >=1.10 -synopsis: STM stores for apecs -description: - Apecs stores that live in the STM monad, and other tools. - -extra-source-files: - README.md, - CHANGELOG.md - -source-repository head - type: git - location: git://github.com/jonascarpay/apecs-stm.git - -library - hs-source-dirs: - src - exposed-modules: - Apecs.STM, Apecs.STM.Prelude - default-language: - Haskell2010 - build-depends: - apecs >= 0.7 && < 0.10, - base >= 4.9 && < 5, - containers >= 0.5 && < 0.8, - list-t >= 1 && < 1.2, - stm >= 2.3 && < 3, - stm-containers >= 1.1 && < 2, - template-haskell >= 2.12 && < 3, - vector >= 0.10 && < 0.13 - ghc-options: - -Wall diff --git a/apecs-stm/src/Apecs/STM.hs b/apecs-stm/src/Apecs/STM.hs deleted file mode 100644 index 26f7003..0000000 --- a/apecs-stm/src/Apecs/STM.hs +++ /dev/null @@ -1,206 +0,0 @@ -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE GeneralizedNewtypeDeriving #-} -{-# LANGUAGE LambdaCase #-} -{-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE TemplateHaskell #-} -{-# LANGUAGE TypeFamilies #-} - --- | This module contains STM-supporting versions of regular apecs stores, and some convenience functions. --- It is designed to be imported qualified, since it shadows both apecs and STM names. --- There is also an @Apecs.STM.Prelude@ module, which can be imported by itself. --- --- Note that if you want to be able to create entities in STM, you will also need to use a STM-supported @EntityCounter@, typically done through this module's @makeWorld@. - -module Apecs.STM - ( -- * Stores - Map (..) - , Unique (..) - , Global (..) - -- * EntityCounter - , EntityCounter (..) - , nextEntity, newEntity, makeWorld - -- * STM conveniences - , atomically, retry, check, forkSys, threadDelay, STM - ) where - -import qualified Control.Concurrent as S -import Control.Concurrent.STM (STM) -import qualified Control.Concurrent.STM as S -import Control.Concurrent.STM.TVar as S -import Control.Monad -import Data.Maybe -import Data.Monoid (Sum (..)) -import Data.Semigroup -import Data.Typeable (Typeable, typeRep) -import qualified Data.Vector.Unboxed as U -import Language.Haskell.TH -import qualified ListT as L -import qualified StmContainers.Map as M - -import Apecs (ask, get, global, lift, liftIO, - runSystem, set) -import Apecs.Core -import Apecs.TH (makeWorldNoEC) - -newtype Map c = Map (M.Map Int c) -type instance Elem (Map c) = c - -instance ExplInit STM (Map c) where - explInit = Map <$> M.new -instance Typeable c => ExplGet STM (Map c) where - {-# INLINE explExists #-} - {-# INLINE explGet #-} - explExists (Map m) ety = isJust <$> M.lookup ety m - explGet (Map m) ety = flip fmap (M.lookup ety m) $ \case - Just c -> c - notFound -> error $ unwords - [ "Reading non-existent STM Map component" - , show (typeRep notFound) - , "for entity" - , show ety - ] - -instance ExplSet STM (Map c) where - {-# INLINE explSet #-} - explSet (Map m) ety x = M.insert x ety m -instance ExplDestroy STM (Map c) where - {-# INLINE explDestroy #-} - explDestroy (Map m) ety = M.delete ety m -instance ExplMembers STM (Map c) where - {-# INLINE explMembers #-} - explMembers (Map m) = U.unfoldrM L.uncons $ fst <$> M.listT m - -instance ExplInit IO (Map c) where - {-# INLINE explInit #-} - explInit = S.atomically explInit -instance Typeable c => ExplGet IO (Map c) where - {-# INLINE explExists #-} - {-# INLINE explGet #-} - explExists m e = S.atomically $ explExists m e - explGet m e = S.atomically $ explGet m e -instance ExplSet IO (Map c) where - {-# INLINE explSet #-} - explSet m e x = S.atomically $ explSet m e x -instance ExplDestroy IO (Map c) where - {-# INLINE explDestroy #-} - explDestroy m e = S.atomically $ explDestroy m e -instance ExplMembers IO (Map c) where - {-# INLINE explMembers #-} - explMembers m = S.atomically $ explMembers m - -newtype Unique c = Unique (TVar (Maybe (Int, c))) -type instance Elem (Unique c) = c -instance ExplInit STM (Unique c) where - explInit = Unique <$> newTVar Nothing - -instance Typeable c => ExplGet STM (Unique c) where - {-# INLINE explGet #-} - explGet (Unique ref) _ = flip fmap (readTVar ref) $ \case - Just (_, c) -> c - notFound -> error $ unwords - [ "Reading non-existent STM Unique component" - , show (typeRep notFound) - ] - {-# INLINE explExists #-} - explExists (Unique ref) ety = maybe False ((==ety) . fst) <$> readTVar ref - -instance ExplSet STM (Unique c) where - {-# INLINE explSet #-} - explSet (Unique ref) ety c = writeTVar ref (Just (ety, c)) - -instance ExplDestroy STM (Unique c) where - {-# INLINE explDestroy #-} - explDestroy (Unique ref) ety = readTVar ref >>= - mapM_ (flip when (writeTVar ref Nothing) . (==ety) . fst) - -instance ExplMembers STM (Unique c) where - {-# INLINE explMembers #-} - explMembers (Unique ref) = flip fmap (readTVar ref) $ \case - Nothing -> mempty - Just (ety, _) -> U.singleton ety - -instance ExplInit IO (Unique c) where - {-# INLINE explInit #-} - explInit = S.atomically explInit -instance Typeable c => ExplGet IO (Unique c) where - {-# INLINE explExists #-} - explExists m e = S.atomically $ explExists m e - {-# INLINE explGet #-} - explGet m e = S.atomically $ explGet m e -instance ExplSet IO (Unique c) where - {-# INLINE explSet #-} - explSet m e x = S.atomically $ explSet m e x -instance ExplDestroy IO (Unique c) where - {-# INLINE explDestroy #-} - explDestroy m e = S.atomically $ explDestroy m e -instance ExplMembers IO (Unique c) where - {-# INLINE explMembers #-} - explMembers m = S.atomically $ explMembers m - -newtype Global c = Global (TVar c) -type instance Elem (Global c) = c -instance Monoid c => ExplInit STM (Global c) where - {-# INLINE explInit #-} - explInit = Global <$> newTVar mempty -instance ExplGet STM (Global c) where - {-# INLINE explGet #-} - explGet (Global ref) _ = readTVar ref - {-# INLINE explExists #-} - explExists _ _ = return True -instance ExplSet STM (Global c) where - {-# INLINE explSet #-} - explSet (Global ref) _ c = writeTVar ref c - -instance Monoid c => ExplInit IO (Global c) where - {-# INLINE explInit #-} - explInit = S.atomically explInit -instance ExplGet IO (Global c) where - {-# INLINE explExists #-} - explExists m e = S.atomically $ explExists m e - {-# INLINE explGet #-} - explGet m e = S.atomically $ explGet m e -instance ExplSet IO (Global c) where - {-# INLINE explSet #-} - explSet m e x = S.atomically $ explSet m e x - -newtype EntityCounter = EntityCounter {getCounter :: Sum Int} deriving (Semigroup, Monoid, Eq, Show) - -instance Component EntityCounter where - type Storage EntityCounter = Global EntityCounter - -{-# INLINE nextEntity #-} -nextEntity :: (Get w m EntityCounter, Set w m EntityCounter) => SystemT w m Entity -nextEntity = do EntityCounter n <- get global - set global (EntityCounter $ n+1) - return (Entity . getSum $ n) - -{-# INLINE newEntity #-} -newEntity :: (Set w m c, Get w m EntityCounter, Set w m EntityCounter) - => c -> SystemT w m Entity -newEntity c = do ety <- nextEntity - set ety c - return ety - --- | Like @makeWorld@ from @Apecs@, but uses the STM @EntityCounter@ -makeWorld :: String -> [Name] -> Q [Dec] -makeWorld worldName cTypes = makeWorldNoEC worldName (cTypes ++ [''EntityCounter]) - --- | @atomically@ from STM, lifted to the System level. -atomically :: SystemT w STM a -> SystemT w IO a -atomically sys = ask >>= liftIO . S.atomically . runSystem sys - --- | @retry@ from STM, lifted to the System level. -retry :: SystemT w STM a -retry = lift S.retry - --- | @check@ from STM, lifted to the System level. -check :: Bool -> SystemT w STM () -check = lift . S.check - --- | Runs a system on a new thread. -forkSys :: SystemT w IO () -> SystemT w IO S.ThreadId -forkSys sys = ask >>= liftIO . S.forkIO . runSystem sys - --- | Suspends the current thread for a number of microseconds. -threadDelay :: Int -> SystemT w IO () -threadDelay = liftIO . S.threadDelay diff --git a/apecs-stm/src/Apecs/STM/Prelude.hs b/apecs-stm/src/Apecs/STM/Prelude.hs deleted file mode 100644 index a1978ce..0000000 --- a/apecs-stm/src/Apecs/STM/Prelude.hs +++ /dev/null @@ -1,13 +0,0 @@ --- | This module re-exports the apecs prelude with STM versions. - -module Apecs.STM.Prelude - ( System - , module Apecs.STM - , module Apecs - ) where - -import Apecs hiding (EntityCounter, Global, Map, System, Unique, - makeWorld, newEntity) -import Apecs.STM - -type System w a = SystemT w STM a diff --git a/apecs.cabal b/apecs.cabal new file mode 100644 index 0000000..bb73761 --- /dev/null +++ b/apecs.cabal @@ -0,0 +1,54 @@ +cabal-version: 2.4 +name: apecs +version: 1.0 +license: BSD-3-Clause +build-type: Simple +license-file: LICENSE +author: Jonas Carpay +maintainer: Jonas Carpay +copyright: 2017 Jonas Carpay +category: Game, Control, Data +tested-with: GHC ==8.8.3 +extra-doc-files: + CHANGELOG.md + README.md + +common common-options + build-depends: + , base >=4.9.0.0 + , mtl + + default-language: Haskell2010 + ghc-options: + -Wall -Wcompat -Widentities -Wincomplete-uni-patterns + -Wincomplete-record-updates -Wredundant-constraints + -fhide-source-paths -Wmissing-export-lists -Wpartial-fields + +library + import: common-options + hs-source-dirs: src + exposed-modules: + Apecs + Apecs.Core + Apecs.Example + Apecs.Focus + Apecs.Reactive + Apecs.Stores + Apecs.System + + build-depends: + , array + , containers + , vector + +benchmark apecs-bench + import: common-options + type: exitcode-stdio-1.0 + hs-source-dirs: bench + main-is: Main.hs + build-depends: + , apecs + , criterion + , linear + + ghc-options: -threaded -O2 diff --git a/apecs/CHANGELOG.md b/apecs/CHANGELOG.md deleted file mode 100644 index 3e2cb9f..0000000 --- a/apecs/CHANGELOG.md +++ /dev/null @@ -1,140 +0,0 @@ -## [0.9.2] -### Changed -- (#68) Add instances of MonadThrow, MonadCatch, and MonadMask to SystemT -- Cleaned up the README -- Small haddock fixes - -## [0.9.1] -### Changed -- (#63) Fixed bug where `modify` on non-existent components crashes - -## [0.9.0] -### Added -- (#59) Expose `makeMapComponents`, which creates `Component` instances with `Map` stores -### Changed -- (#60) Add `Component` type names in non-existent component errors -- Relaxed the type of `modify` to allow a different return type -- Constrain the `cmapM_` type `(c -> SystemT w m ()) -> SystemT w m ()`, to make it clearer that the inner function does not update `Components` -- Simplify nix infrastructure - -## [0.8.3] -### Changed -- (#58) Added support for Template Haskell 2.15.0.0 through CPP flags - -## [0.8.2] -### Changed -- (#55) Fixed a bug where components where not properly deleted from the cache following the cache bitmasking update - -## [0.8.1] -### Changed -- Changed `Cache`s to use bitmasks instead of the remainder operation. This makes caches up to three times faster. -- Fixed bug in `Cache` where the same entity appeared in member list of both the cache and the underlying store - -## [0.8.0] -There are a number of unsolved problems in apecs' design space. -Most notably, it needs a good way to do streaming and reactivity, or find a way to integrate with existing solutions. -I'm hesitant to accept some of the feature requests I've gotten because they would be obsoleted when we figure this out, and I don't want to pollute the API with unstable features. - -However, I don't think we should let perfect get in the way of good. -So, apecs 0.8 have new `Apecs.Experimental.*` modules, that I want to use for features that might or might not get removed or changed. -They should provide some conveniences, but the catch is that their API might undergo significant changes between point releases (and therefore within LTS'es). -Some of the existing modules were already marked experimental, and those have been moved. - -### Added -- Experimental `Head` component -- `OrdMap` and `IxMap` reactive maps - -### Changed -- Moved `ReadOnly` to `Apecs.Stores` since `EntityCounter` now depends on it -- Moved spatial hashing to `Experimental.Util` -- Moved `Redirect` and `Stores.Extra` to `Experimental.Stores` -- Moved `Reactive` to `Experimental.Reactive` -- `rget` has been replaced by `withReactive` -- Improved error messages for unsafe operations - -## [0.7.3] -### Changed -- Added Data.Semigroup to Stores.Extra to build with GHC 8.2.2 in hackage matrix - -## [0.7.2] -### Changed -- Fixed bug in the `Pushdown` store -- `Apecs` module no longer re-exports the entire `Data.Proxy` module, but instead just `Proxy (..)`. -- Added (approximate?) lower and upper version bounds to dependencies - -## [0.7.1] -### Added -- `$=` and `$~` operators as synonyms for `set` and `get` respectively -### Removed -- `getAll` and `count`, which were made redundant by `cfold`. - -## [0.7.0] -### Added -- The `Reactive` store and module is a redesign of the `Register` store, and provides a more general solution for 'stores that perform additional actions when written to'. -- The `Apecs.Stores.Extra` submodule, which contains the `Pushdown` and `ReadOnly` stores. `Pushdown` adds pushdown semantics to stores, and `ReadOnly` hides the `ExplSet` instances of whatever it wraps. -- The `EntityCounter` and associated functions have all been specified to `IO`, since `Global EntityCounter` only works in IO. Furthermore, `EntityCounter` now uses a `ReadOnly` store, to prevent users from accidentally changing its value. -- `Redirect` component that writes to another entity in `cmap`. -### Changed -- Default stores have `MonadIO m => m` instances, rather than `IO`. This makes it easier to nest `SystemT`. -- All apecs packages have been consolidated into a single git repo. -- `Apecs.Components` contains the components (and corresponding stores) from `Apecs.Core`. - -## [0.6.0.0] -### Changed -- Nothing, but since 0.5.1 was API-breaking I've decided to bump to 0.6 -## [0.5.1.1] -### Changed -- `Register` needs UndecidableInstances in GHC 8.6.2, I'm looking for a way around this. I've removed it for now. - -## [0.5.1.0] -### Added -- The `Register` store, which allows reverse lookups for bounded enums. - For example, if `Bool` has storage `Register (Map Bool)`, `regLookup True` will yield a list of all entities with a `True` component. - Can also be used to emulate a hash table, where `fromEnum` is the hashing function. - This allows us to make simple spatial hashes. - I'm open to suggestions for better names than Register. -- `cmapIf`, cmap with a conditional test -### Changed -- `ExplInit` now too takes a monad argument. -- Started rewrite of the test suite -- Caches now internally use -2 to denote absence, to avoid possible conflict with -1 as a global entity -### Removed -- The STM instances have been removed, to be moved to their own package - -## [0.5.0.0] -### Changed -- `System w a` is now a synonym for `SystemT w IO a`. - A variable monad argument allows apecs to be run in monads like ST or STM. - Most of the library has been rewritten to be as permissive as possible in its monad argument. -### Added -- STM stores. These will be moved to a separate package soon. - -## [0.4.1.2] -### Changed -- Either can now be deleted, deleting `Either a b` is the same as deleting `(a,b)`. -- Some were missing their inline pragma's, now they don't - -## [0.4.1.1] -### Changed -- Export `Get`, `Set`, `Destroy`, `Members` by default -- Export `cfold`, `cfoldM`, `cfoldM_` by default -- Fix () instance - -## [0.4.1.0] -### Added -- `cfold`, `cfoldM`, `cfoldM_` -- `Either` instances and `EitherStore` - -### Changed -- Changed MaybeStore implementation to no longer use -1 for missing entities. -- Fixed some outdated documentation. -- Change the `global` void entity to -2, just to be sure it won't conflict if accidentally used in a cache. - -## [0.4.0.0] -### Added -- A changelog - -### Changed -- `Store` is now split into 5 separate type classes; `ExplInit`, `ExplGet`, `ExplSet`, `ExplDestroy`, and `ExplMembers`. - This makes it illegal to e.g. iterate over a `Not`. -- phantom arguments are now given as `Proxy` values, re-exported from `Data.Proxy`. This makes phantom arguments explicit and avoids undefined values. diff --git a/apecs/LICENSE b/apecs/LICENSE deleted file mode 100644 index 32cc34e..0000000 --- a/apecs/LICENSE +++ /dev/null @@ -1,30 +0,0 @@ -Copyright Jonas Carpay (c) 2017 - -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - - * Neither the name of Jonas Carpay nor the names of other - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/apecs/README.md b/apecs/README.md deleted file mode 100644 index 25972a2..0000000 --- a/apecs/README.md +++ /dev/null @@ -1,87 +0,0 @@ -# apecs - -apecs is an [_Entity Component System_](https://en.wikipedia.org/wiki/Entity_component_system) (ECS) library for game development. - -apecs aims to be -* **Fast** - Performance is competitive with Rust ECS libraries (see benchmark results below) -* **Safe** - Completely hides the dangers of the low-level machinery -* **Concise** - Game logic is expressed using a small number of powerful combinators -* **Flexible** - Easily add new modules or backends -* **Cool** - -![Benchmarks](https://raw.githubusercontent.com/jonascarpay/apecs/master/apecs/bench/chart.png) - -### Links -- [documentation on hackage](https://hackage.haskell.org/package/apecs/docs/Apecs.html) -- [tutorial](https://github.com/jonascarpay/apecs/blob/master/examples/Shmup.md) and other [examples](../examples/) - -##### Games/articles -- [An Introduction to Developing Games in Haskell with Apecs](https://aas.sh/blog/making-a-game-with-haskell-and-apecs/) -- [mallRL](https://github.com/nmaehlmann/mallRL) - a _grocery shopping roguelike_ -- [An implementation of the Unity tutorial project using apecs](https://github.com/mewhhaha/apecs-unity-tutorial-haskell) -- [(Your game here)](https://github.com/jonascarpay/apecs/pulls) - -##### Packages -- [apecs](https://github.com/jonascarpay/apecs/tree/master/apecs) - - [![Hackage](https://img.shields.io/hackage/v/apecs.svg)](https://hackage.haskell.org/package/apecs) - [![Stackage](https://www.stackage.org/package/apecs/badge/lts?label=lts)](https://www.stackage.org/package/apecs) - [![Stackage](https://www.stackage.org/package/apecs/badge/nightly?label=nightly)](https://www.stackage.org/package/apecs) - [![Build Status](https://travis-ci.org/jonascarpay/apecs.svg?branch=master)](https://travis-ci.org/jonascarpay/apecs) - -- [apecs-physics](https://github.com/jonascarpay/apecs/tree/master/apecs-physics) - 2D physics using the [Chipmunk2D](https://github.com/slembcke/Chipmunk2D) engine - - [![Hackage](https://img.shields.io/hackage/v/apecs-physics.svg)](https://hackage.haskell.org/package/apecs-physics) - [![Stackage](https://www.stackage.org/package/apecs-physics/badge/lts?label=lts)](https://www.stackage.org/package/apecs-physics) - [![Stackage](https://www.stackage.org/package/apecs-physics/badge/nightly?label=nightly)](https://www.stackage.org/package/apecs-physics) - [![Build Status](https://travis-ci.org/jonascarpay/apecs.svg?branch=master)](https://travis-ci.org/jonascarpay/apecs) - -- [apecs-gloss](https://github.com/jonascarpay/apecs/tree/master/apecs-gloss) - Simple frontend for [gloss](http://hackage.haskell.org/package/gloss)-based rendering - - [![Hackage](https://img.shields.io/hackage/v/apecs-gloss.svg)](https://hackage.haskell.org/package/apecs-gloss) - [![Stackage](https://www.stackage.org/package/apecs-gloss/badge/lts?label=lts)](https://www.stackage.org/package/apecs-gloss) - [![Stackage](https://www.stackage.org/package/apecs-gloss/badge/nightly?label=nightly)](https://www.stackage.org/package/apecs-gloss) - [![Build Status](https://travis-ci.org/jonascarpay/apecs.svg?branch=master)](https://travis-ci.org/jonascarpay/apecs) - -- [apecs-stm](https://github.com/jonascarpay/apecs/tree/master/apecs-stm) - STM-based stores for easy concurrency - - [![Hackage](https://img.shields.io/hackage/v/apecs-stm.svg)](https://hackage.haskell.org/package/apecs-stm) - [![Build Status](https://travis-ci.org/jonascarpay/apecs.svg?branch=master)](https://travis-ci.org/jonascarpay/apecs) - -- [*examples*](https://github.com/jonascarpay/apecs/tree/master/examples/) - - [![Build Status](https://travis-ci.org/jonascarpay/apecs.svg?branch=master)](https://travis-ci.org/jonascarpay/apecs) - -### Example -```haskell -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE TemplateHaskell #-} -{-# LANGUAGE TypeFamilies #-} - -import Apecs -import Linear (V2 (..)) - -newtype Position = Position (V2 Double) deriving Show -newtype Velocity = Velocity (V2 Double) deriving Show -data Flying = Flying - -makeWorldAndComponents "World" [''Position, ''Velocity, ''Flying] - -game :: System World () -game = do - newEntity (Position 0, Velocity 1) - newEntity (Position 2, Velocity 1) - newEntity (Position 1, Velocity 2, Flying) - - -- 1. Add velocity to position - -- 2. Apply gravity to non-flying entities - -- 3. Print a list of entities and their positions - cmap $ \(Position p, Velocity v) -> Position (v+p) - cmap $ \(Velocity v, _ :: Not Flying) -> Velocity (v - V2 0 1) - cmapM_ $ \(Position p, Entity e) -> liftIO . print $ (e, p) - -main :: IO () -main = initWorld >>= runSystem game -``` diff --git a/apecs/Setup.hs b/apecs/Setup.hs deleted file mode 100644 index 9a994af..0000000 --- a/apecs/Setup.hs +++ /dev/null @@ -1,2 +0,0 @@ -import Distribution.Simple -main = defaultMain diff --git a/apecs/apecs.cabal b/apecs/apecs.cabal deleted file mode 100644 index 05583e7..0000000 --- a/apecs/apecs.cabal +++ /dev/null @@ -1,93 +0,0 @@ -name: apecs -version: 0.9.2 -homepage: https://github.com/jonascarpay/apecs#readme -license: BSD3 -license-file: LICENSE -author: Jonas Carpay -maintainer: jonascarpay@gmail.com -category: Game, Control, Data -build-type: Simple -cabal-version: >=1.10 -synopsis: Fast Entity-Component-System library for game programming -description: - apecs is a fast, type-driven Entity-Component-System library for game programming. - -extra-source-files: - README.md, - CHANGELOG.md - -source-repository head - type: git - location: git://github.com/jonascarpay/apecs.git - -library - hs-source-dirs: - src - exposed-modules: - Apecs, - Apecs.Core, - Apecs.Components, - Apecs.Stores, - Apecs.System, - Apecs.TH, - Apecs.Util, - - Apecs.Experimental.Components, - Apecs.Experimental.Reactive, - Apecs.Experimental.Stores, - Apecs.Experimental.Util - other-modules: - Apecs.THTuples - default-language: - Haskell2010 - build-depends: - base >= 4.9 && < 5, - array >= 0.4 && < 0.6, - containers >= 0.5 && < 0.8, - exceptions >= 0.10.0 && < 0.11, - mtl >= 2.2 && < 2.3, - template-haskell >= 2.12 && < 3, - vector >= 0.11 && < 0.13 - ghc-options: - -Wall - -test-suite apecs-test - type: - exitcode-stdio-1.0 - main-is: - Main.hs - hs-source-dirs: - test - build-depends: - apecs, - base >= 4.9 && < 5, - containers >= 0.5 && < 0.8, - linear >= 1.20 && < 2, - vector >= 0.10 && < 0.13, - QuickCheck >= 2.10 && < 3 - default-language: - Haskell2010 - ghc-options: -Wall - -benchmark apecs-bench - type: - exitcode-stdio-1.0 - hs-source-dirs: - bench - main-is: - Main.hs - build-depends: - apecs, - base >= 4.9 && < 5, - criterion >= 1.3 && < 2, - linear >= 1.20 && < 2 - default-language: - Haskell2010 - ghc-options: - -Wall - -O2 - -optlo-O3 - -- -fllvm - -threaded - -funfolding-use-threshold1000 - -funfolding-keeness-factor1000 diff --git a/apecs/bench/Main.hs b/apecs/bench/Main.hs deleted file mode 100644 index 04d49a2..0000000 --- a/apecs/bench/Main.hs +++ /dev/null @@ -1,43 +0,0 @@ -{-# LANGUAGE DataKinds #-} -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE Strict #-} -{-# LANGUAGE TemplateHaskell #-} -{-# LANGUAGE TypeFamilies #-} -{-# LANGUAGE TypeOperators #-} - -import Control.Monad -import Criterion -import qualified Criterion.Main as C -import Criterion.Types -import Linear - -import Apecs - --- pos_vel -newtype ECSPos = ECSPos (V2 Float) deriving (Eq, Show) -instance Component ECSPos where type Storage ECSPos = Cache 10000 (Map ECSPos) - -newtype ECSVel = ECSVel (V2 Float) deriving (Eq, Show) -instance Component ECSVel where type Storage ECSVel = Cache 1000 (Map ECSVel) - -makeWorld "PosVel" [''ECSPos, ''ECSVel] - -posVelInit :: System PosVel () -posVelInit = do - replicateM_ 1000 $ newEntity (ECSPos 0, ECSVel 1) - replicateM_ 9000 $ newEntity (ECSPos 0) - -posVelStep :: System PosVel () -posVelStep = cmap $ \(ECSVel v, ECSPos p) -> ECSPos (p+v) - -main :: IO () -main = C.defaultMainWith (C.defaultConfig {timeLimit = 10}) - [ bgroup "pos_vel" - [ bench "init" $ whnfIO (initPosVel >>= runSystem posVelInit) - , bench "step" $ whnfIO (initPosVel >>= runSystem (posVelInit >> posVelStep)) - ] - ] - diff --git a/apecs/bench/chart.png b/apecs/bench/chart.png deleted file mode 100644 index 80527bb..0000000 Binary files a/apecs/bench/chart.png and /dev/null differ diff --git a/apecs/prepub.pdf b/apecs/prepub.pdf deleted file mode 100644 index ceae8a2..0000000 Binary files a/apecs/prepub.pdf and /dev/null differ diff --git a/apecs/src/Apecs.hs b/apecs/src/Apecs.hs deleted file mode 100644 index b83ddc3..0000000 --- a/apecs/src/Apecs.hs +++ /dev/null @@ -1,39 +0,0 @@ -{-| -This module forms the apecs Prelude. -It selectively re-exports the user-facing functions from the submodules. --} -module Apecs ( - -- * Core types - SystemT(..), System, Component(..), Entity(..), Has(..), Not(..), - Get, Set, Destroy, Members, - - -- * Stores - Map, Unique, Global, Cache, - explInit, - - -- * Systems - get, set, ($=), - destroy, exists, - modify, ($~), - cmap, cmapM, cmapM_, - cfold, cfoldM, cfoldM_, - - -- * Other - runSystem, runWith, - runGC, EntityCounter, newEntity, global, - makeWorld, makeWorldAndComponents, - - -- * Re-exports - asks, ask, liftIO, lift, Proxy (..) -) where - -import Control.Monad.IO.Class (liftIO) -import Control.Monad.Reader (ask, asks, lift) -import Data.Proxy - -import Apecs.Components -import Apecs.Core -import Apecs.Stores -import Apecs.System -import Apecs.TH -import Apecs.Util diff --git a/apecs/src/Apecs/Components.hs b/apecs/src/Apecs/Components.hs deleted file mode 100644 index b1aa1ae..0000000 --- a/apecs/src/Apecs/Components.hs +++ /dev/null @@ -1,203 +0,0 @@ -{-# OPTIONS_GHC -Wno-orphans #-} - -{-# LANGUAGE ConstraintKinds #-} -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE GeneralizedNewtypeDeriving #-} -{-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE RankNTypes #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE StandaloneDeriving #-} -{-# LANGUAGE TemplateHaskell #-} -{-# LANGUAGE TypeFamilies #-} - -module Apecs.Components where - -import Data.Functor.Identity - -import Apecs.Core -import qualified Apecs.THTuples as T - --- | Identity component. @Identity c@ is equivalent to @c@, so mostly useless. -instance Component c => Component (Identity c) where - type Storage (Identity c) = Identity (Storage c) - -instance Has w m c => Has w m (Identity c) where - {-# INLINE getStore #-} - getStore = Identity <$> getStore - -type instance Elem (Identity s) = Identity (Elem s) - -instance ExplGet m s => ExplGet m (Identity s) where - {-# INLINE explGet #-} - explGet (Identity s) e = Identity <$> explGet s e - {-# INLINE explExists #-} - explExists (Identity s) = explExists s - -instance ExplSet m s => ExplSet m (Identity s) where - {-# INLINE explSet #-} - explSet (Identity s) e (Identity x) = explSet s e x -instance ExplMembers m s => ExplMembers m (Identity s) where - {-# INLINE explMembers #-} - explMembers (Identity s) = explMembers s -instance ExplDestroy m s => ExplDestroy m (Identity s) where - {-# INLINE explDestroy #-} - explDestroy (Identity s) = explDestroy s - -T.makeInstances [2..8] - --- | Pseudocomponent indicating the absence of @a@. --- Mainly used as e.g. @cmap $ \(a, Not b) -> c@ to iterate over entities with an @a@ but no @b@. --- Can also be used to delete components, like @cmap $ \a -> (Not :: Not a)@ to delete every @a@ component. -data Not a = Not - --- | Pseudostore used to produce values of type @Not a@, inverts @explExists@, and destroys instead of @explSet@. -newtype NotStore s = NotStore s - -instance Component c => Component (Not c) where - type Storage (Not c) = NotStore (Storage c) - -instance (Has w m c) => Has w m (Not c) where - {-# INLINE getStore #-} - getStore = NotStore <$> getStore - -type instance Elem (NotStore s) = Not (Elem s) - -instance ExplGet m s => ExplGet m (NotStore s) where - {-# INLINE explGet #-} - explGet _ _ = return Not - {-# INLINE explExists #-} - explExists (NotStore sa) ety = not <$> explExists sa ety - -instance ExplDestroy m s => ExplSet m (NotStore s) where - {-# INLINE explSet #-} - explSet (NotStore sa) ety _ = explDestroy sa ety - --- | Pseudostore used to produce values of type @Maybe a@. --- Will always return @True@ for @explExists@. --- Writing can both set and delete a component using @Just@ and @Nothing@ respectively. -newtype MaybeStore s = MaybeStore s -instance Component c => Component (Maybe c) where - type Storage (Maybe c) = MaybeStore (Storage c) - -instance (Has w m c) => Has w m (Maybe c) where - {-# INLINE getStore #-} - getStore = MaybeStore <$> getStore - -type instance Elem (MaybeStore s) = Maybe (Elem s) - -instance ExplGet m s => ExplGet m (MaybeStore s) where - {-# INLINE explGet #-} - explGet (MaybeStore sa) ety = do - e <- explExists sa ety - if e then Just <$> explGet sa ety - else return Nothing - explExists _ _ = return True - -instance (ExplDestroy m s, ExplSet m s) => ExplSet m (MaybeStore s) where - {-# INLINE explSet #-} - explSet (MaybeStore sa) ety Nothing = explDestroy sa ety - explSet (MaybeStore sa) ety (Just x) = explSet sa ety x - --- | Used for 'Either', a logical disjunction between two components. --- As expected, Either is used to model error values. --- Getting an @Either a b@ will first attempt to get a @b@ and return it as @Right b@, or if it does not exist, get an @a@ as @Left a@. --- Can also be used to set one of two things. -data EitherStore sa sb = EitherStore sa sb -instance (Component ca, Component cb) => Component (Either ca cb) where - type Storage (Either ca cb) = EitherStore (Storage ca) (Storage cb) - -instance (Has w m ca, Has w m cb) => Has w m (Either ca cb) where - {-# INLINE getStore #-} - getStore = EitherStore <$> getStore <*> getStore - -type instance Elem (EitherStore sa sb) = Either (Elem sa) (Elem sb) - -instance (ExplGet m sa, ExplGet m sb) => ExplGet m (EitherStore sa sb) where - {-# INLINE explGet #-} - explGet (EitherStore sa sb) ety = do - e <- explExists sb ety - if e then Right <$> explGet sb ety - else Left <$> explGet sa ety - {-# INLINE explExists #-} - explExists (EitherStore sa sb) ety = do - e <- explExists sb ety - if e then return True - else explExists sa ety - -instance (ExplSet m sa, ExplSet m sb) => ExplSet m (EitherStore sa sb) where - {-# INLINE explSet #-} - explSet (EitherStore _ sb) ety (Right b) = explSet sb ety b - explSet (EitherStore sa _) ety (Left a) = explSet sa ety a - -instance (ExplDestroy m sa, ExplDestroy m sb) - => ExplDestroy m (EitherStore sa sb) where - {-# INLINE explDestroy #-} - explDestroy (EitherStore sa sb) ety = - explDestroy sa ety >> explDestroy sb ety - --- Unit instances () -instance Monad m => Has w m () where - {-# INLINE getStore #-} - getStore = return () -instance Component () where - type Storage () = () -type instance Elem () = () -instance Monad m => ExplGet m () where - {-# INLINE explExists #-} - explExists _ _ = return True - {-# INLINE explGet #-} - explGet _ _ = return () -instance Monad m => ExplSet m () where - {-# INLINE explSet #-} - explSet _ _ _ = return () -instance Monad m => ExplDestroy m () where - {-# INLINE explDestroy #-} - explDestroy _ _ = return () - --- | Pseudocomponent that functions normally for @explExists@ and @explMembers@, but always return @Filter@ for @explGet@. --- Can be used in cmap as @cmap $ \(Filter :: Filter a) -> b@. --- Since the above can be written more consicely as @cmap $ \(_ :: a) -> b@, it is rarely directly. --- More interestingly, we can define reusable filters like @movables = Filter :: Filter (Position, Velocity)@. --- Note that 'Filter c' is equivalent to 'Not (Not c)'. -data Filter c = Filter deriving (Eq, Show) - --- Pseudostore for 'Filter'. -newtype FilterStore s = FilterStore s - -instance Component c => Component (Filter c) where - type Storage (Filter c) = FilterStore (Storage c) - -instance Has w m c => Has w m (Filter c) where - {-# INLINE getStore #-} - getStore = FilterStore <$> getStore - -type instance Elem (FilterStore s) = Filter (Elem s) - -instance ExplGet m s => ExplGet m (FilterStore s) where - {-# INLINE explGet #-} - explGet _ _ = return Filter - {-# INLINE explExists #-} - explExists (FilterStore s) ety = explExists s ety - -instance ExplMembers m s => ExplMembers m (FilterStore s) where - {-# INLINE explMembers #-} - explMembers (FilterStore s) = explMembers s - --- | Pseudostore used to produce components of type 'Entity'. --- Always returns @True@ for @explExists@, and echoes back the entity argument for @explGet@. --- Used in e.g. @cmap $ \(a, ety :: Entity) -> b@ to access the current entity. -data EntityStore = EntityStore -instance Component Entity where - type Storage Entity = EntityStore - -instance Monad m => Has w m Entity where - {-# INLINE getStore #-} - getStore = return EntityStore - -type instance Elem EntityStore = Entity -instance Monad m => ExplGet m EntityStore where - {-# INLINE explGet #-} - explGet _ ety = return $ Entity ety - {-# INLINE explExists #-} - explExists _ _ = return True diff --git a/apecs/src/Apecs/Core.hs b/apecs/src/Apecs/Core.hs deleted file mode 100644 index 1d60696..0000000 --- a/apecs/src/Apecs/Core.hs +++ /dev/null @@ -1,81 +0,0 @@ -{-# LANGUAGE ConstraintKinds #-} -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE GeneralizedNewtypeDeriving #-} -{-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE RankNTypes #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE StandaloneDeriving #-} -{-# LANGUAGE TemplateHaskell #-} -{-# LANGUAGE TypeFamilies #-} - -module Apecs.Core where - -import Control.Monad.Catch -import Control.Monad.IO.Class -import Control.Monad.Reader -import qualified Data.Vector.Unboxed as U - --- | An Entity is just an integer, used to index into a component store. --- In general, use @newEntity@, @cmap@, and component tags instead of manipulating these directly. --- --- For performance reasons, negative values like (-1) are reserved for stores to represent special values, so avoid using these. -newtype Entity = Entity {unEntity :: Int} deriving (Num, Eq, Ord, Show, Enum) - --- | A SystemT is a newtype around `ReaderT w m a`, where `w` is the game world variable. --- Systems serve to --- --- * Allow type-based lookup of a component's store through @getStore@. --- --- * Lift side effects into their host Monad. -newtype SystemT w m a = SystemT {unSystem :: ReaderT w m a} deriving (Functor, Monad, Applicative, MonadTrans, MonadIO, MonadThrow, MonadCatch, MonadMask) -type System w a = SystemT w IO a - -deriving instance Monad m => MonadReader w (SystemT w m) - --- | A component is defined by specifying how it is stored. --- The constraint ensures that stores and components are mapped one-to-one. -class (Elem (Storage c) ~ c) => Component c where - type Storage c - --- | @Has w m c@ means that world @w@ can produce a @Storage c@. --- It is parameterized over @m@ to allow stores to be foreign. -class (Monad m, Component c) => Has w m c where - getStore :: SystemT w m (Storage c) - --- | The type of components stored by a store, e.g. @Elem (Map c) = c@. -type family Elem s - --- | Indicates that the store @s@ can be initialized. --- Generally, \"base\" stores like @Map c@ can be initialized, but composite stores like @MaybeStore s@ cannot. -class ExplInit m s where - -- | Initialize a new empty store. - explInit :: m s - --- | Stores that we can read using @explGet@ and @explExists@. --- For some entity @e@, @eplGet s e@ is only guaranteed to be safe if @explExists s e@ returns @True@. -class Monad m => ExplGet m s where - -- | Reads a component from the store. What happens if the component does not exist is left undefined, and might not necessarily crash. - explGet :: s -> Int -> m (Elem s) - -- | Returns whether there is a component for the given index. - explExists :: s -> Int -> m Bool - --- | Stores that can be written. -class Monad m => ExplSet m s where - -- | Writes a component to the store. - explSet :: s -> Int -> Elem s -> m () - --- | Stores that components can be removed from. -class Monad m => ExplDestroy m s where - -- | Destroys the component for a given index. - explDestroy :: s -> Int -> m () - --- | Stores that we can request a list of member entities for. -class Monad m => ExplMembers m s where - -- | Returns an unboxed vector of member indices - explMembers :: s -> m (U.Vector Int) - -type Get w m c = (Has w m c, ExplGet m (Storage c)) -type Set w m c = (Has w m c, ExplSet m (Storage c)) -type Members w m c = (Has w m c, ExplMembers m (Storage c)) -type Destroy w m c = (Has w m c, ExplDestroy m (Storage c)) diff --git a/apecs/src/Apecs/Experimental/Components.hs b/apecs/src/Apecs/Experimental/Components.hs deleted file mode 100644 index d593c64..0000000 --- a/apecs/src/Apecs/Experimental/Components.hs +++ /dev/null @@ -1,54 +0,0 @@ -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE TypeFamilies #-} - -{-| -Stability : experimental - -This module is experimental, and its API might change between point releases. Use at your own risk. ---} -module Apecs.Experimental.Components - ( Redirect (..) - , Head (..) - ) where - -import qualified Data.Vector.Unboxed as U - -import Apecs.Core - --- | Pseudocomponent that when written to, actually writes @c@ to its entity argument. --- Can be used to write to other entities in a 'cmap'. -data Redirect c = Redirect Entity c deriving (Eq, Show) -instance Component c => Component (Redirect c) where - type Storage (Redirect c) = RedirectStore (Storage c) - -newtype RedirectStore s = RedirectStore s -type instance Elem (RedirectStore s) = Redirect (Elem s) - -instance Has w m c => Has w m (Redirect c) where - getStore = RedirectStore <$> getStore - -instance (ExplSet m s) => ExplSet m (RedirectStore s) where - explSet (RedirectStore s) _ (Redirect (Entity ety) c) = explSet s ety c - - --- | Pseudocomponent that can be read like any other component, but will only --- yield a single member when iterated over. Intended to be used as --- @cmap $ Head (...) -> ...@ -newtype Head c = Head c deriving (Eq, Show) -instance Component c => Component (Head c) where - type Storage (Head c) = HeadStore (Storage c) - -newtype HeadStore s = HeadStore s -type instance Elem (HeadStore s) = Head (Elem s) - -instance Has w m c => Has w m (Head c) where - getStore = HeadStore <$> getStore - -instance (ExplGet m s) => ExplGet m (HeadStore s) where - explExists (HeadStore s) ety = explExists s ety - explGet (HeadStore s) ety = Head <$> explGet s ety - -instance (ExplMembers m s) => ExplMembers m (HeadStore s) where - explMembers (HeadStore s) = U.take 1 <$> explMembers s diff --git a/apecs/src/Apecs/Experimental/Reactive.hs b/apecs/src/Apecs/Experimental/Reactive.hs deleted file mode 100644 index 9620894..0000000 --- a/apecs/src/Apecs/Experimental/Reactive.hs +++ /dev/null @@ -1,182 +0,0 @@ -{-| -Stability : experimental - -This module is experimental, and its API might change between point releases. Use at your own risk. - -Adds the @Reactive r s@ store, which when wrapped around store @s@, will call the @react@ on its @r@. - -@Show c => Reactive (Printer c) (Map c)@ will print a message every time a @c@ value is set. - -@Enum c => Reactive (EnumMap c) (Map c)@ allows you to look up entities by component value. -Use e.g. @rget >>= mapLookup True@ to retrieve a list of entities that have a @True@ component. - --} -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE RankNTypes #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE TypeFamilies #-} - -module Apecs.Experimental.Reactive - ( Reacts (..), Reactive, withReactive - , EnumMap, enumLookup - , OrdMap, ordLookup - , IxMap, ixLookup - ) where - -import Control.Monad -import Control.Monad.IO.Class -import Control.Monad.Reader -import qualified Data.Array.IO as A -import qualified Data.IntMap.Strict as IM -import qualified Data.IntSet as S -import Data.IORef -import Data.Ix -import qualified Data.Map.Strict as M - -import Apecs.Components -import Apecs.Core - --- | Class required by @Reactive@. --- Given some @r@ and update information about some component, will run a side-effect in monad @m@. --- Note that there are also instances for @(,)@. -class Monad m => Reacts m r where - rempty :: m r - react :: Entity -> Maybe (Elem r) -> Maybe (Elem r) -> r -> m () - --- | Wrapper for reactivity around some store s. -data Reactive r s = Reactive r s - -type instance Elem (Reactive r s) = Elem s - --- | Performs an action with a reactive state token. -withReactive :: forall w m r s a. - ( Component (Elem r) - , Has w m (Elem r) - , Storage (Elem r) ~ Reactive r s - ) => (r -> m a) -> SystemT w m a -withReactive f = do - Reactive r (_ :: s) <- getStore - lift$ f r - -instance (Reacts m r, ExplInit m s) => ExplInit m (Reactive r s) where - explInit = liftM2 Reactive rempty explInit - -instance (Reacts m r, ExplSet m s, ExplGet m s, Elem s ~ Elem r) - => ExplSet m (Reactive r s) where - {-# INLINE explSet #-} - explSet (Reactive r s) ety c = do - old <- explGet (MaybeStore s) ety - react (Entity ety) old (Just c) r - explSet s ety c - -instance (Reacts m r, ExplDestroy m s, ExplGet m s, Elem s ~ Elem r) - => ExplDestroy m (Reactive r s) where - {-# INLINE explDestroy #-} - explDestroy (Reactive r s) ety = do - old <- explGet (MaybeStore s) ety - react (Entity ety) old Nothing r - explDestroy s ety - -instance ExplGet m s => ExplGet m (Reactive r s) where - {-# INLINE explExists #-} - explExists (Reactive _ s) = explExists s - {-# INLINE explGet #-} - explGet (Reactive _ s) = explGet s - -instance ExplMembers m s => ExplMembers m (Reactive r s) where - {-# INLINE explMembers #-} - explMembers (Reactive _ s) = explMembers s - --- | Prints a message to stdout every time a component is updated. -data Printer c = Printer -type instance Elem (Printer c) = c - -instance (MonadIO m, Show c) => Reacts m (Printer c) where - {-# INLINE rempty #-} - rempty = return Printer - {-# INLINE react #-} - react (Entity ety) (Just c) Nothing _ = liftIO$ - putStrLn $ "Entity " ++ show ety ++ ": destroyed component " ++ show c - react (Entity ety) Nothing (Just c) _ = liftIO$ - putStrLn $ "Entity " ++ show ety ++ ": created component " ++ show c - react (Entity ety) (Just old) (Just new) _ = liftIO$ - putStrLn $ "Entity " ++ show ety ++ ": update component " ++ show old ++ " to " ++ show new - react _ _ _ _ = return () - --- | Allows you to look up entities by component value. --- Use e.g. @withReactive $ mapLookup True@ to retrieve a list of entities that have a @True@ component. --- Based on an @IntMap IntSet@ internally. -newtype EnumMap c = EnumMap (IORef (IM.IntMap S.IntSet)) - -type instance Elem (EnumMap c) = c -instance (MonadIO m, Enum c) => Reacts m (EnumMap c) where - {-# INLINE rempty #-} - rempty = liftIO$ EnumMap <$> newIORef mempty - {-# INLINE react #-} - react _ Nothing Nothing _ = return () - react (Entity ety) (Just c) Nothing (EnumMap ref) = liftIO$ - modifyIORef' ref (IM.adjust (S.delete ety) (fromEnum c)) - react (Entity ety) Nothing (Just c) (EnumMap ref) = liftIO$ - modifyIORef' ref (IM.insertWith mappend (fromEnum c) (S.singleton ety)) - react (Entity ety) (Just old) (Just new) (EnumMap ref) = liftIO$ do - modifyIORef' ref (IM.adjust (S.delete ety) (fromEnum old)) - modifyIORef' ref (IM.insertWith mappend (fromEnum new) (S.singleton ety)) - -{-# INLINE enumLookup #-} -enumLookup :: (MonadIO m, Enum c) => c -> EnumMap c -> m [Entity] -enumLookup c = \(EnumMap ref) -> do - emap <- liftIO $ readIORef ref - return $ maybe [] (fmap Entity . S.toList) (IM.lookup (fromEnum c) emap) - --- | Allows you to look up entities by component value. --- Based on a @Map c IntSet@ internally -newtype OrdMap c = OrdMap (IORef (M.Map c S.IntSet)) - -type instance Elem (OrdMap c) = c -instance (MonadIO m, Ord c) => Reacts m (OrdMap c) where - {-# INLINE rempty #-} - rempty = liftIO$ OrdMap <$> newIORef mempty - {-# INLINE react #-} - react _ Nothing Nothing _ = return () - react (Entity ety) (Just c) Nothing (OrdMap ref) = liftIO$ - modifyIORef' ref (M.adjust (S.delete ety) c) - react (Entity ety) Nothing (Just c) (OrdMap ref) = liftIO$ - modifyIORef' ref (M.insertWith mappend c (S.singleton ety)) - react (Entity ety) (Just old) (Just new) (OrdMap ref) = liftIO$ do - modifyIORef' ref (M.adjust (S.delete ety) old) - modifyIORef' ref (M.insertWith mappend new (S.singleton ety)) - -{-# INLINE ordLookup #-} -ordLookup :: (MonadIO m, Ord c) => c -> OrdMap c -> m [Entity] -ordLookup c = \(OrdMap ref) -> do - emap <- liftIO $ readIORef ref - return $ maybe [] (fmap Entity . S.toList) (M.lookup c emap) - --- | Allows you to look up entities by component value. --- Based on an @IOArray c IntSet@ internally -newtype IxMap c = IxMap (A.IOArray c S.IntSet) - -{-# INLINE modifyArray #-} -modifyArray :: Ix i => A.IOArray i a -> i -> (a -> a) -> IO () -modifyArray ref ix f = A.readArray ref ix >>= A.writeArray ref ix . f - -type instance Elem (IxMap c) = c -instance (MonadIO m, Ix c, Bounded c) => Reacts m (IxMap c) where - {-# INLINE rempty #-} - rempty = liftIO$ IxMap <$> A.newArray (minBound, maxBound) mempty - {-# INLINE react #-} - react _ Nothing Nothing _ = return () - react (Entity ety) (Just c) Nothing (IxMap ref) = liftIO$ - modifyArray ref c (S.delete ety) - react (Entity ety) Nothing (Just c) (IxMap ref) = liftIO$ - modifyArray ref c (S.insert ety) - react (Entity ety) (Just old) (Just new) (IxMap ref) = liftIO$ do - modifyArray ref old (S.delete ety) - modifyArray ref new (S.insert ety) - -{-# INLINE ixLookup #-} -ixLookup :: (MonadIO m, Ix c) => c -> IxMap c -> m [Entity] -ixLookup c = \(IxMap ref) -> do - liftIO $ fmap Entity . S.toList <$> A.readArray ref c diff --git a/apecs/src/Apecs/Experimental/Stores.hs b/apecs/src/Apecs/Experimental/Stores.hs deleted file mode 100644 index f1d2cca..0000000 --- a/apecs/src/Apecs/Experimental/Stores.hs +++ /dev/null @@ -1,121 +0,0 @@ -{-| -Stability: experimtal - -This module is experimental, and its API might change between point releases. Use at your own risk. --} - -{-# OPTIONS_GHC -fno-warn-name-shadowing #-} -{-# OPTIONS_GHC -fno-warn-unused-imports #-} -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE GeneralizedNewtypeDeriving #-} -{-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE PatternSynonyms #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE TypeFamilies #-} -{-# LANGUAGE UndecidableInstances #-} - -module Apecs.Experimental.Stores - ( Pushdown(..), Stack(..) - ) where - -import Control.Monad.Reader -import Data.Proxy -import Data.Semigroup - -import Apecs.Components (MaybeStore (..)) -import Apecs.Core - --- | Overrides a store to have history/pushdown semantics. --- Setting this store adds a new value on top of the stack. --- Destroying pops the stack. --- You can view the entire stack using the 'Stack' wrapper. -newtype Pushdown s c = Pushdown (s (Stack c)) -newtype Stack c = Stack {getStack :: [c]} deriving (Eq, Show, Functor, Applicative, Monad, Foldable, Monoid, Semigroup) - -type instance Elem (Pushdown s c) = c - -instance (Functor m, ExplInit m (s (Stack c))) => ExplInit m (Pushdown s c) where - explInit = Pushdown <$> explInit - -pattern StackList :: c -> [c] -> Maybe (Stack c) -pattern StackList x xs = Just (Stack (x:xs)) - -instance - ( Monad m - , ExplGet m (s (Stack c)) - , Elem (s (Stack c)) ~ Stack c - ) => ExplGet m (Pushdown s c) where - explExists (Pushdown s) ety = f <$> explGet (MaybeStore s) ety - where - f (StackList _ _) = True - f _ = False - explGet (Pushdown s) ety = head . getStack <$> explGet s ety - -instance - ( Monad m - , ExplGet m (s (Stack c)) - , ExplSet m (s (Stack c)) - , Elem (s (Stack c)) ~ Stack c - ) => ExplSet m (Pushdown s c) where - explSet (Pushdown s) ety c = do - ms <- explGet (MaybeStore s) ety - let tail (StackList _ cs) = cs - tail _ = [] - explSet s ety (Stack (c:tail ms)) - -instance - ( Monad m - , ExplGet m (s (Stack c)) - , ExplSet m (s (Stack c)) - , ExplDestroy m (s (Stack c)) - , Elem (s (Stack c)) ~ Stack c - ) => ExplDestroy m (Pushdown s c) where - explDestroy (Pushdown s) ety = do - mscs <- explGet (MaybeStore s) ety - case mscs of - StackList _ cs' -> explSet s ety (Stack cs') - _ -> explDestroy s ety - -instance - ( Monad m - , ExplMembers m (s (Stack c)) - , Elem (s (Stack c)) ~ Stack c - ) => ExplMembers m (Pushdown s c) where - explMembers (Pushdown s) = explMembers s - -instance (Storage c ~ Pushdown s c, Component c) => Component (Stack c) where - type Storage (Stack c) = StackStore (Storage c) - -newtype StackStore s = StackStore s -type instance Elem (StackStore s) = Stack (Elem s) - -instance (Storage c ~ Pushdown s c, Has w m c) => Has w m (Stack c) where - getStore = StackStore <$> getStore - -instance - ( Elem (s (Stack c)) ~ Stack c - , ExplGet m (s (Stack c)) - ) => ExplGet m (StackStore (Pushdown s c)) where - explExists (StackStore s) = explExists s - explGet (StackStore (Pushdown s)) = explGet s - -instance - ( Elem (s (Stack c)) ~ Stack c - , ExplSet m (s (Stack c)) - , ExplDestroy m (s (Stack c)) - ) => ExplSet m (StackStore (Pushdown s c)) where - explSet (StackStore (Pushdown s)) ety (Stack []) = explDestroy s ety - explSet (StackStore (Pushdown s)) ety st = explSet s ety st - -instance - ( Elem (s (Stack c)) ~ Stack c - , ExplDestroy m (s (Stack c)) - ) => ExplDestroy m (StackStore (Pushdown s c)) where - explDestroy (StackStore (Pushdown s)) = explDestroy s - -instance - ( Elem (s (Stack c)) ~ Stack c - , ExplMembers m (s (Stack c)) - ) => ExplMembers m (StackStore (Pushdown s c)) where - explMembers (StackStore (Pushdown s)) = explMembers s diff --git a/apecs/src/Apecs/Experimental/Util.hs b/apecs/src/Apecs/Experimental/Util.hs deleted file mode 100644 index 72ce143..0000000 --- a/apecs/src/Apecs/Experimental/Util.hs +++ /dev/null @@ -1,63 +0,0 @@ -{-| -Stability : experimental - -This module is experimental, and its API might change between point releases. Use at your own risk. ---} -module Apecs.Experimental.Util - ( -- * Spatial hashing - -- $hash - quantize, flatten, inbounds, region, flatten', - ) where - -import Control.Applicative (liftA2) - --- $hash --- The following are helper functions for spatial hashing. --- Your spatial hash is defined by two vectors; --- --- - The cell size vector contains real components and dictates --- how large each cell in your table is in world space units. --- It is used by @quantize@ to translate a world space coordinate into a table space index vector --- - The table size vector contains integral components and dictates how --- many cells your field consists of in each direction. --- It is used by @flatten@ to translate a table-space index vector into a flat integer - --- | Quantize turns a world-space coordinate into a table-space coordinate by dividing --- by the given cell size and rounding towards negative infinity. -{-# INLINE quantize #-} -quantize :: (Fractional (v a), Integral b, RealFrac a, Functor v) - => v a -- ^ Quantization cell size - -> v a -- ^ Vector to be quantized - -> v b -quantize cell vec = floor <$> vec/cell - --- | Turns a table-space vector into an integral index, given some table size vector. --- Yields Nothing for out-of-bounds queries -{-# INLINE flatten #-} -flatten :: (Applicative v, Integral a, Foldable v) - => v a -- Field size vector - -> v a -> Maybe a -flatten size vec = if inbounds size vec then Just (flatten' size vec) else Nothing - --- | Tests whether a vector is in the region given by 0 and the size vector (inclusive) -{-# INLINE inbounds #-} -inbounds :: (Num a, Ord a, Applicative v, Foldable v) - => v a -- Field size vector - -> v a -> Bool -inbounds size vec = and (liftA2 (\v s -> v >= 0 && v <= s) vec size) - --- | For two table-space vectors indicating a region's bounds, gives a list of the vectors contained between them. --- This is useful for querying a spatial hash. -{-# INLINE region #-} -region :: (Enum a, Applicative v, Traversable v) - => v a -- ^ Lower bound for the region - -> v a -- ^ Higher bound for the region - -> [v a] -region a b = sequence $ liftA2 enumFromTo a b - --- | flatten, but yields garbage for out-of-bounds vectors. -{-# INLINE flatten' #-} -flatten' :: (Applicative v, Integral a, Foldable v) - => v a -- Field size vector - -> v a -> a -flatten' size vec = foldr (\(n,x) acc -> n*acc + x) 0 (liftA2 (,) size vec) diff --git a/apecs/src/Apecs/Stores.hs b/apecs/src/Apecs/Stores.hs deleted file mode 100644 index 4a34625..0000000 --- a/apecs/src/Apecs/Stores.hs +++ /dev/null @@ -1,247 +0,0 @@ -{-# LANGUAGE DataKinds #-} -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE KindSignatures #-} -{-# LANGUAGE LambdaCase #-} -{-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE Strict #-} -{-# LANGUAGE TypeApplications #-} -{-# LANGUAGE TypeFamilies #-} - -module Apecs.Stores - ( Map, Cache, Unique, - Global, - Cachable, - ReadOnly, setReadOnly, destroyReadOnly - -- Register, regLookup - ) where - -import Control.Monad -import Control.Monad.IO.Class -import Control.Monad.Reader -import Data.Bits (shiftL, (.&.)) -import qualified Data.IntMap.Strict as M -import Data.IORef -import Data.Proxy -import Data.Typeable (Typeable, typeRep) -import qualified Data.Vector.Mutable as VM -import qualified Data.Vector.Unboxed as U -import qualified Data.Vector.Unboxed.Mutable as UM -import GHC.TypeLits - -import Apecs.Core - --- | A map based on 'Data.IntMap.Strict'. O(log(n)) for most operations. -newtype Map c = Map (IORef (M.IntMap c)) - -type instance Elem (Map c) = c -instance MonadIO m => ExplInit m (Map c) where - explInit = liftIO$ Map <$> newIORef mempty - -instance (MonadIO m, Typeable c) => ExplGet m (Map c) where - explExists (Map ref) ety = liftIO$ M.member ety <$> readIORef ref - explGet (Map ref) ety = liftIO$ flip fmap (M.lookup ety <$> readIORef ref) $ \case - Just c -> c - notFound -> error $ unwords - [ "Reading non-existent Map component" - , show (typeRep notFound) - , "for entity" - , show ety - ] - {-# INLINE explExists #-} - {-# INLINE explGet #-} - -instance MonadIO m => ExplSet m (Map c) where - {-# INLINE explSet #-} - explSet (Map ref) ety x = liftIO$ - modifyIORef' ref (M.insert ety x) - -instance MonadIO m => ExplDestroy m (Map c) where - {-# INLINE explDestroy #-} - explDestroy (Map ref) ety = liftIO$ - readIORef ref >>= writeIORef ref . M.delete ety - -instance MonadIO m => ExplMembers m (Map c) where - {-# INLINE explMembers #-} - explMembers (Map ref) = liftIO$ U.fromList . M.keys <$> readIORef ref - --- | A Unique contains zero or one component. --- Writing to it overwrites both the previous component and its owner. --- Its main purpose is to be a 'Map' optimized for when only ever one component inhabits it. -newtype Unique c = Unique (IORef (Maybe (Int, c))) -type instance Elem (Unique c) = c -instance MonadIO m => ExplInit m (Unique c) where - explInit = liftIO$ Unique <$> newIORef Nothing - -instance (MonadIO m, Typeable c) => ExplGet m (Unique c) where - {-# INLINE explGet #-} - explGet (Unique ref) _ = liftIO$ flip fmap (readIORef ref) $ \case - Just (_, c) -> c - notFound -> error $ unwords - [ "Reading non-existent Unique component" - , show (typeRep notFound) - ] - - {-# INLINE explExists #-} - explExists (Unique ref) ety = liftIO$ maybe False ((==ety) . fst) <$> readIORef ref - -instance MonadIO m => ExplSet m (Unique c) where - {-# INLINE explSet #-} - explSet (Unique ref) ety c = liftIO$ writeIORef ref (Just (ety, c)) - -instance MonadIO m => ExplDestroy m (Unique c) where - {-# INLINE explDestroy #-} - explDestroy (Unique ref) ety = liftIO$ readIORef ref >>= - mapM_ (flip when (writeIORef ref Nothing) . (==ety) . fst) - -instance MonadIO m => ExplMembers m (Unique c) where - {-# INLINE explMembers #-} - explMembers (Unique ref) = liftIO$ flip fmap (readIORef ref) $ \case - Nothing -> mempty - Just (ety, _) -> U.singleton ety - --- | A 'Global' contains exactly one component. --- The initial value is 'mempty' from the component's 'Monoid' instance. --- Querying a 'Global' at /any/ Entity yields this one component, effectively sharing the component between /all/ entities. --- --- A Global component can be read with @'get' 0@ or @'get' 1@ or even @'get' undefined@. --- The convenience entity 'global' is defined as -1, and can be used to make operations on a global more explicit, i.e. 'Time t <- get global'. --- --- You also can read and write Globals during a 'cmap' over other components. -newtype Global c = Global (IORef c) -type instance Elem (Global c) = c -instance (Monoid c, MonadIO m) => ExplInit m (Global c) where - {-# INLINE explInit #-} - explInit = liftIO$ Global <$> newIORef mempty - -instance MonadIO m => ExplGet m (Global c) where - {-# INLINE explGet #-} - explGet (Global ref) _ = liftIO$ readIORef ref - {-# INLINE explExists #-} - explExists _ _ = return True - -instance MonadIO m => ExplSet m (Global c) where - {-# INLINE explSet #-} - explSet (Global ref) _ c = liftIO$ writeIORef ref c - --- | Class of stores that behave like a regular map, and can therefore safely be cached. --- This prevents stores like `Unique` and 'Global', which do /not/ behave like simple maps, from being cached. -class Cachable s -instance Cachable (Map s) -instance (KnownNat n, Cachable s) => Cachable (Cache n s) - --- | A cache around another store. --- Caches store their members in a fixed-size vector, so read/write operations become O(1). --- Caches can provide huge performance boosts, especially when working with large numbers of components. --- --- The cache size is given as a type-level argument. --- --- Note that iterating over a cache is linear in cache size, so sparsely populated caches might /decrease/ performance. --- In general, the exact size of the cache does not matter as long as it reasonably approximates the number of components present. --- --- The cache uses entity (-2) internally to represent missing entities. --- If you manually manipulate Entity values, be careful that you do not use (-2) --- --- The actual cache is not necessarily the given argument, but the next biggest power of two. --- This is allows most operations to be expressed as bit masks, for a large potential performance boost. -data Cache (n :: Nat) s = - Cache Int (UM.IOVector Int) (VM.IOVector (Elem s)) s - -cacheMiss :: t -cacheMiss = error "Cache miss! If you are seeing this during normal operation, please open a bug report at https://github.com/jonascarpay/apecs" - -type instance Elem (Cache n s) = Elem s - -instance (MonadIO m, ExplInit m s, KnownNat n, Cachable s) => ExplInit m (Cache n s) where - {-# INLINE explInit #-} - explInit = do - let n = fromIntegral$ natVal (Proxy @n) :: Int - size = head . dropWhile ( ExplGet m (Cache n s) where - {-# INLINE explGet #-} - explGet (Cache mask tags cache s) ety = do - let index = ety .&. mask - tag <- liftIO$ UM.unsafeRead tags index - if tag == ety - then liftIO$ VM.unsafeRead cache index - else explGet s ety - - {-# INLINE explExists #-} - explExists (Cache mask tags _ s) ety = do - tag <- liftIO$ UM.unsafeRead tags (ety .&. mask) - if tag == ety then return True else explExists s ety - -instance (MonadIO m, ExplSet m s) => ExplSet m (Cache n s) where - {-# INLINE explSet #-} - explSet (Cache mask tags cache s) ety x = do - let index = ety .&. mask - tag <- liftIO$ UM.unsafeRead tags index - when (tag /= (-2) && tag /= ety) $ do - cached <- liftIO$ VM.unsafeRead cache index - explSet s tag cached - liftIO$ UM.unsafeWrite tags index ety - liftIO$ VM.unsafeWrite cache index x - -instance (MonadIO m, ExplDestroy m s) => ExplDestroy m (Cache n s) where - {-# INLINE explDestroy #-} - explDestroy (Cache mask tags cache s) ety = do - let index = ety .&. mask - tag <- liftIO$ UM.unsafeRead tags (ety .&. mask) - when (tag == ety) $ liftIO $ do - UM.unsafeWrite tags index (-2) - VM.unsafeWrite cache index cacheMiss - explDestroy s ety - -instance (MonadIO m, ExplMembers m s) => ExplMembers m (Cache n s) where - {-# INLINE explMembers #-} - explMembers (Cache mask tags _ s) = do - cached <- liftIO$ U.filter (/= (-2)) <$> U.freeze tags - let etyFilter ety = (/= ety) <$> UM.unsafeRead tags (ety .&. mask) - stored <- explMembers s >>= liftIO . U.filterM etyFilter - return $! cached U.++ stored - --- | Wrapper that makes a store read-only by hiding its 'ExplSet' and 'ExplDestroy' instances. --- This is primarily used to protect the 'EntityCounter' from accidental overwrites. --- Use 'setReadOnly' and 'destroyReadOnly' to override. -newtype ReadOnly s = ReadOnly s -type instance Elem (ReadOnly s) = Elem s - -instance (Functor m, ExplInit m s) => ExplInit m (ReadOnly s) where - explInit = ReadOnly <$> explInit - -instance ExplGet m s => ExplGet m (ReadOnly s) where - explExists (ReadOnly s) = explExists s - explGet (ReadOnly s) = explGet s - {-# INLINE explExists #-} - {-# INLINE explGet #-} - -instance ExplMembers m s => ExplMembers m (ReadOnly s) where - {-# INLINE explMembers #-} - explMembers (ReadOnly s) = explMembers s - -setReadOnly :: forall w m s c. - ( Has w m c - , Storage c ~ ReadOnly s - , Elem s ~ c - , ExplSet m s - ) => Entity -> c -> SystemT w m () -setReadOnly (Entity ety) c = do - ReadOnly s <- getStore - lift $ explSet s ety c - -destroyReadOnly :: forall w m s c. - ( Has w m c - , Storage c ~ ReadOnly s - , Elem s ~ c - , ExplDestroy m s - ) => Entity -> Proxy c -> SystemT w m () -destroyReadOnly (Entity ety) _ = do - ReadOnly s :: Storage c <- getStore - lift $ explDestroy s ety diff --git a/apecs/src/Apecs/System.hs b/apecs/src/Apecs/System.hs deleted file mode 100644 index f210207..0000000 --- a/apecs/src/Apecs/System.hs +++ /dev/null @@ -1,163 +0,0 @@ -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE Strict #-} - -module Apecs.System where - -import Control.Monad -import Control.Monad.Reader -import Data.Proxy -import qualified Data.Vector.Unboxed as U - -import Apecs.Components () -import Apecs.Core - --- | Run a system in a game world -{-# INLINE runSystem #-} -runSystem :: SystemT w m a -> w -> m a -runSystem sys = runReaderT (unSystem sys) - --- | Run a system in a game world -{-# INLINE runWith #-} -runWith :: w -> SystemT w m a -> m a -runWith = flip runSystem - --- | Read a Component -{-# INLINE get #-} -get :: forall w m c. Get w m c => Entity -> SystemT w m c -get (Entity ety) = do - s :: Storage c <- getStore - lift$ explGet s ety - --- | Writes a Component to a given Entity. Will overwrite existing Components. -{-# INLINE set #-} -set, ($=) :: forall w m c. Set w m c => Entity -> c -> SystemT w m () -set (Entity ety) x = do - s :: Storage c <- getStore - lift$ explSet s ety x - --- | @set@ operator -($=) = set -infixr 2 $= - --- | Returns whether the given entity has component @c@ -{-# INLINE exists #-} -exists :: forall w m c. Get w m c => Entity -> Proxy c -> SystemT w m Bool -exists (Entity ety) _ = do - s :: Storage c <- getStore - lift$ explExists s ety - --- | Destroys component @c@ for the given entity. -{-# INLINE destroy #-} -destroy :: forall w m c. Destroy w m c => Entity -> Proxy c -> SystemT w m () -destroy (Entity ety) ~_ = do - s :: Storage c <- getStore - lift$ explDestroy s ety - --- | Applies a function, if possible. -{-# INLINE modify #-} -modify, ($~) :: forall w m cx cy. (Get w m cx, Set w m cy) => Entity -> (cx -> cy) -> SystemT w m () -modify (Entity ety) f = do - sx :: Storage cx <- getStore - sy :: Storage cy <- getStore - lift$ do - possible <- explExists sx ety - when possible $ do - x <- explGet sx ety - explSet sy ety (f x) - --- | @modify@ operator -($~) = modify -infixr 2 $~ - --- | Maps a function over all entities with a @cx@, and writes their @cy@. -{-# INLINE cmap #-} -cmap :: forall w m cx cy. (Get w m cx, Members w m cx, Set w m cy) - => (cx -> cy) -> SystemT w m () -cmap f = do - sx :: Storage cx <- getStore - sy :: Storage cy <- getStore - lift$ do - sl <- explMembers sx - U.forM_ sl $ \ e -> do - r <- explGet sx e - explSet sy e (f r) - --- | Conditional @cmap@, that first tests whether the argument satisfies some property. --- The entity needs to have both a cx and cp component. -{-# INLINE cmapIf #-} -cmapIf :: forall w m cp cx cy. - ( Get w m cx - , Get w m cp - , Members w m cx - , Set w m cy ) - => (cp -> Bool) - -> (cx -> cy) - -> SystemT w m () -cmapIf cond f = do - sp :: Storage cp <- getStore - sx :: Storage cx <- getStore - sy :: Storage cy <- getStore - lift$ do - sl <- explMembers (sx,sp) - U.forM_ sl $ \ e -> do - p <- explGet sp e - when (cond p) $ do - x <- explGet sx e - explSet sy e (f x) - --- | Monadically iterates over all entites with a @cx@, and writes their @cy@. -{-# INLINE cmapM #-} -cmapM :: forall w m cx cy. (Get w m cx, Set w m cy, Members w m cx) - => (cx -> SystemT w m cy) -> SystemT w m () -cmapM sys = do - sx :: Storage cx <- getStore - sy :: Storage cy <- getStore - sl <- lift$ explMembers sx - U.forM_ sl $ \ e -> do - x <- lift$ explGet sx e - y <- sys x - lift$ explSet sy e y - --- | Monadically iterates over all entites with a @cx@ -{-# INLINE cmapM_ #-} -cmapM_ :: forall w m c. (Get w m c, Members w m c) - => (c -> SystemT w m ()) -> SystemT w m () -cmapM_ sys = do - s :: Storage c <- getStore - sl <- lift$ explMembers s - U.forM_ sl $ \ ety -> do - x <- lift$ explGet s ety - sys x - --- | Fold over the game world; for example, @cfold max (minBound :: Foo)@ will find the maximum value of @Foo@. --- Strict in the accumulator. -{-# INLINE cfold #-} -cfold :: forall w m c a. (Members w m c, Get w m c) - => (a -> c -> a) -> a -> SystemT w m a -cfold f a0 = do - s :: Storage c <- getStore - sl <- lift$ explMembers s - lift$ U.foldM' (\a e -> f a <$> explGet s e) a0 sl - --- | Monadically fold over the game world. --- Strict in the accumulator. -{-# INLINE cfoldM #-} -cfoldM :: forall w m c a. (Members w m c, Get w m c) - => (a -> c -> SystemT w m a) -> a -> SystemT w m a -cfoldM sys a0 = do - s :: Storage c <- getStore - sl <- lift$ explMembers s - U.foldM' (\a e -> lift (explGet s e) >>= sys a) a0 sl - --- | Monadically fold over the game world. --- Strict in the accumulator. -{-# INLINE cfoldM_ #-} -cfoldM_ :: forall w m c a. (Members w m c, Get w m c) - => (a -> c -> SystemT w m a) -> a -> SystemT w m () -cfoldM_ sys a0 = do - s :: Storage c <- getStore - sl <- lift$ explMembers s - U.foldM'_ (\a e -> lift (explGet s e) >>= sys a) a0 sl diff --git a/apecs/src/Apecs/TH.hs b/apecs/src/Apecs/TH.hs deleted file mode 100644 index 0fa98be..0000000 --- a/apecs/src/Apecs/TH.hs +++ /dev/null @@ -1,90 +0,0 @@ -{-# LANGUAGE TemplateHaskell #-} -{-# LANGUAGE TypeFamilies #-} - -module Apecs.TH - ( makeWorld - , makeWorldNoEC - , makeWorldAndComponents - , makeMapComponents - ) where - -import Control.Monad -import Language.Haskell.TH - -import Apecs.Core -import Apecs.Stores -import Apecs.Util (EntityCounter) - -genName :: String -> Q Name -genName s = mkName . show <$> newName s - --- | Same as 'makeWorld', but does not include an 'EntityCounter' --- You don't typically want to use this, but it's exposed in case you know what you're doing. -makeWorldNoEC :: String -> [Name] -> Q [Dec] -makeWorldNoEC worldName cTypes = do - cTypesNames <- forM cTypes $ \t -> do - rec <- genName "rec" - return (ConT t, rec) - - let wld = mkName worldName - has = mkName "Has" - sys = mkName "SystemT" - m = VarT $ mkName "m" - wldDecl = DataD [] wld [] Nothing [RecC wld records] [] - - makeRecord (t,n) = (n, Bang NoSourceUnpackedness SourceStrict, ConT (mkName "Storage") `AppT` t) - records = makeRecord <$> cTypesNames - - makeInstance (t,n) = - InstanceD Nothing [ConT (mkName "Monad") `AppT` m] (ConT has `AppT` ConT wld `AppT` m `AppT` t) - [ FunD (mkName "getStore") [Clause [] - (NormalB$ ConE sys `AppE` (VarE (mkName "asks") `AppE` VarE n)) - [] ] - ] - - initWorldName = mkName $ "init" ++ worldName - initSig = SigD initWorldName (AppT (ConT (mkName "IO")) (ConT wld)) - initDecl = FunD initWorldName [Clause [] - (NormalB$ iterate (\wE -> AppE (AppE (VarE $ mkName "<*>") wE) (VarE $ mkName "explInit")) (AppE (VarE $ mkName "return") (ConE wld)) !! length records) - [] ] - - hasDecl = makeInstance <$> cTypesNames - - return $ wldDecl : initSig : initDecl : hasDecl - --- | Creates 'Component' instances with 'Map' stores -makeMapComponents :: [Name] -> Q [Dec] -makeMapComponents = mapM makeMapComponent - -makeMapComponent :: Name -> Q Dec -makeMapComponent comp = do - let ct = return$ ConT comp - head <$> [d| instance Component $ct where type Storage $ct = Map $ct |] - --- | Calls 'makeWorld' and 'makeMapComponents', i.e. makes a world and also defines 'Component' instances with a 'Map' store. -makeWorldAndComponents :: String -> [Name] -> Q [Dec] -makeWorldAndComponents worldName cTypes = do - wdecls <- makeWorld worldName cTypes - cdecls <- makeMapComponents cTypes - return $ wdecls ++ cdecls - -{-| - -The typical way to create a @world@ record, associated 'Has' instances, and initialization function. - -> makeWorld "MyWorld" [''Component1, ''Component2, ...] - -turns into - -> data MyWorld = MyWorld Component1 Component2 ... EntityCounter -> instance MyWorld `Has` Component1 where ... -> instance MyWorld `Has` Component2 where ... -> ... -> instance MyWorld `Has` EntityCounter where ... -> -> initMyWorld :: IO MyWorld -> initMyWorld = MyWorld <$> initStore <*> initStore <*> ... <*> initStore - --} -makeWorld :: String -> [Name] -> Q [Dec] -makeWorld worldName cTypes = makeWorldNoEC worldName (cTypes ++ [''EntityCounter]) diff --git a/apecs/src/Apecs/THTuples.hs b/apecs/src/Apecs/THTuples.hs deleted file mode 100644 index 4873138..0000000 --- a/apecs/src/Apecs/THTuples.hs +++ /dev/null @@ -1,167 +0,0 @@ -{-# LANGUAGE TemplateHaskell #-} -{-# LANGUAGE CPP #-} - -module Apecs.THTuples where - -import qualified Data.Vector.Unboxed as U -import Language.Haskell.TH - -{-- -instance (Component a, Component b) => Component (a, b) where - type Storage (a,b) = (Storage a, Storage b) - -instance (Has w a, Has w b) => Has w (a,b) where - getStore = liftM2 (,) getStore getStore - -type instance Elem (a,b) = (Elem a, Elem b) - -instance (ExplGet a, ExplGet b) => ExplGet (a, b) where - explExists (sa, sb) ety = liftM2 (&&) (explExists sa ety) (explExists sb ety) - explGet (sa, sb) ety = liftM2 (,) (explGet sa ety) (explGet sb ety) - -instance (ExplSet a, ExplSet b) => ExplSet (a, b) where - explSet (sa,sb) ety (a,b) = explSet sa ety a >> explSet sb ety b - -instance (ExplDestroy a, ExplDestroy b) => ExplDestroy (a, b) where - explDestroy (sa, sb) ety = explDestroy sa ety >> explDestroy sb ety - -instance (ExplMembers a, ExplGet b) => ExplMembers (a, b) where - explMembers (sa, sb) = explMembers sa >>= U.filterM (explExists sb) ---} - --- | Generate tuple instances for the following tuple sizes. -makeInstances :: [Int] -> Q [Dec] -makeInstances is = concat <$> traverse tupleInstances is - -tupleInstances :: Int -> Q [Dec] -tupleInstances n = do - let vars = [ VarT . mkName $ "t_" ++ show i | i <- [0..n-1]] - m = VarT $ mkName "m" - - -- [''a,''b] -> ''(a,b) - tupleUpT :: [Type] -> Type - tupleUpT = foldl AppT (TupleT n) - -- ''(t_0, t_1, .. ) - varTuple :: Type - varTuple = tupleUpT vars - - tupleName :: Name - tupleName = tupleDataName n - tuplE :: Exp - tuplE = ConE tupleName - - -- Component - compN = mkName "Component" - compT var = ConT compN `AppT` var - strgN = mkName "Storage" - strgT var = ConT strgN `AppT` var - compI = InstanceD Nothing (fmap compT vars) (compT varTuple) [ -#if MIN_VERSION_template_haskell(2,15,0) - TySynInstD $ TySynEqn Nothing (strgT varTuple) (tupleUpT . fmap strgT $ vars) -#else - TySynInstD strgN $ TySynEqn [varTuple] (tupleUpT . fmap strgT $ vars) -#endif - ] - - -- Has - hasN = mkName "Has" - hasT var = ConT hasN `AppT` VarT (mkName "w") `AppT` m `AppT` var - getStoreN = mkName "getStore" - getStoreE = VarE getStoreN - apN = mkName "<*>" - apE = VarE apN - hasI = InstanceD Nothing (hasT <$> vars) (hasT varTuple) - [ FunD getStoreN - [Clause [] (NormalB$ liftAll tuplE (replicate n getStoreE )) [] ] - , PragmaD$ InlineP getStoreN Inline FunLike AllPhases - ] - - liftAll f mas = foldl (\a x -> AppE (AppE apE a) x) (AppE (VarE (mkName "pure")) f) mas - sequenceAll :: [Exp] -> Exp - sequenceAll = foldl1 (\a x -> AppE (AppE (VarE$ mkName ">>") a) x) - - -- Elem - elemN = mkName "Elem" - elemT var = ConT elemN `AppT` var -#if MIN_VERSION_template_haskell(2,15,0) - elemI = TySynInstD $ TySynEqn Nothing (elemT varTuple) (tupleUpT $ fmap elemT vars) -#else - elemI = TySynInstD elemN $ TySynEqn [varTuple] (tupleUpT $ fmap elemT vars) -#endif - - -- s, ety, w arguments - sNs = [ mkName $ "s_" ++ show i | i <- [0..n-1]] - sPat = ConP tupleName (VarP <$> sNs) - sEs = VarE <$> sNs - etyN = mkName "ety" - etyE = VarE etyN - etyPat = VarP etyN - wNs = [ mkName $ "w_" ++ show i | i <- [0..n-1]] - wPat = ConP tupleName (VarP <$> wNs) - wEs = VarE <$> wNs - - getN = mkName "ExplGet" - setN = mkName "ExplSet" - membersN = mkName "ExplMembers" - destroyN = mkName "ExplDestroy" - - getT s = ConT getN `AppT` m `AppT` s - setT s = ConT setN `AppT` m `AppT` s - membersT s = ConT membersN `AppT` m `AppT` s - destroyT s = ConT destroyN `AppT` m `AppT` s - - explSetN = mkName "explSet" - explDestroyN = mkName "explDestroy" - explExistsN = mkName "explExists" - explMembersN = mkName "explMembers" - explGetN = mkName "explGet" - - explSetE = VarE explSetN - explDestroyE = VarE explDestroyN - explExistsE = VarE explExistsN - explMembersE = VarE explMembersN - explGetE = VarE explGetN - - explSetF sE wE = AppE explSetE sE `AppE` etyE `AppE` wE - explDestroyF sE = AppE explDestroyE sE `AppE` etyE - explExistsF sE = AppE explExistsE sE - explMembersF sE = AppE explMembersE sE - explGetF sE = AppE explGetE sE `AppE` etyE - - explExistsAnd va vb = - AppE (AppE (VarE '(>>=)) va) - (LamCaseE [ Match (ConP 'False []) (NormalB$ AppE (VarE 'return) (ConE 'False)) [] - , Match (ConP 'True []) (NormalB vb) [] - ]) - - explMembersFold va vb = AppE (VarE '(>>=)) va `AppE` AppE (VarE 'U.filterM) vb - - getI = InstanceD Nothing (getT <$> vars) (getT varTuple) - [ FunD explGetN [Clause [sPat, etyPat] - (NormalB$ liftAll tuplE (explGetF <$> sEs)) [] ] - , PragmaD$ InlineP explGetN Inline FunLike AllPhases - - , FunD explExistsN [Clause [sPat, etyPat] - (NormalB$ foldr explExistsAnd (AppE (VarE 'pure) (ConE 'True)) ((`AppE` etyE) . explExistsF <$> sEs)) [] ] - , PragmaD$ InlineP explExistsN Inline FunLike AllPhases - ] - - setI = InstanceD Nothing (setT <$> vars) (setT varTuple) - [ FunD explSetN [Clause [sPat, etyPat, wPat] - (NormalB$ sequenceAll (zipWith explSetF sEs wEs)) [] ] - , PragmaD$ InlineP explSetN Inline FunLike AllPhases - ] - - destroyI = InstanceD Nothing (destroyT <$> vars) (destroyT varTuple) - [ FunD explDestroyN [Clause [sPat, etyPat] - (NormalB$ sequenceAll (explDestroyF <$> sEs)) [] ] - , PragmaD$ InlineP explDestroyN Inline FunLike AllPhases - ] - - membersI = InstanceD Nothing (membersT (head vars) : (getT <$> tail vars)) (membersT varTuple) - [ FunD explMembersN [Clause [sPat] - (NormalB$ foldl explMembersFold (explMembersF (head sEs)) (explExistsF <$> tail sEs)) [] ] - , PragmaD$ InlineP explMembersN Inline FunLike AllPhases - ] - - return [compI, hasI, elemI, getI, setI, destroyI, membersI] diff --git a/apecs/src/Apecs/Util.hs b/apecs/src/Apecs/Util.hs deleted file mode 100644 index 9e868d7..0000000 --- a/apecs/src/Apecs/Util.hs +++ /dev/null @@ -1,59 +0,0 @@ -{-# OPTIONS_GHC -fno-warn-unused-imports #-} -- For Data.Semigroup compatibility - -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE GeneralizedNewtypeDeriving #-} -{-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE Strict #-} -{-# LANGUAGE TypeFamilies #-} - -module Apecs.Util ( - -- * Utility - runGC, global, - - -- * EntityCounter - EntityCounter(..), nextEntity, newEntity, -) where - -import Control.Applicative (liftA2) -import Control.Monad.IO.Class -import Control.Monad.Reader -import Data.Monoid -import Data.Semigroup -import System.Mem (performMajorGC) - -import Apecs.Core -import Apecs.Stores -import Apecs.System - --- | Convenience entity, for use in places where the entity value does not matter, i.e. a global store. -global :: Entity -global = Entity (-1) - --- | Component used by newEntity to track the number of issued entities. --- Automatically added to any world created with @makeWorld@ -newtype EntityCounter = EntityCounter {getCounter :: Sum Int} deriving (Semigroup, Monoid, Eq, Show) - -instance Component EntityCounter where - type Storage EntityCounter = ReadOnly (Global EntityCounter) - --- | Bumps the EntityCounter and yields its value -{-# INLINE nextEntity #-} -nextEntity :: (MonadIO m, Get w m EntityCounter) => SystemT w m Entity -nextEntity = do EntityCounter n <- get global - setReadOnly global (EntityCounter $ n+1) - return (Entity . getSum $ n) - --- | Writes the given components to a new entity, and yields that entity. --- The return value is often ignored. -{-# INLINE newEntity #-} -newEntity :: (MonadIO m, Set w m c, Get w m EntityCounter) - => c -> SystemT w m Entity -newEntity c = do ety <- nextEntity - set ety c - return ety - --- | Explicitly invoke the garbage collector -runGC :: System w () -runGC = lift performMajorGC diff --git a/apecs/test/Main.hs b/apecs/test/Main.hs deleted file mode 100644 index 6053c3b..0000000 --- a/apecs/test/Main.hs +++ /dev/null @@ -1,163 +0,0 @@ -{-# LANGUAGE DataKinds #-} -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE GeneralizedNewtypeDeriving #-} -{-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE TemplateHaskell #-} -{-# LANGUAGE TypeApplications #-} -{-# LANGUAGE TypeFamilies #-} -{-# LANGUAGE UndecidableInstances #-} - -{-# OPTIONS_GHC -w #-} - -import Control.Monad -import qualified Data.IntSet as S -import Data.IORef -import Data.List (sort) -import qualified Data.Vector.Unboxed as U -import Test.QuickCheck -import Test.QuickCheck.Monadic -import Data.List (nub) - -import Apecs -import Apecs.Core -import Apecs.Experimental.Reactive -import Apecs.Experimental.Stores -import Apecs.Stores -import Apecs.Util - -type Vec = (Double, Double) - --- Preamble -instance Arbitrary Entity where - arbitrary = Entity . getNonNegative <$> arbitrary - -assertSys :: IO w -> System w Bool -> Property -assertSys initW sys = monadicIO $ run (initW >>= runSystem sys) >>= assert - -genericSetGet :: forall w c. - ( ExplGet IO (Storage c) - , ExplSet IO (Storage c) - , ExplDestroy IO (Storage c) - , Has w IO c - , Eq c - , Arbitrary c ) - => IO w - -> c - -> [(Entity, c)] -> [Entity] - -> Entity -> c - -> [(Entity, c)] -> [Entity] - -> Property -genericSetGet initSys _ sets1 dels1 ety c sets2 dels2 = do - assertSys initSys $ do - -- insert and delete random data - forM_ sets1 $ uncurry set - forM_ dels1 $ flip destroy (Proxy @c) - set ety c - forM_ (filter ((/= ety) . fst) sets2) $ uncurry set - forM_ (filter (/= ety) dels2) $ flip destroy (Proxy @c) - c' <- get ety - return (c == c') - -genericSetSet :: forall w c. - ( ExplGet IO (Storage c) - , ExplSet IO (Storage c) - , ExplDestroy IO (Storage c) - , Has w IO c - , Eq c - , Arbitrary c ) - => IO w - -> c - -> [(Entity, c)] -> [Entity] - -> Entity -> c - -> [(Entity, c)] -> [Entity] - -> c - -> [(Entity, c)] -> [Entity] - -> Property -genericSetSet initSys _ sets1 dels1 ety c1 sets2 dels2 c2 sets3 dels3 = do - assertSys initSys $ do - -- insert and delete random data - forM_ sets1 $ uncurry set - forM_ dels1 $ flip destroy (Proxy @c) - set ety c1 - forM_ (filter ((/= ety) . fst) sets2) $ uncurry set - forM_ (filter (/= ety) dels2) $ flip destroy (Proxy @c) - set ety c2 - forM_ (filter ((/= ety) . fst) sets3) $ uncurry set - forM_ (filter (/= ety) dels3) $ flip destroy (Proxy @c) - c' <- get ety - return (c2 == c') - --- Tests whether writing and reading gives back the original component -newtype MapInt = MapInt Int deriving (Eq, Show, Arbitrary) -instance Component MapInt where type Storage MapInt = Map MapInt -makeWorld "Simple" [''MapInt] - -prop_setGetMap = genericSetGet initSimple (undefined :: MapInt) -prop_setSetMap = genericSetSet initSimple (undefined :: MapInt) - --- Tests whether this is also true for caches -newtype CacheInt = CacheInt Int deriving (Eq, Show, Arbitrary) -instance Component CacheInt where type Storage CacheInt = Cache 2 (Map CacheInt) -makeWorld "Cached" [''CacheInt] - -prop_setGetCache = genericSetGet initCached (undefined :: CacheInt) -prop_setSetCache = genericSetSet initCached (undefined :: CacheInt) - -prop_cacheUnique :: [CacheInt] -> [Entity] -> [(Entity, CacheInt)] -> Property -prop_cacheUnique eInit eDel eSet = assertSys initCached $ do - mapM newEntity eInit - mapM (flip set (Not @CacheInt)) eDel - mapM (uncurry set) eSet - es <- cfold (\a (_ :: CacheInt, Entity e) -> e : a) [] - pure $ es == nub es - --- Tests basic tuple functionality -newtype T1 = T1 Int deriving (Eq, Show, Arbitrary) -newtype T2 = T2 Int deriving (Eq, Show, Arbitrary) -newtype T3 = T3 Int deriving (Eq, Show, Arbitrary) -instance Component T1 where type Storage T1 = Map T1 -instance Component T2 where type Storage T2 = Map T2 -instance Component T3 where type Storage T3 = Map T3 - -makeWorld "Tuples" [''T1, ''T2, ''T3] - -prop_setGetTuple = genericSetGet initTuples (undefined :: (T1,T2,T3)) -prop_setSetTuple = genericSetSet initTuples (undefined :: (T1,T2,T3)) - --- Tests Reactive store properties -newtype TestEnum = TestEnum Bool deriving (Eq, Show, Bounded, Enum, Arbitrary) -instance Component TestEnum where type Storage TestEnum = Reactive (EnumMap TestEnum) (Map TestEnum) - -makeWorld "ReactiveWld" [''TestEnum] - -prop_setGetReactive = genericSetGet initReactiveWld (undefined :: TestEnum) -prop_setSetReactive = genericSetSet initReactiveWld (undefined :: TestEnum) -prop_lookupValid :: [(Entity, TestEnum)] -> [Entity] -> Property -prop_lookupValid writes deletes = assertSys initReactiveWld $ do - forM_ writes $ uncurry set - forM_ deletes $ flip destroy (Proxy @TestEnum) - - let getAll = cfold (flip (:)) [] :: SystemT ReactiveWld IO [(TestEnum, Entity)] - et <- fmap snd . filter ((== TestEnum True ) . fst) <$> getAll - ef <- fmap snd . filter ((== TestEnum False) . fst) <$> getAll - - rt <- withReactive $ enumLookup (TestEnum True) - rf <- withReactive $ enumLookup (TestEnum False) - - return ( sort rt == sort et - && sort rf == sort ef - && all (`notElem` ef) et - ) - --- Tests Pushdown -newtype StackInt = StackInt Int deriving (Eq, Show, Arbitrary) -instance Component StackInt where type Storage StackInt = Pushdown Map StackInt - -makeWorld "StackWld" [''StackInt] - -prop_setGetStack = genericSetSet initStackWld (undefined :: StackInt) - -return [] -main = $quickCheckAll diff --git a/bench/Main.hs b/bench/Main.hs new file mode 100644 index 0000000..4bee17f --- /dev/null +++ b/bench/Main.hs @@ -0,0 +1,42 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE Strict #-} + +module Main (main) where + +import Apecs +import Control.Monad +import Control.Monad.Reader +import Criterion.Main +import Linear + +newtype Pos = Pos (V2 Float) + +newtype Vel = Vel (V2 Float) + +data World + = World + (Cache 10000 (Map Pos) Pos) + (Cache 1000 (Map Vel) Vel) + EntityCounter + deriving (Generic, Initialize IO) + +benchInit :: System World () +benchInit = do + replicateM_ 1000 $ newEntity (Pos 0, Vel 0) + replicateM_ 9000 $ newEntity (Pos 0) + +benchStep :: System World () +benchStep = cmap $ \(Vel v, Pos p) -> Pos (p + v) + +main :: IO () +main = + defaultMain + [ bgroup + "pos_vel" + [ bench "init" $ whnfIO (initialize >>= runReaderT benchInit), + bench "step" $ whnfIO (initialize >>= runReaderT (benchInit >> benchStep)), + bench "step 100" $ whnfIO (initialize >>= runReaderT (benchInit >> replicateM_ 100 benchStep)) + ] + ] diff --git a/cabal.project b/cabal.project new file mode 100644 index 0000000..8834d04 --- /dev/null +++ b/cabal.project @@ -0,0 +1,2 @@ +packages: + ./ diff --git a/examples/Constraints.hs b/examples/Constraints.hs deleted file mode 100644 index 3343773..0000000 --- a/examples/Constraints.hs +++ /dev/null @@ -1,122 +0,0 @@ -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE TemplateHaskell #-} -{-# LANGUAGE TypeFamilies #-} - -import Apecs.Physics as P -import Apecs.Physics.Gloss -import Apecs.Util -import Control.Monad - -data MouseBody = MouseBody -instance Component MouseBody - where type Storage MouseBody = Unique MouseBody - -makeWorld "World" [''Physics, ''Camera, ''MouseBody] - -material = (Friction 0.4, Elasticity 0.8, Density 1) - -collisionFilter = CollisionFilter 1 maskAll maskAll - -initialize = do - set global ( Camera 0 150 - , earthGravity ) - - let gridLines' = gridLines (V2 4 3) 4 3 - - grid <- newEntity StaticBody - forM_ gridLines' $ \gline -> - newEntity (Shape grid $ setRadius 0.01 gline, collisionFilter) - - let mkBall pos = do - ball <- newEntity (DynamicBody, pos) - newEntity (Shape ball (cCircle 0.1)) - return ball - - springA <- mkBall $ Position (V2 (-1.5) 1) - springB <- mkBall $ Position (V2 (-1.7) 1) - newEntity $ Constraint springA springB (DampedSpring 0 0 0.3 3 1e-4) - - let mkBox pos = do - box <- newEntity (DynamicBody, pos) - newEntity (Shape box (oRectangle 0 0.2)) - return box - - pinA <- mkBox $ Position (V2 (-0.55) 1) - pinB <- mkBox $ Position (V2 (-0.3) 1) - newEntity $ Constraint pinA pinB (PinJoint (V2 0.2 0.2) (V2 0 0.2)) - - slideA <- mkBox $ Position (V2 0.75 1) - slideB <- mkBox $ Position (V2 0.5 1) - newEntity $ Constraint slideA slideB (SlideJoint (V2 0.2 0.2) (V2 0 0.2) 0 0.1) - - pivotA <- mkBox $ Position (V2 1.1 1) - pivotB <- mkBox $ Position (V2 1.3 1) - pivotC <- mkBox $ Position (V2 1.5 1) - newEntity $ Constraint pivotA pivotB (PivotJoint (V2 1.3 1)) - newEntity $ Constraint pivotB pivotC (PivotJoint (V2 1.5 1)) - - let mkPaddle (Position pos) = do - paddle <- newEntity (DynamicBody, Position pos) - newEntity $ Shape paddle (cRectangle (V2 0.06 0.4)) - newEntity $ Constraint paddle grid (PivotJoint pos) - return paddle - - gearA <- mkPaddle $ Position (V2 (-1.25) 0) - gearB <- mkPaddle $ Position (V2 (-1.75) 0) - newEntity $ Constraint gearA gearB (GearJoint 0 3) - - drsA <- mkPaddle $ Position (V2 (-0.25) 0) - drsB <- mkPaddle $ Position (V2 (-0.75) 0) - newEntity $ Constraint drsA drsB (DampedRotarySpring 0 1e-2 1e-4) - - rlA <- mkPaddle $ Position (V2 0.25 0) - rlB <- mkPaddle $ Position (V2 0.75 0) - newEntity (Constraint rlA rlB (RotaryLimitJoint 0 1)) - - motA <- mkPaddle $ Position (V2 1.25 0) - motB <- mkPaddle $ Position (V2 1.75 0) - newEntity (Constraint motA motB (SimpleMotor pi)) - - cmap $ \(_ :: Shape) -> material - -mousePos :: (Float, Float) -> System World (V2 Double) -mousePos mouseWin = do - cam <- get global - return . fmap realToFrac $ windowToWorld cam mouseWin - -handle :: Event -> System World () -handle (EventMotion mouseWin) = do - mpos <- mousePos mouseWin - cmap $ \MouseBody -> Position mpos - -handle (EventKey (MouseButton LeftButton) Down _ mouseWin) = do - mpos <- mousePos mouseWin - pq <- pointQuery mpos 0 collisionFilter - forM_ pq $ \(PointQueryResult shape _ _ _) -> do - Shape otherEty _ <- get shape - mouse <- newEntity (MouseBody, StaticBody, Position mpos) - newEntity (Constraint mouse otherEty (PivotJoint mpos), MaxForce 2) - -handle (EventKey (MouseButton LeftButton) Up _ _) = - cmap $ \MouseBody -> Not :: Not (MouseBody, Body) - -handle (EventKey (MouseButton RightButton) Down _ mouseWin) = do - mpos <- mousePos mouseWin - box <- newEntity (DynamicBody, Position mpos) - newEntity (Shape box (cRectangle 0.3), material) - return () - -handle _ = return () - -disp = InWindow "Constraint Gallery" (640,640) (10,10) -main = (initWorld >>=) . runSystem $ do - initialize - play disp - black - 60 - (foldDrawM drawBody) - handle - (const $ stepPhysics (1/60)) diff --git a/examples/HelloWorld.hs b/examples/HelloWorld.hs deleted file mode 100644 index cf46e6a..0000000 --- a/examples/HelloWorld.hs +++ /dev/null @@ -1,22 +0,0 @@ -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE TemplateHaskell #-} - -import Apecs.Physics -import Apecs.Physics.Gloss -import Control.Monad - -makeWorld "World" [''Physics, ''Camera] - -initialize = do - set global ( Camera (V2 0 1) 60 - , earthGravity ) - - lineBody <- newEntity (StaticBody, Angle (-pi/20)) - newEntity (Shape lineBody (hLine 6), Elasticity 0.9) - - ball <- newEntity (DynamicBody, Position (V2 0 3)) - newEntity (Shape ball (cCircle 0.5), Density 1, Elasticity 0.9) - -disp = InWindow "Hello World" (640,640) (10,10) -main = initWorld >>= runSystem (initialize >> simulate disp) diff --git a/examples/LICENSE b/examples/LICENSE deleted file mode 100644 index 32cc34e..0000000 --- a/examples/LICENSE +++ /dev/null @@ -1,30 +0,0 @@ -Copyright Jonas Carpay (c) 2017 - -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - - * Neither the name of Jonas Carpay nor the names of other - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/examples/Shmup.lhs b/examples/Shmup.lhs deleted file mode 100644 index d031d4e..0000000 --- a/examples/Shmup.lhs +++ /dev/null @@ -1,420 +0,0 @@ -This document breaks down a small game written using apecs. -We're going to be making a little shoot 'em up-style game, mirroring the Entitas example. -Consider this a vertical slice; we'll cover most of apecs' features, but not in great detail. -If you are not familiar with the basics of ECS it might be worth reading the introductory sections of the paper. - -If you want to run the game, clone this repository and run `stack exec shmup`. -Since this document is a literate Haskell file (or a rendered markdown file, in which case the `.lhs` file is in the same folder), you can also compile it directly with GHC and run the game. -The arrow keys move you, space shoots, escape quits. -If you have any questions or suggestions while working through this tutorial, don't hesitate to create an issue or send a message. - -Let's start at the top. -Apecs' type-level machinery tends to effect a large number of pragma's. -Don't worry, GHC will happily let you know if you missed any. - -> {-# LANGUAGE DataKinds #-} -> {-# LANGUAGE FlexibleContexts #-} -> {-# LANGUAGE FlexibleInstances #-} -> {-# LANGUAGE MultiParamTypeClasses #-} -> {-# LANGUAGE ScopedTypeVariables #-} -> {-# LANGUAGE TemplateHaskell #-} -> {-# LANGUAGE TypeApplications #-} -> {-# LANGUAGE TypeFamilies #-} -> {-# LANGUAGE GeneralizedNewtypeDeriving #-} - -The `Apecs` module forms the apecs prelude, it re-exports everything you typically need. - -> import Apecs - -For graphics and input we use `apecs-gloss`, which is a (thin) layer around the `gloss` graphics library. -Gloss is very easy to use, and ideal for simple games such as this one. - -> import Apecs.Gloss - -The `linear` library is the de facto library for small-dimensional vector types. - -> import Linear - -Finally, we use `random` for our RNG, and import some base stuff. - -> import System.Random -> import System.Exit -> import Control.Monad -> import Data.Monoid -> import Data.Semigroup (Semigroup) - -We need `Monoid` for `mempty`, but in recent GHC's that requires also defining `Semigroup` instances. -So, depending on your GHC version, you might not actually need the `Semigroup` import/instances. - -With the imports taken care of, we can start defining Components. -We do so by first defining a data type, and then give it an instance of `Component`. - -Each Component is stored in a separate data structure, called its storage or store. -The `Component` instance declaration specifies which store a Component uses. -We'll mostly be using the most basic store here, called `Map`. - -`Position` and `Velocity` are straightforward Components; they define an Entity's position and velocity as two-dimensional vectors of `Float`s. -The reason we use `Float` over `Double` is that most OpenGL-based libraries, including gloss, use `Float`s. -You can use `Double`, but if you don't need the extra accuracy, using `Float` will save you a bunch of conversions. - -> newtype Position = Position (V2 Float) deriving Show -> instance Component Position where type Storage Position = Map Position -> -> newtype Velocity = Velocity (V2 Float) deriving Show -> instance Component Velocity where type Storage Velocity = Map Velocity - -The following two Components are unit types, i.e. they only have a single inhabitant. -Unit types are common in apecs, as they can be used to tag an Entity. - -> data Target = Target deriving Show -> instance Component Target where type Storage Target = Map Target -> -> data Bullet = Bullet deriving Show -> instance Component Bullet where type Storage Bullet = Map Bullet - -`Particle` is also used to tag an Entity, but unlike `Target` and `Bullet`, also has a remaining life span (in seconds) field. - -> data Particle = Particle Float deriving Show -> instance Component Particle where type Storage Particle = Map Particle - -`Player` is a unit type, but instead of storing it in a `Map`, we use a `Unique`. -A `Unique` is a `Map` that will only hold a single Component; if we assign `Player` to Entity 3 and then to Entity 4, only Entity 4 will have a `Player`. -This enforces that there is only ever one player at the store level, and it will be the first example of how a store can change the behavior of a Component. - -> data Player = Player deriving Show -> instance Component Player where type Storage Player = Unique Player - -The third store we will use is `Global`, used to model global variables. -`Global` stores also hold a single Component, but unlike `Unique`, that Component does not belong to any particular Entity. -Instead, a `Global` store will always yield its one Component, regardless of the Entity it is queried for. -So more accurately, a global Component belongs to /every/ Entity. - -The initial value of a `Global` will be drawn from that Component's `Monoid` instance. - -`Score` keeps the score, and `Time` the total elapsed time. - -> newtype Score = Score Int deriving (Show, Num) -> instance Semigroup Score where (<>) = (+) -> instance Monoid Score where mempty = 0 -> instance Component Score where type Storage Score = Global Score -> -> newtype Time = Time Float deriving (Show, Num) -> instance Semigroup Time where (<>) = (+) -> instance Monoid Time where mempty = 0 -> instance Component Time where type Storage Time = Global Time - -You might already have noticed that there is more than one way to divide the game state into Components. -For example, since `Player`, `Target`, `Bullet`, and `Particle` are mutually exclusive, we could have enforced that by defining a single Component like this: - -< data EtyType = Player | Target | Bullet | Particle Float - -Defining separate Components makes it easier to efficiently iterate over one type of Entity, so that's the approach we will use in this tutorial, but both ways are equally valid and can be equally fast. - -Now that we have defined our Components, we need to create a game world. -This is generally done through Template Haskell, as follows: - -> makeWorld "World" [''Position, ''Velocity, ''Player, ''Target, ''Bullet, ''Score, ''Time, ''Particle, ''Camera] - -`makeWorld` defines a `World` data type, and the necessary instances. -More information on what exactly it generates can be found in the apecs paper. - -At this point I also like to define some type synonyms and constants: - -> type System' a = System World a -> type Kinetic = (Position, Velocity) -> -> playerSpeed, bulletSpeed, enemySpeed, xmin, xmax :: Float -> playerSpeed = 170 -> bulletSpeed = 500 -> enemySpeed = 80 -> xmin = -100 -> xmax = 100 -> -> hitBonus, missPenalty :: Int -> hitBonus = 100 -> missPenalty = 40 -> -> playerPos, scorePos :: V2 Float -> playerPos = V2 0 (-120) -> scorePos = V2 xmin (-170) - -With that, we are ready to start writing our first Systems. - -> initialize :: System' () -> initialize = do -> playerEty <- newEntity (Player, Position playerPos, Velocity 0) -> return () - -`initialize` initializes our game state. -In this case we only create a player, at the initial player position and with a velocity of 0. -`playerEty` is a value of type `Entity`, which is actually just an integer value. -In this case it will be 0, since it is the first Entity, and counting starts at 0. -In practice, we almost never use `Entity` values directly, and we won't actually use the `playerEty` value. - -> stepPosition :: Float -> System' () -> stepPosition dT = cmap $ \(Position p, Velocity v) -> Position (p + dT *^ v) - -`stepPosition` is the canonical example of a System; it adds every Entity's velocity to its position. -`cmap` is ubiquitous in apecs, and you'll use it to define most of your game logic. -`cmap`'s behaviour is heavily dependent on the type of the function we map, so I will briefly discuss that type for every use of `cmap`, and how to interpret it. -In this case, that's `(Position, Velocity) -> Position`. - -`cmap` will iterate over every Entity that has the Component on the left-hand side, and write the Component on the right-hand side. -In this case, the left-hand Component is `(Position, Velocity)`. -As you can see, a tuple of Components is considered a Component as well, and it is the first example of how Components can be Composed into bigger Components. -When we iterate over a tuple, what happens internally is that we iterate over all Entities that have the first Component (`Position`), and then test whether the Entity also has the remaining Components, in this case `Velocity`. - -> clampPlayer :: System' () -> clampPlayer = cmap $ \(Player, Position (V2 x y)) -> -> Position (V2 (min xmax . max xmin $ x) y) - -`clampPlayer` constrains the player's x position between `xmin` and `xmax`. -We can express it using a simple `cmap` as well. -The function has type `(Player, Position) -> Position`, which iterates over `Player`, reads `Player` and `Position`, and writes `Position`. - -Here we see the usefulness of unit types. -We never have to worry about the actual `Entity` value of the player, but instead we just refer to it using the `Player` Component. -Since `Player` is a `Unique` value, we can be sure that this only ever affects at most one Entity. - -> incrTime :: Float -> System' () -> incrTime dT = modify global $ \(Time t) -> Time (t+dT) - -`incrTime` increments the total elapsed time by `dT`. -In this case, we cannot use `cmap`, as we cannot iterate over a `Global`. -If you try to do so, you will get a type error about how `Global Time` does not have an instance of `ExplMembers`; which is to say you cannot retrieve a list of members from a `Global`. -Instead we have to use `modify`, which is like `cmap` for a single Entity. -As mentioned before, the exact Entity argument does not matter for a global Component. -`global` is just an alias for -1. - -Side note: -In earlier versions of apecs, the members of a global were defined to be `[ -1 ]`, so that you could `cmap` over a global. -This was removed, since it violated a number of common-sense properties such as `cmap` over `(a, b)` being semantically equivalent to `(b, a)`. -So, now it is simply a type error. - -Let's make things more interesting. -`clearTargets` needs to destroy the targets that move out of bounds. -We are going to try expressing this using `cmap`. - -An important thing to be aware of is that in apecs, there is no such thing as destroying/deleting/removing an /Entity/. -Instead, you can only destroy /Components/, and if you want to get rid of an Entity entirely, you often need to destroy each of its Components individually. - -Our mapped function will have type `(Target, Position, Velocity) -> Maybe (Target, Position, Velocity)`. -`Maybe` represent optionality, on the left-hand side it represents a read that might fail, on the right-hand side it is a write that can also delete a Component. -If we return a `Just c`, `c` gets written as normal, but when we return `Nothing`, those same Components will be deleted instead. - -> clearTargets :: System' () -> clearTargets = cmap $ \all@(Target, Position (V2 x _), Velocity _) -> -> if x < xmin || x > xmax -> then Nothing -> else Just all - -This works fine, but it's not ideal. -We don't really need to read `Velocity`, we are only ever interested in removing it. -Furthermore, we don't want to have to write all three Components, we just want to be able to delete them. - -The next System illustrates how we can make our `cmap`s more specific. -`stepParticles` needs to decrement the life time of all `Particle`s, and remove them if their timer reaches 0. -So we need to iterate over and read `Particle`, and either delete `Particle`, `Position`, and `Velocity`, or write `Particle`. -This is done with a function of type `Particle -> Either Particle (Not (Particle, Kinetic))`. - -As in most Haskell libraries, where tuples represent conjunction, `Either` represents a disjunction. -In the case of apecs, the Component `(a,b)` represents the presence of both `a` and `b`, whereas `Either a b` represents the presence of at least one of `a` or `b` (with `b` having precedence when reading). - -`Not :: Not c` can be used to delete something, just like `Nothing :: Maybe c`. -It can also occur on the left-hand side, where an Entity has a Component `(a, Not b)` if it has an `a`, but no `b`. -We will see this behaviour later. - -Combined, `Either a (Not b)` will either write `a`, or delete `b`. - -> stepParticles :: Float -> System' () -> stepParticles dT = cmap $ \(Particle t) -> -> if t < 0 -> then Right $ Not @(Particle, Kinetic) -> else Left $ Particle (t-dT) - -If you've never seen it, the `Not @c` syntax is from the `TypeApplications` pragma, and is equivalent to `Not :: Not c`. - -We can take `cmap` even further. -For bullets, we want to clear them when they leave the screen, and if so, decrement the score. - -We will use a function of type `(Bullet, Position, Score) -> Either () (Not (Bullet, Kinetic), Score)`. -Let's break this down. - -* `(Bullet, Position, Score)` means we iterate over Entities that have all three of those Components. - Score is a global Component, and as explained previously, can be said to belong to every Entity. - Remember though, we cannot iterate over globals, so you get a type error if `Score` is in the first position on the left-hand side. - -* Writing a `Left ()` value does nothing. - -* Writing `Right (Not, Score s)` will both delete `(Bullet, Kinetic)`, and write `Score`. - What happens when you change the type to `Either () (Not (Bullet, Kinetic, Score))`? - Try it out, the answer might surprise you. - -Putting it together: - -> clearBullets :: System' () -> clearBullets = cmap $ \(Bullet, Position (V2 _ y), Score s) -> -> if y > 170 -> then Right $ (Not @(Bullet, Kinetic), Score (s-missPenalty)) -> else Left () - -In some cases, just because something can be expressed in `cmap`, does not mean it necessarily should. -In the example above, you could argue that we perform an unnecessary read on the Score, since we read it for every `Bullet`. -Furthermore, we jump through a lot of hoops just to be able to use `Left ()` to essentially do nothing. -In this particular case, we could have used - -< cmapIf (\(Position y) -> y > 170) (\(_ :: Bullet) -> Not @ (Bullet, Position)) - -Sometimes, however, it might be easier to just add some side-effects. -It might not surprise you that there also is a `cmapM` and a `cmapM_`. -These pretty much work as you would expect, they take a function of type `cx -> SystemT w m cy`, meaning they iterate over and read their input argument, and run the System in the output. - -We'll use `cmapM_` to do some collision handling. -We first iterate over all `(Target, Position, Entity)`s, and for each, iterate over all `(Bullet, Position, Entity)` Entities. -Then, when the distance between two positions is below the threshold, we destroy both Entities, create a bunch of particles, and update the score. - -The `Entity` Component is the same `Entity` returned by e.g. `newEntity`, it is just an integer value in a newtype. -When read as a Component, it will return whatever Entity it is queried at, i.e. for `Entity ety' <- get ety`, `ety' == ety` will be true by definition. - -Destroying Components for a specific Entity (rather than through `cmap`) is done with `destroy`. - -Side note: -There are 5 primitive operations on stores/Components: `exists`, `get`, `set`, `destroy`, and `members`. -All other `System`s are implemented using these 5 operations. -For example, we could write `cmap` as follows: - -< myCmap :: (..) => (cx -> cy) -> System' () -< myCmap f = do -< etys <- members (Proxy @cx) -< forM_ etys $ \ety -> do -< cx <- get ety -< set ety (f cx) - -I won't go into further detail here, but the take-away here is that `cmap` is pretty ordinary; the actual magic happens by choosing interesting implementations of the above 5 functions. - -Anyway, collision handling: - -> handleCollisions = -> cmapM_ $ \(Target, Position posT, etyT) -> -> cmapM_ $ \(Bullet, Position posB, etyB) -> -> when (norm (posT - posB) < 10) $ do -> destroy etyT (Proxy @(Target, Kinetic)) -> destroy etyB (Proxy @(Bullet, Kinetic)) -> spawnParticles 15 (Position posB) (-500,500) (200,-50) -> modify global $ \(Score x) -> Score (x + hitBonus) - -Again, every time we delete e.g. a `Bullet`, we have to remember to also delete its `Kinetic` (position and velocity). -If you forget to do so, you will have a Component with just a `Position` and a `Velocity` floating around. -It won't really interact with anything, but it will still take up memory and have its position updated in every `stepPosition`. - -People have asked why there is no way to simply delete an Entity and all of its Components. -The reason apecs can't do it for you is kind of technical, but comes down to that there is no obvious way of centrally tracking what Entities have what Components. -Furthermore, adding such a System would be limiting in other ways, and additional complexity would make it harder to integrate apecs with other Systems. -Instead, in this tutorial we use type synonyms like `Kinetic` to define hierarchies of Components and easily delete many at once. -If this is an issue for you, you could write an `All` type synonym, a tuple containing all Components. -If you then `destroy ety (Proxy @All)`, you would be sure that all Components get deleted. - -`triggerEvery` runs a `System` periodically. -Nothing about this is apecs-specific, except for the `get global`, which we've seen before. -If you hadn't noticed by the way, `get` doesn't need a `Proxy` because unlike e.g. `destroy` it can infer what Component to act on from its return value. - -> triggerEvery :: Float -> Float -> Float -> System' a -> System' () -> triggerEvery dT period phase sys = do -> Time t <- get global -> let t' = t + phase -> trigger = floor (t'/period) /= floor ((t'+dT)/period) -> when trigger $ void sys - -`spawnParticles` does what it says on the tin. -The random values are generated in the IO monad, so we use `liftIO`. - -> spawnParticles :: Int -> Position -> (Float,Float) -> (Float,Float) -> System' () -> spawnParticles n pos dvx dvy = replicateM_ n $ do -> vx <- liftIO $ randomRIO dvx -> vy <- liftIO $ randomRIO dvy -> t <- liftIO $ randomRIO (0.02,0.3) -> newEntity (Particle t, pos, Velocity (V2 vx vy)) - -Finally, we assemble all our pieces into a single System. - -> step :: Float -> System' () -> step dT = do -> incrTime dT -> stepPosition dT -> clampPlayer -> clearTargets -> clearBullets -> stepParticles dT -> handleCollisions -> triggerEvery dT 0.6 0 $ newEntity (Target, Position (V2 xmin 80), Velocity (V2 enemySpeed 0)) -> triggerEvery dT 0.6 0.3 $ newEntity (Target, Position (V2 xmax 120), Velocity (V2 (negate enemySpeed) 0)) - -apecs-gloss provides a layer of convenience around the gloss `Graphics.Gloss.Interface.IO.Game` module. -We'll use it to make a window, render the game, and handle player input. - -Let's start by looking at input handling. -We define a function that maps each possible input to a System: - -> handleEvent :: Event -> System' () -> handleEvent (EventKey (SpecialKey KeyLeft) Down _ _) = -> cmap $ \(Player, Velocity (V2 x _)) -> Velocity (V2 (x-playerSpeed) 0) -> -> handleEvent (EventKey (SpecialKey KeyLeft) Up _ _) = -> cmap $ \(Player, Velocity (V2 x _)) -> Velocity (V2 (x+playerSpeed) 0) -> -> handleEvent (EventKey (SpecialKey KeyRight) Down _ _) = -> cmap $ \(Player, Velocity (V2 x _)) -> Velocity (V2 (x+playerSpeed) 0) -> -> handleEvent (EventKey (SpecialKey KeyRight) Up _ _) = -> cmap $ \(Player, Velocity (V2 x _)) -> Velocity (V2 (x-playerSpeed) 0) -> -> handleEvent (EventKey (SpecialKey KeySpace) Down _ _) = -> cmapM_ $ \(Player, pos) -> do -> newEntity (Bullet, pos, Velocity (V2 0 bulletSpeed)) -> spawnParticles 7 pos (-80,80) (10,100) -> -> handleEvent (EventKey (SpecialKey KeyEsc) Down _ _) = liftIO exitSuccess -> -> handleEvent _ = return () - -Next, we'll look at drawing. -This is done by constructing gloss `Picture` values. -I recommend looking at the gloss documentation to see what sort of things you can do with it. - -Our drawing function will produce such a `Picture`. -The easiest way to draw multiple Entities is to use the `foldDraw` function from apecs-gloss. -It performs a `cfold` of some drawing function, and combines all results into a larger `Picture`. - -> translate' :: Position -> Picture -> Picture -> translate' (Position (V2 x y)) = translate x y -> -> triangle, diamond :: Picture -> triangle = Line [(0,0),(-0.5,-1),(0.5,-1),(0,0)] -> diamond = Line [(-1,0),(0,-1),(1,0),(0,1),(-1,0)] -> -> draw :: System' Picture -> draw = do -> player <- foldDraw $ \(Player, pos) -> translate' pos . color white . scale 10 20 $ triangle -> targets <- foldDraw $ \(Target, pos) -> translate' pos . color red . scale 10 10 $ diamond -> bullets <- foldDraw $ \(Bullet, pos) -> translate' pos . color yellow . scale 4 4 $ diamond -> -> particles <- foldDraw $ -> \(Particle _, Velocity (V2 vx vy), pos) -> -> translate' pos . color orange $ Line [(0,0),(vx/10, vy/10)] -> -> Score s <- get global -> let score = color white . translate' (Position scorePos) . scale 0.1 0.1 . Text $ "Score: " ++ show s -> -> return $ player <> targets <> bullets <> score <> particles - -And with that, we can run our little game! - -> main :: IO () -> main = do -> w <- initWorld -> runWith w $ do -> initialize -> play (InWindow "Shmup" (220, 360) (10, 10)) black 60 draw handleEvent step - -That concludes our tour. -Again, please let me know if you have any questions or comments, through GitHub issues/twitter/reddit. diff --git a/examples/Shmup.md b/examples/Shmup.md deleted file mode 100644 index b02ef5d..0000000 --- a/examples/Shmup.md +++ /dev/null @@ -1,560 +0,0 @@ -This document breaks down a small game written using apecs. We’re going -to be making a little shoot ‘em up-style game, mirroring the Entitas -example. Consider this a vertical slice; we’ll cover most of apecs’ -features, but not in great detail. If you are not familiar with the -basics of ECS it might be worth reading the introductory sections of the -paper. - -If you want to run the game, clone this repository and run `stack exec -shmup`. Since this document is a literate Haskell file (or a rendered -markdown file, in which case the `.lhs` file is in the same folder), you -can also compile it directly with GHC and run the game. The arrow keys -move you, space shoots, escape quits. If you have any questions or -suggestions while working through this tutorial, don’t hesitate to -create an issue or send a message. - -Let’s start at the top. Apecs’ type-level machinery tends to effect a -large number of pragma’s. Don’t worry, GHC will happily let you know if -you missed any. - -``` haskell -{-# LANGUAGE DataKinds #-} -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE TemplateHaskell #-} -{-# LANGUAGE TypeApplications #-} -{-# LANGUAGE TypeFamilies #-} -{-# LANGUAGE GeneralizedNewtypeDeriving #-} -``` - -The `Apecs` module forms the apecs prelude, it re-exports everything you -typically need. - -``` haskell -import Apecs -``` - -For graphics and input we use `apecs-gloss`, which is a (thin) layer -around the `gloss` graphics library. Gloss is very easy to use, and -ideal for simple games such as this one. - -``` haskell -import Apecs.Gloss -``` - -The `linear` library is the de facto library for small-dimensional -vector types. - -``` haskell -import Linear -``` - -Finally, we use `random` for our RNG, and import some base stuff. - -``` haskell -import System.Random -import System.Exit -import Control.Monad -import Data.Monoid -import Data.Semigroup (Semigroup) -``` - -We need `Monoid` for `mempty`, but in recent GHC’s that requires also -defining `Semigroup` instances. So, depending on your GHC version, you -might not actually need the `Semigroup` import/instances. - -With the imports taken care of, we can start defining Components. We do -so by first defining a data type, and then give it an instance of -`Component`. - -Each Component is stored in a separate data structure, called its -storage or store. The `Component` instance declaration specifies which -store a Component uses. We’ll mostly be using the most basic store here, -called `Map`. - -`Position` and `Velocity` are straightforward Components; they define an -Entity’s position and velocity as two-dimensional vectors of `Float`s. -The reason we use `Float` over `Double` is that most OpenGL-based -libraries, including gloss, use `Float`s. You can use `Double`, but if -you don’t need the extra accuracy, using `Float` will save you a bunch -of conversions. - -``` haskell -newtype Position = Position (V2 Float) deriving Show -instance Component Position where type Storage Position = Map Position - -newtype Velocity = Velocity (V2 Float) deriving Show -instance Component Velocity where type Storage Velocity = Map Velocity -``` - -The following two Components are unit types, i.e. they only have a -single inhabitant. Unit types are common in apecs, as they can be used -to tag an Entity. - -``` haskell -data Target = Target deriving Show -instance Component Target where type Storage Target = Map Target - -data Bullet = Bullet deriving Show -instance Component Bullet where type Storage Bullet = Map Bullet -``` - -`Particle` is also used to tag an Entity, but unlike `Target` and -`Bullet`, also has a remaining life span (in seconds) field. - -``` haskell -data Particle = Particle Float deriving Show -instance Component Particle where type Storage Particle = Map Particle -``` - -`Player` is a unit type, but instead of storing it in a `Map`, we use a -`Unique`. A `Unique` is a `Map` that will only hold a single Component; -if we assign `Player` to Entity 3 and then to Entity 4, only Entity 4 -will have a `Player`. This enforces that there is only ever one player -at the store level, and it will be the first example of how a store can -change the behavior of a Component. - -``` haskell -data Player = Player deriving Show -instance Component Player where type Storage Player = Unique Player -``` - -The third store we will use is `Global`, used to model global variables. -`Global` stores also hold a single Component, but unlike `Unique`, that -Component does not belong to any particular Entity. Instead, a `Global` -store will always yield its one Component, regardless of the Entity it -is queried for. So more accurately, a global Component belongs to -/every/ Entity. - -The initial value of a `Global` will be drawn from that Component’s -`Monoid` instance. - -`Score` keeps the score, and `Time` the total elapsed time. - -``` haskell -newtype Score = Score Int deriving (Show, Num) -instance Semigroup Score where (<>) = (+) -instance Monoid Score where mempty = 0 -instance Component Score where type Storage Score = Global Score - -newtype Time = Time Float deriving (Show, Num) -instance Semigroup Time where (<>) = (+) -instance Monoid Time where mempty = 0 -instance Component Time where type Storage Time = Global Time -``` - -You might already have noticed that there is more than one way to divide -the game state into Components. For example, since `Player`, `Target`, -`Bullet`, and `Particle` are mutually exclusive, we could have enforced -that by defining a single Component like this: - -``` sourceCode haskell -data EtyType = Player | Target | Bullet | Particle Float -``` - -Defining separate Components makes it easier to efficiently iterate over -one type of Entity, so that’s the approach we will use in this tutorial, -but both ways are equally valid and can be equally fast. - -Now that we have defined our Components, we need to create a game world. -This is generally done through Template Haskell, as -follows: - -``` haskell -makeWorld "World" [''Position, ''Velocity, ''Player, ''Target, ''Bullet, ''Score, ''Time, ''Particle, ''Camera] -``` - -`makeWorld` defines a `World` data type, and the necessary instances. -More information on what exactly it generates can be found in the apecs -paper. - -At this point I also like to define some type synonyms and constants: - -``` haskell -type System' a = System World a -type Kinetic = (Position, Velocity) - -playerSpeed, bulletSpeed, enemySpeed, xmin, xmax :: Float -playerSpeed = 170 -bulletSpeed = 500 -enemySpeed = 80 -xmin = -100 -xmax = 100 - -hitBonus, missPenalty :: Int -hitBonus = 100 -missPenalty = 40 - -playerPos, scorePos :: V2 Float -playerPos = V2 0 (-120) -scorePos = V2 xmin (-170) -``` - -With that, we are ready to start writing our first Systems. - -``` haskell -initialize :: System' () -initialize = do - playerEty <- newEntity (Player, Position playerPos, Velocity 0) - return () -``` - -`initialize` initializes our game state. In this case we only create a -player, at the initial player position and with a velocity of 0. -`playerEty` is a value of type `Entity`, which is actually just an -integer value. In this case it will be 0, since it is the first Entity, -and counting starts at 0. In practice, we almost never use `Entity` -values directly, and we won’t actually use the `playerEty` value. - -``` haskell -stepPosition :: Float -> System' () -stepPosition dT = cmap $ \(Position p, Velocity v) -> Position (p + dT *^ v) -``` - -`stepPosition` is the canonical example of a System; it adds every -Entity’s velocity to its position. `cmap` is ubiquitous in apecs, and -you’ll use it to define most of your game logic. `cmap`’s behaviour is -heavily dependent on the type of the function we map, so I will briefly -discuss that type for every use of `cmap`, and how to interpret it. In -this case, that’s `(Position, Velocity) -> Position`. - -`cmap` will iterate over every Entity that has the Component on the -left-hand side, and write the Component on the right-hand side. In this -case, the left-hand Component is `(Position, Velocity)`. As you can see, -a tuple of Components is considered a Component as well, and it is the -first example of how Components can be Composed into bigger Components. -When we iterate over a tuple, what happens internally is that we iterate -over all Entities that have the first Component (`Position`), and then -test whether the Entity also has the remaining Components, in this case -`Velocity`. - -``` haskell -clampPlayer :: System' () -clampPlayer = cmap $ \(Player, Position (V2 x y)) - -> Position (V2 (min xmax . max xmin $ x) y) -``` - -`clampPlayer` constrains the player’s x position between `xmin` and -`xmax`. We can express it using a simple `cmap` as well. The function -has type `(Player, Position) -> Position`, which iterates over `Player`, -reads `Player` and `Position`, and writes `Position`. - -Here we see the usefulness of unit types. We never have to worry about -the actual `Entity` value of the player, but instead we just refer to it -using the `Player` Component. Since `Player` is a `Unique` value, we can -be sure that this only ever affects at most one Entity. - -``` haskell -incrTime :: Float -> System' () -incrTime dT = modify global $ \(Time t) -> Time (t+dT) -``` - -`incrTime` increments the total elapsed time by `dT`. In this case, we -cannot use `cmap`, as we cannot iterate over a `Global`. If you try to -do so, you will get a type error about how `Global Time` does not have -an instance of `ExplMembers`; which is to say you cannot retrieve a list -of members from a `Global`. Instead we have to use `modify`, which is -like `cmap` for a single Entity. As mentioned before, the exact Entity -argument does not matter for a global Component. `global` is just an -alias for -1. - -Side note: In earlier versions of apecs, the members of a global were -defined to be `[ -1 ]`, so that you could `cmap` over a global. This was -removed, since it violated a number of common-sense properties such as -`cmap` over `(a, b)` being semantically equivalent to `(b, a)`. So, now -it is simply a type error. - -Let’s make things more interesting. `clearTargets` needs to destroy the -targets that move out of bounds. We are going to try expressing this -using `cmap`. - -An important thing to be aware of is that in apecs, there is no such -thing as destroying/deleting/removing an /Entity/. Instead, you can only -destroy /Components/, and if you want to get rid of an Entity entirely, -you often need to destroy each of its Components individually. - -Our mapped function will have type `(Target, Position, Velocity) -> -Maybe (Target, Position, Velocity)`. `Maybe` represent optionality, on -the left-hand side it represents a read that might fail, on the -right-hand side it is a write that can also delete a Component. If we -return a `Just c`, `c` gets written as normal, but when we return -`Nothing`, those same Components will be deleted instead. - -``` haskell -clearTargets :: System' () -clearTargets = cmap $ \all@(Target, Position (V2 x _), Velocity _) -> - if x < xmin || x > xmax - then Nothing - else Just all -``` - -This works fine, but it’s not ideal. We don’t really need to read -`Velocity`, we are only ever interested in removing it. Furthermore, we -don’t want to have to write all three Components, we just want to be -able to delete them. - -The next System illustrates how we can make our `cmap`s more specific. -`stepParticles` needs to decrement the life time of all `Particle`s, and -remove them if their timer reaches 0. So we need to iterate over and -read `Particle`, and either delete `Particle`, `Position`, and -`Velocity`, or write `Particle`. This is done with a function of type -`Particle -> Either Particle (Not (Particle, Kinetic))`. - -As in most Haskell libraries, where tuples represent conjunction, -`Either` represents a disjunction. In the case of apecs, the Component -`(a,b)` represents the presence of both `a` and `b`, whereas `Either a -b` represents the presence of at least one of `a` or `b` (with `b` -having precedence when reading). - -`Not :: Not c` can be used to delete something, just like `Nothing :: -Maybe c`. It can also occur on the left-hand side, where an Entity has a -Component `(a, Not b)` if it has an `a`, but no `b`. We will see this -behaviour later. - -Combined, `Either a (Not b)` will either write `a`, or delete `b`. - -``` haskell -stepParticles :: Float -> System' () -stepParticles dT = cmap $ \(Particle t) -> - if t < 0 - then Right $ Not @(Particle, Kinetic) - else Left $ Particle (t-dT) -``` - -If you’ve never seen it, the `Not @c` syntax is from the -`TypeApplications` pragma, and is equivalent to `Not :: Not c`. - -We can take `cmap` even further. For bullets, we want to clear them when -they leave the screen, and if so, decrement the score. - -We will use a function of type `(Bullet, Position, Score) -> Either () -(Not (Bullet, Kinetic), Score)`. Let’s break this down. - - - `(Bullet, Position, Score)` means we iterate over Entities that have - all three of those Components. Score is a global Component, and as - explained previously, can be said to belong to every Entity. - Remember though, we cannot iterate over globals, so you get a type - error if `Score` is in the first position on the left-hand side. - - - Writing a `Left ()` value does nothing. - - - Writing `Right (Not, Score s)` will both delete `(Bullet, Kinetic)`, - and write `Score`. What happens when you change the type to `Either - () (Not (Bullet, Kinetic, Score))`? Try it out, the answer might - surprise you. - -Putting it together: - -``` haskell -clearBullets :: System' () -clearBullets = cmap $ \(Bullet, Position (V2 _ y), Score s) -> - if y > 170 - then Right $ (Not @(Bullet, Kinetic), Score (s-missPenalty)) - else Left () -``` - -In some cases, just because something can be expressed in `cmap`, does -not mean it necessarily should. In the example above, you could argue -that we perform an unnecessary read on the Score, since we read it for -every `Bullet`. Furthermore, we jump through a lot of hoops just to be -able to use `Left ()` to essentially do nothing. In this particular -case, we could have -used - -``` sourceCode haskell -cmapIf (\(Position y) -> y > 170) (\(_ :: Bullet) -> Not @ (Bullet, Position)) -``` - -Sometimes, however, it might be easier to just add some side-effects. It -might not surprise you that there also is a `cmapM` and a `cmapM_`. -These pretty much work as you would expect, they take a function of type -`cx -> SystemT w m cy`, meaning they iterate over and read their input -argument, and run the System in the output. - -We’ll use `cmapM_` to do some collision handling. We first iterate over -all `(Target, Position, Entity)`s, and for each, iterate over all -`(Bullet, Position, Entity)` Entities. Then, when the distance between -two positions is below the threshold, we destroy both Entities, create a -bunch of particles, and update the score. - -The `Entity` Component is the same `Entity` returned by e.g. -`newEntity`, it is just an integer value in a newtype. When read as a -Component, it will return whatever Entity it is queried at, i.e. for -`Entity ety' <- get ety`, `ety' == ety` will be true by definition. - -Destroying Components for a specific Entity (rather than through `cmap`) -is done with `destroy`. - -Side note: There are 5 primitive operations on stores/Components: -`exists`, `get`, `set`, `destroy`, and `members`. All other `System`s -are implemented using these 5 operations. For example, we could write -`cmap` as follows: - -``` sourceCode haskell -myCmap :: (..) => (cx -> cy) -> System' () -myCmap f = do - etys <- members (Proxy @cx) - forM_ etys $ \ety -> do - cx <- get ety - set ety (f cx) -``` - -I won’t go into further detail here, but the take-away here is that -`cmap` is pretty ordinary; the actual magic happens by choosing -interesting implementations of the above 5 functions. - -Anyway, collision handling: - -``` haskell -handleCollisions = - cmapM_ $ \(Target, Position posT, etyT) -> - cmapM_ $ \(Bullet, Position posB, etyB) -> - when (norm (posT - posB) < 10) $ do - destroy etyT (Proxy @(Target, Kinetic)) - destroy etyB (Proxy @(Bullet, Kinetic)) - spawnParticles 15 (Position posB) (-500,500) (200,-50) - modify global $ \(Score x) -> Score (x + hitBonus) -``` - -Again, every time we delete e.g. a `Bullet`, we have to remember to also -delete its `Kinetic` (position and velocity). If you forget to do so, -you will have a Component with just a `Position` and a `Velocity` -floating around. It won’t really interact with anything, but it will -still take up memory and have its position updated in every -`stepPosition`. - -People have asked why there is no way to simply delete an Entity and all -of its Components. The reason apecs can’t do it for you is kind of -technical, but comes down to that there is no obvious way of centrally -tracking what Entities have what Components. Furthermore, adding such a -System would be limiting in other ways, and additional complexity would -make it harder to integrate apecs with other Systems. Instead, in this -tutorial we use type synonyms like `Kinetic` to define hierarchies of -Components and easily delete many at once. If this is an issue for you, -you could write an `All` type synonym, a tuple containing all -Components. If you then `destroy ety (Proxy @All)`, you would be sure -that all Components get deleted. - -`triggerEvery` runs a `System` periodically. Nothing about this is -apecs-specific, except for the `get global`, which we’ve seen before. If -you hadn’t noticed by the way, `get` doesn’t need a `Proxy` because -unlike e.g. `destroy` it can infer what Component to act on from its -return value. - -``` haskell -triggerEvery :: Float -> Float -> Float -> System' a -> System' () -triggerEvery dT period phase sys = do - Time t <- get global - let t' = t + phase - trigger = floor (t'/period) /= floor ((t'+dT)/period) - when trigger $ void sys -``` - -`spawnParticles` does what it says on the tin. The random values are -generated in the IO monad, so we use -`liftIO`. - -``` haskell -spawnParticles :: Int -> Position -> (Float,Float) -> (Float,Float) -> System' () -spawnParticles n pos dvx dvy = replicateM_ n $ do - vx <- liftIO $ randomRIO dvx - vy <- liftIO $ randomRIO dvy - t <- liftIO $ randomRIO (0.02,0.3) - newEntity (Particle t, pos, Velocity (V2 vx vy)) -``` - -Finally, we assemble all our pieces into a single System. - -``` haskell -step :: Float -> System' () -step dT = do - incrTime dT - stepPosition dT - clampPlayer - clearTargets - clearBullets - stepParticles dT - handleCollisions - triggerEvery dT 0.6 0 $ newEntity (Target, Position (V2 xmin 80), Velocity (V2 enemySpeed 0)) - triggerEvery dT 0.6 0.3 $ newEntity (Target, Position (V2 xmax 120), Velocity (V2 (negate enemySpeed) 0)) -``` - -apecs-gloss provides a layer of convenience around the gloss -`Graphics.Gloss.Interface.IO.Game` module. We’ll use it to make a -window, render the game, and handle player input. - -Let’s start by looking at input handling. We define a function that maps -each possible input to a System: - -``` haskell -handleEvent :: Event -> System' () -handleEvent (EventKey (SpecialKey KeyLeft) Down _ _) = - cmap $ \(Player, Velocity (V2 x _)) -> Velocity (V2 (x-playerSpeed) 0) - -handleEvent (EventKey (SpecialKey KeyLeft) Up _ _) = - cmap $ \(Player, Velocity (V2 x _)) -> Velocity (V2 (x+playerSpeed) 0) - -handleEvent (EventKey (SpecialKey KeyRight) Down _ _) = - cmap $ \(Player, Velocity (V2 x _)) -> Velocity (V2 (x+playerSpeed) 0) - -handleEvent (EventKey (SpecialKey KeyRight) Up _ _) = - cmap $ \(Player, Velocity (V2 x _)) -> Velocity (V2 (x-playerSpeed) 0) - -handleEvent (EventKey (SpecialKey KeySpace) Down _ _) = - cmapM_ $ \(Player, pos) -> do - newEntity (Bullet, pos, Velocity (V2 0 bulletSpeed)) - spawnParticles 7 pos (-80,80) (10,100) - -handleEvent (EventKey (SpecialKey KeyEsc) Down _ _) = liftIO exitSuccess - -handleEvent _ = return () -``` - -Next, we’ll look at drawing. This is done by constructing gloss -`Picture` values. I recommend looking at the gloss documentation to see -what sort of things you can do with it. - -Our drawing function will produce such a `Picture`. The easiest way to -draw multiple Entities is to use the `foldDraw` function from -apecs-gloss. It performs a `cfold` of some drawing function, and -combines all results into a larger `Picture`. - -``` haskell -translate' :: Position -> Picture -> Picture -translate' (Position (V2 x y)) = translate x y - -triangle, diamond :: Picture -triangle = Line [(0,0),(-0.5,-1),(0.5,-1),(0,0)] -diamond = Line [(-1,0),(0,-1),(1,0),(0,1),(-1,0)] - -draw :: System' Picture -draw = do - player <- foldDraw $ \(Player, pos) -> translate' pos . color white . scale 10 20 $ triangle - targets <- foldDraw $ \(Target, pos) -> translate' pos . color red . scale 10 10 $ diamond - bullets <- foldDraw $ \(Bullet, pos) -> translate' pos . color yellow . scale 4 4 $ diamond - - particles <- foldDraw $ - \(Particle _, Velocity (V2 vx vy), pos) -> - translate' pos . color orange $ Line [(0,0),(vx/10, vy/10)] - - Score s <- get global - let score = color white . translate' (Position scorePos) . scale 0.1 0.1 . Text $ "Score: " ++ show s - - return $ player <> targets <> bullets <> score <> particles -``` - -And with that, we can run our little game\! - -``` haskell -main :: IO () -main = do - w <- initWorld - runWith w $ do - initialize - play (InWindow "Shmup" (220, 360) (10, 10)) black 60 draw handleEvent step -``` - -That concludes our tour. Again, please let me know if you have any -questions or comments, through GitHub issues/twitter/reddit. diff --git a/examples/Simple.hs b/examples/Simple.hs deleted file mode 100644 index f94159c..0000000 --- a/examples/Simple.hs +++ /dev/null @@ -1,30 +0,0 @@ -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE TemplateHaskell #-} -{-# LANGUAGE TypeFamilies #-} - -import Apecs -import Linear (V2 (..)) - -newtype Position = Position (V2 Double) deriving Show -newtype Velocity = Velocity (V2 Double) deriving Show -data Flying = Flying - -makeWorldAndComponents "World" [''Position, ''Velocity, ''Flying] - -game :: System World () -game = do - newEntity (Position 0, Velocity 1) - newEntity (Position 2, Velocity 1) - newEntity (Position 1, Velocity 2, Flying) - - -- 1. Add velocity to position - -- 2. Apply gravity to non-flying entities - -- 3. Print a list of entities and their positions - cmap $ \(Position p, Velocity v) -> Position (v+p) - cmap $ \(Velocity v, _ :: Not Flying) -> Velocity (v - V2 0 1) - cmapM_ $ \(Position p, Entity e) -> liftIO . print $ (e, p) - -main :: IO () -main = initWorld >>= runSystem game diff --git a/examples/Tumbler.hs b/examples/Tumbler.hs deleted file mode 100644 index ea8ed17..0000000 --- a/examples/Tumbler.hs +++ /dev/null @@ -1,36 +0,0 @@ -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE TemplateHaskell #-} -{-# LANGUAGE TypeFamilies #-} - -import Apecs.Physics -import Apecs.Physics.Gloss -import Apecs.Util -import Control.Monad -import System.Random - -makeWorld "World" [''Physics, ''Camera] - -initialize :: System World () -initialize = do - set global ( Camera 0 50 - , earthGravity ) - - let sides = toEdges $ cRectangle 5 - tumbler <- newEntity ( KinematicBody - , AngularVelocity (-1) - ) - - forM_ sides $ newEntity . Shape tumbler . setRadius 0.05 - - replicateM_ 200 $ do - x <- liftIO$ randomRIO (-2, 2) - y <- liftIO$ randomRIO (-2, 2) - r <- liftIO$ randomRIO (0.1, 0.2) - let c = (realToFrac x+2)/3 - ball <- newEntity (DynamicBody, Position (V2 x y)) - newEntity (Shape ball (cCircle r), Density 1 ) - -disp = InWindow "Tumbler" (640,640) (10,10) -main = initWorld >>= runSystem (initialize >> simulate disp) diff --git a/examples/examples.cabal b/examples/examples.cabal deleted file mode 100644 index aa2c6c2..0000000 --- a/examples/examples.cabal +++ /dev/null @@ -1,71 +0,0 @@ -name: examples -version: 0.1.0.0 -homepage: https://github.com/jonascarpay/apecs/examples#readme -license: BSD3 -license-file: LICENSE -synopsis: examples for apecs -description: Examples for apecs and related libraries -author: Jonas Carpay -maintainer: jonascarpay@gmail.com -category: Game, Control, Data -build-type: Simple -cabal-version: >=1.10 - -executable simple - main-is: - Simple.hs - build-depends: - base >= 4.9 && < 5, - apecs, - linear - default-language: - Haskell2010 - -executable shmup - main-is: - Shmup.lhs - build-depends: - base >= 4.9 && < 5, - apecs, - apecs-gloss, - linear, - random - default-language: - Haskell2010 - ghc-options: - -Wall - -executable helloworld - hs-source-dirs: . - main-is: HelloWorld.hs - build-depends: - base >= 4.9 && < 5, - apecs, - apecs-physics, - apecs-gloss, - gloss - default-language: Haskell2010 - -executable tumbler - hs-source-dirs: . - main-is: Tumbler.hs - build-depends: - base >= 4.9 && < 5, - apecs, - apecs-physics, - apecs-gloss, - gloss, - random - default-language: Haskell2010 - -executable constraints - hs-source-dirs: . - main-is: Constraints.hs - build-depends: - base >= 4.9 && < 5, - apecs, - apecs-physics, - apecs-gloss, - gloss - default-language: Haskell2010 - diff --git a/hie.yaml b/hie.yaml new file mode 100644 index 0000000..7f935f3 --- /dev/null +++ b/hie.yaml @@ -0,0 +1,6 @@ +cradle: + cabal: + - path: "src/" + component: "lib:apecs" + - path: "bench/" + component: "apecs:apecs-bench" diff --git a/pkgs.nix b/pkgs.nix new file mode 100644 index 0000000..d624570 --- /dev/null +++ b/pkgs.nix @@ -0,0 +1,29 @@ +let + haskellNix = + let + # 2021-04-13 + commit = "0057d59cffdaed7fa7475f9d6e9a6e84064b6870"; + sha256 = "07wsgvaarvbl043nwpliq7mygasa16lrrwnc5nyz2j8anpdd4jq4"; + in + import + (builtins.fetchTarball { + url = "https://github.com/input-output-hk/haskell.nix/archive/${commit}.tar.gz"; + inherit sha256; + }) + { }; + # It might be worth setting this to a more stable channel, but see https://github.com/jonascarpay/template-haskell/issues/9 + pkgsSrc = haskellNix.sources.nixpkgs-unstable; + pkgsArgs = haskellNix.nixpkgsArgs; + overlay = self: _: { + hsPkgs = self.haskell-nix.project { + src = self.haskell-nix.haskellLib.cleanGit { + src = ./.; + name = "apecs"; + }; + compiler-nix-name = "ghc8104"; + }; + }; +in +import pkgsSrc (pkgsArgs // { + overlays = pkgsArgs.overlays ++ [ overlay ]; +}) diff --git a/shell.nix b/shell.nix index 0a0f183..e0c6eb9 100644 --- a/shell.nix +++ b/shell.nix @@ -1,38 +1,24 @@ let - config = { - packageOverrides = pkgs: rec { - haskellPackages = pkgs.haskellPackages.override { - overrides = self: super: rec { - apecs = self.callCabal2nix "apecs" ./apecs {}; - apecs-stm = self.callCabal2nix "apecs-stm" ./apecs-stm {}; - apecs-physics = self.callCabal2nix "apecs-physics" ./apecs-physics {}; - apecs-gloss = self.callCabal2nix "apecs-gloss" ./apecs-gloss {}; - examples = self.callCabal2nix "examples" ./examples {}; - }; - }; - }; - }; - - pkgs = import { inherit config; }; - hpkgs = pkgs.haskellPackages; - - f = { mkDerivation, stdenv, ghcid, hlint - , apecs, apecs-stm, apecs-physics, apecs-gloss, examples }: - mkDerivation { - pname = "apecs-suite"; - version = "1"; - license = stdenv.lib.licenses.bsd3; - libraryHaskellDepends = [ - ghcid - hlint - apecs - # apecs-stm # 1.1.0.4 is marked as broken - apecs-physics - apecs-gloss - examples - ]; - src = ./.; - }; - + pkgs = import ./pkgs.nix; + hsPkgs = pkgs.hsPkgs; + ## To configure ormolu, I often end up creating a wrapped version in which I pass it the desired flags. + ## To use it, remove the ormolu line from the `tools` section, uncomment the lines below, and add `ormolu-wrapped` to `shellFor`'s `buildInputs`. + # ormolu-wrapped = + # let ormolu = pkgs.haskell-nix.tool hsPkgs.projectModule.compiler-nix-name "ormolu" "latest"; + # in + # pkgs.writeShellScriptBin "ormolu" '' + # ${ormolu}/bin/ormolu --ghc-opt=-XImportQualifiedPost $@ + # ''; in - (hpkgs.callPackage f {}).env +hsPkgs.shellFor { + withHoogle = true; + tools = { + cabal = "latest"; + ghcid = "latest"; + haskell-language-server = "latest"; + ormolu = "latest"; + hlint = "latest"; + }; + # buildInputs = [ ormolu-wrapped ]; # See note about ormolu-wrapped above + exactDeps = true; +} diff --git a/src/Apecs.hs b/src/Apecs.hs new file mode 100644 index 0000000..d79864f --- /dev/null +++ b/src/Apecs.hs @@ -0,0 +1,38 @@ +module Apecs + ( -- * Core types + Entity (..), + SystemT, + System, + + -- * Systems + cmap, + cmapM_, + Get, + Set, + Destroy, + Members, + newEntity, + + -- * Stores + EntityCounter (..), + Map (..), + Global (..), + Unique (..), + Cacheable, + Cache (..), + ReadOnly (..), + forceReadonly, + + -- * Misc + Not (..), + Generic, + Proxy (..), + Initialize (..), + ) +where + +import Apecs.Core +import Apecs.Stores +import Apecs.System +import Data.Proxy +import GHC.Generics (Generic) diff --git a/src/Apecs/Core.hs b/src/Apecs/Core.hs new file mode 100644 index 0000000..4b4112b --- /dev/null +++ b/src/Apecs/Core.hs @@ -0,0 +1,328 @@ +{-# LANGUAGE DefaultSignatures #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE DerivingStrategies #-} +{-# LANGUAGE EmptyCase #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE GeneralizedNewtypeDeriving #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE PolyKinds #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeApplications #-} +{-# LANGUAGE TypeOperators #-} +{-# LANGUAGE UndecidableInstances #-} + +module Apecs.Core + ( Entity (..), + SystemT, + System, + Get (..), + Set (..), + Destroy (..), + Members (..), + Initialize (..), + Not (..), + ) +where + +import Apecs.Focus +import Control.Applicative +import Control.Monad.Reader +import Data.IntSet as IS +import Data.Proxy +import Data.Vector.Storable (Vector) +import qualified Data.Vector.Storable as V +import GHC.Generics + +-- Core + +newtype Entity = Entity {unEntity :: Int} + deriving (Eq, Show, V.Storable) + +type SystemT w m a = ReaderT w m a + +type System w a = ReaderT w IO a + +-- law: +-- no side-effects: get ety >> get ety = get ety +class Get w m c where + exists :: Proxy c -> Entity -> SystemT w m Bool + get :: Entity -> SystemT w m c + + default exists :: GGet w m (Rep c) => Proxy c -> Entity -> SystemT w m Bool + {-# INLINE exists #-} + exists _ ety = gexists (Proxy @(Rep c)) ety + default get :: (Functor m, Generic c, GGet w m (Rep c)) => Entity -> SystemT w m c + {-# INLINE get #-} + get ety = to <$> gget ety + +class Set w m c where + set :: c -> Entity -> SystemT w m () + default set :: (Generic c, GSet w m (Rep c)) => c -> Entity -> SystemT w m () + {-# INLINE set #-} + set c ety = gset (from c) ety + +class Destroy w m c where + destroy :: Proxy c -> Entity -> SystemT w m () + default destroy :: GDestroy w m (Rep c) => Proxy c -> Entity -> SystemT w m () + {-# INLINE destroy #-} + destroy _ ety = gdestroy (Proxy @(Rep c)) ety + +class Members w m c where + members :: Proxy c -> SystemT w m (Vector Entity) + default members :: GMembers w m (Rep c) => Proxy c -> SystemT w m (Vector Entity) + {-# INLINE members #-} + members _ = gmembers (Proxy @(Rep c)) + +class Initialize m w where + initialize :: m w + default initialize :: (Functor m, Generic w, GInitialize m (Rep w)) => m w + {-# INLINE initialize #-} + initialize = to <$> ginitialize + +-- Generic Core classes +-- These are used to derive instances for _Components_ + +class GGet w m c where + gexists :: Proxy c -> Entity -> SystemT w m Bool + gget :: Entity -> SystemT w m (c x) + +class GSet w m c where + gset :: c x -> Entity -> SystemT w m () + +class GDestroy w m c where + gdestroy :: Proxy c -> Entity -> SystemT w m () + +class GMembers w m c where + gmembers :: Proxy c -> SystemT w m (Vector Entity) + +class GInitialize m w where + ginitialize :: m (w x) + +-- Magic Instances +-- These define instances for _Stores_ +-- "Magic" because overlappable and undecidable + +instance {-# OVERLAPPABLE #-} (HasStore w c, Get (GetStore w c) m c) => Get w m c where + {-# INLINE exists #-} + exists p ety = focusStore @c $ exists p ety + {-# INLINE get #-} + get ety = focusStore @c $ get ety + +instance {-# OVERLAPPABLE #-} (HasStore w c, Set (GetStore w c) m c) => Set w m c where + {-# INLINE set #-} + set a ety = focusStore @c $ set a ety + +instance {-# OVERLAPPABLE #-} (HasStore w c, Destroy (GetStore w c) m c) => Destroy w m c where + {-# INLINE destroy #-} + destroy p ety = focusStore @c $ destroy p ety + +instance {-# OVERLAPPABLE #-} (HasStore w c, Members (GetStore w c) m c) => Members w m c where + {-# INLINE members #-} + members p = focusStore @c $ members p + +-- Generic Instances + +--- Get +instance (Functor m, GGet w m c) => GGet w m (M1 p q c) where + {-# INLINE gexists #-} + gexists _ ety = gexists (Proxy @c) ety + {-# INLINE gget #-} + gget ety = M1 <$> gget ety + +instance (Functor m, Get w m c) => GGet w m (K1 p c) where + {-# INLINE gexists #-} + gexists _ ety = exists (Proxy @c) ety + {-# INLINE gget #-} + gget ety = K1 <$> get ety + +instance (Applicative m, GGet w m l, GGet w m r) => GGet w m (l :*: r) where + {-# INLINE gexists #-} + gexists _ ety = liftA2 (&&) (gexists (Proxy @l) ety) (gexists (Proxy @r) ety) + {-# INLINE gget #-} + gget ety = liftA2 (:*:) (gget ety) (gget ety) + +instance (Monad m, GGet w m l, GGet w m r) => GGet w m (l :+: r) where + {-# INLINE gexists #-} + gexists _ ety = do + rExists <- gexists (Proxy @r) ety + if rExists + then pure True + else gexists (Proxy @l) ety + {-# INLINE gget #-} + gget ety = do + rExists <- gexists (Proxy @r) ety + if rExists + then R1 <$> gget ety + else L1 <$> gget ety + +instance Applicative m => GGet w m U1 where + {-# INLINE gexists #-} + gexists _ _ = pure True + {-# INLINE gget #-} + gget _ = pure U1 + +instance Applicative m => GGet w m V1 where + {-# INLINE gexists #-} + gexists _ _ = pure False + {-# INLINE gget #-} + gget (Entity ety) = error $ "Querying void value for entity " <> show ety + +--- Set +instance GSet w m c => GSet w m (M1 p q c) where + {-# INLINE gset #-} + gset (M1 c) ety = gset c ety + +instance Set w m c => GSet w m (K1 p c) where + {-# INLINE gset #-} + gset (K1 c) ety = set c ety + +instance (Applicative m, GSet w m l, GSet w m r) => GSet w m (l :*: r) where + {-# INLINE gset #-} + gset (l :*: r) ety = gset l ety *> gset r ety + +instance (GSet w m l, GSet w m r) => GSet w m (l :+: r) where + {-# INLINE gset #-} + gset (R1 r) ety = gset r ety + gset (L1 l) ety = gset l ety + +instance GSet w m V1 where + {-# INLINE gset #-} + gset v = case v of + +--- Destroy +instance GDestroy w m c => GDestroy w m (M1 p q c) where + {-# INLINE gdestroy #-} + gdestroy _ ety = gdestroy (Proxy @c) ety + +instance Destroy w m c => GDestroy w m (K1 p c) where + {-# INLINE gdestroy #-} + gdestroy _ ety = destroy (Proxy @c) ety + +instance (Applicative m, GDestroy w m l, GDestroy w m r) => GDestroy w m (l :*: r) where + {-# INLINE gdestroy #-} + gdestroy _ ety = gdestroy (Proxy @l) ety *> gdestroy (Proxy @r) ety + +instance Applicative m => GDestroy w m V1 where + {-# INLINE gdestroy #-} + gdestroy _ _ = pure () + +--- Members +instance GMembers w m c => GMembers w m (M1 p q c) where + {-# INLINE gmembers #-} + gmembers _ = gmembers (Proxy @c) + +instance Members w m c => GMembers w m (K1 p c) where + {-# INLINE gmembers #-} + gmembers _ = members (Proxy @c) + +instance (GMembers w m l, GGet w m r, Monad m) => GMembers w m (l :*: r) where + {-# INLINE gmembers #-} + gmembers _ = gmembers (Proxy @l) >>= V.filterM (gexists (Proxy @r)) + +instance (Applicative m, GMembers w m l, GMembers w m r) => GMembers w m (l :+: r) where + {-# INLINE gmembers #-} + gmembers _ = + liftA2 + (\l r -> V.fromList . fmap Entity . IS.toList $ l <> r) + (IS.fromList . fmap unEntity . V.toList <$> gmembers (Proxy @l)) + (IS.fromList . fmap unEntity . V.toList <$> gmembers (Proxy @r)) + +instance Applicative m => GMembers w m V1 where + {-# INLINE gmembers #-} + gmembers _ = pure V.empty + +-- Initialize +instance (GInitialize m w, Functor m) => GInitialize m (M1 p q w) where + {-# INLINE ginitialize #-} + ginitialize = M1 <$> ginitialize + +instance (Initialize m w, Functor m) => GInitialize m (K1 p w) where + {-# INLINE ginitialize #-} + ginitialize = K1 <$> initialize + +instance (GInitialize m l, GInitialize m r, Applicative m) => GInitialize m (l :*: r) where + {-# INLINE ginitialize #-} + ginitialize = liftA2 (:*:) ginitialize ginitialize + +instance Applicative m => GInitialize m U1 where + {-# INLINE ginitialize #-} + ginitialize = pure U1 + +-- Instances + +--- () +instance Applicative m => Get w m () + +instance Applicative m => Set w m () where set _ _ = pure () + +--- Maybe +instance (Get w m c, Monad m) => Get w m (Maybe c) where exists _ _ = pure True + +instance (Destroy w m c, Set w m c) => Set w m (Maybe c) where + {-# INLINE set #-} + set Nothing ety = destroy (Proxy @c) ety + set (Just c) ety = set c ety + +--- Entity +instance Applicative m => Get w m Entity where + {-# INLINE get #-} + get ety = pure ety + {-# INLINE exists #-} + exists _ _ = pure True + +--- Not +data Not a = Not + deriving (Eq, Show, Generic) + +instance (Applicative m, Get w m c) => Get w m (Not c) where + {-# INLINE exists #-} + exists _ ety = not <$> exists (Proxy @c) ety + +instance Destroy w m c => Set w m (Not c) where + {-# INLINE set #-} + set _ ety = destroy (Proxy @c) ety + +{- ORMOLU_DISABLE -} + +-- Either + +instance (Get w m a, Get w m b, Monad m) => Get w m (Either a b) +instance (Members w m a, Members w m b, Monad m) => Members w m (Either a b) + +--- Tuples +instance (Get w m a, Get w m b, Applicative m) => Get w m (a, b) +instance (Set w m a, Set w m b, Applicative m) => Set w m (a, b) +instance (Destroy w m a, Destroy w m b, Applicative m) => Destroy w m (a, b) +instance (Members w m a, Get w m b, Monad m) => Members w m (a, b) +instance (Initialize m a, Initialize m b, Applicative m) => Initialize m (a, b) + +instance (Get w m a, Get w m b, Get w m c, Applicative m) => Get w m (a, b, c) +instance (Set w m a, Set w m b, Set w m c, Applicative m) => Set w m (a, b, c) +instance (Destroy w m a, Destroy w m b, Destroy w m c, Applicative m) => Destroy w m (a, b, c) +instance (Members w m a, Get w m b, Get w m c, Monad m) => Members w m (a, b, c) +instance (Initialize m a, Initialize m b, Initialize m c, Applicative m) => Initialize m (a, b, c) + +instance (Get w m a, Get w m b, Get w m c, Get w m d, Applicative m) => Get w m (a, b, c, d) +instance (Set w m a, Set w m b, Set w m c, Set w m d, Applicative m) => Set w m (a, b, c, d) +instance (Destroy w m a, Destroy w m b, Destroy w m c, Destroy w m d, Applicative m) => Destroy w m (a, b, c, d) +instance (Members w m a, Get w m b, Get w m c, Get w m d, Monad m) => Members w m (a, b, c, d) +instance (Initialize m a, Initialize m b, Initialize m c, Initialize m d, Applicative m) => Initialize m (a, b, c, d) + +instance (Get w m a, Get w m b, Get w m c, Get w m d, Get w m e, Applicative m) => Get w m (a, b, c, d, e) +instance (Set w m a, Set w m b, Set w m c, Set w m d, Set w m e, Applicative m) => Set w m (a, b, c, d, e) +instance (Destroy w m a, Destroy w m b, Destroy w m c, Destroy w m d, Destroy w m e, Applicative m) => Destroy w m (a, b, c, d, e) +instance (Members w m a, Get w m b, Get w m c, Get w m d, Get w m e, Monad m) => Members w m (a, b, c, d, e) +instance (Initialize m a, Initialize m b, Initialize m c, Initialize m d, Initialize m e, Applicative m) => Initialize m (a, b, c, d, e) + +instance (Get w m a, Get w m b, Get w m c, Get w m d, Get w m e, Get w m f, Applicative m) => Get w m (a, b, c, d, e, f) +instance (Set w m a, Set w m b, Set w m c, Set w m d, Set w m e, Set w m f, Applicative m) => Set w m (a, b, c, d, e, f) +instance (Destroy w m a, Destroy w m b, Destroy w m c, Destroy w m d, Destroy w m e, Destroy w m f, Applicative m) => Destroy w m (a, b, c, d, e, f) +instance (Members w m a, Get w m b, Get w m c, Get w m d, Get w m e, Get w m f, Monad m) => Members w m (a, b, c, d, e, f) +instance (Initialize m a, Initialize m b, Initialize m c, Initialize m d, Initialize m e, Initialize m f, Applicative m) => Initialize m (a, b, c, d, e, f) + +instance (Get w m a, Get w m b, Get w m c, Get w m d, Get w m e, Get w m f, Get w m g, Applicative m) => Get w m (a, b, c, d, e, f, g) +instance (Set w m a, Set w m b, Set w m c, Set w m d, Set w m e, Set w m f, Set w m g, Applicative m) => Set w m (a, b, c, d, e, f, g) +instance (Destroy w m a, Destroy w m b, Destroy w m c, Destroy w m d, Destroy w m e, Destroy w m f, Destroy w m g, Applicative m) => Destroy w m (a, b, c, d, e, f, g) +instance (Members w m a, Get w m b, Get w m c, Get w m d, Get w m e, Get w m f, Get w m g, Monad m) => Members w m (a, b, c, d, e, f, g) +instance (Initialize m a, Initialize m b, Initialize m c, Initialize m d, Initialize m e, Initialize m f, Initialize m g, Applicative m) => Initialize m (a, b, c, d, e, f, g) diff --git a/src/Apecs/Example.hs b/src/Apecs/Example.hs new file mode 100644 index 0000000..5264046 --- /dev/null +++ b/src/Apecs/Example.hs @@ -0,0 +1,41 @@ +{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# OPTIONS_GHC -Wno-unused-do-bind #-} + +module Apecs.Example (main) where + +import Apecs.Core +import Apecs.Stores +import Apecs.System +import Control.Monad.Reader +import GHC.Generics + +newtype Position = Position Float deriving (Eq, Show) + +newtype Velocity = Velocity Float deriving (Eq, Show) + +data World + = World + (Map Position) + (Map Velocity) + EntityCounter + deriving (Generic, Initialize IO) + +main :: IO () +main = do + (w :: World) <- initialize + flip runReaderT w $ do + newEntity (Position 0, Velocity 3) + newEntity (Position 1, Velocity 4) + newEntity (Position 2, Velocity 5) + cmap $ \(Position p, Velocity v) -> Position (p + v) + cmapM_ $ \(Position p) -> liftIO $ print p + +-- Custom composite component +data Kinetic = Kinetic + { pos :: Position, + vel :: Velocity + } + deriving (Eq, Show, Generic, Get World IO) diff --git a/src/Apecs/Focus.hs b/src/Apecs/Focus.hs new file mode 100644 index 0000000..af2ee0e --- /dev/null +++ b/src/Apecs/Focus.hs @@ -0,0 +1,137 @@ +{-# LANGUAGE AllowAmbiguousTypes #-} +{-# LANGUAGE ConstraintKinds #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE PolyKinds #-} +{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeApplications #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE TypeOperators #-} +{-# LANGUAGE UndecidableInstances #-} + +module Apecs.Focus + ( Components, + Default, + focusField, + focusStore, + HasStore, + HasField, + GetStore, + ) +where + +import Control.Monad.Reader +import Data.Kind +import GHC.Generics +import GHC.TypeLits + +type family Append (l :: [Type]) (r :: [Type]) :: [Type] where + Append (l : ls) rs = l : Append ls rs + Append '[] rs = rs + +-- It seems that finding the correct store is a lot easier if we first figure +-- out what store we're looking for, and then try to find that store. + +-- Step 1 +-- Figure out the store type from component type + +type family Components (t :: Type) :: [Type] + +type family Elem (c :: Type) (cs :: [Type]) (t :: r) (f :: r) :: r where + Elem c '[] t f = f + Elem c (c ': cs) t f = t + Elem c (q ': cs) t f = Elem c cs t f + +type family Unique (ts :: [t]) (e0 :: ErrorMessage) (en :: ErrorMessage) :: t where + Unique '[c] e0 en = c + Unique '[] e0 en = TypeError e0 + Unique cs e0 en = TypeError en + +type family Stores w c :: [Type] where + Stores (K1 q k) c = Elem c (Components k) '[k] '[] + Stores (M1 p q k) c = Stores k c + Stores (l :*: r) c = Append (Stores l c) (Stores r c) + +data Void1 + +type family Default (break :: t) (def :: t) :: t where + Default Void1 def = def + Default break _ = break + +-- Step 2 Find a store by type + +-- Count how often a certain record type occurs in a Generic +-- Used to guide instance resolution for finding a store in a world. +type family CountTypes w s :: Nat where + CountTypes (K1 q s) s = 1 + CountTypes (K1 q w) s = 0 + CountTypes (M1 p q k) s = CountTypes k s + CountTypes (l :*: r) s = CountTypes l s + CountTypes r s + +class GHasField w s where + askField :: w x -> s + +instance GHasField r s => GHasField (M1 p q r) s where + {-# INLINE askField #-} + askField = askField . unM1 + +instance GHasField (K1 p s) s where + {-# INLINE askField #-} + askField = unK1 + +instance GHasTypeProduct (CountTypes l s) (l :*: r) s => GHasField (l :*: r) s where + {-# INLINE askField #-} + askField = askFieldProduct @(CountTypes l s) + +class GHasTypeProduct (wl :: Nat) w s where + askFieldProduct :: w x -> s + +instance GHasField l s => GHasTypeProduct 1 (l :*: r) s where + {-# INLINE askFieldProduct #-} + askFieldProduct (l :*: _) = askField l + +instance GHasField r s => GHasTypeProduct 0 (l :*: r) s where + {-# INLINE askFieldProduct #-} + askFieldProduct (_ :*: r) = askField r + +-- Step 3 Tie them together + +type GetStore w c = + Unique + (Stores (Rep w) c) + ( 'Text "Type '" + ':<>: 'ShowType w + ':<>: 'Text "' contains no stores for component type '" + ':<>: 'ShowType c + ':<>: 'Text "'" + ) + ( 'Text "Type '" + ':<>: 'ShowType w + ':<>: 'Text "' contains more than one store providing component type '" + ':<>: 'ShowType c + ':<>: 'Text "':" + ':$$: 'ShowType (Stores (Rep w) c) + ) + +type HasField w t = (Generic w, GHasField (Rep w) t) + +type HasStore w c = HasField w (GetStore w c) + +{-# INLINE focusField #-} +focusField :: + forall t w m a. + HasField w t => + ReaderT t m a -> + ReaderT w m a +focusField = withReaderT (\w -> (askField (from w) :: t)) + +{-# INLINE focusStore #-} +focusStore :: + forall c w m a. + HasStore w c => + ReaderT (GetStore w c) m a -> + ReaderT w m a +focusStore = focusField diff --git a/src/Apecs/Reactive.hs b/src/Apecs/Reactive.hs new file mode 100644 index 0000000..dd5da40 --- /dev/null +++ b/src/Apecs/Reactive.hs @@ -0,0 +1,94 @@ +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeApplications #-} +{-# LANGUAGE TypeFamilies #-} + +module Apecs.Reactive + ( SpatialHash, + inverseGet, + inverseGetLocal, + ) +where + +import Apecs.Core +import Apecs.Focus +import Control.Monad.Reader +import qualified Data.Array.IO as A +import qualified Data.IntSet as IS +import Data.Ix + +data SpatialHash c s = SpatialHash + { shTable :: A.IOArray c IS.IntSet, + shChild :: s + } + +type instance Components (SpatialHash c s) = Components s + +{-# INLINE inverseGetLocal #-} +inverseGetLocal :: (Ix c, MonadIO m) => c -> SystemT (SpatialHash c s) m [Entity] +inverseGetLocal c = do + t <- asks shTable + liftIO $ fmap Entity . IS.toList <$> A.readArray t c + +inverseGet :: + forall w m s c. + ( Ix c, + MonadIO m, + HasStore w c, + GetStore w c ~ SpatialHash c s + ) => + c -> + SystemT w m [Entity] +inverseGet c = focusStore @c $ inverseGetLocal c + +{-# INLINE register #-} +register :: (Ix c, MonadIO m) => c -> Entity -> SystemT (SpatialHash c s) m () +register c (Entity ety) = do + table <- asks shTable + liftIO $ A.readArray table c >>= A.writeArray table c . IS.insert ety + +{-# INLINE deregister #-} +deregister :: (Ix c, MonadIO m) => c -> Entity -> SystemT (SpatialHash c s) m () +deregister c (Entity ety) = do + table <- asks shTable + liftIO $ A.readArray table c >>= A.writeArray table c . IS.delete ety + +instance (Ix c, Bounded c, MonadIO m, Initialize m s) => Initialize m (SpatialHash c s) where + initialize = do + t <- liftIO $ A.newArray (minBound, maxBound) mempty + SpatialHash t <$> initialize + +instance Get s m c => Get (SpatialHash c' s) m c where + get = withReaderT shChild . get + exists p = withReaderT shChild . exists p + +instance Members s m c => Members (SpatialHash c' s) m c where + members = withReaderT shChild . members + +instance (Get s m c, Set s m c, Ix c, MonadIO m) => Set (SpatialHash c s) m c where + -- TODO: + -- Smarter (de)registration? + -- Do we check if the underlying store actually works properly? + -- Use Cacheable to ensure well-behavedness? + -- SpatialHash is not cacheable itself, so precludes nesting? + -- SpatialHash is not cacheable itself, so precludes nesting? + -- This also goes for Destroy + set c ety = do + old :: Maybe c <- withReaderT shChild $ get ety + forM_ old $ \c' -> deregister c' ety + register c ety + withReaderT shChild $ set c ety + +instance (Set s m c) => Set (SpatialHash d s) m c where + set c = withReaderT shChild . set c + +instance (Ix c, Get s m c, MonadIO m, Destroy s m c) => Destroy (SpatialHash c s) m c where + destroy p ety = do + old :: Maybe c <- withReaderT shChild $ get ety + forM_ old $ \c' -> deregister c' ety + withReaderT shChild $ destroy p ety + +instance Destroy s m c => Destroy (SpatialHash d s) m c where + destroy p = withReaderT shChild . destroy p diff --git a/src/Apecs/Stores.hs b/src/Apecs/Stores.hs new file mode 100644 index 0000000..689f774 --- /dev/null +++ b/src/Apecs/Stores.hs @@ -0,0 +1,264 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE DerivingStrategies #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE StandaloneDeriving #-} +{-# LANGUAGE TypeApplications #-} +{-# LANGUAGE TypeFamilies #-} +{-# OPTIONS_GHC -Wno-redundant-constraints #-} + +-- | A collection of default stores in the IO monad. +module Apecs.Stores + ( EntityCounter (..), + newEntity, + Map (..), + Global (..), + Unique (..), + Cacheable, + Cache (..), + ReadOnly (..), + forceReadonly, + ) +where + +import Apecs.Core +import Apecs.Focus +import Control.Monad +import Control.Monad.IO.Class (MonadIO (..)) +import Control.Monad.Reader (ask, withReaderT) +import Data.Bits +import Data.IORef +import qualified Data.IntMap.Strict as IM +import Data.Proxy +import qualified Data.Vector.Mutable as VM +import qualified Data.Vector.Storable as VS +import qualified Data.Vector.Storable.Mutable as VSM +import GHC.Generics +import GHC.TypeNats + +-- Map +newtype Map c = Map {getMap :: IORef (IM.IntMap c)} + +type instance Components (Map c) = '[c] + +instance MonadIO m => Get (Map c) m c where + get (Entity ety) = do + Map ref <- ask + liftIO $ (IM.! ety) <$> readIORef ref + exists _ (Entity ety) = do + Map ref <- ask + liftIO $ IM.member ety <$> readIORef ref + +instance MonadIO m => Set (Map c) m c where + set c (Entity ety) = do + Map ref <- ask + liftIO $ modifyIORef' ref (IM.insert ety c) + +instance MonadIO m => Destroy (Map c) m c where + destroy _ (Entity ety) = do + Map ref <- ask + liftIO $ modifyIORef' ref (IM.delete ety) + +instance MonadIO m => Members (Map c) m c where + members _ = do + Map ref <- ask + etys <- liftIO $ fmap Entity . IM.keys <$> readIORef ref + pure $ VS.fromList etys + +instance MonadIO m => Initialize m (Map c) where + initialize = liftIO $ Map <$> newIORef mempty + +-- Global +newtype Global c = Global {getGlobal :: IORef c} + +type instance Components (Global c) = '[c] + +instance MonadIO m => Get (Global c) m c where + exists _ _ = pure True + get _ = do + Global ref <- ask + liftIO $ readIORef ref + +instance MonadIO m => Set (Global c) m c where + set s _ = do + Global ref <- ask + liftIO $ writeIORef ref s + +instance (Monoid c, MonadIO m) => Initialize m (Global c) where + initialize = Global <$> liftIO (newIORef mempty) + +-- Unique +newtype Unique c = Unique {getUnique :: IORef (Maybe (Entity, c))} + +type instance Components (Unique c) = '[c] + +instance MonadIO m => Get (Unique c) m c where + exists _ ety = do + Unique ref <- ask + r <- liftIO $ readIORef ref + pure $ case r of + Just (n, _) | n == ety -> True + _ -> False + + get _ = do + Unique ref <- ask + r <- liftIO $ readIORef ref + pure $ case r of + Just (_, c) -> c + _ -> error "Invalid `get` from Unique" + +instance MonadIO m => Set (Unique c) m c where + set s ety = do + Unique ref <- ask + liftIO $ writeIORef ref (Just (ety, s)) + +instance MonadIO m => Initialize m (Unique c) where + initialize = Unique <$> liftIO (newIORef Nothing) + +-- Cache +class Cacheable s c + +instance Cacheable (Map c) c + +instance Cacheable s c => Cacheable (Cache n s c) c + +data Cache (n :: Nat) s c = Cache + { _cacheBitMask :: Int, + _cacheTags :: VSM.IOVector Entity, + _cacheMembers :: VM.IOVector c, + _cacheChild :: s + } + +{-# INLINE withCached #-} +withCached :: SystemT s m a -> SystemT (Cache n s c) m a +withCached = withReaderT _cacheChild + +cacheMiss :: t +cacheMiss = error "Cache miss! If you are seeing this during normal operation, please open a bug report at https://github.com/jonascarpay/apecs" + +type instance Components (Cache n s c) = Components s + +instance (MonadIO m, Initialize m s, KnownNat n, Cacheable s c) => Initialize m (Cache n s c) where + initialize = do + let n = fromIntegral $ natVal (Proxy @n) :: Int + size = head . dropWhile (< n) $ iterate (`shiftL` 1) 1 + mask = size - 1 + tags <- liftIO $ VSM.replicate size (Entity (-2)) + cache <- liftIO $ VM.replicate size cacheMiss + child <- initialize + return (Cache mask tags cache child) + {-# INLINE initialize #-} + +instance {-# OVERLAPPING #-} (MonadIO m, Get s m c) => Get (Cache n s c) m c where + {-# INLINE get #-} + get ety = do + (Cache mask tags cache _) <- ask + let index = unEntity ety .&. mask + tag <- liftIO $ VSM.unsafeRead tags index + if tag == ety + then liftIO $ VM.unsafeRead cache index + else withCached (get ety) + + {-# INLINE exists #-} + exists p ety = do + Cache mask tags _ _ <- ask + tag <- liftIO $ VSM.unsafeRead tags (unEntity ety .&. mask) + if tag == ety + then return True + else withCached (exists p ety) + +instance {-# OVERLAPPING #-} (MonadIO m, Set s m c) => Set (Cache n s c) m c where + {-# INLINE set #-} + set c ety = do + Cache mask tags cache _ <- ask + let index = unEntity ety .&. mask + tag <- liftIO $ VSM.unsafeRead tags index + when (tag /= Entity (-2) && tag /= ety) $ do + cached <- liftIO $ VM.unsafeRead cache index + withCached $ set cached tag + liftIO $ VSM.unsafeWrite tags index ety + liftIO $ VM.unsafeWrite cache index c + +instance (MonadIO m, Destroy s m c) => Destroy (Cache n s c) m c where + {-# INLINE destroy #-} + destroy p ety = do + Cache mask tags cache _ <- ask + let index = unEntity ety .&. mask + tag <- liftIO $ VSM.unsafeRead tags mask + when (tag == ety) $ liftIO $ do + VSM.unsafeWrite tags index (Entity (-2)) + VM.unsafeWrite cache index cacheMiss + withCached $ destroy p ety + +instance (MonadIO m, Members s m c) => Members (Cache n s c) m c where + {-# INLINE members #-} + members p = do + (Cache mask tags _ _) <- ask + cached <- liftIO $ VS.filter (/= Entity (-2)) <$> VS.freeze tags + let etyFilter ety = (/= ety) <$> liftIO (VSM.unsafeRead tags (unEntity ety .&. mask)) + stored <- withCached (members p) >>= liftIO . VS.filterM etyFilter + return $! cached VS.++ stored + +instance {-# OVERLAPPABLE #-} Get s m c => Get (Cache n s c') m c where + {-# INLINE get #-} + get = withCached . get + {-# INLINE exists #-} + exists p = withCached . exists p + +instance {-# OVERLAPPABLE #-} Set s m c => Set (Cache n s c') m c where + {-# INLINE set #-} + set c = withCached . set c + +instance {-# OVERLAPPABLE #-} Destroy s m c => Destroy (Cache n s c') m c where + {-# INLINE destroy #-} + destroy p = withCached . destroy p + +instance {-# OVERLAPPABLE #-} Members s m c => Members (Cache n s c') m c where + {-# INLINE members #-} + members p = withCached $ members p + +-- ReadOnly +newtype ReadOnly s = ReadOnly {getReadOnly :: s} + deriving (Generic) + +{-# INLINE forceReadonly #-} +forceReadonly :: SystemT s m c -> SystemT (ReadOnly s) m c +forceReadonly = withReaderT getReadOnly + +type instance Components (ReadOnly s) = Components s + +instance Get s m c => Get (ReadOnly s) m c where + get = forceReadonly . get + exists p = forceReadonly . exists p + +instance Members s m c => Members (ReadOnly s) m c where + members = forceReadonly . members + +instance (Initialize m s, Functor m) => Initialize m (ReadOnly s) + +-- EntityCounter +newtype EntityCounter = EntityCounter (IORef Entity) + +type instance Components EntityCounter = '[] + +instance MonadIO m => Initialize m EntityCounter where + initialize = liftIO $ EntityCounter <$> newIORef (Entity 0) + +{-# INLINE newEntity #-} +newEntity :: + ( Generic w, + HasField w EntityCounter, + MonadIO m, + Set w m c + ) => + c -> + SystemT w m Entity +newEntity c = do + EntityCounter ref <- focusField ask + ety <- liftIO $ readIORef ref + set c ety + liftIO $ writeIORef ref (Entity . (+ 1) . unEntity $ ety) + pure ety diff --git a/src/Apecs/System.hs b/src/Apecs/System.hs new file mode 100644 index 0000000..bb8a719 --- /dev/null +++ b/src/Apecs/System.hs @@ -0,0 +1,79 @@ +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE Strict #-} +{-# LANGUAGE TypeApplications #-} +{-# LANGUAGE TypeFamilies #-} + +module Apecs.System + ( cmap, + cmapIf, + cmapM, + cmapM_, + ) +where + +import Apecs.Core +import Control.Monad +import Data.Proxy +import qualified Data.Vector.Storable as V + +{-# INLINE forMembers_ #-} +forMembers_ :: + (Monad m, Members w m c) => + Proxy c -> + (Entity -> SystemT w m a) -> + SystemT w m () +forMembers_ p f = members p >>= V.mapM_ f + +{-# INLINE cmap #-} +cmap :: + forall w m cx cy. + (Get w m cx, Set w m cy, Members w m cx, Monad m) => + (cx -> cy) -> + SystemT w m () +cmap f = + forMembers_ (Proxy @cx) $ \ety -> do + x <- get ety + set (f x) ety + +{-# INLINE cmapM_ #-} +cmapM_ :: + forall w m c. + (Get w m c, Members w m c, Monad m) => + (c -> SystemT w m ()) -> + SystemT w m () +cmapM_ f = forMembers_ (Proxy @c) $ get >=> f + +{-# INLINE cmapM #-} +cmapM :: + forall w m cx cy. + (Get w m cx, Set w m cy, Members w m cx, Monad m) => + (cx -> SystemT w m cy) -> + SystemT w m () +cmapM f = + forMembers_ (Proxy @cx) $ \ety -> do + x <- get ety + y <- f x + set y ety + +{-# INLINE cmapIf #-} +cmapIf :: + forall w m cp cx cy. + (Get w m cx, Get w m cp, Set w m cy, Members w m cp, Monad m) => + (cp -> Bool) -> + (cx -> cy) -> + SystemT w m () +cmapIf cond f = forMembers_ (Proxy @(cp, cx)) $ \ety -> do + p <- get ety + when (cond p) $ do + x <- get ety + set (f x) ety + +-- cfold :: +-- forall w m c a. +-- (Members w m c, Get w m c) => +-- (a -> c -> a) -> +-- a -> +-- SystemT w m a +-- cfold f a0 = do +-- members (Proxy @c) >>= mapM_ $ \ety -> undefined diff --git a/stack.yaml b/stack.yaml deleted file mode 100644 index 50e9382..0000000 --- a/stack.yaml +++ /dev/null @@ -1,21 +0,0 @@ -resolver: lts-13.28 - -packages: - - apecs - - apecs-physics - - apecs-gloss - - apecs-stm - - examples - -extra-deps: - - stm-containers-1.1.0.2 - - stm-hamt-1.2.0.2 - - primitive-extras-0.7.1 - -nix: - pure: false - packages: - - libGL - - freeglut - - mesa_glu - - zlib