pax_global_header00006660000000000000000000000064134765047610014527gustar00rootroot0000000000000052 comment=87340c216bf97554dc552371bbdecf283f7c540e Chipmunk2D-Chipmunk-7.0.3/000077500000000000000000000000001347650476100152565ustar00rootroot00000000000000Chipmunk2D-Chipmunk-7.0.3/.gitignore000066400000000000000000000015071347650476100172510ustar00rootroot00000000000000.DS_Store doc/index.html *.pbxuser *.perspectivev3 xcshareddata xcuserdata project.xcworkspace DerivedData build Chipmunk-Mac Chipmunk-iOS ObjectiveChipmunk-Mac ObjectiveChipmunk-iOS generated_docs CMakeCache.txt CMakeFiles Makefile cmake_install.cmake install_manifest.txt *.o *.obj *.dll *.so *.so.* *.dylib *.a *.exe *.lib chipmunk_demos # Visual studio *.opensdf *.opendb *.sdf *.suo *.pdb *.vcxproj.user msvc/vc10/demo/ipch/ msvc/vc10/chipmunk/Win32/ msvc/vc10/chipmunk/x64/ msvc/vc10/demo/Win32/ msvc/vc10/demo/x64/ msvc/vc12/chipmunk/Win32/ msvc/vc12/chipmunk/x64/ msvc/vc12/demo/Win32/ msvc/vc12/demo/x64/ msvc/vc13/chipmunk/Win32/ msvc/vc13/chipmunk/x64/ msvc/vc13/demo/Win32/ msvc/vc13/demo/x64/ msvc/vc14/demo/Win32/ msvc/vc14/demo/x64/ *.log *.tlog *.db .vscode Chipmunk2D-Chipmunk-7.0.3/CMakeLists.txt000066400000000000000000000043041347650476100200170ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.7) project(chipmunk) # to change the prefix, run cmake with the parameter: # -D CMAKE_INSTALL_PREFIX=/my/prefix # to change the build type, run cmake with the parameter: # -D CMAKE_BUILD_TYPE= # run "cmake --help-variable CMAKE_BUILD_TYPE" for details if(NOT CMAKE_BUILD_TYPE) SET(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel." FORCE) endif() # to manually select install locations of libraries and executables # -D LIB_INSTALL_DIR mylib # -D BIN_INSTALL_DIR newbin set(LIB_INSTALL_DIR lib CACHE STRING "Install location of libraries") set(BIN_INSTALL_DIR bin CACHE STRING "Install location of executables") # other options for the build, you can i.e. activate the shared library by passing # -D BUILD_SHARED=ON # to cmake. Other options analog if(ANDROID) option(BUILD_DEMOS "Build the demo applications" OFF) option(INSTALL_DEMOS "Install the demo applications" OFF) option(BUILD_SHARED "Build and install the shared library" ON) option(BUILD_STATIC "Build as static library" ON) option(INSTALL_STATIC "Install the static library" OFF) else() option(BUILD_DEMOS "Build the demo applications" ON) option(INSTALL_DEMOS "Install the demo applications" OFF) option(BUILD_SHARED "Build and install the shared library" ON) option(BUILD_STATIC "Build as static library" ON) option(INSTALL_STATIC "Install the static library" ON) endif() if(CMAKE_C_COMPILER_ID STREQUAL "Clang") option(FORCE_CLANG_BLOCKS "Force enable Clang blocks" YES) endif() # sanity checks... if(INSTALL_DEMOS) set(BUILD_DEMOS ON FORCE) endif() # these need the static lib too if(BUILD_DEMOS OR INSTALL_STATIC) set(BUILD_STATIC ON FORCE) endif() if(NOT MSVC) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99") # always use gnu99 if(FORCE_CLANG_BLOCKS) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fblocks") endif() set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -ffast-math") # extend release-profile with fast-math set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wall") # extend debug-profile with -Wall endif() add_subdirectory(src) if(BUILD_DEMOS) add_subdirectory(demo) endif() Chipmunk2D-Chipmunk-7.0.3/LICENSE.txt000066400000000000000000000021001347650476100170720ustar00rootroot00000000000000Copyright (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. Chipmunk2D-Chipmunk-7.0.3/README.textile000066400000000000000000000120421347650476100176120ustar00rootroot00000000000000!http://files.slembcke.net/chipmunk/logo/logo1_med.png! h2. NEW IN CHIPMUNK 7 Chipmunk 7 is complete and now includes the ARM NEON optimizations, the autogeometry code, and the mulithreaded solver. The latest "programming guide":http://chipmunk-physics.net/release/ChipmunkLatest-Docs/ is available. h2. ABOUT: Chipmunk2D is a simple, lightweight, fast and portable 2D rigid body physics library written in C. It's licensed under the unrestrictive, OSI approved MIT license. My aim is to give 2D developers access to the same quality of physics you find in newer 3D games. I hope you enjoy using Chipmunk2D! h2. FEATURES: * Designed specifically for 2D video games. * Circle, convex polygon, and beveled line segment collision primitives. * Multiple collision primitives can be attached to a single rigid body. * Fast broad phase collision detection by using a bounding box tree with great temporal coherence or a spatial hash. * Extremely fast impulse solving by utilizing Erin Catto's contact persistence algorithm. * Supports sleeping objects that have come to rest to reduce the CPU load. * Support for collision event callbacks based on user definable object types types. * Flexible collision filtering system with layers, exclusion groups and callbacks. ** Can be used to create all sorts of effects like one way platforms or buoyancy areas. (Examples included) * Supports nearest point, segment (raycasting), shape and bounding box queries to the collision detection system. * Collision impulses amounts can be retrieved for gameplay effects, sound effects, etc. * Large variety of joints - easily make vehicles, ragdolls, and more. * Joint callbacks. ** Can be used to easily implement breakable or animated joints. (Examples included) * Maintains a contact graph of all colliding objects. * Lightweight C99 implementation with no external dependencies outside of the Std. C library. * "Many language bindings available":http://chipmunk2d.net/bindingsAndPorts.php. * Simple, read the "documentation":http://chipmunk2d.net/documentation.php and see! * Unrestrictive MIT license h2. CONTRACTING: Howling Moon Software (my company) is available for contracting if you want to make the physics in your game really stand out. Given our unique experience with the library, we can help you use Chipmunk to its fullest potential. Feel free to contact us through our webpage: http://howlingmoonsoftware.com/ h2. BUILDING: Mac OS X: There is an included Xcode project file for building the static library and demo application. Alternatively you could use the CMake files or the macstatic.command script inside the xcode/ directory to build a static lib and package up the headers for you. iPhone: A native Objective-C API is included. The Xcode project can build a static library with all the proper compiler settings. Alternatively, you can just run iphonestatic.command in the xcode/ directory. It will build you a fat library compiled as release for the device and debug for the simulator. After running it, you can simply drop the Chipmunk-iOS directory into your iPhone project! UNIXes: A forum user was kind enough to make a set of CMake files for Chipmunk. This will require you to have CMake installed. To build run 'cmake .' then 'make'. This should build a dynamic library, a static library, and the demo application. A number of people have had build errors on Ubuntu due to not having GLUT or libxmu installed. Windows: Visual Studio projects are included in the msvc/ directory. While I try to make sure the MSVC 10 project is up to date, I don't have MSVC 9 to keep that project updated regularly. It may not work. I'd appreciate a hand fixing it if that's the case. h2. GET UP TO DATE: If you got the source from a point release download, you might want to consider getting the latest source from GitHub. Bugs are fixed and new features are added regularly. Big changes are done in branches and tested before merging them in it's rare for the point release downloads to be better or more bug free than the latest code. Head on over to "GitHub":https://github.com/slembcke/Chipmunk2D and experience the future TODAY! (Okay, so maybe it's not that exciting.) h2. GETTING STARTED: First of all, you can find the C API documentation in the doc/ directory. A good starting point is to take a look at the included Demo application. The demos all just set up a Chipmunk simulation space and the demo app draws the graphics directly out of that. This makes it easy to see how the Chipmunk API works without worrying about the graphics code. You are free to use the demo drawing routines in your own projects, though it is certainly not the recommended way of drawing Chipmunk objects as it pokes around at the undocumented/private APIs of Chipmunk. h2. SUPPORT: The best way to get support is to visit the "Chipmunk Forums":http://chipmunk2d.net/forum/. There are plenty of people around using Chipmunk on the just about every platform I've ever heard of. If you are working on a commercial project and want some more direct help, Howling Moon Software is also available for "contracting":http://howlingmoonsoftware.com/. Chipmunk2D-Chipmunk-7.0.3/TODO.txt000066400000000000000000000035651347650476100165750ustar00rootroot00000000000000Priorities: Basics tutorial. Simple top down player controls. * test tiny omega bug in cpvslerp(). User definable default winding. User definable fast collision filtering. Assertion (or warning?) for destroying a body with things still attached Bilinearly sampled image sampler function. (override the sample method to always take bilinear samples) RGB Image Sampler class. Reorganize Chimpnuk Pro directory structure. Too flat and confusing. Improve the ACD splitting plane algorithm. Fishing game Motorcycle in a spinning cage Squid thingy like TomorrowPlusX. Investigate getting better PhysicsEditor support. Unordered Fix solver issues with the groove joint. Use a slop tolerance on joint boundaries. Website things Don't make any mistakes: http://www.gamasutra.com/view/feature/185773/the_top_10_mistakes_tool_.php Several people want a Chipmunk Facebook page. figure out and redo front page. Chipmunk js examples. Future things to think about: breakable object support functions? Serialization Tests for the query methods Building bodies from shape collections. Per body iterations and timestep? Per body damping and gravity coefs? Easy callback programable joint? Top down racing game. (Need the callback constraint) cpBodyActivateStatic() should also activate joints? Chipmunk 7: Speculative contacts User definable constraint Custom contact constraint with rolling friction and per contact surface v. Serialization API changes, different body/shape instantiation. Collision handler objects with additional callbacks. Calculate contact anchors to get rid of contact pos. (needed for speculative contacts anyway) Mass property calculation changes. Change apply force/impulse and point velocity functions. Separate doxygen docs for Objective-C parts and C parts. Cocos2D xcode templates. Custom contacts using cpArbiter user data. Built in transform type. Chipmunk2D-Chipmunk-7.0.3/VERSION.txt000066400000000000000000000571051347650476100171540ustar00rootroot00000000000000What's new in 7.0.3: * MISC: Replacing GLFW with Sokol in the demo application. No need to push GLFW binaries and has a nice x-platform renderer to build on. * MISC: Fixed some 'const' warnings for MSCV. 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.Chipmunk2D-Chipmunk-7.0.3/android/000077500000000000000000000000001347650476100166765ustar00rootroot00000000000000Chipmunk2D-Chipmunk-7.0.3/android/Android.mk000066400000000000000000000077361347650476100206240ustar00rootroot00000000000000# Copyright (C) PlayControl Software, LLC. # Eric Wing # # This is a "Prebuilt" Android Makefile provided as an example/template (or direct use if no tweaking is required). # The idea is that you have already built the chipmunk .so and .a libraries using CMake. # Now you want to use those prebuilt libraries in your own project. # Android support prebuilt exteneral modules through its ndk-build system, but you need to have all the pieces setup and in the right place. This is one of those pieces. # # This file assumes you built all your chipmunk libs and put things into a directory structure like so: # # Android.mk (this file) # libs/armeabi/libchipmunk.a # libs/armeabi/libchipmunk.a # libs/armeabi-v7a/libchipmunk.a # libs/armeabi-v7a/libchipmunk.a # libs/x86/libchipmunk.a # libs/x86/libchipmunk.a # # include/chipmunk/chipmunk.h # ... (the other header files here) # # Note that this file is copied into the directory above libs and include. # Below is the code you need to make this Makefile export the correct headers, libraries, and flags for both dynamic and static versions. # LOCAL_PATH needs to be before include LOCAL_PATH := $(call my-dir) # For the dynamic library include $(CLEAR_VARS) # This is the name of module the caller will use in LOCAL_SHARED_LIBRARIES LOCAL_MODULE := chipmunk_shared LOCAL_SRC_FILES := libs/$(TARGET_ARCH_ABI)/libchipmunk.so LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include/chipmunk # Use LOCAL_EXPORT_CFLAGS to automatically export the correct flags (as necessary) to the calling module so the caller doesn't need to know the details. #LOCAL_EXPORT_CFLAGS := -DFOO=1 -DCP_USE_DOUBLES=1 -DCP_USE_CGPOINTS=0 # The .so is already linked so we don't really need to export this. #LOCAL_EXPORT_LDLIBS := -lm include $(PREBUILT_SHARED_LIBRARY) # For the static library include $(CLEAR_VARS) # This is the name of module the caller will use in LOCAL_STATIC_LIBRARIES LOCAL_MODULE := chipmunk_static LOCAL_SRC_FILES := libs/$(TARGET_ARCH_ABI)/libchipmunk.a # Use LOCAL_EXPORT_CFLAGS to automatically export the correct flags (as necessary) to the calling module so the caller doesn't need to know the details. #LOCAL_EXPORT_CFLAGS := -DFOO=1 -DCP_USE_DOUBLES=1 -DCP_USE_CGPOINTS=0 # Since the .a isn't linked, it's link dependencies must be passed on to the calling project. LOCAL_EXPORT_LDLIBS := -lm LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include/chipmunk include $(PREBUILT_STATIC_LIBRARY) # Two other pieces are needed to make this work which fall outside the scope of this file. # First, you must have a directory convention for the calling makefile. # So let's say we put all the above in a directory called Chipmunk2D. The layout looks like this: # Chipmunk2D/ # Android.mk (this file) # libs/armeabi/libchipmunk.a # libs/armeabi/libchipmunk.a # libs/armeabi-v7a/libchipmunk.a # libs/armeabi-v7a/libchipmunk.a # libs/x86/libchipmunk.a # libs/x86/libchipmunk.a # # include/chipmunk/chipmunk.h # ... (the other header files here) # So the calling makefile looks something like: # LOCAL_PATH := $(call my-dir) # include $(CLEAR_VARS) # LOCAL_MODULE := hello-jni # LOCAL_SRC_FILES := hello-jni.c # These are the LOCAL_MODULE names as defined in the prebuilt module's Android.mk. Define either shared or static, but not both. If you use dynamic, don't forget you need to do a System.loadLibrary("chipmunk") in your Java code. # #LOCAL_SHARED_LIBRARIES := chipmunk_shared # LOCAL_STATIC_LIBRARIES := chipmunk_static # include $(BUILD_SHARED_LIBRARY) # Android build system will look for folder `Chipmunk2D` in all import paths: # $(call import-module,Chipmunk2D) # ------ end ----- # Second, you need to set the environmental variable NDK_MODULE_PATH to list the directory containing Chipmunk2D. # So if Chipmunk2D is in /Library/Frameworks/Android/PrebuiltModules # export NDK_MODULE_PATH=/Library/Frameworks/Android/PrebuiltModules # Note that NDK_MODULE_PATH may contain multiple directories like the PATH environmental variable. Chipmunk2D-Chipmunk-7.0.3/codeblocks/000077500000000000000000000000001347650476100173665ustar00rootroot00000000000000Chipmunk2D-Chipmunk-7.0.3/codeblocks/Chipmunk.cbp000066400000000000000000000116321347650476100216350ustar00rootroot00000000000000 Chipmunk2D-Chipmunk-7.0.3/demo/000077500000000000000000000000001347650476100162025ustar00rootroot00000000000000Chipmunk2D-Chipmunk-7.0.3/demo/Bench.c000066400000000000000000000665431347650476100174030ustar00rootroot00000000000000#include "chipmunk/chipmunk.h" #include "chipmunk/chipmunk_unsafe.h" #include "ChipmunkDemo.h" #define ENABLE_HASTY 0 #if ENABLE_HASTY #include "chipmunk/cpHastySpace.h" static cpSpace *MakeHastySpace(){ cpSpace *space = cpHastySpaceNew(); cpHastySpaceSetThreads(space, 0); return space; } #define BENCH_SPACE_NEW MakeHastySpace #define BENCH_SPACE_FREE cpHastySpaceFree #define BENCH_SPACE_STEP cpHastySpaceStep #else #define BENCH_SPACE_NEW cpSpaceNew #define BENCH_SPACE_FREE cpSpaceFree #define BENCH_SPACE_STEP cpSpaceStep #endif const cpFloat bevel = 1.0; static cpVect simple_terrain_verts[] = { {350.00, 425.07}, {336.00, 436.55}, {272.00, 435.39}, {258.00, 427.63}, {225.28, 420.00}, {202.82, 396.00}, {191.81, 388.00}, {189.00, 381.89}, {173.00, 380.39}, {162.59, 368.00}, {150.47, 319.00}, {128.00, 311.55}, {119.14, 286.00}, {126.84, 263.00}, {120.56, 227.00}, {141.14, 178.00}, {137.52, 162.00}, {146.51, 142.00}, {156.23, 136.00}, {158.00, 118.27}, {170.00, 100.77}, {208.43, 84.00}, {224.00, 69.65}, {249.30, 68.00}, {257.00, 54.77}, {363.00, 45.94}, {374.15, 54.00}, {386.00, 69.60}, {413.00, 70.73}, {456.00, 84.89}, {468.09, 99.00}, {467.09, 123.00}, {464.92, 135.00}, {469.00, 141.03}, {497.00, 148.67}, {513.85, 180.00}, {509.56, 223.00}, {523.51, 247.00}, {523.00, 277.00}, {497.79, 311.00}, {478.67, 348.00}, {467.90, 360.00}, {456.76, 382.00}, {432.95, 389.00}, {417.00, 411.32}, {373.00, 433.19}, {361.00, 430.02}, {350.00, 425.07}, }; static int simple_terrain_count = sizeof(simple_terrain_verts)/sizeof(cpVect); //cpBody bodies[1000] = {}; //cpCircleShape circles[1000] = {}; static void add_circle(cpSpace *space, int index, cpFloat radius){ cpFloat mass = radius*radius/25.0f; cpBody *body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForCircle(mass, 0.0f, radius, cpvzero))); // cpBody *body = cpSpaceAddBody(space, cpBodyInit(&bodies[i], mass, cpMomentForCircle(mass, 0.0f, radius, cpvzero))); cpBodySetPosition(body, cpvmult(frand_unit_circle(), 180.0f)); cpShape *shape = cpSpaceAddShape(space, cpCircleShapeNew(body, radius, cpvzero)); // cpShape *shape = cpSpaceAddShape(space, cpCircleShapeInit(&circles[i], body, radius, cpvzero)); cpShapeSetElasticity(shape, 0.0); cpShapeSetFriction(shape, 0.9); } static void add_box(cpSpace *space, int index, cpFloat size){ cpFloat mass = size*size/100.0f; cpBody *body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForBox(mass, size, size))); // cpBody *body = cpSpaceAddBody(space, cpBodyInit(&bodies[i], mass, cpMomentForBox(mass, size, size))); cpBodySetPosition(body, cpvmult(frand_unit_circle(), 180.0f)); cpShape *shape = cpSpaceAddShape(space, cpBoxShapeNew(body, size - bevel*2, size - bevel*2, 0.0)); cpPolyShapeSetRadius(shape, bevel); cpShapeSetElasticity(shape, 0.0); cpShapeSetFriction(shape, 0.9); } static void add_hexagon(cpSpace *space, int index, cpFloat radius){ cpVect hexagon[6]; for(int i=0; i<6; i++){ cpFloat angle = -CP_PI*2.0f*i/6.0f; hexagon[i] = cpvmult(cpv(cos(angle), sin(angle)), radius - bevel); } cpFloat mass = radius*radius; cpBody *body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForPoly(mass, 6, hexagon, cpvzero, 0.0f))); cpBodySetPosition(body, cpvmult(frand_unit_circle(), 180.0f)); cpShape *shape = cpSpaceAddShape(space, cpPolyShapeNew(body, 6, hexagon, cpTransformIdentity, bevel)); cpShapeSetElasticity(shape, 0.0); cpShapeSetFriction(shape, 0.9); } static cpSpace * SetupSpace_simpleTerrain(){ cpSpace *space = BENCH_SPACE_NEW(); cpSpaceSetIterations(space, 10); cpSpaceSetGravity(space, cpv(0, -100)); cpSpaceSetCollisionSlop(space, 0.5f); cpVect offset = cpv(-320, -240); for(int i=0; i<(simple_terrain_count - 1); i++){ cpVect a = simple_terrain_verts[i], b = simple_terrain_verts[i+1]; cpSpaceAddShape(space, cpSegmentShapeNew(cpSpaceGetStaticBody(space), cpvadd(a, offset), cpvadd(b, offset), 0.0f)); } return space; } // SimpleTerrain constant sized objects static cpSpace *init_SimpleTerrainCircles_1000(void){ cpSpace *space = SetupSpace_simpleTerrain(); for(int i=0; i<1000; i++) add_circle(space, i, 5.0f); return space; } static cpSpace *init_SimpleTerrainCircles_500(void){ cpSpace *space = SetupSpace_simpleTerrain(); for(int i=0; i<500; i++) add_circle(space, i, 5.0f); return space; } static cpSpace *init_SimpleTerrainCircles_100(void){ cpSpace *space = SetupSpace_simpleTerrain(); for(int i=0; i<100; i++) add_circle(space, i, 5.0f); return space; } static cpSpace *init_SimpleTerrainBoxes_1000(void){ cpSpace *space = SetupSpace_simpleTerrain(); for(int i=0; i<1000; i++) add_box(space, i, 10.0f); return space; } static cpSpace *init_SimpleTerrainBoxes_500(void){ cpSpace *space = SetupSpace_simpleTerrain(); for(int i=0; i<500; i++) add_box(space, i, 10.0f); return space; } static cpSpace *init_SimpleTerrainBoxes_100(void){ cpSpace *space = SetupSpace_simpleTerrain(); for(int i=0; i<100; i++) add_box(space, i, 10.0f); return space; } static cpSpace *init_SimpleTerrainHexagons_1000(void){ cpSpace *space = SetupSpace_simpleTerrain(); for(int i=0; i<1000; i++) add_hexagon(space, i, 5.0f); return space; } static cpSpace *init_SimpleTerrainHexagons_500(void){ cpSpace *space = SetupSpace_simpleTerrain(); for(int i=0; i<500; i++) add_hexagon(space, i, 5.0f); return space; } static cpSpace *init_SimpleTerrainHexagons_100(void){ cpSpace *space = SetupSpace_simpleTerrain(); for(int i=0; i<100; i++) add_hexagon(space, i, 5.0f); return space; } // SimpleTerrain variable sized objects static cpFloat rand_size(){ return cpfpow(1.5, cpflerp(-1.5, 3.5, frand())); } static cpSpace *init_SimpleTerrainVCircles_200(void){ cpSpace *space = SetupSpace_simpleTerrain(); for(int i=0; i<200; i++) add_circle(space, i, 5.0f*rand_size()); return space; } static cpSpace *init_SimpleTerrainVBoxes_200(void){ cpSpace *space = SetupSpace_simpleTerrain(); for(int i=0; i<200; i++) add_box(space, i, 8.0f*rand_size()); return space; } static cpSpace *init_SimpleTerrainVHexagons_200(void){ cpSpace *space = SetupSpace_simpleTerrain(); for(int i=0; i<200; i++) add_hexagon(space, i, 5.0f*rand_size()); return space; } // ComplexTerrain static cpVect complex_terrain_verts[] = { { 46.78, 479.00}, { 35.00, 475.63}, { 27.52, 469.00}, { 23.52, 455.00}, { 23.78, 441.00}, { 28.41, 428.00}, { 49.61, 394.00}, { 59.00, 381.56}, { 80.00, 366.03}, { 81.46, 358.00}, { 86.31, 350.00}, { 77.74, 320.00}, { 70.26, 278.00}, { 67.51, 270.00}, { 58.86, 260.00}, { 57.19, 247.00}, { 38.00, 235.60}, { 25.76, 221.00}, { 24.58, 209.00}, { 27.63, 202.00}, { 31.28, 198.00}, { 40.00, 193.72}, { 48.00, 193.73}, { 55.00, 196.70}, { 62.10, 204.00}, { 71.00, 209.04}, { 79.00, 206.55}, { 88.00, 206.81}, { 95.88, 211.00}, {103.00, 220.49}, {131.00, 220.51}, {137.00, 222.66}, {143.08, 228.00}, {146.22, 234.00}, {147.08, 241.00}, {145.45, 248.00}, {142.31, 253.00}, {132.00, 259.30}, {115.00, 259.70}, {109.28, 270.00}, {112.91, 296.00}, {119.69, 324.00}, {129.00, 336.26}, {141.00, 337.59}, {153.00, 331.57}, {175.00, 325.74}, {188.00, 325.19}, {235.00, 317.46}, {250.00, 317.19}, {255.00, 309.12}, {262.62, 302.00}, {262.21, 295.00}, {248.00, 273.59}, {229.00, 257.93}, {221.00, 255.48}, {215.00, 251.59}, {210.79, 246.00}, {207.47, 234.00}, {203.25, 227.00}, {179.00, 205.90}, {148.00, 189.54}, {136.00, 181.45}, {120.00, 180.31}, {110.00, 181.65}, { 95.00, 179.31}, { 63.00, 166.96}, { 50.00, 164.23}, { 31.00, 154.49}, { 19.76, 145.00}, { 15.96, 136.00}, { 16.65, 127.00}, { 20.57, 120.00}, { 28.00, 114.63}, { 40.00, 113.67}, { 65.00, 127.22}, { 73.00, 128.69}, { 81.96, 120.00}, { 77.58, 103.00}, { 78.18, 92.00}, { 59.11, 77.00}, { 52.00, 67.29}, { 31.29, 55.00}, { 25.67, 47.00}, { 24.65, 37.00}, { 27.82, 29.00}, { 35.00, 22.55}, { 44.00, 20.35}, { 49.00, 20.81}, { 61.00, 25.69}, { 79.00, 37.81}, { 88.00, 49.64}, { 97.00, 56.65}, {109.00, 49.61}, {143.00, 38.96}, {197.00, 37.27}, {215.00, 35.30}, {222.00, 36.65}, {228.42, 41.00}, {233.30, 49.00}, {234.14, 57.00}, {231.00, 65.80}, {224.00, 72.38}, {218.00, 74.50}, {197.00, 76.62}, {145.00, 78.81}, {123.00, 87.41}, {117.59, 98.00}, {117.79, 104.00}, {119.00, 106.23}, {138.73, 120.00}, {148.00, 129.50}, {158.50, 149.00}, {203.93, 175.00}, {229.00, 196.60}, {238.16, 208.00}, {245.20, 221.00}, {275.45, 245.00}, {289.00, 263.24}, {303.60, 287.00}, {312.00, 291.57}, {339.25, 266.00}, {366.33, 226.00}, {363.43, 216.00}, {364.13, 206.00}, {353.00, 196.72}, {324.00, 181.05}, {307.00, 169.63}, {274.93, 156.00}, {256.00, 152.48}, {228.00, 145.13}, {221.09, 142.00}, {214.87, 135.00}, {212.67, 127.00}, {213.81, 119.00}, {219.32, 111.00}, {228.00, 106.52}, {236.00, 106.39}, {290.00, 119.40}, {299.33, 114.00}, {300.52, 109.00}, {300.30, 53.00}, {301.46, 47.00}, {305.00, 41.12}, {311.00, 36.37}, {317.00, 34.43}, {325.00, 34.81}, {334.90, 41.00}, {339.45, 50.00}, {339.82, 132.00}, {346.09, 139.00}, {350.00, 150.26}, {380.00, 167.38}, {393.00, 166.48}, {407.00, 155.54}, {430.00, 147.30}, {437.78, 135.00}, {433.13, 122.00}, {410.23, 78.00}, {401.59, 69.00}, {393.48, 56.00}, {392.80, 44.00}, {395.50, 38.00}, {401.00, 32.49}, {409.00, 29.41}, {420.00, 30.84}, {426.92, 36.00}, {432.32, 44.00}, {439.49, 51.00}, {470.13, 108.00}, {475.71, 124.00}, {483.00, 130.11}, {488.00, 139.43}, {529.00, 139.40}, {536.00, 132.52}, {543.73, 129.00}, {540.47, 115.00}, {541.11, 100.00}, {552.18, 68.00}, {553.78, 47.00}, {559.00, 39.76}, {567.00, 35.52}, {577.00, 35.45}, {585.00, 39.58}, {591.38, 50.00}, {591.67, 66.00}, {590.31, 79.00}, {579.76, 109.00}, {582.25, 119.00}, {583.66, 136.00}, {586.45, 143.00}, {586.44, 151.00}, {580.42, 168.00}, {577.15, 173.00}, {572.00, 177.13}, {564.00, 179.49}, {478.00, 178.81}, {443.00, 184.76}, {427.10, 190.00}, {424.00, 192.11}, {415.94, 209.00}, {408.82, 228.00}, {405.82, 241.00}, {411.00, 250.82}, {415.00, 251.50}, {428.00, 248.89}, {469.00, 246.29}, {505.00, 246.49}, {533.00, 243.60}, {541.87, 248.00}, {547.55, 256.00}, {548.48, 267.00}, {544.00, 276.00}, {534.00, 282.24}, {513.00, 285.46}, {468.00, 285.76}, {402.00, 291.70}, {392.00, 290.29}, {377.00, 294.46}, {367.00, 294.43}, {356.44, 304.00}, {354.22, 311.00}, {362.00, 321.36}, {390.00, 322.44}, {433.00, 330.16}, {467.00, 332.76}, {508.00, 347.64}, {522.00, 357.67}, {528.00, 354.46}, {536.00, 352.96}, {546.06, 336.00}, {553.47, 306.00}, {564.19, 282.00}, {567.84, 268.00}, {578.72, 246.00}, {585.00, 240.97}, {592.00, 238.91}, {600.00, 239.72}, {606.00, 242.82}, {612.36, 251.00}, {613.35, 263.00}, {588.75, 324.00}, {583.25, 350.00}, {572.12, 370.00}, {575.45, 378.00}, {575.20, 388.00}, {589.00, 393.81}, {599.20, 404.00}, {607.14, 416.00}, {609.96, 430.00}, {615.45, 441.00}, {613.44, 462.00}, {610.48, 469.00}, {603.00, 475.63}, {590.96, 479.00}, }; static int complex_terrain_count = sizeof(complex_terrain_verts)/sizeof(cpVect); static cpSpace *init_ComplexTerrainCircles_1000(void){ cpSpace *space = BENCH_SPACE_NEW(); cpSpaceSetIterations(space, 10); cpSpaceSetGravity(space, cpv(0, -100)); cpSpaceSetCollisionSlop(space, 0.5f); cpVect offset = cpv(-320, -240); for(int i=0; i<(complex_terrain_count - 1); i++){ cpVect a = complex_terrain_verts[i], b = complex_terrain_verts[i+1]; cpSpaceAddShape(space, cpSegmentShapeNew(cpSpaceGetStaticBody(space), cpvadd(a, offset), cpvadd(b, offset), 0.0f)); } for(int i=0; i<1000; i++){ cpFloat radius = 5.0f; cpFloat mass = radius*radius; cpBody *body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForCircle(mass, 0.0f, radius, cpvzero))); cpBodySetPosition(body, cpvadd(cpvmult(frand_unit_circle(), 180.0f), cpv(0.0f, 300.0f))); cpShape *shape = cpSpaceAddShape(space, cpCircleShapeNew(body, radius, cpvzero)); cpShapeSetElasticity(shape, 0.0); cpShapeSetFriction(shape, 0.0); } return space; } static cpSpace *init_ComplexTerrainHexagons_1000(void){ cpSpace *space = BENCH_SPACE_NEW(); cpSpaceSetIterations(space, 10); cpSpaceSetGravity(space, cpv(0, -100)); cpSpaceSetCollisionSlop(space, 0.5f); cpVect offset = cpv(-320, -240); for(int i=0; i<(complex_terrain_count - 1); i++){ cpVect a = complex_terrain_verts[i], b = complex_terrain_verts[i+1]; cpSpaceAddShape(space, cpSegmentShapeNew(cpSpaceGetStaticBody(space), cpvadd(a, offset), cpvadd(b, offset), 0.0f)); } cpFloat radius = 5.0f; cpVect hexagon[6]; for(int i=0; i<6; i++){ cpFloat angle = -CP_PI*2.0f*i/6.0f; hexagon[i] = cpvmult(cpv(cos(angle), sin(angle)), radius - bevel); } for(int i=0; i<1000; i++){ cpFloat mass = radius*radius; cpBody *body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForPoly(mass, 6, hexagon, cpvzero, 0.0f))); cpBodySetPosition(body, cpvadd(cpvmult(frand_unit_circle(), 180.0f), cpv(0.0f, 300.0f))); cpShape *shape = cpSpaceAddShape(space, cpPolyShapeNew(body, 6, hexagon, cpTransformIdentity, bevel)); cpShapeSetElasticity(shape, 0.0); cpShapeSetFriction(shape, 0.0); } return space; } // BouncyTerrain static cpVect bouncy_terrain_verts[] = { {537.18, 23.00}, {520.50, 36.00}, {501.53, 63.00}, {496.14, 76.00}, {498.86, 86.00}, {504.00, 90.51}, {508.00, 91.36}, {508.77, 84.00}, {513.00, 77.73}, {519.00, 74.48}, {530.00, 74.67}, {545.00, 54.65}, {554.00, 48.77}, {562.00, 46.39}, {568.00, 45.94}, {568.61, 47.00}, {567.94, 55.00}, {571.27, 64.00}, {572.92, 80.00}, {572.00, 81.39}, {563.00, 79.93}, {556.00, 82.69}, {551.49, 88.00}, {549.00, 95.76}, {538.00, 93.40}, {530.00, 102.38}, {523.00, 104.00}, {517.00, 103.02}, {516.22, 109.00}, {518.96, 116.00}, {526.00, 121.15}, {534.00, 116.48}, {543.00, 116.77}, {549.28, 121.00}, {554.00, 130.17}, {564.00, 125.67}, {575.60, 129.00}, {573.31, 121.00}, {567.77, 111.00}, {575.00, 106.47}, {578.51, 102.00}, {580.25, 95.00}, {577.98, 87.00}, {582.00, 85.71}, {597.00, 89.46}, {604.80, 95.00}, {609.28, 104.00}, {610.55, 116.00}, {609.30, 125.00}, {600.80, 142.00}, {597.31, 155.00}, {584.00, 167.23}, {577.86, 175.00}, {583.52, 184.00}, {582.64, 195.00}, {591.00, 196.56}, {597.81, 201.00}, {607.45, 219.00}, {607.51, 246.00}, {600.00, 275.46}, {588.00, 267.81}, {579.00, 264.91}, {557.00, 264.41}, {552.98, 259.00}, {548.00, 246.18}, {558.00, 247.12}, {565.98, 244.00}, {571.10, 237.00}, {571.61, 229.00}, {568.25, 222.00}, {562.00, 217.67}, {544.00, 213.93}, {536.73, 214.00}, {535.60, 204.00}, {539.69, 181.00}, {542.84, 171.00}, {550.43, 161.00}, {540.00, 156.27}, {536.62, 152.00}, {534.70, 146.00}, {527.00, 141.88}, {518.59, 152.00}, {514.51, 160.00}, {510.33, 175.00}, {519.38, 183.00}, {520.52, 194.00}, {516.00, 201.27}, {505.25, 206.00}, {507.57, 223.00}, {519.90, 260.00}, {529.00, 260.48}, {534.00, 262.94}, {538.38, 268.00}, {540.00, 275.00}, {537.06, 284.00}, {530.00, 289.23}, {520.00, 289.23}, {513.00, 284.18}, {509.71, 286.00}, {501.69, 298.00}, {501.56, 305.00}, {504.30, 311.00}, {512.00, 316.43}, {521.00, 316.42}, {525.67, 314.00}, {535.00, 304.98}, {562.00, 294.80}, {573.00, 294.81}, {587.52, 304.00}, {600.89, 310.00}, {596.96, 322.00}, {603.28, 327.00}, {606.52, 333.00}, {605.38, 344.00}, {597.65, 352.00}, {606.36, 375.00}, {607.16, 384.00}, {603.40, 393.00}, {597.00, 398.14}, {577.00, 386.15}, {564.35, 373.00}, {565.21, 364.00}, {562.81, 350.00}, {553.00, 346.06}, {547.48, 338.00}, {547.48, 330.00}, {550.00, 323.30}, {544.00, 321.53}, {537.00, 322.70}, {532.00, 326.23}, {528.89, 331.00}, {527.83, 338.00}, {533.02, 356.00}, {542.00, 360.73}, {546.68, 369.00}, {545.38, 379.00}, {537.58, 386.00}, {537.63, 388.00}, {555.00, 407.47}, {563.00, 413.52}, {572.57, 418.00}, {582.72, 426.00}, {578.00, 431.12}, {563.21, 440.00}, {558.00, 449.27}, {549.00, 452.94}, {541.00, 451.38}, {536.73, 448.00}, {533.00, 441.87}, {520.00, 437.96}, {514.00, 429.69}, {490.00, 415.15}, {472.89, 399.00}, {472.03, 398.00}, {474.00, 396.71}, {486.00, 393.61}, {492.00, 385.85}, {492.00, 376.15}, {489.04, 371.00}, {485.00, 368.11}, {480.00, 376.27}, {472.00, 379.82}, {463.00, 378.38}, {455.08, 372.00}, {446.00, 377.69}, {439.00, 385.24}, {436.61, 391.00}, {437.52, 404.00}, {440.00, 409.53}, {463.53, 433.00}, {473.80, 441.00}, {455.00, 440.30}, {443.00, 436.18}, {436.00, 431.98}, {412.00, 440.92}, {397.00, 442.46}, {393.59, 431.00}, {393.71, 412.00}, {400.00, 395.10}, {407.32, 387.00}, {408.54, 380.00}, {407.42, 375.00}, {403.97, 370.00}, {399.00, 366.74}, {393.00, 365.68}, {391.23, 374.00}, {387.00, 380.27}, {381.00, 383.52}, {371.56, 384.00}, {364.98, 401.00}, {362.96, 412.00}, {363.63, 435.00}, {345.00, 433.55}, {344.52, 442.00}, {342.06, 447.00}, {337.00, 451.38}, {330.00, 453.00}, {325.00, 452.23}, {318.00, 448.17}, {298.00, 453.70}, {284.00, 451.49}, {278.62, 449.00}, {291.47, 408.00}, {291.77, 398.00}, {301.00, 393.83}, {305.00, 393.84}, {305.60, 403.00}, {310.00, 409.47}, {318.00, 413.07}, {325.00, 412.40}, {332.31, 407.00}, {335.07, 400.00}, {334.40, 393.00}, {329.00, 385.69}, {319.00, 382.79}, {301.00, 389.23}, {289.00, 389.97}, {265.00, 389.82}, {251.00, 385.85}, {245.00, 389.23}, {239.00, 389.94}, {233.00, 388.38}, {226.00, 382.04}, {206.00, 374.75}, {206.00, 394.00}, {204.27, 402.00}, {197.00, 401.79}, {191.00, 403.49}, {186.53, 407.00}, {183.60, 412.00}, {183.60, 422.00}, {189.00, 429.31}, {196.00, 432.07}, {203.00, 431.40}, {209.47, 427.00}, {213.00, 419.72}, {220.00, 420.21}, {227.00, 418.32}, {242.00, 408.41}, {258.98, 409.00}, {250.00, 435.43}, {239.00, 438.78}, {223.00, 448.19}, {209.00, 449.70}, {205.28, 456.00}, {199.00, 460.23}, {190.00, 460.52}, {182.73, 456.00}, {178.00, 446.27}, {160.00, 441.42}, {148.35, 435.00}, {149.79, 418.00}, {157.72, 401.00}, {161.00, 396.53}, {177.00, 385.00}, {180.14, 380.00}, {181.11, 374.00}, {180.00, 370.52}, {170.00, 371.68}, {162.72, 368.00}, {158.48, 361.00}, {159.56, 349.00}, {154.00, 342.53}, {146.00, 339.85}, {136.09, 343.00}, {130.64, 351.00}, {131.74, 362.00}, {140.61, 374.00}, {130.68, 387.00}, {120.75, 409.00}, {118.09, 421.00}, {117.92, 434.00}, {100.00, 432.40}, { 87.00, 427.48}, { 81.59, 423.00}, { 73.64, 409.00}, { 72.57, 398.00}, { 74.62, 386.00}, { 78.80, 378.00}, { 88.00, 373.43}, { 92.49, 367.00}, { 93.32, 360.00}, { 91.30, 353.00}, {103.00, 342.67}, {109.00, 343.10}, {116.00, 340.44}, {127.33, 330.00}, {143.00, 327.24}, {154.30, 322.00}, {145.00, 318.06}, {139.77, 311.00}, {139.48, 302.00}, {144.95, 293.00}, {143.00, 291.56}, {134.00, 298.21}, {118.00, 300.75}, {109.40, 305.00}, { 94.67, 319.00}, { 88.00, 318.93}, { 81.00, 321.69}, { 67.24, 333.00}, { 56.68, 345.00}, { 53.00, 351.40}, { 47.34, 333.00}, { 50.71, 314.00}, { 56.57, 302.00}, { 68.00, 287.96}, { 91.00, 287.24}, {110.00, 282.36}, {133.80, 271.00}, {147.34, 256.00}, {156.47, 251.00}, {157.26, 250.00}, {154.18, 242.00}, {154.48, 236.00}, {158.72, 229.00}, {166.71, 224.00}, {170.15, 206.00}, {170.19, 196.00}, {167.24, 188.00}, {160.00, 182.67}, {150.00, 182.66}, {143.60, 187.00}, {139.96, 195.00}, {139.50, 207.00}, {136.45, 221.00}, {136.52, 232.00}, {133.28, 238.00}, {129.00, 241.38}, {119.00, 243.07}, {115.00, 246.55}, {101.00, 253.16}, { 86.00, 257.32}, { 63.00, 259.24}, { 57.00, 257.31}, { 50.54, 252.00}, { 47.59, 247.00}, { 46.30, 240.00}, { 47.58, 226.00}, { 50.00, 220.57}, { 58.00, 226.41}, { 69.00, 229.17}, { 79.00, 229.08}, { 94.50, 225.00}, {100.21, 231.00}, {107.00, 233.47}, {107.48, 224.00}, {109.94, 219.00}, {115.00, 214.62}, {122.57, 212.00}, {116.00, 201.49}, {104.00, 194.57}, { 90.00, 194.04}, { 79.00, 198.21}, { 73.00, 198.87}, { 62.68, 191.00}, { 62.58, 184.00}, { 64.42, 179.00}, { 75.00, 167.70}, { 80.39, 157.00}, { 68.79, 140.00}, { 61.67, 126.00}, { 61.47, 117.00}, { 64.43, 109.00}, { 63.10, 96.00}, { 56.48, 82.00}, { 48.00, 73.88}, { 43.81, 66.00}, { 43.81, 56.00}, { 50.11, 46.00}, { 59.00, 41.55}, { 71.00, 42.64}, { 78.00, 36.77}, { 83.00, 34.75}, { 99.00, 34.32}, {117.00, 38.92}, {133.00, 55.15}, {142.00, 50.70}, {149.74, 51.00}, {143.55, 68.00}, {153.28, 74.00}, {156.23, 79.00}, {157.00, 84.00}, {156.23, 89.00}, {153.28, 94.00}, {144.58, 99.00}, {151.52, 112.00}, {151.51, 124.00}, {150.00, 126.36}, {133.00, 130.25}, {126.71, 125.00}, {122.00, 117.25}, {114.00, 116.23}, {107.73, 112.00}, {104.48, 106.00}, {104.32, 99.00}, {106.94, 93.00}, {111.24, 89.00}, {111.60, 85.00}, {107.24, 73.00}, {102.00, 67.57}, { 99.79, 67.00}, { 99.23, 76.00}, { 95.00, 82.27}, { 89.00, 85.52}, { 79.84, 86.00}, { 86.73, 114.00}, { 98.00, 136.73}, { 99.00, 137.61}, {109.00, 135.06}, {117.00, 137.94}, {122.52, 146.00}, {122.94, 151.00}, {121.00, 158.58}, {134.00, 160.97}, {153.00, 157.45}, {171.30, 150.00}, {169.06, 142.00}, {169.77, 136.00}, {174.00, 129.73}, {181.46, 126.00}, {182.22, 120.00}, {182.20, 111.00}, {180.06, 101.00}, {171.28, 85.00}, {171.75, 80.00}, {182.30, 53.00}, {189.47, 50.00}, {190.62, 38.00}, {194.00, 33.73}, {199.00, 30.77}, {208.00, 30.48}, {216.00, 34.94}, {224.00, 31.47}, {240.00, 30.37}, {247.00, 32.51}, {249.77, 35.00}, {234.75, 53.00}, {213.81, 93.00}, {212.08, 99.00}, {213.00, 101.77}, {220.00, 96.77}, {229.00, 96.48}, {236.28, 101.00}, {240.00, 107.96}, {245.08, 101.00}, {263.00, 65.32}, {277.47, 48.00}, {284.00, 47.03}, {286.94, 41.00}, {292.00, 36.62}, {298.00, 35.06}, {304.00, 35.77}, {314.00, 43.81}, {342.00, 32.56}, {359.00, 31.32}, {365.00, 32.57}, {371.00, 36.38}, {379.53, 48.00}, {379.70, 51.00}, {356.00, 52.19}, {347.00, 54.74}, {344.38, 66.00}, {341.00, 70.27}, {335.00, 73.52}, {324.00, 72.38}, {317.00, 65.75}, {313.00, 67.79}, {307.57, 76.00}, {315.00, 78.62}, {319.28, 82.00}, {322.23, 87.00}, {323.00, 94.41}, {334.00, 92.49}, {347.00, 87.47}, {349.62, 80.00}, {353.00, 75.73}, {359.00, 72.48}, {366.00, 72.32}, {372.00, 74.94}, {377.00, 81.34}, {382.00, 83.41}, {392.00, 83.40}, {399.00, 79.15}, {404.00, 85.74}, {411.00, 85.06}, {417.00, 86.62}, {423.38, 93.00}, {425.05, 104.00}, {438.00, 110.35}, {450.00, 112.17}, {452.62, 103.00}, {456.00, 98.73}, {462.00, 95.48}, {472.00, 95.79}, {471.28, 92.00}, {464.00, 84.62}, {445.00, 80.39}, {436.00, 75.33}, {428.00, 68.46}, {419.00, 68.52}, {413.00, 65.27}, {408.48, 58.00}, {409.87, 46.00}, {404.42, 39.00}, {408.00, 33.88}, {415.00, 29.31}, {429.00, 26.45}, {455.00, 28.77}, {470.00, 33.81}, {482.00, 42.16}, {494.00, 46.85}, {499.65, 36.00}, {513.00, 25.95}, {529.00, 22.42}, {537.18, 23.00}, }; static int bouncy_terrain_count = sizeof(bouncy_terrain_verts)/sizeof(cpVect); static cpSpace *init_BouncyTerrainCircles_500(void){ cpSpace *space = BENCH_SPACE_NEW(); cpSpaceSetIterations(space, 10); cpVect offset = cpv(-320, -240); for(int i=0; i<(bouncy_terrain_count - 1); i++){ cpVect a = bouncy_terrain_verts[i], b = bouncy_terrain_verts[i+1]; cpShape *shape = cpSpaceAddShape(space, cpSegmentShapeNew(cpSpaceGetStaticBody(space), cpvadd(a, offset), cpvadd(b, offset), 0.0f)); cpShapeSetElasticity(shape, 1.0); } for(int i=0; i<500; i++){ cpFloat radius = 5.0f; cpFloat mass = radius*radius; cpBody *body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForCircle(mass, 0.0f, radius, cpvzero))); cpBodySetPosition(body, cpvadd(cpvmult(frand_unit_circle(), 130.0f), cpvzero)); cpBodySetVelocity(body, cpvmult(frand_unit_circle(), 50.0f)); cpShape *shape = cpSpaceAddShape(space, cpCircleShapeNew(body, radius, cpvzero)); cpShapeSetElasticity(shape, 1.0); } return space; } static cpSpace *init_BouncyTerrainHexagons_500(void){ cpSpace *space = BENCH_SPACE_NEW(); cpSpaceSetIterations(space, 10); cpVect offset = cpv(-320, -240); for(int i=0; i<(bouncy_terrain_count - 1); i++){ cpVect a = bouncy_terrain_verts[i], b = bouncy_terrain_verts[i+1]; cpShape *shape = cpSpaceAddShape(space, cpSegmentShapeNew(cpSpaceGetStaticBody(space), cpvadd(a, offset), cpvadd(b, offset), 0.0f)); cpShapeSetElasticity(shape, 1.0); } cpFloat radius = 5.0f; cpVect hexagon[6]; for(int i=0; i<6; i++){ cpFloat angle = -CP_PI*2.0f*i/6.0f; hexagon[i] = cpvmult(cpv(cos(angle), sin(angle)), radius - bevel); } for(int i=0; i<500; i++){ cpFloat mass = radius*radius; cpBody *body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForPoly(mass, 6, hexagon, cpvzero, 0.0f))); cpBodySetPosition(body, cpvadd(cpvmult(frand_unit_circle(), 130.0f), cpvzero)); cpBodySetVelocity(body, cpvmult(frand_unit_circle(), 50.0f)); cpShape *shape = cpSpaceAddShape(space, cpPolyShapeNew(body, 6, hexagon, cpTransformIdentity, bevel)); cpShapeSetElasticity(shape, 1.0); } return space; } // No collisions static cpBool NoCollide_begin(cpArbiter *arb, cpSpace *space, void *data){ abort(); return cpTrue; } static cpSpace *init_NoCollide(void){ cpSpace *space = BENCH_SPACE_NEW(); cpSpaceSetIterations(space, 10); cpCollisionHandler *handler = cpSpaceAddCollisionHandler(space, 2, 2); handler->beginFunc = NoCollide_begin; float radius = 4.5f; cpShapeSetElasticity(cpSpaceAddShape(space, cpSegmentShapeNew(cpSpaceGetStaticBody(space), cpv(-330-radius, -250-radius), cpv( 330+radius, -250-radius), 0.0f)), 1.0f); cpShapeSetElasticity(cpSpaceAddShape(space, cpSegmentShapeNew(cpSpaceGetStaticBody(space), cpv( 330+radius, 250+radius), cpv( 330+radius, -250-radius), 0.0f)), 1.0f); cpShapeSetElasticity(cpSpaceAddShape(space, cpSegmentShapeNew(cpSpaceGetStaticBody(space), cpv( 330+radius, 250+radius), cpv(-330-radius, 250+radius), 0.0f)), 1.0f); cpShapeSetElasticity(cpSpaceAddShape(space, cpSegmentShapeNew(cpSpaceGetStaticBody(space), cpv(-330-radius, -250-radius), cpv(-330-radius, 250+radius), 0.0f)), 1.0f); for(int x=-320; x<=320; x+=20){ for(int y=-240; y<=240; y+=20){ cpSpaceAddShape(space, cpCircleShapeNew(cpSpaceGetStaticBody(space), radius, cpv(x, y))); } } for(int y=10-240; y<=240; y+=40){ cpFloat mass = 7.0f; cpBody *body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForCircle(mass, 0.0f, radius, cpvzero))); cpBodySetPosition(body, cpv(-320.0f, y)); cpBodySetVelocity(body, cpv(100.0f, 0.0f)); cpShape *shape = cpSpaceAddShape(space, cpCircleShapeNew(body, radius, cpvzero)); cpShapeSetElasticity(shape, 1.0); cpShapeSetCollisionType(shape, 2); } for(int x=30-320; x<=320; x+=40){ cpFloat mass = 7.0f; cpBody *body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForCircle(mass, 0.0f, radius, cpvzero))); cpBodySetPosition(body, cpv(x, -240.0f)); cpBodySetVelocity(body, cpv(0.0f, 100.0f)); cpShape *shape = cpSpaceAddShape(space, cpCircleShapeNew(body, radius, cpvzero)); cpShapeSetElasticity(shape, 1.0); cpShapeSetCollisionType(shape, 2); } return space; } // TODO ideas: // addition/removal // Memory usage? (too small to matter?) // http://forums.tigsource.com/index.php?topic=18077.msg518578#msg518578 // Build benchmark list static void update(cpSpace *space, double dt){ BENCH_SPACE_STEP(space, dt); } static void destroy(cpSpace *space){ ChipmunkDemoFreeSpaceChildren(space); BENCH_SPACE_FREE(space); } // Make a second demo declaration for this demo to use in the regular demo set. ChipmunkDemo BouncyHexagons = { "Bouncy Hexagons", 1.0/60.0, init_BouncyTerrainHexagons_500, update, ChipmunkDemoDefaultDrawImpl, destroy, }; #define BENCH(n) {"benchmark - " #n, 1.0/60.0, init_##n, update, ChipmunkDemoDefaultDrawImpl, destroy} ChipmunkDemo bench_list[] = { BENCH(SimpleTerrainCircles_1000), BENCH(SimpleTerrainCircles_500), BENCH(SimpleTerrainCircles_100), BENCH(SimpleTerrainBoxes_1000), BENCH(SimpleTerrainBoxes_500), BENCH(SimpleTerrainBoxes_100), BENCH(SimpleTerrainHexagons_1000), BENCH(SimpleTerrainHexagons_500), BENCH(SimpleTerrainHexagons_100), BENCH(SimpleTerrainVCircles_200), BENCH(SimpleTerrainVBoxes_200), BENCH(SimpleTerrainVHexagons_200), BENCH(ComplexTerrainCircles_1000), BENCH(ComplexTerrainHexagons_1000), BENCH(BouncyTerrainCircles_500), BENCH(BouncyTerrainHexagons_500), BENCH(NoCollide), }; int bench_count = sizeof(bench_list)/sizeof(ChipmunkDemo); Chipmunk2D-Chipmunk-7.0.3/demo/Buoyancy.c000066400000000000000000000170451347650476100201460ustar00rootroot00000000000000/* 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 "chipmunk/chipmunk.h" #include "ChipmunkDemo.h" static void update(cpSpace *space, double dt) { cpSpaceStep(space, dt); } #define FLUID_DENSITY 0.00014 #define FLUID_DRAG 2.0 char messageBuffer[1024]; // Modified from chipmunk_private.h static inline cpFloat k_scalar_body(cpBody *body, cpVect point, cpVect n) { cpFloat rcn = cpvcross(cpvsub(point, cpBodyGetPosition(body)), n); return 1.0f/cpBodyGetMass(body) + rcn*rcn/cpBodyGetMoment(body); } static cpBool waterPreSolve(cpArbiter *arb, cpSpace *space, void *ptr) { CP_ARBITER_GET_SHAPES(arb, water, poly); cpBody *body = cpShapeGetBody(poly); // Get the top of the water sensor bounding box to use as the water level. cpFloat level = cpShapeGetBB(water).t; // Clip the polygon against the water level int count = cpPolyShapeGetCount(poly); int clippedCount = 0; #ifdef _MSC_VER // MSVC is pretty much the only compiler in existence that doesn't support variable sized arrays. cpVect clipped[10]; #else cpVect clipped[count + 1]; #endif for(int i=0, j=count-1; ipreSolveFunc = (cpCollisionPreSolveFunc)waterPreSolve; return space; } static void destroy(cpSpace *space) { ChipmunkDemoFreeSpaceChildren(space); cpSpaceFree(space); } ChipmunkDemo Buoyancy = { "Simple Sensor based fluids.", 1.0/180.0, init, update, ChipmunkDemoDefaultDrawImpl, destroy, }; Chipmunk2D-Chipmunk-7.0.3/demo/CMakeLists.txt000066400000000000000000000027751347650476100207550ustar00rootroot00000000000000cmake_policy(SET CMP0015 NEW) # Convert relative paths set(OpenGL_GL_PREFERENCE GLVND) find_package(OpenGL REQUIRED) set(chipmunk_demos_include_dirs ${chipmunk_SOURCE_DIR}/include ${OPENGL_INCLUDE_DIR} ) set(chipmunk_demos_libraries chipmunk_static ${OPENGL_LIBRARIES} ) file(GLOB chipmunk_demos_source_files "*.c") if(APPLE) FIND_LIBRARY(APPKIT AppKit) FIND_LIBRARY(IOKIT IOKit) list(APPEND chipmunk_demos_libraries ${APPKIT} ${IOKIT}) list(APPEND chipmunk_demos_source_files "sokol/sokol.m") set_property(SOURCE "sokol/sokol.m" APPEND_STRING PROPERTY COMPILE_FLAGS "-fobjc-arc") else() list(APPEND chipmunk_demos_source_files "sokol/sokol.c") endif(APPLE) IF(${CMAKE_SYSTEM_NAME} MATCHES "Linux") list(APPEND chipmunk_demos_libraries dl X11) endif() if(NOT MSVC) list(APPEND chipmunk_demos_libraries m pthread) endif(NOT MSVC) if(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") list(APPEND chipmunk_demos_libraries BlocksRuntime) endif(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") include_directories(${chipmunk_demos_include_dirs}) link_directories(${chipmunk_demos_library_dirs}) add_executable(chipmunk_demos ${chipmunk_demos_source_files}) target_link_libraries(chipmunk_demos ${chipmunk_demos_libraries}) # Tell MSVC to compile the code as C++. if(MSVC) set_source_files_properties(${chipmunk_demos_source_files} PROPERTIES LANGUAGE CXX) set_target_properties(chipmunk_demos PROPERTIES LINKER_LANGUAGE CXX) endif(MSVC) if(INSTALL_DEMOS) install(TARGETS chipmunk_demos RUNTIME DESTINATION bin) endif(INSTALL_DEMOS) Chipmunk2D-Chipmunk-7.0.3/demo/Chains.c000066400000000000000000000114761347650476100175640ustar00rootroot00000000000000/* 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 "chipmunk/chipmunk.h" #include "ChipmunkDemo.h" #define CHAIN_COUNT 8 #define LINK_COUNT 10 static void BreakablejointPostStepRemove(cpSpace *space, cpConstraint *joint, void *unused) { cpSpaceRemoveConstraint(space, joint); cpConstraintFree(joint); } static void BreakableJointPostSolve(cpConstraint *joint, cpSpace *space) { cpFloat dt = cpSpaceGetCurrentTimeStep(space); // Convert the impulse to a force by dividing it by the timestep. cpFloat force = cpConstraintGetImpulse(joint)/dt; cpFloat maxForce = cpConstraintGetMaxForce(joint); // If the force is almost as big as the joint's max force, break it. if(force > 0.9*maxForce){ cpSpaceAddPostStepCallback(space, (cpPostStepFunc)BreakablejointPostStepRemove, joint, NULL); } } static void update(cpSpace *space, double dt) { cpSpaceStep(space, dt); } static cpSpace * init(void) { cpSpace *space = cpSpaceNew(); cpSpaceSetIterations(space, 30); cpSpaceSetGravity(space, cpv(0, -100)); cpSpaceSetSleepTimeThreshold(space, 0.5f); cpBody *body, *staticBody = cpSpaceGetStaticBody(space); cpShape *shape; // Create segments around the edge of the screen. shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320,-240), cpv(-320,240), 0.0f)); cpShapeSetElasticity(shape, 1.0f); cpShapeSetFriction(shape, 1.0f); cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(320,-240), cpv(320,240), 0.0f)); cpShapeSetElasticity(shape, 1.0f); cpShapeSetFriction(shape, 1.0f); cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320,-240), cpv(320,-240), 0.0f)); cpShapeSetElasticity(shape, 1.0f); cpShapeSetFriction(shape, 1.0f); cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320,240), cpv(320,240), 0.0f)); cpShapeSetElasticity(shape, 1.0f); cpShapeSetFriction(shape, 1.0f); cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); cpFloat mass = 1; cpFloat width = 20; cpFloat height = 30; cpFloat spacing = width*0.3; // Add lots of boxes. for(int i=0; i #include #include #include "sokol/sokol_gfx.h" #include "chipmunk/chipmunk_private.h" #include "ChipmunkDebugDraw.h" cpTransform ChipmunkDebugDrawVPMatrix; float ChipmunkDebugDrawPointLineScale = 1.0f; #define GLSL33(x) "#version 330\n" #x static sg_bindings bindings; static sg_pipeline pipeline; typedef struct {float x, y;} float2; typedef struct {uint8_t r, g, b, a;} RGBA8; typedef struct {float2 pos; float2 uv; float r; RGBA8 fill, outline;} Vertex; typedef uint16_t Index; static RGBA8 cp_to_rgba(cpSpaceDebugColor c){return (RGBA8){(uint8_t)(0xFF*c.r), (uint8_t)(0xFF*c.g), (uint8_t)(0xFF*c.b), (uint8_t)(0xFF*c.a)};} typedef struct { float U_vp_matrix[16]; } Uniforms; // Meh, just max out 16 bit index size. #define VERTEX_MAX (64*1024) #define INDEX_MAX (128*1024) static sg_buffer VertexBuffer, IndexBuffer; static size_t VertexCount, IndexCount; static Vertex Vertexes[VERTEX_MAX]; static uint16_t Indexes[INDEX_MAX]; void ChipmunkDebugDrawInit(void) { VertexBuffer = sg_make_buffer(&(sg_buffer_desc){ .label = "ChipmunkDebugDraw Vertex Buffer", .size = VERTEX_MAX*sizeof(Vertex), .type = SG_BUFFERTYPE_VERTEXBUFFER, .usage = SG_USAGE_STREAM, }); IndexBuffer = sg_make_buffer(&(sg_buffer_desc){ .label = "ChipmunkDebugDraw Index Buffer", .size = INDEX_MAX*sizeof(Index), .type = SG_BUFFERTYPE_INDEXBUFFER, .usage = SG_USAGE_STREAM, }); bindings = (sg_bindings){ .vertex_buffers[0] = VertexBuffer, .index_buffer = IndexBuffer, }; sg_shader shd = sg_make_shader(&(sg_shader_desc){ .vs.uniform_blocks[0] = { .size = sizeof(Uniforms), .uniforms[0] = {.name = "U_vp_matrix", .type = SG_UNIFORMTYPE_MAT4}, }, .vs.source = GLSL33( layout(location = 0) in vec2 IN_pos; layout(location = 1) in vec2 IN_uv; layout(location = 2) in float IN_radius; layout(location = 3) in vec4 IN_fill; layout(location = 4) in vec4 IN_outline; uniform mat4 U_vp_matrix; out struct { vec2 uv; vec4 fill; vec4 outline; } FRAG; void main(){ gl_Position = U_vp_matrix*vec4(IN_pos + IN_radius*IN_uv, 0, 1); FRAG.uv = IN_uv; FRAG.fill = IN_fill; FRAG.fill.rgb *= IN_fill.a; FRAG.outline = IN_outline; FRAG.outline.a *= IN_outline.a; } ), .fs.source = GLSL33( in struct { vec2 uv; vec4 fill; vec4 outline; } FRAG; out vec4 OUT_color; void main(){ float len = length(FRAG.uv); float fw = length(fwidth(FRAG.uv)); float mask = smoothstep(-1, fw - 1, -len); float outline = 1 - fw; float outline_mask = smoothstep(outline - fw, outline, len); vec4 color = FRAG.fill + (FRAG.outline - FRAG.fill*FRAG.outline.a)*outline_mask; OUT_color = color*mask; } ), }); pipeline = sg_make_pipeline(&(sg_pipeline_desc){ .shader = shd, .blend = { .enabled = true, .src_factor_rgb = SG_BLENDFACTOR_ONE, .dst_factor_rgb = SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA }, .index_type = SG_INDEXTYPE_UINT16, .layout = { .attrs = { [0] = {.offset = offsetof(Vertex, pos), .format = SG_VERTEXFORMAT_FLOAT2}, [1] = {.offset = offsetof(Vertex, uv), .format = SG_VERTEXFORMAT_FLOAT2}, [2] = {.offset = offsetof(Vertex, r), .format = SG_VERTEXFORMAT_FLOAT}, [3] = {.offset = offsetof(Vertex, fill), .format = SG_VERTEXFORMAT_UBYTE4N}, [4] = {.offset = offsetof(Vertex, outline), .format = SG_VERTEXFORMAT_UBYTE4N}, } } }); } static Vertex *push_vertexes(size_t vcount, const Index *index_src, size_t icount){ cpAssertHard(VertexCount + vcount <= VERTEX_MAX || IndexCount + icount <= INDEX_MAX, "Geometry buffer full."); Vertex *vertex_dst = Vertexes + VertexCount; size_t base = VertexCount; VertexCount += vcount; Index *index_dst = Indexes + IndexCount; for(size_t i = 0; i < icount; i++) index_dst[i] = index_src[i] + (Index)base; IndexCount += icount; return vertex_dst; } void ChipmunkDebugDrawDot(cpFloat size, cpVect pos, cpSpaceDebugColor fillColor) { float r = (float)(size*0.5f*ChipmunkDebugDrawPointLineScale); RGBA8 fill = cp_to_rgba(fillColor); Vertex *vertexes = push_vertexes(4, (Index[]){0, 1, 2, 0, 2, 3}, 6); vertexes[0] = (Vertex){{(float)pos.x, (float)pos.y}, {-1, -1}, r, fill, fill}; vertexes[1] = (Vertex){{(float)pos.x, (float)pos.y}, {-1, 1}, r, fill, fill}; vertexes[2] = (Vertex){{(float)pos.x, (float)pos.y}, { 1, 1}, r, fill, fill}; vertexes[3] = (Vertex){{(float)pos.x, (float)pos.y}, { 1, -1}, r, fill, fill}; } void ChipmunkDebugDrawCircle(cpVect pos, cpFloat angle, cpFloat radius, cpSpaceDebugColor outlineColor, cpSpaceDebugColor fillColor) { float r = (float)radius + ChipmunkDebugDrawPointLineScale; RGBA8 fill = cp_to_rgba(fillColor), outline = cp_to_rgba(outlineColor); Vertex *vertexes = push_vertexes(4, (Index[]){0, 1, 2, 0, 2, 3}, 6); vertexes[0] = (Vertex){{(float)pos.x, (float)pos.y}, {-1, -1}, r, fill, outline}; vertexes[1] = (Vertex){{(float)pos.x, (float)pos.y}, {-1, 1}, r, fill, outline}; vertexes[2] = (Vertex){{(float)pos.x, (float)pos.y}, { 1, 1}, r, fill, outline}; vertexes[3] = (Vertex){{(float)pos.x, (float)pos.y}, { 1, -1}, r, fill, outline}; ChipmunkDebugDrawSegment(pos, cpvadd(pos, cpvmult(cpvforangle(angle), 0.75f*radius)), outlineColor); } void ChipmunkDebugDrawSegment(cpVect a, cpVect b, cpSpaceDebugColor color) { ChipmunkDebugDrawFatSegment(a, b, 0.0f, color, color); } void ChipmunkDebugDrawFatSegment(cpVect a, cpVect b, cpFloat radius, cpSpaceDebugColor outlineColor, cpSpaceDebugColor fillColor) { static const Index indexes[] = {0, 1, 2, 1, 2, 3, 2, 3, 4, 3, 4, 5, 4, 5, 6, 5, 6, 7}; Vertex *vertexes = push_vertexes(8, indexes, 18); cpVect t = cpvnormalize(cpvsub(b, a)); float r = (float)radius + ChipmunkDebugDrawPointLineScale; RGBA8 fill = cp_to_rgba(fillColor), outline = cp_to_rgba(outlineColor); vertexes[0] = (Vertex){{(float)a.x, (float)a.y}, {(float)(-t.x + t.y), (float)(-t.x - t.y)}, r, fill, outline}; vertexes[1] = (Vertex){{(float)a.x, (float)a.y}, {(float)(-t.x - t.y), (float)(+t.x - t.y)}, r, fill, outline}; vertexes[2] = (Vertex){{(float)a.x, (float)a.y}, {(float)(-0.0 + t.y), (float)(-t.x + 0.0)}, r, fill, outline}; vertexes[3] = (Vertex){{(float)a.x, (float)a.y}, {(float)(-0.0 - t.y), (float)(+t.x + 0.0)}, r, fill, outline}; vertexes[4] = (Vertex){{(float)b.x, (float)b.y}, {(float)(+0.0 + t.y), (float)(-t.x - 0.0)}, r, fill, outline}; vertexes[5] = (Vertex){{(float)b.x, (float)b.y}, {(float)(+0.0 - t.y), (float)(+t.x - 0.0)}, r, fill, outline}; vertexes[6] = (Vertex){{(float)b.x, (float)b.y}, {(float)(+t.x + t.y), (float)(-t.x + t.y)}, r, fill, outline}; vertexes[7] = (Vertex){{(float)b.x, (float)b.y}, {(float)(+t.x - t.y), (float)(+t.x + t.y)}, r, fill, outline}; } #define MAX_POLY_VERTEXES 64 // Fill needs (count - 2) triangles. // Outline needs 4*count triangles. #define MAX_POLY_INDEXES (3*(5*MAX_POLY_VERTEXES - 2)) void ChipmunkDebugDrawPolygon(int count, const cpVect *verts, cpFloat radius, cpSpaceDebugColor outlineColor, cpSpaceDebugColor fillColor) { RGBA8 fill = cp_to_rgba(fillColor), outline = cp_to_rgba(outlineColor); Index indexes[MAX_POLY_INDEXES]; // Polygon fill triangles. for(int i = 0; i < count - 2; i++){ indexes[3*i + 0] = 0; indexes[3*i + 1] = 4*(i + 1); indexes[3*i + 2] = 4*(i + 2); } // Polygon outline triangles. Index *cursor = indexes + 3*(count - 2); for(int i0 = 0; i0 < count; i0++){ int i1 = (i0 + 1)%count; cursor[12*i0 + 0] = 4*i0 + 0; cursor[12*i0 + 1] = 4*i0 + 1; cursor[12*i0 + 2] = 4*i0 + 2; cursor[12*i0 + 3] = 4*i0 + 0; cursor[12*i0 + 4] = 4*i0 + 2; cursor[12*i0 + 5] = 4*i0 + 3; cursor[12*i0 + 6] = 4*i0 + 0; cursor[12*i0 + 7] = 4*i0 + 3; cursor[12*i0 + 8] = 4*i1 + 0; cursor[12*i0 + 9] = 4*i0 + 3; cursor[12*i0 + 10] = 4*i1 + 0; cursor[12*i0 + 11] = 4*i1 + 1; } float inset = (float)-cpfmax(0, 2*ChipmunkDebugDrawPointLineScale - radius); float outset = (float)radius + ChipmunkDebugDrawPointLineScale; float r = outset - inset; Vertex *vertexes = push_vertexes(4*count, indexes, 3*(5*count - 2)); for(int i=0; i #include #include #include #include #include "chipmunk/chipmunk_private.h" #include "ChipmunkDemo.h" #include "ChipmunkDemoTextSupport.h" #include "sokol/sokol.h" #undef Convex static ChipmunkDemo demos[32]; static int demo_count; static int demo_index; static cpBool paused = cpFalse; static cpBool step = cpFalse; static cpSpace *space; static double Accumulator; static double LastTime; int ChipmunkDemoTicks; double ChipmunkDemoTime; cpVect ChipmunkDemoMouse; cpBool ChipmunkDemoRightClick; cpBool ChipmunkDemoRightDown; cpVect ChipmunkDemoKeyboard; static cpBody *mouse_body = NULL; static cpConstraint *mouse_joint = NULL; char const *ChipmunkDemoMessageString = NULL; #define GRABBABLE_MASK_BIT (1<<31) cpShapeFilter GRAB_FILTER = {CP_NO_GROUP, GRABBABLE_MASK_BIT, GRABBABLE_MASK_BIT}; cpShapeFilter NOT_GRABBABLE_FILTER = {CP_NO_GROUP, ~GRABBABLE_MASK_BIT, ~GRABBABLE_MASK_BIT}; cpVect view_translate = {0, 0}; cpFloat view_scale = 1.0; static void ShapeFreeWrap(cpSpace *space, cpShape *shape, void *unused){ cpSpaceRemoveShape(space, shape); cpShapeFree(shape); } static void PostShapeFree(cpShape *shape, cpSpace *space){ cpSpaceAddPostStepCallback(space, (cpPostStepFunc)ShapeFreeWrap, shape, NULL); } static void ConstraintFreeWrap(cpSpace *space, cpConstraint *constraint, void *unused){ cpSpaceRemoveConstraint(space, constraint); cpConstraintFree(constraint); } static void PostConstraintFree(cpConstraint *constraint, cpSpace *space){ cpSpaceAddPostStepCallback(space, (cpPostStepFunc)ConstraintFreeWrap, constraint, NULL); } static void BodyFreeWrap(cpSpace *space, cpBody *body, void *unused){ cpSpaceRemoveBody(space, body); cpBodyFree(body); } static void PostBodyFree(cpBody *body, cpSpace *space){ cpSpaceAddPostStepCallback(space, (cpPostStepFunc)BodyFreeWrap, body, NULL); } // Safe and future proof way to remove and free all objects that have been added to the space. void ChipmunkDemoFreeSpaceChildren(cpSpace *space) { // Must remove these BEFORE freeing the body or you will access dangling pointers. cpSpaceEachShape(space, (cpSpaceShapeIteratorFunc)PostShapeFree, space); cpSpaceEachConstraint(space, (cpSpaceConstraintIteratorFunc)PostConstraintFree, space); cpSpaceEachBody(space, (cpSpaceBodyIteratorFunc)PostBodyFree, space); } static void DrawCircle(cpVect p, cpFloat a, cpFloat r, cpSpaceDebugColor outline, cpSpaceDebugColor fill, cpDataPointer data) {ChipmunkDebugDrawCircle(p, a, r, outline, fill);} static void DrawSegment(cpVect a, cpVect b, cpSpaceDebugColor color, cpDataPointer data) {ChipmunkDebugDrawSegment(a, b, color);} static void DrawFatSegment(cpVect a, cpVect b, cpFloat r, cpSpaceDebugColor outline, cpSpaceDebugColor fill, cpDataPointer data) {ChipmunkDebugDrawFatSegment(a, b, r, outline, fill);} static void DrawPolygon(int count, const cpVect *verts, cpFloat r, cpSpaceDebugColor outline, cpSpaceDebugColor fill, cpDataPointer data) {ChipmunkDebugDrawPolygon(count, verts, r, outline, fill);} static void DrawDot(cpFloat size, cpVect pos, cpSpaceDebugColor color, cpDataPointer data) {ChipmunkDebugDrawDot(size, pos, color);} static cpSpaceDebugColor Colors[] = { {0xb5/255.0f, 0x89/255.0f, 0x00/255.0f, 1.0f}, {0xcb/255.0f, 0x4b/255.0f, 0x16/255.0f, 1.0f}, {0xdc/255.0f, 0x32/255.0f, 0x2f/255.0f, 1.0f}, {0xd3/255.0f, 0x36/255.0f, 0x82/255.0f, 1.0f}, {0x6c/255.0f, 0x71/255.0f, 0xc4/255.0f, 1.0f}, {0x26/255.0f, 0x8b/255.0f, 0xd2/255.0f, 1.0f}, {0x2a/255.0f, 0xa1/255.0f, 0x98/255.0f, 1.0f}, {0x85/255.0f, 0x99/255.0f, 0x00/255.0f, 1.0f}, }; static cpSpaceDebugColor ColorForShape(cpShape *shape, cpDataPointer data) { if(cpShapeGetSensor(shape)){ return LAColor(1.0f, 0.1f); } else { cpBody *body = cpShapeGetBody(shape); if(cpBodyIsSleeping(body)){ return RGBAColor(0x58/255.0f, 0x6e/255.0f, 0x75/255.0f, 1.0f); } else if(body->sleeping.idleTime > shape->space->sleepTimeThreshold) { return RGBAColor(0x93/255.0f, 0xa1/255.0f, 0xa1/255.0f, 1.0f); } else { uint32_t val = (uint32_t)shape->hashid; // scramble the bits up using Robert Jenkins' 32 bit integer hash function val = (val+0x7ed55d16) + (val<<12); val = (val^0xc761c23c) ^ (val>>19); val = (val+0x165667b1) + (val<<5); val = (val+0xd3a2646c) ^ (val<<9); val = (val+0xfd7046c5) + (val<<3); val = (val^0xb55a4f09) ^ (val>>16); return Colors[val & 0x7]; } } } void ChipmunkDemoDefaultDrawImpl(cpSpace *space) { cpSpaceDebugDrawOptions drawOptions = { DrawCircle, DrawSegment, DrawFatSegment, DrawPolygon, DrawDot, (cpSpaceDebugDrawFlags)(CP_SPACE_DEBUG_DRAW_SHAPES | CP_SPACE_DEBUG_DRAW_CONSTRAINTS | CP_SPACE_DEBUG_DRAW_COLLISION_POINTS), {0xEE/255.0f, 0xE8/255.0f, 0xD5/255.0f, 1.0f}, // Outline color ColorForShape, {0.0f, 0.75f, 0.0f, 1.0f}, // Constraint color {1.0f, 0.0f, 0.0f, 1.0f}, // Collision point color NULL, }; cpSpaceDebugDraw(space, &drawOptions); } static void DrawInstructions() { static char title[1024]; sprintf(title, "Demo(%c): %s", 'A' + demo_index, demos[demo_index].name); ChipmunkDemoTextDrawString(cpv(-300, 220), title); ChipmunkDemoTextDrawString(cpv(-300, 200), "Controls:\n" "A - Z Switch demos. (return restarts)\n" "Use the mouse to grab objects.\n" ); } static int max_arbiters = 0; static int max_points = 0; static int max_constraints = 0; static void DrawInfo() { int arbiters = space->arbiters->num; int points = 0; for(int i=0; iarbiters->arr[i]))->count; int constraints = (space->constraints->num + points)*space->iterations; max_arbiters = arbiters > max_arbiters ? arbiters : max_arbiters; max_points = points > max_points ? points : max_points; max_constraints = constraints > max_constraints ? constraints : max_constraints; char buffer[1024]; const char *format = "Arbiters: %d (%d) - " "Contact Points: %d (%d)\n" "Other Constraints: %d, Iterations: %d\n" "Constraints x Iterations: %d (%d)\n" "Time:% 5.2fs, KE:% 5.2e"; cpArray *bodies = space->dynamicBodies; cpFloat ke = 0.0f; for(int i=0; inum; i++){ cpBody *body = (cpBody *)bodies->arr[i]; if(body->m == INFINITY || body->i == INFINITY) continue; ke += body->m*cpvdot(body->v, body->v) + body->i*body->w*body->w; } sprintf(buffer, format, arbiters, max_arbiters, points, max_points, space->constraints->num, space->iterations, constraints, max_constraints, ChipmunkDemoTime, (ke < 1e-10f ? 0.0f : ke) ); ChipmunkDemoTextDrawString(cpv(0, 220), buffer); } static char PrintStringBuffer[1024*8]; static char *PrintStringCursor; void ChipmunkDemoPrintString(char const *fmt, ...) { if (PrintStringCursor == NULL) { return; } ChipmunkDemoMessageString = PrintStringBuffer; va_list args; va_start(args, fmt); int remaining = sizeof(PrintStringBuffer) - (PrintStringCursor - PrintStringBuffer); int would_write = vsnprintf(PrintStringCursor, remaining, fmt, args); if (would_write > 0 && would_write < remaining) { PrintStringCursor += would_write; } else { // encoding error or overflow, prevent further use until reinitialized PrintStringCursor = NULL; } va_end(args); } static void Tick(double dt) { if(!paused || step){ PrintStringBuffer[0] = 0; PrintStringCursor = PrintStringBuffer; // Completely reset the renderer only at the beginning of a tick. // That way it can always display at least the last ticks' debug drawing. ChipmunkDebugDrawClearRenderer(); ChipmunkDemoTextClearRenderer(); cpVect new_point = cpvlerp(mouse_body->p, ChipmunkDemoMouse, 0.25f); mouse_body->v = cpvmult(cpvsub(new_point, mouse_body->p), 60.0f); mouse_body->p = new_point; demos[demo_index].updateFunc(space, dt); ChipmunkDemoTicks++; ChipmunkDemoTime += dt; step = cpFalse; ChipmunkDemoRightDown = cpFalse; ChipmunkDemoTextDrawString(cpv(-300, -200), ChipmunkDemoMessageString); } } static void Update(void) { double time = stm_sec(stm_now()); double dt = time - LastTime; if(dt > 0.2) dt = 0.2; double fixed_dt = demos[demo_index].timestep; for(Accumulator += dt; Accumulator > fixed_dt; Accumulator -= fixed_dt){ Tick(fixed_dt); } LastTime = time; } static void Display(void) { cpVect screen_size = {sapp_width(), sapp_height()}; cpTransform view_matrix = cpTransformMult(cpTransformScale(view_scale, view_scale), cpTransformTranslate(view_translate)); float screen_scale = (float)cpfmin(screen_size.x/640.0, screen_size.y/480.0); float hw = (float)screen_size.x*(0.5f/screen_scale); float hh = (float)screen_size.y*(0.5f/screen_scale); cpTransform projection_matrix = cpTransformOrtho(cpBBNew(-hw, -hh, hw, hh)); ChipmunkDebugDrawPointLineScale = 1.0f/(float)view_scale; ChipmunkDebugDrawVPMatrix = cpTransformMult(projection_matrix, view_matrix); Update(); // Save the drawing commands from the most recent tick. ChipmunkDebugDrawPushRenderer(); ChipmunkDemoTextPushRenderer(); demos[demo_index].drawFunc(space); // // Highlight the shape under the mouse because it looks neat. // cpShape *nearest = cpSpacePointQueryNearest(space, ChipmunkDemoMouse, 0.0f, CP_ALL_LAYERS, CP_NO_GROUP, NULL); // if(nearest) ChipmunkDebugDrawShape(nearest, RGBAColor(1.0f, 0.0f, 0.0f, 1.0f), LAColor(0.0f, 0.0f)); sg_pass_action action = { .colors[0] = {.action = SG_ACTION_CLEAR, .val = {0x07/255.0f, 0x36/255.0f, 0x42/255.0f}}, }; sg_begin_default_pass(&action, (int)screen_size.x, (int)screen_size.y); // Draw the renderer contents and reset it back to the last tick's state. ChipmunkDebugDrawFlushRenderer(); // // Now render all the UI text. DrawInstructions(); DrawInfo(); ChipmunkDemoTextMatrix = projection_matrix; ChipmunkDemoTextFlushRenderer(); ChipmunkDebugDrawPopRenderer(); ChipmunkDemoTextPopRenderer(); sg_end_pass(); sg_commit(); } static void RunDemo(int index) { srand(45073); demo_index = index; ChipmunkDemoTicks = 0; ChipmunkDemoTime = 0.0; Accumulator = 0.0; LastTime = stm_sec(stm_now()); mouse_joint = NULL; ChipmunkDemoMessageString = ""; max_arbiters = 0; max_points = 0; max_constraints = 0; space = demos[demo_index].initFunc(); // Not supported by Sokol yet. // static char title[1024]; // sprintf(title, "Demo(%c): %s", 'a' + demo_index, demos[demo_index].name); } static void Keyboard(const sapp_event *event) { float translate_increment = 50.0f/(float)view_scale; float scale_increment = 1.2f; if(event->type == SAPP_EVENTTYPE_CHAR && !event->key_repeat){ int index = event->char_code - 'a'; if(0 <= index && index < demo_count){ demos[demo_index].destroyFunc(space); RunDemo(index); } } else if(event->type == SAPP_EVENTTYPE_KEY_DOWN){ switch(event->key_code){ case SAPP_KEYCODE_SPACE:{ if(!event->key_repeat){ demos[demo_index].destroyFunc(space); RunDemo(demo_index); } } break; case SAPP_KEYCODE_GRAVE_ACCENT : { if(!event->key_repeat) paused = !paused; } break; case SAPP_KEYCODE_1: { step = cpTrue; } break; case SAPP_KEYCODE_KP_4: view_translate.x += translate_increment; break; case SAPP_KEYCODE_KP_6: view_translate.x -= translate_increment; break; case SAPP_KEYCODE_KP_2: view_translate.y += translate_increment; break; case SAPP_KEYCODE_KP_8: view_translate.y -= translate_increment; break; case SAPP_KEYCODE_KP_7: view_scale /= scale_increment; break; case SAPP_KEYCODE_KP_9: view_scale *= scale_increment; break; case SAPP_KEYCODE_KP_5: { view_translate.x = 0.0f; view_translate.y = 0.0f; view_scale = 1.0f; } break; default: break; } } if(!event->key_repeat){ switch(event->key_code){ case SAPP_KEYCODE_UP : ChipmunkDemoKeyboard.y += (event->type == SAPP_EVENTTYPE_KEY_DOWN ? 1.0 : -1.0); break; case SAPP_KEYCODE_DOWN : ChipmunkDemoKeyboard.y += (event->type == SAPP_EVENTTYPE_KEY_DOWN ? -1.0 : 1.0); break; case SAPP_KEYCODE_LEFT : ChipmunkDemoKeyboard.x += (event->type == SAPP_EVENTTYPE_KEY_DOWN ? -1.0 : 1.0); break; case SAPP_KEYCODE_RIGHT : ChipmunkDemoKeyboard.x += (event->type == SAPP_EVENTTYPE_KEY_DOWN ? 1.0 : -1.0); break; default: break; } } } static cpVect MouseToSpace(const sapp_event *event) { // Calculate clip coord for mouse. cpVect screen_size = cpv(sapp_width(), sapp_height()); cpVect clip_coord = cpv(2*event->mouse_x/screen_size.x - 1, 1 - 2*event->mouse_y/screen_size.y); // Use the VP matrix to transform to world space. cpTransform vp_inverse = cpTransformInverse(ChipmunkDebugDrawVPMatrix); return cpTransformPoint(vp_inverse, clip_coord); } static void Click(const sapp_event *event) { cpVect mouse_pos = MouseToSpace(event); if(event->mouse_button == SAPP_MOUSEBUTTON_LEFT){ if(event->type == SAPP_EVENTTYPE_MOUSE_DOWN){ // give the mouse click a little radius to make it easier to click small shapes. cpFloat radius = 5.0; cpPointQueryInfo info = {0}; cpShape *shape = cpSpacePointQueryNearest(space, mouse_pos, radius, GRAB_FILTER, &info); if(shape && cpBodyGetMass(cpShapeGetBody(shape)) < INFINITY){ // Use the closest point on the surface if the click is outside of the shape. cpVect nearest = (info.distance > 0.0f ? info.point : mouse_pos); cpBody *body = cpShapeGetBody(shape); mouse_joint = cpPivotJointNew2(mouse_body, body, cpvzero, cpBodyWorldToLocal(body, nearest)); mouse_joint->maxForce = 50000.0f; mouse_joint->errorBias = cpfpow(1.0f - 0.15f, 60.0f); cpSpaceAddConstraint(space, mouse_joint); } } else if(mouse_joint){ cpSpaceRemoveConstraint(space, mouse_joint); cpConstraintFree(mouse_joint); mouse_joint = NULL; } } else if(event->mouse_button == SAPP_MOUSEBUTTON_RIGHT){ ChipmunkDemoRightDown = ChipmunkDemoRightClick = (event->type == SAPP_EVENTTYPE_MOUSE_DOWN); } } static void Event(const sapp_event *event) { switch(event->type){ case SAPP_EVENTTYPE_CHAR: case SAPP_EVENTTYPE_KEY_UP: case SAPP_EVENTTYPE_KEY_DOWN: { Keyboard(event); } break; case SAPP_EVENTTYPE_MOUSE_MOVE: { ChipmunkDemoMouse = MouseToSpace(event); }; break; case SAPP_EVENTTYPE_MOUSE_UP: case SAPP_EVENTTYPE_MOUSE_DOWN: { Click(event); } break; default: break; } } static void TimeTrial(int index, int count) { space = demos[index].initFunc(); double start_time = stm_sec(stm_now()); double dt = demos[index].timestep; for(int i=0; i #include #include "sokol/sokol_gfx.h" // #include "chipmunk/chipmunk_private.h" // #include "ChipmunkDemo.h" #include "ChipmunkDemoTextSupport.h" #include "VeraMoBd.ttf_sdf.h" //#define Scale 3.0f #define Scale 0.70f #define LineHeight (18.0f*Scale) // char -> glyph indexes generated by the lonesock tool. static int glyph_indexes[256]; cpTransform ChipmunkDemoTextMatrix; #define GLSL33(x) "#version 330\n" #x static sg_bindings bindings; static sg_pipeline pipeline; typedef struct {float x, y;} float2; typedef struct {uint8_t r, g, b, a;} RGBA8; typedef struct {float2 pos; float2 uv; RGBA8 color;} Vertex; typedef uint16_t Index; typedef struct { float U_vp_matrix[16]; } Uniforms; // Meh, just max out 16 bit index size. #define VERTEX_MAX (64*1024) #define INDEX_MAX (128*1024) static sg_buffer VertexBuffer, IndexBuffer; static size_t VertexCount, IndexCount; static Vertex Vertexes[VERTEX_MAX]; static uint16_t Indexes[INDEX_MAX]; void ChipmunkDemoTextInit(void) { VertexBuffer = sg_make_buffer(&(sg_buffer_desc){ .label = "ChipmunkDemoText Vertex Buffer", .size = VERTEX_MAX*sizeof(Vertex), .type = SG_BUFFERTYPE_VERTEXBUFFER, .usage = SG_USAGE_STREAM, }); IndexBuffer = sg_make_buffer(&(sg_buffer_desc){ .label = "ChipmunkDemoText Index Buffer", .size = INDEX_MAX*sizeof(Index), .type = SG_BUFFERTYPE_INDEXBUFFER, .usage = SG_USAGE_STREAM, }); sg_image sdf_texture = sg_make_image(&(sg_image_desc){ .label = "ChipmunkDemoText SDF Atlas", .width = sdf_tex_width, .height = sdf_tex_height, .pixel_format = SG_PIXELFORMAT_L8, .min_filter = SG_FILTER_LINEAR, .mag_filter = SG_FILTER_LINEAR, .wrap_u = SG_WRAP_CLAMP_TO_EDGE, .wrap_v = SG_WRAP_CLAMP_TO_EDGE, .content.subimage[0][0] = {.ptr = sdf_data, .size = sizeof(sdf_data)}, }); bindings = (sg_bindings){ .vertex_buffers[0] = VertexBuffer, .index_buffer = IndexBuffer, .fs_images[0] = sdf_texture, }; sg_shader shd = sg_make_shader(&(sg_shader_desc){ .vs.uniform_blocks[0] = { .size = sizeof(Uniforms), .uniforms[0] = {.name = "U_vp_matrix", .type = SG_UNIFORMTYPE_MAT4}, }, .vs.source = GLSL33( layout(location = 0) in vec2 IN_pos; layout(location = 1) in vec2 IN_uv; layout(location = 2) in vec4 IN_color; uniform mat4 U_vp_matrix; out struct { vec2 uv; vec4 color; } FRAG; void main(){ gl_Position = U_vp_matrix*vec4(IN_pos, 0, 1); FRAG.uv = IN_uv; FRAG.color = IN_color; } ), .fs.images[0] = {.name = "U_texture", .type = SG_IMAGETYPE_2D}, .fs.source = GLSL33( in struct { vec2 uv; vec4 color; } FRAG; uniform sampler2D U_texture; out vec4 OUT_color; void main(){ float sdf = texture(U_texture, FRAG.uv).r; float fw = 0.5*fwidth(sdf); float mask = smoothstep(0.5 - fw, 0.5 + fw, sdf); OUT_color = FRAG.color*mask; OUT_color.a = 0.25*smoothstep(0.0, 0.5, sdf); } ), }); pipeline = sg_make_pipeline(&(sg_pipeline_desc){ .shader = shd, .blend = { .enabled = true, .src_factor_rgb = SG_BLENDFACTOR_ONE, .dst_factor_rgb = SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA }, .index_type = SG_INDEXTYPE_UINT16, .layout = { .attrs = { [0] = {.offset = offsetof(Vertex, pos), .format = SG_VERTEXFORMAT_FLOAT2}, [1] = {.offset = offsetof(Vertex, uv), .format = SG_VERTEXFORMAT_FLOAT2}, [2] = {.offset = offsetof(Vertex, color), .format = SG_VERTEXFORMAT_UBYTE4N}, } } }); // Fill in the glyph index array. for(int i=0; imagnitudeSum += cpvlength(j); context->vectorSum = cpvadd(context->vectorSum, j); } #endif static void update(cpSpace *space, double dt) { cpSpaceStep(space, dt); ChipmunkDemoPrintString("Place objects on the scale to weigh them. The ball marks the shapes it's sitting on.\n"); // Sum the total impulse applied to the scale from all collision pairs in the contact graph. // If your compiler supports blocks, your life is a little easier. // You can use the "Block" versions of the functions without needing the callbacks above. #if USE_BLOCKS __block cpVect impulseSum = cpvzero; cpBodyEachArbiter_b(scaleStaticBody, ^(cpArbiter *arb){ impulseSum = cpvadd(impulseSum, cpArbiterTotalImpulse(arb)); }); #else cpVect impulseSum = cpvzero; cpBodyEachArbiter(scaleStaticBody, (cpBodyArbiterIteratorFunc)ScaleIterator, &impulseSum); #endif // Force is the impulse divided by the timestep. cpFloat force = cpvlength(impulseSum)/dt; // Weight can be found similarly from the gravity vector. cpVect g = cpSpaceGetGravity(space); cpFloat weight = cpvdot(g, impulseSum)/(cpvlengthsq(g)*dt); ChipmunkDemoPrintString("Total force: %5.2f, Total weight: %5.2f. ", force, weight); // Highlight and count the number of shapes the ball is touching. #if USE_BLOCKS __block int count = 0; cpBodyEachArbiter_b(ballBody, ^(cpArbiter *arb){ // body is the body we are iterating the arbiters for. // CP_ARBITER_GET_*() in an arbiter iterator always returns the body/shape for the iterated body first. CP_ARBITER_GET_SHAPES(arb, ball, other); ChipmunkDebugDrawBB(cpShapeGetBB(other), RGBAColor(1, 0, 0, 1)); count++; }); #else int count = 0; cpBodyEachArbiter(ballBody, (cpBodyArbiterIteratorFunc)BallIterator, &count); #endif ChipmunkDemoPrintString("The ball is touching %d shapes.\n", count); #if USE_BLOCKS __block cpFloat magnitudeSum = 0.0f; __block cpVect vectorSum = cpvzero; cpBodyEachArbiter_b(ballBody, ^(cpArbiter *arb){ cpVect j = cpArbiterTotalImpulse(arb); magnitudeSum += cpvlength(j); vectorSum = cpvadd(vectorSum, j); }); cpFloat crushForce = (magnitudeSum - cpvlength(vectorSum))*dt; #else struct CrushingContext crush = {0.0f, cpvzero}; cpBodyEachArbiter(ballBody, (cpBodyArbiterIteratorFunc)EstimateCrushing, &crush); cpFloat crushForce = (crush.magnitudeSum - cpvlength(crush.vectorSum))*dt; #endif if(crushForce > 10.0f){ ChipmunkDemoPrintString("The ball is being crushed. (f: %.2f)", crushForce); } else { ChipmunkDemoPrintString("The ball is not being crushed. (f: %.2f)", crushForce); } } #define WIDTH 4.0f #define HEIGHT 30.0f static cpSpace * init(void) { cpSpace *space = cpSpaceNew(); cpSpaceSetIterations(space, 30); cpSpaceSetGravity(space, cpv(0, -300)); cpSpaceSetCollisionSlop(space, 0.5); cpSpaceSetSleepTimeThreshold(space, 1.0f); cpBody *body, *staticBody = cpSpaceGetStaticBody(space); cpShape *shape; // Create segments around the edge of the screen. shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320,-240), cpv(-320,240), 0.0f)); cpShapeSetElasticity(shape, 1.0f); cpShapeSetFriction(shape, 1.0f); cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(320,-240), cpv(320,240), 0.0f)); cpShapeSetElasticity(shape, 1.0f); cpShapeSetFriction(shape, 1.0f); cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320,-240), cpv(320,-240), 0.0f)); cpShapeSetElasticity(shape, 1.0f); cpShapeSetFriction(shape, 1.0f); cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); scaleStaticBody = cpSpaceAddBody(space, cpBodyNewStatic()); shape = cpSpaceAddShape(space, cpSegmentShapeNew(scaleStaticBody, cpv(-240,-180), cpv(-140,-180), 4.0f)); cpShapeSetElasticity(shape, 1.0f); cpShapeSetFriction(shape, 1.0f); cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); // add some boxes to stack on the scale for(int i=0; i<5; i++){ body = cpSpaceAddBody(space, cpBodyNew(1.0f, cpMomentForBox(1.0f, 30.0f, 30.0f))); cpBodySetPosition(body, cpv(0, i*32 - 220)); shape = cpSpaceAddShape(space, cpBoxShapeNew(body, 30.0f, 30.0f, 0.0)); cpShapeSetElasticity(shape, 0.0f); cpShapeSetFriction(shape, 0.8f); } // Add a ball that we'll track which objects are beneath it. cpFloat radius = 15.0f; ballBody = cpSpaceAddBody(space, cpBodyNew(10.0f, cpMomentForCircle(10.0f, 0.0f, radius, cpvzero))); cpBodySetPosition(ballBody, cpv(120, -240 + radius+5)); shape = cpSpaceAddShape(space, cpCircleShapeNew(ballBody, radius, cpvzero)); cpShapeSetElasticity(shape, 0.0f); cpShapeSetFriction(shape, 0.9f); return space; } static void destroy(cpSpace *space) { ChipmunkDemoFreeSpaceChildren(space); cpSpaceFree(space); } ChipmunkDemo ContactGraph = { "Contact Graph", 1.0/60.0, init, update, ChipmunkDemoDefaultDrawImpl, destroy, }; Chipmunk2D-Chipmunk-7.0.3/demo/ContactPoints.c_000066400000000000000000000076201347650476100213020ustar00rootroot00000000000000/* 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 #include #include #include "chipmunk/chipmunk_private.h" #include "ChipmunkDemo.h" static cpBool NeverCollide(cpArbiter *arb, cpSpace *space, void *data){return cpFalse;} static void update(cpSpace *space) { int steps = 1; cpFloat dt = 1.0f/60.0f/(cpFloat)steps; for(int i=0; i tolerance){ cpBody *body = cpShapeGetBody(shape); int count = cpPolyShapeGetCount(shape); // Allocate the space for the new vertexes on the stack. cpVect *verts = (cpVect *)alloca((count + 1)*sizeof(cpVect)); for(int i=0; ibeginFunc = (cpCollisionBeginFunc)HookCrate; return space; } static void destroy(cpSpace *space) { ChipmunkDemoFreeSpaceChildren(space); cpSpaceFree(space); } ChipmunkDemo Crane = { "Crane", 1.0/60.0, init, update, ChipmunkDemoDefaultDrawImpl, destroy, }; Chipmunk2D-Chipmunk-7.0.3/demo/GJK.c_000066400000000000000000000103301347650476100171150ustar00rootroot00000000000000/* 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 #include #include #include "chipmunk_private.h" #include "chipmunk_unsafe.h" #include "ChipmunkDemo.h" static cpShape *shape1, *shape2; static void update(cpSpace *space, cpFloat dt) { cpSpaceStep(space, dt); } static void draw(cpSpace *space) { ChipmunkDemoDefaultDrawImpl(space); struct cpContact arr[CP_MAX_CONTACTS_PER_ARBITER]; // cpCollideShapes(shape1, shape2, (cpCollisionID[]){0}, arr); cpCollisionInfo info = cpCollideShapes(shape2, shape1, 0x00000000, arr); } static cpSpace * init(void) { cpSpace *space = cpSpaceNew(); cpSpaceSetIterations(space, 5); space->damping = 0.1; cpFloat mass = 1.0f; { cpFloat size = 100.0; cpBody *body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForBox(mass, size, size))); cpBodySetPosition(body, cpv(100.0, 50.0f)); shape1 = cpSpaceAddShape(space, cpBoxShapeNew(body, size, size, 0.0)); shape1->group = 1; }{ cpFloat size = 100.0; cpBody *body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForBox(mass, size, size))); cpBodySetPosition(body, cpv(120.0, -40.0f)); cpBodySetAngle(body, 1e-2); shape2 = cpSpaceAddShape(space, cpBoxShapeNew(body, size, size, 0.0)); shape2->group = 1; } // { // cpFloat size = 100.0; // const int NUM_VERTS = 5; // // cpVect verts[NUM_VERTS]; // for(int i=0; igroup = 1; // } // { // cpFloat size = 100.0; // const int NUM_VERTS = 4; // // cpVect verts[NUM_VERTS]; // for(int i=0; igroup = 1; // } // // { // cpFloat size = 150.0; // cpFloat radius = 25.0; // // cpVect a = cpv( size/2.0, 0.0); // cpVect b = cpv(-size/2.0, 0.0); // cpBody *body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForSegment(mass, a, b))); // cpBodySetPosition(body, cpv(0, 25)); // // shape1 = cpSpaceAddShape(space, cpSegmentShapeNew(body, a, b, radius)); // shape1->group = 1; // } // { // cpFloat radius = 50.0; // // cpBody *body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForCircle(mass, 0.0f, radius, cpvzero))); // cpBodySetPosition(body, cpv(0, -25)); // // shape2 = cpSpaceAddShape(space, cpCircleShapeNew(body, radius, cpvzero)); // shape2->group = 1; // } return space; } static void destroy(cpSpace *space) { ChipmunkDemoFreeSpaceChildren(space); cpSpaceFree(space); } ChipmunkDemo GJK = { "GJK", 1.0f/60.0f, init, update, draw, destroy, }; Chipmunk2D-Chipmunk-7.0.3/demo/GLEWConfig.cmake000066400000000000000000000002761347650476100210750ustar00rootroot00000000000000set(GLEW_INCLUDE_DIR "@CMAKE_INSTALL_PREFIX@/include") set(GLEW_LIBRARY_DIR "@CMAKE_INSTALL_PREFIX@/lib@LIB_SUFFIX@") find_library(GLEW_LIBRARY "@GLEW_LIB_NAME@" HINTS ${GLEW_LIBRARY_DIR}) Chipmunk2D-Chipmunk-7.0.3/demo/GLFWConfig.cmake000066400000000000000000000006771347650476100211030ustar00rootroot00000000000000# - Config file for the glfw package # It defines the following variables # GLFW_INCLUDE_DIR, the path where GLFW headers are located # GLFW_LIBRARY_DIR, folder in which the GLFW library is located # GLFW_LIBRARY, library to link against to use GLFW set(GLFW_INCLUDE_DIR "@CMAKE_INSTALL_PREFIX@/include") set(GLFW_LIBRARY_DIR "@CMAKE_INSTALL_PREFIX@/lib@LIB_SUFFIX@") find_library(GLFW_LIBRARY "@GLFW_LIB_NAME@" HINTS ${GLFW_LIBRARY_DIR}) Chipmunk2D-Chipmunk-7.0.3/demo/Joints.c000066400000000000000000000262671347650476100176310ustar00rootroot00000000000000/* 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 "chipmunk/chipmunk.h" #include "ChipmunkDemo.h" static cpBody * addBall(cpSpace *space, cpVect pos, cpVect boxOffset) { cpFloat radius = 15.0f; cpFloat mass = 1.0f; cpBody *body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForCircle(mass, 0.0f, radius, cpvzero))); cpBodySetPosition(body, cpvadd(pos, boxOffset)); cpShape *shape = cpSpaceAddShape(space, cpCircleShapeNew(body, radius, cpvzero)); cpShapeSetElasticity(shape, 0.0f); cpShapeSetFriction(shape, 0.7f); return body; } static cpBody * addLever(cpSpace *space, cpVect pos, cpVect boxOffset) { cpFloat mass = 1.0f; cpVect a = cpv(0, 15); cpVect b = cpv(0, -15); cpBody *body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForSegment(mass, a, b, 0.0f))); cpBodySetPosition(body, cpvadd(pos, cpvadd(boxOffset, cpv(0, -15)))); cpShape *shape = cpSpaceAddShape(space, cpSegmentShapeNew(body, a, b, 5.0f)); cpShapeSetElasticity(shape, 0.0f); cpShapeSetFriction(shape, 0.7f); return body; } static cpBody * addBar(cpSpace *space, cpVect pos, cpVect boxOffset) { cpFloat mass = 2.0f; cpVect a = cpv(0, 30); cpVect b = cpv(0, -30); cpBody *body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForSegment(mass, a, b, 0.0f))); cpBodySetPosition(body, cpvadd(pos, boxOffset)); cpShape *shape = cpSpaceAddShape(space, cpSegmentShapeNew(body, a, b, 5.0f)); cpShapeSetElasticity(shape, 0.0f); cpShapeSetFriction(shape, 0.7f); cpShapeSetFilter(shape, cpShapeFilterNew(1, CP_ALL_CATEGORIES, CP_ALL_CATEGORIES)); return body; } static cpBody * addWheel(cpSpace *space, cpVect pos, cpVect boxOffset) { cpFloat radius = 15.0f; cpFloat mass = 1.0f; cpBody *body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForCircle(mass, 0.0f, radius, cpvzero))); cpBodySetPosition(body, cpvadd(pos, boxOffset)); cpShape *shape = cpSpaceAddShape(space, cpCircleShapeNew(body, radius, cpvzero)); cpShapeSetElasticity(shape, 0.0f); cpShapeSetFriction(shape, 0.7f); cpShapeSetFilter(shape, cpShapeFilterNew(1, CP_ALL_CATEGORIES, CP_ALL_CATEGORIES)); return body; } static cpBody * addChassis(cpSpace *space, cpVect pos, cpVect boxOffset) { cpFloat mass = 5.0f; cpFloat width = 80; cpFloat height = 30; cpBody *body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForBox(mass, width, height))); cpBodySetPosition(body, cpvadd(pos, boxOffset)); cpShape *shape = cpSpaceAddShape(space, cpBoxShapeNew(body, width, height, 0.0)); cpShapeSetElasticity(shape, 0.0f); cpShapeSetFriction(shape, 0.7f); cpShapeSetFilter(shape, cpShapeFilterNew(1, CP_ALL_CATEGORIES, CP_ALL_CATEGORIES)); return body; } static cpSpace * init(void) { cpSpace *space = cpSpaceNew(); cpSpaceSetIterations(space, 10); cpSpaceSetGravity(space, cpv(0, -100)); cpSpaceSetSleepTimeThreshold(space, 0.5f); cpBody *staticBody = cpSpaceGetStaticBody(space); cpShape *shape; shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320,240), cpv(320,240), 0.0f)); cpShapeSetElasticity(shape, 1.0f); cpShapeSetFriction(shape, 1.0f); cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320,120), cpv(320,120), 0.0f)); cpShapeSetElasticity(shape, 1.0f); cpShapeSetFriction(shape, 1.0f); cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320,0), cpv(320,0), 0.0f)); cpShapeSetElasticity(shape, 1.0f); cpShapeSetFriction(shape, 1.0f); cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320,-120), cpv(320,-120), 0.0f)); cpShapeSetElasticity(shape, 1.0f); cpShapeSetFriction(shape, 1.0f); cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320,-240), cpv(320,-240), 0.0f)); cpShapeSetElasticity(shape, 1.0f); cpShapeSetFriction(shape, 1.0f); cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320,-240), cpv(-320,240), 0.0f)); cpShapeSetElasticity(shape, 1.0f); cpShapeSetFriction(shape, 1.0f); cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-160,-240), cpv(-160,240), 0.0f)); cpShapeSetElasticity(shape, 1.0f); cpShapeSetFriction(shape, 1.0f); cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(0,-240), cpv(0,240), 0.0f)); cpShapeSetElasticity(shape, 1.0f); cpShapeSetFriction(shape, 1.0f); cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(160,-240), cpv(160,240), 0.0f)); cpShapeSetElasticity(shape, 1.0f); cpShapeSetFriction(shape, 1.0f); cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(320,-240), cpv(320,240), 0.0f)); cpShapeSetElasticity(shape, 1.0f); cpShapeSetFriction(shape, 1.0f); cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); cpVect boxOffset; cpBody *body1, *body2; cpVect posA = cpv( 50, 60); cpVect posB = cpv(110, 60); #define POS_A cpvadd(boxOffset, posA) #define POS_B cpvadd(boxOffset, posB) // Pin Joints - Link shapes with a solid bar or pin. // Keeps the anchor points the same distance apart from when the joint was created. boxOffset = cpv(-320, -240); body1 = addBall(space, posA, boxOffset); body2 = addBall(space, posB, boxOffset); cpSpaceAddConstraint(space, cpPinJointNew(body1, body2, cpv(15,0), cpv(-15,0))); // Slide Joints - Like pin joints but with a min/max distance. // Can be used for a cheap approximation of a rope. boxOffset = cpv(-160, -240); body1 = addBall(space, posA, boxOffset); body2 = addBall(space, posB, boxOffset); cpSpaceAddConstraint(space, cpSlideJointNew(body1, body2, cpv(15,0), cpv(-15,0), 20.0f, 40.0f)); // Pivot Joints - Holds the two anchor points together. Like a swivel. boxOffset = cpv(0, -240); body1 = addBall(space, posA, boxOffset); body2 = addBall(space, posB, boxOffset); cpSpaceAddConstraint(space, cpPivotJointNew(body1, body2, cpvadd(boxOffset, cpv(80,60)))); // cpPivotJointNew() takes it's anchor parameter in world coordinates. The anchors are calculated from that // cpPivotJointNew2() lets you specify the two anchor points explicitly // Groove Joints - Like a pivot joint, but one of the anchors is a line segment that the pivot can slide in boxOffset = cpv(160, -240); body1 = addBall(space, posA, boxOffset); body2 = addBall(space, posB, boxOffset); cpSpaceAddConstraint(space, cpGrooveJointNew(body1, body2, cpv(30,30), cpv(30,-30), cpv(-30,0))); // Damped Springs boxOffset = cpv(-320, -120); body1 = addBall(space, posA, boxOffset); body2 = addBall(space, posB, boxOffset); cpSpaceAddConstraint(space, cpDampedSpringNew(body1, body2, cpv(15,0), cpv(-15,0), 20.0f, 5.0f, 0.3f)); // Damped Rotary Springs boxOffset = cpv(-160, -120); body1 = addBar(space, posA, boxOffset); body2 = addBar(space, posB, boxOffset); // Add some pin joints to hold the circles in place. cpSpaceAddConstraint(space, cpPivotJointNew(body1, staticBody, POS_A)); cpSpaceAddConstraint(space, cpPivotJointNew(body2, staticBody, POS_B)); cpSpaceAddConstraint(space, cpDampedRotarySpringNew(body1, body2, 0.0f, 3000.0f, 60.0f)); // Rotary Limit Joint boxOffset = cpv(0, -120); body1 = addLever(space, posA, boxOffset); body2 = addLever(space, posB, boxOffset); // Add some pin joints to hold the circles in place. cpSpaceAddConstraint(space, cpPivotJointNew(body1, staticBody, POS_A)); cpSpaceAddConstraint(space, cpPivotJointNew(body2, staticBody, POS_B)); // Hold their rotation within 90 degrees of each other. cpSpaceAddConstraint(space, cpRotaryLimitJointNew(body1, body2, -CP_PI/2.0f, CP_PI/2.0f)); // Ratchet Joint - A rotary ratchet, like a socket wrench boxOffset = cpv(160, -120); body1 = addLever(space, posA, boxOffset); body2 = addLever(space, posB, boxOffset); // Add some pin joints to hold the circles in place. cpSpaceAddConstraint(space, cpPivotJointNew(body1, staticBody, POS_A)); cpSpaceAddConstraint(space, cpPivotJointNew(body2, staticBody, POS_B)); // Ratchet every 90 degrees cpSpaceAddConstraint(space, cpRatchetJointNew(body1, body2, 0.0f, CP_PI/2.0f)); // Gear Joint - Maintain a specific angular velocity ratio boxOffset = cpv(-320, 0); body1 = addBar(space, posA, boxOffset); body2 = addBar(space, posB, boxOffset); // Add some pin joints to hold the circles in place. cpSpaceAddConstraint(space, cpPivotJointNew(body1, staticBody, POS_A)); cpSpaceAddConstraint(space, cpPivotJointNew(body2, staticBody, POS_B)); // Force one to sping 2x as fast as the other cpSpaceAddConstraint(space, cpGearJointNew(body1, body2, 0.0f, 2.0f)); // Simple Motor - Maintain a specific angular relative velocity boxOffset = cpv(-160, 0); body1 = addBar(space, posA, boxOffset); body2 = addBar(space, posB, boxOffset); // Add some pin joints to hold the circles in place. cpSpaceAddConstraint(space, cpPivotJointNew(body1, staticBody, POS_A)); cpSpaceAddConstraint(space, cpPivotJointNew(body2, staticBody, POS_B)); // Make them spin at 1/2 revolution per second in relation to each other. cpSpaceAddConstraint(space, cpSimpleMotorNew(body1, body2, CP_PI)); // Make a car with some nice soft suspension boxOffset = cpv(0, 0); cpBody *wheel1 = addWheel(space, posA, boxOffset); cpBody *wheel2 = addWheel(space, posB, boxOffset); cpBody *chassis = addChassis(space, cpv(80, 100), boxOffset); cpSpaceAddConstraint(space, cpGrooveJointNew(chassis, wheel1, cpv(-30, -10), cpv(-30, -40), cpvzero)); cpSpaceAddConstraint(space, cpGrooveJointNew(chassis, wheel2, cpv( 30, -10), cpv( 30, -40), cpvzero)); cpSpaceAddConstraint(space, cpDampedSpringNew(chassis, wheel1, cpv(-30, 0), cpvzero, 50.0f, 20.0f, 10.0f)); cpSpaceAddConstraint(space, cpDampedSpringNew(chassis, wheel2, cpv( 30, 0), cpvzero, 50.0f, 20.0f, 10.0f)); return space; } static void update(cpSpace *space, double dt) { cpSpaceStep(space, dt); } static void destroy(cpSpace *space) { ChipmunkDemoFreeSpaceChildren(space); cpSpaceFree(space); } ChipmunkDemo Joints = { "Joints and Constraints", 1.0/60.0, init, update, ChipmunkDemoDefaultDrawImpl, destroy, }; Chipmunk2D-Chipmunk-7.0.3/demo/LogoSmash.c000066400000000000000000000136711347650476100202520ustar00rootroot00000000000000/* 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 "chipmunk/chipmunk.h" #include "ChipmunkDemo.h" static const int image_width = 188; static const int image_height = 35; static const int image_row_length = 24; static const unsigned char image_bitmap[] = { 15,-16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,-64,15,63,-32,-2,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,31,-64,15,127,-125,-1,-128,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,127,-64,15,127,15,-1,-64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,-1,-64,15,-2, 31,-1,-64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,-1,-64,0,-4,63,-1,-32,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,1,-1,-64,15,-8,127,-1,-32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 1,-1,-64,0,-8,-15,-1,-32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,-31,-1,-64,15,-8,-32, -1,-32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,-15,-1,-64,9,-15,-32,-1,-32,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,31,-15,-1,-64,0,-15,-32,-1,-32,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,63,-7,-1,-64,9,-29,-32,127,-61,-16,63,15,-61,-1,-8,31,-16,15,-8,126,7,-31, -8,31,-65,-7,-1,-64,9,-29,-32,0,7,-8,127,-97,-25,-1,-2,63,-8,31,-4,-1,15,-13, -4,63,-1,-3,-1,-64,9,-29,-32,0,7,-8,127,-97,-25,-1,-2,63,-8,31,-4,-1,15,-13, -2,63,-1,-3,-1,-64,9,-29,-32,0,7,-8,127,-97,-25,-1,-1,63,-4,63,-4,-1,15,-13, -2,63,-33,-1,-1,-32,9,-25,-32,0,7,-8,127,-97,-25,-1,-1,63,-4,63,-4,-1,15,-13, -1,63,-33,-1,-1,-16,9,-25,-32,0,7,-8,127,-97,-25,-1,-1,63,-4,63,-4,-1,15,-13, -1,63,-49,-1,-1,-8,9,-57,-32,0,7,-8,127,-97,-25,-8,-1,63,-2,127,-4,-1,15,-13, -1,-65,-49,-1,-1,-4,9,-57,-32,0,7,-8,127,-97,-25,-8,-1,63,-2,127,-4,-1,15,-13, -1,-65,-57,-1,-1,-2,9,-57,-32,0,7,-8,127,-97,-25,-8,-1,63,-2,127,-4,-1,15,-13, -1,-1,-57,-1,-1,-1,9,-57,-32,0,7,-1,-1,-97,-25,-8,-1,63,-1,-1,-4,-1,15,-13,-1, -1,-61,-1,-1,-1,-119,-57,-32,0,7,-1,-1,-97,-25,-8,-1,63,-1,-1,-4,-1,15,-13,-1, -1,-61,-1,-1,-1,-55,-49,-32,0,7,-1,-1,-97,-25,-8,-1,63,-1,-1,-4,-1,15,-13,-1, -1,-63,-1,-1,-1,-23,-49,-32,127,-57,-1,-1,-97,-25,-1,-1,63,-1,-1,-4,-1,15,-13, -1,-1,-63,-1,-1,-1,-16,-49,-32,-1,-25,-1,-1,-97,-25,-1,-1,63,-33,-5,-4,-1,15, -13,-1,-1,-64,-1,-9,-1,-7,-49,-32,-1,-25,-8,127,-97,-25,-1,-1,63,-33,-5,-4,-1, 15,-13,-1,-1,-64,-1,-13,-1,-32,-49,-32,-1,-25,-8,127,-97,-25,-1,-2,63,-49,-13, -4,-1,15,-13,-1,-1,-64,127,-7,-1,-119,-17,-15,-1,-25,-8,127,-97,-25,-1,-2,63, -49,-13,-4,-1,15,-13,-3,-1,-64,127,-8,-2,15,-17,-1,-1,-25,-8,127,-97,-25,-1, -8,63,-49,-13,-4,-1,15,-13,-3,-1,-64,63,-4,120,0,-17,-1,-1,-25,-8,127,-97,-25, -8,0,63,-57,-29,-4,-1,15,-13,-4,-1,-64,63,-4,0,15,-17,-1,-1,-25,-8,127,-97, -25,-8,0,63,-57,-29,-4,-1,-1,-13,-4,-1,-64,31,-2,0,0,103,-1,-1,-57,-8,127,-97, -25,-8,0,63,-57,-29,-4,-1,-1,-13,-4,127,-64,31,-2,0,15,103,-1,-1,-57,-8,127, -97,-25,-8,0,63,-61,-61,-4,127,-1,-29,-4,127,-64,15,-8,0,0,55,-1,-1,-121,-8, 127,-97,-25,-8,0,63,-61,-61,-4,127,-1,-29,-4,63,-64,15,-32,0,0,23,-1,-2,3,-16, 63,15,-61,-16,0,31,-127,-127,-8,31,-1,-127,-8,31,-128,7,-128,0,0 }; static inline int get_pixel(int x, int y) { return (image_bitmap[(x>>3) + y*image_row_length]>>(~x&0x7)) & 1; } static int bodyCount = 0; static void update(cpSpace *space, double dt) { cpSpaceStep(space, dt); } static void DrawDot(cpBody *body, void *unused) { ChipmunkDebugDrawDot(3.5/ChipmunkDebugDrawPointLineScale, cpBodyGetPosition(body), RGBAColor(0xee/255.0f, 0xe8/255.0f, 0xd5/255.0f, 1.0f)); } static void draw(cpSpace *space) { cpSpaceEachBody(space, DrawDot, NULL); // ChipmunkDebugDrawCollisionPoints(space); } static cpShape * make_ball(cpFloat x, cpFloat y) { cpBody *body = cpBodyNew(1.0, INFINITY); cpBodySetPosition(body, cpv(x, y)); cpShape *shape = cpCircleShapeNew(body, 0.95, cpvzero); cpShapeSetElasticity(shape, 0.0); cpShapeSetFriction(shape, 0.0); return shape; } static cpSpace * init(void) { cpSpace *space = cpSpaceNew(); cpSpaceSetIterations(space, 1); // The space will contain a very large number of similary sized objects. // This is the perfect candidate for using the spatial hash. // Generally you will never need to do this. cpSpaceUseSpatialHash(space, 2.0, 10000); bodyCount = 0; cpBody *body; cpShape *shape; for(int y=0; yn) < 0){ return cpArbiterIgnore(arb); } return cpTrue; } static void update(cpSpace *space, double dt) { cpSpaceStep(space, dt); } static cpSpace * init(void) { ChipmunkDemoMessageString = "One way platforms are trivial in Chipmunk using a very simple collision callback."; cpSpace *space = cpSpaceNew(); cpSpaceSetIterations(space, 10); cpSpaceSetGravity(space, cpv(0, -100)); cpBody *body, *staticBody = cpSpaceGetStaticBody(space); cpShape *shape; // Create segments around the edge of the screen. shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320,-240), cpv(-320,240), 0.0f)); cpShapeSetElasticity(shape, 1.0f); cpShapeSetFriction(shape, 1.0f); cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(320,-240), cpv(320,240), 0.0f)); cpShapeSetElasticity(shape, 1.0f); cpShapeSetFriction(shape, 1.0f); cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320,-240), cpv(320,-240), 0.0f)); cpShapeSetElasticity(shape, 1.0f); cpShapeSetFriction(shape, 1.0f); cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); // Add our one way segment shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-160,-100), cpv(160,-100), 10.0f)); cpShapeSetElasticity(shape, 1.0f); cpShapeSetFriction(shape, 1.0f); cpShapeSetCollisionType(shape, COLLISION_TYPE_ONE_WAY); cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); // We'll use the data pointer for the OneWayPlatform struct platformInstance.n = cpv(0, 1); // let objects pass upwards cpShapeSetUserData(shape, &platformInstance); // Add a ball to test it out cpFloat radius = 15.0f; body = cpSpaceAddBody(space, cpBodyNew(10.0f, cpMomentForCircle(10.0f, 0.0f, radius, cpvzero))); cpBodySetPosition(body, cpv(0, -200)); cpBodySetVelocity(body, cpv(0, 170)); shape = cpSpaceAddShape(space, cpCircleShapeNew(body, radius, cpvzero)); cpShapeSetElasticity(shape, 0.0f); cpShapeSetFriction(shape, 0.9f); cpShapeSetCollisionType(shape, 2); cpCollisionHandler *handler = cpSpaceAddWildcardHandler(space, COLLISION_TYPE_ONE_WAY); handler->preSolveFunc = PreSolve; return space; } static void destroy(cpSpace *space) { ChipmunkDemoFreeSpaceChildren(space); cpSpaceFree(space); } ChipmunkDemo OneWay = { "One Way Platforms", 1.0/60.0, init, update, ChipmunkDemoDefaultDrawImpl, destroy, }; Chipmunk2D-Chipmunk-7.0.3/demo/Planet.c000066400000000000000000000074201347650476100175740ustar00rootroot00000000000000/* 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 "chipmunk/chipmunk.h" #include "ChipmunkDemo.h" static cpBody *planetBody; static cpFloat gravityStrength = 5.0e6f; static void update(cpSpace *space, double dt) { cpSpaceStep(space, dt); } static void planetGravityVelocityFunc(cpBody *body, cpVect gravity, cpFloat damping, cpFloat dt) { // Gravitational acceleration is proportional to the inverse square of // distance, and directed toward the origin. The central planet is assumed // to be massive enough that it affects the satellites but not vice versa. cpVect p = cpBodyGetPosition(body); cpFloat sqdist = cpvlengthsq(p); cpVect g = cpvmult(p, -gravityStrength / (sqdist * cpfsqrt(sqdist))); cpBodyUpdateVelocity(body, g, damping, dt); } static cpVect rand_pos(cpFloat radius) { cpVect v; do { v = cpv(frand()*(640 - 2*radius) - (320 - radius), frand()*(480 - 2*radius) - (240 - radius)); } while(cpvlength(v) < 85.0f); return v; } static void add_box(cpSpace *space) { const cpFloat size = 10.0f; const cpFloat mass = 1.0f; cpVect verts[] = { cpv(-size,-size), cpv(-size, size), cpv( size, size), cpv( size,-size), }; cpFloat radius = cpvlength(cpv(size, size)); cpVect pos = rand_pos(radius); cpBody *body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForPoly(mass, 4, verts, cpvzero, 0.0f))); cpBodySetVelocityUpdateFunc(body, planetGravityVelocityFunc); cpBodySetPosition(body, pos); // Set the box's velocity to put it into a circular orbit from its // starting position. cpFloat r = cpvlength(pos); cpFloat v = cpfsqrt(gravityStrength / r) / r; cpBodySetVelocity(body, cpvmult(cpvperp(pos), v)); // Set the box's angular velocity to match its orbital period and // align its initial angle with its position. cpBodySetAngularVelocity(body, v); cpBodySetAngle(body, cpfatan2(pos.y, pos.x)); cpShape *shape = cpSpaceAddShape(space, cpPolyShapeNew(body, 4, verts, cpTransformIdentity, 0.0)); cpShapeSetElasticity(shape, 0.0f); cpShapeSetFriction(shape, 0.7f); } static cpSpace * init(void) { // Create a rouge body to control the planet manually. cpSpace *space = cpSpaceNew(); cpSpaceSetIterations(space, 20); planetBody = cpSpaceAddBody(space, cpBodyNewKinematic()); cpBodySetAngularVelocity(planetBody, 0.2f); for(int i=0; i<30; i++){ add_box(space); } cpShape *shape = cpSpaceAddShape(space, cpCircleShapeNew(planetBody, 70.0f, cpvzero)); cpShapeSetElasticity(shape, 1.0f); cpShapeSetFriction(shape, 1.0f); cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); return space; } static void destroy(cpSpace *space) { ChipmunkDemoFreeSpaceChildren(space); cpSpaceFree(space); } ChipmunkDemo Planet = { "Planet", 1.0/60.0, init, update, ChipmunkDemoDefaultDrawImpl, destroy, }; Chipmunk2D-Chipmunk-7.0.3/demo/Player.c000066400000000000000000000127331347650476100176100ustar00rootroot00000000000000/* 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 "chipmunk/chipmunk_private.h" #include "ChipmunkDemo.h" #define PLAYER_VELOCITY 500.0 #define PLAYER_GROUND_ACCEL_TIME 0.1 #define PLAYER_GROUND_ACCEL (PLAYER_VELOCITY/PLAYER_GROUND_ACCEL_TIME) #define PLAYER_AIR_ACCEL_TIME 0.25 #define PLAYER_AIR_ACCEL (PLAYER_VELOCITY/PLAYER_AIR_ACCEL_TIME) #define JUMP_HEIGHT 50.0 #define JUMP_BOOST_HEIGHT 55.0 #define FALL_VELOCITY 900.0 #define GRAVITY 2000.0 static cpBody *playerBody = NULL; static cpShape *playerShape = NULL; static cpFloat remainingBoost = 0; static cpBool grounded = cpFalse; static cpBool lastJumpState = cpFalse; static void SelectPlayerGroundNormal(cpBody *body, cpArbiter *arb, cpVect *groundNormal){ cpVect n = cpvneg(cpArbiterGetNormal(arb)); if(n.y > groundNormal->y){ (*groundNormal) = n; } } static void playerUpdateVelocity(cpBody *body, cpVect gravity, cpFloat damping, cpFloat dt) { int jumpState = (ChipmunkDemoKeyboard.y > 0.0f); // Grab the grounding normal from last frame cpVect groundNormal = cpvzero; cpBodyEachArbiter(playerBody, (cpBodyArbiterIteratorFunc)SelectPlayerGroundNormal, &groundNormal); grounded = (groundNormal.y > 0.0); if(groundNormal.y < 0.0f) remainingBoost = 0.0f; // Do a normal-ish update cpBool boost = (jumpState && remainingBoost > 0.0f); cpVect g = (boost ? cpvzero : gravity); cpBodyUpdateVelocity(body, g, damping, dt); // Target horizontal speed for air/ground control cpFloat target_vx = PLAYER_VELOCITY*ChipmunkDemoKeyboard.x; // Update the surface velocity and friction // Note that the "feet" move in the opposite direction of the player. cpVect surface_v = cpv(-target_vx, 0.0); playerShape->surfaceV = surface_v; playerShape->u = (grounded ? PLAYER_GROUND_ACCEL/GRAVITY : 0.0); // Apply air control if not grounded if(!grounded){ // Smoothly accelerate the velocity playerBody->v.x = cpflerpconst(playerBody->v.x, target_vx, PLAYER_AIR_ACCEL*dt); } body->v.y = cpfclamp(body->v.y, -FALL_VELOCITY, INFINITY); } static void update(cpSpace *space, double dt) { int jumpState = (ChipmunkDemoKeyboard.y > 0.0f); // If the jump key was just pressed this frame, jump! if(jumpState && !lastJumpState && grounded){ cpFloat jump_v = cpfsqrt(2.0*JUMP_HEIGHT*GRAVITY); playerBody->v = cpvadd(playerBody->v, cpv(0.0, jump_v)); remainingBoost = JUMP_BOOST_HEIGHT/jump_v; } // Step the space cpSpaceStep(space, dt); remainingBoost -= dt; lastJumpState = jumpState; } static cpSpace * init(void) { cpSpace *space = cpSpaceNew(); space->iterations = 10; space->gravity = cpv(0, -GRAVITY); // space->sleepTimeThreshold = 1000; cpBody *body, *staticBody = cpSpaceGetStaticBody(space); cpShape *shape; // Create segments around the edge of the screen. shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320,-240), cpv(-320,240), 0.0f)); shape->e = 1.0f; shape->u = 1.0f; cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(320,-240), cpv(320,240), 0.0f)); shape->e = 1.0f; shape->u = 1.0f; cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320,-240), cpv(320,-240), 0.0f)); shape->e = 1.0f; shape->u = 1.0f; cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320,240), cpv(320,240), 0.0f)); shape->e = 1.0f; shape->u = 1.0f; cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); // Set up the player body = cpSpaceAddBody(space, cpBodyNew(1.0f, INFINITY)); body->p = cpv(0, -200); body->velocity_func = playerUpdateVelocity; playerBody = body; shape = cpSpaceAddShape(space, cpBoxShapeNew2(body, cpBBNew(-15.0, -27.5, 15.0, 27.5), 10.0)); // shape = cpSpaceAddShape(space, cpSegmentShapeNew(playerBody, cpvzero, cpv(0, radius), radius)); shape->e = 0.0f; shape->u = 0.0f; shape->type = 1; playerShape = shape; // Add some boxes to jump on for(int i=0; i<6; i++){ for(int j=0; j<3; j++){ body = cpSpaceAddBody(space, cpBodyNew(4.0f, INFINITY)); body->p = cpv(100 + j*60, -200 + i*60); shape = cpSpaceAddShape(space, cpBoxShapeNew(body, 50, 50, 0.0)); shape->e = 0.0f; shape->u = 0.7f; } } return space; } static void destroy(cpSpace *space) { ChipmunkDemoFreeSpaceChildren(space); cpSpaceFree(space); } ChipmunkDemo Player = { "Platformer Player Controls", 1.0/180.0, init, update, ChipmunkDemoDefaultDrawImpl, destroy, }; Chipmunk2D-Chipmunk-7.0.3/demo/Plink.c000066400000000000000000000075351347650476100174350ustar00rootroot00000000000000/* 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 "chipmunk/chipmunk.h" #include "ChipmunkDemo.h" static cpFloat pentagon_mass = 0.0f; static cpFloat pentagon_moment = 0.0f; // Iterate over all of the bodies and reset the ones that have fallen offscreen. static void eachBody(cpBody *body, void *unused) { cpVect pos = cpBodyGetPosition(body); if(pos.y < -260 || cpfabs(pos.x) > 340){ cpFloat x = rand()/(cpFloat)RAND_MAX*640 - 320; cpBodySetPosition(body, cpv(x, 260)); } } static void update(cpSpace *space, double dt) { if(ChipmunkDemoRightDown){ cpShape *nearest = cpSpacePointQueryNearest(space, ChipmunkDemoMouse, 0.0, GRAB_FILTER, NULL); if(nearest){ cpBody *body = cpShapeGetBody(nearest); if(cpBodyGetType(body) == CP_BODY_TYPE_STATIC){ cpBodySetType(body, CP_BODY_TYPE_DYNAMIC); cpBodySetMass(body, pentagon_mass); cpBodySetMoment(body, pentagon_moment); } else if(cpBodyGetType(body) == CP_BODY_TYPE_DYNAMIC) { cpBodySetType(body, CP_BODY_TYPE_STATIC); } } } cpSpaceEachBody(space, &eachBody, NULL); cpSpaceStep(space, dt); } #define NUM_VERTS 5 static cpSpace * init(void) { ChipmunkDemoMessageString = "Right click to make pentagons static/dynamic."; cpSpace *space = cpSpaceNew(); cpSpaceSetIterations(space, 5); cpSpaceSetGravity(space, cpv(0, -100)); cpBody *body, *staticBody = cpSpaceGetStaticBody(space); cpShape *shape; // Vertexes for a triangle shape. cpVect tris[] = { cpv(-15,-15), cpv( 0, 10), cpv( 15,-15), }; // Create the static triangles. for(int i=0; i<9; i++){ for(int j=0; j<6; j++){ cpFloat stagger = (j%2)*40; cpVect offset = cpv(i*80 - 320 + stagger, j*70 - 240); shape = cpSpaceAddShape(space, cpPolyShapeNew(staticBody, 3, tris, cpTransformTranslate(offset), 0.0)); cpShapeSetElasticity(shape, 1.0f); cpShapeSetFriction(shape, 1.0f); cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); } } // Create vertexes for a pentagon shape. cpVect verts[NUM_VERTS]; for(int i=0; i 320.0f){ cpBodySetVelocity(ball, cpvzero); cpBodySetPosition(ball, cpv(-224.0f, 200.0f)); } } } static cpBody * add_ball(cpSpace *space, cpVect pos) { cpBody *body = cpSpaceAddBody(space, cpBodyNew(1.0f, cpMomentForCircle(1.0f, 30, 0, cpvzero))); cpBodySetPosition(body, pos); cpShape *shape = cpSpaceAddShape(space, cpCircleShapeNew(body, 30, cpvzero)); cpShapeSetElasticity(shape, 0.0f); cpShapeSetFriction(shape, 0.5f); return body; } static cpSpace * init(void) { ChipmunkDemoMessageString = "Use the arrow keys to control the machine."; cpSpace *space = cpSpaceNew(); cpSpaceSetGravity(space, cpv(0, -600)); cpBody *staticBody = cpSpaceGetStaticBody(space); cpShape *shape; // beveling all of the line segments slightly helps prevent things from getting stuck on cracks shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-256,16), cpv(-256,300), 2.0f)); cpShapeSetElasticity(shape, 0.0f); cpShapeSetFriction(shape, 0.5f); cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-256,16), cpv(-192,0), 2.0f)); cpShapeSetElasticity(shape, 0.0f); cpShapeSetFriction(shape, 0.5f); cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-192,0), cpv(-192, -64), 2.0f)); cpShapeSetElasticity(shape, 0.0f); cpShapeSetFriction(shape, 0.5f); cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-128,-64), cpv(-128,144), 2.0f)); cpShapeSetElasticity(shape, 0.0f); cpShapeSetFriction(shape, 0.5f); cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-192,80), cpv(-192,176), 2.0f)); cpShapeSetElasticity(shape, 0.0f); cpShapeSetFriction(shape, 0.5f); cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-192,176), cpv(-128,240), 2.0f)); cpShapeSetElasticity(shape, 0.0f); cpShapeSetFriction(shape, 0.5f); cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-128,144), cpv(192,64), 2.0f)); cpShapeSetElasticity(shape, 0.0f); cpShapeSetFriction(shape, 0.5f); cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); cpVect verts[] = { cpv(-30,-80), cpv(-30, 80), cpv( 30, 64), cpv( 30,-80), }; cpBody *plunger = cpSpaceAddBody(space, cpBodyNew(1.0f, INFINITY)); cpBodySetPosition(plunger, cpv(-160,-80)); shape = cpSpaceAddShape(space, cpPolyShapeNew(plunger, 4, verts, cpTransformIdentity, 0.0)); cpShapeSetElasticity(shape, 1.0f); cpShapeSetFriction(shape, 0.5f); cpShapeSetFilter(shape, cpShapeFilterNew(CP_NO_GROUP, 1, 1)); // add balls to hopper for(int i=0; i #include "chipmunk/chipmunk.h" #include "ChipmunkDemo.h" #define DENSITY (1.0/10000.0) #define MAX_VERTEXES_PER_VORONOI 16 struct WorleyContex { uint32_t seed; cpFloat cellSize; int width, height; cpBB bb; cpVect focus; }; static inline cpVect HashVect(uint32_t x, uint32_t y, uint32_t seed) { // cpFloat border = 0.21f; cpFloat border = 0.05f; uint32_t h = (x*1640531513 ^ y*2654435789) + seed; return cpv( cpflerp(border, 1.0f - border, (cpFloat)( h & 0xFFFF)/(cpFloat)0xFFFF), cpflerp(border, 1.0f - border, (cpFloat)((h>>16) & 0xFFFF)/(cpFloat)0xFFFF) ); } static cpVect WorleyPoint(int i, int j, struct WorleyContex *context) { cpFloat size = context->cellSize; int width = context->width; int height = context->height; cpBB bb = context->bb; // cpVect fv = cpv(0.5, 0.5); cpVect fv = HashVect(i, j, context->seed); return cpv( cpflerp(bb.l, bb.r, 0.5f) + size*(i + fv.x - width*0.5f), cpflerp(bb.b, bb.t, 0.5f) + size*(j + fv.y - height*0.5f) ); } static int ClipCell(cpShape *shape, cpVect center, int i, int j, struct WorleyContex *context, cpVect *verts, cpVect *clipped, int count) { cpVect other = WorleyPoint(i, j, context); // printf(" other %dx%d: (% 5.2f, % 5.2f) ", i, j, other.x, other.y); if(cpShapePointQuery(shape, other, NULL) > 0.0f){ // printf("excluded\n"); memcpy(clipped, verts, count*sizeof(cpVect)); return count; } else { // printf("clipped\n"); } cpVect n = cpvsub(other, center); cpFloat dist = cpvdot(n, cpvlerp(center, other, 0.5f)); int clipped_count = 0; for(int j=0, i=count-1; j MAX_VERTEXES_PER_VORONOI ? MAX_VERTEXES_PER_VORONOI : count); for(int i=0; iwidth; i++){ for(int j=0; jheight; j++){ if( !(i == cell_i && j == cell_j) && cpShapePointQuery(shape, cell, NULL) < 0.0f ){ count = ClipCell(shape, cell, i, j, context, ping, pong, count); memcpy(ping, pong, count*sizeof(cpVect)); } } } cpVect centroid = cpCentroidForPoly(count, ping); cpFloat mass = cpAreaForPoly(count, ping, 0.0f)*DENSITY; cpFloat moment = cpMomentForPoly(mass, count, ping, cpvneg(centroid), 0.0f); cpBody *new_body = cpSpaceAddBody(space, cpBodyNew(mass, moment)); cpBodySetPosition(new_body, centroid); cpBodySetVelocity(new_body, cpBodyGetVelocityAtWorldPoint(body, centroid)); cpBodySetAngularVelocity(new_body, cpBodyGetAngularVelocity(body)); cpTransform transform = cpTransformTranslate(cpvneg(centroid)); cpShape *new_shape = cpSpaceAddShape(space, cpPolyShapeNew(new_body, count, ping, transform, 0.0)); // Copy whatever properties you have set on the original shape that are important cpShapeSetFriction(new_shape, cpShapeGetFriction(shape)); } static void ShatterShape(cpSpace *space, cpShape *shape, cpFloat cellSize, cpVect focus) { cpSpaceRemoveShape(space, shape); cpSpaceRemoveBody(space, cpShapeGetBody(shape)); cpBB bb = cpShapeGetBB(shape); int width = (int)((bb.r - bb.l)/cellSize) + 1; int height = (int)((bb.t - bb.b)/cellSize) + 1; // printf("Splitting as %dx%d\n", width, height); struct WorleyContex context = {rand(), cellSize, width, height, bb, focus}; for(int i=0; i 5.0f){ ShatterShape(space, (cpShape *)info.shape, cell_size, ChipmunkDemoMouse); } else { // printf("Too small to splinter %f\n", cell_size); } } } } static cpSpace * init(void) { ChipmunkDemoMessageString = "Right click something to shatter it."; cpSpace *space = cpSpaceNew(); cpSpaceSetIterations(space, 30); cpSpaceSetGravity(space, cpv(0, -500)); cpSpaceSetSleepTimeThreshold(space, 0.5f); cpSpaceSetCollisionSlop(space, 0.5f); cpBody *body, *staticBody = cpSpaceGetStaticBody(space); cpShape *shape; // Create segments around the edge of the screen. shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-1000, -240), cpv( 1000, -240), 0.0f)); cpShapeSetElasticity(shape, 1.0f); cpShapeSetFriction(shape, 1.0f); cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); cpFloat width = 200.0f; cpFloat height = 200.0f; cpFloat mass = width*height*DENSITY; cpFloat moment = cpMomentForBox(mass, width, height); body = cpSpaceAddBody(space, cpBodyNew(mass, moment)); shape = cpSpaceAddShape(space, cpBoxShapeNew(body, width, height, 0.0)); cpShapeSetFriction(shape, 0.6f); return space; } static void destroy(cpSpace *space) { ChipmunkDemoFreeSpaceChildren(space); cpSpaceFree(space); } ChipmunkDemo Shatter = { "Shatter.", 1.0f/60.0f, init, update, ChipmunkDemoDefaultDrawImpl, destroy, }; Chipmunk2D-Chipmunk-7.0.3/demo/Slice.c000066400000000000000000000134731347650476100174150ustar00rootroot00000000000000/* 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 "chipmunk/chipmunk.h" #include "ChipmunkDemo.h" #define DENSITY (1.0/10000.0) static void ClipPoly(cpSpace *space, cpShape *shape, cpVect n, cpFloat dist) { cpBody *body = cpShapeGetBody(shape); int count = cpPolyShapeGetCount(shape); int clippedCount = 0; cpVect *clipped = (cpVect *)alloca((count + 1)*sizeof(cpVect)); for(int i=0, j=count-1; ia; cpVect b = context->b; // Clipping plane normal and distance. cpVect n = cpvnormalize(cpvperp(cpvsub(b, a))); cpFloat dist = cpvdot(a, n); ClipPoly(space, shape, n, dist); ClipPoly(space, shape, cpvneg(n), -dist); cpBody *body = cpShapeGetBody(shape); cpSpaceRemoveShape(space, shape); cpSpaceRemoveBody(space, body); cpShapeFree(shape); cpBodyFree(body); } static void SliceQuery(cpShape *shape, cpVect point, cpVect normal, cpFloat alpha, struct SliceContext *context) { cpVect a = context->a; cpVect b = context->b; // Check that the slice was complete by checking that the endpoints aren't in the sliced shape. if(cpShapePointQuery(shape, a, NULL) > 0.0f && cpShapePointQuery(shape, b, NULL) > 0.0f){ // Can't modify the space during a query. // Must make a post-step callback to do the actual slicing. cpSpaceAddPostStepCallback(context->space, (cpPostStepFunc)SliceShapePostStep, shape, context); } } static void update(cpSpace *space, double dt) { cpSpaceStep(space, dt); static cpBool lastClickState = cpFalse; static cpVect sliceStart = {0.0, 0.0}; // Annoying state tracking code that you wouldn't need // in a real event driven system. if(ChipmunkDemoRightClick != lastClickState){ if(ChipmunkDemoRightClick){ // MouseDown sliceStart = ChipmunkDemoMouse; } else { // MouseUp struct SliceContext context = {sliceStart, ChipmunkDemoMouse, space}; cpSpaceSegmentQuery(space, sliceStart, ChipmunkDemoMouse, 0.0, GRAB_FILTER, (cpSpaceSegmentQueryFunc)SliceQuery, &context); } lastClickState = ChipmunkDemoRightClick; } if(ChipmunkDemoRightClick){ ChipmunkDebugDrawSegment(sliceStart, ChipmunkDemoMouse, RGBAColor(1, 0, 0, 1)); } } static cpSpace * init(void) { ChipmunkDemoMessageString = "Right click and drag to slice up the block."; cpSpace *space = cpSpaceNew(); cpSpaceSetIterations(space, 30); cpSpaceSetGravity(space, cpv(0, -500)); cpSpaceSetSleepTimeThreshold(space, 0.5f); cpSpaceSetCollisionSlop(space, 0.5f); cpBody *body, *staticBody = cpSpaceGetStaticBody(space); cpShape *shape; // Create segments around the edge of the screen. shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-1000,-240), cpv(1000,-240), 0.0f)); cpShapeSetElasticity(shape, 1.0f); cpShapeSetFriction(shape, 1.0f); cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); cpFloat width = 200.0f; cpFloat height = 300.0f; cpFloat mass = width*height*DENSITY; cpFloat moment = cpMomentForBox(mass, width, height); body = cpSpaceAddBody(space, cpBodyNew(mass, moment)); shape = cpSpaceAddShape(space, cpBoxShapeNew(body, width, height, 0.0)); cpShapeSetFriction(shape, 0.6f); return space; } static void destroy(cpSpace *space) { ChipmunkDemoFreeSpaceChildren(space); cpSpaceFree(space); } ChipmunkDemo Slice = { "Slice.", 1.0/60.0, init, update, ChipmunkDemoDefaultDrawImpl, destroy, }; Chipmunk2D-Chipmunk-7.0.3/demo/Smooth.c_000066400000000000000000000074401347650476100177630ustar00rootroot00000000000000/* 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 #include #include #include "chipmunk/chipmunk_private.h" #include "ChipmunkDemo.h" static cpBool DrawContacts(cpArbiter *arb, cpSpace *space, void *data){ cpContactPointSet set = cpArbiterGetContactPointSet(arb); for(int i=0; i b ? a : b) #define MIN(a, b) (a < b ? a : b) static cpSpace * init(void) { cpSpace *space = cpSpaceNew(); cpSpaceSetIterations(space, 5); cpSpaceSetDamping(space, 0.1f); cpSpaceSetDefaultCollisionHandler(space, NULL, DrawContacts, NULL, NULL, NULL); { cpFloat mass = 1.0f; cpFloat length = 100.0f; cpVect a = cpv(-length/2.0f, 0.0f), b = cpv(length/2.0f, 0.0f); cpBody *body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForSegment(mass, a, b))); cpBodySetPos(body, cpv(-160.0f, 80.0f)); cpSpaceAddShape(space, cpSegmentShapeNew(body, a, b, 30.0f)); } { cpFloat mass = 1.0f; const int NUM_VERTS = 5; cpVect verts[NUM_VERTS]; for(int i=0; i #include "chipmunk/chipmunk.h" #include "ChipmunkDemo.h" static cpFloat springForce(cpConstraint *spring, cpFloat dist) { cpFloat clamp = 20.0f; return cpfclamp(cpDampedSpringGetRestLength(spring) - dist, -clamp, clamp)*cpDampedSpringGetStiffness(spring); } static cpConstraint * new_spring(cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB, cpFloat restLength, cpFloat stiff, cpFloat damp) { cpConstraint *spring = cpDampedSpringNew(a, b, anchorA, anchorB, restLength, stiff, damp); cpDampedSpringSetSpringForceFunc(spring, springForce); return spring; } static void update(cpSpace *space, double dt) { cpSpaceStep(space, dt); } static cpBody * add_bar(cpSpace *space, cpVect a, cpVect b, int group) { cpVect center = cpvmult(cpvadd(a, b), 1.0f/2.0f); cpFloat length = cpvlength(cpvsub(b, a)); cpFloat mass = length/160.0f; cpBody *body = cpSpaceAddBody(space, cpBodyNew(mass, mass*length*length/12.0f)); cpBodySetPosition(body, center); cpShape *shape = cpSpaceAddShape(space, cpSegmentShapeNew(body, cpvsub(a, center), cpvsub(b, center), 10.0f)); cpShapeSetFilter(shape, cpShapeFilterNew(group, CP_ALL_CATEGORIES, CP_ALL_CATEGORIES)); return body; } static cpSpace * init(void) { cpSpace *space = cpSpaceNew(); cpBody *staticBody = cpSpaceGetStaticBody(space); cpBody *body1 = add_bar(space, cpv(-240, 160), cpv(-160, 80), 1); cpBody *body2 = add_bar(space, cpv(-160, 80), cpv( -80, 160), 1); cpBody *body3 = add_bar(space, cpv( 0, 160), cpv( 80, 0), 0); cpBody *body4 = add_bar(space, cpv( 160, 160), cpv( 240, 160), 0); cpBody *body5 = add_bar(space, cpv(-240, 0), cpv(-160, -80), 2); cpBody *body6 = add_bar(space, cpv(-160, -80), cpv( -80, 0), 2); cpBody *body7 = add_bar(space, cpv( -80, 0), cpv( 0, 0), 2); cpBody *body8 = add_bar(space, cpv( 0, -80), cpv( 80, -80), 0); cpBody *body9 = add_bar(space, cpv( 240, 80), cpv( 160, 0), 3); cpBody *body10 = add_bar(space, cpv( 160, 0), cpv( 240, -80), 3); cpBody *body11 = add_bar(space, cpv(-240, -80), cpv(-160, -160), 4); cpBody *body12 = add_bar(space, cpv(-160, -160), cpv( -80, -160), 4); cpBody *body13 = add_bar(space, cpv( 0, -160), cpv( 80, -160), 0); cpBody *body14 = add_bar(space, cpv( 160, -160), cpv( 240, -160), 0); cpSpaceAddConstraint(space, cpPivotJointNew2( body1, body2, cpv( 40,-40), cpv(-40,-40))); cpSpaceAddConstraint(space, cpPivotJointNew2( body5, body6, cpv( 40,-40), cpv(-40,-40))); cpSpaceAddConstraint(space, cpPivotJointNew2( body6, body7, cpv( 40, 40), cpv(-40, 0))); cpSpaceAddConstraint(space, cpPivotJointNew2( body9, body10, cpv(-40,-40), cpv(-40, 40))); cpSpaceAddConstraint(space, cpPivotJointNew2(body11, body12, cpv( 40,-40), cpv(-40, 0))); cpFloat stiff = 100.0f; cpFloat damp = 0.5f; cpSpaceAddConstraint(space, new_spring(staticBody, body1, cpv(-320, 240), cpv(-40, 40), 0.0f, stiff, damp)); cpSpaceAddConstraint(space, new_spring(staticBody, body1, cpv(-320, 80), cpv(-40, 40), 0.0f, stiff, damp)); cpSpaceAddConstraint(space, new_spring(staticBody, body1, cpv(-160, 240), cpv(-40, 40), 0.0f, stiff, damp)); cpSpaceAddConstraint(space, new_spring(staticBody, body2, cpv(-160, 240), cpv( 40, 40), 0.0f, stiff, damp)); cpSpaceAddConstraint(space, new_spring(staticBody, body2, cpv( 0, 240), cpv( 40, 40), 0.0f, stiff, damp)); cpSpaceAddConstraint(space, new_spring(staticBody, body3, cpv( 80, 240), cpv(-40, 80), 0.0f, stiff, damp)); cpSpaceAddConstraint(space, new_spring(staticBody, body4, cpv( 80, 240), cpv(-40, 0), 0.0f, stiff, damp)); cpSpaceAddConstraint(space, new_spring(staticBody, body4, cpv( 320, 240), cpv( 40, 0), 0.0f, stiff, damp)); cpSpaceAddConstraint(space, new_spring(staticBody, body5, cpv(-320, 80), cpv(-40, 40), 0.0f, stiff, damp)); cpSpaceAddConstraint(space, new_spring(staticBody, body9, cpv( 320, 80), cpv( 40, 40), 0.0f, stiff, damp)); cpSpaceAddConstraint(space, new_spring(staticBody, body10, cpv( 320, 0), cpv( 40,-40), 0.0f, stiff, damp)); cpSpaceAddConstraint(space, new_spring(staticBody, body10, cpv( 320,-160), cpv( 40,-40), 0.0f, stiff, damp)); cpSpaceAddConstraint(space, new_spring(staticBody, body11, cpv(-320,-160), cpv(-40, 40), 0.0f, stiff, damp)); cpSpaceAddConstraint(space, new_spring(staticBody, body12, cpv(-240,-240), cpv(-40, 0), 0.0f, stiff, damp)); cpSpaceAddConstraint(space, new_spring(staticBody, body12, cpv( 0,-240), cpv( 40, 0), 0.0f, stiff, damp)); cpSpaceAddConstraint(space, new_spring(staticBody, body13, cpv( 0,-240), cpv(-40, 0), 0.0f, stiff, damp)); cpSpaceAddConstraint(space, new_spring(staticBody, body13, cpv( 80,-240), cpv( 40, 0), 0.0f, stiff, damp)); cpSpaceAddConstraint(space, new_spring(staticBody, body14, cpv( 80,-240), cpv(-40, 0), 0.0f, stiff, damp)); cpSpaceAddConstraint(space, new_spring(staticBody, body14, cpv( 240,-240), cpv( 40, 0), 0.0f, stiff, damp)); cpSpaceAddConstraint(space, new_spring(staticBody, body14, cpv( 320,-160), cpv( 40, 0), 0.0f, stiff, damp)); cpSpaceAddConstraint(space, new_spring( body1, body5, cpv( 40,-40), cpv(-40, 40), 0.0f, stiff, damp)); cpSpaceAddConstraint(space, new_spring( body1, body6, cpv( 40,-40), cpv( 40, 40), 0.0f, stiff, damp)); cpSpaceAddConstraint(space, new_spring( body2, body3, cpv( 40, 40), cpv(-40, 80), 0.0f, stiff, damp)); cpSpaceAddConstraint(space, new_spring( body3, body4, cpv(-40, 80), cpv(-40, 0), 0.0f, stiff, damp)); cpSpaceAddConstraint(space, new_spring( body3, body4, cpv( 40,-80), cpv(-40, 0), 0.0f, stiff, damp)); cpSpaceAddConstraint(space, new_spring( body3, body7, cpv( 40,-80), cpv( 40, 0), 0.0f, stiff, damp)); cpSpaceAddConstraint(space, new_spring( body3, body7, cpv(-40, 80), cpv(-40, 0), 0.0f, stiff, damp)); cpSpaceAddConstraint(space, new_spring( body3, body8, cpv( 40,-80), cpv( 40, 0), 0.0f, stiff, damp)); cpSpaceAddConstraint(space, new_spring( body3, body9, cpv( 40,-80), cpv(-40,-40), 0.0f, stiff, damp)); cpSpaceAddConstraint(space, new_spring( body4, body9, cpv( 40, 0), cpv( 40, 40), 0.0f, stiff, damp)); cpSpaceAddConstraint(space, new_spring( body5, body11, cpv(-40, 40), cpv(-40, 40), 0.0f, stiff, damp)); cpSpaceAddConstraint(space, new_spring( body5, body11, cpv( 40,-40), cpv( 40,-40), 0.0f, stiff, damp)); cpSpaceAddConstraint(space, new_spring( body7, body8, cpv( 40, 0), cpv(-40, 0), 0.0f, stiff, damp)); cpSpaceAddConstraint(space, new_spring( body8, body12, cpv(-40, 0), cpv( 40, 0), 0.0f, stiff, damp)); cpSpaceAddConstraint(space, new_spring( body8, body13, cpv(-40, 0), cpv(-40, 0), 0.0f, stiff, damp)); cpSpaceAddConstraint(space, new_spring( body8, body13, cpv( 40, 0), cpv( 40, 0), 0.0f, stiff, damp)); cpSpaceAddConstraint(space, new_spring( body8, body14, cpv( 40, 0), cpv(-40, 0), 0.0f, stiff, damp)); cpSpaceAddConstraint(space, new_spring(body10, body14, cpv( 40,-40), cpv(-40, 0), 0.0f, stiff, damp)); cpSpaceAddConstraint(space, new_spring(body10, body14, cpv( 40,-40), cpv(-40, 0), 0.0f, stiff, damp)); return space; } static void destroy(cpSpace *space) { ChipmunkDemoFreeSpaceChildren(space); cpSpaceFree(space); } ChipmunkDemo Springies = { "Springies", 1.0/60.0, init, update, ChipmunkDemoDefaultDrawImpl, destroy, }; Chipmunk2D-Chipmunk-7.0.3/demo/Sticky.c000066400000000000000000000156011347650476100176170ustar00rootroot00000000000000/* 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 "chipmunk/chipmunk.h" #include "ChipmunkDemo.h" enum { COLLISION_TYPE_STICKY = 1, }; #define STICK_SENSOR_THICKNESS 2.5f static void PostStepAddJoint(cpSpace *space, void *key, void *data) { // printf("Adding joint for %p\n", data); cpConstraint *joint = (cpConstraint *)key; cpSpaceAddConstraint(space, joint); } static cpBool StickyPreSolve(cpArbiter *arb, cpSpace *space, void *data) { // We want to fudge the collisions a bit to allow shapes to overlap more. // This simulates their squishy sticky surface, and more importantly // keeps them from separating and destroying the joint. // Track the deepest collision point and use that to determine if a rigid collision should occur. cpFloat deepest = INFINITY; // Grab the contact set and iterate over them. cpContactPointSet contacts = cpArbiterGetContactPointSet(arb); for(int i=0; ipreSolveFunc = StickyPreSolve; handler->separateFunc = StickySeparate; return space; } static void destroy(cpSpace *space) { ChipmunkDemoFreeSpaceChildren(space); cpSpaceFree(space); } ChipmunkDemo Sticky = { "Sticky Surfaces", 1.0/60.0, init, update, ChipmunkDemoDefaultDrawImpl, destroy, }; Chipmunk2D-Chipmunk-7.0.3/demo/Tank.c000066400000000000000000000122271347650476100172470ustar00rootroot00000000000000/* 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 "chipmunk/chipmunk.h" #include "ChipmunkDemo.h" static cpBody *tankBody, *tankControlBody; static void update(cpSpace *space, double dt) { // turn the control body based on the angle relative to the actual body cpVect mouseDelta = cpvsub(ChipmunkDemoMouse, cpBodyGetPosition(tankBody)); cpFloat turn = cpvtoangle(cpvunrotate(cpBodyGetRotation(tankBody), mouseDelta)); cpBodySetAngle(tankControlBody, cpBodyGetAngle(tankBody) - turn); // drive the tank towards the mouse if(cpvnear(ChipmunkDemoMouse, cpBodyGetPosition(tankBody), 30.0)){ cpBodySetVelocity(tankControlBody, cpvzero); // stop } else { cpFloat direction = (cpvdot(mouseDelta, cpBodyGetRotation(tankBody)) > 0.0 ? 1.0 : -1.0); cpBodySetVelocity(tankControlBody, cpvrotate(cpBodyGetRotation(tankBody), cpv(30.0f*direction, 0.0f))); } cpSpaceStep(space, dt); } static cpBody * add_box(cpSpace *space, cpFloat size, cpFloat mass) { cpFloat radius = cpvlength(cpv(size, size)); cpBody *body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForBox(mass, size, size))); cpBodySetPosition(body, cpv(frand()*(640 - 2*radius) - (320 - radius), frand()*(480 - 2*radius) - (240 - radius))); cpShape *shape = cpSpaceAddShape(space, cpBoxShapeNew(body, size, size, 0.0)); cpShapeSetElasticity(shape, 0.0f); cpShapeSetFriction(shape, 0.7f); return body; } static cpSpace * init(void) { ChipmunkDemoMessageString = "Use the mouse to drive the tank, it will follow the cursor."; cpSpace *space = cpSpaceNew(); cpSpaceSetIterations(space, 10); cpSpaceSetSleepTimeThreshold(space, 0.5f); cpBody *staticBody = cpSpaceGetStaticBody(space); cpShape *shape; // Create segments around the edge of the screen. shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320,-240), cpv(-320,240), 0.0f)); cpShapeSetElasticity(shape, 1.0f); cpShapeSetFriction(shape, 1.0f); cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(320,-240), cpv(320,240), 0.0f)); cpShapeSetElasticity(shape, 1.0f); cpShapeSetFriction(shape, 1.0f); cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320,-240), cpv(320,-240), 0.0f)); cpShapeSetElasticity(shape, 1.0f); cpShapeSetFriction(shape, 1.0f); cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320,240), cpv(320,240), 0.0f)); cpShapeSetElasticity(shape, 1.0f); cpShapeSetFriction(shape, 1.0f); cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); for(int i=0; i<50; i++){ cpBody *body = add_box(space, 20, 1); cpConstraint *pivot = cpSpaceAddConstraint(space, cpPivotJointNew2(staticBody, body, cpvzero, cpvzero)); cpConstraintSetMaxBias(pivot, 0); // disable joint correction cpConstraintSetMaxForce(pivot, 1000.0f); // emulate linear friction cpConstraint *gear = cpSpaceAddConstraint(space, cpGearJointNew(staticBody, body, 0.0f, 1.0f)); cpConstraintSetMaxBias(gear, 0); // disable joint correction cpConstraintSetMaxForce(gear, 5000.0f); // emulate angular friction } // We joint the tank to the control body and control the tank indirectly by modifying the control body. tankControlBody = cpSpaceAddBody(space, cpBodyNewKinematic()); tankBody = add_box(space, 30, 10); cpConstraint *pivot = cpSpaceAddConstraint(space, cpPivotJointNew2(tankControlBody, tankBody, cpvzero, cpvzero)); cpConstraintSetMaxBias(pivot, 0); // disable joint correction cpConstraintSetMaxForce(pivot, 10000.0f); // emulate linear friction cpConstraint *gear = cpSpaceAddConstraint(space, cpGearJointNew(tankControlBody, tankBody, 0.0f, 1.0f)); cpConstraintSetErrorBias(gear, 0); // attempt to fully correct the joint each step cpConstraintSetMaxBias(gear, 1.2f); // but limit it's angular correction rate cpConstraintSetMaxForce(gear, 50000.0f); // emulate angular friction return space; } static void destroy(cpSpace *space) { ChipmunkDemoFreeSpaceChildren(space); cpSpaceFree(space); } ChipmunkDemo Tank = { "Tank", 1.0/60.0, init, update, ChipmunkDemoDefaultDrawImpl, destroy, }; Chipmunk2D-Chipmunk-7.0.3/demo/TheoJansen.c000066400000000000000000000136711347650476100204140ustar00rootroot00000000000000/* 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. */ /* * The previous WalkBot demo I designed was fairly disappointing, so I implemented * the mechanism that Theo Jansen uses in his kinetic sculptures. Brilliant. * Read more here: http://en.wikipedia.org/wiki/Theo_Jansen */ #include "chipmunk/chipmunk.h" #include "ChipmunkDemo.h" static cpConstraint *motor; static void update(cpSpace *space, double dt) { cpFloat coef = (2.0f + ChipmunkDemoKeyboard.y)/3.0f; cpFloat rate = ChipmunkDemoKeyboard.x*10.0f*coef; cpSimpleMotorSetRate(motor, rate); cpConstraintSetMaxForce(motor, (rate) ? 100000.0f : 0.0f); cpSpaceStep(space, dt); } static cpFloat seg_radius = 3.0f; static void make_leg(cpSpace *space, cpFloat side, cpFloat offset, cpBody *chassis, cpBody *crank, cpVect anchor) { cpVect a, b; cpShape *shape; cpFloat leg_mass = 1.0f; // make leg a = cpvzero, b = cpv(0.0f, side); cpBody *upper_leg = cpSpaceAddBody(space, cpBodyNew(leg_mass, cpMomentForSegment(leg_mass, a, b, 0.0f))); cpBodySetPosition(upper_leg, cpv(offset, 0.0f)); shape = cpSpaceAddShape(space, cpSegmentShapeNew(upper_leg, a, b, seg_radius)); cpShapeSetFilter(shape, cpShapeFilterNew(1, CP_ALL_CATEGORIES, CP_ALL_CATEGORIES)); cpSpaceAddConstraint(space, cpPivotJointNew2(chassis, upper_leg, cpv(offset, 0.0f), cpvzero)); // lower leg a = cpvzero, b = cpv(0.0f, -1.0f*side); cpBody *lower_leg = cpSpaceAddBody(space, cpBodyNew(leg_mass, cpMomentForSegment(leg_mass, a, b, 0.0f))); cpBodySetPosition(lower_leg, cpv(offset, -side)); shape = cpSpaceAddShape(space, cpSegmentShapeNew(lower_leg, a, b, seg_radius)); cpShapeSetFilter(shape, cpShapeFilterNew(1, CP_ALL_CATEGORIES, CP_ALL_CATEGORIES)); shape = cpSpaceAddShape(space, cpCircleShapeNew(lower_leg, seg_radius*2.0f, b)); cpShapeSetFilter(shape, cpShapeFilterNew(1, CP_ALL_CATEGORIES, CP_ALL_CATEGORIES)); cpShapeSetElasticity(shape, 0.0f); cpShapeSetFriction(shape, 1.0f); cpSpaceAddConstraint(space, cpPinJointNew(chassis, lower_leg, cpv(offset, 0.0f), cpvzero)); cpSpaceAddConstraint(space, cpGearJointNew(upper_leg, lower_leg, 0.0f, 1.0f)); cpConstraint *constraint; cpFloat diag = cpfsqrt(side*side + offset*offset); constraint = cpSpaceAddConstraint(space, cpPinJointNew(crank, upper_leg, anchor, cpv(0.0f, side))); cpPinJointSetDist(constraint, diag); constraint = cpSpaceAddConstraint(space, cpPinJointNew(crank, lower_leg, anchor, cpvzero)); cpPinJointSetDist(constraint, diag); } static cpSpace * init(void) { ChipmunkDemoMessageString = "Use the arrow keys to control the machine."; cpSpace *space = cpSpaceNew(); cpSpaceSetIterations(space, 20); cpSpaceSetGravity(space, cpv(0,-500)); cpBody *staticBody = cpSpaceGetStaticBody(space); cpShape *shape; cpVect a, b; // Create segments around the edge of the screen. shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320,-240), cpv(-320,240), 0.0f)); cpShapeSetElasticity(shape, 1.0f); cpShapeSetFriction(shape, 1.0f); cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(320,-240), cpv(320,240), 0.0f)); cpShapeSetElasticity(shape, 1.0f); cpShapeSetFriction(shape, 1.0f); cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320,-240), cpv(320,-240), 0.0f)); cpShapeSetElasticity(shape, 1.0f); cpShapeSetFriction(shape, 1.0f); cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); cpFloat offset = 30.0f; // make chassis cpFloat chassis_mass = 2.0f; a = cpv(-offset, 0.0f), b = cpv(offset, 0.0f); cpBody *chassis = cpSpaceAddBody(space, cpBodyNew(chassis_mass, cpMomentForSegment(chassis_mass, a, b, 0.0f))); shape = cpSpaceAddShape(space, cpSegmentShapeNew(chassis, a, b, seg_radius)); cpShapeSetFilter(shape, cpShapeFilterNew(1, CP_ALL_CATEGORIES, CP_ALL_CATEGORIES)); // make crank cpFloat crank_mass = 1.0f; cpFloat crank_radius = 13.0f; cpBody *crank = cpSpaceAddBody(space, cpBodyNew(crank_mass, cpMomentForCircle(crank_mass, crank_radius, 0.0f, cpvzero))); shape = cpSpaceAddShape(space, cpCircleShapeNew(crank, crank_radius, cpvzero)); cpShapeSetFilter(shape, cpShapeFilterNew(1, CP_ALL_CATEGORIES, CP_ALL_CATEGORIES)); cpSpaceAddConstraint(space, cpPivotJointNew2(chassis, crank, cpvzero, cpvzero)); cpFloat side = 30.0f; int num_legs = 2; for(int i=0; ifips # this area is managed by fips, do not edit .fips-* *.pyc # 1.0f) ? 0.0f : g; sg_begin_default_pass(&pass_action, sapp_width(), sapp_height()); sg_end_pass(); sg_commit(); } void cleanup(void) { sg_shutdown(); } sapp_desc sokol_main(int argc, char* argv[]) { return (sapp_desc){ .init_cb = init, .frame_cb = frame, .cleanup_cb = cleanup, .width = 400, .height = 300, .window_title = "Clear (sokol app)", }; } ``` # sokol_audio.h A minimal audio-streaming API: - you provide a mono- or stereo-stream of 32-bit float samples which sokol_audio.h forwards into platform-specific backends - two ways to provide the data: 1. directly fill backend audio buffer from your callback function running in the audio thread 2. alternatively push small packets of audio data from your main loop, or a separate thread created by you - platform backends: - Windows: WASAPI - macOS/iOS: CoreAudio - Linux: ALSA - emscripten: WebAudio + ScriptProcessorNode (doesn't use the emscripten-provided OpenAL or SDL Audio wrappers) A simple mono square-wave generator using the callback model: ```cpp // the sample callback, running in audio thread static void stream_cb(float* buffer, int num_frames, int num_channels) { assert(1 == num_channels); static uint32_t count = 0; for (int i = 0; i < num_frames; i++) { buffer[i] = (count++ & (1<<3)) ? 0.5f : -0.5f; } } int main() { // init sokol-audio with default params saudio_setup(&(saudio_desc){ .stream_cb = stream_cb }); // run main loop ... // shutdown sokol-audio saudio_shutdown(); return 0; ``` The same code using the push-model ```cpp #define BUF_SIZE (32) int main() { // init sokol-audio with default params, no callback saudio_setup(&(saudio_desc){0}); assert(saudio_channels() == 1); // a small intermediate buffer so we don't need to push // individual samples, which would be quite inefficient float buf[BUF_SIZE]; int buf_pos = 0; uint32_t count = 0; // push samples from main loop bool done = false; while (!done) { // generate and push audio samples... int num_frames = saudio_expect(); for (int i = 0; i < num_frames; i++) { // simple square wave generator buf[buf_pos++] = (count++ & (1<<3)) ? 0.5f : -0.5f; if (buf_pos == BUF_SIZE) { buf_pos = 0; saudio_push(buf, BUF_SIZE); } } // handle other per-frame stuff... ... } // shutdown sokol-audio saudio_shutdown(); return 0; } ``` # sokol_time.h: Simple cross-platform time measurement: ```c #include "sokol_time.h" ... /* initialize sokol_time */ stm_setup(); /* take start timestamp */ uint64_t start = stm_now(); ...some code to measure... /* compute elapsed time */ uint64_t elapsed = stm_since(start); /* convert to time units */ double seconds = stm_sec(elapsed); double milliseconds = stm_ms(elapsed); double microseconds = stm_us(elapsed); double nanoseconds = stm_ns(elapsed); /* difference between 2 time stamps */ uint64_t start = stm_now(); ... uint64_t end = stm_now(); uint64_t elapsed = stm_diff(end, start); /* compute a 'lap time' (e.g. for fps) */ uint64_t last_time = 0; while (!done) { ...render something... double frame_time_ms = stm_ms(stm_laptime(&last_time)); } ``` # sokol_args.h Unified argument parsing for web and native apps. Uses argc/argv on native platforms and the URL query string on the web. Example URL with one arg: https://floooh.github.io/tiny8bit/kc85.html?type=kc85_4 The same as command line app: > kc85 type=kc85_4 Parsed like this: ```c #include "sokol_args.h" int main(int argc, char* argv[]) { sargs_setup(&(sargs_desc){ .argc=argc, .argv=argv }); if (sargs_exists("type")) { if (sargs_equals("type", "kc85_4")) { // start as KC85/4 } else if (sargs_equals("type", "kc85_3")) { // start as KC85/3 } else { // start as KC85/2 } } sargs_shutdown(); return 0; } ``` See the sokol_args.h header for a more complete documentation, and the [Tiny Emulators](https://floooh.github.io/tiny8bit/) for more interesting usage examples. # Overview of planned features A list of things I'd like to do next: ## sokol_gfx.h planned features: - 2 small additions to the per-pool-slot generation counters in sokol_gfx.h: - an sg_setup() option to disable a pool slot when it's generation counter overflows, this makes the dangling-check for resource ids watertight at the cost that the pool will run out of slots at some point - instead of the 16/16-bit split in the resource ids for unique-tag vs slot-index, only use as many bits as necessary for the slot-index (based on the number of slots in the pool), and the remaining bits for the unique-tag ## sokol_app.h planned features: Mainly some "missing features" for desktop apps: - allow 'programmatic quit' requested by the application - allow to intercept the window close button, so that the app can show a 'do you really want to quit?' dialog box - define an application icon - change the window title on existing window - allow to programmatically activate and deactivate fullscreen - pointer lock - show/hide mouse cursor - allow to change mouse cursor image (at first only switch between system-provided standard images) ## sokol_audio.h planned features: - implement an alternative WebAudio backend using Audio Worklets and WASM threads ## Potential new sokol headers: - system clipboard support - query filesystem standard locations - simple file access API (at least async file/URL loading) - gamepad support - simple cross-platform touch gesture recognition # Updates - **15-Mar-2019**: various Dear ImGui related changes: - there's a new utility header sokol_imgui.h with a simple drop-in renderer for Dear ImGui on top of sokol_gfx.h and sokol_app.h (sokol_app.h is optional, and only used for input handling) - the sokol_gfx_imgui.h debug inspection header no longer depends on internal data structures and functions of sokol_gfx.h, as such it is now a normal *utility header* and has been moved to the *utils* directory - the implementation macro for sokol_gfx_imgui.h has been changed from SOKOL_IMPL to SOKOL_GFX_IMGUI_IMPL (so when you suddenly get unresoled linker errors, that's the reason) - all headers now have two preprocessor defines for the declaration and implementation (for instance in sokol_gfx.h: SOKOL_GFX_INCLUDED and SOKOL_GFX_IMPL_INCLUDED) these are checked in the utility-headers to provide useful error message when dependent headers are missing - **05-Mar-2019**: sokol_gfx.h now has a 'trace hook' API, and I have started implementing optional debug-inspection-UI headers on top of Dear ImGui: - sokol_gfx.h has a new function *sg_install_trace_hooks()*, this allows you to install a callback function for each public sokol_gfx.h function (and a couple of error callbacks). For more details, search for "TRACE HOOKS" in sokol_gfx.h - when creating sokol_gfx.h resources, you can now set a 'debug label' in the desc structure, this is ignored by sokol_gfx.h itself, but is useful for debuggers or profilers hooking in via the new trace hooks - likewise, two new functions *sg_push_debug_group()* and *sg_pop_debug_group()* can be used to group related drawing functions under a name, this is also ignored by sokol_gfx.h itself and only useful when hooking into the API calls - I have started a new 'subproject' in the 'imgui' directory, this will contain a slowly growing set of optional debug-inspection-UI headers which allow to peek under the hood of the Sokol headers. The UIs are implemented with [Dear ImGui](https://github.com/ocornut/imgui). Again, see the README in the 'imgui' directory and the headers in there for details, and check out the live demos on the [Sokol Sample Webpage](https://floooh.github.io/sokol-html5/) (click on the little UI buttons in the top right corner of each thumbnail) - **21-Feb-2019**: sokol_app.h and sokol_audio.h now have an alternative set of callbacks with user_data arguments. This is useful if you don't want or cannot store your own application state in global variables. See the header documentation in sokol_app.h and sokol_audio.h for details, and check out the samples *sapp/noentry-sapp.c* and *sapp/modplay-sapp.c* in https://github.com/floooh/sokol-samples - **19-Feb-2019**: sokol_app.h now has an alternative mode where it doesn't "hijack" the platform's main() function. Search for SOKOL_NO_ENTRY in sokol_app.h for details and documentation. - **26-Jan-2019**: sokol_app.h now has an Android backend contributed by [Gustav Olsson](https://github.com/gustavolsson)! See the [sokol-samples readme](https://github.com/floooh/sokol-samples/blob/master/README.md) for build instructions. - **21-Jan-2019**: sokol_gfx.h - pool-slot-generation-counters and a dummy backend: - Resource pool slots now have a generation-counter for the resource-id unique-tag, instead of a single counter for the whole pool. This allows to create many more unique handles. - sokol_gfx.h now has a dummy backend, activated by defining SOKOL_DUMMY_BACKEND (instead of SOKOL_METAL, SOKOL_D3D11, ...), this allows to write 'headless' tests (and there's now a sokol-gfx-test in the sokol-samples repository which mainly tests the resource pool system) - **12-Jan-2019**: sokol_gfx.h - setting the pipeline state and resource bindings now happens in separate calls, specifically: - *sg_apply_draw_state()* has been replaced with *sg_apply_pipeline()* and *sg_apply_bindings()* - the *sg_draw_state* struct has been replaced with *sg_bindings* - *sg_apply_uniform_block()* has been renamed to *sg_apply_uniforms()* All existing code will still work. See [this blog post](https://floooh.github.io/2019/01/12/sokol-apply-pipeline.html) for details. - **29-Oct-2018**: - sokol_gfx.h has a new function **sg_append_buffer()** which allows to append new data to a buffer multiple times per frame and interleave this with draw calls. This basically implements the D3D11_MAP_WRITE_NO_OVERWRITE update strategy for buffer objects. For example usage, see the updated Dear ImGui samples in the [sokol_samples repo](https://github.com/floooh/sokol-samples) - the GL state cache in sokol_gfx.h handles buffers bindings in a more robust way, previously it might have happened that the buffer binding gets confused when creating buffers or updating buffer contents in the render loop - **17-Oct-2018**: sokol_args.h added - **26-Sep-2018**: sokol_audio.h ready for prime time :) - **11-May-2018**: sokol_gfx.h now autodetects iOS vs MacOS in the Metal backend during compilation using the standard define TARGET_OS_IPHONE defined in the TargetConditionals.h system header, please replace the old backend-selection defines SOKOL_METAL_MACOS and SOKOL_METAL_IOS with **SOKOL_METAL** - **20-Apr-2018**: 3 new context-switching functions have been added to make it possible to use sokol together with applications that use multiple GL contexts. On D3D11 and Metal, the functions are currently empty. See the new section ```WORKING WITH CONTEXTS``` in the sokol_gfx.h header documentation, and the new sample [multiwindow-glfw](https://github.com/floooh/sokol-samples/blob/master/glfw/multiwindow-glfw.c) - **31-Jan-2018**: The vertex layout declaration in sg\_pipeline\_desc had some fairly subtle flaws and has been changed to work like Metal or Vulkan. The gist is that the vertex-buffer-layout properties (vertex stride, vertex-step-rate and -step-function for instancing) is now defined in a separate array from the vertex attributes. This removes some brittle backend code which tries to guess the right vertex attribute slot if no attribute names are given, and which was wrong for shader-code-generation pipelines which reorder the vertex attributes (I stumbled over this when porting the Oryol Gfx module over to sokol-gfx). Some code samples: ```c // a complete vertex layout declaration with a single input buffer // with two vertex attributes sg_pipeline pip = sg_make_pipeline(&(sg_pipeline_desc){ .layout = { .buffers = { [0] = { .stride = 20, .step_func = SG_VERTEXSTEP_PER_VERTEX, .step_rate = 1 } }, .attrs = { [0] = { .name = "pos", .offset = 0, .format = SG_VERTEXFORMAT_FLOAT3, .buffer_index = 0 }, [1] = { .name = "uv", .offset = 12, .format = SG_VERTEXFORMAT_FLOAT2, .buffer_index = 0 } } }, ... }); // if the vertex layout has no gaps, we can get rid of the strides and offsets: sg_pipeline pip = sg_make_pipeline(&(sg_pipeline_desc){ .layout = { .buffers = { [0] = { .step_func = SG_VERTEXSTEP_PER_VERTEX, .step_rate=1 } }, .attrs = { [0] = { .name = "pos", .format = SG_VERTEXFORMAT_FLOAT3, .buffer_index = 0 }, [1] = { .name = "uv", .format = SG_VERTEXFORMAT_FLOAT2, .buffer_index = 0 } } }, ... }); // we can also get rid of the other default-values, which leaves buffers[0] // as all-defaults, so it can disappear completely: sg_pipeline pip = sg_make_pipeline(&(sg_pipeline_desc){ .layout = { .attrs = { [0] = { .name = "pos", .format = SG_VERTEXFORMAT_FLOAT3 }, [1] = { .name = "uv", .format = SG_VERTEXFORMAT_FLOAT2 } } }, ... }); // and finally on GL3.3 and Metal and we don't need the attribute names // (on D3D11, a semantic name and index must be provided though) sg_pipeline pip = sg_make_pipeline(&(sg_pipeline_desc){ .layout = { .attrs = { [0] = { .format = SG_VERTEXFORMAT_FLOAT3 }, [1] = { .format = SG_VERTEXFORMAT_FLOAT2 } } }, ... }); ``` Please check the sample code in https://github.com/floooh/sokol-samples for more examples! Enjoy!Chipmunk2D-Chipmunk-7.0.3/demo/sokol/fips.yml000066400000000000000000000000501347650476100210100ustar00rootroot00000000000000exports: header-dirs: [ ".", "util" ] Chipmunk2D-Chipmunk-7.0.3/demo/sokol/sokol.c000066400000000000000000000000461347650476100206240ustar00rootroot00000000000000#define SOKOL_IMPL #include "sokol.h" Chipmunk2D-Chipmunk-7.0.3/demo/sokol/sokol.h000066400000000000000000000001741347650476100206330ustar00rootroot00000000000000#define SOKOL_WIN32_FORCE_MAIN #define SOKOL_GLCORE33 #include "sokol_app.h" #include "sokol_time.h" #include "sokol_gfx.h" Chipmunk2D-Chipmunk-7.0.3/demo/sokol/sokol.m000066400000000000000000000001541347650476100206360ustar00rootroot00000000000000// Blegh. Will deal with Metal later. #define GL_SILENCE_DEPRECATION #define SOKOL_IMPL #include "sokol.h" Chipmunk2D-Chipmunk-7.0.3/demo/sokol/sokol_app.h000066400000000000000000010016741347650476100215020ustar00rootroot00000000000000#pragma once /* sokol_app.h -- cross-platform application wrapper Project URL: https://github.com/floooh/sokol Do this: #define SOKOL_IMPL before you include this file in *one* C or C++ file to create the implementation. Optionally provide the following defines with your own implementations: SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) SOKOL_LOG(msg) - your own logging function (default: puts(msg)) SOKOL_UNREACHABLE() - a guard macro for unreachable code (default: assert(false)) SOKOL_ABORT() - called after an unrecoverable error (default: abort()) SOKOL_WIN32_FORCE_MAIN - define this on Win32 to use a main() entry point instead of WinMain SOKOL_NO_ENTRY - define this if sokol_app.h shouldn't "hijack" the main() function SOKOL_API_DECL - public function declaration prefix (default: extern) SOKOL_API_IMPL - public function implementation prefix (default: -) Optionally define the following to force debug checks and validations even in release mode: SOKOL_DEBUG - by default this is defined if _DEBUG is defined If sokol_app.h is compiled as a DLL, define the following before including the declaration or implementation: SOKOL_DLL On Windows, SOKOL_DLL will define SOKOL_API_DECL as __declspec(dllexport) or __declspec(dllimport) as needed. Portions of the Windows and Linux GL initialization and event code have been taken from GLFW (http://www.glfw.org/) iOS onscreen keyboard support 'inspired' by libgdx. If you use sokol_app.h together with sokol_gfx.h, include both headers in the implementation source file, and include sokol_app.h before sokol_gfx.h since sokol_app.h will also include the required 3D-API headers. On Windows, a minimal 'GL header' and function loader is integrated which contains just enough of GL for sokol_gfx.h. If you want to use your own GL header-generator/loader instead, define SOKOL_WIN32_NO_GL_LOADER before including the implementation part of sokol_app.h. For example code, see https://github.com/floooh/sokol-samples/tree/master/sapp FEATURE OVERVIEW ================ sokol_app.h provides a minimalistic cross-platform API which implements the 'application-wrapper' parts of a 3D application: - a common application entry function - creates a window and 3D-API context/device with a 'default framebuffer' - makes the rendered frame visible - provides keyboard-, mouse- and low-level touch-events - platforms: MacOS, iOS, HTML5, Win32, Linux, Android (RaspberryPi) - 3D-APIs: Metal, D3D11, GL3.2, GLES2, GLES3, WebGL, WebGL2 FEATURE/PLATFORM MATRIX ======================= | Windows | macOS | Linux | iOS | Android | Raspi | HTML5 --------------------+---------+-------+-------+-------+---------+-------+------- gl 3.x | YES | YES | YES | --- | --- | --- | --- gles2/webgl | --- | --- | --- | YES | YES | TODO | YES gles3/webgl2 | --- | --- | --- | YES | YES | --- | YES metal | --- | YES | --- | YES | --- | --- | --- d3d11 | YES | --- | --- | --- | --- | --- | --- KEY_DOWN | YES | YES | YES | SOME | TODO | TODO | YES KEY_UP | YES | YES | YES | SOME | TODO | TODO | YES CHAR | YES | YES | YES | YES | TODO | TODO | YES MOUSE_DOWN | YES | YES | YES | --- | --- | TODO | YES MOUSE_UP | YES | YES | YES | --- | --- | TODO | YES MOUSE_SCROLL | YES | YES | YES | --- | --- | TODO | YES MOUSE_MOVE | YES | YES | YES | --- | --- | TODO | YES MOUSE_ENTER | YES | YES | YES | --- | --- | TODO | YES MOUSE_LEAVE | YES | YES | YES | --- | --- | TODO | YES TOUCHES_BEGAN | --- | --- | --- | YES | YES | --- | YES TOUCHES_MOVED | --- | --- | --- | YES | YES | --- | YES TOUCHES_ENDED | --- | --- | --- | YES | YES | --- | YES TOUCHES_CANCELLED | --- | --- | --- | YES | YES | --- | YES RESIZED | YES | YES | YES | YES | YES | --- | YES ICONIFIED | YES | YES | YES | --- | --- | --- | --- RESTORED | YES | YES | YES | --- | --- | --- | --- SUSPENDED | --- | --- | --- | YES | YES | --- | TODO RESUMED | --- | --- | --- | YES | YES | --- | TODO UPDATE_CURSOR | YES | YES | TODO | --- | --- | --- | TODO IME | TODO | TODO? | TODO | ??? | TODO | ??? | ??? windowed | YES | YES | YES | --- | --- | TODO | YES fullscreen | YES | YES | TODO | YES | YES | TODO | --- pointer lock | TODO | TODO | TODO | --- | --- | TODO | TODO screen keyboard | --- | --- | --- | YES | TODO | --- | YES swap interval | YES | YES | YES | YES | TODO | TODO | YES high-dpi | YES | YES | TODO | YES | YES | TODO | YES - what about bluetooth keyboard / mouse on mobile platforms? STEP BY STEP ============ --- Add a sokol_main() to your code which returns a sapp_desc structure with initialization parameters and callback function pointers. This function is called very early, usually at the start of the platform's entry function (e.g. main or WinMain). You should do as little as possible here, since the rest of your code might be called from another thread (this depends on the platform): sapp_desc sokol_main(int argc, char* argv[]) { return (sapp_desc) { .width = 640, .height = 480, .init_cb = my_init_func, .frame_cb = my_frame_func, .cleanup_cb = my_cleanup_func, .event_cb = my_event_func, ... }; } There are many more setup parameters, but these are the most important. For a complete list search for the sapp_desc structure declaration below. DO NOT call any sokol-app function from inside sokol_main(), since sokol-app will not be initialized at this point. The .width and .height parameters are the preferred size of the 3D rendering canvas. The actual size may differ from this depending on platform and other circumstances. Also the canvas size may change at any time (for instance when the user resizes the application window, or rotates the mobile device). All provided function callbacks will be called from the same thread, but this may be different from the thread where sokol_main() was called. .init_cb (void (*)(void)) This function is called once after the application window, 3D rendering context and swap chain have been created. The function takes no arguments and has no return value. .frame_cb (void (*)(void)) This is the per-frame callback, which is usually called 60 times per second. This is where your application would update most of its state and perform all rendering. .cleanup_cb (void (*)(void)) The cleanup callback is called once right before the application quits. .event_cb (void (*)(const sapp_event* event)) The event callback is mainly for input handling, but in the future may also be used to communicate other types of events to the application. Keep the event_cb struct member zero-initialized if your application doesn't require event handling. .fail_cb (void (*)(const char* msg)) The fail callback is called when a fatal error is encountered during start which doesn't allow the program to continue. Providing a callback here gives you a chance to show an error message to the user. The default behaviour is SOKOL_LOG(msg) As you can see, those 'standard callbacks' don't have a user_data argument, so any data that needs to be preserved between callbacks must live in global variables. If you're allergic to global variables or cannot use them for other reasons, an alternative set of callbacks can be defined in sapp_desc, together with a user_data pointer: .user_data (void*) The user-data argument for the callbacks below .init_userdata_cb (void (*)(void* user_data)) .frame_userdata_cb (void (*)(void* user_data)) .cleanup_userdata_cb (void (*)(void* user_data)) .event_cb (void(*)(const sapp_event* event, void* user_data)) .fail_cb (void(*)(const char* msg, void* user_data)) These are the user-data versions of the callback functions. You can mix those with the standard callbacks that don't have the user_data argument. NOTE that there's also an alternative compile mode where sokol_app.h doesn't "hijack" the main() function. Search below for SOKOL_NO_ENTRY. --- Implement the initialization callback function, this is called once after the rendering surface, 3D API and swap chain have been initialized by sokol_app. All sokol-app functions can be called from inside the initialization callback, the most useful functions at this point are: int sapp_width(void) Returns the current width of the default framebuffer, this may change from one frame to the next. int sapp_height(void) Likewise, returns the current height of the default framebuffer. bool sapp_gles2(void) Returns true if a GLES2 or WebGL context has been created. This is useful when a GLES3/WebGL2 context was requested but is not available so that sokol_app.h had to fallback to GLES2/WebGL. const void* sapp_metal_get_device(void) const void* sapp_metal_get_renderpass_descriptor(void) const void* sapp_metal_get_drawable(void) If the Metal backend has been selected, these functions return pointers to various Metal API objects required for rendering, otherwise they return a null pointer. Note that the returned pointers to the renderpass-descriptor and drawable may change from one frame to the next! const void* sapp_macos_get_window(void) On macOS, get the NSWindow object pointer, otherwise a null pointer. const void* sapp_ios_get_window(void) On iOS, get the UIWindow object pointer, otherwise a null pointer. const void* sapp_win32_get_hwnd(void) On Windows, get the window's HWND, otherwise a null pointer. const void* sapp_d3d11_get_device(void); const void* sapp_d3d11_get_device_context(void); const void* sapp_d3d11_get_render_target_view(void); const void* sapp_d3d11_get_depth_stencil_view(void); Similar to the sapp_metal_* functions, the sapp_d3d11_* functions return pointers to D3D11 API objects required for rendering, only if the D3D11 backend has been selected. Otherwise they return a null pointer. Note that the returned pointers to the render-target-view and depth-stencil-view may change from one frame to the next! --- Implement the frame-callback function, this function will be called on the same thread as the init callback, but might be on a different thread than the sokol_main() function. Note that the size of the rendering framebuffer might have change since the frame callback was called last. Call the functions sapp_width() and sapp_height() to get the current size. --- Optionally implement the event-callback to handle input events. sokol-app provides the following type of input events: - a 'virtual key' was pressed down or released - a single text character was entered (provided as UTF-32 code point) - a mouse button was pressed down or released (left, right, middle) - mouse-wheel or 2D scrolling events - the mouse was moved - the mouse has entered or left the application window boundaries - low-level, portable multi-touch events (began, moved, ended, cancelled) More types of events will be added in the future (like window minimized, maximized, application life cycle events, etc...) --- Implement the cleanup-callback function, this is called once after the user quits the application (currently there's now way to quite the application programmatically) HIGH-DPI RENDERING ================== You can set the sapp_desc.high_dpi flag during initialization to request a full-resolution framebuffer on HighDPI displays. The default behaviour is sapp_desc.high_dpi=false, this means that the application will render to a lower-resolution framebuffer on HighDPI displays and the rendered content will be upscaled by the window system composer. In a HighDPI scenario, you still request the same window size during sokol_main(), but the framebuffer sizes returned by sapp_width() and sapp_height() will be scaled up according to the DPI scaling ratio. You can also get a DPI scaling factor with the function sapp_dpi_scale(). Here's an example on a Mac with Retina display: sapp_desc sokol_main() { return (sapp_desc) { .width = 640, .height = 480, .high_dpi = true, ... }; } The functions sapp_width(), sapp_height() and sapp_dpi_scale() will return the following values: sapp_width -> 1280 sapp_height -> 960 sapp_dpi_scale -> 2.0 If the high_dpi flag is false, or you're not running on a Retina display, the values would be: sapp_width -> 640 sapp_height -> 480 sapp_dpi_scale -> 1.0 FULLSCREEN ========== If the sapp_desc.fullscreen flag is true, sokol-app will try to create a fullscreen window on platforms with a 'proper' window system (mobile devices will always use fullscreen). The implementation details depend on the target platform, in general sokol-app will use a 'soft approach' which doesn't interfere too much with the platform's window system (for instance borderless fullscreen window instead of a 'real' fullscreen mode). Such details might change over time as sokol-app is adapted for different needs. The most important effect of fullscreen mode to keep in mind is that the requested canvas width and height will be ignored for the initial window size, calling sapp_width() and sapp_height() will instead return the resolution of the fullscreen canvas (however the provided size might still be used for the non-fullscreen window, in case the user can switch back from fullscreen- to windowed-mode). ONSCREEN KEYBOARD ================= On some platforms which don't provide a physical keyboard, sokol-app can display the platform's integrated onscreen keyboard for text input. To request that the onscreen keyboard is shown, call sapp_show_keyboard(true); Likewise, to hide the keyboard call: sapp_show_keyboard(false); Note that on the web platform, the keyboard can only be shown from inside an input handler. On such platforms, sapp_show_keyboard() will only work as expected when it is called from inside the sokol-app event callback function. When called from other places, an internal flag will be set, and the onscreen keyboard will be called at the next 'legal' opportunity (when the next input event is handled). OPTIONAL: DON'T HIJACK main() (#define SOKOL_NO_ENTRY) ====================================================== In its default configuration, sokol_app.h "hijacks" the platform's standard main() function. This was done because different platforms have different main functions which are not compatible with C's main() (for instance WinMain on Windows has completely different arguments). However, this "main hijacking" posed a problem for usage scenarios like integrating sokol_app.h with other languages than C or C++, so an alternative SOKOL_NO_ENTRY mode has been added in which the user code provides the platform's main function: - define SOKOL_NO_ENTRY before including the sokol_app.h implementation - do *not* provide a sokol_main() function - instead provide the standard main() function of the platform - from the main function, call the function ```sapp_run()``` which takes a pointer to an ```sapp_desc``` structure. - ```sapp_run()``` takes over control and calls the provided init-, frame-, shutdown- and event-callbacks just like in the default model, it will only return when the application quits (or not at all on some platforms, like emscripten) NOTE: SOKOL_NO_ENTRY is currently not supported on Android. TEMP NOTE DUMP ============== - need a way to quit application programmatically (sapp_request_quit()) - need a way to intercept a pending quit via UI close button (could be done via frame_cb return value, and a sapp_quit_requested() function) - onscreen keyboard support on Android requires Java :(, should we even bother? - sapp_desc needs a bool whether to initialize depth-stencil surface - GL context initialization needs more control (at least what GL version to initialize) - application icon - mouse pointer visibility(?) - the UPDATE_CURSOR event currently behaves differently between Win32 and OSX (Win32 sends the event each frame when the mouse moves and is inside the window client area, OSX sends it only once when the mouse enters the client area) - the Android implementation calls cleanup_cb() and destroys the egl context in onDestroy at the latest but should do it earlier, in onStop, as an app is "killable" after onStop on Android Honeycomb and later (it can't be done at the moment as the app may be started again after onStop and the sokol lifecycle does not yet handle context teardown/bringup) FIXME: ERROR HANDLING (this will need an error callback function) zlib/libpng license Copyright (c) 2018 Andre Weissflog This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #define SOKOL_APP_INCLUDED (1) #include #include #ifndef SOKOL_API_DECL #if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_IMPL) #define SOKOL_API_DECL __declspec(dllexport) #elif defined(_WIN32) && defined(SOKOL_DLL) #define SOKOL_API_DECL __declspec(dllimport) #else #define SOKOL_API_DECL extern #endif #endif #ifdef __cplusplus extern "C" { #endif enum { SAPP_MAX_TOUCHPOINTS = 8, SAPP_MAX_MOUSEBUTTONS = 3, SAPP_MAX_KEYCODES = 512, }; typedef enum sapp_event_type { SAPP_EVENTTYPE_INVALID, SAPP_EVENTTYPE_KEY_DOWN, SAPP_EVENTTYPE_KEY_UP, SAPP_EVENTTYPE_CHAR, SAPP_EVENTTYPE_MOUSE_DOWN, SAPP_EVENTTYPE_MOUSE_UP, SAPP_EVENTTYPE_MOUSE_SCROLL, SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_EVENTTYPE_MOUSE_ENTER, SAPP_EVENTTYPE_MOUSE_LEAVE, SAPP_EVENTTYPE_TOUCHES_BEGAN, SAPP_EVENTTYPE_TOUCHES_MOVED, SAPP_EVENTTYPE_TOUCHES_ENDED, SAPP_EVENTTYPE_TOUCHES_CANCELLED, SAPP_EVENTTYPE_RESIZED, SAPP_EVENTTYPE_ICONIFIED, SAPP_EVENTTYPE_RESTORED, SAPP_EVENTTYPE_SUSPENDED, SAPP_EVENTTYPE_RESUMED, SAPP_EVENTTYPE_UPDATE_CURSOR, _SAPP_EVENTTYPE_NUM, _SAPP_EVENTTYPE_FORCE_U32 = 0x7FFFFFF } sapp_event_type; /* key codes are the same names and values as GLFW */ typedef enum sapp_keycode { SAPP_KEYCODE_INVALID = 0, SAPP_KEYCODE_SPACE = 32, SAPP_KEYCODE_APOSTROPHE = 39, /* ' */ SAPP_KEYCODE_COMMA = 44, /* , */ SAPP_KEYCODE_MINUS = 45, /* - */ SAPP_KEYCODE_PERIOD = 46, /* . */ SAPP_KEYCODE_SLASH = 47, /* / */ SAPP_KEYCODE_0 = 48, SAPP_KEYCODE_1 = 49, SAPP_KEYCODE_2 = 50, SAPP_KEYCODE_3 = 51, SAPP_KEYCODE_4 = 52, SAPP_KEYCODE_5 = 53, SAPP_KEYCODE_6 = 54, SAPP_KEYCODE_7 = 55, SAPP_KEYCODE_8 = 56, SAPP_KEYCODE_9 = 57, SAPP_KEYCODE_SEMICOLON = 59, /* ; */ SAPP_KEYCODE_EQUAL = 61, /* = */ SAPP_KEYCODE_A = 65, SAPP_KEYCODE_B = 66, SAPP_KEYCODE_C = 67, SAPP_KEYCODE_D = 68, SAPP_KEYCODE_E = 69, SAPP_KEYCODE_F = 70, SAPP_KEYCODE_G = 71, SAPP_KEYCODE_H = 72, SAPP_KEYCODE_I = 73, SAPP_KEYCODE_J = 74, SAPP_KEYCODE_K = 75, SAPP_KEYCODE_L = 76, SAPP_KEYCODE_M = 77, SAPP_KEYCODE_N = 78, SAPP_KEYCODE_O = 79, SAPP_KEYCODE_P = 80, SAPP_KEYCODE_Q = 81, SAPP_KEYCODE_R = 82, SAPP_KEYCODE_S = 83, SAPP_KEYCODE_T = 84, SAPP_KEYCODE_U = 85, SAPP_KEYCODE_V = 86, SAPP_KEYCODE_W = 87, SAPP_KEYCODE_X = 88, SAPP_KEYCODE_Y = 89, SAPP_KEYCODE_Z = 90, SAPP_KEYCODE_LEFT_BRACKET = 91, /* [ */ SAPP_KEYCODE_BACKSLASH = 92, /* \ */ SAPP_KEYCODE_RIGHT_BRACKET = 93, /* ] */ SAPP_KEYCODE_GRAVE_ACCENT = 96, /* ` */ SAPP_KEYCODE_WORLD_1 = 161, /* non-US #1 */ SAPP_KEYCODE_WORLD_2 = 162, /* non-US #2 */ SAPP_KEYCODE_ESCAPE = 256, SAPP_KEYCODE_ENTER = 257, SAPP_KEYCODE_TAB = 258, SAPP_KEYCODE_BACKSPACE = 259, SAPP_KEYCODE_INSERT = 260, SAPP_KEYCODE_DELETE = 261, SAPP_KEYCODE_RIGHT = 262, SAPP_KEYCODE_LEFT = 263, SAPP_KEYCODE_DOWN = 264, SAPP_KEYCODE_UP = 265, SAPP_KEYCODE_PAGE_UP = 266, SAPP_KEYCODE_PAGE_DOWN = 267, SAPP_KEYCODE_HOME = 268, SAPP_KEYCODE_END = 269, SAPP_KEYCODE_CAPS_LOCK = 280, SAPP_KEYCODE_SCROLL_LOCK = 281, SAPP_KEYCODE_NUM_LOCK = 282, SAPP_KEYCODE_PRINT_SCREEN = 283, SAPP_KEYCODE_PAUSE = 284, SAPP_KEYCODE_F1 = 290, SAPP_KEYCODE_F2 = 291, SAPP_KEYCODE_F3 = 292, SAPP_KEYCODE_F4 = 293, SAPP_KEYCODE_F5 = 294, SAPP_KEYCODE_F6 = 295, SAPP_KEYCODE_F7 = 296, SAPP_KEYCODE_F8 = 297, SAPP_KEYCODE_F9 = 298, SAPP_KEYCODE_F10 = 299, SAPP_KEYCODE_F11 = 300, SAPP_KEYCODE_F12 = 301, SAPP_KEYCODE_F13 = 302, SAPP_KEYCODE_F14 = 303, SAPP_KEYCODE_F15 = 304, SAPP_KEYCODE_F16 = 305, SAPP_KEYCODE_F17 = 306, SAPP_KEYCODE_F18 = 307, SAPP_KEYCODE_F19 = 308, SAPP_KEYCODE_F20 = 309, SAPP_KEYCODE_F21 = 310, SAPP_KEYCODE_F22 = 311, SAPP_KEYCODE_F23 = 312, SAPP_KEYCODE_F24 = 313, SAPP_KEYCODE_F25 = 314, SAPP_KEYCODE_KP_0 = 320, SAPP_KEYCODE_KP_1 = 321, SAPP_KEYCODE_KP_2 = 322, SAPP_KEYCODE_KP_3 = 323, SAPP_KEYCODE_KP_4 = 324, SAPP_KEYCODE_KP_5 = 325, SAPP_KEYCODE_KP_6 = 326, SAPP_KEYCODE_KP_7 = 327, SAPP_KEYCODE_KP_8 = 328, SAPP_KEYCODE_KP_9 = 329, SAPP_KEYCODE_KP_DECIMAL = 330, SAPP_KEYCODE_KP_DIVIDE = 331, SAPP_KEYCODE_KP_MULTIPLY = 332, SAPP_KEYCODE_KP_SUBTRACT = 333, SAPP_KEYCODE_KP_ADD = 334, SAPP_KEYCODE_KP_ENTER = 335, SAPP_KEYCODE_KP_EQUAL = 336, SAPP_KEYCODE_LEFT_SHIFT = 340, SAPP_KEYCODE_LEFT_CONTROL = 341, SAPP_KEYCODE_LEFT_ALT = 342, SAPP_KEYCODE_LEFT_SUPER = 343, SAPP_KEYCODE_RIGHT_SHIFT = 344, SAPP_KEYCODE_RIGHT_CONTROL = 345, SAPP_KEYCODE_RIGHT_ALT = 346, SAPP_KEYCODE_RIGHT_SUPER = 347, SAPP_KEYCODE_MENU = 348, } sapp_keycode; typedef struct sapp_touchpoint { uintptr_t identifier; float pos_x; float pos_y; bool changed; } sapp_touchpoint; typedef enum sapp_mousebutton { SAPP_MOUSEBUTTON_INVALID = -1, SAPP_MOUSEBUTTON_LEFT = 0, SAPP_MOUSEBUTTON_RIGHT = 1, SAPP_MOUSEBUTTON_MIDDLE = 2, } sapp_mousebutton; enum { SAPP_MODIFIER_SHIFT = (1<<0), SAPP_MODIFIER_CTRL = (1<<1), SAPP_MODIFIER_ALT = (1<<2), SAPP_MODIFIER_SUPER = (1<<3) }; typedef struct sapp_event { sapp_event_type type; uint32_t frame_count; sapp_keycode key_code; uint32_t char_code; bool key_repeat; uint32_t modifiers; sapp_mousebutton mouse_button; float mouse_x; float mouse_y; float scroll_x; float scroll_y; int num_touches; sapp_touchpoint touches[SAPP_MAX_TOUCHPOINTS]; int window_width; int window_height; int framebuffer_width; int framebuffer_height; } sapp_event; typedef struct sapp_desc { void (*init_cb)(void); /* these are the user-provided callbacks without user data */ void (*frame_cb)(void); void (*cleanup_cb)(void); void (*event_cb)(const sapp_event*); void (*fail_cb)(const char*); void* user_data; /* these are the user-provided callbacks with user data */ void (*init_userdata_cb)(void*); void (*frame_userdata_cb)(void*); void (*cleanup_userdata_cb)(void*); void (*event_userdata_cb)(const sapp_event*, void*); void (*fail_userdata_cb)(const char*, void*); int width; /* the preferred width of the window / canvas */ int height; /* the preferred height of the window / canvas */ int sample_count; /* MSAA sample count */ int swap_interval; /* the preferred swap interval (ignored on some platforms) */ bool high_dpi; /* whether the rendering canvas is full-resolution on HighDPI displays */ bool fullscreen; /* whether the window should be created in fullscreen mode */ bool alpha; /* whether the framebuffer should have an alpha channel (ignored on some platforms) */ const char* window_title; /* the window title as UTF-8 encoded string */ bool user_cursor; /* if true, user is expected to manage cursor image in SAPP_EVENTTYPE_UPDATE_CURSOR */ const char* html5_canvas_name; /* the name (id) of the HTML5 canvas element, default is "canvas" */ bool html5_canvas_resize; /* if true, the HTML5 canvas size is set to sapp_desc.width/height, otherwise canvas size is tracked */ bool html5_preserve_drawing_buffer; /* HTML5 only: whether to preserve default framebuffer content between frames */ bool html5_premultiplied_alpha; /* HTML5 only: whether the rendered pixels use premultiplied alpha convention */ bool ios_keyboard_resizes_canvas; /* if true, showing the iOS keyboard shrinks the canvas */ bool gl_force_gles2; /* if true, setup GLES2/WebGL even if GLES3/WebGL2 is available */ } sapp_desc; /* user-provided functions */ extern sapp_desc sokol_main(int argc, char* argv[]); /* sokol_app API functions */ SOKOL_API_DECL bool sapp_isvalid(void); SOKOL_API_DECL int sapp_width(void); SOKOL_API_DECL int sapp_height(void); SOKOL_API_DECL bool sapp_high_dpi(void); SOKOL_API_DECL float sapp_dpi_scale(void); SOKOL_API_DECL void sapp_show_keyboard(bool visible); SOKOL_API_DECL bool sapp_keyboard_shown(void); /* GL/GLES specific functions */ SOKOL_API_DECL bool sapp_gles2(void); /* OSX/Metal specific functions */ SOKOL_API_DECL const void* sapp_metal_get_device(void); SOKOL_API_DECL const void* sapp_metal_get_renderpass_descriptor(void); SOKOL_API_DECL const void* sapp_metal_get_drawable(void); SOKOL_API_DECL const void* sapp_macos_get_window(void); SOKOL_API_DECL const void* sapp_ios_get_window(void); /* Win32/D3D11 specific functions */ SOKOL_API_DECL const void* sapp_d3d11_get_device(void); SOKOL_API_DECL const void* sapp_d3d11_get_device_context(void); SOKOL_API_DECL const void* sapp_d3d11_get_render_target_view(void); SOKOL_API_DECL const void* sapp_d3d11_get_depth_stencil_view(void); SOKOL_API_DECL const void* sapp_win32_get_hwnd(void); /* special run-function for SOKOL_NO_ENTRY (in standard mode this is an empty stub) */ SOKOL_API_DECL int sapp_run(const sapp_desc* desc); #ifdef __cplusplus } /* extern "C" */ #endif /*-- IMPLEMENTATION ----------------------------------------------------------*/ #ifdef SOKOL_IMPL #define SOKOL_APP_IMPL_INCLUDED (1) #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */ #pragma warning(disable:4115) /* named type definition in parentheses */ #pragma warning(disable:4054) /* 'type cast': from function pointer */ #pragma warning(disable:4055) /* 'type cast': from data pointer */ #pragma warning(disable:4505) /* unreferenced local function has been removed */ #endif #include /* memset */ /* check if the config defines are alright */ #if defined(__APPLE__) #if !__has_feature(objc_arc) #error "sokol_app.h requires ARC (Automatic Reference Counting) on MacOS and iOS" #endif #include #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE /* iOS */ #if !defined(SOKOL_METAL) && !defined(SOKOL_GLES3) #error("sokol_app.h: unknown 3D API selected for iOS, must be SOKOL_METAL or SOKOL_GLES3") #endif #else /* MacOS */ #if !defined(SOKOL_METAL) && !defined(SOKOL_GLCORE33) #error("sokol_app.h: unknown 3D API selected for MacOS, must be SOKOL_METAL or SOKOL_GLCORE33") #endif #endif #elif defined(__EMSCRIPTEN__) /* emscripten (asm.js or wasm) */ #if !defined(SOKOL_GLES3) && !defined(SOKOL_GLES2) #error("sokol_app.h: unknown 3D API selected for emscripten, must be SOKOL_GLES3 or SOKOL_GLES2") #endif #elif defined(_WIN32) /* Windows (D3D11 or GL) */ #if !defined(SOKOL_D3D11) && !defined(SOKOL_GLCORE33) #error("sokol_app.h: unknown 3D API selected for Win32, must be SOKOL_D3D11 or SOKOL_GLCORE33") #endif #elif defined(__ANDROID__) /* Android */ #if !defined(SOKOL_GLES3) && !defined(SOKOL_GLES2) #error("sokol_app.h: unknown 3D API selected for Android, must be SOKOL_GLES3 or SOKOL_GLES2") #endif #if defined(SOKOL_NO_ENTRY) #error("sokol_app.h: SOKOL_NO_ENTRY is not supported on Android") #endif #elif defined(__linux__) || defined(__unix__) /* Linux */ #if !defined(SOKOL_GLCORE33) #error("sokol_app.h: unknown 3D API selected for Linux, must be SOKOL_GLCORE33") #endif #else #error "sokol_app.h: Unknown platform" #endif #ifndef SOKOL_API_IMPL #define SOKOL_API_IMPL #endif #ifndef SOKOL_DEBUG #ifndef NDEBUG #define SOKOL_DEBUG (1) #endif #endif #ifndef SOKOL_ASSERT #include #define SOKOL_ASSERT(c) assert(c) #endif #if !defined(SOKOL_CALLOC) && !defined(SOKOL_FREE) #include #endif #if !defined(SOKOL_CALLOC) #define SOKOL_CALLOC(n,s) calloc(n,s) #endif #if !defined(SOKOL_FREE) #define SOKOL_FREE(p) free(p) #endif #ifndef SOKOL_LOG #ifdef SOKOL_DEBUG #if defined(__ANDROID__) #include #define SOKOL_LOG(s) { SOKOL_ASSERT(s); __android_log_write(ANDROID_LOG_INFO, "SOKOL_APP", s); } #else #include #define SOKOL_LOG(s) { SOKOL_ASSERT(s); puts(s); } #endif #else #define SOKOL_LOG(s) #endif #endif #ifndef SOKOL_ABORT #include #define SOKOL_ABORT() abort() #endif #ifndef _SOKOL_PRIVATE #if defined(__GNUC__) #define _SOKOL_PRIVATE __attribute__((unused)) static #else #define _SOKOL_PRIVATE static #endif #endif #ifndef _SOKOL_UNUSED #define _SOKOL_UNUSED(x) (void)(x) #endif /* helper macros */ #define _sapp_def(val, def) (((val) == 0) ? (def) : (val)) #define _sapp_absf(a) (((a)<0.0f)?-(a):(a)) enum { _SAPP_MAX_TITLE_LENGTH = 128, }; typedef struct { bool valid; int window_width; int window_height; int framebuffer_width; int framebuffer_height; int sample_count; int swap_interval; float dpi_scale; bool gles2_fallback; bool first_frame; bool init_called; bool cleanup_called; bool html5_canvas_resize; const char* html5_canvas_name; char window_title[_SAPP_MAX_TITLE_LENGTH]; /* UTF-8 */ wchar_t window_title_wide[_SAPP_MAX_TITLE_LENGTH]; /* UTF-32 or UCS-2 */ uint32_t frame_count; float mouse_x; float mouse_y; bool win32_mouse_tracked; bool onscreen_keyboard_shown; sapp_event event; sapp_desc desc; sapp_keycode keycodes[SAPP_MAX_KEYCODES]; } _sapp_state; static _sapp_state _sapp; _SOKOL_PRIVATE void _sapp_fail(const char* msg) { if (_sapp.desc.fail_cb) { _sapp.desc.fail_cb(msg); } else if (_sapp.desc.fail_userdata_cb) { _sapp.desc.fail_userdata_cb(msg, _sapp.desc.user_data); } else { SOKOL_LOG(msg); } SOKOL_ABORT(); } _SOKOL_PRIVATE void _sapp_call_init(void) { if (_sapp.desc.init_cb) { _sapp.desc.init_cb(); } else if (_sapp.desc.init_userdata_cb) { _sapp.desc.init_userdata_cb(_sapp.desc.user_data); } _sapp.init_called = true; } _SOKOL_PRIVATE void _sapp_call_frame(void) { if (_sapp.init_called && !_sapp.cleanup_called) { if (_sapp.desc.frame_cb) { _sapp.desc.frame_cb(); } else if (_sapp.desc.frame_userdata_cb) { _sapp.desc.frame_userdata_cb(_sapp.desc.user_data); } } } _SOKOL_PRIVATE void _sapp_call_cleanup(void) { if (_sapp.desc.cleanup_cb) { _sapp.desc.cleanup_cb(); } else if (_sapp.desc.cleanup_userdata_cb) { _sapp.desc.cleanup_userdata_cb(_sapp.desc.user_data); } _sapp.cleanup_called = true; } _SOKOL_PRIVATE void _sapp_call_event(const sapp_event* e) { if (_sapp.desc.event_cb) { _sapp.desc.event_cb(e); } else if (_sapp.desc.event_userdata_cb) { _sapp.desc.event_userdata_cb(e, _sapp.desc.user_data); } } _SOKOL_PRIVATE void _sapp_strcpy(const char* src, char* dst, int max_len) { SOKOL_ASSERT(src && dst && (max_len > 0)); char* const end = &(dst[max_len-1]); char c = 0; for (int i = 0; i < max_len; i++) { c = *src; if (c != 0) { src++; } *dst++ = c; } /* truncated? */ if (c != 0) { *end = 0; } } _SOKOL_PRIVATE void _sapp_init_state(const sapp_desc* desc) { memset(&_sapp, 0, sizeof(_sapp)); _sapp.desc = *desc; _sapp.first_frame = true; _sapp.window_width = _sapp_def(_sapp.desc.width, 640); _sapp.window_height = _sapp_def(_sapp.desc.height, 480); _sapp.framebuffer_width = _sapp.window_width; _sapp.framebuffer_height = _sapp.window_height; _sapp.sample_count = _sapp_def(_sapp.desc.sample_count, 1); _sapp.swap_interval = _sapp_def(_sapp.desc.swap_interval, 1); _sapp.html5_canvas_name = _sapp_def(_sapp.desc.html5_canvas_name, "canvas"); _sapp.html5_canvas_resize = _sapp.desc.html5_canvas_resize; if (_sapp.desc.window_title) { _sapp_strcpy(_sapp.desc.window_title, _sapp.window_title, sizeof(_sapp.window_title)); } else { _sapp_strcpy("sokol_app", _sapp.window_title, sizeof(_sapp.window_title)); } _sapp.dpi_scale = 1.0f; } _SOKOL_PRIVATE void _sapp_init_event(sapp_event_type type) { memset(&_sapp.event, 0, sizeof(_sapp.event)); _sapp.event.type = type; _sapp.event.frame_count = _sapp.frame_count; _sapp.event.mouse_button = SAPP_MOUSEBUTTON_INVALID; _sapp.event.window_width = _sapp.window_width; _sapp.event.window_height = _sapp.window_height; _sapp.event.framebuffer_width = _sapp.framebuffer_width; _sapp.event.framebuffer_height = _sapp.framebuffer_height; } _SOKOL_PRIVATE bool _sapp_events_enabled(void) { /* only send events when an event callback is set, and the init function was called */ return (_sapp.desc.event_cb || _sapp.desc.event_userdata_cb) && _sapp.init_called; } _SOKOL_PRIVATE sapp_keycode _sapp_translate_key(int scan_code) { if ((scan_code >= 0) && (scan_code < SAPP_MAX_KEYCODES)) { return _sapp.keycodes[scan_code]; } else { return SAPP_KEYCODE_INVALID; } } _SOKOL_PRIVATE void _sapp_frame(void) { if (_sapp.first_frame) { _sapp.first_frame = false; _sapp_call_init(); } _sapp_call_frame(); _sapp.frame_count++; } /*== MacOS/iOS ===============================================================*/ #if defined(__APPLE__) /*== MacOS ===================================================================*/ #if defined(TARGET_OS_IPHONE) && !TARGET_OS_IPHONE #if defined(SOKOL_METAL) #import #import #elif defined(SOKOL_GLCORE33) #ifndef GL_SILENCE_DEPRECATION #define GL_SILENCE_DEPRECATION #endif #include #include #endif @interface _sapp_macos_app_delegate : NSObject @end @interface _sapp_macos_window_delegate : NSObject @end #if defined(SOKOL_METAL) @interface _sapp_macos_mtk_view_dlg : NSObject @end @interface _sapp_macos_view : MTKView { NSTrackingArea* trackingArea; } @end #elif defined(SOKOL_GLCORE33) @interface _sapp_macos_view : NSOpenGLView { NSTrackingArea* trackingArea; } - (void)timerFired:(id)sender; - (void)prepareOpenGL; - (void)drawRect:(NSRect)bounds; @end #endif static NSWindow* _sapp_macos_window_obj; static _sapp_macos_window_delegate* _sapp_macos_win_dlg_obj; static _sapp_macos_app_delegate* _sapp_macos_app_dlg_obj; static _sapp_macos_view* _sapp_view_obj; #if defined(SOKOL_METAL) static _sapp_macos_mtk_view_dlg* _sapp_macos_mtk_view_dlg_obj; static id _sapp_mtl_device_obj; #elif defined(SOKOL_GLCORE33) static NSOpenGLPixelFormat* _sapp_macos_glpixelformat_obj; static NSTimer* _sapp_macos_timer_obj; #endif static uint32_t _sapp_macos_flags_changed_store; _SOKOL_PRIVATE void _sapp_macos_init_keytable(void) { _sapp.keycodes[0x1D] = SAPP_KEYCODE_0; _sapp.keycodes[0x12] = SAPP_KEYCODE_1; _sapp.keycodes[0x13] = SAPP_KEYCODE_2; _sapp.keycodes[0x14] = SAPP_KEYCODE_3; _sapp.keycodes[0x15] = SAPP_KEYCODE_4; _sapp.keycodes[0x17] = SAPP_KEYCODE_5; _sapp.keycodes[0x16] = SAPP_KEYCODE_6; _sapp.keycodes[0x1A] = SAPP_KEYCODE_7; _sapp.keycodes[0x1C] = SAPP_KEYCODE_8; _sapp.keycodes[0x19] = SAPP_KEYCODE_9; _sapp.keycodes[0x00] = SAPP_KEYCODE_A; _sapp.keycodes[0x0B] = SAPP_KEYCODE_B; _sapp.keycodes[0x08] = SAPP_KEYCODE_C; _sapp.keycodes[0x02] = SAPP_KEYCODE_D; _sapp.keycodes[0x0E] = SAPP_KEYCODE_E; _sapp.keycodes[0x03] = SAPP_KEYCODE_F; _sapp.keycodes[0x05] = SAPP_KEYCODE_G; _sapp.keycodes[0x04] = SAPP_KEYCODE_H; _sapp.keycodes[0x22] = SAPP_KEYCODE_I; _sapp.keycodes[0x26] = SAPP_KEYCODE_J; _sapp.keycodes[0x28] = SAPP_KEYCODE_K; _sapp.keycodes[0x25] = SAPP_KEYCODE_L; _sapp.keycodes[0x2E] = SAPP_KEYCODE_M; _sapp.keycodes[0x2D] = SAPP_KEYCODE_N; _sapp.keycodes[0x1F] = SAPP_KEYCODE_O; _sapp.keycodes[0x23] = SAPP_KEYCODE_P; _sapp.keycodes[0x0C] = SAPP_KEYCODE_Q; _sapp.keycodes[0x0F] = SAPP_KEYCODE_R; _sapp.keycodes[0x01] = SAPP_KEYCODE_S; _sapp.keycodes[0x11] = SAPP_KEYCODE_T; _sapp.keycodes[0x20] = SAPP_KEYCODE_U; _sapp.keycodes[0x09] = SAPP_KEYCODE_V; _sapp.keycodes[0x0D] = SAPP_KEYCODE_W; _sapp.keycodes[0x07] = SAPP_KEYCODE_X; _sapp.keycodes[0x10] = SAPP_KEYCODE_Y; _sapp.keycodes[0x06] = SAPP_KEYCODE_Z; _sapp.keycodes[0x27] = SAPP_KEYCODE_APOSTROPHE; _sapp.keycodes[0x2A] = SAPP_KEYCODE_BACKSLASH; _sapp.keycodes[0x2B] = SAPP_KEYCODE_COMMA; _sapp.keycodes[0x18] = SAPP_KEYCODE_EQUAL; _sapp.keycodes[0x32] = SAPP_KEYCODE_GRAVE_ACCENT; _sapp.keycodes[0x21] = SAPP_KEYCODE_LEFT_BRACKET; _sapp.keycodes[0x1B] = SAPP_KEYCODE_MINUS; _sapp.keycodes[0x2F] = SAPP_KEYCODE_PERIOD; _sapp.keycodes[0x1E] = SAPP_KEYCODE_RIGHT_BRACKET; _sapp.keycodes[0x29] = SAPP_KEYCODE_SEMICOLON; _sapp.keycodes[0x2C] = SAPP_KEYCODE_SLASH; _sapp.keycodes[0x0A] = SAPP_KEYCODE_WORLD_1; _sapp.keycodes[0x33] = SAPP_KEYCODE_BACKSPACE; _sapp.keycodes[0x39] = SAPP_KEYCODE_CAPS_LOCK; _sapp.keycodes[0x75] = SAPP_KEYCODE_DELETE; _sapp.keycodes[0x7D] = SAPP_KEYCODE_DOWN; _sapp.keycodes[0x77] = SAPP_KEYCODE_END; _sapp.keycodes[0x24] = SAPP_KEYCODE_ENTER; _sapp.keycodes[0x35] = SAPP_KEYCODE_ESCAPE; _sapp.keycodes[0x7A] = SAPP_KEYCODE_F1; _sapp.keycodes[0x78] = SAPP_KEYCODE_F2; _sapp.keycodes[0x63] = SAPP_KEYCODE_F3; _sapp.keycodes[0x76] = SAPP_KEYCODE_F4; _sapp.keycodes[0x60] = SAPP_KEYCODE_F5; _sapp.keycodes[0x61] = SAPP_KEYCODE_F6; _sapp.keycodes[0x62] = SAPP_KEYCODE_F7; _sapp.keycodes[0x64] = SAPP_KEYCODE_F8; _sapp.keycodes[0x65] = SAPP_KEYCODE_F9; _sapp.keycodes[0x6D] = SAPP_KEYCODE_F10; _sapp.keycodes[0x67] = SAPP_KEYCODE_F11; _sapp.keycodes[0x6F] = SAPP_KEYCODE_F12; _sapp.keycodes[0x69] = SAPP_KEYCODE_F13; _sapp.keycodes[0x6B] = SAPP_KEYCODE_F14; _sapp.keycodes[0x71] = SAPP_KEYCODE_F15; _sapp.keycodes[0x6A] = SAPP_KEYCODE_F16; _sapp.keycodes[0x40] = SAPP_KEYCODE_F17; _sapp.keycodes[0x4F] = SAPP_KEYCODE_F18; _sapp.keycodes[0x50] = SAPP_KEYCODE_F19; _sapp.keycodes[0x5A] = SAPP_KEYCODE_F20; _sapp.keycodes[0x73] = SAPP_KEYCODE_HOME; _sapp.keycodes[0x72] = SAPP_KEYCODE_INSERT; _sapp.keycodes[0x7B] = SAPP_KEYCODE_LEFT; _sapp.keycodes[0x3A] = SAPP_KEYCODE_LEFT_ALT; _sapp.keycodes[0x3B] = SAPP_KEYCODE_LEFT_CONTROL; _sapp.keycodes[0x38] = SAPP_KEYCODE_LEFT_SHIFT; _sapp.keycodes[0x37] = SAPP_KEYCODE_LEFT_SUPER; _sapp.keycodes[0x6E] = SAPP_KEYCODE_MENU; _sapp.keycodes[0x47] = SAPP_KEYCODE_NUM_LOCK; _sapp.keycodes[0x79] = SAPP_KEYCODE_PAGE_DOWN; _sapp.keycodes[0x74] = SAPP_KEYCODE_PAGE_UP; _sapp.keycodes[0x7C] = SAPP_KEYCODE_RIGHT; _sapp.keycodes[0x3D] = SAPP_KEYCODE_RIGHT_ALT; _sapp.keycodes[0x3E] = SAPP_KEYCODE_RIGHT_CONTROL; _sapp.keycodes[0x3C] = SAPP_KEYCODE_RIGHT_SHIFT; _sapp.keycodes[0x36] = SAPP_KEYCODE_RIGHT_SUPER; _sapp.keycodes[0x31] = SAPP_KEYCODE_SPACE; _sapp.keycodes[0x30] = SAPP_KEYCODE_TAB; _sapp.keycodes[0x7E] = SAPP_KEYCODE_UP; _sapp.keycodes[0x52] = SAPP_KEYCODE_KP_0; _sapp.keycodes[0x53] = SAPP_KEYCODE_KP_1; _sapp.keycodes[0x54] = SAPP_KEYCODE_KP_2; _sapp.keycodes[0x55] = SAPP_KEYCODE_KP_3; _sapp.keycodes[0x56] = SAPP_KEYCODE_KP_4; _sapp.keycodes[0x57] = SAPP_KEYCODE_KP_5; _sapp.keycodes[0x58] = SAPP_KEYCODE_KP_6; _sapp.keycodes[0x59] = SAPP_KEYCODE_KP_7; _sapp.keycodes[0x5B] = SAPP_KEYCODE_KP_8; _sapp.keycodes[0x5C] = SAPP_KEYCODE_KP_9; _sapp.keycodes[0x45] = SAPP_KEYCODE_KP_ADD; _sapp.keycodes[0x41] = SAPP_KEYCODE_KP_DECIMAL; _sapp.keycodes[0x4B] = SAPP_KEYCODE_KP_DIVIDE; _sapp.keycodes[0x4C] = SAPP_KEYCODE_KP_ENTER; _sapp.keycodes[0x51] = SAPP_KEYCODE_KP_EQUAL; _sapp.keycodes[0x43] = SAPP_KEYCODE_KP_MULTIPLY; _sapp.keycodes[0x4E] = SAPP_KEYCODE_KP_SUBTRACT; } _SOKOL_PRIVATE void _sapp_run(const sapp_desc* desc) { _sapp_init_state(desc); _sapp_macos_init_keytable(); [NSApplication sharedApplication]; NSApp.activationPolicy = NSApplicationActivationPolicyRegular; _sapp_macos_app_dlg_obj = [[_sapp_macos_app_delegate alloc] init]; NSApp.delegate = _sapp_macos_app_dlg_obj; [NSApp activateIgnoringOtherApps:YES]; [NSApp run]; } /* MacOS entry function */ #if !defined(SOKOL_NO_ENTRY) int main(int argc, char* argv[]) { sapp_desc desc = sokol_main(argc, argv); _sapp_run(&desc); return 0; } #endif /* SOKOL_NO_ENTRY */ _SOKOL_PRIVATE void _sapp_macos_update_dimensions(void) { #if defined(SOKOL_METAL) const CGSize fb_size = [_sapp_view_obj drawableSize]; _sapp.framebuffer_width = fb_size.width; _sapp.framebuffer_height = fb_size.height; #elif defined(SOKOL_GLCORE33) const NSRect fb_rect = [_sapp_view_obj convertRectToBacking:[_sapp_view_obj frame]]; _sapp.framebuffer_width = fb_rect.size.width; _sapp.framebuffer_height = fb_rect.size.height; #endif const NSRect bounds = [_sapp_view_obj bounds]; _sapp.window_width = bounds.size.width; _sapp.window_height = bounds.size.height; SOKOL_ASSERT((_sapp.framebuffer_width > 0) && (_sapp.framebuffer_height > 0)); _sapp.dpi_scale = (float)_sapp.framebuffer_width / (float)_sapp.window_width; } _SOKOL_PRIVATE void _sapp_macos_frame(void) { const NSPoint mouse_pos = [_sapp_macos_window_obj mouseLocationOutsideOfEventStream]; _sapp.mouse_x = mouse_pos.x * _sapp.dpi_scale; _sapp.mouse_y = _sapp.framebuffer_height - (mouse_pos.y * _sapp.dpi_scale) - 1; _sapp_frame(); } @implementation _sapp_macos_app_delegate - (void)applicationDidFinishLaunching:(NSNotification*)aNotification { if (_sapp.desc.fullscreen) { NSRect screen_rect = NSScreen.mainScreen.frame; _sapp.window_width = screen_rect.size.width; _sapp.window_height = screen_rect.size.height; if (_sapp.desc.high_dpi) { _sapp.framebuffer_width = 2 * _sapp.window_width; _sapp.framebuffer_height = 2 * _sapp.window_height; } else { _sapp.framebuffer_width = _sapp.window_width; _sapp.framebuffer_height = _sapp.window_height; } _sapp.dpi_scale = (float)_sapp.framebuffer_width / (float) _sapp.window_width; } const NSUInteger style = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable; NSRect window_rect = NSMakeRect(0, 0, _sapp.window_width, _sapp.window_height); _sapp_macos_window_obj = [[NSWindow alloc] initWithContentRect:window_rect styleMask:style backing:NSBackingStoreBuffered defer:NO]; _sapp_macos_window_obj.title = [NSString stringWithUTF8String:_sapp.window_title]; _sapp_macos_window_obj.acceptsMouseMovedEvents = YES; _sapp_macos_window_obj.restorable = YES; _sapp_macos_win_dlg_obj = [[_sapp_macos_window_delegate alloc] init]; _sapp_macos_window_obj.delegate = _sapp_macos_win_dlg_obj; #if defined(SOKOL_METAL) _sapp_mtl_device_obj = MTLCreateSystemDefaultDevice(); _sapp_macos_mtk_view_dlg_obj = [[_sapp_macos_mtk_view_dlg alloc] init]; _sapp_view_obj = [[_sapp_macos_view alloc] init]; [_sapp_view_obj updateTrackingAreas]; _sapp_view_obj.preferredFramesPerSecond = 60 / _sapp.swap_interval; _sapp_view_obj.delegate = _sapp_macos_mtk_view_dlg_obj; _sapp_view_obj.device = _sapp_mtl_device_obj; _sapp_view_obj.colorPixelFormat = MTLPixelFormatBGRA8Unorm; _sapp_view_obj.depthStencilPixelFormat = MTLPixelFormatDepth32Float_Stencil8; _sapp_view_obj.sampleCount = _sapp.sample_count; _sapp_macos_window_obj.contentView = _sapp_view_obj; [_sapp_macos_window_obj makeFirstResponder:_sapp_view_obj]; if (!_sapp.desc.high_dpi) { CGSize drawable_size = { (CGFloat) _sapp.framebuffer_width, (CGFloat) _sapp.framebuffer_height }; _sapp_view_obj.drawableSize = drawable_size; } _sapp_macos_update_dimensions(); _sapp_view_obj.layer.magnificationFilter = kCAFilterNearest; #elif defined(SOKOL_GLCORE33) NSOpenGLPixelFormatAttribute attrs[32]; int i = 0; attrs[i++] = NSOpenGLPFAAccelerated; attrs[i++] = NSOpenGLPFADoubleBuffer; attrs[i++] = NSOpenGLPFAOpenGLProfile; attrs[i++] = NSOpenGLProfileVersion3_2Core; attrs[i++] = NSOpenGLPFAColorSize; attrs[i++] = 24; attrs[i++] = NSOpenGLPFAAlphaSize; attrs[i++] = 8; attrs[i++] = NSOpenGLPFADepthSize; attrs[i++] = 24; attrs[i++] = NSOpenGLPFAStencilSize; attrs[i++] = 8; if (_sapp.sample_count > 1) { attrs[i++] = NSOpenGLPFAMultisample; attrs[i++] = NSOpenGLPFASampleBuffers; attrs[i++] = 1; attrs[i++] = NSOpenGLPFASamples; attrs[i++] = _sapp.sample_count; } else { attrs[i++] = NSOpenGLPFASampleBuffers; attrs[i++] = 0; } attrs[i++] = 0; _sapp_macos_glpixelformat_obj = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs]; SOKOL_ASSERT(_sapp_macos_glpixelformat_obj != nil); _sapp_view_obj = [[_sapp_macos_view alloc] initWithFrame:window_rect pixelFormat:_sapp_macos_glpixelformat_obj]; [_sapp_view_obj updateTrackingAreas]; if (_sapp.desc.high_dpi) { [_sapp_view_obj setWantsBestResolutionOpenGLSurface:YES]; } _sapp_macos_window_obj.contentView = _sapp_view_obj; [_sapp_macos_window_obj makeFirstResponder:_sapp_view_obj]; _sapp_macos_timer_obj = [NSTimer timerWithTimeInterval:0.001 target:_sapp_view_obj selector:@selector(timerFired:) userInfo:nil repeats:YES]; [[NSRunLoop currentRunLoop] addTimer:_sapp_macos_timer_obj forMode:NSDefaultRunLoopMode]; #endif _sapp.valid = true; if (_sapp.desc.fullscreen) { /* on GL, this already toggles a rendered frame, so set the valid flag before */ [_sapp_macos_window_obj toggleFullScreen:self]; } else { [_sapp_macos_window_obj center]; } [_sapp_macos_window_obj makeKeyAndOrderFront:nil]; } - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication*)sender { return YES; } @end _SOKOL_PRIVATE uint32_t _sapp_macos_mod(NSEventModifierFlags f) { uint32_t m = 0; if (f & NSEventModifierFlagShift) { m |= SAPP_MODIFIER_SHIFT; } if (f & NSEventModifierFlagControl) { m |= SAPP_MODIFIER_CTRL; } if (f & NSEventModifierFlagOption) { m |= SAPP_MODIFIER_ALT; } if (f & NSEventModifierFlagCommand) { m |= SAPP_MODIFIER_SUPER; } return m; } _SOKOL_PRIVATE void _sapp_macos_mouse_event(sapp_event_type type, sapp_mousebutton btn, uint32_t mod) { if (_sapp_events_enabled()) { _sapp_init_event(type); _sapp.event.mouse_button = btn; _sapp.event.modifiers = mod; _sapp.event.mouse_x = _sapp.mouse_x; _sapp.event.mouse_y = _sapp.mouse_y; _sapp_call_event(&_sapp.event); } } _SOKOL_PRIVATE void _sapp_macos_key_event(sapp_event_type type, sapp_keycode key, bool repeat, uint32_t mod) { if (_sapp_events_enabled()) { _sapp_init_event(type); _sapp.event.key_code = key; _sapp.event.key_repeat = repeat; _sapp.event.modifiers = mod; _sapp_call_event(&_sapp.event); } } _SOKOL_PRIVATE void _sapp_macos_app_event(sapp_event_type type) { if (_sapp_events_enabled()) { _sapp_init_event(type); _sapp_call_event(&_sapp.event); } } @implementation _sapp_macos_window_delegate - (BOOL)windowShouldClose:(id)sender { _sapp_call_cleanup(); return YES; } - (void)windowDidResize:(NSNotification*)notification { _sapp_macos_update_dimensions(); _sapp_macos_app_event(SAPP_EVENTTYPE_RESIZED); } - (void)windowDidMiniaturize:(NSNotification*)notification { _sapp_macos_app_event(SAPP_EVENTTYPE_ICONIFIED); } - (void)windowDidDeminiaturize:(NSNotification*)notification { _sapp_macos_app_event(SAPP_EVENTTYPE_RESTORED); } @end #if defined(SOKOL_METAL) @implementation _sapp_macos_mtk_view_dlg - (void)drawInMTKView:(MTKView*)view { @autoreleasepool { _sapp_macos_frame(); } } - (void)mtkView:(MTKView*)view drawableSizeWillChange:(CGSize)size { /* this is required by the protocol, but we can't do anything useful here */ } @end #endif @implementation _sapp_macos_view #if defined(SOKOL_GLCORE33) - (void)timerFired:(id)sender { [self setNeedsDisplay:YES]; } - (void)prepareOpenGL { [super prepareOpenGL]; GLint swapInt = 1; NSOpenGLContext* ctx = [_sapp_view_obj openGLContext]; [ctx setValues:&swapInt forParameter:NSOpenGLContextParameterSwapInterval]; [ctx makeCurrentContext]; } - (void)drawRect:(NSRect)bound { _sapp_macos_frame(); glFlush(); [[_sapp_view_obj openGLContext] flushBuffer]; } #endif - (BOOL)isOpaque { return YES; } - (BOOL)canBecomeKey { return YES; } - (BOOL)acceptsFirstResponder { return YES; } - (void)updateTrackingAreas { if (trackingArea != nil) { [self removeTrackingArea:trackingArea]; trackingArea = nil; } const NSTrackingAreaOptions options = NSTrackingMouseEnteredAndExited | NSTrackingActiveInKeyWindow | NSTrackingEnabledDuringMouseDrag | NSTrackingCursorUpdate | NSTrackingInVisibleRect | NSTrackingAssumeInside; trackingArea = [[NSTrackingArea alloc] initWithRect:[self bounds] options:options owner:self userInfo:nil]; [self addTrackingArea:trackingArea]; [super updateTrackingAreas]; } - (void)mouseEntered:(NSEvent*)event { _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_ENTER, SAPP_MOUSEBUTTON_INVALID, _sapp_macos_mod(event.modifierFlags)); } - (void)mouseExited:(NSEvent*)event { _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_LEAVE, SAPP_MOUSEBUTTON_INVALID, _sapp_macos_mod(event.modifierFlags)); } - (void)mouseDown:(NSEvent*)event { _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, SAPP_MOUSEBUTTON_LEFT, _sapp_macos_mod(event.modifierFlags)); } - (void)mouseUp:(NSEvent*)event { _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_UP, SAPP_MOUSEBUTTON_LEFT, _sapp_macos_mod(event.modifierFlags)); } - (void)rightMouseDown:(NSEvent*)event { _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, SAPP_MOUSEBUTTON_RIGHT, _sapp_macos_mod(event.modifierFlags)); } - (void)rightMouseUp:(NSEvent*)event { _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_UP, SAPP_MOUSEBUTTON_RIGHT, _sapp_macos_mod(event.modifierFlags)); } - (void)mouseMoved:(NSEvent*)event { _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID , _sapp_macos_mod(event.modifierFlags)); } - (void)mouseDragged:(NSEvent*)event { _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID , _sapp_macos_mod(event.modifierFlags)); } - (void)rightMouseDragged:(NSEvent*)event { _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID, _sapp_macos_mod(event.modifierFlags)); } - (void)scrollWheel:(NSEvent*)event { if (_sapp_events_enabled()) { float dx = (float) event.scrollingDeltaX; float dy = (float) event.scrollingDeltaY; if (event.hasPreciseScrollingDeltas) { dx *= 0.1; dy *= 0.1; } if ((_sapp_absf(dx) > 0.0f) || (_sapp_absf(dy) > 0.0f)) { _sapp_init_event(SAPP_EVENTTYPE_MOUSE_SCROLL); _sapp.event.modifiers = _sapp_macos_mod(event.modifierFlags); _sapp.event.mouse_x = _sapp.mouse_x; _sapp.event.mouse_y = _sapp.mouse_y; _sapp.event.scroll_x = dx; _sapp.event.scroll_y = dy; _sapp_call_event(&_sapp.event); } } } - (void)keyDown:(NSEvent*)event { if (_sapp_events_enabled()) { const uint32_t mods = _sapp_macos_mod(event.modifierFlags); _sapp_macos_key_event(SAPP_EVENTTYPE_KEY_DOWN, _sapp_translate_key(event.keyCode), event.isARepeat, mods); const NSString* chars = event.characters; const NSUInteger len = chars.length; if (len > 0) { _sapp_init_event(SAPP_EVENTTYPE_CHAR); _sapp.event.modifiers = mods; for (NSUInteger i = 0; i < len; i++) { const unichar codepoint = [chars characterAtIndex:i]; if ((codepoint & 0xFF00) == 0xF700) { continue; } _sapp.event.char_code = codepoint; _sapp.event.key_repeat = event.isARepeat; _sapp_call_event(&_sapp.event); } } } } - (void)keyUp:(NSEvent*)event { _sapp_macos_key_event(SAPP_EVENTTYPE_KEY_UP, _sapp_translate_key(event.keyCode), event.isARepeat, _sapp_macos_mod(event.modifierFlags)); } - (void)flagsChanged:(NSEvent*)event { const uint32_t old_f = _sapp_macos_flags_changed_store; const uint32_t new_f = event.modifierFlags; _sapp_macos_flags_changed_store = new_f; sapp_keycode key_code = SAPP_KEYCODE_INVALID; bool down = false; if ((new_f ^ old_f) & NSEventModifierFlagShift) { key_code = SAPP_KEYCODE_LEFT_SHIFT; down = 0 != (new_f & NSEventModifierFlagShift); } if ((new_f ^ old_f) & NSEventModifierFlagControl) { key_code = SAPP_KEYCODE_LEFT_CONTROL; down = 0 != (new_f & NSEventModifierFlagControl); } if ((new_f ^ old_f) & NSEventModifierFlagOption) { key_code = SAPP_KEYCODE_LEFT_ALT; down = 0 != (new_f & NSEventModifierFlagOption); } if ((new_f ^ old_f) & NSEventModifierFlagCommand) { key_code = SAPP_KEYCODE_LEFT_SUPER; down = 0 != (new_f & NSEventModifierFlagCommand); } if (key_code != SAPP_KEYCODE_INVALID) { _sapp_macos_key_event(down ? SAPP_EVENTTYPE_KEY_DOWN : SAPP_EVENTTYPE_KEY_UP, key_code, event.isARepeat, _sapp_macos_mod(event.modifierFlags)); } } - (void)cursorUpdate:(NSEvent*)event { if (_sapp.desc.user_cursor) { _sapp_macos_app_event(SAPP_EVENTTYPE_UPDATE_CURSOR); } } @end #endif /* MacOS */ /*== iOS =====================================================================*/ #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE #import #if defined(SOKOL_METAL) #import #import #else #import #include #include #endif @interface _sapp_app_delegate : NSObject @end @interface _sapp_textfield_dlg : NSObject - (void)keyboardWasShown:(NSNotification*)notif; - (void)keyboardWillBeHidden:(NSNotification*)notif; - (void)keyboardDidChangeFrame:(NSNotification*)notif; @end #if defined(SOKOL_METAL) @interface _sapp_ios_mtk_view_dlg : NSObject @end @interface _sapp_ios_view : MTKView; @end #else @interface _sapp_ios_glk_view_dlg : NSObject @end @interface _sapp_ios_view : GLKView @end #endif static bool _sapp_ios_suspended; static UIWindow* _sapp_ios_window_obj; static _sapp_ios_view* _sapp_view_obj; static UITextField* _sapp_ios_textfield_obj; static _sapp_textfield_dlg* _sapp_ios_textfield_dlg_obj; #if defined(SOKOL_METAL) static _sapp_ios_mtk_view_dlg* _sapp_ios_mtk_view_dlg_obj; static UIViewController* _sapp_ios_view_ctrl_obj; static id _sapp_mtl_device_obj; #else static EAGLContext* _sapp_ios_eagl_ctx_obj; static _sapp_ios_glk_view_dlg* _sapp_ios_glk_view_dlg_obj; static GLKViewController* _sapp_ios_view_ctrl_obj; #endif _SOKOL_PRIVATE void _sapp_run(const sapp_desc* desc) { _sapp_init_state(desc); static int argc = 1; static char* argv[] = { "sokol_app" }; UIApplicationMain(argc, argv, nil, NSStringFromClass([_sapp_app_delegate class])); } /* iOS entry function */ #if !defined(SOKOL_NO_ENTRY) int main(int argc, char* argv[]) { sapp_desc desc = sokol_main(argc, argv); _sapp_run(&desc); return 0; } #endif /* SOKOL_NO_ENTRY */ _SOKOL_PRIVATE void _sapp_ios_app_event(sapp_event_type type) { if (_sapp_events_enabled()) { _sapp_init_event(type); _sapp_call_event(&_sapp.event); } } _SOKOL_PRIVATE void _sapp_ios_update_dimensions(void) { CGRect screen_rect = UIScreen.mainScreen.bounds; _sapp.window_width = (int) screen_rect.size.width; _sapp.window_height = (int) screen_rect.size.height; int cur_fb_width, cur_fb_height; #if defined(SOKOL_METAL) const CGSize fb_size = _sapp_view_obj.drawableSize; cur_fb_width = (int) fb_size.width; cur_fb_height = (int) fb_size.height; #else cur_fb_width = (int) _sapp_view_obj.drawableWidth; cur_fb_height = (int) _sapp_view_obj.drawableHeight; #endif const bool dim_changed = (_sapp.framebuffer_width != cur_fb_width) || (_sapp.framebuffer_height != cur_fb_height); _sapp.framebuffer_width = cur_fb_width; _sapp.framebuffer_height = cur_fb_height; SOKOL_ASSERT((_sapp.framebuffer_width > 0) && (_sapp.framebuffer_height > 0)); _sapp.dpi_scale = (float)_sapp.framebuffer_width / (float) _sapp.window_width; if (dim_changed) { _sapp_ios_app_event(SAPP_EVENTTYPE_RESIZED); } } _SOKOL_PRIVATE void _sapp_ios_frame(void) { _sapp_ios_update_dimensions(); _sapp_frame(); } _SOKOL_PRIVATE void _sapp_ios_show_keyboard(bool shown) { /* if not happened yet, create an invisible text field */ if (nil == _sapp_ios_textfield_obj) { _sapp_ios_textfield_dlg_obj = [[_sapp_textfield_dlg alloc] init]; _sapp_ios_textfield_obj = [[UITextField alloc] initWithFrame:CGRectMake(10, 10, 100, 50)]; _sapp_ios_textfield_obj.keyboardType = UIKeyboardTypeDefault; _sapp_ios_textfield_obj.returnKeyType = UIReturnKeyDefault; _sapp_ios_textfield_obj.autocapitalizationType = UITextAutocapitalizationTypeNone; _sapp_ios_textfield_obj.autocorrectionType = UITextAutocorrectionTypeNo; _sapp_ios_textfield_obj.spellCheckingType = UITextSpellCheckingTypeNo; _sapp_ios_textfield_obj.hidden = YES; _sapp_ios_textfield_obj.text = @"x"; _sapp_ios_textfield_obj.delegate = _sapp_ios_textfield_dlg_obj; [_sapp_ios_view_ctrl_obj.view addSubview:_sapp_ios_textfield_obj]; [[NSNotificationCenter defaultCenter] addObserver:_sapp_ios_textfield_dlg_obj selector:@selector(keyboardWasShown:) name:UIKeyboardDidShowNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:_sapp_ios_textfield_dlg_obj selector:@selector(keyboardWillBeHidden:) name:UIKeyboardWillHideNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:_sapp_ios_textfield_dlg_obj selector:@selector(keyboardDidChangeFrame:) name:UIKeyboardDidChangeFrameNotification object:nil]; } if (shown) { /* setting the text field as first responder brings up the onscreen keyboard */ [_sapp_ios_textfield_obj becomeFirstResponder]; } else { [_sapp_ios_textfield_obj resignFirstResponder]; } } @implementation _sapp_app_delegate - (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions { CGRect screen_rect = UIScreen.mainScreen.bounds; _sapp_ios_window_obj = [[UIWindow alloc] initWithFrame:screen_rect]; _sapp.window_width = screen_rect.size.width; _sapp.window_height = screen_rect.size.height; if (_sapp.desc.high_dpi) { _sapp.framebuffer_width = 2 * _sapp.window_width; _sapp.framebuffer_height = 2 * _sapp.window_height; } else { _sapp.framebuffer_width = _sapp.window_width; _sapp.framebuffer_height = _sapp.window_height; } _sapp.dpi_scale = (float)_sapp.framebuffer_width / (float) _sapp.window_width; #if defined(SOKOL_METAL) _sapp_mtl_device_obj = MTLCreateSystemDefaultDevice(); _sapp_ios_mtk_view_dlg_obj = [[_sapp_ios_mtk_view_dlg alloc] init]; _sapp_view_obj = [[_sapp_ios_view alloc] init]; _sapp_view_obj.preferredFramesPerSecond = 60 / _sapp.swap_interval; _sapp_view_obj.delegate = _sapp_ios_mtk_view_dlg_obj; _sapp_view_obj.device = _sapp_mtl_device_obj; _sapp_view_obj.colorPixelFormat = MTLPixelFormatBGRA8Unorm; _sapp_view_obj.depthStencilPixelFormat = MTLPixelFormatDepth32Float_Stencil8; _sapp_view_obj.sampleCount = _sapp.sample_count; if (_sapp.desc.high_dpi) { _sapp_view_obj.contentScaleFactor = 2.0; } else { _sapp_view_obj.contentScaleFactor = 1.0; } _sapp_view_obj.userInteractionEnabled = YES; _sapp_view_obj.multipleTouchEnabled = YES; [_sapp_ios_window_obj addSubview:_sapp_view_obj]; _sapp_ios_view_ctrl_obj = [[UIViewController alloc] init]; _sapp_ios_view_ctrl_obj.view = _sapp_view_obj; _sapp_ios_window_obj.rootViewController = _sapp_ios_view_ctrl_obj; #else if (_sapp.desc.gl_force_gles2) { _sapp_ios_eagl_ctx_obj = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; _sapp.gles2_fallback = true; } else { _sapp_ios_eagl_ctx_obj = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]; if (_sapp_ios_eagl_ctx_obj == nil) { _sapp_ios_eagl_ctx_obj = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; _sapp.gles2_fallback = true; } } _sapp_ios_glk_view_dlg_obj = [[_sapp_ios_glk_view_dlg alloc] init]; _sapp_view_obj = [[_sapp_ios_view alloc] initWithFrame:screen_rect]; _sapp_view_obj.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888; _sapp_view_obj.drawableDepthFormat = GLKViewDrawableDepthFormat24; _sapp_view_obj.drawableStencilFormat = GLKViewDrawableStencilFormatNone; _sapp_view_obj.drawableMultisample = GLKViewDrawableMultisampleNone; /* FIXME */ _sapp_view_obj.context = _sapp_ios_eagl_ctx_obj; _sapp_view_obj.delegate = _sapp_ios_glk_view_dlg_obj; _sapp_view_obj.enableSetNeedsDisplay = NO; _sapp_view_obj.userInteractionEnabled = YES; _sapp_view_obj.multipleTouchEnabled = YES; if (_sapp.desc.high_dpi) { _sapp_view_obj.contentScaleFactor = 2.0; } else { _sapp_view_obj.contentScaleFactor = 1.0; } [_sapp_ios_window_obj addSubview:_sapp_view_obj]; _sapp_ios_view_ctrl_obj = [[GLKViewController alloc] init]; _sapp_ios_view_ctrl_obj.view = _sapp_view_obj; _sapp_ios_view_ctrl_obj.preferredFramesPerSecond = 60 / _sapp.swap_interval; _sapp_ios_window_obj.rootViewController = _sapp_ios_view_ctrl_obj; #endif [_sapp_ios_window_obj makeKeyAndVisible]; _sapp.valid = true; return YES; } - (void)applicationWillResignActive:(UIApplication *)application { if (!_sapp_ios_suspended) { _sapp_ios_suspended = true; _sapp_ios_app_event(SAPP_EVENTTYPE_SUSPENDED); } } - (void)applicationDidBecomeActive:(UIApplication *)application { if (_sapp_ios_suspended) { _sapp_ios_suspended = false; _sapp_ios_app_event(SAPP_EVENTTYPE_RESUMED); } } @end @implementation _sapp_textfield_dlg - (void)keyboardWasShown:(NSNotification*)notif { _sapp.onscreen_keyboard_shown = true; /* query the keyboard's size, and modify the content view's size */ if (_sapp.desc.ios_keyboard_resizes_canvas) { NSDictionary* info = notif.userInfo; CGFloat kbd_h = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size.height; CGRect view_frame = UIScreen.mainScreen.bounds; view_frame.size.height -= kbd_h; _sapp_view_obj.frame = view_frame; } } - (void)keyboardWillBeHidden:(NSNotification*)notif { _sapp.onscreen_keyboard_shown = false; if (_sapp.desc.ios_keyboard_resizes_canvas) { _sapp_view_obj.frame = UIScreen.mainScreen.bounds; } } - (void)keyboardDidChangeFrame:(NSNotification*)notif { /* this is for the case when the screen rotation changes while the keyboard is open */ if (_sapp.onscreen_keyboard_shown && _sapp.desc.ios_keyboard_resizes_canvas) { NSDictionary* info = notif.userInfo; CGFloat kbd_h = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size.height; CGRect view_frame = UIScreen.mainScreen.bounds; view_frame.size.height -= kbd_h; _sapp_view_obj.frame = view_frame; } } - (BOOL)textField:(UITextField*)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString*)string { if (_sapp_events_enabled()) { const NSUInteger len = string.length; if (len > 0) { for (NSUInteger i = 0; i < len; i++) { unichar c = [string characterAtIndex:i]; if (c >= 32) { /* ignore surrogates for now */ if ((c < 0xD800) || (c > 0xDFFF)) { _sapp_init_event(SAPP_EVENTTYPE_CHAR); _sapp.event.char_code = c; _sapp_call_event(&_sapp.event); } } if (c <= 32) { sapp_keycode k = SAPP_KEYCODE_INVALID; switch (c) { case 10: k = SAPP_KEYCODE_ENTER; break; case 32: k = SAPP_KEYCODE_SPACE; break; default: break; } if (k != SAPP_KEYCODE_INVALID) { _sapp_init_event(SAPP_EVENTTYPE_KEY_DOWN); _sapp.event.key_code = k; _sapp_call_event(&_sapp.event); _sapp_init_event(SAPP_EVENTTYPE_KEY_UP); _sapp.event.key_code = k; _sapp_call_event(&_sapp.event); } } } } else { /* this was a backspace */ _sapp_init_event(SAPP_EVENTTYPE_KEY_DOWN); _sapp.event.key_code = SAPP_KEYCODE_BACKSPACE; _sapp_call_event(&_sapp.event); _sapp_init_event(SAPP_EVENTTYPE_KEY_UP); _sapp.event.key_code = SAPP_KEYCODE_BACKSPACE; _sapp_call_event(&_sapp.event); } } return NO; } @end #if defined(SOKOL_METAL) @implementation _sapp_ios_mtk_view_dlg - (void)drawInMTKView:(MTKView*)view { @autoreleasepool { _sapp_ios_frame(); } } - (void)mtkView:(MTKView*)view drawableSizeWillChange:(CGSize)size { /* this is required by the protocol, but we can't do anything useful here */ } @end #else @implementation _sapp_ios_glk_view_dlg - (void)glkView:(GLKView*)view drawInRect:(CGRect)rect { @autoreleasepool { _sapp_ios_frame(); } } @end #endif _SOKOL_PRIVATE void _sapp_ios_touch_event(sapp_event_type type, NSSet* touches, UIEvent* event) { if (_sapp_events_enabled()) { _sapp_init_event(type); NSEnumerator* enumerator = event.allTouches.objectEnumerator; UITouch* ios_touch; while ((ios_touch = [enumerator nextObject])) { if ((_sapp.event.num_touches + 1) < SAPP_MAX_TOUCHPOINTS) { CGPoint ios_pos = [ios_touch locationInView:_sapp_view_obj]; sapp_touchpoint* cur_point = &_sapp.event.touches[_sapp.event.num_touches++]; cur_point->identifier = (uintptr_t) ios_touch; cur_point->pos_x = ios_pos.x * _sapp.dpi_scale; cur_point->pos_y = ios_pos.y * _sapp.dpi_scale; cur_point->changed = [touches containsObject:ios_touch]; } } if (_sapp.event.num_touches > 0) { _sapp_call_event(&_sapp.event); } } } @implementation _sapp_ios_view - (BOOL) isOpaque { return YES; } - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent*)event { _sapp_ios_touch_event(SAPP_EVENTTYPE_TOUCHES_BEGAN, touches, event); } - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent*)event { _sapp_ios_touch_event(SAPP_EVENTTYPE_TOUCHES_MOVED, touches, event); } - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent*)event { _sapp_ios_touch_event(SAPP_EVENTTYPE_TOUCHES_ENDED, touches, event); } - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent*)event { _sapp_ios_touch_event(SAPP_EVENTTYPE_TOUCHES_CANCELLED, touches, event); } @end #endif /* TARGET_OS_IPHONE */ #endif /* __APPLE__ */ /*== EMSCRIPTEN ==============================================================*/ #if defined(__EMSCRIPTEN__) #if defined(SOKOL_GLES3) #include #else #ifndef GL_EXT_PROTOTYPES #define GL_GLEXT_PROTOTYPES #endif #include #include #endif #include #include static bool _sapp_emsc_input_created; static bool _sapp_emsc_wants_show_keyboard; static bool _sapp_emsc_wants_hide_keyboard; /* this function is called from a JS event handler when the user hides the onscreen keyboard pressing the 'dismiss keyboard key' */ #ifdef __cplusplus extern "C" { #endif EMSCRIPTEN_KEEPALIVE void _sapp_emsc_notify_keyboard_hidden(void) { _sapp.onscreen_keyboard_shown = false; } #ifdef __cplusplus } /* extern "C" */ #endif /* Javascript helper functions for mobile virtual keyboard input */ EM_JS(void, sapp_js_create_textfield, (void), { var _sapp_inp = document.createElement("input"); _sapp_inp.type = "text"; _sapp_inp.id = "_sokol_app_input_element"; _sapp_inp.autocapitalize = "none"; _sapp_inp.addEventListener("focusout", function(_sapp_event) { __sapp_emsc_notify_keyboard_hidden() }); document.body.append(_sapp_inp); }); EM_JS(void, sapp_js_focus_textfield, (void), { document.getElementById("_sokol_app_input_element").focus(); }); EM_JS(void, sapp_js_unfocus_textfield, (void), { document.getElementById("_sokol_app_input_element").blur(); }); /* called from the emscripten event handler to update the keyboard visibility state, this must happen from an JS input event handler, otherwise the request will be ignored by the browser */ _SOKOL_PRIVATE void _sapp_emsc_update_keyboard_state(void) { if (_sapp_emsc_wants_show_keyboard) { /* create input text field on demand */ if (!_sapp_emsc_input_created) { _sapp_emsc_input_created = true; sapp_js_create_textfield(); } /* focus the text input field, this will bring up the keyboard */ _sapp.onscreen_keyboard_shown = true; _sapp_emsc_wants_show_keyboard = false; sapp_js_focus_textfield(); } if (_sapp_emsc_wants_hide_keyboard) { /* unfocus the text input field */ if (_sapp_emsc_input_created) { _sapp.onscreen_keyboard_shown = false; _sapp_emsc_wants_hide_keyboard = false; sapp_js_unfocus_textfield(); } } } /* actually showing the onscreen keyboard must be initiated from a JS input event handler, so we'll just keep track of the desired state, and the actual state change will happen with the next input event */ _SOKOL_PRIVATE void _sapp_emsc_show_keyboard(bool show) { if (show) { _sapp_emsc_wants_show_keyboard = true; } else { _sapp_emsc_wants_hide_keyboard = true; } } _SOKOL_PRIVATE EM_BOOL _sapp_emsc_size_changed(int event_type, const EmscriptenUiEvent* ui_event, void* user_data) { double w, h; emscripten_get_element_css_size(_sapp.html5_canvas_name, &w, &h); /* The above method might report zero when toggling HTML5 fullscreen, in that case use the window's inner width reported by the emscripten event. This works ok when toggling *into* fullscreen but doesn't properly restore the previous canvas size when switching back from fullscreen. In general, due to the HTML5's fullscreen API's flaky nature it is recommended to use 'soft fullscreen' (stretching the WebGL canvas over the browser window's client rect) with a CSS definition like this: position: absolute; top: 0px; left: 0px; margin: 0px; border: 0; width: 100%; height: 100%; overflow: hidden; display: block; */ if (w < 1.0) { w = ui_event->windowInnerWidth; } else { _sapp.window_width = (int) w; } if (h < 1.0) { h = ui_event->windowInnerHeight; } else { _sapp.window_height = (int) h; } if (_sapp.desc.high_dpi) { _sapp.dpi_scale = emscripten_get_device_pixel_ratio(); w *= _sapp.dpi_scale; h *= _sapp.dpi_scale; } _sapp.framebuffer_width = (int) w; _sapp.framebuffer_height = (int) h; SOKOL_ASSERT((_sapp.framebuffer_width > 0) && (_sapp.framebuffer_height > 0)); emscripten_set_canvas_element_size(_sapp.html5_canvas_name, w, h); if (_sapp_events_enabled()) { _sapp_init_event(SAPP_EVENTTYPE_RESIZED); _sapp_call_event(&_sapp.event); } return true; } _SOKOL_PRIVATE EM_BOOL _sapp_emsc_frame(double time, void* userData) { _SOKOL_UNUSED(time); _SOKOL_UNUSED(userData); _sapp_frame(); return EM_TRUE; } _SOKOL_PRIVATE EM_BOOL _sapp_emsc_context_cb(int emsc_type, const void* reserved, void* user_data) { sapp_event_type type; switch (emsc_type) { case EMSCRIPTEN_EVENT_WEBGLCONTEXTLOST: type = SAPP_EVENTTYPE_SUSPENDED; break; case EMSCRIPTEN_EVENT_WEBGLCONTEXTRESTORED: type = SAPP_EVENTTYPE_RESUMED; break; default: type = SAPP_EVENTTYPE_INVALID; break; } if (_sapp_events_enabled() && (SAPP_EVENTTYPE_INVALID != type)) { _sapp_init_event(type); _sapp_call_event(&_sapp.event); } return true; } _SOKOL_PRIVATE EM_BOOL _sapp_emsc_mouse_cb(int emsc_type, const EmscriptenMouseEvent* emsc_event, void* user_data) { _sapp.mouse_x = (emsc_event->targetX * _sapp.dpi_scale); _sapp.mouse_y = (emsc_event->targetY * _sapp.dpi_scale); if (_sapp_events_enabled() && (emsc_event->button >= 0) && (emsc_event->button < SAPP_MAX_MOUSEBUTTONS)) { sapp_event_type type; bool is_button_event = false; switch (emsc_type) { case EMSCRIPTEN_EVENT_MOUSEDOWN: type = SAPP_EVENTTYPE_MOUSE_DOWN; is_button_event = true; break; case EMSCRIPTEN_EVENT_MOUSEUP: type = SAPP_EVENTTYPE_MOUSE_UP; is_button_event = true; break; case EMSCRIPTEN_EVENT_MOUSEMOVE: type = SAPP_EVENTTYPE_MOUSE_MOVE; break; case EMSCRIPTEN_EVENT_MOUSEENTER: type = SAPP_EVENTTYPE_MOUSE_ENTER; break; case EMSCRIPTEN_EVENT_MOUSELEAVE: type = SAPP_EVENTTYPE_MOUSE_LEAVE; break; default: type = SAPP_EVENTTYPE_INVALID; break; } if (type != SAPP_EVENTTYPE_INVALID) { _sapp_init_event(type); if (emsc_event->ctrlKey) { _sapp.event.modifiers |= SAPP_MODIFIER_CTRL; } if (emsc_event->shiftKey) { _sapp.event.modifiers |= SAPP_MODIFIER_SHIFT; } if (emsc_event->altKey) { _sapp.event.modifiers |= SAPP_MODIFIER_ALT; } if (emsc_event->metaKey) { _sapp.event.modifiers |= SAPP_MODIFIER_SUPER; } if (is_button_event) { switch (emsc_event->button) { case 0: _sapp.event.mouse_button = SAPP_MOUSEBUTTON_LEFT; break; case 1: _sapp.event.mouse_button = SAPP_MOUSEBUTTON_MIDDLE; break; case 2: _sapp.event.mouse_button = SAPP_MOUSEBUTTON_RIGHT; break; default: _sapp.event.mouse_button = (sapp_mousebutton)emsc_event->button; break; } } else { _sapp.event.mouse_button = SAPP_MOUSEBUTTON_INVALID; } _sapp.event.mouse_x = _sapp.mouse_x; _sapp.event.mouse_y = _sapp.mouse_y; _sapp_call_event(&_sapp.event); } } _sapp_emsc_update_keyboard_state(); return true; } _SOKOL_PRIVATE EM_BOOL _sapp_emsc_wheel_cb(int emsc_type, const EmscriptenWheelEvent* emsc_event, void* user_data) { if (_sapp_events_enabled()) { _sapp_init_event(SAPP_EVENTTYPE_MOUSE_SCROLL); if (emsc_event->mouse.ctrlKey) { _sapp.event.modifiers |= SAPP_MODIFIER_CTRL; } if (emsc_event->mouse.shiftKey) { _sapp.event.modifiers |= SAPP_MODIFIER_SHIFT; } if (emsc_event->mouse.altKey) { _sapp.event.modifiers |= SAPP_MODIFIER_ALT; } if (emsc_event->mouse.metaKey) { _sapp.event.modifiers |= SAPP_MODIFIER_SUPER; } _sapp.event.scroll_x = -0.1 * (float)emsc_event->deltaX; _sapp.event.scroll_y = -0.1 * (float)emsc_event->deltaY; _sapp_call_event(&_sapp.event); } _sapp_emsc_update_keyboard_state(); return true; } _SOKOL_PRIVATE EM_BOOL _sapp_emsc_key_cb(int emsc_type, const EmscriptenKeyboardEvent* emsc_event, void* user_data) { bool retval = true; if (_sapp_events_enabled()) { sapp_event_type type; switch (emsc_type) { case EMSCRIPTEN_EVENT_KEYDOWN: type = SAPP_EVENTTYPE_KEY_DOWN; break; case EMSCRIPTEN_EVENT_KEYUP: type = SAPP_EVENTTYPE_KEY_UP; break; case EMSCRIPTEN_EVENT_KEYPRESS: type = SAPP_EVENTTYPE_CHAR; break; default: type = SAPP_EVENTTYPE_INVALID; break; } if (type != SAPP_EVENTTYPE_INVALID) { _sapp_init_event(type); if (emsc_event->ctrlKey) { _sapp.event.modifiers |= SAPP_MODIFIER_CTRL; } if (emsc_event->shiftKey) { _sapp.event.modifiers |= SAPP_MODIFIER_SHIFT; } if (emsc_event->altKey) { _sapp.event.modifiers |= SAPP_MODIFIER_ALT; } if (emsc_event->metaKey) { _sapp.event.modifiers |= SAPP_MODIFIER_SUPER; } if (type == SAPP_EVENTTYPE_CHAR) { _sapp.event.char_code = emsc_event->charCode; } else { _sapp.event.key_code = _sapp_translate_key(emsc_event->keyCode); /* only forward a certain key ranges to the browser */ switch (_sapp.event.key_code) { case SAPP_KEYCODE_WORLD_1: case SAPP_KEYCODE_WORLD_2: case SAPP_KEYCODE_ESCAPE: case SAPP_KEYCODE_ENTER: case SAPP_KEYCODE_TAB: case SAPP_KEYCODE_BACKSPACE: case SAPP_KEYCODE_INSERT: case SAPP_KEYCODE_DELETE: case SAPP_KEYCODE_RIGHT: case SAPP_KEYCODE_LEFT: case SAPP_KEYCODE_DOWN: case SAPP_KEYCODE_UP: case SAPP_KEYCODE_PAGE_UP: case SAPP_KEYCODE_PAGE_DOWN: case SAPP_KEYCODE_HOME: case SAPP_KEYCODE_END: case SAPP_KEYCODE_CAPS_LOCK: case SAPP_KEYCODE_SCROLL_LOCK: case SAPP_KEYCODE_NUM_LOCK: case SAPP_KEYCODE_PRINT_SCREEN: case SAPP_KEYCODE_PAUSE: case SAPP_KEYCODE_F1: case SAPP_KEYCODE_F2: case SAPP_KEYCODE_F3: case SAPP_KEYCODE_F4: case SAPP_KEYCODE_F5: case SAPP_KEYCODE_F6: case SAPP_KEYCODE_F7: case SAPP_KEYCODE_F8: case SAPP_KEYCODE_F9: case SAPP_KEYCODE_F10: case SAPP_KEYCODE_F11: case SAPP_KEYCODE_F12: case SAPP_KEYCODE_F13: case SAPP_KEYCODE_F14: case SAPP_KEYCODE_F15: case SAPP_KEYCODE_F16: case SAPP_KEYCODE_F17: case SAPP_KEYCODE_F18: case SAPP_KEYCODE_F19: case SAPP_KEYCODE_F20: case SAPP_KEYCODE_F21: case SAPP_KEYCODE_F22: case SAPP_KEYCODE_F23: case SAPP_KEYCODE_F24: case SAPP_KEYCODE_F25: case SAPP_KEYCODE_LEFT_SHIFT: case SAPP_KEYCODE_LEFT_CONTROL: case SAPP_KEYCODE_LEFT_ALT: case SAPP_KEYCODE_LEFT_SUPER: case SAPP_KEYCODE_RIGHT_SHIFT: case SAPP_KEYCODE_RIGHT_CONTROL: case SAPP_KEYCODE_RIGHT_ALT: case SAPP_KEYCODE_RIGHT_SUPER: case SAPP_KEYCODE_MENU: /* consume the event */ break; default: /* forward key to browser */ retval = false; break; } } _sapp_call_event(&_sapp.event); } } _sapp_emsc_update_keyboard_state(); return retval; } _SOKOL_PRIVATE EM_BOOL _sapp_emsc_touch_cb(int emsc_type, const EmscriptenTouchEvent* emsc_event, void* user_data) { bool retval = true; if (_sapp_events_enabled()) { sapp_event_type type; switch (emsc_type) { case EMSCRIPTEN_EVENT_TOUCHSTART: type = SAPP_EVENTTYPE_TOUCHES_BEGAN; break; case EMSCRIPTEN_EVENT_TOUCHMOVE: type = SAPP_EVENTTYPE_TOUCHES_MOVED; break; case EMSCRIPTEN_EVENT_TOUCHEND: type = SAPP_EVENTTYPE_TOUCHES_ENDED; break; case EMSCRIPTEN_EVENT_TOUCHCANCEL: type = SAPP_EVENTTYPE_TOUCHES_CANCELLED; break; default: type = SAPP_EVENTTYPE_INVALID; retval = false; break; } if (type != SAPP_EVENTTYPE_INVALID) { _sapp_init_event(type); if (emsc_event->ctrlKey) { _sapp.event.modifiers |= SAPP_MODIFIER_CTRL; } if (emsc_event->shiftKey) { _sapp.event.modifiers |= SAPP_MODIFIER_SHIFT; } if (emsc_event->altKey) { _sapp.event.modifiers |= SAPP_MODIFIER_ALT; } if (emsc_event->metaKey) { _sapp.event.modifiers |= SAPP_MODIFIER_SUPER; } _sapp.event.num_touches = emsc_event->numTouches; if (_sapp.event.num_touches > SAPP_MAX_TOUCHPOINTS) { _sapp.event.num_touches = SAPP_MAX_TOUCHPOINTS; } for (int i = 0; i < _sapp.event.num_touches; i++) { const EmscriptenTouchPoint* src = &emsc_event->touches[i]; sapp_touchpoint* dst = &_sapp.event.touches[i]; dst->identifier = src->identifier; dst->pos_x = src->targetX * _sapp.dpi_scale; dst->pos_y = src->targetY * _sapp.dpi_scale; dst->changed = src->isChanged; } _sapp_call_event(&_sapp.event); } } _sapp_emsc_update_keyboard_state(); return retval; } _SOKOL_PRIVATE void _sapp_emsc_init_keytable(void) { _sapp.keycodes[8] = SAPP_KEYCODE_BACKSPACE; _sapp.keycodes[9] = SAPP_KEYCODE_TAB; _sapp.keycodes[13] = SAPP_KEYCODE_ENTER; _sapp.keycodes[16] = SAPP_KEYCODE_LEFT_SHIFT; _sapp.keycodes[17] = SAPP_KEYCODE_LEFT_CONTROL; _sapp.keycodes[18] = SAPP_KEYCODE_LEFT_ALT; _sapp.keycodes[19] = SAPP_KEYCODE_PAUSE; _sapp.keycodes[27] = SAPP_KEYCODE_ESCAPE; _sapp.keycodes[32] = SAPP_KEYCODE_SPACE; _sapp.keycodes[33] = SAPP_KEYCODE_PAGE_UP; _sapp.keycodes[34] = SAPP_KEYCODE_PAGE_DOWN; _sapp.keycodes[35] = SAPP_KEYCODE_END; _sapp.keycodes[36] = SAPP_KEYCODE_HOME; _sapp.keycodes[37] = SAPP_KEYCODE_LEFT; _sapp.keycodes[38] = SAPP_KEYCODE_UP; _sapp.keycodes[39] = SAPP_KEYCODE_RIGHT; _sapp.keycodes[40] = SAPP_KEYCODE_DOWN; _sapp.keycodes[45] = SAPP_KEYCODE_INSERT; _sapp.keycodes[46] = SAPP_KEYCODE_DELETE; _sapp.keycodes[48] = SAPP_KEYCODE_0; _sapp.keycodes[49] = SAPP_KEYCODE_1; _sapp.keycodes[50] = SAPP_KEYCODE_2; _sapp.keycodes[51] = SAPP_KEYCODE_3; _sapp.keycodes[52] = SAPP_KEYCODE_4; _sapp.keycodes[53] = SAPP_KEYCODE_5; _sapp.keycodes[54] = SAPP_KEYCODE_6; _sapp.keycodes[55] = SAPP_KEYCODE_7; _sapp.keycodes[56] = SAPP_KEYCODE_8; _sapp.keycodes[57] = SAPP_KEYCODE_9; _sapp.keycodes[59] = SAPP_KEYCODE_SEMICOLON; _sapp.keycodes[64] = SAPP_KEYCODE_EQUAL; _sapp.keycodes[65] = SAPP_KEYCODE_A; _sapp.keycodes[66] = SAPP_KEYCODE_B; _sapp.keycodes[67] = SAPP_KEYCODE_C; _sapp.keycodes[68] = SAPP_KEYCODE_D; _sapp.keycodes[69] = SAPP_KEYCODE_E; _sapp.keycodes[70] = SAPP_KEYCODE_F; _sapp.keycodes[71] = SAPP_KEYCODE_G; _sapp.keycodes[72] = SAPP_KEYCODE_H; _sapp.keycodes[73] = SAPP_KEYCODE_I; _sapp.keycodes[74] = SAPP_KEYCODE_J; _sapp.keycodes[75] = SAPP_KEYCODE_K; _sapp.keycodes[76] = SAPP_KEYCODE_L; _sapp.keycodes[77] = SAPP_KEYCODE_M; _sapp.keycodes[78] = SAPP_KEYCODE_N; _sapp.keycodes[79] = SAPP_KEYCODE_O; _sapp.keycodes[80] = SAPP_KEYCODE_P; _sapp.keycodes[81] = SAPP_KEYCODE_Q; _sapp.keycodes[82] = SAPP_KEYCODE_R; _sapp.keycodes[83] = SAPP_KEYCODE_S; _sapp.keycodes[84] = SAPP_KEYCODE_T; _sapp.keycodes[85] = SAPP_KEYCODE_U; _sapp.keycodes[86] = SAPP_KEYCODE_V; _sapp.keycodes[87] = SAPP_KEYCODE_W; _sapp.keycodes[88] = SAPP_KEYCODE_X; _sapp.keycodes[89] = SAPP_KEYCODE_Y; _sapp.keycodes[90] = SAPP_KEYCODE_Z; _sapp.keycodes[91] = SAPP_KEYCODE_LEFT_SUPER; _sapp.keycodes[93] = SAPP_KEYCODE_MENU; _sapp.keycodes[96] = SAPP_KEYCODE_KP_0; _sapp.keycodes[97] = SAPP_KEYCODE_KP_1; _sapp.keycodes[98] = SAPP_KEYCODE_KP_2; _sapp.keycodes[99] = SAPP_KEYCODE_KP_3; _sapp.keycodes[100] = SAPP_KEYCODE_KP_4; _sapp.keycodes[101] = SAPP_KEYCODE_KP_5; _sapp.keycodes[102] = SAPP_KEYCODE_KP_6; _sapp.keycodes[103] = SAPP_KEYCODE_KP_7; _sapp.keycodes[104] = SAPP_KEYCODE_KP_8; _sapp.keycodes[105] = SAPP_KEYCODE_KP_9; _sapp.keycodes[106] = SAPP_KEYCODE_KP_MULTIPLY; _sapp.keycodes[107] = SAPP_KEYCODE_KP_ADD; _sapp.keycodes[109] = SAPP_KEYCODE_KP_SUBTRACT; _sapp.keycodes[110] = SAPP_KEYCODE_KP_DECIMAL; _sapp.keycodes[111] = SAPP_KEYCODE_KP_DIVIDE; _sapp.keycodes[112] = SAPP_KEYCODE_F1; _sapp.keycodes[113] = SAPP_KEYCODE_F2; _sapp.keycodes[114] = SAPP_KEYCODE_F3; _sapp.keycodes[115] = SAPP_KEYCODE_F4; _sapp.keycodes[116] = SAPP_KEYCODE_F5; _sapp.keycodes[117] = SAPP_KEYCODE_F6; _sapp.keycodes[118] = SAPP_KEYCODE_F7; _sapp.keycodes[119] = SAPP_KEYCODE_F8; _sapp.keycodes[120] = SAPP_KEYCODE_F9; _sapp.keycodes[121] = SAPP_KEYCODE_F10; _sapp.keycodes[122] = SAPP_KEYCODE_F11; _sapp.keycodes[123] = SAPP_KEYCODE_F12; _sapp.keycodes[144] = SAPP_KEYCODE_NUM_LOCK; _sapp.keycodes[145] = SAPP_KEYCODE_SCROLL_LOCK; _sapp.keycodes[173] = SAPP_KEYCODE_MINUS; _sapp.keycodes[186] = SAPP_KEYCODE_SEMICOLON; _sapp.keycodes[187] = SAPP_KEYCODE_EQUAL; _sapp.keycodes[188] = SAPP_KEYCODE_COMMA; _sapp.keycodes[189] = SAPP_KEYCODE_MINUS; _sapp.keycodes[190] = SAPP_KEYCODE_PERIOD; _sapp.keycodes[191] = SAPP_KEYCODE_SLASH; _sapp.keycodes[192] = SAPP_KEYCODE_GRAVE_ACCENT; _sapp.keycodes[219] = SAPP_KEYCODE_LEFT_BRACKET; _sapp.keycodes[220] = SAPP_KEYCODE_BACKSLASH; _sapp.keycodes[221] = SAPP_KEYCODE_RIGHT_BRACKET; _sapp.keycodes[222] = SAPP_KEYCODE_APOSTROPHE; _sapp.keycodes[224] = SAPP_KEYCODE_LEFT_SUPER; } _SOKOL_PRIVATE void _sapp_run(const sapp_desc* desc) { _sapp_init_state(desc); _sapp_emsc_init_keytable(); double w, h; if (_sapp.html5_canvas_resize) { w = (double) _sapp.desc.width; h = (double) _sapp.desc.height; } else { emscripten_get_element_css_size(_sapp.html5_canvas_name, &w, &h); emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, false, _sapp_emsc_size_changed); } if (_sapp.desc.high_dpi) { _sapp.dpi_scale = emscripten_get_device_pixel_ratio(); w *= _sapp.dpi_scale; h *= _sapp.dpi_scale; } emscripten_set_canvas_element_size(_sapp.html5_canvas_name, w, h); _sapp.framebuffer_width = (int) w; _sapp.framebuffer_height = (int) h; EmscriptenWebGLContextAttributes attrs; emscripten_webgl_init_context_attributes(&attrs); attrs.alpha = _sapp.desc.alpha; attrs.depth = true; attrs.stencil = true; attrs.antialias = _sapp.sample_count > 1; attrs.premultipliedAlpha = _sapp.desc.html5_premultiplied_alpha; attrs.preserveDrawingBuffer = _sapp.desc.html5_preserve_drawing_buffer; attrs.enableExtensionsByDefault = true; #if defined(SOKOL_GLES3) if (_sapp.desc.gl_force_gles2) { attrs.majorVersion = 1; _sapp.gles2_fallback = true; } else { attrs.majorVersion = 2; } #endif EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx = emscripten_webgl_create_context(_sapp.html5_canvas_name, &attrs); if (!ctx) { attrs.majorVersion = 1; ctx = emscripten_webgl_create_context(_sapp.html5_canvas_name, &attrs); _sapp.gles2_fallback = true; } emscripten_webgl_make_context_current(ctx); _sapp.valid = true; emscripten_set_mousedown_callback(_sapp.html5_canvas_name, 0, true, _sapp_emsc_mouse_cb); emscripten_set_mouseup_callback(_sapp.html5_canvas_name, 0, true, _sapp_emsc_mouse_cb); emscripten_set_mousemove_callback(_sapp.html5_canvas_name, 0, true, _sapp_emsc_mouse_cb); emscripten_set_mouseenter_callback(_sapp.html5_canvas_name, 0, true, _sapp_emsc_mouse_cb); emscripten_set_mouseleave_callback(_sapp.html5_canvas_name, 0, true, _sapp_emsc_mouse_cb); emscripten_set_wheel_callback(_sapp.html5_canvas_name, 0, true, _sapp_emsc_wheel_cb); emscripten_set_keydown_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, _sapp_emsc_key_cb); emscripten_set_keyup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, _sapp_emsc_key_cb); emscripten_set_keypress_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, _sapp_emsc_key_cb); emscripten_set_touchstart_callback(_sapp.html5_canvas_name, 0, true, _sapp_emsc_touch_cb); emscripten_set_touchmove_callback(_sapp.html5_canvas_name, 0, true, _sapp_emsc_touch_cb); emscripten_set_touchend_callback(_sapp.html5_canvas_name, 0, true, _sapp_emsc_touch_cb); emscripten_set_touchcancel_callback(_sapp.html5_canvas_name, 0, true, _sapp_emsc_touch_cb); emscripten_set_webglcontextlost_callback(_sapp.html5_canvas_name, 0, true, _sapp_emsc_context_cb); emscripten_set_webglcontextrestored_callback(_sapp.html5_canvas_name, 0, true, _sapp_emsc_context_cb); emscripten_request_animation_frame_loop(_sapp_emsc_frame, 0); } #if !defined(SOKOL_NO_ENTRY) int main(int argc, char* argv[]) { sapp_desc desc = sokol_main(argc, argv); _sapp_run(&desc); return 0; } #endif /* SOKOL_NO_ENTRY */ #endif /* __EMSCRIPTEN__ */ /*== MISC GL SUPPORT FUNCTIONS ================================================*/ #if defined(SOKOL_GLCORE33) typedef struct { int red_bits; int green_bits; int blue_bits; int alpha_bits; int depth_bits; int stencil_bits; int samples; bool doublebuffer; uintptr_t handle; } _sapp_gl_fbconfig; _SOKOL_PRIVATE void _sapp_gl_init_fbconfig(_sapp_gl_fbconfig* fbconfig) { memset(fbconfig, 0, sizeof(_sapp_gl_fbconfig)); /* -1 means "don't care" */ fbconfig->red_bits = -1; fbconfig->green_bits = -1; fbconfig->blue_bits = -1; fbconfig->alpha_bits = -1; fbconfig->depth_bits = -1; fbconfig->stencil_bits = -1; fbconfig->samples = -1; } _SOKOL_PRIVATE const _sapp_gl_fbconfig* _sapp_gl_choose_fbconfig(const _sapp_gl_fbconfig* desired, const _sapp_gl_fbconfig* alternatives, unsigned int count) { unsigned int i; unsigned int missing, least_missing = 1000000; unsigned int color_diff, least_color_diff = 10000000; unsigned int extra_diff, least_extra_diff = 10000000; const _sapp_gl_fbconfig* current; const _sapp_gl_fbconfig* closest = NULL; for (i = 0; i < count; i++) { current = alternatives + i; if (desired->doublebuffer != current->doublebuffer) { continue; } missing = 0; if (desired->alpha_bits > 0 && current->alpha_bits == 0) { missing++; } if (desired->depth_bits > 0 && current->depth_bits == 0) { missing++; } if (desired->stencil_bits > 0 && current->stencil_bits == 0) { missing++; } if (desired->samples > 0 && current->samples == 0) { /* Technically, several multisampling buffers could be involved, but that's a lower level implementation detail and not important to us here, so we count them as one */ missing++; } /* These polynomials make many small channel size differences matter less than one large channel size difference Calculate color channel size difference value */ color_diff = 0; if (desired->red_bits != -1) { color_diff += (desired->red_bits - current->red_bits) * (desired->red_bits - current->red_bits); } if (desired->green_bits != -1) { color_diff += (desired->green_bits - current->green_bits) * (desired->green_bits - current->green_bits); } if (desired->blue_bits != -1) { color_diff += (desired->blue_bits - current->blue_bits) * (desired->blue_bits - current->blue_bits); } /* Calculate non-color channel size difference value */ extra_diff = 0; if (desired->alpha_bits != -1) { extra_diff += (desired->alpha_bits - current->alpha_bits) * (desired->alpha_bits - current->alpha_bits); } if (desired->depth_bits != -1) { extra_diff += (desired->depth_bits - current->depth_bits) * (desired->depth_bits - current->depth_bits); } if (desired->stencil_bits != -1) { extra_diff += (desired->stencil_bits - current->stencil_bits) * (desired->stencil_bits - current->stencil_bits); } if (desired->samples != -1) { extra_diff += (desired->samples - current->samples) * (desired->samples - current->samples); } /* Figure out if the current one is better than the best one found so far Least number of missing buffers is the most important heuristic, then color buffer size match and lastly size match for other buffers */ if (missing < least_missing) { closest = current; } else if (missing == least_missing) { if ((color_diff < least_color_diff) || (color_diff == least_color_diff && extra_diff < least_extra_diff)) { closest = current; } } if (current == closest) { least_missing = missing; least_color_diff = color_diff; least_extra_diff = extra_diff; } } return closest; } #endif /*== WINDOWS ==================================================================*/ #if defined(_WIN32) #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include #include #if defined(SOKOL_D3D11) #ifndef D3D11_NO_HELPERS #define D3D11_NO_HELPERS #endif #ifndef CINTERFACE #define CINTERFACE #endif #ifndef COBJMACROS #define COBJMACROS #endif #include #include #include #if (defined(WINAPI_FAMILY_PARTITION) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)) #pragma comment (lib, "WindowsApp.lib") #else #pragma comment (lib, "user32.lib") #pragma comment (lib, "dxgi.lib") #pragma comment (lib, "d3d11.lib") #pragma comment (lib, "dxguid.lib") #endif #endif /* see https://github.com/floooh/sokol/issues/138 */ #ifndef WM_MOUSEHWHEEL #define WM_MOUSEHWHEEL (0x020E) #endif #ifndef DPI_ENUMS_DECLARED typedef enum PROCESS_DPI_AWARENESS { PROCESS_DPI_UNAWARE = 0, PROCESS_SYSTEM_DPI_AWARE = 1, PROCESS_PER_MONITOR_DPI_AWARE = 2 } PROCESS_DPI_AWARENESS; typedef enum MONITOR_DPI_TYPE { MDT_EFFECTIVE_DPI = 0, MDT_ANGULAR_DPI = 1, MDT_RAW_DPI = 2, MDT_DEFAULT = MDT_EFFECTIVE_DPI } MONITOR_DPI_TYPE; #endif /*DPI_ENUMS_DECLARED*/ static HWND _sapp_win32_hwnd; static HDC _sapp_win32_dc; static bool _sapp_win32_in_create_window; static bool _sapp_win32_dpi_aware; static float _sapp_win32_content_scale; static float _sapp_win32_window_scale; static float _sapp_win32_mouse_scale; static bool _sapp_win32_iconified; typedef BOOL(WINAPI * SETPROCESSDPIAWARE_T)(void); typedef HRESULT(WINAPI * SETPROCESSDPIAWARENESS_T)(PROCESS_DPI_AWARENESS); typedef HRESULT(WINAPI * GETDPIFORMONITOR_T)(HMONITOR, MONITOR_DPI_TYPE, UINT*, UINT*); static SETPROCESSDPIAWARE_T _sapp_win32_setprocessdpiaware; static SETPROCESSDPIAWARENESS_T _sapp_win32_setprocessdpiawareness; static GETDPIFORMONITOR_T _sapp_win32_getdpiformonitor; #if defined(SOKOL_D3D11) static ID3D11Device* _sapp_d3d11_device; static ID3D11DeviceContext* _sapp_d3d11_device_context; static DXGI_SWAP_CHAIN_DESC _sapp_dxgi_swap_chain_desc; static IDXGISwapChain* _sapp_dxgi_swap_chain; static ID3D11Texture2D* _sapp_d3d11_rt; static ID3D11RenderTargetView* _sapp_d3d11_rtv; static ID3D11Texture2D* _sapp_d3d11_ds; static ID3D11DepthStencilView* _sapp_d3d11_dsv; #endif #define WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000 #define WGL_SUPPORT_OPENGL_ARB 0x2010 #define WGL_DRAW_TO_WINDOW_ARB 0x2001 #define WGL_PIXEL_TYPE_ARB 0x2013 #define WGL_TYPE_RGBA_ARB 0x202b #define WGL_ACCELERATION_ARB 0x2003 #define WGL_NO_ACCELERATION_ARB 0x2025 #define WGL_RED_BITS_ARB 0x2015 #define WGL_RED_SHIFT_ARB 0x2016 #define WGL_GREEN_BITS_ARB 0x2017 #define WGL_GREEN_SHIFT_ARB 0x2018 #define WGL_BLUE_BITS_ARB 0x2019 #define WGL_BLUE_SHIFT_ARB 0x201a #define WGL_ALPHA_BITS_ARB 0x201b #define WGL_ALPHA_SHIFT_ARB 0x201c #define WGL_ACCUM_BITS_ARB 0x201d #define WGL_ACCUM_RED_BITS_ARB 0x201e #define WGL_ACCUM_GREEN_BITS_ARB 0x201f #define WGL_ACCUM_BLUE_BITS_ARB 0x2020 #define WGL_ACCUM_ALPHA_BITS_ARB 0x2021 #define WGL_DEPTH_BITS_ARB 0x2022 #define WGL_STENCIL_BITS_ARB 0x2023 #define WGL_AUX_BUFFERS_ARB 0x2024 #define WGL_STEREO_ARB 0x2012 #define WGL_DOUBLE_BUFFER_ARB 0x2011 #define WGL_SAMPLES_ARB 0x2042 #define WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20a9 #define WGL_CONTEXT_DEBUG_BIT_ARB 0x00000001 #define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002 #define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126 #define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 #define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002 #define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091 #define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 #define WGL_CONTEXT_FLAGS_ARB 0x2094 #define WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB 0x00000004 #define WGL_LOSE_CONTEXT_ON_RESET_ARB 0x8252 #define WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB 0x8256 #define WGL_NO_RESET_NOTIFICATION_ARB 0x8261 #define WGL_CONTEXT_RELEASE_BEHAVIOR_ARB 0x2097 #define WGL_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB 0 #define WGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB 0x2098 #define WGL_COLORSPACE_EXT 0x309d #define WGL_COLORSPACE_SRGB_EXT 0x3089 #define ERROR_INVALID_VERSION_ARB 0x2095 #define ERROR_INVALID_PROFILE_ARB 0x2096 #define ERROR_INCOMPATIBLE_DEVICE_CONTEXTS_ARB 0x2054 typedef BOOL (WINAPI * PFNWGLSWAPINTERVALEXTPROC)(int); typedef BOOL (WINAPI * PFNWGLGETPIXELFORMATATTRIBIVARBPROC)(HDC,int,int,UINT,const int*,int*); typedef const char* (WINAPI * PFNWGLGETEXTENSIONSSTRINGEXTPROC)(void); typedef const char* (WINAPI * PFNWGLGETEXTENSIONSSTRINGARBPROC)(HDC); typedef HGLRC (WINAPI * PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC,HGLRC,const int*); typedef HGLRC (WINAPI * PFN_wglCreateContext)(HDC); typedef BOOL (WINAPI * PFN_wglDeleteContext)(HGLRC); typedef PROC (WINAPI * PFN_wglGetProcAddress)(LPCSTR); typedef HDC (WINAPI * PFN_wglGetCurrentDC)(void); typedef BOOL (WINAPI * PFN_wglMakeCurrent)(HDC,HGLRC); static HINSTANCE _sapp_opengl32; static HGLRC _sapp_gl_ctx; static PFN_wglCreateContext _sapp_wglCreateContext; static PFN_wglDeleteContext _sapp_wglDeleteContext; static PFN_wglGetProcAddress _sapp_wglGetProcAddress; static PFN_wglGetCurrentDC _sapp_wglGetCurrentDC; static PFN_wglMakeCurrent _sapp_wglMakeCurrent; static PFNWGLSWAPINTERVALEXTPROC _sapp_SwapIntervalEXT; static PFNWGLGETPIXELFORMATATTRIBIVARBPROC _sapp_GetPixelFormatAttribivARB; static PFNWGLGETEXTENSIONSSTRINGEXTPROC _sapp_GetExtensionsStringEXT; static PFNWGLGETEXTENSIONSSTRINGARBPROC _sapp_GetExtensionsStringARB; static PFNWGLCREATECONTEXTATTRIBSARBPROC _sapp_CreateContextAttribsARB; static bool _sapp_ext_swap_control; static bool _sapp_arb_multisample; static bool _sapp_arb_pixel_format; static bool _sapp_arb_create_context; static bool _sapp_arb_create_context_profile; static HWND _sapp_win32_msg_hwnd; static HDC _sapp_win32_msg_dc; /* NOTE: the optional GL loader only contains the GL constants and functions required for sokol_gfx.h, if you need more, you'll need to use you own gl header-generator/loader */ #if !defined(SOKOL_WIN32_NO_GL_LOADER) #if defined(SOKOL_GLCORE33) #define __gl_h_ 1 #define __gl32_h_ 1 #define __gl31_h_ 1 #define __GL_H__ 1 #define __glext_h_ 1 #define __GLEXT_H_ 1 #define __gltypes_h_ 1 #define __glcorearb_h_ 1 #define __gl_glcorearb_h_ 1 #define GL_APIENTRY APIENTRY typedef unsigned int GLenum; typedef unsigned int GLuint; typedef int GLsizei; typedef char GLchar; typedef ptrdiff_t GLintptr; typedef ptrdiff_t GLsizeiptr; typedef double GLclampd; typedef unsigned short GLushort; typedef unsigned char GLubyte; typedef unsigned char GLboolean; typedef uint64_t GLuint64; typedef double GLdouble; typedef unsigned short GLhalf; typedef float GLclampf; typedef unsigned int GLbitfield; typedef signed char GLbyte; typedef short GLshort; typedef void GLvoid; typedef int64_t GLint64; typedef float GLfloat; typedef struct __GLsync * GLsync; typedef int GLint; #define GL_INT_2_10_10_10_REV 0x8D9F #define GL_R32F 0x822E #define GL_PROGRAM_POINT_SIZE 0x8642 #define GL_STENCIL_ATTACHMENT 0x8D20 #define GL_DEPTH_ATTACHMENT 0x8D00 #define GL_COLOR_ATTACHMENT2 0x8CE2 #define GL_COLOR_ATTACHMENT0 0x8CE0 #define GL_R16F 0x822D #define GL_COLOR_ATTACHMENT22 0x8CF6 #define GL_DRAW_FRAMEBUFFER 0x8CA9 #define GL_FRAMEBUFFER_COMPLETE 0x8CD5 #define GL_NUM_EXTENSIONS 0x821D #define GL_INFO_LOG_LENGTH 0x8B84 #define GL_VERTEX_SHADER 0x8B31 #define GL_INCR 0x1E02 #define GL_DYNAMIC_DRAW 0x88E8 #define GL_STATIC_DRAW 0x88E4 #define GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519 #define GL_TEXTURE_CUBE_MAP 0x8513 #define GL_FUNC_SUBTRACT 0x800A #define GL_FUNC_REVERSE_SUBTRACT 0x800B #define GL_CONSTANT_COLOR 0x8001 #define GL_DECR_WRAP 0x8508 #define GL_R8 0x8229 #define GL_LINEAR_MIPMAP_LINEAR 0x2703 #define GL_ELEMENT_ARRAY_BUFFER 0x8893 #define GL_SHORT 0x1402 #define GL_DEPTH_TEST 0x0B71 #define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518 #define GL_LINK_STATUS 0x8B82 #define GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517 #define GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E #define GL_RGBA16F 0x881A #define GL_CONSTANT_ALPHA 0x8003 #define GL_READ_FRAMEBUFFER 0x8CA8 #define GL_TEXTURE0 0x84C0 #define GL_TEXTURE_MIN_LOD 0x813A #define GL_CLAMP_TO_EDGE 0x812F #define GL_UNSIGNED_SHORT_5_6_5 0x8363 #define GL_TEXTURE_WRAP_R 0x8072 #define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 #define GL_NEAREST_MIPMAP_NEAREST 0x2700 #define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 #define GL_SRC_ALPHA_SATURATE 0x0308 #define GL_STREAM_DRAW 0x88E0 #define GL_ONE 1 #define GL_NEAREST_MIPMAP_LINEAR 0x2702 #define GL_RGB10_A2 0x8059 #define GL_RGBA8 0x8058 #define GL_COLOR_ATTACHMENT1 0x8CE1 #define GL_RGBA4 0x8056 #define GL_RGB8 0x8051 #define GL_ARRAY_BUFFER 0x8892 #define GL_STENCIL 0x1802 #define GL_TEXTURE_2D 0x0DE1 #define GL_DEPTH 0x1801 #define GL_FRONT 0x0404 #define GL_STENCIL_BUFFER_BIT 0x00000400 #define GL_REPEAT 0x2901 #define GL_RGBA 0x1908 #define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515 #define GL_DECR 0x1E03 #define GL_FRAGMENT_SHADER 0x8B30 #define GL_FLOAT 0x1406 #define GL_TEXTURE_MAX_LOD 0x813B #define GL_DEPTH_COMPONENT 0x1902 #define GL_ONE_MINUS_DST_ALPHA 0x0305 #define GL_COLOR 0x1800 #define GL_TEXTURE_2D_ARRAY 0x8C1A #define GL_TRIANGLES 0x0004 #define GL_UNSIGNED_BYTE 0x1401 #define GL_TEXTURE_MAG_FILTER 0x2800 #define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004 #define GL_NONE 0 #define GL_SRC_COLOR 0x0300 #define GL_BYTE 0x1400 #define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A #define GL_LINE_STRIP 0x0003 #define GL_TEXTURE_3D 0x806F #define GL_CW 0x0900 #define GL_LINEAR 0x2601 #define GL_RENDERBUFFER 0x8D41 #define GL_GEQUAL 0x0206 #define GL_COLOR_BUFFER_BIT 0x00004000 #define GL_RGBA32F 0x8814 #define GL_BLEND 0x0BE2 #define GL_ONE_MINUS_SRC_ALPHA 0x0303 #define GL_ONE_MINUS_CONSTANT_COLOR 0x8002 #define GL_TEXTURE_WRAP_T 0x2803 #define GL_TEXTURE_WRAP_S 0x2802 #define GL_TEXTURE_MIN_FILTER 0x2801 #define GL_LINEAR_MIPMAP_NEAREST 0x2701 #define GL_EXTENSIONS 0x1F03 #define GL_NO_ERROR 0 #define GL_REPLACE 0x1E01 #define GL_KEEP 0x1E00 #define GL_CCW 0x0901 #define GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516 #define GL_RGB 0x1907 #define GL_TRIANGLE_STRIP 0x0005 #define GL_FALSE 0 #define GL_ZERO 0 #define GL_CULL_FACE 0x0B44 #define GL_INVERT 0x150A #define GL_UNSIGNED_INT 0x1405 #define GL_UNSIGNED_SHORT 0x1403 #define GL_NEAREST 0x2600 #define GL_SCISSOR_TEST 0x0C11 #define GL_LEQUAL 0x0203 #define GL_STENCIL_TEST 0x0B90 #define GL_DITHER 0x0BD0 #define GL_DEPTH_COMPONENT16 0x81A5 #define GL_EQUAL 0x0202 #define GL_FRAMEBUFFER 0x8D40 #define GL_RGB5 0x8050 #define GL_LINES 0x0001 #define GL_DEPTH_BUFFER_BIT 0x00000100 #define GL_SRC_ALPHA 0x0302 #define GL_INCR_WRAP 0x8507 #define GL_LESS 0x0201 #define GL_MULTISAMPLE 0x809D #define GL_FRAMEBUFFER_BINDING 0x8CA6 #define GL_BACK 0x0405 #define GL_ALWAYS 0x0207 #define GL_FUNC_ADD 0x8006 #define GL_ONE_MINUS_DST_COLOR 0x0307 #define GL_NOTEQUAL 0x0205 #define GL_DST_COLOR 0x0306 #define GL_COMPILE_STATUS 0x8B81 #define GL_RED 0x1903 #define GL_COLOR_ATTACHMENT3 0x8CE3 #define GL_DST_ALPHA 0x0304 #define GL_RGB5_A1 0x8057 #define GL_GREATER 0x0204 #define GL_POLYGON_OFFSET_FILL 0x8037 #define GL_TRUE 1 #define GL_NEVER 0x0200 #define GL_POINTS 0x0000 #define GL_ONE_MINUS_SRC_COLOR 0x0301 #define GL_MIRRORED_REPEAT 0x8370 #define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8B4D typedef void (GL_APIENTRY *PFN_glBindVertexArray)(GLuint array); static PFN_glBindVertexArray _sapp_glBindVertexArray; typedef void (GL_APIENTRY *PFN_glFramebufferTextureLayer)(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); static PFN_glFramebufferTextureLayer _sapp_glFramebufferTextureLayer; typedef void (GL_APIENTRY *PFN_glGenFramebuffers)(GLsizei n, GLuint * framebuffers); static PFN_glGenFramebuffers _sapp_glGenFramebuffers; typedef void (GL_APIENTRY *PFN_glBindFramebuffer)(GLenum target, GLuint framebuffer); static PFN_glBindFramebuffer _sapp_glBindFramebuffer; typedef void (GL_APIENTRY *PFN_glBindRenderbuffer)(GLenum target, GLuint renderbuffer); static PFN_glBindRenderbuffer _sapp_glBindRenderbuffer; typedef const GLubyte * (GL_APIENTRY *PFN_glGetStringi)(GLenum name, GLuint index); static PFN_glGetStringi _sapp_glGetStringi; typedef void (GL_APIENTRY *PFN_glClearBufferfi)(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil); static PFN_glClearBufferfi _sapp_glClearBufferfi; typedef void (GL_APIENTRY *PFN_glClearBufferfv)(GLenum buffer, GLint drawbuffer, const GLfloat * value); static PFN_glClearBufferfv _sapp_glClearBufferfv; typedef void (GL_APIENTRY *PFN_glClearBufferuiv)(GLenum buffer, GLint drawbuffer, const GLuint * value); static PFN_glClearBufferuiv _sapp_glClearBufferuiv; typedef void (GL_APIENTRY *PFN_glDeleteRenderbuffers)(GLsizei n, const GLuint * renderbuffers); static PFN_glDeleteRenderbuffers _sapp_glDeleteRenderbuffers; typedef void (GL_APIENTRY *PFN_glUniform4fv)(GLint location, GLsizei count, const GLfloat * value); static PFN_glUniform4fv _sapp_glUniform4fv; typedef void (GL_APIENTRY *PFN_glUniform2fv)(GLint location, GLsizei count, const GLfloat * value); static PFN_glUniform2fv _sapp_glUniform2fv; typedef void (GL_APIENTRY *PFN_glUseProgram)(GLuint program); static PFN_glUseProgram _sapp_glUseProgram; typedef void (GL_APIENTRY *PFN_glShaderSource)(GLuint shader, GLsizei count, const GLchar *const* string, const GLint * length); static PFN_glShaderSource _sapp_glShaderSource; typedef void (GL_APIENTRY *PFN_glLinkProgram)(GLuint program); static PFN_glLinkProgram _sapp_glLinkProgram; typedef GLint (GL_APIENTRY *PFN_glGetUniformLocation)(GLuint program, const GLchar * name); static PFN_glGetUniformLocation _sapp_glGetUniformLocation; typedef void (GL_APIENTRY *PFN_glGetShaderiv)(GLuint shader, GLenum pname, GLint * params); static PFN_glGetShaderiv _sapp_glGetShaderiv; typedef void (GL_APIENTRY *PFN_glGetProgramInfoLog)(GLuint program, GLsizei bufSize, GLsizei * length, GLchar * infoLog); static PFN_glGetProgramInfoLog _sapp_glGetProgramInfoLog; typedef GLint (GL_APIENTRY *PFN_glGetAttribLocation)(GLuint program, const GLchar * name); static PFN_glGetAttribLocation _sapp_glGetAttribLocation; typedef void (GL_APIENTRY *PFN_glDisableVertexAttribArray)(GLuint index); static PFN_glDisableVertexAttribArray _sapp_glDisableVertexAttribArray; typedef void (GL_APIENTRY *PFN_glDeleteShader)(GLuint shader); static PFN_glDeleteShader _sapp_glDeleteShader; typedef void (GL_APIENTRY *PFN_glDeleteProgram)(GLuint program); static PFN_glDeleteProgram _sapp_glDeleteProgram; typedef void (GL_APIENTRY *PFN_glCompileShader)(GLuint shader); static PFN_glCompileShader _sapp_glCompileShader; typedef void (GL_APIENTRY *PFN_glStencilFuncSeparate)(GLenum face, GLenum func, GLint ref, GLuint mask); static PFN_glStencilFuncSeparate _sapp_glStencilFuncSeparate; typedef void (GL_APIENTRY *PFN_glStencilOpSeparate)(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); static PFN_glStencilOpSeparate _sapp_glStencilOpSeparate; typedef void (GL_APIENTRY *PFN_glRenderbufferStorageMultisample)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); static PFN_glRenderbufferStorageMultisample _sapp_glRenderbufferStorageMultisample; typedef void (GL_APIENTRY *PFN_glDrawBuffers)(GLsizei n, const GLenum * bufs); static PFN_glDrawBuffers _sapp_glDrawBuffers; typedef void (GL_APIENTRY *PFN_glVertexAttribDivisor)(GLuint index, GLuint divisor); static PFN_glVertexAttribDivisor _sapp_glVertexAttribDivisor; typedef void (GL_APIENTRY *PFN_glBufferSubData)(GLenum target, GLintptr offset, GLsizeiptr size, const void * data); static PFN_glBufferSubData _sapp_glBufferSubData; typedef void (GL_APIENTRY *PFN_glGenBuffers)(GLsizei n, GLuint * buffers); static PFN_glGenBuffers _sapp_glGenBuffers; typedef GLenum (GL_APIENTRY *PFN_glCheckFramebufferStatus)(GLenum target); static PFN_glCheckFramebufferStatus _sapp_glCheckFramebufferStatus; typedef void (GL_APIENTRY *PFN_glFramebufferRenderbuffer)(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); static PFN_glFramebufferRenderbuffer _sapp_glFramebufferRenderbuffer; typedef void (GL_APIENTRY *PFN_glCompressedTexImage2D)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void * data); static PFN_glCompressedTexImage2D _sapp_glCompressedTexImage2D; typedef void (GL_APIENTRY *PFN_glCompressedTexImage3D)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void * data); static PFN_glCompressedTexImage3D _sapp_glCompressedTexImage3D; typedef void (GL_APIENTRY *PFN_glActiveTexture)(GLenum texture); static PFN_glActiveTexture _sapp_glActiveTexture; typedef void (GL_APIENTRY *PFN_glTexSubImage3D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void * pixels); static PFN_glTexSubImage3D _sapp_glTexSubImage3D; typedef void (GL_APIENTRY *PFN_glUniformMatrix4fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); static PFN_glUniformMatrix4fv _sapp_glUniformMatrix4fv; typedef void (GL_APIENTRY *PFN_glRenderbufferStorage)(GLenum target, GLenum internalformat, GLsizei width, GLsizei height); static PFN_glRenderbufferStorage _sapp_glRenderbufferStorage; typedef void (GL_APIENTRY *PFN_glGenTextures)(GLsizei n, GLuint * textures); static PFN_glGenTextures _sapp_glGenTextures; typedef void (GL_APIENTRY *PFN_glPolygonOffset)(GLfloat factor, GLfloat units); static PFN_glPolygonOffset _sapp_glPolygonOffset; typedef void (GL_APIENTRY *PFN_glDrawElements)(GLenum mode, GLsizei count, GLenum type, const void * indices); static PFN_glDrawElements _sapp_glDrawElements; typedef void (GL_APIENTRY *PFN_glDeleteFramebuffers)(GLsizei n, const GLuint * framebuffers); static PFN_glDeleteFramebuffers _sapp_glDeleteFramebuffers; typedef void (GL_APIENTRY *PFN_glBlendEquationSeparate)(GLenum modeRGB, GLenum modeAlpha); static PFN_glBlendEquationSeparate _sapp_glBlendEquationSeparate; typedef void (GL_APIENTRY *PFN_glDeleteTextures)(GLsizei n, const GLuint * textures); static PFN_glDeleteTextures _sapp_glDeleteTextures; typedef void (GL_APIENTRY *PFN_glGetProgramiv)(GLuint program, GLenum pname, GLint * params); static PFN_glGetProgramiv _sapp_glGetProgramiv; typedef void (GL_APIENTRY *PFN_glBindTexture)(GLenum target, GLuint texture); static PFN_glBindTexture _sapp_glBindTexture; typedef void (GL_APIENTRY *PFN_glTexImage3D)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void * pixels); static PFN_glTexImage3D _sapp_glTexImage3D; typedef GLuint (GL_APIENTRY *PFN_glCreateShader)(GLenum type); static PFN_glCreateShader _sapp_glCreateShader; typedef void (GL_APIENTRY *PFN_glTexSubImage2D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void * pixels); static PFN_glTexSubImage2D _sapp_glTexSubImage2D; typedef void (GL_APIENTRY *PFN_glClearDepth)(GLdouble depth); static PFN_glClearDepth _sapp_glClearDepth; typedef void (GL_APIENTRY *PFN_glFramebufferTexture2D)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); static PFN_glFramebufferTexture2D _sapp_glFramebufferTexture2D; typedef GLuint (GL_APIENTRY *PFN_glCreateProgram)(); static PFN_glCreateProgram _sapp_glCreateProgram; typedef void (GL_APIENTRY *PFN_glViewport)(GLint x, GLint y, GLsizei width, GLsizei height); static PFN_glViewport _sapp_glViewport; typedef void (GL_APIENTRY *PFN_glDeleteBuffers)(GLsizei n, const GLuint * buffers); static PFN_glDeleteBuffers _sapp_glDeleteBuffers; typedef void (GL_APIENTRY *PFN_glDrawArrays)(GLenum mode, GLint first, GLsizei count); static PFN_glDrawArrays _sapp_glDrawArrays; typedef void (GL_APIENTRY *PFN_glDrawElementsInstanced)(GLenum mode, GLsizei count, GLenum type, const void * indices, GLsizei instancecount); static PFN_glDrawElementsInstanced _sapp_glDrawElementsInstanced; typedef void (GL_APIENTRY *PFN_glVertexAttribPointer)(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void * pointer); static PFN_glVertexAttribPointer _sapp_glVertexAttribPointer; typedef void (GL_APIENTRY *PFN_glUniform1i)(GLint location, GLint v0); static PFN_glUniform1i _sapp_glUniform1i; typedef void (GL_APIENTRY *PFN_glDisable)(GLenum cap); static PFN_glDisable _sapp_glDisable; typedef void (GL_APIENTRY *PFN_glColorMask)(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); static PFN_glColorMask _sapp_glColorMask; typedef void (GL_APIENTRY *PFN_glBindBuffer)(GLenum target, GLuint buffer); static PFN_glBindBuffer _sapp_glBindBuffer; typedef void (GL_APIENTRY *PFN_glDeleteVertexArrays)(GLsizei n, const GLuint * arrays); static PFN_glDeleteVertexArrays _sapp_glDeleteVertexArrays; typedef void (GL_APIENTRY *PFN_glDepthMask)(GLboolean flag); static PFN_glDepthMask _sapp_glDepthMask; typedef void (GL_APIENTRY *PFN_glDrawArraysInstanced)(GLenum mode, GLint first, GLsizei count, GLsizei instancecount); static PFN_glDrawArraysInstanced _sapp_glDrawArraysInstanced; typedef void (GL_APIENTRY *PFN_glClearStencil)(GLint s); static PFN_glClearStencil _sapp_glClearStencil; typedef void (GL_APIENTRY *PFN_glScissor)(GLint x, GLint y, GLsizei width, GLsizei height); static PFN_glScissor _sapp_glScissor; typedef void (GL_APIENTRY *PFN_glUniform3fv)(GLint location, GLsizei count, const GLfloat * value); static PFN_glUniform3fv _sapp_glUniform3fv; typedef void (GL_APIENTRY *PFN_glGenRenderbuffers)(GLsizei n, GLuint * renderbuffers); static PFN_glGenRenderbuffers _sapp_glGenRenderbuffers; typedef void (GL_APIENTRY *PFN_glBufferData)(GLenum target, GLsizeiptr size, const void * data, GLenum usage); static PFN_glBufferData _sapp_glBufferData; typedef void (GL_APIENTRY *PFN_glBlendFuncSeparate)(GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); static PFN_glBlendFuncSeparate _sapp_glBlendFuncSeparate; typedef void (GL_APIENTRY *PFN_glTexParameteri)(GLenum target, GLenum pname, GLint param); static PFN_glTexParameteri _sapp_glTexParameteri; typedef void (GL_APIENTRY *PFN_glGetIntegerv)(GLenum pname, GLint * data); static PFN_glGetIntegerv _sapp_glGetIntegerv; typedef void (GL_APIENTRY *PFN_glEnable)(GLenum cap); static PFN_glEnable _sapp_glEnable; typedef void (GL_APIENTRY *PFN_glBlitFramebuffer)(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); static PFN_glBlitFramebuffer _sapp_glBlitFramebuffer; typedef void (GL_APIENTRY *PFN_glStencilMask)(GLuint mask); static PFN_glStencilMask _sapp_glStencilMask; typedef void (GL_APIENTRY *PFN_glAttachShader)(GLuint program, GLuint shader); static PFN_glAttachShader _sapp_glAttachShader; typedef GLenum (GL_APIENTRY *PFN_glGetError)(); static PFN_glGetError _sapp_glGetError; typedef void (GL_APIENTRY *PFN_glClearColor)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); static PFN_glClearColor _sapp_glClearColor; typedef void (GL_APIENTRY *PFN_glBlendColor)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); static PFN_glBlendColor _sapp_glBlendColor; typedef void (GL_APIENTRY *PFN_glTexParameterf)(GLenum target, GLenum pname, GLfloat param); static PFN_glTexParameterf _sapp_glTexParameterf; typedef void (GL_APIENTRY *PFN_glGetShaderInfoLog)(GLuint shader, GLsizei bufSize, GLsizei * length, GLchar * infoLog); static PFN_glGetShaderInfoLog _sapp_glGetShaderInfoLog; typedef void (GL_APIENTRY *PFN_glDepthFunc)(GLenum func); static PFN_glDepthFunc _sapp_glDepthFunc; typedef void (GL_APIENTRY *PFN_glStencilOp)(GLenum fail, GLenum zfail, GLenum zpass); static PFN_glStencilOp _sapp_glStencilOp; typedef void (GL_APIENTRY *PFN_glStencilFunc)(GLenum func, GLint ref, GLuint mask); static PFN_glStencilFunc _sapp_glStencilFunc; typedef void (GL_APIENTRY *PFN_glEnableVertexAttribArray)(GLuint index); static PFN_glEnableVertexAttribArray _sapp_glEnableVertexAttribArray; typedef void (GL_APIENTRY *PFN_glBlendFunc)(GLenum sfactor, GLenum dfactor); static PFN_glBlendFunc _sapp_glBlendFunc; typedef void (GL_APIENTRY *PFN_glUniform1fv)(GLint location, GLsizei count, const GLfloat * value); static PFN_glUniform1fv _sapp_glUniform1fv; typedef void (GL_APIENTRY *PFN_glReadBuffer)(GLenum src); static PFN_glReadBuffer _sapp_glReadBuffer; typedef void (GL_APIENTRY *PFN_glClear)(GLbitfield mask); static PFN_glClear _sapp_glClear; typedef void (GL_APIENTRY *PFN_glTexImage2D)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void * pixels); static PFN_glTexImage2D _sapp_glTexImage2D; typedef void (GL_APIENTRY *PFN_glGenVertexArrays)(GLsizei n, GLuint * arrays); static PFN_glGenVertexArrays _sapp_glGenVertexArrays; typedef void (GL_APIENTRY *PFN_glFrontFace)(GLenum mode); static PFN_glFrontFace _sapp_glFrontFace; typedef void (GL_APIENTRY *PFN_glCullFace)(GLenum mode); static PFN_glCullFace _sapp_glCullFace; _SOKOL_PRIVATE void* _sapp_win32_glgetprocaddr(const char* name) { void* proc_addr = (void*) _sapp_wglGetProcAddress(name); if (0 == proc_addr) { proc_addr = (void*) GetProcAddress(_sapp_opengl32, name); } SOKOL_ASSERT(proc_addr); return proc_addr; } #define _SAPP_GLPROC(name) _sapp_ ## name = (PFN_ ## name) _sapp_win32_glgetprocaddr(#name) _SOKOL_PRIVATE void _sapp_win32_gl_loadfuncs(void) { SOKOL_ASSERT(_sapp_wglGetProcAddress); SOKOL_ASSERT(_sapp_opengl32); _SAPP_GLPROC(glBindVertexArray); _SAPP_GLPROC(glFramebufferTextureLayer); _SAPP_GLPROC(glGenFramebuffers); _SAPP_GLPROC(glBindFramebuffer); _SAPP_GLPROC(glBindRenderbuffer); _SAPP_GLPROC(glGetStringi); _SAPP_GLPROC(glClearBufferfi); _SAPP_GLPROC(glClearBufferfv); _SAPP_GLPROC(glClearBufferuiv); _SAPP_GLPROC(glDeleteRenderbuffers); _SAPP_GLPROC(glUniform4fv); _SAPP_GLPROC(glUniform2fv); _SAPP_GLPROC(glUseProgram); _SAPP_GLPROC(glShaderSource); _SAPP_GLPROC(glLinkProgram); _SAPP_GLPROC(glGetUniformLocation); _SAPP_GLPROC(glGetShaderiv); _SAPP_GLPROC(glGetProgramInfoLog); _SAPP_GLPROC(glGetAttribLocation); _SAPP_GLPROC(glDisableVertexAttribArray); _SAPP_GLPROC(glDeleteShader); _SAPP_GLPROC(glDeleteProgram); _SAPP_GLPROC(glCompileShader); _SAPP_GLPROC(glStencilFuncSeparate); _SAPP_GLPROC(glStencilOpSeparate); _SAPP_GLPROC(glRenderbufferStorageMultisample); _SAPP_GLPROC(glDrawBuffers); _SAPP_GLPROC(glVertexAttribDivisor); _SAPP_GLPROC(glBufferSubData); _SAPP_GLPROC(glGenBuffers); _SAPP_GLPROC(glCheckFramebufferStatus); _SAPP_GLPROC(glFramebufferRenderbuffer); _SAPP_GLPROC(glCompressedTexImage2D); _SAPP_GLPROC(glCompressedTexImage3D); _SAPP_GLPROC(glActiveTexture); _SAPP_GLPROC(glTexSubImage3D); _SAPP_GLPROC(glUniformMatrix4fv); _SAPP_GLPROC(glRenderbufferStorage); _SAPP_GLPROC(glGenTextures); _SAPP_GLPROC(glPolygonOffset); _SAPP_GLPROC(glDrawElements); _SAPP_GLPROC(glDeleteFramebuffers); _SAPP_GLPROC(glBlendEquationSeparate); _SAPP_GLPROC(glDeleteTextures); _SAPP_GLPROC(glGetProgramiv); _SAPP_GLPROC(glBindTexture); _SAPP_GLPROC(glTexImage3D); _SAPP_GLPROC(glCreateShader); _SAPP_GLPROC(glTexSubImage2D); _SAPP_GLPROC(glClearDepth); _SAPP_GLPROC(glFramebufferTexture2D); _SAPP_GLPROC(glCreateProgram); _SAPP_GLPROC(glViewport); _SAPP_GLPROC(glDeleteBuffers); _SAPP_GLPROC(glDrawArrays); _SAPP_GLPROC(glDrawElementsInstanced); _SAPP_GLPROC(glVertexAttribPointer); _SAPP_GLPROC(glUniform1i); _SAPP_GLPROC(glDisable); _SAPP_GLPROC(glColorMask); _SAPP_GLPROC(glBindBuffer); _SAPP_GLPROC(glDeleteVertexArrays); _SAPP_GLPROC(glDepthMask); _SAPP_GLPROC(glDrawArraysInstanced); _SAPP_GLPROC(glClearStencil); _SAPP_GLPROC(glScissor); _SAPP_GLPROC(glUniform3fv); _SAPP_GLPROC(glGenRenderbuffers); _SAPP_GLPROC(glBufferData); _SAPP_GLPROC(glBlendFuncSeparate); _SAPP_GLPROC(glTexParameteri); _SAPP_GLPROC(glGetIntegerv); _SAPP_GLPROC(glEnable); _SAPP_GLPROC(glBlitFramebuffer); _SAPP_GLPROC(glStencilMask); _SAPP_GLPROC(glAttachShader); _SAPP_GLPROC(glGetError); _SAPP_GLPROC(glClearColor); _SAPP_GLPROC(glBlendColor); _SAPP_GLPROC(glTexParameterf); _SAPP_GLPROC(glGetShaderInfoLog); _SAPP_GLPROC(glDepthFunc); _SAPP_GLPROC(glStencilOp); _SAPP_GLPROC(glStencilFunc); _SAPP_GLPROC(glEnableVertexAttribArray); _SAPP_GLPROC(glBlendFunc); _SAPP_GLPROC(glUniform1fv); _SAPP_GLPROC(glReadBuffer); _SAPP_GLPROC(glClear); _SAPP_GLPROC(glTexImage2D); _SAPP_GLPROC(glGenVertexArrays); _SAPP_GLPROC(glFrontFace); _SAPP_GLPROC(glCullFace); } #define glBindVertexArray _sapp_glBindVertexArray #define glFramebufferTextureLayer _sapp_glFramebufferTextureLayer #define glGenFramebuffers _sapp_glGenFramebuffers #define glBindFramebuffer _sapp_glBindFramebuffer #define glBindRenderbuffer _sapp_glBindRenderbuffer #define glGetStringi _sapp_glGetStringi #define glClearBufferfi _sapp_glClearBufferfi #define glClearBufferfv _sapp_glClearBufferfv #define glClearBufferuiv _sapp_glClearBufferuiv #define glDeleteRenderbuffers _sapp_glDeleteRenderbuffers #define glUniform4fv _sapp_glUniform4fv #define glUniform2fv _sapp_glUniform2fv #define glUseProgram _sapp_glUseProgram #define glShaderSource _sapp_glShaderSource #define glLinkProgram _sapp_glLinkProgram #define glGetUniformLocation _sapp_glGetUniformLocation #define glGetShaderiv _sapp_glGetShaderiv #define glGetProgramInfoLog _sapp_glGetProgramInfoLog #define glGetAttribLocation _sapp_glGetAttribLocation #define glDisableVertexAttribArray _sapp_glDisableVertexAttribArray #define glDeleteShader _sapp_glDeleteShader #define glDeleteProgram _sapp_glDeleteProgram #define glCompileShader _sapp_glCompileShader #define glStencilFuncSeparate _sapp_glStencilFuncSeparate #define glStencilOpSeparate _sapp_glStencilOpSeparate #define glRenderbufferStorageMultisample _sapp_glRenderbufferStorageMultisample #define glDrawBuffers _sapp_glDrawBuffers #define glVertexAttribDivisor _sapp_glVertexAttribDivisor #define glBufferSubData _sapp_glBufferSubData #define glGenBuffers _sapp_glGenBuffers #define glCheckFramebufferStatus _sapp_glCheckFramebufferStatus #define glFramebufferRenderbuffer _sapp_glFramebufferRenderbuffer #define glCompressedTexImage2D _sapp_glCompressedTexImage2D #define glCompressedTexImage3D _sapp_glCompressedTexImage3D #define glActiveTexture _sapp_glActiveTexture #define glTexSubImage3D _sapp_glTexSubImage3D #define glUniformMatrix4fv _sapp_glUniformMatrix4fv #define glRenderbufferStorage _sapp_glRenderbufferStorage #define glGenTextures _sapp_glGenTextures #define glPolygonOffset _sapp_glPolygonOffset #define glDrawElements _sapp_glDrawElements #define glDeleteFramebuffers _sapp_glDeleteFramebuffers #define glBlendEquationSeparate _sapp_glBlendEquationSeparate #define glDeleteTextures _sapp_glDeleteTextures #define glGetProgramiv _sapp_glGetProgramiv #define glBindTexture _sapp_glBindTexture #define glTexImage3D _sapp_glTexImage3D #define glCreateShader _sapp_glCreateShader #define glTexSubImage2D _sapp_glTexSubImage2D #define glClearDepth _sapp_glClearDepth #define glFramebufferTexture2D _sapp_glFramebufferTexture2D #define glCreateProgram _sapp_glCreateProgram #define glViewport _sapp_glViewport #define glDeleteBuffers _sapp_glDeleteBuffers #define glDrawArrays _sapp_glDrawArrays #define glDrawElementsInstanced _sapp_glDrawElementsInstanced #define glVertexAttribPointer _sapp_glVertexAttribPointer #define glUniform1i _sapp_glUniform1i #define glDisable _sapp_glDisable #define glColorMask _sapp_glColorMask #define glBindBuffer _sapp_glBindBuffer #define glDeleteVertexArrays _sapp_glDeleteVertexArrays #define glDepthMask _sapp_glDepthMask #define glDrawArraysInstanced _sapp_glDrawArraysInstanced #define glClearStencil _sapp_glClearStencil #define glScissor _sapp_glScissor #define glUniform3fv _sapp_glUniform3fv #define glGenRenderbuffers _sapp_glGenRenderbuffers #define glBufferData _sapp_glBufferData #define glBlendFuncSeparate _sapp_glBlendFuncSeparate #define glTexParameteri _sapp_glTexParameteri #define glGetIntegerv _sapp_glGetIntegerv #define glEnable _sapp_glEnable #define glBlitFramebuffer _sapp_glBlitFramebuffer #define glStencilMask _sapp_glStencilMask #define glAttachShader _sapp_glAttachShader #define glGetError _sapp_glGetError #define glClearColor _sapp_glClearColor #define glBlendColor _sapp_glBlendColor #define glTexParameterf _sapp_glTexParameterf #define glGetShaderInfoLog _sapp_glGetShaderInfoLog #define glDepthFunc _sapp_glDepthFunc #define glStencilOp _sapp_glStencilOp #define glStencilFunc _sapp_glStencilFunc #define glEnableVertexAttribArray _sapp_glEnableVertexAttribArray #define glBlendFunc _sapp_glBlendFunc #define glUniform1fv _sapp_glUniform1fv #define glReadBuffer _sapp_glReadBuffer #define glClear _sapp_glClear #define glTexImage2D _sapp_glTexImage2D #define glGenVertexArrays _sapp_glGenVertexArrays #define glFrontFace _sapp_glFrontFace #define glCullFace _sapp_glCullFace #endif /* SOKOL_WIN32_NO_GL_LOADER */ #endif /* SOKOL_GLCORE33 */ #if defined(SOKOL_D3D11) #define _SAPP_SAFE_RELEASE(class, obj) if (obj) { class##_Release(obj); obj=0; } _SOKOL_PRIVATE void _sapp_d3d11_create_device_and_swapchain(void) { DXGI_SWAP_CHAIN_DESC* sc_desc = &_sapp_dxgi_swap_chain_desc; sc_desc->BufferDesc.Width = _sapp.framebuffer_width; sc_desc->BufferDesc.Height = _sapp.framebuffer_height; sc_desc->BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; sc_desc->BufferDesc.RefreshRate.Numerator = 60; sc_desc->BufferDesc.RefreshRate.Denominator = 1; sc_desc->OutputWindow = _sapp_win32_hwnd; sc_desc->Windowed = true; sc_desc->SwapEffect = DXGI_SWAP_EFFECT_DISCARD; sc_desc->BufferCount = 1; sc_desc->SampleDesc.Count = _sapp.sample_count; sc_desc->SampleDesc.Quality = _sapp.sample_count > 1 ? D3D11_STANDARD_MULTISAMPLE_PATTERN : 0; sc_desc->BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; int create_flags = D3D11_CREATE_DEVICE_SINGLETHREADED; #if defined(SOKOL_DEBUG) create_flags |= D3D11_CREATE_DEVICE_DEBUG; #endif D3D_FEATURE_LEVEL feature_level; HRESULT hr = D3D11CreateDeviceAndSwapChain( NULL, /* pAdapter (use default) */ D3D_DRIVER_TYPE_HARDWARE, /* DriverType */ NULL, /* Software */ create_flags, /* Flags */ NULL, /* pFeatureLevels */ 0, /* FeatureLevels */ D3D11_SDK_VERSION, /* SDKVersion */ sc_desc, /* pSwapChainDesc */ &_sapp_dxgi_swap_chain, /* ppSwapChain */ &_sapp_d3d11_device, /* ppDevice */ &feature_level, /* pFeatureLevel */ &_sapp_d3d11_device_context); /* ppImmediateContext */ _SOKOL_UNUSED(hr); SOKOL_ASSERT(SUCCEEDED(hr) && _sapp_dxgi_swap_chain && _sapp_d3d11_device && _sapp_d3d11_device_context); } _SOKOL_PRIVATE void _sapp_d3d11_destroy_device_and_swapchain(void) { _SAPP_SAFE_RELEASE(IDXGISwapChain, _sapp_dxgi_swap_chain); _SAPP_SAFE_RELEASE(ID3D11DeviceContext, _sapp_d3d11_device_context); _SAPP_SAFE_RELEASE(ID3D11Device, _sapp_d3d11_device); } _SOKOL_PRIVATE void _sapp_d3d11_create_default_render_target(void) { HRESULT hr; #ifdef __cplusplus hr = IDXGISwapChain_GetBuffer(_sapp_dxgi_swap_chain, 0, IID_ID3D11Texture2D, (void**)&_sapp_d3d11_rt); #else hr = IDXGISwapChain_GetBuffer(_sapp_dxgi_swap_chain, 0, &IID_ID3D11Texture2D, (void**)&_sapp_d3d11_rt); #endif SOKOL_ASSERT(SUCCEEDED(hr) && _sapp_d3d11_rt); hr = ID3D11Device_CreateRenderTargetView(_sapp_d3d11_device, (ID3D11Resource*)_sapp_d3d11_rt, NULL, &_sapp_d3d11_rtv); SOKOL_ASSERT(SUCCEEDED(hr) && _sapp_d3d11_rtv); D3D11_TEXTURE2D_DESC ds_desc; memset(&ds_desc, 0, sizeof(ds_desc)); ds_desc.Width = _sapp.framebuffer_width; ds_desc.Height = _sapp.framebuffer_height; ds_desc.MipLevels = 1; ds_desc.ArraySize = 1; ds_desc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT; ds_desc.SampleDesc = _sapp_dxgi_swap_chain_desc.SampleDesc; ds_desc.Usage = D3D11_USAGE_DEFAULT; ds_desc.BindFlags = D3D11_BIND_DEPTH_STENCIL; hr = ID3D11Device_CreateTexture2D(_sapp_d3d11_device, &ds_desc, NULL, &_sapp_d3d11_ds); SOKOL_ASSERT(SUCCEEDED(hr) && _sapp_d3d11_ds); D3D11_DEPTH_STENCIL_VIEW_DESC dsv_desc; memset(&dsv_desc, 0, sizeof(dsv_desc)); dsv_desc.Format = ds_desc.Format; dsv_desc.ViewDimension = _sapp.sample_count > 1 ? D3D11_DSV_DIMENSION_TEXTURE2DMS : D3D11_DSV_DIMENSION_TEXTURE2D; hr = ID3D11Device_CreateDepthStencilView(_sapp_d3d11_device, (ID3D11Resource*)_sapp_d3d11_ds, &dsv_desc, &_sapp_d3d11_dsv); SOKOL_ASSERT(SUCCEEDED(hr) && _sapp_d3d11_dsv); } _SOKOL_PRIVATE void _sapp_d3d11_destroy_default_render_target(void) { _SAPP_SAFE_RELEASE(ID3D11Texture2D, _sapp_d3d11_rt); _SAPP_SAFE_RELEASE(ID3D11RenderTargetView, _sapp_d3d11_rtv); _SAPP_SAFE_RELEASE(ID3D11Texture2D, _sapp_d3d11_ds); _SAPP_SAFE_RELEASE(ID3D11DepthStencilView, _sapp_d3d11_dsv); } _SOKOL_PRIVATE void _sapp_d3d11_resize_default_render_target(void) { if (_sapp_dxgi_swap_chain) { _sapp_d3d11_destroy_default_render_target(); IDXGISwapChain_ResizeBuffers(_sapp_dxgi_swap_chain, 1, _sapp.framebuffer_width, _sapp.framebuffer_height, DXGI_FORMAT_R8G8B8A8_UNORM, 0); _sapp_d3d11_create_default_render_target(); } } #endif #if defined(SOKOL_GLCORE33) _SOKOL_PRIVATE void _sapp_wgl_init(void) { _sapp_opengl32 = LoadLibraryA("opengl32.dll"); if (!_sapp_opengl32) { _sapp_fail("Failed to load opengl32.dll\n"); } SOKOL_ASSERT(_sapp_opengl32); _sapp_wglCreateContext = (PFN_wglCreateContext) GetProcAddress(_sapp_opengl32, "wglCreateContext"); SOKOL_ASSERT(_sapp_wglCreateContext); _sapp_wglDeleteContext = (PFN_wglDeleteContext) GetProcAddress(_sapp_opengl32, "wglDeleteContext"); SOKOL_ASSERT(_sapp_wglDeleteContext); _sapp_wglGetProcAddress = (PFN_wglGetProcAddress) GetProcAddress(_sapp_opengl32, "wglGetProcAddress"); SOKOL_ASSERT(_sapp_wglGetProcAddress); _sapp_wglGetCurrentDC = (PFN_wglGetCurrentDC) GetProcAddress(_sapp_opengl32, "wglGetCurrentDC"); SOKOL_ASSERT(_sapp_wglGetCurrentDC); _sapp_wglMakeCurrent = (PFN_wglMakeCurrent) GetProcAddress(_sapp_opengl32, "wglMakeCurrent"); SOKOL_ASSERT(_sapp_wglMakeCurrent); _sapp_win32_msg_hwnd = CreateWindowExW(WS_EX_OVERLAPPEDWINDOW, L"SOKOLAPP", L"sokol-app message window", WS_CLIPSIBLINGS|WS_CLIPCHILDREN, 0, 0, 1, 1, NULL, NULL, GetModuleHandleW(NULL), NULL); if (!_sapp_win32_msg_hwnd) { _sapp_fail("Win32: failed to create helper window!\n"); } ShowWindow(_sapp_win32_msg_hwnd, SW_HIDE); MSG msg; while (PeekMessageW(&msg, _sapp_win32_msg_hwnd, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessageW(&msg); } _sapp_win32_msg_dc = GetDC(_sapp_win32_msg_hwnd); if (!_sapp_win32_msg_dc) { _sapp_fail("Win32: failed to obtain helper window DC!\n"); } } _SOKOL_PRIVATE void _sapp_wgl_shutdown(void) { SOKOL_ASSERT(_sapp_opengl32 && _sapp_win32_msg_hwnd); DestroyWindow(_sapp_win32_msg_hwnd); _sapp_win32_msg_hwnd = 0; FreeLibrary(_sapp_opengl32); _sapp_opengl32 = 0; } _SOKOL_PRIVATE bool _sapp_wgl_has_ext(const char* ext, const char* extensions) { SOKOL_ASSERT(ext && extensions); const char* start = extensions; while (true) { const char* where = strstr(start, ext); if (!where) { return false; } const char* terminator = where + strlen(ext); if ((where == start) || (*(where - 1) == ' ')) { if (*terminator == ' ' || *terminator == '\0') { break; } } start = terminator; } return true; } _SOKOL_PRIVATE bool _sapp_wgl_ext_supported(const char* ext) { SOKOL_ASSERT(ext); if (_sapp_GetExtensionsStringEXT) { const char* extensions = _sapp_GetExtensionsStringEXT(); if (extensions) { if (_sapp_wgl_has_ext(ext, extensions)) { return true; } } } if (_sapp_GetExtensionsStringARB) { const char* extensions = _sapp_GetExtensionsStringARB(_sapp_wglGetCurrentDC()); if (extensions) { if (_sapp_wgl_has_ext(ext, extensions)) { return true; } } } return false; } _SOKOL_PRIVATE void _sapp_wgl_load_extensions(void) { SOKOL_ASSERT(_sapp_win32_msg_dc); PIXELFORMATDESCRIPTOR pfd; memset(&pfd, 0, sizeof(pfd)); pfd.nSize = sizeof(pfd); pfd.nVersion = 1; pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; pfd.iPixelType = PFD_TYPE_RGBA; pfd.cColorBits = 24; if (!SetPixelFormat(_sapp_win32_msg_dc, ChoosePixelFormat(_sapp_win32_msg_dc, &pfd), &pfd)) { _sapp_fail("WGL: failed to set pixel format for dummy context\n"); } HGLRC rc = _sapp_wglCreateContext(_sapp_win32_msg_dc); if (!rc) { _sapp_fail("WGL: Failed to create dummy context\n"); } if (!_sapp_wglMakeCurrent(_sapp_win32_msg_dc, rc)) { _sapp_fail("WGL: Failed to make context current\n"); } _sapp_GetExtensionsStringEXT = (PFNWGLGETEXTENSIONSSTRINGEXTPROC) _sapp_wglGetProcAddress("wglGetExtensionsStringEXT"); _sapp_GetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC) _sapp_wglGetProcAddress("wglGetExtensionsStringARB"); _sapp_CreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC) _sapp_wglGetProcAddress("wglCreateContextAttribsARB"); _sapp_SwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC) _sapp_wglGetProcAddress("wglSwapIntervalEXT"); _sapp_GetPixelFormatAttribivARB = (PFNWGLGETPIXELFORMATATTRIBIVARBPROC) _sapp_wglGetProcAddress("wglGetPixelFormatAttribivARB"); _sapp_arb_multisample = _sapp_wgl_ext_supported("WGL_ARB_multisample"); _sapp_arb_create_context = _sapp_wgl_ext_supported("WGL_ARB_create_context"); _sapp_arb_create_context_profile = _sapp_wgl_ext_supported("WGL_ARB_create_context_profile"); _sapp_ext_swap_control = _sapp_wgl_ext_supported("WGL_EXT_swap_control"); _sapp_arb_pixel_format = _sapp_wgl_ext_supported("WGL_ARB_pixel_format"); _sapp_wglMakeCurrent(_sapp_win32_msg_dc, 0); _sapp_wglDeleteContext(rc); } _SOKOL_PRIVATE int _sapp_wgl_attrib(int pixel_format, int attrib) { SOKOL_ASSERT(_sapp_arb_pixel_format); int value = 0; if (!_sapp_GetPixelFormatAttribivARB(_sapp_win32_dc, pixel_format, 0, 1, &attrib, &value)) { _sapp_fail("WGL: Failed to retrieve pixel format attribute\n"); } return value; } _SOKOL_PRIVATE int _sapp_wgl_find_pixel_format(void) { SOKOL_ASSERT(_sapp_win32_dc); SOKOL_ASSERT(_sapp_arb_pixel_format); const _sapp_gl_fbconfig* closest; int native_count = _sapp_wgl_attrib(1, WGL_NUMBER_PIXEL_FORMATS_ARB); _sapp_gl_fbconfig* usable_configs = (_sapp_gl_fbconfig*) SOKOL_CALLOC(native_count, sizeof(_sapp_gl_fbconfig)); int usable_count = 0; for (int i = 0; i < native_count; i++) { const int n = i + 1; _sapp_gl_fbconfig* u = usable_configs + usable_count; _sapp_gl_init_fbconfig(u); if (!_sapp_wgl_attrib(n, WGL_SUPPORT_OPENGL_ARB) || !_sapp_wgl_attrib(n, WGL_DRAW_TO_WINDOW_ARB)) { continue; } if (_sapp_wgl_attrib(n, WGL_PIXEL_TYPE_ARB) != WGL_TYPE_RGBA_ARB) { continue; } if (_sapp_wgl_attrib(n, WGL_ACCELERATION_ARB) == WGL_NO_ACCELERATION_ARB) { continue; } u->red_bits = _sapp_wgl_attrib(n, WGL_RED_BITS_ARB); u->green_bits = _sapp_wgl_attrib(n, WGL_GREEN_BITS_ARB); u->blue_bits = _sapp_wgl_attrib(n, WGL_BLUE_BITS_ARB); u->alpha_bits = _sapp_wgl_attrib(n, WGL_ALPHA_BITS_ARB); u->depth_bits = _sapp_wgl_attrib(n, WGL_DEPTH_BITS_ARB); u->stencil_bits = _sapp_wgl_attrib(n, WGL_STENCIL_BITS_ARB); if (_sapp_wgl_attrib(n, WGL_DOUBLE_BUFFER_ARB)) { u->doublebuffer = true; } if (_sapp_arb_multisample) { u->samples = _sapp_wgl_attrib(n, WGL_SAMPLES_ARB); } u->handle = n; usable_count++; } SOKOL_ASSERT(usable_count > 0); _sapp_gl_fbconfig desired; _sapp_gl_init_fbconfig(&desired); desired.red_bits = 8; desired.green_bits = 8; desired.blue_bits = 8; desired.alpha_bits = 8; desired.depth_bits = 24; desired.stencil_bits = 8; desired.doublebuffer = true; desired.samples = _sapp.sample_count > 1 ? _sapp.sample_count : 0; closest = _sapp_gl_choose_fbconfig(&desired, usable_configs, usable_count); int pixel_format = 0; if (closest) { pixel_format = (int) closest->handle; } SOKOL_FREE(usable_configs); return pixel_format; } _SOKOL_PRIVATE void _sapp_wgl_create_context(void) { int pixel_format = _sapp_wgl_find_pixel_format(); if (0 == pixel_format) { _sapp_fail("WGL: Didn't find matching pixel format.\n"); } PIXELFORMATDESCRIPTOR pfd; if (!DescribePixelFormat(_sapp_win32_dc, pixel_format, sizeof(pfd), &pfd)) { _sapp_fail("WGL: Failed to retrieve PFD for selected pixel format!\n"); } if (!SetPixelFormat(_sapp_win32_dc, pixel_format, &pfd)) { _sapp_fail("WGL: Failed to set selected pixel format!\n"); } if (!_sapp_arb_create_context) { _sapp_fail("WGL: ARB_create_context required!\n"); } if (!_sapp_arb_create_context_profile) { _sapp_fail("WGL: ARB_create_context_profile required!\n"); } const int attrs[] = { WGL_CONTEXT_MAJOR_VERSION_ARB, 3, WGL_CONTEXT_MINOR_VERSION_ARB, 3, WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB, 0, 0 }; _sapp_gl_ctx = _sapp_CreateContextAttribsARB(_sapp_win32_dc, 0, attrs); if (!_sapp_gl_ctx) { const DWORD err = GetLastError(); if (err == (0xc0070000 | ERROR_INVALID_VERSION_ARB)) { _sapp_fail("WGL: Driver does not support OpenGL version 3.3\n"); } else if (err == (0xc0070000 | ERROR_INVALID_PROFILE_ARB)) { _sapp_fail("WGL: Driver does not support the requested OpenGL profile"); } else if (err == (0xc0070000 | ERROR_INCOMPATIBLE_DEVICE_CONTEXTS_ARB)) { _sapp_fail("WGL: The share context is not compatible with the requested context"); } else { _sapp_fail("WGL: Failed to create OpenGL context"); } } _sapp_wglMakeCurrent(_sapp_win32_dc, _sapp_gl_ctx); if (_sapp_ext_swap_control) { /* FIXME: DwmIsCompositionEnabled() (see GLFW) */ _sapp_SwapIntervalEXT(_sapp.swap_interval); } } _SOKOL_PRIVATE void _sapp_wgl_destroy_context(void) { SOKOL_ASSERT(_sapp_gl_ctx); _sapp_wglDeleteContext(_sapp_gl_ctx); _sapp_gl_ctx = 0; } _SOKOL_PRIVATE void _sapp_wgl_swap_buffers(void) { SOKOL_ASSERT(_sapp_win32_dc); /* FIXME: DwmIsCompositionEnabled? (see GLFW) */ SwapBuffers(_sapp_win32_dc); } #endif _SOKOL_PRIVATE bool _sapp_win32_utf8_to_wide(const char* src, wchar_t* dst, int dst_num_bytes) { SOKOL_ASSERT(src && dst && (dst_num_bytes > 1)); memset(dst, 0, dst_num_bytes); const int dst_chars = dst_num_bytes / sizeof(wchar_t); const int dst_needed = MultiByteToWideChar(CP_UTF8, 0, src, -1, 0, 0); if ((dst_needed > 0) && (dst_needed < dst_chars)) { MultiByteToWideChar(CP_UTF8, 0, src, -1, dst, dst_chars); return true; } else { /* input string doesn't fit into destination buffer */ return false; } } _SOKOL_PRIVATE void _sapp_win32_init_keytable(void) { /* same as GLFW */ _sapp.keycodes[0x00B] = SAPP_KEYCODE_0; _sapp.keycodes[0x002] = SAPP_KEYCODE_1; _sapp.keycodes[0x003] = SAPP_KEYCODE_2; _sapp.keycodes[0x004] = SAPP_KEYCODE_3; _sapp.keycodes[0x005] = SAPP_KEYCODE_4; _sapp.keycodes[0x006] = SAPP_KEYCODE_5; _sapp.keycodes[0x007] = SAPP_KEYCODE_6; _sapp.keycodes[0x008] = SAPP_KEYCODE_7; _sapp.keycodes[0x009] = SAPP_KEYCODE_8; _sapp.keycodes[0x00A] = SAPP_KEYCODE_9; _sapp.keycodes[0x01E] = SAPP_KEYCODE_A; _sapp.keycodes[0x030] = SAPP_KEYCODE_B; _sapp.keycodes[0x02E] = SAPP_KEYCODE_C; _sapp.keycodes[0x020] = SAPP_KEYCODE_D; _sapp.keycodes[0x012] = SAPP_KEYCODE_E; _sapp.keycodes[0x021] = SAPP_KEYCODE_F; _sapp.keycodes[0x022] = SAPP_KEYCODE_G; _sapp.keycodes[0x023] = SAPP_KEYCODE_H; _sapp.keycodes[0x017] = SAPP_KEYCODE_I; _sapp.keycodes[0x024] = SAPP_KEYCODE_J; _sapp.keycodes[0x025] = SAPP_KEYCODE_K; _sapp.keycodes[0x026] = SAPP_KEYCODE_L; _sapp.keycodes[0x032] = SAPP_KEYCODE_M; _sapp.keycodes[0x031] = SAPP_KEYCODE_N; _sapp.keycodes[0x018] = SAPP_KEYCODE_O; _sapp.keycodes[0x019] = SAPP_KEYCODE_P; _sapp.keycodes[0x010] = SAPP_KEYCODE_Q; _sapp.keycodes[0x013] = SAPP_KEYCODE_R; _sapp.keycodes[0x01F] = SAPP_KEYCODE_S; _sapp.keycodes[0x014] = SAPP_KEYCODE_T; _sapp.keycodes[0x016] = SAPP_KEYCODE_U; _sapp.keycodes[0x02F] = SAPP_KEYCODE_V; _sapp.keycodes[0x011] = SAPP_KEYCODE_W; _sapp.keycodes[0x02D] = SAPP_KEYCODE_X; _sapp.keycodes[0x015] = SAPP_KEYCODE_Y; _sapp.keycodes[0x02C] = SAPP_KEYCODE_Z; _sapp.keycodes[0x028] = SAPP_KEYCODE_APOSTROPHE; _sapp.keycodes[0x02B] = SAPP_KEYCODE_BACKSLASH; _sapp.keycodes[0x033] = SAPP_KEYCODE_COMMA; _sapp.keycodes[0x00D] = SAPP_KEYCODE_EQUAL; _sapp.keycodes[0x029] = SAPP_KEYCODE_GRAVE_ACCENT; _sapp.keycodes[0x01A] = SAPP_KEYCODE_LEFT_BRACKET; _sapp.keycodes[0x00C] = SAPP_KEYCODE_MINUS; _sapp.keycodes[0x034] = SAPP_KEYCODE_PERIOD; _sapp.keycodes[0x01B] = SAPP_KEYCODE_RIGHT_BRACKET; _sapp.keycodes[0x027] = SAPP_KEYCODE_SEMICOLON; _sapp.keycodes[0x035] = SAPP_KEYCODE_SLASH; _sapp.keycodes[0x056] = SAPP_KEYCODE_WORLD_2; _sapp.keycodes[0x00E] = SAPP_KEYCODE_BACKSPACE; _sapp.keycodes[0x153] = SAPP_KEYCODE_DELETE; _sapp.keycodes[0x14F] = SAPP_KEYCODE_END; _sapp.keycodes[0x01C] = SAPP_KEYCODE_ENTER; _sapp.keycodes[0x001] = SAPP_KEYCODE_ESCAPE; _sapp.keycodes[0x147] = SAPP_KEYCODE_HOME; _sapp.keycodes[0x152] = SAPP_KEYCODE_INSERT; _sapp.keycodes[0x15D] = SAPP_KEYCODE_MENU; _sapp.keycodes[0x151] = SAPP_KEYCODE_PAGE_DOWN; _sapp.keycodes[0x149] = SAPP_KEYCODE_PAGE_UP; _sapp.keycodes[0x045] = SAPP_KEYCODE_PAUSE; _sapp.keycodes[0x146] = SAPP_KEYCODE_PAUSE; _sapp.keycodes[0x039] = SAPP_KEYCODE_SPACE; _sapp.keycodes[0x00F] = SAPP_KEYCODE_TAB; _sapp.keycodes[0x03A] = SAPP_KEYCODE_CAPS_LOCK; _sapp.keycodes[0x145] = SAPP_KEYCODE_NUM_LOCK; _sapp.keycodes[0x046] = SAPP_KEYCODE_SCROLL_LOCK; _sapp.keycodes[0x03B] = SAPP_KEYCODE_F1; _sapp.keycodes[0x03C] = SAPP_KEYCODE_F2; _sapp.keycodes[0x03D] = SAPP_KEYCODE_F3; _sapp.keycodes[0x03E] = SAPP_KEYCODE_F4; _sapp.keycodes[0x03F] = SAPP_KEYCODE_F5; _sapp.keycodes[0x040] = SAPP_KEYCODE_F6; _sapp.keycodes[0x041] = SAPP_KEYCODE_F7; _sapp.keycodes[0x042] = SAPP_KEYCODE_F8; _sapp.keycodes[0x043] = SAPP_KEYCODE_F9; _sapp.keycodes[0x044] = SAPP_KEYCODE_F10; _sapp.keycodes[0x057] = SAPP_KEYCODE_F11; _sapp.keycodes[0x058] = SAPP_KEYCODE_F12; _sapp.keycodes[0x064] = SAPP_KEYCODE_F13; _sapp.keycodes[0x065] = SAPP_KEYCODE_F14; _sapp.keycodes[0x066] = SAPP_KEYCODE_F15; _sapp.keycodes[0x067] = SAPP_KEYCODE_F16; _sapp.keycodes[0x068] = SAPP_KEYCODE_F17; _sapp.keycodes[0x069] = SAPP_KEYCODE_F18; _sapp.keycodes[0x06A] = SAPP_KEYCODE_F19; _sapp.keycodes[0x06B] = SAPP_KEYCODE_F20; _sapp.keycodes[0x06C] = SAPP_KEYCODE_F21; _sapp.keycodes[0x06D] = SAPP_KEYCODE_F22; _sapp.keycodes[0x06E] = SAPP_KEYCODE_F23; _sapp.keycodes[0x076] = SAPP_KEYCODE_F24; _sapp.keycodes[0x038] = SAPP_KEYCODE_LEFT_ALT; _sapp.keycodes[0x01D] = SAPP_KEYCODE_LEFT_CONTROL; _sapp.keycodes[0x02A] = SAPP_KEYCODE_LEFT_SHIFT; _sapp.keycodes[0x15B] = SAPP_KEYCODE_LEFT_SUPER; _sapp.keycodes[0x137] = SAPP_KEYCODE_PRINT_SCREEN; _sapp.keycodes[0x138] = SAPP_KEYCODE_RIGHT_ALT; _sapp.keycodes[0x11D] = SAPP_KEYCODE_RIGHT_CONTROL; _sapp.keycodes[0x036] = SAPP_KEYCODE_RIGHT_SHIFT; _sapp.keycodes[0x15C] = SAPP_KEYCODE_RIGHT_SUPER; _sapp.keycodes[0x150] = SAPP_KEYCODE_DOWN; _sapp.keycodes[0x14B] = SAPP_KEYCODE_LEFT; _sapp.keycodes[0x14D] = SAPP_KEYCODE_RIGHT; _sapp.keycodes[0x148] = SAPP_KEYCODE_UP; _sapp.keycodes[0x052] = SAPP_KEYCODE_KP_0; _sapp.keycodes[0x04F] = SAPP_KEYCODE_KP_1; _sapp.keycodes[0x050] = SAPP_KEYCODE_KP_2; _sapp.keycodes[0x051] = SAPP_KEYCODE_KP_3; _sapp.keycodes[0x04B] = SAPP_KEYCODE_KP_4; _sapp.keycodes[0x04C] = SAPP_KEYCODE_KP_5; _sapp.keycodes[0x04D] = SAPP_KEYCODE_KP_6; _sapp.keycodes[0x047] = SAPP_KEYCODE_KP_7; _sapp.keycodes[0x048] = SAPP_KEYCODE_KP_8; _sapp.keycodes[0x049] = SAPP_KEYCODE_KP_9; _sapp.keycodes[0x04E] = SAPP_KEYCODE_KP_ADD; _sapp.keycodes[0x053] = SAPP_KEYCODE_KP_DECIMAL; _sapp.keycodes[0x135] = SAPP_KEYCODE_KP_DIVIDE; _sapp.keycodes[0x11C] = SAPP_KEYCODE_KP_ENTER; _sapp.keycodes[0x037] = SAPP_KEYCODE_KP_MULTIPLY; _sapp.keycodes[0x04A] = SAPP_KEYCODE_KP_SUBTRACT; } /* updates current window and framebuffer size from the window's client rect, returns true if size has changed */ _SOKOL_PRIVATE bool _sapp_win32_update_dimensions(void) { RECT rect; if (GetClientRect(_sapp_win32_hwnd, &rect)) { const int cur_width = (int)((float)(rect.right - rect.left) / _sapp_win32_window_scale); const int cur_height = (int)((float)(rect.bottom - rect.top) / _sapp_win32_window_scale); if ((cur_width != _sapp.window_width) || (cur_height != _sapp.window_height)) { _sapp.window_width = cur_width; _sapp.window_height = cur_height; } const int fb_width = (int)((float)_sapp.window_width * _sapp_win32_content_scale); const int fb_height = (int)((float)_sapp.window_height * _sapp_win32_content_scale); if ((fb_width != _sapp.framebuffer_width) || (fb_height != _sapp.framebuffer_height)) { _sapp.framebuffer_width = (int)((float)_sapp.window_width * _sapp_win32_content_scale); _sapp.framebuffer_height = (int)((float)_sapp.window_height * _sapp_win32_content_scale); /* prevent a framebuffer size of 0 when window is minimized */ if (_sapp.framebuffer_width == 0) { _sapp.framebuffer_width = 1; } if (_sapp.framebuffer_height == 0) { _sapp.framebuffer_height = 1; } return true; } } else { _sapp.window_width = _sapp.window_height = 1; _sapp.framebuffer_width = _sapp.framebuffer_height = 1; } return false; } _SOKOL_PRIVATE uint32_t _sapp_win32_mods(void) { uint32_t mods = 0; if (GetKeyState(VK_SHIFT) & (1<<31)) { mods |= SAPP_MODIFIER_SHIFT; } if (GetKeyState(VK_CONTROL) & (1<<31)) { mods |= SAPP_MODIFIER_CTRL; } if (GetKeyState(VK_MENU) & (1<<31)) { mods |= SAPP_MODIFIER_ALT; } if ((GetKeyState(VK_LWIN) | GetKeyState(VK_RWIN)) & (1<<31)) { mods |= SAPP_MODIFIER_SUPER; } return mods; } _SOKOL_PRIVATE void _sapp_win32_mouse_event(sapp_event_type type, sapp_mousebutton btn) { if (_sapp_events_enabled()) { _sapp_init_event(type); _sapp.event.modifiers = _sapp_win32_mods(); _sapp.event.mouse_button = btn; _sapp.event.mouse_x = _sapp.mouse_x; _sapp.event.mouse_y = _sapp.mouse_y; _sapp_call_event(&_sapp.event); } } _SOKOL_PRIVATE void _sapp_win32_scroll_event(float x, float y) { if (_sapp_events_enabled()) { _sapp_init_event(SAPP_EVENTTYPE_MOUSE_SCROLL); _sapp.event.modifiers = _sapp_win32_mods(); _sapp.event.scroll_x = -x / 30.0f; _sapp.event.scroll_y = y / 30.0f; _sapp_call_event(&_sapp.event); } } _SOKOL_PRIVATE void _sapp_win32_key_event(sapp_event_type type, int vk, bool repeat) { if (_sapp_events_enabled() && (vk < SAPP_MAX_KEYCODES)) { _sapp_init_event(type); _sapp.event.modifiers = _sapp_win32_mods(); _sapp.event.key_code = _sapp.keycodes[vk]; _sapp.event.key_repeat = repeat; _sapp_call_event(&_sapp.event); } } _SOKOL_PRIVATE void _sapp_win32_char_event(uint32_t c, bool repeat) { if (_sapp_events_enabled() && (c >= 32)) { _sapp_init_event(SAPP_EVENTTYPE_CHAR); _sapp.event.modifiers = _sapp_win32_mods(); _sapp.event.char_code = c; _sapp.event.key_repeat = repeat; _sapp_call_event(&_sapp.event); } } _SOKOL_PRIVATE void _sapp_win32_app_event(sapp_event_type type) { if (_sapp_events_enabled()) { _sapp_init_event(type); _sapp_call_event(&_sapp.event); } } _SOKOL_PRIVATE LRESULT CALLBACK _sapp_win32_wndproc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { /* FIXME: refresh rendering during resize with a WM_TIMER event */ if (!_sapp_win32_in_create_window) { switch (uMsg) { case WM_CLOSE: PostQuitMessage(0); return 0; case WM_SYSCOMMAND: switch (wParam & 0xFFF0) { case SC_SCREENSAVE: case SC_MONITORPOWER: if (_sapp.desc.fullscreen) { /* disable screen saver and blanking in fullscreen mode */ return 0; } break; case SC_KEYMENU: /* user trying to access menu via ALT */ return 0; } break; case WM_ERASEBKGND: return 1; case WM_SIZE: { const bool iconified = wParam == SIZE_MINIMIZED; if (iconified != _sapp_win32_iconified) { _sapp_win32_iconified = iconified; if (iconified) { _sapp_win32_app_event(SAPP_EVENTTYPE_ICONIFIED); } else { _sapp_win32_app_event(SAPP_EVENTTYPE_RESTORED); } } if (_sapp_win32_update_dimensions()) { #if defined(SOKOL_D3D11) _sapp_d3d11_resize_default_render_target(); #endif _sapp_win32_app_event(SAPP_EVENTTYPE_RESIZED); } } break; case WM_SETCURSOR: if (_sapp.desc.user_cursor) { if (LOWORD(lParam) == HTCLIENT) { _sapp_win32_app_event(SAPP_EVENTTYPE_UPDATE_CURSOR); return 1; } } break; case WM_LBUTTONDOWN: _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, SAPP_MOUSEBUTTON_LEFT); break; case WM_RBUTTONDOWN: _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, SAPP_MOUSEBUTTON_RIGHT); break; case WM_MBUTTONDOWN: _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, SAPP_MOUSEBUTTON_MIDDLE); break; case WM_LBUTTONUP: _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_UP, SAPP_MOUSEBUTTON_LEFT); break; case WM_RBUTTONUP: _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_UP, SAPP_MOUSEBUTTON_RIGHT); break; case WM_MBUTTONUP: _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_UP, SAPP_MOUSEBUTTON_MIDDLE); break; case WM_MOUSEMOVE: _sapp.mouse_x = (float)GET_X_LPARAM(lParam) * _sapp_win32_mouse_scale; _sapp.mouse_y = (float)GET_Y_LPARAM(lParam) * _sapp_win32_mouse_scale; if (!_sapp.win32_mouse_tracked) { _sapp.win32_mouse_tracked = true; TRACKMOUSEEVENT tme; memset(&tme, 0, sizeof(tme)); tme.cbSize = sizeof(tme); tme.dwFlags = TME_LEAVE; tme.hwndTrack = _sapp_win32_hwnd; TrackMouseEvent(&tme); _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_ENTER, SAPP_MOUSEBUTTON_INVALID); } _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID); break; case WM_MOUSELEAVE: _sapp.win32_mouse_tracked = false; _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_LEAVE, SAPP_MOUSEBUTTON_INVALID); break; case WM_MOUSEWHEEL: _sapp_win32_scroll_event(0.0f, (float)((SHORT)HIWORD(wParam))); break; case WM_MOUSEHWHEEL: _sapp_win32_scroll_event((float)((SHORT)HIWORD(wParam)), 0.0f); break; case WM_CHAR: _sapp_win32_char_event((uint32_t)wParam, !!(lParam&0x40000000)); break; case WM_KEYDOWN: case WM_SYSKEYDOWN: _sapp_win32_key_event(SAPP_EVENTTYPE_KEY_DOWN, (int)(HIWORD(lParam)&0x1FF), !!(lParam&0x40000000)); break; case WM_KEYUP: case WM_SYSKEYUP: _sapp_win32_key_event(SAPP_EVENTTYPE_KEY_UP, (int)(HIWORD(lParam)&0x1FF), false); break; default: break; } } return DefWindowProcW(hWnd, uMsg, wParam, lParam); } _SOKOL_PRIVATE void _sapp_win32_create_window(void) { WNDCLASSW wndclassw; memset(&wndclassw, 0, sizeof(wndclassw)); wndclassw.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; wndclassw.lpfnWndProc = (WNDPROC) _sapp_win32_wndproc; wndclassw.hInstance = GetModuleHandleW(NULL); wndclassw.hCursor = LoadCursor(NULL, IDC_ARROW); wndclassw.hIcon = LoadIcon(NULL, IDI_WINLOGO); wndclassw.lpszClassName = L"SOKOLAPP"; RegisterClassW(&wndclassw); DWORD win_style; const DWORD win_ex_style = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; RECT rect = { 0, 0, 0, 0 }; if (_sapp.desc.fullscreen) { win_style = WS_POPUP | WS_SYSMENU | WS_VISIBLE; rect.right = GetSystemMetrics(SM_CXSCREEN); rect.bottom = GetSystemMetrics(SM_CYSCREEN); } else { win_style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SIZEBOX; rect.right = (int) ((float)_sapp.window_width * _sapp_win32_window_scale); rect.bottom = (int) ((float)_sapp.window_height * _sapp_win32_window_scale); } AdjustWindowRectEx(&rect, win_style, FALSE, win_ex_style); const int win_width = rect.right - rect.left; const int win_height = rect.bottom - rect.top; _sapp_win32_in_create_window = true; _sapp_win32_hwnd = CreateWindowExW( win_ex_style, /* dwExStyle */ L"SOKOLAPP", /* lpClassName */ _sapp.window_title_wide, /* lpWindowName */ win_style, /* dwStyle */ CW_USEDEFAULT, /* X */ CW_USEDEFAULT, /* Y */ win_width, /* nWidth */ win_height, /* nHeight */ NULL, /* hWndParent */ NULL, /* hMenu */ GetModuleHandle(NULL), /* hInstance */ NULL); /* lParam */ ShowWindow(_sapp_win32_hwnd, SW_SHOW); _sapp_win32_in_create_window = false; _sapp_win32_dc = GetDC(_sapp_win32_hwnd); SOKOL_ASSERT(_sapp_win32_dc); _sapp_win32_update_dimensions(); } _SOKOL_PRIVATE void _sapp_win32_destroy_window(void) { DestroyWindow(_sapp_win32_hwnd); _sapp_win32_hwnd = 0; UnregisterClassW(L"SOKOLAPP", GetModuleHandleW(NULL)); } _SOKOL_PRIVATE void _sapp_win32_init_dpi(void) { SOKOL_ASSERT(0 == _sapp_win32_setprocessdpiaware); SOKOL_ASSERT(0 == _sapp_win32_setprocessdpiawareness); SOKOL_ASSERT(0 == _sapp_win32_getdpiformonitor); HINSTANCE user32 = LoadLibraryA("user32.dll"); if (user32) { _sapp_win32_setprocessdpiaware = (SETPROCESSDPIAWARE_T) GetProcAddress(user32, "SetProcessDPIAware"); } HINSTANCE shcore = LoadLibraryA("shcore.dll"); if (shcore) { _sapp_win32_setprocessdpiawareness = (SETPROCESSDPIAWARENESS_T) GetProcAddress(shcore, "SetProcessDpiAwareness"); _sapp_win32_getdpiformonitor = (GETDPIFORMONITOR_T) GetProcAddress(shcore, "GetDpiForMonitor"); } if (_sapp_win32_setprocessdpiawareness) { /* if the app didn't request HighDPI rendering, let Windows do the upscaling */ PROCESS_DPI_AWARENESS process_dpi_awareness = PROCESS_SYSTEM_DPI_AWARE; _sapp_win32_dpi_aware = true; if (!_sapp.desc.high_dpi) { process_dpi_awareness = PROCESS_DPI_UNAWARE; _sapp_win32_dpi_aware = false; } _sapp_win32_setprocessdpiawareness(process_dpi_awareness); } else if (_sapp_win32_setprocessdpiaware) { _sapp_win32_setprocessdpiaware(); _sapp_win32_dpi_aware = true; } /* get dpi scale factor for main monitor */ if (_sapp_win32_getdpiformonitor && _sapp_win32_dpi_aware) { POINT pt = { 1, 1 }; HMONITOR hm = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST); UINT dpix, dpiy; HRESULT hr = _sapp_win32_getdpiformonitor(hm, MDT_EFFECTIVE_DPI, &dpix, &dpiy); _SOKOL_UNUSED(hr); SOKOL_ASSERT(SUCCEEDED(hr)); /* clamp window scale to an integer factor */ _sapp_win32_window_scale = (float)dpix / 96.0f; } else { _sapp_win32_window_scale = 1.0f; } if (_sapp.desc.high_dpi) { _sapp_win32_content_scale = _sapp_win32_window_scale; _sapp_win32_mouse_scale = 1.0f; } else { _sapp_win32_content_scale = 1.0f; _sapp_win32_mouse_scale = 1.0f / _sapp_win32_window_scale; } _sapp.dpi_scale = _sapp_win32_content_scale; if (user32) { FreeLibrary(user32); } if (shcore) { FreeLibrary(shcore); } } _SOKOL_PRIVATE void _sapp_run(const sapp_desc* desc) { _sapp_init_state(desc); _sapp_win32_init_keytable(); _sapp_win32_utf8_to_wide(_sapp.window_title, _sapp.window_title_wide, sizeof(_sapp.window_title_wide)); _sapp_win32_init_dpi(); _sapp_win32_create_window(); #if defined(SOKOL_D3D11) _sapp_d3d11_create_device_and_swapchain(); _sapp_d3d11_create_default_render_target(); #endif #if defined(SOKOL_GLCORE33) _sapp_wgl_init(); _sapp_wgl_load_extensions(); _sapp_wgl_create_context(); #if !defined(SOKOL_WIN32_NO_GL_LOADER) _sapp_win32_gl_loadfuncs(); #endif #endif _sapp.valid = true; bool done = false; while (!done) { MSG msg; while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) { if (WM_QUIT == msg.message) { done = true; } else { TranslateMessage(&msg); DispatchMessage(&msg); } } _sapp_frame(); #if defined(SOKOL_D3D11) IDXGISwapChain_Present(_sapp_dxgi_swap_chain, _sapp.swap_interval, 0); if (IsIconic(_sapp_win32_hwnd)) { Sleep(16 * _sapp.swap_interval); } #endif #if defined(SOKOL_GLCORE33) _sapp_wgl_swap_buffers(); #endif } _sapp_call_cleanup(); #if defined(SOKOL_D3D11) _sapp_d3d11_destroy_default_render_target(); _sapp_d3d11_destroy_device_and_swapchain(); #else _sapp_wgl_destroy_context(); _sapp_wgl_shutdown(); #endif _sapp_win32_destroy_window(); } #if !defined(SOKOL_NO_ENTRY) #if defined(SOKOL_WIN32_FORCE_MAIN) int main(int argc, char* argv[]) { #else int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow) { _SOKOL_UNUSED(hInstance); _SOKOL_UNUSED(hPrevInstance); _SOKOL_UNUSED(lpCmdLine); _SOKOL_UNUSED(nCmdShow); int argc = __argc; char** argv = __argv; #endif sapp_desc desc = sokol_main(argc, argv); _sapp_run(&desc); return 0; } #endif /* SOKOL_NO_ENTRY */ #undef _SAPP_SAFE_RELEASE #endif /* WINDOWS */ /*== Android ================================================================*/ #if defined(__ANDROID__) #include #include #include #include #include #if defined(SOKOL_GLES3) #include #else #ifndef GL_EXT_PROTOTYPES #define GL_GLEXT_PROTOTYPES #endif #include #include #endif typedef struct { pthread_t thread; pthread_mutex_t mutex; pthread_cond_t cond; int read_from_main_fd; int write_from_main_fd; } _sapp_android_pt_t; typedef struct { ANativeWindow* window; AInputQueue* input; } _sapp_android_resources_t; typedef enum { _SOKOL_ANDROID_MSG_CREATE, _SOKOL_ANDROID_MSG_RESUME, _SOKOL_ANDROID_MSG_PAUSE, _SOKOL_ANDROID_MSG_FOCUS, _SOKOL_ANDROID_MSG_NO_FOCUS, _SOKOL_ANDROID_MSG_SET_NATIVE_WINDOW, _SOKOL_ANDROID_MSG_SET_INPUT_QUEUE, _SOKOL_ANDROID_MSG_DESTROY, } _sapp_android_msg_t; typedef struct { ANativeActivity* activity; _sapp_android_pt_t pt; _sapp_android_resources_t pending; _sapp_android_resources_t current; ALooper* looper; bool is_thread_started; bool is_thread_stopping; bool is_thread_stopped; bool has_created; bool has_resumed; bool has_focus; EGLConfig config; EGLDisplay display; EGLContext context; EGLSurface surface; } _sapp_android_state_t; static _sapp_android_state_t _sapp_android_state; /* android loop thread */ _SOKOL_PRIVATE bool _sapp_android_init_egl(void) { _sapp_android_state_t* state = &_sapp_android_state; SOKOL_ASSERT(state->display == EGL_NO_DISPLAY); SOKOL_ASSERT(state->context == EGL_NO_CONTEXT); EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); if (display == EGL_NO_DISPLAY) { return false; } if (eglInitialize(display, NULL, NULL) == EGL_FALSE) { return false; } EGLint alpha_size = _sapp.desc.alpha ? 8 : 0; const EGLint cfg_attributes[] = { EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, alpha_size, EGL_DEPTH_SIZE, 16, EGL_STENCIL_SIZE, 0, EGL_NONE, }; EGLConfig available_cfgs[32]; EGLint cfg_count; eglChooseConfig(display, cfg_attributes, available_cfgs, 32, &cfg_count); SOKOL_ASSERT(cfg_count > 0); SOKOL_ASSERT(cfg_count <= 32); /* find config with 8-bit rgb buffer if available, ndk sample does not trust egl spec */ EGLConfig config; bool exact_cfg_found = false; for (int i = 0; i < cfg_count; ++i) { EGLConfig c = available_cfgs[i]; EGLint r, g, b, a, d; if (eglGetConfigAttrib(display, c, EGL_RED_SIZE, &r) == EGL_TRUE && eglGetConfigAttrib(display, c, EGL_GREEN_SIZE, &g) == EGL_TRUE && eglGetConfigAttrib(display, c, EGL_BLUE_SIZE, &b) == EGL_TRUE && eglGetConfigAttrib(display, c, EGL_ALPHA_SIZE, &a) == EGL_TRUE && eglGetConfigAttrib(display, c, EGL_DEPTH_SIZE, &d) == EGL_TRUE && r == 8 && g == 8 && b == 8 && (alpha_size == 0 || a == alpha_size) && d == 16) { exact_cfg_found = true; config = c; break; } } if (!exact_cfg_found) { config = available_cfgs[0]; } EGLint ctx_attributes[] = { #if defined(SOKOL_GLES3) EGL_CONTEXT_CLIENT_VERSION, _sapp.desc.gl_force_gles2 ? 2 : 3, #else EGL_CONTEXT_CLIENT_VERSION, 2, #endif EGL_NONE, }; EGLContext context = eglCreateContext(display, config, EGL_NO_CONTEXT, ctx_attributes); if (context == EGL_NO_CONTEXT) { return false; } state->config = config; state->display = display; state->context = context; return true; } _SOKOL_PRIVATE void _sapp_android_cleanup_egl(void) { _sapp_android_state_t* state = &_sapp_android_state; if (state->display != EGL_NO_DISPLAY) { eglMakeCurrent(state->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); if (state->surface != EGL_NO_SURFACE) { SOKOL_LOG("Destroying egl surface"); eglDestroySurface(state->display, state->surface); state->surface = EGL_NO_SURFACE; } if (state->context != EGL_NO_CONTEXT) { SOKOL_LOG("Destroying egl context"); eglDestroyContext(state->display, state->context); state->context = EGL_NO_CONTEXT; } SOKOL_LOG("Terminating egl display"); eglTerminate(state->display); state->display = EGL_NO_DISPLAY; } } _SOKOL_PRIVATE bool _sapp_android_init_egl_surface(ANativeWindow* window) { _sapp_android_state_t* state = &_sapp_android_state; SOKOL_ASSERT(state->display != EGL_NO_DISPLAY); SOKOL_ASSERT(state->context != EGL_NO_CONTEXT); SOKOL_ASSERT(state->surface == EGL_NO_SURFACE); SOKOL_ASSERT(window); /* TODO: set window flags */ /* ANativeActivity_setWindowFlags(activity, AWINDOW_FLAG_KEEP_SCREEN_ON, 0); */ /* create egl surface and make it current */ EGLSurface surface = eglCreateWindowSurface(state->display, state->config, window, NULL); if (surface == EGL_NO_SURFACE) { return false; } if (eglMakeCurrent(state->display, surface, surface, state->context) == EGL_FALSE) { return false; } state->surface = surface; return true; } _SOKOL_PRIVATE void _sapp_android_cleanup_egl_surface(void) { _sapp_android_state_t* state = &_sapp_android_state; if (state->display == EGL_NO_DISPLAY) { return; } eglMakeCurrent(state->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); if (state->surface != EGL_NO_SURFACE) { eglDestroySurface(state->display, state->surface); state->surface = EGL_NO_SURFACE; } } _SOKOL_PRIVATE void _sapp_android_app_event(sapp_event_type type) { if (_sapp_events_enabled()) { _sapp_init_event(type); SOKOL_LOG("event_cb()"); _sapp_call_event(&_sapp.event); } } _SOKOL_PRIVATE void _sapp_android_update_dimensions(ANativeWindow* window, bool force_update) { _sapp_android_state_t* state = &_sapp_android_state; SOKOL_ASSERT(state->display != EGL_NO_DISPLAY); SOKOL_ASSERT(state->context != EGL_NO_CONTEXT); SOKOL_ASSERT(state->surface != EGL_NO_SURFACE); SOKOL_ASSERT(window); const int32_t win_w = ANativeWindow_getWidth(window); const int32_t win_h = ANativeWindow_getHeight(window); SOKOL_ASSERT(win_w >= 0 && win_h >= 0); const bool win_changed = (win_w != _sapp.window_width) || (win_h != _sapp.window_height); _sapp.window_width = win_w; _sapp.window_height = win_h; if (win_changed || force_update) { if (!_sapp.desc.high_dpi) { const int32_t buf_w = win_w / 2; const int32_t buf_h = win_h / 2; EGLint format; EGLBoolean egl_result = eglGetConfigAttrib(state->display, state->config, EGL_NATIVE_VISUAL_ID, &format); SOKOL_ASSERT(egl_result == EGL_TRUE); /* NOTE: calling ANativeWindow_setBuffersGeometry() with the same dimensions as the ANativeWindow size results in weird display artefacts, that's why it's only called when the buffer geometry is different from the window size */ int32_t result = ANativeWindow_setBuffersGeometry(window, buf_w, buf_h, format); SOKOL_ASSERT(result == 0); } } /* query surface size */ EGLint fb_w, fb_h; EGLBoolean egl_result_w = eglQuerySurface(state->display, state->surface, EGL_WIDTH, &fb_w); EGLBoolean egl_result_h = eglQuerySurface(state->display, state->surface, EGL_HEIGHT, &fb_h); SOKOL_ASSERT(egl_result_w == EGL_TRUE); SOKOL_ASSERT(egl_result_h == EGL_TRUE); const bool fb_changed = (fb_w != _sapp.framebuffer_width) || (fb_h != _sapp.framebuffer_height); _sapp.framebuffer_width = fb_w; _sapp.framebuffer_height = fb_h; _sapp.dpi_scale = (float)_sapp.framebuffer_width / (float)_sapp.window_width; if (win_changed || fb_changed || force_update) { if (!_sapp.first_frame) { SOKOL_LOG("SAPP_EVENTTYPE_RESIZED"); _sapp_android_app_event(SAPP_EVENTTYPE_RESIZED); } } } _SOKOL_PRIVATE void _sapp_android_cleanup(void) { _sapp_android_state_t* state = &_sapp_android_state; SOKOL_LOG("Cleaning up"); if (state->surface != EGL_NO_SURFACE) { /* egl context is bound, cleanup gracefully */ if (_sapp.init_called && !_sapp.cleanup_called) { SOKOL_LOG("cleanup_cb()"); _sapp_call_cleanup(); } } /* always try to cleanup by destroying egl context */ _sapp_android_cleanup_egl(); } _SOKOL_PRIVATE void _sapp_android_shutdown(void) { /* try to cleanup while we still have a surface and can call cleanup_cb() */ _sapp_android_cleanup(); /* request exit */ ANativeActivity_finish(_sapp_android_state.activity); } _SOKOL_PRIVATE void _sapp_android_frame(void) { _sapp_android_state_t* state = &_sapp_android_state; SOKOL_ASSERT(state->display != EGL_NO_DISPLAY); SOKOL_ASSERT(state->context != EGL_NO_CONTEXT); SOKOL_ASSERT(state->surface != EGL_NO_SURFACE); _sapp_android_update_dimensions(state->current.window, false); _sapp_frame(); eglSwapBuffers(state->display, _sapp_android_state.surface); } _SOKOL_PRIVATE bool _sapp_android_touch_event(const AInputEvent* e) { if (AInputEvent_getType(e) != AINPUT_EVENT_TYPE_MOTION) { return false; } if (!_sapp_events_enabled()) { return false; } int32_t action_idx = AMotionEvent_getAction(e); int32_t action = action_idx & AMOTION_EVENT_ACTION_MASK; sapp_event_type type = SAPP_EVENTTYPE_INVALID; switch (action) { case AMOTION_EVENT_ACTION_DOWN: SOKOL_LOG("Touch: down"); case AMOTION_EVENT_ACTION_POINTER_DOWN: SOKOL_LOG("Touch: ptr down"); type = SAPP_EVENTTYPE_TOUCHES_BEGAN; break; case AMOTION_EVENT_ACTION_MOVE: type = SAPP_EVENTTYPE_TOUCHES_MOVED; break; case AMOTION_EVENT_ACTION_UP: SOKOL_LOG("Touch: up"); case AMOTION_EVENT_ACTION_POINTER_UP: SOKOL_LOG("Touch: ptr up"); type = SAPP_EVENTTYPE_TOUCHES_ENDED; break; case AMOTION_EVENT_ACTION_CANCEL: SOKOL_LOG("Touch: cancel"); type = SAPP_EVENTTYPE_TOUCHES_CANCELLED; break; default: break; } if (type == SAPP_EVENTTYPE_INVALID) { return false; } int32_t idx = action_idx >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; _sapp_init_event(type); _sapp.event.num_touches = AMotionEvent_getPointerCount(e); if (_sapp.event.num_touches > SAPP_MAX_TOUCHPOINTS) { _sapp.event.num_touches = SAPP_MAX_TOUCHPOINTS; } for (int32_t i = 0; i < _sapp.event.num_touches; i++) { sapp_touchpoint* dst = &_sapp.event.touches[i]; dst->identifier = AMotionEvent_getPointerId(e, i); dst->pos_x = (AMotionEvent_getRawX(e, i) / _sapp.window_width) * _sapp.framebuffer_width; dst->pos_y = (AMotionEvent_getRawY(e, i) / _sapp.window_height) * _sapp.framebuffer_height; if (action == AMOTION_EVENT_ACTION_POINTER_DOWN || action == AMOTION_EVENT_ACTION_POINTER_UP) { dst->changed = i == idx; } else { dst->changed = true; } } _sapp_call_event(&_sapp.event); return true; } _SOKOL_PRIVATE bool _sapp_android_key_event(const AInputEvent* e) { if (AInputEvent_getType(e) != AINPUT_EVENT_TYPE_KEY) { return false; } if (AKeyEvent_getKeyCode(e) == AKEYCODE_BACK) { /* FIXME: this should be hooked into a "really quit?" mechanism so the app can ask the user for confirmation, this is currently generally missing in sokol_app.h */ _sapp_android_shutdown(); return true; } return false; } _SOKOL_PRIVATE int _sapp_android_input_cb(int fd, int events, void* data) { if ((events & ALOOPER_EVENT_INPUT) == 0) { SOKOL_LOG("_sapp_android_input_cb() encountered unsupported event"); return 1; } _sapp_android_state_t* state = &_sapp_android_state;; SOKOL_ASSERT(state->current.input); AInputEvent* event = NULL; while (AInputQueue_getEvent(state->current.input, &event) >= 0) { if (AInputQueue_preDispatchEvent(state->current.input, event) != 0) { continue; } int32_t handled = 0; if (_sapp_android_touch_event(event) || _sapp_android_key_event(event)) { handled = 1; } AInputQueue_finishEvent(state->current.input, event, handled); } return 1; } _SOKOL_PRIVATE int _sapp_android_main_cb(int fd, int events, void* data) { if ((events & ALOOPER_EVENT_INPUT) == 0) { SOKOL_LOG("_sapp_android_main_cb() encountered unsupported event"); return 1; } _sapp_android_state_t* state = &_sapp_android_state; _sapp_android_msg_t msg; if (read(fd, &msg, sizeof(msg)) != sizeof(msg)) { SOKOL_LOG("Could not write to read_from_main_fd"); return 1; } pthread_mutex_lock(&state->pt.mutex); switch (msg) { case _SOKOL_ANDROID_MSG_CREATE: { SOKOL_LOG("MSG_CREATE"); SOKOL_ASSERT(!_sapp.valid); bool result = _sapp_android_init_egl(); SOKOL_ASSERT(result); _sapp.valid = true; state->has_created = true; } break; case _SOKOL_ANDROID_MSG_RESUME: SOKOL_LOG("MSG_RESUME"); state->has_resumed = true; _sapp_android_app_event(SAPP_EVENTTYPE_RESUMED); break; case _SOKOL_ANDROID_MSG_PAUSE: SOKOL_LOG("MSG_PAUSE"); state->has_resumed = false; _sapp_android_app_event(SAPP_EVENTTYPE_SUSPENDED); break; case _SOKOL_ANDROID_MSG_FOCUS: SOKOL_LOG("MSG_FOCUS"); state->has_focus = true; break; case _SOKOL_ANDROID_MSG_NO_FOCUS: SOKOL_LOG("MSG_NO_FOCUS"); state->has_focus = false; break; case _SOKOL_ANDROID_MSG_SET_NATIVE_WINDOW: SOKOL_LOG("MSG_SET_NATIVE_WINDOW"); if (state->current.window != state->pending.window) { if (state->current.window != NULL) { _sapp_android_cleanup_egl_surface(); } if (state->pending.window != NULL) { SOKOL_LOG("Creating egl surface ..."); if (_sapp_android_init_egl_surface(state->pending.window)) { SOKOL_LOG("... ok!"); _sapp_android_update_dimensions(state->pending.window, true); } else { SOKOL_LOG("... failed!"); _sapp_android_shutdown(); } } } state->current.window = state->pending.window; break; case _SOKOL_ANDROID_MSG_SET_INPUT_QUEUE: SOKOL_LOG("MSG_SET_INPUT_QUEUE"); if (state->current.input != state->pending.input) { if (state->current.input != NULL) { AInputQueue_detachLooper(state->current.input); } if (state->pending.input != NULL) { AInputQueue_attachLooper( state->pending.input, state->looper, ALOOPER_POLL_CALLBACK, _sapp_android_input_cb, NULL); /* data */ } } state->current.input = state->pending.input; break; case _SOKOL_ANDROID_MSG_DESTROY: SOKOL_LOG("MSG_DESTROY"); _sapp_android_cleanup(); _sapp.valid = false; state->is_thread_stopping = true; break; default: SOKOL_LOG("Unknown msg type received"); break; } pthread_cond_broadcast(&state->pt.cond); /* signal "received" */ pthread_mutex_unlock(&state->pt.mutex); return 1; } _SOKOL_PRIVATE bool _sapp_android_should_update(void) { bool is_in_front = _sapp_android_state.has_resumed && _sapp_android_state.has_focus; bool has_surface = _sapp_android_state.surface != EGL_NO_SURFACE; return is_in_front && has_surface; } _SOKOL_PRIVATE void _sapp_android_show_keyboard(bool shown) { SOKOL_ASSERT(_sapp.valid); /* This seems to be broken in the NDK, but there is (a very cumbersome) workaround... */ if (shown) { SOKOL_LOG("Showing keyboard"); ANativeActivity_showSoftInput(_sapp_android_state.activity, ANATIVEACTIVITY_SHOW_SOFT_INPUT_FORCED); } else { SOKOL_LOG("Hiding keyboard"); ANativeActivity_hideSoftInput(_sapp_android_state.activity, ANATIVEACTIVITY_HIDE_SOFT_INPUT_NOT_ALWAYS); } } _SOKOL_PRIVATE void* _sapp_android_loop(void* obj) { SOKOL_LOG("Loop thread started"); _sapp_android_state_t* state = (_sapp_android_state_t*)obj; state->looper = ALooper_prepare(0 /* or ALOOPER_PREPARE_ALLOW_NON_CALLBACKS*/); ALooper_addFd(state->looper, state->pt.read_from_main_fd, ALOOPER_POLL_CALLBACK, ALOOPER_EVENT_INPUT, _sapp_android_main_cb, NULL); /* data */ /* signal start to main thread */ pthread_mutex_lock(&state->pt.mutex); state->is_thread_started = true; pthread_cond_broadcast(&state->pt.cond); pthread_mutex_unlock(&state->pt.mutex); /* main loop */ while (!state->is_thread_stopping) { /* sokol frame */ if (_sapp_android_should_update()) { _sapp_android_frame(); } /* process all events (or stop early if app is requested to quit) */ bool process_events = true; while (process_events && !state->is_thread_stopping) { bool block_until_event = !state->is_thread_stopping && !_sapp_android_should_update(); process_events = ALooper_pollOnce(block_until_event ? -1 : 0, NULL, NULL, NULL) == ALOOPER_POLL_CALLBACK; } } /* cleanup thread */ if (state->current.input != NULL) { AInputQueue_detachLooper(state->current.input); } /* the following causes heap corruption on exit, why?? ALooper_removeFd(state->looper, state->pt.read_from_main_fd); ALooper_release(state->looper);*/ /* signal "destroyed" */ pthread_mutex_lock(&state->pt.mutex); state->is_thread_stopped = true; pthread_cond_broadcast(&state->pt.cond); pthread_mutex_unlock(&state->pt.mutex); SOKOL_LOG("Loop thread done"); return NULL; } /* android main/ui thread */ _SOKOL_PRIVATE void _sapp_android_msg(_sapp_android_state_t* state, _sapp_android_msg_t msg) { if (write(state->pt.write_from_main_fd, &msg, sizeof(msg)) != sizeof(msg)) { SOKOL_LOG("Could not write to write_from_main_fd"); } } _SOKOL_PRIVATE void _sapp_android_on_start(ANativeActivity* activity) { SOKOL_LOG("NativeActivity onStart()"); } _SOKOL_PRIVATE void _sapp_android_on_resume(ANativeActivity* activity) { SOKOL_LOG("NativeActivity onResume()"); _sapp_android_msg(&_sapp_android_state, _SOKOL_ANDROID_MSG_RESUME); } _SOKOL_PRIVATE void* _sapp_android_on_save_instance_state(ANativeActivity* activity, size_t* out_size) { SOKOL_LOG("NativeActivity onSaveInstanceState()"); *out_size = 0; return NULL; } _SOKOL_PRIVATE void _sapp_android_on_window_focus_changed(ANativeActivity* activity, int has_focus) { SOKOL_LOG("NativeActivity onWindowFocusChanged()"); if (has_focus) { _sapp_android_msg(&_sapp_android_state, _SOKOL_ANDROID_MSG_FOCUS); } else { _sapp_android_msg(&_sapp_android_state, _SOKOL_ANDROID_MSG_NO_FOCUS); } } _SOKOL_PRIVATE void _sapp_android_on_pause(ANativeActivity* activity) { SOKOL_LOG("NativeActivity onPause()"); _sapp_android_msg(&_sapp_android_state, _SOKOL_ANDROID_MSG_PAUSE); } _SOKOL_PRIVATE void _sapp_android_on_stop(ANativeActivity* activity) { SOKOL_LOG("NativeActivity onStop()"); } _SOKOL_PRIVATE void _sapp_android_msg_set_native_window(_sapp_android_state_t* state, ANativeWindow* window) { pthread_mutex_lock(&state->pt.mutex); state->pending.window = window; _sapp_android_msg(state, _SOKOL_ANDROID_MSG_SET_NATIVE_WINDOW); while (state->current.window != window) { pthread_cond_wait(&state->pt.cond, &state->pt.mutex); } pthread_mutex_unlock(&state->pt.mutex); } _SOKOL_PRIVATE void _sapp_android_on_native_window_created(ANativeActivity* activity, ANativeWindow* window) { SOKOL_LOG("NativeActivity onNativeWindowCreated()"); _sapp_android_msg_set_native_window(&_sapp_android_state, window); } _SOKOL_PRIVATE void _sapp_android_on_native_window_destroyed(ANativeActivity* activity, ANativeWindow* window) { SOKOL_LOG("NativeActivity onNativeWindowDestroyed()"); _sapp_android_msg_set_native_window(&_sapp_android_state, NULL); } _SOKOL_PRIVATE void _sapp_android_msg_set_input_queue(_sapp_android_state_t* state, AInputQueue* input) { pthread_mutex_lock(&state->pt.mutex); state->pending.input = input; _sapp_android_msg(state, _SOKOL_ANDROID_MSG_SET_INPUT_QUEUE); while (state->current.input != input) { pthread_cond_wait(&state->pt.cond, &state->pt.mutex); } pthread_mutex_unlock(&state->pt.mutex); } _SOKOL_PRIVATE void _sapp_android_on_input_queue_created(ANativeActivity* activity, AInputQueue* queue) { SOKOL_LOG("NativeActivity onInputQueueCreated()"); _sapp_android_msg_set_input_queue(&_sapp_android_state, queue); } _SOKOL_PRIVATE void _sapp_android_on_input_queue_destroyed(ANativeActivity* activity, AInputQueue* queue) { SOKOL_LOG("NativeActivity onInputQueueDestroyed()"); _sapp_android_msg_set_input_queue(&_sapp_android_state, NULL); } _SOKOL_PRIVATE void _sapp_android_on_config_changed(ANativeActivity* activity) { SOKOL_LOG("NativeActivity onConfigurationChanged()"); /* see android:configChanges in manifest */ } _SOKOL_PRIVATE void _sapp_android_on_low_memory(ANativeActivity* activity) { SOKOL_LOG("NativeActivity onLowMemory()"); } _SOKOL_PRIVATE void _sapp_android_on_destroy(ANativeActivity* activity) { /* * For some reason even an empty app using nativeactivity.h will crash (WIN DEATH) * on my device (Moto X 2nd gen) when the app is removed from the task view * (TaskStackView: onTaskViewDismissed). * * However, if ANativeActivity_finish() is explicitly called from for example * _sapp_android_on_stop(), the crash disappears. Is this a bug in NativeActivity? */ SOKOL_LOG("NativeActivity onDestroy()"); _sapp_android_state_t* state = &_sapp_android_state; /* send destroy msg */ pthread_mutex_lock(&state->pt.mutex); _sapp_android_msg(state, _SOKOL_ANDROID_MSG_DESTROY); while (!_sapp_android_state.is_thread_stopped) { pthread_cond_wait(&state->pt.cond, &state->pt.mutex); } pthread_mutex_unlock(&state->pt.mutex); /* clean up main thread */ pthread_cond_destroy(&state->pt.cond); pthread_mutex_destroy(&state->pt.mutex); close(state->pt.read_from_main_fd); close(state->pt.write_from_main_fd); SOKOL_LOG("NativeActivity done"); /* this is a bit naughty, but causes a clean restart of the app (static globals are reset) */ exit(0); } JNIEXPORT void ANativeActivity_onCreate(ANativeActivity* activity, void* saved_state, size_t saved_state_size) { SOKOL_LOG("NativeActivity onCreate()"); sapp_desc desc = sokol_main(0, NULL); _sapp_init_state(&desc); /* start loop thread */ _sapp_android_state = (_sapp_android_state_t){0}; _sapp_android_state_t* state = &_sapp_android_state; state->activity = activity; int pipe_fd[2]; if (pipe(pipe_fd) != 0) { SOKOL_LOG("Could not create thread pipe"); return; } state->pt.read_from_main_fd = pipe_fd[0]; state->pt.write_from_main_fd = pipe_fd[1]; pthread_mutex_init(&state->pt.mutex, NULL); pthread_cond_init(&state->pt.cond, NULL); pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); pthread_create(&state->pt.thread, &attr, _sapp_android_loop, state); pthread_attr_destroy(&attr); /* wait until main loop has started */ pthread_mutex_lock(&state->pt.mutex); while (!state->is_thread_started) { pthread_cond_wait(&state->pt.cond, &state->pt.mutex); } pthread_mutex_unlock(&state->pt.mutex); /* send create msg */ pthread_mutex_lock(&state->pt.mutex); _sapp_android_msg(state, _SOKOL_ANDROID_MSG_CREATE); while (!state->has_created) { pthread_cond_wait(&state->pt.cond, &state->pt.mutex); } pthread_mutex_unlock(&state->pt.mutex); /* register for callbacks */ activity->instance = state; activity->callbacks->onStart = _sapp_android_on_start; activity->callbacks->onResume = _sapp_android_on_resume; activity->callbacks->onSaveInstanceState = _sapp_android_on_save_instance_state; activity->callbacks->onWindowFocusChanged = _sapp_android_on_window_focus_changed; activity->callbacks->onPause = _sapp_android_on_pause; activity->callbacks->onStop = _sapp_android_on_stop; activity->callbacks->onDestroy = _sapp_android_on_destroy; activity->callbacks->onNativeWindowCreated = _sapp_android_on_native_window_created; /* activity->callbacks->onNativeWindowResized = _sapp_android_on_native_window_resized; */ /* activity->callbacks->onNativeWindowRedrawNeeded = _sapp_android_on_native_window_redraw_needed; */ activity->callbacks->onNativeWindowDestroyed = _sapp_android_on_native_window_destroyed; activity->callbacks->onInputQueueCreated = _sapp_android_on_input_queue_created; activity->callbacks->onInputQueueDestroyed = _sapp_android_on_input_queue_destroyed; /* activity->callbacks->onContentRectChanged = _sapp_android_on_content_rect_changed; */ activity->callbacks->onConfigurationChanged = _sapp_android_on_config_changed; activity->callbacks->onLowMemory = _sapp_android_on_low_memory; SOKOL_LOG("NativeActivity successfully created"); } #endif /* Android */ /*== LINUX ==================================================================*/ #if (defined(__linux__) || defined(__unix__)) && !defined(__EMSCRIPTEN__) && !defined(__ANDROID__) #define GL_GLEXT_PROTOTYPES #include #include #include #include #include #include /* CARD32 */ #include #include /* dlopen, dlsym, dlclose */ #include /* LONG_MAX */ #define GLX_VENDOR 1 #define GLX_RGBA_BIT 0x00000001 #define GLX_WINDOW_BIT 0x00000001 #define GLX_DRAWABLE_TYPE 0x8010 #define GLX_RENDER_TYPE 0x8011 #define GLX_RGBA_TYPE 0x8014 #define GLX_DOUBLEBUFFER 5 #define GLX_STEREO 6 #define GLX_AUX_BUFFERS 7 #define GLX_RED_SIZE 8 #define GLX_GREEN_SIZE 9 #define GLX_BLUE_SIZE 10 #define GLX_ALPHA_SIZE 11 #define GLX_DEPTH_SIZE 12 #define GLX_STENCIL_SIZE 13 #define GLX_ACCUM_RED_SIZE 14 #define GLX_ACCUM_GREEN_SIZE 15 #define GLX_ACCUM_BLUE_SIZE 16 #define GLX_ACCUM_ALPHA_SIZE 17 #define GLX_SAMPLES 0x186a1 #define GLX_VISUAL_ID 0x800b #define GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20b2 #define GLX_CONTEXT_DEBUG_BIT_ARB 0x00000001 #define GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002 #define GLX_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 #define GLX_CONTEXT_PROFILE_MASK_ARB 0x9126 #define GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002 #define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091 #define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092 #define GLX_CONTEXT_FLAGS_ARB 0x2094 #define GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB 0x00000004 #define GLX_LOSE_CONTEXT_ON_RESET_ARB 0x8252 #define GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB 0x8256 #define GLX_NO_RESET_NOTIFICATION_ARB 0x8261 #define GLX_CONTEXT_RELEASE_BEHAVIOR_ARB 0x2097 #define GLX_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB 0 #define GLX_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB 0x2098 typedef XID GLXWindow; typedef XID GLXDrawable; typedef struct __GLXFBConfig* GLXFBConfig; typedef struct __GLXcontext* GLXContext; typedef void (*__GLXextproc)(void); typedef int (*PFNGLXGETFBCONFIGATTRIBPROC)(Display*,GLXFBConfig,int,int*); typedef const char* (*PFNGLXGETCLIENTSTRINGPROC)(Display*,int); typedef Bool (*PFNGLXQUERYEXTENSIONPROC)(Display*,int*,int*); typedef Bool (*PFNGLXQUERYVERSIONPROC)(Display*,int*,int*); typedef void (*PFNGLXDESTROYCONTEXTPROC)(Display*,GLXContext); typedef Bool (*PFNGLXMAKECURRENTPROC)(Display*,GLXDrawable,GLXContext); typedef void (*PFNGLXSWAPBUFFERSPROC)(Display*,GLXDrawable); typedef const char* (*PFNGLXQUERYEXTENSIONSSTRINGPROC)(Display*,int); typedef GLXFBConfig* (*PFNGLXGETFBCONFIGSPROC)(Display*,int,int*); typedef GLXContext (*PFNGLXCREATENEWCONTEXTPROC)(Display*,GLXFBConfig,int,GLXContext,Bool); typedef __GLXextproc (* PFNGLXGETPROCADDRESSPROC)(const GLubyte *procName); typedef void (*PFNGLXSWAPINTERVALEXTPROC)(Display*,GLXDrawable,int); typedef XVisualInfo* (*PFNGLXGETVISUALFROMFBCONFIGPROC)(Display*,GLXFBConfig); typedef GLXWindow (*PFNGLXCREATEWINDOWPROC)(Display*,GLXFBConfig,Window,const int*); typedef void (*PFNGLXDESTROYWINDOWPROC)(Display*,GLXWindow); typedef int (*PFNGLXSWAPINTERVALMESAPROC)(int); typedef GLXContext (*PFNGLXCREATECONTEXTATTRIBSARBPROC)(Display*,GLXFBConfig,GLXContext,Bool,const int*); static bool _sapp_x11_quit_requested; static Display* _sapp_x11_display; static int _sapp_x11_screen; static Window _sapp_x11_root; static Colormap _sapp_x11_colormap; static Window _sapp_x11_window; static float _sapp_x11_dpi; static int _sapp_x11_window_state; static unsigned char _sapp_x11_error_code; static void* _sapp_glx_libgl; static int _sapp_glx_major; static int _sapp_glx_minor; static int _sapp_glx_eventbase; static int _sapp_glx_errorbase; static GLXContext _sapp_glx_ctx; static GLXWindow _sapp_glx_window; static Atom _sapp_x11_UTF8_STRING; static Atom _sapp_x11_WM_PROTOCOLS; static Atom _sapp_x11_WM_DELETE_WINDOW; static Atom _sapp_x11_WM_STATE; static Atom _sapp_x11_NET_WM_NAME; static Atom _sapp_x11_NET_WM_ICON_NAME; // GLX 1.3 functions static PFNGLXGETFBCONFIGSPROC _sapp_glx_GetFBConfigs; static PFNGLXGETFBCONFIGATTRIBPROC _sapp_glx_GetFBConfigAttrib; static PFNGLXGETCLIENTSTRINGPROC _sapp_glx_GetClientString; static PFNGLXQUERYEXTENSIONPROC _sapp_glx_QueryExtension; static PFNGLXQUERYVERSIONPROC _sapp_glx_QueryVersion; static PFNGLXDESTROYCONTEXTPROC _sapp_glx_DestroyContext; static PFNGLXMAKECURRENTPROC _sapp_glx_MakeCurrent; static PFNGLXSWAPBUFFERSPROC _sapp_glx_SwapBuffers; static PFNGLXQUERYEXTENSIONSSTRINGPROC _sapp_glx_QueryExtensionsString; static PFNGLXCREATENEWCONTEXTPROC _sapp_glx_CreateNewContext; static PFNGLXGETVISUALFROMFBCONFIGPROC _sapp_glx_GetVisualFromFBConfig; static PFNGLXCREATEWINDOWPROC _sapp_glx_CreateWindow; static PFNGLXDESTROYWINDOWPROC _sapp_glx_DestroyWindow; // GLX 1.4 and extension functions static PFNGLXGETPROCADDRESSPROC _sapp_glx_GetProcAddress; static PFNGLXGETPROCADDRESSPROC _sapp_glx_GetProcAddressARB; static PFNGLXSWAPINTERVALEXTPROC _sapp_glx_SwapIntervalEXT; static PFNGLXSWAPINTERVALMESAPROC _sapp_glx_SwapIntervalMESA; static PFNGLXCREATECONTEXTATTRIBSARBPROC _sapp_glx_CreateContextAttribsARB; static bool _sapp_glx_EXT_swap_control; static bool _sapp_glx_MESA_swap_control; static bool _sapp_glx_ARB_multisample; static bool _sapp_glx_ARB_framebuffer_sRGB; static bool _sapp_glx_EXT_framebuffer_sRGB; static bool _sapp_glx_ARB_create_context; static bool _sapp_glx_ARB_create_context_profile; /* see GLFW's xkb_unicode.c */ static const struct _sapp_x11_codepair { uint16_t keysym; uint16_t ucs; } _sapp_x11_keysymtab[] = { { 0x01a1, 0x0104 }, { 0x01a2, 0x02d8 }, { 0x01a3, 0x0141 }, { 0x01a5, 0x013d }, { 0x01a6, 0x015a }, { 0x01a9, 0x0160 }, { 0x01aa, 0x015e }, { 0x01ab, 0x0164 }, { 0x01ac, 0x0179 }, { 0x01ae, 0x017d }, { 0x01af, 0x017b }, { 0x01b1, 0x0105 }, { 0x01b2, 0x02db }, { 0x01b3, 0x0142 }, { 0x01b5, 0x013e }, { 0x01b6, 0x015b }, { 0x01b7, 0x02c7 }, { 0x01b9, 0x0161 }, { 0x01ba, 0x015f }, { 0x01bb, 0x0165 }, { 0x01bc, 0x017a }, { 0x01bd, 0x02dd }, { 0x01be, 0x017e }, { 0x01bf, 0x017c }, { 0x01c0, 0x0154 }, { 0x01c3, 0x0102 }, { 0x01c5, 0x0139 }, { 0x01c6, 0x0106 }, { 0x01c8, 0x010c }, { 0x01ca, 0x0118 }, { 0x01cc, 0x011a }, { 0x01cf, 0x010e }, { 0x01d0, 0x0110 }, { 0x01d1, 0x0143 }, { 0x01d2, 0x0147 }, { 0x01d5, 0x0150 }, { 0x01d8, 0x0158 }, { 0x01d9, 0x016e }, { 0x01db, 0x0170 }, { 0x01de, 0x0162 }, { 0x01e0, 0x0155 }, { 0x01e3, 0x0103 }, { 0x01e5, 0x013a }, { 0x01e6, 0x0107 }, { 0x01e8, 0x010d }, { 0x01ea, 0x0119 }, { 0x01ec, 0x011b }, { 0x01ef, 0x010f }, { 0x01f0, 0x0111 }, { 0x01f1, 0x0144 }, { 0x01f2, 0x0148 }, { 0x01f5, 0x0151 }, { 0x01f8, 0x0159 }, { 0x01f9, 0x016f }, { 0x01fb, 0x0171 }, { 0x01fe, 0x0163 }, { 0x01ff, 0x02d9 }, { 0x02a1, 0x0126 }, { 0x02a6, 0x0124 }, { 0x02a9, 0x0130 }, { 0x02ab, 0x011e }, { 0x02ac, 0x0134 }, { 0x02b1, 0x0127 }, { 0x02b6, 0x0125 }, { 0x02b9, 0x0131 }, { 0x02bb, 0x011f }, { 0x02bc, 0x0135 }, { 0x02c5, 0x010a }, { 0x02c6, 0x0108 }, { 0x02d5, 0x0120 }, { 0x02d8, 0x011c }, { 0x02dd, 0x016c }, { 0x02de, 0x015c }, { 0x02e5, 0x010b }, { 0x02e6, 0x0109 }, { 0x02f5, 0x0121 }, { 0x02f8, 0x011d }, { 0x02fd, 0x016d }, { 0x02fe, 0x015d }, { 0x03a2, 0x0138 }, { 0x03a3, 0x0156 }, { 0x03a5, 0x0128 }, { 0x03a6, 0x013b }, { 0x03aa, 0x0112 }, { 0x03ab, 0x0122 }, { 0x03ac, 0x0166 }, { 0x03b3, 0x0157 }, { 0x03b5, 0x0129 }, { 0x03b6, 0x013c }, { 0x03ba, 0x0113 }, { 0x03bb, 0x0123 }, { 0x03bc, 0x0167 }, { 0x03bd, 0x014a }, { 0x03bf, 0x014b }, { 0x03c0, 0x0100 }, { 0x03c7, 0x012e }, { 0x03cc, 0x0116 }, { 0x03cf, 0x012a }, { 0x03d1, 0x0145 }, { 0x03d2, 0x014c }, { 0x03d3, 0x0136 }, { 0x03d9, 0x0172 }, { 0x03dd, 0x0168 }, { 0x03de, 0x016a }, { 0x03e0, 0x0101 }, { 0x03e7, 0x012f }, { 0x03ec, 0x0117 }, { 0x03ef, 0x012b }, { 0x03f1, 0x0146 }, { 0x03f2, 0x014d }, { 0x03f3, 0x0137 }, { 0x03f9, 0x0173 }, { 0x03fd, 0x0169 }, { 0x03fe, 0x016b }, { 0x047e, 0x203e }, { 0x04a1, 0x3002 }, { 0x04a2, 0x300c }, { 0x04a3, 0x300d }, { 0x04a4, 0x3001 }, { 0x04a5, 0x30fb }, { 0x04a6, 0x30f2 }, { 0x04a7, 0x30a1 }, { 0x04a8, 0x30a3 }, { 0x04a9, 0x30a5 }, { 0x04aa, 0x30a7 }, { 0x04ab, 0x30a9 }, { 0x04ac, 0x30e3 }, { 0x04ad, 0x30e5 }, { 0x04ae, 0x30e7 }, { 0x04af, 0x30c3 }, { 0x04b0, 0x30fc }, { 0x04b1, 0x30a2 }, { 0x04b2, 0x30a4 }, { 0x04b3, 0x30a6 }, { 0x04b4, 0x30a8 }, { 0x04b5, 0x30aa }, { 0x04b6, 0x30ab }, { 0x04b7, 0x30ad }, { 0x04b8, 0x30af }, { 0x04b9, 0x30b1 }, { 0x04ba, 0x30b3 }, { 0x04bb, 0x30b5 }, { 0x04bc, 0x30b7 }, { 0x04bd, 0x30b9 }, { 0x04be, 0x30bb }, { 0x04bf, 0x30bd }, { 0x04c0, 0x30bf }, { 0x04c1, 0x30c1 }, { 0x04c2, 0x30c4 }, { 0x04c3, 0x30c6 }, { 0x04c4, 0x30c8 }, { 0x04c5, 0x30ca }, { 0x04c6, 0x30cb }, { 0x04c7, 0x30cc }, { 0x04c8, 0x30cd }, { 0x04c9, 0x30ce }, { 0x04ca, 0x30cf }, { 0x04cb, 0x30d2 }, { 0x04cc, 0x30d5 }, { 0x04cd, 0x30d8 }, { 0x04ce, 0x30db }, { 0x04cf, 0x30de }, { 0x04d0, 0x30df }, { 0x04d1, 0x30e0 }, { 0x04d2, 0x30e1 }, { 0x04d3, 0x30e2 }, { 0x04d4, 0x30e4 }, { 0x04d5, 0x30e6 }, { 0x04d6, 0x30e8 }, { 0x04d7, 0x30e9 }, { 0x04d8, 0x30ea }, { 0x04d9, 0x30eb }, { 0x04da, 0x30ec }, { 0x04db, 0x30ed }, { 0x04dc, 0x30ef }, { 0x04dd, 0x30f3 }, { 0x04de, 0x309b }, { 0x04df, 0x309c }, { 0x05ac, 0x060c }, { 0x05bb, 0x061b }, { 0x05bf, 0x061f }, { 0x05c1, 0x0621 }, { 0x05c2, 0x0622 }, { 0x05c3, 0x0623 }, { 0x05c4, 0x0624 }, { 0x05c5, 0x0625 }, { 0x05c6, 0x0626 }, { 0x05c7, 0x0627 }, { 0x05c8, 0x0628 }, { 0x05c9, 0x0629 }, { 0x05ca, 0x062a }, { 0x05cb, 0x062b }, { 0x05cc, 0x062c }, { 0x05cd, 0x062d }, { 0x05ce, 0x062e }, { 0x05cf, 0x062f }, { 0x05d0, 0x0630 }, { 0x05d1, 0x0631 }, { 0x05d2, 0x0632 }, { 0x05d3, 0x0633 }, { 0x05d4, 0x0634 }, { 0x05d5, 0x0635 }, { 0x05d6, 0x0636 }, { 0x05d7, 0x0637 }, { 0x05d8, 0x0638 }, { 0x05d9, 0x0639 }, { 0x05da, 0x063a }, { 0x05e0, 0x0640 }, { 0x05e1, 0x0641 }, { 0x05e2, 0x0642 }, { 0x05e3, 0x0643 }, { 0x05e4, 0x0644 }, { 0x05e5, 0x0645 }, { 0x05e6, 0x0646 }, { 0x05e7, 0x0647 }, { 0x05e8, 0x0648 }, { 0x05e9, 0x0649 }, { 0x05ea, 0x064a }, { 0x05eb, 0x064b }, { 0x05ec, 0x064c }, { 0x05ed, 0x064d }, { 0x05ee, 0x064e }, { 0x05ef, 0x064f }, { 0x05f0, 0x0650 }, { 0x05f1, 0x0651 }, { 0x05f2, 0x0652 }, { 0x06a1, 0x0452 }, { 0x06a2, 0x0453 }, { 0x06a3, 0x0451 }, { 0x06a4, 0x0454 }, { 0x06a5, 0x0455 }, { 0x06a6, 0x0456 }, { 0x06a7, 0x0457 }, { 0x06a8, 0x0458 }, { 0x06a9, 0x0459 }, { 0x06aa, 0x045a }, { 0x06ab, 0x045b }, { 0x06ac, 0x045c }, { 0x06ae, 0x045e }, { 0x06af, 0x045f }, { 0x06b0, 0x2116 }, { 0x06b1, 0x0402 }, { 0x06b2, 0x0403 }, { 0x06b3, 0x0401 }, { 0x06b4, 0x0404 }, { 0x06b5, 0x0405 }, { 0x06b6, 0x0406 }, { 0x06b7, 0x0407 }, { 0x06b8, 0x0408 }, { 0x06b9, 0x0409 }, { 0x06ba, 0x040a }, { 0x06bb, 0x040b }, { 0x06bc, 0x040c }, { 0x06be, 0x040e }, { 0x06bf, 0x040f }, { 0x06c0, 0x044e }, { 0x06c1, 0x0430 }, { 0x06c2, 0x0431 }, { 0x06c3, 0x0446 }, { 0x06c4, 0x0434 }, { 0x06c5, 0x0435 }, { 0x06c6, 0x0444 }, { 0x06c7, 0x0433 }, { 0x06c8, 0x0445 }, { 0x06c9, 0x0438 }, { 0x06ca, 0x0439 }, { 0x06cb, 0x043a }, { 0x06cc, 0x043b }, { 0x06cd, 0x043c }, { 0x06ce, 0x043d }, { 0x06cf, 0x043e }, { 0x06d0, 0x043f }, { 0x06d1, 0x044f }, { 0x06d2, 0x0440 }, { 0x06d3, 0x0441 }, { 0x06d4, 0x0442 }, { 0x06d5, 0x0443 }, { 0x06d6, 0x0436 }, { 0x06d7, 0x0432 }, { 0x06d8, 0x044c }, { 0x06d9, 0x044b }, { 0x06da, 0x0437 }, { 0x06db, 0x0448 }, { 0x06dc, 0x044d }, { 0x06dd, 0x0449 }, { 0x06de, 0x0447 }, { 0x06df, 0x044a }, { 0x06e0, 0x042e }, { 0x06e1, 0x0410 }, { 0x06e2, 0x0411 }, { 0x06e3, 0x0426 }, { 0x06e4, 0x0414 }, { 0x06e5, 0x0415 }, { 0x06e6, 0x0424 }, { 0x06e7, 0x0413 }, { 0x06e8, 0x0425 }, { 0x06e9, 0x0418 }, { 0x06ea, 0x0419 }, { 0x06eb, 0x041a }, { 0x06ec, 0x041b }, { 0x06ed, 0x041c }, { 0x06ee, 0x041d }, { 0x06ef, 0x041e }, { 0x06f0, 0x041f }, { 0x06f1, 0x042f }, { 0x06f2, 0x0420 }, { 0x06f3, 0x0421 }, { 0x06f4, 0x0422 }, { 0x06f5, 0x0423 }, { 0x06f6, 0x0416 }, { 0x06f7, 0x0412 }, { 0x06f8, 0x042c }, { 0x06f9, 0x042b }, { 0x06fa, 0x0417 }, { 0x06fb, 0x0428 }, { 0x06fc, 0x042d }, { 0x06fd, 0x0429 }, { 0x06fe, 0x0427 }, { 0x06ff, 0x042a }, { 0x07a1, 0x0386 }, { 0x07a2, 0x0388 }, { 0x07a3, 0x0389 }, { 0x07a4, 0x038a }, { 0x07a5, 0x03aa }, { 0x07a7, 0x038c }, { 0x07a8, 0x038e }, { 0x07a9, 0x03ab }, { 0x07ab, 0x038f }, { 0x07ae, 0x0385 }, { 0x07af, 0x2015 }, { 0x07b1, 0x03ac }, { 0x07b2, 0x03ad }, { 0x07b3, 0x03ae }, { 0x07b4, 0x03af }, { 0x07b5, 0x03ca }, { 0x07b6, 0x0390 }, { 0x07b7, 0x03cc }, { 0x07b8, 0x03cd }, { 0x07b9, 0x03cb }, { 0x07ba, 0x03b0 }, { 0x07bb, 0x03ce }, { 0x07c1, 0x0391 }, { 0x07c2, 0x0392 }, { 0x07c3, 0x0393 }, { 0x07c4, 0x0394 }, { 0x07c5, 0x0395 }, { 0x07c6, 0x0396 }, { 0x07c7, 0x0397 }, { 0x07c8, 0x0398 }, { 0x07c9, 0x0399 }, { 0x07ca, 0x039a }, { 0x07cb, 0x039b }, { 0x07cc, 0x039c }, { 0x07cd, 0x039d }, { 0x07ce, 0x039e }, { 0x07cf, 0x039f }, { 0x07d0, 0x03a0 }, { 0x07d1, 0x03a1 }, { 0x07d2, 0x03a3 }, { 0x07d4, 0x03a4 }, { 0x07d5, 0x03a5 }, { 0x07d6, 0x03a6 }, { 0x07d7, 0x03a7 }, { 0x07d8, 0x03a8 }, { 0x07d9, 0x03a9 }, { 0x07e1, 0x03b1 }, { 0x07e2, 0x03b2 }, { 0x07e3, 0x03b3 }, { 0x07e4, 0x03b4 }, { 0x07e5, 0x03b5 }, { 0x07e6, 0x03b6 }, { 0x07e7, 0x03b7 }, { 0x07e8, 0x03b8 }, { 0x07e9, 0x03b9 }, { 0x07ea, 0x03ba }, { 0x07eb, 0x03bb }, { 0x07ec, 0x03bc }, { 0x07ed, 0x03bd }, { 0x07ee, 0x03be }, { 0x07ef, 0x03bf }, { 0x07f0, 0x03c0 }, { 0x07f1, 0x03c1 }, { 0x07f2, 0x03c3 }, { 0x07f3, 0x03c2 }, { 0x07f4, 0x03c4 }, { 0x07f5, 0x03c5 }, { 0x07f6, 0x03c6 }, { 0x07f7, 0x03c7 }, { 0x07f8, 0x03c8 }, { 0x07f9, 0x03c9 }, { 0x08a1, 0x23b7 }, { 0x08a2, 0x250c }, { 0x08a3, 0x2500 }, { 0x08a4, 0x2320 }, { 0x08a5, 0x2321 }, { 0x08a6, 0x2502 }, { 0x08a7, 0x23a1 }, { 0x08a8, 0x23a3 }, { 0x08a9, 0x23a4 }, { 0x08aa, 0x23a6 }, { 0x08ab, 0x239b }, { 0x08ac, 0x239d }, { 0x08ad, 0x239e }, { 0x08ae, 0x23a0 }, { 0x08af, 0x23a8 }, { 0x08b0, 0x23ac }, { 0x08bc, 0x2264 }, { 0x08bd, 0x2260 }, { 0x08be, 0x2265 }, { 0x08bf, 0x222b }, { 0x08c0, 0x2234 }, { 0x08c1, 0x221d }, { 0x08c2, 0x221e }, { 0x08c5, 0x2207 }, { 0x08c8, 0x223c }, { 0x08c9, 0x2243 }, { 0x08cd, 0x21d4 }, { 0x08ce, 0x21d2 }, { 0x08cf, 0x2261 }, { 0x08d6, 0x221a }, { 0x08da, 0x2282 }, { 0x08db, 0x2283 }, { 0x08dc, 0x2229 }, { 0x08dd, 0x222a }, { 0x08de, 0x2227 }, { 0x08df, 0x2228 }, { 0x08ef, 0x2202 }, { 0x08f6, 0x0192 }, { 0x08fb, 0x2190 }, { 0x08fc, 0x2191 }, { 0x08fd, 0x2192 }, { 0x08fe, 0x2193 }, { 0x09e0, 0x25c6 }, { 0x09e1, 0x2592 }, { 0x09e2, 0x2409 }, { 0x09e3, 0x240c }, { 0x09e4, 0x240d }, { 0x09e5, 0x240a }, { 0x09e8, 0x2424 }, { 0x09e9, 0x240b }, { 0x09ea, 0x2518 }, { 0x09eb, 0x2510 }, { 0x09ec, 0x250c }, { 0x09ed, 0x2514 }, { 0x09ee, 0x253c }, { 0x09ef, 0x23ba }, { 0x09f0, 0x23bb }, { 0x09f1, 0x2500 }, { 0x09f2, 0x23bc }, { 0x09f3, 0x23bd }, { 0x09f4, 0x251c }, { 0x09f5, 0x2524 }, { 0x09f6, 0x2534 }, { 0x09f7, 0x252c }, { 0x09f8, 0x2502 }, { 0x0aa1, 0x2003 }, { 0x0aa2, 0x2002 }, { 0x0aa3, 0x2004 }, { 0x0aa4, 0x2005 }, { 0x0aa5, 0x2007 }, { 0x0aa6, 0x2008 }, { 0x0aa7, 0x2009 }, { 0x0aa8, 0x200a }, { 0x0aa9, 0x2014 }, { 0x0aaa, 0x2013 }, { 0x0aae, 0x2026 }, { 0x0aaf, 0x2025 }, { 0x0ab0, 0x2153 }, { 0x0ab1, 0x2154 }, { 0x0ab2, 0x2155 }, { 0x0ab3, 0x2156 }, { 0x0ab4, 0x2157 }, { 0x0ab5, 0x2158 }, { 0x0ab6, 0x2159 }, { 0x0ab7, 0x215a }, { 0x0ab8, 0x2105 }, { 0x0abb, 0x2012 }, { 0x0abc, 0x2329 }, { 0x0abe, 0x232a }, { 0x0ac3, 0x215b }, { 0x0ac4, 0x215c }, { 0x0ac5, 0x215d }, { 0x0ac6, 0x215e }, { 0x0ac9, 0x2122 }, { 0x0aca, 0x2613 }, { 0x0acc, 0x25c1 }, { 0x0acd, 0x25b7 }, { 0x0ace, 0x25cb }, { 0x0acf, 0x25af }, { 0x0ad0, 0x2018 }, { 0x0ad1, 0x2019 }, { 0x0ad2, 0x201c }, { 0x0ad3, 0x201d }, { 0x0ad4, 0x211e }, { 0x0ad6, 0x2032 }, { 0x0ad7, 0x2033 }, { 0x0ad9, 0x271d }, { 0x0adb, 0x25ac }, { 0x0adc, 0x25c0 }, { 0x0add, 0x25b6 }, { 0x0ade, 0x25cf }, { 0x0adf, 0x25ae }, { 0x0ae0, 0x25e6 }, { 0x0ae1, 0x25ab }, { 0x0ae2, 0x25ad }, { 0x0ae3, 0x25b3 }, { 0x0ae4, 0x25bd }, { 0x0ae5, 0x2606 }, { 0x0ae6, 0x2022 }, { 0x0ae7, 0x25aa }, { 0x0ae8, 0x25b2 }, { 0x0ae9, 0x25bc }, { 0x0aea, 0x261c }, { 0x0aeb, 0x261e }, { 0x0aec, 0x2663 }, { 0x0aed, 0x2666 }, { 0x0aee, 0x2665 }, { 0x0af0, 0x2720 }, { 0x0af1, 0x2020 }, { 0x0af2, 0x2021 }, { 0x0af3, 0x2713 }, { 0x0af4, 0x2717 }, { 0x0af5, 0x266f }, { 0x0af6, 0x266d }, { 0x0af7, 0x2642 }, { 0x0af8, 0x2640 }, { 0x0af9, 0x260e }, { 0x0afa, 0x2315 }, { 0x0afb, 0x2117 }, { 0x0afc, 0x2038 }, { 0x0afd, 0x201a }, { 0x0afe, 0x201e }, { 0x0ba3, 0x003c }, { 0x0ba6, 0x003e }, { 0x0ba8, 0x2228 }, { 0x0ba9, 0x2227 }, { 0x0bc0, 0x00af }, { 0x0bc2, 0x22a5 }, { 0x0bc3, 0x2229 }, { 0x0bc4, 0x230a }, { 0x0bc6, 0x005f }, { 0x0bca, 0x2218 }, { 0x0bcc, 0x2395 }, { 0x0bce, 0x22a4 }, { 0x0bcf, 0x25cb }, { 0x0bd3, 0x2308 }, { 0x0bd6, 0x222a }, { 0x0bd8, 0x2283 }, { 0x0bda, 0x2282 }, { 0x0bdc, 0x22a2 }, { 0x0bfc, 0x22a3 }, { 0x0cdf, 0x2017 }, { 0x0ce0, 0x05d0 }, { 0x0ce1, 0x05d1 }, { 0x0ce2, 0x05d2 }, { 0x0ce3, 0x05d3 }, { 0x0ce4, 0x05d4 }, { 0x0ce5, 0x05d5 }, { 0x0ce6, 0x05d6 }, { 0x0ce7, 0x05d7 }, { 0x0ce8, 0x05d8 }, { 0x0ce9, 0x05d9 }, { 0x0cea, 0x05da }, { 0x0ceb, 0x05db }, { 0x0cec, 0x05dc }, { 0x0ced, 0x05dd }, { 0x0cee, 0x05de }, { 0x0cef, 0x05df }, { 0x0cf0, 0x05e0 }, { 0x0cf1, 0x05e1 }, { 0x0cf2, 0x05e2 }, { 0x0cf3, 0x05e3 }, { 0x0cf4, 0x05e4 }, { 0x0cf5, 0x05e5 }, { 0x0cf6, 0x05e6 }, { 0x0cf7, 0x05e7 }, { 0x0cf8, 0x05e8 }, { 0x0cf9, 0x05e9 }, { 0x0cfa, 0x05ea }, { 0x0da1, 0x0e01 }, { 0x0da2, 0x0e02 }, { 0x0da3, 0x0e03 }, { 0x0da4, 0x0e04 }, { 0x0da5, 0x0e05 }, { 0x0da6, 0x0e06 }, { 0x0da7, 0x0e07 }, { 0x0da8, 0x0e08 }, { 0x0da9, 0x0e09 }, { 0x0daa, 0x0e0a }, { 0x0dab, 0x0e0b }, { 0x0dac, 0x0e0c }, { 0x0dad, 0x0e0d }, { 0x0dae, 0x0e0e }, { 0x0daf, 0x0e0f }, { 0x0db0, 0x0e10 }, { 0x0db1, 0x0e11 }, { 0x0db2, 0x0e12 }, { 0x0db3, 0x0e13 }, { 0x0db4, 0x0e14 }, { 0x0db5, 0x0e15 }, { 0x0db6, 0x0e16 }, { 0x0db7, 0x0e17 }, { 0x0db8, 0x0e18 }, { 0x0db9, 0x0e19 }, { 0x0dba, 0x0e1a }, { 0x0dbb, 0x0e1b }, { 0x0dbc, 0x0e1c }, { 0x0dbd, 0x0e1d }, { 0x0dbe, 0x0e1e }, { 0x0dbf, 0x0e1f }, { 0x0dc0, 0x0e20 }, { 0x0dc1, 0x0e21 }, { 0x0dc2, 0x0e22 }, { 0x0dc3, 0x0e23 }, { 0x0dc4, 0x0e24 }, { 0x0dc5, 0x0e25 }, { 0x0dc6, 0x0e26 }, { 0x0dc7, 0x0e27 }, { 0x0dc8, 0x0e28 }, { 0x0dc9, 0x0e29 }, { 0x0dca, 0x0e2a }, { 0x0dcb, 0x0e2b }, { 0x0dcc, 0x0e2c }, { 0x0dcd, 0x0e2d }, { 0x0dce, 0x0e2e }, { 0x0dcf, 0x0e2f }, { 0x0dd0, 0x0e30 }, { 0x0dd1, 0x0e31 }, { 0x0dd2, 0x0e32 }, { 0x0dd3, 0x0e33 }, { 0x0dd4, 0x0e34 }, { 0x0dd5, 0x0e35 }, { 0x0dd6, 0x0e36 }, { 0x0dd7, 0x0e37 }, { 0x0dd8, 0x0e38 }, { 0x0dd9, 0x0e39 }, { 0x0dda, 0x0e3a }, { 0x0ddf, 0x0e3f }, { 0x0de0, 0x0e40 }, { 0x0de1, 0x0e41 }, { 0x0de2, 0x0e42 }, { 0x0de3, 0x0e43 }, { 0x0de4, 0x0e44 }, { 0x0de5, 0x0e45 }, { 0x0de6, 0x0e46 }, { 0x0de7, 0x0e47 }, { 0x0de8, 0x0e48 }, { 0x0de9, 0x0e49 }, { 0x0dea, 0x0e4a }, { 0x0deb, 0x0e4b }, { 0x0dec, 0x0e4c }, { 0x0ded, 0x0e4d }, { 0x0df0, 0x0e50 }, { 0x0df1, 0x0e51 }, { 0x0df2, 0x0e52 }, { 0x0df3, 0x0e53 }, { 0x0df4, 0x0e54 }, { 0x0df5, 0x0e55 }, { 0x0df6, 0x0e56 }, { 0x0df7, 0x0e57 }, { 0x0df8, 0x0e58 }, { 0x0df9, 0x0e59 }, { 0x0ea1, 0x3131 }, { 0x0ea2, 0x3132 }, { 0x0ea3, 0x3133 }, { 0x0ea4, 0x3134 }, { 0x0ea5, 0x3135 }, { 0x0ea6, 0x3136 }, { 0x0ea7, 0x3137 }, { 0x0ea8, 0x3138 }, { 0x0ea9, 0x3139 }, { 0x0eaa, 0x313a }, { 0x0eab, 0x313b }, { 0x0eac, 0x313c }, { 0x0ead, 0x313d }, { 0x0eae, 0x313e }, { 0x0eaf, 0x313f }, { 0x0eb0, 0x3140 }, { 0x0eb1, 0x3141 }, { 0x0eb2, 0x3142 }, { 0x0eb3, 0x3143 }, { 0x0eb4, 0x3144 }, { 0x0eb5, 0x3145 }, { 0x0eb6, 0x3146 }, { 0x0eb7, 0x3147 }, { 0x0eb8, 0x3148 }, { 0x0eb9, 0x3149 }, { 0x0eba, 0x314a }, { 0x0ebb, 0x314b }, { 0x0ebc, 0x314c }, { 0x0ebd, 0x314d }, { 0x0ebe, 0x314e }, { 0x0ebf, 0x314f }, { 0x0ec0, 0x3150 }, { 0x0ec1, 0x3151 }, { 0x0ec2, 0x3152 }, { 0x0ec3, 0x3153 }, { 0x0ec4, 0x3154 }, { 0x0ec5, 0x3155 }, { 0x0ec6, 0x3156 }, { 0x0ec7, 0x3157 }, { 0x0ec8, 0x3158 }, { 0x0ec9, 0x3159 }, { 0x0eca, 0x315a }, { 0x0ecb, 0x315b }, { 0x0ecc, 0x315c }, { 0x0ecd, 0x315d }, { 0x0ece, 0x315e }, { 0x0ecf, 0x315f }, { 0x0ed0, 0x3160 }, { 0x0ed1, 0x3161 }, { 0x0ed2, 0x3162 }, { 0x0ed3, 0x3163 }, { 0x0ed4, 0x11a8 }, { 0x0ed5, 0x11a9 }, { 0x0ed6, 0x11aa }, { 0x0ed7, 0x11ab }, { 0x0ed8, 0x11ac }, { 0x0ed9, 0x11ad }, { 0x0eda, 0x11ae }, { 0x0edb, 0x11af }, { 0x0edc, 0x11b0 }, { 0x0edd, 0x11b1 }, { 0x0ede, 0x11b2 }, { 0x0edf, 0x11b3 }, { 0x0ee0, 0x11b4 }, { 0x0ee1, 0x11b5 }, { 0x0ee2, 0x11b6 }, { 0x0ee3, 0x11b7 }, { 0x0ee4, 0x11b8 }, { 0x0ee5, 0x11b9 }, { 0x0ee6, 0x11ba }, { 0x0ee7, 0x11bb }, { 0x0ee8, 0x11bc }, { 0x0ee9, 0x11bd }, { 0x0eea, 0x11be }, { 0x0eeb, 0x11bf }, { 0x0eec, 0x11c0 }, { 0x0eed, 0x11c1 }, { 0x0eee, 0x11c2 }, { 0x0eef, 0x316d }, { 0x0ef0, 0x3171 }, { 0x0ef1, 0x3178 }, { 0x0ef2, 0x317f }, { 0x0ef3, 0x3181 }, { 0x0ef4, 0x3184 }, { 0x0ef5, 0x3186 }, { 0x0ef6, 0x318d }, { 0x0ef7, 0x318e }, { 0x0ef8, 0x11eb }, { 0x0ef9, 0x11f0 }, { 0x0efa, 0x11f9 }, { 0x0eff, 0x20a9 }, { 0x13a4, 0x20ac }, { 0x13bc, 0x0152 }, { 0x13bd, 0x0153 }, { 0x13be, 0x0178 }, { 0x20ac, 0x20ac }, { 0xfe50, '`' }, { 0xfe51, 0x00b4 }, { 0xfe52, '^' }, { 0xfe53, '~' }, { 0xfe54, 0x00af }, { 0xfe55, 0x02d8 }, { 0xfe56, 0x02d9 }, { 0xfe57, 0x00a8 }, { 0xfe58, 0x02da }, { 0xfe59, 0x02dd }, { 0xfe5a, 0x02c7 }, { 0xfe5b, 0x00b8 }, { 0xfe5c, 0x02db }, { 0xfe5d, 0x037a }, { 0xfe5e, 0x309b }, { 0xfe5f, 0x309c }, { 0xfe63, '/' }, { 0xfe64, 0x02bc }, { 0xfe65, 0x02bd }, { 0xfe66, 0x02f5 }, { 0xfe67, 0x02f3 }, { 0xfe68, 0x02cd }, { 0xfe69, 0xa788 }, { 0xfe6a, 0x02f7 }, { 0xfe6e, ',' }, { 0xfe6f, 0x00a4 }, { 0xfe80, 'a' }, /* XK_dead_a */ { 0xfe81, 'A' }, /* XK_dead_A */ { 0xfe82, 'e' }, /* XK_dead_e */ { 0xfe83, 'E' }, /* XK_dead_E */ { 0xfe84, 'i' }, /* XK_dead_i */ { 0xfe85, 'I' }, /* XK_dead_I */ { 0xfe86, 'o' }, /* XK_dead_o */ { 0xfe87, 'O' }, /* XK_dead_O */ { 0xfe88, 'u' }, /* XK_dead_u */ { 0xfe89, 'U' }, /* XK_dead_U */ { 0xfe8a, 0x0259 }, { 0xfe8b, 0x018f }, { 0xfe8c, 0x00b5 }, { 0xfe90, '_' }, { 0xfe91, 0x02c8 }, { 0xfe92, 0x02cc }, { 0xff80 /*XKB_KEY_KP_Space*/, ' ' }, { 0xff95 /*XKB_KEY_KP_7*/, 0x0037 }, { 0xff96 /*XKB_KEY_KP_4*/, 0x0034 }, { 0xff97 /*XKB_KEY_KP_8*/, 0x0038 }, { 0xff98 /*XKB_KEY_KP_6*/, 0x0036 }, { 0xff99 /*XKB_KEY_KP_2*/, 0x0032 }, { 0xff9a /*XKB_KEY_KP_9*/, 0x0039 }, { 0xff9b /*XKB_KEY_KP_3*/, 0x0033 }, { 0xff9c /*XKB_KEY_KP_1*/, 0x0031 }, { 0xff9d /*XKB_KEY_KP_5*/, 0x0035 }, { 0xff9e /*XKB_KEY_KP_0*/, 0x0030 }, { 0xffaa /*XKB_KEY_KP_Multiply*/, '*' }, { 0xffab /*XKB_KEY_KP_Add*/, '+' }, { 0xffac /*XKB_KEY_KP_Separator*/, ',' }, { 0xffad /*XKB_KEY_KP_Subtract*/, '-' }, { 0xffae /*XKB_KEY_KP_Decimal*/, '.' }, { 0xffaf /*XKB_KEY_KP_Divide*/, '/' }, { 0xffb0 /*XKB_KEY_KP_0*/, 0x0030 }, { 0xffb1 /*XKB_KEY_KP_1*/, 0x0031 }, { 0xffb2 /*XKB_KEY_KP_2*/, 0x0032 }, { 0xffb3 /*XKB_KEY_KP_3*/, 0x0033 }, { 0xffb4 /*XKB_KEY_KP_4*/, 0x0034 }, { 0xffb5 /*XKB_KEY_KP_5*/, 0x0035 }, { 0xffb6 /*XKB_KEY_KP_6*/, 0x0036 }, { 0xffb7 /*XKB_KEY_KP_7*/, 0x0037 }, { 0xffb8 /*XKB_KEY_KP_8*/, 0x0038 }, { 0xffb9 /*XKB_KEY_KP_9*/, 0x0039 }, { 0xffbd /*XKB_KEY_KP_Equal*/, '=' } }; _SOKOL_PRIVATE int _sapp_x11_error_handler(Display* display, XErrorEvent* event) { _sapp_x11_error_code = event->error_code; return 0; } _SOKOL_PRIVATE void _sapp_x11_grab_error_handler(void) { _sapp_x11_error_code = Success; XSetErrorHandler(_sapp_x11_error_handler); } _SOKOL_PRIVATE void _sapp_x11_release_error_handler(void) { XSync(_sapp_x11_display, False); XSetErrorHandler(NULL); } _SOKOL_PRIVATE void _sapp_x11_init_extensions(void) { _sapp_x11_UTF8_STRING = XInternAtom(_sapp_x11_display, "UTF8_STRING", False); _sapp_x11_WM_PROTOCOLS = XInternAtom(_sapp_x11_display, "WM_PROTOCOLS", False); _sapp_x11_WM_DELETE_WINDOW = XInternAtom(_sapp_x11_display, "WM_DELETE_WINDOW", False); _sapp_x11_WM_STATE = XInternAtom(_sapp_x11_display, "WM_STATE", False); _sapp_x11_NET_WM_NAME = XInternAtom(_sapp_x11_display, "_NET_WM_NAME", False); _sapp_x11_NET_WM_ICON_NAME = XInternAtom(_sapp_x11_display, "_NET_WM_ICON_NAME", False); } _SOKOL_PRIVATE void _sapp_x11_query_system_dpi(void) { /* from GLFW: NOTE: Default to the display-wide DPI as we don't currently have a policy for which monitor a window is considered to be on _sapp_x11_dpi = DisplayWidth(_sapp_x11_display, _sapp_x11_screen) * 25.4f / DisplayWidthMM(_sapp_x11_display, _sapp_x11_screen); NOTE: Basing the scale on Xft.dpi where available should provide the most consistent user experience (matches Qt, Gtk, etc), although not always the most accurate one */ char* rms = XResourceManagerString(_sapp_x11_display); if (rms) { XrmDatabase db = XrmGetStringDatabase(rms); if (db) { XrmValue value; char* type = NULL; if (XrmGetResource(db, "Xft.dpi", "Xft.Dpi", &type, &value)) { if (type && strcmp(type, "String") == 0) { _sapp_x11_dpi = atof(value.addr); } } XrmDestroyDatabase(db); } } } _SOKOL_PRIVATE bool _sapp_glx_has_ext(const char* ext, const char* extensions) { SOKOL_ASSERT(ext); const char* start = extensions; while (true) { const char* where = strstr(start, ext); if (!where) { return false; } const char* terminator = where + strlen(ext); if ((where == start) || (*(where - 1) == ' ')) { if (*terminator == ' ' || *terminator == '\0') { break; } } start = terminator; } return true; } _SOKOL_PRIVATE bool _sapp_glx_extsupported(const char* ext, const char* extensions) { if (extensions) { return _sapp_glx_has_ext(ext, extensions); } else { return false; } } _SOKOL_PRIVATE void* _sapp_glx_getprocaddr(const char* procname) { if (_sapp_glx_GetProcAddress) { return (void*) _sapp_glx_GetProcAddress((const GLubyte*) procname); } else if (_sapp_glx_GetProcAddressARB) { return (void*) _sapp_glx_GetProcAddressARB((const GLubyte*) procname); } else { return dlsym(_sapp_glx_libgl, procname); } } _SOKOL_PRIVATE void _sapp_glx_init() { const char* sonames[] = { "libGL.so.1", "libGL.so", 0 }; for (int i = 0; sonames[i]; i++) { _sapp_glx_libgl = dlopen(sonames[i], RTLD_LAZY|RTLD_GLOBAL); if (_sapp_glx_libgl) { break; } } if (!_sapp_glx_libgl) { _sapp_fail("GLX: failed to load libGL"); } _sapp_glx_GetFBConfigs = (PFNGLXGETFBCONFIGSPROC) dlsym(_sapp_glx_libgl, "glXGetFBConfigs"); _sapp_glx_GetFBConfigAttrib = (PFNGLXGETFBCONFIGATTRIBPROC) dlsym(_sapp_glx_libgl, "glXGetFBConfigAttrib"); _sapp_glx_GetClientString = (PFNGLXGETCLIENTSTRINGPROC) dlsym(_sapp_glx_libgl, "glXGetClientString"); _sapp_glx_QueryExtension = (PFNGLXQUERYEXTENSIONPROC) dlsym(_sapp_glx_libgl, "glXQueryExtension"); _sapp_glx_QueryVersion = (PFNGLXQUERYVERSIONPROC) dlsym(_sapp_glx_libgl, "glXQueryVersion"); _sapp_glx_DestroyContext = (PFNGLXDESTROYCONTEXTPROC) dlsym(_sapp_glx_libgl, "glXDestroyContext"); _sapp_glx_MakeCurrent = (PFNGLXMAKECURRENTPROC) dlsym(_sapp_glx_libgl, "glXMakeCurrent"); _sapp_glx_SwapBuffers = (PFNGLXSWAPBUFFERSPROC) dlsym(_sapp_glx_libgl, "glXSwapBuffers"); _sapp_glx_QueryExtensionsString = (PFNGLXQUERYEXTENSIONSSTRINGPROC) dlsym(_sapp_glx_libgl, "glXQueryExtensionsString"); _sapp_glx_CreateNewContext = (PFNGLXCREATENEWCONTEXTPROC) dlsym(_sapp_glx_libgl, "glXCreateNewContext"); _sapp_glx_CreateWindow = (PFNGLXCREATEWINDOWPROC) dlsym(_sapp_glx_libgl, "glXCreateWindow"); _sapp_glx_DestroyWindow = (PFNGLXDESTROYWINDOWPROC) dlsym(_sapp_glx_libgl, "glXDestroyWindow"); _sapp_glx_GetProcAddress = (PFNGLXGETPROCADDRESSPROC) dlsym(_sapp_glx_libgl, "glXGetProcAddress"); _sapp_glx_GetProcAddressARB = (PFNGLXGETPROCADDRESSPROC) dlsym(_sapp_glx_libgl, "glXGetProcAddressARB"); _sapp_glx_GetVisualFromFBConfig = (PFNGLXGETVISUALFROMFBCONFIGPROC) dlsym(_sapp_glx_libgl, "glXGetVisualFromFBConfig"); if (!_sapp_glx_GetFBConfigs || !_sapp_glx_GetFBConfigAttrib || !_sapp_glx_GetClientString || !_sapp_glx_QueryExtension || !_sapp_glx_QueryVersion || !_sapp_glx_DestroyContext || !_sapp_glx_MakeCurrent || !_sapp_glx_SwapBuffers || !_sapp_glx_QueryExtensionsString || !_sapp_glx_CreateNewContext || !_sapp_glx_CreateWindow || !_sapp_glx_DestroyWindow || !_sapp_glx_GetProcAddress || !_sapp_glx_GetProcAddressARB || !_sapp_glx_GetVisualFromFBConfig) { _sapp_fail("GLX: failed to load required entry points"); } if (!_sapp_glx_QueryExtension(_sapp_x11_display, &_sapp_glx_errorbase, &_sapp_glx_eventbase)) { _sapp_fail("GLX: GLX extension not found"); } if (!_sapp_glx_QueryVersion(_sapp_x11_display, &_sapp_glx_major, &_sapp_glx_minor)) { _sapp_fail("GLX: Failed to query GLX version"); } if (_sapp_glx_major == 1 && _sapp_glx_minor < 3) { _sapp_fail("GLX: GLX version 1.3 is required"); } const char* exts = _sapp_glx_QueryExtensionsString(_sapp_x11_display, _sapp_x11_screen); if (_sapp_glx_extsupported("GLX_EXT_swap_control", exts)) { _sapp_glx_SwapIntervalEXT = (PFNGLXSWAPINTERVALEXTPROC) _sapp_glx_getprocaddr("glXSwapIntervalEXT"); _sapp_glx_EXT_swap_control = 0 != _sapp_glx_SwapIntervalEXT; } if (_sapp_glx_extsupported("GLX_MESA_swap_control", exts)) { _sapp_glx_SwapIntervalMESA = (PFNGLXSWAPINTERVALMESAPROC) _sapp_glx_getprocaddr("glXSwapIntervalMESA"); _sapp_glx_MESA_swap_control = 0 != _sapp_glx_SwapIntervalMESA; } _sapp_glx_ARB_multisample = _sapp_glx_extsupported("GLX_ARB_multisample", exts); _sapp_glx_ARB_framebuffer_sRGB = _sapp_glx_extsupported("GLX_ARB_framebuffer_sRGB", exts); _sapp_glx_EXT_framebuffer_sRGB = _sapp_glx_extsupported("GLX_EXT_framebuffer_sRGB", exts); if (_sapp_glx_extsupported("GLX_ARB_create_context", exts)) { _sapp_glx_CreateContextAttribsARB = (PFNGLXCREATECONTEXTATTRIBSARBPROC) _sapp_glx_getprocaddr("glXCreateContextAttribsARB"); _sapp_glx_ARB_create_context = 0 != _sapp_glx_CreateContextAttribsARB; } _sapp_glx_ARB_create_context_profile = _sapp_glx_extsupported("GLX_ARB_create_context_profile", exts); } _SOKOL_PRIVATE int _sapp_glx_attrib(GLXFBConfig fbconfig, int attrib) { int value; _sapp_glx_GetFBConfigAttrib(_sapp_x11_display, fbconfig, attrib, &value); return value; } _SOKOL_PRIVATE GLXFBConfig _sapp_glx_choosefbconfig() { GLXFBConfig* native_configs; _sapp_gl_fbconfig* usable_configs; const _sapp_gl_fbconfig* closest; int i, native_count, usable_count; const char* vendor; bool trust_window_bit = true; /* HACK: This is a (hopefully temporary) workaround for Chromium (VirtualBox GL) not setting the window bit on any GLXFBConfigs */ vendor = _sapp_glx_GetClientString(_sapp_x11_display, GLX_VENDOR); if (vendor && strcmp(vendor, "Chromium") == 0) { trust_window_bit = false; } native_configs = _sapp_glx_GetFBConfigs(_sapp_x11_display, _sapp_x11_screen, &native_count); if (!native_configs || !native_count) { _sapp_fail("GLX: No GLXFBConfigs returned"); } usable_configs = (_sapp_gl_fbconfig*) SOKOL_CALLOC(native_count, sizeof(_sapp_gl_fbconfig)); usable_count = 0; for (i = 0; i < native_count; i++) { const GLXFBConfig n = native_configs[i]; _sapp_gl_fbconfig* u = usable_configs + usable_count; _sapp_gl_init_fbconfig(u); /* Only consider RGBA GLXFBConfigs */ if (0 == (_sapp_glx_attrib(n, GLX_RENDER_TYPE) & GLX_RGBA_BIT)) { continue; } /* Only consider window GLXFBConfigs */ if (0 == (_sapp_glx_attrib(n, GLX_DRAWABLE_TYPE) & GLX_WINDOW_BIT)) { if (trust_window_bit) { continue; } } u->red_bits = _sapp_glx_attrib(n, GLX_RED_SIZE); u->green_bits = _sapp_glx_attrib(n, GLX_GREEN_SIZE); u->blue_bits = _sapp_glx_attrib(n, GLX_BLUE_SIZE); u->alpha_bits = _sapp_glx_attrib(n, GLX_ALPHA_SIZE); u->depth_bits = _sapp_glx_attrib(n, GLX_DEPTH_SIZE); u->stencil_bits = _sapp_glx_attrib(n, GLX_STENCIL_SIZE); if (_sapp_glx_attrib(n, GLX_DOUBLEBUFFER)) { u->doublebuffer = true; } if (_sapp_glx_ARB_multisample) { u->samples = _sapp_glx_attrib(n, GLX_SAMPLES); } u->handle = (uintptr_t) n; usable_count++; } _sapp_gl_fbconfig desired; _sapp_gl_init_fbconfig(&desired); desired.red_bits = 8; desired.green_bits = 8; desired.blue_bits = 8; desired.alpha_bits = 8; desired.depth_bits = 24; desired.stencil_bits = 8; desired.doublebuffer = true; desired.samples = _sapp.sample_count > 1 ? _sapp.sample_count : 0; closest = _sapp_gl_choose_fbconfig(&desired, usable_configs, usable_count); GLXFBConfig result = 0; if (closest) { result = (GLXFBConfig) closest->handle; } XFree(native_configs); SOKOL_FREE(usable_configs); return result; } _SOKOL_PRIVATE void _sapp_glx_choose_visual(Visual** visual, int* depth) { GLXFBConfig native = _sapp_glx_choosefbconfig(); if (0 == native) { _sapp_fail("GLX: Failed to find a suitable GLXFBConfig"); } XVisualInfo* result = _sapp_glx_GetVisualFromFBConfig(_sapp_x11_display, native); if (!result) { _sapp_fail("GLX: Failed to retrieve Visual for GLXFBConfig"); } *visual = result->visual; *depth = result->depth; XFree(result); } _SOKOL_PRIVATE void _sapp_glx_create_context(void) { GLXFBConfig native = _sapp_glx_choosefbconfig(); if (0 == native){ _sapp_fail("GLX: Failed to find a suitable GLXFBConfig (2)"); } if (!(_sapp_glx_ARB_create_context && _sapp_glx_ARB_create_context_profile)) { _sapp_fail("GLX: ARB_create_context and ARB_create_context_profile required"); } _sapp_x11_grab_error_handler(); const int attribs[] = { GLX_CONTEXT_MAJOR_VERSION_ARB, 3, GLX_CONTEXT_MINOR_VERSION_ARB, 3, GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB, GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, 0, 0 }; _sapp_glx_ctx = _sapp_glx_CreateContextAttribsARB(_sapp_x11_display, native, NULL, True, attribs); if (!_sapp_glx_ctx) { _sapp_fail("GLX: failed to create GL context"); } _sapp_x11_release_error_handler(); _sapp_glx_window = _sapp_glx_CreateWindow(_sapp_x11_display, native, _sapp_x11_window, NULL); if (!_sapp_glx_window) { _sapp_fail("GLX: failed to create window"); } } _SOKOL_PRIVATE void _sapp_glx_destroy_context(void) { if (_sapp_glx_window) { _sapp_glx_DestroyWindow(_sapp_x11_display, _sapp_glx_window); _sapp_glx_window = 0; } if (_sapp_glx_ctx) { _sapp_glx_DestroyContext(_sapp_x11_display, _sapp_glx_ctx); _sapp_glx_ctx = 0; } } _SOKOL_PRIVATE void _sapp_glx_make_current(void) { _sapp_glx_MakeCurrent(_sapp_x11_display, _sapp_glx_window, _sapp_glx_ctx); } _SOKOL_PRIVATE void _sapp_glx_swap_buffers(void) { _sapp_glx_SwapBuffers(_sapp_x11_display, _sapp_glx_window); } _SOKOL_PRIVATE void _sapp_glx_swapinterval(int interval) { _sapp_glx_make_current(); if (_sapp_glx_EXT_swap_control) { _sapp_glx_SwapIntervalEXT(_sapp_x11_display, _sapp_glx_window, interval); } else if (_sapp_glx_MESA_swap_control) { _sapp_glx_SwapIntervalMESA(interval); } } _SOKOL_PRIVATE void _sapp_x11_update_window_title(void) { Xutf8SetWMProperties(_sapp_x11_display, _sapp_x11_window, _sapp.window_title, _sapp.window_title, NULL, 0, NULL, NULL, NULL); XChangeProperty(_sapp_x11_display, _sapp_x11_window, _sapp_x11_NET_WM_NAME, _sapp_x11_UTF8_STRING, 8, PropModeReplace, (unsigned char*)_sapp.window_title, strlen(_sapp.window_title)); XChangeProperty(_sapp_x11_display, _sapp_x11_window, _sapp_x11_NET_WM_ICON_NAME, _sapp_x11_UTF8_STRING, 8, PropModeReplace, (unsigned char*)_sapp.window_title, strlen(_sapp.window_title)); XFlush(_sapp_x11_display); } _SOKOL_PRIVATE void _sapp_x11_query_window_size(void) { XWindowAttributes attribs; XGetWindowAttributes(_sapp_x11_display, _sapp_x11_window, &attribs); _sapp.window_width = attribs.width; _sapp.window_height = attribs.height; _sapp.framebuffer_width = _sapp.window_width; _sapp.framebuffer_height = _sapp.framebuffer_height; } _SOKOL_PRIVATE void _sapp_x11_create_window(Visual* visual, int depth) { _sapp_x11_colormap = XCreateColormap(_sapp_x11_display, _sapp_x11_root, visual, AllocNone); XSetWindowAttributes wa; memset(&wa, 0, sizeof(wa)); const uint32_t wamask = CWBorderPixel | CWColormap | CWEventMask; wa.colormap = _sapp_x11_colormap; wa.border_pixel = 0; wa.event_mask = StructureNotifyMask | KeyPressMask | KeyReleaseMask | PointerMotionMask | ButtonPressMask | ButtonReleaseMask | ExposureMask | FocusChangeMask | VisibilityChangeMask | EnterWindowMask | LeaveWindowMask | PropertyChangeMask; _sapp_x11_grab_error_handler(); _sapp_x11_window = XCreateWindow(_sapp_x11_display, _sapp_x11_root, 0, 0, _sapp.window_width, _sapp.window_height, 0, /* border width */ depth, /* color depth */ InputOutput, visual, wamask, &wa); _sapp_x11_release_error_handler(); if (!_sapp_x11_window) { _sapp_fail("X11: Failed to create window"); } Atom protocols[] = { _sapp_x11_WM_DELETE_WINDOW }; XSetWMProtocols(_sapp_x11_display, _sapp_x11_window, protocols, 1); XSizeHints* hints = XAllocSizeHints(); hints->flags |= PWinGravity; hints->win_gravity = StaticGravity; XSetWMNormalHints(_sapp_x11_display, _sapp_x11_window, hints); XFree(hints); _sapp_x11_update_window_title(); _sapp_x11_query_window_size(); } _SOKOL_PRIVATE void _sapp_x11_destroy_window(void) { if (_sapp_x11_window) { XUnmapWindow(_sapp_x11_display, _sapp_x11_window); XDestroyWindow(_sapp_x11_display, _sapp_x11_window); _sapp_x11_window = 0; } if (_sapp_x11_colormap) { XFreeColormap(_sapp_x11_display, _sapp_x11_colormap); _sapp_x11_colormap = 0; } XFlush(_sapp_x11_display); } _SOKOL_PRIVATE bool _sapp_x11_window_visible(void) { XWindowAttributes wa; XGetWindowAttributes(_sapp_x11_display, _sapp_x11_window, &wa); return wa.map_state == IsViewable; } _SOKOL_PRIVATE void _sapp_x11_show_window(void) { if (!_sapp_x11_window_visible()) { XMapWindow(_sapp_x11_display, _sapp_x11_window); XRaiseWindow(_sapp_x11_display, _sapp_x11_window); XFlush(_sapp_x11_display); } } _SOKOL_PRIVATE void _sapp_x11_hide_window(void) { XUnmapWindow(_sapp_x11_display, _sapp_x11_window); XFlush(_sapp_x11_display); } _SOKOL_PRIVATE unsigned long _sapp_x11_get_window_property(Atom property, Atom type, unsigned char** value) { Atom actualType; int actualFormat; unsigned long itemCount, bytesAfter; XGetWindowProperty(_sapp_x11_display, _sapp_x11_window, property, 0, LONG_MAX, False, type, &actualType, &actualFormat, &itemCount, &bytesAfter, value); return itemCount; } _SOKOL_PRIVATE int _sapp_x11_get_window_state(void) { int result = WithdrawnState; struct { CARD32 state; Window icon; } *state = NULL; if (_sapp_x11_get_window_property(_sapp_x11_WM_STATE, _sapp_x11_WM_STATE, (unsigned char**)&state) >= 2) { result = state->state; } if (state) { XFree(state); } return result; } _SOKOL_PRIVATE uint32_t _sapp_x11_mod(int x11_mods) { uint32_t mods = 0; if (x11_mods & ShiftMask) { mods |= SAPP_MODIFIER_SHIFT; } if (x11_mods & ControlMask) { mods |= SAPP_MODIFIER_CTRL; } if (x11_mods & Mod1Mask) { mods |= SAPP_MODIFIER_ALT; } if (x11_mods & Mod4Mask) { mods |= SAPP_MODIFIER_SUPER; } return mods; } _SOKOL_PRIVATE void _sapp_x11_app_event(sapp_event_type type) { if (_sapp_events_enabled()) { _sapp_init_event(type); _sapp_call_event(&_sapp.event); } } _SOKOL_PRIVATE sapp_mousebutton _sapp_x11_translate_button(const XEvent* event) { switch (event->xbutton.button) { case Button1: return SAPP_MOUSEBUTTON_LEFT; case Button2: return SAPP_MOUSEBUTTON_MIDDLE; case Button3: return SAPP_MOUSEBUTTON_RIGHT; default: return SAPP_MOUSEBUTTON_INVALID; } } _SOKOL_PRIVATE void _sapp_x11_mouse_event(sapp_event_type type, sapp_mousebutton btn, uint32_t mods) { if (_sapp_events_enabled()) { _sapp_init_event(type); _sapp.event.mouse_button = btn; _sapp.event.modifiers = mods; _sapp.event.mouse_x = _sapp.mouse_x; _sapp.event.mouse_y = _sapp.mouse_y; _sapp_call_event(&_sapp.event); } } _SOKOL_PRIVATE void _sapp_x11_scroll_event(float x, float y, uint32_t mods) { if (_sapp_events_enabled()) { _sapp_init_event(SAPP_EVENTTYPE_MOUSE_SCROLL); _sapp.event.modifiers = mods; _sapp.event.scroll_x = x; _sapp.event.scroll_y = y; _sapp_call_event(&_sapp.event); } } _SOKOL_PRIVATE void _sapp_x11_key_event(sapp_event_type type, sapp_keycode key, bool repeat, uint32_t mods) { if (_sapp_events_enabled()) { _sapp_init_event(type); _sapp.event.key_code = key; _sapp.event.key_repeat = repeat; _sapp.event.modifiers = mods; _sapp_call_event(&_sapp.event); } } _SOKOL_PRIVATE void _sapp_x11_char_event(uint32_t chr, bool repeat, uint32_t mods) { if (_sapp_events_enabled()) { _sapp_init_event(SAPP_EVENTTYPE_CHAR); _sapp.event.char_code = chr; _sapp.event.key_repeat = repeat; _sapp.event.modifiers = mods; _sapp_call_event(&_sapp.event); } } _SOKOL_PRIVATE sapp_keycode _sapp_x11_translate_key(int scancode) { int dummy; KeySym* keysyms = XGetKeyboardMapping(_sapp_x11_display, scancode, 1, &dummy); SOKOL_ASSERT(keysyms); KeySym keysym = keysyms[0]; XFree(keysyms); switch (keysym) { case XK_Escape: return SAPP_KEYCODE_ESCAPE; case XK_Tab: return SAPP_KEYCODE_TAB; case XK_Shift_L: return SAPP_KEYCODE_LEFT_SHIFT; case XK_Shift_R: return SAPP_KEYCODE_RIGHT_SHIFT; case XK_Control_L: return SAPP_KEYCODE_LEFT_CONTROL; case XK_Control_R: return SAPP_KEYCODE_RIGHT_CONTROL; case XK_Meta_L: case XK_Alt_L: return SAPP_KEYCODE_LEFT_ALT; case XK_Mode_switch: /* Mapped to Alt_R on many keyboards */ case XK_ISO_Level3_Shift: /* AltGr on at least some machines */ case XK_Meta_R: case XK_Alt_R: return SAPP_KEYCODE_RIGHT_ALT; case XK_Super_L: return SAPP_KEYCODE_LEFT_SUPER; case XK_Super_R: return SAPP_KEYCODE_RIGHT_SUPER; case XK_Menu: return SAPP_KEYCODE_MENU; case XK_Num_Lock: return SAPP_KEYCODE_NUM_LOCK; case XK_Caps_Lock: return SAPP_KEYCODE_CAPS_LOCK; case XK_Print: return SAPP_KEYCODE_PRINT_SCREEN; case XK_Scroll_Lock: return SAPP_KEYCODE_SCROLL_LOCK; case XK_Pause: return SAPP_KEYCODE_PAUSE; case XK_Delete: return SAPP_KEYCODE_DELETE; case XK_BackSpace: return SAPP_KEYCODE_BACKSPACE; case XK_Return: return SAPP_KEYCODE_ENTER; case XK_Home: return SAPP_KEYCODE_HOME; case XK_End: return SAPP_KEYCODE_END; case XK_Page_Up: return SAPP_KEYCODE_PAGE_UP; case XK_Page_Down: return SAPP_KEYCODE_PAGE_DOWN; case XK_Insert: return SAPP_KEYCODE_INSERT; case XK_Left: return SAPP_KEYCODE_LEFT; case XK_Right: return SAPP_KEYCODE_RIGHT; case XK_Down: return SAPP_KEYCODE_DOWN; case XK_Up: return SAPP_KEYCODE_UP; case XK_F1: return SAPP_KEYCODE_F1; case XK_F2: return SAPP_KEYCODE_F2; case XK_F3: return SAPP_KEYCODE_F3; case XK_F4: return SAPP_KEYCODE_F4; case XK_F5: return SAPP_KEYCODE_F5; case XK_F6: return SAPP_KEYCODE_F6; case XK_F7: return SAPP_KEYCODE_F7; case XK_F8: return SAPP_KEYCODE_F8; case XK_F9: return SAPP_KEYCODE_F9; case XK_F10: return SAPP_KEYCODE_F10; case XK_F11: return SAPP_KEYCODE_F11; case XK_F12: return SAPP_KEYCODE_F12; case XK_F13: return SAPP_KEYCODE_F13; case XK_F14: return SAPP_KEYCODE_F14; case XK_F15: return SAPP_KEYCODE_F15; case XK_F16: return SAPP_KEYCODE_F16; case XK_F17: return SAPP_KEYCODE_F17; case XK_F18: return SAPP_KEYCODE_F18; case XK_F19: return SAPP_KEYCODE_F19; case XK_F20: return SAPP_KEYCODE_F20; case XK_F21: return SAPP_KEYCODE_F21; case XK_F22: return SAPP_KEYCODE_F22; case XK_F23: return SAPP_KEYCODE_F23; case XK_F24: return SAPP_KEYCODE_F24; case XK_F25: return SAPP_KEYCODE_F25; case XK_KP_Divide: return SAPP_KEYCODE_KP_DIVIDE; case XK_KP_Multiply: return SAPP_KEYCODE_KP_MULTIPLY; case XK_KP_Subtract: return SAPP_KEYCODE_KP_SUBTRACT; case XK_KP_Add: return SAPP_KEYCODE_KP_ADD; case XK_KP_Insert: return SAPP_KEYCODE_KP_0; case XK_KP_End: return SAPP_KEYCODE_KP_1; case XK_KP_Down: return SAPP_KEYCODE_KP_2; case XK_KP_Page_Down: return SAPP_KEYCODE_KP_3; case XK_KP_Left: return SAPP_KEYCODE_KP_4; case XK_KP_Begin: return SAPP_KEYCODE_KP_5; case XK_KP_Right: return SAPP_KEYCODE_KP_6; case XK_KP_Home: return SAPP_KEYCODE_KP_7; case XK_KP_Up: return SAPP_KEYCODE_KP_8; case XK_KP_Page_Up: return SAPP_KEYCODE_KP_9; case XK_KP_Delete: return SAPP_KEYCODE_KP_DECIMAL; case XK_KP_Equal: return SAPP_KEYCODE_KP_EQUAL; case XK_KP_Enter: return SAPP_KEYCODE_KP_ENTER; case XK_a: return SAPP_KEYCODE_A; case XK_b: return SAPP_KEYCODE_B; case XK_c: return SAPP_KEYCODE_C; case XK_d: return SAPP_KEYCODE_D; case XK_e: return SAPP_KEYCODE_E; case XK_f: return SAPP_KEYCODE_F; case XK_g: return SAPP_KEYCODE_G; case XK_h: return SAPP_KEYCODE_H; case XK_i: return SAPP_KEYCODE_I; case XK_j: return SAPP_KEYCODE_J; case XK_k: return SAPP_KEYCODE_K; case XK_l: return SAPP_KEYCODE_L; case XK_m: return SAPP_KEYCODE_M; case XK_n: return SAPP_KEYCODE_N; case XK_o: return SAPP_KEYCODE_O; case XK_p: return SAPP_KEYCODE_P; case XK_q: return SAPP_KEYCODE_Q; case XK_r: return SAPP_KEYCODE_R; case XK_s: return SAPP_KEYCODE_S; case XK_t: return SAPP_KEYCODE_T; case XK_u: return SAPP_KEYCODE_U; case XK_v: return SAPP_KEYCODE_V; case XK_w: return SAPP_KEYCODE_W; case XK_x: return SAPP_KEYCODE_X; case XK_y: return SAPP_KEYCODE_Y; case XK_z: return SAPP_KEYCODE_Z; case XK_1: return SAPP_KEYCODE_1; case XK_2: return SAPP_KEYCODE_2; case XK_3: return SAPP_KEYCODE_3; case XK_4: return SAPP_KEYCODE_4; case XK_5: return SAPP_KEYCODE_5; case XK_6: return SAPP_KEYCODE_6; case XK_7: return SAPP_KEYCODE_7; case XK_8: return SAPP_KEYCODE_8; case XK_9: return SAPP_KEYCODE_9; case XK_0: return SAPP_KEYCODE_0; case XK_space: return SAPP_KEYCODE_SPACE; case XK_minus: return SAPP_KEYCODE_MINUS; case XK_equal: return SAPP_KEYCODE_EQUAL; case XK_bracketleft: return SAPP_KEYCODE_LEFT_BRACKET; case XK_bracketright: return SAPP_KEYCODE_RIGHT_BRACKET; case XK_backslash: return SAPP_KEYCODE_BACKSLASH; case XK_semicolon: return SAPP_KEYCODE_SEMICOLON; case XK_apostrophe: return SAPP_KEYCODE_APOSTROPHE; case XK_grave: return SAPP_KEYCODE_GRAVE_ACCENT; case XK_comma: return SAPP_KEYCODE_COMMA; case XK_period: return SAPP_KEYCODE_PERIOD; case XK_slash: return SAPP_KEYCODE_SLASH; case XK_less: return SAPP_KEYCODE_WORLD_1; /* At least in some layouts... */ default: return SAPP_KEYCODE_INVALID; } } _SOKOL_PRIVATE int32_t _sapp_x11_keysym_to_unicode(KeySym keysym) { int min = 0; int max = sizeof(_sapp_x11_keysymtab) / sizeof(struct _sapp_x11_codepair) - 1; int mid; /* First check for Latin-1 characters (1:1 mapping) */ if ((keysym >= 0x0020 && keysym <= 0x007e) || (keysym >= 0x00a0 && keysym <= 0x00ff)) { return keysym; } /* Also check for directly encoded 24-bit UCS characters */ if ((keysym & 0xff000000) == 0x01000000) { return keysym & 0x00ffffff; } /* Binary search in table */ while (max >= min) { mid = (min + max) / 2; if (_sapp_x11_keysymtab[mid].keysym < keysym) { min = mid + 1; } else if (_sapp_x11_keysymtab[mid].keysym > keysym) { max = mid - 1; } else { return _sapp_x11_keysymtab[mid].ucs; } } /* No matching Unicode value found */ return -1; } // XLib manual says keycodes are in the range [8, 255] inclusive. // https://tronche.com/gui/x/xlib/input/keyboard-encoding.html static bool _sapp_x11_keycodes[256]; _SOKOL_PRIVATE void _sapp_x11_process_event(XEvent* event) { switch (event->type) { case KeyPress: { int keycode = event->xkey.keycode; const sapp_keycode key = _sapp_x11_translate_key(keycode); bool repeat = _sapp_x11_keycodes[keycode & 0xFF]; _sapp_x11_keycodes[keycode & 0xFF] = true; const uint32_t mods = _sapp_x11_mod(event->xkey.state); if (key != SAPP_KEYCODE_INVALID) { _sapp_x11_key_event(SAPP_EVENTTYPE_KEY_DOWN, key, repeat, mods); } KeySym keysym; XLookupString(&event->xkey, NULL, 0, &keysym, NULL); int32_t chr = _sapp_x11_keysym_to_unicode(keysym); if (chr > 0) { _sapp_x11_char_event((uint32_t)chr, repeat, mods); } } break; case KeyRelease: { int keycode = event->xkey.keycode; const sapp_keycode key = _sapp_x11_translate_key(keycode); _sapp_x11_keycodes[keycode & 0xFF] = false; if (key != SAPP_KEYCODE_INVALID) { const uint32_t mods = _sapp_x11_mod(event->xkey.state); _sapp_x11_key_event(SAPP_EVENTTYPE_KEY_UP, key, false, mods); } } break; case ButtonPress: { const sapp_mousebutton btn = _sapp_x11_translate_button(event); const uint32_t mods = _sapp_x11_mod(event->xbutton.state); if (btn != SAPP_MOUSEBUTTON_INVALID) { _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, btn, mods); } else { /* might be a scroll event */ switch (event->xbutton.button) { case 4: _sapp_x11_scroll_event(0.0f, 1.0f, mods); break; case 5: _sapp_x11_scroll_event(0.0f, -1.0f, mods); break; case 6: _sapp_x11_scroll_event(1.0f, 0.0f, mods); break; case 7: _sapp_x11_scroll_event(-1.0f, 0.0f, mods); break; } } } break; case ButtonRelease: { const sapp_mousebutton btn = _sapp_x11_translate_button(event); if (btn != SAPP_MOUSEBUTTON_INVALID) { _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_UP, btn, _sapp_x11_mod(event->xbutton.state)); } } break; case EnterNotify: _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_ENTER, SAPP_MOUSEBUTTON_INVALID, _sapp_x11_mod(event->xcrossing.state)); break; case LeaveNotify: _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_LEAVE, SAPP_MOUSEBUTTON_INVALID, _sapp_x11_mod(event->xcrossing.state)); break; case MotionNotify: _sapp.mouse_x = event->xmotion.x; _sapp.mouse_y = event->xmotion.y; _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID, _sapp_x11_mod(event->xmotion.state)); break; case ConfigureNotify: if ((event->xconfigure.width != _sapp.window_width) || (event->xconfigure.height != _sapp.window_height)) { _sapp.window_width = event->xconfigure.width; _sapp.window_height = event->xconfigure.height; _sapp.framebuffer_width = _sapp.window_width; _sapp.framebuffer_height = _sapp.window_height; _sapp_x11_app_event(SAPP_EVENTTYPE_RESIZED); } break; case PropertyNotify: if (event->xproperty.state == PropertyNewValue) { if (event->xproperty.atom == _sapp_x11_WM_STATE) { const int state = _sapp_x11_get_window_state(); if (state != _sapp_x11_window_state) { _sapp_x11_window_state = state; if (state == IconicState) { _sapp_x11_app_event(SAPP_EVENTTYPE_ICONIFIED); } else if (state == NormalState) { _sapp_x11_app_event(SAPP_EVENTTYPE_RESTORED); } } } } break; case ClientMessage: if (event->xclient.message_type == _sapp_x11_WM_PROTOCOLS) { const Atom protocol = event->xclient.data.l[0]; if (protocol == _sapp_x11_WM_DELETE_WINDOW) { _sapp_x11_quit_requested = true; } } break; case DestroyNotify: break; } } _SOKOL_PRIVATE void _sapp_run(const sapp_desc* desc) { _sapp_init_state(desc); _sapp_x11_quit_requested = false; _sapp_x11_window_state = NormalState; XInitThreads(); XrmInitialize(); _sapp_x11_display = XOpenDisplay(NULL); if (!_sapp_x11_display) { _sapp_fail("XOpenDisplay() failed!\n"); } _sapp_x11_screen = DefaultScreen(_sapp_x11_display); _sapp_x11_root = DefaultRootWindow(_sapp_x11_display); XkbSetDetectableAutoRepeat(_sapp_x11_display, true, NULL); _sapp_x11_query_system_dpi(); _sapp.dpi_scale = _sapp_x11_dpi / 96.0f; _sapp_x11_init_extensions(); _sapp_glx_init(); Visual* visual = 0; int depth = 0; _sapp_glx_choose_visual(&visual, &depth); _sapp_x11_create_window(visual, depth); _sapp_glx_create_context(); _sapp.valid = true; _sapp_x11_show_window(); _sapp_glx_swapinterval(_sapp.swap_interval); XFlush(_sapp_x11_display); while (!_sapp_x11_quit_requested) { _sapp_glx_make_current(); int count = XPending(_sapp_x11_display); while (count--) { XEvent event; XNextEvent(_sapp_x11_display, &event); _sapp_x11_process_event(&event); } _sapp_frame(); _sapp_glx_swap_buffers(); XFlush(_sapp_x11_display); } _sapp_call_cleanup(); _sapp_glx_destroy_context(); _sapp_x11_destroy_window(); XCloseDisplay(_sapp_x11_display); } #if !defined(SOKOL_NO_ENTRY) int main(int argc, char* argv[]) { sapp_desc desc = sokol_main(argc, argv); _sapp_run(&desc); return 0; } #endif /* SOKOL_NO_ENTRY */ #endif /* LINUX */ /*== PUBLIC API FUNCTIONS ====================================================*/ #if defined(SOKOL_NO_ENTRY) SOKOL_API_IMPL int sapp_run(const sapp_desc* desc) { SOKOL_ASSERT(desc); _sapp_run(desc); return 0; } /* this is just a stub so the linker doesn't complain */ sapp_desc sokol_main(int argc, char* argv[]) { _SOKOL_UNUSED(argc); _SOKOL_UNUSED(argv); sapp_desc desc; memset(&desc, 0, sizeof(desc)); return desc; } #else /* likewise, in normal mode, sapp_run() is just an empty stub */ SOKOL_API_IMPL int sapp_run(const sapp_desc* desc) { _SOKOL_UNUSED(desc); return 0; } #endif SOKOL_API_IMPL bool sapp_isvalid(void) { return _sapp.valid; } SOKOL_API_IMPL int sapp_width(void) { return (_sapp.framebuffer_width > 0) ? _sapp.framebuffer_width : 1; } SOKOL_API_IMPL int sapp_height(void) { return (_sapp.framebuffer_height > 0) ? _sapp.framebuffer_height : 1; } SOKOL_API_IMPL bool sapp_high_dpi(void) { return _sapp.desc.high_dpi && (_sapp.dpi_scale > 1.5f); } SOKOL_API_IMPL float sapp_dpi_scale(void) { return _sapp.dpi_scale; } SOKOL_API_IMPL bool sapp_gles2(void) { return _sapp.gles2_fallback; } SOKOL_API_IMPL void sapp_show_keyboard(bool shown) { #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE _sapp_ios_show_keyboard(shown); #elif defined(__EMSCRIPTEN__) _sapp_emsc_show_keyboard(shown); #elif defined(__ANDROID__) _sapp_android_show_keyboard(shown); #else _SOKOL_UNUSED(shown); #endif } SOKOL_API_IMPL bool sapp_keyboard_shown(void) { return _sapp.onscreen_keyboard_shown; } SOKOL_API_IMPL const void* sapp_metal_get_device(void) { SOKOL_ASSERT(_sapp.valid); #if defined(SOKOL_METAL) const void* obj = (__bridge const void*) _sapp_mtl_device_obj; SOKOL_ASSERT(obj); return obj; #else return 0; #endif } SOKOL_API_IMPL const void* sapp_metal_get_renderpass_descriptor(void) { SOKOL_ASSERT(_sapp.valid); #if defined(SOKOL_METAL) const void* obj = (__bridge const void*) [_sapp_view_obj currentRenderPassDescriptor]; SOKOL_ASSERT(obj); return obj; #else return 0; #endif } SOKOL_API_IMPL const void* sapp_metal_get_drawable(void) { SOKOL_ASSERT(_sapp.valid); #if defined(SOKOL_METAL) const void* obj = (__bridge const void*) [_sapp_view_obj currentDrawable]; SOKOL_ASSERT(obj); return obj; #else return 0; #endif } SOKOL_API_IMPL const void* sapp_macos_get_window(void) { #if defined(__APPLE__) && !TARGET_OS_IPHONE const void* obj = (__bridge const void*) _sapp_macos_window_obj; SOKOL_ASSERT(obj); return obj; #else return 0; #endif } SOKOL_API_IMPL const void* sapp_ios_get_window(void) { #if defined(__APPLE__) && TARGET_OS_IPHONE const void* obj = (__bridge const void*) _sapp_ios_window_obj; SOKOL_ASSERT(obj); return obj; #else return 0; #endif } SOKOL_API_IMPL const void* sapp_d3d11_get_device(void) { SOKOL_ASSERT(_sapp.valid); #if defined(SOKOL_D3D11) return _sapp_d3d11_device; #else return 0; #endif } SOKOL_API_IMPL const void* sapp_d3d11_get_device_context(void) { SOKOL_ASSERT(_sapp.valid); #if defined(SOKOL_D3D11) return _sapp_d3d11_device_context; #else return 0; #endif } SOKOL_API_IMPL const void* sapp_d3d11_get_render_target_view(void) { SOKOL_ASSERT(_sapp.valid); #if defined(SOKOL_D3D11) return _sapp_d3d11_rtv; #else return 0; #endif } SOKOL_API_IMPL const void* sapp_d3d11_get_depth_stencil_view(void) { SOKOL_ASSERT(_sapp.valid); #if defined(SOKOL_D3D11) return _sapp_d3d11_dsv; #else return 0; #endif } SOKOL_API_IMPL const void* sapp_win32_get_hwnd(void) { SOKOL_ASSERT(_sapp.valid); #if defined(_WIN32) return _sapp_win32_hwnd; #else return 0; #endif } #undef _sapp_def #ifdef _MSC_VER #pragma warning(pop) #endif #endif /* SOKOL_IMPL */ Chipmunk2D-Chipmunk-7.0.3/demo/sokol/sokol_gfx.h000066400000000000000000015124701347650476100215070ustar00rootroot00000000000000#pragma once /* sokol_gfx.h -- simple 3D API wrapper Project URL: https://github.com/floooh/sokol Do this: #define SOKOL_IMPL before you include this file in *one* C or C++ file to create the implementation. In the same place define one of the following to select the rendering backend: #define SOKOL_GLCORE33 #define SOKOL_GLES2 #define SOKOL_GLES3 #define SOKOL_D3D11 #define SOKOL_METAL #define SOKOL_DUMMY_BACKEND I.e. for the GL 3.3 Core Profile it should look like this: #include ... #include ... #define SOKOL_IMPL #define SOKOL_GLCORE33 #include "sokol_gfx.h" The dummy backend replaces the platform-specific backend code with empty stub functions. This is useful for writing tests that need to run on the command line. Optionally provide the following defines with your own implementations: SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) SOKOL_MALLOC(s) - your own malloc function (default: malloc(s)) SOKOL_FREE(p) - your own free function (default: free(p)) SOKOL_LOG(msg) - your own logging function (default: puts(msg)) SOKOL_UNREACHABLE() - a guard macro for unreachable code (default: assert(false)) SOKOL_API_DECL - public function declaration prefix (default: extern) SOKOL_API_IMPL - public function implementation prefix (default: -) SOKOL_TRACE_HOOKS - enable trace hook callbacks (search below for TRACE HOOKS) If sokol_gfx.h is compiled as a DLL, define the following before including the declaration or implementation: SOKOL_DLL On Windows, SOKOL_DLL will define SOKOL_API_DECL as __declspec(dllexport) or __declspec(dllimport) as needed. If you want to compile without deprecated structs and functions, define: SOKOL_NO_DEPRECATED API usage validation macros: SOKOL_VALIDATE_BEGIN() - begin a validation block (default:_sg_validate_begin()) SOKOL_VALIDATE(cond, err) - like assert but for API validation (default: _sg_validate(cond, err)) SOKOL_VALIDATE_END() - end a validation block, return true if all checks in block passed (default: bool _sg_validate()) If you don't want validation errors to be fatal, define SOKOL_VALIDATE_NON_FATAL, be aware though that this may spam SOKOL_LOG messages. Optionally define the following to force debug checks and validations even in release mode: SOKOL_DEBUG - by default this is defined if _DEBUG is defined sokol_gfx DOES NOT: =================== - create a window or the 3D-API context/device, you must do this before sokol_gfx is initialized, and pass any required information (like 3D device pointers) to the sokol_gfx initialization call - present the rendered frame, how this is done exactly usually depends on how the window and 3D-API context/device was created - provide a unified shader language, instead 3D-API-specific shader source-code or shader-bytecode must be provided For complete code examples using the various backend 3D-APIs, see: https://github.com/floooh/sokol-samples For an optional shader-cross-compile solution, see: https://github.com/floooh/sokol-tools/blob/master/docs/sokol-shdc.md STEP BY STEP ============ --- to initialize sokol_gfx, after creating a window and a 3D-API context/device, call: sg_setup(const sg_desc*) --- create resource objects (at least buffers, shaders and pipelines, and optionally images and passes): sg_buffer sg_make_buffer(const sg_buffer_desc*) sg_image sg_make_image(const sg_image_desc*) sg_shader sg_make_shader(const sg_shader_desc*) sg_pipeline sg_make_pipeline(const sg_pipeline_desc*) sg_pass sg_make_pass(const sg_pass_desc*) --- start rendering to the default frame buffer with: sg_begin_default_pass(const sg_pass_action* actions, int width, int height) --- or start rendering to an offscreen framebuffer with: sg_begin_pass(sg_pass pass, const sg_pass_action* actions) --- set the pipeline state for the next draw call with: sg_apply_pipeline(sg_pipeline pip) --- fill an sg_bindings struct with the resource bindings for the next draw call (1..N vertex buffers, 0 or 1 index buffer, 0..N image objects to use as textures each on the vertex-shader- and fragment-shader-stage and then call sg_apply_bindings(const sg_bindings* bindings) to update the resource bindings --- optionally update shader uniform data with: sg_apply_uniforms(sg_shader_stage stage, int ub_index, const void* data, int num_bytes) --- kick off a draw call with: sg_draw(int base_element, int num_elements, int num_instances) --- finish the current rendering pass with: sg_end_pass() --- when done with the current frame, call sg_commit() --- at the end of your program, shutdown sokol_gfx with: sg_shutdown() --- if you need to destroy resources before sg_shutdown(), call: sg_destroy_buffer(sg_buffer buf) sg_destroy_image(sg_image img) sg_destroy_shader(sg_shader shd) sg_destroy_pipeline(sg_pipeline pip) sg_destroy_pass(sg_pass pass) --- to set a new viewport rectangle, call sg_apply_viewport(int x, int y, int width, int height, bool origin_top_left) --- to set a new scissor rect, call: sg_apply_scissor_rect(int x, int y, int width, int height, bool origin_top_left) both sg_apply_viewport() and sg_apply_scissor_rect() must be called inside a rendering pass beginning a pass will reset the viewport to the size of the framebuffer used in the new pass, --- to update (overwrite) the content of buffer and image resources, call: sg_update_buffer(sg_buffer buf, const void* ptr, int num_bytes) sg_update_image(sg_image img, const sg_image_content* content) Buffers and images to be updated must have been created with SG_USAGE_DYNAMIC or SG_USAGE_STREAM Only one update per frame is allowed for buffer and image resources. The rationale is to have a simple countermeasure to avoid the CPU scribbling over data the GPU is currently using, or the CPU having to wait for the GPU Buffer and image updates can be partial, as long as a rendering operation only references the valid (updated) data in the buffer or image. --- to append a chunk of data to a buffer resource, call: int sg_append_buffer(sg_buffer buf, const void* ptr, int num_bytes) The difference to sg_update_buffer() is that sg_append_buffer() can be called multiple times per frame to append new data to the buffer piece by piece, optionally interleaved with draw calls referencing the previously written data. sg_append_buffer() returns a byte offset to the start of the written data, this offset can be assigned to sg_bindings.vertex_buffer_offsets[n] or sg_bindings.index_buffer_offset Code example: for (...) { const void* data = ...; const int num_bytes = ...; int offset = sg_append_buffer(buf, data, num_bytes); bindings.vertex_buffer_offsets[0] = offset; sg_apply_pipeline(pip); sg_apply_bindings(&bindings); sg_apply_uniforms(...); sg_draw(...); } A buffer to be used with sg_append_buffer() must have been created with SG_USAGE_DYNAMIC or SG_USAGE_STREAM. If the application appends more data to the buffer then fits into the buffer, the buffer will go into the "overflow" state for the rest of the frame. Any draw calls attempting to render an overflown buffer will be silently dropped (in debug mode this will also result in a validation error). You can also check manually if a buffer is in overflow-state by calling bool sg_query_buffer_overflow(sg_buffer buf) --- to check for support of optional features: bool sg_query_feature(sg_feature feature) --- if you need to call into the underlying 3D-API directly, you must call: sg_reset_state_cache() ...before calling sokol_gfx functions again --- you can inspect the original sg_desc structure handed to sg_setup() by calling sg_query_desc(). This will return an sg_desc struct with the default values patched in instead of any zero-initialized values --- you can inspect various internal resource attributes via: sg_buffer_info sg_query_buffer_info(sg_buffer buf) sg_image_info sg_query_image_info(sg_image img) sg_shader_info sg_query_shader_info(sg_shader shd) sg_pipeline_info sg_query_pipeline_info(sg_pipeline pip) sg_pass_info sg_query_pass_info(sg_pass pass) ...please note that the returned info-structs are tied quite closely to sokol_gfx.h internals, and may change more often than other public API functions and structs. --- you can ask at runtime what backend sokol_gfx.h has been compiled for, or whether the GLES3 backend had to fall back to GLES2 with: sg_backend sg_query_backend(void) BACKEND-SPECIFIC TOPICS: ======================== --- the GL backends need to know about the internal structure of uniform blocks, and the texture sampler-name and -type: typedef struct { float mvp[16]; // model-view-projection matrix float offset0[2]; // some 2D vectors float offset1[2]; float offset2[2]; } params_t; // uniform block structure and texture image definition in sg_shader_desc: sg_shader_desc desc = { // uniform block description (size and internal structure) .vs.uniform_blocks[0] = { .size = sizeof(params_t), .uniforms = { [0] = { .name="mvp", .type=SG_UNIFORMTYPE_MAT4 }, [1] = { .name="offset0", .type=SG_UNIFORMTYPE_VEC2 }, ... } }, // one texture on the fragment-shader-stage, GLES2/WebGL needs name and image type .fs.images[0] = { .name="tex", .type=SG_IMAGETYPE_ARRAY } ... }; --- the Metal and D3D11 backends only need to know the size of uniform blocks, not their internal member structure, and they only need to know the type of a texture sampler, not its name: sg_shader_desc desc = { .vs.uniform_blocks[0].size = sizeof(params_t), .fs.images[0].type = SG_IMAGETYPE_ARRAY, ... }; --- when creating a shader object, GLES2/WebGL need to know the vertex attribute names as used in the vertex shader: sg_shader_desc desc = { .attrs = { [0] = { .name="position" }, [1] = { .name="color1" } } }; The vertex attribute names provided when creating a shader will be used later in sg_create_pipeline() for matching the vertex layout to vertex shader inputs. --- on D3D11 you need to provide a semantic name and semantic index in the shader description struct instead (see the D3D11 documentation on D3D11_INPUT_ELEMENT_DESC for details): sg_shader_desc desc = { .attrs = { [0] = { .sem_name="POSITION", .sem_index=0 } [1] = { .sem_name="COLOR", .sem_index=1 } } }; The provided semantic information will be used later in sg_create_pipeline() to match the vertex layout to vertex shader inputs. --- on Metal, GL 3.3 or GLES3/WebGL2, you don't need to provide an attribute name or semantic name, since vertex attributes can be bound by their slot index (this is mandatory in Metal, and optional in GL): sg_pipeline_desc desc = { .layout = { .attrs = { [0] = { .format=SG_VERTEXFORMAT_FLOAT3 }, [1] = { .format=SG_VERTEXFORMAT_FLOAT4 } } } }; WORKING WITH CONTEXTS ===================== sokol-gfx allows to switch between different rendering contexts and associate resource objects with contexts. This is useful to create GL applications that render into multiple windows. A rendering context keeps track of all resources created while the context is active. When the context is destroyed, all resources "belonging to the context" are destroyed as well. A default context will be created and activated implicitly in sg_setup(), and destroyed in sg_shutdown(). So for a typical application which *doesn't* use multiple contexts, nothing changes, and calling the context functions isn't necessary. Three functions have been added to work with contexts: --- sg_context sg_setup_context(): This must be called once after a GL context has been created and made active. --- void sg_activate_context(sg_context ctx) This must be called after making a different GL context active. Apart from 3D-API-specific actions, the call to sg_activate_context() will internally call sg_reset_state_cache(). --- void sg_discard_context(sg_context ctx) This must be called right before a GL context is destroyed and will destroy all resources associated with the context (that have been created while the context was active) The GL context must be active at the time sg_discard_context(sg_context ctx) is called. Also note that resources (buffers, images, shaders and pipelines) must only be used or destroyed while the same GL context is active that was also active while the resource was created (an exception is resource sharing on GL, such resources can be used while another context is active, but must still be destroyed under the same context that was active during creation). For more information, check out the multiwindow-glfw sample: https://github.com/floooh/sokol-samples/blob/master/glfw/multiwindow-glfw.c TRACE HOOKS: ============ sokol_gfx.h optionally allows to install "trace hook" callbacks for each public API functions. When a public API function is called, and a trace hook callback has been installed for this function, the callback will be invoked with the parameters and result of the function. This is useful for things like debugging- and profiling-tools, or keeping track of resource creation and destruction. To use the trace hook feature: --- Define SOKOL_TRACE_HOOKS before including the implementation. --- Setup an sg_trace_hooks structure with your callback function pointers (keep all function pointers you're not interested in zero-initialized), optionally set the user_data member in the sg_trace_hooks struct. --- Install the trace hooks by calling sg_install_trace_hooks(), the return value of this function is another sg_trace_hooks struct which contains the previously set of trace hooks. You should keep this struct around, and call those previous functions pointers from your own trace callbacks for proper chaining. As an example of how trace hooks are used, have a look at the imgui/sokol_gfx_imgui.h header which implements a realtime debugging UI for sokol_gfx.h on top of Dear ImGui. A NOTE ON PORTABLE PACKED VERTEX FORMATS: ========================================= There are two things to consider when using packed vertex formats like UBYTE4, SHORT2, etc which need to work across all backends: - D3D11 can only convert *normalized* vertex formats to floating point during vertex fetch, normalized formats have a trailing 'N', and are "normalized" to a range -1.0..+1.0 (for the signed formats) or 0.0..1.0 (for the unsigned formats): - SG_VERTEXFORMAT_BYTE4N - SG_VERTEXFORMAT_UBYTE4N - SG_VERTEXFORMAT_SHORT2N - SG_VERTEXFORMAT_SHORT4N D3D11 will not convert *non-normalized* vertex formats to floating point vertex shader inputs, those can only use the ivecn formats when D3D11 is used as backend (GL and should Metal can use both formats) - SG_VERTEXFORMAT_BYTE4, - SG_VERTEXFORMAT_UBYTE4 - SG_VERTEXFORMAT_SHORT2 - SG_VERTEXFORMAT_SHORT4 - WebGL/GLES2 cannot use integer vertex shader inputs (int or ivecn) - SG_VERTEXFORMAT_UINT10_N2 is not supported on WebGL/GLES2 So for a vertex input layout which works on all platforms, only use the following vertex formats, and if needed "expand" the normalized vertex shader inputs in the vertex shader by multiplying with 127.0, 255.0, 32767.0 or 65535.0: - SG_VERTEXFORMAT_FLOAT, - SG_VERTEXFORMAT_FLOAT2, - SG_VERTEXFORMAT_FLOAT3, - SG_VERTEXFORMAT_FLOAT4, - SG_VERTEXFORMAT_BYTE4N, - SG_VERTEXFORMAT_UBYTE4N, - SG_VERTEXFORMAT_SHORT2N, - SG_VERTEXFORMAT_SHORT4N, TODO: ==== - talk about asynchronous resource creation zlib/libpng license Copyright (c) 2018 Andre Weissflog This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #define SOKOL_GFX_INCLUDED (1) #include #include #ifndef SOKOL_API_DECL #if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_IMPL) #define SOKOL_API_DECL __declspec(dllexport) #elif defined(_WIN32) && defined(SOKOL_DLL) #define SOKOL_API_DECL __declspec(dllimport) #else #define SOKOL_API_DECL extern #endif #endif #ifdef __cplusplus extern "C" { #endif #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */ #endif /* Resource id typedefs: sg_buffer: vertex- and index-buffers sg_image: textures and render targets sg_shader: vertex- and fragment-shaders, uniform blocks sg_pipeline: associated shader and vertex-layouts, and render states sg_pass: a bundle of render targets and actions on them sg_context: a 'context handle' for switching between 3D-API contexts Instead of pointers, resource creation functions return a 32-bit number which uniquely identifies the resource object. The 32-bit resource id is split into a 16-bit pool index in the lower bits, and a 16-bit 'unique counter' in the upper bits. The index allows fast pool lookups, and combined with the unique-mask it allows to detect 'dangling accesses' (trying to use an object which no longer exists, and its pool slot has been reused for a new object) The resource ids are wrapped into a struct so that the compiler can complain when the wrong resource type is used. */ typedef struct sg_buffer { uint32_t id; } sg_buffer; typedef struct sg_image { uint32_t id; } sg_image; typedef struct sg_shader { uint32_t id; } sg_shader; typedef struct sg_pipeline { uint32_t id; } sg_pipeline; typedef struct sg_pass { uint32_t id; } sg_pass; typedef struct sg_context { uint32_t id; } sg_context; /* various compile-time constants FIXME: it may make sense to convert some of those into defines so that the user code can override them. */ enum { SG_INVALID_ID = 0, SG_NUM_SHADER_STAGES = 2, SG_NUM_INFLIGHT_FRAMES = 2, SG_MAX_COLOR_ATTACHMENTS = 4, SG_MAX_SHADERSTAGE_BUFFERS = 4, SG_MAX_SHADERSTAGE_IMAGES = 12, SG_MAX_SHADERSTAGE_UBS = 4, SG_MAX_UB_MEMBERS = 16, SG_MAX_VERTEX_ATTRIBUTES = 16, SG_MAX_MIPMAPS = 16, SG_MAX_TEXTUREARRAY_LAYERS = 128 }; /* sg_backend The active 3D-API backend, use the function sg_query_backend() to get the currently active backend. For returned value corresponds with the compile-time define to select a backend, with the only exception of SOKOL_GLES3: this may return SG_BACKEND_GLES2 if the backend has to fallback to GLES2 mode because GLES3 isn't supported. */ typedef enum sg_backend { SG_BACKEND_GLCORE33, SG_BACKEND_GLES2, SG_BACKEND_GLES3, SG_BACKEND_D3D11, SG_BACKEND_METAL_IOS, SG_BACKEND_METAL_MACOS, SG_BACKEND_METAL_SIMULATOR, SG_BACKEND_DUMMY, } sg_backend; /* sg_feature These are optional features, use the function sg_query_feature() to check whether the feature is supported. */ typedef enum sg_feature { SG_FEATURE_INSTANCING, SG_FEATURE_TEXTURE_COMPRESSION_DXT, SG_FEATURE_TEXTURE_COMPRESSION_PVRTC, SG_FEATURE_TEXTURE_COMPRESSION_ATC, SG_FEATURE_TEXTURE_COMPRESSION_ETC2, SG_FEATURE_TEXTURE_FLOAT, SG_FEATURE_TEXTURE_HALF_FLOAT, SG_FEATURE_ORIGIN_BOTTOM_LEFT, SG_FEATURE_ORIGIN_TOP_LEFT, SG_FEATURE_MSAA_RENDER_TARGETS, SG_FEATURE_PACKED_VERTEX_FORMAT_10_2, SG_FEATURE_MULTIPLE_RENDER_TARGET, SG_FEATURE_IMAGETYPE_3D, SG_FEATURE_IMAGETYPE_ARRAY, SG_NUM_FEATURES } sg_feature; /* sg_resource_state The current state of a resource in its resource pool. Resources start in the INITIAL state, which means the pool slot is unoccupied and can be allocated. When a resource is created, first an id is allocated, and the resource pool slot is set to state ALLOC. After allocation, the resource is initialized, which may result in the VALID or FAILED state. The reason why allocation and initialization are separate is because some resource types (e.g. buffers and images) might be asynchronously initialized by the user application. If a resource which is not in the VALID state is attempted to be used for rendering, rendering operations will silently be dropped. The special INVALID state is returned in sg_query_xxx_state() if no resource object exists for the provided resource id. */ typedef enum sg_resource_state { SG_RESOURCESTATE_INITIAL, SG_RESOURCESTATE_ALLOC, SG_RESOURCESTATE_VALID, SG_RESOURCESTATE_FAILED, SG_RESOURCESTATE_INVALID, _SG_RESOURCESTATE_FORCE_U32 = 0x7FFFFFFF } sg_resource_state; /* sg_usage A resource usage hint describing the update strategy of buffers and images. This is used in the sg_buffer_desc.usage and sg_image_desc.usage members when creating buffers and images: SG_USAGE_IMMUTABLE: the resource will never be updated with new data, instead the data content of the resource must be provided on creation SG_USAGE_DYNAMIC: the resource will be updated infrequently with new data (this could range from "once after creation", to "quite often but not every frame") SG_USAGE_STREAM: the resource will be updated each frame with new content The rendering backends use this hint to prevent that the CPU needs to wait for the GPU when attempting to update a resource that might be currently accessed by the GPU. Resource content is updated with the function sg_update_buffer() for buffer objects, and sg_update_image() for image objects. Only one update is allowed per frame and resource object. The application must update all data required for rendering (this means that the update data can be smaller than the resource size, if only a part of the overall resource size is used for rendering, you only need to make sure that the data that *is* used is valid. The default usage is SG_USAGE_IMMUTABLE. */ typedef enum sg_usage { _SG_USAGE_DEFAULT, /* value 0 reserved for default-init */ SG_USAGE_IMMUTABLE, SG_USAGE_DYNAMIC, SG_USAGE_STREAM, _SG_USAGE_NUM, _SG_USAGE_FORCE_U32 = 0x7FFFFFFF } sg_usage; /* sg_buffer_type This indicates whether a buffer contains vertex- or index-data, used in the sg_buffer_desc.type member when creating a buffer. The default value is SG_BUFFERTYPE_VERTEXBUFFER. */ typedef enum sg_buffer_type { _SG_BUFFERTYPE_DEFAULT, /* value 0 reserved for default-init */ SG_BUFFERTYPE_VERTEXBUFFER, SG_BUFFERTYPE_INDEXBUFFER, _SG_BUFFERTYPE_NUM, _SG_BUFFERTYPE_FORCE_U32 = 0x7FFFFFFF } sg_buffer_type; /* sg_index_type Indicates whether indexed rendering (fetching vertex-indices from an index buffer) is used, and if yes, the index data type (16- or 32-bits). This is used in the sg_pipeline_desc.index_type member when creating a pipeline object. The default index type is SG_INDEXTYPE_NONE. */ typedef enum sg_index_type { _SG_INDEXTYPE_DEFAULT, /* value 0 reserved for default-init */ SG_INDEXTYPE_NONE, SG_INDEXTYPE_UINT16, SG_INDEXTYPE_UINT32, _SG_INDEXTYPE_NUM, _SG_INDEXTYPE_FORCE_U32 = 0x7FFFFFFF } sg_index_type; /* sg_image_type Indicates the basic image type (2D-texture, cubemap, 3D-texture or 2D-array-texture). 3D- and array-textures are not supported on the GLES2/WebGL backend. The image type is used in the sg_image_desc.type member when creating an image. The default image type when creating an image is SG_IMAGETYPE_2D. */ typedef enum sg_image_type { _SG_IMAGETYPE_DEFAULT, /* value 0 reserved for default-init */ SG_IMAGETYPE_2D, SG_IMAGETYPE_CUBE, SG_IMAGETYPE_3D, SG_IMAGETYPE_ARRAY, _SG_IMAGETYPE_NUM, _SG_IMAGETYPE_FORCE_U32 = 0x7FFFFFFF } sg_image_type; /* sg_cube_face The cubemap faces. Use these as indices in the sg_image_desc.content array. */ typedef enum sg_cube_face { SG_CUBEFACE_POS_X, SG_CUBEFACE_NEG_X, SG_CUBEFACE_POS_Y, SG_CUBEFACE_NEG_Y, SG_CUBEFACE_POS_Z, SG_CUBEFACE_NEG_Z, SG_CUBEFACE_NUM, _SG_CUBEFACE_FORCE_U32 = 0x7FFFFFFF } sg_cube_face; /* sg_shader_stage There are 2 shader stages: vertex- and fragment-shader-stage. Each shader stage consists of: - one slot for a shader function (provided as source- or byte-code) - SG_MAX_SHADERSTAGE_UBS slots for uniform blocks - SG_MAX_SHADERSTAGE_IMAGES slots for images used as textures by the shader function */ typedef enum sg_shader_stage { SG_SHADERSTAGE_VS, SG_SHADERSTAGE_FS, _SG_SHADERSTAGE_FORCE_U32 = 0x7FFFFFFF } sg_shader_stage; /* sg_pixel_format This is a common subset of useful and widely supported pixel formats. The pixel format enum is mainly used when creating an image object in the sg_image_desc.pixel_format member. The default pixel format when creating an image is SG_PIXELFORMAT_RGBA8. */ typedef enum sg_pixel_format { _SG_PIXELFORMAT_DEFAULT, /* value 0 reserved for default-init */ SG_PIXELFORMAT_NONE, SG_PIXELFORMAT_RGBA8, SG_PIXELFORMAT_RGB8, SG_PIXELFORMAT_RGBA4, SG_PIXELFORMAT_R5G6B5, SG_PIXELFORMAT_R5G5B5A1, SG_PIXELFORMAT_R10G10B10A2, SG_PIXELFORMAT_RGBA32F, SG_PIXELFORMAT_RGBA16F, SG_PIXELFORMAT_R32F, SG_PIXELFORMAT_R16F, SG_PIXELFORMAT_L8, SG_PIXELFORMAT_DXT1, SG_PIXELFORMAT_DXT3, SG_PIXELFORMAT_DXT5, SG_PIXELFORMAT_DEPTH, SG_PIXELFORMAT_DEPTHSTENCIL, SG_PIXELFORMAT_PVRTC2_RGB, SG_PIXELFORMAT_PVRTC4_RGB, SG_PIXELFORMAT_PVRTC2_RGBA, SG_PIXELFORMAT_PVRTC4_RGBA, SG_PIXELFORMAT_ETC2_RGB8, SG_PIXELFORMAT_ETC2_SRGB8, _SG_PIXELFORMAT_NUM, _SG_PIXELFORMAT_FORCE_U32 = 0x7FFFFFFF } sg_pixel_format; /* sg_primitive_type This is the common subset of 3D primitive types supported across all 3D APIs. This is used in the sg_pipeline_desc.primitive_type member when creating a pipeline object. The default primitive type is SG_PRIMITIVETYPE_TRIANGLES. */ typedef enum sg_primitive_type { _SG_PRIMITIVETYPE_DEFAULT, /* value 0 reserved for default-init */ SG_PRIMITIVETYPE_POINTS, SG_PRIMITIVETYPE_LINES, SG_PRIMITIVETYPE_LINE_STRIP, SG_PRIMITIVETYPE_TRIANGLES, SG_PRIMITIVETYPE_TRIANGLE_STRIP, _SG_PRIMITIVETYPE_NUM, _SG_PRIMITIVETYPE_FORCE_U32 = 0x7FFFFFFF } sg_primitive_type; /* sg_filter The filtering mode when sampling a texture image. This is used in the sg_image_desc.min_filter and sg_image_desc.mag_filter members when creating an image object. The default filter mode is SG_FILTER_NEAREST. */ typedef enum sg_filter { _SG_FILTER_DEFAULT, /* value 0 reserved for default-init */ SG_FILTER_NEAREST, SG_FILTER_LINEAR, SG_FILTER_NEAREST_MIPMAP_NEAREST, SG_FILTER_NEAREST_MIPMAP_LINEAR, SG_FILTER_LINEAR_MIPMAP_NEAREST, SG_FILTER_LINEAR_MIPMAP_LINEAR, _SG_FILTER_NUM, _SG_FILTER_FORCE_U32 = 0x7FFFFFFF } sg_filter; /* sg_wrap The texture coordinates wrapping mode when sampling a texture image. This is used in the sg_image_desc.wrap_u, .wrap_v and .wrap_w members when creating an image. The default wrap mode is SG_WRAP_REPEAT. */ typedef enum sg_wrap { _SG_WRAP_DEFAULT, /* value 0 reserved for default-init */ SG_WRAP_REPEAT, SG_WRAP_CLAMP_TO_EDGE, SG_WRAP_MIRRORED_REPEAT, _SG_WRAP_NUM, _SG_WRAP_FORCE_U32 = 0x7FFFFFFF } sg_wrap; /* sg_vertex_format The data type of a vertex component. This is used to describe the layout of vertex data when creating a pipeline object. */ typedef enum sg_vertex_format { SG_VERTEXFORMAT_INVALID, SG_VERTEXFORMAT_FLOAT, SG_VERTEXFORMAT_FLOAT2, SG_VERTEXFORMAT_FLOAT3, SG_VERTEXFORMAT_FLOAT4, SG_VERTEXFORMAT_BYTE4, SG_VERTEXFORMAT_BYTE4N, SG_VERTEXFORMAT_UBYTE4, SG_VERTEXFORMAT_UBYTE4N, SG_VERTEXFORMAT_SHORT2, SG_VERTEXFORMAT_SHORT2N, SG_VERTEXFORMAT_SHORT4, SG_VERTEXFORMAT_SHORT4N, SG_VERTEXFORMAT_UINT10_N2, _SG_VERTEXFORMAT_NUM, _SG_VERTEXFORMAT_FORCE_U32 = 0x7FFFFFFF } sg_vertex_format; /* sg_vertex_step Defines whether the input pointer of a vertex input stream is advanced 'per vertex' or 'per instance'. The default step-func is SG_VERTEXSTEP_PER_VERTEX. SG_VERTEXSTEP_PER_INSTANCE is used with instanced-rendering. The vertex-step is part of the vertex-layout definition when creating pipeline objects. */ typedef enum sg_vertex_step { _SG_VERTEXSTEP_DEFAULT, /* value 0 reserved for default-init */ SG_VERTEXSTEP_PER_VERTEX, SG_VERTEXSTEP_PER_INSTANCE, _SG_VERTEXSTEP_NUM, _SG_VERTEXSTEP_FORCE_U32 = 0x7FFFFFFF } sg_vertex_step; /* sg_uniform_type The data type of a uniform block member. This is used to describe the internal layout of uniform blocks when creating a shader object. */ typedef enum sg_uniform_type { SG_UNIFORMTYPE_INVALID, SG_UNIFORMTYPE_FLOAT, SG_UNIFORMTYPE_FLOAT2, SG_UNIFORMTYPE_FLOAT3, SG_UNIFORMTYPE_FLOAT4, SG_UNIFORMTYPE_MAT4, _SG_UNIFORMTYPE_NUM, _SG_UNIFORMTYPE_FORCE_U32 = 0x7FFFFFFF } sg_uniform_type; /* sg_cull_mode The face-culling mode, this is used in the sg_pipeline_desc.rasterizer.cull_mode member when creating a pipeline object. The default cull mode is SG_CULLMODE_NONE */ typedef enum sg_cull_mode { _SG_CULLMODE_DEFAULT, /* value 0 reserved for default-init */ SG_CULLMODE_NONE, SG_CULLMODE_FRONT, SG_CULLMODE_BACK, _SG_CULLMODE_NUM, _SG_CULLMODE_FORCE_U32 = 0x7FFFFFFF } sg_cull_mode; /* sg_face_winding The vertex-winding rule that determines a front-facing primitive. This is used in the member sg_pipeline_desc.rasterizer.face_winding when creating a pipeline object. The default winding is SG_FACEWINDING_CW (clockwise) */ typedef enum sg_face_winding { _SG_FACEWINDING_DEFAULT, /* value 0 reserved for default-init */ SG_FACEWINDING_CCW, SG_FACEWINDING_CW, _SG_FACEWINDING_NUM, _SG_FACEWINDING_FORCE_U32 = 0x7FFFFFFF } sg_face_winding; /* sg_compare_func The compare-function for depth- and stencil-ref tests. This is used when creating pipeline objects in the members: sg_pipeline_desc .depth_stencil .depth_compare_func .stencil_front.compare_func .stencil_back.compare_func The default compare func for depth- and stencil-tests is SG_COMPAREFUNC_ALWAYS. */ typedef enum sg_compare_func { _SG_COMPAREFUNC_DEFAULT, /* value 0 reserved for default-init */ SG_COMPAREFUNC_NEVER, SG_COMPAREFUNC_LESS, SG_COMPAREFUNC_EQUAL, SG_COMPAREFUNC_LESS_EQUAL, SG_COMPAREFUNC_GREATER, SG_COMPAREFUNC_NOT_EQUAL, SG_COMPAREFUNC_GREATER_EQUAL, SG_COMPAREFUNC_ALWAYS, _SG_COMPAREFUNC_NUM, _SG_COMPAREFUNC_FORCE_U32 = 0x7FFFFFFF } sg_compare_func; /* sg_stencil_op The operation performed on a currently stored stencil-value when a comparison test passes or fails. This is used when creating a pipeline object in the members: sg_pipeline_desc .depth_stencil .stencil_front .fail_op .depth_fail_op .pass_op .stencil_back .fail_op .depth_fail_op .pass_op The default value is SG_STENCILOP_KEEP. */ typedef enum sg_stencil_op { _SG_STENCILOP_DEFAULT, /* value 0 reserved for default-init */ SG_STENCILOP_KEEP, SG_STENCILOP_ZERO, SG_STENCILOP_REPLACE, SG_STENCILOP_INCR_CLAMP, SG_STENCILOP_DECR_CLAMP, SG_STENCILOP_INVERT, SG_STENCILOP_INCR_WRAP, SG_STENCILOP_DECR_WRAP, _SG_STENCILOP_NUM, _SG_STENCILOP_FORCE_U32 = 0x7FFFFFFF } sg_stencil_op; /* sg_blend_factor The source and destination factors in blending operations. This is used in the following members when creating a pipeline object: sg_pipeline_desc .blend .src_factor_rgb .dst_factor_rgb .src_factor_alpha .dst_factor_alpha The default value is SG_BLENDFACTOR_ONE for source factors, and SG_BLENDFACTOR_ZERO for destination factors. */ typedef enum sg_blend_factor { _SG_BLENDFACTOR_DEFAULT, /* value 0 reserved for default-init */ SG_BLENDFACTOR_ZERO, SG_BLENDFACTOR_ONE, SG_BLENDFACTOR_SRC_COLOR, SG_BLENDFACTOR_ONE_MINUS_SRC_COLOR, SG_BLENDFACTOR_SRC_ALPHA, SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SG_BLENDFACTOR_DST_COLOR, SG_BLENDFACTOR_ONE_MINUS_DST_COLOR, SG_BLENDFACTOR_DST_ALPHA, SG_BLENDFACTOR_ONE_MINUS_DST_ALPHA, SG_BLENDFACTOR_SRC_ALPHA_SATURATED, SG_BLENDFACTOR_BLEND_COLOR, SG_BLENDFACTOR_ONE_MINUS_BLEND_COLOR, SG_BLENDFACTOR_BLEND_ALPHA, SG_BLENDFACTOR_ONE_MINUS_BLEND_ALPHA, _SG_BLENDFACTOR_NUM, _SG_BLENDFACTOR_FORCE_U32 = 0x7FFFFFFF } sg_blend_factor; /* sg_blend_op Describes how the source and destination values are combined in the fragment blending operation. It is used in the following members when creating a pipeline object: sg_pipeline_desc .blend .op_rgb .op_alpha The default value is SG_BLENDOP_ADD. */ typedef enum sg_blend_op { _SG_BLENDOP_DEFAULT, /* value 0 reserved for default-init */ SG_BLENDOP_ADD, SG_BLENDOP_SUBTRACT, SG_BLENDOP_REVERSE_SUBTRACT, _SG_BLENDOP_NUM, _SG_BLENDOP_FORCE_U32 = 0x7FFFFFFF } sg_blend_op; /* sg_color_mask Selects the color channels when writing a fragment color to the framebuffer. This is used in the members sg_pipeline_desc.blend.color_write_mask when creating a pipeline object. The default colormask is SG_COLORMASK_RGBA (write all colors channels) */ typedef enum sg_color_mask { _SG_COLORMASK_DEFAULT = 0, /* value 0 reserved for default-init */ SG_COLORMASK_NONE = (0x10), /* special value for 'all channels disabled */ SG_COLORMASK_R = (1<<0), SG_COLORMASK_G = (1<<1), SG_COLORMASK_B = (1<<2), SG_COLORMASK_A = (1<<3), SG_COLORMASK_RGB = 0x7, SG_COLORMASK_RGBA = 0xF, _SG_COLORMASK_FORCE_U32 = 0x7FFFFFFF } sg_color_mask; /* sg_action Defines what action should be performed at the start of a render pass: SG_ACTION_CLEAR: clear the render target image SG_ACTION_LOAD: load the previous content of the render target image SG_ACTION_DONTCARE: leave the render target image content undefined This is used in the sg_pass_action structure. The default action for all pass attachments is SG_ACTION_CLEAR, with the clear color rgba = {0.5f, 0.5f, 0.5f, 1.0f], depth=1.0 and stencil=0. If you want to override the default behaviour, it is important to not only set the clear color, but the 'action' field as well (as long as this is in its _SG_ACTION_DEFAULT, the value fields will be ignored). */ typedef enum sg_action { _SG_ACTION_DEFAULT, SG_ACTION_CLEAR, SG_ACTION_LOAD, SG_ACTION_DONTCARE, _SG_ACTION_NUM, _SG_ACTION_FORCE_U32 = 0x7FFFFFFF } sg_action; /* sg_pass_action The sg_pass_action struct defines the actions to be performed at the start of a rendering pass in the functions sg_begin_pass() and sg_begin_default_pass(). A separate action and clear values can be defined for each color attachment, and for the depth-stencil attachment. The default clear values are defined by the macros: - SG_DEFAULT_CLEAR_RED: 0.5f - SG_DEFAULT_CLEAR_GREEN: 0.5f - SG_DEFAULT_CLEAR_BLUE: 0.5f - SG_DEFAULT_CLEAR_ALPHA: 1.0f - SG_DEFAULT_CLEAR_DEPTH: 1.0f - SG_DEFAULT_CLEAR_STENCIL: 0 */ typedef struct sg_color_attachment_action { sg_action action; float val[4]; } sg_color_attachment_action; typedef struct sg_depth_attachment_action { sg_action action; float val; } sg_depth_attachment_action; typedef struct sg_stencil_attachment_action { sg_action action; uint8_t val; } sg_stencil_attachment_action; typedef struct sg_pass_action { uint32_t _start_canary; sg_color_attachment_action colors[SG_MAX_COLOR_ATTACHMENTS]; sg_depth_attachment_action depth; sg_stencil_attachment_action stencil; uint32_t _end_canary; } sg_pass_action; /* sg_bindings The sg_bindings structure defines the resource binding slots of the sokol_gfx render pipeline, used as argument to the sg_apply_bindings() function. A resource binding struct contains: - 1..N vertex buffers - 0..N vertex buffer offsets - 0..1 index buffers - 0..1 index buffer offsets - 0..N vertex shader stage images - 0..N fragment shader stage images The max number of vertex buffer and shader stage images are defined by the SG_MAX_SHADERSTAGE_BUFFERS and SG_MAX_SHADERSTAGE_IMAGES configuration constants. The optional buffer offsets can be used to group different chunks of vertex- and/or index-data into the same buffer objects. */ typedef struct sg_bindings { uint32_t _start_canary; sg_buffer vertex_buffers[SG_MAX_SHADERSTAGE_BUFFERS]; int vertex_buffer_offsets[SG_MAX_SHADERSTAGE_BUFFERS]; sg_buffer index_buffer; int index_buffer_offset; sg_image vs_images[SG_MAX_SHADERSTAGE_IMAGES]; sg_image fs_images[SG_MAX_SHADERSTAGE_IMAGES]; uint32_t _end_canary; } sg_bindings; /* sg_buffer_desc Creation parameters for sg_buffer objects, used in the sg_make_buffer() call. The default configuration is: .size: 0 (this *must* be set to a valid size in bytes) .type: SG_BUFFERTYPE_VERTEXBUFFER .usage: SG_USAGE_IMMUTABLE .content 0 .label 0 (optional string label for trace hooks) The dbg_label will be ignored by sokol_gfx.h, it is only useful when hooking into sg_make_buffer() or sg_init_buffer() via the sg_install_trace_hook ADVANCED TOPIC: Injecting native 3D-API buffers: The following struct members allow to inject your own GL, Metal or D3D11 buffers into sokol_gfx: .gl_buffers[SG_NUM_INFLIGHT_FRAMES] .mtl_buffers[SG_NUM_INFLIGHT_FRAMES] .d3d11_buffer You must still provide all other members except the .content member, and these must match the creation parameters of the native buffers you provide. For SG_USAGE_IMMUTABLE, only provide a single native 3D-API buffer, otherwise you need to provide SG_NUM_INFLIGHT_FRAMES buffers (only for GL and Metal, not D3D11). Providing multiple buffers for GL and Metal is necessary because sokol_gfx will rotate through them when calling sg_update_buffer() to prevent lock-stalls. Note that it is expected that immutable injected buffer have already been initialized with content, and the .content member must be 0! Also you need to call sg_reset_state_cache() after calling native 3D-API functions, and before calling any sokol_gfx function. */ typedef struct sg_buffer_desc { uint32_t _start_canary; int size; sg_buffer_type type; sg_usage usage; const void* content; const char* label; /* GL specific */ uint32_t gl_buffers[SG_NUM_INFLIGHT_FRAMES]; /* Metal specific */ const void* mtl_buffers[SG_NUM_INFLIGHT_FRAMES]; /* D3D11 specific */ const void* d3d11_buffer; uint32_t _end_canary; } sg_buffer_desc; /* sg_subimage_content Pointer to and size of a subimage-surface data, this is used to describe the initial content of immutable-usage images, or for updating a dynamic- or stream-usage images. For 3D- or array-textures, one sg_subimage_content item describes an entire mipmap level consisting of all array- or 3D-slices of the mipmap level. It is only possible to update an entire mipmap level, not parts of it. */ typedef struct sg_subimage_content { const void* ptr; /* pointer to subimage data */ int size; /* size in bytes of pointed-to subimage data */ } sg_subimage_content; /* sg_image_content Defines the content of an image through a 2D array of sg_subimage_content structs. The first array dimension is the cubemap face, and the second array dimension the mipmap level. */ typedef struct sg_image_content { sg_subimage_content subimage[SG_CUBEFACE_NUM][SG_MAX_MIPMAPS]; } sg_image_content; /* sg_image_desc Creation parameters for sg_image objects, used in the sg_make_image() call. The default configuration is: .type: SG_IMAGETYPE_2D .render_target: false .width 0 (must be set to >0) .height 0 (must be set to >0) .depth/.layers: 1 .num_mipmaps: 1 .usage: SG_USAGE_IMMUTABLE .pixel_format: SG_PIXELFORMAT_RGBA8 .sample_count: 1 (only used in render_targets) .min_filter: SG_FILTER_NEAREST .mag_filter: SG_FILTER_NEAREST .wrap_u: SG_WRAP_REPEAT .wrap_v: SG_WRAP_REPEAT .wrap_w: SG_WRAP_REPEAT (only SG_IMAGETYPE_3D) .max_anisotropy 1 (must be 1..16) .min_lod 0.0f .max_lod FLT_MAX .content an sg_image_content struct to define the initial content .label 0 (optional string label for trace hooks) SG_IMAGETYPE_ARRAY and SG_IMAGETYPE_3D are not supported on WebGL/GLES2, use sg_query_feature(SG_FEATURE_IMAGETYPE_ARRAY) and sg_query_feature(SG_FEATURE_IMAGETYPE_3D) at runtime to check if array- and 3D-textures are supported. Images with usage SG_USAGE_IMMUTABLE must be fully initialized by providing a valid .content member which points to initialization data. ADVANCED TOPIC: Injecting native 3D-API textures: The following struct members allow to inject your own GL, Metal or D3D11 textures into sokol_gfx: .gl_textures[SG_NUM_INFLIGHT_FRAMES] .mtl_textures[SG_NUM_INFLIGHT_FRAMES] .d3d11_texture The same rules apply as for injecting native buffers (see sg_buffer_desc documentation for more details). */ typedef struct sg_image_desc { uint32_t _start_canary; sg_image_type type; bool render_target; int width; int height; union { int depth; int layers; }; int num_mipmaps; sg_usage usage; sg_pixel_format pixel_format; int sample_count; sg_filter min_filter; sg_filter mag_filter; sg_wrap wrap_u; sg_wrap wrap_v; sg_wrap wrap_w; uint32_t max_anisotropy; float min_lod; float max_lod; sg_image_content content; const char* label; /* GL specific */ uint32_t gl_textures[SG_NUM_INFLIGHT_FRAMES]; /* Metal specific */ const void* mtl_textures[SG_NUM_INFLIGHT_FRAMES]; /* D3D11 specific */ const void* d3d11_texture; uint32_t _end_canary; } sg_image_desc; /* sg_shader_desc The structure sg_shader_desc defines all creation parameters for shader programs, used as input to the sg_make_shader() function: - reflection information for vertex attributes (vertex shader inputs): - vertex attribute name (required for GLES2, optional for GLES3 and GL) - a semantic name and index (required for D3D11) - for each vertex- and fragment-shader-stage: - the shader source or bytecode - an optional entry function name - reflection info for each uniform block used by the shader stage: - the size of the uniform block in bytes - reflection info for each uniform block member (only required for GL backends): - member name - member type (SG_UNIFORMTYPE_xxx) - if the member is an array, the number of array items - reflection info for the texture images used by the shader stage: - the image type (SG_IMAGETYPE_xxx) - the name of the texture sampler (required for GLES2, optional everywhere else) For all GL backends, shader source-code must be provided. For D3D11 and Metal, either shader source-code or byte-code can be provided. For D3D11, if source code is provided, the d3dcompiler_47.dll will be loaded on demand. If this fails, shader creation will fail. */ typedef struct sg_shader_attr_desc { const char* name; /* GLSL vertex attribute name (only required for GLES2) */ const char* sem_name; /* HLSL semantic name */ int sem_index; /* HLSL semantic index */ } sg_shader_attr_desc; typedef struct sg_shader_uniform_desc { const char* name; sg_uniform_type type; int array_count; } sg_shader_uniform_desc; typedef struct sg_shader_uniform_block_desc { int size; sg_shader_uniform_desc uniforms[SG_MAX_UB_MEMBERS]; } sg_shader_uniform_block_desc; typedef struct sg_shader_image_desc { const char* name; sg_image_type type; } sg_shader_image_desc; typedef struct sg_shader_stage_desc { const char* source; const uint8_t* byte_code; int byte_code_size; const char* entry; sg_shader_uniform_block_desc uniform_blocks[SG_MAX_SHADERSTAGE_UBS]; sg_shader_image_desc images[SG_MAX_SHADERSTAGE_IMAGES]; } sg_shader_stage_desc; typedef struct sg_shader_desc { uint32_t _start_canary; sg_shader_attr_desc attrs[SG_MAX_VERTEX_ATTRIBUTES]; sg_shader_stage_desc vs; sg_shader_stage_desc fs; const char* label; uint32_t _end_canary; } sg_shader_desc; /* sg_pipeline_desc The sg_pipeline_desc struct defines all creation parameters for an sg_pipeline object, used as argument to the sg_make_pipeline() function: - the vertex layout for all input vertex buffers - a shader object - the 3D primitive type (points, lines, triangles, ...) - the index type (none, 16- or 32-bit) - depth-stencil state - alpha-blending state - rasterizer state If the vertex data has no gaps between vertex components, you can omit the .layout.buffers[].stride and layout.attrs[].offset items (leave them default-initialized to 0), sokol will then compute the offsets and strides from the vertex component formats (.layout.attrs[].offset). Please note that ALL vertex attribute offsets must be 0 in order for the the automatic offset computation to kick in. The default configuration is as follows: .layout: .buffers[]: vertex buffer layouts .stride: 0 (if no stride is given it will be computed) .step_func SG_VERTEXSTEP_PER_VERTEX .step_rate 1 .attrs[]: vertex attribute declarations .buffer_index 0 the vertex buffer bind slot .offset 0 (offsets can be omitted if the vertex layout has no gaps) .format SG_VERTEXFORMAT_INVALID (must be initialized!) .shader: 0 (must be intilized with a valid sg_shader id!) .primitive_type: SG_PRIMITIVETYPE_TRIANGLES .index_type: SG_INDEXTYPE_NONE .depth_stencil: .stencil_front, .stencil_back: .fail_op: SG_STENCILOP_KEEP .depth_fail_op: SG_STENCILOP_KEEP .pass_op: SG_STENCILOP_KEEP .compare_func SG_COMPAREFUNC_ALWAYS .depth_compare_func: SG_COMPAREFUNC_ALWAYS .depth_write_enabled: false .stencil_enabled: false .stencil_read_mask: 0 .stencil_write_mask: 0 .stencil_ref: 0 .blend: .enabled: false .src_factor_rgb: SG_BLENDFACTOR_ONE .dst_factor_rgb: SG_BLENDFACTOR_ZERO .op_rgb: SG_BLENDOP_ADD .src_factor_alpha: SG_BLENDFACTOR_ONE .dst_factor_alpha: SG_BLENDFACTOR_ZERO .op_alpha: SG_BLENDOP_ADD .color_write_mask: SG_COLORMASK_RGBA .color_attachment_count 1 .color_format SG_PIXELFORMAT_RGBA8 .depth_format SG_PIXELFORMAT_DEPTHSTENCIL .blend_color: { 0.0f, 0.0f, 0.0f, 0.0f } .rasterizer: .alpha_to_coverage_enabled: false .cull_mode: SG_CULLMODE_NONE .face_winding: SG_FACEWINDING_CW .sample_count: 1 .depth_bias: 0.0f .depth_bias_slope_scale: 0.0f .depth_bias_clamp: 0.0f .label 0 (optional string label for trace hooks) */ typedef struct sg_buffer_layout_desc { int stride; sg_vertex_step step_func; int step_rate; } sg_buffer_layout_desc; typedef struct sg_vertex_attr_desc { int buffer_index; int offset; sg_vertex_format format; } sg_vertex_attr_desc; typedef struct sg_layout_desc { sg_buffer_layout_desc buffers[SG_MAX_SHADERSTAGE_BUFFERS]; sg_vertex_attr_desc attrs[SG_MAX_VERTEX_ATTRIBUTES]; } sg_layout_desc; typedef struct sg_stencil_state { sg_stencil_op fail_op; sg_stencil_op depth_fail_op; sg_stencil_op pass_op; sg_compare_func compare_func; } sg_stencil_state; typedef struct sg_depth_stencil_state { sg_stencil_state stencil_front; sg_stencil_state stencil_back; sg_compare_func depth_compare_func; bool depth_write_enabled; bool stencil_enabled; uint8_t stencil_read_mask; uint8_t stencil_write_mask; uint8_t stencil_ref; } sg_depth_stencil_state; typedef struct sg_blend_state { bool enabled; sg_blend_factor src_factor_rgb; sg_blend_factor dst_factor_rgb; sg_blend_op op_rgb; sg_blend_factor src_factor_alpha; sg_blend_factor dst_factor_alpha; sg_blend_op op_alpha; uint8_t color_write_mask; int color_attachment_count; sg_pixel_format color_format; sg_pixel_format depth_format; float blend_color[4]; } sg_blend_state; typedef struct sg_rasterizer_state { bool alpha_to_coverage_enabled; sg_cull_mode cull_mode; sg_face_winding face_winding; int sample_count; float depth_bias; float depth_bias_slope_scale; float depth_bias_clamp; } sg_rasterizer_state; typedef struct sg_pipeline_desc { uint32_t _start_canary; sg_layout_desc layout; sg_shader shader; sg_primitive_type primitive_type; sg_index_type index_type; sg_depth_stencil_state depth_stencil; sg_blend_state blend; sg_rasterizer_state rasterizer; const char* label; uint32_t _end_canary; } sg_pipeline_desc; /* sg_pass_desc Creation parameters for an sg_pass object, used as argument to the sg_make_pass() function. A pass object contains 1..4 color-attachments and none, or one, depth-stencil-attachment. Each attachment consists of an image, and two additional indices describing which subimage the pass will render: one mipmap index, and if the image is a cubemap, array-texture or 3D-texture, the face-index, array-layer or depth-slice. Pass images must fulfill the following requirements: All images must have: - been created as render target (sg_image_desc.render_target = true) - the same size - the same sample count In addition, all color-attachment images must have the same pixel format. */ typedef struct sg_attachment_desc { sg_image image; int mip_level; union { int face; int layer; int slice; }; } sg_attachment_desc; typedef struct sg_pass_desc { uint32_t _start_canary; sg_attachment_desc color_attachments[SG_MAX_COLOR_ATTACHMENTS]; sg_attachment_desc depth_stencil_attachment; const char* label; uint32_t _end_canary; } sg_pass_desc; /* sg_trace_hooks Installable callback function to keep track of the sokol_gfx calls, this is useful for debugging, or keeping track of resource creation and destruction. Trace hooks are installed with sg_install_trace_hooks(), this returns another sg_trace_hooks functions with the previous set of trace hook function pointers. These should be invoked by the new trace hooks to form a proper call chain. */ typedef struct sg_trace_hooks { void* user_data; void (*query_feature)(sg_feature feature, bool result, void* user_data); void (*reset_state_cache)(void* user_data); void (*make_buffer)(const sg_buffer_desc* desc, sg_buffer result, void* user_data); void (*make_image)(const sg_image_desc* desc, sg_image result, void* user_data); void (*make_shader)(const sg_shader_desc* desc, sg_shader result, void* user_data); void (*make_pipeline)(const sg_pipeline_desc* desc, sg_pipeline result, void* user_data); void (*make_pass)(const sg_pass_desc* desc, sg_pass result, void* user_data); void (*destroy_buffer)(sg_buffer buf, void* user_data); void (*destroy_image)(sg_image img, void* user_data); void (*destroy_shader)(sg_shader shd, void* user_data); void (*destroy_pipeline)(sg_pipeline pip, void* user_data); void (*destroy_pass)(sg_pass pass, void* user_data); void (*update_buffer)(sg_buffer buf, const void* data_ptr, int data_size, void* user_data); void (*update_image)(sg_image img, const sg_image_content* data, void* user_data); void (*append_buffer)(sg_buffer buf, const void* data_ptr, int data_size, int result, void* user_data); void (*begin_default_pass)(const sg_pass_action* pass_action, int width, int height, void* user_data); void (*begin_pass)(sg_pass pass, const sg_pass_action* pass_action, void* user_data); void (*apply_viewport)(int x, int y, int width, int height, bool origin_top_left, void* user_data); void (*apply_scissor_rect)(int x, int y, int width, int height, bool origin_top_left, void* user_data); void (*apply_pipeline)(sg_pipeline pip, void* user_data); void (*apply_bindings)(const sg_bindings* bindings, void* user_data); void (*apply_uniforms)(sg_shader_stage stage, int ub_index, const void* data, int num_bytes, void* user_data); void (*draw)(int base_element, int num_elements, int num_instances, void* user_data); void (*end_pass)(void* user_data); void (*commit)(void* user_data); void (*alloc_buffer)(sg_buffer result, void* user_data); void (*alloc_image)(sg_image result, void* user_data); void (*alloc_shader)(sg_shader result, void* user_data); void (*alloc_pipeline)(sg_pipeline result, void* user_data); void (*alloc_pass)(sg_pass result, void* user_data); void (*init_buffer)(sg_buffer buf_id, const sg_buffer_desc* desc, void* user_data); void (*init_image)(sg_image img_id, const sg_image_desc* desc, void* user_data); void (*init_shader)(sg_shader shd_id, const sg_shader_desc* desc, void* user_data); void (*init_pipeline)(sg_pipeline pip_id, const sg_pipeline_desc* desc, void* user_data); void (*init_pass)(sg_pass pass_id, const sg_pass_desc* desc, void* user_data); void (*fail_buffer)(sg_buffer buf_id, void* user_data); void (*fail_image)(sg_image img_id, void* user_data); void (*fail_shader)(sg_shader shd_id, void* user_data); void (*fail_pipeline)(sg_pipeline pip_id, void* user_data); void (*fail_pass)(sg_pass pass_id, void* user_data); void (*push_debug_group)(const char* name, void* user_data); void (*pop_debug_group)(void* user_data); void (*err_buffer_pool_exhausted)(void* user_data); void (*err_image_pool_exhausted)(void* user_data); void (*err_shader_pool_exhausted)(void* user_data); void (*err_pipeline_pool_exhausted)(void* user_data); void (*err_pass_pool_exhausted)(void* user_data); void (*err_context_mismatch)(void* user_data); void (*err_pass_invalid)(void* user_data); void (*err_draw_invalid)(void* user_data); void (*err_bindings_invalid)(void* user_data); } sg_trace_hooks; /* sg_buffer_info sg_image_info sg_shader_info sg_pipeline_info sg_pass_info These structs contain various internal resource attributes which might be useful for debug-inspection. Please don't rely on the actual content of those structs too much, as they are quite closely tied to sokol_gfx.h internals and may change more frequently than the other public API elements. The *_info structs are used as the return values of the following functions: sg_query_buffer_info() sg_query_image_info() sg_query_shader_info() sg_query_pipeline_info() sg_query_pass_info() */ typedef struct sg_slot_info { sg_resource_state state; /* the current state of this resource slot */ uint32_t res_id; /* type-neutral resource if (e.g. sg_buffer.id) */ uint32_t ctx_id; /* the context this resource belongs to */ } sg_slot_info; typedef struct sg_buffer_info { sg_slot_info slot; /* resource pool slot info */ uint32_t update_frame_index; /* frame index of last sg_update_buffer() */ uint32_t append_frame_index; /* frame index of last sg_append_buffer() */ int append_pos; /* current position in buffer for sg_append_buffer() */ bool append_overflow; /* is buffer in overflow state (due to sg_append_buffer) */ int num_slots; /* number of renaming-slots for dynamically updated buffers */ int active_slot; /* currently active write-slot for dynamically updated buffers */ } sg_buffer_info; typedef struct sg_image_info { sg_slot_info slot; /* resource pool slot info */ uint32_t upd_frame_index; /* frame index of last sg_update_image() */ int num_slots; /* number of renaming-slots for dynamically updated images */ int active_slot; /* currently active write-slot for dynamically updated images */ } sg_image_info; typedef struct sg_shader_info { sg_slot_info slot; /* resoure pool slot info */ } sg_shader_info; typedef struct sg_pipeline_info { sg_slot_info slot; /* resource pool slot info */ } sg_pipeline_info; typedef struct sg_pass_info { sg_slot_info slot; /* resource pool slot info */ } sg_pass_info; /* sg_desc The sg_desc struct contains configuration values for sokol_gfx, it is used as parameter to the sg_setup() call. The default configuration is: .buffer_pool_size: 128 .image_pool_size: 128 .shader_pool_size: 32 .pipeline_pool_size: 64 .pass_pool_size: 16 .context_pool_size: 16 GL specific: .gl_force_gles2 if this is true the GL backend will act in "GLES2 fallback mode" even when compiled with SOKOL_GLES3, this is useful to fall back to traditional WebGL if a browser doesn't support a WebGL2 context Metal specific: (NOTE: All Objective-C object references are transferred through a bridged (const void*) to sokol_gfx, which will use a unretained bridged cast (__bridged id) to retrieve the Objective-C references back. Since the bridge cast is unretained, the caller must hold a strong reference to the Objective-C object for the duration of the sokol_gfx call! .mtl_device a pointer to the MTLDevice object .mtl_renderpass_descriptor_cb a C callback function to obtain the MTLRenderPassDescriptor for the current frame when rendering to the default framebuffer, will be called in sg_begin_default_pass() .mtl_drawable_cb a C callback function to obtain a MTLDrawable for the current frame when rendering to the default framebuffer, will be called in sg_end_pass() of the default pass .mtl_global_uniform_buffer_size the size of the global uniform buffer in bytes, this must be big enough to hold all uniform block updates for a single frame, the default value is 4 MByte (4 * 1024 * 1024) .mtl_sampler_cache_size the number of slots in the sampler cache, the Metal backend will share texture samplers with the same state in this cache, the default value is 64 D3D11 specific: .d3d11_device a pointer to the ID3D11Device object, this must have been created before sg_setup() is called .d3d11_device_context a pointer to the ID3D11DeviceContext object .d3d11_render_target_view_cb a C callback function to obtain a pointer to the current ID3D11RenderTargetView object of the default framebuffer, this function will be called in sg_begin_pass() when rendering to the default framebuffer .d3d11_depth_stencil_view_cb a C callback function to obtain a pointer to the current ID3D11DepthStencilView object of the default framebuffer, this function will be called in sg_begin_pass() when rendering to the default framebuffer */ typedef struct sg_desc { uint32_t _start_canary; int buffer_pool_size; int image_pool_size; int shader_pool_size; int pipeline_pool_size; int pass_pool_size; int context_pool_size; /* GL specific */ bool gl_force_gles2; /* Metal-specific */ const void* mtl_device; const void* (*mtl_renderpass_descriptor_cb)(void); const void* (*mtl_drawable_cb)(void); int mtl_global_uniform_buffer_size; int mtl_sampler_cache_size; /* D3D11-specific */ const void* d3d11_device; const void* d3d11_device_context; const void* (*d3d11_render_target_view_cb)(void); const void* (*d3d11_depth_stencil_view_cb)(void); uint32_t _end_canary; } sg_desc; /* setup and misc functions */ SOKOL_API_DECL void sg_setup(const sg_desc* desc); SOKOL_API_DECL void sg_shutdown(void); SOKOL_API_DECL bool sg_isvalid(void); SOKOL_API_DECL sg_desc sg_query_desc(void); SOKOL_API_DECL sg_backend sg_query_backend(void); SOKOL_API_DECL bool sg_query_feature(sg_feature feature); SOKOL_API_DECL void sg_reset_state_cache(void); SOKOL_API_DECL sg_trace_hooks sg_install_trace_hooks(const sg_trace_hooks* trace_hooks); SOKOL_API_DECL void sg_push_debug_group(const char* name); SOKOL_API_DECL void sg_pop_debug_group(void); /* resource creation, destruction and updating */ SOKOL_API_DECL sg_buffer sg_make_buffer(const sg_buffer_desc* desc); SOKOL_API_DECL sg_image sg_make_image(const sg_image_desc* desc); SOKOL_API_DECL sg_shader sg_make_shader(const sg_shader_desc* desc); SOKOL_API_DECL sg_pipeline sg_make_pipeline(const sg_pipeline_desc* desc); SOKOL_API_DECL sg_pass sg_make_pass(const sg_pass_desc* desc); SOKOL_API_DECL void sg_destroy_buffer(sg_buffer buf); SOKOL_API_DECL void sg_destroy_image(sg_image img); SOKOL_API_DECL void sg_destroy_shader(sg_shader shd); SOKOL_API_DECL void sg_destroy_pipeline(sg_pipeline pip); SOKOL_API_DECL void sg_destroy_pass(sg_pass pass); SOKOL_API_DECL void sg_update_buffer(sg_buffer buf, const void* data_ptr, int data_size); SOKOL_API_DECL void sg_update_image(sg_image img, const sg_image_content* data); SOKOL_API_DECL int sg_append_buffer(sg_buffer buf, const void* data_ptr, int data_size); SOKOL_API_DECL bool sg_query_buffer_overflow(sg_buffer buf); /* get resource state (initial, alloc, valid, failed) */ SOKOL_API_DECL sg_resource_state sg_query_buffer_state(sg_buffer buf); SOKOL_API_DECL sg_resource_state sg_query_image_state(sg_image img); SOKOL_API_DECL sg_resource_state sg_query_shader_state(sg_shader shd); SOKOL_API_DECL sg_resource_state sg_query_pipeline_state(sg_pipeline pip); SOKOL_API_DECL sg_resource_state sg_query_pass_state(sg_pass pass); /* rendering functions */ SOKOL_API_DECL void sg_begin_default_pass(const sg_pass_action* pass_action, int width, int height); SOKOL_API_DECL void sg_begin_pass(sg_pass pass, const sg_pass_action* pass_action); SOKOL_API_DECL void sg_apply_viewport(int x, int y, int width, int height, bool origin_top_left); SOKOL_API_DECL void sg_apply_scissor_rect(int x, int y, int width, int height, bool origin_top_left); SOKOL_API_DECL void sg_apply_pipeline(sg_pipeline pip); SOKOL_API_DECL void sg_apply_bindings(const sg_bindings* bindings); SOKOL_API_DECL void sg_apply_uniforms(sg_shader_stage stage, int ub_index, const void* data, int num_bytes); SOKOL_API_DECL void sg_draw(int base_element, int num_elements, int num_instances); SOKOL_API_DECL void sg_end_pass(void); SOKOL_API_DECL void sg_commit(void); /* separate resource allocation and initialization (for async setup) */ SOKOL_API_DECL sg_buffer sg_alloc_buffer(void); SOKOL_API_DECL sg_image sg_alloc_image(void); SOKOL_API_DECL sg_shader sg_alloc_shader(void); SOKOL_API_DECL sg_pipeline sg_alloc_pipeline(void); SOKOL_API_DECL sg_pass sg_alloc_pass(void); SOKOL_API_DECL void sg_init_buffer(sg_buffer buf_id, const sg_buffer_desc* desc); SOKOL_API_DECL void sg_init_image(sg_image img_id, const sg_image_desc* desc); SOKOL_API_DECL void sg_init_shader(sg_shader shd_id, const sg_shader_desc* desc); SOKOL_API_DECL void sg_init_pipeline(sg_pipeline pip_id, const sg_pipeline_desc* desc); SOKOL_API_DECL void sg_init_pass(sg_pass pass_id, const sg_pass_desc* desc); SOKOL_API_DECL void sg_fail_buffer(sg_buffer buf_id); SOKOL_API_DECL void sg_fail_image(sg_image img_id); SOKOL_API_DECL void sg_fail_shader(sg_shader shd_id); SOKOL_API_DECL void sg_fail_pipeline(sg_pipeline pip_id); SOKOL_API_DECL void sg_fail_pass(sg_pass pass_id); /* get internal resource attributes */ SOKOL_API_DECL sg_buffer_info sg_query_buffer_info(sg_buffer buf); SOKOL_API_DECL sg_image_info sg_query_image_info(sg_image img); SOKOL_API_DECL sg_shader_info sg_query_shader_info(sg_shader shd); SOKOL_API_DECL sg_pipeline_info sg_query_pipeline_info(sg_pipeline pip); SOKOL_API_DECL sg_pass_info sg_query_pass_info(sg_pass pass); /* rendering contexts (optional) */ SOKOL_API_DECL sg_context sg_setup_context(void); SOKOL_API_DECL void sg_activate_context(sg_context ctx_id); SOKOL_API_DECL void sg_discard_context(sg_context ctx_id); /* deprecated structs and functions */ #ifndef SOKOL_NO_DEPRECATED typedef struct sg_draw_state { uint32_t _start_canary; sg_pipeline pipeline; sg_buffer vertex_buffers[SG_MAX_SHADERSTAGE_BUFFERS]; int vertex_buffer_offsets[SG_MAX_SHADERSTAGE_BUFFERS]; sg_buffer index_buffer; int index_buffer_offset; sg_image vs_images[SG_MAX_SHADERSTAGE_IMAGES]; sg_image fs_images[SG_MAX_SHADERSTAGE_IMAGES]; uint32_t _end_canary; } sg_draw_state; SOKOL_API_DECL void sg_apply_draw_state(const sg_draw_state* ds); SOKOL_API_DECL void sg_apply_uniform_block(sg_shader_stage stage, int ub_index, const void* data, int num_bytes); #endif #ifdef _MSC_VER #pragma warning(pop) #endif #ifdef __cplusplus } /* extern "C" */ #endif /*--- IMPLEMENTATION ---------------------------------------------------------*/ #ifdef SOKOL_IMPL #define SOKOL_GFX_IMPL_INCLUDED (1) #if !(defined(SOKOL_GLCORE33)||defined(SOKOL_GLES2)||defined(SOKOL_GLES3)||defined(SOKOL_D3D11)||defined(SOKOL_METAL)||defined(SOKOL_DUMMY_BACKEND)) #error "Please select a backend with SOKOL_GLCORE33, SOKOL_GLES2, SOKOL_GLES3, SOKOL_D3D11, SOKOL_METAL or SOKOL_DUMMY_BACKEND" #endif #include /* memset */ #include /* FLT_MAX */ #ifndef SOKOL_API_IMPL #define SOKOL_API_IMPL #endif #ifndef SOKOL_DEBUG #ifndef NDEBUG #define SOKOL_DEBUG (1) #endif #endif #ifndef SOKOL_ASSERT #include #define SOKOL_ASSERT(c) assert(c) #endif #ifndef SOKOL_VALIDATE_BEGIN #define SOKOL_VALIDATE_BEGIN() _sg_validate_begin() #endif #ifndef SOKOL_VALIDATE #define SOKOL_VALIDATE(cond, err) _sg_validate((cond), err) #endif #ifndef SOKOL_VALIDATE_END #define SOKOL_VALIDATE_END() _sg_validate_end() #endif #ifndef SOKOL_UNREACHABLE #define SOKOL_UNREACHABLE SOKOL_ASSERT(false) #endif #ifndef SOKOL_MALLOC #include #define SOKOL_MALLOC(s) malloc(s) #define SOKOL_FREE(p) free(p) #endif #ifndef SOKOL_LOG #ifdef SOKOL_DEBUG #include #define SOKOL_LOG(s) { SOKOL_ASSERT(s); puts(s); } #else #define SOKOL_LOG(s) #endif #endif #ifndef _SOKOL_PRIVATE #if defined(__GNUC__) #define _SOKOL_PRIVATE __attribute__((unused)) static #else #define _SOKOL_PRIVATE static #endif #endif #ifndef _SOKOL_UNUSED #define _SOKOL_UNUSED(x) (void)(x) #endif #if defined(SOKOL_TRACE_HOOKS) #define _SG_TRACE_ARGS(fn, ...) if (_sg.hooks.fn) { _sg.hooks.fn(__VA_ARGS__, _sg.hooks.user_data); } #define _SG_TRACE_NOARGS(fn) if (_sg.hooks.fn) { _sg.hooks.fn(_sg.hooks.user_data); } #else #define _SG_TRACE_ARGS(fn, ...) #define _SG_TRACE_NOARGS(fn) #endif /* default clear values */ #ifndef SG_DEFAULT_CLEAR_RED #define SG_DEFAULT_CLEAR_RED (0.5f) #endif #ifndef SG_DEFAULT_CLEAR_GREEN #define SG_DEFAULT_CLEAR_GREEN (0.5f) #endif #ifndef SG_DEFAULT_CLEAR_BLUE #define SG_DEFAULT_CLEAR_BLUE (0.5f) #endif #ifndef SG_DEFAULT_CLEAR_ALPHA #define SG_DEFAULT_CLEAR_ALPHA (1.0f) #endif #ifndef SG_DEFAULT_CLEAR_DEPTH #define SG_DEFAULT_CLEAR_DEPTH (1.0f) #endif #ifndef SG_DEFAULT_CLEAR_STENCIL #define SG_DEFAULT_CLEAR_STENCIL (0) #endif #if defined(SOKOL_GLCORE33) || defined(SOKOL_GLES2) || defined(SOKOL_GLES3) #ifndef GL_UNSIGNED_INT_2_10_10_10_REV #define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368 #endif #ifndef GL_UNSIGNED_INT_24_8 #define GL_UNSIGNED_INT_24_8 0x84FA #endif #ifndef GL_TEXTURE_MAX_ANISOTROPY_EXT #define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE #endif #ifndef GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT #define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF #endif #ifndef GL_COMPRESSED_RGBA_S3TC_DXT1_EXT #define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1 #endif #ifndef GL_COMPRESSED_RGBA_S3TC_DXT3_EXT #define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2 #endif #ifndef GL_COMPRESSED_RGBA_S3TC_DXT5_EXT #define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 #endif #ifndef GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG #define GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG 0x8C01 #endif #ifndef GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG #define GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG 0x8C00 #endif #ifndef GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG #define GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG 0x8C03 #endif #ifndef GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG #define GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG 0x8C02 #endif #ifndef GL_COMPRESSED_RGB8_ETC2 #define GL_COMPRESSED_RGB8_ETC2 0x9274 #endif #ifndef GL_COMPRESSED_SRGB8_ETC2 #define GL_COMPRESSED_SRGB8_ETC2 0x9275 #endif #ifndef GL_DEPTH24_STENCIL8 #define GL_DEPTH24_STENCIL8 0x88F0 #endif #ifndef GL_HALF_FLOAT #define GL_HALF_FLOAT 0x140B #endif #ifndef GL_DEPTH_STENCIL #define GL_DEPTH_STENCIL 0x84F9 #endif #ifndef GL_LUMINANCE #define GL_LUMINANCE 0x1909 #endif #ifdef SOKOL_GLES2 # ifdef GL_ANGLE_instanced_arrays # define SOKOL_INSTANCING_ENABLED # define glDrawArraysInstanced(mode, first, count, instancecount) glDrawArraysInstancedANGLE(mode, first, count, instancecount) # define glDrawElementsInstanced(mode, count, type, indices, instancecount) glDrawElementsInstancedANGLE(mode, count, type, indices, instancecount) # define glVertexAttribDivisor(index, divisor) glVertexAttribDivisorANGLE(index, divisor) # elif defined(GL_EXT_draw_instanced) && defined(GL_EXT_instanced_arrays) # define SOKOL_INSTANCING_ENABLED # define glDrawArraysInstanced(mode, first, count, instancecount) glDrawArraysInstancedEXT(mode, first, count, instancecount) # define glDrawElementsInstanced(mode, count, type, indices, instancecount) glDrawElementsInstancedEXT(mode, count, type, indices, instancecount) # define glVertexAttribDivisor(index, divisor) glVertexAttribDivisorEXT(index, divisor) # else # define SOKOL_GLES2_INSTANCING_ERROR "Select GL_ANGLE_instanced_arrays or (GL_EXT_draw_instanced & GL_EXT_instanced_arrays) to enable instancing in GLES2" # define glDrawArraysInstanced(mode, first, count, instancecount) SOKOL_ASSERT(0 && SOKOL_GLES2_INSTANCING_ERROR) # define glDrawElementsInstanced(mode, count, type, indices, instancecount) SOKOL_ASSERT(0 && SOKOL_GLES2_INSTANCING_ERROR) # define glVertexAttribDivisor(index, divisor) SOKOL_ASSERT(0 && SOKOL_GLES2_INSTANCING_ERROR) # endif #else # define SOKOL_INSTANCING_ENABLED #endif #define _SG_GL_CHECK_ERROR() { SOKOL_ASSERT(glGetError() == GL_NO_ERROR); } #elif defined(SOKOL_D3D11) #ifndef D3D11_NO_HELPERS #define D3D11_NO_HELPERS #endif #ifndef CINTERFACE #define CINTERFACE #endif #ifndef COBJMACROS #define COBJMACROS #endif #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include #include #include #if (defined(WINAPI_FAMILY_PARTITION) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)) #pragma comment (lib, "WindowsApp.lib") #else #pragma comment (lib, "user32.lib") #pragma comment (lib, "dxgi.lib") #pragma comment (lib, "d3d11.lib") #pragma comment (lib, "dxguid.lib") #endif #elif defined(SOKOL_METAL) #if !__has_feature(objc_arc) #error "Please enable ARC when using the Metal backend" #endif #include #import #endif #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */ #pragma warning(disable:4115) /* named type definition in parentheses */ #pragma warning(disable:4505) /* unreferenced local function has been removed */ #endif /*=== PRIVATE DECLS ==========================================================*/ /* resource pool slots */ typedef struct { uint32_t id; uint32_t ctx_id; sg_resource_state state; } _sg_slot_t; /* constants */ enum { _SG_STRING_SIZE = 16, _SG_SLOT_SHIFT = 16, _SG_SLOT_MASK = (1<<_SG_SLOT_SHIFT)-1, _SG_MAX_POOL_SIZE = (1<<_SG_SLOT_SHIFT), _SG_DEFAULT_BUFFER_POOL_SIZE = 128, _SG_DEFAULT_IMAGE_POOL_SIZE = 128, _SG_DEFAULT_SHADER_POOL_SIZE = 32, _SG_DEFAULT_PIPELINE_POOL_SIZE = 64, _SG_DEFAULT_PASS_POOL_SIZE = 16, _SG_DEFAULT_CONTEXT_POOL_SIZE = 16, _SG_MTL_DEFAULT_UB_SIZE = 4 * 1024 * 1024, _SG_MTL_DEFAULT_SAMPLER_CACHE_CAPACITY = 64, }; /* fixed-size string */ typedef struct { char buf[_SG_STRING_SIZE]; } _sg_str_t; /* helper macros */ #define _sg_def(val, def) (((val) == 0) ? (def) : (val)) #define _sg_def_flt(val, def) (((val) == 0.0f) ? (def) : (val)) #define _sg_min(a,b) ((ab)?a:b) #define _sg_clamp(v,v0,v1) ((vv1)?(v1):(v))) #define _sg_fequal(val,cmp,delta) (((val-cmp)> -delta)&&((val-cmp) _sg_mtl_device; static id _sg_mtl_cmd_queue; static id _sg_mtl_cmd_buffer; static id _sg_mtl_uniform_buffers[SG_NUM_INFLIGHT_FRAMES]; static id _sg_mtl_cmd_encoder; static dispatch_semaphore_t _sg_mtl_sem; #endif /* SOKOL_METAL */ /*=== RESOURCE POOL DECLARATIONS =============================================*/ /* this *MUST* remain 0 */ #define _SG_INVALID_SLOT_INDEX (0) typedef struct { int size; int queue_top; uint32_t* gen_ctrs; int* free_queue; } _sg_pool_t; typedef struct { _sg_pool_t buffer_pool; _sg_pool_t image_pool; _sg_pool_t shader_pool; _sg_pool_t pipeline_pool; _sg_pool_t pass_pool; _sg_pool_t context_pool; _sg_buffer_t* buffers; _sg_image_t* images; _sg_shader_t* shaders; _sg_pipeline_t* pipelines; _sg_pass_t* passes; _sg_context_t* contexts; } _sg_pools_t; /*=== VALIDATION LAYER DECLARATIONS ==========================================*/ typedef enum { /* special case 'validation was successful' */ _SG_VALIDATE_SUCCESS, /* buffer creation */ _SG_VALIDATE_BUFFERDESC_CANARY, _SG_VALIDATE_BUFFERDESC_SIZE, _SG_VALIDATE_BUFFERDESC_CONTENT, _SG_VALIDATE_BUFFERDESC_NO_CONTENT, /* image creation */ _SG_VALIDATE_IMAGEDESC_CANARY, _SG_VALIDATE_IMAGEDESC_WIDTH, _SG_VALIDATE_IMAGEDESC_HEIGHT, _SG_VALIDATE_IMAGEDESC_RT_PIXELFORMAT, _SG_VALIDATE_IMAGEDESC_NONRT_PIXELFORMAT, _SG_VALIDATE_IMAGEDESC_MSAA_BUT_NO_RT, _SG_VALIDATE_IMAGEDESC_NO_MSAA_RT_SUPPORT, _SG_VALIDATE_IMAGEDESC_RT_IMMUTABLE, _SG_VALIDATE_IMAGEDESC_RT_NO_CONTENT, _SG_VALIDATE_IMAGEDESC_CONTENT, _SG_VALIDATE_IMAGEDESC_NO_CONTENT, /* shader creation */ _SG_VALIDATE_SHADERDESC_CANARY, _SG_VALIDATE_SHADERDESC_SOURCE, _SG_VALIDATE_SHADERDESC_BYTECODE, _SG_VALIDATE_SHADERDESC_SOURCE_OR_BYTECODE, _SG_VALIDATE_SHADERDESC_NO_BYTECODE_SIZE, _SG_VALIDATE_SHADERDESC_NO_CONT_UBS, _SG_VALIDATE_SHADERDESC_NO_CONT_IMGS, _SG_VALIDATE_SHADERDESC_NO_CONT_UB_MEMBERS, _SG_VALIDATE_SHADERDESC_NO_UB_MEMBERS, _SG_VALIDATE_SHADERDESC_UB_MEMBER_NAME, _SG_VALIDATE_SHADERDESC_UB_SIZE_MISMATCH, _SG_VALIDATE_SHADERDESC_IMG_NAME, _SG_VALIDATE_SHADERDESC_ATTR_NAMES, _SG_VALIDATE_SHADERDESC_ATTR_SEMANTICS, _SG_VALIDATE_SHADERDESC_ATTR_STRING_TOO_LONG, /* pipeline creation */ _SG_VALIDATE_PIPELINEDESC_CANARY, _SG_VALIDATE_PIPELINEDESC_SHADER, _SG_VALIDATE_PIPELINEDESC_NO_ATTRS, _SG_VALIDATE_PIPELINEDESC_LAYOUT_STRIDE4, _SG_VALIDATE_PIPELINEDESC_ATTR_NAME, _SG_VALIDATE_PIPELINEDESC_ATTR_SEMANTICS, /* pass creation */ _SG_VALIDATE_PASSDESC_CANARY, _SG_VALIDATE_PASSDESC_NO_COLOR_ATTS, _SG_VALIDATE_PASSDESC_NO_CONT_COLOR_ATTS, _SG_VALIDATE_PASSDESC_IMAGE, _SG_VALIDATE_PASSDESC_MIPLEVEL, _SG_VALIDATE_PASSDESC_FACE, _SG_VALIDATE_PASSDESC_LAYER, _SG_VALIDATE_PASSDESC_SLICE, _SG_VALIDATE_PASSDESC_IMAGE_NO_RT, _SG_VALIDATE_PASSDESC_COLOR_PIXELFORMATS, _SG_VALIDATE_PASSDESC_COLOR_INV_PIXELFORMAT, _SG_VALIDATE_PASSDESC_DEPTH_INV_PIXELFORMAT, _SG_VALIDATE_PASSDESC_IMAGE_SIZES, _SG_VALIDATE_PASSDESC_IMAGE_SAMPLE_COUNTS, /* sg_begin_pass validation */ _SG_VALIDATE_BEGINPASS_PASS, _SG_VALIDATE_BEGINPASS_IMAGE, /* sg_apply_pipeline validation */ _SG_VALIDATE_APIP_PIPELINE_VALID_ID, _SG_VALIDATE_APIP_PIPELINE_EXISTS, _SG_VALIDATE_APIP_PIPELINE_VALID, _SG_VALIDATE_APIP_SHADER_EXISTS, _SG_VALIDATE_APIP_SHADER_VALID, _SG_VALIDATE_APIP_ATT_COUNT, _SG_VALIDATE_APIP_COLOR_FORMAT, _SG_VALIDATE_APIP_DEPTH_FORMAT, _SG_VALIDATE_APIP_SAMPLE_COUNT, /* sg_apply_bindings validation */ _SG_VALIDATE_ABND_PIPELINE, _SG_VALIDATE_ABND_PIPELINE_EXISTS, _SG_VALIDATE_ABND_PIPELINE_VALID, _SG_VALIDATE_ABND_VBS, _SG_VALIDATE_ABND_VB_EXISTS, _SG_VALIDATE_ABND_VB_TYPE, _SG_VALIDATE_ABND_VB_OVERFLOW, _SG_VALIDATE_ABND_NO_IB, _SG_VALIDATE_ABND_IB, _SG_VALIDATE_ABND_IB_EXISTS, _SG_VALIDATE_ABND_IB_TYPE, _SG_VALIDATE_ABND_IB_OVERFLOW, _SG_VALIDATE_ABND_VS_IMGS, _SG_VALIDATE_ABND_VS_IMG_EXISTS, _SG_VALIDATE_ABND_VS_IMG_TYPES, _SG_VALIDATE_ABND_FS_IMGS, _SG_VALIDATE_ABND_FS_IMG_EXISTS, _SG_VALIDATE_ABND_FS_IMG_TYPES, /* sg_apply_uniforms validation */ _SG_VALIDATE_AUB_NO_PIPELINE, _SG_VALIDATE_AUB_NO_UB_AT_SLOT, _SG_VALIDATE_AUB_SIZE, /* sg_update_buffer validation */ _SG_VALIDATE_UPDATEBUF_USAGE, _SG_VALIDATE_UPDATEBUF_SIZE, _SG_VALIDATE_UPDATEBUF_ONCE, _SG_VALIDATE_UPDATEBUF_APPEND, /* sg_append_buffer validation */ _SG_VALIDATE_APPENDBUF_USAGE, _SG_VALIDATE_APPENDBUF_SIZE, _SG_VALIDATE_APPENDBUF_UPDATE, /* sg_update_image validation */ _SG_VALIDATE_UPDIMG_USAGE, _SG_VALIDATE_UPDIMG_NOTENOUGHDATA, _SG_VALIDATE_UPDIMG_SIZE, _SG_VALIDATE_UPDIMG_COMPRESSED, _SG_VALIDATE_UPDIMG_ONCE } _sg_validate_error_t; /*=== GENERIC BACKEND STATE ==================================================*/ typedef struct { bool valid; sg_desc desc; /* original desc with default values patched in */ uint32_t frame_index; sg_context active_context; sg_pass cur_pass; sg_pipeline cur_pipeline; bool pass_valid; bool bindings_valid; bool next_draw_valid; #if defined(SOKOL_DEBUG) _sg_validate_error_t validate_error; #endif _sg_pools_t pools; #if defined(SOKOL_GLCORE33) || defined(SOKOL_GLES2) || defined(SOKOL_GLES3) _sg_gl_backend_t gl; #elif defined(SOKOL_METAL) _sg_mtl_backend_t mtl; #elif defined(SOKOL_D3D11) _sg_d3d11_backend_t d3d11; #endif #if defined(SOKOL_TRACE_HOOKS) sg_trace_hooks hooks; #endif } _sg_state_t; static _sg_state_t _sg; /*-- helper functions --------------------------------------------------------*/ _SOKOL_PRIVATE bool _sg_strempty(const _sg_str_t* str) { return 0 == str->buf[0]; } _SOKOL_PRIVATE const char* _sg_strptr(const _sg_str_t* str) { return &str->buf[0]; } _SOKOL_PRIVATE void _sg_strcpy(_sg_str_t* dst, const char* src) { SOKOL_ASSERT(dst); if (src) { #if defined(_MSC_VER) strncpy_s(dst->buf, _SG_STRING_SIZE, src, (_SG_STRING_SIZE-1)); #else strncpy(dst->buf, src, _SG_STRING_SIZE); #endif dst->buf[_SG_STRING_SIZE-1] = 0; } else { memset(dst->buf, 0, _SG_STRING_SIZE); } } /* return byte size of a vertex format */ _SOKOL_PRIVATE int _sg_vertexformat_bytesize(sg_vertex_format fmt) { switch (fmt) { case SG_VERTEXFORMAT_FLOAT: return 4; case SG_VERTEXFORMAT_FLOAT2: return 8; case SG_VERTEXFORMAT_FLOAT3: return 12; case SG_VERTEXFORMAT_FLOAT4: return 16; case SG_VERTEXFORMAT_BYTE4: return 4; case SG_VERTEXFORMAT_BYTE4N: return 4; case SG_VERTEXFORMAT_UBYTE4: return 4; case SG_VERTEXFORMAT_UBYTE4N: return 4; case SG_VERTEXFORMAT_SHORT2: return 4; case SG_VERTEXFORMAT_SHORT2N: return 4; case SG_VERTEXFORMAT_SHORT4: return 8; case SG_VERTEXFORMAT_SHORT4N: return 8; case SG_VERTEXFORMAT_UINT10_N2: return 4; case SG_VERTEXFORMAT_INVALID: return 0; default: SOKOL_UNREACHABLE; return -1; } } /* return the byte size of a shader uniform */ _SOKOL_PRIVATE int _sg_uniform_size(sg_uniform_type type, int count) { switch (type) { case SG_UNIFORMTYPE_INVALID: return 0; case SG_UNIFORMTYPE_FLOAT: return 4 * count; case SG_UNIFORMTYPE_FLOAT2: return 8 * count; case SG_UNIFORMTYPE_FLOAT3: return 12 * count; /* FIXME: std140??? */ case SG_UNIFORMTYPE_FLOAT4: return 16 * count; case SG_UNIFORMTYPE_MAT4: return 64 * count; default: SOKOL_UNREACHABLE; return -1; } } /* return true if pixel format is a compressed format */ _SOKOL_PRIVATE bool _sg_is_compressed_pixel_format(sg_pixel_format fmt) { switch (fmt) { case SG_PIXELFORMAT_DXT1: case SG_PIXELFORMAT_DXT3: case SG_PIXELFORMAT_DXT5: case SG_PIXELFORMAT_PVRTC2_RGB: case SG_PIXELFORMAT_PVRTC4_RGB: case SG_PIXELFORMAT_PVRTC2_RGBA: case SG_PIXELFORMAT_PVRTC4_RGBA: case SG_PIXELFORMAT_ETC2_RGB8: case SG_PIXELFORMAT_ETC2_SRGB8: return true; default: return false; } } /* return true if pixel format is a valid render target format */ _SOKOL_PRIVATE bool _sg_is_valid_rendertarget_color_format(sg_pixel_format fmt) { switch (fmt) { case SG_PIXELFORMAT_RGBA8: case SG_PIXELFORMAT_R10G10B10A2: case SG_PIXELFORMAT_RGBA32F: case SG_PIXELFORMAT_RGBA16F: return true; default: return false; } } /* return true if pixel format is a valid depth format */ _SOKOL_PRIVATE bool _sg_is_valid_rendertarget_depth_format(sg_pixel_format fmt) { switch (fmt) { case SG_PIXELFORMAT_DEPTH: case SG_PIXELFORMAT_DEPTHSTENCIL: return true; default: return false; } } /* return true if pixel format is a depth-stencil format */ _SOKOL_PRIVATE bool _sg_is_depth_stencil_format(sg_pixel_format fmt) { /* FIXME: more depth stencil formats? */ return (SG_PIXELFORMAT_DEPTHSTENCIL == fmt); } /* return the bytes-per-pixel for a pixel format */ _SOKOL_PRIVATE int _sg_pixelformat_bytesize(sg_pixel_format fmt) { switch (fmt) { case SG_PIXELFORMAT_RGBA32F: return 16; case SG_PIXELFORMAT_RGBA16F: return 8; case SG_PIXELFORMAT_RGBA8: case SG_PIXELFORMAT_R10G10B10A2: case SG_PIXELFORMAT_R32F: return 4; case SG_PIXELFORMAT_RGB8: return 3; case SG_PIXELFORMAT_R5G5B5A1: case SG_PIXELFORMAT_R5G6B5: case SG_PIXELFORMAT_RGBA4: case SG_PIXELFORMAT_R16F: return 2; case SG_PIXELFORMAT_L8: return 1; default: SOKOL_UNREACHABLE; return 0; } } /* return row pitch for an image */ _SOKOL_PRIVATE int _sg_row_pitch(sg_pixel_format fmt, int width) { int pitch; switch (fmt) { case SG_PIXELFORMAT_DXT1: case SG_PIXELFORMAT_ETC2_RGB8: case SG_PIXELFORMAT_ETC2_SRGB8: pitch = ((width + 3) / 4) * 8; pitch = pitch < 8 ? 8 : pitch; break; case SG_PIXELFORMAT_DXT3: case SG_PIXELFORMAT_DXT5: pitch = ((width + 3) / 4) * 16; pitch = pitch < 16 ? 16 : pitch; break; case SG_PIXELFORMAT_PVRTC4_RGB: case SG_PIXELFORMAT_PVRTC4_RGBA: { const int block_size = 4*4; const int bpp = 4; int width_blocks = width / 4; width_blocks = width_blocks < 2 ? 2 : width_blocks; pitch = width_blocks * ((block_size * bpp) / 8); } break; case SG_PIXELFORMAT_PVRTC2_RGB: case SG_PIXELFORMAT_PVRTC2_RGBA: { const int block_size = 8*4; const int bpp = 2; int width_blocks = width / 4; width_blocks = width_blocks < 2 ? 2 : width_blocks; pitch = width_blocks * ((block_size * bpp) / 8); } break; default: pitch = width * _sg_pixelformat_bytesize(fmt); break; } return pitch; } /* return pitch of a 2D subimage / texture slice */ _SOKOL_PRIVATE int _sg_surface_pitch(sg_pixel_format fmt, int width, int height) { int num_rows = 0; switch (fmt) { case SG_PIXELFORMAT_DXT1: case SG_PIXELFORMAT_DXT3: case SG_PIXELFORMAT_DXT5: case SG_PIXELFORMAT_ETC2_RGB8: case SG_PIXELFORMAT_ETC2_SRGB8: case SG_PIXELFORMAT_PVRTC2_RGB: case SG_PIXELFORMAT_PVRTC2_RGBA: case SG_PIXELFORMAT_PVRTC4_RGB: case SG_PIXELFORMAT_PVRTC4_RGBA: num_rows = ((height + 3) / 4); break; default: num_rows = height; break; } if (num_rows < 1) { num_rows = 1; } return num_rows * _sg_row_pitch(fmt, width); } /* resolve pass action defaults into a new pass action struct */ _SOKOL_PRIVATE void _sg_resolve_default_pass_action(const sg_pass_action* from, sg_pass_action* to) { SOKOL_ASSERT(from && to); *to = *from; for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { if (to->colors[i].action == _SG_ACTION_DEFAULT) { to->colors[i].action = SG_ACTION_CLEAR; to->colors[i].val[0] = SG_DEFAULT_CLEAR_RED; to->colors[i].val[1] = SG_DEFAULT_CLEAR_GREEN; to->colors[i].val[2] = SG_DEFAULT_CLEAR_BLUE; to->colors[i].val[3] = SG_DEFAULT_CLEAR_ALPHA; } } if (to->depth.action == _SG_ACTION_DEFAULT) { to->depth.action = SG_ACTION_CLEAR; to->depth.val = SG_DEFAULT_CLEAR_DEPTH; } if (to->stencil.action == _SG_ACTION_DEFAULT) { to->stencil.action = SG_ACTION_CLEAR; to->stencil.val = SG_DEFAULT_CLEAR_STENCIL; } } /*== DUMMY BACKEND IMPL ======================================================*/ #if defined(SOKOL_DUMMY_BACKEND) _SOKOL_PRIVATE void _sg_setup_backend(const sg_desc* desc) { SOKOL_ASSERT(desc); _SOKOL_UNUSED(desc); } _SOKOL_PRIVATE void _sg_discard_backend(void) { /* empty */ } _SOKOL_PRIVATE bool _sg_query_feature(sg_feature f) { _SOKOL_UNUSED(f); return true; } _SOKOL_PRIVATE void _sg_reset_state_cache(void) { /* empty*/ } _SOKOL_PRIVATE sg_resource_state _sg_create_context(_sg_context_t* ctx) { SOKOL_ASSERT(ctx); _SOKOL_UNUSED(ctx); return SG_RESOURCESTATE_VALID; } _SOKOL_PRIVATE void _sg_destroy_context(_sg_context_t* ctx) { SOKOL_ASSERT(ctx); _SOKOL_UNUSED(ctx); } _SOKOL_PRIVATE void _sg_activate_context(_sg_context_t* ctx) { SOKOL_ASSERT(ctx); _SOKOL_UNUSED(ctx); } _SOKOL_PRIVATE sg_resource_state _sg_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { SOKOL_ASSERT(buf && desc); buf->size = desc->size; buf->append_pos = 0; buf->append_overflow = false; buf->type = desc->type; buf->usage = desc->usage; buf->update_frame_index = 0; buf->append_frame_index = 0; buf->num_slots = (buf->usage == SG_USAGE_IMMUTABLE) ? 1 : SG_NUM_INFLIGHT_FRAMES; buf->active_slot = 0; return SG_RESOURCESTATE_VALID; } _SOKOL_PRIVATE void _sg_destroy_buffer(_sg_buffer_t* buf) { SOKOL_ASSERT(buf); _SOKOL_UNUSED(buf); } _SOKOL_PRIVATE sg_resource_state _sg_create_image(_sg_image_t* img, const sg_image_desc* desc) { SOKOL_ASSERT(img && desc); img->type = desc->type; img->render_target = desc->render_target; img->width = desc->width; img->height = desc->height; img->depth = desc->depth; img->num_mipmaps = desc->num_mipmaps; img->usage = desc->usage; img->pixel_format = desc->pixel_format; img->sample_count = desc->sample_count; img->min_filter = desc->min_filter; img->mag_filter = desc->mag_filter; img->wrap_u = desc->wrap_u; img->wrap_v = desc->wrap_v; img->wrap_w = desc->wrap_w; img->max_anisotropy = desc->max_anisotropy; img->upd_frame_index = 0; img->num_slots = (img->usage == SG_USAGE_IMMUTABLE) ? 1 :SG_NUM_INFLIGHT_FRAMES; img->active_slot = 0; return SG_RESOURCESTATE_VALID; } _SOKOL_PRIVATE void _sg_destroy_image(_sg_image_t* img) { SOKOL_ASSERT(img); _SOKOL_UNUSED(img); } _SOKOL_PRIVATE sg_resource_state _sg_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { SOKOL_ASSERT(shd && desc); /* uniform block sizes and image types */ for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { const sg_shader_stage_desc* stage_desc = (stage_index == SG_SHADERSTAGE_VS) ? &desc->vs : &desc->fs; _sg_shader_stage_t* stage = &shd->stage[stage_index]; SOKOL_ASSERT(stage->num_uniform_blocks == 0); for (int ub_index = 0; ub_index < SG_MAX_SHADERSTAGE_UBS; ub_index++) { const sg_shader_uniform_block_desc* ub_desc = &stage_desc->uniform_blocks[ub_index]; if (0 == ub_desc->size) { break; } _sg_uniform_block_t* ub = &stage->uniform_blocks[ub_index]; ub->size = ub_desc->size; stage->num_uniform_blocks++; } SOKOL_ASSERT(stage->num_images == 0); for (int img_index = 0; img_index < SG_MAX_SHADERSTAGE_IMAGES; img_index++) { const sg_shader_image_desc* img_desc = &stage_desc->images[img_index]; if (img_desc->type == _SG_IMAGETYPE_DEFAULT) { break; } stage->images[img_index].type = img_desc->type; stage->num_images++; } } return SG_RESOURCESTATE_VALID; } _SOKOL_PRIVATE void _sg_destroy_shader(_sg_shader_t* shd) { SOKOL_ASSERT(shd); _SOKOL_UNUSED(shd); } _SOKOL_PRIVATE sg_resource_state _sg_create_pipeline(_sg_pipeline_t* pip, _sg_shader_t* shd, const sg_pipeline_desc* desc) { SOKOL_ASSERT(pip && desc); pip->shader = shd; pip->shader_id = desc->shader; for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { const sg_vertex_attr_desc* a_desc = &desc->layout.attrs[attr_index]; if (a_desc->format == SG_VERTEXFORMAT_INVALID) { break; } SOKOL_ASSERT((a_desc->buffer_index >= 0) && (a_desc->buffer_index < SG_MAX_SHADERSTAGE_BUFFERS)); pip->vertex_layout_valid[a_desc->buffer_index] = true; } pip->color_attachment_count = desc->blend.color_attachment_count; pip->color_format = desc->blend.color_format; pip->depth_format = desc->blend.depth_format; pip->sample_count = desc->rasterizer.sample_count; pip->depth_bias = desc->rasterizer.depth_bias; pip->depth_bias_slope_scale = desc->rasterizer.depth_bias_slope_scale; pip->depth_bias_clamp = desc->rasterizer.depth_bias_clamp; pip->index_type = desc->index_type; for (int i = 0; i < 4; i++) { pip->blend_color[i] = desc->blend.blend_color[i]; } return SG_RESOURCESTATE_VALID; } _SOKOL_PRIVATE void _sg_destroy_pipeline(_sg_pipeline_t* pip) { SOKOL_ASSERT(pip); _SOKOL_UNUSED(pip); } _SOKOL_PRIVATE sg_resource_state _sg_create_pass(_sg_pass_t* pass, _sg_image_t** att_images, const sg_pass_desc* desc) { SOKOL_ASSERT(pass && desc); SOKOL_ASSERT(att_images && att_images[0]); /* copy image pointers and desc attributes */ const sg_attachment_desc* att_desc; _sg_attachment_t* att; for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { SOKOL_ASSERT(0 == pass->color_atts[i].image); att_desc = &desc->color_attachments[i]; if (att_desc->image.id != SG_INVALID_ID) { pass->num_color_atts++; SOKOL_ASSERT(att_images[i] && (att_images[i]->slot.id == att_desc->image.id)); SOKOL_ASSERT(_sg_is_valid_rendertarget_color_format(att_images[i]->pixel_format)); att = &pass->color_atts[i]; SOKOL_ASSERT((att->image == 0) && (att->image_id.id == SG_INVALID_ID)); att->image = att_images[i]; att->image_id = att_desc->image; att->mip_level = att_desc->mip_level; att->slice = att_desc->slice; } } SOKOL_ASSERT(0 == pass->ds_att.image); att_desc = &desc->depth_stencil_attachment; const int ds_img_index = SG_MAX_COLOR_ATTACHMENTS; if (att_desc->image.id != SG_INVALID_ID) { SOKOL_ASSERT(att_images[ds_img_index] && (att_images[ds_img_index]->slot.id == att_desc->image.id)); SOKOL_ASSERT(_sg_is_valid_rendertarget_depth_format(att_images[ds_img_index]->pixel_format)); att = &pass->ds_att; SOKOL_ASSERT((att->image == 0) && (att->image_id.id == SG_INVALID_ID)); att->image = att_images[ds_img_index]; att->image_id = att_desc->image; att->mip_level = att_desc->mip_level; att->slice = att_desc->slice; } return SG_RESOURCESTATE_VALID; } _SOKOL_PRIVATE void _sg_destroy_pass(_sg_pass_t* pass) { SOKOL_ASSERT(pass); _SOKOL_UNUSED(pass); } _SOKOL_PRIVATE void _sg_begin_pass(_sg_pass_t* pass, const sg_pass_action* action, int w, int h) { SOKOL_ASSERT(action); _SOKOL_UNUSED(pass); _SOKOL_UNUSED(action); _SOKOL_UNUSED(w); _SOKOL_UNUSED(h); } _SOKOL_PRIVATE void _sg_end_pass(void) { /* empty */ } _SOKOL_PRIVATE void _sg_commit(void) { /* empty */ } _SOKOL_PRIVATE void _sg_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { _SOKOL_UNUSED(x); _SOKOL_UNUSED(y); _SOKOL_UNUSED(w); _SOKOL_UNUSED(h); _SOKOL_UNUSED(origin_top_left); } _SOKOL_PRIVATE void _sg_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) { _SOKOL_UNUSED(x); _SOKOL_UNUSED(y); _SOKOL_UNUSED(w); _SOKOL_UNUSED(h); _SOKOL_UNUSED(origin_top_left); } _SOKOL_PRIVATE void _sg_apply_pipeline(_sg_pipeline_t* pip) { SOKOL_ASSERT(pip); _SOKOL_UNUSED(pip); } _SOKOL_PRIVATE void _sg_apply_bindings( _sg_pipeline_t* pip, _sg_buffer_t** vbs, const int* vb_offsets, int num_vbs, _sg_buffer_t* ib, int ib_offset, _sg_image_t** vs_imgs, int num_vs_imgs, _sg_image_t** fs_imgs, int num_fs_imgs) { SOKOL_ASSERT(pip); SOKOL_ASSERT(vbs && vb_offsets); SOKOL_ASSERT(vs_imgs); SOKOL_ASSERT(fs_imgs); _SOKOL_UNUSED(pip); _SOKOL_UNUSED(vbs); _SOKOL_UNUSED(vb_offsets); _SOKOL_UNUSED(num_vbs); _SOKOL_UNUSED(ib); _SOKOL_UNUSED(ib_offset); _SOKOL_UNUSED(vs_imgs); _SOKOL_UNUSED(num_vs_imgs); _SOKOL_UNUSED(fs_imgs); _SOKOL_UNUSED(num_fs_imgs); } _SOKOL_PRIVATE void _sg_apply_uniforms(sg_shader_stage stage_index, int ub_index, const void* data, int num_bytes) { SOKOL_ASSERT(data && (num_bytes > 0)); SOKOL_ASSERT((stage_index >= 0) && ((int)stage_index < SG_NUM_SHADER_STAGES)); SOKOL_ASSERT((ub_index >= 0) && (ub_index < SG_MAX_SHADERSTAGE_UBS)); _SOKOL_UNUSED(stage_index); _SOKOL_UNUSED(ub_index); _SOKOL_UNUSED(data); _SOKOL_UNUSED(num_bytes); } _SOKOL_PRIVATE void _sg_draw(int base_element, int num_elements, int num_instances) { _SOKOL_UNUSED(base_element); _SOKOL_UNUSED(num_elements); _SOKOL_UNUSED(num_instances); } _SOKOL_PRIVATE void _sg_update_buffer(_sg_buffer_t* buf, const void* data, int data_size) { SOKOL_ASSERT(buf && data && (data_size > 0)); _SOKOL_UNUSED(data); _SOKOL_UNUSED(data_size); if (++buf->active_slot >= buf->num_slots) { buf->active_slot = 0; } } _SOKOL_PRIVATE void _sg_append_buffer(_sg_buffer_t* buf, const void* data, int data_size, bool new_frame) { SOKOL_ASSERT(buf && data && (data_size > 0)); _SOKOL_UNUSED(data); _SOKOL_UNUSED(data_size); if (new_frame) { if (++buf->active_slot >= buf->num_slots) { buf->active_slot = 0; } } } _SOKOL_PRIVATE void _sg_update_image(_sg_image_t* img, const sg_image_content* data) { SOKOL_ASSERT(img && data); _SOKOL_UNUSED(data); if (++img->active_slot >= img->num_slots) { img->active_slot = 0; } } /*== GL BACKEND ==============================================================*/ #elif defined(SOKOL_GLCORE33) || defined(SOKOL_GLES2) || defined(SOKOL_GLES3) /*-- type translation --------------------------------------------------------*/ _SOKOL_PRIVATE GLenum _sg_gl_buffer_target(sg_buffer_type t) { switch (t) { case SG_BUFFERTYPE_VERTEXBUFFER: return GL_ARRAY_BUFFER; case SG_BUFFERTYPE_INDEXBUFFER: return GL_ELEMENT_ARRAY_BUFFER; default: SOKOL_UNREACHABLE; return 0; } } _SOKOL_PRIVATE GLenum _sg_gl_texture_target(sg_image_type t) { switch (t) { case SG_IMAGETYPE_2D: return GL_TEXTURE_2D; case SG_IMAGETYPE_CUBE: return GL_TEXTURE_CUBE_MAP; #if !defined(SOKOL_GLES2) case SG_IMAGETYPE_3D: return GL_TEXTURE_3D; case SG_IMAGETYPE_ARRAY: return GL_TEXTURE_2D_ARRAY; #endif default: SOKOL_UNREACHABLE; return 0; } } _SOKOL_PRIVATE GLenum _sg_gl_usage(sg_usage u) { switch (u) { case SG_USAGE_IMMUTABLE: return GL_STATIC_DRAW; case SG_USAGE_DYNAMIC: return GL_DYNAMIC_DRAW; case SG_USAGE_STREAM: return GL_STREAM_DRAW; default: SOKOL_UNREACHABLE; return 0; } } _SOKOL_PRIVATE GLenum _sg_gl_shader_stage(sg_shader_stage stage) { switch (stage) { case SG_SHADERSTAGE_VS: return GL_VERTEX_SHADER; case SG_SHADERSTAGE_FS: return GL_FRAGMENT_SHADER; default: SOKOL_UNREACHABLE; return 0; } } _SOKOL_PRIVATE GLint _sg_gl_vertexformat_size(sg_vertex_format fmt) { switch (fmt) { case SG_VERTEXFORMAT_FLOAT: return 1; case SG_VERTEXFORMAT_FLOAT2: return 2; case SG_VERTEXFORMAT_FLOAT3: return 3; case SG_VERTEXFORMAT_FLOAT4: return 4; case SG_VERTEXFORMAT_BYTE4: return 4; case SG_VERTEXFORMAT_BYTE4N: return 4; case SG_VERTEXFORMAT_UBYTE4: return 4; case SG_VERTEXFORMAT_UBYTE4N: return 4; case SG_VERTEXFORMAT_SHORT2: return 2; case SG_VERTEXFORMAT_SHORT2N: return 2; case SG_VERTEXFORMAT_SHORT4: return 4; case SG_VERTEXFORMAT_SHORT4N: return 4; case SG_VERTEXFORMAT_UINT10_N2: return 4; default: SOKOL_UNREACHABLE; return 0; } } _SOKOL_PRIVATE GLenum _sg_gl_vertexformat_type(sg_vertex_format fmt) { switch (fmt) { case SG_VERTEXFORMAT_FLOAT: case SG_VERTEXFORMAT_FLOAT2: case SG_VERTEXFORMAT_FLOAT3: case SG_VERTEXFORMAT_FLOAT4: return GL_FLOAT; case SG_VERTEXFORMAT_BYTE4: case SG_VERTEXFORMAT_BYTE4N: return GL_BYTE; case SG_VERTEXFORMAT_UBYTE4: case SG_VERTEXFORMAT_UBYTE4N: return GL_UNSIGNED_BYTE; case SG_VERTEXFORMAT_SHORT2: case SG_VERTEXFORMAT_SHORT2N: case SG_VERTEXFORMAT_SHORT4: case SG_VERTEXFORMAT_SHORT4N: return GL_SHORT; case SG_VERTEXFORMAT_UINT10_N2: return GL_UNSIGNED_INT_2_10_10_10_REV; default: SOKOL_UNREACHABLE; return 0; } } _SOKOL_PRIVATE GLboolean _sg_gl_vertexformat_normalized(sg_vertex_format fmt) { switch (fmt) { case SG_VERTEXFORMAT_BYTE4N: case SG_VERTEXFORMAT_UBYTE4N: case SG_VERTEXFORMAT_SHORT2N: case SG_VERTEXFORMAT_SHORT4N: case SG_VERTEXFORMAT_UINT10_N2: return GL_TRUE; default: return GL_FALSE; } } _SOKOL_PRIVATE GLenum _sg_gl_primitive_type(sg_primitive_type t) { switch (t) { case SG_PRIMITIVETYPE_POINTS: return GL_POINTS; case SG_PRIMITIVETYPE_LINES: return GL_LINES; case SG_PRIMITIVETYPE_LINE_STRIP: return GL_LINE_STRIP; case SG_PRIMITIVETYPE_TRIANGLES: return GL_TRIANGLES; case SG_PRIMITIVETYPE_TRIANGLE_STRIP: return GL_TRIANGLE_STRIP; default: SOKOL_UNREACHABLE; return 0; } } _SOKOL_PRIVATE GLenum _sg_gl_index_type(sg_index_type t) { switch (t) { case SG_INDEXTYPE_NONE: return 0; case SG_INDEXTYPE_UINT16: return GL_UNSIGNED_SHORT; case SG_INDEXTYPE_UINT32: return GL_UNSIGNED_INT; default: SOKOL_UNREACHABLE; return 0; } } _SOKOL_PRIVATE GLenum _sg_gl_compare_func(sg_compare_func cmp) { switch (cmp) { case SG_COMPAREFUNC_NEVER: return GL_NEVER; case SG_COMPAREFUNC_LESS: return GL_LESS; case SG_COMPAREFUNC_EQUAL: return GL_EQUAL; case SG_COMPAREFUNC_LESS_EQUAL: return GL_LEQUAL; case SG_COMPAREFUNC_GREATER: return GL_GREATER; case SG_COMPAREFUNC_NOT_EQUAL: return GL_NOTEQUAL; case SG_COMPAREFUNC_GREATER_EQUAL: return GL_GEQUAL; case SG_COMPAREFUNC_ALWAYS: return GL_ALWAYS; default: SOKOL_UNREACHABLE; return 0; } } _SOKOL_PRIVATE GLenum _sg_gl_stencil_op(sg_stencil_op op) { switch (op) { case SG_STENCILOP_KEEP: return GL_KEEP; case SG_STENCILOP_ZERO: return GL_ZERO; case SG_STENCILOP_REPLACE: return GL_REPLACE; case SG_STENCILOP_INCR_CLAMP: return GL_INCR; case SG_STENCILOP_DECR_CLAMP: return GL_DECR; case SG_STENCILOP_INVERT: return GL_INVERT; case SG_STENCILOP_INCR_WRAP: return GL_INCR_WRAP; case SG_STENCILOP_DECR_WRAP: return GL_DECR_WRAP; default: SOKOL_UNREACHABLE; return 0; } } _SOKOL_PRIVATE GLenum _sg_gl_blend_factor(sg_blend_factor f) { switch (f) { case SG_BLENDFACTOR_ZERO: return GL_ZERO; case SG_BLENDFACTOR_ONE: return GL_ONE; case SG_BLENDFACTOR_SRC_COLOR: return GL_SRC_COLOR; case SG_BLENDFACTOR_ONE_MINUS_SRC_COLOR: return GL_ONE_MINUS_SRC_COLOR; case SG_BLENDFACTOR_SRC_ALPHA: return GL_SRC_ALPHA; case SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA: return GL_ONE_MINUS_SRC_ALPHA; case SG_BLENDFACTOR_DST_COLOR: return GL_DST_COLOR; case SG_BLENDFACTOR_ONE_MINUS_DST_COLOR: return GL_ONE_MINUS_DST_COLOR; case SG_BLENDFACTOR_DST_ALPHA: return GL_DST_ALPHA; case SG_BLENDFACTOR_ONE_MINUS_DST_ALPHA: return GL_ONE_MINUS_DST_ALPHA; case SG_BLENDFACTOR_SRC_ALPHA_SATURATED: return GL_SRC_ALPHA_SATURATE; case SG_BLENDFACTOR_BLEND_COLOR: return GL_CONSTANT_COLOR; case SG_BLENDFACTOR_ONE_MINUS_BLEND_COLOR: return GL_ONE_MINUS_CONSTANT_COLOR; case SG_BLENDFACTOR_BLEND_ALPHA: return GL_CONSTANT_ALPHA; case SG_BLENDFACTOR_ONE_MINUS_BLEND_ALPHA: return GL_ONE_MINUS_CONSTANT_ALPHA; default: SOKOL_UNREACHABLE; return 0; } } _SOKOL_PRIVATE GLenum _sg_gl_blend_op(sg_blend_op op) { switch (op) { case SG_BLENDOP_ADD: return GL_FUNC_ADD; case SG_BLENDOP_SUBTRACT: return GL_FUNC_SUBTRACT; case SG_BLENDOP_REVERSE_SUBTRACT: return GL_FUNC_REVERSE_SUBTRACT; default: SOKOL_UNREACHABLE; return 0; } } _SOKOL_PRIVATE GLenum _sg_gl_filter(sg_filter f) { switch (f) { case SG_FILTER_NEAREST: return GL_NEAREST; case SG_FILTER_LINEAR: return GL_LINEAR; case SG_FILTER_NEAREST_MIPMAP_NEAREST: return GL_NEAREST_MIPMAP_NEAREST; case SG_FILTER_NEAREST_MIPMAP_LINEAR: return GL_NEAREST_MIPMAP_LINEAR; case SG_FILTER_LINEAR_MIPMAP_NEAREST: return GL_LINEAR_MIPMAP_NEAREST; case SG_FILTER_LINEAR_MIPMAP_LINEAR: return GL_LINEAR_MIPMAP_LINEAR; default: SOKOL_UNREACHABLE; return 0; } } _SOKOL_PRIVATE GLenum _sg_gl_wrap(sg_wrap w) { switch (w) { case SG_WRAP_CLAMP_TO_EDGE: return GL_CLAMP_TO_EDGE; case SG_WRAP_REPEAT: return GL_REPEAT; case SG_WRAP_MIRRORED_REPEAT: return GL_MIRRORED_REPEAT; default: SOKOL_UNREACHABLE; return 0; } } _SOKOL_PRIVATE GLenum _sg_gl_teximage_type(sg_pixel_format fmt) { switch (fmt) { case SG_PIXELFORMAT_RGBA32F: case SG_PIXELFORMAT_R32F: return GL_FLOAT; case SG_PIXELFORMAT_RGBA16F: case SG_PIXELFORMAT_R16F: return GL_HALF_FLOAT; case SG_PIXELFORMAT_RGBA8: case SG_PIXELFORMAT_RGB8: case SG_PIXELFORMAT_L8: return GL_UNSIGNED_BYTE; case SG_PIXELFORMAT_R10G10B10A2: return GL_UNSIGNED_INT_2_10_10_10_REV; case SG_PIXELFORMAT_R5G5B5A1: return GL_UNSIGNED_SHORT_5_5_5_1; case SG_PIXELFORMAT_R5G6B5: return GL_UNSIGNED_SHORT_5_6_5; case SG_PIXELFORMAT_RGBA4: return GL_UNSIGNED_SHORT_4_4_4_4; case SG_PIXELFORMAT_DEPTH: /* FIXME */ return GL_UNSIGNED_SHORT; case SG_PIXELFORMAT_DEPTHSTENCIL: /* FIXME */ return GL_UNSIGNED_INT_24_8; default: SOKOL_UNREACHABLE; return 0; } } _SOKOL_PRIVATE GLenum _sg_gl_teximage_format(sg_pixel_format fmt) { switch (fmt) { case SG_PIXELFORMAT_NONE: return 0; case SG_PIXELFORMAT_RGBA8: case SG_PIXELFORMAT_R5G5B5A1: case SG_PIXELFORMAT_RGBA4: case SG_PIXELFORMAT_RGBA32F: case SG_PIXELFORMAT_RGBA16F: case SG_PIXELFORMAT_R10G10B10A2: return GL_RGBA; case SG_PIXELFORMAT_RGB8: case SG_PIXELFORMAT_R5G6B5: return GL_RGB; case SG_PIXELFORMAT_L8: case SG_PIXELFORMAT_R32F: case SG_PIXELFORMAT_R16F: #if defined(SOKOL_GLES2) return GL_LUMINANCE; #else if (_sg.gl.gles2) { return GL_LUMINANCE; } else { return GL_RED; } #endif case SG_PIXELFORMAT_DEPTH: return GL_DEPTH_COMPONENT; case SG_PIXELFORMAT_DEPTHSTENCIL: return GL_DEPTH_STENCIL; case SG_PIXELFORMAT_DXT1: return GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; case SG_PIXELFORMAT_DXT3: return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; case SG_PIXELFORMAT_DXT5: return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; case SG_PIXELFORMAT_PVRTC2_RGB: return GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG; case SG_PIXELFORMAT_PVRTC4_RGB: return GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG; case SG_PIXELFORMAT_PVRTC2_RGBA: return GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; case SG_PIXELFORMAT_PVRTC4_RGBA: return GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; case SG_PIXELFORMAT_ETC2_RGB8: return GL_COMPRESSED_RGB8_ETC2; case SG_PIXELFORMAT_ETC2_SRGB8: return GL_COMPRESSED_SRGB8_ETC2; default: SOKOL_UNREACHABLE; return 0; } } _SOKOL_PRIVATE GLenum _sg_gl_teximage_internal_format(sg_pixel_format fmt) { #if defined(SOKOL_GLES2) return _sg_gl_teximage_format(fmt); #else if (_sg.gl.gles2) { return _sg_gl_teximage_format(fmt); } else { switch (fmt) { case SG_PIXELFORMAT_NONE: return 0; case SG_PIXELFORMAT_RGBA8: return GL_RGBA8; case SG_PIXELFORMAT_RGB8: return GL_RGB8; case SG_PIXELFORMAT_RGBA4: return GL_RGBA4; case SG_PIXELFORMAT_R5G6B5: #if defined(SOKOL_GLES3) return GL_RGB565; #else return GL_RGB5; #endif case SG_PIXELFORMAT_R5G5B5A1: return GL_RGB5_A1; case SG_PIXELFORMAT_R10G10B10A2: return GL_RGB10_A2; case SG_PIXELFORMAT_RGBA32F: return GL_RGBA32F; case SG_PIXELFORMAT_RGBA16F: return GL_RGBA16F; case SG_PIXELFORMAT_R32F: return GL_R32F; case SG_PIXELFORMAT_R16F: return GL_R16F; case SG_PIXELFORMAT_L8: return GL_R8; case SG_PIXELFORMAT_DEPTH: /* FIXME */ return GL_DEPTH_COMPONENT16; case SG_PIXELFORMAT_DEPTHSTENCIL: return GL_DEPTH24_STENCIL8; case SG_PIXELFORMAT_DXT1: return GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; case SG_PIXELFORMAT_DXT3: return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; case SG_PIXELFORMAT_DXT5: return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; case SG_PIXELFORMAT_PVRTC2_RGB: return GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG; case SG_PIXELFORMAT_PVRTC4_RGB: return GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG; case SG_PIXELFORMAT_PVRTC2_RGBA: return GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; case SG_PIXELFORMAT_PVRTC4_RGBA: return GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; case SG_PIXELFORMAT_ETC2_RGB8: return GL_COMPRESSED_RGB8_ETC2; case SG_PIXELFORMAT_ETC2_SRGB8: return GL_COMPRESSED_SRGB8_ETC2; default: SOKOL_UNREACHABLE; return 0; } } #endif } _SOKOL_PRIVATE GLenum _sg_gl_cubeface_target(int face_index) { switch (face_index) { case 0: return GL_TEXTURE_CUBE_MAP_POSITIVE_X; case 1: return GL_TEXTURE_CUBE_MAP_NEGATIVE_X; case 2: return GL_TEXTURE_CUBE_MAP_POSITIVE_Y; case 3: return GL_TEXTURE_CUBE_MAP_NEGATIVE_Y; case 4: return GL_TEXTURE_CUBE_MAP_POSITIVE_Z; case 5: return GL_TEXTURE_CUBE_MAP_NEGATIVE_Z; default: SOKOL_UNREACHABLE; return 0; } } _SOKOL_PRIVATE GLenum _sg_gl_depth_attachment_format(sg_pixel_format fmt) { switch (fmt) { case SG_PIXELFORMAT_DEPTH: return GL_DEPTH_COMPONENT16; case SG_PIXELFORMAT_DEPTHSTENCIL: return GL_DEPTH24_STENCIL8; default: SOKOL_UNREACHABLE; return 0; } } _SOKOL_PRIVATE void _sg_gl_init_attr(_sg_gl_attr_t* attr) { attr->vb_index = -1; attr->divisor = -1; } _SOKOL_PRIVATE void _sg_gl_init_stencil_state(sg_stencil_state* s) { SOKOL_ASSERT(s); s->fail_op = SG_STENCILOP_KEEP; s->depth_fail_op = SG_STENCILOP_KEEP; s->pass_op = SG_STENCILOP_KEEP; s->compare_func = SG_COMPAREFUNC_ALWAYS; } _SOKOL_PRIVATE void _sg_gl_init_depth_stencil_state(sg_depth_stencil_state* s) { SOKOL_ASSERT(s); _sg_gl_init_stencil_state(&s->stencil_front); _sg_gl_init_stencil_state(&s->stencil_back); s->depth_compare_func = SG_COMPAREFUNC_ALWAYS; } _SOKOL_PRIVATE void _sg_gl_init_blend_state(sg_blend_state* s) { SOKOL_ASSERT(s); s->src_factor_rgb = SG_BLENDFACTOR_ONE; s->dst_factor_rgb = SG_BLENDFACTOR_ZERO; s->op_rgb = SG_BLENDOP_ADD; s->src_factor_alpha = SG_BLENDFACTOR_ONE; s->dst_factor_alpha = SG_BLENDFACTOR_ZERO; s->op_alpha = SG_BLENDOP_ADD; s->color_write_mask = SG_COLORMASK_RGBA; } _SOKOL_PRIVATE void _sg_gl_init_rasterizer_state(sg_rasterizer_state* s) { SOKOL_ASSERT(s); s->cull_mode = SG_CULLMODE_NONE; s->face_winding = SG_FACEWINDING_CW; s->sample_count = 1; } /*-- state cache implementation ----------------------------------------------*/ _SOKOL_PRIVATE void _sg_gl_clear_buffer_bindings(bool force) { if (force || (_sg.gl.cache.vertex_buffer != 0)) { glBindBuffer(GL_ARRAY_BUFFER, 0); _sg.gl.cache.vertex_buffer = 0; } if (force || (_sg.gl.cache.index_buffer != 0)) { glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); _sg.gl.cache.index_buffer = 0; } } _SOKOL_PRIVATE void _sg_gl_bind_buffer(GLenum target, GLuint buffer) { SOKOL_ASSERT((GL_ARRAY_BUFFER == target) || (GL_ELEMENT_ARRAY_BUFFER == target)); if (target == GL_ARRAY_BUFFER) { if (_sg.gl.cache.vertex_buffer != buffer) { _sg.gl.cache.vertex_buffer = buffer; glBindBuffer(target, buffer); } } else { if (_sg.gl.cache.index_buffer != buffer) { _sg.gl.cache.index_buffer = buffer; glBindBuffer(target, buffer); } } } _SOKOL_PRIVATE void _sg_gl_store_buffer_binding(GLenum target) { if (target == GL_ARRAY_BUFFER) { _sg.gl.cache.stored_vertex_buffer = _sg.gl.cache.vertex_buffer; } else { _sg.gl.cache.stored_index_buffer = _sg.gl.cache.index_buffer; } } _SOKOL_PRIVATE void _sg_gl_restore_buffer_binding(GLenum target) { if (target == GL_ARRAY_BUFFER) { _sg_gl_bind_buffer(target, _sg.gl.cache.stored_vertex_buffer); } else { _sg_gl_bind_buffer(target, _sg.gl.cache.stored_index_buffer); } } _SOKOL_PRIVATE void _sg_gl_clear_texture_bindings(bool force) { for (int i = 0; (i < SG_MAX_SHADERSTAGE_IMAGES) && (i < _sg.gl.max_combined_texture_image_units); i++) { if (force || (_sg.gl.cache.textures[i].texture != 0)) { glActiveTexture(GL_TEXTURE0 + i); glBindTexture(GL_TEXTURE_2D, 0); glBindTexture(GL_TEXTURE_CUBE_MAP, 0); #if !defined(SOKOL_GLES2) if (!_sg.gl.gles2) { glBindTexture(GL_TEXTURE_3D, 0); glBindTexture(GL_TEXTURE_2D_ARRAY, 0); } #endif _sg.gl.cache.textures[i].target = 0; _sg.gl.cache.textures[i].texture = 0; } } } _SOKOL_PRIVATE void _sg_gl_bind_texture(int slot_index, GLenum target, GLuint texture) { /* it's valid to call this function with target=0 and/or texture=0 target=0 will unbind the previous binding, texture=0 will clear the new binding */ SOKOL_ASSERT(slot_index < SG_MAX_SHADERSTAGE_IMAGES); if (slot_index >= _sg.gl.max_combined_texture_image_units) { return; } _sg_gl_texture_bind_slot* slot = &_sg.gl.cache.textures[slot_index]; if ((slot->target != target) || (slot->texture != texture)) { glActiveTexture(GL_TEXTURE0 + slot_index); /* if the target has changed, clear the previous binding on that target */ if ((target != slot->target) && (slot->target != 0)) { glBindTexture(slot->target, 0); } /* apply new binding (texture can be 0 to unbind) */ if (target != 0) { glBindTexture(target, texture); } slot->target = target; slot->texture = texture; } } _SOKOL_PRIVATE void _sg_gl_store_texture_binding(int slot_index) { SOKOL_ASSERT(slot_index < SG_MAX_SHADERSTAGE_IMAGES); _sg.gl.cache.stored_texture = _sg.gl.cache.textures[slot_index]; } _SOKOL_PRIVATE void _sg_gl_restore_texture_binding(int slot_index) { SOKOL_ASSERT(slot_index < SG_MAX_SHADERSTAGE_IMAGES); const _sg_gl_texture_bind_slot* slot = &_sg.gl.cache.stored_texture; _sg_gl_bind_texture(slot_index, slot->target, slot->texture); } _SOKOL_PRIVATE void _sg_gl_reset_state_cache(void) { _SG_GL_CHECK_ERROR(); memset(&_sg.gl.cache, 0, sizeof(_sg.gl.cache)); _sg_gl_clear_buffer_bindings(true); _SG_GL_CHECK_ERROR(); _sg_gl_clear_texture_bindings(true); _SG_GL_CHECK_ERROR(); for (int i = 0; i < SG_MAX_VERTEX_ATTRIBUTES; i++) { _sg_gl_init_attr(&_sg.gl.cache.attrs[i].gl_attr); glDisableVertexAttribArray(i); _SG_GL_CHECK_ERROR(); } _sg.gl.cache.cur_primitive_type = GL_TRIANGLES; /* depth-stencil state */ _sg_gl_init_depth_stencil_state(&_sg.gl.cache.ds); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_ALWAYS); glDepthMask(GL_FALSE); glDisable(GL_STENCIL_TEST); glStencilFunc(GL_ALWAYS, 0, 0); glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); glStencilMask(0); /* blend state */ _sg_gl_init_blend_state(&_sg.gl.cache.blend); glDisable(GL_BLEND); glBlendFuncSeparate(GL_ONE, GL_ZERO, GL_ONE, GL_ZERO); glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glBlendColor(0.0f, 0.0f, 0.0f, 0.0f); /* rasterizer state */ _sg_gl_init_rasterizer_state(&_sg.gl.cache.rast); glPolygonOffset(0.0f, 0.0f); glDisable(GL_POLYGON_OFFSET_FILL); glDisable(GL_CULL_FACE); glFrontFace(GL_CW); glCullFace(GL_BACK); glEnable(GL_SCISSOR_TEST); glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE); glEnable(GL_DITHER); glDisable(GL_POLYGON_OFFSET_FILL); #if defined(SOKOL_GLCORE33) glEnable(GL_MULTISAMPLE); glEnable(GL_PROGRAM_POINT_SIZE); #endif } /*-- main GL backend state and functions -------------------------------------*/ _SOKOL_PRIVATE void _sg_setup_backend(const sg_desc* desc) { /* assumes that _sg.gl is already zero-initialized */ _sg.gl.valid = true; #if defined(SOKOL_GLES2) || defined(SOKOL_GLES3) _sg.gl.gles2 = desc->gl_force_gles2; #else _SOKOL_UNUSED(desc); _sg.gl.gles2 = false; #endif /* clear initial GL error state */ #if defined(SOKOL_DEBUG) while (glGetError() != GL_NO_ERROR); #endif /* initialize feature flags */ _sg.gl.features[SG_FEATURE_ORIGIN_BOTTOM_LEFT] = true; #if defined(SOKOL_GLCORE33) _sg.gl.features[SG_FEATURE_INSTANCING] = true; _sg.gl.features[SG_FEATURE_TEXTURE_FLOAT] = true; _sg.gl.features[SG_FEATURE_TEXTURE_HALF_FLOAT] = true; _sg.gl.features[SG_FEATURE_MSAA_RENDER_TARGETS] = true; _sg.gl.features[SG_FEATURE_PACKED_VERTEX_FORMAT_10_2] = true; _sg.gl.features[SG_FEATURE_MULTIPLE_RENDER_TARGET] = true; _sg.gl.features[SG_FEATURE_IMAGETYPE_3D] = true; _sg.gl.features[SG_FEATURE_IMAGETYPE_ARRAY] = true; GLint num_ext = 0; glGetIntegerv(GL_NUM_EXTENSIONS, &num_ext); for (int i = 0; i < num_ext; i++) { const char* ext = (const char*) glGetStringi(GL_EXTENSIONS, i); if (ext) { if (strstr(ext, "_texture_compression_s3tc")) { _sg.gl.features[SG_FEATURE_TEXTURE_COMPRESSION_DXT] = true; continue; } else if (strstr(ext, "_texture_filter_anisotropic")) { _sg.gl.ext_anisotropic = true; continue; } } } #elif defined(SOKOL_GLES3) const char* ext = (const char*) glGetString(GL_EXTENSIONS); if (ext) { if (!_sg.gl.gles2) { _sg.gl.features[SG_FEATURE_INSTANCING] = true; _sg.gl.features[SG_FEATURE_TEXTURE_FLOAT] = true; _sg.gl.features[SG_FEATURE_TEXTURE_HALF_FLOAT] = true; _sg.gl.features[SG_FEATURE_IMAGETYPE_3D] = true; _sg.gl.features[SG_FEATURE_IMAGETYPE_ARRAY] = true; _sg.gl.features[SG_FEATURE_MSAA_RENDER_TARGETS] = true; _sg.gl.features[SG_FEATURE_PACKED_VERTEX_FORMAT_10_2] = true; _sg.gl.features[SG_FEATURE_MULTIPLE_RENDER_TARGET] = true; } else { _sg.gl.features[SG_FEATURE_INSTANCING] = strstr(ext, "_instanced_arrays"); _sg.gl.features[SG_FEATURE_TEXTURE_FLOAT] = strstr(ext, "_texture_float"); _sg.gl.features[SG_FEATURE_TEXTURE_HALF_FLOAT] = strstr(ext, "_texture_half_float"); } _sg.gl.features[SG_FEATURE_TEXTURE_COMPRESSION_DXT] = strstr(ext, "_texture_compression_s3tc") || strstr(ext, "_compressed_texture_s3tc") || strstr(ext, "texture_compression_dxt1"); _sg.gl.features[SG_FEATURE_TEXTURE_COMPRESSION_PVRTC] = strstr(ext, "_texture_compression_pvrtc") || strstr(ext, "_compressed_texture_pvrtc"); _sg.gl.features[SG_FEATURE_TEXTURE_COMPRESSION_ATC] = strstr(ext, "_compressed_texture_atc"); _sg.gl.ext_anisotropic = strstr(ext, "_texture_filter_anisotropic"); } #elif defined(SOKOL_GLES2) const char* ext = (const char*) glGetString(GL_EXTENSIONS); if (ext) { _sg.gl.features[SG_FEATURE_INSTANCING] = strstr(ext, "_instanced_arrays"); _sg.gl.features[SG_FEATURE_TEXTURE_FLOAT] = strstr(ext, "_texture_float"); _sg.gl.features[SG_FEATURE_TEXTURE_HALF_FLOAT] = strstr(ext, "_texture_half_float"); _sg.gl.features[SG_FEATURE_TEXTURE_COMPRESSION_DXT] = strstr(ext, "_texture_compression_s3tc") || strstr(ext, "_compressed_texture_s3tc") || strstr(ext, "texture_compression_dxt1"); _sg.gl.features[SG_FEATURE_TEXTURE_COMPRESSION_PVRTC] = strstr(ext, "_texture_compression_pvrtc") || strstr(ext, "_compressed_texture_pvrtc"); _sg.gl.features[SG_FEATURE_TEXTURE_COMPRESSION_ATC] = strstr(ext, "_compressed_texture_atc"); _sg.gl.ext_anisotropic = strstr(ext, "_texture_filter_anisotropic"); } #endif _sg.gl.max_anisotropy = 1; if (_sg.gl.ext_anisotropic) { glGetIntegerv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &_sg.gl.max_anisotropy); } glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &_sg.gl.max_combined_texture_image_units); } _SOKOL_PRIVATE void _sg_discard_backend(void) { SOKOL_ASSERT(_sg.gl.valid); _sg.gl.valid = false; } _SOKOL_PRIVATE void _sg_reset_state_cache(void) { if (_sg.gl.cur_context) { #if !defined(SOKOL_GLES2) if (!_sg.gl.gles2) { _SG_GL_CHECK_ERROR(); glBindVertexArray(_sg.gl.cur_context->vao); _SG_GL_CHECK_ERROR(); } #endif _sg_gl_reset_state_cache(); } } _SOKOL_PRIVATE bool _sg_query_feature(sg_feature f) { SOKOL_ASSERT((f>=0) && (fdefault_framebuffer); _SG_GL_CHECK_ERROR(); glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&ctx->default_framebuffer); _SG_GL_CHECK_ERROR(); #if !defined(SOKOL_GLES2) if (!_sg.gl.gles2) { SOKOL_ASSERT(0 == ctx->vao); glGenVertexArrays(1, &ctx->vao); glBindVertexArray(ctx->vao); _SG_GL_CHECK_ERROR(); } #endif return SG_RESOURCESTATE_VALID; } _SOKOL_PRIVATE void _sg_destroy_context(_sg_context_t* ctx) { SOKOL_ASSERT(ctx); #if !defined(SOKOL_GLES2) if (!_sg.gl.gles2) { if (ctx->vao) { glDeleteVertexArrays(1, &ctx->vao); } _SG_GL_CHECK_ERROR(); } #endif } _SOKOL_PRIVATE sg_resource_state _sg_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { SOKOL_ASSERT(buf && desc); _SG_GL_CHECK_ERROR(); buf->size = desc->size; buf->append_pos = 0; buf->append_overflow = false; buf->type = desc->type; buf->usage = desc->usage; buf->update_frame_index = 0; buf->append_frame_index = 0; buf->num_slots = (buf->usage == SG_USAGE_IMMUTABLE) ? 1 : SG_NUM_INFLIGHT_FRAMES; buf->active_slot = 0; buf->ext_buffers = (0 != desc->gl_buffers[0]); GLenum gl_target = _sg_gl_buffer_target(buf->type); GLenum gl_usage = _sg_gl_usage(buf->usage); for (int slot = 0; slot < buf->num_slots; slot++) { GLuint gl_buf = 0; if (buf->ext_buffers) { SOKOL_ASSERT(desc->gl_buffers[slot]); gl_buf = desc->gl_buffers[slot]; } else { glGenBuffers(1, &gl_buf); _sg_gl_store_buffer_binding(gl_target); _sg_gl_bind_buffer(gl_target, gl_buf); glBufferData(gl_target, buf->size, 0, gl_usage); if (buf->usage == SG_USAGE_IMMUTABLE) { SOKOL_ASSERT(desc->content); glBufferSubData(gl_target, 0, buf->size, desc->content); } _sg_gl_restore_buffer_binding(gl_target); } buf->gl_buf[slot] = gl_buf; } _SG_GL_CHECK_ERROR(); return SG_RESOURCESTATE_VALID; } _SOKOL_PRIVATE void _sg_destroy_buffer(_sg_buffer_t* buf) { SOKOL_ASSERT(buf); _SG_GL_CHECK_ERROR(); if (!buf->ext_buffers) { for (int slot = 0; slot < buf->num_slots; slot++) { if (buf->gl_buf[slot]) { glDeleteBuffers(1, &buf->gl_buf[slot]); } } _SG_GL_CHECK_ERROR(); } } _SOKOL_PRIVATE bool _sg_gl_supported_texture_format(sg_pixel_format fmt) { switch (fmt) { case SG_PIXELFORMAT_DXT1: case SG_PIXELFORMAT_DXT3: case SG_PIXELFORMAT_DXT5: return _sg.gl.features[SG_FEATURE_TEXTURE_COMPRESSION_DXT]; case SG_PIXELFORMAT_PVRTC2_RGB: case SG_PIXELFORMAT_PVRTC4_RGB: case SG_PIXELFORMAT_PVRTC2_RGBA: case SG_PIXELFORMAT_PVRTC4_RGBA: return _sg.gl.features[SG_FEATURE_TEXTURE_COMPRESSION_PVRTC]; case SG_PIXELFORMAT_ETC2_RGB8: case SG_PIXELFORMAT_ETC2_SRGB8: return _sg.gl.features[SG_FEATURE_TEXTURE_COMPRESSION_ETC2]; default: return true; } } _SOKOL_PRIVATE sg_resource_state _sg_create_image(_sg_image_t* img, const sg_image_desc* desc) { SOKOL_ASSERT(img && desc); _SG_GL_CHECK_ERROR(); img->type = desc->type; img->render_target = desc->render_target; img->width = desc->width; img->height = desc->height; img->depth = desc->depth; img->num_mipmaps = desc->num_mipmaps; img->usage = desc->usage; img->pixel_format = desc->pixel_format; img->sample_count = desc->sample_count; img->min_filter = desc->min_filter; img->mag_filter = desc->mag_filter; img->wrap_u = desc->wrap_u; img->wrap_v = desc->wrap_v; img->wrap_w = desc->wrap_w; img->max_anisotropy = desc->max_anisotropy; img->upd_frame_index = 0; /* check if texture format is support */ if (!_sg_gl_supported_texture_format(img->pixel_format)) { SOKOL_LOG("compressed texture format not supported by GL context\n"); return SG_RESOURCESTATE_FAILED; } /* check for optional texture types */ if ((img->type == SG_IMAGETYPE_3D) && !_sg.gl.features[SG_FEATURE_IMAGETYPE_3D]) { SOKOL_LOG("3D textures not supported by GL context\n"); return SG_RESOURCESTATE_FAILED; } if ((img->type == SG_IMAGETYPE_ARRAY) && !_sg.gl.features[SG_FEATURE_IMAGETYPE_ARRAY]) { SOKOL_LOG("array textures not supported by GL context\n"); return SG_RESOURCESTATE_FAILED; } /* create 1 or 2 GL textures, depending on requested update strategy */ img->num_slots = (img->usage == SG_USAGE_IMMUTABLE) ? 1 : SG_NUM_INFLIGHT_FRAMES; img->active_slot = 0; img->ext_textures = (0 != desc->gl_textures[0]); #if !defined(SOKOL_GLES2) bool msaa = false; if (!_sg.gl.gles2) { msaa = (img->sample_count > 1) && (_sg.gl.features[SG_FEATURE_MSAA_RENDER_TARGETS]); } #endif if (_sg_is_valid_rendertarget_depth_format(img->pixel_format)) { /* special case depth-stencil-buffer? */ SOKOL_ASSERT((img->usage == SG_USAGE_IMMUTABLE) && (img->num_slots == 1)); SOKOL_ASSERT(!img->ext_textures); /* cannot provide external texture for depth images */ glGenRenderbuffers(1, &img->gl_depth_render_buffer); glBindRenderbuffer(GL_RENDERBUFFER, img->gl_depth_render_buffer); GLenum gl_depth_format = _sg_gl_depth_attachment_format(img->pixel_format); #if !defined(SOKOL_GLES2) if (!_sg.gl.gles2 && msaa) { glRenderbufferStorageMultisample(GL_RENDERBUFFER, img->sample_count, gl_depth_format, img->width, img->height); } else #endif { glRenderbufferStorage(GL_RENDERBUFFER, gl_depth_format, img->width, img->height); } } else { /* regular color texture */ img->gl_target = _sg_gl_texture_target(img->type); const GLenum gl_internal_format = _sg_gl_teximage_internal_format(img->pixel_format); /* if this is a MSAA render target, need to create a separate render buffer */ #if !defined(SOKOL_GLES2) if (!_sg.gl.gles2 && img->render_target && msaa) { glGenRenderbuffers(1, &img->gl_msaa_render_buffer); glBindRenderbuffer(GL_RENDERBUFFER, img->gl_msaa_render_buffer); glRenderbufferStorageMultisample(GL_RENDERBUFFER, img->sample_count, gl_internal_format, img->width, img->height); } #endif if (img->ext_textures) { /* inject externally GL textures */ for (int slot = 0; slot < img->num_slots; slot++) { SOKOL_ASSERT(desc->gl_textures[slot]); img->gl_tex[slot] = desc->gl_textures[slot]; } } else { /* create our own GL texture(s) */ const GLenum gl_format = _sg_gl_teximage_format(img->pixel_format); const bool is_compressed = _sg_is_compressed_pixel_format(img->pixel_format); for (int slot = 0; slot < img->num_slots; slot++) { glGenTextures(1, &img->gl_tex[slot]); _sg_gl_store_texture_binding(0); _sg_gl_bind_texture(0, img->gl_target, img->gl_tex[slot]); GLenum gl_min_filter = _sg_gl_filter(img->min_filter); GLenum gl_mag_filter = _sg_gl_filter(img->mag_filter); glTexParameteri(img->gl_target, GL_TEXTURE_MIN_FILTER, gl_min_filter); glTexParameteri(img->gl_target, GL_TEXTURE_MAG_FILTER, gl_mag_filter); if (_sg.gl.ext_anisotropic && (img->max_anisotropy > 1)) { GLint max_aniso = (GLint) img->max_anisotropy; if (max_aniso > _sg.gl.max_anisotropy) { max_aniso = _sg.gl.max_anisotropy; } glTexParameteri(img->gl_target, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_aniso); } if (img->type == SG_IMAGETYPE_CUBE) { glTexParameteri(img->gl_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(img->gl_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); } else { glTexParameteri(img->gl_target, GL_TEXTURE_WRAP_S, _sg_gl_wrap(img->wrap_u)); glTexParameteri(img->gl_target, GL_TEXTURE_WRAP_T, _sg_gl_wrap(img->wrap_v)); #if !defined(SOKOL_GLES2) if (!_sg.gl.gles2 && (img->type == SG_IMAGETYPE_3D)) { glTexParameteri(img->gl_target, GL_TEXTURE_WRAP_R, _sg_gl_wrap(img->wrap_w)); } #endif } #if !defined(SOKOL_GLES2) if (!_sg.gl.gles2) { /* GL spec has strange defaults for mipmap min/max lod: -1000 to +1000 */ const float min_lod = _sg_clamp(desc->min_lod, 0.0f, 1000.0f); const float max_lod = _sg_clamp(desc->max_lod, 0.0f, 1000.0f); glTexParameterf(img->gl_target, GL_TEXTURE_MIN_LOD, min_lod); glTexParameterf(img->gl_target, GL_TEXTURE_MAX_LOD, max_lod); } #endif const int num_faces = img->type == SG_IMAGETYPE_CUBE ? 6 : 1; int data_index = 0; for (int face_index = 0; face_index < num_faces; face_index++) { for (int mip_index = 0; mip_index < img->num_mipmaps; mip_index++, data_index++) { GLenum gl_img_target = img->gl_target; if (SG_IMAGETYPE_CUBE == img->type) { gl_img_target = _sg_gl_cubeface_target(face_index); } const GLvoid* data_ptr = desc->content.subimage[face_index][mip_index].ptr; const int data_size = desc->content.subimage[face_index][mip_index].size; int mip_width = img->width >> mip_index; if (mip_width == 0) { mip_width = 1; } int mip_height = img->height >> mip_index; if (mip_height == 0) { mip_height = 1; } if ((SG_IMAGETYPE_2D == img->type) || (SG_IMAGETYPE_CUBE == img->type)) { if (is_compressed) { glCompressedTexImage2D(gl_img_target, mip_index, gl_internal_format, mip_width, mip_height, 0, data_size, data_ptr); } else { const GLenum gl_type = _sg_gl_teximage_type(img->pixel_format); glTexImage2D(gl_img_target, mip_index, gl_internal_format, mip_width, mip_height, 0, gl_format, gl_type, data_ptr); } } #if !defined(SOKOL_GLES2) else if (!_sg.gl.gles2 && ((SG_IMAGETYPE_3D == img->type) || (SG_IMAGETYPE_ARRAY == img->type))) { int mip_depth = img->depth; if (SG_IMAGETYPE_3D == img->type) { mip_depth >>= mip_index; } if (mip_depth == 0) { mip_depth = 1; } if (is_compressed) { glCompressedTexImage3D(gl_img_target, mip_index, gl_internal_format, mip_width, mip_height, mip_depth, 0, data_size, data_ptr); } else { const GLenum gl_type = _sg_gl_teximage_type(img->pixel_format); glTexImage3D(gl_img_target, mip_index, gl_internal_format, mip_width, mip_height, mip_depth, 0, gl_format, gl_type, data_ptr); } } #endif } } _sg_gl_restore_texture_binding(0); } } } _SG_GL_CHECK_ERROR(); return SG_RESOURCESTATE_VALID; } _SOKOL_PRIVATE void _sg_destroy_image(_sg_image_t* img) { SOKOL_ASSERT(img); _SG_GL_CHECK_ERROR(); if (!img->ext_textures) { for (int slot = 0; slot < img->num_slots; slot++) { if (img->gl_tex[slot]) { glDeleteTextures(1, &img->gl_tex[slot]); } } } if (img->gl_depth_render_buffer) { glDeleteRenderbuffers(1, &img->gl_depth_render_buffer); } if (img->gl_msaa_render_buffer) { glDeleteRenderbuffers(1, &img->gl_msaa_render_buffer); } _SG_GL_CHECK_ERROR(); } _SOKOL_PRIVATE GLuint _sg_gl_compile_shader(sg_shader_stage stage, const char* src) { SOKOL_ASSERT(src); _SG_GL_CHECK_ERROR(); GLuint gl_shd = glCreateShader(_sg_gl_shader_stage(stage)); glShaderSource(gl_shd, 1, &src, 0); glCompileShader(gl_shd); GLint compile_status = 0; glGetShaderiv(gl_shd, GL_COMPILE_STATUS, &compile_status); if (!compile_status) { /* compilation failed, log error and delete shader */ GLint log_len = 0; glGetShaderiv(gl_shd, GL_INFO_LOG_LENGTH, &log_len); if (log_len > 0) { GLchar* log_buf = (GLchar*) SOKOL_MALLOC(log_len); glGetShaderInfoLog(gl_shd, log_len, &log_len, log_buf); SOKOL_LOG(log_buf); SOKOL_FREE(log_buf); } glDeleteShader(gl_shd); gl_shd = 0; } _SG_GL_CHECK_ERROR(); return gl_shd; } _SOKOL_PRIVATE sg_resource_state _sg_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { SOKOL_ASSERT(shd && desc); SOKOL_ASSERT(!shd->gl_prog); _SG_GL_CHECK_ERROR(); /* copy vertex attribute names over, these are required for GLES2, and optional for GLES3 and GL3.x */ for (int i = 0; i < SG_MAX_VERTEX_ATTRIBUTES; i++) { _sg_strcpy(&shd->attrs[i].name, desc->attrs[i].name); } GLuint gl_vs = _sg_gl_compile_shader(SG_SHADERSTAGE_VS, desc->vs.source); GLuint gl_fs = _sg_gl_compile_shader(SG_SHADERSTAGE_FS, desc->fs.source); if (!(gl_vs && gl_fs)) { return SG_RESOURCESTATE_FAILED; } GLuint gl_prog = glCreateProgram(); glAttachShader(gl_prog, gl_vs); glAttachShader(gl_prog, gl_fs); glLinkProgram(gl_prog); glDeleteShader(gl_vs); glDeleteShader(gl_fs); _SG_GL_CHECK_ERROR(); GLint link_status; glGetProgramiv(gl_prog, GL_LINK_STATUS, &link_status); if (!link_status) { GLint log_len = 0; glGetProgramiv(gl_prog, GL_INFO_LOG_LENGTH, &log_len); if (log_len > 0) { GLchar* log_buf = (GLchar*) SOKOL_MALLOC(log_len); glGetProgramInfoLog(gl_prog, log_len, &log_len, log_buf); SOKOL_LOG(log_buf); SOKOL_FREE(log_buf); } glDeleteProgram(gl_prog); return SG_RESOURCESTATE_FAILED; } shd->gl_prog = gl_prog; /* resolve uniforms */ _SG_GL_CHECK_ERROR(); for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { const sg_shader_stage_desc* stage_desc = (stage_index == SG_SHADERSTAGE_VS)? &desc->vs : &desc->fs; _sg_shader_stage_t* stage = &shd->stage[stage_index]; SOKOL_ASSERT(stage->num_uniform_blocks == 0); for (int ub_index = 0; ub_index < SG_MAX_SHADERSTAGE_UBS; ub_index++) { const sg_shader_uniform_block_desc* ub_desc = &stage_desc->uniform_blocks[ub_index]; if (0 == ub_desc->size) { break; } _sg_uniform_block_t* ub = &stage->uniform_blocks[ub_index]; ub->size = ub_desc->size; SOKOL_ASSERT(ub->num_uniforms == 0); int cur_uniform_offset = 0; for (int u_index = 0; u_index < SG_MAX_UB_MEMBERS; u_index++) { const sg_shader_uniform_desc* u_desc = &ub_desc->uniforms[u_index]; if (u_desc->type == SG_UNIFORMTYPE_INVALID) { break; } _sg_uniform_t* u = &ub->uniforms[u_index]; u->type = u_desc->type; u->count = (uint8_t) u_desc->array_count; u->offset = (uint16_t) cur_uniform_offset; cur_uniform_offset += _sg_uniform_size(u->type, u->count); if (u_desc->name) { u->gl_loc = glGetUniformLocation(gl_prog, u_desc->name); } else { u->gl_loc = u_index; } ub->num_uniforms++; } SOKOL_ASSERT(ub_desc->size == cur_uniform_offset); stage->num_uniform_blocks++; } } /* resolve image locations */ _SG_GL_CHECK_ERROR(); int gl_tex_slot = 0; for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { const sg_shader_stage_desc* stage_desc = (stage_index == SG_SHADERSTAGE_VS)? &desc->vs : &desc->fs; _sg_shader_stage_t* stage = &shd->stage[stage_index]; SOKOL_ASSERT(stage->num_images == 0); for (int img_index = 0; img_index < SG_MAX_SHADERSTAGE_IMAGES; img_index++) { const sg_shader_image_desc* img_desc = &stage_desc->images[img_index]; if (img_desc->type == _SG_IMAGETYPE_DEFAULT) { break; } _sg_shader_image_t* img = &stage->images[img_index]; img->type = img_desc->type; img->gl_loc = img_index; if (img_desc->name) { img->gl_loc = glGetUniformLocation(gl_prog, img_desc->name); } if (img->gl_loc != -1) { img->gl_tex_slot = gl_tex_slot++; } else { img->gl_tex_slot = -1; } stage->num_images++; } } _SG_GL_CHECK_ERROR(); return SG_RESOURCESTATE_VALID; } _SOKOL_PRIVATE void _sg_destroy_shader(_sg_shader_t* shd) { SOKOL_ASSERT(shd); _SG_GL_CHECK_ERROR(); if (shd->gl_prog) { glDeleteProgram(shd->gl_prog); } _SG_GL_CHECK_ERROR(); } _SOKOL_PRIVATE sg_resource_state _sg_create_pipeline(_sg_pipeline_t* pip, _sg_shader_t* shd, const sg_pipeline_desc* desc) { SOKOL_ASSERT(pip && shd && desc); SOKOL_ASSERT(!pip->shader && pip->shader_id.id == SG_INVALID_ID); SOKOL_ASSERT(desc->shader.id == shd->slot.id); SOKOL_ASSERT(shd->gl_prog); pip->shader = shd; pip->shader_id = desc->shader; pip->primitive_type = desc->primitive_type; pip->index_type = desc->index_type; pip->color_attachment_count = desc->blend.color_attachment_count; pip->color_format = desc->blend.color_format; pip->depth_format = desc->blend.depth_format; pip->sample_count = desc->rasterizer.sample_count; pip->depth_stencil = desc->depth_stencil; pip->blend = desc->blend; pip->rast = desc->rasterizer; /* resolve vertex attributes */ for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { pip->gl_attrs[attr_index].vb_index = -1; } for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { const sg_vertex_attr_desc* a_desc = &desc->layout.attrs[attr_index]; if (a_desc->format == SG_VERTEXFORMAT_INVALID) { break; } SOKOL_ASSERT((a_desc->buffer_index >= 0) && (a_desc->buffer_index < SG_MAX_SHADERSTAGE_BUFFERS)); const sg_buffer_layout_desc* l_desc = &desc->layout.buffers[a_desc->buffer_index]; const sg_vertex_step step_func = l_desc->step_func; const int step_rate = l_desc->step_rate; GLint attr_loc = attr_index; if (!_sg_strempty(&shd->attrs[attr_index].name)) { attr_loc = glGetAttribLocation(pip->shader->gl_prog, _sg_strptr(&shd->attrs[attr_index].name)); } SOKOL_ASSERT(attr_loc < SG_MAX_VERTEX_ATTRIBUTES); if (attr_loc != -1) { _sg_gl_attr_t* gl_attr = &pip->gl_attrs[attr_loc]; SOKOL_ASSERT(gl_attr->vb_index == -1); gl_attr->vb_index = (int8_t) a_desc->buffer_index; if (step_func == SG_VERTEXSTEP_PER_VERTEX) { gl_attr->divisor = 0; } else { gl_attr->divisor = (int8_t) step_rate; } SOKOL_ASSERT(l_desc->stride > 0); gl_attr->stride = (uint8_t) l_desc->stride; gl_attr->offset = a_desc->offset; gl_attr->size = (uint8_t) _sg_gl_vertexformat_size(a_desc->format); gl_attr->type = _sg_gl_vertexformat_type(a_desc->format); gl_attr->normalized = _sg_gl_vertexformat_normalized(a_desc->format); pip->vertex_layout_valid[a_desc->buffer_index] = true; } else { SOKOL_LOG("Vertex attribute not found in shader: "); SOKOL_LOG(_sg_strptr(&shd->attrs[attr_index].name)); } } return SG_RESOURCESTATE_VALID; } _SOKOL_PRIVATE void _sg_destroy_pipeline(_sg_pipeline_t* pip) { SOKOL_ASSERT(pip); /* empty */ } /* _sg_create_pass att_imgs must point to a _sg_image* att_imgs[SG_MAX_COLOR_ATTACHMENTS+1] array, first entries are the color attachment images (or nullptr), last entry is the depth-stencil image (or nullptr). */ _SOKOL_PRIVATE sg_resource_state _sg_create_pass(_sg_pass_t* pass, _sg_image_t** att_images, const sg_pass_desc* desc) { SOKOL_ASSERT(pass && att_images && desc); SOKOL_ASSERT(att_images && att_images[0]); _SG_GL_CHECK_ERROR(); /* copy image pointers and desc attributes */ const sg_attachment_desc* att_desc; _sg_attachment_t* att; for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { SOKOL_ASSERT(0 == pass->color_atts[i].image); att_desc = &desc->color_attachments[i]; if (att_desc->image.id != SG_INVALID_ID) { pass->num_color_atts++; SOKOL_ASSERT(att_images[i] && (att_images[i]->slot.id == att_desc->image.id)); SOKOL_ASSERT(_sg_is_valid_rendertarget_color_format(att_images[i]->pixel_format)); att = &pass->color_atts[i]; SOKOL_ASSERT((att->image == 0) && (att->image_id.id == SG_INVALID_ID)); att->image = att_images[i]; att->image_id = att_desc->image; att->mip_level = att_desc->mip_level; att->slice = att_desc->slice; } } SOKOL_ASSERT(0 == pass->ds_att.image); att_desc = &desc->depth_stencil_attachment; const int ds_img_index = SG_MAX_COLOR_ATTACHMENTS; if (att_desc->image.id != SG_INVALID_ID) { SOKOL_ASSERT(att_images[ds_img_index] && (att_images[ds_img_index]->slot.id == att_desc->image.id)); SOKOL_ASSERT(_sg_is_valid_rendertarget_depth_format(att_images[ds_img_index]->pixel_format)); att = &pass->ds_att; SOKOL_ASSERT((att->image == 0) && (att->image_id.id == SG_INVALID_ID)); att->image = att_images[ds_img_index]; att->image_id = att_desc->image; att->mip_level = att_desc->mip_level; att->slice = att_desc->slice; } /* store current framebuffer binding (restored at end of function) */ GLuint gl_orig_fb; glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&gl_orig_fb); /* create a framebuffer object */ glGenFramebuffers(1, &pass->gl_fb); glBindFramebuffer(GL_FRAMEBUFFER, pass->gl_fb); /* attach msaa render buffer or textures */ const bool is_msaa = (0 != att_images[0]->gl_msaa_render_buffer); if (is_msaa) { for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { const _sg_image_t* att_img = pass->color_atts[i].image; if (att_img) { const GLuint gl_render_buffer = att_img->gl_msaa_render_buffer; SOKOL_ASSERT(gl_render_buffer); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0+i, GL_RENDERBUFFER, gl_render_buffer); } } } else { for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { const _sg_image_t* att_img = pass->color_atts[i].image; const int mip_level = pass->color_atts[i].mip_level; const int slice = pass->color_atts[i].slice; if (att_img) { const GLuint gl_tex = att_img->gl_tex[0]; SOKOL_ASSERT(gl_tex); const GLenum gl_att = GL_COLOR_ATTACHMENT0 + i; switch (att_img->type) { case SG_IMAGETYPE_2D: glFramebufferTexture2D(GL_FRAMEBUFFER, gl_att, GL_TEXTURE_2D, gl_tex, mip_level); break; case SG_IMAGETYPE_CUBE: glFramebufferTexture2D(GL_FRAMEBUFFER, gl_att, _sg_gl_cubeface_target(slice), gl_tex, mip_level); break; default: /* 3D- or array-texture */ #if !defined(SOKOL_GLES2) if (!_sg.gl.gles2) { glFramebufferTextureLayer(GL_FRAMEBUFFER, gl_att, gl_tex, mip_level, slice); } #endif break; } } } } /* attach depth-stencil buffer to framebuffer */ if (pass->ds_att.image) { const GLuint gl_render_buffer = pass->ds_att.image->gl_depth_render_buffer; SOKOL_ASSERT(gl_render_buffer); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, gl_render_buffer); if (_sg_is_depth_stencil_format(pass->ds_att.image->pixel_format)) { glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, gl_render_buffer); } } /* check if framebuffer is complete */ if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { SOKOL_LOG("Framebuffer completeness check failed!\n"); return SG_RESOURCESTATE_FAILED; } /* create MSAA resolve framebuffers if necessary */ if (is_msaa) { for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { att = &pass->color_atts[i]; if (att->image) { SOKOL_ASSERT(0 == att->gl_msaa_resolve_buffer); glGenFramebuffers(1, &att->gl_msaa_resolve_buffer); glBindFramebuffer(GL_FRAMEBUFFER, att->gl_msaa_resolve_buffer); const GLuint gl_tex = att->image->gl_tex[0]; SOKOL_ASSERT(gl_tex); switch (att->image->type) { case SG_IMAGETYPE_2D: glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, gl_tex, att->mip_level); break; case SG_IMAGETYPE_CUBE: glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, _sg_gl_cubeface_target(att->slice), gl_tex, att->mip_level); break; default: #if !defined(SOKOL_GLES2) if (!_sg.gl.gles2) { glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, gl_tex, att->mip_level, att->slice); } #endif break; } /* check if framebuffer is complete */ if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { SOKOL_LOG("Framebuffer completeness check failed (msaa resolve buffer)!\n"); return SG_RESOURCESTATE_FAILED; } } } } /* restore original framebuffer binding */ glBindFramebuffer(GL_FRAMEBUFFER, gl_orig_fb); _SG_GL_CHECK_ERROR(); return SG_RESOURCESTATE_VALID; } _SOKOL_PRIVATE void _sg_destroy_pass(_sg_pass_t* pass) { SOKOL_ASSERT(pass); _SG_GL_CHECK_ERROR(); if (0 != pass->gl_fb) { glDeleteFramebuffers(1, &pass->gl_fb); } for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { if (pass->color_atts[i].gl_msaa_resolve_buffer) { glDeleteFramebuffers(1, &pass->color_atts[i].gl_msaa_resolve_buffer); } } if (pass->ds_att.gl_msaa_resolve_buffer) { glDeleteFramebuffers(1, &pass->ds_att.gl_msaa_resolve_buffer); } _SG_GL_CHECK_ERROR(); } /*-- GL backend rendering functions ------------------------------------------*/ _SOKOL_PRIVATE void _sg_begin_pass(_sg_pass_t* pass, const sg_pass_action* action, int w, int h) { /* FIXME: what if a texture used as render target is still bound, should we unbind all currently bound textures in begin pass? */ SOKOL_ASSERT(action); SOKOL_ASSERT(!_sg.gl.in_pass); _SG_GL_CHECK_ERROR(); _sg.gl.in_pass = true; _sg.gl.cur_pass = pass; /* can be 0 */ if (pass) { _sg.gl.cur_pass_id.id = pass->slot.id; } else { _sg.gl.cur_pass_id.id = SG_INVALID_ID; } _sg.gl.cur_pass_width = w; _sg.gl.cur_pass_height = h; if (pass) { /* offscreen pass */ SOKOL_ASSERT(pass->gl_fb); glBindFramebuffer(GL_FRAMEBUFFER, pass->gl_fb); #if !defined(SOKOL_GLES2) if (!_sg.gl.gles2) { GLenum att[SG_MAX_COLOR_ATTACHMENTS] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3 }; int num_attrs = 0; for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { if (pass->color_atts[num_attrs].image) { num_attrs++; } else { break; } } glDrawBuffers(num_attrs, att); } #endif } else { /* default pass */ SOKOL_ASSERT(_sg.gl.cur_context); glBindFramebuffer(GL_FRAMEBUFFER, _sg.gl.cur_context->default_framebuffer); } glViewport(0, 0, w, h); glScissor(0, 0, w, h); bool need_pip_cache_flush = false; if (_sg.gl.cache.blend.color_write_mask != SG_COLORMASK_RGBA) { need_pip_cache_flush = true; _sg.gl.cache.blend.color_write_mask = SG_COLORMASK_RGBA; glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); } if (!_sg.gl.cache.ds.depth_write_enabled) { need_pip_cache_flush = true; _sg.gl.cache.ds.depth_write_enabled = true; glDepthMask(GL_TRUE); } if (_sg.gl.cache.ds.depth_compare_func != SG_COMPAREFUNC_ALWAYS) { need_pip_cache_flush = true; _sg.gl.cache.ds.depth_compare_func = SG_COMPAREFUNC_ALWAYS; glDepthFunc(GL_ALWAYS); } if (_sg.gl.cache.ds.stencil_write_mask != 0xFF) { need_pip_cache_flush = true; _sg.gl.cache.ds.stencil_write_mask = 0xFF; glStencilMask(0xFF); } if (need_pip_cache_flush) { /* we messed with the state cache directly, need to clear cached pipeline to force re-evaluation in next sg_apply_pipeline() */ _sg.gl.cache.cur_pipeline = 0; _sg.gl.cache.cur_pipeline_id.id = SG_INVALID_ID; } bool use_mrt_clear = (0 != pass); #if defined(SOKOL_GLES2) use_mrt_clear = false; #else if (_sg.gl.gles2) { use_mrt_clear = false; } #endif if (!use_mrt_clear) { GLbitfield clear_mask = 0; if (action->colors[0].action == SG_ACTION_CLEAR) { clear_mask |= GL_COLOR_BUFFER_BIT; const float* c = action->colors[0].val; glClearColor(c[0], c[1], c[2], c[3]); } if (action->depth.action == SG_ACTION_CLEAR) { clear_mask |= GL_DEPTH_BUFFER_BIT; #ifdef SOKOL_GLCORE33 glClearDepth(action->depth.val); #else glClearDepthf(action->depth.val); #endif } if (action->stencil.action == SG_ACTION_CLEAR) { clear_mask |= GL_STENCIL_BUFFER_BIT; glClearStencil(action->stencil.val); } if (0 != clear_mask) { glClear(clear_mask); } } #if !defined SOKOL_GLES2 else { SOKOL_ASSERT(pass); for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { if (pass->color_atts[i].image) { if (action->colors[i].action == SG_ACTION_CLEAR) { glClearBufferfv(GL_COLOR, i, action->colors[i].val); } } else { break; } } if (pass->ds_att.image) { if ((action->depth.action == SG_ACTION_CLEAR) && (action->stencil.action == SG_ACTION_CLEAR)) { glClearBufferfi(GL_DEPTH_STENCIL, 0, action->depth.val, action->stencil.val); } else if (action->depth.action == SG_ACTION_CLEAR) { glClearBufferfv(GL_DEPTH, 0, &action->depth.val); } else if (action->stencil.action == SG_ACTION_CLEAR) { GLuint val = action->stencil.val; glClearBufferuiv(GL_STENCIL, 0, &val); } } } #endif _SG_GL_CHECK_ERROR(); } _SOKOL_PRIVATE void _sg_end_pass(void) { SOKOL_ASSERT(_sg.gl.in_pass); _SG_GL_CHECK_ERROR(); /* if this was an offscreen pass, and MSAA rendering was used, need to resolve into the pass images */ #if !defined(SOKOL_GLES2) if (!_sg.gl.gles2 && _sg.gl.cur_pass) { /* check if the pass object is still valid */ const _sg_pass_t* pass = _sg.gl.cur_pass; SOKOL_ASSERT(pass->slot.id == _sg.gl.cur_pass_id.id); bool is_msaa = (0 != _sg.gl.cur_pass->color_atts[0].gl_msaa_resolve_buffer); if (is_msaa) { SOKOL_ASSERT(pass->gl_fb); glBindFramebuffer(GL_READ_FRAMEBUFFER, pass->gl_fb); SOKOL_ASSERT(pass->color_atts[0].image); const int w = pass->color_atts[0].image->width; const int h = pass->color_atts[0].image->height; for (int att_index = 0; att_index < SG_MAX_COLOR_ATTACHMENTS; att_index++) { const _sg_attachment_t* att = &pass->color_atts[att_index]; if (att->image) { SOKOL_ASSERT(att->gl_msaa_resolve_buffer); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, att->gl_msaa_resolve_buffer); glReadBuffer(GL_COLOR_ATTACHMENT0 + att_index); const GLenum gl_att = GL_COLOR_ATTACHMENT0; glDrawBuffers(1, &gl_att); glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST); } else { break; } } } } #endif _sg.gl.cur_pass = 0; _sg.gl.cur_pass_id.id = SG_INVALID_ID; _sg.gl.cur_pass_width = 0; _sg.gl.cur_pass_height = 0; SOKOL_ASSERT(_sg.gl.cur_context); glBindFramebuffer(GL_FRAMEBUFFER, _sg.gl.cur_context->default_framebuffer); _sg.gl.in_pass = false; _SG_GL_CHECK_ERROR(); } _SOKOL_PRIVATE void _sg_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { SOKOL_ASSERT(_sg.gl.in_pass); y = origin_top_left ? (_sg.gl.cur_pass_height - (y+h)) : y; glViewport(x, y, w, h); } _SOKOL_PRIVATE void _sg_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) { SOKOL_ASSERT(_sg.gl.in_pass); y = origin_top_left ? (_sg.gl.cur_pass_height - (y+h)) : y; glScissor(x, y, w, h); } _SOKOL_PRIVATE void _sg_apply_pipeline(_sg_pipeline_t* pip) { SOKOL_ASSERT(pip); SOKOL_ASSERT(pip->shader); _SG_GL_CHECK_ERROR(); if ((_sg.gl.cache.cur_pipeline != pip) || (_sg.gl.cache.cur_pipeline_id.id != pip->slot.id)) { _sg.gl.cache.cur_pipeline = pip; _sg.gl.cache.cur_pipeline_id.id = pip->slot.id; _sg.gl.cache.cur_primitive_type = _sg_gl_primitive_type(pip->primitive_type); _sg.gl.cache.cur_index_type = _sg_gl_index_type(pip->index_type); /* update depth-stencil state */ const sg_depth_stencil_state* new_ds = &pip->depth_stencil; sg_depth_stencil_state* cache_ds = &_sg.gl.cache.ds; if (new_ds->depth_compare_func != cache_ds->depth_compare_func) { cache_ds->depth_compare_func = new_ds->depth_compare_func; glDepthFunc(_sg_gl_compare_func(new_ds->depth_compare_func)); } if (new_ds->depth_write_enabled != cache_ds->depth_write_enabled) { cache_ds->depth_write_enabled = new_ds->depth_write_enabled; glDepthMask(new_ds->depth_write_enabled); } if (new_ds->stencil_enabled != cache_ds->stencil_enabled) { cache_ds->stencil_enabled = new_ds->stencil_enabled; if (new_ds->stencil_enabled) glEnable(GL_STENCIL_TEST); else glDisable(GL_STENCIL_TEST); } if (new_ds->stencil_write_mask != cache_ds->stencil_write_mask) { cache_ds->stencil_write_mask = new_ds->stencil_write_mask; glStencilMask(new_ds->stencil_write_mask); } for (int i = 0; i < 2; i++) { const sg_stencil_state* new_ss = (i==0)? &new_ds->stencil_front : &new_ds->stencil_back; sg_stencil_state* cache_ss = (i==0)? &cache_ds->stencil_front : &cache_ds->stencil_back; GLenum gl_face = (i==0)? GL_FRONT : GL_BACK; if ((new_ss->compare_func != cache_ss->compare_func) || (new_ds->stencil_read_mask != cache_ds->stencil_read_mask) || (new_ds->stencil_ref != cache_ds->stencil_ref)) { cache_ss->compare_func = new_ss->compare_func; glStencilFuncSeparate(gl_face, _sg_gl_compare_func(new_ss->compare_func), new_ds->stencil_ref, new_ds->stencil_read_mask); } if ((new_ss->fail_op != cache_ss->fail_op) || (new_ss->depth_fail_op != cache_ss->depth_fail_op) || (new_ss->pass_op != cache_ss->pass_op)) { cache_ss->fail_op = new_ss->fail_op; cache_ss->depth_fail_op = new_ss->depth_fail_op; cache_ss->pass_op = new_ss->pass_op; glStencilOpSeparate(gl_face, _sg_gl_stencil_op(new_ss->fail_op), _sg_gl_stencil_op(new_ss->depth_fail_op), _sg_gl_stencil_op(new_ss->pass_op)); } } cache_ds->stencil_read_mask = new_ds->stencil_read_mask; cache_ds->stencil_ref = new_ds->stencil_ref; /* update blend state */ const sg_blend_state* new_b = &pip->blend; sg_blend_state* cache_b = &_sg.gl.cache.blend; if (new_b->enabled != cache_b->enabled) { cache_b->enabled = new_b->enabled; if (new_b->enabled) glEnable(GL_BLEND); else glDisable(GL_BLEND); } if ((new_b->src_factor_rgb != cache_b->src_factor_rgb) || (new_b->dst_factor_rgb != cache_b->dst_factor_rgb) || (new_b->src_factor_alpha != cache_b->src_factor_alpha) || (new_b->dst_factor_alpha != cache_b->dst_factor_alpha)) { cache_b->src_factor_rgb = new_b->src_factor_rgb; cache_b->dst_factor_rgb = new_b->dst_factor_rgb; cache_b->src_factor_alpha = new_b->src_factor_alpha; cache_b->dst_factor_alpha = new_b->dst_factor_alpha; glBlendFuncSeparate(_sg_gl_blend_factor(new_b->src_factor_rgb), _sg_gl_blend_factor(new_b->dst_factor_rgb), _sg_gl_blend_factor(new_b->src_factor_alpha), _sg_gl_blend_factor(new_b->dst_factor_alpha)); } if ((new_b->op_rgb != cache_b->op_rgb) || (new_b->op_alpha != cache_b->op_alpha)) { cache_b->op_rgb = new_b->op_rgb; cache_b->op_alpha = new_b->op_alpha; glBlendEquationSeparate(_sg_gl_blend_op(new_b->op_rgb), _sg_gl_blend_op(new_b->op_alpha)); } if (new_b->color_write_mask != cache_b->color_write_mask) { cache_b->color_write_mask = new_b->color_write_mask; glColorMask((new_b->color_write_mask & SG_COLORMASK_R) != 0, (new_b->color_write_mask & SG_COLORMASK_G) != 0, (new_b->color_write_mask & SG_COLORMASK_B) != 0, (new_b->color_write_mask & SG_COLORMASK_A) != 0); } if (!_sg_fequal(new_b->blend_color[0], cache_b->blend_color[0], 0.0001f) || !_sg_fequal(new_b->blend_color[1], cache_b->blend_color[1], 0.0001f) || !_sg_fequal(new_b->blend_color[2], cache_b->blend_color[2], 0.0001f) || !_sg_fequal(new_b->blend_color[3], cache_b->blend_color[3], 0.0001f)) { const float* bc = new_b->blend_color; for (int i=0; i<4; i++) { cache_b->blend_color[i] = bc[i]; } glBlendColor(bc[0], bc[1], bc[2], bc[3]); } /* update rasterizer state */ const sg_rasterizer_state* new_r = &pip->rast; sg_rasterizer_state* cache_r = &_sg.gl.cache.rast; if (new_r->cull_mode != cache_r->cull_mode) { cache_r->cull_mode = new_r->cull_mode; if (SG_CULLMODE_NONE == new_r->cull_mode) { glDisable(GL_CULL_FACE); } else { glEnable(GL_CULL_FACE); GLenum gl_mode = (SG_CULLMODE_FRONT == new_r->cull_mode) ? GL_FRONT : GL_BACK; glCullFace(gl_mode); } } if (new_r->face_winding != cache_r->face_winding) { cache_r->face_winding = new_r->face_winding; GLenum gl_winding = (SG_FACEWINDING_CW == new_r->face_winding) ? GL_CW : GL_CCW; glFrontFace(gl_winding); } if (new_r->alpha_to_coverage_enabled != cache_r->alpha_to_coverage_enabled) { cache_r->alpha_to_coverage_enabled = new_r->alpha_to_coverage_enabled; if (new_r->alpha_to_coverage_enabled) glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE); else glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE); } #ifdef SOKOL_GLCORE33 if (new_r->sample_count != cache_r->sample_count) { cache_r->sample_count = new_r->sample_count; if (new_r->sample_count > 1) glEnable(GL_MULTISAMPLE); else glDisable(GL_MULTISAMPLE); } #endif if (!_sg_fequal(new_r->depth_bias, cache_r->depth_bias, 0.000001f) || !_sg_fequal(new_r->depth_bias_slope_scale, cache_r->depth_bias_slope_scale, 0.000001f)) { /* according to ANGLE's D3D11 backend: D3D11 SlopeScaledDepthBias ==> GL polygonOffsetFactor D3D11 DepthBias ==> GL polygonOffsetUnits DepthBiasClamp has no meaning on GL */ cache_r->depth_bias = new_r->depth_bias; cache_r->depth_bias_slope_scale = new_r->depth_bias_slope_scale; glPolygonOffset(new_r->depth_bias_slope_scale, new_r->depth_bias); bool po_enabled = true; if (_sg_fequal(new_r->depth_bias, 0.0f, 0.000001f) && _sg_fequal(new_r->depth_bias_slope_scale, 0.0f, 0.000001f)) { po_enabled = false; } if (po_enabled != _sg.gl.cache.polygon_offset_enabled) { _sg.gl.cache.polygon_offset_enabled = po_enabled; if (po_enabled) glEnable(GL_POLYGON_OFFSET_FILL); else glDisable(GL_POLYGON_OFFSET_FILL); } } /* bind shader program */ glUseProgram(pip->shader->gl_prog); } } _SOKOL_PRIVATE void _sg_apply_bindings( _sg_pipeline_t* pip, _sg_buffer_t** vbs, const int* vb_offsets, int num_vbs, _sg_buffer_t* ib, int ib_offset, _sg_image_t** vs_imgs, int num_vs_imgs, _sg_image_t** fs_imgs, int num_fs_imgs) { SOKOL_ASSERT(pip); _SOKOL_UNUSED(num_fs_imgs); _SOKOL_UNUSED(num_vs_imgs); _SOKOL_UNUSED(num_vbs); _SG_GL_CHECK_ERROR(); /* bind textures */ _SG_GL_CHECK_ERROR(); for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { const _sg_shader_stage_t* stage = &pip->shader->stage[stage_index]; _sg_image_t** imgs = (stage_index == SG_SHADERSTAGE_VS)? vs_imgs : fs_imgs; SOKOL_ASSERT(((stage_index == SG_SHADERSTAGE_VS)? num_vs_imgs : num_fs_imgs) == stage->num_images); for (int img_index = 0; img_index < stage->num_images; img_index++) { const _sg_shader_image_t* shd_img = &stage->images[img_index]; if (shd_img->gl_loc != -1) { _sg_image_t* img = imgs[img_index]; const GLuint gl_tex = img->gl_tex[img->active_slot]; SOKOL_ASSERT(img && img->gl_target); SOKOL_ASSERT((shd_img->gl_tex_slot != -1) && gl_tex); glUniform1i(shd_img->gl_loc, shd_img->gl_tex_slot); _sg_gl_bind_texture(shd_img->gl_tex_slot, img->gl_target, gl_tex); } } } _SG_GL_CHECK_ERROR(); /* index buffer (can be 0) */ const GLuint gl_ib = ib ? ib->gl_buf[ib->active_slot] : 0; _sg_gl_bind_buffer(GL_ELEMENT_ARRAY_BUFFER, gl_ib); _sg.gl.cache.cur_ib_offset = ib_offset; /* vertex attributes */ for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { _sg_gl_attr_t* attr = &pip->gl_attrs[attr_index]; _sg_gl_cache_attr_t* cache_attr = &_sg.gl.cache.attrs[attr_index]; bool cache_attr_dirty = false; int vb_offset = 0; GLuint gl_vb = 0; if (attr->vb_index >= 0) { /* attribute is enabled */ SOKOL_ASSERT(attr->vb_index < num_vbs); _sg_buffer_t* vb = vbs[attr->vb_index]; SOKOL_ASSERT(vb); gl_vb = vb->gl_buf[vb->active_slot]; vb_offset = vb_offsets[attr->vb_index] + attr->offset; if ((gl_vb != cache_attr->gl_vbuf) || (attr->size != cache_attr->gl_attr.size) || (attr->type != cache_attr->gl_attr.type) || (attr->normalized != cache_attr->gl_attr.normalized) || (attr->stride != cache_attr->gl_attr.stride) || (vb_offset != cache_attr->gl_attr.offset) || (cache_attr->gl_attr.divisor != attr->divisor)) { _sg_gl_bind_buffer(GL_ARRAY_BUFFER, gl_vb); glVertexAttribPointer(attr_index, attr->size, attr->type, attr->normalized, attr->stride, (const GLvoid*)(GLintptr)vb_offset); #ifdef SOKOL_INSTANCING_ENABLED if (_sg.gl.features[SG_FEATURE_INSTANCING]) { glVertexAttribDivisor(attr_index, attr->divisor); } #endif cache_attr_dirty = true; } if (cache_attr->gl_attr.vb_index == -1) { glEnableVertexAttribArray(attr_index); cache_attr_dirty = true; } } else { /* attribute is disabled */ if (cache_attr->gl_attr.vb_index != -1) { glDisableVertexAttribArray(attr_index); cache_attr_dirty = true; } } if (cache_attr_dirty) { cache_attr->gl_attr = *attr; cache_attr->gl_attr.offset = vb_offset; cache_attr->gl_vbuf = gl_vb; } } _SG_GL_CHECK_ERROR(); } _SOKOL_PRIVATE void _sg_apply_uniforms(sg_shader_stage stage_index, int ub_index, const void* data, int num_bytes) { _SOKOL_UNUSED(num_bytes); SOKOL_ASSERT(data && (num_bytes > 0)); SOKOL_ASSERT((stage_index >= 0) && ((int)stage_index < SG_NUM_SHADER_STAGES)); SOKOL_ASSERT(_sg.gl.cache.cur_pipeline); SOKOL_ASSERT(_sg.gl.cache.cur_pipeline->slot.id == _sg.gl.cache.cur_pipeline_id.id); SOKOL_ASSERT(_sg.gl.cache.cur_pipeline->shader->slot.id == _sg.gl.cache.cur_pipeline->shader_id.id); _sg_shader_stage_t* stage = &_sg.gl.cache.cur_pipeline->shader->stage[stage_index]; SOKOL_ASSERT(ub_index < stage->num_uniform_blocks); _sg_uniform_block_t* ub = &stage->uniform_blocks[ub_index]; SOKOL_ASSERT(ub->size == num_bytes); for (int u_index = 0; u_index < ub->num_uniforms; u_index++) { _sg_uniform_t* u = &ub->uniforms[u_index]; SOKOL_ASSERT(u->type != SG_UNIFORMTYPE_INVALID); if (u->gl_loc == -1) { continue; } GLfloat* ptr = (GLfloat*) (((uint8_t*)data) + u->offset); switch (u->type) { case SG_UNIFORMTYPE_INVALID: break; case SG_UNIFORMTYPE_FLOAT: glUniform1fv(u->gl_loc, u->count, ptr); break; case SG_UNIFORMTYPE_FLOAT2: glUniform2fv(u->gl_loc, u->count, ptr); break; case SG_UNIFORMTYPE_FLOAT3: glUniform3fv(u->gl_loc, u->count, ptr); break; case SG_UNIFORMTYPE_FLOAT4: glUniform4fv(u->gl_loc, u->count, ptr); break; case SG_UNIFORMTYPE_MAT4: glUniformMatrix4fv(u->gl_loc, u->count, GL_FALSE, ptr); break; default: SOKOL_UNREACHABLE; break; } } } _SOKOL_PRIVATE void _sg_draw(int base_element, int num_elements, int num_instances) { const GLenum i_type = _sg.gl.cache.cur_index_type; const GLenum p_type = _sg.gl.cache.cur_primitive_type; if (0 != i_type) { /* indexed rendering */ const int i_size = (i_type == GL_UNSIGNED_SHORT) ? 2 : 4; const int ib_offset = _sg.gl.cache.cur_ib_offset; const GLvoid* indices = (const GLvoid*)(GLintptr)(base_element*i_size+ib_offset); if (num_instances == 1) { glDrawElements(p_type, num_elements, i_type, indices); } else { if (_sg.gl.features[SG_FEATURE_INSTANCING]) { glDrawElementsInstanced(p_type, num_elements, i_type, indices, num_instances); } } } else { /* non-indexed rendering */ if (num_instances == 1) { glDrawArrays(p_type, base_element, num_elements); } else { if (_sg.gl.features[SG_FEATURE_INSTANCING]) { glDrawArraysInstanced(p_type, base_element, num_elements, num_instances); } } } } _SOKOL_PRIVATE void _sg_commit(void) { SOKOL_ASSERT(!_sg.gl.in_pass); /* "soft" clear bindings (only those that are actually bound) */ _sg_gl_clear_buffer_bindings(false); _sg_gl_clear_texture_bindings(false); } _SOKOL_PRIVATE void _sg_update_buffer(_sg_buffer_t* buf, const void* data_ptr, int data_size) { SOKOL_ASSERT(buf && data_ptr && (data_size > 0)); /* only one update per buffer per frame allowed */ if (++buf->active_slot >= buf->num_slots) { buf->active_slot = 0; } GLenum gl_tgt = _sg_gl_buffer_target(buf->type); SOKOL_ASSERT(buf->active_slot < SG_NUM_INFLIGHT_FRAMES); GLuint gl_buf = buf->gl_buf[buf->active_slot]; SOKOL_ASSERT(gl_buf); _SG_GL_CHECK_ERROR(); _sg_gl_store_buffer_binding(gl_tgt); _sg_gl_bind_buffer(gl_tgt, gl_buf); glBufferSubData(gl_tgt, 0, data_size, data_ptr); _sg_gl_restore_buffer_binding(gl_tgt); _SG_GL_CHECK_ERROR(); } _SOKOL_PRIVATE void _sg_append_buffer(_sg_buffer_t* buf, const void* data_ptr, int data_size, bool new_frame) { SOKOL_ASSERT(buf && data_ptr && (data_size > 0)); if (new_frame) { if (++buf->active_slot >= buf->num_slots) { buf->active_slot = 0; } } GLenum gl_tgt = _sg_gl_buffer_target(buf->type); SOKOL_ASSERT(buf->active_slot < SG_NUM_INFLIGHT_FRAMES); GLuint gl_buf = buf->gl_buf[buf->active_slot]; SOKOL_ASSERT(gl_buf); _SG_GL_CHECK_ERROR(); _sg_gl_store_buffer_binding(gl_tgt); _sg_gl_bind_buffer(gl_tgt, gl_buf); glBufferSubData(gl_tgt, buf->append_pos, data_size, data_ptr); _sg_gl_restore_buffer_binding(gl_tgt); _SG_GL_CHECK_ERROR(); } _SOKOL_PRIVATE void _sg_update_image(_sg_image_t* img, const sg_image_content* data) { SOKOL_ASSERT(img && data); /* only one update per image per frame allowed */ if (++img->active_slot >= img->num_slots) { img->active_slot = 0; } SOKOL_ASSERT(img->active_slot < SG_NUM_INFLIGHT_FRAMES); SOKOL_ASSERT(0 != img->gl_tex[img->active_slot]); _sg_gl_store_texture_binding(0); _sg_gl_bind_texture(0, img->gl_target, img->gl_tex[img->active_slot]); const GLenum gl_img_format = _sg_gl_teximage_format(img->pixel_format); const GLenum gl_img_type = _sg_gl_teximage_type(img->pixel_format); const int num_faces = img->type == SG_IMAGETYPE_CUBE ? 6 : 1; const int num_mips = img->num_mipmaps; for (int face_index = 0; face_index < num_faces; face_index++) { for (int mip_index = 0; mip_index < num_mips; mip_index++) { GLenum gl_img_target = img->gl_target; if (SG_IMAGETYPE_CUBE == img->type) { gl_img_target = _sg_gl_cubeface_target(face_index); } const GLvoid* data_ptr = data->subimage[face_index][mip_index].ptr; int mip_width = img->width >> mip_index; if (mip_width == 0) { mip_width = 1; } int mip_height = img->height >> mip_index; if (mip_height == 0) { mip_height = 1; } if ((SG_IMAGETYPE_2D == img->type) || (SG_IMAGETYPE_CUBE == img->type)) { glTexSubImage2D(gl_img_target, mip_index, 0, 0, mip_width, mip_height, gl_img_format, gl_img_type, data_ptr); } #if !defined(SOKOL_GLES2) else if (!_sg.gl.gles2 && ((SG_IMAGETYPE_3D == img->type) || (SG_IMAGETYPE_ARRAY == img->type))) { int mip_depth = img->depth >> mip_index; if (mip_depth == 0) { mip_depth = 1; } glTexSubImage3D(gl_img_target, mip_index, 0, 0, 0, mip_width, mip_height, mip_depth, gl_img_format, gl_img_type, data_ptr); } #endif } } _sg_gl_restore_texture_binding(0); } /*== D3D11 BACKEND IMPLEMENTATION ============================================*/ #elif defined(SOKOL_D3D11) /*-- enum translation functions ----------------------------------------------*/ _SOKOL_PRIVATE D3D11_USAGE _sg_d3d11_usage(sg_usage usg) { switch (usg) { case SG_USAGE_IMMUTABLE: return D3D11_USAGE_IMMUTABLE; case SG_USAGE_DYNAMIC: case SG_USAGE_STREAM: return D3D11_USAGE_DYNAMIC; default: SOKOL_UNREACHABLE; return (D3D11_USAGE) 0; } } _SOKOL_PRIVATE UINT _sg_d3d11_cpu_access_flags(sg_usage usg) { switch (usg) { case SG_USAGE_IMMUTABLE: return 0; case SG_USAGE_DYNAMIC: case SG_USAGE_STREAM: return D3D11_CPU_ACCESS_WRITE; default: SOKOL_UNREACHABLE; return 0; } } _SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_texture_format(sg_pixel_format fmt) { /* NOTE: the following pixel formats are only supported on D3D11.1 (we're running on D3D11.0): DXGI_FORMAT_B4G4R4A4_UNORM DXGI_FORMAT_B5G6R5_UNORM DXGI_FORMAT_B5G5R5A1_UNORM */ switch (fmt) { case SG_PIXELFORMAT_RGBA8: return DXGI_FORMAT_R8G8B8A8_UNORM; case SG_PIXELFORMAT_R10G10B10A2: return DXGI_FORMAT_R10G10B10A2_UNORM; case SG_PIXELFORMAT_RGBA32F: return DXGI_FORMAT_R32G32B32A32_FLOAT; case SG_PIXELFORMAT_RGBA16F: return DXGI_FORMAT_R16G16B16A16_FLOAT; case SG_PIXELFORMAT_R32F: return DXGI_FORMAT_R32_FLOAT; case SG_PIXELFORMAT_R16F: return DXGI_FORMAT_R16_FLOAT; case SG_PIXELFORMAT_L8: return DXGI_FORMAT_R8_UNORM; case SG_PIXELFORMAT_DXT1: return DXGI_FORMAT_BC1_UNORM; case SG_PIXELFORMAT_DXT3: return DXGI_FORMAT_BC2_UNORM; case SG_PIXELFORMAT_DXT5: return DXGI_FORMAT_BC3_UNORM; default: return DXGI_FORMAT_UNKNOWN; }; } _SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_rendertarget_color_format(sg_pixel_format fmt) { switch (fmt) { case SG_PIXELFORMAT_RGBA8: return DXGI_FORMAT_R8G8B8A8_UNORM; case SG_PIXELFORMAT_RGBA32F: return DXGI_FORMAT_R32G32B32A32_FLOAT; case SG_PIXELFORMAT_RGBA16F: return DXGI_FORMAT_R16G16B16A16_FLOAT; case SG_PIXELFORMAT_R32F: return DXGI_FORMAT_R32_FLOAT; case SG_PIXELFORMAT_R16F: return DXGI_FORMAT_R16_FLOAT; case SG_PIXELFORMAT_L8: return DXGI_FORMAT_R8_UNORM; default: return DXGI_FORMAT_UNKNOWN; } } _SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_rendertarget_depth_format(sg_pixel_format fmt) { switch (fmt) { case SG_PIXELFORMAT_DEPTH: return DXGI_FORMAT_D16_UNORM; case SG_PIXELFORMAT_DEPTHSTENCIL: return DXGI_FORMAT_D24_UNORM_S8_UINT; default: return DXGI_FORMAT_UNKNOWN; } } _SOKOL_PRIVATE D3D11_PRIMITIVE_TOPOLOGY _sg_d3d11_primitive_topology(sg_primitive_type prim_type) { switch (prim_type) { case SG_PRIMITIVETYPE_POINTS: return D3D11_PRIMITIVE_TOPOLOGY_POINTLIST; case SG_PRIMITIVETYPE_LINES: return D3D11_PRIMITIVE_TOPOLOGY_LINELIST; case SG_PRIMITIVETYPE_LINE_STRIP: return D3D11_PRIMITIVE_TOPOLOGY_LINESTRIP; case SG_PRIMITIVETYPE_TRIANGLES: return D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST; case SG_PRIMITIVETYPE_TRIANGLE_STRIP: return D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP; default: SOKOL_UNREACHABLE; return (D3D11_PRIMITIVE_TOPOLOGY) 0; } } _SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_index_format(sg_index_type index_type) { switch (index_type) { case SG_INDEXTYPE_NONE: return DXGI_FORMAT_UNKNOWN; case SG_INDEXTYPE_UINT16: return DXGI_FORMAT_R16_UINT; case SG_INDEXTYPE_UINT32: return DXGI_FORMAT_R32_UINT; default: SOKOL_UNREACHABLE; return (DXGI_FORMAT) 0; } } _SOKOL_PRIVATE D3D11_FILTER _sg_d3d11_filter(sg_filter min_f, sg_filter mag_f, uint32_t max_anisotropy) { if (max_anisotropy > 1) { return D3D11_FILTER_ANISOTROPIC; } else if (mag_f == SG_FILTER_NEAREST) { switch (min_f) { case SG_FILTER_NEAREST: case SG_FILTER_NEAREST_MIPMAP_NEAREST: return D3D11_FILTER_MIN_MAG_MIP_POINT; case SG_FILTER_LINEAR: case SG_FILTER_LINEAR_MIPMAP_NEAREST: return D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT; case SG_FILTER_NEAREST_MIPMAP_LINEAR: return D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR; case SG_FILTER_LINEAR_MIPMAP_LINEAR: return D3D11_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR; default: SOKOL_UNREACHABLE; break; } } else if (mag_f == SG_FILTER_LINEAR) { switch (min_f) { case SG_FILTER_NEAREST: case SG_FILTER_NEAREST_MIPMAP_NEAREST: return D3D11_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT; case SG_FILTER_LINEAR: case SG_FILTER_LINEAR_MIPMAP_NEAREST: return D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT; case SG_FILTER_NEAREST_MIPMAP_LINEAR: return D3D11_FILTER_MIN_POINT_MAG_MIP_LINEAR; case SG_FILTER_LINEAR_MIPMAP_LINEAR: return D3D11_FILTER_MIN_MAG_MIP_LINEAR; default: SOKOL_UNREACHABLE; break; } } /* invalid value for mag filter */ SOKOL_UNREACHABLE; return D3D11_FILTER_MIN_MAG_MIP_POINT; } _SOKOL_PRIVATE D3D11_TEXTURE_ADDRESS_MODE _sg_d3d11_address_mode(sg_wrap m) { switch (m) { case SG_WRAP_REPEAT: return D3D11_TEXTURE_ADDRESS_WRAP; case SG_WRAP_CLAMP_TO_EDGE: return D3D11_TEXTURE_ADDRESS_CLAMP; case SG_WRAP_MIRRORED_REPEAT: return D3D11_TEXTURE_ADDRESS_MIRROR; default: SOKOL_UNREACHABLE; return (D3D11_TEXTURE_ADDRESS_MODE) 0; } } _SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_vertex_format(sg_vertex_format fmt) { switch (fmt) { case SG_VERTEXFORMAT_FLOAT: return DXGI_FORMAT_R32_FLOAT; case SG_VERTEXFORMAT_FLOAT2: return DXGI_FORMAT_R32G32_FLOAT; case SG_VERTEXFORMAT_FLOAT3: return DXGI_FORMAT_R32G32B32_FLOAT; case SG_VERTEXFORMAT_FLOAT4: return DXGI_FORMAT_R32G32B32A32_FLOAT; case SG_VERTEXFORMAT_BYTE4: return DXGI_FORMAT_R8G8B8A8_SINT; case SG_VERTEXFORMAT_BYTE4N: return DXGI_FORMAT_R8G8B8A8_SNORM; case SG_VERTEXFORMAT_UBYTE4: return DXGI_FORMAT_R8G8B8A8_UINT; case SG_VERTEXFORMAT_UBYTE4N: return DXGI_FORMAT_R8G8B8A8_UNORM; case SG_VERTEXFORMAT_SHORT2: return DXGI_FORMAT_R16G16_SINT; case SG_VERTEXFORMAT_SHORT2N: return DXGI_FORMAT_R16G16_SNORM; case SG_VERTEXFORMAT_SHORT4: return DXGI_FORMAT_R16G16B16A16_SINT; case SG_VERTEXFORMAT_SHORT4N: return DXGI_FORMAT_R16G16B16A16_SNORM; case SG_VERTEXFORMAT_UINT10_N2: return DXGI_FORMAT_R10G10B10A2_UNORM; default: SOKOL_UNREACHABLE; return (DXGI_FORMAT) 0; } } _SOKOL_PRIVATE D3D11_INPUT_CLASSIFICATION _sg_d3d11_input_classification(sg_vertex_step step) { switch (step) { case SG_VERTEXSTEP_PER_VERTEX: return D3D11_INPUT_PER_VERTEX_DATA; case SG_VERTEXSTEP_PER_INSTANCE: return D3D11_INPUT_PER_INSTANCE_DATA; default: SOKOL_UNREACHABLE; return (D3D11_INPUT_CLASSIFICATION) 0; } } _SOKOL_PRIVATE D3D11_CULL_MODE _sg_d3d11_cull_mode(sg_cull_mode m) { switch (m) { case SG_CULLMODE_NONE: return D3D11_CULL_NONE; case SG_CULLMODE_FRONT: return D3D11_CULL_FRONT; case SG_CULLMODE_BACK: return D3D11_CULL_BACK; default: SOKOL_UNREACHABLE; return (D3D11_CULL_MODE) 0; } } _SOKOL_PRIVATE D3D11_COMPARISON_FUNC _sg_d3d11_compare_func(sg_compare_func f) { switch (f) { case SG_COMPAREFUNC_NEVER: return D3D11_COMPARISON_NEVER; case SG_COMPAREFUNC_LESS: return D3D11_COMPARISON_LESS; case SG_COMPAREFUNC_EQUAL: return D3D11_COMPARISON_EQUAL; case SG_COMPAREFUNC_LESS_EQUAL: return D3D11_COMPARISON_LESS_EQUAL; case SG_COMPAREFUNC_GREATER: return D3D11_COMPARISON_GREATER; case SG_COMPAREFUNC_NOT_EQUAL: return D3D11_COMPARISON_NOT_EQUAL; case SG_COMPAREFUNC_GREATER_EQUAL: return D3D11_COMPARISON_GREATER_EQUAL; case SG_COMPAREFUNC_ALWAYS: return D3D11_COMPARISON_ALWAYS; default: SOKOL_UNREACHABLE; return (D3D11_COMPARISON_FUNC) 0; } } _SOKOL_PRIVATE D3D11_STENCIL_OP _sg_d3d11_stencil_op(sg_stencil_op op) { switch (op) { case SG_STENCILOP_KEEP: return D3D11_STENCIL_OP_KEEP; case SG_STENCILOP_ZERO: return D3D11_STENCIL_OP_ZERO; case SG_STENCILOP_REPLACE: return D3D11_STENCIL_OP_REPLACE; case SG_STENCILOP_INCR_CLAMP: return D3D11_STENCIL_OP_INCR_SAT; case SG_STENCILOP_DECR_CLAMP: return D3D11_STENCIL_OP_DECR_SAT; case SG_STENCILOP_INVERT: return D3D11_STENCIL_OP_INVERT; case SG_STENCILOP_INCR_WRAP: return D3D11_STENCIL_OP_INCR; case SG_STENCILOP_DECR_WRAP: return D3D11_STENCIL_OP_DECR; default: SOKOL_UNREACHABLE; return (D3D11_STENCIL_OP) 0; } } _SOKOL_PRIVATE D3D11_BLEND _sg_d3d11_blend_factor(sg_blend_factor f) { switch (f) { case SG_BLENDFACTOR_ZERO: return D3D11_BLEND_ZERO; case SG_BLENDFACTOR_ONE: return D3D11_BLEND_ONE; case SG_BLENDFACTOR_SRC_COLOR: return D3D11_BLEND_SRC_COLOR; case SG_BLENDFACTOR_ONE_MINUS_SRC_COLOR: return D3D11_BLEND_INV_SRC_COLOR; case SG_BLENDFACTOR_SRC_ALPHA: return D3D11_BLEND_SRC_ALPHA; case SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA: return D3D11_BLEND_INV_SRC_ALPHA; case SG_BLENDFACTOR_DST_COLOR: return D3D11_BLEND_DEST_COLOR; case SG_BLENDFACTOR_ONE_MINUS_DST_COLOR: return D3D11_BLEND_INV_DEST_COLOR; case SG_BLENDFACTOR_DST_ALPHA: return D3D11_BLEND_DEST_ALPHA; case SG_BLENDFACTOR_ONE_MINUS_DST_ALPHA: return D3D11_BLEND_INV_DEST_ALPHA; case SG_BLENDFACTOR_SRC_ALPHA_SATURATED: return D3D11_BLEND_SRC_ALPHA_SAT; case SG_BLENDFACTOR_BLEND_COLOR: return D3D11_BLEND_BLEND_FACTOR; case SG_BLENDFACTOR_ONE_MINUS_BLEND_COLOR: return D3D11_BLEND_INV_BLEND_FACTOR; case SG_BLENDFACTOR_BLEND_ALPHA: return D3D11_BLEND_BLEND_FACTOR; case SG_BLENDFACTOR_ONE_MINUS_BLEND_ALPHA: return D3D11_BLEND_INV_BLEND_FACTOR; default: SOKOL_UNREACHABLE; return (D3D11_BLEND) 0; } } _SOKOL_PRIVATE D3D11_BLEND_OP _sg_d3d11_blend_op(sg_blend_op op) { switch (op) { case SG_BLENDOP_ADD: return D3D11_BLEND_OP_ADD; case SG_BLENDOP_SUBTRACT: return D3D11_BLEND_OP_SUBTRACT; case SG_BLENDOP_REVERSE_SUBTRACT: return D3D11_BLEND_OP_REV_SUBTRACT; default: SOKOL_UNREACHABLE; return (D3D11_BLEND_OP) 0; } } _SOKOL_PRIVATE UINT8 _sg_d3d11_color_write_mask(sg_color_mask m) { UINT8 res = 0; if (m & SG_COLORMASK_R) { res |= D3D11_COLOR_WRITE_ENABLE_RED; } if (m & SG_COLORMASK_G) { res |= D3D11_COLOR_WRITE_ENABLE_GREEN; } if (m & SG_COLORMASK_B) { res |= D3D11_COLOR_WRITE_ENABLE_BLUE; } if (m & SG_COLORMASK_A) { res |= D3D11_COLOR_WRITE_ENABLE_ALPHA; } return res; } _SOKOL_PRIVATE void _sg_setup_backend(const sg_desc* desc) { /* assume _sg.d3d11 already is zero-initialized */ SOKOL_ASSERT(desc); SOKOL_ASSERT(desc->d3d11_device); SOKOL_ASSERT(desc->d3d11_device_context); SOKOL_ASSERT(desc->d3d11_render_target_view_cb); SOKOL_ASSERT(desc->d3d11_depth_stencil_view_cb); SOKOL_ASSERT(desc->d3d11_render_target_view_cb != desc->d3d11_depth_stencil_view_cb); _sg.d3d11.valid = true; _sg.d3d11.dev = (ID3D11Device*) desc->d3d11_device; _sg.d3d11.ctx = (ID3D11DeviceContext*) desc->d3d11_device_context; _sg.d3d11.rtv_cb = desc->d3d11_render_target_view_cb; _sg.d3d11.dsv_cb = desc->d3d11_depth_stencil_view_cb; } _SOKOL_PRIVATE void _sg_discard_backend(void) { SOKOL_ASSERT(_sg.d3d11.valid); _sg.d3d11.valid = false; } _SOKOL_PRIVATE bool _sg_query_feature(sg_feature f) { switch (f) { case SG_FEATURE_INSTANCING: case SG_FEATURE_TEXTURE_COMPRESSION_DXT: case SG_FEATURE_TEXTURE_FLOAT: case SG_FEATURE_TEXTURE_HALF_FLOAT: case SG_FEATURE_ORIGIN_TOP_LEFT: case SG_FEATURE_MSAA_RENDER_TARGETS: case SG_FEATURE_MULTIPLE_RENDER_TARGET: case SG_FEATURE_IMAGETYPE_3D: case SG_FEATURE_IMAGETYPE_ARRAY: case SG_FEATURE_PACKED_VERTEX_FORMAT_10_2: return true; default: return false; } } _SOKOL_PRIVATE void _sg_d3d11_clear_state(void) { /* clear all the device context state, so that resource refs don't keep stuck in the d3d device context */ ID3D11DeviceContext_OMSetRenderTargets(_sg.d3d11.ctx, SG_MAX_COLOR_ATTACHMENTS, _sg.d3d11.zero_rtvs, NULL); ID3D11DeviceContext_RSSetState(_sg.d3d11.ctx, NULL); ID3D11DeviceContext_OMSetDepthStencilState(_sg.d3d11.ctx, NULL, 0); ID3D11DeviceContext_OMSetBlendState(_sg.d3d11.ctx, NULL, NULL, 0xFFFFFFFF); ID3D11DeviceContext_IASetVertexBuffers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_BUFFERS, _sg.d3d11.zero_vbs, _sg.d3d11.zero_vb_strides, _sg.d3d11.zero_vb_offsets); ID3D11DeviceContext_IASetIndexBuffer(_sg.d3d11.ctx, NULL, DXGI_FORMAT_UNKNOWN, 0); ID3D11DeviceContext_IASetInputLayout(_sg.d3d11.ctx, NULL); ID3D11DeviceContext_VSSetShader(_sg.d3d11.ctx, NULL, NULL, 0); ID3D11DeviceContext_PSSetShader(_sg.d3d11.ctx, NULL, NULL, 0); ID3D11DeviceContext_VSSetConstantBuffers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_UBS, _sg.d3d11.zero_cbs); ID3D11DeviceContext_PSSetConstantBuffers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_UBS, _sg.d3d11.zero_cbs); ID3D11DeviceContext_VSSetShaderResources(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_IMAGES, _sg.d3d11.zero_srvs); ID3D11DeviceContext_PSSetShaderResources(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_IMAGES, _sg.d3d11.zero_srvs); ID3D11DeviceContext_VSSetSamplers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_IMAGES, _sg.d3d11.zero_smps); ID3D11DeviceContext_PSSetSamplers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_IMAGES, _sg.d3d11.zero_smps); } _SOKOL_PRIVATE void _sg_reset_state_cache(void) { /* just clear the d3d11 device context state */ _sg_d3d11_clear_state(); } _SOKOL_PRIVATE void _sg_activate_context(_sg_context_t* ctx) { _SOKOL_UNUSED(ctx); _sg_reset_state_cache(); } _SOKOL_PRIVATE sg_resource_state _sg_create_context(_sg_context_t* ctx) { SOKOL_ASSERT(ctx); _SOKOL_UNUSED(ctx); return SG_RESOURCESTATE_VALID; } _SOKOL_PRIVATE void _sg_destroy_context(_sg_context_t* ctx) { SOKOL_ASSERT(ctx); _SOKOL_UNUSED(ctx); /* empty */ } _SOKOL_PRIVATE sg_resource_state _sg_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { SOKOL_ASSERT(buf && desc); SOKOL_ASSERT(!buf->d3d11_buf); buf->size = desc->size; buf->append_pos = 0; buf->append_overflow = false; buf->type = desc->type; buf->usage = desc->usage; buf->update_frame_index = 0; buf->append_frame_index = 0; const bool injected = (0 != desc->d3d11_buffer); if (injected) { buf->d3d11_buf = (ID3D11Buffer*) desc->d3d11_buffer; ID3D11Buffer_AddRef(buf->d3d11_buf); } else { D3D11_BUFFER_DESC d3d11_desc; memset(&d3d11_desc, 0, sizeof(d3d11_desc)); d3d11_desc.ByteWidth = buf->size; d3d11_desc.Usage = _sg_d3d11_usage(buf->usage); d3d11_desc.BindFlags = buf->type == SG_BUFFERTYPE_VERTEXBUFFER ? D3D11_BIND_VERTEX_BUFFER : D3D11_BIND_INDEX_BUFFER; d3d11_desc.CPUAccessFlags = _sg_d3d11_cpu_access_flags(buf->usage); D3D11_SUBRESOURCE_DATA* init_data_ptr = 0; D3D11_SUBRESOURCE_DATA init_data; memset(&init_data, 0, sizeof(init_data)); if (buf->usage == SG_USAGE_IMMUTABLE) { SOKOL_ASSERT(desc->content); init_data.pSysMem = desc->content; init_data_ptr = &init_data; } HRESULT hr = ID3D11Device_CreateBuffer(_sg.d3d11.dev, &d3d11_desc, init_data_ptr, &buf->d3d11_buf); _SOKOL_UNUSED(hr); SOKOL_ASSERT(SUCCEEDED(hr) && buf->d3d11_buf); } return SG_RESOURCESTATE_VALID; } _SOKOL_PRIVATE void _sg_destroy_buffer(_sg_buffer_t* buf) { SOKOL_ASSERT(buf); if (buf->d3d11_buf) { ID3D11Buffer_Release(buf->d3d11_buf); } } _SOKOL_PRIVATE void _sg_d3d11_fill_subres_data(const _sg_image_t* img, const sg_image_content* content) { const int num_faces = (img->type == SG_IMAGETYPE_CUBE) ? 6:1; const int num_slices = (img->type == SG_IMAGETYPE_ARRAY) ? img->depth:1; int subres_index = 0; for (int face_index = 0; face_index < num_faces; face_index++) { for (int slice_index = 0; slice_index < num_slices; slice_index++) { for (int mip_index = 0; mip_index < img->num_mipmaps; mip_index++, subres_index++) { SOKOL_ASSERT(subres_index < (SG_MAX_MIPMAPS * SG_MAX_TEXTUREARRAY_LAYERS)); D3D11_SUBRESOURCE_DATA* subres_data = &_sg.d3d11.subres_data[subres_index]; const int mip_width = ((img->width>>mip_index)>0) ? img->width>>mip_index : 1; const int mip_height = ((img->height>>mip_index)>0) ? img->height>>mip_index : 1; const sg_subimage_content* subimg_content = &(content->subimage[face_index][mip_index]); const int slice_size = subimg_content->size / num_slices; const int slice_offset = slice_size * slice_index; const uint8_t* ptr = (const uint8_t*) subimg_content->ptr; subres_data->pSysMem = ptr + slice_offset; subres_data->SysMemPitch = _sg_row_pitch(img->pixel_format, mip_width); if (img->type == SG_IMAGETYPE_3D) { /* FIXME? const int mip_depth = ((img->depth>>mip_index)>0) ? img->depth>>mip_index : 1; */ subres_data->SysMemSlicePitch = _sg_surface_pitch(img->pixel_format, mip_width, mip_height); } else { subres_data->SysMemSlicePitch = 0; } } } } } _SOKOL_PRIVATE sg_resource_state _sg_create_image(_sg_image_t* img, const sg_image_desc* desc) { SOKOL_ASSERT(img && desc); SOKOL_ASSERT(!img->d3d11_tex2d && !img->d3d11_tex3d && !img->d3d11_texds && !img->d3d11_texmsaa); SOKOL_ASSERT(!img->d3d11_srv && !img->d3d11_smp); HRESULT hr; img->type = desc->type; img->render_target = desc->render_target; img->width = desc->width; img->height = desc->height; img->depth = desc->depth; img->num_mipmaps = desc->num_mipmaps; img->usage = desc->usage; img->pixel_format = desc->pixel_format; img->sample_count = desc->sample_count; img->min_filter = desc->min_filter; img->mag_filter = desc->mag_filter; img->wrap_u = desc->wrap_u; img->wrap_v = desc->wrap_v; img->wrap_w = desc->wrap_w; img->max_anisotropy = desc->max_anisotropy; img->upd_frame_index = 0; const bool injected = (0 != desc->d3d11_texture); const bool msaa = (img->sample_count > 1); /* special case depth-stencil buffer? */ if (_sg_is_valid_rendertarget_depth_format(img->pixel_format)) { /* create only a depth-texture */ SOKOL_ASSERT(!injected); img->d3d11_format = _sg_d3d11_rendertarget_depth_format(img->pixel_format); if (img->d3d11_format == DXGI_FORMAT_UNKNOWN) { /* trying to create a texture format that's not supported by D3D */ SOKOL_LOG("trying to create a D3D11 depth-texture with unsupported pixel format\n"); return SG_RESOURCESTATE_FAILED; } D3D11_TEXTURE2D_DESC d3d11_desc; memset(&d3d11_desc, 0, sizeof(d3d11_desc)); d3d11_desc.Width = img->width; d3d11_desc.Height = img->height; d3d11_desc.MipLevels = 1; d3d11_desc.ArraySize = 1; d3d11_desc.Format = img->d3d11_format; d3d11_desc.Usage = D3D11_USAGE_DEFAULT; d3d11_desc.BindFlags = D3D11_BIND_DEPTH_STENCIL; d3d11_desc.SampleDesc.Count = img->sample_count; d3d11_desc.SampleDesc.Quality = msaa ? D3D11_STANDARD_MULTISAMPLE_PATTERN : 0; hr = ID3D11Device_CreateTexture2D(_sg.d3d11.dev, &d3d11_desc, NULL, &img->d3d11_texds); SOKOL_ASSERT(SUCCEEDED(hr) && img->d3d11_texds); } else { /* create (or inject) color texture */ /* prepare initial content pointers */ D3D11_SUBRESOURCE_DATA* init_data = 0; if (!injected && (img->usage == SG_USAGE_IMMUTABLE) && !img->render_target) { _sg_d3d11_fill_subres_data(img, &desc->content); init_data = _sg.d3d11.subres_data; } if (img->type != SG_IMAGETYPE_3D) { /* 2D-, cube- or array-texture */ /* if this is an MSAA render target, the following texture will be the 'resolve-texture' */ D3D11_TEXTURE2D_DESC d3d11_tex_desc; memset(&d3d11_tex_desc, 0, sizeof(d3d11_tex_desc)); d3d11_tex_desc.Width = img->width; d3d11_tex_desc.Height = img->height; d3d11_tex_desc.MipLevels = img->num_mipmaps; switch (img->type) { case SG_IMAGETYPE_ARRAY: d3d11_tex_desc.ArraySize = img->depth; break; case SG_IMAGETYPE_CUBE: d3d11_tex_desc.ArraySize = 6; break; default: d3d11_tex_desc.ArraySize = 1; break; } d3d11_tex_desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; if (img->render_target) { img->d3d11_format = _sg_d3d11_rendertarget_color_format(img->pixel_format); d3d11_tex_desc.Format = img->d3d11_format; d3d11_tex_desc.Usage = D3D11_USAGE_DEFAULT; if (!msaa) { d3d11_tex_desc.BindFlags |= D3D11_BIND_RENDER_TARGET; } d3d11_tex_desc.CPUAccessFlags = 0; } else { img->d3d11_format = _sg_d3d11_texture_format(img->pixel_format); d3d11_tex_desc.Format = img->d3d11_format; d3d11_tex_desc.Usage = _sg_d3d11_usage(img->usage); d3d11_tex_desc.CPUAccessFlags = _sg_d3d11_cpu_access_flags(img->usage); } if (img->d3d11_format == DXGI_FORMAT_UNKNOWN) { /* trying to create a texture format that's not supported by D3D */ SOKOL_LOG("trying to create a D3D11 texture with unsupported pixel format\n"); return SG_RESOURCESTATE_FAILED; } d3d11_tex_desc.SampleDesc.Count = 1; d3d11_tex_desc.SampleDesc.Quality = 0; d3d11_tex_desc.MiscFlags = (img->type == SG_IMAGETYPE_CUBE) ? D3D11_RESOURCE_MISC_TEXTURECUBE : 0; if (injected) { img->d3d11_tex2d = (ID3D11Texture2D*) desc->d3d11_texture; ID3D11Texture2D_AddRef(img->d3d11_tex2d); } else { hr = ID3D11Device_CreateTexture2D(_sg.d3d11.dev, &d3d11_tex_desc, init_data, &img->d3d11_tex2d); SOKOL_ASSERT(SUCCEEDED(hr) && img->d3d11_tex2d); } /* shader-resource-view */ D3D11_SHADER_RESOURCE_VIEW_DESC d3d11_srv_desc; memset(&d3d11_srv_desc, 0, sizeof(d3d11_srv_desc)); d3d11_srv_desc.Format = d3d11_tex_desc.Format; switch (img->type) { case SG_IMAGETYPE_2D: d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; d3d11_srv_desc.Texture2D.MipLevels = img->num_mipmaps; break; case SG_IMAGETYPE_CUBE: d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE; d3d11_srv_desc.TextureCube.MipLevels = img->num_mipmaps; break; case SG_IMAGETYPE_ARRAY: d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY; d3d11_srv_desc.Texture2DArray.MipLevels = img->num_mipmaps; d3d11_srv_desc.Texture2DArray.ArraySize = img->depth; break; default: SOKOL_UNREACHABLE; break; } hr = ID3D11Device_CreateShaderResourceView(_sg.d3d11.dev, (ID3D11Resource*)img->d3d11_tex2d, &d3d11_srv_desc, &img->d3d11_srv); SOKOL_ASSERT(SUCCEEDED(hr) && img->d3d11_srv); } else { /* 3D texture */ D3D11_TEXTURE3D_DESC d3d11_tex_desc; memset(&d3d11_tex_desc, 0, sizeof(d3d11_tex_desc)); d3d11_tex_desc.Width = img->width; d3d11_tex_desc.Height = img->height; d3d11_tex_desc.Depth = img->depth; d3d11_tex_desc.MipLevels = img->num_mipmaps; d3d11_tex_desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; if (img->render_target) { img->d3d11_format = _sg_d3d11_rendertarget_color_format(img->pixel_format); d3d11_tex_desc.Format = img->d3d11_format; d3d11_tex_desc.Usage = D3D11_USAGE_DEFAULT; if (!msaa) { d3d11_tex_desc.BindFlags |= D3D11_BIND_RENDER_TARGET; } d3d11_tex_desc.CPUAccessFlags = 0; } else { img->d3d11_format = _sg_d3d11_texture_format(img->pixel_format); d3d11_tex_desc.Format = img->d3d11_format; d3d11_tex_desc.Usage = _sg_d3d11_usage(img->usage); d3d11_tex_desc.CPUAccessFlags = _sg_d3d11_cpu_access_flags(img->usage); } if (img->d3d11_format == DXGI_FORMAT_UNKNOWN) { /* trying to create a texture format that's not supported by D3D */ SOKOL_LOG("trying to create a D3D11 texture with unsupported pixel format\n"); return SG_RESOURCESTATE_FAILED; } if (injected) { img->d3d11_tex3d = (ID3D11Texture3D*) desc->d3d11_texture; ID3D11Texture3D_AddRef(img->d3d11_tex3d); } else { hr = ID3D11Device_CreateTexture3D(_sg.d3d11.dev, &d3d11_tex_desc, init_data, &img->d3d11_tex3d); SOKOL_ASSERT(SUCCEEDED(hr) && img->d3d11_tex3d); } /* shader resource view for 3d texture */ D3D11_SHADER_RESOURCE_VIEW_DESC d3d11_srv_desc; memset(&d3d11_srv_desc, 0, sizeof(d3d11_srv_desc)); d3d11_srv_desc.Format = d3d11_tex_desc.Format; d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D; d3d11_srv_desc.Texture3D.MipLevels = img->num_mipmaps; hr = ID3D11Device_CreateShaderResourceView(_sg.d3d11.dev, (ID3D11Resource*)img->d3d11_tex3d, &d3d11_srv_desc, &img->d3d11_srv); SOKOL_ASSERT(SUCCEEDED(hr) && img->d3d11_srv); } /* also need to create a separate MSAA render target texture? */ if (msaa) { D3D11_TEXTURE2D_DESC d3d11_tex_desc; memset(&d3d11_tex_desc, 0, sizeof(d3d11_tex_desc)); d3d11_tex_desc.Width = img->width; d3d11_tex_desc.Height = img->height; d3d11_tex_desc.MipLevels = 1; d3d11_tex_desc.ArraySize = 1; d3d11_tex_desc.Format = img->d3d11_format; d3d11_tex_desc.Usage = D3D11_USAGE_DEFAULT; d3d11_tex_desc.BindFlags = D3D11_BIND_RENDER_TARGET; d3d11_tex_desc.CPUAccessFlags = 0; d3d11_tex_desc.SampleDesc.Count = img->sample_count; d3d11_tex_desc.SampleDesc.Quality = (UINT)D3D11_STANDARD_MULTISAMPLE_PATTERN; hr = ID3D11Device_CreateTexture2D(_sg.d3d11.dev, &d3d11_tex_desc, NULL, &img->d3d11_texmsaa); SOKOL_ASSERT(SUCCEEDED(hr) && img->d3d11_texmsaa); } /* sampler state object, note D3D11 implements an internal shared-pool for sampler objects */ D3D11_SAMPLER_DESC d3d11_smp_desc; memset(&d3d11_smp_desc, 0, sizeof(d3d11_smp_desc)); d3d11_smp_desc.Filter = _sg_d3d11_filter(img->min_filter, img->mag_filter, img->max_anisotropy); d3d11_smp_desc.AddressU = _sg_d3d11_address_mode(img->wrap_u); d3d11_smp_desc.AddressV = _sg_d3d11_address_mode(img->wrap_v); d3d11_smp_desc.AddressW = _sg_d3d11_address_mode(img->wrap_w); d3d11_smp_desc.MaxAnisotropy = img->max_anisotropy; d3d11_smp_desc.ComparisonFunc = D3D11_COMPARISON_NEVER; d3d11_smp_desc.MinLOD = desc->min_lod; d3d11_smp_desc.MaxLOD = desc->max_lod; hr = ID3D11Device_CreateSamplerState(_sg.d3d11.dev, &d3d11_smp_desc, &img->d3d11_smp); SOKOL_ASSERT(SUCCEEDED(hr) && img->d3d11_smp); } return SG_RESOURCESTATE_VALID; } _SOKOL_PRIVATE void _sg_destroy_image(_sg_image_t* img) { SOKOL_ASSERT(img); if (img->d3d11_tex2d) { ID3D11Texture2D_Release(img->d3d11_tex2d); } if (img->d3d11_tex3d) { ID3D11Texture3D_Release(img->d3d11_tex3d); } if (img->d3d11_texds) { ID3D11Texture2D_Release(img->d3d11_texds); } if (img->d3d11_texmsaa) { ID3D11Texture2D_Release(img->d3d11_texmsaa); } if (img->d3d11_srv) { ID3D11ShaderResourceView_Release(img->d3d11_srv); } if (img->d3d11_smp) { ID3D11SamplerState_Release(img->d3d11_smp); } } _SOKOL_PRIVATE bool _sg_d3d11_load_d3dcompiler_dll(void) { /* on UWP, don't do anything (not tested) */ #if (defined(WINAPI_FAMILY_PARTITION) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)) return true; #else /* load DLL on demand */ if ((0 == _sg.d3d11.d3dcompiler_dll) && !_sg.d3d11.d3dcompiler_dll_load_failed) { _sg.d3d11.d3dcompiler_dll = LoadLibraryA("d3dcompiler_47.dll"); if (0 == _sg.d3d11.d3dcompiler_dll) { /* don't attempt to load missing DLL in the future */ SOKOL_LOG("failed to load d3dcompiler_47.dll!\n"); _sg.d3d11.d3dcompiler_dll_load_failed = true; return false; } /* look up function pointers */ _sg.d3d11.D3DCompile_func = (pD3DCompile) GetProcAddress(_sg.d3d11.d3dcompiler_dll, "D3DCompile"); SOKOL_ASSERT(_sg.d3d11.D3DCompile_func); } return 0 != _sg.d3d11.d3dcompiler_dll; #endif } #if (defined(WINAPI_FAMILY_PARTITION) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)) #define _sg_d3d11_D3DCompile D3DCompile #else #define _sg_d3d11_D3DCompile _sg.d3d11.D3DCompile_func #endif _SOKOL_PRIVATE ID3DBlob* _sg_d3d11_compile_shader(const sg_shader_stage_desc* stage_desc, const char* target) { if (!_sg_d3d11_load_d3dcompiler_dll()) { return NULL; } ID3DBlob* output = NULL; ID3DBlob* errors = NULL; _sg_d3d11_D3DCompile( stage_desc->source, /* pSrcData */ strlen(stage_desc->source), /* SrcDataSize */ NULL, /* pSourceName */ NULL, /* pDefines */ NULL, /* pInclude */ stage_desc->entry ? stage_desc->entry : "main", /* pEntryPoint */ target, /* pTarget (vs_5_0 or ps_5_0) */ D3DCOMPILE_PACK_MATRIX_COLUMN_MAJOR | D3DCOMPILE_OPTIMIZATION_LEVEL3, /* Flags1 */ 0, /* Flags2 */ &output, /* ppCode */ &errors); /* ppErrorMsgs */ if (errors) { SOKOL_LOG((LPCSTR)ID3D10Blob_GetBufferPointer(errors)); ID3D10Blob_Release(errors); errors = NULL; return NULL; } return output; } #define _sg_d3d11_roundup(val, round_to) (((val)+((round_to)-1))&~((round_to)-1)) _SOKOL_PRIVATE sg_resource_state _sg_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { SOKOL_ASSERT(shd && desc); SOKOL_ASSERT(!shd->d3d11_vs && !shd->d3d11_fs && !shd->d3d11_vs_blob); HRESULT hr; sg_resource_state result = SG_RESOURCESTATE_FAILED; /* copy vertex attribute semantic names and indices */ for (int i = 0; i < SG_MAX_VERTEX_ATTRIBUTES; i++) { _sg_strcpy(&shd->attrs[i].sem_name, desc->attrs[i].sem_name); shd->attrs[i].sem_index = desc->attrs[i].sem_index; } /* shader stage uniform blocks and image slots */ for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { const sg_shader_stage_desc* stage_desc = (stage_index == SG_SHADERSTAGE_VS) ? &desc->vs : &desc->fs; _sg_shader_stage_t* stage = &shd->stage[stage_index]; SOKOL_ASSERT(stage->num_uniform_blocks == 0); for (int ub_index = 0; ub_index < SG_MAX_SHADERSTAGE_UBS; ub_index++) { const sg_shader_uniform_block_desc* ub_desc = &stage_desc->uniform_blocks[ub_index]; if (0 == ub_desc->size) { break; } _sg_uniform_block_t* ub = &stage->uniform_blocks[ub_index]; ub->size = ub_desc->size; /* create a D3D constant buffer */ SOKOL_ASSERT(!stage->d3d11_cbs[ub_index]); D3D11_BUFFER_DESC cb_desc; memset(&cb_desc, 0, sizeof(cb_desc)); cb_desc.ByteWidth = _sg_d3d11_roundup(ub->size, 16); cb_desc.Usage = D3D11_USAGE_DEFAULT; cb_desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; hr = ID3D11Device_CreateBuffer(_sg.d3d11.dev, &cb_desc, NULL, &stage->d3d11_cbs[ub_index]); SOKOL_ASSERT(SUCCEEDED(hr) && stage->d3d11_cbs[ub_index]); stage->num_uniform_blocks++; } SOKOL_ASSERT(stage->num_images == 0); for (int img_index = 0; img_index < SG_MAX_SHADERSTAGE_IMAGES; img_index++) { const sg_shader_image_desc* img_desc = &stage_desc->images[img_index]; if (img_desc->type == _SG_IMAGETYPE_DEFAULT) { break; } stage->images[img_index].type = img_desc->type; stage->num_images++; } } const void* vs_ptr = 0, *fs_ptr = 0; SIZE_T vs_length = 0, fs_length = 0; ID3DBlob* vs_blob = 0, *fs_blob = 0; if (desc->vs.byte_code && desc->fs.byte_code) { /* create from byte code */ vs_ptr = desc->vs.byte_code; fs_ptr = desc->fs.byte_code; vs_length = desc->vs.byte_code_size; fs_length = desc->fs.byte_code_size; } else { /* compile shader code */ vs_blob = _sg_d3d11_compile_shader(&desc->vs, "vs_5_0"); fs_blob = _sg_d3d11_compile_shader(&desc->fs, "ps_5_0"); if (vs_blob && fs_blob) { vs_ptr = ID3D10Blob_GetBufferPointer(vs_blob); vs_length = ID3D10Blob_GetBufferSize(vs_blob); fs_ptr = ID3D10Blob_GetBufferPointer(fs_blob); fs_length = ID3D10Blob_GetBufferSize(fs_blob); } } if (vs_ptr && fs_ptr && (vs_length > 0) && (fs_length > 0)) { /* create the D3D vertex- and pixel-shader objects */ hr = ID3D11Device_CreateVertexShader(_sg.d3d11.dev, vs_ptr, vs_length, NULL, &shd->d3d11_vs); SOKOL_ASSERT(SUCCEEDED(hr) && shd->d3d11_vs); hr = ID3D11Device_CreatePixelShader(_sg.d3d11.dev, fs_ptr, fs_length, NULL, &shd->d3d11_fs); SOKOL_ASSERT(SUCCEEDED(hr) && shd->d3d11_fs); /* need to store the vertex shader byte code, this is needed later in sg_create_pipeline */ shd->d3d11_vs_blob_length = (int)vs_length; shd->d3d11_vs_blob = SOKOL_MALLOC((int)vs_length); SOKOL_ASSERT(shd->d3d11_vs_blob); memcpy(shd->d3d11_vs_blob, vs_ptr, vs_length); result = SG_RESOURCESTATE_VALID; } if (vs_blob) { ID3D10Blob_Release(vs_blob); vs_blob = 0; } if (fs_blob) { ID3D10Blob_Release(fs_blob); fs_blob = 0; } return result; } _SOKOL_PRIVATE void _sg_destroy_shader(_sg_shader_t* shd) { SOKOL_ASSERT(shd); if (shd->d3d11_vs) { ID3D11VertexShader_Release(shd->d3d11_vs); } if (shd->d3d11_fs) { ID3D11PixelShader_Release(shd->d3d11_fs); } if (shd->d3d11_vs_blob) { SOKOL_FREE(shd->d3d11_vs_blob); } for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { _sg_shader_stage_t* stage = &shd->stage[stage_index]; for (int ub_index = 0; ub_index < stage->num_uniform_blocks; ub_index++) { if (stage->d3d11_cbs[ub_index]) { ID3D11Buffer_Release(stage->d3d11_cbs[ub_index]); } } } } _SOKOL_PRIVATE sg_resource_state _sg_create_pipeline(_sg_pipeline_t* pip, _sg_shader_t* shd, const sg_pipeline_desc* desc) { SOKOL_ASSERT(pip && shd && desc); SOKOL_ASSERT(desc->shader.id == shd->slot.id); SOKOL_ASSERT(shd->slot.state == SG_RESOURCESTATE_VALID); SOKOL_ASSERT(shd->d3d11_vs_blob && shd->d3d11_vs_blob_length > 0); SOKOL_ASSERT(!pip->d3d11_il && !pip->d3d11_rs && !pip->d3d11_dss && !pip->d3d11_bs); HRESULT hr; pip->shader = shd; pip->shader_id = desc->shader; pip->index_type = desc->index_type; pip->color_attachment_count = desc->blend.color_attachment_count; pip->color_format = desc->blend.color_format; pip->depth_format = desc->blend.depth_format; pip->sample_count = desc->rasterizer.sample_count; pip->d3d11_index_format = _sg_d3d11_index_format(pip->index_type); pip->d3d11_topology = _sg_d3d11_primitive_topology(desc->primitive_type); for (int i = 0; i < 4; i++) { pip->blend_color[i] = desc->blend.blend_color[i]; } pip->d3d11_stencil_ref = desc->depth_stencil.stencil_ref; /* create input layout object */ D3D11_INPUT_ELEMENT_DESC d3d11_comps[SG_MAX_VERTEX_ATTRIBUTES]; memset(d3d11_comps, 0, sizeof(d3d11_comps)); int attr_index = 0; for (; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { const sg_vertex_attr_desc* a_desc = &desc->layout.attrs[attr_index]; if (a_desc->format == SG_VERTEXFORMAT_INVALID) { break; } SOKOL_ASSERT((a_desc->buffer_index >= 0) && (a_desc->buffer_index < SG_MAX_SHADERSTAGE_BUFFERS)); const sg_buffer_layout_desc* l_desc = &desc->layout.buffers[a_desc->buffer_index]; const sg_vertex_step step_func = l_desc->step_func; const int step_rate = l_desc->step_rate; D3D11_INPUT_ELEMENT_DESC* d3d11_comp = &d3d11_comps[attr_index]; d3d11_comp->SemanticName = _sg_strptr(&shd->attrs[attr_index].sem_name); d3d11_comp->SemanticIndex = shd->attrs[attr_index].sem_index; d3d11_comp->Format = _sg_d3d11_vertex_format(a_desc->format); d3d11_comp->InputSlot = a_desc->buffer_index; d3d11_comp->AlignedByteOffset = a_desc->offset; d3d11_comp->InputSlotClass = _sg_d3d11_input_classification(step_func); if (SG_VERTEXSTEP_PER_INSTANCE == step_func) { d3d11_comp->InstanceDataStepRate = step_rate; } pip->vertex_layout_valid[a_desc->buffer_index] = true; } for (int layout_index = 0; layout_index < SG_MAX_SHADERSTAGE_BUFFERS; layout_index++) { if (pip->vertex_layout_valid[layout_index]) { const sg_buffer_layout_desc* l_desc = &desc->layout.buffers[layout_index]; SOKOL_ASSERT(l_desc->stride > 0); pip->d3d11_vb_strides[layout_index] = l_desc->stride; } else { pip->d3d11_vb_strides[layout_index] = 0; } } hr = ID3D11Device_CreateInputLayout(_sg.d3d11.dev, d3d11_comps, /* pInputElementDesc */ attr_index, /* NumElements */ shd->d3d11_vs_blob, /* pShaderByteCodeWithInputSignature */ shd->d3d11_vs_blob_length, /* BytecodeLength */ &pip->d3d11_il); SOKOL_ASSERT(SUCCEEDED(hr) && pip->d3d11_il); /* create rasterizer state */ D3D11_RASTERIZER_DESC rs_desc; memset(&rs_desc, 0, sizeof(rs_desc)); rs_desc.FillMode = D3D11_FILL_SOLID; rs_desc.CullMode = _sg_d3d11_cull_mode(desc->rasterizer.cull_mode); rs_desc.FrontCounterClockwise = desc->rasterizer.face_winding == SG_FACEWINDING_CCW; rs_desc.DepthBias = (INT) desc->rasterizer.depth_bias; rs_desc.DepthBiasClamp = desc->rasterizer.depth_bias_clamp; rs_desc.SlopeScaledDepthBias = desc->rasterizer.depth_bias_slope_scale; rs_desc.DepthClipEnable = TRUE; rs_desc.ScissorEnable = TRUE; rs_desc.MultisampleEnable = desc->rasterizer.sample_count > 1; rs_desc.AntialiasedLineEnable = FALSE; hr = ID3D11Device_CreateRasterizerState(_sg.d3d11.dev, &rs_desc, &pip->d3d11_rs); SOKOL_ASSERT(SUCCEEDED(hr) && pip->d3d11_rs); /* create depth-stencil state */ D3D11_DEPTH_STENCIL_DESC dss_desc; memset(&dss_desc, 0, sizeof(dss_desc)); dss_desc.DepthEnable = TRUE; dss_desc.DepthWriteMask = desc->depth_stencil.depth_write_enabled ? D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO; dss_desc.DepthFunc = _sg_d3d11_compare_func(desc->depth_stencil.depth_compare_func); dss_desc.StencilEnable = desc->depth_stencil.stencil_enabled; dss_desc.StencilReadMask = desc->depth_stencil.stencil_read_mask; dss_desc.StencilWriteMask = desc->depth_stencil.stencil_write_mask; const sg_stencil_state* sf = &desc->depth_stencil.stencil_front; dss_desc.FrontFace.StencilFailOp = _sg_d3d11_stencil_op(sf->fail_op); dss_desc.FrontFace.StencilDepthFailOp = _sg_d3d11_stencil_op(sf->depth_fail_op); dss_desc.FrontFace.StencilPassOp = _sg_d3d11_stencil_op(sf->pass_op); dss_desc.FrontFace.StencilFunc = _sg_d3d11_compare_func(sf->compare_func); const sg_stencil_state* sb = &desc->depth_stencil.stencil_back; dss_desc.BackFace.StencilFailOp = _sg_d3d11_stencil_op(sb->fail_op); dss_desc.BackFace.StencilDepthFailOp = _sg_d3d11_stencil_op(sb->depth_fail_op); dss_desc.BackFace.StencilPassOp = _sg_d3d11_stencil_op(sb->pass_op); dss_desc.BackFace.StencilFunc = _sg_d3d11_compare_func(sb->compare_func); hr = ID3D11Device_CreateDepthStencilState(_sg.d3d11.dev, &dss_desc, &pip->d3d11_dss); SOKOL_ASSERT(SUCCEEDED(hr) && pip->d3d11_dss); /* create blend state */ D3D11_BLEND_DESC bs_desc; memset(&bs_desc, 0, sizeof(bs_desc)); bs_desc.AlphaToCoverageEnable = desc->rasterizer.alpha_to_coverage_enabled; bs_desc.IndependentBlendEnable = FALSE; bs_desc.RenderTarget[0].BlendEnable = desc->blend.enabled; bs_desc.RenderTarget[0].SrcBlend = _sg_d3d11_blend_factor(desc->blend.src_factor_rgb); bs_desc.RenderTarget[0].DestBlend = _sg_d3d11_blend_factor(desc->blend.dst_factor_rgb); bs_desc.RenderTarget[0].BlendOp = _sg_d3d11_blend_op(desc->blend.op_rgb); bs_desc.RenderTarget[0].SrcBlendAlpha = _sg_d3d11_blend_factor(desc->blend.src_factor_alpha); bs_desc.RenderTarget[0].DestBlendAlpha = _sg_d3d11_blend_factor(desc->blend.dst_factor_alpha); bs_desc.RenderTarget[0].BlendOpAlpha = _sg_d3d11_blend_op(desc->blend.op_alpha); bs_desc.RenderTarget[0].RenderTargetWriteMask = _sg_d3d11_color_write_mask((sg_color_mask)desc->blend.color_write_mask); hr = ID3D11Device_CreateBlendState(_sg.d3d11.dev, &bs_desc, &pip->d3d11_bs); SOKOL_ASSERT(SUCCEEDED(hr) && pip->d3d11_bs); return SG_RESOURCESTATE_VALID; } _SOKOL_PRIVATE void _sg_destroy_pipeline(_sg_pipeline_t* pip) { SOKOL_ASSERT(pip); if (pip->d3d11_il) { ID3D11InputLayout_Release(pip->d3d11_il); } if (pip->d3d11_rs) { ID3D11RasterizerState_Release(pip->d3d11_rs); } if (pip->d3d11_dss) { ID3D11DepthStencilState_Release(pip->d3d11_dss); } if (pip->d3d11_bs) { ID3D11BlendState_Release(pip->d3d11_bs); } } _SOKOL_PRIVATE sg_resource_state _sg_create_pass(_sg_pass_t* pass, _sg_image_t** att_images, const sg_pass_desc* desc) { SOKOL_ASSERT(pass && desc); SOKOL_ASSERT(att_images && att_images[0]); SOKOL_ASSERT(_sg.d3d11.dev); const sg_attachment_desc* att_desc; _sg_attachment_t* att; for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { SOKOL_ASSERT(0 == pass->color_atts[i].image); SOKOL_ASSERT(pass->d3d11_rtvs[i] == 0); att_desc = &desc->color_attachments[i]; if (att_desc->image.id != SG_INVALID_ID) { pass->num_color_atts++; SOKOL_ASSERT(att_images[i] && (att_images[i]->slot.id == att_desc->image.id)); SOKOL_ASSERT(_sg_is_valid_rendertarget_color_format(att_images[i]->pixel_format)); att = &pass->color_atts[i]; SOKOL_ASSERT((att->image == 0) && (att->image_id.id == SG_INVALID_ID)); att->image = att_images[i]; att->image_id = att_desc->image; att->mip_level = att_desc->mip_level; att->slice = att_desc->slice; /* create D3D11 render-target-view */ ID3D11Resource* d3d11_res = 0; const bool is_msaa = att->image->sample_count > 1; D3D11_RENDER_TARGET_VIEW_DESC d3d11_rtv_desc; memset(&d3d11_rtv_desc, 0, sizeof(d3d11_rtv_desc)); d3d11_rtv_desc.Format = att->image->d3d11_format; if ((att->image->type == SG_IMAGETYPE_2D) || is_msaa) { if (is_msaa) { d3d11_res = (ID3D11Resource*) att->image->d3d11_texmsaa; d3d11_rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMS; } else { d3d11_res = (ID3D11Resource*) att->image->d3d11_tex2d; d3d11_rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; d3d11_rtv_desc.Texture2D.MipSlice = att->mip_level; } } else if ((att->image->type == SG_IMAGETYPE_CUBE) || (att->image->type == SG_IMAGETYPE_ARRAY)) { d3d11_res = (ID3D11Resource*) att->image->d3d11_tex2d; d3d11_rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY; d3d11_rtv_desc.Texture2DArray.MipSlice = att->mip_level; d3d11_rtv_desc.Texture2DArray.FirstArraySlice = att->slice; d3d11_rtv_desc.Texture2DArray.ArraySize = 1; } else { SOKOL_ASSERT(att->image->type == SG_IMAGETYPE_3D); d3d11_res = (ID3D11Resource*) att->image->d3d11_tex3d; d3d11_rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE3D; d3d11_rtv_desc.Texture3D.MipSlice = att->mip_level; d3d11_rtv_desc.Texture3D.FirstWSlice = att->slice; d3d11_rtv_desc.Texture3D.WSize = 1; } SOKOL_ASSERT(d3d11_res); HRESULT hr = ID3D11Device_CreateRenderTargetView(_sg.d3d11.dev, d3d11_res, &d3d11_rtv_desc, &pass->d3d11_rtvs[i]); _SOKOL_UNUSED(hr); SOKOL_ASSERT(SUCCEEDED(hr) && pass->d3d11_rtvs[i]); } } /* optional depth-stencil image */ SOKOL_ASSERT(0 == pass->ds_att.image); SOKOL_ASSERT(pass->d3d11_dsv == 0); att_desc = &desc->depth_stencil_attachment; const int ds_img_index = SG_MAX_COLOR_ATTACHMENTS; if (att_desc->image.id != SG_INVALID_ID) { SOKOL_ASSERT(att_images[ds_img_index] && (att_images[ds_img_index]->slot.id == att_desc->image.id)); SOKOL_ASSERT(_sg_is_valid_rendertarget_depth_format(att_images[ds_img_index]->pixel_format)); att = &pass->ds_att; SOKOL_ASSERT((att->image == 0) && (att->image_id.id == SG_INVALID_ID)); att->image = att_images[ds_img_index]; att->image_id = att_desc->image; att->mip_level = att_desc->mip_level; att->slice = att_desc->slice; /* create D3D11 depth-stencil-view */ D3D11_DEPTH_STENCIL_VIEW_DESC d3d11_dsv_desc; memset(&d3d11_dsv_desc, 0, sizeof(d3d11_dsv_desc)); d3d11_dsv_desc.Format = att->image->d3d11_format; const bool is_msaa = att->image->sample_count > 1; if (is_msaa) { d3d11_dsv_desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DMS; } else { d3d11_dsv_desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D; } ID3D11Resource* d3d11_res = (ID3D11Resource*) att->image->d3d11_texds; SOKOL_ASSERT(d3d11_res); HRESULT hr = ID3D11Device_CreateDepthStencilView(_sg.d3d11.dev, d3d11_res, &d3d11_dsv_desc, &pass->d3d11_dsv); _SOKOL_UNUSED(hr); SOKOL_ASSERT(SUCCEEDED(hr) && pass->d3d11_dsv); } return SG_RESOURCESTATE_VALID; } _SOKOL_PRIVATE void _sg_destroy_pass(_sg_pass_t* pass) { SOKOL_ASSERT(pass); for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { if (pass->d3d11_rtvs[i]) { ID3D11RenderTargetView_Release(pass->d3d11_rtvs[i]); } } if (pass->d3d11_dsv) { ID3D11DepthStencilView_Release(pass->d3d11_dsv); } } _SOKOL_PRIVATE void _sg_begin_pass(_sg_pass_t* pass, const sg_pass_action* action, int w, int h) { SOKOL_ASSERT(action); SOKOL_ASSERT(!_sg.d3d11.in_pass); _sg.d3d11.in_pass = true; _sg.d3d11.cur_width = w; _sg.d3d11.cur_height = h; if (pass) { _sg.d3d11.cur_pass = pass; _sg.d3d11.cur_pass_id.id = pass->slot.id; _sg.d3d11.num_rtvs = 0; for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { _sg.d3d11.cur_rtvs[i] = pass->d3d11_rtvs[i]; if (_sg.d3d11.cur_rtvs[i]) { _sg.d3d11.num_rtvs++; } } _sg.d3d11.cur_dsv = pass->d3d11_dsv; } else { /* render to default frame buffer */ _sg.d3d11.cur_pass = 0; _sg.d3d11.cur_pass_id.id = SG_INVALID_ID; _sg.d3d11.num_rtvs = 1; _sg.d3d11.cur_rtvs[0] = (ID3D11RenderTargetView*) _sg.d3d11.rtv_cb(); for (int i = 1; i < SG_MAX_COLOR_ATTACHMENTS; i++) { _sg.d3d11.cur_rtvs[i] = 0; } _sg.d3d11.cur_dsv = (ID3D11DepthStencilView*) _sg.d3d11.dsv_cb(); SOKOL_ASSERT(_sg.d3d11.cur_rtvs[0] && _sg.d3d11.cur_dsv); } /* apply the render-target- and depth-stencil-views */ ID3D11DeviceContext_OMSetRenderTargets(_sg.d3d11.ctx, SG_MAX_COLOR_ATTACHMENTS, _sg.d3d11.cur_rtvs, _sg.d3d11.cur_dsv); /* set viewport and scissor rect to cover whole screen */ D3D11_VIEWPORT vp; memset(&vp, 0, sizeof(vp)); vp.Width = (FLOAT) w; vp.Height = (FLOAT) h; vp.MaxDepth = 1.0f; ID3D11DeviceContext_RSSetViewports(_sg.d3d11.ctx, 1, &vp); D3D11_RECT rect; rect.left = 0; rect.top = 0; rect.right = w; rect.bottom = h; ID3D11DeviceContext_RSSetScissorRects(_sg.d3d11.ctx, 1, &rect); /* perform clear action */ for (int i = 0; i < _sg.d3d11.num_rtvs; i++) { if (action->colors[i].action == SG_ACTION_CLEAR) { ID3D11DeviceContext_ClearRenderTargetView(_sg.d3d11.ctx, _sg.d3d11.cur_rtvs[i], action->colors[i].val); } } UINT ds_flags = 0; if (action->depth.action == SG_ACTION_CLEAR) { ds_flags |= D3D11_CLEAR_DEPTH; } if (action->stencil.action == SG_ACTION_CLEAR) { ds_flags |= D3D11_CLEAR_STENCIL; } if ((0 != ds_flags) && _sg.d3d11.cur_dsv) { ID3D11DeviceContext_ClearDepthStencilView(_sg.d3d11.ctx, _sg.d3d11.cur_dsv, ds_flags, action->depth.val, action->stencil.val); } } /* D3D11CalcSubresource only exists for C++ */ _SOKOL_PRIVATE UINT _sg_d3d11_calcsubresource(UINT mip_slice, UINT array_slice, UINT mip_levels) { return mip_slice + array_slice * mip_levels; } _SOKOL_PRIVATE void _sg_end_pass(void) { SOKOL_ASSERT(_sg.d3d11.in_pass && _sg.d3d11.ctx); _sg.d3d11.in_pass = false; /* need to resolve MSAA render target into texture? */ if (_sg.d3d11.cur_pass) { SOKOL_ASSERT(_sg.d3d11.cur_pass->slot.id == _sg.d3d11.cur_pass_id.id); for (int i = 0; i < _sg.d3d11.num_rtvs; i++) { _sg_attachment_t* att = &_sg.d3d11.cur_pass->color_atts[i]; SOKOL_ASSERT(att->image && (att->image->slot.id == att->image_id.id)); if (att->image->sample_count > 1) { /* FIXME: support MSAA resolve into 3D texture */ SOKOL_ASSERT(att->image->d3d11_tex2d && att->image->d3d11_texmsaa && !att->image->d3d11_tex3d); SOKOL_ASSERT(DXGI_FORMAT_UNKNOWN != att->image->d3d11_format); const _sg_image_t* img = att->image; UINT dst_subres = _sg_d3d11_calcsubresource(att->mip_level, att->slice, img->num_mipmaps); ID3D11DeviceContext_ResolveSubresource(_sg.d3d11.ctx, (ID3D11Resource*) img->d3d11_tex2d, /* pDstResource */ dst_subres, /* DstSubresource */ (ID3D11Resource*) img->d3d11_texmsaa, /* pSrcResource */ 0, /* SrcSubresource */ img->d3d11_format); } } } _sg.d3d11.cur_pass = 0; _sg.d3d11.cur_pass_id.id = SG_INVALID_ID; _sg.d3d11.cur_pipeline = 0; _sg.d3d11.cur_pipeline_id.id = SG_INVALID_ID; for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { _sg.d3d11.cur_rtvs[i] = 0; } _sg.d3d11.cur_dsv = 0; _sg_d3d11_clear_state(); } _SOKOL_PRIVATE void _sg_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { SOKOL_ASSERT(_sg.d3d11.ctx); SOKOL_ASSERT(_sg.d3d11.in_pass); D3D11_VIEWPORT vp; vp.TopLeftX = (FLOAT) x; vp.TopLeftY = (FLOAT) (origin_top_left ? y : (_sg.d3d11.cur_height - (y + h))); vp.Width = (FLOAT) w; vp.Height = (FLOAT) h; vp.MinDepth = 0.0f; vp.MaxDepth = 1.0f; ID3D11DeviceContext_RSSetViewports(_sg.d3d11.ctx, 1, &vp); } _SOKOL_PRIVATE void _sg_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) { SOKOL_ASSERT(_sg.d3d11.ctx); SOKOL_ASSERT(_sg.d3d11.in_pass); D3D11_RECT rect; rect.left = x; rect.top = (origin_top_left ? y : (_sg.d3d11.cur_height - (y + h))); rect.right = x + w; rect.bottom = origin_top_left ? (y + h) : (_sg.d3d11.cur_height - y); ID3D11DeviceContext_RSSetScissorRects(_sg.d3d11.ctx, 1, &rect); } _SOKOL_PRIVATE void _sg_apply_pipeline(_sg_pipeline_t* pip) { SOKOL_ASSERT(pip); SOKOL_ASSERT(pip->shader); SOKOL_ASSERT(_sg.d3d11.ctx); SOKOL_ASSERT(_sg.d3d11.in_pass); SOKOL_ASSERT(pip->d3d11_rs && pip->d3d11_bs && pip->d3d11_dss && pip->d3d11_il); _sg.d3d11.cur_pipeline = pip; _sg.d3d11.cur_pipeline_id.id = pip->slot.id; _sg.d3d11.use_indexed_draw = (pip->d3d11_index_format != DXGI_FORMAT_UNKNOWN); ID3D11DeviceContext_RSSetState(_sg.d3d11.ctx, pip->d3d11_rs); ID3D11DeviceContext_OMSetDepthStencilState(_sg.d3d11.ctx, pip->d3d11_dss, pip->d3d11_stencil_ref); ID3D11DeviceContext_OMSetBlendState(_sg.d3d11.ctx, pip->d3d11_bs, pip->blend_color, 0xFFFFFFFF); ID3D11DeviceContext_IASetPrimitiveTopology(_sg.d3d11.ctx, pip->d3d11_topology); ID3D11DeviceContext_IASetInputLayout(_sg.d3d11.ctx, pip->d3d11_il); ID3D11DeviceContext_VSSetShader(_sg.d3d11.ctx, pip->shader->d3d11_vs, NULL, 0); ID3D11DeviceContext_VSSetConstantBuffers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_UBS, pip->shader->stage[SG_SHADERSTAGE_VS].d3d11_cbs); ID3D11DeviceContext_PSSetShader(_sg.d3d11.ctx, pip->shader->d3d11_fs, NULL, 0); ID3D11DeviceContext_PSSetConstantBuffers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_UBS, pip->shader->stage[SG_SHADERSTAGE_FS].d3d11_cbs); } _SOKOL_PRIVATE void _sg_apply_bindings( _sg_pipeline_t* pip, _sg_buffer_t** vbs, const int* vb_offsets, int num_vbs, _sg_buffer_t* ib, int ib_offset, _sg_image_t** vs_imgs, int num_vs_imgs, _sg_image_t** fs_imgs, int num_fs_imgs) { SOKOL_ASSERT(pip); SOKOL_ASSERT(_sg.d3d11.ctx); SOKOL_ASSERT(_sg.d3d11.in_pass); /* gather all the D3D11 resources into arrays */ ID3D11Buffer* d3d11_ib = ib ? ib->d3d11_buf : 0; ID3D11Buffer* d3d11_vbs[SG_MAX_SHADERSTAGE_BUFFERS]; UINT d3d11_vb_offsets[SG_MAX_SHADERSTAGE_BUFFERS]; ID3D11ShaderResourceView* d3d11_vs_srvs[SG_MAX_SHADERSTAGE_IMAGES]; ID3D11SamplerState* d3d11_vs_smps[SG_MAX_SHADERSTAGE_IMAGES]; ID3D11ShaderResourceView* d3d11_fs_srvs[SG_MAX_SHADERSTAGE_IMAGES]; ID3D11SamplerState* d3d11_fs_smps[SG_MAX_SHADERSTAGE_IMAGES]; int i; for (i = 0; i < num_vbs; i++) { SOKOL_ASSERT(vbs[i]->d3d11_buf); d3d11_vbs[i] = vbs[i]->d3d11_buf; d3d11_vb_offsets[i] = vb_offsets[i]; } for (; i < SG_MAX_SHADERSTAGE_BUFFERS; i++) { d3d11_vbs[i] = 0; d3d11_vb_offsets[i] = 0; } for (i = 0; i < num_vs_imgs; i++) { SOKOL_ASSERT(vs_imgs[i]->d3d11_srv); SOKOL_ASSERT(vs_imgs[i]->d3d11_smp); d3d11_vs_srvs[i] = vs_imgs[i]->d3d11_srv; d3d11_vs_smps[i] = vs_imgs[i]->d3d11_smp; } for (; i < SG_MAX_SHADERSTAGE_IMAGES; i++) { d3d11_vs_srvs[i] = 0; d3d11_vs_smps[i] = 0; } for (i = 0; i < num_fs_imgs; i++) { SOKOL_ASSERT(fs_imgs[i]->d3d11_srv); SOKOL_ASSERT(fs_imgs[i]->d3d11_smp); d3d11_fs_srvs[i] = fs_imgs[i]->d3d11_srv; d3d11_fs_smps[i] = fs_imgs[i]->d3d11_smp; } for (; i < SG_MAX_SHADERSTAGE_IMAGES; i++) { d3d11_fs_srvs[i] = 0; d3d11_fs_smps[i] = 0; } ID3D11DeviceContext_IASetVertexBuffers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_BUFFERS, d3d11_vbs, pip->d3d11_vb_strides, d3d11_vb_offsets); ID3D11DeviceContext_IASetIndexBuffer(_sg.d3d11.ctx, d3d11_ib, pip->d3d11_index_format, ib_offset); ID3D11DeviceContext_VSSetShaderResources(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_IMAGES, d3d11_vs_srvs); ID3D11DeviceContext_VSSetSamplers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_IMAGES, d3d11_vs_smps); ID3D11DeviceContext_PSSetShaderResources(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_IMAGES, d3d11_fs_srvs); ID3D11DeviceContext_PSSetSamplers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_IMAGES, d3d11_fs_smps); } _SOKOL_PRIVATE void _sg_apply_uniforms(sg_shader_stage stage_index, int ub_index, const void* data, int num_bytes) { _SOKOL_UNUSED(num_bytes); SOKOL_ASSERT(_sg.d3d11.ctx && _sg.d3d11.in_pass); SOKOL_ASSERT(data && (num_bytes > 0)); SOKOL_ASSERT((stage_index >= 0) && (stage_index < SG_NUM_SHADER_STAGES)); SOKOL_ASSERT((ub_index >= 0) && (ub_index < SG_MAX_SHADERSTAGE_UBS)); SOKOL_ASSERT(_sg.d3d11.cur_pipeline && _sg.d3d11.cur_pipeline->slot.id == _sg.d3d11.cur_pipeline_id.id); SOKOL_ASSERT(_sg.d3d11.cur_pipeline->shader && _sg.d3d11.cur_pipeline->shader->slot.id == _sg.d3d11.cur_pipeline->shader_id.id); SOKOL_ASSERT(ub_index < _sg.d3d11.cur_pipeline->shader->stage[stage_index].num_uniform_blocks); SOKOL_ASSERT(num_bytes == _sg.d3d11.cur_pipeline->shader->stage[stage_index].uniform_blocks[ub_index].size); ID3D11Buffer* cb = _sg.d3d11.cur_pipeline->shader->stage[stage_index].d3d11_cbs[ub_index]; SOKOL_ASSERT(cb); ID3D11DeviceContext_UpdateSubresource(_sg.d3d11.ctx, (ID3D11Resource*)cb, 0, NULL, data, 0, 0); } _SOKOL_PRIVATE void _sg_draw(int base_element, int num_elements, int num_instances) { SOKOL_ASSERT(_sg.d3d11.in_pass); if (_sg.d3d11.use_indexed_draw) { if (1 == num_instances) { ID3D11DeviceContext_DrawIndexed(_sg.d3d11.ctx, num_elements, base_element, 0); } else { ID3D11DeviceContext_DrawIndexedInstanced(_sg.d3d11.ctx, num_elements, num_instances, base_element, 0, 0); } } else { if (1 == num_instances) { ID3D11DeviceContext_Draw(_sg.d3d11.ctx, num_elements, base_element); } else { ID3D11DeviceContext_DrawInstanced(_sg.d3d11.ctx, num_elements, num_instances, base_element, 0); } } } _SOKOL_PRIVATE void _sg_commit(void) { SOKOL_ASSERT(!_sg.d3d11.in_pass); } _SOKOL_PRIVATE void _sg_update_buffer(_sg_buffer_t* buf, const void* data_ptr, int data_size) { SOKOL_ASSERT(buf && data_ptr && (data_size > 0)); SOKOL_ASSERT(_sg.d3d11.ctx); SOKOL_ASSERT(buf->d3d11_buf); D3D11_MAPPED_SUBRESOURCE d3d11_msr; HRESULT hr = ID3D11DeviceContext_Map(_sg.d3d11.ctx, (ID3D11Resource*)buf->d3d11_buf, 0, D3D11_MAP_WRITE_DISCARD, 0, &d3d11_msr); _SOKOL_UNUSED(hr); SOKOL_ASSERT(SUCCEEDED(hr)); memcpy(d3d11_msr.pData, data_ptr, data_size); ID3D11DeviceContext_Unmap(_sg.d3d11.ctx, (ID3D11Resource*)buf->d3d11_buf, 0); } _SOKOL_PRIVATE void _sg_append_buffer(_sg_buffer_t* buf, const void* data_ptr, int data_size, bool new_frame) { SOKOL_ASSERT(buf && data_ptr && (data_size > 0)); SOKOL_ASSERT(_sg.d3d11.ctx); SOKOL_ASSERT(buf->d3d11_buf); D3D11_MAP map_type = new_frame ? D3D11_MAP_WRITE_DISCARD : D3D11_MAP_WRITE_NO_OVERWRITE; D3D11_MAPPED_SUBRESOURCE d3d11_msr; HRESULT hr = ID3D11DeviceContext_Map(_sg.d3d11.ctx, (ID3D11Resource*)buf->d3d11_buf, 0, map_type, 0, &d3d11_msr); _SOKOL_UNUSED(hr); SOKOL_ASSERT(SUCCEEDED(hr)); uint8_t* dst_ptr = (uint8_t*)d3d11_msr.pData + buf->append_pos; memcpy(dst_ptr, data_ptr, data_size); ID3D11DeviceContext_Unmap(_sg.d3d11.ctx, (ID3D11Resource*)buf->d3d11_buf, 0); } _SOKOL_PRIVATE void _sg_update_image(_sg_image_t* img, const sg_image_content* data) { SOKOL_ASSERT(img && data); SOKOL_ASSERT(_sg.d3d11.ctx); SOKOL_ASSERT(img->d3d11_tex2d || img->d3d11_tex3d); ID3D11Resource* d3d11_res = 0; if (img->d3d11_tex3d) { d3d11_res = (ID3D11Resource*) img->d3d11_tex3d; } else { d3d11_res = (ID3D11Resource*) img->d3d11_tex2d; } SOKOL_ASSERT(d3d11_res); const int num_faces = (img->type == SG_IMAGETYPE_CUBE) ? 6:1; const int num_slices = (img->type == SG_IMAGETYPE_ARRAY) ? img->depth:1; int subres_index = 0; HRESULT hr; D3D11_MAPPED_SUBRESOURCE d3d11_msr; for (int face_index = 0; face_index < num_faces; face_index++) { for (int slice_index = 0; slice_index < num_slices; slice_index++) { for (int mip_index = 0; mip_index < img->num_mipmaps; mip_index++, subres_index++) { SOKOL_ASSERT(subres_index < (SG_MAX_MIPMAPS * SG_MAX_TEXTUREARRAY_LAYERS)); const int mip_width = ((img->width>>mip_index)>0) ? img->width>>mip_index : 1; const int mip_height = ((img->height>>mip_index)>0) ? img->height>>mip_index : 1; const int src_pitch = _sg_row_pitch(img->pixel_format, mip_width); const sg_subimage_content* subimg_content = &(data->subimage[face_index][mip_index]); const int slice_size = subimg_content->size / num_slices; const int slice_offset = slice_size * slice_index; const uint8_t* slice_ptr = ((const uint8_t*)subimg_content->ptr) + slice_offset; hr = ID3D11DeviceContext_Map(_sg.d3d11.ctx, d3d11_res, subres_index, D3D11_MAP_WRITE_DISCARD, 0, &d3d11_msr); SOKOL_ASSERT(SUCCEEDED(hr)); /* FIXME: need to handle difference in depth-pitch for 3D textures as well! */ if (src_pitch == (int)d3d11_msr.RowPitch) { memcpy(d3d11_msr.pData, slice_ptr, slice_size); } else { SOKOL_ASSERT(src_pitch < (int)d3d11_msr.RowPitch); const uint8_t* src_ptr = slice_ptr; uint8_t* dst_ptr = (uint8_t*) d3d11_msr.pData; for (int row_index = 0; row_index < mip_height; row_index++) { memcpy(dst_ptr, src_ptr, src_pitch); src_ptr += src_pitch; dst_ptr += d3d11_msr.RowPitch; } } ID3D11DeviceContext_Unmap(_sg.d3d11.ctx, d3d11_res, subres_index); } } } } /*== METAL BACKEND IMPLEMENTATION ============================================*/ #elif defined(SOKOL_METAL) /*-- enum translation functions ----------------------------------------------*/ _SOKOL_PRIVATE MTLLoadAction _sg_mtl_load_action(sg_action a) { switch (a) { case SG_ACTION_CLEAR: return MTLLoadActionClear; case SG_ACTION_LOAD: return MTLLoadActionLoad; case SG_ACTION_DONTCARE: return MTLLoadActionDontCare; default: SOKOL_UNREACHABLE; return (MTLLoadAction)0; } } _SOKOL_PRIVATE MTLResourceOptions _sg_mtl_buffer_resource_options(sg_usage usg) { switch (usg) { case SG_USAGE_IMMUTABLE: return MTLResourceStorageModeShared; case SG_USAGE_DYNAMIC: case SG_USAGE_STREAM: #if defined(TARGET_OS_IPHONE) && !TARGET_OS_IPHONE return MTLCPUCacheModeWriteCombined|MTLResourceStorageModeManaged; #else return MTLCPUCacheModeWriteCombined; #endif default: SOKOL_UNREACHABLE; return 0; } } _SOKOL_PRIVATE MTLVertexStepFunction _sg_mtl_step_function(sg_vertex_step step) { switch (step) { case SG_VERTEXSTEP_PER_VERTEX: return MTLVertexStepFunctionPerVertex; case SG_VERTEXSTEP_PER_INSTANCE: return MTLVertexStepFunctionPerInstance; default: SOKOL_UNREACHABLE; return (MTLVertexStepFunction)0; } } _SOKOL_PRIVATE MTLVertexFormat _sg_mtl_vertex_format(sg_vertex_format fmt) { switch (fmt) { case SG_VERTEXFORMAT_FLOAT: return MTLVertexFormatFloat; case SG_VERTEXFORMAT_FLOAT2: return MTLVertexFormatFloat2; case SG_VERTEXFORMAT_FLOAT3: return MTLVertexFormatFloat3; case SG_VERTEXFORMAT_FLOAT4: return MTLVertexFormatFloat4; case SG_VERTEXFORMAT_BYTE4: return MTLVertexFormatChar4; case SG_VERTEXFORMAT_BYTE4N: return MTLVertexFormatChar4Normalized; case SG_VERTEXFORMAT_UBYTE4: return MTLVertexFormatUChar4; case SG_VERTEXFORMAT_UBYTE4N: return MTLVertexFormatUChar4Normalized; case SG_VERTEXFORMAT_SHORT2: return MTLVertexFormatShort2; case SG_VERTEXFORMAT_SHORT2N: return MTLVertexFormatShort2Normalized; case SG_VERTEXFORMAT_SHORT4: return MTLVertexFormatShort4; case SG_VERTEXFORMAT_SHORT4N: return MTLVertexFormatShort4Normalized; case SG_VERTEXFORMAT_UINT10_N2: return MTLVertexFormatUInt1010102Normalized; default: SOKOL_UNREACHABLE; return (MTLVertexFormat)0; } } _SOKOL_PRIVATE MTLPrimitiveType _sg_mtl_primitive_type(sg_primitive_type t) { switch (t) { case SG_PRIMITIVETYPE_POINTS: return MTLPrimitiveTypePoint; case SG_PRIMITIVETYPE_LINES: return MTLPrimitiveTypeLine; case SG_PRIMITIVETYPE_LINE_STRIP: return MTLPrimitiveTypeLineStrip; case SG_PRIMITIVETYPE_TRIANGLES: return MTLPrimitiveTypeTriangle; case SG_PRIMITIVETYPE_TRIANGLE_STRIP: return MTLPrimitiveTypeTriangleStrip; default: SOKOL_UNREACHABLE; return (MTLPrimitiveType)0; } } _SOKOL_PRIVATE MTLPixelFormat _sg_mtl_texture_format(sg_pixel_format fmt) { switch (fmt) { case SG_PIXELFORMAT_RGBA8: return MTLPixelFormatRGBA8Unorm; case SG_PIXELFORMAT_R10G10B10A2: return MTLPixelFormatRGB10A2Unorm; case SG_PIXELFORMAT_RGBA32F: return MTLPixelFormatRGBA32Float; case SG_PIXELFORMAT_RGBA16F: return MTLPixelFormatRGBA16Float; case SG_PIXELFORMAT_R32F: return MTLPixelFormatR32Float; case SG_PIXELFORMAT_R16F: return MTLPixelFormatR16Float; case SG_PIXELFORMAT_L8: return MTLPixelFormatR8Unorm; #if defined(TARGET_OS_IPHONE) && !TARGET_OS_IPHONE case SG_PIXELFORMAT_DXT1: return MTLPixelFormatBC1_RGBA; case SG_PIXELFORMAT_DXT3: return MTLPixelFormatBC2_RGBA; case SG_PIXELFORMAT_DXT5: return MTLPixelFormatBC3_RGBA; #else case SG_PIXELFORMAT_PVRTC2_RGB: return MTLPixelFormatPVRTC_RGB_2BPP; case SG_PIXELFORMAT_PVRTC4_RGB: return MTLPixelFormatPVRTC_RGB_4BPP; case SG_PIXELFORMAT_PVRTC2_RGBA: return MTLPixelFormatPVRTC_RGBA_2BPP; case SG_PIXELFORMAT_PVRTC4_RGBA: return MTLPixelFormatPVRTC_RGBA_4BPP; case SG_PIXELFORMAT_ETC2_RGB8: return MTLPixelFormatETC2_RGB8; case SG_PIXELFORMAT_ETC2_SRGB8: return MTLPixelFormatETC2_RGB8_sRGB; #endif default: return MTLPixelFormatInvalid; } } _SOKOL_PRIVATE MTLPixelFormat _sg_mtl_rendertarget_color_format(sg_pixel_format fmt) { switch (fmt) { case SG_PIXELFORMAT_RGBA8: return MTLPixelFormatBGRA8Unorm; /* not a bug */ case SG_PIXELFORMAT_RGBA32F: return MTLPixelFormatRGBA32Float; case SG_PIXELFORMAT_RGBA16F: return MTLPixelFormatRGBA16Float; case SG_PIXELFORMAT_R10G10B10A2: return MTLPixelFormatRGB10A2Unorm; default: return MTLPixelFormatInvalid; } } _SOKOL_PRIVATE MTLPixelFormat _sg_mtl_rendertarget_depth_format(sg_pixel_format fmt) { switch (fmt) { case SG_PIXELFORMAT_DEPTH: return MTLPixelFormatDepth32Float; case SG_PIXELFORMAT_DEPTHSTENCIL: /* NOTE: Depth24_Stencil8 isn't universally supported! */ return MTLPixelFormatDepth32Float_Stencil8; default: return MTLPixelFormatInvalid; } } _SOKOL_PRIVATE MTLPixelFormat _sg_mtl_rendertarget_stencil_format(sg_pixel_format fmt) { switch (fmt) { case SG_PIXELFORMAT_DEPTHSTENCIL: return MTLPixelFormatDepth32Float_Stencil8; default: return MTLPixelFormatInvalid; } } _SOKOL_PRIVATE MTLColorWriteMask _sg_mtl_color_write_mask(sg_color_mask m) { MTLColorWriteMask mtl_mask = MTLColorWriteMaskNone; if (m & SG_COLORMASK_R) { mtl_mask |= MTLColorWriteMaskRed; } if (m & SG_COLORMASK_G) { mtl_mask |= MTLColorWriteMaskGreen; } if (m & SG_COLORMASK_B) { mtl_mask |= MTLColorWriteMaskBlue; } if (m & SG_COLORMASK_A) { mtl_mask |= MTLColorWriteMaskAlpha; } return mtl_mask; } _SOKOL_PRIVATE MTLBlendOperation _sg_mtl_blend_op(sg_blend_op op) { switch (op) { case SG_BLENDOP_ADD: return MTLBlendOperationAdd; case SG_BLENDOP_SUBTRACT: return MTLBlendOperationSubtract; case SG_BLENDOP_REVERSE_SUBTRACT: return MTLBlendOperationReverseSubtract; default: SOKOL_UNREACHABLE; return (MTLBlendOperation)0; } } _SOKOL_PRIVATE MTLBlendFactor _sg_mtl_blend_factor(sg_blend_factor f) { switch (f) { case SG_BLENDFACTOR_ZERO: return MTLBlendFactorZero; case SG_BLENDFACTOR_ONE: return MTLBlendFactorOne; case SG_BLENDFACTOR_SRC_COLOR: return MTLBlendFactorSourceColor; case SG_BLENDFACTOR_ONE_MINUS_SRC_COLOR: return MTLBlendFactorOneMinusSourceColor; case SG_BLENDFACTOR_SRC_ALPHA: return MTLBlendFactorSourceAlpha; case SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA: return MTLBlendFactorOneMinusSourceAlpha; case SG_BLENDFACTOR_DST_COLOR: return MTLBlendFactorDestinationColor; case SG_BLENDFACTOR_ONE_MINUS_DST_COLOR: return MTLBlendFactorOneMinusDestinationColor; case SG_BLENDFACTOR_DST_ALPHA: return MTLBlendFactorDestinationAlpha; case SG_BLENDFACTOR_ONE_MINUS_DST_ALPHA: return MTLBlendFactorOneMinusDestinationAlpha; case SG_BLENDFACTOR_SRC_ALPHA_SATURATED: return MTLBlendFactorSourceAlphaSaturated; case SG_BLENDFACTOR_BLEND_COLOR: return MTLBlendFactorBlendColor; case SG_BLENDFACTOR_ONE_MINUS_BLEND_COLOR: return MTLBlendFactorOneMinusBlendColor; case SG_BLENDFACTOR_BLEND_ALPHA: return MTLBlendFactorBlendAlpha; case SG_BLENDFACTOR_ONE_MINUS_BLEND_ALPHA: return MTLBlendFactorOneMinusBlendAlpha; default: SOKOL_UNREACHABLE; return (MTLBlendFactor)0; } } _SOKOL_PRIVATE MTLCompareFunction _sg_mtl_compare_func(sg_compare_func f) { switch (f) { case SG_COMPAREFUNC_NEVER: return MTLCompareFunctionNever; case SG_COMPAREFUNC_LESS: return MTLCompareFunctionLess; case SG_COMPAREFUNC_EQUAL: return MTLCompareFunctionEqual; case SG_COMPAREFUNC_LESS_EQUAL: return MTLCompareFunctionLessEqual; case SG_COMPAREFUNC_GREATER: return MTLCompareFunctionGreater; case SG_COMPAREFUNC_NOT_EQUAL: return MTLCompareFunctionNotEqual; case SG_COMPAREFUNC_GREATER_EQUAL: return MTLCompareFunctionGreaterEqual; case SG_COMPAREFUNC_ALWAYS: return MTLCompareFunctionAlways; default: SOKOL_UNREACHABLE; return (MTLCompareFunction)0; } } _SOKOL_PRIVATE MTLStencilOperation _sg_mtl_stencil_op(sg_stencil_op op) { switch (op) { case SG_STENCILOP_KEEP: return MTLStencilOperationKeep; case SG_STENCILOP_ZERO: return MTLStencilOperationZero; case SG_STENCILOP_REPLACE: return MTLStencilOperationReplace; case SG_STENCILOP_INCR_CLAMP: return MTLStencilOperationIncrementClamp; case SG_STENCILOP_DECR_CLAMP: return MTLStencilOperationDecrementClamp; case SG_STENCILOP_INVERT: return MTLStencilOperationInvert; case SG_STENCILOP_INCR_WRAP: return MTLStencilOperationIncrementWrap; case SG_STENCILOP_DECR_WRAP: return MTLStencilOperationDecrementWrap; default: SOKOL_UNREACHABLE; return (MTLStencilOperation)0; } } _SOKOL_PRIVATE MTLCullMode _sg_mtl_cull_mode(sg_cull_mode m) { switch (m) { case SG_CULLMODE_NONE: return MTLCullModeNone; case SG_CULLMODE_FRONT: return MTLCullModeFront; case SG_CULLMODE_BACK: return MTLCullModeBack; default: SOKOL_UNREACHABLE; return (MTLCullMode)0; } } _SOKOL_PRIVATE MTLWinding _sg_mtl_winding(sg_face_winding w) { switch (w) { case SG_FACEWINDING_CW: return MTLWindingClockwise; case SG_FACEWINDING_CCW: return MTLWindingCounterClockwise; default: SOKOL_UNREACHABLE; return (MTLWinding)0; } } _SOKOL_PRIVATE MTLIndexType _sg_mtl_index_type(sg_index_type t) { switch (t) { case SG_INDEXTYPE_UINT16: return MTLIndexTypeUInt16; case SG_INDEXTYPE_UINT32: return MTLIndexTypeUInt32; default: SOKOL_UNREACHABLE; return (MTLIndexType)0; } } _SOKOL_PRIVATE NSUInteger _sg_mtl_index_size(sg_index_type t) { switch (t) { case SG_INDEXTYPE_NONE: return 0; case SG_INDEXTYPE_UINT16: return 2; case SG_INDEXTYPE_UINT32: return 4; default: SOKOL_UNREACHABLE; return 0; } } _SOKOL_PRIVATE MTLTextureType _sg_mtl_texture_type(sg_image_type t) { switch (t) { case SG_IMAGETYPE_2D: return MTLTextureType2D; case SG_IMAGETYPE_CUBE: return MTLTextureTypeCube; case SG_IMAGETYPE_3D: return MTLTextureType3D; case SG_IMAGETYPE_ARRAY: return MTLTextureType2DArray; default: SOKOL_UNREACHABLE; return (MTLTextureType)0; } } _SOKOL_PRIVATE bool _sg_mtl_is_pvrtc(sg_pixel_format fmt) { switch (fmt) { case SG_PIXELFORMAT_PVRTC2_RGB: case SG_PIXELFORMAT_PVRTC2_RGBA: case SG_PIXELFORMAT_PVRTC4_RGB: case SG_PIXELFORMAT_PVRTC4_RGBA: return true; default: return false; } } _SOKOL_PRIVATE MTLSamplerAddressMode _sg_mtl_address_mode(sg_wrap w) { switch (w) { case SG_WRAP_REPEAT: return MTLSamplerAddressModeRepeat; case SG_WRAP_CLAMP_TO_EDGE: return MTLSamplerAddressModeClampToEdge; case SG_WRAP_MIRRORED_REPEAT: return MTLSamplerAddressModeMirrorRepeat; default: SOKOL_UNREACHABLE; return (MTLSamplerAddressMode)0; } } _SOKOL_PRIVATE MTLSamplerMinMagFilter _sg_mtl_minmag_filter(sg_filter f) { switch (f) { case SG_FILTER_NEAREST: case SG_FILTER_NEAREST_MIPMAP_NEAREST: case SG_FILTER_NEAREST_MIPMAP_LINEAR: return MTLSamplerMinMagFilterNearest; case SG_FILTER_LINEAR: case SG_FILTER_LINEAR_MIPMAP_NEAREST: case SG_FILTER_LINEAR_MIPMAP_LINEAR: return MTLSamplerMinMagFilterLinear; default: SOKOL_UNREACHABLE; return (MTLSamplerMinMagFilter)0; } } _SOKOL_PRIVATE MTLSamplerMipFilter _sg_mtl_mip_filter(sg_filter f) { switch (f) { case SG_FILTER_NEAREST: case SG_FILTER_LINEAR: return MTLSamplerMipFilterNotMipmapped; case SG_FILTER_NEAREST_MIPMAP_NEAREST: case SG_FILTER_LINEAR_MIPMAP_NEAREST: return MTLSamplerMipFilterNearest; case SG_FILTER_NEAREST_MIPMAP_LINEAR: case SG_FILTER_LINEAR_MIPMAP_LINEAR: return MTLSamplerMipFilterLinear; default: SOKOL_UNREACHABLE; return (MTLSamplerMipFilter)0; } } /*-- a pool for all Metal resource objects, with deferred release queue -------*/ _SOKOL_PRIVATE void _sg_mtl_init_pool(const sg_desc* desc) { _sg.mtl.idpool.num_slots = 2 * ( 2 * desc->buffer_pool_size + 5 * desc->image_pool_size + 4 * desc->shader_pool_size + 2 * desc->pipeline_pool_size + desc->pass_pool_size ); _sg_mtl_idpool = [NSMutableArray arrayWithCapacity:_sg.mtl.idpool.num_slots]; NSNull* null = [NSNull null]; for (uint32_t i = 0; i < _sg.mtl.idpool.num_slots; i++) { [_sg_mtl_idpool addObject:null]; } SOKOL_ASSERT([_sg_mtl_idpool count] == _sg.mtl.idpool.num_slots); /* a queue of currently free slot indices */ _sg.mtl.idpool.free_queue_top = 0; _sg.mtl.idpool.free_queue = (uint32_t*)SOKOL_MALLOC(_sg.mtl.idpool.num_slots * sizeof(uint32_t)); /* pool slot 0 is reserved! */ for (int i = _sg.mtl.idpool.num_slots-1; i >= 1; i--) { _sg.mtl.idpool.free_queue[_sg.mtl.idpool.free_queue_top++] = (uint32_t)i; } /* a circular queue which holds release items (frame index when a resource is to be released, and the resource's pool index */ _sg.mtl.idpool.release_queue_front = 0; _sg.mtl.idpool.release_queue_back = 0; _sg.mtl.idpool.release_queue = (_sg_mtl_release_item_t*)SOKOL_MALLOC(_sg.mtl.idpool.num_slots * sizeof(_sg_mtl_release_item_t)); for (uint32_t i = 0; i < _sg.mtl.idpool.num_slots; i++) { _sg.mtl.idpool.release_queue[i].frame_index = 0; _sg.mtl.idpool.release_queue[i].slot_index = _SG_MTL_INVALID_SLOT_INDEX; } } _SOKOL_PRIVATE void _sg_mtl_destroy_pool(void) { SOKOL_FREE(_sg.mtl.idpool.release_queue); _sg.mtl.idpool.release_queue = 0; SOKOL_FREE(_sg.mtl.idpool.free_queue); _sg.mtl.idpool.free_queue = 0; _sg_mtl_idpool = nil; } /* get a new free resource pool slot */ _SOKOL_PRIVATE uint32_t _sg_mtl_alloc_pool_slot(void) { SOKOL_ASSERT(_sg.mtl.idpool.free_queue_top > 0); const uint32_t slot_index = _sg.mtl.idpool.free_queue[--_sg.mtl.idpool.free_queue_top]; SOKOL_ASSERT((slot_index > 0) && (slot_index < _sg.mtl.idpool.num_slots)); return slot_index; } /* put a free resource pool slot back into the free-queue */ _SOKOL_PRIVATE void _sg_mtl_free_pool_slot(uint32_t slot_index) { SOKOL_ASSERT(_sg.mtl.idpool.free_queue_top < _sg.mtl.idpool.num_slots); SOKOL_ASSERT((slot_index > 0) && (slot_index < _sg.mtl.idpool.num_slots)); _sg.mtl.idpool.free_queue[_sg.mtl.idpool.free_queue_top++] = slot_index; } /* add an MTLResource to the pool, return pool index or 0 if input was 'nil' */ _SOKOL_PRIVATE uint32_t _sg_mtl_add_resource(id res) { if (nil == res) { return _SG_MTL_INVALID_SLOT_INDEX; } const uint32_t slot_index = _sg_mtl_alloc_pool_slot(); SOKOL_ASSERT([NSNull null] == _sg_mtl_idpool[slot_index]); _sg_mtl_idpool[slot_index] = res; return slot_index; } /* mark an MTLResource for release, this will put the resource into the deferred-release queue, and the resource will then be released N frames later, the special pool index 0 will be ignored (this means that a nil value was provided to _sg_mtl_add_resource() */ _SOKOL_PRIVATE void _sg_mtl_release_resource(uint32_t frame_index, uint32_t slot_index) { if (slot_index == _SG_MTL_INVALID_SLOT_INDEX) { return; } SOKOL_ASSERT((slot_index > 0) && (slot_index < _sg.mtl.idpool.num_slots)); SOKOL_ASSERT([NSNull null] != _sg_mtl_idpool[slot_index]); int release_index = _sg.mtl.idpool.release_queue_front++; if (_sg.mtl.idpool.release_queue_front >= _sg.mtl.idpool.num_slots) { /* wrap-around */ _sg.mtl.idpool.release_queue_front = 0; } /* release queue full? */ SOKOL_ASSERT(_sg.mtl.idpool.release_queue_front != _sg.mtl.idpool.release_queue_back); SOKOL_ASSERT(0 == _sg.mtl.idpool.release_queue[release_index].frame_index); const uint32_t safe_to_release_frame_index = frame_index + SG_NUM_INFLIGHT_FRAMES + 1; _sg.mtl.idpool.release_queue[release_index].frame_index = safe_to_release_frame_index; _sg.mtl.idpool.release_queue[release_index].slot_index = slot_index; } /* run garbage-collection pass on all resources in the release-queue */ _SOKOL_PRIVATE void _sg_mtl_garbage_collect(uint32_t frame_index) { while (_sg.mtl.idpool.release_queue_back != _sg.mtl.idpool.release_queue_front) { if (frame_index < _sg.mtl.idpool.release_queue[_sg.mtl.idpool.release_queue_back].frame_index) { /* don't need to check further, release-items past this are too young */ break; } /* safe to release this resource */ const uint32_t slot_index = _sg.mtl.idpool.release_queue[_sg.mtl.idpool.release_queue_back].slot_index; SOKOL_ASSERT((slot_index > 0) && (slot_index < _sg.mtl.idpool.num_slots)); SOKOL_ASSERT(_sg_mtl_idpool[slot_index] != [NSNull null]); _sg_mtl_idpool[slot_index] = [NSNull null]; /* put the now free pool index back on the free queue */ _sg_mtl_free_pool_slot(slot_index); /* reset the release queue slot and advance the back index */ _sg.mtl.idpool.release_queue[_sg.mtl.idpool.release_queue_back].frame_index = 0; _sg.mtl.idpool.release_queue[_sg.mtl.idpool.release_queue_back].slot_index = _SG_MTL_INVALID_SLOT_INDEX; _sg.mtl.idpool.release_queue_back++; if (_sg.mtl.idpool.release_queue_back >= _sg.mtl.idpool.num_slots) { /* wrap-around */ _sg.mtl.idpool.release_queue_back = 0; } } } /*-- a very simple sampler cache ----------------------------------------------- since there's only a small number of different samplers, sampler objects will never be deleted (except on shutdown), and searching an identical sampler is a simple linear search */ /* initialize the sampler cache */ _SOKOL_PRIVATE void _sg_mtl_init_sampler_cache(const sg_desc* desc) { SOKOL_ASSERT(desc->mtl_sampler_cache_size > 0); _sg.mtl.sampler_cache.capacity = desc->mtl_sampler_cache_size; _sg.mtl.sampler_cache.num_items = 0; const int size = _sg.mtl.sampler_cache.capacity * sizeof(_sg_mtl_sampler_cache_item_t); _sg.mtl.sampler_cache.items = (_sg_mtl_sampler_cache_item_t*)SOKOL_MALLOC(size); memset(_sg.mtl.sampler_cache.items, 0, size); } /* destroy the sampler cache, and release all sampler objects */ _SOKOL_PRIVATE void _sg_mtl_destroy_sampler_cache(uint32_t frame_index) { SOKOL_ASSERT(_sg.mtl.sampler_cache.items); SOKOL_ASSERT(_sg.mtl.sampler_cache.num_items <= _sg.mtl.sampler_cache.capacity); for (int i = 0; i < _sg.mtl.sampler_cache.num_items; i++) { _sg_mtl_release_resource(frame_index, _sg.mtl.sampler_cache.items[i].mtl_sampler_state); } SOKOL_FREE(_sg.mtl.sampler_cache.items); _sg.mtl.sampler_cache.items = 0; _sg.mtl.sampler_cache.num_items = 0; _sg.mtl.sampler_cache.capacity = 0; } /* create and add an MTLSamplerStateObject and return its resource pool index, reuse identical sampler state if one exists */ _SOKOL_PRIVATE uint32_t _sg_mtl_create_sampler(id mtl_device, const sg_image_desc* img_desc) { SOKOL_ASSERT(img_desc); SOKOL_ASSERT(_sg.mtl.sampler_cache.items); /* sampler state cache is full */ const sg_filter min_filter = img_desc->min_filter; const sg_filter mag_filter = img_desc->mag_filter; const sg_wrap wrap_u = img_desc->wrap_u; const sg_wrap wrap_v = img_desc->wrap_v; const sg_wrap wrap_w = img_desc->wrap_w; const uint32_t max_anisotropy = img_desc->max_anisotropy; /* convert floats to valid int for proper comparison */ const int min_lod = (int)(img_desc->min_lod * 1000.0f); const int max_lod = (int)(_sg_clamp(img_desc->max_lod, 0.0f, 1000.0f) * 1000.0f); /* first try to find identical sampler, number of samplers will be small, so linear search is ok */ for (int i = 0; i < _sg.mtl.sampler_cache.num_items; i++) { _sg_mtl_sampler_cache_item_t* item = &_sg.mtl.sampler_cache.items[i]; if ((min_filter == item->min_filter) && (mag_filter == item->mag_filter) && (wrap_u == item->wrap_u) && (wrap_v == item->wrap_v) && (wrap_w == item->wrap_w) && (max_anisotropy == item->max_anisotropy) && (min_lod == item->min_lod) && (max_lod == item->max_lod)) { return item->mtl_sampler_state; } } /* fallthrough: need to create a new MTLSamplerState object */ SOKOL_ASSERT(_sg.mtl.sampler_cache.num_items < _sg.mtl.sampler_cache.capacity); _sg_mtl_sampler_cache_item_t* new_item = &_sg.mtl.sampler_cache.items[_sg.mtl.sampler_cache.num_items++]; new_item->min_filter = min_filter; new_item->mag_filter = mag_filter; new_item->wrap_u = wrap_u; new_item->wrap_v = wrap_v; new_item->wrap_w = wrap_w; new_item->min_lod = min_lod; new_item->max_lod = max_lod; new_item->max_anisotropy = max_anisotropy; MTLSamplerDescriptor* mtl_desc = [[MTLSamplerDescriptor alloc] init]; mtl_desc.sAddressMode = _sg_mtl_address_mode(wrap_u); mtl_desc.tAddressMode = _sg_mtl_address_mode(wrap_v); if (SG_IMAGETYPE_3D == img_desc->type) { mtl_desc.rAddressMode = _sg_mtl_address_mode(wrap_w); } mtl_desc.minFilter = _sg_mtl_minmag_filter(min_filter); mtl_desc.magFilter = _sg_mtl_minmag_filter(mag_filter); mtl_desc.mipFilter = _sg_mtl_mip_filter(min_filter); mtl_desc.lodMinClamp = img_desc->min_lod; mtl_desc.lodMaxClamp = img_desc->max_lod; mtl_desc.maxAnisotropy = max_anisotropy; mtl_desc.normalizedCoordinates = YES; id mtl_sampler = [mtl_device newSamplerStateWithDescriptor:mtl_desc]; new_item->mtl_sampler_state = _sg_mtl_add_resource(mtl_sampler); return new_item->mtl_sampler_state; } /*-- a simple state cache for the resource bindings --------------------------*/ _SOKOL_PRIVATE void _sg_mtl_clear_state_cache(void) { memset(&_sg.mtl.state_cache, 0, sizeof(_sg.mtl.state_cache)); } /*-- main Metal backend state and functions ----------------------------------*/ _SOKOL_PRIVATE void _sg_setup_backend(const sg_desc* desc) { /* assume already zero-initialized */ SOKOL_ASSERT(desc); SOKOL_ASSERT(desc->mtl_device); SOKOL_ASSERT(desc->mtl_renderpass_descriptor_cb); SOKOL_ASSERT(desc->mtl_drawable_cb); SOKOL_ASSERT(desc->mtl_global_uniform_buffer_size > 0); _sg_mtl_init_pool(desc); _sg_mtl_init_sampler_cache(desc); _sg_mtl_clear_state_cache(); _sg.mtl.valid = true; _sg.mtl.renderpass_descriptor_cb = desc->mtl_renderpass_descriptor_cb; _sg.mtl.drawable_cb = desc->mtl_drawable_cb; _sg.mtl.frame_index = 1; _sg.mtl.ub_size = desc->mtl_global_uniform_buffer_size; _sg_mtl_sem = dispatch_semaphore_create(SG_NUM_INFLIGHT_FRAMES); _sg_mtl_device = (__bridge id) desc->mtl_device; _sg_mtl_cmd_queue = [_sg_mtl_device newCommandQueue]; MTLResourceOptions res_opts = MTLResourceCPUCacheModeWriteCombined; #if defined(TARGET_OS_IPHONE) && !TARGET_OS_IPHONE res_opts |= MTLResourceStorageModeManaged; #endif for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { _sg_mtl_uniform_buffers[i] = [_sg_mtl_device newBufferWithLength:_sg.mtl.ub_size options:res_opts ]; } } _SOKOL_PRIVATE void _sg_discard_backend(void) { SOKOL_ASSERT(_sg.mtl.valid); /* wait for the last frame to finish */ for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { dispatch_semaphore_wait(_sg_mtl_sem, DISPATCH_TIME_FOREVER); } _sg_mtl_destroy_sampler_cache(_sg.mtl.frame_index); _sg_mtl_garbage_collect(_sg.mtl.frame_index + SG_NUM_INFLIGHT_FRAMES + 2); _sg_mtl_destroy_pool(); _sg.mtl.valid = false; _sg_mtl_cmd_encoder = nil; _sg_mtl_cmd_buffer = nil; _sg_mtl_cmd_queue = nil; for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { _sg_mtl_uniform_buffers[i] = nil; } _sg_mtl_device = nil; } _SOKOL_PRIVATE bool _sg_query_feature(sg_feature f) { switch (f) { case SG_FEATURE_INSTANCING: #if defined(TARGET_OS_IPHONE) && !TARGET_OS_IPHONE case SG_FEATURE_TEXTURE_COMPRESSION_DXT: #else case SG_FEATURE_TEXTURE_COMPRESSION_PVRTC: case SG_FEATURE_TEXTURE_COMPRESSION_ETC2: #endif case SG_FEATURE_TEXTURE_FLOAT: case SG_FEATURE_ORIGIN_TOP_LEFT: case SG_FEATURE_MSAA_RENDER_TARGETS: case SG_FEATURE_PACKED_VERTEX_FORMAT_10_2: case SG_FEATURE_MULTIPLE_RENDER_TARGET: case SG_FEATURE_IMAGETYPE_3D: case SG_FEATURE_IMAGETYPE_ARRAY: return true; default: return false; } } _SOKOL_PRIVATE void _sg_reset_state_cache(void) { _sg_mtl_clear_state_cache(); } _SOKOL_PRIVATE sg_resource_state _sg_create_context(_sg_context_t* ctx) { SOKOL_ASSERT(ctx); _SOKOL_UNUSED(ctx); return SG_RESOURCESTATE_VALID; } _SOKOL_PRIVATE void _sg_destroy_context(_sg_context_t* ctx) { SOKOL_ASSERT(ctx); _SOKOL_UNUSED(ctx); /* empty */ } _SOKOL_PRIVATE void _sg_activate_context(_sg_context_t* ctx) { _sg_reset_state_cache(); } _SOKOL_PRIVATE sg_resource_state _sg_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { SOKOL_ASSERT(buf && desc); buf->size = desc->size; buf->append_pos = 0; buf->append_overflow = false; buf->type = desc->type; buf->usage = desc->usage; buf->update_frame_index = 0; buf->append_frame_index = 0; buf->num_slots = (buf->usage == SG_USAGE_IMMUTABLE) ? 1 : SG_NUM_INFLIGHT_FRAMES; buf->active_slot = 0; const bool injected = (0 != desc->mtl_buffers[0]); MTLResourceOptions mtl_options = _sg_mtl_buffer_resource_options(buf->usage); for (int slot = 0; slot < buf->num_slots; slot++) { id mtl_buf; if (injected) { SOKOL_ASSERT(desc->mtl_buffers[slot]); mtl_buf = (__bridge id) desc->mtl_buffers[slot]; } else { if (buf->usage == SG_USAGE_IMMUTABLE) { SOKOL_ASSERT(desc->content); mtl_buf = [_sg_mtl_device newBufferWithBytes:desc->content length:buf->size options:mtl_options]; } else { mtl_buf = [_sg_mtl_device newBufferWithLength:buf->size options:mtl_options]; } } buf->mtl_buf[slot] = _sg_mtl_add_resource(mtl_buf); } return SG_RESOURCESTATE_VALID; } _SOKOL_PRIVATE void _sg_destroy_buffer(_sg_buffer_t* buf) { SOKOL_ASSERT(buf); for (int slot = 0; slot < buf->num_slots; slot++) { /* it's valid to call release resource with '0' */ _sg_mtl_release_resource(_sg.mtl.frame_index, buf->mtl_buf[slot]); } } _SOKOL_PRIVATE void _sg_mtl_copy_image_content(const _sg_image_t* img, __unsafe_unretained id mtl_tex, const sg_image_content* content) { const int num_faces = (img->type == SG_IMAGETYPE_CUBE) ? 6:1; const int num_slices = (img->type == SG_IMAGETYPE_ARRAY) ? img->depth : 1; for (int face_index = 0; face_index < num_faces; face_index++) { for (int mip_index = 0; mip_index < img->num_mipmaps; mip_index++) { SOKOL_ASSERT(content->subimage[face_index][mip_index].ptr); SOKOL_ASSERT(content->subimage[face_index][mip_index].size > 0); const uint8_t* data_ptr = (const uint8_t*)content->subimage[face_index][mip_index].ptr; const int mip_width = _sg_max(img->width >> mip_index, 1); const int mip_height = _sg_max(img->height >> mip_index, 1); /* special case PVRTC formats: bytePerRow must be 0 */ int bytes_per_row = 0; int bytes_per_slice = _sg_surface_pitch(img->pixel_format, mip_width, mip_height); if (!_sg_mtl_is_pvrtc(img->pixel_format)) { bytes_per_row = _sg_row_pitch(img->pixel_format, mip_width); } MTLRegion region; if (img->type == SG_IMAGETYPE_3D) { const int mip_depth = _sg_max(img->depth >> mip_index, 1); region = MTLRegionMake3D(0, 0, 0, mip_width, mip_height, mip_depth); /* FIXME: apparently the minimal bytes_per_image size for 3D texture is 4 KByte... somehow need to handle this */ } else { region = MTLRegionMake2D(0, 0, mip_width, mip_height); } for (int slice_index = 0; slice_index < num_slices; slice_index++) { const int mtl_slice_index = (img->type == SG_IMAGETYPE_CUBE) ? face_index : slice_index; const int slice_offset = slice_index * bytes_per_slice; SOKOL_ASSERT((slice_offset + bytes_per_slice) <= (int)content->subimage[face_index][mip_index].size); [mtl_tex replaceRegion:region mipmapLevel:mip_index slice:mtl_slice_index withBytes:data_ptr + slice_offset bytesPerRow:bytes_per_row bytesPerImage:bytes_per_slice]; } } } } /* FIXME: METAL RESOURCE STORAGE MODE FOR macOS AND iOS For immutable textures on macOS, the recommended procedure is to create a MTLStorageModeManaged texture with the immutable content first, and then use the GPU to blit the content into a MTLStorageModePrivate texture before the first use. On iOS use the same one-time-blit procedure, but from a MTLStorageModeShared to a MTLStorageModePrivate texture. It probably makes sense to handle this in a separate 'resource manager' with a recycable pool of blit-source-textures? */ /* initialize MTLTextureDescritor with common attributes */ _SOKOL_PRIVATE bool _sg_mtl_init_texdesc_common(MTLTextureDescriptor* mtl_desc, _sg_image_t* img) { mtl_desc.textureType = _sg_mtl_texture_type(img->type); if (img->render_target) { if (_sg_is_valid_rendertarget_color_format(img->pixel_format)) { mtl_desc.pixelFormat = _sg_mtl_rendertarget_color_format(img->pixel_format); } else { mtl_desc.pixelFormat = _sg_mtl_rendertarget_depth_format(img->pixel_format); } } else { mtl_desc.pixelFormat = _sg_mtl_texture_format(img->pixel_format); } if (MTLPixelFormatInvalid == mtl_desc.pixelFormat) { SOKOL_LOG("Unsupported texture pixel format!\n"); return false; } mtl_desc.width = img->width; mtl_desc.height = img->height; if (SG_IMAGETYPE_3D == img->type) { mtl_desc.depth = img->depth; } else { mtl_desc.depth = 1; } mtl_desc.mipmapLevelCount = img->num_mipmaps; if (SG_IMAGETYPE_ARRAY == img->type) { mtl_desc.arrayLength = img->depth; } else { mtl_desc.arrayLength = 1; } mtl_desc.usage = MTLTextureUsageShaderRead; if (img->usage != SG_USAGE_IMMUTABLE) { mtl_desc.cpuCacheMode = MTLCPUCacheModeWriteCombined; } #if defined(TARGET_OS_IPHONE) && !TARGET_OS_IPHONE /* macOS: use managed textures */ mtl_desc.resourceOptions = MTLResourceStorageModeManaged; mtl_desc.storageMode = MTLStorageModeManaged; #else /* iOS: use CPU/GPU shared memory */ mtl_desc.resourceOptions = MTLResourceStorageModeShared; mtl_desc.storageMode = MTLStorageModeShared; #endif return true; } /* initialize MTLTextureDescritor with rendertarget attributes */ _SOKOL_PRIVATE void _sg_mtl_init_texdesc_rt(MTLTextureDescriptor* mtl_desc, _sg_image_t* img) { SOKOL_ASSERT(img->render_target); /* reset the cpuCacheMode to 'default' */ mtl_desc.cpuCacheMode = MTLCPUCacheModeDefaultCache; /* render targets are only visible to the GPU */ mtl_desc.resourceOptions = MTLResourceStorageModePrivate; mtl_desc.storageMode = MTLStorageModePrivate; /* non-MSAA render targets are shader-readable */ mtl_desc.usage = MTLTextureUsageShaderRead | MTLTextureUsageRenderTarget; } /* initialize MTLTextureDescritor with MSAA attributes */ _SOKOL_PRIVATE void _sg_mtl_init_texdesc_rt_msaa(MTLTextureDescriptor* mtl_desc, _sg_image_t* img) { SOKOL_ASSERT(img->sample_count > 1); /* reset the cpuCacheMode to 'default' */ mtl_desc.cpuCacheMode = MTLCPUCacheModeDefaultCache; /* render targets are only visible to the GPU */ mtl_desc.resourceOptions = MTLResourceStorageModePrivate; mtl_desc.storageMode = MTLStorageModePrivate; /* MSAA render targets are not shader-readable (instead they are resolved) */ mtl_desc.usage = MTLTextureUsageRenderTarget; mtl_desc.textureType = MTLTextureType2DMultisample; mtl_desc.depth = 1; mtl_desc.arrayLength = 1; mtl_desc.mipmapLevelCount = 1; mtl_desc.sampleCount = img->sample_count; } _SOKOL_PRIVATE sg_resource_state _sg_create_image(_sg_image_t* img, const sg_image_desc* desc) { SOKOL_ASSERT(img && desc); img->type = desc->type; img->render_target = desc->render_target; img->width = desc->width; img->height = desc->height; img->depth = desc->depth; img->num_mipmaps = desc->num_mipmaps; img->usage = desc->usage; img->pixel_format = desc->pixel_format; img->sample_count = desc->sample_count; img->min_filter = desc->min_filter; img->mag_filter = desc->mag_filter; img->wrap_u = desc->wrap_u; img->wrap_v = desc->wrap_v; img->wrap_w = desc->wrap_w; img->max_anisotropy = desc->max_anisotropy; img->upd_frame_index = 0; img->num_slots = (img->usage == SG_USAGE_IMMUTABLE) ? 1 :SG_NUM_INFLIGHT_FRAMES; img->active_slot = 0; const bool injected = (0 != desc->mtl_textures[0]); const bool msaa = (img->sample_count > 1); /* first initialize all Metal resource pool slots to 'empty' */ for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { img->mtl_tex[i] = _sg_mtl_add_resource(nil); } img->mtl_sampler_state = _sg_mtl_add_resource(nil); img->mtl_depth_tex = _sg_mtl_add_resource(nil); img->mtl_msaa_tex = _sg_mtl_add_resource(nil); /* initialize a Metal texture descriptor with common attributes */ MTLTextureDescriptor* mtl_desc = [[MTLTextureDescriptor alloc] init]; if (!_sg_mtl_init_texdesc_common(mtl_desc, img)) { return SG_RESOURCESTATE_FAILED; } /* special case depth-stencil-buffer? */ if (_sg_is_valid_rendertarget_depth_format(img->pixel_format)) { /* depth-stencil buffer texture must always be a render target */ SOKOL_ASSERT(img->render_target); SOKOL_ASSERT(img->type == SG_IMAGETYPE_2D); SOKOL_ASSERT(img->num_mipmaps == 1); SOKOL_ASSERT(!injected); if (msaa) { _sg_mtl_init_texdesc_rt_msaa(mtl_desc, img); } else { _sg_mtl_init_texdesc_rt(mtl_desc, img); } id tex = [_sg_mtl_device newTextureWithDescriptor:mtl_desc]; SOKOL_ASSERT(nil != tex); img->mtl_depth_tex = _sg_mtl_add_resource(tex); } else { /* create the color texture In case this is a render target without MSAA, add the relevant render-target descriptor attributes. In case this is a render target *with* MSAA, the color texture will serve as MSAA-resolve target (not as render target), and rendering will go into a separate render target texture of type MTLTextureType2DMultisample. */ if (img->render_target && !msaa) { _sg_mtl_init_texdesc_rt(mtl_desc, img); } for (int slot = 0; slot < img->num_slots; slot++) { id tex; if (injected) { SOKOL_ASSERT(desc->mtl_textures[slot]); tex = (__bridge id) desc->mtl_textures[slot]; } else { tex = [_sg_mtl_device newTextureWithDescriptor:mtl_desc]; if ((img->usage == SG_USAGE_IMMUTABLE) && !img->render_target) { _sg_mtl_copy_image_content(img, tex, &desc->content); } } img->mtl_tex[slot] = _sg_mtl_add_resource(tex); } /* if MSAA color render target, create an additional MSAA render-surface texture */ if (img->render_target && msaa) { _sg_mtl_init_texdesc_rt_msaa(mtl_desc, img); id tex = [_sg_mtl_device newTextureWithDescriptor:mtl_desc]; img->mtl_msaa_tex = _sg_mtl_add_resource(tex); } /* create (possibly shared) sampler state */ img->mtl_sampler_state = _sg_mtl_create_sampler(_sg_mtl_device, desc); } return SG_RESOURCESTATE_VALID; } _SOKOL_PRIVATE void _sg_destroy_image(_sg_image_t* img) { SOKOL_ASSERT(img); /* it's valid to call release resource with a 'null resource' */ for (int slot = 0; slot < img->num_slots; slot++) { _sg_mtl_release_resource(_sg.mtl.frame_index, img->mtl_tex[slot]); } _sg_mtl_release_resource(_sg.mtl.frame_index, img->mtl_depth_tex); _sg_mtl_release_resource(_sg.mtl.frame_index, img->mtl_msaa_tex); /* NOTE: sampler state objects are shared and not released until shutdown */ } _SOKOL_PRIVATE id _sg_mtl_compile_library(const char* src) { NSError* err = NULL; id lib = [_sg_mtl_device newLibraryWithSource:[NSString stringWithUTF8String:src] options:nil error:&err ]; if (err) { SOKOL_LOG([err.localizedDescription UTF8String]); } return lib; } _SOKOL_PRIVATE id _sg_mtl_library_from_bytecode(const uint8_t* ptr, int num_bytes) { NSError* err = NULL; dispatch_data_t lib_data = dispatch_data_create(ptr, num_bytes, NULL, DISPATCH_DATA_DESTRUCTOR_DEFAULT); id lib = [_sg_mtl_device newLibraryWithData:lib_data error:&err]; if (err) { SOKOL_LOG([err.localizedDescription UTF8String]); } return lib; } _SOKOL_PRIVATE sg_resource_state _sg_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { SOKOL_ASSERT(shd && desc); /* uniform block sizes and image types */ for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { const sg_shader_stage_desc* stage_desc = (stage_index == SG_SHADERSTAGE_VS) ? &desc->vs : &desc->fs; _sg_shader_stage_t* stage = &shd->stage[stage_index]; SOKOL_ASSERT(stage->num_uniform_blocks == 0); for (int ub_index = 0; ub_index < SG_MAX_SHADERSTAGE_UBS; ub_index++) { const sg_shader_uniform_block_desc* ub_desc = &stage_desc->uniform_blocks[ub_index]; if (0 == ub_desc->size) { break; } _sg_uniform_block_t* ub = &stage->uniform_blocks[ub_index]; ub->size = ub_desc->size; stage->num_uniform_blocks++; } SOKOL_ASSERT(stage->num_images == 0); for (int img_index = 0; img_index < SG_MAX_SHADERSTAGE_IMAGES; img_index++) { const sg_shader_image_desc* img_desc = &stage_desc->images[img_index]; if (img_desc->type == _SG_IMAGETYPE_DEFAULT) { break; } stage->images[img_index].type = img_desc->type; stage->num_images++; } } /* create metal libray objects and lookup entry functions */ id vs_lib; id fs_lib; id vs_func; id fs_func; const char* vs_entry = desc->vs.entry; const char* fs_entry = desc->fs.entry; if (desc->vs.byte_code && desc->fs.byte_code) { /* separate byte code provided */ vs_lib = _sg_mtl_library_from_bytecode(desc->vs.byte_code, desc->vs.byte_code_size); fs_lib = _sg_mtl_library_from_bytecode(desc->fs.byte_code, desc->fs.byte_code_size); if (nil == vs_lib || nil == fs_lib) { return SG_RESOURCESTATE_FAILED; } vs_func = [vs_lib newFunctionWithName:[NSString stringWithUTF8String:vs_entry]]; fs_func = [fs_lib newFunctionWithName:[NSString stringWithUTF8String:fs_entry]]; } else if (desc->vs.source && desc->fs.source) { /* separate sources provided */ vs_lib = _sg_mtl_compile_library(desc->vs.source); fs_lib = _sg_mtl_compile_library(desc->fs.source); if (nil == vs_lib || nil == fs_lib) { return SG_RESOURCESTATE_FAILED; } vs_func = [vs_lib newFunctionWithName:[NSString stringWithUTF8String:vs_entry]]; fs_func = [fs_lib newFunctionWithName:[NSString stringWithUTF8String:fs_entry]]; } else { return SG_RESOURCESTATE_FAILED; } if (nil == vs_func) { SOKOL_LOG("vertex shader entry function not found\n"); return SG_RESOURCESTATE_FAILED; } if (nil == fs_func) { SOKOL_LOG("fragment shader entry function not found\n"); return SG_RESOURCESTATE_FAILED; } /* it is legal to call _sg_mtl_add_resource with a nil value, this will return a special 0xFFFFFFFF index */ shd->stage[SG_SHADERSTAGE_VS].mtl_lib = _sg_mtl_add_resource(vs_lib); shd->stage[SG_SHADERSTAGE_FS].mtl_lib = _sg_mtl_add_resource(fs_lib); shd->stage[SG_SHADERSTAGE_VS].mtl_func = _sg_mtl_add_resource(vs_func); shd->stage[SG_SHADERSTAGE_FS].mtl_func = _sg_mtl_add_resource(fs_func); return SG_RESOURCESTATE_VALID; } _SOKOL_PRIVATE void _sg_destroy_shader(_sg_shader_t* shd) { SOKOL_ASSERT(shd); /* it is valid to call _sg_mtl_release_resource with a 'null resource' */ _sg_mtl_release_resource(_sg.mtl.frame_index, shd->stage[SG_SHADERSTAGE_VS].mtl_func); _sg_mtl_release_resource(_sg.mtl.frame_index, shd->stage[SG_SHADERSTAGE_VS].mtl_lib); _sg_mtl_release_resource(_sg.mtl.frame_index, shd->stage[SG_SHADERSTAGE_FS].mtl_func); _sg_mtl_release_resource(_sg.mtl.frame_index, shd->stage[SG_SHADERSTAGE_FS].mtl_lib); } _SOKOL_PRIVATE sg_resource_state _sg_create_pipeline(_sg_pipeline_t* pip, _sg_shader_t* shd, const sg_pipeline_desc* desc) { SOKOL_ASSERT(pip && shd && desc); SOKOL_ASSERT(desc->shader.id == shd->slot.id); pip->shader = shd; pip->shader_id = desc->shader; pip->color_attachment_count = desc->blend.color_attachment_count; pip->color_format = desc->blend.color_format; pip->depth_format = desc->blend.depth_format; pip->sample_count = desc->rasterizer.sample_count; pip->depth_bias = desc->rasterizer.depth_bias; pip->depth_bias_slope_scale = desc->rasterizer.depth_bias_slope_scale; pip->depth_bias_clamp = desc->rasterizer.depth_bias_clamp; sg_primitive_type prim_type = desc->primitive_type; pip->mtl_prim_type = _sg_mtl_primitive_type(prim_type); pip->index_type = desc->index_type; pip->mtl_index_size = _sg_mtl_index_size(pip->index_type); if (SG_INDEXTYPE_NONE != pip->index_type) { pip->mtl_index_type = _sg_mtl_index_type(pip->index_type); } pip->mtl_cull_mode = _sg_mtl_cull_mode(desc->rasterizer.cull_mode); pip->mtl_winding = _sg_mtl_winding(desc->rasterizer.face_winding); pip->mtl_stencil_ref = desc->depth_stencil.stencil_ref; for (int i = 0; i < 4; i++) { pip->blend_color[i] = desc->blend.blend_color[i]; } /* create vertex-descriptor */ MTLVertexDescriptor* vtx_desc = [MTLVertexDescriptor vertexDescriptor]; for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { const sg_vertex_attr_desc* a_desc = &desc->layout.attrs[attr_index]; if (a_desc->format == SG_VERTEXFORMAT_INVALID) { break; } SOKOL_ASSERT((a_desc->buffer_index >= 0) && (a_desc->buffer_index < SG_MAX_SHADERSTAGE_BUFFERS)); vtx_desc.attributes[attr_index].format = _sg_mtl_vertex_format(a_desc->format); vtx_desc.attributes[attr_index].offset = a_desc->offset; vtx_desc.attributes[attr_index].bufferIndex = a_desc->buffer_index + SG_MAX_SHADERSTAGE_UBS; pip->vertex_layout_valid[a_desc->buffer_index] = true; } for (int layout_index = 0; layout_index < SG_MAX_SHADERSTAGE_BUFFERS; layout_index++) { if (pip->vertex_layout_valid[layout_index]) { const sg_buffer_layout_desc* l_desc = &desc->layout.buffers[layout_index]; const int mtl_vb_slot = layout_index + SG_MAX_SHADERSTAGE_UBS; SOKOL_ASSERT(l_desc->stride > 0); vtx_desc.layouts[mtl_vb_slot].stride = l_desc->stride; vtx_desc.layouts[mtl_vb_slot].stepFunction = _sg_mtl_step_function(l_desc->step_func); vtx_desc.layouts[mtl_vb_slot].stepRate = l_desc->step_rate; } } /* render-pipeline descriptor */ MTLRenderPipelineDescriptor* rp_desc = [[MTLRenderPipelineDescriptor alloc] init]; rp_desc.vertexDescriptor = vtx_desc; SOKOL_ASSERT(shd->stage[SG_SHADERSTAGE_VS].mtl_func != _SG_MTL_INVALID_SLOT_INDEX); rp_desc.vertexFunction = _sg_mtl_idpool[shd->stage[SG_SHADERSTAGE_VS].mtl_func]; SOKOL_ASSERT(shd->stage[SG_SHADERSTAGE_FS].mtl_func != _SG_MTL_INVALID_SLOT_INDEX); rp_desc.fragmentFunction = _sg_mtl_idpool[shd->stage[SG_SHADERSTAGE_FS].mtl_func]; rp_desc.sampleCount = desc->rasterizer.sample_count; rp_desc.alphaToCoverageEnabled = desc->rasterizer.alpha_to_coverage_enabled; rp_desc.alphaToOneEnabled = NO; rp_desc.rasterizationEnabled = YES; rp_desc.depthAttachmentPixelFormat = _sg_mtl_rendertarget_depth_format(desc->blend.depth_format); rp_desc.stencilAttachmentPixelFormat = _sg_mtl_rendertarget_stencil_format(desc->blend.depth_format); /* FIXME: this only works on macOS 10.13! for (int i = 0; i < (SG_MAX_SHADERSTAGE_UBS+SG_MAX_SHADERSTAGE_BUFFERS); i++) { rp_desc.vertexBuffers[i].mutability = MTLMutabilityImmutable; } for (int i = 0; i < SG_MAX_SHADERSTAGE_UBS; i++) { rp_desc.fragmentBuffers[i].mutability = MTLMutabilityImmutable; } */ const int att_count = desc->blend.color_attachment_count; for (int i = 0; i < att_count; i++) { rp_desc.colorAttachments[i].pixelFormat = _sg_mtl_rendertarget_color_format(desc->blend.color_format); rp_desc.colorAttachments[i].writeMask = _sg_mtl_color_write_mask((sg_color_mask)desc->blend.color_write_mask); rp_desc.colorAttachments[i].blendingEnabled = desc->blend.enabled; rp_desc.colorAttachments[i].alphaBlendOperation = _sg_mtl_blend_op(desc->blend.op_alpha); rp_desc.colorAttachments[i].rgbBlendOperation = _sg_mtl_blend_op(desc->blend.op_rgb); rp_desc.colorAttachments[i].destinationAlphaBlendFactor = _sg_mtl_blend_factor(desc->blend.dst_factor_alpha); rp_desc.colorAttachments[i].destinationRGBBlendFactor = _sg_mtl_blend_factor(desc->blend.dst_factor_rgb); rp_desc.colorAttachments[i].sourceAlphaBlendFactor = _sg_mtl_blend_factor(desc->blend.src_factor_alpha); rp_desc.colorAttachments[i].sourceRGBBlendFactor = _sg_mtl_blend_factor(desc->blend.src_factor_rgb); } NSError* err = NULL; id mtl_rps = [_sg_mtl_device newRenderPipelineStateWithDescriptor:rp_desc error:&err]; if (nil == mtl_rps) { SOKOL_ASSERT(err); SOKOL_LOG([err.localizedDescription UTF8String]); return SG_RESOURCESTATE_FAILED; } /* depth-stencil-state */ MTLDepthStencilDescriptor* ds_desc = [[MTLDepthStencilDescriptor alloc] init]; ds_desc.depthCompareFunction = _sg_mtl_compare_func(desc->depth_stencil.depth_compare_func); ds_desc.depthWriteEnabled = desc->depth_stencil.depth_write_enabled; if (desc->depth_stencil.stencil_enabled) { const sg_stencil_state* sb = &desc->depth_stencil.stencil_back; ds_desc.backFaceStencil = [[MTLStencilDescriptor alloc] init]; ds_desc.backFaceStencil.stencilFailureOperation = _sg_mtl_stencil_op(sb->fail_op); ds_desc.backFaceStencil.depthFailureOperation = _sg_mtl_stencil_op(sb->depth_fail_op); ds_desc.backFaceStencil.depthStencilPassOperation = _sg_mtl_stencil_op(sb->pass_op); ds_desc.backFaceStencil.stencilCompareFunction = _sg_mtl_compare_func(sb->compare_func); ds_desc.backFaceStencil.readMask = desc->depth_stencil.stencil_read_mask; ds_desc.backFaceStencil.writeMask = desc->depth_stencil.stencil_write_mask; const sg_stencil_state* sf = &desc->depth_stencil.stencil_front; ds_desc.frontFaceStencil = [[MTLStencilDescriptor alloc] init]; ds_desc.frontFaceStencil.stencilFailureOperation = _sg_mtl_stencil_op(sf->fail_op); ds_desc.frontFaceStencil.depthFailureOperation = _sg_mtl_stencil_op(sf->depth_fail_op); ds_desc.frontFaceStencil.depthStencilPassOperation = _sg_mtl_stencil_op(sf->pass_op); ds_desc.frontFaceStencil.stencilCompareFunction = _sg_mtl_compare_func(sf->compare_func); ds_desc.frontFaceStencil.readMask = desc->depth_stencil.stencil_read_mask; ds_desc.frontFaceStencil.writeMask = desc->depth_stencil.stencil_write_mask; } id mtl_dss = [_sg_mtl_device newDepthStencilStateWithDescriptor:ds_desc]; pip->mtl_rps = _sg_mtl_add_resource(mtl_rps); pip->mtl_dss = _sg_mtl_add_resource(mtl_dss); return SG_RESOURCESTATE_VALID; } _SOKOL_PRIVATE void _sg_destroy_pipeline(_sg_pipeline_t* pip) { SOKOL_ASSERT(pip); /* it's valid to call release resource with a 'null resource' */ _sg_mtl_release_resource(_sg.mtl.frame_index, pip->mtl_rps); _sg_mtl_release_resource(_sg.mtl.frame_index, pip->mtl_dss); } _SOKOL_PRIVATE sg_resource_state _sg_create_pass(_sg_pass_t* pass, _sg_image_t** att_images, const sg_pass_desc* desc) { SOKOL_ASSERT(pass && desc); SOKOL_ASSERT(att_images && att_images[0]); /* copy image pointers and desc attributes */ const sg_attachment_desc* att_desc; _sg_attachment_t* att; for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { SOKOL_ASSERT(0 == pass->color_atts[i].image); att_desc = &desc->color_attachments[i]; if (att_desc->image.id != SG_INVALID_ID) { pass->num_color_atts++; SOKOL_ASSERT(att_images[i] && (att_images[i]->slot.id == att_desc->image.id)); SOKOL_ASSERT(_sg_is_valid_rendertarget_color_format(att_images[i]->pixel_format)); att = &pass->color_atts[i]; SOKOL_ASSERT((att->image == 0) && (att->image_id.id == SG_INVALID_ID)); att->image = att_images[i]; att->image_id = att_desc->image; att->mip_level = att_desc->mip_level; att->slice = att_desc->slice; } } SOKOL_ASSERT(0 == pass->ds_att.image); att_desc = &desc->depth_stencil_attachment; const int ds_img_index = SG_MAX_COLOR_ATTACHMENTS; if (att_desc->image.id != SG_INVALID_ID) { SOKOL_ASSERT(att_images[ds_img_index] && (att_images[ds_img_index]->slot.id == att_desc->image.id)); SOKOL_ASSERT(_sg_is_valid_rendertarget_depth_format(att_images[ds_img_index]->pixel_format)); att = &pass->ds_att; SOKOL_ASSERT((att->image == 0) && (att->image_id.id == SG_INVALID_ID)); att->image = att_images[ds_img_index]; att->image_id = att_desc->image; att->mip_level = att_desc->mip_level; att->slice = att_desc->slice; } return SG_RESOURCESTATE_VALID; } _SOKOL_PRIVATE void _sg_destroy_pass(_sg_pass_t* pass) { SOKOL_ASSERT(pass); _SOKOL_UNUSED(pass); } _SOKOL_PRIVATE void _sg_begin_pass(_sg_pass_t* pass, const sg_pass_action* action, int w, int h) { SOKOL_ASSERT(action); SOKOL_ASSERT(!_sg.mtl.in_pass); SOKOL_ASSERT(_sg_mtl_cmd_queue); SOKOL_ASSERT(!_sg_mtl_cmd_encoder); SOKOL_ASSERT(_sg.mtl.renderpass_descriptor_cb); _sg.mtl.in_pass = true; _sg.mtl.cur_width = w; _sg.mtl.cur_height = h; _sg_mtl_clear_state_cache(); /* if this is the first pass in the frame, create a command buffer */ if (nil == _sg_mtl_cmd_buffer) { /* block until the oldest frame in flight has finished */ dispatch_semaphore_wait(_sg_mtl_sem, DISPATCH_TIME_FOREVER); _sg_mtl_cmd_buffer = [_sg_mtl_cmd_queue commandBufferWithUnretainedReferences]; } /* if this is first pass in frame, get uniform buffer base pointer */ if (0 == _sg.mtl.cur_ub_base_ptr) { _sg.mtl.cur_ub_base_ptr = (uint8_t*)[_sg_mtl_uniform_buffers[_sg.mtl.cur_frame_rotate_index] contents]; } /* initialize a render pass descriptor */ MTLRenderPassDescriptor* pass_desc = nil; if (pass) { /* offscreen render pass */ pass_desc = [MTLRenderPassDescriptor renderPassDescriptor]; } else { /* default render pass, call user-provided callback to provide render pass descriptor */ pass_desc = (__bridge MTLRenderPassDescriptor*) _sg.mtl.renderpass_descriptor_cb(); } if (pass_desc) { _sg.mtl.pass_valid = true; } else { /* default pass descriptor will not be valid if window is minimized, don't do any rendering in this case */ _sg.mtl.pass_valid = false; return; } if (pass) { /* setup pass descriptor for offscreen rendering */ SOKOL_ASSERT(pass->slot.state == SG_RESOURCESTATE_VALID); for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { const _sg_attachment_t* att = &pass->color_atts[i]; if (0 == att->image) { break; } SOKOL_ASSERT(att->image->slot.state == SG_RESOURCESTATE_VALID); SOKOL_ASSERT(att->image->slot.id == att->image_id.id); const bool is_msaa = (att->image->sample_count > 1); pass_desc.colorAttachments[i].loadAction = _sg_mtl_load_action(action->colors[i].action); pass_desc.colorAttachments[i].storeAction = is_msaa ? MTLStoreActionMultisampleResolve : MTLStoreActionStore; const float* c = &(action->colors[i].val[0]); pass_desc.colorAttachments[i].clearColor = MTLClearColorMake(c[0], c[1], c[2], c[3]); if (is_msaa) { SOKOL_ASSERT(att->image->mtl_msaa_tex != _SG_MTL_INVALID_SLOT_INDEX); SOKOL_ASSERT(att->image->mtl_tex[att->image->active_slot] != _SG_MTL_INVALID_SLOT_INDEX); pass_desc.colorAttachments[i].texture = _sg_mtl_idpool[att->image->mtl_msaa_tex]; pass_desc.colorAttachments[i].resolveTexture = _sg_mtl_idpool[att->image->mtl_tex[att->image->active_slot]]; pass_desc.colorAttachments[i].resolveLevel = att->mip_level; switch (att->image->type) { case SG_IMAGETYPE_CUBE: case SG_IMAGETYPE_ARRAY: pass_desc.colorAttachments[i].resolveSlice = att->slice; break; case SG_IMAGETYPE_3D: pass_desc.colorAttachments[i].resolveDepthPlane = att->slice; break; default: break; } } else { SOKOL_ASSERT(att->image->mtl_tex[att->image->active_slot] != _SG_MTL_INVALID_SLOT_INDEX); pass_desc.colorAttachments[i].texture = _sg_mtl_idpool[att->image->mtl_tex[att->image->active_slot]]; pass_desc.colorAttachments[i].level = att->mip_level; switch (att->image->type) { case SG_IMAGETYPE_CUBE: case SG_IMAGETYPE_ARRAY: pass_desc.colorAttachments[i].slice = att->slice; break; case SG_IMAGETYPE_3D: pass_desc.colorAttachments[i].depthPlane = att->slice; break; default: break; } } } if (0 != pass->ds_att.image) { const _sg_attachment_t* att = &pass->ds_att; SOKOL_ASSERT(att->image->slot.state == SG_RESOURCESTATE_VALID); SOKOL_ASSERT(att->image->slot.id == att->image_id.id); SOKOL_ASSERT(att->image->mtl_depth_tex != _SG_MTL_INVALID_SLOT_INDEX); pass_desc.depthAttachment.texture = _sg_mtl_idpool[att->image->mtl_depth_tex]; pass_desc.depthAttachment.loadAction = _sg_mtl_load_action(action->depth.action); pass_desc.depthAttachment.clearDepth = action->depth.val; if (_sg_is_depth_stencil_format(att->image->pixel_format)) { pass_desc.stencilAttachment.texture = _sg_mtl_idpool[att->image->mtl_depth_tex]; pass_desc.stencilAttachment.loadAction = _sg_mtl_load_action(action->stencil.action); pass_desc.stencilAttachment.clearStencil = action->stencil.val; } } } else { /* setup pass descriptor for default rendering */ pass_desc.colorAttachments[0].loadAction = _sg_mtl_load_action(action->colors[0].action); const float* c = &(action->colors[0].val[0]); pass_desc.colorAttachments[0].clearColor = MTLClearColorMake(c[0], c[1], c[2], c[3]); pass_desc.depthAttachment.loadAction = _sg_mtl_load_action(action->depth.action); pass_desc.depthAttachment.clearDepth = action->depth.val; pass_desc.stencilAttachment.loadAction = _sg_mtl_load_action(action->stencil.action); pass_desc.stencilAttachment.clearStencil = action->stencil.val; } /* create a render command encoder, this might return nil if window is minimized */ _sg_mtl_cmd_encoder = [_sg_mtl_cmd_buffer renderCommandEncoderWithDescriptor:pass_desc]; if (_sg_mtl_cmd_encoder == nil) { _sg.mtl.pass_valid = false; return; } /* bind the global uniform buffer, this only happens once per pass */ for (int slot = 0; slot < SG_MAX_SHADERSTAGE_UBS; slot++) { [_sg_mtl_cmd_encoder setVertexBuffer:_sg_mtl_uniform_buffers[_sg.mtl.cur_frame_rotate_index] offset:0 atIndex:slot]; [_sg_mtl_cmd_encoder setFragmentBuffer:_sg_mtl_uniform_buffers[_sg.mtl.cur_frame_rotate_index] offset:0 atIndex:slot]; } } _SOKOL_PRIVATE void _sg_end_pass(void) { SOKOL_ASSERT(_sg.mtl.in_pass); _sg.mtl.in_pass = false; _sg.mtl.pass_valid = false; if (nil != _sg_mtl_cmd_encoder) { [_sg_mtl_cmd_encoder endEncoding]; _sg_mtl_cmd_encoder = nil; } } _SOKOL_PRIVATE void _sg_commit(void) { SOKOL_ASSERT(!_sg.mtl.in_pass); SOKOL_ASSERT(!_sg.mtl.pass_valid); SOKOL_ASSERT(_sg.mtl.drawable_cb); SOKOL_ASSERT(nil == _sg_mtl_cmd_encoder); SOKOL_ASSERT(nil != _sg_mtl_cmd_buffer); #if defined(TARGET_OS_IPHONE) && !TARGET_OS_IPHONE [_sg_mtl_uniform_buffers[_sg.mtl.cur_frame_rotate_index] didModifyRange:NSMakeRange(0, _sg.mtl.cur_ub_offset)]; #endif /* present, commit and signal semaphore when done */ id cur_drawable = (__bridge id) _sg.mtl.drawable_cb(); [_sg_mtl_cmd_buffer presentDrawable:cur_drawable]; __block dispatch_semaphore_t sem = _sg_mtl_sem; [_sg_mtl_cmd_buffer addCompletedHandler:^(id cmd_buffer) { dispatch_semaphore_signal(sem); }]; [_sg_mtl_cmd_buffer commit]; /* garbage-collect resources pending for release */ _sg_mtl_garbage_collect(_sg.mtl.frame_index); /* rotate uniform buffer slot */ if (++_sg.mtl.cur_frame_rotate_index >= SG_NUM_INFLIGHT_FRAMES) { _sg.mtl.cur_frame_rotate_index = 0; } _sg.mtl.frame_index++; _sg.mtl.cur_ub_offset = 0; _sg.mtl.cur_ub_base_ptr = 0; _sg_mtl_cmd_buffer = nil; } _SOKOL_PRIVATE void _sg_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { SOKOL_ASSERT(_sg.mtl.in_pass); if (!_sg.mtl.pass_valid) { return; } SOKOL_ASSERT(_sg_mtl_cmd_encoder); MTLViewport vp; vp.originX = (double) x; vp.originY = (double) (origin_top_left ? y : (_sg.mtl.cur_height - (y + h))); vp.width = (double) w; vp.height = (double) h; vp.znear = 0.0; vp.zfar = 1.0; [_sg_mtl_cmd_encoder setViewport:vp]; } _SOKOL_PRIVATE void _sg_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) { SOKOL_ASSERT(_sg.mtl.in_pass); if (!_sg.mtl.pass_valid) { return; } SOKOL_ASSERT(_sg_mtl_cmd_encoder); /* clip against framebuffer rect */ x = _sg_min(_sg_max(0, x), _sg.mtl.cur_width-1); y = _sg_min(_sg_max(0, y), _sg.mtl.cur_height-1); if ((x + w) > _sg.mtl.cur_width) { w = _sg.mtl.cur_width - x; } if ((y + h) > _sg.mtl.cur_height) { h = _sg.mtl.cur_height - y; } w = _sg_max(w, 1); h = _sg_max(h, 1); MTLScissorRect r; r.x = x; r.y = origin_top_left ? y : (_sg.mtl.cur_height - (y + h)); r.width = w; r.height = h; [_sg_mtl_cmd_encoder setScissorRect:r]; } _SOKOL_PRIVATE void _sg_apply_pipeline(_sg_pipeline_t* pip) { SOKOL_ASSERT(pip); SOKOL_ASSERT(pip->shader); SOKOL_ASSERT(_sg.mtl.in_pass); if (!_sg.mtl.pass_valid) { return; } SOKOL_ASSERT(_sg_mtl_cmd_encoder); if ((_sg.mtl.state_cache.cur_pipeline != pip) || (_sg.mtl.state_cache.cur_pipeline_id.id != pip->slot.id)) { _sg.mtl.state_cache.cur_pipeline = pip; _sg.mtl.state_cache.cur_pipeline_id.id = pip->slot.id; const float* c = pip->blend_color; [_sg_mtl_cmd_encoder setBlendColorRed:c[0] green:c[1] blue:c[2] alpha:c[3]]; [_sg_mtl_cmd_encoder setCullMode:pip->mtl_cull_mode]; [_sg_mtl_cmd_encoder setFrontFacingWinding:pip->mtl_winding]; [_sg_mtl_cmd_encoder setStencilReferenceValue:pip->mtl_stencil_ref]; [_sg_mtl_cmd_encoder setDepthBias:pip->depth_bias slopeScale:pip->depth_bias_slope_scale clamp:pip->depth_bias_clamp]; SOKOL_ASSERT(pip->mtl_rps != _SG_MTL_INVALID_SLOT_INDEX); [_sg_mtl_cmd_encoder setRenderPipelineState:_sg_mtl_idpool[pip->mtl_rps]]; SOKOL_ASSERT(pip->mtl_dss != _SG_MTL_INVALID_SLOT_INDEX); [_sg_mtl_cmd_encoder setDepthStencilState:_sg_mtl_idpool[pip->mtl_dss]]; } } _SOKOL_PRIVATE void _sg_apply_bindings( _sg_pipeline_t* pip, _sg_buffer_t** vbs, const int* vb_offsets, int num_vbs, _sg_buffer_t* ib, int ib_offset, _sg_image_t** vs_imgs, int num_vs_imgs, _sg_image_t** fs_imgs, int num_fs_imgs) { SOKOL_ASSERT(_sg.mtl.in_pass); if (!_sg.mtl.pass_valid) { return; } SOKOL_ASSERT(_sg_mtl_cmd_encoder); /* store index buffer binding, this will be needed later in sg_draw() */ _sg.mtl.state_cache.cur_indexbuffer = ib; _sg.mtl.state_cache.cur_indexbuffer_offset = ib_offset; if (ib) { SOKOL_ASSERT(pip->index_type != SG_INDEXTYPE_NONE); _sg.mtl.state_cache.cur_indexbuffer_id.id = ib->slot.id; } else { SOKOL_ASSERT(pip->index_type == SG_INDEXTYPE_NONE); _sg.mtl.state_cache.cur_indexbuffer_id.id = SG_INVALID_ID; } /* apply vertex buffers */ int slot; for (slot = 0; slot < num_vbs; slot++) { const _sg_buffer_t* vb = vbs[slot]; if ((_sg.mtl.state_cache.cur_vertexbuffers[slot] != vb) || (_sg.mtl.state_cache.cur_vertexbuffer_offsets[slot] != vb_offsets[slot]) || (_sg.mtl.state_cache.cur_vertexbuffer_ids[slot].id != vb->slot.id)) { _sg.mtl.state_cache.cur_vertexbuffers[slot] = vb; _sg.mtl.state_cache.cur_vertexbuffer_offsets[slot] = vb_offsets[slot]; _sg.mtl.state_cache.cur_vertexbuffer_ids[slot].id = vb->slot.id; const NSUInteger mtl_slot = SG_MAX_SHADERSTAGE_UBS + slot; SOKOL_ASSERT(vb->mtl_buf[vb->active_slot] != _SG_MTL_INVALID_SLOT_INDEX); [_sg_mtl_cmd_encoder setVertexBuffer:_sg_mtl_idpool[vb->mtl_buf[vb->active_slot]] offset:vb_offsets[slot] atIndex:mtl_slot]; } } /* apply vertex shader images */ for (slot = 0; slot < num_vs_imgs; slot++) { const _sg_image_t* img = vs_imgs[slot]; if ((_sg.mtl.state_cache.cur_vs_images[slot] != img) || (_sg.mtl.state_cache.cur_vs_image_ids[slot].id != img->slot.id)) { _sg.mtl.state_cache.cur_vs_images[slot] = img; _sg.mtl.state_cache.cur_vs_image_ids[slot].id = img->slot.id; SOKOL_ASSERT(img->mtl_tex[img->active_slot] != _SG_MTL_INVALID_SLOT_INDEX); [_sg_mtl_cmd_encoder setVertexTexture:_sg_mtl_idpool[img->mtl_tex[img->active_slot]] atIndex:slot]; SOKOL_ASSERT(img->mtl_sampler_state != _SG_MTL_INVALID_SLOT_INDEX); [_sg_mtl_cmd_encoder setVertexSamplerState:_sg_mtl_idpool[img->mtl_sampler_state] atIndex:slot]; } } /* apply fragment shader images */ for (slot = 0; slot < num_fs_imgs; slot++) { const _sg_image_t* img = fs_imgs[slot]; if ((_sg.mtl.state_cache.cur_fs_images[slot] != img) || (_sg.mtl.state_cache.cur_fs_image_ids[slot].id != img->slot.id)) { _sg.mtl.state_cache.cur_fs_images[slot] = img; _sg.mtl.state_cache.cur_fs_image_ids[slot].id = img->slot.id; SOKOL_ASSERT(img->mtl_tex[img->active_slot] != _SG_MTL_INVALID_SLOT_INDEX); [_sg_mtl_cmd_encoder setFragmentTexture:_sg_mtl_idpool[img->mtl_tex[img->active_slot]] atIndex:slot]; SOKOL_ASSERT(img->mtl_sampler_state != _SG_MTL_INVALID_SLOT_INDEX); [_sg_mtl_cmd_encoder setFragmentSamplerState:_sg_mtl_idpool[img->mtl_sampler_state] atIndex:slot]; } } } #define _sg_mtl_roundup(val, round_to) (((val)+((round_to)-1))&~((round_to)-1)) _SOKOL_PRIVATE void _sg_apply_uniforms(sg_shader_stage stage_index, int ub_index, const void* data, int num_bytes) { SOKOL_ASSERT(_sg.mtl.in_pass); if (!_sg.mtl.pass_valid) { return; } SOKOL_ASSERT(_sg_mtl_cmd_encoder); SOKOL_ASSERT(data && (num_bytes > 0)); SOKOL_ASSERT((stage_index >= 0) && ((int)stage_index < SG_NUM_SHADER_STAGES)); SOKOL_ASSERT((ub_index >= 0) && (ub_index < SG_MAX_SHADERSTAGE_UBS)); SOKOL_ASSERT((_sg.mtl.cur_ub_offset + num_bytes) <= _sg.mtl.ub_size); SOKOL_ASSERT((_sg.mtl.cur_ub_offset & (_SG_MTL_UB_ALIGN-1)) == 0); SOKOL_ASSERT(_sg.mtl.state_cache.cur_pipeline && _sg.mtl.state_cache.cur_pipeline->shader); SOKOL_ASSERT(_sg.mtl.state_cache.cur_pipeline->slot.id == _sg.mtl.state_cache.cur_pipeline_id.id); SOKOL_ASSERT(_sg.mtl.state_cache.cur_pipeline->shader->slot.id == _sg.mtl.state_cache.cur_pipeline->shader_id.id); SOKOL_ASSERT(ub_index < _sg.mtl.state_cache.cur_pipeline->shader->stage[stage_index].num_uniform_blocks); SOKOL_ASSERT(num_bytes <= _sg.mtl.state_cache.cur_pipeline->shader->stage[stage_index].uniform_blocks[ub_index].size); /* copy to global uniform buffer, record offset into cmd encoder, and advance offset */ uint8_t* dst = &_sg.mtl.cur_ub_base_ptr[_sg.mtl.cur_ub_offset]; memcpy(dst, data, num_bytes); if (stage_index == SG_SHADERSTAGE_VS) { [_sg_mtl_cmd_encoder setVertexBufferOffset:_sg.mtl.cur_ub_offset atIndex:ub_index]; } else { [_sg_mtl_cmd_encoder setFragmentBufferOffset:_sg.mtl.cur_ub_offset atIndex:ub_index]; } _sg.mtl.cur_ub_offset = _sg_mtl_roundup(_sg.mtl.cur_ub_offset + num_bytes, _SG_MTL_UB_ALIGN); } _SOKOL_PRIVATE void _sg_draw(int base_element, int num_elements, int num_instances) { SOKOL_ASSERT(_sg.mtl.in_pass); if (!_sg.mtl.pass_valid) { return; } SOKOL_ASSERT(_sg_mtl_cmd_encoder); SOKOL_ASSERT(_sg.mtl.state_cache.cur_pipeline && (_sg.mtl.state_cache.cur_pipeline->slot.id == _sg.mtl.state_cache.cur_pipeline_id.id)); if (SG_INDEXTYPE_NONE != _sg.mtl.state_cache.cur_pipeline->index_type) { /* indexed rendering */ SOKOL_ASSERT(_sg.mtl.state_cache.cur_indexbuffer && (_sg.mtl.state_cache.cur_indexbuffer->slot.id == _sg.mtl.state_cache.cur_indexbuffer_id.id)); const _sg_buffer_t* ib = _sg.mtl.state_cache.cur_indexbuffer; SOKOL_ASSERT(ib->mtl_buf[ib->active_slot] != _SG_MTL_INVALID_SLOT_INDEX); const NSUInteger index_buffer_offset = _sg.mtl.state_cache.cur_indexbuffer_offset + base_element * _sg.mtl.state_cache.cur_pipeline->mtl_index_size; [_sg_mtl_cmd_encoder drawIndexedPrimitives:_sg.mtl.state_cache.cur_pipeline->mtl_prim_type indexCount:num_elements indexType:_sg.mtl.state_cache.cur_pipeline->mtl_index_type indexBuffer:_sg_mtl_idpool[ib->mtl_buf[ib->active_slot]] indexBufferOffset:index_buffer_offset instanceCount:num_instances]; } else { /* non-indexed rendering */ [_sg_mtl_cmd_encoder drawPrimitives:_sg.mtl.state_cache.cur_pipeline->mtl_prim_type vertexStart:base_element vertexCount:num_elements instanceCount:num_instances]; } } _SOKOL_PRIVATE void _sg_update_buffer(_sg_buffer_t* buf, const void* data, int data_size) { SOKOL_ASSERT(buf && data && (data_size > 0)); if (++buf->active_slot >= buf->num_slots) { buf->active_slot = 0; } __unsafe_unretained id mtl_buf = _sg_mtl_idpool[buf->mtl_buf[buf->active_slot]]; void* dst_ptr = [mtl_buf contents]; memcpy(dst_ptr, data, data_size); #if defined(TARGET_OS_IPHONE) && !TARGET_OS_IPHONE [mtl_buf didModifyRange:NSMakeRange(0, data_size)]; #endif } _SOKOL_PRIVATE void _sg_append_buffer(_sg_buffer_t* buf, const void* data, int data_size, bool new_frame) { SOKOL_ASSERT(buf && data && (data_size > 0)); if (new_frame) { if (++buf->active_slot >= buf->num_slots) { buf->active_slot = 0; } } __unsafe_unretained id mtl_buf = _sg_mtl_idpool[buf->mtl_buf[buf->active_slot]]; uint8_t* dst_ptr = (uint8_t*) [mtl_buf contents]; dst_ptr += buf->append_pos; memcpy(dst_ptr, data, data_size); #if defined(TARGET_OS_IPHONE) && !TARGET_OS_IPHONE [mtl_buf didModifyRange:NSMakeRange(buf->append_pos, data_size)]; #endif } _SOKOL_PRIVATE void _sg_update_image(_sg_image_t* img, const sg_image_content* data) { SOKOL_ASSERT(img && data); if (++img->active_slot >= img->num_slots) { img->active_slot = 0; } __unsafe_unretained id mtl_tex = _sg_mtl_idpool[img->mtl_tex[img->active_slot]]; _sg_mtl_copy_image_content(img, mtl_tex, data); } #endif /*== RESOURCE POOLS ==========================================================*/ _SOKOL_PRIVATE void _sg_init_pool(_sg_pool_t* pool, int num) { SOKOL_ASSERT(pool && (num >= 1)); /* slot 0 is reserved for the 'invalid id', so bump the pool size by 1 */ pool->size = num + 1; pool->queue_top = 0; /* generation counters indexable by pool slot index, slot 0 is reserved */ size_t gen_ctrs_size = sizeof(uint32_t) * pool->size; pool->gen_ctrs = (uint32_t*) SOKOL_MALLOC(gen_ctrs_size); SOKOL_ASSERT(pool->gen_ctrs); memset(pool->gen_ctrs, 0, gen_ctrs_size); /* it's not a bug to only reserve 'num' here */ pool->free_queue = (int*) SOKOL_MALLOC(sizeof(int)*num); SOKOL_ASSERT(pool->free_queue); /* never allocate the zero-th pool item since the invalid id is 0 */ for (int i = pool->size-1; i >= 1; i--) { pool->free_queue[pool->queue_top++] = i; } } _SOKOL_PRIVATE void _sg_discard_pool(_sg_pool_t* pool) { SOKOL_ASSERT(pool); SOKOL_ASSERT(pool->free_queue); SOKOL_FREE(pool->free_queue); pool->free_queue = 0; SOKOL_ASSERT(pool->gen_ctrs); SOKOL_FREE(pool->gen_ctrs); pool->gen_ctrs = 0; pool->size = 0; pool->queue_top = 0; } _SOKOL_PRIVATE int _sg_pool_alloc_index(_sg_pool_t* pool) { SOKOL_ASSERT(pool); SOKOL_ASSERT(pool->free_queue); if (pool->queue_top > 0) { int slot_index = pool->free_queue[--pool->queue_top]; SOKOL_ASSERT((slot_index > 0) && (slot_index < pool->size)); return slot_index; } else { /* pool exhausted */ return _SG_INVALID_SLOT_INDEX; } } _SOKOL_PRIVATE void _sg_pool_free_index(_sg_pool_t* pool, int slot_index) { SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < pool->size)); SOKOL_ASSERT(pool); SOKOL_ASSERT(pool->free_queue); SOKOL_ASSERT(pool->queue_top < pool->size); #ifdef SOKOL_DEBUG /* debug check against double-free */ for (int i = 0; i < pool->queue_top; i++) { SOKOL_ASSERT(pool->free_queue[i] != slot_index); } #endif pool->free_queue[pool->queue_top++] = slot_index; SOKOL_ASSERT(pool->queue_top <= (pool->size-1)); } _SOKOL_PRIVATE void _sg_reset_buffer(_sg_buffer_t* buf) { SOKOL_ASSERT(buf); memset(buf, 0, sizeof(_sg_buffer_t)); } _SOKOL_PRIVATE void _sg_reset_image(_sg_image_t* img) { SOKOL_ASSERT(img); memset(img, 0, sizeof(_sg_image_t)); } _SOKOL_PRIVATE void _sg_reset_shader(_sg_shader_t* shd) { SOKOL_ASSERT(shd); memset(shd, 0, sizeof(_sg_shader_t)); } _SOKOL_PRIVATE void _sg_reset_pipeline(_sg_pipeline_t* pip) { SOKOL_ASSERT(pip); memset(pip, 0, sizeof(_sg_pipeline_t)); } _SOKOL_PRIVATE void _sg_reset_pass(_sg_pass_t* pass) { SOKOL_ASSERT(pass); memset(pass, 0, sizeof(_sg_pass_t)); } _SOKOL_PRIVATE void _sg_reset_context(_sg_context_t* ctx) { SOKOL_ASSERT(ctx); memset(ctx, 0, sizeof(_sg_context_t)); } _SOKOL_PRIVATE void _sg_setup_pools(_sg_pools_t* p, const sg_desc* desc) { SOKOL_ASSERT(p); SOKOL_ASSERT(desc); /* note: the pools here will have an additional item, since slot 0 is reserved */ SOKOL_ASSERT((desc->buffer_pool_size > 0) && (desc->buffer_pool_size < _SG_MAX_POOL_SIZE)); _sg_init_pool(&p->buffer_pool, desc->buffer_pool_size); size_t buffer_pool_byte_size = sizeof(_sg_buffer_t) * p->buffer_pool.size; p->buffers = (_sg_buffer_t*) SOKOL_MALLOC(buffer_pool_byte_size); SOKOL_ASSERT(p->buffers); memset(p->buffers, 0, buffer_pool_byte_size); SOKOL_ASSERT((desc->image_pool_size > 0) && (desc->image_pool_size < _SG_MAX_POOL_SIZE)); _sg_init_pool(&p->image_pool, desc->image_pool_size); size_t image_pool_byte_size = sizeof(_sg_image_t) * p->image_pool.size; p->images = (_sg_image_t*) SOKOL_MALLOC(image_pool_byte_size); SOKOL_ASSERT(p->images); memset(p->images, 0, image_pool_byte_size); SOKOL_ASSERT((desc->shader_pool_size > 0) && (desc->shader_pool_size < _SG_MAX_POOL_SIZE)); _sg_init_pool(&p->shader_pool, desc->shader_pool_size); size_t shader_pool_byte_size = sizeof(_sg_shader_t) * p->shader_pool.size; p->shaders = (_sg_shader_t*) SOKOL_MALLOC(shader_pool_byte_size); SOKOL_ASSERT(p->shaders); memset(p->shaders, 0, shader_pool_byte_size); SOKOL_ASSERT((desc->pipeline_pool_size > 0) && (desc->pipeline_pool_size < _SG_MAX_POOL_SIZE)); _sg_init_pool(&p->pipeline_pool, desc->pipeline_pool_size); size_t pipeline_pool_byte_size = sizeof(_sg_pipeline_t) * p->pipeline_pool.size; p->pipelines = (_sg_pipeline_t*) SOKOL_MALLOC(pipeline_pool_byte_size); SOKOL_ASSERT(p->pipelines); memset(p->pipelines, 0, pipeline_pool_byte_size); SOKOL_ASSERT((desc->pass_pool_size > 0) && (desc->pass_pool_size < _SG_MAX_POOL_SIZE)); _sg_init_pool(&p->pass_pool, desc->pass_pool_size); size_t pass_pool_byte_size = sizeof(_sg_pass_t) * p->pass_pool.size; p->passes = (_sg_pass_t*) SOKOL_MALLOC(pass_pool_byte_size); SOKOL_ASSERT(p->passes); memset(p->passes, 0, pass_pool_byte_size); SOKOL_ASSERT((desc->context_pool_size > 0) && (desc->context_pool_size < _SG_MAX_POOL_SIZE)); _sg_init_pool(&p->context_pool, desc->context_pool_size); size_t context_pool_byte_size = sizeof(_sg_context_t) * p->context_pool.size; p->contexts = (_sg_context_t*) SOKOL_MALLOC(context_pool_byte_size); SOKOL_ASSERT(p->contexts); memset(p->contexts, 0, context_pool_byte_size); } _SOKOL_PRIVATE void _sg_discard_pools(_sg_pools_t* p) { SOKOL_ASSERT(p); SOKOL_FREE(p->contexts); p->contexts = 0; SOKOL_FREE(p->passes); p->passes = 0; SOKOL_FREE(p->pipelines); p->pipelines = 0; SOKOL_FREE(p->shaders); p->shaders = 0; SOKOL_FREE(p->images); p->images = 0; SOKOL_FREE(p->buffers); p->buffers = 0; _sg_discard_pool(&p->context_pool); _sg_discard_pool(&p->pass_pool); _sg_discard_pool(&p->pipeline_pool); _sg_discard_pool(&p->shader_pool); _sg_discard_pool(&p->image_pool); _sg_discard_pool(&p->buffer_pool); } /* allocate the slot at slot_index: - bump the slot's generation counter - create a resource id from the generation counter and slot index - set the slot's id to this id - set the slot's state to ALLOC - return the resource id */ _SOKOL_PRIVATE uint32_t _sg_slot_alloc(_sg_pool_t* pool, _sg_slot_t* slot, int slot_index) { /* FIXME: add handling for an overflowing generation counter, for now, just overflow (another option is to disable the slot) */ SOKOL_ASSERT(pool && pool->gen_ctrs); SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < pool->size)); SOKOL_ASSERT((slot->state == SG_RESOURCESTATE_INITIAL) && (slot->id == SG_INVALID_ID)); uint32_t ctr = ++pool->gen_ctrs[slot_index]; slot->id = (ctr<<_SG_SLOT_SHIFT)|(slot_index & _SG_SLOT_MASK); slot->state = SG_RESOURCESTATE_ALLOC; return slot->id; } /* extract slot index from id */ _SOKOL_PRIVATE int _sg_slot_index(uint32_t id) { int slot_index = (int) (id & _SG_SLOT_MASK); SOKOL_ASSERT(_SG_INVALID_SLOT_INDEX != slot_index); return slot_index; } /* returns pointer to resource by id without matching id check */ _SOKOL_PRIVATE _sg_buffer_t* _sg_buffer_at(const _sg_pools_t* p, uint32_t buf_id) { SOKOL_ASSERT(p && (SG_INVALID_ID != buf_id)); int slot_index = _sg_slot_index(buf_id); SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < p->buffer_pool.size)); return &p->buffers[slot_index]; } _SOKOL_PRIVATE _sg_image_t* _sg_image_at(const _sg_pools_t* p, uint32_t img_id) { SOKOL_ASSERT(p && (SG_INVALID_ID != img_id)); int slot_index = _sg_slot_index(img_id); SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < p->image_pool.size)); return &p->images[slot_index]; } _SOKOL_PRIVATE _sg_shader_t* _sg_shader_at(const _sg_pools_t* p, uint32_t shd_id) { SOKOL_ASSERT(p && (SG_INVALID_ID != shd_id)); int slot_index = _sg_slot_index(shd_id); SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < p->shader_pool.size)); return &p->shaders[slot_index]; } _SOKOL_PRIVATE _sg_pipeline_t* _sg_pipeline_at(const _sg_pools_t* p, uint32_t pip_id) { SOKOL_ASSERT(p && (SG_INVALID_ID != pip_id)); int slot_index = _sg_slot_index(pip_id); SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < p->pipeline_pool.size)); return &p->pipelines[slot_index]; } _SOKOL_PRIVATE _sg_pass_t* _sg_pass_at(const _sg_pools_t* p, uint32_t pass_id) { SOKOL_ASSERT(p && (SG_INVALID_ID != pass_id)); int slot_index = _sg_slot_index(pass_id); SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < p->pass_pool.size)); return &p->passes[slot_index]; } _SOKOL_PRIVATE _sg_context_t* _sg_context_at(const _sg_pools_t* p, uint32_t context_id) { SOKOL_ASSERT(p && (SG_INVALID_ID != context_id)); int slot_index = _sg_slot_index(context_id); SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < p->context_pool.size)); return &p->contexts[slot_index]; } /* returns pointer to resource with matching id check, may return 0 */ _SOKOL_PRIVATE _sg_buffer_t* _sg_lookup_buffer(const _sg_pools_t* p, uint32_t buf_id) { if (SG_INVALID_ID != buf_id) { _sg_buffer_t* buf = _sg_buffer_at(p, buf_id); if (buf->slot.id == buf_id) { return buf; } } return 0; } _SOKOL_PRIVATE _sg_image_t* _sg_lookup_image(const _sg_pools_t* p, uint32_t img_id) { if (SG_INVALID_ID != img_id) { _sg_image_t* img = _sg_image_at(p, img_id); if (img->slot.id == img_id) { return img; } } return 0; } _SOKOL_PRIVATE _sg_shader_t* _sg_lookup_shader(const _sg_pools_t* p, uint32_t shd_id) { SOKOL_ASSERT(p); if (SG_INVALID_ID != shd_id) { _sg_shader_t* shd = _sg_shader_at(p, shd_id); if (shd->slot.id == shd_id) { return shd; } } return 0; } _SOKOL_PRIVATE _sg_pipeline_t* _sg_lookup_pipeline(const _sg_pools_t* p, uint32_t pip_id) { SOKOL_ASSERT(p); if (SG_INVALID_ID != pip_id) { _sg_pipeline_t* pip = _sg_pipeline_at(p, pip_id); if (pip->slot.id == pip_id) { return pip; } } return 0; } _SOKOL_PRIVATE _sg_pass_t* _sg_lookup_pass(const _sg_pools_t* p, uint32_t pass_id) { SOKOL_ASSERT(p); if (SG_INVALID_ID != pass_id) { _sg_pass_t* pass = _sg_pass_at(p, pass_id); if (pass->slot.id == pass_id) { return pass; } } return 0; } _SOKOL_PRIVATE _sg_context_t* _sg_lookup_context(const _sg_pools_t* p, uint32_t ctx_id) { SOKOL_ASSERT(p); if (SG_INVALID_ID != ctx_id) { _sg_context_t* ctx = _sg_context_at(p, ctx_id); if (ctx->slot.id == ctx_id) { return ctx; } } return 0; } _SOKOL_PRIVATE void _sg_destroy_all_resources(_sg_pools_t* p, uint32_t ctx_id) { /* this is a bit dumb since it loops over all pool slots to find the occupied slots, on the other hand it is only ever executed at shutdown NOTE: ONLY EXECUTE THIS AT SHUTDOWN ...because the free queues will not be reset and the resource slots not be cleared! */ for (int i = 1; i < p->buffer_pool.size; i++) { if (p->buffers[i].slot.ctx_id == ctx_id) { sg_resource_state state = p->buffers[i].slot.state; if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) { _sg_destroy_buffer(&p->buffers[i]); } } } for (int i = 1; i < p->image_pool.size; i++) { if (p->images[i].slot.ctx_id == ctx_id) { sg_resource_state state = p->images[i].slot.state; if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) { _sg_destroy_image(&p->images[i]); } } } for (int i = 1; i < p->shader_pool.size; i++) { if (p->shaders[i].slot.ctx_id == ctx_id) { sg_resource_state state = p->shaders[i].slot.state; if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) { _sg_destroy_shader(&p->shaders[i]); } } } for (int i = 1; i < p->pipeline_pool.size; i++) { if (p->pipelines[i].slot.ctx_id == ctx_id) { sg_resource_state state = p->pipelines[i].slot.state; if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) { _sg_destroy_pipeline(&p->pipelines[i]); } } } for (int i = 1; i < p->pass_pool.size; i++) { if (p->passes[i].slot.ctx_id == ctx_id) { sg_resource_state state = p->passes[i].slot.state; if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) { _sg_destroy_pass(&p->passes[i]); } } } } /*== VALIDATION LAYER ========================================================*/ #if defined(SOKOL_DEBUG) /* return a human readable string for an _sg_validate_error */ _SOKOL_PRIVATE const char* _sg_validate_string(_sg_validate_error_t err) { switch (err) { /* buffer creation validation errors */ case _SG_VALIDATE_BUFFERDESC_CANARY: return "sg_buffer_desc not initialized"; case _SG_VALIDATE_BUFFERDESC_SIZE: return "sg_buffer_desc.size cannot be 0"; case _SG_VALIDATE_BUFFERDESC_CONTENT: return "immutable buffers must be initialized with content (sg_buffer_desc.content)"; case _SG_VALIDATE_BUFFERDESC_NO_CONTENT: return "dynamic/stream usage buffers cannot be initialized with content"; /* image creation validation errros */ case _SG_VALIDATE_IMAGEDESC_CANARY: return "sg_image_desc not initialized"; case _SG_VALIDATE_IMAGEDESC_WIDTH: return "sg_image_desc.width must be > 0"; case _SG_VALIDATE_IMAGEDESC_HEIGHT: return "sg_image_desc.height must be > 0"; case _SG_VALIDATE_IMAGEDESC_RT_PIXELFORMAT: return "invalid pixel format for render-target image"; case _SG_VALIDATE_IMAGEDESC_NONRT_PIXELFORMAT: return "invalid pixel format for non-render-target image"; case _SG_VALIDATE_IMAGEDESC_MSAA_BUT_NO_RT: return "non-render-target images cannot be multisampled"; case _SG_VALIDATE_IMAGEDESC_NO_MSAA_RT_SUPPORT: return "MSAA render targets not supported (SG_FEATURE_MSAA_RENDER_TARGETS)"; case _SG_VALIDATE_IMAGEDESC_RT_IMMUTABLE: return "render target images must be SG_USAGE_IMMUTABLE"; case _SG_VALIDATE_IMAGEDESC_RT_NO_CONTENT: return "render target images cannot be initialized with content"; case _SG_VALIDATE_IMAGEDESC_CONTENT: return "missing or invalid content for immutable image"; case _SG_VALIDATE_IMAGEDESC_NO_CONTENT: return "dynamic/stream usage images cannot be initialized with content"; /* shader creation */ case _SG_VALIDATE_SHADERDESC_CANARY: return "sg_shader_desc not initialized"; case _SG_VALIDATE_SHADERDESC_SOURCE: return "shader source code required"; case _SG_VALIDATE_SHADERDESC_BYTECODE: return "shader byte code required"; case _SG_VALIDATE_SHADERDESC_SOURCE_OR_BYTECODE: return "shader source or byte code required"; case _SG_VALIDATE_SHADERDESC_NO_BYTECODE_SIZE: return "shader byte code length (in bytes) required"; case _SG_VALIDATE_SHADERDESC_NO_CONT_UBS: return "shader uniform blocks must occupy continuous slots"; case _SG_VALIDATE_SHADERDESC_NO_CONT_UB_MEMBERS: return "uniform block members must occupy continuous slots"; case _SG_VALIDATE_SHADERDESC_NO_UB_MEMBERS: return "GL backend requires uniform block member declarations"; case _SG_VALIDATE_SHADERDESC_UB_MEMBER_NAME: return "uniform block member name missing"; case _SG_VALIDATE_SHADERDESC_UB_SIZE_MISMATCH: return "size of uniform block members doesn't match uniform block size"; case _SG_VALIDATE_SHADERDESC_NO_CONT_IMGS: return "shader images must occupy continuous slots"; case _SG_VALIDATE_SHADERDESC_IMG_NAME: return "GL backend requires uniform block member names"; case _SG_VALIDATE_SHADERDESC_ATTR_NAMES: return "GLES2 backend requires vertex attribute names"; case _SG_VALIDATE_SHADERDESC_ATTR_SEMANTICS: return "D3D11 backend requires vertex attribute semantics"; case _SG_VALIDATE_SHADERDESC_ATTR_STRING_TOO_LONG: return "vertex attribute name/semantic string too long (max len 16)"; /* pipeline creation */ case _SG_VALIDATE_PIPELINEDESC_CANARY: return "sg_pipeline_desc not initialized"; case _SG_VALIDATE_PIPELINEDESC_SHADER: return "sg_pipeline_desc.shader missing or invalid"; case _SG_VALIDATE_PIPELINEDESC_NO_ATTRS: return "sg_pipeline_desc.layout.attrs is empty or not continuous"; case _SG_VALIDATE_PIPELINEDESC_LAYOUT_STRIDE4: return "sg_pipeline_desc.layout.buffers[].stride must be multiple of 4"; case _SG_VALIDATE_PIPELINEDESC_ATTR_NAME: return "GLES2/WebGL missing vertex attribute name in shader"; case _SG_VALIDATE_PIPELINEDESC_ATTR_SEMANTICS: return "D3D11 missing vertex attribute semantics in shader"; /* pass creation */ case _SG_VALIDATE_PASSDESC_CANARY: return "sg_pass_desc not initialized"; case _SG_VALIDATE_PASSDESC_NO_COLOR_ATTS: return "sg_pass_desc.color_attachments[0] must be valid"; case _SG_VALIDATE_PASSDESC_NO_CONT_COLOR_ATTS: return "color attachments must occupy continuous slots"; case _SG_VALIDATE_PASSDESC_IMAGE: return "pass attachment image is not valid"; case _SG_VALIDATE_PASSDESC_MIPLEVEL: return "pass attachment mip level is bigger than image has mipmaps"; case _SG_VALIDATE_PASSDESC_FACE: return "pass attachment image is cubemap, but face index is too big"; case _SG_VALIDATE_PASSDESC_LAYER: return "pass attachment image is array texture, but layer index is too big"; case _SG_VALIDATE_PASSDESC_SLICE: return "pass attachment image is 3d texture, but slice value is too big"; case _SG_VALIDATE_PASSDESC_IMAGE_NO_RT: return "pass attachment image must be render targets"; case _SG_VALIDATE_PASSDESC_COLOR_PIXELFORMATS: return "all pass color attachment images must have the same pixel format"; case _SG_VALIDATE_PASSDESC_COLOR_INV_PIXELFORMAT: return "pass color-attachment images must have a renderable pixel format"; case _SG_VALIDATE_PASSDESC_DEPTH_INV_PIXELFORMAT: return "pass depth-attachment image must have depth pixel format"; case _SG_VALIDATE_PASSDESC_IMAGE_SIZES: return "all pass attachments must have the same size"; case _SG_VALIDATE_PASSDESC_IMAGE_SAMPLE_COUNTS: return "all pass attachments must have the same sample count"; /* sg_begin_pass */ case _SG_VALIDATE_BEGINPASS_PASS: return "sg_begin_pass: pass must be valid"; case _SG_VALIDATE_BEGINPASS_IMAGE: return "sg_begin_pass: one or more attachment images are not valid"; /* sg_apply_pipeline */ case _SG_VALIDATE_APIP_PIPELINE_VALID_ID: return "sg_apply_pipeline: invalid pipeline id provided"; case _SG_VALIDATE_APIP_PIPELINE_EXISTS: return "sg_apply_pipeline: pipeline object no longer alive"; case _SG_VALIDATE_APIP_PIPELINE_VALID: return "sg_apply_pipeline: pipeline object not in valid state"; case _SG_VALIDATE_APIP_SHADER_EXISTS: return "sg_apply_pipeline: shader object no longer alive"; case _SG_VALIDATE_APIP_SHADER_VALID: return "sg_apply_pipeline: shader object not in valid state"; case _SG_VALIDATE_APIP_ATT_COUNT: return "sg_apply_pipeline: color_attachment_count in pipeline doesn't match number of pass color attachments"; case _SG_VALIDATE_APIP_COLOR_FORMAT: return "sg_apply_pipeline: color_format in pipeline doesn't match pass color attachment pixel format"; case _SG_VALIDATE_APIP_DEPTH_FORMAT: return "sg_apply_pipeline: depth_format in pipeline doesn't match pass depth attachment pixel format"; case _SG_VALIDATE_APIP_SAMPLE_COUNT: return "sg_apply_pipeline: MSAA sample count in pipeline doesn't match render pass attachment sample count"; /* sg_apply_bindings */ case _SG_VALIDATE_ABND_PIPELINE: return "sg_apply_bindings: must be called after sg_apply_pipeline"; case _SG_VALIDATE_ABND_PIPELINE_EXISTS: return "sg_apply_bindings: currently applied pipeline object no longer alive"; case _SG_VALIDATE_ABND_PIPELINE_VALID: return "sg_apply_bindings: currently applied pipeline object not in valid state"; case _SG_VALIDATE_ABND_VBS: return "sg_apply_bindings: number of vertex buffers doesn't match number of pipeline vertex layouts"; case _SG_VALIDATE_ABND_VB_EXISTS: return "sg_apply_bindings: vertex buffer no longer alive"; case _SG_VALIDATE_ABND_VB_TYPE: return "sg_apply_bindings: buffer in vertex buffer slot is not a SG_BUFFERTYPE_VERTEXBUFFER"; case _SG_VALIDATE_ABND_VB_OVERFLOW: return "sg_apply_bindings: buffer in vertex buffer slot is overflown"; case _SG_VALIDATE_ABND_NO_IB: return "sg_apply_bindings: pipeline object defines indexed rendering, but no index buffer provided"; case _SG_VALIDATE_ABND_IB: return "sg_apply_bindings: pipeline object defines non-indexed rendering, but index buffer provided"; case _SG_VALIDATE_ABND_IB_EXISTS: return "sg_apply_bindings: index buffer no longer alive"; case _SG_VALIDATE_ABND_IB_TYPE: return "sg_apply_bindings: buffer in index buffer slot is not a SG_BUFFERTYPE_INDEXBUFFER"; case _SG_VALIDATE_ABND_IB_OVERFLOW: return "sg_apply_bindings: buffer in index buffer slot is overflown"; case _SG_VALIDATE_ABND_VS_IMGS: return "sg_apply_bindings: vertex shader image count doesn't match sg_shader_desc"; case _SG_VALIDATE_ABND_VS_IMG_EXISTS: return "sg_apply_bindings: vertex shader image no longer alive"; case _SG_VALIDATE_ABND_VS_IMG_TYPES: return "sg_apply_bindings: one or more vertex shader image types don't match sg_shader_desc"; case _SG_VALIDATE_ABND_FS_IMGS: return "sg_apply_bindings: fragment shader image count doesn't match sg_shader_desc"; case _SG_VALIDATE_ABND_FS_IMG_EXISTS: return "sg_apply_bindings: fragment shader image no longer alive"; case _SG_VALIDATE_ABND_FS_IMG_TYPES: return "sg_apply_bindings: one or more fragment shader image types don't match sg_shader_desc"; /* sg_apply_uniforms */ case _SG_VALIDATE_AUB_NO_PIPELINE: return "sg_apply_uniforms: must be called after sg_apply_pipeline()"; case _SG_VALIDATE_AUB_NO_UB_AT_SLOT: return "sg_apply_uniforms: no uniform block declaration at this shader stage UB slot"; case _SG_VALIDATE_AUB_SIZE: return "sg_apply_uniforms: data size exceeds declared uniform block size"; /* sg_update_buffer */ case _SG_VALIDATE_UPDATEBUF_USAGE: return "sg_update_buffer: cannot update immutable buffer"; case _SG_VALIDATE_UPDATEBUF_SIZE: return "sg_update_buffer: update size is bigger than buffer size"; case _SG_VALIDATE_UPDATEBUF_ONCE: return "sg_update_buffer: only one update allowed per buffer and frame"; case _SG_VALIDATE_UPDATEBUF_APPEND: return "sg_update_buffer: cannot call sg_update_buffer and sg_append_buffer in same frame"; /* sg_append_buffer */ case _SG_VALIDATE_APPENDBUF_USAGE: return "sg_append_buffer: cannot append to immutable buffer"; case _SG_VALIDATE_APPENDBUF_SIZE: return "sg_append_buffer: overall appended size is bigger than buffer size"; case _SG_VALIDATE_APPENDBUF_UPDATE: return "sg_append_buffer: cannot call sg_append_buffer and sg_update_buffer in same frame"; /* sg_update_image */ case _SG_VALIDATE_UPDIMG_USAGE: return "sg_update_image: cannot update immutable image"; case _SG_VALIDATE_UPDIMG_NOTENOUGHDATA: return "sg_update_image: not enough subimage data provided"; case _SG_VALIDATE_UPDIMG_SIZE: return "sg_update_image: provided subimage data size too big"; case _SG_VALIDATE_UPDIMG_COMPRESSED: return "sg_update_image: cannot update images with compressed format"; case _SG_VALIDATE_UPDIMG_ONCE: return "sg_update_image: only one update allowed per image and frame"; default: return "unknown validation error"; } } #endif /* defined(SOKOL_DEBUG) */ /*-- validation checks -------------------------------------------------------*/ #if defined(SOKOL_DEBUG) _SOKOL_PRIVATE void _sg_validate_begin(void) { _sg.validate_error = _SG_VALIDATE_SUCCESS; } _SOKOL_PRIVATE void _sg_validate(bool cond, _sg_validate_error_t err) { if (!cond) { _sg.validate_error = err; SOKOL_LOG(_sg_validate_string(err)); } } _SOKOL_PRIVATE bool _sg_validate_end(void) { if (_sg.validate_error != _SG_VALIDATE_SUCCESS) { #if !defined(SOKOL_VALIDATE_NON_FATAL) SOKOL_LOG("^^^^ VALIDATION FAILED, TERMINATING ^^^^"); SOKOL_ASSERT(false); #endif return false; } else { return true; } } #endif _SOKOL_PRIVATE bool _sg_validate_buffer_desc(const sg_buffer_desc* desc) { #if !defined(SOKOL_DEBUG) _SOKOL_UNUSED(desc); return true; #else SOKOL_ASSERT(desc); SOKOL_VALIDATE_BEGIN(); SOKOL_VALIDATE(desc->_start_canary == 0, _SG_VALIDATE_BUFFERDESC_CANARY); SOKOL_VALIDATE(desc->_end_canary == 0, _SG_VALIDATE_BUFFERDESC_CANARY); SOKOL_VALIDATE(desc->size > 0, _SG_VALIDATE_BUFFERDESC_SIZE); bool ext = (0 != desc->gl_buffers[0]) || (0 != desc->mtl_buffers[0]) || (0 != desc->d3d11_buffer); if (!ext && (desc->usage == SG_USAGE_IMMUTABLE)) { SOKOL_VALIDATE(0 != desc->content, _SG_VALIDATE_BUFFERDESC_CONTENT); } else { SOKOL_VALIDATE(0 == desc->content, _SG_VALIDATE_BUFFERDESC_NO_CONTENT); } return SOKOL_VALIDATE_END(); #endif } _SOKOL_PRIVATE bool _sg_validate_image_desc(const sg_image_desc* desc) { #if !defined(SOKOL_DEBUG) _SOKOL_UNUSED(desc); return true; #else SOKOL_ASSERT(desc); SOKOL_VALIDATE_BEGIN(); SOKOL_VALIDATE(desc->_start_canary == 0, _SG_VALIDATE_IMAGEDESC_CANARY); SOKOL_VALIDATE(desc->_end_canary == 0, _SG_VALIDATE_IMAGEDESC_CANARY); SOKOL_VALIDATE(desc->width > 0, _SG_VALIDATE_IMAGEDESC_WIDTH); SOKOL_VALIDATE(desc->height > 0, _SG_VALIDATE_IMAGEDESC_HEIGHT); const sg_pixel_format fmt = desc->pixel_format; const sg_usage usage = desc->usage; const bool ext = (0 != desc->gl_textures[0]) || (0 != desc->mtl_textures[0]) || (0 != desc->d3d11_texture); if (desc->render_target) { if (desc->sample_count > 1) { SOKOL_VALIDATE(_sg_query_feature(SG_FEATURE_MSAA_RENDER_TARGETS), _SG_VALIDATE_IMAGEDESC_NO_MSAA_RT_SUPPORT); } const bool valid_color_fmt = _sg_is_valid_rendertarget_color_format(fmt); const bool valid_depth_fmt = _sg_is_valid_rendertarget_depth_format(fmt); SOKOL_VALIDATE(valid_color_fmt || valid_depth_fmt, _SG_VALIDATE_IMAGEDESC_RT_PIXELFORMAT); SOKOL_VALIDATE(usage == SG_USAGE_IMMUTABLE, _SG_VALIDATE_IMAGEDESC_RT_IMMUTABLE); SOKOL_VALIDATE(desc->content.subimage[0][0].ptr==0, _SG_VALIDATE_IMAGEDESC_RT_NO_CONTENT); } else { SOKOL_VALIDATE(desc->sample_count <= 1, _SG_VALIDATE_IMAGEDESC_MSAA_BUT_NO_RT); const bool valid_nonrt_fmt = !_sg_is_valid_rendertarget_depth_format(fmt); SOKOL_VALIDATE(valid_nonrt_fmt, _SG_VALIDATE_IMAGEDESC_NONRT_PIXELFORMAT); /* FIXME: should use the same "expected size" computation as in _sg_validate_update_image() here */ if (!ext && (usage == SG_USAGE_IMMUTABLE)) { const int num_faces = desc->type == SG_IMAGETYPE_CUBE ? 6:1; const int num_mips = desc->num_mipmaps; for (int face_index = 0; face_index < num_faces; face_index++) { for (int mip_index = 0; mip_index < num_mips; mip_index++) { const bool has_data = desc->content.subimage[face_index][mip_index].ptr != 0; const bool has_size = desc->content.subimage[face_index][mip_index].size > 0; SOKOL_VALIDATE(has_data && has_size, _SG_VALIDATE_IMAGEDESC_CONTENT); } } } else { for (int face_index = 0; face_index < SG_CUBEFACE_NUM; face_index++) { for (int mip_index = 0; mip_index < SG_MAX_MIPMAPS; mip_index++) { const bool no_data = 0 == desc->content.subimage[face_index][mip_index].ptr; const bool no_size = 0 == desc->content.subimage[face_index][mip_index].size; SOKOL_VALIDATE(no_data && no_size, _SG_VALIDATE_IMAGEDESC_NO_CONTENT); } } } } return SOKOL_VALIDATE_END(); #endif } _SOKOL_PRIVATE bool _sg_validate_shader_desc(const sg_shader_desc* desc) { #if !defined(SOKOL_DEBUG) _SOKOL_UNUSED(desc); return true; #else SOKOL_ASSERT(desc); SOKOL_VALIDATE_BEGIN(); SOKOL_VALIDATE(desc->_start_canary == 0, _SG_VALIDATE_SHADERDESC_CANARY); SOKOL_VALIDATE(desc->_end_canary == 0, _SG_VALIDATE_SHADERDESC_CANARY); #if defined(SOKOL_GLES2) SOKOL_VALIDATE(0 != desc->attrs[0].name, _SG_VALIDATE_SHADERDESC_ATTR_NAMES); #elif defined(SOKOL_D3D11) SOKOL_VALIDATE(0 != desc->attrs[0].sem_name, _SG_VALIDATE_SHADERDESC_ATTR_SEMANTICS); #endif #if defined(SOKOL_GLCORE33) || defined(SOKOL_GLES2) || defined(SOKOL_GLES3) /* on GL, must provide shader source code */ SOKOL_VALIDATE(0 != desc->vs.source, _SG_VALIDATE_SHADERDESC_SOURCE); SOKOL_VALIDATE(0 != desc->fs.source, _SG_VALIDATE_SHADERDESC_SOURCE); #elif defined(SOKOL_METAL) || defined(SOKOL_D3D11) /* on Metal or D3D11, must provide shader source code or byte code */ SOKOL_VALIDATE((0 != desc->vs.source)||(0 != desc->vs.byte_code), _SG_VALIDATE_SHADERDESC_SOURCE_OR_BYTECODE); SOKOL_VALIDATE((0 != desc->fs.source)||(0 != desc->fs.byte_code), _SG_VALIDATE_SHADERDESC_SOURCE_OR_BYTECODE); #else /* Dummy Backend, don't require source or bytecode */ #endif for (int i = 0; i < SG_MAX_VERTEX_ATTRIBUTES; i++) { if (desc->attrs[i].name) { SOKOL_VALIDATE(strlen(desc->attrs[i].name) < _SG_STRING_SIZE, _SG_VALIDATE_SHADERDESC_ATTR_STRING_TOO_LONG); } if (desc->attrs[i].sem_name) { SOKOL_VALIDATE(strlen(desc->attrs[i].sem_name) < _SG_STRING_SIZE, _SG_VALIDATE_SHADERDESC_ATTR_STRING_TOO_LONG); } } /* if shader byte code, the size must also be provided */ if (0 != desc->vs.byte_code) { SOKOL_VALIDATE(desc->vs.byte_code_size > 0, _SG_VALIDATE_SHADERDESC_NO_BYTECODE_SIZE); } if (0 != desc->fs.byte_code) { SOKOL_VALIDATE(desc->fs.byte_code_size > 0, _SG_VALIDATE_SHADERDESC_NO_BYTECODE_SIZE); } for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { const sg_shader_stage_desc* stage_desc = (stage_index == 0)? &desc->vs : &desc->fs; bool uniform_blocks_continuous = true; for (int ub_index = 0; ub_index < SG_MAX_SHADERSTAGE_UBS; ub_index++) { const sg_shader_uniform_block_desc* ub_desc = &stage_desc->uniform_blocks[ub_index]; if (ub_desc->size > 0) { SOKOL_VALIDATE(uniform_blocks_continuous, _SG_VALIDATE_SHADERDESC_NO_CONT_UBS); bool uniforms_continuous = true; int uniform_offset = 0; int num_uniforms = 0; for (int u_index = 0; u_index < SG_MAX_UB_MEMBERS; u_index++) { const sg_shader_uniform_desc* u_desc = &ub_desc->uniforms[u_index]; if (u_desc->type != SG_UNIFORMTYPE_INVALID) { SOKOL_VALIDATE(uniforms_continuous, _SG_VALIDATE_SHADERDESC_NO_CONT_UB_MEMBERS); #if defined(SOKOL_GLES2) || defined(SOKOL_GLES3) SOKOL_VALIDATE(u_desc->name, _SG_VALIDATE_SHADERDESC_UB_MEMBER_NAME); #endif const int array_count = u_desc->array_count; uniform_offset += _sg_uniform_size(u_desc->type, array_count); num_uniforms++; } else { uniforms_continuous = false; } } #if defined(SOKOL_GLCORE33) || defined(SOKOL_GLES2) || defined(SOKOL_GLES3) SOKOL_VALIDATE(uniform_offset == ub_desc->size, _SG_VALIDATE_SHADERDESC_UB_SIZE_MISMATCH); SOKOL_VALIDATE(num_uniforms > 0, _SG_VALIDATE_SHADERDESC_NO_UB_MEMBERS); #endif } else { uniform_blocks_continuous = false; } } bool images_continuous = true; for (int img_index = 0; img_index < SG_MAX_SHADERSTAGE_IMAGES; img_index++) { const sg_shader_image_desc* img_desc = &stage_desc->images[img_index]; if (img_desc->type != _SG_IMAGETYPE_DEFAULT) { SOKOL_VALIDATE(images_continuous, _SG_VALIDATE_SHADERDESC_NO_CONT_IMGS); #if defined(SOKOL_GLES2) SOKOL_VALIDATE(img_desc->name, _SG_VALIDATE_SHADERDESC_IMG_NAME); #endif } else { images_continuous = false; } } } return SOKOL_VALIDATE_END(); #endif } _SOKOL_PRIVATE bool _sg_validate_pipeline_desc(const sg_pipeline_desc* desc) { #if !defined(SOKOL_DEBUG) _SOKOL_UNUSED(desc); return true; #else SOKOL_ASSERT(desc); SOKOL_VALIDATE_BEGIN(); SOKOL_VALIDATE(desc->_start_canary == 0, _SG_VALIDATE_PIPELINEDESC_CANARY); SOKOL_VALIDATE(desc->_end_canary == 0, _SG_VALIDATE_PIPELINEDESC_CANARY); SOKOL_VALIDATE(desc->shader.id != SG_INVALID_ID, _SG_VALIDATE_PIPELINEDESC_SHADER); const _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, desc->shader.id); SOKOL_VALIDATE(shd && shd->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_PIPELINEDESC_SHADER); for (int buf_index = 0; buf_index < SG_MAX_SHADERSTAGE_BUFFERS; buf_index++) { const sg_buffer_layout_desc* l_desc = &desc->layout.buffers[buf_index]; if (l_desc->stride == 0) { continue; } SOKOL_VALIDATE((l_desc->stride & 3) == 0, _SG_VALIDATE_PIPELINEDESC_LAYOUT_STRIDE4); } SOKOL_VALIDATE(desc->layout.attrs[0].format != SG_VERTEXFORMAT_INVALID, _SG_VALIDATE_PIPELINEDESC_NO_ATTRS); bool attrs_cont = true; for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { const sg_vertex_attr_desc* a_desc = &desc->layout.attrs[attr_index]; if (a_desc->format == SG_VERTEXFORMAT_INVALID) { attrs_cont = false; continue; } SOKOL_VALIDATE(attrs_cont, _SG_VALIDATE_PIPELINEDESC_NO_ATTRS); SOKOL_ASSERT(a_desc->buffer_index < SG_MAX_SHADERSTAGE_BUFFERS); #if defined(SOKOL_GLES2) /* on GLES2, vertex attribute names must be provided */ SOKOL_VALIDATE(!_sg_strempty(&shd->attrs[attr_index].name), _SG_VALIDATE_PIPELINEDESC_ATTR_NAME); #elif defined(SOKOL_D3D11) /* on D3D11, semantic names (and semantic indices) must be provided */ SOKOL_VALIDATE(!_sg_strempty(&shd->attrs[attr_index].sem_name), _SG_VALIDATE_PIPELINEDESC_ATTR_SEMANTICS); #endif } return SOKOL_VALIDATE_END(); #endif } _SOKOL_PRIVATE bool _sg_validate_pass_desc(const sg_pass_desc* desc) { #if !defined(SOKOL_DEBUG) _SOKOL_UNUSED(desc); return true; #else SOKOL_ASSERT(desc); SOKOL_VALIDATE_BEGIN(); SOKOL_VALIDATE(desc->_start_canary == 0, _SG_VALIDATE_PASSDESC_CANARY); SOKOL_VALIDATE(desc->_end_canary == 0, _SG_VALIDATE_PASSDESC_CANARY); bool atts_cont = true; sg_pixel_format color_fmt = SG_PIXELFORMAT_NONE; int width = -1, height = -1, sample_count = -1; for (int att_index = 0; att_index < SG_MAX_COLOR_ATTACHMENTS; att_index++) { const sg_attachment_desc* att = &desc->color_attachments[att_index]; if (att->image.id == SG_INVALID_ID) { SOKOL_VALIDATE(att_index > 0, _SG_VALIDATE_PASSDESC_NO_COLOR_ATTS); atts_cont = false; continue; } SOKOL_VALIDATE(atts_cont, _SG_VALIDATE_PASSDESC_NO_CONT_COLOR_ATTS); const _sg_image_t* img = _sg_lookup_image(&_sg.pools, att->image.id); SOKOL_VALIDATE(img && img->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_PASSDESC_IMAGE); SOKOL_VALIDATE(att->mip_level < img->num_mipmaps, _SG_VALIDATE_PASSDESC_MIPLEVEL); if (img->type == SG_IMAGETYPE_CUBE) { SOKOL_VALIDATE(att->face < 6, _SG_VALIDATE_PASSDESC_FACE); } else if (img->type == SG_IMAGETYPE_ARRAY) { SOKOL_VALIDATE(att->layer < img->depth, _SG_VALIDATE_PASSDESC_LAYER); } else if (img->type == SG_IMAGETYPE_3D) { SOKOL_VALIDATE(att->slice < img->depth, _SG_VALIDATE_PASSDESC_SLICE); } SOKOL_VALIDATE(img->render_target, _SG_VALIDATE_PASSDESC_IMAGE_NO_RT); if (att_index == 0) { color_fmt = img->pixel_format; width = img->width >> att->mip_level; height = img->height >> att->mip_level; sample_count = img->sample_count; } else { SOKOL_VALIDATE(img->pixel_format == color_fmt, _SG_VALIDATE_PASSDESC_COLOR_PIXELFORMATS); SOKOL_VALIDATE(width == img->width >> att->mip_level, _SG_VALIDATE_PASSDESC_IMAGE_SIZES); SOKOL_VALIDATE(height == img->height >> att->mip_level, _SG_VALIDATE_PASSDESC_IMAGE_SIZES); SOKOL_VALIDATE(sample_count == img->sample_count, _SG_VALIDATE_PASSDESC_IMAGE_SAMPLE_COUNTS); } SOKOL_VALIDATE(_sg_is_valid_rendertarget_color_format(img->pixel_format), _SG_VALIDATE_PASSDESC_COLOR_INV_PIXELFORMAT); } if (desc->depth_stencil_attachment.image.id != SG_INVALID_ID) { const sg_attachment_desc* att = &desc->depth_stencil_attachment; const _sg_image_t* img = _sg_lookup_image(&_sg.pools, att->image.id); SOKOL_VALIDATE(img && img->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_PASSDESC_IMAGE); SOKOL_VALIDATE(att->mip_level < img->num_mipmaps, _SG_VALIDATE_PASSDESC_MIPLEVEL); if (img->type == SG_IMAGETYPE_CUBE) { SOKOL_VALIDATE(att->face < 6, _SG_VALIDATE_PASSDESC_FACE); } else if (img->type == SG_IMAGETYPE_ARRAY) { SOKOL_VALIDATE(att->layer < img->depth, _SG_VALIDATE_PASSDESC_LAYER); } else if (img->type == SG_IMAGETYPE_3D) { SOKOL_VALIDATE(att->slice < img->depth, _SG_VALIDATE_PASSDESC_SLICE); } SOKOL_VALIDATE(img->render_target, _SG_VALIDATE_PASSDESC_IMAGE_NO_RT); SOKOL_VALIDATE(width == img->width >> att->mip_level, _SG_VALIDATE_PASSDESC_IMAGE_SIZES); SOKOL_VALIDATE(height == img->height >> att->mip_level, _SG_VALIDATE_PASSDESC_IMAGE_SIZES); SOKOL_VALIDATE(sample_count == img->sample_count, _SG_VALIDATE_PASSDESC_IMAGE_SAMPLE_COUNTS); SOKOL_VALIDATE(_sg_is_valid_rendertarget_depth_format(img->pixel_format), _SG_VALIDATE_PASSDESC_DEPTH_INV_PIXELFORMAT); } return SOKOL_VALIDATE_END(); #endif } _SOKOL_PRIVATE bool _sg_validate_begin_pass(_sg_pass_t* pass) { #if !defined(SOKOL_DEBUG) _SOKOL_UNUSED(pass); return true; #else SOKOL_VALIDATE_BEGIN(); SOKOL_VALIDATE(pass->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_BEGINPASS_PASS); for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { const _sg_attachment_t* att = &pass->color_atts[i]; if (att->image) { SOKOL_VALIDATE(att->image->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_BEGINPASS_IMAGE); SOKOL_VALIDATE(att->image->slot.id == att->image_id.id, _SG_VALIDATE_BEGINPASS_IMAGE); } } if (pass->ds_att.image) { const _sg_attachment_t* att = &pass->ds_att; SOKOL_VALIDATE(att->image->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_BEGINPASS_IMAGE); SOKOL_VALIDATE(att->image->slot.id == att->image_id.id, _SG_VALIDATE_BEGINPASS_IMAGE); } return SOKOL_VALIDATE_END(); #endif } _SOKOL_PRIVATE bool _sg_validate_apply_pipeline(sg_pipeline pip_id) { #if !defined(SOKOL_DEBUG) _SOKOL_UNUSED(pip_id); return true; #else SOKOL_VALIDATE_BEGIN(); /* the pipeline object must be alive and valid */ SOKOL_VALIDATE(pip_id.id != SG_INVALID_ID, _SG_VALIDATE_APIP_PIPELINE_VALID_ID); const _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); SOKOL_VALIDATE(pip != 0, _SG_VALIDATE_APIP_PIPELINE_EXISTS); if (!pip) { return SOKOL_VALIDATE_END(); } SOKOL_VALIDATE(pip->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_APIP_PIPELINE_VALID); /* the pipeline's shader must be alive and valid */ SOKOL_ASSERT(pip->shader); SOKOL_VALIDATE(pip->shader->slot.id == pip->shader_id.id, _SG_VALIDATE_APIP_SHADER_EXISTS); SOKOL_VALIDATE(pip->shader->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_APIP_SHADER_VALID); /* check that pipeline attributes match current pass attributes */ const _sg_pass_t* pass = _sg_lookup_pass(&_sg.pools, _sg.cur_pass.id); if (pass) { /* an offscreen pass */ SOKOL_VALIDATE(pip->color_attachment_count == pass->num_color_atts, _SG_VALIDATE_APIP_ATT_COUNT); SOKOL_VALIDATE(pip->color_format == pass->color_atts[0].image->pixel_format, _SG_VALIDATE_APIP_COLOR_FORMAT); SOKOL_VALIDATE(pip->sample_count == pass->color_atts[0].image->sample_count, _SG_VALIDATE_APIP_SAMPLE_COUNT); if (pass->ds_att.image) { SOKOL_VALIDATE(pip->depth_format == pass->ds_att.image->pixel_format, _SG_VALIDATE_APIP_DEPTH_FORMAT); } else { SOKOL_VALIDATE(pip->depth_format == SG_PIXELFORMAT_NONE, _SG_VALIDATE_APIP_DEPTH_FORMAT); } } else { /* default pass */ SOKOL_VALIDATE(pip->color_attachment_count == 1, _SG_VALIDATE_APIP_ATT_COUNT); SOKOL_VALIDATE(pip->color_format == SG_PIXELFORMAT_RGBA8, _SG_VALIDATE_APIP_COLOR_FORMAT); SOKOL_VALIDATE(pip->depth_format == SG_PIXELFORMAT_DEPTHSTENCIL, _SG_VALIDATE_APIP_DEPTH_FORMAT); /* FIXME: hmm, we don't know if the default framebuffer is multisampled here */ } return SOKOL_VALIDATE_END(); #endif } _SOKOL_PRIVATE bool _sg_validate_apply_bindings(const sg_bindings* bindings) { #if !defined(SOKOL_DEBUG) _SOKOL_UNUSED(bindings); return true; #else SOKOL_VALIDATE_BEGIN(); /* a pipeline object must have been applied */ SOKOL_VALIDATE(_sg.cur_pipeline.id != SG_INVALID_ID, _SG_VALIDATE_ABND_PIPELINE); const _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, _sg.cur_pipeline.id); SOKOL_VALIDATE(pip != 0, _SG_VALIDATE_ABND_PIPELINE_EXISTS); if (!pip) { return SOKOL_VALIDATE_END(); } SOKOL_VALIDATE(pip->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_ABND_PIPELINE_VALID); SOKOL_ASSERT(pip->shader); /* has expected vertex buffers, and vertex buffers still exist */ for (int i = 0; i < SG_MAX_SHADERSTAGE_BUFFERS; i++) { if (bindings->vertex_buffers[i].id != SG_INVALID_ID) { SOKOL_VALIDATE(pip->vertex_layout_valid[i], _SG_VALIDATE_ABND_VBS); /* buffers in vertex-buffer-slots must be of type SG_BUFFERTYPE_VERTEXBUFFER */ const _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, bindings->vertex_buffers[i].id); SOKOL_VALIDATE(buf != 0, _SG_VALIDATE_ABND_VB_EXISTS); if (buf && buf->slot.state == SG_RESOURCESTATE_VALID) { SOKOL_VALIDATE(SG_BUFFERTYPE_VERTEXBUFFER == buf->type, _SG_VALIDATE_ABND_VB_TYPE); SOKOL_VALIDATE(!buf->append_overflow, _SG_VALIDATE_ABND_VB_OVERFLOW); } } else { /* vertex buffer provided in a slot which has no vertex layout in pipeline */ SOKOL_VALIDATE(!pip->vertex_layout_valid[i], _SG_VALIDATE_ABND_VBS); } } /* index buffer expected or not, and index buffer still exists */ if (pip->index_type == SG_INDEXTYPE_NONE) { /* pipeline defines non-indexed rendering, but index buffer provided */ SOKOL_VALIDATE(bindings->index_buffer.id == SG_INVALID_ID, _SG_VALIDATE_ABND_IB); } else { /* pipeline defines indexed rendering, but no index buffer provided */ SOKOL_VALIDATE(bindings->index_buffer.id != SG_INVALID_ID, _SG_VALIDATE_ABND_NO_IB); } if (bindings->index_buffer.id != SG_INVALID_ID) { /* buffer in index-buffer-slot must be of type SG_BUFFERTYPE_INDEXBUFFER */ const _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, bindings->index_buffer.id); SOKOL_VALIDATE(buf != 0, _SG_VALIDATE_ABND_IB_EXISTS); if (buf && buf->slot.state == SG_RESOURCESTATE_VALID) { SOKOL_VALIDATE(SG_BUFFERTYPE_INDEXBUFFER == buf->type, _SG_VALIDATE_ABND_IB_TYPE); SOKOL_VALIDATE(!buf->append_overflow, _SG_VALIDATE_ABND_IB_OVERFLOW); } } /* has expected vertex shader images */ for (int i = 0; i < SG_MAX_SHADERSTAGE_IMAGES; i++) { _sg_shader_stage_t* stage = &pip->shader->stage[SG_SHADERSTAGE_VS]; if (bindings->vs_images[i].id != SG_INVALID_ID) { SOKOL_VALIDATE(i < stage->num_images, _SG_VALIDATE_ABND_VS_IMGS); const _sg_image_t* img = _sg_lookup_image(&_sg.pools, bindings->vs_images[i].id); SOKOL_VALIDATE(img != 0, _SG_VALIDATE_ABND_VS_IMG_EXISTS); if (img && img->slot.state == SG_RESOURCESTATE_VALID) { SOKOL_VALIDATE(img->type == stage->images[i].type, _SG_VALIDATE_ABND_VS_IMG_TYPES); } } else { SOKOL_VALIDATE(i >= stage->num_images, _SG_VALIDATE_ABND_VS_IMGS); } } /* has expected fragment shader images */ for (int i = 0; i < SG_MAX_SHADERSTAGE_IMAGES; i++) { _sg_shader_stage_t* stage = &pip->shader->stage[SG_SHADERSTAGE_FS]; if (bindings->fs_images[i].id != SG_INVALID_ID) { SOKOL_VALIDATE(i < stage->num_images, _SG_VALIDATE_ABND_FS_IMGS); const _sg_image_t* img = _sg_lookup_image(&_sg.pools, bindings->fs_images[i].id); SOKOL_VALIDATE(img != 0, _SG_VALIDATE_ABND_FS_IMG_EXISTS); if (img && img->slot.state == SG_RESOURCESTATE_VALID) { SOKOL_VALIDATE(img->type == stage->images[i].type, _SG_VALIDATE_ABND_FS_IMG_TYPES); } } else { SOKOL_VALIDATE(i >= stage->num_images, _SG_VALIDATE_ABND_FS_IMGS); } } return SOKOL_VALIDATE_END(); #endif } _SOKOL_PRIVATE bool _sg_validate_apply_uniforms(sg_shader_stage stage_index, int ub_index, const void* data, int num_bytes) { _SOKOL_UNUSED(data); #if !defined(SOKOL_DEBUG) _SOKOL_UNUSED(stage_index); _SOKOL_UNUSED(ub_index); _SOKOL_UNUSED(num_bytes); return true; #else SOKOL_ASSERT((stage_index == SG_SHADERSTAGE_VS) || (stage_index == SG_SHADERSTAGE_FS)); SOKOL_ASSERT((ub_index >= 0) && (ub_index < SG_MAX_SHADERSTAGE_UBS)); SOKOL_VALIDATE_BEGIN(); SOKOL_VALIDATE(_sg.cur_pipeline.id != SG_INVALID_ID, _SG_VALIDATE_AUB_NO_PIPELINE); const _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, _sg.cur_pipeline.id); SOKOL_ASSERT(pip && (pip->slot.id == _sg.cur_pipeline.id)); SOKOL_ASSERT(pip->shader && (pip->shader->slot.id == pip->shader_id.id)); /* check that there is a uniform block at 'stage' and 'ub_index' */ const _sg_shader_stage_t* stage = &pip->shader->stage[stage_index]; SOKOL_VALIDATE(ub_index < stage->num_uniform_blocks, _SG_VALIDATE_AUB_NO_UB_AT_SLOT); /* check that the provided data size doesn't exceed the uniform block size */ SOKOL_VALIDATE(num_bytes <= stage->uniform_blocks[ub_index].size, _SG_VALIDATE_AUB_SIZE); return SOKOL_VALIDATE_END(); #endif } _SOKOL_PRIVATE bool _sg_validate_update_buffer(const _sg_buffer_t* buf, const void* data, int size) { #if !defined(SOKOL_DEBUG) _SOKOL_UNUSED(buf); _SOKOL_UNUSED(data); _SOKOL_UNUSED(size); return true; #else SOKOL_ASSERT(buf && data); SOKOL_VALIDATE_BEGIN(); SOKOL_VALIDATE(buf->usage != SG_USAGE_IMMUTABLE, _SG_VALIDATE_UPDATEBUF_USAGE); SOKOL_VALIDATE(buf->size >= size, _SG_VALIDATE_UPDATEBUF_SIZE); SOKOL_VALIDATE(buf->update_frame_index != _sg.frame_index, _SG_VALIDATE_UPDATEBUF_ONCE); SOKOL_VALIDATE(buf->append_frame_index != _sg.frame_index, _SG_VALIDATE_UPDATEBUF_APPEND); return SOKOL_VALIDATE_END(); #endif } _SOKOL_PRIVATE bool _sg_validate_append_buffer(const _sg_buffer_t* buf, const void* data, int size) { #if !defined(SOKOL_DEBUG) _SOKOL_UNUSED(buf); _SOKOL_UNUSED(data); _SOKOL_UNUSED(size); return true; #else SOKOL_ASSERT(buf && data); SOKOL_VALIDATE_BEGIN(); SOKOL_VALIDATE(buf->usage != SG_USAGE_IMMUTABLE, _SG_VALIDATE_APPENDBUF_USAGE); SOKOL_VALIDATE(buf->size >= (buf->append_pos+size), _SG_VALIDATE_APPENDBUF_SIZE); SOKOL_VALIDATE(buf->update_frame_index != _sg.frame_index, _SG_VALIDATE_APPENDBUF_UPDATE); return SOKOL_VALIDATE_END(); #endif } _SOKOL_PRIVATE bool _sg_validate_update_image(const _sg_image_t* img, const sg_image_content* data) { #if !defined(SOKOL_DEBUG) _SOKOL_UNUSED(img); _SOKOL_UNUSED(data); return true; #else SOKOL_ASSERT(img && data); SOKOL_VALIDATE_BEGIN(); SOKOL_VALIDATE(img->usage != SG_USAGE_IMMUTABLE, _SG_VALIDATE_UPDIMG_USAGE); SOKOL_VALIDATE(img->upd_frame_index != _sg.frame_index, _SG_VALIDATE_UPDIMG_ONCE); SOKOL_VALIDATE(!_sg_is_compressed_pixel_format(img->pixel_format), _SG_VALIDATE_UPDIMG_COMPRESSED); const int num_faces = (img->type == SG_IMAGETYPE_CUBE) ? 6 : 1; const int num_mips = img->num_mipmaps; for (int face_index = 0; face_index < num_faces; face_index++) { for (int mip_index = 0; mip_index < num_mips; mip_index++) { SOKOL_VALIDATE(0 != data->subimage[face_index][mip_index].ptr, _SG_VALIDATE_UPDIMG_NOTENOUGHDATA); const int mip_width = _sg_max(img->width >> mip_index, 1); const int mip_height = _sg_max(img->height >> mip_index, 1); const int bytes_per_slice = _sg_surface_pitch(img->pixel_format, mip_width, mip_height); const int expected_size = bytes_per_slice * img->depth; SOKOL_VALIDATE(data->subimage[face_index][mip_index].size <= expected_size, _SG_VALIDATE_UPDIMG_SIZE); } } return SOKOL_VALIDATE_END(); #endif } /*== fill in desc default values =============================================*/ _SOKOL_PRIVATE sg_buffer_desc _sg_buffer_desc_defaults(const sg_buffer_desc* desc) { sg_buffer_desc def = *desc; def.type = _sg_def(def.type, SG_BUFFERTYPE_VERTEXBUFFER); def.usage = _sg_def(def.usage, SG_USAGE_IMMUTABLE); return def; } _SOKOL_PRIVATE sg_image_desc _sg_image_desc_defaults(const sg_image_desc* desc) { sg_image_desc def = *desc; def.type = _sg_def(def.type, SG_IMAGETYPE_2D); def.depth = _sg_def(def.depth, 1); def.num_mipmaps = _sg_def(def.num_mipmaps, 1); def.usage = _sg_def(def.usage, SG_USAGE_IMMUTABLE); def.pixel_format = _sg_def(def.pixel_format, SG_PIXELFORMAT_RGBA8); def.sample_count = _sg_def(def.sample_count, 1); def.min_filter = _sg_def(def.min_filter, SG_FILTER_NEAREST); def.mag_filter = _sg_def(def.mag_filter, SG_FILTER_NEAREST); def.wrap_u = _sg_def(def.wrap_u, SG_WRAP_REPEAT); def.wrap_v = _sg_def(def.wrap_v, SG_WRAP_REPEAT); def.wrap_w = _sg_def(def.wrap_w, SG_WRAP_REPEAT); def.max_anisotropy = _sg_def(def.max_anisotropy, 1); def.max_lod = _sg_def_flt(def.max_lod, FLT_MAX); return def; } _SOKOL_PRIVATE sg_shader_desc _sg_shader_desc_defaults(const sg_shader_desc* desc) { sg_shader_desc def = *desc; #if defined(SOKOL_METAL) def.vs.entry = _sg_def(def.vs.entry, "_main"); def.fs.entry = _sg_def(def.fs.entry, "_main"); #else def.vs.entry = _sg_def(def.vs.entry, "main"); def.fs.entry = _sg_def(def.fs.entry, "main"); #endif for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { sg_shader_stage_desc* stage_desc = (stage_index == SG_SHADERSTAGE_VS)? &def.vs : &def.fs; for (int ub_index = 0; ub_index < SG_MAX_SHADERSTAGE_UBS; ub_index++) { sg_shader_uniform_block_desc* ub_desc = &stage_desc->uniform_blocks[ub_index]; if (0 == ub_desc->size) { break; } for (int u_index = 0; u_index < SG_MAX_UB_MEMBERS; u_index++) { sg_shader_uniform_desc* u_desc = &ub_desc->uniforms[u_index]; if (u_desc->type == SG_UNIFORMTYPE_INVALID) { break; } u_desc->array_count = _sg_def(u_desc->array_count, 1); } } } return def; } _SOKOL_PRIVATE sg_pipeline_desc _sg_pipeline_desc_defaults(const sg_pipeline_desc* desc) { sg_pipeline_desc def = *desc; def.primitive_type = _sg_def(def.primitive_type, SG_PRIMITIVETYPE_TRIANGLES); def.index_type = _sg_def(def.index_type, SG_INDEXTYPE_NONE); def.depth_stencil.stencil_front.fail_op = _sg_def(def.depth_stencil.stencil_front.fail_op, SG_STENCILOP_KEEP); def.depth_stencil.stencil_front.depth_fail_op = _sg_def(def.depth_stencil.stencil_front.depth_fail_op, SG_STENCILOP_KEEP); def.depth_stencil.stencil_front.pass_op = _sg_def(def.depth_stencil.stencil_front.pass_op, SG_STENCILOP_KEEP); def.depth_stencil.stencil_front.compare_func = _sg_def(def.depth_stencil.stencil_front.compare_func, SG_COMPAREFUNC_ALWAYS); def.depth_stencil.stencil_back.fail_op = _sg_def(def.depth_stencil.stencil_back.fail_op, SG_STENCILOP_KEEP); def.depth_stencil.stencil_back.depth_fail_op = _sg_def(def.depth_stencil.stencil_back.depth_fail_op, SG_STENCILOP_KEEP); def.depth_stencil.stencil_back.pass_op = _sg_def(def.depth_stencil.stencil_back.pass_op, SG_STENCILOP_KEEP); def.depth_stencil.stencil_back.compare_func = _sg_def(def.depth_stencil.stencil_back.compare_func, SG_COMPAREFUNC_ALWAYS); def.depth_stencil.depth_compare_func = _sg_def(def.depth_stencil.depth_compare_func, SG_COMPAREFUNC_ALWAYS); def.blend.src_factor_rgb = _sg_def(def.blend.src_factor_rgb, SG_BLENDFACTOR_ONE); def.blend.dst_factor_rgb = _sg_def(def.blend.dst_factor_rgb, SG_BLENDFACTOR_ZERO); def.blend.op_rgb = _sg_def(def.blend.op_rgb, SG_BLENDOP_ADD); def.blend.src_factor_alpha = _sg_def(def.blend.src_factor_alpha, SG_BLENDFACTOR_ONE); def.blend.dst_factor_alpha = _sg_def(def.blend.dst_factor_alpha, SG_BLENDFACTOR_ZERO); def.blend.op_alpha = _sg_def(def.blend.op_alpha, SG_BLENDOP_ADD); if (def.blend.color_write_mask == SG_COLORMASK_NONE) { def.blend.color_write_mask = 0; } else { def.blend.color_write_mask = (uint8_t) _sg_def((sg_color_mask)def.blend.color_write_mask, SG_COLORMASK_RGBA); } def.blend.color_attachment_count = _sg_def(def.blend.color_attachment_count, 1); def.blend.color_format = _sg_def(def.blend.color_format, SG_PIXELFORMAT_RGBA8); def.blend.depth_format = _sg_def(def.blend.depth_format, SG_PIXELFORMAT_DEPTHSTENCIL); def.rasterizer.cull_mode = _sg_def(def.rasterizer.cull_mode, SG_CULLMODE_NONE); def.rasterizer.face_winding = _sg_def(def.rasterizer.face_winding, SG_FACEWINDING_CW); def.rasterizer.sample_count = _sg_def(def.rasterizer.sample_count, 1); for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { sg_vertex_attr_desc* a_desc = &def.layout.attrs[attr_index]; if (a_desc->format == SG_VERTEXFORMAT_INVALID) { break; } SOKOL_ASSERT((a_desc->buffer_index >= 0) && (a_desc->buffer_index < SG_MAX_SHADERSTAGE_BUFFERS)); sg_buffer_layout_desc* b_desc = &def.layout.buffers[a_desc->buffer_index]; b_desc->step_func = _sg_def(b_desc->step_func, SG_VERTEXSTEP_PER_VERTEX); b_desc->step_rate = _sg_def(b_desc->step_rate, 1); } /* resolve vertex layout strides and offsets */ int auto_offset[SG_MAX_SHADERSTAGE_BUFFERS]; memset(auto_offset, 0, sizeof(auto_offset)); bool use_auto_offset = true; for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { /* to use computed offsets, *all* attr offsets must be 0 */ if (def.layout.attrs[attr_index].offset != 0) { use_auto_offset = false; } } for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { sg_vertex_attr_desc* a_desc = &def.layout.attrs[attr_index]; if (a_desc->format == SG_VERTEXFORMAT_INVALID) { break; } SOKOL_ASSERT((a_desc->buffer_index >= 0) && (a_desc->buffer_index < SG_MAX_SHADERSTAGE_BUFFERS)); if (use_auto_offset) { a_desc->offset = auto_offset[a_desc->buffer_index]; } auto_offset[a_desc->buffer_index] += _sg_vertexformat_bytesize(a_desc->format); } /* compute vertex strides if needed */ for (int buf_index = 0; buf_index < SG_MAX_SHADERSTAGE_BUFFERS; buf_index++) { sg_buffer_layout_desc* l_desc = &def.layout.buffers[buf_index]; if (l_desc->stride == 0) { l_desc->stride = auto_offset[buf_index]; } } return def; } _SOKOL_PRIVATE sg_pass_desc _sg_pass_desc_defaults(const sg_pass_desc* desc) { /* FIXME: no values to replace in sg_pass_desc? */ sg_pass_desc def = *desc; return def; } /*== allocate/initialize resource private functions ==========================*/ _SOKOL_PRIVATE sg_buffer _sg_alloc_buffer(void) { sg_buffer res; int slot_index = _sg_pool_alloc_index(&_sg.pools.buffer_pool); if (_SG_INVALID_SLOT_INDEX != slot_index) { res.id = _sg_slot_alloc(&_sg.pools.buffer_pool, &_sg.pools.buffers[slot_index].slot, slot_index); } else { /* pool is exhausted */ res.id = SG_INVALID_ID; } return res; } _SOKOL_PRIVATE sg_image _sg_alloc_image(void) { sg_image res; int slot_index = _sg_pool_alloc_index(&_sg.pools.image_pool); if (_SG_INVALID_SLOT_INDEX != slot_index) { res.id = _sg_slot_alloc(&_sg.pools.image_pool, &_sg.pools.images[slot_index].slot, slot_index); } else { /* pool is exhausted */ res.id = SG_INVALID_ID; } return res; } _SOKOL_PRIVATE sg_shader _sg_alloc_shader(void) { sg_shader res; int slot_index = _sg_pool_alloc_index(&_sg.pools.shader_pool); if (_SG_INVALID_SLOT_INDEX != slot_index) { res.id = _sg_slot_alloc(&_sg.pools.shader_pool, &_sg.pools.shaders[slot_index].slot, slot_index); } else { /* pool is exhausted */ res.id = SG_INVALID_ID; } return res; } _SOKOL_PRIVATE sg_pipeline _sg_alloc_pipeline(void) { sg_pipeline res; int slot_index = _sg_pool_alloc_index(&_sg.pools.pipeline_pool); if (_SG_INVALID_SLOT_INDEX != slot_index) { res.id =_sg_slot_alloc(&_sg.pools.pipeline_pool, &_sg.pools.pipelines[slot_index].slot, slot_index); } else { /* pool is exhausted */ res.id = SG_INVALID_ID; } return res; } _SOKOL_PRIVATE sg_pass _sg_alloc_pass(void) { sg_pass res; int slot_index = _sg_pool_alloc_index(&_sg.pools.pass_pool); if (_SG_INVALID_SLOT_INDEX != slot_index) { res.id = _sg_slot_alloc(&_sg.pools.pass_pool, &_sg.pools.passes[slot_index].slot, slot_index); } else { /* pool is exhausted */ res.id = SG_INVALID_ID; } return res; } _SOKOL_PRIVATE void _sg_init_buffer(sg_buffer buf_id, const sg_buffer_desc* desc) { SOKOL_ASSERT(buf_id.id != SG_INVALID_ID && desc); _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); SOKOL_ASSERT(buf && buf->slot.state == SG_RESOURCESTATE_ALLOC); buf->slot.ctx_id = _sg.active_context.id; if (_sg_validate_buffer_desc(desc)) { buf->slot.state = _sg_create_buffer(buf, desc); } else { buf->slot.state = SG_RESOURCESTATE_FAILED; } SOKOL_ASSERT((buf->slot.state == SG_RESOURCESTATE_VALID)||(buf->slot.state == SG_RESOURCESTATE_FAILED)); } _SOKOL_PRIVATE void _sg_init_image(sg_image img_id, const sg_image_desc* desc) { SOKOL_ASSERT(img_id.id != SG_INVALID_ID && desc); _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); SOKOL_ASSERT(img && img->slot.state == SG_RESOURCESTATE_ALLOC); img->slot.ctx_id = _sg.active_context.id; if (_sg_validate_image_desc(desc)) { img->slot.state = _sg_create_image(img, desc); } else { img->slot.state = SG_RESOURCESTATE_FAILED; } SOKOL_ASSERT((img->slot.state == SG_RESOURCESTATE_VALID)||(img->slot.state == SG_RESOURCESTATE_FAILED)); } _SOKOL_PRIVATE void _sg_init_shader(sg_shader shd_id, const sg_shader_desc* desc) { SOKOL_ASSERT(shd_id.id != SG_INVALID_ID && desc); _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); SOKOL_ASSERT(shd && shd->slot.state == SG_RESOURCESTATE_ALLOC); shd->slot.ctx_id = _sg.active_context.id; if (_sg_validate_shader_desc(desc)) { shd->slot.state = _sg_create_shader(shd, desc); } else { shd->slot.state = SG_RESOURCESTATE_FAILED; } SOKOL_ASSERT((shd->slot.state == SG_RESOURCESTATE_VALID)||(shd->slot.state == SG_RESOURCESTATE_FAILED)); } _SOKOL_PRIVATE void _sg_init_pipeline(sg_pipeline pip_id, const sg_pipeline_desc* desc) { SOKOL_ASSERT(pip_id.id != SG_INVALID_ID && desc); _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); SOKOL_ASSERT(pip && pip->slot.state == SG_RESOURCESTATE_ALLOC); pip->slot.ctx_id = _sg.active_context.id; if (_sg_validate_pipeline_desc(desc)) { _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, desc->shader.id); SOKOL_ASSERT(shd && shd->slot.state == SG_RESOURCESTATE_VALID); pip->slot.state = _sg_create_pipeline(pip, shd, desc); } else { pip->slot.state = SG_RESOURCESTATE_FAILED; } SOKOL_ASSERT((pip->slot.state == SG_RESOURCESTATE_VALID)||(pip->slot.state == SG_RESOURCESTATE_FAILED)); } _SOKOL_PRIVATE void _sg_init_pass(sg_pass pass_id, const sg_pass_desc* desc) { SOKOL_ASSERT(pass_id.id != SG_INVALID_ID && desc); _sg_pass_t* pass = _sg_lookup_pass(&_sg.pools, pass_id.id); SOKOL_ASSERT(pass && pass->slot.state == SG_RESOURCESTATE_ALLOC); pass->slot.ctx_id = _sg.active_context.id; if (_sg_validate_pass_desc(desc)) { /* lookup pass attachment image pointers */ _sg_image_t* att_imgs[SG_MAX_COLOR_ATTACHMENTS + 1]; for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { if (desc->color_attachments[i].image.id) { att_imgs[i] = _sg_lookup_image(&_sg.pools, desc->color_attachments[i].image.id); SOKOL_ASSERT(att_imgs[i] && att_imgs[i]->slot.state == SG_RESOURCESTATE_VALID); } else { att_imgs[i] = 0; } } const int ds_att_index = SG_MAX_COLOR_ATTACHMENTS; if (desc->depth_stencil_attachment.image.id) { att_imgs[ds_att_index] = _sg_lookup_image(&_sg.pools, desc->depth_stencil_attachment.image.id); SOKOL_ASSERT(att_imgs[ds_att_index] && att_imgs[ds_att_index]->slot.state == SG_RESOURCESTATE_VALID); } else { att_imgs[ds_att_index] = 0; } pass->slot.state = _sg_create_pass(pass, att_imgs, desc); } else { pass->slot.state = SG_RESOURCESTATE_FAILED; } SOKOL_ASSERT((pass->slot.state == SG_RESOURCESTATE_VALID)||(pass->slot.state == SG_RESOURCESTATE_FAILED)); } /*== PUBLIC API FUNCTIONS ====================================================*/ SOKOL_API_IMPL void sg_setup(const sg_desc* desc) { SOKOL_ASSERT(desc); SOKOL_ASSERT((desc->_start_canary == 0) && (desc->_end_canary == 0)); memset(&_sg, 0, sizeof(_sg)); _sg.desc = *desc; /* replace zero-init items with their default values */ _sg.desc.buffer_pool_size = _sg_def(_sg.desc.buffer_pool_size, _SG_DEFAULT_BUFFER_POOL_SIZE); _sg.desc.image_pool_size = _sg_def(_sg.desc.image_pool_size, _SG_DEFAULT_IMAGE_POOL_SIZE); _sg.desc.shader_pool_size = _sg_def(_sg.desc.shader_pool_size, _SG_DEFAULT_SHADER_POOL_SIZE); _sg.desc.pipeline_pool_size = _sg_def(_sg.desc.pipeline_pool_size, _SG_DEFAULT_PIPELINE_POOL_SIZE); _sg.desc.pass_pool_size = _sg_def(_sg.desc.pass_pool_size, _SG_DEFAULT_PASS_POOL_SIZE); _sg.desc.context_pool_size = _sg_def(_sg.desc.context_pool_size, _SG_DEFAULT_CONTEXT_POOL_SIZE); _sg.desc.mtl_global_uniform_buffer_size = _sg_def(_sg.desc.mtl_global_uniform_buffer_size, _SG_MTL_DEFAULT_UB_SIZE); _sg.desc.mtl_sampler_cache_size = _sg_def(_sg.desc.mtl_sampler_cache_size, _SG_MTL_DEFAULT_SAMPLER_CACHE_CAPACITY); _sg_setup_pools(&_sg.pools, &_sg.desc); _sg.frame_index = 1; _sg_setup_backend(&_sg.desc); sg_setup_context(); _sg.valid = true; } SOKOL_API_IMPL void sg_shutdown(void) { /* can only delete resources for the currently set context here, if multiple contexts are used, the app code must take care of properly releasing them (since only the app code can switch between 3D-API contexts) */ if (_sg.active_context.id != SG_INVALID_ID) { _sg_context_t* ctx = _sg_lookup_context(&_sg.pools, _sg.active_context.id); if (ctx) { _sg_destroy_all_resources(&_sg.pools, _sg.active_context.id); _sg_destroy_context(ctx); } } _sg_discard_backend(); _sg_discard_pools(&_sg.pools); _sg.valid = false; } SOKOL_API_IMPL bool sg_isvalid(void) { return _sg.valid; } SOKOL_API_IMPL sg_desc sg_query_desc(void) { return _sg.desc; } SOKOL_API_IMPL sg_backend sg_query_backend(void) { #if defined(SOKOL_GLCORE33) return SG_BACKEND_GLCORE33; #elif defined(SOKOL_GLES2) return SG_BACKEND_GLES2; #elif defined(SOKOL_GLES3) return _sg.gl.gles2 ? SG_BACKEND_GLES2 : SG_BACKEND_GLES3; #elif defined(SOKOL_D3D11) return SG_BACKEND_D3D11; #elif defined(SOKOL_METAL) #if defined(TARGET_OS_IPHONE) && !TARGET_OS_IPHONE return SG_BACKEND_METAL_MACOS; #else #if defined(TARGET_IPHONE_SIMULATOR) && TARGET_IPHONE_SIMULATOR return SG_BACKEND_METAL_SIMULATOR; #else return SG_BACKEND_METAL_IOS; #endif #endif #elif defined(SOKOL_DUMMY_BACKEND) return SG_BACKEND_DUMMY; #endif } SOKOL_API_IMPL bool sg_query_feature(sg_feature f) { bool res = _sg_query_feature(f); _SG_TRACE_ARGS(query_feature, f, res); return res; } SOKOL_API_IMPL sg_context sg_setup_context(void) { sg_context res; int slot_index = _sg_pool_alloc_index(&_sg.pools.context_pool); if (_SG_INVALID_SLOT_INDEX != slot_index) { res.id = _sg_slot_alloc(&_sg.pools.context_pool, &_sg.pools.contexts[slot_index].slot, slot_index); _sg_context_t* ctx = _sg_context_at(&_sg.pools, res.id); ctx->slot.state = _sg_create_context(ctx); SOKOL_ASSERT(ctx->slot.state == SG_RESOURCESTATE_VALID); _sg_activate_context(ctx); } else { /* pool is exhausted */ res.id = SG_INVALID_ID; } _sg.active_context = res; return res; } SOKOL_API_IMPL void sg_discard_context(sg_context ctx_id) { _sg_destroy_all_resources(&_sg.pools, ctx_id.id); _sg_context_t* ctx = _sg_lookup_context(&_sg.pools, ctx_id.id); if (ctx) { _sg_destroy_context(ctx); _sg_reset_context(ctx); _sg_pool_free_index(&_sg.pools.context_pool, _sg_slot_index(ctx_id.id)); } _sg.active_context.id = SG_INVALID_ID; _sg_activate_context(0); } SOKOL_API_IMPL void sg_activate_context(sg_context ctx_id) { _sg.active_context = ctx_id; _sg_context_t* ctx = _sg_lookup_context(&_sg.pools, ctx_id.id); /* NOTE: ctx can be 0 here if the context is no longer valid */ _sg_activate_context(ctx); } SOKOL_API_IMPL sg_trace_hooks sg_install_trace_hooks(const sg_trace_hooks* trace_hooks) { SOKOL_ASSERT(trace_hooks); #if defined(SOKOL_TRACE_HOOKS) sg_trace_hooks old_hooks = _sg.hooks; _sg.hooks = *trace_hooks; #else static sg_trace_hooks old_hooks; SOKOL_LOG("sg_install_trace_hooks() called, but SG_TRACE_HOOKS is not defined!"); #endif return old_hooks; } SOKOL_API_IMPL sg_buffer sg_alloc_buffer(void) { sg_buffer res = _sg_alloc_buffer(); _SG_TRACE_ARGS(alloc_buffer, res); return res; } SOKOL_API_IMPL sg_image sg_alloc_image(void) { sg_image res = _sg_alloc_image(); _SG_TRACE_ARGS(alloc_image, res); return res; } SOKOL_API_IMPL sg_shader sg_alloc_shader(void) { sg_shader res = _sg_alloc_shader(); _SG_TRACE_ARGS(alloc_shader, res); return res; } SOKOL_API_IMPL sg_pipeline sg_alloc_pipeline(void) { sg_pipeline res = _sg_alloc_pipeline(); _SG_TRACE_ARGS(alloc_pipeline, res); return res; } SOKOL_API_IMPL sg_pass sg_alloc_pass(void) { sg_pass res = _sg_alloc_pass(); _SG_TRACE_ARGS(alloc_pass, res); return res; } SOKOL_API_IMPL void sg_init_buffer(sg_buffer buf_id, const sg_buffer_desc* desc) { sg_buffer_desc desc_def = _sg_buffer_desc_defaults(desc); _sg_init_buffer(buf_id, &desc_def); _SG_TRACE_ARGS(init_buffer, buf_id, &desc_def); } SOKOL_API_IMPL void sg_init_image(sg_image img_id, const sg_image_desc* desc) { sg_image_desc desc_def = _sg_image_desc_defaults(desc); _sg_init_image(img_id, &desc_def); _SG_TRACE_ARGS(init_image, img_id, &desc_def); } SOKOL_API_IMPL void sg_init_shader(sg_shader shd_id, const sg_shader_desc* desc) { sg_shader_desc desc_def = _sg_shader_desc_defaults(desc); _sg_init_shader(shd_id, &desc_def); _SG_TRACE_ARGS(init_shader, shd_id, &desc_def); } SOKOL_API_IMPL void sg_init_pipeline(sg_pipeline pip_id, const sg_pipeline_desc* desc) { sg_pipeline_desc desc_def = _sg_pipeline_desc_defaults(desc); _sg_init_pipeline(pip_id, &desc_def); _SG_TRACE_ARGS(init_pipeline, pip_id, &desc_def); } SOKOL_API_IMPL void sg_init_pass(sg_pass pass_id, const sg_pass_desc* desc) { sg_pass_desc desc_def = _sg_pass_desc_defaults(desc); _sg_init_pass(pass_id, &desc_def); _SG_TRACE_ARGS(init_pass, pass_id, &desc_def); } /*-- set allocated resource to failed state ----------------------------------*/ SOKOL_API_IMPL void sg_fail_buffer(sg_buffer buf_id) { SOKOL_ASSERT(buf_id.id != SG_INVALID_ID); _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); SOKOL_ASSERT(buf && buf->slot.state == SG_RESOURCESTATE_ALLOC); buf->slot.ctx_id = _sg.active_context.id; buf->slot.state = SG_RESOURCESTATE_FAILED; _SG_TRACE_ARGS(fail_buffer, buf_id); } SOKOL_API_IMPL void sg_fail_image(sg_image img_id) { SOKOL_ASSERT(img_id.id != SG_INVALID_ID); _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); SOKOL_ASSERT(img && img->slot.state == SG_RESOURCESTATE_ALLOC); img->slot.ctx_id = _sg.active_context.id; img->slot.state = SG_RESOURCESTATE_FAILED; _SG_TRACE_ARGS(fail_image, img_id); } SOKOL_API_IMPL void sg_fail_shader(sg_shader shd_id) { SOKOL_ASSERT(shd_id.id != SG_INVALID_ID); _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); SOKOL_ASSERT(shd && shd->slot.state == SG_RESOURCESTATE_ALLOC); shd->slot.ctx_id = _sg.active_context.id; shd->slot.state = SG_RESOURCESTATE_FAILED; _SG_TRACE_ARGS(fail_shader, shd_id); } SOKOL_API_IMPL void sg_fail_pipeline(sg_pipeline pip_id) { SOKOL_ASSERT(pip_id.id != SG_INVALID_ID); _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); SOKOL_ASSERT(pip && pip->slot.state == SG_RESOURCESTATE_ALLOC); pip->slot.ctx_id = _sg.active_context.id; pip->slot.state = SG_RESOURCESTATE_FAILED; _SG_TRACE_ARGS(fail_pipeline, pip_id); } SOKOL_API_IMPL void sg_fail_pass(sg_pass pass_id) { SOKOL_ASSERT(pass_id.id != SG_INVALID_ID); _sg_pass_t* pass = _sg_lookup_pass(&_sg.pools, pass_id.id); SOKOL_ASSERT(pass && pass->slot.state == SG_RESOURCESTATE_ALLOC); pass->slot.ctx_id = _sg.active_context.id; pass->slot.state = SG_RESOURCESTATE_FAILED; _SG_TRACE_ARGS(fail_pass, pass_id); } /*-- get resource state */ SOKOL_API_IMPL sg_resource_state sg_query_buffer_state(sg_buffer buf_id) { _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); sg_resource_state res = buf ? buf->slot.state : SG_RESOURCESTATE_INVALID; return res; } SOKOL_API_IMPL sg_resource_state sg_query_image_state(sg_image img_id) { _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); sg_resource_state res = img ? img->slot.state : SG_RESOURCESTATE_INVALID; return res; } SOKOL_API_IMPL sg_resource_state sg_query_shader_state(sg_shader shd_id) { _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); sg_resource_state res = shd ? shd->slot.state : SG_RESOURCESTATE_INVALID; return res; } SOKOL_API_IMPL sg_resource_state sg_query_pipeline_state(sg_pipeline pip_id) { _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); sg_resource_state res = pip ? pip->slot.state : SG_RESOURCESTATE_INVALID; return res; } SOKOL_API_IMPL sg_resource_state sg_query_pass_state(sg_pass pass_id) { _sg_pass_t* pass = _sg_lookup_pass(&_sg.pools, pass_id.id); sg_resource_state res = pass ? pass->slot.state : SG_RESOURCESTATE_INVALID; return res; } /*-- allocate and initialize resource ----------------------------------------*/ SOKOL_API_IMPL sg_buffer sg_make_buffer(const sg_buffer_desc* desc) { SOKOL_ASSERT(desc); sg_buffer_desc desc_def = _sg_buffer_desc_defaults(desc); sg_buffer buf_id = _sg_alloc_buffer(); if (buf_id.id != SG_INVALID_ID) { _sg_init_buffer(buf_id, &desc_def); } else { SOKOL_LOG("buffer pool exhausted!"); _SG_TRACE_NOARGS(err_buffer_pool_exhausted); } _SG_TRACE_ARGS(make_buffer, &desc_def, buf_id); return buf_id; } SOKOL_API_IMPL sg_image sg_make_image(const sg_image_desc* desc) { SOKOL_ASSERT(desc); sg_image_desc desc_def = _sg_image_desc_defaults(desc); sg_image img_id = _sg_alloc_image(); if (img_id.id != SG_INVALID_ID) { _sg_init_image(img_id, &desc_def); } else { SOKOL_LOG("image pool exhausted!"); _SG_TRACE_NOARGS(err_image_pool_exhausted); } _SG_TRACE_ARGS(make_image, &desc_def, img_id); return img_id; } SOKOL_API_IMPL sg_shader sg_make_shader(const sg_shader_desc* desc) { SOKOL_ASSERT(desc); sg_shader_desc desc_def = _sg_shader_desc_defaults(desc); sg_shader shd_id = _sg_alloc_shader(); if (shd_id.id != SG_INVALID_ID) { _sg_init_shader(shd_id, &desc_def); } else { SOKOL_LOG("shader pool exhausted!"); _SG_TRACE_NOARGS(err_shader_pool_exhausted); } _SG_TRACE_ARGS(make_shader, &desc_def, shd_id); return shd_id; } SOKOL_API_IMPL sg_pipeline sg_make_pipeline(const sg_pipeline_desc* desc) { SOKOL_ASSERT(desc); sg_pipeline_desc desc_def = _sg_pipeline_desc_defaults(desc); sg_pipeline pip_id = _sg_alloc_pipeline(); if (pip_id.id != SG_INVALID_ID) { _sg_init_pipeline(pip_id, &desc_def); } else { SOKOL_LOG("pipeline pool exhausted!"); _SG_TRACE_NOARGS(err_pipeline_pool_exhausted); } _SG_TRACE_ARGS(make_pipeline, &desc_def, pip_id); return pip_id; } SOKOL_API_IMPL sg_pass sg_make_pass(const sg_pass_desc* desc) { SOKOL_ASSERT(desc); sg_pass_desc desc_def = _sg_pass_desc_defaults(desc); sg_pass pass_id = _sg_alloc_pass(); if (pass_id.id != SG_INVALID_ID) { _sg_init_pass(pass_id, &desc_def); } else { SOKOL_LOG("pass pool exhausted!"); _SG_TRACE_NOARGS(err_pass_pool_exhausted); } _SG_TRACE_ARGS(make_pass, &desc_def, pass_id); return pass_id; } /*-- destroy resource --------------------------------------------------------*/ SOKOL_API_IMPL void sg_destroy_buffer(sg_buffer buf_id) { _SG_TRACE_ARGS(destroy_buffer, buf_id); _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); if (buf) { if (buf->slot.ctx_id == _sg.active_context.id) { _sg_destroy_buffer(buf); _sg_reset_buffer(buf); _sg_pool_free_index(&_sg.pools.buffer_pool, _sg_slot_index(buf_id.id)); } else { SOKOL_LOG("sg_destroy_buffer: active context mismatch (must be same as for creation)"); _SG_TRACE_NOARGS(err_context_mismatch); } } } SOKOL_API_IMPL void sg_destroy_image(sg_image img_id) { _SG_TRACE_ARGS(destroy_image, img_id); _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); if (img) { if (img->slot.ctx_id == _sg.active_context.id) { _sg_destroy_image(img); _sg_reset_image(img); _sg_pool_free_index(&_sg.pools.image_pool, _sg_slot_index(img_id.id)); } else { SOKOL_LOG("sg_destroy_image: active context mismatch (must be same as for creation)"); _SG_TRACE_NOARGS(err_context_mismatch); } } } SOKOL_API_IMPL void sg_destroy_shader(sg_shader shd_id) { _SG_TRACE_ARGS(destroy_shader, shd_id); _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); if (shd) { if (shd->slot.ctx_id == _sg.active_context.id) { _sg_destroy_shader(shd); _sg_reset_shader(shd); _sg_pool_free_index(&_sg.pools.shader_pool, _sg_slot_index(shd_id.id)); } else { SOKOL_LOG("sg_destroy_shader: active context mismatch (must be same as for creation)"); _SG_TRACE_NOARGS(err_context_mismatch); } } } SOKOL_API_IMPL void sg_destroy_pipeline(sg_pipeline pip_id) { _SG_TRACE_ARGS(destroy_pipeline, pip_id); _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); if (pip) { if (pip->slot.ctx_id == _sg.active_context.id) { _sg_destroy_pipeline(pip); _sg_reset_pipeline(pip); _sg_pool_free_index(&_sg.pools.pipeline_pool, _sg_slot_index(pip_id.id)); } else { SOKOL_LOG("sg_destroy_pipeline: active context mismatch (must be same as for creation)"); _SG_TRACE_NOARGS(err_context_mismatch); } } } SOKOL_API_IMPL void sg_destroy_pass(sg_pass pass_id) { _SG_TRACE_ARGS(destroy_pass, pass_id); _sg_pass_t* pass = _sg_lookup_pass(&_sg.pools, pass_id.id); if (pass) { if (pass->slot.ctx_id == _sg.active_context.id) { _sg_destroy_pass(pass); _sg_reset_pass(pass); _sg_pool_free_index(&_sg.pools.pass_pool, _sg_slot_index(pass_id.id)); } else { SOKOL_LOG("sg_destroy_pass: active context mismatch (must be same as for creation)"); _SG_TRACE_NOARGS(err_context_mismatch); } } } SOKOL_API_IMPL void sg_begin_default_pass(const sg_pass_action* pass_action, int width, int height) { SOKOL_ASSERT(pass_action); SOKOL_ASSERT((pass_action->_start_canary == 0) && (pass_action->_end_canary == 0)); sg_pass_action pa; _sg_resolve_default_pass_action(pass_action, &pa); _sg.cur_pass.id = SG_INVALID_ID; _sg.pass_valid = true; _sg_begin_pass(0, &pa, width, height); _SG_TRACE_ARGS(begin_default_pass, pass_action, width, height); } SOKOL_API_IMPL void sg_begin_pass(sg_pass pass_id, const sg_pass_action* pass_action) { SOKOL_ASSERT(pass_action); SOKOL_ASSERT((pass_action->_start_canary == 0) && (pass_action->_end_canary == 0)); _sg.cur_pass = pass_id; _sg_pass_t* pass = _sg_lookup_pass(&_sg.pools, pass_id.id); if (pass && _sg_validate_begin_pass(pass)) { _sg.pass_valid = true; sg_pass_action pa; _sg_resolve_default_pass_action(pass_action, &pa); const int w = pass->color_atts[0].image->width; const int h = pass->color_atts[0].image->height; _sg_begin_pass(pass, &pa, w, h); _SG_TRACE_ARGS(begin_pass, pass_id, pass_action); } else { _sg.pass_valid = false; _SG_TRACE_NOARGS(err_pass_invalid); } } SOKOL_API_IMPL void sg_apply_viewport(int x, int y, int width, int height, bool origin_top_left) { if (!_sg.pass_valid) { _SG_TRACE_NOARGS(err_pass_invalid); return; } _sg_apply_viewport(x, y, width, height, origin_top_left); _SG_TRACE_ARGS(apply_viewport, x, y, width, height, origin_top_left); } SOKOL_API_IMPL void sg_apply_scissor_rect(int x, int y, int width, int height, bool origin_top_left) { if (!_sg.pass_valid) { _SG_TRACE_NOARGS(err_pass_invalid); return; } _sg_apply_scissor_rect(x, y, width, height, origin_top_left); _SG_TRACE_ARGS(apply_scissor_rect, x, y, width, height, origin_top_left); } SOKOL_API_IMPL void sg_apply_pipeline(sg_pipeline pip_id) { _sg.bindings_valid = false; if (!_sg_validate_apply_pipeline(pip_id)) { _sg.next_draw_valid = false; _SG_TRACE_NOARGS(err_draw_invalid); return; } if (!_sg.pass_valid) { _SG_TRACE_NOARGS(err_pass_invalid); return; } _sg.cur_pipeline = pip_id; _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); SOKOL_ASSERT(pip); _sg.next_draw_valid = (SG_RESOURCESTATE_VALID == pip->slot.state); SOKOL_ASSERT(pip->shader && (pip->shader->slot.id == pip->shader_id.id)); _sg_apply_pipeline(pip); _SG_TRACE_ARGS(apply_pipeline, pip_id); } SOKOL_API_IMPL void sg_apply_bindings(const sg_bindings* bindings) { SOKOL_ASSERT(bindings); SOKOL_ASSERT((bindings->_start_canary == 0) && (bindings->_end_canary==0)); if (!_sg_validate_apply_bindings(bindings)) { _sg.next_draw_valid = false; _SG_TRACE_NOARGS(err_draw_invalid); return; } _sg.bindings_valid = true; _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, _sg.cur_pipeline.id); SOKOL_ASSERT(pip); _sg_buffer_t* vbs[SG_MAX_SHADERSTAGE_BUFFERS] = { 0 }; int num_vbs = 0; for (int i = 0; i < SG_MAX_SHADERSTAGE_BUFFERS; i++, num_vbs++) { if (bindings->vertex_buffers[i].id) { vbs[i] = _sg_lookup_buffer(&_sg.pools, bindings->vertex_buffers[i].id); SOKOL_ASSERT(vbs[i]); _sg.next_draw_valid &= (SG_RESOURCESTATE_VALID == vbs[i]->slot.state); _sg.next_draw_valid &= !vbs[i]->append_overflow; } else { break; } } _sg_buffer_t* ib = 0; if (bindings->index_buffer.id) { ib = _sg_lookup_buffer(&_sg.pools, bindings->index_buffer.id); SOKOL_ASSERT(ib); _sg.next_draw_valid &= (SG_RESOURCESTATE_VALID == ib->slot.state); _sg.next_draw_valid &= !ib->append_overflow; } _sg_image_t* vs_imgs[SG_MAX_SHADERSTAGE_IMAGES] = { 0 }; int num_vs_imgs = 0; for (int i = 0; i < SG_MAX_SHADERSTAGE_IMAGES; i++, num_vs_imgs++) { if (bindings->vs_images[i].id) { vs_imgs[i] = _sg_lookup_image(&_sg.pools, bindings->vs_images[i].id); SOKOL_ASSERT(vs_imgs[i]); _sg.next_draw_valid &= (SG_RESOURCESTATE_VALID == vs_imgs[i]->slot.state); } else { break; } } _sg_image_t* fs_imgs[SG_MAX_SHADERSTAGE_IMAGES] = { 0 }; int num_fs_imgs = 0; for (int i = 0; i < SG_MAX_SHADERSTAGE_IMAGES; i++, num_fs_imgs++) { if (bindings->fs_images[i].id) { fs_imgs[i] = _sg_lookup_image(&_sg.pools, bindings->fs_images[i].id); SOKOL_ASSERT(fs_imgs[i]); _sg.next_draw_valid &= (SG_RESOURCESTATE_VALID == fs_imgs[i]->slot.state); } else { break; } } if (_sg.next_draw_valid) { const int* vb_offsets = bindings->vertex_buffer_offsets; int ib_offset = bindings->index_buffer_offset; _sg_apply_bindings(pip, vbs, vb_offsets, num_vbs, ib, ib_offset, vs_imgs, num_vs_imgs, fs_imgs, num_fs_imgs); _SG_TRACE_ARGS(apply_bindings, bindings); } else { _SG_TRACE_NOARGS(err_draw_invalid); } } SOKOL_API_IMPL void sg_apply_uniforms(sg_shader_stage stage, int ub_index, const void* data, int num_bytes) { SOKOL_ASSERT((stage == SG_SHADERSTAGE_VS) || (stage == SG_SHADERSTAGE_FS)); SOKOL_ASSERT((ub_index >= 0) && (ub_index < SG_MAX_SHADERSTAGE_UBS)); SOKOL_ASSERT(data && (num_bytes > 0)); if (!_sg_validate_apply_uniforms(stage, ub_index, data, num_bytes)) { _sg.next_draw_valid = false; _SG_TRACE_NOARGS(err_draw_invalid); return; } if (!_sg.pass_valid) { _SG_TRACE_NOARGS(err_pass_invalid); return; } if (!_sg.next_draw_valid) { _SG_TRACE_NOARGS(err_draw_invalid); } _sg_apply_uniforms(stage, ub_index, data, num_bytes); _SG_TRACE_ARGS(apply_uniforms, stage, ub_index, data, num_bytes); } SOKOL_API_IMPL void sg_draw(int base_element, int num_elements, int num_instances) { #if defined(SOKOL_DEBUG) if (!_sg.bindings_valid) { SOKOL_LOG("attempting to draw without resource bindings"); } #endif if (!_sg.pass_valid) { _SG_TRACE_NOARGS(err_pass_invalid); return; } if (!_sg.next_draw_valid) { _SG_TRACE_NOARGS(err_draw_invalid); return; } if (!_sg.bindings_valid) { _SG_TRACE_NOARGS(err_bindings_invalid); return; } _sg_draw(base_element, num_elements, num_instances); _SG_TRACE_ARGS(draw, base_element, num_elements, num_instances); } SOKOL_API_IMPL void sg_end_pass(void) { if (!_sg.pass_valid) { _SG_TRACE_NOARGS(err_pass_invalid); return; } _sg_end_pass(); _sg.cur_pass.id = SG_INVALID_ID; _sg.cur_pipeline.id = SG_INVALID_ID; _sg.pass_valid = false; _SG_TRACE_NOARGS(end_pass); } SOKOL_API_IMPL void sg_commit(void) { _sg_commit(); _SG_TRACE_NOARGS(commit); _sg.frame_index++; } SOKOL_API_IMPL void sg_reset_state_cache(void) { _sg_reset_state_cache(); _SG_TRACE_NOARGS(reset_state_cache); } SOKOL_API_IMPL void sg_update_buffer(sg_buffer buf_id, const void* data, int num_bytes) { _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); if ((num_bytes > 0) && buf && (buf->slot.state == SG_RESOURCESTATE_VALID)) { if (_sg_validate_update_buffer(buf, data, num_bytes)) { SOKOL_ASSERT(num_bytes <= buf->size); /* only one update allowed per buffer and frame */ SOKOL_ASSERT(buf->update_frame_index != _sg.frame_index); /* update and append on same buffer in same frame not allowed */ SOKOL_ASSERT(buf->append_frame_index != _sg.frame_index); _sg_update_buffer(buf, data, num_bytes); buf->update_frame_index = _sg.frame_index; } } _SG_TRACE_ARGS(update_buffer, buf_id, data, num_bytes); } SOKOL_API_IMPL int sg_append_buffer(sg_buffer buf_id, const void* data, int num_bytes) { _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); int result; if (buf) { /* rewind append cursor in a new frame */ if (buf->append_frame_index != _sg.frame_index) { buf->append_pos = 0; buf->append_overflow = false; } if ((buf->append_pos + num_bytes) > buf->size) { buf->append_overflow = true; } const int start_pos = buf->append_pos; if (buf->slot.state == SG_RESOURCESTATE_VALID) { if (_sg_validate_append_buffer(buf, data, num_bytes)) { if (!buf->append_overflow && (num_bytes > 0)) { /* update and append on same buffer in same frame not allowed */ SOKOL_ASSERT(buf->update_frame_index != _sg.frame_index); _sg_append_buffer(buf, data, num_bytes, buf->append_frame_index != _sg.frame_index); buf->append_pos += num_bytes; buf->append_frame_index = _sg.frame_index; } } } result = start_pos; } else { /* FIXME: should we return -1 here? */ result = 0; } _SG_TRACE_ARGS(append_buffer, buf_id, data, num_bytes, result); return result; } SOKOL_API_IMPL bool sg_query_buffer_overflow(sg_buffer buf_id) { _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); bool result = buf ? buf->append_overflow : false; return result; } SOKOL_API_IMPL void sg_update_image(sg_image img_id, const sg_image_content* data) { _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); if (img && img->slot.state == SG_RESOURCESTATE_VALID) { if (_sg_validate_update_image(img, data)) { SOKOL_ASSERT(img->upd_frame_index != _sg.frame_index); _sg_update_image(img, data); img->upd_frame_index = _sg.frame_index; } } _SG_TRACE_ARGS(update_image, img_id, data); } SOKOL_API_IMPL void sg_push_debug_group(const char* name) { SOKOL_ASSERT(name); _SG_TRACE_ARGS(push_debug_group, name); } SOKOL_API_IMPL void sg_pop_debug_group(void) { _SG_TRACE_NOARGS(pop_debug_group); } SOKOL_API_IMPL sg_buffer_info sg_query_buffer_info(sg_buffer buf_id) { sg_buffer_info info; memset(&info, 0, sizeof(info)); const _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); if (buf) { info.slot.state = buf->slot.state; info.slot.res_id = buf->slot.id; info.slot.ctx_id = buf->slot.ctx_id; info.update_frame_index = buf->update_frame_index; info.append_frame_index = buf->append_frame_index; info.append_pos = buf->append_pos; info.append_overflow = buf->append_overflow; #if defined(SOKOL_D3D11) info.num_slots = 1; info.active_slot = 0; #else info.num_slots = buf->num_slots; info.active_slot = buf->active_slot; #endif } return info; } SOKOL_API_IMPL sg_image_info sg_query_image_info(sg_image img_id) { sg_image_info info; memset(&info, 0, sizeof(info)); const _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); if (img) { info.slot.state = img->slot.state; info.slot.res_id = img->slot.id; info.slot.ctx_id = img->slot.ctx_id; #if defined(SOKOL_D3D11) info.num_slots = 1; info.active_slot = 0; #else info.num_slots = img->num_slots; info.active_slot = img->active_slot; #endif } return info; } SOKOL_API_IMPL sg_shader_info sg_query_shader_info(sg_shader shd_id) { sg_shader_info info; memset(&info, 0, sizeof(info)); const _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); if (shd) { info.slot.state = shd->slot.state; info.slot.res_id = shd->slot.id; info.slot.ctx_id = shd->slot.ctx_id; } return info; } SOKOL_API_IMPL sg_pipeline_info sg_query_pipeline_info(sg_pipeline pip_id) { sg_pipeline_info info; memset(&info, 0, sizeof(info)); const _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); if (pip) { info.slot.state = pip->slot.state; info.slot.res_id = pip->slot.id; info.slot.ctx_id = pip->slot.ctx_id; } return info; } SOKOL_API_IMPL sg_pass_info sg_query_pass_info(sg_pass pass_id) { sg_pass_info info; memset(&info, 0, sizeof(info)); const _sg_pass_t* pass = _sg_lookup_pass(&_sg.pools, pass_id.id); if (pass) { info.slot.state = pass->slot.state; info.slot.res_id = pass->slot.id; info.slot.ctx_id = pass->slot.ctx_id; } return info; } /*--- DEPRECATED ---*/ #ifndef SOKOL_NO_DEPRECATED SOKOL_API_IMPL void sg_apply_draw_state(const sg_draw_state* ds) { SOKOL_ASSERT(ds); SOKOL_ASSERT((ds->_start_canary==0) && (ds->_end_canary==0)); sg_apply_pipeline(ds->pipeline); sg_bindings bindings; memset(&bindings, 0, sizeof(bindings)); for (int i = 0; i < SG_MAX_SHADERSTAGE_BUFFERS; i++) { bindings.vertex_buffers[i] = ds->vertex_buffers[i]; bindings.vertex_buffer_offsets[i] = ds->vertex_buffer_offsets[i]; } bindings.index_buffer = ds->index_buffer; bindings.index_buffer_offset = ds->index_buffer_offset; for (int i = 0; i < SG_MAX_SHADERSTAGE_IMAGES; i++) { bindings.vs_images[i] = ds->vs_images[i]; bindings.fs_images[i] = ds->fs_images[i]; } sg_apply_bindings(&bindings); } SOKOL_API_IMPL void sg_apply_uniform_block(sg_shader_stage stage, int ub_index, const void* data, int num_bytes) { sg_apply_uniforms(stage, ub_index, data, num_bytes); } #endif #ifdef _MSC_VER #pragma warning(pop) #endif #endif /* SOKOL_IMPL */ Chipmunk2D-Chipmunk-7.0.3/demo/sokol/sokol_time.h000066400000000000000000000207311347650476100216520ustar00rootroot00000000000000#pragma once /* sokol_time.h -- simple cross-platform time measurement Project URL: https://github.com/floooh/sokol Do this: #define SOKOL_IMPL before you include this file in *one* C or C++ file to create the implementation. Optionally provide the following defines with your own implementations: SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) SOKOL_API_DECL - public function declaration prefix (default: extern) SOKOL_API_IMPL - public function implementation prefix (default: -) If sokol_time.h is compiled as a DLL, define the following before including the declaration or implementation: SOKOL_DLL On Windows, SOKOL_DLL will define SOKOL_API_DECL as __declspec(dllexport) or __declspec(dllimport) as needed. void stm_setup(); Call once before any other functions to initialize sokol_time (this calls for instance QueryPerformanceFrequency on Windows) uint64_t stm_now(); Get current point in time in unspecified 'ticks'. The value that is returned has no relation to the 'wall-clock' time and is not in a specific time unit, it is only useful to compute time differences. uint64_t stm_diff(uint64_t new, uint64_t old); Computes the time difference between new and old. This will always return a positive, non-zero value. uint64_t stm_since(uint64_t start); Takes the current time, and returns the elapsed time since start (this is a shortcut for "stm_diff(stm_now(), start)") uint64_t stm_laptime(uint64_t* last_time); This is useful for measuring frame time and other recurring events. It takes the current time, returns the time difference to the value in last_time, and stores the current time in last_time for the next call. If the value in last_time is 0, the return value will be zero (this usually happens on the very first call). Use the following functions to convert a duration in ticks into useful time units: double stm_sec(uint64_t ticks); double stm_ms(uint64_t ticks); double stm_us(uint64_t ticks); double stm_ns(uint64_t ticks); Converts a tick value into seconds, milliseconds, microseconds or nanoseconds. Note that not all platforms will have nanosecond or even microsecond precision. Uses the following time measurement functions under the hood: Windows: QueryPerformanceFrequency() / QueryPerformanceCounter() MacOS/iOS: mach_absolute_time() emscripten: performance.now() Linux+others: clock_gettime(CLOCK_MONOTONIC) zlib/libpng license Copyright (c) 2018 Andre Weissflog This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #define SOKOL_TIME_INCLUDED (1) #include #ifndef SOKOL_API_DECL #if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_IMPL) #define SOKOL_API_DECL __declspec(dllexport) #elif defined(_WIN32) && defined(SOKOL_DLL) #define SOKOL_API_DECL __declspec(dllimport) #else #define SOKOL_API_DECL extern #endif #endif #ifdef __cplusplus extern "C" { #endif SOKOL_API_DECL void stm_setup(void); SOKOL_API_DECL uint64_t stm_now(void); SOKOL_API_DECL uint64_t stm_diff(uint64_t new_ticks, uint64_t old_ticks); SOKOL_API_DECL uint64_t stm_since(uint64_t start_ticks); SOKOL_API_DECL uint64_t stm_laptime(uint64_t* last_time); SOKOL_API_DECL double stm_sec(uint64_t ticks); SOKOL_API_DECL double stm_ms(uint64_t ticks); SOKOL_API_DECL double stm_us(uint64_t ticks); SOKOL_API_DECL double stm_ns(uint64_t ticks); #ifdef __cplusplus } /* extern "C" */ #endif /*-- IMPLEMENTATION ----------------------------------------------------------*/ #ifdef SOKOL_IMPL #define SOKOL_TIME_IMPL_INCLUDED (1) #include /* memset */ #ifndef SOKOL_API_IMPL #define SOKOL_API_IMPL #endif #ifndef SOKOL_ASSERT #include #define SOKOL_ASSERT(c) assert(c) #endif #ifndef _SOKOL_PRIVATE #if defined(__GNUC__) #define _SOKOL_PRIVATE __attribute__((unused)) static #else #define _SOKOL_PRIVATE static #endif #endif #if defined(_WIN32) #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include typedef struct { uint32_t initialized; LARGE_INTEGER freq; LARGE_INTEGER start; } _stm_state_t; #elif defined(__APPLE__) && defined(__MACH__) #include typedef struct { uint32_t initialized; mach_timebase_info_data_t timebase; uint64_t start; } _stm_state_t; #elif defined(__EMSCRIPTEN__) #include typedef struct { uint32_t initialized; double start; } _stm_state_t; #else /* anything else, this will need more care for non-Linux platforms */ #include typedef struct { uint32_t initialized; uint64_t start; } _stm_state_t; #endif static _stm_state_t _stm; /* prevent 64-bit overflow when computing relative timestamp see https://gist.github.com/jspohr/3dc4f00033d79ec5bdaf67bc46c813e3 */ #if defined(_WIN32) || (defined(__APPLE__) && defined(__MACH__)) _SOKOL_PRIVATE int64_t int64_muldiv(int64_t value, int64_t numer, int64_t denom) { int64_t q = value / denom; int64_t r = value % denom; return q * numer + r * numer / denom; } #endif #if defined(__EMSCRIPTEN__) EM_JS(double, stm_js_perfnow, (void), { return performance.now(); }); #endif SOKOL_API_IMPL void stm_setup(void) { memset(&_stm, 0, sizeof(_stm)); _stm.initialized = 0xABCDABCD; #if defined(_WIN32) QueryPerformanceFrequency(&_stm.freq); QueryPerformanceCounter(&_stm.start); #elif defined(__APPLE__) && defined(__MACH__) mach_timebase_info(&_stm.timebase); _stm.start = mach_absolute_time(); #elif defined(__EMSCRIPTEN__) _stm.start = stm_js_perfnow(); #else struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); _stm.start = (uint64_t)ts.tv_sec*1000000000 + (uint64_t)ts.tv_nsec; #endif } SOKOL_API_IMPL uint64_t stm_now(void) { SOKOL_ASSERT(_stm.initialized == 0xABCDABCD); uint64_t now; #if defined(_WIN32) LARGE_INTEGER qpc_t; QueryPerformanceCounter(&qpc_t); now = int64_muldiv(qpc_t.QuadPart - _stm.start.QuadPart, 1000000000, _stm.freq.QuadPart); #elif defined(__APPLE__) && defined(__MACH__) const uint64_t mach_now = mach_absolute_time() - _stm.start; now = int64_muldiv(mach_now, _stm.timebase.numer, _stm.timebase.denom); #elif defined(__EMSCRIPTEN__) double js_now = stm_js_perfnow() - _stm.start; SOKOL_ASSERT(js_now >= 0.0); now = (uint64_t) (js_now * 1000000.0); #else struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); now = ((uint64_t)ts.tv_sec*1000000000 + (uint64_t)ts.tv_nsec) - _stm.start; #endif return now; } SOKOL_API_IMPL uint64_t stm_diff(uint64_t new_ticks, uint64_t old_ticks) { if (new_ticks > old_ticks) { return new_ticks - old_ticks; } else { return 1; } } SOKOL_API_IMPL uint64_t stm_since(uint64_t start_ticks) { return stm_diff(stm_now(), start_ticks); } SOKOL_API_IMPL uint64_t stm_laptime(uint64_t* last_time) { SOKOL_ASSERT(last_time); uint64_t dt = 0; uint64_t now = stm_now(); if (0 != *last_time) { dt = stm_diff(now, *last_time); } *last_time = now; return dt; } SOKOL_API_IMPL double stm_sec(uint64_t ticks) { return (double)ticks / 1000000000.0; } SOKOL_API_IMPL double stm_ms(uint64_t ticks) { return (double)ticks / 1000000.0; } SOKOL_API_IMPL double stm_us(uint64_t ticks) { return (double)ticks / 1000.0; } SOKOL_API_IMPL double stm_ns(uint64_t ticks) { return (double)ticks; } #endif /* SOKOL_IMPL */ Chipmunk2D-Chipmunk-7.0.3/doc-src/000077500000000000000000000000001347650476100166105ustar00rootroot00000000000000Chipmunk2D-Chipmunk-7.0.3/doc-src/Doxyfile000066400000000000000000002070331347650476100203230ustar00rootroot00000000000000# Doxyfile 1.7.1 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project # # All text after a hash (#) is considered a comment and will be ignored # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" ") #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # http://www.gnu.org/software/libiconv for the list of possible encodings. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. PROJECT_NAME = "Chipmunk2D Pro API Reference" # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = 7.0.3 # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = ../doc/API-Reference # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, # Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English # messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, # Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, # Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = "The $name class" \ "The $name widget" \ "The $name file" \ is \ provides \ specifies \ contains \ represents \ a \ an \ the # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = NO # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful is your file systems # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = YES # If the QT_AUTOBRIEF tag is set to YES then Doxygen will # interpret the first line (until the first dot) of a Qt-style # comment as the brief description. If set to NO, the comments # will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 2 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for # Java. For instance, namespaces will be presented as packages, qualified # scopes will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources only. Doxygen will then generate output that is more tailored for # Fortran. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for # VHDL. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given extension. # Doxygen has a built-in mapping, but you can override or extend it using this # tag. The format is ext=language, where ext is a file extension, and language # is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, # C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make # doxygen treat .inc files as Fortran files (default is PHP), and .f files as C # (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions # you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. EXTENSION_MAPPING = # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. # Doxygen will parse them like normal C++ but will assume all classes use public # instead of private inheritance when no explicit protection keyword is present. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate getter # and setter methods for a property. Setting this option to YES (the default) # will make doxygen to replace the get and set methods by a property in the # documentation. This will only work if the methods are indeed getting or # setting a simple type. If this is not the case, or you want to show the # methods anyway, you should set this option to NO. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum # is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically # be useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. TYPEDEF_HIDES_STRUCT = NO # The SYMBOL_CACHE_SIZE determines the size of the internal cache use to # determine which symbols to keep in memory and which to flush to disk. # When the cache is full, less often used symbols will be written to disk. # For small to medium size projects (<1000 input files) the default value is # probably good enough. For larger projects a too small cache size can cause # doxygen to be busy swapping symbols to and from disk most of the time # causing a significant performance penality. # If the system has enough physical memory increasing the cache will improve the # performance by keeping more symbols in memory. Note that the value works on # a logarithmic scale so increasing the size by one will rougly double the # memory usage. The cache size is given by this formula: # 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols SYMBOL_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = NO # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = YES # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base # name of the file that contains the anonymous namespace. By default # anonymous namespace are hidden. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = YES # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = NO # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen # will list include files with double quotes in the documentation # rather than with sharp brackets. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen # will sort the (brief and detailed) documentation of class members so that # constructors and destructors are listed first. If set to NO (the default) # the constructors will appear in the respective orders defined by # SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. # This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO # and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the # hierarchy of group names into alphabetical order. If set to NO (the default) # the group names will appear in their defined order. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or define consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and defines in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # If the sources in your project are distributed over multiple directories # then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy # in the documentation. The default is NO. SHOW_DIRECTORIES = NO # Set the SHOW_FILES tag to NO to disable the generation of the Files page. # This will remove the Files entry from the Quick Index and from the # Folder Tree View (if specified). The default is YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the # Namespaces page. This will remove the Namespaces entry from the Quick Index # and from the Folder Tree View (if specified). The default is YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. The create the layout file # that represents doxygen's defaults, run doxygen with the -l option. # You can optionally specify a file name after the option, if omitted # DoxygenLayout.xml will be used as the name of the layout file. LAYOUT_FILE = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = YES # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be abled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = doxygen_main \ ../include/chipmunk \ ../objectivec/include/ObjectiveChipmunk # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # also the default input encoding. Doxygen uses libiconv (or the iconv built # into libc) for the transcoding. See http://www.gnu.org/software/libiconv for # the list of possible encodings. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx # *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 FILE_PATTERNS = *.h # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = NO # The EXCLUDE tag can be used to specify files and/or directories that should # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used select whether or not files or # directories that are symbolic links (a Unix filesystem feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = * # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the # filter if there is a match. The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER # is applied to all files. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = NO # If the REFERENCES_RELATION tag is set to YES # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # link to the source code. Otherwise they will link to the documentation. REFERENCES_LINK_SOURCE = NO # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = YES # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = . # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet. Note that doxygen will try to copy # the style sheet file to the HTML output directory, so don't put your own # stylesheet in the HTML output directory as well, or it will be erased! HTML_STYLESHEET = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. # Doxygen will adjust the colors in the stylesheet and background images # according to this color. Hue is specified as an angle on a colorwheel, # see http://en.wikipedia.org/wiki/Hue for more information. # For instance the value 0 represents red, 60 is yellow, 120 is green, # 180 is cyan, 240 is blue, 300 purple, and 360 is red again. # The allowed range is 0 to 359. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of # the colors in the HTML output. For a value of 0 the output will use # grayscales only. A value of 255 will produce the most vivid colors. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to # the luminance component of the colors in the HTML output. Values below # 100 gradually make the output lighter, whereas values above 100 make # the output darker. The value divided by 100 is the actual gamma applied, # so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, # and 100 does not change the gamma. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting # this to NO can help when comparing the output of multiple runs. HTML_TIMESTAMP = YES # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, # files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. HTML_ALIGN_MEMBERS = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. For this to work a browser that supports # JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox # Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). HTML_DYNAMIC_SECTIONS = NO # If the GENERATE_DOCSET tag is set to YES, additional index files # will be generated that can be used as input for Apple's Xcode 3 # integrated development environment, introduced with OSX 10.5 (Leopard). # To create a documentation set, doxygen will generate a Makefile in the # HTML output directory. Running make will produce the docset in that # directory and running "make install" will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find # it at startup. # See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html # for more information. GENERATE_DOCSET = NO # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the # feed. A documentation feed provides an umbrella under which multiple # documentation sets from a single provider (such as a company or product suite) # can be grouped. DOCSET_FEEDNAME = "Doxygen generated docs" # When GENERATE_DOCSET tag is set to YES, this tag specifies a string that # should uniquely identify the documentation set bundle. This should be a # reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen # will append .docset to the name. DOCSET_BUNDLE_ID = org.doxygen.Project # When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify # the documentation publisher. This should be a reverse domain-name style # string, e.g. com.mycompany.MyDocSet.documentation. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compiled HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING # is used to encode HtmlHelp index (hhk), content (hhc) and project file # content. CHM_INDEX_ENCODING = # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated # that can be used as input for Qt's qhelpgenerator to generate a # Qt Compressed Help (.qch) of the generated HTML documentation. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can # be used to specify the file name of the resulting .qch file. # The path specified is relative to the HTML output folder. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#namespace QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#virtual-folders QHP_VIRTUAL_FOLDER = doc # If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to # add. For more information please see # http://doc.trolltech.com/qthelpproject.html#custom-filters QHP_CUST_FILTER_NAME = # The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see # # Qt Help Project / Custom Filters. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's # filter section matches. # # Qt Help Project / Filter Attributes. QHP_SECT_FILTER_ATTRS = # If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can # be used to specify the location of Qt's qhelpgenerator. # If non-empty doxygen will try to run qhelpgenerator on the generated # .qhp file. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files # will be generated, which together with the HTML files, form an Eclipse help # plugin. To install this plugin and make it available under the help contents # menu in Eclipse, the contents of the directory containing the HTML and XML # files needs to be copied into the plugins directory of eclipse. The name of # the directory within the plugins directory should be the same as # the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before # the help appears. GENERATE_ECLIPSEHELP = NO # A unique identifier for the eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have # this name. ECLIPSE_DOC_ID = org.doxygen.Project # The DISABLE_INDEX tag can be used to turn on/off the condensed index at # top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. DISABLE_INDEX = NO # This tag can be used to set the number of enum values (range [1..20]) # that doxygen will group on one line in the generated HTML documentation. ENUM_VALUES_PER_LINE = 4 # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. # If the tag value is set to YES, a side panel will be generated # containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). # Windows users are probably better off using the HTML help feature. GENERATE_TREEVIEW = NO # By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, # and Class Hierarchy pages using a tree view instead of an ordered list. USE_INLINE_TREES = NO # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 # When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open # links to external symbols imported via tag files in a separate window. EXT_LINKS_IN_WINDOW = NO # Use this tag to change the font size of Latex formulas included # as images in the HTML documentation. The default is 10. Note that # when you change the font size after a successful doxygen run you need # to manually remove any form_*.png images from the HTML output directory # to force them to be regenerated. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are # not supported properly for IE 6.0, but are supported on all modern browsers. # Note that when changing this option you need to delete any form_*.png files # in the HTML output before the changes have effect. FORMULA_TRANSPARENT = YES # When the SEARCHENGINE tag is enabled doxygen will generate a search box # for the HTML output. The underlying search engine uses javascript # and DHTML and should work on any modern browser. Note that when using # HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets # (GENERATE_DOCSET) there is already a search function so this one should # typically be disabled. For large projects the javascript based search engine # can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. SEARCHENGINE = YES # When the SERVER_BASED_SEARCH tag is enabled the search engine will be # implemented using a PHP enabled web server instead of at the web client # using Javascript. Doxygen will generate the search PHP script and index # file to put on the web server. The advantage of the server # based approach is that it scales better to large projects and allows # full text search. The disadvances is that it is more difficult to setup # and does not have live searching capabilities. SERVER_BASED_SEARCH = NO #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = NO # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. # Note that when enabling USE_PDFLATEX this option is only used for # generating bitmaps for formulas in the HTML output, but not in the # Makefile that is written to the output directory. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, a4wide, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4wide # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = YES # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = YES # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO # If LATEX_SOURCE_CODE is set to YES then doxygen will include # source code with syntax highlighting in the LaTeX output. # Note that which sources are shown also depends on other settings # such as SOURCE_BROWSER. LATEX_SOURCE_CODE = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load stylesheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. This is useful # if you want to understand what is going on. On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = YES # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # in the INCLUDE_PATH (see below) will be search if a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all function-like macros that are alone # on a line, have an all uppercase name, and do not end with a semicolon. Such # function macros are typically used for boiler-plate code, and will confuse # the parser if not removed. SKIP_FUNCTION_MACROS = NO #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. # Optionally an initial location of the external documentation # can be added for each tagfile. The format of a tag file without # this location is as follows: # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths or # URLs. If a location is present for each tag, the installdox tool # does not have to be run to correct the links. # Note that each tag file must have a unique name # (where the name does NOT include the path) # If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option is superseded by the HAVE_DOT option below. This is only a # fallback. It is recommended to install and use dot, since it yields more # powerful graphs. CLASS_DIAGRAMS = NO # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the # documentation. The MSCGEN_PATH tag allows you to specify the directory where # the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = NO # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is # allowed to run in parallel. When set to 0 (the default) doxygen will # base this on the number of processors available in the system. You can set it # explicitly to a value larger than 0 to get control over the balance # between CPU load and processing speed. DOT_NUM_THREADS = 0 # By default doxygen will write a font called FreeSans.ttf to the output # directory and reference it in all dot files that doxygen generates. This # font does not include all possible unicode characters however, so when you need # these (or just want a differently looking font) you can specify the font name # using DOT_FONTNAME. You need need to make sure dot is able to find the font, # which can be done by putting it in a standard location or by setting the # DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory # containing the font. DOT_FONTNAME = FreeSans.ttf # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. # The default size is 10pt. DOT_FONTSIZE = 10 # By default doxygen will tell dot to use the output directory to look for the # FreeSans.ttf font (which doxygen will put there itself). If you specify a # different font using DOT_FONTNAME you can set the path where dot # can find it using this tag. DOT_FONTPATH = # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # the CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH and HAVE_DOT options are set to YES then # doxygen will generate a call dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable call graphs # for selected functions only using the \callgraph command. CALL_GRAPH = NO # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then # doxygen will generate a caller dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable caller # graphs for selected functions only using the \callergraph command. CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are png, jpg, or gif # If left blank png will be used. DOT_IMAGE_FORMAT = png # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the # number of direct children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, because dot on Windows does not # seem to support this out of the box. Warning: Depending on the platform used, # enabling this option may lead to badly anti-aliased labels on the edges of # a graph (i.e. they become hard to read). DOT_TRANSPARENT = NO # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES Chipmunk2D-Chipmunk-7.0.3/doc-src/MakeDocs.rb000066400000000000000000000056451347650476100206350ustar00rootroot00000000000000require 'rubygems' require 'redcloth' require 'erb' require 'uri/common' class Node attr_reader :anchor attr_reader :children def initialize(title, anchor, parent) @title = title @anchor = (parent.anchor ? "#{parent.anchor}-#{anchor}" : anchor) @children = [] parent.children << self end def outline(level) children = "" if level == 1 children = "
    #{@children.map{|child| child.outline(level + 1)}.join}
" end ["
  • #{@title}#{children}
  • "] end Root = Struct.new(:anchor, :children).new(nil, []) def Root.outline "
      #{self.children.map{|child| child.outline(1)}.join}
    " end end def pop_open_div(name) return %{
    Hide/Show #{name}
    <%= h 1, "Chipmunk2D 7.0.3", "Intro" %> Chipmunk2D is a 2D rigid body physics library distributed under the MIT license. It is blazingly fast, portable, numerically stable, and easy to use. For this reason it has been used in hundreds of games across just about every system you can name. This includes top quality titles such as Night Sky for the Wii and many #1 sellers on the iPhone App Store! I've put thousands of hours of work over many years to make Chipmunk2D what it is today. If you find Chipmunk2D has saved you a lot of time, please consider "donating":https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=6666552. You'll make an indie game developer very happy! First of all, I would like to give a Erin Catto a big thank you, as Chipmunk2D's impulse solver was directly inspired by his example code way back in 2006. (Now a full fledged physics engine all its own: "Box2D.org":http://www.box2d.org/). His contact persistence idea allows for stable stacks of objects with very few iterations of the solver. My previous solver produced mushy piles of objects or required a large amount of CPU to operate stably. <%= h 2, "Why a C Library?", "WhyC" %> A lot of people ask me why I wrote Chipmunk2D in C instead of _pick your favorite language here_. I tend to get really excited about different programming languages. Depending on the month, take your pick of Scheme, OCaml, Ruby, Objective-C, ooc, Lua, Io... the list goes on. The one common factor between most any language is that they are usually dead simple to make bindings to C code. I also wanted Chipmunk2D to be fast, portable, easy to optimize and easy to debug. Writing Chipmunk2D in C made it simpler to achieve all of those goals. That said, I've never developed a whole game in C and I probably never will. There are much more interesting and fun languages than C with all sorts of nice features like garbage collection, closures and all sorts of unique object oriented runtimes. Check out the "Bindings and Ports":http://chipmunk2d.net/bindingsAndPorts.php page to see if you can use Chipmunk2D from your language of choice. Because Chipmunk2D is written in a subset of C99 it compiles cleanly as C, C++, Objective-C and Objective-C++ code, making it easy to integrate into projects in those languages. <%= h 2, "Limitations of a C API:", "Limitations" %> Chipmunk does provide overloaded operators for @*@, @+@, and @-@ (unary and binary) if you are using C++, but falls back to using functions such as @cpvadd()@ and @cpvsub()@ for C code. This is a little harder to read, but works OK once you get used to it. Most of the interesting vector operations that are possible don't have a symbol for them anyway (at least not on a keyboard). Another problem for a C API is access restriction. There are many structs, fields and functions in Chipmunk that are only meant to be used internally. To work around this, I have a separate header full of Chipmunk's private API, @chipmunk_private.h@. I also use a macro, @CP_PRIVATE()@ to mangle names in public structures. While you can feel free to include this header or use the macro in your own code to access the private API, be aware that the fields and functions may be renamed or disappear without warning and I have no plans to document or support usage of the private API. <%= h 2, "Get it, Compile it:", "Compiling" %> If you haven't downloaded it yet, you can always download the newest version of Chipmunk2D "here":http://chipmunk-physics.net/release/ChipmunkLatest.tgz. Inside you'll find a command line build script that works with "CMake":http://www.cmake.org/, a XCode project and project files for Visual Studio '09 and '10. <%= h 3, "Debug or Release?", "DebugRelease" %> Debug mode might be slightly slower, but will include a lot of error checking assertions that can help you find bugs quicker such as removing objects twice or doing things that might cause unsolvable collisions. I highly recommend you use a debug build until you are ready to ship the game and only then switch to a release build. <%= h 3, "XCode (Mac/iPhone)", "XCode" %> The included XCode project has targets for building a static library for the Mac or iOS. Additionally, you might want to just run the @xcode/iphonestatic.command@ or @xcode/macstatic.command@ to build you a directory with the headers and debug/release static libraries that you can just drop right into your projects. Including Chipmunk in your project with all the correct compiler flags applied couldn't be simpler. The iPhone script generates a "fat" library that can be used with both the iOS simulator and devices. The device version is compiled as release, and the simulator version is compiled as debug. <%= h 3, "MSVC", "MSVC" %> I rarely use MSVC, but others have chipped in to help me maintain Visual Studio project files. The MSVC 10 project should work, as I usually remember to test that before making a stable release. The MSVC 9 project may not as I don't have access to that version. Let me know if there are any issues. <%= h 3, "Command Line", "CommandLine" %> The CMake build script should work on any system (Unix/Win/Mac) as long as you have CMake installed. It can even generate XCode or MSVC projects if you want (see CMake's documentation for more information). To compile a Chipmunk debug build on the command line, all you need to do is run:
    cmake -D CMAKE_BUILD_TYPE=Debug .
    make
    If the @-D CMAKE_BUILD_TYPE=Debug@ option is left out, it will make a release build instead. Why CMake? Somebody was kind enough to create the build scripts for me originally, and it seems to handle a lot of the cross-platform issues nicely. I know some people really hate having to install some random non-make build system in order to compile things, but it has saved me a lot of time and effort. <%= h 2, "Hello Chipmunk (World)", "HelloChipmunk" %> Hello world Chipmunk style. Create a simple simulation where a ball falls onto a static line segment, then rolls off. Print out the coordinates of the ball. <%= pop_open_example "Hello Chipmunk" %> <%= h 2, "Support:", "Support" %> The best way to get support is to visit the "Chipmunk Forums":http://www.slembcke.net/forums/viewforum.php?f=1. There are plenty of people around using Chipmunk on the just about every platform I've ever heard of. If you are working on a commercial project, Howling Moon Software (my company) is "available for contracting":http://howlingmoonsoftware.com/contracting.php. We can help with implementing custom Chipmunk behaviors, as well as priority bug fixes and performance tuning. <%= h 2, "Contact:", "ContactUS" %> If you find any bugs in Chipmunk, errors or broken links in this document, or have a question or comment about Chipmunk you can contact me at slembcke(at)gmail(dot)com. (email or GTalk) <%= h 2, "License:", "License" %> Chipmunk is licensed under the MIT license.
    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.
    
    This means that you do not have to buy a license or pay to use Chipmunk in commercial projects. (Though we really appreciate donations) <%= h 2, "Links:", "Links" %> * "Chipmunk Forums":http://chipmunk2d.net/forum - The official forum Chipmunk2D forum. * "Howling Moon Software":http://howlingmoonsoftware.com/ - The software company I co-founded. (We are available for contract work!) * "Games":http://chipmunk2d.net/games.php - A small list of games we know that use Chipmunk. <%= h 1, "Chipmunk2D Basics:", "Basics" %> <%= h 2, "Overview:", "Overview" %> There are 4 basic object types you will use in Chipmunk. * *Rigid Bodies:* A rigid body holds the physical properties of an object. (mass, position, rotation, velocity, etc.) It does not have a shape until you attach one or more collision shapes to it. If you’ve done physics with particles before, rigid bodies differ in that they are able to rotate. Rigid bodies generally tend to have a 1:1 correlation to sprites in a game. You should structure your game so that you use the position and rotation of the rigid body for drawing your sprite. * *Collision Shapes:* By attaching shapes to bodies, you can define the a body’s shape. You can attach as many shapes to a single body as you need to in order to define a complex shape. Shapes contain the surface properties of an object such as how much friction or elasticity it has. * *Constraints/Joints:* Constraints and joints describe how bodies are attached to each other. * *Spaces:* Spaces are containers for simulating objects in Chipmunk. You add bodies, shapes and joints to a space and then update the space as a whole. They control how all the rigid bodies, shapes, and constraints interact together. There is often confusion between rigid bodies and their collision shapes in Chipmunk and how they relate to sprites. A sprite would be a visual representation of an object, while a collision shape is an invisible property that defines how objects should collide. Both the sprite's and the collision shape's position and rotation are controlled by the motion of a rigid body. Generally you want to create a game object type that ties these things all together. <%= h 2, "Memory Management the Chipmunk way:", "Memory" %> For most of the structures you will use, Chipmunk uses a more or less standard and straightforward set of memory management functions. Take the "cpSpace":#cpSpace struct for example: * @cpSpaceNew()@ - Allocates and initializes a @cpSpace@ struct. It calls @cpSpaceAlloc()@ then @cpSpaceInit()@. * @cpSpaceFree(cpSpace *space)@ - Destroys and frees the @cpSpace@ struct. You are responsible for freeing any structs that you allocate. Chipmunk does not do reference counting or garbage collection. If you call a @new@ function, you must call the matching @free@ function or you will leak memory. Additionally if you need more control over allocation and initialization because you are allocating temporary structs on the stack, writting a language binding, or working in a low memory environment you can also use the following functions. _Most people will never have any need to use these functions._ * @cpSpaceAlloc()@ - Allocates but does not initialize a @cpSpace@ struct. All allocation functions look more or less like this: @return (cpSpace *)cpcalloc(1, sizeof(cpSpace));@ You can write your own allocation functions if you want. It is not a requirement that the memory be zeroed. * @cpSpaceInit(cpSpace *space)@ - Initializes a @cpSpace@ struct. * @cpSpaceDestroy(cpSpace *space)@ - Frees all memory allocated by @cpSpaceInit()@, but does not free the @cpSpace@ struct itself. Like calls to the @new@ and @free@ functions. Any memory allocated by an @alloc@ function must be freed by @cpfree()@ or similar. Any call to an @init@ function must be matched with its @destroy@ function. To further ease integration with garbage collectors or other memory management constraints, Chipmunk has a number of compile time defines (@cpcalloc()@, @cprealloc()@, and @cpfree()@) that can be overriden. If you aren't using Chipmunk from a garbage collected language, I'd highly recommend using libGC. It provides nearly transparent garbage collection for C based languages. <%= h 2, "Basic Types:", "Types" %> @chipmunk_types.h@ defines a number of basic types that Chipmunk uses. These can be changed at compile time to better suit your needs: * @cpFloat@: Floating point type. Defaults to @double@. * @cpVect@: 2D vector type. "cpVect documentation":#cpVect * @cpBool@: Like every good C library that wants good cross language compatibility, you end up defining your own boolean type. :-\ Defaults to @int@. * @cpDataPointer@: Pointer type defined for callbacks and the user definable data pointer on most Chipmunk structs. Defaults to @void*@. * @cpCollisionType@: Unique identifier for collision shape types. Defaults to @unsigned int@. Defined type must support the @==@ operator. * @cpGroup@: Unique identifier for collision groups. Defaults to @unsigned int@. A @CP_NO_GROUP@ value is defined that can be used when you don't want to specify a group. Defined type must support the equality @==@ operator. * @cpBitmask@: Type used for collision filter categories and masks. Defaults to @unsigned int@. Defined type must support the bitwise AND @&@ operator. * @cpTransform@: Type used for 2x3 affine transforms in Chipmunk. If you are writting a game engine or language binding on top of Chipmunk, you might want to choose to use object references instead of integers for collision type and group. I often use class pointers for collision types and game object pointers for groups. It's much simpler than keeping a table of enumerations around. *Note:* On the iPhone, @cpFloat@ is defined as @float@ and @cpVect@ is an alias for @CGPoint@ for performance and compatibility reasons. <%= h 2, "Math the Chipmunk way:", "Math" %> First of all, Chipmunk uses double precision floating point numbers throughout its calculations by default. This is likely to be faster on most modern desktop processors, and means you don't have to worry as much about floating point accuracy. You can change the floating point type used by Chipmunk when compiling the library. Look in @chipmunk_types.h@. Chipmunk defines a number of aliases for common math functions so that you can choose to use floats or doubles for Chipmunk's floating point type. In your own code, there probably isn't a strong reason to use these aliases unless you expect you might want to change Chipmunk's floating point type later and a 2% performance hit from using the wrong float/double version of math functions will matter. However, there are a few unique functions you will probably find very useful: * @cpFloat cpfclamp(cpFloat f, cpFloat min, cpFloat max)@ - Clamp @f@ to be between @min@ and @max@. * @cpFloat cpflerp(cpFloat f1, cpFloat f2, cpFloat t)@ - Linearly interpolate between @f1@ and @f2@. * @cpFloat cpflerpconst(cpFloat f1, cpFloat f2, cpFloat d)@ - Linearly interpolate from @f1@ towards @f2@ by no more than @d@. Floating point infinity is defined as @INFINITY@. This is defined by many math libraries, but is not actually part of the C standard library. <%= h 1, "Chipmunk Vectors: @cpVect@", "cpVect" %> <%= h 2, "Struct Definition, Constants and Constructors:", "Basics" %>
    typedef struct cpVect{
    	cpFloat x, y;
    } cpVect
    p(expl). 2D vector packed into a struct. No surprises here.
    static const cpVect cpvzero = {0.0f,0.0f};
    p(expl). Constant for the zero vector.
    cpVect cpv(const cpFloat x, const cpFloat y)
    p(expl). Convenience constructor for creating new @cpVect@ structs. <%= h 2, "Operations:", "Operations" %> * @cpBool cpveql(const cpVect v1, const cpVect v2)@ - Check if two vectors are equal. Chipmunk provides an overloaded @==@ operator when used in C++ programs. _(Be careful when comparing floating point numbers!)_ * @cpVect cpvadd(const cpVect v1, const cpVect v2)@ - Add two vectors. Chipmunk provides an overloaded @+@ operator when used in C++ programs. * @cpVect cpvsub(const cpVect v1, const cpVect v2)@ - Subtract two vectors. Chipmunk provides an overloaded @-@ operator when used in C++ programs. * @cpVect cpvneg(const cpVect v)@ - Negate a vector. Chipmunk provides an overloaded unary negation operator @-@ when used in C++ programs. * @cpVect cpvmult(const cpVect v, const cpFloat s)@ - Scalar multiplication. Chipmunk provides an overloaded @*@ operator when used in C++ programs. * @cpFloat cpvdot(const cpVect v1, const cpVect v2)@ - Vector dot product. * @cpFloat cpvcross(const cpVect v1, const cpVect v2)@ - 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 value along the z-axis. * @cpVect cpvperp(const cpVect v)@ - Returns a perpendicular vector. (90 degree rotation) * @cpVect cpvrperp(const cpVect v)@ - Returns a perpendicular vector. (-90 degree rotation) * @cpVect cpvproject(const cpVect v1, const cpVect v2)@ - Returns the vector projection of @v1@ onto @v2@. * @cpVect cpvrotate(const cpVect v1, const cpVect v2)@ - Uses complex multiplication to rotate @v1@ by @v2@. Scaling will occur if @v1@ is not a unit vector. * @cpVect cpvunrotate(const cpVect v1, const cpVect v2)@ - Inverse of @cpvrotate()@. * @cpFloat cpvlength(const cpVect v)@ - Returns the length of @v@. * @cpFloat cpvlengthsq(const cpVect v)@ - Returns the squared length of @v@. Faster than @cpvlength()@ when you only need to compare lengths. * @cpVect cpvlerp(const cpVect v1, const cpVect v2, const cpFloat t)@ - Linearly interpolate between @v1@ and @v2@. * @cpVect cpvlerpconst(cpVect v1, cpVect v2, cpFloat d)@ - Linearly interpolate between @v1@ towards @v2@ by distance @d@. * @cpVect cpvslerp(const cpVect v1, const cpVect v2, const cpFloat t)@ - Spherical linearly interpolate between @v1@ and @v2@. * @cpVect cpvslerpconst(const cpVect v1, const cpVect v2, const cpFloat a)@ - Spherical linearly interpolate between @v1@ towards @v2@ by no more than angle @a@ in radians. * @cpVect cpvnormalize(const cpVect v)@ - Returns a normalized copy of @v@. As a special case, it returns @cpvzero@ when called on @cpvzero@. * @cpVect cpvclamp(const cpVect v, const cpFloat len)@ - Clamp @v@ to length @len@. * @cpFloat cpvdist(const cpVect v1, const cpVect v2)@ - Returns the distance between @v1@ and @v2@. * @cpFloat cpvdistsq(const cpVect v1, const cpVect v2)@ - Returns the squared distance between @v1@ and @v2@. Faster than @cpvdist()@ when you only need to compare distances. * @cpBool cpvnear(const cpVect v1, const cpVect v2, const cpFloat dist)@ - Returns true if the distance between @v1@ and @v2@ is less than @dist@. * @cpVect cpvforangle(const cpFloat a)@ - Returns the unit length vector for the given angle (in radians). * @cpFloat cpvtoangle(const cpVect v)@ - Returns the angular direction @v@ is pointing in (in radians). <%= h 1, "Chipmunk Axis Aligned Bounding Boxes: @cpBB@", "cpBB" %> <%= h 2, "Struct Definition and Constructors:", "Basics" %>
    typedef struct cpBB{
    	cpFloat l, b, r ,t;
    } cpBB
    p(expl). Simple bounding box struct. Stored as left, bottom, right, top values.
    cpBB cpBBNew(const cpFloat l, const cpFloat b, const cpFloat r, const cpFloat t)
    p(expl). Convenience constructor for @cpBB@ structs. Like @cpv()@ this function returns a copy and not a malloced pointer.
    cpBB cpBBNewForExtents(const cpVect c, const cpFloat hw, const cpFloat hh)
    p(expl). Convenience constructor for making a @cpBB@ fitting with a center point and half width and height.
    cpBB cpBBNewForCircle(const cpVect p, const cpFloat r)
    p(expl). Convenience constructor for making a @cpBB@ fitting a circle at position @p@ with radius @r@. <%= h 2, "Operations:", "Operations" %> * @cpBool cpBBIntersects(const cpBB a, const cpBB b)@ - Returns true if the bounding boxes intersect. * @cpBool cpBBContainsBB(const cpBB bb, const cpBB other)@ - Returns true if @bb@ completely contains @other@. * @cpBool cpBBContainsVect(const cpBB bb, const cpVect v)@ - Returns true if @bb@ contains @v@. * @cpBB cpBBMerge(const cpBB a, const cpBB b)@ - Return the minimal bounding box that contains both @a@ and @b@. * @cpBB cpBBExpand(const cpBB bb, const cpVect v)@ - Return the minimal bounding box that contains both @bb@ and @v@. * @cpVect cpBBCenter(const cpBB bb)@ - Return the center of @bb@. * @cpFloat cpBBArea(cpBB bb)@ - Return the area of @bb@. * @cpFloat cpBBMergedArea(cpBB a, cpBB b)@ - Merges @a@ and @b@ then returns the area of the merged bounding box. * @cpFloat cpBBSegmentQuery(cpBB bb, cpVect a, cpVect b)@ - Returns the fraction along the segment query the cpBB is hit. Returns INFINITY if it doesn't hit. * @cpBool cpBBIntersectsSegment(cpBB bb, cpVect a, cpVect b)@ - Returns true if the segment defined by endpoints @a@ and @b@ intersect @bb@. * @cpVect cpBBClampVect(const cpBB bb, const cpVect v)@ - Returns a copy of @v@ clamped to the bounding box. * @cpVect cpBBWrapVect(const cpBB bb, const cpVect v)@ - Returns a copy of @v@ wrapped to the bounding box. <%= h 1, "Chipmunk Rigid Bodies: @cpBody@", "cpBody" %> <%= h 2, "Dynamic, Kinematic, and Static Bodies:", "BodyTypes" %> Chipmunk supports three different types of bodies with unique behavioral and performance characteristics. Dynamic bodies are the default body type. They react to collisions, are affected by forces and gravity, and have a finite amount of mass. These are the type of bodies that you want the physics engine to simulate for you. Dynamic bodies interact with all types of bodies and can generate collision callbacks. Kinematic bodies are bodies that are controlled from your code instead of inside the physics engine. They arent affected by gravity and they have an infinite amount of mass so they don't react to collisions or forces with other bodies. Kinematic bodies are controlled by setting their velocity, which will cause them to move. Good examples of kinematic bodies might include things like moving platforms. Objects that are touching or jointed to a kinematic body are never allowed to fall asleep. Static bodies are bodies that never (or rarely) move. Using static bodies for things like terrain offers a big performance boost over other body types- because Chipmunk doesn't need to check for collisions between static objects and it never needs to update their collision information. Additionally, because static bodies don't move, Chipmunk knows it's safe to let objects that are touching or jointed to them fall asleep. Generally all of your level geometry will be attached to a static body except for things like moving platforms or doors. Every space provide a built-in static body for your convenience. Static bodies can be moved, but there is a performance penalty as the collision information is recalculated. There is no penalty for having multiple static bodies, and it can be useful for simplifying your code by allowing different parts of your static geometry to be initialized or moved separately. <%= h 2, "Movement, Teleportation, and Velocity:", "Movement" %> A graphics engine only needs to know the position of an object for each frame that its drawn. For a physics engine, this isn't enough information to calculate a collision response. When you set the position of a body, you are effectively asking it to teleport itself. This means that it will instantly move to its new position instead of moving through space and time like a normal object. If you teleport an object so that it overlaps another one, the best the physics engine can do is to attempt to push the objects apart again since there is no information about their movement. This generally results in very mushy looking collisions. So instead of setting the position of an object, it's better to set its velocity and allow the physics engine to update the position. That way it can resolve any resulting colisions natuarally since it knows how the objects were moving. This is why kinematic bodies work the way they do. You set the velocity, and the physics updates their position so the two are never out of sync. For dynamic bodies, setting the velocity explicitly every frame can cause problems. For example, a problem occurs when a light dynamic body (like a person) is pressed against a heavy dynamic body (like a car), and you set velocity of the small object so that it's pushing it into the big body. To the physics engine, the change in velocity is the same as applying a large impulse (a very short, very large force). Even if the velocity is low, the large force can allow the small body to push the big body, even when it normally wouldn't be able to. For example, a person walking into a car can overpower the car's friction and cause it to creep along the ground slowly. Additionally, when you set the velocity of an object that is already in contact, it can cause the two objects to overlap by a small amount. The easiest way to avoid both of these problems is to make smaller changes to the body's velocity, accelerating it over a fraction of a second instead of a single frame. An even better solution, which is covered more thoroughly later, is to use constraints to move the object. <%= h 2, "Memory Management Functions:", "Memory" %>
    cpBody *cpBodyAlloc(void)
    cpBody *cpBodyInit(cpBody *body, cpFloat m, cpFloat i)
    cpBody *cpBodyNew(cpFloat m, cpFloat i)
    
    cpBody *cpBodyInitKinematic(cpBody *body)
    cpBody *cpBodyNewKinematic()
    
    cpBody *cpBodyInitStatic(cpBody *body)
    cpBody *cpBodyNewStatic()
    
    void cpBodyDestroy(cpBody *body)
    void cpBodyFree(cpBody *body)
    p(expl). Standard set of Chipmunk memory management functions. @m@ and @i@ are the mass and moment of inertia for the body. Guessing the mass for a body is usually fine, but guessing a moment of inertia can lead to a very poor simulation so it's recommended to use Chipmunk's moment calculations to estimate the moment for you. Be careful not to free a body before any shapes or constraints attached to it have been removed from a space. <%= h 2, "Creating Dynamic Bodies:", "DynamicBodies" %> There are two ways to set up a dynamic body. The easiest option is to create a body with a mass and moment of 0, and set the mass or density of each collision shape added to the body. Chipmunk will automatically calculate the mass, moment of inertia, and center of gravity for you. This is probably preferred in most cases. TODO example The other option is to set the mass of the body when it's created, and leave the mass of the shapes added to it as 0.0. This approach is more flexible, but is not as easy to use. *Don't* set the mass of both the body and the shapes. If you do so, it will recalculate and overwite your custom mass value when the shapes are added to the body. TODO example <%= h 2, "Properties:", "Properties" %> Chipmunk provides getter/setter functions for a number of properties on rigid bodies. Setting most properties automatically wakes the rigid bodies up if they were sleeping. You can also set the fields directly on the cpBody struct if you wish. They are documented in the headers.
    cpBodyType cpBodyGetType(const cpBody *body)
    void cpBodySetType(cpBody *body, cpBodyType)
    p(expl). Set the type of a body (dynamic, kinematic, static). See the section on "BodyTypes":#BodyTypes for more information. When changing an body to a dynamic body, the mass and moment of inertia are recalculated from the shapes added to the body. Custom calculated moments of inertia are not preseved when changing types. This function cannot be called directly in a collision callback. See "Post-Step Callbacks":#PostStep for more information.
    cpSpace cpBodyGetSpace(const cpBody *body)
    p(expl). The @cpSpace@ this body is currently added to, or @NULL@ if it is not currently added to a space.
    cpFloat cpBodyGetMass(const cpBody *body)
    void cpBodySetMass(cpBody *body, cpFloat m)
    p(expl). Mass of the body.
    cpFloat cpBodyGetMoment(const cpBody *body)
    void cpBodySetMoment(cpBody *body, cpFloat i)
    p(expl). Moment of inertia (MoI or sometimes just moment) of the body. The moment is like the rotational mass of a body. See below for function to help calculate the moment.
    cpVect cpBodyGetPosition(const cpBody *body)
    void cpBodySetPosition(cpBody *body, cpVect pos)
    p(expl). Position of the body. When changing the position you may also want to call @cpSpaceReindexShapesForBody()@ to update the collision detection information for the attached shapes if plan to make any queries against the space.
    cpVect cpBodyGetCenterOfGravity(const cpBody *body)
    void cpBodySetCenterOfGravity(cpBody *body, cpVect cog)
    p(expl). Location of the center of gravity in body local coordinates. The default value is @(0, 0)@, meaning the center of gravity is the same as the position of the body.
    cpVect cpBodyGetVelocity(const cpBody *body)
    void cpBodySetVelocity(cpBody *body, const cpVect value)
    p(expl). Linear velocity of the center of gravity of the body.
    cpVect cpBodyGetForce(const cpBody *body)
    void cpBodySetForce(cpBody *body, const cpVect value)
    p(expl). Force applied to the center of gravity of the body. This value is reset for every time step.
    cpFloat cpBodyGetAngle(const cpBody *body)
    void cpBodySetAngle(cpBody *body, cpFloat a)
    p(expl). Rotation of the body in radians. When changing the rotation you may also want to call @cpSpaceReindexShapesForBody()@ to update the collision detection information for the attached shapes if plan to make any queries against the space. A body rotates around its center of gravity, not its position.
    cpFloat cpBodyGetAngularVelocity(const cpBody *body)
    void cpBodySetAngularVelocity(cpBody *body, const cpFloat value)
    p(expl). The angular velocity of the body in radians per second.
    cpFloat cpBodyGetTorque(const cpBody *body)
    void cpBodySetTorque(cpBody *body, const cpFloat value)
    p(expl). The torque applied to the body. This value is reset for every time step.
    cpVect cpBodyGetRotation(const cpBody *body)
    p(expl). The rotation vector for the body. Can be used with @cpvrotate()@ or @cpvunrotate()@ to perform fast rotations.
    cpSpace* cpBodyGetSpace(const cpBody *body)
    p(expl). Get the @cpSpace@ that @body@ has been added to.
    cpDataPointer cpBodyGetUserData(const cpBody *body)
    void cpBodySetUserData(cpBody *body, const cpDataPointer value)
    p(expl). User data pointer. Use this pointer to get a reference to the game object that owns this body from callbacks. <%= h 2, "Moment of Inertia and Area Helper Functions:", "Helpers" %> Use the following functions to approximate the moment of inertia for your body, adding the results together if you want to use more than one. * @cpFloat cpMomentForCircle(cpFloat m, cpFloat r1, cpFloat r2, cpVect offset)@ - Calculate the moment of inertia for a hollow circle, @r1@ and @r2@ are the inner and outer diameters in no particular order. _(A solid circle has an inner diameter of 0)_ * @cpFloat cpMomentForSegment(cpFloat m, cpVect a, cpVect b, cpFloat radius)@ - Calculate the moment of inertia for a line segment. The endpoints @a@ and @b@ are relative to the body. * @cpFloat cpMomentForPoly(cpFloat m, int count, const cpVect *verts, cpVect offset, cpFloat radius)@ - Calculate the moment of inertia for a solid polygon shape assuming its center of gravity is at its centroid. The offset is added to each vertex. * @cpFloat cpMomentForBox(cpFloat m, cpFloat width, cpFloat height)@ - Calculate the moment of inertia for a solid box centered on the body. <%= pop_open_example "Moments" %> Use the following functions to get the area for common Chipmunk shapes if you want to approximate masses or density or whatnot. * @cpFloat cpAreaForCircle(cpFloat r1, cpFloat r2)@ - Area of a hollow circle. * @cpFloat cpAreaForSegment(cpVect a, cpVect b, cpFloat r)@ - Area of a beveled segment. _(Will always be zero if radius is zero)_ * @cpFloat cpAreaForPoly(const int count, const cpVect *verts, cpFloat radius)@ - Signed area of a polygon shape. Returns a negative number for polygons with a clockwise winding. <%= h 2, "Coordinate Conversion Functions:", "CoordinateConversion" %> Many things are defined in coordinates local to a body meaning that the (0,0) is at the center of gravity of the body and the axis rotate along with the body. * @cpVect cpBodyLocalToWorld(const cpBody *body, const cpVect v)@ - Convert from body local coordinates to world space coordinates. * @cpVect cpBodyWorldToLocal(const cpBody *body, const cpVect v)@ - Convert from world space coordinates to body local coordinates. <%= h 2, "Velocity Conversion Functions:", "VelocityConversion" %> It's often useful to know the absolute velocity of a point on the surface of a body since the angular velocity affects everything except the center of gravity. * @cpVect cpBodyVelocityAtWorldPoint(const cpBody *body, const point)@ - Get the absolute velocity of the rigid body at the given world point @point@. * @cpVect cpBodyVelocityAtLocalPoint(const cpBody *body, const point)@ - Get the absolute velocity of the rigid body at the given body local point @point@. <%= h 2, "Applying Forces and Torques:", "Forces" %> People are sometimes confused by the difference between a force and an impulse. An impulse is a very large force applied over a very short period of time. Some examples are a ball hitting a wall or cannon firing. Chipmunk treats impulses as if they occur instantaneously by adding directly to the velocity of an object. Both impulses and forces are affected the mass of an object. Doubling the mass of the object will halve the effect. * @void cpBodyApplyForceAtWorldPoint(cpBody *body, const cpVect force, const cpVect point)@ - Add the force @force@ to @body@ as if applied from the world point @point@. * @void cpBodyApplyForceAtLocalPoint(cpBody *body, const cpVect force, const cpVect point)@ - Add the local force @force@ to @body@ as if applied from the body local point @point@. * @void cpBodyApplyImpulseAtWorldPoint(cpBody *body, const cpVect impulse, const cpVect point)@ - Add the impulse @impulse@ to @body@ as if applied from the world point @point@. * @void cpBodyApplyImpulseAtLocalPoint(cpBody *body, const cpVect impulse, const cpVect point)@ - Add the local impulse @impulse@ to @body@ as if applied from the body local point @point@. <%= h 2, "Sleeping Functions:", "Sleeping" %> Chipmunk supports a sleeping feature which improves performance by not simulating groups of objects that aren't moving. Read more about it in the "cpSpace section":#cpSpace-Sleeping. * @cpBool cpBodyIsSleeping(const cpBody *body)@ - Returns true if @body@ is sleeping. * @void cpBodyActivate(cpBody *body)@ - Reset the idle timer on a body. If it was sleeping, wake it and any other bodies it was touching. * @void cpBodySleep(cpBody *body)@ - Forces a body to fall asleep immediately even if it's in midair. Cannot be called from a callback. * @void cpBodyActivateStatic(cpBody *body, cpShape *filter)@ - Similar in function to @cpBodyActivate()@. Activates all bodies touching @body@. If @filter@ is not @NULL@, then only bodies touching through @filter@ will be awoken.
    void cpBodySleepWithGroup(cpBody *body, cpBody *group)
    p(expl). When objects in Chipmunk sleep, they sleep as a group of all objects that are touching or jointed together. When an object is woken up, all of the objects in its group are woken up. @cpBodySleepWithGroup()@ allows you group sleeping objects together. It acts identically to @cpBodySleep()@ if you pass @NULL@ as @group@ by starting a new group. If you pass a sleeping body for @group@, @body@ will be awoken when @group@ is awoken. You can use this to initialize levels and start stacks of objects in a pre-sleeping state. <%= pop_open_example "Sleeping" %> <%= h 2, "Iterators", "Iterators" %>
    typedef void (*cpBodyShapeIteratorFunc)(cpBody *body, cpShape *shape, void *data)
    void cpBodyEachShape(cpBody *body, cpBodyShapeIteratorFunc func, void *data)
    p(expl). Call @func@ once for each shape that is attached to @body@ and added to a space. @data@ is passed along as a context value. It is safe to remove shapes using these callbacks.
    typedef void (*cpBodyConstraintIteratorFunc)(cpBody *body, cpConstraint *constraint, void *data)
    void cpBodyEachConstraint(cpBody *body, cpBodyConstraintIteratorFunc func, void *data)
    p(expl). Call @func@ once for each constraint that is attached to @body@ and added to a space. @data@ is passed along as a context value. It is safe to remove constraints using thes callbacks.
    typedef void (*cpBodyArbiterIteratorFunc)(cpBody *body, cpArbiter *arbiter, void *data)
    void cpBodyEachArbiter(cpBody *body, cpBodyArbiterIteratorFunc func, void *data)
    p(expl). This one is more interesting. Calls @func@ once for each collision pair that @body@ is involved in. Calling @cpArbiterGet[Bodies|Shapes]()@ or @CP_ARBITER_GET_[BODIES|SHAPES]()@ will return the body or shape for @body@ as the first argument. You can use this to check all sorts of collision information for a body like if it's touching the ground, another particular object, how much collision force is being applied to an object, etc. Sensor shapes and arbiters that have been rejected by a collision handler callback or @cpArbiterIgnore()@ are not tracked by the contact graph. *Note:* If your compiler supports blocks (such as Clang), there are an alternate set of functions you can call. @cpBodyEachShape_b()@, etc. See @chipmunk.h@ for more information. <%= pop_open_example "Crushing" %> <%= h 2, "Integration Callbacks:", "Integration Callbacks" %> This section is a stub. For now you can look at the Planet demo for an example of how to use integration callbacks to implement planetary gravity. <%= h 1, "Chipmunk Collision Shapes: @cpShape@", "cpShape" %> There are currently 3 collision shape types: * *Circles*: Fastest and simplest collision shape. * *Line segments*: Meant mainly as a static shape. Can be beveled in order to give them a thickness. * *Convex polygons*: Slowest, but most flexible collision shape. You can add as many shapes to a body as you wish. That is why the two types are separate. Combining multiple shapes gives you the flexibility to make any object you want as well as providing different areas of the same object with different friction, elasticity or callback values. When creating different types of shapes, you will always be given a @cpShape*@ pointer back. This is because Chipmunk shapes are meant to be opaque types. Think of the specific collision types such as @cpCircleShape@, @cpSegmentShape@ and @cpPolyShape@ as private subclasses of @cpShape@. You can still read some properties from them using the getter functions, but you are not intended to cast @cpShape@ pointers to their specific types. <%= h 2, "Properties:", "Properties" %> Chipmunk provides getter/setter functions for a number of properties on collision shapes. Setting most properties will automatically wake the attached rigid body, if it's sleeping. You can also set some of the fields directly on the cpShape struct if you wish. They are documented in the headers.
    cpBody * cpShapeGetBody(const cpShape *shape)
    void cpShapeSetBody(cpShape *shape, cpBody *body)
    p(expl). The rigid body the shape is attached to. Can only be set when the shape is not added to a space.
    cpBB cpShapeGetBB(const cpShape *shape)
    p(expl). The bounding box of the shape. Only guaranteed to be valid after @cpShapeCacheBB()@ or @cpSpaceStep()@ is called. Moving a body that a shape is connected to does not update its bounding box. For shapes used for queries that aren't attached to bodies, you can also use @cpShapeUpdate()@.
    cpBool cpShapeGetSensor(const cpShape *shape)
    void cpShapeSetSensor(cpShape *shape, cpBool value)
    p(expl). A boolean value if this shape is a sensor or not. Sensors only call collision callbacks, and never generate real collisions.
    cpFloat cpShapeGetElasticity(const cpShape *shape)
    void cpShapeSetElasticity(cpShape *shape, cpFloat value)
    p(expl). Elasticity of the shape. A value of 0.0 gives no bounce, while a value of 1.0 will give a "perfect" bounce. However due to inaccuracies in the simulation using 1.0 or greater is not recommended however. The elasticity for a collision is found by multiplying the elasticity of the individual shapes together.
    cpFloat cpShapeGetFriction(const cpShape *shape)
    void cpShapeSetFriction(cpShape *shape, cpFloat value)
    p(expl). Friction coefficient. Chipmunk uses the Coulomb friction model, a value of 0.0 is frictionless. The friction for a collision is found by multiplying the friction of the individual shapes together. "Tables of friction coefficients":http://www.roymech.co.uk/Useful_Tables/Tribology/co_of_frict.htm.
    cpVect cpShapeGetSurfaceVelocity(const cpShape *shape)
    void cpShapeSetSurfaceVelocity(cpShape *shape, cpVect value)
    p(expl). The surface velocity of the object. Useful for creating conveyor belts or players that move around. This value is only used when calculating friction, not resolving the collision.
    cpCollisionType cpShapeGetCollisionType(const cpShape *shape)
    void cpShapeSetCollisionType(cpShape *shape, cpCollisionType value)
    p(expl). You can assign types to Chipmunk collision shapes that trigger callbacks when objects of certain types touch. See the "callbacks section":#Callbacks-Handlers for more information.
    cpShapeFilter cpShapeGetFilter(const cpShape *shape)
    void cpShapeSetFilter(cpShape *shape, cpShapeFilter filter)
    p(expl). Set the collision filter for this shape. See "Filtering Collisions":#Filtering for more information.
    cpSpace* cpShapeGetSpace(const cpShape *shape)
    p(expl). Get the @cpSpace@ that @shape@ has been added to.
    cpDataPointer cpShapeGetUserData(const cpShape *shape)
    void cpShapeSetUserData(cpShape *shape, cpDataPointer value)
    p(expl). A user definable data pointer. If you set this to point at the game object the shapes is for, then you can access your game object from Chipmunk callbacks. <%= h 2, "Fast Collision Filtering using cpShapeFilter:", "Filtering" %> Chipmunk has two primary means of ignoring collisions: groups and category masks. *Groups* are used to ignore collisions between parts on a complex object. A ragdoll is a good example. When jointing an arm onto the torso, you'll want them to allow them to overlap. Groups allow you to do exactly that. Shapes that have the same group don't generate collisions. So by placing all of the shapes in a ragdoll in the same group, you'll prevent it from colliding against other parts of itself. *Category masks* allow you to mark which categories an object belongs to and which categories it collidies with. For example, a game has four collision categories: player (0), enemy (1), player bullet (2), and enemy bullet (3). Neither players nor enemies should not collide with their own bullets, and bullets should not collide with other bullets. However, players collide with enemy bullets, and enemies collide with player bullets. | Object | Object Category | Category Mask | | "Player" | 1 | 4, 5 | | "Enemy" | 2 | 2, 3, 5 | | "Player Bullet" | 3 | 1, 5 | | "Enemy Bullet" | 4 | 2, 5 | | "Walls" | 5 | 1, 2, 3, 4 | Note that everything in this example collides with walls. Additionally, the enemies collide with eachother. By default, objects exist in every category and collide with every category. Objects can fall into multiple categories. For instance, you might have a category for a red team, and have a red player bullet. In the above example, each object only has one category. If you make use of multiple categories on an object, you may also wish to consider replacing the @cpShapeFilter@ struct and the @cpShapeFilterReject()@ function in @chipmunk_private.h@ to customize it to better suit your game's needs. The default type of @categories@ and @mask@ in @cpShapeFilter@ is @unsigned int@ which has a resolution of 32 bits on most systems. You can redefine @cpBitmask@ in @chipmunk_types.h@ if you need more bits to work with. There is one last way of filtering collisions using collision handlers. See the "section on callbacks":#Callbacks for more information. Collision handlers can be more flexible, but can be slower. Fast collision filtering rejects collisions before running the expensive collision detection code, so using groups or category masks is preferred. <%= h 2, "Memory Management Functions:", "Memory" %>
    void cpShapeDestroy(cpShape *shape)
    void cpShapeFree(cpShape *shape)
    p(expl). @Destroy@ and @Free@ functions are shared by all shape types. Allocation and initialization functions are specific to each shape type. See below. <%= h 2, "Misc Functions:", "Misc" %> * @cpBB cpShapeCacheBB(cpShape *shape)@ - Synchronizes @shape@ with the body its attached to. * @cpBB cpShapeUpdate(cpShape *shape, cpVect pos, cpVect rot)@ - Sets the position and rotation of the shape to <%= h 2, "Working With Circle Shapes:", "Circles" %>
    cpCircleShape *cpCircleShapeAlloc(void)
    cpCircleShape *cpCircleShapeInit(cpCircleShape *circle, cpBody *body, cpFloat radius, cpVect offset)
    cpShape *cpCircleShapeNew(cpBody *body, cpFloat radius, cpVect offset)
    p(expl). @body@ is the body to attach the circle to, @offset@ is the offset from the body's center of gravity in body local coordinates.
    cpVect cpCircleShapeGetOffset(cpShape *circleShape)
    cpFloat cpCircleShapeGetRadius(cpShape *circleShape)
    p(expl). Getters for circle shape properties. Passing as non-circle shape will throw an assertion. <%= h 2, "Working With Segment Shapes:", "Segments" %>
    cpSegmentShape* cpSegmentShapeAlloc(void)
    cpSegmentShape* cpSegmentShapeInit(cpSegmentShape *seg, cpBody *body, cpVect a, cpVect b, cpFloat radius)
    cpShape* cpSegmentShapeNew(cpBody *body, cpVect a, cpVect b, cpFloat radius)
    p(expl). @body@ is the body to attach the segment to, @a@ and @b@ are the endpoints, and @radius@ is the thickness of the segment.
    cpVect cpSegmentShapeGetA(cpShape *shape)
    cpVect cpSegmentShapeGetB(cpShape *shape)
    cpVect cpSegmentShapeGetNormal(cpShape *shape)
    cpFloat cpSegmentShapeGetRadius(cpShape *shape)
    p(expl). Getters for segment shape properties. Passing a non-segment shape will throw an assertion.
    void cpSegmentShapeSetNeighbors(cpShape *shape, cpVect prev, cpVect next)
    p(expl). When you have a number of segment shapes that are all joined together, things can still collide with the "cracks" between the segments. By setting the neighbor segment endpoints you can tell Chipmunk to avoid colliding with the inner parts of the crack. <%= h 2, "Working With Polygon Shapes:", "Polys" %>
    cpPolyShape *cpPolyShapeAlloc(void)
    
    cpPolyShape *cpPolyShapeInit(cpPolyShape *poly, cpBody *body, int count, const cpVect *verts, cpTransform transform, cpFloat radius)
    cpShape *cpPolyShapeNew(cpBody *body, int numVerts, cpVect *verts, cpTransform transform, cpFloat radius)
    p(expl). @body@ is the body to attach the poly to, @verts@ is an array of @cpVect@ structs, @transform@ will be applied to every vertex. A convex hull will be calculated from the vertexes automatically. The polygon shape will be created with a @radius@, increasing the size of the shape.
    cpPolyShape* cpPolyShapeInitRaw(cpPolyShape *poly, cpBody *body, int count, const cpVect *verts, cpFloat radius)
    cpShape* cpPolyShapeNewRaw(cpBody *body, int count, const cpVect *verts, cpFloat radius)
    Alternate constructors for poly shapes. This version does not apply a transform nor does it create a convex hull. Verticies _must_ be provided with a counter-clockwise winding.
    int cpPolyShapeGetNumVerts(cpShape *shape)
    cpVect cpPolyShapeGetVert(cpShape *shape, int index)
    cpFloat cpPolyShapeGetRadius()
    p(expl). Getters for poly shape properties. Passing a non-poly shape or an index that does not exist will throw an assertion. <%= h 3, "Boxes:", "Boxes" %> Because boxes are so common in physics games, Chipmunk provides shortcuts to create box shaped polygons. The boxes will always be centered at the center of gravity of the body you are attaching them to. Adding a small radius will bevel the corners and can significantly reduce problems where the box gets stuck on seams in your geometry. If you want to create an off-center box, you will need to use @cpPolyShapeNew()@ or @cpPolyShapeInit()@.
    cpPolyShape *cpBoxShapeInit(cpPolyShape *poly, cpBody *body, cpFloat width, cpFloat height, cpFloat radius)
    cpPolyShape *cpBoxShapeInit2(cpPolyShape *poly, cpBody *body, cpBB box, cpFloat radius)
    
    cpShape *cpBoxShapeNew(cpBody *body, cpFloat width, cpFloat height, cpFloat radius)
    cpShape *cpBoxShapeNew2(cpBody *body, cpBB box, cpFloat radius)
    <%= h 3, "Poly Shape Helper Functions:", "PolyHelpers" %> * @cpVect cpCentroidForPoly(const int count, const cpVect *verts)@ - Calculate the centroid for a polygon. <%= h 3, "Convex Hull Helper Functions:", "ConvexHelpers" %>
    int cpConvexHull(int count, cpVect *verts, cpVect *result, int *first, cpFloat tol)
    p(expl). Calculate the convex hull of a given set of points. Returns the count of points in the hull. @result@ must be a pointer to a @cpVect@ array with at least @count@ elements. If @result@ is @NULL@, then @verts@ array wil be reduced instead. @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]@) @tol@ is the allowed amount to shrink the hull when simplifying it. A tolerance of 0.0 creates an exact hull.
    #define CP_CONVEX_HULL(inputCount, inputVerts, outputCount_varName, outputVerts_varName)
    p(expl). Convenience macro for using @cpConvexHull()@. Creates an array on the stack using @alloca()@ and then calls @cpConvexHull()@. Because the output array is created on the stack it doesn't need to be freed. <%= pop_open_example "cpConvexHull" %> <%= h 2, "Modifying cpShapes:", "Modifying" %> The short answer is that you can't because the changes would be only picked up as a change to the position of the shape's surface, but not its velocity. The long answer is that you can using the "unsafe" API as long as you realize that doing so will result in unrealistic physical behavior. These extra functions are defined in a separate header @chipmunk_unsafe.h@. <%= h 2, "Notes:", "Notes" %> * You can attach multiple collision shapes to a rigid body. This should allow you to create almost any shape you could possibly need. * Shapes attached to the same rigid body will never generate collisions. You don't have to worry about overlap when attaching multiple shapes to a rigid body. * Make sure you add both the body and its collision shapes to a space. <%= h 1, "Chipmunk Spaces: @cpSpace@", "cpSpace" %> Spaces in Chipmunk are the basic unit of simulation. You add rigid bodies, shapes, and constraints to the space and then step them all forward through time together. <%= h 2, "What Are Iterations, and Why Should I Care?", "Iterations" %> Chipmunk uses an iterative solver to figure out the forces between objects in the space. What this means is that it builds a big list of all of the collisions, joints, and other constraints between the bodies and makes several passes over the list considering each one individually. The number of passes it makes is the iteration count, and each iteration makes the solution more accurate. If you use too many iterations, the physics should look nice and solid, but may use up too much CPU time. If you use too few iterations, the simulation may seem mushy or bouncy when the objects should be solid. Setting the number of iterations lets you balance between CPU usage and the accuracy of the physics. Chipmunk's default of 10 iterations is sufficient for most simple games. <%= h 2, "Sleeping", "Sleeping" %> Spaces can disable entire groups of objects that have stopped moving to save CPU time and battery life. In order to use this feature you must do two things. You must enable sleeping explicitly by choosing a time threshold value for @cpSpace.sleepTimeThreshold@. This threshold is the amount of time something must be idle before it falls asleep. @cpSpace.idleSpeedThreshold@ defines what is considered idle. If you do not set @cpSpace.idleSpeedThreshold@ explicitly, a value will be chosen automatically based on the current amount of gravity. Be mindful that objects cannot fall asleep if they are touching or jointed to a kinematic body. <%= h 2, "Properties:", "Properties" %>
    int cpSpaceGetIterations(const cpSpace *space)
    void cpSpaceSetIterations(cpSpace *space, int value)
    p(expl). Iterations allow you to control the accuracy of the solver. Defaults to 10. See "above":#cpSpace-Iterations for more information.
    cpVect cpSpaceGetGravity(const cpSpace *space)
    void cpSpaceSetGravity(cpSpace *space, cpVect value)
    p(expl). Global gravity applied to the space. Defaults to @cpvzero@. Can be overridden on a per body basis by writing custom integration functions. Changing the gravity will activate all sleeping bodies in the space.
    cpFloat cpSpaceGetDamping(const cpSpace *space)
    void cpSpaceSetDamping(cpSpace *space, cpFloat value)
    p(expl). Amount of simple damping to apply to the space. A value of 0.9 means that each body will lose 10% of its velocity per second. Defaults to 1. Like @gravity@, it can be overridden on a per body basis.
    cpFloat cpSpaceGetIdleSpeedThreshold(const cpSpace *space)
    void cpSpaceSetIdleSpeedThreshold(cpSpace *space, cpFloat value)
    p(expl). Speed threshold for a body to be considered idle. The default value of 0 means the space estimates a good threshold based on gravity.
    cpFloat cpSpaceGetSleepTimeThreshold(const cpSpace *space)
    void cpSpaceSetSleepTimeThreshold(cpSpace *space, cpFloat value)
    p(expl). Time a group of bodies must remain idle in order to fall asleep. The default value of @INFINITY@ disables the sleeping feature.
    cpFloat cpSpaceGetCollisionSlop(const cpSpace *space)
    void cpSpaceSetCollisionSlop(cpSpace *space, cpFloat value)
    p(expl). Amount of overlap between shapes that is allowed. To improve stability, set this as high as you can without noticable overlapping. It defaults to 0.1.
    cpFloat cpSpaceGetCollisionBias(const cpSpace *space)
    void cpSpaceSetCollisionBias(cpSpace *space, cpFloat value)
    p(expl). Chipmunk allows fast moving objects to overlap, then fixes the overlap over time. Overlapping objects are unavoidable even if swept collisions are supported, and this is an efficient and stable way to deal with overlapping objects. The bias value controls what percentage of overlap remains unfixed after a second and defaults to ~0.2%. Valid values are in the range from 0 to 1, but using 0 is not recommended for stability reasons. The default value is calculated as @cpfpow(1.0f - 0.1f, 60.0f)@ meaning that Chipmunk attempts to correct 10% of error ever 1/60th of a second. *Note:* Very very few games will need to change this value.
    cpTimestamp cpSpaceGetCollisionPersistence(const cpSpace *space)
    void cpSpaceSetCollisionPersistence(cpSpace *space, cpTimestamp value)
    p(expl). The number of frames the space keeps collision solutions around for. Helps prevent jittering contacts from getting worse. This defaults to 3 and very very _very_ few games will need to change this value.
    cpFloat cpSpaceGetCurrentTimeStep(const cpSpace *space)
    p(expl). Retrieves the current (if you are in a callback from @cpSpaceStep()@) or most recent (outside of a @cpSpaceStep()@ call) timestep.
    cpFloat cpSpaceIsLocked(const cpSpace *space)
    p(expl). Returns true when you cannot add/remove objects from the space. In particular, spaces are locked when in a collision callback. Instead, run your code in a post-step callback instead.
    cpDataPointer cpSpaceGetUserData(const cpSpace *space)
    void cpSpaceSetUserData(cpSpace *space, cpDataPointer value)
    p(expl). A user definable data pointer. It is often useful to point this at the gamestate object or scene management object that owns the space.
    cpBody * cpSpaceGetStaticBody(const cpSpace *space)
    p(expl). A dedicated static body for the space. You don't have to use it, but because its memory is managed automatically with the space its very convenient. You can set its user data pointer to something helpful if you want for callbacks. <%= h 2, "Memory Management Functions:", "Memory" %>
    cpSpace* cpSpaceAlloc(void)
    cpSpace* cpSpaceInit(cpSpace *space)
    cpSpace* cpSpaceNew()
    
    void cpSpaceDestroy(cpSpace *space)
    void cpSpaceFree(cpSpace *space)
    p(expl). More standard Chipmunk memory functions. <%= h 2, "Operations:", "Operations" %>
    cpShape *cpSpaceAddShape(cpSpace *space, cpShape *shape)
    cpBody *cpSpaceAddBody(cpSpace *space, cpBody *body)
    cpConstraint *cpSpaceAddConstraint(cpSpace *space, cpConstraint *constraint)
    
    void cpSpaceRemoveShape(cpSpace *space, cpShape *shape)
    void cpSpaceRemoveBody(cpSpace *space, cpBody *body)
    void cpSpaceRemoveConstraint(cpSpace *space, cpConstraint *constraint)
    
    cpBool cpSpaceContainsShape(cpSpace *space, cpShape *shape)
    cpBool cpSpaceContainsBody(cpSpace *space, cpBody *body)
    cpBool cpSpaceContainsConstraint(cpSpace *space, cpConstraint *constraint)
    p(expl). These functions add and remove shapes, bodies and constraints from @space@. The add/remove functions cannot be called from within a callback other than a @postStep()@ callback _(which is different than a postSolve() callback!)_. Attempting to add or remove objects from the space while @cpSpaceStep()@ is still executing will throw an assertion. See the "callbacks section":#Callbacks for more information. The add functions return the thing being added so that you can create and add something in one line. Be careful not to free bodies before removing shapes and constraints attached to them or you will cause crashes.. The contains functions allow you to check if an object has been added to the space or not. <%= h 2, "Spatial Indexing:", "SpatialIndexing" %> Occasionally, you might want to update the collision detection data for a shape. If you move a static shape or a static body you *must* do this to let Chipmunk know it needs to have its collision detection data updated. You may also want to manually update the collision data for normal shapes if you move them and still want to perform queries against them before the next call to @cpSpaceStep()@. * @void cpSpaceReindexShape(cpSpace *space, cpShape *shape)@ - Reindex a specific shape. * @void cpSpaceReindexShapesForBody(cpSpace *space, cpBody *body)@ - Reindex all the shapes for a certain body. * @void cpSpaceReindexStatic(cpSpace *space)@ - Reindex all static shapes. Generally updating only the shapes that changed is faster. <%= h 2, "Iterators:", "Iterators" %>
    typedef void (*cpSpaceBodyIteratorFunc)(cpBody *body, void *data)
    void cpSpaceEachBody(cpSpace *space, cpSpaceBodyIteratorFunc func, void *data)
    p(expl). Call @func@ for each body in the space also passing along your data pointer. Sleeping bodies are included, but static and kinematic bodies are not as they aren't added to the space. <%= pop_open_example "cpSpaceEachBody" %>
    typedef void (*cpSpaceShapeIteratorFunc)(cpShape *shape, void *data)
    void cpSpaceEachShape(cpSpace *space, cpSpaceShapeIteratorFunc func, void *data)
    p(expl). Call @func@ for each shape in the space also passing along your data pointer. Sleeping and static shapes are included.
    typedef void (*cpSpaceConstraintIteratorFunc)(cpConstraint *constraint, void *data)
    void cpSpaceEachConstraint(cpSpace *space, cpSpaceConstraintIteratorFunc func, void *data)
    p(expl). Call @func@ for each constraint in the space also passing along your data pointer. *Note:* If your compiler supports blocks (such as Clang), there are an alternate set of functions you can call. @cpSpaceEachBody_b()@, etc. See @chipmunk.h@ for more information. <%= h 2, "Simulating the Space:", "Simulating" %>
    void cpSpaceStep(cpSpace *space, cpFloat dt)
    p(expl). Update the space for the given time step. Using a fixed time step is _highly_ recommended. Doing so can greatly increase the quality of the simulation. The easiest way to do constant timesteps is to simple step forward by 1/60th of a second (or whatever your target framerate is) for each frame regardless of how long it took to render. This works fine for many games, but a better way to do it is to separate your physics timestep and rendering. "This":http://gafferongames.com/game-physics/fix-your-timestep/ is a good article on how to do that. <%= h 2, "Enabling and Tuning the Spatial Hash:", "SpatialHash" %> Chipmunk officially supports two spatial indexes. The default is an axis-aligned bounding box tree inspired by the one used in the Bullet Physics library, but caching of overlapping leaves was added to give it very good temporal coherence. The tree requires no tuning, and most games will find that they get the best performance using from the tree. The other available spatial index type available is a spatial hash, which can be much faster when you have a very large number (1000s) of objects that are all the same size. For smaller numbers of objects, or objects that vary a lot in size, the spatial hash is usually much slower. It also requires tuning (usually through experimentation) to get the best possible performance.
    void cpSpaceUseSpatialHash(cpSpace *space, cpFloat dim, int count)
    p(expl). Switch the space to use a spatial hash instead of the bounding box tree. The spatial hash data is fairly size sensitive. @dim@ is the size of the hash cells. Setting @dim@ to the average collision shape size is likely to give the best performance. Setting @dim@ too small will cause the shape to be inserted into many cells, setting it too low will cause too many objects into the same hash slot. @count@ is the _suggested_ minimum number of cells in the hash table. If there are too few cells, the spatial hash will return many false positives. Too many cells will be hard on the cache and waste memory. the Setting @count@ to ~10x the number of objects in the space is probably a good starting point. Tune from there if necessary. Using the spatial has visualization in the demo program you can see what I mean. The grey squares represent cells in the spatial hash. The darker the cell, the more objects have been mapped into that cell. A good @dim@ size is when your objects fit nicely into the grid: !images/hash_just_right.png! Notice the light grey meaning that each cell doesn't have too many objects mapped onto it. When you use too small a size, Chipmunk has to insert each object into a lot of cells. This can get expensive. !images/hash_too_small.png! Notice that the grey cells are very small compared to the collision shapes. When you use too big of a size, a lot of shapes will fit into each cell. Each shape has to be checked against every other shape in the cell, so this makes for a lot of unnecessary collision checks. !images/hash_too_big.png! Notice the dark grey cells meaning that many objects are mapped onto them. Chipmunk also has an experimental single axis sort and sweep implementation. It can be very efficient on mobile games if your world is very long and flat like a racing game. See the code for @cpSpaceUseSpatialHash()@ if you want to try enabling it. <%= h 2, "Notes:", "Notes" %> * When removing objects from the space, make sure you remove any other objects that reference it. For instance, when you remove a body, remove the joints and shapes attached to it. * Using more iterations or smaller time steps will increase the physics quality, but also increase the CPU usage. * Because static shapes are only rehashed when you request it, TODO: cpSpaceHashResize <%= h 1, "Chipmunk Constraints: @cpConstraint@", "cpConstraint" %> A constraint is something that describes how two bodies interact with each other. (how they constrain each other) Constraints can be simple joints that allow bodies to pivot around each other like the bones in your body, or they can be more abstract like the gear joint or motors. <%= h 2, "What constraints are and what they are not:", "WhatAreConstraints" %> Constraints in Chipmunk are all velocity based constraints. This means that they act primarily by synchronizing the velocity of two bodies. A pivot joint holds two anchor points on two separate bodies together by defining equations that say that the velocity of the anchor points must be the same and calculating impulses to apply to the bodies to try and keep it that way. A constraint takes a velocity as it's primary input and produces a velocity change as its output. Some constraints, (joints in particular) apply velocity changes to correct differences in positions. More about this in the next section. A spring connected between two bodies is not a constraint. It's very constraint-like as it creates forces that affect the velocities of the two bodies, but a spring takes distances as input and produces forces as its output. If a spring is not a constraint, then why do I have two varieties of spring constraints you ask? The reason is because they are _damped springs_. The damping associated with the spring is a true constraint that creates velocity changes based on the relative velocities of the two bodies it links. As it is convenient to put a damper and a spring together most of the time, I figured I might as well just apply the spring force as part of the constraint instead of having a damper constraint and having the user calculate and apply their own spring forces separately. <%= h 2, "Properties:", "Properties" %>
    cpBody * cpConstraintGetA(const cpConstraint *constraint)
    cpBody * cpConstraintGetB(const cpConstraint *constraint)
    p(expl). Getters for the two bodies the constraint is attached to.
    cpFloat cpConstraintGetMaxForce(const cpConstraint *constraint)
    void cpConstraintSetMaxForce(cpConstraint *constraint, cpFloat value)
    p(expl). The maximum force that the constraint can use to act on the two bodies. Defaults to INFINITY.
    cpFloat cpConstraintGetErrorBias(const cpConstraint *constraint)
    void cpConstraintSetErrorBias(cpConstraint *constraint, cpFloat value)
    p(expl). The percentage of joint error that remains unfixed after a second. This works exactly the same as the collision bias property of a space, but applies to fixing error (stretching) of joints instead of overlapping collisions.
    cpFloat cpConstraintGetMaxBias(const cpConstraint *constraint)
    void cpConstraintSetMaxBias(cpConstraint *constraint, cpFloat value)
    p(expl). The maximum speed at which the constraint can apply error correction. Defaults to INFINITY.
    cpSpace* cpConstraintGetSpace(const cpConstraint *constraint)
    p(expl). Get the @cpSpace@ that @constraint@ has been added to.
    cpBool cpConstraintGetCollideBodies(const cpConstraint *constraint)
    void cpConstraintSetCollideBodies(cpConstraint *constraint, cpBool collideBodies)
    p(expl). Constraints can be used for filtering collisions too. When two bodies collide, Chipmunk ignores the collisions if this property is set to @cpFalse@ on any constraint that connects the two bodies. Defaults to @cpTrue@. This can be used to create a chain that self collides, but adjacent links in the chain do not collide.
    cpDataPointer cpConstraintGetUserData(const cpConstraint *constraint)
    void cpConstraintSetUserData(cpConstraint *constraint, cpDataPointer value)
    p(expl). User data pointer. Use this pointer to get a reference to the game object that owns this constraint from callbacks.
    cpFloat cpConstraintGetImpulse(cpConstraint *constraint)
    p(expl). The most recent impulse that @constraint@ applied. To convert this to a force, divide by the timestep passed to @cpSpaceStep()@. You can use this to implement breakable joints to check if the force they attempted to apply exceeded a certain threshold. <%= pop_open_example "BreakableJoint" %> To access properties of specific joint types, use the getter and setter functions provided (ex: @cpPinJointGetanchorA()@). See the lists of properties for more information. <%= h 2, "Error correction by Feedback:", "ErrorCorrection" %> Joints in Chipmunk are not perfect. A pin joint can't maintain the exact correct distance between its anchor points, nor can a pivot joint hold its anchor points completely together. Instead, they are designed to deal with this by correcting themselves over time. Since Chipmunk 5, you have a fair amount of extra control over how joints correct themselves and can even use this ability to create physical effects that allow you to use joints in unique ways: * Servo motors - Ex: open/close doors or rotate things without going over a maximum force. * Winches - Pull one object towards another at a constant speed without going over a maximum force. * Mouse manipulation - Interact with objects smoothly given coarse/shaky mouse input. There are three properties of cpConstraint structs that control the error correction, @maxForce@, @maxBias@, and @biasCoef@. @maxForce@ is pretty self explanatory, a joint or constraint will not be able to use more than this amount of force in order to function. If it needs more force to be able to hold itself together, it will fall apart. @maxBias@ is the maximum speed at which error correction can be applied. If you change a property on a joint so that the joint will have to correct itself, it normally does so very quickly. By setting a maxSpeed you can make the joint work like a servo, correcting itself at a constant rate over a longer period of time. Lastly, @biasCoef@ is the percentage of error corrected every step before clamping to a maximum speed. You can use this to make joints correct themselves smoothly instead of at a constant speed, but is probably the least useful of the three properties by far. <%= pop_open_example "JointRecipies" %> <%= h 2, "Constraints and Collision Shapes:", "Shapes" %> Neither constraints or collision shapes have any knowledge of the other. When connecting joints to a body the anchor points don't need to be inside of any shapes attached to the body and it often makes sense that they shouldn't. Also, adding a constraint between two bodies doesn't prevent their collision shapes from colliding. In fact, this is the primary reason that the collision group property exists. <%= h 2, "Video Tour of Current Joint Types. (Requires connection to YouTube)", "Video" %> <%= h 2, "Shared Memory Management Functions:", "Memory" %>
    void cpConstraintDestroy(cpConstraint *constraint)
    void cpConstraintFree(cpConstraint *constraint)
    p(expl). @Destroy@ and @Free@ functions are shared by all joint types. Allocation and initialization functions are specific to each joint type. <%= h 1, "Constraint Types:", "ConstraintTypes" %> <%= h 2, "Pin Joints:", "cpPinJoint" %>
    cpPinJoint *cpPinJointAlloc(void)
    cpPinJoint *cpPinJointInit(cpPinJoint *joint, cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB)
    cpConstraint *cpPinJointNew(cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB)
    p(expl). @a@ and @b@ are the two bodies to connect, and @anchorA@ and @anchorB@ are the anchor points on those bodies. The distance between the two anchor points is measured when the joint is created. If you want to set a specific distance, use the setter function to override it. <%= h 3, "Properties", "Properties" %> * @cpVect cpPinJointGetanchorA(const cpConstraint *constraint)@ * @void cpPinJointSetanchorA(cpConstraint *constraint, cpVect value)@ * @cpVect cpPinJointGetanchorB(const cpConstraint *constraint)@ * @void cpPinJointSetanchorB(cpConstraint *constraint, cpVect value)@ * @cpFloat cpPinJointGetDist(const cpConstraint *constraint)@ * @void cpPinJointSetDist(cpConstraint *constraint, cpFloat value)@ <%= h 2, "Slide Joints:", "cpSlideJoint" %>
    cpSlideJoint *cpSlideJointAlloc(void)
    
    cpSlideJoint *cpSlideJointInit(
    	cpSlideJoint *joint, cpBody *a, cpBody *b,
    	cpVect anchorA, cpVect anchorB, cpFloat min, cpFloat max
    )
    
    cpConstraint *cpSlideJointNew(cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB, cpFloat min, cpFloat max)
    p(expl). @a@ and @b@ are the two bodies to connect, @anchorA@ and @anchorB@ are the anchor points on those bodies, and @min@ and @max@ define the allowed distances of the anchor points. <%= h 3, "Properties", "Properties" %> * @cpVect cpSlideJointGetanchorA(const cpConstraint *constraint)@ * @void cpSlideJointSetanchorA(cpConstraint *constraint, cpVect value)@ * @cpVect cpSlideJointGetanchorB(const cpConstraint *constraint)@ * @void cpSlideJointSetanchorB(cpConstraint *constraint, cpVect value)@ * @cpFloat cpSlideJointGetMin(const cpConstraint *constraint)@ * @void cpSlideJointSetMin(cpConstraint *constraint, cpFloat value)@ * @cpFloat cpSlideJointGetMax(const cpConstraint *constraint)@ * @void cpSlideJointSetMax(cpConstraint *constraint, cpFloat value)@ <%= h 2, "Pivot Joints:", "cpPivotJoint" %>
    cpPivotJoint *cpPivotJointAlloc(void)
    cpPivotJoint *cpPivotJointInit(cpPivotJoint *joint, cpBody *a, cpBody *b, cpVect pivot)
    cpConstraint *cpPivotJointNew(cpBody *a, cpBody *b, cpVect pivot)
    cpConstraint *cpPivotJointNew2(cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB)
    p(expl). @a@ and @b@ are the two bodies to connect, and @pivot@ is the point in world coordinates of the pivot. Because the pivot location is given in world coordinates, you must have the bodies moved into the correct positions already. Alternatively you can specify the joint based on a pair of anchor points, but make sure you have the bodies in the right place as the joint will fix itself as soon as you start simulating the space. <%= h 3, "Properties", "Properties" %> * @cpVect cpPivotJointGetanchorA(const cpConstraint *constraint)@ * @void cpPivotJointSetanchorA(cpConstraint *constraint, cpVect value)@ * @cpVect cpPivotJointGetanchorB(const cpConstraint *constraint)@ * @void cpPivotJointSetanchorB(cpConstraint *constraint, cpVect value)@ <%= h 2, "Groove Joint:", "cpGrooveJoint" %>
    cpGrooveJoint *cpGrooveJointAlloc(void)
    
    cpGrooveJoint *cpGrooveJointInit(
    	cpGrooveJoint *joint, cpBody *a, cpBody *b,
    	cpVect groove_a, cpVect groove_b, cpVect anchorB
    )
    
    cpConstraint *cpGrooveJointNew(cpBody *a, cpBody *b, cpVect groove_a, cpVect groove_b, cpVect anchorB)
    p(expl). The groove goes from @groov_a@ to @groove_b@ on body @a@, and the pivot is attached to @anchorB@ on body @b@. All coordinates are body local. <%= h 3, "Properties", "Properties" %> * @cpVect cpGrooveJointGetGrooveA(const cpConstraint *constraint)@ * @void cpGrooveJointSetGrooveA(cpConstraint *constraint, cpVect value)@ * @cpVect cpGrooveJointGetGrooveB(const cpConstraint *constraint)@ * @void cpGrooveJointSetGrooveB(cpConstraint *constraint, cpVect value)@ * @cpVect cpGrooveJointGetanchorB(const cpConstraint *constraint)@ * @void cpGrooveJointSetanchorB(cpConstraint *constraint, cpVect value)@ <%= h 2, "Damped Spring:", "cpDampedSpring" %>
    cpDampedSpring *cpDampedSpringAlloc(void)
    
    cpDampedSpring *cpDampedSpringInit(
    	cpDampedSpring *joint, cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB,
    	cpFloat restLength, cpFloat stiffness, cpFloat damping
    )
    
    cpConstraint *cpDampedSpringNew(
    	cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB,
    	cpFloat restLength, cpFloat stiffness, cpFloat damping
    )
    p(expl). Defined much like a slide joint. @restLength@ is the distance the spring wants to be, @stiffness@ is the spring constant ("Young's modulus":http://en.wikipedia.org/wiki/Young's_modulus), and @damping@ is how soft to make the damping of the spring. <%= h 3, "Properties", "Properties" %> * @cpVect cpDampedSpringGetanchorA(const cpConstraint *constraint)@ * @void cpDampedSpringSetanchorA(cpConstraint *constraint, cpVect value)@ * @cpVect cpDampedSpringGetanchorB(const cpConstraint *constraint)@ * @void cpDampedSpringSetanchorB(cpConstraint *constraint, cpVect value)@ * @cpFloat cpDampedSpringGetRestLength(const cpConstraint *constraint)@ * @void cpDampedSpringSetRestLength(cpConstraint *constraint, cpFloat value)@ * @cpFloat cpDampedSpringGetStiffness(const cpConstraint *constraint)@ * @void cpDampedSpringSetStiffness(cpConstraint *constraint, cpFloat value)@ * @cpFloat cpDampedSpringGetDamping(const cpConstraint *constraint)@ * @void cpDampedSpringSetDamping(cpConstraint *constraint, cpFloat value)@ <%= h 2, "Damped Rotary Spring:", "cpDampedRotarySpring" %>
    cpDampedRotarySpring *cpDampedRotarySpringAlloc(void)
    
    cpDampedRotarySpring *cpDampedRotarySpringInit(
    	cpDampedRotarySpring *joint, cpBody *a, cpBody *b,
    	cpFloat restAngle, cpFloat stiffness, cpFloat damping
    )
    
    cpConstraint *cpDampedRotarySpringNew(cpBody *a, cpBody *b, cpFloat restAngle, cpFloat stiffness, cpFloat damping)
    p(expl). Like a damped spring, but works in an angular fashion. @restAngle@ is the relative angle in radians that the bodies want to have, @stiffness@ and @damping@ work basically the same as on a damped spring. <%= h 3, "Properties", "Properties" %> * @cpFloat cpDampedRotarySpringGetRestAngle(const cpConstraint *constraint)@ * @void cpDampedRotarySpringSetRestAngle(cpConstraint *constraint, cpFloat value)@ * @cpFloat cpDampedRotarySpringGetStiffness(const cpConstraint *constraint)@ * @void cpDampedRotarySpringSetStiffness(cpConstraint *constraint, cpFloat value)@ * @cpFloat cpDampedRotarySpringGetDamping(const cpConstraint *constraint)@ * @void cpDampedRotarySpringSetDamping(cpConstraint *constraint, cpFloat value)@ <%= h 2, "Rotary Limit Joint:", "cpRotaryLimitJoint" %>
    cpRotaryLimitJoint *cpRotaryLimitJointAlloc(void)
    cpRotaryLimitJoint *cpRotaryLimitJointInit(cpRotaryLimitJoint *joint, cpBody *a, cpBody *b, cpFloat min, cpFloat max)
    cpConstraint *cpRotaryLimitJointNew(cpBody *a, cpBody *b, cpFloat min, cpFloat max)
    p(expl). Constrains the relative rotations of two bodies. @min@ and @max@ are the angular limits in radians. It is implemented so that it's possible to for the range to be greater than a full revolution. <%= h 3, "Properties", "Properties" %> * @cpFloat cpRotaryLimitJointGetMin(const cpConstraint *constraint)@ * @void cpRotaryLimitJointSetMin(cpConstraint *constraint, cpFloat value)@ * @cpFloat cpRotaryLimitJointGetMax(const cpConstraint *constraint)@ * @void cpRotaryLimitJointSetMax(cpConstraint *constraint, cpFloat value)@ <%= h 2, "Ratchet Joint:", "cpRatchetJoint" %>
    cpRatchetJoint *cpRatchetJointAlloc(void);
    cpRatchetJoint *cpRatchetJointInit(cpRatchetJoint *joint, cpBody *a, cpBody *b, cpFloat phase, cpFloat ratchet);
    cpConstraint *cpRatchetJointNew(cpBody *a, cpBody *b, cpFloat phase, cpFloat ratchet);
    p(expl). Works like a socket wrench. @ratchet@ is the distance between "clicks", @phase@ is the initial offset to use when deciding where the ratchet angles are. <%= h 3, "Properties", "Properties" %> * @cpFloat cpRatchetJointGetAngle(const cpConstraint *constraint)@ * @void cpRatchetJointSetAngle(cpConstraint *constraint, cpFloat value)@ * @cpFloat cpRatchetJointGetPhase(const cpConstraint *constraint)@ * @void cpRatchetJointSetPhase(cpConstraint *constraint, cpFloat value)@ * @cpFloat cpRatchetJointGetRatchet(const cpConstraint *constraint)@ * @void cpRatchetJointSetRatchet(cpConstraint *constraint, cpFloat value)@ <%= h 2, "Gear Joint:", "cpGearJoint" %>
    cpGearJoint *cpGearJointAlloc(void);
    cpGearJoint *cpGearJointInit(cpGearJoint *joint, cpBody *a, cpBody *b, cpFloat phase, cpFloat ratio);
    cpConstraint *cpGearJointNew(cpBody *a, cpBody *b, cpFloat phase, cpFloat ratio);
    p(expl). Keeps the angular velocity ratio of a pair of bodies constant. @ratio@ is always measured in absolute terms. It is currently not possible to set the ratio in relation to a third body's angular velocity. @phase@ is the initial angular offset of the two bodies. <%= h 3, "Properties", "Properties" %> * @cpFloat cpGearJointGetPhase(const cpConstraint *constraint)@ * @void cpGearJointSetPhase(cpConstraint *constraint, cpFloat value)@ * @cpFloat cpGearJointGetRatio(const cpConstraint *constraint)@ * @void cpGearJointSetRatio(cpConstraint *constraint, cpFloat value)@ <%= h 2, "Simple Motor:", "cpSimpleMotor" %>
    cpSimpleMotor *cpSimpleMotorAlloc(void);
    cpSimpleMotor *cpSimpleMotorInit(cpSimpleMotor *joint, cpBody *a, cpBody *b, cpFloat rate);
    cpConstraint *cpSimpleMotorNew(cpBody *a, cpBody *b, cpFloat rate);
    p(expl). Keeps the relative angular velocity of a pair of bodies constant. @rate@ is the desired relative angular velocity. You will usually want to set an force (torque) maximum for motors as otherwise they will be able to apply a nearly infinite torque to keep the bodies moving. <%= h 3, "Properties", "Properties" %> * @cpFloat cpSimpleMotorGetRate(const cpConstraint *constraint)@ * @void cpSimpleMotorSetRate(cpConstraint *constraint, cpFloat value)@ <%= h 2, "Notes:", "Notes" %> * You can add multiple joints between two bodies, but make sure that they don't fight. Doing so can cause the bodies jitter or spin violently. <%= h 1, "Overview of Collision Detection in Chipmunk:", "CollisionDetection" %> In order to make collision detection in Chipmunk as fast as possible, the process is broken down into several stages. While I've tried to keep it conceptually simple, the implementation can be a bit daunting. Fortunately as a user of the library, you don't need to understand everything about how it works. Though if you are trying to squeeze every bit of performance out of Chipmunk, understanding this section can be helpful. <%= h 2, "Spatial Indexing:", "SpatialIndexing" %> A for loop that checks every object against every other object in the scene would be _very_ slow. So the first stage of the collision detection, commonly called the broadphase, uses a high level spatial algorithm to decide which pairs of objects to check for collisions. Currently Chipmunk supports two spatial indexes, an axis-aligned bounding box tree and a spatial hash. These spatial indexes are able to quickly identify which pairs of shapes are near each other and should be checked for a collision. <%= h 2, "Fast Collision Filtering:", "CollisionsFastFiltering" %> After the spatial index figures out which pairs of shapes are likely to be near each other, it passes each pair back to the space using a callback to perform some additional filtering on the pairs. Before doing anything else, Chipmunk performs a few quick tests to check if shapes should collide. * *Bounding Box Test:* The shapes are not colliding if their bounding boxes are not overlapping. Objects like diagonal line segments can trigger a lot of false positives here, but it's unlikely to be something you should worry about. * *Category Mask Test:* The categories of each shape are bitwise ANDed against the category mask of the other shape. If either result is 0, the shapes do not collide. * *Group Test:* Shapes shouldn't collide with other shapes in the same non-zero group. <%= h 2, "Constraint Based Filtering:", "CollisionsConstraints" %> After fast collision filtering, Chipmunk checks the list of joints on one of the bodies to see if it has a constraint that attaches it to the other body. If that constraint's @collideBodies@ property is false, the collision will be ignored. This check is often very fast since most scenes don't contain a lot of constraints. <%= h 2, "Primitive Shape to Shape Collision Detection:", "PrimitiveTest" %> The most expensive test is to actually check for overlap based on their geometry. Circle to circle and circle to line collisions are very fast. Segment to segment and poly to poly collisions are handled using the GJK/EPA algorithms, and get more expensive as the number of vertexes increases. Simpler shapes make for faster collisions, and often more important, fewer collision points for the solver to run. Chipmunk uses a small dispatch table to figure out which function to use to check if the shapes overlap. Without going into too much detail, the GJK algorithm checks the distance between two objects, and the EPA algorithm checks how much they are overlapping. If you give you segment and poly shapes a small radius when creating them, the EPA algorithm can usually be skipped, speeding up the collision detection considerably. The radius should be at least as big as the amount of allowed collision slop. <%= h 2, "Collision Handler Filtering:", "HandlerFiltering" %> After checking if two shapes overlap Chipmunk will look to see if you have defined a collision handler for the collision types of the shapes. This is vital to process collisions events for the gameplay, but also gives you a very flexible way to filter out collisions. The return value of the @begin()@ and @preSolve()@ callbacks determines whether or not the colliding pair of shapes is discarded or not. Returning true will keep the pair, false will discard it. Rejecting a collision from a @begin()@ callback is permanent, rejecting it from the @preSolve()@ only applies to the step it occured in. If you don't define a handler for the given collision types, Chipmunk will call the space's default handler, which by default is defined to simply accept all collisions. Wildcard collisions can also return a value, but they are handled in a more complicated way. When you create a collision handler between two specific collision types, it's your responsibility to decide when to call the wildcard handlers and what to do with their return values. Otherwise, the default is to call the wildcard handler for the first type, then the second type, and use a logical AND of their return values as filtering value. See @DefaultBegin()@ in @cpSpace.c@ for more information. While using callbacks to filter collisions is the most flexible way, keep in mind that by the time your callback is called all of the most expensive collision detection has already been done. For simulations with a lot of colliding objects each frame, the time spent finding collisions is small compared to the time spent solving the physics for them so it may not be a big deal. Fast collision filtering should be preferred if possible. <%= h 1, "Collision Callbacks:", "CollisionCallbacks" %> A physics library without any events or feedback would not be very useful for games. How would you know when the player bumped into an enemy so that you could take some health points away? How would you know how hard the car hit something so you don't play a loud crash noise when a pebble hits it? What if you need to decide if a collision should be ignored based on specific conditions, like implementing one way platforms? Chipmunk has a number of powerful callback systems that you can use to solve these problems. <%= h 2, "Collision Handlers:", "Handlers" %> A collision handler is a set of 4 function callbacks for the different collision events that Chipmunk recognizes. The event types are: * @begin()@: Two shapes just started touching for the first time this step. Return true from the callback to process the collision normally or false to cause Chipmunk to ignore the collision entirely. If you return false, the @preSolve()@ and @postSolve()@ callbacks will never be run, but you will still recieve a separate event when the shapes stop overlapping. * @preSolve()@: Two shapes are touching during this step. Return false from the callback to make Chipmunk ignore the collision this step or true to process it normally. Additionally, you may override collision values using @cpArbiterSetFriction()@, @cpArbiterSetElasticity()@ or @cpArbiterSetSurfaceVelocity()@ to provide custom friction, elasticity, or surface velocity values. See "cpArbiter":#cpArbiter for more info. * @postSolve()@: Two shapes are touching and their collision response has been processed. You can retrieve the collision impulse or kinetic energy at this time if you want to use it to calculate sound volumes or damage amounts. See "cpArbiter":#cpArbiter for more info. * @separate()@: Two shapes have just stopped touching for the first time this step. To ensure that begin()/separate() are always called in balanced pairs, it will also be called when removing a shape while its in contact with something or when deallocating the space. Collision callbacks are closely associated with "cpArbiter":#cpArbiter structs. You should familiarize yourself with those as well. *Note:* Shapes tagged as sensors (@cpShape.sensor == true@) never generate collisions that get processed, so collisions between sensors shapes and other shapes will never call the @postSolve()@ callback. They still generate @begin()@, and @separate()@ callbacks, and the @preSolve()@ callback is also called every frame even though there is no collision response. *Note #2:* @preSolve()@ callbacks are called before the sleeping algorithm runs. If an object falls asleep, its @postSolve()@ callback won't be called until it's reawoken. <%= h 2, "Collision Handler API:", "HandlerAPI" %>
    typedef int (*cpCollisionBeginFunc)(cpArbiter *arb, struct cpSpace *space, cpDataPointer data)
    typedef int (*cpCollisionPreSolveFunc)(cpArbiter *arb, cpSpace *space, cpDataPointer data)
    typedef void (*cpCollisionPostSolveFunc)(cpArbiter *arb, cpSpace *space, cpDataPointer data)
    typedef void (*cpCollisionSeparateFunc)(cpArbiter *arb, cpSpace *space, cpDataPointer data)
    p(expl). Collision handler function types. While all of them take an arbiter, space, and a user data pointer, only the @begin()@ and @preSolve()@ callbacks return a value. See above for more information.
    
    struct cpCollisionHandler {
    	cpCollisionType typeA, typeB;
    	cpCollisionBeginFunc beginFunc;
    	cpCollisionPreSolveFunc preSolveFunc;
    	cpCollisionPostSolveFunc postSolveFunc;
    	cpCollisionSeparateFunc separateFunc;
    	cpDataPointer userData;
    };
    
    p(expl). This collision handler processes collisions between objects of type @typeA@ and @typeB@. Fill the desired collision callback functions- they are documented above. A user definable context pointer @userData@ is included for your convenience. This pointer is provided as an argument in each callback function.
    cpCollisionHandler *cpSpaceAddCollisionHandler(cpSpace *space, cpCollisionType a, cpCollisionType b)
    p(expl). Add a @cpCollisionHandler@ for specific collision type pair or return the existing handler for the type pair. Whenever shapes with collision types (@cpShape.collision_type@) @a@ and @b@ collide, this handler will be used to process the collision events. When a new collision handler is created, the callbacks will all be set to builtin callbacks that perform the default behavior (call the wildcard handlers, and accept all collisions). <%= pop_open_example "PlaySoundOnCollision" %>
    cpCollisionHandler *cpSpaceAddWildcardHandler(cpSpace *space, cpCollisionType type)
    p(expl). Add a wildcard collision handler for given collision type. This handler will be used any time an object with this type collides with another object, regardless of its type. A good example is a projectile that should be destroyed the first time it hits anything. There may be a specific collision handler and two wildcard handlers. It's up to the specific handler to decide if and when to call the wildcard handlers and what to do with their return values. (See @cpArbiterCallWildcard*()@ below) When a new wildcard handler is created, the callbacks will all be set to builtin callbacks that perform the default behavior. (accept all collisions in @begin()@ and @preSolve()@, or do nothing for @postSolve()@ and @separate()@.
    cpCollisionHandler *cpSpaceAddDefaultCollisionHandler(cpSpace *space)
    p(expl). Return a reference to the default collision handler or that is used to process all collisions that don't have a more specific handler. The default behavior for each of the callbacks is to call the wildcard handlers, ANDing their return values together if applicable. <%= h 2, "Post-Step Callbacks:", "PostStep" %> Post-step callbacks are the one place where you can break the rules about adding or removing objects from within a callback. In fact, their primary function is to help you safely remove objects from the space that you wanted to disable or destroy in a collision or query callback. Post step callbacks are registered as a function and a pointer that is used as a key. You can only register one @postStep()@ callback per key. This prevents you from accidentally removing an object more than once. For instance, say that you get a collision callback between a bullet and object A. You want to destroy both the bullet and object A, so you register a @postStep()@ callback to safely remove them from your game. Then you get a second collision callback between the bullet and object B. You register a @postStep()@ callback to remove object B, and a second @postStep()@ callback to remove the bullet. Because you can only register one callback per key, the @postStep()@ callback for the bullet will only be called once and you can't accidentally try to remove it twice.
    typedef void (*cpPostStepFunc)(cpSpace *space, void *obj, void *data)
    p(expl). Function type used for @postStep()@ callbacks. @space@ is the space the callback was registered on, @obj@ is the pointer value you supplied as the key, and @data@ is a user definable pointer you can use to pass in as a context value.
    cpBool cpSpaceAddPostStepCallback(cpSpace *space, cpPostStepFunc func, void *key, void *data);
    p(expl). Add @func@ to be called before @cpSpaceStep()@ returns. @key@ and @data@ will be passed to your function. Only the first callback registered for any unique value of @key@ will be recorded. It returns @cpTrue@ if the callback is scheduled and @cpfalse@ when the @key@ has already been used. The behavior of adding a @postStep()@ callback from outside of a collision handler or query callback is undefined. *Note:* Post-step callbacks are not run in any particular order. If you need to sequence a number of events, you'll need to put them in a single callback. <%= h 2, "Examples:", "Examples" %> See the "callback examples":examples.html#CollisionCallbacks for more information. <%= h 1, "Chipmunk Collision Pairs: @cpArbiter@", "cpArbiter" %> Chipmunk's @cpArbiter@ struct encapsulates a pair of colliding shapes and all of the data about their collision. cpArbiters are created when a collision starts, and persist until those shapes are no longer colliding. Why are they called arbiters? The short answer is that I kept using the word "arbitrates" to describe the way that collisions were resolved and then I saw that Box2D actually called them arbiters way back in 2006 when I was looking at its solver. An arbiter is like a judge, a person that has authority to settle disputes between two people. It was a fun, fitting name and was shorter to type than CollisionPair which I had been using. It was originally meant to be a private internal structure only, but evolved to be useful from callbacks. <%= h 2, "Memory Management:", "Memory" %> You will never need to create or free an arbiter. More importantly, because they are entirely managed by the space you should *never* store a reference to an arbiter as you don't know when they will be freed or reused. Use them within the callback where they are given to you and then forget about them or copy out the information you need. <%= h 2, "Properties:", "Properties" %>
    cpFloat cpArbiterGetElasticity(const cpArbiter *arb)
    void cpArbiterSetElasticity(cpArbiter *arb, cpFloat value)
    p(expl). The calculated elasticity for this collision pair. Setting the value in a @preSolve()@ callback will override the value calculated by the space. The default calculation multiplies the elasticity of the two shapes together.
    cpFloat cpArbiterGetFriction(const cpArbiter *arb)
    void cpArbiterSetFriction(cpArbiter *arb, cpFloat value)
    p(expl). The calculated friction for this collision pair. Setting the value in a @preSolve()@ callback will override the value calculated by the space. The default calculation multiplies the friction of the two shapes together.
    cpVect cpArbiterGetSurfaceVelocity(const cpArbiter *arb)
    void cpArbiterSetSurfaceVelocity(cpArbiter *arb, cpVect value)
    p(expl). The calculated surface velocity for this collision pair. Setting the value in a @preSolve()@ callback will override the value calculated by the space. the default calculation subtracts the surface velocity of the second shape from the first and then projects that onto the tangent of the collision. This is so that only friction is affected by default calculation. Using a custom calculation, you can make something that responds like a pinball bumper, or where the surface velocity is dependent on the location of the contact point.
    cpDataPointer cpArbiterGetUserData(const cpArbiter *arb)
    void cpArbiterSetUserData(cpArbiter *arb, cpDataPointer data)
    p(expl). A user definable context pointer. The value will persist until just after the @separate()@ callback is called for the pair. *NOTE:* If you need to clean up this pointer, you should implement the @separate()@ callback to do it. Also be careful when destroying the space as there may be active collisions still. In order to trigger the @separate()@ callbacks and clean up your data, you'll need to remove all the shapes from the space before disposing of it. This is something I'd suggest doing anyway. See ChipmunkDemo.c:ChipmunkDemoFreeSpaceChildren() for an example of how to do it easily.
    int cpArbiterGetCount(const cpArbiter *arb)
    cpVect cpArbiterGetNormal(const cpArbiter *arb, int i)
    cpVect cpArbiterGetPoint(const cpArbiter *arb, int i)
    cpFloat cpArbiterGetDepth(const cpArbiter *arb, int i)
    p(expl). Get the number of contacts tracked by this arbiter or the specific collision point, collision normal or penetration depth of a collision point. For the forseeable future, the maximum number of contacts will be two.
    cpBool cpArbiterIsFirstContact(const cpArbiter *arb)
    p(expl). Returns true if this is the first step the two shapes started touching. This can be useful for sound effects for instance. If its the first frame for a certain collision, check the energy of the collision in a @postStep()@ callbock and use that to determine the volume of a sound effect to play.
    cpBool cpArbiterIsRemoval(const cpArbiter *arb)
    p(expl). Returns @cpTrue@ during a @separate()@ callback if the callback was invoked due to an object removal.
    void cpArbiterGetShapes(const cpArbiter *arb, cpShape **a, cpShape **b)
    void cpArbiterGetBodies(const cpArbiter *arb, cpBody **a, cpBody **b)
    p(expl). Get the shapes or bodies in the order that they were defined in the collision handler associated with this arbiter. If you defined the handler as @cpSpaceAddCollisionHandler(space, 1, 2, ...)@, you you will find that @a->collision_type == 1@ and @b->collision_type == 2@.
    cpBool cpArbiterCallWildcardBeginA(cpArbiter *arb, cpSpace *space)
    cpBool cpArbiterCallWildcardBeginB(cpArbiter *arb, cpSpace *space)
    cpBool cpArbiterCallWildcardPreSolveA(cpArbiter *arb, cpSpace *space)
    cpBool cpArbiterCallWildcardPreSolveB(cpArbiter *arb, cpSpace *space)
    void cpArbiterCallWildcardPostSolveA(cpArbiter *arb, cpSpace *space)
    void cpArbiterCallWildcardPostSolveB(cpArbiter *arb, cpSpace *space)
    void cpArbiterCallWildcardSeparateA(cpArbiter *arb, cpSpace *space)
    void cpArbiterCallWildcardSeparateB(cpArbiter *arb, cpSpace *space)
    p(expl). These functions invoke the wildcard handlers for a given collision. For custom collision handlers between specific types or overriding the default handler, you must decide how to invoke the wildcard handlers since it may be important to call the wildcards first, last, or possibly skip them entirely. For the @begin()@ and @preSolve()@ callbacks, you also need to decide what to do with their return values since they may not agree with each other or the specific handler they were called from. Every collision handler is defined for two types, the "A" variants of these functions call the wildcard handler for the first type, and the "B" variants call the handler for the second type. <%= pop_open_example "CollisionCallback" %> <%= h 2, "Contact Point Sets:", "HelperFunctions" %> Contact point sets make getting contact information simpler.
    cpContactPointSet cpArbiterGetContactPointSet(const cpArbiter *arb)
    p(expl). Get a contact point set struct from an arbiter. You might do something like the following to get and process a contact point set:
    cpContactPointSet set = cpArbiterGetContactPointSet(arbiter);
    for(int i=0; i
    void cpArbiterSetContactPointSet(cpArbiter *arb, cpContactPointSet *set)
    p(expl). Replace the contact point set of an Arbiter. You cannot change the number of contacts, but can change the location, normal or penetration distance. The "Sticky" demo uses this to allow objects to overlap an extra amount. You could also use it in a Pong style game to modify the normal of the collision based on the x-position of the collision even though the paddle is a flat shape. <%= h 2, "Helper Functions:", "HelperFunctions" %>
    void cpArbiterGetShapes(cpArbiter *arb, cpShape **a, cpShape **b)
    void cpArbiterGetBodies(const cpArbiter *arb, cpBody **a, cpBody **b)
    p(expl). Get the shapes (or their bodies) in the order that they were defined in the collision handler associated with this arbiter. If you defined the handler as @cpSpaceAddCollisionHandler(space, 1, 2, ...)@, you you will find that @a->collision_type == 1@ and @b->collision_type == 2@. The convenience macro defines and initializes the two shape variables for you. The default collision handler doesn't use collision types so the order is undefined.
    #define CP_ARBITER_GET_SHAPES(arb, a, b) cpShape *a, *b; cpArbiterGetShapes(arb, &a, &b)
    #define CP_ARBITER_GET_BODIES(arb, a, b) cpBody *a, *b; cpArbiterGetBodies(arb, &a, &b);
    p(expl). Shortcut macros for defining variables for and retrieving the shapes/bodies for an arbiter.
    cpVect cpArbiterTotalImpulseWithFriction(cpArbiter *arb);
    cpVect cpArbiterTotalImpulse(cpArbiter *arb);
    p(expl). Returns the impulse that was applied this step to resolve the collision. These functions should only be called from a @postStep()@ or @cpBodyEachArbiter()@ callback, otherwise the result is undefined. If in doubt which function to use, use @cpArbiterTotalImpulseWithFriction()@.
    cpFloat cpArbiterTotalKE(const cpArbiter *arb);
    p(expl). Calculate the amount of energy lost in a collision including static, but not dynamic friction. This function should only be called from a @postSolve()@, @postStep()@ or @cpBodyEachArbiter()@ callback. <%= h 1, "Queries:", "Queries" %> Chipmunk spaces support four kinds of spatial queries, nearest point, segment, shape and fast bounding box queries. Any type can be performed efficiently against an entire space, and point and segment queries can be performed against individual shapes. All types of queries take a collision group and layer that are used to filter matches out using the same rules used for filtering collisions between shapes. See "cpShape":#cpShape for more information. If you don't want to filter out any matches, use @CP_ALL_LAYERS@ for the layers and @CP_NO_GROUP@ as the group. <%= h 2, "Nearest Point Queries:", "PointQueries" %> Point queries are useful for things like mouse picking and simple sensors. They allow you to check if there are shapes within a certain distance of a point, find the closest point on a shape to a given point or find the closest shape to a point.
    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;
    p(expl). Nearest point queries return the point on the surface of the shape as well as the distance from the query point to the surface point.
    cpFloat cpShapeNearestPointQuery(cpShape *shape, cpVect p, cpPointQueryInfo *out)
    p(expl). Find the distance from @point@ to @shape@. If the point is inside of the shape, the distance will be negative and equal to the depth of the point.
    typedef void (*cpSpaceNearestPointQueryFunc)(cpShape *shape, cpFloat distance, cpVect point, void *data);
    
    void cpSpacePointQuery(
    	cpSpace *space, cpVect point, cpFloat maxDistance,
    	cpShapeFilter filter,
    	cpSpaceNearestPointQueryFunc func, void *data
    )
    p(expl). Query @space@ at @point@ for shapes within the given distance range. The @filter@ is applied to the query and follows the same rules as the collision detection. @func@ is called for each shape found along with the distance to the closest point on the shape's surface, the distance to that point and the @data@ argument passed to @cpSpaceNearestPointQuery()@. Sensor shapes are included. If a @maxDistance@ of @0.0@ is used, the point must lie inside a shape. Negative @maxDistance@ is also allowed meaning that the point must be a under a certain depth within a shape to be considered a match.
    cpShape *cpSpacePointQueryNearest(cpSpace *space, cpVect point, cpFloat maxDistance, cpShapeFilter filter, cpPointQueryInfo *out)
    p(expl). Query @space@ at @point@ and return the closest shape within @maxDistance@ units of distance. @out@ is an optional pointer to a @cpPointQueryInfo@ if you want additional information about the match. <%= h 2, "Segment Queries:", "SegmentQueries" %> Segment queries are like ray casting, but because not all spatial indexes allow processing infinitely long ray queries it is limited to segments. In practice this is still very fast and you don't need to worry too much about the performance as long as you aren't using extremely long segments for your queries.
    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;
    p(expl). Segment queries return more information than just a simple yes or no, they also return where a shape was hit and its surface normal at the hit point. @t@ is the percentage between the query start and end points. If you need the hit point in world space or the absolute distance from start, see the segment query helper functions farther down. If a segment query starts within a shape it will have @t = 0@ and @n = cpvzero@.
    cpBool cpShapeSegmentQuery(cpShape *shape, cpVect a, cpVect b, cpFloat radius, cpSegmentQueryInfo *info)
    p(expl). Perform a segment query from @a@ to @b@ with the given @radius@ against a single shape @shape@. @info@ must be a valid pointer to a @cpSegmentQueryInfo@ structure which will be initialized with the raycast info.
    typedef void (*cpSpaceSegmentQueryFunc)(cpShape *shape, cpFloat t, cpVect n, void *data)
    
    void cpSpaceSegmentQuery(
    	cpSpace *space, cpVect start, cpVect end, cpFloat radius,
    	cpShapeFilter filter,
    	cpSpaceSegmentQueryFunc func, void *data
    )
    p(expl). Query @space@ along the line segment from @start@ to @end@ with the given @radius@. The @filter@ is applied to the query and follows the same rules as the collision detection. @func@ is called with the normalized distance along the line and surface normal for each shape found along with the @data@ argument passed to @cpSpacePointQuery()@. Sensor shapes are included.
    cpShape *cpSpaceSegmentQueryFirst(
    	cpSpace *space, cpVect start, cpVect end, cpFloat radius,
    	cpShapeFilter filter,
    	cpSegmentQueryInfo *info
    )
    p(expl). Query @space@ along the line segment from @start@ to @end@ with the given @radius@. The @filter@ is applied to the query and follows the same rules as the collision detection. Only the first shape encountered is returned and the search is short circuited. Returns @NULL@ if no shape was found. The info struct pointed to by @info@ will be initialized with the raycast info unless @info@ is NULL. Sensor shapes are ignored. <%= h 2, "AABB Queries:", "AABBQueries" %> AABB queries give you a fast way to check roughly which shapes are in an area.
    typedef void (*cpSpaceBBQueryFunc)(cpShape *shape, void *data)
    
    void cpSpaceBBQuery(
    	cpSpace *space, cpBB bb,
    	cpShapeFilter filter,
    	cpSpaceBBQueryFunc func, void *data
    )
    p(expl). Query @space@ to find all shapes near @bb@. The @filter@ is applied to the query and follows the same rules as the collision detection. @func@ is called for each shape whose bounding box overlaps @bb@ along with the @data@ argument passed to @cpSpaceBBQuery()@. Sensor shapes are included. <%= h 2, "Shape Queries:", "ShapeQueries" %> Shape queries allow you to check if shapes in a space are overlapping a specific area. You can use this to check if an object already exists at a location you want to add another shape, or to use as sensor queries for AI. You can either create a body/shape pair before querying, or you can create a shape passing @NULL@ for the body and position the shape using @cpShapeUpdate()@ to set the position and rotation of the shape.
    typedef void (*cpSpaceShapeQueryFunc)(cpShape *shape, cpContactPointSet *points, void *data);
    
    cpBool cpSpaceShapeQuery(cpSpace *space, cpShape *shape, cpSpaceShapeQueryFunc func, void *data);
    p(expl). Query @space@ to find all shapes overlapping @shape@. @func@ is called for each overlapping shape along with a pointer to a temporary cpContactPointSet and the @data@ argument passed to @cpSpaceBBQuery()@. Sensor shapes are included. <%= h 2, "Blocks:", "Blocks" %> If your compiler supports blocks (such as Clang), there are an alternate set of functions you can call. @cpSpaceNearestPointQuery_b()@, etc. See @chipmunk.h@ for more information. <%= h 2, "Examples:", "Examples" %> See the "query examples":examples.html#Query for more information. Chipmunk2D-Chipmunk-7.0.3/doc-src/doxygen_generator.rb000066400000000000000000000174401347650476100226660ustar00rootroot00000000000000OUTPUT_NAME = "generated_docs" exit if File.exists?(OUTPUT_NAME) DOC_FILE = open(OUTPUT_NAME, "w") FUNCS = {} IO.readlines("|ruby extract_protos.rb").each{|line| func = eval(line) FUNCS[func[:name]] = func } SKIP = Object.new GETTER_DOCS = { "cpArbiterGetBodies" => SKIP, "cpArbiterGetContactPointSet" => SKIP, "cpArbiterGetCount" => SKIP, "cpArbiterGetDepth" => SKIP, "cpArbiterGetElasticity" => "Get the elasticity for this cpArbiter", "cpArbiterGetFriction" => "Get the friction for this cpArbiter", "cpArbiterGetNormal" => SKIP, "cpArbiterGetPoint" => SKIP, "cpArbiterGetShapes" => SKIP, "cpArbiterGetSurfaceVelocity" => "Get the surface velocity used by the contact calculation for this cpArbiter", "cpBodyGetAngVel" => "Get the angular velocity of this cpBody", "cpBodyGetAngVelLimit" => "Get the angular velocity limit of this cpBody", "cpBodyGetAngle" => "Get the angle of this cpBody", "cpBodyGetForce" => "Get the force applied to this cpBody", "cpBodyGetMass" => "Get the mass of this cpBody", "cpBodyGetMoment" => "Get the moment of inertia of this cpBody", "cpBodyGetPos" => "Get the position of this cpBody", "cpBodyGetRot" => "Get the rotation vector of this cpBody", "cpBodyGetTorque" => "Get the torque applied to this cpBody", "cpBodyGetUserData" => "Get the userdata pointer for this cpBody", "cpBodyGetVel" => "Get the velocity of this cpBody", "cpBodyGetVelLimit" => "Get the velocity limit of this cpBody", "cpCircleShapeGetOffset" => "Get the offset of this cpCircleShape", "cpCircleShapeGetRadius" => "Get the radius of this cpCircleShape", "cpConstraintGetA" => "Get the first of the two bodies this cpConstraint is connected to.", "cpConstraintGetB" => "Get the second of the two bodies this cpConstraint is connected to.", "cpConstraintGetErrorBias" => "Get the percentage of constraint error that remains unfixed after each second.", "cpConstraintGetImpulse" => SKIP, "cpConstraintGetMaxBias" => "Get the maximum rate this cpConstraint can apply to correct itself at.", "cpConstraintGetMaxForce" => "Get the maximum force this cpConstraint can apply to correct itself.", "cpConstraintGetPostSolveFunc" => "Get the function callback that is called each step after the solver runs.", "cpConstraintGetPreSolveFunc" => "Get the function callback that is called each step before the solver runs.", "cpConstraintGetUserData" => "Get the user data pointer for this cpConstraint.", "cpDampedRotarySpringGetDamping" => "Get the damping of this cpDampedRotarySpring.", "cpDampedRotarySpringGetRestAngle" => "Get the restangle of this cpDampedRotarySpring.", "cpDampedRotarySpringGetSpringTorqueFunc" => "Get the springtorquefunc of this cpDampedRotarySpring.", "cpDampedRotarySpringGetStiffness" => "Get the stiffness of this cpDampedRotarySpring.", "cpDampedSpringGetAnchr1" => "Get the anchr1 of this cpDampedSpring.", "cpDampedSpringGetAnchr2" => "Get the anchr2 of this cpDampedSpring.", "cpDampedSpringGetDamping" => "Get the damping of this cpDampedSpring.", "cpDampedSpringGetRestLength" => "Get the rest length of this cpDampedSpring.", "cpDampedSpringGetSpringForceFunc" => "Get the spring force callback function of this cpDampedSpring.", "cpDampedSpringGetStiffness" => "Get the stiffness of this cpDampedSpring.", "cpGearJointGetPhase" => "Get the phase of this cpGearJoint.", "cpGearJointGetRatio" => "Get the ratio of this cpGearJoint.", "cpGrooveJointGetAnchr2" => "Get the anchr2 of this cpGrooveJoint.", "cpGrooveJointGetGrooveA" => "Get the groovea of this cpGrooveJoint.", "cpGrooveJointGetGrooveB" => "Get the grooveb of this cpGrooveJoint.", "cpPinJointGetAnchr1" => "Get the anchr1 of this cpPinJoint.", "cpPinJointGetAnchr2" => "Get the anchr2 of this cpPinJoint.", "cpPinJointGetDist" => "Get the dist between the anchor points of this cpPinJoint.", "cpPivotJointGetAnchr1" => "Get the anchr1 of this cpPivotJoint.", "cpPivotJointGetAnchr2" => "Get the anchr2 of this cpPivotJoint.", "cpPolyShapeGetNumVerts" => SKIP, "cpPolyShapeGetVert" => SKIP, "cpRatchetJointGetAngle" => "Get the angle of this cpRatchetJoint.", "cpRatchetJointGetPhase" => "Get the phase of this cpRatchetJoint.", "cpRatchetJointGetRatchet" => "Get the ratchet angular distance of this cpRatchetJoint.", "cpRotaryLimitJointGetMax" => "Get the max delta angle of this cpRotaryLimitJoint.", "cpRotaryLimitJointGetMin" => "Get the min delta angle of this cpRotaryLimitJoint.", "cpSegmentShapeGetA" => "Get the first endpoint of this cpSegmentShape.", "cpSegmentShapeGetB" => "Get the second endpoint of this cpSegmentShape.", "cpSegmentShapeGetNormal" => "Get the normal of this cpSegmentShape.", "cpSegmentShapeGetRadius" => "Get the radius of this cpSegmentShape.", "cpShapeGetBB" => "Get the bounding box of this cpShape.", "cpShapeGetBody" => "Get the body this cpShape is attached to.", "cpShapeGetCollisionType" => "Get the collision type of this cpShape.", "cpShapeGetElasticity" => "Get the elasticity of this cpShape.", "cpShapeGetFriction" => "Get the friction of this cpShape.", "cpShapeGetGroup" => "Get the group of this cpShape.", "cpShapeGetLayers" => "Get the layer bitmask of this cpShape.", "cpShapeGetSensor" => "Get the sensor flag of this cpShape.", "cpShapeGetSurfaceVelocity" => "Get the surface velocity of this cpShape.", "cpShapeGetUserData" => "Get the user data pointer of this cpShape.", "cpSimpleMotorGetRate" => "Get the rate of this cpSimpleMotor.", "cpSlideJointGetAnchr1" => "Get the anchr1 of this cpSlideJoint.", "cpSlideJointGetAnchr2" => "Get the anchr2 of this cpSlideJoint.", "cpSlideJointGetMax" => "Get the max distance between the anchors of this cpSlideJoint.", "cpSlideJointGetMin" => "Get the min distance between the anchors of this cpSlideJoint.", "cpSpaceGetCollisionBias" => "Get the collision bias of this cpSpace.", "cpSpaceGetCollisionPersistence" => "Get the collision persistence of this cpSpace.", "cpSpaceGetCollisionSlop" => "Get the collision slop of this cpSpace.", "cpSpaceGetCurrentTimeStep" => "Get the most recent timestep used with this cpSpace.", "cpSpaceGetDamping" => "Get the damping of this cpSpace.", "cpSpaceGetEnableContactGraph" => "Get the enable contact graph flag of this cpSpace.", "cpSpaceGetGravity" => "Get the gravity of this cpSpace.", "cpSpaceGetIdleSpeedThreshold" => "Get the idle speed threshold of this cpSpace.", "cpSpaceGetIterations" => "Get the number of solver iterations of this cpSpace.", "cpSpaceGetSleepTimeThreshold" => "Get the sleep time threshold of this cpSpace.", "cpSpaceGetStaticBody" => "Get the static body of this cpSpace.", "cpSpaceGetUserData" => "Get the user data pointer of this cpSpace.", } def output_getter(func) name = func[:name] doc = GETTER_DOCS[name] return if doc == SKIP struct, property = */(cp\w*)Get(.+)/.match(name).captures if doc prototype = "#{func[:inline] ? "static inline " : ""}#{func[:return]} #{name}(#{func[:args]})" DOC_FILE.puts <<-EOF /// @addtogroup #{struct} /// @{ /// @fn #{prototype}; /// @brief #{doc} #{prototype}; /// @} EOF else puts %{\t"#{name}" => "Get the #{property.downcase} of this #{struct}.",} end end def output_setter(func) name = func[:name] doc = GETTER_DOCS[name.gsub("Set", "Get")] return if doc == SKIP struct, property = */(cp\w*)Set(.+)/.match(name).captures if doc prototype = "static inline void #{name}(#{func[:args]})" DOC_FILE.puts <<-EOF /// @addtogroup #{struct} /// @{ /// @fn #{prototype}; /// @brief #{doc.gsub("Get", "Set")} #{prototype}; /// @}" EOF else puts %{\t"#{name}" => "Set the #{property.downcase} of this #{struct}.",} end end getters = FUNCS.keys.find_all{|name| /(cp\w*)Get(.+)/.match(name)}.sort FUNCS.values_at(*getters).each{|func| output_getter(func)} setters = FUNCS.keys.find_all{|name| /(cp\w*)Set(.+)/.match(name)}.sort FUNCS.values_at(*setters).each{|func| output_setter(func)} Chipmunk2D-Chipmunk-7.0.3/doc-src/doxygen_main000066400000000000000000000045341347650476100212220ustar00rootroot00000000000000/** @mainpage Chipmunk2D and Chipmunk2D Pro API reference.

    What is Chipmunk2D?

    First of all, Chipmunk2D is a 2D rigid body physics library distributed under the MIT license. It is intended to be fast, portable, numerically stable, and easy to use. For this reason it's been used in hundreds of games across every recent system you can name. I've put thousands of hours of work over many years to make Chipmunk2D what it is today. I hope you enjoy working with it! Chipmunk2D Pro builds on what we've made with C Chipmunk2D and extended it with more features, platform specific performance tuning, and new features. Currently we offer a Objective-C binding, ARM NEON optimizations for mobile CPUs, and a very high performance and flexible automatic geometry library. Objective-Chipmunk for iPhone/Mac provides extended APIs and a first rate Objective-C wrapper with support for properties, blocks and ARC (automatic reference counting) to make Chipmunk2D coding more efficient with your time on Apple platforms. The ARM optimizations use the NEON SIMD coprocessor found in any smartphone using an ARM Cortex A* CPU including all iOS hardware since the 3GS and many Android phones. The auto geometry library can generate geometry from bitmap images or procedural data. You can use it for generating convex hull shapes from sprites, or create an entire level's collision shapes from a background image. It can also be easily set up to be used for high-performance deformable terrain or infinite procedural terrain. If you'd like to hear more about Chipmunk2D Pro or download a trial library check out the Chipmunk2D Pro page.

    Where do I start?

    You might want to start by looking at some sample code. From there, you probably want to skim over the latest documentation. While geared towards C developers, it introduces all the important building blocks. The Objective-C API simply takes things to the next logical step.

    Note:

    These Doxygen docs cover both the C Chipmunk2D API as well as Objective-Chipmunk. The Objective-Chipmunk classes (Chipmunk*) are not available except with Chipmunk2D Pro. */ Chipmunk2D-Chipmunk-7.0.3/doc-src/extract_protos.rb000066400000000000000000000012611347650476100222150ustar00rootroot00000000000000# match 0 is the whole function proto # match 1 is either "static inline " or nil # match 2 is the return type # match 3 is the function symbol name # match 4 is the arguments PATTERN = /.*?((static inline )?(\w*\*?)\s(cp\w*)\((.*?)\))/ IO.readlines("|gcc -DNDEBUG -E ../include/chipmunk/chipmunk.h").each do|line| str = line while match = PATTERN.match(str) str = match.post_match proto, inline, ret, name, args = match.captures.values_at(0, 1, 2, 3, 4) next if ret == "return" || ret == "" inline = !!inline p({:inline => inline, :return => ret, :name => name, :args => args}) # puts "#{name} - #{inline ? "static inline " : ""}#{ret} #{name}(#{args})" end end Chipmunk2D-Chipmunk-7.0.3/doc-src/upload_docs.sh000066400000000000000000000001511347650476100214350ustar00rootroot00000000000000rsync -r --exclude=".*" ../ slembcke.net:files.slembcke.net/chipmunk/release/Chipmunk-5.x/Chipmunk-Docs/ Chipmunk2D-Chipmunk-7.0.3/doc/000077500000000000000000000000001347650476100160235ustar00rootroot00000000000000Chipmunk2D-Chipmunk-7.0.3/doc/examples.html000066400000000000000000000212061347650476100205300ustar00rootroot00000000000000 Chipmunk Game Dynamics Documentation

    Example Code Snippets:

    Getting a Transformation from a Rigid Body:

    You can quickly and easily build a transformation matrix from a Chipmunk body. The following code is for OpenGL, but it should be trivial to modify for DirectX or affine transforms. (Note that OpenGL matrices are column-major)

    cpVect pos = body->p;
    cpVect rot = body->rot;
    
    GLFloat matrix[16] = {
       rot.x, rot.y, 0.0f, 0.0f,
      -rot.y, rot.x, 0.0f, 0.0f,
       0.0f,   0.0f, 1.0f, 0.0f,
       pos.x, pos.y, 0.0f, 1.0f,
    };
    
    glMultMatrixf(matrix.farr);
    

    Collision Callbacks:

    This snippet demonstrates several Chipmunk collision callback features. It defines a collision handler that is called when collision shapes start touching and also a post-step callback to remove the collision shape and body.

    static void
    postStepRemove(cpSpace *space, cpShape *shape, void *unused)
    {
      cpSpaceRemoveBody(space, shape->body);
      cpSpaceRemoveShape(space, shape);
      
      cpShapeFree(shape);
      cpBodyFree(shape->body);
    }
    
    static int
    begin(cpArbiter *arb, cpSpace *space, void *unused)
    {
      // Get the cpShapes involved in the collision
      // The order will be the same as you defined in the handler definition
      // a->collision_type will be BULLET_TYPE and b->collision_type will be MONSTER_TYPE
      cpShape *a, *b; cpArbiterGetShapes(arb, &a, &b);
      
      // Alternatively you can use the CP_ARBITER_GET_SHAPES() macro
      // It defines and sets the variables for you.
      //CP_ARBITER_GET_SHAPES(arb, a, b);
      
      // Add a post step callback to safely remove the body and shape from the space.
      // Calling cpSpaceRemove*() directly from a collision handler callback can cause crashes.
      cpSpaceAddPostStepCallback(space, (cpPostStepFunc)postStepRemove, b, NULL);
      
      // The object is dead, don’t process the collision further
      return 0;
    }
    
    #define BULLET_TYPE 1
    #define MONSTER_TYPE 2
    
    // Define a collision handler for bullets and monsters
    // Kill the monster by removing it’s shape and body from the space as soon as it’s hit by a bullet 
    cpSpaceAddCollisionHandler(space, BULLET_TYPE, MONSTER_TYPE, begin, NULL, NULL, NULL, NULL);

    For more callback examples, see the One Way Platform Demo, Sensors Demo, or the Player Demo.

    Query Examples:

    The following example is taken directly from ChipmunkDemo.c. When the mouse is clicked, a point query is performed to see if there is a shape under the mouse. If there is, it adds a joint to the body that links it to the mouse's movement.

    static void
    click(int button, int state, int x, int y)
    {
      if(button == GLUT_LEFT_BUTTON){
        if(state == GLUT_DOWN){
          cpVect point = mouseToSpace(x, y);
        
          cpShape *shape = cpSpacePointQueryFirst(space, point, GRABABLE_MASK_BIT, 0);
          if(shape){
            cpBody *body = shape->body;
            mouseJoint = cpPivotJointNew2(mouseBody, body, cpvzero, cpBodyWorld2Local(body, point));
            mouseJoint->maxForce = 50000.0f;
            mouseJoint->biasCoef = 0.15f;
            cpSpaceAddConstraint(space, mouseJoint);
          }
        } else if(mouseJoint){
          cpSpaceRemoveConstraint(space, mouseJoint);
          cpConstraintFree(mouseJoint);
          mouseJoint = NULL;
        }
      }
    }

    Perform a segment query to see if a laser beam hits a shape. We want to draw particles at both the position where the beam enters and exits the shape.

    cpVect a = cpv(...), b = cpv(...);
    
    cpSegmentQueryInfo info = {};
    if(cpSpaceSegmentQueryFirst(space, a, b, -1, 0, &info)){
      cpSegmentQueryInfo info2;
      cpShapeSegmentQuery(info.shape, b, a, &info2);
      
      cpVect enterPoint = cpSegmentQueryHitPoint(a, b, info);
      cpVect exitPoint = cpSegmentQueryHitPoint(b, a, info2);
    }
    Chipmunk2D-Chipmunk-7.0.3/doc/examples/000077500000000000000000000000001347650476100176415ustar00rootroot00000000000000Chipmunk2D-Chipmunk-7.0.3/doc/examples/BreakableJoint.html000066400000000000000000000034611347650476100234070ustar00rootroot00000000000000
    // Create the joint and set it's max force property.
    breakableJoint = cpSpaceAddConstraint(space, cpPinJointNew(body1, body2, cpv(15,0), cpv(-15,0)));
    cpConstraintSetMaxForce(breakableJoint, 4000);
    
    
    // In your update function:
    // Step your space normally...
    cpFloat dt = 1.0/60.0;
    cpSpaceStep(space, dt);
    
    if(breakableJoint){
      // Convert the impulse to a force by dividing it by the timestep.
      cpFloat force = cpConstraintGetImpulse(breakableJoint)/dt;
      cpFloat maxForce = cpConstraintGetMaxForce(breakableJoint);
    
      // If the force is almost as big as the joint's max force, break it.
      if(force > 0.9*maxForce){
        cpSpaceRemoveConstraint(space, breakableJoint);
        breakableJoint = NULL;
      }
    }
    
    Chipmunk2D-Chipmunk-7.0.3/doc/examples/CollisionCallback.html000066400000000000000000000060001347650476100240730ustar00rootroot00000000000000
    static void
    postStepRemove(cpSpace *space, cpShape *shape, void *unused)
    {
      cpSpaceRemoveShape(space, shape);
      cpSpaceRemoveBody(space, shape->body);
      
      cpShapeFree(shape);
      cpBodyFree(shape->body);
    }

    static int
    begin(cpArbiter *arb, cpSpace *space, void *data)
    {
      // Get the cpShapes involved in the collision
      // The order will be the same as you defined in the handler definition
      // a->collision_type will be BULLET_TYPE and b->collision_type will be MONSTER_TYPE
      CP_ARBITER_GET_SHAPES(arb, a, b);
      
      // The macro expands exactly as if you had typed this:
      // cpShape *a, *b; cpArbiterGetShapes(arb, &a, &b);
      
      // Add a post step callback to safely remove the body and shape from the space.
      // Calling cpSpaceRemove*() directly from a collision handler callback can cause crashes.
      cpSpaceAddPostStepCallback(space, (cpPostStepFunc)postStepRemove, b, NULL);
      
      // The object is dead, don’t process the collision further
      return 0;
    }

    #define BULLET_TYPE 1
    #define MONSTER_TYPE
    2

    // Define a collision handler for bullets and monsters
    // Kill the monster by removing it’s shape and body from the space as soon as it’s hit by a bullet
    cpCollisionHandler *handler = cpSpaceAddCollisionHandler(space, BULLET_TYPE, MONSTER_TYPE);
    handler->beginFunc = begin;

    Chipmunk2D-Chipmunk-7.0.3/doc/examples/Crushing.html000066400000000000000000000030251347650476100223110ustar00rootroot00000000000000
    struct CrushingContext {
      cpFloat magnitudeSum;
      cpVect vectorSum;
    };
    
    static void
    EstimateCrushingHelper(cpBody *body, cpArbiter *arb, struct CrushingContext *context)
    {
      cpVect j = cpArbiterTotalImpulseWithFriction(arb);
      context->magnitudeSum += cpvlength(j);
      context->vectorSum = cpvadd(context->vectorSum, j);
    }
    
    cpFloat
    EstimateCrushForce(cpBody *body, cpFloat dt)
    {
      struct CrushingContext crush = {0.0f, cpvzero};
      cpBodyEachArbiter(body, (cpBodyArbiterIteratorFunc)EstimateCrushingHelper, &crush);
      
      // Compare the vector sum magnitude and magnitude sum to see if
      // how much the collision forces oppose one another.
      cpFloat crushForce = (crush.magnitudeSum - cpvlength(crush.vectorSum))*dt;
    }
    Chipmunk2D-Chipmunk-7.0.3/doc/examples/DynamicStatic.html000066400000000000000000000031341347650476100232640ustar00rootroot00000000000000
    // This example is pulled from the Plink demo.
    if(ChipmunkDemoRightDown){
      // Find the shape under the mouse.
      cpShape *nearest = cpSpaceNearestPointQueryNearest(space, ChipmunkDemoMouse, 0.0, GRABABLE_MASK_BIT, CP_NO_GROUP, NULL);
      if(nearest){
        cpBody *body = cpShapeGetBody(nearest);
        if(cpBodyIsStatic(body)){
          // If the body is static, convert it to dynamic and add it to the space.
          cpSpaceConvertBodyToDynamic(space, body, pentagon_mass, pentagon_moment);
          cpSpaceAddBody(space, body);
        } else {
          // If the body is dynamic, remove it from the space and convert it to static.
          cpSpaceRemoveBody(space, body);
          cpSpaceConvertBodyToStatic(space, body);
        }
      }
    }
    
    Chipmunk2D-Chipmunk-7.0.3/doc/examples/Hello Chipmunk.html000066400000000000000000000147461347650476100233450ustar00rootroot00000000000000
    #include <stdio.h>
    #include
    <chipmunk.h>

    int main(void){
      // cpVect is a 2D vector and cpv() is a shortcut for initializing them.
      cpVect gravity = cpv(0, -100);
      
      // Create an empty space.
      cpSpace *space = cpSpaceNew();
      cpSpaceSetGravity(space, gravity);
      
      // Add a static line segment shape for the ground.
      // We'll make it slightly tilted so the ball will roll off.
      // We attach it to a static body to tell Chipmunk it shouldn't be movable.
      cpShape *ground = cpSegmentShapeNew(cpSpaceGetStaticBody(space), cpv(-20, 5), cpv(20, -5), 0);
      cpShapeSetFriction(ground, 1);
      cpSpaceAddShape(space, ground);
      
      // Now let's make a ball that falls onto the line and rolls off.
      // First we need to make a cpBody to hold the physical properties of the object.
      // These include the mass, position, velocity, angle, etc. of the object.
      // Then we attach collision shapes to the cpBody to give it a size and shape.
      
      cpFloat radius = 5;
      cpFloat mass = 1;
      
      // The moment of inertia is like mass for rotation
      // Use the cpMomentFor*() functions to help you approximate it.
      cpFloat moment = cpMomentForCircle(mass, 0, radius, cpvzero);
      
      // The cpSpaceAdd*() functions return the thing that you are adding.
      // It's convenient to create and add an object in one line.
      cpBody *ballBody = cpSpaceAddBody(space, cpBodyNew(mass, moment));
      cpBodySetPosition(ballBody, cpv(0, 15));
      
      // Now we create the collision shape for the ball.
      // You can create multiple collision shapes that point to the same body.
      // They will all be attached to the body and move around to follow it.
      cpShape *ballShape = cpSpaceAddShape(space, cpCircleShapeNew(ballBody, radius, cpvzero));
      cpShapeSetFriction(ballShape, 0.7);
      
      // Now that it's all set up, we simulate all the objects in the space by
      // stepping forward through time in small increments called steps.
      // It is *highly* recommended to use a fixed size time step.
      cpFloat timeStep = 1.0/60.0;
      for(cpFloat time = 0; time < 2; time += timeStep){
        cpVect pos = cpBodyGetPosition(ballBody);
        cpVect vel = cpBodyGetVelocity(ballBody);
        printf(
          "Time is %5.2f. ballBody is at (%5.2f, %5.2f). It's velocity is (%5.2f, %5.2f)\n",
          time, pos.x, pos.y, vel.x, vel.y
        );
        
        cpSpaceStep(space, timeStep);
      }
      
      // Clean up our objects and exit!
      cpShapeFree(ballShape);
      cpBodyFree(ballBody);
      cpShapeFree(ground);
      cpSpaceFree(space);
      
      return 0;
    }
    Chipmunk2D-Chipmunk-7.0.3/doc/examples/JointRecipies.html000066400000000000000000000042171347650476100233020ustar00rootroot00000000000000
    // Faked top down friction.

    // A pivot joint configured this way will calculate friction against the ground for games with a top down perspective.
    // Because the joint correction is disabled, the joint will not recenter itself and only apply to the velocity.
    // The force the joint applies when changing the velocity will be clamped by the max force
    // and this causes it to work exactly like friction!
    cpConstraint *pivot = cpSpaceAddConstraint(space, cpPivotJointNew2(staticBody, body, cpvzero, cpvzero));
    cpConstraintSetMaxBias(pivot, 0.0f); // disable joint correction
    cpConstraintSetMaxForce(pivot, 1000.0f);

    // The pivot joint doesn't apply rotational forces, use a gear joint with a ratio of 1.0 for that.
    cpConstraint *gear = cpSpaceAddConstraint(space, cpGearJointNew(staticBody, body, 0.0f, 1.0f));
    cpConstraintSetMaxBias(gear, 0.0f); // disable joint correction
    cpConstraintSetMaxForce(gear, 5000.0f);

    // Also, instead of connecting the joints to a static body, you can connect them to an infinite mass rogue body.
    // You can then use the rogue body as a control body to the connected body. See the Tank demo as an example.
    Chipmunk2D-Chipmunk-7.0.3/doc/examples/Moments.html000066400000000000000000000035551347650476100221610ustar00rootroot00000000000000
    // Moment for a solid circle with a mass of 2 and radius 5.
    cpFloat circle1 = cpMomentForCircle(2, 0, 5, cpvzero);
    
    // Moment for a hollow circle with a mass of 1, inner radius of 2 and outer radius of 6.
    cpFloat circle2 = cpMomentForCircle(1, 2, 6, cpvzero);
    
    // Moment for a solid circle with a mass of 1, radius of 3 and
    // centered 3 units along the x axis from the center of gravity.
    cpFloat circle3 = cpMomentForCircle(2, 0, 5, cpv(3, 0));
    
    // Composite object. 1x4 box centered on the center of gravity and a circle sitting on top.
    // Just add the moments together.
    cpFloat composite = cpMomentForBox(boxMass, 1, 4) + cpMomentForCircle(circleMass, 0, 1, cpv(0, 3));
    
    Chipmunk2D-Chipmunk-7.0.3/doc/examples/PlaySoundOnCollision.html000066400000000000000000000020201347650476100246100ustar00rootroot00000000000000

    // Callback function
    static cpBool PlaySoundOnImpact(cpArbiter *arb, cpSpace *space, void *data){
        PlayCrashSound();
        return cpTrue;
    }

    // When setting up, reference your callback function:
    {
        ...
        cpCollisionHandler *handler = cpSpaceAddCollisionHandler(space, PLAYER, WALL);
        handler->postSolveFunc = PlaySoundOnImpact;
        ...
    }   
    Chipmunk2D-Chipmunk-7.0.3/doc/examples/Sleeping.html000066400000000000000000000040161347650476100222760ustar00rootroot00000000000000
    // Construct a pile of boxes.
    // Force them to sleep until the first time they are touched.
    // Group them together so that touching any box wakes all of them.
    cpFloat size = 20;
    cpFloat mass = 1;
    cpFloat moment = cpMomentForBox(mass, size, size);
    
    cpBody *lastBody = NULL;
    
    for(int i=0; i<5; i++){
      cpBody *body = cpSpaceAddBody(space, cpBodyNew(mass, moment));
      cpBodySetPos(body, cpv(0, i*size));
      
      cpShape *shape = cpSpaceAddShape(space, cpBoxShapeNew(body, size, size));
      cpShapeSetFriction(shape, 0.7);
      
      // You can use any sleeping body as a group identifier.
      // Here we just keep a reference to the last body we initialized.
      // Passing NULL as the group starts a new sleeping group.
      // You MUST do this after completely initializing the object.
      // Attaching shapes or calling setter functions will wake the body back up.
      cpBodySleepWithGroup(body, lastBody);
      lastBody = body;
    }
    
    Chipmunk2D-Chipmunk-7.0.3/doc/examples/cpConvexHull.html000066400000000000000000000037001347650476100231410ustar00rootroot00000000000000
    int first = 0;
    
    // Create space to store the convex hull.
    // An alloca(), or a variable length array would be a better, but not always portable choice.
    cpVect *hullVerts = (cpVect *)calloc(vertCount, sizeof(cpVect));
    int hullCount = cpConvexHull(vertCount, verts, hullVerts, &first, 0.0);
    
    // hullVerts[0] will be equal to verts[first] here.
    // If you don't care, pass NULL instead of the 'first' pointer.
    
    cpBody *body = cpBodyNew(mass, cpMomentForPoly(mass, hullCount, hullVerts, cpvzero));
    cpShape *shape = cpPolyShapeNew(body, hullCount, hullVerts, cpvzero);
    
    free(hullVerts);
    
    // *********
    // Altenatively you can use the CP_CONVEX_HULL() macro to save yourself a little work
    
    // The macro will declare the hullCount and hullVerts variables.
    // hullVerts is allocated on the stack and does not need to be freed.
    CP_CONVEX_HULL(count, verts, hullCount, hullVerts)
    
    cpBody *body = cpBodyNew(mass, cpMomentForPoly(mass, hullCount, hullVerts, cpvzero));
    cpShape *shape = cpPolyShapeNew(body, hullCount, hullVerts, cpvzero);
    
    Chipmunk2D-Chipmunk-7.0.3/doc/examples/cpSpaceEachBody.html000066400000000000000000000022701347650476100235050ustar00rootroot00000000000000
    // Code snippet to check if all bodies in the space are sleeping
    
    // This function is called once for each body in the space.
    static void EachBody(cpBody *body, cpBool *allSleeping){
      if(!cpBodyIsSleeping(body)) *allSleeping = cpFalse;
    }
    
    // Then in your tick method, do this:
    cpBool allSleeping = true;
    cpSpaceEachBody(space, (cpSpaceBodyIteratorFunc)EachBody, &allSleeping);
    printf("All are sleeping: %s\n", allSleeping ? "true" : "false");
    
    Chipmunk2D-Chipmunk-7.0.3/doc/images/000077500000000000000000000000001347650476100172705ustar00rootroot00000000000000Chipmunk2D-Chipmunk-7.0.3/doc/images/hash_just_right.png000066400000000000000000000253111347650476100231650ustar00rootroot00000000000000‰PNG  IHDRÅøÛi pHYs  šœ2iCCPPhotoshop ICC profilexÚ­’±kqÇ?wQ[¤ÄPÒNoê%´ƒ "I“Vjk iJ›€ÃõîšœÞ]~Þ]b#‚‹stEŠ(¸)E´£"H‹ÿ€nBA[Ïá¼Ë ÕÅ?ø¼Çã½ïûòƒÄ¶*„%¶ã»å³cÊBµ¦ôlÒ‡L/)Rªæ‰|©4Í®ñí#À‡cªÖÍ/wö¯ž¾ø¨~þùrþöËþIw¡Z)¤ë!ç€ôbÈ }Õ>H ­5T¤ë@Æ­” ­ÉzÈ/€äbÈo€d[«û mYG7{“ºái çS÷4ä' í±í¦‰gÀQM¸>$6€Ã ÕšJn‚3G@¾Õ­Õ\xú¶»µá"¤OÀÚýnmk ßzK£#H}.ìý[Cг;nü¸;!± ëW´–Ûþí—$½ƒåáÍaïýˆvãвð`æ©p7ÃóÐÿ J@%‡<:½ÐCLǰU¥hzÂR;ü÷°­V´«èsg.ÏÂ/U"öÚ³ã/™“ëjq*âkÂLÜãN”#¾¤ž+El8s³ñ|«4ÝÝ5÷Þølwfe>b·Už‹ørs*î×b¬Í±f♦?ë§€‰ƒŠB…J'ü›ûR°2$ÿàÕã¯xåË>@¡):®YoøJ^ËÈ(“Žv<£Œd³§øØÆ´áðŽt cHRMmus ö¯…™nšç»1}½v x(IDATxÚí] pUå¿$„ÇÃ+„æAxH[š W‘òŠ‚^©òQµ)à!¾:*AKµ& ²Ø1YÜ©]›lw»8ckÝigev»2Ý®º;íTûÚv¶Ý÷wîÿæäÜs¾ï;ß}ß›üÿóÍsÏýÎåœ_~ÿ×ïû<Ÿ³±±‰ì£>úXbøÈÃ? 66¡Yqâ#‡Í9B1rØØÂà:fo- ÎaoM9NÂaä°±©L†F[$Þ¡ˆ‘ÃÆ&EŽ¢¤ÃÈacsá?ç°±1ç°±%0Î1±ÄÈacsGçÖØØtÍl`Îac‹Ü[³Aˆ‘ÃÆæ‚!í0rØØTÞësØØbÃ9ì­±±…çıªÖ3(¶“8ŸŸ«ý¨Ï~øÃþ(Kâüø=?ꟕ'®•ݱóF¯]»vÏ=÷Üj‹-¢¿›µ¶¶ª¿_v?Œ!rÒÈ"{Þtæ'9êÞlÙí^¼xÑãñLY\1¦Ü9м…æŸ?Îv<:·×¾õÖ[š`ôÿ® 6Ó¡ëœT˜Ùó¦3?qÈQß“ó€uÎk¯½†§¿}ÍÓ=»ºƒcg—ä +ää®î¶ÛãÚï|ç;Šï—ݣŕsRÜs‹ìyÓ™Ÿ|oMç/Á‹/¾ˆ§¿sË6Tô ç™þÑZ÷®}çw>Ö3ëý0Zž·¦é›¹ÎO(rÔ)pÅÉ䨍Æ:BNvõ!ç0®½råJX?Dæ5rR™jžyæ™G}ô‘€–Ø©S§„çu¼ú¤qŽ,2Sœ'ätX‘#tØDç 9àœþ]FKÚq`“94Û©~øá‡:ÏIªpŽ衜#÷ÍDD¤ðÖ\ï‡ÑrRˆÀ3øuqíùºo½« ¯Ž!øàƒèûK⛕ևP°z×YU%Gâ¶ 9GBŒMÎ>Íxˆ'L˜€~VžQšìííM@DÈA%G—j,²qŽúùL2çhê%ì¹5Q—M(`ºdõÖçDõs „Üÿý#FŒÀ}ääÂy­w,éØãM›6% [ ˆs{4³²ÜZësÄÞš´jë!pé¾a}N¬âœîîîU«VQËx_Ŷõ·õ4ÓÀ[`)2D5ª2é­Ï õÖT8q|Ô½Ò¬ÏÑ7¼ð K—.%ÌLôϾ¥ó Åßs0ãÕ×¾ᱎ·>dž+½¸&¦õ½µ´Ñçz´õ9²îÖçÄ„sNž<9mÚ4#˜ñŸÞts}×-¡ãàèêI{â]ççÖ¥„cíÇQô¤«>íßø/uo=>§ù a´ñq=G9f([4²²¥Þ•~ÌÐL¦$[2½µF]ÎúBNeÑÌê •4ªŠf›Ç¾ÒÚ/”ÖâÕW²ÐGÆCn-K5²>'ä ˜A¬OŽhxp‚ÄI;ä¹c ¨¸& 4ã¡ç6§¶YöG6]õ9øÏÀž5kÖl‹™o'Nœ8Ébæ[:(..Þ¼ysdÿ.£Å†œ½{÷š „.BXÞÚσšpíóÏ?ŸHÎYÙèÆ6¡óldú¬6cü¡OçDìÈÅu>£Å†œñãÇçŒA çð Â;~á`¼õ9„^5MÎq}6>ûì3fîܹA‰\¼ãM}N²æ3ZlÈÙ¿?žŠÛt#–‚¥ëç!kw·ó¡Ç*+i§ÏùôÓO_zé%3¯8Ù[Ô4åÖN_sªès’5ŸÑâŒsÈU Ń “v[¯8[Pßm$lPö±=ñK–,ÉÉÉqžÌ[›½`/``‰õmÿœ7?X¾\SŸƒ9VÀŒÏ) ÀôÔ§‘*úœdÍg´8‘Cé8l~1½H)Èœ‚­ 7ãq¿n^Q06¹iês:;;Gñ: .D ¢~~°<âgL.Ê)h¸aq{í}&`ₜT‹aXŸqn MYÃ<…“&ŠyúßRUImó¡ZÊò'¿¹¤çÙšöro>ÅŸs+´ÂŠy°ÎQoŸ]í3ÄýW5Ì„ðyÀÊ8ÈHáö›nL]ü‘>'Yó-Âzy)2̨#z‹t¶ÙŒC ;À ƒqyIÏŽò&bgÏAë­ÅêyÕà–¼Y9­s6:I&Žœ>'Yó-BätttàBÇ€6`ìèBKm;8X3±ár6&xÎ-ìœSPæÏŸ/L'D³®t¸ÏRD5¾ÂÝKºÂ&ÆÈas>… ЫÄp~·Å*++QÖÔIC[Ï[ç¬(,//Çúfã†36æÁ¾)ÍD>ëÖ­kï³3gδKÌùQôRû©¦r£f9 íd‰Ie5ýG”Œ‡;æz˜®®.9‡Ý€fAµ!dÌ¿§aÞëÏ÷0&¬´aÆ:..î^4Ö‡ AùÑ£GÕ8q~W¢¢š:FNj 'ú àñãLJfæ‹Q Á nâwß}7¢s` HÌlS‹<ô ºoHRÆñ‹<·ïWOúCçËþq^»<—ôðÌÖü¡^ü_êêêt¨FÆ9úûç„D5uÇôÙ&€«cŒœ”@ŽŽQ²¸ó©gÛ­íÈ#­û6ïØÕ´qSƒÿvÿ’[ªg̬()-;Λ—'D—ÏçÓAŽÕöì1*3ÎAy»§€‚ê»î+k¨ùÞ!„œÿrúÒ2·ê‹üøÇŒsàÀMüD 3húQ sNò‘±^d’Ÿ›ÛÓqÁe¼zžºÏ~£ãÉg Œí;P3»ŠëýX ‡,??ßRµ'£䣩˜ƒLôãáEÈmbƒŒpœãDåéÂáE¸;Ž)¨Æ´põ`a%Ј^9ÉAN¬z·à‰RœØŽ;.€”̱â~¬„CøùÒ—¾DUQG?…nl[L4afñâÅx­ãë]ØI~Úe ±âjÍõ øô:tHÆoذ×¶Ì\ߌ9'…8'¬˜jxPÖ +Mh;ÐYÌ ÷#ƒ„dœàÔ¡aÇHÄù]SÏ·¤>hÚw°yõR^ŸnŸ1Ûh©FjñĉŠÜšBTîl(Y¬ÄI˜Z#' ½5êÕGß*“&9sæø×p-¾!‚zŽ&f¬o©xŠ&4´¢écæÍ€î–”UfÌ·÷ÜÛb¶T;ãíÐ dþây1m9é‡R#ƒ=´‹9ýLe\ÛÙ©çÃ`l!ç† þQ4A›õÐËné5ä²qÉÙou1s)<8?a°¥šºÚ¬=¶ÒMըҸ†‘wäÄJ?CËV b±×s4 „èÈ\ŒF_ŸCxÐl>Ò DáÈÒkæ1*§˜y禦Ÿ&—zöµ´ŽìjsrZ9ãQºaä$“s¢l)@f Ï„*m|$vÛZîÙm[OCGŸ£ƒ"×ù¨¢¢TŠòO8c²Jx s ‹ŠmP‘AÈÌ455YK7X¹²¿tÓ¨†‘“¢Þš+®Hb M )8çÕó¨É¥ÕúEœC'/]ºÔÐЀv›cÇŽ$²ù¤V0º§Ë›„úR¶=õB§0tì„2ˆˆŒxÆï§Åøƒ¥›…÷ƶâÉúœôŽsìÒáC­ÒaÕsœUQ›¡£Ç¦BÓ'"¬Ì„<…l! 5™êêê@óXZ9¿»¸û'S[Þ›ÑJš6œ_V¿R졉(È<03Ó§OW”nØ[ȉLŸl”Öé’v|Ðí¦h”VësÔANYYYõ¬Y]/ŸkâdkKsSãfÿÒ¥8S4nœ Qð¬Ù—””deeáíÛy¥êÅ€tY8¯w¤ÐC _RÁé­½µñ匡º¥›Ñ#'îȉIÓ4þŽGÄ‚p¹2¤§»Ìl›>ÒÖýÂÒç8¬¯`@¥ç¯_3ÆÅ ¶ƒ®o~Ã@TóA¢J=žkþŒ?z ÑÇžcFI88xû+m¿W„ïùíÌ«a=™s‰·vß}÷É­„|Ž c†¨M:AŒ60vCñõ:ÒŠŽO UËÆÍý˜1ÇÅ×ìg,ˆú· >ïÏGò'Íšd£ƒ÷¶4™ßóIœ 8ŒœY<–ñG~ Ï+*3´nÈ@e(رЮ·N[»v­Î÷»¢Åz’ÔpÒ육 ÂãwŸ8ù¯}Oüÿæ{Mä„€ä’{ž þxH¾äO9Wg¬Ž‡WÆÈIoΉ C7Dë€þ&j²Z§ð<5PÃ`Æ"ÇùÎç¾¾ÆãAgçoÇŒý÷eþW/¹8f6,™ç×oØŽÅHß™´è§ Ì 0r’€œxï¡å|*yLÚÏî—Ï)3!}²téÏ ^òx¾zë*êìt-àÈFfffõ¨²¸fŸ9)Ê9)…%EbÀ êOðŒÐmë4íúK^ùiˆRÄ¢H Ðo‰±¥GÛ¼í fFÎ@öÖ"3ª1Ï@þ‰\™.Õ\¼Ð8øYãf3¦½|ª2“Ö«Î ’a%œ(õ6¬ÏI!äèèsÎ;'\í6J=OdóÃÒç QŽD³,Ðÿ6ôä•ç¾þË|’Ý›‘O4¡í UÑ. KUÏ®2аm5Ûã›1ç¤çàÁEìž•åL…¡p‰D™¹Ú-h´Ú­ c±òëÂÒçäååõsÜœ4Û„UÅÅ5hy¦ žÐ}ö[].ÉèK!­7¸Êáĺ3õ9*‹ßþ<:+õqÞw¦yGÛ½w·n[Ór§¿iÕâ†%3}•åÕ*®/(ãÍ•UrÖ¬YÛûKŸ,æ(2ir.B Í8¬¾¾žþ;h¥bÆùvÌØBwÂá8'ÞȉÓþ<:ÈŸ÷<¥3íz|O{KcÍw–ΙŒ§'¶÷ÇbNhyÔÐÖ×?ÖgØÖ·´­È?›Ù6‰Ÿ¶qK“€p9 ¶ømq¥£Ï3V EÈ“"ØNÖøŽ92¶÷¯¯Ïs*B1ÇÐ~–”TUUmÞ¼ÙÄÏwÜ1|¸±î¡åz™>çÉç;ˆ ’K8ŒœÏã·KœNÔQSS—LÈ0'Äêðè$Äöþõõ9TÌA*ª‘¸mm=bs;A8X½mß¾}>ø µT#spä+mΜÉB íZb£FŽÊ[SìW¬¿‰¼9_Ç[C ¼j²Nœ¸B EJlï__Ÿ,æHh꜖/J»âQOlîÙU×ö… £ò‹B×]wƒýßq€·K–ù)s@BN—pXŸ“ähns-[…Uhš Þ‚:ÂÂŒ9@V‹-ŠíýëësÄŽbèê[ æíóšžØÔðcSÏC«;×Õ¶Ìšä3‰¾¨±An2o¼ÐùýZgäæ¡i 1biöÖÂæœp÷c‘Í×IC˜‚pÅ•^„$È!c{ÿúúA1Gpð:½ÂÐrî¿­=›ÐqrSˆ@J!t¦ýsæÐßffbãÄj§|-þôÂÈqÉJ«Ÿ9ç4õ|ºŠ±ÕLÃ|€sàÚÝ»wÇöþõõ9!ÊáHФ#¼Þœa^+TLæé‡Pà¸õÎnÑË#®7ÛÉÌNÂaä|–“£iúõ#Ô´*2o ×b=¾ØÞ¿¾>Çë'MjXy;‚DüH—éës† ¯LH8Âñ· ›ÿ4Ì ØüÞ3äÛ¹ŒœTDN K:®%|<ŽÆBé­Û4“i!µîÇq-–ä‹íýëës6nÜ8Î!™F}³¢¬ÔwãQVDhç»›±Žà³3ï:¹©G<ïÎhøµ·øØr43KWÁÆúœÄgÔû±hž×\ð•M ¨ØAõ@'4È”#c{ÿaésÌ s°.!v1ÀêjÈ,c(Z Wˆ¨Óóæ}SáZ,iÛûKŸc¾¥íi_Ù|ê¸ÐÐðaàéÿM~Ñùº61áH°´}ܬW22áªýxÞ6ŽsR·ž£_útÍ­ÉŒÖÛGÄ¢Þôö#:µ´B_ ï?,}ŽUßÖpó ÚÈM8Ÿƒe !¢~cúº &ldÃL¯õ ª¥ØS:~zÖçDÛñ©è,~$›ïª‡¡Fi¥o&%tUãÚ>ø ¶÷îþ9´r´AeÏé1vß~‹±™{m­s>!ýâ|´›«†adá+–'Ë7cÎ #+eÌàÊ9Ø~Іý’(ª@¶Fé˜ÜXúZóÖ7{ Á¦çô!¼¶4¬ mwmó 9uôóÑV ¡lª¡cc}ÎàÐçX$O†YÌy’¥ÃºŸØêshKÐêÉ7˜˜1GkãjÚçݺ—(I Œ pj[¤™4¹ç†«ŒÕB{²sœ3xô9ŠFi×1Ã7ÛÚ(`}I *Š -€ O[Ó]˜€‚¹L»©`C[€-rÍœèÃ&©FN2õ9c'"ÖGŠõ™¾T–>Çl”N¼>¹f´ÞÙ}ì~lõœ ·ØêÍŽ™˜OœƒÄ4õG»£Åqé¤KÙ9©¢ÏÙ¶m›P# ÝœE“‹A,ðÊÒ C)`l{Û—3` d…­—¢dÅô9à:‰Î#MNª §uÞ €áŽrpð…éëu¢'„ åö”[“Õ0rRKŸƒ…8hΓ'OÚV»…tÙj·F"¸¡!¬û‰^Ÿƒ¸…ö0l¿KÏp"ôÜÀKpê ¥Í#¥±«/íÒzú©Vz€õ9ƒJŸîj·´¢4ÚÕ¨×3¬û‰^ŸC»¯!†‘òŒ N}‰B»]Y°I+I‚éæœÈõ9´„\ØaŽÓ† ð4Ÿ:u 1T Œíþ9ÖãÈôBQêsP¥1âû-«Cè%x,÷ÙúæL8>è‹ó"1Ýxsk0U tÛ踺¬N•`}Nzésð$yGž]ïŸTYUX^1<ßkó¦àhÁÝšiX˜>œ±çŸ^g©4W8E¦ŠFŸCý5ÍëWXprHJ8§lÌ|­›7_2jÔ(Z¦ƒÕP°íóŸ‘éshä/Hµô#'r}’ÂSùx³Ç:vëÜòlûª#­Kv4T˜ L§Å°-UGŸCý5M+oÑpÒ¸2uè`Éhò¤.**²¼2+¡?]¡%ÏÊë0rˆ>~°Ñr¹6ÖcçÛ}»MPeÖé‹xIÎ8%…œCý5xâå yÐA5*P-™3-7{ØÃ?L]Xì©ü4lD„\ÂO&ûƒËO{<Û&/Kb[4#'–ú#_´£ÉxlçÁBH EÌ9±Õ)ô9¯¼òJvvöÜŠ"θž§ö¤ Qqq1Aè½>=¶F<4¡Š9g èsÀ¯bƒ„'Öóô®E:â=<â×kgóÖ°i;Ö4¤ +l…N9±„ž·Ì§ÆÔCm€!Ú1ÓésÞŒ(1· #+Çä¿LÊúœôÒç †ß.¼/ž±M@,ds"X7=2½PÄú4+„Æ92'MJ8ïØúÍ{7âK€C‡½ q‚­Øi]5²’Š5 {WµžÙÛý­–ž {»ÿ®fÝA,™›“£Ö0ç¤>£®w®30½2þ±‚çÎÇÛp-òlÑ{kaé…¢Ñç,\¸04Ú1ðЫÊÁsåHÓ/gOùýuÞ¿Ïȸ-'gîܹ´~4YÞpï¼ ß†%MÇÛëx¥¯½80¦!,ÔÈ­±>' ô9$Js¢ÂöVø|<™(MSÿ¿Ÿ†ÂWD`Ö'űý‡¤I‚S‡®6¬0åÓ§ƒÄ2IJ/@,xØÐÒc;‰ù999ÁÕ S)IÀȉ$f@ÝQ¾:¼‘½E^¤5Ç$ CŽÍP• äœ:¤ŒvŒ·@Îo229W¦1„XBbÛ!ÀÌpór ß™Æúœ°ãW} šP¨ 34Ë6¥5‘é’…SÇZ”å<è–a3ún@5?Èξ”?úÛ;;­°!D†"°“±zNŠI 9ês4OšS­ï§YOâBMiì~’ˆš!HœÓul¿:½æ›e,o‹ùäéYñ @‹ódà-œ: Š9©e‘Õ© êJ2fÚ:@VæJê‘Õ+‰aêVí‚À¡5èOC#"2“ï4ß™6t ”?|#'u9',eÐí»¨Ur¬(B€„0)š’R8Ç!Z,B(Ød]6O›÷k2.oâ¤-’PÙjA¯4ësR0C Ö·Ë ·:Q!LCÛàd[:ýO£Se‚ääC†îÀ’-@å'‘N°N†8´¦Âgú`!jV¢¨¹gY•áìuúšYŸ“õÅŒõñ‹DYFF/Šª(•AëA‡UÜLnœcÛýàAõAÀ[£µoœ½Eh;²Hæ•)¸c[ýCNW{{k)Í9®úRæ˜eP'½(|6´XWµlžøý4"Ð A4>4êÐ~Œín–.uΧÎÑöt IFäÐ1$•&”õ9IÏJkê[¨ jÉ"æ±ÀÇ£2h4ûó$˜stàeÀp œu<ðéC m  8ܹ¶Q·VndÎIJ¨ EÂ2¨:Oà,ƒFÓ¦™:ÞšÕ–/_Ž=ädº=J ¬éZÀq0•Ái3W3rR9:É.<Þ1c5{mlcV½_¶6§~r/1ȉ Uaõ“ªÃ(éXŽSmi\FN$úœµk×’gæAq•MàdO iGhÈT¢(ƒF©«IMÎqÍ%¶TH5²®ó]¢Ï”¾?o;sNúqŽiW¯^¥Õgà¶¡¦‰~ÚEÙjÕÈÂ"€jvT·îo!P+›l['-!t‘19´ ®Q•ùf’Â(Úv~Ðêü)§àªÍgc}NJõ­E¦oAܤÙÙ³g Tpê„ Úºuk”ûóÄ9±uÒ¬œƒåÚðßß¾¼Ù%%íàŸ+Ë[̆kÝ­™s’Uω~ÿ¨PB:Û¬äDóýéè­™;Zë¡¡8yEÜ=ÐKdzúø¯r¯#äü¢d1ësRÎ[‹íþ9ñ›Ÿ0ä¨÷ÿqåë[S!g­‡*ô9ÈD£[çæÙ~Ì7:wšê—²òþ!¬õ YŸ“”¬tügŠr~ŠpNX~ɳ©Jbi›Wvfoª=H[#ÿfóo‹Ç®ûâò»W¬löxþûºIpØÞö°>'éqNLöωßü4Í­Ñ>@ÂÞ;ZͧA,³‚IJ,}vöü™³[›öv?õBÏ™—{Ú_y{GÓµTŠv9ñÚ?'~óÓ9ê«FMœ3}b! deeA/MÇEcÆ6Ô­h;ð  :^þiÓ^S^úËXЃõ9)‰tÑç(ü4Ù|s1eË–!cÆŒÁºjXÌS=mFÓº»:Oœâ$@2Îñãâëíñü:¿ðj ô0ç¤"«¤¾>'Ü(Ⱥ–qCU43#ÃW]ôÇdh = h-õxNܰˆõ9)š!ˆÉþ9ñ›Ÿäļªc[ÆvÝê'‡o&ƒPŤ’à:8¬ÏIÍzNôûçÄo~úfL#¥FóæmR¨Ç™—áÔáÂßAöÖRŽs¢Ù&1óã÷Óˆl?ùV¡W욈ðÆÅCs¸ppíì;²>'éYéè÷ŸIÌüsNL<7çnVÔ%fL^ªš:]kù5æœdUBS¶$šîÞ¡ˆ¶†G†@ÇI³¾miÜž âjFN:å£ÓZŸc£¼E›ÿ¦›]rüÃÖPâc}NÊeb¾/Mlϧ/çØÀƒ66onžÀ7ë+R*8lŒœôèUú礤Øi?úUex#A‡má}¬Ïaà&Ä-Ö×­Ð r,pØÐ§sëØ)h&øi’Vö`ä°%96«©©AqS˜^%íXµæõ@Û_²r~HR³>‡m° üC›#› ‚¬ò²4g`ñåÞ?ðàgC†Pèï¼E¬Ïa°ÈÆHÔ=¸Å5mÛæ.0·àý¿ÌlƒvXŸÃ6x¼567H tæèWä‚9¥ÌJàùTc;DŽsØrh+87’ ~e¨!R(*8ж£³£åW‚Z·±Ó9lƒ9páhC‘f„¹¾mñ’aYY†˜Ç7³½çqoµÞý/ÕåèÝ>š9‡mqÌX8wÑÍNªiݽTKM•ucËÂÌ™ž&~Ö5+§^,ñý¤rësØ2rlÙll&È×v¯» ‘A[ôÐF#ÓªËOt>`¢Å6.ŒÈýÌ3äÏC‡\<—õ9lƒ…sh;ĺ…7QèZÿ9r„`S×°8À3bØüU矎ÌP˜ÞÊRì­±¥>rdÍ;h&(--¥MD°#/RÕæ>ØHÜß¶CF5¦Ûöqu9!烑YŸÃ6(8‡àtåÊk‘½€ÍMþTìÝmÛ¯úkà³-šÝÍœÃ6H¼5ç®ñ¡É€ÇÛõÆÑö=¸Ð7n:#‡mÀ"Gè¹aƒQY2ÀšLS ʳµÎÙÈú¶ÁÂ9ˆv J§Mt%™3Ê·ž7+'Þ>#‡-…¼µýû÷[ý´vwùlåÞñ¬Ïa8ÈqÕÆQnÀôÖÎ(2:ÚõØ| Vfëô53ç° Š uOO­*kïÕM :mø¸m²µ>XŸÃ–®È‘í·Cé5ÄújßÌ5:ÞÑ’™™L°>‡-M‘Ö”dCÄíØc”ƒŒ=[‡7Öu.âÎú¶ä­™†¾4¸fÒ„ž 7©ÂhÛÁzˆX0¤¶¶6°ÐÔbŽsØ>rH} l#PCˆJ:XÌ ‹!š îÔÕÕá¤?v[V1rØ’‰õþ<$zkj½[óäSÝ¢˜ƒK@2´<•u…7Úk±:FêQF[JpŽ, Bh®7pj0Ì4vSÜ»·Ûa„"|¤›­f}[ "G?[€Ç=˜¤VÆ6‹üó0­²²û‹Øµ-,JëYÛ³Õ¬Ïa0qŽ­Ñ‹ÐUC݉J8yFlô‹=HÂÈV3ç°%9ïÏѨ0IMÉÀ€’ÖýydFŸž?¾¼ÜˆˆZ\·e}[jrަçY5Íf>í9pá ¹h]-N±âŠ^‘HÈš];nj{Š0rØRÝ[³&©’ÔÍkHÃCRfgLÛ²e ò׆ª§pFXøaä°%9l‹@IêisËIwM¢k<„h‘e°ÎËË£œu[ÍvF[ªX¬öš2e žïG}Ô<ÃM~ q€Oˆï¯UêŠF[âýþB×®]{ÿý÷­óc»û<­Bøÿœž·‘ÖÈQo$¬³Ó½m~<ö¦T8Å?Uÿ‘ÖÓ„‡&ùX?ëè9 ÊÏäE6ü0rØ’Ã9²ðFóÈ>ÒçW:²M0ó×&~жÓÚ·E#‡-AȉӞªqÚLÛ¶·)lÏž=„ŸQÃò‘Âfä°%:C ` ê8'æûiÛò×ûöíÖÚ„ø—Ê–øÜšO$£˜?ä( DF*ÿRÙœBHíw©õ?êÔEUF[⫨çǰ’c›¯îâaä°%!·¦Ÿtv+œ¨ãgÛ(#‡-iÞšf1T=?¡1rØRÂ[Ó)àM¨ç Wÿ£@޳I”^9l‰0EêYÈ$úóãáØ*¡Â$#‡-ÑÞZlK¢‰÷Ö˜sØ’çΤsª±"G±#‡-iqNúœ¤d„+0rØ’–[‹¸W-®úç°·Æ–L䤋>G˜[{î¹ç¶nÝŠE§9lIË­¥…>ÇÆ3%%%´rˆ±Ä;ÿRÙ’Â9i¤Ï!ð>|˜Yû•Ö×7èá_*[b“îúœ²²²ñGÞîÁ8ø{kl Ϥ£>Ç$Àæp<Œ¶$äÖÒNŸ)›I8F[“^úè¨MÂaÎaKrÒTŸcÎá>Îaä°%'·–FúÚ¬jã©6+á°·Æ–Lo--ô9 œ’êê#¶¡ñÿ~· náíö:IEND®B`‚Chipmunk2D-Chipmunk-7.0.3/doc/images/hash_too_big.png000066400000000000000000000250041347650476100224240ustar00rootroot00000000000000‰PNG  IHDR7¯ÿ pHYs  šœ2iCCPPhotoshop ICC profilexÚ­’±kqÇ?wQ[¤ÄPÒNoê%´ƒ "I“Vjk iJ›€ÃõîšœÞ]~Þ]b#‚‹stEŠ(¸)E´£"H‹ÿ€nBA[Ïá¼Ë ÕÅ?ø¼Çã½ïûòƒÄ¶*„%¶ã»å³cÊBµ¦ôlÒ‡L/)Rªæ‰|©4Í®ñí#À‡cªÖÍ/wö¯ž¾ø¨~þùrþöËþIw¡Z)¤ë!ç€ôbÈ }Õ>H ­5T¤ë@Æ­” ­ÉzÈ/€äbÈo€d[«û mYG7{“ºái çS÷4ä' í±í¦‰gÀQM¸>$6€Ã ÕšJn‚3G@¾Õ­Õ\xú¶»µá"¤OÀÚýnmk ßzK£#H}.ìý[Cг;nü¸;!± ëW´–Ûþí—$½ƒåáÍaïýˆvãвð`æ©p7ÃóÐÿ J@%‡<:½ÐCLǰU¥hzÂR;ü÷°­V´«èsg.ÏÂ/U"öÚ³ã/™“ëjq*âkÂLÜãN”#¾¤ž+El8s³ñ|«4ÝÝ5÷Þølwfe>b·Už‹ørs*î×b¬Í±f♦?ë§€‰ƒŠB…J'ü›ûR°2$ÿàÕã¯xåË>@¡):®YoøJ^ËÈ(“Žv<£Œd³§øØÆ´áðŽt cHRMmus ö¯…™nšç»1}½v x'LIDATxÚí} pTç•fKÝz«…è-BjKBÔI¸A„ŒI€m@ µM°É¸ÀFUÆ0‚dâxi;“e§ÖöìJ®¼3q«ŠÚ¤¶g\;lÅ3CÆyTf75ž8ŽãJªvgËûÝ>ÝW·ï«o‹~©ûüõW×½·¿¾õýtÎÎùÎoúr¡cddä¨dˆ§|¯ßËõk×®¹$Cvª‰€7-˜¥G Ù×Àxƾõ÷xÂßK•õ¿ Æ3> ÞÈs¬<ˆo|èm©ò‹1øU1žñR[ÊF5ĶTk™¡µö`<ãµð¼躔×`Œ_>ÑÖœFð÷ÊR¿ŽFŒg¼ì”M¨[ªúwQç%ã¯óÏðz¼ü82þÞñÆ_Õ·âo UUƒì×TdñŒ—ÚÒÄ¡ŸA|b¼ÆñŒg|@|‚;·ªøy¼ÙŒg¼>ž¢òx$ÐÏø€™é£l䱎W|hl©~i˜Îš„ñŒ?ªV!hðùV-µ‹?|ˆó¥œ¾gü=âÙÑ K¾”s Œ!ž¥a\—²N’¯‡ä:ëKY_ÊøXÇsí.ëKëxÖ—²¾”ñ±ŽçU(ëKëx^޲¾”ñ±Žç .ëKÏúRÖ—r2ñ¬/e})ãã?<<Ü×××ÖÖVYYYRR244t”õ¥¬/e|tñD˦¦¦²²²´´4“o”ææZÌæÚÚZ©-e})ëK üþýû»»»Ui騭qvwMØï>7á>wºyÅò¥K—êGX_ÊúRƇ¯EËêÒ’^ûšñ¾í®‘Ãà¤rµÝëKY_ÊøðâÁO‘–6=ZN(Ž'&wãS ¹’¥¬/e})ãˆGÔ§¹¹ùÈ‘#âE˜P0mú¸SÁFÙÁ„‚«§ñ)|vãÆGY_ÊúRÆß;\½Y0S¼N¶Ôc{зÝÐÃÏÌ̼téR ê?ív;~¼±îwû0J”De})ãÃŽ÷˜Ófƒ¡]UÓº¦ÆIáÌø3RCz·»ë¹¹ÌÍýéêZ¬Z»êËóóÐîHŽ›Íf¬srr ìžÑÑѱaÆnÏðƒžŸöÂ… ‘ÔEÇO6¹o ¥W"*~*Ö—2>rx¯9õ¯ª6C“bÉ’&ïÅñœœ/M&ÌM&»™é¥õKÛ—:š ¶<;f…Õ†‰ë˜™+¦Im¤›3ñŠÜ,|òäÉp;ºø{!,DGV“•ÎÞžåJ¢²¾”ñáÅk­N –"!†”¿DX¼­,\ZU\$òêmE1ÿÞœ¶¿tÇá†É—3W7»ÅyEr¬3_rÌbŽ·^ª«éæp’aë¤áœrö|ž¢·$½åGÔ­[·²¾”¯Gè:%<•"5ë:àã§mÅѾ«ÔôUç5Ÿ,ìüpIã/óìS;n ¯/é­.<È”ˆþð=Vÿ~`pO¥Ÿ ½¥NT­z Ö—2>ôxDPŠd¨üh›—Ÿ+ʺ¥´gàÄöW;Ç[]~|ë ’ŸÁàÇÖ¹6U ÂU¦Ÿüá³gÏ.Ø E‡ö¬šçä­J§—¸ À0¼¬/e|$ðdNI3ÈVVìɹ®¤ŽèÙöiã–ðJ–Ó?ÀÞÚq™?<::”žKM|FÒÏË•9½ïJx{ËKT,bY_ÊøHàuÌéwkœwò›ß«èíY9 ò³³bðêæ¹«Á¹²‘ÀÆU'‰\­0Œ*rŠÞR±ŸsJ£ún–¯ø º+²¾”ñaÇ“9m÷—˜Â¿½Ü<ùEŠ•â@/{ø¹¾´Wf<µ¬Ü• ן¡ÅŸXûJm¾Ð›{ùòåäk1™Àe‘û·t* Ý’ˆ¨°Þ¬/e|ØñÙÙÙJsz¾¤óK_´ö/ÓK¤ËÎÐÆ„„‡i%»*Fzd$‘PT×Ñ}W±F•V¥8ëK^<ÅN°:¥…(¤§sŠƒÞ2™>K2ÿ2£â†ÉØ¡_PøÎeC´^•%op*ø«k ñ3ð©*QY_ÊøÐã&…9?ÓÓ hý‰’ƒcugÞjžzMÃË],^@¹µš\SJÛEQ «ÎL}rªá'Ï­¥†ý¬/e|ñÒ­_áØ[3v%HããxäŠè‡pb³))ÉÓÞå—Uukµ|]<…|ûûûY_ÊøpáÑx¡¦¦†’Ÿxš[kJ&ñßÄNǨ»²f§¸¾åP3¤;»¾’9úD]PéSÖ—2>h<¬( ß…˲¡pW#ÄQ%*‡°X,ÙY)×^qX—ÞÒ¶®·¼a^EY_ÊøPâ) ³rI=ìÌ¢^|;)ª”””„ש¯µ)+LIÜË]Ö—2>\øõë× æÙ º‘W‚t;c¨’-¯™«g'ìꮯvUIQÖ—2>ÄxR®a¶W˜!Ä£À07­¿ŠÇ¥5€òWCŠÒÂ#Ö—2>ôxê$ØVÚ›P^®Nª†,ê™gí?üó®Ûoõè;ºDQÕ©t ìé©§žb})ã‚§Ž>+JX*Ã?Õ<…ßÉe“éWEŸW/ùHlÔ X¬b¢ÈÊRÃJü°¾”¯w2."EZoýIÍ©}‘ŸRÁƒB_JEy°–¿zåÊ’’cìêxÀ²'Ö—2><õ椌hÜg\‚ÅO·º>IÍ#–þCY–R_Šä*QTµï!øFò¡rx{ï_ü—Ùw¾ïþÆ5—j!1ëK¯Ž§œ,‰¤þsqáoW~œ^üžÉô0õU¹åGQ$WQG Š*ƒ@(܇Íj¡pܸ9 ~bÎz^ÁX\§ÊDÖ—2^ˆ¢±¨ þUÇ ~Kd½­nõ¡–…J ¨²{ ç´ÁdŠ„_žò’ó½9"*&HlÙëK?<yböØîɇîsØÄ¦~à0þúÂPëÿ<øÓPØÍó^f¿§&©¬¼üc¯*cujY_F<Ñc¼«Óý•'1oxÇöŠr‘®dµî¿c‡°KÒ¾©°Tœ +^qìM·¦Ó­EŠeß¾}ª?I^”ŒØ$†OÍPr4Ͻ÷4s¤ê\}O¥fõ¥a¿N~æôÁG‰¥Â<æÄëìÈãc]ŽªU¢ y'¬¢ì>’•’SÆUÌC®GïZ»¤t‰¸ jYC] 2ÁŠG úˆÉ4—œü‰Ýþ£ó“³ºÎ­ÖõSgðeaóUÖ—FÎÀѧ¨Ú<ÿà6ÐÕš–&sbßlkû¶Ùüœ­ØŸœrºŠ¤uNÙìÜ$._ÉPãgÀn¿èFÏ”pã_uÌbs’¡‚¨ú®¯ò"’1Už ¸EçÏŸg}i„ð`šPS²¡ƒì§hHý¼§ÂëÔ®½«ëˆ®¨FùmR¾ï_çe^ŸÐ2¤²S:ÀòuûØ¶ŠÆr³%™:0ÈŠY/<öãøYF ±ô7«V½+)Ô·Ÿˆî¶{âFø›=*ëK#ЧZ^W°žéy¼ØÅ—j¥üDž:5¼…x²E,*ú<`iЇ}·óòò@cÖ—†P-â@Ò3¤ja$LD}Í)fÕX®Ö©ô8ד˜Áõ\oEõim}ýíÿpíŒß¾æC¸’‚Š $XJ„D{jm#K3JWäØÄöe¬/ ¿_lÕii€®Â.}5ÅÄ=­Ô‹2bD×Go>N{²^4Zø³íBÖ,ÿ»¼Á…ŸdfâíWð·,•² £¤.ßÞ³rèpýø³ë¦^éš}}›ûµmîóí‚M¥Y_<©OY °Ueì1çäö|¼çäV%3º¾˜ÛÇ·Q= »©QÄ#´þ²/Jÿ¥'µF²6¿Y$ä%Ç4)Î׺ÝÒSÐ9pàëKÂG9àîêrµm)Üݤä$-oVÉXYà·´¶D§°ž«"†?âKzÿ>9õ?×>#e ’“òw=¯Ò²^Ö—†Š?0-µI †ç/ËS­X8Ÿ™yŠü(^F}®+é>’rÃdº²´UŸÒéÄÒTÚaõ¥!Ó» ¯ÕhöÅs¹ÿ!||ó“u¨¨4³ôz~öØóÃ핾­GYÿ]õ¥!Ç£Ì]ÅÝUa¬Ü¢¢Þè99_z3ozª¥ñ[}ï¾5¾MŒU|PÔÉg,LDÚ‹‹‹³R¬Z´ÔŸÇÖLR2†õ¥¡ÄC2&”.f\¼»¡C¤Ùõ¢\S1âîÒ|cò¡ß§§xYj¬¹›ÄpãiÏÎÎÆ#Aù• H˜çÛü’1¬/ Íõ¡¡¡”¡MQsy¸ŠB?M¢*®¸kçÇ™àØ¯RS‡sr(„r\ÈGQñ§¯ƒ¡y¹®û‚ýUV%ôL˜XÀc³IúÏ·]Ó·¢¯©R2Fʰ¾4døÝ»wciJ[<` 3ÄkšÊºffNæä\ñto@Ÿ¨jhO4 ´e@s†} ›”«)ž>f¨PãŒHŒà¡¤¯¾ëëÆ¼\™…·,&cX_z<¼_ÐU,þ‚ݼ•½j…`2``¦ì¶Ð©¢ŽŒº¢Ð@ë#4@‚ŽTJQª°§’#ÖÆž¾2ÄtbE:ËTDžPôËúÒ°ãaÑZE¤+älCö5ó…ø¾ ã]­ž bUhi¾ÆèÌ€J#tiKáã *HYÿSxdÅ, r**δ¨>êÎáƒxrX_9<(ubQ½°½®V($ô˜V[|ïO†Zô«‹mE©™©É냓ª±^4ÜxêÖKɘ€þík Ó*Mư¾4Òx™Ûº|^Ѱ7Øû‹†š¢V³‰5<¶Ÿ¤¯ØHPWÉa1ÃúÒhâɉMõ´ T¨ ⯵MÓ)ºxb©4óšáô))chƒSÖ—FO{í…½µË½³fÙü±îÅÎ*¯ -Ýïgì̳–C3Su0 3•)C]‘X_e<^ Õ¿=­nçN¿9Šùü¢Óïâô£[…T9s¼õ:ë?c ÿZÛôߥ! þ…9ýýûœF˜)»ˆd 5m`}i”ñdK–ŽîTá¤ÈL廣^–*»×³Û øw&¿0gPMØÏ½‘^C™R‘®Ë¬U¤Œa}i”ñó,%ûéÔ0¡Rë;¾©ÁR®.ˆ<êÀ~žÛŠ~–”,µ¥Z‘$,D1Úkm)v 2 CŠo¶¡¡õ¥ÑÇû±T•Ú6V´¥¼ŒÍ9ÝêzÕœõƒ$ó?–÷ÞZ7%%äSM“{ïs:Ê{—çT!ÅíLaBQx„Eé¥K—X_}¼ÄãÕ5¡ŠE)޲”õŸQÇÿmª· úiù¢…”‹OÒét¾øâ‹¼i,âÕm騂–£*ï*YÊ+Ƙ¿ê˜ù‰%›X õo¥w2P‰4t-C3A‰tpö£££PàՠØS›õ¥QÆŸyðAˆ¿¿ÛZпew1ÎïdT|j2ý.ÙüÓêö©þ&·o~Ò¹áàP36ÚâXµ¾¹¢¡4§Èš–eÒ{÷îe}ihð¨ܯ=nÁë°ÿ8tèÐ ;wþ}a!þÐ~š‘öQg³ºg«7¢c‘¥lâb‚“Aì Wls{k~>õ¦ß©äâìß™>ðªkèåÉíÏ‚ÏK³ómhçËúÒ{¿~äÈ‘¢¢"Sð㈤%ä¿Ô,3=’øÀÄRY^&LLá«s›“““a6åÌOu¯ÃÆÂf}ið´âζ&>®3Çö‰s sÏÞk7ÿ[f–ÐÒœ|·½>ˆÐ‘gÞÞØtÙdÚoÎàŒHÌâ±…,ž ø·*ÆSÍ–Ê&múÄúÒà‰¥àžûÒ×ýæÅ«ò+—äW~´ï1´„|½$_=V¤½L½ÓÓúy–4ÿy’yÖ'ˆaýg¬á‰¥ð]Õí§œ™r»JE‚¬/ ^ÉÒ9995çìäŸà³ýõ+”1ˆWæœ;a{ç›’nw´8ô™hDÒR¼5žô¨饞þ¯ÏaBŸzsÎs€5*õUY8KM<ü‡Š-Õ±«¾S?–Žjû·t*|øPûçÙ‚-ýer ŠÑâ@Ÿ‰®ü¢œ=nô¨ÄRDwå„T]Žú_¼yàUï³Å, KU|]=[ºsõ ã ‘´ßßÒÉÅHFiÄ`h[Aø‡qCBlÿ¯kƒ—Çx—°ã ³4¶4]gd¯Ü„ê9Àã5..åÇ6ôÙ¶›qòHNC„ïûÝψèª]f–FÖã5¾.5¶:õ±´:¯y±§þim47ˆ³’Ä >õ~»tåûC/L–ÊN±še–†‰¥:ÆSå­¿>ý¶¸©¬ÂŸÛŸ¦§"´0}©ÈÒÅèÒÆGþ1긩|¸]í$[úiEãû–Þ*TÍõE–•Y)–jG’>Ø'fSþÔœlMKÁÌNµÐAÀ™•*ô%[]жH³ÿO®úw¡:'Ž«°1És–4¯»«ÈÕ±¥½u[˜¥Ñ_—Þ9øøÿÉÈnÔH2›…ßÓKãš Êu„‹éo| gè±Q›´¨Åg–†¥R³i$sñêO;ØâåGK‚ù·,Ö¬Òí™%ú½bYŸ ŠŸ;5ı¾ô‚cÿÇåy†²/Š„ª½¢‘Y [ª .Åx ŽŒÒÂåC½­×^Ü⾉is>BׇUÆÎŠ¥ ø±QÆßµø/yXZ]Xت<Ø W1Kc"_j„¥VÛJ²cú*‘Sœ¸B€Nc"ÆÈ¤ýÈð÷âRôvD‡Ç–ŠFƒ RÙ„î”Yâ1¦cKµ½_TÛ_öHØ”#Ͼºnü 9ß±²<»z±˜ *lôKÇ«œíqÛq|¹?È)úh³Ó ¾Tz<ÿ(0K#˜/•Ïû"7WˆÔ›LûîSäh97Í\× çÒ8ÀÆ[Eý¦­ìµÊãO_úš§¯GåŸóŸ: â oÔÐÔ—2KÃ`K÷ì .Æ{ñêݾ~13]RØ8ùŒÒZгKÍ–¶º^¤]Y¸ƒ o b+äxÕ—Þ¨üeîäÝph±òDˆ±\ÈÄG;@à†z#‚϶wŠY‘ui })lég¥eøUþÌb¾<þ„ÌNjy¹2»Š’¸4e½¥² 0¾õ¥ðíÁÌ=ýANAeðOTš%•Yâqb÷àôés˜7'ðú<K'E˜3žWqþÕž½§L&ä¾7Í^ס¢üÊÜü)9½j·ô–Îïgÿû—RåŸ_Û8蜘Þ}ÑÝ20ž[n[ȳÅ,úHOþdzÜÝ7´ýÛ7´Ö¨÷ûœÞ˜Õ[R þ>Èñ·)iÖ2óKÁOéÜüŒkEk/³tQt[=kÎö‡D3c*$•™ú¸ïQFÿkŒ†N0s××Ü2®gZ™¥±3t#º7uB¾[ÛšQ`øÍ¢ÎLëS ‘ê¨x¢+yø}çg¤ÌÜ¥àª!ÓÊ,á‹ñ¾aluêý3×߯^‰ïãwI–Û1V*ªoäãu¤ Å›e ¥ U 3¬.Ücfi¬²ÞNÌ”_Ÿzîsk…ûï£b ·Þr}‰J`"ì_JiaŒŽNíÒf¦ê[Œ\·™¥±;PCo8Ì;ñÑé«ÿd[I,ýoe½1âJË­SöP pÊÈ-2hH•«V‡ó*³4vGËÔs]r¤ÒcàaN__Û0&¤:Æb!›¤¨IMžž Ý´Éϧ¸‘N*§|¥Ê,U§×`aÃçÞsĵ¥iû fiGzZ²ÒNz99«r}Îwüá…–Ÿd˜ñËý,%åÉ%V¢ÙãâŠ&T:kW8þg¸ô–ZÝãO/ª§È™9%uýð‹j hKQNˆ;¬í_uƽ繹= *Íãk™¥áBävVÛ¿U·¥=]'†òohÜYæâŠVT<þÊ€·ŽTì&½eÕ¨Vî'þô¢Ftj4Ò¬õ½£úU 2§T¾+’óŒgÒ1³4ŒkQ}ÏVõ léçUVêW¦l,˜žj•»¸’yNrŒ0 ÿÚ’ý7ž¼H8b*Ò2À„ ꊅôµö9­] ˆ÷€'p J_X™ã£èÃ"]™¥áAñS<Qÿ¶!oÂd²qq©ŸÞjuþ6)IHÜ¥,yG⎆êñEJË0e7À¤+é „ÂÝC×܇/¹ï»ß¯ z4Ôô’iÕ ùäÐ…Ž3rŠte–†qÈCGâFâñ?TõcOc«Ë>÷ÈÎkªKP-»úAu¯è9‡CtŠ2]ãÝãx"¬Mßuiµý°‡¢4;ö¨ìû#šVåÌÌ/I·¦HÝ]é1³4‚N¯ºzæ§MyD°Ÿ%%?Ý|0 '•o}oóä¿Ø(óvrÙ@hõ“Òn€‰ Õ¹H¿ Œ‰éa K1w>ã*Pk˜â3­3»üãFmûîSúºgØãÓ«ÊL]×÷_;¼Ý–ïfM L«33]¿µÓ5½vµf0z!tó(˜Ù_=š°«PY‚£ëऔŸÃçeÞ¯t@ž†´Í·w8§s‹ŸH2=§æëòº4RN¯4º;«ËXoà·ëΙÆ_×.O¶&§]‡Ý «ºeaØ{ÉæÓs `¼W/HÚ¦¢úƒ‡ý­¨c/¹Áa­ÇM^›œ .ý"%™Ò¤ÒÐî]™¥‘rzgõ\\U{k«ò¥ëWéP…« Ò¦¦dš´wO fo"!ßPלàkQÚäûïÒ‹ÞFÏ”Ü"‘²é8=­êýJ›ßÿåøZ™9e[m§W™DUdP{¿×'vºTxÈ(u··Ÿü«œÕ÷¢ŸT-Œ{½¨*þ­æ©ß¤-õÜ6uêÒ‹~ÇËž‡}ÍïÿÙ^øŠk‹ÔË¥c^—FÒéU¯­×©FÂëôºÃÊR{~Òµ¨‡ÜEy•&µjxã+4*[w-‘ó¢4ß\3õY’™XúAƒÃÏ^T±¨Òcx¿©~%eh~ÿjÿªo^Û"qqÙ–F7ÒÐãõ·¼,ÓBÜ~¢¢u…ª·RU™Ð*L´Œ æô:×K÷¤_ÝÚí#†œÛ‹zïRµh¾pW^ÏÀU Q³±IfH2HNq@’fŒä¡+Z7tqµ†Ø$il_7ÀD–w‹{bÌ´ô|ØÒ}{ÓÞe™9&NíQOfØpö啃“¿³¤Kß6â4Á´:›NÎ<Ä,]¬C #ÁÅ]SÝ»°›8ÛÖ 5ÕH‰Je€ˆëbwà/]Rt²q“{Ëcî-hö”Vá"Jˆ<:¥ÅLåÅ^ç×AÎß%›?JÏ>Ì×tŸ£œYºXÂHb÷ÝŒæ²"÷È ûèÐT_gq¶·ÂiyN­I{SàDЋJk­hô–U‹üôL®gif¡æÅ ú†T¼ˆBˆÌtëÛ{_|{Ä5ØäÇ,]¤Ãœœr/ŸÞ¿õΑÁΪet½µ¤'Á;_‹ÂnÁó·æ‹ÌTÌÇDïa[ýLi^‰PG}êÀÔõsnšgG\KsK™¥<´ÓB[ç)ê›'kÍÉÉ´(•šÓ„ª^ QÁüß²öÝJrJOû+jÈû• Á¥¶”Š»Û†ˆŸ.Q1íµf)•ᨬ ûé3¤óDÞm[šgò´/„~òn¡ã-Eëí8î`´rI½ßß²ºvmC:?¿ÞÒ#õ~e~oÿ‰?â+í×%ä”rõ@ß³”‡ß°¦¥‚ŠJC*º¾x=ßÒðc_zðƒœº¸×‹ÊbEËQÏìzL˨ÖäàSzK½_¸¾©ÙX޾t|ÚåÏRé„÷»¼ÔÆ,åá WvwøÙÏy‹:(rõý‡»Ÿ)n+„ušò¿)2¢´s„tT{—£R*jòSœûVÔSN|ÃÀø O_C‚´dUNG'µøé’<`ïe–ò0õÖVJÙ¨3ÿWmå­YŸ¤¥žò}¶? íB£.¥ÆEÊẇ¾—«ÅXx¿VKêY“é ³åß“’þû¯­Û­JKÕy°O½.åÿ†·t=@þIEND®B`‚Chipmunk2D-Chipmunk-7.0.3/doc/images/hash_too_small.png000066400000000000000000000211031347650476100227670ustar00rootroot00000000000000‰PNG  IHDR¡Håð pHYs  šœ2iCCPPhotoshop ICC profilexÚ­’±kqÇ?wQ[¤ÄPÒNoê%´ƒ "I“Vjk iJ›€ÃõîšœÞ]~Þ]b#‚‹stEŠ(¸)E´£"H‹ÿ€nBA[Ïá¼Ë ÕÅ?ø¼Çã½ïûòƒÄ¶*„%¶ã»å³cÊBµ¦ôlÒ‡L/)Rªæ‰|©4Í®ñí#À‡cªÖÍ/wö¯ž¾ø¨~þùrþöËþIw¡Z)¤ë!ç€ôbÈ }Õ>H ­5T¤ë@Æ­” ­ÉzÈ/€äbÈo€d[«û mYG7{“ºái çS÷4ä' í±í¦‰gÀQM¸>$6€Ã ÕšJn‚3G@¾Õ­Õ\xú¶»µá"¤OÀÚýnmk ßzK£#H}.ìý[Cг;nü¸;!± ëW´–Ûþí—$½ƒåáÍaïýˆvãвð`æ©p7ÃóÐÿ J@%‡<:½ÐCLǰU¥hzÂR;ü÷°­V´«èsg.ÏÂ/U"öÚ³ã/™“ëjq*âkÂLÜãN”#¾¤ž+El8s³ñ|«4ÝÝ5÷Þølwfe>b·Už‹ørs*î×b¬Í±f♦?ë§€‰ƒŠB…J'ü›ûR°2$ÿàÕã¯xåË>@¡):®YoøJ^ËÈ(“Žv<£Œd³§øØÆ´áðŽt cHRMmus ö¯…™nšç»1}½v x‹IDATxÚí]mpTUšîùH !ˆEù?BB aãàG‰”‰Éð%* ‰A„GLÍ8Žgu„;ºJ(pHÕ’ÚŠúƒÄ_Z»NéTm•þ؂ڭҪý³Z5ö'ûÜ~»OŸ>÷Üs¿»ï½}NÝJunŸ¾Ý}û}Îûùœ7uCxŽïÓã»ï¾£^ˆ·×Ò[ø}½}_õHiaŠ5<Ë"¦ØáÇÃk5Ž¢ÈbõÖzÄXýfm 鑼1S`Yd/ÄÏŸÙùkøá‡ëׯòÉ'}ôÑÙô8•/¾øâž={ÚÛÛzè¡•+WÖ×××ÕÕUWW§R©ªª*<ÀyL»zõê?þ0ÂÓJ±0$"²z¹úF~ž-ÀûšM2Èý¹sçxà›o¾9e=¦Î¬˜1¯¶jñ’šå+n]ׂ£~CÛÒŽ®¥íx€§ØL h$¾úê+"`ÀÛª ›ë\Rý,íþ±‡¢õ›·¼æü]غaí+¼>øÈÛC86¾7¼åòèwŒã¯xŒ‰ñÂå{{ò[æñ€Á{)Ÿ·;Ôßµ†DàA†-–Žúûúù|ß/¾øâرc°gH@±®7ì醬C‚±ÒcEωû˜\âl}&ÁVœW-öùçí•wà½Ö¼rBc p&JßHý}ý»×j Z.ìr¶êÏZ´8#ú²Ek6æ¨×øŽ1•ÄÛÎOÅWŽõˆ×€­Ï‹~}éšSx˜œEÈ„å?&=ïh~*¾6½1ð>!Ê“&OæmýŒ,ŽÉÖì쿘‰ çKÜb»ùZèî€åCqžšÆmï_[)J¯…Eä|¾ëëk èÞ€ñC–Ï}‡;2ÙeÏRøß…p»¼¾Æ€¡Œk×®ÑòÀ<³|T²( ÝÐ|ÄFÇt>ßíõu­„ßQ° €ÃøŒ4ï'‚éyù‡à®<ØßáJXeÁMèŒ[ìl¾Ûëk „+|…ÿn«nZд+E~°üS67O.mMvÙ|ä†qAä¶Îw{}p…/"êH]¬ÔWèïï§åaœŽ1;{=?l¯žË"ê|¾«ëk ƒTÅùù¶ZÂs­„°üÃvÏ[þÇì2»[-üyT¿A±8Ÿïêú Ñþm!Ï_5Ì~ˆinùWJdǘ2¢/›àúÎ绺~*Œ#¨ê€b‰WÁ*ê4~„ÛþEz&{ü—üáùû¢¦˜´ü+”c2‰t3†ÞEp0‚º~*Êëb±ÜÍXDI¥Ÿ¤lV'|ûí·£.­­­mÛ¶m¨QCÐæwÞž˜˜ xû¾à¦L™2eúͳÉ[µôD]®ÜÒùx æ~ýx` À²X°Šºo0­ 1­ô´¶ô¬iêºkqKý‚¦ªÊÚéÓ*Ì4”ÊÊJ€dݺuIww7@bf™HªôTÄàÖ÷;™ €·kØÛÆõÃÂ@ >¢_-^ïëç>óÿRxþHÏ豃㙣/÷ç÷mz²}pÓ#‡[[º›îiHæÍ] *æÍ›‡ —â­Qþ=ƒø$ù対_1og¸Å!\_ëäè6°œC”Eéwvløéóx­š€K! „íÝÖëû™_ÓØDnqà×OEj1|5³«']ü®a¬Én®Æèèè•+WFÓ£µµfD ˜t‚yÂÚ•]jfU@äù.ëõ½ÍgD‚àùYÌH^¥P ºâL܉>n1ÀÆòåËa؈k|ßøñƒ– ˜FØ‘Áê]@KÏT2û¨×÷6MšH`(ŸÀù‘ ð}ùå— =`eþJOF$uU` *X´hœàœd[éÙSx!†ô-( œË×z­×÷6ŸˆFýiàüHµVqk üº¯¿þú÷Ê¡P%e Íœ9˹Äè³ÿ·¶f ¬óõÉϦ:~Ÿõúžçgó¢ï`\ºt‰Bá91„Ìÿ–”-d˜++»ÜJ?p$°éˆpq ¶æäÏ_½¾çùÅ¿>`þ@¤ô€t!§`aýâž¾_0Øæ>o”Ø`·âÛo¿Å]BÜÓ”=‰×Âæá¯<22bÔ6·U¯ïy>@˜ã×'†?`%÷Bº§îŽ%ï~0z~d|ïs†IŠlŽy~‰c€Oãu>6 ZøùóÜSHàµàþJraÁÕë{žŸ#$‰? VdƒÞÝÐégÇÞ^ãF í/Ì× M9qäÛ>]Ð7~àÃïÄÂÏraˆ!*/êø«×÷<Ÿˆ9~}2ø ïI{|áÕkZ>¼lˆ>ý¥cûS=­̶iξg†m¬ <Æk©Vi2ÔÃ¥“Á热×÷3o‰™_kþ€UÚ‹€×<Ú6$Dµ¢ t뜦‡óÓd° ðZ,ÿ¸`cc£˜ Žày>”ƉâüE6vìØ»¿þá¶œÐ_ÀÀ`€ŸœÏ¯± ƒuG °»·sçÎ\¡„%€5v”$¦í€V½x$$>€çùó›[ ~}Äù~9>5¶eËÃëÝÜõ¡IîÙö8!øÔ H¨±/Žèþ¬Šy‰ï³Q8Ö4u`a¸ðøžçç "Ë𣬤?wdÏÀ^ÂKà=ûÁ@Dö†äN®^½y.[È( ?>)Þ œày¾@$ˆ(À3¨kûSÝfA·DBú$Áà7Þ(q=@êB QvX! …wÞy'”@Ææày> ¨^#ºüoXµj¾âžVf€ áü]÷®`à͈2yÒá'FR(Ñ稆•vË–-Ë[hCãxžŸÇ¯?P°žm4©_éb¯8Ø|äÑpDÇKsç€çùŒ_. ¨žm¶¯EbŸZ0|iÀÖR+$’  z—&è¶³ÍÍ3[œWÖÂ=€u倰j(v>~ 4!œ8½}è›Å-WQ(aЇ†Äð<ÛVg*&Bå¬ÿfg§ˆxùÄ EhÂvùçÏskjgÍšõé§Ÿ–¬À²Bý.Cþu%P³` 㶘ÉÄXø¸®œw¡Tê¿§N=A™ày>¹Å9"AHü@ú‰;)Ø5êK`ëÖ­ >TFE¥†ÐïOÃ"·¸;ëøé8FØ+ëгÏ’ày¾Ø¨/ü˜­ß=Ïõ[-üVÉzðëSC3fV'Pšq!Ÿ}‚që*0Àó|s£¾„𠲌^–<•ÀL¥õЀÊì,áñ<Ï_Ã5êK ¹¹Yˆ“*ÒÆS—ÇS¢íÁ))½Û•^œ(,Àó|±Q_Âø”.øÅ‰AEð‡@‹±_C Èõ†g )úQIܳ¤P|Ïóóþç¨×'3€eÍ`á(<À*rC‹¦˜‘.Àó|£Qÿ±£ÌP§¥‚ûÍ7ßÀ¶ XñˆQiÞfÂj{-ëÒqñâŲٳן|³X|Ïósú¢ÏpÕ ‹=€q#0@ÄÓ¬¶íê&f½ZúKj¯EoÛ¨àN‘ày>ñë‰Huþ€C_Í<ä òG„™LlÞZBMÏ×â.(.ÊÚ‹Äð€çù"ADø~*Û~€,׃:Ù™=pÝk HïÍÙ½{7…VŠËð<Ÿ "Çj¹2ŸQw'ÇTƒú~Òœ o¸Ø|Ïóóˆ‘âø7[­lYaõü‚¹õQÖV÷‡9ÄKÚ;‹Îð<ŸøõD$ˆqÿðlu%½Ž‘C¬h©eþ›!Äš?`ºM.0*6«*Àó|ØrbØØñÂÓê-Ý5ÐY̼;C\øæF}1æ„ç›: –ò€CLí~£Àð<ߪQ_œø…ôtÚ˜ ì«nN²Æˆ?ÀŽ\£>?ü-QÛ›‹ÿls&ê7Rï¾zõêUÕfrqà°ù’r·ü¢„íÕ"®¨q¨ÊÔ ‡8R|ÏóE"þ€.-.ºpÂp›á€¤Šð,ö²Ü®0&ü¾QŸ@$pÍÐz À¸pᆠPûêWl*ƒ¿Ýéq,;NeÇÙì@á 6ŒA19m#é_€;ÆïÏGþ;“kÔç™? 1P` à+Ÿ1cî¼Z0„èH9“&M$üc ªªj)ËGƒày> ÖX4ê‹AÿÒÄÀc]>§cxlœ=¾~|îò(oþéüÞÆñÚÛC8~™.Ù·oŸÃyà‹üȨñ<Ï·ä×Ç¢ÿ@ úÄ„<Ñç >ÏÚš]8WÉ“Œ-1>€çùðm°ó\Àü=BÕ;öö°…_z^!˜Ö|ÍíNròYÄøžçã»dÜâùz„Ší{ºoÖ ìÎÉn,I)>€çùD$È5„? GØzÀ¼ü§ÏLXéf ¢@HÊ5›‰-€‡[|ûƒäâ\ñϸJg–ÚÀÖI†_{è0¿À n1ÿ/ÿø\º\1Sÿ äh;‰HñŠ5?¥Ù·…Ø/‡¹*°´‘Î]¾Bðc3E£Ñãe~*Ô]4„½Ã LJ^PK¿ô_¦ü+XÆ$Ž (óÃÕÚ’bà…, ¿ÙaøCºFÒaŽL=úûûÜ¿?îóƒÇ@z¾ öVçðð¹¸ð5s±æ®ÿ@€$¬X Dý}ý8<¨ùð!ãKr?lëpÇoÞ:£hÀìêSÑÎB¹`blù…ë? ›+ÔÇGêýâ½­„—ÓE??`®¡x-]CpÛqL###y»kÅ™?P þº¹±ˆ9ß/Þ­MB;¨B(Ö{«|Û¼`À  ÆŸ?P þ nŒl!oýíÔ áƒ4[Ù?™Ç2„¼œ®™ƒ&QìbïðSÌLÃ# @ý¤;¶&8j¤þ¾~6óÊ`à­3f‰Wøôà—Y ø¯•6Š5 ýtöM®Æ™@90fƒWãÚµk¹žv1çÄ ÿ€ŽºòƒöÐF”S%î1¢^5ö¡ö›>¿>í+±ò`2ø¡ôˆ¾«ÓìÀþÀ ¯ä0àÿë×aülc2ø¡ôжPHƒ’S+ï_»z]Ëú m8ÚÚ;A©ÁbR:PM´ï`?þBèqÀ¢£}û.¼öóÏ?äëËiõñä„Ò Ž®j,jæÖ®]»`Á4Þœ•)7k÷õë×Á߀#îüPú¸júvHzÀÖ°o¥°OÝonàDv|‘H)ÐŽð§™3àÿ3ç¶š‹? ”þž1`Þʦ`©+Ïõ…t-ÔýÌMFóý˜C³-Öü¸ê·ûúɪ†÷„ÁwQ4› ê3‹ýÈJ–?G xè?‘0«ºñ”U³)é|ÿF4‡J–?T=àª/r±ô€ð!Õ ×Âðar=ºK™?pC@›Öøi’’·­ØÆ }±=«æh ølZ Óâó+¨Áðõ×_#PKœjD ù¥Žņϱжý øÇ”ª›6mâ­ ¼9¤ù¥®¤ûýÇHØö7ÀÚ-¯€;›îª­»ÙºŒ9ô§óš? 1ð}Pz ¨$FP_]ÕJ•••øöž®?ŽÿSß ±kù¦M›„¾Lš?PÌðy€Ëd ìü|_á²ÎȈÑòÿ«áßt<ܵ'¡ ò¶ÜÒü‘©Röc.—ÚV_ = ,ÿ8Þ?Ç`°`ÉÂéÓ§K£C%ÍH˜( H}ô¹1Vú% ÿ~è8“{ 8OysHóWø»†ŸIŒà­ ,ÿT—ŠåŸ[øÏ HÀ ˜&O–•, "†„ÿeÉÚBlù?zæ¸YèÍGÕœêI“'·½Aó k/XEéØBùÃÇàïJÅݬpæÐïâ%™ Ù5 X¥¬6«4‚½ílù?0Øo’þsœP©‚•ëW³žïš?˜R6·\& ÕÂZ”=,ÿঠËÿÛ2ëßB'GÙÌr1_¦ù'k´i ´lFÒ^Î%Þ|¼ð¦a±Veš?¤?`ŸÑj”Å€»åŸŸß°¶Ñ¼íŠæ$? ™(âþÚ];ÇñÂÎ å3Ð¡ÃØ•ZóJ' Y0Jt¨>1mb·¶­Å•d~ ÑŠTêWuõïäùÇš?P2ˆ© ›ÞÌ"zÛz¥WƒáÃáßý{u%šÙÿmzÙ•öNÍp¤…` ñª˜ÚBtÿÁ,"©å£Ã¿ þ¿Šr`Çç÷¯Õü×ÁP­Ѝpÿ‘“|޳gOü[UðZ?ÕÕkþ@’3J±Þ²N±‘EôìÀ~ó’oÅ«Êf–5¥RŸ´¶~ðÓŸf¶§Öý‰„U• s°ˆÊ+Êó%ÞÞ€ö€Ð㵬Óç¶%Õý©’„~PÓ§f ‹È¬ žèfðP+ÍשÛЦûh ÄL³±}SöôÚ.ÿͦåŸÔÌMktÿDlamåóÈ":5ú–¥u"AÄ÷íÛ'ÍëS£¾¾~úͳuÿˆsXL3 '@¾oɬhY)Õ´ü/[¶Œõw²Úb ×™2e H6"ßRót@Ó­«êj-÷óF‚EÄbDd!‰FËÿÎ;qëÔ{Ñõ©•%j(ÐÍRó4b x±^·na]y‹ø´—ÄüùóOŸ>mµÉ»Šèf £(ÓÐRó"‚è'w ¶ÝÔ’¡iCEXDlùß²e oI*øÂÇ@CKi³ž–š? õ@ÀzÀÏ–6êmÂÈ"Â@m)ɶ\«´U¿-WÉñ’WôŸ°ý”¤†ÍñãÇŸ~úi…ºpµÉ{{{;`°´£«DùÉÃ@DÒÆ~*¯\m¹îj‹v«w¤¼A½9}V üTY(|ÉÜyë?àd¾âMiÿjp/KŽ?H=[(¨ý®ƒú}©©Rï¬tøÚ Š,8ãß²ê? ƒÛ»122 ´.“AÓýCù#k¹CIU‹ ºBHm*ý ¤ª««‘A3Rš?,Μ9ã §³ñRûQz )†y‘vŒH}ßëׯSmÕ‹G4 8z "ñM‡@EÀþ¦I“(fq‘Êݺu+\LiéŽTDðû¢Ùkcc#¹|.YóÀiƒXÄv~H4Dš9mÚà–öÃ>Òv÷=KæÖ¤¸A¨ {G£^͉fˆÎw„—\WWG»—æ ùÚ>$ªš›n[0¾ÿàxoŸñ7} ?ù Pѳ®¥åŽÅ*ßl¨}ˆš?`„Rî? öín$}Xed!]+V2éÏ!ƒŽóO>3°ñ1õ‹jÓ½dh=z4_H 'aîò<J«ÿ€“ºÜé·¦"6·Yèm3ÛªËÊ}ôÑÝ Q£ŒNøí©xðBí—¨[å½û¡®íYÉ·Y'äã„X¿±»ðø3H°Ó ‘à„Ñ?ÝU-J²õUqŠk|oß„Bdaäàµð bzCx$€Ž]þ@°þ€¢J±¤ô{ ïÆ}N²{-ô€ì)Ä‘ @°¦b}[rHhlZòÍÒâLo4::zåÊ•Ñìp•¬U¯®B–tAD~DH †¬ÉO¢Ó&qÂ8yò$P6{6¥P‰ jN޲>ïDq0P€ì¦ ‹£ùC]@爵ÞU¿yPÈVú¹QE\µ¸Å­Ájò‹/îÞ½›èi4PsýHÀm R<üÞ÷†{³pæ£ÑD¨Ë¼_±V‘°75ä’DŸ©‚ëéÕóÍ“‘ ¶ )¬ ìÉŠiÓ`JÅ”ä`;Pw„ Òž={(¢JÕx(φÔZæLØíèúŸš[þsaÝàŽ]ð:ðÂ5¯  ÕZÝÏ6âjtÙšš˜d¨UI¾-Dr)ÕŸ}öˆ |y‚‡zzõ|ó§¢ ÒaJ³‡;Ï=5 Å Q2ÃH@LÁY£tƒˆ;KÛ;¡(ð`~s ½uOA‡=×Ò›f㸚Ê“§Lž1«¬®aÞÂ{ç‘Ii¨å¤ÞA©? èô0ýÉOŒ¢ÜhÏõôn•mˆk–o0¤Ÿ¢ *íbJøTÔ`×ÖÖvttHŸE1öwÁ/63¼ˆŠŠŠ|ñNÝ4uòô™S+g—¯^PõeS?ëç/?üüÐ/ ï86úÌkã=t´^—@ Å›Y”aQà.üݾ_ß¶ÜØj %kAÕÓ«…@™ Ú ’‚BTj©à¯~#̵qãF|µÚ[*Í{žZ]ŸXl}gŸ`’ÍŽí_ÿ_M·âØðÚx7Îü&ý—;6Px-ÉP×hP„¾ññž®·ÆqÜùPm¸ÉJqœ·w‹ÌLPÈaz8?M†ê:”ÓE*¬ìŸÿÀö¼èyjÝøåÞ–5w°Z=$ÁL,ði±î6#Avd¦íz‚iÔ„c@:.]º„ï[c ¤¿óÇ Í{è—ÀU/B>?•ñc¯k±s…åA!…"…Ïüˆà½÷Þ‹ÒrÿçŸÙp†ü~uÅ8Eö n=É—cÈa¿u’1 ­¿Çƒ¥´zÁ}þxìÄyœç7  œ­B?Û©ÍK$^ád („¬ÑÔбù~Ÿ†ñSS9ðÒF’~(zн«™i5º¨ìjGÖÚ1I|·•ÀøÌè*9=s_¾õè/ýLà¨_ÓFë•:ö™Í…Ô¶ bî{”ï³Zà+`2’$›WÒÏ`F£­&1~©ž5ÎM þÀkŸÑìk$ý%(&³Ç¬x$¬Þ~˜Jö¥ûñûùTÔIÛyeÿ/¨x- ¹¸EÍ·÷³¹ÙXã›n:Õ%Š>÷ïà‰-$£ê0Œ¨FW£µ¸w[ÛH=ÕµX錋DÐÇà@ óR%¢üy¼Š9‹‘-¹µÊ¸ã-@3§î}0'îV0¸ÜÛöðÝ,tcå[cº§¥^¶Òw[h€ÜùùKæÀ(XDYG“!^³´ÉVúÙñø£ð›ñ*f‚ûÿ‚ï¿ÿ>Bà|T{ñœ¹ˆö*`í( $ ÅhÓ>€a5ì–‡ï6›=VÇè{ ¥Ýܬ0«!XÔt«b±Wx!>U0z š=]Ì=ꦖW@¬&ô â§BØ4íLBÃ`iÄRDÆ1 x½„ ¤ÎtmË "­ ¢u+âào;nåa–Ü1×0~2kü~«µ_xaŽÔñ×ÇÅaÒØF»¥Vµ-£@sIèúÖÌtãï„…ÿøa?FQX8àÎÐ9À›€ ÔÉu561..zYy>ð/®ô—{'z ;à!I•:¸]eÓ,£@*`À‘ r‰T׉ÔJÐ.&,fk Ø¿¦°é‘#G7—¥' ñá xë9sæC¬(á¶²”øZ)ˆfxQ þ3Ц¼g.J¶µ+l~ ÚC¿¹ËŽiÉ·òò,%V.‘ðš9jÚNé0'Vú¸ijÌÓdU¥CˆÕºº±BÙlx?Šð)d»Ø[EHé çX££X3_äÜ7`åIæP: á[ávòª¿¾Cƒt¾«èÏ2c ÀBFᓌÞèNúM`@4‰÷…Øõ)Ö·ÿÌv É¿ÏgË%’Ì0§ÃÜ.ül>yÆ”V P¸%ºZŽê ¶Òá?* 9H°½¬K•@¾sÌ—÷ÒõMåÝÎÓd¬\"±¶9dwmÜ%•{ÌOÁ™æ+XÏܲ%ºZŽÕz $[ˆï›K +Íã¼`S™ké¨\–½“Äpö|7Ÿ*6<ŠH­ô®„C})Ü´^Ä—DÒW!÷¶`@8A!,f¶‡ £YFDV"«è ~ˆ3';¥Ñ!•·À=Kµtæ÷t¹ÄÚ¬õßí Æ|êNûÿ_?õ“»‹„IEND®B`‚Chipmunk2D-Chipmunk-7.0.3/doc/images/hms_logo.png000066400000000000000000000270301347650476100216070ustar00rootroot00000000000000‰PNG  IHDRƒƒ®¨f pHYs  šœ OiCCPPhotoshop ICC profilexÚSgTSé=÷ÞôBKˆ€”KoR RB‹€‘&*! Jˆ!¡ÙQÁEEÈ ˆŽŽ€ŒQ, Š Øä!¢Žƒ£ˆŠÊûá{£kÖ¼÷æÍþµ×>ç¬ó³ÏÀ –H3Q5€ ©BàƒÇÄÆáä.@ $p³d!sý#ø~<<+"À¾xÓ ÀM›À0‡ÿêB™\€„Àt‘8K€@zŽB¦@F€˜&S `ËcbãP-`'æÓ€ø™{[”! ‘ eˆDh;¬ÏVŠEX0fKÄ9Ø-0IWfH°·ÀÎ ² 0Qˆ…){`È##x„™FòW<ñ+®ç*x™²<¹$9E[-qWW.(ÎI+6aaš@.Ây™24àóÌ ‘àƒóýxήÎÎ6޶_-ê¿ÿ"bbãþåÏ«p@át~Ñþ,/³€;€mþ¢%îh^  u÷‹f²@µ éÚWópø~<ß5°j>{‘-¨]cöK'XtÀâ÷ò»oÁÔ(€hƒáÏwÿï?ýG %€fI’q^D$.Tʳ?ÇD *°AôÁ,ÀÁÜÁ ü`6„B$ÄÂBB d€r`)¬‚B(†Í°*`/Ô@4ÀQh†“p.ÂU¸=púažÁ(¼ AÈa!ÚˆbŠX#Ž™…ø!ÁH‹$ ɈQ"K‘5H1RŠT UHò=r9‡\Fº‘;È2‚ü†¼G1”²Q=Ô µC¹¨7„F¢ Ðdt1š ›Ðr´=Œ6¡çЫhÚ>CÇ0Àè3Äl0.ÆÃB±8, “c˱"¬ «Æ°V¬»‰õcϱwEÀ 6wB aAHXLXNØH¨ $4Ú 7 „QÂ'"“¨K´&ºùÄb21‡XH,#Ö/{ˆCÄ7$‰C2'¹I±¤TÒÒFÒnR#é,©›4H#“ÉÚdk²9”, +È…ääÃä3ää!ò[ b@q¤øSâ(RÊjJåå4åe˜2AU£šRݨ¡T5ZB­¡¶R¯Q‡¨4uš9̓IK¥­¢•Óhh÷i¯ètºÝ•N—ÐWÒËéGè—èôw †ƒÇˆg(›gw¯˜L¦Ó‹ÇT071ë˜ç™™oUX*¶*|‘Ê •J•&•*/T©ª¦ªÞª UóUËT©^S}®FU3Sã© Ô–«UªPëSSg©;¨‡ªg¨oT?¤~Yý‰YÃLÃOC¤Q ±_ã¼Æ c³x,!k «†u5Ä&±ÍÙ|v*»˜ý»‹=ª©¡9C3J3W³Ró”f?ã˜qøœtN ç(§—ó~ŠÞï)â)¦4L¹1e\kª–—–X«H«Q«Gë½6®í§¦½E»YûAÇJ'\'GgÎçSÙSݧ §M=:õ®.ªk¥¡»Dw¿n§î˜ž¾^€žLo§Þy½çú}/ýTýmú§õG X³ $Û Î<Å5qo</ÇÛñQC]Ã@C¥a•a—á„‘¹Ñ<£ÕFFŒiÆ\ã$ãmÆmÆ£&&!&KMêMîšRM¹¦)¦;L;LÇÍÌÍ¢ÍÖ™5›=1×2ç›ç›×›ß·`ZxZ,¶¨¶¸eI²äZ¦Yî¶¼n…Z9Y¥XUZ]³F­­%Ö»­»§§¹N“N«žÖgðñ¶É¶©·°åØÛ®¶m¶}agbg·Å®Ã“}º}ý= ‡Ù«Z~s´r:V:ޚΜî?}Åô–é/gXÏÏØ3ã¶Ë)ÄiS›ÓGgg¹sƒóˆ‹‰K‚Ë.—>.›ÆÝȽäJtõq]ázÒõ›³›Âí¨Û¯î6îiî‡ÜŸÌ4Ÿ)žY3sÐÃÈCàQåÑ? Ÿ•0k߬~OCOgµç#/c/‘W­×°·¥wª÷aï>ö>rŸã>ã<7Þ2ÞY_Ì7À·È·ËOÃož_…ßC#ÿdÿzÿѧ€%g‰A[ûøz|!¿Ž?:Ûeö²ÙíAŒ ¹AA‚­‚åÁ­!hÈì­!÷ç˜Î‘Îi…P~èÖÐaæa‹Ã~ '…‡…W†?ŽpˆXÑ1—5wÑÜCsßDúD–DÞ›g1O9¯-J5*>ª.j<Ú7º4º?Æ.fYÌÕXXIlK9.*®6nl¾ßüíó‡ââ ã{˜/È]py¡ÎÂô…§©.,:–@LˆN8”ðA*¨Œ%òw%Ž yÂÂg"/Ñ6шØC\*NòH*Mz’쑼5y$Å3¥,幄'©¼L LÝ›:žšv m2=:½1ƒ’‘qBª!M“¶gêgæfvˬe…²þÅn‹·/•Ék³¬Y- ¶B¦èTZ(×*²geWf¿Í‰Ê9–«ž+Íí̳ÊÛ7œïŸÿíÂá’¶¥†KW-X潬j9²‰Š®Û—Ø(Üxå‡oÊ¿™Ü”´©«Ä¹dÏfÒféæÞ-ž[–ª—æ—n ÙÚ´ ßV´íõöEÛ/—Í(Û»ƒ¶C¹£¿<¸¼e§ÉÎÍ;?T¤TôTúT6îÒݵa×ønÑî{¼ö4ìÕÛ[¼÷ý>ɾÛUUMÕfÕeûIû³÷?®‰ªéø–ûm]­NmqíÇÒý#¶×¹ÔÕÒ=TRÖ+ëGǾþïw- 6 UœÆâ#pDyäé÷ ß÷ :ÚvŒ{¬áÓvg/jBšòšF›Sšû[b[ºOÌ>ÑÖêÞzüGÛœ499â?rýéü§CÏdÏ&žþ¢þË®/~øÕë×Îјѡ—ò—“¿m|¥ýêÀë¯ÛÆÂƾÉx31^ôVûíÁwÜwï£ßOä| (ÿhù±õSЧû“““ÿ˜óüc3-Û cHRMz%€ƒùÿ€éu0ê`:˜o’_ÅF#CIDATxÚí] tTU¶¥WÛý[ûc+(6Ò_¿­- ýÕníÆ±eP•APdQ „@2V*#„@&f„„† 32‡•™€8¡¨ ž¿÷­÷ÂK‘Jª’z•ªÀ]ë¬J*•Wïݳï¹g¾5D¤F5§[A÷€Zƒºü@Á (P(Q{ÒÞ÷Ó>×Zû¿{´ëT빪nTô¨h~N>;pRíøRæl: ÓÖ핌U»dü§;$ié6I\R(£ªWþÎ÷ùw~ŽŸçÿñÿy^×åõŒ3Ú}õÕîóLÿAù óö¸x銜þöGÙúe‘äoÿB&®Þ-cÀ\Kî& Ÿ¿AB³×Kˆ¯aøïEälTIʵ½êïñïavÿÇ÷x=^wâªÝê{ø}ü^~)ã¼v¿}e;ñö¤ò6tâšMüÂ/jµ.(<,–ïkÞ&0ÍÆÀQó>“𜠵`“[É"pt€‘ø½ü~Þï‡÷UÊ8¡=G£`pšæƒ.gôGˆèǾRâ{ôâ­Å«—«™Ì Ç+0vi¡bÁAf¹%iÁï×¥ï‹÷ÇûäýÚËÚs5»†ò©©&ZKŒc_ŸÇÞý˜¼­\¡:ClRa½D/ܬV'•À3ß]q˶+p˜ #ñ¾t`ð~yß¼ÿRF¾ö¼7À`GO²3uéÊÙsâœÌølŸšäƒ°'NÄùÏB'Þ/ï›?ó9ø<|.»‘­=ÿu†º TòÞ‚GÏJ&”4îûašâæXLcÂç­—#gKw%?w^1Ä(I;P\Òæ¡îõ†ß€zŽê³ñ+Ì5®NV˜¦Í[Ê™d®öYkaú•í^Øpð”úœµŠÀ`T@õgãsòy-i¦Õæå7× ‚rJ¬^ì©3×ïSb•åÌÄRš³N¦­ýÜ)ïдuŸKÐ\ÏêeQ˜fÞò¹_«SähóT­Á@›»¨Ø:øå’ì>"Ñšhqr"ÉPn 1ÐÎÿtÑ92L>Û>¾Á+À K ¥øây8œÃ(Òæ«Ú¡&(Éø¤N£ìóŠìçÜ{ãò·(‡+cÛ‘322k]•o¥Y œÎçÅn$ióW-ÀP´I2Æ–î:R¼º:qddÐÜu²÷ä׊8ÍøLÛ.¼ úÖÁyáüpž c“6> †fF%‘+y\ÅjK¨ v¯ÛïeÄÊ_ÿøs±:Ê aÑüœ';ÉwÔlg•™@èoô î>þ•Ä/Úª¼‚•™¬@(\9•Ÿ< ëb×(“¥ç‰óE'š³¿¯Áßh2®ÚwÜæÓwÃj †Gñð™o+ÞSêŠJ õf@„kA4Ο êï+`Öï˜ùyÛ_ã>®°G׈…ñÓÅËRÙAsŽ®ê¨½ F÷6çñ—’zD°·ƒ!T¿Ó 0“foÜï’ÉXq%Ó_à®±pÛ2rÎ:¯T&íMPZœÏ %ÍÏPoC±D`“CB5¿¼;ˆâœú²ÝGÝNl,LÔp/ò=”­G¬WóúCÉhh°·Áß„)k÷¸ºI “Ѝ;Ƕ#gm¾‡¼M>ί ü½ ýEwK{o«Ž¦r•Iã*Ô½`“OI»-£Uƒ¡‰6Ÿr9ºŽ`–ým²÷í…ŸÅÝãÄ×? ëËŒŽz# 8ßœw®›Tè;¦ß…n5˜™@´ÿÞmƒ÷èʤ= xߣóXe<•ÂÍ BýhWÆ«è .¸Á¬,m0E 29} º·’óo…<†â Óî_Ù²‹s=‘ ²"ý¼˜56:mË{ÈóéÀyçüï>qÎ>¸å0t(Ž5|÷£r™zb5©ÕœÏä`Ñ·¦á2²X7bâvg–Ô$ÈÃè`6êëùô†éA'OídÔå+¿Š™ƒ®nF5} úü/e‘«úƒ+@¸‰N;ý›,òä ¢rÇ¢OŒO}äK¾£‡¶ do¡Æ7·ƒ¡§þ ‹¾Qû”ÅCI¦ÌN¢´‹ñ›6¾þá'å™$} –\[`Ën+íén0°<ì¸îÂeFNES*JÌu\ŒšG}¬;pB&¯ÙSáœg=|ó·R‰4ž(Æqg‚ ùcpHw¶¼ÏY0¤ëW.@lÀÓ ÃÖ,D9ƒºz ¹býg­±7©Lû‘†6É4#ç¬U/ø!ÈŸ‚’1œtwõmžºóHÞÜìñ2aö†ýêgÝmÌ{Ê-úî‚造—oñÎ/•—2${½×K Î ùd0Ã/:Sçé rõ2w¦u‡U‘Ù¥'ÎZíWÔ =5(R—ïTYRÆ{ñÊíóE~’br+ †õ+1•¡^‹=0™A­ó§=š¶köP•ØÁ^T‡QZ`ü²K~±¢``UÏb[ÉÛ¯ª'YJceDt¸¬«ôä(‚ƒgòš½J—P÷±ìÊ$ùvéªofqYÕZeá="ÉÚ@³€`ËÜ ”ÄŠH=é%kã~©Š±ùði›Å㥎*òü3D6_¨rôBØL“¤WÛçp•-ÙyD¹›ù=½Ü.*ZKQÙÁj­Yö)PVôþÍC¦’WŒ¥{.៺ÁâÐpbVͰhûUÿÁþSß áu‹…«ʇN cêâe©ªÁ´öx/tV‘{®².jüu ɶ”r[RXösÀ‡Ná‘¢kVÙìÅÔØ-®*“`BÚéTå`¾ENá!µm„z‰Jþ‘]²³`¨ :©§“[Lt;Ó\.2¸ôK q—fN.Ò3XÓhV®‚-еI¾ýñçrk#3VîRI'®X³M´.‚ƒƒ1s5uèÐAŠŠŠÊɱ´9«FâªÊYE>’Ÿ†¦c‹ìÍL{0ÜGð“»Ð­Ì ]Áå¸P¾€Žž<­à…L«“ÖCf€€€b@,]º´ÜÏÓƒ»v?U•³ªªtòUWo4~;Cý“l_gf⊠›]r±¿=ÎÄ(u8é+ö3f»udeeÉòåË]úv¡c݃ Ø=ºuŸä«aô) Yüĺ&¤ôØ»«©ï¬›H*ØæTYœE³0ñ,eé&U16ÁYeÑštx ä'ùjX€YŽÀp;è¬n/;Ó`«ÒÍ® ¶ìÛõ93èK N0Ò‰€‘®C°´™ù(u Oå†è R eþg5¾_†WôO,0¹Âu¤¯è`ƒo¦z•—|bÕÒæùÖ«À@hæl=¤, Onä¯a¼R"ôØ=3e<‘ñÌ )NÄ–Jä6ùê{åy *Ã맃!¿¤iUå@(εô°UAþô¨ˆÒÀ°Nß“¹Ê<•ßH1_Ùn,ôúqbG”aº1)†ñ¯¶¬”e;LµØéj䯡fu=îí솭_žñXú»‘ÈHæ5~ýCÅk)×aÛ`¡­ ǵ5sKjÒU>œ œãù‚_ò—|Ö ÿÅ`xNW/Ä9 U¢U%‹ÀÜÜyìl…'øŸ±Ò–'iÔÔf¯µïTåãØ¹ó××S` Ÿ ;ÖsF0ôÓ#L†Ÿ_5Í+¬Zv™½å ÄÿÅ O4û)2tK‰ÃäÙÅ^¤/· …zx¾Ãµ¤ƒuÕφñº š'­xCñ)AÅðóJæ(ÐQµýèñÖ±}­=ÝJˆü%Ÿ ®éñF0¬Q>ôo´(¥7ä7æÙlbZ ¹…×4·ª6ƒõ Ìdöä´hŠ$ù­5:ØŠö€îlªª-¢Ì8½ˆ@2+¹ªãPФ‡MÌð’Î'ò¿&Áð?¢ûÈf™¡^XÌdS¦~SÁäÉ.¥àáÓƒ‘Y[ÏGÏ*‘ä·žSDè§À¨A§Œ·–£[µà4)PºØz§: z"UçÅ)Èçü_oá)‚¡µ§"•î’tASŸØpèTµc4t«[<Ô¤´”fk‚¡«þn†Í÷þžˆzyó$YkVˆÚÓcþV[¡¯'tòÙ®ÁjW‚ÁOO adÏWd“XÒVì’ó?_ôy0|óƒÖñÞ¡ò™ü64?ñ#‚ô|=:@|©Á•¾mp¯M.Ønß(ÓG-‹/=âw ŸÉïKW¥jÁÅŸhÇ3iÄ×À û$(!¦®Ýkß}ÝçÆ÷8fIðLæùL~ü7QC‚JÁ›´å#} Å}`zº»ƒlU öV0»P¤VÍf膓@0$êÙCL‰ªÊó+«Tr»X¾ç˜ÏƒÛ®$›™:@~*ÐË• ü§«gMo0¥ÔÎh§k·:èËPDdfÇZG’Á¡Î@'ƒE“Öï—¼f ß`ú¦ƒŸÝ•üBpÙ—ä«÷ЭÕÕ2w½_ƒ;!¨ªq^“f%%;ÒZ Opˆhû·"$0c‰$O^&O?ÑOºM•$rV ˜L­›„"‹!g@7uÅŽfïËÞìÒކ¸³âtú(½ZTþ­:Hf™³pdM8ô3D‚¦¢öÁ»ß‘.gȈè¹ò‡/É;¥Ê˜Õ{ÊCyV ‹aÒQ `µl!q0 ÉܱHL‰·@îªÙ\ºúO”øOwJÀ2=Òp/ãa1Ä¢2HmgÙÛ¾”dä,ÄN[!Ó–³VɈ×UŸ÷y0°Y•˜°5;ò38ô@R\OÓ_3\M|TJ¾Ôú¯×¤GÐ4 GOTîU½¢ÄŠ¥âTø8ìIö€áƒ)fâz`pçnñrÛ^“OVK,Vx4VøˆÌiðèòAØLIÂ=Ÿ¹Rš¿ì/?Δ>'ˆeê§…kÏCh»{«¼ö^¬Œ_$Ï=í'O6ö“Ø‚’€ëT‡°7ë#͈Y8ò@:ŒM ™Ù{ŒÁo븅RŒëk™- 1d¬Låj “‘a@ÜThTÄ 6!_1^æéø[.ŸÀ 5]î¬ù¦N].1øÖNƒ‹9g×Q‰Ø§ÄˆÆuÛͯI,$@ó—¤îáãä<ÉĽÞ~sk ž¹J&î8"~#&Kã—†Ëdñ°®ÑбÄw½’ú9œ¹ž‰M8ŒZ “Áäþ“dDuò”O#¸b +˜•‹ FÄΓAÃ'IÔì5‹÷“ÁÐûîî(ÿxbÀ°]b°Š£ñYg>€ÓàÞÎRÿ÷%iù. ²Î‘:zK‚¦¯Ä–°Câ‘¢Ö­[‚üç™AÒ¾ß8KÉðÜ]»½ŒÇu;½#úÝ«ò?"r¶üùövÃj‹ëú‡Ï&o…ÊX‚¹…SäjÑ„ r³ea‹Z~yMÔÒa>ƒ†·;EËИ¹24pªÔùï7ä]¿40}§ä`5vz;BjÞÔJ<ô@w‰ÀЇ´õžÐ+¿ )Øç§bÅG‚9±âƒ#fÉŸ~ßZ5$[‹È ©[«½„Ü&¬L»KY(úm+iÙ!J’ =Òñ|çßðIˤ+¤ÁÐÅoÄ$õs$N:À @^j©ô›I¼Aå T‹ˆ&+ÜÜhY8Êgp˜éD%c”¼>ƒÒU®TüíÏ5ßÖïÆÊ„Í%Œ¯ñýB‹@ K_"w(ÿi$‰X‘ѳVKïЙ²êði ä‘ÇûI>B-^!µo~]ºŸ,“¡- ž&õî|[€|š¯ÜVÒp£õnk'-ÚEÊX|&á.l%Aãóåƒþ)rÛï_•nVC’È ­¥@ê—-mñùV.Á çQnþ¢È'^^‰ÓüÝÙDÌQ¦“ÃHþìÌPÜ(V¦€Q÷`sÂgì=.½Œ—[ûŠt9Ufï>&þÑ_þvÿûjUŽÁ‰²ÆeJm€¤Aý÷åw¬Òà»H(L¦Ôd8s÷ðûZ忈‡Že².¶Žf¯‡ÊH¦ l/wÝú–NX$†dÈm HÎúü„d¢T?ÊULà@k$C„ ZöÜÍ| i+wªs6“ßÚÎÑŠÊ57Òav4Á0 ׉2 ;`¨w[[iŽ8«ºGßd¹Œöƒb9bºy³ù¶ ꣡c„&-;j¶‘{ÿÜI"°J;§Äÿkãd4”¼q ##g)F‚É€*£ÄÉŸJ?¶‘!q±Íd$õ Œc+ ûŽš)á£çI§~cÅ‚¿†IÊk5k^löòþ“áX1ûŒ w  ™± G8v˜M‡”tG]fyÙÑ¥ÖMp›˜‚jç-åp"¼‚uo}SþõüPIÇþÿáÐLì÷í vIÁ¡"iüøyž Ä5¬øl,VlÃû»J“Ö!JGÕcô–Ac¨í Ÿ …x¿í¦W¥túò q¢bæHM€æÙ&Ãe,¼Ÿ¸ñ»ny]'æÈ è ¿«ñ¢Ú‚>ÄöðHÃð…ì‘l[½?H”ÖïÇ+k"*÷jCó?y¿j·â(öaeXiÃv0Ê:·leÕM”ZQÅÕ• Ÿš{ P›¼d›<úpoyâ©”ÆîŸ’'Öï& Ï”‡f(Ñþ1”?å@âQ9XåÃSËÈ©+”>€„R+Æ+¯=àƒëÖn+¥ÉDì‹¥ÊSÏúÉC {ICXqp&µï›„kçK$¤ã ñ4c¥„câ^j"mÖvïÅÉ›=aînP××ïŸîÜs^Ö›¡ÔH%½Zñ0_U¥cáU65±¼Š*‡µ–œÐ€€qî5ø[4þM9Àœ®SäéfÃäý‘Sƒu­—¯ü @4²AæB­óX`Z:^‡ÁaÄ­…~‹ \;zQ¡úJ Ùð?J"=“‹¶(Fsåóï4mé·âï1øžÑ0U­è#©ï«¶&b}"¬}Õ䣿®/öÎ2òx)‡³íDžÊº¨Ë«µtº ›L‹Ñüÿœtîód%…-^áÀã¦dA”?ñpiÞ6BíñéìRo™)ü&¨­E›´_ã—m/á´*¦Ü’Íq+múÇhH1õy¹?ú‚d¸b(µ³j ãÑömŽx¨sÍUu¦ »òýrËN΄eòÌÓI˜‰C þ—<-/þÛOÞì5F­üŠÄä“ÀäI21ŽðÑ9 W5d”lˆ ýéâ%ñ…A»_ï× "§Á*»d—ðËFaMs¶?ƒ©[ôJ…"8pØDåœjÚz”da5×»úÂÀTId¬CSn,`$·&n#FQ|2¦A&·þž€uy?Aê!˜6jl®<ø—w¥IË‘Zøw“Òš}¥è†R ÊÔ¢ÒùÀô¹¨e}ÎŒÎ-¦öt"ƃ‘ÇÍ“¾“á+xE *Ùä…¡ c•dP~C«ž ›;Êá®´`ï'fæ<ø&`‚ÆàzÔÆa{†ÀYÃGúHöö#ò(¸wÂ16± *²×™ï/ˆ¯Œù¨>7:—V’ÛGa+’VïJO'S»½Eâ¡R°R{㬷Äg­‘¦/ “—/‘ÉNó3c1üí¥6¢£O"¶Mëz õYù›åáÞ“;àÁ¬_W Bt3&nÌ̺wu”IØ2ý³¿Ôƒ‹`H€5CÍ|û1ß XQ‘´ïI=ŒÏaèáˆ6Ë®÷vp¥Û›©} #´m¢çt‰cÄ¿OFÖT‹f#䉒8€a>Äáãÿ×GîýKgé©Q>Œ^!1Xáó·ðÚ+R«¾üý­»<üP/™€m"(*Kî„3, Šmcè$ôhÒ ¥ªšƒî?á3` éŸVŠùȽžï±‡•®pNp±Sœ«} MëTN„2Ô .ì(¬tîw™H—® òÈ?¨XG\Ú¹A¬fo„K”Ë—”ºÐ)°R&Ñ÷Öy‘ÐeSÝ>H’[ozÒa™D½`Èh^|vˆŠp…‚J×¶/&ÉÒì³W­šˆçÏú¶Ç¸®D3]íkZïhZ<: ˜ÏÔ4júLnùп?ÖOÒÁþ‰ÔÞEüaú0ºs·HD¼âNl­:Z%½­ƒú®ù›V2> ÜáÁÖ ^Ë»kw+NCeÚ VÅ`ôX¥ o¼¦>$D™œ[•¹ÌàV¨“îéŠôŽ6­«<Á0ÌoûV¸|Œè#¥Á0"ÿþûº©Ì©H¼O†ƪÎÚRº#&rß=ïIÄý8€§<œ]áØšŠU(Rðj!lPVx=kÁ]Mï\Ó‡©œ‹aC|­Å¿#÷®7Åz†S)Cä ;çó3Ô'œ9ã«"]å+tÞ„3qv*/S¥ü÷“äÕ®±Šù©p:õEô±îí%¦d$òëÜú†ø#ie>Vùcv—W;Ǩ¸F®ñW˜ïAZ,X·WZ6.O>3DR°Õg,•Ç „ºQz,Ïào¡³×¨((•±}§¾ö90ð¼N2Ðâ œ+ÝÙ†ä•9oÂé“hò¶ÈØû0ñÛ3ì“^­x?f±–À 7ó°ô¥*M->z/G a¥Ÿu¶r5ÓíLñÞ¦Kœ<úøÈg¬<‘ê³d,²¡ÚõN‚Â9@>ÔSF!^ÂT9«–Çûák"¬•è…6?„/gJót¤¿mR%‚‡ˆWù””ƒ‰¥ø»ŠÝÑt<ÑWÀï²0‰k`~~vqˆÝ ÅR)ïÚÕð;úôÕ¡ân8者gT•{zE%°ì“ÐÄTyèŸOAé EB,˜ IA)–³Iæ¢#ëÓŸ“Bã$væiÞ¢…´îéGÑAIY½"p³Aô­ˆ-ã·JÚºC(î@NÃò½ZÔt·d®Û/‰yÒÊ}2cùjI” ©ù6iTNÇ8³Ž(òÄ`v7uƒÊ´IpÇéuåžk©ƒ! jŒ<ú|sIÛvV’–í”ÙqÙkûx@2²r¤€šž…Ê1ÒæíNòôk¯xBÚ¼Ó]­øøåû”té=l]d+¥t×¹–åžx»¾ô¹ˆ¼*!“³k؆ä•GäÞû”€Œl%¾÷~‰ÉÛ( é“ä_Ÿ‘çZ·“¤{¤e§î2)o¥X§/krªÔ¬u‡ M™.íÞí"Ö¤4y䉧%`R®a•&zÙJúf.–VmÚµHû.=$\u€Ý\j·©¯Î_ðy0ðÌÏÐ ú{Üyâm¹ga[r¨’ÌÅk¥AÇ¥ß0y¼é«òap¸<Ùü ™·n‹4xì Ô\ %-3`xVš¶ï‚|Ń’œ³ZZµl%?úiР4éØSR ÏÊôE+äžûëKLêdiÓ£ŸN’—Þé-c×–‰[OI«7;Èó­^—¾áqJ)µƒEµ÷AÍÄ1߯™Ðż«&¾»ÏÂÖ)WwR 3dA%,Û-ñS³% 2ÙϤS¯åýÁbI#-º~( ÓçK½ûP{ûô]ç‘ÔE^nÑR'O—”{¥î½…5‘#/¶é CÓs°ÂÒgËÍ7ߌt„<þ|S1Zš¿×W’ g$`[è‘$-Þl'Ç&AÝr-àò%÷³3c ~wu« ŸÈ/Cj[ny¼v tË‚§°±ä]Æ.Þ.þó¥]ùÀ’"£f­ijÍZJ0~™½Jü’g©Ü‚~63Wšvè.½­é°`²Î/ ¿P)¶Žk£F¢Gx²ŒÂãþ¹2rzŒúd¹²TT润AãfËGI³JX/úQŸ<%Õm¨~ yÎWds.ȧWý 5>V ¤ôâ\=”»‡¤W}§{•”ˆÁϱK‘ Ç·@Ä/Û}5€‹·ÔfJÆhÖ€JUÓ1–ý±K¶k)p%k6ã v¡ŽsG ‰@…±ðÈ©®ƒ&gLäOAɶéÎðÙY0Ô¡C’We¯cfÊ”T&7^ÓtËqF”û+ŠYW¹éÐi©Îƒ^Tg2£Éò‡|ÒÆqn©§~uöpŽ4ñXdW›sì>"Õ}0M-º²:çè'ÚõØîé,]ÃM …ú7èÇúV%¸5LGœâzyÛÊ.À%?ìŽwZ¨ñÍí` Õ§é«{Ç&­Þ]eí…)™=\:´8;xغ#½| ? =)Š4~Õ0 %Bܧá¡»´*zGr…<ý\Oƒ[E¢Ú*6\“«@>e…¨Í)IÿÆÝ'¾Ò:·xVO0T]W#I@FE2R+#$ #©"|­(nêß¼jßq[ìÝ %·Õ¿°šžLSÞàÙ[:8ßœwοajüñtýá˜ýÈóÀ)¹* ©°÷ä×r½¦ÄÅ-Ú¢•à­Wón¨1?檞à.0šè‘Mîg<ŸÚ,@èÇ%3Iãz¬Ø·;çÛPõ«ÆURýnxòì4˜zf‚‡r0zçK1f8t…iòÇ’h|¨QÕ` ùýèÌ#p' ô¾Ðeu6¹žÆ÷èÉ‘†áï>º ¤`# Ü)!"µ–ÂvpcØF°»xèN0Bõ;¤ãžâ Ã×U©°xç—7Ø~íu'ÿÜ †‚Þ0Ýʨh»üH­]î·~¾Áz“$‚™`(¡CðdÚÁdª«ÇèñówcˆÛuOA·2Š=C,§ËÔÕ|>[xê›n°ß6.»Ãj¨ 0šŠ³,Ø:Fn9ã­¤¾Áü¿C£Ú|ÖðU0èžÊMúÑÌ0«ÅPUì0Á¯‡‹nÀÀ6õÍæ•'À ·$N²÷±3#'Är©'ÏÜ0'Õ¼ÕôŸ<:Š–¡{,™¥¤W[ì¶n)×ñ(ÒæËcüñ4H E+ÝÓ«ƒ™Ö͸¼¾u0_ae58M¦‚#G›§Õ zµV/£rI”Å¡™Z»âá(©7‹^OJb/)£ê©:‚A§º TPqÄ…¢ÛQH}âø¹ó× .ióP·*ùQÕ`Љ§ádg‡¡Ù‹—¯\@ÈÖž¿Êùà-`Щ)(ÿ:‘ùÚózÍü{ŒÎªùFf5ò Î7ÛyTÝÀ`¬óòõJÚÚs4òæùöv0Ëû:j¢ÕW´ÊóÚýv'ËÛn€Áuú+¨/h–hgdxÑ8£ÝW_í>}jn} Fª%¶CZÃ@kAž®Ç?¥}o˜vµ|y>} ¥m'8L©+̽´v˜§G‡þ³¾·N®`Rj¹ºðÛ;@Ô¡=]{½ùwrê{Hi=¢ùŠÙ«;Vì<óÀ×N¬öwëvÎ:ÂèCpƒ!÷ô¾'’Òádzôùî1ûë²åíþºá쫺Õùýs¬³LJêˆkOI4`½6 È»o£ ¸Ô´;m5讬5ú&÷¦ÞRëL‘|osŸÐ “@B†Å0¯·cáÝÝp.ÊfÜ´Î5 åÔùИkÓ x®—}Ù?â;Ž~‹¾mïëlP¹4×4 Á,·Kžðš6ÓãV3n±Î6‰j£¸ÿ(MòþŠ>§Â¹&ðúÐs­ß)…uºyèr{­‹4,LrM1›°æðým"_YqÖYÇ‘òhëNS3ñ€€h€¶¼3¼\í‘ÍßëëÄ£)÷çÏýoÉvø¥N‘y¡÷Ýë¿ðr¯·)Ç6Þ:õ(úcTí‰pj6çã%äº @ôr+ö!syã#1Õsªuò¨àWî{4ŽÕÛP< o²øÑ+žVãƒRLvžmI64øAO‘¯Wcás|@äºo•fÁK°ÆÝí~9Èsc¾J™SkºÁ¸ÐЊB!íl¶ÛXcD'3Q¶PlHó†pª÷°ßjU€ÛXçÛÝ퀌½Óÿª øyúûÍ… +XgÜ©£Ðüà‚bª,»Áð²U³\¬„¥VÑüׇ‹ H7gæóÙEë ¤ÿbøëp¡:! lÒ}ŸÕ\ ¥ÍÐü¸³ ÉÎ!DÔ *ÄCFò[—ž2ì—Flx\âª7† hÂxé·3Ññú™åsèj¯tab6¡£¸0­ô§ûôeÑlz>¹|ŒÎ¢=ßê¶EDW8¸N¨HÚÌ” ôãÒq†iÄöË£Ù Œ¸Ëin¦/ùön_¿|‡õéÚáŒGWÛ ³°¾˜†˜#KŒ¸K༱g2ìCFì^l7‘Ë^=»—îˆk! ^âÅúXÜMš#©N)ª„¼k3ê™ØÓ¾ÏÍ9Ãh¾x ÆÿZ‡1"Sßò-ü ·L53i:oùSmö-Ê¬ÃØ‡áÿ„ Ž' HB©i@Ú0oZªåÜp’7¸X*€ÜzŠæ; ùŠ˜ôbì ¥)@Z~ŸÇr}¬y¼¥ WÍâDd2Úxƒ'Ì á¦Êš!Ëu.ß%0"·%æD(&©úÙ4_‰kiÛ~@¨ðcÁî>ïâOzQ* *ù<¦`…æ_À65ç/ nÐsË|fÁ€<#ð³¢Ì=œÎrï…æã÷T#¢ùiÀ-KÓSM‘|³KøÉlù'ºß3> Ÿ[r†‚“ž¥|"3÷ dW0Ý·•>Wd©æ†¦Oæ`ÌVòÞ”ÀDÑwÍ?8ì˃„·‚„ þ-ã´Õ uëBϽIy¹4L½í„Ô)Â7*ÔãÏ™G£Ÿ¼ê^n¥ãµgüŽ)¨í/¼±‚xI· ÷Õj½v³ž¾)…áü,¬çËGŸ×@*1O-È„ V4Q¥1°³¸w=¼1ÎxŸ4 0;ö²Ø£MºNÓÇ9fJom— ŠXÁð3ÇTÇ7÷—_x¢«ø3¹ë[š6~™ŠR_.•~°:«›Îˆà9*ra,èf&€O™.>H/•~âî ‘Øô¶øöö.AoxýI’´£”:Ú‹+ð¯…)X×e7Å_²ü#ù“„”R?Ø7ýl¦b "]ÉcýÆ)Ѐ¤XÐì*ɪé/¼ ¿¤ ®yŒ3fÊ×Yð ÊÂ<@¾%ý aHÑÃoüQ]n¦àP2Øü=Ï»—ra™N­i‹$_3?Ú—¼Â #ÏŠhÁéòq–Ñ$Ì¡<ˆ´ã¹qQ(æœN&2ºG“9ßÂ=1oîèî„¡¢Z@ª}@ÜNaÚ9 ìé/xìȘ(C@˜¼u–…h«ßW¬ÜØŠ-‹¬ÜE·xT–Ä.y @ó³i°r$OŠ^î÷ÒHB,ü7e\‘\t¾¢³9Vÿ$’Z¸Œ< ¦<Ö9ñ>Rß|P"¯©uZHSO3©$·‘ŠÛÒ*CP©eC4?™q–[ÎÄ9Ìÿ;ßÍPeå– —ÐÔË<€`Ž8¡æÞ¥¦9àÔ‘¤n£ ži˜ÖUäôp×ËÊ–þ"'Õë¬Îæ C†‘Tšˆ¶u`?“%vøÝX€¼ÂÿZð…Q’a‹©hæ§v˜Uߥ”l:M~Ž•\†£ùyðxr•³U ¸â·.ùPˆÔIšQÊú\*‹±ôó1ü6åJ rýHjM£ùÛàyÞzáz¨:Lx~ô+ƒJ:@Þ9úñ0³õ—ÜBð˜ÐSPÆX³×©g¦Aü@R©áwh~›kó<ª}÷ßeqÒVîHu€0i,C'¢  ’z™eÀQfþflY—ÒÚ^Å›¸#ZÙhº×“¦Ì¥Á/‰‹*ÕÑòÐì‰ÒCÀ9­[u'?\*2 r݉“Ôj‹q?Ži8¢hsìÙx”õiAa^²â*ã·[WÅâ:5ç˹÷ÒŽ='¢Sã7}ÎS½YU®?FÂèøU ë,¾·@!Õ<{åfô iS¯èC¤Lš½éPHcGø~ãbÂæÎ¿{Uâ®’éœ^¸ñá'¬×ìÑŒ¤ÕÞ˜\ó]í[,îp·2€Àçͽ£9yBH.¡t!ÕXˆH{xc÷¾j’ŠBB ÷º×Z“·2€À óraU“PÛ‚ñ&á «Ÿ>F0ÇÇCÅD$Æø_H$¤üëp Ÿ„/-Ú Í6ÃáJÚ"~iVïDr¿òý¨²Oj“Ò‚þøîwPäøµ%eU EÂÓ !6ذ¸f}¼qZ·ò‚„í¦ÀRÝ ¢/ž<žêoo¹€àÞ6¤ ¶Æù’¤O@Å1\Ï}sb‰€Bßk!üŒÅæSÖq–%á:‹ÈùŠð¾´.YúL1ð Zþ;H6P/>Æýº‚|[ • T3~/DìCÖ^ײ|…DMÔÒ×éóbýÅ„Â.­õÌIÇ8òç•WË¢Vz×ç9¶9†è¢Æñútds«Û)Óÿ¦€ü;À—hsþ8¸š¼Ë, .„y“¶+HZ›ïÍWJægl¤Ö"4ûX ðeÜœês  IB¤Ê’{M›7™µ×ÙT[λ‚J"}Rv"³Q~îþ°²‚žD+ÂåW¼Ž«ìM™† ­'œúg¡£¥á1"kø«-Ýñ Ê­Ïö„1&:#ó³ÀœßÑl”ššy3ÇÓâbo¡éö½Ço¤YníkWxË £Z£ö@ã»™e²ó6—61öȸrožŸäÔÕl[ý¡cÜgUÅn£Úà¤Âijé’Pû ^’r:@ÅpÊOjý¬ÛÜ;7 5ÉëƒEˆž9F.ßÞÙÕ MU«8IºÄàíãZpñ8ó™lsU.1êCl\™/ÆqáÔÕXð :†F%9rÕ=©ZšñAŒzL×Jv2s )å9åºô]îh5˜ŸŠˆ"+™>¸˜ù!W†£\©$;=åÕ«´Â]ö<#‚eJc>Öq™µqe¾iÎçÔeˆZ¢cä²Ó`4ÍD«ÒâߤçHey’IsÒ’ãÓ´Ý+8ÅÚ3 ùÒ<ä 8$Ízý´È€d$¦É€B-2IʬÄ,ÝÝFÄjb4_DÊæ0‡ØD¹L¯Ë<ôØÛ ’²Ô/©³’4V˜&€D„‡²E¢ü·¯%ê"ò6P6v@N‘:]ûÚ#¢ÃÇs±`Ì.²nžC†®ÈJRŠÒôó)ST­1¦Ã“cõñÚÛÛ‹q᪠ZapH†Q2eÅ$€07‡¾!õÙV>Û]›t ñHˆæëg{¾ÁÚŠCH…CÖfùÊ•„ør"@ò0³V–ÌË¢MÎÑ–öÀVùŸÛþ3pÈÜhöÛ¦¹ÔýñÞF]¥¥iuŸNÃOjGœ³ùûöý6dq¦X÷46ëE¡ÙõzìZ çþŒúó·o2–÷j×÷[Que}à@ÿé‘ìvŽÍy>º¹{ú‡½s×tpï¶‹ôíc²§­ï×¾÷¢—l‡Ò5Ý:ø9… ¡0Šèƒ6b²•u‘‚&~Û‘r=±çDÀdˆï²˜¬„ýþSàÈ"œùî~yå¹!! ]·«DQ#§³C~ÔqU¾»l­×´„PT›~ïhXóϹ‰ì÷íjÒŸ±îtÙ+R… €5§Zw!=ùȨAPûœßu¢å»ÜóƒÇ=õœ›isil¸æÏ]žo·î} ÉN¯mp=´™Qéå²6¿éÏOf:-­-÷#ÔYŒv$’‹®E9h+J2óy¡kFƒ¾€ ÈØµSþýçWG°V|Öö[§¯²ú¡vìÙËg«l¸ÉÜùdpÞ‰s».úðƒÔІÉ\¡±˜¡Ø ¿emáëÍúþqäí[ÕvF2ÜÁ¿Ä=¿|#®çÕõl#ÓæÅEÜ:ï²ÃWy=êÂ'Ct^¯-ëóJ ¥Œþu™ $ MÁm8çÞ4y‘<ôZ6ž _ñ+€@¤mD¬§|¥ f8KIù Œ„\›Ñ߯¾¸HQÀH½f˜ªLÅ”N»ªÏ;vl…§ŒíKÓ‡^§Œ³ž¶TÔÊ*±[ZD¸o\1C’ (U$€à|^v#T&7\:IÂËêAíÏ+ük±dëµ€î½F¯þèæ„*thÙ‰w}3´d«ÿ$…O#ß%Ú”b²ív*ÿxÛ&ÜVè˺7Û÷öÔ Ù{eßI¥Ü@¢ÁcÚŠ|ºÉeY0'¡ÌaC HðP`P+ôÈÜ¥T"Ws%¤AÏ—_¿3œ†»õ'°h¥ý¹Ñ›$ú¯vàŠaèªRФÒó²ÔÓ‡ö€µ«¼ü,<{”A3šÇ¾cëÇrßãú*Ìgxã†s¡¤\F\®tÔpjP|•eÌÃîìLñ ˆ%>*™$Wiµºòýž¯5»oìÿÈàøžÝ…Šáù?w¨~-²ÙÄ—r+‰¹4É ×x²ÿÁf«4#žvüZ©…Ç6°‹?Ï„2„jÑ)f'd>]Åpoæ0»‚“ G¹ 'M`ò†ËÍS¢Äd0ÅŒ€(e˜8F!ÍUì©×o|Ï}æw©·V}êÁxNƒ³›WÜÃ\p“D³.tÞ5â/•ì¼÷¬3†=<Ø¿ÿž5ý=Îw=S^S»©÷¡kÃìõ›Ñ>öõÈèJqÒ§Feé¯ñÕgÆI“6tÞ‘DQß5Ùò d.NèÃõ³JÞ† ®¤«0xÀ³;Z ¡Ú>uÙèÓ¸Q5vk.0NΜA)*Öª¸hG#ÀÑTúC¶?2üÔ·•Üú'…ô7aD``à”å—Ú sÖ yržz…’Sc}jºWŸ¸À[ö³«,MíjQí÷·ÿ¬Ù’Ïÿ¨KÛô}•ç{8×Ùè7ƒóxÅ2h6àÙ0- e˜€‘§ ûëO:@è×°^\_9yꚨ¸€2ÑÔcÓFŠïtXLÓú#bHuÂàÕ!b9¼³ìÏ÷×MšúÄß õ¯áÿ·xêjò™>zÓÑTìÕ@›«Ç²Ã~f³ôÁÎéC¶>ýq„on„«-#Iva ØÚ{™JÔœˆ °ËêW—N^y †#>7nnš½úú>;NîS-À† ŽI@°s^nXò4¡‰õ¡Mô„¥KÆyê\Í*ý/ö+ëØ±o9š¼ág¹rU¿÷„WÇžžš;ë_±À×ѰõªÝònUÎà*§h¿7îîÄr»~ ¿×:gáàŠ”{wfb[ö´"ZDÄÝ[±µ;1¡9õeOwM—­¦­˜Ù§ 4ûiÞDcC)h_˜0³lZ@p§D4 ù|Û1vb·W»] ð,¡ýfúì÷íJnREdË·èÔæØì7nO’¶è|xØÚ ’šñ·UŸZ{±ÊÖ|&›Ø†µ%îp‰˜NƒñÜ'¶áÄ!æ—ÐóÖÄ8ßÒòu"6µˆ‹êô@œÐù|¾3ob[P2€ ·Ÿ–M®iæõoSJ‚†íG­ÂÞQÀs0Kbª,aJ¶˜¹VÊTŸÌj¾D¼,VBT˜{Db•1N.jÂàâ«,± Mâ’˜yá–V DE[@˜Ê¡ËFIŠGe ÕX¨µ@“¨$4x\“€”Œü‹pã„Ró9X6òý5âõO‹˜Pºh^–Ðs/¸œ¡T|†x 1\bEv#‹ë7"yY"æCIZ1€ÓXS¤Æ ŽWÏRÈS'È!|jbÞ@ªèÂ{ê‡âÇPT\- „‹š­‚ ->¿^rû!øn;:ùaöÔ‘§Y?ÚZW)•JUQ¤4$DT‚‚xFŠˆ˜ÝoR—+áH]P8ÃO¨7“8F]ß*~‚&|DQ—Œ5½½’pJPBL©ñ]¼)nAxN.ꌈèSõ²( í†X"âˆõ©O9õ© šD€X©ˆ¤.!¤ U–q¬þ¯ÔI¥4J°ÓPD«eO…x‹&R~&S‚‚ÆËï³òr{Ž–O7uòÿƒD–nÿÅ€àÌ’IÖ©+åÀÓJˆUg}t@Ð~ ¿Ûb:óÐÿø]ú –EÂðIEND®B`‚Chipmunk2D-Chipmunk-7.0.3/doc/stylesheet.css000066400000000000000000000017501347650476100207310ustar00rootroot00000000000000h1, h2 { padding: 0.25em; border-radius: 0.25em; } h1 { background-color: #89b4cb; } h2 { background-color: lightGrey; } p { margin-left: 1em; } p.expl { margin-left: 2em; } code { color: #191970 } .HideShow { background-color: lightGrey; padding: 0.5em; border-radius: 1em; border: 2px grey solid; } .PopOpen { border-radius: 1em; border: 1px grey solid; } pre { border-radius: 0.75em; background-color: #F0F0F0; padding: 0.5em; margin-left: 1em; } /*ul { border-radius: 0.75em; background-color: #F0F0F0; margin-left: 1em; }*/ table { border: 2px solid black; border-collapse: collapse; margin-left: 1em; } table td, th { border: 1px black solid; padding: 0.5em; } h1 a:link, h2 a:link, h3 a:link, h1 a:visited, h2 a:visited, h3 a:visited { text-decoration:none; color:black; background-color:transparent } h1 a:hover, h2 a:hover, h3 a:hover, h1 a:active, h2 a:active, h3 a:active { text-decoration:underline; color:black; background-color:transparent } Chipmunk2D-Chipmunk-7.0.3/include/000077500000000000000000000000001347650476100167015ustar00rootroot00000000000000Chipmunk2D-Chipmunk-7.0.3/include/chipmunk/000077500000000000000000000000001347650476100205175ustar00rootroot00000000000000Chipmunk2D-Chipmunk-7.0.3/include/chipmunk/chipmunk.h000066400000000000000000000222021347650476100225040ustar00rootroot00000000000000/* 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.3 #define CP_VERSION_MAJOR 7 #define CP_VERSION_MINOR 0 #define CP_VERSION_RELEASE 3 /// 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 Chipmunk2D-Chipmunk-7.0.3/include/chipmunk/chipmunk_ffi.h000066400000000000000000000060421347650476100233340ustar00rootroot00000000000000/* 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 Chipmunk2D-Chipmunk-7.0.3/include/chipmunk/chipmunk_private.h000066400000000000000000000245541347650476100242520ustar00rootroot00000000000000/* 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/chipmunk.h" #include "chipmunk/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)(const void *ptr, const void *elt); typedef void *(*cpHashSetTransFunc)(const 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); const void *cpHashSetInsert(cpHashSet *set, cpHashValue hash, const void *ptr, cpHashSetTransFunc trans, void *data); const void *cpHashSetRemove(cpHashSet *set, cpHashValue hash, const void *ptr); const void *cpHashSetFind(cpHashSet *set, cpHashValue hash, const 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 Chipmunk2D-Chipmunk-7.0.3/include/chipmunk/chipmunk_structs.h000066400000000000000000000216061347650476100243020ustar00rootroot00000000000000/* 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/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 Chipmunk2D-Chipmunk-7.0.3/include/chipmunk/chipmunk_types.h000066400000000000000000000147111347650476100237360ustar00rootroot00000000000000/* 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 Chipmunk2D-Chipmunk-7.0.3/include/chipmunk/chipmunk_unsafe.h000066400000000000000000000055661347650476100240630ustar00rootroot00000000000000/* 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 /// @} Chipmunk2D-Chipmunk-7.0.3/include/chipmunk/cpArbiter.h000066400000000000000000000212251347650476100226050ustar00rootroot00000000000000/* 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); /// @} Chipmunk2D-Chipmunk-7.0.3/include/chipmunk/cpBB.h000066400000000000000000000124361347650476100215040ustar00rootroot00000000000000/* 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 Chipmunk2D-Chipmunk-7.0.3/include/chipmunk/cpBody.h000066400000000000000000000224731347650476100221200ustar00rootroot00000000000000/* 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); ///@} Chipmunk2D-Chipmunk-7.0.3/include/chipmunk/cpConstraint.h000066400000000000000000000113101347650476100233330ustar00rootroot00000000000000/* 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" ///@} Chipmunk2D-Chipmunk-7.0.3/include/chipmunk/cpDampedRotarySpring.h000066400000000000000000000060501347650476100247720ustar00rootroot00000000000000/* 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); /// @} Chipmunk2D-Chipmunk-7.0.3/include/chipmunk/cpDampedSpring.h000066400000000000000000000070171347650476100235750ustar00rootroot00000000000000/* 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); /// @} Chipmunk2D-Chipmunk-7.0.3/include/chipmunk/cpGearJoint.h000066400000000000000000000041261347650476100231000ustar00rootroot00000000000000/* 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); /// @} Chipmunk2D-Chipmunk-7.0.3/include/chipmunk/cpGrooveJoint.h000066400000000000000000000050741347650476100234660ustar00rootroot00000000000000/* 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); /// @} Chipmunk2D-Chipmunk-7.0.3/include/chipmunk/cpHastySpace.h000066400000000000000000000026231347650476100232620ustar00rootroot00000000000000// 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); Chipmunk2D-Chipmunk-7.0.3/include/chipmunk/cpMarch.h000066400000000000000000000032051347650476100222450ustar00rootroot00000000000000// 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 ); Chipmunk2D-Chipmunk-7.0.3/include/chipmunk/cpPinJoint.h000066400000000000000000000047321347650476100227530ustar00rootroot00000000000000/* 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); ///@} Chipmunk2D-Chipmunk-7.0.3/include/chipmunk/cpPivotJoint.h000066400000000000000000000045571347650476100233330ustar00rootroot00000000000000/* 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); /// @} Chipmunk2D-Chipmunk-7.0.3/include/chipmunk/cpPolyShape.h000066400000000000000000000062101347650476100231160ustar00rootroot00000000000000/* 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); /// @} Chipmunk2D-Chipmunk-7.0.3/include/chipmunk/cpPolyline.h000066400000000000000000000052311347650476100230070ustar00rootroot00000000000000// 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 Chipmunk2D-Chipmunk-7.0.3/include/chipmunk/cpRatchetJoint.h000066400000000000000000000046321347650476100236160ustar00rootroot00000000000000/* 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); /// @} Chipmunk2D-Chipmunk-7.0.3/include/chipmunk/cpRobust.h000066400000000000000000000010241347650476100224660ustar00rootroot00000000000000#include "chipmunk/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); Chipmunk2D-Chipmunk-7.0.3/include/chipmunk/cpRotaryLimitJoint.h000066400000000000000000000045431347650476100245040ustar00rootroot00000000000000/* 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); /// @} Chipmunk2D-Chipmunk-7.0.3/include/chipmunk/cpShape.h000066400000000000000000000221021347650476100222500ustar00rootroot00000000000000/* 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); /// @} Chipmunk2D-Chipmunk-7.0.3/include/chipmunk/cpSimpleMotor.h000066400000000000000000000036701347650476100234730ustar00rootroot00000000000000/* 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); /// @} Chipmunk2D-Chipmunk-7.0.3/include/chipmunk/cpSlideJoint.h000066400000000000000000000055571347650476100232730ustar00rootroot00000000000000/* 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); /// @} Chipmunk2D-Chipmunk-7.0.3/include/chipmunk/cpSpace.h000066400000000000000000000413661347650476100222600ustar00rootroot00000000000000/* 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 /// @} Chipmunk2D-Chipmunk-7.0.3/include/chipmunk/cpSpatialIndex.h000066400000000000000000000232241347650476100236030ustar00rootroot00000000000000/* 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); } ///@} Chipmunk2D-Chipmunk-7.0.3/include/chipmunk/cpTransform.h000066400000000000000000000127701347650476100231750ustar00rootroot00000000000000/* 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 Chipmunk2D-Chipmunk-7.0.3/include/chipmunk/cpVect.h000066400000000000000000000146711347650476100221250ustar00rootroot00000000000000/* 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 Chipmunk2D-Chipmunk-7.0.3/msvc/000077500000000000000000000000001347650476100162265ustar00rootroot00000000000000Chipmunk2D-Chipmunk-7.0.3/msvc/README000066400000000000000000000012011347650476100171000ustar00rootroot00000000000000I'm not much of a Windows dev, so the Visual Studio project is sometimes not up to date. If there is anything wrong, pull requests are highly appreciated! As of June 2015 I've dropped support for pre-2015 versions. In the past, everybody had their favorite version, and they all needed separate projects, but this was a huge pain to handle. I recently rewrote the demos to use Sokol, a even more lightweight replacement for GLFW so I didn't need to bundle GLFW binaries. I decided it was a good time to cutoff support for older versions. If you were still using one of the older projects feel free to update it and send me a pull request. Chipmunk2D-Chipmunk-7.0.3/msvc/VS2015/000077500000000000000000000000001347650476100170665ustar00rootroot00000000000000Chipmunk2D-Chipmunk-7.0.3/msvc/VS2015/chipmunk/000077500000000000000000000000001347650476100207045ustar00rootroot00000000000000Chipmunk2D-Chipmunk-7.0.3/msvc/VS2015/chipmunk/chipmunk.vcxproj000066400000000000000000000601111347650476100241360ustar00rootroot00000000000000 Debug DLL x64 Debug SCRT Win32 Debug DLL Win32 Debug SCRT x64 Debug Win32 Debug x64 Release DLL x64 Release SCRT Win32 Release DLL Win32 Release SCRT x64 Release Win32 Release x64 {C1ACE86E-5A14-490A-9678-104BA2546723} Win32Proj chipmunk 8.1 StaticLibrary true v140 StaticLibrary true v140 StaticLibrary true v140 StaticLibrary true v140 DynamicLibrary true v140 DynamicLibrary true v140 StaticLibrary false true v140 StaticLibrary false true v140 DynamicLibrary false true v140 DynamicLibrary false true v140 DynamicLibrary false true v140 DynamicLibrary false true v140 $(PlatformName)\$(Configuration)\ $(PlatformName)\$(Configuration)\ $(PlatformName)\$(Configuration)\ $(PlatformName)\$(Configuration)\ $(PlatformName)\$(Configuration)\ $(PlatformName)\$(Configuration)\ $(PlatformName)\$(Configuration)\ $(PlatformName)\$(Configuration)\ $(PlatformName)\$(Configuration)\ $(PlatformName)\$(Configuration)\ $(PlatformName)\$(Configuration)\ $(PlatformName)\$(Configuration)\ $(PlatformName)\$(Configuration)\ $(PlatformName)\$(Configuration)\ $(PlatformName)\$(Configuration)\ $(PlatformName)\$(Configuration)\ $(PlatformName)\$(Configuration)\ $(PlatformName)\$(Configuration)\ $(PlatformName)\$(Configuration)\ $(PlatformName)\$(Configuration)\ $(PlatformName)\$(Configuration)\ $(PlatformName)\$(Configuration)\ $(PlatformName)\$(Configuration)\ $(PlatformName)\$(Configuration)\ Level3 Disabled _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) ..\..\..\include Default Windows true Level3 Disabled _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) ..\..\..\include CompileAsC Windows true Level3 Disabled _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) ..\..\..\include MultiThreadedDebug CompileAsC Windows true Level3 Disabled _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) ..\..\..\include MultiThreadedDebug CompileAsC Windows true Level3 Disabled _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) ..\..\..\include CompileAsC Windows true Level3 Disabled _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) ..\..\..\include CompileAsC Windows true Level3 MaxSpeed true _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) ..\..\..\include CompileAsC Fast Windows true true true Level3 MaxSpeed true _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) ..\..\..\include CompileAsC Fast Windows true true true Level3 MaxSpeed true _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) ..\..\..\include MultiThreaded CompileAsC Windows true true true Level3 MaxSpeed true _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) ..\..\..\include MultiThreaded CompileAsC Windows true true true Level3 MaxSpeed true _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) CompileAsC ..\..\..\include Windows true true true Level3 MaxSpeed true _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) CompileAsC ..\..\..\include Windows true true true Chipmunk2D-Chipmunk-7.0.3/msvc/VS2015/chipmunk/chipmunk.vcxproj.filters000066400000000000000000000143731347650476100256160ustar00rootroot00000000000000 {93995380-89BD-4b04-88EB-625FBE52EBFB} {4FC737F1-C7A5-4376-A066-2A32D752A2FF} include include include include include include include include include include include include include include include include include include include include include include include include include src src src src src src src src src src src src src src src src src src src src src src src src src src src src src src Chipmunk2D-Chipmunk-7.0.3/msvc/VS2015/demo/000077500000000000000000000000001347650476100200125ustar00rootroot00000000000000Chipmunk2D-Chipmunk-7.0.3/msvc/VS2015/demo/Chipmunk.sln000066400000000000000000000107501347650476100223110ustar00rootroot00000000000000 Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 14 VisualStudioVersion = 14.0.25420.1 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "chipmunk-demo", "demo.vcxproj", "{659CE82A-959E-46C0-ACCC-A3BE0A0CB860}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "chipmunk", "..\chipmunk\chipmunk.vcxproj", "{C1ACE86E-5A14-490A-9678-104BA2546723}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug DLL|Win32 = Debug DLL|Win32 Debug DLL|x64 = Debug DLL|x64 Debug SCRT|Win32 = Debug SCRT|Win32 Debug SCRT|x64 = Debug SCRT|x64 Debug|Win32 = Debug|Win32 Debug|x64 = Debug|x64 Release DLL|Win32 = Release DLL|Win32 Release DLL|x64 = Release DLL|x64 Release SCRT|Win32 = Release SCRT|Win32 Release SCRT|x64 = Release SCRT|x64 Release|Win32 = Release|Win32 Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {659CE82A-959E-46C0-ACCC-A3BE0A0CB860}.Debug DLL|Win32.ActiveCfg = Debug DLL|Win32 {659CE82A-959E-46C0-ACCC-A3BE0A0CB860}.Debug DLL|Win32.Build.0 = Debug DLL|Win32 {659CE82A-959E-46C0-ACCC-A3BE0A0CB860}.Debug DLL|x64.ActiveCfg = Debug DLL|x64 {659CE82A-959E-46C0-ACCC-A3BE0A0CB860}.Debug SCRT|Win32.ActiveCfg = Debug SCRT|Win32 {659CE82A-959E-46C0-ACCC-A3BE0A0CB860}.Debug SCRT|Win32.Build.0 = Debug SCRT|Win32 {659CE82A-959E-46C0-ACCC-A3BE0A0CB860}.Debug SCRT|x64.ActiveCfg = Debug SCRT|x64 {659CE82A-959E-46C0-ACCC-A3BE0A0CB860}.Debug|Win32.ActiveCfg = Debug|Win32 {659CE82A-959E-46C0-ACCC-A3BE0A0CB860}.Debug|Win32.Build.0 = Debug|Win32 {659CE82A-959E-46C0-ACCC-A3BE0A0CB860}.Debug|x64.ActiveCfg = Debug|x64 {659CE82A-959E-46C0-ACCC-A3BE0A0CB860}.Release DLL|Win32.ActiveCfg = Release DLL|Win32 {659CE82A-959E-46C0-ACCC-A3BE0A0CB860}.Release DLL|Win32.Build.0 = Release DLL|Win32 {659CE82A-959E-46C0-ACCC-A3BE0A0CB860}.Release DLL|x64.ActiveCfg = Release DLL|x64 {659CE82A-959E-46C0-ACCC-A3BE0A0CB860}.Release SCRT|Win32.ActiveCfg = Release SCRT|Win32 {659CE82A-959E-46C0-ACCC-A3BE0A0CB860}.Release SCRT|Win32.Build.0 = Release SCRT|Win32 {659CE82A-959E-46C0-ACCC-A3BE0A0CB860}.Release SCRT|x64.ActiveCfg = Release SCRT|x64 {659CE82A-959E-46C0-ACCC-A3BE0A0CB860}.Release|Win32.ActiveCfg = Release|Win32 {659CE82A-959E-46C0-ACCC-A3BE0A0CB860}.Release|Win32.Build.0 = Release|Win32 {659CE82A-959E-46C0-ACCC-A3BE0A0CB860}.Release|x64.ActiveCfg = Release|x64 {C1ACE86E-5A14-490A-9678-104BA2546723}.Debug DLL|Win32.ActiveCfg = Debug DLL|Win32 {C1ACE86E-5A14-490A-9678-104BA2546723}.Debug DLL|Win32.Build.0 = Debug DLL|Win32 {C1ACE86E-5A14-490A-9678-104BA2546723}.Debug DLL|x64.ActiveCfg = Debug DLL|x64 {C1ACE86E-5A14-490A-9678-104BA2546723}.Debug DLL|x64.Build.0 = Debug DLL|x64 {C1ACE86E-5A14-490A-9678-104BA2546723}.Debug SCRT|Win32.ActiveCfg = Debug SCRT|Win32 {C1ACE86E-5A14-490A-9678-104BA2546723}.Debug SCRT|Win32.Build.0 = Debug SCRT|Win32 {C1ACE86E-5A14-490A-9678-104BA2546723}.Debug SCRT|x64.ActiveCfg = Debug SCRT|x64 {C1ACE86E-5A14-490A-9678-104BA2546723}.Debug SCRT|x64.Build.0 = Debug SCRT|x64 {C1ACE86E-5A14-490A-9678-104BA2546723}.Debug|Win32.ActiveCfg = Debug|Win32 {C1ACE86E-5A14-490A-9678-104BA2546723}.Debug|Win32.Build.0 = Debug|Win32 {C1ACE86E-5A14-490A-9678-104BA2546723}.Debug|x64.ActiveCfg = Debug|x64 {C1ACE86E-5A14-490A-9678-104BA2546723}.Debug|x64.Build.0 = Debug|x64 {C1ACE86E-5A14-490A-9678-104BA2546723}.Release DLL|Win32.ActiveCfg = Release DLL|Win32 {C1ACE86E-5A14-490A-9678-104BA2546723}.Release DLL|Win32.Build.0 = Release DLL|Win32 {C1ACE86E-5A14-490A-9678-104BA2546723}.Release DLL|x64.ActiveCfg = Release DLL|x64 {C1ACE86E-5A14-490A-9678-104BA2546723}.Release DLL|x64.Build.0 = Release DLL|x64 {C1ACE86E-5A14-490A-9678-104BA2546723}.Release SCRT|Win32.ActiveCfg = Release SCRT|Win32 {C1ACE86E-5A14-490A-9678-104BA2546723}.Release SCRT|Win32.Build.0 = Release SCRT|Win32 {C1ACE86E-5A14-490A-9678-104BA2546723}.Release SCRT|x64.ActiveCfg = Release SCRT|x64 {C1ACE86E-5A14-490A-9678-104BA2546723}.Release SCRT|x64.Build.0 = Release SCRT|x64 {C1ACE86E-5A14-490A-9678-104BA2546723}.Release|Win32.ActiveCfg = Release|Win32 {C1ACE86E-5A14-490A-9678-104BA2546723}.Release|Win32.Build.0 = Release|Win32 {C1ACE86E-5A14-490A-9678-104BA2546723}.Release|x64.ActiveCfg = Release|x64 {C1ACE86E-5A14-490A-9678-104BA2546723}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal Chipmunk2D-Chipmunk-7.0.3/msvc/VS2015/demo/demo.vcxproj000066400000000000000000000524231347650476100223610ustar00rootroot00000000000000 Debug DLL x64 Debug SCRT Win32 Debug DLL Win32 Debug SCRT x64 Debug Win32 Debug x64 Release DLL x64 Release SCRT Win32 Release DLL Win32 Release SCRT x64 Release Win32 Release x64 {659CE82A-959E-46C0-ACCC-A3BE0A0CB860} Win32Proj demo chipmunk-demo true v140 true v140 true v140 true v140 true v140 true v140 false true v140 false true v140 false true v140 false true v140 false true v140 false true v140 true $(PlatformName)\$(Configuration)\ $(PlatformName)\$(Configuration)\ true true $(PlatformName)\$(Configuration)\ $(PlatformName)\$(Configuration)\ true true $(PlatformName)\$(Configuration)\ $(PlatformName)\$(Configuration)\ true false $(PlatformName)\$(Configuration)\ $(PlatformName)\$(Configuration)\ false false $(PlatformName)\$(Configuration)\ $(PlatformName)\$(Configuration)\ false false $(PlatformName)\$(Configuration)\ $(PlatformName)\$(Configuration)\ false Level3 Disabled _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) ..\..\..\include;..\..\glew\include;..\..\glfw\include Default EditAndContinue EnableFastChecks Console true OpenGL32.lib;Glu32.lib;%(AdditionalDependencies) Level3 Disabled _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) ..\..\..\include;..\..\glew\include;..\..\glfw\include CompileAsCpp ProgramDatabase EnableFastChecks Console true OpenGL32.lib;Glu32.lib;%(AdditionalDependencies) Level3 Disabled _CRT_SECURE_NO_WARNINGS;_USE_MATH_DEFINES;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) ..\..\..\include;..\..\glew\include;..\..\glfw\include MultiThreadedDebug CompileAsCpp Console true OpenGL32.lib;Glu32.lib;Msvcrt.lib;%(AdditionalDependencies) Level3 Disabled _CRT_SECURE_NO_WARNINGS;_USE_MATH_DEFINES;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) ..\..\..\include;..\..\glew\include;..\..\glfw\include MultiThreadedDebug CompileAsCpp Console true OpenGL32.lib;Glu32.lib;Msvcrt.lib;%(AdditionalDependencies) Level3 Disabled _CRT_SECURE_NO_WARNINGS;_USE_MATH_DEFINES;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) ..\..\..\include;..\..\glew\include;..\..\glfw\include CompileAsCpp Console true OpenGL32.lib;Glu32.lib;%(AdditionalDependencies) Level3 Disabled _CRT_SECURE_NO_WARNINGS;_USE_MATH_DEFINES;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) ..\..\..\include;..\..\glew\include;..\..\glfw\include CompileAsCpp Console true OpenGL32.lib;Glu32.lib;%(AdditionalDependencies) Level3 MaxSpeed true _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ..\..\..\include;..\..\glew\include;..\..\glfw\include CompileAsC Console true true true OpenGL32.lib;Glu32.lib;%(AdditionalDependencies) Level3 MaxSpeed true _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ..\..\..\include;..\..\glew\include;..\..\glfw\include CompileAsCpp Console true true true OpenGL32.lib;Glu32.lib;%(AdditionalDependencies) Level3 MaxSpeed true _CRT_SECURE_NO_WARNINGS;_USE_MATH_DEFINES;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ..\..\..\include;..\..\glew\include;..\..\glfw\include MultiThreaded CompileAsCpp Console true true true OpenGL32.lib;Glu32.lib;Msvcrt.lib;%(AdditionalDependencies) Level3 MaxSpeed true _CRT_SECURE_NO_WARNINGS;_USE_MATH_DEFINES;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ..\..\..\include;..\..\glew\include;..\..\glfw\include MultiThreaded CompileAsCpp Console true true true OpenGL32.lib;Glu32.lib;Msvcrt.lib;%(AdditionalDependencies) Level3 MaxSpeed true _CRT_SECURE_NO_WARNINGS;_USE_MATH_DEFINES;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ..\..\..\include;..\..\glew\include;..\..\glfw\include CompileAsCpp Console true true true OpenGL32.lib;Glu32.lib;%(AdditionalDependencies) Level3 MaxSpeed true _CRT_SECURE_NO_WARNINGS;_USE_MATH_DEFINES;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ..\..\..\include;..\..\glew\include;..\..\glfw\include CompileAsCpp Console true true true OpenGL32.lib;Glu32.lib;%(AdditionalDependencies) {c1ace86e-5a14-490a-9678-104ba2546723} Chipmunk2D-Chipmunk-7.0.3/msvc/VS2015/demo/demo.vcxproj.filters000066400000000000000000000101531347650476100240220ustar00rootroot00000000000000 {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hpp;hxx;hm;inl;inc;xsd Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Header Files Header Files Header Files Header Files Header Files Header Files Chipmunk2D-Chipmunk-7.0.3/objectivec/000077500000000000000000000000001347650476100173735ustar00rootroot00000000000000Chipmunk2D-Chipmunk-7.0.3/objectivec/include/000077500000000000000000000000001347650476100210165ustar00rootroot00000000000000Chipmunk2D-Chipmunk-7.0.3/objectivec/include/ObjectiveChipmunk/000077500000000000000000000000001347650476100244275ustar00rootroot00000000000000Chipmunk2D-Chipmunk-7.0.3/objectivec/include/ObjectiveChipmunk/ChipmunkAutoGeometry.h000066400000000000000000000103731347650476100307270ustar00rootroot00000000000000// Copyright 2013 Howling Moon Software. All rights reserved. // See http://chipmunk2d.net/legal.php for more information. #import "ObjectiveChipmunk/ObjectiveChipmunk.h" #import "chipmunk/cpMarch.h" #import "chipmunk/cpPolyline.h" @class ChipmunkPolylineSet; /// Wrapper for the cpPolyline type. @interface ChipmunkPolyline : NSObject { @private cpPolyline *_line; cpFloat _area; } -(id)initWithPolyline:(cpPolyline *)line; +(ChipmunkPolyline *)fromPolyline:(cpPolyline *)line; /// Returns true if the first and last vertex are equal. @property(nonatomic, readonly) bool isClosed; /// Returns the signed area of the polyline calculated by cpAreaForPoly. /// Non-closed polylines return an area of 0. @property(nonatomic, readonly) cpFloat area; /// Centroid of the polyline calculated by cpCentroidForPoly. /// It is an error to call this on a non-closed polyline. @property(nonatomic, readonly) cpVect centroid; /// Calculates the moment of inertia for a closed polyline with the given mass and offset. -(cpFloat)momentForMass:(cpFloat)mass offset:(cpVect)offset; /// Vertex count. @property(nonatomic, readonly) NSUInteger count; /// Array of vertexes. @property(nonatomic, readonly) const cpVect *verts; /** 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. */ -(ChipmunkPolyline *)simplifyCurves:(cpFloat)tolerance; /** 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. */ -(ChipmunkPolyline *)simplifyVertexes:(cpFloat)tolerance; /// Generate a convex hull that contains a polyline. (closed or not) -(ChipmunkPolyline *)toConvexHull; /// Generate an approximate convex hull that contains a polyline. (closed or not) -(ChipmunkPolyline *)toConvexHull:(cpFloat)tolerance; /// Generate a set of convex hulls for a polyline. /// See the note on cpPolylineConvexDecomposition_BETA() for more information. -(ChipmunkPolylineSet *)toConvexHulls_BETA:(cpFloat)tolerance; /// Create an array of segments for each segment in this polyline. -(NSArray *)asChipmunkSegmentsWithBody:(ChipmunkBody *)body radius:(cpFloat)radius offset:(cpVect)offset; /// Create a ChipmunkPolyShape from this polyline. (Must be convex!) -(ChipmunkPolyShape *)asChipmunkPolyShapeWithBody:(ChipmunkBody *)body transform:(cpTransform)transform radius:(cpFloat)radius; @end /// Wrapper for the cpPolylineSet type. @interface ChipmunkPolylineSet : NSObject { @private NSMutableArray *_lines; } -(id)initWithPolylineSet:(cpPolylineSet *)set; +(ChipmunkPolylineSet *)fromPolylineSet:(cpPolylineSet *)set; @property(nonatomic, readonly) NSUInteger count; -(ChipmunkPolyline *)lineAtIndex:(NSUInteger)index; @end /** A sampler is an object that provides a basis function to build shapes from. This can be from a block of pixel data (loaded from a file, or dumped from the screen), or even a mathematical function such as Perlin noise. */ @interface ChipmunkAbstractSampler : NSObject { @protected cpFloat _marchThreshold; cpMarchSampleFunc _sampleFunc; } /// The threshold passed to the cpMarch*() functions. /// The value of the contour you want to extract. @property(nonatomic, assign) cpFloat marchThreshold; /// Get the primitive cpMarchSampleFunc used by this sampler. @property(nonatomic, readonly) cpMarchSampleFunc sampleFunc; /// Designated initializer. -(id)initWithSamplingFunction:(cpMarchSampleFunc)sampleFunc; /// Sample at a specific point. -(cpFloat)sample:(cpVect)pos; /// March a certain area of the sampler. -(ChipmunkPolylineSet *)march:(cpBB)bb xSamples:(NSUInteger)xSamples ySamples:(NSUInteger)ySamples hard:(bool)hard; @end /// A simple sampler type that wraps a block as it's sampling function. typedef cpFloat (^ChipmunkMarchSampleBlock)(cpVect point); @interface ChipmunkBlockSampler : ChipmunkAbstractSampler { ChipmunkMarchSampleBlock _block; } /// Initializes the sampler using a copy of the passed block. -(id)initWithBlock:(ChipmunkMarchSampleBlock)block; +(ChipmunkBlockSampler *)samplerWithBlock:(ChipmunkMarchSampleBlock)block; @end #import "ChipmunkImageSampler.h" #import "ChipmunkPointCloudSampler.h" #import "ChipmunkTileCache.h" Chipmunk2D-Chipmunk-7.0.3/objectivec/include/ObjectiveChipmunk/ChipmunkBody.h000066400000000000000000000211141347650476100271730ustar00rootroot00000000000000/* 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. */ @class ChipmunkShape; @class ChipmunkConstraint; /** Rigid bodies are the basic unit of simulation in Chipmunk. They hold the physical properties of an object (mass, position, rotation, velocity, etc.). After creating a ChipmunkBody object, you can attach collision shapes (ChipmunkShape) and joints (ChipmunkConstraint) to it. */ @interface ChipmunkBody : NSObject /// Get the ChipmunkBody object associciated with a cpBody pointer. /// Undefined if the cpBody wasn't created using Objective-Chipmunk. +(ChipmunkBody *)bodyFromCPBody:(cpBody *)body; /** Create an autoreleased rigid body with the given mass and moment. Guessing the moment of inertia is usually a bad idea. Use the moment estimation functions (cpMomentFor*()). */ + (id)bodyWithMass:(cpFloat)mass andMoment:(cpFloat)moment; /** Create an autoreleased static body. */ + (id)staticBody; /** Create an autoreleased kinematic body. */ + (id)kinematicBody; /** Initialize a rigid body with the given mass and moment of inertia. Guessing the moment of inertia is usually a bad idea. Use the moment estimation functions (cpMomentFor*()). */ - (id)initWithMass:(cpFloat)mass andMoment:(cpFloat)moment; /// Type of the body (dynamic, kinematic, static). @property(nonatomic, assign) cpBodyType type; /// Mass of the rigid body. Mass does not have to be expressed in any particular units, but relative masses should be consistent. @property(nonatomic, assign) cpFloat mass; /// Moment of inertia of the body. The mass tells you how hard it is to push an object, the MoI tells you how hard it is to spin the object. Don't try to guess the MoI, use the cpMomentFor*() functions to try and estimate it. @property(nonatomic, assign) cpFloat moment; /// Location of the body's center of gravity relative to it's position. Defaults to @c cpvzero. @property(nonatomic, assign) cpVect centerOfGravity; /// The position of the rigid body's center of gravity. @property(nonatomic, assign) cpVect position; /// The linear velocity of the rigid body. @property(nonatomic, assign) cpVect velocity; /// The linear force applied to the rigid body. Unlike in some physics engines, the force does not reset itself during each step. Make sure that you are reseting the force between frames if that is what you intended. @property(nonatomic, assign) cpVect force; /// The rotation angle of the rigid body in radians. @property(nonatomic, assign) cpFloat angle; /// The angular velocity of the rigid body in radians per second. @property(nonatomic, assign) cpFloat angularVelocity; /// The torque being applied to the rigid body. Like force, this property is not reset every frame. @property(nonatomic, assign) cpFloat torque; /// The rigid transform of the body. @property(nonatomic, readonly) cpTransform transform; /// Returns a pointer to the underlying cpBody C struct. @property(nonatomic, readonly) cpBody *body; /** An object that this constraint is associated with. You can use this get a reference to your game object or controller object from within callbacks. @attention Like most @c delegate properties this is a weak reference and does not call @c retain. This prevents reference cycles from occuring. */ @property(nonatomic, assign) id userData; /// Has the body been put to sleep by the space? @property(nonatomic, readonly) bool isSleeping; /// Get the kinetic energy of this body. @property(nonatomic, readonly) cpFloat kineticEnergy; /// Get the space the body is added to. @property(nonatomic, readonly) ChipmunkSpace *space; /** Convert from body local to world coordinates. Convert a point in world (absolute) coordinates to body local coordinates affected by the position and rotation of the rigid body. */ - (cpVect)localToWorld:(cpVect)v; /** Convert from world to body local Coordinates. Convert a point in body local coordinates coordinates to world (absolute) coordinates. */ - (cpVect)worldToLocal:(cpVect)v; /** Get the velocity of a point on a body. Get the world (absolute) velocity of a point on a rigid body specified in body local coordinates. */ - (cpVect)velocityAtLocalPoint:(cpVect)p; /** Get the velocity of a point on a body. Get the world (absolute) velocity of a point on a rigid body specified in world coordinates. */ - (cpVect)velocityAtWorldPoint:(cpVect)p; /** Apply a force to a rigid body. An offset of cpvzero is equivalent to adding directly to the force property. @param force A force in expressed in absolute (word) coordinates. @param offset An offset expressed in world coordinates. Note that it is still an offset, meaning that it's position is relative, but the rotation is not. */ - (void)applyForce:(cpVect)force atLocalPoint:(cpVect)point; - (void)applyForce:(cpVect)force atWorldPoint:(cpVect)point; /** Apply an impulse to a rigid body. @param impulse An impulse in expressed in absolute (word) coordinates. @param offset An offset expressed in world coordinates. Note that it is still an offset, meaning that it's position is relative, but the rotation is not. */ - (void)applyImpulse:(cpVect)impulse atLocalPoint:(cpVect)point; - (void)applyImpulse:(cpVect)impulse atWorldPoint:(cpVect)point; /// Wake up the body if it's sleeping, or reset the idle timer if it's active. - (void)activate; /// Wake up any bodies touching a static body through shape @c filter Pass @c nil for @c filter to away all touching bodies. - (void)activateStatic:(ChipmunkShape *)filter; /** Force the body to sleep immediately. The body will be added to the same group as @c group. When any object in a group is woken up, all of the bodies are woken up with it. If @c group is nil, then a new group is created and the body is added to it. It is an error pass a non-sleeping body as @c group. This is useful if you want an object to be inactive until something hits it such as a pile of boxes you want the player to plow through or a stalactite hanging from a cave ceiling. Make sure the body is fully set up before you call this. Adding this body or any shapes or constraints attached to it to a space, or modifying any of their properties automatically wake a body up. */ - (void)sleepWithGroup:(ChipmunkBody *)group; /** Equivalent to [ChipmunkBody sleepWithGroup:nil]. That is the object is forced to sleep immediately, but is not grouped with any other sleeping bodies. */ - (void)sleep; /// Get a list of shapes that are attached to this body and currently added to a space. - (NSArray *)shapes; /// Get a list of constraints that are attached to this body and currently added to a space. - (NSArray *)constraints; /// Body/arbiter iterator callback block type. typedef void (^ChipmunkBodyArbiterIteratorBlock)(cpArbiter *arbiter); /// Call @c block once for each arbiter that is currently active on the body. - (void)eachArbiter:(ChipmunkBodyArbiterIteratorBlock)block; /// Implements the ChipmunkBaseObject protocol, not particularly useful outside of the library code - (void)addToSpace:(ChipmunkSpace *)space; /// Implements the ChipmunkBaseObject protocol, not particularly useful outside of the library code - (void)removeFromSpace:(ChipmunkSpace *)space; /// Override this to change the way that the body's velocity is integrated. /// You should either understand how the cpBodyUpdateVelocity() function works, or use the super method. -(void)updateVelocity:(cpFloat)dt gravity:(cpVect)gravity damping:(cpFloat)damping; /// OVerride this to change the way that the body's position is intgrated. /// You should either understand how the cpBodyUpdatePosition() function works, or use the super method. -(void)updatePosition:(cpFloat)dt; @end Chipmunk2D-Chipmunk-7.0.3/objectivec/include/ObjectiveChipmunk/ChipmunkConstraint.h000066400000000000000000000415271347650476100304340ustar00rootroot00000000000000/* 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. */ /** Constraints connect two ChipmunkBody objects together. Most often constraints are simple joints, but can also be things like motors, friction generators or servos. @htmlonly @endhtmlonly */ @interface ChipmunkConstraint : NSObject { @private id _userData; } /// Returns a pointer to the underlying cpConstraint C struct. @property(nonatomic, readonly) cpConstraint *constraint; /// The first ChipmunkBody the constraint controls. @property(nonatomic, readonly) ChipmunkBody *bodyA; /// The second ChipmunkBody the constraint controls. @property(nonatomic, readonly) ChipmunkBody *bodyB; /// Get the ChipmunkConstraint object associciated with a cpConstraint pointer. /// Undefined if the cpConstraint wasn't created using Objective-Chipmunk. +(ChipmunkConstraint *)constraintFromCPConstraint:(cpConstraint *)constraint; /** Maximum force this constraint is allowed to use (defalts to infinity). This allows joints to be pulled apart if too much force is applied to them. It also allows you to use constraints as force or friction generators for controlling bodies. */ @property(nonatomic, assign) cpFloat maxForce; /** The 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. */ @property(nonatomic, assign) cpFloat errorBias; /** Maximum rate (speed) that a joint can be corrected at (defaults to infinity). Setting this value to a finite value allows you to control a joint like a servo motor. */ @property(nonatomic, assign) cpFloat maxBias; /** Whether or not the connected bodies should checked for collisions. Collisions are filtered before calling callbacks. Defaults to TRUE. */ @property(nonatomic, assign) BOOL collideBodies; /// Get the most recent impulse applied by this constraint. @property(nonatomic, readonly) cpFloat impulse; /// Get the space the body is added to. @property(nonatomic, readonly) ChipmunkSpace *space; /** An object that this constraint is associated with. You can use this get a reference to your game object or controller object from within callbacks. @attention Like most @c delegate properties this is a weak reference and does not call @c retain. This prevents reference cycles from occuring. */ @property(nonatomic, assign) id userData; /// Override this method to update a constraints parameters just before running the physics each step. -(void)preSolve:(ChipmunkSpace *)space; /// Override this method to poll values from a constraint each frame after the physics runs. /// This can be used to implement breakable joints for instance. -(void)postSolve:(ChipmunkSpace *)space; @end /** Pin joints hold a set distance between points on two bodies. Think of them as connecting a solid pin or rod between the two anchor points. */ @interface ChipmunkPinJoint : ChipmunkConstraint /** Create an autoreleased pin joint between the two bodies with the given anchor points. The distance is calculated when the joint is initialized. It can be set explicitly using the property. */ + (ChipmunkPinJoint *)pinJointWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b anchorA:(cpVect)anchorA anchorB:(cpVect)anchorB; /** Initialize a pin joint between the two bodies with the given anchor points. The distance is calculated when the joint is initialized. It can be set explicitly using the property. */ - (id)initWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b anchorA:(cpVect)anchorA anchorB:(cpVect)anchorB; /// The anchor point on the first body. @property(nonatomic, assign) cpVect anchorA; /// The anchor point on the second body. @property(nonatomic, assign) cpVect anchorB; /// The distance between the two anchor points that the joint keeps. @property(nonatomic, assign) cpFloat dist; @end /** Slide joints hold the distance between points on two bodies between a minimum and a maximum. Think of them as a telescoping ChipmunkPinJoint. */ @interface ChipmunkSlideJoint : ChipmunkConstraint /** Create an autoreleased slide joint between the two bodies with the given anchor points and distance range. */ + (ChipmunkSlideJoint *)slideJointWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b anchorA:(cpVect)anchorA anchorB:(cpVect)anchorB min:(cpFloat)min max:(cpFloat)max; /** Initialize a slide joint between the two bodies with the given anchor points and distance range. */ - (id)initWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b anchorA:(cpVect)anchorA anchorB:(cpVect)anchorB min:(cpFloat)min max:(cpFloat)max; /// The anchor point on the first body. @property(nonatomic, assign) cpVect anchorA; /// The anchor point on the second body. @property(nonatomic, assign) cpVect anchorB; /// The minimum allowed distance between anchor points. @property(nonatomic, assign) cpFloat min; /// The maximum allowed distance between anchor points. @property(nonatomic, assign) cpFloat max; @end /** Pivot joints hold two points on two bodies together allowing them to rotate freely around the pivot. */ @interface ChipmunkPivotJoint : ChipmunkConstraint /** Create an autoreleased pivot joint between the two bodies with the two anchor points. Make sure you have the bodies in the right place as the joint will fix itself as soon as you start simulating the space. */ + (ChipmunkPivotJoint *)pivotJointWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b anchorA:(cpVect)anchorA anchorB:(cpVect)anchorB; /** Create an autoreleased pivot joint between the two bodies by calculating the anchor points from the pivot point given in absolute coordinates. */ + (ChipmunkPivotJoint *)pivotJointWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b pivot:(cpVect)pivot; /** Initialize a pivot joint between the two bodies with the two anchor points. Make sure you have the bodies in the right place as the joint will fix itself as soon as you start simulating the space. */ - (id)initWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b anchorA:(cpVect)anchorA anchorB:(cpVect)anchorB; /** Initialize a pivot joint between the two bodies by calculating the anchor points from the pivot point given in absolute coordinates. */ - (id)initWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b pivot:(cpVect)pivot; /// The anchor point on the first body. @property(nonatomic, assign) cpVect anchorA; /// The anchor point on the second body. @property(nonatomic, assign) cpVect anchorB; @end /** Groove joints hold a pivot point on one body to line along a line segment on another like a pin in a groove. */ @interface ChipmunkGrooveJoint : ChipmunkConstraint /** Create an autoreleased groove joint between the two bodies. Make sure you have the bodies in the right place as the joint will snap into shape as soon as you start simulating the space. @param grooveA The start of the line segment on the first body. @param grooveB The end of the line segment on the first body. @param anchorB The anchor point on the second body that is held to the line segment on the first. */ + (ChipmunkGrooveJoint *)grooveJointWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b grooveA:(cpVect)grooveA grooveB:(cpVect)grooveB anchorB:(cpVect)anchorB; /** Initialize a groove joint between the two bodies. Make sure you have the bodies in the right place as the joint will snap into shape as soon as you start simulating the space. @param grooveA The start of the line segment on the first body. @param grooveB The end of the line segment on the first body. @param anchorB The anchor point on the second body that is held to the line segment on the first. */ - (id)initWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b grooveA:(cpVect)grooveA grooveB:(cpVect)grooveB anchorB:(cpVect)anchorB; /// The start point of the groove on the first body. @property(nonatomic, assign) cpVect grooveA; /// The end point of the groove on the first body. @property(nonatomic, assign) cpVect grooveB; /// The anchor point on the second body. @property(nonatomic, assign) cpVect anchorB; @end /** A spring with a damper. While a spring is not technically a constraint, the damper is. The spring forces are simply a convenience. */ @interface ChipmunkDampedSpring : ChipmunkConstraint /** Create an autoreleased damped spring between two bodies at the given anchor points. @param restLength The length the spring wants to contract or expand to. @param stiffness The young's modulus of the spring. @param damping The amount of viscous damping to apply. */ + (ChipmunkDampedSpring *)dampedSpringWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b anchorA:(cpVect)anchorA anchorB:(cpVect)anchorB restLength:(cpFloat)restLength stiffness:(cpFloat)stiffness damping:(cpFloat)damping; /** Initialize a damped spring between two bodies at the given anchor points. @param restLength The length the spring wants to contract or expand to. @param stiffness The young's modulus of the spring. @param damping The amount of viscous damping to apply. */ - (id)initWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b anchorA:(cpVect)anchorA anchorB:(cpVect)anchorB restLength:(cpFloat)restLength stiffness:(cpFloat)stiffness damping:(cpFloat)damping; /// The anchor point on the first body. @property(nonatomic, assign) cpVect anchorA; /// The anchor point on the second body. @property(nonatomic, assign) cpVect anchorB; /// The length the spring wants to contract or expand to. @property(nonatomic, assign) cpFloat restLength; /// The young's modulus of the spring. @property(nonatomic, assign) cpFloat stiffness; /// The amount of viscous damping to apply. @property(nonatomic, assign) cpFloat damping; @end /** Like a ChipmunkDampedSpring, but operates in a rotational fashion. */ @interface ChipmunkDampedRotarySpring : ChipmunkConstraint /** Create an autoreleased damped rotary spring between the given bodies. @param restAngle The angular offset in radians the spring attempts to keep between the two bodies. @param stiffness The young's modulus of the spring. @param damping The amount of viscous damping to apply. */ + (ChipmunkDampedRotarySpring *)dampedRotarySpringWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b restAngle:(cpFloat)restAngle stiffness:(cpFloat)stiffness damping:(cpFloat)damping; /** Initialize a damped rotary spring between the given bodies. @param restAngle The angular offset in radians the spring attempts to keep between the two bodies. @param stiffness The young's modulus of the spring. @param damping The amount of viscous damping to apply. */ - (id)initWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b restAngle:(cpFloat)restAngle stiffness:(cpFloat)stiffness damping:(cpFloat)damping; /// The angular offset the spring attempts to keep between the two bodies. @property(nonatomic, assign) cpFloat restAngle; /// The young's modulus of the spring. @property(nonatomic, assign) cpFloat stiffness; /// The amount of viscous damping to apply. @property(nonatomic, assign) cpFloat damping; @end /** Constrains the angle between two bodies. This joint is often used in conjuction with a separate ChipmunkPivotJoint in order to limit the rotation around the pivot. */ @interface ChipmunkRotaryLimitJoint : ChipmunkConstraint /** Create an autoreleased rotary limit joint between the two bodies and angular range in radians. Make sure you have the bodies in the right place as the joint will snap into shape as soon as you start simulating the space. */ + (ChipmunkRotaryLimitJoint *)rotaryLimitJointWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b min:(cpFloat)min max:(cpFloat)max; /** Create an autoreleased rotary limit joint between the two bodies and angular range in radians. Make sure you have the bodies in the right place as the joint will snap into shape as soon as you start simulating the space. */ - (id)initWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b min:(cpFloat)min max:(cpFloat)max; /// The minimum angular delta of the joint in radians. @property(nonatomic, assign) cpFloat min; /// The maximum angular delta of the joint in radians. @property(nonatomic, assign) cpFloat max; @end /** Simple motors make two objects spin relative to each other. They are most often used with the ChipmunkConstraint.maxForce property set to a finite value. */ @interface ChipmunkSimpleMotor : ChipmunkConstraint /// Create an autoreleased simple motor between the given bodies and relative rotation rate in radians per second. + (ChipmunkSimpleMotor *)simpleMotorWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b rate:(cpFloat)rate; /// Initialize a simple motor between the given bodies and relative rotation rate in radians per second. - (id)initWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b rate:(cpFloat)rate; /// The relative rotation speed of the two bodies in radians per second. @property(nonatomic, assign) cpFloat rate; @end /** Gear joints constrain the rotational speed of one body to another. A ratio of 1.0 will lock the rotation of two bodies together, and negative ratios will cause them to spin in opposite directions. You can also use gear joints as rotary servos by setting ChipmunkConstraint.maxForce and ChipmunkConstraint.maxBias to finite values and changing the ChipmunkGearJoint.phase property. */ @interface ChipmunkGearJoint : ChipmunkConstraint /** Create an autoreleased gear joint between the given bodies. @param phase The angular offset. @param ratio The ratio of the rotational speeds. */ + (ChipmunkGearJoint *)gearJointWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b phase:(cpFloat)phase ratio:(cpFloat)ratio; /** Initialize a gear joint between the given bodies. @param phase The angular offset in radians. @param ratio The ratio of the rotational speeds. */ - (id)initWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b phase:(cpFloat)phase ratio:(cpFloat)ratio; /// The angular offset in radians. @property(nonatomic, assign) cpFloat phase; /// The ratio of the rotational speeds. @property(nonatomic, assign) cpFloat ratio; @end /** Ratchet joints create rotary ratches similar to a socket wrench. */ @interface ChipmunkRatchetJoint : ChipmunkConstraint /** Create an autoreleased ratchet joint between the given bodies. @param phase The angular offset of the ratchet positions in radians. @param ratchet The angle in radians of each ratchet position. Negative values cause the ratchet to operate in the opposite direction. */ + (ChipmunkRatchetJoint *)ratchetJointWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b phase:(cpFloat)phase ratchet:(cpFloat)ratchet; /** Initialize a ratchet joint between the given bodies. @param phase The angular offset of the ratchet positions in radians. @param ratchet The angle in radians of each ratchet position. Negative values cause the ratchet to operate in the opposite direction. */ - (id)initWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b phase:(cpFloat)phase ratchet:(cpFloat)ratchet; /// The current ratchet position in radians. @property(nonatomic, assign) cpFloat angle; /// The angular offset of the ratchet positions in radians @property(nonatomic, assign) cpFloat phase; /// The angle in radians of each ratchet position. Negative values cause the ratchet to operate in the opposite direction. @property(nonatomic, assign) cpFloat ratchet; @end Chipmunk2D-Chipmunk-7.0.3/objectivec/include/ObjectiveChipmunk/ChipmunkImageSampler.h000066400000000000000000000103741347650476100306520ustar00rootroot00000000000000// Copyright 2013 Howling Moon Software. All rights reserved. // See http://chipmunk2d.net/legal.php for more information. #import "ObjectiveChipmunk/ObjectiveChipmunk.h" #import "ChipmunkAutoGeometry.h" #import #if TARGET_OS_IPHONE == 1 #import #endif /** Generic sampler used with bitmap data. Currently limited to 8 bit per component data. Bitmap samplers currently provide no filtering, but could be easily extended to do so. */ @interface ChipmunkBitmapSampler : ChipmunkAbstractSampler { @private NSUInteger _width, _height, _stride; NSUInteger _bytesPerPixel, _component; bool _flip; const uint8_t *_pixels; NSData *_pixelData; cpFloat _borderValue; cpBB _outputRect; } /// Width of the bitmap in pixels. @property(nonatomic, readonly) NSUInteger width; /// Height of the bitmap in pixels. @property(nonatomic, readonly) NSUInteger height; /// Bytes per pixel of the bitmap. (ex: RGBA8888 would be 4) @property(nonatomic, readonly) NSUInteger bytesPerPixel; /// Zero-based ndex of the component to sample. (ex: alpha of RGBA would be 3) @property(nonatomic, assign) NSUInteger component; /// NSData object holding the pixel data. @property(nonatomic, readonly) NSData *pixelData; /// Rect that the image maps to. /// Defaults to (0.5, 0.5, width - 0.5, height - 0.5) so that pixel centers will be cleanly sampled. @property(nonatomic, assign) cpBB outputRect; /** Init a sampler from bitmap data. Stride refers to the length of a row of pixels in bytes. (Generally just w*h*bytesPerPixel unless there is padding) Image must use one byte per component, but can have any number of components. @c component refers to the 0-based index of the component to sample. (i.e. 3 would sample the alpha in an RGBA bitmap) @c flip allows you to flip the image vertically to match how it migh be drawn. @c pixelData can be either a NSData or NSMutableData (i.e. for deformable terrain) that contains the bitmap data. */ -(id)initWithWidth:(NSUInteger)width height:(NSUInteger)height stride:(NSUInteger)stride bytesPerPixel:(NSUInteger)bytesPerPixel component:(NSUInteger)component flip:(bool)flip pixelData:(NSData *)pixelData; /// Set the border of the bitmap to repeat the edge pixels. -(void)setBorderRepeat; /// Set the border of the bitmap to be a specific value. -(void)setBorderValue:(cpFloat)borderValue; /// March the entire image. -(ChipmunkPolylineSet *)marchAllWithBorder:(bool)bordered hard:(bool)hard; @end /// Sampler built on top of a CGBitmapContext to allow deformable geometry. /// Very efficient when paired with a ChipmunkTileCache. @interface ChipmunkCGContextSampler : ChipmunkBitmapSampler { @private CGContextRef _context; } /// CGBitmapContext for this sampler. @property(nonatomic, readonly) CGContextRef context; /// NSMutableData object holding the pixel data. @property(nonatomic, readonly) NSMutableData *pixelData; /// Initialize a context based sampler. Must provide options for a valid context. /// Find out more here in the Quartz 2D Programming Guide. -(id)initWithWidth:(unsigned long)width height:(unsigned long)height colorSpace:(CGColorSpaceRef)colorSpace bitmapInfo:(CGBitmapInfo)bitmapInfo component:(NSUInteger)component; @end /// A CGBitmapContext sampler initialized with an CGImage. @interface ChipmunkImageSampler : ChipmunkCGContextSampler /// Helper method to easily load CGImageRefs by path. You are responsible for releasing the CGImage. +(CGImageRef)loadImage:(NSURL *)url; /// Initialize an image sampler of a certain size with a CGImage. /// If isMask is TRUE, the image will be loaded as a black and white image, if FALSE only the image alpha will be loaded. -(id)initWithImage:(CGImageRef)image isMask:(bool)isMask contextWidth:(NSUInteger)width contextHeight:(NSUInteger)height; /// Initialize an image sampler with an image file. /// If isMask is TRUE, the image will be loaded as a black and white image, if FALSE only the image alpha will be loaded. -(id)initWithImageFile:(NSURL *)url isMask:(bool)isMask; /// Return an autoreleased image sampler initialized with an image file. /// If isMask is TRUE, the image will be loaded as a black and white image, if FALSE only the image alpha will be loaded. +(ChipmunkImageSampler *)samplerWithImageFile:(NSURL *)url isMask:(bool)isMask; @end Chipmunk2D-Chipmunk-7.0.3/objectivec/include/ObjectiveChipmunk/ChipmunkMultiGrab.h000066400000000000000000000120221347650476100301620ustar00rootroot00000000000000/* 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. */ #import "ObjectiveChipmunk/ObjectiveChipmunk.h" @interface ChipmunkGrab : NSObject { NSArray *_chipmunkObjects; cpVect _pos; cpFloat _smoothing; ChipmunkShape *_grabbedShape; id _data; } /// Last touch location of the grab. @property(nonatomic, readonly) cpVect pos; /// The ChipmunkShape that this grab was created for. @property(nonatomic, readonly) ChipmunkShape *grabbedShape; /// User definable pointer @property(nonatomic, retain) id data; @end /// Simple class to implement multitouch grabbing of physics objects. @interface ChipmunkMultiGrab : NSObject { ChipmunkSpace *_space; NSMutableArray *_grabs; cpFloat _smoothing; cpFloat _grabForce; cpFloat _grabFriction; cpFloat _grabRotaryFriction; cpFloat _grabRadius; cpShapeFilter filter; bool (^_grabFilter)(ChipmunkShape *shape); cpFloat (^_grabSort)(ChipmunkShape *shape, cpFloat depth); bool _pushMode, _pullMode; cpFloat _pushMass; cpFloat _pushFriction; cpFloat _pushElasticity; cpCollisionType _pushCollisionType; } @property(nonatomic, assign) cpFloat smoothing; @property(nonatomic, assign) cpFloat grabForce; /// Layers used for the point query when grabbing objects. @property(nonatomic, assign) cpShapeFilter filter; /// Group used for the point query when grabbing objects @property(nonatomic, assign) cpGroup group; /// Gives you the opportunity to further filter shapes. Return FALSE to ignore a shape. /// The default implementation always returns TRUE. @property(nonatomic, copy) bool (^grabFilter)(ChipmunkShape *shape); /// When clicking on a spot where two shapes overlap, the default behavior is to grab the shape that /// overlaps the grab point the most. It's possible to use a custom sorting order instead however. /// The block is called with each shape and the grab depth. /// It should return a positive float. The shape with the highest value is grabbed. /// The block is only called if the touch location is within a shape. @property(nonatomic, copy) cpFloat (^grabSort)(ChipmunkShape *shape, cpFloat depth); /// Amount of friction applied by the touch. /// Should be less than the grabForce. Defaults to 0.0. @property(nonatomic, assign) cpFloat grabFriction; /// The amount torque to apply to the grab to keep it from spinning. /// Defaults to 0.0. @property(nonatomic, assign) cpFloat grabRotaryFriction; /// On a touch screen, a single point query can make it really hard to grab small objects with a fat finger. /// By providing a radius, it will make it much easier for users to grab objects. /// Defaults to 0.0. @property(nonatomic, assign) cpFloat grabRadius; @property(nonatomic, assign) bool pullMode; @property(nonatomic, assign) bool pushMode; @property(nonatomic, assign) cpFloat pushMass; @property(nonatomic, assign) cpFloat pushFriction; @property(nonatomic, assign) cpFloat pushElasticity; @property(nonatomic, assign) cpCollisionType pushCollisionType; @property(nonatomic, readonly) NSArray *grabs; /** @c space is the space to grab shapes in. @c smoothing is the amount of mouse smoothing to apply as percentage of remaining error per second. cpfpow(0.8, 60) is a good starting point that provides fast response, but smooth mouse updates. @c force is the force the grab points can apply. */ -(id)initForSpace:(ChipmunkSpace *)space withSmoothing:(cpFloat)smoothing withGrabForce:(cpFloat)grabForce; /// Start tracking a new grab point /// Returns the ChipmunkGrab that is tracking the touch, but only if a shape was grabbed. /// Returns nil when creating a push shape (if push mode is enabled), or when no shape is grabbed. -(ChipmunkGrab *)beginLocation:(cpVect)pos; /// Update a grab point. /// Returns the ChipmunkGrab that is tracking the touch, but only if the grab is tracking a shape. -(ChipmunkGrab *)updateLocation:(cpVect)pos; /// End a grab point. /// Returns the ChipmunkGrab that was tracking the touch, but only if the grab was tracking a shape. -(ChipmunkGrab *)endLocation:(cpVect)pos; @end Chipmunk2D-Chipmunk-7.0.3/objectivec/include/ObjectiveChipmunk/ChipmunkPointCloudSampler.h000066400000000000000000000021141347650476100317010ustar00rootroot00000000000000// Copyright 2013 Howling Moon Software. All rights reserved. // See http://chipmunk2d.net/legal.php for more information. #import "ObjectiveChipmunk/ObjectiveChipmunk.h" #import "ChipmunkAutoGeometry.h" /** A point cloud sampler allows you to perform deformable terrain like with a bitmap backed sampler, but without any bounds. It only requires memory for the points you add instead of large RAM chewing bitmap. However, unlike a bitmap, the deformation can only go one way. (i.e. You can add or remove terrain, but not both). Without any points, the sampler will return 1.0. Adding points will put "holes" in it causing it to return lower values. */ @interface ChipmunkPointCloudSampler : ChipmunkAbstractSampler { @private cpFloat _cellSize; cpSpatialIndex *_index; } /// Initialize the sampler with the given cell size, /// which should roughly match the size of the points added to the sampler. - (id)initWithCellSize:(cpFloat)cellSize; /// Add a point to the cloud and return the dirty rect for the point. -(cpBB)addPoint:(cpVect)pos radius:(cpFloat)radius fuzz:(cpFloat)fuzz; @end Chipmunk2D-Chipmunk-7.0.3/objectivec/include/ObjectiveChipmunk/ChipmunkShape.h000066400000000000000000000216161347650476100273450ustar00rootroot00000000000000/* 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. */ @class ChipmunkPointQueryInfo; @class ChipmunkSegmentQueryInfo; /// Abstract base class for collsion shape types. @interface ChipmunkShape : NSObject { @private id _userData; } /// Get the ChipmunkShape object associciated with a cpShape pointer. /// Undefined if the cpShape wasn't created using Objective-Chipmunk. +(ChipmunkShape *)shapeFromCPShape:(cpShape *)shape; /// Returns a pointer to the underlying cpShape C struct. @property(nonatomic, readonly) cpShape *shape; /// The ChipmunkBody that this shape is attached to. @property(nonatomic, retain) ChipmunkBody *body; // TODO doc @property(nonatomic, assign) cpFloat mass; @property(nonatomic, assign) cpFloat density; @property(nonatomic, readonly) cpFloat moment; @property(nonatomic, readonly) cpFloat area; @property(nonatomic, readonly) cpVect centerOfGravity; /// The axis-aligned bounding box for this shape. @property(nonatomic, readonly) cpBB bb; /// Sensor shapes send collision callback messages, but don't create a collision response. @property(nonatomic, assign) BOOL sensor; /// How bouncy this shape is. @property(nonatomic, assign) cpFloat elasticity; /// How much friction this shape has. @property(nonatomic, assign) cpFloat friction; /** The velocity of the shape's surface. This velocity is used in the collision response when calculating the friction only. */ @property(nonatomic, assign) cpVect surfaceVelocity; /** An object reference used as a collision type identifier. This is used when defining collision handlers. @attention Like most @c delegate properties this is a weak reference and does not call @c retain. */ @property(nonatomic, assign) cpCollisionType collisionType; /** The collision filtering parameters of this shape. */ @property(nonatomic, assign) cpShapeFilter filter; /// Get the space the body is added to. @property(nonatomic, readonly) ChipmunkSpace *space; /** An object that this shape is associated with. You can use this get a reference to your game object or controller object from within callbacks. @attention Like most @c delegate properties this is a weak reference and does not call @c retain. This prevents reference cycles from occuring. */ @property(nonatomic, assign) id userData; /// Update and cache the axis-aligned bounding box for this shape. - (cpBB)cacheBB; - (ChipmunkPointQueryInfo *)pointQuery:(cpVect)point; - (ChipmunkSegmentQueryInfo *)segmentQueryFrom:(cpVect)start to:(cpVect)end radius:(cpFloat)radius; @end @interface ChipmunkPointQueryInfo : NSObject { @private cpPointQueryInfo _info; } - (id)initWithInfo:(cpPointQueryInfo *)info; /// Returns a pointer to the underlying cpNearestPointQueryInfo C struct. @property(nonatomic, readonly) cpPointQueryInfo *info; /// The ChipmunkShape found. @property(nonatomic, readonly) ChipmunkShape *shape; /// The closest point on the surface of the shape to the point. @property(nonatomic, readonly) cpVect point; /// The distance between the point and the surface of the shape. /// Negative distances mean that the point is that depth inside the shape. @property(nonatomic, readonly) cpFloat distance; /// The gradient of the signed distance function. /// The same as info.point/info.dist, but accurate even for very small values of info.dist. @property(nonatomic, readonly) cpVect gradient; @end /// Holds collision information from segment queries. You should never need to create one. @interface ChipmunkSegmentQueryInfo : NSObject { @private cpSegmentQueryInfo _info; cpVect _start, _end; } - (id)initWithInfo:(cpSegmentQueryInfo *)info start:(cpVect)start end:(cpVect)end; /// Returns a pointer to the underlying cpSegmentQueryInfo C struct. @property(nonatomic, readonly) cpSegmentQueryInfo *info; /// The ChipmunkShape found. @property(nonatomic, readonly) ChipmunkShape *shape; /// The percentage between the start and end points where the collision occurred. @property(nonatomic, readonly) cpFloat t; /// The normal of the collision with the shape. @property(nonatomic, readonly) cpVect normal; /// The point of the collision in absolute (world) coordinates. @property(nonatomic, readonly) cpVect point; /// The distance from the start point where the collision occurred. @property(nonatomic, readonly) cpFloat dist; /// The start point. @property(nonatomic, readonly) cpVect start; /// The end point. @property(nonatomic, readonly) cpVect end; @end /// Holds collision information from segment queries. You should never need to create one. @interface ChipmunkShapeQueryInfo : NSObject { @private ChipmunkShape *_shape; cpContactPointSet _contactPoints; } - (id)initWithShape:(ChipmunkShape *)shape andPoints:(cpContactPointSet *)set; @property(nonatomic, readonly) ChipmunkShape *shape; @property(nonatomic, readonly) cpContactPointSet *contactPoints; @end /// A perfect circle shape. @interface ChipmunkCircleShape : ChipmunkShape /// Create an autoreleased circle shape with the given radius and offset from the center of gravity. + (id)circleWithBody:(ChipmunkBody *)body radius:(cpFloat)radius offset:(cpVect)offset; /// Initialize a circle shape with the given radius and offset from the center of gravity. - (id)initWithBody:(ChipmunkBody *)body radius:(cpFloat)radius offset:(cpVect)offset; /// The radius of the circle. @property(nonatomic, readonly) cpFloat radius; /// The offset from the center of gravity. @property(nonatomic, readonly) cpVect offset; @end /// A beveled (rounded) segment shape. @interface ChipmunkSegmentShape : ChipmunkShape /// Create an autoreleased segment shape with the given endpoints and radius. + (id)segmentWithBody:(ChipmunkBody *)body from:(cpVect)a to:(cpVect)b radius:(cpFloat)radius; /// Initialize a segment shape with the given endpoints and radius. - (id)initWithBody:(ChipmunkBody *)body from:(cpVect)a to:(cpVect)b radius:(cpFloat)radius; /// Let Chipmunk know about the geometry of adjacent segments to avoid colliding with endcaps. - (void)setPrevNeighbor:(cpVect)prev nextNeighbor:(cpVect)next; /// The start of the segment shape. @property(nonatomic, readonly) cpVect a; /// The end of the segment shape. @property(nonatomic, readonly) cpVect b; /// The normal of the segment shape. @property(nonatomic, readonly) cpVect normal; /// The beveling radius of the segment shape. @property(nonatomic, readonly) cpFloat radius; @end /// A convex polygon shape. @interface ChipmunkPolyShape : ChipmunkShape /// Create an autoreleased polygon shape from the given vertexes after applying the transform and with the given rounding radius. + (id)polyWithBody:(ChipmunkBody *)body count:(int)count verts:(const cpVect *)verts transform:(cpTransform)transform radius:(cpFloat)radius; /// Create an autoreleased box shape centered on the center of gravity. + (id)boxWithBody:(ChipmunkBody *)body width:(cpFloat)width height:(cpFloat)height radius:(cpFloat)radius; /// Create an autoreleased box shape with the given bounding box in body local coordinates and rounding radius. + (id)boxWithBody:(ChipmunkBody *)body bb:(cpBB)bb radius:(cpFloat)radius; /// Initialize a polygon shape from the given vertexes after applying the transform and with the given rounding radius. - (id)initWithBody:(ChipmunkBody *)body count:(int)count verts:(const cpVect *)verts transform:(cpTransform)transform radius:(cpFloat)radius; /// Initialize a box shape centered on the center of gravity. - (id)initBoxWithBody:(ChipmunkBody *)body width:(cpFloat)width height:(cpFloat)height radius:(cpFloat)radius; /// Initialize a box shape with the given bounding box in body local coordinates and rounding radius. - (id)initBoxWithBody:(ChipmunkBody *)body bb:(cpBB)bb radius:(cpFloat)radius; /// The number of vertexes in this polygon. @property(nonatomic, readonly) int count; /// Get the rounding radius of the polygon. @property(nonatomic, readonly) cpFloat radius; /// Access the vertexes of this polygon. - (cpVect)getVertex:(int)index; @end Chipmunk2D-Chipmunk-7.0.3/objectivec/include/ObjectiveChipmunk/ChipmunkSpace.h000066400000000000000000000345141347650476100273410ustar00rootroot00000000000000/* 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. */ /// Default to using the NEON optimized solver. #define CHIPMUNK_SPACE_USE_HASTY_SPACE 1 /** Chipmunk spaces are simulation containers. You add a bunch of physics objects to a space (rigid bodies, collision shapes, and joints) and step the entire space forward through time as a whole. If you have Chipmunk Pro, you'll want to use the ChipmunkHastySpace subclass instead as it has iPhone specific optimizations. Unfortunately because of how Objective-C code is linked I can't dynamically substitute a ChipmunkHastySpace from a static library. */ @interface ChipmunkSpace : NSObject { @protected struct cpSpace *_space; ChipmunkBody *_staticBody; NSMutableSet *_children; NSMutableArray *_handlers; id _userData; } /** The iteration count is how many solver passes the space should use when solving collisions and joints (default is 10). Fewer iterations mean less CPU usage, but lower quality (mushy looking) physics. */ @property(nonatomic, assign) int iterations; /// Global gravity value to use for all rigid bodies in this space (default value is @c cpvzero). @property(nonatomic, assign) cpVect gravity; /** Global viscous damping value to use for all rigid bodies in this space (default value is 1.0 which disables damping). This value is the fraction of velocity a body should have after 1 second. A value of 0.9 would mean that each second, a body would have 80% of the velocity it had the previous second. */ @property(nonatomic, assign) cpFloat damping; /// If a body is moving slower than this speed, it is considered idle. The default value is 0, which signals that the space should guess a good value based on the current gravity. @property(nonatomic, assign) cpFloat idleSpeedThreshold; /** Elapsed time before a group of idle bodies is put to sleep (defaults to infinity which disables sleeping). If an entire group of touching or jointed bodies has been idle for at least this long, the space will put all of the bodies into a sleeping state where they consume very little CPU. */ @property(nonatomic, assign) 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. */ @property(nonatomic, assign) 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. */ @property(nonatomic, assign) cpFloat collisionBias; /** Number of frames that contact information should persist. Defaults to 3. There is probably never a reason to change this value. */ @property(nonatomic, assign) cpTimestamp collisionPersistence; /// Returns a pointer to the underlying cpSpace C struct @property(nonatomic, readonly) cpSpace *space; /** The space's designated static body. Collision shapes added to the body will automatically be marked as static shapes, and rigid bodies that come to rest while touching or jointed to this body will fall asleep. */ @property(nonatomic, readonly) ChipmunkBody *staticBody; /** Retrieves the current (if you are in a callback from [ChipmunkSpace step:]) or most recent (outside of a [ChipmunkSpace step:] call) timestep. */ @property(nonatomic, readonly) cpFloat currentTimeStep; /** Returns true if the space is currently executing a timestep. */ @property(nonatomic, readonly) BOOL isLocked; @property(nonatomic, readonly) BOOL locked __deprecated; /** An object that this space is associated with. You can use this get a reference to your game state or controller object from within callbacks. @attention Like most @c delegate properties this is a weak reference and does not call @c retain. This prevents reference cycles from occuring. */ @property(nonatomic, assign) id userData; /// Get the ChipmunkSpace object associciated with a cpSpace pointer. /// Undefined if the cpSpace wasn't created using Objective-Chipmunk. +(ChipmunkSpace *)spaceFromCPSpace:(cpSpace *)space; /** Set the default collision handler. The default handler is used for all collisions when a specific collision handler cannot be found. The expected method selectors are as follows: @code - (bool)begin:(cpArbiter *)arbiter space:(ChipmunkSpace*)space - (bool)preSolve:(cpArbiter *)arbiter space:(ChipmunkSpace*)space - (void)postSolve:(cpArbiter *)arbiter space:(ChipmunkSpace*)space - (void)separate:(cpArbiter *)arbiter space:(ChipmunkSpace*)space @endcode */ - (void)setDefaultCollisionHandler:(id)delegate begin:(SEL)begin preSolve:(SEL)preSolve postSolve:(SEL)postSolve separate:(SEL)separate; /** Set a collision handler to handle specific collision types. The methods are called only when shapes with the specified collisionTypes collide. @c typeA and @c typeB should be the same object references set to ChipmunkShape.collisionType. They can be any uniquely identifying object. Class and global NSString objects work well as collision types as they are easy to get a reference to and do not require you to allocate any objects. The expected method selectors are as follows: @code - (bool)begin:(cpArbiter *)arbiter space:(ChipmunkSpace*)space - (bool)preSolve:(cpArbiter *)arbiter space:(ChipmunkSpace*)space - (void)postSolve:(cpArbiter *)arbiter space:(ChipmunkSpace*)space - (void)separate:(cpArbiter *)arbiter space:(ChipmunkSpace*)space @endcode */ - (void)addCollisionHandler:(id)delegate typeA:(cpCollisionType)a typeB:(cpCollisionType)b begin:(SEL)begin preSolve:(SEL)preSolve postSolve:(SEL)postSolve separate:(SEL)separate; /** Add an object to the space. This can be any object that implements the ChipmunkObject protocol. This includes all the basic types such as ChipmunkBody, ChipmunkShape and ChipmunkConstraint as well as any composite game objects you may define that implement the protocol. @warning This method may not be called from a collision handler callback. See smartAdd: or ChipmunkSpace.addPostStepCallback:selector:context: for information on how to do that. */ -(id)add:(NSObject *)obj; /** Remove an object from the space. This can be any object that implements the ChipmunkObject protocol. This includes all the basic types such as ChipmunkBody, ChipmunkShape and ChipmunkConstraint as well as any composite game objects you may define that implement the protocol. @warning This method may not be called from a collision handler callback. See smartRemove: or ChipmunkSpace.addPostStepCallback:selector:context: for information on how to do that. */ -(id)remove:(NSObject *)obj; /// Check if a space already contains a particular object: -(BOOL)contains:(NSObject *)obj; /// If the space is locked and it's unsafe to call add: it will call addPostStepAddition: instead. - (id)smartAdd:(NSObject *)obj; /// If the space is locked and it's unsafe to call remove: it will call addPostStepRemoval: instead. - (id)smartRemove:(NSObject *)obj; /// Handy utility method to add a border of collision segments around a box. See ChipmunkShape for more information on the other parameters. /// Returns an NSArray of the shapes. Since NSArray implements the ChipmunkObject protocol, you can use the [ChipmunkSpace remove:] method to remove the bounds. - (NSArray *)addBounds:(cpBB)bounds thickness:(cpFloat)radius elasticity:(cpFloat)elasticity friction:(cpFloat)friction filter:(cpShapeFilter)filter collisionType:(id)collisionType; /** Define a callback to be run just before [ChipmunkSpace step:] finishes. The main reason you want to define post-step callbacks is to get around the restriction that you cannot call the add/remove methods from a collision handler callback. Post-step callbacks run right before the next (or current) call to ChipmunkSpace.step: returns when it is safe to add and remove objects. You can only schedule one post-step callback per key value, this prevents you from accidentally removing an object twice. Registering a second callback for the same key is a no-op. The method signature of the method should be: @code - (void)postStepCallback:(id)key
    @endcode This makes it easy to call a removal method on your game controller to remove a game object that died or was destroyed as the result of a collision: @code [space addPostStepCallback:gameController selector:@selector(remove:) key:gameObject]; @endcode @attention Not to be confused with post-solve collision handler callbacks. @warning @c target and @c object cannot be retained by the ChipmunkSpace. If you need to release either after registering the callback, use autorelease to ensure that they won't be deallocated until after [ChipmunkSpace step:] returns. @see ChipmunkSpace.addPostStepRemoval: */ - (BOOL)addPostStepCallback:(id)target selector:(SEL)selector key:(id)key; /// Block type used with [ChipmunkSpace addPostStepBlock:] typedef void (^ChipmunkPostStepBlock)(void); /// Same as [ChipmunkSpace addPostStepCallback:] but with a block. The block is copied. - (BOOL)addPostStepBlock:(ChipmunkPostStepBlock)block key:(id)key; /// Add the Chipmunk Object to the space at the end of the step. - (void)addPostStepAddition:(NSObject *)obj; /// Remove the Chipmunk Object from the space at the end of the step. - (void)addPostStepRemoval:(NSObject *)obj; /// Return an array of ChipmunkNearestPointQueryInfo objects for shapes within @c maxDistance of @c point. /// The point is treated as having the given group and layers. - (NSArray *)pointQueryAll:(cpVect)point maxDistance:(cpFloat)maxDistance filter:(cpShapeFilter)filter; /// Find the closest shape to a point that is within @c maxDistance of @c point. /// The point is treated as having the given layers and group. - (ChipmunkPointQueryInfo *)pointQueryNearest:(cpVect)point maxDistance:(cpFloat)maxDistance filter:(cpShapeFilter)filter; /// Return a NSArray of ChipmunkSegmentQueryInfo objects for all the shapes that overlap the segment. The objects are unsorted. - (NSArray *)segmentQueryAllFrom:(cpVect)start to:(cpVect)end radius:(cpFloat)radius filter:(cpShapeFilter)filter; /// Returns the first shape that overlaps the given segment. The segment is treated as having the given group and layers. - (ChipmunkSegmentQueryInfo *)segmentQueryFirstFrom:(cpVect)start to:(cpVect)end radius:(cpFloat)radius filter:(cpShapeFilter)filter; /// Returns a NSArray of all shapes whose bounding boxes overlap the given bounding box. The box is treated as having the given group and layers. - (NSArray *)bbQueryAll:(cpBB)bb filter:(cpShapeFilter)filter; /// Returns a NSArray of ChipmunkShapeQueryInfo objects for all the shapes that overlap @c shape. - (NSArray *)shapeQueryAll:(ChipmunkShape *)shape; /// Returns true if the shape overlaps anything in the space. - (BOOL)shapeTest:(ChipmunkShape *)shape; /// Get a copy of the list of all the bodies in the space. - (NSArray *)bodies; /// Get a copy of the list of all the shapes in the space - (NSArray *)shapes; /// Get a copy of the list of all the constraints in the space - (NSArray *)constraints; /// Update all the static shapes. - (void)reindexStatic; /// Update the collision info for a single shape. /// Can be used to update individual static shapes that were moved or active shapes that were moved that you want to query against. - (void)reindexShape:(ChipmunkShape *)shape; /// Update the collision info for all shapes attached to a body. - (void)reindexShapesForBody:(ChipmunkBody *)body; /// Step time forward. While variable timesteps may be used, a constant timestep will allow you to reduce CPU usage by using fewer iterations. - (void)step:(cpFloat)dt; @end /// ChipmunkHastySpace is an Objective-Chipmunk wrapper for cpHastySpace. /// Subclass this class instead of ChipmunkSpace if you want to enable the cpHastySpace optimizations. /// If ChipmunkHastySpace is linked correctly, calling [[ChipmunkSpace alloc] init] will actually return a ChipmunkHastySpace. @interface ChipmunkHastySpace : ChipmunkSpace /// Number of threads to use for the solver. /// Setting 0 will choose the thread count automatically (recommended). /// There is currently little benefit in using more than 2 threads. /// Defaults to 1. @property(nonatomic, assign) NSUInteger threads; @end //MARK: Misc /** A macro that defines and initializes shape variables for you in a collision callback. They are initialized in the order that they were defined in the collision handler associated with the arbiter. If you defined the handler as: @code [space addCollisionHandler:target typeA:foo typeB:bar ...] @endcode You you will find that @code a->collision_type == 1 @endcode and @code b->collision_type == 2 @endcode. */ #define CHIPMUNK_ARBITER_GET_SHAPES(__arb__, __a__, __b__) ChipmunkShape *__a__, *__b__; { \ cpShape *__shapeA__, *__shapeB__; \ cpArbiterGetShapes(__arb__, &__shapeA__, &__shapeB__); \ __a__ = cpShapeGetUserData(__shapeA__); __b__ = cpShapeGetUserData(__shapeB__); \ } #define CHIPMUNK_ARBITER_GET_BODIES(__arb__, __a__, __b__) ChipmunkBody *__a__, *__b__; { \ cpBody *__bodyA__, *__bodyB__; \ cpArbiterGetBodies(__arb__, &__bodyA__, &__bodyB__); \ __a__ = cpBodyGetUserData(__bodyA__); __b__ = cpBodyGetUserData(__bodyB__); \ } Chipmunk2D-Chipmunk-7.0.3/objectivec/include/ObjectiveChipmunk/ChipmunkTileCache.h000066400000000000000000000063231347650476100301240ustar00rootroot00000000000000// Copyright 2013 Howling Moon Software. All rights reserved. // See http://chipmunk2d.net/legal.php for more information. #import "ObjectiveChipmunk/ObjectiveChipmunk.h" #import "ChipmunkAutoGeometry.h" @class ChipmunkCachedTile; /// A tile cache enables an efficient means of updating a large deformable terrain. /// General usage would be to pass a rectangle covering the viewport to ensureRect: /// and calling markDirtyRect: each time a change is made that requires an area to be resampled. @interface ChipmunkAbstractTileCache : NSObject { @private ChipmunkAbstractSampler *_sampler; ChipmunkSpace *_space; cpFloat _tileSize; cpFloat _samplesPerTile; cpVect _tileOffset; NSUInteger _tileCount, _cacheSize; cpSpatialIndex *_tileIndex; ChipmunkCachedTile *_cacheHead, *_cacheTail; cpBB _ensuredBB; bool _ensuredDirty; bool _marchHard; } /// Should the marching be hard or soft? /// See cpMarchHard() and cpMarchSoft() for more information. @property(nonatomic, assign) bool marchHard; /// Offset of the tile grid origin. @property(nonatomic, assign) cpVect tileOffset; /// The sampling function to use. @property(nonatomic, readonly) ChipmunkAbstractSampler *sampler; /// Create the cache from the given sampler, space to add the generated segments to, /// size of the tiles, and the number of samples for each tile. -(id)initWithSampler:(ChipmunkAbstractSampler *)sampler space:(ChipmunkSpace *)space tileSize:(cpFloat)tileSize samplesPerTile:(NSUInteger)samplesPerTile cacheSize:(NSUInteger)cacheSize; /// Clear out all the cached tiles to force a full regen. -(void)resetCache; /// Mark a region as needing an update. /// Geometry is not regenerated until ensureRect: is called. -(void)markDirtyRect:(cpBB)bounds; /// Ensure that the given rect has been fully generated and contains no dirty rects. -(void)ensureRect:(cpBB)bounds; /// Override this in a subclass to make custom polygon simplification behavior. /// Defaults to cpPolylineSimplifyCurves(polyline, 2.0f) -(cpPolyline *)simplify:(cpPolyline *)polyline; /// Override this method to construct the segment shapes. /// By default, it creates a 0 radius segment and sets 1.0 for friction and elasticity and nothing else. -(ChipmunkSegmentShape *)makeSegmentFor:(ChipmunkBody *)staticBody from:(cpVect)a to:(cpVect)b; @end /// Generic tile cache. Configurable enough to be useful for most uses. @interface ChipmunkBasicTileCache : ChipmunkAbstractTileCache { @private cpFloat _simplifyThreshold; cpFloat _segmentRadius; cpFloat _segmentFriction; cpFloat _segmentElasticity; cpShapeFilter _segmentFilter; cpCollisionType _segmentCollisionType; } /// Threshold value used by cpPolylineSimplifyCurves(). @property(nonatomic, assign) cpFloat simplifyThreshold; /// Radius of the generated segments. @property(nonatomic, assign) cpFloat segmentRadius; /// Friction of the generated segments. @property(nonatomic, assign) cpFloat segmentFriction; /// Elasticity of the generated segments. @property(nonatomic, assign) cpFloat segmentElasticity; /// Collision filter of the generated segments. @property(nonatomic, assign) cpShapeFilter segmentFilter; /// Collision type of the generated segments. @property(nonatomic, assign) cpCollisionType segmentCollisionType; @end Chipmunk2D-Chipmunk-7.0.3/objectivec/include/ObjectiveChipmunk/ObjectiveChipmunk.h000066400000000000000000000053051347650476100302140ustar00rootroot00000000000000/* 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. */ #import #if __has_feature(objc_arc) #define CP_DATA_POINTER_TYPE __unsafe_unretained id #define CP_GROUP_TYPE __unsafe_unretained id #define CP_COLLISION_TYPE_TYPE __unsafe_unretained id #else #define CP_DATA_POINTER_TYPE id #define CP_GROUP_TYPE id #define CP_COLLISION_TYPE_TYPE id #endif #ifdef CP_ALLOW_PRIVATE_ACCESS #import "chipmunk/chipmunk_private.h" #else #import "chipmunk/chipmunk.h" #import "chipmunk/chipmunk_structs.h" #endif /** Allows you to add composite objects to a space in a single method call. The easiest way to implement the ChipmunkObject protocol is to add a @c chipmunkObjects instance variable with a type of @c NSArray* to your class, create a synthesized property for it, and initialize it with the ChipmunkObjectFlatten() function. */ @protocol ChipmunkObject /// Returns a list of ChipmunkBaseObject objects. - (id )chipmunkObjects; @end /// A category to have NSArray implement the ChipmunkObject protocol. /// They make for very easy containers. @interface NSArray(ChipmunkObject) @end @class ChipmunkSpace; /** This protocol is implemented by objects that know how to add themselves to a space. It's used internally as part of the ChipmunkObject protocol. You should never need to implement it yourself. */ @protocol ChipmunkBaseObject - (void)addToSpace:(ChipmunkSpace *)space; - (void)removeFromSpace:(ChipmunkSpace *)space; @end #import "ChipmunkBody.h" #import "ChipmunkShape.h" #import "ChipmunkConstraint.h" #import "ChipmunkSpace.h" #import "ChipmunkMultiGrab.h" Chipmunk2D-Chipmunk-7.0.3/objectivec/src/000077500000000000000000000000001347650476100201625ustar00rootroot00000000000000Chipmunk2D-Chipmunk-7.0.3/objectivec/src/ChipmunkAutoGeometry.m000066400000000000000000000120441347650476100244640ustar00rootroot00000000000000// Copyright 2013 Howling Moon Software. All rights reserved. // See http://chipmunk2d.net/legal.php for more information. #import #import "ChipmunkAutoGeometry.h" @implementation ChipmunkPolyline -(id)initWithPolyline:(cpPolyline *)line { if((self = [super init])){ _line = line; } return self; } -(void)dealloc { cpPolylineFree(_line); [super dealloc]; } +(ChipmunkPolyline *)fromPolyline:(cpPolyline *)line { return [[[self alloc] initWithPolyline:line] autorelease]; } -(bool)isClosed { return cpPolylineIsClosed(_line); } -(cpFloat)area { if(_area == 0.0 && [self isClosed]){ _area = cpAreaForPoly(_line->count - 1, _line->verts, 0.0); } return _area; } -(cpVect)centroid { cpAssertHard([self isClosed], "Cannot compute the centroid of a non-looped polyline."); return cpCentroidForPoly(_line->count - 1, _line->verts); } -(cpFloat)momentForMass:(cpFloat)mass offset:(cpVect)offset { cpAssertHard([self isClosed], "Cannot compute the moment of a non-looped polyline."); return cpMomentForPoly(mass, _line->count - 1, _line->verts, offset, 0.0); } -(NSUInteger)count {return _line->count;} -(const cpVect *)verts {return _line->verts;} -(ChipmunkPolyline *)simplifyCurves:(cpFloat)tolerance { return [ChipmunkPolyline fromPolyline:cpPolylineSimplifyCurves(_line, tolerance)]; } -(ChipmunkPolyline *)simplifyVertexes:(cpFloat)tolerance { return [ChipmunkPolyline fromPolyline:cpPolylineSimplifyVertexes(_line, tolerance)]; } -(ChipmunkPolyline *)toConvexHull:(cpFloat)tolerance { return [ChipmunkPolyline fromPolyline:cpPolylineToConvexHull(_line, tolerance)]; } -(ChipmunkPolyline *)toConvexHull { return [self toConvexHull:0.0]; } -(ChipmunkPolylineSet *)toConvexHulls_BETA:(cpFloat)tolerance { cpPolylineSet *set = cpPolylineConvexDecomposition_BETA(_line, tolerance); ChipmunkPolylineSet *value = [ChipmunkPolylineSet fromPolylineSet:set]; cpPolylineSetFree(set, FALSE); return value; } -(NSArray *)asChipmunkSegmentsWithBody:(ChipmunkBody *)body radius:(cpFloat)radius offset:(cpVect)offset { NSMutableArray *arr = [NSMutableArray arrayWithCapacity:_line->count]; cpVect a = cpvadd(_line->verts[0], offset); for(int i=1; i<_line->count; i++){ cpVect b = cpvadd(_line->verts[i], offset); [arr addObject:[ChipmunkSegmentShape segmentWithBody:body from:a to:b radius:radius]]; a = b; } return arr; } -(ChipmunkPolyShape *)asChipmunkPolyShapeWithBody:(ChipmunkBody *)body transform:(cpTransform)transform radius:(cpFloat)radius { cpAssertHard([self isClosed], "Cannot create a poly shape for a non-closed polyline."); return [ChipmunkPolyShape polyWithBody:body count:_line->count - 1 verts:_line->verts transform:transform radius:radius]; } @end @implementation ChipmunkPolylineSet -(id)initWithPolylineSet:(cpPolylineSet *)set { if((self = [super init])){ _lines = [[NSMutableArray alloc] initWithCapacity:set->count]; for(int i=0; icount; i++) [_lines addObject:[ChipmunkPolyline fromPolyline:set->lines[i]]]; } return self; } -(void)dealloc { [_lines release]; [super dealloc]; } +(ChipmunkPolylineSet *)fromPolylineSet:(cpPolylineSet *)set { return [[[self alloc] initWithPolylineSet:set] autorelease]; } -(NSUInteger)count {return _lines.count;} -(ChipmunkPolyline *)lineAtIndex:(NSUInteger)index { return [_lines objectAtIndex:index]; } - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id *)stackbuf count:(NSUInteger)len { return [_lines countByEnumeratingWithState:state objects:stackbuf count:len]; } @end @implementation ChipmunkAbstractSampler @synthesize marchThreshold = _marchThreshold; @synthesize sampleFunc = _sampleFunc; -(id)init { @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:[NSString stringWithFormat:@"Use designated initializer initWithSamplingFunction: to initialize a sampler."] userInfo:nil ]; } -(id)initWithSamplingFunction:(cpMarchSampleFunc)sampleFunc { if((self = [super init])){ _sampleFunc = sampleFunc; _marchThreshold = 0.5; } return self; } -(cpFloat)sample:(cpVect)pos { return _sampleFunc(pos, self); } -(ChipmunkPolylineSet *)march:(cpBB)bb xSamples:(NSUInteger)xSamples ySamples:(NSUInteger)ySamples hard:(bool)hard { cpPolylineSet set; cpPolylineSetInit(&set); (hard ? cpMarchHard : cpMarchSoft)( bb, xSamples, ySamples, _marchThreshold, (cpMarchSegmentFunc)cpPolylineSetCollectSegment, &set, _sampleFunc, self ); ChipmunkPolylineSet *value = [ChipmunkPolylineSet fromPolylineSet:&set]; cpPolylineSetDestroy(&set, FALSE); return value; } @end @implementation ChipmunkBlockSampler static cpFloat SampleFromBlock(cpVect point, ChipmunkBlockSampler *self) { return self->_block(point); } -(id)initWithBlock:(ChipmunkMarchSampleBlock)block { if((self = [super initWithSamplingFunction:(cpMarchSampleFunc)SampleFromBlock])){ _block = [block copy]; } return self; } +(ChipmunkBlockSampler *)samplerWithBlock:(ChipmunkMarchSampleBlock)block { return [[[self alloc] initWithBlock:block] autorelease]; } -(void)dealloc { [_block release]; [super dealloc]; } @end Chipmunk2D-Chipmunk-7.0.3/objectivec/src/ChipmunkBody.m000066400000000000000000000146221347650476100227410ustar00rootroot00000000000000/* 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. */ #import "ObjectiveChipmunk/ObjectiveChipmunk.h" @interface ChipmunkSpace(DoubleDispatch) - (ChipmunkBody *)addBody:(ChipmunkBody *)obj; - (ChipmunkBody *)removeBody:(ChipmunkBody *)obj; @end @implementation ChipmunkBody { cpBody _body; id _userData; } // MARK: Integration Helpers -(void)updateVelocity:(cpFloat)dt gravity:(cpVect)gravity damping:(cpFloat)damping { cpBodyUpdateVelocity(&_body, gravity, damping, dt); } -(void)updatePosition:(cpFloat)dt { cpBodyUpdatePosition(&_body, dt); } static void VelocityFunction (cpBody *body, cpVect gravity, cpFloat damping, cpFloat dt) { [(ChipmunkBody *)body->userData updateVelocity:dt gravity:gravity damping:damping]; } static void PositionFunction (cpBody *body, cpFloat dt) { [(ChipmunkBody *)body->userData updatePosition:dt]; } // Check if the method was overridden. // No reason to add the extra method overhead if it's not needed. -(BOOL)methodIsOverriden:(SEL)selector { return ([self methodForSelector:selector] != [[ChipmunkBody class] instanceMethodForSelector:selector]); } // MARK: Constructors +(ChipmunkBody *)bodyFromCPBody:(cpBody *)body { ChipmunkBody *obj = body->userData; cpAssertHard([obj isKindOfClass:[ChipmunkBody class]], "'body->data' is not a pointer to a ChipmunkBody object."); return obj; } + (id)bodyWithMass:(cpFloat)mass andMoment:(cpFloat)moment { return [[[self alloc] initWithMass:mass andMoment:moment] autorelease]; } + (id)staticBody { ChipmunkBody *body = [[self alloc] initWithMass:0.0f andMoment:0.0f]; body.type = CP_BODY_TYPE_STATIC; return [body autorelease]; } + (id)kinematicBody { ChipmunkBody *body = [[self alloc] initWithMass:0.0f andMoment:0.0f]; body.type = CP_BODY_TYPE_KINEMATIC; return [body autorelease]; } - (id)initWithMass:(cpFloat)mass andMoment:(cpFloat)moment { if((self = [super init])){ cpBodyInit(&_body, mass, moment); _body.userData = self; // Setup integration callbacks if necessary. if([self methodIsOverriden:@selector(updateVelocity:gravity:damping:)]){ _body.velocity_func = VelocityFunction; } if([self methodIsOverriden:@selector(updatePosition:)]){ _body.position_func = PositionFunction; } } return self; } - (void) dealloc { cpBodyDestroy(&_body); [super dealloc]; } - (cpTransform)transform {return _body.transform;} - (cpBody *)body {return &_body;} @synthesize userData = _userData; // accessor macros #define getter(type, lower, upper) \ - (type)lower {return cpBodyGet##upper(&_body);} #define setter(type, lower, upper) \ - (void)set##upper:(type)value {cpBodySet##upper(&_body, value);}; #define both(type, lower, upper) \ getter(type, lower, upper) \ setter(type, lower, upper) both(cpBodyType, type, Type) both(cpFloat, mass, Mass) both(cpFloat, moment, Moment) both(cpVect, centerOfGravity, CenterOfGravity) both(cpVect, position, Position) both(cpVect, velocity, Velocity) both(cpVect, force, Force) both(cpFloat, angle, Angle) both(cpFloat, angularVelocity, AngularVelocity) both(cpFloat, torque, Torque) -(ChipmunkSpace *)space { cpSpace *space = cpBodyGetSpace(&_body); return (ChipmunkSpace *)(space ? cpSpaceGetUserData(space) : nil); } - (cpFloat)kineticEnergy {return cpBodyKineticEnergy(&_body);} - (cpVect)localToWorld:(cpVect)v {return cpBodyLocalToWorld(&_body, v);} - (cpVect)worldToLocal:(cpVect)v {return cpBodyWorldToLocal(&_body, v);} - (cpVect)velocityAtLocalPoint:(cpVect)p {return cpBodyGetVelocityAtLocalPoint(&_body, p);} - (cpVect)velocityAtWorldPoint:(cpVect)p {return cpBodyGetVelocityAtWorldPoint(&_body, p);} - (void)applyForce:(cpVect)force atLocalPoint:(cpVect)point {cpBodyApplyForceAtLocalPoint(&_body, force, point);} - (void)applyForce:(cpVect)force atWorldPoint:(cpVect)point {cpBodyApplyForceAtWorldPoint(&_body, force, point);} - (void)applyImpulse:(cpVect)impulse atLocalPoint:(cpVect)point {cpBodyApplyImpulseAtLocalPoint(&_body, impulse, point);} - (void)applyImpulse:(cpVect)impulse atWorldPoint:(cpVect)point {cpBodyApplyImpulseAtWorldPoint(&_body, impulse, point);} - (bool)isSleeping {return cpBodyIsSleeping(&_body);} - (void)activate {cpBodyActivate(&_body);} - (void)activateStatic:(ChipmunkShape *)filter {cpBodyActivateStatic(&_body, filter.shape);} - (void)sleepWithGroup:(ChipmunkBody *)group {cpBodySleepWithGroup(&_body, group.body);} - (void)sleep {cpBodySleep(&_body);} - (NSArray *)chipmunkObjects {return [NSArray arrayWithObject:self];} - (void)addToSpace:(ChipmunkSpace *)space {[space addBody:self];} - (void)removeFromSpace:(ChipmunkSpace *)space {[space removeBody:self];} static void PushShape(cpBody *ignored, cpShape *shape, NSMutableArray *arr){[arr addObject:shape->userData];} - (NSArray *)shapes { NSMutableArray *arr = [NSMutableArray array]; cpBodyEachShape(&_body, (cpBodyShapeIteratorFunc)PushShape, arr); return arr; } static void PushConstraint(cpBody *ignored, cpConstraint *constraint, NSMutableArray *arr){[arr addObject:constraint->userData];} - (NSArray *)constraints { NSMutableArray *arr = [NSMutableArray array]; cpBodyEachConstraint(&_body, (cpBodyConstraintIteratorFunc)PushConstraint, arr); return arr; } static void CallArbiterBlock(cpBody *body, cpArbiter *arbiter, ChipmunkBodyArbiterIteratorBlock block){block(arbiter);} - (void)eachArbiter:(ChipmunkBodyArbiterIteratorBlock)block { cpBodyEachArbiter(&_body, (cpBodyArbiterIteratorFunc)CallArbiterBlock, block); } @end Chipmunk2D-Chipmunk-7.0.3/objectivec/src/ChipmunkConstraint.m000066400000000000000000000326241347650476100241720ustar00rootroot00000000000000/* 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. */ #import "ObjectiveChipmunk/ObjectiveChipmunk.h" @interface ChipmunkSpace(DoubleDispatch) - (ChipmunkConstraint *)addConstraint:(ChipmunkConstraint *)obj; - (ChipmunkConstraint *)removeConstraint:(ChipmunkConstraint *)obj; @end @implementation ChipmunkConstraint @synthesize userData = _userData; +(ChipmunkConstraint *)constraintFromCPConstraint:(cpConstraint *)constraint { ChipmunkConstraint *obj = constraint->userData; cpAssertHard([obj isKindOfClass:[ChipmunkConstraint class]], "'constraint->data' is not a pointer to a ChipmunkConstraint object."); return obj; } - (void) dealloc { cpConstraint *constraint = self.constraint; [self.bodyA release]; [self.bodyB release]; cpConstraintDestroy(constraint); [super dealloc]; } - (cpConstraint *)constraint { [self doesNotRecognizeSelector:_cmd]; return nil; } // accessor macros #define getter(type, lower, upper) \ - (type)lower {return cpConstraintGet##upper(self.constraint);} #define setter(type, lower, upper) \ - (void)set##upper:(type)value {cpConstraintSet##upper(self.constraint, value);}; #define both(type, lower, upper) \ getter(type, lower, upper) \ setter(type, lower, upper) both(cpFloat, maxForce, MaxForce) both(cpFloat, errorBias, ErrorBias) both(cpFloat, maxBias, MaxBias) both(BOOL, collideBodies, CollideBodies) -(cpFloat)impulse {return cpConstraintGetImpulse(self.constraint);} -(ChipmunkSpace *)space { cpSpace *space = cpConstraintGetSpace(self.constraint); return (ChipmunkSpace *)(space ? cpSpaceGetUserData(space) : nil); } - (ChipmunkBody *)bodyA { cpBody *body = cpConstraintGetBodyA(self.constraint); return (body ? cpBodyGetUserData(body) : nil); } //- (void)setBodyA:(ChipmunkBody *)value { // if(self.bodyA != value){ // [self.bodyA release]; // self.constraint->a = [[value retain] body]; // } //} - (ChipmunkBody *)bodyB { cpBody *body = cpConstraintGetBodyB(self.constraint); return (body ? cpBodyGetUserData(body) : nil); } //- (void)setBodyB:(ChipmunkBody *)value { // if(self.bodyB != value){ // [self.bodyB release]; // self.constraint->b = [[value retain] body]; // } //} - (NSArray *)chipmunkObjects {return [NSArray arrayWithObject:self];} - (void)addToSpace:(ChipmunkSpace *)space {[space addConstraint:self];} - (void)removeFromSpace:(ChipmunkSpace *)space {[space removeConstraint:self];} -(void)preSolve:(ChipmunkSpace *)space {} -(void)postSolve:(ChipmunkSpace *)space {} // MARK: Callbacks static void PreSolve(cpConstraint *constraint, cpSpace *space) { [(ChipmunkConstraint *)constraint->userData preSolve:(ChipmunkSpace *)space->userData]; } static void PostSolve(cpConstraint *constraint, cpSpace *space) { [(ChipmunkConstraint *)constraint->userData postSolve:(ChipmunkSpace *)space->userData]; } // Check if the method was overridden. // No reason to add the extra method overhead if it's not needed. -(BOOL)methodIsOverriden:(SEL)selector { return ([self methodForSelector:selector] != [[ChipmunkConstraint class] instanceMethodForSelector:selector]); } -(void)setupCallbacks { if([self methodIsOverriden:@selector(preSolve:)]){ cpConstraintSetPreSolveFunc(self.constraint, PreSolve); } if([self methodIsOverriden:@selector(postSolve:)]){ cpConstraintSetPostSolveFunc(self.constraint, PostSolve); } } @end // accessor macros #define getter2(type, struct, lower, upper) \ - (type)lower {return struct##Get##upper((cpConstraint *)&_constraint);} #define setter2(type, struct, lower, upper) \ - (void)set##upper:(type)value {struct##Set##upper((cpConstraint *)&_constraint, value);}; #define both2(type, struct, lower, upper) \ getter2(type, struct, lower, upper) \ setter2(type, struct, lower, upper) @implementation ChipmunkPinJoint { cpPinJoint _constraint; } + (id)pinJointWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b anchorA:(cpVect)anchorA anchorB:(cpVect)anchorB { return [[[self alloc] initWithBodyA:a bodyB:b anchorA:anchorA anchorB:anchorB] autorelease]; } - (cpConstraint *)constraint {return (cpConstraint *)&_constraint;} - (id)initWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b anchorA:(cpVect)anchorA anchorB:(cpVect)anchorB { if((self = [super init])){ [a retain]; [b retain]; cpPinJointInit(&_constraint, a.body, b.body, anchorA, anchorB); self.constraint->userData = self; [self setupCallbacks]; } return self; } both2(cpVect, cpPinJoint, anchorA, AnchorA) both2(cpVect, cpPinJoint, anchorB, AnchorB) both2(cpFloat, cpPinJoint, dist, Dist) @end @implementation ChipmunkSlideJoint { cpSlideJoint _constraint; } + (id)slideJointWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b anchorA:(cpVect)anchorA anchorB:(cpVect)anchorB min:(cpFloat)min max:(cpFloat)max { return [[[self alloc] initWithBodyA:a bodyB:b anchorA:anchorA anchorB:anchorB min:min max:max] autorelease]; } - (cpConstraint *)constraint {return (cpConstraint *)&_constraint;} - (id)initWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b anchorA:(cpVect)anchorA anchorB:(cpVect)anchorB min:(cpFloat)min max:(cpFloat)max { if((self = [super init])){ [a retain]; [b retain]; cpSlideJointInit(&_constraint, a.body, b.body, anchorA, anchorB, min, max); self.constraint->userData = self; [self setupCallbacks]; } return self; } both2(cpVect, cpSlideJoint, anchorA, AnchorA) both2(cpVect, cpSlideJoint, anchorB, AnchorB) both2(cpFloat, cpSlideJoint, min, Min) both2(cpFloat, cpSlideJoint, max, Max) @end @implementation ChipmunkPivotJoint { cpPivotJoint _constraint; } + (id)pivotJointWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b anchorA:(cpVect)anchorA anchorB:(cpVect)anchorB { return [[[self alloc] initWithBodyA:a bodyB:b anchorA:anchorA anchorB:anchorB] autorelease]; } + (id)pivotJointWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b pivot:(cpVect)pivot { return [[[self alloc] initWithBodyA:a bodyB:b pivot:pivot] autorelease]; } - (cpConstraint *)constraint {return (cpConstraint *)&_constraint;} - (id)initWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b anchorA:(cpVect)anchorA anchorB:(cpVect)anchorB { if((self = [super init])){ [a retain]; [b retain]; cpPivotJointInit(&_constraint, a.body, b.body, anchorA, anchorB); self.constraint->userData = self; [self setupCallbacks]; } return self; } - (id)initWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b pivot:(cpVect)pivot { return [self initWithBodyA:a bodyB:b anchorA:[a worldToLocal:pivot] anchorB:[b worldToLocal:pivot]]; } both2(cpVect, cpPivotJoint, anchorA, AnchorA) both2(cpVect, cpPivotJoint, anchorB, AnchorB) @end @implementation ChipmunkGrooveJoint { cpGrooveJoint _constraint; } + (id)grooveJointWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b grooveA:(cpVect)grooveA grooveB:(cpVect)grooveB anchorB:(cpVect)anchorB { return [[[self alloc] initWithBodyA:a bodyB:b grooveA:grooveA grooveB:grooveB anchorB:anchorB] autorelease]; } - (cpConstraint *)constraint {return (cpConstraint *)&_constraint;} - (id)initWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b grooveA:(cpVect)grooveA grooveB:(cpVect)grooveB anchorB:(cpVect)anchorB { if((self = [super init])){ [a retain]; [b retain]; cpGrooveJointInit(&_constraint, a.body, b.body, grooveA, grooveB, anchorB); self.constraint->userData = self; [self setupCallbacks]; } return self; } both2(cpVect, cpGrooveJoint, grooveA, GrooveA) both2(cpVect, cpGrooveJoint, grooveB, GrooveB) both2(cpVect, cpGrooveJoint, anchorB, AnchorB) @end @implementation ChipmunkDampedSpring { cpDampedSpring _constraint; } + (id)dampedSpringWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b anchorA:(cpVect)anchorA anchorB:(cpVect)anchorB restLength:(cpFloat)restLength stiffness:(cpFloat)stiffness damping:(cpFloat)damping { return [[[self alloc] initWithBodyA:a bodyB:b anchorA:anchorA anchorB:anchorB restLength:restLength stiffness:stiffness damping:damping] autorelease]; } - (cpConstraint *)constraint {return (cpConstraint *)&_constraint;} - (id)initWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b anchorA:(cpVect)anchorA anchorB:(cpVect)anchorB restLength:(cpFloat)restLength stiffness:(cpFloat)stiffness damping:(cpFloat)damping { if((self = [super init])){ [a retain]; [b retain]; cpDampedSpringInit(&_constraint, a.body, b.body, anchorA, anchorB, restLength, stiffness, damping); self.constraint->userData = self; [self setupCallbacks]; } return self; } both2(cpVect, cpDampedSpring, anchorA, AnchorA) both2(cpVect, cpDampedSpring, anchorB, AnchorB) both2(cpFloat, cpDampedSpring, restLength, RestLength) both2(cpFloat, cpDampedSpring, stiffness, Stiffness) both2(cpFloat, cpDampedSpring, damping, Damping) @end @implementation ChipmunkDampedRotarySpring { cpDampedRotarySpring _constraint; } + (id)dampedRotarySpringWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b restAngle:(cpFloat)restAngle stiffness:(cpFloat)stiffness damping:(cpFloat)damping { return [[[self alloc] initWithBodyA:a bodyB:b restAngle:restAngle stiffness:stiffness damping:damping] autorelease]; } - (cpConstraint *)constraint {return (cpConstraint *)&_constraint;} - (id)initWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b restAngle:(cpFloat)restAngle stiffness:(cpFloat)stiffness damping:(cpFloat)damping { if((self = [super init])){ [a retain]; [b retain]; cpDampedRotarySpringInit(&_constraint, a.body, b.body, restAngle, stiffness, damping); self.constraint->userData = self; [self setupCallbacks]; } return self; } both2(cpFloat, cpDampedRotarySpring, restAngle, RestAngle) both2(cpFloat, cpDampedRotarySpring, stiffness, Stiffness) both2(cpFloat, cpDampedRotarySpring, damping, Damping) @end @implementation ChipmunkRotaryLimitJoint { cpRotaryLimitJoint _constraint; } + (id)rotaryLimitJointWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b min:(cpFloat)min max:(cpFloat)max { return [[[self alloc] initWithBodyA:a bodyB:b min:min max:max] autorelease]; } - (cpConstraint *)constraint {return (cpConstraint *)&_constraint;} - (id)initWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b min:(cpFloat)min max:(cpFloat)max { if((self = [super init])){ [a retain]; [b retain]; cpRotaryLimitJointInit(&_constraint, a.body, b.body, min, max); self.constraint->userData = self; [self setupCallbacks]; } return self; } both2(cpFloat, cpRotaryLimitJoint, min, Min) both2(cpFloat, cpRotaryLimitJoint, max, Max) @end @implementation ChipmunkSimpleMotor { cpSimpleMotor _constraint; } + (id)simpleMotorWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b rate:(cpFloat)rate { return [[[self alloc] initWithBodyA:a bodyB:b rate:rate] autorelease]; } - (cpConstraint *)constraint {return (cpConstraint *)&_constraint;} - (id)initWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b rate:(cpFloat)rate { if((self = [super init])){ [a retain]; [b retain]; cpSimpleMotorInit(&_constraint, a.body, b.body, rate); self.constraint->userData = self; [self setupCallbacks]; } return self; } both2(cpFloat, cpSimpleMotor, rate, Rate) @end @implementation ChipmunkGearJoint { cpGearJoint _constraint; } + (id)gearJointWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b phase:(cpFloat)phase ratio:(cpFloat)ratio { return [[[self alloc] initWithBodyA:a bodyB:b phase:phase ratio:ratio] autorelease]; } - (cpConstraint *)constraint {return (cpConstraint *)&_constraint;} - (id)initWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b phase:(cpFloat)phase ratio:(cpFloat)ratio { if((self = [super init])){ [a retain]; [b retain]; cpGearJointInit(&_constraint, a.body, b.body, phase, ratio); self.constraint->userData = self; [self setupCallbacks]; } return self; } both2(cpFloat, cpGearJoint, phase, Phase) both2(cpFloat, cpGearJoint, ratio, Ratio) @end @implementation ChipmunkRatchetJoint { cpRatchetJoint _constraint; } + (id)ratchetJointWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b phase:(cpFloat)phase ratchet:(cpFloat)ratchet { return [[[self alloc] initWithBodyA:a bodyB:b phase:phase ratchet:ratchet] autorelease]; } - (cpConstraint *)constraint {return (cpConstraint *)&_constraint;} - (id)initWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b phase:(cpFloat)phase ratchet:(cpFloat)ratchet { if((self = [super init])){ [a retain]; [b retain]; cpRatchetJointInit(&_constraint, a.body, b.body, phase, ratchet); self.constraint->userData = self; [self setupCallbacks]; } return self; } both2(cpFloat, cpRatchetJoint, angle, Angle) both2(cpFloat, cpRatchetJoint, phase, Phase) both2(cpFloat, cpRatchetJoint, ratchet, Ratchet) @end Chipmunk2D-Chipmunk-7.0.3/objectivec/src/ChipmunkImageSampler.m000066400000000000000000000152031347650476100244060ustar00rootroot00000000000000// Copyright 2013 Howling Moon Software. All rights reserved. // See http://chipmunk2d.net/legal.php for more information. // // ChipmunkImageSampler.m // DeformableChipmunk // // Created by Scott Lembcke on 8/26/11. // Copyright 2011 __MyCompanyName__. All rights reserved. // #import #if TARGET_OS_IPHONE == 1 #import #endif #import "ChipmunkImageSampler.h" @implementation ChipmunkBitmapSampler @synthesize width = _width, height = _height, bytesPerPixel = _bytesPerPixel, component = _component, pixelData = _pixelData, outputRect = _outputRect; // 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); } // TODO finish this? //static inline cpFloat //SampleFunc4444(cpVect point, ChipmunkImageSampler *self) //{ // int x = (int)point.x; // int y = (int)point.y - (self->_flip ? self->_height - 1 : 0); // // int com = self->_component; // int byte = self->_pixels[y*self->_stride + x*self->_bytesPerPixel + com/2]; // int value = // return (cpFloat)(byte>>())/15.0; //} static cpFloat SampleFunc8Clamp(cpVect point, ChipmunkBitmapSampler *self) { unsigned long w = self->_width; unsigned long h = self->_height; cpBB bb = self->_outputRect; cpVect clamped = cpBBClampVect(bb, point); unsigned long x = floor_int((w - 1)*(clamped.x - bb.l)/(bb.r - bb.l) + 0.5); unsigned long y = floor_int((h - 1)*(clamped.y - bb.b)/(bb.t - bb.b) + 0.5); if(self->_flip) y = h - 1 - y; // printf("(%6.2f, %6.2f) -> (% 4d, % 4d) : %d\n", point.x, point.y, x, y, self->_pixels[y*self->_stride + x*self->_bytesPerPixel + self->_component]); return (cpFloat)self->_pixels[y*self->_stride + x*self->_bytesPerPixel + self->_component]/255.0; } static cpFloat SampleFunc8Border(cpVect point, ChipmunkBitmapSampler *self) { unsigned long w = self->_width; unsigned long h = self->_height; cpBB bb = self->_outputRect; if(cpBBContainsVect(bb, point)){ unsigned long x = floor_int((w - 1)*(point.x - bb.l)/(bb.r - bb.l) + 0.5); unsigned long y = floor_int((h - 1)*(point.y - bb.b)/(bb.t - bb.b) + 0.5); if(self->_flip) y = h - 1 - y; // printf("(%6.2f, %6.2f) -> (% 4d, % 4d)\n", point.x, point.y, x, y); return (cpFloat)self->_pixels[y*self->_stride + x*self->_bytesPerPixel + self->_component]/255.0; } else { return self->_borderValue; } } -(id)initWithWidth:(NSUInteger)width height:(NSUInteger)height stride:(NSUInteger)stride bytesPerPixel:(NSUInteger)bytesPerPixel component:(NSUInteger)component flip:(bool)flip pixelData:(NSData *)pixelData { if((self = [super initWithSamplingFunction:(cpMarchSampleFunc)SampleFunc8Clamp])){ _width = width; _height = height; _stride = stride; _bytesPerPixel = bytesPerPixel; _component = component; _flip = flip; _pixelData = [pixelData retain]; _pixels = [pixelData bytes]; _outputRect = cpBBNew(0.5, 0.5, self.width - 0.5, self.height - 0.5); } return self; } - (void)dealloc { [_pixelData release]; [super dealloc]; } -(void)setBorderRepeat { _sampleFunc = (cpMarchSampleFunc)SampleFunc8Clamp; } -(void)setBorderValue:(cpFloat)borderValue { _sampleFunc = (cpMarchSampleFunc)SampleFunc8Border; _borderValue = borderValue; } static cpBB BorderedBB(cpBB bb, NSUInteger width, NSUInteger height) { cpFloat xBorder = (bb.r - bb.l)/(cpFloat)(width - 1); cpFloat yBorder = (bb.t - bb.b)/(cpFloat)(height - 1); return cpBBNew(bb.l - xBorder, bb.b - yBorder, bb.r + xBorder, bb.t + yBorder); } -(ChipmunkPolylineSet *)marchAllWithBorder:(bool)bordered hard:(bool)hard { NSUInteger width = self.width; NSUInteger height = self.height; cpBB bb = self.outputRect; if(bordered){ return [self march:BorderedBB(bb, width, height) xSamples:width+2 ySamples:height+2 hard:hard]; } else { return [self march:bb xSamples:width ySamples:height hard:hard]; } } @end @implementation ChipmunkCGContextSampler @synthesize context = _context; -(NSMutableData *)pixelData {return (NSMutableData *)super.pixelData;} -(id)initWithWidth:(unsigned long)width height:(unsigned long)height colorSpace:(CGColorSpaceRef)colorSpace bitmapInfo:(CGBitmapInfo)bitmapInfo component:(NSUInteger)component { // Need to create a context to get info about the context. // If you let the context allocate it's own memory it seems to move it around. O_o CGContextRef temp = CGBitmapContextCreate(NULL, width, height, 8, 0, colorSpace, bitmapInfo); cpAssertHard(temp, "Failed to create temporary CGBitmapContext"); unsigned long bpc = CGBitmapContextGetBitsPerComponent(temp); unsigned long bpp = CGBitmapContextGetBitsPerPixel(temp)/8; cpAssertHard(bpc == 8, "Cannot handle non-8bit-per-pixel bitmap data!"); CGContextRelease(temp); unsigned long stride = width*bpp; NSMutableData *pixelData = [NSMutableData dataWithLength:stride*height]; _context = CGBitmapContextCreate([pixelData mutableBytes], width, height, bpc, stride, colorSpace, bitmapInfo); return [self initWithWidth:width height:height stride:stride bytesPerPixel:bpp component:component flip:TRUE pixelData:pixelData]; } -(void)dealloc { CGContextRelease(_context); [super dealloc]; } @end @implementation ChipmunkImageSampler +(CGImageRef)loadImage:(NSURL *)url { CGImageSourceRef image_source = CGImageSourceCreateWithURL((CFURLRef)url, NULL); CGImageRef image = CGImageSourceCreateImageAtIndex(image_source, 0, NULL); cpAssertHard(image, "Image %s could not be loaded.", [[url description] UTF8String]); CFRelease(image_source); return image; } -(id)initWithImage:(CGImageRef)image isMask:(bool)isMask contextWidth:(NSUInteger)width contextHeight:(NSUInteger)height { if(width == 0) width = CGImageGetWidth(image); if(height == 0) height = CGImageGetHeight(image); CGColorSpaceRef colorSpace = (isMask ? CGColorSpaceCreateDeviceGray() : NULL); CGBitmapInfo bitmapInfo = (CGBitmapInfo)(isMask ? kCGImageAlphaNone : kCGImageAlphaOnly); if((self = [super initWithWidth:width height:height colorSpace:colorSpace bitmapInfo:bitmapInfo component:0])){ CGContextDrawImage(self.context, CGRectMake(0, 0, width, height), image); } CGColorSpaceRelease(colorSpace); return self; } -(id)initWithImageFile:(NSURL *)url isMask:(bool)isMask { CGImageRef image = [[self class] loadImage:url]; unsigned long width = CGImageGetWidth(image); unsigned long height = CGImageGetHeight(image); self = [self initWithImage:image isMask:isMask contextWidth:width contextHeight:height]; CGImageRelease(image); return self; } +(ChipmunkImageSampler *)samplerWithImageFile:(NSURL *)url isMask:(bool)isMask { return [[[self alloc] initWithImageFile:url isMask:isMask] autorelease]; } @end Chipmunk2D-Chipmunk-7.0.3/objectivec/src/ChipmunkMultiGrab.m000066400000000000000000000176031347650476100237340ustar00rootroot00000000000000/* 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. */ #import "ChipmunkMultiGrab.h" // A constraint subclass that tracks a grab point @interface ChipmunkGrab() @property(nonatomic, readwrite) cpVect pos; @property(nonatomic, readonly) NSArray *chipmunkObjects; @end @implementation ChipmunkGrab @synthesize pos = _pos; @synthesize chipmunkObjects = _chipmunkObjects; @synthesize grabbedShape = _grabbedShape; @synthesize data = _data; static void GrabPreSolve(cpConstraint *constraint, cpSpace *space) { cpBody *grabBody = cpConstraintGetBodyA(constraint); ChipmunkGrab *grab = [ChipmunkConstraint constraintFromCPConstraint:constraint].userData; cpFloat dt = cpSpaceGetCurrentTimeStep(space); cpFloat coef = cpfpow(grab->_smoothing, dt); // Smooth out the mouse position. cpVect pos = cpvlerp(grab->_pos, cpBodyGetPosition(grabBody), coef); cpBodySetVelocity(grabBody, cpvmult(cpvsub(pos, cpBodyGetPosition(grabBody)), 1.0/dt)); // cpBodySetPosition(grabBody, pos); } // Body will be nil if no object was grabbed. -(id)initWithMultiGrab:(ChipmunkMultiGrab *)multiGrab pos:(cpVect)pos nearest:(cpVect)nearest body:(ChipmunkBody *)body grabbedShape:(ChipmunkShape *)grabbedShape chipmunkObjects:(NSArray *)chipmunkObjects { ChipmunkBody *grabBody = [ChipmunkBody kinematicBody]; grabBody.position = pos; // TODO the repeated appending is a little silly here. chipmunkObjects = [chipmunkObjects arrayByAddingObject:grabBody]; if((self = [super init])){ _pos = pos; _smoothing = multiGrab.smoothing; _grabbedShape = grabbedShape; if(body){ ChipmunkPivotJoint *pivot = [ChipmunkPivotJoint pivotJointWithBodyA:grabBody bodyB:body anchorA:cpvzero anchorB:[body worldToLocal:nearest]]; pivot.maxForce = multiGrab.grabForce; pivot.userData = self; cpConstraintSetPreSolveFunc(pivot.constraint, GrabPreSolve); chipmunkObjects = [chipmunkObjects arrayByAddingObject:pivot]; if(grabbedShape){ cpFloat frictionForce = multiGrab.grabFriction; if(frictionForce > 0.0 && (1.0/body.mass + 1.0/grabBody.mass != 0.0)){ ChipmunkPivotJoint *friction = [ChipmunkPivotJoint pivotJointWithBodyA:grabBody bodyB:body anchorA:cpvzero anchorB:[body worldToLocal:nearest]]; friction.maxForce = frictionForce; friction.maxBias = 0.0; chipmunkObjects = [chipmunkObjects arrayByAddingObject:friction]; } cpFloat rotaryFriction = multiGrab.grabRotaryFriction; if(rotaryFriction > 0.0 && (1.0/body.moment + 1.0/grabBody.moment != 0.0)){ ChipmunkGearJoint *friction = [ChipmunkGearJoint gearJointWithBodyA:grabBody bodyB:body phase:0.0 ratio:1.0]; friction.maxForce = rotaryFriction; friction.maxBias = 0.0; chipmunkObjects = [chipmunkObjects arrayByAddingObject:friction]; } } _chipmunkObjects = [chipmunkObjects retain]; } } return self; } -(void)dealloc { [_chipmunkObjects release]; _chipmunkObjects = nil; [super dealloc]; } @end @implementation ChipmunkMultiGrab @synthesize grabForce = _grabForce; @synthesize smoothing = _smoothing; @synthesize filter = _filter; @synthesize grabFilter = _grabFilter; @synthesize grabSort = _grabSort; @synthesize grabFriction = _grabFriction, grabRotaryFriction = _grabRotaryFriction; @synthesize grabRadius = _grabRadius; @synthesize pullMode = _pullMode, pushMode = _pushMode; @synthesize pushMass = _pushMass; @synthesize pushFriction = _pushFriction, pushElasticity = _pushElasticity; @synthesize pushCollisionType = _pushCollisionType; -(id)initForSpace:(ChipmunkSpace *)space withSmoothing:(cpFloat)smoothing withGrabForce:(cpFloat)grabForce { if((self = [super init])){ _space = [space retain]; _grabs = [[NSMutableArray alloc] init]; _smoothing = smoothing; _grabForce = grabForce; _filter = CP_SHAPE_FILTER_ALL; _grabFilter = ^(ChipmunkShape *shape){return (bool)TRUE;}; _grabSort = ^(ChipmunkShape *shape, cpFloat depth){return depth;}; _pullMode = TRUE; _pushMode = FALSE; } return self; } -(void)dealloc { [_space release]; [_grabs release]; [super dealloc]; } // Don't integrate push bodies. static void PushBodyVelocityUpdate(cpBody *body, cpVect gravity, cpFloat damping, cpFloat dt){} -(ChipmunkGrab *)beginLocation:(cpVect)pos { __block cpFloat min = INFINITY; __block cpVect nearest = pos; __block ChipmunkShape *grabbedShape = nil; if(_pullMode){ cpSpacePointQuery_b(_space.space, pos, _grabRadius, _filter, ^(cpShape *c_shape, cpVect point, cpFloat dist, cpVect gradient){ ChipmunkShape *shape = [ChipmunkShape shapeFromCPShape:c_shape]; cpFloat sort = dist; // Call the sorting callback if dist is negative. // Otherwise just take the nearest shape. if(dist <= 0.0f){ sort = -_grabSort(shape, -dist); cpAssertWarn(sort <= 0.0f, "You must return a positive value from the sorting callback."); } if(sort < min && cpBodyGetMass(cpShapeGetBody(c_shape)) != INFINITY){ if(_grabFilter(shape)){ min = sort; nearest = (dist > 0.0 ? point : pos); grabbedShape = shape; } } }); } ChipmunkBody *pushBody = nil; NSArray *chipmunkObjects = [NSArray array]; if(!grabbedShape && _pushMode){ pushBody = [ChipmunkBody bodyWithMass:_pushMass andMoment:INFINITY]; pushBody.position = pos; cpBodySetVelocityUpdateFunc(pushBody.body, PushBodyVelocityUpdate); ChipmunkShape *pushShape = [ChipmunkCircleShape circleWithBody:pushBody radius:_grabRadius offset:cpvzero]; pushShape.friction = _pushFriction; pushShape.elasticity = _pushElasticity; pushShape.filter = _filter; pushShape.collisionType = _pushCollisionType; chipmunkObjects = [NSArray arrayWithObjects:pushBody, pushShape, nil]; } ChipmunkBody *grabBody = (grabbedShape ? grabbedShape.body : pushBody); ChipmunkGrab *grab = [[ChipmunkGrab alloc] initWithMultiGrab:self pos:pos nearest:nearest body:grabBody grabbedShape:grabbedShape chipmunkObjects:chipmunkObjects]; [_grabs addObject:grab]; [_space add:grab]; [grab release]; return (grab.grabbedShape ? grab : nil); } static ChipmunkGrab * BestGrab(NSArray *grabs, cpVect pos) { ChipmunkGrab *match = nil; cpFloat best = INFINITY; for(ChipmunkGrab *grab in grabs){ cpFloat dist = cpvdistsq(pos, grab.pos); if(dist < best){ match = grab; best = dist; } } return match; } -(ChipmunkGrab *)updateLocation:(cpVect)pos { ChipmunkGrab *grab = BestGrab(_grabs, pos); grab.pos = pos; return (grab.grabbedShape ? grab : nil); } -(ChipmunkGrab *)endLocation:(cpVect)pos { cpAssertHard([_grabs count] != 0, "Grab set is already empty!"); ChipmunkGrab *grab = BestGrab(_grabs, pos); [grab retain]; [_space remove:grab]; [_grabs removeObject:grab]; [grab autorelease]; return (grab.grabbedShape ? grab : nil); } -(NSArray *)grabs { NSMutableArray *grabs = [NSMutableArray array]; for(ChipmunkGrab *grab in _grabs){ if(grab.grabbedShape) [grabs addObject:grab]; } return grabs; } @end Chipmunk2D-Chipmunk-7.0.3/objectivec/src/ChipmunkPointCloudSampler.m000066400000000000000000000034601347650476100254460ustar00rootroot00000000000000// Copyright 2013 Howling Moon Software. All rights reserved. // See http://chipmunk2d.net/legal.php for more information. #import "ChipmunkPointCloudSampler.h" typedef struct DeformPoint { cpVect pos; cpFloat radius; cpFloat fuzz; } DeformPoint; static cpBB PointBB(DeformPoint *point) { cpVect v = point->pos; cpFloat r = point->radius; return cpBBNew(v.x - r, v.y - r, v.x + r, v.y + r); } @implementation ChipmunkPointCloudSampler static inline cpFloat fuzz(cpVect v, cpVect c, cpFloat r, cpFloat softness) { cpFloat distsq = cpvdistsq(v, c); return (distsq < r*r ? 1.0f - cpfclamp01((r - cpfsqrt(distsq))/(softness*r)) : 1.0f); } static void PointQuery(cpVect *v, DeformPoint *point, cpCollisionID id, cpFloat *density) { (*density) *= fuzz(*v, point->pos, point->radius, point->fuzz); } static cpFloat PointCloudSample(ChipmunkPointCloudSampler *cloud, cpVect pos) { cpFloat density = 1.0f; cpSpatialIndexQuery(cloud->_index, &pos, cpBBNewForCircle(pos, 0.0f), (cpSpatialIndexQueryFunc)PointQuery, &density); return density; } - (id)initWithCellSize:(cpFloat)cellSize { if((self = [super initWithSamplingFunction:(cpMarchSampleFunc)PointCloudSample])){ _cellSize = cellSize; // TODO table size _index = cpSpaceHashNew(cellSize, 1000, (cpSpatialIndexBBFunc)PointBB, NULL); } return self; } static void freeWrap(void *ptr, void *unused){cpfree(ptr);} - (void)dealloc { cpSpatialIndexEach(_index, (cpSpatialIndexIteratorFunc)freeWrap, NULL); cpSpatialIndexFree(_index); [super dealloc]; } -(cpBB)addPoint:(cpVect)pos radius:(cpFloat)radius fuzz:(cpFloat)fuzz { DeformPoint *point = (DeformPoint *)cpcalloc(1, sizeof(DeformPoint)); point->pos = pos; point->radius = radius; point->fuzz = fuzz; cpSpatialIndexInsert(_index, point, (cpHashValue)point); return PointBB(point); } @end Chipmunk2D-Chipmunk-7.0.3/objectivec/src/ChipmunkShape.m000066400000000000000000000215171347650476100231050ustar00rootroot00000000000000/* 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. */ #import "ObjectiveChipmunk/ObjectiveChipmunk.h" @interface ChipmunkSpace(DoubleDispatch) - (ChipmunkShape *)addShape:(ChipmunkShape *)obj; - (ChipmunkShape *)removeShape:(ChipmunkShape *)obj; @end @implementation ChipmunkShape @synthesize userData = _userData; +(ChipmunkShape *)shapeFromCPShape:(cpShape *)shape { ChipmunkShape *obj = shape->userData; cpAssertHard([obj isKindOfClass:[ChipmunkShape class]], "'shape->data' is not a pointer to a ChipmunkShape object."); return obj; } - (void) dealloc { [self.body release]; cpShapeDestroy(self.shape); [super dealloc]; } - (cpShape *)shape { [self doesNotRecognizeSelector:_cmd]; return nil; } - (ChipmunkBody *)body { cpBody *body = cpShapeGetBody(self.shape); return (body ? cpBodyGetUserData(body) : nil); } - (void)setBody:(ChipmunkBody *)body { if(self.body != body){ [self.body release]; cpShapeSetBody(self.shape, [body retain].body); } } -(cpFloat)mass {return cpShapeGetMass(self.shape);} -(void)setMass:(cpFloat)mass {cpShapeSetMass(self.shape, mass);} -(cpFloat)density {return cpShapeGetDensity(self.shape);} -(void)setDensity:(cpFloat)density {cpShapeSetDensity(self.shape, density);} -(cpFloat)moment {return cpShapeGetMoment(self.shape);} -(cpFloat)area {return cpShapeGetArea(self.shape);} -(cpVect)centerOfGravity {return cpShapeGetCenterOfGravity(self.shape);} // accessor macros #define getter(type, lower, upper) \ - (type)lower {return cpShapeGet##upper(self.shape);} #define setter(type, lower, upper) \ - (void)set##upper:(type)value {cpShapeSet##upper(self.shape, value);}; #define both(type, lower, upper) \ getter(type, lower, upper) \ setter(type, lower, upper) getter(cpBB, bb, BB) both(BOOL, sensor, Sensor) both(cpFloat, elasticity, Elasticity) both(cpFloat, friction, Friction) both(cpVect, surfaceVelocity, SurfaceVelocity) both(cpCollisionType, collisionType, CollisionType) both(cpShapeFilter, filter, Filter) -(ChipmunkSpace *)space { cpSpace *space = cpShapeGetSpace(self.shape); return (ChipmunkSpace *)(space ? cpSpaceGetUserData(space) : nil); } - (cpBB)cacheBB {return cpShapeCacheBB(self.shape);} - (ChipmunkPointQueryInfo *)pointQuery:(cpVect)point { cpPointQueryInfo info; cpShapePointQuery(self.shape, point, &info); return (info.shape ? [[[ChipmunkPointQueryInfo alloc] initWithInfo:&info] autorelease] : nil); } - (ChipmunkSegmentQueryInfo *)segmentQueryFrom:(cpVect)start to:(cpVect)end radius:(cpFloat)radius { cpSegmentQueryInfo info; if(cpShapeSegmentQuery(self.shape, start, end, radius, &info)){ return [[[ChipmunkSegmentQueryInfo alloc] initWithInfo:&info start:start end:end] autorelease]; } else { return nil; } } - (NSArray *)chipmunkObjects {return [NSArray arrayWithObject:self];} - (void)addToSpace:(ChipmunkSpace *)space {[space addShape:self];} - (void)removeFromSpace:(ChipmunkSpace *)space {[space removeShape:self];} @end @implementation ChipmunkPointQueryInfo - (id)initWithInfo:(cpPointQueryInfo *)info { if((self = [super init])){ _info = (*info); [self.shape retain]; } return self; } - (cpPointQueryInfo *)info {return &_info;} - (ChipmunkShape *)shape {return (_info.shape ? _info.shape->userData : nil);} - (cpVect)point {return _info.point;} - (cpFloat)distance {return _info.distance;} - (cpVect)gradient {return _info.gradient;} - (void)dealloc { [self.shape release]; [super dealloc]; } @end @implementation ChipmunkSegmentQueryInfo - (id)initWithInfo:(cpSegmentQueryInfo *)info start:(cpVect)start end:(cpVect)end { if((self = [super init])){ _info = (*info); _start = start; _end = end; [self.shape retain]; } return self; } - (cpSegmentQueryInfo *)info {return &_info;} - (ChipmunkShape *)shape {return (_info.shape ? _info.shape->userData : nil);} - (cpFloat)t {return _info.alpha;} - (cpVect)normal {return _info.normal;} - (cpVect)point {return _info.point;} - (cpFloat)dist {return cpvdist(_start, _end)*_info.alpha;} - (cpVect)start {return _start;} - (cpVect)end {return _end;} - (void)dealloc { [self.shape release]; [super dealloc]; } @end @implementation ChipmunkShapeQueryInfo @synthesize shape = _shape; - (cpContactPointSet *)contactPoints {return &_contactPoints;} - (id)initWithShape:(ChipmunkShape *)shape andPoints:(cpContactPointSet *)set { if((self = [super init])){ _shape = [shape retain]; _contactPoints = *set; } return self; } - (void)dealloc { [_shape release]; [super dealloc]; } @end @implementation ChipmunkCircleShape { cpCircleShape _shape; } + (ChipmunkCircleShape *)circleWithBody:(ChipmunkBody *)body radius:(cpFloat)radius offset:(cpVect)offset { return [[[self alloc] initWithBody:body radius:radius offset:offset] autorelease]; } - (cpShape *)shape {return (cpShape *)&_shape;} - (id)initWithBody:(ChipmunkBody *)body radius:(cpFloat)radius offset:(cpVect)offset { if((self = [super init])){ [body retain]; cpCircleShapeInit(&_shape, body.body, radius, offset); self.shape->userData = self; } return self; } - (cpFloat)radius {return cpCircleShapeGetRadius((cpShape *)&_shape);} - (cpVect)offset {return cpCircleShapeGetOffset((cpShape *)&_shape);} @end @implementation ChipmunkSegmentShape { cpSegmentShape _shape; } + (ChipmunkSegmentShape *)segmentWithBody:(ChipmunkBody *)body from:(cpVect)a to:(cpVect)b radius:(cpFloat)radius { return [[[self alloc] initWithBody:body from:a to:b radius:radius] autorelease]; } - (cpShape *)shape {return (cpShape *)&_shape;} - (id)initWithBody:(ChipmunkBody *)body from:(cpVect)a to:(cpVect)b radius:(cpFloat)radius { if((self = [super init])){ [body retain]; cpSegmentShapeInit(&_shape, body.body, a, b, radius); self.shape->userData = self; } return self; } - (void)setPrevNeighbor:(cpVect)prev nextNeighbor:(cpVect)next { cpSegmentShapeSetNeighbors((cpShape *)&_shape, prev, next); } - (cpVect)a {return cpSegmentShapeGetA((cpShape *)&_shape);} - (cpVect)b {return cpSegmentShapeGetB((cpShape *)&_shape);} - (cpVect)normal {return cpSegmentShapeGetNormal((cpShape *)&_shape);} - (cpFloat)radius {return cpSegmentShapeGetRadius((cpShape *)&_shape);} @end @implementation ChipmunkPolyShape { cpPolyShape _shape; } + (id)polyWithBody:(ChipmunkBody *)body count:(int)count verts:(const cpVect *)verts transform:(cpTransform)transform radius:(cpFloat)radius { return [[[self alloc] initWithBody:body count:count verts:verts transform:transform radius:radius] autorelease]; } + (id)boxWithBody:(ChipmunkBody *)body width:(cpFloat)width height:(cpFloat)height radius:(cpFloat)radius { return [[[self alloc] initBoxWithBody:body width:width height:height radius:radius] autorelease]; } + (id)boxWithBody:(ChipmunkBody *)body bb:(cpBB)bb radius:(cpFloat)radius { return [[[self alloc] initBoxWithBody:body bb:bb radius:radius] autorelease]; } - (cpShape *)shape {return (cpShape *)&_shape;} - (id)initWithBody:(ChipmunkBody *)body count:(int)count verts:(const cpVect *)verts transform:(cpTransform)transform radius:(cpFloat)radius { if((self = [super init])){ [body retain]; cpPolyShapeInit(&_shape, body.body, count, verts, transform, radius); self.shape->userData = self; } return self; } - (id)initBoxWithBody:(ChipmunkBody *)body width:(cpFloat)width height:(cpFloat)height radius:(cpFloat)radius { if((self = [super init])){ [body retain]; cpBoxShapeInit(&_shape, body.body, width, height, radius); self.shape->userData = self; } return self; } - (id)initBoxWithBody:(ChipmunkBody *)body bb:(cpBB)bb radius:(cpFloat)radius { if((self = [super init])){ [body retain]; cpBoxShapeInit2(&_shape, body.body, bb, radius); self.shape->userData = self; } return self; } - (int)count {return cpPolyShapeGetCount((cpShape *)&_shape);} - (cpFloat)radius {return cpPolyShapeGetRadius((cpShape *)&_shape);} - (cpVect)getVertex:(int)index {return cpPolyShapeGetVert((cpShape *)&_shape, index);} @end Chipmunk2D-Chipmunk-7.0.3/objectivec/src/ChipmunkSpace.m000066400000000000000000000377631347650476100231120ustar00rootroot00000000000000/* 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. */ #import "ObjectiveChipmunk/ObjectiveChipmunk.h" #import "chipmunk/chipmunk_private.h" #import "chipmunk/cpHastySpace.h" #import #import // Just in case the user doesn't have -ObjC in their linker flags. // Annoyingly, this is the case more often than not. @interface NSArrayChipmunkObject : NSArray @property(nonatomic, retain) NSArray *chipmunkObjects; @end @implementation NSArrayChipmunkObject @synthesize chipmunkObjects = _chipmunkObjects; -(id)initWithArray:(NSArray *)objects { if((self = [super init])){ self.chipmunkObjects = objects; } return self; } -(NSUInteger)count { return [_chipmunkObjects count]; } -(id)objectAtIndex:(NSUInteger)index { return [_chipmunkObjects objectAtIndex:index]; } @end @implementation NSArray(ChipmunkObject) -(id)chipmunkObjects { return self; } @end // Private class used to wrap the statically allocated staticBody attached to each space. @interface _ChipmunkStaticBodySingleton : ChipmunkBody { cpBody *_bodyPtr; ChipmunkSpace *space; // weak ref } @end typedef struct HandlerContext { ChipmunkSpace *space; id delegate; cpCollisionType typeA, typeB; SEL beginSelector; SEL preSolveSelector; SEL postSolveSelector; SEL separateSelector; } HandlerContext; @implementation ChipmunkSpace +(ChipmunkSpace *)spaceFromCPSpace:(cpSpace *)space { ChipmunkSpace *obj = space->userData; cpAssertHard([obj isKindOfClass:[ChipmunkSpace class]], "'space->data' is not a pointer to a ChipmunkSpace object."); return obj; } +(instancetype)allocWithZone:(struct _NSZone *)zone { Class class = self; #if CHIPMUNK_SPACE_USE_HASTY_SPACE if (self == [ChipmunkSpace class]) { class = [ChipmunkHastySpace class]; } #endif return NSAllocateObject(class, 0, zone); } - (id)initWithSpace:(cpSpace *)space { if((self = [super init])){ _children = [[NSMutableSet alloc] init]; _handlers = [[NSMutableArray alloc] init]; _space = space; cpSpaceSetUserData(_space, self); _staticBody = [[ChipmunkBody alloc] initWithMass:0.0f andMoment:0.0f]; _staticBody.type = CP_BODY_TYPE_STATIC; cpSpaceSetStaticBody(_space, _staticBody.body); } return self; } - (id)init { return [self initWithSpace:cpSpaceNew()]; } -(void)freeSpace { cpSpaceFree(_space); } - (void) dealloc { [self freeSpace]; [_staticBody release]; [_children release]; [_handlers release]; [super dealloc]; } - (cpSpace *)space {return _space;} @synthesize userData = _userData; // accessor macros #define getter(type, lower, upper) \ - (type)lower {return cpSpaceGet##upper(_space);} #define setter(type, lower, upper) \ - (void)set##upper:(type)value {cpSpaceSet##upper(_space, value);}; #define both(type, lower, upper) \ getter(type, lower, upper) \ setter(type, lower, upper) both(int, iterations, Iterations); both(cpVect, gravity, Gravity); both(cpFloat, damping, Damping); both(cpFloat, idleSpeedThreshold, IdleSpeedThreshold); both(cpFloat, sleepTimeThreshold, SleepTimeThreshold); both(cpFloat, collisionSlop, CollisionSlop); both(cpFloat, collisionBias, CollisionBias); both(cpTimestamp, collisionPersistence, CollisionPersistence); getter(cpFloat, currentTimeStep, CurrentTimeStep); - (BOOL)isLocked {return cpSpaceIsLocked(_space);} - (BOOL)locked {return self.isLocked;} - (ChipmunkBody *)staticBody {return _staticBody;} typedef BOOL (*BeginProto)(id, SEL, cpArbiter *, ChipmunkSpace *); static bool Begin(cpArbiter *arb, struct cpSpace *space, HandlerContext *ctx){return ((BeginProto)objc_msgSend)(ctx->delegate, ctx->beginSelector, arb, ctx->space);} typedef BOOL (*PreSolveProto)(id, SEL, cpArbiter *, ChipmunkSpace *); static bool PreSolve(cpArbiter *arb, struct cpSpace *space, HandlerContext *ctx){return ((PreSolveProto)objc_msgSend)(ctx->delegate, ctx->preSolveSelector, arb, ctx->space);} typedef void (*PostSolveProto)(id, SEL, cpArbiter *, ChipmunkSpace *); static void PostSolve(cpArbiter *arb, struct cpSpace *space, HandlerContext *ctx){((PostSolveProto)objc_msgSend)(ctx->delegate, ctx->postSolveSelector, arb, ctx->space);} typedef void (*SeparateProto)(id, SEL, cpArbiter *, ChipmunkSpace *); static void Separate(cpArbiter *arb, struct cpSpace *space, HandlerContext *ctx){((SeparateProto)objc_msgSend)(ctx->delegate, ctx->separateSelector, arb, ctx->space);} // TODO handlers are never filtered. - (void)setDefaultCollisionHandler:(id)delegate begin:(SEL)begin preSolve:(SEL)preSolve postSolve:(SEL)postSolve separate:(SEL)separate { cpCollisionType sentinel = (cpCollisionType)@"DEFAULT"; HandlerContext context = {self, delegate, sentinel, sentinel, begin, preSolve, postSolve, separate}; NSData *data = [NSData dataWithBytes:&context length:sizeof(context)]; [_handlers addObject:data]; cpCollisionHandler *handler = cpSpaceAddDefaultCollisionHandler(_space); if(begin) handler->beginFunc = (cpCollisionBeginFunc)Begin; if(preSolve) handler->preSolveFunc = (cpCollisionPreSolveFunc)PreSolve; if(postSolve) handler->postSolveFunc = (cpCollisionPostSolveFunc)PostSolve; if(separate) handler->separateFunc = (cpCollisionSeparateFunc)Separate; handler->userData = (void *)[data bytes]; } - (void)addCollisionHandler:(id)delegate typeA:(cpCollisionType)a typeB:(cpCollisionType)b begin:(SEL)begin preSolve:(SEL)preSolve postSolve:(SEL)postSolve separate:(SEL)separate { HandlerContext context = {self, delegate, a, b, begin, preSolve, postSolve, separate}; NSData *data = [NSData dataWithBytes:&context length:sizeof(context)]; [_handlers addObject:data]; cpCollisionHandler *handler = cpSpaceAddCollisionHandler(_space, a, b); if(begin) handler->beginFunc = (cpCollisionBeginFunc)Begin; if(preSolve) handler->preSolveFunc = (cpCollisionPreSolveFunc)PreSolve; if(postSolve) handler->postSolveFunc = (cpCollisionPostSolveFunc)PostSolve; if(separate) handler->separateFunc = (cpCollisionSeparateFunc)Separate; handler->userData = (void *)[data bytes]; } - (id)add:(NSObject *)obj { if([obj conformsToProtocol:@protocol(ChipmunkBaseObject)]){ [(NSObject *)obj addToSpace:self]; } else if([obj conformsToProtocol:@protocol(ChipmunkObject)]){ for(NSObject *child in [obj chipmunkObjects]) [self add:child]; } else { [NSException raise:@"NSArgumentError" format:@"Attempted to add an object of type %@ to a ChipmunkSpace.", [obj class]]; } [_children addObject:obj]; return obj; } - (id)remove:(NSObject *)obj { if([obj conformsToProtocol:@protocol(ChipmunkBaseObject)]){ [(NSObject *)obj removeFromSpace:self]; } else if([obj conformsToProtocol:@protocol(ChipmunkObject)]){ for(NSObject *child in [obj chipmunkObjects]) [self remove:child]; } else { [NSException raise:@"NSArgumentError" format:@"Attempted to remove an object of type %@ from a ChipmunkSpace.", [obj class]]; } [_children removeObject:obj]; return obj; } -(BOOL)contains:(NSObject *)obj { return [_children containsObject:obj]; } - (NSObject *)smartAdd:(NSObject *)obj { if(cpSpaceIsLocked(_space)){ [self addPostStepAddition:obj]; } else { [self add:obj]; } return obj; } - (NSObject *)smartRemove:(NSObject *)obj { if(cpSpaceIsLocked(_space)){ [self addPostStepRemoval:obj]; } else { [self remove:obj]; } return obj; } struct PostStepTargetContext { id target; SEL selector; }; static void postStepPerform(cpSpace *unused, id key, struct PostStepTargetContext *context) { [context->target performSelector:context->selector withObject:key]; [context->target release]; cpfree(context); [key release]; } - (BOOL)addPostStepCallback:(id)target selector:(SEL)selector key:(id)key { if(!cpSpaceGetPostStepCallback(_space, key)){ struct PostStepTargetContext *context = cpcalloc(1, sizeof(struct PostStepTargetContext)); (*context) = (struct PostStepTargetContext){target, selector}; cpSpaceAddPostStepCallback(_space, (cpPostStepFunc)postStepPerform, key, context); [target retain]; [key retain]; return TRUE; } else { return FALSE; } } static void postStepPerformBlock(cpSpace *unused, id key, ChipmunkPostStepBlock block) { block(); [block release]; [key release]; } - (BOOL)addPostStepBlock:(ChipmunkPostStepBlock)block key:(id)key { if(!cpSpaceGetPostStepCallback(_space, key)){ cpSpaceAddPostStepCallback(_space, (cpPostStepFunc)postStepPerformBlock, key, [block copy]); [key retain]; return TRUE; } else { return FALSE; } } - (void)addPostStepAddition:(NSObject *)obj { [self addPostStepCallback:self selector:@selector(add:) key:obj]; } - (void)addPostStepRemoval:(NSObject *)obj { [self addPostStepCallback:self selector:@selector(remove:) key:obj]; } - (NSArray *)pointQueryAll:(cpVect)point maxDistance:(cpFloat)maxDistance filter:(cpShapeFilter)filter { NSMutableArray *array = [NSMutableArray array]; cpSpacePointQuery_b(_space, point, maxDistance, filter, ^(cpShape *shape, cpVect p, cpFloat d, cpVect g){ ChipmunkPointQueryInfo *info = [[ChipmunkPointQueryInfo alloc] initWithInfo:&(cpPointQueryInfo){shape, p, d, g}]; [array addObject:info]; [info release]; }); return array; } - (ChipmunkPointQueryInfo *)pointQueryNearest:(cpVect)point maxDistance:(cpFloat)maxDistance filter:(cpShapeFilter)filter { cpPointQueryInfo info; cpSpacePointQueryNearest(_space, point, maxDistance, filter, &info); return (info.shape ? [[[ChipmunkPointQueryInfo alloc] initWithInfo:&info] autorelease] : nil); } typedef struct segmentQueryContext { cpVect start, end; NSMutableArray *array; } segmentQueryContext; - (NSArray *)segmentQueryAllFrom:(cpVect)start to:(cpVect)end radius:(cpFloat)radius filter:(cpShapeFilter)filter { NSMutableArray *array = [NSMutableArray array]; cpSpaceSegmentQuery_b(_space, start, end, radius, filter, ^(cpShape *shape, cpVect p, cpVect n, cpFloat t){ // TODO point ChipmunkSegmentQueryInfo *info = [[ChipmunkSegmentQueryInfo alloc] initWithInfo:&(cpSegmentQueryInfo){shape, p, n, t} start:start end:end]; [array addObject:info]; [info release]; }); return array; } - (ChipmunkSegmentQueryInfo *)segmentQueryFirstFrom:(cpVect)start to:(cpVect)end radius:(cpFloat)radius filter:(cpShapeFilter)filter { cpSegmentQueryInfo info; cpSpaceSegmentQueryFirst(_space, start, end, radius, filter, &info); return (info.shape ? [[[ChipmunkSegmentQueryInfo alloc] initWithInfo:&info start:start end:end] autorelease] : nil); } - (NSArray *)bbQueryAll:(cpBB)bb filter:(cpShapeFilter)filter { NSMutableArray *array = [NSMutableArray array]; cpSpaceBBQuery_b(_space, bb, filter, ^(cpShape *shape){ [array addObject:shape->userData]; }); return array; } //static void //shapeQueryAll(cpShape *shape, cpContactPointSet *points, NSMutableArray *array) //{ // ChipmunkShapeQueryInfo *info = [[ChipmunkShapeQueryInfo alloc] initWithShape:shape->userData andPoints:points]; // [array addObject:info]; // [info release]; //} - (NSArray *)shapeQueryAll:(ChipmunkShape *)shape { NSMutableArray *array = [NSMutableArray array]; cpSpaceShapeQuery_b(_space, shape.shape, ^(cpShape *shape, cpContactPointSet *points){ ChipmunkShapeQueryInfo *info = [[ChipmunkShapeQueryInfo alloc] initWithShape:shape->userData andPoints:points]; [array addObject:info]; [info release]; }); return array; } - (BOOL)shapeTest:(ChipmunkShape *)shape { return cpSpaceShapeQuery(_space, shape.shape, NULL, NULL); } static void PushBody(cpBody *body, NSMutableArray *arr){[arr addObject:body->userData];} - (NSArray *)bodies { NSMutableArray *arr = [NSMutableArray array]; cpSpaceEachBody(_space, (cpSpaceBodyIteratorFunc)PushBody, arr); return arr; } static void PushShape(cpShape *shape, NSMutableArray *arr){[arr addObject:shape->userData];} - (NSArray *)shapes { NSMutableArray *arr = [NSMutableArray array]; cpSpaceEachShape(_space, (cpSpaceShapeIteratorFunc)PushShape, arr); return arr; } static void PushConstraint(cpConstraint *constraint, NSMutableArray *arr){[arr addObject:constraint->userData];} - (NSArray *)constraints { NSMutableArray *arr = [NSMutableArray array]; cpSpaceEachConstraint(_space, (cpSpaceConstraintIteratorFunc)PushConstraint, arr); return arr; } - (void)reindexStatic { cpSpaceReindexStatic(_space); } - (void)reindexShape:(ChipmunkShape *)shape { cpSpaceReindexShape(_space, shape.shape); } - (void)reindexShapesForBody:(ChipmunkBody *)body { cpSpaceReindexShapesForBody(_space, body.body); } - (void)step:(cpFloat)dt { cpSpaceStep(_space, dt); } //MARK: Extras - (ChipmunkBody *)addBody:(ChipmunkBody *)obj { cpSpaceAddBody(_space, obj.body); [_children addObject:obj]; return obj; } - (ChipmunkBody *)removeBody:(ChipmunkBody *)obj { cpSpaceRemoveBody(_space, obj.body); [_children removeObject:obj]; return obj; } - (ChipmunkShape *)addShape:(ChipmunkShape *)obj { cpSpaceAddShape(_space, obj.shape); [_children addObject:obj]; return obj; } - (ChipmunkShape *)removeShape:(ChipmunkShape *)obj { cpSpaceRemoveShape(_space, obj.shape); [_children removeObject:obj]; return obj; } - (ChipmunkConstraint *)addConstraint:(ChipmunkConstraint *)obj { cpSpaceAddConstraint(_space, obj.constraint); [_children addObject:obj]; return obj; } - (ChipmunkConstraint *)removeConstraint:(ChipmunkConstraint *)obj { cpSpaceRemoveConstraint(_space, obj.constraint); [_children removeObject:obj]; return obj; } static ChipmunkSegmentShape * boundSeg(ChipmunkBody *body, cpVect a, cpVect b, cpFloat radius, cpFloat elasticity,cpFloat friction, cpShapeFilter filter, cpCollisionType collisionType) { ChipmunkSegmentShape *seg = [ChipmunkSegmentShape segmentWithBody:body from:a to:b radius:radius]; seg.elasticity = elasticity; seg.friction = friction; seg.filter = filter; seg.collisionType = collisionType; return seg; } - (NSArray *)addBounds:(cpBB)bounds thickness:(cpFloat)radius elasticity:(cpFloat)elasticity friction:(cpFloat)friction filter:(cpShapeFilter)filter collisionType:(cpCollisionType)collisionType { cpFloat l = bounds.l - radius; cpFloat b = bounds.b - radius; cpFloat r = bounds.r + radius; cpFloat t = bounds.t + radius; NSArray *segs = [[NSArrayChipmunkObject alloc] initWithArray:[NSArray arrayWithObjects: boundSeg(_staticBody, cpv(l,b), cpv(l,t), radius, elasticity, friction, filter, collisionType), boundSeg(_staticBody, cpv(l,t), cpv(r,t), radius, elasticity, friction, filter, collisionType), boundSeg(_staticBody, cpv(r,t), cpv(r,b), radius, elasticity, friction, filter, collisionType), boundSeg(_staticBody, cpv(r,b), cpv(l,b), radius, elasticity, friction, filter, collisionType), nil ]]; [self add:segs]; return [segs autorelease]; } @end @implementation ChipmunkHastySpace - (id)init { return [self initWithSpace:cpHastySpaceNew()]; } -(void)freeSpace { cpHastySpaceFree(_space); } - (void)step:(cpFloat)dt { cpHastySpaceStep(_space, dt); } -(NSUInteger)threads { return cpHastySpaceGetThreads(_space); } -(void)setThreads:(NSUInteger)threads { cpHastySpaceSetThreads(_space, threads); } @end Chipmunk2D-Chipmunk-7.0.3/objectivec/src/ChipmunkTileCache.m000066400000000000000000000215741347650476100236710ustar00rootroot00000000000000// Copyright 2013 Howling Moon Software. All rights reserved. // See http://chipmunk2d.net/legal.php for more information. #import "ChipmunkTileCache.h" @interface ChipmunkCachedTile : NSObject { cpBB _bb; bool _dirty; ChipmunkCachedTile *_next, *_prev; NSArray *_shapes; } @property(nonatomic, readonly) cpBB bb; @property(nonatomic, assign) bool dirty; @property(nonatomic, assign) ChipmunkCachedTile *next; @property(nonatomic, assign) ChipmunkCachedTile *prev; @property(nonatomic, retain) NSArray *shapes; @end @implementation ChipmunkCachedTile @synthesize bb = _bb, dirty = _dirty, shapes = _shapes, next = _next, prev = _prev; static cpBB ChipmunkCachedTileBB(ChipmunkCachedTile *tile) { return tile->_bb; } static void ChipmunkCachedTileQuery(cpVect *pos, ChipmunkCachedTile *tile, cpCollisionID id, ChipmunkCachedTile **out) { if(cpBBContainsVect(tile->_bb, *pos)) (*out) = tile; } -(id)initWithBB:(cpBB)bb { if((self = [super init])) _bb = bb; return self; } -(void)dealloc { self.shapes = nil; [super dealloc]; } @end @implementation ChipmunkAbstractTileCache @synthesize marchHard = _marchHard, sampler = _sampler, tileOffset = _tileOffset; -(id)initWithSampler:(ChipmunkAbstractSampler *)sampler space:(ChipmunkSpace *)space tileSize:(cpFloat)tileSize samplesPerTile:(NSUInteger)samplesPerTile cacheSize:(NSUInteger)cacheSize { if((self = [super init])){ _sampler = [sampler retain]; _space = [space retain]; _tileSize = tileSize; _samplesPerTile =samplesPerTile; _tileOffset = cpvzero; _cacheSize = cacheSize; [self resetCache]; } return self; } -(void)removeShapesForTile:(ChipmunkCachedTile *)tile { for(ChipmunkShape *shape in tile.shapes) [_space remove:shape]; } -(void)dealloc { for(ChipmunkCachedTile *tile = _cacheTail; tile; tile = tile.next){ [tile autorelease]; } [_sampler release]; [_space release]; cpSpatialIndexFree(_tileIndex); [super dealloc]; } -(void)resetCache { _ensuredDirty = TRUE; // Reset the spatial index. if(_tileIndex) cpSpatialIndexFree(_tileIndex); _tileIndex = cpSpaceHashNew(_tileSize, (int)_cacheSize, (cpSpatialIndexBBFunc)ChipmunkCachedTileBB, NULL); // Remove all the shapes and release all the tiles. for(ChipmunkCachedTile *tile = _cacheTail; tile; tile = tile.next){ [self removeShapesForTile:tile]; [tile autorelease]; } // Clear out the tile list. _cacheHead = _cacheTail = nil; _tileCount = 0; } -(void)marchTile:(ChipmunkCachedTile *)tile { // Remove old shapes for this tile. for(ChipmunkShape *shape in tile.shapes) [_space remove:shape]; cpPolylineSet *set = cpPolylineSetNew(); (_marchHard ? cpMarchHard : cpMarchSoft)( tile.bb, _samplesPerTile, _samplesPerTile, _sampler.marchThreshold, (cpMarchSegmentFunc)cpPolylineSetCollectSegment, set, _sampler.sampleFunc, _sampler ); if(set->count){ ChipmunkBody *staticBody = [ChipmunkBody staticBody]; NSMutableArray *shapes = [NSMutableArray array]; for(int i=0; icount; i++){ cpPolyline *simplified = [self simplify:set->lines[i]]; for(int i=0; icount - 1; i++){ ChipmunkSegmentShape *segment = [self makeSegmentFor:staticBody from:simplified->verts[i] to:simplified->verts[i+1]]; [shapes addObject:segment]; [_space add:segment]; } cpPolylineFree(simplified); } tile.shapes = shapes; } else { tile.shapes = nil; } cpPolylineSetFree(set, TRUE); tile.dirty = FALSE; } static inline ChipmunkCachedTile * GetTileAt(cpSpatialIndex *index, int i, int j, cpFloat size, cpVect offset) { // Cannot directly get spatial hash cells, so we'll point query at the centers. cpVect point = cpv((i + 0.5)*size + offset.x, (j + 0.5)*size + offset.y); ChipmunkCachedTile *tile = nil; cpSpatialIndexQuery(index, &point, cpBBNewForCircle(point, 0.0f), (cpSpatialIndexQueryFunc)ChipmunkCachedTileQuery, &tile); return tile; } struct TileRect {int l, b, r, t;}; static inline cpBB BBForTileRect(struct TileRect rect, cpFloat size, cpVect offset) { return cpBBNew(rect.l*size + offset.x, rect.b*size + offset.y, rect.r*size + offset.x, rect.t*size + offset.y); } static inline struct TileRect TileRectForBB(cpBB bb, cpFloat size, cpVect offset, cpFloat spt_inv) { return (struct TileRect){ (int)cpffloor((bb.l - offset.x)/size - spt_inv), (int)cpffloor((bb.b - offset.x)/size - spt_inv), (int) cpfceil((bb.r - offset.y)/size + spt_inv), (int) cpfceil((bb.t - offset.y)/size + spt_inv), }; } -(void)markDirtyRect:(cpBB)bounds { cpFloat size = _tileSize; cpVect offset = _tileOffset; struct TileRect rect = TileRectForBB(bounds, size, offset, 1.0/(cpFloat)_samplesPerTile); if(!_ensuredDirty && cpBBContainsBB(_ensuredBB, BBForTileRect(rect, size, offset))){ _ensuredDirty = TRUE; } for(int i=rect.l; i #{command}" open(SSH_CONNECT, 'w+'){|ssh| ssh.puts command} end system("rsync '/tmp/Chipmunk-#{VERS}.tgz' 'chipmunk-physics.net:chipmunk-physics.net/release/Chipmunk-7.x/'") DOC = "Chipmunk-7.x/Chipmunk-#{VERS}-Docs" system("rsync -r '/tmp/Chipmunk-#{VERS}/doc/' 'chipmunk-physics.net:chipmunk-physics.net/release/#{DOC}'") system_remote("rm 'ChipmunkLatest.tgz'; ln -s 'Chipmunk-7.x/Chipmunk-#{VERS}.tgz' 'ChipmunkLatest.tgz'") DOC_LINK = "ChipmunkLatest-Docs" REF_LINK = "ChipmunkLatest-API-Reference" system_remote("rm '#{DOC_LINK}'; ln -s '#{DOC}' '#{DOC_LINK}'") system_remote("rm '#{REF_LINK}'; ln -s '#{DOC}/API-Reference' '#{REF_LINK}'") Chipmunk2D-Chipmunk-7.0.3/src/000077500000000000000000000000001347650476100160455ustar00rootroot00000000000000Chipmunk2D-Chipmunk-7.0.3/src/CMakeLists.txt000066400000000000000000000045061347650476100206120ustar00rootroot00000000000000file(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.3 set(CHIPMUNK_VERSION_MAJOR 7) set(CHIPMUNK_VERSION_MINOR 0) set(CHIPMUNK_VERSION_PATCH 3) 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) Chipmunk2D-Chipmunk-7.0.3/src/chipmunk.c000066400000000000000000000232751347650476100200400ustar00rootroot00000000000000/* 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/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, 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/chipmunk_ffi.h" Chipmunk2D-Chipmunk-7.0.3/src/cpArbiter.c000066400000000000000000000331501347650476100201260ustar00rootroot00000000000000/* 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/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))); } } Chipmunk2D-Chipmunk-7.0.3/src/cpArray.c000066400000000000000000000044701347650476100176170ustar00rootroot00000000000000/* 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/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; } Chipmunk2D-Chipmunk-7.0.3/src/cpBBTree.c000066400000000000000000000517221347650476100176460ustar00rootroot00000000000000/* 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/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 Chipmunk2D-Chipmunk-7.0.3/src/cpBody.c000066400000000000000000000345711347650476100174430ustar00rootroot00000000000000/* 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/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 = mass == 0.0f ? INFINITY : 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 = moment == 0.0f ? INFINITY : 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; } } Chipmunk2D-Chipmunk-7.0.3/src/cpCollision.c000066400000000000000000000617011347650476100204740ustar00rootroot00000000000000/* 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/chipmunk_private.h" #include "chipmunk/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); } Chipmunk2D-Chipmunk-7.0.3/src/cpDampedRotarySpring.c000066400000000000000000000137271347650476100223240ustar00rootroot00000000000000/* 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/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; } Chipmunk2D-Chipmunk-7.0.3/src/cpDampedSpring.c000066400000000000000000000157321347650476100211210ustar00rootroot00000000000000/* 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/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; } Chipmunk2D-Chipmunk-7.0.3/src/cpGearJoint.c000066400000000000000000000103131347650476100204140ustar00rootroot00000000000000/* 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/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; } Chipmunk2D-Chipmunk-7.0.3/src/cpGrooveJoint.c000066400000000000000000000136321347650476100210060ustar00rootroot00000000000000/* 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/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; } Chipmunk2D-Chipmunk-7.0.3/src/cpHashSet.c000066400000000000000000000132501347650476100200740ustar00rootroot00000000000000/* 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/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; } const void * cpHashSetInsert(cpHashSet *set, cpHashValue hash, const 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; } const void * cpHashSetRemove(cpHashSet *set, cpHashValue hash, const 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--; const void *elt = bin->elt; recycleBin(set, bin); return elt; } return NULL; } const void * cpHashSetFind(cpHashSet *set, cpHashValue hash, const 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; } } } Chipmunk2D-Chipmunk-7.0.3/src/cpHastySpace.c000066400000000000000000000447451347650476100206160ustar00rootroot00000000000000// 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/chipmunk_private.h" #include "chipmunk/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); } Chipmunk2D-Chipmunk-7.0.3/src/cpMarch.c000066400000000000000000000147731347650476100176020ustar00rootroot00000000000000// Copyright 2013 Howling Moon Software. All rights reserved. // See http://chipmunk2d.net/legal.php for more information. #include #include #include #include "chipmunk/chipmunk.h" #include "chipmunk/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); } Chipmunk2D-Chipmunk-7.0.3/src/cpPinJoint.c000066400000000000000000000122721347650476100202720ustar00rootroot00000000000000/* 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/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; } Chipmunk2D-Chipmunk-7.0.3/src/cpPivotJoint.c000066400000000000000000000111211347650476100206350ustar00rootroot00000000000000/* 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/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; } Chipmunk2D-Chipmunk-7.0.3/src/cpPolyShape.c000066400000000000000000000224141347650476100204430ustar00rootroot00000000000000/* 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/chipmunk_private.h" #include "chipmunk/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); } Chipmunk2D-Chipmunk-7.0.3/src/cpPolyline.c000066400000000000000000000420421347650476100203310ustar00rootroot00000000000000// Copyright 2013 Howling Moon Software. All rights reserved. // See http://chipmunk2d.net/legal.php for more information. #include #include #include #include #include "chipmunk/chipmunk_private.h" #include "chipmunk/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; } Chipmunk2D-Chipmunk-7.0.3/src/cpRatchetJoint.c000066400000000000000000000122311347650476100211310ustar00rootroot00000000000000/* 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/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; } Chipmunk2D-Chipmunk-7.0.3/src/cpRobust.c000066400000000000000000000004721347650476100200150ustar00rootroot00000000000000#include "chipmunk/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)); } Chipmunk2D-Chipmunk-7.0.3/src/cpRotaryLimitJoint.c000066400000000000000000000111041347650476100220140ustar00rootroot00000000000000/* 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/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; } Chipmunk2D-Chipmunk-7.0.3/src/cpShape.c000066400000000000000000000355511347650476100176050ustar00rootroot00000000000000/* 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/chipmunk_private.h" #include "chipmunk/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; cpFloat r_over_d = d > 0.0f ? r/d : r; info->point = cpvadd(circle->tc, cpvmult(delta, r_over_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); } Chipmunk2D-Chipmunk-7.0.3/src/cpSimpleMotor.c000066400000000000000000000065621347650476100210170ustar00rootroot00000000000000/* 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/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; } Chipmunk2D-Chipmunk-7.0.3/src/cpSlideJoint.c000066400000000000000000000133761347650476100206120ustar00rootroot00000000000000/* 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/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; } Chipmunk2D-Chipmunk-7.0.3/src/cpSpace.c000066400000000000000000000470311347650476100175740ustar00rootroot00000000000000/* 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/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("Initializing cpSpace - Chipmunk v%s (Debug Enabled)\n", cpVersionString); printf("Compile with -DNDEBUG defined 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; } Chipmunk2D-Chipmunk-7.0.3/src/cpSpaceComponent.c000066400000000000000000000300521347650476100214520ustar00rootroot00000000000000/* 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/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); } Chipmunk2D-Chipmunk-7.0.3/src/cpSpaceDebug.c000066400000000000000000000142351347650476100205430ustar00rootroot00000000000000/* 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/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 Chipmunk2D-Chipmunk-7.0.3/src/cpSpaceHash.c000066400000000000000000000366151347650476100204060ustar00rootroot00000000000000/* 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/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 Chipmunk2D-Chipmunk-7.0.3/src/cpSpaceQuery.c000066400000000000000000000163021347650476100206170ustar00rootroot00000000000000/* 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/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; } Chipmunk2D-Chipmunk-7.0.3/src/cpSpaceStep.c000066400000000000000000000334521347650476100204320ustar00rootroot00000000000000/* 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/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); } Chipmunk2D-Chipmunk-7.0.3/src/cpSpatialIndex.c000066400000000000000000000046361347650476100211320ustar00rootroot00000000000000/* 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/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); } } Chipmunk2D-Chipmunk-7.0.3/src/cpSweep1D.c000066400000000000000000000146331347650476100200130ustar00rootroot00000000000000/* 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/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;} Chipmunk2D-Chipmunk-7.0.3/src/prime.h000066400000000000000000000034051347650476100173340ustar00rootroot00000000000000/* 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]; } Chipmunk2D-Chipmunk-7.0.3/xcode/000077500000000000000000000000001347650476100163605ustar00rootroot00000000000000Chipmunk2D-Chipmunk-7.0.3/xcode/Benchmark/000077500000000000000000000000001347650476100202525ustar00rootroot00000000000000Chipmunk2D-Chipmunk-7.0.3/xcode/Benchmark/Benchmark-Info.plist000066400000000000000000000026411347650476100241150ustar00rootroot00000000000000 CFBundleDevelopmentRegion en CFBundleDisplayName ${PRODUCT_NAME} CFBundleExecutable ${EXECUTABLE_NAME} CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName ${PRODUCT_NAME} CFBundlePackageType APPL CFBundleShortVersionString 1.0 CFBundleSignature ???? CFBundleVersion 1.0 LSRequiresIPhoneOS UIRequiredDeviceCapabilities armv7 UISupportedInterfaceOrientations UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UISupportedInterfaceOrientations~ipad UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight Chipmunk2D-Chipmunk-7.0.3/xcode/Benchmark/iPhoneBenchmarkMain.m000066400000000000000000000066351347650476100243040ustar00rootroot00000000000000/* 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. */ #import #import #import "chipmunk/chipmunk.h" #import "ChipmunkDemo.h" static void shapeFreeWrap(cpSpace *space, cpShape *shape, void *unused){ cpSpaceRemoveShape(space, shape); cpShapeFree(shape); } static void postShapeFree(cpShape *shape, cpSpace *space){ cpSpaceAddPostStepCallback(space, (cpPostStepFunc)shapeFreeWrap, shape, NULL); } static void constraintFreeWrap(cpSpace *space, cpConstraint *constraint, void *unused){ cpSpaceRemoveConstraint(space, constraint); cpConstraintFree(constraint); } static void postConstraintFree(cpConstraint *constraint, cpSpace *space){ cpSpaceAddPostStepCallback(space, (cpPostStepFunc)constraintFreeWrap, constraint, NULL); } static void bodyFreeWrap(cpSpace *space, cpBody *body, void *unused){ cpSpaceRemoveBody(space, body); cpBodyFree(body); } static void postBodyFree(cpBody *body, cpSpace *space){ cpSpaceAddPostStepCallback(space, (cpPostStepFunc)bodyFreeWrap, body, NULL); } // Safe and future proof way to remove and free all objects that have been added to the space. void ChipmunkDemoFreeSpaceChildren(cpSpace *space) { // Must remove these BEFORE freeing the body or you will access dangling pointers. cpSpaceEachShape(space, (cpSpaceShapeIteratorFunc)postShapeFree, space); cpSpaceEachConstraint(space, (cpSpaceConstraintIteratorFunc)postConstraintFree, space); cpSpaceEachBody(space, (cpSpaceBodyIteratorFunc)postBodyFree, space); } void ChipmunkDemoDefaultDrawImpl(cpSpace *space){} extern ChipmunkDemo bench_list[]; extern int bench_count; #include #include static double GetMilliseconds(){ struct timeval time; gettimeofday(&time, NULL); return (time.tv_sec*1000.0 + time.tv_usec/1000.0); } static void time_trial(int index, int count) { cpSpace *space = bench_list[index].initFunc(); double start_time = GetMilliseconds(); for(int i=0; i #import "ObjectiveChipmunk/ObjectiveChipmunk.h" @interface UpdateTestBody : ChipmunkBody @property(nonatomic, assign) BOOL velocityUpdated; @property(nonatomic, assign) BOOL positionUpdated; @end @implementation UpdateTestBody -(void)updateVelocity:(cpFloat)dt gravity:(cpVect)gravity damping:(cpFloat)damping { self.velocityUpdated = TRUE; } -(void)updatePosition:(cpFloat)dt { self.positionUpdated = TRUE; } @end @interface BodyTest : XCTestCase {} @end @implementation BodyTest #define TestAccessors(o, p, v) o.p = v; XCTAssertEqual(o.p, v, @""); #define TestAccessorsV(o, p, v) o.p = v; XCTAssertTrue(cpveql(o.p, v), @""); -(void)testProperties { ChipmunkBody *body = [ChipmunkBody bodyWithMass:123 andMoment:123]; XCTAssertEqual(body.mass, (cpFloat)123, @""); XCTAssertEqual(body.moment, (cpFloat)123, @""); XCTAssertNotEqual(body.body, NULL, @""); XCTAssertNil(body.userData, @""); TestAccessors(body, userData, @"object"); TestAccessors(body, mass, (cpFloat)5); TestAccessors(body, moment, (cpFloat)5); TestAccessorsV(body, position, cpv(5,6)); TestAccessorsV(body, velocity, cpv(5,6)); TestAccessorsV(body, force, cpv(5,6)); TestAccessors(body, angle, (cpFloat)5); TestAccessors(body, angularVelocity, (cpFloat)5); TestAccessors(body, torque, (cpFloat)5); body.angle = 0; body.angle = CP_PI; body.angle = CP_PI/2.0f; // TODO transform tests XCTAssertFalse(body.isSleeping, @""); XCTAssertEqual(body.type, CP_BODY_TYPE_DYNAMIC, @""); } -(void)testBasic { ChipmunkBody *body = [ChipmunkBody bodyWithMass:1 andMoment:1]; [body applyForce:cpv(0,1) atWorldPoint:cpv(1,0)]; XCTAssertTrue(body.force.y > 0, @""); XCTAssertTrue(body.force.x == 0, @""); XCTAssertTrue(body.torque > 0, @""); body.force = cpvzero; body.torque = 0.0f; XCTAssertTrue(cpveql(body.force, cpvzero), @""); XCTAssertEqual(body.torque, (cpFloat)0, @""); [body applyImpulse:cpv(0,1) atWorldPoint:cpv(1,0)]; XCTAssertTrue(body.velocity.y > 0, @""); XCTAssertTrue(body.velocity.x == 0, @""); XCTAssertTrue(body.angularVelocity > 0, @""); } -(void)testMisc { ChipmunkBody *staticBody = [ChipmunkBody staticBody]; XCTAssertFalse(staticBody.isSleeping, @""); XCTAssertEqual(staticBody.type, CP_BODY_TYPE_STATIC, @""); ChipmunkSpace *space = [[ChipmunkSpace alloc] init]; space.sleepTimeThreshold = 1.0; ChipmunkBody *body = [ChipmunkBody bodyWithMass:1 andMoment:1]; [space add:body]; XCTAssertFalse(body.isSleeping, @""); XCTAssertEqual(body.type, CP_BODY_TYPE_DYNAMIC, @""); [body sleep]; XCTAssertTrue(body.isSleeping, @""); [space release]; } -(void)testUpdate { ChipmunkSpace *space = [[ChipmunkSpace alloc] init]; UpdateTestBody *body = [space add:[UpdateTestBody bodyWithMass:1.0 andMoment:1.0]]; XCTAssertTrue(body.body->velocity_func != cpBodyUpdateVelocity, @"Did not set velocity integration callback."); XCTAssertTrue(body.body->position_func != cpBodyUpdatePosition, @"Did not set position integration callback."); [space step:1.0]; XCTAssertTrue(body.velocityUpdated, @"Did not call velocity integration callback."); XCTAssertTrue(body.positionUpdated, @"Did not call position integration callback."); [space release]; } -(void)testSpace { ChipmunkSpace *space = [[ChipmunkSpace alloc] init]; ChipmunkBody *body = [ChipmunkBody bodyWithMass:1.0 andMoment:1.0]; XCTAssertNil(body.space, @"body.space should be nil."); [space add:body]; XCTAssertEqual(body.space, space, @"body.space should be space."); [space release]; } -(void)testTimeSteping { ChipmunkSpace *space = [[ChipmunkSpace alloc] init]; space.gravity = cpv(0, -1); ChipmunkBody *dynamicBody = [space add:[ChipmunkBody bodyWithMass:1.0 andMoment:1.0]]; ChipmunkBody *staticBody = [space add:[ChipmunkBody staticBody]]; ChipmunkBody *kinematicBody = [space add:[ChipmunkBody kinematicBody]]; kinematicBody.velocity = cpv(1, 0); [space step:1.0]; // Should have accelerated but not moved. XCTAssert(cpveql(dynamicBody.position, cpvzero), @""); XCTAssert(cpveql(dynamicBody.velocity, cpv(0, -1)), @""); // Should not have moved nor accelerated. XCTAssert(cpveql(staticBody.position, cpvzero), @""); XCTAssert(cpveql(staticBody.velocity, cpvzero), @""); // Should have moved right but not accelerated downward. XCTAssert(cpveql(kinematicBody.position, cpv(1, 0)), @""); XCTAssert(cpveql(kinematicBody.velocity, cpv(1, 0)), @""); [space step:1.0]; // Should have moved and accelerated more. XCTAssert(cpveql(dynamicBody.position, cpv(0, -1)), @""); XCTAssert(cpveql(dynamicBody.velocity, cpv(0, -2)), @""); // Still should not have moved nor accelerated. XCTAssert(cpveql(staticBody.position, cpvzero), @""); XCTAssert(cpveql(staticBody.velocity, cpvzero), @""); // Should have moved right more but still not accelerated downward. XCTAssert(cpveql(kinematicBody.position, cpv(2, 0)), @""); XCTAssert(cpveql(kinematicBody.velocity, cpv(1, 0)), @""); [space release]; } @end Chipmunk2D-Chipmunk-7.0.3/xcode/ObjectiveChipmunkTests/CallbacksTest.m000066400000000000000000000356671347650476100257320ustar00rootroot00000000000000/* 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. */ #import #import "ObjectiveChipmunk/ObjectiveChipmunk.h" @interface CallbacksTest : XCTestCase {} @end @implementation CallbacksTest // TODO test callbacks trigger // TODO test reject from begin // TODO test reject from pre-solve // TODO test sensors // TODO test first collision // TODO test post-step callbacks static cpBool Begin(cpArbiter *arb, cpSpace *space, NSMutableString *string){ [string appendString:@"Begin-"]; return cpTrue; } static cpBool PreSolve(cpArbiter *arb, cpSpace *space, NSMutableString *string){ [string appendString:@"PreSolve-"]; return cpTrue; } static void PostSolve(cpArbiter *arb, cpSpace *space, NSMutableString *string){ [string appendString:@"PostSolve-"]; } static void Separate(cpArbiter *arb, cpSpace *space, NSMutableString *string){ [string appendString:@"Separate-"]; } static void testHandlersHelper(id self, bool separateByRemove, bool enableContactGraph){ ChipmunkSpace *space = [[ChipmunkSpace alloc] init]; space.collisionBias = 1.0f; cpFloat radius = 5; ChipmunkBody *body1 = [space add:[ChipmunkBody bodyWithMass:1 andMoment:1]]; body1.position = cpv(0*radius*1.5,0); [space add:[ChipmunkCircleShape circleWithBody:body1 radius:radius offset:cpvzero]]; ChipmunkBody *body2 = [space add:[ChipmunkBody bodyWithMass:1 andMoment:1]]; body2.position = cpv(1*radius*1.5,0); ChipmunkShape *shape2 = [space add:[ChipmunkCircleShape circleWithBody:body2 radius:radius offset:cpvzero]]; NSMutableString *string = [NSMutableString string]; cpCollisionHandler *handler = cpSpaceAddCollisionHandler(space.space, nil, nil); handler->beginFunc = (cpCollisionBeginFunc)Begin, handler->preSolveFunc = (cpCollisionPreSolveFunc)PreSolve, handler->postSolveFunc = (cpCollisionPostSolveFunc)PostSolve, handler->separateFunc = (cpCollisionSeparateFunc)Separate, handler->userData = string; // Test for separate callback when moving: [space step:0.1]; XCTAssertEqualObjects(string, @"Begin-PreSolve-PostSolve-", @""); [space step:0.1]; XCTAssertEqualObjects(string, @"Begin-PreSolve-PostSolve-PreSolve-PostSolve-", @""); if(separateByRemove){ [space remove:shape2]; } else { body2.position = cpv(100, 100); [space step:0.1]; } XCTAssertEqualObjects(string, @"Begin-PreSolve-PostSolve-PreSolve-PostSolve-Separate-", @""); // Step once more to check for dangling pointers [space step:0.1]; // Cleanup [space release]; } -(void)testHandlers { testHandlersHelper(self, true, true); testHandlersHelper(self, false, true); testHandlersHelper(self, false, false); testHandlersHelper(self, true, false); } static void testHandlersSleepingHelper(id self, int wakeRemoveType){ ChipmunkSpace *space = [[ChipmunkSpace alloc] init]; space.collisionBias = 1.0f; space.sleepTimeThreshold = 0.15; NSString *type = @"type"; cpFloat radius = 5; ChipmunkBody *body1 = [space add:[ChipmunkBody bodyWithMass:1 andMoment:1]]; body1.position = cpv(0*radius*1.5,0); ChipmunkShape *shape1 = [space add:[ChipmunkCircleShape circleWithBody:body1 radius:radius offset:cpvzero]]; shape1.collisionType = type; ChipmunkBody *body2 = [space add:[ChipmunkBody bodyWithMass:1 andMoment:1]]; body2.position = cpv(1*radius*1.5,0); ChipmunkShape *shape2 = [space add:[ChipmunkCircleShape circleWithBody:body2 radius:radius offset:cpvzero]]; shape2.collisionType = type; NSMutableString *string = [NSMutableString string]; cpCollisionHandler *handler = cpSpaceAddCollisionHandler(space.space, type, type); handler->beginFunc = (cpCollisionBeginFunc)Begin, handler->preSolveFunc = (cpCollisionPreSolveFunc)PreSolve, handler->postSolveFunc = (cpCollisionPostSolveFunc)PostSolve, handler->separateFunc = (cpCollisionSeparateFunc)Separate, handler->userData = string; // Test for separate callback when moving: [space step:0.1]; XCTAssertEqualObjects(string, @"Begin-PreSolve-PostSolve-", @""); [space step:0.1]; XCTAssertEqualObjects(string, @"Begin-PreSolve-PostSolve-PreSolve-", @""); [space step:0.1]; XCTAssertEqualObjects(string, @"Begin-PreSolve-PostSolve-PreSolve-", @""); switch(wakeRemoveType){ case 0: // Separate by removal [space remove:shape2]; XCTAssertEqualObjects(string, @"Begin-PreSolve-PostSolve-PreSolve-Separate-", @""); break; case 1: // Separate by move body2.position = cpv(100, 100); [space step:0.1]; XCTAssertEqualObjects(string, @"Begin-PreSolve-PostSolve-PreSolve-Separate-", @""); break; default:break; } // Step once more to check for dangling pointers [space step:0.1]; // Cleanup [space release]; } -(void)testHandlersSleeping { testHandlersSleepingHelper(self, 0); testHandlersSleepingHelper(self, 1); // BUG if the time threshold is less than dt the bodies fall asleep the same frame after being awoken // Separate is not called because of the short circuit in cpSpaceArbiterSetFilter(). // This is a weird edge case though as it's a really bad idea to use such a small threshold } static void testSleepingSensorCallbacksHelper(id self, int wakeRemoveType){ ChipmunkSpace *space = [[ChipmunkSpace alloc] init]; space.collisionBias = 1.0f; space.sleepTimeThreshold = 0.15; cpFloat radius = 5; ChipmunkBody *body1 = [space add:[ChipmunkBody bodyWithMass:1 andMoment:1]]; body1.position = cpv(0*radius*1.5,0); [space add:[ChipmunkCircleShape circleWithBody:body1 radius:radius offset:cpvzero]]; ChipmunkBody *body2 = [space add:[ChipmunkBody staticBody]]; body2.position = cpv(1*radius*1.5,0); ChipmunkShape *shape2 = [space add:[ChipmunkCircleShape circleWithBody:body2 radius:radius offset:cpvzero]]; shape2.sensor = true; NSMutableString *string = [NSMutableString string]; cpCollisionHandler *handler = cpSpaceAddCollisionHandler(space.space, nil, nil); handler->beginFunc = (cpCollisionBeginFunc)Begin, handler->preSolveFunc = (cpCollisionPreSolveFunc)PreSolve, handler->postSolveFunc = (cpCollisionPostSolveFunc)PostSolve, handler->separateFunc = (cpCollisionSeparateFunc)Separate, handler->userData = string; // Test for separate callback when moving: [space step:0.1]; XCTAssertEqualObjects(string, @"Begin-PreSolve-", @""); [space step:0.1]; XCTAssertEqualObjects(string, @"Begin-PreSolve-PreSolve-", @""); [space step:0.1]; XCTAssertEqualObjects(string, @"Begin-PreSolve-PreSolve-", @""); switch(wakeRemoveType){ case 0: // Separate by removal [space remove:shape2]; XCTAssertEqualObjects(string, @"Begin-PreSolve-PreSolve-Separate-", @""); break; case 1: // Separate by move body1.position = cpv(100, 100); [space step:0.1]; XCTAssertEqualObjects(string, @"Begin-PreSolve-PreSolve-Separate-", @""); break; default:break; } // Step once more to check for dangling pointers [space step:0.1]; // Cleanup [space release]; } -(void)testSleepingSensorCallbacks { testSleepingSensorCallbacksHelper(self, 0); testSleepingSensorCallbacksHelper(self, 1); } static cpBool CallBlock(cpArbiter *arb, cpSpace *space, cpBool (^block)(cpArbiter *arb)){return block(arb);} -(void)testPostStepRemoval { NSString *ballType = @"ballType"; NSString *barType = @"barType"; ChipmunkSpace *space = [[ChipmunkSpace alloc] init]; space.gravity = cpv(0, -100); // TODO cpCollisionHandler *handler = cpSpaceAddCollisionHandler(space.space, ballType, barType); handler->beginFunc = (cpCollisionBeginFunc)CallBlock, handler->userData = ^(cpArbiter *arb){ CHIPMUNK_ARBITER_GET_SHAPES(arb, ballShape, barShape); [space addPostStepRemoval:barShape]; return TRUE; }; ChipmunkShape *shape; // The ball will stop on this bar shape = [space add:[ChipmunkSegmentShape segmentWithBody:space.staticBody from:cpv(-10,0) to:cpv(10,0) radius:1]]; // but remove this one shape = [space add:[ChipmunkSegmentShape segmentWithBody:space.staticBody from:cpv(-10,2) to:cpv(10,2) radius:1]]; shape.collisionType = barType; ChipmunkBody *ball = [space add:[ChipmunkBody bodyWithMass:1 andMoment:cpMomentForCircle(1, 0, 1, cpvzero)]]; ball.position = cpv(0, 10); shape = [space add:[ChipmunkCircleShape circleWithBody:ball radius:1 offset:cpvzero]]; shape.collisionType = ballType; for(int i=0; i<100; i++) [space step:0.01]; XCTAssertEqualWithAccuracy(ball.position.y, (cpFloat)2, 1.1*space.collisionSlop, @""); [space release]; } // Make sure that adding a post step callback from inside a post step callback works correctly. -(void)testPostStepFromPostStep { ChipmunkSpace *space = [[ChipmunkSpace alloc] init]; ChipmunkShape *staticShape = [space add:[ChipmunkCircleShape circleWithBody:space.staticBody radius:1.0 offset:cpvzero]]; staticShape.collisionType = staticShape; ChipmunkBody *body1 = [space add:[ChipmunkBody kinematicBody]]; ChipmunkShape *shape1 = [space add:[ChipmunkCircleShape circleWithBody:body1 radius:1.0 offset:cpvzero]]; shape1.collisionType = shape1; shape1.sensor = TRUE; ChipmunkBody *body2 = [space add:[ChipmunkBody kinematicBody]]; ChipmunkShape *shape2 = [space add:[ChipmunkCircleShape circleWithBody:body2 radius:10.0 offset:cpvzero]]; shape2.collisionType = shape2; shape2.sensor = TRUE; __block bool trigger1 = FALSE; __block bool trigger2 = FALSE; __block bool trigger3 = FALSE; __block bool trigger4 = FALSE; __block bool trigger5 = FALSE; __block bool trigger6 = FALSE; __block bool trigger7 = FALSE; cpCollisionHandler *handler1 = cpSpaceAddCollisionHandler(space.space, staticShape, shape1); handler1->separateFunc = (cpCollisionSeparateFunc)CallBlock, handler1->userData = ^(cpArbiter *arb){ XCTAssertTrue(cpSpaceIsLocked(space.space), @""); // When body1 moves it will trigger the first separate callback. [space addPostStepBlock:^{ XCTAssertFalse(cpSpaceIsLocked(space.space), @""); // Calling remove will immediately trigger the next separate callback. [space remove:shape1]; trigger2 = TRUE; } key:shape1]; trigger1 = TRUE; }; cpCollisionHandler *handler2 = cpSpaceAddCollisionHandler(space.space, shape1, shape2); handler2->separateFunc = (cpCollisionSeparateFunc)CallBlock, handler2->userData = ^(cpArbiter *arb){ XCTAssertTrue(cpSpaceIsLocked(space.space), @""); // schedule a second post step callback within the old one with the same key. // This one shouldn't be called. [space addPostStepBlock:^{trigger4 = TRUE;} key:shape1]; trigger3 = TRUE; }; cpCollisionHandler *handler3 = cpSpaceAddCollisionHandler(space.space, staticShape, shape2); handler3->separateFunc = (cpCollisionSeparateFunc)CallBlock, handler3->userData = ^(cpArbiter *arb){ XCTAssertTrue(cpSpaceIsLocked(space.space), @""); [space addPostStepBlock:^{ [space addPostStepBlock:^{trigger7 = TRUE;} key:shape2]; trigger6 = TRUE; } key:shape2]; trigger5 = TRUE; }; [space step:1.0]; body1.position = cpv(10, 0); [space step:1.0]; XCTAssertTrue(trigger1, @""); XCTAssertTrue(trigger2, @""); XCTAssertTrue(trigger3, @""); XCTAssertFalse(trigger4, @""); [space remove:shape2]; XCTAssertTrue(trigger5, @""); XCTAssertTrue(trigger6, @""); XCTAssertFalse(trigger7, @""); [space release]; } -(void)testBlockIterators { ChipmunkSpace *space = [[ChipmunkSpace alloc] init]; ChipmunkShape *_staticShape = [ChipmunkCircleShape circleWithBody:space.staticBody radius:1.0 offset:cpvzero]; ChipmunkBody *_body = [ChipmunkBody bodyWithMass:1.0 andMoment:1.0]; ChipmunkShape *_shape = [ChipmunkCircleShape circleWithBody:_body radius:1.0 offset:cpvzero]; ChipmunkConstraint *_constraint = [ChipmunkPivotJoint pivotJointWithBodyA:space.staticBody bodyB:_body pivot:cpvzero]; { __block int counter = 0; cpSpaceEachBody_b(space.space, ^(cpBody *body){ XCTAssertEqual(cpBodyGetUserData(body), _body, @""); counter++; }); cpSpaceEachShape_b(space.space, ^(cpShape *shape){ if(cpBodyGetType(cpShapeGetBody(shape)) == CP_BODY_TYPE_STATIC){ XCTAssertEqual(shape, _staticShape.shape, @""); } else { XCTAssertEqual(shape, _shape.shape, @""); } counter++; }); cpSpaceEachConstraint_b(space.space, ^(cpConstraint *constraint){ XCTAssertEqual(constraint, _constraint.constraint, @""); counter++; }); cpBodyEachShape_b(space.staticBody.body, ^(cpShape *shape){ XCTAssertEqual(shape, _staticShape.shape, @""); counter++; }); cpBodyEachConstraint_b(space.staticBody.body, ^(cpConstraint *constraint){ XCTAssertEqual(constraint, _constraint.constraint, @""); counter++; }); cpBodyEachShape_b(_body.body, ^(cpShape *shape){ XCTAssertEqual(shape, _shape.shape, @""); counter++; }); cpBodyEachConstraint_b(_body.body, ^(cpConstraint *constraint){ XCTAssertEqual(constraint, _constraint.constraint, @""); counter++; }); XCTAssertEqual(counter, 0, @""); } [space add:_staticShape]; [space add:_body]; [space add:_shape]; [space add:_constraint]; { __block int counter = 0; cpSpaceEachBody_b(space.space, ^(cpBody *body){ XCTAssertEqual(body, _body.body, @""); counter++; }); cpSpaceEachShape_b(space.space, ^(cpShape *shape){ if(cpBodyGetType(cpShapeGetBody(shape)) == CP_BODY_TYPE_STATIC){ XCTAssertEqual(shape, _staticShape.shape, @""); } else { XCTAssertEqual(shape, _shape.shape, @""); } counter++; }); cpSpaceEachConstraint_b(space.space, ^(cpConstraint *constraint){ XCTAssertEqual(constraint, _constraint.constraint, @""); counter++; }); cpBodyEachShape_b(space.staticBody.body, ^(cpShape *shape){ XCTAssertEqual(shape, _staticShape.shape, @""); counter++; }); cpBodyEachConstraint_b(space.staticBody.body, ^(cpConstraint *constraint){ XCTAssertEqual(constraint, _constraint.constraint, @""); counter++; }); cpBodyEachShape_b(_body.body, ^(cpShape *shape){ XCTAssertEqual(shape, _shape.shape, @""); counter++; }); cpBodyEachConstraint_b(_body.body, ^(cpConstraint *constraint){ XCTAssertEqual(constraint, _constraint.constraint, @""); counter++; }); XCTAssertEqual(counter, 8, @""); } [space release]; } @end Chipmunk2D-Chipmunk-7.0.3/xcode/ObjectiveChipmunkTests/ConvexTest.m000066400000000000000000000224661347650476100253060ustar00rootroot00000000000000/* 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. */ #import #import "ObjectiveChipmunk/ObjectiveChipmunk.h" #import "ObjectiveChipmunk/ChipmunkAutogeometry.h" @interface ConvexTest : XCTestCase {} @end @implementation ConvexTest //MARK: QuickHull tests static inline cpFloat frand(void){return (cpFloat)rand()/(cpFloat)RAND_MAX;} static int FirstExpected(int expectedCount, cpVect *expectedVerts, cpVect *verts) { for(int i=0; i= 0.0f, @""); XCTAssertTrue(cpAreaForPoly(resultCount, resultVerts, 0.0f) >= 0.0f, @""); XCTAssertTrue(cpveql(resultVerts[0], rotated[first]), @""); AssertExpected(self, resultVerts, expectedCount, expectedVerts); AssertHullsEqual(self, resultCount, resultVerts); } } static void AssertRandomHull(id self, int count) { cpVect verts[count]; for(int i=0; i 0.0f, @""); } -(void)testConvexHull { { // Trivial cases, reversed windings up to a square. cpVect verts[] = {{0,0}, {1,0}}; cpVect expected[] = {{0,0}, {1,0}}; AssertHull(self, 1, verts, 1, expected); AssertHull(self, 2, verts, 2, expected); } { cpVect verts[] = {{0,0}, {1,1}, {1,0}}; cpVect expected[] = {{0,0}, {1,0}, {1,1}}; AssertHull(self, 3, verts, 3, expected); } { cpVect verts[] = {{0,0}, {0,1}, {1,1}, {1,0}}; cpVect expected[] = {{0,0}, {1,0}, {1,1}, {0,1}}; AssertHull(self, 4, verts, 4, expected); } { // Degenerate hourglass shaped square. cpVect verts[] = {{0,0}, {1,0}, {0,1}, {1,1}}; cpVect expected[] = {{0,0}, {1,0}, {1,1}, {0,1}}; AssertHull(self, 4, verts, 4, expected); } { // A unit square and a bunch of random vertexes inside it. cpVect verts[] = { {0.9, 0.5}, {0.2, 0.3}, {1.0, 0.0}, {0.8, 0.6}, {0.8, 0.4}, {0.1, 0.4}, {0.4, 0.1}, {0.0, 0.0}, {0.1, 0.9}, {0.9, 0.6}, {0.6, 0.2}, {0.7, 0.3}, {0.1, 0.3}, {0.1, 0.5}, {0.8, 0.3}, {0.3, 0.8}, {0.0, 1.0}, {0.6, 0.7}, {1.0, 1.0}, {0.6, 0.8}, {0.1, 0.7}, {0.7, 0.9}, {0.9, 0.2}, {0.1, 0.1} }; cpVect expected[] = {{0,0}, {1,0}, {1,1}, {0,1}}; AssertHull(self, 24, verts, 4, expected); } { // A bunch of colinear points on X cpVect verts[] = {{0,0}, {0,1}, {0,2}, {0,3}, {0,4}, {0,5}}; cpVect expected[] = {{0,0}, {0,5}}; AssertHull(self, 6, verts, 2, expected); } { // A bunch of colinear points on Y cpVect verts[] = {{0,0}, {1,0}, {2,0}, {3,0}, {4,0}, {5,0}}; cpVect expected[] = {{0,0}, {5,0}}; AssertHull(self, 6, verts, 2, expected); } { // A bunch of colinear points on XY cpVect verts[] = {{0,0}, {1,1}, {2,2}, {3,3}, {4,4}, {5,5}}; cpVect expected[] = {{0,0}, {5,5}}; AssertHull(self, 6, verts, 2, expected); } { // A bunch of equivalent points cpVect verts[] = {{0,0}, {0,0}, {0,0}, {0,0}, {0,0}}; cpVect expected[] = {{0,0}}; AssertHull(self, 1, verts, 1, expected); AssertHull(self, 2, verts, 1, expected); AssertHull(self, 3, verts, 1, expected); AssertHull(self, 4, verts, 1, expected); AssertHull(self, 5, verts, 1, expected); } { // Unit square with reversed winding and colinear face points. cpVect verts[] = { {1.0f, 0.0f}, {1.0f, 0.5f}, {1.0f, 1.0f}, {0.5f, 1.0f}, {0.0f, 1.0f}, {0.0f, 0.5f}, {0.0f, 0.0f}, {0.5f, 0.0f}, }; cpVect expected[] = {{0,0}, {1,0}, {1,1}, {0,1}}; AssertHull(self, 8, verts, 4, expected); } { // Unit square with duplicate vertexes. cpVect verts1[] = {{0,0}, {0,1}, {1,1}, {1,0}, {0,0}, {0,1}, {1,1}, {1,0}}; cpVect verts2[] = {{0,0}, {0,0}, {0,1}, {0,1}, {1,1}, {1,1}, {1,0}, {1,0}}; cpVect expected[] = {{0,0}, {1,0}, {1,1}, {0,1}}; AssertHull(self, 8, verts1, 4, expected); AssertHull(self, 8, verts2, 4, expected); } // Check that a convex hull returns itself using (seeded) random hulls. srand(5594); AssertRandomHull(self, 100); AssertRandomHull(self, 1000); AssertRandomHull(self, 10000); // TODO Need tolerance tests. } -(void)testValidate { cpVect a = cpv(3.354, 2.546); cpVect b = cpv(55.213, 77.1232); int numVerts = 113; // cpVect verts[] = {a, cpvadd(cpvlerp(a, b, 0.5), cpv(0, -1e-7)), b}; cpVect verts[numVerts]; for(int i=0; i= 0.0f, @""); } static cpPolyline * MakeLoopedPolyline(id self, int count, cpVect *verts) { cpPolylineSet *set = cpPolylineSetNew(); cpPolylineSetCollectSegment(verts[count - 1], verts[0], set); for(int i=1; icount == 1, @""); XCTAssert(cpPolylineIsClosed(set->lines[0]), @""); cpPolyline *line = set->lines[0]; cpPolylineSetFree(set, cpFalse); return line; } static void AssertPolyline(id self, cpPolyline *line, int expectedCount, cpVect *expectedVerts) { XCTAssertEqual(line->count, expectedCount + 1, @""); XCTAssert(cpPolylineIsClosed(line), @""); AssertExpected(self, line->verts, expectedCount, expectedVerts); } -(void)testConvexDecomposition { { cpVect verts[] = {{0,0}}; cpPolyline *line = MakeLoopedPolyline(self, 1, verts); cpPolylineSet *set = cpPolylineConvexDecomposition_BETA(line, 0.0); XCTAssertEqual(set->count, 1, @""); AssertPolyline(self, set->lines[0], 1, verts); } { cpVect verts[] = {{0,0}, {1,0}}; cpPolyline *line = MakeLoopedPolyline(self, 2, verts); cpPolylineSet *set = cpPolylineConvexDecomposition_BETA(line, 0.0); XCTAssertEqual(set->count, 1, @""); AssertPolyline(self, set->lines[0], 2, verts); } { cpVect verts[] = {{0,0}, {1,0}, {0,1}}; cpPolyline *line = MakeLoopedPolyline(self, 3, verts); cpPolylineSet *set = cpPolylineConvexDecomposition_BETA(line, 0.0); XCTAssertEqual(set->count, 1, @""); AssertPolyline(self, set->lines[0], 3, verts); } { cpVect verts[] = {{0,0}, {1,0}, {1,1}, {0,1}}; cpPolyline *line = MakeLoopedPolyline(self, 4, verts); cpPolylineSet *set = cpPolylineConvexDecomposition_BETA(line, 0.0); XCTAssertEqual(set->count, 1, @""); AssertPolyline(self, set->lines[0], 4, verts); } { cpVect verts[] = {{0,0}, {1,1}, {2,0}, {1,2}}; cpVect expected1[] = {{1,1}, {2,0}, {1,2}}; cpVect expected2[] = {{0,0}, {1,1}, {1,2}}; cpPolyline *line = MakeLoopedPolyline(self, 4, verts); cpPolylineSet *set = cpPolylineConvexDecomposition_BETA(line, 0.0); XCTAssertEqual(set->count, 2, @""); AssertPolyline(self, set->lines[0], 3, expected1); AssertPolyline(self, set->lines[1], 3, expected2); } } @end Chipmunk2D-Chipmunk-7.0.3/xcode/ObjectiveChipmunkTests/MemoryTest.m000066400000000000000000000126761347650476100253160ustar00rootroot00000000000000/* 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. */ #import #import "ObjectiveChipmunk/ObjectiveChipmunk.h" #import "chipmunk/chipmunk_private.h" #define AssertRetainCount(obj, count) XCTAssertEqual([obj retainCount], (NSUInteger)count, @"") @interface MemoryTest : XCTestCase {} @end @implementation MemoryTest -(void)testBasic { ChipmunkSpace *space = [[ChipmunkSpace alloc] init]; ChipmunkBody *body1 = [[ChipmunkBody alloc] initWithMass:1 andMoment:1]; ChipmunkBody *body2 = [[ChipmunkBody alloc] initWithMass:1 andMoment:1]; ChipmunkShape *shape1 = [[ChipmunkCircleShape alloc] initWithBody:body1 radius:1 offset:cpvzero]; ChipmunkShape *shape2 = [[ChipmunkCircleShape alloc] initWithBody:body2 radius:1 offset:cpvzero]; ChipmunkConstraint *joint1 = [[ChipmunkPivotJoint alloc] initWithBodyA:body1 bodyB:body2 pivot:cpvzero]; ChipmunkConstraint *joint2 = [[ChipmunkPivotJoint alloc] initWithBodyA:body1 bodyB:body2 pivot:cpvzero]; [space add:body1]; [space add:body2]; [space add:shape1]; [space add:shape2]; [space add:joint1]; [space add:joint2]; AssertRetainCount(body1, 5); AssertRetainCount(body2, 5); AssertRetainCount(shape1, 2); AssertRetainCount(shape2, 2); AssertRetainCount(joint1, 2); AssertRetainCount(joint2, 2); [space remove:shape1]; [space remove:joint1]; AssertRetainCount(body1, 5); AssertRetainCount(body2, 5); AssertRetainCount(shape1, 1); AssertRetainCount(shape2, 2); AssertRetainCount(joint1, 1); AssertRetainCount(joint2, 2); [space release]; AssertRetainCount(body1, 4); AssertRetainCount(body2, 4); AssertRetainCount(shape1, 1); AssertRetainCount(shape2, 1); AssertRetainCount(joint1, 1); AssertRetainCount(joint2, 1); [joint1 release]; [joint2 release]; AssertRetainCount(body1, 2); AssertRetainCount(body2, 2); [shape1 release]; [shape2 release]; AssertRetainCount(body1, 1); AssertRetainCount(body2, 1); [body1 release]; [body2 release]; } -(void)testStaticBody { ChipmunkSpace *space = [[ChipmunkSpace alloc] init]; ChipmunkBody *staticBody = space.staticBody; AssertRetainCount(staticBody, 1); ChipmunkShape *shape = [[ChipmunkCircleShape alloc] initWithBody:space.staticBody radius:1 offset:cpvzero]; AssertRetainCount(staticBody, 2); [space add:shape]; AssertRetainCount(shape, 2); AssertRetainCount(staticBody, 2); [space release]; AssertRetainCount(shape, 1); AssertRetainCount(staticBody, 1); [shape release]; } -(void)testSetters { ChipmunkBody *body = [[ChipmunkBody alloc] initWithMass:1.0 andMoment:1.0]; ChipmunkShape *shape = [ChipmunkCircleShape circleWithBody:nil radius:1 offset:cpvzero]; shape.body = body; AssertRetainCount(body, 2); shape.body = body; AssertRetainCount(body, 2); shape.body = nil; AssertRetainCount(body, 1); // ChipmunkConstraint *joint = [ChipmunkPivotJoint pivotJointWithBodyA:nil bodyB:nil pivot:cpvzero]; // joint.bodyA = body; joint.bodyB = body; // AssertRetainCount(body, 3); // // joint.bodyA = body; joint.bodyB = body; // AssertRetainCount(body, 3); // // joint.bodyA = nil; joint.bodyB = nil; // AssertRetainCount(body, 1); } -(void)testPostStepCallbacks { ChipmunkSpace *space = [[ChipmunkSpace alloc] init]; NSObject *obj1 = [[NSObject alloc] init]; NSObject *obj2 = [[NSObject alloc] init]; // Lock the space to avoid triggering warnings. cpSpaceLock(space.space); // Registering the callback should retain the object twice XCTAssertTrue([space addPostStepCallback:obj1 selector:@selector(isEqual:) key:obj1], @""); AssertRetainCount(obj1, 3); // Registering the same callback a second time should not add more retains XCTAssertFalse([space addPostStepCallback:obj1 selector:@selector(isEqual:) key:obj1], @""); AssertRetainCount(obj1, 3); // A key can only be registered once to prevent double removals. // Registering a second target with the same key is a no-op. XCTAssertFalse([space addPostStepCallback:obj2 selector:@selector(isEqual:) key:obj1], @""); AssertRetainCount(obj1, 3); AssertRetainCount(obj2, 1); XCTAssertTrue([space addPostStepCallback:obj1 selector:@selector(isEqual:) key:obj2], @""); AssertRetainCount(obj1, 4); AssertRetainCount(obj2, 2); cpSpaceUnlock(space.space, FALSE); // Stepping the space should release the callback handler and both objects [space step:1]; AssertRetainCount(obj1, 1); AssertRetainCount(obj2, 1); [space release]; [obj1 release]; [obj2 release]; } @end Chipmunk2D-Chipmunk-7.0.3/xcode/ObjectiveChipmunkTests/MiscTest.m000066400000000000000000000243641347650476100247360ustar00rootroot00000000000000/* 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. */ #import #import "ObjectiveChipmunk/ObjectiveChipmunk.h" #import "ObjectiveChipmunk/ChipmunkAutoGeometry.h" @interface MiscTest : XCTestCase {} @end @implementation MiscTest #define AssertNearlyZero(__exp__) XCTAssertTrue((__exp__) < 1e-5, @"") -(void)testSlerp { { cpVect a = cpvmult(cpvforangle(0.0), 1.0); cpVect b = cpvmult(cpvforangle(1.0), 1.0); cpVect c = a; cpVect v = cpvslerp(a, b, 0.0); AssertNearlyZero(cpvdist(v, c)); } { cpVect a = cpvmult(cpvforangle(0.0), 1.0); cpVect b = cpvmult(cpvforangle(1.0), 1.0); cpVect c = b; cpVect v = cpvslerp(a, b, 1.0); AssertNearlyZero(cpvdist(v, c)); } { cpVect a = cpvmult(cpvforangle(0.0), 1.0); cpVect b = cpvmult(cpvforangle(1.0), 1.0); cpVect c = cpvmult(cpvforangle(0.5), 1.0); cpVect v = cpvslerp(a, b, 0.5); AssertNearlyZero(cpvdist(v, c)); } { cpVect a = cpvmult(cpvforangle(-1.0), 1.0); cpVect b = cpvmult(cpvforangle( 1.0), 1.0); cpVect c = cpvmult(cpvforangle( 0.0), 1.0); cpVect v = cpvslerp(a, b, 0.5); AssertNearlyZero(cpvdist(v, c)); } { cpVect a = cpvmult(cpvforangle(0.0), 1.0); cpVect b = cpvmult(cpvforangle(CP_PI/2.0), 2.0); cpVect c = cpvadd(cpvmult(a, cpfcos(CP_PI/4.0)), cpvmult(b, cpfsin(CP_PI/4.0))); cpVect v = cpvslerp(a, b, 0.5); AssertNearlyZero(cpvdist(v, c)); } { cpVect a = cpvmult(cpvforangle(0.0), 1.0); cpVect b = a; cpVect c = a; cpVect v = cpvslerp(a, b, 0.5); AssertNearlyZero(cpvdist(v, c)); } // TODO should it handle this? // { // cpVect a = cpv( 1.0, 0.01); // cpVect b = cpv(-1.0, 0.0); // cpVect v = cpvslerp(a, b, 0.5); // GHAssertLessThan(cpvdot(a, v)); // GHAssertLessThan(cpvdot(b, v)); // GHAssertLessThan(cpvlength(v) - 1.0); // } // Slerp const { cpVect a = cpvmult(cpvforangle(0.0), 1.0); cpVect b = cpvmult(cpvforangle(CP_PI/2.0), 1.0); cpVect c = cpvadd(cpvmult(a, cpfcos(CP_PI/4.0)), cpvmult(b, cpfsin(CP_PI/4.0))); cpVect v = cpvslerpconst(a, b, CP_PI/4.0); AssertNearlyZero(cpvdist(v, c)); } { cpVect a = cpvmult(cpvforangle(0.0), 1.0); cpVect b = cpvmult(cpvforangle(CP_PI/2.0), 1.0); cpVect c = b; cpVect v = cpvslerpconst(a, b, CP_PI/2.0); AssertNearlyZero(cpvdist(v, c)); } { cpVect a = cpvmult(cpvforangle(0.0), 1.0); cpVect b = cpvmult(cpvforangle(CP_PI/2.0), 1.0); cpVect c = b; cpVect v = cpvslerpconst(a, b, INFINITY); AssertNearlyZero(cpvdist(v, c)); } { cpVect a = cpvmult(cpvforangle(0.0), 1.0); cpVect b = cpvmult(cpvforangle(CP_PI/2.0), 1.0); cpVect c = a; cpVect v = cpvslerpconst(a, b, 0); AssertNearlyZero(cpvdist(v, c)); } { cpVect a = cpvmult(cpvforangle(0.0), 1.0); cpVect b = cpvmult(cpvforangle(CP_PI/2.0), 1.0); cpVect c = cpvmult(cpvforangle(CP_PI/4.0), 1.0); cpVect v = cpvslerpconst(a, b, CP_PI/4.0); AssertNearlyZero(cpvdist(v, c)); } } -(void)testBBSegmentQuery { { // left cpVect a = cpv(-2, 0); cpVect b = cpv( 0, 0); cpBB bb = cpBBNew(-1, -1, 1, 1); cpFloat t = cpBBSegmentQuery(bb, a, b); XCTAssertEqualWithAccuracy(t, 0.5, 1e-5); } { // right cpVect a = cpv( 2, 0); cpVect b = cpv( 0, 0); cpBB bb = cpBBNew(-1, -1, 1, 1); cpFloat t = cpBBSegmentQuery(bb, a, b); XCTAssertEqualWithAccuracy(t, 0.5, 1e-5); } { // bottom cpVect a = cpv(0, -2); cpVect b = cpv(0, 0); cpBB bb = cpBBNew(-1, -1, 1, 1); cpFloat t = cpBBSegmentQuery(bb, a, b); XCTAssertEqualWithAccuracy(t, 0.5, 1e-5); } { // top cpVect a = cpv(0, 2); cpVect b = cpv(0, 0); cpBB bb = cpBBNew(-1, -1, 1, 1); cpFloat t = cpBBSegmentQuery(bb, a, b); XCTAssertEqualWithAccuracy(t, 0.5, 1e-5); } { // diagonal corner cpVect a = cpv(-2, -2); cpVect b = cpv( 0, 0); cpBB bb = cpBBNew(-1, -1, 1, 1); cpFloat t = cpBBSegmentQuery(bb, a, b); XCTAssertEqualWithAccuracy(t, 0.5, 1e-5); } { // diagonal edge cpVect a = cpv(-2, -1); cpVect b = cpv( 0, 1); cpBB bb = cpBBNew(-1, -1, 1, 1); cpFloat t = cpBBSegmentQuery(bb, a, b); XCTAssertEqualWithAccuracy(t, 0.5, 1e-5); } { // x-aligned low. cpVect a = cpv(-2, -1); cpVect b = cpv( 0, -1); cpBB bb = cpBBNew(-1, -1, 1, 1); cpFloat t = cpBBSegmentQuery(bb, a, b); XCTAssertEqual(t, 0.5); } { // x-aligned high. cpVect a = cpv(-2, 1); cpVect b = cpv( 0, 1); cpBB bb = cpBBNew(-1, -1, 1, 1); cpFloat t = cpBBSegmentQuery(bb, a, b); XCTAssertEqual(t, 0.5); } { // y-aligned low. cpVect a = cpv(-1, -2); cpVect b = cpv(-1, 0); cpBB bb = cpBBNew(-1, -1, 1, 1); cpFloat t = cpBBSegmentQuery(bb, a, b); XCTAssertEqual(t, 0.5); } { // y-aligned high. cpVect a = cpv(1, -2); cpVect b = cpv(1, 0); cpBB bb = cpBBNew(-1, -1, 1, 1); cpFloat t = cpBBSegmentQuery(bb, a, b); XCTAssertEqual(t, 0.5); } { // Miss, x-aligned low. cpVect a = cpv( 2, -2); cpVect b = cpv( 0, -2); cpBB bb = cpBBNew(-1, -1, 1, 1); cpFloat t = cpBBSegmentQuery(bb, a, b); XCTAssertEqual(t, INFINITY); } { // Miss, x-aligned high. cpVect a = cpv( 2, 2); cpVect b = cpv( 0, 2); cpBB bb = cpBBNew(-1, -1, 1, 1); cpFloat t = cpBBSegmentQuery(bb, a, b); XCTAssertEqual(t, INFINITY); } { // Miss, y-aligned low. cpVect a = cpv(-2, 2); cpVect b = cpv(-2, 0); cpBB bb = cpBBNew(-1, -1, 1, 1); cpFloat t = cpBBSegmentQuery(bb, a, b); XCTAssertEqual(t, INFINITY); } { // Miss, y-aligned high. cpVect a = cpv(2, 2); cpVect b = cpv(2, 0); cpBB bb = cpBBNew(-1, -1, 1, 1); cpFloat t = cpBBSegmentQuery(bb, a, b); XCTAssertEqual(t, INFINITY); } } -(void)testImageSamplerLA { { NSBundle *bundle = [NSBundle bundleForClass:self.class]; CGImageRef image = [ChipmunkImageSampler loadImage:[bundle URLForResource:@"TestImageLA" withExtension:@"png"]]; ChipmunkAbstractSampler *sampler = [[ChipmunkImageSampler alloc] initWithImage:image isMask:TRUE contextWidth:0 contextHeight:0]; XCTAssertEqualWithAccuracy([sampler sample:cpv(0.5, 0.5)], (cpFloat)0.0, 1e-5, @""); XCTAssertEqualWithAccuracy([sampler sample:cpv(0.5, 3.5)], (cpFloat)1.0, 1e-5, @""); XCTAssertEqualWithAccuracy([sampler sample:cpv(3.5, 0.5)], (cpFloat)1.0, 1e-5, @""); XCTAssertEqualWithAccuracy([sampler sample:cpv(2.0 - 1e-5, 0.5)], (cpFloat)0.0, 1e-5, @""); XCTAssertEqualWithAccuracy([sampler sample:cpv(2.0 + 1e-5, 0.5)], (cpFloat)1.0, 1e-5, @""); XCTAssertEqualWithAccuracy([sampler sample:cpv(0.5, 2.0 - 1e-5)], (cpFloat)0.0, 1e-5, @""); XCTAssertEqualWithAccuracy([sampler sample:cpv(0.5, 2.0 + 1e-5)], (cpFloat)1.0, 1e-5, @""); [sampler release]; } { NSBundle *bundle = [NSBundle bundleForClass:self.class]; CGImageRef image = [ChipmunkImageSampler loadImage:[bundle URLForResource:@"TestImageLA" withExtension:@"png"]]; ChipmunkAbstractSampler *sampler = [[ChipmunkImageSampler alloc] initWithImage:image isMask:FALSE contextWidth:0 contextHeight:0]; XCTAssertEqualWithAccuracy([sampler sample:cpv(0.5, 0.5)], (cpFloat)1.0, 1e-5, @""); XCTAssertEqualWithAccuracy([sampler sample:cpv(0.5, 3.5)], (cpFloat)1.0, 1e-5, @""); XCTAssertEqualWithAccuracy([sampler sample:cpv(3.5, 0.5)], (cpFloat)1.0, 1e-5, @""); XCTAssertEqualWithAccuracy([sampler sample:cpv(3.5, 3.5)], (cpFloat)0.0, 1e-5, @""); XCTAssertEqualWithAccuracy([sampler sample:cpv(2.0 - 1e-5, 3.5)], (cpFloat)1.0, 1e-5, @""); XCTAssertEqualWithAccuracy([sampler sample:cpv(2.0 + 1e-5, 3.5)], (cpFloat)0.0, 1e-5, @""); XCTAssertEqualWithAccuracy([sampler sample:cpv(3.5, 2.0 - 1e-5)], (cpFloat)1.0, 1e-5, @""); XCTAssertEqualWithAccuracy([sampler sample:cpv(3.5, 2.0 + 1e-5)], (cpFloat)0.0, 1e-5, @""); [sampler release]; } } -(void)testMultiGrabSort { ChipmunkSpace *space = [[ChipmunkSpace alloc] init]; ChipmunkMultiGrab *multiGrab = [[ChipmunkMultiGrab alloc] initForSpace:space withSmoothing:0.0 withGrabForce:1.0]; ChipmunkBody *body = [space add:[ChipmunkBody bodyWithMass:1.0 andMoment:1.0]]; ChipmunkShape *big = [space add:[ChipmunkCircleShape circleWithBody:body radius:10.0 offset:cpvzero]]; ChipmunkShape *small = [space add:[ChipmunkCircleShape circleWithBody:body radius:5.0 offset:cpvzero]]; // Used for the custom sorting orders. big.userData = @0; small.userData = @1; ChipmunkGrab *grab1 = [multiGrab beginLocation:cpvzero]; XCTAssertEqual(grab1.grabbedShape, big, @"Should have grabbed 'big' since it has the largest penetration depth."); multiGrab.grabSort = ^(ChipmunkShape *shape, cpFloat depth){ NSNumber *n = shape.userData; return (cpFloat)n.floatValue; }; // Should grab small since it's sorting order will be the largest; ChipmunkGrab *grab2 = [multiGrab beginLocation:cpvzero]; XCTAssertEqual(grab2.grabbedShape, small, @"Should have grabbed 'small' since it has the highest custom sort value."); [multiGrab release]; [space release]; } -(void)testPolyArea { cpFloat area1 = cpAreaForCircle(0.0, 1.0); cpFloat area2 = cpAreaForPoly(1, (cpVect[]){cpvzero}, 1.0); XCTAssertEqualWithAccuracy(area1, area2, 1e-3, @""); cpFloat area3 = cpAreaForSegment(cpv(-1,0), cpv(1,0), 1.0); cpFloat area4 = cpAreaForPoly(2, (cpVect[]){cpv(-1,0), cpv(1,0)}, 1.0); XCTAssertEqualWithAccuracy(area3, area4, 1e-3, @""); cpFloat area5 = area1 + 4.0; cpFloat area6 = cpAreaForPoly(2, (cpVect[]){cpv(-1,-1), cpv(1,-1), cpv(1,1), cpv(-1,1)}, 1.0); XCTAssertEqualWithAccuracy(area5, area6, 1e-3, @""); } @end Chipmunk2D-Chipmunk-7.0.3/xcode/ObjectiveChipmunkTests/ObjectiveChipmunkTests-Info.plist000066400000000000000000000012421347650476100314150ustar00rootroot00000000000000 CFBundleDevelopmentRegion en CFBundleExecutable ${EXECUTABLE_NAME} CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundlePackageType BNDL CFBundleShortVersionString 1.0 CFBundleSignature ???? CFBundleVersion 1 Chipmunk2D-Chipmunk-7.0.3/xcode/ObjectiveChipmunkTests/ShapeTest.m000066400000000000000000000100431347650476100250700ustar00rootroot00000000000000/* 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. */ #import #import "ObjectiveChipmunk/ObjectiveChipmunk.h" @interface ShapeTest : XCTestCase {} @end @implementation ShapeTest #define AssertEqualStruct(a, b, m) {typeof(a) __a = a; typeof(b) __b = b; XCTAssertTrue(memcmp(&__a, &__b, sizeof(a)) == 0, m);} #define TestAccessors(o, p, v) o.p = v; XCTAssertEqual(o.p, v, @""); #define TestAccessorsV(o, p, v) o.p = v; XCTAssertTrue(cpveql(o.p, v), @""); #define TestAccessorsS(o, p, v) o.p = v; AssertEqualStruct(o.p, v, @""); static void testPropertiesHelper(id self, ChipmunkBody *body, ChipmunkShape *shape) { XCTAssertNotEqual(shape.shape, NULL, @""); XCTAssertEqual(body, shape.body, @""); XCTAssertNil(shape.userData, @""); XCTAssertFalse(shape.sensor, @""); XCTAssertEqual(shape.elasticity, (cpFloat)0, @""); XCTAssertEqual(shape.friction, (cpFloat)0, @""); XCTAssertTrue(cpveql(shape.surfaceVelocity, cpvzero), @""); XCTAssertNil(shape.collisionType, @""); AssertEqualStruct(shape.filter, CP_SHAPE_FILTER_ALL, @""); cpBB bb = [shape cacheBB]; AssertEqualStruct(shape.bb, bb, @""); TestAccessors(shape, userData, @"object"); TestAccessors(shape, sensor, YES); TestAccessors(shape, elasticity, (cpFloat)0); TestAccessors(shape, friction, (cpFloat)0); TestAccessorsV(shape, surfaceVelocity, cpv(5,6)); TestAccessors(shape, collisionType, @"type"); cpShapeFilter f = {@"group", 456, 789}; TestAccessorsS(shape, filter, f); } -(void)testProperties { ChipmunkBody *body = [ChipmunkBody bodyWithMass:1 andMoment:1]; ChipmunkCircleShape *circle = [ChipmunkCircleShape circleWithBody:body radius:1 offset:cpv(1,2)]; testPropertiesHelper(self, body, circle); XCTAssertEqual(circle.radius, (cpFloat)1, @""); XCTAssertTrue(cpveql(circle.offset, cpv(1,2)), @""); XCTAssertTrue([circle pointQuery:cpv(1,2)].distance <= 0.0f, @""); XCTAssertTrue([circle pointQuery:cpv(1,2.9)].distance <= 0.0f, @""); XCTAssertFalse([circle pointQuery:cpv(1,3.1)].distance <= 0.0f, @""); ChipmunkSegmentShape *segment = [ChipmunkSegmentShape segmentWithBody:body from:cpvzero to:cpv(1,0) radius:1]; testPropertiesHelper(self, body, segment); XCTAssertTrue(cpveql(segment.a, cpvzero), @""); XCTAssertTrue(cpveql(segment.b, cpv(1,0)), @""); XCTAssertTrue(cpveql(segment.normal, cpv(0,-1)), @""); XCTAssertTrue([segment pointQuery:cpvzero].distance <= 0.0f, @""); XCTAssertTrue([segment pointQuery:cpv(1,0)].distance <= 0.0f, @""); XCTAssertTrue([segment pointQuery:cpv(0.5, 0.5)].distance <= 0.0f, @""); XCTAssertFalse([segment pointQuery:cpv(0,3)].distance <= 0.0f, @""); ChipmunkPolyShape *poly = [ChipmunkPolyShape boxWithBody:body width:10 height:10 radius:0.0f]; testPropertiesHelper(self, body, poly); XCTAssertTrue([poly pointQuery:cpv(0,0)].distance <= 0.0f, @""); XCTAssertTrue([poly pointQuery:cpv(3,3)].distance <= 0.0f, @""); XCTAssertFalse([poly pointQuery:cpv(-10,0)].distance <= 0.0f, @""); // TODO should add segment query tests } -(void)testSpace { // TODO } @end Chipmunk2D-Chipmunk-7.0.3/xcode/ObjectiveChipmunkTests/SpaceTest.m000066400000000000000000000412261347650476100250720ustar00rootroot00000000000000/* 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. */ #import #import "ObjectiveChipmunk/ObjectiveChipmunk.h" @interface SpaceTest : XCTestCase {} @end @implementation SpaceTest #define TestAccessors(o, p, v) o.p = v; XCTAssertEqual(o.p, v, @""); #define TestAccessorsV(o, p, v) o.p = v; XCTAssertTrue(cpveql(o.p, v), @""); #define AssertRetainCount(obj, count) XCTAssertEqual([obj retainCount], (NSUInteger)count, @"") -(void)testProperties { ChipmunkSpace *space = [[ChipmunkSpace alloc] init]; XCTAssertTrue(cpveql(space.gravity, cpvzero), @""); XCTAssertEqual(space.damping, (cpFloat)1.0, @""); XCTAssertEqual(space.idleSpeedThreshold, (cpFloat)0, @""); XCTAssertEqual(space.sleepTimeThreshold, (cpFloat)INFINITY, @""); XCTAssertNotEqual(space.space, NULL, @""); XCTAssertNotNil(space.staticBody, @""); TestAccessors(space, iterations, 50); TestAccessorsV(space, gravity, cpv(1,2)); TestAccessors(space, damping, (cpFloat)5); TestAccessors(space, idleSpeedThreshold, (cpFloat)5); TestAccessors(space, sleepTimeThreshold, (cpFloat)5); [space release]; } static NSSet * pointQueryInfoToShapes(NSArray *arr) { NSMutableSet *set = [NSMutableSet setWithCapacity:[arr count]]; for(ChipmunkPointQueryInfo *info in arr)[set addObject:info.shape]; return set; } static NSSet * segmentQueryInfoToShapes(NSArray *arr) { NSMutableSet *set = [NSMutableSet setWithCapacity:[arr count]]; for(ChipmunkSegmentQueryInfo *info in arr)[set addObject:info.shape]; return set; } static NSSet * shapeQueryInfoToShapes(NSArray *arr) { NSMutableSet *set = [NSMutableSet setWithCapacity:[arr count]]; for(ChipmunkShapeQueryInfo *info in arr)[set addObject:info.shape]; return set; } static void testPointQueries_helper(id self, ChipmunkSpace *space, ChipmunkBody *body) { ChipmunkShape *circle = [space add:[[ChipmunkCircleShape alloc] initWithBody:body radius:1 offset:cpv(1,1)]]; ChipmunkShape *segment = [space add:[[ChipmunkSegmentShape alloc] initWithBody:body from:cpvzero to:cpv(1,1) radius:1]]; ChipmunkShape *box = [space add:[[ChipmunkPolyShape alloc] initBoxWithBody:body width:1 height:1 radius:0]]; NSSet *set; NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // Point queries set = pointQueryInfoToShapes([space pointQueryAll:cpvzero maxDistance:0.0 filter:CP_SHAPE_FILTER_ALL]); XCTAssertEqualObjects(set, ([NSSet setWithObjects:segment, box, nil]), @""); set = pointQueryInfoToShapes([space pointQueryAll:cpv(1,1) maxDistance:0.0 filter:CP_SHAPE_FILTER_ALL]); XCTAssertEqualObjects(set, ([NSSet setWithObjects:circle, segment, nil]), @""); set = pointQueryInfoToShapes([space pointQueryAll:cpv(0.4, 0.4) maxDistance:0.0 filter:CP_SHAPE_FILTER_ALL]); XCTAssertEqualObjects(set, ([NSSet setWithObjects:circle, segment, box, nil]), @""); set = pointQueryInfoToShapes([space pointQueryAll:cpv(-0.5, -0.5) maxDistance:0.0 filter:CP_SHAPE_FILTER_ALL]); XCTAssertEqualObjects(set, ([NSSet setWithObjects:segment, nil]), @""); set = pointQueryInfoToShapes([space pointQueryAll:cpv(-1,-1) maxDistance:0.0 filter:CP_SHAPE_FILTER_ALL]); XCTAssertEqualObjects(set, ([NSSet set]), @""); cpSpacePointQuery_b(space.space, cpv(-0.6, -0.6), 0.0, CP_SHAPE_FILTER_ALL, ^(cpShape *shape, cpVect p, cpFloat d, cpVect g){ XCTAssertEqual(shape, segment.shape, @""); XCTAssertEqualWithAccuracy(cpvdist(p, cpvnormalize(cpv(-1, -1))), (cpFloat)0.0, 1e-5, @""); XCTAssertEqualWithAccuracy(d, cpfsqrt(2*0.6*0.6) - 1.0f, 1e-5, @""); }); ChipmunkPointQueryInfo *pointInfo = nil; pointInfo = [space pointQueryNearest:cpv(-3, -3) maxDistance:10 filter:CP_SHAPE_FILTER_ALL]; XCTAssertEqual(pointInfo.shape, segment, @""); // A point query that misses should return nil. pointInfo = [space pointQueryNearest:cpv(-3, -3) maxDistance:0 filter:CP_SHAPE_FILTER_ALL]; XCTAssertNil(pointInfo, @""); // Segment queries set = segmentQueryInfoToShapes([space segmentQueryAllFrom:cpv(-2,-2) to:cpv(4,4) radius:0.0 filter:CP_SHAPE_FILTER_ALL]); XCTAssertEqualObjects(set, ([NSSet setWithObjects:circle, segment, box, nil]), @""); set = segmentQueryInfoToShapes([space segmentQueryAllFrom:cpv(2,-2) to:cpv(-2,2) radius:0.0 filter:CP_SHAPE_FILTER_ALL]); XCTAssertEqualObjects(set, ([NSSet setWithObjects:segment, box, nil]), @""); set = segmentQueryInfoToShapes([space segmentQueryAllFrom:cpv(3,-1) to:cpv(-1,3) radius:0.0 filter:CP_SHAPE_FILTER_ALL]); XCTAssertEqualObjects(set, ([NSSet setWithObjects:circle, segment, nil]), @""); set = segmentQueryInfoToShapes([space segmentQueryAllFrom:cpv(2.4,-1.6) to:cpv(-1.6,2.4) radius:0.0 filter:CP_SHAPE_FILTER_ALL]); XCTAssertEqualObjects(set, ([NSSet setWithObjects:circle, segment, box, nil]), @""); set = segmentQueryInfoToShapes([space segmentQueryAllFrom:cpv(2,2) to:cpv(3,3) radius:0.0 filter:CP_SHAPE_FILTER_ALL]); XCTAssertEqualObjects(set, ([NSSet set]), @""); ChipmunkSegmentQueryInfo *segmentInfo = nil; segmentInfo = [space segmentQueryFirstFrom:cpv(-2,-2) to:cpv(1,1) radius:0.0 filter:CP_SHAPE_FILTER_ALL]; XCTAssertEqual(segmentInfo.shape, segment, @""); segmentInfo = [space segmentQueryFirstFrom:cpv(-2,-2) to:cpv(-1,-1) radius:0.0 filter:CP_SHAPE_FILTER_ALL]; XCTAssertEqualObjects(segmentInfo.shape, nil, @""); cpSpaceSegmentQuery_b(space.space, cpv(-1.0, -0.6), cpv(1.0, -0.6), 0.0f, CP_SHAPE_FILTER_ALL, ^(cpShape *shape, cpVect p, cpVect n, cpFloat t){ XCTAssertEqual(shape, segment.shape, @""); XCTAssertEqualWithAccuracy(cpvlength(n), 1.0f, 1e-5, @""); XCTAssertEqualWithAccuracy(n.y, -0.6f, 1e-5, @""); XCTAssertEqualWithAccuracy(cpvdist(cpv(-1.0, -0.6), n)/2.0f, t, 1e-5, @""); }); // Segment queries starting from inside a shape segmentInfo = [space segmentQueryFirstFrom:cpvzero to:cpv(1,1) radius:0.0 filter:CP_SHAPE_FILTER_ALL]; XCTAssertEqual(segmentInfo.t, (cpFloat)0, @"Starting inside a shape should return t=0."); segmentInfo = [space segmentQueryFirstFrom:cpv(1,1) to:cpvzero radius:0.0 filter:CP_SHAPE_FILTER_ALL]; XCTAssertEqual(segmentInfo.t, (cpFloat)0, @"Starting inside a shape should return t=0."); segmentInfo = [space segmentQueryFirstFrom:cpv(-0.6, -0.6) to:cpvzero radius:0.0 filter:CP_SHAPE_FILTER_ALL]; XCTAssertEqual(segmentInfo.t, (cpFloat)0, @"Starting inside a shape should return t=0."); XCTAssertEqual(segmentInfo.shape, segment, @"Should have picked the segment shape."); // A segment query that misses should return nil. segmentInfo = [space segmentQueryFirstFrom:cpv(-10, 0) to:cpv(-10, -10) radius:0.0 filter:CP_SHAPE_FILTER_ALL]; XCTAssertNil(segmentInfo, @""); // Shape queries ChipmunkBody *queryBody = [ChipmunkBody bodyWithMass:1 andMoment:1]; ChipmunkShape *queryShape = [ChipmunkCircleShape circleWithBody:queryBody radius:1 offset:cpvzero]; queryBody.position = cpvzero; set = shapeQueryInfoToShapes([space shapeQueryAll:queryShape]); XCTAssertEqualObjects(set, ([NSSet setWithObjects:circle, segment, box, nil]), @""); queryBody.position = cpv(1,1); set = shapeQueryInfoToShapes([space shapeQueryAll:queryShape]); XCTAssertEqualObjects(set, ([NSSet setWithObjects:circle, segment, box, nil]), @""); queryBody.position = cpv(0,-1); set = shapeQueryInfoToShapes([space shapeQueryAll:queryShape]); XCTAssertEqualObjects(set, ([NSSet setWithObjects:segment, box, nil]), @""); queryBody.position = cpv(0,-1.6); set = shapeQueryInfoToShapes([space shapeQueryAll:queryShape]); XCTAssertEqualObjects(set, ([NSSet setWithObjects:segment, nil]), @""); cpSpaceShapeQuery_b(space.space, queryShape.shape, ^(cpShape *shape, cpContactPointSet *points){ XCTAssertEqual(shape, segment.shape, @""); XCTAssertEqual(points->count, 1, @""); }); queryBody.position = cpv(2,2); set = shapeQueryInfoToShapes([space shapeQueryAll:queryShape]); XCTAssertEqualObjects(set, ([NSSet setWithObjects:circle, segment, nil]), @""); queryBody.position = cpv(4,4); set = shapeQueryInfoToShapes([space shapeQueryAll:queryShape]); XCTAssertEqualObjects(set, ([NSSet set]), @""); [space remove:circle]; [space remove:segment]; [space remove:box]; [pool release]; AssertRetainCount(circle, 1); AssertRetainCount(segment, 1); AssertRetainCount(box, 1); [circle release]; [segment release]; [box release]; } -(void)testPointQueries { ChipmunkSpace *space = [[ChipmunkSpace alloc] init]; // With static bodies testPointQueries_helper(self, space, space.staticBody); testPointQueries_helper(self, space, [ChipmunkBody staticBody]); // Kinematic bodies testPointQueries_helper(self, space, [space add:[ChipmunkBody kinematicBody]]); // With regular bodies. testPointQueries_helper(self, space, [space add:[ChipmunkBody bodyWithMass:1 andMoment:1]]); // Rogue bodies testPointQueries_helper(self, space, [ChipmunkBody bodyWithMass:1 andMoment:1]); [space release]; } -(void)testShapes { ChipmunkSpace *space = [[ChipmunkSpace alloc] init]; ChipmunkShape *shape; // Check that static shapes get added correctly shape = [space add:[ChipmunkCircleShape circleWithBody:space.staticBody radius:1 offset:cpvzero]]; XCTAssertTrue(cpSpatialIndexContains(space.space->staticShapes, shape.shape, shape.shape->hashid), @""); shape = [space add:[ChipmunkCircleShape circleWithBody:[ChipmunkBody staticBody] radius:1 offset:cpvzero]]; XCTAssertTrue(cpSpatialIndexContains(space.space->staticShapes, shape.shape, shape.shape->hashid), @""); // Check that normal shapes get added correctly ChipmunkBody *rogue = [ChipmunkBody bodyWithMass:1 andMoment:1]; shape = [space add:[ChipmunkCircleShape circleWithBody:rogue radius:1 offset:cpvzero]]; XCTAssertTrue(cpSpatialIndexContains(space.space->dynamicShapes, shape.shape, shape.shape->hashid), @""); ChipmunkBody *normal = [space add:[ChipmunkBody bodyWithMass:1 andMoment:1]]; shape = [space add:[ChipmunkCircleShape circleWithBody:normal radius:1 offset:cpvzero]]; XCTAssertTrue(cpSpatialIndexContains(space.space->dynamicShapes, shape.shape, shape.shape->hashid), @""); [space release]; } -(void)testBasicSimulation { ChipmunkSpace *space = [[ChipmunkSpace alloc] init]; space.gravity = cpv(0, -100); [space addBounds:cpBBNew(-50, 0, 50, 100) thickness:1 elasticity:1 friction:1 filter:CP_SHAPE_FILTER_ALL collisionType:nil]; ChipmunkBody *ball = [space add:[ChipmunkBody bodyWithMass:1 andMoment:cpMomentForCircle(1, 0, 1, cpvzero)]]; ball.position = cpv(-10, 10); [space add:[ChipmunkCircleShape circleWithBody:ball radius:1 offset:cpvzero]]; ChipmunkBody *box = [space add:[ChipmunkBody bodyWithMass:1 andMoment:cpMomentForBox(1, 2, 2)]]; box.position = cpv(10, 10); [space add:[ChipmunkPolyShape boxWithBody:box width:2 height:2 radius:0]]; for(int i=0; i<100; i++) [space step:0.01]; XCTAssertTrue(cpfabs(ball.position.y - 1) < 1.1*space.collisionSlop, @""); XCTAssertTrue(cpfabs(box.position.y - 1) < 1.1*space.collisionSlop, @""); [space release]; } - (void)testInitialSleepingObjects { // http://www.cocos2d-iphone.org/forum/topic/28896#post-142428 ChipmunkSpace *space = [[ChipmunkSpace alloc] init]; space.sleepTimeThreshold = 10.0; ChipmunkBody *body1 = [space add:[ChipmunkBody bodyWithMass:1.0 andMoment:1.0]]; [space add:[ChipmunkCircleShape circleWithBody:body1 radius:1.0 offset:cpvzero]]; ChipmunkBody *body2 = [space add:[ChipmunkBody bodyWithMass:1.0 andMoment:1.0]]; [space add:[ChipmunkCircleShape circleWithBody:body2 radius:1.0 offset:cpvzero]]; [body1 sleep]; [space step:1.0]; XCTAssertFalse(body1.isSleeping, @""); XCTAssertFalse(body2.isSleeping, @""); [space release]; } -(void)testInitStepFree { // This was crashing on GCC. // Possible uninitialized field? cpSpace *space = cpSpaceNew(); cpSpaceStep(space, 1); cpSpaceFree(space); } -(bool)beginSleepSensorRemoveBug:(cpArbiter *)arb space:(ChipmunkSpace*)space { // 'b' is the shape we are using to trigger the callback. CHIPMUNK_ARBITER_GET_SHAPES(arb, a, b); NSLog(@"space: %p, arb: %p, a: %p, b: %p", space.space, arb, a.shape, a.body); [space addPostStepRemoval:b]; return FALSE; } static void VerifyContactGraph(id self, ChipmunkBody *body1, ChipmunkBody *body2) { __block int counter = 0; [body1 eachArbiter:^(cpArbiter *arb){ CHIPMUNK_ARBITER_GET_BODIES(arb, a, b); XCTAssertTrue(a == body1 && b == body2, @"Unexpected contact graph"); counter++; }]; [body2 eachArbiter:^(cpArbiter *arb){ CHIPMUNK_ARBITER_GET_BODIES(arb, a, b); XCTAssertTrue(a == body2 && b == body1, @"Unexpected contact graph"); counter++; }]; XCTAssertEqual(counter, 2, @"Wrong number of arbiters in contact graph."); } // This one was a doozy to find. :-\ // http://chipmunk-physics.net/forum/viewtopic.php?f=1&t=2472&start=40#p10924 -(void)testSleepSensorRemoveBug { ChipmunkSpace *space = [[ChipmunkSpace alloc] init]; space.sleepTimeThreshold = 0.1; ChipmunkBody *body1 = [space add:[ChipmunkBody bodyWithMass:1 andMoment:INFINITY]]; [space add:[ChipmunkCircleShape circleWithBody:body1 radius:1 offset:cpvzero]]; ChipmunkBody *body2 = [space add:[ChipmunkBody bodyWithMass:1 andMoment:INFINITY]]; [space add:[ChipmunkCircleShape circleWithBody:body2 radius:1 offset:cpvzero]]; body2.position = cpv(0, 1.9); for(int i=0; !body1.isSleeping; i++){ [space step:0.01]; XCTAssertTrue(i < 100, @"body1 failed to fall asleep"); } XCTAssertTrue(body2.isSleeping, @"body2 is not sleeping"); VerifyContactGraph(self, body1, body2); // Objects are now sleeping and have their contact graph data all set up carefully. NSString *type = @"woo"; ChipmunkBody *body3 = [space add:[ChipmunkBody bodyWithMass:1 andMoment:INFINITY]]; ChipmunkShape *shape = [space add:[ChipmunkCircleShape circleWithBody:body3 radius:1 offset:cpv(-0.5, 0)]]; shape.collisionType = type; [space addCollisionHandler:self typeA:nil typeB:type begin:@selector(beginSleepSensorRemoveBug:space:) preSolve:nil postSolve:nil separate:nil]; // Now step again and shape should get removed. [space step:0.01]; XCTAssertFalse([space contains:shape], @"'shape' did not get removed."); VerifyContactGraph(self, body1, body2); } -(bool)beginSleepActivateOnImpact:(cpArbiter *)arb space:(ChipmunkSpace*)space { // 'b' is the shape we are using to trigger the callback. CHIPMUNK_ARBITER_GET_BODIES(arb, sleeping, awake); XCTAssertTrue(sleeping.isSleeping, @"Body 'sleeping' should be sleeping."); XCTAssertFalse(awake.isSleeping, @"Body 'awake' should not be sleeping."); [sleeping activate]; return TRUE; } // Causing a sleeping body to be activated from a pre-solve callback causes issues with the deffered waking. // http://chipmunk-physics.net/forum/viewtopic.php?f=1&t=2477 -(void)testSleepActivateOnImpact { NSString *sleepType = @"sleeping"; NSString *awakeType = @"awake"; ChipmunkSpace *space = [[ChipmunkSpace alloc] init]; space.sleepTimeThreshold = 0.1; ChipmunkBody *body1 = [space add:[ChipmunkBody bodyWithMass:1 andMoment:INFINITY]]; ChipmunkShape *shape1 = [space add:[ChipmunkCircleShape circleWithBody:body1 radius:1 offset:cpvzero]]; shape1.collisionType = sleepType; [body1 sleep]; ChipmunkBody *body2 = [space add:[ChipmunkBody bodyWithMass:1 andMoment:INFINITY]]; ChipmunkShape *shape2 = [space add:[ChipmunkCircleShape circleWithBody:body2 radius:1 offset:cpvzero]]; shape2.collisionType = awakeType; [space addCollisionHandler:self typeA:sleepType typeB:awakeType begin:@selector(beginSleepActivateOnImpact:space:) preSolve:nil postSolve:nil separate:nil]; [space step:0.01]; XCTAssertFalse(body1.isSleeping, @"body1 did not awake."); XCTAssertFalse(body2.isSleeping, @"body2 did not awake."); } -(void)testAddBounds { ChipmunkSpace *space = [[ChipmunkSpace alloc] init]; NSArray *objs = [space addBounds:cpBBNew(0, 0, 10, 10) thickness:5 elasticity:0 friction:1 filter:CP_SHAPE_FILTER_ALL collisionType:nil]; XCTAssertTrue([space contains:objs], @""); [space release]; } // TODO more sleeping tests @end Chipmunk2D-Chipmunk-7.0.3/xcode/ObjectiveChipmunkTests/TestImageLA.png000066400000000000000000000002301347650476100256140ustar00rootroot00000000000000‰PNG  IHDRøVõ pHYs  šœ cHRMz%€ƒùÿ€éu0ê`:˜o’_ÅFIDATxÚLÈ¡ æÿ?ÏbHZ„q¢Ô›b^ÓÔ<<IEND®B`‚Chipmunk2D-Chipmunk-7.0.3/xcode/ObjectiveChipmunkTests/TestImageRGB.png000066400000000000000000000002421347650476100257350ustar00rootroot00000000000000‰PNG  IHDR©ñž~ pHYs  šœ cHRMz%€ƒùÿ€éu0ê`:˜o’_ÅF(IDATxÚ4˱ 0„À3ûïü)B¬D0³g¦ò9_ªaÍ›^亦 LgòIEND®B`‚Chipmunk2D-Chipmunk-7.0.3/xcode/ObjectiveChipmunkTests/en.lproj/000077500000000000000000000000001347650476100245435ustar00rootroot00000000000000Chipmunk2D-Chipmunk-7.0.3/xcode/ObjectiveChipmunkTests/en.lproj/InfoPlist.strings000066400000000000000000000000551347650476100300650ustar00rootroot00000000000000/* Localized versions of Info.plist keys */ Chipmunk2D-Chipmunk-7.0.3/xcode/bench.rb000066400000000000000000000004131347650476100177620ustar00rootroot00000000000000results = [] cmd = "|build/Release/ChipmunkDemo.app/Contents/MacOS/ChipmunkDemo -bench -trial" open(cmd) do|io| while str = io.gets do puts str results << str.split[2] end end puts "Placing results in paste buffer." open("|pbcopy", "w"){|io| io.puts results} Chipmunk2D-Chipmunk-7.0.3/xcode/bench2.rb000066400000000000000000000003121347650476100200420ustar00rootroot00000000000000results = [] cmd = "|pbpaste" open(cmd) do|io| while str = io.gets do puts str results << str.split[2] end end puts "Placing results in paste buffer." open("|pbcopy", "w"){|io| io.puts results} Chipmunk2D-Chipmunk-7.0.3/xcode/iphonestatic.command000077500000000000000000000037441347650476100224250ustar00rootroot00000000000000#! /usr/bin/ruby Dir.chdir(File.dirname($0)) require 'Tempfile' BUILD_LOG = Tempfile.new("Chipmunk-") BUILD_LOG_PATH = BUILD_LOG.path def log(string) puts string open(BUILD_LOG_PATH, 'a'){|f| f.puts string} end def latest_sdk() sdks = `xcodebuild -showsdks`.split("\n") versions = sdks.map do|elt| # Match only lines with "iphoneos" in them. m = elt.match(/iphoneos(\d\.\d)/) (m ? m.captures[0] : "0.0") end return versions.max end # Or you can pick a specific version string (ex: "5.1") IOS_SDK_VERSION = latest_sdk() log("Building using iOS SDK #{IOS_SDK_VERSION}") PROJECT = "Chipmunk7.xcodeproj" VERBOSE = (not ARGV.include?("--quiet")) def system(command) log "> #{command}" result = Kernel.system(VERBOSE ? "#{command} | tee -a #{BUILD_LOG_PATH}; exit ${PIPESTATUS[0]}" : "#{command} >> #{BUILD_LOG_PATH}") unless $? == 0 log "===========================================" log "Command failed with status #{$?}: #{command}" log "Build errors encountered. Aborting build script" log "Check the build log for more information: #{BUILD_LOG_PATH}" raise end end def build(target, configuration, simulator) sdk_os = (simulator ? "iphonesimulator" : "iphoneos") sdk = "#{sdk_os}#{IOS_SDK_VERSION}" command = "xcodebuild -project #{PROJECT} -sdk #{sdk} -configuration #{configuration} -target #{target}" system command return "build/#{configuration}-#{sdk_os}/lib#{target}.a" end def build_fat_lib(target, copy_list) iphone_lib = build(target, "Release", false) simulator_lib = build(target, "Debug", true) dirname = "#{target}" system "rm -rf '#{dirname}'" system "mkdir '#{dirname}'" system "lipo #{iphone_lib} #{simulator_lib} -create -output '#{dirname}/lib#{target}.a'" copy_list.each{|src| system "rsync -r --exclude='.*' '#{src}' '#{dirname}'"} puts "\n#{dirname}/ Succesfully built" end build_fat_lib( "Chipmunk-iOS", [ "../include", ]) build_fat_lib("ObjectiveChipmunk-iOS", [ "../include", "../objectivec/include", ]) BUILD_LOG.delete Chipmunk2D-Chipmunk-7.0.3/xcode/macstatic.command000077500000000000000000000027221347650476100216760ustar00rootroot00000000000000#! /usr/bin/ruby Dir.chdir(File.dirname($0)) require 'Tempfile' BUILD_LOG = Tempfile.new("Chipmunk-") BUILD_LOG_PATH = BUILD_LOG.path def log(string) puts string open(BUILD_LOG_PATH, 'a'){|f| f.puts string} end PROJECT = "Chipmunk7.xcodeproj" VERBOSE = (not ARGV.include?("--quiet")) def system(command) log "> #{command}" result = Kernel.system(VERBOSE ? "#{command} | tee -a #{BUILD_LOG_PATH}; exit ${PIPESTATUS[0]}" : "#{command} >> #{BUILD_LOG_PATH}") unless $? == 0 log "===========================================" log "Command failed with status #{$?}: #{command}" log "Build errors encountered. Aborting build script" log "Check the build log for more information: #{BUILD_LOG_PATH}" raise end end def build(target, configuration) command = "xcodebuild -project #{PROJECT} -configuration #{configuration} -arch x86_64 -target #{target}" system command return "build/#{configuration}/lib#{target}.a" end def build_lib(target, copy_list) debug_lib = build(target, "Debug") release_lib = build(target, "Release") dirname = "#{target}" system "rm -rf '#{dirname}'" system "mkdir '#{dirname}'" system "cp '#{debug_lib}' '#{dirname}/lib#{target}-Debug.a'" system "cp '#{release_lib}' '#{dirname}/lib#{target}.a'" copy_list.each{|src| system "rsync -r --exclude='.*' '#{src}' '#{dirname}'"} end build_lib("Chipmunk-Mac", [ "../include", ]) build_lib("ObjectiveChipmunk-Mac", [ "../include", "../objectivec/include", ]) BUILD_LOG.delete Chipmunk2D-Chipmunk-7.0.3/xcode/main-Info.plist000066400000000000000000000013361347650476100212550ustar00rootroot00000000000000 CFBundleDevelopmentRegion English CFBundleExecutable ${EXECUTABLE_NAME} CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundlePackageType APPL CFBundleSignature ???? CFBundleVersion 1.0 NSMainNibFile MainMenu NSPrincipalClass NSApplication